diff --git a/.gitignore b/.gitignore index c2658d7..075b5c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ +.DS_Store node_modules/ +dist/ +tests/scripts/index.html diff --git a/.jscs.json b/.jscs.json index a03292c..e4169a9 100644 --- a/.jscs.json +++ b/.jscs.json @@ -1,112 +1,143 @@ { - "excludeFiles": [ - ] - , "requireCurlyBraces": [ - "if" - , "else" - , "for" - , "while" - , "do" - , "try" - , "catch" - ] - , "requireSpaceAfterKeywords": [ - "if" - , "else" - , "for" - , "while" - , "do" - , "switch" - , "return" - , "try" - , "catch" - ] - , "requireSpacesInFunctionExpression": { - "beforeOpeningCurlyBrace": true - } - , "disallowSpacesInFunctionExpression": { - "beforeOpeningRoundBrace": true - } - , "disallowEmptyBlocks": true - , "disallowSpacesInsideObjectBrackets": true - , "disallowSpacesInsideArrayBrackets": true - , "disallowSpacesInsideParentheses": true - , "disallowSpaceAfterObjectKeys": true - , "disallowCommaBeforeLineBreak": true - , "requireOperatorBeforeLineBreak": [ - "?" - , "+" - , "-" - , "/" - , "*" - , "=" - , "==" - , "===" - , "!=" - , "!==" - , ">" - , ">=" - , "<" - , "<=" - ] - , "disallowSpaceAfterPrefixUnaryOperators": [ - "++" - , "--" - , "+" - , "-" - , "~" - , "!" - ] - , "disallowSpaceBeforePostfixUnaryOperators": [ - "++" - , "--" - ] - , "requireSpaceBeforeBinaryOperators": [ - "+" - , "-" - , "/" - , "*" - , "=" - , "==" - , "===" - , "!=" - , "!==" - ] - , "requireSpaceAfterBinaryOperators": [ - "+" - , "-" - , "/" - , "*" - , "=" - , "==" - , "===" - , "!=" - , "!==" - ] - , "disallowImplicitTypeConversion": [ - "numeric" - , "boolean" - , "binary" - , "string" - ] - , "disallowKeywords": [ - "with" - ] - , "disallowMultipleLineStrings": true - - , "validateQuoteMarks": "\"" - , "disallowMixedSpacesAndTabs": true - , "disallowTrailingWhitespace": true - - , "requireKeywordsOnNewLine": [ - ] - , "requireLineFeedAtFileEnd": true - , "requireCapitalizedConstructors": true - , "safeContextKeyword": "that" - - , "validateJSDoc": { - "checkParamNames": true - , "checkRedundantParams": true - , "requireParamTypes": true + "excludeFiles": [], + "requireCurlyBraces": [ + "if", + "else", + "for", + "while", + "do", + "try", + "catch" + ], + "requireSpaceAfterKeywords": [ + "if", + "else", + "for", + "while", + "do", + "switch", + "return", + "try", + "catch" + ], + "requireSpacesInFunctionExpression": { + "beforeOpeningCurlyBrace": true + }, + "disallowSpacesInFunctionExpression": { + "beforeOpeningRoundBrace": true + }, + "disallowEmptyBlocks": true, + "disallowSpacesInsideParentheses": true, + "disallowSpacesInsideObjectBrackets": true, + "disallowSpacesInsideArrayBrackets": true, + "disallowQuotedKeysInObjects": "allButReserved", + "disallowSpaceAfterObjectKeys": true, + "requireCommaBeforeLineBreak": true, + "requireOperatorBeforeLineBreak": [ + "?", + "+", + "-", + "/", + "*", + "=", + "==", + "===", + "!=", + "!==", + ">", + ">=", + "<", + "<=" + ], + "disallowLeftStickedOperators": [ + "?", + "+", + "-", + "/", + "*", + "=", + "==", + "===", + "!=", + "!==", + ">", + ">=", + "<", + "<=" + ], + "requireRightStickedOperators": [ + "!" + ], + "disallowRightStickedOperators": [ + "?", + "+", + "/", + "*", + ":", + "=", + "==", + "===", + "!=", + "!==", + ">", + ">=", + "<", + "<=" + ], + "disallowSpaceAfterPrefixUnaryOperators": [ + "++", + "--", + "+", + "-", + "~", + "!" + ], + "disallowSpaceBeforePostfixUnaryOperators": [ + "++", + "--" + ], + "requireSpaceBeforeBinaryOperators": [ + "+", + "-", + "/", + "*", + "=", + "==", + "===", + "!=", + "!==" + ], + "requireSpaceAfterBinaryOperators": [ + "+", + "-", + "/", + "*", + "=", + "==", + "===", + "!=", + "!==" + ], + "disallowImplicitTypeConversion": [ + "numeric", + "boolean", + "binary", + "string" + ], + "disallowKeywords": [ + "with" + ], + "disallowMultipleLineStrings": true, + "validateQuoteMarks": "\"", + "disallowMixedSpacesAndTabs": true, + "disallowTrailingWhitespace": true, + "requireKeywordsOnNewLine": [], + "requireLineFeedAtFileEnd": true, + "requireCapitalizedConstructors": true, + "safeContextKeyword": "that", + "validateJSDoc": { + "checkParamNames": true, + "checkRedundantParams": true, + "requireParamTypes": true } } diff --git a/.jshintrc b/.jshintrc index 7568631..182e34d 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,4 +1,4 @@ { - "asi": true - , "laxcomma": true + "asi": true, + "laxcomma": true } diff --git a/README.md b/README.md index e9f4ba0..efe5461 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Pjax - +[![browser support](https://ci.testling.com/MoOx/pjax.png)](https://ci.testling.com/MoOx/pjax) > Easily enable fast Ajax navigation on any website (using pushState + xhr) @@ -15,12 +15,9 @@ Especially for user that have low bandwidth connection._ **No more full page reload. No more lots of HTTP request.** -## No tests or Demo ? +## Demo -~~There is still some work to make this repo sexy with tests & simple demo.~~ -Actually there is a [WIP branch where I'm adding lot of tests](https://github.com/MoOx/pjax/tree/testling) - -For now [you can see this running on my website](http://moox.io), with sexy CSS animations when switching pages. +[You can see this running on my website](http://moox.io), with sexy CSS animations when switching pages. ## How Pjax works @@ -135,8 +132,8 @@ Let's talk more about the most basic way to get started: ```js new Pjax({ - elements: "a" // default is "a[href], form[action]" -, selectors: ["title", ".my-Header", ".my-Content", ".my-Sidebar"] + elements: "a", // default is "a[href], form[action]" + selectors: ["title", ".my-Header", ".my-Content", ".my-Sidebar"] }) ``` @@ -203,8 +200,8 @@ Examples: ```js new Pjax({ - selectors: ["title", ".Navbar", ".js-Pjax"] -, switches: { + selectors: ["title", ".Navbar", ".js-Pjax"], + switches: { // "title": Pjax.switches.outerHTML // default behavior ".Navbar": function(oldEl, newEl, options) { // here it's a stupid example since it's the default behavior too @@ -249,23 +246,23 @@ with or without [WOW.js](https://github.com/matthieua/WOW). ```js new Pjax({ - selectors: ["title", ".js-Pjax"] -, switches: { + selectors: ["title", ".js-Pjax"], + switches: { ".js-Pjax": Pjax.switches.sideBySide - } -, switchesOptions: { + }, + switchesOptions: { ".js-Pjax": { classNames: { // class added on the element that will be removed - remove: "Animated Animated--reverse Animate--fast Animate--noDelay" + remove: "Animated Animated--reverse Animate--fast Animate--noDelay", // class added on the element that will be added - , add: "Animated" + add: "Animated", // class added on the element when it go backward - , backward: "Animate--slideInRight" + backward: "Animate--slideInRight", // class added on the element when it go forward (used for new page too) - , forward: "Animate--slideInLeft" - } - , callbacks: { + forward: "Animate--slideInLeft" + }, + callbacks: { // to make a nice transition with 2 pages as the same time // we are playing with absolute positioning for element we are removing // & we need live metrics to have something great @@ -399,14 +396,6 @@ Pjax behavior, as you wish. Here is a summary of functions: -- `Pjax.isSupported` (`function()`): return wheter or not the browser handle pushState correctly -- `Pjax.on` (`function(els, events, listener, useCapture)`): addEventListener, that handles NodeList & supports space separated event name -- `Pjax.off` (`function(els, events, listener, useCapture)`): removeEventListener, that handles NodeList & supports space separated event name -- `Pjax.trigger` (`function(els, events)`): fireEvent, that handles NodeList & supports space separated event name -- `Pjax.clone` (`function(obj)`): clone object -- `Pjax.executeScripts` (`function(el)`): execute scripts that are inside an element (script src or inline scripts through `Pjax.evalScript`) -- `Pjax.evalScript` (`function(el)`): execute inline script. Don't execute a script if it contains `document.write`. - - `Pjax.prototype.log` (`function()`): console.log function that is enable/disabled by `debug` option - `Pjax.prototype.getElements` (`function(el)`): retrieve elements to attach Pjax behavior - `Pjax.prototype.parseDOM` (`function(el)`): parse DOM to attach behavior using `Pjax.prototype.getElements` & `Pjax.prototype.attachLink` @@ -495,9 +484,9 @@ wrapper on each page (to avoid differences of DOM between pages) ```html diff --git a/tests/lib/clone.js b/tests/lib/clone.js new file mode 100644 index 0000000..9997943 --- /dev/null +++ b/tests/lib/clone.js @@ -0,0 +1,17 @@ +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/eval-scripts.js b/tests/lib/eval-scripts.js new file mode 100644 index 0000000..43113b9 --- /dev/null +++ b/tests/lib/eval-scripts.js @@ -0,0 +1,22 @@ +var tape = require("tape") + +var evalScript = require("../../lib/eval-script") + +tape("test evalScript method", function(t) { + document.body.className = "" + + var script = document.createElement("script") + script.innerHTML = "document.body.className = 'executed'" + + t.equal(document.body.className, "", "script hasn't been executed yet") + + evalScript(script) + t.equal(document.body.className, "executed", "script has been properly executed") + + // script.innerHTML = "document.write('failure')" + // var bodyText = document.body.text + // evalScript(script) + // t.equal(document.body.text, bodyText, "document.write hasn't been executed") + + t.end() +}) diff --git a/tests/lib/events.js b/tests/lib/events.js new file mode 100644 index 0000000..f13e112 --- /dev/null +++ b/tests/lib/events.js @@ -0,0 +1,92 @@ +var tape = require("tape") + +var on = require("../../lib/events/on") +var off = require("../../lib/events/off") +var trigger = require("../../lib/events/trigger") + +var el = document.createElement("div") +var el2 = document.createElement("span") +var els = [el, el2] +var eventType2 = "resize" +var eventsType = "click resize" +var classCb = function() { + this.className += "on" +} +var attrCb = function() { + this.setAttribute("data-state", this.getAttribute("data-state") + "ON") +} + +tape("test events on/off/trigger for one element, one event", function(t) { + el.className = "" + on(el, "click", classCb) + trigger(el, "click") + t.equal(el.className, "on", "attached callback has been fired properly") + + el.className = "off" + off(el, "click", classCb) + trigger(el, "click") + t.equal(el.className, "off", "triggered event didn't fire detached callback") + + t.end() +}) + +tape("test events on/off/trigger for multiple elements, one event", function(t) { + el.className = "" + el2.className = "" + + on(els, "click", classCb) + trigger(els, "click") + t.equal(el.className, "on", "attached callback has been fired properly on the first element") + t.equal(el2.className, "on", "attached callback has been fired properly on the second element") + + el.className = "off" + el2.className = "off" + off(els, "click", classCb) + trigger(els, "click") + t.equal(el.className, "off", "triggered event didn't fire detached callback on the first element") + t.equal(el2.className, "off", "triggered event didn't fire detached callback on the second element") + + t.end() +}) + +tape("test events on/off/trigger for one element, multiple events", function(t) { + el.className = "" + on(el, "click mouseover", classCb) + trigger(el, "click mouseover") + t.equal(el.className, "onon", "attached callbacks have been fired properly") + + el.className = "off" + off(el, "click mouseover", classCb) + trigger(el, "click mouseover") + t.equal(el.className, "off", "triggered events didn't fire detached callback") + + t.end() +}) + +tape("test events on/off/trigger for multiple elements, multiple events", function(t) { + el.className = "" + el2.className = "" + el.setAttribute("data-state", "") + el2.setAttribute("data-state", "") + on(els, "click mouseover", classCb) + on(els, "resize scroll", attrCb) + trigger(els, "click mouseover resize scroll") + t.equal(el.className, "onon", "attached callbacks has been fired properly on the first element") + t.equal(el.getAttribute("data-state"), "ONON", "attached callbacks has been fired properly on the first element") + t.equal(el2.className, "onon", "attached callbacks has been fired properly on the second element") + t.equal(el2.getAttribute("data-state"), "ONON", "attached callbacks has been fired properly on the second element") + + el.className = "off" + el2.className = "off" + el.setAttribute("data-state", "off") + el2.setAttribute("data-state", "off") + off(els, "click mouseover", classCb) + off(els, "resize scroll", attrCb) + trigger(els, "click mouseover resize scroll") + t.equal(el.className, "off", "triggered events didn't fire detached callbacks on the first element") + t.equal(el.getAttribute("data-state"), "off", "triggered events didn't fire detached callbacks on the first element") + t.equal(el2.className, "off", "triggered events didn't fire detached callbacks on the first element") + t.equal(el2.getAttribute("data-state"), "off", "triggered events didn't fire detached callbacks on the first element") + + t.end() +}) diff --git a/tests/lib/execute-scripts.js b/tests/lib/execute-scripts.js new file mode 100644 index 0000000..bfea9b1 --- /dev/null +++ b/tests/lib/execute-scripts.js @@ -0,0 +1,16 @@ +var tape = require("tape") + +var executeScripts = require("../../lib/execute-scripts") + +tape("test executeScripts method", function(t) { + document.body.className = "" + + var container = document.createElement("div") + container.innerHTML = "<" + "script" + ">document.body.className = 'executed';<" + "script" + ">document.body.className += ' correctly';" + + t.equal(document.body.className, "", "script hasn't been executed yet") + executeScripts(container) + t.equal(document.body.className, "executed correctly", "script has been properly executed") + + t.end() +}) diff --git a/tests/lib/foreach-els.js b/tests/lib/foreach-els.js new file mode 100644 index 0000000..7212626 --- /dev/null +++ b/tests/lib/foreach-els.js @@ -0,0 +1,45 @@ +var tape = require("tape") + +var forEachEls = require("../../lib/foreach-els.js") + +var div = document.createElement("div") +var span = document.createElement("span") +var cb = function(el) { + el.innerHTML = "boom" +} + +tape("test forEachEls on one element", function(t) { + div.innerHTML = "div tag" + forEachEls(div, cb) + + t.equal(div.innerHTML, "boom", "works correctly on one element") + t.end() +}) + + +tape("test forEachEls on an array", function(t) { + div.innerHTML = "div tag" + span.innerHTML = "span tag" + + forEachEls([div, span], cb) + + t.equal(div.innerHTML, "boom", "works correctly on the first element of the array") + t.equal(span.innerHTML, "boom", "works correctly on the last element of the array") + + t.end() +}) + +tape("test forEachEls on a NodeList", function(t) { + div.innerHTML = "div tag" + span.innerHTML = "span tag" + + var frag = document.createDocumentFragment() + frag.appendChild(div) + frag.appendChild(span) + forEachEls(frag.childNodes, cb) + + t.equal(div.innerHTML, "boom", "works correctly on the first element of the document fragment") + t.equal(span.innerHTML, "boom", "works correctly on the last element of the document fragment") + + t.end() +}) diff --git a/tests/lib/foreach-selectors.js b/tests/lib/foreach-selectors.js new file mode 100644 index 0000000..1175b32 --- /dev/null +++ b/tests/lib/foreach-selectors.js @@ -0,0 +1,24 @@ +var tape = require("tape") + +var forEachEls = require("../../lib/foreach-selectors.js") + +var cb = function(el) { + el.className = "modified" +} + +tape("test forEachSelector", function(t) { + forEachEls(["html", "body"], cb) + + t.equal(document.documentElement.className, "modified", "callback has been executed on first selector") + t.equal(document.body.className, "modified", "callback has been executed on first selector") + + document.documentElement.className = "" + document.body.className = "" + + forEachEls(["html", "body"], cb, null, document.documentElement) + + t.equal(document.documentElement.className, "", "callback has not been executed on first selector when context is used") + t.equal(document.body.className, "modified", "callback has been executed on first selector when context is used") + + t.end() +}) diff --git a/tests/lib/is-supported.js b/tests/lib/is-supported.js new file mode 100644 index 0000000..d24f2c3 --- /dev/null +++ b/tests/lib/is-supported.js @@ -0,0 +1,8 @@ +var tape = require("tape") + +var isSupported = require("../../lib/is-supported.js") + +tape("test isSupported method", function(t) { + t.true(isSupported(), "well, we run test on supported browser, so it should be ok here") + t.end() +}) diff --git a/tests/lib/proto/attach-link.js b/tests/lib/proto/attach-link.js new file mode 100644 index 0000000..aa66a88 --- /dev/null +++ b/tests/lib/proto/attach-link.js @@ -0,0 +1,58 @@ +var tape = require("tape") + +var on = require("../../../lib/events/on") +var trigger = require("../../../lib/events/trigger") +var attachLink = require("../../../lib/proto/attach-link") + +var a = document.createElement("a") +var attr = "data-pjax-click-state" +var preventDefault = function(e) { e.preventDefault() } + +tape("test attach link prototype method", function(t) { + t.plan(7) + + attachLink.call({ + options: {}, + refresh: function() { + t.equal(a.getAttribute(attr), "refresh", "triggering exact same url refresh the page") + }, + loadUrl: function() { + t.equal(a.getAttribute(attr), "load", "triggering a internal link actually load the page") + } + }, a) + + var internalUri = window.location.protocol + "//" + window.location.host + window.location.pathname + window.location.search + + a.href = internalUri + on(a, "click", preventDefault) // to avoid link to be open (break testing env) + trigger(a, "click", {metaKey: true}) + t.equal(a.getAttribute(attr), "modifier", "event key modifier stop behavior") + + a.href = "http://external.com/" + trigger(a, "click") + t.equal(a.getAttribute(attr), "external", "external url stop behavior") + + a.href = internalUri + "#anchor" + trigger(a, "click") + t.equal(a.getAttribute(attr), "anchor-present", "internal anchor stop behavior") + + window.location.hash = "#anchor" + a.href = internalUri + "#another-anchor" + trigger(a, "click") + t.notEqual(a.getAttribute(attr), "anchor", "differents anchors stop behavior") + window.location.hash = "" + + a.href = internalUri + "#" + trigger(a, "click") + t.equal(a.getAttribute(attr), "anchor-empty", "empty anchor stop behavior") + + a.href = internalUri + trigger(a, "click") + // see refresh defined above + + a.href = window.location.protocol + "//" + window.location.host + "/internal" + trigger(a, "click") + // see loadUrl defined above + + t.end() +}) diff --git a/tests/lib/proto/parse-element.js b/tests/lib/proto/parse-element.js new file mode 100644 index 0000000..69de686 --- /dev/null +++ b/tests/lib/proto/parse-element.js @@ -0,0 +1,18 @@ +var tape = require("tape") + +var parseElement = require("../../../lib/proto/parse-element") +var protoMock = {attachLink: function() { return true}} +tape("test parse element prototype method", function(t) { + + t.doesNotThrow(function() { + var a = document.createElement("a") + parseElement.call(protoMock, a) + }, " element can be parsed") + + t.throws(function() { + var form = document.createElement("form") + parseElement.call(protoMock, form) + }, "
cannot be used (for now)") + + t.end() +}) diff --git a/tests/lib/request.js b/tests/lib/request.js new file mode 100644 index 0000000..41f8252 --- /dev/null +++ b/tests/lib/request.js @@ -0,0 +1,16 @@ +var tape = require("tape") + +var request = require("../../lib/request.js") + +tape("test xhr request", function(t) { + var xhr = request("https://api.github.com/", function(result) { + try { + result = JSON.parse(result) + } + catch (e) { + t.fail("xhr doesn't get a JSON response") + } + t.same(typeof result, "object", "xhr request get a result") + t.end() + }) +}) diff --git a/tests/lib/switch-selectors.js b/tests/lib/switch-selectors.js new file mode 100644 index 0000000..19830da --- /dev/null +++ b/tests/lib/switch-selectors.js @@ -0,0 +1,9 @@ +var tape = require("tape") + +var switchesSelectors = require("../../lib/switches-selectors.js") + +tape("test switchesSelectors", function(t) { + t.fail() + + t.end() +}) diff --git a/tests/todo,sorry b/tests/todo,sorry deleted file mode 100644 index e69de29..0000000