1 /* 2 * Copyright (c) 2012-2014 LabKey Corporation 3 * 4 * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 5 */ 6 Ext.namespace('LABKEY.ext'); 7 8 // TODO: Get these off the global 'Date' object 9 Ext.ns("Date.patterns"); 10 Ext.applyIf(Date.patterns,{ 11 ISO8601Long:"Y-m-d H:i:s", 12 ISO8601Short:"Y-m-d" 13 }); 14 15 LABKEY.ext.Utils = new function() { 16 var createHttpProxyImpl = function(containerPath, errorListener) { 17 var proxy = new Ext.data.HttpProxy(new Ext.data.Connection({ 18 //where to retrieve data 19 url: LABKEY.ActionURL.buildURL("query", "selectRows", containerPath), //url to data object (server side script) 20 method: 'GET' 21 })); 22 23 if (errorListener) 24 proxy.on("loadexception", errorListener); 25 26 proxy.on("beforeload", mapQueryParameters); 27 28 return proxy; 29 }; 30 31 var mapQueryParameters = function(store, options) { 32 // map all parameters from ext names to labkey names: 33 for (var p in options) 34 { 35 if (options.hasOwnProperty(p)) { 36 if (_extParamMapping[p]) 37 options[_extParamMapping[p]] = options[p]; 38 } 39 } 40 41 // fix up any necessary parameter values: 42 if ("DESC" == options['query.sortdir']) 43 { 44 var sortCol = options['query.sort']; 45 options['query.sort'] = "-" + sortCol; 46 } 47 }; 48 49 // migrated from util.js 50 var handleTabsInTextArea = function(event) { 51 // Check if the user hit TAB or SHIFT-TAB 52 if (event.getKey() == Ext.EventObject.TAB && !event.ctrlKey && !event.altKey) 53 { 54 var t = event.target; 55 56 if (Ext.isIE) 57 { 58 var range = document.selection.createRange(); 59 var stored_range = range.duplicate(); 60 stored_range.moveToElementText(t); 61 stored_range.setEndPoint('EndToEnd', range); 62 t.selectionStart = stored_range.text.length - range.text.length; 63 t.selectionEnd = t.selectionStart + range.text.length; 64 t.setSelectionRange = function(start, end) 65 { 66 var range = this.createTextRange(); 67 range.collapse(true); 68 range.moveStart("character", start); 69 range.moveEnd("character", end - start); 70 range.select(); 71 }; 72 } 73 74 var ss = t.selectionStart; 75 var se = t.selectionEnd; 76 var newSelectionStart = ss; 77 var scrollTop = t.scrollTop; 78 79 if (ss != se) 80 { 81 // In case selection was not the entire line (e.g. selection begins in the middle of a line) 82 // we need to tab at the beginning as well as at the start of every following line. 83 var pre = t.value.slice(0,ss); 84 var sel = t.value.slice(ss,se); 85 var post = t.value.slice(se,t.value.length); 86 87 // If our selection starts in the middle of the line, include the full line 88 if (pre.length > 0 && pre.lastIndexOf('\n') != pre.length - 1) 89 { 90 // Add the beginning of the line to the indented area 91 sel = pre.slice(pre.lastIndexOf('\n') + 1, pre.length).concat(sel); 92 // Remove it from the prefix 93 pre = pre.slice(0, pre.lastIndexOf('\n') + 1); 94 if (!event.shiftKey) 95 { 96 // Add one to the starting index since we're going to add a tab before it 97 newSelectionStart++; 98 } 99 } 100 // If our last selected character is a new line, don't add a tab after it since that's 101 // part of the next line 102 if (sel.lastIndexOf('\n') == sel.length - 1) 103 { 104 sel = sel.slice(0, sel.length - 1); 105 post = '\n' + post; 106 } 107 108 // Shift means remove indentation 109 if (event.shiftKey) 110 { 111 // Remove one tab after each newline 112 sel = sel.replace(/\n\t/g,"\n"); 113 if (sel.indexOf('\t') == 0) 114 { 115 // Remove one leading tab, if present 116 sel = sel.slice(1, sel.length); 117 // We're stripping out a tab before the selection, so march it back one character 118 newSelectionStart--; 119 } 120 } 121 else 122 { 123 pre = pre.concat('\t'); 124 sel = sel.replace(/\n/g,"\n\t"); 125 } 126 127 var originalLength = t.value.length; 128 t.value = pre.concat(sel).concat(post); 129 t.setSelectionRange(newSelectionStart, se + (t.value.length - originalLength)); 130 } 131 // No text is selected 132 else 133 { 134 // Shift means remove indentation 135 if (event.shiftKey) 136 { 137 // Figure out where the current line starts 138 var lineStart = t.value.slice(0, ss).lastIndexOf('\n'); 139 if (lineStart < 0) 140 { 141 lineStart = 0; 142 } 143 // Look for the first tab 144 var tabIndex = t.value.slice(lineStart, ss).indexOf('\t'); 145 if (tabIndex != -1) 146 { 147 // The line has a tab - need to remove it 148 tabIndex += lineStart; 149 t.value = t.value.slice(0, tabIndex).concat(t.value.slice(tabIndex + 1, t.value.length)); 150 if (ss == se) 151 { 152 ss--; 153 se = ss; 154 } 155 else 156 { 157 ss--; 158 se--; 159 } 160 } 161 } 162 else 163 { 164 // Shove a tab in at the cursor 165 t.value = t.value.slice(0,ss).concat('\t').concat(t.value.slice(ss,t.value.length)); 166 if (ss == se) 167 { 168 ss++; 169 se = ss; 170 } 171 else 172 { 173 ss++; 174 se++; 175 } 176 } 177 t.setSelectionRange(ss, se); 178 } 179 t.scrollTop = scrollTop; 180 181 // Don't let the browser treat it as a focus traversal 182 event.preventDefault(); 183 } 184 }; 185 186 return { 187 /** 188 * Creates an Ext.data.Store that queries the LabKey Server database and can be used as the data source 189 * for various components, including GridViews, ComboBoxes, and so forth. 190 * @deprecated 191 * @param {Object} config Describes the GridView's properties. 192 * @param {String} config.schemaName Name of a schema defined within the current 193 * container. Example: 'study'. See also: <a class="link" 194 href="https://www.labkey.org/wiki/home/Documentation/page.view?name=findNames"> 195 How To Find schemaName, queryName & viewName</a>. 196 * @param {String} config.queryName Name of a query defined within the specified schema 197 * in the current container. Example: 'SpecimenDetail'. See also: <a class="link" 198 href="https://www.labkey.org/wiki/home/Documentation/page.view?name=findNames"> 199 How To Find schemaName, queryName & viewName</a>. 200 * @param {String} [config.containerPath] The container path in which the schemaName and queryName are defined. 201 * @param {String} [config.viewName] Name of a custom view defined over the specified query. 202 * in the current container. Example: 'SpecimenDetail'. See also: <a class="link" 203 href="https://www.labkey.org/wiki/home/Documentation/page.view?name=findNames"> 204 How To Find schemaName, queryName & viewName</a>. 205 * @param {Object} [config.allowNull] If specified, this configuration will be used to insert a blank 206 * entry as the first entry in the store. 207 * @param {String} [config.allowNull.keyColumn] If specified, the name of the column in the underlying database 208 * that holds the key. 209 * @param {String} [config.allowNull.displayColumn] If specified, the name of the column in the underlying database 210 * that holds the value to be shown by default in the display component. 211 * @param {String} [config.allowNull.emptyName] If specified, what to show in the list for the blank entry. 212 * Defaults to '[None]'. 213 * @param {String} [config.allowNull.emptyValue] If specified, the value to be used for the blank entry. 214 * Defaults to the empty string. 215 * 216 * @return {Ext.data.Store} The initialized Store object 217 */ 218 createExtStore: function(storeConfig) { 219 if (!storeConfig) 220 storeConfig = {}; 221 if (!storeConfig.baseParams) 222 storeConfig.baseParams = {}; 223 storeConfig.baseParams['query.queryName'] = storeConfig.queryName; 224 storeConfig.baseParams['schemaName'] = storeConfig.schemaName; 225 if (storeConfig.viewName) 226 storeConfig.baseParams['query.viewName'] = storeConfig.viewName; 227 228 if (!storeConfig.proxy) 229 storeConfig.proxy = createHttpProxyImpl(storeConfig.containerPath); 230 231 if (!storeConfig.remoteSort) 232 storeConfig.remoteSort = true; 233 234 if (!storeConfig.listeners || !storeConfig.listeners.loadexception) 235 storeConfig.listeners = { loadexception : { fn : handleLoadError } }; 236 237 storeConfig.reader = new Ext.data.JsonReader(); 238 239 var result = new Ext.data.Store(storeConfig); 240 241 if (storeConfig.allowNull) 242 { 243 var emptyValue = storeConfig.allowNull.emptyValue; 244 if (!emptyValue) 245 { 246 emptyValue = ""; 247 } 248 var emptyName = storeConfig.allowNull.emptyName; 249 if (!emptyName) 250 { 251 emptyName = "[None]"; 252 } 253 result.on("load", function(store) 254 { 255 var emptyRecordConstructor = Ext.data.Record.create([storeConfig.allowNull.keyColumn, storeConfig.allowNull.displayColumn]); 256 var recordData = {}; 257 recordData[storeConfig.allowNull.keyColumn] = emptyValue; 258 recordData[storeConfig.allowNull.displayColumn] = emptyName; 259 var emptyRecord = new emptyRecordConstructor(recordData); 260 store.insert(0, emptyRecord); 261 }); 262 } 263 264 return result; 265 }, 266 267 /** 268 * Ensure BoxComponent is visible on the page. 269 * @param boxComponent 270 * @deprecated 271 */ 272 ensureBoxVisible: function(boxComponent) { 273 var box = boxComponent.getBox(true); 274 var viewportWidth = Ext.lib.Dom.getViewWidth(); 275 var scrollLeft = Ext.dd.DragDropMgr.getScrollLeft(); 276 277 var scrollBarWidth = 20; 278 if (viewportWidth - scrollBarWidth + scrollLeft < box.width + box.x) { 279 boxComponent.setPosition(viewportWidth + scrollLeft - box.width - scrollBarWidth); 280 } 281 }, 282 283 /** 284 * Event handler that can be attached to text areas to let them handle indent/outdent with TAB/SHIFT-TAB. 285 * Handles region selection for multi-line indenting as well. 286 * Note that this overrides the browser's standard focus traversal keystrokes. 287 * Based off of postings from http://ajaxian.com/archives/handling-tabs-in-textareas 288 * Wire it up with a call like: 289 * Ext.EventManager.on('queryText', 'keydown', LABKEY.ext.Utils.handleTabsInTextArea); 290 * @param event an Ext.EventObject for the keydown event 291 */ 292 handleTabsInTextArea: handleTabsInTextArea, 293 294 /** 295 * This method takes an object that is/extends an Ext.Container (e.g. Panels, Toolbars, Viewports, Menus) and 296 * resizes it so the Container fits inside the viewable region of the window. This is generally used in the case 297 * where the Container is not rendered to a webpart but rather displayed on the page itself (e.g. SchemaBrowser, 298 * manageFolders, etc). 299 * @param extContainer - (Required) outer container which is the target to be resized 300 * @param width - (Required) width of the viewport. In many cases, the window width. If a negative width is passed than 301 * the width will not be set. 302 * @param height - (Required) height of the viewport. In many cases, the window height. If a negative height is passed than 303 * the height will not be set. 304 * @param paddingX - distance from the right edge of the viewport. Defaults to 35. 305 * @param paddingY - distance from the bottom edge of the viewport. Defaults to 35. 306 */ 307 resizeToViewport: function(extContainer, width, height, paddingX, paddingY, offsetX, offsetY) 308 { 309 if (!extContainer || !extContainer.rendered) 310 return; 311 312 if (width < 0 && height < 0) 313 return; 314 315 var padding = []; 316 if (offsetX == undefined || offsetX == null) 317 offsetX = 35; 318 if (offsetY == undefined || offsetY == null) 319 offsetY = 35; 320 321 if (paddingX !== undefined && paddingX != null) 322 padding.push(paddingX); 323 else 324 { 325 326 var bp = Ext.get('bodypanel'); 327 if (bp) { 328 var t = Ext.query('table.labkey-proj'); 329 if (t && t.length > 0) { 330 t = Ext.get(t[0]); 331 padding.push((t.getWidth()-(bp.getWidth())) + offsetX); 332 } 333 else 334 padding.push(offsetX); 335 } 336 else 337 padding.push(offsetX); 338 } 339 if (paddingY !== undefined && paddingY != null) 340 padding.push(paddingY); 341 else 342 padding.push(offsetY); 343 344 var xy = extContainer.el.getXY(); 345 var size = { 346 width : Math.max(100,width-xy[0]-padding[0]), 347 height : Math.max(100,height-xy[1]-padding[1]) 348 }; 349 350 if (width < 0) 351 extContainer.setHeight(size.height); 352 else if (height < 0) 353 extContainer.setWidth(size.width); 354 else 355 extContainer.setSize(size); 356 extContainer.doLayout(); 357 } 358 }; 359 };