Prettier fixes

This commit is contained in:
Behind The Math
2019-02-11 23:17:28 -05:00
parent 8386b355c9
commit 2e459fb7bc
16 changed files with 531 additions and 425 deletions

View File

@@ -1,51 +1,49 @@
/* global Pjax */ /* global Pjax */
var pjax; var pjax;
var initButtons = function() { var initButtons = function() {
var buttons = document.querySelectorAll("button[data-manual-trigger]") var buttons = document.querySelectorAll("button[data-manual-trigger]");
if (!buttons) { if (!buttons) {
return return;
} }
// jshint -W083 // jshint -W083
for (var i = 0; i < buttons.length; i++) { for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener("click", function(e) { buttons[i].addEventListener("click", function(e) {
var el = e.currentTarget var el = e.currentTarget;
if (el.getAttribute("data-manual-trigger-override") === "true") { if (el.getAttribute("data-manual-trigger-override") === "true") {
// Manually load URL with overridden Pjax instance options // Manually load URL with overridden Pjax instance options
pjax.loadUrl("/example/page2.html", {cacheBust: false}) pjax.loadUrl("/example/page2.html", { cacheBust: false });
} } else {
else
{
// Manually load URL with current Pjax instance options // Manually load URL with current Pjax instance options
pjax.loadUrl("/example/page2.html") pjax.loadUrl("/example/page2.html");
} }
}) });
} }
// jshint +W083 // jshint +W083
} };
console.log("Document initialized:", window.location.href) console.log("Document initialized:", window.location.href);
document.addEventListener("pjax:send", function() { document.addEventListener("pjax:send", function() {
console.log("Event: pjax:send", arguments) console.log("Event: pjax:send", arguments);
}) });
document.addEventListener("pjax:complete", function() { document.addEventListener("pjax:complete", function() {
console.log("Event: pjax:complete", arguments) console.log("Event: pjax:complete", arguments);
}) });
document.addEventListener("pjax:error", function() { document.addEventListener("pjax:error", function() {
console.log("Event: pjax:error", arguments) console.log("Event: pjax:error", arguments);
}) });
document.addEventListener("pjax:success", function() { document.addEventListener("pjax:success", function() {
console.log("Event: pjax:success", arguments) console.log("Event: pjax:success", arguments);
// Init page content // Init page content
initButtons() initButtons();
}) });
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function() {
// Init Pjax instance // Init Pjax instance
@@ -53,9 +51,9 @@ document.addEventListener("DOMContentLoaded", function() {
elements: [".js-Pjax"], elements: [".js-Pjax"],
selectors: [".body", "title"], selectors: [".body", "title"],
cacheBust: true cacheBust: true
}) });
console.log("Pjax initialized.", pjax) console.log("Pjax initialized.", pjax);
// Init page content // Init page content
initButtons() initButtons();
}) });

270
index.js
View File

