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
// * [!] Just redo basically everything with view management so there are no
29
// pre-`activeViewChange` side effects and handle the rest of these things:
31
// * Seems like any view created via `createView` should listen for the view's
32
// `destroy` event and use that to remove it from the `_viewsInfoMap`. I
33
// should look at what ModelList does for Models as a reference.
35
// * Should we have a companion `destroyView()` method? Maybe this wouldn't be
36
// needed if we have a `getView(name, create)` method, and already doing the
37
// above? We could do `app.getView('foo').destroy()` and it would be removed
38
// from the `_viewsInfoMap` as well.
40
// * Should we wait to call a view's `render()` method inside of the
41
// `_attachView()` method?
43
// * Should named views support a collection of instances instead of just one?
49
PjaxBase = Y.PjaxBase,
53
getClassName = Y.ClassNameManager.getClassName,
60
Provides a top-level application component which manages navigation and views.
62
This gives you a foundation and structure on which to build your application; it
63
combines robust URL navigation with powerful routing and flexible view
67
@param {Object} [config] The following are configuration properties that can be
68
specified _in addition_ to default attribute values and the non-attribute
69
properties provided by `Y.Base`:
70
@param {Object} [config.views] Hash of view-name to metadata used to
71
declaratively describe an application's views and their relationship with
72
the app and other views. The views specified here will override any defaults
73
provided by the `views` object on the `prototype`.
81
App = Y.Base.create('app', Y.Base, [View, Router, PjaxBase], {
82
// -- Public Properties ----------------------------------------------------
85
Hash of view-name to metadata used to declaratively describe an
86
application's views and their relationship with the app and its other views.
88
The view metadata is composed of Objects keyed to a view-name that can have
89
any or all of the following properties:
91
* `type`: Function or a string representing the view constructor to use to
92
create view instances. If a string is used, the constructor function is
93
assumed to be on the `Y` object; e.g. `"SomeView"` -> `Y.SomeView`.
95
* `preserve`: Boolean for whether the view instance should be retained. By
96
default, the view instance will be destroyed when it is no longer the
97
`activeView`. If `true` the view instance will simply be `removed()`
98
from the DOM when it is no longer active. This is useful when the view
99
is frequently used and may be expensive to re-create.
101
* `parent`: String to another named view in this hash that represents the
102
parent view within the application's view hierarchy; e.g. a `"photo"`
103
view could have `"album"` has its `parent` view. This parent/child
104
relationship is a useful cue for things like transitions.
106
* `instance`: Used internally to manage the current instance of this named
107
view. This can be used if your view instance is created up-front, or if
108
you would rather manage the View lifecycle, but you probably should just
109
let this be handled for you.
111
If `views` are specified at instantiation time, the metadata in the `views`
112
Object here will be used as defaults when creating the instance's `views`.
114
Every `Y.App` instance gets its own copy of a `views` object so this Object
115
on the prototype will not be polluted.
118
// Imagine that `Y.UsersView` and `Y.UserView` have been defined.
119
var app = new Y.App({
140
// -- Protected Properties -------------------------------------------------
143
Map of view instance id (via `Y.stamp()`) to view-info object in `views`.
145
This mapping is used to tie a specific view instance back to its metadata by
146
adding a reference to the the related view info on the `views` object.
148
@property _viewInfoMap
155
// -- Lifecycle Methods ----------------------------------------------------
156
initializer: function (config) {
157
config || (config = {});
161
// Merges-in specified view metadata into local `views` object.
162
function mergeViewConfig(view, name) {
163
views[name] = Y.merge(views[name], view);
166
// First, each view in the `views` prototype object gets its metadata
167
// merged-in, providing the defaults.
168
YObject.each(this.views, mergeViewConfig);
170
// Then, each view in the specified `config.views` object gets its
171
// metadata merged-in.
172
YObject.each(config.views, mergeViewConfig);
174
// The resulting hodgepodge of metadata is then stored as the instance's
175
// `views` object, and no one's objects were harmed in the making.
177
this._viewInfoMap = {};
179
// Using `bind()` to aid extensibility.
180
this.after('activeViewChange', Y.bind('_afterActiveViewChange', this));
182
// PjaxBase will bind click events when `html5` is `true`, so this just
183
// forces the binding when `serverRouting` and `html5` are both falsy.
184
if (!this.get('serverRouting')) {
189
// TODO: `destructor` to destroy the `activeView`?
191
// -- Public Methods -------------------------------------------------------
194
Creates and returns a new view instance using the provided `name` to look up
195
the view info metadata defined in the `views` object. The passed-in `config`
196
object is passed to the view constructor function.
198
This function also maps a view instance back to its view info metadata.
201
@param {String} name The name of a view defined on the `views` object.
202
@param {Object} [config] The configuration object passed to the view
203
constructor function when creating the new view instance.
204
@return {View} The new view instance.
207
createView: function (name, config) {
208
var viewInfo = this.getViewInfo(name),
209
type = (viewInfo && viewInfo.type) || View,
210
ViewConstructor, view;
212
// Looks for a namespaced constructor function on `Y`.
213
ViewConstructor = Lang.isString(type) ?
214
YObject.getValue(Y, type.split('.')) : type;
216
// Create the view instance and map it with its metadata.
217
view = new ViewConstructor(config);
218
this._viewInfoMap[Y.stamp(view, true)] = viewInfo;
224
Returns the metadata associated with a view instance or view name defined on
228
@param {View|String} view View instance, or name of a view defined on the
230
@return {Object} The metadata for the view, or `undefined` if the view is
234
getViewInfo: function (view) {
235
if (Lang.isString(view)) {
236
return this.views[view];
239
return view && this._viewInfoMap[Y.stamp(view, true)];
243
Navigates to the specified URL if there is a route handler that matches. In
244
browsers capable of using HTML5 history or when `serverRouting` is falsy,
245
the navigation will be enhanced by firing the `navigate` event and having
246
the app handle the "request". When `serverRouting` is `true`, non-HTML5
247
browsers will navigate to the new URL via a full page reload.
249
When there is a route handler for the specified URL and it is being
250
navigated to, this method will return `true`, otherwise it will return
253
**Note:** The specified URL _must_ be of the same origin as the current URL,
254
otherwise an error will be logged and navigation will not occur. This is
255
intended as both a security constraint and a purposely imposed limitation as
256
it does not make sense to tell the app to navigate to a URL on a
257
different scheme, host, or port.
260
@param {String} url The URL to navigate to. This must be of the same origin
262
@param {Object} [options] Additional options to configure the navigation.
263
These are mixed into the `navigate` event facade.
264
@param {Boolean} [options.replace] Whether or not the current history
265
entry will be replaced, or a new entry will be created. Will default
266
to `true` if the specified `url` is the same as the current URL.
267
@param {Boolean} [options.force] Whether the enhanced navigation
268
should occur even in browsers without HTML5 history. Will default to
269
`true` when `serverRouting` is falsy.
270
@see PjaxBase.navigate()
272
// Does not override `navigate()` but does use extra `options`.
275
Renders this application by appending the `viewContainer` node to the
276
`container` node if it isn't already a child of the container, and the
277
`activeView` will be appended the view container, if it isn't already.
279
You should call this method at least once, usually after the initialization
280
of your app instance so the proper DOM structure is setup and optionally
281
append the container to the DOM if it's not there already.
283
You may override this method to customize the app's rendering, but you
284
should expect that the `viewContainer`'s contents will be modified by the
285
app for the purpose of rendering the `activeView` when it changes.
291
render: function () {
292
var container = this.get('container'),
293
viewContainer = this.get('viewContainer'),
294
activeView = this.get('activeView'),
295
activeViewContainer = activeView && activeView.get('container'),
296
areSame = container.compareTo(viewContainer);
298
container.addClass(App.CSS_CLASS);
299
viewContainer.addClass(App.VIEWS_CSS_CLASS);
301
// Prevents needless shuffling around of nodes and maintains DOM order.
302
if (activeView && !viewContainer.contains(activeViewContainer)) {
303
viewContainer.appendChild(activeViewContainer);
306
// Prevents needless shuffling around of nodes and maintains DOM order.
307
if (!container.contains(viewContainer) && !areSame) {
308
container.appendChild(viewContainer);
315
Sets which view is active/visible for the application. This will set the
316
app's `activeView` attribute to the specified `view`.
318
The `view` will be "attached" to this app, meaning it will be both rendered
319
into this app's `viewContainer` node and all of its events will bubble to
320
the app. The previous `activeView` will be "detached" from this app.
322
When a string-name is provided for a view which has been registered on this
323
app's `views` object, the referenced metadata will be used and the
324
`activeView` will be set to either a preserved view instance, or a new
325
instance of the registered view will be created using the specified `config`
326
object passed-into this method.
328
A callback function can be specified as either the third or fourth argument,
329
and this function will be called after the new `view` becomes the
330
`activeView`, is rendered to the `viewContainer`, and is ready to use.
333
var app = new Y.App({
336
// Imagine that `Y.UsersView` has been defined.
341
users: new Y.ModelList()
344
app.route('/users/', function () {
345
this.showView('usersView', {users: this.get('users')});
349
app.navigate('/uses/'); // => Creates a new `Y.UsersView` and shows it.
352
@param {String|View} view The name of a view defined in the `views` object,
353
or a view instance which should become this app's `activeView`.
354
@param {Object} [config] Optional configuration to use when creating a new
355
view instance. This config object can also be used to update an existing
356
or preserved view's attributes when `options.update` is `true`.
357
@param {Object} [options] Optional object containing any of the following
359
@param {Function} [options.callback] Optional callback function to call
360
after new `activeView` is ready to use, the function will be passed:
361
@param {View} options.callback.view A reference to the new
363
@param {Boolean} [options.prepend=false] Whether the `view` should be
364
prepended instead of appended to the `viewContainer`.
365
@param {Boolean} [options.render] Whether the `view` should be rendered.
366
**Note:** If no value is specified, a view instance will only be
367
rendered if it's newly created by this method.
368
@param {Boolean} [options.update=false] Whether an existing view should
369
have its attributes updated by passing the `config` object to its
370
`setAttrs()` method. **Note:** This option does not have an effect if
371
the `view` instance is created as a result of calling this method.
372
@param {Function} [callback] Optional callback Function to call after the
373
new `activeView` is ready to use. **Note:** this will override
374
`options.callback` and it can be specified as either the third or fourth
375
argument. The function will be passed the following:
376
@param {View} callback.view A reference to the new `activeView`.
380
showView: function (view, config, options, callback) {
381
var viewInfo, created;
383
options || (options = {});
385
// Support the callback function being either the third or fourth arg.
387
options.callback = callback;
388
} else if (Lang.isFunction(options)) {
389
options = {callback: options};
392
if (Lang.isString(view)) {
393
viewInfo = this.getViewInfo(view);
395
// Use the preserved view instance, or create a new view.
396
// TODO: Maybe we can remove the strict check for `preserve` and
397
// assume we'll use a View instance if it is there, and just check
398
// `preserve` when detaching?
399
if (viewInfo && viewInfo.preserve && viewInfo.instance) {
400
view = viewInfo.instance;
402
// Make sure there's a mapping back to the view metadata.
403
this._viewInfoMap[Y.stamp(view, true)] = viewInfo;
405
// TODO: Add the app as a bubble target during construction, but
406
// make sure to check that it isn't already in `bubbleTargets`!
407
// This will allow the app to be notified for about _all_ of the
408
// view's events. **Note:** This should _only_ happen if the
409
// view is created _after_ `activeViewChange`.
411
view = this.createView(view, config);
416
// Update the specified or preserved `view` when signaled to do so.
417
// There's no need to updated a view if it was _just_ created.
418
if (options.update && !created) {
419
view.setAttrs(config);
422
// TODO: Hold off on rendering the view until after it has been
423
// "attached", and move the call to render into `_attachView()`.
425
// When a value is specified for `options.render`, prefer it because it
426
// represents the developer's intent. When no value is specified, the
427
// `view` will only be rendered if it was just created.
428
if ('render' in options) {
429
options.render && view.render();
430
} else if (created) {
434
return this._set('activeView', view, {options: options});
437
// -- Protected Methods ----------------------------------------------------
440
Helper method to attach the view instance to the application by making the
441
app a bubble target of the view, append the view to the `viewContainer`, and
442
assign it to the `instance` property of the associated view info metadata.
445
@param {View} view View to attach.
446
@param {Boolean} prepend=false Whether the view should be prepended instead
447
of appended to the `viewContainer`.
451
_attachView: function (view, prepend) {
456
var viewInfo = this.getViewInfo(view),
457
viewContainer = this.get('viewContainer');
459
view.addTarget(this);
460
viewInfo && (viewInfo.instance = view);
462
// TODO: Attach events here for persevered Views?
463
// See related TODO in `_detachView`.
465
// TODO: Actually render the view here so that it gets "attached" before
468
// Insert view into the DOM.
469
viewContainer[prepend ? 'prepend' : 'append'](view.get('container'));
473
Overrides View's container destruction to deal with the `viewContainer` and
474
checks to make sure not to remove and purge the `<body>`.
476
@method _destroyContainer
478
@see View._destroyContainer()
480
_destroyContainer: function () {
481
var container = this.get('container'),
482
viewContainer = this.get('viewContainer'),
483
areSame = container.compareTo(viewContainer);
485
// We do not want to remove or destroy the `<body>`.
486
if (Y.one('body').compareTo(container)) {
487
// Just clean-up our events listeners.
490
// Clean-up `yui3-app` CSS class on the `container`.
491
container && container.removeClass(App.CSS_CLASS);
494
// Clean-up `yui3-app-views` CSS class on the `container`.
495
container && container.removeClass(App.VIEWS_CSS_CLASS);
497
// Destroy and purge the `viewContainer`.
498
viewContainer && viewContainer.remove(true);
504
// Remove and purge events from both containers.
505
viewContainer && viewContainer.remove(true);
506
!areSame && container && container.remove(true);
510
Helper method to detach the view instance from the application by removing
511
the application as a bubble target of the view, and either just removing the
512
view if it is intended to be preserved, or destroying the instance
516
@param {View} view View to detach.
520
_detachView: function (view) {
525
var viewInfo = this.getViewInfo(view) || {};
527
if (viewInfo.preserve) {
529
// TODO: Detach events here for preserved Views? It is possible that
530
// some event subscriptions are made on elements other than the
531
// View's `container`.
533
view.destroy({remove: true});
535
// TODO: The following should probably happen automagically from
536
// `destroy()` being called! Possibly `removeTarget()` as well.
538
// Remove from view to view-info map.
539
delete this._viewInfoMap[Y.stamp(view, true)];
541
// Remove from view-info instance property.
542
if (view === viewInfo.instance) {
543
delete viewInfo.instance;
547
view.removeTarget(this);
551
Getter for the `viewContainer` attribute.
553
@method _getViewContainer
554
@param {Node|null} value Current attribute value.
555
@return {Node} View container node.
559
_getViewContainer: function (value) {
560
// This wackiness is necessary to enable fully lazy creation of the
561
// container node both when no container is specified and when one is
562
// specified via a valueFn.
564
if (!value && !this._viewContainer) {
565
// Create a default container and set that as the new attribute
566
// value. The `this._viewContainer` property prevents infinite
568
value = this._viewContainer = this.create();
569
this._set('viewContainer', value);
576
Gets the current full URL. When `html5` is false, the URL will first be
577
upgraded before it's returned.
580
@return {String} URL.
582
@see Router._getURL()
584
_getURL: function () {
585
var url = Y.getLocation().toString();
586
return this._html5 ? url : this._upgradeURL(url);
590
Provides the default value for the `html5` attribute.
592
The value returned is dependent on the value of the `serverRouting`
593
attribute. When `serverRouting` is explicit set to `false` (not just falsy),
594
the default value for `html5` will be set to `false` for *all* browsers.
596
When `serverRouting` is `true` or `undefined` the returned value will be
597
dependent on the browser's capability of using HTML5 history.
600
@return {Boolean} Whether or not HTML5 history should be used.
604
_initHtml5: function () {
605
// When `serverRouting` is explicitly set to `false` (not just falsy),
606
// forcing hash-based URLs in all browsers.
607
if (this.get('serverRouting') === false) {
615
Determines if the specified `view` is configured as a child of the specified
616
`parent` view. This requires both views to be either named-views, or view
617
instances created using configuration data that exists in the `views`
618
object, e.g. created by the `createView()` or `showView()` method.
621
@param {View|String} view The name of a view defined in the `views` object,
623
@param {View|String} parent The name of a view defined in the `views`
624
object, or a view instance.
625
@return {Boolean} Whether the view is configured as a child of the parent.
629
_isChildView: function (view, parent) {
630
var viewInfo = this.getViewInfo(view),
631
parentInfo = this.getViewInfo(parent);
633
if (viewInfo && parentInfo) {
634
return this.getViewInfo(viewInfo.parent) === parentInfo;
641
Determines if the specified `view` is configured as the parent of the
642
specified `child` view. This requires both views to be either named-views,
643
or view instances created using configuration data that exists in the
644
`views` object, e.g. created by the `createView()` or `showView()` method.
646
@method _isParentView
647
@param {View|String} view The name of a view defined in the `views` object,
649
@param {View|String} parent The name of a view defined in the `views`
650
object, or a view instance.
651
@return {Boolean} Whether the view is configured as the parent of the child.
655
_isParentView: function (view, child) {
656
var viewInfo = this.getViewInfo(view),
657
childInfo = this.getViewInfo(child);
659
if (viewInfo && childInfo) {
660
return this.getViewInfo(childInfo.parent) === viewInfo;
667
Underlying implementation for `navigate()`.
670
@param {String} url The fully-resolved URL that the app should dispatch to
671
its route handlers to fulfill the enhanced navigation "request", or use to
672
update `window.location` in non-HTML5 history capable browsers when
673
`serverRouting` is `true`.
674
@param {Object} [options] Additional options to configure the navigation.
675
These are mixed into the `navigate` event facade.
676
@param {Boolean} [options.replace] Whether or not the current history
677
entry will be replaced, or a new entry will be created. Will default
678
to `true` if the specified `url` is the same as the current URL.
679
@param {Boolean} [options.force] Whether the enhanced navigation
680
should occur even in browsers without HTML5 history. Will default to
681
`true` when `serverRouting` is falsy.
683
@see PjaxBase._navigate()
685
_navigate: function (url, options) {
686
url = this._upgradeURL(url);
688
options || (options = {});
690
if (!this.get('serverRouting')) {
691
// Force navigation to be enhanced and handled by the app when
692
// `serverRouting` is falsy because the server might not be able to
693
// properly handle the request.
694
'force' in options || (options.force = true);
697
return PjaxBase.prototype._navigate.call(this, url, options);
701
Will either save a history entry using `pushState()` or the location hash,
702
or gracefully-degrade to sending a request to the server causing a full-page
705
Overrides Router's `_save()` method to preform graceful-degradation when the
706
app's `serverRouting` is `true` and `html5` is `false` by updating the full
707
URL via standard assignment to `window.location` or by calling
708
`window.location.replace()`; both of which will cause a request to the
709
server resulting in a full-page reload.
711
Otherwise this will just delegate off to Router's `_save()` method allowing
712
the client-side enhanced routing to occur.
715
@param {String} [url] URL for the history entry.
716
@param {Boolean} [replace=false] If `true`, the current history entry will
717
be replaced instead of a new one being added.
722
_save: function (url, replace) {
723
// Forces full-path URLs to always be used by modifying
724
// `window.location` in non-HTML5 history capable browsers.
725
if (this.get('serverRouting') && !this.get('html5')) {
726
// Perform same-origin check on the specified URL.
727
if (!this._hasSameOrigin(url)) {
728
Y.error('Security error: The new URL must be of the same origin as the current URL.');
732
// Results in the URL's full path starting with '/'.
733
url = this._joinURL(url || '');
735
// Either replace the current history entry or create a new one
736
// while navigating to the `url`.
738
win && win.location.replace(url);
740
win && (win.location = url);
746
return Router.prototype._save.apply(this, arguments);
750
Performs the actual change of this app's `activeView` by attaching the
751
`newView` to this app, and detaching the `oldView` from this app using any
754
The `newView` is attached to the app by rendering it to the `viewContainer`,
755
and making this app a bubble target of its events.
757
The `oldView` is detached from the app by removing it from the
758
`viewContainer`, and removing this app as a bubble target for its events.
759
The `oldView` will either be preserved or properly destroyed.
761
**Note:** The `activeView` attribute is read-only and can be changed by
762
calling the `showView()` method.
764
@method _uiSetActiveView
765
@param {View} newView The View which is now this app's `activeView`.
766
@param {View} [oldView] The View which was this app's `activeView`.
767
@param {Object} [options] Optional object containing any of the following
769
@param {Function} [options.callback] Optional callback function to call
770
after new `activeView` is ready to use, the function will be passed:
771
@param {View} options.callback.view A reference to the new
773
@param {Boolean} [options.prepend=false] Whether the `view` should be
774
prepended instead of appended to the `viewContainer`.
775
@param {Boolean} [options.render] Whether the `view` should be rendered.
776
**Note:** If no value is specified, a view instance will only be
777
rendered if it's newly created by this method.
778
@param {Boolean} [options.update=false] Whether an existing view should
779
have its attributes updated by passing the `config` object to its
780
`setAttrs()` method. **Note:** This option does not have an effect if
781
the `view` instance is created as a result of calling this method.
785
_uiSetActiveView: function (newView, oldView, options) {
786
options || (options = {});
788
var callback = options.callback,
789
isChild = this._isChildView(newView, oldView),
790
isParent = !isChild && this._isParentView(newView, oldView),
791
prepend = !!options.prepend || isParent;
793
// Prevent detaching (thus removing) the view we want to show. Also hard
794
// to animate out and in, the same view.
795
if (newView === oldView) {
796
return callback && callback.call(this, newView);
799
this._attachView(newView, prepend);
800
this._detachView(oldView);
802
callback && callback.call(this, newView);
806
Upgrades a hash-based URL to a full-path URL, if necessary.
808
The specified `url` will be upgraded if its of the same origin as the
809
current URL and has a path-like hash. URLs that don't need upgrading will be
813
app._upgradeURL('http://example.com/#/foo/'); // => 'http://example.com/foo/';
816
@param {String} url The URL to upgrade from hash-based to full-path.
817
@return {String} The upgraded URL, or the specified URL untouched.
821
_upgradeURL: function (url) {
822
// We should not try to upgrade paths for external URLs.
823
if (!this._hasSameOrigin(url)) {
827
// TODO: Should the `root` be removed first, and the hash only
828
// considered if in the form of '/#/'?
829
var hash = (url.match(/#(.*)$/) || [])[1] || '',
830
hashPrefix = Y.HistoryHash.hashPrefix;
832
// Strip any hash prefix, like hash-bangs.
833
if (hashPrefix && hash.indexOf(hashPrefix) === 0) {
834
hash = hash.replace(hashPrefix, '');
837
// If the hash looks like a URL path, assume it is, and upgrade it!
838
if (hash && hash.charAt(0) === '/') {
839
// Re-join with configured `root` before resolving.
840
url = this._resolveURL(this._joinURL(hash));
846
// -- Protected Event Handlers ---------------------------------------------
849
Handles the application's `activeViewChange` event (which is fired when the
850
`activeView` attribute changes) by detaching the old view, attaching the new
853
The `activeView` attribute is read-only, so the public API to change its
854
value is through the `showView()` method.
856
@method _afterActiveViewChange
857
@param {EventFacade} e
861
_afterActiveViewChange: function (e) {
862
this._uiSetActiveView(e.newVal, e.prevVal, e.options);
867
The application's active/visible view.
869
This attribute is read-only, to set the `activeView` use the
872
@attribute activeView
876
@see App.Base.showView()
885
Container node which represents the application's bounding-box, into
886
which this app's content will be rendered.
888
The container node serves as the host for all DOM events attached by the
889
app. Delegation is used to handle events on children of the container,
890
allowing the container's contents to be re-rendered at any time without
891
losing event subscriptions.
893
The default container is the `<body>` Node, but you can override this in
894
a subclass, or by passing in a custom `container` config value at
897
When `container` is overridden by a subclass or passed as a config
898
option at instantiation time, it may be provided as a selector string, a
899
DOM element, or a `Y.Node` instance. During initialization, this app's
900
`create()` method will be called to convert the container into a
901
`Y.Node` instance if it isn't one already and stamp it with the CSS
904
The container is not added to the page automatically. This allows you to
905
have full control over how and when your app is actually rendered to
909
@type HTMLElement|Node|String
910
@default Y.one('body')
914
valueFn: function () {
915
return Y.one('body');
920
Whether or not this browser is capable of using HTML5 history.
922
This value is dependent on the value of `serverRouting` and will default
925
Setting this to `false` will force the use of hash-based history even on
926
HTML5 browsers, but please don't do this unless you understand the
935
valueFn: '_initHtml5'
939
CSS selector string used to filter link click events so that only the
940
links which match it will have the enhanced-navigation behavior of pjax
943
When a link is clicked and that link matches this selector, navigating
944
to the link's `href` URL using the enhanced, pjax, behavior will be
945
attempted; and the browser's default way to navigate to new pages will
948
By default this selector will match _all_ links on the page.
950
@attribute linkSelector
951
@type String|Function
959
Whether or not this application's server is capable of properly routing
960
all requests and rendering the initial state in the HTML responses.
962
This can have three different values, each having particular
963
implications on how the app will handle routing and navigation:
965
* `undefined`: The best form of URLs will be chosen based on the
966
capabilities of the browser. Given no information about the server
967
environmentm a balanced approach to routing and navigation is
970
The server should be capable of handling full-path requests, since
971
full-URLs will be generated by browsers using HTML5 history. If this
972
is a client-side-only app the server could handle full-URL requests
973
by sending a redirect back to the root with a hash-based URL, e.g:
975
Request: http://example.com/users/1
976
Redirect to: http://example.com/#/users/1
978
* `true`: The server is *fully* capable of properly handling requests
979
to all full-path URLs the app can produce.
981
This is the best option for progressive-enhancement because it will
982
cause **all URLs to always have full-paths**, which means the server
983
will be able to accurately handle all URLs this app produces. e.g.
985
http://example.com/users/1
987
To meet this strict full-URL requirement, browsers which are not
988
capable of using HTML5 history will make requests to the server
989
resulting in full-page reloads.
991
* `false`: The server is *not* capable of properly handling requests
992
to all full-path URLs the app can produce, therefore all routing
993
will be handled by this App instance.
995
Be aware that this will cause **all URLs to always be hash-based**,
996
even in browsers that are capable of using HTML5 history. e.g.
998
http://example.com/#/users/1
1000
A single-page or client-side-only app where the server sends a
1001
"shell" page with JavaScript to the client might have this
1002
restriction. If you're setting this to `false`, read the following:
1004
**Note:** When this is set to `false`, the server will *never* receive
1005
the full URL because browsers do not send the fragment-part to the
1006
server, that is everything after and including the "#".
1008
Consider the following example:
1010
URL shown in browser: http://example.com/#/users/1
1011
URL sent to server: http://example.com/
1013
You should feel bad about hurting our precious web if you forcefully set
1014
either `serverRouting` or `html5` to `false`, because you're basically
1015
punching the web in the face here with your lossy URLs! Please make sure
1016
you know what you're doing and that you understand the implications.
1018
Ideally you should always prefer full-path URLs (not /#/foo/), and want
1019
full-page reloads when the client's browser is not capable of enhancing
1020
the experience using the HTML5 history APIs. Setting this to `true` is
1021
the best option for progressive-enhancement (and graceful-degradation).
1023
@attribute serverRouting
1031
writeOnce: 'initOnly'
1035
The node into which this app's `views` will be rendered when they become
1038
The view container node serves as the container to hold the app's
1039
`activeView`. Each time the `activeView` is set via `showView()`, the
1040
previous view will be removed from this node, and the new active view's
1041
`container` node will be appended.
1043
The default view container is a `<div>` Node, but you can override this
1044
in a subclass, or by passing in a custom `viewContainer` config value at
1045
instantiation time. The `viewContainer` may be provided as a selector
1046
string, DOM element, or a `Y.Node` instance (having the `viewContainer`
1047
and the `container` be the same node is also supported).
1049
The app's `render()` method will stamp the view container with the CSS
1050
class `"yui3-app-views"` and append it to the app's `container` node if
1051
it isn't already, and any `activeView` will be appended to this node if
1054
@attribute viewContainer
1055
@type HTMLElement|Node|String
1056
@default Y.Node.create(this.containerTemplate)
1061
getter : '_getViewContainer',
1067
// TODO: Should these go on the `prototype`?
1068
// TODO: These should also just go in a `CLASS_NAMES` object.
1071
CSS class added to an app's `container` node.
1079
CSS_CLASS: getClassName('app'),
1082
CSS class added to an app's `viewContainer` node.
1084
@property VIEWS_CSS_CLASS
1086
@default "yui3-app-views"
1090
VIEWS_CSS_CLASS: getClassName('app', 'views'),
1093
Properties that shouldn't be turned into ad-hoc attributes when passed to
1096
@property _NON_ATTRS_CFG
1102
_NON_ATTRS_CFG: ['views']
1105
// -- Namespace ----------------------------------------------------------------
1106
Y.namespace('App').Base = App;
1109
Provides a top-level application component which manages navigation and views.
1111
This gives you a foundation and structure on which to build your application; it
1112
combines robust URL navigation with powerful routing and flexible view
1115
`Y.App` is both a namespace and constructor function. The `Y.App` class is
1116
special in that any `Y.App` class extensions that are included in the YUI
1117
instance will be **auto-mixed** on to the `Y.App` class. Consider this example:
1119
YUI().use('app-base', 'app-transitions', function (Y) {
1120
// This will create two YUI Apps, `basicApp` will not have transitions,
1121
// but `fancyApp` will have transitions support included and turn it on.
1122
var basicApp = new Y.App.Base(),
1123
fancyApp = new Y.App({transitions: true});
1127
@param {Object} [config] The following are configuration properties that can be
1128
specified _in addition_ to default attribute values and the non-attribute
1129
properties provided by `Y.Base`:
1130
@param {Object} [config.views] Hash of view-name to metadata used to
1131
declaratively describe an application's views and their relationship with
1132
the app and other views. The views specified here will override any defaults
1133
provided by the `views` object on the `prototype`.
1136
@uses App.Transitions
1139
Y.App = Y.mix(Y.Base.create('app', Y.App.Base, []), Y.App, true);
1142
}, '3.5.1' ,{requires:['classnamemanager', 'pjax-base', 'router', 'view']});