[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/webroot/rsrc/externals/javelin/lib/ -> History.js (source)

   1  /**
   2   * @requires javelin-stratcom
   3   *           javelin-install
   4   *           javelin-uri
   5   *           javelin-util
   6   * @provides javelin-history
   7   * @javelin
   8   */
   9  
  10  /**
  11   * JX.History provides a stable interface for managing the browser's history
  12   * stack. Whenever the history stack mutates, the "history:change" event is
  13   * invoked via JX.Stratcom.
  14   *
  15   * Inspired by History Manager implemented by Christoph Pojer (@cpojer)
  16   * @see https://github.com/cpojer/mootools-history
  17   */
  18  JX.install('History', {
  19  
  20    statics : {
  21  
  22      // Mechanisms to @{JX.History.install} with (in preferred support order).
  23      // The default behavior is to use the best supported mechanism.
  24      DEFAULT : Infinity,
  25      PUSHSTATE : 3,
  26      HASHCHANGE : 2,
  27      POLLING : 1,
  28  
  29      // Last path parsed from the URL fragment.
  30      _hash : null,
  31  
  32      // Some browsers fire an extra "popstate" on initial page load, so we keep
  33      // track of the initial path to normalize behavior (and not fire the extra
  34      // event).
  35      _initialPath : null,
  36  
  37      // Mechanism used to interface with the browser history stack.
  38      _mechanism : null,
  39  
  40      /**
  41       * Starts history management. This method must be invoked first before any
  42       * other JX.History method can be used.
  43       *
  44       * @param int An optional mechanism used to interface with the browser
  45       *            history stack. If it is not supported, the next supported
  46       *            mechanism will be used.
  47       */
  48      install : function(mechanism) {
  49        if (__DEV__) {
  50          if (JX.History._installed) {
  51            JX.$E('JX.History.install(): can only install once.');
  52          }
  53          JX.History._installed = true;
  54        }
  55  
  56        mechanism = mechanism || JX.History.DEFAULT;
  57  
  58        if (mechanism >= JX.History.PUSHSTATE && 'pushState' in history) {
  59          JX.History._mechanism = JX.History.PUSHSTATE;
  60          JX.History._initialPath = JX.History._getBasePath(location.href);
  61          JX.Stratcom.listen('popstate', null, JX.History._handleChange);
  62        } else if (mechanism >= JX.History.HASHCHANGE &&
  63                   'onhashchange' in window) {
  64          JX.History._mechanism = JX.History.HASHCHANGE;
  65          JX.Stratcom.listen('hashchange', null, JX.History._handleChange);
  66        } else {
  67          JX.History._mechanism = JX.History.POLLING;
  68          setInterval(JX.History._handleChange, 200);
  69        }
  70      },
  71  
  72      /**
  73       * Get the name of the mechanism used to interface with the browser
  74       * history stack.
  75       *
  76       * @return string Mechanism, either pushstate, hashchange, or polling.
  77       */
  78      getMechanism : function() {
  79        if (__DEV__) {
  80          if (!JX.History._installed) {
  81            JX.$E(
  82              'JX.History.getMechanism(): ' +
  83              'must call JX.History.install() first.');
  84          }
  85        }
  86        return JX.History._mechanism;
  87      },
  88  
  89      /**
  90       * Returns the path on top of the history stack.
  91       *
  92       * If the HTML5 History API is unavailable and an eligible path exists in
  93       * the current URL fragment, the fragment is parsed for a path. Otherwise,
  94       * the current URL path is returned.
  95       *
  96       * @return string Path on top of the history stack.
  97       */
  98      getPath : function() {
  99        if (__DEV__) {
 100          if (!JX.History._installed) {
 101            JX.$E(
 102              'JX.History.getPath(): ' +
 103              'must call JX.History.install() first.');
 104          }
 105        }
 106        if (JX.History.getMechanism() === JX.History.PUSHSTATE) {
 107          return JX.History._getBasePath(location.href);
 108        } else {
 109          var parsed = JX.History._parseFragment(location.hash);
 110          return parsed || JX.History._getBasePath(location.href);
 111        }
 112      },
 113  
 114      /**
 115       * Pushes a path onto the history stack.
 116       *
 117       * @param string Path.
 118       * @return void
 119       */
 120      push : function(path) {
 121        if (__DEV__) {
 122          if (!JX.History._installed) {
 123            JX.$E(
 124              'JX.History.push(): ' +
 125              'must call JX.History.install() first.');
 126          }
 127        }
 128        if (JX.History.getMechanism() === JX.History.PUSHSTATE) {
 129          if (JX.History._initialPath && JX.History._initialPath !== path) {
 130            JX.History._initialPath = null;
 131          }
 132          history.pushState(null, null, path);
 133          JX.History._fire(path);
 134        } else {
 135          location.hash = JX.History._composeFragment(path);
 136        }
 137      },
 138  
 139      /**
 140       * Modifies the path on top of the history stack.
 141       *
 142       * @param string Path.
 143       * @return void
 144       */
 145      replace : function(path) {
 146        if (__DEV__) {
 147          if (!JX.History._installed) {
 148            JX.$E(
 149              'JX.History.replace(): ' +
 150              'must call JX.History.install() first.');
 151          }
 152        }
 153        if (JX.History.getMechanism() === JX.History.PUSHSTATE) {
 154          history.replaceState(null, null, path);
 155          JX.History._fire(path);
 156        } else {
 157          var uri = JX.$U(location.href);
 158          uri.setFragment(JX.History._composeFragment(path));
 159          // Safari bug: "location.replace" does not respect changes made via
 160          // setting "location.hash", so use "history.replaceState" if possible.
 161          if ('replaceState' in history) {
 162            history.replaceState(null, null, uri.toString());
 163            JX.History._handleChange();
 164          } else {
 165            location.replace(uri.toString());
 166          }
 167        }
 168      },
 169  
 170      _handleChange : function() {
 171        var path = JX.History.getPath();
 172        if (JX.History.getMechanism() === JX.History.PUSHSTATE) {
 173          if (path === JX.History._initialPath) {
 174            JX.History._initialPath = null;
 175          } else {
 176            JX.History._fire(path);
 177          }
 178        } else {
 179          if (path !== JX.History._hash) {
 180            JX.History._hash = path;
 181            JX.History._fire(path);
 182          }
 183        }
 184      },
 185  
 186      _fire : function(path) {
 187        JX.Stratcom.invoke('history:change', null, {
 188          path: JX.History._getBasePath(path)
 189        });
 190      },
 191  
 192      _getBasePath : function(href) {
 193        return JX.$U(href).setProtocol(null).setDomain(null).toString();
 194      },
 195  
 196      _composeFragment : function(path) {
 197        path = JX.History._getBasePath(path);
 198        // If the URL fragment does not change, the new path will not get pushed
 199        // onto the stack. So we alternate the hash prefix to force a new state.
 200        if (JX.History.getPath() === path) {
 201          var hash = location.hash;
 202          if (hash && hash.charAt(1) === '!') {
 203            return '~!' + path;
 204          }
 205        }
 206        return '!' + path;
 207      },
 208  
 209      _parseFragment : function(fragment) {
 210        if (fragment) {
 211          if (fragment.charAt(1) === '!') {
 212            return fragment.substr(2);
 213          } else if (fragment.substr(1, 2) === '~!') {
 214            return fragment.substr(3);
 215          }
 216        }
 217        return null;
 218      }
 219  
 220    }
 221  
 222  });


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1