Update JSON API
[src/app-framework-demo.git] / afm-client / bower_components / 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           if (angular.version.minor > 2) {
147             $animate.enter(element, null, target).then(cb);
148           } else {
149             $animate.enter(element, null, target, cb);
150           }
151         },
152         leave: function(element, cb) {
153           if (angular.version.minor > 2) {
154             $animate.leave(element).then(cb);
155           } else {
156             $animate.leave(element, cb);
157           }
158         }
159       };
160     }
161
162     if ($animator) {
163       var animate = $animator && $animator(scope, attrs);
164
165       return {
166         enter: function(element, target, cb) {animate.enter(element, null, target); cb(); },
167         leave: function(element, cb) { animate.leave(element); cb(); }
168       };
169     }
170
171     return statics();
172   }
173
174   var directive = {
175     restrict: 'ECA',
176     terminal: true,
177     priority: 400,
178     transclude: 'element',
179     compile: function (tElement, tAttrs, $transclude) {
180       return function (scope, $element, attrs) {
181         var previousEl, currentEl, currentScope, latestLocals,
182             onloadExp     = attrs.onload || '',
183             autoScrollExp = attrs.autoscroll,
184             renderer      = getRenderer(attrs, scope);
185
186         scope.$on('$stateChangeSuccess', function() {
187           updateView(false);
188         });
189
190         updateView(true);
191
192         function cleanupLastView() {
193           var _previousEl = previousEl;
194           var _currentScope = currentScope;
195
196           if (_currentScope) {
197             _currentScope._willBeDestroyed = true;
198           }
199
200           function cleanOld() {
201             if (_previousEl) {
202               _previousEl.remove();
203             }
204
205             if (_currentScope) {
206               _currentScope.$destroy();
207             }
208           }
209
210           if (currentEl) {
211             renderer.leave(currentEl, function() {
212               cleanOld();
213               previousEl = null;
214             });
215
216             previousEl = currentEl;
217           } else {
218             cleanOld();
219             previousEl = null;
220           }
221
222           currentEl = null;
223           currentScope = null;
224         }
225
226         function updateView(firstTime) {
227           var newScope,
228               name            = getUiViewName(scope, attrs, $element, $interpolate),
229               previousLocals  = name && $state.$current && $state.$current.locals[name];
230
231           if (!firstTime && previousLocals === latestLocals || scope._willBeDestroyed) return; // nothing to do
232           newScope = scope.$new();
233           latestLocals = $state.$current.locals[name];
234
235           /**
236            * @ngdoc event
237            * @name ui.router.state.directive:ui-view#$viewContentLoading
238            * @eventOf ui.router.state.directive:ui-view
239            * @eventType emits on ui-view directive scope
240            * @description
241            *
242            * Fired once the view **begins loading**, *before* the DOM is rendered.
243            *
244            * @param {Object} event Event object.
245            * @param {string} viewName Name of the view.
246            */
247           newScope.$emit('$viewContentLoading', name);
248
249           var clone = $transclude(newScope, function(clone) {
250             renderer.enter(clone, $element, function onUiViewEnter() {
251               if(currentScope) {
252                 currentScope.$emit('$viewContentAnimationEnded');
253               }
254
255               if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) {
256                 $uiViewScroll(clone);
257               }
258             });
259             cleanupLastView();
260           });
261
262           currentEl = clone;
263           currentScope = newScope;
264           /**
265            * @ngdoc event
266            * @name ui.router.state.directive:ui-view#$viewContentLoaded
267            * @eventOf ui.router.state.directive:ui-view
268            * @eventType emits on ui-view directive scope
269            * @description
270            * Fired once the view is **loaded**, *after* the DOM is rendered.
271            *
272            * @param {Object} event Event object.
273            * @param {string} viewName Name of the view.
274            */
275           currentScope.$emit('$viewContentLoaded', name);
276           currentScope.$eval(onloadExp);
277         }
278       };
279     }
280   };
281
282   return directive;
283 }
284
285 $ViewDirectiveFill.$inject = ['$compile', '$controller', '$state', '$interpolate'];
286 function $ViewDirectiveFill (  $compile,   $controller,   $state,   $interpolate) {
287   return {
288     restrict: 'ECA',
289     priority: -400,
290     compile: function (tElement) {
291       var initial = tElement.html();
292       return function (scope, $element, attrs) {
293         var current = $state.$current,
294             name = getUiViewName(scope, attrs, $element, $interpolate),
295             locals  = current && current.locals[name];
296
297         if (! locals) {
298           return;
299         }
300
301         $element.data('$uiView', { name: name, state: locals.$$state });
302         $element.html(locals.$template ? locals.$template : initial);
303
304         var link = $compile($element.contents());
305
306         if (locals.$$controller) {
307           locals.$scope = scope;
308           locals.$element = $element;
309           var controller = $controller(locals.$$controller, locals);
310           if (locals.$$controllerAs) {
311             scope[locals.$$controllerAs] = controller;
312           }
313           $element.data('$ngControllerController', controller);
314           $element.children().data('$ngControllerController', controller);
315         }
316
317         link(scope);
318       };
319     }
320   };
321 }
322
323 /**
324  * Shared ui-view code for both directives:
325  * Given scope, element, and its attributes, return the view's name
326  */
327 function getUiViewName(scope, attrs, element, $interpolate) {
328   var name = $interpolate(attrs.uiView || attrs.name || '')(scope);
329   var inherited = element.inheritedData('$uiView');
330   return name.indexOf('@') >= 0 ?  name :  (name + '@' + (inherited ? inherited.state.name : ''));
331 }
332
333 angular.module('ui.router.state').directive('uiView', $ViewDirective);
334 angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill);