From 0a560913537804ec067c6b61d805b373e1a9ac34 Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Tue, 2 Jul 2013 10:37:48 +0100 Subject: [PATCH 01/23] replace title cross-browser on ajax update --- jquery.djax.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/jquery.djax.js b/jquery.djax.js index 6529c15..c759460 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -134,7 +134,10 @@ } // Set page title as new page title - $('title').text($(result).filter('title').text()); + // Set title cross-browser: + // - $('title').text(title_text); returns an error on IE7 + // + document.title = $(result).filter('title').text(); // Loop through each block and find new page equivalent blocks.each(function () { From 95659ab56a920a08763e0a5bd6de52abac19b56a Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Tue, 2 Jul 2013 10:39:53 +0100 Subject: [PATCH 02/23] trigger event once all markup is replaced --- jquery.djax.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/jquery.djax.js b/jquery.djax.js index c759460..3c8e51b 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -209,6 +209,16 @@ self.triggered = true; self.djaxing = false; } + + // Trigger a djaxLoaded event when done + $(window).trigger( + 'djaxLoaded', + [{ + 'url' : url, + 'title' : $(result).filter('title').text(), + 'response' : response + }] + ); }; $.get(url, function (response) { replaceBlocks(response); From f54cf00971d6d83620ec7a38844903f18b0fd365 Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Tue, 2 Jul 2013 10:44:30 +0100 Subject: [PATCH 03/23] use $.ajax call instead of $.get call --- jquery.djax.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/jquery.djax.js b/jquery.djax.js index 3c8e51b..b815f15 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -220,12 +220,17 @@ }] ); }; - $.get(url, function (response) { - replaceBlocks(response); - }).error(function (response) { - // handle error - console.log('error', response); - replaceBlocks(response['responseText']); + + $.ajax({ + 'url' : url, + 'success' : function (response) { + replaceBlocks(response); + }, + 'error' : function (response, textStatus, errorThrown) { + // handle error + console.log('error', response, textStatus, errorThrown); + replaceBlocks(response['responseText']); + } }); }; /* End self.navigate */ From 58f03a0c823f05f555c7b582897f5b6659cb544f Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Tue, 2 Jul 2013 10:46:35 +0100 Subject: [PATCH 04/23] append new block to previous block if no parent 1) in case no previous sibling of a certain *new* block is found, djax used to prepend this block within its parent, and this behaviour is preserved. 2) however, in case a new block has no parent (or no id is specified for the parent enclosing a certain set of blocks) the new block is inserted after the previous block. The case 2) is triggered with a response like:
...
...
...
(note that the enclosing block has no id). --- jquery.djax.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/jquery.djax.js b/jquery.djax.js index b815f15..6d02683 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -64,7 +64,6 @@ // Exclude the link exceptions self.attachClick = function (element, event) { - var link = $(element), exception = false; @@ -163,6 +162,8 @@ }); // Loop through new page blocks and add in as needed + var $previousBlock; + $.each(newBlocks, function () { var newBlock = $(this), @@ -181,11 +182,20 @@ newBlock.insertAfter('#' + $previousSibling.attr('id')); } else { // There's no previous sibling, so prepend to parent instead - newBlock.prependTo('#' + newBlock.parent().attr('id')); + var parent_id = newBlock.parent().attr('id'); + if (parent_id === undefined && $previousBlock !== undefined) { + newBlock.insertAfter('#' + $previousBlock.attr('id')); + } + else { + newBlock.prependTo('#' + parent_id); + } } } - // Only add a class to internal links + // Keep the previous block + $previousBlock = newBlock; + + // Only add a class to internal links $('a', newBlock).filter(function () { return this.hostname === location.hostname; }).addClass('dJAX_internal').on('click', function (event) { @@ -251,5 +261,4 @@ }); }; - -}(jQuery, window)); \ No newline at end of file +}(jQuery, window)); From 5f514317f1c23f71bda4be34f54d3d0e1c98cdbf Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Tue, 2 Jul 2013 11:38:48 +0100 Subject: [PATCH 05/23] namespace for click events --- jquery.djax.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jquery.djax.js b/jquery.djax.js index 6d02683..24fef34 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -147,7 +147,7 @@ $('a', newBlock).filter(function () { return this.hostname === location.hostname; - }).addClass('dJAX_internal').on('click', function (event) { + }).addClass('dJAX_internal').on('click.djax', function (event) { return self.attachClick(this, event); }); @@ -198,7 +198,7 @@ // Only add a class to internal links $('a', newBlock).filter(function () { return this.hostname === location.hostname; - }).addClass('dJAX_internal').on('click', function (event) { + }).addClass('dJAX_internal').on('click.djax', function (event) { return self.attachClick(this, event); }); @@ -247,7 +247,7 @@ // Only add a class to internal links $(this).find('a').filter(function () { return this.hostname === location.hostname; - }).addClass('dJAX_internal').on('click', function (event) { + }).addClass('dJAX_internal').on('click.djax', function (event) { return self.attachClick(this, event); }); From 53871aa9a3e2c223003acd158ec8a709944ffaa9 Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Tue, 2 Jul 2013 14:15:52 +0100 Subject: [PATCH 06/23] code refactoring - public method to trigger a djax update programmatically - make code more standard as explained in http://learn.jquery.com/plugins/basic-plugin-creation/ --- example.js | 9 +- jquery.djax.js | 419 ++++++++++++++++++++++++++++--------------------- 2 files changed, 244 insertions(+), 184 deletions(-) diff --git a/example.js b/example.js index ae0e1bb..62dd9ee 100644 --- a/example.js +++ b/example.js @@ -24,8 +24,13 @@ jQuery('document').ready(function($) { } - $('body').djax('.one-third', [], transition); + $('body').djax({ + 'selector' : '.one-third', + 'exceptions' : [], + 'replaceBlockFunction' : transition + }); + $(window).bind('djaxLoad', function(e, params) { console.log($('
'+params.response+'
')); }) -}); \ No newline at end of file +}); diff --git a/jquery.djax.js b/jquery.djax.js index 24fef34..13d6b82 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -34,38 +34,14 @@ (function ($, exports) { 'use strict'; + + var _methods = { + 'attachClick' : function (element, event) { + var $this = this; - $.fn.djax = function (selector, exceptions, replaceBlockWithFunc) { - - // If browser doesn't support pushState, abort now - if (!history.pushState) { - return $(this); - } - - var self = this, - blockSelector = selector, - excludes = (exceptions && exceptions.length) ? exceptions : [], - replaceBlockWith = (replaceBlockWithFunc) ? replaceBlockWithFunc : $.fn.replaceWith, - djaxing = false; - - // Ensure that the history is correct when going from 2nd page to 1st - window.history.replaceState( - { - 'url' : window.location.href, - 'title' : $('title').text() - }, - $('title').text(), - window.location.href - ); - - self.clearDjaxing = function() { - self.djaxing = false; - } - - // Exclude the link exceptions - self.attachClick = function (element, event) { var link = $(element), - exception = false; + exception = false, + excludes = $this.data('djaxUserExcludes'); $.each(excludes, function (index, exclusion) { if (link.attr('href').indexOf(exclusion) !== -1) { @@ -86,23 +62,212 @@ event.preventDefault(); // If we're already doing djaxing, return now and silently fail - if (self.djaxing) { - setTimeout(self.clearDjaxing, 1000); + if ($this.djaxing) { + setTimeout($this.clearDjaxing, 1000); return $(element); } $(window).trigger('djaxClick', [element]); - self.reqUrl = link.attr('href'); - self.triggered = false; - self.navigate(link.attr('href'), true); - }; - - // Handle the navigation - self.navigate = function (url, add) { - + $this.reqUrl = link.attr('href'); + $this.triggered = false; + methods.navigate.call($this, link.attr('href'), true); + }, + 'replaceBlocks' : function (url, add, blocks, response) { + var $this = this; + + var blockSelector = $this.data('djaxBlockSelector'); + var replaceBlockWithFunc = $this.data('djaxReplaceBlockWith'); + + if (url !== $this.reqUrl) { + methods.navigate.call($this, $this.reqUrl, false); + return true; + } + + var result = $(response), + newBlocks = $(result).find(blockSelector); + + if (add) { + window.history.pushState( + { + 'url' : url, + 'title' : $(result).filter('title').text() + }, + $(result).filter('title').text(), + url + ); + } + + // Set page title as new page title + // Set title cross-browser: + // - $('title').text(title_text); returns an error on IE7 + // + document.title = $(result).filter('title').text(); + + // Loop through each block and find new page equivalent + blocks.each(function () { + + var id = '#' + $(this).attr('id'), + newBlock = newBlocks.filter(id), + block = $(this); + + $('a', newBlock).filter(function () { + return this.hostname === location.hostname; + }).addClass('dJAX_internal').on('click.djax', function (event) { + _methods.attachClick.call($this, this, event); + }); + + if (newBlock.length) { + if (block.html() !== newBlock.html()) { + replaceBlockWithFunc.call(block, newBlock); + } + } else { + block.remove(); + } + + }); + + // Loop through new page blocks and add in as needed + var $previousBlock; + + $.each(newBlocks, function () { + + var newBlock = $(this), + id = '#' + $(this).attr('id'), + $previousSibling; + + // If there is a new page block without an equivalent block + // in the old page, we need to find out where to insert it + if (!$(id).length) { + + // Find the previous sibling + $previousSibling = $(result).find(id).prev(); + + if ($previousSibling.length) { + // Insert after the previous element + newBlock.insertAfter('#' + $previousSibling.attr('id')); + } else { + // There's no previous sibling, so prepend to parent instead + var parent_id = newBlock.parent().attr('id'); + if (parent_id === undefined && $previousBlock !== undefined) { + newBlock.insertAfter('#' + $previousBlock.attr('id')); + } + else { + newBlock.prependTo('#' + parent_id); + } + } + } + + // Keep the previous block + $previousBlock = newBlock; + + // Only add a class to internal links + $('a', newBlock).filter(function () { + return this.hostname === location.hostname; + }).addClass('dJAX_internal').on('click.djax', function (event) { + return _methods.attachClick.call($this, this, event); + }); + + }); + + + + // Trigger djaxLoad event as a pseudo ready() + if (!$this.triggered) { + $(window).trigger( + 'djaxLoad', + [{ + 'url' : url, + 'title' : $(result).filter('title').text(), + 'response' : response + }] + ); + $this.triggered = true; + $this.djaxing = false; + } + + // Trigger a djaxLoaded event when done + $(window).trigger( + 'djaxLoaded', + [{ + 'url' : url, + 'title' : $(result).filter('title').text(), + 'response' : response + }] + ); + } + }; + + var methods = { + 'init' : function (options) { + + var settings = $.extend({ + 'selector' : undefined, + 'exceptions' : [], + 'replaceBlockFunction' : undefined + }, options); + + return this.each(function() { + var $this = $(this); + + // If browser doesn't support pushState, abort now + if (!history.pushState) { + return $(this); + } + + var excludes = settings.exceptions, + replaceBlockWith = (settings.replaceBlockFunction) ? settings.replaceBlockFunction: $.fn.replaceWith, + djaxing = false; + + var blockSelector = settings.selector; + + // Save block selector internally so that we can use it in later calls + $this.data('djaxBlockSelector', blockSelector); + + // Save the replaceBlockWith function internally too... + $this.data('djaxReplaceBlockWith', replaceBlockWith); + + // Save excludes so that we can use them later... + $this.data('djaxUserExcludes', excludes); + + // Ensure that the history is correct when going from 2nd page to 1st + window.history.replaceState( + { + 'url' : window.location.href, + 'title' : $('title').text() + }, + $('title').text(), + window.location.href + ); + + $this.clearDjaxing = function() { + $this.djaxing = false; + } + + // Exclude the link exceptions + // Only add a class to internal links + $this.find('a').filter(function () { + return this.hostname === location.hostname; + }).addClass('dJAX_internal').on('click.djax', function (event) { + return _methods.attachClick.call($this, this, event); + }); + + // On new page load + $(window).bind('popstate', function (event) { + $this.triggered = false; + if (event.originalEvent.state) { + $this.reqUrl = event.originalEvent.state.url; + methods.navigate.call($this, event.originalEvent.state.url, false); + } + }); + }); + }, + 'navigate' : function (url, add) { + var $this = this; + + var blockSelector = $this.data('djaxBlockSelector'); var blocks = $(blockSelector); - self.djaxing = true; + $this.djaxing = true; // Get the new page $(window).trigger( @@ -111,154 +276,44 @@ 'url' : url }] ); - - var replaceBlocks = function (response) { - if (url !== self.reqUrl) { - self.navigate(self.reqUrl, false); - return true; - } - - var result = $(response), - newBlocks = $(result).find(blockSelector); - - if (add) { - window.history.pushState( - { - 'url' : url, - 'title' : $(result).filter('title').text() - }, - $(result).filter('title').text(), - url - ); - } - - // Set page title as new page title - // Set title cross-browser: - // - $('title').text(title_text); returns an error on IE7 - // - document.title = $(result).filter('title').text(); - - // Loop through each block and find new page equivalent - blocks.each(function () { - - var id = '#' + $(this).attr('id'), - newBlock = newBlocks.filter(id), - block = $(this); - - $('a', newBlock).filter(function () { - return this.hostname === location.hostname; - }).addClass('dJAX_internal').on('click.djax', function (event) { - return self.attachClick(this, event); - }); - - if (newBlock.length) { - if (block.html() !== newBlock.html()) { - replaceBlockWith.call(block, newBlock); - } - } else { - block.remove(); - } - - }); - - // Loop through new page blocks and add in as needed - var $previousBlock; - - $.each(newBlocks, function () { - - var newBlock = $(this), - id = '#' + $(this).attr('id'), - $previousSibling; - - // If there is a new page block without an equivalent block - // in the old page, we need to find out where to insert it - if (!$(id).length) { - - // Find the previous sibling - $previousSibling = $(result).find(id).prev(); - - if ($previousSibling.length) { - // Insert after the previous element - newBlock.insertAfter('#' + $previousSibling.attr('id')); - } else { - // There's no previous sibling, so prepend to parent instead - var parent_id = newBlock.parent().attr('id'); - if (parent_id === undefined && $previousBlock !== undefined) { - newBlock.insertAfter('#' + $previousBlock.attr('id')); - } - else { - newBlock.prependTo('#' + parent_id); - } - } - } - - // Keep the previous block - $previousBlock = newBlock; - - // Only add a class to internal links - $('a', newBlock).filter(function () { - return this.hostname === location.hostname; - }).addClass('dJAX_internal').on('click.djax', function (event) { - return self.attachClick(this, event); - }); - - }); - - - - // Trigger djaxLoad event as a pseudo ready() - if (!self.triggered) { - $(window).trigger( - 'djaxLoad', - [{ - 'url' : url, - 'title' : $(result).filter('title').text(), - 'response' : response - }] - ); - self.triggered = true; - self.djaxing = false; - } - - // Trigger a djaxLoaded event when done - $(window).trigger( - 'djaxLoaded', - [{ - 'url' : url, - 'title' : $(result).filter('title').text(), - 'response' : response - }] - ); - }; - + $.ajax({ 'url' : url, 'success' : function (response) { - replaceBlocks(response); + _methods.replaceBlocks.call($this, url, add, blocks, response); }, 'error' : function (response, textStatus, errorThrown) { // handle error - console.log('error', response, textStatus, errorThrown); - replaceBlocks(response['responseText']); + // console.log('error', response, textStatus, errorThrown); + + // still replace blocks as we may end up here if the + // correct content type is not set by the webserver - + // (e.g., with content type set to application/json an + // error may be returned) + _methods.replaceBlocks.call($this, url, add, blocks, response['responseText']); } }); - }; /* End self.navigate */ - - // Only add a class to internal links - $(this).find('a').filter(function () { - return this.hostname === location.hostname; - }).addClass('dJAX_internal').on('click.djax', function (event) { - return self.attachClick(this, event); - }); - - // On new page load - $(window).bind('popstate', function (event) { - self.triggered = false; - if (event.originalEvent.state) { - self.reqUrl = event.originalEvent.state.url; - self.navigate(event.originalEvent.state.url, false); - } - }); + } /* End self.navigate */ + }; + + $.fn.djax = function(method) { + /* + * Just a router for method calls + */ + if (methods[method]) { + // call a method + return methods[method].apply(this, + Array.prototype.slice.call(arguments, 1) + ); + } + else if (typeof method == 'object' || !method) { + // call init, user passed the settings as parameters + return methods.init.apply(this, arguments); + } + else { + $.error('Cannot call method ' + method); + } + + }; - }; }(jQuery, window)); From f723ccb33d147737b840f7d4c4bc22d042673896 Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Tue, 2 Jul 2013 14:31:19 +0100 Subject: [PATCH 07/23] update documentation --- readme.md | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/readme.md b/readme.md index 8897db5..3938c3b 100644 --- a/readme.md +++ b/readme.md @@ -13,7 +13,10 @@ Then instantiate on the largest page element where you will have updating conten @@ -21,7 +24,7 @@ Congrats, you're done! Well mostly... ##Markup -djax will track elements with the class you pass it as a first argument. In the above example I've passed the class 'updatable,' so my markup would look something like this: +djax will track elements with the class you pass it as the 'selector' argument. In the above example I've passed the class 'updatable,' so my markup would look something like this:
@@ -55,28 +58,33 @@ the parent element (by ID.) ##Parameters -The plugin accepts only two parameters, and only one is required. +The plugin accepts only two parameters: 'selector' and 'exceptions'. Only 'selector' is required. ###Tracking Class -The first and only required parameter is the class you will use to identify trackable elements. If my code looks like the below sample, every dynamic element in my markup should have a class +The 'selector' parameter is the only required parameter and is the class you will use to identify trackable elements. If my code looks like the below sample, every dynamic element in my markup should have a class of djaxable ###Exceptions -By default djax works on any internal links, but sometimes you may want to exclude certain URLs on your site. The second parameter allows you to pass an array of URL fragments to exclude from djax +By default djax works on any internal links, but sometimes you may want to exclude certain URLs on your site. The 'exceptions' parameter allows you to pass an array of URL fragments to exclude from djax loading. This is performed with a simple Javascript 'indexOf,' so the more of the URL you provide, the more specifically your exclusions will be matched. The below example will djax any internal links that do not contain admin, resources, or ?s= in the url. @@ -97,7 +105,11 @@ the page. The following example fades out the old content, and fades in the new }); } jQuery(document).ready(function($) { - $('body').djax('.djaxable', [], transition); + $('body').djax( + 'selector' : '.djaxable', + 'exceptions' : [], + 'replaceBlockFunction' : transition + ); }); From 8e60ddfdaed5fe732c90fbad29a2dc51c8ecea49 Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Tue, 2 Jul 2013 14:46:51 +0100 Subject: [PATCH 08/23] rename variable for readability --- jquery.djax.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jquery.djax.js b/jquery.djax.js index 13d6b82..e3ec58d 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -261,7 +261,7 @@ }); }); }, - 'navigate' : function (url, add) { + 'navigate' : function (url, add_to_history) { var $this = this; var blockSelector = $this.data('djaxBlockSelector'); @@ -280,7 +280,7 @@ $.ajax({ 'url' : url, 'success' : function (response) { - _methods.replaceBlocks.call($this, url, add, blocks, response); + _methods.replaceBlocks.call($this, url, add_to_history, blocks, response); }, 'error' : function (response, textStatus, errorThrown) { // handle error @@ -290,7 +290,7 @@ // correct content type is not set by the webserver - // (e.g., with content type set to application/json an // error may be returned) - _methods.replaceBlocks.call($this, url, add, blocks, response['responseText']); + _methods.replaceBlocks.call($this, url, add_to_history, blocks, response['responseText']); } }); } /* End self.navigate */ From 605117d522526a903468f2dde0aba18441126551 Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Tue, 2 Jul 2013 19:14:34 +0100 Subject: [PATCH 09/23] add public navigate method --- jquery.djax.js | 72 ++++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/jquery.djax.js b/jquery.djax.js index e3ec58d..c2836fb 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -36,6 +36,38 @@ 'use strict'; var _methods = { + 'navigate' : function (url, add_to_history) { + var $this = this; + + var blockSelector = $this.data('djaxBlockSelector'); + var blocks = $(blockSelector); + + $this.djaxing = true; + + // Get the new page + $(window).trigger( + 'djaxLoading', [{ + 'url' : url + }] + ); + + $.ajax({ + 'url' : url, + 'success' : function (response) { + _methods.replaceBlocks.call($this, url, add_to_history, blocks, response); + }, + 'error' : function (response, textStatus, errorThrown) { + // handle error + // console.log('error', response, textStatus, errorThrown); + + // still replace blocks as we may end up here if the + // correct content type is not set by the webserver - + // (e.g., with content type set to application/json an + // error may be returned) + _methods.replaceBlocks.call($this, url, add_to_history, blocks, response['responseText']); + } + }); + }, 'attachClick' : function (element, event) { var $this = this; @@ -70,7 +102,7 @@ $(window).trigger('djaxClick', [element]); $this.reqUrl = link.attr('href'); $this.triggered = false; - methods.navigate.call($this, link.attr('href'), true); + _methods.navigate.call($this, link.attr('href'), true); }, 'replaceBlocks' : function (url, add, blocks, response) { var $this = this; @@ -79,7 +111,7 @@ var replaceBlockWithFunc = $this.data('djaxReplaceBlockWith'); if (url !== $this.reqUrl) { - methods.navigate.call($this, $this.reqUrl, false); + _methods.navigate.call($this, $this.reqUrl, false); return true; } @@ -256,44 +288,16 @@ $this.triggered = false; if (event.originalEvent.state) { $this.reqUrl = event.originalEvent.state.url; - methods.navigate.call($this, event.originalEvent.state.url, false); + _methods.navigate.call($this, event.originalEvent.state.url, false); } }); }); }, 'navigate' : function (url, add_to_history) { var $this = this; - - var blockSelector = $this.data('djaxBlockSelector'); - var blocks = $(blockSelector); - - $this.djaxing = true; - - // Get the new page - $(window).trigger( - 'djaxLoading', - [{ - 'url' : url - }] - ); - - $.ajax({ - 'url' : url, - 'success' : function (response) { - _methods.replaceBlocks.call($this, url, add_to_history, blocks, response); - }, - 'error' : function (response, textStatus, errorThrown) { - // handle error - // console.log('error', response, textStatus, errorThrown); - - // still replace blocks as we may end up here if the - // correct content type is not set by the webserver - - // (e.g., with content type set to application/json an - // error may be returned) - _methods.replaceBlocks.call($this, url, add_to_history, blocks, response['responseText']); - } - }); - } /* End self.navigate */ + $this.reqUrl = url; + _methods.navigate.call($this, url, add_to_history); + } }; $.fn.djax = function(method) { From 72d40fcbbbe84a20bcfd2b03d0a294b4bb17cc04 Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Thu, 4 Jul 2013 18:00:48 +0100 Subject: [PATCH 10/23] store djaxing status in globally --- jquery.djax.js | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/jquery.djax.js b/jquery.djax.js index c2836fb..481d513 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -34,15 +34,22 @@ (function ($, exports) { 'use strict'; + + var djaxing = false; + var reqUrl; + var triggered; var _methods = { + 'clearDjaxing' : function () { + djaxing = false; + }, 'navigate' : function (url, add_to_history) { var $this = this; var blockSelector = $this.data('djaxBlockSelector'); var blocks = $(blockSelector); - $this.djaxing = true; + djaxing = true; // Get the new page $(window).trigger( @@ -94,14 +101,14 @@ event.preventDefault(); // If we're already doing djaxing, return now and silently fail - if ($this.djaxing) { - setTimeout($this.clearDjaxing, 1000); + if (djaxing) { + setTimeout(_methods.clearDjaxing, 1000); return $(element); } $(window).trigger('djaxClick', [element]); - $this.reqUrl = link.attr('href'); - $this.triggered = false; + reqUrl = link.attr('href'); + triggered = false; _methods.navigate.call($this, link.attr('href'), true); }, 'replaceBlocks' : function (url, add, blocks, response) { @@ -110,8 +117,8 @@ var blockSelector = $this.data('djaxBlockSelector'); var replaceBlockWithFunc = $this.data('djaxReplaceBlockWith'); - if (url !== $this.reqUrl) { - _methods.navigate.call($this, $this.reqUrl, false); + if (url !== reqUrl) { + _methods.navigate.call($this, reqUrl, false); return true; } @@ -204,7 +211,7 @@ // Trigger djaxLoad event as a pseudo ready() - if (!$this.triggered) { + if (!triggered) { $(window).trigger( 'djaxLoad', [{ @@ -213,8 +220,8 @@ 'response' : response }] ); - $this.triggered = true; - $this.djaxing = false; + triggered = true; + djaxing = false; } // Trigger a djaxLoaded event when done @@ -247,8 +254,9 @@ } var excludes = settings.exceptions, - replaceBlockWith = (settings.replaceBlockFunction) ? settings.replaceBlockFunction: $.fn.replaceWith, - djaxing = false; + replaceBlockWith = (settings.replaceBlockFunction) ? settings.replaceBlockFunction: $.fn.replaceWith; + + djaxing = false; var blockSelector = settings.selector; @@ -271,10 +279,6 @@ window.location.href ); - $this.clearDjaxing = function() { - $this.djaxing = false; - } - // Exclude the link exceptions // Only add a class to internal links $this.find('a').filter(function () { @@ -285,9 +289,9 @@ // On new page load $(window).bind('popstate', function (event) { - $this.triggered = false; + triggered = false; if (event.originalEvent.state) { - $this.reqUrl = event.originalEvent.state.url; + reqUrl = event.originalEvent.state.url; _methods.navigate.call($this, event.originalEvent.state.url, false); } }); @@ -295,7 +299,10 @@ }, 'navigate' : function (url, add_to_history) { var $this = this; - $this.reqUrl = url; + + triggered = false; + $(window).trigger('djaxClick', []); + reqUrl = url; _methods.navigate.call($this, url, add_to_history); } }; From bceab47cf90205ebe1b871690e80d42379d3f71b Mon Sep 17 00:00:00 2001 From: Savio Date: Sun, 7 Jul 2013 14:31:36 +0100 Subject: [PATCH 11/23] fix examples syntax in readme.md --- readme.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/readme.md b/readme.md index 3938c3b..08c79df 100644 --- a/readme.md +++ b/readme.md @@ -67,9 +67,9 @@ of djaxable @@ -81,10 +81,10 @@ that do not contain admin, resources, or ?s= in the url. @@ -105,11 +105,11 @@ the page. The following example fades out the old content, and fades in the new }); } jQuery(document).ready(function($) { - $('body').djax( + $('body').djax({ 'selector' : '.djaxable', 'exceptions' : [], 'replaceBlockFunction' : transition - ); + }); }); From ff987de223ee7920c2a50e7b54d30d1b3aff7be4 Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Mon, 19 Aug 2013 14:31:18 +0100 Subject: [PATCH 12/23] handle user navigating quickly - only the last click is finally considered the intended click. - if the user clicks too fast, we assume he doesn't want to even store the intermediate locations in the history... --- jquery.djax.js | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/jquery.djax.js b/jquery.djax.js index 481d513..4350974 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -35,13 +35,27 @@ (function ($, exports) { 'use strict'; + var url_queue = []; var djaxing = false; var reqUrl; var triggered; var _methods = { 'clearDjaxing' : function () { + var $this = this; djaxing = false; + + // check in the queue to see if there is something else that + // needs to be navigated + if (url_queue.length) { + var url_addToHist = url_queue.pop(); + url_queue = []; + + + var url = url_addToHist[0], + addToHistory = url_addToHist[1]; + methods.navigate.call($this, url, addToHistory); + } }, 'navigate' : function (url, add_to_history) { var $this = this; @@ -297,13 +311,31 @@ }); }); }, - 'navigate' : function (url, add_to_history) { + 'is_djaxing' : function () { + return djaxing; + }, + 'navigate' : function (url, add_to_history, data) { var $this = this; - triggered = false; - $(window).trigger('djaxClick', []); - reqUrl = url; - _methods.navigate.call($this, url, add_to_history); + if (typeof data === 'undefined') { + data = []; + } + + if (djaxing) { + // push url in the queue and handle once the previous ajax + // request has completed + url_queue.push([url, add_to_history]); + + // handle queue + setTimeout(function () { _methods.clearDjaxing.call($this)} , 1000); + return $this; + } + else { + triggered = false; + $(window).trigger('djaxClick', data); + reqUrl = url; + _methods.navigate.call($this, url, add_to_history); + } } }; From c54ff6dde5010bcbb8fbf81c9b8ab2126bec1e8e Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Mon, 19 Aug 2013 14:34:19 +0100 Subject: [PATCH 13/23] enable cross-domain requests on IE --- jquery.djax.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jquery.djax.js b/jquery.djax.js index 4350974..13abcd2 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -35,6 +35,8 @@ (function ($, exports) { 'use strict'; + $.support.cors = true; + var url_queue = []; var djaxing = false; var reqUrl; From d3588bb31ce2b92e867e3482950586e391e94c82 Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Mon, 19 Aug 2013 14:35:03 +0100 Subject: [PATCH 14/23] some error handling --- jquery.djax.js | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/jquery.djax.js b/jquery.djax.js index 13abcd2..ea9b893 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -73,21 +73,38 @@ 'url' : url }] ); + + var settings = $this.data('settings'); $.ajax({ 'url' : url, - 'success' : function (response) { - _methods.replaceBlocks.call($this, url, add_to_history, blocks, response); + 'data' : settings.ajax_data_parameter, + 'crossDomain' : true, + 'success' : function (responseData, textStatus, jqXHR) { + // use the url shown indicated in the response if possible + var target_url = jqXHR.getResponseHeader("TargetUrl"); + if (typeof target_url === 'undefined') { + target_url = url; + } + else { + // trust the header + reqUrl = target_url; + } + _methods.replaceBlocks.call($this, target_url, add_to_history, blocks, responseData); }, - 'error' : function (response, textStatus, errorThrown) { - // handle error - // console.log('error', response, textStatus, errorThrown); - - // still replace blocks as we may end up here if the - // correct content type is not set by the webserver - - // (e.g., with content type set to application/json an - // error may be returned) - _methods.replaceBlocks.call($this, url, add_to_history, blocks, response['responseText']); + 'error' : function (jqXHR, textStatus, errorThrown) { + if (errorThrown === "" && textStatus === "error") { + // just "browse" to the url provided to handle redirects + window.location.href = this.url; + } + else { + // handle error + // still replace blocks as we may end up here if the + // correct content type is not set by the webserver - + // (e.g., with content type set to application/json an + // error may be returned) + _methods.replaceBlocks.call($this, url, add_to_history, blocks, jqXHR['responseText']); + } } }); }, From 7baa40af041ebdb8099814c59cf78e972e8f1e24 Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Mon, 19 Aug 2013 14:37:50 +0100 Subject: [PATCH 15/23] better naming and replacement logic --- jquery.djax.js | 97 +++++++++++++++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 36 deletions(-) diff --git a/jquery.djax.js b/jquery.djax.js index ea9b893..b322b3e 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -135,7 +135,7 @@ // If we're already doing djaxing, return now and silently fail if (djaxing) { - setTimeout(_methods.clearDjaxing, 1000); + setTimeout(function() { _methods.clearDjaxing.call($this); }, 1000); return $(element); } @@ -144,66 +144,88 @@ triggered = false; _methods.navigate.call($this, link.attr('href'), true); }, - 'replaceBlocks' : function (url, add, blocks, response) { + 'replaceBlocks' : function (url, add, currentBlocks, response) { var $this = this; - var blockSelector = $this.data('djaxBlockSelector'); - var replaceBlockWithFunc = $this.data('djaxReplaceBlockWith'); - if (url !== reqUrl) { _methods.navigate.call($this, reqUrl, false); return true; } - var result = $(response), - newBlocks = $(result).find(blockSelector); + // get some settings + var blockSelector = $this.data('djaxBlockSelector'); + var replaceBlockWithFunc = $this.data('djaxReplaceBlockWith'); + + var $result = $(response); + // add them to the history if requested if (add) { window.history.pushState( { 'url' : url, - 'title' : $(result).filter('title').text() + 'title' : $result.filter('title').text() }, - $(result).filter('title').text(), + $result.filter('title').text(), url ); } // Set page title as new page title + // // Set title cross-browser: // - $('title').text(title_text); returns an error on IE7 // - document.title = $(result).filter('title').text(); + document.title = $result.filter('title').text(); + + // parse new blocks + var $newBlocks = $(response).find(blockSelector); // Loop through each block and find new page equivalent - blocks.each(function () { + currentBlocks.each(function () { - var id = '#' + $(this).attr('id'), - newBlock = newBlocks.filter(id), - block = $(this); + var $currentBlock = $(this); + var id = '#' + $currentBlock.attr('id'); + var newBlock = $newBlocks.filter(id); + // take all internal links in the new block $('a', newBlock).filter(function () { return this.hostname === location.hostname; - }).addClass('dJAX_internal').on('click.djax', function (event) { - _methods.attachClick.call($this, this, event); - }); + }) + // add the dJAX_internal class to them + .addClass('dJAX_internal') + + // attach the click event + .on('click.djax', function (event) { + _methods.attachClick.call($this, this, event); + }); if (newBlock.length) { - if (block.html() !== newBlock.html()) { - replaceBlockWithFunc.call(block, newBlock); - } - } else { - block.remove(); - } + // compare the html of the new and the current block + var block_html = $currentBlock.clone().wrap('
').parent().html(), + newblock_html = newBlock.clone().wrap('
').parent().html(); + + if (block_html !== newblock_html) { + // perform replacement + var detatched = replaceBlockWithFunc.call($currentBlock, newBlock); + // get rid of detatched DOM elements + $(detatched).remove(); + } + else { + // get rid of the new block if the html of the old + // block is exactly the same! + $(newBlock).remove(); + } + } }); + // Loop through new page blocks and add in as needed var $previousBlock; - $.each(newBlocks, function () { + $.each($newBlocks, function () { - var newBlock = $(this), + var $newBlock = $(this), id = '#' + $(this).attr('id'), $previousSibling; @@ -212,28 +234,28 @@ if (!$(id).length) { // Find the previous sibling - $previousSibling = $(result).find(id).prev(); + $previousSibling = $result.find(id).prev(); if ($previousSibling.length) { // Insert after the previous element - newBlock.insertAfter('#' + $previousSibling.attr('id')); + $newBlock.insertAfter('#' + $previousSibling.attr('id')); } else { // There's no previous sibling, so prepend to parent instead - var parent_id = newBlock.parent().attr('id'); + var parent_id = $newBlock.parent().attr('id'); if (parent_id === undefined && $previousBlock !== undefined) { - newBlock.insertAfter('#' + $previousBlock.attr('id')); + $newBlock.insertAfter('#' + $previousBlock.attr('id')); } else { - newBlock.prependTo('#' + parent_id); + $newBlock.prependTo('#' + parent_id); } } } // Keep the previous block - $previousBlock = newBlock; + $previousBlock = $newBlock; // Only add a class to internal links - $('a', newBlock).filter(function () { + $('a', $newBlock).filter(function () { return this.hostname === location.hostname; }).addClass('dJAX_internal').on('click.djax', function (event) { return _methods.attachClick.call($this, this, event); @@ -249,7 +271,7 @@ 'djaxLoad', [{ 'url' : url, - 'title' : $(result).filter('title').text(), + 'title' : $result.filter('title').text(), 'response' : response }] ); @@ -262,7 +284,7 @@ 'djaxLoaded', [{ 'url' : url, - 'title' : $(result).filter('title').text(), + 'title' : $result.filter('title').text(), 'response' : response }] ); @@ -275,12 +297,15 @@ var settings = $.extend({ 'selector' : undefined, 'exceptions' : [], - 'replaceBlockFunction' : undefined + 'replaceBlockFunction' : undefined, }, options); return this.each(function() { var $this = $(this); + // save settings + $this.data('settings', settings); + // If browser doesn't support pushState, abort now if (!history.pushState) { return $(this); @@ -301,7 +326,7 @@ // Save excludes so that we can use them later... $this.data('djaxUserExcludes', excludes); - + // Ensure that the history is correct when going from 2nd page to 1st window.history.replaceState( { From 8a74fb1ae21b74f21ef91c67620452222e7d0e7c Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Mon, 19 Aug 2013 14:38:38 +0100 Subject: [PATCH 16/23] get ajax settings from initialiization --- jquery.djax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/jquery.djax.js b/jquery.djax.js index b322b3e..86b85c7 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -298,6 +298,7 @@ 'selector' : undefined, 'exceptions' : [], 'replaceBlockFunction' : undefined, + 'ajax_data_parameter' : { } }, options); return this.each(function() { From b6519f40d40a335a3b7919d7e8551d37b76f8462 Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Mon, 19 Aug 2013 16:11:26 +0100 Subject: [PATCH 17/23] support blocking callback on djax click --- jquery.djax.js | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/jquery.djax.js b/jquery.djax.js index 86b85c7..106346d 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -75,10 +75,15 @@ ); var settings = $this.data('settings'); - + + var ajax_data = settings.ajax_data_parameter; + if (typeof ajax_data === 'function') { + ajax_data = ajax_data(); + } + $.ajax({ 'url' : url, - 'data' : settings.ajax_data_parameter, + 'data' : ajax_data, 'crossDomain' : true, 'success' : function (responseData, textStatus, jqXHR) { // use the url shown indicated in the response if possible @@ -139,7 +144,14 @@ return $(element); } - $(window).trigger('djaxClick', [element]); + // trigger asynchronous click event + var djaxClickData = [element]; + $(window).trigger('djaxClick', djaxClickData); + + // call blocking callback + var settings = $this.data('settings'); + settings.onDjaxClickCallback.call($this, djaxClickData); + reqUrl = link.attr('href'); triggered = false; _methods.navigate.call($this, link.attr('href'), true); @@ -298,7 +310,8 @@ 'selector' : undefined, 'exceptions' : [], 'replaceBlockFunction' : undefined, - 'ajax_data_parameter' : { } + 'ajax_data_parameter' : { }, + 'onDjaxClickCallback' : function (djaxClickData) { return; } }, options); return this.each(function() { @@ -378,9 +391,25 @@ else { triggered = false; $(window).trigger('djaxClick', data); + + // call blocking callback + var settings = $this.data('settings'); + settings.onDjaxClickCallback.call($this, data); + reqUrl = url; _methods.navigate.call($this, url, add_to_history); } + }, + 'set_ajax_data_parameter' : function (ajax_parameters_func_or_obj) { + var $this = this; + var settings = $this.data('settings'); + + // if function: will be called when needed; if object: will be + // passed straight away to the $.ajax call. + settings.ajax_data_parameter = ajax_parameters_func_or_obj; + + // save parameters + $this.data('settings', settings); } }; From d77594561b48fdbe5480bfdf6a9c491455487641 Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Tue, 3 Dec 2013 11:00:58 +0000 Subject: [PATCH 18/23] deferred djax --- jquery.djax.js | 204 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 144 insertions(+), 60 deletions(-) diff --git a/jquery.djax.js b/jquery.djax.js index 106346d..a3c3761 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -21,10 +21,6 @@ * */ -/*jslint browser: true, indent: 4, maxerr: 50, sub: true */ -/*jshint bitwise:true, curly:true, eqeqeq:true, forin:true, immed:true, latedef:true, noarg:true, noempty:true, nomen:true, nonew:true, onevar:true, plusplus:true, regexp:true, smarttabs:true, strict:true, trailing:true, undef:true, white:true, browser:true, jquery:true, indent:4, maxerr:50, */ -/*global jQuery */ - // ==ClosureCompiler== // @compilation_level ADVANCED_OPTIMIZATIONS // @output_file_name jquery.djax.js @@ -41,6 +37,7 @@ var djaxing = false; var reqUrl; var triggered; + var popstateUrl = ''; var _methods = { 'clearDjaxing' : function () { @@ -55,8 +52,9 @@ var url = url_addToHist[0], - addToHistory = url_addToHist[1]; - methods.navigate.call($this, url, addToHistory); + addToHistory = url_addToHist[1], + urlData = url_addToHist[2]; + methods.navigate.call($this, url, addToHistory, urlData); } }, 'navigate' : function (url, add_to_history) { @@ -98,7 +96,11 @@ _methods.replaceBlocks.call($this, target_url, add_to_history, blocks, responseData); }, 'error' : function (jqXHR, textStatus, errorThrown) { - if (errorThrown === "" && textStatus === "error") { + if (textStatus === 'error' + && (jqXHR.status === 404 || + errorThrown === "" || + typeof jqXHR.responseText === 'undefined')) { + // just "browse" to the url provided to handle redirects window.location.href = this.url; } @@ -151,14 +153,24 @@ // call blocking callback var settings = $this.data('settings'); settings.onDjaxClickCallback.call($this, djaxClickData); - - reqUrl = link.attr('href'); + + var urlDataAttribute = settings.urlDataAttribute; + if (typeof urlDataAttribute !== 'undefined') { + reqUrl = link.data(urlDataAttribute); + } + + if (typeof reqUrl === 'undefined') { + reqUrl = link.attr('href'); + } + triggered = false; _methods.navigate.call($this, link.attr('href'), true); }, 'replaceBlocks' : function (url, add, currentBlocks, response) { var $this = this; + var settings = $this.data('settings'); + if (url !== reqUrl) { _methods.navigate.call($this, reqUrl, false); return true; @@ -182,6 +194,16 @@ ); } + // here store all replacements to be performed + // they look like: + // { + // type: 'replace', // or prependTo, insertAfter, remove + // new_block: $jquery_element, + // target: $jquery_element, // or null if a new block needs to be removed + // } + // + var replacements_config = []; + // Set page title as new page title // // Set title cross-browser: @@ -192,7 +214,9 @@ // parse new blocks var $newBlocks = $(response).find(blockSelector); - // Loop through each block and find new page equivalent + // + // Case in which blocks need to be replaced + // currentBlocks.each(function () { var $currentBlock = $(this); @@ -200,16 +224,16 @@ var newBlock = $newBlocks.filter(id); // take all internal links in the new block - $('a', newBlock).filter(function () { + $('a:not(.' + settings.ignoreClass + ')', newBlock).filter(function () { return this.hostname === location.hostname; }) - // add the dJAX_internal class to them - .addClass('dJAX_internal') - - // attach the click event - .on('click.djax', function (event) { - _methods.attachClick.call($this, this, event); - }); + // add the dJAX_internal class to them + .addClass('dJAX_internal') + + // attach the click event + .on('click.djax', function (event) { + _methods.attachClick.call($this, this, event); + }); if (newBlock.length) { // compare the html of the new and the current block @@ -217,22 +241,28 @@ newblock_html = newBlock.clone().wrap('
').parent().html(); if (block_html !== newblock_html) { - // perform replacement - var detatched = replaceBlockWithFunc.call($currentBlock, newBlock); - - // get rid of detatched DOM elements - $(detatched).remove(); + replacements_config.push({ + 'type': 'replace', + 'new_block' : newBlock, + 'target' : $currentBlock + }); } else { // get rid of the new block if the html of the old // block is exactly the same! - $(newBlock).remove(); + replacements_config.push({ + 'type': 'remove', + 'new_block': newBlock, + 'target' : undefined + }); } } }); - // Loop through new page blocks and add in as needed + // + // Case in which blocks need to be added/appended + // var $previousBlock; $.each($newBlocks, function () { @@ -250,55 +280,85 @@ if ($previousSibling.length) { // Insert after the previous element - $newBlock.insertAfter('#' + $previousSibling.attr('id')); + replacements_config.push({ + 'type' : 'insertAfter', + 'target' : $('#' + $previousSibling.attr('id')), + 'new_block' : $newBlock + }); } else { // There's no previous sibling, so prepend to parent instead var parent_id = $newBlock.parent().attr('id'); if (parent_id === undefined && $previousBlock !== undefined) { - $newBlock.insertAfter('#' + $previousBlock.attr('id')); + replacements_config.push({ + 'type' : 'insertAfter', + 'target' : $('#' + $previousBlock.attr('id')), + 'new_block' : $newBlock + }); } else { - $newBlock.prependTo('#' + parent_id); + replacements_config.push({ + 'type' : 'prependTo', + 'target' : $('#' + parent_id), + 'new_block' : $newBlock + }); } } } // Keep the previous block $previousBlock = $newBlock; - + // Only add a class to internal links - $('a', $newBlock).filter(function () { - return this.hostname === location.hostname; - }).addClass('dJAX_internal').on('click.djax', function (event) { - return _methods.attachClick.call($this, this, event); + // TODO: Remove this + // $('a:not(.' + settings.ignoreClass + ')', $newBlock).filter(function () { + // return this.hostname === location.hostname; + // }).addClass('dJAX_internal').on('click.djax', function (event) { + // return _methods.attachClick.call($this, this, event); + // }); + + var replace_fn = function ($newBlock) { + $('a:not(.' + settings.ignoreClass + ')', $newBlock).filter(function () { + return this.hostname === location.hostname; + }).addClass('dJAX_internal').on('click.djax', function (event) { + return _methods.attachClick.call($this, this, event); + }); + } + + replacements_config.push({ + 'type' : 'function', + 'target': replace_fn, + 'args' : [$newBlock] }); }); - - - // Trigger djaxLoad event as a pseudo ready() - if (!triggered) { - $(window).trigger( - 'djaxLoad', - [{ - 'url' : url, - 'title' : $result.filter('title').text(), - 'response' : response - }] - ); - triggered = true; - djaxing = false; + // Delegate block replacement + + var done_fn = function () { + // Trigger djaxLoad event as a pseudo ready() + if (!triggered) { + $(window).trigger( + 'djaxLoad', + [{ + 'url' : url, + 'title' : $result.filter('title').text(), + 'response' : response + }] + ); + triggered = true; + djaxing = false; + } } + + replacements_config.push({ + 'type' : 'function', + 'target': done_fn, + 'args' : [] + }); - // Trigger a djaxLoaded event when done $(window).trigger( - 'djaxLoaded', - [{ - 'url' : url, - 'title' : $result.filter('title').text(), - 'response' : response - }] + 'djaxDeferReplacements', + [ replacements_config ] ); } }; @@ -308,10 +368,13 @@ var settings = $.extend({ 'selector' : undefined, + 'ignoreClass' : '', 'exceptions' : [], + 'urlDataAttribute' : undefined, 'replaceBlockFunction' : undefined, 'ajax_data_parameter' : { }, - 'onDjaxClickCallback' : function (djaxClickData) { return; } + 'onDjaxClickCallback' : function (djaxClickData) { return; }, + 'onHistoryPopStateCallback' : function () { return; } }, options); return this.each(function() { @@ -326,6 +389,7 @@ } var excludes = settings.exceptions, + ignoreClass = settings.ignoreClass, replaceBlockWith = (settings.replaceBlockFunction) ? settings.replaceBlockFunction: $.fn.replaceWith; djaxing = false; @@ -340,7 +404,7 @@ // Save excludes so that we can use them later... $this.data('djaxUserExcludes', excludes); - + // Ensure that the history is correct when going from 2nd page to 1st window.history.replaceState( { @@ -353,7 +417,7 @@ // Exclude the link exceptions // Only add a class to internal links - $this.find('a').filter(function () { + $this.find('a:not(.' + ignoreClass + ')').filter(function () { return this.hostname === location.hostname; }).addClass('dJAX_internal').on('click.djax', function (event) { return _methods.attachClick.call($this, this, event); @@ -362,9 +426,29 @@ // On new page load $(window).bind('popstate', function (event) { triggered = false; + if (event.originalEvent.state) { - reqUrl = event.originalEvent.state.url; - _methods.navigate.call($this, event.originalEvent.state.url, false); + var targetUrl = event.originalEvent.state.url; + + // prevent IE <= 9 to navigate repeatedly to the current url + var url_parts = targetUrl.split("#"); + if (url_parts.length === 2) { + if (url_parts[0].indexOf(url_parts[1]) >= 0) { + popstateUrl = ''; + return; + } + } + + if (popstateUrl != targetUrl) { + settings.onHistoryPopStateCallback(); + reqUrl = targetUrl; + popstateUrl = targetUrl; + _methods.navigate.call($this, popstateUrl, false); + } + else { + // second time just reset the popstate url. + popstateUrl = ''; + } } }); }); @@ -382,7 +466,7 @@ if (djaxing) { // push url in the queue and handle once the previous ajax // request has completed - url_queue.push([url, add_to_history]); + url_queue.push([url, add_to_history, data]); // handle queue setTimeout(function () { _methods.clearDjaxing.call($this)} , 1000); From 43269fb050c36b0a8443c2c37f9cd236dbe70814 Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Wed, 29 Jan 2014 18:18:50 +0100 Subject: [PATCH 19/23] Support request method and parameters --- jquery.djax.js | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/jquery.djax.js b/jquery.djax.js index a3c3761..e981456 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -50,16 +50,23 @@ var url_addToHist = url_queue.pop(); url_queue = []; - var url = url_addToHist[0], addToHistory = url_addToHist[1], - urlData = url_addToHist[2]; - methods.navigate.call($this, url, addToHistory, urlData); + urlData = url_addToHist[2], + method = url_addToHist[3], + requestParameters = url_addToHist[4]; + + methods.navigate.call($this, url, addToHistory, urlData, method, requestParameters); } }, - 'navigate' : function (url, add_to_history) { + 'navigate' : function (url, add_to_history, method) { var $this = this; + // decide which method to use for the ajax call (POST/GET allowed) + if (method !== 'POST') { + method = 'GET'; + } + var blockSelector = $this.data('djaxBlockSelector'); var blocks = $(blockSelector); @@ -82,6 +89,7 @@ $.ajax({ 'url' : url, 'data' : ajax_data, + 'type' : method, // "GET" or "POST" 'crossDomain' : true, 'success' : function (responseData, textStatus, jqXHR) { // use the url shown indicated in the response if possible @@ -152,7 +160,7 @@ // call blocking callback var settings = $this.data('settings'); - settings.onDjaxClickCallback.call($this, djaxClickData); + settings.onDjaxClickCallback.call($this, djaxClickData, {}); var urlDataAttribute = settings.urlDataAttribute; if (typeof urlDataAttribute !== 'undefined') { @@ -164,7 +172,7 @@ } triggered = false; - _methods.navigate.call($this, link.attr('href'), true); + _methods.navigate.call($this, link.attr('href'), true, 'GET'); }, 'replaceBlocks' : function (url, add, currentBlocks, response) { var $this = this; @@ -172,7 +180,7 @@ var settings = $this.data('settings'); if (url !== reqUrl) { - _methods.navigate.call($this, reqUrl, false); + _methods.navigate.call($this, reqUrl, false, 'GET'); return true; } @@ -373,7 +381,16 @@ 'urlDataAttribute' : undefined, 'replaceBlockFunction' : undefined, 'ajax_data_parameter' : { }, - 'onDjaxClickCallback' : function (djaxClickData) { return; }, + /* + * Called synchronously before ajax call starts. + * + * - djaxClickData: element or identifier of element the + * user interacted with; + * + * - requestParameters: parameters that will be included in the + * ajax call. This is an object. + */ + 'onDjaxClickCallback' : function (djaxClickData, requestParameters) { return; }, 'onHistoryPopStateCallback' : function () { return; } }, options); @@ -443,7 +460,7 @@ settings.onHistoryPopStateCallback(); reqUrl = targetUrl; popstateUrl = targetUrl; - _methods.navigate.call($this, popstateUrl, false); + _methods.navigate.call($this, popstateUrl, false, 'GET'); } else { // second time just reset the popstate url. @@ -456,7 +473,7 @@ 'is_djaxing' : function () { return djaxing; }, - 'navigate' : function (url, add_to_history, data) { + 'navigate' : function (url, add_to_history, data, method, requestParameters) { var $this = this; if (typeof data === 'undefined') { @@ -466,7 +483,7 @@ if (djaxing) { // push url in the queue and handle once the previous ajax // request has completed - url_queue.push([url, add_to_history, data]); + url_queue.push([url, add_to_history, data, method, requestParameters]); // handle queue setTimeout(function () { _methods.clearDjaxing.call($this)} , 1000); @@ -478,10 +495,10 @@ // call blocking callback var settings = $this.data('settings'); - settings.onDjaxClickCallback.call($this, data); + settings.onDjaxClickCallback.call($this, data, requestParameters); reqUrl = url; - _methods.navigate.call($this, url, add_to_history); + _methods.navigate.call($this, url, add_to_history, method); } }, 'set_ajax_data_parameter' : function (ajax_parameters_func_or_obj) { From 146cc7c6630b101543bdae1dc851a5a40d3c7f0e Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Thu, 30 Jan 2014 18:09:21 +0100 Subject: [PATCH 20/23] Deal with the targetUrl header on errors --- jquery.djax.js | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/jquery.djax.js b/jquery.djax.js index e981456..b58c674 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -59,6 +59,13 @@ methods.navigate.call($this, url, addToHistory, urlData, method, requestParameters); } }, + 'getUrlFromHeaders' : function (defaultUrl, jqXHR) { + var targetUrl = jqXHR.getResponseHeader("TargetUrl"); + if (typeof targetUrl === 'undefined') { + targetUrl = defaultUrl; + } + return targetUrl; + }, 'navigate' : function (url, add_to_history, method) { var $this = this; @@ -92,22 +99,20 @@ 'type' : method, // "GET" or "POST" 'crossDomain' : true, 'success' : function (responseData, textStatus, jqXHR) { - // use the url shown indicated in the response if possible - var target_url = jqXHR.getResponseHeader("TargetUrl"); - if (typeof target_url === 'undefined') { - target_url = url; - } - else { - // trust the header - reqUrl = target_url; - } - _methods.replaceBlocks.call($this, target_url, add_to_history, blocks, responseData); + // get url from headers + var targetUrl = _methods.getUrlFromHeaders.call($this, url, jqXHR); + + // keep url we are going to + reqUrl = targetUrl; + + _methods.replaceBlocks.call($this, targetUrl, add_to_history, blocks, responseData); }, 'error' : function (jqXHR, textStatus, errorThrown) { if (textStatus === 'error' && (jqXHR.status === 404 || errorThrown === "" || typeof jqXHR.responseText === 'undefined')) { + // just "browse" to the url provided to handle redirects window.location.href = this.url; @@ -118,7 +123,14 @@ // correct content type is not set by the webserver - // (e.g., with content type set to application/json an // error may be returned) - _methods.replaceBlocks.call($this, url, add_to_history, blocks, jqXHR['responseText']); + + // try to get url from the headers + var targetUrl = _methods.getUrlFromHeaders.call($this, url, jqXHR); + + // keep url we are going to + reqUrl = targetUrl; + + _methods.replaceBlocks.call($this, targetUrl, add_to_history, blocks, jqXHR['responseText']); } } }); From 5d0ecdf8c905f56bcf5e48cfa8ca6d31ccfbc21d Mon Sep 17 00:00:00 2001 From: Savio Dimatteo Date: Wed, 5 Feb 2014 19:23:31 +0100 Subject: [PATCH 21/23] Wrap reponse to obtain all the descendants --- jquery.djax.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jquery.djax.js b/jquery.djax.js index b58c674..971ea19 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -231,8 +231,8 @@ // document.title = $result.filter('title').text(); - // parse new blocks - var $newBlocks = $(response).find(blockSelector); + // parse new blocks (wrap reponse to obtain all the descendants) + var $newBlocks = $('
' + response + '
').find(blockSelector); // // Case in which blocks need to be replaced From dbb2521a5c2b26aa8bce25ce24bcaca1e1b32420 Mon Sep 17 00:00:00 2001 From: Samuel Scully Date: Tue, 18 Mar 2014 11:32:58 +0000 Subject: [PATCH 22/23] Add ajax timeout time and callback to settings --- jquery.djax.js | 249 ++++++++++++++++++++++++++++--------------------- 1 file changed, 144 insertions(+), 105 deletions(-) diff --git a/jquery.djax.js b/jquery.djax.js index 971ea19..d97eff1 100644 --- a/jquery.djax.js +++ b/jquery.djax.js @@ -29,7 +29,7 @@ // http://closure-compiler.appspot.com/home (function ($, exports) { - 'use strict'; + 'use strict'; $.support.cors = true; @@ -38,7 +38,7 @@ var reqUrl; var triggered; var popstateUrl = ''; - + var _methods = { 'clearDjaxing' : function () { var $this = this; @@ -60,115 +60,135 @@ } }, 'getUrlFromHeaders' : function (defaultUrl, jqXHR) { - var targetUrl = jqXHR.getResponseHeader("TargetUrl"); - if (typeof targetUrl === 'undefined') { - targetUrl = defaultUrl; - } + var targetUrl = jqXHR.getResponseHeader("TargetUrl"); + if (typeof targetUrl === 'undefined') { + targetUrl = defaultUrl; + } return targetUrl; }, - 'navigate' : function (url, add_to_history, method) { + 'navigate' : function (options) { var $this = this; - // decide which method to use for the ajax call (POST/GET allowed) - if (method !== 'POST') { - method = 'GET'; - } + djaxing = true; - var blockSelector = $this.data('djaxBlockSelector'); - var blocks = $(blockSelector); + $(window).trigger( + 'djaxLoading', [{ + 'url' : options.url + }] + ); - djaxing = true; + $.ajax(_methods.ajaxSettings($this, options)); + }, + 'ajaxSettings' : function(element, options) { + if (options.method !== 'POST') { + options.method = 'GET'; + } - // Get the new page - $(window).trigger( - 'djaxLoading', [{ - 'url' : url - }] - ); + options.blocks = $(element.data('djaxBlockSelector')); - var settings = $this.data('settings'); + var settings = element.data('settings'); var ajax_data = settings.ajax_data_parameter; if (typeof ajax_data === 'function') { ajax_data = ajax_data(); } - $.ajax({ - 'url' : url, - 'data' : ajax_data, - 'type' : method, // "GET" or "POST" + return { + 'url' : options.url, + 'data' : ajax_data, + 'timeout' : settings.ajax_timeout, + 'type' : options.method, 'crossDomain' : true, - 'success' : function (responseData, textStatus, jqXHR) { - // get url from headers - var targetUrl = _methods.getUrlFromHeaders.call($this, url, jqXHR); + 'success' : _methods.changePage( + element, + options.url, + options.add_to_history, + options.blocks + ), + 'error' : _methods.changePageError( + element, + options.url, + options.add_to_history, + options.blocks, + settings.ajax_timeout_callback + ) + }; + }, + 'changePage' : function (element, url, add_to_history, blocks) { + return function (responseData, textStatus, jqXHR) { + var targetUrl = _methods.getUrlFromHeaders.call(element, url, jqXHR); - // keep url we are going to - reqUrl = targetUrl; + // keep url we are going to + reqUrl = targetUrl; - _methods.replaceBlocks.call($this, targetUrl, add_to_history, blocks, responseData); - }, - 'error' : function (jqXHR, textStatus, errorThrown) { - if (textStatus === 'error' - && (jqXHR.status === 404 || - errorThrown === "" || - typeof jqXHR.responseText === 'undefined')) { - - - // just "browse" to the url provided to handle redirects - window.location.href = this.url; - } - else { - // handle error - // still replace blocks as we may end up here if the - // correct content type is not set by the webserver - - // (e.g., with content type set to application/json an - // error may be returned) + _methods.replaceBlocks.call(element, targetUrl, add_to_history, blocks, responseData); + } + }, + 'changePageError' : function (element, url, add_to_history, blocks, timeout_callback) { + return function (jqXHR, textStatus, errorThrown) { + if (textStatus === 'error' + && (jqXHR.status === 404 || + errorThrown === "" || + typeof jqXHR.responseText === 'undefined')) { - // try to get url from the headers - var targetUrl = _methods.getUrlFromHeaders.call($this, url, jqXHR); - // keep url we are going to - reqUrl = targetUrl; + // just "browse" to the url provided to handle redirects + window.location.href = this.url; + } + else if (textStatus === 'timeout') { + timeout_callback && timeout_callback(this); + } + else { + // handle error + // still replace blocks as we may end up here if the + // correct content type is not set by the webserver - + // (e.g., with content type set to application/json an + // error may be returned) - _methods.replaceBlocks.call($this, targetUrl, add_to_history, blocks, jqXHR['responseText']); - } - } - }); - }, + // try to get url from the headers + var targetUrl = _methods.getUrlFromHeaders.call(element, url, jqXHR); + + // keep url we are going to + reqUrl = targetUrl; + + _methods.replaceBlocks.call(element, targetUrl, add_to_history, blocks, jqXHR['responseText']); + } + } + }, 'attachClick' : function (element, event) { var $this = this; - var link = $(element), - exception = false, + var link = $(element), + exception = false, excludes = $this.data('djaxUserExcludes'); - $.each(excludes, function (index, exclusion) { - if (link.attr('href').indexOf(exclusion) !== -1) { - exception = true; - } - if (window.location.href.indexOf(exclusion) !== -1) { - exception = true; - } - }); - - // If the link is one of the exceptions, return early so that - // the link can be clicked and a full page load as normal - if (exception) { - return $(element); - } - - // From this point on, we handle the behaviour - event.preventDefault(); - - // If we're already doing djaxing, return now and silently fail - if (djaxing) { - setTimeout(function() { _methods.clearDjaxing.call($this); }, 1000); - return $(element); - } + $.each(excludes, function (index, exclusion) { + if (link.attr('href').indexOf(exclusion) !== -1) { + exception = true; + } + if (window.location.href.indexOf(exclusion) !== -1) { + exception = true; + } + }); + + // If the link is one of the exceptions, return early so that + // the link can be clicked and a full page load as normal + if (exception) { + return $(element); + } + + // From this point on, we handle the behaviour + event.preventDefault(); + + // If we're already doing djaxing, return now and silently fail + if (djaxing) { + setTimeout(function() { _methods.clearDjaxing.call($this); }, 1000); + return $(element); + } // trigger asynchronous click event var djaxClickData = [element]; - $(window).trigger('djaxClick', djaxClickData); + $(window).trigger('djaxClick', djaxClickData); // call blocking callback var settings = $this.data('settings'); @@ -183,16 +203,24 @@ reqUrl = link.attr('href'); } - triggered = false; - _methods.navigate.call($this, link.attr('href'), true, 'GET'); - }, + triggered = false; + _methods.navigate.call($this, { + url: link.attr('href'), + add_to_history: true, + type: 'GET' + }); + }, 'replaceBlocks' : function (url, add, currentBlocks, response) { var $this = this; var settings = $this.data('settings'); if (url !== reqUrl) { - _methods.navigate.call($this, reqUrl, false, 'GET'); + _methods.navigate.call($this, { + url: reqUrl, + add_to_history: false, + method: 'GET' + }); return true; } @@ -242,19 +270,19 @@ var $currentBlock = $(this); var id = '#' + $currentBlock.attr('id'); var newBlock = $newBlocks.filter(id); - + // take all internal links in the new block $('a:not(.' + settings.ignoreClass + ')', newBlock).filter(function () { return this.hostname === location.hostname; }) // add the dJAX_internal class to them .addClass('dJAX_internal') - + // attach the click event .on('click.djax', function (event) { _methods.attachClick.call($this, this, event); }); - + if (newBlock.length) { // compare the html of the new and the current block var block_html = $currentBlock.clone().wrap('
').parent().html(), @@ -276,11 +304,11 @@ 'target' : undefined }); } - } + } }); - // + // // Case in which blocks need to be added/appended // var $previousBlock; @@ -327,7 +355,7 @@ // Keep the previous block $previousBlock = $newBlock; - + // Only add a class to internal links // TODO: Remove this // $('a:not(.' + settings.ignoreClass + ')', $newBlock).filter(function () { @@ -369,7 +397,7 @@ djaxing = false; } } - + replacements_config.push({ 'type' : 'function', 'target': done_fn, @@ -393,6 +421,8 @@ 'urlDataAttribute' : undefined, 'replaceBlockFunction' : undefined, 'ajax_data_parameter' : { }, + 'ajax_timeout': undefined, + 'ajax_timeout_callback' : undefined, /* * Called synchronously before ajax call starts. * @@ -405,7 +435,7 @@ 'onDjaxClickCallback' : function (djaxClickData, requestParameters) { return; }, 'onHistoryPopStateCallback' : function () { return; } }, options); - + return this.each(function() { var $this = $(this); @@ -427,7 +457,7 @@ // Save block selector internally so that we can use it in later calls $this.data('djaxBlockSelector', blockSelector); - + // Save the replaceBlockWith function internally too... $this.data('djaxReplaceBlockWith', replaceBlockWith); @@ -443,7 +473,7 @@ $('title').text(), window.location.href ); - + // Exclude the link exceptions // Only add a class to internal links $this.find('a:not(.' + ignoreClass + ')').filter(function () { @@ -462,7 +492,7 @@ // prevent IE <= 9 to navigate repeatedly to the current url var url_parts = targetUrl.split("#"); if (url_parts.length === 2) { - if (url_parts[0].indexOf(url_parts[1]) >= 0) { + if (url_parts[0].indexOf(url_parts[1]) >= 0) { popstateUrl = ''; return; } @@ -472,7 +502,12 @@ settings.onHistoryPopStateCallback(); reqUrl = targetUrl; popstateUrl = targetUrl; - _methods.navigate.call($this, popstateUrl, false, 'GET'); + + _methods.navigate.call($this, { + url: popstateUrl, + add_to_history: false, + method: 'GET' + }); } else { // second time just reset the popstate url. @@ -491,14 +526,14 @@ if (typeof data === 'undefined') { data = []; } - + if (djaxing) { // push url in the queue and handle once the previous ajax // request has completed - url_queue.push([url, add_to_history, data, method, requestParameters]); + url_queue.push([url, add_to_history, data, method, requestParameters]); // handle queue - setTimeout(function () { _methods.clearDjaxing.call($this)} , 1000); + setTimeout(function () { _methods.clearDjaxing.call($this)} , 1000); return $this; } else { @@ -510,13 +545,17 @@ settings.onDjaxClickCallback.call($this, data, requestParameters); reqUrl = url; - _methods.navigate.call($this, url, add_to_history, method); + _methods.navigate.call($this, { + url: url, + add_to_history: add_to_history, + method: method + }); } }, 'set_ajax_data_parameter' : function (ajax_parameters_func_or_obj) { var $this = this; var settings = $this.data('settings'); - + // if function: will be called when needed; if object: will be // passed straight away to the $.ajax call. settings.ajax_data_parameter = ajax_parameters_func_or_obj; @@ -525,7 +564,7 @@ $this.data('settings', settings); } }; - + $.fn.djax = function(method) { /* * Just a router for method calls From 56e4968663a058e8884d457b60631ed0f9b20604 Mon Sep 17 00:00:00 2001 From: Savio Date: Wed, 25 Jun 2014 00:36:03 +0200 Subject: [PATCH 23/23] Update readme.md --- readme.md | 150 +----------------------------------------------------- 1 file changed, 2 insertions(+), 148 deletions(-) diff --git a/readme.md b/readme.md index 08c79df..c17ab54 100644 --- a/readme.md +++ b/readme.md @@ -1,151 +1,5 @@ #djax: Dynamic pjax -##Basic usage +This project is not maintained. It was diverging too much from the original project, that ended up as another project: DDJAX. -djax is very quick to set up, with a few markup requirements to let it work smoothly. - -First include it in your header after jquery: - - - - -Then instantiate on the largest page element where you will have updating content. 'body' is typically the way to go with this: - - - -Congrats, you're done! Well mostly... - -##Markup - -djax will track elements with the class you pass it as the 'selector' argument. In the above example I've passed the class 'updatable,' so my markup would look something like this: - - -
-
- Here's a div that will be monitored for changes by djax. -
-
- Here's another -
-
- - -Your markup can be laid out however you like, and your trackable sections can be anywhere in relation to one another. It's best to keep them top level (nesting is unnecessary and unsupported,) and -there are a few requirements that allow the plugin to function properly. - -###IDs - -Trackable elements must all have IDs. This is how the requested page is matched up with the current page. Only trackable elements that differ between the two pages will be loaded. -Trackable elements that do not exist on the requested page will be removed, and trackable elements that do not exist on the current page will be added. In order to support this, it -is also necessary to ensure the *parent* elements of every trackable element has an ID, as well as the sibling element immediately *prior* to each trackable element (if one exists). - -These ID's are used to add elements when necessary. If an element exists in a requested page, but not the current page, it will be inserted after the prior sibling (by ID,) or prepended to -the parent element (by ID.) - -##Parameters - -The plugin accepts only two parameters: 'selector' and 'exceptions'. Only 'selector' is required. - -###Tracking Class - -The 'selector' parameter is the only required parameter and is the class you will use to identify trackable elements. If my code looks like the below sample, every dynamic element in my markup should have a class -of djaxable - - - -###Exceptions - -By default djax works on any internal links, but sometimes you may want to exclude certain URLs on your site. The 'exceptions' parameter allows you to pass an array of URL fragments to exclude from djax -loading. This is performed with a simple Javascript 'indexOf,' so the more of the URL you provide, the more specifically your exclusions will be matched. The below example will djax any internal links -that do not contain admin, resources, or ?s= in the url. - - - -##DOM Replacement Callbacks (optional) - -Pass in a reference to a function that will handle the DOM replacement logic. The default djax replacement uses the standard jQuery `replaceWith` and does an immediate replace. For transitions, fade in/outs etc, you can control when and how the new content displays on -the page. The following example fades out the old content, and fades in the new content. - - - - -##Events - -###djaxLoad - -By loading new content via ajax, your visitors will only encounter $('document').ready() the first time they land on your site, and any time they manually perform a hard refresh. To help address this, -djax triggers a window level event on each partial load it performs. Here's an example of enabling pageview tracking with Google Analytics on a djax enabled site: - - $(window).bind('djaxLoad', function(e, data) { - _gaq.push(['_trackPageview']); - }); - -As a convenience, the data object passed with the event contains the requested url, the page title for the requested page, and the contents of the requested page as a string. Use something like the following -code to work with the response as a jQuery object - - $(window).bind('djaxLoad', function(e, data) { - var responseObj = $('
'+data.response+'
'); - //do stuff here - }); - -###djaxClick - -This event is triggered when a djax'ed link is clicked. Use something like the code below to scroll top before loading in new content with ajax: - - $(window).bind('djaxClick', function(e, data) { - var bodyelem = ($.browser.safari) ? bodyelem = $("body") : bodyelem = $("html,body"); - bodyelem.scrollTop(0); - }); - -##Live Demo - -djax arose out of a desire to use [pjax](https://github.com/defunkt/jquery-pjax) with complicated and varied layouts. See [here](http://brianzeligson.com/djax) for a WordPress site using a modified version -of the [bones](http://themble.com/bones/) WordPress theme. djax enabling this theme took about [28 lines of code](https://github.com/beezee/bones-responsive/commit/58aadde224d74f8aaa3266a4bd76e961f2888ada) -(if you count adding a class to an element as a line of code.) - -There is also a small working example in the github repository. Feel free to load up any of the included html files in your browser to see how it works. +See DDJAX at https://github.com/lokku/jquery-ddjax