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) 2008-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 /** 21 * @namespace ActionURL static class to supply the current context path, container and action. 22 * Additionally, builds a URL from a controller and an action. 23 * <p>Additional Documentation: 24 * <ul> 25 * <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=url">LabKey URLs</a></li> 26 * <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=tutorialActionURL">Tutorial: Basics: Building URLs and Filters</a></li> 27 * </ul> 28 * </p> 29 */ 30 LABKEY.ActionURL = new function() 31 { 32 // private member variables 33 var _parsedPathName = parsePathName(window.location.pathname); 34 35 // private functions 36 function buildParameterMap(paramString) 37 { 38 if (!paramString && LABKEY.postParameters) 39 { 40 // The caller hasn't requested us to parse a specific URL, and we have POST parameters that were written 41 // back into the page by the server 42 return LABKEY.postParameters; 43 } 44 if (!paramString) 45 { 46 paramString = window.location.search; 47 } 48 if (paramString.charAt(0) == '?') 49 paramString = paramString.substring(1, paramString.length); 50 var paramArray = paramString.split('&'); 51 var parameters = {}; 52 for (var i = 0; i < paramArray.length; i++) 53 { 54 var nameValue = paramArray[i].split('=', 2); 55 if (nameValue.length == 1 && nameValue[0] != '') 56 { 57 // Handle URL parameters with a name but no value or = 58 nameValue[1] = ''; 59 } 60 61 if (nameValue.length == 2) 62 { 63 var name = decodeURIComponent(nameValue[0]); 64 if (undefined == parameters[name]) 65 parameters[name] = decodeURIComponent(nameValue[1]); 66 else 67 { 68 var curValue = parameters[name]; 69 if (LABKEY.Utils.isArray(curValue)) 70 curValue.push(decodeURIComponent(nameValue[1])); 71 else 72 parameters[name] = [curValue, decodeURIComponent(nameValue[1])]; 73 } 74 } 75 } 76 return parameters; 77 } 78 79 function codePath(path, method) 80 { 81 var a = path.split('/'); 82 for (var i=0 ; i<a.length ; i++) 83 a[i] = method(a[i]); 84 return a.join('/'); 85 } 86 87 function parsePathName(path) 88 { 89 var start = LABKEY.contextPath.length; 90 var end = path.lastIndexOf("/"); 91 var action = path.substring(end+1); 92 path = path.substring(start,end); 93 var controller = null; 94 var dash = action.indexOf('-'); 95 if (0 < dash) 96 { 97 controller = action.substring(0,dash); 98 action = action.substring(dash+1); 99 } 100 else 101 { 102 var slash = path.indexOf('/',1); 103 if (slash < 0) // 21945: e.g. '/admin' 104 controller = path.substring(1); 105 else 106 controller = path.substring(1, slash); 107 path = path.substring(slash); 108 } 109 var dot = action.indexOf('.'); 110 if (0 < dot) 111 action = action.substring(0,dot); 112 return { 113 controller: decodeURIComponent(controller), 114 action: decodeURIComponent(action), 115 containerPath: decodeURI(path) 116 }; 117 } 118 119 120 /** @scope LABKEY.ActionURL */ 121 return { 122 // public functions 123 124 /** 125 * Gets the current context path. The default context path for LabKey Server is '/labkey'. 126 * @return {String} Current context path. 127 */ 128 getContextPath : function() 129 { 130 return LABKEY.contextPath; 131 }, 132 133 /** 134 * Gets the current action 135 * @return {String} Current action. 136 */ 137 getAction : function() 138 { 139 return _parsedPathName.action; 140 }, 141 142 /** 143 * Gets the current (unencoded) container path. 144 * @return {String} Current container path. 145 */ 146 getContainer : function() 147 { 148 if (LABKEY.container && LABKEY.container.path) 149 return LABKEY.container.path; 150 return _parsedPathName.containerPath; 151 }, 152 153 /** 154 * Gets the current container's name. For example, if you are in the 155 * /Project/SubFolder/MyFolder container, this method would return 'MyFolder' 156 * while getContainer() would return the entire path. 157 * @return {String} Current container name. 158 */ 159 getContainerName : function() 160 { 161 var containerPath = LABKEY.ActionURL.getContainer(); 162 var start = containerPath.lastIndexOf("/"); 163 return containerPath.substring(start + 1); 164 }, 165 166 /** 167 * Get the current controller name 168 * @return {String} Current controller. 169 */ 170 getController : function() 171 { 172 return _parsedPathName.controller; 173 }, 174 175 /** 176 * Gets a URL parameter by name. Note that if the given parameter name is present more than once 177 * in the query string, the returned value will be the first occurance of that parameter name. To get all 178 * instances of the parameter, use getParameterArray(). 179 * @param {String} parameterName The name of the URL parameter. 180 * @return {String} The value of the named parameter, or undefined of the parameter is not present. 181 */ 182 getParameter : function(parameterName) 183 { 184 var val = buildParameterMap()[parameterName]; 185 return (val && LABKEY.Utils.isArray(val) && val.length > 0) ? val[0] : val; 186 }, 187 188 /** 189 * Gets a URL parameter by name. This method will always return an array of values, one for 190 * each instance of the parameter name in the query string. If the parameter name appears only once 191 * this method will return a one-element array. 192 * @param {String} parameterName The name of the URL parameter. 193 */ 194 getParameterArray : function(parameterName) 195 { 196 var val = buildParameterMap()[parameterName]; 197 return (val && !LABKEY.Utils.isArray(val)) ? [val] : val; 198 }, 199 200 /** 201 * Returns an object mapping URL parameter names to parameter values. If a given parameter 202 * appears more than once on the query string, the value in the map will be an array instead 203 * of a single value. Use LABKEY.Utils.isArray() to determine if the value is an array or not, or use 204 * getParameter() or getParameterArray() to retrieve a specific parameter name as a single value 205 * or array respectively. 206 * @param {String} [url] The URL to parse. If not specified, the browser's current location will be used. 207 * @return {Object} Map of parameter names to values. 208 */ 209 getParameters : function(url) 210 { 211 var paramString; 212 213 if (!url) 214 { 215 return buildParameterMap(url); 216 } 217 if (url.indexOf('?') != -1) 218 paramString = url.substring(url.indexOf('?') + 1, url.length); 219 else 220 paramString = url; 221 return buildParameterMap(paramString); 222 }, 223 224 /** 225 * Builds a URL from a controller and an action. Uses the current container and context path. 226 * @param {String} controller The controller to use in building the URL 227 * @param {String} action The action to use in building the URL 228 * @param {String} [containerPath] The container path to use (defaults to the current container) 229 * @param {Object} [parameters] An object with properties corresponding to GET parameters to append to the URL. 230 * Parameters will be encoded automatically. Parameter values that are arrays will be appended as multiple parameters 231 * with the same name. (Defaults to no parameters) 232 * @example Examples: 233 234 1. Build the URL for the 'getWebPart' action in the 'reports' controller within 235 the current container: 236 237 var url = LABKEY.ActionURL.buildURL("project", "getWebPart"); 238 239 2. Build the URL for the 'updateRows' action in the 'query' controller within 240 the container "My Project/My Folder": 241 242 var url = LABKEY.ActionURL.buildURL("query", "updateRows", 243 "My Project/My Folder"); 244 245 3. Navigate the browser to the study controller's begin action in the current 246 container: 247 248 window.location = LABKEY.ActionURL.buildURL("study", "begin"); 249 250 4. Navigate the browser to the study controller's begin action in the folder 251 "/myproject/mystudyfolder": 252 253 window.location = LABKEY.ActionURL.buildURL("study", "begin", 254 "/myproject/mystudyfolder"); 255 256 5. Navigate to the list controller's insert action, passing a returnUrl parameter 257 that points back to the current page: 258 259 window.location = LABKEY.ActionURL.buildURL("list", "insert", 260 LABKEY.ActionURL.getContainer(), {listId: 50, returnUrl: window.location}); 261 * @return {String} URL constructed from the current container and context path, 262 plus the specified controller and action. 263 */ 264 buildURL : function(controller, action, containerPath, parameters) 265 { 266 if(!containerPath) 267 containerPath = this.getContainer(); 268 containerPath = LABKEY.ActionURL.encodePath(containerPath); 269 270 //ensure that container path begins and ends with a / 271 if(containerPath.charAt(0) != "/") 272 containerPath = "/" + containerPath; 273 if(containerPath.charAt(containerPath.length - 1) != "/") 274 containerPath = containerPath + "/"; 275 if (-1 == action.indexOf('.')) 276 action += '.view'; 277 var query = LABKEY.ActionURL.queryString(parameters); 278 279 var newUrl; 280 if (LABKEY.experimental && LABKEY.experimental.containerRelativeURL) 281 newUrl = LABKEY.contextPath + containerPath + controller + "-" + action; 282 else 283 newUrl = LABKEY.contextPath + "/" + controller + containerPath + action; 284 if (query) 285 newUrl += '?' + query; 286 return newUrl; 287 }, 288 289 /** 290 * @private 291 * Encoder for LabKey container paths that accounts for / to only encode the proper names. NOTE: This method is 292 * marked as private and could change at any time. 293 * @param {String} decodedPath An unencoded container path. 294 * @returns {String} An URI encoded container path. 295 */ 296 encodePath : function(decodedPath) 297 { 298 return codePath(decodedPath, encodeURIComponent); 299 }, 300 301 /** 302 * @private 303 * Decoder for LabKey container paths that accounts for / to only decode the proper names. NOTE: This method is 304 * marked as private and could change at any time. 305 * @param {String} encodedPath An encoded container path. 306 * @returns {String} An URI decoded container path. 307 */ 308 decodePath : function(encodedPath) 309 { 310 return codePath(encodedPath, decodeURIComponent); 311 }, 312 313 /** 314 * Turn the parameter object into a query string (e.g. {x:'fred'} -> "x=fred"). 315 * The returned query string is not prepended by a question mark ('?'). 316 * 317 * @param {Object} [parameters] An object with properties corresponding to GET parameters to append to the URL. 318 * Parameters will be encoded automatically. Parameter values that are arrays will be appended as multiple parameters 319 * with the same name. (Defaults to no parameters.) 320 */ 321 queryString : function(parameters) 322 { 323 if (!parameters) 324 return ''; 325 var query = '', and = '', pval, parameter, aval; 326 327 for (parameter in parameters) 328 { 329 if (parameters.hasOwnProperty(parameter)) 330 { 331 pval = parameters[parameter]; 332 333 if (pval === null || pval === undefined) 334 pval = ''; 335 336 if (LABKEY.Utils.isArray(pval)) 337 { 338 for (var idx = 0; idx < pval.length; ++idx) 339 { 340 aval = pval[idx]; 341 query += and + encodeURIComponent(parameter) + '=' + encodeURIComponent(pval[idx]); 342 and = '&'; 343 } 344 } 345 else 346 { 347 query += and + encodeURIComponent(parameter) + '=' + encodeURIComponent(pval); 348 and = '&'; 349 } 350 } 351 } 352 return query; 353 }, 354 355 356 /** 357 * Get the current base URL, which includes context path by default 358 * for example: http://labkey.org/labkey/ 359 * @param {boolean} [noContextPath] Set true to omit the context path. Defaults to false. 360 * @return {String} Current base URL. 361 */ 362 getBaseURL : function(noContextPath) 363 { 364 return window.location.protocol + '//' + window.location.host + (noContextPath ? '' : LABKEY.ActionURL.getContextPath() + '/'); 365 } 366 }; 367 }; 368 369 370