diff --git a/assets/js/pjax.js b/assets/js/pjax.js index 1e6b6de..7f58fa3 100644 --- a/assets/js/pjax.js +++ b/assets/js/pjax.js @@ -6,311 +6,310 @@ (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 +// 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 - String selector for the element where to place the response body. +// 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 + options = optionsFor(container, options) return this.on('click.pjax', selector, function(event) { - var opts = $.extend({}, optionsFor(container, options)) - if (!opts.container) - opts.container = $(this).attr('data-pjax') || context + var opts = options + if (!opts.container) { + opts = $.extend({}, options) + opts.container = $(this).attr('data-pjax') + } 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. + +// 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') +// +// Returns nothing. function handleClick(event, container, options) { options = optionsFor(container, options) - + var link = event.currentTarget - + var $link = $(link) + 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 case when a hash is being tacked on the current URL if ( link.href.indexOf('#') > -1 && stripHash(link) == stripHash(location) ) return - + // Ignore event with default prevented if (event.isDefaultPrevented()) return - + var defaults = { url: link.href, - container: $(link).attr('data-pjax'), + container: $link.attr('data-pjax'), target: link } - + var opts = $.extend({}, defaults, options) var clickEvent = $.Event('pjax:click') - $(link).trigger(clickEvent, [opts]) - + $link.trigger(clickEvent, [opts]) + if (!clickEvent.isDefaultPrevented()) { pjax(opts) event.preventDefault() - $(link).trigger('pjax:clicked', [opts]) + $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. + +// Public: pjax on form submit handler +// +// Exported as $.pjax.submit +// +// event - "click" jQuery.Event +// options - pjax options +// +// Examples +// +// $(document).on('submit', 'form', function(event) { +// $.pjax.submit(event, '[data-pjax-container]') +// }) +// +// Returns nothing. function handleSubmit(event, container, options) { options = optionsFor(container, options) - + var form = event.currentTarget var $form = $(form) - + if (form.tagName.toUpperCase() !== 'FORM') throw "$.pjax.submit requires a form element" - + var defaults = { type: ($form.attr('method') || 'GET').toUpperCase(), url: $form.attr('action'), container: $form.attr('data-pjax'), target: form } - + if (defaults.type !== 'GET' && window.FormData !== undefined) { - defaults.data = new FormData(form); - defaults.processData = false; - defaults.contentType = false; + defaults.data = new FormData(form) + defaults.processData = false + defaults.contentType = false } else { // Can't handle file uploads, exit - if ($(form).find(':file').length) { - return; + if ($form.find(':file').length) { + return } - + // Fallback to manually serializing the fields - defaults.data = $(form).serializeArray(); + defaults.data = $form.serializeArray() } - + 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. + +// 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 - String selector for where to stick the response body. +// 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) - + + var containerType = $.type(options.container) + if (containerType !== 'string') { + throw "expected string value for 'container' option; got " + containerType + } + var context = options.context = $(options.container) + if (!context.length) { + throw "the container selector '" + options.container + "' did not match anything" + } + // 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 = {} if ($.isArray(options.data)) { - options.data.push({ name: '_pjax', value: context.selector }) + options.data.push({name: '_pjax', value: options.container}) } else { - options.data._pjax = context.selector + options.data._pjax = options.container } - // + function fire(type, args, props) { if (!props) props = {} - props.relatedTarget = target + props.relatedTarget = options.target var event = $.Event(type, props) 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) - + xhr.setRequestHeader('X-PJAX-Container', options.container) + 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 } - + var url = parseURL(settings.url) if (hash) url.hash = hash options.requestUrl = stripInternalParams(url) } - + 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) { - var previousState = pjax.state; - + var previousState = pjax.state + // 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 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) - + var url = parseURL(container.url) if (hash) { url.hash = hash container.url = url.href } - + // 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, + container: options.container, fragment: options.fragment, timeout: options.timeout } - + if (options.push || options.replace) { window.history.replaceState(pjax.state, container.title, container.url) } - + // Only blur the focus if the focused element is within the container. - var blurFocus = $.contains(options.container, document.activeElement) - + var blurFocus = $.contains(context, document.activeElement) + // Clear out any focused controls before inserting new page contents. if (blurFocus) { try { document.activeElement.blur() - } catch (e) { } + } catch (e) { /* ignore */ } } - + if (container.title) document.title = container.title - + fire('pjax:beforeReplace', [container.contents, options], { state: pjax.state, previousState: previousState }) 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. @@ -318,26 +317,26 @@ // 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(); + autofocusEl.focus() } - + executeScriptTags(container.scripts) - + var scrollTo = options.scrollTo - + // Ensure browser scrolls to the element referenced by the URL anchor if (hash) { var name = decodeURIComponent(hash.slice(1)) var target = document.getElementById(name) || document.getElementsByName(name)[0] if (target) scrollTo = $(target).offset().top } - + if (typeof scrollTo == 'number') $(window).scrollTop(scrollTo) - + 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 @@ -347,37 +346,37 @@ id: uniqueId(), url: window.location.href, title: document.title, - container: context.selector, + container: options.container, fragment: options.fragment, timeout: options.timeout } window.history.replaceState(pjax.state, document.title) } - + // Cancel the current request if we're already pjaxing abortXHR(pjax.xhr) - + 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, cloneContents(context)) - + cachePush(pjax.state.id, [options.container, cloneContents(context)]) + window.history.pushState(null, "", 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. + +// Public: Reload current page with pjax. +// +// Returns whatever $.pjax returns. function pjaxReload(container, options) { var defaults = { url: window.location.href, @@ -385,97 +384,98 @@ 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. + +// 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, "", pjax.state.url) 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. + +// 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 + +// 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. + +// 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) { - // Hitting back or forward should override any pending PJAX request. - if (!initialPop) { + + // Hitting back or forward should override any pending PJAX request. + if (!initialPop) { abortXHR(pjax.xhr) } - + var previousState = pjax.state var state = event.state var direction - + 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 (previousState) { // If popping back to the same state, just skip. // Could be clicking back from hashchange rather than a pushState. if (previousState.id === state.id) return - + // Since state IDs always increase, we can deduce the navigation direction direction = previousState.id < state.id ? 'forward' : 'back' } - //changed by jackchain cancel cache; + var cache = cacheMapping[state.id] || [] - //console.log(cache); - var container = $(cache[0] || state.container), contents = cache[1] - //console.log(container); + var containerSelector = cache[0] || state.container + var container = $(containerSelector), contents = cache[1] + if (container.length) { if (previousState) { // Cache current container before replacement and inform the // cache which direction the history shifted. - cachePop(direction, previousState.id, cloneContents(container)) + cachePop(direction, previousState.id, [containerSelector, cloneContents(container)]) } - + var popstateEvent = $.Event('pjax:popstate', { state: state, direction: direction }) container.trigger(popstateEvent) - + var options = { id: state.id, url: state.url, - container: container, + container: containerSelector, push: false, fragment: state.fragment, timeout: state.timeout, scrollTo: false } - + if (contents) { container.trigger('pjax:start', [null, options]) - + pjax.state = state if (state.title) document.title = state.title var beforeReplaceEvent = $.Event('pjax:beforeReplace', { @@ -484,36 +484,36 @@ }) container.trigger(beforeReplaceEvent, [contents, options]) container.html(contents) - + container.trigger('pjax:end', [null, options]) } else { pjax(options) } - + // Force reflow/relayout before the browser tries to restore the // scroll position. - container[0].offsetHeight + container[0].offsetHeight // eslint-disable-line no-unused-expressions } else { - locationReplace(location.href) + 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. + +// 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 = $('
', { method: method === 'GET' ? 'GET' : 'POST', action: url, style: 'display:none' }) - + if (method !== 'GET' && method !== 'POST') { form.append($('', { type: 'hidden', @@ -521,7 +521,7 @@ value: method.toLowerCase() })) } - + var data = options.data if (typeof data === 'string') { $.each(data.split('&'), function(index, value) { @@ -537,244 +537,212 @@ for (key in data) form.append($('', {type: 'hidden', name: key, value: data[key]})) } - + $(document.body).append(form) form.submit() } - - // Internal: Abort an XmlHttpRequest if it hasn't been completed, - // also removing its event handlers. + +// Internal: Abort an XmlHttpRequest if it hasn't been completed, +// also removing its event handlers. function abortXHR(xhr) { if ( xhr && xhr.readyState < 4) { xhr.onreadystatechange = $.noop xhr.abort() } } - - // 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. + +// 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() } - + function cloneContents(container) { var cloned = container.clone() // Unmark script tags as already being eval'd so they can get executed again // when restored from cache. HAXX: Uses jQuery internal method. cloned.find('script').each(function(){ - if (!this.src) jQuery._data(this, 'globalEval', false) + if (!this.src) $._data(this, 'globalEval', false) }) - return [container.selector, cloned.contents()] + return cloned.contents() } - - // Internal: Strip internal query params from parsed URL. - // - // Returns sanitized url.href String. + +// Internal: Strip internal query params from parsed URL. +// +// Returns sanitized url.href String. function stripInternalParams(url) { - url.search = url.search.replace(/([?&])(_pjax|_)=[^&]*/g, '') + url.search = url.search.replace(/([?&])(_pjax|_)=[^&]*/g, '').replace(/^&/, '') return url.href.replace(/\?($|#)/, '$1') } - - // Internal: Parse URL components and returns a Locationish object. - // - // url - String URL - // - // Returns HTMLAnchorElement that acts like Location. + +// 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: Return the `href` component of given URL object with the hash - // portion removed. - // - // location - Location or HTMLAnchorElement - // - // Returns String + +// Internal: Return the `href` component of given URL object with the hash +// portion removed. +// +// location - Location or HTMLAnchorElement +// +// Returns String function stripHash(location) { return location.href.replace(/#.*/, '') } - - // 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. + +// 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 ) + if (container && options) { + options = $.extend({}, 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 options + } else if ($.isPlainObject(container)) { return container - } else if ( container.attr('id') ) { - return $('#' + container.attr('id')) } else { - throw "cant get selector for pjax container!" + return {container: 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. + +// 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)); + 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 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. + +// 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 = {}, fullDocument = /<html/i.test(data) - + // Prefer X-PJAX-URL header if it was set, otherwise fallback to // using the original requested url. var serverUrl = xhr.getResponseHeader('X-PJAX-URL') obj.url = serverUrl ? stripInternalParams(parseURL(serverUrl)) : options.requestUrl - + + var $head, $body // Attempt to parse response html into elements if (fullDocument) { - var $head = $(parseHTML(data.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0])) - var $body = $(parseHTML(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0])) + $body = $(parseHTML(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0])) + var head = data.match(/<head[^>]*>([\s\S.]*)<\/head>/i) + $head = head != null ? $(parseHTML(head[0])) : $body } else { - var $head = $body = $(parseHTML(data)) + $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) { + var $fragment = $body // 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 (options.fragment !== 'body') { + $fragment = findAll($fragment, options.fragment).first() } - + if ($fragment.length) { obj.contents = options.fragment === 'body' ? $fragment : $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 (!fullDocument) { 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 - //console.log(obj.contents); - //obj.scripts = findAll(obj.contents, 'script[src]').remove() - //console.log(obj.contents); - //console.log(obj.scripts); - //obj.contents = obj.contents.not(obj.scripts) - //console.log(obj.contents); + + // 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. + +// 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 src = this.src var matchedScripts = existingScripts.filter(function() { return this.src === src }) if (matchedScripts.length) return - + var script = document.createElement('script') var type = $(this).attr('type') if (type) script.type = type @@ -782,44 +750,44 @@ document.head.appendChild(script) }) } - - // Internal: History DOM caching class. + +// 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. + +// 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 entries in forward history stack after pushing a new page. trimCacheStack(cacheForwardStack, 0) - + // Trim back history stack to max cache length. trimCacheStack(cacheBackStack, pjax.defaults.maxCacheLength) } - - // 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. + +// 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 @@ -827,46 +795,46 @@ pushStack = cacheForwardStack popStack = cacheBackStack } - + pushStack.push(id) - if (id = popStack.pop()) - delete cacheMapping[id] - + id = popStack.pop() + if (id) delete cacheMapping[id] + // Trim whichever stack we just pushed to to max cache length. trimCacheStack(pushStack, pjax.defaults.maxCacheLength) } - - // Trim a cache stack (either cacheBackStack or cacheForwardStack) to be no - // longer than the specified length, deleting cached DOM elements as necessary. - // - // stack - Array of state IDs - // length - Maximum length to trim to - // - // Returns nothing. + +// Trim a cache stack (either cacheBackStack or cacheForwardStack) to be no +// longer than the specified length, deleting cached DOM elements as necessary. +// +// stack - Array of state IDs +// length - Maximum length to trim to +// +// Returns nothing. function trimCacheStack(stack, length) { while (stack.length > length) delete cacheMapping[stack.shift()] } - - // Public: Find version identifier for the initial page load. - // - // Returns String version or undefined. + +// 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. + +// 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 @@ -882,24 +850,23 @@ type: 'GET', dataType: 'html', scrollTo: 0, - maxCacheLength: 0, - //cache:false,//changed by jackchain fire IE cache bug + 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. + +// 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 @@ -908,22 +875,29 @@ $.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 ) + + +// Add the state property to jQuery's event object so we can use it in +// $(window).bind('popstate') + if ($.event.props && $.inArray('state', $.event.props) < 0) { $.event.props.push('state') - - // Is pjax supported by this browser? + } else if (!('state' in $.Event.prototype)) { + $.event.addProp('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]\D|WebApps\/.+CFNetwork)/) - - $.support.pjax ? enable() : disable() - - })(jQuery); \ No newline at end of file + 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]\D|WebApps\/.+CFNetwork)/) + + if ($.support.pjax) { + enable() + } else { + disable() + } + +})(jQuery) \ No newline at end of file