Merge "Add qtip job to pod zte-virtual6"
[releng.git] / utils / test / testapi / 3rd_party / static / testapi-ui / assets / lib / angular-ui-router / src / stateDirectives.js
1 function parseStateRef(ref, current) {
2   var preparsed = ref.match(/^\s*({[^}]*})\s*$/), parsed;
3   if (preparsed) ref = current + '(' + preparsed[1] + ')';
4   parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
5   if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'");
6   return { state: parsed[1], paramExpr: parsed[3] || null };
7 }
8
9 function stateContext(el) {
10   var stateData = el.parent().inheritedData('$uiView');
11
12   if (stateData && stateData.state && stateData.state.name) {
13     return stateData.state;
14   }
15 }
16
17 /**
18  * @ngdoc directive
19  * @name ui.router.state.directive:ui-sref
20  *
21  * @requires ui.router.state.$state
22  * @requires $timeout
23  *
24  * @restrict A
25  *
26  * @description
27  * A directive that binds a link (`<a>` tag) to a state. If the state has an associated 
28  * URL, the directive will automatically generate & update the `href` attribute via 
29  * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking 
30  * the link will trigger a state transition with optional parameters. 
31  *
32  * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be 
33  * handled natively by the browser.
34  *
35  * You can also use relative state paths within ui-sref, just like the relative 
36  * paths passed to `$state.go()`. You just need to be aware that the path is relative
37  * to the state that the link lives in, in other words the state that loaded the 
38  * template containing the link.
39  *
40  * You can specify options to pass to {@link ui.router.state.$state#go $state.go()}
41  * using the `ui-sref-opts` attribute. Options are restricted to `location`, `inherit`,
42  * and `reload`.
43  *
44  * @example
45  * Here's an example of how you'd use ui-sref and how it would compile. If you have the 
46  * following template:
47  * <pre>
48  * <a ui-sref="home">Home</a> | <a ui-sref="about">About</a> | <a ui-sref="{page: 2}">Next page</a>
49  * 
50  * <ul>
51  *     <li ng-repeat="contact in contacts">
52  *         <a ui-sref="contacts.detail({ id: contact.id })">{{ contact.name }}</a>
53  *     </li>
54  * </ul>
55  * </pre>
56  * 
57  * Then the compiled html would be (assuming Html5Mode is off and current state is contacts):
58  * <pre>
59  * <a href="#/home" ui-sref="home">Home</a> | <a href="#/about" ui-sref="about">About</a> | <a href="#/contacts?page=2" ui-sref="{page: 2}">Next page</a>
60  * 
61  * <ul>
62  *     <li ng-repeat="contact in contacts">
63  *         <a href="#/contacts/1" ui-sref="contacts.detail({ id: contact.id })">Joe</a>
64  *     </li>
65  *     <li ng-repeat="contact in contacts">
66  *         <a href="#/contacts/2" ui-sref="contacts.detail({ id: contact.id })">Alice</a>
67  *     </li>
68  *     <li ng-repeat="contact in contacts">
69  *         <a href="#/contacts/3" ui-sref="contacts.detail({ id: contact.id })">Bob</a>
70  *     </li>
71  * </ul>
72  *
73  * <a ui-sref="home" ui-sref-opts="{reload: true}">Home</a>
74  * </pre>
75  *
76  * @param {string} ui-sref 'stateName' can be any valid absolute or relative state
77  * @param {Object} ui-sref-opts options to pass to {@link ui.router.state.$state#go $state.go()}
78  */
79 $StateRefDirective.$inject = ['$state', '$timeout'];
80 function $StateRefDirective($state, $timeout) {
81   var allowedOptions = ['location', 'inherit', 'reload'];
82
83   return {
84     restrict: 'A',
85     require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
86     link: function(scope, element, attrs, uiSrefActive) {
87       var ref = parseStateRef(attrs.uiSref, $state.current.name);
88       var params = null, url = null, base = stateContext(element) || $state.$current;
89       var newHref = null, isAnchor = element.prop("tagName") === "A";
90       var isForm = element[0].nodeName === "FORM";
91       var attr = isForm ? "action" : "href", nav = true;
92
93       var options = { relative: base, inherit: true };
94       var optionsOverride = scope.$eval(attrs.uiSrefOpts) || {};
95
96       angular.forEach(allowedOptions, function(option) {
97         if (option in optionsOverride) {
98           options[option] = optionsOverride[option];
99         }
100       });
101
102       var update = function(newVal) {
103         if (newVal) params = angular.copy(newVal);
104         if (!nav) return;
105
106         newHref = $state.href(ref.state, params, options);
107
108         var activeDirective = uiSrefActive[1] || uiSrefActive[0];
109         if (activeDirective) {
110           activeDirective.$$setStateInfo(ref.state, params);
111         }
112         if (newHref === null) {
113           nav = false;
114           return false;
115         }
116         attrs.$set(attr, newHref);
117       };
118
119       if (ref.paramExpr) {
120         scope.$watch(ref.paramExpr, function(newVal, oldVal) {
121           if (newVal !== params) update(newVal);
122         }, true);
123         params = angular.copy(scope.$eval(ref.paramExpr));
124       }
125       update();
126
127       if (isForm) return;
128
129       element.bind("click", function(e) {
130         var button = e.which || e.button;
131         if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) {
132           // HACK: This is to allow ng-clicks to be processed before the transition is initiated:
133           var transition = $timeout(function() {
134             $state.go(ref.state, params, options);
135           });
136           e.preventDefault();
137
138           // if the state has no URL, ignore one preventDefault from the <a> directive.
139           var ignorePreventDefaultCount = isAnchor && !newHref ? 1: 0;
140           e.preventDefault = function() {
141             if (ignorePreventDefaultCount-- <= 0)
142               $timeout.cancel(transition);
143           };
144         }
145       });
146     }
147   };
148 }
149
150 /**
151  * @ngdoc directive
152  * @name ui.router.state.directive:ui-sref-active
153  *
154  * @requires ui.router.state.$state
155  * @requires ui.router.state.$stateParams
156  * @requires $interpolate
157  *
158  * @restrict A
159  *
160  * @description
161  * A directive working alongside ui-sref to add classes to an element when the
162  * related ui-sref directive's state is active, and removing them when it is inactive.
163  * The primary use-case is to simplify the special appearance of navigation menus
164  * relying on `ui-sref`, by having the "active" state's menu button appear different,
165  * distinguishing it from the inactive menu items.
166  *
167  * ui-sref-active can live on the same element as ui-sref or on a parent element. The first
168  * ui-sref-active found at the same level or above the ui-sref will be used.
169  *
170  * Will activate when the ui-sref's target state or any child state is active. If you
171  * need to activate only when the ui-sref target state is active and *not* any of
172  * it's children, then you will use
173  * {@link ui.router.state.directive:ui-sref-active-eq ui-sref-active-eq}
174  *
175  * @example
176  * Given the following template:
177  * <pre>
178  * <ul>
179  *   <li ui-sref-active="active" class="item">
180  *     <a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a>
181  *   </li>
182  * </ul>
183  * </pre>
184  *
185  *
186  * When the app state is "app.user" (or any children states), and contains the state parameter "user" with value "bilbobaggins",
187  * the resulting HTML will appear as (note the 'active' class):
188  * <pre>
189  * <ul>
190  *   <li ui-sref-active="active" class="item active">
191  *     <a ui-sref="app.user({user: 'bilbobaggins'})" href="/users/bilbobaggins">@bilbobaggins</a>
192  *   </li>
193  * </ul>
194  * </pre>
195  *
196  * The class name is interpolated **once** during the directives link time (any further changes to the
197  * interpolated value are ignored).
198  *
199  * Multiple classes may be specified in a space-separated format:
200  * <pre>
201  * <ul>
202  *   <li ui-sref-active='class1 class2 class3'>
203  *     <a ui-sref="app.user">link</a>
204  *   </li>
205  * </ul>
206  * </pre>
207  */
208
209 /**
210  * @ngdoc directive
211  * @name ui.router.state.directive:ui-sref-active-eq
212  *
213  * @requires ui.router.state.$state
214  * @requires ui.router.state.$stateParams
215  * @requires $interpolate
216  *
217  * @restrict A
218  *
219  * @description
220  * The same as {@link ui.router.state.directive:ui-sref-active ui-sref-active} but will only activate
221  * when the exact target state used in the `ui-sref` is active; no child states.
222  *
223  */
224 $StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
225 function $StateRefActiveDirective($state, $stateParams, $interpolate) {
226   return  {
227     restrict: "A",
228     controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
229       var state, params, activeClass;
230
231       // There probably isn't much point in $observing this
232       // uiSrefActive and uiSrefActiveEq share the same directive object with some
233       // slight difference in logic routing
234       activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope);
235
236       // Allow uiSref to communicate with uiSrefActive[Equals]
237       this.$$setStateInfo = function (newState, newParams) {
238         state = $state.get(newState, stateContext($element));
239         params = newParams;
240         update();
241       };
242
243       $scope.$on('$stateChangeSuccess', update);
244
245       // Update route state
246       function update() {
247         if (isMatch()) {
248           $element.addClass(activeClass);
249         } else {
250           $element.removeClass(activeClass);
251         }
252       }
253
254       function isMatch() {
255         if (typeof $attrs.uiSrefActiveEq !== 'undefined') {
256           return state && $state.is(state.name, params);
257         } else {
258           return state && $state.includes(state.name, params);
259         }
260       }
261     }]
262   };
263 }
264
265 angular.module('ui.router.state')
266   .directive('uiSref', $StateRefDirective)
267   .directive('uiSrefActive', $StateRefActiveDirective)
268   .directive('uiSrefActiveEq', $StateRefActiveDirective);