pjax.js 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261
  1. (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Pjax = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
  2. var executeScripts = require("./lib/execute-scripts");
  3. var forEachEls = require("./lib/foreach-els");
  4. var parseOptions = require("./lib/parse-options");
  5. var switches = require("./lib/switches");
  6. var newUid = require("./lib/uniqueid");
  7. var on = require("./lib/events/on");
  8. var trigger = require("./lib/events/trigger");
  9. var clone = require("./lib/util/clone");
  10. var contains = require("./lib/util/contains");
  11. var extend = require("./lib/util/extend");
  12. var noop = require("./lib/util/noop");
  13. var Pjax = function(options) {
  14. this.state = {
  15. numPendingSwitches: 0,
  16. href: null,
  17. options: null
  18. };
  19. this.options = parseOptions(options);
  20. this.log("Pjax options", this.options);
  21. if (this.options.scrollRestoration && "scrollRestoration" in history) {
  22. history.scrollRestoration = "manual";
  23. }
  24. this.maxUid = this.lastUid = newUid();
  25. this.parseDOM(document);
  26. on(
  27. window,
  28. "popstate",
  29. function(st) {
  30. if (st.state) {
  31. var opt = clone(this.options);
  32. opt.url = st.state.url;
  33. opt.title = st.state.title;
  34. // Since state already exists, prevent it from being pushed again
  35. opt.history = false;
  36. opt.scrollPos = st.state.scrollPos;
  37. if (st.state.uid < this.lastUid) {
  38. opt.backward = true;
  39. } else {
  40. opt.forward = true;
  41. }
  42. this.lastUid = st.state.uid;
  43. // @todo implement history cache here, based on uid
  44. this.loadUrl(st.state.url, opt);
  45. }
  46. }.bind(this)
  47. );
  48. };
  49. Pjax.switches = switches;
  50. Pjax.prototype = {
  51. log: require("./lib/proto/log"),
  52. getElements: function(el) {
  53. return el.querySelectorAll(this.options.elements);
  54. },
  55. parseDOM: function(el) {
  56. var parseElement = require("./lib/proto/parse-element");
  57. forEachEls(this.getElements(el), parseElement, this);
  58. },
  59. refresh: function(el) {
  60. this.parseDOM(el || document);
  61. },
  62. reload: function() {
  63. window.location.reload();
  64. },
  65. attachLink: require("./lib/proto/attach-link"),
  66. attachForm: require("./lib/proto/attach-form"),
  67. forEachSelectors: function(cb, context, DOMcontext) {
  68. return require("./lib/foreach-selectors").bind(this)(
  69. this.options.selectors,
  70. cb,
  71. context,
  72. DOMcontext
  73. );
  74. },
  75. switchSelectors: function(selectors, fromEl, toEl, options) {
  76. return require("./lib/switches-selectors").bind(this)(
  77. this.options.switches,
  78. this.options.switchesOptions,
  79. selectors,
  80. fromEl,
  81. toEl,
  82. options
  83. );
  84. },
  85. latestChance: function(href) {
  86. window.location = href;
  87. },
  88. onSwitch: function() {
  89. trigger(window, "resize scroll");
  90. this.state.numPendingSwitches--;
  91. // debounce calls, so we only run this once after all switches are finished.
  92. if (this.state.numPendingSwitches === 0) {
  93. this.afterAllSwitches();
  94. }
  95. },
  96. loadContent: function(html, options) {
  97. if (typeof html !== "string") {
  98. trigger(document, "pjax:complete pjax:error", options);
  99. return;
  100. }
  101. var tmpEl = document.implementation.createHTMLDocument("pjax");
  102. // parse HTML attributes to copy them
  103. // since we are forced to use documentElement.innerHTML (outerHTML can't be used for <html>)
  104. var htmlRegex = /<html[^>]+>/gi;
  105. var htmlAttribsRegex = /\s?[a-z:]+(?:=['"][^'">]+['"])*/gi;
  106. var matches = html.match(htmlRegex);
  107. if (matches && matches.length) {
  108. matches = matches[0].match(htmlAttribsRegex);
  109. if (matches.length) {
  110. matches.shift();
  111. matches.forEach(function(htmlAttrib) {
  112. var attr = htmlAttrib.trim().split("=");
  113. if (attr.length === 1) {
  114. tmpEl.documentElement.setAttribute(attr[0], true);
  115. } else {
  116. tmpEl.documentElement.setAttribute(attr[0], attr[1].slice(1, -1));
  117. }
  118. });
  119. }
  120. }
  121. tmpEl.documentElement.innerHTML = html;
  122. this.log(
  123. "load content",
  124. tmpEl.documentElement.attributes,
  125. tmpEl.documentElement.innerHTML.length
  126. );
  127. // Clear out any focused controls before inserting new page contents.
  128. if (
  129. document.activeElement &&
  130. contains(document, this.options.selectors, document.activeElement)
  131. ) {
  132. try {
  133. document.activeElement.blur();
  134. } catch (e) {} // eslint-disable-line no-empty
  135. }
  136. this.switchSelectors(this.options.selectors, tmpEl, document, options);
  137. },
  138. abortRequest: require("./lib/abort-request"),
  139. doRequest: require("./lib/send-request"),
  140. handleResponse: require("./lib/proto/handle-response"),
  141. loadUrl: function(href, options) {
  142. options =
  143. typeof options === "object"
  144. ? extend({}, this.options, options)
  145. : clone(this.options);
  146. this.log("load href", href, options);
  147. // Abort any previous request
  148. this.abortRequest(this.request);
  149. trigger(document, "pjax:send", options);
  150. // Do the request
  151. this.request = this.doRequest(
  152. href,
  153. options,
  154. this.handleResponse.bind(this)
  155. );
  156. },
  157. afterAllSwitches: function() {
  158. // FF bug: Won’t autofocus fields that are inserted via JS.
  159. // This behavior is incorrect. So if theres no current focus, autofocus
  160. // the last field.
  161. //
  162. // http://www.w3.org/html/wg/drafts/html/master/forms.html
  163. var autofocusEl = Array.prototype.slice
  164. .call(document.querySelectorAll("[autofocus]"))
  165. .pop();
  166. if (autofocusEl && document.activeElement !== autofocusEl) {
  167. autofocusEl.focus();
  168. }
  169. // execute scripts when DOM have been completely updated
  170. this.options.selectors.forEach(function(selector) {
  171. forEachEls(document.querySelectorAll(selector), function(el) {
  172. executeScripts(el);
  173. });
  174. });
  175. var state = this.state;
  176. if (state.options.history) {
  177. if (!window.history.state) {
  178. this.lastUid = this.maxUid = newUid();
  179. window.history.replaceState(
  180. {
  181. url: window.location.href,
  182. title: document.title,
  183. uid: this.maxUid,
  184. scrollPos: [0, 0]
  185. },
  186. document.title
  187. );
  188. }
  189. // Update browser history
  190. this.lastUid = this.maxUid = newUid();
  191. window.history.pushState(
  192. {
  193. url: state.href,
  194. title: state.options.title,
  195. uid: this.maxUid,
  196. scrollPos: [0, 0]
  197. },
  198. state.options.title,
  199. state.href
  200. );
  201. }
  202. this.forEachSelectors(function(el) {
  203. this.parseDOM(el);
  204. }, this);
  205. // Fire Events
  206. trigger(document, "pjax:complete pjax:success", state.options);
  207. if (typeof state.options.analytics === "function") {
  208. state.options.analytics();
  209. }
  210. if (state.options.history) {
  211. // First parse url and check for hash to override scroll
  212. var a = document.createElement("a");
  213. a.href = this.state.href;
  214. if (a.hash) {
  215. var name = a.hash.slice(1);
  216. name = decodeURIComponent(name);
  217. var curtop = 0;
  218. var target =
  219. document.getElementById(name) || document.getElementsByName(name)[0];
  220. if (target) {
  221. // http://stackoverflow.com/questions/8111094/cross-browser-javascript-function-to-find-actual-position-of-an-element-in-page
  222. if (target.offsetParent) {
  223. do {
  224. curtop += target.offsetTop;
  225. target = target.offsetParent;
  226. } while (target);
  227. }
  228. }
  229. window.scrollTo(0, curtop);
  230. } else if (state.options.scrollTo !== false) {
  231. // Scroll page to top on new page load
  232. if (state.options.scrollTo.length > 1) {
  233. window.scrollTo(state.options.scrollTo[0], state.options.scrollTo[1]);
  234. } else {
  235. window.scrollTo(0, state.options.scrollTo);
  236. }
  237. }
  238. } else if (state.options.scrollRestoration && state.options.scrollPos) {
  239. window.scrollTo(state.options.scrollPos[0], state.options.scrollPos[1]);
  240. }
  241. this.state = {
  242. numPendingSwitches: 0,
  243. href: null,
  244. options: null
  245. };
  246. }
  247. };
  248. Pjax.isSupported = require("./lib/is-supported");
  249. // arguably could do `if( require("./lib/is-supported")()) {` but that might be a little to simple
  250. if (Pjax.isSupported()) {
  251. module.exports = Pjax;
  252. }
  253. // if there isn’t required browser functions, returning stupid api
  254. else {
  255. var stupidPjax = noop;
  256. for (var key in Pjax.prototype) {
  257. if (
  258. Pjax.prototype.hasOwnProperty(key) &&
  259. typeof Pjax.prototype[key] === "function"
  260. ) {
  261. stupidPjax[key] = noop;
  262. }
  263. }
  264. module.exports = stupidPjax;
  265. }
  266. },{"./lib/abort-request":2,"./lib/events/on":4,"./lib/events/trigger":5,"./lib/execute-scripts":6,"./lib/foreach-els":7,"./lib/foreach-selectors":8,"./lib/is-supported":9,"./lib/parse-options":10,"./lib/proto/attach-form":11,"./lib/proto/attach-link":12,"./lib/proto/handle-response":13,"./lib/proto/log":14,"./lib/proto/parse-element":15,"./lib/send-request":16,"./lib/switches":18,"./lib/switches-selectors":17,"./lib/uniqueid":19,"./lib/util/clone":20,"./lib/util/contains":21,"./lib/util/extend":22,"./lib/util/noop":23}],2:[function(require,module,exports){
  267. var noop = require("./util/noop");
  268. module.exports = function(request) {
  269. if (request && request.readyState < 4) {
  270. request.onreadystatechange = noop;
  271. request.abort();
  272. }
  273. };
  274. },{"./util/noop":23}],3:[function(require,module,exports){
  275. module.exports = function(el) {
  276. var code = el.text || el.textContent || el.innerHTML || "";
  277. var src = el.src || "";
  278. var parent =
  279. el.parentNode || document.querySelector("head") || document.documentElement;
  280. var script = document.createElement("script");
  281. if (code.match("document.write")) {
  282. if (console && console.log) {
  283. console.log(
  284. "Script contains document.write. Can’t be executed correctly. Code skipped ",
  285. el
  286. );
  287. }
  288. return false;
  289. }
  290. script.type = "text/javascript";
  291. script.id = el.id;
  292. /* istanbul ignore if */
  293. if (src !== "") {
  294. script.src = src;
  295. script.async = false; // force synchronous loading of peripheral JS
  296. }
  297. if (code !== "") {
  298. try {
  299. script.appendChild(document.createTextNode(code));
  300. } catch (e) {
  301. /* istanbul ignore next */
  302. // old IEs have funky script nodes
  303. script.text = code;
  304. }
  305. }
  306. // execute
  307. parent.appendChild(script);
  308. // avoid pollution only in head or body tags
  309. if (
  310. (parent instanceof HTMLHeadElement || parent instanceof HTMLBodyElement) &&
  311. parent.contains(script)
  312. ) {
  313. parent.removeChild(script);
  314. }
  315. return true;
  316. };
  317. },{}],4:[function(require,module,exports){
  318. var forEachEls = require("../foreach-els");
  319. module.exports = function(els, events, listener, useCapture) {
  320. events = typeof events === "string" ? events.split(" ") : events;
  321. events.forEach(function(e) {
  322. forEachEls(els, function(el) {
  323. el.addEventListener(e, listener, useCapture);
  324. });
  325. });
  326. };
  327. },{"../foreach-els":7}],5:[function(require,module,exports){
  328. var forEachEls = require("../foreach-els");
  329. module.exports = function(els, events, opts) {
  330. events = typeof events === "string" ? events.split(" ") : events;
  331. events.forEach(function(e) {
  332. var event;
  333. event = document.createEvent("HTMLEvents");
  334. event.initEvent(e, true, true);
  335. event.eventName = e;
  336. if (opts) {
  337. Object.keys(opts).forEach(function(key) {
  338. event[key] = opts[key];
  339. });
  340. }
  341. forEachEls(els, function(el) {
  342. var domFix = false;
  343. if (!el.parentNode && el !== document && el !== window) {
  344. // THANK YOU IE (9/10/11)
  345. // dispatchEvent doesn't work if the element is not in the DOM
  346. domFix = true;
  347. document.body.appendChild(el);
  348. }
  349. el.dispatchEvent(event);
  350. if (domFix) {
  351. el.parentNode.removeChild(el);
  352. }
  353. });
  354. });
  355. };
  356. },{"../foreach-els":7}],6:[function(require,module,exports){
  357. var forEachEls = require("./foreach-els");
  358. var evalScript = require("./eval-script");
  359. // Finds and executes scripts (used for newly added elements)
  360. // Needed since innerHTML does not run scripts
  361. module.exports = function(el) {
  362. if (el.tagName.toLowerCase() === "script") {
  363. evalScript(el);
  364. }
  365. forEachEls(el.querySelectorAll("script"), function(script) {
  366. if (!script.type || script.type.toLowerCase() === "text/javascript") {
  367. if (script.parentNode) {
  368. script.parentNode.removeChild(script);
  369. }
  370. evalScript(script);
  371. }
  372. });
  373. };
  374. },{"./eval-script":3,"./foreach-els":7}],7:[function(require,module,exports){
  375. /* global HTMLCollection: true */
  376. module.exports = function(els, fn, context) {
  377. if (
  378. els instanceof HTMLCollection ||
  379. els instanceof NodeList ||
  380. els instanceof Array
  381. ) {
  382. return Array.prototype.forEach.call(els, fn, context);
  383. }
  384. // assume simple DOM element
  385. return fn.call(context, els);
  386. };
  387. },{}],8:[function(require,module,exports){
  388. var forEachEls = require("./foreach-els");
  389. module.exports = function(selectors, cb, context, DOMcontext) {
  390. DOMcontext = DOMcontext || document;
  391. selectors.forEach(function(selector) {
  392. forEachEls(DOMcontext.querySelectorAll(selector), cb, context);
  393. });
  394. };
  395. },{"./foreach-els":7}],9:[function(require,module,exports){
  396. module.exports = function() {
  397. // Borrowed wholesale from https://github.com/defunkt/jquery-pjax
  398. return (
  399. window.history &&
  400. window.history.pushState &&
  401. window.history.replaceState &&
  402. // pushState isn’t reliable on iOS until 5.
  403. !navigator.userAgent.match(
  404. /((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/
  405. )
  406. );
  407. };
  408. },{}],10:[function(require,module,exports){
  409. /* global _gaq: true, ga: true */
  410. var defaultSwitches = require("./switches");
  411. module.exports = function(options) {
  412. options = options || {};
  413. options.elements = options.elements || "a[href], form[action]";
  414. options.selectors = options.selectors || ["title", ".js-Pjax"];
  415. options.switches = options.switches || {};
  416. options.switchesOptions = options.switchesOptions || {};
  417. options.history =
  418. typeof options.history === "undefined" ? true : options.history;
  419. options.analytics =
  420. typeof options.analytics === "function" || options.analytics === false
  421. ? options.analytics
  422. : defaultAnalytics;
  423. options.scrollTo =
  424. typeof options.scrollTo === "undefined" ? 0 : options.scrollTo;
  425. options.scrollRestoration =
  426. typeof options.scrollRestoration !== "undefined"
  427. ? options.scrollRestoration
  428. : true;
  429. options.cacheBust =
  430. typeof options.cacheBust === "undefined" ? true : options.cacheBust;
  431. options.debug = options.debug || false;
  432. options.timeout = options.timeout || 0;
  433. options.currentUrlFullReload =
  434. typeof options.currentUrlFullReload === "undefined"
  435. ? false
  436. : options.currentUrlFullReload;
  437. // We can’t replace body.outerHTML or head.outerHTML.
  438. // It creates a bug where a new body or head are created in the DOM.
  439. // If you set head.outerHTML, a new body tag is appended, so the DOM has 2 body nodes, and vice versa
  440. if (!options.switches.head) {
  441. options.switches.head = defaultSwitches.switchElementsAlt;
  442. }
  443. if (!options.switches.body) {
  444. options.switches.body = defaultSwitches.switchElementsAlt;
  445. }
  446. return options;
  447. };
  448. /* istanbul ignore next */
  449. function defaultAnalytics() {
  450. if (window._gaq) {
  451. _gaq.push(["_trackPageview"]);
  452. }
  453. if (window.ga) {
  454. ga("send", "pageview", { page: location.pathname, title: document.title });
  455. }
  456. }
  457. },{"./switches":18}],11:[function(require,module,exports){
  458. var on = require("../events/on");
  459. var clone = require("../util/clone");
  460. var attrState = "data-pjax-state";
  461. var formAction = function(el, event) {
  462. if (isDefaultPrevented(event)) {
  463. return;
  464. }
  465. // Since loadUrl modifies options and we may add our own modifications below,
  466. // clone it so the changes don't persist
  467. var options = clone(this.options);
  468. // Initialize requestOptions
  469. options.requestOptions = {
  470. requestUrl: el.getAttribute("action") || window.location.href,
  471. requestMethod: el.getAttribute("method") || "GET"
  472. };
  473. // create a testable virtual link of the form action
  474. var virtLinkElement = document.createElement("a");
  475. virtLinkElement.setAttribute("href", options.requestOptions.requestUrl);
  476. var attrValue = checkIfShouldAbort(virtLinkElement, options);
  477. if (attrValue) {
  478. el.setAttribute(attrState, attrValue);
  479. return;
  480. }
  481. event.preventDefault();
  482. if (el.enctype === "multipart/form-data") {
  483. options.requestOptions.formData = new FormData(el);
  484. } else {
  485. options.requestOptions.requestParams = parseFormElements(el);
  486. }
  487. el.setAttribute(attrState, "submit");
  488. options.triggerElement = el;
  489. this.loadUrl(virtLinkElement.href, options);
  490. };
  491. function parseFormElements(el) {
  492. var requestParams = [];
  493. var formElements = el.elements;
  494. for (var i = 0; i < formElements.length; i++) {
  495. var element = formElements[i];
  496. var tagName = element.tagName.toLowerCase();
  497. // jscs:disable disallowImplicitTypeConversion
  498. if (
  499. !!element.name &&
  500. element.attributes !== undefined &&
  501. tagName !== "button"
  502. ) {
  503. // jscs:enable disallowImplicitTypeConversion
  504. var type = element.attributes.type;
  505. if (
  506. !type ||
  507. (type.value !== "checkbox" && type.value !== "radio") ||
  508. element.checked
  509. ) {
  510. // Build array of values to submit
  511. var values = [];
  512. if (tagName === "select") {
  513. var opt;
  514. for (var j = 0; j < element.options.length; j++) {
  515. opt = element.options[j];
  516. if (opt.selected && !opt.disabled) {
  517. values.push(opt.hasAttribute("value") ? opt.value : opt.text);
  518. }
  519. }
  520. } else {
  521. values.push(element.value);
  522. }
  523. for (var k = 0; k < values.length; k++) {
  524. requestParams.push({
  525. name: encodeURIComponent(element.name),
  526. value: encodeURIComponent(values[k])
  527. });
  528. }
  529. }
  530. }
  531. }
  532. return requestParams;
  533. }
  534. function checkIfShouldAbort(virtLinkElement, options) {
  535. // Ignore external links.
  536. if (
  537. virtLinkElement.protocol !== window.location.protocol ||
  538. virtLinkElement.host !== window.location.host
  539. ) {
  540. return "external";
  541. }
  542. // Ignore click if we are on an anchor on the same page
  543. if (
  544. virtLinkElement.hash &&
  545. virtLinkElement.href.replace(virtLinkElement.hash, "") ===
  546. window.location.href.replace(location.hash, "")
  547. ) {
  548. return "anchor";
  549. }
  550. // Ignore empty anchor "foo.html#"
  551. if (virtLinkElement.href === window.location.href.split("#")[0] + "#") {
  552. return "anchor-empty";
  553. }
  554. // if declared as a full reload, just normally submit the form
  555. if (
  556. options.currentUrlFullReload &&
  557. virtLinkElement.href === window.location.href.split("#")[0]
  558. ) {
  559. return "reload";
  560. }
  561. }
  562. var isDefaultPrevented = function(event) {
  563. return event.defaultPrevented || event.returnValue === false;
  564. };
  565. module.exports = function(el) {
  566. var that = this;
  567. el.setAttribute(attrState, "");
  568. on(el, "submit", function(event) {
  569. formAction.call(that, el, event);
  570. });
  571. };
  572. },{"../events/on":4,"../util/clone":20}],12:[function(require,module,exports){
  573. var on = require("../events/on");
  574. var clone = require("../util/clone");
  575. var attrState = "data-pjax-state";
  576. var linkAction = function(el, event) {
  577. if (isDefaultPrevented(event)) {
  578. return;
  579. }
  580. // Since loadUrl modifies options and we may add our own modifications below,
  581. // clone it so the changes don't persist
  582. var options = clone(this.options);
  583. var attrValue = checkIfShouldAbort(el, event);
  584. if (attrValue) {
  585. el.setAttribute(attrState, attrValue);
  586. return;
  587. }
  588. event.preventDefault();
  589. // don’t do "nothing" if user try to reload the page by clicking the same link twice
  590. if (
  591. this.options.currentUrlFullReload &&
  592. el.href === window.location.href.split("#")[0]
  593. ) {
  594. el.setAttribute(attrState, "reload");
  595. this.reload();
  596. return;
  597. }
  598. el.setAttribute(attrState, "load");
  599. options.triggerElement = el;
  600. this.loadUrl(el.href, options);
  601. };
  602. function checkIfShouldAbort(el, event) {
  603. // Don’t break browser special behavior on links (like page in new window)
  604. if (
  605. event.which > 1 ||
  606. event.metaKey ||
  607. event.ctrlKey ||
  608. event.shiftKey ||
  609. event.altKey
  610. ) {
  611. return "modifier";
  612. }
  613. // we do test on href now to prevent unexpected behavior if for some reason
  614. // user have href that can be dynamically updated
  615. // Ignore external links.
  616. if (
  617. el.protocol !== window.location.protocol ||
  618. el.host !== window.location.host
  619. ) {
  620. return "external";
  621. }
  622. // Ignore anchors on the same page (keep native behavior)
  623. if (
  624. el.hash &&
  625. el.href.replace(el.hash, "") ===
  626. window.location.href.replace(location.hash, "")
  627. ) {
  628. return "anchor";
  629. }
  630. // Ignore empty anchor "foo.html#"
  631. if (el.href === window.location.href.split("#")[0] + "#") {
  632. return "anchor-empty";
  633. }
  634. }
  635. var isDefaultPrevented = function(event) {
  636. return event.defaultPrevented || event.returnValue === false;
  637. };
  638. module.exports = function(el) {
  639. var that = this;
  640. el.setAttribute(attrState, "");
  641. on(el, "click", function(event) {
  642. linkAction.call(that, el, event);
  643. });
  644. on(
  645. el,
  646. "keyup",
  647. function(event) {
  648. if (event.keyCode === 13) {
  649. linkAction.call(that, el, event);
  650. }
  651. }.bind(this)
  652. );
  653. };
  654. },{"../events/on":4,"../util/clone":20}],13:[function(require,module,exports){
  655. var clone = require("../util/clone");
  656. var newUid = require("../uniqueid");
  657. var trigger = require("../events/trigger");
  658. module.exports = function(responseText, request, href, options) {
  659. options = clone(options || this.options);
  660. options.request = request;
  661. // Fail if unable to load HTML via AJAX
  662. if (responseText === false) {
  663. trigger(document, "pjax:complete pjax:error", options);
  664. return;
  665. }
  666. // push scroll position to history
  667. var currentState = window.history.state || {};
  668. window.history.replaceState(
  669. {
  670. url: currentState.url || window.location.href,
  671. title: currentState.title || document.title,
  672. uid: currentState.uid || newUid(),
  673. scrollPos: [
  674. document.documentElement.scrollLeft || document.body.scrollLeft,
  675. document.documentElement.scrollTop || document.body.scrollTop
  676. ]
  677. },
  678. document.title,
  679. window.location.href
  680. );
  681. // Check for redirects
  682. var oldHref = href;
  683. if (request.responseURL) {
  684. if (href !== request.responseURL) {
  685. href = request.responseURL;
  686. }
  687. } else if (request.getResponseHeader("X-PJAX-URL")) {
  688. href = request.getResponseHeader("X-PJAX-URL");
  689. } else if (request.getResponseHeader("X-XHR-Redirected-To")) {
  690. href = request.getResponseHeader("X-XHR-Redirected-To");
  691. }
  692. // Add back the hash if it was removed
  693. var a = document.createElement("a");
  694. a.href = oldHref;
  695. var oldHash = a.hash;
  696. a.href = href;
  697. if (oldHash && !a.hash) {
  698. a.hash = oldHash;
  699. href = a.href;
  700. }
  701. this.state.href = href;
  702. this.state.options = options;
  703. try {
  704. this.loadContent(responseText, options);
  705. } catch (e) {
  706. trigger(document, "pjax:error", options);
  707. if (!this.options.debug) {
  708. if (console && console.error) {
  709. console.error("Pjax switch fail: ", e);
  710. }
  711. return this.latestChance(href);
  712. } else {
  713. throw e;
  714. }
  715. }
  716. };
  717. },{"../events/trigger":5,"../uniqueid":19,"../util/clone":20}],14:[function(require,module,exports){
  718. module.exports = function() {
  719. if (this.options.debug && console) {
  720. if (typeof console.log === "function") {
  721. console.log.apply(console, arguments);
  722. }
  723. // IE is weird
  724. else if (console.log) {
  725. console.log(arguments);
  726. }
  727. }
  728. };
  729. },{}],15:[function(require,module,exports){
  730. var attrState = "data-pjax-state";
  731. module.exports = function(el) {
  732. switch (el.tagName.toLowerCase()) {
  733. case "a":
  734. // only attach link if el does not already have link attached
  735. if (!el.hasAttribute(attrState)) {
  736. this.attachLink(el);
  737. }
  738. break;
  739. case "form":
  740. // only attach link if el does not already have link attached
  741. if (!el.hasAttribute(attrState)) {
  742. this.attachForm(el);
  743. }
  744. break;
  745. default:
  746. throw "Pjax can only be applied on <a> or <form> submit";
  747. }
  748. };
  749. },{}],16:[function(require,module,exports){
  750. var updateQueryString = require("./util/update-query-string");
  751. module.exports = function(location, options, callback) {
  752. options = options || {};
  753. var queryString;
  754. var requestOptions = options.requestOptions || {};
  755. var requestMethod = (requestOptions.requestMethod || "GET").toUpperCase();
  756. var requestParams = requestOptions.requestParams || null;
  757. var formData = requestOptions.formData || null;
  758. var requestPayload = null;
  759. var request = new XMLHttpRequest();
  760. var timeout = options.timeout || 0;
  761. request.onreadystatechange = function() {
  762. if (request.readyState === 4) {
  763. if (request.status === 200) {
  764. callback(request.responseText, request, location, options);
  765. } else if (request.status !== 0) {
  766. callback(null, request, location, options);
  767. }
  768. }
  769. };
  770. request.onerror = function(e) {
  771. console.log(e);
  772. callback(null, request, location, options);
  773. };
  774. request.ontimeout = function() {
  775. callback(null, request, location, options);
  776. };
  777. // Prepare the request payload for forms, if available
  778. if (requestParams && requestParams.length) {
  779. // Build query string
  780. queryString = requestParams
  781. .map(function(param) {
  782. return param.name + "=" + param.value;
  783. })
  784. .join("&");
  785. switch (requestMethod) {
  786. case "GET":
  787. // Reset query string to avoid an issue with repeat submissions where checkboxes that were
  788. // previously checked are incorrectly preserved
  789. location = location.split("?")[0];
  790. // Append new query string
  791. location += "?" + queryString;
  792. break;
  793. case "POST":
  794. // Send query string as request payload
  795. requestPayload = queryString;
  796. break;
  797. }
  798. } else if (formData) {
  799. requestPayload = formData;
  800. }
  801. // Add a timestamp as part of the query string if cache busting is enabled
  802. if (options.cacheBust) {
  803. location = updateQueryString(location, "t", Date.now());
  804. }
  805. request.open(requestMethod, location, true);
  806. request.timeout = timeout;
  807. request.setRequestHeader("X-Requested-With", "XMLHttpRequest");
  808. request.setRequestHeader("X-PJAX", "true");
  809. request.setRequestHeader(
  810. "X-PJAX-Selectors",
  811. JSON.stringify(options.selectors)
  812. );
  813. // Send the proper header information for POST forms
  814. if (requestPayload && requestMethod === "POST" && !formData) {
  815. request.setRequestHeader(
  816. "Content-Type",
  817. "application/x-www-form-urlencoded"
  818. );
  819. }
  820. request.send(requestPayload);
  821. return request;
  822. };
  823. },{"./util/update-query-string":24}],17:[function(require,module,exports){
  824. var forEachEls = require("./foreach-els");
  825. var defaultSwitches = require("./switches");
  826. module.exports = function(
  827. switches,
  828. switchesOptions,
  829. selectors,
  830. fromEl,
  831. toEl,
  832. options
  833. ) {
  834. var switchesQueue = [];
  835. selectors.forEach(function(selector) {
  836. var newEls = fromEl.querySelectorAll(selector);
  837. var oldEls = toEl.querySelectorAll(selector);
  838. if (this.log) {
  839. this.log("Pjax switch", selector, newEls, oldEls);
  840. }
  841. if (newEls.length !== oldEls.length) {
  842. throw "DOM doesn’t look the same on new loaded page: ’" +
  843. selector +
  844. "’ - new " +
  845. newEls.length +
  846. ", old " +
  847. oldEls.length;
  848. }
  849. forEachEls(
  850. newEls,
  851. function(newEl, i) {
  852. var oldEl = oldEls[i];
  853. if (this.log) {
  854. this.log("newEl", newEl, "oldEl", oldEl);
  855. }
  856. var callback = switches[selector]
  857. ? switches[selector].bind(
  858. this,
  859. oldEl,
  860. newEl,
  861. options,
  862. switchesOptions[selector]
  863. )
  864. : defaultSwitches.outerHTML.bind(this, oldEl, newEl, options);
  865. switchesQueue.push(callback);
  866. },
  867. this
  868. );
  869. }, this);
  870. this.state.numPendingSwitches = switchesQueue.length;
  871. switchesQueue.forEach(function(queuedSwitch) {
  872. queuedSwitch();
  873. });
  874. };
  875. },{"./foreach-els":7,"./switches":18}],18:[function(require,module,exports){
  876. var on = require("./events/on");
  877. module.exports = {
  878. outerHTML: function(oldEl, newEl) {
  879. oldEl.outerHTML = newEl.outerHTML;
  880. this.onSwitch();
  881. },
  882. innerHTML: function(oldEl, newEl) {
  883. oldEl.innerHTML = newEl.innerHTML;
  884. if (newEl.className === "") {
  885. oldEl.removeAttribute("class");
  886. } else {
  887. oldEl.className = newEl.className;
  888. }
  889. this.onSwitch();
  890. },
  891. switchElementsAlt: function(oldEl, newEl) {
  892. oldEl.innerHTML = newEl.innerHTML;
  893. // Copy attributes from the new element to the old one
  894. if (newEl.hasAttributes()) {
  895. var attrs = newEl.attributes;
  896. for (var i = 0; i < attrs.length; i++) {
  897. oldEl.attributes.setNamedItem(attrs[i].cloneNode());
  898. }
  899. }
  900. this.onSwitch();
  901. },
  902. // Equivalent to outerHTML(), but doesn't require switchElementsAlt() for <head> and <body>
  903. replaceNode: function(oldEl, newEl) {
  904. oldEl.parentNode.replaceChild(newEl, oldEl);
  905. this.onSwitch();
  906. },
  907. sideBySide: function(oldEl, newEl, options, switchOptions) {
  908. var forEach = Array.prototype.forEach;
  909. var elsToRemove = [];
  910. var elsToAdd = [];
  911. var fragToAppend = document.createDocumentFragment();
  912. var animationEventNames =
  913. "animationend webkitAnimationEnd MSAnimationEnd oanimationend";
  914. var animatedElsNumber = 0;
  915. var sexyAnimationEnd = function(e) {
  916. if (e.target !== e.currentTarget) {
  917. // end triggered by an animation on a child
  918. return;
  919. }
  920. animatedElsNumber--;
  921. if (animatedElsNumber <= 0 && elsToRemove) {
  922. elsToRemove.forEach(function(el) {
  923. // browsing quickly can make the el
  924. // already removed by last page update ?
  925. if (el.parentNode) {
  926. el.parentNode.removeChild(el);
  927. }
  928. });
  929. elsToAdd.forEach(function(el) {
  930. el.className = el.className.replace(
  931. el.getAttribute("data-pjax-classes"),
  932. ""
  933. );
  934. el.removeAttribute("data-pjax-classes");
  935. });
  936. elsToAdd = null; // free memory
  937. elsToRemove = null; // free memory
  938. // this is to trigger some repaint (example: picturefill)
  939. this.onSwitch();
  940. }
  941. }.bind(this);
  942. switchOptions = switchOptions || {};
  943. forEach.call(oldEl.childNodes, function(el) {
  944. elsToRemove.push(el);
  945. if (el.classList && !el.classList.contains("js-Pjax-remove")) {
  946. // for fast switch, clean element that just have been added, & not cleaned yet.
  947. if (el.hasAttribute("data-pjax-classes")) {
  948. el.className = el.className.replace(
  949. el.getAttribute("data-pjax-classes"),
  950. ""
  951. );
  952. el.removeAttribute("data-pjax-classes");
  953. }
  954. el.classList.add("js-Pjax-remove");
  955. if (switchOptions.callbacks && switchOptions.callbacks.removeElement) {
  956. switchOptions.callbacks.removeElement(el);
  957. }
  958. if (switchOptions.classNames) {
  959. el.className +=
  960. " " +
  961. switchOptions.classNames.remove +
  962. " " +
  963. (options.backward
  964. ? switchOptions.classNames.backward
  965. : switchOptions.classNames.forward);
  966. }
  967. animatedElsNumber++;
  968. on(el, animationEventNames, sexyAnimationEnd, true);
  969. }
  970. });
  971. forEach.call(newEl.childNodes, function(el) {
  972. if (el.classList) {
  973. var addClasses = "";
  974. if (switchOptions.classNames) {
  975. addClasses =
  976. " js-Pjax-add " +
  977. switchOptions.classNames.add +
  978. " " +
  979. (options.backward
  980. ? switchOptions.classNames.forward
  981. : switchOptions.classNames.backward);
  982. }
  983. if (switchOptions.callbacks && switchOptions.callbacks.addElement) {
  984. switchOptions.callbacks.addElement(el);
  985. }
  986. el.className += addClasses;
  987. el.setAttribute("data-pjax-classes", addClasses);
  988. elsToAdd.push(el);
  989. fragToAppend.appendChild(el);
  990. animatedElsNumber++;
  991. on(el, animationEventNames, sexyAnimationEnd, true);
  992. }
  993. });
  994. // pass all className of the parent
  995. oldEl.className = newEl.className;
  996. oldEl.appendChild(fragToAppend);
  997. }
  998. };
  999. },{"./events/on":4}],19:[function(require,module,exports){
  1000. module.exports = (function() {
  1001. var counter = 0;
  1002. return function() {
  1003. var id = "pjax" + new Date().getTime() + "_" + counter;
  1004. counter++;
  1005. return id;
  1006. };
  1007. })();
  1008. },{}],20:[function(require,module,exports){
  1009. module.exports = function(obj) {
  1010. /* istanbul ignore if */
  1011. if (null === obj || "object" !== typeof obj) {
  1012. return obj;
  1013. }
  1014. var copy = obj.constructor();
  1015. for (var attr in obj) {
  1016. if (obj.hasOwnProperty(attr)) {
  1017. copy[attr] = obj[attr];
  1018. }
  1019. }
  1020. return copy;
  1021. };
  1022. },{}],21:[function(require,module,exports){
  1023. module.exports = function contains(doc, selectors, el) {
  1024. for (var i = 0; i < selectors.length; i++) {
  1025. var selectedEls = doc.querySelectorAll(selectors[i]);
  1026. for (var j = 0; j < selectedEls.length; j++) {
  1027. if (selectedEls[j].contains(el)) {
  1028. return true;
  1029. }
  1030. }
  1031. }
  1032. return false;
  1033. };
  1034. },{}],22:[function(require,module,exports){
  1035. module.exports = function(target) {
  1036. if (target == null) {
  1037. return null;
  1038. }
  1039. var to = Object(target);
  1040. for (var i = 1; i < arguments.length; i++) {
  1041. var source = arguments[i];
  1042. if (source != null) {
  1043. for (var key in source) {
  1044. // Avoid bugs when hasOwnProperty is shadowed
  1045. if (Object.prototype.hasOwnProperty.call(source, key)) {
  1046. to[key] = source[key];
  1047. }
  1048. }
  1049. }
  1050. }
  1051. return to;
  1052. };
  1053. },{}],23:[function(require,module,exports){
  1054. module.exports = function() {};
  1055. },{}],24:[function(require,module,exports){
  1056. module.exports = function(uri, key, value) {
  1057. var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i");
  1058. var separator = uri.indexOf("?") !== -1 ? "&" : "?";
  1059. if (uri.match(re)) {
  1060. return uri.replace(re, "$1" + key + "=" + value + "$2");
  1061. } else {
  1062. return uri + separator + key + "=" + value;
  1063. }
  1064. };
  1065. },{}]},{},[1])(1)
  1066. });