You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
843 lines
22 KiB
843 lines
22 KiB
// jquery.pjax.js |
|
// copyright chris wanstrath |
|
// https://github.com/defunkt/jquery-pjax |
|
|
|
(function($){ |
|
|
|
// When called on a container with a selector, fetches the href with |
|
// ajax into the container or with the data-pjax attribute on the link |
|
// itself. |
|
// |
|
// Tries to make sure the back button and ctrl+click work the way |
|
// you'd expect. |
|
// |
|
// Exported as $.fn.pjax |
|
// |
|
// Accepts a jQuery ajax options object that may include these |
|
// pjax specific options: |
|
// |
|
// |
|
// container - Where to stick the response body. Usually a String selector. |
|
// $(container).html(xhr.responseBody) |
|
// (default: current jquery context) |
|
// push - Whether to pushState the URL. Defaults to true (of course). |
|
// replace - Want to use replaceState instead? That's cool. |
|
// |
|
// For convenience the second parameter can be either the container or |
|
// the options object. |
|
// |
|
// Returns the jQuery object |
|
function fnPjax(selector, container, options) { |
|
var context = this |
|
return this.on('click.pjax', selector, function(event) { |
|
var opts = $.extend({}, optionsFor(container, options)) |
|
if (!opts.container) |
|
opts.container = $(this).attr('data-pjax') || context |
|
handleClick(event, opts) |
|
}) |
|
} |
|
|
|
// Public: pjax on click handler |
|
// |
|
// Exported as $.pjax.click. |
|
// |
|
// event - "click" jQuery.Event |
|
// options - pjax options |
|
// |
|
// Examples |
|
// |
|
// $(document).on('click', 'a', $.pjax.click) |
|
// // is the same as |
|
// $(document).pjax('a') |
|
// |
|
// $(document).on('click', 'a', function(event) { |
|
// var container = $(this).closest('[data-pjax-container]') |
|
// $.pjax.click(event, container) |
|
// }) |
|
// |
|
// Returns nothing. |
|
function handleClick(event, container, options) { |
|
options = optionsFor(container, options) |
|
|
|
var link = event.currentTarget |
|
|
|
// Ignore links with data-pjax="0" |
|
if ($(link).data('pjax')==0) |
|
return |
|
|
|
if (link.tagName.toUpperCase() !== 'A') |
|
throw "$.fn.pjax or $.pjax.click requires an anchor element" |
|
|
|
// Middle click, cmd click, and ctrl click should open |
|
// links in a new tab as normal. |
|
if ( event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey ) |
|
return |
|
|
|
// Ignore cross origin links |
|
if ( location.protocol !== link.protocol || location.hostname !== link.hostname ) |
|
return |
|
|
|
// Ignore anchors on the same page |
|
if (link.hash && link.href.replace(link.hash, '') === |
|
location.href.replace(location.hash, '')) |
|
return |
|
|
|
// Ignore empty anchor "foo.html#" |
|
if (link.href === location.href + '#') |
|
return |
|
|
|
var defaults = { |
|
url: link.href, |
|
container: $(link).attr('data-pjax'), |
|
target: link |
|
} |
|
|
|
var opts = $.extend({}, defaults, options) |
|
var clickEvent = $.Event('pjax:click') |
|
$(link).trigger(clickEvent, [opts]) |
|
|
|
if (!clickEvent.isDefaultPrevented()) { |
|
pjax(opts) |
|
event.preventDefault() |
|
$(link).trigger('pjax:clicked', [opts]) |
|
} |
|
} |
|
|
|
// Public: pjax on form submit handler |
|
// |
|
// Exported as $.pjax.submit |
|
// |
|
// event - "click" jQuery.Event |
|
// options - pjax options |
|
// |
|
// Examples |
|
// |
|
// $(document).on('submit', 'form', function(event) { |
|
// var container = $(this).closest('[data-pjax-container]') |
|
// $.pjax.submit(event, container) |
|
// }) |
|
// |
|
// Returns nothing. |
|
function handleSubmit(event, container, options) { |
|
options = optionsFor(container, options) |
|
|
|
var form = event.currentTarget |
|
|
|
if (form.tagName.toUpperCase() !== 'FORM') |
|
throw "$.pjax.submit requires a form element" |
|
|
|
var defaults = { |
|
type: form.method.toUpperCase(), |
|
url: form.action, |
|
data: $(form).serializeArray(), |
|
container: $(form).attr('data-pjax'), |
|
target: form |
|
} |
|
|
|
pjax($.extend({}, defaults, options)) |
|
|
|
event.preventDefault() |
|
} |
|
|
|
// Loads a URL with ajax, puts the response body inside a container, |
|
// then pushState()'s the loaded URL. |
|
// |
|
// 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).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. |
|
function pjax(options) { |
|
options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options) |
|
|
|
if ($.isFunction(options.url)) { |
|
options.url = options.url() |
|
} |
|
|
|
var target = options.target |
|
|
|
var hash = parseURL(options.url).hash |
|
|
|
var context = options.context = findContainerFor(options.container) |
|
|
|
// 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. |
|
if (!options.data) options.data = {} |
|
options.data._pjax = context.selector |
|
|
|
function fire(type, args) { |
|
var event = $.Event(type, { relatedTarget: target }) |
|
context.trigger(event, args) |
|
return !event.isDefaultPrevented() |
|
} |
|
|
|
var timeoutTimer |
|
|
|
options.beforeSend = function(xhr, settings) { |
|
// No timeout for non-GET requests |
|
// Its not safe to request the resource again with a fallback method. |
|
if (settings.type !== 'GET') { |
|
settings.timeout = 0 |
|
} |
|
|
|
xhr.setRequestHeader('X-PJAX', 'true') |
|
xhr.setRequestHeader('X-PJAX-Container', context.selector) |
|
|
|
if (!fire('pjax:beforeSend', [xhr, settings])) |
|
return false |
|
|
|
if (settings.timeout > 0) { |
|
timeoutTimer = setTimeout(function() { |
|
if (fire('pjax:timeout', [xhr, options])) |
|
xhr.abort('timeout') |
|
}, settings.timeout) |
|
|
|
// Clear timeout setting so jquerys internal timeout isn't invoked |
|
settings.timeout = 0 |
|
} |
|
|
|
options.requestUrl = parseURL(settings.url).href |
|
} |
|
|
|
options.complete = function(xhr, textStatus) { |
|
if (timeoutTimer) |
|
clearTimeout(timeoutTimer) |
|
|
|
fire('pjax:complete', [xhr, textStatus, options]) |
|
|
|
fire('pjax:end', [xhr, options]) |
|
} |
|
|
|
options.error = function(xhr, textStatus, errorThrown) { |
|
var container = extractContainer("", xhr, options) |
|
|
|
var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options]) |
|
if (options.type == 'GET' && textStatus !== 'abort' && allowed) { |
|
locationReplace(container.url) |
|
} |
|
} |
|
|
|
options.success = function(data, status, xhr) { |
|
// If $.pjax.defaults.version is a function, invoke it first. |
|
// Otherwise it can be a static string. |
|
var currentVersion = (typeof $.pjax.defaults.version === 'function') ? |
|
$.pjax.defaults.version() : |
|
$.pjax.defaults.version |
|
|
|
var latestVersion = xhr.getResponseHeader('X-PJAX-Version') |
|
|
|
var container = extractContainer(data, xhr, options) |
|
|
|
// If there is a layout version mismatch, hard load the new url |
|
if (currentVersion && latestVersion && currentVersion !== latestVersion) { |
|
locationReplace(container.url) |
|
return |
|
} |
|
|
|
// If the new response is missing a body, hard load the page |
|
if (!container.contents) { |
|
locationReplace(container.url) |
|
return |
|
} |
|
|
|
pjax.state = { |
|
id: options.id || uniqueId(), |
|
url: container.url, |
|
title: container.title, |
|
container: context.selector, |
|
fragment: options.fragment, |
|
timeout: options.timeout |
|
} |
|
|
|
if (options.push || options.replace) { |
|
window.history.replaceState(pjax.state, container.title, container.url) |
|
} |
|
|
|
// Clear out any focused controls before inserting new page contents. |
|
document.activeElement.blur() |
|
|
|
if (container.title) document.title = container.title |
|
context.html(container.contents) |
|
|
|
// FF bug: Won't autofocus fields that are inserted via JS. |
|
// This behavior is incorrect. So if theres no current focus, autofocus |
|
// the last field. |
|
// |
|
// http://www.w3.org/html/wg/drafts/html/master/forms.html |
|
var autofocusEl = context.find('input[autofocus], textarea[autofocus]').last()[0] |
|
if (autofocusEl && document.activeElement !== autofocusEl) { |
|
autofocusEl.focus(); |
|
} |
|
|
|
executeScriptTags(container.scripts) |
|
|
|
// Scroll to top by default |
|
if (typeof options.scrollTo === 'number') |
|
$(window).scrollTop(options.scrollTo) |
|
|
|
// If the URL has a hash in it, make sure the browser |
|
// knows to navigate to the hash. |
|
if ( hash !== '' ) { |
|
// Avoid using simple hash set here. Will add another history |
|
// entry. Replace the url with replaceState and scroll to target |
|
// by hand. |
|
// |
|
// window.location.hash = hash |
|
var url = parseURL(container.url) |
|
url.hash = hash |
|
|
|
pjax.state.url = url.href |
|
window.history.replaceState(pjax.state, container.title, url.href) |
|
|
|
var target = $(url.hash) |
|
if (target.length) $(window).scrollTop(target.offset().top) |
|
} |
|
|
|
fire('pjax:success', [data, status, xhr, options]) |
|
} |
|
|
|
|
|
// Initialize pjax.state for the initial page load. Assume we're |
|
// using the container and options of the link we're loading for the |
|
// back button to the initial page. This ensures good back button |
|
// behavior. |
|
if (!pjax.state) { |
|
pjax.state = { |
|
id: uniqueId(), |
|
url: window.location.href, |
|
title: document.title, |
|
container: context.selector, |
|
fragment: options.fragment, |
|
timeout: options.timeout |
|
} |
|
window.history.replaceState(pjax.state, document.title) |
|
} |
|
|
|
// Cancel the current request if we're already pjaxing |
|
var xhr = pjax.xhr |
|
if ( xhr && xhr.readyState < 4) { |
|
xhr.onreadystatechange = $.noop |
|
xhr.abort() |
|
} |
|
|
|
pjax.options = options |
|
var xhr = pjax.xhr = $.ajax(options) |
|
|
|
if (xhr.readyState > 0) { |
|
if (options.push && !options.replace) { |
|
// Cache current container element before replacing it |
|
cachePush(pjax.state.id, context.clone().contents()) |
|
|
|
window.history.pushState(null, "", stripPjaxParam(options.requestUrl)) |
|
} |
|
|
|
fire('pjax:start', [xhr, options]) |
|
fire('pjax:send', [xhr, options]) |
|
} |
|
|
|
return pjax.xhr |
|
} |
|
|
|
// Public: Reload current page with pjax. |
|
// |
|
// Returns whatever $.pjax returns. |
|
function pjaxReload(container, options) { |
|
var defaults = { |
|
url: window.location.href, |
|
push: false, |
|
replace: true, |
|
scrollTo: false |
|
} |
|
|
|
return pjax($.extend(defaults, optionsFor(container, options))) |
|
} |
|
|
|
// Internal: Hard replace current state with url. |
|
// |
|
// Work for around WebKit |
|
// https://bugs.webkit.org/show_bug.cgi?id=93506 |
|
// |
|
// Returns nothing. |
|
function locationReplace(url) { |
|
window.history.replaceState(null, "", "#") |
|
window.location.replace(url) |
|
} |
|
|
|
|
|
var initialPop = true |
|
var initialURL = window.location.href |
|
var initialState = window.history.state |
|
|
|
// Initialize $.pjax.state if possible |
|
// Happens when reloading a page and coming forward from a different |
|
// session history. |
|
if (initialState && initialState.container) { |
|
pjax.state = initialState |
|
} |
|
|
|
// Non-webkit browsers don't fire an initial popstate event |
|
if ('state' in window.history) { |
|
initialPop = false |
|
} |
|
|
|
// popstate handler takes care of the back and forward buttons |
|
// |
|
// You probably shouldn't use pjax on pages with other pushState |
|
// stuff yet. |
|
function onPjaxPopstate(event) { |
|
var state = event.state |
|
|
|
if (state && state.container) { |
|
// When coming forward from a separate history session, will get an |
|
// initial pop with a state we are already at. Skip reloading the current |
|
// page. |
|
if (initialPop && initialURL == state.url) return |
|
|
|
// If popping back to the same state, just skip. |
|
// Could be clicking back from hashchange rather than a pushState. |
|
if (pjax.state.id === state.id) return |
|
|
|
var container = $(state.container) |
|
if (container.length) { |
|
var direction, contents = cacheMapping[state.id] |
|
|
|
if (pjax.state) { |
|
// Since state ids always increase, we can deduce the history |
|
// direction from the previous state. |
|
direction = pjax.state.id < state.id ? 'forward' : 'back' |
|
|
|
// Cache current container before replacement and inform the |
|
// cache which direction the history shifted. |
|
cachePop(direction, pjax.state.id, container.clone().contents()) |
|
} |
|
|
|
var popstateEvent = $.Event('pjax:popstate', { |
|
state: state, |
|
direction: direction |
|
}) |
|
container.trigger(popstateEvent) |
|
|
|
var options = { |
|
id: state.id, |
|
url: state.url, |
|
container: container, |
|
push: false, |
|
fragment: state.fragment, |
|
timeout: state.timeout, |
|
scrollTo: false |
|
} |
|
|
|
if (contents) { |
|
container.trigger('pjax:start', [null, options]) |
|
|
|
if (state.title) document.title = state.title |
|
container.html(contents) |
|
pjax.state = state |
|
|
|
container.trigger('pjax:end', [null, options]) |
|
} else { |
|
pjax(options) |
|
} |
|
|
|
// Force reflow/relayout before the browser tries to restore the |
|
// scroll position. |
|
container[0].offsetHeight |
|
} else { |
|
locationReplace(location.href) |
|
} |
|
} |
|
initialPop = false |
|
} |
|
|
|
// Fallback version of main pjax function for browsers that don't |
|
// support pushState. |
|
// |
|
// Returns nothing since it retriggers a hard form submission. |
|
function fallbackPjax(options) { |
|
var url = $.isFunction(options.url) ? options.url() : options.url, |
|
method = options.type ? options.type.toUpperCase() : 'GET' |
|
|
|
var form = $('<form>', { |
|
method: method === 'GET' ? 'GET' : 'POST', |
|
action: url, |
|
style: 'display:none' |
|
}) |
|
|
|
if (method !== 'GET' && method !== 'POST') { |
|
form.append($('<input>', { |
|
type: 'hidden', |
|
name: '_method', |
|
value: method.toLowerCase() |
|
})) |
|
} |
|
|
|
var data = options.data |
|
if (typeof data === 'string') { |
|
$.each(data.split('&'), function(index, value) { |
|
var pair = value.split('=') |
|
form.append($('<input>', {type: 'hidden', name: pair[0], value: pair[1]})) |
|
}) |
|
} else if (typeof data === 'object') { |
|
for (key in data) |
|
form.append($('<input>', {type: 'hidden', name: key, value: data[key]})) |
|
} |
|
|
|
$(document.body).append(form) |
|
form.submit() |
|
} |
|
|
|
// Internal: Generate unique id for state object. |
|
// |
|
// Use a timestamp instead of a counter since ids should still be |
|
// unique across page loads. |
|
// |
|
// Returns Number. |
|
function uniqueId() { |
|
return (new Date).getTime() |
|
} |
|
|
|
// Internal: Strips _pjax param from url |
|
// |
|
// url - String |
|
// |
|
// Returns String. |
|
function stripPjaxParam(url) { |
|
return url |
|
.replace(/\?_pjax=[^&]+&?/, '?') |
|
.replace(/_pjax=[^&]+&?/, '') |
|
.replace(/[\?&]$/, '') |
|
} |
|
|
|
// Internal: Parse URL components and returns a Locationish object. |
|
// |
|
// url - String URL |
|
// |
|
// Returns HTMLAnchorElement that acts like Location. |
|
function parseURL(url) { |
|
var a = document.createElement('a') |
|
a.href = url |
|
return a |
|
} |
|
|
|
// Internal: Build options Object for arguments. |
|
// |
|
// For convenience the first parameter can be either the container or |
|
// the options object. |
|
// |
|
// Examples |
|
// |
|
// optionsFor('#container') |
|
// // => {container: '#container'} |
|
// |
|
// optionsFor('#container', {push: true}) |
|
// // => {container: '#container', push: true} |
|
// |
|
// optionsFor({container: '#container', push: true}) |
|
// // => {container: '#container', push: true} |
|
// |
|
// Returns options Object. |
|
function optionsFor(container, options) { |
|
// Both container and options |
|
if ( container && options ) |
|
options.container = container |
|
|
|
// First argument is options Object |
|
else if ( $.isPlainObject(container) ) |
|
options = container |
|
|
|
// Only container |
|
else |
|
options = {container: container} |
|
|
|
// Find and validate container |
|
if (options.container) |
|
options.container = findContainerFor(options.container) |
|
|
|
return options |
|
} |
|
|
|
// Internal: Find container element for a variety of inputs. |
|
// |
|
// Because we can't persist elements using the history API, we must be |
|
// able to find a String selector that will consistently find the Element. |
|
// |
|
// container - A selector String, jQuery object, or DOM Element. |
|
// |
|
// Returns a jQuery object whose context is `document` and has a selector. |
|
function findContainerFor(container) { |
|
container = $(container) |
|
|
|
if ( !container.length ) { |
|
throw "no pjax container for " + container.selector |
|
} else if ( container.selector !== '' && container.context === document ) { |
|
return container |
|
} else if ( container.attr('id') ) { |
|
return $('#' + container.attr('id')) |
|
} else { |
|
throw "cant get selector for pjax container!" |
|
} |
|
} |
|
|
|
// Internal: Filter and find all elements matching the selector. |
|
// |
|
// Where $.fn.find only matches descendants, findAll will test all the |
|
// top level elements in the jQuery object as well. |
|
// |
|
// elems - jQuery object of Elements |
|
// selector - String selector to match |
|
// |
|
// Returns a jQuery object. |
|
function findAll(elems, selector) { |
|
return elems.filter(selector).add(elems.find(selector)); |
|
} |
|
|
|
function parseHTML(html) { |
|
return $.parseHTML(html, document, true) |
|
} |
|
|
|
// Internal: Extracts container and metadata from response. |
|
// |
|
// 1. Extracts X-PJAX-URL header if set |
|
// 2. Extracts inline <title> tags |
|
// 3. Builds response Element and extracts fragment if set |
|
// |
|
// data - String response data |
|
// xhr - XHR response |
|
// options - pjax options Object |
|
// |
|
// Returns an Object with url, title, and contents keys. |
|
function extractContainer(data, xhr, options) { |
|
var obj = {} |
|
|
|
// Prefer X-PJAX-URL header if it was set, otherwise fallback to |
|
// using the original requested url. |
|
obj.url = stripPjaxParam(xhr.getResponseHeader('X-PJAX-URL') || options.requestUrl) |
|
|
|
// Attempt to parse response html into elements |
|
if (/<html/i.test(data)) { |
|
var $head = $(parseHTML(data.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0])) |
|
var $body = $(parseHTML(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0])) |
|
} else { |
|
var $head = $body = $(parseHTML(data)) |
|
} |
|
|
|
// If response data is empty, return fast |
|
if ($body.length === 0) |
|
return obj |
|
|
|
// If there's a <title> tag in the header, use it as |
|
// the page's title. |
|
obj.title = findAll($head, 'title').last().text() |
|
|
|
if (options.fragment) { |
|
// If they specified a fragment, look for it in the response |
|
// and pull it out. |
|
if (options.fragment === 'body') { |
|
var $fragment = $body |
|
} else { |
|
var $fragment = findAll($body, options.fragment).first() |
|
} |
|
|
|
if ($fragment.length) { |
|
obj.contents = $fragment.contents() |
|
|
|
// If there's no title, look for data-title and title attributes |
|
// on the fragment |
|
if (!obj.title) |
|
obj.title = $fragment.attr('title') || $fragment.data('title') |
|
} |
|
|
|
} else if (!/<html/i.test(data)) { |
|
obj.contents = $body |
|
} |
|
|
|
// Clean up any <title> tags |
|
if (obj.contents) { |
|
// Remove any parent title elements |
|
obj.contents = obj.contents.not(function() { return $(this).is('title') }) |
|
|
|
// Then scrub any titles from their descendants |
|
obj.contents.find('title').remove() |
|
|
|
// Gather all script[src] elements |
|
obj.scripts = findAll(obj.contents, 'script[src]').remove() |
|
obj.contents = obj.contents.not(obj.scripts) |
|
} |
|
|
|
// Trim any whitespace off the title |
|
if (obj.title) obj.title = $.trim(obj.title) |
|
|
|
return obj |
|
} |
|
|
|
// Load an execute scripts using standard script request. |
|
// |
|
// Avoids jQuery's traditional $.getScript which does a XHR request and |
|
// globalEval. |
|
// |
|
// scripts - jQuery object of script Elements |
|
// |
|
// Returns nothing. |
|
function executeScriptTags(scripts) { |
|
if (!scripts) return |
|
|
|
var existingScripts = $('script[src]') |
|
|
|
scripts.each(function() { |
|
var src = this.src |
|
var matchedScripts = existingScripts.filter(function() { |
|
return this.src === src |
|
}) |
|
if (matchedScripts.length) return |
|
|
|
var script = document.createElement('script') |
|
script.type = $(this).attr('type') |
|
script.src = $(this).attr('src') |
|
document.head.appendChild(script) |
|
}) |
|
} |
|
|
|
// Internal: History DOM caching class. |
|
var cacheMapping = {} |
|
var cacheForwardStack = [] |
|
var cacheBackStack = [] |
|
|
|
// Push previous state id and container contents into the history |
|
// cache. Should be called in conjunction with `pushState` to save the |
|
// previous container contents. |
|
// |
|
// id - State ID Number |
|
// value - DOM Element to cache |
|
// |
|
// Returns nothing. |
|
function cachePush(id, value) { |
|
cacheMapping[id] = value |
|
cacheBackStack.push(id) |
|
|
|
// Remove all entires in forward history stack after pushing |
|
// a new page. |
|
while (cacheForwardStack.length) |
|
delete cacheMapping[cacheForwardStack.shift()] |
|
|
|
// Trim back history stack to max cache length. |
|
while (cacheBackStack.length > pjax.defaults.maxCacheLength) |
|
delete cacheMapping[cacheBackStack.shift()] |
|
} |
|
|
|
// Shifts cache from directional history cache. Should be |
|
// called on `popstate` with the previous state id and container |
|
// contents. |
|
// |
|
// direction - "forward" or "back" String |
|
// id - State ID Number |
|
// value - DOM Element to cache |
|
// |
|
// Returns nothing. |
|
function cachePop(direction, id, value) { |
|
var pushStack, popStack |
|
cacheMapping[id] = value |
|
|
|
if (direction === 'forward') { |
|
pushStack = cacheBackStack |
|
popStack = cacheForwardStack |
|
} else { |
|
pushStack = cacheForwardStack |
|
popStack = cacheBackStack |
|
} |
|
|
|
pushStack.push(id) |
|
if (id = popStack.pop()) |
|
delete cacheMapping[id] |
|
} |
|
|
|
// Public: Find version identifier for the initial page load. |
|
// |
|
// Returns String version or undefined. |
|
function findVersion() { |
|
return $('meta').filter(function() { |
|
var name = $(this).attr('http-equiv') |
|
return name && name.toUpperCase() === 'X-PJAX-VERSION' |
|
}).attr('content') |
|
} |
|
|
|
// Install pjax functions on $.pjax to enable pushState behavior. |
|
// |
|
// Does nothing if already enabled. |
|
// |
|
// Examples |
|
// |
|
// $.pjax.enable() |
|
// |
|
// Returns nothing. |
|
function enable() { |
|
$.fn.pjax = fnPjax |
|
$.pjax = pjax |
|
$.pjax.enable = $.noop |
|
$.pjax.disable = disable |
|
$.pjax.click = handleClick |
|
$.pjax.submit = handleSubmit |
|
$.pjax.reload = pjaxReload |
|
$.pjax.defaults = { |
|
timeout: 650, |
|
push: true, |
|
replace: false, |
|
type: 'GET', |
|
dataType: 'html', |
|
scrollTo: 0, |
|
maxCacheLength: 20, |
|
version: findVersion |
|
} |
|
$(window).on('popstate.pjax', onPjaxPopstate) |
|
} |
|
|
|
// Disable pushState behavior. |
|
// |
|
// This is the case when a browser doesn't support pushState. It is |
|
// sometimes useful to disable pushState for debugging on a modern |
|
// browser. |
|
// |
|
// Examples |
|
// |
|
// $.pjax.disable() |
|
// |
|
// Returns nothing. |
|
function disable() { |
|
$.fn.pjax = function() { return this } |
|
$.pjax = fallbackPjax |
|
$.pjax.enable = enable |
|
$.pjax.disable = $.noop |
|
$.pjax.click = $.noop |
|
$.pjax.submit = $.noop |
|
$.pjax.reload = function() { window.location.reload() } |
|
|
|
$(window).off('popstate.pjax', onPjaxPopstate) |
|
} |
|
|
|
|
|
// Add the state property to jQuery's event object so we can use it in |
|
// $(window).bind('popstate') |
|
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 until 5. |
|
!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/) |
|
|
|
$.support.pjax ? enable() : disable() |
|
|
|
})(jQuery);
|
|
|