3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
7 YUI.add('app-base', function(Y) {
10 The App Framework provides simple MVC-like building blocks (models, model lists,
11 views, and URL-based routing) for writing single-page JavaScript applications.
19 Provides a top-level application component which manages navigation and views.
26 // TODO: Better handling of lifecycle for registered views:
28 // * Seems like any view created via `createView` should listen for the view's
29 // `destroy` event and use that to remove it from the `_viewsInfoMap`. I
30 // should look at what ModelList does for Models as a reference.
32 // * Should we have a companion `destroyView()` method? Maybe this wouldn't be
33 // needed if we have a `getView(name, create)` method, and already doing the
34 // above? We could do `app.getView('foo').destroy()` and it would be removed
35 // from the `_viewsInfoMap` as well.
37 // * Should named views support a collection of instances instead of just one?
43 PjaxBase = Y.PjaxBase,
47 getClassName = Y.ClassNameManager.getClassName,
54 Provides a top-level application component which manages navigation and views.
56 This gives you a foundation and structure on which to build your application; it
57 combines robust URL navigation with powerful routing and flexible view
61 @param {Object} [config] The following are configuration properties that can be
62 specified _in addition_ to default attribute values and the non-attribute
63 properties provided by `Y.Base`:
64 @param {Object} [config.views] Hash of view-name to metadata used to
65 declaratively describe an application's views and their relationship with
66 the app and other views. The views specified here will override any defaults
67 provided by the `views` object on the `prototype`.
75 App = Y.Base.create('app', Y.Base, [View, Router, PjaxBase], {
76 // -- Public Properties ----------------------------------------------------
79 Hash of view-name to metadata used to declaratively describe an
80 application's views and their relationship with the app and its other views.
82 The view metadata is composed of Objects keyed to a view-name that can have
83 any or all of the following properties:
85 * `type`: Function or a string representing the view constructor to use to
86 create view instances. If a string is used, the constructor function is
87 assumed to be on the `Y` object; e.g. `"SomeView"` -> `Y.SomeView`.
89 * `preserve`: Boolean for whether the view instance should be retained. By
90 default, the view instance will be destroyed when it is no longer the
91 `activeView`. If `true` the view instance will simply be `removed()`
92 from the DOM when it is no longer active. This is useful when the view
93 is frequently used and may be expensive to re-create.
95 * `parent`: String to another named view in this hash that represents the
96 parent view within the application's view hierarchy; e.g. a `"photo"`
97 view could have `"album"` has its `parent` view. This parent/child
98 relationship is a useful cue for things like transitions.
100 * `instance`: Used internally to manage the current instance of this named
101 view. This can be used if your view instance is created up-front, or if
102 you would rather manage the View lifecycle, but you probably should just
103 let this be handled for you.
105 If `views` are specified at instantiation time, the metadata in the `views`
106 Object here will be used as defaults when creating the instance's `views`.
108 Every `Y.App` instance gets its own copy of a `views` object so this Object
109 on the prototype will not be polluted.
112 // Imagine that `Y.UsersView` and `Y.UserView` have been defined.
113 var app = new Y.App({
134 // -- Protected Properties -------------------------------------------------
137 Map of view instance id (via `Y.stamp()`) to view-info object in `views`.
139 This mapping is used to tie a specific view instance back to its metadata by
140 adding a reference to the the related view info on the `views` object.
142 @property _viewInfoMap
149 // -- Lifecycle Methods ----------------------------------------------------
150 initializer: function (config) {
151 config || (config = {});
155 // Merges-in specified view metadata into local `views` object.
156 function mergeViewConfig(view, name) {
157 views[name] = Y.merge(views[name], view);
160 // First, each view in the `views` prototype object gets its metadata
161 // merged-in, providing the defaults.
162 YObject.each(this.views, mergeViewConfig);
164 // Then, each view in the specified `config.views` object gets its
165 // metadata merged-in.
166 YObject.each(config.views, mergeViewConfig);
168 // The resulting hodgepodge of metadata is then stored as the instance's
169 // `views` object, and no one's objects were harmed in the making.
171 this._viewInfoMap = {};
173 // Using `bind()` to aid extensibility.
174 this.after('activeViewChange', Y.bind('_afterActiveViewChange', this));
176 // PjaxBase will bind click events when `html5` is `true`, so this just
177 // forces the binding when `serverRouting` and `html5` are both falsy.
178 if (!this.get('serverRouting')) {
183 // TODO: `destructor` to destroy the `activeView`?
185 // -- Public Methods -------------------------------------------------------
188 Creates and returns a new view instance using the provided `name` to look up
189 the view info metadata defined in the `views` object. The passed-in `config`
190 object is passed to the view constructor function.
192 This function also maps a view instance back to its view info metadata.
195 @param {String} name The name of a view defined on the `views` object.
196 @param {Object} [config] The configuration object passed to the view
197 constructor function when creating the new view instance.
198 @return {View} The new view instance.
201 createView: function (name, config) {
202 var viewInfo = this.getViewInfo(name),
203 type = (viewInfo && viewInfo.type) || View,
204 ViewConstructor, view;
206 // Looks for a namespaced constructor function on `Y`.
207 ViewConstructor = Lang.isString(type) ?
208 YObject.getValue(Y, type.split('.')) : type;
210 // Create the view instance and map it with its metadata.
211 view = new ViewConstructor(config);
212 this._viewInfoMap[Y.stamp(view, true)] = viewInfo;
218 Returns the metadata associated with a view instance or view name defined on
222 @param {View|String} view View instance, or name of a view defined on the
224 @return {Object} The metadata for the view, or `undefined` if the view is
228 getViewInfo: function (view) {
229 if (Lang.isString(view)) {
230 return this.views[view];
233 return view && this._viewInfoMap[Y.stamp(view, true)];
237 Navigates to the specified URL if there is a route handler that matches. In
238 browsers capable of using HTML5 history or when `serverRouting` is falsy,
239 the navigation will be enhanced by firing the `navigate` event and having
240 the app handle the "request". When `serverRouting` is `true`, non-HTML5
241 browsers will navigate to the new URL via a full page reload.
243 When there is a route handler for the specified URL and it is being
244 navigated to, this method will return `true`, otherwise it will return
247 **Note:** The specified URL _must_ be of the same origin as the current URL,
248 otherwise an error will be logged and navigation will not occur. This is
249 intended as both a security constraint and a purposely imposed limitation as
250 it does not make sense to tell the app to navigate to a URL on a
251 different scheme, host, or port.
254 @param {String} url The URL to navigate to. This must be of the same origin
256 @param {Object} [options] Additional options to configure the navigation.
257 These are mixed into the `navigate` event facade.
258 @param {Boolean} [options.replace] Whether or not the current history
259 entry will be replaced, or a new entry will be created. Will default
260 to `true` if the specified `url` is the same as the current URL.
261 @param {Boolean} [options.force] Whether the enhanced navigation
262 should occur even in browsers without HTML5 history. Will default to
263 `true` when `serverRouting` is falsy.
264 @see PjaxBase.navigate()
266 // Does not override `navigate()` but does use extra `options`.
269 Renders this application by appending the `viewContainer` node to the
270 `container` node if it isn't already a child of the container, and the
271 `activeView` will be appended the view container, if it isn't already.
273 You should call this method at least once, usually after the initialization
274 of your app instance so the proper DOM structure is setup and optionally
275 append the container to the DOM if it's not there already.
277 You may override this method to customize the app's rendering, but you
278 should expect that the `viewContainer`'s contents will be modified by the
279 app for the purpose of rendering the `activeView` when it changes.
285 render: function () {
286 var container = this.get('container'),
287 viewContainer = this.get('viewContainer'),
288 activeView = this.get('activeView'),
289 activeViewContainer = activeView && activeView.get('container'),
290 areSame = container.compareTo(viewContainer);
292 container.addClass(App.CSS_CLASS);
293 viewContainer.addClass(App.VIEWS_CSS_CLASS);
295 // Prevents needless shuffling around of nodes and maintains DOM order.
296 if (activeView && !viewContainer.contains(activeViewContainer)) {
297 viewContainer.appendChild(activeViewContainer);
300 // Prevents needless shuffling around of nodes and maintains DOM order.
301 if (!container.contains(viewContainer) && !areSame) {
302 container.appendChild(viewContainer);
309 Sets which view is active/visible for the application. This will set the
310 app's `activeView` attribute to the specified `view`.
312 When a string-name is provided for a view which has been registered on this
313 app's `views` object, the referenced metadata will be used and the
314 `activeView` will be set to either a preserved view instance, or a new
315 instance of the registered view will be created using the specified `config`
316 object passed-into this method.
318 A callback function can be specified as either the third or fourth argument,
319 and this function will be called after the new `view` becomes the
320 `activeView`, is rendered to the `viewContainer`, and is ready to use.
323 var app = new Y.App({
326 // Imagine that `Y.UsersView` has been defined.
332 app.route('/users/', function () {
333 this.showView('users');
337 app.navigate('/uses/'); // => Creates a new `Y.UsersView` and shows it.
340 @param {String|View} view The name of a view defined in the `views` object,
342 @param {Object} [config] Optional configuration to use when creating a new
344 @param {Object} [options] Optional object containing any of the following
346 @param {Function} [options.callback] Optional callback function to call
347 after new `activeView` is ready to use, the function will be passed:
348 @param {View} options.callback.view A reference to the new
350 @param {Boolean} [options.prepend] Whether the new view should be
351 prepended instead of appended to the `viewContainer`.
352 @param {Function} [callback] Optional callback Function to call after the
353 new `activeView` is ready to use. **Note:** this will override
354 `options.callback`. The function will be passed the following:
355 @param {View} callback.view A reference to the new `activeView`.
359 showView: function (view, config, options, callback) {
362 if (Lang.isString(view)) {
363 viewInfo = this.getViewInfo(view);
365 // Use the preserved view instance, or create a new view.
366 // TODO: Maybe we can remove the strict check for `preserve` and
367 // assume we'll use a View instance if it is there, and just check
368 // `preserve` when detaching?
369 if (viewInfo && viewInfo.preserve && viewInfo.instance) {
370 view = viewInfo.instance;
371 // Make sure there's a mapping back to the view metadata.
372 this._viewInfoMap[Y.stamp(view, true)] = viewInfo;
374 view = this.createView(view, config);
379 // TODO: Add `options.update` to update to view with the `config`, if
380 // needed. This could also call `setAttrs()` when the specified `view`
381 // already a View instance. Is this be too much overloading of the API?
383 // TODO: Add `options.render` to provide a way to control whether a view
384 // is rendered or not; by default, `render()` will only be called if
385 // this method created the View.
387 options || (options = {});
390 options.callback = callback;
391 } else if (Lang.isFunction(options)) {
392 options = {callback: options};
395 // TODO: Should the `callback` _always_ be called, even when the
396 // `activeView` does not change?
398 return this._set('activeView', view, {options: options});
401 // -- Protected Methods ----------------------------------------------------
404 Helper method to attach the view instance to the application by making the
405 app a bubble target of the view, append the view to the `viewContainer`, and
406 assign it to the `instance` property of the associated view info metadata.
409 @param {View} view View to attach.
410 @param {Boolean} prepend Whether the view should be prepended instead of
411 appended to the `viewContainer`.
415 _attachView: function (view, prepend) {
420 var viewInfo = this.getViewInfo(view),
421 viewContainer = this.get('viewContainer');
423 view.addTarget(this);
424 viewInfo && (viewInfo.instance = view);
426 // TODO: Attach events here for persevered Views?
427 // See related TODO in `_detachView`.
429 // Insert view into the DOM.
430 viewContainer[prepend ? 'prepend' : 'append'](view.get('container'));
434 Overrides View's container destruction to deal with the `viewContainer` and
435 checks to make sure not to remove and purge the `<body>`.
437 @method _destroyContainer
439 @see View._destroyContainer()
441 _destroyContainer: function () {
442 var container = this.get('container'),
443 viewContainer = this.get('viewContainer'),
444 areSame = container.compareTo(viewContainer);
446 // We do not want to remove or destroy the `<body>`.
447 if (Y.one('body').compareTo(container)) {
448 // Just clean-up our events listeners.
451 // Clean-up `yui3-app` CSS class on the `container`.
452 container && container.removeClass(App.CSS_CLASS);
455 // Clean-up `yui3-app-views` CSS class on the `container`.
456 container && container.removeClass(App.VIEWS_CSS_CLASS);
458 // Destroy and purge the `viewContainer`.
459 viewContainer && viewContainer.remove(true);
465 // Remove and purge events from both containers.
466 viewContainer && viewContainer.remove(true);
467 !areSame && container && container.remove(true);
471 Helper method to detach the view instance from the application by removing
472 the application as a bubble target of the view, and either just removing the
473 view if it is intended to be preserved, or destroying the instance
477 @param {View} view View to detach.
481 _detachView: function (view) {
486 var viewInfo = this.getViewInfo(view) || {};
488 if (viewInfo.preserve) {
490 // TODO: Detach events here for preserved Views? It is possible that
491 // some event subscriptions are made on elements other than the
492 // View's `container`.
494 view.destroy({remove: true});
496 // TODO: The following should probably happen automagically from
497 // `destroy()` being called! Possibly `removeTarget()` as well.
499 // Remove from view to view-info map.
500 delete this._viewInfoMap[Y.stamp(view, true)];
502 // Remove from view-info instance property.
503 if (view === viewInfo.instance) {
504 delete viewInfo.instance;
508 view.removeTarget(this);
512 Getter for the `viewContainer` attribute.
514 @method _getViewContainer
515 @param {Node|null} value Current attribute value.
516 @return {Node} View container node.
520 _getViewContainer: function (value) {
521 // This wackiness is necessary to enable fully lazy creation of the
522 // container node both when no container is specified and when one is
523 // specified via a valueFn.
525 if (!value && !this._viewContainer) {
526 // Create a default container and set that as the new attribute
527 // value. The `this._viewContainer` property prevents infinite
529 value = this._viewContainer = this.create();
530 this._set('viewContainer', value);
537 Gets the current full URL. When `html5` is false, the URL will first be
538 upgraded before it's returned.
541 @return {String} URL.
543 @see Router._getURL()
545 _getURL: function () {
546 var url = Y.getLocation().toString();
547 return this._html5 ? url : this._upgradeURL(url);
551 Provides the default value for the `html5` attribute.
553 The value returned is dependent on the value of the `serverRouting`
554 attribute. When `serverRouting` is explicit set to `false` (not just falsy),
555 the default value for `html5` will be set to `false` for *all* browsers.
557 When `serverRouting` is `true` or `undefined` the returned value will be
558 dependent on the browser's capability of using HTML5 history.
561 @return {Boolean} Whether or not HTML5 history should be used.
565 _initHtml5: function () {
566 // When `serverRouting` is explicitly set to `false` (not just falsy),
567 // forcing hash-based URLs in all browsers.
568 if (this.get('serverRouting') === false) {
576 Determines if the specified `view` is configured as a child of the specified
577 `parent` view. This requires both views to be either named-views, or view
578 instances created using configuration data that exists in the `views`
579 object, e.g. created by the `createView()` or `showView()` method.
582 @param {View|String} view The name of a view defined in the `views` object,
584 @param {View|String} parent The name of a view defined in the `views`
585 object, or a view instance.
586 @return {Boolean} Whether the view is configured as a child of the parent.
590 _isChildView: function (view, parent) {
591 var viewInfo = this.getViewInfo(view),
592 parentInfo = this.getViewInfo(parent);
594 if (viewInfo && parentInfo) {
595 return this.getViewInfo(viewInfo.parent) === parentInfo;
602 Determines if the specified `view` is configured as the parent of the
603 specified `child` view. This requires both views to be either named-views,
604 or view instances created using configuration data that exists in the
605 `views` object, e.g. created by the `createView()` or `showView()` method.
607 @method _isParentView
608 @param {View|String} view The name of a view defined in the `views` object,
610 @param {View|String} parent The name of a view defined in the `views`
611 object, or a view instance.
612 @return {Boolean} Whether the view is configured as the parent of the child.
616 _isParentView: function (view, child) {
617 var viewInfo = this.getViewInfo(view),
618 childInfo = this.getViewInfo(child);
620 if (viewInfo && childInfo) {
621 return this.getViewInfo(childInfo.parent) === viewInfo;
628 Underlying implementation for `navigate()`.
631 @param {String} url The fully-resolved URL that the app should dispatch to
632 its route handlers to fulfill the enhanced navigation "request", or use to
633 update `window.location` in non-HTML5 history capable browsers when
634 `serverRouting` is `true`.
635 @param {Object} [options] Additional options to configure the navigation.
636 These are mixed into the `navigate` event facade.
637 @param {Boolean} [options.replace] Whether or not the current history
638 entry will be replaced, or a new entry will be created. Will default
639 to `true` if the specified `url` is the same as the current URL.
640 @param {Boolean} [options.force] Whether the enhanced navigation
641 should occur even in browsers without HTML5 history. Will default to
642 `true` when `serverRouting` is falsy.
644 @see PjaxBase._navigate()
646 _navigate: function (url, options) {
647 url = this._upgradeURL(url);
649 options || (options = {});
651 if (!this.get('serverRouting')) {
652 // Force navigation to be enhanced and handled by the app when
653 // `serverRouting` is falsy because the server might not be able to
654 // properly handle the request.
655 Lang.isValue(options.force) || (options.force = true);
658 return PjaxBase.prototype._navigate.call(this, url, options);
662 Will either save a history entry using `pushState()` or the location hash,
663 or gracefully-degrade to sending a request to the server causing a full-page
666 Overrides Router's `_save()` method to preform graceful-degradation when the
667 app's `serverRouting` is `true` and `html5` is `false` by updating the full
668 URL via standard assignment to `window.location` or by calling
669 `window.location.replace()`; both of which will cause a request to the
670 server resulting in a full-page reload.
672 Otherwise this will just delegate off to Router's `_save()` method allowing
673 the client-side enhanced routing to occur.
676 @param {String} [url] URL for the history entry.
677 @param {Boolean} [replace=false] If `true`, the current history entry will
678 be replaced instead of a new one being added.
683 _save: function (url, replace) {
684 // Forces full-path URLs to always be used by modifying
685 // `window.location` in non-HTML5 history capable browsers.
686 if (this.get('serverRouting') && !this.get('html5')) {
687 // Perform same-origin check on the specified URL.
688 if (!this._hasSameOrigin(url)) {
689 Y.error('Security error: The new URL must be of the same origin as the current URL.');
693 // Results in the URL's full path starting with '/'.
694 url = this._joinURL(url || '');
696 // Either replace the current history entry or create a new one
697 // while navigating to the `url`.
699 win && win.location.replace(url);
701 win && (win.location = url);
707 return Router.prototype._save.apply(this, arguments);
711 Performs the actual change of this app's `activeView` by attaching the
712 `newView` to this app, and detaching the `oldView` from this app using any
715 The `newView` is attached to the app by rendering it to the `viewContainer`,
716 and making this app a bubble target of its events.
718 The `oldView` is detached from the app by removing it from the
719 `viewContainer`, and removing this app as a bubble target for its events.
720 The `oldView` will either be preserved or properly destroyed.
722 **Note:** The `activeView` attribute is read-only and can be changed by
723 calling the `showView()` method.
725 @method _uiSetActiveView
726 @param {View} newView The View which is now this app's `activeView`.
727 @param {View} [oldView] The View which was this app's `activeView`.
728 @param {Object} [options] Optional object containing any of the following
730 @param {Function} [options.callback] Optional callback function to call
731 after new `activeView` is ready to use, the function will be passed:
732 @param {View} options.callback.view A reference to the new
734 @param {Boolean} [options.prepend] Whether the new view should be
735 prepended instead of appended to the `viewContainer`.
739 _uiSetActiveView: function (newView, oldView, options) {
740 options || (options = {});
742 var callback = options.callback,
743 isChild = this._isChildView(newView, oldView),
744 isParent = !isChild && this._isParentView(newView, oldView),
745 prepend = !!options.prepend || isParent;
747 // Prevent detaching (thus removing) the view we want to show. Also hard
748 // to animate out and in, the same view.
749 if (newView === oldView) {
750 return callback && callback.call(this, newView);
753 this._attachView(newView, prepend);
754 this._detachView(oldView);
756 callback && callback.call(this, newView);
760 Upgrades a hash-based URL to a full-path URL, if necessary.
762 The specified `url` will be upgraded if its of the same origin as the
763 current URL and has a path-like hash. URLs that don't need upgrading will be
767 app._upgradeURL('http://example.com/#/foo/'); // => 'http://example.com/foo/';
770 @param {String} url The URL to upgrade from hash-based to full-path.
771 @return {String} The upgraded URL, or the specified URL untouched.
775 _upgradeURL: function (url) {
776 // We should not try to upgrade paths for external URLs.
777 if (!this._hasSameOrigin(url)) {
781 // TODO: Should the `root` be removed first, and the hash only
782 // considered if in the form of '/#/'?
783 var hash = (url.match(/#(.*)$/) || [])[1] || '',
784 hashPrefix = Y.HistoryHash.hashPrefix;
786 // Strip any hash prefix, like hash-bangs.
787 if (hashPrefix && hash.indexOf(hashPrefix) === 0) {
788 hash = hash.replace(hashPrefix, '');
791 // If the hash looks like a URL path, assume it is, and upgrade it!
792 if (hash && hash.charAt(0) === '/') {
793 // Re-join with configured `root` before resolving.
794 url = this._resolveURL(this._joinURL(hash));
800 // -- Protected Event Handlers ---------------------------------------------
803 Handles the application's `activeViewChange` event (which is fired when the
804 `activeView` attribute changes) by detaching the old view, attaching the new
807 The `activeView` attribute is read-only, so the public API to change its
808 value is through the `showView()` method.
810 @method _afterActiveViewChange
811 @param {EventFacade} e
815 _afterActiveViewChange: function (e) {
816 this._uiSetActiveView(e.newVal, e.prevVal, e.options);
821 The application's active/visible view.
823 This attribute is read-only, to set the `activeView` use the
826 @attribute activeView
830 @see AppBase.showView()
839 Container node which represents the application's bounding-box, into
840 which this app's content will be rendered.
842 The container node serves as the host for all DOM events attached by the
843 app. Delegation is used to handle events on children of the container,
844 allowing the container's contents to be re-rendered at any time without
845 losing event subscriptions.
847 The default container is the `<body>` Node, but you can override this in
848 a subclass, or by passing in a custom `container` config value at
851 When `container` is overridden by a subclass or passed as a config
852 option at instantiation time, it may be provided as a selector string, a
853 DOM element, or a `Y.Node` instance. During initialization, this app's
854 `create()` method will be called to convert the container into a
855 `Y.Node` instance if it isn't one already and stamp it with the CSS
858 The container is not added to the page automatically. This allows you to
859 have full control over how and when your app is actually rendered to
863 @type HTMLElement|Node|String
864 @default Y.one('body')
868 valueFn: function () {
869 return Y.one('body');
874 Whether or not this browser is capable of using HTML5 history.
876 This value is dependent on the value of `serverRouting` and will default
879 Setting this to `false` will force the use of hash-based history even on
880 HTML5 browsers, but please don't do this unless you understand the
889 valueFn: '_initHtml5'
893 CSS selector string used to filter link click events so that only the
894 links which match it will have the enhanced-navigation behavior of pjax
897 When a link is clicked and that link matches this selector, navigating
898 to the link's `href` URL using the enhanced, pjax, behavior will be
899 attempted; and the browser's default way to navigate to new pages will
902 By default this selector will match _all_ links on the page.
904 @attribute linkSelector
905 @type String|Function
913 Whether or not this application's server is capable of properly routing
914 all requests and rendering the initial state in the HTML responses.
916 This can have three different values, each having particular
917 implications on how the app will handle routing and navigation:
919 * `undefined`: The best form of URLs will be chosen based on the
920 capabilities of the browser. Given no information about the server
921 environmentm a balanced approach to routing and navigation is
924 The server should be capable of handling full-path requests, since
925 full-URLs will be generated by browsers using HTML5 history. If this
926 is a client-side-only app the server could handle full-URL requests
927 by sending a redirect back to the root with a hash-based URL, e.g:
929 Request: http://example.com/users/1
930 Redirect to: http://example.com/#/users/1
932 * `true`: The server is *fully* capable of properly handling requests
933 to all full-path URLs the app can produce.
935 This is the best option for progressive-enhancement because it will
936 cause **all URLs to always have full-paths**, which means the server
937 will be able to accurately handle all URLs this app produces. e.g.
939 http://example.com/users/1
941 To meet this strict full-URL requirement, browsers which are not
942 capable of using HTML5 history will make requests to the server
943 resulting in full-page reloads.
945 * `false`: The server is *not* capable of properly handling requests
946 to all full-path URLs the app can produce, therefore all routing
947 will be handled by this App instance.
949 Be aware that this will cause **all URLs to always be hash-based**,
950 even in browsers that are capable of using HTML5 history. e.g.
952 http://example.com/#/users/1
954 A single-page or client-side-only app where the server sends a
955 "shell" page with JavaScript to the client might have this
956 restriction. If you're setting this to `false`, read the following:
958 **Note:** When this is set to `false`, the server will *never* receive
959 the full URL because browsers do not send the fragment-part to the
960 server, that is everything after and including the "#".
962 Consider the following example:
964 URL shown in browser: http://example.com/#/users/1
965 URL sent to server: http://example.com/
967 You should feel bad about hurting our precious web if you forcefully set
968 either `serverRouting` or `html5` to `false`, because you're basically
969 punching the web in the face here with your lossy URLs! Please make sure
970 you know what you're doing and that you understand the implications.
972 Ideally you should always prefer full-path URLs (not /#/foo/), and want
973 full-page reloads when the client's browser is not capable of enhancing
974 the experience using the HTML5 history APIs. Setting this to `true` is
975 the best option for progressive-enhancement (and graceful-degradation).
977 @attribute serverRouting
985 writeOnce: 'initOnly'
989 The node into which this app's `views` will be rendered when they become
992 The view container node serves as the container to hold the app's
993 `activeView`. Each time the `activeView` is set via `showView()`, the
994 previous view will be removed from this node, and the new active view's
995 `container` node will be appended.
997 The default view container is a `<div>` Node, but you can override this
998 in a subclass, or by passing in a custom `viewContainer` config value at
999 instantiation time. The `viewContainer` may be provided as a selector
1000 string, DOM element, or a `Y.Node` instance (having the `viewContainer`
1001 and the `container` be the same node is also supported).
1003 The app's `render()` method will stamp the view container with the CSS
1004 class `"yui3-app-views"` and append it to the app's `container` node if
1005 it isn't already, and any `activeView` will be appended to this node if
1008 @attribute viewContainer
1009 @type HTMLElement|Node|String
1010 @default Y.Node.create(this.containerTemplate)
1015 getter : '_getViewContainer',
1021 // TODO: Should these go on the `prototype`?
1022 // TODO: These should also just go in a `CLASS_NAMES` object.
1025 CSS class added to an app's `container` node.
1033 CSS_CLASS: getClassName('app'),
1036 CSS class added to an app's `viewContainer` node.
1038 @property VIEWS_CSS_CLASS
1040 @default "yui3-app-views"
1044 VIEWS_CSS_CLASS: getClassName('app', 'views'),
1047 Properties that shouldn't be turned into ad-hoc attributes when passed to
1050 @property _NON_ATTRS_CFG
1056 _NON_ATTRS_CFG: ['views']
1059 // -- Namespace ----------------------------------------------------------------
1060 Y.namespace('App').Base = App;
1063 Provides a top-level application component which manages navigation and views.
1065 This gives you a foundation and structure on which to build your application; it
1066 combines robust URL navigation with powerful routing and flexible view
1069 `Y.App` is both a namespace and constructor function. The `Y.App` class is
1070 special in that any `Y.App` class extensions that are included in the YUI
1071 instance will be **auto-mixed** on to the `Y.App` class. Consider this example:
1073 YUI().use('app-base', 'app-transitions', function (Y) {
1074 // This will create two YUI Apps, `basicApp` will not have transitions,
1075 // but `fancyApp` will have transitions support included and turn it on.
1076 var basicApp = new Y.App.Base(),
1077 fancyApp = new Y.App({transitions: true});
1081 @param {Object} [config] The following are configuration properties that can be
1082 specified _in addition_ to default attribute values and the non-attribute
1083 properties provided by `Y.Base`:
1084 @param {Object} [config.views] Hash of view-name to metadata used to
1085 declaratively describe an application's views and their relationship with
1086 the app and other views. The views specified here will override any defaults
1087 provided by the `views` object on the `prototype`.
1090 @uses App.Transitions
1093 Y.App = Y.mix(Y.Base.create('app', Y.App.Base, []), Y.App, true);
1096 }, '3.5.0' ,{requires:['classnamemanager', 'pjax-base', 'router', 'view']});