1/** 2 * @license AngularJS v1.8.2
vendor: 25,829 bytes, lines 2-692
2 3 * (c) 2010-2020 Google LLC. http://angularjs.org 4 * License: MIT 5 */ 6(function(window, angular) {'use strict'; 7 8/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 9 * Any commits to this file should be reviewed with security in mind. * 10 * Changes to this file can potentially create security vulnerabilities. * 11 * An approval from 2 Core members with history of modifying * 12 * this file is required. * 13 * * 14 * Does the change somehow allow for arbitrary javascript to be executed? * 15 * Or allows for someone to change the prototype of built-in objects? * 16 * Or gives undesired access to variables likes document or window? * 17 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 18 19var $sanitizeMinErr = angular.$$minErr('$sanitize'); 20var bind; 21var extend; 22var forEach; 23var isArray; 24var isDefined; 25var lowercase; 26var noop; 27var nodeContains; 28var htmlParser; 29var htmlSanitizeWriter; 30 31/** 32 * @ngdoc module 33 * @name ngSanitize 34 * @description 35 * 36 * The `ngSanitize` module provides functionality to sanitize HTML. 37 * 38 * See {@link ngSanitize.$sanitize `$sanitize`} for usage. 39 */ 40 41/** 42 * @ngdoc service 43 * @name $sanitize 44 * @kind function 45 * 46 * @description 47 * Sanitizes an html string by stripping all potentially dangerous tokens. 48 * 49 * The input is sanitized by parsing the HTML into tokens. All safe tokens (from a trusted URI list) are 50 * then serialized back to a properly escaped HTML string. This means that no unsafe input can make 51 * it into the returned string. 52 * 53 * The trusted URIs for URL sanitization of attribute values is configured using the functions 54 * `aHrefSanitizationTrustedUrlList` and `imgSrcSanitizationTrustedUrlList` of {@link $compileProvider}. 55 * 56 * The input may also contain SVG markup if this is enabled via {@link $sanitizeProvider}. 57 * 58 * @param {string} html HTML input. 59 * @returns {string} Sanitized HTML. 60 * 61 * @example 62 <example module="sanitizeExample" deps="angular-sanitize.js" name="sanitize-service"> 63 <file name="index.html"> 64 <script> 65 angular.module('sanitizeExample', ['ngSanitize']) 66 .controller('ExampleController', ['$scope', '$sce', function($scope, $sce) { 67 $scope.snippet = 68 '<p style="color:blue">an html\n' + 69 '<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' + 70 'snippet</p>'; 71 $scope.deliberatelyTrustDangerousSnippet = function() { 72 return $sce.trustAsHtml($scope.snippet); 73 }; 74 }]); 75 </script> 76 <div ng-controller="ExampleController"> 77 Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea> 78 <table> 79 <tr> 80 <td>Directive</td> 81 <td>How</td> 82 <td>Source</td> 83 <td>Rendered</td> 84 </tr> 85 <tr id="bind-html-with-sanitize"> 86 <td>ng-bind-html</td> 87 <td>Automatically uses $sanitize</td> 88 <td><pre><div ng-bind-html="snippet"><br/></div></pre></td> 89 <td><div ng-bind-html="snippet"></div></td> 90 </tr> 91 <tr id="bind-html-with-trust"> 92 <td>ng-bind-html</td> 93 <td>Bypass $sanitize by explicitly trusting the dangerous value</td> 94 <td> 95 <pre><div ng-bind-html="deliberatelyTrustDangerousSnippet()"> 96</div></pre> 97 </td> 98 <td><div ng-bind-html="deliberatelyTrustDangerousSnippet()"></div></td> 99 </tr> 100 <tr id="bind-default"> 101 <td>ng-bind</td> 102 <td>Automatically escapes</td> 103 <td><pre><div ng-bind="snippet"><br/></div></pre></td> 104 <td><div ng-bind="snippet"></div></td> 105 </tr> 106 </table> 107 </div> 108 </file> 109 <file name="protractor.js" type="protractor"> 110 it('should sanitize the html snippet by default', function() { 111 expect(element(by.css('#bind-html-with-sanitize div')).getAttribute('innerHTML')). 112 toBe('<p>an html\n<em>click here</em>\nsnippet</p>'); 113 }); 114 115 it('should inline raw snippet if bound to a trusted value', function() { 116 expect(element(by.css('#bind-html-with-trust div')).getAttribute('innerHTML')). 117 toBe("<p style=\"color:blue\">an html\n" + 118 "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + 119 "snippet</p>"); 120 }); 121 122 it('should escape snippet without any filter', function() { 123 expect(element(by.css('#bind-default div')).getAttribute('innerHTML')). 124 toBe("<p style=\"color:blue\">an html\n" + 125 "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + 126 "snippet</p>"); 127 }); 128 129 it('should update', function() { 130 element(by.model('snippet')).clear(); 131 element(by.model('snippet')).sendKeys('new <b onclick="alert(1)">text</b>'); 132 expect(element(by.css('#bind-html-with-sanitize div')).getAttribute('innerHTML')). 133 toBe('new <b>text</b>'); 134 expect(element(by.css('#bind-html-with-trust div')).getAttribute('innerHTML')).toBe( 135 'new <b onclick="alert(1)">text</b>'); 136 expect(element(by.css('#bind-default div')).getAttribute('innerHTML')).toBe( 137 "new <b onclick=\"alert(1)\">text</b>"); 138 }); 139 </file> 140 </example> 141 */ 142 143 144/** 145 * @ngdoc provider 146 * @name $sanitizeProvider 147 * @this 148 * 149 * @description 150 * Creates and configures {@link $sanitize} instance. 151 */ 152function $SanitizeProvider() { 153 var hasBeenInstantiated = false; 154 var svgEnabled = false; 155 156 this.$get = ['$$sanitizeUri', function($$sanitizeUri) { 157 hasBeenInstantiated = true; 158 if (svgEnabled) { 159 extend(validElements, svgElements); 160 } 161 return function(html) { 162 var buf = []; 163 htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) { 164 return !/^unsafe:/.test($$sanitizeUri(uri, isImage)); 165 })); 166 return buf.join(''); 167 }; 168 }]; 169 170 171 /** 172 * @ngdoc method 173 * @name $sanitizeProvider#enableSvg 174 * @kind function 175 * 176 * @description 177 * Enables a subset of svg to be supported by the sanitizer. 178 * 179 * <div class="alert alert-warning"> 180 * <p>By enabling this setting without taking other precautions, you might expose your 181 * application to click-hijacking attacks. In these attacks, sanitized svg elements could be positioned 182 * outside of the containing element and be rendered over other elements on the page (e.g. a login 183 * link). Such behavior can then result in phishing incidents.</p> 184 * 185 * <p>To protect against these, explicitly setup `overflow: hidden` css rule for all potential svg 186 * tags within the sanitized content:</p> 187 * 188 * <br> 189 * 190 * <pre><code> 191 * .rootOfTheIncludedContent svg { 192 * overflow: hidden !important; 193 * } 194 * </code></pre> 195 * </div> 196 * 197 * @param {boolean=} flag Enable or disable SVG support in the sanitizer. 198 * @returns {boolean|$sanitizeProvider} Returns the currently configured value if called 199 * without an argument or self for chaining otherwise. 200 */ 201 this.enableSvg = function(enableSvg) { 202 if (isDefined(enableSvg)) { 203 svgEnabled = enableSvg; 204 return this; 205 } else { 206 return svgEnabled; 207 } 208 }; 209 210 211 /** 212 * @ngdoc method 213 * @name $sanitizeProvider#addValidElements 214 * @kind function 215 * 216 * @description 217 * Extends the built-in lists of valid HTML/SVG elements, i.e. elements that are considered safe 218 * and are not stripped off during sanitization. You can extend the following lists of elements: 219 * 220 * - `htmlElements`: A list of elements (tag names) to extend the current list of safe HTML 221 * elements. HTML elements considered safe will not be removed during sanitization. All other 222 * elements will be stripped off. 223 * 224 * - `htmlVoidElements`: This is similar to `htmlElements`, but marks the elements as 225 * "void elements" (similar to HTML 226 * [void elements](https://rawgit.com/w3c/html/html5.1-2/single-page.html#void-elements)). These 227 * elements have no end tag and cannot have content. 228 * 229 * - `svgElements`: This is similar to `htmlElements`, but for SVG elements. This list is only 230 * taken into account if SVG is {@link ngSanitize.$sanitizeProvider#enableSvg enabled} for 231 * `$sanitize`. 232 * 233 * <div class="alert alert-info"> 234 * This method must be called during the {@link angular.Module#config config} phase. Once the 235 * `$sanitize` service has been instantiated, this method has no effect. 236 * </div> 237 * 238 * <div class="alert alert-warning"> 239 * Keep in mind that extending the built-in lists of elements may expose your app to XSS or 240 * other vulnerabilities. Be very mindful of the elements you add. 241 * </div> 242 * 243 * @param {Array<String>|Object} elements - A list of valid HTML elements or an object with one or 244 * more of the following properties: 245 * - **htmlElements** - `{Array<String>}` - A list of elements to extend the current list of 246 * HTML elements. 247 * - **htmlVoidElements** - `{Array<String>}` - A list of elements to extend the current list of 248 * void HTML elements; i.e. elements that do not have an end tag. 249 * - **svgElements** - `{Array<String>}` - A list of elements to extend the current list of SVG 250 * elements. The list of SVG elements is only taken into account if SVG is 251 * {@link ngSanitize.$sanitizeProvider#enableSvg enabled} for `$sanitize`. 252 * 253 * Passing an array (`[...]`) is equivalent to passing `{htmlElements: [...]}`. 254 * 255 * @return {$sanitizeProvider} Returns self for chaining. 256 */ 257 this.addValidElements = function(elements) { 258 if (!hasBeenInstantiated) { 259 if (isArray(elements)) { 260 elements = {htmlElements: elements}; 261 } 262 263 addElementsTo(svgElements, elements.svgElements); 264 addElementsTo(voidElements, elements.htmlVoidElements); 265 addElementsTo(validElements, elements.htmlVoidElements); 266 addElementsTo(validElements, elements.htmlElements); 267 } 268 269 return this; 270 }; 271 272 273 /** 274 * @ngdoc method 275 * @name $sanitizeProvider#addValidAttrs 276 * @kind function 277 * 278 * @description 279 * Extends the built-in list of valid attributes, i.e. attributes that are considered safe and are 280 * not stripped off during sanitization. 281 * 282 * **Note**: 283 * The new attributes will not be treated as URI attributes, which means their values will not be 284 * sanitized as URIs using `$compileProvider`'s 285 * {@link ng.$compileProvider#aHrefSanitizationTrustedUrlList aHrefSanitizationTrustedUrlList} and 286 * {@link ng.$compileProvider#imgSrcSanitizationTrustedUrlList imgSrcSanitizationTrustedUrlList}. 287 * 288 * <div class="alert alert-info"> 289 * This method must be called during the {@link angular.Module#config config} phase. Once the 290 * `$sanitize` service has been instantiated, this method has no effect. 291 * </div> 292 * 293 * <div class="alert alert-warning"> 294 * Keep in mind that extending the built-in list of attributes may expose your app to XSS or 295 * other vulnerabilities. Be very mindful of the attributes you add. 296 * </div> 297 * 298 * @param {Array<String>} attrs - A list of valid attributes. 299 * 300 * @returns {$sanitizeProvider} Returns self for chaining. 301 */ 302 this.addValidAttrs = function(attrs) { 303 if (!hasBeenInstantiated) { 304 extend(validAttrs, arrayToMap(attrs, true)); 305 } 306 return this; 307 }; 308 309 ////////////////////////////////////////////////////////////////////////////////////////////////// 310 // Private stuff 311 ////////////////////////////////////////////////////////////////////////////////////////////////// 312 313 bind = angular.bind; 314 extend = angular.extend; 315 forEach = angular.forEach; 316 isArray = angular.isArray; 317 isDefined = angular.isDefined; 318 lowercase = angular.$$lowercase; 319 noop = angular.noop; 320 321 htmlParser = htmlParserImpl; 322 htmlSanitizeWriter = htmlSanitizeWriterImpl; 323 324 nodeContains = window.Node.prototype.contains || /** @this */ function(arg) { 325 // eslint-disable-next-line no-bitwise 326 return !!(this.compareDocumentPosition(arg) & 16); 327 }; 328 329 // Regular Expressions for parsing tags and attributes 330 var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g, 331 // Match everything outside of normal chars and " (quote character) 332 NON_ALPHANUMERIC_REGEXP = /([^#-~ |!])/g; 333 334 335 // Good source of info about elements and attributes 336 // http://dev.w3.org/html5/spec/Overview.html#semantics 337 // http://simon.html5.org/html-elements 338 339 // Safe Void Elements - HTML5 340 // http://dev.w3.org/html5/spec/Overview.html#void-elements 341 var voidElements = stringToMap('area,br,col,hr,img,wbr'); 342 343 // Elements that you can, intentionally, leave open (and which close themselves) 344 // http://dev.w3.org/html5/spec/Overview.html#optional-tags 345 var optionalEndTagBlockElements = stringToMap('colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr'), 346 optionalEndTagInlineElements = stringToMap('rp,rt'), 347 optionalEndTagElements = extend({}, 348 optionalEndTagInlineElements, 349 optionalEndTagBlockElements); 350 351 // Safe Block Elements - HTML5 352 var blockElements = extend({}, optionalEndTagBlockElements, stringToMap('address,article,' + 353 'aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,' + 354 'h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul')); 355 356 // Inline Elements - HTML5 357 var inlineElements = extend({}, optionalEndTagInlineElements, stringToMap('a,abbr,acronym,b,' + 358 'bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,' + 359 'samp,small,span,strike,strong,sub,sup,time,tt,u,var')); 360 361 // SVG Elements 362 // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements 363 // Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted. 364 // They can potentially allow for arbitrary javascript to be executed. See #11290 365 var svgElements = stringToMap('circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,' + 366 'hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,' + 367 'radialGradient,rect,stop,svg,switch,text,title,tspan'); 368 369 // Blocked Elements (will be stripped) 370 var blockedElements = stringToMap('script,style'); 371 372 var validElements = extend({}, 373 voidElements, 374 blockElements, 375 inlineElements, 376 optionalEndTagElements); 377 378 //Attributes that have href and hence need to be sanitized 379 var uriAttrs = stringToMap('background,cite,href,longdesc,src,xlink:href,xml:base'); 380 381 var htmlAttrs = stringToMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' + 382 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' + 383 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' + 384 'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' + 385 'valign,value,vspace,width'); 386 387 // SVG attributes (without "id" and "name" attributes) 388 // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes 389 var svgAttrs = stringToMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' + 390 'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' + 391 'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' + 392 'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' + 393 'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' + 394 'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' + 395 'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' + 396 'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' + 397 'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' + 398 'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' + 399 'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' + 400 'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' + 401 'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' + 402 'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' + 403 'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true); 404 405 var validAttrs = extend({}, 406 uriAttrs, 407 svgAttrs, 408 htmlAttrs); 409 410 function stringToMap(str, lowercaseKeys) { 411 return arrayToMap(str.split(','), lowercaseKeys); 412 } 413 414 function arrayToMap(items, lowercaseKeys) { 415 var obj = {}, i; 416 for (i = 0; i < items.length; i++) { 417 obj[lowercaseKeys ? lowercase(items[i]) : items[i]] = true; 418 } 419 return obj; 420 } 421 422 function addElementsTo(elementsMap, newElements) { 423 if (newElements && newElements.length) { 424 extend(elementsMap, arrayToMap(newElements)); 425 } 426 } 427 428 /** 429 * Create an inert document that contains the dirty HTML that needs sanitizing. 430 * We use the DOMParser API by default and fall back to createHTMLDocument if DOMParser is not 431 * available. 432 */ 433 var getInertBodyElement /* function(html: string): HTMLBodyElement */ = (function(window, document) { 434 if (isDOMParserAvailable()) { 435 return getInertBodyElement_DOMParser; 436 } 437 438 if (!document || !document.implementation) { 439 throw $sanitizeMinErr('noinert', 'Can\'t create an inert html document'); 440 } 441 var inertDocument = document.implementation.createHTMLDocument('inert'); 442 var inertBodyElement = (inertDocument.documentElement || inertDocument.getDocumentElement()).querySelector('body'); 443 return getInertBodyElement_InertDocument; 444 445 function isDOMParserAvailable() { 446 try { 447 return !!getInertBodyElement_DOMParser(''); 448 } catch (e) { 449 return false; 450 } 451 } 452 453 function getInertBodyElement_DOMParser(html) { 454 // We add this dummy element to ensure that the rest of the content is parsed as expected 455 // e.g. leading whitespace is maintained and tags like `<meta>` do not get hoisted to the `<head>` tag. 456 html = '<remove></remove>' + html; 457 try { 458 var body = new window.DOMParser().parseFromString(html, 'text/html').body; 459 body.firstChild.remove(); 460 return body; 461 } catch (e) { 462 return undefined; 463 } 464 } 465 466 function getInertBodyElement_InertDocument(html) { 467 inertBodyElement.innerHTML = html; 468 469 // Support: IE 9-11 only 470 // strip custom-namespaced attributes on IE<=11 471 if (document.documentMode) { 472 stripCustomNsAttrs(inertBodyElement); 473 } 474 475 return inertBodyElement; 476 } 477 })(window, window.document); 478 479 /** 480 * @example 481 * htmlParser(htmlString, { 482 * start: function(tag, attrs) {}, 483 * end: function(tag) {}, 484 * chars: function(text) {}, 485 * comment: function(text) {} 486 * }); 487 * 488 * @param {string} html string 489 * @param {object} handler 490 */ 491 function htmlParserImpl(html, handler) { 492 if (html === null || html === undefined) { 493 html = ''; 494 } else if (typeof html !== 'string') { 495 html = '' + html; 496 } 497 498 var inertBodyElement = getInertBodyElement(html); 499 if (!inertBodyElement) return ''; 500 501 //mXSS protection 502 var mXSSAttempts = 5; 503 do { 504 if (mXSSAttempts === 0) { 505 throw $sanitizeMinErr('uinput', 'Failed to sanitize html because the input is unstable'); 506 } 507 mXSSAttempts--; 508 509 // trigger mXSS if it is going to happen by reading and writing the innerHTML 510 html = inertBodyElement.innerHTML; 511 inertBodyElement = getInertBodyElement(html); 512 } while (html !== inertBodyElement.innerHTML); 513 514 var node = inertBodyElement.firstChild; 515 while (node) { 516 switch (node.nodeType) { 517 case 1: // ELEMENT_NODE 518 handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes)); 519 break; 520 case 3: // TEXT NODE 521 handler.chars(node.textContent); 522 break; 523 } 524 525 var nextNode; 526 if (!(nextNode = node.firstChild)) { 527 if (node.nodeType === 1) { 528 handler.end(node.nodeName.toLowerCase()); 529 } 530 nextNode = getNonDescendant('nextSibling', node); 531 if (!nextNode) { 532 while (nextNode == null) { 533 node = getNonDescendant('parentNode', node); 534 if (node === inertBodyElement) break; 535 nextNode = getNonDescendant('nextSibling', node); 536 if (node.nodeType === 1) { 537 handler.end(node.nodeName.toLowerCase()); 538 } 539 } 540 } 541 } 542 node = nextNode; 543 } 544 545 while ((node = inertBodyElement.firstChild)) { 546 inertBodyElement.removeChild(node); 547 } 548 } 549 550 function attrToMap(attrs) { 551 var map = {}; 552 for (var i = 0, ii = attrs.length; i < ii; i++) { 553 var attr = attrs[i]; 554 map[attr.name] = attr.value; 555 } 556 return map; 557 } 558 559 560 /** 561 * Escapes all potentially dangerous characters, so that the 562 * resulting string can be safely inserted into attribute or 563 * element text. 564 * @param value 565 * @returns {string} escaped text 566 */ 567 function encodeEntities(value) { 568 return value. 569 replace(/&/g, '&'). 570 replace(SURROGATE_PAIR_REGEXP, function(value) { 571 var hi = value.charCodeAt(0); 572 var low = value.charCodeAt(1); 573 return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';'; 574 }). 575 replace(NON_ALPHANUMERIC_REGEXP, function(value) { 576 return '&#' + value.charCodeAt(0) + ';'; 577 }). 578 replace(/</g, '<'). 579 replace(/>/g, '>'); 580 } 581 582 /** 583 * create an HTML/XML writer which writes to buffer 584 * @param {Array} buf use buf.join('') to get out sanitized html string 585 * @returns {object} in the form of { 586 * start: function(tag, attrs) {}, 587 * end: function(tag) {}, 588 * chars: function(text) {}, 589 * comment: function(text) {} 590 * } 591 */ 592 function htmlSanitizeWriterImpl(buf, uriValidator) { 593 var ignoreCurrentElement = false; 594 var out = bind(buf, buf.push); 595 return { 596 start: function(tag, attrs) { 597 tag = lowercase(tag); 598 if (!ignoreCurrentElement && blockedElements[tag]) { 599 ignoreCurrentElement = tag; 600 } 601 if (!ignoreCurrentElement && validElements[tag] === true) { 602 out('<'); 603 out(tag); 604 forEach(attrs, function(value, key) { 605 var lkey = lowercase(key); 606 var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background'); 607 if (validAttrs[lkey] === true && 608 (uriAttrs[lkey] !== true || uriValidator(value, isImage))) { 609 out(' '); 610 out(key); 611 out('="'); 612 out(encodeEntities(value)); 613 out('"'); 614 } 615 }); 616 out('>'); 617 } 618 }, 619 end: function(tag) { 620 tag = lowercase(tag); 621 if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) { 622 out('</'); 623 out(tag); 624 out('>'); 625 } 626 // eslint-disable-next-line eqeqeq 627 if (tag == ignoreCurrentElement) { 628 ignoreCurrentElement = false; 629 } 630 }, 631 chars: function(chars) { 632 if (!ignoreCurrentElement) { 633 out(encodeEntities(chars)); 634 } 635 } 636 }; 637 } 638 639 640 /** 641 * When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare 642 * ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want 643 * to allow any of these custom attributes. This method strips them all. 644 * 645 * @param node Root element to process 646 */ 647 function stripCustomNsAttrs(node) { 648 while (node) { 649 if (node.nodeType === window.Node.ELEMENT_NODE) { 650 var attrs = node.attributes; 651 for (var i = 0, l = attrs.length; i < l; i++) { 652 var attrNode = attrs[i]; 653 var attrName = attrNode.name.toLowerCase(); 654 if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) { 655 node.removeAttributeNode(attrNode); 656 i--; 657 l--; 658 } 659 } 660 } 661 662 var nextNode = node.firstChild; 663 if (nextNode) { 664 stripCustomNsAttrs(nextNode); 665 } 666 667 node = getNonDescendant('nextSibling', node); 668 } 669 } 670 671 function getNonDescendant(propName, node) { 672 // An element is clobbered if its `propName` property points to one of its descendants 673 var nextNode = node[propName]; 674 if (nextNode && nodeContains.call(node, nextNode)) { 675 throw $sanitizeMinErr('elclob', 'Failed to sanitize html because the element is clobbered: {0}', node.outerHTML || node.outerText); 676 } 677 return nextNode; 678 } 679} 680 681function sanitizeText(chars) { 682 var buf = []; 683 var writer = htmlSanitizeWriter(buf, noop); 684 writer.chars(chars); 685 return buf.join(''); 686} 687 688 689// define ngSanitize module and register $sanitize service 690angular.module('ngSanitize', []) 691 .provider('$sanitize', $SanitizeProvider) 692 .info({ angularVersion: '1.8.
6922
vendor: 7,335 bytes, lines 692-891
692' }); 693 694/** 695 * @ngdoc filter 696 * @name linky 697 * @kind function 698 * 699 * @description 700 * Finds links in text input and turns them into html links. Supports `http/https/ftp/sftp/mailto` and 701 * plain email address links. 702 * 703 * Requires the {@link ngSanitize `ngSanitize`} module to be installed. 704 * 705 * @param {string} text Input text. 706 * @param {string} [target] Window (`_blank|_self|_parent|_top`) or named frame to open links in. 707 * @param {object|function(url)} [attributes] Add custom attributes to the link element. 708 * 709 * Can be one of: 710 * 711 * - `object`: A map of attributes 712 * - `function`: Takes the url as a parameter and returns a map of attributes 713 * 714 * If the map of attributes contains a value for `target`, it overrides the value of 715 * the target parameter. 716 * 717 * 718 * @returns {string} Html-linkified and {@link $sanitize sanitized} text. 719 * 720 * @usage 721 <span ng-bind-html="linky_expression | linky"></span> 722 * 723 * @example 724 <example module="linkyExample" deps="angular-sanitize.js" name="linky-filter"> 725 <file name="index.html"> 726 <div ng-controller="ExampleController"> 727 Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea> 728 <table> 729 <tr> 730 <th>Filter</th> 731 <th>Source</th> 732 <th>Rendered</th> 733 </tr> 734 <tr id="linky-filter"> 735 <td>linky filter</td> 736 <td> 737 <pre><div ng-bind-html="snippet | linky"><br></div></pre> 738 </td> 739 <td> 740 <div ng-bind-html="snippet | linky"></div> 741 </td> 742 </tr> 743 <tr id="linky-target"> 744 <td>linky target</td> 745 <td> 746 <pre><div ng-bind-html="snippetWithSingleURL | linky:'_blank'"><br></div></pre> 747 </td> 748 <td> 749 <div ng-bind-html="snippetWithSingleURL | linky:'_blank'"></div> 750 </td> 751 </tr> 752 <tr id="linky-custom-attributes"> 753 <td>linky custom attributes</td> 754 <td> 755 <pre><div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}"><br></div></pre> 756 </td> 757 <td> 758 <div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}"></div> 759 </td> 760 </tr> 761 <tr id="escaped-html"> 762 <td>no filter</td> 763 <td><pre><div ng-bind="snippet"><br></div></pre></td> 764 <td><div ng-bind="snippet"></div></td> 765 </tr> 766 </table> 767 </file> 768 <file name="script.js"> 769 angular.module('linkyExample', ['ngSanitize']) 770 .controller('ExampleController', ['$scope', function($scope) { 771 $scope.snippet = 772 'Pretty text with some links:\n' + 773 'http://angularjs.org/,\n' + 774 'mailto:[email protected],\n' + 775 '[email protected],\n' + 776 'and one more: ftp://127.0.0.1/.'; 777 $scope.snippetWithSingleURL = 'http://angularjs.org/'; 778 }]); 779 </file> 780 <file name="protractor.js" type="protractor"> 781 it('should linkify the snippet with urls', function() { 782 expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()). 783 toBe('Pretty text with some links: http://angularjs.org/, [email protected], ' + 784 '[email protected], and one more: ftp://127.0.0.1/.'); 785 expect(element.all(by.css('#linky-filter a')).count()).toEqual(4); 786 }); 787 788 it('should not linkify snippet without the linky filter', function() { 789 expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()). 790 toBe('Pretty text with some links: http://angularjs.org/, mailto:[email protected], ' + 791 '[email protected], and one more: ftp://127.0.0.1/.'); 792 expect(element.all(by.css('#escaped-html a')).count()).toEqual(0); 793 }); 794 795 it('should update', function() { 796 element(by.model('snippet')).clear(); 797 element(by.model('snippet')).sendKeys('new http://link.'); 798 expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()). 799 toBe('new http://link.'); 800 expect(element.all(by.css('#linky-filter a')).count()).toEqual(1); 801 expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()) 802 .toBe('new http://link.'); 803 }); 804 805 it('should work with the target property', function() { 806 expect(element(by.id('linky-target')). 807 element(by.binding("snippetWithSingleURL | linky:'_blank'")).getText()). 808 toBe('http://angularjs.org/'); 809 expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank'); 810 }); 811 812 it('should optionally add custom attributes', function() { 813 expect(element(by.id('linky-custom-attributes')). 814 element(by.binding("snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}")).getText()). 815 toBe('http://angularjs.org/'); 816 expect(element(by.css('#linky-custom-attributes a')).getAttribute('rel')).toEqual('nofollow'); 817 }); 818 </file> 819 </example> 820 */ 821angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) { 822 var LINKY_URL_REGEXP = 823 /((s?ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i, 824 MAILTO_REGEXP = /^mailto:/i; 825 826 var linkyMinErr = angular.$$minErr('linky'); 827 var isDefined = angular.isDefined; 828 var isFunction = angular.isFunction; 829 var isObject = angular.isObject; 830 var isString = angular.isString; 831 832 return function(text, target, attributes) { 833 if (text == null || text === '') return text; 834 if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text); 835 836 var attributesFn = 837 isFunction(attributes) ? attributes : 838 isObject(attributes) ? function getAttributesObject() {return attributes;} : 839 function getEmptyAttributesObject() {return {};}; 840 841 var match; 842 var raw = text; 843 var html = []; 844 var url; 845 var i; 846 while ((match = raw.match(LINKY_URL_REGEXP))) { 847 // We can not end in these as they are sometimes found at the end of the sentence 848 url = match[0]; 849 // if we did not match ftp/http/www/mailto then assume mailto 850 if (!match[2] && !match[4]) { 851 url = (match[3] ? 'http://' : 'mailto:') + url; 852 } 853 i = match.index; 854 addText(raw.substr(0, i)); 855 addLink(url, match[0].replace(MAILTO_REGEXP, '')); 856 raw = raw.substring(i + match[0].length); 857 } 858 addText(raw); 859 return $sanitize(html.join('')); 860 861 function addText(text) { 862 if (!text) { 863 return; 864 } 865 html.push(sanitizeText(text)); 866 } 867 868 function addLink(url, text) { 869 var key, linkAttributes = attributesFn(url); 870 html.push('<a '); 871 872 for (key in linkAttributes) { 873 html.push(key + '="' + linkAttributes[key] + '" '); 874 } 875 876 if (isDefined(target) && !('target' in linkAttributes)) { 877 html.push('target="', 878 target, 879 '" '); 880 } 881 html.push('href="', 882 url.replace(/"/g, '"'), 883 '">'); 884 addText(text); 885 html.push('</a>'); 886 } 887 }; 888}]); 889 890 891})(window, window.angular);
892 893/* 894 Webnames Error 895 ----------------------------- 896 Module is meant to handle server and client side errors. 897 898Scenarios: 8991.) Unexpected error 900 StatusCode = 500 901 902 Symptoms: 903 - threw out of controller action method 904 905 What should happen: 906 - gets logged as special critical error to investigate 907 - if Angular error handler is present, let that handle the user error 908 - if it's a production environment, allow them to log an error report 909 - if it's local host, display stack trace etc 910 911 Examples: 912 - developer forgets to handle an exception that can be thrown, fixable error occurs (null reference) 913 - the user should be able to report that an error occurred and the error should be investigated 914 - these prohibit the user from continuing their intended action on the site 915 916 Solution: 917 - the user should log an error 918 919 - inline form or link to oops page 920 921 9222.) Expected error 923 StatusCode = 422 924 925 Symptoms: 926 - it was caught in the controller, the StatusCode was manually set to 422 927 928 What should happen: 929 - should be handled in Angular controller, displaying a special message to the user (should/could use the WnErrorHandler) 930 - handle required view state on the promise rejection 931 932 Examples: 933 - server side validation of a domain name fails, return an error object and error message to display to the user 934 935 Author: Akos Sebestyen 936*/ 937(function () { 938 "use strict"; 939 940 angular.module('WebnamesError', ['ngSanitize']) 941 942 .config(['$httpProvider', function ($httpProvider) { 943 $httpProvider.interceptors.push('WnHttpInterceptor'); 944 }]) 945 946 .factory('WnHttpInterceptor', ['$log', '$q', '$sce', 'WnErrorHandler', function ($log, $q, $sce, WnErrorHandler) { 947 return { 948 'request': function (config) { 949 // Add a custom header so our BaseController.cs can recognize that the WebnamesError module is present. 950 return config; 951 }, 952 953 'responseError': function (response) { 954 // Convert the array buffer data into an object 955 if (response.data instanceof ArrayBuffer) { 956 response.data = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(response.data))); 957 } 958 959 // Push the error into the error handler directive 960 if (response.status === 500 && !!response.data.wnAngularError) { 961 WnErrorHandler.PushError(!!response.data.debugMode ? $sce.trustAsHtml(response.data.wnAngularError) : response.data.wnAngularError, true); 962 } 963 964 return $q.reject(response); 965 } 966 } 967 }]); 968 969})(); 970 971/* 972 * Webnames Error 973 * ---------------- 974 * Directives for displaying errors to the user from Angular. 975 * 976 * Author: Akos Sebestyen 977 */ 978(function () { 979 "use strict"; 980 981 angular.module('WebnamesError') 982 983 // you can specify the 'timeout-length' property to determine how long the notification should stay on screen 984 .directive('wnNotifications', wnNotifications) 985 986 // errors for the user to see 987 .directive('wnErrors', wnErrors) 988 989 .directive('helpdeskError', helpdeskError); 990 991 function wnNotifications() { 992 return { 993 restrict: 'EA', 994 replace: true, 995 template: '<div class="alert {{ notification.sCSSClass || \'alert-info\' }} alert-dismissible" data-ng-repeat="notification in wnn.notifications track by notification.id" role="alert">' + 996 '<button class="close" data-dismiss="alert" aria-label="Close"><i class="fas fa-times"></i></button>' + 997 '<p data-ng-bind-html="notification.message"></p>' + 998 '</div>', 999 controllerAs: 'wnn', 1000 link: function (scope, elem, attrs) { 1001 scope.$emit('wnNotifications loaded'); 1002 if (!!attrs.timeoutLength) { 1003 scope.timeoutLength = attrs.timeoutLength; 1004 } 1005 }, 1006 controller: ['$scope', '$timeout', 'WnErrorHandler', function ($scope, $timeout, WnErrorHandler) { 1007 var vm = this; 1008 1009 vm.notifications = WnErrorHandler.Notifications; 1010 1011 $scope.$on('WnNotificationPushed', function (event, id) { 1012 if (!!$scope.timeoutLength && $scope.timeoutLength > 0) { 1013 $timeout(function () { 1014 WnErrorHandler.RemoveById(id); 1015 }, $scope.timeoutLength * 1000); 1016 } 1017 }); 1018 1019 vm.clear = function (id) { 1020 WnErrorHandler.RemoveById(id); 1021 } 1022 }] 1023 } 1024 } 1025 1026 function wnErrors() { 1027 return { 1028 restrict: 'EA', 1029 replace: true, 1030 template: '<div id="webnames-error-directive" class="alert alert-danger alert-dismissible" data-ng-repeat="error in wne.errors track by error.id" role="alert">
1030' + 1031 '<button class="close" data-dismiss="alert" aria-label="Close" data-ng-if="!error.isCritical" data-ng-click="wne.clear(error.id)"><i class="fas fa-times"></i></button>' + 1032 '<p data-ng-bind-html="error.message"></p>' + 1033 '<p data-ng-if="error.isCritical">Timestamp: {{error.dtTimestamp | date: "yyyy-MM-dd H:mm:ss.sss"}}</p>' + 1034 '</div>', 1035 link: function (scope) { 1036 scope.$emit('wnErrors loaded'); 1037 }, 1038 controllerAs: 'wne', 1039 controller: ['$scope', '$timeout', 'WnErrorHandler', function ($scope, $timeout, WnErrorHandler) { 1040 var vm = this; 1041 1042 vm.errors = WnErrorHandler.Errors; 1043 1044 vm.clear = function (id) { 1045 WnErrorHandler.RemoveById(id); 1046 } 1047 }] 1048 } 1049 } 1050 1051 function helpdeskError() { 1052 return { 1053 restrict: 'EA', 1054 template: '<div class="red-tip">' + 1055 '<h1>Cart Errors</h1>' + 1056 '<h3>Message</h3>' + 1057 '<p>{{message}}</p>' + 1058 '<h3>StackTrace</h3>' + 1059 '<p>{{stackTrace}}</p>' + 1060 '<h3>Suggested Solution</h3>' + 1061 '<p>{{suggestedSolution}}</p>' + 1062 '<h3>Timestamp</h3>' + 1063 '<p>{{dtCreated}}</p>' + 1064 '</div>', 1065 scope: { 1066 error: '=' 1067 }, 1068 controller: [ 1069 '$scope', function ($scope) { 1070 // When the error object changes, make sure it has all attributes and then display the error 1071 $scope.$watch('error', function () { 1072 if ($scope.error && 1073 $scope.error.message && 1074 $scope.error.stackTrace && 1075 $scope.error.suggestedSolution && 1076 $scope.error.dtCreated) { 1077 $scope.message = $scope.error.message; 1078 $scope.stackTrace = $scope.error.stackTrace; 1079 $scope.suggestedSolution = $scope.error.suggestedSolution; 1080 $scope.dtCreated = $scope.error.dtCreated; 1081 } 1082 }); 1083 } 1084 ] 1085 } 1086 }; 1087})(); 1088 1089/* 1090 WebnamesError - WnErrorHandler 1091 ------------------ 1092 Service used to push messages to the directive which will display them to the user. 1093 1094 Author: Akos Sebestyen 1095 */ 1096(function () { 1097 "use strict"; 1098 1099 angular.module('WebnamesError') 1100 .service('WnErrorHandler', ['$rootScope', '$timeout', '$window', '$location', '$anchorScroll', 'WnMessage', '$log', function ($rootScope, $timeout, $window, $location, $anchorScroll, WnMessage, $log) { 1101 var service = this; 1102 var messageCount = 0; 1103 1104 var wnNotificationsPresent = false; 1105 var wnErrorsPresent = false; 1106 1107 service.scrollToError = true; 1108 service.Notifications = []; 1109 service.Errors = []; 1110 1111 service.ClearAll = function () { 1112 // Must clear array using length so that references aren't lost 1113 service.Notifications.length = 0; 1114 service.Errors.length = 0; 1115 } 1116 1117 // Use this to push notifications to the user 1118 service.PushNotification = function (errorString, sCSSClass) { 1119 var id = messageCount; 1120 service.Notifications.push(new WnMessage(id, errorString, false, sCSSClass)); 1121 $rootScope.$broadcast('WnNotificationPushed', id); 1122 messageCount++; 1123 1124 return service.Notifications[service.Notifications.length - 1]; 1125 } 1126 1127 // Use this to push errors to the user 1128 service.PushError = function (errorString, isCritical) { 1129 var id = messageCount; 1130 service.Errors.push(new WnMessage(id, errorString, !!isCritical)); 1131 $rootScope.$broadcast('WnErrorPushed', id); 1132 messageCount++; 1133 1134 if (service.scrollToError) { 1135 $location.hash("webnames-error-directive"); 1136 $anchorScroll(); 1137 } 1138 1139 return service.Errors[service.Errors.length - 1]; 1140 } 1141 1142 service.GetById = function (id) { 1143 return _.findWhere(service.Notifications, { id: id }); 1144 } 1145 1146 service.RemoveById = function (id) { 1147 var index = _.findIndex(service.Notifications, { id: id }); 1148 1149 if (index < 0) { 1150 index = _.findIndex(service.Errors, { id: id }); 1151 if (index > -1 && !service.Errors[index].isCritical) service.Errors.splice(index, 1); 1152 } else { 1153 service.Notifications.splice(index, 1); 1154 } 1155 } 1156 1157 // TODO: implement parameters 1158 /** 1159 * Redirects client to the robot page. 1160 * @returns {} 1161 */ 1162 service.KillNonRecoverable = function () { 1163 $window.location.href = $window._SmartURL + "error/oops/default.aspx"; 1164 } 1165 1166 // Save events from link function of directives for page validation 1167 $rootScope.$on('wnNotifications loaded', function () { 1168 wnNotificationsPresent = true; 1169 }); 1170 1171 $rootScope.$on('wnErrors loaded', function () { 1172 wnErrorsPresent = true; 1173 }); 1174 1175 // Used for validation of the Angular page 1176 $timeout(function () { 1177 if (!wnErrorsPresent) { 1178 throw "No 'wnErrors' directive found! You should not use this module without placing this directive on your page first!"; 1179 } 1180 if (!wnNotificationsPresent) { 1181 throw "No 'wnNotifications' directive found! You should not use this module without placing this directive on your page first!"; 1182 } 1183 }); 1184 }]) 1185 1186 .factory('WnMessage', [function () { 1187 function WnMessage(id, message, isCritical, sCSSClass) { 1188 var model = this; 1189 1190 model.id = id; 1191 model.dtTimestamp = new Date(); 1192 model.isCritical = isCritical; 1193 model.sCSSClass = sCSSClass || ""; 1194 1195 model.message = message; 1196 } 1197 1198 return WnMessage; 1199 }]); 1200})(); 1201 1202/* 1203 Webnames Analytics 1204 -------------------------------- 1205 Module to allow easy abstraction of analytics work in Angular. 1206 1207 Has services for Google Analytics. 1208 1209 It's best implemented using the server side Analytics helpers 1210 to construct the required JSON objects from BillPackages etc. 1211 1212 Modules 1213 GAService webnamesAnalytics.ga.service.cs 1214 1215 Server Side Helpers 1216 GoogleAnalyticsHelpers.cs 1217 1218 Dependencies 1219 ~ Underscore : Using UnderscoreJS for any array/object work. 1220 1221 Author: Akos Sebestyen 2015 1222*/ 1223 1224(function () { 1225 angular.module("WebnamesAnalytics", []) 1226 .config(['$logProvider', function($logProvider) { 1227 $logProvider.debugEnabled(true); 1228 } 1229 ]); 1230})(); 1231/***** 1232 WebnamesAnalytics::GoogleAnalytics Service 1233 1234 Handles Analytics requests from an Ajax call to push to Google Analytics 1235 1236 Sample Usage (in controller, etc) 1237 GAService.AddProduct($a.remove[i][0]); 1238 1239 Depends on: 1240 $window 1241 $log 1242****/ 1243(function () { 1244 angular.module("WebnamesAnalytics") 1245 .service('GAService', ['$window', '$log', gaService]); 1246 1247 function gaService($window, $log) { 1248 if (!$window.ga) { 1249 // This could be inject tested. (like underscore) 1250 throw "You must have GA defined globally to use this service!"; 1251 } 1252 1253 //***************** IMPORTANT ***************/ 1254 // use GoogleAnalyticsHelper.cs to create 'objects' server side and then pass those in 1255 1256 var service = this; 1257 1258 // Example
1259 // ga('ec:addProduct', { 'id': 'PHOSTUNIX1FD_NEW' , 'name': 'Hosting Value Plan (Free Domain)' , 'brand': 'Webnames.ca' , 'variant': '6' , 'quantity': 1 , 'price': 37.5000 }); 1260 service.AddProduct = addProduct; 1261 service.AddProductAndSend = addProductAndSend; 1262 1263 service.RemoveProductAndSend = removeProductAndSend; 1264 1265 // Must be set by calling module module (set to undefined, but we don't test this later on) 1266 service.GAProperties = undefined; 1267 1268 // Example 1269 // ga('ec:setAction', 'add', { 'list': 'Cart Page' }); 1270 service.TrackAction = trackAction; 1271 1272 // Example 1273 // ga('send', 'event', 'Cart Page', 'Add to Cart'); 1274 service.Send = send; 1275 1276 function addProduct(obj) { 1277 $log.debug('ec:addProduct', obj); 1278 $window.ga('ec:addProduct', obj); 1279 } 1280 1281 function addProductAndSend(categoryName, actionName, arrProperties) { 1282 for (var i = 0; i < arrProperties.length; i++) { 1283 service.AddProduct(arrProperties[i]); 1284 } 1285 1286 service.TrackAction('add', { 'list': actionName }); 1287 1288 service.Send(categoryName, actionName); 1289 } 1290 1291 function removeProductAndSend(categoryName, actionName, arrProperties) { 1292 for (var i = 0; i < arrProperties.length; i++) { 1293 service.AddProduct(arrProperties[i]); 1294 } 1295 1296 service.TrackAction('remove', { 'list': actionName }); 1297 1298 service.Send(categoryName, actionName); 1299 } 1300 1301 function trackAction(action, obj) { 1302 $log.debug('ec:setAction', action, obj); 1303 $window.ga('ec:setAction', action, obj); 1304 } 1305 1306 function send(name, desc) { 1307 $log.debug('send', 'event', name, desc); 1308 $window.ga('send', 'event', name, desc); 1309 } 1310 } 1311})(); 1312(function () { 1313 angular.module("WebnamesAnalytics") 1314 .service('GA4Service', ['$window', '$log', GA4Service]); 1315 1316 function GA4Service($window, $log) { 1317 1318 var service = this; 1319 1320 function sendEvent (sEventName, sEventData) { 1321 gtag('event', sEventName, sEventData); 1322 }; 1323 1324 service.sendAddToCart = function (oData) { 1325 var sEventName = "add_to_cart"; 1326 sendEvent(sEventName, oData); 1327 1328 } 1329 service.sendRemoveFromCart = function (oData) { 1330 var sEventName = "remove_from_cart"; 1331 sendEvent(sEventName, oData); 1332 1333 } 1334 } 1335})(); 1336(function () { 1337 "use strict"; 1338 angular.module("WebnamesLocationHelper", ["WebnamesCommon"]); 1339})(); 1340(function () { 1341 "use strict"; 1342 1343 angular 1344 .module("WebnamesLocationHelper") 1345 .service("QueryStringFilterBinder", ["$location", "$rootScope", "$filter", QueryStringFilterBinder]); 1346 1347 function QueryStringFilterBinder($location, $rootScope, $filter) { 1348 var service = this; 1349 1350 var currentBindings = []; 1351 1352 /** 1353 * Unbinds all listeners set up 1354 * @returns {undefined} 1355 */ 1356 service.UnbindAllFilters = function () { 1357 var startingLength = currentBindings.length; 1358 for (var i = 0; i < startingLength; i++) { 1359 var binding = currentBindings.pop(); 1360 1361 binding.deregisterLocationListener(); 1362 binding.deregisterWatch(); 1363 } 1364 } 1365 1366 /** 1367 * Binds a flat object of filters to url params 1368 * @param {Object} filterObj a flat object of filters which will be bound to the url params by type 1369 * @param {Boolean} bExcludeFilterKeys a boolean flag to indicate whether it shouldn't bind to URL params if all of the filter properties are true 1370 * @param {Object}
1370 excludeObj an object containing parameters with keys that are not counted when determining if location parameters are applied 1371 * @param {Object} includeObj an object containing parameters with keys that are counted when determining if location parameters are applied 1372 * Note: if (includeObj === undefined), all parameters (even random ones) will be counted 1373 * @returns {Function} a deregister function for this binding 1374 */ 1375 service.BindFilters = function (filterObj, bExcludeFilterKeys, excludeObj, includeObj) { 1376 1377 if (bExcludeFilterKeys === undefined) bExcludeFilterKeys = false; 1378 if (excludeObj === undefined) excludeObj = {}; 1379 1380 if (!_.isObject(filterObj)) throw new Error("parameter being bound must be an object"); 1381 1382 // only apply location parameters if there are any in the URL 1383 //(that aren't "excluded", and, if includeObj is defined, also are "included") 1384 var bApplyLocationParams = false; 1385 for (var sParam in $location.search()) { 1386 if ($location.search().hasOwnProperty(sParam)) { 1387 var bDontExclude = true; 1388 for (var sExcludeParam in excludeObj) { 1389 if (excludeObj.hasOwnProperty(sExcludeParam)) { 1390 if (sParam == sExcludeParam) { 1391 bDontExclude = false; 1392 break; 1393 } 1394 } 1395 } 1396 if (!bDontExclude) continue; 1397 1398 if (includeObj === undefined) { 1399 bApplyLocationParams = true; 1400 break; 1401 } 1402 var bInclude = false; 1403 for (var sIncudeParam in includeObj) { 1404 if (includeObj.hasOwnProperty(sIncudeParam)) { 1405 if (sParam == sIncudeParam) { 1406 bInclude = true; 1407 break; 1408 } 1409 } 1410 } 1411 if (bInclude) { 1412 bApplyLocationParams = true; 1413 break; 1414 } 1415 } 1416 } 1417 if (bApplyLocationParams) { 1418 applyLocationParams(filterObj, bExcludeFilterKeys, true); 1419 } 1420 1421 // Set up a watch on filter object to bind each one of its properties to changing the url 1422 var deregisterWatch = $rootScope.$watch(function () { return filterObj }, function (newVal) { 1423 _.each(_.keys(newVal), function (key) { 1424 if (_.isBoolean(newVal[key])) { 1425 // if we are doing exclusion, reverse our set operation 1426 if (!bExcludeFilterKeys) { 1427 $location.search(key, newVal[key] ? true : null); 1428 } else { 1429 $location.search(key, !newVal[key] ? true : null); 1430 } 1431 } 1432 else if (_.isNumber(newVal[key]) || _.isString(newVal[key])) { 1433 $location.search(key, newVal[key]); 1434 } 1435 else { 1436 $location.search(key, newVal[key]); 1437 } 1438 }); 1439 }, true); 1440 1441 var deregisterLocationListener = $rootScope.$on("$locationChangeSuccess", function () { 1442 applyLocationParams(filterObj, bExcludeFilterKeys); 1443 }); 1444 1445 currentBindings.push({ 1446 filterObj: filterObj, 1447 deregisterLocationListener: deregisterLocationListener, 1448 deregisterWatch: deregisterWatch 1449 }); 1450 1451 return function () { 1452 var bindingIndex = _.findIndex(currentBindings, function(binding) { 1453 return binding.filterObj === filterObj; 1454 }); 1455 currentBindings[bindingIndex].deregisterWatch(); 1456 currentBindings[bindingIndex].deregisterLocationListener(); 1457 currentBindings.splice(bindingIndex, 1); 1458 } 1459 } 1460 1461 function applyLocationParams(filterObj, allTrueDontBind) { 1462 var params = $location.search(); 1463 var paramKeys = _.keys(params); 1464 // Loop through each key of the filter object and update its value based on URL parameters 1465 _.each(_.keys(filterObj), function (key) { 1466 1467 switch (typeof filterObj[key]) { 1468 case "boolean": 1469 filterObj[key] = ((_.indexOf(paramKeys, key) < 0) ? !!allTrueDontBind : !allTrueDontBind); 1470 break; 1471 case "number": 1472 if (params.hasOwnProperty(key) && !isNaN(params[key])) { 1473 filterObj[key] = parseInt(params[key]); 1474 } 1475 break; 1476 default: 1477 filterObj[key]= params[key]; 1478 break; 1479 } 1480 }); 1481 } 1482 } 1483})(); 1484 1485(function () { 1486 "use strict"; 1487 1488 angular.module("WebnamesLocationHelper") 1489 .service("LegacyParam", ['$window', function($window) { 1490 var service = this; 1491 1492 service.Params = {}; 1493 1494 /** 1495 * Helper to get a list of domain name parameters from the URL 1496 * @returns {Array} An array of domain parameters (params that begin with 'dom') 1497 */ 1498 service.GetDomains = function() { 1499 return _.chain(service.Params) 1500 .map(function (value, key) { 1501 if (key.substr(0, 3) === "dom") return value; 1502 else return undefined; 1503 }) 1504 .compact() 1505 .uniq() 1506 .value(); 1507 } 1508 1509 var paramArray = $window.location.search.substr(1).split("&"); 1510 1511 _.each(paramArray, function (param) { 1512 var pair = param.split("="); 1513 service.Params[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]); 1514 }); 1515 }]); 1516})(); 1517(function () { 1518 "use strict"; 1519 1520 angular 1521 .module("WebnamesLocationHelper") 1522 .directive("wnHref", ["$window", wnHref]); 1523 1524 function wnHref($window) { 1525 var directive = { 1526 link: link, 1527 restrict: "A" 1528 }; 1529 1530 function link(scope, element, attrs) { 1531 attrs.$set("href", $window._SmartURL + attrs.wnHref); 1532 } 1533 1534 return directive; 1535 } 1536})(); 1537/* 1538 Webnames Common 1539 -------------------------------- 1540 Module to hold common Angular functions 1541 1542 Author: Akos Sebestyen 2015 1543*/ 1544(function () { 1545 "use strict"; 1546
1547 angular.module('WebnamesCommon', []); 1548 1549})(); 1550 1551 1552/* 1553 Generic Injector 1554 -------------------------------- 1555 Used to inject data into Angular on pageload. 1556 1557 The provider exposes a "InjectGenericProperty" method that can be used at config time to get JSON data into the service. 1558 1559 Then you can access the data from GenericInjector.PropBag.yourProperty 1560 1561 Author: Akos Sebestyen 2015 1562*/ 1563(function () { 1564 "use strict"; 1565 1566 angular.module('WebnamesCommon') 1567 .provider('GenericInjector', function () { 1568 var _propBag = {}; 1569 1570 // Assign the prop bag 1571 var GenericInjector = function () { 1572 this.PropBag = _propBag; 1573 }; 1574 1575 return { 1576 $get: ['$injector', function ($injector) { return $injector.instantiate(GenericInjector) }], 1577 InjectGenericProperty: function (propName, propValue) { _propBag[propName] = propValue} 1578 } 1579 }); 1580 1581})(); 1582 1583 1584(function () { 1585 "use strict"; 1586 1587 angular.module("WebnamesCommon") 1588 .factory("WnFocus", ['$timeout', '$document', function ($timeout, $document) { 1589 return function(id) { 1590 $timeout(function () { 1591 var element = $document.getElementById(id); 1592 if (element) 1593 element.focus(); 1594 }); 1595 } 1596 }]); 1597})(); 1598// Scrolls to the top of the given selector. Use offset to offset the value by pixels 1599 1600(function () { 1601 "use strict"; 1602 1603 angular.module("WebnamesCommon") 1604 .factory("WnScroll", ['$timeout', function ($timeout) { 1605 return function (selector, offset, animateTime) { 1606 if (!offset) offset = 0; 1607 if (!animateTime) animateTime = 500; 1608 $timeout(function () { 1609 $('html, body').animate({ scrollTop: ($(selector).offset().top + offset) }, animateTime); 1610 }); 1611 } 1612 }]); 1613})(); 1614// https://gist.github.com/tommaitland/7579618 1615(function () { 1616 angular.module('WebnamesCommon').directive('ngDebounce', ['$timeout', function ($timeout) { 1617 return { 1618 restrict: 'A', 1619 require: 'ngModel', 1620 priority: 99, 1621 link: function (scope, elm, attr, ngModelCtrl) { 1622 if (attr.type === 'radio' || attr.type === 'checkbox') return; 1623 1624 elm.unbind('input'); 1625 1626 var debounce; 1627 elm.bind('input', function () { 1628 $timeout.cancel(debounce); 1629 debounce = $timeout(function () { 1630 scope.$apply(function () { 1631 ngModelCtrl.$setViewValue(elm.val()); 1632 }); 1633 }, attr.ngDebounce || 1000); 1634 }); 1635 elm.bind('blur', function () { 1636 scope.$apply(function () { 1637 ngModelCtrl.$setViewValue(elm.val()); 1638 }); 1639 }); 1640 } 1641 1642 } 1643 }]); 1644})(); 1645 1646(function () { 1647 "use strict"; 1648 1649 angular.module('WebnamesCommon') 1650 .filter('startFrom', function () { 1651 1652 return function (input, start) { 1653 if (input !== undefined && angular.isArray(input)) { 1654 return input.slice(start); 1655 } else { 1656 return []; 1657 } 1658 } 1659 }) 1660 .filter("getActiveKeys", [function () { 1661 /** 1662 * Takes an object and returns an array of keys where the values are truthy 1663 * @param {Object} obj Flat object of key value pairs 1664 * @returns {Array} array of keys with truthy values 1665 */ 1666 return function(obj) { 1667 return _.chain(obj) 1668 .pairs() // Turn object into array of arrays as key value pairs 1669 .filter(function (kvp) { return !!kvp[1] }) // Return all Key-Value pairs where value is truthy 1670 .map(function (kvp) { return kvp[0] }) // Map into an array of just strings of keys 1671 .value(); 1672 } 1673 }]) 1674 .filter('nullToEnd', function () { 1675 return function (array, key) { 1676 if (!angular.isArray(array)) return; 1677 1678 var present = array.filter(function (item) { 1679 return item[key]; 1680 }); 1681 var empty = array.filter(function (item) { 1682 return !item[key]; 1683 }); 1684 1685 return present.concat(empty); 1686 } 1687 }) 1688 .run(["$http", function ($http) { 1689 $http.defaults.headers.common['X-XSRF-Token'] = 1690 angular.element(document.querySelector('input[name="__RequestVerificationToken"]')).attr('value'); 1691 }]); 1692})(); 1693 1694(function() { 1695 "use strict"; 1696 1697 angular.module("WebnamesGridHelper", []) 1698 .directive("orderedHeader", [function () { 1699 return { 1700 restrict: 'EA', 1701 replace: true, 1702 transclude: true, 1703 template: '<th><span class="pointer h-underline" ng-click="toggleOrder()"><span ng-transclude></span ng-transclude>' + 1704 '<span class="ml5 fa-xs" ng-show="ordering.predicate === predicate && ordering.reverse"><i class="fa-solid fa-caret-down"></i></span>' + 1705 '<span class="ml5 fa-xs" ng-show="ordering.predicate === predicate && !ordering.reverse"><i class="fa-solid fa-caret-up"></i></span>' + 1706 '</span></span></th>', 1707 controller: ['$scope', function ($scope) { 1708 $scope.toggleOrder = function () { 1709 if ($scope.ordering.predicate === $scope.predicate) { 1710 $scope.ordering.reverse = !$scope.ordering.reverse; 1711 } else { 1712 $scope.ordering.predicate = $scope.predicate; 1713 $scope.ordering.reverse = false; 1714 } 1715 } 1716 }], 1717 scope: { 1718 predicate: "@", 1719 ordering: "=" 1720 } 1721 } 1722 }]); 1723})(); 1724/** 1725 * State-based routing for AngularJS 1.x 1726 * NOTICE: This monolithic bundle also bundles the @uirouter/core code. 1727 * This causes it to be incompatible with plugins that depend on @uirouter/core. 1728 * We recommend switching to the ui-router-core.js and ui-router-angularjs.js bundles instead. 1729 * For more information, see https://ui-router.github.io/blog/uirouter-for-angularjs-umd-bundles 1730 * @version v1.0.30 1731 * @link https://ui-router.github.io 1732 * @license MIT License, http://www.opensource.org/licenses/MIT 1733 */ 1734(function (global, factory) { 1735 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('angular')) : 1736 typeof define === 'function' && define.amd ? define(['exports', 'angular'], factory) : 1737 (global = global || self, factory(global['@uirouter/angularjs'] = {}, global.angular)); 1738}(this, (function (exports, ng_from_import) { 'use strict'; 1739 1740 /** @publicapi @module ng1 */ /** */ 1741 /** @hidden */ var ng_from_global = angular; 1742 /** @hidden */ var ng = ng_from_import && ng_from_import.module ? ng_from_import : ng_from_global; 1743 1744 /** 1745 * Higher order functions 1746 * 1747 * These utility functions are exported, but are subject to change without notice. 1748 * 1749 * @packageDocumentation 1750 */
1751 var __spreadArrays = (undefined && undefined.__spreadArrays) || function () { 1752 for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; 1753 for (var r = Array(s), k = 0, i = 0; i < il; i++) 1754 for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) 1755 r[k] = a[j]; 1756 return r; 1757 }; 1758 /** 1759 * Returns a new function for [Partial Application](https://en.wikipedia.org/wiki/Partial_application) of the original function. 1760 * 1761 * Given a function with N parameters, returns a new function that supports partial application. 1762 * The new function accepts anywhere from 1 to N parameters. When that function is called with M parameters, 1763 * where M is less than N, it returns a new function that accepts the remaining parameters. It continues to 1764 * accept more parameters until all N parameters have been supplied. 1765 * 1766 * 1767 * This contrived example uses a partially applied function as an predicate, which returns true 1768 * if an object is found in both arrays. 1769 * @example 1770 * ``` 1771 * // returns true if an object is in both of the two arrays 1772 * function inBoth(array1, array2, object) { 1773 * return array1.indexOf(object) !== -1 && 1774 * array2.indexOf(object) !== 1; 1775 * } 1776 * let obj1, obj2, obj3, obj4, obj5, obj6, obj7 1777 * let foos = [obj1, obj3] 1778 * let bars = [obj3, obj4, obj5] 1779 * 1780 * // A curried "copy" of inBoth 1781 * let curriedInBoth = curry(inBoth); 1782 * // Partially apply both the array1 and array2 1783 * let inFoosAndBars = curriedInBoth(foos, bars); 1784 * 1785 * // Supply the final argument; since all arguments are 1786 * // supplied, the original inBoth function is then called. 1787 * let obj1InBoth = inFoosAndBars(obj1); // false 1788 * 1789 * // Use the inFoosAndBars as a predicate. 1790 * // Filter, on each iteration, supplies the final argument 1791 * let allObjs = [ obj1, obj2, obj3, obj4, obj5, obj6, obj7 ]; 1792 * let foundInBoth = allObjs.filter(inFoosAndBars); // [ obj3 ] 1793 * 1794 * ``` 1795 * 1796 * @param fn 1797 * @returns {*|function(): (*|any)} 1798 */ 1799 function curry(fn) { 1800 return function curried() { 1801 if (arguments.length >= fn.length) { 1802 return fn.apply(this, arguments); 1803 } 1804 var args = Array.prototype.slice.call(arguments); 1805 return curried.bind.apply(curried, __spreadArrays([this], args)); 1806 }; 1807 } 1808 /** 1809 * Given a varargs list of functions, returns a function that composes the argument functions, right-to-left 1810 * given: f(x), g(x), h(x) 1811 * let composed = compose(f,g,h) 1812 * then, composed is: f(g(h(x))) 1813 */ 1814 function compose() { 1815 var args = arguments; 1816 var start = args.length - 1; 1817 return function () { 1818 var i = start, result = args[start].apply(this, arguments); 1819 while (i--) 1820 result = args[i].call(this, result); 1821 return result; 1822 }; 1823 } 1824 /** 1825 * Given a varargs list of functions, returns a function that is composes the argument functions, left-to-right 1826 * given: f(x), g(x), h(x) 1827 * let piped = pipe(f,g,h); 1828 * then, piped is: h(g(f(x))) 1829 */ 1830 function pipe() { 1831 var funcs = []; 1832 for (var _i = 0; _i < arguments.length; _i++) { 1833 funcs[_i] = arguments[_i]; 1834 } 1835 return compose.apply(null, [].slice.call(arguments).reverse()); 1836 } 1837 /** 1838 * Given a property name, returns a function that returns that property from an object 1839 * let obj = { foo: 1, name: "blarg" }; 1840 * let getName = prop("name"); 1841 * getName(obj) === "blarg" 1842 */ 1843 var prop = function (name) { return function (obj) { return obj && obj[name]; }; }; 1844 /** 1845 * Given a property name and a value, returns a function that returns a boolean based on whether 1846 * the passed object has a property that matches the value 1847 * let obj = { foo: 1, name: "blarg" }; 1848 * let getName = propEq("name", "blarg"); 1849 * getName(obj) === true 1850 */ 1851 var propEq = curry(function (name, _val, obj) { return obj && obj[name] === _val; }); 1852 /** 1853 * Given a dotted property name, returns a function that returns a nested property from an object, or undefined 1854 * let obj = { id: 1, nestedObj: { foo: 1, name: "blarg" }, }; 1855 * let getName = prop("nestedObj.name"); 1856 * getName(obj) === "blarg" 1857 * let propNotFound = prop("this.property.doesnt.exist"); 1858 * propNotFound(obj) === undefined 1859 */ 1860 var parse = function (name) { return pipe.apply(null, name.split('.').map(prop)); }; 1861 /** 1862 * Given a function that returns a truthy or falsey value, returns a 1863 * function that returns the opposite (falsey or truthy) value given the same inputs 1864 */ 1865 var not = function (fn) { return function () { 1866 var args = []; 1867 for (var _i = 0; _i < arguments.length; _i++) { 1868 args[_i] = arguments[_i]; 1869 }
1870 return !fn.apply(null, args); 1871 }; }; 1872 /** 1873 * Given two functions that return truthy or falsey values, returns a function that returns truthy 1874 * if both functions return truthy for the given arguments 1875 */ 1876 function and(fn1, fn2) { 1877 return function () { 1878 var args = []; 1879 for (var _i = 0; _i < arguments.length; _i++) { 1880 args[_i] = arguments[_i]; 1881 } 1882 return fn1.apply(null, args) && fn2.apply(null, args); 1883 }; 1884 } 1885 /** 1886 * Given two functions that return truthy or falsey values, returns a function that returns truthy 1887 * if at least one of the functions returns truthy for the given arguments 1888 */ 1889 function or(fn1, fn2) { 1890 return function () { 1891 var args = []; 1892 for (var _i = 0; _i < arguments.length; _i++) { 1893 args[_i] = arguments[_i]; 1894 } 1895 return fn1.apply(null, args) || fn2.apply(null, args); 1896 }; 1897 } 1898 /** 1899 * Check if all the elements of an array match a predicate function 1900 * 1901 * @param fn1 a predicate function `fn1` 1902 * @returns a function which takes an array and returns true if `fn1` is true for all elements of the array 1903 */ 1904 var all = function (fn1) { return function (arr) { return arr.reduce(function (b, x) { return b && !!fn1(x); }, true); }; }; 1905 // tslint:disable-next-line:variable-name 1906 var any = function (fn1) { return function (arr) { return arr.reduce(function (b, x) { return b || !!fn1(x); }, false); }; }; 1907 /** Given a class, returns a Predicate function that returns true if the object is of that class */ 1908 var is = function (ctor) { return function (obj) { 1909 return (obj != null && obj.constructor === ctor) || obj instanceof ctor; 1910 }; }; 1911 /** Given a value, returns a Predicate function that returns true if another value is === equal to the original value */ 1912 var eq = function (value) { return function (other) { return value === other; }; }; 1913 /** Given a value, returns a function which returns the value */ 1914 var val = function (v) { return function () { return v; }; }; 1915 function invoke(fnName, args) { 1916 return function (obj) { return obj[fnName].apply(obj, args); }; 1917 } 1918 /** 1919 * Sorta like Pattern Matching (a functional programming conditional construct) 1920 * 1921 * See http://c2.com/cgi/wiki?PatternMatching 1922 * 1923 * This is a conditional construct which allows a series of predicates and output functions 1924 * to be checked and then applied. Each predicate receives the input. If the predicate 1925 * returns truthy, then its matching output function (mapping function) is provided with 1926 * the input and, then the result is returned. 1927 * 1928 * Each combination (2-tuple) of predicate + output function should be placed in an array 1929 * of size 2: [ predicate, mapFn ] 1930 * 1931 * These 2-tuples should be put in an outer array. 1932 * 1933 * @example 1934 * ``` 1935 * 1936 * // Here's a 2-tuple where the first element is the isString predicate 1937 * // and the second element is a function that returns a description of the input 1938 * let firstTuple = [ angular.isString, (input) => `Heres your string ${input}` ]; 1939 * 1940 * // Second tuple: predicate "isNumber", mapfn returns a description 1941 * let secondTuple = [ angular.isNumber, (input) => `(${input}) That's a number!` ]; 1942 * 1943 * let third = [ (input) => input === null, (input) => `Oh, null...` ]; 1944 * 1945 * let fourth = [ (input) => input === undefined, (input) => `notdefined` ]; 1946 * 1947 * let descriptionOf = pattern([ firstTuple, secondTuple, third, fourth ]); 1948 * 1949 * console.log(descriptionOf(undefined)); // 'notdefined' 1950 * console.log(descriptionOf(55)); // '(55) That's a number!' 1951 * console.log(descriptionOf("foo")); // 'Here's your string foo' 1952 * ``` 1953 * 1954 * @param struct A 2D array. Each element of the array should be an array, a 2-tuple, 1955 * with a Predicate and a mapping/output function 1956 * @returns {function(any): *} 1957 */ 1958 function pattern(struct) { 1959 return function (x) { 1960 for (var i = 0; i < struct.length; i++) { 1961 if (struct[i][0](x)) 1962 return struct[i][1](x); 1963 } 1964 }; 1965 } 1966 1967 /** 1968 * Predicates 1969 * 1970 * These predicates return true/false based on the input. 1971 * Although these functions are exported, they are subject to change without notice. 1972 * 1973 * @packageDocumentation 1974 */ 1975 var toStr = Object.prototype.toString; 1976 var tis = function (t) { return function (x) { return typeof x === t; }; }; 1977 var isUndefined = tis('undefined'); 1978 var isDefined = not(isUndefined); 1979 var isNull = function (o) { return o === null; }; 1980 var isNullOrUndefined = or(isNull, isUndefined); 1981 var isFunction = tis('function'); 1982 var isNumber = tis('number');
1983 var isString = tis('string'); 1984 var isObject = function (x) { return x !== null && typeof x === 'object'; }; 1985 var isArray = Array.isArray; 1986 var isDate = (function (x) { return toStr.call(x) === '[object Date]'; }); 1987 var isRegExp = (function (x) { return toStr.call(x) === '[object RegExp]'; }); 1988 /** 1989 * Predicate which checks if a value is injectable 1990 * 1991 * A value is "injectable" if it is a function, or if it is an ng1 array-notation-style array 1992 * where all the elements in the array are Strings, except the last one, which is a Function 1993 */ 1994 function isInjectable(val) { 1995 if (isArray(val) && val.length) { 1996 var head = val.slice(0, -1), tail = val.slice(-1); 1997 return !(head.filter(not(isString)).length || tail.filter(not(isFunction)).length); 1998 } 1999 return isFunction(val); 2000 } 2001 /** 2002 * Predicate which checks if a value looks like a Promise 2003 * 2004 * It is probably a Promise if it's an object, and it has a `then` property which is a Function 2005 */ 2006 var isPromise = and(isObject, pipe(prop('then'), isFunction)); 2007 2008 var noImpl = function (fnname) { return function () { 2009 throw new Error("No implementation for " + fnname + ". The framework specific code did not implement this method."); 2010 }; }; 2011 var makeStub = function (service, methods) { 2012 return methods.reduce(function (acc, key) { return ((acc[key] = noImpl(service + "." + key + "()")), acc); }, {}); 2013 }; 2014 var services = { 2015 $q: undefined, 2016 $injector: undefined, 2017 }; 2018 2019 var __spreadArrays$1 = (undefined && undefined.__spreadArrays) || function () { 2020 for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; 2021 for (var r = Array(s), k = 0, i = 0; i < il; i++) 2022 for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) 2023 r[k] = a[j]; 2024 return r; 2025 }; 2026 var root = (typeof self === 'object' && self.self === self && self) || 2027 (typeof global === 'object' && global.global === global && global) || 2028 undefined; 2029 var angular$1 = root.angular || {}; 2030 var fromJson = angular$1.fromJson || JSON.parse.bind(JSON); 2031 var toJson = angular$1.toJson || JSON.stringify.bind(JSON); 2032 var forEach = angular$1.forEach || _forEach; 2033 var extend = Object.assign || _extend; 2034 var equals = angular$1.equals || _equals; 2035 function identity(x) { 2036 return x; 2037 } 2038 function noop() { } 2039 /** 2040 * Builds proxy functions on the `to` object which pass through to the `from` object. 2041 * 2042 * For each key in `fnNames`, creates a proxy function on the `to` object. 2043 * The proxy function calls the real function on the `from` object. 2044 * 2045 * 2046 * #### Example: 2047 * This example creates an new class instance whose functions are prebound to the new'd object. 2048 * ```js 2049 * class Foo { 2050 * constructor(data) { 2051 * // Binds all functions from Foo.prototype to 'this', 2052 * // then copies them to 'this' 2053 * bindFunctions(Foo.prototype, this, this); 2054 * this.data = data; 2055 * } 2056 * 2057 * log() { 2058 * console.log(this.data); 2059 * } 2060 * } 2061 * 2062 * let myFoo = new Foo([1,2,3]); 2063 * var logit = myFoo.log; 2064 * logit(); // logs [1, 2, 3] from the myFoo 'this' instance 2065 * ``` 2066 * 2067 * #### Example: 2068 * This example creates a bound version of a service function, and copies it to another object 2069 * ``` 2070 * 2071 * var SomeService = { 2072 * this.data = [3, 4, 5]; 2073 * this.log = function() { 2074 * console.log(this.data); 2075 * } 2076 * } 2077 * 2078 * // Constructor fn 2079 * function OtherThing() { 2080 * // Binds all functions from SomeService to SomeService, 2081 * // then copies them to 'this' 2082 * bindFunctions(SomeService, this, SomeService); 2083 * } 2084 * 2085 * let myOtherThing = new OtherThing(); 2086 * myOtherThing.log(); // logs [3, 4, 5] from SomeService's 'this' 2087 * ``` 2088 * 2089 * @param source A function that returns the source object which contains the original functions to be bound 2090 * @param target A function that returns the target object which will receive the bound functions 2091 * @param bind A function that returns the object which the functions will be bound to 2092 * @param fnNames The function names which will be bound (Defaults to all the functions foun
2092d on the 'from' object) 2093 * @param latebind If true, the binding of the function is delayed until the first time it's invoked 2094 */ 2095 function createProxyFunctions(source, target, bind, fnNames, latebind) { 2096 if (latebind === void 0) { latebind = false; } 2097 var bindFunction = function (fnName) { return source()[fnName].bind(bind()); }; 2098 var makeLateRebindFn = function (fnName) { 2099 return function lateRebindFunction() { 2100 target[fnName] = bindFunction(fnName); 2101 return target[fnName].apply(null, arguments); 2102 }; 2103 }; 2104 fnNames = fnNames || Object.keys(source()); 2105 return fnNames.reduce(function (acc, name) { 2106 acc[name] = latebind ? makeLateRebindFn(name) : bindFunction(name); 2107 return acc; 2108 }, target); 2109 } 2110 /** 2111 * prototypal inheritance helper. 2112 * Creates a new object which has `parent` object as its prototype, and then copies the properties from `extra` onto it 2113 */ 2114 var inherit = function (parent, extra) { return extend(Object.create(parent), extra); }; 2115 /** Given an array, returns true if the object is found in the array, (using indexOf) */ 2116 var inArray = curry(_inArray); 2117 function _inArray(array, obj) { 2118 return array.indexOf(obj) !== -1; 2119 } 2120 /** 2121 * Given an array, and an item, if the item is found in the array, it removes it (in-place). 2122 * The same array is returned 2123 */ 2124 var removeFrom = curry(_removeFrom); 2125 function _removeFrom(array, obj) { 2126 var idx = array.indexOf(obj); 2127 if (idx >= 0) 2128 array.splice(idx, 1); 2129 return array; 2130 } 2131 /** pushes a values to an array and returns the value */ 2132 var pushTo = curry(_pushTo); 2133 function _pushTo(arr, val) { 2134 return arr.push(val), val; 2135 } 2136 /** Given an array of (deregistration) functions, calls all functions and removes each one from the source array */ 2137 var deregAll = function (functions) { 2138 return functions.slice().forEach(function (fn) { 2139 typeof fn === 'function' && fn(); 2140 removeFrom(functions, fn); 2141 }); 2142 }; 2143 /** 2144 * Applies a set of defaults to an options object. The options object is filtered 2145 * to only those properties of the objects in the defaultsList. 2146 * Earlier objects in the defaultsList take precedence when applying defaults. 2147 */ 2148 function defaults(opts) { 2149 var defaultsList = []; 2150 for (var _i = 1; _i < arguments.length; _i++) { 2151 defaultsList[_i - 1] = arguments[_i]; 2152 } 2153 var defaultVals = extend.apply(void 0, __spreadArrays$1([{}], defaultsList.reverse())); 2154 return extend(defaultVals, pick(opts || {}, Object.keys(defaultVals))); 2155 } 2156 /** Reduce function that merges each element of the list into a single object, using extend */ 2157 var mergeR = function (memo, item) { return extend(memo, item); }; 2158 /** 2159 * Finds the common ancestor path between two states. 2160 * 2161 * @param {Object} first The first state. 2162 * @param {Object} second The second state. 2163 * @return {Array} Returns an array of state names in descending order, not including the root. 2164 */ 2165 function ancestors(first, second) { 2166 var path = []; 2167 // tslint:disable-next-line:forin 2168 for (var n in first.path) { 2169 if (first.path[n] !== second.path[n]) 2170 break; 2171 path.push(first.path[n]); 2172 } 2173 return path; 2174 } 2175 /** 2176 * Return a copy of the object only containing the whitelisted properties. 2177 * 2178 * #### Example: 2179 * ``` 2180 * var foo = { a: 1, b: 2, c: 3 }; 2181 * var ab = pick(foo, ['a', 'b']); // { a: 1, b: 2 } 2182 * ``` 2183 * @param obj the source object 2184 * @param propNames an Array of strings, which are the whitelisted property names 2185 */ 2186 function pick(obj, propNames) { 2187 var objCopy = {}; 2188 for (var _prop in obj) { 2189 if (propNames.indexOf(_prop) !== -1) { 2190 objCopy[_prop] = obj[_prop]; 2191 } 2192 } 2193 return objCopy; 2194 } 2195 /**
2196 * Return a copy of the object omitting the blacklisted properties. 2197 * 2198 * @example 2199 * ``` 2200 * 2201 * var foo = { a: 1, b: 2, c: 3 }; 2202 * var ab = omit(foo, ['a', 'b']); // { c: 3 } 2203 * ``` 2204 * @param obj the source object 2205 * @param propNames an Array of strings, which are the blacklisted property names 2206 */ 2207 function omit(obj, propNames) { 2208 return Object.keys(obj) 2209 .filter(not(inArray(propNames))) 2210 .reduce(function (acc, key) { return ((acc[key] = obj[key]), acc); }, {}); 2211 } 2212 /** 2213 * Maps an array, or object to a property (by name) 2214 */ 2215 function pluck(collection, propName) { 2216 return map(collection, prop(propName)); 2217 } 2218 /** Filters an Array or an Object's properties based on a predicate */ 2219 function filter(collection, callback) { 2220 var arr = isArray(collection), result = arr ? [] : {}; 2221 var accept = arr ? function (x) { return result.push(x); } : function (x, key) { return (result[key] = x); }; 2222 forEach(collection, function (item, i) { 2223 if (callback(item, i)) 2224 accept(item, i); 2225 }); 2226 return result; 2227 } 2228 /** Finds an object from an array, or a property of an object, that matches a predicate */ 2229 function find(collection, callback) { 2230 var result; 2231 forEach(collection, function (item, i) { 2232 if (result) 2233 return; 2234 if (callback(item, i)) 2235 result = item; 2236 }); 2237 return result; 2238 } 2239 /** Given an object, returns a new object, where each property is transformed by the callback function */ 2240 var mapObj = map; 2241 /** Maps an array or object properties using a callback function */ 2242 function map(collection, callback, target) { 2243 target = target || (isArray(collection) ? [] : {}); 2244 forEach(collection, function (item, i) { return (target[i] = callback(item, i)); }); 2245 return target; 2246 } 2247 /** 2248 * Given an object, return its enumerable property values 2249 * 2250 * @example 2251 * ``` 2252 * 2253 * let foo = { a: 1, b: 2, c: 3 } 2254 * let vals = values(foo); // [ 1, 2, 3 ] 2255 * ``` 2256 */ 2257 var values = function (obj) { return Object.keys(obj).map(function (key) { return obj[key]; }); }; 2258 /** 2259 * Reduce function that returns true if all of the values are truthy. 2260 * 2261 * @example 2262 * ``` 2263 * 2264 * let vals = [ 1, true, {}, "hello world"]; 2265 * vals.reduce(allTrueR, true); // true 2266 * 2267 * vals.push(0); 2268 * vals.reduce(allTrueR, true); // false 2269 * ``` 2270 */ 2271 var allTrueR = function (memo, elem) { return memo && elem; }; 2272 /** 2273 * Reduce function that returns true if any of the values are truthy. 2274 * 2275 * * @example 2276 * ``` 2277 * 2278 * let vals = [ 0, null, undefined ]; 2279 * vals.reduce(anyTrueR, true); // false 2280 * 2281 * vals.push("hello world"); 2282 * vals.reduce(anyTrueR, true); // true 2283 * ``` 2284 */ 2285 var anyTrueR = function (memo, elem) { return memo || elem; }; 2286 /** 2287 * Reduce function which un-nests a single level of arrays 2288 * @example 2289 * ``` 2290 * 2291 * let input = [ [ "a", "b" ], [ "c", "d" ], [ [ "double", "nested" ] ] ]; 2292 * input.reduce(unnestR, []) // [ "a", "b", "c", "d", [ "double, "nested" ] ] 2293 * ``` 2294 */ 2295 var unnestR = function (memo, elem) { return memo.concat(elem); }; 2296 /** 2297 * Reduce function which recursively un-nests all arrays 2298 * 2299 * @example 2300 * ``` 2301 * 2302 * let input = [ [ "a", "b" ], [ "c", "d" ], [ [ "double", "nested" ] ] ]; 2303 * input.reduce(unnestR, []) // [ "a", "b", "c", "d", "double, "nested" ] 2304 * ``` 2305 */ 2306 var flattenR = function (memo, elem) { 2307 return isArray(elem) ? memo.concat(elem.reduce(flattenR, [])) : pushR(memo, elem); 2308 }; 2309 /** 2310 * Reduce function that pushes an object to an array, then returns the array. 2311 * Mostly just for [[flattenR]] and [[uniqR]] 2312 */ 2313 function pushR(arr, obj) { 2314 arr.push(obj); 2315 return arr; 2316 } 2317 /** Reduce function that filters out duplicates */ 2318 var uniqR = function (acc, token) { return (inArray(acc, token) ? acc : pushR(acc, token)); }; 2319 /** 2320 * Return a new array with a single level of arrays unnested. 2321 * 2322 * @example 2323 * ``` 2324 * 2325 * let input = [ [ "a", "b" ], [ "c", "d" ], [ [ "double", "nested" ] ] ]; 2326 * unnest(input) // [ "a", "b", "c", "d", [ "double, "nested" ] ] 2327 * ``` 2328 */ 2329 var unnest = function (arr) { return arr.reduce(unnestR, []); }; 2330 /** 2331 * Return a completely flattened version of an array.
2332 * 2333 * @example 2334 * ``` 2335 * 2336 * let input = [ [ "a", "b" ], [ "c", "d" ], [ [ "double", "nested" ] ] ]; 2337 * flatten(input) // [ "a", "b", "c", "d", "double, "nested" ] 2338 * ``` 2339 */ 2340 var flatten = function (arr) { return arr.reduce(flattenR, []); }; 2341 /** 2342 * Given a .filter Predicate, builds a .filter Predicate which throws an error if any elements do not pass. 2343 * @example 2344 * ``` 2345 * 2346 * let isNumber = (obj) => typeof(obj) === 'number'; 2347 * let allNumbers = [ 1, 2, 3, 4, 5 ]; 2348 * allNumbers.filter(assertPredicate(isNumber)); //OK 2349 * 2350 * let oneString = [ 1, 2, 3, 4, "5" ]; 2351 * oneString.filter(assertPredicate(isNumber, "Not all numbers")); // throws Error(""Not all numbers""); 2352 * ``` 2353 */ 2354 var assertPredicate = assertFn; 2355 /** 2356 * Given a .map function, builds a .map function which throws an error if any mapped elements do not pass a truthyness test. 2357 * @example 2358 * ``` 2359 * 2360 * var data = { foo: 1, bar: 2 }; 2361 * 2362 * let keys = [ 'foo', 'bar' ] 2363 * let values = keys.map(assertMap(key => data[key], "Key not found")); 2364 * // values is [1, 2] 2365 * 2366 * let keys = [ 'foo', 'bar', 'baz' ] 2367 * let values = keys.map(assertMap(key => data[key], "Key not found")); 2368 * // throws Error("Key not found") 2369 * ``` 2370 */ 2371 var assertMap = assertFn; 2372 function assertFn(predicateOrMap, errMsg) { 2373 if (errMsg === void 0) { errMsg = 'assert failure'; } 2374 return function (obj) { 2375 var result = predicateOrMap(obj); 2376 if (!result) { 2377 throw new Error(isFunction(errMsg) ? errMsg(obj) : errMsg); 2378 } 2379 return result; 2380 }; 2381 } 2382 /** 2383 * Like _.pairs: Given an object, returns an array of key/value pairs 2384 * 2385 * @example 2386 * ``` 2387 * 2388 * pairs({ foo: "FOO", bar: "BAR }) // [ [ "foo", "FOO" ], [ "bar": "BAR" ] ] 2389 * ``` 2390 */ 2391 var pairs = function (obj) { return Object.keys(obj).map(function (key) { return [key, obj[key]]; }); }; 2392 /** 2393 * Given two or more parallel arrays, returns an array of tuples where 2394 * each tuple is composed of [ a[i], b[i], ... z[i] ] 2395 * 2396 * @example 2397 * ``` 2398 * 2399 * let foo = [ 0, 2, 4, 6 ]; 2400 * let bar = [ 1, 3, 5, 7 ]; 2401 * let baz = [ 10, 30, 50, 70 ]; 2402 * arrayTuples(foo, bar); // [ [0, 1], [2, 3], [4, 5], [6, 7] ] 2403 * arrayTuples(foo, bar, baz); // [ [0, 1, 10], [2, 3, 30], [4, 5, 50], [6, 7, 70] ] 2404 * ``` 2405 */ 2406 function arrayTuples() { 2407 var args = []; 2408 for (var _i = 0; _i < arguments.length; _i++) { 2409 args[_i] = arguments[_i]; 2410 } 2411 if (args.length === 0) 2412 return []; 2413 var maxArrayLen = args.reduce(function (min, arr) { return Math.min(arr.length, min); }, 9007199254740991); // aka 2^53 − 1 aka Number.MAX_SAFE_INTEGER 2414 var result = []; 2415 var _loop_1 = function (i) { 2416 // This is a hot function 2417 // Unroll when there are 1-4 arguments 2418 switch (args.length) { 2419 case 1: 2420 result.push([args[0][i]]); 2421 break; 2422 case 2: 2423 result.push([args[0][i], args[1][i]]); 2424 break; 2425 case 3: 2426 result.push([args[0][i], args[1][i], args[2][i]]); 2427 break; 2428 case 4: 2429 result.push([args[0][i], args[1][i], args[2][i], args[3][i]]); 2430 break; 2431 default: 2432 result.push(args.map(function (array) { return array[i]; })); 2433 break; 2434 } 2435 }; 2436 for (var i = 0; i < maxArrayLen; i++) { 2437 _loop_1(i); 2438 } 2439 return result; 2440 } 2441 /** 2442 * Reduce function which builds an object from an array of [key, value] pairs. 2443 * 2444 * Each iteration sets the key/val pair on the memo object, then returns the memo for the next iteration. 2445 * 2446 * Each keyValueTuple should be an array with values [ key: string, value: any ] 2447 * 2448 * @example 2449 * ``` 2450 * 2451 * var pairs = [ ["fookey", "fooval"], ["barkey", "barval"] ] 2452 * 2453 * var pairsToObj = pairs.reduce((memo, pair) => applyPairs(memo, pair), {}) 2454 * // pairsToObj == { fookey: "fooval", barkey: "barval" } 2455 * 2456 * // Or, more simply: 2457 * var pairsToObj = pairs.reduce(applyPairs, {}) 2458 * // pairsToObj == { fookey: "fooval", barkey: "barval" } 2459 * ``` 2460 */ 2461 function applyPairs(memo, keyValTuple) { 2462 var key, value; 2463 if (isArray(keyValTuple)) 2464 key = keyValTuple[0], value = keyValTuple[1]; 2465 if (!isString(key)) 2466 throw new Error('invalid parameters to applyPairs'); 2467 memo[key] = value; 2468 return memo; 2469 } 2470 /** Get the last element of an array */ 2471 function tail(arr) { 2472 return (arr.length && arr[arr.length - 1]) || undefined; 2473 } 2474 /** 2475 * shallow copy from src to dest 2476 */ 2477 function copy(src, dest) { 2478 if (dest) 2479 Object.keys(dest).forEach(function (key) { return delete dest[key]; }); 2480 if (!dest) 2481 dest = {}; 2482 return extend(dest, src); 2483 }
2484 /** Naive forEach implementation works with Objects or Arrays */ 2485 function _forEach(obj, cb, _this) { 2486 if (isArray(obj)) 2487 return obj.forEach(cb, _this); 2488 Object.keys(obj).forEach(function (key) { return cb(obj[key], key); }); 2489 } 2490 function _extend(toObj) { 2491 for (var i = 1; i < arguments.length; i++) { 2492 var obj = arguments[i]; 2493 if (!obj) 2494 continue; 2495 var keys = Object.keys(obj); 2496 for (var j = 0; j < keys.length; j++) { 2497 toObj[keys[j]] = obj[keys[j]]; 2498 } 2499 } 2500 return toObj; 2501 } 2502 function _equals(o1, o2) { 2503 if (o1 === o2) 2504 return true; 2505 if (o1 === null || o2 === null) 2506 return false; 2507 if (o1 !== o1 && o2 !== o2) 2508 return true; // NaN === NaN 2509 var t1 = typeof o1, t2 = typeof o2; 2510 if (t1 !== t2 || t1 !== 'object') 2511 return false; 2512 var tup = [o1, o2]; 2513 if (all(isArray)(tup)) 2514 return _arraysEq(o1, o2); 2515 if (all(isDate)(tup)) 2516 return o1.getTime() === o2.getTime(); 2517 if (all(isRegExp)(tup)) 2518 return o1.toString() === o2.toString(); 2519 if (all(isFunction)(tup)) 2520 return true; // meh 2521 var predicates = [isFunction, isArray, isDate, isRegExp]; 2522 if (predicates.map(any).reduce(function (b, fn) { return b || !!fn(tup); }, false)) 2523 return false; 2524 var keys = {}; 2525 // tslint:disable-next-line:forin 2526 for (var key in o1) { 2527 if (!_equals(o1[key], o2[key])) 2528 return false; 2529 keys[key] = true; 2530 } 2531 for (var key in o2) { 2532 if (!keys[key]) 2533 return false; 2534 } 2535 return true; 2536 } 2537 function _arraysEq(a1, a2) { 2538 if (a1.length !== a2.length) 2539 return false; 2540 return arrayTuples(a1, a2).reduce(function (b, t) { return b && _equals(t[0], t[1]); }, true); 2541 } 2542 // issue #2676 2543 var silenceUncaughtInPromise = function (promise) { return promise.catch(function (e) { return 0; }) && promise; }; 2544 var silentRejection = function (error) { return silenceUncaughtInPromise(services.$q.reject(error)); }; 2545 2546 /** 2547 * Matches state names using glob-like pattern strings. 2548 * 2549 * Globs can be used in specific APIs including: 2550 * 2551 * - [[StateService.is]] 2552 * - [[StateService.includes]] 2553 * - The first argument to Hook Registration functions like [[TransitionService.onStart]] 2554 * - [[HookMatchCriteria]] and [[HookMatchCriterion]] 2555 * 2556 * A `Glob` string is a pattern which matches state names. 2557 * Nested state names are split into segments (separated by a dot) when processing. 2558 * The state named `foo.bar.baz` is split into three segments ['foo', 'bar', 'baz'] 2559 * 2560 * Globs work according to the following rules: 2561 * 2562 * ### Exact match: 2563 * 2564 * The glob `'A.B'` matches the state named exactly `'A.B'`. 2565 * 2566 * | Glob |Matches states named|Does not match state named| 2567 * |:------------|:--------------------|:---------------------| 2568 * | `'A'` | `'A'` | `'B'` , `'A.C'` | 2569 * | `'A.B'` | `'A.B'` | `'A'` , `'A.B.C'` | 2570 * | `'foo'` | `'foo'` | `'FOO'` , `'foo.bar'`| 2571 * 2572 * ### Single star (`*`) 2573 * 2574 * A single star (`*`) is a wildcard that matches exactly one segment. 2575 * 2576 * | Glob |Matches states named |Does not match state named | 2577 * |:------------|:---------------------|:--------------------------| 2578 * | `'*'` | `'A'` , `'Z'` | `'A.B'` , `'Z.Y.X'` | 2579 * | `'A.*'` | `'A.B'` , `'A.C'` | `'A'` , `'A.B.C'` | 2580 * | `'A.*.*'` | `'A.B.C'` , `'A.X.Y'`| `'A'`, `'A.B'` , `'Z.Y.X'`| 2581 * 2582 * ### Double star (`**`) 2583 * 2584 * A double star (`'**'`) is a wildcard that matches *zero or more segments* 2585 * 2586 * | Glob |Matches states named |Does not match state named | 2587 * |:------------|:----------------------------------------------|:----------------------------------| 2588 * | `'**'` | `'A'` , `'A.B'`, `'Z.Y.X'` | (matches all states) | 2589 * | `'A.**'` | `'A'` , `'A.B'` , `'A.C.X'` | `'Z.Y.X'` | 2590 * | `'**.X'` | `'X'` , `'A.X'` , `'Z.Y.X'` | `'A'` , `'A.login.Z'` | 2591 * | `'A.**.X'` | `'A.X'` , `'A.B.X'` , `'A.B.C.X'` | `'A'` , `'A.B.C'` | 2592 * 2593 * @packageDocumentation 2594 */ 2595 var Glob = /** @class */ (function () { 2596 function Glob(text) { 2597 this.text = text; 2598 this.glob = text.split('.'); 2599 var regexpString = this.text 2600 .split('.') 2601 .map(function (seg) { 2602 if (seg === '**') 2603 return '(?:|(?:\\.[^.]*)*)'; 2604 if (seg === '*') 2605 return '\\.[^.]*'; 2606 return '\\.' + seg; 2607 }) 2608 .join(''); 2609 this.regexp = new RegExp('^' + regexpString + '$'); 2610 } 2611 /** Returns true if the string has glob-like characters in it */ 2612 Glob.is = function (text) { 2613 return !!/[!,*]+/.exec(text); 2614 }; 2615 /** Returns a glob from the string, or null if the string isn't Glob-like */ 2616 Glob.fromString = function (text) { 2617 return Glob.is(text) ? new Glob(text) : null; 2618 }; 2619 Glob.prototype.matches = function (name) { 2620 return this.regexp.test('.' + name); 2621 }; 2622 return Glob; 2623 }()); 2624 2625 var Queue = /** @class */ (function () { 2626 function Queue(_items, _limit) { 2627 if (_items === void 0) { _items = []; } 2628 if (_limit === void 0) { _limit = null; } 2629 this._items = _items; 2630 this._limit = _limit; 2631 this._evictListeners = []; 2632 this.onEvict = pushTo(this._evictListeners); 2633 } 2634 Queue.prototype.enqueue = function (item) { 2635 var items = this._items; 2636 items.push(item); 2637 if (this._limit && items.length > this._limit) 2638 this.evict(); 2639 return item; 2640 }; 2641 Queue.prototype.evict = function () { 2642 var item = this._items.shift();
2643 this._evictListeners.forEach(function (fn) { return fn(item); }); 2644 return item; 2645 }; 2646 Queue.prototype.dequeue = function () { 2647 if (this.size()) 2648 return this._items.splice(0, 1)[0]; 2649 }; 2650 Queue.prototype.clear = function () { 2651 var current = this._items; 2652 this._items = []; 2653 return current; 2654 }; 2655 Queue.prototype.size = function () { 2656 return this._items.length; 2657 }; 2658 Queue.prototype.remove = function (item) { 2659 var idx = this._items.indexOf(item); 2660 return idx > -1 && this._items.splice(idx, 1)[0]; 2661 }; 2662 Queue.prototype.peekTail = function () { 2663 return this._items[this._items.length - 1]; 2664 }; 2665 Queue.prototype.peekHead = function () { 2666 if (this.size()) 2667 return this._items[0]; 2668 }; 2669 return Queue; 2670 }()); 2671 2672 /** An enum for Transition Rejection reasons */ 2673 2674 (function (RejectType) { 2675 /** 2676 * A new transition superseded this one. 2677 * 2678 * While this transition was running, a new transition started. 2679 * This transition is cancelled because it was superseded by new transition. 2680 */ 2681 RejectType[RejectType["SUPERSEDED"] = 2] = "SUPERSEDED"; 2682 /** 2683 * The transition was aborted 2684 * 2685 * The transition was aborted by a hook which returned `false` 2686 */ 2687 RejectType[RejectType["ABORTED"] = 3] = "ABORTED"; 2688 /** 2689 * The transition was invalid 2690 * 2691 * The transition was never started because it was invali
2691d 2692 */ 2693 RejectType[RejectType["INVALID"] = 4] = "INVALID"; 2694 /** 2695 * The transition was ignored 2696 * 2697 * The transition was ignored because it would have no effect. 2698 * 2699 * Either: 2700 * 2701 * - The transition is targeting the current state and parameter values 2702 * - The transition is targeting the same state and parameter values as the currently running transition. 2703 */ 2704 RejectType[RejectType["IGNORED"] = 5] = "IGNORED"; 2705 /** 2706 * The transition errored. 2707 * 2708 * This generally means a hook threw an error or returned a rejected promise 2709 */ 2710 RejectType[RejectType["ERROR"] = 6] = "ERROR"; 2711 })(exports.RejectType || (exports.RejectType = {})); 2712 /** @internal */ 2713 var id = 0; 2714 var Rejection = /** @class */ (function () { 2715 function Rejection(type, message, detail) { 2716 /** @internal */ 2717 this.$id = id++; 2718 this.type = type; 2719 this.message = message; 2720 this.detail = detail; 2721 } 2722 /** Returns true if the obj is a rejected promise created from the `asPromise` factory */ 2723 Rejection.isRejectionPromise = function (obj) { 2724 return obj && typeof obj.then === 'function' && is(Rejection)(obj._transitionRejection); 2725 }; 2726 /** Returns a Rejection due to transition superseded */ 2727 Rejection.superseded = function (detail, options) { 2728 var message = 'The transition has been superseded by a different transition'; 2729 var rejection = new Rejection(exports.RejectType.SUPERSEDED, message, detail); 2730 if (options && options.redirected) { 2731 rejection.redirected = true; 2732 } 2733 return rejection; 2734 }; 2735 /** Returns a Rejection due to redirected transition */ 2736 Rejection.redirected = function (detail) { 2737 return Rejection.superseded(detail, { redirected: true }); 2738 }; 2739 /** Returns a Rejection due to invalid transition */ 2740 Rejection.invalid = function (detail) { 2741 var message = 'This transition is invalid'; 2742 return new Rejection(exports.RejectType.INVALID, message, detail); 2743 }; 2744 /** Returns a Rejection due to ignored transition */ 2745 Rejection.ignored = function (detail) { 2746 var message = 'The transition was ignored'; 2747 return new Rejection(exports.RejectType.IGNORED, message, detail); 2748 }; 2749 /** Returns a Rejection due to aborted transition */ 2750 Rejection.aborted = function (detail) { 2751 var message = 'The transition has been aborted'; 2752 return new Rejection(exports.RejectType.ABORTED, message, detail); 2753 }; 2754 /** Returns a Rejection due to aborted transition */ 2755 Rejection.errored = function (detail) { 2756 var message = 'The transition errored'; 2757 return new Rejection(exports.RejectType.ERROR, message, detail); 2758 }; 2759 /** 2760 * Returns a Rejection 2761 * 2762 * Normalizes a value as a Rejection. 2763 * If the value is already a Rejection, returns it. 2764 * Otherwise, wraps and returns the value as a Rejection (Rejection type: ERROR). 2765 * 2766 * @returns `detail` if it is already a `Rejection`, else returns an ERROR Rejection. 2767 */ 2768 Rejection.normalize = function (detail) { 2769 return is(Rejection)(detail) ? detail : Rejection.errored(detail); 2770 }; 2771 Rejection.prototype.toString = function () { 2772 var detailString = function (d) { return (d && d.toString !== Object.prototype.toString ? d.toString() : stringify(d)); }; 2773 var detail = detailString(this.detail); 2774 var _a = this, $id = _a.$id, type = _a.type, message = _a.message; 2775 return "Transition Rejection($id: " + $id + " type: " + type + ", message: " + message + ", detail: " + detail + ")"; 2776 }; 2777 Rejection.prototype.toPromise = function () { 2778 return extend(silentRejection(this), { _transitionRejection: this }); 2779 }; 2780 return Rejection; 2781 }()); 2782 2783 /**
2784 * Functions that manipulate strings 2785 * 2786 * Although these functions are exported, they are subject to change without notice. 2787 * 2788 * @packageDocumentation 2789 */ 2790 /** 2791 * Returns a string shortened to a maximum length 2792 * 2793 * If the string is already less than the `max` length, return the string. 2794 * Else return the string, shortened to `max - 3` and append three dots ("..."). 2795 * 2796 * @param max the maximum length of the string to return 2797 * @param str the input string 2798 */ 2799 function maxLength(max, str) { 2800 if (str.length <= max) 2801 return str; 2802 return str.substr(0, max - 3) + '...'; 2803 } 2804 /** 2805 * Returns a string, with spaces added to the end, up to a desired str length 2806 * 2807 * If the string is already longer than the desired length, return the string. 2808 * Else returns the string, with extra spaces on the end, such that it reaches `length` characters. 2809 * 2810 * @param length the desired length of the string to return 2811 * @param str the input string 2812 */ 2813 function padString(length, str) { 2814 while (str.length < length) 2815 str += ' '; 2816 return str; 2817 } 2818 function kebobString(camelCase) { 2819 return camelCase 2820 .replace(/^([A-Z])/, function ($1) { return $1.toLowerCase(); }) // replace first char 2821 .replace(/([A-Z])/g, function ($1) { return '-' + $1.toLowerCase(); }); // replace rest 2822 } 2823 function functionToString(fn) { 2824 var fnStr = fnToString(fn); 2825 var namedFunctionMatch = fnStr.match(/^(function [^ ]+\([^)]*\))/); 2826 var toStr = namedFunctionMatch ? namedFunctionMatch[1] : fnStr; 2827 var fnName = fn['name'] || ''; 2828 if (fnName && toStr.match(/function \(/)) { 2829 return 'function ' + fnName + toStr.substr(9); 2830 } 2831 return toStr; 2832 } 2833 function fnToString(fn) { 2834 var _fn = isArray(fn) ? fn.slice(-1)[0] : fn; 2835 return (_fn && _fn.toString()) || 'undefined'; 2836 } 2837 function stringify(o) { 2838 var seen = []; 2839 var isRejection = Rejection.isRejectionPromise; 2840 var hasToString = function (obj) { 2841 return isObject(obj) && !isArray(obj) && obj.constructor !== Object && isFunction(obj.toString); 2842 }; 2843 var stringifyPattern = pattern([ 2844 [isUndefined, val('undefined')], 2845 [isNull, val('null')], 2846 [isPromise, val('[Promise]')], 2847 [isRejection, function (x) { return x._transitionRejection.toString(); }], 2848 [hasToString, function (x) { return x.toString(); }], 2849 [isInjectable, functionToString], 2850 [val(true), identity], 2851 ]); 2852 function format(value) { 2853 if (isObject(value)) { 2854 if (seen.indexOf(value) !== -1) 2855 return '[circular ref]'; 2856 seen.push(value); 2857 } 2858 return stringifyPattern(value); 2859 } 2860 if (isUndefined(o)) { 2861 // Workaround for IE & Edge Spec incompatibility where replacer function would not be called when JSON.stringify 2862 // is given `undefined` as value. To work around that, we simply detect `undefined` and bail out early by 2863 // manually stringifying it. 2864 return format(o); 2865 } 2866 return JSON.stringify(o, function (key, value) { return format(value); }).replace(/\\"/g, '"'); 2867 } 2868 /** Returns a function that splits a string on a character or substring */ 2869 var beforeAfterSubstr = function (char) { 2870 return function (str) { 2871 if (!str) 2872 return ['', '']; 2873 var idx = str.indexOf(char); 2874 if (idx === -1) 2875 return [str, '']; 2876 return [str.substr(0, idx), str.substr(idx + 1)]; 2877 }; 2878 }; 2879 var hostRegex = new RegExp('^(?:[a-z]+:)?//[^/]+/'); 2880 var stripLastPathElement = function (str) { return str.replace(/\/[^/]*$/, ''); }; 2881 var splitHash = beforeAfterSubstr('#'); 2882 var splitQuery = beforeAfterSubstr('?'); 2883 var splitEqual = beforeAfterSubstr('='); 2884 var trimHashVal = function (str) { return (str ? str.replace(/^#/, '') : ''); }; 2885 /** 2886 * Splits on a delimiter, but returns the delimiters in the array 2887 * 2888 * #### Example: 2889 * ```js 2890 * var splitOnSlashes = splitOnDelim('/'); 2891 * splitOnSlashes("/foo"); // ["/", "foo"] 2892 * splitOnSlashes("/foo/"); // ["/", "foo", "/"] 2893 * ``` 2894 */ 2895 function splitOnDelim(delim) { 2896 var re = new RegExp('(' + delim + ')', 'g'); 2897 return function (str) { return str.split(re).filter(identity); }; 2898 } 2899 /** 2900 * Reduce fn that joins neighboring strings 2901 *
2902 * Given an array of strings, returns a new array 2903 * where all neighboring strings have been joined. 2904 * 2905 * #### Example: 2906 * ```js 2907 * let arr = ["foo", "bar", 1, "baz", "", "qux" ]; 2908 * arr.reduce(joinNeighborsR, []) // ["foobar", 1, "bazqux" ] 2909 * ``` 2910 */ 2911 function joinNeighborsR(acc, x) { 2912 if (isString(tail(acc)) && isString(x)) 2913 return acc.slice(0, -1).concat(tail(acc) + x); 2914 return pushR(acc, x); 2915 } 2916 2917 /** 2918 * workaround for missing console object in IE9 when dev tools haven't been opened o_O 2919 * @packageDocumentation 2920 */ 2921 var noopConsoleStub = { log: noop, error: noop, table: noop }; 2922 function ie9Console(console) { 2923 var bound = function (fn) { return Function.prototype.bind.call(fn, console); }; 2924 return { 2925 log: bound(console.log), 2926 error: bound(console.log), 2927 table: bound(console.log), 2928 }; 2929 } 2930 function fallbackConsole(console) { 2931 var log = console.log.bind(console); 2932 var error = console.error ? console.error.bind(console) : log; 2933 var table = console.table ? console.table.bind(console) : log; 2934 return { log: log, error: error, table: table }; 2935 } 2936 function getSafeConsole() { 2937 // @ts-ignore 2938 var isIE9 = typeof document !== 'undefined' && document.documentMode && document.documentMode === 9; 2939 if (isIE9) { 2940 return window && window.console ? ie9Console(window.console) : noopConsoleStub; 2941 } 2942 else if (!console.table || !console.error) { 2943 return fallbackConsole(console); 2944 } 2945 else { 2946 return console; 2947 } 2948 } 2949 var safeConsole = getSafeConsole(); 2950 2951 /** 2952 * # Transition tracing (debug) 2953 * 2954 * Enable transition tracing to print transition information to the console, 2955 * in order to help debug your application. 2956 * Tracing logs detailed information about each Transition to your console. 2957 * 2958 * To enable tracing, import the [[Trace]] singleton and enable one or more categories. 2959 * 2960 * ### ES6 2961 * ```js 2962 * import {trace} from "@uirouter/core"; 2963 * trace.enable(1, 5); // TRANSITION and VIEWCONFIG 2964 * ``` 2965 * 2966 * ### CJS 2967 * ```js 2968 * let trace = require("@uirouter/core").trace; 2969 * trace.enable("TRANSITION", "VIEWCONFIG"); 2970 * ``` 2971 * 2972 * ### Globals 2973 * ```js 2974 * let trace = window["@uirouter/core"].trace; 2975 * trace.enable(); // Trace everything (very verbose) 2976 * ``` 2977 * 2978 * ### Angular 1: 2979 * ```js 2980 * app.run($trace => $trace.enable()); 2981 * ``` 2982 * 2983 * @packageDocumentation 2984 */ 2985 function uiViewString(uiview) { 2986 if (!uiview) 2987 return 'ui-view (defunct)'; 2988 var state = uiview.creationContext ? uiview.creationContext.name || '(root)' : '(none)'; 2989 return "[ui-view#" + uiview.id + " " + uiview.$type + ":" + uiview.fqn + " (" + uiview.name + "@" + state + ")]"; 2990 } 2991 var viewConfigString = function (viewConfig) { 2992 var view = viewConfig.viewDecl; 2993 var state = view.$context.name || '(root)'; 2994 return "[View#" + viewConfig.$id + " from '" + state + "' state]: target ui-view: '" + view.$uiViewName + "@" + view.$uiViewContextAnchor + "'"; 2995 }; 2996 function normalizedCat(input) { 2997 return isNumber(input) ? exports.Category[input] : exports.Category[exports.Category[input]]; 2998 } 2999 /** 3000 * Trace categories Enum 3001 * 3002 * Enable or disable a category using [[Trace.enable]] or [[Trace.disable]] 3003 * 3004 * `trace.enable(Category.TRANSITION)` 3005 * 3006 * These can also be provided using a matching string, or position ordinal 3007 * 3008 * `trace.enable("TRANSITION")` 3009 * 3010 * `trace.enable(1)` 3011 */ 3012 3013 (function (Category) { 3014 Category[Category["RESOLVE"] = 0] = "RESOLVE"; 3015 Category[Category["TRANSITION"] = 1] = "TRANSITION"; 3016 Category[Category["HOOK"] = 2] = "HOOK"; 3017 Category[Category["UIVIEW"] = 3] = "UIVIEW"; 3018 Category[Category["VIEWCONFIG"] = 4] = "VIEWCONFIG"; 3019 })(exports.Category || (exports.Category = {})); 3020 var _tid = parse('$id');
3021 var _rid = parse('router.$id'); 3022 var transLbl = function (trans) { return "Transition #" + _tid(trans) + "-" + _rid(trans); }; 3023 /** 3024 * Prints UI-Router Transition trace information to the console. 3025 */ 3026 var Trace = /** @class */ (function () { 3027 /** @internal */ 3028 function Trace() { 3029 /** @internal */ 3030 this._enabled = {}; 3031 this.approximateDigests = 0; 3032 } 3033 /** @internal */ 3034 Trace.prototype._set = function (enabled, categories) { 3035 var _this = this; 3036 if (!categories.length) { 3037 categories = Object.keys(exports.Category) 3038 .map(function (k) { return parseInt(k, 10); }) 3039 .filter(function (k) { return !isNaN(k); }) 3040 .map(function (key) { return exports.Category[key]; }); 3041 } 3042 categories.map(normalizedCat).forEach(function (category) { return (_this._enabled[category] = enabled); }); 3043 }; 3044 Trace.prototype.enable = function () { 3045 var categories = []; 3046 for (var _i = 0; _i < arguments.length; _i++) { 3047 categories[_i] = arguments[_i]; 3048 } 3049 this._set(true, categories); 3050 }; 3051 Trace.prototype.disable = function () { 3052 var categories = []; 3053 for (var _i = 0; _i < arguments.length; _i++) { 3054 categories[_i] = arguments[_i]; 3055 } 3056 this._set(false, categories); 3057 }; 3058 /** 3059 * Retrieves the enabled stateus of a [[Category]] 3060 * 3061 * ```js 3062 * trace.enabled("VIEWCONFIG"); // true or false 3063 * ``` 3064 * 3065 * @returns boolean true if the category is enabled 3066 */ 3067 Trace.prototype.enabled = function (category) { 3068 return !!this._enabled[normalizedCat(category)]; 3069 }; 3070 /** @internal called by ui-router code */ 3071 Trace.prototype.traceTransitionStart = function (trans) { 3072 if (!this.enabled(exports.Category.TRANSITION)) 3073 return; 3074 safeConsole.log(transLbl(trans) + ": Started -> " + stringify(trans)); 3075 }; 3076 /** @internal called by ui-router code */ 3077 Trace.prototype.traceTransitionIgnored = function (trans) { 3078 if (!this.enabled(exports.Category.TRANSITION)) 3079 return; 3080 safeConsole.log(transLbl(trans) + ": Ignored <> " + stringify(trans)); 3081 }; 3082 /** @internal called by ui-router code */ 3083 Trace.prototype.traceHookInvocation = function (step, trans, options) { 3084 if (!this.enabled(exports.Category.HOOK)) 3085 return; 3086 var event = parse('traceData.hookType')(options) || 'internal', context = parse('traceData.context.state.name')(options) || parse('traceData.context')(options) || 'unknown', name = functionToString(step.registeredHook.callback); 3087 safeConsole.log(transLbl(trans) + ": Hook -> " + event + " context: " + context + ", " + maxLength(200, name)); 3088 }; 3089 /** @internal called by ui-router code */ 3090 Trace.prototype.traceHookResult = function (hookResult, trans, transitionOptions) { 3091 if (!this.enabled(exports.Category.HOOK)) 3092 return; 3093 safeConsole.log(transLbl(trans) + ": <- Hook returned: " + maxLength(200, stringify(hookResult))); 3094 }; 3095 /** @internal called by ui-router code */ 3096 Trace.prototype.traceResolvePath = function (path, when, trans) { 3097 if (!this.enabled(exports.Category.RESOLVE)) 3098 return; 3099 safeConsole.log(transLbl(trans) + ": Resolving " + path + " (" + when + ")"); 3100 }; 3101 /** @internal called by ui-router code */ 3102 Trace.prototype.traceResolvableResolved = function (resolvable, trans) { 3103 if (!this.enabled(exports.Category.RESOLVE)) 3104 return; 3105 safeConsole.log(transLbl(trans) + ": <- Resolved " + resolvable + " to: " + maxLength(200, stringify(resolvable.data))); 3106 };
3107 /** @internal called by ui-router code */ 3108 Trace.prototype.traceError = function (reason, trans) { 3109 if (!this.enabled(exports.Category.TRANSITION)) 3110 return; 3111 safeConsole.log(transLbl(trans) + ": <- Rejected " + stringify(trans) + ", reason: " + reason); 3112 }; 3113 /** @internal called by ui-router code */ 3114 Trace.prototype.traceSuccess = function (finalState, trans) { 3115 if (!this.enabled(exports.Category.TRANSITION)) 3116 return; 3117 safeConsole.log(transLbl(trans) + ": <- Success " + stringify(trans) + ", final state: " + finalState.name); 3118 }; 3119 /** @internal called by ui-router code */ 3120 Trace.prototype.traceUIViewEvent = function (event, viewData, extra) { 3121 if (extra === void 0) { extra = ''; } 3122 if (!this.enabled(exports.Category.UIVIEW)) 3123 return; 3124 safeConsole.log("ui-view: " + padString(30, event) + " " + uiViewString(viewData) + extra); 3125 }; 3126 /** @internal called by ui-router code */ 3127 Trace.prototype.traceUIViewConfigUpdated = function (viewData, context) { 3128 if (!this.enabled(exports.Category.UIVIEW)) 3129 return; 3130 this.traceUIViewEvent('Updating', viewData, " with ViewConfig from context='" + context + "'"); 3131 }; 3132 /** @internal called by ui-router code */ 3133 Trace.prototype.traceUIViewFill = function (viewData, html) { 3134 if (!this.enabled(exports.Category.UIVIEW)) 3135 return; 3136 this.traceUIViewEvent('Fill', viewData, " with: " + maxLength(200, html)); 3137 }; 3138 /** @internal called by ui-router code */ 3139 Trace.prototype.traceViewSync = function (pairs) { 3140 if (!this.enabled(exports.Category.VIEWCONFIG)) 3141 return; 3142 var uivheader = 'uiview component fqn'; 3143 var cfgheader = 'view config state (view name)'; 3144 var mapping = pairs 3145 .map(function (_a) { 3146 var _b; 3147 var uiView = _a.uiView, viewConfig = _a.viewConfig; 3148 var uiv = uiView && uiView.fqn; 3149 var cfg = viewConfig && viewConfig.viewDecl.$context.name + ": (" + viewConfig.viewDecl.$name + ")"; 3150 return _b = {}, _b[uivheader] = uiv, _b[cfgheader] = cfg, _b; 3151 }) 3152 .sort(function (a, b) { return (a[uivheader] || '').localeCompare(b[uivheader] || ''); }); 3153 safeConsole.table(mapping); 3154 }; 3155 /** @internal called by ui-router code */ 3156 Trace.prototype.traceViewServiceEvent = function (event, viewConfig) { 3157 if (!this.enabled(exports.Category.VIEWCONFIG)) 3158 return; 3159 safeConsole.log("VIEWCONFIG: " + event + " " + viewConfigString(viewConfig)); 3160 }; 3161 /** @internal called by ui-router code */ 3162 Trace.prototype.traceViewServiceUIViewEvent = function (event, viewData) { 3163 if (!this.enabled(exports.Category.VIEWCONFIG)) 3164 return; 3165 safeConsole.log("VIEWCONFIG: " + event + " " + uiViewString(viewData)); 3166 }; 3167 return Trace; 3168 }()); 3169 /** 3170 * The [[Trace]] singleton 3171 * 3172 * #### Example: 3173 * ```js 3174 * import {trace} from "@uirouter/core"; 3175 * trace.enable(1, 5); 3176 * ``` 3177 */ 3178 var trace = new Trace(); 3179 3180 /** 3181 * An internal class which implements [[ParamTypeDefinition]]. 3182 * 3183 * A [[ParamTypeDefinition]] is a plain javascript object used to register custom parameter types. 3184 * When a param type definition is registered, an instance of this class is created internally. 3185 * 3186 * This class has naive implementations for all the [[ParamTypeDefinition]] methods. 3187 * 3188 * Used by [[UrlMatcher]] when matching or formatting URLs, or comparing and validating parameter values. 3189 * 3190 * #### Example: 3191 * ```js 3192 * var paramTypeDef = { 3193 * decode: function(val) { return parseInt(val, 10); }, 3194 * encode: function(val) { return val && val.toString(); }, 3195 * equals: function(a, b) { return this.is(a) && a === b; }, 3196 * is: function(val) { return angular.isNumber(val) && isFinite(val) && val % 1 === 0; }, 3197 * pattern: /\d+/ 3198 * } 3199 * 3200 * var paramType = new ParamType(paramTypeDef); 3201 * ``` 3202 */ 3203 var ParamType = /** @class */ (function () { 3204 /** 3205 * @param def A configuration object which contains the custom type definition. The object's 3206 * properties will override the default methods and/or pattern in `ParamType`'s public interface. 3207 * @returns a new ParamType object 3208 */ 3209 function ParamType(def) { 3210 /** @inheritdoc */ 3211 this.pattern = /.*/; 3212 /** @inheritdoc */ 3213 this.inherit = true; 3214 extend(this, def); 3215 } 3216 // consider these four methods to be "abstract methods" that should be overridden 3217 /** @inheritdoc */ 3218 ParamType.prototype.is = function (val, key) { 3219 return true; 3220 }; 3221 /** @inheritdoc */ 3222 ParamType.prototype.encode = function (val, key) { 3223 return val; 3224 }; 3225 /** @inheritdoc */ 3226 ParamType.prototype.decode = function (val, key) { 3227 return val; 3228 }; 3229 /** @inheritdoc */ 3230 ParamType.prototype.equals = function (a, b) { 3231 // tslint:disable-next-line:triple-equals 3232 return a == b; 3233 }; 3234 ParamType.prototype.$subPattern = function () { 3235 var sub = this.pattern.toString(); 3236 return sub.substr(1, sub.length - 2); 3237 }; 3238 ParamType.prototype.toString = function () { 3239 return "{ParamType:" + this.name + "}"; 3240 }; 3241 /** Given an encoded string, or a decoded object, returns a decoded object */ 3242 ParamType.prototype.$normalize = function (val) { 3243 return this.is(val) ? val : this.decode(val); 3244 }; 3245 /** 3246 * Wraps an existing custom ParamType as an array of ParamType, depending on 'mode'. 3247 * e.g.: 3248 * - urlmatcher pattern "/path?{queryParam[]:int}" 3249 * - url: "/path?queryParam=1&queryParam=2 3250 * - $stateParams.queryParam will be [1, 2] 3251 * if `mode` is "auto", then 3252 * - url: "/path?queryParam=1 will create $stateParams.queryParam: 1 3253 * - url: "/path?queryParam=1&queryParam=2 will create $stateParams.queryParam: [1, 2] 3254 */ 3255 ParamType.prototype.$asArray = function (mode, isSearch) { 3256 if (!mode) 3257 return this; 3258 if (mode === 'auto' && !isSearch) 3259 throw new Error("'auto' array mode is for query parameters only"); 3260 return new ArrayType(this, mode); 3261 }; 3262 return ParamType; 3263 }()); 3264 /** Wraps up a `ParamType` object to handle array values. */ 3265 function ArrayType(type, mode) { 3266 var _this = this; 3267 // Wrap non-array value as array 3268 function arrayWrap(val) { 3269 return isArray(val) ? val : isDefined(val) ? [val] : []; 3270 } 3271 // Unwrap array value for "auto" mode. Return undefined for empty array. 3272 function arrayUnwrap(val) { 3273 switch (val.length) { 3274 case 0: 3275 return undefined; 3276 case 1: 3277 return mode === 'auto' ? val[0] : val; 3278 default: 3279 return val; 3280 } 3281 } 3282 // Wraps type (.is/.encode/.decode) functions to operate on each value of an array 3283 function arrayHandler(callback, allTruthyMode) { 3284 return function handleArray(val) { 3285 if (isArray(val) && val.length === 0) 3286 return val; 3287 var arr = arrayWrap(val); 3288 var result = map(arr, callback); 3289 return allTruthyMode === true ? filter(result, function (x) { return !x; }).length === 0 : arrayUnwrap(result); 3290 }; 3291 } 3292 // Wraps type (.equals) functions to operate on each value of an array 3293 function arrayEqualsHandler(callback) { 3294 return function handleArray(val1, val2) { 3295 var left = arrayWrap(val1), right = arrayWrap(val2); 3296 if (left.length !== right.length) 3297 return false;
3298 for (var i = 0; i < left.length; i++) { 3299 if (!callback(left[i], right[i])) 3300 return false; 3301 } 3302 return true; 3303 }; 3304 } 3305 ['encode', 'decode', 'equals', '$normalize'].forEach(function (name) { 3306 var paramTypeFn = type[name].bind(type); 3307 var wrapperFn = name === 'equals' ? arrayEqualsHandler : arrayHandler; 3308 _this[name] = wrapperFn(paramTypeFn); 3309 }); 3310 extend(this, { 3311 dynamic: type.dynamic, 3312 name: type.name, 3313 pattern: type.pattern, 3314 inherit: type.inherit, 3315 raw: type.raw, 3316 is: arrayHandler(type.is.bind(type), true), 3317 $arrayMode: mode, 3318 }); 3319 } 3320 3321 var hasOwn = Object.prototype.hasOwnProperty; 3322 var isShorthand = function (cfg) { 3323 return ['value', 'type', 'squash', 'array', 'dynamic'].filter(hasOwn.bind(cfg || {})).length === 0; 3324 }; 3325 3326 (function (DefType) { 3327 DefType[DefType["PATH"] = 0] = "PATH"; 3328 DefType[DefType["SEARCH"] = 1] = "SEARCH"; 3329 DefType[DefType["CONFIG"] = 2] = "CONFIG"; 3330 })(exports.DefType || (exports.DefType = {})); 3331 function getParamDeclaration(paramName, location, state) { 3332 var noReloadOnSearch = (state.reloadOnSearch === false && location === exports.DefType.SEARCH) || undefined; 3333 var dynamic = find([state.dynamic, noReloadOnSearch], isDefined); 3334 var defaultConfig = isDefined(dynamic) ? { dynamic: dynamic } : {}; 3335 var paramConfig = unwrapShorthand(state && state.params && state.params[paramName]); 3336 return extend(defaultConfig, paramConfig); 3337 } 3338 function unwrapShorthand(cfg) { 3339 cfg = isShorthand(cfg) ? { value: cfg } : cfg; 3340 getStaticDefaultValue['__cacheable'] = true; 3341 function getStaticDefaultValue() { 3342 return cfg.value; 3343 } 3344 var $$fn = isInjectable(cfg.value) ? cfg.value : getStaticDefaultValue; 3345 return extend(cfg, { $$fn: $$fn }); 3346 } 3347 function getType(cfg, urlType, location, id, paramTypes) { 3348 if (cfg.type && urlType && urlType.name !== 'string') 3349 throw new Error("Param '" + id + "' has two type configurations."); 3350 if (cfg.type && urlType && urlType.name === 'string' && paramTypes.type(cfg.type)) 3351 return paramTypes.type(cfg.type); 3352 if (urlType) 3353 return urlType; 3354 if (!cfg.type) { 3355 var type = location === exports.DefType.CONFIG 3356 ? 'any' 3357 : location === exports.DefType.PATH 3358 ? 'path' 3359 : location === exports.DefType.SEARCH 3360 ? 'query' 3361 : 'string'; 3362 return paramTypes.type(type); 3363 } 3364 return cfg.type instanceof ParamType ? cfg.type : paramTypes.type(cfg.type); 3365 } 3366 /** returns false, true, or the squash value to indicate the "default parameter url squash policy". */ 3367 function getSquashPolicy(config, isOptional, defaultPolicy) { 3368 var squash = config.squash; 3369 if (!isOptional || squash === false) 3370 return false; 3371 if (!isDefined(squash) || squash == null) 3372 return defaultPolicy; 3373 if (squash === true || isString(squash)) 3374 return squash; 3375 throw new Error("Invalid squash policy: '" + squash + "'. Valid policies: false, true, or arbitrary string"); 3376 } 3377 function getReplace(config, arrayMode, isOptional, squash) { 3378 var defaultPolicy = [ 3379 { from: '', to: isOptional || arrayMode ? undefined : '' }, 3380 { from: null, to: isOptional || arrayMode ? undefined : '' }, 3381 ]; 3382 var replace = isArray(config.replace) ? config.replace : []; 3383 if (isString(squash)) 3384 replace.push({ from: squash, to: undefined }); 3385 var configuredKeys = map(replace, prop('from')); 3386 return filter(defaultPolicy, function (item) { return configuredKeys.indexOf(item.from) === -1; }).concat(replace); 3387 } 3388 var Param = /** @class */ (function () { 3389 function Param(id, type, location, urlConfig, state) { 3390 var config = getParamDeclaration(id, location, state); 3391 type = getType(config, type, location, id, urlConfig.paramTypes); 3392 var arrayMode = getArrayMode(); 3393 type = arrayMode ? type.$asArray(arrayMode, location === exports.DefType.SEARCH) : type; 3394 var isOptional = config.value !== undefined || location === exports.DefType.SEARCH; 3395 var dynamic = isDefined(config.dynamic) ? !!config.dynamic : !!type.dynamic; 3396 var raw = isDefined(config.raw) ? !!config.raw : !!type.raw; 3397 var squash = getSquashPolicy(config, isOptional, urlConfig.defaultSquashPolicy()); 3398 var replace = getReplace(config, arrayMode, isOptional, squash); 3399 var inherit = isDefined(config.inherit) ? !!config.inherit : !!type.inherit; 3400 // array config: param name (param[]) overrides default settings. explicit config overrides param name. 3401 function getArrayMode() { 3402 var arrayDefaults = { array: location === exports.DefType.SEARCH ? 'auto' : false }; 3403 var arrayParamNomenclature = id.match(/\[\]$/) ? { array: true } : {}; 3404 return extend(arrayDefaults, arrayParamNomenclature, config).array; 3405 } 3406 extend(this, { id: id, type: type, location: location, isOptional: isOptional, dynamic: dynamic, raw: raw, squash: squash, replace: replace, inherit: inherit, array: arrayMode, config: config }); 3407 } 3408 Param.values = function (params, values) { 3409 if (values === void 0) { values = {}; } 3410 var paramValues = {}; 3411 for (var _i = 0, params_1 = params; _i < params_1.length; _i++) { 3412 var param = params_1[_i]; 3413 paramValues[param.id] = param.value(values[param.id]); 3414 } 3415 return paramValues; 3416 }; 3417 /** 3418 * Finds [[Param]] objects which have different param values 3419 * 3420 * Filters a list of [[Param]] objects to only those whose parameter values differ in two param value objects 3421 * 3422 * @param params: The list of Param objects to filter 3423 * @param values1: The first set of parameter values 3424 * @param values2: the second set of parameter values 3425 * 3426 * @returns any Param objects whose values were different between values1 and values2 3427 */ 3428 Param.changed = function (params, values1, values2) { 3429 if (values1 === void 0) { values1 = {}; } 3430 if (values2 === void 0) { values2 = {}; } 3431 return params.filter(function (param) { return !param.type.equals(values1[param.id], values2[param.id]); }); 3432 }; 3433 /** 3434 * Checks if two param value objects are equal (for a set of [[Param]] objects) 3435 * 3436 * @param params The list of [[Param]] objects to check 3437 * @param values1 The first set of param values 3438 * @param values2 The second set of param values 3439 * 3440 * @returns true if the param values in values1 and values2 are equal 3441 */ 3442 Param.equals = function (params, values1, values2) { 3443 if (values1 === void 0) { values1 = {}; } 3444 if (values2 === void 0) { values2 = {}; } 3445 return Param.changed(params, values1, values2).length === 0; 3446 }; 3447 /** Returns true if a the parameter values are valid, according to the Param definitions */ 3448 Param.validates = function (params, values) { 3449 if (values === void 0) { values = {}; } 3450 return params.map(function (param) { return param.validates(values[param.id]); }).reduce(allTrueR, true); 3451 }; 3452 Param.prototype.isDefaultValue = function (value) { 3453 return this.isOptional && this.type.equals(this.value(), value); 3454 }; 3455 /** 3456 * [Internal] Gets the decoded representation of a value if the value is defined, otherwise, returns the 3457 * default value, which may be the result of an injectable function. 3458 */ 3459 Param.prototype.value = function (value) { 3460 var _this = this; 3461 /** 3462 * [Internal] Get the default value of a parameter, which may be an injectable function. 3463 */ 3464 var getDefaultValue = function () { 3465 if (_this._defaultValueCache) 3466 return _this._defaultValueCache.defaultValue; 3467 if (!services.$injector) 3468 throw new Error('Injectable functions cannot be called at configuration time'); 3469 var defaultValue = services.$injector.invoke(_this.config.$$fn); 3470 if (defaultValue !== null && defaultValue !== undefined && !_this.type.is(defaultValue)) 3471 throw new Error("Default value (" + defaultValue + ") for parameter '" + _this.id + "' is not an instance of ParamType (" + _this.type.name + ")"); 3472 if (_this.config.$$fn['__cacheable']) { 3473 _this._defaultValueCache = { defaultValue: defaultValue }; 3474 } 3475 return defaultValue; 3476 }; 3477 var replaceSpecialValues = function (val) { 3478 for (var _i = 0, _a = _this.replace; _i < _a.length; _i++) { 3479 var tuple = _a[_i]; 3480 if (tuple.from === val) 3481 return tuple.to; 3482 } 3483 return val; 3484 }; 3485 value = replaceSpecialValues(value); 3486 return isUndefined(value) ? getDefaultValue() : this.type.$normalize(value); 3487 }; 3488 Param.prototype.isSearch = function () { 3489 return this.location === exports.DefType.SEARCH; 3490 }; 3491 Param.prototype.validates = function (value) { 3492 // There was no parameter value, but the param is optional 3493 if ((isUndefined(value) || value === null) && this.isOptional) 3494 return true; 3495 // The value was not of the correct ParamType, and could not be decoded to the correct ParamType 3496 var normalized = this.type.$normalize(value); 3497 if (!this.type.is(normalized)) 3498 return false;
3499 // The value was of the correct type, but when encoded, did not match the ParamType's regexp 3500 var encoded = this.type.encode(normalized); 3501 return !(isString(encoded) && !this.type.pattern.exec(encoded)); 3502 }; 3503 Param.prototype.toString = function () { 3504 return "{Param:" + this.id + " " + this.type + " squash: '" + this.squash + "' optional: " + this.isOptional + "}"; 3505 }; 3506 return Param; 3507 }()); 3508 3509 /** 3510 * A registry for parameter types. 3511 * 3512 * This registry manages the built-in (and custom) parameter types. 3513 * 3514 * The built-in parameter types are: 3515 * 3516 * - [[string]] 3517 * - [[path]] 3518 * - [[query]] 3519 * - [[hash]] 3520 * - [[int]] 3521 * - [[bool]] 3522 * - [[date]] 3523 * - [[json]] 3524 * - [[any]] 3525 * 3526 * To register custom parameter types, use [[UrlConfig.type]], i.e., 3527 * 3528 * ```js 3529 * router.urlService.config.type(customType) 3530 * ``` 3531 */ 3532 var ParamTypes = /** @class */ (function () { 3533 function ParamTypes() { 3534 this.enqueue = true; 3535 this.typeQueue = []; 3536 this.defaultTypes = pick(ParamTypes.prototype, [ 3537 'hash', 3538 'string', 3539 'query', 3540 'path', 3541 'int', 3542 'bool', 3543 'date', 3544 'json', 3545 'any', 3546 ]); 3547 // Register default types. Store them in the prototype of this.types. 3548 var makeType = function (definition, name) { return new ParamType(extend({ name: name }, definition)); }; 3549 this.types = inherit(map(this.defaultTypes, makeType), {}); 3550 } 3551 ParamTypes.prototype.dispose = function () { 3552 this.types = {}; 3553 }; 3554 /** 3555 * Registers a parameter type 3556 * 3557 * End users should call [[UrlMatcherFactory.type]], which delegates to this method. 3558 */ 3559 ParamTypes.prototype.type = function (name, definition, definitionFn) { 3560 if (!isDefined(definition)) 3561 return this.types[name]; 3562 if (this.types.hasOwnProperty(name)) 3563 throw new Error("A type named '" + name + "' has already been defined."); 3564 this.types[name] = new ParamType(extend({ name: name }, definition)); 3565 if (definitionFn) { 3566 this.typeQueue.push({ name: name, def: definitionFn }); 3567 if (!this.enqueue) 3568 this._flushTypeQueue(); 3569 } 3570 return this; 3571 }; 3572 ParamTypes.prototype._flushTypeQueue = function () { 3573 while (this.typeQueue.length) { 3574 var type = this.typeQueue.shift(); 3575 if (type.pattern) 3576 throw new Error("You cannot override a type's .pattern at runtime."); 3577 extend(this.types[type.name], services.$injector.invoke(type.def)); 3578 } 3579 }; 3580 return ParamTypes; 3581 }()); 3582 function initDefaultTypes() { 3583 var makeDefaultType = function (def) { 3584 var valToString = function (val) { return (val != null ? val.toString() : val); }; 3585 var defaultTypeBase = { 3586 encode: valToString, 3587 decode: valToString, 3588 is: is(String), 3589 pattern: /.*/, 3590 // tslint:disable-next-line:triple-equals 3591 equals: function (a, b) { return a == b; }, 3592 }; 3593 return extend({}, defaultTypeBase, def); 3594 }; 3595 // Default Parameter Type Definitions 3596 extend(ParamTypes.prototype, { 3597 string: makeDefaultType({}), 3598 path: makeDefaultType({ 3599 pattern: /[^/]*/, 3600 }), 3601 query: makeDefaultType({}), 3602 hash: makeDefaultType({ 3603 inherit: false, 3604 }), 3605 int: makeDefaultType({ 3606 decode: function (val) { return parseInt(val, 10); }, 3607 is: function (val) { 3608 return !isNullOrUndefined(val) && this.decode(val.toString()) === val; 3609 }, 3610 pattern: /-?\d+/, 3611 }), 3612 bool: makeDefaultType({ 3613 encode: function (val) { return (val && 1) || 0; }, 3614 decode: function (val) { return parseInt(val, 10) !== 0; }, 3615 is: is(Boolean), 3616 pattern: /0|1/, 3617 }), 3618 date: makeDefaultType({ 3619 encode: function (val) { 3620 return !this.is(val) 3621 ? undefined 3622 : [val.getFullYear(), ('0' + (val.getMonth() + 1)).slice(-2), ('0' + val.getDate()).slice(-2)].join('-'); 3623 }, 3624 decode: function (val) { 3625 if (this.is(val)) 3626 return val; 3627 var match = this.capture.exec(val); 3628 return match ? new Date(match[1], match[2] - 1, match[3]) : undefined; 3629 }, 3630 is: function (val) { return val instanceof Date && !isNaN(val.valueOf()); }, 3631 equals: function (l, r) { 3632 return ['getFullYear', 'getMonth', 'getDate'].reduce(function (acc, fn) { return acc && l[fn]() === r[fn](); }, true); 3633 }, 3634 pattern: /[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/, 3635 capture: /([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/, 3636 }), 3637 json: makeDefaultType({ 3638 encode: toJson, 3639 decode: fromJson, 3640 is: is(Object), 3641 equals: equals, 3642 pattern: /[^/]*/, 3643 }), 3644 // does not encode/decode 3645 any: makeDefaultType({ 3646 encode: identity, 3647 decode: identity, 3648 is: function () { return true; }, 3649 equals: equals, 3650 }), 3651 }); 3652 } 3653 initDefaultTypes(); 3654 3655 var StateParams = /** @class */ (function () { 3656 function StateParams(params) { 3657 if (params === void 0) { params = {}; } 3658 extend(this, params); 3659 } 3660 /** 3661 * Merges a set of parameters with all parameters inherited between the common parents of the 3662 * current state and a given destination state. 3663 * 3664 * @param {Object}
3664 newParams The set of parameters which will be composited with inherited params. 3665 * @param {Object} $current Internal definition of object representing the current state. 3666 * @param {Object} $to Internal definition of object representing state to transition to. 3667 */ 3668 StateParams.prototype.$inherit = function (newParams, $current, $to) { 3669 var parentParams; 3670 var parents = ancestors($current, $to), inherited = {}, inheritList = []; 3671 for (var i in parents) { 3672 if (!parents[i] || !parents[i].params) 3673 continue; 3674 parentParams = Object.keys(parents[i].params); 3675 if (!parentParams.length) 3676 continue; 3677 for (var j in parentParams) { 3678 if (inheritList.indexOf(parentParams[j]) >= 0) 3679 continue; 3680 inheritList.push(parentParams[j]); 3681 inherited[parentParams[j]] = this[parentParams[j]]; 3682 } 3683 } 3684 return extend({}, inherited, newParams); 3685 }; 3686 return StateParams; 3687 }()); 3688 3689 /** 3690 * A node in a [[TreeChanges]] path 3691 * 3692 * For a [[TreeChanges]] path, this class holds the stateful information for a single node in the path. 3693 * Each PathNode corresponds to a state being entered, exited, or retained. 3694 * The stateful information includes parameter values and resolve data. 3695 */ 3696 var PathNode = /** @class */ (function () { 3697 function PathNode(stateOrNode) { 3698 if (stateOrNode instanceof PathNode) { 3699 var node = stateOrNode; 3700 this.state = node.state; 3701 this.paramSchema = node.paramSchema.slice(); 3702 this.paramValues = extend({}, node.paramValues); 3703 this.resolvables = node.resolvables.slice(); 3704 this.views = node.views && node.views.slice(); 3705 } 3706 else { 3707 var state = stateOrNode; 3708 this.state = state; 3709 this.paramSchema = state.parameters({ inherit: false }); 3710 this.paramValues = {}; 3711 this.resolvables = state.resolvables.map(function (res) { return res.clone(); }); 3712 } 3713 } 3714 PathNode.prototype.clone = function () { 3715 return new PathNode(this); 3716 }; 3717 /** Sets [[paramValues]] for the node, from the values of an object hash */ 3718 PathNode.prototype.applyRawParams = function (params) { 3719 var getParamVal = function (paramDef) { return [paramDef.id, paramDef.value(params[paramDef.id])]; }; 3720 this.paramValues = this.paramSchema.reduce(function (memo, pDef) { return applyPairs(memo, getParamVal(pDef)); }, {}); 3721 return this; 3722 }; 3723 /** Gets a specific [[Param]] metadata that belongs to the node */ 3724 PathNode.prototype.parameter = function (name) { 3725 return find(this.paramSchema, propEq('id', name)); 3726 }; 3727 /** 3728 * @returns true if the state and parameter values for another PathNode are 3729 * equal to the state and param values for this PathNode 3730 */ 3731 PathNode.prototype.equals = function (node, paramsFn) { 3732 var diff = this.diff(node, paramsFn); 3733 return diff && diff.length === 0; 3734 }; 3735 /** 3736 * Finds Params with different parameter values on another PathNode. 3737 * 3738 * Given another node (of the same state), finds the parameter values which differ. 3739 * Returns the [[Param]] (schema objects) whose parameter values differ. 3740 * 3741 * Given another node for a different state, returns `false` 3742 * 3743 * @param node The node to compare to 3744 * @param paramsFn A function that returns which parameters should be compared. 3745 * @returns The [[Param]]s which differ, or null if the two nodes are for different states 3746 */ 3747 PathNode.prototype.diff = function (node, paramsFn) { 3748 if (this.state !== node.state) 3749 return false;
3750 var params = paramsFn ? paramsFn(this) : this.paramSchema; 3751 return Param.changed(params, this.paramValues, node.paramValues); 3752 }; 3753 /** 3754 * Returns a clone of the PathNode 3755 * @deprecated use instance method `node.clone()` 3756 */ 3757 PathNode.clone = function (node) { return node.clone(); }; 3758 return PathNode; 3759 }()); 3760 3761 /** 3762 * Encapsulate the target (destination) state/params/options of a [[Transition]]. 3763 * 3764 * This class is frequently used to redirect a transition to a new destination. 3765 * 3766 * See: 3767 * 3768 * - [[HookResult]] 3769 * - [[TransitionHookFn]] 3770 * - [[TransitionService.onStart]] 3771 * 3772 * To create a `TargetState`, use [[StateService.target]]. 3773 * 3774 * --- 3775 * 3776 * This class wraps: 3777 * 3778 * 1) an identifier for a state 3779 * 2) a set of parameters 3780 * 3) and transition options 3781 * 4) the registered state object (the [[StateDeclaration]]) 3782 * 3783 * Many UI-Router APIs such as [[StateService.go]] take a [[StateOrName]] argument which can 3784 * either be a *state object* (a [[StateDeclaration]] or [[StateObject]]) or a *state name* (a string). 3785 * The `TargetState` class normalizes those options. 3786 * 3787 * A `TargetState` may be valid (the state being targeted exists in the registry) 3788 * or invalid (the state being targeted is not registered). 3789 */ 3790 var TargetState = /** @class */ (function () { 3791 /** 3792 * The TargetState constructor 3793 * 3794 * Note: Do not construct a `TargetState` manually. 3795 * To create a `TargetState`, use the [[StateService.target]] factory method. 3796 * 3797 * @param _stateRegistry The StateRegistry to use to look up the _definition 3798 * @param _identifier An identifier for a state. 3799 * Either a fully-qualified state name, or the object used to define the state. 3800 * @param _params Parameters for the target state 3801 * @param _options Transition options. 3802 * 3803 * @internal 3804 */ 3805 function TargetState(_stateRegistry, _identifier, _params, _options) { 3806 this._stateRegistry = _stateRegistry; 3807 this._identifier = _identifier; 3808 this._identifier = _identifier; 3809 this._params = extend({}, _params || {}); 3810 this._options = extend({}, _options || {}); 3811 this._definition = _stateRegistry.matcher.find(_identifier, this._options.relative); 3812 } 3813 /** The name of the state this object targets */ 3814 TargetState.prototype.name = function () { 3815 return (this._definition && this._definition.name) || this._identifier; 3816 }; 3817 /** The identifier used when creating this TargetState */ 3818 TargetState.prototype.identifier = function () { 3819 return this._identifier; 3820 }; 3821 /** The target parameter values */ 3822 TargetState.prototype.params = function () { 3823 return this._params; 3824 }; 3825 /** The internal state object (if it was found) */ 3826 TargetState.prototype.$state = function () { 3827 return this._definition; 3828 }; 3829 /** The internal state declaration (if it was found) */ 3830 TargetState.prototype.state = function () { 3831 return this._definition && this._definition.self; 3832 }; 3833 /** The target options */ 3834 TargetState.prototype.options = function () { 3835 return this._options; 3836 }; 3837 /** True if the target state was found */ 3838 TargetState.prototype.exists = function () { 3839 return !!(this._definition && this._definition.self); 3840 }; 3841 /** True if the object is valid */ 3842 TargetState.prototype.valid = function () { 3843 return !this.error(); 3844 }; 3845 /** If the object is invalid, returns the reason why */ 3846 TargetState.prototype.error = function () { 3847 var base = this.options().relative; 3848 if (!this._definition && !!base) { 3849 var stateName = base.name ? base.name : base; 3850 return "Could not resolve '" + this.name() + "' from state '" + stateName + "'"; 3851 } 3852 if (!this._definition) 3853 return "No such state '" + this.name() + "'"; 3854 if (!this._definition.self) 3855 return "State '" + this.name() + "' has an invali
3855d definition"; 3856 }; 3857 TargetState.prototype.toString = function () { 3858 return "'" + this.name() + "'" + stringify(this.params()); 3859 }; 3860 /** 3861 * Returns a copy of this TargetState which targets a different state. 3862 * The new TargetState has the same parameter values and transition options. 3863 * 3864 * @param state The new state that should be targeted 3865 */ 3866 TargetState.prototype.withState = function (state) { 3867 return new TargetState(this._stateRegistry, state, this._params, this._options); 3868 }; 3869 /** 3870 * Returns a copy of this TargetState, using the specified parameter values. 3871 * 3872 * @param params the new parameter values to use 3873 * @param replace When false (default) the new parameter values will be merged with the current values. 3874 * When true the parameter values will be used instead of the current values. 3875 */ 3876 TargetState.prototype.withParams = function (params, replace) { 3877 if (replace === void 0) { replace = false; } 3878 var newParams = replace ? params : extend({}, this._params, params); 3879 return new TargetState(this._stateRegistry, this._identifier, newParams, this._options); 3880 }; 3881 /** 3882 * Returns a copy of this TargetState, using the specified Transition Options. 3883 * 3884 * @param options the new options to use 3885 * @param replace When false (default) the new options will be merged with the current options. 3886 * When true the options will be used instead of the current options. 3887 */ 3888 TargetState.prototype.withOptions = function (options, replace) { 3889 if (replace === void 0) { replace = false; } 3890 var newOpts = replace ? options : extend({}, this._options, options); 3891 return new TargetState(this._stateRegistry, this._identifier, this._params, newOpts); 3892 }; 3893 /** Returns true if the object has a state property that might be a state or state name */ 3894 TargetState.isDef = function (obj) { 3895 return obj && obj.state && (isString(obj.state) || (isObject(obj.state) && isString(obj.state.name))); 3896 }; 3897 return TargetState; 3898 }()); 3899 3900 /** 3901 * This class contains functions which convert TargetStates, Nodes and paths from one type to another. 3902 */ 3903 var PathUtils = /** @class */ (function () { 3904 function PathUtils() { 3905 } 3906 /** Given a PathNode[], create an TargetState */ 3907 PathUtils.makeTargetState = function (registry, path) { 3908 var state = tail(path).state; 3909 return new TargetState(registry, state, path.map(prop('paramValues')).reduce(mergeR, {}), {}); 3910 }; 3911 PathUtils.buildPath = function (targetState) { 3912 var toParams = targetState.params(); 3913 return targetState.$state().path.map(function (state) { return new PathNode(state).applyRawParams(toParams); }); 3914 }; 3915 /** Given a fromPath: PathNode[] and a TargetState, builds a toPath: PathNode[] */ 3916 PathUtils.buildToPath = function (fromPath, targetState) { 3917 var toPath = PathUtils.buildPath(targetState); 3918 if (targetState.options().inherit) { 3919 return PathUtils.inheritParams(fromPath, toPath, Object.keys(targetState.params())); 3920 } 3921 return toPath; 3922 }; 3923 /** 3924 * Creates ViewConfig objects and adds to nodes. 3925 * 3926 * On each [[PathNode]], creates ViewConfig objects from the views: property of the node's state 3927 */ 3928 PathUtils.applyViewConfigs = function ($view, path, states) { 3929 // Only apply the viewConfigs to the nodes for the given states 3930 path 3931 .filter(function (node) { return inArray(states, node.state); }) 3932 .forEach(function (node) { 3933 var viewDecls = values(node.state.views || {}); 3934 var subPath = PathUtils.subPath(path, function (n) { return n === node; }); 3935 var viewConfigs = viewDecls.map(function (view) { return $view.createViewConfig(subPath, view); }); 3936 node.views = viewConfigs.reduce(unnestR, []); 3937 }); 3938 }; 3939 /** 3940 * Given a fromPath and a toPath, returns a new to path which inherits parameters from the fromPath 3941 * 3942 * For a parameter in a node to be inherited from the from path: 3943 * - The toPath's node must have a matching node in the fromPath (by state). 3944 * - The parameter name must not be found in the toKeys parameter array. 3945 * 3946 * Note: the keys provided in toKeys are intended to be those param keys explicitly specified by some 3947 * caller, for instance, $state.transitionTo(..., toParams). If a key was found in toParams, 3948 * it is not inherited from the fromPath. 3949 */ 3950 PathUtils.inheritParams = function (fromPath, toPath, toKeys) { 3951 if (toKeys === void 0) { toKeys = []; } 3952 function nodeParamVals(path, state) { 3953 var node = find(path, propEq('state', state)); 3954 return extend({}, node && node.paramValues); 3955 } 3956 var noInherit = fromPath 3957 .map(function (node) { return node.paramSchema; }) 3958 .reduce(unnestR, []) 3959 .filter(function (param) { return !param.inherit; }) 3960 .map(prop('id')); 3961 /** 3962 * Given an [[PathNode]] "toNode", return a new [[PathNode]] with param values inherited from the 3963 * matching node in fromPath. Only inherit keys that aren't found in "toKeys" from the node in "fromPath"" 3964 */ 3965 function makeInheritedParamsNode(toNode) { 3966 // All param values for the node (may include default key/vals, when key was not found in toParams) 3967 var toParamVals = extend({}, toNode && toNode.paramValues); 3968 // limited to only those keys found in toParams 3969 var incomingParamVals = pick(toParamVals, toKeys); 3970 toParamVals = omit(toParamVals, toKeys); 3971 var fromParamVals = omit(nodeParamVals(fromPath, toNode.state) || {}, noInherit); 3972 // extend toParamVals with any fromParamVals, then override any of those those with incomingParamVals 3973 var ownParamVals = extend(toParamVals, fromParamVals, incomingParamVals); 3974 return new PathNode(toNode.state).applyRawParams(ownParamVals); 3975 } 3976 // The param keys specified by the incoming toParams 3977 return toPath.map(makeInheritedParamsNode); 3978 }; 3979 /** 3980 * Computes the tree changes (entering, exiting) between a fromPath and toPath. 3981 */ 3982 PathUtils.treeChanges = function (fromPath, toPath, reloadState) { 3983 var max = Math.min(fromPath.length, toPath.length); 3984 var keep = 0; 3985 var nodesMatch = function (node1, node2) { return node1.equals(node2, PathUtils.nonDynamicParams); }; 3986 while (keep < max && fromPath[keep].state !== reloadState && nodesMatch(fromPath[keep], toPath[keep])) { 3987 keep++; 3988 } 3989 /** Given a retained node, return a new node which uses the to node's param values */ 3990 function applyToParams(retainedNode, idx) { 3991 var cloned = retainedNode.clone(); 3992 cloned.paramValues = toPath[idx].paramValues; 3993 return cloned; 3994 } 3995 var from, retained, exiting, entering, to; 3996 from = fromPath; 3997 retained = from.slice(0, keep); 3998 exiting = from.slice(keep); 3999 // Create a new retained path (with shallow copies of nodes) which have the params of the toPath mapped 4000 var retainedWithToParams = retained.map(applyToParams); 4001 entering = toPath.slice(keep); 4002 to = retainedWithToParams.concat(entering); 4003 return { from: from, to: to, retained: retained, retainedWithToParams: retainedWithToParams, exiting: exiting, entering: entering }; 4004 }; 4005 /** 4006 * Returns a new path which is: the subpath of the first path which matches the second path. 4007 *
4008 * The new path starts from root and contains any nodes that match the nodes in the second path. 4009 * It stops before the first non-matching node. 4010 * 4011 * Nodes are compared using their state property and their parameter values. 4012 * If a `paramsFn` is provided, only the [[Param]] returned by the function will be considered when comparing nodes. 4013 * 4014 * @param pathA the first path 4015 * @param pathB the second path 4016 * @param paramsFn a function which returns the parameters to consider when comparing 4017 * 4018 * @returns an array of PathNodes from the first path which match the nodes in the second path 4019 */ 4020 PathUtils.matching = function (pathA, pathB, paramsFn) { 4021 var done = false; 4022 var tuples = arrayTuples(pathA, pathB); 4023 return tuples.reduce(function (matching, _a) { 4024 var nodeA = _a[0], nodeB = _a[1]; 4025 done = done || !nodeA.equals(nodeB, paramsFn); 4026 return done ? matching : matching.concat(nodeA); 4027 }, []); 4028 }; 4029 /** 4030 * Returns true if two paths are identical. 4031 * 4032 * @param pathA 4033 * @param pathB 4034 * @param paramsFn a function which returns the parameters to consider when comparing 4035 * @returns true if the the states and parameter values for both paths are identical 4036 */ 4037 PathUtils.equals = function (pathA, pathB, paramsFn) { 4038 return pathA.length === pathB.length && PathUtils.matching(pathA, pathB, paramsFn).length === pathA.length; 4039 }; 4040 /** 4041 * Return a subpath of a path, which stops at the first matching node 4042 * 4043 * Given an array of nodes, returns a subset of the array starting from the first node, 4044 * stopping when the first node matches the predicate. 4045 * 4046 * @param path a path of [[PathNode]]s 4047 * @param predicate a [[Predicate]] fn that matches [[PathNode]]s 4048 * @returns a subpath up to the matching node, or undefined if no match is found 4049 */ 4050 PathUtils.subPath = function (path, predicate) { 4051 var node = find(path, predicate); 4052 var elementIdx = path.indexOf(node); 4053 return elementIdx === -1 ? undefined : path.slice(0, elementIdx + 1); 4054 }; 4055 PathUtils.nonDynamicParams = function (node) { 4056 return node.state.parameters({ inherit: false }).filter(function (param) { return !param.dynamic; }); 4057 }; 4058 /** Gets the raw parameter values from a path */ 4059 PathUtils.paramValues = function (path) { return path.reduce(function (acc, node) { return extend(acc, node.paramValues); }, {}); }; 4060 return PathUtils; 4061 }()); 4062 4063 var resolvePolicies = { 4064 when: { 4065 LAZY: 'LAZY', 4066 EAGER: 'EAGER', 4067 }, 4068 async: { 4069 WAIT: 'WAIT', 4070 NOWAIT: 'NOWAIT', 4071 }, 4072 }; 4073 4074 // TODO: explicitly make this user configurable 4075 var defaultResolvePolicy = { 4076 when: 'LAZY', 4077 async: 'WAIT', 4078 }; 4079 /** 4080 * The basic building block for the resolve system. 4081 * 4082 * Resolvables encapsulate a state's resolve's resolveFn, the resolveFn's declared dependencies, the wrapped (.promise), 4083 * and the unwrapped-when-complete (.data) result of the resolveFn. 4084 * 4085 * Resolvable.get() either retrieves the Resolvable's existing promise, or else invokes resolve() (which invokes the 4086 * resolveFn) and returns the resulting promise. 4087 * 4088 * Resolvable.get() and Resolvable.resolve() both execute within a context path, which is passed as the first 4089 * parameter to those fns. 4090 */ 4091 var Resolvable = /** @class */ (function () { 4092 function Resolvable(arg1, resolveFn, deps, policy, data) { 4093 this.resolved = false; 4094 this.promise = undefined; 4095 if (arg1 instanceof Resolvable) { 4096 extend(this, arg1); 4097 } 4098 else if (isFunction(resolveFn)) { 4099 if (isNullOrUndefined(arg1)) 4100 throw new Error('new Resolvable(): token argument is required'); 4101 if (!isFunction(resolveFn)) 4102 throw new Error('new Resolvable(): resolveFn argument must be a function'); 4103 this.token = arg1; 4104 this.policy = policy; 4105 this.resolveFn = resolveFn; 4106 this.deps = deps || []; 4107 this.data = data; 4108 this.resolved = data !== undefined; 4109 this.promise = this.resolved ? services.$q.when(this.data) : undefined; 4110 } 4111 else if (isObject(arg1) && arg1.token && (arg1.hasOwnProperty('resolveFn') || arg1.hasOwnProperty('data'))) { 4112 var literal = arg1; 4113 return new Resolvable(literal.token, literal.resolveFn, literal.deps, literal.policy, literal.data); 4114 } 4115 } 4116 Resolvable.prototype.getPolicy = function (state) { 4117 var thisPolicy = this.policy || {}; 4118 var statePolicy = (state && state.resolvePolicy) || {}; 4119 return { 4120 when: thisPolicy.when || statePolicy.when || defaultResolvePolicy.when, 4121 async: thisPolicy.async || statePolicy.async || defaultResolvePolicy.async, 4122 }; 4123 }; 4124 /** 4125 * Asynchronously resolve this Resolvable's data 4126 * 4127 * Given a ResolveContext that this Resolvable is found in:
4128 * Wait for this Resolvable's dependencies, then invoke this Resolvable's function 4129 * and update the Resolvable's state 4130 */ 4131 Resolvable.prototype.resolve = function (resolveContext, trans) { 4132 var _this = this; 4133 var $q = services.$q; 4134 // Gets all dependencies from ResolveContext and wait for them to be resolved 4135 var getResolvableDependencies = function () { 4136 return $q.all(resolveContext.getDependencies(_this).map(function (resolvable) { return resolvable.get(resolveContext, trans); })); 4137 }; 4138 // Invokes the resolve function passing the resolved dependencies as arguments 4139 var invokeResolveFn = function (resolvedDeps) { return _this.resolveFn.apply(null, resolvedDeps); }; 4140 var node = resolveContext.findNode(this); 4141 var state = node && node.state; 4142 var asyncPolicy = this.getPolicy(state).async; 4143 var customAsyncPolicy = isFunction(asyncPolicy) ? asyncPolicy : identity; 4144 // After the final value has been resolved, update the state of the Resolvable 4145 var applyResolvedValue = function (resolvedValue) { 4146 _this.data = resolvedValue; 4147 _this.resolved = true; 4148 _this.resolveFn = null; 4149 trace.traceResolvableResolved(_this, trans); 4150 return _this.data; 4151 }; 4152 // Sets the promise property first, then getsResolvableDependencies in the context of the promise chain. Always waits one tick. 4153 return (this.promise = $q 4154 .when() 4155 .then(getResolvableDependencies) 4156 .then(invokeResolveFn) 4157 .then(customAsyncPolicy) 4158 .then(applyResolvedValue)); 4159 }; 4160 /** 4161 * Gets a promise for this Resolvable's data. 4162 * 4163 * Fetches the data and returns a promise. 4164 * Returns the existing promise if it has already been fetched once. 4165 */ 4166 Resolvable.prototype.get = function (resolveContext, trans) { 4167 return this.promise || this.resolve(resolveContext, trans); 4168 }; 4169 Resolvable.prototype.toString = function () { 4170 return "Resolvable(token: " + stringify(this.token) + ", requires: [" + this.deps.map(stringify) + "])"; 4171 }; 4172 Resolvable.prototype.clone = function () { 4173 return new Resolvable(this); 4174 }; 4175 Resolvable.fromData = function (token, data) { return new Resolvable(token, function () { return data; }, null, null, data); }; 4176 return Resolvable; 4177 }()); 4178 4179 var whens = resolvePolicies.when; 4180 var ALL_WHENS = [whens.EAGER, whens.LAZY]; 4181 var EAGER_WHENS = [whens.EAGER]; 4182 // tslint:disable-next-line:no-inferrable-types 4183 var NATIVE_INJECTOR_TOKEN = 'Native Injector'; 4184 /** 4185 * Encapsulates Dependency Injection for a path of nodes 4186 * 4187 * UI-Router states are organized as a tree. 4188 * A nested state has a path of ancestors to the root of the tree. 4189 * When a state is being activated, each element in the path is wrapped as a [[PathNode]]. 4190 * A `PathNode` is a stateful object that holds things like parameters and resolvables for the state being activated. 4191 * 4192 * The ResolveContext closes over the [[PathNode]]s, and provides DI for the last node in the path. 4193 */ 4194 var ResolveContext = /** @class */ (function () { 4195 function ResolveContext(_path) { 4196 this._path = _path; 4197 } 4198 /** Gets all the tokens found in the resolve context, de-duplicated */ 4199 ResolveContext.prototype.getTokens = function () { 4200 return this._path.reduce(function (acc, node) { return acc.concat(node.resolvables.map(function (r) { return r.token; })); }, []).reduce(uniqR, []); 4201 }; 4202 /** 4203 * Gets the Resolvable that matches the token 4204 * 4205 * Gets the last Resolvable that matches the token in this context, or undefined. 4206 * Throws an error if it doesn't exist in the ResolveContext 4207 */ 4208 ResolveContext.prototype.getResolvable = function (token) { 4209 var matching = this._path 4210 .map(function (node) { return node.resolvables; }) 4211 .reduce(unnestR, []) 4212 .filter(function (r) { return r.token === token; }); 4213 return tail(matching); 4214 }; 4215 /** Returns the [[ResolvePolicy]] for the given [[Resolvable]] */ 4216 ResolveContext.prototype.getPolicy = function (resolvable) { 4217 var node = this.findNode(resolvable); 4218 return resolvable.getPolicy(node.state); 4219 }; 4220 /** 4221 * Returns a ResolveContext that includes a portion of this one 4222 * 4223 * Given a state, this method creates a new ResolveContext from this one. 4224 * The new context starts at the first node (root) and stops at the node for the `state` parameter. 4225 * 4226 * #### Why 4227 * 4228 * When a transition is created, the nodes in the "To Path" are injected from a ResolveContext. 4229 * A ResolveContext closes over a path of [[PathNode]]s and processes the resolvables. 4230 * The "To State" can inject values from its own resolvables, as well as those from all its ancestor state's (node'
4230s). 4231 * This method is used to create a narrower context when injecting ancestor nodes. 4232 * 4233 * @example 4234 * `let ABCD = new ResolveContext([A, B, C, D]);` 4235 * 4236 * Given a path `[A, B, C, D]`, where `A`, `B`, `C` and `D` are nodes for states `a`, `b`, `c`, `d`: 4237 * When injecting `D`, `D` should have access to all resolvables from `A`, `B`, `C`, `D`. 4238 * However, `B` should only be able to access resolvables from `A`, `B`. 4239 * 4240 * When resolving for the `B` node, first take the full "To Path" Context `[A,B,C,D]` and limit to the subpath `[A,B]`. 4241 * `let AB = ABCD.subcontext(a)` 4242 */ 4243 ResolveContext.prototype.subContext = function (state) { 4244 return new ResolveContext(PathUtils.subPath(this._path, function (node) { return node.state === state; })); 4245 }; 4246 /** 4247 * Adds Resolvables to the node that matches the state 4248 * 4249 * This adds a [[Resolvable]] (generally one created on the fly; not declared on a [[StateDeclaration.resolve]] block). 4250 * The resolvable is added to the node matching the `state` parameter. 4251 * 4252 * These new resolvables are not automatically fetched. 4253 * The calling code should either fetch them, fetch something that depends on them, 4254 * or rely on [[resolvePath]] being called when some state is being entered. 4255 * 4256 * Note: each resolvable's [[ResolvePolicy]] is merged with the state's policy, and the global default. 4257 * 4258 * @param newResolvables the new Resolvables 4259 * @param state Used to find the node to put the resolvable on 4260 */ 4261 ResolveContext.prototype.addResolvables = function (newResolvables, state) { 4262 var node = find(this._path, propEq('state', state)); 4263 var keys = newResolvables.map(function (r) { return r.token; }); 4264 node.resolvables = node.resolvables.filter(function (r) { return keys.indexOf(r.token) === -1; }).concat(newResolvables); 4265 }; 4266 /** 4267 * Returns a promise for an array of resolved path Element promises 4268 * 4269 * @param when 4270 * @param trans 4271 * @returns {Promise<any>|any} 4272 */
4273 ResolveContext.prototype.resolvePath = function (when, trans) { 4274 var _this = this; 4275 if (when === void 0) { when = 'LAZY'; } 4276 // This option determines which 'when' policy Resolvables we are about to fetch. 4277 var whenOption = inArray(ALL_WHENS, when) ? when : 'LAZY'; 4278 // If the caller specified EAGER, only the EAGER Resolvables are fetched. 4279 // if the caller specified LAZY, both EAGER and LAZY Resolvables are fetched.` 4280 var matchedWhens = whenOption === resolvePolicies.when.EAGER ? EAGER_WHENS : ALL_WHENS; 4281 // get the subpath to the state argument, if provided 4282 trace.traceResolvePath(this._path, when, trans); 4283 var matchesPolicy = function (acceptedVals, whenOrAsync) { return function (resolvable) { 4284 return inArray(acceptedVals, _this.getPolicy(resolvable)[whenOrAsync]); 4285 }; }; 4286 // Trigger all the (matching) Resolvables in the path 4287 // Reduce all the "WAIT" Resolvables into an array 4288 var promises = this._path.reduce(function (acc, node) { 4289 var nodeResolvables = node.resolvables.filter(matchesPolicy(matchedWhens, 'when')); 4290 var nowait = nodeResolvables.filter(matchesPolicy(['NOWAIT'], 'async')); 4291 var wait = nodeResolvables.filter(not(matchesPolicy(['NOWAIT'], 'async'))); 4292 // For the matching Resolvables, start their async fetch process. 4293 var subContext = _this.subContext(node.state); 4294 var getResult = function (r) { 4295 return r 4296 .get(subContext, trans) 4297 // Return a tuple that includes the Resolvable's token 4298 .then(function (value) { return ({ token: r.token, value: value }); }); 4299 }; 4300 nowait.forEach(getResult); 4301 return acc.concat(wait.map(getResult)); 4302 }, []); 4303 // Wait for all the "WAIT" resolvables 4304 return services.$q.all(promises); 4305 }; 4306 ResolveContext.prototype.injector = function () { 4307 return this._injector || (this._injector = new UIInjectorImpl(this)); 4308 }; 4309 ResolveContext.prototype.findNode = function (resolvable) { 4310 return find(this._path, function (node) { return inArray(node.resolvables, resolvable); }); 4311 }; 4312 /** 4313 * Gets the async dependencies of a Resolvable 4314 * 4315 * Given a Resolvable, returns its dependencies as a Resolvable[] 4316 */ 4317 ResolveContext.prototype.getDependencies = function (resolvable) { 4318 var _this = this; 4319 var node = this.findNode(resolvable); 4320 // Find which other resolvables are "visible" to the `resolvable` argument 4321 // subpath stopping at resolvable's node, or the whole path (if the resolvable isn't in the path) 4322 var subPath = PathUtils.subPath(this._path, function (x) { return x === node; }) || this._path; 4323 var availableResolvables = subPath 4324 .reduce(function (acc, _node) { return acc.concat(_node.resolvables); }, []) // all of subpath's resolvables 4325 .filter(function (res) { return res !== resolvable; }); // filter out the `resolvable` argument 4326 var getDependency = function (token) { 4327 var matching = availableResolvables.filter(function (r) { return r.token === token; }); 4328 if (matching.length) 4329 return tail(matching); 4330 var fromInjector = _this.injector().getNative(token); 4331 if (isUndefined(fromInjector)) { 4332 throw new Error('Could not find Dependency Injection token: ' + stringify(token)); 4333 } 4334 return new Resolvable(token, function () { return fromInjector; }, [], fromInjector); 4335 }; 4336 return resolvable.deps.map(getDependency); 4337 }; 4338 return ResolveContext; 4339 }()); 4340 /** @internal */ 4341 var UIInjectorImpl = /** @class */ (function () { 4342 function UIInjectorImpl(context) { 4343 this.context = context; 4344 this.native = this.get(NATIVE_INJECTOR_TOKEN) || services.$injector; 4345 } 4346 UIInjectorImpl.prototype.get = function (token) { 4347 var resolvable = this.context.getResolvable(token); 4348 if (resolvable) { 4349 if (this.context.getPolicy(resolvable).async === 'NOWAIT') { 4350 return resolvable.get(this.context); 4351 } 4352 if (!resolvable.resolved) { 4353 throw new Error('Resolvable async .get() not complete:' + stringify(resolvable.token)); 4354 } 4355 return resolvable.data; 4356 } 4357 return this.getNative(token); 4358 }; 4359 UIInjectorImpl.prototype.getAsync = function (token) { 4360 var resolvable = this.context.getResolvable(token); 4361 if (resolvable) 4362 return resolvable.get(this.context); 4363 return services.$q.when(this.native.get(token)); 4364 }; 4365 UIInjectorImpl.prototype.getNative = function (token) { 4366 return this.native && this.native.get(token); 4367 }; 4368 return UIInjectorImpl; 4369 }()); 4370 4371 var parseUrl = function (url) { 4372 if (!isString(url)) 4373 return false;
4374 var root = url.charAt(0) === '^'; 4375 return { val: root ? url.substring(1) : url, root: root }; 4376 }; 4377 function nameBuilder(state) { 4378 return state.name; 4379 } 4380 function selfBuilder(state) { 4381 state.self.$$state = function () { return state; }; 4382 return state.self; 4383 } 4384 function dataBuilder(state) { 4385 if (state.parent && state.parent.data) { 4386 state.data = state.self.data = inherit(state.parent.data, state.data); 4387 } 4388 return state.data; 4389 } 4390 var getUrlBuilder = function ($urlMatcherFactoryProvider, root) { 4391 return function urlBuilder(stateObject) { 4392 var stateDec = stateObject.self; 4393 // For future states, i.e., states whose name ends with `.**`, 4394 // match anything that starts with the url prefix 4395 if (stateDec && stateDec.url && stateDec.name && stateDec.name.match(/\.\*\*$/)) { 4396 var newStateDec = {}; 4397 copy(stateDec, newStateDec); 4398 newStateDec.url += '{remainder:any}'; // match any path (.*) 4399 stateDec = newStateDec; 4400 } 4401 var parent = stateObject.parent; 4402 var parsed = parseUrl(stateDec.url); 4403 var url = !parsed ? stateDec.url : $urlMatcherFactoryProvider.compile(parsed.val, { state: stateDec }); 4404 if (!url) 4405 return null; 4406 if (!$urlMatcherFactoryProvider.isMatcher(url)) 4407 throw new Error("Invalid url '" + url + "' in state '" + stateObject + "'"); 4408 return parsed && parsed.root ? url : ((parent && parent.navigable) || root()).url.append(url); 4409 }; 4410 }; 4411 var getNavigableBuilder = function (isRoot) { 4412 return function navigableBuilder(state) { 4413 return !isRoot(state) && state.url ? state : state.parent ? state.parent.navigable : null; 4414 }; 4415 }; 4416 var getParamsBuilder = function (paramFactory) { 4417 return function paramsBuilder(state) { 4418 var makeConfigParam = function (config, id) { return paramFactory.fromConfig(id, null, state.self); }; 4419 var urlParams = (state.url && state.url.parameters({ inherit: false })) || []; 4420 var nonUrlParams = values(mapObj(omit(state.params || {}, urlParams.map(prop('id'))), makeConfigParam)); 4421 return urlParams 4422 .concat(nonUrlParams) 4423 .map(function (p) { return [p.id, p]; }) 4424 .reduce(applyPairs, {}); 4425 }; 4426 }; 4427 function pathBuilder(state) { 4428 return state.parent ? state.parent.path.concat(state) : /*root*/ [state]; 4429 } 4430 function includesBuilder(state) { 4431 var includes = state.parent ? extend({}, state.parent.includes) : {}; 4432 includes[state.name] = true; 4433 return includes; 4434 } 4435 /** 4436 * This is a [[StateBuilder.builder]] function for the `resolve:` block on a [[StateDeclaration]]. 4437 * 4438 * When the [[StateBuilder]] builds a [[StateObject]] object from a raw [[StateDeclaration]], this builder 4439 * validates the `resolve` property and converts it to a [[Resolvable]] array. 4440 * 4441 * resolve: input value can be: 4442 * 4443 * { 4444 * // analyzed but not injected 4445 * myFooResolve: function() { return "myFooData"; }, 4446 * 4447 * // function.toString() parsed, "DependencyName" dep as string (not min-safe) 4448 * myBarResolve: function(DependencyName) { return DependencyName.fetchSomethingAsPromise() }, 4449 * 4450 * // Array split; "DependencyName" dep as string 4451 * myBazResolve: [ "DependencyName", function(dep) { return dep.fetchSomethingAsPromise() }, 4452 * 4453 * // Array split; DependencyType dep as token (compared using ===) 4454 * myQuxResolve: [ DependencyType, function(dep) { return dep.fetchSometingAsPromise() }, 4455 * 4456 * // val.$inject used as deps 4457 * // where: 4458 * // corgeResolve.$inject = ["DependencyName"]; 4459 * // function corgeResolve(dep) { dep.fetchSometingAsPromise() } 4460 * // then "DependencyName" dep as string 4461 * myCorgeResolve: corgeResolve, 4462 * 4463 * // inject service by name 4464 * // When a string is found, desugar creating a resolve that injects the named service 4465 * myGraultResolve: "SomeService" 4466 * } 4467 * 4468 * or: 4469 * 4470 * [ 4471 * new Resolvable("myFooResolve", function() { return "myFooData" }), 4472 * new Resolvable("myBarResolve", function(dep) { return dep.fetchSomethingAsPromise() }, [ "DependencyName" ]), 4473 * { provide: "myBazResolve", useFactory: function(dep) { dep.fetchSomethingAsPromise() }, deps: [ "DependencyName" ] } 4474 * ] 4475 */ 4476 function resolvablesBuilder(state) { 4477 /** convert resolve: {} and resolvePolicy: {} objects to an array of tuples */ 4478 var objects2Tuples = function (resolveObj, resolvePolicies) { 4479 return Object.keys(resolveObj || {}).map(function (token) { return ({ 4480 token: token, 4481 val: resolveObj[token], 4482 deps: undefined, 4483 policy: resolvePolicies[token], 4484 }); }); 4485 }; 4486 /** fetch DI annotations from a function or ng1-style array */ 4487 var annotate = function (fn) { 4488 var $injector = services.$injector; 4489 // ng1 doesn't have an $injector until runtime. 4490 // If the $injector doesn't exist, use "deferred" literal as a 4491 // marker indicating they should be annotated when runtime starts 4492 return fn['$inject'] || ($injector && $injector.annotate(fn, $injector.strictDi)) || 'deferred'; 4493 }; 4494 /** true if the object has both `token` and `resolveFn`, and is probably a [[ResolveLiteral]] */ 4495 var isResolveLiteral = function (obj) { return !!(obj.token && obj.resolveFn); }; 4496 /** true if the object looks like a provide literal, or a ng2 Provider */ 4497 var isLikeNg2Provider = function (obj) { 4498 return !!((obj.provide || obj.token) && (obj.useValue || obj.useFactory || obj.useExisting || obj.useClass)); 4499 }; 4500 /** true if the object looks like a tuple from obj2Tuples */ 4501 var isTupleFromObj = function (obj) { 4502 return !!(obj && obj.val && (isString(obj.val) || isArray(obj.val) || isFunction(obj.val))); 4503 }; 4504 /** extracts the token from a Provider or provide literal */ 4505 var getToken = function (p) { return p.provide || p.token; }; 4506 // prettier-ignore: Given a literal resolve or provider object, returns a Resolvable 4507 var literal2Resolvable = pattern([ 4508 [prop('resolveFn'), function (p) { return new Resolvable(getToken(p), p.resolveFn, p.deps, p.policy); }], 4509 [prop('useFactory'), function (p) { return new Resolvable(getToken(p), p.useFactory, p.deps || p.dependencies, p.policy); }], 4510 [prop('useClass'), function (p) { return new Resolvable(getToken(p), function () { return new p.useClass(); }, [], p.policy); }], 4511 [prop('useValue'), function (p) { return new Resolvable(getToken(p), function () { return p.useValue; }, [], p.policy, p.useValue); }], 4512 [prop('useExisting'), function (p) { return new Resolvable(getToken(p), identity, [p.useExisting], p.policy); }], 4513 ]); 4514 // prettier-ignore 4515 var tuple2Resolvable = pattern([ 4516 [pipe(prop('val'), isString), function (tuple) { return new Resolvable(tuple.token, identity, [tuple.val], tuple.policy); }], 4517 [pipe(prop('val'), isArray), function (tuple) { return new Resolvable(tuple.token, tail(tuple.val), tuple.val.slice(0, -1), tuple.policy); }], 4518 [pipe(prop('val'), isFunction), function (tuple) { return new Resolvable(tuple.token, tuple.val, annotate(tuple.val), tuple.policy); }], 4519 ]); 4520 // prettier-ignore 4521 var item2Resolvable = pattern([ 4522 [is(Resolvable), function (r) { return r; }], 4523 [isResolveLiteral, literal2Resolvable], 4524 [isLikeNg2Provider, literal2Resolvable], 4525 [isTupleFromObj, tuple2Resolvable], 4526 [val(true), function (obj) { throw new Error('Invalid resolve value: ' + stringify(obj)); },], 4527 ]); 4528 // If resolveBlock is already an array, use it as-is. 4529 // Otherwise, assume it's an object and convert to an Array of tuples 4530 var decl = state.resolve; 4531 var items = isArray(decl) ? decl : objects2Tuples(decl, state.resolvePolicy || {}); 4532 return items.map(item2Resolvable); 4533 } 4534 /** 4535 * A internal global service 4536 * 4537 * StateBuilder is a factory for the internal [[StateObject]] objects. 4538 * 4539 * When you register a state with the [[StateRegistry]], you register a plain old javascript object which 4540 * conforms to the [[StateDeclaration]] interface. This factory takes that object and builds the corresponding 4541 * [[StateObject]] object, which has an API and is used internally. 4542 * 4543 * Custom properties or API may be added to the internal [[StateObject]] object by registering a decorator function 4544 * using the [[builder]] method. 4545 */ 4546 var StateBuilder = /** @class */ (function () { 4547 function StateBuilder(matcher, urlMatcherFactory) { 4548 this.matcher = matcher; 4549 var self = this; 4550 var root = function () { return matcher.find(''); };
4551 var isRoot = function (state) { return state.name === ''; }; 4552 function parentBuilder(state) { 4553 if (isRoot(state)) 4554 return null; 4555 return matcher.find(self.parentName(state)) || root(); 4556 } 4557 this.builders = { 4558 name: [nameBuilder], 4559 self: [selfBuilder], 4560 parent: [parentBuilder], 4561 data: [dataBuilder], 4562 // Build a URLMatcher if necessary, either via a relative or absolute URL 4563 url: [getUrlBuilder(urlMatcherFactory, root)], 4564 // Keep track of the closest ancestor state that has a URL (i.e. is navigable) 4565 navigable: [getNavigableBuilder(isRoot)], 4566 params: [getParamsBuilder(urlMatcherFactory.paramFactory)], 4567 // Each framework-specific ui-router implementation should define its own `views` builder 4568 // e.g., src/ng1/statebuilders/views.ts 4569 views: [], 4570 // Keep a full path from the root down to this state as this is needed for state activation. 4571 path: [pathBuilder], 4572 // Speed up $state.includes() as it's used a lot 4573 includes: [includesBuilder], 4574 resolvables: [resolvablesBuilder], 4575 }; 4576 } 4577 StateBuilder.prototype.builder = function (name, fn) { 4578 var builders = this.builders; 4579 var array = builders[name] || []; 4580 // Backwards compat: if only one builder exists, return it, else return whole arary. 4581 if (isString(name) && !isDefined(fn)) 4582 return array.length > 1 ? array : array[0]; 4583 if (!isString(name) || !isFunction(fn)) 4584 return; 4585 builders[name] = array; 4586 builders[name].push(fn); 4587 return function () { return builders[name].splice(builders[name].indexOf(fn, 1)) && null; }; 4588 }; 4589 /** 4590 * Builds all of the properties on an essentially blank State object, returning a State object which has all its 4591 * properties and API built. 4592 * 4593 * @param state an uninitialized State object 4594 * @returns the built State object 4595 */ 4596 StateBuilder.prototype.build = function (state) { 4597 var _a = this, matcher = _a.matcher, builders = _a.builders; 4598 var parent = this.parentName(state); 4599 if (parent && !matcher.find(parent, undefined, false)) { 4600 return null; 4601 } 4602 for (var key in builders) { 4603 if (!builders.hasOwnProperty(key)) 4604 continue; 4605 var chain = builders[key].reduce(function (parentFn, step) { return function (_state) { return step(_state, parentFn); }; }, noop); 4606 state[key] = chain(state); 4607 } 4608 return state; 4609 }; 4610 StateBuilder.prototype.parentName = function (state) { 4611 // name = 'foo.bar.baz.**' 4612 var name = state.name || ''; 4613 // segments = ['foo', 'bar', 'baz', '.**'] 4614 var segments = name.split('.'); 4615 // segments = ['foo', 'bar', 'baz'] 4616 var lastSegment = segments.pop(); 4617 // segments = ['foo', 'bar'] (ignore .** segment for future states) 4618 if (lastSegment === '**') 4619 segments.pop(); 4620 if (segments.length) { 4621 if (state.parent) { 4622 throw new Error("States that specify the 'parent:' property should not have a '.' in their name (" + name + ")"); 4623 } 4624 // 'foo.bar' 4625 return segments.join('.'); 4626 } 4627 if (!state.parent) 4628 return ''; 4629 return isString(state.parent) ? state.parent : state.parent.name; 4630 }; 4631 StateBuilder.prototype.name = function (state) { 4632 var name = state.name; 4633 if (name.indexOf('.') !== -1 || !state.parent) 4634 return name; 4635 var parentName = isString(state.parent) ? state.parent : state.parent.name; 4636 return parentName ? parentName + '.' + name : name; 4637 }; 4638 return StateBuilder; 4639 }()); 4640 4641 /** 4642 * Internal representation of a UI-Router state. 4643 * 4644 * Instances of this class are created when a [[StateDeclaration]] is registered with the [[StateRegistry]]. 4645 * 4646 * A registered [[StateDeclaration]] is augmented with a getter ([[StateDeclaration.$$state]]) which returns the corresponding [[StateObject]] object. 4647 * 4648 * This class prototypally inherits from the corresponding [[StateDeclaration]]. 4649 * Each of its own properties (i.e., `hasOwnProperty`) are built using builders from the [[StateBuilder]]. 4650 */ 4651 var StateObject = /** @class */ (function () { 4652 /** @deprecated use State.create() */ 4653 function StateObject(config) { 4654 return StateObject.create(config || {}); 4655 } 4656 /** 4657 * Create a state object to put the private/internal implementation details onto. 4658 * The object's prototype chain looks like: 4659 * (Internal State Object) -> (Copy of State.prototype) -> (State Declaration object) -> (State Declaration's prototype...) 4660 * 4661 * @param stateDecl the user-supplied State Declaration 4662 * @returns {StateObject} an internal State object 4663 */ 4664 StateObject.create = function (stateDecl) { 4665 stateDecl = StateObject.isStateClass(stateDecl) ? new stateDecl() : stateDecl; 4666 var state = inherit(inherit(stateDecl, StateObject.prototype)); 4667 stateDecl.$$state = function () { return state; }; 4668 state.self = stateDecl; 4669 state.__stateObjectCache = { 4670 nameGlob: Glob.fromString(state.name), 4671 }; 4672 return state; 4673 }; 4674 /** 4675 * Returns true if the provided parameter is the same state. 4676 * 4677 * Compares the identity of the state against the passed value, which is either an object 4678 * reference to the actual `State` instance, the original definition object passed to 4679 * `$stateProvider.state()`, or the fully-qualified name. 4680 * 4681 * @param ref Can be one of (a) a `State` instance, (b) an object that was passed 4682 * into `$stateProvider.state()`, (c) the fully-qualified name of a state as a string. 4683 * @returns Returns `true` if `ref` matches the current `State` instance. 4684 */ 4685 StateObject.prototype.is = function (ref) { 4686 return this === ref || this.self === ref || this.fqn() === ref; 4687 }; 4688 /** 4689 * @deprecated this does not properly handle dot notation 4690 * @returns Returns a dot-separated name of the state. 4691 */ 4692 StateObject.prototype.fqn = function () { 4693 if (!this.parent || !(this.parent instanceof this.constructor)) 4694 return this.name; 4695 var name = this.parent.fqn(); 4696 return name ? name + '.' + this.name : this.name; 4697 }; 4698 /** 4699 * Returns the root node of this state's tree. 4700 * 4701 * @returns The root of this state's tree. 4702 */ 4703 StateObject.prototype.root = function () { 4704 return (this.parent && this.parent.root()) || this; 4705 }; 4706 /** 4707 * Gets the state's `Param` objects 4708 * 4709 * Gets the list of [[Param]] objects owned by the state. 4710 * If `opts.inherit` is true, it also includes the ancestor states' [[Param]] objects. 4711 * If `opts.matchingKeys` exists, returns only `Param`s whose `id` is a key on the `matchingKeys` object 4712 * 4713 * @param opts options 4714 */ 4715 StateObject.prototype.parameters = function (opts) { 4716 opts = defaults(opts, { inherit: true, matchingKeys: null }); 4717 var inherited = (opts.inherit && this.parent && this.parent.parameters()) || []; 4718 return inherited 4719 .concat(values(this.params)) 4720 .filter(function (param) { return !opts.matchingKeys || opts.matchingKeys.hasOwnProperty(param.id); }); 4721 }; 4722 /** 4723 * Returns a single [[Param]] that is owned by the state 4724 * 4725 * If `opts.inherit` is true, it also searches the ancestor states` [[Param]]s. 4726 * @param id the name of the [[Param]] to return 4727 * @param opts options 4728 */ 4729 StateObject.prototype.parameter = function (id, opts) { 4730 if (opts === void 0) { opts = {}; } 4731 return ((this.url && this.url.parameter(id, opts)) || 4732 find(values(this.params), propEq('id', id)) || 4733 (opts.inherit && this.parent && this.parent.parameter(id))); 4734 }; 4735 StateObject.prototype.toString = function () { 4736 return this.fqn(); 4737 }; 4738 /** Predicate which returns true if the object is an class with @State() decorator */ 4739 StateObject.isStateClass = function (stateDecl) { 4740 return isFunction(stateDecl) && stateDecl['__uiRouterState'] === true; 4741 }; 4742 /** Predicate which returns true if the object is a [[StateDeclaration]] object */ 4743 StateObject.isStateDeclaration = function (obj) { return isFunction(obj['$$state']); }; 4744 /** Predicate which returns true if the object is an internal [[StateObject]] object */ 4745 StateObject.isState = function (obj) { return isObject(obj['__stateObjectCache']); }; 4746 return StateObject; 4747 }()); 4748 4749 var StateMatcher = /** @class */ (function () { 4750 function StateMatcher(_states) { 4751 this._states = _states; 4752 } 4753 StateMatcher.prototype.isRelative = function (stateName) { 4754 stateName = stateName || ''; 4755 return stateName.indexOf('.') === 0 || stateName.indexOf('^') === 0; 4756 }; 4757 StateMatcher.prototype.find = function (stateOrName, base, matchGlob) { 4758 if (matchGlob === void 0) { matchGlob = true; } 4759 if (!stateOrName && stateOrName !== '') 4760 return undefined; 4761 var isStr = isString(stateOrName); 4762 var name = isStr ? stateOrName : stateOrName.name; 4763 if (this.isRelative(name)) 4764 name = this.resolvePath(name, base); 4765 var state = this._states[name]; 4766 if (state && (isStr || (!isStr && (state === stateOrName || state.self === stateOrName)))) { 4767 return state; 4768 } 4769 else if (isStr && matchGlob) { 4770 var _states = values(this._states); 4771 var matches = _states.filter(function (_state) { return _state.__stateObjectCache.nameGlob && _state.__stateObjectCache.nameGlob.matches(name); }); 4772 if (matches.length > 1) { 4773 safeConsole.error("stateMatcher.find: Found multiple matches for " + name + " using glob: ", matches.map(function (match) { return match.name; })); 4774 } 4775 return matches[0]; 4776 } 4777 return undefined; 4778 };
4779 StateMatcher.prototype.resolvePath = function (name, base) { 4780 if (!base) 4781 throw new Error("No reference point given for path '" + name + "'"); 4782 var baseState = this.find(base); 4783 var splitName = name.split('.'); 4784 var pathLength = splitName.length; 4785 var i = 0, current = baseState; 4786 for (; i < pathLength; i++) { 4787 if (splitName[i] === '' && i === 0) { 4788 current = baseState; 4789 continue; 4790 } 4791 if (splitName[i] === '^') { 4792 if (!current.parent) 4793 throw new Error("Path '" + name + "' not valid for state '" + baseState.name + "'"); 4794 current = current.parent; 4795 continue; 4796 } 4797 break; 4798 } 4799 var relName = splitName.slice(i).join('.'); 4800 return current.name + (current.name && relName ? '.' : '') + relName; 4801 }; 4802 return StateMatcher; 4803 }()); 4804 4805 var StateQueueManager = /** @class */ (function () { 4806 function StateQueueManager(router, states, builder, listeners) { 4807 this.router = router; 4808 this.states = states; 4809 this.builder = builder; 4810 this.listeners = listeners; 4811 this.queue = []; 4812 } 4813 StateQueueManager.prototype.dispose = function () { 4814 this.queue = []; 4815 }; 4816 StateQueueManager.prototype.register = function (stateDecl) { 4817 var queue = this.queue; 4818 var state = StateObject.create(stateDecl); 4819 var name = state.name; 4820 if (!isString(name)) 4821 throw new Error('State must have a valid name'); 4822 if (this.states.hasOwnProperty(name) || inArray(queue.map(prop('name')), name)) 4823 throw new Error("State '" + name + "' is already defined"); 4824 queue.push(state); 4825 this.flush(); 4826 return state; 4827 }; 4828 StateQueueManager.prototype.flush = function () { 4829 var _this = this; 4830 var _a = this, queue = _a.queue, states = _a.states, builder = _a.builder; 4831 var registered = [], // states that got registered 4832 orphans = [], // states that don't yet have a parent registered 4833 previousQueueLength = {}; // keep track of how long the queue when an orphan was first encountered 4834 var getState = function (name) { return _this.states.hasOwnProperty(name) && _this.states[name]; }; 4835 var notifyListeners = function () { 4836 if (registered.length) { 4837 _this.listeners.forEach(function (listener) { 4838 return listener('registered', registered.map(function (s) { return s.self; })); 4839 }); 4840 } 4841 }; 4842 while (queue.length > 0) { 4843 var state = queue.shift(); 4844 var name_1 = state.name; 4845 var result = builder.build(state); 4846 var orphanIdx = orphans.indexOf(state); 4847 if (result) { 4848 var existingState = getState(name_1); 4849 if (existingState && existingState.name === name_1) { 4850 throw new Error("State '" + name_1 + "' is already defined"); 4851 } 4852 var existingFutureState = getState(name_1 + '.**'); 4853 if (existingFutureState) { 4854 // Remove future state of the same name 4855 this.router.stateRegistry.deregister(existingFutureState); 4856 } 4857 states[name_1] = state; 4858 this.attachRoute(state); 4859 if (orphanIdx >= 0) 4860 orphans.splice(orphanIdx, 1); 4861 registered.push(state); 4862 continue; 4863 } 4864 var prev = previousQueueLength[name_1]; 4865 previousQueueLength[name_1] = queue.length; 4866 if (orphanIdx >= 0 && prev === queue.length) { 4867 // Wait until two consecutive iterations where no additional states were dequeued successfully. 4868 // throw new Error(`Cannot register orphaned state '${name}'`); 4869 queue.push(state); 4870 notifyListeners(); 4871 return states; 4872 } 4873 else if (orphanIdx < 0) { 4874 orphans.push(state); 4875 } 4876 queue.push(state); 4877 } 4878 notifyListeners(); 4879 return states; 4880 }; 4881 StateQueueManager.prototype.attachRoute = function (state) { 4882 if (state.abstract || !state.url) 4883 return; 4884 var rulesApi = this.router.urlService.rules; 4885 rulesApi.rule(rulesApi.urlRuleFactory.create(state)); 4886 }; 4887 return StateQueueManager; 4888 }()); 4889 4890 /** 4891 * A registry for all of the application's [[StateDeclaration]]s 4892 * 4893 * This API is found at `router.stateRegistry` ([[UIRouter.stateRegistry]]) 4894 */ 4895 var StateRegistry = /** @class */ (function () { 4896 /** @internal */ 4897 function StateRegistry(router) { 4898 this.router = router; 4899 this.states = {}; 4900 /** @internal */ 4901 this.listeners = []; 4902 this.matcher = new StateMatcher(this.states); 4903 this.builder = new StateBuilder(this.matcher, router.urlMatcherFactory); 4904 this.stateQueue = new StateQueueManager(router, this.states, this.builder, this.listeners); 4905 this._registerRoot(); 4906 } 4907 /** @internal */ 4908 StateRegistry.prototype._registerRoot = function () { 4909 var rootStateDef = { 4910 name: '', 4911 url: '^', 4912 views: null, 4913 params: { 4914 '#': { value: null, type: 'hash', dynamic: true }, 4915 }, 4916 abstract: true, 4917 }; 4918 var _root = (this._root = this.stateQueue.register(rootStateDef)); 4919 _root.navigable = null; 4920 }; 4921 /** @internal */ 4922 StateRegistry.prototype.dispose = function () { 4923 var _this = this; 4924 this.stateQueue.dispose(); 4925 this.listeners = [];
4926 this.get().forEach(function (state) { return _this.get(state) && _this.deregister(state); }); 4927 }; 4928 /** 4929 * Listen for a State Registry events 4930 * 4931 * Adds a callback that is invoked when states are registered or deregistered with the StateRegistry. 4932 * 4933 * #### Example: 4934 * ```js 4935 * let allStates = registry.get(); 4936 * 4937 * // Later, invoke deregisterFn() to remove the listener 4938 * let deregisterFn = registry.onStatesChanged((event, states) => { 4939 * switch(event) { 4940 * case: 'registered': 4941 * states.forEach(state => allStates.push(state)); 4942 * break; 4943 * case: 'deregistered': 4944 * states.forEach(state => { 4945 * let idx = allStates.indexOf(state); 4946 * if (idx !== -1) allStates.splice(idx, 1); 4947 * }); 4948 * break; 4949 * } 4950 * }); 4951 * ``` 4952 * 4953 * @param listener a callback function invoked when the registered states changes. 4954 * The function receives two parameters, `event` and `state`. 4955 * See [[StateRegistryListener]] 4956 * @return a function that deregisters the listener 4957 */ 4958 StateRegistry.prototype.onStatesChanged = function (listener) { 4959 this.listeners.push(listener); 4960 return function deregisterListener() { 4961 removeFrom(this.listeners)(listener); 4962 }.bind(this); 4963 }; 4964 /** 4965 * Gets the implicit root state 4966 * 4967 * Gets the root of the state tree. 4968 * The root state is implicitly created by UI-Router. 4969 * Note: this returns the internal [[StateObject]] representation, not a [[StateDeclaration]] 4970 * 4971 * @return the root [[StateObject]] 4972 */ 4973 StateRegistry.prototype.root = function () { 4974 return this._root; 4975 }; 4976 /** 4977 * Adds a state to the registry 4978 * 4979 * Registers a [[StateDeclaration]] or queues it for registration. 4980 * 4981 * Note: a state will be queued if the state's parent isn't yet registered. 4982 * 4983 * @param stateDefinition the definition of the state to register. 4984 * @returns the internal [[StateObject]] object. 4985 * If the state was successfully registered, then the object is fully built (See: [[StateBuilder]]). 4986 * If the state was only queued, then the object is not fully built. 4987 */ 4988 StateRegistry.prototype.register = function (stateDefinition) { 4989 return this.stateQueue.register(stateDefinition); 4990 }; 4991 /** @internal */ 4992 StateRegistry.prototype._deregisterTree = function (state) { 4993 var _this = this; 4994 var all = this.get().map(function (s) { return s.$$state(); }); 4995 var getChildren = function (states) { 4996 var _children = all.filter(function (s) { return states.indexOf(s.parent) !== -1; }); 4997 return _children.length === 0 ? _children : _children.concat(getChildren(_children)); 4998 }; 4999 var children = getChildren([state]); 5000 var deregistered = [state].concat(children).reverse(); 5001 deregistered.forEach(function (_state) { 5002 var rulesApi = _this.router.urlService.rules; 5003 // Remove URL rule 5004 rulesApi 5005 .rules() 5006 .filter(propEq('state', _state)) 5007 .forEach(function (rule) { return rulesApi.removeRule(rule); }); 5008 // Remove state from registry 5009 delete _this.states[_state.name]; 5010 }); 5011 return deregistered; 5012 }; 5013 /** 5014 * Removes a state from the registry 5015 * 5016 * This removes a state from the registry. 5017 * If the state has children, they are are also removed from the registry. 5018 * 5019 * @param stateOrName the state's name or object representation 5020 * @returns {StateObject[]} a list of removed states 5021 */ 5022 StateRegistry.prototype.deregister = function (stateOrName) { 5023 var _state = this.get(stateOrName); 5024 if (!_state) 5025 throw new Error("Can't deregister state; not found: " + stateOrName); 5026 var deregisteredStates = this._deregisterTree(_state.$$state());
5027 this.listeners.forEach(function (listener) { 5028 return listener('deregistered', deregisteredStates.map(function (s) { return s.self; })); 5029 }); 5030 return deregisteredStates; 5031 }; 5032 StateRegistry.prototype.get = function (stateOrName, base) { 5033 var _this = this; 5034 if (arguments.length === 0) 5035 return Object.keys(this.states).map(function (name) { return _this.states[name].self; }); 5036 var found = this.matcher.find(stateOrName, base); 5037 return (found && found.self) || null; 5038 }; 5039 /** 5040 * Registers a [[BuilderFunction]] for a specific [[StateObject]] property (e.g., `parent`, `url`, or `path`). 5041 * More than one BuilderFunction can be registered for a given property. 5042 * 5043 * The BuilderFunction(s) will be used to define the property on any subsequently built [[StateObject]] objects. 5044 * 5045 * @param property The name of the State property being registered for. 5046 * @param builderFunction The BuilderFunction which will be used to build the State property 5047 * @returns a function which deregisters the BuilderFunction 5048 */ 5049 StateRegistry.prototype.decorator = function (property, builderFunction) { 5050 return this.builder.builder(property, builderFunction); 5051 }; 5052 return StateRegistry; 5053 }()); 5054 5055 (function (TransitionHookPhase) { 5056 TransitionHookPhase[TransitionHookPhase["CREATE"] = 0] = "CREATE"; 5057 TransitionHookPhase[TransitionHookPhase["BEFORE"] = 1] = "BEFORE"; 5058 TransitionHookPhase[TransitionHookPhase["RUN"] = 2] = "RUN"; 5059 TransitionHookPhase[TransitionHookPhase["SUCCESS"] = 3] = "SUCCESS"; 5060 TransitionHookPhase[TransitionHookPhase["ERROR"] = 4] = "ERROR"; 5061 })(exports.TransitionHookPhase || (exports.TransitionHookPhase = {})); 5062 5063 (function (TransitionHookScope) { 5064 TransitionHookScope[TransitionHookScope["TRANSITION"] = 0] = "TRANSITION"; 5065 TransitionHookScope[TransitionHookScope["STATE"] = 1] = "STATE"; 5066 })(exports.TransitionHookScope || (exports.TransitionHookScope = {})); 5067 5068 var defaultOptions = { 5069 current: noop, 5070 transition: null, 5071 traceData: {}, 5072 bind: null, 5073 }; 5074 var TransitionHook = /** @class */ (function () { 5075 function TransitionHook(transition, stateContext, registeredHook, options) { 5076 var _this = this; 5077 this.transition = transition; 5078 this.stateContext = stateContext; 5079 this.registeredHook = registeredHook; 5080 this.options = options; 5081 this.isSuperseded = function () { return _this.type.hookPhase === exports.TransitionHookPhase.RUN && !_this.options.transition.isActive(); }; 5082 this.options = defaults(options, defaultOptions); 5083 this.type = registeredHook.eventType; 5084 } 5085 /** 5086 * Chains together an array of TransitionHooks. 5087 * 5088 * Given a list of [[TransitionHook]] objects, chains them together. 5089 * Each hook is invoked after the previous one completes. 5090 * 5091 * #### Example: 5092 * ```js 5093 * var hooks: TransitionHook[] = getHooks(); 5094 * let promise: Promise<any> = TransitionHook.chain(hooks); 5095 * 5096 * promise.then(handleSuccess, handleError); 5097 * ``` 5098 * 5099 * @param hooks the list of hooks to chain together 5100 * @param waitFor if provided, the chain is `.then()`'ed off this promise 5101 * @returns a `Promise` for sequentially invoking the hooks (in order) 5102 */ 5103 TransitionHook.chain = function (hooks, waitFor) { 5104 // Chain the next hook off the previous 5105 var createHookChainR = function (prev, nextHook) { return prev.then(function () { return nextHook.invokeHook(); }); }; 5106 return hooks.reduce(createHookChainR, waitFor || services.$q.when()); 5107 }; 5108 /** 5109 * Invokes all the provided TransitionHooks, in order. 5110 * Each hook's return value is checked. 5111 * If any hook returns a promise, then the rest of the hooks are chained off that promise, and the promise is returned. 5112 * If no hook returns a promise, then all hooks are processed synchronously. 5113 * 5114 * @param hooks the list of TransitionHooks to invoke 5115 * @param doneCallback a callback that is invoked after all the hooks have successfully completed 5116 * 5117 * @returns a promise for the async result, or the result of the callback 5118 */ 5119 TransitionHook.invokeHooks = function (hooks, doneCallback) { 5120 for (var idx = 0; idx < hooks.length; idx++) { 5121 var hookResult = hooks[idx].invokeHook();
5122 if (isPromise(hookResult)) { 5123 var remainingHooks = hooks.slice(idx + 1); 5124 return TransitionHook.chain(remainingHooks, hookResult).then(doneCallback); 5125 } 5126 } 5127 return doneCallback(); 5128 }; 5129 /** 5130 * Run all TransitionHooks, ignoring their return value. 5131 */ 5132 TransitionHook.runAllHooks = function (hooks) { 5133 hooks.forEach(function (hook) { return hook.invokeHook(); }); 5134 }; 5135 TransitionHook.prototype.logError = function (err) { 5136 this.transition.router.stateService.defaultErrorHandler()(err); 5137 }; 5138 TransitionHook.prototype.invokeHook = function () { 5139 var _this = this; 5140 var hook = this.registeredHook; 5141 if (hook._deregistered) 5142 return; 5143 var notCurrent = this.getNotCurrentRejection(); 5144 if (notCurrent) 5145 return notCurrent; 5146 var options = this.options; 5147 trace.traceHookInvocation(this, this.transition, options); 5148 var invokeCallback = function () { return hook.callback.call(options.bind, _this.transition, _this.stateContext); }; 5149 var normalizeErr = function (err) { return Rejection.normalize(err).toPromise(); }; 5150 var handleError = function (err) { return hook.eventType.getErrorHandler(_this)(err); }; 5151 var handleResult = function (result) { return hook.eventType.getResultHandler(_this)(result); }; 5152 try { 5153 var result = invokeCallback(); 5154 if (!this.type.synchronous && isPromise(result)) { 5155 return result.catch(normalizeErr).then(handleResult, handleError); 5156 } 5157 else { 5158 return handleResult(result); 5159 } 5160 } 5161 catch (err) { 5162 // If callback throws (synchronously) 5163 return handleError(Rejection.normalize(err)); 5164 } 5165 finally { 5166 if (hook.invokeLimit && ++hook.invokeCount >= hook.invokeLimit) { 5167 hook.deregister(); 5168 } 5169 } 5170 }; 5171 /** 5172 * This method handles the return value of a Transition Hook. 5173 * 5174 * A hook can return false (cancel), a TargetState (redirect), 5175 * or a promise (which may later resolve to false or a redirect) 5176 * 5177 * This also handles "transition superseded" -- when a new transition 5178 * was started while the hook was still running 5179 */ 5180 TransitionHook.prototype.handleHookResult = function (result) { 5181 var _this = this; 5182 var notCurrent = this.getNotCurrentRejection(); 5183 if (notCurrent) 5184 return notCurrent; 5185 // Hook returned a promise 5186 if (isPromise(result)) { 5187 // Wait for the promise, then reprocess with the resulting value 5188 return result.then(function (val) { return _this.handleHookResult(val); }); 5189 } 5190 trace.traceHookResult(result, this.transition, this.options); 5191 // Hook returned false 5192 if (result === false) { 5193 // Abort this Transition 5194 return Rejection.aborted('Hook aborted transition').toPromise(); 5195 } 5196 var isTargetState = is(TargetState); 5197 // hook returned a TargetState 5198 if (isTargetState(result)) { 5199 // Halt the current Transition and redirect (a new Transition) to the TargetState. 5200 return Rejection.redirected(result).toPromise(); 5201 } 5202 }; 5203 /** 5204 * Return a Rejection promise if the transition is no longer current due 5205 * to a stopped router (disposed), or a new transition has started and superseded this one. 5206 */ 5207 TransitionHook.prototype.getNotCurrentRejection = function () { 5208 var router = this.transition.router; 5209 // The router is stopped 5210 if (router._disposed) { 5211 return Rejection.aborted("UIRouter instance #" + router.$i
5211d + " has been stopped (disposed)").toPromise(); 5212 } 5213 if (this.transition._aborted) { 5214 return Rejection.aborted().toPromise(); 5215 } 5216 // This transition is no longer current. 5217 // Another transition started while this hook was still running. 5218 if (this.isSuperseded()) { 5219 // Abort this transition 5220 return Rejection.superseded(this.options.current()).toPromise(); 5221 } 5222 }; 5223 TransitionHook.prototype.toString = function () { 5224 var _a = this, options = _a.options, registeredHook = _a.registeredHook; 5225 var event = parse('traceData.hookType')(options) || 'internal', context = parse('traceData.context.state.name')(options) || parse('traceData.context')(options) || 'unknown', name = fnToString(registeredHook.callback); 5226 return event + " context: " + context + ", " + maxLength(200, name); 5227 }; 5228 /** 5229 * These GetResultHandler(s) are used by [[invokeHook]] below 5230 * Each HookType chooses a GetResultHandler (See: [[TransitionService._defineCoreEvents]]) 5231 */ 5232 TransitionHook.HANDLE_RESULT = function (hook) { return function (result) { 5233 return hook.handleHookResult(result); 5234 }; }; 5235 /** 5236 * If the result is a promise rejection, log it. 5237 * Otherwise, ignore the result. 5238 */ 5239 TransitionHook.LOG_REJECTED_RESULT = function (hook) { return function (result) { 5240 isPromise(result) && result.catch(function (err) { return hook.logError(Rejection.normalize(err)); }); 5241 return undefined; 5242 }; }; 5243 /** 5244 * These GetErrorHandler(s) are used by [[invokeHook]] below 5245 * Each HookType chooses a GetErrorHandler (See: [[TransitionService._defineCoreEvents]]) 5246 */ 5247 TransitionHook.LOG_ERROR = function (hook) { return function (error) { return hook.logError(error); }; }; 5248 TransitionHook.REJECT_ERROR = function (hook) { return function (error) { return silentRejection(error); }; }; 5249 TransitionHook.THROW_ERROR = function (hook) { return function (error) { 5250 throw error; 5251 }; }; 5252 return TransitionHook; 5253 }()); 5254 5255 /** 5256 * Determines if the given state matches the matchCriteria 5257 * 5258 * @internal 5259 * 5260 * @param state a State Object to test against 5261 * @param criterion 5262 * - If a string, matchState uses the string as a glob-matcher against the state name 5263 * - If an array (of strings), matchState uses each string in the array as a glob-matchers against the state name 5264 * and returns a positive match if any of the globs match. 5265 * - If a function, matchState calls the function with the state and returns true if the function's result is truthy. 5266 * @returns {boolean} 5267 */ 5268 function matchState(state, criterion, transition) { 5269 var toMatch = isString(criterion) ? [criterion] : criterion; 5270 function matchGlobs(_state) { 5271 var globStrings = toMatch; 5272 for (var i = 0; i < globStrings.length; i++) { 5273 var glob = new Glob(globStrings[i]); 5274 if ((glob && glob.matches(_state.name)) || (!glob && globStrings[i] === _state.name)) { 5275 return true; 5276 } 5277 } 5278 return false; 5279 } 5280 var matchFn = (isFunction(toMatch) ? toMatch : matchGlobs); 5281 return !!matchFn(state, transition); 5282 } 5283 /** 5284 * The registration data for a registered transition hook 5285 */ 5286 var RegisteredHook = /** @class */ (function () { 5287 function RegisteredHook(tranSvc, eventType, callback, matchCriteria, removeHookFromRegistry, options) { 5288 if (options === void 0) { options = {}; } 5289 this.tranSvc = tranSvc; 5290 this.eventType = eventType; 5291 this.callback = callback; 5292 this.matchCriteria = matchCriteria; 5293 this.removeHookFromRegistry = removeHookFromRegistry; 5294 this.invokeCount = 0; 5295 this._deregistered = false;
5296 this.priority = options.priority || 0; 5297 this.bind = options.bind || null; 5298 this.invokeLimit = options.invokeLimit; 5299 } 5300 /** 5301 * Gets the matching [[PathNode]]s 5302 * 5303 * Given an array of [[PathNode]]s, and a [[HookMatchCriterion]], returns an array containing 5304 * the [[PathNode]]s that the criteria matches, or `null` if there were no matching nodes. 5305 * 5306 * Returning `null` is significant to distinguish between the default 5307 * "match-all criterion value" of `true` compared to a `() => true` function, 5308 * when the nodes is an empty array. 5309 * 5310 * This is useful to allow a transition match criteria of `entering: true` 5311 * to still match a transition, even when `entering === []`. Contrast that 5312 * with `entering: (state) => true` which only matches when a state is actually 5313 * being entered. 5314 */ 5315 RegisteredHook.prototype._matchingNodes = function (nodes, criterion, transition) { 5316 if (criterion === true) 5317 return nodes; 5318 var matching = nodes.filter(function (node) { return matchState(node.state, criterion, transition); }); 5319 return matching.length ? matching : null; 5320 }; 5321 /** 5322 * Gets the default match criteria (all `true`) 5323 * 5324 * Returns an object which has all the criteria match paths as keys and `true` as values, i.e.: 5325 * 5326 * ```js 5327 * { 5328 * to: true, 5329 * from: true, 5330 * entering: true, 5331 * exiting: true, 5332 * retained: true, 5333 * } 5334 */ 5335 RegisteredHook.prototype._getDefaultMatchCriteria = function () { 5336 return mapObj(this.tranSvc._pluginapi._getPathTypes(), function () { return true; }); 5337 }; 5338 /** 5339 * Gets matching nodes as [[IMatchingNodes]] 5340 * 5341 * Create a IMatchingNodes object from the TransitionHookTypes that is roughly equivalent to: 5342 * 5343 * ```js 5344 * let matches: IMatchingNodes = { 5345 * to: _matchingNodes([tail(treeChanges.to)], mc.to), 5346 * from: _matchingNodes([tail(treeChanges.from)], mc.from), 5347 * exiting: _matchingNodes(treeChanges.exiting, mc.exiting), 5348 * retained: _matchingNodes(treeChanges.retained, mc.retained), 5349 * entering: _matchingNodes(treeChanges.entering, mc.entering), 5350 * }; 5351 * ``` 5352 */ 5353 RegisteredHook.prototype._getMatchingNodes = function (treeChanges, transition) { 5354 var _this = this; 5355 var criteria = extend(this._getDefaultMatchCriteria(), this.matchCriteria); 5356 var paths = values(this.tranSvc._pluginapi._getPathTypes()); 5357 return paths.reduce(function (mn, pathtype) { 5358 // STATE scope criteria matches against every node in the path. 5359 // TRANSITION scope criteria matches against only the last node in the path 5360 var isStateHook = pathtype.scope === exports.TransitionHookScope.STATE; 5361 var path = treeChanges[pathtype.name] || []; 5362 var nodes = isStateHook ? path : [tail(path)]; 5363 mn[pathtype.name] = _this._matchingNodes(nodes, criteria[pathtype.name], transition); 5364 return mn; 5365 }, {}); 5366 }; 5367 /** 5368 * Determines if this hook's [[matchCriteria]] match the given [[TreeChanges]] 5369 * 5370 * @returns an IMatchingNodes object, or null. If an IMatchingNodes object is returned, its values 5371 * are the matching [[PathNode]]s for each [[HookMatchCriterion]] (to, from, exiting, retained, entering) 5372 */ 5373 RegisteredHook.prototype.matches = function (treeChanges, transition) { 5374 var matches = this._getMatchingNodes(treeChanges, transition); 5375 // Check if all the criteria matched the TreeChanges object 5376 var allMatched = values(matches).every(identity); 5377 return allMatched ? matches : null; 5378 }; 5379 RegisteredHook.prototype.deregister = function () { 5380 this.removeHookFromRegistry(this); 5381 this._deregistered = true; 5382 }; 5383 return RegisteredHook; 5384 }()); 5385 /** Return a registration function of the requested type. */ 5386 function makeEvent(registry, transitionService, eventType) { 5387 // Create the object which holds the registered transition hooks. 5388 var _registeredHooks = (registry._registeredHooks = registry._registeredHooks || {}); 5389 var hooks = (_registeredHooks[eventType.name] = []); 5390 var removeHookFn = removeFrom(hooks); 5391 // Create hook registration function on the IHookRegistry for the event 5392 registry[eventType.name] = hookRegistrationFn; 5393 function hookRegistrationFn(matchObject, callback, options) { 5394 if (options === void 0) { options = {}; } 5395 var registeredHook = new RegisteredHook(transitionService, eventType, callback, matchObject, removeHookFn, options); 5396 hooks.push(registeredHook); 5397 return registeredHook.deregister.bind(registeredHook); 5398 } 5399 return hookRegistrationFn; 5400 } 5401 5402 /** 5403 * This class returns applicable TransitionHooks for a specific Transition instance. 5404 * 5405 * Hooks ([[RegisteredHook]]) may be registered globally, e.g., $transitions.onEnter(...), or locally, e.g. 5406 * myTransition.onEnter(...). The HookBuilder finds matching RegisteredHooks (where the match criteria is 5407 * determined by the type of hook) 5408 * 5409 * The HookBuilder also converts RegisteredHooks objects to TransitionHook objects, which are used to run a Transition. 5410 * 5411 * The HookBuilder constructor is given the $transitions service and a Transition instance. Thus, a HookBuilder 5412 * instance may only be used for one specific Transition object. (side note: the _treeChanges accessor is private 5413 * in the Transition class, so we must also provide the Transition's _treeChanges) 5414 */ 5415 var HookBuilder = /** @class */ (function () { 5416 function HookBuilder(transition) { 5417 this.transition = transition; 5418 } 5419 HookBuilder.prototype.buildHooksForPhase = function (phase) { 5420 var _this = this; 5421 var $transitions = this.transition.router.transitionService; 5422 return $transitions._pluginapi 5423 ._getEvents(phase) 5424 .map(function (type) { return _this.buildHooks(type); }) 5425 .reduce(unnestR, []) 5426 .filter(identity); 5427 }; 5428 /** 5429 * Returns an array of newly built TransitionHook objects. 5430 * 5431 * - Finds all RegisteredHooks registered for the given `hookType` which matched the transition's [[TreeChanges]]. 5432 * - Finds [[PathNode]] (or `PathNode[]`) to use as the TransitionHook context(s) 5433 * - For each of the [[PathNode]]s, creates a TransitionHook 5434 * 5435 * @param hookType the type of the hook registration function, e.g., 'onEnter', 'onFinish'. 5436 */ 5437 HookBuilder.prototype.buildHooks = function (hookType) { 5438 var transition = this.transition; 5439 var treeChanges = transition.treeChanges(); 5440 // Find all the matching registered hooks for a given hook type 5441 var matchingHooks = this.getMatchingHooks(hookType, treeChanges, transition); 5442 if (!matchingHooks) 5443 return []; 5444 var baseHookOptions = { 5445 transition: transition, 5446 current: transition.options().current, 5447 }; 5448 var makeTransitionHooks = function (hook) { 5449 // Fetch the Nodes that caused this hook to match. 5450 var matches = hook.matches(treeChanges, transition); 5451 // Select the PathNode[] that will be used as TransitionHook context objects 5452 var matchingNodes = matches[hookType.criteriaMatchPath.name]; 5453 // Return an array of HookTuples 5454 return matchingNodes.map(function (node) { 5455 var _options = extend({ 5456 bind: hook.bind, 5457 traceData: { hookType: hookType.name, context: node }, 5458 }, baseHookOptions); 5459 var state = hookType.criteriaMatchPath.scope === exports.TransitionHookScope.STATE ? node.state.self : null; 5460 var transitionHook = new TransitionHook(transition, state, hook, _options); 5461 return { hook: hook, node: node, transitionHook: transitionHook }; 5462 }); 5463 }; 5464 return matchingHooks 5465 .map(makeTransitionHooks) 5466 .reduce(unnestR, []) 5467 .sort(tupleSort(hookType.reverseSort)) 5468 .map(function (tuple) { return tuple.transitionHook; }); 5469 }; 5470 /** 5471 * Finds all RegisteredHooks from: 5472 * - The Transition object instance hook registry 5473 * - The TransitionService ($transitions) global hook registry 5474 * 5475 * which matched: 5476 * - the eventType 5477 * - the matchCriteria (to, from, exiting, retained, entering) 5478 * 5479 * @returns an array of matched [[RegisteredHook]]s 5480 */ 5481 HookBuilder.prototype.getMatchingHooks = function (hookType, treeChanges, transition) { 5482 var isCreate = hookType.hookPhase === exports.TransitionHookPhase.CREATE; 5483 // Instance and Global hook registries 5484 var $transitions = this.transition.router.transitionService; 5485 var registries = isCreate ? [$transitions] : [this.transition, $transitions]; 5486 return registries 5487 .map(function (reg) { return reg.getHooks(hookType.name); }) // Get named hooks from registries 5488 .filter(assertPredicate(isArray, "broken event named: " + hookType.name)) // Sanity check 5489 .reduce(unnestR, []) // Un-nest RegisteredHook[][] to RegisteredHook[] array 5490 .filter(function (hook) { return hook.matches(treeChanges, transition); }); // Only those satisfying matchCriteria 5491 }; 5492 return HookBuilder; 5493 }()); 5494 /** 5495 * A factory for a sort function for HookTuples. 5496 * 5497 * The sort function first compares the PathNode depth (how deep in the state tree a node is), then compares 5498 * the EventHook priority. 5499 * 5500 * @param reverseDepthSort a boolean, when true, reverses the sort order for the node depth 5501 * @returns a tuple sort function 5502 */ 5503 function tupleSort(reverseDepthSort) { 5504 if (reverseDepthSort === void 0) { reverseDepthSort = false; } 5505 return function nodeDepthThenPriority(l, r) { 5506 var factor = reverseDepthSort ? -1 : 1; 5507 var depthDelta = (l.node.state.path.length - r.node.state.path.length) * factor; 5508 return depthDelta !== 0 ? depthDelta : r.hook.priority - l.hook.priority; 5509 }; 5510 } 5511 5512 /** @internal */ 5513 var stateSelf = prop('self'); 5514 /** 5515 * Represents a transition between two states. 5516 * 5517 * When navigating to a state, we are transitioning **from** the current state **to** the new state. 5518 *
5519 * This object contains all contextual information about the to/from states, parameters, resolves. 5520 * It has information about all states being entered and exited as a result of the transition. 5521 */ 5522 var Transition = /** @class */ (function () { 5523 /** 5524 * Creates a new Transition object. 5525 * 5526 * If the target state is not valid, an error is thrown. 5527 * 5528 * @internal 5529 * 5530 * @param fromPath The path of [[PathNode]]s from which the transition is leaving. The last node in the `fromPath` 5531 * encapsulates the "from state". 5532 * @param targetState The target state and parameters being transitioned to (also, the transition options) 5533 * @param router The [[UIRouter]] instance 5534 * @internal 5535 */ 5536 function Transition(fromPath, targetState, router) { 5537 var _this = this; 5538 /** @internal */ 5539 this._deferred = services.$q.defer(); 5540 /** 5541 * This promise is resolved or rejected based on the outcome of the Transition. 5542 * 5543 * When the transition is successful, the promise is resolved 5544 * When the transition is unsuccessful, the promise is rejected with the [[Rejection]] or javascript error 5545 */ 5546 this.promise = this._deferred.promise; 5547 /** @internal Holds the hook registration functions such as those passed to Transition.onStart() */ 5548 this._registeredHooks = {}; 5549 /** @internal */ 5550 this._hookBuilder = new HookBuilder(this); 5551 /** Checks if this transition is currently active/running. */ 5552 this.isActive = function () { return _this.router.globals.transition === _this; }; 5553 this.router = router; 5554 this._targetState = targetState; 5555 if (!targetState.valid()) { 5556 throw new Error(targetState.error()); 5557 } 5558 // current() is assumed to come from targetState.options, but provide a naive implementation otherwise. 5559 this._options = extend({ current: val(this) }, targetState.options()); 5560 this.$id = router.transitionService._transitionCount++; 5561 var toPath = PathUtils.buildToPath(fromPath, targetState); 5562 this._treeChanges = PathUtils.treeChanges(fromPath, toPath, this._options.reloadState); 5563 this.createTransitionHookRegFns(); 5564 var onCreateHooks = this._hookBuilder.buildHooksForPhase(exports.TransitionHookPhase.CREATE); 5565 TransitionHook.invokeHooks(onCreateHooks, function () { return null; }); 5566 this.applyViewConfigs(router); 5567 } 5568 /** @internal */ 5569 Transition.prototype.onBefore = function (criteria, callback, options) { 5570 return; 5571 }; 5572 /** @inheritdoc */ 5573 Transition.prototype.onStart = function (criteria, callback, options) { 5574 return; 5575 }; 5576 /** @inheritdoc */ 5577 Transition.prototype.onExit = function (criteria, callback, options) { 5578 return; 5579 }; 5580 /** @inheritdoc */ 5581 Transition.prototype.onRetain = function (criteria, callback, options) { 5582 return; 5583 }; 5584 /** @inheritdoc */ 5585 Transition.prototype.onEnter = function (criteria, callback, options) { 5586 return; 5587 }; 5588 /** @inheritdoc */ 5589 Transition.prototype.onFinish = function (criteria, callback, options) { 5590 return; 5591 }; 5592 /** @inheritdoc */ 5593 Transition.prototype.onSuccess = function (criteria, callback, options) { 5594 return; 5595 }; 5596 /** @inheritdoc */ 5597 Transition.prototype.onError = function (criteria, callback, options) { 5598 return; 5599 }; 5600 /** @internal 5601 * Creates the transition-level hook registration functions 5602 * (which can then be used to register hooks) 5603 */ 5604 Transition.prototype.createTransitionHookRegFns = function () { 5605 var _this = this; 5606 this.router.transitionService._pluginapi 5607 ._getEvents() 5608 .filter(function (type) { return type.hookPhase !== exports.TransitionHookPhase.CREATE; }) 5609 .forEach(function (type) { return makeEvent(_this, _this.router.transitionService, type); }); 5610 }; 5611 /** @internal */
5612 Transition.prototype.getHooks = function (hookName) { 5613 return this._registeredHooks[hookName]; 5614 }; 5615 Transition.prototype.applyViewConfigs = function (router) { 5616 var enteringStates = this._treeChanges.entering.map(function (node) { return node.state; }); 5617 PathUtils.applyViewConfigs(router.transitionService.$view, this._treeChanges.to, enteringStates); 5618 }; 5619 /** 5620 * @internal 5621 * @returns the internal from [State] object 5622 */ 5623 Transition.prototype.$from = function () { 5624 return tail(this._treeChanges.from).state; 5625 }; 5626 /** 5627 * @internal 5628 * @returns the internal to [State] object 5629 */ 5630 Transition.prototype.$to = function () { 5631 return tail(this._treeChanges.to).state; 5632 }; 5633 /** 5634 * Returns the "from state" 5635 * 5636 * Returns the state that the transition is coming *from*. 5637 * 5638 * @returns The state declaration object for the Transition's ("from state"). 5639 */ 5640 Transition.prototype.from = function () { 5641 return this.$from().self; 5642 }; 5643 /** 5644 * Returns the "to state" 5645 * 5646 * Returns the state that the transition is going *to*. 5647 * 5648 * @returns The state declaration object for the Transition's target state ("to state"). 5649 */ 5650 Transition.prototype.to = function () { 5651 return this.$to().self; 5652 }; 5653 /** 5654 * Gets the Target State 5655 * 5656 * A transition's [[TargetState]] encapsulates the [[to]] state, the [[params]], and the [[options]] as a single object. 5657 * 5658 * @returns the [[TargetState]] of this Transition 5659 */ 5660 Transition.prototype.targetState = function () { 5661 return this._targetState; 5662 }; 5663 /** 5664 * Determines whether two transitions are equivalent. 5665 * @deprecated 5666 */ 5667 Transition.prototype.is = function (compare) { 5668 if (compare instanceof Transition) { 5669 // TODO: Also compare parameters 5670 return this.is({ to: compare.$to().name, from: compare.$from().name }); 5671 } 5672 return !((compare.to && !matchState(this.$to(), compare.to, this)) || 5673 (compare.from && !matchState(this.$from(), compare.from, this))); 5674 }; 5675 Transition.prototype.params = function (pathname) { 5676 if (pathname === void 0) { pathname = 'to'; } 5677 return Object.freeze(this._treeChanges[pathname].map(prop('paramValues')).reduce(mergeR, {})); 5678 }; 5679 Transition.prototype.paramsChanged = function () { 5680 var fromParams = this.params('from'); 5681 var toParams = this.params('to'); 5682 // All the parameters declared on both the "to" and "from" paths 5683 var allParamDescriptors = [] 5684 .concat(this._treeChanges.to) 5685 .concat(this._treeChanges.from) 5686 .map(function (pathNode) { return pathNode.paramSchema; }) 5687 .reduce(flattenR, []) 5688 .reduce(uniqR, []); 5689 var changedParamDescriptors = Param.changed(allParamDescriptors, fromParams, toParams); 5690 return changedParamDescriptors.reduce(function (changedValues, descriptor) { 5691 changedValues[descriptor.id] = toParams[descriptor.id]; 5692 return changedValues; 5693 }, {}); 5694 }; 5695 /** 5696 * Creates a [[UIInjector]] Dependency Injector 5697 * 5698 * Returns a Dependency Injector for the Transition's target state (to state). 5699 * The injector provides resolve values which the target state has access to. 5700 * 5701 * The `UIInjector` can also provide values from the native root/global injector (ng1/ng2). 5702 * 5703 * #### Example: 5704 * ```js 5705 * .onEnter({ entering: 'myState' }, trans => { 5706 * var myResolveValue = trans.injector().get('myResolve'); 5707 * // Inject a global service from the global/native injector (if it exists) 5708 * var MyService = trans.injector().get('MyService'); 5709 * }) 5710 * ``` 5711 *
5712 * In some cases (such as `onBefore`), you may need access to some resolve data but it has not yet been fetched. 5713 * You can use [[UIInjector.getAsync]] to get a promise for the data. 5714 * #### Example: 5715 * ```js 5716 * .onBefore({}, trans => { 5717 * return trans.injector().getAsync('myResolve').then(myResolveValue => 5718 * return myResolveValue !== 'ABORT'; 5719 * }); 5720 * }); 5721 * ``` 5722 * 5723 * If a `state` is provided, the injector that is returned will be limited to resolve values that the provided state has access to. 5724 * This can be useful if both a parent state `foo` and a child state `foo.bar` have both defined a resolve such as `data`. 5725 * #### Example: 5726 * ```js 5727 * .onEnter({ to: 'foo.bar' }, trans => { 5728 * // returns result of `foo` state's `myResolve` resolve 5729 * // even though `foo.bar` also has a `myResolve` resolve 5730 * var fooData = trans.injector('foo').get('myResolve'); 5731 * }); 5732 * ``` 5733 * 5734 * If you need resolve data from the exiting states, pass `'from'` as `pathName`. 5735 * The resolve data from the `from` path will be returned. 5736 * #### Example: 5737 * ```js 5738 * .onExit({ exiting: 'foo.bar' }, trans => { 5739 * // Gets the resolve value of `myResolve` from the state being exited 5740 * var fooData = trans.injector(null, 'from').get('myResolve'); 5741 * }); 5742 * ``` 5743 * 5744 * 5745 * @param state Limits the resolves provided to only the resolves the provided state has access to. 5746 * @param pathName Default: `'to'`: Chooses the path for which to create the injector. Use this to access resolves for `exiting` states. 5747 * 5748 * @returns a [[UIInjector]] 5749 */ 5750 Transition.prototype.injector = function (state, pathName) { 5751 if (pathName === void 0) { pathName = 'to'; } 5752 var path = this._treeChanges[pathName]; 5753 if (state) 5754 path = PathUtils.subPath(path, function (node) { return node.state === state || node.state.name === state; }); 5755 return new ResolveContext(path).injector(); 5756 }; 5757 /** 5758 * Gets all available resolve tokens (keys) 5759 * 5760 * This method can be used in conjunction with [[injector]] to inspect the resolve values 5761 * available to the Transition. 5762 * 5763 * This returns all the tokens defined on [[StateDeclaration.resolve]] blocks, for the states 5764 * in the Transition's [[TreeChanges.to]] path. 5765 * 5766 * #### Example: 5767 * This example logs all resolve values 5768 * ```js 5769 * let tokens = trans.getResolveTokens(); 5770 * tokens.forEach(token => console.log(token + " = " + trans.injector().get(token))); 5771 * ``` 5772 * 5773 * #### Example: 5774 * This example creates promises for each resolve value. 5775 * This triggers fetches of resolves (if any have not yet been fetched). 5776 * When all promises have all settled, it logs the resolve values. 5777 * ```js 5778 * let tokens = trans.getResolveTokens(); 5779 * let promise = tokens.map(token => trans.injector().getAsync(token)); 5780 * Promise.all(promises).then(values => console.log("Resolved values: " + values)); 5781 * ``` 5782 * 5783 * Note: Angular 1 users whould use `$q.all()` 5784 * 5785 * @param pathname resolve context's path name (e.g., `to` or `from`) 5786 * 5787 * @returns an array of resolve tokens (keys) 5788 */ 5789 Transition.prototype.getResolveTokens = function (pathname) { 5790 if (pathname === void 0) { pathname = 'to'; } 5791 return new ResolveContext(this._treeChanges[pathname]).getTokens(); 5792 }; 5793 /** 5794 * Dynamically adds a new [[Resolvable]] (i.e., [[StateDeclaration.resolve]]) to this transition. 5795 * 5796 * Allows a transition hook to dynamically add a Resolvable to this Transition. 5797 * 5798 * Use the [[Transition.injector]] to retrieve the resolved data in subsequent hooks ([[UIInjector.get]]
5798). 5799 * 5800 * If a `state` argument is provided, the Resolvable is processed when that state is being entered. 5801 * If no `state` is provided then the root state is used. 5802 * If the given `state` has already been entered, the Resolvable is processed when any child state is entered. 5803 * If no child states will be entered, the Resolvable is processed during the `onFinish` phase of the Transition. 5804 * 5805 * The `state` argument also scopes the resolved data. 5806 * The resolved data is available from the injector for that `state` and any children states. 5807 * 5808 * #### Example: 5809 * ```js 5810 * transitionService.onBefore({}, transition => { 5811 * transition.addResolvable({ 5812 * token: 'myResolve', 5813 * deps: ['MyService'], 5814 * resolveFn: myService => myService.getData() 5815 * }); 5816 * }); 5817 * ``` 5818 * 5819 * @param resolvable a [[ResolvableLiteral]] object (or a [[Resolvable]]) 5820 * @param state the state in the "to path" which should receive the new resolve (otherwise, the root state) 5821 */ 5822 Transition.prototype.addResolvable = function (resolvable, state) { 5823 if (state === void 0) { state = ''; } 5824 resolvable = is(Resolvable)(resolvable) ? resolvable : new Resolvable(resolvable); 5825 var stateName = typeof state === 'string' ? state : state.name; 5826 var topath = this._treeChanges.to; 5827 var targetNode = find(topath, function (node) { return node.state.name === stateName; }); 5828 var resolveContext = new ResolveContext(topath); 5829 resolveContext.addResolvables([resolvable], targetNode.state); 5830 }; 5831 /** 5832 * Gets the transition from which this transition was redirected. 5833 * 5834 * If the current transition is a redirect, this method returns the transition that was redirected. 5835 * 5836 * #### Example: 5837 * ```js 5838 * let transitionA = $state.go('A').transition 5839 * transitionA.onStart({}, () => $state.target('B')); 5840 * $transitions.onSuccess({ to: 'B' }, (trans) => { 5841 * trans.to().name === 'B'; // true 5842 * trans.redirectedFrom() === transitionA; // true 5843 * }); 5844 * ``` 5845 * 5846 * @returns The previous Transition, or null if this Transition is not the result of a redirection 5847 */ 5848 Transition.prototype.redirectedFrom = function () { 5849 return this._options.redirectedFrom || null; 5850 }; 5851 /** 5852 * Gets the original transition in a redirect chain 5853 * 5854 * A transition might belong to a long chain of multiple redirects. 5855 * This method walks the [[redirectedFrom]] chain back to the original (first) transition in the chain. 5856 * 5857 * #### Example: 5858 * ```js 5859 * // states 5860 * registry.register({ name: 'A', redirectTo: 'B' }); 5861 * registry.register({ name: 'B', redirectTo: 'C' }); 5862 * registry.register({ name: 'C', redirectTo: 'D' }); 5863 * registry.register({ name: 'D' }); 5864 * 5865 * let transitionA = $state.go('A').transition 5866 * 5867 * $transitions.onSuccess({ to: 'D' }, (trans) => { 5868 * trans.to().name === 'D'; // true 5869 * trans.redirectedFrom().to().name === 'C'; // true 5870 * trans.originalTransition() === transitionA; // true 5871 * trans.originalTransition().to().name === 'A'; // true 5872 * }); 5873 * ``` 5874 * 5875 * @returns The original Transition that started a redirect chain 5876 */ 5877 Transition.prototype.originalTransition = function () { 5878 var rf = this.redirectedFrom(); 5879 return (rf && rf.originalTransition()) || this; 5880 }; 5881 /** 5882 * Get the transition options 5883 * 5884 * @returns the options for this Transition. 5885 */ 5886 Transition.prototype.options = function () { 5887 return this._options; 5888 }; 5889 /** 5890 * Gets the states being entered. 5891 * 5892 * @returns an array of states that will be entered during this transition. 5893 */ 5894 Transition.prototype.entering = function () { 5895 return map(this._treeChanges.entering, prop('state')).map(stateSelf); 5896 }; 5897 /** 5898 * Gets the states being exited. 5899 * 5900 * @returns an array of states that will be exited during this transition. 5901 */ 5902 Transition.prototype.exiting = function () { 5903 return map(this._treeChanges.exiting, prop('state')).map(stateSelf).reverse(); 5904 }; 5905 /** 5906 * Gets the states being retained. 5907 * 5908 * @returns an array of states that are already entered from a previous Transition, that will not be 5909 * exited during this Transition 5910 */ 5911 Transition.prototype.retained = function () { 5912 return map(this._treeChanges.retained, prop('state')).map(stateSelf); 5913 }; 5914 /** 5915 * Get the [[ViewConfig]]s associated with this Transition 5916 * 5917 * Each state can define one or more views (template/controller), which are encapsulated as `ViewConfig` objects. 5918 * This method fetches the `ViewConfigs` for a given path in the Transition (e.g., "to" or "entering").
5919 * 5920 * @param pathname the name of the path to fetch views for: 5921 * (`'to'`, `'from'`, `'entering'`, `'exiting'`, `'retained'`) 5922 * @param state If provided, only returns the `ViewConfig`s for a single state in the path 5923 * 5924 * @returns a list of ViewConfig objects for the given path. 5925 */ 5926 Transition.prototype.views = function (pathname, state) { 5927 if (pathname === void 0) { pathname = 'entering'; } 5928 var path = this._treeChanges[pathname]; 5929 path = !state ? path : path.filter(propEq('state', state)); 5930 return path.map(prop('views')).filter(identity).reduce(unnestR, []); 5931 }; 5932 Transition.prototype.treeChanges = function (pathname) { 5933 return pathname ? this._treeChanges[pathname] : this._treeChanges; 5934 }; 5935 /** 5936 * Creates a new transition that is a redirection of the current one. 5937 * 5938 * This transition can be returned from a [[TransitionService]] hook to 5939 * redirect a transition to a new state and/or set of parameters. 5940 * 5941 * @internal 5942 * 5943 * @returns Returns a new [[Transition]] instance. 5944 */ 5945 Transition.prototype.redirect = function (targetState) { 5946 var redirects = 1, trans = this; 5947 // tslint:disable-next-line:no-conditional-assignment 5948 while ((trans = trans.redirectedFrom()) != null) { 5949 if (++redirects > 20) 5950 throw new Error("Too many consecutive Transition redirects (20+)"); 5951 } 5952 var redirectOpts = { redirectedFrom: this, source: 'redirect' }; 5953 // If the original transition was caused by URL sync, then use { location: 'replace' } 5954 // on the new transition (unless the target state explicitly specifies location: false). 5955 // This causes the original url to be replaced with the url for the redirect target 5956 // so the original url disappears from the browser history. 5957 if (this.options().source === 'url' && targetState.options().location !== false) { 5958 redirectOpts.location = 'replace'; 5959 } 5960 var newOptions = extend({}, this.options(), targetState.options(), redirectOpts); 5961 targetState = targetState.withOptions(newOptions, true); 5962 var newTransition = this.router.transitionService.create(this._treeChanges.from, targetState); 5963 var originalEnteringNodes = this._treeChanges.entering; 5964 var redirectEnteringNodes = newTransition._treeChanges.entering; 5965 // --- Re-use resolve data from original transition --- 5966 // When redirecting from a parent state to a child state where the parent parameter values haven't changed 5967 // (because of the redirect), the resolves fetched by the original transition are still valid in the 5968 // redirected transition. 5969 // 5970 // This allows you to define a redirect on a parent state which depends on an async resolve value. 5971 // You can wait for the resolve, then redirect to a child state based on the result. 5972 // The redirected transition does not have to re-fetch the resolve. 5973 // --------------------------------------------------------- 5974 var nodeIsReloading = function (reloadState) { return function (node) { 5975 return reloadState && node.state.includes[reloadState.name]; 5976 }; }; 5977 // Find any "entering" nodes in the redirect path that match the original path and aren't being reloaded 5978 var matchingEnteringNodes = PathUtils.matching(redirectEnteringNodes, originalEnteringNodes, PathUtils.nonDynamicParams).filter(not(nodeIsReloading(targetState.options().reloadState))); 5979 // Use the existing (possibly pre-resolved) resolvables for the matching entering nodes. 5980 matchingEnteringNodes.forEach(function (node, idx) { 5981 node.resolvables = originalEnteringNodes[idx].resolvables; 5982 }); 5983 return newTransition; 5984 }; 5985 /** @internal If a transition doesn't exit/enter any states, returns any [[Param]] whose value changed */ 5986 Transition.prototype._changedParams = function () { 5987 var tc = this._treeChanges; 5988 /** Return undefined if it's not a "dynamic" transition, for the following reasons */ 5989 // If user explicitly wants a reload 5990 if (this._options.reload) 5991 return undefined; 5992 // If any states are exiting or entering 5993 if (tc.exiting.length || tc.entering.length) 5994 return undefined; 5995 // If to/from path lengths differ 5996 if (tc.to.length !== tc.from.length) 5997 return undefined; 5998 // If the to/from paths are different 5999 var pathsDiffer = arrayTuples(tc.to, tc.from) 6000 .map(function (tuple) { return tuple[0].state !== tuple[1].state; }) 6001 .reduce(anyTrueR, false); 6002 if (pathsDiffer) 6003 return undefined; 6004 // Find any parameter values that differ 6005 var nodeSchemas = tc.to.map(function (node) { return node.paramSchema; }); 6006 var _a = [tc.to, tc.from].map(function (path) { return path.map(function (x) { return x.paramValues; }); }), toValues = _a[0], fromValues = _a[1]; 6007 var tuples = arrayTuples(nodeSchemas, toValues, fromValues); 6008 return tuples.map(function (_a) { 6009 var schema = _a[0], toVals = _a[1], fromVals = _a[2]; 6010 return Param.changed(schema, toVals, fromVals); 6011 }).reduce(unnestR, []); 6012 }; 6013 /** 6014 * Returns true if the transition is dynamic. 6015 * 6016 * A transition is dynamic if no states are entered nor exited, but at least one dynamic parameter has changed. 6017 * 6018 * @returns true if the Transition is dynamic 6019 */ 6020 Transition.prototype.dynamic = function () { 6021 var changes = this._changedParams(); 6022 return !changes ? false : changes.map(function (x) { return x.dynamic; }).reduce(anyTrueR, false); 6023 }; 6024 /** 6025 * Returns true if the transition is ignored. 6026 * 6027 * A transition is ignored if no states are entered nor exited, and no parameter values have changed. 6028 * 6029 * @returns true if the Transition is ignored. 6030 */ 6031 Transition.prototype.ignored = function () { 6032 return !!this._ignoredReason(); 6033 }; 6034 /** @internal */ 6035 Transition.prototype._ignoredReason = function () { 6036 var pending = this.router.globals.transition; 6037 var reloadState = this._options.reloadState; 6038 var same = function (pathA, pathB) { 6039 if (pathA.length !== pathB.length) 6040 return false;
6041 var matching = PathUtils.matching(pathA, pathB); 6042 return pathA.length === matching.filter(function (node) { return !reloadState || !node.state.includes[reloadState.name]; }).length; 6043 }; 6044 var newTC = this.treeChanges(); 6045 var pendTC = pending && pending.treeChanges(); 6046 if (pendTC && same(pendTC.to, newTC.to) && same(pendTC.exiting, newTC.exiting)) 6047 return 'SameAsPending'; 6048 if (newTC.exiting.length === 0 && newTC.entering.length === 0 && same(newTC.from, newTC.to)) 6049 return 'SameAsCurrent'; 6050 }; 6051 /** 6052 * Runs the transition 6053 * 6054 * This method is generally called from the [[StateService.transitionTo]] 6055 * 6056 * @internal 6057 * 6058 * @returns a promise for a successful transition. 6059 */ 6060 Transition.prototype.run = function () { 6061 var _this = this; 6062 var runAllHooks = TransitionHook.runAllHooks; 6063 // Gets transition hooks array for the given phase 6064 var getHooksFor = function (phase) { return _this._hookBuilder.buildHooksForPhase(phase); }; 6065 // When the chain is complete, then resolve or reject the deferred 6066 var transitionSuccess = function () { 6067 trace.traceSuccess(_this.$to(), _this); 6068 _this.success = true; 6069 _this._deferred.resolve(_this.to()); 6070 runAllHooks(getHooksFor(exports.TransitionHookPhase.SUCCESS)); 6071 }; 6072 var transitionError = function (reason) { 6073 trace.traceError(reason, _this); 6074 _this.success = false; 6075 _this._deferred.reject(reason); 6076 _this._error = reason; 6077 runAllHooks(getHooksFor(exports.TransitionHookPhase.ERROR)); 6078 }; 6079 var runTransition = function () { 6080 // Wait to build the RUN hook chain until the BEFORE hooks are done 6081 // This allows a BEFORE hook to dynamically add additional RUN hooks via the Transition object. 6082 var allRunHooks = getHooksFor(exports.TransitionHookPhase.RUN); 6083 var done = function () { return services.$q.when(undefined); }; 6084 return TransitionHook.invokeHooks(allRunHooks, done); 6085 }; 6086 var startTransition = function () { 6087 var globals = _this.router.globals; 6088 globals.lastStartedTransitionId = _this.$id; 6089 globals.transition = _this; 6090 globals.transitionHistory.enqueue(_this); 6091 trace.traceTransitionStart(_this); 6092 return services.$q.when(undefined); 6093 }; 6094 var allBeforeHooks = getHooksFor(exports.TransitionHookPhase.BEFORE); 6095 TransitionHook.invokeHooks(allBeforeHooks, startTransition) 6096 .then(runTransition) 6097 .then(transitionSuccess, transitionError); 6098 return this.promise; 6099 }; 6100 /** 6101 * Checks if the Transition is valid 6102 * 6103 * @returns true if the Transition is valid 6104 */ 6105 Transition.prototype.valid = function () { 6106 return !this.error() || this.success !== undefined; 6107 }; 6108 /** 6109 * Aborts this transition 6110 * 6111 * Imperative API to abort a Transition. 6112 * This only applies to Transitions that are not yet complete. 6113 */ 6114 Transition.prototype.abort = function () { 6115 // Do not set flag if the transition is already complete 6116 if (isUndefined(this.success)) { 6117 this._aborted = true; 6118 } 6119 }; 6120 /** 6121 * The Transition error reason. 6122 * 6123 * If the transition is invalid (and could not be run), returns the reason the transition is invalid. 6124 * If the transition was valid and ran, but was not successful, returns the reason the transition failed. 6125 * 6126 * @returns a transition rejection explaining why the transition is invalid, or the reason the transition failed. 6127 */ 6128 Transition.prototype.error = function () { 6129 var state = this.$to(); 6130 if (state.self.abstract) { 6131 return Rejection.invalid("Cannot transition to abstract state '" + state.name + "'"); 6132 } 6133 var paramDefs = state.parameters(); 6134 var values = this.params(); 6135 var invalidParams = paramDefs.filter(function (param) { return !param.validates(values[param.id]); }); 6136 if (invalidParams.length) { 6137 var invalidValues = invalidParams.map(function (param) { return "[" + param.id + ":" + stringify(values[param.id]) + "]"; }).join(', '); 6138 var detail = "The following parameter values are not valid for state '" + state.name + "': " + invalidValues; 6139 return Rejection.invalid(detail); 6140 } 6141 if (this.success === false) 6142 return this._error; 6143 }; 6144 /** 6145 * A string representation of the Transition 6146 * 6147 * @returns A string representation of the Transition 6148 */ 6149 Transition.prototype.toString = function () { 6150 var fromStateOrName = this.from(); 6151 var toStateOrName = this.to(); 6152 var avoidEmptyHash = function (params) { 6153 return params['#'] !== null && params['#'] !== undefined ? params : omit(params, ['#']); 6154 };
6155 // (X) means the to state is invalid. 6156 var id = this.$id, from = isObject(fromStateOrName) ? fromStateOrName.name : fromStateOrName, fromParams = stringify(avoidEmptyHash(this._treeChanges.from.map(prop('paramValues')).reduce(mergeR, {}))), toValid = this.valid() ? '' : '(X) ', to = isObject(toStateOrName) ? toStateOrName.name : toStateOrName, toParams = stringify(avoidEmptyHash(this.params())); 6157 return "Transition#" + id + "( '" + from + "'" + fromParams + " -> " + toValid + "'" + to + "'" + toParams + " )"; 6158 }; 6159 /** @internal */ 6160 Transition.diToken = Transition; 6161 return Transition; 6162 }()); 6163 6164 function quoteRegExp(str, param) { 6165 var surroundPattern = ['', ''], result = str.replace(/[\\\[\]\^$*+?.()|{}]/g, '\\$&'); 6166 if (!param) 6167 return result; 6168 switch (param.squash) { 6169 case false: 6170 surroundPattern = ['(', ')' + (param.isOptional ? '?' : '')]; 6171 break; 6172 case true: 6173 result = result.replace(/\/$/, ''); 6174 surroundPattern = ['(?:/(', ')|/)?']; 6175 break; 6176 default: 6177 surroundPattern = ["(" + param.squash + "|", ')?']; 6178 break; 6179 } 6180 return result + surroundPattern[0] + param.type.pattern.source + surroundPattern[1]; 6181 } 6182 var memoizeTo = function (obj, _prop, fn) { return (obj[_prop] = obj[_prop] || fn()); }; 6183 var splitOnSlash = splitOnDelim('/'); 6184 var defaultConfig = { 6185 state: { params: {} }, 6186 strict: true, 6187 caseInsensitive: true, 6188 decodeParams: true, 6189 }; 6190 /** 6191 * Matches URLs against patterns. 6192 * 6193 * Matches URLs against patterns and extracts named parameters from the path or the search 6194 * part of the URL. 6195 * 6196 * A URL pattern consists of a path pattern, optionally followed by '?' and a list of search (query) 6197 * parameters. Multiple search parameter names are separated by '&'. Search parameters 6198 * do not influence whether or not a URL is matched, but their values are passed through into 6199 * the matched parameters returned by [[UrlMatcher.exec]]. 6200 * 6201 * - *Path parameters* are defined using curly brace placeholders (`/somepath/{param}`) 6202 * or colon placeholders (`/somePath/:param`). 6203 * 6204 * - *A parameter RegExp* may be defined for a param after a colon 6205 * (`/somePath/{param:[a-zA-Z0-9]+}`) in a curly brace placeholder. 6206 * The regexp must match for the url to be matched. 6207 * Should the regexp itself contain curly braces, they must be in matched pairs or escaped with a backslash. 6208 * 6209 * Note: a RegExp parameter will encode its value using either [[ParamTypes.path]] or [[ParamTypes.query]]. 6210 * 6211 * - *Custom parameter types* may also be specified after a colon (`/somePath/{param:int}`) in curly brace parameters. 6212 * See [[UrlMatcherFactory.type]] for more information. 6213 * 6214 * - *Catch-all parameters* are defined using an asterisk placeholder (`/somepath/*catchallparam`). 6215 * A catch-all * parameter value will contain the remainder of the URL. 6216 * 6217 * --- 6218 * 6219 * Parameter names may contain only word characters (latin letters, digits, and underscore) and 6220 * must be unique within the pattern (across both path and search parameters). 6221 * A path parameter matches any number of characters other than '/'. For catch-all 6222 * placeholders the path parameter matches any number of characters. 6223 * 6224 * Examples: 6225 * 6226 * * `'/hello/'` - Matches only if the path is exactly '/hello/'. There is no special treatment for 6227 * trailing slashes, and patterns have to match the entire path, not just a prefix. 6228 * * `'/user/:id'` - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or 6229 * '/user/bob/details'. The second path segment will be captured as the parameter 'id'. 6230 * * `'/user/{id}'` - Same as the previous example, but using curly brace syntax. 6231 * * `'/user/{id:[^/]*}'` - Same as the previous example. 6232 * * `'/user/{id:[0-9a-fA-F]{1,8}}'` - Similar to the previous example, but only matches if the id 6233 * parameter consists of 1 to 8 hex digits. 6234 * * `'/files/{path:.*}'` - Matches any URL starting with '/files/' and captures the rest of the 6235 * path into the parameter 'path'. 6236 * * `'/files/*path'` - ditto. 6237 * * `'/calendar/{start:date}'` - Matches "/calendar/2014-11-12" (because the pattern defined 6238 * in the built-in `date` ParamType matches `2014-11-12`) and provides a Date object in $stateParams.start 6239 * 6240 */ 6241 var UrlMatcher = /** @class */ (function () { 6242 /** 6243 * @param pattern The pattern to compile into a matcher. 6244 * @param paramTypes The [[ParamTypes]] registry 6245 * @param paramFactory A [[ParamFactory]] object 6246 * @param config A [[UrlMatcherCompileConfig]] configuration object 6247 */ 6248 function UrlMatcher(pattern, paramTypes, paramFactory, config) { 6249 var _this = this; 6250 /** @internal */ 6251 this._cache = { path: [this] }; 6252 /** @internal */ 6253 this._children = []; 6254 /** @internal */ 6255 this._params = []; 6256 /** @internal */ 6257 this._segments = []; 6258 /** @internal */ 6259 this._compiled = []; 6260 this.config = config = defaults(config, defaultConfig); 6261 this.pattern = pattern; 6262 // Find all placeholders and create a compiled pattern, using either classic or curly syntax: 6263 // '*' name 6264 // ':' name 6265 // '{' name '}' 6266 // '{' name ':' regexp '}' 6267 // The regular expression is somewhat complicated due to the need to allow curly braces 6268 // inside the regular expression. The placeholder regexp breaks down as follows: 6269 // ([:*])([\w\[\]]+) - classic placeholder ($1 / $2) (search version has - for snake-case) 6270 // \{([\w\[\]]+)(?:\:\s*( ... ))?\} - curly brace placeholder ($3) with optional regexp/type ... ($4) (search version has - for snake-case 6271 // (?: ... | ... | ... )+ - the regexp consists of any number of atoms, an atom being either 6272 // [^{}\\]+ - anything other than curly braces or backslash 6273 // \\. - a backslash escape 6274 // \{(?:[^{}\\]+|\\.)*\} - a matched set of curly braces containing other atoms 6275 var placeholder = /([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g; 6276 var searchPlaceholder = /([:]?)([\w\[\].-]+)|\{([\w\[\].-]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g; 6277 var patterns = []; 6278 var last = 0; 6279 var matchArray; 6280 var checkParamErrors = function (id) { 6281 if (!UrlMatcher.nameValidator.test(id)) 6282 throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'"); 6283 if (find(_this._params, propEq('id', id))) 6284 throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'"); 6285 }; 6286 // Split into static segments separated by path parameter placeholders. 6287 // The number of segments is always 1 more than the number of parameters. 6288 var matchDetails = function (m, isSearch) { 6289 // IE[78] returns '' for unmatched groups instead of null 6290 var id = m[2] || m[3]; 6291 var regexp = isSearch ? m[4] : m[4] || (m[1] === '*' ? '[\\s\\S]*' : null); 6292 var makeRegexpType = function (str) { 6293 return inherit(paramTypes.type(isSearch ? 'query' : 'path'), { 6294 pattern: new RegExp(str, _this.config.caseInsensitive ? 'i' : undefined), 6295 }); 6296 }; 6297 return { 6298 id: id, 6299 regexp: regexp, 6300 segment: pattern.substring(last, m.index), 6301 type: !regexp ? null : paramTypes.type(regexp) || makeRegexpType(regexp), 6302 }; 6303 }; 6304 var details; 6305 var segment; 6306 // tslint:disable-next-line:no-conditional-assignment 6307 while ((matchArray = placeholder.exec(pattern))) { 6308 details = matchDetails(matchArray, false); 6309 if (details.segment.indexOf('?') >= 0) 6310 break; // we're into the search part
6311 checkParamErrors(details.id); 6312 this._params.push(paramFactory.fromPath(details.id, details.type, config.state)); 6313 this._segments.push(details.segment); 6314 patterns.push([details.segment, tail(this._params)]); 6315 last = placeholder.lastIndex; 6316 } 6317 segment = pattern.substring(last); 6318 // Find any search parameter names and remove them from the last segment 6319 var i = segment.indexOf('?'); 6320 if (i >= 0) { 6321 var search = segment.substring(i); 6322 segment = segment.substring(0, i); 6323 if (search.length > 0) { 6324 last = 0; 6325 // tslint:disable-next-line:no-conditional-assignment 6326 while ((matchArray = searchPlaceholder.exec(search))) { 6327 details = matchDetails(matchArray, true); 6328 checkParamErrors(details.id); 6329 this._params.push(paramFactory.fromSearch(details.id, details.type, config.state)); 6330 last = placeholder.lastIndex; 6331 // check if ?& 6332 } 6333 } 6334 } 6335 this._segments.push(segment); 6336 this._compiled = patterns.map(function (_pattern) { return quoteRegExp.apply(null, _pattern); }).concat(quoteRegExp(segment)); 6337 } 6338 /** @internal */ 6339 UrlMatcher.encodeDashes = function (str) { 6340 // Replace dashes with encoded "\-" 6341 return encodeURIComponent(str).replace(/-/g, function (c) { return "%5C%" + c.charCodeAt(0).toString(16).toUpperCase(); }); 6342 }; 6343 /** @internal Given a matcher, return an array with the matcher's path segments and path params, in order */ 6344 UrlMatcher.pathSegmentsAndParams = function (matcher) { 6345 var staticSegments = matcher._segments; 6346 var pathParams = matcher._params.filter(function (p) { return p.location === exports.DefType.PATH; }); 6347 return arrayTuples(staticSegments, pathParams.concat(undefined)) 6348 .reduce(unnestR, []) 6349 .filter(function (x) { return x !== '' && isDefined(x); }); 6350 }; 6351 /** @internal Given a matcher, return an array with the matcher's query params */ 6352 UrlMatcher.queryParams = function (matcher) { 6353 return matcher._params.filter(function (p) { return p.location === exports.DefType.SEARCH; }); 6354 }; 6355 /** 6356 * Compare two UrlMatchers 6357 * 6358 * This comparison function converts a UrlMatcher into static and dynamic path segments. 6359 * Each static path segment is a static string between a path separator (slash character). 6360 * Each dynamic segment is a path parameter. 6361 * 6362 * The comparison function sorts static segments before dynamic ones. 6363 */ 6364 UrlMatcher.compare = function (a, b) { 6365 /** 6366 * Turn a UrlMatcher and all its parent matchers into an array 6367 * of slash literals '/', string literals, and Param objects 6368 * 6369 * This example matcher matches strings like "/foo/:param/tail": 6370 * var matcher = $umf.compile("/foo").append($umf.compile("/:param")).append($umf.compile("/")).append($umf.compile("tail")); 6371 * var result = segments(matcher); // [ '/', 'foo', '/', Param, '/', 'tail' ] 6372 * 6373 * Caches the result as `matcher._cache.segments` 6374 */ 6375 var segments = function (matcher) { 6376 return (matcher._cache.segments = 6377 matcher._cache.segments || 6378 matcher._cache.path 6379 .map(UrlMatcher.pathSegmentsAndParams) 6380 .reduce(unnestR, []) 6381 .reduce(joinNeighborsR, []) 6382 .map(function (x) { return (isString(x) ? splitOnSlash(x) : x); }) 6383 .reduce(unnestR, [])); 6384 }; 6385 /** 6386 * Gets the sort weight for each segment of a UrlMatcher 6387 * 6388 * Caches the result as `matcher._cache.weights` 6389 */ 6390 var weights = function (matcher) { 6391 return (matcher._cache.weights = 6392 matcher._cache.weights || 6393 segments(matcher).map(function (segment) { 6394 // Sort slashes first, then static strings, the Param
6394s 6395 if (segment === '/') 6396 return 1; 6397 if (isString(segment)) 6398 return 2; 6399 if (segment instanceof Param) 6400 return 3; 6401 })); 6402 }; 6403 /** 6404 * Pads shorter array in-place (mutates) 6405 */ 6406 var padArrays = function (l, r, padVal) { 6407 var len = Math.max(l.length, r.length); 6408 while (l.length < len) 6409 l.push(padVal); 6410 while (r.length < len) 6411 r.push(padVal); 6412 }; 6413 var weightsA = weights(a), weightsB = weights(b); 6414 padArrays(weightsA, weightsB, 0); 6415 var _pairs = arrayTuples(weightsA, weightsB); 6416 var cmp, i; 6417 for (i = 0; i < _pairs.length; i++) { 6418 cmp = _pairs[i][0] - _pairs[i][1]; 6419 if (cmp !== 0) 6420 return cmp; 6421 } 6422 return 0; 6423 }; 6424 /** 6425 * Creates a new concatenated UrlMatcher 6426 * 6427 * Builds a new UrlMatcher by appending another UrlMatcher to this one. 6428 * 6429 * @param url A `UrlMatcher` instance to append as a child of the current `UrlMatcher`. 6430 */ 6431 UrlMatcher.prototype.append = function (url) { 6432 this._children.push(url); 6433 url._cache = { 6434 path: this._cache.path.concat(url), 6435 parent: this, 6436 pattern: null, 6437 }; 6438 return url; 6439 }; 6440 /** @internal */ 6441 UrlMatcher.prototype.isRoot = function () { 6442 return this._cache.path[0] === this; 6443 }; 6444 /** Returns the input pattern string */ 6445 UrlMatcher.prototype.toString = function () { 6446 return this.pattern; 6447 }; 6448 UrlMatcher.prototype._getDecodedParamValue = function (value, param) { 6449 if (isDefined(value)) { 6450 if (this.config.decodeParams && !param.type.raw) { 6451 if (isArray(value)) { 6452 value = value.map(function (paramValue) { return decodeURIComponent(paramValue); }); 6453 } 6454 else { 6455 value = decodeURIComponent(value); 6456 } 6457 } 6458 value = param.type.decode(value); 6459 } 6460 return param.value(value); 6461 }; 6462 /** 6463 * Tests the specified url/path against this matcher. 6464 * 6465 * Tests if the given url matches this matcher's pattern, and returns an object c
6465ontaining the captured 6466 * parameter values. Returns null if the path does not match. 6467 * 6468 * The returned object contains the values 6469 * of any search parameters that are mentioned in the pattern, but their value may be null if 6470 * they are not present in `search`. This means that search parameters are always treated 6471 * as optional. 6472 * 6473 * #### Example: 6474 * ```js 6475 * new UrlMatcher('/user/{id}?q&r').exec('/user/bob', { 6476 * x: '1', q: 'hello' 6477 * }); 6478 * // returns { id: 'bob', q: 'hello', r: null } 6479 * ``` 6480 * 6481 * @param path The URL path to match, e.g. `$location.path()`. 6482 * @param search URL search parameters, e.g. `$location.search()`. 6483 * @param hash URL hash e.g. `$location.hash()`. 6484 * @param options 6485 * 6486 * @returns The captured parameter values. 6487 */ 6488 UrlMatcher.prototype.exec = function (path, search, hash, options) { 6489 var _this = this; 6490 if (search === void 0) { search = {}; } 6491 var match = memoizeTo(this._cache, 'pattern', function () { 6492 return new RegExp([ 6493 '^', 6494 unnest(_this._cache.path.map(prop('_compiled'))).join(''), 6495 _this.config.strict === false ? '/?' : '', 6496 '$', 6497 ].join(''), _this.config.caseInsensitive ? 'i' : undefined); 6498 }).exec(path); 6499 if (!match) 6500 return null; 6501 // options = defaults(options, { isolate: false }); 6502 var allParams = this.parameters(), pathParams = allParams.filter(function (param) { return !param.isSearch(); }), searchParams = allParams.filter(function (param) { return param.isSearch(); }), nPathSegments = this._cache.path.map(function (urlm) { return urlm._segments.length - 1; }).reduce(function (a, x) { return a + x; }), values = {}; 6503 if (nPathSegments !== match.length - 1) 6504 throw new Error("Unbalanced capture group in route '" + this.pattern + "'"); 6505 function decodePathArray(paramVal) { 6506 var reverseString = function (str) { return str.split('').reverse().join(''); }; 6507 var unquoteDashes = function (str) { return str.replace(/\\-/g, '-'); }; 6508 var split = reverseString(paramVal).split(/-(?!\\)/); 6509 var allReversed = map(split, reverseString); 6510 return map(allReversed, unquoteDashes).reverse(); 6511 } 6512 for (var i = 0; i < nPathSegments; i++) { 6513 var param = pathParams[i]; 6514 var value = match[i + 1]; 6515 // if the param value matches a pre-replace pair, replace the value before decoding. 6516 for (var j = 0; j < param.replace.length; j++) { 6517 if (param.replace[j].from === value) 6518 value = param.replace[j].to; 6519 } 6520 if (value && param.array === true) 6521 value = decodePathArray(value); 6522 values[param.id] = this._getDecodedParamValue(value, param); 6523 } 6524 searchParams.forEach(function (param) { 6525 var value = search[param.id]; 6526 for (var j = 0; j < param.replace.length; j++) { 6527 if (param.replace[j].from === value) 6528 value = param.replace[j].to; 6529 } 6530 values[param.id] = _this._getDecodedParamValue(value, param); 6531 }); 6532 if (hash) 6533 values['#'] = hash; 6534 return values; 6535 }; 6536 /** 6537 * @internal 6538 * Returns all the [[Param]] objects of all path and search parameters of this pattern in order of appearance. 6539 * 6540 * @returns {Array.<Param>} An array of [[Param]] objects. Must be treated as read-only. If the 6541 * pattern has no parameters, an empty array is returned. 6542 */ 6543 UrlMatcher.prototype.parameters = function (opts) { 6544 if (opts === void 0) { opts = {}; } 6545 if (opts.inherit === false) 6546 return this._params; 6547 return unnest(this._cache.path.map(function (matcher) { return matcher._params; })); 6548 }; 6549 /** 6550 * @internal 6551 * Returns a single parameter from this UrlMatcher by id 6552 * 6553 * @param id 6554 * @param opts 6555 * @returns {T|Param|any|boolean|UrlMatcher|null} 6556 */ 6557 UrlMatcher.prototype.parameter = function (id, opts) { 6558 var _this = this; 6559 if (opts === void 0) { opts = {}; } 6560 var findParam = function () { 6561 for (var _i = 0, _a = _this._params; _i < _a.length; _i++) { 6562 var param = _a[_i]; 6563 if (param.id === id) 6564 return param; 6565 } 6566 }; 6567 var parent = this._cache.parent; 6568 return findParam() || (opts.inherit !== false && parent && parent.parameter(id, opts)) || null; 6569 }; 6570 /** 6571 * Validates the input parameter values against this UrlMatcher 6572 * 6573 * Checks an object hash of parameters to validate their correctness according to the parameter 6574 * types of this `UrlMatcher`. 6575 * 6576 * @param params The object hash of parameters to validate. 6577 * @returns Returns `true` if `params` validates, otherwise `false`. 6578 */ 6579 UrlMatcher.prototype.validates = function (params) {
6580 var validParamVal = function (param, val) { return !param || param.validates(val); }; 6581 params = params || {}; 6582 // I'm not sure why this checks only the param keys passed in, and not all the params known to the matcher 6583 var paramSchema = this.parameters().filter(function (paramDef) { return params.hasOwnProperty(paramDef.id); }); 6584 return paramSchema.map(function (paramDef) { return validParamVal(paramDef, params[paramDef.id]); }).reduce(allTrueR, true); 6585 }; 6586 /** 6587 * Given a set of parameter values, creates a URL from this UrlMatcher. 6588 * 6589 * Creates a URL that matches this pattern by substituting the specified values 6590 * for the path and search parameters. 6591 * 6592 * #### Example: 6593 * ```js 6594 * new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' }); 6595 * // returns '/user/bob?q=yes' 6596 * ``` 6597 * 6598 * @param values the values to substitute for the parameters in this pattern. 6599 * @returns the formatted URL (path and optionally search part). 6600 */ 6601 UrlMatcher.prototype.format = function (values) { 6602 if (values === void 0) { values = {}; } 6603 // Build the full path of UrlMatchers (including all parent UrlMatchers) 6604 var urlMatchers = this._cache.path; 6605 // Extract all the static segments and Params (processed as ParamDetails) 6606 // into an ordered array 6607 var pathSegmentsAndParams = urlMatchers 6608 .map(UrlMatcher.pathSegmentsAndParams) 6609 .reduce(unnestR, []) 6610 .map(function (x) { return (isString(x) ? x : getDetails(x)); }); 6611 // Extract the query params into a separate array 6612 var queryParams = urlMatchers 6613 .map(UrlMatcher.queryParams) 6614 .reduce(unnestR, []) 6615 .map(getDetails); 6616 var isInvalid = function (param) { return param.isValid === false; }; 6617 if (pathSegmentsAndParams.concat(queryParams).filter(isInvalid).length) { 6618 return null; 6619 } 6620 /** 6621 * Given a Param, applies the parameter value, then returns detailed information about it 6622 */ 6623 function getDetails(param) { 6624 // Normalize to typed value 6625 var value = param.value(values[param.id]); 6626 var isValid = param.validates(value); 6627 var isDefaultValue = param.isDefaultValue(value); 6628 // Check if we're in squash mode for the parameter 6629 var squash = isDefaultValue ? param.squash : false; 6630 // Allow the Parameter's Type to encode the value 6631 var encoded = param.type.encode(value); 6632 return { param: param, value: value, isValid: isValid, isDefaultValue: isDefaultValue, squash: squash, encoded: encoded }; 6633 } 6634 // Build up the path-portion from the list of static segments and parameters 6635 var pathString = pathSegmentsAndParams.reduce(function (acc, x) { 6636 // The element is a static segment (a raw string); just append it 6637 if (isString(x)) 6638 return acc + x; 6639 // Otherwise, it's a ParamDetails. 6640 var squash = x.squash, encoded = x.encoded, param = x.param; 6641 // If squash is === true, try to remove a slash from the path 6642 if (squash === true) 6643 return acc.match(/\/$/) ? acc.slice(0, -1) : acc; 6644 // If squash is a string, use the string for the param value 6645 if (isString(squash)) 6646 return acc + squash; 6647 if (squash !== false) 6648 return acc; // ? 6649 if (encoded == null) 6650 return acc; 6651 // If this parameter value is an array, encode the value using encodeDashes 6652 if (isArray(encoded)) 6653 return acc + map(encoded, UrlMatcher.encodeDashes).join('-'); 6654 // If the parameter type is "raw", then do not encodeURIComponent 6655 if (param.raw) 6656 return acc + encoded; 6657 // Encode the value 6658 return acc + encodeURIComponent(encoded); 6659 }, ''); 6660 // Build the query string by applying parameter values (array or regular) 6661 // then mapping to key=value, then flattening and joining using "&" 6662 var queryString = queryParams 6663 .map(function (paramDetails) { 6664 var param = paramDetails.param, squash = paramDetails.squash, encoded = paramDetails.encoded, isDefaultValue = paramDetails.isDefaultValue; 6665 if (encoded == null || (isDefaultValue && squash !== false)) 6666 return; 6667 if (!isArray(encoded)) 6668 encoded = [encoded]; 6669 if (encoded.length === 0) 6670 return; 6671 if (!param.raw) 6672 encoded = map(encoded, encodeURIComponent); 6673 return encoded.map(function (val) { return param.id + "=" + val; }); 6674 }) 6675 .filter(identity) 6676 .reduce(unnestR, []) 6677 .join('&'); 6678 // Concat the pathstring with the queryString (if exists) and the hashString (if exists) 6679 return pathString + (queryString ? "?" + queryString : '') + (values['#'] ? '#' + values['#'] : ''); 6680 }; 6681 /** @internal */ 6682 UrlMatcher.nameValidator = /^\w+([-.]+\w+)*(?:\[\])?$/; 6683 return UrlMatcher; 6684 }()); 6685 6686 var __assign = (undefined && undefined.__assign) || function () { 6687 __assign = Object.assign || function(t) { 6688 for (var s, i = 1, n = arguments.length; i < n; i++) { 6689 s = arguments[i]; 6690 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) 6691 t[p] = s[p]; 6692 } 6693 return t; 6694 }; 6695 return __assign.apply(this, arguments); 6696 }; 6697 var ParamFactory = /** @class */ (function () { 6698 function ParamFactory(router) { 6699 this.router = router; 6700 } 6701 ParamFactory.prototype.fromConfig = function (id, type, state) { 6702 return new Param(id, type, exports.DefType.CONFIG, this.router.urlService.config, state); 6703 }; 6704 ParamFactory.prototype.fromPath = function (id, type, state) { 6705 return new Param(id, type, exports.DefType.PATH, this.router.urlService.config, state); 6706 }; 6707 ParamFactory.prototype.fromSearch = function (id, type, state) { 6708 return new Param(id, type, exports.DefType.SEARCH, this.router.urlService.config, state); 6709 }; 6710 return ParamFactory; 6711 }()); 6712 /** 6713 * Factory for [[UrlMatcher]] instances. 6714 * 6715 * The factory is available to ng1 services as 6716 * `$urlMatcherFactory` or ng1 providers as `$urlMatcherFactoryProvi
6716der`. 6717 */ 6718 var UrlMatcherFactory = /** @class */ (function () { 6719 // TODO: move implementations to UrlConfig (urlService.config) 6720 function UrlMatcherFactory(/** @internal */ router) { 6721 var _this = this; 6722 this.router = router; 6723 /** Creates a new [[Param]] for a given location (DefType) */ 6724 this.paramFactory = new ParamFactory(this.router); 6725 // TODO: Check if removal of this will break anything, then remove these 6726 this.UrlMatcher = UrlMatcher; 6727 this.Param = Param; 6728 /** @deprecated use [[UrlConfig.caseInsensitive]] */ 6729 this.caseInsensitive = function (value) { return _this.router.urlService.config.caseInsensitive(value); }; 6730 /** @deprecated use [[UrlConfig.defaultSquashPolicy]] */ 6731 this.defaultSquashPolicy = function (value) { return _this.router.urlService.config.defaultSquashPolicy(value); }; 6732 /** @deprecated use [[UrlConfig.strictMode]] */ 6733 this.strictMode = function (value) { return _this.router.urlService.config.strictMode(value); }; 6734 /** @deprecated use [[UrlConfig.type]] */ 6735 this.type = function (name, definition, definitionFn) { 6736 return _this.router.urlService.config.type(name, definition, definitionFn) || _this; 6737 }; 6738 } 6739 /** 6740 * Creates a [[UrlMatcher]] for the specified pattern. 6741 * 6742 * @param pattern The URL pattern. 6743 * @param config The config object hash. 6744 * @returns The UrlMatcher. 6745 */ 6746 UrlMatcherFactory.prototype.compile = function (pattern, config) { 6747 var urlConfig = this.router.urlService.config; 6748 // backward-compatible support for config.params -> config.state.params 6749 var params = config && !config.state && config.params; 6750 config = params ? __assign({ state: { params: params } }, config) : config; 6751 var globalConfig = { 6752 strict: urlConfig._isStrictMode, 6753 caseInsensitive: urlConfig._isCaseInsensitive, 6754 decodeParams: urlConfig._decodeParams, 6755 }; 6756 return new UrlMatcher(pattern, urlConfig.paramTypes, this.paramFactory, extend(globalConfig, config)); 6757 }; 6758 /** 6759 * Returns true if the specified object is a [[UrlMatcher]], or false otherwise. 6760 * 6761 * @param object The object to perform the type check against. 6762 * @returns `true` if the object matches the `UrlMatcher` interface, by 6763 * implementing all the same methods. 6764 */ 6765 UrlMatcherFactory.prototype.isMatcher = function (object) { 6766 // TODO: typeof? 6767 if (!isObject(object)) 6768 return false; 6769 var result = true; 6770 forEach(UrlMatcher.prototype, function (val, name) { 6771 if (isFunction(val)) 6772 result = result && isDefined(object[name]) && isFunction(object[name]); 6773 }); 6774 return result; 6775 }; 6776 /** @internal */ 6777 UrlMatcherFactory.prototype.$get = function () { 6778 var urlConfig = this.router.urlService.config; 6779 urlConfig.paramTypes.enqueue = false; 6780 urlConfig.paramTypes._flushTypeQueue(); 6781 return this; 6782 }; 6783 return UrlMatcherFactory; 6784 }()); 6785 6786 /** 6787 * Creates a [[UrlRule]] 6788 * 6789 * Creates a [[UrlRule]] from a: 6790 * 6791 * - `string` 6792 * - [[UrlMatcher]] 6793 * - `RegExp` 6794 * - [[StateObject]] 6795 */ 6796 var UrlRuleFactory = /** @class */ (function () { 6797 function UrlRuleFactory(router) { 6798 this.router = router; 6799 } 6800 UrlRuleFactory.prototype.compile = function (str) { 6801 return this.router.urlMatcherFactory.compile(str); 6802 }; 6803 UrlRuleFactory.prototype.create = function (what, handler) { 6804 var _this = this; 6805 var isState = StateObject.isState, isStateDeclaration = StateObject.isStateDeclaration; 6806 var makeRule = pattern([ 6807 [isString, function (_what) { return makeRule(_this.compile(_what)); }], 6808 [is(UrlMatcher), function (_what) { return _this.fromUrlMatcher(_what, handler); }], 6809 [or(isState, isStateDeclaration), function (_what) { return _this.fromState(_what, _this.router); }], 6810 [is(RegExp), function (_what) { return _this.fromRegExp(_what, handler); }], 6811 [isFunction, function (_what) { return new BaseUrlRule(_what, handler); }], 6812 ]); 6813 var rule = makeRule(what); 6814 if (!rule) 6815 throw new Error("invalid 'what' in when()"); 6816 return rule; 6817 }; 6818 /** 6819 * A UrlRule which matches based on a UrlMatcher 6820 * 6821 * The `handler` may be either a `string`, a [[UrlRuleHandlerFn]] or another [[UrlMatcher]] 6822 * 6823 * ## Handler as a function 6824 * 6825 * If `handler` is a function, the function is invoked with: 6826 * 6827 * - matched parameter values ([[RawParams]] from [[UrlMatcher.exec]]) 6828 * - url: the current Url ([[UrlParts]]) 6829 * - router: the router object ([[UIRouter]]) 6830 * 6831 * #### Example: 6832 * ```js 6833 * var urlMatcher = $umf.compile("/foo/:fooId/:barId"); 6834 * var rule = factory.fromUrlMatcher(urlMatcher, match => "/home/" + match.fooId + "/" + match.barId); 6835 * var match = rule.match('/foo/123/456'); // results in { fooId: '123', barId: '456' } 6836 * var result = rule.handler(match); // '/home/123/456' 6837 * ``` 6838 * 6839 * ## Handler as UrlMatcher 6840 * 6841 * If `handler` is a UrlMatcher, the handler matcher is used to create the new url. 6842 * The `handler` UrlMatcher is formatted using the matched param from the first matcher. 6843 * The url is replaced with the result. 6844 * 6845 * #### Example: 6846 * ```js 6847 * var urlMatcher = $umf.compile("/foo/:fooId/:barId"); 6848 * var handler = $umf.compile("/home/:fooId/:barId"); 6849 * var rule = factory.fromUrlMatcher(urlMatcher, handler); 6850 * var match = rule.match('/foo/123/456'); // results in { fooId: '123', barId: '456' } 6851 * var result = rule.handler(match); // '/home/123/456' 6852 * ``` 6853 */ 6854 UrlRuleFactory.prototype.fromUrlMatcher = function (urlMatcher, handler) { 6855 var _handler = handler; 6856 if (isString(handler)) 6857 handler = this.router.urlMatcherFactory.compile(handler); 6858 if (is(UrlMatcher)(handler)) 6859 _handler = function (match) { return handler.format(match); }; 6860 function matchUrlParamters(url) { 6861 var params = urlMatcher.exec(url.path, url.search, url.hash); 6862 return urlMatcher.validates(params) && params; 6863 } 6864 // Prioritize URLs, lowest to highest: 6865 // - Some optional URL parameters, but none matched 6866 // - No optional parameters in URL 6867 // - Some optional parameters, some matched 6868 // - Some optional parameters, all matched 6869 function matchPriority(params) { 6870 var optional = urlMatcher.parameters().filter(function (param) { return param.isOptional; }); 6871 if (!optional.length) 6872 return 0.000001; 6873 var matched = optional.filter(function (param) { return params[param.id]; }); 6874 return matched.length / optional.length; 6875 } 6876 var details = { urlMatcher: urlMatcher, matchPriority: matchPriority, type: 'URLMATCHER' }; 6877 return extend(new BaseUrlRule(matchUrlParamters, _handler), details); 6878 }; 6879 /** 6880 * A UrlRule which matches a state by its url 6881 * 6882 * #### Example: 6883 * ```js 6884 * var rule = factory.fromState($state.get('foo'), router); 6885 * var match = rule.match('/foo/123/456'); // results in { fooId: '123', barId: '456' } 6886 * var result = rule.handler(match); 6887 * // Starts a transition to 'foo' with params: { fooId: '123', barId: '456' } 6888 * ``` 6889 */ 6890 UrlRuleFactory.prototype.fromState = function (stateOrDecl, router) { 6891 var state = StateObject.isStateDeclaration(stateOrDecl) ? stateOrDecl.$$state() : stateOrDecl; 6892 /** 6893 * Handles match by transitioning to matched state 6894 * 6895 * First checks if the router should start a new transition. 6896 * A new transition is not required if the current state's URL 6897 * and the new URL are already identical 6898 */ 6899 var handler = function (match) { 6900 var $state = router.stateService; 6901 var globals = router.globals; 6902 if ($state.href(state, match) !== $state.href(globals.current, globals.params)) { 6903 $state.transitionTo(state, match, { inherit: true, source: 'url' }); 6904 } 6905 }; 6906 var details = { state: state, type: 'STATE' }; 6907 return extend(this.fromUrlMatcher(state.url, handler), details); 6908 }; 6909 /** 6910 * A UrlRule which matches based on a regular expression 6911 * 6912 * The `handler` may be either a [[UrlRuleHandlerFn]] or a string. 6913 * 6914 * ## Handler as a function 6915 * 6916 * If `handler` is a function, the function is invoked with: 6917 * 6918 * - regexp match array (from `regexp`) 6919 * - url: the current Url ([[UrlParts]]) 6920 * - router: the router object ([[UIRouter]]) 6921 * 6922 * #### Example: 6923 * ```js 6924 * var rule = factory.fromRegExp(/^\/foo\/(bar|baz)$/, match => "/home/" + match[1]) 6925 * var match = rule.match('/foo/bar'); // results in [ '/foo/bar', 'bar' ] 6926 * var result = rule.handler(match); // '/home/bar' 6927 * ``` 6928 * 6929 * ## Handler as string 6930 * 6931 * If `handler` is a string, the url is *replaced by the string* when the Rule is invoked. 6932 * The string is first interpolated using `string.replace()` style pattern. 6933 * 6934 * #### Example: 6935 * ```js 6936 * var rule = factory.fromRegExp(/^\/foo\/(bar|baz)$/, "/home/$1") 6937 * var match = rule.match('/foo/bar'); // results in [ '/foo/bar', 'bar' ] 6938 * var result = rule.handler(match); // '/home/bar' 6939 * ``` 6940 */ 6941 UrlRuleFactory.prototype.fromRegExp = function (regexp, handler) { 6942 if (regexp.global || regexp.sticky) 6943 throw new Error('Rule RegExp must not be global or sticky'); 6944 /** 6945 * If handler is a string, the url will be replaced by the string. 6946 * If the string has any String.replace() style variables in it (like `$2`), 6947 * they will be replaced by the captures from [[match]] 6948 */ 6949 var redirectUrlTo = function (match) { 6950 // Interpolates matched values into $1 $2, etc using a String.replace()-style pattern 6951 return handler.replace(/\$(\$|\d{1,2})/, function (m, what) { return match[what === '$' ? 0 : Number(what)]; }); 6952 }; 6953 var _handler = isString(handler) ? redirectUrlTo : handler; 6954 var matchParamsFromRegexp = function (url) { return regexp.exec(url.path); }; 6955 var details = { regexp: regexp, type: 'REGEXP' }; 6956 return extend(new BaseUrlRule(matchParamsFromRegexp, _handler), details); 6957 }; 6958 UrlRuleFactory.isUrlRule = function (obj) { return obj && ['type', 'match', 'handler'].every(function (key) { return isDefined(obj[key]); }); }; 6959 return UrlRuleFactory; 6960 }()); 6961 /** 6962 * A base rule which calls `match` 6963 * 6964 * The value from the `match` function is passed through to the `handler`. 6965 * @internal 6966 */ 6967 var BaseUrlRule = /** @class */ (function () { 6968 function BaseUrlRule(match, handler) { 6969 var _this = this; 6970 this.match = match; 6971 this.type = 'RAW'; 6972 this.matchPriority = function (match) { return 0 - _this.$id; }; 6973 this.handler = handler || identity; 6974 } 6975 return BaseUrlRule; 6976 }()); 6977 6978 function appendBasePath(url, isHtml5, absolute, baseHref) { 6979 if (baseHref === '/') 6980 return url; 6981 if (isHtml5) 6982 return stripLastPathElement(baseHref) + url; 6983 if (absolute) 6984 return baseHref.slice(1) + url; 6985 return url; 6986 } 6987 /** 6988 * Updates URL and responds to URL changes 6989 * 6990 * ### Deprecation warning: 6991 * This class is now considered to be an internal API 6992 * Use the [[UrlService]] instead. 6993 * For configuring URL rules, use the [[UrlRules]] which can be found as [[UrlService.rules]]. 6994 */ 6995 var UrlRouter = /** @class */ (function () { 6996 /** @internal */ 6997 function UrlRouter(/** @internal */ router) { 6998 var _this = this; 6999 this.router = router; 7000 // Delegate these calls to [[UrlService]] 7001 /** @deprecated use [[UrlService.sync]]*/ 7002 this.sync = function (evt) { return _this.router.urlService.sync(evt); }; 7003 /** @deprecated use [[UrlService.listen]]*/ 7004 this.listen = function (enabled) { return _this.router.urlService.listen(enabled); }; 7005 /** @deprecated use [[UrlService.deferIntercept]]*/ 7006 this.deferIntercept = function (defer) { return _this.router.urlService.deferIntercept(defer); }; 7007 /** @deprecated use [[UrlService.match]]*/ 7008 this.match = function (urlParts) { return _this.router.urlService.match(urlParts); }; 7009 // Delegate these calls to [[UrlRules]] 7010 /** @deprecated use [[UrlRules.initial]]*/ 7011 this.initial = function (handler) { 7012 return _this.router.urlService.rules.initial(handler); 7013 }; 7014 /** @deprecated use [[UrlRules.otherwise]]*/ 7015 this.otherwise = function (handler) { 7016 return _this.router.urlService.rules.otherwise(handler); 7017 }; 7018 /** @deprecated use [[UrlRules.removeRule]]*/ 7019 this.removeRule = function (rule) { return _this.router.urlService.rules.removeRule(rule); }; 7020 /** @deprecated use [[UrlRules.rule]]*/ 7021 this.rule = function (rule) { return _this.router.urlService.rules.rule(rule); }; 7022 /** @deprecated use [[UrlRules.rules]]*/ 7023 this.rules = function () { return _this.router.urlService.rules.rules(); }; 7024 /** @deprecated use [[UrlRules.sort]]*/ 7025 this.sort = function (compareFn) { return _this.router.urlService.rules.sort(compareFn); }; 7026 /** @deprecated use [[UrlRules.when]]*/ 7027 this.when = function (matcher, handler, options) { return _this.router.urlService.rules.when(matcher, handler, options); }; 7028 this.urlRuleFactory = new UrlRuleFactory(router); 7029 } 7030 /** Internal API. */ 7031 UrlRouter.prototype.update = function (read) { 7032 var $url = this.router.locationService; 7033 if (read) { 7034 this.location = $url.url(); 7035 return; 7036 } 7037 if ($url.url() === this.location) 7038 return; 7039 $url.url(this.location, true); 7040 }; 7041 /** 7042 * Internal API. 7043 * 7044 * Pushes a new location to the browser history. 7045 * 7046 * @internal 7047 * @param urlMatcher 7048 * @param params 7049 * @param options 7050 */ 7051 UrlRouter.prototype.push = function (urlMatcher, params, options) { 7052 var replace = options && !!options.replace; 7053 this.router.urlService.url(urlMatcher.format(params || {}), replace); 7054 }; 7055 /** 7056 * Builds and returns a URL with interpolated parameters 7057 * 7058 * #### Example: 7059 * ```js 7060 * matcher = $umf.compile("/about/:person"); 7061 * params = { person: "bob" }; 7062 * $bob = $urlRouter.href(matcher, params); 7063 * // $bob == "/about/bob"; 7064 * ``` 7065 * 7066 * @param urlMatcher The [[UrlMatcher]] object which is used as the template of the URL to generate. 7067 * @param pa
7067rams An object of parameter values to fill the matcher's required parameters. 7068 * @param options Options object. The options are: 7069 * 7070 * - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl". 7071 * 7072 * @returns Returns the fully compiled URL, or `null` if `params` fail validation against `urlMatcher` 7073 */ 7074 UrlRouter.prototype.href = function (urlMatcher, params, options) { 7075 var url = urlMatcher.format(params); 7076 if (url == null) 7077 return null; 7078 options = options || { absolute: false }; 7079 var cfg = this.router.urlService.config; 7080 var isHtml5 = cfg.html5Mode(); 7081 if (!isHtml5 && url !== null) { 7082 url = '#' + cfg.hashPrefix() + url; 7083 } 7084 url = appendBasePath(url, isHtml5, options.absolute, cfg.baseHref()); 7085 if (!options.absolute || !url) { 7086 return url; 7087 } 7088 var slash = !isHtml5 && url ? '/' : ''; 7089 var cfgPort = cfg.port(); 7090 var port = (cfgPort === 80 || cfgPort === 443 ? '' : ':' + cfgPort); 7091 return [cfg.protocol(), '://', cfg.host(), port, slash, url].join(''); 7092 }; 7093 Object.defineProperty(UrlRouter.prototype, "interceptDeferred", { 7094 /** @deprecated use [[UrlService.interceptDeferred]]*/ 7095 get: function () { 7096 return this.router.urlService.interceptDeferred; 7097 }, 7098 enumerable: false, 7099 configurable: true 7100 }); 7101 return UrlRouter; 7102 }()); 7103 7104 /** 7105 * The View service 7106 * 7107 * This service pairs existing `ui-view` components (which live in the DOM) 7108 * with view configs (from the state declaration objects: [[StateDeclaration.views]]). 7109 * 7110 * - After a successful Transition, the views from the newly entered states are activated via [[activateViewConfig]]. 7111 * The views from exited states are deactivated via [[deactivateViewConfig]]. 7112 * (See: the [[registerActivateViews]] Transition Hook) 7113 * 7114 * - As `ui-view` components pop in and out of existence, they register themselves using [[registerUIView]]. 7115 * 7116 * - When the [[sync]] function is called, the registered `ui-view`(s) ([[ActiveU
7116IView]]) 7117 * are configured with the matching [[ViewConfig]](s) 7118 * 7119 */ 7120 var ViewService = /** @class */ (function () { 7121 /** @internal */ 7122 function ViewService(/** @internal */ router) { 7123 var _this = this; 7124 this.router = router; 7125 /** @internal */ this._uiViews = []; 7126 /** @internal */ this._viewConfigs = []; 7127 /** @internal */ this._viewConfigFactories = {}; 7128 /** @internal */ this._listeners = []; 7129 /** @internal */ 7130 this._pluginapi = { 7131 _rootViewContext: this._rootViewContext.bind(this), 7132 _viewConfigFactory: this._viewConfigFactory.bind(this), 7133 _registeredUIView: function (id) { return find(_this._uiViews, function (view) { return _this.router.$id + "." + view.id === id; }); }, 7134 _registeredUIViews: function () { return _this._uiViews; }, 7135 _activeViewConfigs: function () { return _this._viewConfigs; }, 7136 _onSync: function (listener) { 7137 _this._listeners.push(listener); 7138 return function () { return removeFrom(_this._listeners, listener); }; 7139 }, 7140 }; 7141 } 7142 /** 7143 * Normalizes a view's name from a state.views configuration block. 7144 * 7145 * This should be used by a framework implementation to calculate the values for 7146 * [[_ViewDeclaration.$uiViewName]] and [[_ViewDeclaration.$uiViewContextAnchor]]. 7147 * 7148 * @param context the context object (state declaration) that the view belongs to 7149 * @param rawViewName the name of the view, as declared in the [[StateDeclaration.views]] 7150 * 7151 * @returns the normalized uiViewName and uiViewContextAnchor that the view targets 7152 */ 7153 ViewService.normalizeUIViewTarget = function (context, rawViewName) { 7154 if (rawViewName === void 0) { rawViewName = ''; } 7155 // TODO: Validate incoming view name with a regexp to allow: 7156 // ex: "[email protected]" , "^.^.view.name" , "view.name@^.^" , "" , 7157 // "@" , "$default@^" , "!$default.$default" , "!foo.bar" 7158 var viewAtContext = rawViewName.split('@'); 7159 var uiViewName = viewAtContext[0] || '$default'; // default to unnamed view 7160 var uiViewContextAnchor = isString(viewAtContext[1]) ? viewAtContext[1] : '^'; // default to parent context 7161 // Handle relative view-name sugar syntax. 7162 // Matches rawViewName "^.^.^.foo.bar" into array: ["^.^.^.foo.bar", "^.^.^", "foo.bar"], 7163 var relativeViewNameSugar = /^(\^(?:\.\^)*)\.(.*$)/.exec(uiViewName); 7164 if (relativeViewNameSugar) { 7165 // Clobbers existing contextAnchor (rawViewName validation will fix this) 7166 uiViewContextAnchor = relativeViewNameSugar[1]; // set anchor to "^.^.^" 7167 uiViewName = relativeViewNameSugar[2]; // set view-name to "foo.bar" 7168 } 7169 if (uiViewName.charAt(0) === '!') { 7170 uiViewName = uiViewName.substr(1); 7171 uiViewContextAnchor = ''; // target absolutely from root 7172 } 7173 // handle parent relative targeting "^.^.^" 7174 var relativeMatch = /^(\^(?:\.\^)*)$/; 7175 if (relativeMatch.exec(uiViewContextAnchor)) { 7176 var anchorState = uiViewContextAnchor.split('.').reduce(function (anchor, x) { return anchor.parent; }, context); 7177 uiViewContextAnchor = anchorState.name; 7178 } 7179 else if (uiViewContextAnchor === '.') { 7180 uiViewContextAnchor = context.name; 7181 } 7182 return { uiViewName: uiViewName, uiViewContextAnchor: uiViewContextAnchor }; 7183 }; 7184 /** @internal */ 7185 ViewService.prototype._rootViewContext = function (context) { 7186 return (this._rootContext = context || this._rootContext); 7187 }; 7188 /** @internal */ 7189 ViewService.prototype._viewConfigFactory = function (viewType, factory) {
7190 this._viewConfigFactories[viewType] = factory; 7191 }; 7192 ViewService.prototype.createViewConfig = function (path, decl) { 7193 var cfgFactory = this._viewConfigFactories[decl.$type]; 7194 if (!cfgFactory) 7195 throw new Error('ViewService: No view config factory registered for type ' + decl.$type); 7196 var cfgs = cfgFactory(path, decl); 7197 return isArray(cfgs) ? cfgs : [cfgs]; 7198 }; 7199 /** 7200 * Deactivates a ViewConfig. 7201 * 7202 * This function deactivates a `ViewConfig`. 7203 * After calling [[sync]], it will un-pair from any `ui-view` with which it is currently paired. 7204 * 7205 * @param viewConfig The ViewConfig view to deregister. 7206 */ 7207 ViewService.prototype.deactivateViewConfig = function (viewConfig) { 7208 trace.traceViewServiceEvent('<- Removing', viewConfig); 7209 removeFrom(this._viewConfigs, viewConfig); 7210 }; 7211 ViewService.prototype.activateViewConfig = function (viewConfig) { 7212 trace.traceViewServiceEvent('-> Registering', viewConfig); 7213 this._viewConfigs.push(viewConfig); 7214 }; 7215 ViewService.prototype.sync = function () { 7216 var _this = this; 7217 var uiViewsByFqn = this._uiViews.map(function (uiv) { return [uiv.fqn, uiv]; }).reduce(applyPairs, {}); 7218 // Return a weighted depth value for a uiView. 7219 // The depth is the nesting depth of ui-views (based on FQN; times 10,000) 7220 // plus the depth of the state that is populating the uiView 7221 function uiViewDepth(uiView) { 7222 var stateDepth = function (context) { return (context && context.parent ? stateDepth(context.parent) + 1 : 1); }; 7223 return uiView.fqn.split('.').length * 10000 + stateDepth(uiView.creationContext); 7224 } 7225 // Return the ViewConfig's context's depth in the context tree. 7226 function viewConfigDepth(config) { 7227 var context = config.viewDecl.$context, count = 0; 7228 while (++count && context.parent) 7229 context = context.parent; 7230 return count; 7231 } 7232 // Given a depth function, returns a compare function which can return either ascending or descending order 7233 var depthCompare = curry(function (depthFn, posNeg, left, right) { return posNeg * (depthFn(left) - depthFn(right)); }); 7234 var matchingConfigPair = function (uiView) { 7235 var matchingConfigs = _this._viewConfigs.filter(ViewService.matches(uiViewsByFqn, uiView)); 7236 if (matchingConfigs.length > 1) { 7237 // This is OK. Child states can target a ui-view that the parent state also targets (the child wins) 7238 // Sort by depth and return the match from the deepest child 7239 // console.log(`Multiple matching view configs for ${uiView.fqn}`, matchingConfigs); 7240 matchingConfigs.sort(depthCompare(viewConfigDepth, -1)); // descending 7241 } 7242 return { uiView: uiView, viewConfig: matchingConfigs[0] }; 7243 }; 7244 var configureUIView = function (tuple) { 7245 // If a parent ui-view is reconfigured, it could destroy child ui-views. 7246 // Before configuring a child ui-view, make sure it's still in the active uiViews array. 7247 if (_this._uiViews.indexOf(tuple.uiView) !== -1) 7248 tuple.uiView.configUpdated(tuple.viewConfig); 7249 }; 7250 // Sort views by FQN and state depth. Process uiviews nearest the root first. 7251 var uiViewTuples = this._uiViews.sort(depthCompare(uiViewDepth, 1)).map(matchingConfigPair); 7252 var matchedViewConfigs = uiViewTuples.map(function (tuple) { return tuple.viewConfig; }); 7253 var unmatchedConfigTuples = this._viewConfigs 7254 .filter(function (config) { return !inArray(matchedViewConfigs, config); }) 7255 .map(function (viewConfig) { return ({ uiView: undefined, viewConfig: viewConfig }); });
7256 uiViewTuples.forEach(configureUIView); 7257 var allTuples = uiViewTuples.concat(unmatchedConfigTuples); 7258 this._listeners.forEach(function (cb) { return cb(allTuples); }); 7259 trace.traceViewSync(allTuples); 7260 }; 7261 /** 7262 * Registers a `ui-view` component 7263 * 7264 * When a `ui-view` component is created, it uses this method to register itself. 7265 * After registration the [[sync]] method is used to ensure all `ui-view` are configured with the proper [[ViewConfig]]. 7266 * 7267 * Note: the `ui-view` component uses the `ViewConfig` to determine what view should be loaded inside the `ui-view`, 7268 * and what the view's state context is. 7269 * 7270 * Note: There is no corresponding `deregisterUIView`. 7271 * A `ui-view` should hang on to the return value of `registerUIView` and invoke it to deregister itself. 7272 * 7273 * @param uiView The metadata for a UIView 7274 * @return a de-registration function used when the view is destroyed. 7275 */ 7276 ViewService.prototype.registerUIView = function (uiView) { 7277 trace.traceViewServiceUIViewEvent('-> Registering', uiView); 7278 var uiViews = this._uiViews; 7279 var fqnAndTypeMatches = function (uiv) { return uiv.fqn === uiView.fqn && uiv.$type === uiView.$type; }; 7280 if (uiViews.filter(fqnAndTypeMatches).length) 7281 trace.traceViewServiceUIViewEvent('!!!! duplicate uiView named:', uiView); 7282 uiViews.push(uiView); 7283 this.sync(); 7284 return function () { 7285 var idx = uiViews.indexOf(uiView); 7286 if (idx === -1) { 7287 trace.traceViewServiceUIViewEvent('Tried removing non-registered uiView', uiView); 7288 return; 7289 } 7290 trace.traceViewServiceUIViewEvent('<- Deregistering', uiView); 7291 removeFrom(uiViews)(uiView); 7292 }; 7293 }; 7294 /** 7295 * Returns the list of views currently available on the page, by fully-qualified name. 7296 * 7297 * @return {Array} Returns an array of fully-qualified view names. 7298 */ 7299 ViewService.prototype.available = function () { 7300 return this._uiViews.map(prop('fqn')); 7301 }; 7302 /** 7303 * Returns the list of views on the page containing loaded content. 7304 * 7305 * @return {Array} Returns an array of fully-qualified view names. 7306 */ 7307 ViewService.prototype.active = function () { 7308 return this._uiViews.filter(prop('$config')).map(prop('name')); 7309 }; 7310 /** 7311 * Given a ui-view and a ViewConfig, determines if they "match". 7312 * 7313 * A ui-view has a fully qualified name (fqn) and a context object. The fqn is built from its overall location in 7314 * the DOM, describing its nesting relationship to any parent ui-view tags it is nested inside of. 7315 * 7316 * A ViewConfig has a target ui-view name and a context anchor. The ui-view name can be a simple name, or 7317 * can be a segmented ui-view path, describing a portion of a ui-view fqn. 7318 * 7319 * In order for a ui-view to match ViewConfig, ui-view's $type must match the ViewConfig's $type 7320 * 7321 * If the ViewConfig's target ui-view name is a simple name (no dots), then a ui-view matches if: 7322 * - the ui-view's name matches the ViewConfig's target name 7323 * - the ui-view's context matches the ViewConfig's anchor 7324 * 7325 * If the ViewConfig's target ui-view name is a segmented name (with dots), then a ui-view matches if: 7326 * - There exists a parent ui-view where: 7327 * - the parent ui-view's name matches the first segment (index 0) of the ViewConfig's target name 7328 * - the parent ui-view's context matches the ViewConfig's anchor 7329 * - And the remaining segments (index 1..n) of the ViewConfig's target name match the tail of the ui-view's fqn 7330 * 7331 * Example: 7332 * 7333 * DOM:
7334 * <ui-view> <!-- created in the root context (name: "") --> 7335 * <ui-view name="foo"> <!-- created in the context named: "A" --> 7336 * <ui-view> <!-- created in the context named: "A.B" --> 7337 * <ui-view name="bar"> <!-- created in the context named: "A.B.C" --> 7338 * </ui-view> 7339 * </ui-view> 7340 * </ui-view> 7341 * </ui-view> 7342 * 7343 * uiViews: [ 7344 * { fqn: "$default", creationContext: { name: "" } }, 7345 * { fqn: "$default.foo", creationContext: { name: "A" } }, 7346 * { fqn: "$default.foo.$default", creationContext: { name: "A.B" } } 7347 * { fqn: "$default.foo.$default.bar", creationContext: { name: "A.B.C" } } 7348 * ] 7349 * 7350 * These four view configs all match the ui-view with the fqn: "$default.foo.$default.bar": 7351 * 7352 * - ViewConfig1: { uiViewName: "bar", uiViewContextAnchor: "A.B.C" } 7353 * - ViewConfig2: { uiViewName: "$default.bar", uiViewContextAnchor: "A.B" } 7354 * - ViewConfig3: { uiViewName: "foo.$default.bar", uiViewContextAnchor: "A" } 7355 * - ViewConfig4: { uiViewName: "$default.foo.$default.bar", uiViewContextAnchor: "" } 7356 * 7357 * Using ViewConfig3 as an example, it matches the ui-view with fqn "$default.foo.$default.bar" because: 7358 * - The ViewConfig's segmented target name is: [ "foo", "$default", "bar" ] 7359 * - There exists a parent ui-view (which has fqn: "$default.foo") where: 7360 * - the parent ui-view's name "foo" matches the first segment "foo" of the ViewConfig's target name 7361 * - the parent ui-view's context "A" matches the ViewConfig's anchor context "A" 7362 * - And the remaining segments [ "$default", "bar" ].join("."_ of the ViewConfig's target name match 7363 * the tail of the ui-view's fqn "default.bar" 7364 * 7365 * @internal 7366 */ 7367 ViewService.matches = function (uiViewsByFqn, uiView) { return function (viewConfig) { 7368 // Don't supply an ng1 ui-view with an ng2 ViewConfig, etc 7369 if (uiView.$type !== viewConfig.viewDecl.$type) 7370 return false; 7371 // Split names apart from both viewConfig and uiView into segments 7372 var vc = viewConfig.viewDecl; 7373 var vcSegments = vc.$uiViewName.split('.'); 7374 var uivSegments = uiView.fqn.split('.'); 7375 // Check if the tails of the segment arrays match. ex, these arrays' tails match: 7376 // vc: ["foo", "bar"], uiv fqn: ["$default", "foo", "bar"] 7377 if (!equals(vcSegments, uivSegments.slice(0 - vcSegments.length))) 7378 return false; 7379 // Now check if the fqn ending at the first segment of the viewConfig matches the context: 7380 // ["$default", "foo"].join(".") == "$default.foo", does the ui-view $default.foo context match? 7381 var negOffset = 1 - vcSegments.length || undefined; 7382 var fqnToFirstSegment = uivSegments.slice(0, negOffset).join('.'); 7383 var uiViewContext = uiViewsByFqn[fqnToFirstSegment].creationContext; 7384 return vc.$uiViewContextAnchor === (uiViewContext && uiViewContext.name); 7385 }; }; 7386 return ViewService; 7387 }()); 7388 7389 /** 7390 * Global router state 7391 * 7392 * This is where we hold the global mutable state such as current state, current 7393 * params, current transition, etc. 7394 */ 7395 var UIRouterGlobals = /** @class */ (function () { 7396 function UIRouterGlobals() { 7397 /** 7398 * Current parameter values 7399 * 7400 * The parameter values from the latest successful transition 7401 */ 7402 this.params = new StateParams(); 7403 /** @internal */ 7404 this.lastStartedTransitionId = -1; 7405 /** @internal */ 7406 this.transitionHistory = new Queue([], 1); 7407 /** @internal */ 7408 this.successfulTransitions = new Queue([], 1); 7409 } 7410 UIRouterGlobals.prototype.dispose = function () { 7411 this.transitionHistory.clear(); 7412 this.successfulTransitions.clear(); 7413 this.transition = null; 7414 }; 7415 return UIRouterGlobals; 7416 }()); 7417 7418 var prioritySort = function (a, b) { return (b.priority || 0) - (a.priority || 0); }; 7419 var typeSort = function (a, b) { 7420 var weights = { STATE: 4, URLMATCHER: 4, REGEXP: 3, RAW: 2, OTHER: 1 };
7421 return (weights[a.type] || 0) - (weights[b.type] || 0); 7422 }; 7423 var urlMatcherSort = function (a, b) { 7424 return !a.urlMatcher || !b.urlMatcher ? 0 : UrlMatcher.compare(a.urlMatcher, b.urlMatcher); 7425 }; 7426 var idSort = function (a, b) { 7427 // Identically sorted STATE and URLMATCHER best rule will be chosen by `matchPriority` after each rule matches the URL 7428 var useMatchPriority = { STATE: true, URLMATCHER: true }; 7429 var equal = useMatchPriority[a.type] && useMatchPriority[b.type]; 7430 return equal ? 0 : (a.$id || 0) - (b.$id || 0); 7431 }; 7432 /** 7433 * Default rule priority sorting function. 7434 * 7435 * Sorts rules by: 7436 * 7437 * - Explicit priority (set rule priority using [[UrlRules.when]]) 7438 * - Rule type (STATE: 4, URLMATCHER: 4, REGEXP: 3, RAW: 2, OTHER: 1) 7439 * - `UrlMatcher` specificity ([[UrlMatcher.compare]]): works for STATE and URLMATCHER types to pick the most specific rule. 7440 * - Rule registration order (for rule types other than STATE and URLMATCHER) 7441 * - Equally sorted State and UrlMatcher rules will each match the URL. 7442 * Then, the *best* match is chosen based on how many parameter values were matched. 7443 */ 7444 var defaultRuleSortFn; 7445 defaultRuleSortFn = function (a, b) { 7446 var cmp = prioritySort(a, b); 7447 if (cmp !== 0) 7448 return cmp; 7449 cmp = typeSort(a, b); 7450 if (cmp !== 0) 7451 return cmp; 7452 cmp = urlMatcherSort(a, b); 7453 if (cmp !== 0) 7454 return cmp; 7455 return idSort(a, b); 7456 }; 7457 function getHandlerFn(handler) { 7458 if (!isFunction(handler) && !isString(handler) && !is(TargetState)(handler) && !TargetState.isDef(handler)) { 7459 throw new Error("'handler' must be a string, function, TargetState, or have a state: 'newtarget' property"); 7460 } 7461 return isFunction(handler) ? handler : val(handler); 7462 } 7463 /** 7464 * API for managing URL rules 7465 * 7466 * This API is used to create and manage URL rules. 7467 * URL rules are a mechanism to respond to specific URL patterns. 7468 * 7469 * The most commonly used methods are [[otherwise]] and [[when]]. 7470 * 7471 * This API is found at `router.urlService.rules` (see: [[UIRouter.urlService]], [[URLService.rules]]) 7472 */ 7473 var UrlRules = /** @class */ (function () { 7474 /** @internal */ 7475 function UrlRules(/** @internal */ router) { 7476 this.router = router; 7477 /** @internal */ this._sortFn = defaultRuleSortFn; 7478 /** @internal */ this._rules = []; 7479 /** @internal */ this._id = 0; 7480 this.urlRuleFactory = new UrlRuleFactory(router); 7481 } 7482 /** @internal */ 7483 UrlRules.prototype.dispose = function (router) { 7484 this._rules = []; 7485 delete this._otherwiseFn; 7486 }; 7487 /** 7488 * Defines the initial state, path, or behavior to use when the app starts. 7489 * 7490 * This rule defines the initial/starting state for the application. 7491 * 7492 * This rule is triggered the first time the URL is checked (when the app initially loads). 7493 * The rule is triggered only when the url matches either `""` or `"/"`. 7494 * 7495 * Note: The rule is intended to be used when the root of the application is directly linked to. 7496 * When the URL is *not* `""` or `"/"` and doesn't match other rules, the [[otherwise]] rule is triggered. 7497 * This allows 404-like behavior when an unknown URL is deep-linked. 7498 * 7499 * #### Example: 7500 * Start app at `home` state. 7501 * ```js 7502 * .initial({ state: 'home' }); 7503 * ``` 7504 * 7505 * #### Example: 7506 * Start app at `/home` (by url) 7507 * ```js 7508 * .initial('/home'); 7509 * ``` 7510 * 7511 * #### Example: 7512 * When no other url rule matches, go to `home` state 7513 * ```js 7514 * .initial((matchValue, url, router) => { 7515 * console.log('initial state'); 7516 * return { state: 'home' }; 7517 * }) 7518 * ``` 7519 * 7520 * @param handler The initial state or url path, or a function which returns the state or url path (or performs custom logic). 7521 */ 7522 UrlRules.prototype.initial = function (handler) { 7523 var handlerFn = getHandlerFn(handler); 7524 var matchFn = function (urlParts, router) { 7525 return router.globals.transitionHistory.size() === 0 && !!/^\/?$/.exec(urlParts.path); 7526 }; 7527 this.rule(this.urlRuleFactory.create(matchFn, handlerFn)); 7528 }; 7529 /** 7530 * Defines the state, url, or behavior to use when no other rule matches the URL. 7531 * 7532 * This rule is matched when *no other rule* matches. 7533 * It is generally used to handle unknown URLs (similar to "404" behavior, but on the client side). 7534 * 7535 * - If `handler` a string, it is treated as a url redirect 7536 * 7537 * #### Example: 7538 * When no other url rule matches, redirect to `/index` 7539 * ```js 7540 * .otherwise('/index'); 7541 * ``` 7542 * 7543 * - If `handler` is an object with a `state` property, the state is activated. 7544 * 7545 * #### Example: 7546 * When no other url rule matches, redirect to `home` and provide a `dashboard` parameter value. 7547 * ```js 7548 * .otherwise({ state: 'home', params: { dashboard: 'default' } }); 7549 * ``` 7550 * 7551 * - If `handler` is a function, the function receives the current url ([[UrlParts]]) and the [[UIRouter]] object. 7552 * The function can perform actions, and/or return a value. 7553 * 7554 * #### Example: 7555 * When no other url rule matches, manually trigger a transition to the `home` state 7556 * ```js 7557 * .otherwise((matchValue, urlParts, router) => { 7558 * router.stateService.go('home'); 7559 * }); 7560 * ``` 7561 * 7562 * #### Example: 7563 * When no other url rule matches, go to `home` state 7564 * ```js 7565 * .otherwise((matchValue, urlParts, router) => { 7566 * return { state: 'home' }; 7567 * }); 7568 * ``` 7569 * 7570 * @param handler The url path to redirect to, or a function which returns the url path (or performs custom logic). 7571 */ 7572 UrlRules.prototype.otherwise = function (handler) { 7573 var handlerFn = getHandlerFn(handler); 7574 this._otherwiseFn = this.urlRuleFactory.create(val(true), handlerFn); 7575 this._sorted = false;
7576 }; 7577 /** 7578 * Remove a rule previously registered 7579 * 7580 * @param rule the matcher rule that was previously registered using [[rule]] 7581 */ 7582 UrlRules.prototype.removeRule = function (rule) { 7583 removeFrom(this._rules, rule); 7584 }; 7585 /** 7586 * Manually adds a URL Rule. 7587 * 7588 * Usually, a url rule is added using [[StateDeclaration.url]] or [[when]]. 7589 * This api can be used directly for more control (to register a [[BaseUrlRule]], for example). 7590 * Rules can be created using [[urlRuleFactory]], or created manually as simple objects. 7591 * 7592 * A rule should have a `match` function which returns truthy if the rule matched. 7593 * It should also have a `handler` function which is invoked if the rule is the best match. 7594 * 7595 * @return a function that deregisters the rule 7596 */ 7597 UrlRules.prototype.rule = function (rule) { 7598 var _this = this; 7599 if (!UrlRuleFactory.isUrlRule(rule)) 7600 throw new Error('invalid rule'); 7601 rule.$id = this._id++; 7602 rule.priority = rule.priority || 0; 7603 this._rules.push(rule); 7604 this._sorted = false; 7605 return function () { return _this.removeRule(rule); }; 7606 }; 7607 /** 7608 * Gets all registered rules 7609 * 7610 * @returns an array of all the registered rules 7611 */ 7612 UrlRules.prototype.rules = function () { 7613 this.ensureSorted(); 7614 return this._rules.concat(this._otherwiseFn ? [this._otherwiseFn] : []); 7615 }; 7616 /** 7617 * Defines URL Rule priorities 7618 * 7619 * More than one rule ([[UrlRule]]) might match a given URL. 7620 * This `compareFn` is used to sort the rules by priority. 7621 * Higher priority rules should sort earlier. 7622 * 7623 * The [[defaultRuleSortFn]] is used by default. 7624 * 7625 * You only need to call this function once. 7626 * The `compareFn` will be used to sort the rules as each is registered. 7627 * 7628 * If called without any parameter, it will re-sort the rules. 7629 * 7630 * --- 7631 * 7632 * Url rules may come from multiple sources: states's urls ([[StateDeclaration.url]]), [[when]], and [[rule]]. 7633 * Each rule has a (user-provided) [[UrlRule.priority]], a [[UrlRule.type]], and a [[UrlRule.$id]] 7634 * The `$id` is is the order in which the rule was registered. 7635 * 7636 * The sort function should use these data, or data found on a specific type 7637 * of [[UrlRule]] (such as [[StateRule.state]]), to order the rules as desired. 7638 * 7639 * #### Example: 7640 * This compare function prioritizes rules by the order in which the rules were registered. 7641 * A rule registered earlier has higher priority. 7642 * 7643 * ```js 7644 * function compareFn(a, b) { 7645 * return a.$id - b.$id; 7646 * } 7647 * ``` 7648 * 7649 * @param compareFn a function that compares to [[UrlRule]] objects. 7650 * The `compareFn` should abide by the `Array.sort` compare function rules. 7651 * Given two rules, `a` and `b`, return a negative number if `a` should be higher priority. 7652 * Return a positive number if `b` should be higher priority. 7653 * Return `0` if the rules are identical. 7654 * 7655 * See the [mozilla reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Description) 7656 * for details. 7657 */ 7658 UrlRules.prototype.sort = function (compareFn) { 7659 var sorted = this.stableSort(this._rules, (this._sortFn = compareFn || this._sortFn)); 7660 // precompute _sortGroup values and apply to each rule 7661 var group = 0; 7662 for (var i = 0; i < sorted.length; i++) { 7663 sorted[i]._group = group; 7664 if (i < sorted.length - 1 && this._sortFn(sorted[i], sorted[i + 1]) !== 0) { 7665 group++; 7666 } 7667 } 7668 this._rules = sorted; 7669 this._sorted = true; 7670 }; 7671 /** @internal */ 7672 UrlRules.prototype.ensureSorted = function () { 7673 this._sorted || this.sort(); 7674 }; 7675 /** @internal */ 7676 UrlRules.prototype.stableSort = function (arr, compareFn) { 7677 var arrOfWrapper = arr.map(function (elem, idx) { return ({ elem: elem, idx: idx }); }); 7678 arrOfWrapper.sort(function (wrapperA, wrapperB) { 7679 var cmpDiff = compareFn(wrapperA.elem, wrapperB.elem);
7680 return cmpDiff === 0 ? wrapperA.idx - wrapperB.idx : cmpDiff; 7681 }); 7682 return arrOfWrapper.map(function (wrapper) { return wrapper.elem; }); 7683 }; 7684 /** 7685 * Registers a `matcher` and `handler` for custom URLs handling. 7686 * 7687 * The `matcher` can be: 7688 * 7689 * - a [[UrlMatcher]]: See: [[UrlMatcherFactory.compile]] 7690 * - a `string`: The string is compiled to a [[UrlMatcher]] 7691 * - a `RegExp`: The regexp is used to match the url. 7692 * 7693 * The `handler` can be: 7694 * 7695 * - a string: The url is redirected to the value of the string. 7696 * - a function: The url is redirected to the return value of the function. 7697 * 7698 * --- 7699 * 7700 * When the `handler` is a `string` and the `matcher` is a `UrlMatcher` (or string), the redirect 7701 * string is interpolated with parameter values. 7702 * 7703 * #### Example: 7704 * When the URL is `/foo/123` the rule will redirect to `/bar/123`. 7705 * ```js 7706 * .when("/foo/:param1", "/bar/:param1") 7707 * ``` 7708 * 7709 * --- 7710 * 7711 * When the `handler` is a string and the `matcher` is a `RegExp`, the redirect string is
7712 * interpolated with capture groups from the RegExp. 7713 * 7714 * #### Example: 7715 * When the URL is `/foo/123` the rule will redirect to `/bar/123`. 7716 * ```js 7717 * .when(new RegExp("^/foo/(.*)$"), "/bar/$1"); 7718 * ``` 7719 * 7720 * --- 7721 * 7722 * When the handler is a function, it receives the matched value, the current URL, and the `UIRouter` object (See [[UrlRuleHandlerFn]]). 7723 * The "matched value" differs based on the `matcher`. 7724 * For [[UrlMatcher]]s, it will be the matched state params. 7725 * For `RegExp`, it will be the match array from `regexp.exec()`. 7726 * 7727 * If the handler returns a string, the URL is redirected to the string. 7728 * 7729 * #### Example: 7730 * When the URL is `/foo/123` the rule will redirect to `/bar/123`. 7731 * ```js 7732 * .when(new RegExp("^/foo/(.*)$"), match => "/bar/" + match[1]); 7733 * ``` 7734 * 7735 * Note: the `handler` may also invoke arbitrary code, such as `$state.go()` 7736 * 7737 * @param matcher A pattern `string` to match, compiled as a [[UrlMatcher]], or a `RegExp`. 7738 * @param handler The path to redirect to, or a function that returns the path. 7739 * @param options `{ priority: number }` 7740 * 7741 * @return the registered [[UrlRule]] 7742 */ 7743 UrlRules.prototype.when = function (matcher, handler, options) { 7744 var rule = this.urlRuleFactory.create(matcher, handler); 7745 if (isDefined(options && options.priority)) 7746 rule.priority = options.priority; 7747 this.rule(rule); 7748 return rule; 7749 }; 7750 return UrlRules; 7751 }()); 7752 7753 /** 7754 * An API to customize the URL behavior and retrieve URL configuration 7755 * 7756 * This API is used to customize the behavior of the URL. 7757 * This includes optional trailing slashes ([[strictMode]]), case sensitivity ([[caseInsensitive]]), 7758 * and custom parameter encoding (custom [[type]]). 7759 * 7760 * It also has information about the location (url) configuration such as [[port]] and [[baseHref]]. 7761 * This information can be used to build absolute URLs, such as 7762 * `https://example.com:443/basepath/state/substate?param1=a#hashvalue`; 7763 * 7764 * This API is found at `router.urlService.config` (see: [[UIRouter.urlService]], [[URLService.config]]) 7765 */ 7766 var UrlConfig = /** @class */ (function () { 7767 /** @internal */ function UrlConfig(/** @internal */ router) { 7768 var _this = this; 7769 this.router = router; 7770 /** @internal */ this.paramTypes = new ParamTypes(); 7771 /** @internal */ this._decodeParams = true; 7772 /** @internal */ this._isCaseInsensitive = false; 7773 /** @internal */ this._isStrictMode = true; 7774 /** @internal */ this._defaultSquashPolicy = false; 7775 /** @internal */ this.dispose = function () { return _this.paramTypes.dispose(); }; 7776 // Delegate these calls to the current LocationConfig implementation 7777 /** 7778 * Gets the base Href, e.g., `http://localhost/approot/` 7779 * 7780 * @return the application's base href 7781 */ 7782 this.baseHref = function () { return _this.router.locationConfig.baseHref(); }; 7783 /** 7784 * Gets or sets the hashPrefix 7785 * 7786 * This only applies when not running in [[html5Mode]] (pushstate mode) 7787 * 7788 * If the current url is `http://localhost/app#!/uirouter/path/#anchor`, it returns `!` which is the prefix for the "hashbang" portion. 7789 * 7790 * @return the hash prefix 7791 */ 7792 this.hashPrefix = function (newprefix) { return _this.router.locationConfig.hashPrefix(newprefix); }; 7793 /** 7794 * Gets the host, e.g., `localhost` 7795 * 7796 * @return the protocol 7797 */ 7798 this.host = function () { return _this.router.locationConfig.host(); }; 7799 /** 7800 * Returns true when running in pushstate mode 7801 * 7802 * @return true when running in html5 mode (pushstate mode). 7803 */ 7804 this.html5Mode = function () { return _this.router.locationConfig.html5Mode(); }; 7805 /** 7806 * Gets the port, e.g., `80` 7807 * 7808 * @return the port number 7809 */ 7810 this.port = function () { return _this.router.locationConfig.port(); }; 7811 /** 7812 * Gets the protocol, e.g., `http` 7813 * 7814 * @return the protocol 7815 */ 7816 this.protocol = function () { return _this.router.locationConfig.protocol(); }; 7817 } 7818 /** 7819 * Defines whether URL matching should be case sensitive (the default behavior), or not. 7820 * 7821 * #### Example: 7822 * ```js 7823 * // Allow case insensitive url matches 7824 * urlService.config.caseInsensitive(true); 7825 * ``` 7826 * 7827 * @param value `false` to match URL in a case sensitive manner; otherwise `true`; 7828 * @returns the current value of caseInsensitive 7829 */ 7830 UrlConfig.prototype.caseInsensitive = function (value) { 7831 return (this._isCaseInsensitive = isDefined(value) ? value : this._isCaseInsensitive); 7832 }; 7833 /** 7834 * Sets the default behavior when generating or matching URLs with default parameter values. 7835 * 7836 * #### Example: 7837 * ```js 7838 * // Remove default parameter values from the url 7839 * urlService.config.defaultSquashPolicy(true); 7840 * ``` 7841 * 7842 * @param value A string that defines the default parameter URL squashing behavior. 7843 * - `nosquash`: When generating an href with a default parameter value, do not squash the parameter value from the URL 7844 * - `slash`: When generating an href with a default parameter value, squash (remove) the parameter value, and, if the 7845 * parameter is surrounded by slashes, squash (remove) one slash from the URL 7846 * - any other string, e.g. "~": When generating an href with a default parameter value, squash (remove) 7847 * the parameter value from the URL and replace it with this string. 7848 * @returns the current value of defaultSquashPolicy 7849 */ 7850 UrlConfig.prototype.defaultSquashPolicy = function (value) { 7851 if (isDefined(value) && value !== true && value !== false && !isString(value)) 7852 throw new Error("Invalid squash policy: " + value + ". Valid policies: false, true, arbitrary-string"); 7853 return (this._defaultSquashPolicy = isDefined(value) ? value : this._defaultSquashPolicy); 7854 }; 7855 /** 7856 * Defines whether URLs should match trailing slashes, or not (the default behavior). 7857 * 7858 * #### Example: 7859 * ```js 7860 * // Allow optional trailing slashes 7861 * urlService.config.strictMode(false); 7862 * ``` 7863 * 7864 * @param value `false` to match trailing slashes in URLs, otherwise `true`. 7865 * @returns the current value of strictMode 7866 */ 7867 UrlConfig.prototype.strictMode = function (value) { 7868 return (this._isStrictMode = isDefined(value) ? value : this._isStrictMode); 7869 }; 7870 /** 7871 * Creates and registers a custom [[ParamType]] object 7872 * 7873 * A custom parameter type can be used to generate URLs with typed parameters or custom encoding/decoding. 7874 * 7875 * #### Note: Register custom types *before using them* in a state definition. 7876 * 7877 * #### Example: 7878 * ```js 7879 * // Encode object parameter as JSON string 7880 * urlService.config.type('myjson', { 7881 * encode: (obj) => JSON.stringify(obj), 7882 * decode: (str) => JSON.parse(str), 7883 * is: (val) => typeof(val) === 'object', 7884 * pattern: /[^/]+/, 7885 * equals: (a, b) => _.isEqual(a, b), 7886 * }); 7887 * ``` 7888 * 7889 * See [[ParamTypeDefinition]] for more examples 7890 * 7891 * @param name The type name. 7892 * @param definition The type definition. See [[ParamTypeDefinition]] for information on the values accepted. 7893 * @param definitionFn A function that is injected before the app runtime starts. 7894 * The result of this function should be a [[ParamTypeDefinition]]. 7895 * The result is merged into the existing `definition`. 7896 * See [[ParamType]] for information on the values accepted. 7897 * 7898 * @returns if only the `name` parameter was specified: the currently registered [[ParamType]] object, or undefined 7899 */ 7900 UrlConfig.prototype.type = function (name, definition, definitionFn) { 7901 var type = this.paramTypes.type(name, definition, definitionFn); 7902 return !isDefined(definition) ? type : this; 7903 }; 7904 return UrlConfig; 7905 }()); 7906 7907 /** 7908 * API for URL management 7909 */ 7910 var UrlService = /** @class */ (function () { 7911 /** @internal */ 7912 function UrlService(/** @internal */ router) { 7913 var _this = this; 7914 this.router = router; 7915 /** @internal */ this.interceptDeferred = false;
7916 /** 7917 * The nested [[UrlRules]] API for managing URL rules and rewrites 7918 * 7919 * See: [[UrlRules]] for details 7920 */ 7921 this.rules = new UrlRules(this.router); 7922 /** 7923 * The nested [[UrlConfig]] API to configure the URL and retrieve URL information 7924 * 7925 * See: [[UrlConfig]] for details 7926 */ 7927 this.config = new UrlConfig(this.router); 7928 // Delegate these calls to the current LocationServices implementation 7929 /** 7930 * Gets the current url, or updates the url 7931 * 7932 * ### Getting the current URL 7933 * 7934 * When no arguments are passed, returns the current URL. 7935 * The URL is normalized using the internal [[path]]/[[search]]/[[hash]] values. 7936 * 7937 * For example, the URL may be stored in the hash ([[HashLocationServices]]) or 7938 * have a base HREF prepended ([[PushStateLocationServices]]). 7939 * 7940 * The raw URL in the browser might be: 7941 * 7942 * ``` 7943 * http://mysite.com/somepath/index.html#/internal/path/123?param1=foo#anchor 7944 * ``` 7945 * 7946 * or 7947 * 7948 * ``` 7949 * http://mysite.com/basepath/internal/path/123?param1=foo#anchor 7950 * ``` 7951 * 7952 * then this method returns: 7953 * 7954 * ``` 7955 * /internal/path/123?param1=foo#anchor 7956 * ``` 7957 * 7958 * 7959 * #### Example: 7960 * ```js 7961 * locationServices.url(); // "/some/path?query=value#anchor" 7962 * ``` 7963 * 7964 * ### Updating the URL 7965 * 7966 * When `newurl` arguments is provided, changes the URL to reflect `newurl` 7967 * 7968 * #### Example: 7969 * ```js 7970 * locationServices.url("/some/path?query=value#anchor", true); 7971 * ``` 7972 * 7973 * @param newurl The new value for the URL. 7974 * This url should reflect only the new internal [[path]], [[search]], and [[hash]] values. 7975 * It should not include the protocol, site, port, or base path of an absolute HREF. 7976 * @param replace When true, replaces the current history entry (instead of appending it) with this new url 7977 * @param state The history's state object, i.e., pushState (if the LocationServices implementation supports it) 7978 * 7979 * @return the url (after potentially being processed) 7980 */ 7981 this.url = function (newurl, replace, state) { 7982 return _this.router.locationService.url(newurl, replace, state); 7983 }; 7984 /** 7985 * Gets the path part of the current url 7986 * 7987 * If the current URL is `/some/path?query=value#anchor`, this returns `/some/path` 7988 * 7989 * @return the path portion of the url 7990 */ 7991 this.path = function () { return _this.router.locationService.path(); }; 7992 /** 7993 * Gets the search part of the current url as an object 7994 * 7995 * If the current URL is `/some/path?query=value#anchor`, this returns `{ query: 'value' }` 7996 * 7997 * @return the search (query) portion of the url, as an object 7998 */ 7999 this.search = function () { return _this.router.locationService.search(); }; 8000 /** 8001 * Gets the hash part of the current url 8002 * 8003 * If the current URL is `/some/path?query=value#anchor`, this returns `anchor` 8004 * 8005 * @return the hash (anchor) portion of the url 8006 */ 8007 this.hash = function () { return _this.router.locationService.hash(); }; 8008 /** 8009 * @internal 8010 * 8011 * Registers a low level url change handler 8012 * 8013 * Note: Because this is a low level handler, it's not recommended for general use. 8014 * 8015 * #### Example: 8016 * ```js 8017 * let deregisterFn = locationServices.onChange((evt) => console.log("url change", evt)); 8018 * ``` 8019 * 8020 * @param callback a function that will be called when the url is changing 8021 * @return a function that de-registers the callback 8022 */ 8023 this.onChange = function (callback) { return _this.router.locationService.onChange(callback); }; 8024 } 8025 /** @internal */ 8026 UrlService.prototype.dispose = function () { 8027 this.listen(false); 8028 this.rules.dispose(); 8029 }; 8030 /** 8031 * Gets the current URL parts 8032 * 8033 * This method returns the different parts of the current URL (the [[path]], [[search]], and [[hash]]) as a [[UrlParts]] object. 8034 */ 8035 UrlService.prototype.parts = function () { 8036 return { path: this.path(), search: this.search(), hash: this.hash() }; 8037 }; 8038 /** 8039 * Activates the best rule for the current URL 8040 * 8041 * Checks the current URL for a matching [[UrlRule]], then invokes that rule's handler.
8042 * This method is called internally any time the URL has changed. 8043 * 8044 * This effectively activates the state (or redirect, etc) which matches the current URL. 8045 * 8046 * #### Example: 8047 * ```js 8048 * urlService.deferIntercept(); 8049 * 8050 * fetch('/states.json').then(resp => resp.json()).then(data => { 8051 * data.forEach(state => $stateRegistry.register(state)); 8052 * urlService.listen(); 8053 * // Find the matching URL and invoke the handler. 8054 * urlService.sync(); 8055 * }); 8056 * ``` 8057 */ 8058 UrlService.prototype.sync = function (evt) { 8059 if (evt && evt.defaultPrevented) 8060 return; 8061 var _a = this.router, urlService = _a.urlService, stateService = _a.stateService; 8062 var url = { path: urlService.path(), search: urlService.search(), hash: urlService.hash() }; 8063 var best = this.match(url); 8064 var applyResult = pattern([ 8065 [isString, function (newurl) { return urlService.url(newurl, true); }], 8066 [TargetState.isDef, function (def) { return stateService.go(def.state, def.params, def.options); }], 8067 [is(TargetState), function (target) { return stateService.go(target.state(), target.params(), target.options()); }], 8068 ]); 8069 applyResult(best && best.rule.handler(best.match, url, this.router)); 8070 }; 8071 /** 8072 * Starts or stops listening for URL changes 8073 * 8074 * Call this sometime after calling [[deferIntercept]] to start monitoring the url. 8075 * This causes UI-Router to start listening for changes to the URL, if it wasn't already listening. 8076 * 8077 * If called with `false`, UI-Router will stop listening (call listen(true) to start listening again). 8078 * 8079 * #### Example: 8080 * ```js 8081 * urlService.deferIntercept(); 8082 * 8083 * fetch('/states.json').then(resp => resp.json()).then(data => { 8084 * data.forEach(state => $stateRegistry.register(state)); 8085 * // Start responding to URL changes 8086 * urlService.listen(); 8087 * urlService.sync(); 8088 * }); 8089 * ``` 8090 * 8091 * @param enabled `true` or `false` to start or stop listening to URL changes 8092 */ 8093 UrlService.prototype.listen = function (enabled) { 8094 var _this = this; 8095 if (enabled === false) { 8096 this._stopListeningFn && this._stopListeningFn(); 8097 delete this._stopListeningFn; 8098 } 8099 else { 8100 return (this._stopListeningFn = 8101 this._stopListeningFn || this.router.urlService.onChange(function (evt) { return _this.sync(evt); })); 8102 } 8103 }; 8104 /** 8105 * Disables monitoring of the URL. 8106 * 8107 * Call this method before UI-Router has bootstrapped. 8108 * It will stop UI-Router from performing the initial url sync. 8109 * 8110 * This can be useful to perform some asynchronous initialization before the router starts. 8111 * Once the initialization is complete, call [[listen]] to tell UI-Router to start watching and synchronizing the URL. 8112 * 8113 * #### Example: 8114 * ```js 8115 * // Prevent UI-Router from automatically intercepting URL changes when it starts; 8116 * urlService.deferIntercept(); 8117 * 8118 * fetch('/states.json').then(resp => resp.json()).then(data => { 8119 * data.forEach(state => $stateRegistry.register(state)); 8120 * urlService.listen(); 8121 * urlService.sync(); 8122 * }); 8123 * ``` 8124 * 8125 * @param defer Indicates whether to defer location change interception. 8126 * Passing no parameter is equivalent to `true`. 8127 */ 8128 UrlService.prototype.deferIntercept = function (defer) { 8129 if (defer === undefined) 8130 defer = true; 8131 this.interceptDeferred = defer; 8132 }; 8133 /** 8134 * Matches a URL 8135 * 8136 * Given a URL (as a [[UrlParts]] object), check all rules and determine the best matching rule. 8137 * Return the result as a [[MatchResult]]. 8138 */ 8139 UrlService.prototype.match = function (url) { 8140 var _this = this; 8141 url = extend({ path: '', search: {}, hash: '' }, url); 8142 var rules = this.rules.rules(); 8143 // Checks a single rule. Returns { rule: rule, match: match, weight: weight } if it matched, or undefined 8144 var checkRule = function (rule) { 8145 var match = rule.match(url, _this.router); 8146 return match && { match: match, rule: rule, weight: rule.matchPriority(match) }; 8147 }; 8148 // The rules are pre-sorted. 8149 // - Find the first matching rule. 8150 // - Find any other matching rule that sorted *exactly the same*, according to `.sort()`. 8151 // - Choose the rule with the highest match weight. 8152 var best; 8153 for (var i = 0; i < rules.length; i++) { 8154 // Stop when there is a 'best' rule and the next rule sorts differently than it. 8155 if (best && best.rule._group !== rules[i]._group) 8156 break; 8157 var current = checkRule(rules[i]); 8158 // Pick the best MatchResult 8159 best = !best || (current && current.weight > best.weight) ? current : best; 8160 } 8161 return best; 8162 }; 8163 return UrlService; 8164 }()); 8165 8166 /** @internal */ 8167 var _routerInstance = 0; 8168 /** @internal */ 8169 var locSvcFns = ['url', 'path', 'search', 'hash', 'onChange']; 8170 /** @internal */ 8171 var locCfgFns = ['port', 'protocol', 'host', 'baseHref', 'html5Mode', 'hashPrefix']; 8172 /** @internal */ 8173 var locationServiceStub = makeStub('LocationServices', locSvcFns); 8174 /** @internal */ 8175 var locationConfigStub = makeStub('LocationConfig', locCfgFns); 8176 /** 8177 * An instance of UI-Router. 8178 *
8179 * This object contains references to service APIs which define your application's routing behavior. 8180 */ 8181 var UIRouter = /** @class */ (function () { 8182 /** 8183 * Creates a new `UIRouter` object 8184 * 8185 * @param locationService a [[LocationServices]] implementation 8186 * @param locationConfig a [[LocationConfig]] implementation 8187 * @internal 8188 */ 8189 function UIRouter(locationService, locationConfig) { 8190 if (locationService === void 0) { locationService = locationServiceStub; } 8191 if (locationConfig === void 0) { locationConfig = locationConfigStub; } 8192 this.locationService = locationService; 8193 this.locationConfig = locationConfig; 8194 /** @internal */ this.$id = _routerInstance++; 8195 /** @internal */ this._disposed = false; 8196 /** @internal */ this._disposables = []; 8197 /** Enable/disable tracing to the javascript console */ 8198 this.trace = trace; 8199 /** Provides services related to ui-view synchronization */ 8200 this.viewService = new ViewService(this); 8201 /** An object that contains global router state, such as the current state and params */ 8202 this.globals = new UIRouterGlobals(); 8203 /** A service that exposes global Transition Hooks */ 8204 this.transitionService = new TransitionService(this); 8205 /** 8206 * Deprecated for public use. Use [[urlService]] instead. 8207 * @deprecated Use [[urlService]] instead 8208 */ 8209 this.urlMatcherFactory = new UrlMatcherFactory(this); 8210 /** 8211 * Deprecated for public use. Use [[urlService]] instead. 8212 * @deprecated Use [[urlService]] instead 8213 */ 8214 this.urlRouter = new UrlRouter(this); 8215 /** Provides services related to the URL */ 8216 this.urlService = new UrlService(this); 8217 /** Provides a registry for states, and related registration services */ 8218 this.stateRegistry = new StateRegistry(this); 8219 /** Provides services related to states */ 8220 this.stateService = new StateService(this); 8221 /** @internal plugin instances are registered here */ 8222 this._plugins = {}; 8223 this.viewService._pluginapi._rootViewContext(this.stateRegistry.root()); 8224 this.globals.$current = this.stateRegistry.root(); 8225 this.globals.current = this.globals.$current.self; 8226 this.disposable(this.globals); 8227 this.disposable(this.stateService); 8228 this.disposable(this.stateRegistry); 8229 this.disposable(this.transitionService); 8230 this.disposable(this.urlService); 8231 this.disposable(locationService); 8232 this.disposable(locationConfig); 8233 } 8234 /** Registers an object to be notified when the router is disposed */ 8235 UIRouter.prototype.disposable = function (disposable) { 8236 this._disposables.push(disposable); 8237 }; 8238 /** 8239 * Disposes this router instance 8240 * 8241 * When called, clears resources retained by the router by calling `dispose(this)` on all 8242 * registered [[disposable]] objects. 8243 * 8244 * Or, if a `disposable` object is provided, calls `dispose(this)` on that object only. 8245 * 8246 * @internal 8247 * @param disposable (optional) the disposable to dispose 8248 */ 8249 UIRouter.prototype.dispose = function (disposable) { 8250 var _this = this; 8251 if (disposable && isFunction(disposable.dispose)) { 8252 disposable.dispose(this); 8253 return undefined; 8254 } 8255 this._disposed = true; 8256 this._disposables.slice().forEach(function (d) { 8257 try { 8258 typeof d.dispose === 'function' && d.dispose(_this); 8259 removeFrom(_this._disposables, d); 8260 } 8261 catch (ignored) { } 8262 }); 8263 }; 8264 /** 8265 * Adds a plugin to UI-Router 8266 * 8267 * This method adds a UI-Router Plugin. 8268 * A plugin can enhance or change UI-Router behavior using any public API. 8269 * 8270 * #### Example: 8271 * ```js 8272 * import { MyCoolPlugin } from "ui-router-cool-plugin"; 8273 * 8274 * var plugin = router.addPlugin(MyCoolPlugin); 8275 * ``` 8276 * 8277 * ### Plugin authoring 8278 * 8279 * A plugin is simply a class (or constructor function) which accepts a [[UIRouter]] instance and (optionally) an options object. 8280 * 8281 * The plugin can implement its functionality using any of the public APIs of [[UIRouter]]. 8282 * For example, it may configure router options or add a Transition Hook. 8283 * 8284 * The plugin can then be published as a separate module. 8285 * 8286 * #### Example: 8287 * ```js 8288 * export class MyAuthPlugin implements UIRouterPlugin { 8289 * constructor(router: UIRouter, options: any) { 8290 * this.name = "MyAuthPlugin"; 8291 * let $transitions = router.transitionService; 8292 * let $state = router.stateService; 8293 * 8294 * let authCriteria = { 8295 * to: (state) => state.data && state.data.requiresAuth 8296 * }; 8297 * 8298 * function authHook(transition: Transition) { 8299 * let authService = transition.injector().get('AuthService'); 8300 * if (!authService.isAuthenticated()) { 8301 * return $state.target('login'); 8302 * } 8303 * } 8304 * 8305 * $transitions.onStart(authCriteria, authHook); 8306 * } 8307 * } 8308 * ``` 8309 * 8310 * @param plugin one of: 8311 * - a plugin class which implements [[UIRouterPlugin]] 8312 * - a constructor function for a [[UIRouterPlugin]] which accepts a [[UIRouter]] instance 8313 * - a factory function which accepts a [[UIRouter]] instance and returns a [[UIRouterPlugin]] instance 8314 * @param options options to pass to the plugin class/factory 8315 * @returns the registered plugin instance 8316 */ 8317 UIRouter.prototype.plugin = function (plugin, options) { 8318 if (options === void 0) { options = {}; } 8319 var pluginInstance = new plugin(this, options); 8320 if (!pluginInstance.name) 8321 throw new Error('Required property `name` missing on plugin: ' + pluginInstance); 8322 this._disposables.push(pluginInstance); 8323 return (this._plugins[pluginInstance.name] = pluginInstance); 8324 }; 8325 UIRouter.prototype.getPlugin = function (pluginName) { 8326 return pluginName ? this._plugins[pluginName] : values(this._plugins); 8327 }; 8328 return UIRouter; 8329 }()); 8330 8331 function addCoreResolvables(trans) { 8332 trans.addResolvable(Resolvable.fromData(UIRouter, trans.router), ''); 8333 trans.addResolvable(Resolvable.fromData(Transition, trans), ''); 8334 trans.addResolvable(Resolvable.fromData('$transition$', trans), ''); 8335 trans.addResolvable(Resolvable.fromData('$stateParams', trans.params()), '');
8336 trans.entering().forEach(function (state) { 8337 trans.addResolvable(Resolvable.fromData('$state$', state), state); 8338 }); 8339 } 8340 var registerAddCoreResolvables = function (transitionService) { 8341 return transitionService.onCreate({}, addCoreResolvables); 8342 }; 8343 var TRANSITION_TOKENS = ['$transition$', Transition]; 8344 var isTransition = inArray(TRANSITION_TOKENS); 8345 // References to Transition in the treeChanges pathnodes makes all 8346 // previous Transitions reachable in memory, causing a memory leak 8347 // This function removes resolves for '$transition$' and `Transition` from the treeChanges. 8348 // Do not use this on current transitions, only on old ones. 8349 var treeChangesCleanup = function (trans) { 8350 var nodes = values(trans.treeChanges()).reduce(unnestR, []).reduce(uniqR, []); 8351 // If the resolvable is a Transition, return a new resolvable with null data 8352 var replaceTransitionWithNull = function (r) { 8353 return isTransition(r.token) ? Resolvable.fromData(r.token, null) : r; 8354 }; 8355 nodes.forEach(function (node) { 8356 node.resolvables = node.resolvables.map(replaceTransitionWithNull); 8357 }); 8358 }; 8359 8360 /** 8361 * A [[TransitionHookFn]] that redirects to a different state or params 8362 * 8363 * Registered using `transitionService.onStart({ to: (state) => !!state.redirectTo }, redirectHook);` 8364 * 8365 * See [[StateDeclaration.redirectTo]] 8366 */ 8367 var redirectToHook = function (trans) { 8368 var redirect = trans.to().redirectTo; 8369 if (!redirect) 8370 return; 8371 var $state = trans.router.stateService; 8372 function handleResult(result) { 8373 if (!result) 8374 return; 8375 if (result instanceof TargetState) 8376 return result; 8377 if (isString(result)) 8378 return $state.target(result, trans.params(), trans.options()); 8379 if (result['state'] || result['params']) 8380 return $state.target(result['state'] || trans.to(), result['params'] || trans.params(), trans.options()); 8381 } 8382 if (isFunction(redirect)) { 8383 return services.$q.when(redirect(trans)).then(handleResult); 8384 } 8385 return handleResult(redirect); 8386 }; 8387 var registerRedirectToHook = function (transitionService) { 8388 return transitionService.onStart({ to: function (state) { return !!state.redirectTo; } }, redirectToHook); 8389 }; 8390 8391 /** 8392 * A factory which creates an onEnter, onExit or onRetain transition hook function 8393 * 8394 * The returned function invokes the (for instance) state.onEnter hook when the 8395 * state is being entered. 8396 */ 8397 function makeEnterExitRetainHook(hookName) { 8398 return function (transition, state) { 8399 var _state = state.$$state(); 8400 var hookFn = _state[hookName]; 8401 return hookFn(transition, state); 8402 }; 8403 } 8404 /** 8405 * The [[TransitionStateHookFn]] for onExit 8406 * 8407 * When the state is being exited, the state's .onExit function is invoked. 8408 * 8409 * Registered using `transitionService.onExit({ exiting: (state) => !!state.onExit }, onExitHook);` 8410 * 8411 * See: [[IHookRegistry.onExit]] 8412 */ 8413 var onExitHook = makeEnterExitRetainHook('onExit'); 8414 var registerOnExitHook = function (transitionService) { 8415 return transitionService.onExit({ exiting: function (state) { return !!state.onExit; } }, onExitHook); 8416 }; 8417 /** 8418 * The [[TransitionStateHookFn]] for onRetain 8419 * 8420 * When the state was already entered, and is not being exited or re-entered, the state's .onRetain function is invoked. 8421 * 8422 * Registered using `transitionService.onRetain({ retained: (state) => !!state.onRetain }, onRetainHook);` 8423 * 8424 * See: [[IHookRegistry.onRetain]] 8425 */ 8426 var onRetainHook = makeEnterExitRetainHook('onRetain'); 8427 var registerOnRetainHook = function (transitionService) { 8428 return transitionService.onRetain({ retained: function (state) { return !!state.onRetain; } }, onRetainHook); 8429 }; 8430 /** 8431 * The [[TransitionStateHookFn]] for onEnter 8432 * 8433 * When the state is being entered, the state's .onEnter function is invoked. 8434 * 8435 * Registered using `transitionService.onEnter({ entering: (state) => !!state.onEnter }, onEnterHook);` 8436 * 8437 * See: [[IHookRegistry.onEnter]] 8438 */ 8439 var onEnterHook = makeEnterExitRetainHook('onEnter'); 8440 var registerOnEnterHook = function (transitionService) { 8441 return transitionService.onEnter({ entering: function (state) { return !!state.onEnter; } }, onEnterHook); 8442 }; 8443 8444 var RESOLVE_HOOK_PRIORITY = 1000; 8445 /** 8446 * A [[TransitionHookFn]] which resolves all EAGER Resolvables in the To Path 8447 * 8448 * Registered using `transitionService.onStart({}
8448, eagerResolvePath, { priority: 1000 });` 8449 * 8450 * When a Transition starts, this hook resolves all the EAGER Resolvables, which the transition then waits for. 8451 * 8452 * See [[StateDeclaration.resolve]] 8453 */ 8454 var eagerResolvePath = function (trans) { 8455 return new ResolveContext(trans.treeChanges().to).resolvePath('EAGER', trans).then(noop); 8456 }; 8457 var registerEagerResolvePath = function (transitionService) { 8458 return transitionService.onStart({}, eagerResolvePath, { priority: RESOLVE_HOOK_PRIORITY }); 8459 }; 8460 /** 8461 * A [[TransitionHookFn]] which resolves all LAZY Resolvables for the state (and all its ancestors) in the To Path 8462 * 8463 * Registered using `transitionService.onEnter({ entering: () => true }, lazyResolveState, { priority: 1000 });` 8464 * 8465 * When a State is being entered, this hook resolves all the Resolvables for this state, which the transition then waits for. 8466 * 8467 * See [[StateDeclaration.resolve]] 8468 */ 8469 var lazyResolveState = function (trans, state) { 8470 return new ResolveContext(trans.treeChanges().to).subContext(state.$$state()).resolvePath('LAZY', trans).then(noop); 8471 }; 8472 var registerLazyResolveState = function (transitionService) { 8473 return transitionService.onEnter({ entering: val(true) }, lazyResolveState, { priority: RESOLVE_HOOK_PRIORITY }); 8474 }; 8475 /** 8476 * A [[TransitionHookFn]] which resolves any dynamically added (LAZY or EAGER) Resolvables. 8477 * 8478 * Registered using `transitionService.onFinish({}
8478, eagerResolvePath, { priority: 1000 });` 8479 * 8480 * After all entering states have been entered, this hook resolves any remaining Resolvables. 8481 * These are typically dynamic resolves which were added by some Transition Hook using [[Transition.addResolvable]]. 8482 * 8483 * See [[StateDeclaration.resolve]] 8484 */ 8485 var resolveRemaining = function (trans) { 8486 return new ResolveContext(trans.treeChanges().to).resolvePath('LAZY', trans).then(noop); 8487 }; 8488 var registerResolveRemaining = function (transitionService) { 8489 return transitionService.onFinish({}, resolveRemaining, { priority: RESOLVE_HOOK_PRIORITY }); 8490 }; 8491 8492 /** 8493 * A [[TransitionHookFn]] which waits for the views to load 8494 * 8495 * Registered using `transitionService.onStart({}, loadEnteringViews);` 8496 * 8497 * Allows the views to do async work in [[ViewConfig.load]] before the transition continues. 8498 * In angular 1, this includes loading the templates. 8499 */ 8500 var loadEnteringViews = function (transition) { 8501 var $q = services.$q; 8502 var enteringViews = transition.views('entering'); 8503 if (!enteringViews.length) 8504 return; 8505 return $q.all(enteringViews.map(function (view) { return $q.when(view.load()); })).then(noop); 8506 }; 8507 var registerLoadEnteringViews = function (transitionService) { 8508 return transitionService.onFinish({}, loadEnteringViews); 8509 }; 8510 /** 8511 * A [[TransitionHookFn]] which activates the new views when a transition is successful. 8512 * 8513 * Registered using `transitionService.onSuccess({}, activateViews);` 8514 * 8515 * After a transition is complete, this hook deactivates the old views from the previous state, 8516 * and activates the new views from the destination state. 8517 * 8518 * See [[ViewService]] 8519 */ 8520 var activateViews = function (transition) { 8521 var enteringViews = transition.views('entering'); 8522 var exitingViews = transition.views('exiting'); 8523 if (!enteringViews.length && !exitingViews.length) 8524 return; 8525 var $view = transition.router.viewService; 8526 exitingViews.forEach(function (vc) { return $view.deactivateViewConfig(vc); }); 8527 enteringViews.forEach(function (vc) { return $view.activateViewConfig(vc); }); 8528 $view.sync(); 8529 }; 8530 var registerActivateViews = function (transitionService) { 8531 return transitionService.onSuccess({}, activateViews); 8532 }; 8533 8534 /** 8535 * A [[TransitionHookFn]] which updates global UI-Router state 8536 * 8537 * Registered using `transitionService.onBefore({}, updateGlobalState);` 8538 * 8539 * Before a [[Transition]] starts, updates the global value of "the current transition" ([[Globals.transition]]). 8540 * After a successful [[Transition]], updates the global values of "the current state" 8541 * ([[Globals.current]] and [[Globals.$current]]) and "the current param values" ([[Globals.params]]). 8542 * 8543 * See also the deprecated properties: 8544 * [[StateService.transition]], [[StateService.current]], [[StateService.params]] 8545 */ 8546 var updateGlobalState = function (trans) { 8547 var globals = trans.router.globals; 8548 var transitionSuccessful = function () { 8549 globals.successfulTransitions.enqueue(trans); 8550 globals.$current = trans.$to(); 8551 globals.current = globals.$current.self; 8552 copy(trans.params(), globals.params); 8553 }; 8554 var clearCurrentTransition = function () { 8555 // Do not clear globals.transition if a different transition has started in the meantime 8556 if (globals.transition === trans) 8557 globals.transition = null; 8558 }; 8559 trans.onSuccess({}, transitionSuccessful, { priority: 10000 }); 8560 trans.promise.then(clearCurrentTransition, clearCurrentTransition); 8561 }; 8562 var registerUpdateGlobalState = function (transitionService) { 8563 return transitionService.onCreate({}, updateGlobalState); 8564 }; 8565 8566 /** 8567 * A [[TransitionHookFn]] which updates the URL after a successful transition 8568 * 8569 * Registered using `transitionService.onSuccess({}, updateUrl);` 8570 */ 8571 var updateUrl = function (transition) { 8572 var options = transition.options(); 8573 var $state = transition.router.stateService; 8574 var $urlRouter = transition.router.urlRouter; 8575 // Dont update the url in these situations: 8576 // The transition was triggered by a URL sync (options.source === 'url') 8577 // The user doesn't want the url to update (options.location === false) 8578 // The destination state, and all parents have no navigable url 8579 if (options.source !== 'url' && options.location && $state.$current.navigable) { 8580 var urlOptions = { replace: options.location === 'replace' }; 8581 $urlRouter.push($state.$current.navigable.url, $state.params, urlOptions); 8582 } 8583 $urlRouter.update(true); 8584 }; 8585 var registerUpdateUrl = function (transitionService) { 8586 return transitionService.onSuccess({}, updateUrl, { priority: 9999 }); 8587 }; 8588 8589 /** 8590 * A [[TransitionHookFn]] that performs lazy loading 8591 * 8592 * When entering a state "abc" which has a `lazyLoad` function defined: 8593 * - Invoke the `lazyLoad` function (unless it is already in process) 8594 * - Flag the hook function as "in process" 8595 * - The function should return a promise (that resolves when lazy loading is complete) 8596 * - Wait for the promise to settle 8597 * - If the promise resolves to a [[LazyLoadResult]], then register those states 8598 * - Flag the hook function as "not in process" 8599 * - If the hook was successful 8600 * - Remove the `lazyLoad` function from the state declaration 8601 * - If all the hooks were successful
8602 * - Retry the transition (by returning a TargetState) 8603 * 8604 * ``` 8605 * .state('abc', { 8606 * component: 'fooComponent', 8607 * lazyLoad: () => import('./fooComponent') 8608 * }); 8609 * ``` 8610 * 8611 * See [[StateDeclaration.lazyLoad]] 8612 */ 8613 var lazyLoadHook = function (transition) { 8614 var router = transition.router; 8615 function retryTransition() { 8616 if (transition.originalTransition().options().source !== 'url') { 8617 // The original transition was not triggered via url sync 8618 // The lazy state should be loaded now, so re-try the original transition 8619 var orig = transition.targetState(); 8620 return router.stateService.target(orig.identifier(), orig.params(), orig.options()); 8621 } 8622 // The original transition was triggered via url sync 8623 // Run the URL rules and find the best match 8624 var $url = router.urlService; 8625 var result = $url.match($url.parts()); 8626 var rule = result && result.rule; 8627 // If the best match is a state, redirect the transition (instead 8628 // of calling sync() which supersedes the current transition) 8629 if (rule && rule.type === 'STATE') { 8630 var state = rule.state; 8631 var params = result.match; 8632 return router.stateService.target(state, params, transition.options()); 8633 } 8634 // No matching state found, so let .sync() choose the best non-state match/otherwise 8635 router.urlService.sync(); 8636 } 8637 var promises = transition 8638 .entering() 8639 .filter(function (state) { return !!state.$$state().lazyLoad; }) 8640 .map(function (state) { return lazyLoadState(transition, state); }); 8641 return services.$q.all(promises).then(retryTransition); 8642 }; 8643 var registerLazyLoadHook = function (transitionService) { 8644 return transitionService.onBefore({ entering: function (state) { return !!state.lazyLoad; } }, lazyLoadHook); 8645 }; 8646 /** 8647 * Invokes a state's lazy load function 8648 * 8649 * @param transition a Transition context 8650 * @param state the state to lazy load 8651 * @returns A promise for the lazy load result 8652 */ 8653 function lazyLoadState(transition, state) { 8654 var lazyLoadFn = state.$$state().lazyLoad; 8655 // Store/get the lazy load promise on/from the hookfn so it doesn't get re-invoked 8656 var promise = lazyLoadFn['_promise']; 8657 if (!promise) { 8658 var success = function (result) { 8659 delete state.lazyLoad; 8660 delete state.$$state().lazyLoad; 8661 delete lazyLoadFn['_promise']; 8662 return result; 8663 }; 8664 var error = function (err) { 8665 delete lazyLoadFn['_promise']; 8666 return services.$q.reject(err); 8667 }; 8668 promise = lazyLoadFn['_promise'] = services.$q 8669 .when(lazyLoadFn(transition, state)) 8670 .then(updateStateRegistry) 8671 .then(success, error); 8672 } 8673 /** Register any lazy loaded state definitions */ 8674 function updateStateRegistry(result) { 8675 if (result && Array.isArray(result.states)) { 8676 result.states.forEach(function (_state) { return transition.router.stateRegistry.register(_state); }); 8677 } 8678 return result; 8679 } 8680 return promise; 8681 } 8682 8683 /** 8684 * This class defines a type of hook, such as `onBefore` or `onEnter`. 8685 * Plugins can define custom hook types, such as sticky states does for `onInactive`. 8686 */ 8687 var TransitionEventType = /** @class */ (function () { 8688 /* tslint:disable:no-inferrable-types */ 8689 function TransitionEventType(name, hookPhase, hookOrder, criteriaMatchPath, reverseSort, getResultHandler, getErrorHandler, synchronous) { 8690 if (reverseSort === void 0) { reverseSort = false; } 8691 if (getResultHandler === void 0) { getResultHandler = TransitionHook.HANDLE_RESULT; } 8692 if (getErrorHandler === void 0) { getErrorHandler = TransitionHook.REJECT_ERROR; } 8693 if (synchronous === void 0) { synchronous = false; } 8694 this.name = name; 8695 this.hookPhase = hookPhase; 8696 this.hookOrder = hookOrder; 8697 this.criteriaMatchPath = criteriaMatchPath; 8698 this.reverseSort = reverseSort; 8699 this.getResultHandler = getResultHandler; 8700 this.getErrorHandler = getErrorHandler; 8701 this.synchronous = synchronous; 8702 } 8703 return TransitionEventType; 8704 }()); 8705 8706 /** 8707 * A [[TransitionHookFn]] that skips a transition if it should be ignored 8708 * 8709 * This hook is invoked at the end of the onBefore phase. 8710 * 8711 * If the transition should be ignored (because no parameter or states changed) 8712 * then the transition is ignored and not processed. 8713 */ 8714 function ignoredHook(trans) { 8715 var ignoredReason = trans._ignoredReason(); 8716 if (!ignoredReason) 8717 return; 8718 trace.traceTransitionIgnored(trans); 8719 var pending = trans.router.globals.transition; 8720 // The user clicked a link going back to the *current state* ('A')
8721 // However, there is also a pending transition in flight (to 'B') 8722 // Abort the transition to 'B' because the user now wants to be back at 'A'. 8723 if (ignoredReason === 'SameAsCurrent' && pending) { 8724 pending.abort(); 8725 } 8726 return Rejection.ignored().toPromise(); 8727 } 8728 var registerIgnoredTransitionHook = function (transitionService) { 8729 return transitionService.onBefore({}, ignoredHook, { priority: -9999 }); 8730 }; 8731 8732 /** 8733 * A [[TransitionHookFn]] that rejects the Transition if it is invalid 8734 * 8735 * This hook is invoked at the end of the onBefore phase. 8736 * If the transition is invalid (for example, param values do not validate) 8737 * then the transition is rejected. 8738 */ 8739 function invalidTransitionHook(trans) { 8740 if (!trans.valid()) { 8741 throw new Error(trans.error().toString()); 8742 } 8743 } 8744 var registerInvalidTransitionHook = function (transitionService) { 8745 return transitionService.onBefore({}, invalidTransitionHook, { priority: -10000 }); 8746 }; 8747 8748 /** 8749 * The default [[Transition]] options. 8750 * 8751 * Include this object when applying custom defaults: 8752 * let reloadOpts = { reload: true, notify: true } 8753 * let options = defaults(theirOpts, customDefaults, defaultOptions); 8754 */ 8755 var defaultTransOpts = { 8756 location: true, 8757 relative: null, 8758 inherit: false, 8759 notify: true, 8760 reload: false, 8761 supercede: true, 8762 custom: {}, 8763 current: function () { return null; }, 8764 source: 'unknown', 8765 }; 8766 /** 8767 * This class provides services related to Transitions. 8768 * 8769 * - Most importantly, it allows global Transition Hooks to be registered. 8770 * - It allows the default transition error handler to be set. 8771 * - It also has a factory function for creating new [[Transition]] objects, (used internally by the [[StateService]]). 8772 * 8773 * At bootstrap, [[UIRouter]] creates a single instance (singleton) of this class. 8774 * 8775 * This API is located at `router.transitionService` ([[UIRouter.transitionService]]) 8776 */ 8777 var TransitionService = /** @class */ (function () { 8778 /** @internal */ 8779 function TransitionService(_router) { 8780 /** @internal */ 8781 this._transitionCount = 0; 8782 /** The transition hook types, such as `onEnter`, `onStart`, etc */ 8783 this._eventTypes = []; 8784 /** @internal The registered transition hooks */ 8785 this._registeredHooks = {}; 8786 /** The paths on a criteria object */ 8787 this._criteriaPaths = {}; 8788 this._router = _router; 8789 this.$view = _router.viewService; 8790 this._deregisterHookFns = {}; 8791 this._pluginapi = (createProxyFunctions(val(this), {}, val(this), [ 8792 '_definePathType', 8793 '_defineEvent', 8794 '_getPathTypes', 8795 '_getEvents', 8796 'getHooks', 8797 ])); 8798 this._defineCorePaths(); 8799 this._defineCoreEvents(); 8800 this._registerCoreTransitionHooks(); 8801 _router.globals.successfulTransitions.onEvict(treeChangesCleanup); 8802 } 8803 /** 8804 * Registers a [[TransitionHookFn]], called *while a transition is being constructed*. 8805 * 8806 * Registers a transition lifecycle hook, which is invoked during transition construction. 8807 * 8808 * This low level hook should only be used by plugins. 8809 * This can be a useful time for plugins to add resolves or mutate the transition as needed. 8810 * The Sticky States plugin uses this hook to modify the treechanges. 8811 * 8812 * ### Lifecycle 8813 * 8814 * `onCreate` hooks are invoked *while a transition is being constructed*. 8815 * 8816 * ### Return value 8817 * 8818 * The hook's return value is ignored 8819 * 8820 * @internal 8821 * @param criteria defines which Transitions the Hook should be invoked for. 8822 * @param callback the hook function which will be invoked. 8823 * @param options the registration options 8824 * @returns a function which deregisters the hook. 8825 */ 8826 TransitionService.prototype.onCreate = function (criteria, callback, options) { 8827 return; 8828 }; 8829 /** @inheritdoc */ 8830 TransitionService.prototype.onBefore = function (criteria, callback, options) { 8831 return; 8832 }; 8833 /** @inheritdoc */ 8834 TransitionService.prototype.onStart = function (criteria, callback, options) { 8835 return; 8836 }; 8837 /** @inheritdoc */ 8838 TransitionService.prototype.onExit = function (criteria, callback, options) { 8839 return; 8840 }; 8841 /** @inheritdoc */ 8842 TransitionService.prototype.onRetain = function (criteria, callback, options) { 8843 return; 8844 }; 8845 /** @inheritdoc */ 8846 TransitionService.prototype.onEnter = function (criteria, callback, options) { 8847 return; 8848 }; 8849 /** @inheritdoc */ 8850 TransitionService.prototype.onFinish = function (criteria, callback, options) { 8851 return; 8852 }; 8853 /** @inheritdoc */ 8854 TransitionService.prototype.onSuccess = function (criteria, callback, options) { 8855 return; 8856 }; 8857 /** @inheritdoc */ 8858 TransitionService.prototype.onError = function (criteria, callback, options) { 8859 return; 8860 }; 8861 /** 8862 * dispose 8863 * @internal 8864 */ 8865 TransitionService.prototype.dispose = function (router) {
8866 values(this._registeredHooks).forEach(function (hooksArray) { 8867 return hooksArray.forEach(function (hook) { 8868 hook._deregistered = true; 8869 removeFrom(hooksArray, hook); 8870 }); 8871 }); 8872 }; 8873 /** 8874 * Creates a new [[Transition]] object 8875 * 8876 * This is a factory function for creating new Transition objects. 8877 * It is used internally by the [[StateService]] and should generally not be called by application code. 8878 * 8879 * @internal 8880 * @param fromPath the path to the current state (the from state) 8881 * @param targetState the target state (destination) 8882 * @returns a Transition 8883 */ 8884 TransitionService.prototype.create = function (fromPath, targetState) { 8885 return new Transition(fromPath, targetState, this._router); 8886 }; 8887 /** @internal */ 8888 TransitionService.prototype._defineCoreEvents = function () { 8889 var Phase = exports.TransitionHookPhase; 8890 var TH = TransitionHook; 8891 var paths = this._criteriaPaths; 8892 var NORMAL_SORT = false, REVERSE_SORT = true; 8893 var SYNCHRONOUS = true; 8894 this._defineEvent('onCreate', Phase.CREATE, 0, paths.to, NORMAL_SORT, TH.LOG_REJECTED_RESULT, TH.THROW_ERROR, SYNCHRONOUS); 8895 this._defineEvent('onBefore', Phase.BEFORE, 0, paths.to); 8896 this._defineEvent('onStart', Phase.RUN, 0, paths.to); 8897 this._defineEvent('onExit', Phase.RUN, 100, paths.exiting, REVERSE_SORT); 8898 this._defineEvent('onRetain', Phase.RUN, 200, paths.retained); 8899 this._defineEvent('onEnter', Phase.RUN, 300, paths.entering); 8900 this._defineEvent('onFinish', Phase.RUN, 400, paths.to); 8901 this._defineEvent('onSuccess', Phase.SUCCESS, 0, paths.to, NORMAL_SORT, TH.LOG_REJECTED_RESULT, TH.LOG_ERROR, SYNCHRONOUS); 8902 this._defineEvent('onError', Phase.ERROR, 0, paths.to, NORMAL_SORT, TH.LOG_REJECTED_RESULT, TH.LOG_ERROR, SYNCHRONOUS); 8903 }; 8904 /** @internal */ 8905 TransitionService.prototype._defineCorePaths = function () { 8906 var STATE = exports.TransitionHookScope.STATE, TRANSITION = exports.TransitionHookScope.TRANSITION; 8907 this._definePathType('to', TRANSITION); 8908 this._definePathType('from', TRANSITION); 8909 this._definePathType('exiting', STATE); 8910 this._definePathType('retained', STATE); 8911 this._definePathType('entering', STATE); 8912 }; 8913 /** @internal */ 8914 TransitionService.prototype._defineEvent = function (name, hookPhase, hookOrder, criteriaMatchPath, reverseSort, getResultHandler, getErrorHandler, synchronous) { 8915 if (reverseSort === void 0) { reverseSort = false; } 8916 if (getResultHandler === void 0) { getResultHandler = TransitionHook.HANDLE_RESULT; } 8917 if (getErrorHandler === void 0) { getErrorHandler = TransitionHook.REJECT_ERROR; } 8918 if (synchronous === void 0) { synchronous = false; } 8919 var eventType = new TransitionEventType(name, hookPhase, hookOrder, criteriaMatchPath, reverseSort, getResultHandler, getErrorHandler, synchronous); 8920 this._eventTypes.push(eventType); 8921 makeEvent(this, this, eventType); 8922 }; 8923 /** @internal */ 8924 TransitionService.prototype._getEvents = function (phase) { 8925 var transitionHookTypes = isDefined(phase) 8926 ? this._eventTypes.filter(function (type) { return type.hookPhase === phase; }) 8927 : this._eventTypes.slice(); 8928 return transitionHookTypes.sort(function (l, r) { 8929 var cmpByPhase = l.hookPhase - r.hookPhase; 8930 return cmpByPhase === 0 ? l.hookOrder - r.hookOrder : cmpByPhase; 8931 }); 8932 }; 8933 /** 8934 * Adds a Path to be used as a criterion against a TreeChanges path 8935 * 8936 * For example: the `exiting` path in [[HookMatchCriteria]] is a STATE scoped path. 8937 * It was defined by calling `defineTreeChangesCriterion('exiting', TransitionHookScope.STATE)` 8938 * Each state in the exiting path is checked against the criteria and returned as part of the match. 8939 * 8940 * Another example: the `to` path in [[HookMatchCriteria]] is a TRANSITION scoped path. 8941 * It was defined by calling `defineTreeChangesCriterion('to', TransitionHookScope.TRANSITION)` 8942 * Only the tail of the `to` path is checked against the criteria and returned as part of the match. 8943 * 8944 * @internal 8945 */ 8946 TransitionService.prototype._definePathType = function (name, hookScope) { 8947 this._criteriaPaths[name] = { name: name, scope: hookScope }; 8948 }; 8949 /** @internal */ 8950 // tslint:disable-next-line 8951 TransitionService.prototype._getPathTypes = function () { 8952 return this._criteriaPaths; 8953 }; 8954 /** @internal */
8955 TransitionService.prototype.getHooks = function (hookName) { 8956 return this._registeredHooks[hookName]; 8957 }; 8958 /** @internal */ 8959 TransitionService.prototype._registerCoreTransitionHooks = function () { 8960 var fns = this._deregisterHookFns; 8961 fns.addCoreResolves = registerAddCoreResolvables(this); 8962 fns.ignored = registerIgnoredTransitionHook(this); 8963 fns.invalid = registerInvalidTransitionHook(this); 8964 // Wire up redirectTo hook 8965 fns.redirectTo = registerRedirectToHook(this); 8966 // Wire up onExit/Retain/Enter state hooks 8967 fns.onExit = registerOnExitHook(this); 8968 fns.onRetain = registerOnRetainHook(this); 8969 fns.onEnter = registerOnEnterHook(this); 8970 // Wire up Resolve hooks 8971 fns.eagerResolve = registerEagerResolvePath(this); 8972 fns.lazyResolve = registerLazyResolveState(this); 8973 fns.resolveAll = registerResolveRemaining(this); 8974 // Wire up the View management hooks 8975 fns.loadViews = registerLoadEnteringViews(this); 8976 fns.activateViews = registerActivateViews(this); 8977 // Updates global state after a transition 8978 fns.updateGlobals = registerUpdateGlobalState(this); 8979 // After globals.current is updated at priority: 10000 8980 fns.updateUrl = registerUpdateUrl(this); 8981 // Lazy load state trees 8982 fns.lazyLoad = registerLazyLoadHook(this); 8983 }; 8984 return TransitionService; 8985 }()); 8986 8987 /** 8988 * Provides services related to ui-router states. 8989 * 8990 * This API is located at `router.stateService` ([[UIRouter.stateService]]) 8991 */ 8992 var StateService = /** @class */ (function () { 8993 /** @internal */ 8994 function StateService(/** @internal */ router) { 8995 this.router = router; 8996 /** @internal */ 8997 this.invalidCallbacks = []; 8998 /** @internal */ 8999 this._defaultErrorHandler = function $defaultErrorHandler($error$) { 9000 if ($error$ instanceof Error && $error$.stack) { 9001 console.error($error$); 9002 console.error($error$.stack); 9003 } 9004 else if ($error$ instanceof Rejection) { 9005 console.error($error$.toString()); 9006 if ($error$.detail && $error$.detail.stack) 9007 console.error($error$.detail.stack); 9008 } 9009 else { 9010 console.error($error$); 9011 } 9012 }; 9013 var getters = ['current', '$current', 'params', 'transition']; 9014 var boundFns = Object.keys(StateService.prototype).filter(not(inArray(getters))); 9015 createProxyFunctions(val(StateService.prototype), this, val(this), boundFns); 9016 } 9017 Object.defineProperty(StateService.prototype, "transition", { 9018 /** 9019 * The [[Transition]] currently in progress (or null) 9020 * 9021 * @deprecated This is a passthrough through to [[UIRouterGlobals.transition]] 9022 */ 9023 get: function () { 9024 return this.router.globals.transition; 9025 }, 9026 enumerable: false, 9027 configurable: true 9028 }); 9029 Object.defineProperty(StateService.prototype, "params", { 9030 /** 9031 * The latest successful state parameters 9032 * 9033 * @deprecated This is a passthrough through to [[UIRouterGlobals.params]] 9034 */ 9035 get: function () { 9036 return this.router.globals.params; 9037 }, 9038 enumerable: false, 9039 configurable: true 9040 }); 9041 Object.defineProperty(StateService.prototype, "current", { 9042 /** 9043 * The current [[StateDeclaration]] 9044 * 9045 * @deprecated This is a passthrough through to [[UIRouterGlobals.current]] 9046 */ 9047 get: function () { 9048 return this.router.globals.current; 9049 }, 9050 enumerable: false, 9051 configurable: true 9052 }); 9053 Object.defineProperty(StateService.prototype, "$current", { 9054 /** 9055 * The current [[StateObject]] (an internal API) 9056 * 9057 * @deprecated This is a passthrough through to [[UIRouterGlobals.$current]] 9058 */ 9059 get: function () { 9060 return this.router.globals.$current; 9061 }, 9062 enumerable: false, 9063 configurable: true 9064 }); 9065 /** @internal */ 9066 StateService.prototype.dispose = function () { 9067 this.defaultErrorHandler(noop); 9068 this.invalidCallbacks = []; 9069 }; 9070 /** 9071 * Handler for when [[transitionTo]] is called with an invali
9071d state. 9072 * 9073 * Invokes the [[onInvalid]] callbacks, in natural order. 9074 * Each callback's return value is checked in sequence until one of them returns an instance of TargetState. 9075 * The results of the callbacks are wrapped in $q.when(), so the callbacks may return promises. 9076 * 9077 * If a callback returns an TargetState, then it is used as arguments to $state.transitionTo() and the result returned. 9078 * 9079 * @internal 9080 */ 9081 StateService.prototype._handleInvalidTargetState = function (fromPath, toState) { 9082 var _this = this; 9083 var fromState = PathUtils.makeTargetState(this.router.stateRegistry, fromPath); 9084 var globals = this.router.globals; 9085 var latestThing = function () { return globals.transitionHistory.peekTail(); }; 9086 var latest = latestThing(); 9087 var callbackQueue = new Queue(this.invalidCallbacks.slice()); 9088 var injector = new ResolveContext(fromPath).injector(); 9089 var checkForRedirect = function (result) { 9090 if (!(result instanceof TargetState)) { 9091 return; 9092 } 9093 var target = result; 9094 // Recreate the TargetState, in case the state is now defined. 9095 target = _this.target(target.identifier(), target.params(), target.options()); 9096 if (!target.valid()) { 9097 return Rejection.invalid(target.error()).toPromise(); 9098 } 9099 if (latestThing() !== latest) { 9100 return Rejection.superseded().toPromise(); 9101 } 9102 return _this.transitionTo(target.identifier(), target.params(), target.options()); 9103 }; 9104 function invokeNextCallback() { 9105 var nextCallback = callbackQueue.dequeue(); 9106 if (nextCallback === undefined) 9107 return Rejection.invalid(toState.error()).toPromise(); 9108 var callbackResult = services.$q.when(nextCallback(toState, fromState, injector)); 9109 return callbackResult.then(checkForRedirect).then(function (result) { return result || invokeNextCallback(); }); 9110 } 9111 return invokeNextCallback(); 9112 }; 9113 /** 9114 * Registers an Invalid State handler 9115 * 9116 * Registers a [[OnInvalidCallback]] function to be invoked when [[StateService.transitionTo]] 9117 * has been called with an invalid state reference parameter 9118 * 9119 * Example: 9120 * ```js 9121 * stateService.onInvalid(function(to, from, injector) { 9122 * if (to.name() === 'foo') { 9123 * let lazyLoader = injector.get('LazyLoadService'); 9124 * return lazyLoader.load('foo') 9125 * .then(() => stateService.target('foo')); 9126 * } 9127 * }); 9128 * ``` 9129 * 9130 * @param {function} callback invoked when the toState is invalid 9131 * This function receives the (invalid) toState, the fromState, and an injector. 9132 * The function may optionally return a [[TargetState]] or a Promise for a TargetState. 9133 * If one is returned, it is treated as a redirect. 9134 * 9135 * @returns a function which deregisters the callback 9136 */ 9137 StateService.prototype.onInvalid = function (callback) { 9138 this.invalidCallbacks.push(callback); 9139 return function deregisterListener() { 9140 removeFrom(this.invalidCallbacks)(callback); 9141 }.bind(this); 9142 }; 9143 /** 9144 * Reloads the current state 9145 * 9146 * A method that force reloads the current state, or a partial state hierarchy. 9147 * All resolves are re-resolved, and components reinstantiated. 9148 * 9149 * #### Example: 9150 * ```js 9151 * let app angular.module('app', ['ui.router']); 9152 * 9153 * app.controller('ctrl', function ($scope, $state) { 9154 * $scope.reload = function(){ 9155 * $state.reload(); 9156 * } 9157 * }); 9158 * ``` 9159 * 9160 * Note: `reload()` is just an alias for: 9161 * 9162 * ```js 9163 * $state.transitionTo($state.current, $state.params, { 9164 * reload: true, inherit: false 9165 * }); 9166 * ``` 9167 * 9168 * @param reloadState A state name or a state object. 9169 * If present, this state and all its children will be reloaded, but ancestors will not reload. 9170 * 9171 * #### Example: 9172 * ```js 9173 * //assuming app application consists of 3 states: 'contacts', 'contacts.detail', 'contacts.detail.item' 9174 * //and current state is 'contacts.detail.item' 9175 * let app angular.module('app', ['ui.router']); 9176 * 9177 * app.controller('ctrl', function ($scope, $state) { 9178 * $scope.reload = function(){ 9179 * //will reload 'contact.detail' and nested 'contact.detail.item' states 9180 * $state.reload('contact.detail'); 9181 * } 9182 * }); 9183 * ``` 9184 * 9185 * @returns A promise representing the state of the new transition. See [[StateService.go]] 9186 */ 9187 StateService.prototype.reload = function (reloadState) { 9188 return this.transitionTo(this.current, this.params, { 9189 reload: isDefined(reloadState) ? reloadState : true, 9190 inherit: false, 9191 notify: false, 9192 }); 9193 }; 9194 /** 9195 * Transition to a different state and/or parameters 9196 * 9197 * Convenience method for transitioning to a new state. 9198 * 9199 * `$state.go` calls `$state.transitionTo` internally but automatically sets options to 9200 * `{ location: true, inherit: true, relative: router.globals.$current, notify: true }`. 9201 * This allows you to use either an absolute or relative `to` argument (because of `relative: router.globals.$current`). 9202 * It also allows you to specify * only the parameters you'd like to update, while letting unspecified parameters 9203 * inherit from the current parameter values (because of `inherit: true`). 9204 * 9205 * #### Example: 9206 * ```js 9207 * let app = angular.module('app', ['ui.router']); 9208 * 9209 * app.controller('ctrl', function ($scope, $state) { 9210 * $scope.changeState = function () { 9211 * $state.go('contact.detail'); 9212 * }; 9213 * }); 9214 * ``` 9215 * 9216 * @param to Absolute state name, state object, or relative state path (relative to current state). 9217 * 9218 * Some examples: 9219 * 9220 * - `$state.go('contact.detail')` - will go to the `contact.detail` state 9221 * - `$state.go('^')` - will go to the parent state 9222 * - `$state.go('^.sibling')` - if current state is `home.child`, will go to the `home.sibling` state 9223 * - `$state.go('.child.grandchild')` - if current state is home, will go to the `home.child.grandchild` state 9224 * 9225 * @param params A map of the parameters that will be sent to the state, will populate $stateParams. 9226 * 9227 * Any parameters that are not specified will be inherited from current parameter values (because of `inherit: true`). 9228 * This allows, for example, going to a sibling state that shares parameters defined by a parent state. 9229 * 9230 * @param options Transition options 9231 * 9232 * @returns {promise} A promise representing the state of the new transition. 9233 */ 9234 StateService.prototype.go = function (to, params, options) { 9235 var defautGoOpts = { relative: this.$current, inherit: true }; 9236 var transOpts = defaults(options, defautGoOpts, defaultTransOpts); 9237 return this.transitionTo(to, params, transOpts); 9238 }; 9239 /**
9240 * Creates a [[TargetState]] 9241 * 9242 * This is a factory method for creating a TargetState 9243 * 9244 * This may be returned from a Transition Hook to redirect a transition, for example. 9245 */ 9246 StateService.prototype.target = function (identifier, params, options) { 9247 if (options === void 0) { options = {}; } 9248 // If we're reloading, find the state object to reload from 9249 if (isObject(options.reload) && !options.reload.name) 9250 throw new Error('Invalid reload state object'); 9251 var reg = this.router.stateRegistry; 9252 options.reloadState = 9253 options.reload === true ? reg.root() : reg.matcher.find(options.reload, options.relative); 9254 if (options.reload && !options.reloadState) 9255 throw new Error("No such reload state '" + (isString(options.reload) ? options.reload : options.reload.name) + "'"); 9256 return new TargetState(this.router.stateRegistry, identifier, params, options); 9257 }; 9258 /** @internal */ 9259 StateService.prototype.getCurrentPath = function () { 9260 var _this = this; 9261 var globals = this.router.globals; 9262 var latestSuccess = globals.successfulTransitions.peekTail(); 9263 var rootPath = function () { return [new PathNode(_this.router.stateRegistry.root())]; }; 9264 return latestSuccess ? latestSuccess.treeChanges().to : rootPath(); 9265 }; 9266 /** 9267 * Low-level method for transitioning to a new state. 9268 * 9269 * The [[go]] method (which uses `transitionTo` internally) is recommended in most situations. 9270 * 9271 * #### Example: 9272 * ```js 9273 * let app = angular.module('app', ['ui.router']); 9274 * 9275 * app.controller('ctrl', function ($scope, $state) { 9276 * $scope.changeState = function () { 9277 * $state.transitionTo('contact.detail'); 9278 * }; 9279 * }); 9280 * ``` 9281 * 9282 * @param to State name or state object. 9283 * @param toParams A map of the parameters that will be sent to the state, 9284 * will populate $stateParams. 9285 * @param options Transition options 9286 * 9287 * @returns A promise representing the state of the new transition. See [[go]] 9288 */ 9289 StateService.prototype.transitionTo = function (to, toParams, options) { 9290 var _this = this; 9291 if (toParams === void 0) { toParams = {}; } 9292 if (options === void 0) { options = {}; } 9293 var router = this.router; 9294 var globals = router.globals; 9295 options = defaults(options, defaultTransOpts); 9296 var getCurrent = function () { return globals.transition; }; 9297 options = extend(options, { current: getCurrent }); 9298 var ref = this.target(to, toParams, options); 9299 var currentPath = this.getCurrentPath(); 9300 if (!ref.exists()) 9301 return this._handleInvalidTargetState(currentPath, ref); 9302 if (!ref.valid()) 9303 return silentRejection(ref.error()); 9304 if (options.supercede === false && getCurrent()) { 9305 return (Rejection.ignored('Another transition is in progress and supercede has been set to false in TransitionOptions for the transition. So the transition was ignored in favour of the existing one in progress.').toPromise()); 9306 } 9307 /** 9308 * Special handling for Ignored, Aborted, and Redirected transitions 9309 * 9310 * The semantics for the transition.run() promise and the StateService.transitionTo() 9311 * promise differ. For instance, the run() promise may be rejected because it was 9312 * IGNORED, but the transitionTo() promise is resolved because from the user perspective 9313 * no error occurred. Likewise, the transition.run() promise may be rejected because of 9314 * a Redirect, but the transitionTo() promise is chained to the new Transition's promise. 9315 */ 9316 var rejectedTransitionHandler = function (trans) { return function (error) { 9317 if (error instanceof Rejection) { 9318 var isLatest = router.globals.lastStartedTransitionId <= trans.$id; 9319 if (error.type === exports.RejectType.IGNORED) {
9320 isLatest && router.urlRouter.update(); 9321 // Consider ignored `Transition.run()` as a successful `transitionTo` 9322 return services.$q.when(globals.current); 9323 } 9324 var detail = error.detail; 9325 if (error.type === exports.RejectType.SUPERSEDED && error.redirected && detail instanceof TargetState) { 9326 // If `Transition.run()` was redirected, allow the `transitionTo()` promise to resolve successfully 9327 // by returning the promise for the new (redirect) `Transition.run()`. 9328 var redirect = trans.redirect(detail); 9329 return redirect.run().catch(rejectedTransitionHandler(redirect)); 9330 } 9331 if (error.type === exports.RejectType.ABORTED) { 9332 isLatest && router.urlRouter.update(); 9333 return services.$q.reject(error); 9334 } 9335 } 9336 var errorHandler = _this.defaultErrorHandler(); 9337 errorHandler(error); 9338 return services.$q.reject(error); 9339 }; }; 9340 var transition = this.router.transitionService.create(currentPath, ref); 9341 var transitionToPromise = transition.run().catch(rejectedTransitionHandler(transition)); 9342 silenceUncaughtInPromise(transitionToPromise); // issue #2676 9343 // Return a promise for the transition, which also has the transition object on it. 9344 return extend(transitionToPromise, { transition: transition }); 9345 }; 9346 /** 9347 * Checks if the current state *is* the provided state 9348 * 9349 * Similar to [[includes]] but only checks for the full state name. 9350 * If params is supplied then it will be tested for strict equality against the current 9351 * active params object, so all params must match with none missing and no extras. 9352 * 9353 * #### Example: 9354 * ```js 9355 * $state.$current.name = 'contacts.details.item'; 9356 * 9357 * // absolute name 9358 * $state.is('contact.details.item'); // returns true 9359 * $state.is(contactDetailItemStateObject); // returns true 9360 * ``` 9361 * 9362 * // relative name (. and ^), typically from a template 9363 * // E.g. from the 'contacts.details' template 9364 * ```html 9365 * <div ng-class="{highlighted: $state.is('.item')}">Item</div> 9366 * ``` 9367 * 9368 * @param stateOrName The state name (absolute or relative) or state object you'd like to check. 9369 * @param params A param object, e.g. `{sectionId: section.id}`, that you'd like 9370 * to test against the current active state. 9371 * @param options An options object. The options are: 9372 * - `relative`: If `stateOrName` is a relative state name and `options.relative` is set, .is will 9373 * test relative to `options.relative` state (or name). 9374 * 9375 * @returns Returns true if it is the state. 9376 */ 9377 StateService.prototype.is = function (stateOrName, params, options) { 9378 options = defaults(options, { relative: this.$current }); 9379 var state = this.router.stateRegistry.matcher.find(stateOrName, options.relative); 9380 if (!isDefined(state)) 9381 return undefined; 9382 if (this.$current !== state) 9383 return false; 9384 if (!params) 9385 return true; 9386 var schema = state.parameters({ inherit: true, matchingKeys: params }); 9387 return Param.equals(schema, Param.values(schema, params), this.params); 9388 }; 9389 /** 9390 * Checks if the current state *includes* the provided state 9391 * 9392 * A method to determine if the current active state is equal to or is the child of the 9393 * state stateName. If any params are passed then they will be tested for a match as well. 9394 * Not all the parameters need to be passed, just the ones you'd like to test for equality. 9395 * 9396 * #### Example when `$state.$current.name === 'contacts.details.item'` 9397 * ```js 9398 * // Using partial names 9399 * $state.includes("contacts"); // returns true 9400 * $state.includes("contacts.details"); // returns true 9401 * $state.includes("contacts.details.item"); // returns true 9402 * $state.includes("contacts.list"); // returns false 9403 * $state.includes("about"); // returns false 9404 * ``` 9405 * 9406 * #### Glob Examples when `* $state.$current.name === 'contacts.details.item.url'`: 9407 * ```js 9408 * $state.includes("*.details.*.*"); // returns true 9409 * $state.includes("*.details.**"); // returns true 9410 * $state.includes("**.item.**"); // returns true 9411 * $state.includes("*.details.item.url"); // returns true 9412 * $state.includes("*.details.*.url"); // returns true 9413 * $state.includes("*.details.*"); // returns false 9414 * $state.includes("item.**"); // returns false 9415 * ``` 9416 * 9417 * @param stateOrName A partial name, relative name, glob pattern, 9418 * or state object to be searched for within the current state name. 9419 * @param params A param object, e.g. `{sectionId: section.id}`, 9420 * that you'd like to test against the current active state. 9421 * @param options An options object. The options are: 9422 * - `relative`: If `stateOrName` is a relative state name and `options.relative` is set, .is will 9423 * test relative to `options.relative` state (or name). 9424 * 9425 * @returns {boolean} Returns true if it does include the state 9426 */ 9427 StateService.prototype.includes = function (stateOrName, params, options) { 9428 options = defaults(options, { relative: this.$current }); 9429 var glob = isString(stateOrName) && Glob.fromString(stateOrName); 9430 if (glob) { 9431 if (!glob.matches(this.$current.name)) 9432 return false;
9433 stateOrName = this.$current.name; 9434 } 9435 var state = this.router.stateRegistry.matcher.find(stateOrName, options.relative), include = this.$current.includes; 9436 if (!isDefined(state)) 9437 return undefined; 9438 if (!isDefined(include[state.name])) 9439 return false; 9440 if (!params) 9441 return true; 9442 var schema = state.parameters({ inherit: true, matchingKeys: params }); 9443 return Param.equals(schema, Param.values(schema, params), this.params); 9444 }; 9445 /** 9446 * Generates a URL for a state and parameters 9447 * 9448 * Returns the url for the given state populated with the given params. 9449 * 9450 * #### Example: 9451 * ```js 9452 * expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob"); 9453 * ``` 9454 * 9455 * @param stateOrName The state name or state object you'd like to generate a url from. 9456 * @param params An object of parameter values to fill the state's required parameters. 9457 * @param options Options object. The options are: 9458 * 9459 * @returns {string} compiled state url 9460 */ 9461 StateService.prototype.href = function (stateOrName, params, options) { 9462 var defaultHrefOpts = { 9463 lossy: true, 9464 inherit: true, 9465 absolute: false, 9466 relative: this.$current, 9467 }; 9468 options = defaults(options, defaultHrefOpts); 9469 params = params || {}; 9470 var state = this.router.stateRegistry.matcher.find(stateOrName, options.relative); 9471 if (!isDefined(state)) 9472 return null; 9473 if (options.inherit) 9474 params = this.params.$inherit(params, this.$current, state); 9475 var nav = state && options.lossy ? state.navigable : state; 9476 if (!nav || nav.url === undefined || nav.url === null) { 9477 return null; 9478 } 9479 return this.router.urlRouter.href(nav.url, params, { absolute: options.absolute }); 9480 }; 9481 /** 9482 * Sets or gets the default [[transitionTo]] error handler. 9483 * 9484 * The error handler is called when a [[Transition]] is rejected or when any error occurred during the Transition. 9485 * This includes errors caused by resolves and transition hooks. 9486 * 9487 * Note: 9488 * This handler does not receive certain Transition rejections. 9489 * Redirected and Ignored Transitions are not considered to be errors by [[StateService.transitionTo]]. 9490 * 9491 * The built-in default error handler logs the error to the console. 9492 * 9493 * You can provide your own custom handler. 9494 * 9495 * #### Example: 9496 * ```js 9497 * stateService.defaultErrorHandler(function() { 9498 * // Do not log transitionTo errors 9499 * }); 9500 * ``` 9501 * 9502 * @param handler a global error handler function 9503 * @returns the current global error handler 9504 */ 9505 StateService.prototype.defaultErrorHandler = function (handler) { 9506 return (this._defaultErrorHandler = handler || this._defaultErrorHandler); 9507 }; 9508 StateService.prototype.get = function (stateOrName, base) { 9509 var reg = this.router.stateRegistry; 9510 if (arguments.length === 0) 9511 return reg.get(); 9512 return reg.get(stateOrName, base || this.$current); 9513 }; 9514 /** 9515 * Lazy loads a state 9516 * 9517 * Explicitly runs a state's [[StateDeclaration.lazyLoad]] function. 9518 * 9519 * @param stateOrName the state that should be lazy loaded 9520 * @param transition the optional Transition context to use (if the lazyLoad function requires an injector, etc) 9521 * Note: If no transition is provided, a noop transition is created using the from the current state to the current state. 9522 * This noop transition is not actually run. 9523 * 9524 * @returns a promise to lazy load 9525 */ 9526 StateService.prototype.lazyLoad = function (stateOrName, transition) { 9527 var state = this.get(stateOrName); 9528 if (!state || !state.lazyLoad) 9529 throw new Error('Can not lazy load ' + stateOrName); 9530 var currentPath = this.getCurrentPath();
9531 var target = PathUtils.makeTargetState(this.router.stateRegistry, currentPath); 9532 transition = transition || this.router.transitionService.create(currentPath, target); 9533 return lazyLoadState(transition, state); 9534 }; 9535 return StateService; 9536 }()); 9537 9538 /** 9539 * An angular1-like promise api 9540 * 9541 * This object implements four methods similar to the 9542 * [angular 1 promise api](https://docs.angularjs.org/api/ng/service/$q) 9543 * 9544 * UI-Router evolved from an angular 1 library to a framework agnostic library. 9545 * However, some of the `@uirouter/core` code uses these ng1 style APIs to support ng1 style dependency injection. 9546 * 9547 * This API provides native ES6 promise support wrapped as a $q-like API. 9548 * Internally, UI-Router uses this $q object to perform promise operations. 9549 * The `angular-ui-router` (ui-router for angular 1) uses the $q API provided by angular. 9550 * 9551 * $q-like promise api 9552 */ 9553 var $q = { 9554 /** Normalizes a value as a promise */ 9555 when: function (val) { return new Promise(function (resolve, reject) { return resolve(val); }); }, 9556 /** Normalizes a value as a promise rejection */ 9557 reject: function (val) { 9558 return new Promise(function (resolve, reject) { 9559 reject(val); 9560 }); 9561 }, 9562 /** @returns a deferred object, which has `resolve` and `reject` functions */ 9563 defer: function () { 9564 var deferred = {}; 9565 deferred.promise = new Promise(function (resolve, reject) { 9566 deferred.resolve = resolve; 9567 deferred.reject = reject; 9568 }); 9569 return deferred; 9570 }, 9571 /** Like Promise.all(), but also supports object key/promise notation like $q */ 9572 all: function (promises) { 9573 if (isArray(promises)) { 9574 return Promise.all(promises); 9575 } 9576 if (isObject(promises)) { 9577 // Convert promises map to promises array. 9578 // When each promise resolves, map it to a tuple { key: key, val: val } 9579 var chain = Object.keys(promises).map(function (key) { return promises[key].then(function (val) { return ({ key: key, val: val }); }); }); 9580 // Then wait for all promises to resolve, and convert them back to an object 9581 return $q.all(chain).then(function (values) { 9582 return values.reduce(function (acc, tuple) { 9583 acc[tuple.key] = tuple.val; 9584 return acc; 9585 }, {}); 9586 }); 9587 } 9588 }, 9589 }; 9590 9591 // globally available injectables 9592 var globals = {}; 9593 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm; 9594 var ARGUMENT_NAMES = /([^\s,]+)/g; 9595 /** 9596 * A basic angular1-like injector api 9597 * 9598 * This object implements four methods similar to the 9599 * [angular 1 dependency injector](https://docs.angularjs.org/api/auto/service/$injector) 9600 * 9601 * UI-Router evolved from an angular 1 library to a framework agnostic library. 9602 * However, some of the `@uirouter/core` code uses these ng1 style APIs to support ng1 style dependency injection. 9603 * 9604 * This object provides a naive implementation of a globally scoped dependency injection system. 9605 * It supports the following DI approaches: 9606 * 9607 * ### Function parameter names 9608 * 9609 * A function's `.toString()` is called, and the parameter names are parsed. 9610 * This only works when the parameter names aren't "mangled" by a minifier such as UglifyJS. 9611 * 9612 * ```js 9613 * function injectedFunction(FooService, BarService) { 9614 * // FooService and BarService are injected 9615 * } 9616 * ``` 9617 * 9618 * ### Function annotation 9619 * 9620 * A function may be annotated with an array of dependency names as the `$inject` property. 9621 * 9622 * ```js 9623 * injectedFunction.$inject = [ 'FooService', 'BarService' ]; 9624 * function injectedFunction(fs, bs) { 9625 * // FooService and BarService are injected as fs and bs parameters 9626 * } 9627 * ``` 9628 * 9629 * ### Array notation 9630 * 9631 * An array provides the names of the dependencies to inject (as strings). 9632 * The function is the last element of the array. 9633 * 9634 * ```js 9635 * [ 'FooService', 'BarService', function (fs, bs) { 9636 * // FooService and BarService are injected as fs and bs parameters 9637 * }] 9638 * ``` 9639 * 9640 * @type {$InjectorLike} 9641 */ 9642 var $injector = { 9643 /** Gets an object from DI based on a string token */ 9644 get: function (name) { return globals[name]; }, 9645 /** Returns true if an object named `name` exists in global DI */ 9646 has: function (name) { return $injector.get(name) != null; }, 9647 /** 9648 * Injects a function 9649 * 9650 * @param fn the function to inject 9651 * @param context the function's `this` binding 9652 * @param locals An object with additional DI tokens and values, such as `{ someToken: { foo: 1 } }` 9653 */ 9654 invoke: function (fn, context, locals) { 9655 var all = extend({}, globals, locals || {}); 9656 var params = $injector.annotate(fn); 9657 var ensureExist = assertPredicate(function (key) { return all.hasOwnProperty(key); }, function (key) { return "DI can't find injectable: '" + key + "'"; }); 9658 var args = params.filter(ensureExist).map(function (x) { return all[x]; }); 9659 if (isFunction(fn))
9660 return fn.apply(context, args); 9661 else 9662 return fn.slice(-1)[0].apply(context, args); 9663 }, 9664 /** 9665 * Returns a function's dependencies 9666 * 9667 * Analyzes a function (or array) and returns an array of DI tokens that the function requires. 9668 * @return an array of `string`s 9669 */ 9670 annotate: function (fn) { 9671 if (!isInjectable(fn)) 9672 throw new Error("Not an injectable function: " + fn); 9673 if (fn && fn.$inject) 9674 return fn.$inject; 9675 if (isArray(fn)) 9676 return fn.slice(0, -1); 9677 var fnStr = fn.toString().replace(STRIP_COMMENTS, ''); 9678 var result = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARGUMENT_NAMES); 9679 return result || []; 9680 }, 9681 }; 9682 9683 var keyValsToObjectR = function (accum, _a) { 9684 var key = _a[0], val = _a[1]; 9685 if (!accum.hasOwnProperty(key)) { 9686 accum[key] = val; 9687 } 9688 else if (isArray(accum[key])) { 9689 accum[key].push(val); 9690 } 9691 else { 9692 accum[key] = [accum[key], val]; 9693 } 9694 return accum; 9695 }; 9696 var getParams = function (queryString) { 9697 return queryString.split('&').filter(identity).map(splitEqual).reduce(keyValsToObjectR, {}); 9698 }; 9699 function parseUrl$1(url) { 9700 var orEmptyString = function (x) { return x || ''; }; 9701 var _a = splitHash(url).map(orEmptyString), beforehash = _a[0], hash = _a[1]; 9702 var _b = splitQuery(beforehash).map(orEmptyString), path = _b[0], search = _b[1]; 9703 return { path: path, search: search, hash: hash, url: url }; 9704 } 9705 var buildUrl = function (loc) { 9706 var path = loc.path(); 9707 var searchObject = loc.search(); 9708 var hash = loc.hash(); 9709 var search = Object.keys(searchObject) 9710 .map(function (key) { 9711 var param = searchObject[key]; 9712 var vals = isArray(param) ? param : [param]; 9713 return vals.map(function (val) { return key + '=' + val; }); 9714 }) 9715 .reduce(unnestR, []) 9716 .join('&'); 9717 return path + (search ? '?' + search : '') + (hash ? '#' + hash : ''); 9718 }; 9719 function locationPluginFactory(name, isHtml5, serviceClass, configurationClass) { 9720 return function (uiRouter) { 9721 var service = (uiRouter.locationService = new serviceClass(uiRouter)); 9722 var configuration = (uiRouter.locationConfig = new configurationClass(uiRouter, isHtml5)); 9723 function dispose(router) { 9724 router.dispose(service); 9725 router.dispose(configuration); 9726 } 9727 return { name: name, service: service, configuration: configuration, dispose: dispose }; 9728 }; 9729 } 9730 9731 /** A base `LocationServices` */ 9732 var BaseLocationServices = /** @class */ (function () { 9733 function BaseLocationServices(router, fireAfterUpdate) { 9734 var _this = this; 9735 this.fireAfterUpdate = fireAfterUpdate; 9736 this._listeners = []; 9737 this._listener = function (evt) { return _this._listeners.forEach(function (cb) { return cb(evt); }); }; 9738 this.hash = function () { return parseUrl$1(_this._get()).hash; }; 9739 this.path = function () { return parseUrl$1(_this._get()).path; }; 9740 this.search = function () { return getParams(parseUrl$1(_this._get()).search); }; 9741 this._location = root.location; 9742 this._history = root.history; 9743 } 9744 BaseLocationServices.prototype.url = function (url, replace) { 9745 if (replace === void 0) { replace = true; } 9746 if (isDefined(url) && url !== this._get()) { 9747 this._set(null, null, url, replace); 9748 if (this.fireAfterUpdate) { 9749 this._listeners.forEach(function (cb) { return cb({ url: url }); }); 9750 } 9751 } 9752 return buildUrl(this); 9753 }; 9754 BaseLocationServices.prototype.onChange = function (cb) { 9755 var _this = this; 9756 this._listeners.push(cb); 9757 return function () { return removeFrom(_this._listeners, cb); }; 9758 }; 9759 BaseLocationServices.prototype.dispose = function (router) { 9760 deregAll(this._listeners); 9761 }; 9762 return BaseLocationServices; 9763 }()); 9764 9765 var __extends = (undefined && undefined.__extends) || (function () { 9766 var extendStatics = function (d, b) { 9767 extendStatics = Object.setPrototypeOf || 9768 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 9769 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 9770 return extendStatics(d, b); 9771 }; 9772 return function (d, b) { 9773 extendStatics(d, b); 9774 function __() { this.constructor = d; } 9775 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 9776 }; 9777 })(); 9778 /** A `LocationServices` that uses the browser hash "#" to get/set the current location */ 9779 var HashLocationService = /** @class */ (function (_super) { 9780 __extends(HashLocationService, _super); 9781 function HashLocationService(router) { 9782 var _this = _super.call(this, router, false) || this; 9783 root.addEventListener('hashchange', _this._listener, false); 9784 return _this; 9785 } 9786 HashLocationService.prototype._get = function () { 9787 return trimHashVal(this._location.hash); 9788 }; 9789 HashLocationService.prototype._set = function (state, title, url, replace) { 9790 this._location.hash = url; 9791 }; 9792 HashLocationService.prototype.dispose = function (router) { 9793 _super.prototype.dispose.call(this, router); 9794 root.removeEventListener('hashchange', this._listener); 9795 }; 9796 return HashLocationService; 9797 }(BaseLocationServices)); 9798 9799 var __extends$1 = (undefined && undefined.__extends) || (function () { 9800 var extendStatics = function (d, b) { 9801 extendStatics = Object.setPrototypeOf || 9802 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 9803 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 9804 return extendStatics(d, b); 9805 }; 9806 return function (d, b) { 9807 extendStatics(d, b); 9808 function __() { this.constructor = d; } 9809 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 9810 }; 9811 })(); 9812 /** A `LocationServices` that gets/sets the current location from an in-memory object */ 9813 var MemoryLocationService = /** @class */ (function (_super) { 9814 __extends$1(MemoryLocationService, _super); 9815 function MemoryLocationService(router) { 9816 return _super.call(this, router, true) || this; 9817 } 9818 MemoryLocationService.prototype._get = function () { 9819 return this._url; 9820 }; 9821 MemoryLocationService.prototype._set = function (state, title, url, replace) { 9822 this._url = url; 9823 }; 9824 return MemoryLocationService; 9825 }(BaseLocationServices)); 9826 9827 var __extends$2 = (undefined && undefined.__extends) || (function () { 9828 var extendStatics = function (d, b) { 9829 extendStatics = Object.setPrototypeOf || 9830 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 9831 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 9832 return extendStatics(d, b); 9833 }; 9834 return function (d, b) { 9835 extendStatics(d, b); 9836 function __() { this.constructor = d; } 9837 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 9838 }; 9839 })(); 9840 /** 9841 * A `LocationServices` that gets/sets the current location using the browser's `location` and `history` apis 9842 * 9843 * Uses `history.pushState` and `history.replaceState` 9844 */ 9845 var PushStateLocationService = /** @class */ (function (_super) { 9846 __extends$2(PushStateLocationService, _super); 9847 function PushStateLocationService(router) { 9848 var _this = _super.call(this, router, true) || this; 9849 _this._config = router.urlService.config; 9850 root.addEventListener('popstate', _this._listener, false); 9851 return _this; 9852 } 9853 /** 9854 * Gets the base prefix without: 9855 * - trailing slash 9856 * - trailing filename 9857 * - protocol and hostname 9858 * 9859 * If <base href='/base/'>, this returns '/base'. 9860 * If <base href='/foo/base/'>, this returns '/foo/base'. 9861 * If <base href='/base/index.html'>, this returns '/base'. 9862 * If <base href='http://localhost:8080/base/index.html'>, this returns '/base'. 9863 * If <base href='/base'>, this returns ''. 9864 * If <base href='http://localhost:8080'>, this returns ''. 9865 * If <base href='http://localhost:8080/'>, this returns ''. 9866 * 9867 * See: https://html.spec.whatwg.org/dev/semantics.html#the-base-element 9868 */ 9869 PushStateLocationService.prototype._getBasePrefix = function () { 9870 return stripLastPathElement(this._config.baseHref()); 9871 }; 9872 PushStateLocationService.prototype._get = function () { 9873 var _a = this._location, pathname = _a.pathname, hash = _a.hash, search = _a.search; 9874 search = splitQuery(search)[1]; // strip ? if found 9875 hash = splitHash(hash)[1]; // strip # if found 9876 var basePrefix = this._getBasePrefix(); 9877 var exactBaseHrefMatch = pathname === this._config.baseHref(); 9878 var startsWithBase = pathname.substr(0, basePrefix.length) === basePrefix; 9879 pathname = exactBaseHrefMatch ? '/' : startsWithBase ? pathname.substring(basePrefix.length) : pathname; 9880 return pathname + (search ? '?' + search : '') + (hash ? '#' + hash : ''); 9881 }; 9882 PushStateLocationService.prototype._set = function (state, title, url, replace) { 9883 var basePrefix = this._getBasePrefix(); 9884 var slash = url && url[0] !== '/' ? '/' : ''; 9885 var fullUrl = url === '' || url === '/' ? this._config.baseHref() : basePrefix + slash + url; 9886 if (replace) { 9887 this._history.replaceState(state, title, fullUrl); 9888 } 9889 else { 9890 this._history.pushState(state, title, fullUrl); 9891 } 9892 }; 9893 PushStateLocationService.prototype.dispose = function (router) { 9894 _super.prototype.dispose.call(this, router); 9895 root.removeEventListener('popstate', this._listener); 9896 }; 9897 return PushStateLocationService; 9898 }(BaseLocationServices)); 9899 9900 /** A `LocationConfig` mock that gets/sets all config from an in-memory object */ 9901 var MemoryLocationConfig = /** @class */ (function () { 9902 function MemoryLocationConfig() { 9903 var _this = this; 9904 this.dispose = noop; 9905 this._baseHref = ''; 9906 this._port = 80; 9907 this._protocol = 'http'; 9908 this._host = 'localhost'; 9909 this._hashPrefix = ''; 9910 this.port = function () { return _this._port; }; 9911 this.protocol = function () { return _this._protocol; }; 9912 this.host = function () { return _this._host; }; 9913 this.baseHref = function () { return _this._baseHref; }; 9914 this.html5Mode = function () { return false; }; 9915 this.hashPrefix = function (newval) { return (isDefined(newval) ? (_this._hashPrefix = newval)
9915: _this._hashPrefix); }; 9916 } 9917 return MemoryLocationConfig; 9918 }()); 9919 9920 /** A `LocationConfig` that delegates to the browser's `location` object */ 9921 var BrowserLocationConfig = /** @class */ (function () { 9922 function BrowserLocationConfig(router, _isHtml5) { 9923 if (_isHtml5 === void 0) { _isHtml5 = false; } 9924 this._isHtml5 = _isHtml5; 9925 this._baseHref = undefined; 9926 this._hashPrefix = ''; 9927 } 9928 BrowserLocationConfig.prototype.port = function () { 9929 if (location.port) { 9930 return Number(location.port); 9931 } 9932 return this.protocol() === 'https' ? 443 : 80; 9933 }; 9934 BrowserLocationConfig.prototype.protocol = function () { 9935 return location.protocol.replace(/:/g, ''); 9936 }; 9937 BrowserLocationConfig.prototype.host = function () { 9938 return location.hostname; 9939 }; 9940 BrowserLocationConfig.prototype.html5Mode = function () { 9941 return this._isHtml5; 9942 }; 9943 BrowserLocationConfig.prototype.hashPrefix = function (newprefix) { 9944 return isDefined(newprefix) ? (this._hashPrefix = newprefix) : this._hashPrefix; 9945 }; 9946 BrowserLocationConfig.prototype.baseHref = function (href) { 9947 if (isDefined(href)) 9948 this._baseHref = href; 9949 if (isUndefined(this._baseHref)) 9950 this._baseHref = this.getBaseHref(); 9951 return this._baseHref; 9952 }; 9953 BrowserLocationConfig.prototype.getBaseHref = function () { 9954 var baseTag = document.getElementsByTagName('base')[0]; 9955 if (baseTag && baseTag.href) { 9956 return baseTag.href.replace(/^([^/:]*:)?\/\/[^/]*/, ''); 9957 } 9958 return this._isHtml5 ? '/' : location.pathname || '/'; 9959 }; 9960 BrowserLocationConfig.prototype.dispose = function () { }; 9961 return BrowserLocationConfig; 9962 }()); 9963 9964 function servicesPlugin(router) { 9965 services.$injector = $injector; 9966 services.$q = $q; 9967 return { name: 'vanilla.services', $q: $q, $injector: $injector, dispose: function () { return null; } }; 9968 } 9969 /** A `UIRouterPlugin` uses the browser hash to get/set the current location */ 9970 var hashLocationPlugin = locationPluginFactory('vanilla.hashBangLocation', false, HashLocationService, BrowserLocationConfig); 9971 /** A `UIRouterPlugin` that gets/sets the current location using the browser's `location` and `history` apis */ 9972 var pushStateLocationPlugin = locationPluginFactory('vanilla.pushStateLocation', true, PushStateLocationService, BrowserLocationConfig); 9973 /** A `UIRouterPlugin` that gets/sets the current location from an in-memory object */ 9974 var memoryLocationPlugin = locationPluginFactory('vanilla.memoryLocation', false, MemoryLocationService, MemoryLocationConfig); 9975 9976 var UIRouterPluginBase = /** @class */ (function () { 9977 function UIRouterPluginBase() { 9978 } 9979 UIRouterPluginBase.prototype.dispose = function (router) { }; 9980 return UIRouterPluginBase; 9981 }()); 9982 9983 var index = /*#__PURE__*/Object.freeze({ 9984 __proto__: null, 9985 root: root, 9986 fromJson: fromJson, 9987 toJson: toJson, 9988 forEach: forEach, 9989 extend: extend, 9990 equals: equals, 9991 identity: identity, 9992 noop: noop, 9993 createProxyFunctions: createProxyFunctions, 9994 inherit: inherit, 9995 inArray: inArray, 9996 _inArray: _inArray, 9997 removeFrom: removeFrom, 9998 _removeFrom: _removeFrom, 9999 pushTo: pushTo, 10000 _pushTo: _pushTo, 10001 deregAll: deregAll, 10002 defaults: defaults, 10003 mergeR: mergeR, 10004 ancestors: ancestors, 10005 pick: pick, 10006 omit: omit, 10007 pluck: pluck, 10008 filter: filter, 10009 find: find, 10010 mapObj: mapObj, 10011 map: map, 10012 values: values, 10013 allTrueR: allTrueR, 10014 anyTrueR: anyTrueR, 10015 unnestR: unnestR, 10016 flattenR: flattenR, 10017 pushR: pushR, 10018 uniqR: uniqR, 10019 unnest: unnest, 10020 flatten: flatten, 10021 assertPredicate: assertPredicate, 10022 assertMap: assertMap, 10023 assertFn: assertFn, 10024 pairs: pairs, 10025 arrayTuples: arrayTuples, 10026 applyPairs: applyPairs, 10027 tail: tail, 10028 copy: copy, 10029 _extend: _extend,
10030 silenceUncaughtInPromise: silenceUncaughtInPromise, 10031 silentRejection: silentRejection, 10032 makeStub: makeStub, 10033 services: services, 10034 Glob: Glob, 10035 curry: curry, 10036 compose: compose, 10037 pipe: pipe, 10038 prop: prop, 10039 propEq: propEq, 10040 parse: parse, 10041 not: not, 10042 and: and, 10043 or: or, 10044 all: all, 10045 any: any, 10046 is: is, 10047 eq: eq, 10048 val: val, 10049 invoke: invoke, 10050 pattern: pattern, 10051 isUndefined: isUndefined, 10052 isDefined: isDefined, 10053 isNull: isNull, 10054 isNullOrUndefined: isNullOrUndefined, 10055 isFunction: isFunction, 10056 isNumber: isNumber, 10057 isString: isString, 10058 isObject: isObject, 10059 isArray: isArray, 10060 isDate: isDate, 10061 isRegExp: isRegExp, 10062 isInjectable: isInjectable, 10063 isPromise: isPromise, 10064 Queue: Queue, 10065 maxLength: maxLength, 10066 padString: padString, 10067 kebobString: kebobString, 10068 functionToString: functionToString, 10069 fnToString: fnToString, 10070 stringify: stringify, 10071 beforeAfterSubstr: beforeAfterSubstr, 10072 hostRegex: hostRegex, 10073 stripLastPathElement: stripLastPathElement, 10074 splitHash: splitHash, 10075 splitQuery: splitQuery, 10076 splitEqual: splitEqual, 10077 trimHashVal: trimHashVal, 10078 splitOnDelim: splitOnDelim, 10079 joinNeighborsR: joinNeighborsR, 10080 get Category () { return exports.Category; }, 10081 Trace: Trace, 10082 trace: trace, 10083 get DefType () { return exports.DefType; }, 10084 Param: Param, 10085 ParamTypes: ParamTypes, 10086 StateParams: StateParams, 10087 ParamType: ParamType, 10088 PathNode: PathNode, 10089 PathUtils: PathUtils, 10090 resolvePolicies: resolvePolicies, 10091 defaultResolvePolicy: defaultResolvePolicy, 10092 Resolvable: Resolvable, 10093 NATIVE_INJECTOR_TOKEN: NATIVE_INJECTOR_TOKEN, 10094 ResolveContext: ResolveContext, 10095 resolvablesBuilder: resolvablesBuilder, 10096 StateBuilder: StateBuilder, 10097 StateObject: StateObject, 10098 StateMatcher: StateMatcher, 10099 StateQueueManager: StateQueueManager, 10100 StateRegistry: StateRegistry, 10101 StateService: StateService, 10102 TargetState: TargetState, 10103 get TransitionHookPhase () { return exports.TransitionHookPhase; }, 10104 get TransitionHookScope () { return exports.TransitionHookScope; }, 10105 HookBuilder: HookBuilder, 10106 matchState: matchState, 10107 RegisteredHook: RegisteredHook, 10108 makeEvent: makeEvent, 10109 get RejectType () { return exports.RejectType; }, 10110 Rejection: Rejection, 10111 Transition: Transition, 10112 TransitionHook: TransitionHook, 10113 TransitionEventType: TransitionEventType, 10114 defaultTransOpts: defaultTransOpts, 10115 TransitionService: TransitionService, 10116 UrlRules: UrlRules, 10117 UrlConfig: UrlConfig, 10118 UrlMatcher: UrlMatcher, 10119 ParamFactory: ParamFactory, 10120 UrlMatcherFactory: UrlMatcherFactory, 10121 UrlRouter: UrlRouter, 10122 UrlRuleFactory: UrlRuleFactory, 10123 BaseUrlRule: BaseUrlRule, 10124 UrlService: UrlService, 10125 ViewService: ViewService, 10126 UIRouterGlobals: UIRouterGlobals, 10127 UIRouter: UIRouter, 10128 $q: $q, 10129 $injector: $injector, 10130 BaseLocationServices: BaseLocationServices, 10131 HashLocationService: HashLocationService, 10132 MemoryLocationService: MemoryLocationService, 10133 PushStateLocationService: PushStateLocationService, 10134 MemoryLocationConfig: MemoryLocationConfig, 10135 BrowserLocationConfig: BrowserLocationConfig, 10136 keyValsToObjectR: keyValsToObjectR, 10137 getParams: getParams, 10138 parseUrl: parseUrl$1, 10139 buildUrl: buildUrl, 10140 locationPluginFactory: locationPluginFactory, 10141 servicesPlugin: servicesPlugin, 10142 hashLocationPlugin: hashLocationPlugin, 10143 pushStateLocationPlugin: pushStateLocationPlugin, 10144 memoryLocationPlugin: memoryLocationPlugin, 10145 UIRouterPluginBase: UIRouterPluginBase 10146 }); 10147 10148 /** @publicapi @module ng1 */ /** */ 10149 /** @internalapi */ 10150 function getNg1ViewConfigFactory() { 10151 var templateFactory = null; 10152 return function (path, view) { 10153 templateFactory = templateFactory || services.$injector.get('$templateFactory'); 10154 return [new Ng1ViewConfig(path, view, templateFactory)]; 10155 }; 10156 } 10157 /** @internalapi */
10158 var hasAnyKey = function (keys, obj) { return keys.reduce(function (acc, key) { return acc || isDefined(obj[key]); }, false); }; 10159 /** 10160 * This is a [[StateBuilder.builder]] function for angular1 `views`. 10161 * 10162 * When the [[StateBuilder]] builds a [[StateObject]] object from a raw [[StateDeclaration]], this builder 10163 * handles the `views` property with logic specific to @uirouter/angularjs (ng1). 10164 * 10165 * If no `views: {}` property exists on the [[StateDeclaration]], then it creates the `views` object 10166 * and applies the state-level configuration to a view named `$default`. 10167 * 10168 * @internalapi 10169 */ 10170 function ng1ViewsBuilder(state) { 10171 // Do not process root state 10172 if (!state.parent) 10173 return {}; 10174 var tplKeys = ['templateProvider', 'templateUrl', 'template', 'notify', 'async'], ctrlKeys = ['controller', 'controllerProvider', 'controllerAs', 'resolveAs'], compKeys = ['component', 'bindings', 'componentProvider'], nonCompKeys = tplKeys.concat(ctrlKeys), allViewKeys = compKeys.concat(nonCompKeys); 10175 // Do not allow a state to have both state-level props and also a `views: {}` property. 10176 // A state without a `views: {}` property can declare properties for the `$default` view as properties of the state. 10177 // However, the `$default` approach should not be mixed with a separate `views: ` block. 10178 if (isDefined(state.views) && hasAnyKey(allViewKeys, state)) { 10179 throw new Error("State '" + state.name + "' has a 'views' object. " + 10180 "It cannot also have \"view properties\" at the state level. " + 10181 "Move the following properties into a view (in the 'views' object): " + 10182 (" " + allViewKeys.filter(function (key) { return isDefined(state[key]); }).join(', '))); 10183 } 10184 var views = {}, viewsObject = state.views || { $default: pick(state, allViewKeys) }; 10185 forEach(viewsObject, function (config, name) { 10186 // Account for views: { "": { template... } } 10187 name = name || '$default'; 10188 // Account for views: { header: "headerComponent" } 10189 if (isString(config)) 10190 config = { component: config }; 10191 // Make a shallow copy of the config object 10192 config = extend({}, config); 10193 // Do not allow a view to mix props for component-style view with props for template/controller-style view 10194 if (hasAnyKey(compKeys, config) && hasAnyKey(nonCompKeys, config)) { 10195 throw new Error("Cannot combine: " + compKeys.join('|') + " with: " + nonCompKeys.join('|') + " in stateview: '" + name + "@" + state.name + "'"); 10196 } 10197 config.resolveAs = config.resolveAs || '$resolve'; 10198 config.$type = 'ng1'; 10199 config.$context = state; 10200 config.$name = name; 10201 var normalized = ViewService.normalizeUIViewTarget(config.$context, config.$name); 10202 config.$uiViewName = normalized.uiViewName; 10203 config.$uiViewContextAnchor = normalized.uiViewContextAnchor; 10204 views[name] = config; 10205 }); 10206 return views; 10207 } 10208 /** @hidden */ 10209 var id$1 = 0; 10210 /** @internalapi */ 10211 var Ng1ViewConfig = /** @class */ (function () { 10212 function Ng1ViewConfig(path, viewDecl, factory) { 10213 var _this = this; 10214 this.path = path; 10215 this.viewDecl = viewDecl; 10216 this.factory = factory; 10217 this.$id = id$1++; 10218 this.loaded = false; 10219 this.getTemplate = function (uiView, context) { 10220 return _this.component 10221 ? _this.factory.makeComponentTemplate(uiView, context, _this.component, _this.viewDecl.bindings) 10222 : _this.template; 10223 }; 10224 } 10225 Ng1ViewConfig.prototype.load = function () { 10226 var _this = this; 10227 var $q = services.$q; 10228 var context = new ResolveContext(this.path); 10229 var params = this.path.reduce(function (acc, node) { return extend(acc, node.paramValues); }, {}); 10230 var promises = { 10231 template: $q.when(this.factory.fromConfig(this.viewDecl, params, context)), 10232 controller: $q.when(this.getController(context)), 10233 }; 10234 return $q.all(promises).then(function (results) { 10235 trace.traceViewServiceEvent('Loaded', _this); 10236 _this.controller = results.controller; 10237 extend(_this, results.template); // Either { template: "tpl" } or { component: "cmpName" } 10238 return _this; 10239 }); 10240 }; 10241 /** 10242 * Gets the controller for a view configuration. 10243 * 10244 * @returns {Function|Promise.<Function>} Returns a controller, or a promise that resolves to a controller. 10245 */ 10246 Ng1ViewConfig.prototype.getController = function (context) { 10247 var provider = this.viewDecl.controllerProvider; 10248 if (!isInjectable(provider)) 10249 return this.viewDecl.controller; 10250 var deps = services.$injector.annotate(provider); 10251 var providerFn = isArray(provider) ? tail(provider) : provider; 10252 var resolvable = new Resolvable('', providerFn, deps); 10253 return resolvable.get(context); 10254 }; 10255 return Ng1ViewConfig; 10256 }()); 10257 10258 /** @publicapi @module view */ /** */ 10259 /** 10260 * Service which manages loading of templates from a ViewConfig. 10261 */ 10262 var TemplateFactory = /** @class */ (function () { 10263 function TemplateFactory() { 10264 var _this = this; 10265 /** @hidden */ this._useHttp = ng.version.minor < 3; 10266 /** @hidden */ this.$get = [ 10267 '$http', 10268 '$templateCache', 10269 '$injector', 10270 function ($http, $templateCache, $injector) { 10271 _this.$templateRequest = $injector.has && $injector.has('$templateRequest') && $injector.get('$templateRequest'); 10272 _this.$http = $http; 10273 _this.$templateCache = $templateCache; 10274 return _this; 10275 }, 10276 ]; 10277 } 10278 /** @hidden */ 10279 TemplateFactory.prototype.useHttpService = function (value) { 10280 this._useHttp = value; 10281 }; 10282 /** 10283 * Creates a template from a configuration object. 10284 *
10285 * @param config Configuration object for which to load a template. 10286 * The following properties are search in the specified order, and the first one 10287 * that is defined is used to create the template: 10288 * 10289 * @param params Parameters to pass to the template function. 10290 * @param context The resolve context associated with the template's view 10291 * 10292 * @return {string|object} The template html as a string, or a promise for 10293 * that string,or `null` if no template is configured. 10294 */ 10295 TemplateFactory.prototype.fromConfig = function (config, params, context) { 10296 var defaultTemplate = '<ui-view></ui-view>'; 10297 var asTemplate = function (result) { return services.$q.when(result).then(function (str) { return ({ template: str }); }); }; 10298 var asComponent = function (result) { return services.$q.when(result).then(function (str) { return ({ component: str }); }); }; 10299 return isDefined(config.template) 10300 ? asTemplate(this.fromString(config.template, params)) 10301 : isDefined(config.templateUrl) 10302 ? asTemplate(this.fromUrl(config.templateUrl, params)) 10303 : isDefined(config.templateProvider) 10304 ? asTemplate(this.fromProvider(config.templateProvider, params, context)) 10305 : isDefined(config.component) 10306 ? asComponent(config.component) 10307 : isDefined(config.componentProvider) 10308 ? asComponent(this.fromComponentProvider(config.componentProvider, params, context)) 10309 : asTemplate(defaultTemplate); 10310 }; 10311 /** 10312 * Creates a template from a string or a function returning a string. 10313 * 10314 * @param template html template as a string or function that returns an html template as a string. 10315 * @param params Parameters to pass to the template function. 10316 * 10317 * @return {string|object} The template html as a string, or a promise for that 10318 * string. 10319 */ 10320 TemplateFactory.prototype.fromString = function (template, params) { 10321 return isFunction(template) ? template(params) : template; 10322 }; 10323 /** 10324 * Loads a template from the a URL via `$http` and `$templateCache`. 10325 * 10326 * @param {string|Function} url url of the template to load, or a function 10327 * that returns a url. 10328 * @param {Object} params Parameters to pass to the url function. 10329 * @return {string|Promise.<string>} The template html as a string, or a promise 10330 * for that string. 10331 */ 10332 TemplateFactory.prototype.fromUrl = function (url, params) { 10333 if (isFunction(url)) 10334 url = url(params); 10335 if (url == null) 10336 return null; 10337 if (this._useHttp) { 10338 return this.$http 10339 .get(url, { cache: this.$templateCache, headers: { Accept: 'text/html' } }) 10340 .then(function (response) { 10341 return response.data; 10342 }); 10343 } 10344 return this.$templateRequest(url); 10345 }; 10346 /** 10347 * Creates a template by invoking an injectable provider function. 10348 * 10349 * @param provider Function to invoke via `locals` 10350 * @param {Function} injectFn a function used to invoke the template provider 10351 * @return {string|Promise.<string>} The template html as a string, or a promise 10352 * for that string. 10353 */ 10354 TemplateFactory.prototype.fromProvider = function (provider, params, context) { 10355 var deps = services.$injector.annotate(provider); 10356 var providerFn = isArray(provider) ? tail(provider) : provider; 10357 var resolvable = new Resolvable('', providerFn, deps); 10358 return resolvable.get(context); 10359 }; 10360 /** 10361 * Creates a component's template by invoking an injectable provider function. 10362 * 10363 * @param provider Function to invoke via `locals` 10364 * @param {Function} injectFn a function used to invoke the template provider 10365 * @return {string} The template html as a string: "<component-name input1='::$resolve.foo'></component-name>". 10366 */ 10367 TemplateFactory.prototype.fromComponentProvider = function (provider, params, context) { 10368 var deps = services.$injector.annotate(provider); 10369 var providerFn = isArray(provider) ? tail(provider) : provider; 10370 var resolvable = new Resolvable('', providerFn, deps); 10371 return resolvable.get(context); 10372 }; 10373 /** 10374 * Creates a template from a component's name 10375 * 10376 * This implements route-to-component. 10377 * It works by retrieving the component (directive) metadata from the injector. 10378 * It analyses the component's bindings, then constructs a template that instantiates the component. 10379 * The template wires input and output bindings to resolves or from the parent component. 10380 * 10381 * @param uiView {object} The parent ui-view (for binding outputs to callbacks) 10382 * @param context The ResolveContext (for binding outputs to callbacks returned from resolves) 10383 * @param component {string} Component's name in camel case. 10384 * @param bindings An object defining the component's bindings: {foo: '<'} 10385 * @return {string} The template as a string: "<component-name input1='::$resolve.foo'></component-name>". 10386 */ 10387 TemplateFactory.prototype.makeComponentTemplate = function (uiView, context, component, bindings) { 10388 bindings = bindings || {}; 10389 // Bind once prefix 10390 var prefix = ng.version.minor >= 3 ? '::' : ''; 10391 // Convert to kebob name. Add x- prefix if the string starts with `x-` or `data-` 10392 var kebob = function (camelCase) { 10393 var kebobed = kebobString(camelCase); 10394 return /^(x|data)-/.exec(kebobed) ? "x-" + kebobed : kebobed; 10395 }; 10396 var attributeTpl = function (input) { 10397 var name = input.name, type = input.type; 10398 var attrName = kebob(name); 10399 // If the ui-view has an attribute which matches a binding on the routed component 10400 // then pass that attribute through to the routed component template. 10401 // Prefer ui-view wired mappings to resolve data, unless the resolve was explicitly bound using `bindings:` 10402 if (uiView.attr(attrName) && !bindings[name]) 10403 return attrName + "='" + uiView.attr(attrName) + "'"; 10404 var resolveName = bindings[name] || name; 10405 // Pre-evaluate the expression for "@" bindings by enclosing in {{ }} 10406 // some-attr="{{ ::$resolve.someResolveName }}" 10407 if (type === '@') 10408 return attrName + "='{{" + prefix + "$resolve." + resolveName + "}}'";
10409 // Wire "&" callbacks to resolves that return a callback function 10410 // Get the result of the resolve (should be a function) and annotate it to get its arguments. 10411 // some-attr="$resolve.someResolveResultName(foo, bar)" 10412 if (type === '&') { 10413 var res = context.getResolvable(resolveName); 10414 var fn = res && res.data; 10415 var args = (fn && services.$injector.annotate(fn)) || []; 10416 // account for array style injection, i.e., ['foo', function(foo) {}] 10417 var arrayIdxStr = isArray(fn) ? "[" + (fn.length - 1) + "]" : ''; 10418 return attrName + "='$resolve." + resolveName + arrayIdxStr + "(" + args.join(',') + ")'"; 10419 } 10420 // some-attr="::$resolve.someResolveName" 10421 return attrName + "='" + prefix + "$resolve." + resolveName + "'"; 10422 }; 10423 var attrs = getComponentBindings(component).map(attributeTpl).join(' '); 10424 var kebobName = kebob(component); 10425 return "<" + kebobName + " " + attrs + "></" + kebobName + ">"; 10426 }; 10427 return TemplateFactory; 10428 }()); 10429 // Gets all the directive(s)' inputs ('@', '=', and '<') and outputs ('&') 10430 function getComponentBindings(name) { 10431 var cmpDefs = services.$injector.get(name + 'Directive'); // could be multiple 10432 if (!cmpDefs || !cmpDefs.length) 10433 throw new Error("Unable to find component named '" + name + "'"); 10434 return cmpDefs.map(getBindings).reduce(unnestR, []); 10435 } 10436 // Given a directive definition, find its object input attributes 10437 // Use different properties, depending on the type of directive (component, bindToController, normal) 10438 var getBindings = function (def) { 10439 if (isObject(def.bindToController)) 10440 return scopeBindings(def.bindToController); 10441 return scopeBindings(def.scope); 10442 }; 10443 // for ng 1.2 style, process the scope: { input: "=foo" } 10444 // for ng 1.3 through ng 1.5, process the component's bindToController: { input: "=foo" } object 10445 var scopeBindings = function (bindingsObj) { 10446 return Object.keys(bindingsObj || {}) 10447 // [ 'input', [ '=foo', '=', 'foo' ] ] 10448 .map(function (key) { return [key, /^([=<@&])[?]?(.*)/.exec(bindingsObj[key])]; }) 10449 // skip malformed values 10450 .filter(function (tuple) { return isDefined(tuple) && isArray(tuple[1]); }) 10451 // { name: ('foo' || 'input'), type: '=' } 10452 .map(function (tuple) { return ({ name: tuple[1][2] || tuple[0], type: tuple[1][1] }); }); 10453 }; 10454 10455 /** @publicapi @module ng1 */ /** */ 10456 /** 10457 * The Angular 1 `StateProvider` 10458 * 10459 * The `$stateProvider` works similar to Angular's v1 router, but it focuses purely 10460 * on state. 10461 * 10462 * A state corresponds to a "place" in the application in terms of the overall UI and 10463 * navigation. A state describes (via the controller / template / view properties) what 10464 * the UI looks like and does at that place. 10465 * 10466 * States often have things in common, and the primary way of factoring out these 10467 * commonalities in this model is via the state hierarchy, i.e. parent/child states aka 10468 * nested states. 10469 * 10470 * The `$stateProvider` provides interfaces to declare these states for your app. 10471 */ 10472 var StateProvider = /** @class */ (function () { 10473 function StateProvider(stateRegistry, stateService) { 10474 this.stateRegistry = stateRegistry; 10475 this.stateService = stateService; 10476 createProxyFunctions(val(StateProvider.prototype), this, val(this)); 10477 } 10478 /** 10479 * Decorates states when they are registered 10480 * 10481 * Allows you to extend (carefully) or override (at your own peril) the 10482 * `stateBuilder` object used internally by [[StateRegistry]]. 10483 * This can be used to add custom functionality to ui-router, 10484 * for example inferring templateUrl based on the state name. 10485 * 10486 * When passing only a name, it returns the current (original or decorated) builder 10487 * function that matches `name`. 10488 * 10489 * The builder functions that can be decorated are listed below. Though not all 10490 * necessarily have a good use case for decoration, that is up to you to decide. 10491 * 10492 * In addition, users can attach custom decorators, which will generate new 10493 * properties within the state's internal definition. There is currently no clear 10494 * use-case for this beyond accessing internal states (i.e. $state.$current), 10495 * however, expect this to become increasingly relevant as we introduce additional 10496 * meta-programming features. 10497 * 10498 * **Warning**: Decorators should not be interdependent because the order of 10499 * execution of the builder functions in non-deterministic. Builder functions 10500 * should only be dependent on the state definition object and super function. 10501 * 10502 * 10503 * Existing builder functions and current return values: 10504 * 10505 * - **parent** `{object}` - returns the parent state object. 10506 * - **data** `{object}` - returns state data, including any inherited data that is not 10507 * overridden by own values (if any). 10508 * - **url** `{object}` - returns a {@link ui.router.util.type:UrlMatcher UrlMatcher} 10509 * or `null`. 10510 * - **navigable** `{object}` - returns closest ancestor state that has a URL (aka is 10511 * navigable). 10512 * - **params** `{object}` - returns an array of state params that are ensured to 10513 * be a super-set of parent's params. 10514 * - **views** `{object}` - returns a views object where each key is an absolute view 10515 * name (i.e. "viewName@stateName") and each value is the config object 10516 * (template, controller) for the view. Even when you don't use the views object 10517 * explicitly on a state config, one is still created for you internally. 10518 * So by decorating this builder function you have access to decorating template 10519 * and controller properties. 10520 * - **ownParams** `{object}` - returns an array of params that belong to the state, 10521 * not including any params defined by ancestor states. 10522 * - **path** `{string}` - returns the full path from the root down to this state. 10523 * Needed for state activation. 10524 * - **includes** `{object}` - returns an object that includes every state that 10525 * would pass a `$state.includes()` test. 10526 * 10527 * #### Example: 10528 * Override the internal 'views' builder with a function that takes the state 10529 * definition, and a reference to the internal function being overridden: 10530 * ```js 10531 * $stateProvider.decorator('views', function (state, parent) { 10532 * let result = {}, 10533 * views = parent(state); 10534 * 10535 * angular.forEach(views, function (config, name) { 10536 * let autoName = (state.name + '.' + name).replace('.', '/'); 10537 * config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html'; 10538 * result[name] = config; 10539 * }); 10540 * return result; 10541 * }); 10542 * 10543 * $stateProvider.state('home', { 10544 * views: { 10545 * 'contact.list': { controller: 'ListController' },
10546 * 'contact.item': { controller: 'ItemController' } 10547 * } 10548 * }); 10549 * ``` 10550 * 10551 * 10552 * ```js 10553 * // Auto-populates list and item views with /partials/home/contact/list.html, 10554 * // and /partials/home/contact/item.html, respectively. 10555 * $state.go('home'); 10556 * ``` 10557 * 10558 * @param {string} name The name of the builder function to decorate. 10559 * @param {object} func A function that is responsible for decorating the original 10560 * builder function. The function receives two parameters: 10561 * 10562 * - `{object}` - state - The state config object. 10563 * - `{object}` - super - The original builder function. 10564 * 10565 * @return {object} $stateProvider - $stateProvider instance 10566 */ 10567 StateProvider.prototype.decorator = function (name, func) { 10568 return this.stateRegistry.decorator(name, func) || this; 10569 }; 10570 StateProvider.prototype.state = function (name, definition) { 10571 if (isObject(name)) { 10572 definition = name; 10573 } 10574 else { 10575 definition.name = name; 10576 } 10577 this.stateRegistry.register(definition); 10578 return this; 10579 }; 10580 /** 10581 * Registers an invalid state handler 10582 * 10583 * This is a passthrough to [[StateService.onInvalid]] for ng1. 10584 */ 10585 StateProvider.prototype.onInvalid = function (callback) { 10586 return this.stateService.onInvalid(callback); 10587 }; 10588 return StateProvider; 10589 }()); 10590 10591 /** @publicapi @module ng1 */ /** */ 10592 /** 10593 * This is a [[StateBuilder.builder]] function for angular1 `onEnter`, `onExit`, 10594 * `onRetain` callback hooks on a [[Ng1StateDeclaration]]. 10595 * 10596 * When the [[StateBuilder]] builds a [[StateObject]] object from a raw [[StateDeclaration]], this builder 10597 * ensures that those hooks are injectable for @uirouter/angularjs (ng1). 10598 * 10599 * @internalapi 10600 */ 10601 var getStateHookBuilder = function (hookName) { 10602 return function stateHookBuilder(stateObject) { 10603 var hook = stateObject[hookName]; 10604 var pathname = hookName === 'onExit' ? 'from' : 'to'; 10605 function decoratedNg1Hook(trans, state) { 10606 var resolveContext = new ResolveContext(trans.treeChanges(pathname)); 10607 var subContext = resolveContext.subContext(state.$$state()); 10608 var locals = extend(getLocals(subContext), { $state$: state, $transition$: trans }); 10609 return services.$injector.invoke(hook, this, locals); 10610 } 10611 return hook ? decoratedNg1Hook : undefined; 10612 }; 10613 }; 10614 10615 /** @publicapi @module ng1 */ /** */ 10616 /** 10617 * Implements UI-Router LocationServices and LocationConfig using Angular 1's $location service 10618 * @internalapi 10619 */ 10620 var Ng1LocationServices = /** @class */ (function () { 10621 function Ng1LocationServices($locationProvider) { 10622 // .onChange() registry 10623 this._urlListeners = []; 10624 this.$locationProvider = $locationProvider; 10625 var _lp = val($locationProvider); 10626 createProxyFunctions(_lp, this, _lp, ['hashPrefix']); 10627 } 10628 /** 10629 * Applys ng1-specific path parameter encoding 10630 * 10631 * The Angular 1 `$location` service is a bit weird. 10632 * It doesn't allow slashes to be encoded/decoded bi-directionally. 10633 * 10634 * See the writeup at https://github.com/angular-ui/ui-router/issues/2598 10635 * 10636 * This code patches the `path` parameter type so it encoded/decodes slashes as ~2F 10637 * 10638 * @param router 10639 */ 10640 Ng1LocationServices.monkeyPatchPathParameterType = function (router) { 10641 var pathType = router.urlMatcherFactory.type('path'); 10642 pathType.encode = function (x) { 10643 return x != null ? x.toString().replace(/(~|\/)/g, function (m) { return ({ '~': '~~', '/': '~2F' }[m]); }) : x; 10644 }; 10645 pathType.decode = function (x) { 10646 return x != null ? x.toString().replace(/(~~|~2F)/g, function (m) { return ({ '~~': '~', '~2F': '/' }[m]); }) : x; 10647 }; 10648 }; 10649 // eslint-disable-next-line @typescript-eslint/no-empty-function 10650 Ng1LocationServices.prototype.dispose = function () { }; 10651 Ng1LocationServices.prototype.onChange = function (callback) { 10652 var _this = this; 10653 this._urlListeners.push(callback); 10654 return function () { return removeFrom(_this._urlListeners)(callback); }; 10655 }; 10656 Ng1LocationServices.prototype.html5Mode = function () { 10657 var html5Mode = this.$locationProvider.html5Mode(); 10658 html5Mode = isObject(html5Mode) ? html5Mode.enabled : html5Mode; 10659 return html5Mode && this.$sniffer.history; 10660 }; 10661 Ng1LocationServices.prototype.baseHref = function () { 10662 return this._baseHref || (this._baseHref = this.$browser.baseHref() || this.$window.location.pathname); 10663 }; 10664 Ng1LocationServices.prototype.url = function (newUrl, replace, state) { 10665 if (replace === void 0) { replace = false; } 10666 if (isDefined(newUrl)) 10667 this.$location.url(newUrl); 10668 if (replace) 10669 this.$location.replace(); 10670 if (state) 10671 this.$location.state(state); 10672 return this.$location.url(); 10673 }; 10674 Ng1LocationServices.prototype._runtimeServices = function ($rootScope, $location, $sniffer, $browser, $window) { 10675 var _this = this; 10676 this.$location = $location; 10677 this.$sniffer = $sniffer; 10678 this.$browser = $browser; 10679 this.$window = $window; 10680 // Bind $locationChangeSuccess to the listeners registered in LocationService.onChange 10681 $rootScope.$on('$locationChangeSuccess', function (evt) { return _this._urlListeners.forEac
10681h(function (fn) { return fn(evt); }); }); 10682 var _loc = val($location); 10683 // Bind these LocationService functions to $location 10684 createProxyFunctions(_loc, this, _loc, ['replace', 'path', 'search', 'hash']); 10685 // Bind these LocationConfig functions to $location 10686 createProxyFunctions(_loc, this, _loc, ['port', 'protocol', 'host']); 10687 }; 10688 return Ng1LocationServices; 10689 }()); 10690 10691 /** @publicapi @module url */ /** */ 10692 /** 10693 * Manages rules for client-side URL 10694 * 10695 * ### Deprecation warning: 10696 * This class is now considered to be an internal API 10697 * Use the [[UrlService]] instead. 10698 * For configuring URL rules, use the [[UrlRulesApi]] which can be found as [[UrlService.rules]]. 10699 * 10700 * This class manages the router rules for what to do when the URL changes. 10701 * 10702 * This provider remains for backwards compatibility. 10703 * 10704 * @internalapi 10705 * @deprecated 10706 */ 10707 var UrlRouterProvider = /** @class */ (function () { 10708 /** @hidden */ 10709 function UrlRouterProvider(/** @hidden */ router) { 10710 this.router = router; 10711 } 10712 UrlRouterProvider.injectableHandler = function (router, handler) { 10713 return function (match) { return services.$injector.invoke(handler, null, { $match: match, $stateParams: router.globals.params }); }; 10714 }; 10715 /** @hidden */ 10716 UrlRouterProvider.prototype.$get = function () { 10717 var urlService = this.router.urlService; 10718 this.router.urlRouter.update(true); 10719 if (!urlService.interceptDeferred) 10720 urlService.listen(); 10721 return this.router.urlRouter; 10722 }; 10723 /** 10724 * Registers a url handler function. 10725 * 10726 * Registers a low level url handler (a `rule`). 10727 * A rule detects specific URL patterns and returns a redirect, or performs s
10727ome action. 10728 * 10729 * If a rule returns a string, the URL is replaced with the string, and all rules are fired again. 10730 * 10731 * #### Example: 10732 * ```js 10733 * var app = angular.module('app', ['ui.router.router']); 10734 * 10735 * app.config(function ($urlRouterProvider) { 10736 * // Here's an example of how you might allow case insensitive urls 10737 * $urlRouterProvider.rule(function ($injector, $location) { 10738 * var path = $location.path(), 10739 * normalized = path.toLowerCase(); 10740 * 10741 * if (path !== normalized) { 10742 * return normalized; 10743 * } 10744 * }); 10745 * }); 10746 * ``` 10747 * 10748 * @param ruleFn 10749 * Handler function that takes `$injector` and `$location` services as arguments. 10750 * You can use them to detect a url and return a different url as a string. 10751 * 10752 * @return [[UrlRouterProvider]] (`this`) 10753 */ 10754 UrlRouterProvider.prototype.rule = function (ruleFn) { 10755 var _this = this; 10756 if (!isFunction(ruleFn)) 10757 throw new Error("'rule' must be a function"); 10758 var match = function () { return ruleFn(services.$injector, _this.router.locationService); }; 10759 var rule = new BaseUrlRule(match, identity); 10760 this.router.urlService.rules.rule(rule); 10761 return this; 10762 }; 10763 /** 10764 * Defines the path or behavior to use when no url can be matched. 10765 * 10766 * #### Example: 10767 * ```js 10768 * var app = angular.module('app', ['ui.router.router']); 10769 * 10770 * app.config(function ($urlRouterProvider) { 10771 * // if the path doesn't match any of the urls you configured 10772 * // otherwise will take care of routing the user to the 10773 * // specified url 10774 * $urlRouterProvider.otherwise('/index'); 10775 * 10776 * // Example of using function rule as param 10777 * $urlRouterProvider.otherwise(function ($injector, $location) { 10778 * return '/a/valid/url'; 10779 * }); 10780 * }); 10781 * ``` 10782 * 10783 * @param rule 10784 * The url path you want to redirect to or a function rule that returns the url path or performs a `$state.go()`. 10785 * The function version is passed two params: `$injector` and `$location` services, and should return a url string. 10786 * 10787 * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance 10788 */ 10789 UrlRouterProvider.prototype.otherwise = function (rule) { 10790 var _this = this; 10791 var urlRules = this.router.urlService.rules; 10792 if (isString(rule)) { 10793 urlRules.otherwise(rule); 10794 } 10795 else if (isFunction(rule)) { 10796 urlRules.otherwise(function () { return rule(services.$injector, _this.router.locationService); }); 10797 } 10798 else { 10799 throw new Error("'rule' must be a string or function"); 10800 } 10801 return this; 10802 }; 10803 /** 10804 * Registers a handler for a given url matching. 10805 * 10806 * If the handler is a string, it is 10807 * treated as a redirect, and is interpolated according to the syntax of match 10808 * (i.e. like `String.replace()` for `RegExp`, or like a `UrlMatcher` pattern otherwise). 10809 * 10810 * If the handler is a function, it is injectable. 10811 * It gets invoked if `$location` matches. 10812 * You have the option of inject the match object as `$match`. 10813 * 10814 * The handler can return 10815 * 10816 * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter` 10817 * will continue trying to find another one that matches. 10818 * - **string** which is treated as a redirect and passed to `$location.url()` 10819 * - **void** or any **truthy** value tells `$urlRouter` that the url was handled. 10820 * 10821 * #### Example: 10822 * ```js 10823 * var app = angular.module('app', ['ui.router.router']); 10824 * 10825 * app.config(function ($urlRouterProvider) { 10826 * $urlRouterProvider.when($state.url, function ($match, $stateParams) { 10827 * if ($state.$current.navigable !== state || 10828 * !equalForKeys($match, $stateParams) { 10829 * $state.transitionTo(state, $match, false); 10830 * } 10831 * }); 10832 * }); 10833 * ``` 10834 * 10835 * @param what A pattern string to match, compiled as a [[UrlMatcher]]. 10836 * @param handler The path (or function that returns a path) that you want to redirect your user to. 10837 * @param ruleCallback [optional] A callback that receives the `rule` registered with [[UrlMatcher.rule]] 10838 * 10839 * Note: the handler may also invoke arbitrary code, such as `$state.go()` 10840 */ 10841 UrlRouterProvider.prototype.when = function (what, handler) { 10842 if (isArray(handler) || isFunction(handler)) { 10843 handler = UrlRouterProvider.injectableHandler(this.router, handler); 10844 } 10845 this.router.urlService.rules.when(what, handler); 10846 return this; 10847 }; 10848 /** 10849 * Disables monitoring of the URL. 10850 * 10851 * Call this method before UI-Router has bootstrapped. 10852 * It will stop UI-Router from performing the initial url sync. 10853 * 10854 * This can be useful to perform some asynchronous initialization before the router starts. 10855 * Once the initialization is complete, call [[listen]] to tell UI-Router to start watching and synchronizing the URL. 10856 * 10857 * #### Example: 10858 * ```js 10859 * var app = angular.module('app', ['ui.router']); 10860 * 10861 * app.config(function ($urlRouterProvider) { 10862 * // Prevent $urlRouter from automatically intercepting URL changes; 10863 * $urlRouterProvider.deferIntercept(); 10864 * }) 10865 * 10866 * app.run(function (MyService, $urlRouter, $http) { 10867 * $http.get("/stuff").then(function(resp) { 10868 * MyService.doStuff(resp.data); 10869 * $urlRouter.listen(); 10870 * $urlRouter.sync(); 10871 * }); 10872 * }); 10873 * ``` 10874 * 10875 * @param defer Indicates whether to defer location change interception. 10876 * Passing no parameter is equivalent to `true`. 10877 */ 10878 UrlRouterProvider.prototype.deferIntercept = function (defer) { 10879 this.router.urlService.deferIntercept(defer); 10880 }; 10881 return UrlRouterProvider; 10882 }()); 10883 10884 /* eslint-disable @typescript-eslint/no-empty-function */ 10885 ng.module('ui.router.angular1', []); 10886 var mod_init = ng.module('ui.router.init', ['ng']); 10887 var mod_util = ng.module('ui.router.util', ['ui.router.init']); 10888 var mod_rtr = ng.module('ui.router.router', ['ui.router.util']); 10889 var mod_state = ng.module('ui.router.state', ['ui.router.router', 'ui.router.util', 'ui.router.angular1']); 10890 var mod_main = ng.module('ui.router', ['ui.router.init', 'ui.router.state', 'ui.router.angular1']); 10891 var mod_cmpt = ng.module('ui.router.compat', ['ui.router']); 10892 var router = null; 10893 $uiRouterProvider.$inject = ['$locationProvider']; 10894 /** This angular 1 provider instantiates a Router and exposes its services via the angular injector */ 10895 function $uiRouterProvider($locationProvider) { 10896 // Create a new instance of the Router when the $uiRouterProvider is
10896initialized 10897 router = this.router = new UIRouter(); 10898 router.stateProvider = new StateProvider(router.stateRegistry, router.stateService); 10899 // Apply ng1 specific StateBuilder code for `views`, `resolve`, and `onExit/Retain/Enter` properties 10900 router.stateRegistry.decorator('views', ng1ViewsBuilder); 10901 router.stateRegistry.decorator('onExit', getStateHookBuilder('onExit')); 10902 router.stateRegistry.decorator('onRetain', getStateHookBuilder('onRetain')); 10903 router.stateRegistry.decorator('onEnter', getStateHookBuilder('onEnter')); 10904 router.viewService._pluginapi._viewConfigFactory('ng1', getNg1ViewConfigFactory()); 10905 // Disable decoding of params by UrlMatcherFactory because $location already handles this 10906 router.urlService.config._decodeParams = false; 10907 var ng1LocationService = (router.locationService = router.locationConfig = new Ng1LocationServices($locationProvider)); 10908 Ng1LocationServices.monkeyPatchPathParameterType(router); 10909 // backwards compat: also expose router instance as $uiRouterProvider.router 10910 router['router'] = router; 10911 router['$get'] = $get; 10912 $get.$inject = ['$location', '$browser', '$window', '$sniffer', '$rootScope', '$http', '$templateCache']; 10913 function $get($location, $browser, $window, $sniffer, $rootScope, $http, $templateCache) { 10914 ng1LocationService._runtimeServices($rootScope, $location, $sniffer, $browser, $window); 10915 delete router['router']; 10916 delete router['$get']; 10917 return router; 10918 } 10919 return router; 10920 } 10921 var getProviderFor = function (serviceName) { return [ 10922 '$uiRouterProvider', 10923 function ($urp) { 10924 var service = $urp.router[serviceName]; 10925 service['$get'] = function () { return service; }; 10926 return service; 10927 }, 10928 ]; }; 10929 // This effectively calls $get() on `$uiRouterProvider` to trigger init (when ng enters runtime) 10930 runBlock.$inject = ['$injector', '$q', '$uiRouter']; 10931 function runBlock($injector, $q, $uiRouter) { 10932 services.$injector = $injector; 10933 services.$q = $q; 10934 // https://github.com/angular-ui/ui-router/issues/3678 10935 if (!Object.prototype.hasOwnProperty.call($injector, 'strictDi')) { 10936 try { 10937 $injector.invoke(function (checkStrictDi) { }); 10938 } 10939 catch (error) { 10940 $injector.strictDi = !!/strict mode/.exec(error && error.toString()); 10941 } 10942 } 10943 // The $injector is now available. 10944 // Find any resolvables that had dependency annotation deferred 10945 $uiRouter.stateRegistry 10946 .get() 10947 .map(function (x) { return x.$$state().resolvables; }) 10948 .reduce(unnestR, []) 10949 .filter(function (x) { return x.deps === 'deferred'; }) 10950 .forEach(function (resolvable) { return (resolvable.deps = $injector.annotate(resolvable.resolveFn, $injector.strictDi)); }); 10951 } 10952 // $urlRouter service and $urlRouterProvider 10953 var getUrlRouterProvider = function (uiRouter) { return (uiRouter.urlRouterProvider = new UrlRouterProvider(uiRouter)); }; 10954 // $state service and $stateProvider 10955 // $urlRouter service and $urlRouterProvider 10956 var getStateProvider = function () { return extend(router.stateProvider, { $get: function () { return router.stateService; } }); }; 10957 watchDigests.$inject = ['$rootScope']; 10958 function watchDigests($rootScope) { 10959 $rootScope.$watch(function () { 10960 trace.approximateDigests++; 10961 }); 10962 } 10963 mod_init.provider('$uiRouter', $uiRouterProvider); 10964 mod_rtr.provider('$urlRouter', ['$uiRouterProvider', getUrlRouterProvider]); 10965 mod_util.provider('$urlService', getProviderFor('urlService')); 10966 mod_util.provider('$urlMatcherFactory', ['$uiRouterProvider', function () { return router.urlMatcherFactory; }]); 10967 mod_util.provider('$templateFactory', function () { return new TemplateFactory(); }); 10968 mod_state.provider('$stateRegistry', getProviderFor('stateRegistry')); 10969 mod_state.provider('$uiRouterGlobals', getProviderFor('globals')); 10970 mod_state.provider('$transitions', getProviderFor('transitionService')); 10971 mod_state.provider('$state', ['$uiRouterProvider', getStateProvider]); 10972 mod_state.factory('$stateParams', ['$uiRouter', function ($uiRouter) { return $uiRouter.globals.params; }]); 10973 mod_main.factory('$view', function () { return router.viewService; }); 10974 mod_main.service('$trace', function () { return trace; }); 10975 mod_main.run(watchDigests); 10976 mod_util.run(['$urlMatcherFactory', function ($urlMatcherFactory) { }]); 10977 mod_state.run(['$state', function ($state) { }]); 10978 mod_rtr.run(['$urlRouter', function ($urlRouter) { }]); 10979 mod_init.run(runBlock);
10980 /** @hidden TODO: find a place to move this */ 10981 var getLocals = function (ctx) { 10982 var tokens = ctx.getTokens().filter(isString); 10983 var tuples = tokens.map(function (key) { 10984 var resolvable = ctx.getResolvable(key); 10985 var waitPolicy = ctx.getPolicy(resolvable).async; 10986 return [key, waitPolicy === 'NOWAIT' ? resolvable.promise : resolvable.data]; 10987 }); 10988 return tuples.reduce(applyPairs, {}); 10989 }; 10990 10991 /* eslint-disable @typescript-eslint/no-empty-interface */ 10992 /** @hidden */ 10993 function parseStateRef(ref) { 10994 var paramsOnly = ref.match(/^\s*({[^}]*})\s*$/); 10995 if (paramsOnly) 10996 ref = '(' + paramsOnly[1] + ')'; 10997 var parsed = ref.replace(/\n/g, ' ').match(/^\s*([^(]*?)\s*(\((.*)\))?\s*$/); 10998 if (!parsed || parsed.length !== 4) 10999 throw new Error("Invalid state ref '" + ref + "'"); 11000 return { state: parsed[1] || null, paramExpr: parsed[3] || null }; 11001 } 11002 /** @hidden */ 11003 function stateContext(el) { 11004 var $uiView = el.parent().inheritedData('$uiView'); 11005 var path = parse('$cfg.path')($uiView); 11006 return path ? tail(path).state.name : undefined; 11007 } 11008 /** @hidden */ 11009 function processedDef($state, $element, def) { 11010 var uiState = def.uiState || $state.current.name; 11011 var uiStateOpts = extend(defaultOpts($element, $state), def.uiStateOpts || {}); 11012 var href = $state.href(uiState, def.uiStateParams, uiStateOpts); 11013 return { uiState: uiState, uiStateParams: def.uiStateParams, uiStateOpts: uiStateOpts, href: href }; 11014 } 11015 /** @hidden */ 11016 function getTypeInfo(el) { 11017 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute. 11018 var isSvg = Object.prototype.toString.call(el.prop('href')) === '[object SVGAnimatedString]'; 11019 var isForm = el[0].nodeName === 'FORM'; 11020 return { 11021 attr: isForm ? 'action' : isSvg ? 'xlink:href' : 'href', 11022 isAnchor: el.prop('tagName').toUpperCase() === 'A', 11023 clickable: !isForm, 11024 }; 11025 } 11026 /** @hidden */ 11027 function clickHook(el, $state, $timeout, type, getDef) { 11028 return function (e) { 11029 var button = e.which || e.button, target = getDef(); 11030 if (!(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || el.attr('target'))) { 11031 // HACK: This is to allow ng-clicks to be processed before the transition is initiated: 11032 var transition_1 = $timeout(function () { 11033 if (!el.attr('disabled')) { 11034 $state.go(target.uiState, target.uiStateParams, target.uiStateOpts); 11035 } 11036 }); 11037 e.preventDefault(); 11038 // if the state has no URL, ignore one preventDefault from the <a> directive. 11039 var ignorePreventDefaultCount_1 = type.isAnchor && !target.href ? 1 : 0; 11040 e.preventDefault = function () { 11041 if (ignorePreventDefaultCount_1-- <= 0) 11042 $timeout.cancel(transition_1); 11043 }; 11044 } 11045 }; 11046 } 11047 /** @hidden */ 11048 function defaultOpts(el, $state) { 11049 return { 11050 relative: stateContext(el) || $state.$current, 11051 inherit: true, 11052 source: 'sref', 11053 }; 11054 } 11055 /** @hidden */ 11056 function bindEvents(element, scope, hookFn, uiStateOpts) { 11057 var events; 11058 if (uiStateOpts) { 11059 events = uiStateOpts.events; 11060 } 11061 if (!isArray(events)) { 11062 events = ['click']; 11063 } 11064 var on = element.on ? 'on' : 'bind'; 11065 for (var _i = 0, events_1 = events; _i < events_1.length; _i++) { 11066 var event_1 = events_1[_i]; 11067 element[on](event_1, hookFn); 11068 } 11069 scope.$on('$destroy', function () { 11070 var off = element.off ? 'off' : 'unbind'; 11071 for (var _i = 0, events_2 = events; _i < events_2.length; _i++) { 11072 var event_2 = events_2[_i]; 11073 element[off](event_2, hookFn); 11074 } 11075 }); 11076 } 11077 /** 11078 * `ui-sref`: A directive for linking to a state 11079 * 11080 * A directive which links to a state (and optionally, parameters). 11081 * When clicked, this directive activates the linked state with the supplied parameter values. 11082 * 11083 * ### Linked State 11084 * The attribute value of the `ui-sref` is the name of the state to link to. 11085 * 11086 * #### Example: 11087 * This will activate the `home` state when the link is clicked. 11088 * ```html 11089 * <a ui-sref="home">Home</a> 11090 * ``` 11091 * 11092 * ### Relative Links 11093 * You can also use relative state paths within `ui-sref`, just like a relative path passed to `$state.go()` ([[StateService.go]]). 11094 * You just need to be aware that the path is relative to the state that *created* the link.
11095 * This allows a state to create a relative `ui-sref` which always targets the same destination. 11096 * 11097 * #### Example: 11098 * Both these links are relative to the parent state, even when a child state is currently active. 11099 * ```html 11100 * <a ui-sref=".child1">child 1 state</a> 11101 * <a ui-sref=".child2">child 2 state</a> 11102 * ``` 11103 * 11104 * This link activates the parent state. 11105 * ```html 11106 * <a ui-sref="^">Return</a> 11107 * ``` 11108 * 11109 * ### hrefs 11110 * If the linked state has a URL, the directive will automatically generate and 11111 * update the `href` attribute (using the [[StateService.href]] method). 11112 * 11113 * #### Example: 11114 * Assuming the `users` state has a url of `/users/` 11115 * ```html 11116 * <a ui-sref="users" href="/users/">Users</a> 11117 * ``` 11118 * 11119 * ### Parameter Values 11120 * In addition to the state name, a `ui-sref` can include parameter values which are applied when activating the state. 11121 * Param values can be provided in the `ui-sref` value after the state name, enclosed by parentheses. 11122 * The content inside the parentheses is an expression, evaluated to the parameter values. 11123 * 11124 * #### Example: 11125 * This example renders a list of links to users. 11126 * The state's `userId` parameter value comes from each user's `user.id` property. 11127 * ```html 11128 * <li ng-repeat="user in users"> 11129 * <a ui-sref="users.detail({ userId: user.id })">{{ user.displayName }}</a> 11130 * </li> 11131 * ``` 11132 * 11133 * Note: 11134 * The parameter values expression is `$watch`ed for updates. 11135 * 11136 * ### Transition Options 11137 * You can specify [[TransitionOptions]] to pass to [[StateService.go]] by using the `ui-sref-opts` attribute. 11138 * Options are restricted to `location`, `inherit`, and `reload`. 11139 * 11140 * #### Example: 11141 * ```html 11142 * <a ui-sref="home" ui-sref-opts="{ reload: true }">Home</a> 11143 * ``` 11144 * 11145 * ### Other DOM Events 11146 * 11147 * You can also customize which DOM events to respond to (instead of `click`) by 11148 * providing an `events` array in the `ui-sref-opts` attribute. 11149 * 11150 * #### Example: 11151 * ```html 11152 * <input type="text" ui-sref="contacts" ui-sref-opts="{ events: ['change', 'blur'] }"> 11153 * ``` 11154 * 11155 * ### Highlighting the active link 11156 * This directive can be used in conjunction with [[uiSrefActive]] to highlight the active link. 11157 * 11158 * ### Examples 11159 * If you have the following template: 11160 * 11161 * ```html 11162 * <a ui-sref="home">Home</a> 11163 * <a ui-sref="about">About</a> 11164 * <a ui-sref="{page: 2}">Next page</a> 11165 * 11166 * <ul> 11167 * <li ng-repeat="contact in contacts"> 11168 * <a ui-sref="contacts.detail({ id: contact.id })">{{ contact.name }}</a> 11169 * </li> 11170 * </ul> 11171 * ``` 11172 * 11173 * Then (assuming the current state is `contacts`) the rendered html including hrefs would be: 11174 * 11175 * ```html 11176 * <a href="#/home" ui-sref="home">Home</a> 11177 * <a href="#/about" ui-sref="about">About</a> 11178 * <a href="#/contacts?page=2" ui-sref="{page: 2}">Next page</a> 11179 * 11180 * <ul> 11181 * <li ng-repeat="contact in contacts"> 11182 * <a href="#/contacts/1" ui-sref="contacts.detail({ id: contact.id })">Joe</a> 11183 * </li> 11184 * <li ng-repeat="contact in contacts"> 11185 * <a href="#/contacts/2" ui-sref="contacts.detail({ id: contact.id })">Alice</a> 11186 * </li> 11187 * <li ng-repeat="contact in contacts"> 11188 * <a href="#/contacts/3" ui-sref="contacts.detail({ id: contact.id })">Bob</a> 11189 * </li> 11190 * </ul> 11191 * 11192 * <a href="#/home" ui-sref="home" ui-sref-opts="{reload: true}">Home</a> 11193 * ``` 11194 * 11195 * ### Notes 11196 * 11197 * - You can use `ui-sref` to change **only the parameter values** by omitting the state name and parentheses. 11198 * #### Example: 11199 * Sets the `lang` parameter to `en` and remains on the same state. 11200 * 11201 * ```html 11202 * <a ui-sref="{ lang: 'en' }">English</a> 11203 * ``` 11204 * 11205 * - A middle-click, right-click, or ctrl-click is handled (natively) by the browser to open the href in a
11205new window, for example. 11206 * 11207 * - Unlike the parameter values expression, the state name is not `$watch`ed (for performance reasons). 11208 * If you need to dynamically update the state being linked to, use the fully dynamic [[uiState]] directive. 11209 */ 11210 var uiSrefDirective; 11211 uiSrefDirective = [ 11212 '$uiRouter', 11213 '$timeout', 11214 function $StateRefDirective($uiRouter, $timeout) { 11215 var $state = $uiRouter.stateService; 11216 return { 11217 restrict: 'A', 11218 require: ['?^uiSrefActive', '?^uiSrefActiveEq'], 11219 link: function (scope, element, attrs, uiSrefActive) { 11220 var type = getTypeInfo(element); 11221 var active = uiSrefActive[1] || uiSrefActive[0]; 11222 var unlinkInfoFn = null; 11223 var rawDef = {}; 11224 var getDef = function () { return processedDef($state, element, rawDef); }; 11225 var ref = parseStateRef(attrs.uiSref); 11226 rawDef.uiState = ref.state; 11227 rawDef.uiStateOpts = attrs.uiSrefOpts ? scope.$eval(attrs.uiSrefOpts) : {}; 11228 function update() { 11229 var def = getDef(); 11230 if (unlinkInfoFn) 11231 unlinkInfoFn(); 11232 if (active) 11233 unlinkInfoFn = active.$$addStateInfo(def.uiState, def.uiStateParams); 11234 if (def.href != null) 11235 attrs.$set(type.attr, def.href); 11236 } 11237 if (ref.paramExpr) { 11238 scope.$watch(ref.paramExpr, function (val) { 11239 rawDef.uiStateParams = extend({}, val); 11240 update(); 11241 }, true); 11242 rawDef.uiStateParams = extend({}, scope.$eval(ref.paramExpr)); 11243 } 11244 update(); 11245 scope.$on('$destroy', $uiRouter.stateRegistry.onStatesChanged(update)); 11246 scope.$on('$destroy', $uiRouter.transitionService.onSuccess({}, update)); 11247 if (!type.clickable) 11248 return; 11249 var hookFn = clickHook(element, $state, $timeout, type, getDef); 11250 bindEvents(element, scope, hookFn, rawDef.uiStateOpts); 11251 }, 11252 }; 11253 }, 11254 ]; 11255 /** 11256 * `ui-state`: A fully dynamic directive for linking to a state 11257 * 11258 * A directive which links to a state (and optionally, parameters). 11259 * When clicked, this directive activates the linked state with the supplied parameter values. 11260 * 11261 * **This directive is very similar to [[uiSref]], but it `$observe`s and `$watch`es/evaluates all its inputs.** 11262 * 11263 * A directive which links to a state (and optionally, parameters). 11264 * When clicked, this directive activates the linked state with the supplied parameter values. 11265 * 11266 * ### Linked State 11267 * The attribute value of `ui-state` is an expression which is `$watch`ed and evaluated as the state to link to. 11268 * **This is in contrast with `ui-sref`, which takes a state name as a string literal.** 11269 * 11270 * #### Example: 11271 * Create a list of links. 11272 * ```html 11273 * <li ng-repeat="link in navlinks"> 11274 * <a ui-state="link.state">{{ link.displayName }}</a> 11275 * </li> 11276 * ``` 11277 * 11278 * ### Relative Links 11279 * If the expression evaluates to a relative path, it is processed like [[uiSref]]. 11280 * You just need to be aware that the path is relative to the state that *created* the link. 11281 * This allows a state to create relative `ui-state` which always targets the same destination. 11282 * 11283 * ### hrefs 11284 * If the linked state has a URL, the directive will automatically generate and 11285 * update the `href` attribute (using the [[StateService.href]] method). 11286 * 11287 * ### Parameter Values 11288 * In addition to the state name expression, a `ui-state` can include parameter values which are applied when activating the state. 11289 * Param values should be provided using the `ui-state-params` attribute. 11290 * The `ui-state-params` attribute value is `$watch`ed and evaluated as an expression. 11291 * 11292 * #### Example: 11293 * This example renders a list of links with param values. 11294 * The state's `userId` parameter value comes from each user's `user.id` property. 11295 * ```html 11296 * <li ng-repeat="link in navlinks"> 11297 * <a ui-state="link.state" ui-state-params="link.params">{{ link.displayName }}</a> 11298 * </li> 11299 * ``` 11300 * 11301 * ### Transition Options 11302 * You can specify [[TransitionOptions]] to pass to [[StateService.go]] by using the `ui-state-opts` attribute. 11303 * Options are restricted to `location`, `inherit`, and `reload`. 11304 * The value of the `ui-state-opts` is `$watch`ed and evaluated as an expression. 11305 * 11306 * #### Example: 11307 * ```html 11308 * <a ui-state="returnto.state" ui-state-opts="{ reload: true }">Home</a> 11309 * ``` 11310 * 11311 * ### Other DOM Events 11312 * 11313 * You can also customize which DOM events to respond to (instead of `click`) by 11314 * providing an `events` array in the `ui-state-opts` attribute. 11315 * 11316 * #### Example: 11317 * ```html 11318 * <input type="text" ui-state="contacts" ui-state-opts="{ events: ['change', 'blur'] }"> 11319 * ``` 11320 * 11321 * ### Highlighting the active link 11322 * This directive can be used in conjunction with [[uiSrefActive]] to highlight the active link. 11323 * 11324 * ### Notes 11325 *
11326 * - You can use `ui-params` to change **only the parameter values** by omitting the state name and supplying only `ui-state-params`. 11327 * However, it might be simpler to use [[uiSref]] parameter-only links. 11328 * 11329 * #### Example: 11330 * Sets the `lang` parameter to `en` and remains on the same state. 11331 * 11332 * ```html 11333 * <a ui-state="" ui-state-params="{ lang: 'en' }">English</a> 11334 * ``` 11335 * 11336 * - A middle-click, right-click, or ctrl-click is handled (natively) by the browser to open the href in a new window, for example. 11337 * ``` 11338 */ 11339 var uiStateDirective; 11340 uiStateDirective = [ 11341 '$uiRouter', 11342 '$timeout', 11343 function $StateRefDynamicDirective($uiRouter, $timeout) { 11344 var $state = $uiRouter.stateService; 11345 return { 11346 restrict: 'A', 11347 require: ['?^uiSrefActive', '?^uiSrefActiveEq'], 11348 link: function (scope, element, attrs, uiSrefActive) { 11349 var type = getTypeInfo(element); 11350 var active = uiSrefActive[1] || uiSrefActive[0]; 11351 var unlinkInfoFn = null; 11352 var hookFn; 11353 var rawDef = {}; 11354 var getDef = function () { return processedDef($state, element, rawDef); }; 11355 var inputAttrs = ['uiState', 'uiStateParams', 'uiStateOpts']; 11356 var watchDeregFns = inputAttrs.reduce(function (acc, attr) { return ((acc[attr] = noop), acc); }, {}); 11357 function update() { 11358 var def = getDef(); 11359 if (unlinkInfoFn) 11360 unlinkInfoFn(); 11361 if (active) 11362 unlinkInfoFn = active.$$addStateInfo(def.uiState, def.uiStateParams); 11363 if (def.href != null) 11364 attrs.$set(type.attr, def.href); 11365 } 11366 inputAttrs.forEach(function (field) { 11367 rawDef[field] = attrs[field] ? scope.$eval(attrs[field]) : null; 11368 attrs.$observe(field, function (expr) { 11369 watchDeregFns[field](); 11370 watchDeregFns[field] = scope.$watch(expr, function (newval) { 11371 rawDef[field] = newval; 11372 update(); 11373 }, true); 11374 }); 11375 }); 11376 update(); 11377 scope.$on('$destroy', $uiRouter.stateRegistry.onStatesChanged(update)); 11378 scope.$on('$destroy', $uiRouter.transitionService.onSuccess({}, update)); 11379 if (!type.clickable) 11380 return; 11381 hookFn = clickHook(element, $state, $timeout, type, getDef); 11382 bindEvents(element, scope, hookFn, rawDef.uiStateOpts); 11383 }, 11384 }; 11385 }, 11386 ]; 11387 /** 11388 * `ui-sref-active` and `ui-sref-active-eq`: A directive that adds a CSS class when a `ui-sref` is active 11389 * 11390 * A directive working alongside [[uiSref]] and [[uiState]] to add classes to an element when the 11391 * related directive's state is active (and remove them when it is inactive). 11392 * 11393 * The primary use-case is to highlight the active link in navigation menus, 11394 * distinguishing it from the inactive menu items. 11395 * 11396 * ### Linking to a `ui-sref` or `ui-state` 11397 * `ui-sref-active` can live on the same element as `ui-sref`/`ui-state`, or it can be on a parent element. 11398 * If a `ui-sref-active` is a parent to more than one `ui-sref`/`ui-state`, it will apply the CSS class when **any of the links are active**. 11399 * 11400 * ### Matching 11401 * 11402 * The `ui-sref-active` directive applies the CSS class when the `ui-sref`/`ui-state`'s target state **or any child state is active**. 11403 * This is a "fuzzy match" which uses [[StateService.includes]]. 11404 * 11405 * The `ui-sref-active-eq` directive applies the CSS class when the `ui-sref`/`ui-state`'s target state is directly active (not when child states are active). 11406 * This is an "exact match" which uses [[StateService.is]]. 11407 * 11408 * ### Parameter values 11409 * If the `ui-sref`/`ui-state` includes parameter values, the current parameter values must match the link's values for the link to be highlighted. 11410 * This allows a list of links to the same state with different parameters to be rendered, and the correct one highlighted. 11411 * 11412 * #### Example: 11413 * ```html 11414 * <li ng-repeat="user in users" ui-sref-active="active"> 11415 * <a ui-sref="user.details({ userId: user.id })">{{ user.lastName }}</a> 11416 * </li> 11417 * ``` 11418 * 11419 * ### Examples 11420 * 11421 * Given the following template: 11422 * #### Example: 11423 * ```html 11424 * <ul> 11425 * <li ui-sref-active="active" class="item"> 11426 * <a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a> 11427 * </li> 11428 * </ul> 11429 * ``` 11430 * 11431 * When the app state is `app.user` (or any child state), 11432 * and contains the state parameter "user" with value "bilbobaggins", 11433 * the resulting HTML will appear as (note the 'active' class): 11434 * 11435 * ```html 11436 * <ul> 11437 * <li ui-sref-active="active" class="item active"> 11438 * <a ui-sref="app.user({user: 'bilbobaggins'})" href="/users/bilbobaggins">@bilbobaggins</a> 11439 * </li> 11440 * </ul> 11441 * ``` 11442 * 11443 * ### Glob mode 11444 * 11445 * It is possible to pass `ui-sref-active` an expression that evaluates to an object. 11446 * The objects keys represent active class names and values represent the respective state names/globs. 11447 * `ui-sref-active` will match if the current active state **includes** any of 11448 * the specified state names/globs, even the abstract ones. 11449 * 11450 * #### Example: 11451 * Given the following template, with "admin" being an abstract state: 11452 * ```html 11453 * <div ui-sref-active="{'active': 'admin.**'}"> 11454 * <a ui-sref-active="active" ui-sref="admin.roles">Roles</a> 11455 * </div> 11456 * ``` 11457 * 11458 * Arrays are also supported as values in the `ngClass`-like interface. 11459 * This allows multiple states to add `active` class. 11460 * 11461 * #### Example: 11462 * Given the following template, with "admin.roles" being the current state, the class will be added too: 11463 * ```html 11464 * <div ui-sref-active="{'active': ['owner.**', 'admin.**']}"> 11465 * <a ui-sref-active="active" ui-sref="admin.roles">Roles</a> 11466 * </div> 11467 * ``` 11468 * 11469 * When the current state is "admin.roles" the "active" class will be applied to both the `<div>` and `<a>` elements. 11470 * It is important to note that the state names/globs passed to `ui-sref-active` override any state provided by a linked `ui-sref`. 11471 * 11472 * ### Notes: 11473 * 11474 * - The class name is interpolated **once** during the directives link time (any further changes to the 11475 * interpolated value are ignored). 11476 * 11477 * - Multiple classes may be specified in a space-separated format: `ui-sref-active='class1 class2 class3'` 11478 */ 11479 var uiSrefActiveDirective; 11480 uiSrefActiveDirective = [ 11481 '$state', 11482 '$stateParams', 11483 '$interpolate', 11484 '$uiRouter', 11485 function $StateRefActiveDirective($state, $stateParams, $interpolate, $uiRouter) { 11486 return { 11487 restrict: 'A', 11488 controller: [ 11489 '$scope', 11490 '$element', 11491 '$attrs', 11492 function ($scope, $element, $attrs) { 11493 var states = []; 11494 var activeEqClass; 11495 var uiSrefActive; 11496 // There probably isn't much point in $observing this 11497 // uiSrefActive and uiSrefActiveEq share the same directive object with some 11498 // slight difference in logic routing 11499 activeEqClass = $interpolate($attrs.uiSrefActiveEq || '', false)($scope); 11500 try { 11501 uiSrefActive = $scope.$eval($attrs.uiSrefActive); 11502 } 11503 catch (e) {
11504 // Do nothing. uiSrefActive is not a valid expression. 11505 // Fall back to using $interpolate below 11506 } 11507 uiSrefActive = uiSrefActive || $interpolate($attrs.uiSrefActive || '', false)($scope); 11508 setStatesFromDefinitionObject(uiSrefActive); 11509 // Allow uiSref to communicate with uiSrefActive[Equals] 11510 this.$$addStateInfo = function (newState, newParams) { 11511 // we already got an explicit state provided by ui-sref-active, so we 11512 // shadow the one that comes from ui-sref 11513 if (isObject(uiSrefActive) && states.length > 0) { 11514 return; 11515 } 11516 var deregister = addState(newState, newParams, uiSrefActive); 11517 update(); 11518 return deregister; 11519 }; 11520 function updateAfterTransition(trans) { 11521 trans.promise.then(update, noop); 11522 } 11523 $scope.$on('$destroy', setupEventListeners()); 11524 if ($uiRouter.globals.transition) { 11525 updateAfterTransition($uiRouter.globals.transition); 11526 } 11527 function setupEventListeners() { 11528 var deregisterStatesChangedListener = $uiRouter.stateRegistry.onStatesChanged(handleStatesChanged); 11529 var deregisterOnStartListener = $uiRouter.transitionService.onStart({}, updateAfterTransition); 11530 var deregisterStateChangeSuccessListener = $scope.$on('$stateChangeSuccess', update); 11531 return function cleanUp() { 11532 deregisterStatesChangedListener(); 11533 deregisterOnStartListener(); 11534 deregisterStateChangeSuccessListener(); 11535 }; 11536 } 11537 function handleStatesChanged() { 11538 setStatesFromDefinitionObject(uiSrefActive); 11539 } 11540 function setStatesFromDefinitionObject(statesDefinition) { 11541 if (isObject(statesDefinition)) { 11542 states = []; 11543 forEach(statesDefinition, function (stateOrName, activeClass) { 11544 // Helper function to abstract adding state. 11545 var addStateForClass = function (stateOrName, activeClass) { 11546 var ref = parseStateRef(stateOrName); 11547 addState(ref.state, $scope.$eval(ref.paramExpr), activeClass); 11548 }; 11549 if (isString(stateOrName)) { 11550 // If state is string, just add it. 11551 addStateForClass(stateOrName, activeClass); 11552 } 11553 else if (isArray(stateOrName)) { 11554 // If state is an array, iterate over it and add each array item individually. 11555 forEach(stateOrName, function (stateOrName) { 11556 addStateForClass(stateOrName, activeClass); 11557 }); 11558 } 11559 }); 11560 } 11561 } 11562 function addState(stateName, stateParams, activeClass) { 11563 var state = $state.get(stateName, stateContext($element)); 11564 var stateInfo = { 11565 state: state || { name: stateName }, 11566 params: stateParams, 11567 activeClass: activeClass, 11568 }; 11569 states.push(stateInfo); 11570 return function removeState() { 11571 removeFrom(states)(stateInfo); 11572 }; 11573 } 11574 // Update route state
11575 function update() { 11576 var splitClasses = function (str) { return str.split(/\s/).filter(identity); }; 11577 var getClasses = function (stateList) { 11578 return stateList 11579 .map(function (x) { return x.activeClass; }) 11580 .map(splitClasses) 11581 .reduce(unnestR, []); 11582 }; 11583 var allClasses = getClasses(states).concat(splitClasses(activeEqClass)).reduce(uniqR, []); 11584 var fuzzyClasses = getClasses(states.filter(function (x) { return $state.includes(x.state.name, x.params); })); 11585 var exactlyMatchesAny = !!states.filter(function (x) { return $state.is(x.state.name, x.params); }).length; 11586 var exactClasses = exactlyMatchesAny ? splitClasses(activeEqClass) : []; 11587 var addClasses = fuzzyClasses.concat(exactClasses).reduce(uniqR, []); 11588 var removeClasses = allClasses.filter(function (cls) { return !inArray(addClasses, cls); }); 11589 $scope.$evalAsync(function () { 11590 addClasses.forEach(function (className) { return $element.addClass(className); }); 11591 removeClasses.forEach(function (className) { return $element.removeClass(className); }); 11592 }); 11593 } 11594 update(); 11595 }, 11596 ], 11597 }; 11598 }, 11599 ]; 11600 ng 11601 .module('ui.router.state') 11602 .directive('uiSref', uiSrefDirective) 11603 .directive('uiSrefActive', uiSrefActiveDirective) 11604 .directive('uiSrefActiveEq', uiSrefActiveDirective) 11605 .directive('uiState', uiStateDirective); 11606 11607 /** @publicapi @module ng1 */ /** */ 11608 /** 11609 * `isState` Filter: truthy if the current state is the parameter 11610 * 11611 * Translates to [[StateService.is]] `$state.is("stateName")`. 11612 * 11613 * #### Example: 11614 * ```html 11615 * <div ng-if="'stateName' | isState">show if state is 'stateName'</div> 11616 * ``` 11617 */ 11618 $IsStateFilter.$inject = ['$state']; 11619 function $IsStateFilter($state) { 11620 var isFilter = function (state, params, options) { 11621 return $state.is(state, params, options); 11622 }; 11623 isFilter.$stateful = true; 11624 return isFilter; 11625 } 11626 /** 11627 * `includedByState` Filter: truthy if the current state includes the parameter 11628 * 11629 * Translates to [[StateService.includes]]` $state.is("fullOrPartialStateName")`. 11630 * 11631 * #### Example: 11632 * ```html 11633 * <div ng-if="'fullOrPartialStateName' | includedByState">show if state includes 'fullOrPartialStateName'</div> 11634 * ``` 11635 */ 11636 $IncludedByStateFilter.$inject = ['$state']; 11637 function $IncludedByStateFilter($state) { 11638 var includesFilter = function (state, params, options) { 11639 return $state.includes(state, params, options); 11640 }; 11641 includesFilter.$stateful = true; 11642 return includesFilter; 11643 } 11644 ng.module('ui.router.state').filter('isState', $IsStateFilter).filter('includedByState', $IncludedByStateFilter); 11645 11646 /** @publicapi @module directives */ /** */ 11647 /** 11648 * `ui-view`: A viewport directive which is filled in by a view from the active state. 11649 * 11650 * ### Attributes 11651 * 11652 * - `name`: (Optional) A view name. 11653 * The name should be unique amongst the other views in the same state. 11654 * You can have views of the same name that live in different states. 11655 * The ui-view can be targeted in a View using the name ([[Ng1StateDeclaration.views]]). 11656 * 11657 * - `autoscroll`: an expression. When it evaluates to true, the `ui-view` will be scrolled into view when it is activated. 11658 * Uses [[$uiViewScroll]] to do the scrolling. 11659 * 11660 * - `onload`: Expression to evaluate whenever the view updates. 11661 * 11662 * #### Example: 11663 * A view can be unnamed or named. 11664 * ```html 11665 * <!-- Unnamed --> 11666 * <div ui-view></div> 11667 * 11668 * <!-- Named --> 11669 * <div ui-view="viewName"></div> 11670 * 11671 * <!-- Named (different style) -->
11672 * <ui-view name="viewName"></ui-view> 11673 * ``` 11674 * 11675 * You can only have one unnamed view within any template (or root html). If you are only using a 11676 * single view and it is unnamed then you can populate it like so: 11677 * 11678 * ```html 11679 * <div ui-view></div> 11680 * $stateProvider.state("home", { 11681 * template: "<h1>HELLO!</h1>" 11682 * }) 11683 * ``` 11684 * 11685 * The above is a convenient shortcut equivalent to specifying your view explicitly with the 11686 * [[Ng1StateDeclaration.views]] config property, by name, in this case an empty name: 11687 * 11688 * ```js 11689 * $stateProvider.state("home", { 11690 * views: { 11691 * "": { 11692 * template: "<h1>HELLO!</h1>" 11693 * } 11694 * } 11695 * }) 11696 * ``` 11697 * 11698 * But typically you'll only use the views property if you name your view or have more than one view 11699 * in the same template. There's not really a compelling reason to name a view if its the only one, 11700 * but you could if you wanted, like so: 11701 * 11702 * ```html 11703 * <div ui-view="main"></div> 11704 * ``` 11705 * 11706 * ```js 11707 * $stateProvider.state("home", { 11708 * views: { 11709 * "main": { 11710 * template: "<h1>HELLO!</h1>" 11711 * } 11712 * } 11713 * }) 11714 * ``` 11715 * 11716 * Really though, you'll use views to set up multiple views: 11717 * 11718 * ```html 11719 * <div ui-view></div> 11720 * <div ui-view="chart"></div> 11721 * <div ui-view="data"></div> 11722 * ``` 11723 * 11724 * ```js 11725 * $stateProvider.state("home", { 11726 * views: { 11727 * "": { 11728 * template: "<h1>HELLO!</h1>" 11729 * }, 11730 * "chart": { 11731 * template: "<chart_thing/>" 11732 * }, 11733 * "data": { 11734 * template: "<data_thing/>" 11735 * } 11736 * } 11737 * }) 11738 * ``` 11739 * 11740 * #### Examples for `autoscroll`: 11741 * ```html 11742 * <!-- If autoscroll present with no expression, 11743 * then scroll ui-view into view --> 11744 * <ui-view autoscroll/> 11745 * 11746 * <!-- If autoscroll present with valid expression, 11747 * then scroll ui-view into view if expression evaluates to true --> 11748 * <ui-view autoscroll='true'/> 11749 * <ui-view autoscroll='false'/> 11750 * <ui-view autoscroll='scopeVariable'/> 11751 * ``` 11752 * 11753 * Resolve data: 11754 * 11755 * The resolved data from the state's `resolve` block is placed on the scope as `$resolve` (this 11756 * can be customized using [[Ng1ViewDeclaration.resolveAs]]). This can be then accessed from the template. 11757 * 11758 * Note that when `controllerAs` is being used, `$resolve` is set on the controller instance *after* the 11759 * controller is instantiated. The `$onInit()` hook can be used to perform initialization code which 11760 * depends on `$resolve` data. 11761 * 11762 * #### Example: 11763 * ```js 11764 * $stateProvider.state('home', { 11765 * template: '<my-component user="$resolve.user"></my-component>', 11766 * resolve: { 11767 * user: function(UserService) { return UserService.fetchUser(); } 11768 * } 11769 * }); 11770 * ``` 11771 */ 11772 var uiView; 11773 // eslint-disable-next-line prefer-const 11774 uiView = [ 11775 '$view', 11776 '$animate', 11777 '$uiViewScroll', 11778 '$interpolate', 11779 '$q', 11780 function $ViewDirective($view, $animate, $uiViewScroll, $interpolate, $q) { 11781 function getRenderer() { 11782 return { 11783 enter: function (element, target, cb) { 11784 if (ng.version.minor > 2) { 11785 $animate.enter(element, null, target).then(cb); 11786 } 11787 else { 11788 $animate.enter(element, null, target, cb); 11789 } 11790 }, 11791 leave: function (element, cb) { 11792 if (ng.version.minor > 2) { 11793 $animate.leave(element).then(cb); 11794 } 11795 else { 11796 $animate.leave(element, cb); 11797 } 11798 }, 11799 }; 11800 } 11801 function configsEqual(config1, config2) { 11802 return config1 === config2; 11803 } 11804 var rootData = { 11805 $cfg: { viewDecl: { $context: $view._pluginapi._rootViewContext() } }, 11806 $uiView: {}, 11807 }; 11808 var directive = { 11809 count: 0, 11810 restrict: 'ECA', 11811 terminal: true, 11812 priority: 400, 11813 transclude: 'element', 11814 compile: function (tElement, tAttrs, $transclude) { 11815 return function (scope, $element, attrs) { 11816 var onloadExp = attrs['onload'] || '', autoScrollExp = attrs['autoscroll'], renderer = getRenderer(), inherited = $element.inheritedData('$uiView') || rootData, name = $interpolate(attrs['uiView'] || attrs['name'] || '')(scope) || '$default'; 11817 var previousEl, currentEl, currentScope, viewConfig; 11818 var activeUIView = { 11819 $type: 'ng1', 11820 id: directive.count++, 11821 name: name, 11822 fqn: inherited.$uiView.fqn ? inherited.$uiView.fqn + '.' + name : name, 11823 config: null, 11824 configUpdated: configUpdatedCallback, 11825 get creationContext() { 11826 // The context in which this ui-view "tag" was created 11827 var fromParentTagConfig = parse('$cfg.viewDecl.$context')(inherited); 11828 // Allow <ui-view name="foo"><ui-view name="bar"></ui-view></ui-view> 11829 // See https://github.com/angular-ui/ui-router/issues/3355 11830 var fromParentTag = parse('$uiView.creationContext')(inherited); 11831 return fromParentTagConfig || fromParentTag; 11832 }, 11833 }; 11834 trace.traceUIViewEvent('Linking', activeUIView); 11835 function configUpdatedCallback(config) { 11836 if (config && !(config instanceof Ng1ViewConfig)) 11837 return; 11838 if (configsEqual(viewConfig, config)) 11839 return; 11840 trace.traceUIViewConfigUpdated(activeUIView, config && config.viewDecl && config.viewDecl.$context); 11841 viewConfig = config; 11842 updateView(config); 11843 } 11844 $element.data('$uiView', { $uiView: activeUIView }); 11845 updateView(); 11846 var unregister = $view.registerUIView(activeUIView); 11847 scope.$on('$destroy', function () { 11848 trace.traceUIViewEvent('Destroying/Unregistering', activeUIView); 11849 unregister(); 11850 }); 11851 function cleanupLastView() { 11852 if (previousEl) { 11853 trace.traceUIViewEvent('Removing (previous) el', previousEl.data('$uiView')); 11854 previousEl.remove(); 11855 previousEl = null; 11856 } 11857 if (currentScope) { 11858 trace.traceUIViewEvent('Destroying scope', activeUIView); 11859 currentScope.$destroy(); 11860 currentScope = null; 11861 } 11862 if (currentEl) { 11863 var _viewData_1 = currentEl.data('$uiViewAnim'); 11864 trace.traceUIViewEvent('Animate out', _viewData_1); 11865 renderer.leave(currentEl, function () { 11866 _viewData_1.$$animLeave.resolve(); 11867 previousEl = null; 11868 }); 11869 previousEl = currentEl; 11870 currentEl = null; 11871 } 11872 } 11873 function updateView(config) { 11874 var newScope = scope.$new(); 11875 var animEnter = $q.defer(), animLeave = $q.defer(); 11876 var $uiViewData = { 11877 $cfg: config, 11878 $uiView: activeUIView, 11879 }; 11880 var $uiViewAnim = { 11881 $animEnter: animEnter.promise, 11882 $animLeave: animLeave.promise, 11883 $$animLeave: animLeave, 11884 }; 11885 /** 11886 * @ngdoc event 11887 * @name ui.router.state.directive:ui-view#$viewContentLoading 11888 * @eventOf ui.router.state.directive:ui-view 11889 * @eventType emits on ui-view directive scope
11890 * @description 11891 * 11892 * Fired once the view **begins loading**, *before* the DOM is rendered. 11893 * 11894 * @param {Object} event Event object. 11895 * @param {string} viewName Name of the view. 11896 */ 11897 newScope.$emit('$viewContentLoading', name); 11898 var cloned = $transclude(newScope, function (clone) { 11899 clone.data('$uiViewAnim', $uiViewAnim); 11900 clone.data('$uiView', $uiViewData); 11901 renderer.enter(clone, $element, function onUIViewEnter() { 11902 animEnter.resolve(); 11903 if (currentScope) 11904 currentScope.$emit('$viewContentAnimationEnded'); 11905 if ((isDefined(autoScrollExp) && !autoScrollExp) || scope.$eval(autoScrollExp)) { 11906 $uiViewScroll(clone); 11907 } 11908 }); 11909 cleanupLastView(); 11910 }); 11911 currentEl = cloned; 11912 currentScope = newScope; 11913 /** 11914 * @ngdoc event 11915 * @name ui.router.state.directive:ui-view#$viewContentLoaded 11916 * @eventOf ui.router.state.directive:ui-view 11917 * @eventType emits on ui-view directive scope 11918 * @description * 11919 * Fired once the view is **loaded**, *after* the DOM is rendered. 11920 * 11921 * @param {Object} event Event object. 11922 */ 11923 currentScope.$emit('$viewContentLoaded', config || viewConfig); 11924 currentScope.$eval(onloadExp); 11925 } 11926 }; 11927 }, 11928 }; 11929 return directive; 11930 }, 11931 ]; 11932 $ViewDirectiveFill.$inject = ['$compile', '$controller', '$transitions', '$view', '$q']; 11933 /** @hidden */ 11934 function $ViewDirectiveFill($compile, $controller, $transitions, $view, $q) { 11935 var getControllerAs = parse('viewDecl.controllerAs'); 11936 var getResolveAs = parse('viewDecl.resolveAs'); 11937 return { 11938 restrict: 'ECA', 11939 priority: -400, 11940 compile: function (tElement) { 11941 var initial = tElement.html(); 11942 tElement.empty(); 11943 return function (scope, $element) { 11944 var data = $element.data('$uiView'); 11945 if (!data) { 11946 $element.html(initial); 11947 $compile($element.contents())(scope); 11948 return; 11949 } 11950 var cfg = data.$cfg || { viewDecl: {}, getTemplate: noop }; 11951 var resolveCtx = cfg.path && new ResolveContext(cfg.path); 11952 $element.html(cfg.getTemplate($element, resolveCtx) || initial); 11953 trace.traceUIViewFill(data.$uiView, $element.html()); 11954 var link = $compile($element.contents()); 11955 var controller = cfg.controller; 11956 var controllerAs = getControllerAs(cfg); 11957 var resolveAs = getResolveAs(cfg); 11958 var locals = resolveCtx && getLocals(resolveCtx); 11959 scope[resolveAs] = locals; 11960 if (controller) { 11961 var controllerInstance = ($controller(controller, extend({}, locals, { $scope: scope, $element: $element }))); 11962 if (controllerAs) { 11963 scope[controllerAs] = controllerInstance; 11964 scope[controllerAs][resolveAs] = locals; 11965 } 11966 // TODO: Use $view service as a central point for registering component-level hooks 11967 // Then, when a component is created, tell the $view service, so it can invoke hooks 11968 // $view.componentLoaded(controllerInstance, { $scope: scope, $element: $element }); 11969 // scope.$on('$destroy', () => $view.componentUnloaded(controllerInstance, { $scope: scope, $element: $element })); 11970 $element.data('$ngControllerController', controllerInstance); 11971 $element.children().data('$ngControllerController', controllerInstance); 11972 registerControllerCallbacks($q, $transitions, controllerInstance, scope, cfg); 11973 } 11974 // Wait for the component to appear in the DOM 11975 if (isString(cfg.component)) { 11976 var kebobName = kebobString(cfg.component); 11977 var tagRegexp_1 = new RegExp("^(x-|data-)?" + kebobName + "$", 'i'); 11978 var getComponentController = function () { 11979 var directiveEl = [].slice 11980 .call($element[0].children) 11981 .filter(function (el) { return el && el.tagName && tagRegexp_1.exec(el.tagName); }); 11982 return directiveEl && ng.element(directiveEl).data("$" + cfg.component + "Controller"); 11983 }; 11984 var deregisterWatch_1 = scope.$watch(getComponentController, function (ctrlInstance) { 11985 if (!ctrlInstance) 11986 return; 11987 registerControllerCallbacks($q, $transitions, ctrlInstance, scope, cfg); 11988 deregisterWatch_1(); 11989 }); 11990 } 11991 link(scope); 11992 }; 11993 }, 11994 }; 11995 } 11996 /** @hidden */ 11997 var hasComponentImpl = typeof ng.module('ui.router')['component'] === 'function'; 11998 /** @hidden incrementing id */ 11999 var _uiCanExitId = 0; 12000 /** @hidden TODO: move these callbacks to $view and/or `/hooks/components.ts` or something */ 12001 function registerControllerCallbacks($q, $transitions, controllerInstance, $scope, cfg) { 12002 // Call $onInit() ASAP 12003 if (isFunction(controllerInstance.$onInit) && 12004 !((cfg.viewDecl.component || cfg.viewDecl.componentProvider) && hasComponentImpl)) { 12005 controllerInstance.$onInit(); 12006 } 12007 var viewState = tail(cfg.path).state.self; 12008 var hookOptions = { bind: controllerInstance }; 12009 // Add component-level hook for onUiParamsChanged 12010 if (isFunction(controllerInstance.uiOnParamsChanged)) { 12011 var resolveContext = new ResolveContext(cfg.path);
12012 var viewCreationTrans_1 = resolveContext.getResolvable('$transition$').data; 12013 // Fire callback on any successful transition 12014 var paramsUpdated = function ($transition$) { 12015 // Exit early if the $transition$ is the same as the view was created within. 12016 // Exit early if the $transition$ will exit the state the view is for. 12017 if ($transition$ === viewCreationTrans_1 || $transition$.exiting().indexOf(viewState) !== -1) 12018 return; 12019 var toParams = $transition$.params('to'); 12020 var fromParams = $transition$.params('from'); 12021 var getNodeSchema = function (node) { return node.paramSchema; }; 12022 var toSchema = $transition$.treeChanges('to').map(getNodeSchema).reduce(unnestR, []); 12023 var fromSchema = $transition$.treeChanges('from').map(getNodeSchema).reduce(unnestR, []); 12024 // Find the to params that have different values than the from params 12025 var changedToParams = toSchema.filter(function (param) { 12026 var idx = fromSchema.indexOf(param); 12027 return idx === -1 || !fromSchema[idx].type.equals(toParams[param.id], fromParams[param.id]); 12028 }); 12029 // Only trigger callback if a to param has changed or is new 12030 if (changedToParams.length) { 12031 var changedKeys_1 = changedToParams.map(function (x) { return x.id; }); 12032 // Filter the params to only changed/new to params. `$transition$.params()` may be used to get all params. 12033 var newValues = filter(toParams, function (val, key) { return changedKeys_1.indexOf(key) !== -1; }); 12034 controllerInstance.uiOnParamsChanged(newValues, $transition$); 12035 } 12036 }; 12037 $scope.$on('$destroy', $transitions.onSuccess({}, paramsUpdated, hookOptions)); 12038 } 12039 // Add component-level hook for uiCanExit 12040 if (isFunction(controllerInstance.uiCanExit)) { 12041 var id_1 = _uiCanExitId++; 12042 var cacheProp_1 = '_uiCanExitIds'; 12043 // Returns true if a redirect transition already answered truthy 12044 var prevTruthyAnswer_1 = function (trans) { 12045 return !!trans && ((trans[cacheProp_1] && trans[cacheProp_1][id_1] === true) || prevTruthyAnswer_1(trans.redirectedFrom())); 12046 }; 12047 // If a user answered yes, but the transition was later redirected, don't also ask for the new redirect transition 12048 var wrappedHook = function (trans) { 12049 var promise; 12050 var ids = (trans[cacheProp_1] = trans[cacheProp_1] || {}); 12051 if (!prevTruthyAnswer_1(trans)) { 12052 promise = $q.when(controllerInstance.uiCanExit(trans)); 12053 promise.then(function (val) { return (ids[id_1] = val !== false); }); 12054 } 12055 return promise; 12056 }; 12057 var criteria = { exiting: viewState.name }; 12058 $scope.$on('$destroy', $transitions.onBefore(criteria, wrappedHook, hookOptions)); 12059 } 12060 } 12061 ng.module('ui.router.state').directive('uiView', uiView); 12062 ng.module('ui.router.state').directive('uiView', $ViewDirectiveFill); 12063 12064 /** @publicapi @module ng1 */ /** */ 12065 /** @hidden */ 12066 function $ViewScrollProvider() { 12067 var useAnchorScroll = false; 12068 this.useAnchorScroll = function () { 12069 useAnchorScroll = true; 12070 }; 12071 this.$get = [ 12072 '$anchorScroll', 12073 '$timeout', 12074 function ($anchorScroll, $timeout) { 12075 if (useAnchorScroll) { 12076 return $anchorScroll; 12077 } 12078 return function ($element) { 12079 return $timeout(function () { 12080 $element[0].scrollIntoView(); 12081 }, 0, false); 12082 }; 12083 }, 12084 ]; 12085 } 12086 ng.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider); 12087 12088 /** 12089 * Main entry point for angular 1.x build 12090 * @publicapi @module ng1 12091 */ /** */ 12092 var index$1 = 'ui.router'; 12093 12094 exports.$injector = $injector; 12095 exports.$q = $q; 12096 exports.BaseLocationServices = BaseLocationServices; 12097 exports.BaseUrlRule = BaseUrlRule; 12098 exports.BrowserLocationConfig = BrowserLocationConfig; 12099 exports.Glob = Glob; 12100 exports.HashLocationService = HashLocationService; 12101 exports.HookBuilder = HookBuilder; 12102 exports.MemoryLocationConfig = MemoryLocationConfig; 12103 exports.MemoryLocationService = MemoryLocationService; 12104 exports.NATIVE_INJECTOR_TOKEN = NATIVE_INJECTOR_TOKEN; 12105 exports.Ng1ViewConfig = Ng1ViewConfig; 12106 exports.Param = Param; 12107 exports.ParamFactory = ParamFactory; 12108 exports.ParamType = ParamType; 12109 exports.ParamTypes = ParamTypes; 12110 exports.PathNode = PathNode; 12111 exports.PathUtils = PathUtils; 12112 exports.PushStateLocationService = PushStateLocationService; 12113 exports.Queue = Queue; 12114 exports.RegisteredHook = RegisteredHook; 12115 exports.Rejection = Rejection; 12116 exports.Resolvable = Resolvable; 12117 exports.ResolveContext = ResolveContext; 12118 exports.StateBuilder = StateBuilder; 12119 exports.StateMatcher = StateMatcher; 12120 exports.StateObject = StateObject; 12121 exports.StateParams = StateParams; 12122 exports.StateProvider = StateProvider; 12123 exports.StateQueueManager = StateQueueManager; 12124 exports.StateRegistry = StateRegistry; 12125 exports.StateService = StateService; 12126 exports.TargetState = TargetState; 12127 exports.Trace = Trace; 12128 exports.Transition = Transition; 12129 exports.TransitionEventType = TransitionEventType; 12130 exports.TransitionHook = TransitionHook; 12131 exports.TransitionService = TransitionService; 12132 exports.UIRouter = UIRouter; 12133 exports.UIRouterGlobals = UIRouterGlobals; 12134 exports.UIRouterPluginBase = UIRouterPluginBase; 12135 exports.UrlConfig = UrlConfig; 12136 exports.UrlMatcher = UrlMatcher; 12137 exports.UrlMatcherFactory = UrlMatcherFactory; 12138 exports.UrlRouter = UrlRouter; 12139 exports.UrlRouterProvider = UrlRouterProvider; 12140 exports.UrlRuleFactory = UrlRuleFactory; 12141 exports.UrlRules = UrlRules; 12142 exports.UrlService = UrlService; 12143 exports.ViewService = ViewService; 12144 exports._extend = _extend; 12145 exports._inArray = _inArray; 12146 exports._pushTo = _pushTo; 12147 exports._removeFrom = _removeFrom; 12148 exports.all = all; 12149 exports.allTrueR = allTrueR; 12150 exports.ancestors = ancestors; 12151 exports.and = and; 12152 exports.any = any; 12153 exports.anyTrueR = anyTrueR; 12154 exports.applyPairs = applyPairs; 12155 exports.arrayTuples = arrayTuples; 12156 exports.assertFn = assertFn; 12157 exports.assertMap = assertMap; 12158 exports.assertPredicate = assertPredicate; 12159 exports.beforeAfterSubstr = beforeAfterSubstr; 12160 exports.buildUrl = buildUrl; 12161 exports.compose = compose; 12162 exports.copy = copy; 12163 exports.core = index; 12164 exports.createProxyFunctions = createProxyFunctions; 12165 exports.curry = curry; 12166 exports.default = index$1; 12167 exports.defaultResolvePolicy = defaultResolvePolicy; 12168 exports.defaultTransOpts = defaultTransOpts; 12169 exports.defaults = defaults; 12170 exports.deregAll = deregAll; 12171 exports.eq = eq; 12172 exports.equals = equals; 12173 exports.extend = extend;
12174 exports.filter = filter; 12175 exports.find = find; 12176 exports.flatten = flatten; 12177 exports.flattenR = flattenR; 12178 exports.fnToString = fnToString; 12179 exports.forEach = forEach; 12180 exports.fromJson = fromJson; 12181 exports.functionToString = functionToString; 12182 exports.getLocals = getLocals; 12183 exports.getNg1ViewConfigFactory = getNg1ViewConfigFactory; 12184 exports.getParams = getParams; 12185 exports.hashLocationPlugin = hashLocationPlugin; 12186 exports.hostRegex = hostRegex; 12187 exports.identity = identity; 12188 exports.inArray = inArray; 12189 exports.inherit = inherit; 12190 exports.invoke = invoke; 12191 exports.is = is; 12192 exports.isArray = isArray; 12193 exports.isDate = isDate; 12194 exports.isDefined = isDefined; 12195 exports.isFunction = isFunction; 12196 exports.isInjectable = isInjectable; 12197 exports.isNull = isNull; 12198 exports.isNullOrUndefined = isNullOrUndefined; 12199 exports.isNumber = isNumber; 12200 exports.isObject = isObject; 12201 exports.isPromise = isPromise; 12202 exports.isRegExp = isRegExp; 12203 exports.isString = isString; 12204 exports.isUndefined = isUndefined; 12205 exports.joinNeighborsR = joinNeighborsR; 12206 exports.kebobString = kebobString; 12207 exports.keyValsToObjectR = keyValsToObjectR; 12208 exports.locationPluginFactory = locationPluginFactory; 12209 exports.makeEvent = makeEvent; 12210 exports.makeStub = makeStub; 12211 exports.map = map; 12212 exports.mapObj = mapObj; 12213 exports.matchState = matchState; 12214 exports.maxLength = maxLength; 12215 exports.memoryLocationPlugin = memoryLocationPlugin; 12216 exports.mergeR = mergeR; 12217 exports.ng1ViewsBuilder = ng1ViewsBuilder; 12218 exports.noop = noop; 12219 exports.not = not; 12220 exports.omit = omit; 12221 exports.or = or; 12222 exports.padString = padString; 12223 exports.pairs = pairs; 12224 exports.parse = parse; 12225 exports.parseUrl = parseUrl$1; 12226 exports.pattern = pattern; 12227 exports.pick = pick; 12228 exports.pipe = pipe; 12229 exports.pluck = pluck; 12230 exports.prop = prop; 12231 exports.propEq = propEq; 12232 exports.pushR = pushR; 12233 exports.pushStateLocationPlugin = pushStateLocationPlugin; 12234 exports.pushTo = pushTo; 12235 exports.removeFrom = removeFrom; 12236 exports.resolvablesBuilder = resolvablesBuilder; 12237 exports.resolvePolicies = resolvePolicies; 12238 exports.root = root; 12239 exports.services = services; 12240 exports.servicesPlugin = servicesPlugin; 12241 exports.silenceUncaughtInPromise = silenceUncaughtInPromise; 12242 exports.silentRejection = silentRejection; 12243 exports.splitEqual = splitEqual; 12244 exports.splitHash = splitHash; 12245 exports.splitOnDelim = splitOnDelim; 12246 exports.splitQuery = splitQuery; 12247 exports.stringify = stringify; 12248 exports.stripLastPathElement = stripLastPathElement; 12249 exports.tail = tail; 12250 exports.toJson = toJson; 12251 exports.trace = trace; 12252 exports.trimHashVal = trimHashVal; 12253 exports.uniqR = uniqR; 12254 exports.unnest = unnest; 12255 exports.unnestR = unnestR; 12256 exports.val = val; 12257 exports.values = values; 12258 exports.watchDigests = watchDigests; 12259 12260 Object.defineProperty(exports, '__esModule', { value: true }); 12261 12262}))); 12263//# sourceMappingURL=angular-ui-router.js.map 12264 12265(function() { 12266 "use strict"; 12267 12268 angular.module("PricingApp", ["ui.router", "WebnamesError", "WebnamesCommon", "WebnamesAnalytics", "WebnamesGridHelper"]) 12269 .config(["$stateProvider", "$urlRouterProvider", "$locationProvider", function ($stateProvider, $urlRouterProvider, $locationProvider) { 12270 $urlRouterProvider.otherwise("/domains"); 12271 12272 $locationProvider.hashPrefix("!"); 12273 12274 $stateProvider 12275 .state("none", { 12276 url: "/none" 12277 }) 12278 .state("domains", { 12279 url: "/domains", 12280 templateUrl: "Domains.cshtml", 12281 controller: "DomainsController as domCtrl", 12282 }) 12283 .state("hosting", { 12284 url: "/hosting", 12285 templateUrl: "Hosting.cshtml", 12286 controller: "HostingController as hostCtrl" 12287 }) 12288 .state("email", { 12289 url: "/email", 12290 templateUrl: "Email.cshtml", 12291 controller: "EmailController as emailCtrl" 12292 }) 12293 .state("ssl", { 12294 url: "/ssl", 12295 templateUrl: "Ssl.cshtml", 12296 controller: "SSLController as sslCtrl" 12297 }) 12298 .state("other", { 12299 url: "/other", 12300 templateUrl: "Other.cshtml" 12301 }); 12302 12303 }]) 12304 .service("PricingStore", ["$window", "$http", "$timeout", "CustomerType", "CustomerSubType", function ($window, $http, $timeout, CustomerType, CustomerSubType) { 12305 var service = this; 12306 12307 var domainPricingsCache = {}; 12308 12309 /** 12310 * Queries domain pricings and returns them. Caches values locally. 12311 * @param {string} customerSubType optional to get pricing for a subtype other than current 12312 * @returns {Promise} a promise which will resolve to the domain pricing 12313 */ 12314 service.QueryDomainPricings = function (customerSubType) { 12315 var requestPath = $window._SmartURL + "pricing/ExtensionPricing"; 12316 12317 if (!!customerSubType && angular.isString(customerSubType) && customerSubType !== CustomerSubType) { 12318 requestPath += "/" + CustomerType + "?subType=" + customerSubType; 12319 } 12320 12321 if (angular.isArray(domainPricingsCache[customerSubType]) && domainPricingsCache[customerSubType].length > 0) { 12322 return $timeout(function() { return domainPricingsCache[customerSubType] }); 12323 } 12324 12325 return $http.get(requestPath).then(function (resp) { 12326 domainPricingsCache[customerSubType] = resp.data; 12327 return resp.data; 12328 }); 12329 } 12330 12331 var nonDomainPricings = {}; 12332 service.QueryPromise = null; 12333 service.ExchangeRatePromise = null; 12334 service.PlanCodes = {}; 12335 12336 /** 12337 * Queries pricings based on the PlanCodes. Does not return data, pricing can be retrieved using GetNonDomainPricing() 12338 * @returns {Promise} a promise to indicate when query is done 12339 */ 12340 service.QueryPricings = function () { 12341 if (service.QueryPromise) return service.QueryPromise; 12342 12343 var lsMissingPricingCodes = _.difference(_.keys(service.PlanCodes), _.keys(nonDomainPricings)); 12344 12345 if (lsMissingPricingCodes.length < 1) return $timeout(function(){ return "done"}); 12346 12347 service.QueryPromise = $http.post($window._SmartURL + "pricing/NonDomainPricing", { packageCodes: lsMissingPricingCodes }).then(function (resp) { 12348 nonDomainPricings = _.extend(nonDomainPricings, resp.data); 12349 service.QueryPromise = null; 12350 return "done"; 12351 }); 12352 12353 return service.QueryPromise; 12354 } 12355 12356 /** 12357 * Gets pricing from local pricing cache. Make sure plan code is in PlanCodes and QueryPricings is called to cache it. 12358 * @param {string} packageCode 12359 * @returns {object} pricing object to assign to directive 12360 */ 12361 service.GetNonDomainPricing = function(packageCode) { 12362 return nonDomainPricings[packageCode]; 12363 } 12364 12365 service.GetExchangeRate = function() { 12366 if (service.ExchangeRatePromise) return service.ExchangeRatePromise; 12367 12368 service.ExchangeRatePromise = $http.get($window._SmartURL + "pricing/ExchangeRate").then(function (resp) { return resp.data; }); 12369 12370 return service.ExchangeRatePromise; 12371 } 12372 }]) 12373 .run(["$state", function ($state) { 12374 // If we are in the 'none' state while resizing to tab view, default to domains 12375 $(window).resize(function () { 12376 var viewport = $(window).width(); 12377 12378 if (viewport > 767 && $state.current.name === "none") $state.go("domains"); 12379 }); 12380 }]) 12381 .controller("MainController", ["$state", "$rootScope", "$log", function ($state, $rootScope, $log) { 12382 this.state = $state; 12383 this.isLoading = true; 12384 var vm = this; 12385 $rootScope.$on("ShowHideLoadingAnimation", function (event, bShowHide) { 12386 vm.isLoading = bShowHide; 12387 }); 12388 }]) 12389 .controller("DomainsController", ["$scope", "$rootScope", "$window", "$http", "$location", "PricingStore", "CustomerType", "CustomerSubType", "CustomerTypeStore", function ($scope, $rootScope, $window, $http, $location, PricingStore, CustomerType, CustomerSubType, CustomerTypeStore) { 12390 var vm = this; 12391 12392 vm.bShowExchangeRate = false;
12393 vm.ExchangeRate = PricingStore.GetExchangeRate().then(function (data) { 12394 vm.ExchangeRate = data; 12395 vm.bShowExchangeRate = true; 12396 }); 12397 12398 $rootScope.$emit("ShowHideLoadingAnimation", true); 12399 12400 PricingStore.QueryDomainPricings().then(function (aPricing) { 12401 vm.pricings = aPricing; 12402 $rootScope.$emit("ShowHideLoadingAnimation", false); 12403 }); 12404 12405 vm.ordering = { 12406 predicate: "ExtensionSortOrder", 12407 reverse: false 12408 } 12409 12410 vm.extensionFilter = ""; 12411 12412 vm.yearSelect = "3"; 12413 12414 vm.showTiers = !!$location.search()["showTiers"]; 12415 12416 vm.toggleTiers = function() { 12417 vm.showTiers = !vm.showTiers 12418 $location.search("showTiers", vm.showTiers ? true : null); 12419 } 12420 12421 $scope.$on("$locationChangeSuccess", function () { vm.showTiers = $location.search()["showTiers"] }); 12422 12423 vm.currentCustomerType = CustomerType; 12424 vm.currentCustomerSubType = CustomerSubType; 12425 vm.selectedCustomerSubType = vm.currentCustomerSubType; 12426 12427 vm.pricingTierNames = CustomerTypeStore; 12428 12429 vm.getTierButtonText = function (customerSubType) { 12430 return vm.selectedCustomerSubType === customerSubType ? "Currently viewing" : "Preview prices below"; 12431 } 12432 12433 /** 12434 * Get pricing for a specific customerSubType 12435 * If called without params, gets cached (faster) for current acc 12436 * @param {string} customerSubType Only allows subtypes associated with correct customerTypes 12437 */ 12438 vm.getPricing = function (customerSubType) { 12439 vm.selectedCustomerSubType = customerSubType || CustomerSubType; 12440 $rootScope.$emit("ShowHideLoadingAnimation", true); 12441 12442 PricingStore.QueryDomainPricings(vm.selectedCustomerSubType).then(function (pricings) { 12443 $rootScope.$emit("ShowHideLoadingAnimation", false); 12444 vm.pricings = pricings; 12445 }); 12446 } 12447 }]) 12448 .controller("HostingController", ["$scope", "$rootScope", function ($scope, $rootScope) { 12449 var vm = this; 12450 12451 vm.termSelect = "12"; 12452 $rootScope.$emit("ShowHideLoadingAnimation", false); 12453 }]) 12454 .controller("SSLController", ["$scope", "$rootScope", function ($scope, $rootScope) { 12455 var vm = this; 12456 $rootScope.$emit("ShowHideLoadingAnimation", false); 12457 }]) 12458 .controller("EmailController", ["$scope", "$rootScope", "$timeout", "$log", "PricingStore", function ($scope, $rootScope, $timeout, $log, PricingStore) { 12459 var vm = this; 12460 vm.termSelect = "12"; 12461 12462 var emailCodes = [ 12463 "EMACTL3Q0001", 12464 "EMACTL3Q0002", 12465 "EMACTL3Q0005", 12466 "EMACTL3Q0010", 12467 "EMACTL4Q0001", 12468 "EMACTL4Q0002", 12469 "EMACTL4Q0005", 12470 "EMACTL4Q0010" 12471 ]; 12472 12473 vm.showTermDropdown = false; 12474 12475 $timeout(function() { 12476 if (PricingStore.QueryPromise) { 12477 PricingStore.QueryPromise.then(function () { 12478 shouldShowTermDropdown(); 12479 $rootScope.$emit("ShowHideLoadingAnimation", false); 12480 }); 12481 } else { 12482 shouldShowTermDropdown(); 12483 } 12484 }); 12485 12486 // The pricing dropdown should only be shown if any of the plans' smallest term's price per term is not the same as its largest counterpart 12487 function shouldShowTermDropdown() { 12488 _.each(emailCodes, function (code) { 12489 var pricing = PricingStore.GetNonDomainPricing(code); 12490 var terms = _.map(_.keys(pricing.NEW), function (key) { return parseInt(key) }).sort(function (a, b) { return a - b }); 12491 if (pricing.NEW[terms[0].toString()].Price !== pricing.NEW[terms[terms.length - 1].toString()].Price) { 12492 vm.showTermDropdown = true; 12493 return; 12494 } 12495 }); 12496 } 12497 }]) 12498 .directive("priceRow", ["PricingStore", function (PricingStore) { 12499 return { 12500 restrict: "A",
12501 template: "<tr>" + 12502 "<td>{{displayName}}</td>" + 12503 "<td ng-if='pricing[\"NEW\"][planTerm]'><small>{{pricing['NEW'][planTerm].DisplayedCurrency}}</small><span class='price'>{{pricing['NEW'][planTerm].DisplayedPrice | currency}}</span><small>/month<span class='on-sale bg-yellow border-rounded text-bold ml5' ng-if='pricing[\"NEW\"][planTerm].PromoCode'>On Sale</span></td>" + 12504 "<td ng-if='!pricing[\"NEW\"][planTerm]'><small>N/A</small></td>" + 12505 "<td ng-if='pricing[\"RENEW\"][planTerm]'><small>{{pricing['NEW'][planTerm].DisplayedCurrency}}</small><span class='price'>{{pricing['RENEW'][planTerm].DisplayedPrice | currency}}</span><small>/month</span><span class='on-sale bg-yellow border-rounded text-bold ml5' ng-if='pricing[\"RENEW\"][planTerm].PromoCode'>On Sale</span></td>" + 12506 "<td ng-if='!pricing[\"RENEW\"][planTerm]'><small>N/A</small></td>" + 12507 "</tr>", 12508 replace: true, 12509 scope: { 12510 displayName: "@", 12511 planCode: "@", 12512 planTerm: "=" 12513 }, 12514 link: function(scope, elem, attrs) { 12515 PricingStore.PlanCodes[attrs.planCode] = true; 12516 scope.pricing = {}; 12517 scope.$watch("planCode", function () { 12518 PricingStore.QueryPricings().then(function() { 12519 scope.pricing = PricingStore.GetNonDomainPricing(scope.planCode); 12520 }); 12521 }); 12522 } 12523 } 12524 }]) 12525 .filter("extensionLandingPageUrl", ["$window", function ($window) { 12526 return function(extension) { 12527 return $window._SmartURL + "domain-registration/register-" + extension.substring(1) + "-domain-names/default.aspx"; 12528 } 12529 }]) 12530 .filter("orderExtensionsToTop", [function() { 12531 return function(pricings, ordering, extensions) { 12532 if (ordering.predicate !== "Extension" || pricings.length === 0) return pricings; 12533 12534 var oExtensions = {}; 12535 var i; 12536 for (i = 0; i < extensions.length; i++) { 12537 oExtensions[extensions[i]] = { extIndex: i }; 12538 } 12539 12540 var sortedPricings = []; 12541 for (i = 0; i < pricings.length; i++) { 12542 if (!!oExtensions[pricings[i].Extension]) { 12543 oExtensions[pricings[i].Extension].pricingIndex = i; 12544 } else { 12545 sortedPricings.push(pricings[i]); 12546 } 12547 } 12548 for (i = extensions.length - 1; i >= 0; --i) { 12549 if (oExtensions[extensions[i]].pricingIndex === undefined) continue; 12550 if (!ordering.reverse) { 12551 sortedPricings.unshift(pricings[oExtensions[extensions[i]].pricingIndex]); 12552 } else { 12553 sortedPricings.push(pricings[oExtensions[extensions[i]].pricingIndex]); 12554 } 12555 } 12556 12557 return sortedPricings; 12558 } 12559 }]); 12560})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.