Fix async switches #110

Merged
BehindTheMath merged 3 commits from fix/async-switches into master 2018-01-22 10:55:30 -05:00
5 changed files with 92 additions and 58 deletions

125
index.js
View File

@@ -14,6 +14,11 @@ var defaultSwitches = require("./lib/switches")
var Pjax = function(options) { var Pjax = function(options) {
this.firstrun = true this.firstrun = true
this.state = {
numPendingSwitches: 0,
href: null,
options: null
}
var parseOptions = require("./lib/proto/parse-options.js"); var parseOptions = require("./lib/proto/parse-options.js");
parseOptions.apply(this,[options]) parseOptions.apply(this,[options])
@@ -83,8 +88,12 @@ Pjax.prototype = {
}, },
onSwitch: function() { onSwitch: function() {
this.parseDOM(document) this.state.numPendingSwitches--
trigger(window, "resize scroll")
// debounce calls, so we only run this once after all switches are finished.
if (this.state.numPendingSwitches === 0) {
this.afterAllSwitches()
}
}, },
loadContent: function(html, options) { loadContent: function(html, options) {
@@ -125,22 +134,6 @@ Pjax.prototype = {
// try { // try {
this.switchSelectors(this.options.selectors, tmpEl, document, options) this.switchSelectors(this.options.selectors, tmpEl, document, options)
// FF bug: Wont 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 = Array.prototype.slice.call(document.querySelectorAll("[autofocus]")).pop()
if (autofocusEl && document.activeElement !== autofocusEl) {
autofocusEl.focus();
}
// execute scripts when DOM have been completely updated
this.options.selectors.forEach(function(selector) {
forEachEls(document.querySelectorAll(selector), function(el) {
executeScripts(el)
})
})
// } // }
// catch(e) { // catch(e) {
// if (this.options.debug) { // if (this.options.debug) {
@@ -181,6 +174,8 @@ Pjax.prototype = {
else if (request.getResponseHeader("X-XHR-Redirected-To")) { else if (request.getResponseHeader("X-XHR-Redirected-To")) {
href = request.getResponseHeader("X-XHR-Redirected-To") href = request.getResponseHeader("X-XHR-Redirected-To")
} }
this.state.href = href
this.state.options = clone(options)
try { try {
this.loadContent(html, options) this.loadContent(html, options)
@@ -197,49 +192,79 @@ Pjax.prototype = {
throw e throw e
} }
} }
}.bind(this))
},
if (options.history) { afterAllSwitches: function() {
if (this.firstrun) { trigger(window, "resize scroll")
this.lastUid = this.maxUid = newUid()
this.firstrun = false // FF bug: Wont autofocus fields that are inserted via JS.
window.history.replaceState({ // 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 = Array.prototype.slice.call(document.querySelectorAll("[autofocus]")).pop()
if (autofocusEl && document.activeElement !== autofocusEl) {
autofocusEl.focus();
}
// execute scripts when DOM have been completely updated
this.options.selectors.forEach(function(selector) {
forEachEls(document.querySelectorAll(selector), function(el) {
executeScripts(el)
})
})
var state = this.state
if (state.options.history) {
if (this.firstrun) {
this.lastUid = this.maxUid = newUid()
this.firstrun = false
window.history.replaceState({
url: window.location.href, url: window.location.href,
title: document.title, title: document.title,
uid: this.maxUid uid: this.maxUid
}, },
document.title) document.title)
} }
// Update browser history // Update browser history
this.lastUid = this.maxUid = newUid() this.lastUid = this.maxUid = newUid()
window.history.pushState({
url: href, window.history.pushState({
title: options.title, url: state.href,
title: state.options.title,
uid: this.maxUid uid: this.maxUid
}, },
options.title, state.options.title,
href) state.href)
}
this.forEachSelectors(function(el) {
this.parseDOM(el)
}, this)
// Fire Events
trigger(document,"pjax:complete pjax:success", state.options)
state.options.analytics()
// Scroll page to top on new page load
if (state.options.scrollTo !== false) {
if (state.options.scrollTo.length > 1) {
window.scrollTo(state.options.scrollTo[0], state.options.scrollTo[1])
} }
else {
this.forEachSelectors(function(el) { window.scrollTo(0, state.options.scrollTo)
this.parseDOM(el)
}, this)
// Fire Events
trigger(document,"pjax:complete pjax:success", options)
options.analytics()
// Scroll page to top on new page load
if (options.scrollTo !== false) {
if (options.scrollTo.length > 1) {
window.scrollTo(options.scrollTo[0], options.scrollTo[1])
}
else {
window.scrollTo(0, options.scrollTo)
}
} }
}.bind(this)) }
this.state = {
numPendingSwitches: 0,
href: null,
options: null
}
} }
} }

View File

@@ -3,6 +3,8 @@ 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(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)
@@ -24,12 +26,18 @@ module.exports = function(switches, switchesOptions, selectors, fromEl, toEl, op
if (this.log) { if (this.log) {
this.log("newEl", newEl, "oldEl", oldEl) this.log("newEl", newEl, "oldEl", oldEl)
} }
if (switches[selector]) {
switches[selector].bind(this)(oldEl, newEl, options, switchesOptions[selector]) var callback = (switches[selector]) ?
} switches[selector].bind(this, oldEl, newEl, options, switchesOptions[selector]) :
else { defaultSwitches.outerHTML.bind(this, oldEl, newEl, options)
defaultSwitches.outerHTML.bind(this)(oldEl, newEl, options)
} switchesQueue.push(callback)
}, this) }, this)
}, this) }, this)
this.state.numPendingSwitches = switchesQueue.length
switchesQueue.forEach(function(queuedSwitch) {
queuedSwitch()
})
} }

View File

@@ -38,7 +38,7 @@
"lint": "jscs . && jshint . --exclude-path .gitignore", "lint": "jscs . && jshint . --exclude-path .gitignore",
"standalone": "browserify index.js --standalone Pjax > pjax.js", "standalone": "browserify index.js --standalone Pjax > pjax.js",
"build-debug": "browserify index.js --debug --standalone Pjax > pjax.js", "build-debug": "browserify index.js --debug --standalone Pjax > pjax.js",
"tests": "tape -r ./tests/index.js ./tests/**/*.js", "tests": "tape -r ./tests/setup.js ./tests/**/*.js",
"test": "npm run lint && npm run tests | tap-spec", "test": "npm run lint && npm run tests | tap-spec",
"coverage-tests": "npm run tests | tap-nyc", "coverage-tests": "npm run tests | tap-nyc",
"coverage": "nyc -x \"tests/**\" npm run coverage-tests", "coverage": "nyc -x \"tests/**\" npm run coverage-tests",

View File

@@ -9,7 +9,8 @@ tape("test switchesSelectors", function(t) {
var pjax = { var pjax = {
onSwitch: function() { onSwitch: function() {
console.log("Switched") console.log("Switched")
} },
state: {}
} }
var tmpEl = document.implementation.createHTMLDocument() var tmpEl = document.implementation.createHTMLDocument()