Merge "add web portal framework for TestAPI"
[releng.git] / utils / test / testapi / 3rd_party / static / testapi-ui / assets / lib / angular-ui-router / src / viewDirective.js
1 /**
2  * @ngdoc directive
3  * @name ui.router.state.directive:ui-view
4  *
5  * @requires ui.router.state.$state
6  * @requires $compile
7  * @requires $controller
8  * @requires $injector
9  * @requires ui.router.state.$uiViewScroll
10  * @requires $document
11  *
12  * @restrict ECA
13  *
14  * @description
15  * The ui-view directive tells $state where to place your templates.
16  *
17  * @param {string=} name A view name. The name should be unique amongst the other views in the
18  * same state. You can have views of the same name that live in different states.
19  *
20  * @param {string=} autoscroll It allows you to set the scroll behavior of the browser window
21  * when a view is populated. By default, $anchorScroll is overridden by ui-router's custom scroll
22  * service, {@link ui.router.state.$uiViewScroll}. This custom service let's you
23  * scroll ui-view elements into view when they are populated during a state activation.
24  *
25  * *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll)
26  * functionality, call `$uiViewScrollProvider.useAnchorScroll()`.*
27  *
28  * @param {string=} onload Expression to evaluate whenever the view updates.
29  * 
30  * @example
31  * A view can be unnamed or named. 
32  * <pre>
33  * <!-- Unnamed -->
34  * <div ui-view></div> 
35  * 
36  * <!-- Named -->
37  * <div ui-view="viewName"></div>
38  * </pre>
39  *
40  * You can only have one unnamed view within any template (or root html). If you are only using a 
41  * single view and it is unnamed then you can populate it like so:
42  * <pre>
43  * <div ui-view></div> 
44  * $stateProvider.state("home", {
45  *   template: "<h1>HELLO!</h1>"
46  * })
47  * </pre>
48  * 
49  * The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#views `views`}
50  * config property, by name, in this case an empty name:
51  * <pre>
52  * $stateProvider.state("home", {
53  *   views: {
54  *     "": {
55  *       template: "<h1>HELLO!</h1>"
56  *     }
57  *   }    
58  * })
59  * </pre>
60  * 
61  * But typically you'll only use the views property if you name your view or have more than one view 
62  * in the same template. There's not really a compelling reason to name a view if its the only one, 
63  * but you could if you wanted, like so:
64  * <pre>
65  * <div ui-view="main"></div>
66  * </pre> 
67  * <pre>
68  * $stateProvider.state("home", {
69  *   views: {
70  *     "main": {
71  *       template: "<h1>HELLO!</h1>"
72  *     }
73  *   }    
74  * })
75  * </pre>
76  * 
77  * Really though, you'll use views to set up multiple views:
78  * <pre>
79  * <div ui-view></div>
80  * <div ui-view="chart"></div> 
81  * <div ui-view="data"></div> 
82  * </pre>
83  * 
84  * <pre>
85  * $stateProvider.state("home", {
86  *   views: {
87  *     "": {
88  *       template: "<h1>HELLO!</h1>"
89  *     },
90  *     "chart": {
91  *       template: "<chart_thing/>"
92  *     },
93  *     "data": {
94  *       template: "<data_thing/>"
95  *     }
96  *   }    
97  * })
98  * </pre>
99  *
100  * Examples for `autoscroll`:
101  *
102  * <pre>
103  * <!-- If autoscroll present with no expression,
104  *      then scroll ui-view into view -->
105  * <ui-view autoscroll/>
106  *
107  * <!-- If autoscroll present with valid expression,
108  *      then scroll ui-view into view if expression evaluates to true -->
109  * <ui-view autoscroll='true'/>
110  * <ui-view autoscroll='false'/>
111  * <ui-view autoscroll='scopeVariable'/>
112  * </pre>
113  */
114 $ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate'];
115 function $ViewDirective(   $state,   $injector,   $uiViewScroll,   $interpolate) {
116
117   function getService() {
118     return ($injector.has) ? function(service) {
119       return $injector.has(service) ? $injector.get(service) : null;
120     } : function(service) {
121       try {
122         return $injector.get(service);
123       } catch (e) {
124         return null;
125       }
126     };
127   }
128
129   var service = getService(),
130       $animator = service('$animator'),
131       $animate = service('$animate');
132
133   // Returns a set of DOM manipulation functions based on which Angular version
134   // it should use
135   function getRenderer(attrs, scope) {
136     var statics = function() {
137       return {
138         enter: function (element, target, cb) { target.after(element); cb(); },
139         leave: function (element, cb) { element.remove(); cb(); }
140       };
141     };
142
143     if ($animate) {
144       return {
145         enter: function(element, target, cb) {
146           var promise = $animate.enter(element, null, target, cb);
147           if (promise && promise.then) promise.then(cb);
148         },
149         leave: function(element, cb) {
150           var promise = $animate.leave(element, cb);
151           if (promise && promise.then) promise.then(cb);
152         }
153       };
154     }
155
156     if ($animator) {
157       var animate = $animator && $animator(scope, attrs);
158
159       return {
160         enter: function(element, target, cb) {animate.enter(element, null, target); cb(); },
161         leave: function(element, cb) { animate.leave(element); cb(); }
162       };
163     }
164
165     return statics();
166   }
167
168   var directive = {
169     restrict: 'ECA',
170     terminal: true,
171     priority: 400,
172     transclude: 'element',
173     compile: function (tElement, tAttrs, $transclude) {
174       return function (scope, $element, attrs) {
175         var previousEl, currentEl, currentScope, latestLocals,
176             onloadExp     = attrs.onload || '',
177             autoScrollExp = attrs.autoscroll,
178             renderer      = getRenderer(attrs, scope);
179
180         scope.$on('$stateChangeSuccess', function() {
181           updateView(false);
182         });
183         scope.$on('$viewContentLoading', function() {
184           updateView(false);
185         });
186
187         updateView(true);
188
189         function cleanupLastView() {
190           if (previousEl) {
191             previousEl.remove();
192             previousEl = null;
193           }
194
195           if (currentScope) {
196             currentScope.$destroy();
197             currentScope = null;
198           }
199
200           if (currentEl) {
201             renderer.leave(currentEl, function() {
202               previousEl = null;
203             });
204
205             previousEl = currentEl;
206             currentEl = null;
207           }
208         }
209
210         function updateView(firstTime) {
211           var newScope,
212               name            = getUiViewName(scope, attrs, $element, $interpolate),
213               previousLocals  = name && $state.$current && $state.$current.locals[name];
214
215           if (!firstTime && previousLocals === latestLocals) return; // nothing to do
216           newScope = scope.$new();
217           latestLocals = $state.$current.locals[name];
218
219           var clone = $transclude(newScope, function(clone) {
220             renderer.enter(clone, $element, function onUiViewEnter() {
221               if(currentScope) {
222                 currentScope.$emit('$viewContentAnimationEnded');
223               }
224
225               if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) {
226                 $uiViewScroll(clone);
227               }
228             });
229             cleanupLastView();
230           });
231
232           currentEl = clone;
233           currentScope = newScope;
234           /**
235            * @ngdoc event
236            * @name ui.router.state.directive:ui-view#$viewContentLoaded
237            * @eventOf ui.router.state.directive:ui-view
238            * @eventType emits on ui-view directive scope
239            * @description           *
240            * Fired once the view is **loaded**, *after* the DOM is rendered.
241            *
242            * @param {Object} event Event object.
243            */
244           currentScope.$emit('$viewContentLoaded');
245           currentScope.$eval(onloadExp);
246         }
247       };
248     }
249   };
250
251   return directive;
252 }
253
254 $ViewDirectiveFill.$inject = ['$compile', '$controller', '$state', '$interpolate'];
255 function $ViewDirectiveFill (  $compile,   $controller,   $state,   $interpolate) {
256   return {
257     restrict: 'ECA',
258     priority: -400,
259     compile: function (tElement) {
260       var initial = tElement.html();
261       return function (scope, $element, attrs) {
262         var current = $state.$current,
263             name = getUiViewName(scope, attrs, $element, $interpolate),
264             locals  = current && current.locals[name];
265
266         if (! locals) {
267           return;
268         }
269
270         $element.data('$uiView', { name: name, state: locals.$$state });
271         $element.html(locals.$template ? locals.$template : initial);
272
273         var link = $compile($element.contents());
274
275         if (locals.$$controller) {
276           locals.$scope = scope;
277           var controller = $controller(locals.$$controller, locals);
278           if (locals.$$controllerAs) {
279             scope[locals.$$controllerAs] = controller;
280           }
281           $element.data('$ngControllerController', controller);
282           $element.children().data('$ngControllerController', controller);
283         }
284
285         link(scope);
286       };
287     }
288   };
289 }
290
291 /**
292  * Shared ui-view code for both directives:
293  * Given scope, element, and its attributes, return the view's name
294  */
295 function getUiViewName(scope, attrs, element, $interpolate) {
296   var name = $interpolate(attrs.uiView || attrs.name || '')(scope);
297   var inherited = element.inheritedData('$uiView');
298   return name.indexOf('@') >= 0 ?  name :  (name + '@' + (inherited ? inherited.state.name : ''));
299 }
300
301 angular.module('ui.router.state').directive('uiView', $ViewDirective);
302 angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill);