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