Skip to content

Instantly share code, notes, and snippets.

@cyrusbeer
Created September 13, 2012 11:58
Show Gist options
  • Save cyrusbeer/3713883 to your computer and use it in GitHub Desktop.
Save cyrusbeer/3713883 to your computer and use it in GitHub Desktop.
Intercepts urls on your site and instead makes ajax calls and then changes the url shown in the browser with pushState to make it look like the original url was submitted.
$(function( $ ) {
$(document).ready(function() {
$(".ajax-intercept").ajaxInterceptor({
pageElementToUpdateSelector: "div#content",
showLoadingMessage: true,
loadingMessageDelay: 600,
loadingMessage: 'Loading...',
loadingMessageSelector: 'div.loading-message'
});
});
});
/*
* Author: Cyrus Beer
* 9/13/2012
* Requires: history.js, history.adapter.jquery.js, json2.js
*/
(function($) {
var settings = {
'pageElementToUpdateSelector' : undefined, // The selector for the page element that will be updated
'showLoadingMessage' : true,
'loadingMessageDelay': 600, // with these defaults, if the ajax call takes > .6 seconds, a loading message will show until the ajax call completes.
'loadingMessage': 'Loading...',
'loadingMessageSelector': 'div.loading-message'
};
var History = null;
var selector = '';
var lastUrlLoaded;
var methods = {
init : function(options) { // Initializes the plugin
selector = this.selector;
if (options) {
$.extend(settings, options);
}
if (settings.pageElementToUpdateSelector === null) {
$.error('Missing required parameter pageElementToUpdateSelector on jQuery.ajaxInterceptor');
return;
}
if (methods.supported()) {
methods.bindEvents.call($(this));
}
return $(this);
},
// If the browser does not support HTML 5, the ajax interceptor does not get called
// so it degrades nicely.
supported: function() {
History = window.History;
return History.enabled;
},
bindEvents: function() {
$(selector).off('click').on('click', function(e) {
e.preventDefault();
// allow a link to override the pageElementToUpdateSelector if need be.
if ($(this).attr('pageElementToUpdateSelector')) {
settings.pageElementToUpdateSelector = $(this).attr('pageElementToUpdateSelector');
}
var url = $(this).attr('href');
History.pushState({url:url}, '', url);
return false;
});
// Bind state change for preserving back/forwards browser button functionality when loading ajax
History.Adapter.bind(window, 'statechange', function(e) {
var state = History.getState();
if (state !== null && state.url != lastUrlLoaded) {
methods.loadContent(state.url);
lastUrlLoaded = state.url;
}
});
},
// Loads the ajax content
loadContent: function(url) {
var destination = $(settings.pageElementToUpdateSelector);
$.ajax({
url:url,
data: {'page_element_to_update_selector':settings.pageElementToUpdateSelector},
method:'GET',
beforeSend: function(xhr) {
if (settings.showLoadingMessage === true) {
$.showLoadingMessage({
delay:settings.loadingMessageDelay,
message:settings.loadingMessage,
selector:settings.loadingMessageSelector
});
}
},
complete: function(xhr, status) {
if (settings.showLoadingMessage === true) {
$.hideLoadingMessage({selector:settings.loadingMessageSelector});
}
},
success: function(ajaxHtml) {
destination.html(ajaxHtml);
document.title = $(ajaxHtml).filter("div#page_title").text();
if (typeof settings.ready == 'function') { // Fire the callback
settings.ready.call($(this), url);
}
},
// on error, send the url to the browser, so the error will be clearly visible
error: function(jqXHR, textStatus, errorThrown) {
window.location = url;
}
});
}
};
$.fn.ajaxInterceptor = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
}
else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
}
else {
$.error('Method ' + method + ' does not exist on jQuery.ajaxInterceptor');
}
};
$.showLoadingMessage = function(optionsOverride){
var options = {
message:'Loading...',
delay: 0,
selector: undefined
};
$.extend(options, optionsOverride);
if (options.delay > 0) {
$(document).data("_loading_timeout", setTimeout(function() { $.showLoadingMessageNow(options)}, options.delay));
} else {
$.showLoadingMessageNow(options);
}
};
$.showLoadingMessageNow = function(options){
//if this element has delayed mask scheduled then remove it and display the new one
if ($(document).data("_loading_timeout") !== undefined) {
clearTimeout($(document).data("_loading_timeout"));
$(document).removeData("_loading_timeout");
}
$(options.selector)
.html(options.message)
.show();
};
$.hideLoadingMessage = function(options){
//if this element has delayed mask scheduled then remove it
if ($(document).data("_loading_timeout") !== undefined) {
clearTimeout($(document).data("_loading_timeout"));
$(document).removeData("_loading_timeout");
}
$(options.selector).hide();
};
})(jQuery);
#{if (!params.page_element_to_update_selector)}
<!DOCTYPE html>
<html>
<head>
<title>#{get 'title' /}</title>
<meta charset="${_response_encoding}">
#{stylesheet 'main.css' /}
#{stylesheet 'bootstrap.min.css' /}
#{/if}
#{get 'moreStyles' /}
#{if (!params.page_element_to_update_selector)}
<link rel="shortcut icon" type="image/png" href="@{'/public/images/favicon.png'}">
#{script 'jquery-1.8.1.min.js' /}
#{script 'history.js' /}
#{script 'history.adapter.jquery.js' /}
#{script 'json2.js' /}
#{script 'jquery.ajax.interceptor.js' /}
#{script 'application.js' /}
#{/if}
#{get 'moreScripts' /}
#{if (!params.page_element_to_update_selector)}
</head>
<body>
<div class="navbar">
<div class="navbar-inner">
<a class="brand" href="#">Company XYZ</a>
<ul class="nav">
<li name="heading1" class="active">
<a href="/" class="ajax-intercept">Heading 1</a>
</li>
<li name="heading2">
<a href="/page2" class="ajax-intercept">Heading 2</a>
</li>
</ul>
</div>
</div>
<div class="loading-message"></div>
<div class="container" id="content">
#{/if}
<div style="display:none" id="page_title">#{get 'title' /}</div>
#{doLayout /}
#{if (!params.page_element_to_update_selector)}
</div>
</body>
</html>
#{/if}
#{extends 'main.html' /}
#{set title:'Page 2' /}
#{set 'moreScripts'}
#{script 'script-to-load-only-on-this-page.js' /}
#{/set}
<script type="text/javascript">
$(document).ready(function() {
$("div.navbar-inner li.active").removeClass("active");
$("div.navbar-inner li[name='heading2']").addClass("active");
});
</script>
<h2>Page 2</h2>
<br/><br/>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment