./lib/assets/javascripts/pjax/jquery_pjax.js in pjax_rails-0.1.8 vs ./lib/assets/javascripts/pjax/jquery_pjax.js in pjax_rails-0.1.9

- old
+ new

@@ -1,9 +1,11 @@ -// jquery_pjax.js +// jquery.pjax.js // copyright chris wanstrath -// https://github.com/defunkt/pjax +// https://github.com/defunkt/jquery-pjax +(function($){ + // When called on a link, fetches the href with ajax into the // container specified as the first parameter or with the data-pjax // attribute on the link itself. // // Tries to make sure the back button and ctrl+click work the way @@ -25,19 +27,28 @@ if ( options ) options.container = container else options = $.isPlainObject(container) ? container : {container:container} + // We can't persist $objects using the history API so we must use + // a String selector. Bail if we got anything else. + if ( options.container && typeof options.container !== 'string' ) { + throw "pjax container must be a string selector!" + return false + } + return this.live('click', function(event){ // Middle click, cmd click, and ctrl click should open // links in a new tab as normal. if ( event.which > 1 || event.metaKey ) return true var defaults = { url: this.href, - container: $(this).attr('data-pjax') + container: $(this).attr('data-pjax'), + clickedElement: $(this), + fragment: null } $.pjax($.extend({}, defaults, options)) event.preventDefault() @@ -51,143 +62,168 @@ // Works just like $.ajax in that it accepts a jQuery ajax // settings object (with keys like url, type, data, etc). // // Accepts these extra keys: // -// container - Where to stick the response body. +// container - Where to stick the response body. Must be a String. // $(container).html(xhr.responseBody) // push - Whether to pushState the URL. Defaults to true (of course). // replace - Want to use replaceState instead? That's cool. // // Use it just like $.ajax: // // var xhr = $.pjax({ url: this.href, container: '#main' }) // console.log( xhr.readyState ) // // Returns whatever $.ajax returns. -$.pjax = function( options ) { +var pjax = $.pjax = function( options ) { var $container = $(options.container), success = options.success || $.noop // We don't want to let anyone override our success handler. delete options.success - var defaults = { - timeout: 1500, - push: true, - replace: false, - // We want the browser to maintain two separate internal caches: one for - // pjax'd partial page loads and one for normal page loads. Without - // adding this secret parameter, some browsers will often confuse the two. - data: { _pjax: true }, - type: 'GET', - dataType: 'html', - beforeSend: function(xhr){ - $(document).trigger('start.pjax') - xhr.setRequestHeader('X-PJAX', 'true') - }, - error: function(){ - window.location = options.url - }, - complete: function(){ - $(document).trigger('end.pjax') - }, - success: function(data){ - // If we got no data or an entire web page, go directly - // to the page and let normal error handling happen. - if ( !$.trim(data) || /<html/i.test(data) ) - return window.location = options.url + // We can't persist $objects using the history API so we must use + // a String selector. Bail if we got anything else. + if ( typeof options.container !== 'string' ) + throw "pjax container must be a string selector!" - // Make it happen. - $container.html(data) + options = $.extend(true, {}, pjax.defaults, options) - // If there's a <title> tag in the response, use it as - // the page's title. - var oldTitle = document.title, - title = $.trim( $container.find('title').remove().text() ) - if ( title ) document.title = title + if ( $.isFunction(options.url) ) { + options.url = options.url() + } - var state = { - pjax: options.container, - timeout: options.timeout - } + options.context = $container - // We can't persist $objects using the history API so we need to store - // the string selector. - if ( $.isPlainObject(state.pjax) ) - state.pjax = state.pjax.selector + options.success = function(data){ + if ( options.fragment ) { + // If they specified a fragment, look for it in the response + // and pull it out. + var $fragment = $(data).find(options.fragment) + if ( $fragment.length ) + data = $fragment.children() + else + return window.location = options.url + } else { + // If we got no data or an entire web page, go directly + // to the page and let normal error handling happen. + if ( !$.trim(data) || /<html/i.test(data) ) + return window.location = options.url + } - // If there are extra params, save the complete URL in the state object - var query = $.param(options.data) - if ( query != "_pjax=true" ) - state.url = options.url + (/\?/.test(options.url) ? "&" : "?") + query + // Make it happen. + this.html(data) - if ( options.replace ) { - window.history.replaceState(state, document.title, options.url) - } else if ( options.push ) { - // this extra replaceState before first push ensures good back - // button behavior - if ( !$.pjax.active ) { - window.history.replaceState($.extend({}, state, {url:null}), oldTitle) - $.pjax.active = true - } + // If there's a <title> tag in the response, use it as + // the page's title. + var oldTitle = document.title, + title = $.trim( this.find('title').remove().text() ) + if ( title ) document.title = title - window.history.pushState(state, document.title, options.url) - } + var state = { + pjax: options.container, + fragment: options.fragment, + timeout: options.timeout + } - // Google Analytics support - if ( (options.replace || options.push) && window._gaq ) - _gaq.push(['_trackPageview']) + // If there are extra params, save the complete URL in the state object + var query = $.param(options.data) + if ( query != "_pjax=true" ) + state.url = options.url + (/\?/.test(options.url) ? "&" : "?") + query - // Invoke their success handler if they gave us one. - success.apply(this, arguments) + if ( options.replace ) { + window.history.replaceState(state, document.title, options.url) + } else if ( options.push ) { + // this extra replaceState before first push ensures good back + // button behavior + if ( !pjax.active ) { + window.history.replaceState($.extend({}, state, {url:null}), oldTitle) + pjax.active = true + } + + window.history.pushState(state, document.title, options.url) } - } - options = $.extend(true, {}, defaults, options) + // Google Analytics support + if ( (options.replace || options.push) && window._gaq ) + _gaq.push(['_trackPageview']) - if ( $.isFunction(options.url) ) { - options.url = options.url() + // If the URL has a hash in it, make sure the browser + // knows to navigate to the hash. + var hash = window.location.hash.toString() + if ( hash !== '' ) { + window.location.href = hash + } + + // Invoke their success handler if they gave us one. + success.apply(this, arguments) } // Cancel the current request if we're already pjaxing - var xhr = $.pjax.xhr + var xhr = pjax.xhr if ( xhr && xhr.readyState < 4) { xhr.onreadystatechange = $.noop xhr.abort() } - $.pjax.xhr = $.ajax(options) - $(document).trigger('pjax', $.pjax.xhr, options) + pjax.options = options + pjax.xhr = $.ajax(options) + $(document).trigger('pjax', [pjax.xhr, options]) - return $.pjax.xhr + return pjax.xhr } +pjax.defaults = { + timeout: 650, + push: true, + replace: false, + // We want the browser to maintain two separate internal caches: one for + // pjax'd partial page loads and one for normal page loads. Without + // adding this secret parameter, some browsers will often confuse the two. + data: { _pjax: true }, + type: 'GET', + dataType: 'html', + beforeSend: function(xhr){ + this.trigger('start.pjax', [xhr, pjax.options]) + xhr.setRequestHeader('X-PJAX', 'true') + }, + error: function(xhr, textStatus, errorThrown){ + if ( textStatus !== 'abort' ) + window.location = pjax.options.url + }, + complete: function(xhr){ + this.trigger('end.pjax', [xhr, pjax.options]) + } +} + + // Used to detect initial (useless) popstate. // If history.state exists, assume browser isn't going to fire initial popstate. var popped = ('state' in window.history), initialURL = location.href // popstate handler takes care of the back and forward buttons // // You probably shouldn't use pjax on pages with other pushState // stuff yet. -$(window).bind('popstate', function(event) { +$(window).bind('popstate', function(event){ // Ignore inital popstate that some browsers fire on page load var initialPop = !popped && location.href == initialURL popped = true if ( initialPop ) return var state = event.state if ( state && state.pjax ) { - var $container = $(state.pjax+'') - if ( $container.length ) + var container = state.pjax + if ( $(container+'').length ) $.pjax({ url: state.url || location.href, - container: $container, + fragment: state.fragment, + container: container, push: false, timeout: state.timeout }) else window.location = location.href @@ -195,14 +231,25 @@ }) // Add the state property to jQuery's event object so we can use it in // $(window).bind('popstate') -if ( $.event.props.indexOf('state') < 0 ) +if ( $.inArray('state', $.event.props) < 0 ) $.event.props.push('state') +// Is pjax supported by this browser? +$.support.pjax = + window.history && window.history.pushState && window.history.replaceState + // pushState isn't reliable on iOS yet. + && !navigator.userAgent.match(/(iPod|iPhone|iPad|WebApps\/.+CFNetwork)/) + + // Fall back to normalcy for older browsers. -if ( !window.history || !window.history.pushState ) { - $.pjax = $.noop +if ( !$.support.pjax ) { + $.pjax = function( options ) { + window.location = $.isFunction(options.url) ? options.url() : options.url + } $.fn.pjax = function() { return this } -} +} + +})(jQuery); \ No newline at end of file