Relocate all the things
This commit is contained in:
12
lib/clone.js
Normal file
12
lib/clone.js
Normal file
@@ -0,0 +1,12 @@
|
||||
module.exports = function(obj) {
|
||||
if (null === obj || "object" != typeof obj) {
|
||||
return obj
|
||||
}
|
||||
var copy = obj.constructor()
|
||||
for (var attr in obj) {
|
||||
if (obj.hasOwnProperty(attr)) {
|
||||
copy[attr] = obj[attr]
|
||||
}
|
||||
}
|
||||
return copy
|
||||
}
|
||||
29
lib/eval-script.js
Normal file
29
lib/eval-script.js
Normal file
@@ -0,0 +1,29 @@
|
||||
module.exports = function(el) {
|
||||
// console.log("going to execute script", el)
|
||||
|
||||
var code = (el.text || el.textContent || el.innerHTML || "")
|
||||
var head = document.querySelector("head") || document.documentElement
|
||||
var script = document.createElement("script")
|
||||
|
||||
if (code.match("document.write")) {
|
||||
if (console && console.log) {
|
||||
console.log("Script contains document.write. Can’t be executed correctly. Code skipped ", el)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
script.type = "text/javascript"
|
||||
try {
|
||||
script.appendChild(document.createTextNode(code))
|
||||
}
|
||||
catch (e) {
|
||||
// old IEs have funky script nodes
|
||||
script.text = code
|
||||
}
|
||||
|
||||
// execute
|
||||
head.insertBefore(script, head.firstChild)
|
||||
head.removeChild(script) // avoid pollution
|
||||
|
||||
return true
|
||||
}
|
||||
11
lib/events/off.js
Normal file
11
lib/events/off.js
Normal file
@@ -0,0 +1,11 @@
|
||||
var forEachEls = require("../foreach-els")
|
||||
|
||||
module.exports = function(els, events, listener, useCapture) {
|
||||
events = (typeof events === "string" ? events.split(" ") : events)
|
||||
|
||||
events.forEach(function(e) {
|
||||
forEachEls(els, function(el) {
|
||||
el.removeEventListener(e, listener, useCapture)
|
||||
})
|
||||
})
|
||||
}
|
||||
11
lib/events/on.js
Normal file
11
lib/events/on.js
Normal file
@@ -0,0 +1,11 @@
|
||||
var forEachEls = require("../foreach-els")
|
||||
|
||||
module.exports = function(els, events, listener, useCapture) {
|
||||
events = (typeof events === "string" ? events.split(" ") : events)
|
||||
|
||||
events.forEach(function(e) {
|
||||
forEachEls(els, function(el) {
|
||||
el.addEventListener(e, listener, useCapture)
|
||||
})
|
||||
})
|
||||
}
|
||||
31
lib/events/trigger.js
Normal file
31
lib/events/trigger.js
Normal file
@@ -0,0 +1,31 @@
|
||||
var forEachEls = require("../foreach-els")
|
||||
|
||||
module.exports = function(els, events, opts) {
|
||||
events = (typeof events === "string" ? events.split(" ") : events)
|
||||
|
||||
events.forEach(function(e) {
|
||||
var event // = new CustomEvent(e) // doesn't everywhere yet
|
||||
event = document.createEvent("HTMLEvents")
|
||||
event.initEvent(e, true, true)
|
||||
event.eventName = e
|
||||
if (opts) {
|
||||
Object.keys(opts).forEach(function(key) {
|
||||
event[key] = opts[key]
|
||||
})
|
||||
}
|
||||
|
||||
forEachEls(els, function(el) {
|
||||
var domFix = false
|
||||
if (!el.parentNode) {
|
||||
// THANKS YOU IE (9/10//11 concerned)
|
||||
// dispatchEvent doesn't work if element is not in the dom
|
||||
domFix = true
|
||||
document.body.appendChild(el)
|
||||
}
|
||||
el.dispatchEvent(event)
|
||||
if (domFix) {
|
||||
el.parentNode.removeChild(el)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
15
lib/execute-scripts.js
Normal file
15
lib/execute-scripts.js
Normal file
@@ -0,0 +1,15 @@
|
||||
var forEachEls = require("./foreach-els")
|
||||
var evalScript = require("./eval-script")
|
||||
// Finds and executes scripts (used for newly added elements)
|
||||
// Needed since innerHTML does not run scripts
|
||||
module.exports = function(el) {
|
||||
// console.log("going to execute scripts for ", el)
|
||||
forEachEls(el.querySelectorAll("script"), function(script) {
|
||||
if (!script.type || script.type.toLowerCase() === "text/javascript") {
|
||||
if (script.parentNode) {
|
||||
script.parentNode.removeChild(script)
|
||||
}
|
||||
evalScript(script)
|
||||
}
|
||||
})
|
||||
}
|
||||
7
lib/foreach-els.js
Normal file
7
lib/foreach-els.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = function(els, fn, context) {
|
||||
if (els instanceof HTMLCollection || els instanceof NodeList || els instanceof Array) {
|
||||
return Array.prototype.forEach.call(els, fn, context)
|
||||
}
|
||||
// assume simple dom element
|
||||
return fn.call(context, els)
|
||||
}
|
||||
8
lib/foreach-selectors.js
Normal file
8
lib/foreach-selectors.js
Normal file
@@ -0,0 +1,8 @@
|
||||
var forEachEls = require("./foreach-els")
|
||||
|
||||
module.exports = function(selectors, cb, context, DOMcontext) {
|
||||
DOMcontext = DOMcontext || document
|
||||
selectors.forEach(function(selector) {
|
||||
forEachEls(DOMcontext.querySelectorAll(selector), cb, context)
|
||||
})
|
||||
}
|
||||
8
lib/is-supported.js
Normal file
8
lib/is-supported.js
Normal file
@@ -0,0 +1,8 @@
|
||||
module.exports = function() {
|
||||
// Borrowed wholesale from https://github.com/defunkt/jquery-pjax
|
||||
return 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)/)
|
||||
}
|
||||
20
lib/polyfills/Function.prototype.bind.js
Normal file
20
lib/polyfills/Function.prototype.bind.js
Normal file
@@ -0,0 +1,20 @@
|
||||
if (!Function.prototype.bind) {
|
||||
Function.prototype.bind = function (oThis) {
|
||||
if (typeof this !== "function") {
|
||||
// closest thing possible to the ECMAScript 5 internal IsCallable function
|
||||
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable")
|
||||
}
|
||||
|
||||
var aArgs = Array.prototype.slice.call(arguments, 1)
|
||||
var fToBind = this
|
||||
var fNOP = function () {}
|
||||
var fBound = function () {
|
||||
return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)))
|
||||
}
|
||||
|
||||
fNOP.prototype = this.prototype
|
||||
fBound.prototype = new fNOP()
|
||||
|
||||
return fBound
|
||||
}
|
||||
}
|
||||
76
lib/proto/attach-link.js
Normal file
76
lib/proto/attach-link.js
Normal file
@@ -0,0 +1,76 @@
|
||||
require("../polyfills/Function.prototype.bind")
|
||||
|
||||
var on = require("../events/on")
|
||||
var clone = require("../clone")
|
||||
|
||||
var attrClick = "data-pjax-click-state"
|
||||
var attrKey = "data-pjax-keyup-state"
|
||||
|
||||
var linkAction = function(el, event) {
|
||||
// Don’t break browser special behavior on links (like page in new window)
|
||||
if (event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
|
||||
el.setAttribute(attrClick, "modifier")
|
||||
return
|
||||
}
|
||||
|
||||
// we do test on href now to prevent unexpected behavior if for some reason
|
||||
// user have href that can be dynamically updated
|
||||
|
||||
// Ignore external links.
|
||||
if (el.protocol !== window.location.protocol || el.host !== window.location.host) {
|
||||
el.setAttribute(attrClick, "external")
|
||||
return
|
||||
}
|
||||
|
||||
// Ignore click if we are on an anchor on the same page
|
||||
if (el.pathname === window.location.pathname && el.hash.length > 0) {
|
||||
el.setAttribute(attrClick, "anchor-present")
|
||||
return
|
||||
}
|
||||
|
||||
// Ignore anchors on the same page (keep native behavior)
|
||||
if (el.hash && el.href.replace(el.hash, "") === window.location.href.replace(location.hash, "")) {
|
||||
el.setAttribute(attrClick, "anchor")
|
||||
return
|
||||
}
|
||||
|
||||
// Ignore empty anchor "foo.html#"
|
||||
if (el.href === window.location.href.split("#")[0] + "#") {
|
||||
el.setAttribute(attrClick, "anchor-empty")
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
|
||||
// don’t do "nothing" if user try to reload the page by clicking the same link twice
|
||||
if (el.href === window.location.href.split("#")[0]) {
|
||||
el.setAttribute(attrClick, "refresh")
|
||||
this.refresh()
|
||||
return
|
||||
}
|
||||
|
||||
el.setAttribute(attrClick, "load")
|
||||
this.loadUrl(el.href, clone(this.options))
|
||||
}
|
||||
|
||||
module.exports = function(el) {
|
||||
var instance = this
|
||||
|
||||
on(el, "click", function(event) {
|
||||
linkAction.call(instance, el, event)
|
||||
})
|
||||
|
||||
on(el, "keyup", function(event) {
|
||||
|
||||
// Don’t break browser special behavior on links (like page in new window)
|
||||
if (event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
|
||||
el.setAttribute(attrKey, "modifier")
|
||||
return
|
||||
}
|
||||
|
||||
if(event.keyCode == 13) {
|
||||
linkAction.call(instance, el, event)
|
||||
}
|
||||
|
||||
}.bind(this))
|
||||
}
|
||||
3
lib/proto/get-elements.js
Normal file
3
lib/proto/get-elements.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = function(el) {
|
||||
return el.querySelectorAll(this.options.elements)
|
||||
}
|
||||
11
lib/proto/log.js
Normal file
11
lib/proto/log.js
Normal file
@@ -0,0 +1,11 @@
|
||||
module.exports = function() {
|
||||
if (this.options.debug && console) {
|
||||
if (typeof console.log === "function") {
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
// ie is weird
|
||||
else if (console.log) {
|
||||
console.log(arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
7
lib/proto/parse-dom.js
Normal file
7
lib/proto/parse-dom.js
Normal file
@@ -0,0 +1,7 @@
|
||||
var forEachEls = require("../foreach-els")
|
||||
|
||||
var parseElement = require("../parse-element")
|
||||
|
||||
module.exports = function(el) {
|
||||
forEachEls(this.getElements(el), parseElement }, this)
|
||||
}
|
||||
14
lib/proto/parse-element.js
Normal file
14
lib/proto/parse-element.js
Normal file
@@ -0,0 +1,14 @@
|
||||
module.exports = function(el) {
|
||||
switch (el.tagName.toLowerCase()) {
|
||||
case "a":
|
||||
this.attachLink(el)
|
||||
break
|
||||
|
||||
case "form":
|
||||
throw "Pjax doesnt support <form> yet."
|
||||
break
|
||||
|
||||
default:
|
||||
throw "Pjax can only be applied on <a> or <form> submit"
|
||||
}
|
||||
}
|
||||
3
lib/refresh.js
Normal file
3
lib/refresh.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = function() {
|
||||
window.location.reload()
|
||||
}
|
||||
19
lib/request.js
Normal file
19
lib/request.js
Normal file
@@ -0,0 +1,19 @@
|
||||
module.exports = function(location, callback) {
|
||||
var request = new XMLHttpRequest()
|
||||
|
||||
request.onreadystatechange = function() {
|
||||
if (request.readyState === 4) {
|
||||
if (request.status === 200) {
|
||||
callback(request.responseText, request)
|
||||
}
|
||||
else {
|
||||
callback(null, request)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
request.open("GET", location + (!/[?&]/.test(location) ? "?": "&") + (new Date().getTime()), true)
|
||||
request.setRequestHeader("X-Requested-With", "XMLHttpRequest")
|
||||
request.send(null)
|
||||
return request
|
||||
}
|
||||
35
lib/switches-selectors.js
Normal file
35
lib/switches-selectors.js
Normal file
@@ -0,0 +1,35 @@
|
||||
var forEachEls = require("./foreach-els")
|
||||
|
||||
var defaultSwitches = require("./switches")
|
||||
|
||||
module.exports = function(switches, switchesOptions, selectors, fromEl, toEl, options) {
|
||||
selectors.forEach(function(selector) {
|
||||
var newEls = fromEl.querySelectorAll(selector)
|
||||
var oldEls = toEl.querySelectorAll(selector)
|
||||
if (this.log) {
|
||||
this.log("Pjax switch", selector, newEls, oldEls)
|
||||
}
|
||||
if (newEls.length !== oldEls.length) {
|
||||
// forEachEls(newEls, function(el) {
|
||||
// this.log("newEl", el, el.outerHTML)
|
||||
// }, this)
|
||||
// forEachEls(oldEls, function(el) {
|
||||
// this.log("oldEl", el, el.outerHTML)
|
||||
// }, this)
|
||||
throw "DOM doesn’t look the same on new loaded page: ’" + selector + "’ - new " + newEls.length + ", old " + oldEls.length
|
||||
}
|
||||
|
||||
forEachEls(newEls, function(newEl, i) {
|
||||
var oldEl = oldEls[i]
|
||||
if (this.log) {
|
||||
this.log("newEl", newEl, "oldEl", oldEl)
|
||||
}
|
||||
if (switches[selector]) {
|
||||
switches[selector].bind(this)(oldEl, newEl, options, switchesOptions[selector])
|
||||
}
|
||||
else {
|
||||
defaultSwitches.outerHTML.bind(this)(oldEl, newEl, options)
|
||||
}
|
||||
}, this)
|
||||
}, this)
|
||||
}
|
||||
110
lib/switches.js
Normal file
110
lib/switches.js
Normal file
@@ -0,0 +1,110 @@
|
||||
module.exports = {
|
||||
outerHTML: function(oldEl, newEl, options) {
|
||||
oldEl.outerHTML = newEl.outerHTML
|
||||
this.onSwitch()
|
||||
},
|
||||
|
||||
innerHTML: function(oldEl, newEl, options) {
|
||||
oldEl.innerHTML = newEl.innerHTML
|
||||
oldEl.className = newEl.className
|
||||
this.onSwitch()
|
||||
},
|
||||
|
||||
sideBySide: function(oldEl, newEl, options, switchOptions) {
|
||||
var forEach = Array.prototype.forEach
|
||||
var elsToRemove = []
|
||||
var elsToAdd = []
|
||||
var fragToAppend = document.createDocumentFragment()
|
||||
// height transition are shitty on safari
|
||||
// so commented for now (until I found something ?)
|
||||
//var relevantHeight = 0
|
||||
var animationEventNames = "animationend webkitAnimationEnd MSAnimationEnd oanimationend"
|
||||
var animatedElsNumber = 0
|
||||
var sexyAnimationEnd = function(e) {
|
||||
if (e.target != e.currentTarget) {
|
||||
// end triggered by an animation on a child
|
||||
return
|
||||
}
|
||||
|
||||
animatedElsNumber--
|
||||
if (animatedElsNumber <= 0 && elsToRemove) {
|
||||
elsToRemove.forEach(function(el) {
|
||||
// browsing quickly can make the el
|
||||
// already removed by last page update ?
|
||||
if (el.parentNode) {
|
||||
el.parentNode.removeChild(el)
|
||||
}
|
||||
})
|
||||
|
||||
elsToAdd.forEach(function(el) {
|
||||
el.className = el.className.replace(el.getAttribute("data-pjax-classes"), "")
|
||||
el.removeAttribute("data-pjax-classes")
|
||||
// Pjax.off(el, animationEventNames, sexyAnimationEnd, true)
|
||||
})
|
||||
|
||||
elsToAdd = null // free memory
|
||||
elsToRemove = null // free memory
|
||||
|
||||
// assume the height is now useless (avoid bug since there is overflow hidden on the parent)
|
||||
// oldEl.style.height = "auto"
|
||||
|
||||
// this is to trigger some repaint (example: picturefill)
|
||||
this.onSwitch()
|
||||
//Pjax.trigger(window, "scroll")
|
||||
}
|
||||
}.bind(this)
|
||||
|
||||
// Force height to be able to trigger css animation
|
||||
// here we get the relevant height
|
||||
// oldEl.parentNode.appendChild(newEl)
|
||||
// relevantHeight = newEl.getBoundingClientRect().height
|
||||
// oldEl.parentNode.removeChild(newEl)
|
||||
// oldEl.style.height = oldEl.getBoundingClientRect().height + "px"
|
||||
|
||||
switchOptions = switchOptions || {}
|
||||
|
||||
forEach.call(oldEl.childNodes, function(el) {
|
||||
elsToRemove.push(el)
|
||||
if (el.classList && !el.classList.contains("js-Pjax-remove")) {
|
||||
// for fast switch, clean element that just have been added, & not cleaned yet.
|
||||
if (el.hasAttribute("data-pjax-classes")) {
|
||||
el.className = el.className.replace(el.getAttribute("data-pjax-classes"), "")
|
||||
el.removeAttribute("data-pjax-classes")
|
||||
}
|
||||
el.classList.add("js-Pjax-remove")
|
||||
if (switchOptions.callbacks && switchOptions.callbacks.removeElement) {
|
||||
switchOptions.callbacks.removeElement(el)
|
||||
}
|
||||
if (switchOptions.classNames) {
|
||||
el.className += " " + switchOptions.classNames.remove + " " + (options.backward ? switchOptions.classNames.backward : switchOptions.classNames.forward)
|
||||
}
|
||||
animatedElsNumber++
|
||||
Pjax.on(el, animationEventNames, sexyAnimationEnd, true)
|
||||
}
|
||||
})
|
||||
|
||||
forEach.call(newEl.childNodes, function(el) {
|
||||
if (el.classList) {
|
||||
var addClasses = ""
|
||||
if (switchOptions.classNames) {
|
||||
addClasses = " js-Pjax-add " + switchOptions.classNames.add + " " + (options.backward ? switchOptions.classNames.forward : switchOptions.classNames.backward)
|
||||
}
|
||||
if (switchOptions.callbacks && switchOptions.callbacks.addElement) {
|
||||
switchOptions.callbacks.addElement(el)
|
||||
}
|
||||
el.className += addClasses
|
||||
el.setAttribute("data-pjax-classes", addClasses)
|
||||
elsToAdd.push(el)
|
||||
fragToAppend.appendChild(el)
|
||||
animatedElsNumber++
|
||||
Pjax.on(el, animationEventNames, sexyAnimationEnd, true)
|
||||
}
|
||||
})
|
||||
|
||||
// pass all className of the parent
|
||||
oldEl.className = newEl.className
|
||||
oldEl.appendChild(fragToAppend)
|
||||
|
||||
// oldEl.style.height = relevantHeight + "px"
|
||||
}
|
||||
}
|
||||
1
lib/uniqueid.js
Normal file
1
lib/uniqueid.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = "pjax" + (new Date().getTime())
|
||||
Reference in New Issue
Block a user