NOBUG: Fixed file access permissions
[moodle.git] / lib / yuilib / 3.13.0 / app-base / app-base.js
blob3ca15b46d641bd7ad2968392ed0e67db14a495dc
1 /*
2 YUI 3.13.0 (build 508226d)
3 Copyright 2013 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
8 YUI.add('app-base', function (Y, NAME) {
10 /**
11 The App Framework provides simple MVC-like building blocks (models, model lists,
12 views, and URL-based routing) for writing single-page JavaScript applications.
14 @main app
15 @module app
16 @since 3.4.0
17 **/
19 /**
20 Provides a top-level application component which manages navigation and views.
22 @module app
23 @submodule app-base
24 @since 3.5.0
25 **/
27 // TODO: Better handling of lifecycle for registered views:
29 //   * [!] Just redo basically everything with view management so there are no
30 //     pre-`activeViewChange` side effects and handle the rest of these things:
32 //   * Seems like any view created via `createView` should listen for the view's
33 //     `destroy` event and use that to remove it from the `_viewsInfoMap`. I
34 //     should look at what ModelList does for Models as a reference.
36 //   * Should we have a companion `destroyView()` method? Maybe this wouldn't be
37 //     needed if we have a `getView(name, create)` method, and already doing the
38 //     above? We could do `app.getView('foo').destroy()` and it would be removed
39 //     from the `_viewsInfoMap` as well.
41 //   * Should we wait to call a view's `render()` method inside of the
42 //     `_attachView()` method?
44 //   * Should named views support a collection of instances instead of just one?
47 var Lang    = Y.Lang,
48     YObject = Y.Object,
50     PjaxBase = Y.PjaxBase,
51     Router   = Y.Router,
52     View     = Y.View,
54     getClassName = Y.ClassNameManager.getClassName,
56     win = Y.config.win,
58     AppBase;
60 /**
61 Provides a top-level application component which manages navigation and views.
63 This gives you a foundation and structure on which to build your application; it
64 combines robust URL navigation with powerful routing and flexible view
65 management.
67 @class App.Base
68 @param {Object} [config] The following are configuration properties that can be
69     specified _in addition_ to default attribute values and the non-attribute
70     properties provided by `Y.Base`:
71   @param {Object} [config.views] Hash of view-name to metadata used to
72     declaratively describe an application's views and their relationship with
73     the app and other views. The views specified here will override any defaults
74     provided by the `views` object on the `prototype`.
75 @constructor
76 @extends Base
77 @uses View
78 @uses Router
79 @uses PjaxBase
80 @since 3.5.0
81 **/
82 AppBase = Y.Base.create('app', Y.Base, [View, Router, PjaxBase], {
83     // -- Public Properties ----------------------------------------------------
85     /**
86     Hash of view-name to metadata used to declaratively describe an
87     application's views and their relationship with the app and its other views.
89     The view metadata is composed of Objects keyed to a view-name that can have
90     any or all of the following properties:
92       * `type`: Function or a string representing the view constructor to use to
93         create view instances. If a string is used, the constructor function is
94         assumed to be on the `Y` object; e.g. `"SomeView"` -> `Y.SomeView`.
96       * `preserve`: Boolean for whether the view instance should be retained. By
97         default, the view instance will be destroyed when it is no longer the
98         `activeView`. If `true` the view instance will simply be `removed()`
99         from the DOM when it is no longer active. This is useful when the view
100         is frequently used and may be expensive to re-create.
102       * `parent`: String to another named view in this hash that represents the
103         parent view within the application's view hierarchy; e.g. a `"photo"`
104         view could have `"album"` has its `parent` view. This parent/child
105         relationship is a useful cue for things like transitions.
107       * `instance`: Used internally to manage the current instance of this named
108         view. This can be used if your view instance is created up-front, or if
109         you would rather manage the View lifecycle, but you probably should just
110         let this be handled for you.
112     If `views` are specified at instantiation time, the metadata in the `views`
113     Object here will be used as defaults when creating the instance's `views`.
115     Every `Y.App` instance gets its own copy of a `views` object so this Object
116     on the prototype will not be polluted.
118     @example
119         // Imagine that `Y.UsersView` and `Y.UserView` have been defined.
120         var app = new Y.App({
121             views: {
122                 users: {
123                     type    : Y.UsersView,
124                     preserve: true
125                 },
127                 user: {
128                     type  : Y.UserView,
129                     parent: 'users'
130                 }
131             }
132         });
134     @property views
135     @type Object
136     @default {}
137     @since 3.5.0
138     **/
139     views: {},
141     // -- Protected Properties -------------------------------------------------
143     /**
144     Map of view instance id (via `Y.stamp()`) to view-info object in `views`.
146     This mapping is used to tie a specific view instance back to its metadata by
147     adding a reference to the the related view info on the `views` object.
149     @property _viewInfoMap
150     @type Object
151     @default {}
152     @protected
153     @since 3.5.0
154     **/
156     // -- Lifecycle Methods ----------------------------------------------------
157     initializer: function (config) {
158         config || (config = {});
160         var views = {};
162         // Merges-in specified view metadata into local `views` object.
163         function mergeViewConfig(view, name) {
164             views[name] = Y.merge(views[name], view);
165         }
167         // First, each view in the `views` prototype object gets its metadata
168         // merged-in, providing the defaults.
169         YObject.each(this.views, mergeViewConfig);
171         // Then, each view in the specified `config.views` object gets its
172         // metadata merged-in.
173         YObject.each(config.views, mergeViewConfig);
175         // The resulting hodgepodge of metadata is then stored as the instance's
176         // `views` object, and no one's objects were harmed in the making.
177         this.views        = views;
178         this._viewInfoMap = {};
180         // Using `bind()` to aid extensibility.
181         this.after('activeViewChange', Y.bind('_afterActiveViewChange', this));
183         // PjaxBase will bind click events when `html5` is `true`, so this just
184         // forces the binding when `serverRouting` and `html5` are both falsy.
185         if (!this.get('serverRouting')) {
186             this._pjaxBindUI();
187         }
188     },
190     // TODO: `destructor` to destroy the `activeView`?
192     // -- Public Methods -------------------------------------------------------
194     /**
195     Creates and returns a new view instance using the provided `name` to look up
196     the view info metadata defined in the `views` object. The passed-in `config`
197     object is passed to the view constructor function.
199     This function also maps a view instance back to its view info metadata.
201     @method createView
202     @param {String} name The name of a view defined on the `views` object.
203     @param {Object} [config] The configuration object passed to the view
204       constructor function when creating the new view instance.
205     @return {View} The new view instance.
206     @since 3.5.0
207     **/
208     createView: function (name, config) {
209         var viewInfo = this.getViewInfo(name),
210             type     = (viewInfo && viewInfo.type) || View,
211             ViewConstructor, view;
213         // Looks for a namespaced constructor function on `Y`.
214         ViewConstructor = Lang.isString(type) ?
215                 YObject.getValue(Y, type.split('.')) : type;
217         // Create the view instance and map it with its metadata.
218         view = new ViewConstructor(config);
219         this._viewInfoMap[Y.stamp(view, true)] = viewInfo;
221         return view;
222     },
224     /**
225     Returns the metadata associated with a view instance or view name defined on
226     the `views` object.
228     @method getViewInfo
229     @param {View|String} view View instance, or name of a view defined on the
230       `views` object.
231     @return {Object} The metadata for the view, or `undefined` if the view is
232       not registered.
233     @since 3.5.0
234     **/
235     getViewInfo: function (view) {
236         if (Lang.isString(view)) {
237             return this.views[view];
238         }
240         return view && this._viewInfoMap[Y.stamp(view, true)];
241     },
243     /**
244     Navigates to the specified URL if there is a route handler that matches. In
245     browsers capable of using HTML5 history or when `serverRouting` is falsy,
246     the navigation will be enhanced by firing the `navigate` event and having
247     the app handle the "request". When `serverRouting` is `true`, non-HTML5
248     browsers will navigate to the new URL via a full page reload.
250     When there is a route handler for the specified URL and it is being
251     navigated to, this method will return `true`, otherwise it will return
252     `false`.
254     **Note:** The specified URL _must_ be of the same origin as the current URL,
255     otherwise an error will be logged and navigation will not occur. This is
256     intended as both a security constraint and a purposely imposed limitation as
257     it does not make sense to tell the app to navigate to a URL on a
258     different scheme, host, or port.
260     @method navigate
261     @param {String} url The URL to navigate to. This must be of the same origin
262       as the current URL.
263     @param {Object} [options] Additional options to configure the navigation.
264       These are mixed into the `navigate` event facade.
265         @param {Boolean} [options.replace] Whether or not the current history
266           entry will be replaced, or a new entry will be created. Will default
267           to `true` if the specified `url` is the same as the current URL.
268         @param {Boolean} [options.force] Whether the enhanced navigation
269           should occur even in browsers without HTML5 history. Will default to
270           `true` when `serverRouting` is falsy.
271     @see PjaxBase.navigate()
272     **/
273     // Does not override `navigate()` but does use extra `options`.
275     /**
276     Renders this application by appending the `viewContainer` node to the
277     `container` node if it isn't already a child of the container, and the
278     `activeView` will be appended the view container, if it isn't already.
280     You should call this method at least once, usually after the initialization
281     of your app instance so the proper DOM structure is setup and optionally
282     append the container to the DOM if it's not there already.
284     You may override this method to customize the app's rendering, but you
285     should expect that the `viewContainer`'s contents will be modified by the
286     app for the purpose of rendering the `activeView` when it changes.
288     @method render
289     @chainable
290     @see View.render()
291     **/
292     render: function () {
293         var CLASS_NAMES         = Y.App.CLASS_NAMES,
294             container           = this.get('container'),
295             viewContainer       = this.get('viewContainer'),
296             activeView          = this.get('activeView'),
297             activeViewContainer = activeView && activeView.get('container'),
298             areSame             = container.compareTo(viewContainer);
300         container.addClass(CLASS_NAMES.app);
301         viewContainer.addClass(CLASS_NAMES.views);
303         // Prevents needless shuffling around of nodes and maintains DOM order.
304         if (activeView && !viewContainer.contains(activeViewContainer)) {
305             viewContainer.appendChild(activeViewContainer);
306         }
308         // Prevents needless shuffling around of nodes and maintains DOM order.
309         if (!container.contains(viewContainer) && !areSame) {
310             container.appendChild(viewContainer);
311         }
313         return this;
314     },
316     /**
317     Sets which view is active/visible for the application. This will set the
318     app's `activeView` attribute to the specified `view`.
320     The `view` will be "attached" to this app, meaning it will be both rendered
321     into this app's `viewContainer` node and all of its events will bubble to
322     the app. The previous `activeView` will be "detached" from this app.
324     When a string-name is provided for a view which has been registered on this
325     app's `views` object, the referenced metadata will be used and the
326     `activeView` will be set to either a preserved view instance, or a new
327     instance of the registered view will be created using the specified `config`
328     object passed-into this method.
330     A callback function can be specified as either the third or fourth argument,
331     and this function will be called after the new `view` becomes the
332     `activeView`, is rendered to the `viewContainer`, and is ready to use.
334     @example
335         var app = new Y.App({
336             views: {
337                 usersView: {
338                     // Imagine that `Y.UsersView` has been defined.
339                     type: Y.UsersView
340                 }
341             },
343             users: new Y.ModelList()
344         });
346         app.route('/users/', function () {
347             this.showView('usersView', {users: this.get('users')});
348         });
350         app.render();
351         app.navigate('/uses/'); // => Creates a new `Y.UsersView` and shows it.
353     @method showView
354     @param {String|View} view The name of a view defined in the `views` object,
355         or a view instance which should become this app's `activeView`.
356     @param {Object} [config] Optional configuration to use when creating a new
357         view instance. This config object can also be used to update an existing
358         or preserved view's attributes when `options.update` is `true`.
359     @param {Object} [options] Optional object containing any of the following
360         properties:
361       @param {Function} [options.callback] Optional callback function to call
362         after new `activeView` is ready to use, the function will be passed:
363           @param {View} options.callback.view A reference to the new
364             `activeView`.
365       @param {Boolean} [options.prepend=false] Whether the `view` should be
366         prepended instead of appended to the `viewContainer`.
367       @param {Boolean} [options.render] Whether the `view` should be rendered.
368         **Note:** If no value is specified, a view instance will only be
369         rendered if it's newly created by this method.
370       @param {Boolean} [options.update=false] Whether an existing view should
371         have its attributes updated by passing the `config` object to its
372         `setAttrs()` method. **Note:** This option does not have an effect if
373         the `view` instance is created as a result of calling this method.
374     @param {Function} [callback] Optional callback Function to call after the
375         new `activeView` is ready to use. **Note:** this will override
376         `options.callback` and it can be specified as either the third or fourth
377         argument. The function will be passed the following:
378       @param {View} callback.view A reference to the new `activeView`.
379     @chainable
380     @since 3.5.0
381     **/
382     showView: function (view, config, options, callback) {
383         var viewInfo, created;
385         options || (options = {});
387         // Support the callback function being either the third or fourth arg.
388         if (callback) {
389             options = Y.merge(options, {callback: callback});
390         } else if (Lang.isFunction(options)) {
391             options = {callback: options};
392         }
394         if (Lang.isString(view)) {
395             viewInfo = this.getViewInfo(view);
397             // Use the preserved view instance, or create a new view.
398             // TODO: Maybe we can remove the strict check for `preserve` and
399             // assume we'll use a View instance if it is there, and just check
400             // `preserve` when detaching?
401             if (viewInfo && viewInfo.preserve && viewInfo.instance) {
402                 view = viewInfo.instance;
404                 // Make sure there's a mapping back to the view metadata.
405                 this._viewInfoMap[Y.stamp(view, true)] = viewInfo;
406             } else {
407                 // TODO: Add the app as a bubble target during construction, but
408                 // make sure to check that it isn't already in `bubbleTargets`!
409                 // This will allow the app to be notified for about _all_ of the
410                 // view's events. **Note:** This should _only_ happen if the
411                 // view is created _after_ `activeViewChange`.
413                 view    = this.createView(view, config);
414                 created = true;
415             }
416         }
418         // Update the specified or preserved `view` when signaled to do so.
419         // There's no need to updated a view if it was _just_ created.
420         if (options.update && !created) {
421             view.setAttrs(config);
422         }
424         // TODO: Hold off on rendering the view until after it has been
425         // "attached", and move the call to render into `_attachView()`.
427         // When a value is specified for `options.render`, prefer it because it
428         // represents the developer's intent. When no value is specified, the
429         // `view` will only be rendered if it was just created.
430         if ('render' in options) {
431             if (options.render) {
432                 view.render();
433             }
434         } else if (created) {
435             view.render();
436         }
438         return this._set('activeView', view, {options: options});
439     },
441     // -- Protected Methods ----------------------------------------------------
443     /**
444     Helper method to attach the view instance to the application by making the
445     app a bubble target of the view, append the view to the `viewContainer`, and
446     assign it to the `instance` property of the associated view info metadata.
448     @method _attachView
449     @param {View} view View to attach.
450     @param {Boolean} prepend=false Whether the view should be prepended instead
451       of appended to the `viewContainer`.
452     @protected
453     @since 3.5.0
454     **/
455     _attachView: function (view, prepend) {
456         if (!view) {
457             return;
458         }
460         var viewInfo      = this.getViewInfo(view),
461             viewContainer = this.get('viewContainer');
463         // Bubble the view's events to this app.
464         view.addTarget(this);
466         // Save the view instance in the `views` registry.
467         if (viewInfo) {
468             viewInfo.instance = view;
469         }
471         // TODO: Attach events here for persevered Views?
472         // See related TODO in `_detachView`.
474         // TODO: Actually render the view here so that it gets "attached" before
475         // it gets rendered?
477         // Insert view into the DOM.
478         viewContainer[prepend ? 'prepend' : 'append'](view.get('container'));
479     },
481     /**
482     Overrides View's container destruction to deal with the `viewContainer` and
483     checks to make sure not to remove and purge the `<body>`.
485     @method _destroyContainer
486     @protected
487     @see View._destroyContainer()
488     **/
489     _destroyContainer: function () {
490         var CLASS_NAMES   = Y.App.CLASS_NAMES,
491             container     = this.get('container'),
492             viewContainer = this.get('viewContainer'),
493             areSame       = container.compareTo(viewContainer);
495         // We do not want to remove or destroy the `<body>`.
496         if (Y.one('body').compareTo(container)) {
497             // Just clean-up our events listeners.
498             this.detachEvents();
500             // Clean-up `yui3-app` CSS class on the `container`.
501             container.removeClass(CLASS_NAMES.app);
503             if (areSame) {
504                 // Clean-up `yui3-app-views` CSS class on the `container`.
505                 container.removeClass(CLASS_NAMES.views);
506             } else {
507                 // Destroy and purge the `viewContainer`.
508                 viewContainer.remove(true);
509             }
511             return;
512         }
514         // Remove and purge events from both containers.
516         viewContainer.remove(true);
518         if (!areSame) {
519             container.remove(true);
520         }
521     },
523     /**
524     Helper method to detach the view instance from the application by removing
525     the application as a bubble target of the view, and either just removing the
526     view if it is intended to be preserved, or destroying the instance
527     completely.
529     @method _detachView
530     @param {View} view View to detach.
531     @protected
532     @since 3.5.0
533     **/
534     _detachView: function (view) {
535         if (!view) {
536             return;
537         }
539         var viewInfo = this.getViewInfo(view) || {};
541         if (viewInfo.preserve) {
542             view.remove();
543             // TODO: Detach events here for preserved Views? It is possible that
544             // some event subscriptions are made on elements other than the
545             // View's `container`.
546         } else {
547             view.destroy({remove: true});
549             // TODO: The following should probably happen automagically from
550             // `destroy()` being called! Possibly `removeTarget()` as well.
552             // Remove from view to view-info map.
553             delete this._viewInfoMap[Y.stamp(view, true)];
555             // Remove from view-info instance property.
556             if (view === viewInfo.instance) {
557                 delete viewInfo.instance;
558             }
559         }
561         view.removeTarget(this);
562     },
564     /**
565     Gets a request object that can be passed to a route handler.
567     This delegates to `Y.Router`'s `_getRequest()` method and adds a reference
568     to this app instance at `req.app`.
570     @method _getRequest
571     @param {String} src What initiated the URL change and need for the request.
572     @return {Object} Request object.
573     @protected
574     @see Router._getRequest
575     **/
576     _getRequest: function () {
577         var req = Router.prototype._getRequest.apply(this, arguments);
578         req.app = this;
579         return req;
580     },
582     /**
583     Getter for the `viewContainer` attribute.
585     @method _getViewContainer
586     @param {Node|null} value Current attribute value.
587     @return {Node} View container node.
588     @protected
589     @since 3.5.0
590     **/
591     _getViewContainer: function (value) {
592         // This wackiness is necessary to enable fully lazy creation of the
593         // container node both when no container is specified and when one is
594         // specified via a valueFn.
596         if (!value && !this._viewContainer) {
597             // Create a default container and set that as the new attribute
598             // value. The `this._viewContainer` property prevents infinite
599             // recursion.
600             value = this._viewContainer = this.create();
601             this._set('viewContainer', value);
602         }
604         return value;
605     },
607     /**
608     Provides the default value for the `html5` attribute.
610     The value returned is dependent on the value of the `serverRouting`
611     attribute. When `serverRouting` is explicit set to `false` (not just falsy),
612     the default value for `html5` will be set to `false` for *all* browsers.
614     When `serverRouting` is `true` or `undefined` the returned value will be
615     dependent on the browser's capability of using HTML5 history.
617     @method _initHtml5
618     @return {Boolean} Whether or not HTML5 history should be used.
619     @protected
620     @since 3.5.0
621     **/
622     _initHtml5: function () {
623         // When `serverRouting` is explicitly set to `false` (not just falsy),
624         // forcing hash-based URLs in all browsers.
625         if (this.get('serverRouting') === false) {
626             return false;
627         }
629         // Defaults to whether or not the browser supports HTML5 history.
630         return Router.html5;
631     },
633     /**
634     Determines if the specified `view` is configured as a child of the specified
635     `parent` view. This requires both views to be either named-views, or view
636     instances created using configuration data that exists in the `views`
637     object, e.g. created by the `createView()` or `showView()` method.
639     @method _isChildView
640     @param {View|String} view The name of a view defined in the `views` object,
641       or a view instance.
642     @param {View|String} parent The name of a view defined in the `views`
643       object, or a view instance.
644     @return {Boolean} Whether the view is configured as a child of the parent.
645     @protected
646     @since 3.5.0
647     **/
648     _isChildView: function (view, parent) {
649         var viewInfo   = this.getViewInfo(view),
650             parentInfo = this.getViewInfo(parent);
652         if (viewInfo && parentInfo) {
653             return this.getViewInfo(viewInfo.parent) === parentInfo;
654         }
656         return false;
657     },
659     /**
660     Determines if the specified `view` is configured as the parent of the
661     specified `child` view. This requires both views to be either named-views,
662     or view instances created using configuration data that exists in the
663     `views` object, e.g. created by the `createView()` or `showView()` method.
665     @method _isParentView
666     @param {View|String} view The name of a view defined in the `views` object,
667       or a view instance.
668     @param {View|String} parent The name of a view defined in the `views`
669       object, or a view instance.
670     @return {Boolean} Whether the view is configured as the parent of the child.
671     @protected
672     @since 3.5.0
673     **/
674     _isParentView: function (view, child) {
675         var viewInfo  = this.getViewInfo(view),
676             childInfo = this.getViewInfo(child);
678         if (viewInfo && childInfo) {
679             return this.getViewInfo(childInfo.parent) === viewInfo;
680         }
682         return false;
683     },
685     /**
686     Underlying implementation for `navigate()`.
688     @method _navigate
689     @param {String} url The fully-resolved URL that the app should dispatch to
690       its route handlers to fulfill the enhanced navigation "request", or use to
691       update `window.location` in non-HTML5 history capable browsers when
692       `serverRouting` is `true`.
693     @param {Object} [options] Additional options to configure the navigation.
694       These are mixed into the `navigate` event facade.
695         @param {Boolean} [options.replace] Whether or not the current history
696           entry will be replaced, or a new entry will be created. Will default
697           to `true` if the specified `url` is the same as the current URL.
698         @param {Boolean} [options.force] Whether the enhanced navigation
699           should occur even in browsers without HTML5 history. Will default to
700           `true` when `serverRouting` is falsy.
701     @protected
702     @see PjaxBase._navigate()
703     **/
704     _navigate: function (url, options) {
705         if (!this.get('serverRouting')) {
706             // Force navigation to be enhanced and handled by the app when
707             // `serverRouting` is falsy because the server might not be able to
708             // properly handle the request.
709             options = Y.merge({force: true}, options);
710         }
712         return PjaxBase.prototype._navigate.call(this, url, options);
713     },
715     /**
716     Will either save a history entry using `pushState()` or the location hash,
717     or gracefully-degrade to sending a request to the server causing a full-page
718     reload.
720     Overrides Router's `_save()` method to preform graceful-degradation when the
721     app's `serverRouting` is `true` and `html5` is `false` by updating the full
722     URL via standard assignment to `window.location` or by calling
723     `window.location.replace()`; both of which will cause a request to the
724     server resulting in a full-page reload.
726     Otherwise this will just delegate off to Router's `_save()` method allowing
727     the client-side enhanced routing to occur.
729     @method _save
730     @param {String} [url] URL for the history entry.
731     @param {Boolean} [replace=false] If `true`, the current history entry will
732       be replaced instead of a new one being added.
733     @chainable
734     @protected
735     @see Router._save()
736     **/
737     _save: function (url, replace) {
738         var path;
740         // Forces full-path URLs to always be used by modifying
741         // `window.location` in non-HTML5 history capable browsers.
742         if (this.get('serverRouting') && !this.get('html5')) {
743             // Perform same-origin check on the specified URL.
744             if (!this._hasSameOrigin(url)) {
745                 Y.error('Security error: The new URL must be of the same origin as the current URL.');
746                 return this;
747             }
749             // Either replace the current history entry or create a new one
750             // while navigating to the `url`.
751             if (win) {
752                 // Results in the URL's full path starting with '/'.
753                 path = this._joinURL(url || '');
755                 if (replace) {
756                     win.location.replace(path);
757                 } else {
758                     win.location = path;
759                 }
760             }
762             return this;
763         }
765         return Router.prototype._save.apply(this, arguments);
766     },
768     /**
769     Performs the actual change of this app's `activeView` by attaching the
770     `newView` to this app, and detaching the `oldView` from this app using any
771     specified `options`.
773     The `newView` is attached to the app by rendering it to the `viewContainer`,
774     and making this app a bubble target of its events.
776     The `oldView` is detached from the app by removing it from the
777     `viewContainer`, and removing this app as a bubble target for its events.
778     The `oldView` will either be preserved or properly destroyed.
780     **Note:** The `activeView` attribute is read-only and can be changed by
781     calling the `showView()` method.
783     @method _uiSetActiveView
784     @param {View} newView The View which is now this app's `activeView`.
785     @param {View} [oldView] The View which was this app's `activeView`.
786     @param {Object} [options] Optional object containing any of the following
787         properties:
788       @param {Function} [options.callback] Optional callback function to call
789         after new `activeView` is ready to use, the function will be passed:
790           @param {View} options.callback.view A reference to the new
791             `activeView`.
792       @param {Boolean} [options.prepend=false] Whether the `view` should be
793         prepended instead of appended to the `viewContainer`.
794       @param {Boolean} [options.render] Whether the `view` should be rendered.
795         **Note:** If no value is specified, a view instance will only be
796         rendered if it's newly created by this method.
797       @param {Boolean} [options.update=false] Whether an existing view should
798         have its attributes updated by passing the `config` object to its
799         `setAttrs()` method. **Note:** This option does not have an effect if
800         the `view` instance is created as a result of calling this method.
801     @protected
802     @since 3.5.0
803     **/
804     _uiSetActiveView: function (newView, oldView, options) {
805         options || (options = {});
807         var callback = options.callback,
808             isChild  = this._isChildView(newView, oldView),
809             isParent = !isChild && this._isParentView(newView, oldView),
810             prepend  = !!options.prepend || isParent;
812         // Prevent detaching (thus removing) the view we want to show. Also hard
813         // to animate out and in, the same view.
814         if (newView === oldView) {
815             return callback && callback.call(this, newView);
816         }
818         this._attachView(newView, prepend);
819         this._detachView(oldView);
821         if (callback) {
822             callback.call(this, newView);
823         }
824     },
826     // -- Protected Event Handlers ---------------------------------------------
828     /**
829     Handles the application's `activeViewChange` event (which is fired when the
830     `activeView` attribute changes) by detaching the old view, attaching the new
831     view.
833     The `activeView` attribute is read-only, so the public API to change its
834     value is through the `showView()` method.
836     @method _afterActiveViewChange
837     @param {EventFacade} e
838     @protected
839     @since 3.5.0
840     **/
841     _afterActiveViewChange: function (e) {
842         this._uiSetActiveView(e.newVal, e.prevVal, e.options);
843     }
844 }, {
845     ATTRS: {
846         /**
847         The application's active/visible view.
849         This attribute is read-only, to set the `activeView` use the
850         `showView()` method.
852         @attribute activeView
853         @type View
854         @default null
855         @readOnly
856         @see App.Base.showView()
857         @since 3.5.0
858         **/
859         activeView: {
860             value   : null,
861             readOnly: true
862         },
864         /**
865         Container node which represents the application's bounding-box, into
866         which this app's content will be rendered.
868         The container node serves as the host for all DOM events attached by the
869         app. Delegation is used to handle events on children of the container,
870         allowing the container's contents to be re-rendered at any time without
871         losing event subscriptions.
873         The default container is the `<body>` Node, but you can override this in
874         a subclass, or by passing in a custom `container` config value at
875         instantiation time.
877         When `container` is overridden by a subclass or passed as a config
878         option at instantiation time, it may be provided as a selector string, a
879         DOM element, or a `Y.Node` instance. During initialization, this app's
880         `create()` method will be called to convert the container into a
881         `Y.Node` instance if it isn't one already and stamp it with the CSS
882         class: `"yui3-app"`.
884         The container is not added to the page automatically. This allows you to
885         have full control over how and when your app is actually rendered to
886         the page.
888         @attribute container
889         @type HTMLElement|Node|String
890         @default Y.one('body')
891         @initOnly
892         **/
893         container: {
894             valueFn: function () {
895                 return Y.one('body');
896             }
897         },
899         /**
900         Whether or not this browser is capable of using HTML5 history.
902         This value is dependent on the value of `serverRouting` and will default
903         accordingly.
905         Setting this to `false` will force the use of hash-based history even on
906         HTML5 browsers, but please don't do this unless you understand the
907         consequences.
909         @attribute html5
910         @type Boolean
911         @initOnly
912         @see serverRouting
913         **/
914         html5: {
915             valueFn: '_initHtml5'
916         },
918         /**
919         CSS selector string used to filter link click events so that only the
920         links which match it will have the enhanced-navigation behavior of pjax
921         applied.
923         When a link is clicked and that link matches this selector, navigating
924         to the link's `href` URL using the enhanced, pjax, behavior will be
925         attempted; and the browser's default way to navigate to new pages will
926         be the fallback.
928         By default this selector will match _all_ links on the page.
930         @attribute linkSelector
931         @type String|Function
932         @default "a"
933         **/
934         linkSelector: {
935             value: 'a'
936         },
938         /**
939         Whether or not this application's server is capable of properly routing
940         all requests and rendering the initial state in the HTML responses.
942         This can have three different values, each having particular
943         implications on how the app will handle routing and navigation:
945           * `undefined`: The best form of URLs will be chosen based on the
946             capabilities of the browser. Given no information about the server
947             environmentm a balanced approach to routing and navigation is
948             chosen.
950             The server should be capable of handling full-path requests, since
951             full-URLs will be generated by browsers using HTML5 history. If this
952             is a client-side-only app the server could handle full-URL requests
953             by sending a redirect back to the root with a hash-based URL, e.g:
955                 Request:     http://example.com/users/1
956                 Redirect to: http://example.com/#/users/1
958           * `true`: The server is *fully* capable of properly handling requests
959             to all full-path URLs the app can produce.
961             This is the best option for progressive-enhancement because it will
962             cause **all URLs to always have full-paths**, which means the server
963             will be able to accurately handle all URLs this app produces. e.g.
965                 http://example.com/users/1
967             To meet this strict full-URL requirement, browsers which are not
968             capable of using HTML5 history will make requests to the server
969             resulting in full-page reloads.
971           * `false`: The server is *not* capable of properly handling requests
972             to all full-path URLs the app can produce, therefore all routing
973             will be handled by this App instance.
975             Be aware that this will cause **all URLs to always be hash-based**,
976             even in browsers that are capable of using HTML5 history. e.g.
978                 http://example.com/#/users/1
980             A single-page or client-side-only app where the server sends a
981             "shell" page with JavaScript to the client might have this
982             restriction. If you're setting this to `false`, read the following:
984         **Note:** When this is set to `false`, the server will *never* receive
985         the full URL because browsers do not send the fragment-part to the
986         server, that is everything after and including the "#".
988         Consider the following example:
990             URL shown in browser: http://example.com/#/users/1
991             URL sent to server:   http://example.com/
993         You should feel bad about hurting our precious web if you forcefully set
994         either `serverRouting` or `html5` to `false`, because you're basically
995         punching the web in the face here with your lossy URLs! Please make sure
996         you know what you're doing and that you understand the implications.
998         Ideally you should always prefer full-path URLs (not /#/foo/), and want
999         full-page reloads when the client's browser is not capable of enhancing
1000         the experience using the HTML5 history APIs. Setting this to `true` is
1001         the best option for progressive-enhancement (and graceful-degradation).
1003         @attribute serverRouting
1004         @type Boolean
1005         @default undefined
1006         @initOnly
1007         @since 3.5.0
1008         **/
1009         serverRouting: {
1010             valueFn  : function () { return Y.App.serverRouting; },
1011             writeOnce: 'initOnly'
1012         },
1014         /**
1015         The node into which this app's `views` will be rendered when they become
1016         the `activeView`.
1018         The view container node serves as the container to hold the app's
1019         `activeView`. Each time the `activeView` is set via `showView()`, the
1020         previous view will be removed from this node, and the new active view's
1021         `container` node will be appended.
1023         The default view container is a `<div>` Node, but you can override this
1024         in a subclass, or by passing in a custom `viewContainer` config value at
1025         instantiation time. The `viewContainer` may be provided as a selector
1026         string, DOM element, or a `Y.Node` instance (having the `viewContainer`
1027         and the `container` be the same node is also supported).
1029         The app's `render()` method will stamp the view container with the CSS
1030         class `"yui3-app-views"` and append it to the app's `container` node if
1031         it isn't already, and any `activeView` will be appended to this node if
1032         it isn't already.
1034         @attribute viewContainer
1035         @type HTMLElement|Node|String
1036         @default Y.Node.create(this.containerTemplate)
1037         @initOnly
1038         @since 3.5.0
1039         **/
1040         viewContainer: {
1041             getter   : '_getViewContainer',
1042             setter   : Y.one,
1043             writeOnce: true
1044         }
1045     },
1047     /**
1048     Properties that shouldn't be turned into ad-hoc attributes when passed to
1049     App's constructor.
1051     @property _NON_ATTRS_CFG
1052     @type Array
1053     @static
1054     @protected
1055     @since 3.5.0
1056     **/
1057     _NON_ATTRS_CFG: ['views']
1060 // -- Namespace ----------------------------------------------------------------
1061 Y.namespace('App').Base = AppBase;
1064 Provides a top-level application component which manages navigation and views.
1066 This gives you a foundation and structure on which to build your application; it
1067 combines robust URL navigation with powerful routing and flexible view
1068 management.
1070 `Y.App` is both a namespace and constructor function. The `Y.App` class is
1071 special in that any `Y.App` class extensions that are included in the YUI
1072 instance will be **auto-mixed** on to the `Y.App` class. Consider this example:
1074     YUI().use('app-base', 'app-transitions', function (Y) {
1075         // This will create two YUI Apps, `basicApp` will not have transitions,
1076         // but `fancyApp` will have transitions support included and turn it on.
1077         var basicApp = new Y.App.Base(),
1078             fancyApp = new Y.App({transitions: true});
1079     });
1081 @class App
1082 @param {Object} [config] The following are configuration properties that can be
1083     specified _in addition_ to default attribute values and the non-attribute
1084     properties provided by `Y.Base`:
1085   @param {Object} [config.views] Hash of view-name to metadata used to
1086     declaratively describe an application's views and their relationship with
1087     the app and other views. The views specified here will override any defaults
1088     provided by the `views` object on the `prototype`.
1089 @constructor
1090 @extends App.Base
1091 @uses App.Content
1092 @uses App.Transitions
1093 @uses PjaxContent
1094 @since 3.5.0
1096 Y.App = Y.mix(Y.Base.create('app', AppBase, []), Y.App, true);
1099 CSS classes used by `Y.App`.
1101 @property CLASS_NAMES
1102 @type Object
1103 @default {}
1104 @static
1105 @since 3.6.0
1107 Y.App.CLASS_NAMES = {
1108     app  : getClassName('app'),
1109     views: getClassName('app', 'views')
1113 Default `serverRouting` attribute value for all apps.
1115 @property serverRouting
1116 @type Boolean
1117 @default undefined
1118 @static
1119 @since 3.6.0
1123 }, '3.13.0', {"requires": ["classnamemanager", "pjax-base", "router", "view"]});