From b7fc3457de76738133fb778e53b28978eeaf378f Mon Sep 17 00:00:00 2001 From: Robin North Date: Wed, 21 Feb 2018 18:26:50 +0000 Subject: [PATCH 1/4] Fix check for radio and checkbox inputs --- lib/proto/attach-form.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/proto/attach-form.js b/lib/proto/attach-form.js index fa4d6af..4f6ed82 100644 --- a/lib/proto/attach-form.js +++ b/lib/proto/attach-form.js @@ -50,7 +50,7 @@ var formAction = function(el, event) { // jscs:disable disallowImplicitTypeConversion if (!!element.name && element.attributes !== undefined && element.tagName.toLowerCase() !== "button") { // jscs:enable disallowImplicitTypeConversion - if ((element.attributes.type !== "checkbox" && element.attributes.type !== "radio") || element.checked) { + if ((!element.attributes.type || element.attributes.type.value !== "checkbox" && element.attributes.type.value !== "radio") || element.checked) { paramObject.push({name: encodeURIComponent(element.name), value: encodeURIComponent(element.value)}) } } -- 2.49.1 From a5b51f34dfd9dce904c4887bc7d3dfd9857c70e1 Mon Sep 17 00:00:00 2001 From: Robin North Date: Wed, 21 Feb 2018 18:27:26 +0000 Subject: [PATCH 2/4] Fix GET form submission --- lib/proto/attach-form.js | 10 +++----- lib/send-request.js | 41 +++++++++++++++++++++++++-------- lib/util/update-query-string.js | 10 ++++++++ 3 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 lib/util/update-query-string.js diff --git a/lib/proto/attach-form.js b/lib/proto/attach-form.js index 4f6ed82..43ac1a6 100644 --- a/lib/proto/attach-form.js +++ b/lib/proto/attach-form.js @@ -44,23 +44,19 @@ var formAction = function(el, event) { event.preventDefault() - var paramObject = [] + var params = [] for (var elementKey in el.elements) { var element = el.elements[elementKey] // jscs:disable disallowImplicitTypeConversion if (!!element.name && element.attributes !== undefined && element.tagName.toLowerCase() !== "button") { // jscs:enable disallowImplicitTypeConversion if ((!element.attributes.type || element.attributes.type.value !== "checkbox" && element.attributes.type.value !== "radio") || element.checked) { - paramObject.push({name: encodeURIComponent(element.name), value: encodeURIComponent(element.value)}) + params.push({name: encodeURIComponent(element.name), value: encodeURIComponent(element.value)}) } } } - // Creating a getString - var paramsString = (paramObject.map(function(value) {return value.name + "=" + value.value})).join("&") - - options.requestOptions.requestPayload = paramObject - options.requestOptions.requestPayloadString = paramsString + options.requestOptions.requestParams = params el.setAttribute(attrClick, "submit") diff --git a/lib/send-request.js b/lib/send-request.js index 618f65b..58ce7fd 100644 --- a/lib/send-request.js +++ b/lib/send-request.js @@ -1,7 +1,10 @@ +var updateQueryString = require("./util/update-query-string"); + module.exports = function(location, options, callback) { options = options || {} - var requestMethod = options.requestMethod || "GET" - var requestPayload = options.requestPayloadString || null + var requestMethod = (options.requestMethod || "GET").toUpperCase() + var requestParams = options.requestParams || null + var requestPayload = null var request = new XMLHttpRequest() request.onreadystatechange = function() { @@ -24,19 +27,39 @@ module.exports = function(location, options, callback) { callback(null, request) } - // Add a timestamp as part of the query string if cache busting is enabled - if (this.options.cacheBust) { - location += (!/[?&]/.test(location) ? "?" : "&") + new Date().getTime() + // Prepare the request payload for forms, if available + if (requestParams && requestParams.length) { + switch (requestMethod) { + case "GET": + // Reset query string to avoid an issue with repeat submissions where checkboxes that were + // previously checked are incorrectly preserved + location = location.split("?")[0] + + // Build new query string + requestParams.forEach(function(param) { + location = updateQueryString(location, param.name, param.value) + }); + break; + + case "POST": + // Build payload string + requestPayload = (requestParams.map(function(param) {return param.name + "=" + param.value})).join("&") + break; + } } - request.open(requestMethod.toUpperCase(), location, true) + // Add a timestamp as part of the query string if cache busting is enabled + if (this.options.cacheBust) { + location = updateQueryString(location, "t", Date.now()) + } + + request.open(requestMethod, location, true) request.timeout = options.timeout request.setRequestHeader("X-Requested-With", "XMLHttpRequest") request.setRequestHeader("X-PJAX", "true") - // Add the request payload if available - if (options.requestPayloadString !== undefined && options.requestPayloadString !== "") { - // Send the proper header information along with the request + // Send the proper header information for POST forms + if (requestPayload && requestMethod === "POST") { request.setRequestHeader("Content-type", "application/x-www-form-urlencoded") } diff --git a/lib/util/update-query-string.js b/lib/util/update-query-string.js new file mode 100644 index 0000000..3635de0 --- /dev/null +++ b/lib/util/update-query-string.js @@ -0,0 +1,10 @@ +module.exports = function(uri, key, value) { + var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i") + var separator = uri.indexOf("?") !== -1 ? "&" : "?" + if (uri.match(re)) { + return uri.replace(re, "$1" + key + "=" + value + "$2") + } + else { + return uri + separator + key + "=" + value + } +} -- 2.49.1 From ca479618fe2a21c960cd7c65c65068af6edc6344 Mon Sep 17 00:00:00 2001 From: Robin North Date: Wed, 21 Feb 2018 18:27:50 +0000 Subject: [PATCH 3/4] Add example forms for testing --- example/example.js | 3 +- example/forms.html | 138 +++++++++++++++++++++++++++++++++++++++++++++ example/index.html | 17 ++++-- example/page2.html | 12 ++-- example/page3.html | 12 ++-- 5 files changed, 163 insertions(+), 19 deletions(-) create mode 100644 example/forms.html diff --git a/example/example.js b/example/example.js index f52f8bf..594b3a5 100644 --- a/example/example.js +++ b/example/example.js @@ -20,7 +20,8 @@ document.addEventListener("pjax:success", function() { document.addEventListener("DOMContentLoaded", function() { var pjax = new Pjax({ elements: [".js-Pjax"], - selectors: [".body"] + selectors: [".body", "title"], + cacheBust: true, // currentUrlFullReload: true, }) console.log("Pjax initialized.", pjax) diff --git a/example/forms.html b/example/forms.html new file mode 100644 index 0000000..eee4469 --- /dev/null +++ b/example/forms.html @@ -0,0 +1,138 @@ + + + + + Forms + + + + +
+

Forms

+ Hello. Try out the examples below and inspect the results in your browser's developer tools, or go to the Index. + +

GET form

+ +
+ + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ +
+ + + + + +
+ +
+
+ + + + +
+
+ + + + +
+
+ + +
+ +
+
+ +

POST form

+ +
+ + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ +
+ + + + + +
+ +
+
+ + + + +
+
+ + + + +
+
+ + +
+
+ + diff --git a/example/index.html b/example/index.html index f9e3da5..2245d5b 100644 --- a/example/index.html +++ b/example/index.html @@ -1,17 +1,22 @@ - + Hello - - + + -
+

Index

Hello. - Go to Page 2 or Page 3 and view your console to see Pjax events. - Clicking on this page will just reload the page entirely. + Go to Page 2 or Page 3 and view your console to see Pjax events. + Clicking on this page will just reload the page entirely. + +

Forms

+ + You can submit GET or POST forms with Pjax! Go to the form examples to try it out. +
diff --git a/example/page2.html b/example/page2.html index 36d2e33..7d0b941 100644 --- a/example/page2.html +++ b/example/page2.html @@ -1,15 +1,15 @@ - - Hello - - + + Page 2 + + -
+

Page 2

- Hello. Go to Index. + Hello. Go to Index.
diff --git a/example/page3.html b/example/page3.html index 7546dd1..9864eb6 100644 --- a/example/page3.html +++ b/example/page3.html @@ -1,15 +1,15 @@ - - Hello - - + + Page 3 + + -
+

Page 3

- Hello. Go to Index. + Hello. Go to Index.
-- 2.49.1 From 4e805ad8afc9046dcfe0f50264641ff0fd7c013b Mon Sep 17 00:00:00 2001 From: Robin North Date: Thu, 1 Mar 2018 10:19:35 +0000 Subject: [PATCH 4/4] Refactor query string building --- lib/proto/attach-form.js | 11 ++++++----- lib/send-request.js | 18 ++++++++++-------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/proto/attach-form.js b/lib/proto/attach-form.js index 43ac1a6..3ca881b 100644 --- a/lib/proto/attach-form.js +++ b/lib/proto/attach-form.js @@ -11,7 +11,8 @@ var formAction = function(el, event) { // Initialize requestOptions options.requestOptions = { requestUrl: el.getAttribute("action") || window.location.href, - requestMethod: el.getAttribute("method") || "GET" + requestMethod: el.getAttribute("method") || "GET", + requestParams: [] } // create a testable virtual link of the form action @@ -44,20 +45,20 @@ var formAction = function(el, event) { event.preventDefault() - var params = [] for (var elementKey in el.elements) { var element = el.elements[elementKey] // jscs:disable disallowImplicitTypeConversion if (!!element.name && element.attributes !== undefined && element.tagName.toLowerCase() !== "button") { // jscs:enable disallowImplicitTypeConversion if ((!element.attributes.type || element.attributes.type.value !== "checkbox" && element.attributes.type.value !== "radio") || element.checked) { - params.push({name: encodeURIComponent(element.name), value: encodeURIComponent(element.value)}) + options.requestOptions.requestParams.push({ + name: encodeURIComponent(element.name), + value: encodeURIComponent(element.value) + }) } } } - options.requestOptions.requestParams = params - el.setAttribute(attrClick, "submit") options.triggerElement = el diff --git a/lib/send-request.js b/lib/send-request.js index 58ce7fd..f2817b5 100644 --- a/lib/send-request.js +++ b/lib/send-request.js @@ -2,6 +2,7 @@ var updateQueryString = require("./util/update-query-string"); module.exports = function(location, options, callback) { options = options || {} + var queryString var requestMethod = (options.requestMethod || "GET").toUpperCase() var requestParams = options.requestParams || null var requestPayload = null @@ -29,22 +30,23 @@ module.exports = function(location, options, callback) { // Prepare the request payload for forms, if available if (requestParams && requestParams.length) { + // Build query string + queryString = (requestParams.map(function(param) {return param.name + "=" + param.value})).join("&") + switch (requestMethod) { case "GET": // Reset query string to avoid an issue with repeat submissions where checkboxes that were // previously checked are incorrectly preserved location = location.split("?")[0] - // Build new query string - requestParams.forEach(function(param) { - location = updateQueryString(location, param.name, param.value) - }); - break; + // Append new query string + location += "?" + queryString + break case "POST": - // Build payload string - requestPayload = (requestParams.map(function(param) {return param.name + "=" + param.value})).join("&") - break; + // Send query string as request payload + requestPayload = queryString + break } } -- 2.49.1