From aa615b4d6ca4a7cc584b2211619c77f5297b7fc7 Mon Sep 17 00:00:00 2001 From: Robin North Date: Sun, 4 Mar 2018 16:00:55 +0000 Subject: [PATCH 1/2] `loadUrl` enhancements - Make `options` parameter optional - Allow partial overriding of instance options when calling `loadUrl` directly - Make `requestOptions` optional - Document `loadUrl` usage and provide examples --- README.md | 34 ++++++++++++++++------ example/example.js | 39 ++++++++++++++++++++++++-- example/index.html | 7 +++++ index.js | 16 +++++++---- lib/{proto => }/parse-options.js | 4 +-- lib/proto/attach-form.js | 2 +- lib/proto/attach-link.js | 5 +--- lib/send-request.js | 10 ++++--- lib/{ => util}/clone.js | 0 lib/util/extend.js | 21 ++++++++++++++ tests/lib/clone.js | 17 ----------- tests/lib/{proto => }/parse-options.js | 8 +++--- tests/lib/send-request.js | 14 ++------- tests/lib/util/clone.js | 17 +++++++++++ tests/lib/util/extend.js | 16 +++++++++++ 15 files changed, 148 insertions(+), 62 deletions(-) rename lib/{proto => }/parse-options.js (95%) rename lib/{ => util}/clone.js (100%) create mode 100644 lib/util/extend.js delete mode 100644 tests/lib/clone.js rename tests/lib/{proto => }/parse-options.js (87%) create mode 100644 tests/lib/util/clone.js create mode 100644 tests/lib/util/extend.js diff --git a/README.md b/README.md index 6d0f92d..4e1aad9 100644 --- a/README.md +++ b/README.md @@ -124,9 +124,13 @@ To see if Pjax is actually supported by your browser, use `Pjax.isSupported()`. ## Usage -### `new Pjax()` +### Methods -Let's talk more about the most basic way to get started: +#### `new Pjax()` + +Let's talk more about the most basic way to get started. + +When instantiating `Pjax`, you can pass options in to the constructor as an object: ```js new Pjax({ @@ -153,12 +157,24 @@ Pjax.prototype.getElements = function() { return document.getElementsByClassName(".js-Pjax") } -new Pjax({}) +new Pjax() ``` -When instantiating a `Pjax` object, you need to pass all options as an object: +#### `loadUrl(href, (options))` + +With this method, you can manually trigger loading of a URL: + +```js +var pjax = new Pjax() + +// use case 1 (without options override) +pjax.loadUrl("/your-url") + +// use case 2 (with options override) +pjax.loadUrl("/your-other-url", {timeout: 10}) +``` -#### Options +### Options ##### `elements` (String, default: `"a[href], form[action]"`) @@ -398,8 +414,8 @@ Enables verbose mode. Useful to debug page layout differences. When set to true, clicking on a link that points to the current URL will trigger a full page reload. -The default is `false`, so clicking on such a link will do nothing. -If you want to add some custom behavior, add a click listener to the link, +The default is `false`, so clicking on such a link will do nothing. +If you want to add some custom behavior, add a click listener to the link, and set `preventDefault` to true, to prevent Pjax from receiving the event. Here is some sample code: @@ -420,8 +436,8 @@ Here is some sample code: } ``` -(Note that if `cacheBust` is set to true, the code that checks if the href -is the same as the current page's URL will not work, due to the query string +(Note that if `cacheBust` is set to true, the code that checks if the href +is the same as the current page's URL will not work, due to the query string appended to force a cache bust). ##### `timeout` (Integer, default: `0`) diff --git a/example/example.js b/example/example.js index 594b3a5..9880439 100644 --- a/example/example.js +++ b/example/example.js @@ -1,4 +1,31 @@ /* global Pjax */ +var pjax; +var initButtons = function() { + var buttons = document.querySelectorAll("button[data-manual-trigger]") + + if (!buttons) { + return + } + + // jshint -W083 + for (var i = 0; i < buttons.length; i++) { + buttons[i].addEventListener("click", function(e) { + var el = e.currentTarget + + if (el.getAttribute("data-manual-trigger-override") === "true") { + // Manually load URL with overridden Pjax instance options + pjax.loadUrl("/example/page2.html", {cacheBust: false}) + } + else + { + // Manually load URL with current Pjax instance options + pjax.loadUrl("/example/page2.html") + } + }) + } + // jshint +W083 +} + console.log("Document initialized:", window.location.href) document.addEventListener("pjax:send", function() { @@ -15,14 +42,20 @@ document.addEventListener("pjax:error", function() { document.addEventListener("pjax:success", function() { console.log("Event: pjax:success", arguments) + + // Init page content + initButtons() }) document.addEventListener("DOMContentLoaded", function() { - var pjax = new Pjax({ + // Init Pjax instance + pjax = new Pjax({ elements: [".js-Pjax"], selectors: [".body", "title"], - cacheBust: true, - // currentUrlFullReload: true, + cacheBust: true }) console.log("Pjax initialized.", pjax) + + // Init page content + initButtons() }) diff --git a/example/index.html b/example/index.html index 2245d5b..39cd261 100644 --- a/example/index.html +++ b/example/index.html @@ -13,6 +13,13 @@

