Keine Beschreibung

ui-router-angularjs.js 87KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015
  1. /**
  2. * State-based routing for AngularJS 1.x
  3. * This bundle requires the ui-router-core.js bundle from the @uirouter/core package.
  4. * @version v1.0.10
  5. * @link https://ui-router.github.io
  6. * @license MIT License, http://www.opensource.org/licenses/MIT
  7. */
  8. (function (global, factory) {
  9. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('angular'), require('@uirouter/core')) :
  10. typeof define === 'function' && define.amd ? define(['exports', 'angular', '@uirouter/core'], factory) :
  11. (factory((global['@uirouter/angularjs'] = {}),global.angular,global['@uirouter/core']));
  12. }(this, (function (exports,ng_from_import,core) { 'use strict';
  13. var ng_from_global = angular;
  14. var ng = (ng_from_import && ng_from_import.module) ? ng_from_import : ng_from_global;
  15. function getNg1ViewConfigFactory() {
  16. var templateFactory = null;
  17. return function (path, view) {
  18. templateFactory = templateFactory || core.services.$injector.get("$templateFactory");
  19. return [new Ng1ViewConfig(path, view, templateFactory)];
  20. };
  21. }
  22. var hasAnyKey = function (keys, obj) {
  23. return keys.reduce(function (acc, key) { return acc || core.isDefined(obj[key]); }, false);
  24. };
  25. /**
  26. * This is a [[StateBuilder.builder]] function for angular1 `views`.
  27. *
  28. * When the [[StateBuilder]] builds a [[StateObject]] object from a raw [[StateDeclaration]], this builder
  29. * handles the `views` property with logic specific to @uirouter/angularjs (ng1).
  30. *
  31. * If no `views: {}` property exists on the [[StateDeclaration]], then it creates the `views` object
  32. * and applies the state-level configuration to a view named `$default`.
  33. */
  34. function ng1ViewsBuilder(state) {
  35. // Do not process root state
  36. if (!state.parent)
  37. return {};
  38. var tplKeys = ['templateProvider', 'templateUrl', 'template', 'notify', 'async'], ctrlKeys = ['controller', 'controllerProvider', 'controllerAs', 'resolveAs'], compKeys = ['component', 'bindings', 'componentProvider'], nonCompKeys = tplKeys.concat(ctrlKeys), allViewKeys = compKeys.concat(nonCompKeys);
  39. // Do not allow a state to have both state-level props and also a `views: {}` property.
  40. // A state without a `views: {}` property can declare properties for the `$default` view as properties of the state.
  41. // However, the `$default` approach should not be mixed with a separate `views: ` block.
  42. if (core.isDefined(state.views) && hasAnyKey(allViewKeys, state)) {
  43. throw new Error("State '" + state.name + "' has a 'views' object. " +
  44. "It cannot also have \"view properties\" at the state level. " +
  45. "Move the following properties into a view (in the 'views' object): " +
  46. (" " + allViewKeys.filter(function (key) { return core.isDefined(state[key]); }).join(", ")));
  47. }
  48. var views = {}, viewsObject = state.views || { "$default": core.pick(state, allViewKeys) };
  49. core.forEach(viewsObject, function (config, name) {
  50. // Account for views: { "": { template... } }
  51. name = name || "$default";
  52. // Account for views: { header: "headerComponent" }
  53. if (core.isString(config))
  54. config = { component: config };
  55. // Make a shallow copy of the config object
  56. config = core.extend({}, config);
  57. // Do not allow a view to mix props for component-style view with props for template/controller-style view
  58. if (hasAnyKey(compKeys, config) && hasAnyKey(nonCompKeys, config)) {
  59. throw new Error("Cannot combine: " + compKeys.join("|") + " with: " + nonCompKeys.join("|") + " in stateview: '" + name + "@" + state.name + "'");
  60. }
  61. config.resolveAs = config.resolveAs || '$resolve';
  62. config.$type = "ng1";
  63. config.$context = state;
  64. config.$name = name;
  65. var normalized = core.ViewService.normalizeUIViewTarget(config.$context, config.$name);
  66. config.$uiViewName = normalized.uiViewName;
  67. config.$uiViewContextAnchor = normalized.uiViewContextAnchor;
  68. views[name] = config;
  69. });
  70. return views;
  71. }
  72. var id = 0;
  73. var Ng1ViewConfig = /** @class */ (function () {
  74. function Ng1ViewConfig(path, viewDecl, factory) {
  75. var _this = this;
  76. this.path = path;
  77. this.viewDecl = viewDecl;
  78. this.factory = factory;
  79. this.$id = id++;
  80. this.loaded = false;
  81. this.getTemplate = function (uiView, context) {
  82. return _this.component ? _this.factory.makeComponentTemplate(uiView, context, _this.component, _this.viewDecl.bindings) : _this.template;
  83. };
  84. }
  85. Ng1ViewConfig.prototype.load = function () {
  86. var _this = this;
  87. var $q = core.services.$q;
  88. var context = new core.ResolveContext(this.path);
  89. var params = this.path.reduce(function (acc, node) { return core.extend(acc, node.paramValues); }, {});
  90. var promises = {
  91. template: $q.when(this.factory.fromConfig(this.viewDecl, params, context)),
  92. controller: $q.when(this.getController(context))
  93. };
  94. return $q.all(promises).then(function (results) {
  95. core.trace.traceViewServiceEvent("Loaded", _this);
  96. _this.controller = results.controller;
  97. core.extend(_this, results.template); // Either { template: "tpl" } or { component: "cmpName" }
  98. return _this;
  99. });
  100. };
  101. /**
  102. * Gets the controller for a view configuration.
  103. *
  104. * @returns {Function|Promise.<Function>} Returns a controller, or a promise that resolves to a controller.
  105. */
  106. Ng1ViewConfig.prototype.getController = function (context) {
  107. var provider = this.viewDecl.controllerProvider;
  108. if (!core.isInjectable(provider))
  109. return this.viewDecl.controller;
  110. var deps = core.services.$injector.annotate(provider);
  111. var providerFn = core.isArray(provider) ? core.tail(provider) : provider;
  112. var resolvable = new core.Resolvable("", providerFn, deps);
  113. return resolvable.get(context);
  114. };
  115. return Ng1ViewConfig;
  116. }());
  117. /** @module view */
  118. /** for typedoc */
  119. /**
  120. * Service which manages loading of templates from a ViewConfig.
  121. */
  122. var TemplateFactory = /** @class */ (function () {
  123. function TemplateFactory() {
  124. var _this = this;
  125. /** @hidden */ this._useHttp = ng.version.minor < 3;
  126. /** @hidden */ this.$get = ['$http', '$templateCache', '$injector', function ($http, $templateCache, $injector) {
  127. _this.$templateRequest = $injector.has && $injector.has('$templateRequest') && $injector.get('$templateRequest');
  128. _this.$http = $http;
  129. _this.$templateCache = $templateCache;
  130. return _this;
  131. }];
  132. }
  133. /** @hidden */
  134. TemplateFactory.prototype.useHttpService = function (value) {
  135. this._useHttp = value;
  136. };
  137. /**
  138. * Creates a template from a configuration object.
  139. *
  140. * @param config Configuration object for which to load a template.
  141. * The following properties are search in the specified order, and the first one
  142. * that is defined is used to create the template:
  143. *
  144. * @param params Parameters to pass to the template function.
  145. * @param context The resolve context associated with the template's view
  146. *
  147. * @return {string|object} The template html as a string, or a promise for
  148. * that string,or `null` if no template is configured.
  149. */
  150. TemplateFactory.prototype.fromConfig = function (config, params, context) {
  151. var defaultTemplate = "<ui-view></ui-view>";
  152. var asTemplate = function (result) { return core.services.$q.when(result).then(function (str) { return ({ template: str }); }); };
  153. var asComponent = function (result) { return core.services.$q.when(result).then(function (str) { return ({ component: str }); }); };
  154. return (core.isDefined(config.template) ? asTemplate(this.fromString(config.template, params)) :
  155. core.isDefined(config.templateUrl) ? asTemplate(this.fromUrl(config.templateUrl, params)) :
  156. core.isDefined(config.templateProvider) ? asTemplate(this.fromProvider(config.templateProvider, params, context)) :
  157. core.isDefined(config.component) ? asComponent(config.component) :
  158. core.isDefined(config.componentProvider) ? asComponent(this.fromComponentProvider(config.componentProvider, params, context)) :
  159. asTemplate(defaultTemplate));
  160. };
  161. /**
  162. * Creates a template from a string or a function returning a string.
  163. *
  164. * @param template html template as a string or function that returns an html template as a string.
  165. * @param params Parameters to pass to the template function.
  166. *
  167. * @return {string|object} The template html as a string, or a promise for that
  168. * string.
  169. */
  170. TemplateFactory.prototype.fromString = function (template, params) {
  171. return core.isFunction(template) ? template(params) : template;
  172. };
  173. /**
  174. * Loads a template from the a URL via `$http` and `$templateCache`.
  175. *
  176. * @param {string|Function} url url of the template to load, or a function
  177. * that returns a url.
  178. * @param {Object} params Parameters to pass to the url function.
  179. * @return {string|Promise.<string>} The template html as a string, or a promise
  180. * for that string.
  181. */
  182. TemplateFactory.prototype.fromUrl = function (url, params) {
  183. if (core.isFunction(url))
  184. url = url(params);
  185. if (url == null)
  186. return null;
  187. if (this._useHttp) {
  188. return this.$http.get(url, { cache: this.$templateCache, headers: { Accept: 'text/html' } })
  189. .then(function (response) {
  190. return response.data;
  191. });
  192. }
  193. return this.$templateRequest(url);
  194. };
  195. /**
  196. * Creates a template by invoking an injectable provider function.
  197. *
  198. * @param provider Function to invoke via `locals`
  199. * @param {Function} injectFn a function used to invoke the template provider
  200. * @return {string|Promise.<string>} The template html as a string, or a promise
  201. * for that string.
  202. */
  203. TemplateFactory.prototype.fromProvider = function (provider, params, context) {
  204. var deps = core.services.$injector.annotate(provider);
  205. var providerFn = core.isArray(provider) ? core.tail(provider) : provider;
  206. var resolvable = new core.Resolvable("", providerFn, deps);
  207. return resolvable.get(context);
  208. };
  209. /**
  210. * Creates a component's template by invoking an injectable provider function.
  211. *
  212. * @param provider Function to invoke via `locals`
  213. * @param {Function} injectFn a function used to invoke the template provider
  214. * @return {string} The template html as a string: "<component-name input1='::$resolve.foo'></component-name>".
  215. */
  216. TemplateFactory.prototype.fromComponentProvider = function (provider, params, context) {
  217. var deps = core.services.$injector.annotate(provider);
  218. var providerFn = core.isArray(provider) ? core.tail(provider) : provider;
  219. var resolvable = new core.Resolvable("", providerFn, deps);
  220. return resolvable.get(context);
  221. };
  222. /**
  223. * Creates a template from a component's name
  224. *
  225. * This implements route-to-component.
  226. * It works by retrieving the component (directive) metadata from the injector.
  227. * It analyses the component's bindings, then constructs a template that instantiates the component.
  228. * The template wires input and output bindings to resolves or from the parent component.
  229. *
  230. * @param uiView {object} The parent ui-view (for binding outputs to callbacks)
  231. * @param context The ResolveContext (for binding outputs to callbacks returned from resolves)
  232. * @param component {string} Component's name in camel case.
  233. * @param bindings An object defining the component's bindings: {foo: '<'}
  234. * @return {string} The template as a string: "<component-name input1='::$resolve.foo'></component-name>".
  235. */
  236. TemplateFactory.prototype.makeComponentTemplate = function (uiView, context, component, bindings) {
  237. bindings = bindings || {};
  238. // Bind once prefix
  239. var prefix = ng.version.minor >= 3 ? "::" : "";
  240. // Convert to kebob name. Add x- prefix if the string starts with `x-` or `data-`
  241. var kebob = function (camelCase) {
  242. var kebobed = core.kebobString(camelCase);
  243. return /^(x|data)-/.exec(kebobed) ? "x-" + kebobed : kebobed;
  244. };
  245. var attributeTpl = function (input) {
  246. var name = input.name, type = input.type;
  247. var attrName = kebob(name);
  248. // If the ui-view has an attribute which matches a binding on the routed component
  249. // then pass that attribute through to the routed component template.
  250. // Prefer ui-view wired mappings to resolve data, unless the resolve was explicitly bound using `bindings:`
  251. if (uiView.attr(attrName) && !bindings[name])
  252. return attrName + "='" + uiView.attr(attrName) + "'";
  253. var resolveName = bindings[name] || name;
  254. // Pre-evaluate the expression for "@" bindings by enclosing in {{ }}
  255. // some-attr="{{ ::$resolve.someResolveName }}"
  256. if (type === '@')
  257. return attrName + "='{{" + prefix + "$resolve." + resolveName + "}}'";
  258. // Wire "&" callbacks to resolves that return a callback function
  259. // Get the result of the resolve (should be a function) and annotate it to get its arguments.
  260. // some-attr="$resolve.someResolveResultName(foo, bar)"
  261. if (type === '&') {
  262. var res = context.getResolvable(resolveName);
  263. var fn = res && res.data;
  264. var args = fn && core.services.$injector.annotate(fn) || [];
  265. // account for array style injection, i.e., ['foo', function(foo) {}]
  266. var arrayIdxStr = core.isArray(fn) ? "[" + (fn.length - 1) + "]" : '';
  267. return attrName + "='$resolve." + resolveName + arrayIdxStr + "(" + args.join(",") + ")'";
  268. }
  269. // some-attr="::$resolve.someResolveName"
  270. return attrName + "='" + prefix + "$resolve." + resolveName + "'";
  271. };
  272. var attrs = getComponentBindings(component).map(attributeTpl).join(" ");
  273. var kebobName = kebob(component);
  274. return "<" + kebobName + " " + attrs + "></" + kebobName + ">";
  275. };
  276. return TemplateFactory;
  277. }());
  278. // Gets all the directive(s)' inputs ('@', '=', and '<') and outputs ('&')
  279. function getComponentBindings(name) {
  280. var cmpDefs = core.services.$injector.get(name + "Directive"); // could be multiple
  281. if (!cmpDefs || !cmpDefs.length)
  282. throw new Error("Unable to find component named '" + name + "'");
  283. return cmpDefs.map(getBindings).reduce(core.unnestR, []);
  284. }
  285. // Given a directive definition, find its object input attributes
  286. // Use different properties, depending on the type of directive (component, bindToController, normal)
  287. var getBindings = function (def) {
  288. if (core.isObject(def.bindToController))
  289. return scopeBindings(def.bindToController);
  290. return scopeBindings(def.scope);
  291. };
  292. // for ng 1.2 style, process the scope: { input: "=foo" }
  293. // for ng 1.3 through ng 1.5, process the component's bindToController: { input: "=foo" } object
  294. var scopeBindings = function (bindingsObj) { return Object.keys(bindingsObj || {})
  295. .map(function (key) { return [key, /^([=<@&])[?]?(.*)/.exec(bindingsObj[key])]; })
  296. .filter(function (tuple) { return core.isDefined(tuple) && core.isArray(tuple[1]); })
  297. .map(function (tuple) { return ({ name: tuple[1][2] || tuple[0], type: tuple[1][1] }); }); };
  298. /** @module ng1 */ /** for typedoc */
  299. /**
  300. * The Angular 1 `StateProvider`
  301. *
  302. * The `$stateProvider` works similar to Angular's v1 router, but it focuses purely
  303. * on state.
  304. *
  305. * A state corresponds to a "place" in the application in terms of the overall UI and
  306. * navigation. A state describes (via the controller / template / view properties) what
  307. * the UI looks like and does at that place.
  308. *
  309. * States often have things in common, and the primary way of factoring out these
  310. * commonalities in this model is via the state hierarchy, i.e. parent/child states aka
  311. * nested states.
  312. *
  313. * The `$stateProvider` provides interfaces to declare these states for your app.
  314. */
  315. var StateProvider = /** @class */ (function () {
  316. function StateProvider(stateRegistry, stateService) {
  317. this.stateRegistry = stateRegistry;
  318. this.stateService = stateService;
  319. core.createProxyFunctions(core.val(StateProvider.prototype), this, core.val(this));
  320. }
  321. /**
  322. * Decorates states when they are registered
  323. *
  324. * Allows you to extend (carefully) or override (at your own peril) the
  325. * `stateBuilder` object used internally by [[StateRegistry]].
  326. * This can be used to add custom functionality to ui-router,
  327. * for example inferring templateUrl based on the state name.
  328. *
  329. * When passing only a name, it returns the current (original or decorated) builder
  330. * function that matches `name`.
  331. *
  332. * The builder functions that can be decorated are listed below. Though not all
  333. * necessarily have a good use case for decoration, that is up to you to decide.
  334. *
  335. * In addition, users can attach custom decorators, which will generate new
  336. * properties within the state's internal definition. There is currently no clear
  337. * use-case for this beyond accessing internal states (i.e. $state.$current),
  338. * however, expect this to become increasingly relevant as we introduce additional
  339. * meta-programming features.
  340. *
  341. * **Warning**: Decorators should not be interdependent because the order of
  342. * execution of the builder functions in non-deterministic. Builder functions
  343. * should only be dependent on the state definition object and super function.
  344. *
  345. *
  346. * Existing builder functions and current return values:
  347. *
  348. * - **parent** `{object}` - returns the parent state object.
  349. * - **data** `{object}` - returns state data, including any inherited data that is not
  350. * overridden by own values (if any).
  351. * - **url** `{object}` - returns a {@link ui.router.util.type:UrlMatcher UrlMatcher}
  352. * or `null`.
  353. * - **navigable** `{object}` - returns closest ancestor state that has a URL (aka is
  354. * navigable).
  355. * - **params** `{object}` - returns an array of state params that are ensured to
  356. * be a super-set of parent's params.
  357. * - **views** `{object}` - returns a views object where each key is an absolute view
  358. * name (i.e. "viewName@stateName") and each value is the config object
  359. * (template, controller) for the view. Even when you don't use the views object
  360. * explicitly on a state config, one is still created for you internally.
  361. * So by decorating this builder function you have access to decorating template
  362. * and controller properties.
  363. * - **ownParams** `{object}` - returns an array of params that belong to the state,
  364. * not including any params defined by ancestor states.
  365. * - **path** `{string}` - returns the full path from the root down to this state.
  366. * Needed for state activation.
  367. * - **includes** `{object}` - returns an object that includes every state that
  368. * would pass a `$state.includes()` test.
  369. *
  370. * #### Example:
  371. * Override the internal 'views' builder with a function that takes the state
  372. * definition, and a reference to the internal function being overridden:
  373. * ```js
  374. * $stateProvider.decorator('views', function (state, parent) {
  375. * let result = {},
  376. * views = parent(state);
  377. *
  378. * angular.forEach(views, function (config, name) {
  379. * let autoName = (state.name + '.' + name).replace('.', '/');
  380. * config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html';
  381. * result[name] = config;
  382. * });
  383. * return result;
  384. * });
  385. *
  386. * $stateProvider.state('home', {
  387. * views: {
  388. * 'contact.list': { controller: 'ListController' },
  389. * 'contact.item': { controller: 'ItemController' }
  390. * }
  391. * });
  392. * ```
  393. *
  394. *
  395. * ```js
  396. * // Auto-populates list and item views with /partials/home/contact/list.html,
  397. * // and /partials/home/contact/item.html, respectively.
  398. * $state.go('home');
  399. * ```
  400. *
  401. * @param {string} name The name of the builder function to decorate.
  402. * @param {object} func A function that is responsible for decorating the original
  403. * builder function. The function receives two parameters:
  404. *
  405. * - `{object}` - state - The state config object.
  406. * - `{object}` - super - The original builder function.
  407. *
  408. * @return {object} $stateProvider - $stateProvider instance
  409. */
  410. StateProvider.prototype.decorator = function (name, func) {
  411. return this.stateRegistry.decorator(name, func) || this;
  412. };
  413. StateProvider.prototype.state = function (name, definition) {
  414. if (core.isObject(name)) {
  415. definition = name;
  416. }
  417. else {
  418. definition.name = name;
  419. }
  420. this.stateRegistry.register(definition);
  421. return this;
  422. };
  423. /**
  424. * Registers an invalid state handler
  425. *
  426. * This is a passthrough to [[StateService.onInvalid]] for ng1.
  427. */
  428. StateProvider.prototype.onInvalid = function (callback) {
  429. return this.stateService.onInvalid(callback);
  430. };
  431. return StateProvider;
  432. }());
  433. /** @module ng1 */ /** */
  434. /**
  435. * This is a [[StateBuilder.builder]] function for angular1 `onEnter`, `onExit`,
  436. * `onRetain` callback hooks on a [[Ng1StateDeclaration]].
  437. *
  438. * When the [[StateBuilder]] builds a [[StateObject]] object from a raw [[StateDeclaration]], this builder
  439. * ensures that those hooks are injectable for @uirouter/angularjs (ng1).
  440. */
  441. var getStateHookBuilder = function (hookName) {
  442. return function stateHookBuilder(state, parentFn) {
  443. var hook = state[hookName];
  444. var pathname = hookName === 'onExit' ? 'from' : 'to';
  445. function decoratedNg1Hook(trans, state) {
  446. var resolveContext = new core.ResolveContext(trans.treeChanges(pathname));
  447. var locals = core.extend(getLocals(resolveContext), { $state$: state, $transition$: trans });
  448. return core.services.$injector.invoke(hook, this, locals);
  449. }
  450. return hook ? decoratedNg1Hook : undefined;
  451. };
  452. };
  453. /**
  454. * Implements UI-Router LocationServices and LocationConfig using Angular 1's $location service
  455. */
  456. var Ng1LocationServices = /** @class */ (function () {
  457. function Ng1LocationServices($locationProvider) {
  458. // .onChange() registry
  459. this._urlListeners = [];
  460. this.$locationProvider = $locationProvider;
  461. var _lp = core.val($locationProvider);
  462. core.createProxyFunctions(_lp, this, _lp, ['hashPrefix']);
  463. }
  464. Ng1LocationServices.prototype.dispose = function () { };
  465. Ng1LocationServices.prototype.onChange = function (callback) {
  466. var _this = this;
  467. this._urlListeners.push(callback);
  468. return function () { return core.removeFrom(_this._urlListeners)(callback); };
  469. };
  470. Ng1LocationServices.prototype.html5Mode = function () {
  471. var html5Mode = this.$locationProvider.html5Mode();
  472. html5Mode = core.isObject(html5Mode) ? html5Mode.enabled : html5Mode;
  473. return html5Mode && this.$sniffer.history;
  474. };
  475. Ng1LocationServices.prototype.url = function (newUrl, replace, state) {
  476. if (replace === void 0) { replace = false; }
  477. if (newUrl)
  478. this.$location.url(newUrl);
  479. if (replace)
  480. this.$location.replace();
  481. if (state)
  482. this.$location.state(state);
  483. return this.$location.url();
  484. };
  485. Ng1LocationServices.prototype._runtimeServices = function ($rootScope, $location, $sniffer, $browser) {
  486. var _this = this;
  487. this.$location = $location;
  488. this.$sniffer = $sniffer;
  489. // Bind $locationChangeSuccess to the listeners registered in LocationService.onChange
  490. $rootScope.$on("$locationChangeSuccess", function (evt) { return _this._urlListeners.forEach(function (fn) { return fn(evt); }); });
  491. var _loc = core.val($location);
  492. var _browser = core.val($browser);
  493. // Bind these LocationService functions to $location
  494. core.createProxyFunctions(_loc, this, _loc, ["replace", "path", "search", "hash"]);
  495. // Bind these LocationConfig functions to $location
  496. core.createProxyFunctions(_loc, this, _loc, ['port', 'protocol', 'host']);
  497. // Bind these LocationConfig functions to $browser
  498. core.createProxyFunctions(_browser, this, _browser, ['baseHref']);
  499. };
  500. /**
  501. * Applys ng1-specific path parameter encoding
  502. *
  503. * The Angular 1 `$location` service is a bit weird.
  504. * It doesn't allow slashes to be encoded/decoded bi-directionally.
  505. *
  506. * See the writeup at https://github.com/angular-ui/ui-router/issues/2598
  507. *
  508. * This code patches the `path` parameter type so it encoded/decodes slashes as ~2F
  509. *
  510. * @param router
  511. */
  512. Ng1LocationServices.monkeyPatchPathParameterType = function (router) {
  513. var pathType = router.urlMatcherFactory.type('path');
  514. pathType.encode = function (val$$1) {
  515. return val$$1 != null ? val$$1.toString().replace(/(~|\/)/g, function (m) { return ({ '~': '~~', '/': '~2F' }[m]); }) : val$$1;
  516. };
  517. pathType.decode = function (val$$1) {
  518. return val$$1 != null ? val$$1.toString().replace(/(~~|~2F)/g, function (m) { return ({ '~~': '~', '~2F': '/' }[m]); }) : val$$1;
  519. };
  520. };
  521. return Ng1LocationServices;
  522. }());
  523. /** @module url */ /** */
  524. /**
  525. * Manages rules for client-side URL
  526. *
  527. * ### Deprecation warning:
  528. * This class is now considered to be an internal API
  529. * Use the [[UrlService]] instead.
  530. * For configuring URL rules, use the [[UrlRulesApi]] which can be found as [[UrlService.rules]].
  531. *
  532. * This class manages the router rules for what to do when the URL changes.
  533. *
  534. * This provider remains for backwards compatibility.
  535. *
  536. * @deprecated
  537. */
  538. var UrlRouterProvider = /** @class */ (function () {
  539. /** @hidden */
  540. function UrlRouterProvider(router) {
  541. this._router = router;
  542. this._urlRouter = router.urlRouter;
  543. }
  544. /** @hidden */
  545. UrlRouterProvider.prototype.$get = function () {
  546. var urlRouter = this._urlRouter;
  547. urlRouter.update(true);
  548. if (!urlRouter.interceptDeferred)
  549. urlRouter.listen();
  550. return urlRouter;
  551. };
  552. /**
  553. * Registers a url handler function.
  554. *
  555. * Registers a low level url handler (a `rule`).
  556. * A rule detects specific URL patterns and returns a redirect, or performs some action.
  557. *
  558. * If a rule returns a string, the URL is replaced with the string, and all rules are fired again.
  559. *
  560. * #### Example:
  561. * ```js
  562. * var app = angular.module('app', ['ui.router.router']);
  563. *
  564. * app.config(function ($urlRouterProvider) {
  565. * // Here's an example of how you might allow case insensitive urls
  566. * $urlRouterProvider.rule(function ($injector, $location) {
  567. * var path = $location.path(),
  568. * normalized = path.toLowerCase();
  569. *
  570. * if (path !== normalized) {
  571. * return normalized;
  572. * }
  573. * });
  574. * });
  575. * ```
  576. *
  577. * @param ruleFn
  578. * Handler function that takes `$injector` and `$location` services as arguments.
  579. * You can use them to detect a url and return a different url as a string.
  580. *
  581. * @return [[UrlRouterProvider]] (`this`)
  582. */
  583. UrlRouterProvider.prototype.rule = function (ruleFn) {
  584. var _this = this;
  585. if (!core.isFunction(ruleFn))
  586. throw new Error("'rule' must be a function");
  587. var match = function () {
  588. return ruleFn(core.services.$injector, _this._router.locationService);
  589. };
  590. var rule = new core.BaseUrlRule(match, core.identity);
  591. this._urlRouter.rule(rule);
  592. return this;
  593. };
  594. /**
  595. * Defines the path or behavior to use when no url can be matched.
  596. *
  597. * #### Example:
  598. * ```js
  599. * var app = angular.module('app', ['ui.router.router']);
  600. *
  601. * app.config(function ($urlRouterProvider) {
  602. * // if the path doesn't match any of the urls you configured
  603. * // otherwise will take care of routing the user to the
  604. * // specified url
  605. * $urlRouterProvider.otherwise('/index');
  606. *
  607. * // Example of using function rule as param
  608. * $urlRouterProvider.otherwise(function ($injector, $location) {
  609. * return '/a/valid/url';
  610. * });
  611. * });
  612. * ```
  613. *
  614. * @param rule
  615. * The url path you want to redirect to or a function rule that returns the url path or performs a `$state.go()`.
  616. * The function version is passed two params: `$injector` and `$location` services, and should return a url string.
  617. *
  618. * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
  619. */
  620. UrlRouterProvider.prototype.otherwise = function (rule) {
  621. var _this = this;
  622. var urlRouter = this._urlRouter;
  623. if (core.isString(rule)) {
  624. urlRouter.otherwise(rule);
  625. }
  626. else if (core.isFunction(rule)) {
  627. urlRouter.otherwise(function () { return rule(core.services.$injector, _this._router.locationService); });
  628. }
  629. else {
  630. throw new Error("'rule' must be a string or function");
  631. }
  632. return this;
  633. };
  634. /**
  635. * Registers a handler for a given url matching.
  636. *
  637. * If the handler is a string, it is
  638. * treated as a redirect, and is interpolated according to the syntax of match
  639. * (i.e. like `String.replace()` for `RegExp`, or like a `UrlMatcher` pattern otherwise).
  640. *
  641. * If the handler is a function, it is injectable.
  642. * It gets invoked if `$location` matches.
  643. * You have the option of inject the match object as `$match`.
  644. *
  645. * The handler can return
  646. *
  647. * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter`
  648. * will continue trying to find another one that matches.
  649. * - **string** which is treated as a redirect and passed to `$location.url()`
  650. * - **void** or any **truthy** value tells `$urlRouter` that the url was handled.
  651. *
  652. * #### Example:
  653. * ```js
  654. * var app = angular.module('app', ['ui.router.router']);
  655. *
  656. * app.config(function ($urlRouterProvider) {
  657. * $urlRouterProvider.when($state.url, function ($match, $stateParams) {
  658. * if ($state.$current.navigable !== state ||
  659. * !equalForKeys($match, $stateParams) {
  660. * $state.transitionTo(state, $match, false);
  661. * }
  662. * });
  663. * });
  664. * ```
  665. *
  666. * @param what A pattern string to match, compiled as a [[UrlMatcher]].
  667. * @param handler The path (or function that returns a path) that you want to redirect your user to.
  668. * @param ruleCallback [optional] A callback that receives the `rule` registered with [[UrlMatcher.rule]]
  669. *
  670. * Note: the handler may also invoke arbitrary code, such as `$state.go()`
  671. */
  672. UrlRouterProvider.prototype.when = function (what, handler) {
  673. if (core.isArray(handler) || core.isFunction(handler)) {
  674. handler = UrlRouterProvider.injectableHandler(this._router, handler);
  675. }
  676. this._urlRouter.when(what, handler);
  677. return this;
  678. };
  679. UrlRouterProvider.injectableHandler = function (router, handler) {
  680. return function (match) {
  681. return core.services.$injector.invoke(handler, null, { $match: match, $stateParams: router.globals.params });
  682. };
  683. };
  684. /**
  685. * Disables monitoring of the URL.
  686. *
  687. * Call this method before UI-Router has bootstrapped.
  688. * It will stop UI-Router from performing the initial url sync.
  689. *
  690. * This can be useful to perform some asynchronous initialization before the router starts.
  691. * Once the initialization is complete, call [[listen]] to tell UI-Router to start watching and synchronizing the URL.
  692. *
  693. * #### Example:
  694. * ```js
  695. * var app = angular.module('app', ['ui.router']);
  696. *
  697. * app.config(function ($urlRouterProvider) {
  698. * // Prevent $urlRouter from automatically intercepting URL changes;
  699. * $urlRouterProvider.deferIntercept();
  700. * })
  701. *
  702. * app.run(function (MyService, $urlRouter, $http) {
  703. * $http.get("/stuff").then(function(resp) {
  704. * MyService.doStuff(resp.data);
  705. * $urlRouter.listen();
  706. * $urlRouter.sync();
  707. * });
  708. * });
  709. * ```
  710. *
  711. * @param defer Indicates whether to defer location change interception.
  712. * Passing no parameter is equivalent to `true`.
  713. */
  714. UrlRouterProvider.prototype.deferIntercept = function (defer) {
  715. this._urlRouter.deferIntercept(defer);
  716. };
  717. return UrlRouterProvider;
  718. }());
  719. /**
  720. * # Angular 1 types
  721. *
  722. * UI-Router core provides various Typescript types which you can use for code completion and validating parameter values, etc.
  723. * The customizations to the core types for Angular UI-Router are documented here.
  724. *
  725. * The optional [[$resolve]] service is also documented here.
  726. *
  727. * @module ng1
  728. * @preferred
  729. */
  730. /** for typedoc */
  731. ng.module("ui.router.angular1", []);
  732. var mod_init = ng.module('ui.router.init', []);
  733. var mod_util = ng.module('ui.router.util', ['ng', 'ui.router.init']);
  734. var mod_rtr = ng.module('ui.router.router', ['ui.router.util']);
  735. var mod_state = ng.module('ui.router.state', ['ui.router.router', 'ui.router.util', 'ui.router.angular1']);
  736. var mod_main = ng.module('ui.router', ['ui.router.init', 'ui.router.state', 'ui.router.angular1']);
  737. var mod_cmpt = ng.module('ui.router.compat', ['ui.router']); // tslint:disable-line
  738. var router = null;
  739. $uiRouter.$inject = ['$locationProvider'];
  740. /** This angular 1 provider instantiates a Router and exposes its services via the angular injector */
  741. function $uiRouter($locationProvider) {
  742. // Create a new instance of the Router when the $uiRouterProvider is initialized
  743. router = this.router = new core.UIRouter();
  744. router.stateProvider = new StateProvider(router.stateRegistry, router.stateService);
  745. // Apply ng1 specific StateBuilder code for `views`, `resolve`, and `onExit/Retain/Enter` properties
  746. router.stateRegistry.decorator("views", ng1ViewsBuilder);
  747. router.stateRegistry.decorator("onExit", getStateHookBuilder("onExit"));
  748. router.stateRegistry.decorator("onRetain", getStateHookBuilder("onRetain"));
  749. router.stateRegistry.decorator("onEnter", getStateHookBuilder("onEnter"));
  750. router.viewService._pluginapi._viewConfigFactory('ng1', getNg1ViewConfigFactory());
  751. var ng1LocationService = router.locationService = router.locationConfig = new Ng1LocationServices($locationProvider);
  752. Ng1LocationServices.monkeyPatchPathParameterType(router);
  753. // backwards compat: also expose router instance as $uiRouterProvider.router
  754. router['router'] = router;
  755. router['$get'] = $get;
  756. $get.$inject = ['$location', '$browser', '$sniffer', '$rootScope', '$http', '$templateCache'];
  757. function $get($location, $browser, $sniffer, $rootScope, $http, $templateCache) {
  758. ng1LocationService._runtimeServices($rootScope, $location, $sniffer, $browser);
  759. delete router['router'];
  760. delete router['$get'];
  761. return router;
  762. }
  763. return router;
  764. }
  765. var getProviderFor = function (serviceName) { return ['$uiRouterProvider', function ($urp) {
  766. var service = $urp.router[serviceName];
  767. service["$get"] = function () { return service; };
  768. return service;
  769. }]; };
  770. // This effectively calls $get() on `$uiRouterProvider` to trigger init (when ng enters runtime)
  771. runBlock.$inject = ['$injector', '$q', '$uiRouter'];
  772. function runBlock($injector, $q, $uiRouter) {
  773. core.services.$injector = $injector;
  774. core.services.$q = $q;
  775. // The $injector is now available.
  776. // Find any resolvables that had dependency annotation deferred
  777. $uiRouter.stateRegistry.get()
  778. .map(function (x) { return x.$$state().resolvables; })
  779. .reduce(core.unnestR, [])
  780. .filter(function (x) { return x.deps === "deferred"; })
  781. .forEach(function (resolvable) { return resolvable.deps = $injector.annotate(resolvable.resolveFn, $injector.strictDi); });
  782. }
  783. // $urlRouter service and $urlRouterProvider
  784. var getUrlRouterProvider = function (uiRouter) {
  785. return uiRouter.urlRouterProvider = new UrlRouterProvider(uiRouter);
  786. };
  787. // $state service and $stateProvider
  788. // $urlRouter service and $urlRouterProvider
  789. var getStateProvider = function () {
  790. return core.extend(router.stateProvider, { $get: function () { return router.stateService; } });
  791. };
  792. watchDigests.$inject = ['$rootScope'];
  793. function watchDigests($rootScope) {
  794. $rootScope.$watch(function () { core.trace.approximateDigests++; });
  795. }
  796. mod_init.provider("$uiRouter", $uiRouter);
  797. mod_rtr.provider('$urlRouter', ['$uiRouterProvider', getUrlRouterProvider]);
  798. mod_util.provider('$urlService', getProviderFor('urlService'));
  799. mod_util.provider('$urlMatcherFactory', ['$uiRouterProvider', function () { return router.urlMatcherFactory; }]);
  800. mod_util.provider('$templateFactory', function () { return new TemplateFactory(); });
  801. mod_state.provider('$stateRegistry', getProviderFor('stateRegistry'));
  802. mod_state.provider('$uiRouterGlobals', getProviderFor('globals'));
  803. mod_state.provider('$transitions', getProviderFor('transitionService'));
  804. mod_state.provider('$state', ['$uiRouterProvider', getStateProvider]);
  805. mod_state.factory('$stateParams', ['$uiRouter', function ($uiRouter) { return $uiRouter.globals.params; }]);
  806. mod_main.factory('$view', function () { return router.viewService; });
  807. mod_main.service("$trace", function () { return core.trace; });
  808. mod_main.run(watchDigests);
  809. mod_util.run(['$urlMatcherFactory', function ($urlMatcherFactory) { }]);
  810. mod_state.run(['$state', function ($state) { }]);
  811. mod_rtr.run(['$urlRouter', function ($urlRouter) { }]);
  812. mod_init.run(runBlock);
  813. /** @hidden TODO: find a place to move this */
  814. var getLocals = function (ctx) {
  815. var tokens = ctx.getTokens().filter(core.isString);
  816. var tuples = tokens.map(function (key) {
  817. var resolvable = ctx.getResolvable(key);
  818. var waitPolicy = ctx.getPolicy(resolvable).async;
  819. return [key, waitPolicy === 'NOWAIT' ? resolvable.promise : resolvable.data];
  820. });
  821. return tuples.reduce(core.applyPairs, {});
  822. };
  823. /**
  824. * # Angular 1 injectable services
  825. *
  826. * This is a list of the objects which can be injected using angular's injector.
  827. *
  828. * There are three different kind of injectable objects:
  829. *
  830. * ## **Provider** objects
  831. * #### injectable into a `.config()` block during configtime
  832. *
  833. * - [[$uiRouterProvider]]: The UI-Router instance
  834. * - [[$stateProvider]]: State registration
  835. * - [[$transitionsProvider]]: Transition hooks
  836. * - [[$urlServiceProvider]]: All URL related public APIs
  837. *
  838. * - [[$uiViewScrollProvider]]: Disable ui-router view scrolling
  839. * - [[$urlRouterProvider]]: (deprecated) Url matching rules
  840. * - [[$urlMatcherFactoryProvider]]: (deprecated) Url parsing config
  841. *
  842. * ## **Service** objects
  843. * #### injectable globally during runtime
  844. *
  845. * - [[$uiRouter]]: The UI-Router instance
  846. * - [[$trace]]: Enable transition trace/debug
  847. * - [[$transitions]]: Transition hooks
  848. * - [[$state]]: Imperative state related APIs
  849. * - [[$stateRegistry]]: State registration
  850. * - [[$urlService]]: All URL related public APIs
  851. * - [[$uiRouterGlobals]]: Global variables
  852. * - [[$uiViewScroll]]: Scroll an element into view
  853. *
  854. * - [[$stateParams]]: (deprecated) Global state param values
  855. * - [[$urlRouter]]: (deprecated) URL synchronization
  856. * - [[$urlMatcherFactory]]: (deprecated) URL parsing config
  857. *
  858. * ## **Per-Transition** objects
  859. *
  860. * - These kind of objects are injectable into:
  861. * - Resolves ([[Ng1StateDeclaration.resolve]]),
  862. * - Transition Hooks ([[TransitionService.onStart]], etc),
  863. * - Routed Controllers ([[Ng1ViewDeclaration.controller]])
  864. *
  865. * #### Different instances are injected based on the [[Transition]]
  866. *
  867. * - [[$transition$]]: The current Transition object
  868. * - [[$stateParams]]: State param values for pending Transition (deprecated)
  869. * - Any resolve data defined using [[Ng1StateDeclaration.resolve]]
  870. *
  871. * @ng1api
  872. * @preferred
  873. * @module injectables
  874. */ /** */
  875. /**
  876. * The current (or pending) State Parameters
  877. *
  878. * An injectable global **Service Object** which holds the state parameters for the latest **SUCCESSFUL** transition.
  879. *
  880. * The values are not updated until *after* a `Transition` successfully completes.
  881. *
  882. * **Also:** an injectable **Per-Transition Object** object which holds the pending state parameters for the pending `Transition` currently running.
  883. *
  884. * ### Deprecation warning:
  885. *
  886. * The value injected for `$stateParams` is different depending on where it is injected.
  887. *
  888. * - When injected into an angular service, the object injected is the global **Service Object** with the parameter values for the latest successful `Transition`.
  889. * - When injected into transition hooks, resolves, or view controllers, the object is the **Per-Transition Object** with the parameter values for the running `Transition`.
  890. *
  891. * Because of these confusing details, this service is deprecated.
  892. *
  893. * ### Instead of using the global `$stateParams` service object,
  894. * inject [[$uiRouterGlobals]] and use [[UIRouterGlobals.params]]
  895. *
  896. * ```js
  897. * MyService.$inject = ['$uiRouterGlobals'];
  898. * function MyService($uiRouterGlobals) {
  899. * return {
  900. * paramValues: function () {
  901. * return $uiRouterGlobals.params;
  902. * }
  903. * }
  904. * }
  905. * ```
  906. *
  907. * ### Instead of using the per-transition `$stateParams` object,
  908. * inject the current `Transition` (as [[$transition$]]) and use [[Transition.params]]
  909. *
  910. * ```js
  911. * MyController.$inject = ['$transition$'];
  912. * function MyController($transition$) {
  913. * var username = $transition$.params().username;
  914. * // .. do something with username
  915. * }
  916. * ```
  917. *
  918. * ---
  919. *
  920. * This object can be injected into other services.
  921. *
  922. * #### Deprecated Example:
  923. * ```js
  924. * SomeService.$inject = ['$http', '$stateParams'];
  925. * function SomeService($http, $stateParams) {
  926. * return {
  927. * getUser: function() {
  928. * return $http.get('/api/users/' + $stateParams.username);
  929. * }
  930. * }
  931. * };
  932. * angular.service('SomeService', SomeService);
  933. * ```
  934. * @deprecated
  935. */
  936. /**
  937. * # Angular 1 Directives
  938. *
  939. * These are the directives included in UI-Router for Angular 1.
  940. * These directives are used in templates to create viewports and link/navigate to states.
  941. *
  942. * @ng1api
  943. * @preferred
  944. * @module directives
  945. */ /** for typedoc */
  946. /** @hidden */
  947. function parseStateRef(ref) {
  948. var paramsOnly = ref.match(/^\s*({[^}]*})\s*$/), parsed;
  949. if (paramsOnly)
  950. ref = '(' + paramsOnly[1] + ')';
  951. parsed = ref.replace(/\n/g, " ").match(/^\s*([^(]*?)\s*(\((.*)\))?\s*$/);
  952. if (!parsed || parsed.length !== 4)
  953. throw new Error("Invalid state ref '" + ref + "'");
  954. return { state: parsed[1] || null, paramExpr: parsed[3] || null };
  955. }
  956. /** @hidden */
  957. function stateContext(el) {
  958. var $uiView = el.parent().inheritedData('$uiView');
  959. var path = core.parse('$cfg.path')($uiView);
  960. return path ? core.tail(path).state.name : undefined;
  961. }
  962. /** @hidden */
  963. function processedDef($state, $element, def) {
  964. var uiState = def.uiState || $state.current.name;
  965. var uiStateOpts = core.extend(defaultOpts($element, $state), def.uiStateOpts || {});
  966. var href = $state.href(uiState, def.uiStateParams, uiStateOpts);
  967. return { uiState: uiState, uiStateParams: def.uiStateParams, uiStateOpts: uiStateOpts, href: href };
  968. }
  969. /** @hidden */
  970. function getTypeInfo(el) {
  971. // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
  972. var isSvg = Object.prototype.toString.call(el.prop('href')) === '[object SVGAnimatedString]';
  973. var isForm = el[0].nodeName === "FORM";
  974. return {
  975. attr: isForm ? "action" : (isSvg ? 'xlink:href' : 'href'),
  976. isAnchor: el.prop("tagName").toUpperCase() === "A",
  977. clickable: !isForm
  978. };
  979. }
  980. /** @hidden */
  981. function clickHook(el, $state, $timeout, type, getDef) {
  982. return function (e) {
  983. var button = e.which || e.button, target = getDef();
  984. if (!(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || el.attr('target'))) {
  985. // HACK: This is to allow ng-clicks to be processed before the transition is initiated:
  986. var transition = $timeout(function () {
  987. $state.go(target.uiState, target.uiStateParams, target.uiStateOpts);
  988. });
  989. e.preventDefault();
  990. // if the state has no URL, ignore one preventDefault from the <a> directive.
  991. var ignorePreventDefaultCount = type.isAnchor && !target.href ? 1 : 0;
  992. e.preventDefault = function () {
  993. if (ignorePreventDefaultCount-- <= 0)
  994. $timeout.cancel(transition);
  995. };
  996. }
  997. };
  998. }
  999. /** @hidden */
  1000. function defaultOpts(el, $state) {
  1001. return {
  1002. relative: stateContext(el) || $state.$current,
  1003. inherit: true,
  1004. source: "sref"
  1005. };
  1006. }
  1007. /** @hidden */
  1008. function bindEvents(element, scope, hookFn, uiStateOpts) {
  1009. var events;
  1010. if (uiStateOpts) {
  1011. events = uiStateOpts.events;
  1012. }
  1013. if (!core.isArray(events)) {
  1014. events = ['click'];
  1015. }
  1016. var on = element.on ? 'on' : 'bind';
  1017. for (var _i = 0, events_1 = events; _i < events_1.length; _i++) {
  1018. var event_1 = events_1[_i];
  1019. element[on](event_1, hookFn);
  1020. }
  1021. scope.$on('$destroy', function () {
  1022. var off = element.off ? 'off' : 'unbind';
  1023. for (var _i = 0, events_2 = events; _i < events_2.length; _i++) {
  1024. var event_2 = events_2[_i];
  1025. element[off](event_2, hookFn);
  1026. }
  1027. });
  1028. }
  1029. /**
  1030. * `ui-sref`: A directive for linking to a state
  1031. *
  1032. * A directive which links to a state (and optionally, parameters).
  1033. * When clicked, this directive activates the linked state with the supplied parameter values.
  1034. *
  1035. * ### Linked State
  1036. * The attribute value of the `ui-sref` is the name of the state to link to.
  1037. *
  1038. * #### Example:
  1039. * This will activate the `home` state when the link is clicked.
  1040. * ```html
  1041. * <a ui-sref="home">Home</a>
  1042. * ```
  1043. *
  1044. * ### Relative Links
  1045. * You can also use relative state paths within `ui-sref`, just like a relative path passed to `$state.go()` ([[StateService.go]]).
  1046. * You just need to be aware that the path is relative to the state that *created* the link.
  1047. * This allows a state to create a relative `ui-sref` which always targets the same destination.
  1048. *
  1049. * #### Example:
  1050. * Both these links are relative to the parent state, even when a child state is currently active.
  1051. * ```html
  1052. * <a ui-sref=".child1">child 1 state</a>
  1053. * <a ui-sref=".child2">child 2 state</a>
  1054. * ```
  1055. *
  1056. * This link activates the parent state.
  1057. * ```html
  1058. * <a ui-sref="^">Return</a>
  1059. * ```
  1060. *
  1061. * ### hrefs
  1062. * If the linked state has a URL, the directive will automatically generate and
  1063. * update the `href` attribute (using the [[StateService.href]] method).
  1064. *
  1065. * #### Example:
  1066. * Assuming the `users` state has a url of `/users/`
  1067. * ```html
  1068. * <a ui-sref="users" href="/users/">Users</a>
  1069. * ```
  1070. *
  1071. * ### Parameter Values
  1072. * In addition to the state name, a `ui-sref` can include parameter values which are applied when activating the state.
  1073. * Param values can be provided in the `ui-sref` value after the state name, enclosed by parentheses.
  1074. * The content inside the parentheses is an expression, evaluated to the parameter values.
  1075. *
  1076. * #### Example:
  1077. * This example renders a list of links to users.
  1078. * The state's `userId` parameter value comes from each user's `user.id` property.
  1079. * ```html
  1080. * <li ng-repeat="user in users">
  1081. * <a ui-sref="users.detail({ userId: user.id })">{{ user.displayName }}</a>
  1082. * </li>
  1083. * ```
  1084. *
  1085. * Note:
  1086. * The parameter values expression is `$watch`ed for updates.
  1087. *
  1088. * ### Transition Options
  1089. * You can specify [[TransitionOptions]] to pass to [[StateService.go]] by using the `ui-sref-opts` attribute.
  1090. * Options are restricted to `location`, `inherit`, and `reload`.
  1091. *
  1092. * #### Example:
  1093. * ```html
  1094. * <a ui-sref="home" ui-sref-opts="{ reload: true }">Home</a>
  1095. * ```
  1096. *
  1097. * ### Other DOM Events
  1098. *
  1099. * You can also customize which DOM events to respond to (instead of `click`) by
  1100. * providing an `events` array in the `ui-sref-opts` attribute.
  1101. *
  1102. * #### Example:
  1103. * ```html
  1104. * <input type="text" ui-sref="contacts" ui-sref-opts="{ events: ['change', 'blur'] }">
  1105. * ```
  1106. *
  1107. * ### Highlighting the active link
  1108. * This directive can be used in conjunction with [[uiSrefActive]] to highlight the active link.
  1109. *
  1110. * ### Examples
  1111. * If you have the following template:
  1112. *
  1113. * ```html
  1114. * <a ui-sref="home">Home</a>
  1115. * <a ui-sref="about">About</a>
  1116. * <a ui-sref="{page: 2}">Next page</a>
  1117. *
  1118. * <ul>
  1119. * <li ng-repeat="contact in contacts">
  1120. * <a ui-sref="contacts.detail({ id: contact.id })">{{ contact.name }}</a>
  1121. * </li>
  1122. * </ul>
  1123. * ```
  1124. *
  1125. * Then (assuming the current state is `contacts`) the rendered html including hrefs would be:
  1126. *
  1127. * ```html
  1128. * <a href="#/home" ui-sref="home">Home</a>
  1129. * <a href="#/about" ui-sref="about">About</a>
  1130. * <a href="#/contacts?page=2" ui-sref="{page: 2}">Next page</a>
  1131. *
  1132. * <ul>
  1133. * <li ng-repeat="contact in contacts">
  1134. * <a href="#/contacts/1" ui-sref="contacts.detail({ id: contact.id })">Joe</a>
  1135. * </li>
  1136. * <li ng-repeat="contact in contacts">
  1137. * <a href="#/contacts/2" ui-sref="contacts.detail({ id: contact.id })">Alice</a>
  1138. * </li>
  1139. * <li ng-repeat="contact in contacts">
  1140. * <a href="#/contacts/3" ui-sref="contacts.detail({ id: contact.id })">Bob</a>
  1141. * </li>
  1142. * </ul>
  1143. *
  1144. * <a href="#/home" ui-sref="home" ui-sref-opts="{reload: true}">Home</a>
  1145. * ```
  1146. *
  1147. * ### Notes
  1148. *
  1149. * - You can use `ui-sref` to change **only the parameter values** by omitting the state name and parentheses.
  1150. * #### Example:
  1151. * Sets the `lang` parameter to `en` and remains on the same state.
  1152. *
  1153. * ```html
  1154. * <a ui-sref="{ lang: 'en' }">English</a>
  1155. * ```
  1156. *
  1157. * - A middle-click, right-click, or ctrl-click is handled (natively) by the browser to open the href in a new window, for example.
  1158. *
  1159. * - Unlike the parameter values expression, the state name is not `$watch`ed (for performance reasons).
  1160. * If you need to dynamically update the state being linked to, use the fully dynamic [[uiState]] directive.
  1161. */
  1162. var uiSref;
  1163. uiSref = ['$uiRouter', '$timeout',
  1164. function $StateRefDirective($uiRouter, $timeout) {
  1165. var $state = $uiRouter.stateService;
  1166. return {
  1167. restrict: 'A',
  1168. require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
  1169. link: function (scope, element, attrs, uiSrefActive) {
  1170. var type = getTypeInfo(element);
  1171. var active = uiSrefActive[1] || uiSrefActive[0];
  1172. var unlinkInfoFn = null;
  1173. var hookFn;
  1174. var rawDef = {};
  1175. var getDef = function () { return processedDef($state, element, rawDef); };
  1176. var ref = parseStateRef(attrs.uiSref);
  1177. rawDef.uiState = ref.state;
  1178. rawDef.uiStateOpts = attrs.uiSrefOpts ? scope.$eval(attrs.uiSrefOpts) : {};
  1179. function update() {
  1180. var def = getDef();
  1181. if (unlinkInfoFn)
  1182. unlinkInfoFn();
  1183. if (active)
  1184. unlinkInfoFn = active.$$addStateInfo(def.uiState, def.uiStateParams);
  1185. if (def.href != null)
  1186. attrs.$set(type.attr, def.href);
  1187. }
  1188. if (ref.paramExpr) {
  1189. scope.$watch(ref.paramExpr, function (val$$1) {
  1190. rawDef.uiStateParams = core.extend({}, val$$1);
  1191. update();
  1192. }, true);
  1193. rawDef.uiStateParams = core.extend({}, scope.$eval(ref.paramExpr));
  1194. }
  1195. update();
  1196. scope.$on('$destroy', $uiRouter.stateRegistry.onStatesChanged(update));
  1197. scope.$on('$destroy', $uiRouter.transitionService.onSuccess({}, update));
  1198. if (!type.clickable)
  1199. return;
  1200. hookFn = clickHook(element, $state, $timeout, type, getDef);
  1201. bindEvents(element, scope, hookFn, rawDef.uiStateOpts);
  1202. }
  1203. };
  1204. }];
  1205. /**
  1206. * `ui-state`: A fully dynamic directive for linking to a state
  1207. *
  1208. * A directive which links to a state (and optionally, parameters).
  1209. * When clicked, this directive activates the linked state with the supplied parameter values.
  1210. *
  1211. * **This directive is very similar to [[uiSref]], but it `$observe`s and `$watch`es/evaluates all its inputs.**
  1212. *
  1213. * A directive which links to a state (and optionally, parameters).
  1214. * When clicked, this directive activates the linked state with the supplied parameter values.
  1215. *
  1216. * ### Linked State
  1217. * The attribute value of `ui-state` is an expression which is `$watch`ed and evaluated as the state to link to.
  1218. * **This is in contrast with `ui-sref`, which takes a state name as a string literal.**
  1219. *
  1220. * #### Example:
  1221. * Create a list of links.
  1222. * ```html
  1223. * <li ng-repeat="link in navlinks">
  1224. * <a ui-state="link.state">{{ link.displayName }}</a>
  1225. * </li>
  1226. * ```
  1227. *
  1228. * ### Relative Links
  1229. * If the expression evaluates to a relative path, it is processed like [[uiSref]].
  1230. * You just need to be aware that the path is relative to the state that *created* the link.
  1231. * This allows a state to create relative `ui-state` which always targets the same destination.
  1232. *
  1233. * ### hrefs
  1234. * If the linked state has a URL, the directive will automatically generate and
  1235. * update the `href` attribute (using the [[StateService.href]] method).
  1236. *
  1237. * ### Parameter Values
  1238. * In addition to the state name expression, a `ui-state` can include parameter values which are applied when activating the state.
  1239. * Param values should be provided using the `ui-state-params` attribute.
  1240. * The `ui-state-params` attribute value is `$watch`ed and evaluated as an expression.
  1241. *
  1242. * #### Example:
  1243. * This example renders a list of links with param values.
  1244. * The state's `userId` parameter value comes from each user's `user.id` property.
  1245. * ```html
  1246. * <li ng-repeat="link in navlinks">
  1247. * <a ui-state="link.state" ui-state-params="link.params">{{ link.displayName }}</a>
  1248. * </li>
  1249. * ```
  1250. *
  1251. * ### Transition Options
  1252. * You can specify [[TransitionOptions]] to pass to [[StateService.go]] by using the `ui-state-opts` attribute.
  1253. * Options are restricted to `location`, `inherit`, and `reload`.
  1254. * The value of the `ui-state-opts` is `$watch`ed and evaluated as an expression.
  1255. *
  1256. * #### Example:
  1257. * ```html
  1258. * <a ui-state="returnto.state" ui-state-opts="{ reload: true }">Home</a>
  1259. * ```
  1260. *
  1261. * ### Other DOM Events
  1262. *
  1263. * You can also customize which DOM events to respond to (instead of `click`) by
  1264. * providing an `events` array in the `ui-state-opts` attribute.
  1265. *
  1266. * #### Example:
  1267. * ```html
  1268. * <input type="text" ui-state="contacts" ui-state-opts="{ events: ['change', 'blur'] }">
  1269. * ```
  1270. *
  1271. * ### Highlighting the active link
  1272. * This directive can be used in conjunction with [[uiSrefActive]] to highlight the active link.
  1273. *
  1274. * ### Notes
  1275. *
  1276. * - You can use `ui-params` to change **only the parameter values** by omitting the state name and supplying only `ui-state-params`.
  1277. * However, it might be simpler to use [[uiSref]] parameter-only links.
  1278. *
  1279. * #### Example:
  1280. * Sets the `lang` parameter to `en` and remains on the same state.
  1281. *
  1282. * ```html
  1283. * <a ui-state="" ui-state-params="{ lang: 'en' }">English</a>
  1284. * ```
  1285. *
  1286. * - A middle-click, right-click, or ctrl-click is handled (natively) by the browser to open the href in a new window, for example.
  1287. * ```
  1288. */
  1289. var uiState;
  1290. uiState = ['$uiRouter', '$timeout',
  1291. function $StateRefDynamicDirective($uiRouter, $timeout) {
  1292. var $state = $uiRouter.stateService;
  1293. return {
  1294. restrict: 'A',
  1295. require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
  1296. link: function (scope, element, attrs, uiSrefActive) {
  1297. var type = getTypeInfo(element);
  1298. var active = uiSrefActive[1] || uiSrefActive[0];
  1299. var unlinkInfoFn = null;
  1300. var hookFn;
  1301. var rawDef = {};
  1302. var getDef = function () { return processedDef($state, element, rawDef); };
  1303. var inputAttrs = ['uiState', 'uiStateParams', 'uiStateOpts'];
  1304. var watchDeregFns = inputAttrs.reduce(function (acc, attr) { return (acc[attr] = core.noop, acc); }, {});
  1305. function update() {
  1306. var def = getDef();
  1307. if (unlinkInfoFn)
  1308. unlinkInfoFn();
  1309. if (active)
  1310. unlinkInfoFn = active.$$addStateInfo(def.uiState, def.uiStateParams);
  1311. if (def.href != null)
  1312. attrs.$set(type.attr, def.href);
  1313. }
  1314. inputAttrs.forEach(function (field) {
  1315. rawDef[field] = attrs[field] ? scope.$eval(attrs[field]) : null;
  1316. attrs.$observe(field, function (expr) {
  1317. watchDeregFns[field]();
  1318. watchDeregFns[field] = scope.$watch(expr, function (newval) {
  1319. rawDef[field] = newval;
  1320. update();
  1321. }, true);
  1322. });
  1323. });
  1324. update();
  1325. scope.$on('$destroy', $uiRouter.stateRegistry.onStatesChanged(update));
  1326. scope.$on('$destroy', $uiRouter.transitionService.onSuccess({}, update));
  1327. if (!type.clickable)
  1328. return;
  1329. hookFn = clickHook(element, $state, $timeout, type, getDef);
  1330. bindEvents(element, scope, hookFn, rawDef.uiStateOpts);
  1331. }
  1332. };
  1333. }];
  1334. /**
  1335. * `ui-sref-active` and `ui-sref-active-eq`: A directive that adds a CSS class when a `ui-sref` is active
  1336. *
  1337. * A directive working alongside [[uiSref]] and [[uiState]] to add classes to an element when the
  1338. * related directive's state is active (and remove them when it is inactive).
  1339. *
  1340. * The primary use-case is to highlight the active link in navigation menus,
  1341. * distinguishing it from the inactive menu items.
  1342. *
  1343. * ### Linking to a `ui-sref` or `ui-state`
  1344. * `ui-sref-active` can live on the same element as `ui-sref`/`ui-state`, or it can be on a parent element.
  1345. * 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**.
  1346. *
  1347. * ### Matching
  1348. *
  1349. * The `ui-sref-active` directive applies the CSS class when the `ui-sref`/`ui-state`'s target state **or any child state is active**.
  1350. * This is a "fuzzy match" which uses [[StateService.includes]].
  1351. *
  1352. * 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).
  1353. * This is an "exact match" which uses [[StateService.is]].
  1354. *
  1355. * ### Parameter values
  1356. * 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.
  1357. * This allows a list of links to the same state with different parameters to be rendered, and the correct one highlighted.
  1358. *
  1359. * #### Example:
  1360. * ```html
  1361. * <li ng-repeat="user in users" ui-sref-active="active">
  1362. * <a ui-sref="user.details({ userId: user.id })">{{ user.lastName }}</a>
  1363. * </li>
  1364. * ```
  1365. *
  1366. * ### Examples
  1367. *
  1368. * Given the following template:
  1369. * #### Example:
  1370. * ```html
  1371. * <ul>
  1372. * <li ui-sref-active="active" class="item">
  1373. * <a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a>
  1374. * </li>
  1375. * </ul>
  1376. * ```
  1377. *
  1378. * When the app state is `app.user` (or any child state),
  1379. * and contains the state parameter "user" with value "bilbobaggins",
  1380. * the resulting HTML will appear as (note the 'active' class):
  1381. *
  1382. * ```html
  1383. * <ul>
  1384. * <li ui-sref-active="active" class="item active">
  1385. * <a ui-sref="app.user({user: 'bilbobaggins'})" href="/users/bilbobaggins">@bilbobaggins</a>
  1386. * </li>
  1387. * </ul>
  1388. * ```
  1389. *
  1390. * ### Glob mode
  1391. *
  1392. * It is possible to pass `ui-sref-active` an expression that evaluates to an object.
  1393. * The objects keys represent active class names and values represent the respective state names/globs.
  1394. * `ui-sref-active` will match if the current active state **includes** any of
  1395. * the specified state names/globs, even the abstract ones.
  1396. *
  1397. * #### Example:
  1398. * Given the following template, with "admin" being an abstract state:
  1399. * ```html
  1400. * <div ui-sref-active="{'active': 'admin.**'}">
  1401. * <a ui-sref-active="active" ui-sref="admin.roles">Roles</a>
  1402. * </div>
  1403. * ```
  1404. *
  1405. * When the current state is "admin.roles" the "active" class will be applied to both the <div> and <a> elements.
  1406. * It is important to note that the state names/globs passed to `ui-sref-active` override any state provided by a linked `ui-sref`.
  1407. *
  1408. * ### Notes:
  1409. *
  1410. * - The class name is interpolated **once** during the directives link time (any further changes to the
  1411. * interpolated value are ignored).
  1412. *
  1413. * - Multiple classes may be specified in a space-separated format: `ui-sref-active='class1 class2 class3'`
  1414. */
  1415. var uiSrefActive;
  1416. uiSrefActive = ['$state', '$stateParams', '$interpolate', '$uiRouter',
  1417. function $StateRefActiveDirective($state, $stateParams, $interpolate, $uiRouter) {
  1418. return {
  1419. restrict: "A",
  1420. controller: ['$scope', '$element', '$attrs',
  1421. function ($scope, $element, $attrs) {
  1422. var states = [], activeEqClass, uiSrefActive;
  1423. // There probably isn't much point in $observing this
  1424. // uiSrefActive and uiSrefActiveEq share the same directive object with some
  1425. // slight difference in logic routing
  1426. activeEqClass = $interpolate($attrs.uiSrefActiveEq || '', false)($scope);
  1427. try {
  1428. uiSrefActive = $scope.$eval($attrs.uiSrefActive);
  1429. }
  1430. catch (e) {
  1431. // Do nothing. uiSrefActive is not a valid expression.
  1432. // Fall back to using $interpolate below
  1433. }
  1434. uiSrefActive = uiSrefActive || $interpolate($attrs.uiSrefActive || '', false)($scope);
  1435. if (core.isObject(uiSrefActive)) {
  1436. core.forEach(uiSrefActive, function (stateOrName, activeClass) {
  1437. if (core.isString(stateOrName)) {
  1438. var ref = parseStateRef(stateOrName);
  1439. addState(ref.state, $scope.$eval(ref.paramExpr), activeClass);
  1440. }
  1441. });
  1442. }
  1443. // Allow uiSref to communicate with uiSrefActive[Equals]
  1444. this.$$addStateInfo = function (newState, newParams) {
  1445. // we already got an explicit state provided by ui-sref-active, so we
  1446. // shadow the one that comes from ui-sref
  1447. if (core.isObject(uiSrefActive) && states.length > 0) {
  1448. return;
  1449. }
  1450. var deregister = addState(newState, newParams, uiSrefActive);
  1451. update();
  1452. return deregister;
  1453. };
  1454. function updateAfterTransition(trans) {
  1455. trans.promise.then(update, core.noop);
  1456. }
  1457. $scope.$on('$stateChangeSuccess', update);
  1458. $scope.$on('$destroy', $uiRouter.transitionService.onStart({}, updateAfterTransition));
  1459. if ($uiRouter.globals.transition) {
  1460. updateAfterTransition($uiRouter.globals.transition);
  1461. }
  1462. function addState(stateName, stateParams, activeClass) {
  1463. var state = $state.get(stateName, stateContext($element));
  1464. var stateInfo = {
  1465. state: state || { name: stateName },
  1466. params: stateParams,
  1467. activeClass: activeClass
  1468. };
  1469. states.push(stateInfo);
  1470. return function removeState() {
  1471. core.removeFrom(states)(stateInfo);
  1472. };
  1473. }
  1474. // Update route state
  1475. function update() {
  1476. var splitClasses = function (str) {
  1477. return str.split(/\s/).filter(core.identity);
  1478. };
  1479. var getClasses = function (stateList) {
  1480. return stateList.map(function (x) { return x.activeClass; }).map(splitClasses).reduce(core.unnestR, []);
  1481. };
  1482. var allClasses = getClasses(states).concat(splitClasses(activeEqClass)).reduce(core.uniqR, []);
  1483. var fuzzyClasses = getClasses(states.filter(function (x) { return $state.includes(x.state.name, x.params); }));
  1484. var exactlyMatchesAny = !!states.filter(function (x) { return $state.is(x.state.name, x.params); }).length;
  1485. var exactClasses = exactlyMatchesAny ? splitClasses(activeEqClass) : [];
  1486. var addClasses = fuzzyClasses.concat(exactClasses).reduce(core.uniqR, []);
  1487. var removeClasses = allClasses.filter(function (cls) { return !core.inArray(addClasses, cls); });
  1488. $scope.$evalAsync(function () {
  1489. addClasses.forEach(function (className) { return $element.addClass(className); });
  1490. removeClasses.forEach(function (className) { return $element.removeClass(className); });
  1491. });
  1492. }
  1493. update();
  1494. }]
  1495. };
  1496. }];
  1497. ng.module('ui.router.state')
  1498. .directive('uiSref', uiSref)
  1499. .directive('uiSrefActive', uiSrefActive)
  1500. .directive('uiSrefActiveEq', uiSrefActive)
  1501. .directive('uiState', uiState);
  1502. /** @module ng1 */ /** for typedoc */
  1503. /**
  1504. * `isState` Filter: truthy if the current state is the parameter
  1505. *
  1506. * Translates to [[StateService.is]] `$state.is("stateName")`.
  1507. *
  1508. * #### Example:
  1509. * ```html
  1510. * <div ng-if="'stateName' | isState">show if state is 'stateName'</div>
  1511. * ```
  1512. */
  1513. $IsStateFilter.$inject = ['$state'];
  1514. function $IsStateFilter($state) {
  1515. var isFilter = function (state, params, options) {
  1516. return $state.is(state, params, options);
  1517. };
  1518. isFilter.$stateful = true;
  1519. return isFilter;
  1520. }
  1521. /**
  1522. * `includedByState` Filter: truthy if the current state includes the parameter
  1523. *
  1524. * Translates to [[StateService.includes]]` $state.is("fullOrPartialStateName")`.
  1525. *
  1526. * #### Example:
  1527. * ```html
  1528. * <div ng-if="'fullOrPartialStateName' | includedByState">show if state includes 'fullOrPartialStateName'</div>
  1529. * ```
  1530. */
  1531. $IncludedByStateFilter.$inject = ['$state'];
  1532. function $IncludedByStateFilter($state) {
  1533. var includesFilter = function (state, params, options) {
  1534. return $state.includes(state, params, options);
  1535. };
  1536. includesFilter.$stateful = true;
  1537. return includesFilter;
  1538. }
  1539. ng.module('ui.router.state')
  1540. .filter('isState', $IsStateFilter)
  1541. .filter('includedByState', $IncludedByStateFilter);
  1542. /**
  1543. * @ng1api
  1544. * @module directives
  1545. */ /** for typedoc */
  1546. /**
  1547. * `ui-view`: A viewport directive which is filled in by a view from the active state.
  1548. *
  1549. * ### Attributes
  1550. *
  1551. * - `name`: (Optional) A view name.
  1552. * The name should be unique amongst the other views in the same state.
  1553. * You can have views of the same name that live in different states.
  1554. * The ui-view can be targeted in a View using the name ([[Ng1StateDeclaration.views]]).
  1555. *
  1556. * - `autoscroll`: an expression. When it evaluates to true, the `ui-view` will be scrolled into view when it is activated.
  1557. * Uses [[$uiViewScroll]] to do the scrolling.
  1558. *
  1559. * - `onload`: Expression to evaluate whenever the view updates.
  1560. *
  1561. * #### Example:
  1562. * A view can be unnamed or named.
  1563. * ```html
  1564. * <!-- Unnamed -->
  1565. * <div ui-view></div>
  1566. *
  1567. * <!-- Named -->
  1568. * <div ui-view="viewName"></div>
  1569. *
  1570. * <!-- Named (different style) -->
  1571. * <ui-view name="viewName"></ui-view>
  1572. * ```
  1573. *
  1574. * You can only have one unnamed view within any template (or root html). If you are only using a
  1575. * single view and it is unnamed then you can populate it like so:
  1576. *
  1577. * ```html
  1578. * <div ui-view></div>
  1579. * $stateProvider.state("home", {
  1580. * template: "<h1>HELLO!</h1>"
  1581. * })
  1582. * ```
  1583. *
  1584. * The above is a convenient shortcut equivalent to specifying your view explicitly with the
  1585. * [[Ng1StateDeclaration.views]] config property, by name, in this case an empty name:
  1586. *
  1587. * ```js
  1588. * $stateProvider.state("home", {
  1589. * views: {
  1590. * "": {
  1591. * template: "<h1>HELLO!</h1>"
  1592. * }
  1593. * }
  1594. * })
  1595. * ```
  1596. *
  1597. * But typically you'll only use the views property if you name your view or have more than one view
  1598. * in the same template. There's not really a compelling reason to name a view if its the only one,
  1599. * but you could if you wanted, like so:
  1600. *
  1601. * ```html
  1602. * <div ui-view="main"></div>
  1603. * ```
  1604. *
  1605. * ```js
  1606. * $stateProvider.state("home", {
  1607. * views: {
  1608. * "main": {
  1609. * template: "<h1>HELLO!</h1>"
  1610. * }
  1611. * }
  1612. * })
  1613. * ```
  1614. *
  1615. * Really though, you'll use views to set up multiple views:
  1616. *
  1617. * ```html
  1618. * <div ui-view></div>
  1619. * <div ui-view="chart"></div>
  1620. * <div ui-view="data"></div>
  1621. * ```
  1622. *
  1623. * ```js
  1624. * $stateProvider.state("home", {
  1625. * views: {
  1626. * "": {
  1627. * template: "<h1>HELLO!</h1>"
  1628. * },
  1629. * "chart": {
  1630. * template: "<chart_thing/>"
  1631. * },
  1632. * "data": {
  1633. * template: "<data_thing/>"
  1634. * }
  1635. * }
  1636. * })
  1637. * ```
  1638. *
  1639. * #### Examples for `autoscroll`:
  1640. * ```html
  1641. * <!-- If autoscroll present with no expression,
  1642. * then scroll ui-view into view -->
  1643. * <ui-view autoscroll/>
  1644. *
  1645. * <!-- If autoscroll present with valid expression,
  1646. * then scroll ui-view into view if expression evaluates to true -->
  1647. * <ui-view autoscroll='true'/>
  1648. * <ui-view autoscroll='false'/>
  1649. * <ui-view autoscroll='scopeVariable'/>
  1650. * ```
  1651. *
  1652. * Resolve data:
  1653. *
  1654. * The resolved data from the state's `resolve` block is placed on the scope as `$resolve` (this
  1655. * can be customized using [[Ng1ViewDeclaration.resolveAs]]). This can be then accessed from the template.
  1656. *
  1657. * Note that when `controllerAs` is being used, `$resolve` is set on the controller instance *after* the
  1658. * controller is instantiated. The `$onInit()` hook can be used to perform initialization code which
  1659. * depends on `$resolve` data.
  1660. *
  1661. * #### Example:
  1662. * ```js
  1663. * $stateProvider.state('home', {
  1664. * template: '<my-component user="$resolve.user"></my-component>',
  1665. * resolve: {
  1666. * user: function(UserService) { return UserService.fetchUser(); }
  1667. * }
  1668. * });
  1669. * ```
  1670. */
  1671. var uiView;
  1672. uiView = ['$view', '$animate', '$uiViewScroll', '$interpolate', '$q',
  1673. function $ViewDirective($view, $animate, $uiViewScroll, $interpolate, $q) {
  1674. function getRenderer(attrs, scope) {
  1675. return {
  1676. enter: function (element, target, cb) {
  1677. if (ng.version.minor > 2) {
  1678. $animate.enter(element, null, target).then(cb);
  1679. }
  1680. else {
  1681. $animate.enter(element, null, target, cb);
  1682. }
  1683. },
  1684. leave: function (element, cb) {
  1685. if (ng.version.minor > 2) {
  1686. $animate.leave(element).then(cb);
  1687. }
  1688. else {
  1689. $animate.leave(element, cb);
  1690. }
  1691. }
  1692. };
  1693. }
  1694. function configsEqual(config1, config2) {
  1695. return config1 === config2;
  1696. }
  1697. var rootData = {
  1698. $cfg: { viewDecl: { $context: $view._pluginapi._rootViewContext() } },
  1699. $uiView: {}
  1700. };
  1701. var directive = {
  1702. count: 0,
  1703. restrict: 'ECA',
  1704. terminal: true,
  1705. priority: 400,
  1706. transclude: 'element',
  1707. compile: function (tElement, tAttrs, $transclude) {
  1708. return function (scope, $element, attrs) {
  1709. var previousEl, currentEl, currentScope, unregister, onloadExp = attrs['onload'] || '', autoScrollExp = attrs['autoscroll'], renderer = getRenderer(attrs, scope), viewConfig = undefined, inherited = $element.inheritedData('$uiView') || rootData, name = $interpolate(attrs['uiView'] || attrs['name'] || '')(scope) || '$default';
  1710. var activeUIView = {
  1711. $type: 'ng1',
  1712. id: directive.count++,
  1713. name: name,
  1714. fqn: inherited.$uiView.fqn ? inherited.$uiView.fqn + "." + name : name,
  1715. config: null,
  1716. configUpdated: configUpdatedCallback,
  1717. get creationContext() {
  1718. var fromParentTagConfig = core.parse('$cfg.viewDecl.$context')(inherited);
  1719. // Allow <ui-view name="foo"><ui-view name="bar"></ui-view></ui-view>
  1720. // See https://github.com/angular-ui/ui-router/issues/3355
  1721. var fromParentTag = core.parse('$uiView.creationContext')(inherited);
  1722. return fromParentTagConfig || fromParentTag;
  1723. }
  1724. };
  1725. core.trace.traceUIViewEvent("Linking", activeUIView);
  1726. function configUpdatedCallback(config) {
  1727. if (config && !(config instanceof Ng1ViewConfig))
  1728. return;
  1729. if (configsEqual(viewConfig, config))
  1730. return;
  1731. core.trace.traceUIViewConfigUpdated(activeUIView, config && config.viewDecl && config.viewDecl.$context);
  1732. viewConfig = config;
  1733. updateView(config);
  1734. }
  1735. $element.data('$uiView', { $uiView: activeUIView });
  1736. updateView();
  1737. unregister = $view.registerUIView(activeUIView);
  1738. scope.$on("$destroy", function () {
  1739. core.trace.traceUIViewEvent("Destroying/Unregistering", activeUIView);
  1740. unregister();
  1741. });
  1742. function cleanupLastView() {
  1743. if (previousEl) {
  1744. core.trace.traceUIViewEvent("Removing (previous) el", previousEl.data('$uiView'));
  1745. previousEl.remove();
  1746. previousEl = null;
  1747. }
  1748. if (currentScope) {
  1749. core.trace.traceUIViewEvent("Destroying scope", activeUIView);
  1750. currentScope.$destroy();
  1751. currentScope = null;
  1752. }
  1753. if (currentEl) {
  1754. var _viewData_1 = currentEl.data('$uiViewAnim');
  1755. core.trace.traceUIViewEvent("Animate out", _viewData_1);
  1756. renderer.leave(currentEl, function () {
  1757. _viewData_1.$$animLeave.resolve();
  1758. previousEl = null;
  1759. });
  1760. previousEl = currentEl;
  1761. currentEl = null;
  1762. }
  1763. }
  1764. function updateView(config) {
  1765. var newScope = scope.$new();
  1766. var animEnter = $q.defer(), animLeave = $q.defer();
  1767. var $uiViewData = {
  1768. $cfg: config,
  1769. $uiView: activeUIView,
  1770. };
  1771. var $uiViewAnim = {
  1772. $animEnter: animEnter.promise,
  1773. $animLeave: animLeave.promise,
  1774. $$animLeave: animLeave
  1775. };
  1776. /**
  1777. * @ngdoc event
  1778. * @name ui.router.state.directive:ui-view#$viewContentLoading
  1779. * @eventOf ui.router.state.directive:ui-view
  1780. * @eventType emits on ui-view directive scope
  1781. * @description
  1782. *
  1783. * Fired once the view **begins loading**, *before* the DOM is rendered.
  1784. *
  1785. * @param {Object} event Event object.
  1786. * @param {string} viewName Name of the view.
  1787. */
  1788. newScope.$emit('$viewContentLoading', name);
  1789. var cloned = $transclude(newScope, function (clone) {
  1790. clone.data('$uiViewAnim', $uiViewAnim);
  1791. clone.data('$uiView', $uiViewData);
  1792. renderer.enter(clone, $element, function onUIViewEnter() {
  1793. animEnter.resolve();
  1794. if (currentScope)
  1795. currentScope.$emit('$viewContentAnimationEnded');
  1796. if (core.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) {
  1797. $uiViewScroll(clone);
  1798. }
  1799. });
  1800. cleanupLastView();
  1801. });
  1802. currentEl = cloned;
  1803. currentScope = newScope;
  1804. /**
  1805. * @ngdoc event
  1806. * @name ui.router.state.directive:ui-view#$viewContentLoaded
  1807. * @eventOf ui.router.state.directive:ui-view
  1808. * @eventType emits on ui-view directive scope
  1809. * @description *
  1810. * Fired once the view is **loaded**, *after* the DOM is rendered.
  1811. *
  1812. * @param {Object} event Event object.
  1813. */
  1814. currentScope.$emit('$viewContentLoaded', config || viewConfig);
  1815. currentScope.$eval(onloadExp);
  1816. }
  1817. };
  1818. }
  1819. };
  1820. return directive;
  1821. }];
  1822. $ViewDirectiveFill.$inject = ['$compile', '$controller', '$transitions', '$view', '$q', '$timeout'];
  1823. /** @hidden */
  1824. function $ViewDirectiveFill($compile, $controller, $transitions, $view, $q, $timeout) {
  1825. var getControllerAs = core.parse('viewDecl.controllerAs');
  1826. var getResolveAs = core.parse('viewDecl.resolveAs');
  1827. return {
  1828. restrict: 'ECA',
  1829. priority: -400,
  1830. compile: function (tElement) {
  1831. var initial = tElement.html();
  1832. tElement.empty();
  1833. return function (scope, $element) {
  1834. var data = $element.data('$uiView');
  1835. if (!data) {
  1836. $element.html(initial);
  1837. $compile($element.contents())(scope);
  1838. return;
  1839. }
  1840. var cfg = data.$cfg || { viewDecl: {}, getTemplate: ng_from_import.noop };
  1841. var resolveCtx = cfg.path && new core.ResolveContext(cfg.path);
  1842. $element.html(cfg.getTemplate($element, resolveCtx) || initial);
  1843. core.trace.traceUIViewFill(data.$uiView, $element.html());
  1844. var link = $compile($element.contents());
  1845. var controller = cfg.controller;
  1846. var controllerAs = getControllerAs(cfg);
  1847. var resolveAs = getResolveAs(cfg);
  1848. var locals = resolveCtx && getLocals(resolveCtx);
  1849. scope[resolveAs] = locals;
  1850. if (controller) {
  1851. var controllerInstance = $controller(controller, core.extend({}, locals, { $scope: scope, $element: $element }));
  1852. if (controllerAs) {
  1853. scope[controllerAs] = controllerInstance;
  1854. scope[controllerAs][resolveAs] = locals;
  1855. }
  1856. // TODO: Use $view service as a central point for registering component-level hooks
  1857. // Then, when a component is created, tell the $view service, so it can invoke hooks
  1858. // $view.componentLoaded(controllerInstance, { $scope: scope, $element: $element });
  1859. // scope.$on('$destroy', () => $view.componentUnloaded(controllerInstance, { $scope: scope, $element: $element }));
  1860. $element.data('$ngControllerController', controllerInstance);
  1861. $element.children().data('$ngControllerController', controllerInstance);
  1862. registerControllerCallbacks($q, $transitions, controllerInstance, scope, cfg);
  1863. }
  1864. // Wait for the component to appear in the DOM
  1865. if (core.isString(cfg.viewDecl.component)) {
  1866. var cmp_1 = cfg.viewDecl.component;
  1867. var kebobName = core.kebobString(cmp_1);
  1868. var tagRegexp_1 = new RegExp("^(x-|data-)?" + kebobName + "$", "i");
  1869. var getComponentController = function () {
  1870. var directiveEl = [].slice.call($element[0].children)
  1871. .filter(function (el) { return el && el.tagName && tagRegexp_1.exec(el.tagName); });
  1872. return directiveEl && ng.element(directiveEl).data("$" + cmp_1 + "Controller");
  1873. };
  1874. var deregisterWatch_1 = scope.$watch(getComponentController, function (ctrlInstance) {
  1875. if (!ctrlInstance)
  1876. return;
  1877. registerControllerCallbacks($q, $transitions, ctrlInstance, scope, cfg);
  1878. deregisterWatch_1();
  1879. });
  1880. }
  1881. link(scope);
  1882. };
  1883. }
  1884. };
  1885. }
  1886. /** @hidden */
  1887. var hasComponentImpl = typeof ng.module('ui.router')['component'] === 'function';
  1888. /** @hidden incrementing id */
  1889. var _uiCanExitId = 0;
  1890. /** @hidden TODO: move these callbacks to $view and/or `/hooks/components.ts` or something */
  1891. function registerControllerCallbacks($q, $transitions, controllerInstance, $scope, cfg) {
  1892. // Call $onInit() ASAP
  1893. if (core.isFunction(controllerInstance.$onInit) && !(cfg.viewDecl.component && hasComponentImpl)) {
  1894. controllerInstance.$onInit();
  1895. }
  1896. var viewState = core.tail(cfg.path).state.self;
  1897. var hookOptions = { bind: controllerInstance };
  1898. // Add component-level hook for onParamsChange
  1899. if (core.isFunction(controllerInstance.uiOnParamsChanged)) {
  1900. var resolveContext = new core.ResolveContext(cfg.path);
  1901. var viewCreationTrans_1 = resolveContext.getResolvable('$transition$').data;
  1902. // Fire callback on any successful transition
  1903. var paramsUpdated = function ($transition$) {
  1904. // Exit early if the $transition$ is the same as the view was created within.
  1905. // Exit early if the $transition$ will exit the state the view is for.
  1906. if ($transition$ === viewCreationTrans_1 || $transition$.exiting().indexOf(viewState) !== -1)
  1907. return;
  1908. var toParams = $transition$.params("to");
  1909. var fromParams = $transition$.params("from");
  1910. var toSchema = $transition$.treeChanges().to.map(function (node) { return node.paramSchema; }).reduce(core.unnestR, []);
  1911. var fromSchema = $transition$.treeChanges().from.map(function (node) { return node.paramSchema; }).reduce(core.unnestR, []);
  1912. // Find the to params that have different values than the from params
  1913. var changedToParams = toSchema.filter(function (param) {
  1914. var idx = fromSchema.indexOf(param);
  1915. return idx === -1 || !fromSchema[idx].type.equals(toParams[param.id], fromParams[param.id]);
  1916. });
  1917. // Only trigger callback if a to param has changed or is new
  1918. if (changedToParams.length) {
  1919. var changedKeys_1 = changedToParams.map(function (x) { return x.id; });
  1920. // Filter the params to only changed/new to params. `$transition$.params()` may be used to get all params.
  1921. var newValues = core.filter(toParams, function (val$$1, key) { return changedKeys_1.indexOf(key) !== -1; });
  1922. controllerInstance.uiOnParamsChanged(newValues, $transition$);
  1923. }
  1924. };
  1925. $scope.$on('$destroy', $transitions.onSuccess({}, paramsUpdated, hookOptions));
  1926. }
  1927. // Add component-level hook for uiCanExit
  1928. if (core.isFunction(controllerInstance.uiCanExit)) {
  1929. var id_1 = _uiCanExitId++;
  1930. var cacheProp_1 = '_uiCanExitIds';
  1931. // Returns true if a redirect transition already answered truthy
  1932. var prevTruthyAnswer_1 = function (trans) {
  1933. return !!trans && (trans[cacheProp_1] && trans[cacheProp_1][id_1] === true || prevTruthyAnswer_1(trans.redirectedFrom()));
  1934. };
  1935. // If a user answered yes, but the transition was later redirected, don't also ask for the new redirect transition
  1936. var wrappedHook = function (trans) {
  1937. var promise, ids = trans[cacheProp_1] = trans[cacheProp_1] || {};
  1938. if (!prevTruthyAnswer_1(trans)) {
  1939. promise = $q.when(controllerInstance.uiCanExit(trans));
  1940. promise.then(function (val$$1) { return ids[id_1] = (val$$1 !== false); });
  1941. }
  1942. return promise;
  1943. };
  1944. var criteria = { exiting: viewState.name };
  1945. $scope.$on('$destroy', $transitions.onBefore(criteria, wrappedHook, hookOptions));
  1946. }
  1947. }
  1948. ng.module('ui.router.state').directive('uiView', uiView);
  1949. ng.module('ui.router.state').directive('uiView', $ViewDirectiveFill);
  1950. /** @module ng1 */ /** */
  1951. /** @hidden */
  1952. function $ViewScrollProvider() {
  1953. var useAnchorScroll = false;
  1954. this.useAnchorScroll = function () {
  1955. useAnchorScroll = true;
  1956. };
  1957. this.$get = ['$anchorScroll', '$timeout', function ($anchorScroll, $timeout) {
  1958. if (useAnchorScroll) {
  1959. return $anchorScroll;
  1960. }
  1961. return function ($element) {
  1962. return $timeout(function () {
  1963. $element[0].scrollIntoView();
  1964. }, 0, false);
  1965. };
  1966. }];
  1967. }
  1968. ng.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider);
  1969. /**
  1970. * Main entry point for angular 1.x build
  1971. * @module ng1
  1972. */ /** */
  1973. var index = "ui.router";
  1974. exports['default'] = index;
  1975. exports.core = core;
  1976. exports.watchDigests = watchDigests;
  1977. exports.getLocals = getLocals;
  1978. exports.getNg1ViewConfigFactory = getNg1ViewConfigFactory;
  1979. exports.ng1ViewsBuilder = ng1ViewsBuilder;
  1980. exports.Ng1ViewConfig = Ng1ViewConfig;
  1981. exports.StateProvider = StateProvider;
  1982. exports.UrlRouterProvider = UrlRouterProvider;
  1983. Object.keys(core).forEach(function (key) { exports[key] = core[key]; });
  1984. Object.defineProperty(exports, '__esModule', { value: true });
  1985. })));
  1986. //# sourceMappingURL=ui-router-angularjs.js.map