[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/history-base/ -> history-base.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('history-base', function (Y, NAME) {
   9  
  10  /**
  11   * Provides browser history management functionality using a simple
  12   * add/replace/get paradigm. This can be used to ensure that the browser's back
  13   * and forward buttons work as the user expects and to provide bookmarkable URLs
  14   * that return the user to the current application state, even in an Ajax
  15   * application that doesn't perform full-page refreshes.
  16   *
  17   * @module history
  18   * @main history
  19   * @since 3.2.0
  20   */
  21  
  22  /**
  23   * Provides global state management backed by an object, but with no browser
  24   * history integration. For actual browser history integration and back/forward
  25   * support, use the history-html5 or history-hash modules.
  26   *
  27   * @module history
  28   * @submodule history-base
  29   * @class HistoryBase
  30   * @uses EventTarget
  31   * @constructor
  32   * @param {Object} config (optional) configuration object, which may contain
  33   *   zero or more of the following properties:
  34   *
  35   * <dl>
  36   *   <dt>force (Boolean)</dt>
  37   *   <dd>
  38   *     If `true`, a `history:change` event will be fired whenever the URL
  39   *     changes, even if there is no associated state change. Default is `false`.
  40   *   </dd>
  41   *
  42   *   <dt>initialState (Object)</dt>
  43   *   <dd>
  44   *     Initial state to set, as an object hash of key/value pairs. This will be
  45   *     merged into the current global state.
  46   *   </dd>
  47   * </dl>
  48   */
  49  
  50  var Lang      = Y.Lang,
  51      Obj       = Y.Object,
  52      GlobalEnv = YUI.namespace('Env.History'),
  53      YArray    = Y.Array,
  54  
  55      doc       = Y.config.doc,
  56      docMode   = doc.documentMode,
  57      win       = Y.config.win,
  58  
  59      DEFAULT_OPTIONS = {merge: true},
  60      EVT_CHANGE      = 'change',
  61      SRC_ADD         = 'add',
  62      SRC_REPLACE     = 'replace';
  63  
  64  function HistoryBase() {
  65      this._init.apply(this, arguments);
  66  }
  67  
  68  Y.augment(HistoryBase, Y.EventTarget, null, null, {
  69      emitFacade : true,
  70      prefix     : 'history',
  71      preventable: false,
  72      queueable  : true
  73  });
  74  
  75  if (!GlobalEnv._state) {
  76      GlobalEnv._state = {};
  77  }
  78  
  79  // -- Private Methods ----------------------------------------------------------
  80  
  81  /**
  82   * Returns <code>true</code> if <i>value</i> is a simple object and not a
  83   * function or an array.
  84   *
  85   * @method _isSimpleObject
  86   * @param {mixed} value
  87   * @return {Boolean}
  88   * @private
  89   */
  90  function _isSimpleObject(value) {
  91      return Lang.type(value) === 'object';
  92  }
  93  
  94  // -- Public Static Properties -------------------------------------------------
  95  
  96  /**
  97   * Name of this component.
  98   *
  99   * @property NAME
 100   * @type String
 101   * @static
 102   */
 103  HistoryBase.NAME = 'historyBase';
 104  
 105  /**
 106   * Constant used to identify state changes originating from the
 107   * <code>add()</code> method.
 108   *
 109   * @property SRC_ADD
 110   * @type String
 111   * @static
 112   * @final
 113   */
 114  HistoryBase.SRC_ADD = SRC_ADD;
 115  
 116  /**
 117   * Constant used to identify state changes originating from the
 118   * <code>replace()</code> method.
 119   *
 120   * @property SRC_REPLACE
 121   * @type String
 122   * @static
 123   * @final
 124   */
 125  HistoryBase.SRC_REPLACE = SRC_REPLACE;
 126  
 127  /**
 128   * Whether or not this browser supports the HTML5 History API.
 129   *
 130   * @property html5
 131   * @type Boolean
 132   * @static
 133   */
 134  
 135  // All HTML5-capable browsers except Gecko 2+ (Firefox 4+) correctly return
 136  // true for 'onpopstate' in win. In order to support Gecko 2, we fall back to a
 137  // UA sniff for now. (current as of Firefox 4.0b2)
 138  HistoryBase.html5 = !!(win.history && win.history.pushState &&
 139          win.history.replaceState && ('onpopstate' in win || Y.UA.gecko >= 2) &&
 140          (!Y.UA.android || Y.UA.android >= 2.4));
 141  
 142  /**
 143   * Whether or not this browser supports the <code>window.onhashchange</code>
 144   * event natively. Note that even if this is <code>true</code>, you may
 145   * still want to use HistoryHash's synthetic <code>hashchange</code> event
 146   * since it normalizes implementation differences and fixes spec violations
 147   * across various browsers.
 148   *
 149   * @property nativeHashChange
 150   * @type Boolean
 151   * @static
 152   */
 153  
 154  // Most browsers that support hashchange expose it on the window. Opera 10.6+
 155  // exposes it on the document (but you can still attach to it on the window).
 156  //
 157  // IE8 supports the hashchange event, but only in IE8 Standards
 158  // Mode. However, IE8 in IE7 compatibility mode still defines the
 159  // event but never fires it, so we can't just detect the event. We also can't
 160  // just UA sniff for IE8, since other browsers support this event as well.
 161  HistoryBase.nativeHashChange = ('onhashchange' in win || 'onhashchange' in doc) &&
 162          (!docMode || docMode > 7);
 163  
 164  Y.mix(HistoryBase.prototype, {
 165      // -- Initialization -------------------------------------------------------
 166  
 167      /**
 168       * Initializes this HistoryBase instance. This method is called by the
 169       * constructor.
 170       *
 171       * @method _init
 172       * @param {Object} config configuration object
 173       * @protected
 174       */
 175      _init: function (config) {
 176          var initialState;
 177  
 178          /**
 179           * Configuration object provided by the user on instantiation, or an
 180           * empty object if one wasn't provided.
 181           *
 182           * @property _config
 183           * @type Object
 184           * @default {}
 185           * @protected
 186           */
 187          config = this._config = config || {};
 188  
 189          /**
 190           * If `true`, a `history:change` event will be fired whenever the URL
 191           * changes, even if there is no associated state change.
 192           *
 193           * @property force
 194           * @type Boolean
 195           * @default false
 196           */
 197           this.force = !!config.force;
 198  
 199          /**
 200           * Resolved initial state: a merge of the user-supplied initial state
 201           * (if any) and any initial state provided by a subclass. This may
 202           * differ from <code>_config.initialState</code>. If neither the config
 203           * nor a subclass supplies an initial state, this property will be
 204           * <code>null</code>.
 205           *
 206           * @property _initialState
 207           * @type Object|null
 208           * @default {}
 209           * @protected
 210           */
 211          initialState = this._initialState = this._initialState ||
 212                  config.initialState || null;
 213  
 214          /**
 215           * Fired when the state changes. To be notified of all state changes
 216           * regardless of the History or YUI instance that generated them,
 217           * subscribe to this event on <code>Y.Global</code>. If you would rather
 218           * be notified only about changes generated by this specific History
 219           * instance, subscribe to this event on the instance.
 220           *
 221           * @event history:change
 222           * @param {EventFacade} e Event facade with the following additional
 223           *   properties:
 224           *
 225           * <dl>
 226           *   <dt>changed (Object)</dt>
 227           *   <dd>
 228           *     Object hash of state items that have been added or changed. The
 229           *     key is the item key, and the value is an object containing
 230           *     <code>newVal</code> and <code>prevVal</code> properties
 231           *     representing the values of the item both before and after the
 232           *     change. If the item was newly added, <code>prevVal</code> will be
 233           *     <code>undefined</code>.
 234           *   </dd>
 235           *
 236           *   <dt>newVal (Object)</dt>
 237           *   <dd>
 238           *     Object hash of key/value pairs of all state items after the
 239           *     change.
 240           *   </dd>
 241           *
 242           *   <dt>prevVal (Object)</dt>
 243           *   <dd>
 244           *     Object hash of key/value pairs of all state items before the
 245           *     change.
 246           *   </dd>
 247           *
 248           *   <dt>removed (Object)</dt>
 249           *   <dd>
 250           *     Object hash of key/value pairs of state items that have been
 251           *     removed. Values are the old values prior to removal.
 252           *   </dd>
 253           *
 254           *   <dt>src (String)</dt>
 255           *   <dd>
 256           *     The source of the event. This can be used to selectively ignore
 257           *     events generated by certain sources.
 258           *   </dd>
 259           * </dl>
 260           */
 261          this.publish(EVT_CHANGE, {
 262              broadcast: 2,
 263              defaultFn: this._defChangeFn
 264          });
 265  
 266          // If initialState was provided, merge it into the current state.
 267          if (initialState) {
 268              this.replace(initialState);
 269          }
 270      },
 271  
 272      // -- Public Methods -------------------------------------------------------
 273  
 274      /**
 275       * Adds a state entry with new values for the specified keys. By default,
 276       * the new state will be merged into the existing state, and new values will
 277       * override existing values. Specifying a <code>null</code> or
 278       * <code>undefined</code> value will cause that key to be removed from the
 279       * new state entry.
 280       *
 281       * @method add
 282       * @param {Object} state Object hash of key/value pairs.
 283       * @param {Object} options (optional) Zero or more of the following options:
 284       *   <dl>
 285       *     <dt>merge (Boolean)</dt>
 286       *     <dd>
 287       *       <p>
 288       *       If <code>true</code> (the default), the new state will be merged
 289       *       into the existing state. New values will override existing values,
 290       *       and <code>null</code> or <code>undefined</code> values will be
 291       *       removed from the state.
 292       *       </p>
 293       *
 294       *       <p>
 295       *       If <code>false</code>, the existing state will be discarded as a
 296       *       whole and the new state will take its place.
 297       *       </p>
 298       *     </dd>
 299       *   </dl>
 300       * @chainable
 301       */
 302      add: function () {
 303          var args = YArray(arguments, 0, true);
 304          args.unshift(SRC_ADD);
 305          return this._change.apply(this, args);
 306      },
 307  
 308      /**
 309       * Adds a state entry with a new value for a single key. By default, the new
 310       * value will be merged into the existing state values, and will override an
 311       * existing value with the same key if there is one. Specifying a
 312       * <code>null</code> or <code>undefined</code> value will cause the key to
 313       * be removed from the new state entry.
 314       *
 315       * @method addValue
 316       * @param {String} key State parameter key.
 317       * @param {String} value New value.
 318       * @param {Object} options (optional) Zero or more options. See
 319       *   <code>add()</code> for a list of supported options.
 320       * @chainable
 321       */
 322      addValue: function (key, value, options) {
 323          var state = {};
 324          state[key] = value;
 325          return this._change(SRC_ADD, state, options);
 326      },
 327  
 328      /**
 329       * Returns the current value of the state parameter specified by <i>key</i>,
 330       * or an object hash of key/value pairs for all current state parameters if
 331       * no key is specified.
 332       *
 333       * @method get
 334       * @param {String} key (optional) State parameter key.
 335       * @return {Object|String} Value of the specified state parameter, or an
 336       *   object hash of key/value pairs for all current state parameters.
 337       */
 338      get: function (key) {
 339          var state    = GlobalEnv._state,
 340              isObject = _isSimpleObject(state);
 341  
 342          if (key) {
 343              return isObject && Obj.owns(state, key) ? state[key] : undefined;
 344          } else {
 345              return isObject ? Y.mix({}, state, true) : state; // mix provides a fast shallow clone.
 346          }
 347      },
 348  
 349      /**
 350       * Same as <code>add()</code> except that a new browser history entry will
 351       * not be created. Instead, the current history entry will be replaced with
 352       * the new state.
 353       *
 354       * @method replace
 355       * @param {Object} state Object hash of key/value pairs.
 356       * @param {Object} options (optional) Zero or more options. See
 357       *   <code>add()</code> for a list of supported options.
 358       * @chainable
 359       */
 360      replace: function () {
 361          var args = YArray(arguments, 0, true);
 362          args.unshift(SRC_REPLACE);
 363          return this._change.apply(this, args);
 364      },
 365  
 366      /**
 367       * Same as <code>addValue()</code> except that a new browser history entry
 368       * will not be created. Instead, the current history entry will be replaced
 369       * with the new state.
 370       *
 371       * @method replaceValue
 372       * @param {String} key State parameter key.
 373       * @param {String} value New value.
 374       * @param {Object} options (optional) Zero or more options. See
 375       *   <code>add()</code> for a list of supported options.
 376       * @chainable
 377       */
 378      replaceValue: function (key, value, options) {
 379          var state = {};
 380          state[key] = value;
 381          return this._change(SRC_REPLACE, state, options);
 382      },
 383  
 384      // -- Protected Methods ----------------------------------------------------
 385  
 386      /**
 387       * Changes the state. This method provides a common implementation shared by
 388       * the public methods for changing state.
 389       *
 390       * @method _change
 391       * @param {String} src Source of the change, for inclusion in event facades
 392       *   to facilitate filtering.
 393       * @param {Object} state Object hash of key/value pairs.
 394       * @param {Object} options (optional) Zero or more options. See
 395       *   <code>add()</code> for a list of supported options.
 396       * @protected
 397       * @chainable
 398       */
 399      _change: function (src, state, options) {
 400          options = options ? Y.merge(DEFAULT_OPTIONS, options) : DEFAULT_OPTIONS;
 401  
 402          if (options.merge && _isSimpleObject(state) &&
 403                  _isSimpleObject(GlobalEnv._state)) {
 404              state = Y.merge(GlobalEnv._state, state);
 405          }
 406  
 407          this._resolveChanges(src, state, options);
 408          return this;
 409      },
 410  
 411      /**
 412       * Called by _resolveChanges() when the state has changed. This method takes
 413       * care of actually firing the necessary events.
 414       *
 415       * @method _fireEvents
 416       * @param {String} src Source of the changes, for inclusion in event facades
 417       *   to facilitate filtering.
 418       * @param {Object} changes Resolved changes.
 419       * @param {Object} options Zero or more options. See <code>add()</code> for
 420       *   a list of supported options.
 421       * @protected
 422       */
 423      _fireEvents: function (src, changes, options) {
 424          // Fire the global change event.
 425          this.fire(EVT_CHANGE, {
 426              _options: options,
 427              changed : changes.changed,
 428              newVal  : changes.newState,
 429              prevVal : changes.prevState,
 430              removed : changes.removed,
 431              src     : src
 432          });
 433  
 434          // Fire change/remove events for individual items.
 435          Obj.each(changes.changed, function (value, key) {
 436              this._fireChangeEvent(src, key, value);
 437          }, this);
 438  
 439          Obj.each(changes.removed, function (value, key) {
 440              this._fireRemoveEvent(src, key, value);
 441          }, this);
 442      },
 443  
 444      /**
 445       * Fires a dynamic "[key]Change" event.
 446       *
 447       * @method _fireChangeEvent
 448       * @param {String} src source of the change, for inclusion in event facades
 449       *   to facilitate filtering
 450       * @param {String} key key of the item that was changed
 451       * @param {Object} value object hash containing <i>newVal</i> and
 452       *   <i>prevVal</i> properties for the changed item
 453       * @protected
 454       */
 455      _fireChangeEvent: function (src, key, value) {
 456          /**
 457           * <p>
 458           * Dynamic event fired when an individual history item is added or
 459           * changed. The name of this event depends on the name of the key that
 460           * changed. To listen to change events for a key named "foo", subscribe
 461           * to the <code>fooChange</code> event; for a key named "bar", subscribe
 462           * to <code>barChange</code>, etc.
 463           * </p>
 464           *
 465           * <p>
 466           * Key-specific events are only fired for instance-level changes; that
 467           * is, changes that were made via the same History instance on which the
 468           * event is subscribed. To be notified of changes made by other History
 469           * instances, subscribe to the global <code>history:change</code> event.
 470           * </p>
 471           *
 472           * @event [key]Change
 473           * @param {EventFacade} e Event facade with the following additional
 474           *   properties:
 475           *
 476           * <dl>
 477           *   <dt>newVal (mixed)</dt>
 478           *   <dd>
 479           *     The new value of the item after the change.
 480           *   </dd>
 481           *
 482           *   <dt>prevVal (mixed)</dt>
 483           *   <dd>
 484           *     The previous value of the item before the change, or
 485           *     <code>undefined</code> if the item was just added and has no
 486           *     previous value.
 487           *   </dd>
 488           *
 489           *   <dt>src (String)</dt>
 490           *   <dd>
 491           *     The source of the event. This can be used to selectively ignore
 492           *     events generated by certain sources.
 493           *   </dd>
 494           * </dl>
 495           */
 496          this.fire(key + 'Change', {
 497              newVal : value.newVal,
 498              prevVal: value.prevVal,
 499              src    : src
 500          });
 501      },
 502  
 503      /**
 504       * Fires a dynamic "[key]Remove" event.
 505       *
 506       * @method _fireRemoveEvent
 507       * @param {String} src source of the change, for inclusion in event facades
 508       *   to facilitate filtering
 509       * @param {String} key key of the item that was removed
 510       * @param {mixed} value value of the item prior to its removal
 511       * @protected
 512       */
 513      _fireRemoveEvent: function (src, key, value) {
 514          /**
 515           * <p>
 516           * Dynamic event fired when an individual history item is removed. The
 517           * name of this event depends on the name of the key that was removed.
 518           * To listen to remove events for a key named "foo", subscribe to the
 519           * <code>fooRemove</code> event; for a key named "bar", subscribe to
 520           * <code>barRemove</code>, etc.
 521           * </p>
 522           *
 523           * <p>
 524           * Key-specific events are only fired for instance-level changes; that
 525           * is, changes that were made via the same History instance on which the
 526           * event is subscribed. To be notified of changes made by other History
 527           * instances, subscribe to the global <code>history:change</code> event.
 528           * </p>
 529           *
 530           * @event [key]Remove
 531           * @param {EventFacade} e Event facade with the following additional
 532           *   properties:
 533           *
 534           * <dl>
 535           *   <dt>prevVal (mixed)</dt>
 536           *   <dd>
 537           *     The value of the item before it was removed.
 538           *   </dd>
 539           *
 540           *   <dt>src (String)</dt>
 541           *   <dd>
 542           *     The source of the event. This can be used to selectively ignore
 543           *     events generated by certain sources.
 544           *   </dd>
 545           * </dl>
 546           */
 547          this.fire(key + 'Remove', {
 548              prevVal: value,
 549              src    : src
 550          });
 551      },
 552  
 553      /**
 554       * Resolves the changes (if any) between <i>newState</i> and the current
 555       * state and fires appropriate events if things have changed.
 556       *
 557       * @method _resolveChanges
 558       * @param {String} src source of the changes, for inclusion in event facades
 559       *   to facilitate filtering
 560       * @param {Object} newState object hash of key/value pairs representing the
 561       *   new state
 562       * @param {Object} options Zero or more options. See <code>add()</code> for
 563       *   a list of supported options.
 564       * @protected
 565       */
 566      _resolveChanges: function (src, newState, options) {
 567          var changed   = {},
 568              isChanged,
 569              prevState = GlobalEnv._state,
 570              removed   = {};
 571  
 572          newState || (newState = {});
 573          options  || (options  = {});
 574  
 575          if (_isSimpleObject(newState) && _isSimpleObject(prevState)) {
 576              // Figure out what was added or changed.
 577              Obj.each(newState, function (newVal, key) {
 578                  var prevVal = prevState[key];
 579  
 580                  if (newVal !== prevVal) {
 581                      changed[key] = {
 582                          newVal : newVal,
 583                          prevVal: prevVal
 584                      };
 585  
 586                      isChanged = true;
 587                  }
 588              }, this);
 589  
 590              // Figure out what was removed.
 591              Obj.each(prevState, function (prevVal, key) {
 592                  if (!Obj.owns(newState, key) || newState[key] === null) {
 593                      delete newState[key];
 594                      removed[key] = prevVal;
 595                      isChanged = true;
 596                  }
 597              }, this);
 598          } else {
 599              isChanged = newState !== prevState;
 600          }
 601  
 602          if (isChanged || this.force) {
 603              this._fireEvents(src, {
 604                  changed  : changed,
 605                  newState : newState,
 606                  prevState: prevState,
 607                  removed  : removed
 608              }, options);
 609          }
 610      },
 611  
 612      /**
 613       * Stores the specified state. Don't call this method directly; go through
 614       * _resolveChanges() to ensure that changes are resolved and all events are
 615       * fired properly.
 616       *
 617       * @method _storeState
 618       * @param {String} src source of the changes
 619       * @param {Object} newState new state to store
 620       * @param {Object} options Zero or more options. See <code>add()</code> for
 621       *   a list of supported options.
 622       * @protected
 623       */
 624      _storeState: function (src, newState) {
 625          // Note: the src and options params aren't used here, but they are used
 626          // by subclasses.
 627          GlobalEnv._state = newState || {};
 628      },
 629  
 630      // -- Protected Event Handlers ---------------------------------------------
 631  
 632      /**
 633       * Default <code>history:change</code> event handler.
 634       *
 635       * @method _defChangeFn
 636       * @param {EventFacade} e state change event facade
 637       * @protected
 638       */
 639      _defChangeFn: function (e) {
 640          this._storeState(e.src, e.newVal, e._options);
 641      }
 642  }, true);
 643  
 644  Y.HistoryBase = HistoryBase;
 645  
 646  
 647  }, '3.17.2', {"requires": ["event-custom-complex"]});


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