Index

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. +

Manual URL loading

+ + You can use Pjax's loadUrl method to manually load a URL. Click the buttons below to try it out!

+ +

+ +

Forms

You can submit GET or POST forms with Pjax! Go to the form examples to try it out. diff --git a/index.js b/index.js index 493ecd9..b8d34db 100644 --- a/index.js +++ b/index.js @@ -1,13 +1,15 @@ -var clone = require("./lib/clone.js") var executeScripts = require("./lib/execute-scripts.js") var forEachEls = require("./lib/foreach-els.js") +var parseOptions = require("./lib/parse-options.js") var switches = require("./lib/switches") var newUid = require("./lib/uniqueid.js") var on = require("./lib/events/on.js") var trigger = require("./lib/events/trigger.js") +var clone = require("./lib/util/clone.js") var contains = require("./lib/util/contains.js") +var extend = require("./lib/util/extend.js") var noop = require("./lib/util/noop") var Pjax = function(options) { @@ -17,8 +19,8 @@ var Pjax = function(options) { options: null } - var parseOptions = require("./lib/proto/parse-options.js") - parseOptions.call(this,options) + + this.options = parseOptions(options) this.log("Pjax options", this.options) if (this.options.scrollRestoration && "scrollRestoration" in history) { @@ -35,7 +37,6 @@ var Pjax = function(options) { opt.url = st.state.url opt.title = st.state.title opt.history = false - opt.requestOptions = {} opt.scrollPos = st.state.scrollPos if (st.state.uid < this.lastUid) { opt.backward = true @@ -142,6 +143,10 @@ Pjax.prototype = { doRequest: require("./lib/send-request.js"), loadUrl: function(href, options) { + options = typeof options === "object" ? + extend({}, this.options, options) : + clone(this.options) + this.log("load href", href, options) // Abort any previous request @@ -150,8 +155,7 @@ Pjax.prototype = { trigger(document, "pjax:send", options) // Do the request - options.requestOptions.timeout = this.options.timeout - this.request = this.doRequest(href, options.requestOptions, function(html, request) { + this.request = this.doRequest(href, options, function(html, request) { // Fail if unable to load HTML via AJAX if (html === false) { trigger(document, "pjax:complete pjax:error", options) diff --git a/lib/proto/parse-options.js b/lib/parse-options.js similarity index 95% rename from lib/proto/parse-options.js rename to lib/parse-options.js index 8118d50..a3f5239 100644 --- a/lib/proto/parse-options.js +++ b/lib/parse-options.js @@ -1,6 +1,6 @@ /* global _gaq: true, ga: true */ -var defaultSwitches = require("../switches") +var defaultSwitches = require("./switches") module.exports = function(options) { options = options || {} @@ -36,5 +36,5 @@ module.exports = function(options) { options.switches.body = defaultSwitches.switchElementsAlt } - this.options = options + return options } diff --git a/lib/proto/attach-form.js b/lib/proto/attach-form.js index 3ca881b..9ad4c77 100644 --- a/lib/proto/attach-form.js +++ b/lib/proto/attach-form.js @@ -1,5 +1,5 @@ var on = require("../events/on") -var clone = require("../clone") +var clone = require("../util/clone") var attrClick = "data-pjax-click-state" diff --git a/lib/proto/attach-link.js b/lib/proto/attach-link.js index f7009e5..7de3317 100644 --- a/lib/proto/attach-link.js +++ b/lib/proto/attach-link.js @@ -1,5 +1,5 @@ var on = require("../events/on") -var clone = require("../clone") +var clone = require("../util/clone") var attrClick = "data-pjax-click-state" var attrKey = "data-pjax-keyup-state" @@ -9,9 +9,6 @@ var linkAction = function(el, event) { // clone it so the changes don't persist var options = clone(this.options) - // Initialize requestOptions since loadUrl expects it to be an object - options.requestOptions = {} - // 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") diff --git a/lib/send-request.js b/lib/send-request.js index f2817b5..9e203aa 100644 --- a/lib/send-request.js +++ b/lib/send-request.js @@ -3,10 +3,12 @@ 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 requestOptions = options.requestOptions || {} + var requestMethod = (requestOptions.requestMethod || "GET").toUpperCase() + var requestParams = requestOptions.requestParams || null var requestPayload = null var request = new XMLHttpRequest() + var timeout = options.timeout || 0 request.onreadystatechange = function() { if (request.readyState === 4) { @@ -51,12 +53,12 @@ module.exports = function(location, options, callback) { } // Add a timestamp as part of the query string if cache busting is enabled - if (this.options.cacheBust) { + if (options.cacheBust) { location = updateQueryString(location, "t", Date.now()) } request.open(requestMethod, location, true) - request.timeout = options.timeout + request.timeout = timeout request.setRequestHeader("X-Requested-With", "XMLHttpRequest") request.setRequestHeader("X-PJAX", "true") diff --git a/lib/clone.js b/lib/util/clone.js similarity index 100% rename from lib/clone.js rename to lib/util/clone.js diff --git a/lib/util/extend.js b/lib/util/extend.js new file mode 100644 index 0000000..07efde9 --- /dev/null +++ b/lib/util/extend.js @@ -0,0 +1,21 @@ +module.exports = function(target) { + if (target == null) { + return target + } + + var to = Object(target) + + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] + + if (source != null) { + for (var key in source) { + // Avoid bugs when hasOwnProperty is shadowed + if (Object.prototype.hasOwnProperty.call(source, key)) { + to[key] = source[key] + } + } + } + } + return to +} diff --git a/tests/lib/clone.js b/tests/lib/clone.js deleted file mode 100644 index 9997943..0000000 --- a/tests/lib/clone.js +++ /dev/null @@ -1,17 +0,0 @@ -var tape = require("tape") - -var clone = require("../../lib/clone") - -tape("test clone method", function(t) { - var obj = {one: 1, two: 2} - var cloned = clone(obj) - - t.notEqual(obj, cloned, "cloned object isn't the object") - - t.same(obj, cloned, "cloned object have the same values than object") - - cloned.tree = 3 - t.notSame(obj, cloned, "modified cloned object haven't the same values than object") - - t.end() -}) diff --git a/tests/lib/proto/parse-options.js b/tests/lib/parse-options.js similarity index 87% rename from tests/lib/proto/parse-options.js rename to tests/lib/parse-options.js index 4665bcb..9c7785a 100644 --- a/tests/lib/proto/parse-options.js +++ b/tests/lib/parse-options.js @@ -1,10 +1,10 @@ var tape = require("tape") -var parseOptions = require("../../../lib/proto/parse-options.js") +var parseOptions = require("../../lib/parse-options.js") tape("test parse initalization options function", function(t) { t.test("- default options", function(t) { var pjax = {} - parseOptions.call(pjax, {}) + pjax.options = parseOptions({}) t.equal(pjax.options.elements, "a[href], form[action]") t.equal(pjax.options.selectors.length, 2, "selectors length") @@ -30,7 +30,7 @@ tape("test parse initalization options function", function(t) { // verify analytics always ends up as a function even when passed not a function t.test("- analytics is a function", function(t) { var pjax = {} - parseOptions.call(pjax, {analytics: "some string"}) + pjax.options = parseOptions({analytics: "some string"}) t.deepEqual(typeof pjax.options.analytics, "function") t.end() @@ -39,7 +39,7 @@ tape("test parse initalization options function", function(t) { // verify that the value false for scrollTo is not squashed t.test("- scrollTo remains false", function(t) { var pjax = {} - parseOptions.call(pjax, {scrollTo: false}) + pjax.options = parseOptions({scrollTo: false}) t.deepEqual(pjax.options.scrollTo, false) t.end() diff --git a/tests/lib/send-request.js b/tests/lib/send-request.js index 9ecaf56..0c95b30 100644 --- a/tests/lib/send-request.js +++ b/tests/lib/send-request.js @@ -18,12 +18,7 @@ tape("test xhr request", function(t) { var url = "https://httpbin.org/get" t.test("- request is made, gets a result, and is cache-busted", function(t) { - var requestCacheBust = sendRequest.bind({ - options: { - cacheBust: true - } - }) - var r = requestCacheBust(url, {}, function(result) { + var r = sendRequest(url, {cacheBust: true}, function(result) { t.equal(r.responseURL.indexOf("?"), url.length, "XHR URL is cache-busted when configured to be") try { result = JSON.parse(result) @@ -36,12 +31,7 @@ tape("test xhr request", function(t) { }) }) t.test("- request is not cache-busted when configured not to be", function(t) { - var requestNoCacheBust = sendRequest.bind({ - options: { - cacheBust: false - } - }) - var r = requestNoCacheBust(url, {}, function() { + var r = sendRequest(url, {}, function() { t.equal(r.responseURL, url, "XHR URL is left untouched") t.end() }) diff --git a/tests/lib/util/clone.js b/tests/lib/util/clone.js new file mode 100644 index 0000000..e79a4cd --- /dev/null +++ b/tests/lib/util/clone.js @@ -0,0 +1,17 @@ +var tape = require("tape") + +var clone = require("../../../lib/util/clone") + +tape("test clone method", function(t) { + var obj = {one: 1, two: 2} + var cloned = clone(obj) + + t.notEqual(obj, cloned, "cloned object isn't the original object") + + t.same(obj, cloned, "cloned object has the same values as original object") + + cloned.three = 3 + t.notSame(obj, cloned, "modified cloned object doesn't have the same values as original object") + + t.end() +}) diff --git a/tests/lib/util/extend.js b/tests/lib/util/extend.js new file mode 100644 index 0000000..0319797 --- /dev/null +++ b/tests/lib/util/extend.js @@ -0,0 +1,16 @@ +var tape = require("tape") + +var extend = require("../../../lib/util/extend") + +tape("test extend method", function(t) { + var obj = {one: 1, two: 2} + var extended = extend({}, obj, {two: "two", three: 3}) + + t.notEqual(obj, extended, "extended object isn't the original object") + + t.notSame(obj, extended, "extended object doesn't have the same values as original object") + + t.notSame(obj.two, extended.two, "extended object value overwrites value from original object") + + t.end() +}) From d5a147048d49fee0781c94927589c3c8ba521d41 Mon Sep 17 00:00:00 2001 From: Robin North Date: Mon, 5 Mar 2018 09:36:55 +0000 Subject: [PATCH 2/2] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e1aad9..38efdb5 100644 --- a/README.md +++ b/README.md @@ -160,7 +160,7 @@ Pjax.prototype.getElements = function() { new Pjax() ``` -#### `loadUrl(href, (options))` +#### `loadUrl(href, [options])` With this method, you can manually trigger loading of a URL: