brutaldon-interfaccia-web-m.../brutaldon/static/js/intercooler.js

2082 lines
65 KiB
JavaScript

(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module unless amdModuleId is set
define(["jquery"], function (a0) {
return (root['Intercooler'] = factory(a0));
});
} else if (typeof module === 'object' && module.exports) {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory(require("jquery"));
} else {
root['Intercooler'] = factory(root["jQuery"]);
}
}(this, function ($) {
////////////////////////////////////
/**
* Intercooler.js - there is no need to be upset.
*/
var Intercooler = Intercooler || (function() {
'use strict'; // inside function for better merging
// work around zepto build issue TODO - fix me
if((typeof Zepto !== "undefined") && ($ == null)) {
window["$"] = Zepto
}
//--------------------------------------------------
// Vars
//--------------------------------------------------
var USE_DATA = $('meta[name="intercoolerjs:use-data-prefix"]').attr("content") == "true";
var USE_ACTUAL_HTTP_METHOD = $('meta[name="intercoolerjs:use-actual-http-method"]').attr("content") == "true";
var _MACROS = $.map(['ic-get-from', 'ic-post-to', 'ic-put-to', 'ic-patch-to', 'ic-delete-from',
'ic-style-src', 'ic-attr-src', 'ic-prepend-from', 'ic-append-from', 'ic-action'],
function(elt){ return fixICAttributeName(elt) });
var _scrollHandler = null;
var _UUID = 1;
var _readyHandlers = [];
var _isDependentFunction = function(src, dest) {
if (!src || !dest) {
return false;
}
// For two urls to be considered dependant, either one must contain all
// of the path arguments the other has, like so:
// - chomp off everything after ? or #. This is a design decision, so this
// function will fail to determine dependencies for sites that store
// their model IDs in query/hash params. If your usecase is not covered
// by this you need to implement this function yourself by overriding
// Intercooler.setIsDependentFunction(function(src, dest) { return bool; });
// - split by / to get the individual path elements, clear out empty values,
// then simply compare them
var asrc = src.split(/[\?#]/, 1)[0].split("/").filter(function(e) {
return e != "";
});
var adest = dest.split(/[\?#]/, 1)[0].split("/").filter(function(e) {
return e != "";
});
// ignore purely local tags (local transport)
if (asrc == "" || adest == "") {
return false;
}
return adest.slice(0, asrc.length).join("/") == asrc.join("/") ||
asrc.slice(0, adest.length).join("/") == adest.join("/");
};
//============================================================
// Base Swap Definitions
//============================================================
function remove(elt) {
elt.remove();
}
function showIndicator(elt) {
if (elt.closest('.ic-use-transition').length > 0) {
elt.data('ic-use-transition', true);
elt.removeClass('ic-use-transition');
} else {
elt.show();
}
}
function hideIndicator(elt) {
if (elt.data('ic-use-transition') || elt.data('ic-indicator-cleared')) {
elt.data('ic-use-transition', null);
elt.addClass('ic-use-transition');
elt.data('ic-indicator-cleared', true);
} else {
elt.hide();
}
}
function fixICAttributeName(s) {
if (USE_DATA) {
return 'data-' + s;
} else {
return s;
}
}
function getICAttribute(element, attributeName) {
return element.attr(fixICAttributeName(attributeName));
}
function setICAttribute(element, attributeName, attributeValue) {
element.attr(fixICAttributeName(attributeName), attributeValue);
}
function prepend(parent, responseContent) {
try {
parent.prepend(responseContent);
} catch (e) {
log(parent, formatError(e), "ERROR");
}
if (getICAttribute(parent, 'ic-limit-children')) {
var limit = parseInt(getICAttribute(parent, 'ic-limit-children'));
if (parent.children().length > limit) {
parent.children().slice(limit, parent.children().length).remove();
}
}
}
function append(parent, responseContent) {
try {
parent.append(responseContent);
} catch (e) {
log(parent, formatError(e), "ERROR");
}
if (getICAttribute(parent, 'ic-limit-children')) {
var limit = parseInt(getICAttribute(parent, 'ic-limit-children'));
if (parent.children().length > limit) {
parent.children().slice(0, parent.children().length - limit).remove();
}
}
}
//============================================================
// Utility Methods
//============================================================
function triggerEvent(elt, event, args){
if($.zepto) {
event = event.split(".").reverse().join(":");
}
elt.trigger(event, args);
}
function log(elt, msg, level) {
if (elt == null) {
elt = $('body');
}
triggerEvent(elt, "log.ic", [msg, level, elt]);
if (level == "ERROR") {
if (window.console) {
window.console.log("Intercooler Error : " + msg);
}
var errorUrl = closestAttrValue($('body'), 'ic-post-errors-to');
if (errorUrl) {
$.post(errorUrl, {'error': msg});
}
}
}
function uuid() {
return _UUID++;
}
function icSelectorFor(elt) {
return getICAttributeSelector("ic-id='" + getIntercoolerId(elt) + "'");
}
function parseInterval(str) {
log(null, "POLL: Parsing interval string " + str, 'DEBUG');
if (str == "null" || str == "false" || str == "") {
return null;
} else if (str.lastIndexOf("ms") == str.length - 2) {
return parseFloat(str.substr(0, str.length - 2));
} else if (str.lastIndexOf("s") == str.length - 1) {
return parseFloat(str.substr(0, str.length - 1)) * 1000;
} else {
return 1000;
}
}
function getICAttributeSelector(attribute) {
return "[" + fixICAttributeName(attribute) + "]";
}
function initScrollHandler() {
if (_scrollHandler == null) {
_scrollHandler = function() {
$(getICAttributeSelector("ic-trigger-on='scrolled-into-view'")).each(function() {
var _this = $(this);
if (isScrolledIntoView(getTriggeredElement(_this)) && _this.data('ic-scrolled-into-view-loaded') != true) {
_this.data('ic-scrolled-into-view-loaded', true);
fireICRequest(_this);
}
});
};
$(window).scroll(_scrollHandler);
}
}
function currentUrl() {
return window.location.pathname + window.location.search + window.location.hash;
}
// taken from turbolinks.js
function createDocument(html) {
var doc = null;
if (/<(html|body)/i.test(html)) {
doc = document.documentElement.cloneNode();
doc.innerHTML = html;
} else {
doc = document.documentElement.cloneNode(true);
doc.querySelector('body').innerHTML = html;
}
return $(doc);
}
//============================================================
// Request/Parameter/Include Processing
//============================================================
function getTarget(elt) {
return getTargetImpl(elt, 'ic-target')
}
function getTargetImpl(elt, attibuteName) {
var closest = $(elt).closest(getICAttributeSelector(attibuteName));
var targetValue = getICAttribute(closest, attibuteName);
if (targetValue == 'this') {
return closest;
} else if (targetValue && targetValue.indexOf('this.') != 0) {
if (targetValue.indexOf('closest ') == 0) {
return elt.closest(targetValue.substr(8));
} else if (targetValue.indexOf('find ') == 0) {
return elt.find(targetValue.substr(5));
} else {
return $(targetValue);
}
} else {
return elt;
}
}
function processHeaders(elt, xhr) {
elt = $(elt);
triggerEvent(elt, "beforeHeaders.ic", [elt, xhr]);
log(elt, "response headers: " + xhr.getAllResponseHeaders(), "DEBUG");
var target = null;
// set page title by header
if (xhr.getResponseHeader("X-IC-Title")) {
document.title = xhr.getResponseHeader("X-IC-Title");
}
if (xhr.getResponseHeader("X-IC-Title-Encoded")) {
var decodedTitle = decodeURIComponent((xhr.getResponseHeader("X-IC-Title-Encoded")).replace(/\+/g, '%20'));
document.title = decodedTitle;
}
if (xhr.getResponseHeader("X-IC-Refresh")) {
var pathsToRefresh = xhr.getResponseHeader("X-IC-Refresh").split(",");
log(elt, "X-IC-Refresh: refreshing " + pathsToRefresh, "DEBUG");
$.each(pathsToRefresh, function(i, str) {
refreshDependencies(str.replace(/ /g, ""), elt);
});
}
if (xhr.getResponseHeader("X-IC-Script")) {
log(elt, "X-IC-Script: evaling " + xhr.getResponseHeader("X-IC-Script"), "DEBUG");
globalEval(xhr.getResponseHeader("X-IC-Script"), [["elt", elt]]);
}
if (xhr.getResponseHeader("X-IC-Redirect")) {
log(elt, "X-IC-Redirect: redirecting to " + xhr.getResponseHeader("X-IC-Redirect"), "DEBUG");
window.location = xhr.getResponseHeader("X-IC-Redirect");
}
if (xhr.getResponseHeader("X-IC-CancelPolling") == "true") {
cancelPolling(elt.closest(getICAttributeSelector('ic-poll')));
}
if (xhr.getResponseHeader("X-IC-ResumePolling") == "true") {
var pollingElt = elt.closest(getICAttributeSelector('ic-poll'));
setICAttribute(pollingElt, 'ic-pause-polling', null);
startPolling(pollingElt);
}
if (xhr.getResponseHeader("X-IC-SetPollInterval")) {
var pollingElt = elt.closest(getICAttributeSelector('ic-poll'));
cancelPolling(pollingElt);
setICAttribute(pollingElt, 'ic-poll', xhr.getResponseHeader("X-IC-SetPollInterval"));
startPolling(pollingElt);
}
if (xhr.getResponseHeader("X-IC-Open")) {
log(elt, "X-IC-Open: opening " + xhr.getResponseHeader("X-IC-Open"), "DEBUG");
window.open(xhr.getResponseHeader("X-IC-Open"));
}
var triggerValue = xhr.getResponseHeader("X-IC-Trigger");
if (triggerValue) {
log(elt, "X-IC-Trigger: found trigger " + triggerValue, "DEBUG");
target = getTarget(elt);
// Deprecated API
if (xhr.getResponseHeader("X-IC-Trigger-Data")) {
var triggerArgs = $.parseJSON(xhr.getResponseHeader("X-IC-Trigger-Data"));
triggerEvent(target, triggerValue, triggerArgs);
} else {
if (triggerValue.indexOf("{") >= 0) {
$.each($.parseJSON(triggerValue), function(event, args) {
triggerEvent(target, event, args);
});
} else {
triggerEvent(target, triggerValue, []);
}
}
}
var localVars = xhr.getResponseHeader("X-IC-Set-Local-Vars");
if (localVars) {
$.each($.parseJSON(localVars), function(key, val) {
localStorage.setItem(key, val);
});
}
if (xhr.getResponseHeader("X-IC-Remove")) {
if (elt) {
var removeVal = xhr.getResponseHeader("X-IC-Remove");
removeVal += ''; // normalize as string for zapto
var removeValAsInterval = parseInterval(removeVal);
log(elt, "X-IC-Remove header found.", "DEBUG");
target = getTarget(elt);
if(removeVal == "true" || removeValAsInterval == null) {
remove(target);
} else {
target.addClass('ic-removing');
setTimeout(function () {
remove(target);
}, removeValAsInterval);
}
}
}
triggerEvent(elt, "afterHeaders.ic", [elt, xhr]);
return true;
}
function beforeRequest(elt) {
elt.addClass('disabled');
elt.addClass('ic-request-in-flight');
elt.data('ic-request-in-flight', true);
}
function requestCleanup(indicator, globalIndicator, elt) {
if (indicator.length > 0) {
hideIndicator(indicator);
}
if (globalIndicator.length > 0) {
hideIndicator(globalIndicator);
}
elt.removeClass('disabled');
elt.removeClass('ic-request-in-flight');
elt.data('ic-request-in-flight', false);
if (elt.data('ic-next-request')) {
elt.data('ic-next-request')["req"]();
elt.data('ic-next-request', null);
}
}
function replaceOrAddMethod(data, actualMethod) {
if ($.type(data) === "string") {
var regex = /(&|^)_method=[^&]*/;
var content = "&_method=" + actualMethod;
if (regex.test(data)) {
return data.replace(regex, content)
} else {
return data + content;
}
} else {
data.append("_method", actualMethod);
return data;
}
}
/*
Is the provided text a valid JavaScript identifier path?
We should also probably check if an identifier is a JavaScript keyword here.
*/
function isIdentifier(txt) {
return /^[$A-Z_][0-9A-Z_$]*$/i.test(txt);
}
/*
Evaluate a script snippet provided by the user.
script: A string. If this is an identifier, it is assumed to be a callable, retrieved from the
global namespace, and called. If it is a compound statement, it is evaluated using eval.
args: A list of [name, value] tuples. These will be injected into the namespace of evaluated
scripts, and be passed as arguments to safe evaluations.
*/
// It would be nice to use the spread operator here globalEval(script, ...args) - but it breaks
// uglify and isn't supported in some older browsers.
function globalEval(script, args) {
var names = [];
var values = [];
if (args) {
for (var i = 0; i < args.length; i++) {
names.push(args[i][0]);
values.push(args[i][1]);
}
}
if (isIdentifier(script)) {
return window[script].apply(this, values);
} else {
var outerfunc = window["eval"].call(
window,
'(function (' + names.join(", ") + ') {' + script + '})'
);
return outerfunc.apply(this, values);
}
}
function closestAttrValue(elt, attr) {
var closestElt = $(elt).closest(getICAttributeSelector(attr));
if (closestElt.length > 0) {
return getICAttribute(closestElt, attr);
} else {
return null;
}
}
function formatError(e) {
var msg = e.toString() + "\n";
try {
msg += e.stack;
} catch (e) {
// ignore
}
return msg;
}
function getLocalURL(baseURL, paramsToPush, data) {
if (paramsToPush) {
baseURL = baseURL + "?";
var vars = {};
data.replace(/([^=&]+)=([^&]*)/gi, function(m,key,value) {
vars[key] = value;
});
$(paramsToPush.split(",")).each(function(index) {
var param = $.trim(this);
var value = vars[param] || "";
baseURL += (index == 0) ? "" : "&";
baseURL += param + "=" + value;
});
}
return baseURL;
}
function handleRemoteRequest(elt, type, url, data, success) {
beforeRequest(elt);
data = replaceOrAddMethod(data, type);
// Global spinner support
var globalIndicator = findGlobalIndicator(elt);
if (globalIndicator && globalIndicator.length > 0) {
showIndicator(globalIndicator);
}
// Spinner support
var indicator = findIndicator(elt);
if (indicator.length > 0) {
showIndicator(indicator);
}
var requestId = uuid();
var requestStart = new Date();
var actualRequestType;
if(USE_ACTUAL_HTTP_METHOD) {
actualRequestType = type;
} else {
actualRequestType = type == 'GET' ? 'GET' : 'POST';
}
var ajaxSetup = {
type: actualRequestType,
url: url,
data: data,
dataType: 'text',
headers: {
"Accept": "text/html-partial, */*; q=0.9",
"X-IC-Request": true,
"X-HTTP-Method-Override": type
},
beforeSend: function(xhr, settings) {
triggerEvent(elt, "beforeSend.ic", [elt, data, settings, xhr, requestId]);
log(elt, "before AJAX request " + requestId + ": " + type + " to " + url, "DEBUG");
var onBeforeSend = closestAttrValue(elt, 'ic-on-beforeSend');
if (onBeforeSend) {
globalEval(onBeforeSend, [["elt", elt], ["data", data], ["settings", settings], ["xhr", xhr]]);
}
maybeInvokeLocalAction(elt, "-beforeSend");
},
success: function(responseData, textStatus, xhr) {
triggerEvent(elt, "success.ic", [elt, responseData, textStatus, xhr, requestId]);
log(elt, "AJAX request " + requestId + " was successful.", "DEBUG");
var onSuccess = closestAttrValue(elt, 'ic-on-success');
if (onSuccess) {
if (globalEval(onSuccess, [["elt", elt], ["data", responseData], ["textStatus", textStatus], ["xhr", xhr]]) == false) {
return;
}
}
var beforeHeaders = new Date();
var oldTitle = document.title;
try {
if (processHeaders(elt, xhr)) {
log(elt, "Processed headers for request " + requestId + " in " + (new Date() - beforeHeaders) + "ms", "DEBUG");
var beforeSuccess = new Date();
if (xhr.getResponseHeader("X-IC-PushURL") || closestAttrValue(elt, 'ic-push-url') == "true") {
try {
requestCleanup(indicator, globalIndicator, elt); // clean up before snap-shotting HTML
var baseURL = closestAttrValue(elt, 'ic-src');
var paramsToPush = closestAttrValue(elt, 'ic-push-params');
var newUrl = xhr.getResponseHeader("X-IC-PushURL") || getLocalURL(baseURL, paramsToPush, data);
if(_history) {
_history.snapshotForHistory(newUrl, oldTitle);
} else {
throw "History support not enabled";
}
} catch (e) {
log(elt, "Error during history snapshot for " + requestId + ": " + formatError(e), "ERROR");
}
}
success(responseData, textStatus, elt, xhr);
log(elt, "Process content for request " + requestId + " in " + (new Date() - beforeSuccess) + "ms", "DEBUG");
}
triggerEvent(elt, "after.success.ic", [elt, responseData, textStatus, xhr, requestId]);
maybeInvokeLocalAction(elt, "-success");
} catch (e) {
log(elt, "Error processing successful request " + requestId + " : " + formatError(e), "ERROR");
}
},
error: function(xhr, status, str) {
triggerEvent(elt, "error.ic", [elt, status, str, xhr]);
var onError = closestAttrValue(elt, 'ic-on-error');
if (onError) {
globalEval(onError, [["elt", elt], ["status", status], ["str", str], ["xhr", xhr]]);
}
processHeaders(elt, xhr);
maybeInvokeLocalAction(elt, "-error");
log(elt, "AJAX request " + requestId + " to " + url + " experienced an error: " + str, "ERROR");
},
complete: function(xhr, status) {
log(elt, "AJAX request " + requestId + " completed in " + (new Date() - requestStart) + "ms", "DEBUG");
requestCleanup(indicator, globalIndicator, elt);
try {
if ($.contains(document, elt[0])) {
triggerEvent(elt, "complete.ic", [elt, data, status, xhr, requestId]);
} else {
triggerEvent($('body'), "complete.ic", [elt, data, status, xhr, requestId]);
}
} catch (e) {
log(elt, "Error during complete.ic event for " + requestId + " : " + formatError(e), "ERROR");
}
var onComplete = closestAttrValue(elt, 'ic-on-complete');
if (onComplete) {
globalEval(onComplete, [["elt", elt], ["xhr", xhr], ["status", status]]);
}
maybeInvokeLocalAction(elt, "-complete");
}
};
if ($.type(data) != "string") {
ajaxSetup.dataType = null;
ajaxSetup.processData = false;
ajaxSetup.contentType = false;
}
triggerEvent($(document), "beforeAjaxSend.ic", [ajaxSetup, elt]);
if(ajaxSetup.cancel) {
requestCleanup(indicator, globalIndicator, elt);
} else {
$.ajax(ajaxSetup)
}
}
function findGlobalIndicator(elt) {
var indicator = $([]);
elt = $(elt);
var attr = closestAttrValue(elt, 'ic-global-indicator');
if (attr && attr !== "false") {
indicator = $(attr).first();
}
return indicator;
}
function findIndicator(elt) {
var indicator = $([]);
elt = $(elt);
if (getICAttribute(elt, 'ic-indicator')) {
indicator = $(getICAttribute(elt, 'ic-indicator')).first();
} else {
indicator = elt.find(".ic-indicator").first();
if (indicator.length == 0) {
var parent = closestAttrValue(elt, 'ic-indicator');
if (parent) {
indicator = $(parent).first();
} else {
if (elt.next().is('.ic-indicator')) {
indicator = elt.next();
}
}
}
}
return indicator;
}
function processIncludes(data, str) {
if ($.trim(str).indexOf("{") == 0) {
var obj = $.parseJSON(str);
$.each(obj, function(name, value) {
data = appendData(data, name, value);
});
} else {
$(str).each(function() {
var obj = $(this).serializeArray();
$.each(obj, function(i, input) {
data = appendData(data, input.name, input.value);
});
});
}
return data;
}
function processLocalVars(data, str) {
$(str.split(",")).each(function() {
var key = $.trim(this);
var item = localStorage.getItem(key);
if(item) {
data = appendData(data, key, item);
}
});
return data;
}
function appendData(data, key, value) {
if ($.type(data) === "string") {
if($.type(value) !== "string") {
value = JSON.stringify(value);
}
return data + "&" + key + "=" + encodeURIComponent(value);
} else {
data.append(key, value);
return data;
}
}
function getParametersForElement(verb, elt, triggerOrigin) {
var target = getTarget(elt);
var data = null;
if (elt.is('form') && elt.attr('enctype') == 'multipart/form-data') {
data = new FormData(elt[0]);
data = appendData(data, 'ic-request', true);
} else {
data = "ic-request=true";
// if the element is in a form, include the entire form
var closestForm = elt.closest('form');
if (elt.is('form') || (verb != "GET" && closestForm.length > 0)) {
data += "&" + closestForm.serialize();
// include data from a focused button (to capture clicked button value)
var buttonData = elt.data('ic-last-clicked-button');
if(buttonData) {
data = appendData(data, buttonData.name, buttonData.value);
}
} else { // otherwise include the element
data += "&" + elt.serialize();
}
}
var promptText = closestAttrValue(elt, 'ic-prompt');
if (promptText) {
var promptVal = prompt(promptText);
if (promptVal) {
var promptParamName = closestAttrValue(elt, 'ic-prompt-name') || 'ic-prompt-value';
data = appendData(data, promptParamName, promptVal);
} else {
return null;
}
}
if (elt.attr('id')) {
data = appendData(data, 'ic-element-id', elt.attr('id'));
}
if (elt.attr('name')) {
data = appendData(data, 'ic-element-name', elt.attr('name'));
}
if (getICAttribute(target, 'ic-id')) {
data = appendData(data, 'ic-id', getICAttribute(target, 'ic-id'));
}
if (target.attr('id')) {
data = appendData(data, 'ic-target-id', target.attr('id'));
}
if (triggerOrigin && triggerOrigin.attr('id')) {
data = appendData(data, 'ic-trigger-id', triggerOrigin.attr('id'));
}
if (triggerOrigin && triggerOrigin.attr('name')) {
data = appendData(data, 'ic-trigger-name', triggerOrigin.attr('name'));
}
var includeAttr = closestAttrValue(elt, 'ic-include');
if (includeAttr) {
data = processIncludes(data, includeAttr);
}
var localVars = closestAttrValue(elt, 'ic-local-vars');
if (localVars) {
data = processLocalVars(data, localVars);
}
$(getICAttributeSelector('ic-global-include')).each(function() {
data = processIncludes(data, getICAttribute($(this), 'ic-global-include'));
});
data = appendData(data, 'ic-current-url', currentUrl());
var selectFromResp = closestAttrValue(elt, 'ic-select-from-response');
if(selectFromResp) {
data = appendData(data, 'ic-select-from-response', selectFromResp);
}
log(elt, "request parameters " + data, "DEBUG");
return data;
}
function maybeSetIntercoolerInfo(elt) {
var target = getTarget(elt);
getIntercoolerId(target);
if (elt.data('elementAdded.ic') != true) {
elt.data('elementAdded.ic', true);
triggerEvent(elt, "elementAdded.ic");
}
}
function getIntercoolerId(elt) {
if (!getICAttribute(elt, 'ic-id')) {
setICAttribute(elt, 'ic-id', uuid());
}
return getICAttribute(elt, 'ic-id');
}
//============================================================
// Tree Processing
//============================================================
function processNodes(elt) {
elt = $(elt);
if (elt.length > 1) {
elt.each(function() {
processNodes(this);
});
} else {
processMacros(elt);
processEnhancement(elt);
processSources(elt);
processPolling(elt);
processEventSources(elt);
processTriggerOn(elt);
processRemoveAfter(elt);
processAddClasses(elt);
processRemoveClasses(elt);
}
}
function fireReadyStuff(elt) {
triggerEvent(elt, 'nodesProcessed.ic');
$.each(_readyHandlers, function(i, handler) {
try {
handler(elt);
} catch (e) {
log(elt, formatError(e), "ERROR");
}
});
}
function autoFocus(elt) {
elt.find('[autofocus]').last().focus();
}
function processMacros(elt) {
$.each(_MACROS, function(i, macro) {
if (elt.closest('.ic-ignore').length == 0) {
if (elt.is('[' + macro + ']')) {
processMacro(macro, elt);
}
elt.find('[' + macro + ']').each(function() {
var _this = $(this);
if (_this.closest('.ic-ignore').length == 0) {
processMacro(macro, _this);
}
});
}
});
}
function processSources(elt) {
if (elt.closest('.ic-ignore').length == 0) {
if (elt.is(getICAttributeSelector("ic-src"))) {
maybeSetIntercoolerInfo(elt);
}
elt.find(getICAttributeSelector("ic-src")).each(function() {
var _this = $(this);
if (_this.closest('.ic-ignore').length == 0) {
maybeSetIntercoolerInfo(_this);
}
});
}
}
function processPolling(elt) {
if (elt.closest('.ic-ignore').length == 0) {
if (elt.is(getICAttributeSelector("ic-poll"))) {
maybeSetIntercoolerInfo(elt);
startPolling(elt);
}
elt.find(getICAttributeSelector("ic-poll")).each(function() {
var _this = $(this);
if (_this.closest('.ic-ignore').length == 0) {
maybeSetIntercoolerInfo(_this);
startPolling(_this);
}
});
}
}
function processTriggerOn(elt) {
if (elt.closest('.ic-ignore').length == 0) {
handleTriggerOn(elt);
elt.find(getICAttributeSelector('ic-trigger-on')).each(function() {
var _this = $(this);
if (_this.closest('.ic-ignore').length == 0) {
handleTriggerOn(_this);
}
});
}
}
function processRemoveAfter(elt) {
if (elt.closest('.ic-ignore').length == 0) {
handleRemoveAfter(elt);
elt.find(getICAttributeSelector('ic-remove-after')).each(function() {
var _this = $(this);
if (_this.closest('.ic-ignore').length == 0) {
handleRemoveAfter(_this);
}
});
}
}
function processAddClasses(elt) {
if (elt.closest('.ic-ignore').length == 0) {
handleAddClasses(elt);
elt.find(getICAttributeSelector('ic-add-class')).each(function() {
var _this = $(this);
if (_this.closest('.ic-ignore').length == 0) {
handleAddClasses(_this);
}
});
}
}
function processRemoveClasses(elt) {
if (elt.closest('.ic-ignore').length == 0) {
handleRemoveClasses(elt);
elt.find(getICAttributeSelector('ic-remove-class')).each(function() {
var _this = $(this);
if (_this.closest('.ic-ignore').length == 0) {
handleRemoveClasses(_this);
}
});
}
}
function processEnhancement(elt) {
if (elt.closest('.ic-ignore').length == 0) {
if(closestAttrValue(elt, 'ic-enhance') === 'true') {
enhanceDomTree(elt);
} else {
elt.find(getICAttributeSelector('ic-enhance')).each(function(){
enhanceDomTree($(this));
});
}
}
}
function processEventSources(elt) {
if (elt.closest('.ic-ignore').length == 0) {
handleEventSource(elt);
elt.find(getICAttributeSelector('ic-sse-src')).each(function() {
var _this = $(this);
if (_this.closest('.ic-ignore').length == 0) {
handleEventSource(_this);
}
});
}
}
//============================================================
// Polling support
//============================================================
function startPolling(elt) {
if (elt.data('ic-poll-interval-id') == null && getICAttribute(elt, 'ic-pause-polling') != 'true') {
var interval = parseInterval(getICAttribute(elt, 'ic-poll'));
if (interval != null) {
var selector = icSelectorFor(elt);
var repeats = parseInt(getICAttribute(elt, 'ic-poll-repeats')) || -1;
var currentIteration = 0;
log(elt, "POLL: Starting poll for element " + selector, "DEBUG");
var timerId = setInterval(function() {
var target = $(selector);
triggerEvent(elt, "onPoll.ic", target);
if ((target.length == 0) || (currentIteration == repeats) || elt.data('ic-poll-interval-id') != timerId) {
log(elt, "POLL: Clearing poll for element " + selector, "DEBUG");
clearTimeout(timerId);
} else {
fireICRequest(target);
}
currentIteration++;
}, 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'));
elt.data('ic-poll-interval-id', null);
}
}
//============================================================----
// Dependency support
//============================================================----
function refreshDependencies(dest, src) {
log(src, "refreshing dependencies for path " + dest, "DEBUG");
$(getICAttributeSelector('ic-src')).each(function() {
var fired = false;
var _this = $(this);
if (verbFor(_this) == "GET" && getICAttribute(_this, 'ic-deps') != 'ignore' ) {
if (isDependent(dest, getICAttribute(_this, 'ic-src'))) {
if (src == null || $(src)[0] != _this[0]) {
fireICRequest(_this);
fired = true;
}
} else if (isICDepsDependent(dest, getICAttribute(_this, 'ic-deps')) || getICAttribute(_this, 'ic-deps') == "*") {
if (src == null || $(src)[0] != _this[0]) {
fireICRequest(_this);
fired = true;
}
}
}
if (fired) {
log(_this, "depends on path " + dest + ", refreshing...", "DEBUG")
}
});
}
function isICDepsDependent(src, dest) {
if(dest) {
var paths = dest.split(",");
for (var i = 0; i < paths.length; i++) {
var str = paths[i].trim();
if(isDependent(src, str)) {
return true;
}
}
}
return false;
}
function isDependent(src, dest) {
return !!_isDependentFunction(src, dest);
}
//============================================================----
// Trigger-On support
//============================================================----
function verbFor(elt) {
elt = $(elt);
if (getICAttribute(elt, 'ic-verb')) {
return getICAttribute(elt, 'ic-verb').toUpperCase();
}
return "GET";
}
function eventFor(attr, elt) {
if (attr == "default") {
elt = $(elt);
if (elt.is('button')) {
return 'click';
} else if (elt.is('form')) {
return 'submit';
} else if (elt.is('input, textarea, select, button')) {
return 'change';
} else {
return 'click';
}
} else {
return attr;
}
}
function preventDefault(elt, evt) {
return elt.is('form') ||
(elt.is('input[type="submit"], button') && elt.closest('form').length == 1) ||
(elt.is('a') && elt.is('[href]') && elt.attr('href').indexOf('#') != 0);
}
function handleRemoveAfter(elt) {
elt = $(elt);
if (getICAttribute(elt, 'ic-remove-after')) {
var interval = parseInterval(getICAttribute(elt, 'ic-remove-after'));
setTimeout(function() {
remove(elt);
}, interval);
}
}
function parseAndApplyClass(classInfo, elt, operation) {
var cssClass = "";
var delay = 50;
if (classInfo.indexOf(":") > 0) {
var split = classInfo.split(':');
cssClass = split[0];
delay = parseInterval(split[1]);
} else {
cssClass = classInfo;
}
setTimeout(function() {
elt[operation](cssClass)
}, delay);
}
function handleAddClasses(elt) {
elt = $(elt);
if (getICAttribute(elt, 'ic-add-class')) {
var values = getICAttribute(elt, 'ic-add-class').split(",");
var arrayLength = values.length;
for (var i = 0; i < arrayLength; i++) {
parseAndApplyClass($.trim(values[i]), elt, 'addClass');
}
}
}
function handleRemoveClasses(elt) {
elt = $(elt);
if (getICAttribute(elt, 'ic-remove-class')) {
var values = getICAttribute(elt, 'ic-remove-class').split(",");
var arrayLength = values.length;
for (var i = 0; i < arrayLength; i++) {
parseAndApplyClass($.trim(values[i]), elt, 'removeClass');
}
}
}
function handleEventSource(elt) {
elt = $(elt);
if (getICAttribute(elt, 'ic-sse-src')) {
var evtSrcUrl = getICAttribute(elt, 'ic-sse-src');
var evtSrcWithCredentials = getICAttribute(elt, 'ic-sse-with-credentials') === 'true';
var eventSource = initEventSource(elt, evtSrcUrl, evtSrcWithCredentials);
elt.data('ic-event-sse-source', eventSource);
elt.data('ic-event-sse-map', {});
}
}
function initEventSource(elt, evtSrcUrl, evtSrcWithCredentials) {
var eventSource = Intercooler._internal.initEventSource(evtSrcUrl, evtSrcWithCredentials);
eventSource.onmessage = function(e) {
processICResponse(e.data, elt, false);
};
return eventSource;
}
function registerSSE(sourceElement, event) {
var source = sourceElement.data('ic-event-sse-source');
var eventMap = sourceElement.data('ic-event-sse-map');
if(source.addEventListener && eventMap[event] != true) {
source.addEventListener(event, function(){
sourceElement.find(getICAttributeSelector('ic-trigger-on')).each(function(){
var _that = $(this);
if(_that.attr('ic-trigger-on') == "sse:" + event) {
fireICRequest(_that);
}
});
})
}
}
function getTriggeredElement(elt) {
var triggerFrom = getICAttribute(elt, 'ic-trigger-from');
if(triggerFrom) {
if (triggerFrom == "document") {
return $(document);
} else if (triggerFrom == "window") {
return $(window);
} else {
return $(triggerFrom);
}
} else {
return elt;
}
}
function handleTriggerOn(elt) {
var triggerOnValue = getICAttribute(elt, 'ic-trigger-on');
if (triggerOnValue) {
// record button or submit input click info
if(elt.is('form')) {
elt.on('click focus', 'input, button, select, textarea', function(e){
if($(this).is('input[type="submit"], button') && $(this).is("[name]")) {
elt.data('ic-last-clicked-button', {name:$(this).attr("name"), value:$(this).val()})
} else {
elt.data('ic-last-clicked-button', null)
}
});
}
var triggerOnArray = triggerOnValue.split(",");
for (var i = 0; i < triggerOnArray.length; i++) {
var triggerOn = $.trim(triggerOnArray[i]);
var splitTriggerOn = triggerOn.split(" ");
var eventString = eventFor(splitTriggerOn[0], $(elt));
var eventModifier = splitTriggerOn[1];
if (triggerOn == 'load') {
fireICRequest(elt);
} else if (triggerOn == 'scrolled-into-view') {
initScrollHandler();
setTimeout(function() {
triggerEvent($(window), 'scroll');
}, 100); // Trigger a scroll in case element is already viewable
} else {
if(eventString.indexOf("sse:") == 0) {
//Server-sent event, find closest event source and register for it
var sourceElt = elt.closest(getICAttributeSelector('ic-sse-src'));
if(sourceElt.length > 0) {
registerSSE(sourceElt, splitTriggerOn[0].substr(4))
}
} else {
$(getTriggeredElement(elt)).on(eventString, function(e) {
var onBeforeTrigger = closestAttrValue(elt, 'ic-on-beforeTrigger');
if (onBeforeTrigger) {
if (globalEval(onBeforeTrigger, [["elt", elt], ["evt", e], ["elt", elt]]) == false) {
log(elt, "ic-trigger cancelled by ic-on-beforeTrigger", "DEBUG");
return false;
}
}
if (eventModifier == 'changed') {
var currentVal = elt.val();
var previousVal = elt.data('ic-previous-val');
elt.data('ic-previous-val', currentVal);
if (currentVal != previousVal) {
fireICRequest(elt);
}
} else if (eventModifier == 'once') {
var alreadyTriggered = elt.data('ic-already-triggered');
elt.data('ic-already-triggered', true);
if (alreadyTriggered !== true) {
fireICRequest(elt);
}
} else {
fireICRequest(elt);
}
if (preventDefault(elt, e)) {
e.preventDefault();
return false;
}
return true;
});
if(eventString && (eventString.indexOf("timeout:") == 0)) {
var timeout = parseInterval(eventString.split(":")[1]);
setTimeout(function () {
$(getTriggeredElement(elt)).trigger(eventString);
}, timeout);
}
}
}
}
}
}
//============================================================----
// Macro support
//============================================================----
function macroIs(macro, constant) {
return macro == fixICAttributeName(constant);
}
function processMacro(macro, elt) {
// action attributes
if (macroIs(macro, 'ic-post-to')) {
setIfAbsent(elt, 'ic-src', getICAttribute(elt, 'ic-post-to'));
setIfAbsent(elt, 'ic-verb', 'POST');
setIfAbsent(elt, 'ic-trigger-on', 'default');
setIfAbsent(elt, 'ic-deps', 'ignore');
}
if (macroIs(macro, 'ic-put-to')) {
setIfAbsent(elt, 'ic-src', getICAttribute(elt, 'ic-put-to'));
setIfAbsent(elt, 'ic-verb', 'PUT');
setIfAbsent(elt, 'ic-trigger-on', 'default');
setIfAbsent(elt, 'ic-deps', 'ignore');
}
if (macroIs(macro, 'ic-patch-to')) {
setIfAbsent(elt, 'ic-src', getICAttribute(elt, 'ic-patch-to'));
setIfAbsent(elt, 'ic-verb', 'PATCH');
setIfAbsent(elt, 'ic-trigger-on', 'default');
setIfAbsent(elt, 'ic-deps', 'ignore');
}
if (macroIs(macro, 'ic-get-from')) {
setIfAbsent(elt, 'ic-src', getICAttribute(elt, 'ic-get-from'));
setIfAbsent(elt, 'ic-trigger-on', 'default');
setIfAbsent(elt, 'ic-deps', 'ignore');
}
if (macroIs(macro, 'ic-delete-from')) {
setIfAbsent(elt, 'ic-src', getICAttribute(elt, 'ic-delete-from'));
setIfAbsent(elt, 'ic-verb', 'DELETE');
setIfAbsent(elt, 'ic-trigger-on', 'default');
setIfAbsent(elt, 'ic-deps', 'ignore');
}
if (macroIs(macro, 'ic-action')) {
setIfAbsent(elt, 'ic-trigger-on', 'default');
}
// non-action attributes
var value = null;
var url = null;
if (macroIs(macro, 'ic-style-src')) {
value = getICAttribute(elt, 'ic-style-src').split(":");
var styleAttribute = value[0];
url = value[1];
setIfAbsent(elt, 'ic-src', url);
setIfAbsent(elt, 'ic-target', 'this.style.' + styleAttribute);
}
if (macroIs(macro, 'ic-attr-src')) {
value = getICAttribute(elt, 'ic-attr-src').split(":");
var attribute = value[0];
url = value[1];
setIfAbsent(elt, 'ic-src', url);
setIfAbsent(elt, 'ic-target', 'this.' + attribute);
}
if (macroIs(macro, 'ic-prepend-from')) {
setIfAbsent(elt, 'ic-src', getICAttribute(elt, 'ic-prepend-from'));
setIfAbsent(elt, 'ic-swap-style', 'prepend');
}
if (macroIs(macro, 'ic-append-from')) {
setIfAbsent(elt, 'ic-src', getICAttribute(elt, 'ic-append-from'));
setIfAbsent(elt, 'ic-swap-style', 'append');
}
}
function isLocalLink(anchor) {
return location.hostname === anchor[0].hostname &&
anchor.attr('href') &&
!anchor.attr('href').startsWith("#")
}
function enhanceAnchor(anchor) {
if (closestAttrValue(anchor, 'ic-enhance') === "true") {
if (isLocalLink(anchor)) {
setIfAbsent(anchor, 'ic-src', anchor.attr('href'));
setIfAbsent(anchor, 'ic-trigger-on', 'default');
setIfAbsent(anchor, 'ic-deps', 'ignore');
setIfAbsent(anchor, 'ic-push-url', 'true');
}
}
}
function determineFormVerb(form) {
return form.find('input[name="_method"]').val() || form.attr('method') || form[0].method;
}
function enhanceForm(form) {
if (closestAttrValue(form, 'ic-enhance') === "true") {
setIfAbsent(form, 'ic-src', form.attr('action'));
setIfAbsent(form, 'ic-trigger-on', 'default');
setIfAbsent(form, 'ic-deps', 'ignore');
setIfAbsent(form, 'ic-verb', determineFormVerb(form));
}
}
function enhanceDomTree(elt) {
if(elt.is('a')) {
enhanceAnchor(elt);
}
elt.find('a').each(function(){
enhanceAnchor($(this));
});
if(elt.is('form')){
enhanceForm(elt);
}
elt.find('form').each(function(){
enhanceForm($(this));
});
}
function setIfAbsent(elt, attr, value) {
if (getICAttribute(elt, attr) == null) {
setICAttribute(elt, attr, value);
}
}
//============================================================----
// Utilities
//============================================================----
function isScrolledIntoView(elem) {
elem = $(elem);
if (elem.height() == 0 && elem.width() == 0) {
return false;
}
var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height();
var elemTop = elem.offset().top;
var elemBottom = elemTop + elem.height();
return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom)
&& (elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}
function maybeScrollToTarget(elt, target) {
if (closestAttrValue(elt, 'ic-scroll-to-target') != "false" &&
(closestAttrValue(elt, 'ic-scroll-to-target') == 'true' ||
closestAttrValue(target, 'ic-scroll-to-target') == 'true')) {
var offset = -50; // -50 px default offset padding
if (closestAttrValue(elt, 'ic-scroll-offset')) {
offset = parseInt(closestAttrValue(elt, 'ic-scroll-offset'));
} else if (closestAttrValue(target, 'ic-scroll-offset')) {
offset = parseInt(closestAttrValue(target, 'ic-scroll-offset'));
}
var currentPosition = target.offset().top;
var portalTop = $(window).scrollTop();
var portalEnd = portalTop + window.innerHeight;
//if the current top of this element is not visible, scroll it to the top position
if (currentPosition < portalTop || currentPosition > portalEnd) {
offset += currentPosition;
$('html,body').animate({scrollTop: offset}, 400);
}
}
}
function getTransitionDuration(elt, target) {
var transitionDuration = closestAttrValue(elt, 'ic-transition-duration');
if (transitionDuration) {
return parseInterval(transitionDuration);
}
transitionDuration = closestAttrValue(target, 'ic-transition-duration');
if (transitionDuration) {
return parseInterval(transitionDuration);
}
target = $(target);
var duration = 0;
var durationStr = target.css('transition-duration');
if (durationStr) {
duration += parseInterval(durationStr);
}
var delayStr = target.css('transition-delay');
if (delayStr) {
duration += parseInterval(delayStr);
}
return duration;
}
function closeSSESource(elt) {
var src = elt.data('ic-event-sse-source');
try {
if(src) {
src.close();
}
} catch (e) {
log(elt, "Error closing ServerSentEvent source" + e, "ERROR");
}
}
function beforeSwapCleanup(target) {
target.find(getICAttributeSelector('ic-sse-src')).each(function() {
closeSSESource($(this));
});
triggerEvent(target, 'beforeSwap.ic');
}
function processICResponse(responseContent, elt, forHistory, url) {
if (responseContent && responseContent != "" && responseContent != " ") {
log(elt, "response content: \n" + responseContent, "DEBUG");
var target = getTarget(elt);
var transformer = closestAttrValue(elt, 'ic-transform-response');
if(transformer) {
responseContent = globalEval(transformer, [["content", responseContent], ["url", url], ["elt", elt]]);
}
var contentToSwap = maybeFilter(responseContent, closestAttrValue(elt, 'ic-select-from-response'));
if (closestAttrValue(elt, 'ic-fix-ids') == "true") {
fixIDs(contentToSwap);
}
var doSwap = function() {
if (closestAttrValue(elt, 'ic-replace-target') == "true") {
try {
beforeSwapCleanup(target);
closeSSESource(target);
target.replaceWith(contentToSwap);
target = contentToSwap;
} catch (e) {
log(elt, formatError(e), "ERROR");
}
processNodes(contentToSwap);
fireReadyStuff(target);
autoFocus(target);
} else {
if (getICAttribute(elt, 'ic-swap-style') == "prepend") {
prepend(target, contentToSwap);
processNodes(contentToSwap);
fireReadyStuff(target);
autoFocus(target);
} else if (getICAttribute(elt, 'ic-swap-style') == "append") {
append(target, contentToSwap);
processNodes(contentToSwap);
fireReadyStuff(target);
autoFocus(target);
} else {
try {
beforeSwapCleanup(target);
target.empty().append(contentToSwap);
} catch (e) {
log(elt, formatError(e), "ERROR");
}
target.children().each(function() {
processNodes(this);
});
fireReadyStuff(target);
autoFocus(target);
}
if (forHistory != true) {
maybeScrollToTarget(elt, target);
}
var switchClass = elt.closest(getICAttributeSelector('ic-switch-class'));
var classToSwitch = switchClass.attr(fixICAttributeName('ic-switch-class'));
if(classToSwitch) {
switchClass.children().removeClass(classToSwitch);
switchClass.children().each(function(){
if($.contains($(this)[0], $(elt)[0]) || $(this)[0] == $(elt)[0]) {
$(this).addClass(classToSwitch);
$(this).addClass(classToSwitch);
}
})
}
}
};
if (target.length == 0) {
//TODO cgross - refactor getTarget to return printable string here
log(elt, "Invalid target for element: " + getICAttribute(elt.closest(getICAttributeSelector('ic-target')), 'ic-target'), "ERROR");
return;
}
var delay = getTransitionDuration(elt, target);
target.addClass('ic-transitioning');
setTimeout(function() {
try {
doSwap();
} catch (e) {
log(elt, "Error during content swap : " + formatError(e), "ERROR");
}
setTimeout(function() {
try {
target.removeClass('ic-transitioning');
if(_history) {
_history.updateHistory();
}
triggerEvent(target, "complete_transition.ic", [target]);
} catch (e) {
log(elt, "Error during transition complete : " + formatError(e), "ERROR");
}
}, 20);
}, delay);
} else {
log(elt, "Empty response, nothing to do here.", "DEBUG");
}
}
function maybeFilter(newContent, filter) {
var asQuery;
if ($.zepto) {
var newDoc = createDocument(newContent);
asQuery = $(newDoc).find('body').contents();
} else {
asQuery = $($.parseHTML(newContent, null, true));
}
if (filter) {
return walkTree(asQuery, filter).contents();
} else {
return asQuery;
}
}
function walkTree(elt, filter) {
return elt.filter(filter).add(elt.find(filter));
}
function fixIDs(contentToSwap) {
var fixedIDs = {};
walkTree(contentToSwap, "[id]").each(function() {
var originalID = $(this).attr("id");
var fixedID;
do {
fixedID = "ic-fixed-id-" + uuid();
} while ($("#" + fixedID).length > 0);
fixedIDs[originalID] = fixedID;
$(this).attr("id", fixedID);
});
walkTree(contentToSwap, "label[for]").each(function () {
var originalID = $(this).attr("for");
$(this).attr("for", fixedIDs[originalID] || originalID);
});
walkTree(contentToSwap, "*").each(function () {
$.each(this.attributes, function () {
if (this.value.indexOf("#") !== -1) {
this.value = this.value.replace(/#([-_A-Za-z0-9]+)/g, function(match, originalID) {
return "#" + (fixedIDs[originalID] || originalID);
});
}
})
});
}
function getStyleTarget(elt) {
var val = closestAttrValue(elt, 'ic-target');
if (val && val.indexOf("this.style.") == 0) {
return val.substr(11)
} else {
return null;
}
}
function getAttrTarget(elt) {
var val = closestAttrValue(elt, 'ic-target');
if (val && val.indexOf("this.") == 0) {
return val.substr(5)
} else {
return null;
}
}
function fireICRequest(elt, alternateHandler) {
elt = $(elt);
var triggerOrigin = elt;
if (!elt.is(getICAttributeSelector('ic-src')) && getICAttribute(elt, 'ic-action') == undefined) {
elt = elt.closest(getICAttributeSelector('ic-src'));
}
var confirmText = closestAttrValue(elt, 'ic-confirm');
if (confirmText) {
if (!confirm(confirmText)) {
return;
}
}
if("true" == closestAttrValue(elt, 'ic-disable-when-doc-hidden')) {
if(document['hidden']) {
return;
}
}
if("true" == closestAttrValue(elt, 'ic-disable-when-doc-inactive')) {
if(!document.hasFocus()) {
return;
}
}
if (elt.length > 0) {
var icEventId = uuid();
elt.data('ic-event-id', icEventId);
var invokeRequest = function() {
// if an existing request is in flight for this element, push this request as the next to be executed
if (elt.data('ic-request-in-flight') == true) {
elt.data('ic-next-request', {"req" : invokeRequest});
return;
}
if (elt.data('ic-event-id') == icEventId) {
var styleTarget = getStyleTarget(elt);
var attrTarget = styleTarget ? null : getAttrTarget(elt);
var verb = verbFor(elt);
var url = getICAttribute(elt, 'ic-src');
if (url) {
var success = alternateHandler || function(data) {
if (styleTarget) {
elt.css(styleTarget, data);
} else if (attrTarget) {
elt.attr(attrTarget, data);
} else {
processICResponse(data, elt, false, url);
if (verb != 'GET') {
refreshDependencies(getICAttribute(elt, 'ic-src'), elt);
}
}
};
var data = getParametersForElement(verb, elt, triggerOrigin);
if(data) {
handleRemoteRequest(elt, verb, url, data, success);
}
}
maybeInvokeLocalAction(elt, "");
}
};
var triggerDelay = closestAttrValue(elt, 'ic-trigger-delay');
if (triggerDelay) {
setTimeout(invokeRequest, parseInterval(triggerDelay));
} else {
invokeRequest();
}
}
}
function maybeInvokeLocalAction(elt, modifier) {
var actions = getICAttribute(elt, 'ic' + modifier + '-action');
if (actions) {
invokeLocalAction(elt, actions, modifier);
}
}
function invokeLocalAction(elt, actions, modifier) {
var actionTargetVal = closestAttrValue(elt, 'ic' + modifier + '-action-target');
if(actionTargetVal === null && modifier !== "") {
actionTargetVal = closestAttrValue(elt, 'ic-action-target');
}
var target = null;
if(actionTargetVal) {
target = getTargetImpl(elt, 'ic-action-target');
} else {
target = getTarget(elt);
}
var actionArr = actions.split(";");
var actionsArr = [];
var delay = 0;
$.each(actionArr, function(i, actionStr) {
var actionDef = $.trim(actionStr);
var action = actionDef;
var actionArgs = [];
if (actionDef.indexOf(":") > 0) {
action = actionDef.substr(0, actionDef.indexOf(":"));
actionArgs = computeArgs(actionDef.substr(actionDef.indexOf(":") + 1, actionDef.length));
}
if (action == "") {
// ignore blanks
} else if (action == "delay") {
if (delay == null) {
delay = 0;
}
delay += parseInterval(actionArgs[0] + ""); // custom interval increase
} else {
if (delay == null) {
delay = 420; // 420ms default interval increase (400ms jQuery default + 20ms slop)
}
actionsArr.push([delay, makeApplyAction(target, action, actionArgs)]);
delay = null;
}
});
delay = 0;
$.each(actionsArr, function(i, action) {
delay += action[0];
setTimeout(action[1], delay);
});
}
function computeArgs(args) {
try {
return eval("[" + args + "]")
} catch (e) {
return [$.trim(args)];
}
}
function makeApplyAction(target, action, args) {
return function() {
var func = target[action] || window[action];
if (func) {
func.apply(target, args);
} else {
log(target, "Action " + action + " was not found", "ERROR");
}
};
}
//============================================================
// History Support
//============================================================
function newIntercoolerHistory(storage, history, slotLimit, historyVersion) {
/* Constants */
var HISTORY_SUPPORT_SLOT = 'ic-history-support';
var HISTORY_SLOT_PREFIX = "ic-hist-elt-";
/* Instance Vars */
var historySupportData = JSON.parse(storage.getItem(HISTORY_SUPPORT_SLOT));
var _snapshot = null;
// Reset history if the history config has changed
if (historyConfigHasChanged(historySupportData)) {
log(getTargetForHistory($('body')), "Intercooler History configuration changed, clearing history", "INFO");
clearHistory();
}
if (historySupportData == null) {
historySupportData = {
slotLimit: slotLimit,
historyVersion: historyVersion,
lruList: []
};
}
/* Instance Methods */
function historyConfigHasChanged(historySupportData) {
return historySupportData == null ||
historySupportData.slotLimit != slotLimit ||
historySupportData.historyVersion != historyVersion ||
historySupportData.lruList == null
}
function clearHistory() {
var keys = [];
for (var i = 0; i < storage.length; i++) {
if (storage.key(i).indexOf(HISTORY_SLOT_PREFIX) == 0) {
keys.push(storage.key(i));
}
}
for (var j = 0; j < keys.length; j++) {
storage.removeItem(keys[j]);
}
storage.removeItem(HISTORY_SUPPORT_SLOT);
historySupportData = {
slotLimit: slotLimit,
historyVersion: historyVersion,
lruList: []
};
}
function updateLRUList(url) {
var lruList = historySupportData.lruList;
var currentIndex = lruList.indexOf(url);
var t = getTargetForHistory($('body'));
// found in current list, shift it to the end
if (currentIndex >= 0) {
log(t, "URL found in LRU list, moving to end", "INFO");
lruList.splice(currentIndex, 1);
lruList.push(url);
} else {
// not found, add and shift if necessary
log(t, "URL not found in LRU list, adding", "INFO");
lruList.push(url);
if (lruList.length > historySupportData.slotLimit) {
var urlToDelete = lruList.shift();
log(t, "History overflow, removing local history for " + urlToDelete, "INFO");
storage.removeItem(HISTORY_SLOT_PREFIX + urlToDelete);
}
}
// save history metadata
storage.setItem(HISTORY_SUPPORT_SLOT, JSON.stringify(historySupportData));
return lruList;
}
function saveHistoryData(restorationData) {
var content = JSON.stringify(restorationData);
try {
storage.setItem(restorationData.id, content);
} catch (e) {
//quota error, nuke local cache
try {
clearHistory();
storage.setItem(restorationData.id, content);
} catch (e) {
log(getTargetForHistory($('body')), "Unable to save intercooler history with entire history cleared, is something else eating " +
"local storage? History Limit:" + slotLimit, "ERROR");
}
}
}
function makeHistoryEntry(html, yOffset, url, title) {
var restorationData = {
"url": url,
"id": HISTORY_SLOT_PREFIX + url,
"content": html,
"yOffset": yOffset,
"timestamp": new Date().getTime(),
"title": title
};
updateLRUList(url);
// save to the history slot
saveHistoryData(restorationData);
return restorationData;
}
function addPopStateHandler(windowToAdd) {
if (windowToAdd.onpopstate == null || windowToAdd.onpopstate['ic-on-pop-state-handler'] != true) {
var currentOnPopState = windowToAdd.onpopstate;
windowToAdd.onpopstate = function(event) {
triggerEvent(getTargetForHistory($('body')), 'handle.onpopstate.ic');
if (!handleHistoryNavigation(event)) {
if (currentOnPopState) {
currentOnPopState(event);
}
}
triggerEvent(getTargetForHistory($('body')), 'pageLoad.ic');
};
windowToAdd.onpopstate['ic-on-pop-state-handler'] = true;
}
}
function updateHistory() {
if (_snapshot) {
pushUrl(_snapshot.newUrl, currentUrl(), _snapshot.oldHtml, _snapshot.yOffset, _snapshot.oldTitle);
_snapshot = null;
}
}
function pushUrl(newUrl, originalUrl, originalHtml, yOffset, originalTitle) {
var historyEntry = makeHistoryEntry(originalHtml, yOffset, originalUrl, originalTitle);
history.replaceState({"ic-id": historyEntry.id}, "", "");
var t = getTargetForHistory($('body'));
var restorationData = makeHistoryEntry(t.html(), window.pageYOffset, newUrl, document.title);
history.pushState({'ic-id': restorationData.id}, "", newUrl);
triggerEvent(t, "pushUrl.ic", [t, restorationData]);
}
function handleHistoryNavigation(event) {
var data = event.state;
if (data && data['ic-id']) {
var historyData = JSON.parse(storage.getItem(data['ic-id']));
if (historyData) {
processICResponse(historyData["content"], getTargetForHistory($('body')), true);
if (historyData["yOffset"]) {
setTimeout(function () {
window.scrollTo(0, historyData["yOffset"]);
}, 100);
}
if (historyData["title"]) {
document.title = historyData["title"];
}
return true;
} else {
$.get(currentUrl(), {'ic-restore-history': true}, function(data, status) {
var newDoc = createDocument(data);
var replacementHtml = getTargetForHistory(newDoc).html();
processICResponse(replacementHtml, getTargetForHistory($('body')), true);
});
}
}
return false;
}
function getTargetForHistory(elt) {
var explicitHistoryTarget = elt.find(getICAttributeSelector('ic-history-elt'));
if (explicitHistoryTarget.length > 0) {
return explicitHistoryTarget;
} else {
return elt;
}
}
function snapshotForHistory(newUrl, oldTitle) {
var t = getTargetForHistory($('body'));
triggerEvent(t, "beforeHistorySnapshot.ic", [t]);
_snapshot = {
newUrl: newUrl,
oldHtml: t.html(),
yOffset: window.pageYOffset,
oldTitle: oldTitle
};
}
function dumpLocalStorage() {
var str = "";
var keys = [];
for (var x in storage) {
keys.push(x);
}
keys.sort();
var total = 0;
for (var i in keys) {
var size = (storage[keys[i]].length * 2);
total += size;
str += keys[i] + "=" + (size / 1024 / 1024).toFixed(2) + " MB\n";
}
return str + "\nTOTAL LOCAL STORAGE: " + (total / 1024 / 1024).toFixed(2) + " MB";
}
function supportData() {
return historySupportData;
}
/* API */
return {
clearHistory: clearHistory,
updateHistory: updateHistory,
addPopStateHandler: addPopStateHandler,
snapshotForHistory: snapshotForHistory,
_internal: {
addPopStateHandler: addPopStateHandler,
supportData: supportData,
dumpLocalStorage: dumpLocalStorage,
updateLRUList: updateLRUList
}
};
}
function getSlotLimit() {
return 20;
}
function refresh(val) {
if (typeof val == 'string' || val instanceof String) {
refreshDependencies(val);
} else {
fireICRequest(val);
}
return Intercooler;
}
var _history = null;
try {
_history = newIntercoolerHistory(localStorage, window.history, getSlotLimit(), .1);
} catch(e) {
log($('body'), "Could not initialize history", "WARN");
}
//============================================================
// Local references transport
//============================================================
if($.ajaxTransport) {
$.ajaxTransport("text", function(options, origOptions) {
if (origOptions.url[0] == "#") {
var ltAttr = fixICAttributeName("ic-local-");
var src = $(origOptions.url);
var rsphdr = [];
var status = 200;
var statusText = "OK";
src.each(function(i, el) {
$.each(el.attributes, function(j, attr) {
if (attr.name.substr(0, ltAttr.length) == ltAttr) {
var lhName = attr.name.substring(ltAttr.length);
if (lhName == "status") {
var statusLine = attr.value.match(/(\d+)\s?(.*)/);
if (statusLine != null) {
status = statusLine[1];
statusText = statusLine[2];
} else {
status = "500";
statusText = "Attribute Error";
}
} else {
rsphdr.push(lhName + ": " + attr.value);
}
}
});
});
var rsp = src.length > 0 ? src.html() : "";
return {
send: function(reqhdr, completeCallback) {
completeCallback(status, statusText, {html: rsp}, rsphdr.join("\n"));
},
abort: function() {
}
};
} else {
return null;
}
}
);
}
//============================================================
// Bootstrap
//============================================================
function init() {
var elt = $('body');
processNodes(elt);
fireReadyStuff(elt);
if(_history) {
_history.addPopStateHandler(window);
}
if($.zepto) {
$('body').data('zeptoDataTest', {});
if(typeof($('body').data('zeptoDataTest')) == "string") {
log(null,
"!!!! Please include the data module with Zepto! Intercooler requires full data support to function !!!!",
"ERROR")
}
}
}
$(function() {
init();
});
/* ===================================================
* API
* =================================================== */
return {
refresh: refresh,
history: _history,
triggerRequest: fireICRequest,
processNodes: processNodes,
closestAttrValue: closestAttrValue,
verbFor: verbFor,
isDependent: isDependent,
getTarget: getTarget,
processHeaders: processHeaders,
startPolling: startPolling,
cancelPolling: cancelPolling,
setIsDependentFunction: function(func) {
_isDependentFunction = func;
},
ready: function(readyHandler) {
_readyHandlers.push(readyHandler);
},
_internal: {
init: init,
replaceOrAddMethod: replaceOrAddMethod,
initEventSource: function(url, withCredentials) {
return new EventSource(url, {withCredentials: withCredentials});
},
globalEval: globalEval,
getLocalURL: getLocalURL
}
};
})();
return Intercooler;
}));