diff --git a/brutaldon/static/js/intercooler.js b/brutaldon/static/js/intercooler.js index ff2b172..dec7355 100644 --- a/brutaldon/static/js/intercooler.js +++ b/brutaldon/static/js/intercooler.js @@ -4,7 +4,7 @@ define(["jquery"], function (a0) { return (root['Intercooler'] = factory(a0)); }); - } else if (typeof exports === 'object') { + } 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. @@ -24,7 +24,7 @@ var Intercooler = Intercooler || (function() { // work around zepto build issue TODO - fix me if((typeof Zepto !== "undefined") && ($ == null)) { - $ = Zepto + window["$"] = Zepto } //-------------------------------------------------- @@ -68,7 +68,7 @@ var Intercooler = Intercooler || (function() { return false; } return adest.slice(0, asrc.length).join("/") == asrc.join("/") || - asrc.slice(0, adest.length).join("/") == adest.join("/"); + asrc.slice(0, adest.length).join("/") == adest.join("/"); }; //============================================================ @@ -88,9 +88,10 @@ var Intercooler = Intercooler || (function() { } function hideIndicator(elt) { - if (elt.data('ic-use-transition')) { + 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(); } @@ -259,6 +260,11 @@ var Intercooler = Intercooler || (function() { 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"); @@ -351,14 +357,19 @@ var Intercooler = Intercooler || (function() { function beforeRequest(elt) { elt.addClass('disabled'); + elt.addClass('ic-request-in-flight'); elt.data('ic-request-in-flight', true); } - function requestCleanup(indicator, elt) { + 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"](); @@ -404,19 +415,19 @@ var Intercooler = Intercooler || (function() { var names = []; var values = []; if (args) { - for (var i = 0; i < args.length; i++) { - names.push(args[i][0]); - values.push(args[i][1]); - } + 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); + return window[script].apply(this, values); } else { - var outerfunc = window["eval"].call( - window, - '(function (' + names.join(", ") + ') {' + script + '})' - ); - return outerfunc.apply(this, values); + var outerfunc = window["eval"].call( + window, + '(function (' + names.join(", ") + ') {' + script + '})' + ); + return outerfunc.apply(this, values); } } @@ -439,12 +450,35 @@ var Intercooler = Intercooler || (function() { 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) { @@ -479,17 +513,18 @@ var Intercooler = Intercooler || (function() { } maybeInvokeLocalAction(elt, "-beforeSend"); }, - success: function(data, textStatus, xhr) { - triggerEvent(elt, "success.ic", [elt, data, textStatus, xhr, requestId]); + 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", data], ["textStatus", textStatus], ["xhr", xhr]]) == false) { + 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"); @@ -497,10 +532,12 @@ var Intercooler = Intercooler || (function() { if (xhr.getResponseHeader("X-IC-PushURL") || closestAttrValue(elt, 'ic-push-url') == "true") { try { - requestCleanup(indicator, elt); // clean up before snap-shotting HTML - var newUrl = xhr.getResponseHeader("X-IC-PushURL") || closestAttrValue(elt, 'ic-src'); + 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); + _history.snapshotForHistory(newUrl, oldTitle); } else { throw "History support not enabled"; } @@ -509,11 +546,11 @@ var Intercooler = Intercooler || (function() { } } - success(data, textStatus, elt, xhr); + success(responseData, textStatus, elt, xhr); log(elt, "Process content for request " + requestId + " in " + (new Date() - beforeSuccess) + "ms", "DEBUG"); } - triggerEvent(elt, "after.success.ic", [elt, data, textStatus, xhr, requestId]); + 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"); @@ -531,7 +568,7 @@ var Intercooler = Intercooler || (function() { }, complete: function(xhr, status) { log(elt, "AJAX request " + requestId + " completed in " + (new Date() - requestStart) + "ms", "DEBUG"); - requestCleanup(indicator, elt); + requestCleanup(indicator, globalIndicator, elt); try { if ($.contains(document, elt[0])) { triggerEvent(elt, "complete.ic", [elt, data, status, xhr, requestId]); @@ -557,14 +594,24 @@ var Intercooler = Intercooler || (function() { triggerEvent($(document), "beforeAjaxSend.ic", [ajaxSetup, elt]); if(ajaxSetup.cancel) { - requestCleanup(indicator, elt); + 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 = null; + var indicator = $([]); elt = $(elt); if (getICAttribute(elt, 'ic-indicator')) { indicator = $(getICAttribute(elt, 'ic-indicator')).first(); @@ -612,14 +659,14 @@ var Intercooler = Intercooler || (function() { return data; } - function appendData(data, string, value) { + function appendData(data, key, value) { if ($.type(data) === "string") { if($.type(value) !== "string") { value = JSON.stringify(value); } - return data + "&" + string + "=" + encodeURIComponent(value); + return data + "&" + key + "=" + encodeURIComponent(value); } else { - data.append(string, value); + data.append(key, value); return data; } } @@ -727,6 +774,7 @@ var Intercooler = Intercooler || (function() { }); } else { processMacros(elt); + processEnhancement(elt); processSources(elt); processPolling(elt); processEventSources(elt); @@ -846,6 +894,18 @@ var Intercooler = Intercooler || (function() { } } + 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); @@ -1024,14 +1084,15 @@ var Intercooler = Intercooler || (function() { elt = $(elt); if (getICAttribute(elt, 'ic-sse-src')) { var evtSrcUrl = getICAttribute(elt, 'ic-sse-src'); - var eventSource = initEventSource(elt, evtSrcUrl); + 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) { - var eventSource = Intercooler._internal.initEventSource(evtSrcUrl); + function initEventSource(elt, evtSrcUrl, evtSrcWithCredentials) { + var eventSource = Intercooler._internal.initEventSource(evtSrcUrl, evtSrcWithCredentials); eventSource.onmessage = function(e) { processICResponse(e.data, elt, false); }; @@ -1069,7 +1130,9 @@ var Intercooler = Intercooler || (function() { } function handleTriggerOn(elt) { - if (getICAttribute(elt, 'ic-trigger-on')) { + + 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){ @@ -1080,59 +1143,65 @@ var Intercooler = Intercooler || (function() { } }); } - if (getICAttribute(elt, 'ic-trigger-on') == 'load') { - fireICRequest(elt); - } else if (getICAttribute(elt, 'ic-trigger-on') == 'scrolled-into-view') { - initScrollHandler(); - setTimeout(function() { - triggerEvent($(window), 'scroll'); - }, 100); // Trigger a scroll in case element is already viewable - } else { - var triggerOn = getICAttribute(elt, 'ic-trigger-on').split(" "); - if(triggerOn[0].indexOf("sse:") == 0) { - //Server-sent event, find closest event source and register for it - var sourceElt = elt.closest(getICAttributeSelector('ic-sse-src')); - if(sourceElt) { - registerSSE(sourceElt, triggerOn[0].substr(4)) - } + 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 { - var triggerOn = getICAttribute($(elt), 'ic-trigger-on').split(" "); - var event = eventFor(triggerOn[0], $(elt)); - $(getTriggeredElement(elt)).on(event, 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"); + 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); } - - if (triggerOn[1] == '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 (triggerOn[1] == '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(event && (event.indexOf("timeout:") == 0)) { - setTimeout(function () { - $(getTriggeredElement(elt)).trigger(event); - }, parseInterval(event.split(":")[1])); } } } @@ -1178,6 +1247,7 @@ var Intercooler = Intercooler || (function() { setIfAbsent(elt, 'ic-trigger-on', 'default'); setIfAbsent(elt, 'ic-deps', 'ignore'); } + if (macroIs(macro, 'ic-action')) { setIfAbsent(elt, 'ic-trigger-on', 'default'); } @@ -1209,6 +1279,51 @@ var Intercooler = Intercooler || (function() { } } + 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); @@ -1222,7 +1337,7 @@ var Intercooler = Intercooler || (function() { function isScrolledIntoView(elem) { elem = $(elem); if (elem.height() == 0 && elem.width() == 0) { - return false; + return false; } var docViewTop = $(window).scrollTop(); var docViewBottom = docViewTop + $(window).height(); @@ -1231,13 +1346,13 @@ var Intercooler = Intercooler || (function() { var elemBottom = elemTop + elem.height(); return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom) - && (elemBottom <= docViewBottom) && (elemTop >= docViewTop)); + && (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')) { + (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')); @@ -1352,6 +1467,19 @@ var Intercooler = Intercooler || (function() { 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); + } + }) + } + } }; @@ -1449,7 +1577,7 @@ var Intercooler = Intercooler || (function() { } } - function fireICRequest(elt, alternateHandler) { + function fireICRequest(elt, alternateHandler) { elt = $(elt); var triggerOrigin = elt; @@ -1628,9 +1756,9 @@ var Intercooler = Intercooler || (function() { /* Instance Methods */ function historyConfigHasChanged(historySupportData) { return historySupportData == null || - historySupportData.slotLimit != slotLimit || - historySupportData.historyVersion != historyVersion || - historySupportData.lruList == null + historySupportData.slotLimit != slotLimit || + historySupportData.historyVersion != historyVersion || + historySupportData.lruList == null } function clearHistory() { @@ -1687,18 +1815,19 @@ var Intercooler = Intercooler || (function() { 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"); + "local storage? History Limit:" + slotLimit, "ERROR"); } } } - function makeHistoryEntry(html, yOffset, url) { + function makeHistoryEntry(html, yOffset, url, title) { var restorationData = { "url": url, "id": HISTORY_SLOT_PREFIX + url, "content": html, "yOffset": yOffset, - "timestamp": new Date().getTime() + "timestamp": new Date().getTime(), + "title": title }; updateLRUList(url); // save to the history slot @@ -1724,18 +1853,18 @@ var Intercooler = Intercooler || (function() { function updateHistory() { if (_snapshot) { - pushUrl(_snapshot.newUrl, currentUrl(), _snapshot.oldHtml, _snapshot.yOffset); + pushUrl(_snapshot.newUrl, currentUrl(), _snapshot.oldHtml, _snapshot.yOffset, _snapshot.oldTitle); _snapshot = null; } } - function pushUrl(newUrl, originalUrl, originalHtml, yOffset) { + function pushUrl(newUrl, originalUrl, originalHtml, yOffset, originalTitle) { - var historyEntry = makeHistoryEntry(originalHtml, yOffset, originalUrl); + 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); + var restorationData = makeHistoryEntry(t.html(), window.pageYOffset, newUrl, document.title); history.pushState({'ic-id': restorationData.id}, "", newUrl); triggerEvent(t, "pushUrl.ic", [t, restorationData]); @@ -1748,7 +1877,12 @@ var Intercooler = Intercooler || (function() { if (historyData) { processICResponse(historyData["content"], getTargetForHistory($('body')), true); if (historyData["yOffset"]) { - window.scrollTo(0, historyData["yOffset"]) + setTimeout(function () { + window.scrollTo(0, historyData["yOffset"]); + }, 100); + } + if (historyData["title"]) { + document.title = historyData["title"]; } return true; } else { @@ -1771,13 +1905,14 @@ var Intercooler = Intercooler || (function() { } } - function snapshotForHistory(newUrl) { + function snapshotForHistory(newUrl, oldTitle) { var t = getTargetForHistory($('body')); triggerEvent(t, "beforeHistorySnapshot.ic", [t]); _snapshot = { newUrl: newUrl, oldHtml: t.html(), - yOffset: window.pageYOffset + yOffset: window.pageYOffset, + oldTitle: oldTitle }; } @@ -1897,12 +2032,11 @@ var Intercooler = Intercooler || (function() { if($.zepto) { $('body').data('zeptoDataTest', {}); if(typeof($('body').data('zeptoDataTest')) == "string") { - console.log("!!!! Please include the data module with Zepto! Intercooler requires full data support to function !!!!") + log(null, + "!!!! Please include the data module with Zepto! Intercooler requires full data support to function !!!!", + "ERROR") } } - if (location.search && location.search.indexOf("ic-launch-debugger=true") >= 0) { - Intercooler.debug(); - } } $(function() { @@ -1922,27 +2056,22 @@ var Intercooler = Intercooler || (function() { isDependent: isDependent, getTarget: getTarget, processHeaders: processHeaders, + startPolling: startPolling, + cancelPolling: cancelPolling, setIsDependentFunction: function(func) { _isDependentFunction = func; }, ready: function(readyHandler) { _readyHandlers.push(readyHandler); }, - debug: function() { - var debuggerUrl = closestAttrValue('body', 'ic-debugger-url') || - "https://intercoolerreleases-leaddynocom.netdna-ssl.com/intercooler-debugger.js"; - $.getScript(debuggerUrl) - .fail(function(jqxhr, settings, exception) { - log($('body'), formatError(exception), "ERROR"); - }); - }, _internal: { init: init, replaceOrAddMethod: replaceOrAddMethod, - initEventSource: function(url) { - return new EventSource(url); + initEventSource: function(url, withCredentials) { + return new EventSource(url, {withCredentials: withCredentials}); }, - globalEval: globalEval + globalEval: globalEval, + getLocalURL: getLocalURL } }; })();