intercooler
Version:
Making AJAX as easy as anchor tags
835 lines (755 loc) • 28.6 kB
JavaScript
;
////////////////////////////////////
/**
* Intercooler.js
*
* A javascript library for people who don't don't want to write a lot
* of javascript.
*
*/
var Intercooler = Intercooler || (function () {
//--------------------------------------------------
// Vars
//--------------------------------------------------
// Logging constants
var _DEBUG = 1;
var _INFO = 2;
var _WARN = 3;
var _ERROR = 4;
var _SRC_ATTRS = ['ic-src', 'ic-style-src', 'ic-attr-src', 'ic-prepend-from', 'ic-append-from'];
var _DEST_ATTRS = ['ic-get-from', 'ic-post-to', 'ic-put-to', 'ic-delete-from'];
var _remote = $;
var _urlHandlers = [];
var _logger = window.console;
var _loggingLevel = null;
var _loggingGrep = null;
var _scrollHandler = null;
//============================================================
// Utility Methods
//============================================================
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
var CryptoJS=CryptoJS||function(e,m){var p={},j=p.lib={},l=function(){},f=j.Base={extend:function(a){l.prototype=this;var c=new l;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
n=j.WordArray=f.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=m?c:4*a.length},toString:function(a){return(a||h).stringify(this)},concat:function(a){var c=this.words,q=a.words,d=this.sigBytes;a=a.sigBytes;this.clamp();if(d%4)for(var b=0;b<a;b++)c[d+b>>>2]|=(q[b>>>2]>>>24-8*(b%4)&255)<<24-8*((d+b)%4);else if(65535<q.length)for(b=0;b<a;b+=4)c[d+b>>>2]=q[b>>>2];else c.push.apply(c,q);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
32-8*(c%4);a.length=e.ceil(c/4)},clone:function(){var a=f.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],b=0;b<a;b+=4)c.push(4294967296*e.random()|0);return new n.init(c,a)}}),b=p.enc={},h=b.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],d=0;d<a;d++){var f=c[d>>>2]>>>24-8*(d%4)&255;b.push((f>>>4).toString(16));b.push((f&15).toString(16))}return b.join("")},parse:function(a){for(var c=a.length,b=[],d=0;d<c;d+=2)b[d>>>3]|=parseInt(a.substr(d,
2),16)<<24-4*(d%8);return new n.init(b,c/2)}},g=b.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],d=0;d<a;d++)b.push(String.fromCharCode(c[d>>>2]>>>24-8*(d%4)&255));return b.join("")},parse:function(a){for(var c=a.length,b=[],d=0;d<c;d++)b[d>>>2]|=(a.charCodeAt(d)&255)<<24-8*(d%4);return new n.init(b,c)}},r=b.Utf8={stringify:function(a){try{return decodeURIComponent(escape(g.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return g.parse(unescape(encodeURIComponent(a)))}},
k=j.BufferedBlockAlgorithm=f.extend({reset:function(){this._data=new n.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=r.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,b=c.words,d=c.sigBytes,f=this.blockSize,h=d/(4*f),h=a?e.ceil(h):e.max((h|0)-this._minBufferSize,0);a=h*f;d=e.min(4*a,d);if(a){for(var g=0;g<a;g+=f)this._doProcessBlock(b,g);g=b.splice(0,a);c.sigBytes-=d}return new n.init(g,d)},clone:function(){var a=f.clone.call(this);
a._data=this._data.clone();return a},_minBufferSize:0});j.Hasher=k.extend({cfg:f.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){k.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(c,b){return(new a.init(b)).finalize(c)}},_createHmacHelper:function(a){return function(b,f){return(new s.HMAC.init(a,
f)).finalize(b)}}});var s=p.algo={};return p}(Math);
(function(){var e=CryptoJS,m=e.lib,p=m.WordArray,j=m.Hasher,l=[],m=e.algo.SHA1=j.extend({_doReset:function(){this._hash=new p.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(f,n){for(var b=this._hash.words,h=b[0],g=b[1],e=b[2],k=b[3],j=b[4],a=0;80>a;a++){if(16>a)l[a]=f[n+a]|0;else{var c=l[a-3]^l[a-8]^l[a-14]^l[a-16];l[a]=c<<1|c>>>31}c=(h<<5|h>>>27)+j+l[a];c=20>a?c+((g&e|~g&k)+1518500249):40>a?c+((g^e^k)+1859775393):60>a?c+((g&e|g&k|e&k)-1894007588):c+((g^e^
k)-899497514);j=k;k=e;e=g<<30|g>>>2;g=h;h=c}b[0]=b[0]+h|0;b[1]=b[1]+g|0;b[2]=b[2]+e|0;b[3]=b[3]+k|0;b[4]=b[4]+j|0},_doFinalize:function(){var f=this._data,e=f.words,b=8*this._nDataBytes,h=8*f.sigBytes;e[h>>>5]|=128<<24-h%32;e[(h+64>>>9<<4)+14]=Math.floor(b/4294967296);e[(h+64>>>9<<4)+15]=b;f.sigBytes=4*e.length;this._process();return this._hash},clone:function(){var e=j.clone.call(this);e._hash=this._hash.clone();return e}});e.SHA1=j._createHelper(m);e.HmacSHA1=j._createHmacHelper(m)})();
function fp(elt) {
return CryptoJS.SHA1(elt).toString();
}
function levelPrefix(level) {
if(level == _DEBUG) {
return "IC DEBUG: ";
} else if(level == _INFO) {
return "IC INFO: ";
} else if(level == _WARN) {
return "IC WARN: ";
} else if(level == _ERROR) {
return "IC ERROR: ";
} else {
return "IC UNKNOWN: ";
}
}
function log(msg, level) {
var srcLevel = level || _INFO;
var targetLevel = _loggingLevel || _ERROR;
if (_logger && (srcLevel >= targetLevel) && (_loggingGrep == null || _loggingGrep.test(msg))) {
_logger.log(levelPrefix(srcLevel), msg);
}
}
function uuid() {
var d = new Date().getTime();
//noinspection UnnecessaryLocalVariableJS
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c == 'x' ? r : (r & 0x7 | 0x8)).toString(16);
});
return uuid;
}
function icSelectorFor(elt) {
return "[ic-id='" + elt.attr("ic-id") + "']";
}
function findByICId(icId) {
return $("[ic-id='" + icId + "']");
}
function findById(x) {
return $("[id='" + x + "']");
}
function parseInterval(str) {
log("POLL: Parsing interval string " + str, _DEBUG);
if (str == "null" || str == "false" || str == "") {
return null;
} else if (str.lastIndexOf("ms") == str.length - 2) {
return parseInt(str.substr(0, str.length - 2));
} else if (str.lastIndexOf("s") == str.length - 1) {
return parseInt(str.substr(0, str.length - 1)) * 1000;
} else {
return 1000;
}
}
//============================================================
// Request/Parameter/Include Processing
//============================================================
function getTarget(elt) {
if(elt.attr('ic-target')) {
return $(elt.attr('ic-target'));
} else {
return elt;
}
}
function processHeaders(elt, xhr, pop) {
elt.trigger("ic.beforeHeaders", elt, xhr);
if (xhr.getResponseHeader("X-IC-Refresh")) {
var pathsToRefresh = xhr.getResponseHeader("X-IC-Refresh").split(",");
log("IC HEADER: refreshing " + pathsToRefresh, _DEBUG);
$.each(pathsToRefresh, function (i, str) {
refreshDependencies(str.replace(/ /g, ""), elt);
});
}
if (xhr.getResponseHeader("X-IC-Script")) {
log("IC HEADER: evaling " + xhr.getResponseHeader("X-IC-Script"), _DEBUG);
eval(xhr.getResponseHeader("X-IC-Script"));
}
if (xhr.getResponseHeader("X-IC-Redirect")) {
log("IC HEADER: redirecting to " + xhr.getResponseHeader("X-IC-Redirect"), _DEBUG);
window.location = xhr.getResponseHeader("X-IC-Redirect");
}
if (xhr.getResponseHeader("X-IC-CancelPolling") == "true") {
cancelPolling(elt);
}
if (xhr.getResponseHeader("X-IC-Open")) {
log("IC HEADER: opening " + xhr.getResponseHeader("X-IC-Open"), _DEBUG);
window.open(xhr.getResponseHeader("X-IC-Open"));
}
if (xhr.getResponseHeader("X-IC-SetLocation") && pop != true) {
log("IC HEADER: pushing " + xhr.getResponseHeader("X-IC-SetLocation"), _DEBUG);
_historySupport.pushUrl(xhr.getResponseHeader("X-IC-SetLocation"), elt);
}
if(xhr.getResponseHeader("X-IC-Transition")) {
log("IC HEADER: setting transition to " + xhr.getResponseHeader("X-IC-Transition"), _DEBUG);
var target = getTarget(elt);
target.data("ic-tmp-transition", xhr.getResponseHeader("X-IC-Transition"));
}
if (xhr.getResponseHeader("X-IC-Remove")) {
log("IC HEADER REMOVE COMMAND");
if (elt) {
var target = getTarget(elt);
log("IC REMOVING: " + target.html(), _DEBUG);
if (isTransition(target, "none")) {
target.remove();
} else {
target.fadeOut('fast', function () {
target.remove();
});
}
}
}
elt.trigger("ic.afterHeaders", elt, xhr);
return true;
}
function handleTestResponse(elt, success, returnVal) {
var spinner = findSpinner(elt);
spinner.fadeIn('fast');
var headers = {};
if(returnVal && returnVal.headers) {
headers = returnVal.headers;
}
var body = "";
if (returnVal) {
if(typeof returnVal == 'string' || returnVal instanceof String) {
body = returnVal;
} else if(typeof returnVal.body == 'string' || returnVal.body instanceof String) {
body = returnVal.body;
}
}
processHeaders(elt, {
getResponseHeader: function (key) {
return headers[key];
}
});
success(body, "", elt);
setTimeout(function(){ spinner.fadeOut('fast'); }, 800);
}
function beforeRequest(elt) {
elt.addClass('disabled');
}
function afterRequest(elt) {
elt.removeClass('disabled')
}
function replaceOrAddMethod(data, actualMethod) {
var regex = /(&|^)_method=[^&]*/;
var content = "&_method=" + actualMethod;
if(regex.test(data)) {
return data.replace(regex, content)
} else {
return data + "&" + content;
}
}
function handleRemoteRequest(elt, type, url, data, success) {
data = replaceOrAddMethod(data, type);
var pop = data.indexOf("&ic-handle-pop=true") >= 0;
for (var i = 0, l = _urlHandlers.length; i < l; i++) {
var handler = _urlHandlers[i];
var returnVal = null;
if(handler.url == null || new RegExp(handler.url.replace(/\*/g, ".*").replace(/\//g, "\\/")).test(url)) {
if (type == "GET" && handler.get) {
if(handler.get) {
returnVal = handler.get(url, parseParams(data));
}
handleTestResponse(elt, success, returnVal)
}
if (type == "POST") {
if(handler.post) {
returnVal = handler.post(url, parseParams(data));
}
handleTestResponse(elt, success, returnVal)
}
if (type == "PUT") {
if(handler.put) {
//noinspection JSCheckFunctionSignatures
returnVal = handler.put(url, parseParams(data));
}
handleTestResponse(elt, success, returnVal)
}
if (type == "DELETE") {
if(handler.delete) {
returnVal = handler.delete(url, parseParams(data));
}
handleTestResponse(elt, success, returnVal)
}
return;
}
}
beforeRequest(elt);
// Spinner support
var spinner = findSpinner(elt);
spinner.fadeIn('fast');
_remote.ajax({
type: type,
url: url,
data: data,
dataType: 'text',
beforeSend : function(){
elt.trigger("ic.beforeSend", elt, data);
},
success: function (data, textStatus, xhr) {
elt.trigger("ic.success", elt, data, textStatus, xhr);
var target = getTarget(elt);
target.data("ic-tmp-transition", elt.attr('ic-transition')); // copy transition
if (processHeaders(elt, xhr, pop)) {
success(data, textStatus, elt, xhr);
}
target.data("ic-tmp-transition", null);
},
error: function (req, status, str) {
elt.trigger("ic.error", elt, req, status, str);
log("An error occurred: " + str, _ERROR);
},
complete : function(){
elt.trigger("ic.complete", elt, data);
if(spinner.length > 0) {
spinner.fadeOut('fast', function(){
afterRequest(elt);
});
} else {
afterRequest(elt);
}
}
})
}
function findSpinner(elt) {
var child = null;
if ($(elt).attr('ic-indicator')) {
child = $($(elt).attr('ic-indicator')).first();
} else {
var nearestParentWithSpinner = $(elt).closest("[ic-indicator]");
if(nearestParentWithSpinner.length > 0) {
child = $(nearestParentWithSpinner.first().attr('ic-indicator')).first();
} else {
child = $(elt).find(".ic-indicator").first();
}
}
return child;
}
// Taken from https://gist.github.com/kares/956897
function parseParams(str) {
var re = /([^&=]+)=?([^&]*)/g;
var decode = function (str) {
return decodeURIComponent(str.replace(/\+/g, ' '));
};
var params = {}, e;
if (str) {
if (str.substr(0, 1) == '?') {
str = str.substr(1);
}
while (e = re.exec(str)) {
var k = decode(e[1]);
var v = decode(e[2]);
if (params[k] !== undefined) {
if (!$.isArray(params[k])) {
params[k] = [params[k]];
}
params[k].push(v);
} else {
params[k] = v;
}
}
}
return params;
}
function processIncludes(str) {
var returnString = "";
$(str).each(function(){
returnString += "&" + $(this).serialize();
});
return returnString;
}
function getParametersForElement(elt) {
var target = getTarget(elt);
var str = "ic-request=true";
// if the element is in a form, include the entire form
if(elt.closest('form').length > 0) {
str += "&" + elt.closest('form').serialize();
} else { // otherwise include the element
str += "&" + elt.serialize();
}
if (elt.attr('id')) {
str += "&ic-element-id=" + elt.attr('id');
}
if (elt.attr('name')) {
str += "&ic-element-name=" + elt.attr('name');
}
if (target.attr('ic-id')) {
str += "&ic-id=" + target.attr('ic-id');
}
if (target.attr('ic-last-refresh')) {
str += "&ic-last-refresh=" + target.attr('ic-last-refresh');
}
if (target.attr('ic-fingerprint')) {
str += "&ic-fingerprint=" + target.attr('ic-fingerprint');
}
if (elt.attr('ic-include')) {
str += processIncludes(elt.attr('ic-include'));
}
log("PARAMS: Returning parameters " + str + " for " + elt);
return str;
}
//============================================================
// Tree Processing
//============================================================
function maybeSetIntercoolerInfo(elt) {
var target = getTarget(elt);
getIntercoolerId(target);
maybeSetIntercoolerMetadata(target);
}
function updateIntercoolerMetaData(elt) {
elt.attr('ic-fingerprint', fp(elt.text()));
elt.attr('ic-last-refresh', new Date().getTime());
}
function maybeSetIntercoolerMetadata(elt) {
if (!elt.attr('ic-fingerprint')) {
updateIntercoolerMetaData(elt);
}
}
function getIntercoolerId(elt) {
if (!elt.attr('ic-id')) {
elt.attr('ic-id', uuid());
}
return elt.attr('ic-id');
}
function withAttrs(attributes, func) {
$.each(attributes, function(i, attr) {
func(attr);
});
}
function processSources(elt) {
withAttrs(_SRC_ATTRS, function (attr) {
if ($(elt).is("[" + attr + "]")) {
maybeSetIntercoolerInfo($(elt));
}
$(elt).find("[" + attr + "]").each(function () {
maybeSetIntercoolerInfo($(this));
});
});
}
function startPolling(elt) {
if(elt.data('ic-poll-interval-id') == null) {
var interval = parseInterval(elt.attr('ic-poll'));
if(interval != null) {
var selector = icSelectorFor(elt);
log("POLL: Starting poll for element " + selector, _DEBUG);
var timerId = setInterval(function () {
var target = $(selector);
elt.trigger("ic.onPoll", target);
if (target.length == 0) {
log("POLL: Clearing poll for element " + selector, _DEBUG);
clearTimeout(timerId);
} else {
updateElement(target);
}
}, interval);
elt.data('ic-poll-interval-id', timerId);
}
}
}
function cancelPolling(elt) {
if(elt.data('ic-poll-interval-id') != null) {
clearTimeout(elt.data('ic-poll-interval-id'));
}
}
function processPolling(elt) {
if ($(elt).is('[ic-poll]')) {
maybeSetIntercoolerInfo($(elt));
startPolling(elt);
}
$(elt).find('[ic-poll]').each(function () {
maybeSetIntercoolerInfo($(this));
startPolling($(this));
});
}
function isDependent(src, dest) {
return (src && dest) && (dest.indexOf(src) == 0 || src.indexOf(dest) == 0);
}
function refreshDependencies(dest, src) {
withAttrs(_SRC_ATTRS, function (attr) {
$('[' + attr + ']').each(function () {
if (isDependent(dest, $(this).attr(attr))) {
if(src == null || $(src)[0] != $(this)[0]) {
updateElement($(this));
}
} else if (isDependent(dest, $(this).attr('ic-deps')) || $(this).attr('ic-deps') == "*") {
if(src == null || $(src)[0] != $(this)[0]) {
updateElement($(this));
}
}
});
});
}
function verbFor(elt, attr) {
if(elt.attr('ic-verb')) {
return elt.attr('ic-verb').toUpperCase();
}
if(attr == "ic-post-to") {
return "POST";
} else if(attr == "ic-put-to") {
return "PUT";
} else if (attr == "ic-delete-from") {
return "DELETE";
} else if (attr == "ic-get-from") {
return "GET";
} else {
return "POST";
}
}
function initButtonDestination(elt, attr) {
var destinationStr = $(elt).attr(attr);
$(elt).click(function (event) {
event.preventDefault();
handleRemoteRequest(elt, verbFor(elt, attr), destinationStr, getParametersForElement(elt),
function (data) {
processICResponse(data, elt);
refreshDependencies(destinationStr);
})
});
}
function initInputDestination(elt, attr) {
var destinationStr = $(elt).attr(attr);
$(elt).change(function () {
handleRemoteRequest(elt, verbFor(elt, attr), destinationStr, getParametersForElement(elt),
function (data) {
processICResponse(data, elt);
refreshDependencies(destinationStr);
})
});
}
function initDestination(elt, attr) {
if ($(elt).is('button, a, div, span')) {
initButtonDestination(elt, attr);
} else if ($(elt).is('input, select')) {
initInputDestination(elt, attr);
}
}
function processDestinations(elt) {
withAttrs(_DEST_ATTRS, function(attr){
if ($(elt).is('[' + attr + ']')) {
maybeSetIntercoolerInfo($(elt));
initDestination(elt, attr);
}
$(elt).find('[' + attr + ']').each(function () {
maybeSetIntercoolerInfo($(this));
initDestination($(this), attr);
});
});
}
function isScrolledIntoView(elem) {
var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height();
var elemTop = $(elem).offset().top;
var elemBottom = elemTop + $(elem).height();
var inViewport = ((elemBottom >= docViewTop) && (elemTop <= docViewBottom)
&& (elemBottom <= docViewBottom) && (elemTop >= docViewTop));
return inViewport;
}
function handleLoadOn(elt) {
if ($(elt).attr('ic-load-on')) {
if($(elt).attr('ic-load-on') == 'load') {
updateElement(elt);
} else if($(elt).attr('ic-load-on') == 'scrolled-into-view') {
if (_scrollHandler == null) {
_scrollHandler = function () {
$("[ic-load-on='scrolled-into-view']").each(function () {
if (isScrolledIntoView($(this)) && $(this).data('ic-scrolled-into-view-loaded') != true) {
$(this).data('ic-scrolled-into-view-loaded', true);
updateElement($(this));
}
})
};
$(window).scroll(_scrollHandler);
}
setTimeout(function(){$(window).trigger('scroll');}, 100); // Trigger a scroll in case element is already viewable
} else {
$(elt).on($(elt).attr('ic-load-on'), function(){
updateElement(elt);
});
}
}
}
function processLoadOn(elt) {
handleLoadOn(elt);
$(elt).find('[ic-load-on]').each(function () {
handleLoadOn($(this));
});
}
function processNodes(elt) {
processSources(elt);
processPolling(elt);
processDestinations(elt);
processLoadOn(elt)
}
function isTransition(target, transitionName) {
return target.attr('ic-transition') == transitionName ||
target.data('ic-tmp-transition') == transitionName;
}
function processICResponse(data, elt) {
if (data && /\S/.test(data)) {
log("IC RESPONSE: Received: " + data, _DEBUG);
var target = getTarget(elt);
var dummy = $("<div></div>").html(data);
if (fp(dummy.text()) != target.attr('ic-fingerprint') || target.attr('ic-always-update') == 'true') {
if (isTransition(target, 'none')) {
target.html(data);
updateIntercoolerMetaData(target);
processNodes(target);
} else {
target.fadeOut('fast', function () {
target.html(data);
updateIntercoolerMetaData(target);
processNodes(target);
target.fadeIn('fast');
});
}
}
dummy.remove();
}
}
function updateElement(element) {
var elt = element;
if (elt.attr('ic-src')) {
handleRemoteRequest(element, "GET", elt.attr('ic-src'), getParametersForElement(elt),
function (data) {
processICResponse(data, elt);
});
} else if (elt.attr('ic-prepend-from')) {
handleRemoteRequest(element, "GET", elt.attr('ic-prepend-from'), getParametersForElement(elt),
function (data) {
var elts = $(data);
elts.hide();
var target = getTarget(elt);
log("target is ");
log(target);
target.prepend(elts);
elts.fadeIn();
processNodes(elts);
if (target.attr('ic-limit-children')) {
var limit = parseInt(elt.attr('ic-limit-children'));
if (elt.children().length > limit) {
target.children().slice(limit, target.children().length).remove();
}
}
});
} else if (elt.attr('ic-append-from')) {
handleRemoteRequest(element, "GET", elt.attr('ic-append-from'), getParametersForElement(elt),
function (data) {
var elts = $(data);
elts.hide();
var target = getTarget(elt);
target.append(elts);
elts.fadeIn();
processNodes(elts);
if (target.attr('ic-limit-children')) {
var limit = parseInt(elt.attr('ic-limit-children'));
if (target.children().length > limit) {
target.children().slice(0, target.children().length - limit).remove();
}
}
});
} else if (elt.attr('ic-style-src')) {
var styleSrc = elt.attr('ic-style-src').split(":");
handleRemoteRequest(element, "GET", styleSrc[1], getParametersForElement(elt),
function (data) {
elt.css(styleSrc[0], data);
});
} else if (elt.attr('ic-attr-src')) {
var attrSrc = elt.attr('ic-attr-src').split(":");
handleRemoteRequest(element, "GET", attrSrc[1], getParametersForElement(elt),
function (data) {
elt.attr(attrSrc[0], data);
});
}
}
//============================================================
// History Support
//============================================================
var _historySupport = {
/* vars */
stateCache: null,
popping: false,
/* functions */
getRestorationURL: function (elt) {
if (elt.attr('ic-restore-from')) {
return elt.attr('ic-restore-from');
} else {
return window.location.pathname + window.location.search + window.location.hash;
}
},
onPageLoad: function () {
_historySupport.stateCache = {"ic-setlocation": true,
"restore-from": window.location.pathname + window.location.search + window.location.hash,
"timestamp": new Date().getTime()
};
if (window.onpopstate == null || window.onpopstate['ic-on-pop-state-handler'] != true) {
var currentOnPopState = window.onpopstate;
window.onpopstate = function(event) {
if(!_historySupport.handlePop(event)){
if(currentOnPopState) {
currentOnPopState(event);
}
}
};
window.onpopstate['ic-on-pop-state-handler'] = true;
}
},
pushUrl: function (url, elt) {
log("IC HISTORY: pushing location " + url, _DEBUG);
var target = getTarget(elt);
var id = target.attr('id');
if(id == null) {
log("To support history for a given element, you must have a valid id attribute on the element", _ERROR);
return;
}
_historySupport.initHistory(elt);
var data = {
"ic-setlocation": true,
"id-to-restore": id.toString(),
"restore-from": url,
"timestamp": new Date().getTime()
};
elt.trigger("ic.pushUrl", target, data);
window.history.pushState(data, "", url);
},
initHistory: function (elt) {
if(_historySupport.stateCache) {
var target = getTarget(elt);
var id = target.attr('id');
_historySupport.stateCache["id-to-restore"] = id.toString();
window.history.replaceState(_historySupport.stateCache);
_historySupport.stateCache = null;
}
},
handlePop: function (event) {
var data = event.state;
if (data && data['ic-setlocation']) {
var elt = findById(data["id-to-restore"]);
var params = getParametersForElement(elt);
params += "&ic-handle-pop=true"
handleRemoteRequest(elt, "GET", data["restore-from"], params,
function (data) {
elt.trigger("ic.handlePop", elt, data);
processICResponse(data, elt);
});
return true;
}
return false;
}
};
/**
* Process initial nodes
*/
$(function () {
processNodes('body');
_historySupport.onPageLoad();
});
/**
* Public API
*/
return {
/* ===================================================
* Core API
* =================================================== */
refresh: function (elt) {
updateElement(elt);
return Intercooler;
},
/* ===================================================
* Mock Testing API
* =================================================== */
addURLHandler: function (handler) {
if (!handler.url) {
throw "Handlers must include a URL pattern"
}
_urlHandlers.push(handler);
return Intercooler;
},
setRemote: function (remote) {
_remote = remote;
return Intercooler;
},
/* ===================================================
* Logging API
* =================================================== */
setLogger: function (logger, level, grep) {
_logger = logger;
if (level) {
_loggingLevel = level;
}
if (grep) {
_loggingGrep = grep;
}
return Intercooler;
},
log: function (msg, level) {
log(msg, level);
return Intercooler;
},
/* LOGGING LEVELS */
setLogLevel: function (level) {
_loggingLevel = level;
return Intercooler;
},
logLevels: {
DEBUG: _DEBUG,
INFO: _INFO,
WARNING: _WARN,
ERROR: _ERROR
}
}
})();