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/
8 YUI.add('app-base', function (Y, NAME) {
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.
20 Provides a top-level application component which manages navigation and views.
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?
50 PjaxBase = Y.PjaxBase,
54 getClassName = Y.ClassNameManager.getClassName,
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
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`.
82 AppBase = Y.Base.create('app', Y.Base, [View, Router, PjaxBase], {
83 // -- Public Properties ----------------------------------------------------
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.
119 // Imagine that `Y.UsersView` and `Y.UserView` have been defined.
120 var app = new Y.App({
141 // -- Protected Properties -------------------------------------------------
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
156 // -- Lifecycle Methods ----------------------------------------------------
157 initializer: function (config) {
158 config || (config = {});
162 // Merges-in specified view metadata into local `views` object.
163 function mergeViewConfig(view, name) {
164 views[name] = Y.merge(views[name], view);
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.
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')) {
190 // TODO: `destructor` to destroy the `activeView`?
192 // -- Public Methods -------------------------------------------------------
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.
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.
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;
225 Returns the metadata associated with a view instance or view name defined on
229 @param {View|String} view View instance, or name of a view defined on the
231 @return {Object} The metadata for the view, or `undefined` if the view is
235 getViewInfo: function (view) {
236 if (Lang.isString(view)) {
237 return this.views[view];
240 return view && this._viewInfoMap[Y.stamp(view, true)];
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
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.
261 @param {String} url The URL to navigate to. This must be of the same origin
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()
273 // Does not override `navigate()` but does use extra `options`.
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.
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);
308 // Prevents needless shuffling around of nodes and maintains DOM order.
309 if (!container.contains(viewContainer) && !areSame) {
310 container.appendChild(viewContainer);
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.
335 var app = new Y.App({
338 // Imagine that `Y.UsersView` has been defined.
343 users: new Y.ModelList()
346 app.route('/users/', function () {
347 this.showView('usersView', {users: this.get('users')});
351 app.navigate('/uses/'); // => Creates a new `Y.UsersView` and shows it.
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
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
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`.
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.
389 options = Y.merge(options, {callback: callback});
390 } else if (Lang.isFunction(options)) {
391 options = {callback: options};
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;
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);
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);
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) {
434 } else if (created) {
438 return this._set('activeView', view, {options: options});
441 // -- Protected Methods ----------------------------------------------------
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.
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`.
455 _attachView: function (view, prepend) {
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.
468 viewInfo.instance = view;
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
477 // Insert view into the DOM.
478 viewContainer[prepend ? 'prepend' : 'append'](view.get('container'));
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
487 @see View._destroyContainer()
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.
500 // Clean-up `yui3-app` CSS class on the `container`.
501 container.removeClass(CLASS_NAMES.app);
504 // Clean-up `yui3-app-views` CSS class on the `container`.
505 container.removeClass(CLASS_NAMES.views);
507 // Destroy and purge the `viewContainer`.
508 viewContainer.remove(true);
514 // Remove and purge events from both containers.
516 viewContainer.remove(true);
519 container.remove(true);
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
530 @param {View} view View to detach.
534 _detachView: function (view) {
539 var viewInfo = this.getViewInfo(view) || {};
541 if (viewInfo.preserve) {
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`.
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;
561 view.removeTarget(this);
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`.
571 @param {String} src What initiated the URL change and need for the request.
572 @return {Object} Request object.
574 @see Router._getRequest
576 _getRequest: function () {
577 var req = Router.prototype._getRequest.apply(this, arguments);
583 Getter for the `viewContainer` attribute.
585 @method _getViewContainer
586 @param {Node|null} value Current attribute value.
587 @return {Node} View container node.
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
600 value = this._viewContainer = this.create();
601 this._set('viewContainer', value);
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.
618 @return {Boolean} Whether or not HTML5 history should be used.
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) {
629 // Defaults to whether or not the browser supports HTML5 history.
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.
640 @param {View|String} view The name of a view defined in the `views` object,
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.
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;
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,
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.
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;
686 Underlying implementation for `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.
702 @see PjaxBase._navigate()
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);
712 return PjaxBase.prototype._navigate.call(this, url, options);
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
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.
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.
737 _save: function (url, replace) {
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.');
749 // Either replace the current history entry or create a new one
750 // while navigating to the `url`.
752 // Results in the URL's full path starting with '/'.
753 path = this._joinURL(url || '');
756 win.location.replace(path);
765 return Router.prototype._save.apply(this, arguments);
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
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
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
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.
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);
818 this._attachView(newView, prepend);
819 this._detachView(oldView);
822 callback.call(this, newView);
826 // -- Protected Event Handlers ---------------------------------------------
829 Handles the application's `activeViewChange` event (which is fired when the
830 `activeView` attribute changes) by detaching the old view, attaching the new
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
841 _afterActiveViewChange: function (e) {
842 this._uiSetActiveView(e.newVal, e.prevVal, e.options);
847 The application's active/visible view.
849 This attribute is read-only, to set the `activeView` use the
852 @attribute activeView
856 @see App.Base.showView()
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
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
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
889 @type HTMLElement|Node|String
890 @default Y.one('body')
894 valueFn: function () {
895 return Y.one('body');
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
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
915 valueFn: '_initHtml5'
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
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
928 By default this selector will match _all_ links on the page.
930 @attribute linkSelector
931 @type String|Function
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
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
1010 valueFn : function () { return Y.App.serverRouting; },
1011 writeOnce: 'initOnly'
1015 The node into which this app's `views` will be rendered when they become
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
1034 @attribute viewContainer
1035 @type HTMLElement|Node|String
1036 @default Y.Node.create(this.containerTemplate)
1041 getter : '_getViewContainer',
1048 Properties that shouldn't be turned into ad-hoc attributes when passed to
1051 @property _NON_ATTRS_CFG
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
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});
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`.
1092 @uses App.Transitions
1096 Y.App = Y.mix(Y.Base.create('app', AppBase, []), Y.App, true);
1099 CSS classes used by `Y.App`.
1101 @property CLASS_NAMES
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
1123 }, '3.13.0', {"requires": ["classnamemanager", "pjax-base", "router", "view"]});