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