1 /**
  2  * @fileOverview
  3  * @author <a href="https://www.labkey.org">LabKey</a> (<a href="mailto:info@labkey.com">info@labkey.com</a>)
  4  * @license Copyright (c) 2014-2017 LabKey Corporation
  5  * <p/>
  6  * Licensed under the Apache License, Version 2.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at
  9  * <p/>
 10  * http://www.apache.org/licenses/LICENSE-2.0
 11  * <p/>
 12  * Unless required by applicable law or agreed to in writing, software
 13  * distributed under the License is distributed on an "AS IS" BASIS,
 14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  * See the License for the specific language governing permissions and
 16  * limitations under the License.
 17  * <p/>
 18  */
 19 
 20 LABKEY.Utils = new function(impl, $) {
 21 
 22     // Insert a hidden <form> into to page, put the JSON into it, and submit it - the server's response will
 23     // make the browser pop up a dialog
 24     var formSubmit = function(url, value)
 25     {
 26         var formId = LABKEY.Utils.generateUUID();
 27         var formHTML = '<form method="POST" id="' + formId + '" action="' + url + '">' +
 28                 '<input type="hidden" name="json" value="' + LABKEY.Utils.encodeHtml(LABKEY.Utils.encode(value)) + '" />' +
 29                 '<input type="hidden" name="X-LABKEY-CSRF" value="' + LABKEY.CSRF + '" />' +
 30                 '</form>';
 31         $('body').append(formHTML);
 32         $('#'+formId).submit();
 33     };
 34 
 35     var displayModal = function(title, msg) {
 36         var modal = $('#lk-utils-modal');
 37 
 38         if (modal.length === 0) {
 39             $('body').append([
 40                 '<div id="lk-utils-modal" class="modal fade" role="dialog">',
 41                     '<div class="modal-dialog"><div class="modal-content"></div></div>',
 42                 '</div>'
 43             ].join(''));
 44 
 45             modal = $('#lk-utils-modal');
 46         }
 47 
 48         modal.find('.modal-content').html([
 49             '<div class="modal-header">',
 50                 '<button type="button" class="close" data-dismiss="modal">×</button>',
 51                 '<h4 class="modal-title">' + LABKEY.Utils.encodeHtml(title) + '</h4>',
 52             '</div>',
 53             '<div class="modal-body">',
 54                 '<br>',
 55                 '<p>' + LABKEY.Utils.encodeHtml(msg) + '</p>',
 56                 '<br>',
 57             '</div>'
 58         ].join(''));
 59 
 60         modal.modal('show');
 61     };
 62 
 63     /**
 64      * Documentation available in core/Utils.js -- search for "@name displayAjaxErrorResponse"
 65      */
 66     impl.displayAjaxErrorResponse = function(responseObj, exceptionObj, showExceptionClass, msgPrefix)
 67     {
 68         if (responseObj.status == 0)
 69         {
 70             // Don't show an error dialog if the user cancelled the request in the browser, like navigating
 71             // to another page
 72             return;
 73         }
 74 
 75         var error = LABKEY.Utils.getMsgFromError(responseObj, exceptionObj, {
 76             msgPrefix: msgPrefix,
 77             showExceptionClass: showExceptionClass
 78         });
 79         LABKEY.Utils.alert("Error", error);
 80     };
 81 
 82     /**
 83      * Documentation available in core/Utils.js -- search for "@name convertToExcel"
 84      */
 85     impl.convertToExcel = function(spreadsheet) {
 86         formSubmit(LABKEY.ActionURL.buildURL("experiment", "convertArraysToExcel"), spreadsheet);
 87     };
 88 
 89     /**
 90      * Documentation available in core/Utils.js -- search for "@name convertToTable"
 91      */
 92     impl.convertToTable = function(config) {
 93         formSubmit(LABKEY.ActionURL.buildURL("experiment", "convertArraysToTable"), config);
 94     };
 95 
 96     /**
 97      * Documentation specified in core/Utils.js -- search for "@name alert"
 98      */
 99     impl.alert = function(title, msg) {
100         if (window.Ext4) {
101             Ext4.Msg.alert(title?Ext4.htmlEncode(title):"", msg?Ext4.htmlEncode(msg):"")
102         }
103         else if (window.Ext) {
104             Ext.Msg.alert(title?Ext.util.Format.htmlEncode(title):"", msg?Ext.util.Format.htmlEncode(msg):"");
105         }
106         else {
107             displayModal(title, msg);
108         }
109     };
110 
111     /**
112      * Documentation specified in core/Utils.js -- search for "@name onError"
113      */
114     impl.onError = function(error) {
115 
116         if (!error)
117             return;
118 
119         console.log('ERROR: ' + error.exception);
120         console.log(error);
121 
122         if (!error.noAuditLog)
123         {
124             LABKEY.Query.insertRows({
125                 //it would be nice to store them in the current folder, but we cant guarantee the user has write access..
126                 containerPath: error.containerPath || '/shared',
127                 schemaName: 'auditlog',
128                 queryName: 'Client API Actions',
129                 rows: [{
130                     EventType: "Client API Actions",
131                     Key1: 'Client Error',
132                     //NOTE: labkey should automatically crop these strings to the allowable length for that field
133                     Key2: window.location.href,
134                     Key3: (error.stackTrace && LABKEY.Utils.isArray(error.stackTrace) ? error.stackTrace.join('\n') : null),
135                     Comment: (error.exception || error.statusText || error.message),
136                     Date: new Date()
137                 }],
138                 success: function() {},
139                 failure: function(error){
140                     console.log('Problem logging error');
141                     console.log(error);
142                 }
143             });
144         }
145     };
146 
147     /**
148      * Documentation specified in core/Utils.js -- search for "@name setWebpartTitle"
149      */
150     impl.setWebpartTitle = function(title, webPartId)
151     {
152         $('#webpart_' + webPartId + ' span.labkey-wp-title-text').html(LABKEY.Utils.encodeHtml(title));
153     };
154 
155     /**
156      * Documentation specified in core/Utils.js -- search for "@name onReady"
157      */
158     impl.onReady = function(config)
159     {
160         var scope;
161         var callback;
162         var scripts;
163 
164         if (LABKEY.Utils.isFunction(config))
165         {
166             scope = this;
167             callback = config;
168             scripts = null;
169         }
170         else if (LABKEY.Utils.isObject(config) && LABKEY.Utils.isFunction(config.callback))
171         {
172             scope = config.scope || this;
173             callback = config.callback;
174             scripts = config.scripts;
175         }
176         else
177         {
178             LABKEY.Utils.alert("Configuration Error", "Improper configuration for LABKEY.onReady()");
179             return;
180         }
181 
182         if (scripts)
183         {
184             LABKEY.requiresScript(scripts, callback, scope, true);
185         }
186         else
187         {
188             $(function() { callback.call(scope); });
189         }
190     };
191 
192     impl.addClass = function(element, cls)
193     {
194         if (LABKEY.Utils.isDefined(element))
195         {
196             if (LABKEY.Utils.isDefined(element.classList))
197             {
198                 element.classList.add(cls);
199             }
200             else
201             {
202                 element.className += " " + cls;
203             }
204         }
205     };
206 
207     impl.removeClass = function(element, cls)
208     {
209         if (LABKEY.Utils.isDefined(element))
210         {
211             if (LABKEY.Utils.isDefined(element.classList))
212             {
213                 element.classList.remove(cls);
214             }
215             else
216             {
217                 // http://stackoverflow.com/questions/195951/change-an-elements-css-class-with-javascript
218                 var reg = new RegExp("(?:^|\\s)" + cls + "(?!\\S)/g");
219                 element.className.replace(reg, '');
220             }
221         }
222     };
223 
224     impl.replaceClass = function(element, removeCls, addCls)
225     {
226         LABKEY.Utils.removeClass(element, removeCls);
227         LABKEY.Utils.addClass(element, addCls);
228     };
229 
230     //private
231     impl.loadAjaxContent = function(response, targetEl, success, scope, useReplace) {
232         var json = LABKEY.Utils.decode(response.responseText);
233         if (!json)
234             return;
235 
236         if (json.moduleContext)
237             LABKEY.applyModuleContext(json.moduleContext);
238 
239         if (json.requiredCssScripts)
240             LABKEY.requiresCss(json.requiredCssScripts);
241 
242         if (json.implicitCssIncludes)
243         {
244             for (var i=0;i<json.implicitCssIncludes.length;i++)
245             {
246                 LABKEY.requestedCssFiles(json.implicitCssIncludes[i]);
247             }
248         }
249 
250         if (json.requiredJsScripts && json.requiredJsScripts.length)
251         {
252             LABKEY.requiresScript(json.requiredJsScripts, onLoaded, this, true);
253         }
254         else
255         {
256             onLoaded();
257         }
258 
259         function onLoaded()
260         {
261             if (json.html)
262             {
263                 if (LABKEY.Utils.isString(targetEl)) {
264                     targetEl = $('#'+targetEl);
265                 }
266 
267                 if (useReplace === true) {
268                     targetEl.replaceWith(json.html);
269                 }
270                 else {
271                     targetEl.html(json.html); // execute scripts...so bad
272                 }
273 
274                 if (LABKEY.Utils.isFunction(success)) {
275                     success.call(scope || window);
276                 }
277 
278                 if (json.implicitJsIncludes)
279                     LABKEY.loadedScripts(json.implicitJsIncludes);
280             }
281         }
282     };
283 
284     impl.tabInputHandler = function(elementSelector) {
285         // http://stackoverflow.com/questions/1738808/keypress-in-jquery-press-tab-inside-textarea-when-editing-an-existing-text
286         $(elementSelector).keydown(function (e) {
287             if (e.keyCode == 9) {
288                 var myValue = "\t";
289                 var startPos = this.selectionStart;
290                 var endPos = this.selectionEnd;
291                 var scrollTop = this.scrollTop;
292                 this.value = this.value.substring(0, startPos) + myValue + this.value.substring(endPos,this.value.length);
293                 this.focus();
294                 this.selectionStart = startPos + myValue.length;
295                 this.selectionEnd = startPos + myValue.length;
296                 this.scrollTop = scrollTop;
297 
298                 e.preventDefault();
299             }
300         });
301     };
302 
303     impl.signalWebDriverTest = function(signalName, signalResult)
304     {
305         var signalContainerId = 'testSignals';
306         var signalContainerSelector = '#' + signalContainerId;
307         var signalContainer = $(signalContainerSelector);
308         var formHTML = '<div id="' + signalContainerId + '"/>';
309 
310         if (!signalContainer.length)
311         {
312             $('body').append(formHTML);
313             signalContainer = $(signalContainerSelector);
314             signalContainer.hide();
315         }
316 
317         signalContainer.find('div[name=' + LABKEY.Utils.encode(signalName) + ']').remove();
318         signalContainer.append('<div name="' + LABKEY.Utils.encodeHtml(signalName) + '" id="' + LABKEY.Utils.id() + '"/>');
319         if (signalResult !== undefined)
320         {
321             signalContainer.find('div[name="' + LABKEY.Utils.encodeHtml(signalName) + '"]').attr("value", LABKEY.Utils.encodeHtml(signalResult));
322         }
323     };
324 
325     /**
326      * Returns a string containing an absolute URL to a specific labkey.org documentation page. Modeled after HelpTopic.java getHelpTopicHref().
327      * <li>topic (required) The documentation page name</li>
328      */
329     impl.getHelpTopicHref = function(topic)
330     {
331         return LABKEY.helpLinkPrefix + topic;
332     };
333 
334     /**
335      * Returns a string containing a well-formed html anchor that opens a link to a specific labkey.org documentation
336      * page in a separate tab, using the standard target name. Modeled after HelpTopic.java getSimpleLinkHtml().
337      * <li>topic (required) The documentation page name</li>
338      * <li>displayText (required) The text to display inside the anchor</li>
339      */
340     impl.getSimpleLinkHtml = function(topic, displayText)
341     {
342         return '<a href="' + LABKEY.Utils.encodeHtml(LABKEY.Utils.getHelpTopicHref(topic)) + '" target="labkeyHelp">' + LABKEY.Utils.encodeHtml(displayText) + "</a>";
343     };
344 
345     return impl;
346 
347 }(LABKEY.Utils || new function() { return {}; }, jQuery);
348