[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/app-base/ -> app-base-debug.js (source)

   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"]});


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1