From 0f277f7d98b1e1fc6f9f8f88063409cd6f69d30f Mon Sep 17 00:00:00 2001 From: thybag Date: Tue, 18 Mar 2014 22:41:45 +0000 Subject: [PATCH 1/5] Add support for accessing response headers in events see: https://github.com/thybag/PJAX-Standalone/issues/23 --- bower.json | 2 +- package.json | 2 +- pjax-standalone.js | 41 ++++++++++++++++++++++++++++++++++++----- pjax-standalone.min.js | 4 ++-- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/bower.json b/bower.json index e8deefe..1773814 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "thybag/PJAX-Standalone", - "version": "0.6.1", + "version": "0.6.2", "main": "pjax-standalone.min.js", "description": "A standalone implementation of Pushstate AJAX, for non-jquery webpages.", "license": "MIT", diff --git a/package.json b/package.json index 4058905..7f66175 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "Pjax-Standalone", "description": "A standalone implementation of Pushstate AJAX, for non-jquery webpages", - "version": "0.6.1", + "version": "0.6.2", "devDependencies": { "grunt-cli": "~0.1.13", "grunt-contrib-jshint": "~0.8.0", diff --git a/pjax-standalone.js b/pjax-standalone.js index 28fa167..35b8509 100644 --- a/pjax-standalone.js +++ b/pjax-standalone.js @@ -4,7 +4,7 @@ * A standalone implementation of Pushstate AJAX, for non-jQuery web pages. * jQuery are recommended to use the original implementation at: http://github.com/defunkt/jquery-pjax * - * @version 0.6.1 + * @version 0.6.2 * @author Carl * @source https://github.com/thybag/PJAX-Standalone * @license MIT @@ -333,7 +333,10 @@ internal.triggerEvent(options.container, 'beforeSend', options); // Do the request - internal.request(options.url, function(html) { + internal.request(options.url, function(html, headers) { + + // Make response headers available in options + options.headers = headers; // Fail if unable to load HTML via AJAX if(html === false){ @@ -403,11 +406,13 @@ // Add state listener. xmlhttp.onreadystatechange = function() { if ((xmlhttp.readyState === 4) && (xmlhttp.status === 200)) { - // Success, Return HTML - callback(xmlhttp.responseText); + // Get headers in useful format + var headers = internal.parseResponseHeaders(xmlhttp.getAllResponseHeaders()); + // Success, Return HTML & headers + callback(xmlhttp.responseText, headers); }else if((xmlhttp.readyState === 4) && (xmlhttp.status === 404 || xmlhttp.status === 500)){ // error (return false) - callback(false); + callback(false, {}); } }; // Secret pjax ?get param so browser doesn't return pjax content from cache when we don't want it to @@ -420,6 +425,32 @@ xmlhttp.send(null); }; + /** + * ParseResponseHeaders + * Convert response headers to obj + * + * @scope private + * @param raw headers + * @param object of headers + */ + internal.parseResponseHeaders = function(raw){ + // Parsed headers + var parsedHeaders = {}; + + var headers = raw.split("\r\n"); + for(var h in headers){ + var header = headers[h]; + // Ignore blanks + if(header === '') continue; + var idx = header.indexOf(":"); + // before : = name + // after ": " (+2) = data + parsedHeaders[header.substr(0,idx)] = header.substr(idx+2); + } + + return parsedHeaders; + }; + /** * parseOptions * Validate and correct options object while connecting up any listeners. diff --git a/pjax-standalone.min.js b/pjax-standalone.min.js index fe066f3..21eab5a 100644 --- a/pjax-standalone.min.js +++ b/pjax-standalone.min.js @@ -4,9 +4,9 @@ * A standalone implementation of Pushstate AJAX, for non-jQuery web pages. * jQuery are recommended to use the original implementation at: http://github.com/defunkt/jquery-pjax * - * @version 0.6.1 + * @version 0.6.2 * @author Carl * @source https://github.com/thybag/PJAX-Standalone * @license MIT */ -(function(){var internal={firstrun:!0,is_supported:window.history&&window.history.pushState&&window.history.replaceState&&!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/),loaded_scripts:[]};if(!internal.is_supported){var pjax_shell={connect:function(){},invoke:function(){var a=2===arguments.length?arguments[0]:arguments.url;document.location=a}};return void("function"==typeof define&&define.amd?define(function(){return pjax_shell}):window.pjax=pjax_shell)}internal.addEvent=function(a,b,c){a.addEventListener(b,c,!1)},internal.clone=function(a){var b={};for(var c in a)b[c]=a[c];return b},internal.triggerEvent=function(a,b,c){var d=document.createEvent("HTMLEvents");d.initEvent(b,!0,!0),"undefined"!=typeof c&&(d.data=c),a.dispatchEvent(d)},internal.addEvent(window,"popstate",function(a){if(null!==a.state){var b={url:a.state.url,container:a.state.container,title:a.state.title,history:!1};if("undefined"!=typeof internal.options)for(var c in internal.options)"undefined"==typeof b[c]&&(b[c]=internal.options[c]);var d=internal.parseOptions(b);if(d===!1)return;internal.handle(d)}}),internal.attach=function(a,b){if(a.protocol===document.location.protocol&&a.host===document.location.host&&!(a.pathname===location.pathname&&a.hash.length>0)){var c=["pdf","doc","docx","zip","rar","7z","gif","jpeg","jpg","png"];"undefined"==typeof b.ignoreFileTypes&&(b.ignoreFileTypes=c),-1===b.ignoreFileTypes.indexOf(a.pathname.split(".").pop().toLowerCase())&&(b.url=a.href,a.getAttribute("data-pjax")&&(b.container=a.getAttribute("data-pjax")),a.getAttribute("data-title")&&(b.title=a.getAttribute("data-title")),b=internal.parseOptions(b),b!==!1&&internal.addEvent(a,"click",function(a){return a.which>1||a.metaKey||a.ctrlKey?void 0:(a.preventDefault?a.preventDefault():a.returnValue=!1,document.location.href===b.url?!1:void internal.handle(b))}))}},internal.parseLinks=function(a,b){var c;c="undefined"!=typeof b.useClass?a.getElementsByClassName(b.useClass):a.getElementsByTagName("a");for(var d,e=0;e0)){var c=["pdf","doc","docx","zip","rar","7z","gif","jpeg","jpg","png"];"undefined"==typeof b.ignoreFileTypes&&(b.ignoreFileTypes=c),-1===b.ignoreFileTypes.indexOf(a.pathname.split(".").pop().toLowerCase())&&(b.url=a.href,a.getAttribute("data-pjax")&&(b.container=a.getAttribute("data-pjax")),a.getAttribute("data-title")&&(b.title=a.getAttribute("data-title")),b=internal.parseOptions(b),b!==!1&&internal.addEvent(a,"click",function(a){return a.which>1||a.metaKey||a.ctrlKey?void 0:(a.preventDefault?a.preventDefault():a.returnValue=!1,document.location.href===b.url?!1:void internal.handle(b))}))}},internal.parseLinks=function(a,b){var c;c="undefined"!=typeof b.useClass?a.getElementsByClassName(b.useClass):a.getElementsByTagName("a");for(var d,e=0;e Date: Wed, 16 Jul 2014 11:21:06 +0200 Subject: [PATCH 2/5] Update pjax-standalone.js Several bugfixes: - Title was not taken into account in some situation - script dynamic loading/execution was not operating on full page content but on the fragment only --- pjax-standalone.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/pjax-standalone.js b/pjax-standalone.js index 9eb3b53..27d0305 100644 --- a/pjax-standalone.js +++ b/pjax-standalone.js @@ -240,7 +240,8 @@ // Grab the title if there is one var title = html.getElementsByTagName('title')[0]; if(title) - document.title = title.innerHTML; + //ensure this title is not overwritten in the caller function + options.title=document.title = title.innerHTML; // Going by caniuse all browsers that support the pushstate API also support querySelector's // see: http://caniuse.com/#search=push @@ -264,7 +265,8 @@ // Create in memory DOM node, to make parsing returned data easier var tmp = document.createElement('div'); tmp.innerHTML = html; - + //to ensure parseJS will take all of the scripts + var full=tmp; // Ensure we have the correct HTML to apply to our container. if(options.smartLoad) tmp = internal.smartLoad(tmp, options); @@ -275,7 +277,7 @@ // Attempt to grab title from non-smart loaded page contents if(!options.smartLoad){ - var tmpTitle = tmp.getElementsByTagName('title'); + var tmpTitle = full.getElementsByTagName('title'); if(tmpTitle.length !== 0) options.title = tmpTitle[0].innerHTML; } } @@ -284,7 +286,7 @@ options.container.innerHTML = tmp.innerHTML; // Run included JS? - if(options.parseJS) internal.runScripts(tmp); + if(options.parseJS) internal.runScripts(full); // Send data back to handle return options; @@ -305,6 +307,8 @@ var scripts = html.getElementsByTagName('script'); for(var sc=0; sc < scripts.length;sc++) { // If has an src & src isn't in "loaded_scripts", load the script. + if(scripts[sc].src) + { if(scripts[sc].src && internal.loaded_scripts.indexOf(scripts[sc].src) === -1){ // Append to head to include var s = document.createElement("script"); @@ -312,9 +316,13 @@ document.head.appendChild(s); // Add to loaded list internal.loaded_scripts.push(scripts[sc].src); + } }else{ + //avoid non JS ie type="x-tmpl-mustache" + if(scripts[sc].type=="" || scripts[sc].type=="text/javascript") // If raw JS, eval it. - eval(scripts[sc].innerHTML); + try { eval(scripts[sc].innerHTML); } catch(e){ + console.log(e);} } } }; From d5c09409c4d59e3f92101155ef8b6cb10d2d74ed Mon Sep 17 00:00:00 2001 From: James Wragg Date: Fri, 10 Oct 2014 11:55:30 +0100 Subject: [PATCH 3/5] Update pjax-standalone.js I was hoping a reference to the clicked node was available in beforeSend (I wanted to add a class to the link) so added it here. Available in the event passed to beforeSend as e.data.node. --- pjax-standalone.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pjax-standalone.js b/pjax-standalone.js index 9eb3b53..5d9674b 100644 --- a/pjax-standalone.js +++ b/pjax-standalone.js @@ -151,6 +151,9 @@ // Add link HREF to object options.url = node.href; + + // Add link reference to object allowing beforeSend access to clicked node + options.node = node; // If PJAX data is specified, use as container if(node.getAttribute('data-pjax')) { From 3e74c3f6700e21f5e4b5a3cc1c8ca0c0cafd7611 Mon Sep 17 00:00:00 2001 From: Kahlil Lechelt Date: Tue, 13 Jan 2015 21:49:50 +0100 Subject: [PATCH 4/5] add CommonJS support --- pjax-standalone.js | 75 +++++++++++++++++++++--------------------- pjax-standalone.min.js | 4 +-- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/pjax-standalone.js b/pjax-standalone.js index 9eb3b53..f666d9d 100644 --- a/pjax-standalone.js +++ b/pjax-standalone.js @@ -3,13 +3,13 @@ * * A standalone implementation of Pushstate AJAX, for non-jQuery web pages. * jQuery are recommended to use the original implementation at: http://github.com/defunkt/jquery-pjax - * + * * @version 0.6.1 * @author Carl * @source https://github.com/thybag/PJAX-Standalone * @license MIT */ -(function(){ +(function(){ // Object to store private values/methods. var internal = { @@ -21,7 +21,7 @@ // Track which scripts have been included in to the page. (used if e) "loaded_scripts": [] }; - + // If PJAX isn't supported we can skip setting up the library all together // So as not to break any code expecting PJAX to be there, return a shell object containing // IE7 + compatible versions of connect (which needs to do nothing) and invoke ( which just changes the page) @@ -32,14 +32,14 @@ "invoke": function() { var url = (arguments.length === 2) ? arguments[0] : arguments.url; document.location = url; - return; - } + return; + } }; // AMD support - if (typeof define === 'function' && define.amd) { - define( function() { return pjax_shell; }); - } else { - window.pjax = pjax_shell; + if (typeof define === 'function' && define.amd) { + define( function() { return pjax_shell; }); + } else { + window.pjax = pjax_shell; } return; } @@ -98,16 +98,16 @@ internal.addEvent(window, 'popstate', function(st) { if(st.state !== null) { - var opt = { - 'url': st.state.url, - 'container': st.state.container, + var opt = { + 'url': st.state.url, + 'container': st.state.container, 'title' : st.state.title, 'history': false }; // Merge original in original connect options if(typeof internal.options !== 'undefined'){ - for(var a in internal.options){ + for(var a in internal.options){ if(typeof opt[a] === 'undefined') opt[a] = internal.options[a]; } } @@ -126,7 +126,7 @@ * Attach PJAX listeners to a link. * @scope private * @param link_node. link that will be clicked. - * @param content_node. + * @param content_node. */ internal.attach = function(node, options) { @@ -221,7 +221,7 @@ // Fire ready event once all links are connected internal.triggerEvent(internal.get_container_node(options.container), 'ready'); - + } }; @@ -263,7 +263,7 @@ internal.updateContent = function(html, options){ // Create in memory DOM node, to make parsing returned data easier var tmp = document.createElement('div'); - tmp.innerHTML = html; + tmp.innerHTML = html; // Ensure we have the correct HTML to apply to our container. if(options.smartLoad) tmp = internal.smartLoad(tmp, options); @@ -273,7 +273,7 @@ // Use current doc title (this will be updated via smart load if its enabled) options.title = document.title; - // Attempt to grab title from non-smart loaded page contents + // Attempt to grab title from non-smart loaded page contents if(!options.smartLoad){ var tmpTitle = tmp.getElementsByTagName('title'); if(tmpTitle.length !== 0) options.title = tmpTitle[0].innerHTML; @@ -285,7 +285,7 @@ // Run included JS? if(options.parseJS) internal.runScripts(tmp); - + // Send data back to handle return options; }; @@ -307,13 +307,13 @@ // If has an src & src isn't in "loaded_scripts", load the script. if(scripts[sc].src && internal.loaded_scripts.indexOf(scripts[sc].src) === -1){ // Append to head to include - var s = document.createElement("script"); + var s = document.createElement("script"); s.src = scripts[sc].src; document.head.appendChild(s); // Add to loaded list internal.loaded_scripts.push(scripts[sc].src); }else{ - // If raw JS, eval it. + // If raw JS, eval it. eval(scripts[sc].innerHTML); } } @@ -328,7 +328,7 @@ * @param addtohistory. Does this load require a history event. */ internal.handle = function(options) { - + // Fire beforeSend Event. internal.triggerEvent(options.container, 'beforeSend', options); @@ -344,7 +344,7 @@ // Parse page & update DOM options = internal.updateContent(html, options); - + // Do we need to add this to the history? if(options.history) { // If this is the first time pjax has run, create a state object for the current page. @@ -364,10 +364,10 @@ // Fire Events internal.triggerEvent(options.container,'complete', options); internal.triggerEvent(options.container,'success', options); - + // Don't track if page isn't part of history, or if autoAnalytics is disabled if(options.autoAnalytics && options.history) { - // If autoAnalytics is enabled and a Google analytics tracker is detected push + // If autoAnalytics is enabled and a Google analytics tracker is detected push // a trackPageView, so PJAX loaded pages can be tracked successfully. if(window._gaq) _gaq.push(['_trackPageview']); if(window.ga) ga('send', 'pageview', {'page': options.url, 'title': options.title}); @@ -379,7 +379,7 @@ // Scroll page to top on new page load if(options.returnToTop) { window.scrollTo(0, 0); - } + } }); }; @@ -394,11 +394,11 @@ internal.request = function(location, callback) { // Create xmlHttpRequest object. var xmlhttp; - try { - xmlhttp = window.XMLHttpRequest? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); - } catch (e) { + try { + xmlhttp = window.XMLHttpRequest? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); + } catch (e) { console.log("Unable to create XMLHTTP Request"); - return; + return; } // Add state listener. xmlhttp.onreadystatechange = function() { @@ -434,7 +434,7 @@ * * - history: track event to history (on by default, set to off when performing back operation) * - parseLinksOnload: Enabled by default. Process pages loaded via PJAX and setup PJAX on any links found. - * - smartLoad: Tries to ensure the correct HTML is loaded. If you are certain your back end + * - smartLoad: Tries to ensure the correct HTML is loaded. If you are certain your back end * will only return PJAX ready content this can be disabled for a slight performance boost. * - autoAnalytics: Automatically attempt to log events to Google analytics (if tracker is available) * - returnToTop: Scroll user back to top of page, when new page is opened by PJAX @@ -505,7 +505,7 @@ * @scope public * * Can be called in 3 ways. - * Calling as connect(); + * Calling as connect(); * Will look for links with the data-pjax attribute. * * Calling as connect(container_id) @@ -514,7 +514,7 @@ * Calling as connect(container_id, class_name) * Will try to attach any links with the given class name, using container_id as the target. * - * Calling as connect({ + * Calling as connect({ * 'url':'somepage.php', * 'container':'somecontainer', * 'beforeSend': function(){console.log("sending");} @@ -542,26 +542,26 @@ // Delete history and title if provided. These options should only be provided via invoke(); delete options.title; delete options.history; - + internal.options = options; if(document.readyState === 'complete') { internal.parseLinks(document, options); } else { //Don't run until the window is ready. - internal.addEvent(window, 'load', function(){ + internal.addEvent(window, 'load', function(){ //Parse links using specified options internal.parseLinks(document, options); }); } }; - + /** * invoke * Directly invoke a pjax page load. * invoke({url: 'file.php', 'container':'content'}); * * @scope public - * @param options + * @param options */ this.invoke = function(/* options */) { @@ -587,10 +587,11 @@ define( function() { return pjax_obj; }); + }else if(typeof module === "object" && module.exports) { + module.exports = pjax_obj; }else{ // Make PJAX object accessible in global name space window.pjax = pjax_obj; } - }).call({}); diff --git a/pjax-standalone.min.js b/pjax-standalone.min.js index fe066f3..78009a9 100644 --- a/pjax-standalone.min.js +++ b/pjax-standalone.min.js @@ -3,10 +3,10 @@ * * A standalone implementation of Pushstate AJAX, for non-jQuery web pages. * jQuery are recommended to use the original implementation at: http://github.com/defunkt/jquery-pjax - * + * * @version 0.6.1 * @author Carl * @source https://github.com/thybag/PJAX-Standalone * @license MIT */ -(function(){var internal={firstrun:!0,is_supported:window.history&&window.history.pushState&&window.history.replaceState&&!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/),loaded_scripts:[]};if(!internal.is_supported){var pjax_shell={connect:function(){},invoke:function(){var a=2===arguments.length?arguments[0]:arguments.url;document.location=a}};return void("function"==typeof define&&define.amd?define(function(){return pjax_shell}):window.pjax=pjax_shell)}internal.addEvent=function(a,b,c){a.addEventListener(b,c,!1)},internal.clone=function(a){var b={};for(var c in a)b[c]=a[c];return b},internal.triggerEvent=function(a,b,c){var d=document.createEvent("HTMLEvents");d.initEvent(b,!0,!0),"undefined"!=typeof c&&(d.data=c),a.dispatchEvent(d)},internal.addEvent(window,"popstate",function(a){if(null!==a.state){var b={url:a.state.url,container:a.state.container,title:a.state.title,history:!1};if("undefined"!=typeof internal.options)for(var c in internal.options)"undefined"==typeof b[c]&&(b[c]=internal.options[c]);var d=internal.parseOptions(b);if(d===!1)return;internal.handle(d)}}),internal.attach=function(a,b){if(a.protocol===document.location.protocol&&a.host===document.location.host&&!(a.pathname===location.pathname&&a.hash.length>0)){var c=["pdf","doc","docx","zip","rar","7z","gif","jpeg","jpg","png"];"undefined"==typeof b.ignoreFileTypes&&(b.ignoreFileTypes=c),-1===b.ignoreFileTypes.indexOf(a.pathname.split(".").pop().toLowerCase())&&(b.url=a.href,a.getAttribute("data-pjax")&&(b.container=a.getAttribute("data-pjax")),a.getAttribute("data-title")&&(b.title=a.getAttribute("data-title")),b=internal.parseOptions(b),b!==!1&&internal.addEvent(a,"click",function(a){return a.which>1||a.metaKey||a.ctrlKey?void 0:(a.preventDefault?a.preventDefault():a.returnValue=!1,document.location.href===b.url?!1:void internal.handle(b))}))}},internal.parseLinks=function(a,b){var c;c="undefined"!=typeof b.useClass?a.getElementsByClassName(b.useClass):a.getElementsByTagName("a");for(var d,e=0;e0)){var c=["pdf","doc","docx","zip","rar","7z","gif","jpeg","jpg","png"];"undefined"==typeof b.ignoreFileTypes&&(b.ignoreFileTypes=c),-1===b.ignoreFileTypes.indexOf(a.pathname.split(".").pop().toLowerCase())&&(b.url=a.href,a.getAttribute("data-pjax")&&(b.container=a.getAttribute("data-pjax")),a.getAttribute("data-title")&&(b.title=a.getAttribute("data-title")),b=internal.parseOptions(b),b!==!1&&internal.addEvent(a,"click",function(a){return a.which>1||a.metaKey||a.ctrlKey?void 0:(a.preventDefault?a.preventDefault():a.returnValue=!1,document.location.href===b.url?!1:void internal.handle(b))}))}},internal.parseLinks=function(a,b){var c;c="undefined"!=typeof b.useClass?a.getElementsByClassName(b.useClass):a.getElementsByTagName("a");for(var d,e=0;e Date: Mon, 30 Mar 2015 20:30:23 +0100 Subject: [PATCH 5/5] Make versions consistent --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 1773814..a0e7bf0 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "thybag/PJAX-Standalone", - "version": "0.6.2", + "version": "0.6.3", "main": "pjax-standalone.min.js", "description": "A standalone implementation of Pushstate AJAX, for non-jquery webpages.", "license": "MIT", diff --git a/package.json b/package.json index 7f66175..1283067 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "Pjax-Standalone", "description": "A standalone implementation of Pushstate AJAX, for non-jquery webpages", - "version": "0.6.2", + "version": "0.6.3", "devDependencies": { "grunt-cli": "~0.1.13", "grunt-contrib-jshint": "~0.8.0",