[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/pjax-base/ -> pjax-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('pjax-base', function (Y, NAME) {
   9  
  10  /**
  11  `Y.Router` extension that provides the core plumbing for enhanced navigation
  12  implemented using the pjax technique (HTML5 pushState + Ajax).
  13  
  14  @module pjax
  15  @submodule pjax-base
  16  @since 3.5.0
  17  **/
  18  
  19  var win = Y.config.win,
  20  
  21      // The CSS class name used to filter link clicks from only the links which
  22      // the pjax enhanced navigation should be used.
  23      CLASS_PJAX = Y.ClassNameManager.getClassName('pjax'),
  24  
  25      /**
  26      Fired when navigating to a URL via Pjax.
  27  
  28      When the `navigate()` method is called or a pjax link is clicked, this event
  29      will be fired if the browser supports HTML5 history _and_ the router has a
  30      route handler for the specified URL.
  31  
  32      This is a useful event to listen to for adding a visual loading indicator
  33      while the route handlers are busy handling the URL change.
  34  
  35      @event navigate
  36      @param {String} url The URL that the router will dispatch to its route
  37        handlers in order to fulfill the enhanced navigation "request".
  38      @param {Boolean} [force=false] Whether the enhanced navigation should occur
  39        even in browsers without HTML5 history.
  40      @param {String} [hash] The hash-fragment (including "#") of the `url`. This
  41        will be present when the `url` differs from the current URL only by its
  42        hash and `navigateOnHash` has been set to `true`.
  43      @param {Event} [originEvent] The event that caused the navigation. Usually
  44        this would be a click event from a "pjax" anchor element.
  45      @param {Boolean} [replace] Whether or not the current history entry will be
  46        replaced, or a new entry will be created. Will default to `true` if the
  47        specified `url` is the same as the current URL.
  48      @since 3.5.0
  49      **/
  50      EVT_NAVIGATE = 'navigate';
  51  
  52  /**
  53  `Y.Router` extension that provides the core plumbing for enhanced navigation
  54  implemented using the pjax technique (HTML5 `pushState` + Ajax).
  55  
  56  This makes it easy to enhance the navigation between the URLs of an application
  57  in HTML5 history capable browsers by delegating to the router to fulfill the
  58  "request" and seamlessly falling-back to using standard full-page reloads in
  59  older, less-capable browsers.
  60  
  61  The `PjaxBase` class isn't useful on its own, but can be mixed into a
  62  `Router`-based class to add Pjax functionality to that Router. For a pre-made
  63  standalone Pjax router, see the `Pjax` class.
  64  
  65      var MyRouter = Y.Base.create('myRouter', Y.Router, [Y.PjaxBase], {
  66          // ...
  67      });
  68  
  69  @class PjaxBase
  70  @extensionfor Router
  71  @since 3.5.0
  72  **/
  73  function PjaxBase() {}
  74  
  75  PjaxBase.prototype = {
  76      // -- Protected Properties -------------------------------------------------
  77  
  78      /**
  79      Holds the delegated pjax-link click handler.
  80  
  81      @property _pjaxEvents
  82      @type EventHandle
  83      @protected
  84      @since 3.5.0
  85      **/
  86  
  87      // -- Lifecycle Methods ----------------------------------------------------
  88      initializer: function () {
  89          this.publish(EVT_NAVIGATE, {defaultFn: this._defNavigateFn});
  90  
  91          // Pjax is all about progressively enhancing the navigation between
  92          // "pages", so by default we only want to handle and route link clicks
  93          // in HTML5 `pushState`-compatible browsers.
  94          if (this.get('html5')) {
  95              this._pjaxBindUI();
  96          }
  97      },
  98  
  99      destructor: function () {
 100          if (this._pjaxEvents) {
 101              this._pjaxEvents.detach();
 102          }
 103      },
 104  
 105      // -- Public Methods -------------------------------------------------------
 106  
 107      /**
 108      Navigates to the specified URL if there is a route handler that matches. In
 109      browsers capable of using HTML5 history, the navigation will be enhanced by
 110      firing the `navigate` event and having the router handle the "request".
 111      Non-HTML5 browsers will navigate to the new URL via manipulation of
 112      `window.location`.
 113  
 114      When there is a route handler for the specified URL and it is being
 115      navigated to, this method will return `true`, otherwise it will return
 116      `false`.
 117  
 118      **Note:** The specified URL _must_ be of the same origin as the current URL,
 119      otherwise an error will be logged and navigation will not occur. This is
 120      intended as both a security constraint and a purposely imposed limitation as
 121      it does not make sense to tell the router to navigate to a URL on a
 122      different scheme, host, or port.
 123  
 124      @method navigate
 125      @param {String} url The URL to navigate to. This must be of the same origin
 126        as the current URL.
 127      @param {Object} [options] Additional options to configure the navigation.
 128        These are mixed into the `navigate` event facade.
 129          @param {Boolean} [options.replace] Whether or not the current history
 130            entry will be replaced, or a new entry will be created. Will default
 131            to `true` if the specified `url` is the same as the current URL.
 132          @param {Boolean} [options.force=false] Whether the enhanced navigation
 133            should occur even in browsers without HTML5 history.
 134      @return {Boolean} `true` if the URL was navigated to, `false` otherwise.
 135      @since 3.5.0
 136      **/
 137      navigate: function (url, options) {
 138          // The `_navigate()` method expects fully-resolved URLs.
 139          url = this._resolveURL(url);
 140  
 141          if (this._navigate(url, options)) {
 142              return true;
 143          }
 144  
 145          if (!this._hasSameOrigin(url)) {
 146              Y.error('Security error: The new URL must be of the same origin as the current URL.');
 147          }
 148  
 149          return false;
 150      },
 151  
 152      // -- Protected Methods ----------------------------------------------------
 153  
 154      /**
 155      Utility method to test whether a specified link/anchor node's `href` is of
 156      the same origin as the page's current location.
 157  
 158      This normalize browser inconsistencies with how the `port` is reported for
 159      anchor elements (IE reports a value for the default port, e.g. "80").
 160  
 161      @method _isLinkSameOrigin
 162      @param {Node} link The anchor element to test whether its `href` is of the
 163          same origin as the page's current location.
 164      @return {Boolean} Whether or not the link's `href` is of the same origin as
 165          the page's current location.
 166      @protected
 167      @since 3.6.0
 168      **/
 169      _isLinkSameOrigin: function (link) {
 170          var location = Y.getLocation(),
 171              protocol = location.protocol,
 172              hostname = location.hostname,
 173              port     = parseInt(location.port, 10) || null,
 174              linkPort;
 175  
 176          // Link must have the same `protocol` and `hostname` as the page's
 177          // currrent location.
 178          if (link.get('protocol') !== protocol ||
 179                  link.get('hostname') !== hostname) {
 180  
 181              return false;
 182          }
 183  
 184          linkPort = parseInt(link.get('port'), 10) || null;
 185  
 186          // Normalize ports. In most cases browsers use an empty string when the
 187          // port is the default port, but IE does weird things with anchor
 188          // elements, so to be sure, this will re-assign the default ports before
 189          // they are compared.
 190          if (protocol === 'http:') {
 191              port     || (port     = 80);
 192              linkPort || (linkPort = 80);
 193          } else if (protocol === 'https:') {
 194              port     || (port     = 443);
 195              linkPort || (linkPort = 443);
 196          }
 197  
 198          // Finally, to be from the same origin, the link's `port` must match the
 199          // page's current `port`.
 200          return linkPort === port;
 201      },
 202  
 203      /**
 204      Underlying implementation for `navigate()`.
 205  
 206      @method _navigate
 207      @param {String} url The fully-resolved URL that the router should dispatch
 208        to its route handlers to fulfill the enhanced navigation "request", or use
 209        to update `window.location` in non-HTML5 history capable browsers.
 210      @param {Object} [options] Additional options to configure the navigation.
 211        These are mixed into the `navigate` event facade.
 212          @param {Boolean} [options.replace] Whether or not the current history
 213            entry will be replaced, or a new entry will be created. Will default
 214            to `true` if the specified `url` is the same as the current URL.
 215          @param {Boolean} [options.force=false] Whether the enhanced navigation
 216            should occur even in browsers without HTML5 history.
 217      @return {Boolean} `true` if the URL was navigated to, `false` otherwise.
 218      @protected
 219      @since 3.5.0
 220      **/
 221      _navigate: function (url, options) {
 222          url = this._upgradeURL(url);
 223  
 224          // Navigation can only be enhanced if there is a route-handler.
 225          if (!this.hasRoute(url)) {
 226              return false;
 227          }
 228  
 229          // Make a copy of `options` before modifying it.
 230          options = Y.merge(options, {url: url});
 231  
 232          var currentURL = this._getURL(),
 233              hash, hashlessURL;
 234  
 235          // Captures the `url`'s hash and returns a URL without that hash.
 236          hashlessURL = url.replace(/(#.*)$/, function (u, h, i) {
 237              hash = h;
 238              return u.substring(i);
 239          });
 240  
 241          if (hash && hashlessURL === currentURL.replace(/#.*$/, '')) {
 242              // When the specified `url` and current URL only differ by the hash,
 243              // the browser should handle this in-page navigation normally.
 244              if (!this.get('navigateOnHash')) {
 245                  return false;
 246              }
 247  
 248              options.hash = hash;
 249          }
 250  
 251          // When navigating to the same URL as the current URL, behave like a
 252          // browser and replace the history entry instead of creating a new one.
 253          'replace' in options || (options.replace = url === currentURL);
 254  
 255          // The `navigate` event will only fire and therefore enhance the
 256          // navigation to the new URL in HTML5 history enabled browsers or when
 257          // forced. Otherwise it will fallback to assigning or replacing the URL
 258          // on `window.location`.
 259          if (this.get('html5') || options.force) {
 260              this.fire(EVT_NAVIGATE, options);
 261          } else if (win) {
 262              if (options.replace) {
 263                  win.location.replace(url);
 264              } else {
 265                  win.location = url;
 266              }
 267          }
 268  
 269          return true;
 270      },
 271  
 272      /**
 273      Binds the delegation of link-click events that match the `linkSelector` to
 274      the `_onLinkClick()` handler.
 275  
 276      By default this method will only be called if the browser is capable of
 277      using HTML5 history.
 278  
 279      @method _pjaxBindUI
 280      @protected
 281      @since 3.5.0
 282      **/
 283      _pjaxBindUI: function () {
 284          // Only bind link if we haven't already.
 285          if (!this._pjaxEvents) {
 286              this._pjaxEvents = Y.one('body').delegate('click',
 287                  this._onLinkClick, this.get('linkSelector'), this);
 288          }
 289      },
 290  
 291      // -- Protected Event Handlers ---------------------------------------------
 292  
 293      /**
 294      Default handler for the `navigate` event.
 295  
 296      Adds a new history entry or replaces the current entry for the specified URL
 297      and will scroll the page to the top if configured to do so.
 298  
 299      @method _defNavigateFn
 300      @param {EventFacade} e
 301      @protected
 302      @since 3.5.0
 303      **/
 304      _defNavigateFn: function (e) {
 305          this[e.replace ? 'replace' : 'save'](e.url);
 306  
 307          if (win && this.get('scrollToTop')) {
 308              // Scroll to the top of the page. The timeout ensures that the
 309              // scroll happens after navigation begins, so that the current
 310              // scroll position will be restored if the user clicks the back
 311              // button.
 312              setTimeout(function () {
 313                  win.scroll(0, 0);
 314              }, 1);
 315          }
 316      },
 317  
 318      /**
 319      Handler for delegated link-click events which match the `linkSelector`.
 320  
 321      This will attempt to enhance the navigation to the link element's `href` by
 322      passing the URL to the `_navigate()` method. When the navigation is being
 323      enhanced, the default action is prevented.
 324  
 325      If the user clicks a link with the middle/right mouse buttons, or is holding
 326      down the Ctrl or Command keys, this method's behavior is not applied and
 327      allows the native behavior to occur. Similarly, if the router is not capable
 328      or handling the URL because no route-handlers match, the link click will
 329      behave natively.
 330  
 331      @method _onLinkClick
 332      @param {EventFacade} e
 333      @protected
 334      @since 3.5.0
 335      **/
 336      _onLinkClick: function (e) {
 337          var link, url, navigated;
 338  
 339          // Allow the native behavior on middle/right-click, or when Ctrl or
 340          // Command are pressed.
 341          if (e.button !== 1 || e.ctrlKey || e.metaKey) { return; }
 342  
 343          link = e.currentTarget;
 344  
 345          // Only allow anchor elements because we need access to its `protocol`,
 346          // `host`, and `href` attributes.
 347          if (link.get('tagName').toUpperCase() !== 'A') {
 348              return;
 349          }
 350  
 351          // Same origin check to prevent trying to navigate to URLs from other
 352          // sites or things like mailto links.
 353          if (!this._isLinkSameOrigin(link)) {
 354              return;
 355          }
 356  
 357          // All browsers fully resolve an anchor's `href` property.
 358          url = link.get('href');
 359  
 360          // Try and navigate to the URL via the router, and prevent the default
 361          // link-click action if we do.
 362          if (url) {
 363              navigated = this._navigate(url, {originEvent: e});
 364  
 365              if (navigated) {
 366                  e.preventDefault();
 367              }
 368          }
 369      }
 370  };
 371  
 372  PjaxBase.ATTRS = {
 373      /**
 374      CSS selector string used to filter link click events so that only the links
 375      which match it will have the enhanced navigation behavior of Pjax applied.
 376  
 377      When a link is clicked and that link matches this selector, Pjax will
 378      attempt to dispatch to any route handlers matching the link's `href` URL. If
 379      HTML5 history is not supported or if no route handlers match, the link click
 380      will be handled by the browser just like any old link.
 381  
 382      @attribute linkSelector
 383      @type String|Function
 384      @default "a.yui3-pjax"
 385      @initOnly
 386      @since 3.5.0
 387      **/
 388      linkSelector: {
 389          value    : 'a.' + CLASS_PJAX,
 390          writeOnce: 'initOnly'
 391      },
 392  
 393      /**
 394      Whether navigating to a hash-fragment identifier on the current page should
 395      be enhanced and cause the `navigate` event to fire.
 396  
 397      By default Pjax allows the browser to perform its default action when a user
 398      is navigating within a page by clicking in-page links
 399      (e.g. `<a href="#top">Top of page</a>`) and does not attempt to interfere or
 400      enhance in-page navigation.
 401  
 402      @attribute navigateOnHash
 403      @type Boolean
 404      @default false
 405      @since 3.5.0
 406      **/
 407      navigateOnHash: {
 408          value: false
 409      },
 410  
 411      /**
 412      Whether the page should be scrolled to the top after navigating to a URL.
 413  
 414      When the user clicks the browser's back button, the previous scroll position
 415      will be maintained.
 416  
 417      @attribute scrollToTop
 418      @type Boolean
 419      @default true
 420      @since 3.5.0
 421      **/
 422      scrollToTop: {
 423          value: true
 424      }
 425  };
 426  
 427  Y.PjaxBase = PjaxBase;
 428  
 429  
 430  }, '3.17.2', {"requires": ["classnamemanager", "node-event-delegate", "router"]});


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