1 // Copyright (c) 2009-2014 SAP SE, All Rights Reserved 2 /** 3 * @fileOverview An API which allows applications to talk to a surrounding shell. 4 * <p> 5 * <b>Note:</b> Including this file automatically performs <b>domain relaxation</b>.<br> 6 * Since version 1.26.3, the way how domain relaxation is performed can be 7 * configured by setting the property <code>domainRelaxation</code> in the global variable 8 * <code>sap-ui2-shell-api-config</code> before including the script. The property 9 * <code>domainRelaxation</code> has to be an object with the following properties:<br> 10 * <ul> 11 * <li><code>integrated</code>: 12 * domain relaxation used, when application is started inside of an iframe, 13 * a frameset or a popup window</li> 14 * <li><code>standalone</code>: 15 * domain relaxation used, when application runs standalone</li> 16 * <li><code>maxrelax</code> 17 * maximal relaxation, for "auto" and "maximal"</li> 18 * </ul> 19 * <br> 20 * Possible values for integrated/standalone:<br> 21 * <ul> 22 * <li><code>"none"</code> : No domain relaxation will be done</li> 23 * <li><code>"auto"</code> : Domain relaxation will automatically adapt to the parent window. 24 * If no matching relaxation can be found, domain relaxation is 25 * "maximal". When running standalone this defaults to "minimal".</li> 26 * <li><code>"minimal"</code> : Only the first part (hostname) of the domain will be removed</li> 27 * <li><code>"maximal"</code> : Remove as much as possible, without relaxing to the top-level domain</li> 28 * </ul> 29 * <br> 30 * Possible value for maxrelax: 31 * Integer, that determines the number of domain parts, that have to be kept. 32 * E.g. 2 means "sap.com", 3 means "sub.sap.com" 33 * <p> 34 * Example:<br> 35 * <code> 36 * <pre> 37 * window["sap-ui2-shell-api-config"] = { 38 * domainRelaxation : { 39 * integrated : "auto", 40 * standalone : "none", 41 * maxrelax : 2 42 * } 43 * } 44 *</pre> 45 *</code> 46 * <p> 47 * The default behavior is: 48 * <ul> 49 * <li>integrated: "auto"</li> 50 * <li>standalone: "minimal"</li> 51 * <li>maxrelax: 2</li> 52 * </ul> 53 * This means that when including the script without configuration, domain relaxation of 1 level is 54 * usually applied. This is also the default for all versions before 1.26.3.<br> 55 * <p> 56 * <b>Note:</b> Including this file automatically transports the user's settings from an ABAP 57 * backend to SAPUI5, e.g. preferred language incl. its right-to-left status, date, time, and 58 * number formats, theme and possibly others (in future). For this to take proper effect, this file 59 * <b>must</b> be included <b>before</b> the SAPUI5 bootstrap. <b>Beware!</b> This has an effect 60 * on SAPUI5's bootstrap sequence with two known consequences: 61 * <ol> 62 * <li> You must not refer to SAPUI5 code other than <code>sap.ui.getCore().attachInit()</code> 63 * until SAPUI5's core is properly initialized. 64 * <li> You cannot include style sheets directly from the HTML page and override SAPUI5 style 65 * classes in them, because SAPUI5's bootstrap is delayed and their standard style sheets are 66 * included only <b>after</b> the page has loaded completely. Use 67 * <code>jQuery.sap.includeStyleSheet()</code> from your code and obey the first rule. 68 * <b>Override SAPUI5 style classes with caution!</b> Be sure not to break the built-in theming 69 * support, e.g. by hard-coding custom colors. 70 * </ol> 71 */ 72 73 this.sap = this.sap || {}; 74 75 // Error (from services/sap/ui2/srvc/error.js) ********************************* 76 // Copyright (c) 2009-2014 SAP SE, All Rights Reserved 77 /** 78 * @fileOverview An error object which logs the error message immediately. 79 */ 80 81 this.sap = this.sap || {}; 82 83 (function () { 84 "use strict"; 85 /*global jQuery, sap */ 86 87 // namespace "sap.ui2.srvc" ************************************************** 88 sap.ui2 = sap.ui2 || {}; 89 sap.ui2.srvc = sap.ui2.srvc || {}; 90 91 // Note: jQuery might not yet be available! 92 if (typeof jQuery === 'function' && jQuery.sap) { 93 jQuery.sap.declare("sap.ui2.srvc.error"); 94 } 95 96 /** 97 * Creates an <code>Error</code> object and logs the error message immediately. 98 * 99 * @param {string} sMessage 100 * the error message 101 * @param {string} [sComponent] 102 * the error component to log 103 * 104 * @class An error that is written to the log. 105 * @constructor 106 * @since 1.2.0 107 */ 108 sap.ui2.srvc.Error = function (sMessage, sComponent) { 109 // see also redundant declaration in utils.js which has to be in sync 110 var that = new Error(sMessage); // reuse Error constructor to benefit from it (e.g. stack) 111 that.name = "sap.ui2.srvc.Error"; 112 sap.ui2.srvc.log.error(sMessage, null, sComponent); 113 return that; 114 }; 115 // to avoid (new Error()) instanceof sap.ui2.srvc.Error === true we do not set the prototype, 116 // we also tolerate that (new sap.ui2.srvc.Error()) instanceof sap.ui2.srvc.Error === false now 117 // sap.ui2.srvc.Error.prototype = Error.prototype; 118 119 }()); 120 121 // Utils (from services/sap/ui2/srvc/utils.js) ********************************* 122 // Copyright (c) 2009-2014 SAP SE, All Rights Reserved 123 /** 124 * @fileOverview This file contains miscellaneous utility functions. 125 */ 126 127 this.sap = this.sap || {}; 128 129 (function () { 130 "use strict"; 131 /*global window,console, DOMParser, jQuery, location, sap, setTimeout, URI, XMLHttpRequest */ 132 133 // ensure that Function.prototype.bind is available, even with iOS 5 134 // utils.js is used with startup service, shell API and page building services 135 if (!Function.prototype.bind) { 136 /** 137 * Replacement for ECMAScript 5 feature which might still be missing. 138 * 139 * @param {object} oThis 140 * The value to be passed as the <code>this</code> parameter to the target 141 * function when the bound function is called. The value is ignored if the 142 * bound function is constructed using the <code>new</code> operator. 143 * @param {...object} aVarArgs 144 * Arguments to prepend to arguments provided to the bound function when 145 * invoking the target function. 146 * 147 * @see <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind">bind in ECMAScript 5</a> 148 */ 149 Function.prototype.bind = function (oThis) { 150 if (typeof this !== "function") { 151 // closest thing possible to the ECMAScript 5 internal IsCallable function 152 throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); 153 } 154 155 var aArgs = Array.prototype.slice.call(arguments, 1), 156 fToBind = this, 157 /** @ignore */ // no, JSDoc, this is not a global function! 158 NOP = function () {/* no-op c'tor */}, 159 /** @ignore */ // no, JSDoc, this is not a global function! 160 fBound = function () { 161 return fToBind.apply( 162 // passing "window" as "this" has been removed (cf. "use strict";) 163 this instanceof NOP ? this : oThis, 164 aArgs.concat(Array.prototype.slice.call(arguments)) 165 ); 166 }; 167 NOP.prototype = this.prototype; 168 fBound.prototype = new NOP(); 169 return fBound; 170 }; 171 } 172 173 // namespace "sap.ui2.srvc" ************************************************** 174 sap.ui2 = sap.ui2 || {}; 175 sap.ui2.srvc = sap.ui2.srvc || {}; 176 if (sap.ui2.srvc.log) { 177 return; // It's OK. Don't load twice. 178 } 179 180 // cache for GET requests 181 var oCache; 182 183 if (typeof jQuery === 'function' && jQuery.sap) { 184 jQuery.sap.declare("sap.ui2.srvc.utils"); 185 } 186 187 // "private static" methods ************************************************** 188 189 /** 190 * Tells whether the package <code>jQuery.sap.log</code> currently exists. 191 * 192 * @returns {boolean} 193 */ 194 function jQuerySapLogExists() { 195 return typeof jQuery === 'function' && jQuery.sap && jQuery.sap.log; 196 } 197 198 /** 199 * Formats the message for a simple output to <code>window.console</code>. 200 * Mimics SAPUI5 log behavior for the three last parts: 201 * <code> 202 * var logText = oLogEntry.date + " " + oLogEntry.time + " " + sWindowName + 203 * oLogEntry.message + " - " + oLogEntry.details + " " + oLogEntry.component; 204 * </code> 205 */ 206 function formatMessage(sMessage, sDetails, sComponent) { 207 return (sMessage || "") + " - " + (sDetails || "") + " " + (sComponent || ""); 208 } 209 210 // "public static" methods *************************************************** 211 212 /** 213 * @namespace The namespace for functions which log messages even if SAPUI5 is not present. 214 * @since 1.3.0 215 */ 216 sap.ui2.srvc.log = { 217 /** 218 * Wrapper function for <code>jQuery.sap.log.debug()</code>. Writes a simple log message to 219 * the console if SAPUI5 is not present. 220 * 221 * @param {string} sMessage 222 * the log message 223 * @param {string} sDetails 224 * the message details 225 * @param {string} sComponent 226 * the component which issues the message 227 * @since 1.3.0 228 */ 229 debug: function (sMessage, sDetails, sComponent) { 230 if (jQuerySapLogExists()) { 231 jQuery.sap.log.debug(sMessage, sDetails, sComponent); 232 return; 233 } 234 if (typeof console === "object") { 235 if (typeof console.debug === "function") { // e.g. Chrome 236 console.debug(formatMessage(sMessage, sDetails, sComponent)); 237 } else { // e.g. IE9 238 console.log(formatMessage(sMessage, sDetails, sComponent)); 239 } 240 } 241 }, 242 243 /** 244 * Wrapper function for <code>jQuery.sap.log.error()</code>. Writes a simple error message to 245 * the console if SAPUI5 is not present. 246 * 247 * @param {string} sMessage 248 * the log message 249 * @param {string} sDetails 250 * the message details 251 * @param {string} sComponent 252 * the component which issues the message 253 * @since 1.3.0 254 */ 255 error: function (sMessage, sDetails, sComponent) { 256 if (jQuerySapLogExists()) { 257 jQuery.sap.log.error(sMessage, sDetails, sComponent); 258 return; 259 } 260 if (typeof console === "object") { 261 console.error(formatMessage(sMessage, sDetails, sComponent)); 262 } 263 }, 264 265 /** 266 * Wrapper function for <code>jQuery.sap.log.info()</code>. Writes a simple info message to 267 * the console if SAPUI5 is not present. 268 * 269 * @param {string} sMessage 270 * the log message 271 * @param {string} sDetails 272 * the message details 273 * @param {string} sComponent 274 * the component which issues the message 275 * @since 1.3.0 276 */ 277 info: function (sMessage, sDetails, sComponent) { 278 if (jQuerySapLogExists()) { 279 jQuery.sap.log.info(sMessage, sDetails, sComponent); 280 return; 281 } 282 if (typeof console === "object") { 283 console.info(formatMessage(sMessage, sDetails, sComponent)); 284 } 285 }, 286 287 /** 288 * Wrapper function for <code>jQuery.sap.log.warning()</code>. Writes a simple warning message 289 * to the console if SAPUI5 is not present. 290 * 291 * @param {string} sMessage 292 * the log message 293 * @param {string} sDetails 294 * the message details 295 * @param {string} sComponent 296 * the component which issues the message 297 * @since 1.3.0 298 */ 299 warning: function (sMessage, sDetails, sComponent) { 300 if (jQuerySapLogExists()) { 301 jQuery.sap.log.warning(sMessage, sDetails, sComponent); 302 return; 303 } 304 if (typeof console === "object") { 305 console.warn(formatMessage(sMessage, sDetails, sComponent)); 306 } 307 } 308 }; 309 310 /** 311 * Makes the given relative URL absolute. URLs containing host and/or protocol 312 * and URLs with an absolute path remain unchanged. The URL is in no way 313 * normalized; the function simply cuts off the file name from the base and 314 * appends the relative URL. 315 * 316 * @param {string} sUrl 317 * the (possibly server-relative) URL 318 * @param {string} [sBase=location.href] 319 * the base URL; it <b>must</b> at least be server-absolute 320 * @returns {string} 321 * the absolute URL 322 * @since 1.2.0 323 */ 324 sap.ui2.srvc.absoluteUrl = function (sUrl, sBase) { 325 /*jslint regexp: true */ 326 327 // default base is the page location 328 sBase = sBase || location.href; 329 // base must be absolute 330 if (sBase.indexOf('://') < 0 && sBase.charAt(0) !== '/') { 331 throw new sap.ui2.srvc.Error("Illegal base URL: " + sBase, "sap.ui2.srvc"); 332 } 333 // do not change empty or absolute URL 334 if (!sUrl || sUrl.indexOf('://') >= 0 || sUrl.charAt(0) === '/') { 335 return this.addCacheBusterTokenUsingUshellConfig(sUrl); 336 } 337 if (sBase.search(/^([^:]*:)?\/\/[^\/]+$/) < 0) { 338 // not a pure server URL -> cut off the file name 339 sBase = sBase.replace(/\/[^\/]*$/, ''); 340 } 341 // append the relative path 342 return this.addCacheBusterTokenUsingUshellConfig(sBase + '/' + sUrl); 343 }; 344 345 /** 346 * Calls the given success handler (a)synchronously. Errors thrown in the success handler are 347 * caught and the error message is reported to the error handler; if an error stack is 348 * available, it is logged. 349 * 350 * @param {function ()} fnSuccess 351 * no-args success handler 352 * @param {function (string)} [fnFailure] 353 * error handler, taking an error message; MUST NOT throw any error itself! 354 * @param {boolean} [bAsync=false] 355 * whether the call shall be asynchronously 356 * @since 1.2.0 357 */ 358 sap.ui2.srvc.call = function (fnSuccess, fnFailure, bAsync) { 359 // see also redundant declaration in sap.ushell.utils.call which has to be in sync 360 var sMessage; 361 362 if (bAsync) { 363 setTimeout(function () { 364 sap.ui2.srvc.call(fnSuccess, fnFailure, false); 365 }, 0); 366 return; 367 } 368 369 try { 370 fnSuccess(); 371 } catch (e) { 372 sMessage = e.message || e.toString(); 373 sap.ui2.srvc.log.error("Call to success handler failed: " + sMessage, 374 e.stack, //may be undefined: only supported in Chrome, FF; as of now not in Safari, IE 375 "sap.ui2.srvc"); 376 if (fnFailure) { 377 fnFailure(sMessage); 378 } 379 } 380 }; 381 382 /** 383 * GETs the given URL (as XML if indicated) and hands it to the given 384 * success handler. As this is a root cause for asynchronous behaviour, 385 * special precautions are taken: errors thrown in the success handler are 386 * caught and reported to the error handler! 387 * 388 * @param {string} sUrl 389 * URL for GET request 390 * @param {boolean} bXml 391 * whether the handler expects XML instead of plain text 392 * @param {function (DOMDocument|string)} fnSuccess 393 * success handler, taking a DOM document or text string 394 * @param {function (string, string)} fnFailure 395 * error handler, taking an error message and (if http status is not OK) the GET response as 396 * text; MUST NOT throw any error itself! 397 * @param {object} [oXHR] 398 * the XMLHttpRequest object which may be predefined (e.g. by setting request headers). If 399 * <code>undefined</code>, a new XMLHttpRequest object is created. 400 * @param {boolean} [bCache] 401 * whether the response is cached for further calls (since 1.8.1). XML responses cannot be 402 * cached. An <code>sap.ui2.srvc.Error</code> is thrown if both <code>bXml</code> and 403 * <code>bCache</code> are set to <code>true</code>. 404 * @since 1.2.0 405 */ 406 sap.ui2.srvc.get = function (sUrl, bXml, fnSuccess, fnFailure, oXHR, bCache) { 407 if (typeof fnSuccess !== "function") { 408 throw new sap.ui2.srvc.Error("Missing success handler", "sap.ui2.srvc"); 409 } 410 if (typeof fnFailure !== "function") { 411 throw new sap.ui2.srvc.Error("Missing error handler", "sap.ui2.srvc"); 412 } 413 if (bXml && bCache) { 414 throw new sap.ui2.srvc.Error("Caching of XML responses not supported", "sap.ui2.srvc"); 415 } 416 if (typeof sap.ui2.srvc.addCacheBusterTokenUsingUshellConfig === "function") { 417 sUrl = sap.ui2.srvc.addCacheBusterTokenUsingUshellConfig(sUrl); 418 } 419 oXHR = oXHR || new XMLHttpRequest(); 420 421 /** 422 * @private 423 */ 424 oXHR.onreadystatechange = function () { 425 var oResult, oXml; 426 // Note: "this" refers to oXHR according to W3C 427 if (this.readyState !== /*DONE*/4) { 428 return; // not yet DONE 429 } 430 sap.ui2.srvc.get.pending -= 1; 431 if (this.status !== /*OK*/200) { 432 // HTTP status not OK 433 sap.ui2.srvc.log.error("Error " + this.status + " in response for URL " + sUrl, 434 null, "sap.ui2.srvc"); 435 fnFailure(sUrl + ": " + this.status + " " + this.statusText, this.responseText); 436 return; 437 } 438 439 sap.ui2.srvc.log.debug("Received response for URL " + sUrl, null, "sap.ui2.srvc"); 440 if (bXml) { 441 oXml = this.responseXML; 442 if (oXml === null || !oXml.documentElement) { 443 // in FF it is null, in IE it is a document with only an error message 444 fnFailure(sUrl + ": no valid XML"); 445 return; 446 } 447 oResult = oXml; 448 } else { 449 oResult = this.responseText; 450 if (bCache) { 451 oCache.put(sUrl, oResult); 452 } 453 } 454 sap.ui2.srvc.call(fnSuccess.bind(null, oResult), fnFailure); 455 }; 456 457 if (!bXml && oCache.containsKey(sUrl)) { 458 sap.ui2.srvc.log.debug("Return cached response for URL " + sUrl, null, "sap.ui2.srvc"); 459 sap.ui2.srvc.call(fnSuccess.bind(null, oCache.get(sUrl)), fnFailure); 460 } else { 461 try { 462 oXHR.open("GET", sUrl, /*asynchronously*/true); 463 oXHR.send(); 464 sap.ui2.srvc.get.pending += 1; 465 sap.ui2.srvc.log.debug("Sent request to URL " + sUrl, null, "sap.ui2.srvc"); 466 } catch (e) { 467 sap.ui2.srvc.log.error("Error '" + (e.message || e) + "' in request to URL " + sUrl, 468 null, "sap.ui2.srvc"); 469 throw e; 470 } 471 } 472 }; 473 474 /** 475 * Gets an URL and adds the given cache buster token to it if no other token is already 476 * contained. In case the URL is no valid URL the token is not added. 477 * 478 * @param {string} sUrl 479 * e.g. "/sap/bc/ui5_ui5/application/path" 480 * URL to be changed 481 * @param {regEx} oPattern 482 * e.g /^\/sap\/bc\/ui5_ui5\// 483 * RegExp to determine if sUrl matches and needs to be extended by the cache buster token 484 * sToken 485 * @param {string} sReplacement 486 * e.g. "/sap/bc/ui5_ui5/[CacheBusterToken]/" 487 * The part of sUrl matched by oPattern will be exchanged by this. Before that is done sToken 488 * is inserted in sReplacement at the position indicated by [CacheBusterToken] 489 * The replacement may refer to capture groups of oPattern 490 * @param {string} sToken 491 * e.g. "~201412132350000~" 492 * token to be inserted in sUrl. It will be inserted as indicated in the final constructed URL! 493 * @returns {string} 494 * - if sUrl did not matched oPattern: unchanged sUrl 495 * - if sUrl matched oPattern: sUrl enhanced with sToken, 496 * e.g. "/sap/bc/ui5_ui5/~201412132350000~/application/path" 497 * 498 * @private 499 */ 500 sap.ui2.srvc.addCacheBusterToken = function (sUrl, oPattern, sReplacement, sToken) { 501 if (oPattern.test(sUrl)) { //url matches the pattern 502 sUrl = sUrl.replace(oPattern, sReplacement); 503 //replace the token placeholder globally in the final url (!) 504 // (also allow the token to be added elsewhere) 505 sUrl = sUrl.replace(/\[CacheBusterToken\]/g, sToken); 506 } 507 return sUrl; 508 }; 509 510 /** 511 * Removes a cache buster token (if available) of an Url and normalizes the url afterwards 512 * @param {string} sUrl 513 * @returns {string} 514 * normalized url (without a cache buster token) 515 * @since 1.28.1 516 * 517 * @private 518 */ 519 sap.ui2.srvc.removeCBAndNormalizeUrl = function (sUrl) { 520 var oUrl, 521 sUrlModified = "/"; 522 523 if (typeof sUrl !== "string" || sUrl === "") { 524 return sUrl; 525 } 526 527 // TODO URI may not be defined in use cases without UI5 528 oUrl = new URI(sUrl); 529 if (/\/~[\w\-]+~[A-Z0-9]?\//.test(sUrl)) { 530 // Url contains a cache buster token 531 // 1) Removing the cache buster token 532 // 1.1) Splitting the url into its path segments 533 // 1.2) Removing the segment representing the cache buster token 534 oUrl.segment().forEach(function (sSegment) { 535 if (!(/~[\w\-]+~[A-Z0-9]?/.test(sSegment))) { 536 // Building url 537 sUrlModified += sSegment + "/"; 538 } 539 }); 540 541 // Removing the last slash of the built url 542 if (sUrlModified.charAt(sUrlModified.length - 1) === "/") { 543 sUrlModified = sUrlModified.substr(0, sUrlModified.length - 1); 544 } 545 } else { 546 sUrlModified = sUrl; 547 } 548 549 // Normalizing url 550 oUrl = new URI(sUrlModified); 551 oUrl.normalizePathname(); 552 553 return oUrl.path(); 554 }; 555 556 /** 557 * Gets an URL and adds the given cache buster token to it if no other token is already 558 * contained. The rules to be applied are coming from the ushell configuration: 559 * sap-ushell-config.cacheBusting.patterns 560 * The rules are applied by there order property (lowest first) and the modified URL is returned 561 * as soon as the first rule matched. 562 * 563 * @param {string} sUrl 564 * e.g. "/sap/bc/ui5_ui5/application/path" 565 * URL to be changed 566 * @returns {string} 567 * - if sUrl already contained a cache buster token (e.g. ~00000~): unchanged sUrl 568 * - if sUrl did not match any pattern: unchanged sUrl 569 * - if sUrl matched pattern: sUrl enhanced with sToken, 570 * e.g. "/sap/bc/ui5_ui5/~201412132350000~/application/path" 571 * - if the modified sUrl (normalized and cache buster token was removed) 572 * is found as an attribute of the config 573 * (window["sap-ushell-config"].cacheBusting.urls), 574 * the cache buster token which is defined as the value of this attribute 575 * is going to be returned. 576 * 577 * @private 578 */ 579 sap.ui2.srvc.addCacheBusterTokenUsingUshellConfig = function (sUrl) { 580 //TODO move to sap.ushell.utils 581 var oCacheBusting = window["sap-ushell-config"] && 582 window["sap-ushell-config"].cacheBusting, 583 oPatterns = oCacheBusting && oCacheBusting.patterns, 584 sCacheBusterUrl = sUrl, 585 aParameterMap = [], 586 sSapUshellNoCb, 587 aRules = []; 588 589 aParameterMap = sap.ui2.srvc.getParameterMap(); 590 sSapUshellNoCb = aParameterMap["sap-ushell-nocb"] && aParameterMap["sap-ushell-nocb"][0]; 591 592 // When URL disables Cache Busting return URL without cache busting token 593 if (sSapUshellNoCb === 'true' || sSapUshellNoCb === 'X') { 594 return sUrl; 595 } 596 597 // don't continue if the string is empty or a token is already present, 598 // either as token (e.g.: /~0123_-Abc~/) 599 // this case happens during navigation, because this method is both called from 600 // NavTargetResolution service as well as from the stubbed jQuery.sap.registerModulePath method 601 // or as =~xxxxxx~ 602 // syntax for application cache-buster contains now an additional scope qualifier that can be 603 // either empty, "R" for resource, "5" for UI5 app, "W" for web app and "C" for custom 604 // see ABAP class /UI5/CL_UI5_APP_HTTP_HANDLER for details 605 if (!oCacheBusting 606 || typeof sUrl !== "string" 607 || sUrl === "" 608 || /\/~[\w\-]+~[A-Z0-9]?\//.test(sUrl) // matches intermediate segment with cb-token 609 || /\/~[\w\-]+~[A-Z0-9]?$/.test(sUrl)) { // matches last segment with cb-token (no trailing slash) 610 return sUrl; 611 } 612 613 if (oCacheBusting && oCacheBusting.urls) { 614 // Removing the last slash of the input url 615 if (sUrl.charAt(sUrl.length - 1) === "/") { 616 sUrl = sUrl.substr(0, sUrl.length - 1); 617 } 618 // Config contains the modified url (without a slash at the end) 619 if (oCacheBusting.urls.hasOwnProperty(sUrl)) { 620 return sUrl + "/" + oCacheBusting.urls[sUrl].cacheBusterToken; 621 } 622 // Config contains the modified url (having a slash at the end) 623 if (oCacheBusting.urls.hasOwnProperty(sUrl + "/")) { 624 return sUrl + "/" + oCacheBusting.urls[sUrl + "/"].cacheBusterToken; 625 } 626 } 627 628 if (!oPatterns) { 629 return sUrl; 630 } 631 632 // put rules in aRules and sort them by oRule.order 633 Object.keys(oPatterns).forEach(function (sPattern) { 634 if (oPatterns.hasOwnProperty(sPattern)) { 635 var oRule = oPatterns[sPattern]; 636 // the property name is the pattern to be used, copy it to the object itself for later 637 oRule.pattern = new RegExp(sPattern); 638 aRules.push(oRule); 639 } 640 }); 641 aRules.sort(function (oRule1, oRule2) { return oRule1.order - oRule2.order; }); 642 643 // apply rules 644 aRules.every(function (oRule) { // use every to be able to break 645 if (oRule.pattern.test(sUrl)) { 646 if (!oRule.cacheBusterToken) { 647 oRule.cacheBusterToken = oCacheBusting.cacheBusterToken; 648 } 649 650 //url matches the pattern, note that this is not redundant 651 //one can define patterns without a replacement to match and end the matching process! 652 sCacheBusterUrl = sap.ui2.srvc.addCacheBusterToken(sUrl, oRule.pattern, oRule.replacement, 653 oRule.cacheBusterToken); 654 // break as soon as first rule matches (irrespective of alteration) 655 return false; 656 } 657 return true; 658 }); 659 660 return sCacheBusterUrl; 661 }; 662 663 /** 664 * Clear cache for GET requests. 665 * 666 * @since 1.8.1 667 */ 668 sap.ui2.srvc.get.clearCache = function () { 669 oCache = new sap.ui2.srvc.Map(); 670 }; 671 672 /** 673 * Number of pending XHR requests. 674 * 675 * @type {number} 676 */ 677 sap.ui2.srvc.get.pending = 0; 678 679 /** 680 * Gets the device's form factor. Based on <code>sap.ui.Device.system</code> from SAPUI5. 681 * @returns {string} 682 * the device's form factor ("desktop", "tablet" or "phone") 683 * @since 1.19.1 684 */ 685 sap.ui2.srvc.getFormFactor = function () { 686 // see also redundant declaration in sap.ushell.utils.getFormFactor which has to be in sync 687 var oSystem = sap.ui.Device.system; 688 689 return oSystem.desktop ? oSystem.SYSTEMTYPE.DESKTOP : 690 (oSystem.tablet ? oSystem.SYSTEMTYPE.TABLET : 691 (oSystem.phone ? oSystem.SYSTEMTYPE.PHONE : undefined)); 692 }; 693 694 /** 695 * Returns a map of all search parameters present in the given search string 696 * or this window's current URL. To be precise, <code>location.search</code> 697 * is used as a default and any given search string must use the same syntax 698 * (start with a "?" and not include a "#"). 699 * 700 * @param {string} [sSearchString=location.search] 701 * search string starting with a "?" (unless empty) and not including a "#" 702 * @returns {object} 703 * a <code>map<string, string[]></code> from key to array of values 704 * @since 1.2.0 705 * 706 * @see <a href="http://java.sun.com/javaee/5/docs/api/javax/servlet/ServletRequest.html#getParameterMap()"> 707 * javax.servlet.ServletRequest#getParameterMap()</a> 708 * @see <a href="https://sapui5.hana.ondemand.com/sdk/docs/api/symbols/jQuery.sap.util.UriParameters.html"> 709 * Interface jQuery.sap.util.UriParameters</a> 710 */ 711 sap.ui2.srvc.getParameterMap = function (sSearchString) { 712 var i, 713 n, 714 mResult = {}, 715 sKey, 716 sValue, 717 iIndexOfEquals, 718 aKeyValuePairs, 719 // Note: location.search starts with "?" if not empty 720 sSearch = arguments.length > 0 ? sSearchString : location.search; 721 722 if (sSearch && sSearch.charAt(0) !== "?") { 723 throw new sap.ui2.srvc.Error("Illegal search string " + sSearch, "sap.ui2.srvc"); 724 } 725 if (!sSearch || sSearch === "?") { 726 return {}; // Note: split("") would return [""] 727 } 728 729 // Note: W3C recommends that servers support ";" as well as "&" 730 // (http://www.w3.org/TR/1999/REC-html401-19991224/appendix/notes.html#h-B.2.2) 731 // http://unixpapa.com/js/querystring.html advocates this on the client-side also! 732 aKeyValuePairs = sSearch.substring(1).replace(/\+/g, ' ').split(/[&;]/); 733 734 for (i = 0, n = aKeyValuePairs.length; i < n; i += 1) { 735 // decode key/value pair at first "=" character 736 sKey = aKeyValuePairs[i]; 737 sValue = ""; // Note: empty value may be omitted altogether 738 iIndexOfEquals = sKey.indexOf("="); 739 if (iIndexOfEquals >= 0) { 740 sValue = sKey.slice(iIndexOfEquals + 1); 741 sValue = decodeURIComponent(sValue); 742 sKey = sKey.slice(0, iIndexOfEquals); 743 } 744 sKey = decodeURIComponent(sKey); 745 746 // map key to value(s) 747 // Note: beware of inherited functions! 748 if (!Object.prototype.hasOwnProperty.call(mResult, sKey)) { 749 mResult[sKey] = []; 750 } 751 mResult[sKey].push(sValue); 752 } 753 754 return mResult; 755 }; 756 757 /** 758 * Returns the value of the given URL's GET parameter with the given name, properly decoded. 759 * Returns "" if no such parameter can be found. 760 * 761 * @param {string} sUrl 762 * any URL 763 * @param {string} sName 764 * the name of the GET parameter we are looking for 765 * @returns {string} 766 * the parameter value, properly decoded 767 * 768 * @private 769 * @since 1.17.0 770 */ 771 sap.ui2.srvc.getParameterValue = function (sUrl, sName) { 772 var oParameterMap, iQueryIndex; 773 774 if (typeof sName !== "string") { 775 // avoid surprises when sName would later be converted into a string 776 throw new sap.ui2.srvc.Error("Missing parameter name", "sap.ui2.srvc"); 777 } 778 779 sUrl = sUrl.split('#')[0]; 780 iQueryIndex = sUrl.indexOf("?"); 781 if (iQueryIndex >= 0) { 782 oParameterMap = sap.ui2.srvc.getParameterMap(sUrl.slice(iQueryIndex)); 783 if (oParameterMap[sName]) { 784 return oParameterMap[sName][0]; 785 } 786 } 787 return ""; 788 }; 789 790 /** 791 * Tells whether the given value is an array. 792 * 793 * @param {object} o 794 * any value 795 * @returns {boolean} 796 * <code>true</code> if and only if the given value is an array 797 * @since 1.2.0 798 */ 799 sap.ui2.srvc.isArray = function (o) { 800 // see Crockford page 61 801 return Object.prototype.toString.apply(o) === '[object Array]'; 802 }; 803 804 /** 805 * Parses the given XML string and returns it as a document. 806 * 807 * @param {string} sXml 808 * the XML 809 * @returns {DOMDocument} 810 * a DOM document, or <code>null</code> in case of missing or empty XML string 811 * @throws {Error} 812 * in case of invalid XML string 813 * @since 1.2.0 814 */ 815 sap.ui2.srvc.parseXml = function (sXml) { 816 var oXml; 817 if (!sXml || typeof sXml !== "string") { 818 return null; 819 } 820 oXml = new DOMParser().parseFromString(sXml, "text/xml"); 821 if (oXml.getElementsByTagName("parsererror").length) { // Chrome, Firefox 822 throw new sap.ui2.srvc.Error("Invalid XML: " + sXml, "sap.ui2.srvc"); 823 } 824 return oXml; 825 }; 826 827 /** 828 * Serves as a marker for functions that are to be exposed in QUnit tests. Calls to this function 829 * are expected to be placed directly before the named function declaration (even <b>after</b> 830 * the JSDoc). The function itself does nothing. 831 * 832 * @param {object} o 833 * the object to which this function will be attached in tests; must not be <code>this</code> 834 * (use <code>that</code> instead) 835 * @since 1.3.0 836 */ 837 sap.ui2.srvc.testPublishAt = function (o) { 838 // intentionally left blank 839 }; 840 841 // "public classes" ********************************************************** 842 843 if (sap.ui2.srvc.Error === undefined) { 844 sap.ui2.srvc.Error = function (sMessage, sComponent) { 845 // see also redundant declaration in error.js which has to be in sync 846 var that = new Error(sMessage); // reuse Error constructor to benefit from it (e.g. stack) 847 that.name = "sap.ui2.srvc.Error"; 848 sap.ui2.srvc.log.error(sMessage, null, sComponent); 849 return that; 850 }; 851 // to avoid (new Error()) instanceof sap.ui2.srvc.Error === true we do not set the prototype, 852 // we also tolerate that (new sap.ui2.srvc.Error()) instanceof sap.ui2.srvc.Error === false now 853 // sap.ui2.srvc.Error.prototype = Error.prototype; 854 } 855 856 /** 857 * Creates an empty map. 858 * @class A mapping from arbitrary string(!) keys (including "get" or "hasOwnProperty") to values 859 * of any type. 860 * @since 1.5.0 861 */ 862 sap.ui2.srvc.Map = function () { 863 this.entries = {}; 864 }; 865 866 /** 867 * Associates the specified value with the specified key in this map. If the map previously 868 * contained a