Added support do do a push-state ajax request with forms
This commit is contained in:
6
index.js
6
index.js
@@ -27,7 +27,7 @@ var Pjax = function(options) {
|
||||
opt.url = st.state.url
|
||||
opt.title = st.state.title
|
||||
opt.history = false
|
||||
|
||||
opt.requestOptions = {};
|
||||
if (st.state.uid < this.lastUid) {
|
||||
opt.backward = true
|
||||
}
|
||||
@@ -55,6 +55,8 @@ Pjax.prototype = {
|
||||
|
||||
attachLink: require("./lib/proto/attach-link.js"),
|
||||
|
||||
attachForm: require("./lib/proto/attach-form.js"),
|
||||
|
||||
forEachSelectors: function(cb, context, DOMcontext) {
|
||||
return require("./lib/foreach-selectors.js").bind(this)(this.options.selectors, cb, context, DOMcontext)
|
||||
},
|
||||
@@ -151,7 +153,7 @@ Pjax.prototype = {
|
||||
trigger(document, "pjax:send", options);
|
||||
|
||||
// Do the request
|
||||
this.doRequest(href, function(html) {
|
||||
this.doRequest(href, options.requestOptions, function(html) {
|
||||
// Fail if unable to load HTML via AJAX
|
||||
if (html === false) {
|
||||
trigger(document,"pjax:complete pjax:error", options)
|
||||
|
||||
@@ -2,7 +2,7 @@ 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 parent = el.parentNode || document.querySelector("head") || document.documentElement
|
||||
var script = document.createElement("script")
|
||||
|
||||
if (code.match("document.write")) {
|
||||
@@ -22,8 +22,11 @@ module.exports = function(el) {
|
||||
}
|
||||
|
||||
// execute
|
||||
head.insertBefore(script, head.firstChild)
|
||||
head.removeChild(script) // avoid pollution
|
||||
parent.appendChild(script);
|
||||
// avoid pollution only in head or body tags
|
||||
if (["head","body"].indexOf(parent.tagName.toLowerCase()) > 0) {
|
||||
parent.removeChild(script)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -4,6 +4,11 @@ var evalScript = require("./eval-script")
|
||||
// Needed since innerHTML does not run scripts
|
||||
module.exports = function(el) {
|
||||
// console.log("going to execute scripts for ", el)
|
||||
|
||||
if (el.tagName.toLowerCase() === "script") {
|
||||
evalScript(el);
|
||||
}
|
||||
|
||||
forEachEls(el.querySelectorAll("script"), function(script) {
|
||||
if (!script.type || script.type.toLowerCase() === "text/javascript") {
|
||||
if (script.parentNode) {
|
||||
|
||||
93
lib/proto/attach-form.js
Normal file
93
lib/proto/attach-form.js
Normal file
@@ -0,0 +1,93 @@
|
||||
require("../polyfills/Function.prototype.bind")
|
||||
|
||||
var on = require("../events/on")
|
||||
var clone = require("../clone")
|
||||
|
||||
var attrClick = "data-pjax-click-state"
|
||||
|
||||
var formAction = function(el, event){
|
||||
|
||||
this.options.requestOptions = {
|
||||
requestUrl : el.getAttribute('action') || window.location.href,
|
||||
requestMethod : el.getAttribute('method') || 'GET',
|
||||
}
|
||||
|
||||
//create a testable virtual link of the form action
|
||||
var virtLinkElement = document.createElement('a');
|
||||
virtLinkElement.setAttribute('href', this.options.requestOptions.requestUrl);
|
||||
|
||||
// Ignore external links.
|
||||
if (virtLinkElement.protocol !== window.location.protocol || virtLinkElement.host !== window.location.host) {
|
||||
el.setAttribute(attrClick, "external");
|
||||
return
|
||||
}
|
||||
|
||||
// Ignore click if we are on an anchor on the same page
|
||||
if (virtLinkElement.pathname === window.location.pathname && virtLinkElement.hash.length > 0) {
|
||||
el.setAttribute(attrClick, "anchor-present");
|
||||
return
|
||||
}
|
||||
|
||||
// Ignore empty anchor "foo.html#"
|
||||
if (virtLinkElement.href === window.location.href.split("#")[0] + "#") {
|
||||
el.setAttribute(attrClick, "anchor-empty")
|
||||
return
|
||||
}
|
||||
|
||||
// if declared as a full reload, just normally submit the form
|
||||
if ( this.options.currentUrlFullReload) {
|
||||
el.setAttribute(attrClick, "reload");
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
|
||||
var paramObject = [];
|
||||
for(var elementKey in el.elements) {
|
||||
var element = el.elements[elementKey];
|
||||
if (!!element.name && element.attributes !== undefined && element.tagName.toLowerCase() !== 'button'){
|
||||
if ((element.attributes.type !== 'checkbox' && element.attributes.type !== 'radio') || element.checked) {
|
||||
paramObject.push({ name: encodeURIComponent(element.name), value: encodeURIComponent(element.value)});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Creating a getString
|
||||
var paramsString = (paramObject.map(function(value){return value.name+"="+value.value;})).join('&');
|
||||
|
||||
this.options.requestOptions.requestPayload = paramObject;
|
||||
this.options.requestOptions.requestPayloadString = paramsString;
|
||||
|
||||
el.setAttribute(attrClick, "submit");
|
||||
|
||||
this.loadUrl(virtLinkElement.href, clone(this.options))
|
||||
|
||||
};
|
||||
|
||||
var isDefaultPrevented = function(event) {
|
||||
return event.defaultPrevented || event.returnValue === false;
|
||||
};
|
||||
|
||||
|
||||
module.exports = function(el) {
|
||||
var that = this
|
||||
|
||||
on(el, "submit", function(event) {
|
||||
if (isDefaultPrevented(event)) {
|
||||
return
|
||||
}
|
||||
|
||||
formAction.call(that, el, event)
|
||||
})
|
||||
|
||||
on(el, "keyup", function(event) {
|
||||
if (isDefaultPrevented(event)) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (event.keyCode == 13) {
|
||||
formAction.call(that, el, event)
|
||||
}
|
||||
}.bind(this))
|
||||
}
|
||||
@@ -51,7 +51,7 @@ var linkAction = function(el, event) {
|
||||
this.reload()
|
||||
return
|
||||
}
|
||||
|
||||
this.options.requestOptions = this.options.requestOptions || {};
|
||||
el.setAttribute(attrClick, "load")
|
||||
this.loadUrl(el.href, clone(this.options))
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@ module.exports = function(el) {
|
||||
break
|
||||
|
||||
case "form":
|
||||
throw "Pjax doesnt support <form> yet."
|
||||
// only attach link if el does not already have link attached
|
||||
if (!el.hasAttribute('data-pjax-click-state')) {
|
||||
this.attachForm(el)
|
||||
}
|
||||
break
|
||||
|
||||
default:
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
module.exports = function(location, callback) {
|
||||
module.exports = function(location, options, callback) {
|
||||
options = options || {};
|
||||
var requestMethod = options.requestMethod || "GET";
|
||||
var requestPayload = options.requestPayloadString || null;
|
||||
var request = new XMLHttpRequest()
|
||||
|
||||
request.onreadystatechange = function() {
|
||||
@@ -17,8 +20,16 @@ module.exports = function(location, callback) {
|
||||
location += (!/[?&]/.test(location) ? "?" : "&") + new Date().getTime()
|
||||
}
|
||||
|
||||
request.open("GET", location, true)
|
||||
request.open(requestMethod.toUpperCase(), location, true)
|
||||
request.setRequestHeader("X-Requested-With", "XMLHttpRequest")
|
||||
request.send(null)
|
||||
|
||||
// Add the request payload if available
|
||||
if (options.requestPayloadString != undefined && options.requestPayloadString != "") {
|
||||
// Send the proper header information along with the request
|
||||
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
}
|
||||
|
||||
request.send(requestPayload)
|
||||
|
||||
return request
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
"scripts": {
|
||||
"lint": "jscs **/*.js && jshint . --exclude-path .gitignore",
|
||||
"standalone": "browserify index.js --standalone Pjax > pjax.js",
|
||||
"build-debug": "browserify index.js --debug --standalone Pjax > pjax.js",
|
||||
"tests": "testling",
|
||||
"test": "npm run lint && npm run standalone && npm run tests",
|
||||
"test--html": "testling --html > tests/scripts/index.html",
|
||||
|
||||
78
tests/lib/proto/attach-form.js
Normal file
78
tests/lib/proto/attach-form.js
Normal file
@@ -0,0 +1,78 @@
|
||||
var tape = require("tape")
|
||||
|
||||
var on = require("../../../lib/events/on")
|
||||
var trigger = require("../../../lib/events/trigger")
|
||||
var attachForm = require("../../../lib/proto/attach-form")
|
||||
|
||||
var form = document.createElement("form")
|
||||
var attr = "data-pjax-click-state"
|
||||
var preventDefault = function(e) { e.preventDefault() }
|
||||
|
||||
tape("test attach form prototype method", function(t) {
|
||||
t.plan(7)
|
||||
|
||||
attachForm.call({
|
||||
options: {},
|
||||
reload: function() {
|
||||
t.equal(form.getAttribute(attr), "reload", "triggering a simple reload will just submit the form")
|
||||
},
|
||||
loadUrl: function() {
|
||||
t.equal(form.getAttribute(attr), "submit", "triggering a post to the next page")
|
||||
}
|
||||
}, form)
|
||||
|
||||
var internalUri = window.location.protocol + "//" + window.location.host + window.location.pathname + window.location.search
|
||||
|
||||
form.action = "http://external.com/"
|
||||
trigger(form, "submit")
|
||||
t.equal(form.getAttribute(attr), "external", "external url stop behavior")
|
||||
|
||||
form.action = internalUri + "#anchor"
|
||||
trigger(form, "submit")
|
||||
t.equal(form.getAttribute(attr), "anchor-present", "internal anchor stop behavior")
|
||||
|
||||
window.location.hash = "#anchor"
|
||||
form.action = internalUri + "#another-anchor"
|
||||
trigger(form, "submit")
|
||||
t.notEqual(form.getAttribute(attr), "anchor", "differents anchors stop behavior")
|
||||
window.location.hash = ""
|
||||
|
||||
form.action = internalUri + "#"
|
||||
trigger(form, "submit")
|
||||
t.equal(form.getAttribute(attr), "anchor-empty", "empty anchor stop behavior")
|
||||
|
||||
form.action = internalUri
|
||||
trigger(form, "submit")
|
||||
// see reload defined above
|
||||
|
||||
form.action = window.location.protocol + "//" + window.location.host + "/internal"
|
||||
form.method = 'POST'
|
||||
trigger(form, "submit")
|
||||
// see post defined above
|
||||
|
||||
form.action = window.location.protocol + "//" + window.location.host + "/internal"
|
||||
form.method = 'GET'
|
||||
trigger(form, "submit")
|
||||
// see post defined above
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tape("test attach form preventDefaulted events", function(t) {
|
||||
var callbacked = false
|
||||
var form = document.createElement("form")
|
||||
|
||||
attachForm.call({
|
||||
options: {},
|
||||
loadUrl: function() {
|
||||
callbacked = true
|
||||
}
|
||||
}, form)
|
||||
|
||||
form.action = "#"
|
||||
on(form, "submit", preventDefault)
|
||||
trigger(form, "submit")
|
||||
t.equal(callbacked, false, "events that are preventDefaulted should not fire callback")
|
||||
|
||||
t.end()
|
||||
})
|
||||
@@ -1,17 +1,21 @@
|
||||
var tape = require("tape")
|
||||
|
||||
var parseElement = require("../../../lib/proto/parse-element")
|
||||
var protoMock = {attachLink: function() { return true}}
|
||||
var protoMock = {
|
||||
attachLink: function() { return true },
|
||||
attachForm: function() { return true }
|
||||
}
|
||||
|
||||
tape("test parse element prototype method", function(t) {
|
||||
t.doesNotThrow(function() {
|
||||
var a = document.createElement("a")
|
||||
parseElement.call(protoMock, a)
|
||||
}, "<a> element can be parsed")
|
||||
|
||||
t.throws(function() {
|
||||
t.doesNotThrow(function() {
|
||||
var form = document.createElement("form")
|
||||
parseElement.call(protoMock, form)
|
||||
}, "<form> cannot be used (for now)")
|
||||
}, "<form> element can be parsed")
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user