Compare commits
18 Commits
feature/ya
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5e28873da | ||
|
|
c26c223a65 | ||
|
|
3b3f4d7794 | ||
|
|
7940a6e3e5 | ||
|
|
493d56c2d0 | ||
|
|
c13149626b | ||
|
|
3c1a4b2e18 | ||
|
|
2c6506af65 | ||
|
|
fefb63ae87 | ||
|
|
52fb3bf938 | ||
|
|
6b648a7c90 | ||
|
|
03ebc657f0 | ||
|
|
6f39767cf9 | ||
|
|
03d64863c8 | ||
|
|
c36225a24c | ||
|
|
c589ab9c25 | ||
|
|
8abb21e1e9 | ||
|
|
8dbe7553b9 |
3
.eslintignore
Normal file
3
.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pjax.js
|
||||||
|
pjax.min.js
|
||||||
|
*.json
|
||||||
8
.eslintrc.json
Normal file
8
.eslintrc.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": ["eslint-config-i-am-meticulous/es5"],
|
||||||
|
"rules": {
|
||||||
|
"import/order": "off",
|
||||||
|
"import/max-dependencies": "off",
|
||||||
|
"import/extensions": ["error", "never"]
|
||||||
|
}
|
||||||
|
}
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,6 +1,9 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
node_modules/
|
node_modules/
|
||||||
|
.nyc_output/
|
||||||
|
.idea/
|
||||||
|
dist/
|
||||||
|
coverage/
|
||||||
tests/scripts/index.html
|
tests/scripts/index.html
|
||||||
pjax.js
|
pjax.js
|
||||||
.nyc_output/
|
|
||||||
pjax.min.js
|
pjax.min.js
|
||||||
|
|||||||
132
.jscsrc
132
.jscsrc
@@ -1,132 +0,0 @@
|
|||||||
{
|
|
||||||
"excludeFiles": [
|
|
||||||
"node_modules/**",
|
|
||||||
"pjax.js",
|
|
||||||
"pjax.min.js"
|
|
||||||
],
|
|
||||||
"fileExtensions": [
|
|
||||||
".js"
|
|
||||||
],
|
|
||||||
"requireCurlyBraces": [
|
|
||||||
"if",
|
|
||||||
"else",
|
|
||||||
"for",
|
|
||||||
"while",
|
|
||||||
"do",
|
|
||||||
"try",
|
|
||||||
"catch"
|
|
||||||
],
|
|
||||||
"requireSpaceAfterKeywords": [
|
|
||||||
"if",
|
|
||||||
"else",
|
|
||||||
"for",
|
|
||||||
"while",
|
|
||||||
"do",
|
|
||||||
"switch",
|
|
||||||
"return",
|
|
||||||
"try",
|
|
||||||
"catch"
|
|
||||||
],
|
|
||||||
"requireSpaceBeforeBlockStatements": true,
|
|
||||||
"requireParenthesesAroundIIFE": true,
|
|
||||||
"requireSpacesInConditionalExpression": {
|
|
||||||
"afterTest": true,
|
|
||||||
"beforeConsequent": true,
|
|
||||||
"afterConsequent": true,
|
|
||||||
"beforeAlternate": true
|
|
||||||
},
|
|
||||||
"requireSpacesInFunctionExpression": {
|
|
||||||
"beforeOpeningCurlyBrace": true
|
|
||||||
},
|
|
||||||
"disallowSpacesInFunctionExpression": {
|
|
||||||
"beforeOpeningRoundBrace": true
|
|
||||||
},
|
|
||||||
"disallowMultipleVarDecl": true,
|
|
||||||
"requireBlocksOnNewline": 1,
|
|
||||||
"disallowPaddingNewlinesInBlocks": true,
|
|
||||||
"disallowEmptyBlocks": true,
|
|
||||||
"disallowSpacesInsideObjectBrackets": true,
|
|
||||||
"disallowSpacesInsideArrayBrackets": true,
|
|
||||||
"disallowSpacesInsideParentheses": true,
|
|
||||||
"disallowQuotedKeysInObjects": "allButReserved",
|
|
||||||
"disallowSpaceAfterObjectKeys": true,
|
|
||||||
"requireCommaBeforeLineBreak": true,
|
|
||||||
"requireOperatorBeforeLineBreak": [
|
|
||||||
"?",
|
|
||||||
"+",
|
|
||||||
"-",
|
|
||||||
"/",
|
|
||||||
"*",
|
|
||||||
"=",
|
|
||||||
"==",
|
|
||||||
"===",
|
|
||||||
"!=",
|
|
||||||
"!==",
|
|
||||||
">",
|
|
||||||
">=",
|
|
||||||
"<",
|
|
||||||
"<="
|
|
||||||
],
|
|
||||||
"disallowSpaceAfterPrefixUnaryOperators": [
|
|
||||||
"++",
|
|
||||||
"--",
|
|
||||||
"+",
|
|
||||||
"-",
|
|
||||||
"~",
|
|
||||||
"!"
|
|
||||||
],
|
|
||||||
"disallowSpaceBeforePostfixUnaryOperators": [
|
|
||||||
"++",
|
|
||||||
"--"
|
|
||||||
],
|
|
||||||
"requireSpaceBeforeBinaryOperators": [
|
|
||||||
"+",
|
|
||||||
"-",
|
|
||||||
"/",
|
|
||||||
"*",
|
|
||||||
"=",
|
|
||||||
"==",
|
|
||||||
"===",
|
|
||||||
"!=",
|
|
||||||
"!=="
|
|
||||||
],
|
|
||||||
"requireSpaceAfterBinaryOperators": [
|
|
||||||
"+",
|
|
||||||
"-",
|
|
||||||
"/",
|
|
||||||
"*",
|
|
||||||
"=",
|
|
||||||
"==",
|
|
||||||
"===",
|
|
||||||
"!=",
|
|
||||||
"!=="
|
|
||||||
],
|
|
||||||
"disallowImplicitTypeConversion": [
|
|
||||||
"numeric",
|
|
||||||
"boolean",
|
|
||||||
"binary",
|
|
||||||
"string"
|
|
||||||
],
|
|
||||||
"requireCamelCaseOrUpperCaseIdentifiers": "ignoreProperties",
|
|
||||||
"disallowKeywords": [
|
|
||||||
"with"
|
|
||||||
],
|
|
||||||
"disallowMultipleLineStrings": true,
|
|
||||||
"validateQuoteMarks": "\"",
|
|
||||||
"validateIndentation": 2,
|
|
||||||
"disallowMixedSpacesAndTabs": true,
|
|
||||||
"disallowTrailingWhitespace": true,
|
|
||||||
"requireKeywordsOnNewLine": [
|
|
||||||
"else"
|
|
||||||
],
|
|
||||||
"requireLineFeedAtFileEnd": true,
|
|
||||||
"requireCapitalizedConstructors": true,
|
|
||||||
"safeContextKeyword": "that",
|
|
||||||
"requireDotNotation": true,
|
|
||||||
"jsDoc": {
|
|
||||||
"checkParamNames": true,
|
|
||||||
"checkRedundantParams": true,
|
|
||||||
"requireParamTypes": true
|
|
||||||
},
|
|
||||||
"requireSpaceAfterLineComment": true
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"newcap": false,
|
|
||||||
"undef": true,
|
|
||||||
"unused": true,
|
|
||||||
"asi": true,
|
|
||||||
"esnext": true,
|
|
||||||
"node": true,
|
|
||||||
"browser": true
|
|
||||||
}
|
|
||||||
3
.prettierignore
Normal file
3
.prettierignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pjax.js
|
||||||
|
pjax.min.js
|
||||||
|
*.json
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
language: "node_js"
|
language: "node_js"
|
||||||
node_js:
|
node_js:
|
||||||
- "6"
|
- "10"
|
||||||
- "8"
|
|
||||||
# Force Travis to use npm v5
|
|
||||||
# https://github.com/travis-ci/travis-ci/issues/4653#issuecomment-194051953
|
|
||||||
before_install: if [[ `npm -v` != 5* ]]; then npm i -g npm@5; fi
|
|
||||||
|
|||||||
28
CHANGELOG.md
28
CHANGELOG.md
@@ -1,3 +1,31 @@
|
|||||||
|
# 0.2.8 - 2019-03-09
|
||||||
|
|
||||||
|
- Fixed: Edge form support.
|
||||||
|
([#178](https://github.com/MoOx/pjax/pull/178) - @robinnorth)
|
||||||
|
- Fixed: Removed keyup event listener for forms.
|
||||||
|
([#184](https://github.com/MoOx/pjax/pull/184) - @BehindTheMath)
|
||||||
|
- Fixed: Bugs in evalScripts().
|
||||||
|
([#186](https://github.com/MoOx/pjax/pull/186) - @BehindTheMath)
|
||||||
|
- Fixed: Handle non-string HTML passed to loadContent().
|
||||||
|
([#200](https://github.com/MoOx/pjax/pull/200) - @BehindTheMath)
|
||||||
|
- Tooling: Switch linting to ESLint and Prettier.
|
||||||
|
([#191](https://github.com/MoOx/pjax/pull/191) - @BehindTheMath)
|
||||||
|
|
||||||
|
# 0.2.7 - 2018-08-15
|
||||||
|
|
||||||
|
- Fixed: Parsing values of option elements in forms.
|
||||||
|
([#162](https://github.com/MoOx/pjax/pull/162) - @BehindTheMath)
|
||||||
|
- Fixed: Added index.d.ts to package.json so it will be installed by npm.
|
||||||
|
([c589ab9](https://github.com/MoOx/pjax/commit/c589ab9c25bee6161bf3e557eaca44e51c14fb89) - @BehindTheMath)
|
||||||
|
- Fixed: `options.history` to correctly parse being set to false.
|
||||||
|
([#165](https://github.com/MoOx/pjax/pull/165) - @BehindTheMath).
|
||||||
|
- Fixed: Pass the current `options` object to `loadContent()`.
|
||||||
|
([#171](https://github.com/MoOx/pjax/pull/171) - @BehindTheMath)
|
||||||
|
- Fixed: Ensure correct XHR encoding for multipart/form-data forms
|
||||||
|
([#174](https://github.com/MoOx/pjax/pull/174) - @BehindTheMath)
|
||||||
|
- Added: More documentation.
|
||||||
|
([#160](https://github.com/MoOx/pjax/pull/160), [#171](https://github.com/MoOx/pjax/pull/171) - @robinnorth, @BehindTheMath)
|
||||||
|
|
||||||
# 0.2.6 - 2018-04-30
|
# 0.2.6 - 2018-04-30
|
||||||
|
|
||||||
- Fixed: Form submission for GET requests.
|
- Fixed: Form submission for GET requests.
|
||||||
|
|||||||
456
README.md
456
README.md
@@ -4,14 +4,9 @@
|
|||||||
|
|
||||||
> Easily enable fast AJAX navigation on any website (using pushState() + XHR)
|
> Easily enable fast AJAX navigation on any website (using pushState() + XHR)
|
||||||
|
|
||||||
Pjax is **a standalone JavaScript module** that uses
|
Pjax is **a standalone JavaScript module** that uses [AJAX](https://developer.mozilla.org/en-US/docs/Web/Guide/AJAX) (XmlHttpRequest) and [pushState()](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history) to deliver a fast browsing experience.
|
||||||
AJAX (XmlHttpRequest) and
|
|
||||||
[pushState()](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history)
|
|
||||||
to deliver a fast browsing experience.
|
|
||||||
|
|
||||||
_It allows you to completely transform the user experience of standard websites
|
_It allows you to completely transform the user experience of standard websites (server-side generated or static ones) to make users feel like they are browsing an app, especially for those with low bandwidth connections._
|
||||||
(server-side generated or static ones) to make them feel like they are browsing an app,
|
|
||||||
especially for users with low bandwidth connection._
|
|
||||||
|
|
||||||
**No more full page reloads. No more multiple HTTP requests.**
|
**No more full page reloads. No more multiple HTTP requests.**
|
||||||
|
|
||||||
@@ -19,44 +14,66 @@ _Pjax does not rely on other libraries, like jQuery or similar. It is written en
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
- You can install Pjax from **npm**:
|
- You can link directly to the [bundle](https://cdn.jsdelivr.net/npm/pjax/pjax.js):
|
||||||
```shell
|
|
||||||
npm install pjax
|
|
||||||
```
|
|
||||||
|
|
||||||
- You can also link directly to the [bundle](https://cdn.jsdelivr.net/npm/pjax/pjax.js):
|
|
||||||
```html
|
```html
|
||||||
<script src="https://cdn.jsdelivr.net/npm/pjax@VERSION/pjax.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/pjax@VERSION/pjax.js"></script>
|
||||||
```
|
```
|
||||||
Or the [minified bundle](https://cdn.jsdelivr.net/npm/pjax/pjax.min.js):
|
|
||||||
|
- Or the [minified bundle](https://cdn.jsdelivr.net/npm/pjax/pjax.min.js):
|
||||||
```html
|
```html
|
||||||
<script src="https://cdn.jsdelivr.net/npm/pjax@VERSION/pjax.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/pjax@VERSION/pjax.min.js"></script>
|
||||||
```
|
```
|
||||||
|
|
||||||
## How Pjax works
|
- You can also install Pjax from **npm**:
|
||||||
|
```shell
|
||||||
|
npm install pjax
|
||||||
|
```
|
||||||
|
**Note**: If you use this option, you will need to do one of the following:
|
||||||
|
- Link a script tag to either `pjax.js` or `pjax.min.js`. E.g.:
|
||||||
|
```html
|
||||||
|
<script src="./node_modules/pjax/pjax.js"></script>
|
||||||
|
```
|
||||||
|
- Use a bundler like Webpack. (`index.js` cannot be used in the browser without a bundler).
|
||||||
|
|
||||||
Pjax loads pages using AJAX and updates the browser's current URL using `pushState()` without reloading your page's layout or any resources (JS, CSS), giving a fast page load.
|
- Or you can clone the repo and build the bundle from the source using npm:
|
||||||
|
```shell
|
||||||
|
git clone https://github.com/MoOx/pjax.git
|
||||||
|
cd pjax
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
and then link a script tag to either `pjax.js` or `pjax.min.js`. E.g.:
|
||||||
|
```html
|
||||||
|
<script src="./pjax.min.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
_But under the hood, it's just ONE HTTP request with a `pushState()` call._
|
---
|
||||||
|
|
||||||
Obviously, for [browsers that don't support `history.pushState()`](http://caniuse.com/#search=pushstate) Pjax gracefully degrades and does not do anything at all.
|
## What Pjax Does
|
||||||
|
|
||||||
It simply works with all permalinks and can update all parts of the page you
|
_Under the hood, it's just ONE HTTP request with a `pushState()` call._
|
||||||
want (including HTML metas, title, and navigation state).
|
|
||||||
|
|
||||||
- It's not limited to one container, like jQuery-Pjax is.
|
Pjax loads pages using AJAX and updates the browser's current URL using `pushState()` _without_ reloading your page's layout or any resources (JS, CSS), giving a fast page load.
|
||||||
- It fully supports browser history (back and forward buttons).
|
|
||||||
- It supports keyboard browsing.
|
It works with all permalinks and can update all the parts of the page you want (including HTML metas, title, and navigation state).
|
||||||
|
|
||||||
|
In the case of [browsers that don't support `history.pushState()`](http://caniuse.com/#search=pushstate), Pjax gracefully degrades and does not do anything at all.
|
||||||
|
|
||||||
|
Additionally, Pjax:
|
||||||
|
|
||||||
|
- Is not limited to one container, like jQuery-Pjax is.
|
||||||
|
- Fully supports browser history (back and forward buttons).
|
||||||
|
- Supports keyboard browsing.
|
||||||
- Automatically falls back to standard navigation for external pages (thanks to Captain Obvious's help).
|
- Automatically falls back to standard navigation for external pages (thanks to Captain Obvious's help).
|
||||||
- Automatically falls back to standard navigation for internal pages that do not have an appropriate DOM tree.
|
- Automatically falls back to standard navigation for internal pages that do not have an appropriate DOM tree.
|
||||||
- You can add pretty cool CSS transitions (animations) very easily.
|
- Allows for CSS transitions (animations) very easily.
|
||||||
- It's around 4kb (minified and gzipped).
|
- Is only around 6kb (minified and gzipped).
|
||||||
|
|
||||||
### Under the hood
|
## How Pjax Works
|
||||||
|
|
||||||
- It listens to every click on links _you want_ (by default all of them).
|
- It listens to every click on links _you want_ (by default all of them).
|
||||||
- When an internal link is clicked, Pjax grabs HTML from your server via AJAX.
|
- When an internal link is clicked, Pjax fetches the page's HTML via AJAX.
|
||||||
- Pjax renders the page's DOM tree (without loading any resources - images, CSS, JS...).
|
- Pjax renders the page's DOM tree (without loading any resources - images, CSS, JS, etc).
|
||||||
- It checks that all defined parts can be replaced:
|
- It checks that all defined parts can be replaced:
|
||||||
- If the page doesn't meet the requirements, standard navigation is used.
|
- If the page doesn't meet the requirements, standard navigation is used.
|
||||||
- If the page meets the requirements, Pjax does all defined DOM replacements.
|
- If the page meets the requirements, Pjax does all defined DOM replacements.
|
||||||
@@ -64,9 +81,8 @@ want (including HTML metas, title, and navigation state).
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Pjax is fully automatic. You don't need to setup anything in the existing HTML.
|
_Pjax is fully automatic_. You don't need to change anything about your existing HTML,
|
||||||
You just need to designate some elements on your page that will be replaced when
|
you just need to designate which elements on your page that you want to be replaced when your site is navigated.
|
||||||
you navigate your site.
|
|
||||||
|
|
||||||
Consider the following page.
|
Consider the following page.
|
||||||
|
|
||||||
@@ -74,57 +90,89 @@ Consider the following page.
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<!-- metas, title, styles, ... -->
|
<!-- metas, title, styles, etc -->
|
||||||
|
<title>My Cool Blog</title>
|
||||||
|
<meta name="description" content="Welcome to My Cool Blog">
|
||||||
|
<link href="/styles.css" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header class="my-Header"><nav><!-- a .is-active is in there --></nav></header>
|
<header class="the-header">
|
||||||
<section class="my-Content">
|
<nav>
|
||||||
Sha blah <a href="/blah ">blah</a>.
|
<a href="/" class="is-active">Home</a>
|
||||||
|
<a href="/about">About</a>
|
||||||
|
<a href="/contact">Contact</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="the-content">
|
||||||
|
<h1>My Cool Blog</h1>
|
||||||
|
<p>
|
||||||
|
Thanks for stopping by!
|
||||||
|
|
||||||
|
<a href="/about">Click Here to find out more about me.</a>
|
||||||
|
</p>
|
||||||
</section>
|
</section>
|
||||||
<aside class="my-Sidebar">Sidebar stuff</aside>
|
|
||||||
<footer class="my-Footer"></footer>
|
<aside class="the-sidebar">
|
||||||
|
<h3>Recent Posts</h3>
|
||||||
|
<!-- sidebar content -->
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<footer class="the-footer">
|
||||||
|
© My Cool Blog
|
||||||
|
</footer>
|
||||||
|
|
||||||
<script src="onDomReadystuff.js"></script>
|
<script src="onDomReadystuff.js"></script>
|
||||||
<script><!-- analytics --></script>
|
<script>
|
||||||
|
// analytics
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
```
|
```
|
||||||
|
|
||||||
We want Pjax to intercept the URL `/blah`, and replace `.my-Content` with the results of the request.
|
We want Pjax to intercept the URL `/about`, and replace `.the-content` with the resulting content of the request.
|
||||||
Oh and the `<nav>` (that contains a status marker somewhere) can be updated too (or stay the same, as you wish).
|
|
||||||
And also the `<aside>` please.
|
|
||||||
So we want to update `[".my-Header", ".my-Content", ".my-Sidebar"]`, **without reloading styles nor scripts**.
|
|
||||||
|
|
||||||
We do this by telling Pjax to listen on `a` tags and use CSS selectors defined above (without forgetting minimal meta):
|
It would also be nice if we could replace the `<nav>` to show that the `/about` link is active, as well as update our page meta and the `<aside>` sidebar.
|
||||||
|
|
||||||
|
So all in all we want to update the page title and meta, header, content area, and sidebar, **without reloading styles or scripts**.
|
||||||
|
|
||||||
|
We can easily do this by telling Pjax to listen on all `a` tags (which is the default) and use CSS selectors defined above (without forgetting minimal meta):
|
||||||
|
|
||||||
``` javascript
|
``` javascript
|
||||||
var pjax = new Pjax({ selectors: ["title", ".my-Header", ".my-Content", ".my-Sidebar"] })
|
var pjax = new Pjax({
|
||||||
|
selectors: [
|
||||||
|
"title",
|
||||||
|
"meta[name=description]",
|
||||||
|
".the-header",
|
||||||
|
".the-content",
|
||||||
|
".the-sidebar",
|
||||||
|
]
|
||||||
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
Now, when someone in a Pjax-compatible browser clicks "blah", the content of all selectors will be replaced with the one found in the "blah" content.
|
Now, when someone in a Pjax-compatible browser clicks an internal link on the page, the content of each of the selectors will be replaced with the specific content pieces found in the next page's content.
|
||||||
|
|
||||||
_Magic! For real!_ **There is no need to do anything server-side!**
|
_Magic! For real!_ **There is no need to do anything server-side!**
|
||||||
|
|
||||||
## Differences with [jQuery-pjax](https://github.com/defunkt/jquery-pjax)
|
## Differences with [jQuery-pjax](https://github.com/defunkt/jquery-pjax)
|
||||||
|
|
||||||
- No jQuery dependency
|
- No jQuery dependency.
|
||||||
- Not limited to a container
|
- Not limited to a container.
|
||||||
- No server-side requirements
|
- No server-side requirements.
|
||||||
- Works for CommonJS environment (Webpack/Browserify), AMD (RequireJS) or even globally
|
- Works for CommonJS environment (Webpack/Browserify), AMD (RequireJS) or even globally.
|
||||||
- Allow page transition with CSS animations
|
- Allows page transitions with CSS animations.
|
||||||
- Can be easily tweaked, since every method is public (and as a result, overridable)
|
- Can be easily tweaked, since every method is public (and as a result, overridable).
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
Pjax only works with [browsers that support the `history.pushState()` API](http://caniuse.com/#search=pushstate).
|
Pjax only works with [browsers that support the `history.pushState()` API](http://caniuse.com/#search=pushstate). When the API isn't supported, Pjax goes into fallback mode (and it just does nothing).
|
||||||
When the API isn't supported, Pjax goes into fallback mode (and it just does nothing).
|
|
||||||
|
|
||||||
To see if Pjax is actually supported by your browser, use `Pjax.isSupported()`.
|
To see if Pjax is actually supported by your browser, use `Pjax.isSupported()`.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Methods
|
### `new Pjax()`
|
||||||
|
|
||||||
#### `new Pjax()`
|
|
||||||
|
|
||||||
Let's talk more about the most basic way to get started.
|
Let's talk more about the most basic way to get started.
|
||||||
|
|
||||||
@@ -133,23 +181,24 @@ When instantiating `Pjax`, you can pass options in to the constructor as an obje
|
|||||||
```js
|
```js
|
||||||
var pjax = new Pjax({
|
var pjax = new Pjax({
|
||||||
elements: "a", // default is "a[href], form[action]"
|
elements: "a", // default is "a[href], form[action]"
|
||||||
selectors: ["title", ".my-Header", ".my-Content", ".my-Sidebar"]
|
selectors: ["title", ".the-header", ".the-content", ".the-sidebar"]
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
This will enable Pjax on all links, and designate the part to replace using CSS selectors `"title", ".my-Header", ".my-Content", ".my-Sidebar"`.
|
This will enable Pjax on all links, and designate the part to replace using CSS selectors `"title", ".the-header", ".the-content", ".the-sidebar"`.
|
||||||
|
|
||||||
For some reason, you might want to just target some elements to apply Pjax behavior.
|
In some cases, you might want to only target some specific elements to apply Pjax behavior. In that case, you can do two different things:
|
||||||
In that case, you can do two different things:
|
|
||||||
|
|
||||||
- Use a custom selector like "a.js-Pjax" or ".js-Pjax a" depending on what you want.
|
1. Use a custom CSS selector( such as `"a.js-Pjax"` or `".js-Pjax a"`, etc).
|
||||||
- Override `Pjax.prototype.getElements` that just basically `querySelectorAll` the `elements` option. In this function you just need to return a `NodeList`.
|
2. Override `Pjax.prototype.getElements`.
|
||||||
|
- **Note**: If doing this, make sure to return a `NodeList`.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// use case 1
|
// use case 1
|
||||||
var pjax = new Pjax({ elements: "a.js-Pjax" })
|
var pjax = new Pjax({ elements: "a.js-Pjax" })
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
// use case 2
|
// use case 2
|
||||||
Pjax.prototype.getElements = function() {
|
Pjax.prototype.getElements = function() {
|
||||||
return document.getElementsByClassName(".js-Pjax")
|
return document.getElementsByClassName(".js-Pjax")
|
||||||
@@ -158,21 +207,21 @@ Pjax.prototype.getElements = function() {
|
|||||||
var pjax = new Pjax()
|
var pjax = new Pjax()
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `loadUrl(href, [options])`
|
### `loadUrl(href, [options])`
|
||||||
|
|
||||||
With this method, you can manually trigger loading of a URL:
|
With this method, you can manually trigger the loading of a URL:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var pjax = new Pjax()
|
var pjax = new Pjax()
|
||||||
|
|
||||||
// use case 1 (without options override)
|
// use case 1
|
||||||
pjax.loadUrl("/your-url")
|
pjax.loadUrl("/your-url")
|
||||||
|
|
||||||
// use case 2 (with options override)
|
// use case 2 (with options override)
|
||||||
pjax.loadUrl("/your-other-url", { timeout: 10 })
|
pjax.loadUrl("/your-other-url", { timeout: 10 })
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `handleResponse(responseText, request, href)`
|
### `handleResponse(responseText, request, href, options)`
|
||||||
|
|
||||||
This method takes the raw response, processes the URL, then calls `pjax.loadContent()` to actually load it into the DOM.
|
This method takes the raw response, processes the URL, then calls `pjax.loadContent()` to actually load it into the DOM.
|
||||||
|
|
||||||
@@ -181,60 +230,107 @@ It is passed the following arguments:
|
|||||||
* **responseText** (string): This is the raw response text. This is equivalent to `request.responseText`.
|
* **responseText** (string): This is the raw response text. This is equivalent to `request.responseText`.
|
||||||
* **request** (XMLHttpRequest): This is the XHR object.
|
* **request** (XMLHttpRequest): This is the XHR object.
|
||||||
* **href** (string): This is the URL that was passed to `loadUrl()`.
|
* **href** (string): This is the URL that was passed to `loadUrl()`.
|
||||||
|
* **options** (object): This is an object with the options for this request. The structure basically matches the regular options object, with a few extra internal properties.
|
||||||
|
|
||||||
You can override this if you want to process the data before, or instead of, it being loaded into the DOM.
|
You can override this if you want to process the data before, or instead of, it being loaded into the DOM.
|
||||||
|
|
||||||
For example, if you want to check for a JSON response, you could do the following:
|
For example, if you want to check for a non-HTML response, you could do the following:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var pjax = new Pjax();
|
var pjax = new Pjax();
|
||||||
|
|
||||||
pjax._handleResponse = pjax.handleResponse;
|
pjax._handleResponse = pjax.handleResponse;
|
||||||
|
|
||||||
pjax.handleResponse = function(responseText, request, href) {
|
pjax.handleResponse = function(responseText, request, href, options) {
|
||||||
if (request.responseText.match("<html")) {
|
if (request.responseText.match("<html")) {
|
||||||
pjax._handleResponse(responseText, request, href);
|
pjax._handleResponse(responseText, request, href, options);
|
||||||
} else {
|
} else {
|
||||||
// handle response here
|
// handle non-HTML response here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options
|
### `refresh([el])`
|
||||||
|
|
||||||
##### `elements` (String, default: `"a[href], form[action]"`)
|
Use this method to bind Pjax to children of a DOM element that didn't exist when Pjax was initialised e.g. content inserted dynamically by another library or script. If called with no arguments, Pjax will parse the entire document again to look for newly-inserted elements.
|
||||||
|
|
||||||
CSS selector to use to retrieve links to apply Pjax to.
|
```js
|
||||||
|
// Inside a callback or Promise that runs after new DOM content has been inserted
|
||||||
|
var newContent = document.querySelector(".new-content");
|
||||||
|
|
||||||
##### `selectors` (Array, default: `["title", ".js-Pjax"]`)
|
pjax.refresh(newContent);
|
||||||
|
```
|
||||||
|
|
||||||
CSS selectors to replace. If a query returns multiples items, it will just keep the index.
|
|
||||||
|
### `reload()`
|
||||||
|
|
||||||
|
A helper shortcut for `window.location.reload()`. Used to force a page reload.
|
||||||
|
|
||||||
|
```js
|
||||||
|
pjax.reload()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### `elements` (String, default: `"a[href], form[action]"`)
|
||||||
|
|
||||||
|
CSS selector(s) used to find links to apply Pjax to. If needing multiple specific selectors, separate them by a comma.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Single element
|
||||||
|
var pjax = new Pjax({
|
||||||
|
elements: ".ajax"
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Multiple elements
|
||||||
|
var pjax = new Pjax({
|
||||||
|
elements: ".pjax, .ajax",
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### `selectors` (Array, default: `["title", ".js-Pjax"]`)
|
||||||
|
|
||||||
|
CSS selectors used to find which content to replace.
|
||||||
|
|
||||||
|
```js
|
||||||
|
var pjax = new Pjax({
|
||||||
|
selectors: [
|
||||||
|
"title",
|
||||||
|
"the-content",
|
||||||
|
]
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
If a query returns multiples items, it will just keep the index.
|
||||||
|
|
||||||
Example of what you can do:
|
Example of what you can do:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Page title</title>
|
<title>Page title</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="js-Pjax"></header>
|
<header class="js-Pjax">...</header>
|
||||||
<section class="js-Pjax">...</section>
|
<section class="js-Pjax">...</section>
|
||||||
<footer class="my-Footer"></footer>
|
<footer class="the-footer">...</footer>
|
||||||
<script>...</script>
|
<script>...</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
```
|
```
|
||||||
|
|
||||||
This example is correct and should work "as expected".
|
This example is correct and should work "as expected".
|
||||||
_If the current page and new page do not have the same amount of DOM elements,
|
|
||||||
Pjax will fall back to normal page load._
|
|
||||||
|
|
||||||
##### `switches` (Object, default: `{}`)
|
**NOTE:** _If the current page and new page do not have the same amount of DOM elements, Pjax will fall back to normal page load._
|
||||||
|
|
||||||
Objects containing callbacks that can be used to switch old elements with new elements.
|
### `switches` (Object, default: `{}`)
|
||||||
Keys should be one of the defined selectors.
|
|
||||||
|
This is an object containing callbacks that can be used to switch old elements with new elements.
|
||||||
|
|
||||||
|
The object keys should be one of the defined selectors (from the `selectors` option).
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
@@ -242,48 +338,49 @@ Examples:
|
|||||||
var pjax = new Pjax({
|
var pjax = new Pjax({
|
||||||
selectors: ["title", ".Navbar", ".js-Pjax"],
|
selectors: ["title", ".Navbar", ".js-Pjax"],
|
||||||
switches: {
|
switches: {
|
||||||
// "title": Pjax.switches.outerHTML // default behavior
|
"title": Pjax.switches.outerHTML, // default behavior
|
||||||
".Navbar": function(oldEl, newEl, options) {
|
".the-content": function(oldEl, newEl, options) {
|
||||||
// here it's a stupid example since it's the default behavior too
|
// this is identical to the default behavior
|
||||||
oldEl.outerHTML = newEl.outerHTML
|
oldEl.outerHTML = newEl.outerHTML
|
||||||
this.onSwitch()
|
this.onSwitch()
|
||||||
},
|
},
|
||||||
|
|
||||||
".js-Pjax": Pjax.switches.sideBySide
|
".js-Pjax": Pjax.switches.sideBySide
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
Callbacks are bound to Pjax instance itself to allow you to reuse it (ex: `this.onSwitch()`)
|
Callbacks are bound to the Pjax instance itself to allow you to reuse it (ex: `this.onSwitch()`)
|
||||||
|
|
||||||
###### Existing switches callback
|
### Existing Switch Callbacks
|
||||||
|
|
||||||
- `Pjax.switches.outerHTML`: default behavior, replace elements using outerHTML
|
- `Pjax.switches.outerHTML`:
|
||||||
- `Pjax.switches.innerHTML`: replace elements using innerHTML and copy className too
|
The default behavior, replaces elements using `outerHTML`.
|
||||||
- `Pjax.switches.replaceNode`: replace elements using replaceChild
|
- `Pjax.switches.innerHTML`:
|
||||||
- `Pjax.switches.sideBySide`: smart replacement that allows you to have both elements in the same parent when you want to use CSS animations. Old elements are removed when all children have been fully animated ([animationEnd](http://www.w3.org/TR/css3-animations/#animationend) event triggered)
|
Replaces elements using `innerHTML` and copies `className`.
|
||||||
|
- `Pjax.switches.replaceNode`:
|
||||||
|
Replaces elements using `replaceChild`
|
||||||
|
- `Pjax.switches.sideBySide`:
|
||||||
|
Smart replacing that allows you to have both elements in the same parent when you want to use CSS animations. Old elements are removed when all children have been fully animated (an [animationEnd](http://www.w3.org/TR/css3-animations/#animationend) event is triggered).
|
||||||
|
|
||||||
###### Create a switch callback
|
### Creating a Switch Callback
|
||||||
|
|
||||||
Your function can do whatever you want, but you need to:
|
Your callback function can do whatever you want, but you need to:
|
||||||
|
|
||||||
- replace `oldEl`'s content with `newEl`'s content in some fashion
|
1. Replace the `oldEl`'s content with the `newEl`'s content in some fashion.
|
||||||
- call `this.onSwitch()` to trigger the attached callback.
|
2. Call `this.onSwitch()` to trigger the attached callback.
|
||||||
|
|
||||||
Here is the default behavior as an example:
|
Here is the default behavior as an example:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
function(oldEl, newEl, pjaxRequestOptions, switchesClasses) {
|
function(oldEl, newEl, pjaxOptions) {
|
||||||
oldEl.outerHTML = newEl.outerHTML
|
oldEl.outerHTML = newEl.outerHTML
|
||||||
this.onSwitch()
|
this.onSwitch()
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
##### `switchesOptions` (Object, default: `{}`)
|
### `switchesOptions` (Object, default: `{}`)
|
||||||
|
|
||||||
These are options that can be used during switch by switchers (for now, only `Pjax.switches.sideBySide` uses it).
|
These are options that can be used during content replacement by switches. For now, only `Pjax.switches.sideBySide` uses it. This is very convenient when you use something like [Animate.css](https://github.com/daneden/animate.css) with or without [WOW.js](https://github.com/matthieua/WOW).
|
||||||
This is very convenient when you use something like [Animate.css](https://github.com/daneden/animate.css)
|
|
||||||
with or without [WOW.js](https://github.com/matthieua/WOW).
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var pjax = new Pjax({
|
var pjax = new Pjax({
|
||||||
@@ -294,18 +391,18 @@ var pjax = new Pjax({
|
|||||||
switchesOptions: {
|
switchesOptions: {
|
||||||
".js-Pjax": {
|
".js-Pjax": {
|
||||||
classNames: {
|
classNames: {
|
||||||
// class added on the element that will be removed
|
// class added to the old element being replaced, e.g. a fade out
|
||||||
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
|
// class added to the new element that is replacing the old one, e.g. a fade in
|
||||||
add: "Animated",
|
add: "Animated",
|
||||||
// class added on the element when it go backward
|
// class added on the element when navigating back
|
||||||
backward: "Animate--slideInRight",
|
backward: "Animate--slideInRight",
|
||||||
// class added on the element when it go forward (used for new page too)
|
// class added on the element when navigating forward (used for new page too)
|
||||||
forward: "Animate--slideInLeft"
|
forward: "Animate--slideInLeft"
|
||||||
},
|
},
|
||||||
callbacks: {
|
callbacks: {
|
||||||
// to make a nice transition with 2 pages as the same time
|
// to make a nice transition with 2 pages at the same time
|
||||||
// we are playing with absolute positioning for element we are removing
|
// we are playing with absolute positioning for the element we are removing
|
||||||
// & we need live metrics to have something great
|
// & we need live metrics to have something great
|
||||||
// see associated CSS below
|
// see associated CSS below
|
||||||
removeElement: function(el) {
|
removeElement: function(el) {
|
||||||
@@ -316,16 +413,15 @@ var pjax = new Pjax({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
_Note that remove class include `Animated--reverse` which is a simple way to not have
|
|
||||||
to create duplicate transition for (slideIn + reverse => slideOut)._
|
|
||||||
|
|
||||||
The following CSS will be required to make something nice:
|
_Note that `remove` includes `Animated--reverse` which is a simple way to not have to have a duplicate transition (slideIn + reverse => slideOut)._
|
||||||
|
|
||||||
|
Here is some css that works well with the above configuration:
|
||||||
|
|
||||||
```css
|
```css
|
||||||
/*
|
/*
|
||||||
if your content elements doesn't have a fixed width,
|
Note: If your content elements don't have a fixed width it can cause
|
||||||
you can get issue when absolute positioning will be used
|
an issue when positioning absolutely
|
||||||
so you will need that rules
|
|
||||||
*/
|
*/
|
||||||
.js-Pjax { position: relative } /* parent element where switch will be made */
|
.js-Pjax { position: relative } /* parent element where switch will be made */
|
||||||
|
|
||||||
@@ -353,6 +449,7 @@ The following CSS will be required to make something nice:
|
|||||||
.Animate--noDelay { animation-delay: 0s !important; }
|
.Animate--noDelay { animation-delay: 0s !important; }
|
||||||
|
|
||||||
.Animate--slideInRight { animation-name: Animation-slideInRight }
|
.Animate--slideInRight { animation-name: Animation-slideInRight }
|
||||||
|
|
||||||
@keyframes Animation-slideInRight {
|
@keyframes Animation-slideInRight {
|
||||||
0% {
|
0% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@@ -365,6 +462,7 @@ The following CSS will be required to make something nice:
|
|||||||
}
|
}
|
||||||
|
|
||||||
.Animate--slideInLeft { animation-name: Animation-slideInLeft }
|
.Animate--slideInLeft { animation-name: Animation-slideInLeft }
|
||||||
|
|
||||||
@keyframes Animation-slideInLeft {
|
@keyframes Animation-slideInLeft {
|
||||||
0% {
|
0% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@@ -383,7 +481,7 @@ To give context to this CSS, here is an HTML snippet:
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Page title</title>
|
<title>Page Title</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<section class="js-Pjax">
|
<section class="js-Pjax">
|
||||||
@@ -391,13 +489,15 @@ To give context to this CSS, here is an HTML snippet:
|
|||||||
Your content here
|
Your content here
|
||||||
</div>
|
</div>
|
||||||
<!--
|
<!--
|
||||||
when switching will be made you will have the following tree
|
During the replacement process, you'll have the following tree:
|
||||||
|
|
||||||
<div class="js-Pjax-child js-Pjax-remove Animate...">
|
<div class="js-Pjax-child js-Pjax-remove Animate...">
|
||||||
Your OLD content here
|
Your OLD content here
|
||||||
</div>
|
</div>
|
||||||
<div class="js-Pjax-child js-Pjax-add Animate...">
|
<div class="js-Pjax-child js-Pjax-add Animate...">
|
||||||
Your NEW content here
|
Your NEW content here
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
-->
|
-->
|
||||||
</section>
|
</section>
|
||||||
<script>...</script>
|
<script>...</script>
|
||||||
@@ -405,22 +505,20 @@ To give context to this CSS, here is an HTML snippet:
|
|||||||
</html>
|
</html>
|
||||||
```
|
```
|
||||||
|
|
||||||
##### `history` (Boolean, default: `true`)
|
### `history` (Boolean, default: `true`)
|
||||||
|
|
||||||
Enable the use of `pushState()`. Disabling this will prevent Pjax from updating browser history.
|
Enable the use of `pushState()`. Disabling this will prevent Pjax from updating browser history. While possible, there is almost no use case where you would want to do this.
|
||||||
However, there is almost no use case where you would want to do that.
|
|
||||||
|
|
||||||
Internally, this option is used when a `popstate` event triggers Pjax (to not `pushState()` again).
|
Internally, this option is used when a `popstate` event triggers Pjax (to not `pushState()` again).
|
||||||
|
|
||||||
##### `analytics` (Function | Boolean, default: a function that pushes `_gaq` `_trackPageview` or sends `ga` `pageview`
|
### `analytics` (Function | Boolean, default: a function that pushes `_gaq` `_trackPageview` or sends `ga` `pageview`
|
||||||
|
|
||||||
Function that allows you to add behavior for analytics. By default it tries to track
|
Function that allows you to add behavior for analytics. By default it tries to track
|
||||||
a pageview with Google Analytics (if it exists on the page).
|
a pageview with Google Analytics (if it exists on the page). It's called every time a page is switched, even for history navigation.
|
||||||
It's called every time a page is switched, even for history navigation.
|
|
||||||
|
|
||||||
Set to `false` to disable this behavior.
|
Set to `false` to disable this behavior.
|
||||||
|
|
||||||
##### `scrollTo` (Integer | \[Integer, Integer\] | False, default: `0`)
|
### `scrollTo` (Integer | \[Integer, Integer\] | False, default: `0`)
|
||||||
|
|
||||||
When set to an integer, this is the value (in px from the top of the page) to scroll to when a page is switched.
|
When set to an integer, this is the value (in px from the top of the page) to scroll to when a page is switched.
|
||||||
|
|
||||||
@@ -428,30 +526,25 @@ When set to an array of 2 integers (\[x, y\]), this is the value to scroll both
|
|||||||
|
|
||||||
Set this to `false` to disable scrolling, which will mean the page will stay in that same position it was before loading the new elements.
|
Set this to `false` to disable scrolling, which will mean the page will stay in that same position it was before loading the new elements.
|
||||||
|
|
||||||
##### `scrollRestoration` (Boolean, default: `true`)
|
### `scrollRestoration` (Boolean, default: `true`)
|
||||||
|
|
||||||
When set to true, attempt to restore the scroll position when navigating backwards or forwards.
|
When set to `true`, Pjax will attempt to restore the scroll position when navigating backwards or forwards.
|
||||||
|
|
||||||
##### `cacheBust` (Boolean, default: `true`)
|
### `cacheBust` (Boolean, default: `true`)
|
||||||
|
|
||||||
When set to true, append a timestamp query string segment to the requested URLs
|
When set to `true`, Pjax appends a timestamp query string segment to the requested URL in order to skip the browser cache.
|
||||||
in order to skip browser cache.
|
|
||||||
|
|
||||||
##### `debug` (Boolean, default: `false`)
|
### `debug` (Boolean, default: `false`)
|
||||||
|
|
||||||
Enables verbose mode. Useful to debug page layout differences.
|
Enables verbose mode. Useful to debug page layout differences.
|
||||||
|
|
||||||
##### `currentUrlFullReload` (Boolean, default: `false`)
|
### `currentUrlFullReload` (Boolean, default: `false`)
|
||||||
|
|
||||||
When set to true, clicking on a link that points to the current URL will trigger a full page reload.
|
When set to `true`, clicking on a link that points to the current URL will trigger a full page reload.
|
||||||
|
|
||||||
When set to `false`, clicking on such a link will cause Pjax to load the
|
When set to `false`, clicking on such a link will cause Pjax to load the current page without a full page reload. If you want to add some custom behavior, add a click listener to the link and call `preventDefault()`. This will prevent Pjax from receiving the event.
|
||||||
current page like any page.
|
|
||||||
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.
|
|
||||||
|
|
||||||
Note: this must be done before Pjax is instantiated. Otherwise, Pjax's
|
**Note**: This must be done before Pjax is instantiated, otherwise Pjax's event handler will be called first, and `preventDefault()` won't have been called yet.
|
||||||
event handler will be called first, and preventDefault() won't be called yet.
|
|
||||||
|
|
||||||
Here is some sample code:
|
Here is some sample code:
|
||||||
|
|
||||||
@@ -464,6 +557,7 @@ Here is some sample code:
|
|||||||
el.addEventListener("click", function(e) {
|
el.addEventListener("click", function(e) {
|
||||||
if (el.href === window.location.href.split("#")[0]) {
|
if (el.href === window.location.href.split("#")[0]) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
console.log("Link to current page clicked");
|
console.log("Link to current page clicked");
|
||||||
// Custom code goes here.
|
// Custom code goes here.
|
||||||
}
|
}
|
||||||
@@ -473,15 +567,13 @@ Here is some sample code:
|
|||||||
var pjax = new Pjax()
|
var pjax = new Pjax()
|
||||||
```
|
```
|
||||||
|
|
||||||
(Note that if `cacheBust` is set to true, the code that checks if the href
|
(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).
|
||||||
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`)
|
### `timeout` (Integer, default: `0`)
|
||||||
|
|
||||||
The timeout in milliseconds for the XHR requests. Set to 0 to disable the timeout.
|
The timeout in _milliseconds_ for the XHR requests. Set to `0` to disable the timeout.
|
||||||
|
|
||||||
### Events
|
## Events
|
||||||
|
|
||||||
Pjax fires a number of events regardless of how it's invoked.
|
Pjax fires a number of events regardless of how it's invoked.
|
||||||
|
|
||||||
@@ -499,41 +591,32 @@ document.addEventListener('pjax:send', topbar.show)
|
|||||||
document.addEventListener('pjax:complete', topbar.hide)
|
document.addEventListener('pjax:complete', topbar.hide)
|
||||||
```
|
```
|
||||||
|
|
||||||
### HTTP Headers
|
## HTTP Headers
|
||||||
|
|
||||||
Pjax uses several custom headers when it makes and receives HTTP requests.
|
Pjax uses several custom headers when it makes and receives HTTP requests. If the requests are going to your server, you can use those headers for some meta information about the response.
|
||||||
If the requests are going to your server, you can use those headers for
|
|
||||||
some meta information about the response.
|
|
||||||
|
|
||||||
##### Request headers
|
### Request Headers
|
||||||
|
|
||||||
Pjax sends the following headers with every request:
|
Pjax sends the following headers with every request:
|
||||||
|
|
||||||
* `X-Requested-With: "XMLHttpRequest"`
|
- `X-Requested-With: "XMLHttpRequest"`
|
||||||
* `X-PJAX: "true"`
|
- `X-PJAX: "true"`
|
||||||
* `X-PJAX-Selectors`: A serialized JSON array of selectors, taken from
|
- `X-PJAX-Selectors`:
|
||||||
`options.selectors`. You can use this to send back only the elements that
|
A serialized JSON array of selectors, taken from `options.selectors`. You can use this to send back only the elements that Pjax will use to switch, instead of sending the whole page. Note that you'll need to deserialize this on the server (Such as by using `JSON.parse()`)
|
||||||
Pjax will use to switch, instead of sending the whole page. Use `JSON.parse()`
|
|
||||||
server-side to deserialize it back to an array.
|
|
||||||
|
|
||||||
##### Response headers
|
### Response Headers
|
||||||
|
|
||||||
Pjax looks for the following headers on the response:
|
Pjax looks for the following headers on the response:
|
||||||
|
|
||||||
* `X-PJAX-URL` or `X-XHR-Redirected-To`
|
- `X-PJAX-URL` or `X-XHR-Redirected-To`
|
||||||
|
|
||||||
Pjax first checks the `responseURL` property on the XHR object to
|
Pjax first checks the `responseURL` property on the XHR object to see if the request was redirected by the server. Most browsers support this, but not all. To ensure Pjax will be able to tell when the request is redirected, you can include one of these headers with the response, set to the final URL.
|
||||||
check if the request was redirected by the server. Most browsers support
|
|
||||||
this, but not all. To ensure Pjax will be able to tell when the request
|
|
||||||
is redirected, you can include one of these headers with the response,
|
|
||||||
set to the final URL.
|
|
||||||
|
|
||||||
|
## DOM Ready State
|
||||||
#### Note about DOM ready state
|
|
||||||
|
|
||||||
Most of the time, you will have code related to the current DOM that you only execute when the DOM is ready.
|
Most of the time, you will have code related to the current DOM that you only execute when the DOM is ready.
|
||||||
|
|
||||||
Since Pjax doesn't magically re-execute your previous code each time you load a page, you need to add some simple code to achieve this:
|
Since Pjax doesn't automatically re-execute your previous code each time you load a page, you'll need to add code to re-trigger the DOM ready code. Here's a simple example:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
function whenDOMReady() {
|
function whenDOMReady() {
|
||||||
@@ -549,20 +632,16 @@ document.addEventListener("pjax:success", whenDOMReady)
|
|||||||
|
|
||||||
_Note: Don't create the Pjax instance in the `whenDOMReady` function._
|
_Note: Don't create the Pjax instance in the `whenDOMReady` function._
|
||||||
|
|
||||||
For my concern and usage, I `js-Pjax`-ify all body children, including stuff like navigation and footer (to get navigation state easily updated).
|
If you want to just update a specific part (which is a good idea), you can add the DOM-related code in a function and re-execute this function when the `pjax:success` event is fired.
|
||||||
|
|
||||||
The attached behavior is re-executed each time a page is loaded, like in the snippet above.
|
|
||||||
|
|
||||||
If you want to just update a specific part (it's totally a good idea), you can just
|
|
||||||
add the DOM-related code in a function and re-execute this function when "pjax:success" event is fired.
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// do your global stuff
|
// do your global stuff
|
||||||
//... DOM ready blah blah
|
//... DOM ready code
|
||||||
|
|
||||||
function whenContainerReady() {
|
function whenContainerReady() {
|
||||||
// do your container related stuff
|
// do your container related stuff
|
||||||
}
|
}
|
||||||
|
|
||||||
whenContainerReady()
|
whenContainerReady()
|
||||||
|
|
||||||
var pjax = new Pjax()
|
var pjax = new Pjax()
|
||||||
@@ -570,19 +649,17 @@ var pjax = new Pjax()
|
|||||||
document.addEventListener("pjax:success", whenContainerReady)
|
document.addEventListener("pjax:success", whenContainerReady)
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
### Q: Disqus doesn't work anymore, how can I fix that ?
|
### Q: Disqus doesn't work anymore, how can I fix that ?
|
||||||
|
|
||||||
A: There is a few things you need to do:
|
#### A: There are a few things you need to do:
|
||||||
- wrap your Disqus snippet into a DOM element that you will add to the `selector`
|
|
||||||
property (or just wrap it with `class="js-Pjax"`) and be sure to have at least the empty
|
|
||||||
wrapper on each page (to avoid differences of DOM between pages)
|
|
||||||
- edit your Disqus snippet like the one below
|
|
||||||
|
|
||||||
#### Disqus snippet before Pjax integration
|
- Wrap your Disqus snippet into a DOM element that you will add to the `selector` property (or just wrap it with `class="js-Pjax"`) and be sure to have at least an empty wrapper on each page (to avoid differences of DOM between pages).
|
||||||
|
|
||||||
|
- Edit your Disqus snippet like the one below.
|
||||||
|
|
||||||
|
#### Disqus snippet _before_ Pjax integration
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script>
|
<script>
|
||||||
@@ -590,6 +667,7 @@ wrapper on each page (to avoid differences of DOM between pages)
|
|||||||
var disqus_identifier = 'PAGEID'
|
var disqus_identifier = 'PAGEID'
|
||||||
var disqus_url = 'PAGEURL'
|
var disqus_url = 'PAGEURL'
|
||||||
var disqus_script = 'embed.js'
|
var disqus_script = 'embed.js'
|
||||||
|
|
||||||
(function(d,s) {
|
(function(d,s) {
|
||||||
s = d.createElement('script');s.async=1;s.src = '//' + disqus_shortname + '.disqus.com/'+disqus_script;
|
s = d.createElement('script');s.async=1;s.src = '//' + disqus_shortname + '.disqus.com/'+disqus_script;
|
||||||
(d.getElementsByTagName('head')[0]).appendChild(s);
|
(d.getElementsByTagName('head')[0]).appendChild(s);
|
||||||
@@ -597,11 +675,11 @@ wrapper on each page (to avoid differences of DOM between pages)
|
|||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Disqus snippet after Pjax integration
|
#### Disqus snippet _after_ Pjax integration
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div class="js-Pjax"><!-- needs to be here on every Pjax-ified page, even if empty -->
|
<div class="js-Pjax"><!-- needs to be here on every Pjax-ified page, even if empty -->
|
||||||
<!-- if (blah blah) { // eventual server-side test to know whether or not you include this script -->
|
<!-- if (some condition) { // eventual server-side test to know whether or not you include this script -->
|
||||||
<script>
|
<script>
|
||||||
var disqus_shortname = 'YOURSHORTNAME'
|
var disqus_shortname = 'YOURSHORTNAME'
|
||||||
var disqus_identifier = 'PAGEID'
|
var disqus_identifier = 'PAGEID'
|
||||||
@@ -615,8 +693,8 @@ wrapper on each page (to avoid differences of DOM between pages)
|
|||||||
(d.getElementsByTagName('head')[0]).appendChild(s);
|
(d.getElementsByTagName('head')[0]).appendChild(s);
|
||||||
})(document)
|
})(document)
|
||||||
}
|
}
|
||||||
// if disqus <script> already loaded, we just reset disqus the right way
|
// if disqus <script> is already loaded, we just reset disqus the right way
|
||||||
// see http://help.disqus.com/customer/portal/articles/472107-using-disqus-on-ajax-sites
|
// see https://help.disqus.com/developer/using-disqus-on-ajax-sites
|
||||||
else {
|
else {
|
||||||
DISQUS.reset({
|
DISQUS.reset({
|
||||||
reload: true,
|
reload: true,
|
||||||
@@ -633,19 +711,15 @@ wrapper on each page (to avoid differences of DOM between pages)
|
|||||||
|
|
||||||
**Note: Pjax only handles inline `<script>` blocks for the container you are switching.**
|
**Note: Pjax only handles inline `<script>` blocks for the container you are switching.**
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
Clone this repository and run `npm run example`, which will open the example app in your browser.
|
Clone this repository and run `npm run example`, which will open the example app in your browser.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## CONTRIBUTING
|
## CONTRIBUTING
|
||||||
|
|
||||||
* ⇄ Pull requests and ★ Stars are always welcome.
|
- ⇄ Pull requests and ★ Stars are always welcome.
|
||||||
* For bugs and feature requests, please create an issue.
|
- For bugs and feature requests, please create an issue.
|
||||||
* Pull requests must be accompanied by passing automated tests (`npm test`). If the API is changed, please update the Typescript definitions as well (`pjax.d.ts`).
|
- Pull requests must be accompanied by passing automated tests (`npm test`). If the API is changed, please update the Typescript definitions as well (`pjax.d.ts`).
|
||||||
|
|
||||||
## [CHANGELOG](CHANGELOG.md)
|
## [CHANGELOG](CHANGELOG.md)
|
||||||
|
|
||||||
|
|||||||
@@ -1,51 +1,49 @@
|
|||||||
/* global Pjax */
|
/* global Pjax */
|
||||||
var pjax;
|
var pjax;
|
||||||
var initButtons = function() {
|
var initButtons = function() {
|
||||||
var buttons = document.querySelectorAll("button[data-manual-trigger]")
|
var buttons = document.querySelectorAll("button[data-manual-trigger]");
|
||||||
|
|
||||||
if (!buttons) {
|
if (!buttons) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// jshint -W083
|
// jshint -W083
|
||||||
for (var i = 0; i < buttons.length; i++) {
|
for (var i = 0; i < buttons.length; i++) {
|
||||||
buttons[i].addEventListener("click", function(e) {
|
buttons[i].addEventListener("click", function(e) {
|
||||||
var el = e.currentTarget
|
var el = e.currentTarget;
|
||||||
|
|
||||||
if (el.getAttribute("data-manual-trigger-override") === "true") {
|
if (el.getAttribute("data-manual-trigger-override") === "true") {
|
||||||
// Manually load URL with overridden Pjax instance options
|
// Manually load URL with overridden Pjax instance options
|
||||||
pjax.loadUrl("/example/page2.html", {cacheBust: false})
|
pjax.loadUrl("/example/page2.html", { cacheBust: false });
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// Manually load URL with current Pjax instance options
|
// Manually load URL with current Pjax instance options
|
||||||
pjax.loadUrl("/example/page2.html")
|
pjax.loadUrl("/example/page2.html");
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
// jshint +W083
|
// jshint +W083
|
||||||
}
|
};
|
||||||
|
|
||||||
console.log("Document initialized:", window.location.href)
|
console.log("Document initialized:", window.location.href);
|
||||||
|
|
||||||
document.addEventListener("pjax:send", function() {
|
document.addEventListener("pjax:send", function() {
|
||||||
console.log("Event: pjax:send", arguments)
|
console.log("Event: pjax:send", arguments);
|
||||||
})
|
});
|
||||||
|
|
||||||
document.addEventListener("pjax:complete", function() {
|
document.addEventListener("pjax:complete", function() {
|
||||||
console.log("Event: pjax:complete", arguments)
|
console.log("Event: pjax:complete", arguments);
|
||||||
})
|
});
|
||||||
|
|
||||||
document.addEventListener("pjax:error", function() {
|
document.addEventListener("pjax:error", function() {
|
||||||
console.log("Event: pjax:error", arguments)
|
console.log("Event: pjax:error", arguments);
|
||||||
})
|
});
|
||||||
|
|
||||||
document.addEventListener("pjax:success", function() {
|
document.addEventListener("pjax:success", function() {
|
||||||
console.log("Event: pjax:success", arguments)
|
console.log("Event: pjax:success", arguments);
|
||||||
|
|
||||||
// Init page content
|
// Init page content
|
||||||
initButtons()
|
initButtons();
|
||||||
})
|
});
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
// Init Pjax instance
|
// Init Pjax instance
|
||||||
@@ -53,9 +51,9 @@ document.addEventListener("DOMContentLoaded", function() {
|
|||||||
elements: [".js-Pjax"],
|
elements: [".js-Pjax"],
|
||||||
selectors: [".body", "title"],
|
selectors: [".body", "title"],
|
||||||
cacheBust: true
|
cacheBust: true
|
||||||
})
|
});
|
||||||
console.log("Pjax initialized.", pjax)
|
console.log("Pjax initialized.", pjax);
|
||||||
|
|
||||||
// Init page content
|
// Init page content
|
||||||
initButtons()
|
initButtons();
|
||||||
})
|
});
|
||||||
|
|||||||
277
index.js
277
index.js
@@ -1,163 +1,195 @@
|
|||||||
var executeScripts = require("./lib/execute-scripts.js")
|
var executeScripts = require("./lib/execute-scripts");
|
||||||
var forEachEls = require("./lib/foreach-els.js")
|
var forEachEls = require("./lib/foreach-els");
|
||||||
var parseOptions = require("./lib/parse-options.js")
|
var parseOptions = require("./lib/parse-options");
|
||||||
var switches = require("./lib/switches")
|
var switches = require("./lib/switches");
|
||||||
var newUid = require("./lib/uniqueid.js")
|
var newUid = require("./lib/uniqueid");
|
||||||
|
|
||||||
var on = require("./lib/events/on.js")
|
var on = require("./lib/events/on");
|
||||||
var trigger = require("./lib/events/trigger.js")
|
var trigger = require("./lib/events/trigger");
|
||||||
|
|
||||||
var clone = require("./lib/util/clone.js")
|
var clone = require("./lib/util/clone");
|
||||||
var contains = require("./lib/util/contains.js")
|
var contains = require("./lib/util/contains");
|
||||||
var extend = require("./lib/util/extend.js")
|
var extend = require("./lib/util/extend");
|
||||||
var noop = require("./lib/util/noop")
|
var noop = require("./lib/util/noop");
|
||||||
|
|
||||||
var Pjax = function(options) {
|
var Pjax = function(options) {
|
||||||
this.state = {
|
this.state = {
|
||||||
numPendingSwitches: 0,
|
numPendingSwitches: 0,
|
||||||
href: null,
|
href: null,
|
||||||
options: null
|
options: null
|
||||||
}
|
};
|
||||||
|
|
||||||
|
this.options = parseOptions(options);
|
||||||
this.options = parseOptions(options)
|
this.log("Pjax options", this.options);
|
||||||
this.log("Pjax options", this.options)
|
|
||||||
|
|
||||||
if (this.options.scrollRestoration && "scrollRestoration" in history) {
|
if (this.options.scrollRestoration && "scrollRestoration" in history) {
|
||||||
history.scrollRestoration = "manual"
|
history.scrollRestoration = "manual";
|
||||||
}
|
}
|
||||||
|
|
||||||
this.maxUid = this.lastUid = newUid()
|
this.maxUid = this.lastUid = newUid();
|
||||||
|
|
||||||
this.parseDOM(document)
|
this.parseDOM(document);
|
||||||
|
|
||||||
on(window, "popstate", function(st) {
|
on(
|
||||||
|
window,
|
||||||
|
"popstate",
|
||||||
|
function(st) {
|
||||||
if (st.state) {
|
if (st.state) {
|
||||||
var opt = clone(this.options)
|
var opt = clone(this.options);
|
||||||
opt.url = st.state.url
|
opt.url = st.state.url;
|
||||||
opt.title = st.state.title
|
opt.title = st.state.title;
|
||||||
opt.history = false
|
// Since state already exists, prevent it from being pushed again
|
||||||
opt.scrollPos = st.state.scrollPos
|
opt.history = false;
|
||||||
|
opt.scrollPos = st.state.scrollPos;
|
||||||
if (st.state.uid < this.lastUid) {
|
if (st.state.uid < this.lastUid) {
|
||||||
opt.backward = true
|
opt.backward = true;
|
||||||
|
} else {
|
||||||
|
opt.forward = true;
|
||||||
}
|
}
|
||||||
else {
|
this.lastUid = st.state.uid;
|
||||||
opt.forward = true
|
|
||||||
}
|
|
||||||
this.lastUid = st.state.uid
|
|
||||||
|
|
||||||
// @todo implement history cache here, based on uid
|
// @todo implement history cache here, based on uid
|
||||||
this.loadUrl(st.state.url, opt)
|
this.loadUrl(st.state.url, opt);
|
||||||
}
|
|
||||||
}.bind(this))
|
|
||||||
}
|
}
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
Pjax.switches = switches
|
Pjax.switches = switches;
|
||||||
|
|
||||||
Pjax.prototype = {
|
Pjax.prototype = {
|
||||||
log: require("./lib/proto/log.js"),
|
log: require("./lib/proto/log"),
|
||||||
|
|
||||||
getElements: function(el) {
|
getElements: function(el) {
|
||||||
return el.querySelectorAll(this.options.elements)
|
return el.querySelectorAll(this.options.elements);
|
||||||
},
|
},
|
||||||
|
|
||||||
parseDOM: function(el) {
|
parseDOM: function(el) {
|
||||||
var parseElement = require("./lib/proto/parse-element")
|
var parseElement = require("./lib/proto/parse-element");
|
||||||
forEachEls(this.getElements(el), parseElement, this)
|
forEachEls(this.getElements(el), parseElement, this);
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(el) {
|
refresh: function(el) {
|
||||||
this.parseDOM(el || document)
|
this.parseDOM(el || document);
|
||||||
},
|
},
|
||||||
|
|
||||||
reload: function() {
|
reload: function() {
|
||||||
window.location.reload()
|
window.location.reload();
|
||||||
},
|
},
|
||||||
|
|
||||||
attachLink: require("./lib/proto/attach-link.js"),
|
attachLink: require("./lib/proto/attach-link"),
|
||||||
|
|
||||||
attachForm: require("./lib/proto/attach-form.js"),
|
attachForm: require("./lib/proto/attach-form"),
|
||||||
|
|
||||||
forEachSelectors: function(cb, context, DOMcontext) {
|
forEachSelectors: function(cb, context, DOMcontext) {
|
||||||
return require("./lib/foreach-selectors.js").bind(this)(this.options.selectors, cb, context, DOMcontext)
|
return require("./lib/foreach-selectors").bind(this)(
|
||||||
|
this.options.selectors,
|
||||||
|
cb,
|
||||||
|
context,
|
||||||
|
DOMcontext
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
switchSelectors: function(selectors, fromEl, toEl, options) {
|
switchSelectors: function(selectors, fromEl, toEl, options) {
|
||||||
return require("./lib/switches-selectors.js").bind(this)(this.options.switches, this.options.switchesOptions, selectors, fromEl, toEl, options)
|
return require("./lib/switches-selectors").bind(this)(
|
||||||
|
this.options.switches,
|
||||||
|
this.options.switchesOptions,
|
||||||
|
selectors,
|
||||||
|
fromEl,
|
||||||
|
toEl,
|
||||||
|
options
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
latestChance: function(href) {
|
latestChance: function(href) {
|
||||||
window.location = href
|
window.location = href;
|
||||||
},
|
},
|
||||||
|
|
||||||
onSwitch: function() {
|
onSwitch: function() {
|
||||||
trigger(window, "resize scroll")
|
trigger(window, "resize scroll");
|
||||||
|
|
||||||
this.state.numPendingSwitches--
|
this.state.numPendingSwitches--;
|
||||||
|
|
||||||
// debounce calls, so we only run this once after all switches are finished.
|
// debounce calls, so we only run this once after all switches are finished.
|
||||||
if (this.state.numPendingSwitches === 0) {
|
if (this.state.numPendingSwitches === 0) {
|
||||||
this.afterAllSwitches()
|
this.afterAllSwitches();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
loadContent: function(html, options) {
|
loadContent: function(html, options) {
|
||||||
var tmpEl = document.implementation.createHTMLDocument("pjax")
|
if (typeof html !== "string") {
|
||||||
|
trigger(document, "pjax:complete pjax:error", options);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tmpEl = document.implementation.createHTMLDocument("pjax");
|
||||||
|
|
||||||
// parse HTML attributes to copy them
|
// parse HTML attributes to copy them
|
||||||
// since we are forced to use documentElement.innerHTML (outerHTML can't be used for <html>)
|
// since we are forced to use documentElement.innerHTML (outerHTML can't be used for <html>)
|
||||||
var htmlRegex = /<html[^>]+>/gi
|
var htmlRegex = /<html[^>]+>/gi;
|
||||||
var htmlAttribsRegex = /\s?[a-z:]+(?:\=(?:\'|\")[^\'\">]+(?:\'|\"))*/gi
|
var htmlAttribsRegex = /\s?[a-z:]+(?:=['"][^'">]+['"])*/gi;
|
||||||
var matches = html.match(htmlRegex)
|
var matches = html.match(htmlRegex);
|
||||||
if (matches && matches.length) {
|
if (matches && matches.length) {
|
||||||
matches = matches[0].match(htmlAttribsRegex)
|
matches = matches[0].match(htmlAttribsRegex);
|
||||||
if (matches.length) {
|
if (matches.length) {
|
||||||
matches.shift()
|
matches.shift();
|
||||||
matches.forEach(function(htmlAttrib) {
|
matches.forEach(function(htmlAttrib) {
|
||||||
var attr = htmlAttrib.trim().split("=")
|
var attr = htmlAttrib.trim().split("=");
|
||||||
if (attr.length === 1) {
|
if (attr.length === 1) {
|
||||||
tmpEl.documentElement.setAttribute(attr[0], true)
|
tmpEl.documentElement.setAttribute(attr[0], true);
|
||||||
|
} else {
|
||||||
|
tmpEl.documentElement.setAttribute(attr[0], attr[1].slice(1, -1));
|
||||||
}
|
}
|
||||||
else {
|
});
|
||||||
tmpEl.documentElement.setAttribute(attr[0], attr[1].slice(1, -1))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpEl.documentElement.innerHTML = html
|
tmpEl.documentElement.innerHTML = html;
|
||||||
this.log("load content", tmpEl.documentElement.attributes, tmpEl.documentElement.innerHTML.length)
|
this.log(
|
||||||
|
"load content",
|
||||||
|
tmpEl.documentElement.attributes,
|
||||||
|
tmpEl.documentElement.innerHTML.length
|
||||||
|
);
|
||||||
|
|
||||||
// Clear out any focused controls before inserting new page contents.
|
// Clear out any focused controls before inserting new page contents.
|
||||||
if (document.activeElement && contains(document, this.options.selectors, document.activeElement)) {
|
if (
|
||||||
|
document.activeElement &&
|
||||||
|
contains(document, this.options.selectors, document.activeElement)
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
document.activeElement.blur()
|
document.activeElement.blur();
|
||||||
} catch (e) { }
|
} catch (e) {} // eslint-disable-line no-empty
|
||||||
}
|
}
|
||||||
|
|
||||||
this.switchSelectors(this.options.selectors, tmpEl, document, options)
|
this.switchSelectors(this.options.selectors, tmpEl, document, options);
|
||||||
},
|
},
|
||||||
|
|
||||||
abortRequest: require("./lib/abort-request.js"),
|
abortRequest: require("./lib/abort-request"),
|
||||||
|
|
||||||
doRequest: require("./lib/send-request.js"),
|
doRequest: require("./lib/send-request"),
|
||||||
|
|
||||||
handleResponse: require("./lib/proto/handle-response.js"),
|
handleResponse: require("./lib/proto/handle-response"),
|
||||||
|
|
||||||
loadUrl: function(href, options) {
|
loadUrl: function(href, options) {
|
||||||
options = typeof options === "object" ?
|
options =
|
||||||
extend({}, this.options, options) :
|
typeof options === "object"
|
||||||
clone(this.options)
|
? extend({}, this.options, options)
|
||||||
|
: clone(this.options);
|
||||||
|
|
||||||
this.log("load href", href, options)
|
this.log("load href", href, options);
|
||||||
|
|
||||||
// Abort any previous request
|
// Abort any previous request
|
||||||
this.abortRequest(this.request)
|
this.abortRequest(this.request);
|
||||||
|
|
||||||
trigger(document, "pjax:send", options)
|
trigger(document, "pjax:send", options);
|
||||||
|
|
||||||
// Do the request
|
// Do the request
|
||||||
this.request = this.doRequest(href, options, this.handleResponse.bind(this))
|
this.request = this.doRequest(
|
||||||
|
href,
|
||||||
|
options,
|
||||||
|
this.handleResponse.bind(this)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
afterAllSwitches: function() {
|
afterAllSwitches: function() {
|
||||||
@@ -166,114 +198,121 @@ Pjax.prototype = {
|
|||||||
// the last field.
|
// the last field.
|
||||||
//
|
//
|
||||||
// http://www.w3.org/html/wg/drafts/html/master/forms.html
|
// http://www.w3.org/html/wg/drafts/html/master/forms.html
|
||||||
var autofocusEl = Array.prototype.slice.call(document.querySelectorAll("[autofocus]")).pop()
|
var autofocusEl = Array.prototype.slice
|
||||||
|
.call(document.querySelectorAll("[autofocus]"))
|
||||||
|
.pop();
|
||||||
if (autofocusEl && document.activeElement !== autofocusEl) {
|
if (autofocusEl && document.activeElement !== autofocusEl) {
|
||||||
autofocusEl.focus()
|
autofocusEl.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute scripts when DOM have been completely updated
|
// execute scripts when DOM have been completely updated
|
||||||
this.options.selectors.forEach(function(selector) {
|
this.options.selectors.forEach(function(selector) {
|
||||||
forEachEls(document.querySelectorAll(selector), function(el) {
|
forEachEls(document.querySelectorAll(selector), function(el) {
|
||||||
executeScripts(el)
|
executeScripts(el);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
var state = this.state
|
var state = this.state;
|
||||||
|
|
||||||
if (state.options.history) {
|
if (state.options.history) {
|
||||||
if (!window.history.state) {
|
if (!window.history.state) {
|
||||||
this.lastUid = this.maxUid = newUid()
|
this.lastUid = this.maxUid = newUid();
|
||||||
window.history.replaceState({
|
window.history.replaceState(
|
||||||
|
{
|
||||||
url: window.location.href,
|
url: window.location.href,
|
||||||
title: document.title,
|
title: document.title,
|
||||||
uid: this.maxUid,
|
uid: this.maxUid,
|
||||||
scrollPos: [0, 0]
|
scrollPos: [0, 0]
|
||||||
},
|
},
|
||||||
document.title)
|
document.title
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update browser history
|
// Update browser history
|
||||||
this.lastUid = this.maxUid = newUid()
|
this.lastUid = this.maxUid = newUid();
|
||||||
|
|
||||||
window.history.pushState({
|
window.history.pushState(
|
||||||
|
{
|
||||||
url: state.href,
|
url: state.href,
|
||||||
title: state.options.title,
|
title: state.options.title,
|
||||||
uid: this.maxUid,
|
uid: this.maxUid,
|
||||||
scrollPos: [0, 0]
|
scrollPos: [0, 0]
|
||||||
},
|
},
|
||||||
state.options.title,
|
state.options.title,
|
||||||
state.href)
|
state.href
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.forEachSelectors(function(el) {
|
this.forEachSelectors(function(el) {
|
||||||
this.parseDOM(el)
|
this.parseDOM(el);
|
||||||
}, this)
|
}, this);
|
||||||
|
|
||||||
// Fire Events
|
// Fire Events
|
||||||
trigger(document,"pjax:complete pjax:success", state.options)
|
trigger(document, "pjax:complete pjax:success", state.options);
|
||||||
|
|
||||||
if (typeof state.options.analytics === "function") {
|
if (typeof state.options.analytics === "function") {
|
||||||
state.options.analytics()
|
state.options.analytics();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.options.history) {
|
if (state.options.history) {
|
||||||
// First parse url and check for hash to override scroll
|
// First parse url and check for hash to override scroll
|
||||||
var a = document.createElement("a")
|
var a = document.createElement("a");
|
||||||
a.href = this.state.href
|
a.href = this.state.href;
|
||||||
if (a.hash) {
|
if (a.hash) {
|
||||||
var name = a.hash.slice(1)
|
var name = a.hash.slice(1);
|
||||||
name = decodeURIComponent(name)
|
name = decodeURIComponent(name);
|
||||||
|
|
||||||
var curtop = 0
|
var curtop = 0;
|
||||||
var target = document.getElementById(name) || document.getElementsByName(name)[0]
|
var target =
|
||||||
|
document.getElementById(name) || document.getElementsByName(name)[0];
|
||||||
if (target) {
|
if (target) {
|
||||||
// http://stackoverflow.com/questions/8111094/cross-browser-javascript-function-to-find-actual-position-of-an-element-in-page
|
// http://stackoverflow.com/questions/8111094/cross-browser-javascript-function-to-find-actual-position-of-an-element-in-page
|
||||||
if (target.offsetParent) {
|
if (target.offsetParent) {
|
||||||
do {
|
do {
|
||||||
curtop += target.offsetTop
|
curtop += target.offsetTop;
|
||||||
|
|
||||||
target = target.offsetParent
|
target = target.offsetParent;
|
||||||
} while (target)
|
} while (target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.scrollTo(0, curtop)
|
window.scrollTo(0, curtop);
|
||||||
}
|
} else if (state.options.scrollTo !== false) {
|
||||||
else if (state.options.scrollTo !== false) {
|
|
||||||
// Scroll page to top on new page load
|
// Scroll page to top on new page load
|
||||||
if (state.options.scrollTo.length > 1) {
|
if (state.options.scrollTo.length > 1) {
|
||||||
window.scrollTo(state.options.scrollTo[0], state.options.scrollTo[1])
|
window.scrollTo(state.options.scrollTo[0], state.options.scrollTo[1]);
|
||||||
}
|
} else {
|
||||||
else {
|
window.scrollTo(0, state.options.scrollTo);
|
||||||
window.scrollTo(0, state.options.scrollTo)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (state.options.scrollRestoration && state.options.scrollPos) {
|
||||||
else if (state.options.scrollRestoration && state.options.scrollPos) {
|
window.scrollTo(state.options.scrollPos[0], state.options.scrollPos[1]);
|
||||||
window.scrollTo(state.options.scrollPos[0], state.options.scrollPos[1])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
numPendingSwitches: 0,
|
numPendingSwitches: 0,
|
||||||
href: null,
|
href: null,
|
||||||
options: null
|
options: null
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
Pjax.isSupported = require("./lib/is-supported.js")
|
Pjax.isSupported = require("./lib/is-supported");
|
||||||
|
|
||||||
// arguably could do `if( require("./lib/is-supported.js")()) {` but that might be a little to simple
|
// arguably could do `if( require("./lib/is-supported")()) {` but that might be a little to simple
|
||||||
if (Pjax.isSupported()) {
|
if (Pjax.isSupported()) {
|
||||||
module.exports = Pjax
|
module.exports = Pjax;
|
||||||
}
|
}
|
||||||
// if there isn’t required browser functions, returning stupid api
|
// if there isn’t required browser functions, returning stupid api
|
||||||
else {
|
else {
|
||||||
var stupidPjax = noop
|
var stupidPjax = noop;
|
||||||
for (var key in Pjax.prototype) {
|
for (var key in Pjax.prototype) {
|
||||||
if (Pjax.prototype.hasOwnProperty(key) && typeof Pjax.prototype[key] === "function") {
|
if (
|
||||||
stupidPjax[key] = noop
|
Pjax.prototype.hasOwnProperty(key) &&
|
||||||
|
typeof Pjax.prototype[key] === "function"
|
||||||
|
) {
|
||||||
|
stupidPjax[key] = noop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = stupidPjax
|
module.exports = stupidPjax;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
var noop = require("./util/noop")
|
var noop = require("./util/noop");
|
||||||
|
|
||||||
module.exports = function(request) {
|
module.exports = function(request) {
|
||||||
if (request && request.readyState < 4) {
|
if (request && request.readyState < 4) {
|
||||||
request.onreadystatechange = noop
|
request.onreadystatechange = noop;
|
||||||
request.abort()
|
request.abort();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,41 +1,48 @@
|
|||||||
module.exports = function(el) {
|
module.exports = function(el) {
|
||||||
var code = (el.text || el.textContent || el.innerHTML || "")
|
var code = el.text || el.textContent || el.innerHTML || "";
|
||||||
var src = (el.src || "")
|
var src = el.src || "";
|
||||||
var parent = el.parentNode || document.querySelector("head") || document.documentElement
|
var parent =
|
||||||
var script = document.createElement("script")
|
el.parentNode || document.querySelector("head") || document.documentElement;
|
||||||
|
var script = document.createElement("script");
|
||||||
|
|
||||||
if (code.match("document.write")) {
|
if (code.match("document.write")) {
|
||||||
if (console && console.log) {
|
if (console && console.log) {
|
||||||
console.log("Script contains document.write. Can’t be executed correctly. Code skipped ", el)
|
console.log(
|
||||||
|
"Script contains document.write. Can’t be executed correctly. Code skipped ",
|
||||||
|
el
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
script.type = "text/javascript"
|
script.type = "text/javascript";
|
||||||
|
script.id = el.id;
|
||||||
|
|
||||||
/* istanbul ignore if */
|
/* istanbul ignore if */
|
||||||
if (src !== "") {
|
if (src !== "") {
|
||||||
script.src = src
|
script.src = src;
|
||||||
script.async = false // force synchronous loading of peripheral JS
|
script.async = false; // force synchronous loading of peripheral JS
|
||||||
}
|
}
|
||||||
|
|
||||||
if (code !== "") {
|
if (code !== "") {
|
||||||
try {
|
try {
|
||||||
script.appendChild(document.createTextNode(code))
|
script.appendChild(document.createTextNode(code));
|
||||||
}
|
} catch (e) {
|
||||||
catch (e) {
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
// old IEs have funky script nodes
|
// old IEs have funky script nodes
|
||||||
script.text = code
|
script.text = code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
parent.appendChild(script)
|
parent.appendChild(script);
|
||||||
// avoid pollution only in head or body tags
|
// avoid pollution only in head or body tags
|
||||||
if (parent instanceof HTMLHeadElement || parent instanceof HTMLBodyElement) {
|
if (
|
||||||
parent.removeChild(script)
|
(parent instanceof HTMLHeadElement || parent instanceof HTMLBodyElement) &&
|
||||||
|
parent.contains(script)
|
||||||
|
) {
|
||||||
|
parent.removeChild(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
var forEachEls = require("../foreach-els")
|
var forEachEls = require("../foreach-els");
|
||||||
|
|
||||||
module.exports = function(els, events, listener, useCapture) {
|
module.exports = function(els, events, listener, useCapture) {
|
||||||
events = (typeof events === "string" ? events.split(" ") : events)
|
events = typeof events === "string" ? events.split(" ") : events;
|
||||||
|
|
||||||
events.forEach(function(e) {
|
events.forEach(function(e) {
|
||||||
forEachEls(els, function(el) {
|
forEachEls(els, function(el) {
|
||||||
el.removeEventListener(e, listener, useCapture)
|
el.removeEventListener(e, listener, useCapture);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
var forEachEls = require("../foreach-els")
|
var forEachEls = require("../foreach-els");
|
||||||
|
|
||||||
module.exports = function(els, events, listener, useCapture) {
|
module.exports = function(els, events, listener, useCapture) {
|
||||||
events = (typeof events === "string" ? events.split(" ") : events)
|
events = typeof events === "string" ? events.split(" ") : events;
|
||||||
|
|
||||||
events.forEach(function(e) {
|
events.forEach(function(e) {
|
||||||
forEachEls(els, function(el) {
|
forEachEls(els, function(el) {
|
||||||
el.addEventListener(e, listener, useCapture)
|
el.addEventListener(e, listener, useCapture);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
var forEachEls = require("../foreach-els")
|
var forEachEls = require("../foreach-els");
|
||||||
|
|
||||||
module.exports = function(els, events, opts) {
|
module.exports = function(els, events, opts) {
|
||||||
events = (typeof events === "string" ? events.split(" ") : events)
|
events = typeof events === "string" ? events.split(" ") : events;
|
||||||
|
|
||||||
events.forEach(function(e) {
|
events.forEach(function(e) {
|
||||||
var event
|
var event;
|
||||||
event = document.createEvent("HTMLEvents")
|
event = document.createEvent("HTMLEvents");
|
||||||
event.initEvent(e, true, true)
|
event.initEvent(e, true, true);
|
||||||
event.eventName = e
|
event.eventName = e;
|
||||||
if (opts) {
|
if (opts) {
|
||||||
Object.keys(opts).forEach(function(key) {
|
Object.keys(opts).forEach(function(key) {
|
||||||
event[key] = opts[key]
|
event[key] = opts[key];
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachEls(els, function(el) {
|
forEachEls(els, function(el) {
|
||||||
var domFix = false
|
var domFix = false;
|
||||||
if (!el.parentNode && el !== document && el !== window) {
|
if (!el.parentNode && el !== document && el !== window) {
|
||||||
// THANK YOU IE (9/10/11)
|
// THANK YOU IE (9/10/11)
|
||||||
// dispatchEvent doesn't work if the element is not in the DOM
|
// dispatchEvent doesn't work if the element is not in the DOM
|
||||||
domFix = true
|
domFix = true;
|
||||||
document.body.appendChild(el)
|
document.body.appendChild(el);
|
||||||
}
|
}
|
||||||
el.dispatchEvent(event)
|
el.dispatchEvent(event);
|
||||||
if (domFix) {
|
if (domFix) {
|
||||||
el.parentNode.removeChild(el)
|
el.parentNode.removeChild(el);
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
var forEachEls = require("./foreach-els")
|
var forEachEls = require("./foreach-els");
|
||||||
var evalScript = require("./eval-script")
|
var evalScript = require("./eval-script");
|
||||||
// Finds and executes scripts (used for newly added elements)
|
// Finds and executes scripts (used for newly added elements)
|
||||||
// Needed since innerHTML does not run scripts
|
// Needed since innerHTML does not run scripts
|
||||||
module.exports = function(el) {
|
module.exports = function(el) {
|
||||||
if (el.tagName.toLowerCase() === "script") {
|
if (el.tagName.toLowerCase() === "script") {
|
||||||
evalScript(el)
|
evalScript(el);
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachEls(el.querySelectorAll("script"), function(script) {
|
forEachEls(el.querySelectorAll("script"), function(script) {
|
||||||
if (!script.type || script.type.toLowerCase() === "text/javascript") {
|
if (!script.type || script.type.toLowerCase() === "text/javascript") {
|
||||||
if (script.parentNode) {
|
if (script.parentNode) {
|
||||||
script.parentNode.removeChild(script)
|
script.parentNode.removeChild(script);
|
||||||
}
|
}
|
||||||
evalScript(script)
|
evalScript(script);
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
/* global HTMLCollection: true */
|
/* global HTMLCollection: true */
|
||||||
|
|
||||||
module.exports = function(els, fn, context) {
|
module.exports = function(els, fn, context) {
|
||||||
if (els instanceof HTMLCollection || els instanceof NodeList || els instanceof Array) {
|
if (
|
||||||
return Array.prototype.forEach.call(els, fn, context)
|
els instanceof HTMLCollection ||
|
||||||
|
els instanceof NodeList ||
|
||||||
|
els instanceof Array
|
||||||
|
) {
|
||||||
|
return Array.prototype.forEach.call(els, fn, context);
|
||||||
}
|
}
|
||||||
// assume simple DOM element
|
// assume simple DOM element
|
||||||
return fn.call(context, els)
|
return fn.call(context, els);
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
var forEachEls = require("./foreach-els")
|
var forEachEls = require("./foreach-els");
|
||||||
|
|
||||||
module.exports = function(selectors, cb, context, DOMcontext) {
|
module.exports = function(selectors, cb, context, DOMcontext) {
|
||||||
DOMcontext = DOMcontext || document
|
DOMcontext = DOMcontext || document;
|
||||||
selectors.forEach(function(selector) {
|
selectors.forEach(function(selector) {
|
||||||
forEachEls(DOMcontext.querySelectorAll(selector), cb, context)
|
forEachEls(DOMcontext.querySelectorAll(selector), cb, context);
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
module.exports = function() {
|
module.exports = function() {
|
||||||
// Borrowed wholesale from https://github.com/defunkt/jquery-pjax
|
// Borrowed wholesale from https://github.com/defunkt/jquery-pjax
|
||||||
return window.history &&
|
return (
|
||||||
|
window.history &&
|
||||||
window.history.pushState &&
|
window.history.pushState &&
|
||||||
window.history.replaceState &&
|
window.history.replaceState &&
|
||||||
// pushState isn’t reliable on iOS until 5.
|
// pushState isn’t reliable on iOS until 5.
|
||||||
!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/)
|
!navigator.userAgent.match(
|
||||||
}
|
/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,41 +1,53 @@
|
|||||||
/* global _gaq: true, ga: true */
|
/* global _gaq: true, ga: true */
|
||||||
|
|
||||||
var defaultSwitches = require("./switches")
|
var defaultSwitches = require("./switches");
|
||||||
|
|
||||||
module.exports = function(options) {
|
module.exports = function(options) {
|
||||||
options = options || {}
|
options = options || {};
|
||||||
options.elements = options.elements || "a[href], form[action]"
|
options.elements = options.elements || "a[href], form[action]";
|
||||||
options.selectors = options.selectors || ["title", ".js-Pjax"]
|
options.selectors = options.selectors || ["title", ".js-Pjax"];
|
||||||
options.switches = options.switches || {}
|
options.switches = options.switches || {};
|
||||||
options.switchesOptions = options.switchesOptions || {}
|
options.switchesOptions = options.switchesOptions || {};
|
||||||
options.history = options.history || true
|
options.history =
|
||||||
options.analytics = (typeof options.analytics === "function" || options.analytics === false) ? options.analytics : defaultAnalytics
|
typeof options.history === "undefined" ? true : options.history;
|
||||||
options.scrollTo = (typeof options.scrollTo === "undefined") ? 0 : options.scrollTo
|
options.analytics =
|
||||||
options.scrollRestoration = (typeof options.scrollRestoration !== "undefined") ? options.scrollRestoration : true
|
typeof options.analytics === "function" || options.analytics === false
|
||||||
options.cacheBust = (typeof options.cacheBust === "undefined") ? true : options.cacheBust
|
? options.analytics
|
||||||
options.debug = options.debug || false
|
: defaultAnalytics;
|
||||||
options.timeout = options.timeout || 0
|
options.scrollTo =
|
||||||
options.currentUrlFullReload = (typeof options.currentUrlFullReload === "undefined") ? false : options.currentUrlFullReload
|
typeof options.scrollTo === "undefined" ? 0 : options.scrollTo;
|
||||||
|
options.scrollRestoration =
|
||||||
|
typeof options.scrollRestoration !== "undefined"
|
||||||
|
? options.scrollRestoration
|
||||||
|
: true;
|
||||||
|
options.cacheBust =
|
||||||
|
typeof options.cacheBust === "undefined" ? true : options.cacheBust;
|
||||||
|
options.debug = options.debug || false;
|
||||||
|
options.timeout = options.timeout || 0;
|
||||||
|
options.currentUrlFullReload =
|
||||||
|
typeof options.currentUrlFullReload === "undefined"
|
||||||
|
? false
|
||||||
|
: options.currentUrlFullReload;
|
||||||
|
|
||||||
// We can’t replace body.outerHTML or head.outerHTML.
|
// We can’t replace body.outerHTML or head.outerHTML.
|
||||||
// It creates a bug where a new body or head are created in the DOM.
|
// It creates a bug where a new body or head are created in the DOM.
|
||||||
// If you set head.outerHTML, a new body tag is appended, so the DOM has 2 body nodes, and vice versa
|
// If you set head.outerHTML, a new body tag is appended, so the DOM has 2 body nodes, and vice versa
|
||||||
if (!options.switches.head) {
|
if (!options.switches.head) {
|
||||||
options.switches.head = defaultSwitches.switchElementsAlt
|
options.switches.head = defaultSwitches.switchElementsAlt;
|
||||||
}
|
}
|
||||||
if (!options.switches.body) {
|
if (!options.switches.body) {
|
||||||
options.switches.body = defaultSwitches.switchElementsAlt
|
options.switches.body = defaultSwitches.switchElementsAlt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return options
|
return options;
|
||||||
}
|
};
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
function defaultAnalytics() {
|
function defaultAnalytics() {
|
||||||
if (window._gaq) {
|
if (window._gaq) {
|
||||||
_gaq.push(["_trackPageview"])
|
_gaq.push(["_trackPageview"]);
|
||||||
}
|
}
|
||||||
if (window.ga) {
|
if (window.ga) {
|
||||||
ga("send", "pageview", {page: location.pathname, title: document.title})
|
ga("send", "pageview", { page: location.pathname, title: document.title });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,132 +1,139 @@
|
|||||||
var on = require("../events/on")
|
var on = require("../events/on");
|
||||||
var clone = require("../util/clone")
|
var clone = require("../util/clone");
|
||||||
|
|
||||||
var attrState = "data-pjax-state"
|
var attrState = "data-pjax-state";
|
||||||
|
|
||||||
var formAction = function(el, event) {
|
var formAction = function(el, event) {
|
||||||
if (isDefaultPrevented(event)) {
|
if (isDefaultPrevented(event)) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since loadUrl modifies options and we may add our own modifications below,
|
// Since loadUrl modifies options and we may add our own modifications below,
|
||||||
// clone it so the changes don't persist
|
// clone it so the changes don't persist
|
||||||
var options = clone(this.options)
|
var options = clone(this.options);
|
||||||
|
|
||||||
// Initialize requestOptions
|
// Initialize requestOptions
|
||||||
options.requestOptions = {
|
options.requestOptions = {
|
||||||
requestUrl: el.getAttribute("action") || window.location.href,
|
requestUrl: el.getAttribute("action") || window.location.href,
|
||||||
requestMethod: el.getAttribute("method") || "GET"
|
requestMethod: el.getAttribute("method") || "GET"
|
||||||
}
|
};
|
||||||
|
|
||||||
// create a testable virtual link of the form action
|
// create a testable virtual link of the form action
|
||||||
var virtLinkElement = document.createElement("a")
|
var virtLinkElement = document.createElement("a");
|
||||||
virtLinkElement.setAttribute("href", options.requestOptions.requestUrl)
|
virtLinkElement.setAttribute("href", options.requestOptions.requestUrl);
|
||||||
|
|
||||||
var attrValue = checkIfShouldAbort(virtLinkElement, options)
|
var attrValue = checkIfShouldAbort(virtLinkElement, options);
|
||||||
if (attrValue) {
|
if (attrValue) {
|
||||||
el.setAttribute(attrState, attrValue)
|
el.setAttribute(attrState, attrValue);
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
|
|
||||||
if (el.enctype === "multipart/form-data") {
|
if (el.enctype === "multipart/form-data") {
|
||||||
options.requestOptions.formData = new FormData(el)
|
options.requestOptions.formData = new FormData(el);
|
||||||
}
|
} else {
|
||||||
else {
|
options.requestOptions.requestParams = parseFormElements(el);
|
||||||
options.requestOptions.requestParams = parseFormElements(el)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
el.setAttribute(attrState, "submit")
|
el.setAttribute(attrState, "submit");
|
||||||
|
|
||||||
options.triggerElement = el
|
options.triggerElement = el;
|
||||||
this.loadUrl(virtLinkElement.href, options)
|
this.loadUrl(virtLinkElement.href, options);
|
||||||
}
|
};
|
||||||
|
|
||||||
function parseFormElements(el) {
|
function parseFormElements(el) {
|
||||||
var requestParams = []
|
var requestParams = [];
|
||||||
|
var formElements = el.elements;
|
||||||
|
|
||||||
for (var elementKey in el.elements) {
|
for (var i = 0; i < formElements.length; i++) {
|
||||||
if (Number.isNaN(Number(elementKey))) {
|
var element = formElements[i];
|
||||||
continue;
|
var tagName = element.tagName.toLowerCase();
|
||||||
}
|
|
||||||
|
|
||||||
var element = el.elements[elementKey]
|
|
||||||
var tagName = element.tagName.toLowerCase()
|
|
||||||
// jscs:disable disallowImplicitTypeConversion
|
// jscs:disable disallowImplicitTypeConversion
|
||||||
if (!!element.name && element.attributes !== undefined && tagName !== "button") {
|
if (
|
||||||
|
!!element.name &&
|
||||||
|
element.attributes !== undefined &&
|
||||||
|
tagName !== "button"
|
||||||
|
) {
|
||||||
// jscs:enable disallowImplicitTypeConversion
|
// jscs:enable disallowImplicitTypeConversion
|
||||||
var type = element.attributes.type
|
var type = element.attributes.type;
|
||||||
|
|
||||||
if ((!type || type.value !== "checkbox" && type.value !== "radio") || element.checked) {
|
if (
|
||||||
|
!type ||
|
||||||
|
(type.value !== "checkbox" && type.value !== "radio") ||
|
||||||
|
element.checked
|
||||||
|
) {
|
||||||
// Build array of values to submit
|
// Build array of values to submit
|
||||||
var values = []
|
var values = [];
|
||||||
|
|
||||||
if (tagName === "select") {
|
if (tagName === "select") {
|
||||||
var opt
|
var opt;
|
||||||
|
|
||||||
for (var i = 0; i < element.options.length; i++) {
|
for (var j = 0; j < element.options.length; j++) {
|
||||||
opt = element.options[i]
|
opt = element.options[j];
|
||||||
if (opt.selected) {
|
if (opt.selected && !opt.disabled) {
|
||||||
values.push(opt.value || opt.text)
|
values.push(opt.hasAttribute("value") ? opt.value : opt.text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
values.push(element.value);
|
||||||
values.push(element.value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var j = 0; j < values.length; j++) {
|
for (var k = 0; k < values.length; k++) {
|
||||||
requestParams.push({
|
requestParams.push({
|
||||||
name: encodeURIComponent(element.name),
|
name: encodeURIComponent(element.name),
|
||||||
value: encodeURIComponent(values[j])
|
value: encodeURIComponent(values[k])
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return requestParams
|
return requestParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkIfShouldAbort(virtLinkElement, options) {
|
function checkIfShouldAbort(virtLinkElement, options) {
|
||||||
// Ignore external links.
|
// Ignore external links.
|
||||||
if (virtLinkElement.protocol !== window.location.protocol || virtLinkElement.host !== window.location.host) {
|
if (
|
||||||
return "external"
|
virtLinkElement.protocol !== window.location.protocol ||
|
||||||
|
virtLinkElement.host !== window.location.host
|
||||||
|
) {
|
||||||
|
return "external";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore click if we are on an anchor on the same page
|
// Ignore click if we are on an anchor on the same page
|
||||||
if (virtLinkElement.hash && virtLinkElement.href.replace(virtLinkElement.hash, "") === window.location.href.replace(location.hash, "")) {
|
if (
|
||||||
return "anchor"
|
virtLinkElement.hash &&
|
||||||
|
virtLinkElement.href.replace(virtLinkElement.hash, "") ===
|
||||||
|
window.location.href.replace(location.hash, "")
|
||||||
|
) {
|
||||||
|
return "anchor";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore empty anchor "foo.html#"
|
// Ignore empty anchor "foo.html#"
|
||||||
if (virtLinkElement.href === window.location.href.split("#")[0] + "#") {
|
if (virtLinkElement.href === window.location.href.split("#")[0] + "#") {
|
||||||
return "anchor-empty"
|
return "anchor-empty";
|
||||||
}
|
}
|
||||||
|
|
||||||
// if declared as a full reload, just normally submit the form
|
// if declared as a full reload, just normally submit the form
|
||||||
if (options.currentUrlFullReload && virtLinkElement.href === window.location.href.split("#")[0]) {
|
if (
|
||||||
return "reload"
|
options.currentUrlFullReload &&
|
||||||
|
virtLinkElement.href === window.location.href.split("#")[0]
|
||||||
|
) {
|
||||||
|
return "reload";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var isDefaultPrevented = function(event) {
|
var isDefaultPrevented = function(event) {
|
||||||
return event.defaultPrevented || event.returnValue === false
|
return event.defaultPrevented || event.returnValue === false;
|
||||||
}
|
};
|
||||||
|
|
||||||
module.exports = function(el) {
|
module.exports = function(el) {
|
||||||
var that = this
|
var that = this;
|
||||||
|
|
||||||
el.setAttribute(attrState, "")
|
el.setAttribute(attrState, "");
|
||||||
|
|
||||||
on(el, "submit", function(event) {
|
on(el, "submit", function(event) {
|
||||||
formAction.call(that, el, event)
|
formAction.call(that, el, event);
|
||||||
})
|
});
|
||||||
|
};
|
||||||
on(el, "keyup", function(event) {
|
|
||||||
if (event.keyCode === 13) {
|
|
||||||
formAction.call(that, el, event)
|
|
||||||
}
|
|
||||||
}.bind(this))
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,82 +1,99 @@
|
|||||||
var on = require("../events/on")
|
var on = require("../events/on");
|
||||||
var clone = require("../util/clone")
|
var clone = require("../util/clone");
|
||||||
|
|
||||||
var attrState = "data-pjax-state"
|
var attrState = "data-pjax-state";
|
||||||
|
|
||||||
var linkAction = function(el, event) {
|
var linkAction = function(el, event) {
|
||||||
if (isDefaultPrevented(event)) {
|
if (isDefaultPrevented(event)) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since loadUrl modifies options and we may add our own modifications below,
|
// Since loadUrl modifies options and we may add our own modifications below,
|
||||||
// clone it so the changes don't persist
|
// clone it so the changes don't persist
|
||||||
var options = clone(this.options)
|
var options = clone(this.options);
|
||||||
|
|
||||||
var attrValue = checkIfShouldAbort(el, event)
|
var attrValue = checkIfShouldAbort(el, event);
|
||||||
if (attrValue) {
|
if (attrValue) {
|
||||||
el.setAttribute(attrState, attrValue)
|
el.setAttribute(attrState, attrValue);
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
|
|
||||||
// don’t do "nothing" if user try to reload the page by clicking the same link twice
|
// don’t do "nothing" if user try to reload the page by clicking the same link twice
|
||||||
if (
|
if (
|
||||||
this.options.currentUrlFullReload &&
|
this.options.currentUrlFullReload &&
|
||||||
el.href === window.location.href.split("#")[0]
|
el.href === window.location.href.split("#")[0]
|
||||||
) {
|
) {
|
||||||
el.setAttribute(attrState, "reload")
|
el.setAttribute(attrState, "reload");
|
||||||
this.reload()
|
this.reload();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
el.setAttribute(attrState, "load")
|
el.setAttribute(attrState, "load");
|
||||||
|
|
||||||
options.triggerElement = el
|
options.triggerElement = el;
|
||||||
this.loadUrl(el.href, options)
|
this.loadUrl(el.href, options);
|
||||||
}
|
};
|
||||||
|
|
||||||
function checkIfShouldAbort(el, event) {
|
function checkIfShouldAbort(el, event) {
|
||||||
// Don’t break browser special behavior on links (like page in new window)
|
// 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) {
|
if (
|
||||||
return "modifier"
|
event.which > 1 ||
|
||||||
|
event.metaKey ||
|
||||||
|
event.ctrlKey ||
|
||||||
|
event.shiftKey ||
|
||||||
|
event.altKey
|
||||||
|
) {
|
||||||
|
return "modifier";
|
||||||
}
|
}
|
||||||
|
|
||||||
// we do test on href now to prevent unexpected behavior if for some reason
|
// we do test on href now to prevent unexpected behavior if for some reason
|
||||||
// user have href that can be dynamically updated
|
// user have href that can be dynamically updated
|
||||||
|
|
||||||
// Ignore external links.
|
// Ignore external links.
|
||||||
if (el.protocol !== window.location.protocol || el.host !== window.location.host) {
|
if (
|
||||||
return "external"
|
el.protocol !== window.location.protocol ||
|
||||||
|
el.host !== window.location.host
|
||||||
|
) {
|
||||||
|
return "external";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore anchors on the same page (keep native behavior)
|
// Ignore anchors on the same page (keep native behavior)
|
||||||
if (el.hash && el.href.replace(el.hash, "") === window.location.href.replace(location.hash, "")) {
|
if (
|
||||||
return "anchor"
|
el.hash &&
|
||||||
|
el.href.replace(el.hash, "") ===
|
||||||
|
window.location.href.replace(location.hash, "")
|
||||||
|
) {
|
||||||
|
return "anchor";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore empty anchor "foo.html#"
|
// Ignore empty anchor "foo.html#"
|
||||||
if (el.href === window.location.href.split("#")[0] + "#") {
|
if (el.href === window.location.href.split("#")[0] + "#") {
|
||||||
return "anchor-empty"
|
return "anchor-empty";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var isDefaultPrevented = function(event) {
|
var isDefaultPrevented = function(event) {
|
||||||
return event.defaultPrevented || event.returnValue === false
|
return event.defaultPrevented || event.returnValue === false;
|
||||||
}
|
};
|
||||||
|
|
||||||
module.exports = function(el) {
|
module.exports = function(el) {
|
||||||
var that = this
|
var that = this;
|
||||||
|
|
||||||
el.setAttribute(attrState, "")
|
el.setAttribute(attrState, "");
|
||||||
|
|
||||||
on(el, "click", function(event) {
|
on(el, "click", function(event) {
|
||||||
linkAction.call(that, el, event)
|
linkAction.call(that, el, event);
|
||||||
})
|
});
|
||||||
|
|
||||||
on(el, "keyup", function(event) {
|
on(
|
||||||
|
el,
|
||||||
|
"keyup",
|
||||||
|
function(event) {
|
||||||
if (event.keyCode === 13) {
|
if (event.keyCode === 13) {
|
||||||
linkAction.call(that, el, event)
|
linkAction.call(that, el, event);
|
||||||
}
|
|
||||||
}.bind(this))
|
|
||||||
}
|
}
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,70 +1,71 @@
|
|||||||
var clone = require("../util/clone.js")
|
var clone = require("../util/clone");
|
||||||
var newUid = require("../uniqueid.js")
|
var newUid = require("../uniqueid");
|
||||||
var trigger = require("../events/trigger.js")
|
var trigger = require("../events/trigger");
|
||||||
|
|
||||||
module.exports = function(responseText, request, href, options) {
|
module.exports = function(responseText, request, href, options) {
|
||||||
options = clone(options || this.options)
|
options = clone(options || this.options);
|
||||||
options.request = request
|
options.request = request;
|
||||||
|
|
||||||
// Fail if unable to load HTML via AJAX
|
// Fail if unable to load HTML via AJAX
|
||||||
if (responseText === false) {
|
if (responseText === false) {
|
||||||
trigger(document, "pjax:complete pjax:error", options)
|
trigger(document, "pjax:complete pjax:error", options);
|
||||||
|
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// push scroll position to history
|
// push scroll position to history
|
||||||
var currentState = window.history.state || {}
|
var currentState = window.history.state || {};
|
||||||
window.history.replaceState({
|
window.history.replaceState(
|
||||||
|
{
|
||||||
url: currentState.url || window.location.href,
|
url: currentState.url || window.location.href,
|
||||||
title: currentState.title || document.title,
|
title: currentState.title || document.title,
|
||||||
uid: currentState.uid || newUid(),
|
uid: currentState.uid || newUid(),
|
||||||
scrollPos: [document.documentElement.scrollLeft || document.body.scrollLeft,
|
scrollPos: [
|
||||||
document.documentElement.scrollTop || document.body.scrollTop]
|
document.documentElement.scrollLeft || document.body.scrollLeft,
|
||||||
|
document.documentElement.scrollTop || document.body.scrollTop
|
||||||
|
]
|
||||||
},
|
},
|
||||||
document.title, window.location)
|
document.title,
|
||||||
|
window.location.href
|
||||||
|
);
|
||||||
|
|
||||||
// Check for redirects
|
// Check for redirects
|
||||||
var oldHref = href
|
var oldHref = href;
|
||||||
if (request.responseURL) {
|
if (request.responseURL) {
|
||||||
if (href !== request.responseURL) {
|
if (href !== request.responseURL) {
|
||||||
href = request.responseURL
|
href = request.responseURL;
|
||||||
}
|
}
|
||||||
}
|
} else if (request.getResponseHeader("X-PJAX-URL")) {
|
||||||
else if (request.getResponseHeader("X-PJAX-URL")) {
|
href = request.getResponseHeader("X-PJAX-URL");
|
||||||
href = request.getResponseHeader("X-PJAX-URL")
|
} else if (request.getResponseHeader("X-XHR-Redirected-To")) {
|
||||||
}
|
href = request.getResponseHeader("X-XHR-Redirected-To");
|
||||||
else if (request.getResponseHeader("X-XHR-Redirected-To")) {
|
|
||||||
href = request.getResponseHeader("X-XHR-Redirected-To")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add back the hash if it was removed
|
// Add back the hash if it was removed
|
||||||
var a = document.createElement("a")
|
var a = document.createElement("a");
|
||||||
a.href = oldHref
|
a.href = oldHref;
|
||||||
var oldHash = a.hash
|
var oldHash = a.hash;
|
||||||
a.href = href
|
a.href = href;
|
||||||
if (oldHash && !a.hash) {
|
if (oldHash && !a.hash) {
|
||||||
a.hash = oldHash
|
a.hash = oldHash;
|
||||||
href = a.href
|
href = a.href;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state.href = href
|
this.state.href = href;
|
||||||
this.state.options = options
|
this.state.options = options;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.loadContent(responseText, this.options)
|
this.loadContent(responseText, options);
|
||||||
}
|
} catch (e) {
|
||||||
catch (e) {
|
trigger(document, "pjax:error", options);
|
||||||
trigger(document, "pjax:error", options)
|
|
||||||
|
|
||||||
if (!this.options.debug) {
|
if (!this.options.debug) {
|
||||||
if (console && console.error) {
|
if (console && console.error) {
|
||||||
console.error("Pjax switch fail: ", e)
|
console.error("Pjax switch fail: ", e);
|
||||||
}
|
|
||||||
return this.latestChance(href)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw e
|
|
||||||
}
|
}
|
||||||
|
return this.latestChance(href);
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
module.exports = function() {
|
module.exports = function() {
|
||||||
if (this.options.debug && console) {
|
if (this.options.debug && console) {
|
||||||
if (typeof console.log === "function") {
|
if (typeof console.log === "function") {
|
||||||
console.log.apply(console, arguments)
|
console.log.apply(console, arguments);
|
||||||
}
|
}
|
||||||
// IE is weird
|
// IE is weird
|
||||||
else if (console.log) {
|
else if (console.log) {
|
||||||
console.log(arguments)
|
console.log(arguments);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
var attrState = "data-pjax-state"
|
var attrState = "data-pjax-state";
|
||||||
|
|
||||||
module.exports = function(el) {
|
module.exports = function(el) {
|
||||||
switch (el.tagName.toLowerCase()) {
|
switch (el.tagName.toLowerCase()) {
|
||||||
case "a":
|
case "a":
|
||||||
// only attach link if el does not already have link attached
|
// only attach link if el does not already have link attached
|
||||||
if (!el.hasAttribute(attrState)) {
|
if (!el.hasAttribute(attrState)) {
|
||||||
this.attachLink(el)
|
this.attachLink(el);
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
|
|
||||||
case "form":
|
case "form":
|
||||||
// only attach link if el does not already have link attached
|
// only attach link if el does not already have link attached
|
||||||
if (!el.hasAttribute(attrState)) {
|
if (!el.hasAttribute(attrState)) {
|
||||||
this.attachForm(el)
|
this.attachForm(el);
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw "Pjax can only be applied on <a> or <form> submit"
|
throw "Pjax can only be applied on <a> or <form> submit";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,78 +1,86 @@
|
|||||||
var updateQueryString = require("./util/update-query-string");
|
var updateQueryString = require("./util/update-query-string");
|
||||||
|
|
||||||
module.exports = function(location, options, callback) {
|
module.exports = function(location, options, callback) {
|
||||||
options = options || {}
|
options = options || {};
|
||||||
var queryString
|
var queryString;
|
||||||
var requestOptions = options.requestOptions || {}
|
var requestOptions = options.requestOptions || {};
|
||||||
var requestMethod = (requestOptions.requestMethod || "GET").toUpperCase()
|
var requestMethod = (requestOptions.requestMethod || "GET").toUpperCase();
|
||||||
var requestParams = requestOptions.requestParams || null
|
var requestParams = requestOptions.requestParams || null;
|
||||||
var formData = requestOptions.formData || null;
|
var formData = requestOptions.formData || null;
|
||||||
var requestPayload = null
|
var requestPayload = null;
|
||||||
var request = new XMLHttpRequest()
|
var request = new XMLHttpRequest();
|
||||||
var timeout = options.timeout || 0
|
var timeout = options.timeout || 0;
|
||||||
|
|
||||||
request.onreadystatechange = function() {
|
request.onreadystatechange = function() {
|
||||||
if (request.readyState === 4) {
|
if (request.readyState === 4) {
|
||||||
if (request.status === 200) {
|
if (request.status === 200) {
|
||||||
callback(request.responseText, request, location, options)
|
callback(request.responseText, request, location, options);
|
||||||
}
|
} else if (request.status !== 0) {
|
||||||
else if (request.status !== 0) {
|
callback(null, request, location, options);
|
||||||
callback(null, request, location, options)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
request.onerror = function(e) {
|
request.onerror = function(e) {
|
||||||
console.log(e)
|
console.log(e);
|
||||||
callback(null, request, location, options)
|
callback(null, request, location, options);
|
||||||
}
|
};
|
||||||
|
|
||||||
request.ontimeout = function() {
|
request.ontimeout = function() {
|
||||||
callback(null, request, location, options)
|
callback(null, request, location, options);
|
||||||
}
|
};
|
||||||
|
|
||||||
// Prepare the request payload for forms, if available
|
// Prepare the request payload for forms, if available
|
||||||
if (requestParams && requestParams.length) {
|
if (requestParams && requestParams.length) {
|
||||||
// Build query string
|
// Build query string
|
||||||
queryString = (requestParams.map(function(param) {return param.name + "=" + param.value})).join("&")
|
queryString = requestParams
|
||||||
|
.map(function(param) {
|
||||||
|
return param.name + "=" + param.value;
|
||||||
|
})
|
||||||
|
.join("&");
|
||||||
|
|
||||||
switch (requestMethod) {
|
switch (requestMethod) {
|
||||||
case "GET":
|
case "GET":
|
||||||
// Reset query string to avoid an issue with repeat submissions where checkboxes that were
|
// Reset query string to avoid an issue with repeat submissions where checkboxes that were
|
||||||
// previously checked are incorrectly preserved
|
// previously checked are incorrectly preserved
|
||||||
location = location.split("?")[0]
|
location = location.split("?")[0];
|
||||||
|
|
||||||
// Append new query string
|
// Append new query string
|
||||||
location += "?" + queryString
|
location += "?" + queryString;
|
||||||
break
|
break;
|
||||||
|
|
||||||
case "POST":
|
case "POST":
|
||||||
// Send query string as request payload
|
// Send query string as request payload
|
||||||
requestPayload = queryString
|
requestPayload = queryString;
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
} else if (formData) {
|
||||||
else if (formData) {
|
requestPayload = formData;
|
||||||
requestPayload = formData
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a timestamp as part of the query string if cache busting is enabled
|
// Add a timestamp as part of the query string if cache busting is enabled
|
||||||
if (options.cacheBust) {
|
if (options.cacheBust) {
|
||||||
location = updateQueryString(location, "t", Date.now())
|
location = updateQueryString(location, "t", Date.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
request.open(requestMethod, location, true)
|
request.open(requestMethod, location, true);
|
||||||
request.timeout = timeout
|
request.timeout = timeout;
|
||||||
request.setRequestHeader("X-Requested-With", "XMLHttpRequest")
|
request.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
||||||
request.setRequestHeader("X-PJAX", "true")
|
request.setRequestHeader("X-PJAX", "true");
|
||||||
request.setRequestHeader("X-PJAX-Selectors", JSON.stringify(options.selectors))
|
request.setRequestHeader(
|
||||||
|
"X-PJAX-Selectors",
|
||||||
|
JSON.stringify(options.selectors)
|
||||||
|
);
|
||||||
|
|
||||||
// Send the proper header information for POST forms
|
// Send the proper header information for POST forms
|
||||||
if (requestPayload && requestMethod === "POST") {
|
if (requestPayload && requestMethod === "POST" && !formData) {
|
||||||
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
|
request.setRequestHeader(
|
||||||
|
"Content-Type",
|
||||||
|
"application/x-www-form-urlencoded"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
request.send(requestPayload)
|
request.send(requestPayload);
|
||||||
|
|
||||||
return request
|
return request;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,37 +1,59 @@
|
|||||||
var forEachEls = require("./foreach-els")
|
var forEachEls = require("./foreach-els");
|
||||||
|
|
||||||
var defaultSwitches = require("./switches")
|
var defaultSwitches = require("./switches");
|
||||||
|
|
||||||
module.exports = function(switches, switchesOptions, selectors, fromEl, toEl, options) {
|
module.exports = function(
|
||||||
var switchesQueue = []
|
switches,
|
||||||
|
switchesOptions,
|
||||||
|
selectors,
|
||||||
|
fromEl,
|
||||||
|
toEl,
|
||||||
|
options
|
||||||
|
) {
|
||||||
|
var switchesQueue = [];
|
||||||
|
|
||||||
selectors.forEach(function(selector) {
|
selectors.forEach(function(selector) {
|
||||||
var newEls = fromEl.querySelectorAll(selector)
|
var newEls = fromEl.querySelectorAll(selector);
|
||||||
var oldEls = toEl.querySelectorAll(selector)
|
var oldEls = toEl.querySelectorAll(selector);
|
||||||
if (this.log) {
|
if (this.log) {
|
||||||
this.log("Pjax switch", selector, newEls, oldEls)
|
this.log("Pjax switch", selector, newEls, oldEls);
|
||||||
}
|
}
|
||||||
if (newEls.length !== oldEls.length) {
|
if (newEls.length !== oldEls.length) {
|
||||||
throw "DOM doesn’t look the same on new loaded page: ’" + selector + "’ - new " + newEls.length + ", old " + oldEls.length
|
throw "DOM doesn’t look the same on new loaded page: ’" +
|
||||||
|
selector +
|
||||||
|
"’ - new " +
|
||||||
|
newEls.length +
|
||||||
|
", old " +
|
||||||
|
oldEls.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachEls(newEls, function(newEl, i) {
|
forEachEls(
|
||||||
var oldEl = oldEls[i]
|
newEls,
|
||||||
|
function(newEl, i) {
|
||||||
|
var oldEl = oldEls[i];
|
||||||
if (this.log) {
|
if (this.log) {
|
||||||
this.log("newEl", newEl, "oldEl", oldEl)
|
this.log("newEl", newEl, "oldEl", oldEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
var callback = (switches[selector]) ?
|
var callback = switches[selector]
|
||||||
switches[selector].bind(this, oldEl, newEl, options, switchesOptions[selector]) :
|
? switches[selector].bind(
|
||||||
defaultSwitches.outerHTML.bind(this, oldEl, newEl, options)
|
this,
|
||||||
|
oldEl,
|
||||||
|
newEl,
|
||||||
|
options,
|
||||||
|
switchesOptions[selector]
|
||||||
|
)
|
||||||
|
: defaultSwitches.outerHTML.bind(this, oldEl, newEl, options);
|
||||||
|
|
||||||
switchesQueue.push(callback)
|
switchesQueue.push(callback);
|
||||||
}, this)
|
},
|
||||||
}, this)
|
this
|
||||||
|
);
|
||||||
|
}, this);
|
||||||
|
|
||||||
this.state.numPendingSwitches = switchesQueue.length
|
this.state.numPendingSwitches = switchesQueue.length;
|
||||||
|
|
||||||
switchesQueue.forEach(function(queuedSwitch) {
|
switchesQueue.forEach(function(queuedSwitch) {
|
||||||
queuedSwitch()
|
queuedSwitch();
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|||||||
128
lib/switches.js
128
lib/switches.js
@@ -1,122 +1,140 @@
|
|||||||
var on = require("./events/on.js")
|
var on = require("./events/on");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
outerHTML: function(oldEl, newEl) {
|
outerHTML: function(oldEl, newEl) {
|
||||||
oldEl.outerHTML = newEl.outerHTML
|
oldEl.outerHTML = newEl.outerHTML;
|
||||||
this.onSwitch()
|
this.onSwitch();
|
||||||
},
|
},
|
||||||
|
|
||||||
innerHTML: function(oldEl, newEl) {
|
innerHTML: function(oldEl, newEl) {
|
||||||
oldEl.innerHTML = newEl.innerHTML
|
oldEl.innerHTML = newEl.innerHTML;
|
||||||
|
|
||||||
if (newEl.className === "") {
|
if (newEl.className === "") {
|
||||||
oldEl.removeAttribute("class")
|
oldEl.removeAttribute("class");
|
||||||
}
|
} else {
|
||||||
else {
|
oldEl.className = newEl.className;
|
||||||
oldEl.className = newEl.className
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onSwitch()
|
this.onSwitch();
|
||||||
},
|
},
|
||||||
|
|
||||||
switchElementsAlt: function(oldEl, newEl) {
|
switchElementsAlt: function(oldEl, newEl) {
|
||||||
oldEl.innerHTML = newEl.innerHTML
|
oldEl.innerHTML = newEl.innerHTML;
|
||||||
|
|
||||||
// Copy attributes from the new element to the old one
|
// Copy attributes from the new element to the old one
|
||||||
if (newEl.hasAttributes()) {
|
if (newEl.hasAttributes()) {
|
||||||
var attrs = newEl.attributes
|
var attrs = newEl.attributes;
|
||||||
for (var i = 0; i < attrs.length; i++) {
|
for (var i = 0; i < attrs.length; i++) {
|
||||||
oldEl.attributes.setNamedItem(attrs[i].cloneNode())
|
oldEl.attributes.setNamedItem(attrs[i].cloneNode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onSwitch()
|
this.onSwitch();
|
||||||
},
|
},
|
||||||
|
|
||||||
// Equivalent to outerHTML(), but doesn't require switchElementsAlt() for <head> and <body>
|
// Equivalent to outerHTML(), but doesn't require switchElementsAlt() for <head> and <body>
|
||||||
replaceNode: function(oldEl, newEl) {
|
replaceNode: function(oldEl, newEl) {
|
||||||
oldEl.parentNode.replaceChild(newEl, oldEl)
|
oldEl.parentNode.replaceChild(newEl, oldEl);
|
||||||
this.onSwitch()
|
this.onSwitch();
|
||||||
},
|
},
|
||||||
|
|
||||||
sideBySide: function(oldEl, newEl, options, switchOptions) {
|
sideBySide: function(oldEl, newEl, options, switchOptions) {
|
||||||
var forEach = Array.prototype.forEach
|
var forEach = Array.prototype.forEach;
|
||||||
var elsToRemove = []
|
var elsToRemove = [];
|
||||||
var elsToAdd = []
|
var elsToAdd = [];
|
||||||
var fragToAppend = document.createDocumentFragment()
|
var fragToAppend = document.createDocumentFragment();
|
||||||
var animationEventNames = "animationend webkitAnimationEnd MSAnimationEnd oanimationend"
|
var animationEventNames =
|
||||||
var animatedElsNumber = 0
|
"animationend webkitAnimationEnd MSAnimationEnd oanimationend";
|
||||||
|
var animatedElsNumber = 0;
|
||||||
var sexyAnimationEnd = function(e) {
|
var sexyAnimationEnd = function(e) {
|
||||||
if (e.target !== e.currentTarget) {
|
if (e.target !== e.currentTarget) {
|
||||||
// end triggered by an animation on a child
|
// end triggered by an animation on a child
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
animatedElsNumber--
|
animatedElsNumber--;
|
||||||
if (animatedElsNumber <= 0 && elsToRemove) {
|
if (animatedElsNumber <= 0 && elsToRemove) {
|
||||||
elsToRemove.forEach(function(el) {
|
elsToRemove.forEach(function(el) {
|
||||||
// browsing quickly can make the el
|
// browsing quickly can make the el
|
||||||
// already removed by last page update ?
|
// already removed by last page update ?
|
||||||
if (el.parentNode) {
|
if (el.parentNode) {
|
||||||
el.parentNode.removeChild(el)
|
el.parentNode.removeChild(el);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
elsToAdd.forEach(function(el) {
|
elsToAdd.forEach(function(el) {
|
||||||
el.className = el.className.replace(el.getAttribute("data-pjax-classes"), "")
|
el.className = el.className.replace(
|
||||||
el.removeAttribute("data-pjax-classes")
|
el.getAttribute("data-pjax-classes"),
|
||||||
})
|
""
|
||||||
|
);
|
||||||
|
el.removeAttribute("data-pjax-classes");
|
||||||
|
});
|
||||||
|
|
||||||
elsToAdd = null // free memory
|
elsToAdd = null; // free memory
|
||||||
elsToRemove = null // free memory
|
elsToRemove = null; // free memory
|
||||||
|
|
||||||
// this is to trigger some repaint (example: picturefill)
|
// this is to trigger some repaint (example: picturefill)
|
||||||
this.onSwitch()
|
this.onSwitch();
|
||||||
}
|
}
|
||||||
}.bind(this)
|
}.bind(this);
|
||||||
|
|
||||||
switchOptions = switchOptions || {}
|
switchOptions = switchOptions || {};
|
||||||
|
|
||||||
forEach.call(oldEl.childNodes, function(el) {
|
forEach.call(oldEl.childNodes, function(el) {
|
||||||
elsToRemove.push(el)
|
elsToRemove.push(el);
|
||||||
if (el.classList && !el.classList.contains("js-Pjax-remove")) {
|
if (el.classList && !el.classList.contains("js-Pjax-remove")) {
|
||||||
// for fast switch, clean element that just have been added, & not cleaned yet.
|
// for fast switch, clean element that just have been added, & not cleaned yet.
|
||||||
if (el.hasAttribute("data-pjax-classes")) {
|
if (el.hasAttribute("data-pjax-classes")) {
|
||||||
el.className = el.className.replace(el.getAttribute("data-pjax-classes"), "")
|
el.className = el.className.replace(
|
||||||
el.removeAttribute("data-pjax-classes")
|
el.getAttribute("data-pjax-classes"),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
el.removeAttribute("data-pjax-classes");
|
||||||
}
|
}
|
||||||
el.classList.add("js-Pjax-remove")
|
el.classList.add("js-Pjax-remove");
|
||||||
if (switchOptions.callbacks && switchOptions.callbacks.removeElement) {
|
if (switchOptions.callbacks && switchOptions.callbacks.removeElement) {
|
||||||
switchOptions.callbacks.removeElement(el)
|
switchOptions.callbacks.removeElement(el);
|
||||||
}
|
}
|
||||||
if (switchOptions.classNames) {
|
if (switchOptions.classNames) {
|
||||||
el.className += " " + switchOptions.classNames.remove + " " + (options.backward ? switchOptions.classNames.backward : switchOptions.classNames.forward)
|
el.className +=
|
||||||
|
" " +
|
||||||
|
switchOptions.classNames.remove +
|
||||||
|
" " +
|
||||||
|
(options.backward
|
||||||
|
? switchOptions.classNames.backward
|
||||||
|
: switchOptions.classNames.forward);
|
||||||
}
|
}
|
||||||
animatedElsNumber++
|
animatedElsNumber++;
|
||||||
on(el, animationEventNames, sexyAnimationEnd, true)
|
on(el, animationEventNames, sexyAnimationEnd, true);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
forEach.call(newEl.childNodes, function(el) {
|
forEach.call(newEl.childNodes, function(el) {
|
||||||
if (el.classList) {
|
if (el.classList) {
|
||||||
var addClasses = ""
|
var addClasses = "";
|
||||||
if (switchOptions.classNames) {
|
if (switchOptions.classNames) {
|
||||||
addClasses = " js-Pjax-add " + switchOptions.classNames.add + " " + (options.backward ? switchOptions.classNames.forward : switchOptions.classNames.backward)
|
addClasses =
|
||||||
|
" js-Pjax-add " +
|
||||||
|
switchOptions.classNames.add +
|
||||||
|
" " +
|
||||||
|
(options.backward
|
||||||
|
? switchOptions.classNames.forward
|
||||||
|
: switchOptions.classNames.backward);
|
||||||
}
|
}
|
||||||
if (switchOptions.callbacks && switchOptions.callbacks.addElement) {
|
if (switchOptions.callbacks && switchOptions.callbacks.addElement) {
|
||||||
switchOptions.callbacks.addElement(el)
|
switchOptions.callbacks.addElement(el);
|
||||||
}
|
}
|
||||||
el.className += addClasses
|
el.className += addClasses;
|
||||||
el.setAttribute("data-pjax-classes", addClasses)
|
el.setAttribute("data-pjax-classes", addClasses);
|
||||||
elsToAdd.push(el)
|
elsToAdd.push(el);
|
||||||
fragToAppend.appendChild(el)
|
fragToAppend.appendChild(el);
|
||||||
animatedElsNumber++
|
animatedElsNumber++;
|
||||||
on(el, animationEventNames, sexyAnimationEnd, true)
|
on(el, animationEventNames, sexyAnimationEnd, true);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
// pass all className of the parent
|
// pass all className of the parent
|
||||||
oldEl.className = newEl.className
|
oldEl.className = newEl.className;
|
||||||
oldEl.appendChild(fragToAppend)
|
oldEl.appendChild(fragToAppend);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
module.exports = (function() {
|
module.exports = (function() {
|
||||||
var counter = 0
|
var counter = 0;
|
||||||
return function() {
|
return function() {
|
||||||
var id = ("pjax" + (new Date().getTime())) + "_" + counter
|
var id = "pjax" + new Date().getTime() + "_" + counter;
|
||||||
counter++
|
counter++;
|
||||||
return id
|
return id;
|
||||||
}
|
};
|
||||||
})()
|
})();
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
module.exports = function(obj) {
|
module.exports = function(obj) {
|
||||||
/* istanbul ignore if */
|
/* istanbul ignore if */
|
||||||
if (null === obj || "object" !== typeof obj) {
|
if (null === obj || "object" !== typeof obj) {
|
||||||
return obj
|
return obj;
|
||||||
}
|
}
|
||||||
var copy = obj.constructor()
|
var copy = obj.constructor();
|
||||||
for (var attr in obj) {
|
for (var attr in obj) {
|
||||||
if (obj.hasOwnProperty(attr)) {
|
if (obj.hasOwnProperty(attr)) {
|
||||||
copy[attr] = obj[attr]
|
copy[attr] = obj[attr];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return copy
|
return copy;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
module.exports = function contains(doc, selectors, el) {
|
module.exports = function contains(doc, selectors, el) {
|
||||||
for (var i = 0; i < selectors.length; i++) {
|
for (var i = 0; i < selectors.length; i++) {
|
||||||
var selectedEls = doc.querySelectorAll(selectors[i])
|
var selectedEls = doc.querySelectorAll(selectors[i]);
|
||||||
for (var j = 0; j < selectedEls.length; j++) {
|
for (var j = 0; j < selectedEls.length; j++) {
|
||||||
if (selectedEls[j].contains(el)) {
|
if (selectedEls[j].contains(el)) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
module.exports = function(target) {
|
module.exports = function(target) {
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var to = Object(target)
|
var to = Object(target);
|
||||||
|
|
||||||
for (var i = 1; i < arguments.length; i++) {
|
for (var i = 1; i < arguments.length; i++) {
|
||||||
var source = arguments[i]
|
var source = arguments[i];
|
||||||
|
|
||||||
if (source != null) {
|
if (source != null) {
|
||||||
for (var key in source) {
|
for (var key in source) {
|
||||||
// Avoid bugs when hasOwnProperty is shadowed
|
// Avoid bugs when hasOwnProperty is shadowed
|
||||||
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||||
to[key] = source[key]
|
to[key] = source[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return to
|
return to;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
module.exports = function() {}
|
module.exports = function() {};
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
module.exports = function(uri, key, value) {
|
module.exports = function(uri, key, value) {
|
||||||
var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i")
|
var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i");
|
||||||
var separator = uri.indexOf("?") !== -1 ? "&" : "?"
|
var separator = uri.indexOf("?") !== -1 ? "&" : "?";
|
||||||
if (uri.match(re)) {
|
if (uri.match(re)) {
|
||||||
return uri.replace(re, "$1" + key + "=" + value + "$2")
|
return uri.replace(re, "$1" + key + "=" + value + "$2");
|
||||||
}
|
} else {
|
||||||
else {
|
return uri + separator + key + "=" + value;
|
||||||
return uri + separator + key + "=" + value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|||||||
5772
package-lock.json
generated
5772
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
26
package.json
26
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pjax",
|
"name": "pjax",
|
||||||
"version": "0.2.6",
|
"version": "0.2.8",
|
||||||
"description": "Easily enable fast AJAX navigation on any website (using pushState + XHR)",
|
"description": "Easily enable fast AJAX navigation on any website (using pushState + XHR)",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"pjax",
|
"pjax",
|
||||||
@@ -22,18 +22,22 @@
|
|||||||
"index.js",
|
"index.js",
|
||||||
"lib",
|
"lib",
|
||||||
"pjax.js",
|
"pjax.js",
|
||||||
"pjax.min.js"
|
"pjax.min.js",
|
||||||
|
"index.d.ts"
|
||||||
],
|
],
|
||||||
"types": "index.d.ts",
|
"types": "index.d.ts",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"browserify": "^15.0.0",
|
"browserify": "^15.0.0",
|
||||||
"jscs": "^3.0.7",
|
"eslint": "^5.7.0",
|
||||||
|
"eslint-config-i-am-meticulous": "^11.0.0",
|
||||||
|
"husky": "^1.2.0",
|
||||||
"jsdom": "^11.5.1",
|
"jsdom": "^11.5.1",
|
||||||
"jsdom-global": "^3.0.2",
|
"jsdom-global": "^3.0.2",
|
||||||
"jshint": "^2.5.6",
|
"lint-staged": "^8.1.0",
|
||||||
"npmpub": "^3.1.0",
|
"npmpub": "^3.1.0",
|
||||||
"nyc": "^11.4.1",
|
"nyc": "^11.4.1",
|
||||||
"opn-cli": "^3.1.0",
|
"opn-cli": "^3.1.0",
|
||||||
|
"prettier": "^1.14.3",
|
||||||
"serve": "^6.4.4",
|
"serve": "^6.4.4",
|
||||||
"tap-nyc": "^1.0.3",
|
"tap-nyc": "^1.0.3",
|
||||||
"tap-spec": "^4.1.1",
|
"tap-spec": "^4.1.1",
|
||||||
@@ -41,7 +45,7 @@
|
|||||||
"uglify-js": "^3.3.8"
|
"uglify-js": "^3.3.8"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "jscs . && jshint . --exclude-path .gitignore",
|
"lint": "eslint .",
|
||||||
"standalone": "browserify index.js --standalone Pjax > pjax.js",
|
"standalone": "browserify index.js --standalone Pjax > pjax.js",
|
||||||
"build": "npm run standalone && uglifyjs pjax.js -o pjax.min.js",
|
"build": "npm run standalone && uglifyjs pjax.js -o pjax.min.js",
|
||||||
"build-debug": "browserify index.js --debug --standalone Pjax > pjax.js",
|
"build-debug": "browserify index.js --debug --standalone Pjax > pjax.js",
|
||||||
@@ -52,5 +56,17 @@
|
|||||||
"example": "opn http://localhost:3000/example/ && serve -p 3000 .",
|
"example": "opn http://localhost:3000/example/ && serve -p 3000 .",
|
||||||
"prepublish": "npm run build",
|
"prepublish": "npm run build",
|
||||||
"release": "npmpub"
|
"release": "npmpub"
|
||||||
|
},
|
||||||
|
"husky": {
|
||||||
|
"hooks": {
|
||||||
|
"pre-commit": "npm test && lint-staged"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.js": [
|
||||||
|
"eslint --fix",
|
||||||
|
"prettier --write",
|
||||||
|
"git add"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var abortRequest = require("../../lib/abort-request.js")
|
var abortRequest = require("../../lib/abort-request.js");
|
||||||
var sendRequest = require("../../lib/send-request.js")
|
var sendRequest = require("../../lib/send-request.js");
|
||||||
|
|
||||||
// Polyfill responseURL property into XMLHttpRequest if it doesn't exist,
|
// Polyfill responseURL property into XMLHttpRequest if it doesn't exist,
|
||||||
// just for the purposes of this test
|
// just for the purposes of this test
|
||||||
// This polyfill is not complete; it won't show the updated location if a
|
// This polyfill is not complete; it won't show the updated location if a
|
||||||
// redirection occurred, but it's fine for our purposes.
|
// redirection occurred, but it's fine for our purposes.
|
||||||
if (!("responseURL" in XMLHttpRequest.prototype)) {
|
if (!("responseURL" in XMLHttpRequest.prototype)) {
|
||||||
var nativeOpen = XMLHttpRequest.prototype.open
|
var nativeOpen = XMLHttpRequest.prototype.open;
|
||||||
XMLHttpRequest.prototype.open = function(method, url) {
|
XMLHttpRequest.prototype.open = function(method, url) {
|
||||||
this.responseURL = url
|
this.responseURL = url;
|
||||||
return nativeOpen.apply(this, arguments)
|
return nativeOpen.apply(this, arguments);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
tape("test aborting xhr request", function(t) {
|
tape("test aborting xhr request", function(t) {
|
||||||
@@ -20,37 +20,36 @@ tape("test aborting xhr request", function(t) {
|
|||||||
options: {
|
options: {
|
||||||
cacheBust: true
|
cacheBust: true
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
t.test("- pending request is aborted", function(t) {
|
t.test("- pending request is aborted", function(t) {
|
||||||
var r = requestCacheBust("https://httpbin.org/delay/10", {}, function() {
|
var r = requestCacheBust("https://httpbin.org/delay/10", {}, function() {
|
||||||
t.fail("xhr was not aborted")
|
t.fail("xhr was not aborted");
|
||||||
})
|
});
|
||||||
t.equal(r.readyState, 1, "xhr readyState is '1' (SENT)")
|
t.equal(r.readyState, 1, "xhr readyState is '1' (SENT)");
|
||||||
abortRequest(r)
|
abortRequest(r);
|
||||||
t.equal(r.readyState, 0, "xhr readyState is '0' (ABORTED)")
|
t.equal(r.readyState, 0, "xhr readyState is '0' (ABORTED)");
|
||||||
t.equal(r.status, 0, "xhr HTTP status is '0' (ABORTED)")
|
t.equal(r.status, 0, "xhr HTTP status is '0' (ABORTED)");
|
||||||
t.equal(r.responseText, "", "xhr response is empty")
|
t.equal(r.responseText, "", "xhr response is empty");
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
t.test("- request is not aborted if it has already completed", function(t) {
|
t.test("- request is not aborted if it has already completed", function(t) {
|
||||||
var r = requestCacheBust("https://httpbin.org/get", {}, function() {
|
var r = requestCacheBust("https://httpbin.org/get", {}, function() {
|
||||||
abortRequest(r)
|
abortRequest(r);
|
||||||
t.equal(r.readyState, 4, "xhr readyState is '4' (DONE)")
|
t.equal(r.readyState, 4, "xhr readyState is '4' (DONE)");
|
||||||
t.equal(r.status, 200, "xhr HTTP status is '200' (OK)")
|
t.equal(r.status, 200, "xhr HTTP status is '200' (OK)");
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
t.test("- request is not aborted if it is undefined", function(t) {
|
t.test("- request is not aborted if it is undefined", function(t) {
|
||||||
var r
|
var r;
|
||||||
try {
|
try {
|
||||||
abortRequest(r)
|
abortRequest(r);
|
||||||
|
} catch (e) {
|
||||||
|
t.fail("aborting an undefined request threw an error");
|
||||||
}
|
}
|
||||||
catch (e) {
|
t.equal(typeof r, "undefined", "undefined xhr was ignored");
|
||||||
t.fail("aborting an undefined request threw an error")
|
t.end();
|
||||||
}
|
});
|
||||||
t.equal(typeof r, "undefined", "undefined xhr was ignored")
|
t.end();
|
||||||
t.end()
|
});
|
||||||
})
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|||||||
@@ -1,23 +1,49 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var evalScript = require("../../lib/eval-script")
|
var evalScript = require("../../lib/eval-script");
|
||||||
|
|
||||||
tape("test evalScript method", function(t) {
|
tape("test evalScript method", function(t) {
|
||||||
document.body.className = ""
|
document.body.className = "";
|
||||||
|
|
||||||
var script = document.createElement("script")
|
var script = document.createElement("script");
|
||||||
script.innerHTML = "document.body.className = 'executed'"
|
script.innerHTML = "document.body.className = 'executed'";
|
||||||
|
|
||||||
t.equal(document.body.className, "", "script hasn't been executed yet")
|
t.equal(document.body.className, "", "script hasn't been executed yet");
|
||||||
|
|
||||||
evalScript(script)
|
evalScript(script);
|
||||||
t.equal(document.body.className, "executed", "script has been properly executed")
|
t.equal(
|
||||||
|
document.body.className,
|
||||||
|
"executed",
|
||||||
|
"script has been properly executed"
|
||||||
|
);
|
||||||
|
|
||||||
script.innerHTML = "document.write('failure')"
|
script.innerHTML = "document.write('failure')";
|
||||||
var bodyText = "document.write hasn't been executed"
|
var bodyText = "document.write hasn't been executed";
|
||||||
document.body.text = bodyText
|
document.body.text = bodyText;
|
||||||
evalScript(script)
|
evalScript(script);
|
||||||
t.equal(document.body.text, bodyText, "document.write hasn't been executed")
|
t.equal(document.body.text, bodyText, "document.write hasn't been executed");
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
|
tape(
|
||||||
|
"evalScript should not throw an error if the script removed itself",
|
||||||
|
function(t) {
|
||||||
|
var script = document.createElement("script");
|
||||||
|
script.id = "myScript";
|
||||||
|
script.innerHTML =
|
||||||
|
"const script = document.querySelector('#myScript');" +
|
||||||
|
"script.parentNode.removeChild(script);";
|
||||||
|
|
||||||
|
try {
|
||||||
|
evalScript(script);
|
||||||
|
|
||||||
|
t.pass("Missing script tested successfully");
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
t.fail("Attempted to remove missing script");
|
||||||
|
}
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,110 +1,177 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var on = require("../../lib/events/on")
|
var on = require("../../lib/events/on");
|
||||||
var off = require("../../lib/events/off")
|
var off = require("../../lib/events/off");
|
||||||
var trigger = require("../../lib/events/trigger")
|
var trigger = require("../../lib/events/trigger");
|
||||||
|
|
||||||
var el = document.createElement("div")
|
var el = document.createElement("div");
|
||||||
var el2 = document.createElement("span")
|
var el2 = document.createElement("span");
|
||||||
var els = [el, el2]
|
var els = [el, el2];
|
||||||
|
|
||||||
var classCb = function() {
|
var classCb = function() {
|
||||||
this.className += "on"
|
this.className += "on";
|
||||||
}
|
};
|
||||||
var attrCb = function() {
|
var attrCb = function() {
|
||||||
this.setAttribute("data-state", this.getAttribute("data-state") + "ON")
|
this.setAttribute("data-state", this.getAttribute("data-state") + "ON");
|
||||||
}
|
};
|
||||||
|
|
||||||
tape("test events on/off/trigger for one element, one event", function(t) {
|
tape("test events on/off/trigger for one element, one event", function(t) {
|
||||||
el.className = ""
|
el.className = "";
|
||||||
on(el, "click", classCb)
|
on(el, "click", classCb);
|
||||||
trigger(el, "click")
|
trigger(el, "click");
|
||||||
t.equal(el.className, "on", "attached callback has been fired properly")
|
t.equal(el.className, "on", "attached callback has been fired properly");
|
||||||
|
|
||||||
el.className = "off"
|
el.className = "off";
|
||||||
off(el, "click", classCb)
|
off(el, "click", classCb);
|
||||||
trigger(el, "click")
|
trigger(el, "click");
|
||||||
t.equal(el.className, "off", "triggered event didn't fire detached callback")
|
t.equal(el.className, "off", "triggered event didn't fire detached callback");
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test events on/off/trigger for multiple elements, one event", function(t) {
|
tape("test events on/off/trigger for multiple elements, one event", function(
|
||||||
el.className = ""
|
t
|
||||||
el2.className = ""
|
) {
|
||||||
|
el.className = "";
|
||||||
|
el2.className = "";
|
||||||
|
|
||||||
on(els, "click", classCb)
|
on(els, "click", classCb);
|
||||||
trigger(els, "click")
|
trigger(els, "click");
|
||||||
t.equal(el.className, "on", "attached callback has been fired properly on the first element")
|
t.equal(
|
||||||
t.equal(el2.className, "on", "attached callback has been fired properly on the second element")
|
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"
|
el.className = "off";
|
||||||
el2.className = "off"
|
el2.className = "off";
|
||||||
off(els, "click", classCb)
|
off(els, "click", classCb);
|
||||||
trigger(els, "click")
|
trigger(els, "click");
|
||||||
t.equal(el.className, "off", "triggered event didn't fire detached callback on the first element")
|
t.equal(
|
||||||
t.equal(el2.className, "off", "triggered event didn't fire detached callback on the second element")
|
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()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test events on/off/trigger for one element, multiple events", function(t) {
|
tape("test events on/off/trigger for one element, multiple events", function(
|
||||||
el.className = ""
|
t
|
||||||
on(el, "click mouseover", classCb)
|
) {
|
||||||
trigger(el, "click mouseover")
|
el.className = "";
|
||||||
t.equal(el.className, "onon", "attached callbacks have been fired properly")
|
on(el, "click mouseover", classCb);
|
||||||
|
trigger(el, "click mouseover");
|
||||||
|
t.equal(el.className, "onon", "attached callbacks have been fired properly");
|
||||||
|
|
||||||
el.className = "off"
|
el.className = "off";
|
||||||
off(el, "click mouseover", classCb)
|
off(el, "click mouseover", classCb);
|
||||||
trigger(el, "click mouseover")
|
trigger(el, "click mouseover");
|
||||||
t.equal(el.className, "off", "triggered events didn't fire detached callback")
|
t.equal(
|
||||||
|
el.className,
|
||||||
|
"off",
|
||||||
|
"triggered events didn't fire detached callback"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test events on/off/trigger for multiple elements, multiple events", function(t) {
|
tape(
|
||||||
el.className = ""
|
"test events on/off/trigger for multiple elements, multiple events",
|
||||||
el2.className = ""
|
function(t) {
|
||||||
el.setAttribute("data-state", "")
|
el.className = "";
|
||||||
el2.setAttribute("data-state", "")
|
el2.className = "";
|
||||||
on(els, "click mouseover", classCb)
|
el.setAttribute("data-state", "");
|
||||||
on(els, "resize scroll", attrCb)
|
el2.setAttribute("data-state", "");
|
||||||
trigger(els, "click mouseover resize scroll")
|
on(els, "click mouseover", classCb);
|
||||||
t.equal(el.className, "onon", "attached callbacks has been fired properly on the first element")
|
on(els, "resize scroll", attrCb);
|
||||||
t.equal(el.getAttribute("data-state"), "ONON", "attached callbacks has been fired properly on the first element")
|
trigger(els, "click mouseover resize scroll");
|
||||||
t.equal(el2.className, "onon", "attached callbacks has been fired properly on the second element")
|
t.equal(
|
||||||
t.equal(el2.getAttribute("data-state"), "ONON", "attached callbacks has been fired properly on the second element")
|
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"
|
el.className = "off";
|
||||||
el2.className = "off"
|
el2.className = "off";
|
||||||
el.setAttribute("data-state", "off")
|
el.setAttribute("data-state", "off");
|
||||||
el2.setAttribute("data-state", "off")
|
el2.setAttribute("data-state", "off");
|
||||||
off(els, "click mouseover", classCb)
|
off(els, "click mouseover", classCb);
|
||||||
off(els, "resize scroll", attrCb)
|
off(els, "resize scroll", attrCb);
|
||||||
trigger(els, "click mouseover resize scroll")
|
trigger(els, "click mouseover resize scroll");
|
||||||
t.equal(el.className, "off", "triggered events didn't fire detached callbacks on the first element")
|
t.equal(
|
||||||
t.equal(el.getAttribute("data-state"), "off", "triggered events didn't fire detached callbacks on the first element")
|
el.className,
|
||||||
t.equal(el2.className, "off", "triggered events didn't fire detached callbacks on the first element")
|
"off",
|
||||||
t.equal(el2.getAttribute("data-state"), "off", "triggered events didn't fire detached callbacks on the first element")
|
"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()
|
t.end();
|
||||||
})
|
}
|
||||||
|
);
|
||||||
|
|
||||||
tape("test events on top level elements", function(t) {
|
tape("test events on top level elements", function(t) {
|
||||||
var el = document
|
var el = document;
|
||||||
|
|
||||||
el.className = ""
|
el.className = "";
|
||||||
on(el, "click", classCb)
|
on(el, "click", classCb);
|
||||||
trigger(el, "click")
|
trigger(el, "click");
|
||||||
t.equal(el.className, "on", "attached callback has been fired properly on document")
|
t.equal(
|
||||||
|
el.className,
|
||||||
|
"on",
|
||||||
|
"attached callback has been fired properly on document"
|
||||||
|
);
|
||||||
|
|
||||||
el = window
|
el = window;
|
||||||
|
|
||||||
el.className = ""
|
el.className = "";
|
||||||
// With jsdom, the default this is global, not window, so we need to explicitly bind to window.
|
// With jsdom, the default this is global, not window, so we need to explicitly bind to window.
|
||||||
on(el, "click", classCb.bind(window))
|
on(el, "click", classCb.bind(window));
|
||||||
trigger(el, "click")
|
trigger(el, "click");
|
||||||
t.equal(el.className, "on", "attached callback has been fired properly on window")
|
t.equal(
|
||||||
|
el.className,
|
||||||
|
"on",
|
||||||
|
"attached callback has been fired properly on window"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,29 +1,49 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var executeScripts = require("../../lib/execute-scripts")
|
var executeScripts = require("../../lib/execute-scripts");
|
||||||
|
|
||||||
tape("test executeScripts method when the script tag is inside a container", function(t) {
|
tape(
|
||||||
document.body.className = ""
|
"test executeScripts method when the script tag is inside a container",
|
||||||
|
function(t) {
|
||||||
|
document.body.className = "";
|
||||||
|
|
||||||
var container = document.createElement("div")
|
var container = document.createElement("div");
|
||||||
container.innerHTML = "<" + "script" + ">document.body.className = 'executed';</" + "script" + "><" + "script" + ">document.body.className += ' correctly';</" + "script" + ">"
|
container.innerHTML =
|
||||||
|
"<" +
|
||||||
|
"script" +
|
||||||
|
">document.body.className = 'executed';</" +
|
||||||
|
"script" +
|
||||||
|
"><" +
|
||||||
|
"script" +
|
||||||
|
">document.body.className += ' correctly';</" +
|
||||||
|
"script" +
|
||||||
|
">";
|
||||||
|
|
||||||
t.equal(document.body.className, "", "script hasn't been executed yet")
|
t.equal(document.body.className, "", "script hasn't been executed yet");
|
||||||
executeScripts(container)
|
executeScripts(container);
|
||||||
t.equal(document.body.className, "executed correctly", "script has been properly executed")
|
t.equal(
|
||||||
|
document.body.className,
|
||||||
|
"executed correctly",
|
||||||
|
"script has been properly executed"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
}
|
||||||
|
);
|
||||||
|
|
||||||
tape("test executeScripts method with just a script tag", function(t) {
|
tape("test executeScripts method with just a script tag", function(t) {
|
||||||
document.body.className = ""
|
document.body.className = "";
|
||||||
|
|
||||||
var script = document.createElement("script")
|
var script = document.createElement("script");
|
||||||
script.innerHTML = "document.body.className = 'executed correctly';"
|
script.innerHTML = "document.body.className = 'executed correctly';";
|
||||||
|
|
||||||
t.equal(document.body.className, "", "script hasn't been executed yet")
|
t.equal(document.body.className, "", "script hasn't been executed yet");
|
||||||
executeScripts(script)
|
executeScripts(script);
|
||||||
t.equal(document.body.className, "executed correctly", "script has been properly executed")
|
t.equal(
|
||||||
|
document.body.className,
|
||||||
|
"executed correctly",
|
||||||
|
"script has been properly executed"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,45 +1,60 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var forEachEls = require("../../lib/foreach-els.js")
|
var forEachEls = require("../../lib/foreach-els.js");
|
||||||
|
|
||||||
var div = document.createElement("div")
|
var div = document.createElement("div");
|
||||||
var span = document.createElement("span")
|
var span = document.createElement("span");
|
||||||
var cb = function(el) {
|
var cb = function(el) {
|
||||||
el.innerHTML = "boom"
|
el.innerHTML = "boom";
|
||||||
}
|
};
|
||||||
|
|
||||||
tape("test forEachEls on one element", function(t) {
|
tape("test forEachEls on one element", function(t) {
|
||||||
div.innerHTML = "div tag"
|
div.innerHTML = "div tag";
|
||||||
forEachEls(div, cb)
|
forEachEls(div, cb);
|
||||||
|
|
||||||
t.equal(div.innerHTML, "boom", "works correctly on one element")
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
|
t.equal(div.innerHTML, "boom", "works correctly on one element");
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
tape("test forEachEls on an array", function(t) {
|
tape("test forEachEls on an array", function(t) {
|
||||||
div.innerHTML = "div tag"
|
div.innerHTML = "div tag";
|
||||||
span.innerHTML = "span tag"
|
span.innerHTML = "span tag";
|
||||||
|
|
||||||
forEachEls([div, span], cb)
|
forEachEls([div, span], cb);
|
||||||
|
|
||||||
t.equal(div.innerHTML, "boom", "works correctly on the first element of the array")
|
t.equal(
|
||||||
t.equal(span.innerHTML, "boom", "works correctly on the last element of the array")
|
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()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test forEachEls on a NodeList", function(t) {
|
tape("test forEachEls on a NodeList", function(t) {
|
||||||
div.innerHTML = "div tag"
|
div.innerHTML = "div tag";
|
||||||
span.innerHTML = "span tag"
|
span.innerHTML = "span tag";
|
||||||
|
|
||||||
var frag = document.createDocumentFragment()
|
var frag = document.createDocumentFragment();
|
||||||
frag.appendChild(div)
|
frag.appendChild(div);
|
||||||
frag.appendChild(span)
|
frag.appendChild(span);
|
||||||
forEachEls(frag.childNodes, cb)
|
forEachEls(frag.childNodes, cb);
|
||||||
|
|
||||||
t.equal(div.innerHTML, "boom", "works correctly on the first element of the document fragment")
|
t.equal(
|
||||||
t.equal(span.innerHTML, "boom", "works correctly on the last element of the document fragment")
|
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()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,24 +1,40 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var forEachEls = require("../../lib/foreach-selectors.js")
|
var forEachEls = require("../../lib/foreach-selectors.js");
|
||||||
|
|
||||||
var cb = function(el) {
|
var cb = function(el) {
|
||||||
el.className = "modified"
|
el.className = "modified";
|
||||||
}
|
};
|
||||||
|
|
||||||
tape("test forEachSelector", function(t) {
|
tape("test forEachSelector", function(t) {
|
||||||
forEachEls(["html", "body"], cb)
|
forEachEls(["html", "body"], cb);
|
||||||
|
|
||||||
t.equal(document.documentElement.className, "modified", "callback has been executed on first selector")
|
t.equal(
|
||||||
t.equal(document.body.className, "modified", "callback has been executed on first selector")
|
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.documentElement.className = "";
|
||||||
document.body.className = ""
|
document.body.className = "";
|
||||||
|
|
||||||
forEachEls(["html", "body"], cb, null, document.documentElement)
|
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(
|
||||||
t.equal(document.body.className, "modified", "callback has been executed on first selector when context is used")
|
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()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var isSupported = require("../../lib/is-supported.js")
|
var isSupported = require("../../lib/is-supported.js");
|
||||||
|
|
||||||
tape("test isSupported method", function(t) {
|
tape("test isSupported method", function(t) {
|
||||||
t.true(isSupported(), "well, we run test on supported browser, so it should be ok here")
|
t.true(
|
||||||
t.end()
|
isSupported(),
|
||||||
})
|
"well, we run test on supported browser, so it should be ok here"
|
||||||
|
);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,49 +1,50 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
|
var parseOptions = require("../../lib/parse-options.js");
|
||||||
|
|
||||||
var parseOptions = require("../../lib/parse-options.js")
|
|
||||||
tape("test parse initalization options function", function(t) {
|
tape("test parse initalization options function", function(t) {
|
||||||
t.test("- default options", function(t) {
|
t.test("- default options", function(t) {
|
||||||
var pjax = {}
|
var pjax = {};
|
||||||
pjax.options = parseOptions({})
|
pjax.options = parseOptions({});
|
||||||
|
|
||||||
t.equal(pjax.options.elements, "a[href], form[action]")
|
t.equal(pjax.options.elements, "a[href], form[action]");
|
||||||
t.equal(pjax.options.selectors.length, 2, "selectors length")
|
t.equal(pjax.options.selectors.length, 2, "selectors length");
|
||||||
t.equal(pjax.options.selectors[0], "title")
|
t.equal(pjax.options.selectors[0], "title");
|
||||||
t.equal(pjax.options.selectors[1], ".js-Pjax")
|
t.equal(pjax.options.selectors[1], ".js-Pjax");
|
||||||
|
|
||||||
t.equal(typeof pjax.options.switches, "object")
|
t.equal(typeof pjax.options.switches, "object");
|
||||||
t.equal(Object.keys(pjax.options.switches).length, 2)// head and body
|
t.equal(Object.keys(pjax.options.switches).length, 2); // head and body
|
||||||
|
|
||||||
t.equal(typeof pjax.options.switchesOptions, "object")
|
t.equal(typeof pjax.options.switchesOptions, "object");
|
||||||
t.equal(Object.keys(pjax.options.switchesOptions).length, 0)
|
t.equal(Object.keys(pjax.options.switchesOptions).length, 0);
|
||||||
|
|
||||||
t.equal(pjax.options.history, true)
|
t.equal(pjax.options.history, true);
|
||||||
t.equal(typeof pjax.options.analytics, "function")
|
t.equal(typeof pjax.options.analytics, "function");
|
||||||
t.equal(pjax.options.scrollTo, 0)
|
t.equal(pjax.options.scrollTo, 0);
|
||||||
t.equal(pjax.options.scrollRestoration, true)
|
t.equal(pjax.options.scrollRestoration, true);
|
||||||
t.equal(pjax.options.cacheBust, true)
|
t.equal(pjax.options.cacheBust, true);
|
||||||
t.equal(pjax.options.debug, false)
|
t.equal(pjax.options.debug, false);
|
||||||
t.equal(pjax.options.currentUrlFullReload, false)
|
t.equal(pjax.options.currentUrlFullReload, false);
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
// verify analytics always ends up as a function even when passed not a function
|
// verify analytics always ends up as a function even when passed not a function
|
||||||
t.test("- analytics is a function", function(t) {
|
t.test("- analytics is a function", function(t) {
|
||||||
var pjax = {}
|
var pjax = {};
|
||||||
pjax.options = parseOptions({analytics: "some string"})
|
pjax.options = parseOptions({ analytics: "some string" });
|
||||||
|
|
||||||
t.deepEqual(typeof pjax.options.analytics, "function")
|
t.deepEqual(typeof pjax.options.analytics, "function");
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
// verify that the value false for scrollTo is not squashed
|
// verify that the value false for scrollTo is not squashed
|
||||||
t.test("- scrollTo remains false", function(t) {
|
t.test("- scrollTo remains false", function(t) {
|
||||||
var pjax = {}
|
var pjax = {};
|
||||||
pjax.options = parseOptions({scrollTo: false})
|
pjax.options = parseOptions({ scrollTo: false });
|
||||||
|
|
||||||
t.deepEqual(pjax.options.scrollTo, false)
|
t.deepEqual(pjax.options.scrollTo, false);
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,184 +1,231 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var on = require("../../../lib/events/on")
|
var on = require("../../../lib/events/on");
|
||||||
var trigger = require("../../../lib/events/trigger")
|
var trigger = require("../../../lib/events/trigger");
|
||||||
var attachForm = require("../../../lib/proto/attach-form")
|
var attachForm = require("../../../lib/proto/attach-form");
|
||||||
|
|
||||||
var attr = "data-pjax-state"
|
var attr = "data-pjax-state";
|
||||||
|
|
||||||
tape("test attach form prototype method", function(t) {
|
tape("test attach form prototype method", function(t) {
|
||||||
var form = document.createElement("form")
|
var form = document.createElement("form");
|
||||||
var loadUrlCalled = false
|
var loadUrlCalled = false;
|
||||||
|
|
||||||
attachForm.call({
|
attachForm.call(
|
||||||
|
{
|
||||||
options: {
|
options: {
|
||||||
currentUrlFullReload: true
|
currentUrlFullReload: true
|
||||||
},
|
},
|
||||||
loadUrl: function() {
|
loadUrl: function() {
|
||||||
loadUrlCalled = true
|
loadUrlCalled = true;
|
||||||
}
|
}
|
||||||
}, form)
|
},
|
||||||
|
form
|
||||||
|
);
|
||||||
|
|
||||||
var internalUri = window.location.protocol + "//" + window.location.host + window.location.pathname + window.location.search
|
var internalUri =
|
||||||
|
window.location.protocol +
|
||||||
|
"//" +
|
||||||
|
window.location.host +
|
||||||
|
window.location.pathname +
|
||||||
|
window.location.search;
|
||||||
|
|
||||||
form.action = "http://external.com/"
|
form.action = "http://external.com/";
|
||||||
trigger(form, "submit")
|
trigger(form, "submit");
|
||||||
t.equal(form.getAttribute(attr), "external", "external url stop behavior")
|
t.equal(form.getAttribute(attr), "external", "external url stop behavior");
|
||||||
|
|
||||||
form.action = internalUri + "#anchor"
|
form.action = internalUri + "#anchor";
|
||||||
trigger(form, "submit")
|
trigger(form, "submit");
|
||||||
t.equal(form.getAttribute(attr), "anchor", "internal anchor stop behavior")
|
t.equal(form.getAttribute(attr), "anchor", "internal anchor stop behavior");
|
||||||
|
|
||||||
window.location.hash = "#anchor"
|
window.location.hash = "#anchor";
|
||||||
form.action = internalUri + "#another-anchor"
|
form.action = internalUri + "#another-anchor";
|
||||||
trigger(form, "submit")
|
trigger(form, "submit");
|
||||||
t.equal(form.getAttribute(attr), "anchor", "different anchors stop behavior")
|
t.equal(form.getAttribute(attr), "anchor", "different anchors stop behavior");
|
||||||
window.location.hash = ""
|
window.location.hash = "";
|
||||||
|
|
||||||
form.action = internalUri + "#"
|
form.action = internalUri + "#";
|
||||||
trigger(form, "submit")
|
trigger(form, "submit");
|
||||||
t.equal(form.getAttribute(attr), "anchor-empty", "empty anchor stop behavior")
|
t.equal(
|
||||||
|
form.getAttribute(attr),
|
||||||
|
"anchor-empty",
|
||||||
|
"empty anchor stop behavior"
|
||||||
|
);
|
||||||
|
|
||||||
form.action = window.location.href
|
form.action = window.location.href;
|
||||||
trigger(form, "submit")
|
trigger(form, "submit");
|
||||||
t.equal(form.getAttribute(attr), "reload", "submitting when currentUrlFullReload is true will submit normally, without XHR")
|
t.equal(
|
||||||
t.equal(loadUrlCalled, false, "loadUrl() not called")
|
form.getAttribute(attr),
|
||||||
|
"reload",
|
||||||
|
"submitting when currentUrlFullReload is true will submit normally, without XHR"
|
||||||
|
);
|
||||||
|
t.equal(loadUrlCalled, false, "loadUrl() not called");
|
||||||
|
|
||||||
form.action = window.location.protocol + "//" + window.location.host + "/internal"
|
form.action =
|
||||||
form.method = "POST"
|
window.location.protocol + "//" + window.location.host + "/internal";
|
||||||
trigger(form, "submit")
|
form.method = "POST";
|
||||||
t.equal(form.getAttribute(attr), "submit", "triggering a POST request to the next page")
|
trigger(form, "submit");
|
||||||
t.equal(loadUrlCalled, true, "loadUrl() called correctly")
|
t.equal(
|
||||||
|
form.getAttribute(attr),
|
||||||
|
"submit",
|
||||||
|
"triggering a POST request to the next page"
|
||||||
|
);
|
||||||
|
t.equal(loadUrlCalled, true, "loadUrl() called correctly");
|
||||||
|
|
||||||
loadUrlCalled = false
|
loadUrlCalled = false;
|
||||||
form.setAttribute(attr, "")
|
form.setAttribute(attr, "");
|
||||||
form.action = window.location.protocol + "//" + window.location.host + "/internal"
|
form.action =
|
||||||
form.method = "GET"
|
window.location.protocol + "//" + window.location.host + "/internal";
|
||||||
trigger(form, "submit")
|
form.method = "GET";
|
||||||
t.equal(form.getAttribute(attr), "submit", "triggering a GET request to the next page")
|
trigger(form, "submit");
|
||||||
t.equal(loadUrlCalled, true, "loadUrl() called correctly")
|
t.equal(
|
||||||
|
form.getAttribute(attr),
|
||||||
|
"submit",
|
||||||
|
"triggering a GET request to the next page"
|
||||||
|
);
|
||||||
|
t.equal(loadUrlCalled, true, "loadUrl() called correctly");
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test attach form preventDefaulted events", function(t) {
|
tape("test attach form preventDefaulted events", function(t) {
|
||||||
var loadUrlCalled = false
|
var loadUrlCalled = false;
|
||||||
var form = document.createElement("form")
|
var form = document.createElement("form");
|
||||||
|
|
||||||
// This needs to be before the call to attachForm()
|
// This needs to be before the call to attachForm()
|
||||||
on(form, "submit", function(event) { event.preventDefault() })
|
on(form, "submit", function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
attachForm.call({
|
attachForm.call(
|
||||||
|
{
|
||||||
options: {},
|
options: {},
|
||||||
loadUrl: function() {
|
loadUrl: function() {
|
||||||
loadUrlCalled = true
|
loadUrlCalled = true;
|
||||||
}
|
}
|
||||||
}, form)
|
},
|
||||||
|
form
|
||||||
|
);
|
||||||
|
|
||||||
form.action = "#"
|
form.action = "#";
|
||||||
trigger(form, "submit")
|
trigger(form, "submit");
|
||||||
t.equal(loadUrlCalled, false, "events that are preventDefaulted should not fire callback")
|
t.equal(
|
||||||
|
loadUrlCalled,
|
||||||
|
false,
|
||||||
|
"events that are preventDefaulted should not fire callback"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test options are not modified by attachForm", function(t) {
|
tape("test options are not modified by attachForm", function(t) {
|
||||||
var form = document.createElement("form")
|
var form = document.createElement("form");
|
||||||
var options = {foo: "bar"}
|
var options = { foo: "bar" };
|
||||||
var loadUrl = function() {}
|
var loadUrl = function() {};
|
||||||
|
|
||||||
attachForm.call({options: options, loadUrl: loadUrl}, form)
|
attachForm.call({ options: options, loadUrl: loadUrl }, form);
|
||||||
|
|
||||||
form.action = window.location.protocol + "//" + window.location.host + window.location.pathname + window.location.search
|
form.action =
|
||||||
form.method = "GET"
|
window.location.protocol +
|
||||||
trigger(form, "submit")
|
"//" +
|
||||||
|
window.location.host +
|
||||||
|
window.location.pathname +
|
||||||
|
window.location.search;
|
||||||
|
form.method = "GET";
|
||||||
|
trigger(form, "submit");
|
||||||
|
|
||||||
t.equal(1, Object.keys(options).length, "options object that is passed in should not be modified")
|
t.equal(
|
||||||
t.equal("bar", options.foo, "options object that is passed in should not be modified")
|
1,
|
||||||
|
Object.keys(options).length,
|
||||||
|
"options object that is passed in should not be modified"
|
||||||
|
);
|
||||||
|
t.equal(
|
||||||
|
"bar",
|
||||||
|
options.foo,
|
||||||
|
"options object that is passed in should not be modified"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test submit triggered by keyboard", function(t) {
|
|
||||||
var form = document.createElement("form")
|
|
||||||
var pjax = {
|
|
||||||
options: {},
|
|
||||||
loadUrl: function() {
|
|
||||||
t.equal(form.getAttribute(attr), "submit", "triggering a internal link actually submits the form")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t.plan(2)
|
|
||||||
|
|
||||||
attachForm.call(pjax, form)
|
|
||||||
|
|
||||||
form.action = window.location.protocol + "//" + window.location.host + "/internal"
|
|
||||||
|
|
||||||
trigger(form, "keyup", {keyCode: 14})
|
|
||||||
t.equal(form.getAttribute(attr), "", "keycode other than 13 doesn't trigger anything")
|
|
||||||
|
|
||||||
trigger(form, "keyup", {keyCode: 13})
|
|
||||||
// see loadUrl defined above
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape("test form elements parsed correctly", function(t) {
|
tape("test form elements parsed correctly", function(t) {
|
||||||
t.plan(1)
|
t.plan(1);
|
||||||
|
|
||||||
var form = document.createElement("form")
|
var form = document.createElement("form");
|
||||||
var input = document.createElement("input")
|
var input = document.createElement("input");
|
||||||
input.name = "input"
|
input.name = "input";
|
||||||
input.value = "value"
|
input.value = "value";
|
||||||
form.appendChild(input)
|
form.appendChild(input);
|
||||||
|
|
||||||
var params = [{
|
var params = [
|
||||||
|
{
|
||||||
name: "input",
|
name: "input",
|
||||||
value: "value"
|
value: "value"
|
||||||
}]
|
}
|
||||||
|
];
|
||||||
var pjax = {
|
var pjax = {
|
||||||
options: {},
|
options: {},
|
||||||
loadUrl: function(href, options) {
|
loadUrl: function(href, options) {
|
||||||
t.same(options.requestOptions.requestParams, params, "form elements parsed correctly")
|
t.same(
|
||||||
}
|
options.requestOptions.requestParams,
|
||||||
|
params,
|
||||||
|
"form elements parsed correctly"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
attachForm.call(pjax, form)
|
attachForm.call(pjax, form);
|
||||||
|
|
||||||
form.action = window.location.protocol + "//" + window.location.host + "/internal"
|
form.action =
|
||||||
|
window.location.protocol + "//" + window.location.host + "/internal";
|
||||||
|
|
||||||
trigger(form, "submit")
|
trigger(form, "submit");
|
||||||
// see loadUrl defined above
|
// see loadUrl defined above
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test form.enctype=\"multipart/form-data\"", function(t) {
|
tape('test form.enctype="multipart/form-data"', function(t) {
|
||||||
t.plan(4)
|
t.plan(4);
|
||||||
|
|
||||||
var form = document.createElement("form")
|
var form = document.createElement("form");
|
||||||
form.enctype = "multipart/form-data"
|
form.enctype = "multipart/form-data";
|
||||||
var input = document.createElement("input")
|
var input = document.createElement("input");
|
||||||
input.name = "input"
|
input.name = "input";
|
||||||
input.value = "value"
|
input.value = "value";
|
||||||
form.appendChild(input)
|
form.appendChild(input);
|
||||||
|
|
||||||
var pjax = {
|
var pjax = {
|
||||||
options: {},
|
options: {},
|
||||||
loadUrl: function(href, options) {
|
loadUrl: function(href, options) {
|
||||||
t.equals(options.requestOptions.requestParams, undefined, "form elements not parsed manually")
|
t.equals(
|
||||||
t.true(options.requestOptions.formData instanceof FormData, "requestOptions.formData is a FormData")
|
options.requestOptions.requestParams,
|
||||||
t.equals(Array.from(options.requestOptions.formData.entries()).length, 1, "correct number of FormData elements")
|
undefined,
|
||||||
t.equals(options.requestOptions.formData.get("input"), "value", "FormData element value set correctly")
|
"form elements not parsed manually"
|
||||||
}
|
);
|
||||||
|
t.true(
|
||||||
|
options.requestOptions.formData instanceof FormData,
|
||||||
|
"requestOptions.formData is a FormData"
|
||||||
|
);
|
||||||
|
t.equals(
|
||||||
|
Array.from(options.requestOptions.formData.entries()).length,
|
||||||
|
1,
|
||||||
|
"correct number of FormData elements"
|
||||||
|
);
|
||||||
|
t.equals(
|
||||||
|
options.requestOptions.formData.get("input"),
|
||||||
|
"value",
|
||||||
|
"FormData element value set correctly"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
attachForm.call(pjax, form)
|
attachForm.call(pjax, form);
|
||||||
|
|
||||||
form.action = window.location.protocol + "//" + window.location.host + "/internal"
|
form.action =
|
||||||
|
window.location.protocol + "//" + window.location.host + "/internal";
|
||||||
|
|
||||||
trigger(form, "submit")
|
trigger(form, "submit");
|
||||||
// see loadUrl defined above
|
// see loadUrl defined above
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,143 +1,190 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var on = require("../../../lib/events/on")
|
var on = require("../../../lib/events/on");
|
||||||
var trigger = require("../../../lib/events/trigger")
|
var trigger = require("../../../lib/events/trigger");
|
||||||
var attachLink = require("../../../lib/proto/attach-link")
|
var attachLink = require("../../../lib/proto/attach-link");
|
||||||
|
|
||||||
var attr = "data-pjax-state"
|
var attr = "data-pjax-state";
|
||||||
|
|
||||||
tape("test attach link prototype method", function(t) {
|
tape("test attach link prototype method", function(t) {
|
||||||
var a = document.createElement("a")
|
var a = document.createElement("a");
|
||||||
var loadUrlCalled = false
|
var loadUrlCalled = false;
|
||||||
|
|
||||||
attachLink.call({
|
attachLink.call(
|
||||||
|
{
|
||||||
options: {},
|
options: {},
|
||||||
loadUrl: function() {
|
loadUrl: function() {
|
||||||
loadUrlCalled = true
|
loadUrlCalled = true;
|
||||||
}
|
}
|
||||||
}, a)
|
},
|
||||||
|
a
|
||||||
|
);
|
||||||
|
|
||||||
var internalUri = window.location.protocol + "//" + window.location.host + window.location.pathname + window.location.search
|
var internalUri =
|
||||||
|
window.location.protocol +
|
||||||
|
"//" +
|
||||||
|
window.location.host +
|
||||||
|
window.location.pathname +
|
||||||
|
window.location.search;
|
||||||
|
|
||||||
a.href = internalUri
|
a.href = internalUri;
|
||||||
trigger(a, "click", {metaKey: true})
|
trigger(a, "click", { metaKey: true });
|
||||||
t.equal(a.getAttribute(attr), "modifier", "event key modifier stop behavior")
|
t.equal(a.getAttribute(attr), "modifier", "event key modifier stop behavior");
|
||||||
|
|
||||||
a.href = "http://external.com/"
|
a.href = "http://external.com/";
|
||||||
trigger(a, "click")
|
trigger(a, "click");
|
||||||
t.equal(a.getAttribute(attr), "external", "external url stop behavior")
|
t.equal(a.getAttribute(attr), "external", "external url stop behavior");
|
||||||
|
|
||||||
window.location.hash = "#anchor"
|
window.location.hash = "#anchor";
|
||||||
a.href = internalUri + "#anchor"
|
a.href = internalUri + "#anchor";
|
||||||
trigger(a, "click")
|
trigger(a, "click");
|
||||||
t.equal(a.getAttribute(attr), "anchor", "internal anchor stop behavior")
|
t.equal(a.getAttribute(attr), "anchor", "internal anchor stop behavior");
|
||||||
|
|
||||||
a.href = internalUri + "#another-anchor"
|
a.href = internalUri + "#another-anchor";
|
||||||
trigger(a, "click")
|
trigger(a, "click");
|
||||||
t.equal(a.getAttribute(attr), "anchor", "different anchors stop behavior")
|
t.equal(a.getAttribute(attr), "anchor", "different anchors stop behavior");
|
||||||
window.location.hash = ""
|
window.location.hash = "";
|
||||||
|
|
||||||
a.href = internalUri + "#"
|
a.href = internalUri + "#";
|
||||||
trigger(a, "click")
|
trigger(a, "click");
|
||||||
t.equal(a.getAttribute(attr), "anchor-empty", "empty anchor stop behavior")
|
t.equal(a.getAttribute(attr), "anchor-empty", "empty anchor stop behavior");
|
||||||
|
|
||||||
a.href = window.location.protocol + "//" + window.location.host + "/internal"
|
a.href = window.location.protocol + "//" + window.location.host + "/internal";
|
||||||
trigger(a, "click")
|
trigger(a, "click");
|
||||||
t.equals(a.getAttribute(attr), "load", "triggering an internal link sets the state attribute to 'load'")
|
t.equals(
|
||||||
t.equals(loadUrlCalled, true, "triggering an internal link actually loads the page")
|
a.getAttribute(attr),
|
||||||
|
"load",
|
||||||
|
"triggering an internal link sets the state attribute to 'load'"
|
||||||
|
);
|
||||||
|
t.equals(
|
||||||
|
loadUrlCalled,
|
||||||
|
true,
|
||||||
|
"triggering an internal link actually loads the page"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test attach link preventDefaulted events", function(t) {
|
tape("test attach link preventDefaulted events", function(t) {
|
||||||
var loadUrlCalled = false
|
var loadUrlCalled = false;
|
||||||
var a = document.createElement("a")
|
var a = document.createElement("a");
|
||||||
|
|
||||||
// This needs to be before the call to attachLink()
|
// This needs to be before the call to attachLink()
|
||||||
on(a, "click", function(event) {
|
on(a, "click", function(event) {
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
})
|
});
|
||||||
|
|
||||||
attachLink.call({
|
attachLink.call(
|
||||||
|
{
|
||||||
options: {},
|
options: {},
|
||||||
loadUrl: function() {
|
loadUrl: function() {
|
||||||
loadUrlCalled = true
|
loadUrlCalled = true;
|
||||||
}
|
}
|
||||||
}, a)
|
},
|
||||||
|
a
|
||||||
|
);
|
||||||
|
|
||||||
a.href = "#"
|
a.href = "#";
|
||||||
trigger(a, "click")
|
trigger(a, "click");
|
||||||
t.equal(loadUrlCalled, false, "events that are preventDefaulted should not fire callback")
|
t.equal(
|
||||||
|
loadUrlCalled,
|
||||||
|
false,
|
||||||
|
"events that are preventDefaulted should not fire callback"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test options are not modified by attachLink", function(t) {
|
tape("test options are not modified by attachLink", function(t) {
|
||||||
var a = document.createElement("a")
|
var a = document.createElement("a");
|
||||||
var options = {foo: "bar"}
|
var options = { foo: "bar" };
|
||||||
var loadUrl = function() {}
|
var loadUrl = function() {};
|
||||||
|
|
||||||
attachLink.call({options: options, loadUrl: loadUrl}, a)
|
attachLink.call({ options: options, loadUrl: loadUrl }, a);
|
||||||
|
|
||||||
a.href = window.location.protocol + "//" + window.location.host + window.location.pathname + window.location.search
|
a.href =
|
||||||
|
window.location.protocol +
|
||||||
|
"//" +
|
||||||
|
window.location.host +
|
||||||
|
window.location.pathname +
|
||||||
|
window.location.search;
|
||||||
|
|
||||||
trigger(a, "click")
|
trigger(a, "click");
|
||||||
|
|
||||||
t.equal(1, Object.keys(options).length, "options object that is passed in should not be modified")
|
t.equal(
|
||||||
t.equal("bar", options.foo, "options object that is passed in should not be modified")
|
1,
|
||||||
|
Object.keys(options).length,
|
||||||
|
"options object that is passed in should not be modified"
|
||||||
|
);
|
||||||
|
t.equal(
|
||||||
|
"bar",
|
||||||
|
options.foo,
|
||||||
|
"options object that is passed in should not be modified"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test link triggered by keyboard", function(t) {
|
tape("test link triggered by keyboard", function(t) {
|
||||||
var a = document.createElement("a")
|
var a = document.createElement("a");
|
||||||
var pjax = {
|
var pjax = {
|
||||||
options: {},
|
options: {},
|
||||||
loadUrl: function() {
|
loadUrl: function() {
|
||||||
t.equal(a.getAttribute(attr), "load", "triggering a internal link actually loads the page")
|
t.equal(
|
||||||
}
|
a.getAttribute(attr),
|
||||||
|
"load",
|
||||||
|
"triggering a internal link actually loads the page"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
t.plan(3)
|
t.plan(3);
|
||||||
|
|
||||||
attachLink.call(pjax, a)
|
attachLink.call(pjax, a);
|
||||||
|
|
||||||
a.href = window.location.protocol + "//" + window.location.host + "/internal"
|
a.href = window.location.protocol + "//" + window.location.host + "/internal";
|
||||||
|
|
||||||
trigger(a, "keyup", {keyCode: 14})
|
trigger(a, "keyup", { keyCode: 14 });
|
||||||
t.equal(a.getAttribute(attr), "", "keycode other than 13 doesn't trigger anything")
|
t.equal(
|
||||||
|
a.getAttribute(attr),
|
||||||
|
"",
|
||||||
|
"keycode other than 13 doesn't trigger anything"
|
||||||
|
);
|
||||||
|
|
||||||
trigger(a, "keyup", {keyCode: 13, metaKey: true})
|
trigger(a, "keyup", { keyCode: 13, metaKey: true });
|
||||||
t.equal(a.getAttribute(attr), "modifier", "event key modifier stop behavior")
|
t.equal(a.getAttribute(attr), "modifier", "event key modifier stop behavior");
|
||||||
|
|
||||||
trigger(a, "keyup", {keyCode: 13})
|
trigger(a, "keyup", { keyCode: 13 });
|
||||||
// see loadUrl defined above
|
// see loadUrl defined above
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test link with the same URL as the current one, when currentUrlFullReload set to true", function(t) {
|
tape(
|
||||||
var a = document.createElement("a")
|
"test link with the same URL as the current one, when currentUrlFullReload set to true",
|
||||||
|
function(t) {
|
||||||
|
var a = document.createElement("a");
|
||||||
var pjax = {
|
var pjax = {
|
||||||
options: {
|
options: {
|
||||||
currentUrlFullReload: true
|
currentUrlFullReload: true
|
||||||
},
|
},
|
||||||
reload: function() {
|
reload: function() {
|
||||||
t.pass("this.reload() was called correctly")
|
t.pass("this.reload() was called correctly");
|
||||||
},
|
},
|
||||||
loadUrl: function() {
|
loadUrl: function() {
|
||||||
t.fail("loadUrl() was called wrongly")
|
t.fail("loadUrl() was called wrongly");
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
t.plan(2);
|
||||||
|
|
||||||
|
attachLink.call(pjax, a);
|
||||||
|
|
||||||
|
a.href = window.location.href;
|
||||||
|
|
||||||
|
trigger(a, "click");
|
||||||
|
t.equal(a.getAttribute(attr), "reload", "reload stop behavior");
|
||||||
|
|
||||||
|
t.end();
|
||||||
}
|
}
|
||||||
|
);
|
||||||
t.plan(2)
|
|
||||||
|
|
||||||
attachLink.call(pjax, a)
|
|
||||||
|
|
||||||
a.href = window.location.href
|
|
||||||
|
|
||||||
trigger(a, "click")
|
|
||||||
t.equal(a.getAttribute(attr), "reload", "reload stop behavior")
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|||||||
@@ -1,39 +1,47 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var handleReponse = require("../../../lib/proto/handle-response")
|
var handleResponse = require("../../../lib/proto/handle-response");
|
||||||
var noop = require("../../../lib/util/noop")
|
var noop = require("../../../lib/util/noop");
|
||||||
|
var loadContent = require("../../../index").prototype.loadContent;
|
||||||
|
|
||||||
var href = "https://example.org/"
|
var href = "https://example.org/";
|
||||||
|
|
||||||
var storeEventHandler
|
var storeEventHandler;
|
||||||
var pjaxErrorEventTriggerTest
|
var pjaxErrorEventTriggerTest;
|
||||||
|
|
||||||
tape("test events triggered when handleResponse(false) is called", function(t) {
|
tape("test events triggered when handleResponse(false) is called", function(t) {
|
||||||
t.plan(3)
|
t.plan(3);
|
||||||
|
|
||||||
var pjax = {
|
var pjax = {
|
||||||
options: {
|
options: {
|
||||||
test: 1
|
test: 1
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
var events = []
|
var events = [];
|
||||||
|
|
||||||
storeEventHandler = function(e) {
|
storeEventHandler = function(e) {
|
||||||
events.push(e.type)
|
events.push(e.type);
|
||||||
|
|
||||||
t.equal(e.test, 1)
|
t.equal(e.test, 1);
|
||||||
}
|
};
|
||||||
|
|
||||||
document.addEventListener("pjax:complete", storeEventHandler)
|
document.addEventListener("pjax:complete", storeEventHandler);
|
||||||
document.addEventListener("pjax:error", storeEventHandler)
|
document.addEventListener("pjax:error", storeEventHandler);
|
||||||
|
|
||||||
handleReponse.bind(pjax)(false, null)
|
handleResponse.bind(pjax)(false, null);
|
||||||
|
|
||||||
t.same(events, ["pjax:complete", "pjax:error"], "calling handleResponse(false) triggers 'pjax:complete' and 'pjax:error'")
|
t.same(
|
||||||
|
events,
|
||||||
|
["pjax:complete", "pjax:error"],
|
||||||
|
"calling handleResponse(false) triggers 'pjax:complete' and 'pjax:error'"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
document.removeEventListener("pjax:complete", storeEventHandler);
|
||||||
})
|
document.removeEventListener("pjax:error", storeEventHandler);
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
tape("test when handleResponse() is called normally", function(t) {
|
tape("test when handleResponse() is called normally", function(t) {
|
||||||
var pjax = {
|
var pjax = {
|
||||||
@@ -42,167 +50,244 @@ tape("test when handleResponse() is called normally", function(t) {
|
|||||||
},
|
},
|
||||||
loadContent: noop,
|
loadContent: noop,
|
||||||
state: {}
|
state: {}
|
||||||
}
|
};
|
||||||
|
|
||||||
var request = {
|
var request = {
|
||||||
getResponseHeader: noop
|
getResponseHeader: noop
|
||||||
}
|
};
|
||||||
|
|
||||||
handleReponse.bind(pjax)("", request, "href")
|
handleResponse.bind(pjax)("", request, "href");
|
||||||
|
|
||||||
delete window.history.state.uid
|
delete window.history.state.uid;
|
||||||
t.same(window.history.state, {
|
t.same(
|
||||||
|
window.history.state,
|
||||||
|
{
|
||||||
url: href,
|
url: href,
|
||||||
title: "",
|
title: "",
|
||||||
scrollPos: [0, 0]
|
scrollPos: [0, 0]
|
||||||
}, "window.history.state is set correctly")
|
},
|
||||||
t.equals(pjax.state.href, "href", "this.state.href is set correctly")
|
"window.history.state is set correctly"
|
||||||
t.equals(Object.keys(pjax.state.options).length, 2, "this.state.options is set correctly")
|
);
|
||||||
t.same(pjax.state.options.request, request, "this.state.options is set correctly")
|
t.equals(pjax.state.href, "href", "this.state.href is set correctly");
|
||||||
t.equals(pjax.state.options.test, 1, "this.state.options is set correctly")
|
t.equals(
|
||||||
|
Object.keys(pjax.state.options).length,
|
||||||
|
2,
|
||||||
|
"this.state.options is set correctly"
|
||||||
|
);
|
||||||
|
t.same(
|
||||||
|
pjax.state.options.request,
|
||||||
|
request,
|
||||||
|
"this.state.options is set correctly"
|
||||||
|
);
|
||||||
|
t.equals(pjax.state.options.test, 1, "this.state.options is set correctly");
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test when handleResponse() is called normally with request.responseURL", function(t) {
|
tape(
|
||||||
|
"test when handleResponse() is called normally with request.responseURL",
|
||||||
|
function(t) {
|
||||||
var pjax = {
|
var pjax = {
|
||||||
options: {},
|
options: {},
|
||||||
loadContent: noop,
|
loadContent: noop,
|
||||||
state: {}
|
state: {}
|
||||||
}
|
};
|
||||||
|
|
||||||
var request = {
|
var request = {
|
||||||
responseURL: href + "1",
|
responseURL: href + "1",
|
||||||
getResponseHeader: noop
|
getResponseHeader: noop
|
||||||
|
};
|
||||||
|
|
||||||
|
handleResponse.bind(pjax)("", request, "");
|
||||||
|
|
||||||
|
t.equals(
|
||||||
|
pjax.state.href,
|
||||||
|
request.responseURL,
|
||||||
|
"this.state.href is set correctly"
|
||||||
|
);
|
||||||
|
|
||||||
|
t.end();
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
handleReponse.bind(pjax)("", request, "")
|
tape("test when handleResponse() is called normally with X-PJAX-URL", function(
|
||||||
|
t
|
||||||
t.equals(pjax.state.href, request.responseURL, "this.state.href is set correctly")
|
) {
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape("test when handleResponse() is called normally with X-PJAX-URL", function(t) {
|
|
||||||
var pjax = {
|
var pjax = {
|
||||||
options: {},
|
options: {},
|
||||||
loadContent: noop,
|
loadContent: noop,
|
||||||
state: {}
|
state: {}
|
||||||
}
|
};
|
||||||
|
|
||||||
var request = {
|
var request = {
|
||||||
getResponseHeader: function(header) {
|
getResponseHeader: function(header) {
|
||||||
if (header === "X-PJAX-URL") {
|
if (header === "X-PJAX-URL") {
|
||||||
return href + "2"
|
return href + "2";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
handleReponse.bind(pjax)("", request, "")
|
handleResponse.bind(pjax)("", request, "");
|
||||||
|
|
||||||
t.equals(pjax.state.href, href + "2", "this.state.href is set correctly")
|
t.equals(pjax.state.href, href + "2", "this.state.href is set correctly");
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test when handleResponse() is called normally with X-XHR-Redirected-To", function(t) {
|
tape(
|
||||||
|
"test when handleResponse() is called normally with X-XHR-Redirected-To",
|
||||||
|
function(t) {
|
||||||
var pjax = {
|
var pjax = {
|
||||||
options: {},
|
options: {},
|
||||||
loadContent: noop,
|
loadContent: noop,
|
||||||
state: {}
|
state: {}
|
||||||
}
|
};
|
||||||
|
|
||||||
var request = {
|
var request = {
|
||||||
getResponseHeader: function(header) {
|
getResponseHeader: function(header) {
|
||||||
if (header === "X-XHR-Redirected-To") {
|
if (header === "X-XHR-Redirected-To") {
|
||||||
return href + "3"
|
return href + "3";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleResponse.bind(pjax)("", request, "");
|
||||||
|
|
||||||
|
t.equals(pjax.state.href, href + "3", "this.state.href is set correctly");
|
||||||
|
|
||||||
|
t.end();
|
||||||
}
|
}
|
||||||
|
);
|
||||||
handleReponse.bind(pjax)("", request, "")
|
|
||||||
|
|
||||||
t.equals(pjax.state.href, href + "3", "this.state.href is set correctly")
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape("test when handleResponse() is called normally with a hash", function(t) {
|
tape("test when handleResponse() is called normally with a hash", function(t) {
|
||||||
var pjax = {
|
var pjax = {
|
||||||
options: {},
|
options: {},
|
||||||
loadContent: noop,
|
loadContent: noop,
|
||||||
state: {}
|
state: {}
|
||||||
}
|
};
|
||||||
|
|
||||||
var request = {
|
var request = {
|
||||||
responseURL: href + "2",
|
responseURL: href + "2",
|
||||||
getResponseHeader: noop
|
getResponseHeader: noop
|
||||||
}
|
};
|
||||||
|
|
||||||
handleReponse.bind(pjax)("", request, href + "1#test")
|
handleResponse.bind(pjax)("", request, href + "1#test");
|
||||||
|
|
||||||
t.equals(pjax.state.href, href + "2#test", "this.state.href is set correctly")
|
t.equals(
|
||||||
|
pjax.state.href,
|
||||||
|
href + "2#test",
|
||||||
|
"this.state.href is set correctly"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test try...catch for loadContent() when options.debug is true", function(t) {
|
tape("test try...catch for loadContent() when options.debug is true", function(
|
||||||
t.plan(2)
|
t
|
||||||
|
) {
|
||||||
|
t.plan(2);
|
||||||
|
|
||||||
var pjax = {
|
var pjax = {
|
||||||
options: {},
|
options: {},
|
||||||
loadContent: noop,
|
loadContent: noop,
|
||||||
state: {}
|
state: {}
|
||||||
}
|
};
|
||||||
|
|
||||||
var request = {
|
var request = {
|
||||||
getResponseHeader: noop
|
getResponseHeader: noop
|
||||||
}
|
};
|
||||||
|
|
||||||
pjax.loadContent = function() {
|
pjax.loadContent = function() {
|
||||||
throw new Error()
|
throw new Error();
|
||||||
}
|
};
|
||||||
pjax.options.debug = true
|
pjax.options.debug = true;
|
||||||
|
|
||||||
document.removeEventListener("pjax:error", storeEventHandler)
|
document.removeEventListener("pjax:error", storeEventHandler);
|
||||||
pjaxErrorEventTriggerTest = function() {
|
pjaxErrorEventTriggerTest = function() {
|
||||||
t.pass("pjax:error event triggered")
|
t.pass("pjax:error event triggered");
|
||||||
}
|
};
|
||||||
document.addEventListener("pjax:error", pjaxErrorEventTriggerTest)
|
document.addEventListener("pjax:error", pjaxErrorEventTriggerTest);
|
||||||
|
|
||||||
t.throws(function() {
|
t.throws(
|
||||||
handleReponse.bind(pjax)("", request, "")
|
function() {
|
||||||
}, Error, "error is rethrown")
|
handleResponse.bind(pjax)("", request, "");
|
||||||
|
},
|
||||||
|
Error,
|
||||||
|
"error is rethrown"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test try...catch for loadContent()", function(t) {
|
tape("test try...catch for loadContent()", function(t) {
|
||||||
t.plan(2)
|
t.plan(2);
|
||||||
|
|
||||||
var pjax = {
|
var pjax = {
|
||||||
options: {},
|
options: {},
|
||||||
loadContent: noop,
|
loadContent: noop,
|
||||||
state: {}
|
state: {}
|
||||||
}
|
};
|
||||||
|
|
||||||
var request = {
|
var request = {
|
||||||
getResponseHeader: noop
|
getResponseHeader: noop
|
||||||
}
|
};
|
||||||
|
|
||||||
pjax.loadContent = function() {
|
pjax.loadContent = function() {
|
||||||
throw new Error()
|
throw new Error();
|
||||||
}
|
};
|
||||||
pjax.latestChance = function() {
|
pjax.latestChance = function() {
|
||||||
return true
|
return true;
|
||||||
|
};
|
||||||
|
pjax.options.debug = false;
|
||||||
|
|
||||||
|
document.removeEventListener("pjax:error", pjaxErrorEventTriggerTest);
|
||||||
|
|
||||||
|
t.doesNotThrow(
|
||||||
|
function() {
|
||||||
|
t.equals(
|
||||||
|
handleResponse.bind(pjax)("", request, ""),
|
||||||
|
true,
|
||||||
|
"this.latestChance() is called"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
Error,
|
||||||
|
"error is not thrown"
|
||||||
|
);
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
tape(
|
||||||
|
"test events triggered when loadContent() is called with a non-string html argument",
|
||||||
|
function(t) {
|
||||||
|
t.plan(3);
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
test: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
var events = [];
|
||||||
|
|
||||||
|
storeEventHandler = function(e) {
|
||||||
|
events.push(e.type);
|
||||||
|
|
||||||
|
t.equal(e.test, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("pjax:complete", storeEventHandler);
|
||||||
|
document.addEventListener("pjax:error", storeEventHandler);
|
||||||
|
|
||||||
|
loadContent(null, options);
|
||||||
|
|
||||||
|
t.same(
|
||||||
|
events,
|
||||||
|
["pjax:complete", "pjax:error"],
|
||||||
|
"calling loadContent() with a non-string html argument triggers 'pjax:complete' and 'pjax:error'"
|
||||||
|
);
|
||||||
|
|
||||||
|
document.removeEventListener("pjax:complete", storeEventHandler);
|
||||||
|
document.removeEventListener("pjax:error", storeEventHandler);
|
||||||
|
|
||||||
|
t.end();
|
||||||
}
|
}
|
||||||
pjax.options.debug = false
|
);
|
||||||
|
|
||||||
document.removeEventListener("pjax:error", pjaxErrorEventTriggerTest)
|
|
||||||
|
|
||||||
t.doesNotThrow(function() {
|
|
||||||
t.equals(handleReponse.bind(pjax)("", request, ""), true, "this.latestChance() is called")
|
|
||||||
}, Error, "error is not thrown")
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|||||||
@@ -1,27 +1,31 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var parseElement = require("../../../lib/proto/parse-element")
|
var parseElement = require("../../../lib/proto/parse-element");
|
||||||
|
|
||||||
var pjax = {
|
var pjax = {
|
||||||
attachLink: function() { return true },
|
attachLink: function() {
|
||||||
attachForm: function() { return true }
|
return true;
|
||||||
|
},
|
||||||
|
attachForm: function() {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
tape("test parse element prototype method", function(t) {
|
tape("test parse element prototype method", function(t) {
|
||||||
t.doesNotThrow(function() {
|
t.doesNotThrow(function() {
|
||||||
var a = document.createElement("a")
|
var a = document.createElement("a");
|
||||||
parseElement.call(pjax, a)
|
parseElement.call(pjax, a);
|
||||||
}, "<a> element can be parsed")
|
}, "<a> element can be parsed");
|
||||||
|
|
||||||
t.doesNotThrow(function() {
|
t.doesNotThrow(function() {
|
||||||
var form = document.createElement("form")
|
var form = document.createElement("form");
|
||||||
parseElement.call(pjax, form)
|
parseElement.call(pjax, form);
|
||||||
}, "<form> element can be parsed")
|
}, "<form> element can be parsed");
|
||||||
|
|
||||||
t.throws(function() {
|
t.throws(function() {
|
||||||
var el = document.createElement("div")
|
var el = document.createElement("div");
|
||||||
parseElement.call(pjax, el)
|
parseElement.call(pjax, el);
|
||||||
}, "<div> element cannot be parsed")
|
}, "<div> element cannot be parsed");
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,137 +1,165 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var sendRequest = require("../../lib/send-request.js")
|
var sendRequest = require("../../lib/send-request.js");
|
||||||
|
|
||||||
// Polyfill responseURL property into XMLHttpRequest if it doesn't exist,
|
// Polyfill responseURL property into XMLHttpRequest if it doesn't exist,
|
||||||
// just for the purposes of this test
|
// just for the purposes of this test
|
||||||
// This polyfill is not complete; it won't show the updated location if a
|
// This polyfill is not complete; it won't show the updated location if a
|
||||||
// redirection occurred, but it's fine for our purposes.
|
// redirection occurred, but it's fine for our purposes.
|
||||||
if (!("responseURL" in XMLHttpRequest.prototype)) {
|
if (!("responseURL" in XMLHttpRequest.prototype)) {
|
||||||
var nativeOpen = XMLHttpRequest.prototype.open
|
var nativeOpen = XMLHttpRequest.prototype.open;
|
||||||
XMLHttpRequest.prototype.open = function(method, url) {
|
XMLHttpRequest.prototype.open = function(method, url) {
|
||||||
this.responseURL = url
|
this.responseURL = url;
|
||||||
return nativeOpen.apply(this, arguments)
|
return nativeOpen.apply(this, arguments);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
tape("test xhr request", function(t) {
|
tape("test xhr request", function(t) {
|
||||||
var url = "https://httpbin.org/get"
|
var url = "https://httpbin.org/get";
|
||||||
|
|
||||||
t.test("- request is made, gets a result, and is cache-busted", function(t) {
|
t.test("- request is made, gets a result, and is cache-busted", function(t) {
|
||||||
var r = sendRequest(url, { cacheBust: true }, 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")
|
t.equal(
|
||||||
|
r.responseURL.indexOf("?"),
|
||||||
|
url.length,
|
||||||
|
"XHR URL is cache-busted when configured to be"
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
result = JSON.parse(result)
|
result = JSON.parse(result);
|
||||||
|
} catch (e) {
|
||||||
|
t.fail("xhr doesn't get a JSON response");
|
||||||
}
|
}
|
||||||
catch (e) {
|
t.same(typeof result, "object", "xhr request get a result");
|
||||||
t.fail("xhr doesn't get a JSON response")
|
t.end();
|
||||||
}
|
});
|
||||||
t.same(typeof result, "object", "xhr request get a result")
|
});
|
||||||
t.end()
|
t.test("- request is not cache-busted when configured not to be", function(
|
||||||
})
|
t
|
||||||
})
|
) {
|
||||||
t.test("- request is not cache-busted when configured not to be", function(t) {
|
|
||||||
var r = sendRequest(url, {}, function() {
|
var r = sendRequest(url, {}, function() {
|
||||||
t.equal(r.responseURL, url, "XHR URL is left untouched")
|
t.equal(r.responseURL, url, "XHR URL is left untouched");
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("request headers are sent properly", function(t) {
|
tape("request headers are sent properly", function(t) {
|
||||||
var url = "https://httpbin.org/headers"
|
var url = "https://httpbin.org/headers";
|
||||||
var options = {
|
var options = {
|
||||||
selectors: ["div.pjax", "div.container"]
|
selectors: ["div.pjax", "div.container"]
|
||||||
}
|
};
|
||||||
|
|
||||||
sendRequest(url, options, function(responseText) {
|
sendRequest(url, options, function(responseText) {
|
||||||
var headers = JSON.parse(responseText).headers
|
var headers = JSON.parse(responseText).headers;
|
||||||
|
|
||||||
t.equals(headers["X-Requested-With"], "XMLHttpRequest", "X-Requested-With header is set correctly")
|
t.equals(
|
||||||
|
headers["X-Requested-With"],
|
||||||
|
"XMLHttpRequest",
|
||||||
|
"X-Requested-With header is set correctly"
|
||||||
|
);
|
||||||
// Httpbin.org changes the case to 'X-Pjax'
|
// Httpbin.org changes the case to 'X-Pjax'
|
||||||
t.equals(headers["X-Pjax"], "true", "X-PJAX header is set correctly")
|
t.equals(headers["X-Pjax"], "true", "X-PJAX header is set correctly");
|
||||||
t.equals(headers["X-Pjax-Selectors"], "[\"div.pjax\",\"div.container\"]", "X-PJAX-Selectors header is set correctly")
|
t.equals(
|
||||||
|
headers["X-Pjax-Selectors"],
|
||||||
|
'["div.pjax","div.container"]',
|
||||||
|
"X-PJAX-Selectors header is set correctly"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("HTTP status codes other than 200 are handled properly", function(t) {
|
tape("HTTP status codes other than 200 are handled properly", function(t) {
|
||||||
var url = "https://httpbin.org/status/400"
|
var url = "https://httpbin.org/status/400";
|
||||||
|
|
||||||
sendRequest(url, {}, function(responseText, request) {
|
sendRequest(url, {}, function(responseText, request) {
|
||||||
t.equals(responseText, null, "responseText is null")
|
t.equals(responseText, null, "responseText is null");
|
||||||
t.equals(request.status, 400, "HTTP status code is correct")
|
t.equals(request.status, 400, "HTTP status code is correct");
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
tape.skip("XHR error is handled properly", function(t) {
|
tape.skip("XHR error is handled properly", function(t) {
|
||||||
var url = "https://encrypted.google.com/foobar"
|
var url = "https://encrypted.google.com/foobar";
|
||||||
|
|
||||||
sendRequest(url, {}, function(responseText) {
|
sendRequest(url, {}, function(responseText) {
|
||||||
t.equals(responseText, null, "responseText is null")
|
t.equals(responseText, null, "responseText is null");
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("POST body data is sent properly", function(t) {
|
tape("POST body data is sent properly", function(t) {
|
||||||
var url = "https://httpbin.org/post"
|
var url = "https://httpbin.org/post";
|
||||||
var params = [{
|
var params = [
|
||||||
|
{
|
||||||
name: "test",
|
name: "test",
|
||||||
value: "1"
|
value: "1"
|
||||||
}];
|
}
|
||||||
|
];
|
||||||
var options = {
|
var options = {
|
||||||
requestOptions: {
|
requestOptions: {
|
||||||
requestMethod: "POST",
|
requestMethod: "POST",
|
||||||
requestParams: params
|
requestParams: params
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
sendRequest(url, options, function(responseText) {
|
sendRequest(url, options, function(responseText) {
|
||||||
var response = JSON.parse(responseText)
|
var response = JSON.parse(responseText);
|
||||||
|
|
||||||
t.same(response.form[params[0].name], params[0].value, "requestParams were sent properly")
|
t.same(
|
||||||
t.equals(response.headers["Content-Type"], "application/x-www-form-urlencoded", "Content-Type header was set properly")
|
response.form[params[0].name],
|
||||||
|
params[0].value,
|
||||||
|
"requestParams were sent properly"
|
||||||
|
);
|
||||||
|
t.equals(
|
||||||
|
response.headers["Content-Type"],
|
||||||
|
"application/x-www-form-urlencoded",
|
||||||
|
"Content-Type header was set properly"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("GET query data is sent properly", function(t) {
|
tape("GET query data is sent properly", function(t) {
|
||||||
var url = "https://httpbin.org/get"
|
var url = "https://httpbin.org/get";
|
||||||
var params = [{
|
var params = [
|
||||||
|
{
|
||||||
name: "test",
|
name: "test",
|
||||||
value: "1"
|
value: "1"
|
||||||
}];
|
}
|
||||||
|
];
|
||||||
var options = {
|
var options = {
|
||||||
requestOptions: {
|
requestOptions: {
|
||||||
requestParams: params
|
requestParams: params
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
sendRequest(url, options, function(responseText) {
|
sendRequest(url, options, function(responseText) {
|
||||||
var response = JSON.parse(responseText)
|
var response = JSON.parse(responseText);
|
||||||
|
|
||||||
t.same(response.args[params[0].name], params[0].value, "requestParams were sent properly")
|
t.same(
|
||||||
|
response.args[params[0].name],
|
||||||
|
params[0].value,
|
||||||
|
"requestParams were sent properly"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("XHR timeout is handled properly", function(t) {
|
tape("XHR timeout is handled properly", function(t) {
|
||||||
var url = "https://httpbin.org/delay/5"
|
var url = "https://httpbin.org/delay/5";
|
||||||
var options = {
|
var options = {
|
||||||
timeout: 1000
|
timeout: 1000
|
||||||
}
|
};
|
||||||
|
|
||||||
sendRequest(url, options, function(responseText) {
|
sendRequest(url, options, function(responseText) {
|
||||||
t.equals(responseText, null, "responseText is null")
|
t.equals(responseText, null, "responseText is null");
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var switchesSelectors = require("../../lib/switches-selectors.js")
|
var switchesSelectors = require("../../lib/switches-selectors.js");
|
||||||
var noop = require("../../lib/util/noop")
|
var noop = require("../../lib/util/noop");
|
||||||
|
|
||||||
var pjax = {
|
var pjax = {
|
||||||
onSwitch: function() {
|
onSwitch: function() {
|
||||||
console.log("Switched")
|
console.log("Switched");
|
||||||
},
|
},
|
||||||
state: {},
|
state: {},
|
||||||
log: noop
|
log: noop
|
||||||
}
|
};
|
||||||
|
|
||||||
// @author darylteo
|
// @author darylteo
|
||||||
tape("test switchesSelectors", function(t) {
|
tape("test switchesSelectors", function(t) {
|
||||||
// switchesSelectors relies on a higher level function callback
|
// switchesSelectors relies on a higher level function callback
|
||||||
// should really be passed in instead so I'll leave it here as a TODO:
|
// should really be passed in instead so I'll leave it here as a TODO:
|
||||||
var tmpEl = document.implementation.createHTMLDocument()
|
var tmpEl = document.implementation.createHTMLDocument();
|
||||||
|
|
||||||
// a div container is used because swapping the containers
|
// a div container is used because swapping the containers
|
||||||
// will generate a new element, so things get weird
|
// will generate a new element, so things get weird
|
||||||
// using "body" generates a lot of testling cruft that I don't
|
// using "body" generates a lot of testling cruft that I don't
|
||||||
// want so let's avoid that
|
// want so let's avoid that
|
||||||
var container = document.createElement("div")
|
var container = document.createElement("div");
|
||||||
container.innerHTML = "<p>Original Text</p><span>No Change</span>"
|
container.innerHTML = "<p>Original Text</p><span>No Change</span>";
|
||||||
document.body.appendChild(container)
|
document.body.appendChild(container);
|
||||||
|
|
||||||
var container2 = tmpEl.createElement("div")
|
var container2 = tmpEl.createElement("div");
|
||||||
container2.innerHTML = "<p>New Text</p><span>New Span</span>"
|
container2.innerHTML = "<p>New Text</p><span>New Span</span>";
|
||||||
tmpEl.body.appendChild(container2)
|
tmpEl.body.appendChild(container2);
|
||||||
|
|
||||||
switchesSelectors.bind(pjax)(
|
switchesSelectors.bind(pjax)(
|
||||||
{}, // switches
|
{}, // switches
|
||||||
@@ -36,39 +36,49 @@ tape("test switchesSelectors", function(t) {
|
|||||||
tmpEl, // fromEl
|
tmpEl, // fromEl
|
||||||
document, // toEl,
|
document, // toEl,
|
||||||
{} // options
|
{} // options
|
||||||
)
|
);
|
||||||
|
|
||||||
t.equals(container.innerHTML, "<p>New Text</p><span>No Change</span>", "Elements correctly switched")
|
t.equals(
|
||||||
|
container.innerHTML,
|
||||||
|
"<p>New Text</p><span>No Change</span>",
|
||||||
|
"Elements correctly switched"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test switchesSelectors when number of elements don't match", function(t) {
|
tape("test switchesSelectors when number of elements don't match", function(t) {
|
||||||
var newTempDoc = document.implementation.createHTMLDocument()
|
var newTempDoc = document.implementation.createHTMLDocument();
|
||||||
var originalTempDoc = document.implementation.createHTMLDocument()
|
var originalTempDoc = document.implementation.createHTMLDocument();
|
||||||
|
|
||||||
// a div container is used because swapping the containers
|
// a div container is used because swapping the containers
|
||||||
// will generate a new element, so things get weird
|
// will generate a new element, so things get weird
|
||||||
// using "body" generates a lot of testling cruft that I don't
|
// using "body" generates a lot of testling cruft that I don't
|
||||||
// want so let's avoid that
|
// want so let's avoid that
|
||||||
var container = originalTempDoc.createElement("div")
|
var container = originalTempDoc.createElement("div");
|
||||||
container.innerHTML = "<p>Original text</p><span>No change</span>"
|
container.innerHTML = "<p>Original text</p><span>No change</span>";
|
||||||
originalTempDoc.body.appendChild(container)
|
originalTempDoc.body.appendChild(container);
|
||||||
|
|
||||||
var container2 = newTempDoc.createElement("div")
|
var container2 = newTempDoc.createElement("div");
|
||||||
container2.innerHTML = "<p>New text</p><p>More new text</p><span>New span</span>"
|
container2.innerHTML =
|
||||||
newTempDoc.body.appendChild(container2)
|
"<p>New text</p><p>More new text</p><span>New span</span>";
|
||||||
|
newTempDoc.body.appendChild(container2);
|
||||||
|
|
||||||
var switchSelectorsFn = switchesSelectors.bind(pjax,
|
var switchSelectorsFn = switchesSelectors.bind(
|
||||||
|
pjax,
|
||||||
{}, // switches
|
{}, // switches
|
||||||
{}, // switchesOptions
|
{}, // switchesOptions
|
||||||
["p"], // selectors,
|
["p"], // selectors,
|
||||||
newTempDoc, // fromEl
|
newTempDoc, // fromEl
|
||||||
originalTempDoc, // toEl,
|
originalTempDoc, // toEl,
|
||||||
{} // options
|
{} // options
|
||||||
)
|
);
|
||||||
|
|
||||||
t.throws(switchSelectorsFn, null, "error was thrown properly since number of elements don't match")
|
t.throws(
|
||||||
|
switchSelectorsFn,
|
||||||
|
null,
|
||||||
|
"error was thrown properly since number of elements don't match"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,78 +1,94 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
var switches = require("../../lib/switches")
|
var switches = require("../../lib/switches");
|
||||||
var noop = require("../../lib/util/noop")
|
var noop = require("../../lib/util/noop");
|
||||||
|
|
||||||
tape("test outerHTML switch", function(t) {
|
tape("test outerHTML switch", function(t) {
|
||||||
var outerHTML = switches.outerHTML
|
var outerHTML = switches.outerHTML;
|
||||||
|
|
||||||
var doc = document.implementation.createHTMLDocument()
|
var doc = document.implementation.createHTMLDocument();
|
||||||
|
|
||||||
var container = doc.createElement("div")
|
var container = doc.createElement("div");
|
||||||
container.innerHTML = "<p id='p'>Original Text</p>"
|
container.innerHTML = "<p id='p'>Original Text</p>";
|
||||||
doc.body.appendChild(container)
|
doc.body.appendChild(container);
|
||||||
|
|
||||||
var p = doc.createElement("p")
|
var p = doc.createElement("p");
|
||||||
p.innerHTML = "New Text"
|
p.innerHTML = "New Text";
|
||||||
|
|
||||||
outerHTML.bind({
|
outerHTML.bind({
|
||||||
onSwitch: noop
|
onSwitch: noop
|
||||||
})(doc.querySelector("p"), p)
|
})(doc.querySelector("p"), p);
|
||||||
|
|
||||||
t.equals(doc.querySelector("p").innerHTML, "New Text", "Elements correctly switched")
|
t.equals(
|
||||||
t.notEquals(doc.querySelector("p").id, "p", "other attributes overwritten correctly")
|
doc.querySelector("p").innerHTML,
|
||||||
|
"New Text",
|
||||||
|
"Elements correctly switched"
|
||||||
|
);
|
||||||
|
t.notEquals(
|
||||||
|
doc.querySelector("p").id,
|
||||||
|
"p",
|
||||||
|
"other attributes overwritten correctly"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test innerHTML switch", function(t) {
|
tape("test innerHTML switch", function(t) {
|
||||||
var innerHTML = switches.innerHTML
|
var innerHTML = switches.innerHTML;
|
||||||
|
|
||||||
var doc = document.implementation.createHTMLDocument()
|
var doc = document.implementation.createHTMLDocument();
|
||||||
|
|
||||||
var container = doc.createElement("div")
|
var container = doc.createElement("div");
|
||||||
container.innerHTML = "<p id='p'>Original Text</p>"
|
container.innerHTML = "<p id='p'>Original Text</p>";
|
||||||
doc.body.appendChild(container)
|
doc.body.appendChild(container);
|
||||||
|
|
||||||
var p = doc.createElement("p")
|
var p = doc.createElement("p");
|
||||||
p.innerHTML = "New Text"
|
p.innerHTML = "New Text";
|
||||||
p.className = "p"
|
p.className = "p";
|
||||||
|
|
||||||
innerHTML.bind({
|
innerHTML.bind({
|
||||||
onSwitch: noop
|
onSwitch: noop
|
||||||
})(doc.querySelector("p"), p)
|
})(doc.querySelector("p"), p);
|
||||||
|
|
||||||
t.equals(doc.querySelector("p").innerHTML, "New Text", "Elements correctly switched")
|
t.equals(
|
||||||
t.equals(doc.querySelector("p").className, "p", "classname set correctly")
|
doc.querySelector("p").innerHTML,
|
||||||
t.equals(doc.querySelector("p").id, "p", "other attributes set correctly")
|
"New Text",
|
||||||
|
"Elements correctly switched"
|
||||||
|
);
|
||||||
|
t.equals(doc.querySelector("p").className, "p", "classname set correctly");
|
||||||
|
t.equals(doc.querySelector("p").id, "p", "other attributes set correctly");
|
||||||
|
|
||||||
p.removeAttribute("class")
|
p.removeAttribute("class");
|
||||||
|
|
||||||
innerHTML.bind({
|
innerHTML.bind({
|
||||||
onSwitch: noop
|
onSwitch: noop
|
||||||
})(doc.querySelector("p"), p)
|
})(doc.querySelector("p"), p);
|
||||||
|
|
||||||
t.equals(doc.querySelector("p").className, "", "classname set correctly")
|
t.equals(doc.querySelector("p").className, "", "classname set correctly");
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
tape("test replaceNode switch", function(t) {
|
tape("test replaceNode switch", function(t) {
|
||||||
var replaceNode = switches.replaceNode
|
var replaceNode = switches.replaceNode;
|
||||||
|
|
||||||
var doc = document.implementation.createHTMLDocument()
|
var doc = document.implementation.createHTMLDocument();
|
||||||
|
|
||||||
var container = doc.createElement("div")
|
var container = doc.createElement("div");
|
||||||
container.innerHTML = "<p>Original Text</p>"
|
container.innerHTML = "<p>Original Text</p>";
|
||||||
doc.body.appendChild(container)
|
doc.body.appendChild(container);
|
||||||
|
|
||||||
var p = doc.createElement("p")
|
var p = doc.createElement("p");
|
||||||
p.innerHTML = "New Text"
|
p.innerHTML = "New Text";
|
||||||
|
|
||||||
replaceNode.bind({
|
replaceNode.bind({
|
||||||
onSwitch: noop
|
onSwitch: noop
|
||||||
})(doc.querySelector("p"), p)
|
})(doc.querySelector("p"), p);
|
||||||
|
|
||||||
t.equals(doc.querySelector("div").innerHTML, "<p>New Text</p>", "Elements correctly switched")
|
t.equals(
|
||||||
|
doc.querySelector("div").innerHTML,
|
||||||
|
"<p>New Text</p>",
|
||||||
|
"Elements correctly switched"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var uniqueid = require("../../lib/uniqueid.js")
|
var uniqueid = require("../../lib/uniqueid.js");
|
||||||
|
|
||||||
tape("test uniqueid", function(t) {
|
tape("test uniqueid", function(t) {
|
||||||
var a = uniqueid()
|
var a = uniqueid();
|
||||||
var b = uniqueid()
|
var b = uniqueid();
|
||||||
|
|
||||||
t.notEqual(a,b,"Two calls to uniqueid produce different values")
|
t.notEqual(a, b, "Two calls to uniqueid produce different values");
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var clone = require("../../../lib/util/clone")
|
var clone = require("../../../lib/util/clone");
|
||||||
|
|
||||||
tape("test clone method", function(t) {
|
tape("test clone method", function(t) {
|
||||||
var obj = {one: 1, two: 2}
|
var obj = { one: 1, two: 2 };
|
||||||
var cloned = clone(obj)
|
var cloned = clone(obj);
|
||||||
|
|
||||||
t.notEqual(obj, cloned, "cloned object isn't the original object")
|
t.notEqual(obj, cloned, "cloned object isn't the original object");
|
||||||
|
|
||||||
t.same(obj, cloned, "cloned object has the same values as original object")
|
t.same(obj, cloned, "cloned object has the same values as original object");
|
||||||
|
|
||||||
cloned.three = 3
|
cloned.three = 3;
|
||||||
t.notSame(obj, cloned, "modified cloned object doesn't have the same values as original object")
|
t.notSame(
|
||||||
|
obj,
|
||||||
|
cloned,
|
||||||
|
"modified cloned object doesn't have the same values as original object"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,16 +1,25 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var contains = require("../../../lib/util/contains.js")
|
var contains = require("../../../lib/util/contains.js");
|
||||||
|
|
||||||
tape("test contains function", function(t) {
|
tape("test contains function", function(t) {
|
||||||
var tempDoc = document.implementation.createHTMLDocument()
|
var tempDoc = document.implementation.createHTMLDocument();
|
||||||
tempDoc.body.innerHTML = "<div><p id='el' class='js-Pjax'></p></div><span></span>"
|
tempDoc.body.innerHTML =
|
||||||
var selectors = ["div"]
|
"<div><p id='el' class='js-Pjax'></p></div><span></span>";
|
||||||
var el = tempDoc.body.querySelector("#el")
|
var selectors = ["div"];
|
||||||
t.equal(contains(tempDoc, selectors, el), true, "contains() returns true when a selector contains the element")
|
var el = tempDoc.body.querySelector("#el");
|
||||||
|
t.equal(
|
||||||
|
contains(tempDoc, selectors, el),
|
||||||
|
true,
|
||||||
|
"contains() returns true when a selector contains the element"
|
||||||
|
);
|
||||||
|
|
||||||
selectors = ["span"]
|
selectors = ["span"];
|
||||||
t.equal(contains(tempDoc, selectors, el), false, "contains() returns false when the selectors do not contain the element")
|
t.equal(
|
||||||
|
contains(tempDoc, selectors, el),
|
||||||
|
false,
|
||||||
|
"contains() returns false when the selectors do not contain the element"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,17 +1,25 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var extend = require("../../../lib/util/extend")
|
var extend = require("../../../lib/util/extend");
|
||||||
|
|
||||||
tape("test extend method", function(t) {
|
tape("test extend method", function(t) {
|
||||||
var obj = {one: 1, two: 2}
|
var obj = { one: 1, two: 2 };
|
||||||
|
|
||||||
var extended = extend({}, obj, {two: "two", three: 3})
|
var extended = extend({}, obj, { two: "two", three: 3 });
|
||||||
t.notEqual(obj, extended, "extended object isn't the original object")
|
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(
|
||||||
t.notSame(obj.two, extended.two, "extended object value overwrites value from original object")
|
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"
|
||||||
|
);
|
||||||
|
|
||||||
extended = extend(null)
|
extended = extend(null);
|
||||||
t.equals(extended, null, "passing null returns null")
|
t.equals(extended, null, "passing null returns null");
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var noop = require("../../../lib/util/noop")
|
var noop = require("../../../lib/util/noop");
|
||||||
|
|
||||||
tape("test noop function", function(t) {
|
tape("test noop function", function(t) {
|
||||||
t.equal(typeof noop, "function", "noop is a function")
|
t.equal(typeof noop, "function", "noop is a function");
|
||||||
t.equal(noop(), undefined, "noop() returns nothing")
|
t.equal(noop(), undefined, "noop() returns nothing");
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,21 +1,33 @@
|
|||||||
var tape = require("tape")
|
var tape = require("tape");
|
||||||
|
|
||||||
var updateQueryString = require("../../../lib/util/update-query-string")
|
var updateQueryString = require("../../../lib/util/update-query-string");
|
||||||
|
|
||||||
tape("test update query string method", function(t) {
|
tape("test update query string method", function(t) {
|
||||||
var url = "http://example.com"
|
var url = "http://example.com";
|
||||||
var updatedUrl = updateQueryString(url, "foo", "bar")
|
var updatedUrl = updateQueryString(url, "foo", "bar");
|
||||||
|
|
||||||
t.notEqual(url, updatedUrl, "update query string modifies URL")
|
t.notEqual(url, updatedUrl, "update query string modifies URL");
|
||||||
t.equal(updatedUrl, url + "?foo=bar", "update query string creates new query string when no query string params are set")
|
t.equal(
|
||||||
|
updatedUrl,
|
||||||
|
url + "?foo=bar",
|
||||||
|
"update query string creates new query string when no query string params are set"
|
||||||
|
);
|
||||||
|
|
||||||
updatedUrl = updateQueryString(updatedUrl, "foo", "baz")
|
updatedUrl = updateQueryString(updatedUrl, "foo", "baz");
|
||||||
|
|
||||||
t.equal(updatedUrl, url + "?foo=baz", "update query string updates existing query string param")
|
t.equal(
|
||||||
|
updatedUrl,
|
||||||
|
url + "?foo=baz",
|
||||||
|
"update query string updates existing query string param"
|
||||||
|
);
|
||||||
|
|
||||||
updatedUrl = updateQueryString(updatedUrl, "bar", "")
|
updatedUrl = updateQueryString(updatedUrl, "bar", "");
|
||||||
|
|
||||||
t.equal(updatedUrl, url + "?foo=baz&bar=", "update query string appends to existing query string")
|
t.equal(
|
||||||
|
updatedUrl,
|
||||||
|
url + "?foo=baz&bar=",
|
||||||
|
"update query string appends to existing query string"
|
||||||
|
);
|
||||||
|
|
||||||
t.end()
|
t.end();
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
var jsdomOptions = {
|
var jsdomOptions = {
|
||||||
url: "https://example.org/",
|
url: "https://example.org/",
|
||||||
runScripts: "dangerously"
|
runScripts: "dangerously"
|
||||||
}
|
};
|
||||||
|
|
||||||
require("jsdom-global")("", jsdomOptions)
|
require("jsdom-global")("", jsdomOptions);
|
||||||
|
|||||||
Reference in New Issue
Block a user