(function(root, factory) { if (typeof exports === "object") { // CommonJS module.exports = factory() } else if (typeof define === "function" && define.amd) { // AMD define([], factory) } else { // Global Variables root.Pjax = factory() } }(this, function() { function newUid() { return (new Date().getTime()) } var Pjax = function(options) { this.firstrun = true this.options = options this.options.elements = this.options.elements || "a[href], form[action]" this.options.selectors = this.options.selectors || ["title", ".js-Pjax"] this.options.switches = this.options.switches || {} this.options.switchesOptions = this.options.switchesOptions || {} this.options.history = this.options.history || true this.options.analytics = this.options.analytics || function(options) { // options.backward or options.foward can be true or undefined // by default, we do track back/foward hit // https://productforums.google.com/forum/#!topic/analytics/WVwMDjLhXYk if (window._gaq) { _gaq.push(["_trackPageview"]) } if (window.ga) { ga("send", "pageview", {"page": options.url, "title": options.title}) } } this.options.scrollTo = this.options.scrollTo || 0 this.options.debug = this.options.debug || false this.maxUid = this.lastUid = newUid() // we can’t replace body.outerHTML or head.outerHTML // it create a bug where new body or new head are created in the dom // if you set head.outerHTML, a new body tag is appended, so the dom get 2 body // & it break the switchFallback which replace head & body if (!this.options.switches.head) { this.options.switches.head = this.switchElementsAlt } if (!this.options.switches.body) { this.options.switches.body = this.switchElementsAlt } this.log("Pjax options", this.options) if (typeof options.analytics !== "function") { options.analytics = function() {} } this.parseDOM(document) Pjax.on(window, "popstate", function(st) { if (st.state) { var opt = Pjax.clone(this.options) opt.url = st.state.url opt.title = st.state.title opt.history = false if (st.state.uid < this.lastUid) { opt.backward = true } else { opt.forward = true } this.lastUid = st.state.uid // @todo implement history cache here, based on uid this.loadUrl(st.state.url, opt) } }.bind(this)) } // make internal methods public Pjax.isSupported = function() { // Borrowed wholesale from https://github.com/defunkt/jquery-pjax return window.history && window.history.pushState && window.history.replaceState && // pushState isn’t reliable on iOS until 5. !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/) } Pjax.forEachEls = function(els, fn, context) { if (els instanceof HTMLCollection || els instanceof NodeList) { return Array.prototype.forEach.call(els, fn, context) } // assume simple dom element fn.call(context, els) } Pjax.on = function(els, events, listener, useCapture) { events = (typeof events === "string" ? events.split(" ") : events) events.forEach(function(e) { Pjax.forEachEls(els, function(el) { el.addEventListener(e, listener, useCapture) }) }, this) } Pjax.off = function(els, events, listener, useCapture) { events = (typeof events === "string" ? events.split(" ") : events) events.forEach(function(e) { Pjax.forEachEls(els, function(el) { el.removeEventListener(e, listener, useCapture) }) }, this) } Pjax.trigger = function(els, events) { events = (typeof events === "string" ? events.split(" ") : events) events.forEach(function(e) { var event if (document.createEvent) { event = document.createEvent("HTMLEvents") event.initEvent(e, true, true) } else { event = document.createEventObject() event.eventType = e } event.eventName = e if (document.createEvent) { Pjax.forEachEls(els, function(el) { el.dispatchEvent(event) }) } else { Pjax.forEachEls(els, function(el) { el.fireEvent("on" + event.eventType, event) }) } }, this) } Pjax.clone = function(obj) { if (null === obj || "object" != typeof obj) { return obj } var copy = obj.constructor() for (var attr in obj) { if (obj.hasOwnProperty(attr)) { copy[attr] = obj[attr] } } return copy } // Finds and executes scripts (used for newly added elements) // Needed since innerHTML does not run scripts Pjax.executeScripts = function(el) { // console.log("going to execute scripts for ", el) Pjax.forEachEls(el.querySelectorAll("script"), function(script) { if (!script.type || script.type.toLowerCase() === "text/javascript") { if (script.parentNode) { script.parentNode.removeChild(script) } Pjax.evalScript(script) } }) } Pjax.evalScript = function(el) { // console.log("going to execute script", el) var code = (el.text || el.textContent || el.innerHTML || "") , head = document.querySelector("head") || document.documentElement , script = document.createElement("script") if (code.match("document.write")) { if (console && console.log) { console.log("Script contains document.write. Can’t be executed correctly. Code skipped ", el) } return false } script.type = "text/javascript" try { script.appendChild(document.createTextNode(code)) } catch (e) { // old IEs have funky script nodes script.text = code } // execute head.insertBefore(script, head.firstChild) head.removeChild(script) // avoid pollution return true } Pjax.prototype = { log: function() { if (this.options.debug && console) { if (typeof console.log === "function") { console.log.apply(console, arguments); } // ie is weird else if (console.log) { console.log(arguments); } } } , getElements: function(el) { return el.querySelectorAll(this.options.elements) } , parseDOM: function(el) { Pjax.forEachEls(this.getElements(el), function(el) { switch (el.tagName.toLowerCase()) { case "a": this.attachLink(el) break case "form": // todo this.log("Pjax doesnt support