@@ -1,164 +1,189 @@
var executeScripts = require("./lib/execute-scripts.js") var executeScripts = require("./lib/execute-scripts");
var forEachEls = require("./lib/foreach-els.js") var forEachEls = require("./lib/foreach-els");
var parseOptions = require("./lib/parse-options.js") var parseOptions = require("./lib/parse-options");
var switches = require("./lib/switches") var switches = require("./lib/switches");
var newUid = require("./lib/uniqueid.js") var newUid = require("./lib/uniqueid");
var on = require("./lib/events/on.js") var on = require("./lib/events/on");
var trigger = require("./lib/events/trigger.js") var trigger = require("./lib/events/trigger");
var clone = require("./lib/util/clone.js") var clone = require("./lib/util/clone");
var contains = require("./lib/util/contains.js") var contains = require("./lib/util/contains");
var extend = require("./lib/util/extend.js") var extend = require("./lib/util/extend");
var noop = require("./lib/util/noop") var noop = require("./lib/util/noop");
var Pjax = function(options) { var Pjax = function(options) {
this.state = { this.state = {
numPendingSwitches: 0, numPendingSwitches: 0,
href: null, href: null,
options: null options: null
} };
this.options = parseOptions(options);
this.options = parseOptions(options) this.log("Pjax options", this.options);
this.log("Pjax options", this.options)
if (this.options.scrollRestoration && "scrollRestoration" in history) { if (this.options.scrollRestoration && "scrollRestoration" in history) {
history.scrollRestoration = "manual" history.scrollRestoration = "manual";
} }
this.maxUid = this.lastUid = newUid() this.maxUid = this.lastUid = newUid();
this.parseDOM(document) this.parseDOM(document);
on(window, "popstate", function(st) { on(
window,
"popstate",
function(st) {
if (st.state) { if (st.state) {
var opt = clone(this.options) var opt = clone(this.options);
opt.url = st.state.url opt.url = st.state.url;
opt.title = st.state.title opt.title = st.state.title;
// Since state already exists, prevent it from being pushed again // Since state already exists, prevent it from being pushed again
opt.history = false opt.history = false;
opt.scrollPos = st.state.scrollPos opt.scrollPos = st.state.scrollPos;
if (st.state.uid < this.lastUid) { if (st.state.uid < this.lastUid) {
opt.backward = true opt.backward = true;
} else {
opt.forward = true;
} }
else { this.lastUid = st.state.uid;
opt.forward = true
}
this.lastUid = st.state.uid
// @todo implement history cache here, based on uid // @todo implement history cache here, based on uid
this.loadUrl(st.state.url, opt) this.loadUrl(st.state.url, opt);
}
}.bind(this))
} }
}.bind(this)
);
};
Pjax.switches = switches Pjax.switches = switches;
Pjax.prototype = { Pjax.prototype = {
log: require("./lib/proto/log.js"), log: require("./lib/proto/log"),
getElements: function(el) { getElements: function(el) {
return el.querySelectorAll(this.options.elements) return el.querySelectorAll(this.options.elements);
}, },
parseDOM: function(el) { parseDOM: function(el) {
var parseElement = require("./lib/proto/parse-element") var parseElement = require("./lib/proto/parse-element");
forEachEls(this.getElements(el), parseElement, this) forEachEls(this.getElements(el), parseElement, this);
}, },
refresh: function(el) { refresh: function(el) {
this.parseDOM(el || document) this.parseDOM(el || document);
}, },
reload: function() { reload: function() {
window.location.reload() window.location.reload();
}, },
attachLink: require("./lib/proto/attach-link.js"), attachLink: require("./lib/proto/attach-link"),
attachForm: require("./lib/proto/attach-form.js"), attachForm: require("./lib/proto/attach-form"),
forEachSelectors: function(cb, context, DOMcontext) { forEachSelectors: function(cb, context, DOMcontext) {
return require("./lib/foreach-selectors.js").bind(this)(this.options.selectors, cb, context, DOMcontext) return require("./lib/foreach-selectors").bind(this)(
this.options.selectors,
cb,
context,
DOMcontext
);
}, },
switchSelectors: function(selectors, fromEl, toEl, options) { switchSelectors: function(selectors, fromEl, toEl, options) {
return require("./lib/switches-selectors.js").bind(this)(this.options.switches, this.options.switchesOptions, selectors, fromEl, toEl, options) return require("./lib/switches-selectors").bind(this)(
this.options.switches,
this.options.switchesOptions,
selectors,
fromEl,
toEl,
options
);
}, },
latestChance: function(href) { latestChance: function(href) {
window.location = href window.location = href;
}, },
onSwitch: function() { onSwitch: function() {
trigger(window, "resize scroll") trigger(window, "resize scroll");
this.state.numPendingSwitches-- this.state.numPendingSwitches--;
// debounce calls, so we only run this once after all switches are finished. // debounce calls, so we only run this once after all switches are finished.
if (this.state.numPendingSwitches === 0) { if (this.state.numPendingSwitches === 0) {
this.afterAllSwitches() this.afterAllSwitches();
} }
}, },
loadContent: function(html, options) { loadContent: function(html, options) {
var tmpEl = document.implementation.createHTMLDocument("pjax") var tmpEl = document.implementation.createHTMLDocument("pjax");
// parse HTML attributes to copy them // parse HTML attributes to copy them
// since we are forced to use documentElement.innerHTML (outerHTML can't be used for <html>) // since we are forced to use documentElement.innerHTML (outerHTML can't be used for <html>)
var htmlRegex = /<html[^>]+>/gi var htmlRegex = /<html[^>]+>/gi;
var htmlAttribsRegex = /\s?[a-z:]+(?:\=(?:\'|\")[^\'\">]+(?:\'|\"))*/gi var htmlAttribsRegex = /\s?[a-z:]+(?:=['"][^'">]+['"])*/gi;
var matches = html.match(htmlRegex) var matches = html.match(htmlRegex);
if (matches && matches.length) { if (matches && matches.length) {
matches = matches[0].match(htmlAttribsRegex) matches = matches[0].match(htmlAttribsRegex);
if (matches.length) { if (matches.length) {
matches.shift() matches.shift();
matches.forEach(function(htmlAttrib) { matches.forEach(function(htmlAttrib) {
var attr = htmlAttrib.trim().split("=") var attr = htmlAttrib.trim().split("=");
if (attr.length === 1) { if (attr.length === 1) {
tmpEl.documentElement.setAttribute(attr[0], true) tmpEl.documentElement.setAttribute(attr[0], true);
} else {
tmpEl.documentElement.setAttribute(attr[0], attr[1].slice(1, -1));
} }
else { });
tmpEl.documentElement.setAttribute(attr[0], attr[1].slice(1, -1))
}
})
} }
} }
tmpEl.documentElement.innerHTML = html tmpEl.documentElement.innerHTML = html;
this.log("load content", tmpEl.documentElement.attributes, tmpEl.documentElement.innerHTML.length) this.log(
"load content",
tmpEl.documentElement.attributes,
tmpEl.documentElement.innerHTML.length
);
// Clear out any focused controls before inserting new page contents. // Clear out any focused controls before inserting new page contents.
if (document.activeElement && contains(document, this.options.selectors, document.activeElement)) { if (
document.activeElement &&
contains(document, this.options.selectors, document.activeElement)
) {
try { try {
document.activeElement.blur() document.activeElement.blur();
} catch (e) { } } catch (e) {} // eslint-disable-line no-empty
} }
this.switchSelectors(this.options.selectors, tmpEl, document, options) this.switchSelectors(this.options.selectors, tmpEl, document, options);
}, },
abortRequest: require("./lib/abort-request.js"), abortRequest: require("./lib/abort-request"),
doRequest: require("./lib/send-request.js"), doRequest: require("./lib/send-request"),
handleResponse: require("./lib/proto/handle-response.js"), handleResponse: require("./lib/proto/handle-response"),
loadUrl: function(href, options) { loadUrl: function(href, options) {
options = typeof options === "object" ? options =
extend({}, this.options, options) : typeof options === "object"
clone(this.options) ? extend({}, this.options, options)
: clone(this.options);
this.log("load href", href, options) this.log("load href", href, options);
// Abort any previous request // Abort any previous request
this.abortRequest(this.request) this.abortRequest(this.request);
trigger(document, "pjax:send", options) trigger(document, "pjax:send", options);
// Do the request // Do the request
this.request = this.doRequest(href, options, this.handleResponse.bind(this)) this.request = this.doRequest(
href,
options,
this.handleResponse.bind(this)
);
}, },
afterAllSwitches: function() { afterAllSwitches: function() {
@@ -167,114 +192,121 @@ Pjax.prototype = {
// the last field. // the last field.
// //
// http://www.w3.org/html/wg/drafts/html/master/forms.html // http://www.w3.org/html/wg/drafts/html/master/forms.html
var autofocusEl = Array.prototype.slice.call(document.querySelectorAll("[autofocus]")).pop() var autofocusEl = Array.prototype.slice
.call(document.querySelectorAll("[autofocus]"))
.pop();
if (autofocusEl && document.activeElement !== autofocusEl) { if (autofocusEl && document.activeElement !== autofocusEl) {
autofocusEl.focus() autofocusEl.focus();
} }
// execute scripts when DOM have been completely updated // execute scripts when DOM have been completely updated
this.options.selectors.forEach(function(selector) { this.options.selectors.forEach(function(selector) {
forEachEls(document.querySelectorAll(selector), function(el) { forEachEls(document.querySelectorAll(selector), function(el) {
executeScripts(el) executeScripts(el);
}) });
}) });
var state = this.state var state = this.state;
if (state.options.history) { if (state.options.history) {
if (!window.history.state) { if (!window.history.state) {
this.lastUid = this.maxUid = newUid() this.lastUid = this.maxUid = newUid();
window.history.replaceState({ window.history.replaceState(
{
url: window.location.href, url: window.location.href,
title: document.title, title: document.title,
uid: this.maxUid, uid: this.maxUid,
scrollPos: [0, 0] scrollPos: [0, 0]
}, },
document.title) document.title
);
} }
// Update browser history // Update browser history
this.lastUid = this.maxUid = newUid() this.lastUid = this.maxUid = newUid();
window.history.pushState({ window.history.pushState(
{
url: state.href, url: state.href,
title: state.options.title, title: state.options.title,
uid: this.maxUid, uid: this.maxUid,
scrollPos: [0, 0] scrollPos: [0, 0]
}, },
state.options.title, state.options.title,
state.href) state.href
);
} }
this.forEachSelectors(function(el) { this.forEachSelectors(function(el) {
this.parseDOM(el) this.parseDOM(el);
}, this) }, this);
// Fire Events // Fire Events
trigger(document,"pjax:complete pjax:success", state.options) trigger(document, "pjax:complete pjax:success", state.options);
if (typeof state.options.analytics === "function") { if (typeof state.options.analytics === "function") {
state.options.analytics() state.options.analytics();
} }
if (state.options.history) { if (state.options.history) {
// First parse url and check for hash to override scroll // First parse url and check for hash to override scroll
var a = document.createElement("a") var a = document.createElement("a");
a.href = this.state.href a.href = this.state.href;
if (a.hash) { if (a.hash) {
var name = a.hash.slice(1) var name = a.hash.slice(1);
name = decodeURIComponent(name) name = decodeURIComponent(name);
var curtop = 0 var curtop = 0;
var target = document.getElementById(name) || document.getElementsByName(name)[0] var target =
document.getElementById(name) || document.getElementsByName(name)[0];
if (target) { if (target) {
// http://stackoverflow.com/questions/8111094/cross-browser-javascript-function-to-find-actual-position-of-an-element-in-page // http://stackoverflow.com/questions/8111094/cross-browser-javascript-function-to-find-actual-position-of-an-element-in-page
if (target.offsetParent) { if (target.offsetParent) {
do { do {
curtop += target.offsetTop curtop += target.offsetTop;
target = target.offsetParent target = target.offsetParent;
} while (target) } while (target);
} }
} }
window.scrollTo(0, curtop) window.scrollTo(0, curtop);
} } else if (state.options.scrollTo !== false) {
else if (state.options.scrollTo !== false) {
// Scroll page to top on new page load // Scroll page to top on new page load
if (state.options.scrollTo.length > 1) { if (state.options.scrollTo.length > 1) {
window.scrollTo(state.options.scrollTo[0], state.options.scrollTo[1]) window.scrollTo(state.options.scrollTo[0], state.options.scrollTo[1]);
} } else {
else { window.scrollTo(0, state.options.scrollTo);
window.scrollTo(0, state.options.scrollTo)
} }
} }
} } else if (state.options.scrollRestoration && state.options.scrollPos) {
else if (state.options.scrollRestoration && state.options.scrollPos) { window.scrollTo(state.options.scrollPos[0], state.options.scrollPos[1]);
window.scrollTo(state.options.scrollPos[0], state.options.scrollPos[1])
} }
this.state = { this.state = {
numPendingSwitches: 0, numPendingSwitches: 0,
href: null, href: null,
options: null options: null
};
} }
} };
}
Pjax.isSupported = require("./lib/is-supported.js") Pjax.isSupported = require("./lib/is-supported");
// arguably could do `if( require("./lib/is-supported.js")()) {` but that might be a little to simple // arguably could do `if( require("./lib/is-supported")()) {` but that might be a little to simple
if (Pjax.isSupported()) { if (Pjax.isSupported()) {
module.exports = Pjax module.exports = Pjax;
} }
// if there isnt required browser functions, returning stupid api // if there isnt required browser functions, returning stupid api
else { else {
var stupidPjax = noop var stupidPjax = noop;
for (var key in Pjax.prototype) { for (var key in Pjax.prototype) {
if (Pjax.prototype.hasOwnProperty(key) && typeof Pjax.prototype[key] === "function") { if (
stupidPjax[key] = noop Pjax.prototype.hasOwnProperty(key) &&
typeof Pjax.prototype[key] === "function"
) {
stupidPjax[key] = noop;
} }
} }
module.exports = stupidPjax module.exports = stupidPjax;
} }

View File

@@ -1,8 +1,8 @@
var noop = require("./util/noop") var noop = require("./util/noop");
module.exports = function(request) { module.exports = function(request) {
if (request && request.readyState < 4) { if (request && request.readyState < 4) {
request.onreadystatechange = noop request.onreadystatechange = noop;
request.abort() request.abort();
} }
} };

View File

@@ -1,42 +1,48 @@
module.exports = function(el) { module.exports = function(el) {
var code = (el.text || el.textContent || el.innerHTML || "") var code = el.text || el.textContent || el.innerHTML || "";
var src = (el.src || "") var src = el.src || "";
var parent = el.parentNode || document.querySelector("head") || document.documentElement var parent =
var script = document.createElement("script") el.parentNode || document.querySelector("head") || document.documentElement;
var script = document.createElement("script");
if (code.match("document.write")) { if (code.match("document.write")) {
if (console && console.log) { if (console && console.log) {
console.log("Script contains document.write. Cant be executed correctly. Code skipped ", el) console.log(
"Script contains document.write. Cant be executed correctly. Code skipped ",
el
);
} }
return false return false;
} }
script.type = "text/javascript" script.type = "text/javascript";
script.id = el.id; script.id = el.id;
/* istanbul ignore if */ /* istanbul ignore if */
if (src !== "") { if (src !== "") {
script.src = src script.src = src;
script.async = false // force synchronous loading of peripheral JS script.async = false; // force synchronous loading of peripheral JS
} }
if (code !== "") { if (code !== "") {
try { try {
script.appendChild(document.createTextNode(code)) script.appendChild(document.createTextNode(code));
} } catch (e) {
catch (e) {
/* istanbul ignore next */ /* istanbul ignore next */
// old IEs have funky script nodes // old IEs have funky script nodes
script.text = code script.text = code;
} }
} }
// execute // execute
parent.appendChild(script) parent.appendChild(script);
// avoid pollution only in head or body tags // avoid pollution only in head or body tags
if ((parent instanceof HTMLHeadElement || parent instanceof HTMLBodyElement) && parent.contains(script)) { if (
parent.removeChild(script) (parent instanceof HTMLHeadElement || parent instanceof HTMLBodyElement) &&
parent.contains(script)
) {
parent.removeChild(script);
} }
return true return true;
} };

View File

@@ -1,18 +1,18 @@
var forEachEls = require("./foreach-els") var forEachEls = require("./foreach-els");
var evalScript = require("./eval-script") var evalScript = require("./eval-script");
// Finds and executes scripts (used for newly added elements) // Finds and executes scripts (used for newly added elements)
// Needed since innerHTML does not run scripts // Needed since innerHTML does not run scripts
module.exports = function(el) { module.exports = function(el) {
if (el.tagName.toLowerCase() === "script") { if (el.tagName.toLowerCase() === "script") {
evalScript(el) evalScript(el);
} }
forEachEls(el.querySelectorAll("script"), function(script) { forEachEls(el.querySelectorAll("script"), function(script) {
if (!script.type || script.type.toLowerCase() === "text/javascript") { if (!script.type || script.type.toLowerCase() === "text/javascript") {
if (script.parentNode) { if (script.parentNode) {
script.parentNode.removeChild(script) script.parentNode.removeChild(script);
} }
evalScript(script) evalScript(script);
} }
}) });
} };

View File

@@ -1,9 +1,13 @@
/* global HTMLCollection: true */ /* global HTMLCollection: true */
module.exports = function(els, fn, context) { module.exports = function(els, fn, context) {
if (els instanceof HTMLCollection || els instanceof NodeList || els instanceof Array) { if (
return Array.prototype.forEach.call(els, fn, context) els instanceof HTMLCollection ||
els instanceof NodeList ||
els instanceof Array
) {
return Array.prototype.forEach.call(els, fn, context);
} }
// assume simple DOM element // assume simple DOM element
return fn.call(context, els) return fn.call(context, els);
} };

View File

@@ -1,8 +1,8 @@
var forEachEls = require("./foreach-els") var forEachEls = require("./foreach-els");
module.exports = function(selectors, cb, context, DOMcontext) { module.exports = function(selectors, cb, context, DOMcontext) {
DOMcontext = DOMcontext || document DOMcontext = DOMcontext || document;
selectors.forEach(function(selector) { selectors.forEach(function(selector) {
forEachEls(DOMcontext.querySelectorAll(selector), cb, context) forEachEls(DOMcontext.querySelectorAll(selector), cb, context);
}) });
} };

View File

@@ -1,8 +1,12 @@
module.exports = function() { module.exports = function() {
// Borrowed wholesale from https://github.com/defunkt/jquery-pjax // Borrowed wholesale from https://github.com/defunkt/jquery-pjax
return window.history && return (
window.history &&
window.history.pushState && window.history.pushState &&
window.history.replaceState && window.history.replaceState &&
// pushState isnt reliable on iOS until 5. // pushState isnt reliable on iOS until 5.
!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/) !navigator.userAgent.match(
} /((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/
)
);
};

View File

@@ -1,41 +1,53 @@
/* global _gaq: true, ga: true */ /* global _gaq: true, ga: true */
var defaultSwitches = require("./switches") var defaultSwitches = require("./switches");
module.exports = function(options) { module.exports = function(options) {
options = options || {} options = options || {};
options.elements = options.elements || "a[href], form[action]" options.elements = options.elements || "a[href], form[action]";
options.selectors = options.selectors || ["title", ".js-Pjax"] options.selectors = options.selectors || ["title", ".js-Pjax"];
options.switches = options.switches || {} options.switches = options.switches || {};
options.switchesOptions = options.switchesOptions || {} options.switchesOptions = options.switchesOptions || {};
options.history = (typeof options.history === "undefined") ? true : options.history options.history =
options.analytics = (typeof options.analytics === "function" || options.analytics === false) ? options.analytics : defaultAnalytics typeof options.history === "undefined" ? true : options.history;
options.scrollTo = (typeof options.scrollTo === "undefined") ? 0 : options.scrollTo options.analytics =
options.scrollRestoration = (typeof options.scrollRestoration !== "undefined") ? options.scrollRestoration : true typeof options.analytics === "function" || options.analytics === false
options.cacheBust = (typeof options.cacheBust === "undefined") ? true : options.cacheBust ? options.analytics
options.debug = options.debug || false : defaultAnalytics;
options.timeout = options.timeout || 0 options.scrollTo =
options.currentUrlFullReload = (typeof options.currentUrlFullReload === "undefined") ? false : options.currentUrlFullReload typeof options.scrollTo === "undefined" ? 0 : options.scrollTo;
options.scrollRestoration =
typeof options.scrollRestoration !== "undefined"
? options.scrollRestoration
: true;
options.cacheBust =
typeof options.cacheBust === "undefined" ? true : options.cacheBust;
options.debug = options.debug || false;
options.timeout = options.timeout || 0;
options.currentUrlFullReload =
typeof options.currentUrlFullReload === "undefined"
? false
: options.currentUrlFullReload;
// We cant replace body.outerHTML or head.outerHTML. // We cant replace body.outerHTML or head.outerHTML.
// It creates a bug where a new body or head are created in the DOM. // It creates a bug where a new body or head are created in the DOM.
// If you set head.outerHTML, a new body tag is appended, so the DOM has 2 body nodes, and vice versa // If you set head.outerHTML, a new body tag is appended, so the DOM has 2 body nodes, and vice versa
if (!options.switches.head) { if (!options.switches.head) {
options.switches.head = defaultSwitches.switchElementsAlt options.switches.head = defaultSwitches.switchElementsAlt;
} }
if (!options.switches.body) { if (!options.switches.body) {
options.switches.body = defaultSwitches.switchElementsAlt options.switches.body = defaultSwitches.switchElementsAlt;
} }
return options return options;
} };
/* istanbul ignore next */ /* istanbul ignore next */
function defaultAnalytics() { function defaultAnalytics() {
if (window._gaq) { if (window._gaq) {
_gaq.push(["_trackPageview"]) _gaq.push(["_trackPageview"]);
} }
if (window.ga) { if (window.ga) {
ga("send", "pageview", {page: location.pathname, title: document.title}) ga("send", "pageview", { page: location.pathname, title: document.title });
} }
} }

View File

@@ -1,70 +1,71 @@
var clone = require("../util/clone.js") var clone = require("../util/clone");
var newUid = require("../uniqueid.js") var newUid = require("../uniqueid");
var trigger = require("../events/trigger.js") var trigger = require("../events/trigger");
module.exports = function(responseText, request, href, options) { module.exports = function(responseText, request, href, options) {
options = clone(options || this.options) options = clone(options || this.options);
options.request = request options.request = request;
// Fail if unable to load HTML via AJAX // Fail if unable to load HTML via AJAX
if (responseText === false) { if (responseText === false) {
trigger(document, "pjax:complete pjax:error", options) trigger(document, "pjax:complete pjax:error", options);
return return;
} }
// push scroll position to history // push scroll position to history
var currentState = window.history.state || {} var currentState = window.history.state || {};
window.history.replaceState({ window.history.replaceState(
{
url: currentState.url || window.location.href, url: currentState.url || window.location.href,
title: currentState.title || document.title, title: currentState.title || document.title,
uid: currentState.uid || newUid(), uid: currentState.uid || newUid(),
scrollPos: [document.documentElement.scrollLeft || document.body.scrollLeft, scrollPos: [
document.documentElement.scrollTop || document.body.scrollTop] document.documentElement.scrollLeft || document.body.scrollLeft,
document.documentElement.scrollTop || document.body.scrollTop
]
}, },
document.title, window.location) document.title,
window.location.href
);
// Check for redirects // Check for redirects
var oldHref = href var oldHref = href;
if (request.responseURL) { if (request.responseURL) {
if (href !== request.responseURL) { if (href !== request.responseURL) {
href = request.responseURL href = request.responseURL;
} }
} } else if (request.getResponseHeader("X-PJAX-URL")) {
else if (request.getResponseHeader("X-PJAX-URL")) { href = request.getResponseHeader("X-PJAX-URL");
href = request.getResponseHeader("X-PJAX-URL") } else if (request.getResponseHeader("X-XHR-Redirected-To")) {
} href = request.getResponseHeader("X-XHR-Redirected-To");
else if (request.getResponseHeader("X-XHR-Redirected-To")) {
href = request.getResponseHeader("X-XHR-Redirected-To")
} }
// Add back the hash if it was removed // Add back the hash if it was removed
var a = document.createElement("a") var a = document.createElement("a");
a.href = oldHref a.href = oldHref;
var oldHash = a.hash var oldHash = a.hash;
a.href = href a.href = href;
if (oldHash && !a.hash) { if (oldHash && !a.hash) {
a.hash = oldHash a.hash = oldHash;
href = a.href href = a.href;
} }
this.state.href = href this.state.href = href;
this.state.options = options this.state.options = options;
try { try {
this.loadContent(responseText, options) this.loadContent(responseText, options);
} } catch (e) {
catch (e) { trigger(document, "pjax:error", options);
trigger(document, "pjax:error", options)
if (!this.options.debug) { if (!this.options.debug) {
if (console && console.error) { if (console && console.error) {
console.error("Pjax switch fail: ", e) console.error("Pjax switch fail: ", e);
} }
return this.latestChance(href) return this.latestChance(href);
} } else {
else { throw e;
throw e
} }
} }
} };

View File

@@ -1,78 +1,86 @@
var updateQueryString = require("./util/update-query-string"); var updateQueryString = require("./util/update-query-string");
module.exports = function(location, options, callback) { module.exports = function(location, options, callback) {
options = options || {} options = options || {};
var queryString var queryString;
var requestOptions = options.requestOptions || {} var requestOptions = options.requestOptions || {};
var requestMethod = (requestOptions.requestMethod || "GET").toUpperCase() var requestMethod = (requestOptions.requestMethod || "GET").toUpperCase();
var requestParams = requestOptions.requestParams || null var requestParams = requestOptions.requestParams || null;
var formData = requestOptions.formData || null; var formData = requestOptions.formData || null;
var requestPayload = null var requestPayload = null;
var request = new XMLHttpRequest() var request = new XMLHttpRequest();
var timeout = options.timeout || 0 var timeout = options.timeout || 0;
request.onreadystatechange = function() { request.onreadystatechange = function() {
if (request.readyState === 4) { if (request.readyState === 4) {
if (request.status === 200) { if (request.status === 200) {
callback(request.responseText, request, location, options) callback(request.responseText, request, location, options);
} } else if (request.status !== 0) {
else if (request.status !== 0) { callback(null, request, location, options);
callback(null, request, location, options)
}
} }
} }
};
request.onerror = function(e) { request.onerror = function(e) {
console.log(e) console.log(e);
callback(null, request, location, options) callback(null, request, location, options);
} };
request.ontimeout = function() { request.ontimeout = function() {
callback(null, request, location, options) callback(null, request, location, options);
} };
// Prepare the request payload for forms, if available // Prepare the request payload for forms, if available
if (requestParams && requestParams.length) { if (requestParams && requestParams.length) {
// Build query string // Build query string
queryString = (requestParams.map(function(param) {return param.name + "=" + param.value})).join("&") queryString = requestParams
.map(function(param) {
return param.name + "=" + param.value;
})
.join("&");
switch (requestMethod) { switch (requestMethod) {
case "GET": case "GET":
// Reset query string to avoid an issue with repeat submissions where checkboxes that were // Reset query string to avoid an issue with repeat submissions where checkboxes that were
// previously checked are incorrectly preserved // previously checked are incorrectly preserved
location = location.split("?")[0] location = location.split("?")[0];
// Append new query string // Append new query string
location += "?" + queryString location += "?" + queryString;
break break;
case "POST": case "POST":
// Send query string as request payload // Send query string as request payload
requestPayload = queryString requestPayload = queryString;
break break;
} }
} } else if (formData) {
else if (formData) { requestPayload = formData;
requestPayload = formData
} }
// Add a timestamp as part of the query string if cache busting is enabled // Add a timestamp as part of the query string if cache busting is enabled
if (options.cacheBust) { if (options.cacheBust) {
location = updateQueryString(location, "t", Date.now()) location = updateQueryString(location, "t", Date.now());
} }
request.open(requestMethod, location, true) request.open(requestMethod, location, true);
request.timeout = timeout request.timeout = timeout;
request.setRequestHeader("X-Requested-With", "XMLHttpRequest") request.setRequestHeader("X-Requested-With", "XMLHttpRequest");
request.setRequestHeader("X-PJAX", "true") request.setRequestHeader("X-PJAX", "true");
request.setRequestHeader("X-PJAX-Selectors", JSON.stringify(options.selectors)) request.setRequestHeader(
"X-PJAX-Selectors",
JSON.stringify(options.selectors)
);
// Send the proper header information for POST forms // Send the proper header information for POST forms
if (requestPayload && requestMethod === "POST" && !formData) { if (requestPayload && requestMethod === "POST" && !formData) {
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded") request.setRequestHeader(
"Content-Type",
"application/x-www-form-urlencoded"
);
} }
request.send(requestPayload) request.send(requestPayload);
return request return request;
} };

View File

@@ -1,37 +1,59 @@
var forEachEls = require("./foreach-els") var forEachEls = require("./foreach-els");
var defaultSwitches = require("./switches") var defaultSwitches = require("./switches");
module.exports = function(switches, switchesOptions, selectors, fromEl, toEl, options) { module.exports = function(
var switchesQueue = [] switches,
switchesOptions,
selectors,
fromEl,
toEl,
options
) {
var switchesQueue = [];
selectors.forEach(function(selector) { selectors.forEach(function(selector) {
var newEls = fromEl.querySelectorAll(selector) var newEls = fromEl.querySelectorAll(selector);
var oldEls = toEl.querySelectorAll(selector) var oldEls = toEl.querySelectorAll(selector);
if (this.log) { if (this.log) {
this.log("Pjax switch", selector, newEls, oldEls) this.log("Pjax switch", selector, newEls, oldEls);
} }
if (newEls.length !== oldEls.length) { if (newEls.length !== oldEls.length) {
throw "DOM doesnt look the same on new loaded page: " + selector + " - new " + newEls.length + ", old " + oldEls.length throw "DOM doesnt look the same on new loaded page: " +
selector +
" - new " +
newEls.length +
", old " +
oldEls.length;
} }
forEachEls(newEls, function(newEl, i) { forEachEls(
var oldEl = oldEls[i] newEls,
function(newEl, i) {
var oldEl = oldEls[i];
if (this.log) { if (this.log) {
this.log("newEl", newEl, "oldEl", oldEl) this.log("newEl", newEl, "oldEl", oldEl);
} }
var callback = (switches[selector]) ? var callback = switches[selector]
switches[selector].bind(this, oldEl, newEl, options, switchesOptions[selector]) : ? switches[selector].bind(
defaultSwitches.outerHTML.bind(this, oldEl, newEl, options) this,
oldEl,
newEl,
options,
switchesOptions[selector]
)
: defaultSwitches.outerHTML.bind(this, oldEl, newEl, options);
switchesQueue.push(callback) switchesQueue.push(callback);
}, this) },
}, this) this
);
}, this);
this.state.numPendingSwitches = switchesQueue.length this.state.numPendingSwitches = switchesQueue.length;
switchesQueue.forEach(function(queuedSwitch) { switchesQueue.forEach(function(queuedSwitch) {
queuedSwitch() queuedSwitch();
}) });
} };

View File

@@ -1,122 +1,140 @@
var on = require("./events/on.js") var on = require("./events/on");
module.exports = { module.exports = {
outerHTML: function(oldEl, newEl) { outerHTML: function(oldEl, newEl) {
oldEl.outerHTML = newEl.outerHTML oldEl.outerHTML = newEl.outerHTML;
this.onSwitch() this.onSwitch();
}, },
innerHTML: function(oldEl, newEl) { innerHTML: function(oldEl, newEl) {
oldEl.innerHTML = newEl.innerHTML oldEl.innerHTML = newEl.innerHTML;
if (newEl.className === "") { if (newEl.className === "") {
oldEl.removeAttribute("class") oldEl.removeAttribute("class");
} } else {
else { oldEl.className = newEl.className;
oldEl.className = newEl.className
} }
this.onSwitch() this.onSwitch();
}, },
switchElementsAlt: function(oldEl, newEl) { switchElementsAlt: function(oldEl, newEl) {
oldEl.innerHTML = newEl.innerHTML oldEl.innerHTML = newEl.innerHTML;
// Copy attributes from the new element to the old one // Copy attributes from the new element to the old one
if (newEl.hasAttributes()) { if (newEl.hasAttributes()) {
var attrs = newEl.attributes var attrs = newEl.attributes;
for (var i = 0; i < attrs.length; i++) { for (var i = 0; i < attrs.length; i++) {
oldEl.attributes.setNamedItem(attrs[i].cloneNode()) oldEl.attributes.setNamedItem(attrs[i].cloneNode());
} }
} }
this.onSwitch() this.onSwitch();
}, },
// Equivalent to outerHTML(), but doesn't require switchElementsAlt() for <head> and <body> // Equivalent to outerHTML(), but doesn't require switchElementsAlt() for <head> and <body>
replaceNode: function(oldEl, newEl) { replaceNode: function(oldEl, newEl) {
oldEl.parentNode.replaceChild(newEl, oldEl) oldEl.parentNode.replaceChild(newEl, oldEl);
this.onSwitch() this.onSwitch();
}, },
sideBySide: function(oldEl, newEl, options, switchOptions) { sideBySide: function(oldEl, newEl, options, switchOptions) {
var forEach = Array.prototype.forEach var forEach = Array.prototype.forEach;
var elsToRemove = [] var elsToRemove = [];
var elsToAdd = [] var elsToAdd = [];
var fragToAppend = document.createDocumentFragment() var fragToAppend = document.createDocumentFragment();
var animationEventNames = "animationend webkitAnimationEnd MSAnimationEnd oanimationend" var animationEventNames =
var animatedElsNumber = 0 "animationend webkitAnimationEnd MSAnimationEnd oanimationend";
var animatedElsNumber = 0;
var sexyAnimationEnd = function(e) { var sexyAnimationEnd = function(e) {
if (e.target !== e.currentTarget) { if (e.target !== e.currentTarget) {
// end triggered by an animation on a child // end triggered by an animation on a child
return return;
} }
animatedElsNumber-- animatedElsNumber--;
if (animatedElsNumber <= 0 && elsToRemove) { if (animatedElsNumber <= 0 && elsToRemove) {
elsToRemove.forEach(function(el) { elsToRemove.forEach(function(el) {
// browsing quickly can make the el // browsing quickly can make the el
// already removed by last page update ? // already removed by last page update ?
if (el.parentNode) { if (el.parentNode) {
el.parentNode.removeChild(el) el.parentNode.removeChild(el);
} }
}) });
elsToAdd.forEach(function(el) { elsToAdd.forEach(function(el) {
el.className = el.className.replace(el.getAttribute("data-pjax-classes"), "") el.className = el.className.replace(
el.removeAttribute("data-pjax-classes") el.getAttribute("data-pjax-classes"),
}) ""
);
el.removeAttribute("data-pjax-classes");
});
elsToAdd = null // free memory elsToAdd = null; // free memory
elsToRemove = null // free memory elsToRemove = null; // free memory
// this is to trigger some repaint (example: picturefill) // this is to trigger some repaint (example: picturefill)
this.onSwitch() this.onSwitch();
} }
}.bind(this) }.bind(this);
switchOptions = switchOptions || {} switchOptions = switchOptions || {};
forEach.call(oldEl.childNodes, function(el) { forEach.call(oldEl.childNodes, function(el) {
elsToRemove.push(el) elsToRemove.push(el);
if (el.classList && !el.classList.contains("js-Pjax-remove")) { if (el.classList && !el.classList.contains("js-Pjax-remove")) {
// for fast switch, clean element that just have been added, & not cleaned yet. // for fast switch, clean element that just have been added, & not cleaned yet.
if (el.hasAttribute("data-pjax-classes")) { if (el.hasAttribute("data-pjax-classes")) {
el.className = el.className.replace(el.getAttribute("data-pjax-classes"), "") el.className = el.className.replace(
el.removeAttribute("data-pjax-classes") el.getAttribute("data-pjax-classes"),
""
);
el.removeAttribute("data-pjax-classes");
} }
el.classList.add("js-Pjax-remove") el.classList.add("js-Pjax-remove");
if (switchOptions.callbacks && switchOptions.callbacks.removeElement) { if (switchOptions.callbacks && switchOptions.callbacks.removeElement) {
switchOptions.callbacks.removeElement(el) switchOptions.callbacks.removeElement(el);
} }
if (switchOptions.classNames) { if (switchOptions.classNames) {
el.className += " " + switchOptions.classNames.remove + " " + (options.backward ? switchOptions.classNames.backward : switchOptions.classNames.forward) el.className +=
" " +
switchOptions.classNames.remove +
" " +
(options.backward
? switchOptions.classNames.backward
: switchOptions.classNames.forward);
} }
animatedElsNumber++ animatedElsNumber++;
on(el, animationEventNames, sexyAnimationEnd, true) on(el, animationEventNames, sexyAnimationEnd, true);
} }
}) });
forEach.call(newEl.childNodes, function(el) { forEach.call(newEl.childNodes, function(el) {
if (el.classList) { if (el.classList) {
var addClasses = "" var addClasses = "";
if (switchOptions.classNames) { if (switchOptions.classNames) {
addClasses = " js-Pjax-add " + switchOptions.classNames.add + " " + (options.backward ? switchOptions.classNames.forward : switchOptions.classNames.backward) addClasses =
" js-Pjax-add " +
switchOptions.classNames.add +
" " +
(options.backward
? switchOptions.classNames.forward
: switchOptions.classNames.backward);
} }
if (switchOptions.callbacks && switchOptions.callbacks.addElement) { if (switchOptions.callbacks && switchOptions.callbacks.addElement) {
switchOptions.callbacks.addElement(el) switchOptions.callbacks.addElement(el);
} }
el.className += addClasses el.className += addClasses;
el.setAttribute("data-pjax-classes", addClasses) el.setAttribute("data-pjax-classes", addClasses);
elsToAdd.push(el) elsToAdd.push(el);
fragToAppend.appendChild(el) fragToAppend.appendChild(el);
animatedElsNumber++ animatedElsNumber++;
on(el, animationEventNames, sexyAnimationEnd, true) on(el, animationEventNames, sexyAnimationEnd, true);
} }
}) });
// pass all className of the parent // pass all className of the parent
oldEl.className = newEl.className oldEl.className = newEl.className;
oldEl.appendChild(fragToAppend) oldEl.appendChild(fragToAppend);
} }
} };

View File

@@ -1,8 +1,8 @@
module.exports = (function() { module.exports = (function() {
var counter = 0 var counter = 0;
return function() { return function() {
var id = ("pjax" + (new Date().getTime())) + "_" + counter var id = "pjax" + new Date().getTime() + "_" + counter;
counter++ counter++;
return id return id;
} };
})() })();

View File

@@ -1,49 +1,50 @@
var tape = require("tape") var tape = require("tape");
var parseOptions = require("../../lib/parse-options.js");
var parseOptions = require("../../lib/parse-options.js")
tape("test parse initalization options function", function(t) { tape("test parse initalization options function", function(t) {
t.test("- default options", function(t) { t.test("- default options", function(t) {
var pjax = {} var pjax = {};
pjax.options = parseOptions({}) pjax.options = parseOptions({});
t.equal(pjax.options.elements, "a[href], form[action]") t.equal(pjax.options.elements, "a[href], form[action]");
t.equal(pjax.options.selectors.length, 2, "selectors length") t.equal(pjax.options.selectors.length, 2, "selectors length");
t.equal(pjax.options.selectors[0], "title") t.equal(pjax.options.selectors[0], "title");
t.equal(pjax.options.selectors[1], ".js-Pjax") t.equal(pjax.options.selectors[1], ".js-Pjax");
t.equal(typeof pjax.options.switches, "object") t.equal(typeof pjax.options.switches, "object");
t.equal(Object.keys(pjax.options.switches).length, 2)// head and body t.equal(Object.keys(pjax.options.switches).length, 2); // head and body
t.equal(typeof pjax.options.switchesOptions, "object") t.equal(typeof pjax.options.switchesOptions, "object");
t.equal(Object.keys(pjax.options.switchesOptions).length, 0) t.equal(Object.keys(pjax.options.switchesOptions).length, 0);
t.equal(pjax.options.history, true) t.equal(pjax.options.history, true);
t.equal(typeof pjax.options.analytics, "function") t.equal(typeof pjax.options.analytics, "function");
t.equal(pjax.options.scrollTo, 0) t.equal(pjax.options.scrollTo, 0);
t.equal(pjax.options.scrollRestoration, true) t.equal(pjax.options.scrollRestoration, true);
t.equal(pjax.options.cacheBust, true) t.equal(pjax.options.cacheBust, true);
t.equal(pjax.options.debug, false) t.equal(pjax.options.debug, false);
t.equal(pjax.options.currentUrlFullReload, false) t.equal(pjax.options.currentUrlFullReload, false);
t.end() t.end();
}) });
// verify analytics always ends up as a function even when passed not a function // verify analytics always ends up as a function even when passed not a function
t.test("- analytics is a function", function(t) { t.test("- analytics is a function", function(t) {
var pjax = {} var pjax = {};
pjax.options = parseOptions({analytics: "some string"}) pjax.options = parseOptions({ analytics: "some string" });
t.deepEqual(typeof pjax.options.analytics, "function") t.deepEqual(typeof pjax.options.analytics, "function");
t.end() t.end();
}) });
// verify that the value false for scrollTo is not squashed // verify that the value false for scrollTo is not squashed
t.test("- scrollTo remains false", function(t) { t.test("- scrollTo remains false", function(t) {
var pjax = {} var pjax = {};
pjax.options = parseOptions({scrollTo: false}) pjax.options = parseOptions({ scrollTo: false });
t.deepEqual(pjax.options.scrollTo, false) t.deepEqual(pjax.options.scrollTo, false);
t.end() t.end();
}) });
t.end() t.end();
}) });

View File

@@ -1,6 +1,6 @@
var jsdomOptions = { var jsdomOptions = {
url: "https://example.org/", url: "https://example.org/",
runScripts: "dangerously" runScripts: "dangerously"
} };
require("jsdom-global")("", jsdomOptions) require("jsdom-global")("", jsdomOptions);