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