diff --git a/framework/assets/pjax/jquery.pjax.js b/framework/assets/pjax/jquery.pjax.js index 9b11ce4..1934d80 100644 --- a/framework/assets/pjax/jquery.pjax.js +++ b/framework/assets/pjax/jquery.pjax.js @@ -27,15 +27,15 @@ // 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) - }) -} + 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 // @@ -56,47 +56,48 @@ function fnPjax(selector, container, options) { // }) // // Returns nothing. -function handleClick(event, container, options) { - options = optionsFor(container, options) + function handleClick(event, container, options) { + options = optionsFor(container, options) - var link = event.currentTarget + var link = event.currentTarget - if (link.tagName.toUpperCase() !== 'A') - throw "$.fn.pjax or $.pjax.click requires an anchor element" + 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 + // 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 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 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 + // Ignore empty anchor "foo.html#" + if (link.href === location.href + '#') + return - var defaults = { - url: link.href, - container: $(link).attr('data-pjax'), - target: link - } + 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]) + var opts = $.extend({}, defaults, options) + var clickEvent = $.Event('pjax:click') + $(link).trigger(clickEvent, [opts]) - if (!clickEvent.isDefaultPrevented()) { - pjax(opts) - event.preventDefault() - } -} + if (!clickEvent.isDefaultPrevented()) { + pjax(opts) + event.preventDefault() + $(link).trigger('pjax:clicked', [opts]) + } + } // Public: pjax on form submit handler // @@ -113,26 +114,26 @@ function handleClick(event, container, options) { // }) // // Returns nothing. -function handleSubmit(event, container, options) { - options = optionsFor(container, options) + function handleSubmit(event, container, options) { + options = optionsFor(container, options) - var form = event.currentTarget + var form = event.currentTarget - if (form.tagName.toUpperCase() !== 'FORM') - throw "$.pjax.submit requires a form element" + 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 - } + var defaults = { + type: form.method.toUpperCase(), + url: form.action, + data: $(form).serializeArray(), + container: $(form).attr('data-pjax'), + target: form + } - pjax($.extend({}, defaults, options)) + pjax($.extend({}, defaults, options)) - event.preventDefault() -} + event.preventDefault() + } // Loads a URL with ajax, puts the response body inside a container, // then pushState()'s the loaded URL. @@ -153,212 +154,212 @@ function handleSubmit(event, container, options) { // 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 + 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 -} + 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 - } + function pjaxReload(container, options) { + var defaults = { + url: window.location.href, + push: false, + replace: true, + scrollTo: false + } - return pjax($.extend(defaults, optionsFor(container, options))) -} + return pjax($.extend(defaults, optionsFor(container, options))) + } // Internal: Hard replace current state with url. // @@ -366,133 +367,133 @@ function pjaxReload(container, options) { // https://bugs.webkit.org/show_bug.cgi?id=93506 // // Returns nothing. -function locationReplace(url) { - window.history.replaceState(null, "", "#") - window.location.replace(url) -} + function locationReplace(url) { + window.history.replaceState(null, "", "#") + window.location.replace(url) + } -var initialPop = true -var initialURL = window.location.href -var initialState = window.history.state + 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 -} + if (initialState && initialState.container) { + pjax.state = initialState + } // Non-webkit browsers don't fire an initial popstate event -if ('state' in window.history) { - initialPop = false -} + 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 -} + 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 = $('