[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 /* 2 YUI 3.17.2 (build 9c3c78e) 3 Copyright 2014 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 Gets a request object that can be passed to a route handler. 566 567 This delegates to `Y.Router`'s `_getRequest()` method and adds a reference 568 to this app instance at `req.app`. 569 570 @method _getRequest 571 @param {String} src What initiated the URL change and need for the request. 572 @return {Object} Request object. 573 @protected 574 @see Router._getRequest 575 **/ 576 _getRequest: function () { 577 var req = Router.prototype._getRequest.apply(this, arguments); 578 req.app = this; 579 return req; 580 }, 581 582 /** 583 Getter for the `viewContainer` attribute. 584 585 @method _getViewContainer 586 @param {Node|null} value Current attribute value. 587 @return {Node} View container node. 588 @protected 589 @since 3.5.0 590 **/ 591 _getViewContainer: function (value) { 592 // This wackiness is necessary to enable fully lazy creation of the 593 // container node both when no container is specified and when one is 594 // specified via a valueFn. 595 596 if (!value && !this._viewContainer) { 597 // Create a default container and set that as the new attribute 598 // value. The `this._viewContainer` property prevents infinite 599 // recursion. 600 value = this._viewContainer = this.create(); 601 this._set('viewContainer', value); 602 } 603 604 return value; 605 }, 606 607 /** 608 Provides the default value for the `html5` attribute. 609 610 The value returned is dependent on the value of the `serverRouting` 611 attribute. When `serverRouting` is explicit set to `false` (not just falsy), 612 the default value for `html5` will be set to `false` for *all* browsers. 613 614 When `serverRouting` is `true` or `undefined` the returned value will be 615 dependent on the browser's capability of using HTML5 history. 616 617 @method _initHtml5 618 @return {Boolean} Whether or not HTML5 history should be used. 619 @protected 620 @since 3.5.0 621 **/ 622 _initHtml5: function () { 623 // When `serverRouting` is explicitly set to `false` (not just falsy), 624 // forcing hash-based URLs in all browsers. 625 if (this.get('serverRouting') === false) { 626 return false; 627 } 628 629 // Defaults to whether or not the browser supports HTML5 history. 630 return Router.html5; 631 }, 632 633 /** 634 Determines if the specified `view` is configured as a child of the specified 635 `parent` view. This requires both views to be either named-views, or view 636 instances created using configuration data that exists in the `views` 637 object, e.g. created by the `createView()` or `showView()` method. 638 639 @method _isChildView 640 @param {View|String} view The name of a view defined in the `views` object, 641 or a view instance. 642 @param {View|String} parent The name of a view defined in the `views` 643 object, or a view instance. 644 @return {Boolean} Whether the view is configured as a child of the parent. 645 @protected 646 @since 3.5.0 647 **/ 648 _isChildView: function (view, parent) { 649 var viewInfo = this.getViewInfo(view), 650 parentInfo = this.getViewInfo(parent); 651 652 if (viewInfo && parentInfo) { 653 return this.getViewInfo(viewInfo.parent) === parentInfo; 654 } 655 656 return false; 657 }, 658 659 /** 660 Determines if the specified `view` is configured as the parent of the 661 specified `child` view. This requires both views to be either named-views, 662 or view instances created using configuration data that exists in the 663 `views` object, e.g. created by the `createView()` or `showView()` method. 664 665 @method _isParentView 666 @param {View|String} view The name of a view defined in the `views` object, 667 or a view instance. 668 @param {View|String} parent The name of a view defined in the `views` 669 object, or a view instance. 670 @return {Boolean} Whether the view is configured as the parent of the child. 671 @protected 672 @since 3.5.0 673 **/ 674 _isParentView: function (view, child) { 675 var viewInfo = this.getViewInfo(view), 676 childInfo = this.getViewInfo(child); 677 678 if (viewInfo && childInfo) { 679 return this.getViewInfo(childInfo.parent) === viewInfo; 680 } 681 682 return false; 683 }, 684 685 /** 686 Underlying implementation for `navigate()`. 687 688 @method _navigate 689 @param {String} url The fully-resolved URL that the app should dispatch to 690 its route handlers to fulfill the enhanced navigation "request", or use to 691 update `window.location` in non-HTML5 history capable browsers when 692 `serverRouting` is `true`. 693 @param {Object} [options] Additional options to configure the navigation. 694 These are mixed into the `navigate` event facade. 695 @param {Boolean} [options.replace] Whether or not the current history 696 entry will be replaced, or a new entry will be created. Will default 697 to `true` if the specified `url` is the same as the current URL. 698 @param {Boolean} [options.force] Whether the enhanced navigation 699 should occur even in browsers without HTML5 history. Will default to 700 `true` when `serverRouting` is falsy. 701 @protected 702 @see PjaxBase._navigate() 703 **/ 704 _navigate: function (url, options) { 705 if (!this.get('serverRouting')) { 706 // Force navigation to be enhanced and handled by the app when 707 // `serverRouting` is falsy because the server might not be able to 708 // properly handle the request. 709 options = Y.merge({force: true}, options); 710 } 711 712 return PjaxBase.prototype._navigate.call(this, url, options); 713 }, 714 715 /** 716 Will either save a history entry using `pushState()` or the location hash, 717 or gracefully-degrade to sending a request to the server causing a full-page 718 reload. 719 720 Overrides Router's `_save()` method to preform graceful-degradation when the 721 app's `serverRouting` is `true` and `html5` is `false` by updating the full 722 URL via standard assignment to `window.location` or by calling 723 `window.location.replace()`; both of which will cause a request to the 724 server resulting in a full-page reload. 725 726 Otherwise this will just delegate off to Router's `_save()` method allowing 727 the client-side enhanced routing to occur. 728 729 @method _save 730 @param {String} [url] URL for the history entry. 731 @param {Boolean} [replace=false] If `true`, the current history entry will 732 be replaced instead of a new one being added. 733 @chainable 734 @protected 735 @see Router._save() 736 **/ 737 _save: function (url, replace) { 738 var path; 739 740 // Forces full-path URLs to always be used by modifying 741 // `window.location` in non-HTML5 history capable browsers. 742 if (this.get('serverRouting') && !this.get('html5')) { 743 // Perform same-origin check on the specified URL. 744 if (!this._hasSameOrigin(url)) { 745 Y.error('Security error: The new URL must be of the same origin as the current URL.'); 746 return this; 747 } 748 749 // Either replace the current history entry or create a new one 750 // while navigating to the `url`. 751 if (win) { 752 // Results in the URL's full path starting with '/'. 753 path = this._joinURL(url || ''); 754 755 if (replace) { 756 win.location.replace(path); 757 } else { 758 win.location = path; 759 } 760 } 761 762 return this; 763 } 764 765 return Router.prototype._save.apply(this, arguments); 766 }, 767 768 /** 769 Performs the actual change of this app's `activeView` by attaching the 770 `newView` to this app, and detaching the `oldView` from this app using any 771 specified `options`. 772 773 The `newView` is attached to the app by rendering it to the `viewContainer`, 774 and making this app a bubble target of its events. 775 776 The `oldView` is detached from the app by removing it from the 777 `viewContainer`, and removing this app as a bubble target for its events. 778 The `oldView` will either be preserved or properly destroyed. 779 780 **Note:** The `activeView` attribute is read-only and can be changed by 781 calling the `showView()` method. 782 783 @method _uiSetActiveView 784 @param {View} newView The View which is now this app's `activeView`. 785 @param {View} [oldView] The View which was this app's `activeView`. 786 @param {Object} [options] Optional object containing any of the following 787 properties: 788 @param {Function} [options.callback] Optional callback function to call 789 after new `activeView` is ready to use, the function will be passed: 790 @param {View} options.callback.view A reference to the new 791 `activeView`. 792 @param {Boolean} [options.prepend=false] Whether the `view` should be 793 prepended instead of appended to the `viewContainer`. 794 @param {Boolean} [options.render] Whether the `view` should be rendered. 795 **Note:** If no value is specified, a view instance will only be 796 rendered if it's newly created by this method. 797 @param {Boolean} [options.update=false] Whether an existing view should 798 have its attributes updated by passing the `config` object to its 799 `setAttrs()` method. **Note:** This option does not have an effect if 800 the `view` instance is created as a result of calling this method. 801 @protected 802 @since 3.5.0 803 **/ 804 _uiSetActiveView: function (newView, oldView, options) { 805 options || (options = {}); 806 807 var callback = options.callback, 808 isChild = this._isChildView(newView, oldView), 809 isParent = !isChild && this._isParentView(newView, oldView), 810 prepend = !!options.prepend || isParent; 811 812 // Prevent detaching (thus removing) the view we want to show. Also hard 813 // to animate out and in, the same view. 814 if (newView === oldView) { 815 return callback && callback.call(this, newView); 816 } 817 818 this._attachView(newView, prepend); 819 this._detachView(oldView); 820 821 if (callback) { 822 callback.call(this, newView); 823 } 824 }, 825 826 // -- Protected Event Handlers --------------------------------------------- 827 828 /** 829 Handles the application's `activeViewChange` event (which is fired when the 830 `activeView` attribute changes) by detaching the old view, attaching the new 831 view. 832 833 The `activeView` attribute is read-only, so the public API to change its 834 value is through the `showView()` method. 835 836 @method _afterActiveViewChange 837 @param {EventFacade} e 838 @protected 839 @since 3.5.0 840 **/ 841 _afterActiveViewChange: function (e) { 842 this._uiSetActiveView(e.newVal, e.prevVal, e.options); 843 } 844 }, { 845 ATTRS: { 846 /** 847 The application's active/visible view. 848 849 This attribute is read-only, to set the `activeView` use the 850 `showView()` method. 851 852 @attribute activeView 853 @type View 854 @default null 855 @readOnly 856 @see App.Base.showView() 857 @since 3.5.0 858 **/ 859 activeView: { 860 value : null, 861 readOnly: true 862 }, 863 864 /** 865 Container node which represents the application's bounding-box, into 866 which this app's content will be rendered. 867 868 The container node serves as the host for all DOM events attached by the 869 app. Delegation is used to handle events on children of the container, 870 allowing the container's contents to be re-rendered at any time without 871 losing event subscriptions. 872 873 The default container is the `<body>` Node, but you can override this in 874 a subclass, or by passing in a custom `container` config value at 875 instantiation time. 876 877 When `container` is overridden by a subclass or passed as a config 878 option at instantiation time, it may be provided as a selector string, a 879 DOM element, or a `Y.Node` instance. During initialization, this app's 880 `create()` method will be called to convert the container into a 881 `Y.Node` instance if it isn't one already and stamp it with the CSS 882 class: `"yui3-app"`. 883 884 The container is not added to the page automatically. This allows you to 885 have full control over how and when your app is actually rendered to 886 the page. 887 888 @attribute container 889 @type HTMLElement|Node|String 890 @default Y.one('body') 891 @initOnly 892 **/ 893 container: { 894 valueFn: function () { 895 return Y.one('body'); 896 } 897 }, 898 899 /** 900 Whether or not this browser is capable of using HTML5 history. 901 902 This value is dependent on the value of `serverRouting` and will default 903 accordingly. 904 905 Setting this to `false` will force the use of hash-based history even on 906 HTML5 browsers, but please don't do this unless you understand the 907 consequences. 908 909 @attribute html5 910 @type Boolean 911 @initOnly 912 @see serverRouting 913 **/ 914 html5: { 915 valueFn: '_initHtml5' 916 }, 917 918 /** 919 CSS selector string used to filter link click events so that only the 920 links which match it will have the enhanced-navigation behavior of pjax 921 applied. 922 923 When a link is clicked and that link matches this selector, navigating 924 to the link's `href` URL using the enhanced, pjax, behavior will be 925 attempted; and the browser's default way to navigate to new pages will 926 be the fallback. 927 928 By default this selector will match _all_ links on the page. 929 930 @attribute linkSelector 931 @type String|Function 932 @default "a" 933 **/ 934 linkSelector: { 935 value: 'a' 936 }, 937 938 /** 939 Whether or not this application's server is capable of properly routing 940 all requests and rendering the initial state in the HTML responses. 941 942 This can have three different values, each having particular 943 implications on how the app will handle routing and navigation: 944 945 * `undefined`: The best form of URLs will be chosen based on the 946 capabilities of the browser. Given no information about the server 947 environmentm a balanced approach to routing and navigation is 948 chosen. 949 950 The server should be capable of handling full-path requests, since 951 full-URLs will be generated by browsers using HTML5 history. If this 952 is a client-side-only app the server could handle full-URL requests 953 by sending a redirect back to the root with a hash-based URL, e.g: 954 955 Request: http://example.com/users/1 956 Redirect to: http://example.com/#/users/1 957 958 * `true`: The server is *fully* capable of properly handling requests 959 to all full-path URLs the app can produce. 960 961 This is the best option for progressive-enhancement because it will 962 cause **all URLs to always have full-paths**, which means the server 963 will be able to accurately handle all URLs this app produces. e.g. 964 965 http://example.com/users/1 966 967 To meet this strict full-URL requirement, browsers which are not 968 capable of using HTML5 history will make requests to the server 969 resulting in full-page reloads. 970 971 * `false`: The server is *not* capable of properly handling requests 972 to all full-path URLs the app can produce, therefore all routing 973 will be handled by this App instance. 974 975 Be aware that this will cause **all URLs to always be hash-based**, 976 even in browsers that are capable of using HTML5 history. e.g. 977 978 http://example.com/#/users/1 979 980 A single-page or client-side-only app where the server sends a 981 "shell" page with JavaScript to the client might have this 982 restriction. If you're setting this to `false`, read the following: 983 984 **Note:** When this is set to `false`, the server will *never* receive 985 the full URL because browsers do not send the fragment-part to the 986 server, that is everything after and including the "#". 987 988 Consider the following example: 989 990 URL shown in browser: http://example.com/#/users/1 991 URL sent to server: http://example.com/ 992 993 You should feel bad about hurting our precious web if you forcefully set 994 either `serverRouting` or `html5` to `false`, because you're basically 995 punching the web in the face here with your lossy URLs! Please make sure 996 you know what you're doing and that you understand the implications. 997 998 Ideally you should always prefer full-path URLs (not /#/foo/), and want 999 full-page reloads when the client's browser is not capable of enhancing 1000 the experience using the HTML5 history APIs. Setting this to `true` is 1001 the best option for progressive-enhancement (and graceful-degradation). 1002 1003 @attribute serverRouting 1004 @type Boolean 1005 @default undefined 1006 @initOnly 1007 @since 3.5.0 1008 **/ 1009 serverRouting: { 1010 valueFn : function () { return Y.App.serverRouting; }, 1011 writeOnce: 'initOnly' 1012 }, 1013 1014 /** 1015 The node into which this app's `views` will be rendered when they become 1016 the `activeView`. 1017 1018 The view container node serves as the container to hold the app's 1019 `activeView`. Each time the `activeView` is set via `showView()`, the 1020 previous view will be removed from this node, and the new active view's 1021 `container` node will be appended. 1022 1023 The default view container is a `<div>` Node, but you can override this 1024 in a subclass, or by passing in a custom `viewContainer` config value at 1025 instantiation time. The `viewContainer` may be provided as a selector 1026 string, DOM element, or a `Y.Node` instance (having the `viewContainer` 1027 and the `container` be the same node is also supported). 1028 1029 The app's `render()` method will stamp the view container with the CSS 1030 class `"yui3-app-views"` and append it to the app's `container` node if 1031 it isn't already, and any `activeView` will be appended to this node if 1032 it isn't already. 1033 1034 @attribute viewContainer 1035 @type HTMLElement|Node|String 1036 @default Y.Node.create(this.containerTemplate) 1037 @initOnly 1038 @since 3.5.0 1039 **/ 1040 viewContainer: { 1041 getter : '_getViewContainer', 1042 setter : Y.one, 1043 writeOnce: true 1044 } 1045 }, 1046 1047 /** 1048 Properties that shouldn't be turned into ad-hoc attributes when passed to 1049 App's constructor. 1050 1051 @property _NON_ATTRS_CFG 1052 @type Array 1053 @static 1054 @protected 1055 @since 3.5.0 1056 **/ 1057 _NON_ATTRS_CFG: ['views'] 1058 }); 1059 1060 // -- Namespace ---------------------------------------------------------------- 1061 Y.namespace('App').Base = AppBase; 1062 1063 /** 1064 Provides a top-level application component which manages navigation and views. 1065 1066 This gives you a foundation and structure on which to build your application; it 1067 combines robust URL navigation with powerful routing and flexible view 1068 management. 1069 1070 `Y.App` is both a namespace and constructor function. The `Y.App` class is 1071 special in that any `Y.App` class extensions that are included in the YUI 1072 instance will be **auto-mixed** on to the `Y.App` class. Consider this example: 1073 1074 YUI().use('app-base', 'app-transitions', function (Y) { 1075 // This will create two YUI Apps, `basicApp` will not have transitions, 1076 // but `fancyApp` will have transitions support included and turn it on. 1077 var basicApp = new Y.App.Base(), 1078 fancyApp = new Y.App({transitions: true}); 1079 }); 1080 1081 @class App 1082 @param {Object} [config] The following are configuration properties that can be 1083 specified _in addition_ to default attribute values and the non-attribute 1084 properties provided by `Y.Base`: 1085 @param {Object} [config.views] Hash of view-name to metadata used to 1086 declaratively describe an application's views and their relationship with 1087 the app and other views. The views specified here will override any defaults 1088 provided by the `views` object on the `prototype`. 1089 @constructor 1090 @extends App.Base 1091 @uses App.Content 1092 @uses App.Transitions 1093 @uses PjaxContent 1094 @since 3.5.0 1095 **/ 1096 Y.App = Y.mix(Y.Base.create('app', AppBase, []), Y.App, true); 1097 1098 /** 1099 CSS classes used by `Y.App`. 1100 1101 @property CLASS_NAMES 1102 @type Object 1103 @default {} 1104 @static 1105 @since 3.6.0 1106 **/ 1107 Y.App.CLASS_NAMES = { 1108 app : getClassName('app'), 1109 views: getClassName('app', 'views') 1110 }; 1111 1112 /** 1113 Default `serverRouting` attribute value for all apps. 1114 1115 @property serverRouting 1116 @type Boolean 1117 @default undefined 1118 @static 1119 @since 3.6.0 1120 **/ 1121 1122 1123 }, '3.17.2', {"requires": ["classnamemanager", "pjax-base", "router", "view"]});
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |