From ab1668d68b9be4586a406bcc2e8a0c092fcb546c Mon Sep 17 00:00:00 2001 From: carson Date: Tue, 5 May 2020 04:17:30 -0700 Subject: [PATCH] Formalize settle concept --- TODO.md | 1 + dist/htmx.js | 247 ++++++++++++++++++++++++-------------------- dist/htmx.min.js | 2 +- dist/htmx.min.js.gz | Bin 4173 -> 4344 bytes src/htmx.js | 159 ++++++++++++++++------------ test/scratch.html | 10 +- 6 files changed, 238 insertions(+), 181 deletions(-) diff --git a/TODO.md b/TODO.md index 38aa6d37..d8c35b0e 100644 --- a/TODO.md +++ b/TODO.md @@ -17,6 +17,7 @@ * polling cancellation API 205 code * meta config tag * simple logging API +* hx-toggle-class * Testing * polling diff --git a/dist/htmx.js b/dist/htmx.js index 0de78c93..41b3559d 100644 --- a/dist/htmx.js +++ b/dist/htmx.js @@ -117,6 +117,10 @@ var HTMx = HTMx || (function () { return getDocument().body.contains(elt); } + function concat(arr1, arr2) { + return arr1.concat(arr2); + } + //==================================================================== // Node processing //==================================================================== @@ -152,19 +156,21 @@ var HTMx = HTMx || (function () { } function handleOutOfBandSwaps(fragment) { + var settleTasks = []; forEach(fragment.children, function(child){ if (getAttributeValue(child, "hx-swap-oob") === "true") { var target = getDocument().getElementById(child.id); if (target) { var fragment = new DocumentFragment() fragment.append(child); - swapOuterHTML(target, fragment); + settleTasks = settleTasks.concat(swapOuterHTML(target, fragment)); } else { child.parentNode.removeChild(child); triggerEvent(getDocument().body, "oobErrorNoTarget.hx", {id:child.id, content:child}) } } }) + return settleTasks; } function handleAttributes(parentNode, fragment) { @@ -179,11 +185,7 @@ var HTMx = HTMx || (function () { }); } }); - setTimeout(function () { - forEach(attributeSwaps, function (swap) { - swap.call(); - }); - }, 100); + return attributeSwaps; } function insertNodesBefore(parentNode, insertBefore, fragment) { @@ -200,32 +202,37 @@ var HTMx = HTMx || (function () { function swapOuterHTML(target, fragment) { if (target.tagName === "BODY") { - swapInnerHTML(target, fragment); + return swapInnerHTML(target, fragment); } else { - insertNodesBefore(parentElt(target), target, fragment); + var settleTasks = insertNodesBefore(parentElt(target), target, fragment); parentElt(target).removeChild(target); + return settleTasks; } } function swapPrepend(target, fragment) { - insertNodesBefore(target, target.firstChild, fragment); + return insertNodesBefore(target, target.firstChild, fragment); } function swapPrependBefore(target, fragment) { - insertNodesBefore(parentElt(target), target, fragment); + return insertNodesBefore(parentElt(target), target, fragment); } function swapAppend(target, fragment) { - insertNodesBefore(target, null, fragment); + return insertNodesBefore(target, null, fragment); } function swapAppendAfter(target, fragment) { - insertNodesBefore(parentElt(target), target.nextSibling, fragment); + return insertNodesBefore(parentElt(target), target.nextSibling, fragment); } function swapInnerHTML(target, fragment) { - target.innerHTML = ""; - insertNodesBefore(target, null, fragment); + var firstChild = target.firstChild; + return insertNodesBefore(target, firstChild, fragment); + while (firstChild.nextSibling) { + target.removeChild(firstChild.nextSibling); + } + target.removeChild(firstChild); } function maybeSelectFromResponse(elt, fragment) { @@ -240,30 +247,20 @@ var HTMx = HTMx || (function () { return fragment; } - function swapResponse(target, elt, responseText, callBack) { - + function swapResponse(target, elt, responseText) { var fragment = makeFragment(responseText); - handleOutOfBandSwaps(fragment); + var settleTasks = handleOutOfBandSwaps(fragment); fragment = maybeSelectFromResponse(elt, fragment); var swapStyle = getClosestAttributeValue(elt, "hx-swap"); - if (swapStyle === "outerHTML") { - swapOuterHTML(target, fragment); - } else if (swapStyle === "prepend") { - swapPrepend(target, fragment); - } else if (swapStyle === "prependBefore") { - swapPrependBefore(target, fragment); - } else if (swapStyle === "append") { - swapAppend(target, fragment); - } else if (swapStyle === "appendAfter") { - swapAppendAfter(target, fragment); - } else { - swapInnerHTML(target, fragment); - } - - if(callBack) { - callBack.call(); + switch(swapStyle) { + case "outerHTML": return concat(settleTasks, swapOuterHTML(target, fragment)); + case "prepend": return concat(settleTasks, swapPrepend(target, fragment)); + case "prependBefore": return concat(settleTasks, swapPrependBefore(target, fragment)); + case "append": return concat(settleTasks, swapAppend(target, fragment)); + case "appendAfter": return concat(settleTasks, swapAppendAfter(target, fragment)); + default: return concat(settleTasks, swapInnerHTML(target, fragment)); } } @@ -583,62 +580,78 @@ var HTMx = HTMx || (function () { //==================================================================== // History Support //==================================================================== - - function makeHistoryId() { - return Math.random().toString(36).substr(3, 9); - } - function getHistoryElement() { - var historyElt = getDocument().getElementsByClassName('hx-history-element'); - if (historyElt.length > 0) { - return historyElt[0]; - } else { - return getDocument().body; + var historyElt = getDocument().querySelector('.hx-history-element'); + return historyElt || getDocument().body; + } + + function purgeOldestPaths(paths, historyTimestamps) { + var paths = paths.sort(function (path1, path2) { + return historyTimestamps[path2] - historyTimestamps[path1] + }); + var slot = 0; + forEach(paths, function (path) { + slot++; + if (slot > 20) { + delete historyTimestamps[path]; + localStorage.removeItem(path); + } + }); + } + + function bumpHistoryAccessDate(pathAndSearch) { + var historyTimestamps = JSON.parse(localStorage.getItem("hx-history-timestamps")) || {}; + historyTimestamps[pathAndSearch] = Date.now; + var paths = Object.keys(historyTimestamps); + if (paths.length > 20) { + purgeOldestPaths(paths, historyTimestamps); } + localStorage.setItem("hx-history-timestamps", JSON.stringify(historyTimestamps)); } - function saveLocalHistoryData(historyData) { - localStorage.setItem('hx-history', JSON.stringify(historyData)); - } - - function getLocalHistoryData() { - var historyEntry = localStorage.getItem('hx-history'); - var historyData; - if (historyEntry) { - historyData = JSON.parse(historyEntry); - } else { - var initialId = makeHistoryId(); - historyData = {"current": initialId, "slots": [initialId]}; - saveLocalHistoryData(historyData); - } - return historyData; - } - - function newHistoryData() { - var historyData = getLocalHistoryData(); - var newId = makeHistoryId(); - var slots = historyData.slots; - if (slots.length > 20) { - var toEvict = slots.shift(); - localStorage.removeItem('hx-history-' + toEvict); - } - slots.push(newId); - historyData.current = newId; - saveLocalHistoryData(historyData); - } - - function updateCurrentHistoryContent() { + function saveHistory() { var elt = getHistoryElement(); - var historyData = getLocalHistoryData(); - history.replaceState({"hx-history-key": historyData.current}, getDocument().title, window.location.href); - localStorage.setItem('hx-history-' + historyData.current, elt.innerHTML); + var pathAndSearch = location.pathname+location.search; + triggerEvent(getDocument().body, "historyUpdate.hx", {path:pathAndSearch, historyElement:elt}); + history.replaceState({}, getDocument().title, window.location.href); + localStorage.setItem('hx-history-content-' + pathAndSearch, elt.innerHTML); + bumpHistoryAccessDate(pathAndSearch); } - function restoreHistory(data) { - var historyKey = data['hx-history-key']; - var content = localStorage.getItem('hx-history-' + historyKey); - var elt = getHistoryElement(); - swapInnerHTML(elt, makeFragment(content)); + function pushUrlIntoHistory(url) { + history.pushState({}, "", url ); + } + + function settleImmediately(settleTasks) { + forEach(settleTasks, function (task) { + task.call(); + }); + } + + function loadHistoryFromServer(pathAndSearch) { + triggerEvent(getDocument().body, "historyCacheMiss.hx", {path: pathAndSearch}); + var request = new XMLHttpRequest(); + request.open('GET', pathAndSearch, true); + request.onload = function () { + triggerEvent(getDocument().body, "historyCacheMissLoad.hx", {path: pathAndSearch}); + if (this.status >= 200 && this.status < 400) { + var fragment = makeFragment(this.response); + fragment = fragment.querySelector('.hx-history-element') || fragment; + settleImmediately(swapInnerHTML(getHistoryElement(), fragment)); + } + }; + } + + function restoreHistory() { + var pathAndSearch = location.pathname+location.search; + triggerEvent(getDocument().body, "historyUpdate.hx", {path:pathAndSearch}); + var content = localStorage.getItem('hx-history-content-' + pathAndSearch); + if (content) { + bumpHistoryAccessDate(pathAndSearch); + settleImmediately(swapInnerHTML(getHistoryElement(), makeFragment(content))); + } else { + loadHistoryFromServer(pathAndSearch); + } } function shouldPush(elt) { @@ -646,21 +659,6 @@ var HTMx = HTMx || (function () { (elt.tagName === "A" && getInternalData(elt).boosted); } - function snapshotForCurrentHistoryEntry(elt) { - if (shouldPush(elt)) { - // TODO event to allow de-initialization of HTML elements in target - updateCurrentHistoryContent(); - } - } - - function initNewHistoryEntry(elt, url) { - if (shouldPush(elt)) { - newHistoryData(); - history.pushState({}, "", url); - updateCurrentHistoryContent(); - } - } - function addRequestIndicatorClasses(elt) { mutateRequestIndicatorClasses(elt, "add"); } @@ -804,11 +802,14 @@ var HTMx = HTMx || (function () { if(!triggerEvent(elt, 'values.hx', {values: inputValues, target:target})) return endRequestLock(); // request type + var requestURL; if (verb === 'get') { var noValues = Object.keys(inputValues).length === 0; - xhr.open('GET', path + (noValues ? "" : "?" + urlEncode(inputValues)), true); + requestURL = path + (noValues ? "" : "?" + urlEncode(inputValues)); + xhr.open('GET', requestURL, true); } else { - xhr.open('POST', path, true); + requestURL = path; + xhr.open('POST', requestURL, true); setHeader(xhr,'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8', true); if (verb !== 'post') { setHeader(xhr, 'X-HTTP-Method-Override', verb.toUpperCase(), true); @@ -841,30 +842,56 @@ var HTMx = HTMx || (function () { xhr.onload = function () { try { if (!triggerEvent(elt, 'beforeOnLoad.hx', {xhr: xhr, target: target})) return; - snapshotForCurrentHistoryEntry(elt, path); - var trigger = this.getResponseHeader("X-HX-Trigger"); - handleTrigger(elt, trigger); - initNewHistoryEntry(elt, path); + + handleTrigger(elt, this.getResponseHeader("X-HX-Trigger")); + var pushedUrl = this.getResponseHeader("X-HX-Push") + + var shouldSaveHistory = shouldPush(elt) || pushedUrl; + if (this.status >= 200 && this.status < 400) { // don't process 'No Content' response if (this.status !== 204) { // Success! var resp = this.response; if (!triggerEvent(elt, 'beforeSwap.hx', {xhr: xhr, target: target})) return; + + // Save current page + if (shouldSaveHistory) { + saveHistory(); + } + target.classList.add("hx-swapping"); var doSwap = function () { try { - swapResponse(target, elt, resp, function () { - target.classList.remove("hx-swapping"); - updateCurrentHistoryContent(); - triggerEvent(elt, 'afterSwap.hx', {xhr: xhr, target: target}); - }); + var settleTasks = swapResponse(target, elt, resp); + target.classList.remove("hx-swapping"); + target.classList.add("hx-settling"); + triggerEvent(elt, 'afterSwap.hx', {xhr: xhr, target: target}); + + var doSettle = function(){ + forEach(settleTasks, function (task) { + task.call(); + }); + target.classList.remove("hx-settling"); + // push URL and save new page + pushUrlIntoHistory(pushedUrl || requestURL ); + saveHistory(); + triggerEvent(elt, 'afterSettle.hx', {xhr: xhr, target: target}); + } + + var settleDelayStr = getAttributeValue(elt, "hx-settle-delay") || "100ms"; + if (settleDelayStr) { + setTimeout(doSettle, parseInterval(settleDelayStr)) + } else { + doSettle(); + } } catch (e) { triggerEvent(elt, 'swapError.hx', {xhr: xhr, response: xhr.response, status: xhr.status, target: target}); throw e; } }; - var swapDelayStr = getAttributeValue(elt, "hx-swap-delay"); + + var swapDelayStr = getAttributeValue(elt, "hx-swap-delay") || "100ms"; if (swapDelayStr) { setTimeout(doSwap, parseInterval(swapDelayStr)) } else { @@ -908,7 +935,7 @@ var HTMx = HTMx || (function () { ready(function () { processNode(getDocument().body); window.onpopstate = function (event) { - restoreHistory(event.state); + restoreHistory(); }; }) diff --git a/dist/htmx.min.js b/dist/htmx.min.js index 1f67df9d..d3dfefca 100644 --- a/dist/htmx.min.js +++ b/dist/htmx.min.js @@ -1 +1 @@ -var HTMx=HTMx||function(){"use strict";var e=["get","post","put","delete","patch"];function h(e){if(e==="null"||e==="false"||e===""){return null}else if(e.lastIndexOf("ms")===e.length-2){return parseFloat(e.substr(0,e.length-2))}else if(e.lastIndexOf("s")===e.length-1){return parseFloat(e.substr(0,e.length-1))*1e3}else{return parseFloat(e)}}function d(e,t){return e.getAttribute&&e.getAttribute(t)}function g(e,t){return d(e,t)||d(e,"data-"+t)}function i(e){return e.parentElement}function p(){return document}function a(e,t){if(t(e)){return e}else if(i(e)){return a(i(e),t)}else{return null}}function m(e,t){var r=null;a(e,function(e){return r=d(e,t)});return r}function u(e,t){return e!=null&&(e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector).call(e,t)}function o(e,t){do{if(e==null||u(e,t))return e}while(e=e&&i(e))}function s(e){var t=p().createRange();return t.createContextualFragment(e)}function t(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function l(e){return t(e,"Function")}function f(e){return t(e,"Object")}function x(e){var t="hx-data-internal";var r=e[t];if(!r){r=e[t]={}}return r}function r(e){var t=[];c(e,function(e){t.push(e)});return t}function c(e,t){for(var r=0;r=0}function v(e){return p().body.contains(e)}function y(e){var t=a(e,function(e){return d(e,"hx-target")!==null});if(t){var r=d(t,"hx-target");if(r==="this"){return t}else{return p().querySelector(r)}}else{var n=x(e);if(n.boosted){return p().body}else{return e}}}function S(t,r){c(t.attributes,function(e){if(!r.hasAttribute(e.name)){t.removeAttribute(e.name)}});c(r.attributes,function(e){t.setAttribute(e.name,e.value)})}function w(e){c(e.children,function(e){if(g(e,"hx-swap-oob")==="true"){var t=p().getElementById(e.id);if(t){var r=new DocumentFragment;r.append(e);T(t,r)}else{e.parentNode.removeChild(e);Y(p().body,"oobErrorNoTarget.hx",{id:e.id,content:e})}}})}function E(n,e){var i=[];c(e.querySelectorAll("[id]"),function(e){var t=n.querySelector(e.tagName+"[id="+e.id+"]");if(t){var r=e.cloneNode();S(e,t);i.push(function(){S(e,r)})}});setTimeout(function(){c(i,function(e){e.call()})},100)}function b(e,t,r){E(e,r);while(r.childNodes.length>0){var n=r.firstChild;e.insertBefore(n,t);if(n.nodeType!==Node.TEXT_NODE){Y(n,"load.hx",{elt:n,parent:i(n)});_(n)}}}function T(e,t){if(e.tagName==="BODY"){N(e,t)}else{b(i(e),e,t);i(e).removeChild(e)}}function O(e,t){b(e,e.firstChild,t)}function L(e,t){b(i(e),e,t)}function C(e,t){b(e,null,t)}function A(e,t){b(i(e),e.nextSibling,t)}function N(e,t){e.innerHTML="";b(e,null,t)}function H(e,t){var r=m(e,"hx-select");if(r){var n=new DocumentFragment;c(t.querySelectorAll(r),function(e){n.append(e)});t=n}return t}function M(e,t,r,n){var i=s(r);w(i);i=H(t,i);var o=m(t,"hx-swap");if(o==="outerHTML"){T(e,i)}else if(o==="prepend"){O(e,i)}else if(o==="prependBefore"){L(e,i)}else if(o==="append"){C(e,i)}else if(o==="appendAfter"){A(e,i)}else{N(e,i)}if(n){n.call()}}function q(e,t){if(t){if(t.indexOf("{")===0){var r=JSON.parse(t);for(var n in r){if(r.hasOwnProperty(n)){var i=r[n];if(!f(i)){i={value:i}}Y(e,n,i)}}}else{Y(e,t,[])}}}function I(e){var t=m(e,"hx-trigger");if(t){return t}else{if(u(e,"button")){return"click"}else if(u(e,"form")){return"submit"}else if(u(e,"input, textarea, select")){return"change"}else{return"click"}}}function F(o,e,a){var t=e.split(",");c(t,function(e){var t="";var r=50;var n=e.trim();if(n.indexOf(":")>0){var i=n.split(":");t=i[0];r=h(i[1])}else{t=n}setTimeout(function(){o.classList[a].call(o.classList,t)},r)})}function R(e,t,r){var n=I(e);var i=x(e);if(n.trim().indexOf("every ")===0){var o=n.split(/\s+/);var a=o[1];if(a){var u=h(a);i.timeout=setTimeout(function(){if(v(e)){pe(e,t,r);R(e,t,g(e,"hx-"+t))}},u)}}}function X(e){return location.hostname===e.hostname&&d(e,"href")&&!d(e,"href").startsWith("#")}function D(e,t,r){if(e.tagName==="A"&&X(e)||e.tagName==="FORM"){t.boosted=true;var n,i;if(e.tagName==="A"){n="get";i=d(e,"href")}else{var o=d(e,"method");n=o?o.toLowerCase():"get";i=d(e,"action")}k(e,n,i,t,r,true)}}function k(o,a,u,e,t,s){var r=function(e){if(s)e.preventDefault();var t=x(e);var r=x(o);if(!t.handled){t.handled=true;if(g(o,"hx-trigger-once")==="true"){if(r.triggeredOnce){return}else{r.triggeredOnce=true}}if(g(o,"hx-trigger-changed-only")==="true"){if(r.lastValue===o.value){return}else{r.lastValue=o.value}}if(r.delayed){clearTimeout(r.delayed)}var n=g(o,"hx-trigger-delay");var i=function(){pe(o,a,u,e.target)};if(n){r.delayed=setTimeout(i,h(n))}else{i()}}};e.trigger=t;e.eventListener=r;o.addEventListener(t,r)}function B(){if(!window["hxScrollHandler"]){var e=function(){c(p().querySelectorAll("[hx-trigger='reveal']"),function(e){P(e)})};window["hxScrollHandler"]=e;window.addEventListener("scroll",e)}}function P(e){var t=x(e);if(!t.revealed&&n(e)){t.revealed=true;pe(e,t.verb,t.path)}}function U(e){if(!v(e)){e.sseSource.close();return true}}function j(t,e){var r={initializer:function(){new EventSource(e,r.config)},config:{withCredentials:true}};Y(t,"initSSE.mx",{config:r});var n=r.initializer();n.onerror=function(e){Y(t,"sseError.mx",{error:e,source:n});U(t)};x(t).sseSource=n}function J(e,t,r,n){var i=a(t,function(e){return e.sseSource});if(i){var o=function(){if(!U(i)){if(v(t)){pe(t,r,n)}else{i.sseSource.removeEventListener(e,o)}}};i.sseSource.addEventListener(e,o)}else{Y(t,"noSSESourceError.mx")}}function z(e,t,r,n){if(!e.loaded){e.loaded=true;pe(t,r,n)}}function V(r,n,i){var o=false;c(e,function(e){var t=g(r,"hx-"+e);if(t){o=true;n.path=t;n.verb=e;if(i.indexOf("sse:")===0){J(i.substr(4),r,e,t)}else if(i==="revealed"){B();P(r)}else if(i==="load"){z(n,r,e,t)}else if(i.trim().indexOf("every ")===0){n.polling=true;R(r,e,t)}else{k(r,e,t,n,i)}}});return o}function _(e){var t=x(e);if(!t.processed){t.processed=true;var r=I(e);var n=V(e,t,r);if(!n&&m(e,"hx-boost")==="true"){D(e,t,r)}var i=g(e,"hx-sse-source");if(i){j(e,i)}var o=g(e,"hx-add-class");if(o){F(e,o,"add")}var a=g(e,"hx-remove-class");if(a){F(e,a,"remove")}}c(e.children,function(e){_(e)})}function G(e,t,r){var n=m(e,"hx-error-url");if(n){var i=new XMLHttpRequest;i.open("POST",n);i.setRequestHeader("Content-Type","application/json;charset=UTF-8");i.send(JSON.stringify({elt:e.id,event:t,details:r}))}}function W(e,t){var r;if(window.CustomEvent&&typeof window.CustomEvent==="function"){r=new CustomEvent(e,{detail:t})}else{r=p().createEvent("CustomEvent");r.initCustomEvent(e,true,true,t)}return r}function Y(e,t,r){r["elt"]=e;var n=W(t,r);if(HTMx.logger){HTMx.logger(e,t,r);if(t.indexOf("Error")>0){G(e,t,r)}}var i=e.dispatchEvent(n);var o=e.dispatchEvent(W("all.hx",{elt:e,originalDetails:r,originalEvent:n}));return i&&o}function K(e,t,r){var n,i,o;if(l(e)){n=p().body;i="all.hx";o=e}else if(l(t)){n=p().body;i=e;o=t}else{n=e;i=t;o=r}return n.addEventListener(i,o)}function Q(){return Math.random().toString(36).substr(3,9)}function Z(){var e=p().getElementsByClassName("hx-history-element");if(e.length>0){return e[0]}else{return p().body}}function $(e){localStorage.setItem("hx-history",JSON.stringify(e))}function ee(){var e=localStorage.getItem("hx-history");var t;if(e){t=JSON.parse(e)}else{var r=Q();t={current:r,slots:[r]};$(t)}return t}function te(){var e=ee();var t=Q();var r=e.slots;if(r.length>20){var n=r.shift();localStorage.removeItem("hx-history-"+n)}r.push(t);e.current=t;$(e)}function re(){var e=Z();var t=ee();history.replaceState({"hx-history-key":t.current},p().title,window.location.href);localStorage.setItem("hx-history-"+t.current,e.innerHTML)}function ne(e){var t=e["hx-history-key"];var r=localStorage.getItem("hx-history-"+t);var n=Z();N(n,s(r))}function ie(e){return m(e,"hx-push-url")==="true"||e.tagName==="A"&&x(e).boosted}function oe(e){if(ie(e)){re()}}function ae(e,t){if(ie(e)){te();history.pushState({},"",t);re()}}function ue(e){le(e,"add")}function se(e){le(e,"remove")}function le(e,t){var r=m(e,"hx-indicator");if(r){var n=p().querySelectorAll(r)}else{n=[e]}c(n,function(e){e.classList[t].call(e.classList,"hx-show-indicator")})}function fe(e,t){for(var r=0;r=200&&this.status<400){if(this.status!==204){var t=this.response;if(!Y(i,"beforeSwap.hx",{xhr:f,target:u}))return;u.classList.add("hx-swapping");var r=function(){try{M(u,i,t,function(){u.classList.remove("hx-swapping");re();Y(i,"afterSwap.hx",{xhr:f,target:u})})}catch(e){Y(i,"swapError.hx",{xhr:f,response:f.response,status:f.status,target:u});throw e}};var n=g(i,"hx-swap-delay");if(n){setTimeout(r,h(n))}else{r()}}}else{Y(i,"responseError.hx",{xhr:f,response:f.response,status:f.status,target:u})}}catch(e){Y(i,"onLoadError.hx",{xhr:f,response:f.response,status:f.status,target:u});throw e}finally{se(i);a();Y(i,"afterOnLoad.hx",{xhr:f,response:f.response,status:f.status,target:u})}};f.onerror=function(){se(i);Y(i,"loadError.hx",{xhr:f});a()};if(!Y(i,"beforeRequest.hx",{xhr:f,values:c,target:u}))return a();ue(i);f.send(e==="get"?null:de(c))}function me(e){if(p().readyState!=="loading"){e()}else{p().addEventListener("DOMContentLoaded",e)}}me(function(){_(p().body);window.onpopstate=function(e){ne(e.state)}});function xe(e){return eval(e)}return{processElement:_,on:K,version:"0.0.2",_:xe}}(); \ No newline at end of file +var HTMx=HTMx||function(){"use strict";var e=["get","post","put","delete","patch"];function d(e){if(e==="null"||e==="false"||e===""){return null}else if(e.lastIndexOf("ms")===e.length-2){return parseFloat(e.substr(0,e.length-2))}else if(e.lastIndexOf("s")===e.length-1){return parseFloat(e.substr(0,e.length-1))*1e3}else{return parseFloat(e)}}function p(e,t){return e.getAttribute&&e.getAttribute(t)}function g(e,t){return p(e,t)||p(e,"data-"+t)}function i(e){return e.parentElement}function m(){return document}function o(e,t){if(t(e)){return e}else if(i(e)){return o(i(e),t)}else{return null}}function x(e,t){var r=null;o(e,function(e){return r=p(e,t)});return r}function u(e,t){return e!=null&&(e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector).call(e,t)}function a(e,t){do{if(e==null||u(e,t))return e}while(e=e&&i(e))}function s(e){var t=m().createRange();return t.createContextualFragment(e)}function t(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function l(e){return t(e,"Function")}function f(e){return t(e,"Object")}function y(e){var t="hx-data-internal";var r=e[t];if(!r){r=e[t]={}}return r}function r(e){var t=[];S(e,function(e){t.push(e)});return t}function S(e,t){for(var r=0;r=0}function c(e){return m().body.contains(e)}function v(e,t){return e.concat(t)}function w(e){var t=o(e,function(e){return p(e,"hx-target")!==null});if(t){var r=p(t,"hx-target");if(r==="this"){return t}else{return m().querySelector(r)}}else{var n=y(e);if(n.boosted){return m().body}else{return e}}}function h(t,r){S(t.attributes,function(e){if(!r.hasAttribute(e.name)){t.removeAttribute(e.name)}});S(r.attributes,function(e){t.setAttribute(e.name,e.value)})}function b(e){var n=[];S(e.children,function(e){if(g(e,"hx-swap-oob")==="true"){var t=m().getElementById(e.id);if(t){var r=new DocumentFragment;r.append(e);n=n.concat(O(t,r))}else{e.parentNode.removeChild(e);K(m().body,"oobErrorNoTarget.hx",{id:e.id,content:e})}}});return n}function E(n,e){var i=[];S(e.querySelectorAll("[id]"),function(e){var t=n.querySelector(e.tagName+"[id="+e.id+"]");if(t){var r=e.cloneNode();h(e,t);i.push(function(){h(e,r)})}});return i}function T(e,t,r){E(e,r);while(r.childNodes.length>0){var n=r.firstChild;e.insertBefore(n,t);if(n.nodeType!==Node.TEXT_NODE){K(n,"load.hx",{elt:n,parent:i(n)});_(n)}}}function O(e,t){if(e.tagName==="BODY"){return q(e,t)}else{var r=T(i(e),e,t);i(e).removeChild(e);return r}}function L(e,t){return T(e,e.firstChild,t)}function C(e,t){return T(i(e),e,t)}function A(e,t){return T(e,null,t)}function H(e,t){return T(i(e),e.nextSibling,t)}function q(e,t){var r=e.firstChild;return T(e,r,t);while(r.nextSibling){e.removeChild(r.nextSibling)}e.removeChild(r)}function M(e,t){var r=x(e,"hx-select");if(r){var n=new DocumentFragment;S(t.querySelectorAll(r),function(e){n.append(e)});t=n}return t}function N(e,t,r){var n=s(r);var i=b(n);n=M(t,n);var a=x(t,"hx-swap");switch(a){case"outerHTML":return v(i,O(e,n));case"prepend":return v(i,L(e,n));case"prependBefore":return v(i,C(e,n));case"append":return v(i,A(e,n));case"appendAfter":return v(i,H(e,n));default:return v(i,q(e,n))}}function I(e,t){if(t){if(t.indexOf("{")===0){var r=JSON.parse(t);for(var n in r){if(r.hasOwnProperty(n)){var i=r[n];if(!f(i)){i={value:i}}K(e,n,i)}}}else{K(e,t,[])}}}function R(e){var t=x(e,"hx-trigger");if(t){return t}else{if(u(e,"button")){return"click"}else if(u(e,"form")){return"submit"}else if(u(e,"input, textarea, select")){return"change"}else{return"click"}}}function F(a,e,o){var t=e.split(",");S(t,function(e){var t="";var r=50;var n=e.trim();if(n.indexOf(":")>0){var i=n.split(":");t=i[0];r=d(i[1])}else{t=n}setTimeout(function(){a.classList[o].call(a.classList,t)},r)})}function X(e,t,r){var n=R(e);var i=y(e);if(n.trim().indexOf("every ")===0){var a=n.split(/\s+/);var o=a[1];if(o){var u=d(o);i.timeout=setTimeout(function(){if(c(e)){ge(e,t,r);X(e,t,g(e,"hx-"+t))}},u)}}}function D(e){return location.hostname===e.hostname&&p(e,"href")&&!p(e,"href").startsWith("#")}function U(e,t,r){if(e.tagName==="A"&&D(e)||e.tagName==="FORM"){t.boosted=true;var n,i;if(e.tagName==="A"){n="get";i=p(e,"href")}else{var a=p(e,"method");n=a?a.toLowerCase():"get";i=p(e,"action")}P(e,n,i,t,r,true)}}function P(a,o,u,e,t,s){var r=function(e){if(s)e.preventDefault();var t=y(e);var r=y(a);if(!t.handled){t.handled=true;if(g(a,"hx-trigger-once")==="true"){if(r.triggeredOnce){return}else{r.triggeredOnce=true}}if(g(a,"hx-trigger-changed-only")==="true"){if(r.lastValue===a.value){return}else{r.lastValue=a.value}}if(r.delayed){clearTimeout(r.delayed)}var n=g(a,"hx-trigger-delay");var i=function(){ge(a,o,u,e.target)};if(n){r.delayed=setTimeout(i,d(n))}else{i()}}};e.trigger=t;e.eventListener=r;a.addEventListener(t,r)}function j(){if(!window["hxScrollHandler"]){var e=function(){S(m().querySelectorAll("[hx-trigger='reveal']"),function(e){k(e)})};window["hxScrollHandler"]=e;window.addEventListener("scroll",e)}}function k(e){var t=y(e);if(!t.revealed&&n(e)){t.revealed=true;ge(e,t.verb,t.path)}}function B(e){if(!c(e)){e.sseSource.close();return true}}function J(t,e){var r={initializer:function(){new EventSource(e,r.config)},config:{withCredentials:true}};K(t,"initSSE.mx",{config:r});var n=r.initializer();n.onerror=function(e){K(t,"sseError.mx",{error:e,source:n});B(t)};y(t).sseSource=n}function z(e,t,r,n){var i=o(t,function(e){return e.sseSource});if(i){var a=function(){if(!B(i)){if(c(t)){ge(t,r,n)}else{i.sseSource.removeEventListener(e,a)}}};i.sseSource.addEventListener(e,a)}else{K(t,"noSSESourceError.mx")}}function V(e,t,r,n){if(!e.loaded){e.loaded=true;ge(t,r,n)}}function G(r,n,i){var a=false;S(e,function(e){var t=g(r,"hx-"+e);if(t){a=true;n.path=t;n.verb=e;if(i.indexOf("sse:")===0){z(i.substr(4),r,e,t)}else if(i==="revealed"){j();k(r)}else if(i==="load"){V(n,r,e,t)}else if(i.trim().indexOf("every ")===0){n.polling=true;X(r,e,t)}else{P(r,e,t,n,i)}}});return a}function _(e){var t=y(e);if(!t.processed){t.processed=true;var r=R(e);var n=G(e,t,r);if(!n&&x(e,"hx-boost")==="true"){U(e,t,r)}var i=g(e,"hx-sse-source");if(i){J(e,i)}var a=g(e,"hx-add-class");if(a){F(e,a,"add")}var o=g(e,"hx-remove-class");if(o){F(e,o,"remove")}}S(e.children,function(e){_(e)})}function W(e,t,r){var n=x(e,"hx-error-url");if(n){var i=new XMLHttpRequest;i.open("POST",n);i.setRequestHeader("Content-Type","application/json;charset=UTF-8");i.send(JSON.stringify({elt:e.id,event:t,details:r}))}}function Y(e,t){var r;if(window.CustomEvent&&typeof window.CustomEvent==="function"){r=new CustomEvent(e,{detail:t})}else{r=m().createEvent("CustomEvent");r.initCustomEvent(e,true,true,t)}return r}function K(e,t,r){r["elt"]=e;var n=Y(t,r);if(HTMx.logger){HTMx.logger(e,t,r);if(t.indexOf("Error")>0){W(e,t,r)}}var i=e.dispatchEvent(n);var a=e.dispatchEvent(Y("all.hx",{elt:e,originalDetails:r,originalEvent:n}));return i&&a}function Q(e,t,r){var n,i,a;if(l(e)){n=m().body;i="all.hx";a=e}else if(l(t)){n=m().body;i=e;a=t}else{n=e;i=t;a=r}return n.addEventListener(i,a)}function Z(){var e=m().querySelector(".hx-history-element");return e||m().body}function $(e,r){var e=e.sort(function(e,t){return r[t]-r[e]});var t=0;S(e,function(e){t++;if(t>20){delete r[e];localStorage.removeItem(e)}})}function ee(e){var t=JSON.parse(localStorage.getItem("hx-history-timestamps"))||{};t[e]=Date.now;var r=Object.keys(t);if(r.length>20){$(r,t)}localStorage.setItem("hx-history-timestamps",JSON.stringify(t))}function te(){var e=Z();var t=location.pathname+location.search;K(m().body,"historyUpdate.hx",{path:t,historyElement:e});history.replaceState({},m().title,window.location.href);localStorage.setItem("hx-history-content-"+t,e.innerHTML);ee(t)}function re(e){history.pushState({},"",e)}function ne(e){S(e,function(e){e.call()})}function ie(t){K(m().body,"historyCacheMiss.hx",{path:t});var e=new XMLHttpRequest;e.open("GET",t,true);e.onload=function(){K(m().body,"historyCacheMissLoad.hx",{path:t});if(this.status>=200&&this.status<400){var e=s(this.response);e=e.querySelector(".hx-history-element")||e;ne(q(Z(),e))}}}function ae(){var e=location.pathname+location.search;K(m().body,"historyUpdate.hx",{path:e});var t=localStorage.getItem("hx-history-content-"+e);if(t){ee(e);ne(q(Z(),s(t)))}else{ie(e)}}function oe(e){return x(e,"hx-push-url")==="true"||e.tagName==="A"&&y(e).boosted}function ue(e){le(e,"add")}function se(e){le(e,"remove")}function le(e,t){var r=x(e,"hx-indicator");if(r){var n=m().querySelectorAll(r)}else{n=[e]}S(n,function(e){e.classList[t].call(e.classList,"hx-show-indicator")})}function fe(e,t){for(var r=0;r=200&&this.status<400){if(this.status!==204){var i=this.response;if(!K(a,"beforeSwap.hx",{xhr:f,target:u}))return;if(e){te()}u.classList.add("hx-swapping");var t=function(){try{var e=N(u,a,i);u.classList.remove("hx-swapping");u.classList.add("hx-settling");K(a,"afterSwap.hx",{xhr:f,target:u});var t=function(){S(e,function(e){e.call()});u.classList.remove("hx-settling");re(n||v);te();K(a,"afterSettle.hx",{xhr:f,target:u})};var r=g(a,"hx-settle-delay")||"100ms";if(r){setTimeout(t,d(r))}else{t()}}catch(e){K(a,"swapError.hx",{xhr:f,response:f.response,status:f.status,target:u});throw e}};var r=g(a,"hx-swap-delay")||"100ms";if(r){setTimeout(t,d(r))}else{t()}}}else{K(a,"responseError.hx",{xhr:f,response:f.response,status:f.status,target:u})}}catch(e){K(a,"onLoadError.hx",{xhr:f,response:f.response,status:f.status,target:u});throw e}finally{se(a);o();K(a,"afterOnLoad.hx",{xhr:f,response:f.response,status:f.status,target:u})}};f.onerror=function(){se(a);K(a,"loadError.hx",{xhr:f});o()};if(!K(a,"beforeRequest.hx",{xhr:f,values:c,target:u}))return o();ue(a);f.send(e==="get"?null:de(c))}function me(e){if(m().readyState!=="loading"){e()}else{m().addEventListener("DOMContentLoaded",e)}}me(function(){_(m().body);window.onpopstate=function(e){ae()}});function xe(e){return eval(e)}return{processElement:_,on:Q,version:"0.0.2",_:xe}}(); \ No newline at end of file diff --git a/dist/htmx.min.js.gz b/dist/htmx.min.js.gz index 85b2e4f9d2de19e7ba9ed58fa0893f6594b7c395..7595db853b5a7dd044a70f9c1c600ccc808adfad 100644 GIT binary patch literal 4344 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 X96e>p$MHQVIxy(pfX84;6f6J$#N-Xk diff --git a/src/htmx.js b/src/htmx.js index 39d9b791..676acb29 100644 --- a/src/htmx.js +++ b/src/htmx.js @@ -117,6 +117,10 @@ var HTMx = HTMx || (function () { return getDocument().body.contains(elt); } + function concat(arr1, arr2) { + return arr1.concat(arr2); + } + //==================================================================== // Node processing //==================================================================== @@ -152,19 +156,21 @@ var HTMx = HTMx || (function () { } function handleOutOfBandSwaps(fragment) { + var settleTasks = []; forEach(fragment.children, function(child){ if (getAttributeValue(child, "hx-swap-oob") === "true") { var target = getDocument().getElementById(child.id); if (target) { var fragment = new DocumentFragment() fragment.append(child); - swapOuterHTML(target, fragment); + settleTasks = settleTasks.concat(swapOuterHTML(target, fragment)); } else { child.parentNode.removeChild(child); triggerEvent(getDocument().body, "oobErrorNoTarget.hx", {id:child.id, content:child}) } } }) + return settleTasks; } function handleAttributes(parentNode, fragment) { @@ -179,15 +185,11 @@ var HTMx = HTMx || (function () { }); } }); - setTimeout(function () { - forEach(attributeSwaps, function (swap) { - swap.call(); - }); - }, 100); + return attributeSwaps; } function insertNodesBefore(parentNode, insertBefore, fragment) { - handleAttributes(parentNode, fragment); + var settleTasks = handleAttributes(parentNode, fragment); while(fragment.childNodes.length > 0){ var child = fragment.firstChild; parentNode.insertBefore(child, insertBefore); @@ -196,36 +198,45 @@ var HTMx = HTMx || (function () { processNode(child); } } + return settleTasks; } function swapOuterHTML(target, fragment) { if (target.tagName === "BODY") { - swapInnerHTML(target, fragment); + return swapInnerHTML(target, fragment); } else { - insertNodesBefore(parentElt(target), target, fragment); + var settleTasks = insertNodesBefore(parentElt(target), target, fragment); parentElt(target).removeChild(target); + return settleTasks; } } function swapPrepend(target, fragment) { - insertNodesBefore(target, target.firstChild, fragment); + return insertNodesBefore(target, target.firstChild, fragment); } function swapPrependBefore(target, fragment) { - insertNodesBefore(parentElt(target), target, fragment); + return insertNodesBefore(parentElt(target), target, fragment); } function swapAppend(target, fragment) { - insertNodesBefore(target, null, fragment); + return insertNodesBefore(target, null, fragment); } function swapAppendAfter(target, fragment) { - insertNodesBefore(parentElt(target), target.nextSibling, fragment); + return insertNodesBefore(parentElt(target), target.nextSibling, fragment); } function swapInnerHTML(target, fragment) { - target.innerHTML = ""; - insertNodesBefore(target, null, fragment); + var firstChild = target.firstChild; + var settleTasks = insertNodesBefore(target, firstChild, fragment); + if (firstChild) { + while (firstChild.nextSibling) { + target.removeChild(firstChild.nextSibling); + } + target.removeChild(firstChild); + } + return settleTasks; } function maybeSelectFromResponse(elt, fragment) { @@ -240,30 +251,20 @@ var HTMx = HTMx || (function () { return fragment; } - function swapResponse(target, elt, responseText, callBack) { - + function swapResponse(target, elt, responseText) { var fragment = makeFragment(responseText); - handleOutOfBandSwaps(fragment); + var settleTasks = handleOutOfBandSwaps(fragment); fragment = maybeSelectFromResponse(elt, fragment); var swapStyle = getClosestAttributeValue(elt, "hx-swap"); - if (swapStyle === "outerHTML") { - swapOuterHTML(target, fragment); - } else if (swapStyle === "prepend") { - swapPrepend(target, fragment); - } else if (swapStyle === "prependBefore") { - swapPrependBefore(target, fragment); - } else if (swapStyle === "append") { - swapAppend(target, fragment); - } else if (swapStyle === "appendAfter") { - swapAppendAfter(target, fragment); - } else { - swapInnerHTML(target, fragment); - } - - if(callBack) { - callBack.call(); + switch(swapStyle) { + case "outerHTML": return concat(settleTasks, swapOuterHTML(target, fragment)); + case "prepend": return concat(settleTasks, swapPrepend(target, fragment)); + case "prependBefore": return concat(settleTasks, swapPrependBefore(target, fragment)); + case "append": return concat(settleTasks, swapAppend(target, fragment)); + case "appendAfter": return concat(settleTasks, swapAppendAfter(target, fragment)); + default: return concat(settleTasks, swapInnerHTML(target, fragment)); } } @@ -604,7 +605,7 @@ var HTMx = HTMx || (function () { function bumpHistoryAccessDate(pathAndSearch) { var historyTimestamps = JSON.parse(localStorage.getItem("hx-history-timestamps")) || {}; - historyTimestamps[pathAndSearch] = Date.now; + historyTimestamps[pathAndSearch] = Date.now(); var paths = Object.keys(historyTimestamps); if (paths.length > 20) { purgeOldestPaths(paths, historyTimestamps); @@ -612,20 +613,23 @@ var HTMx = HTMx || (function () { localStorage.setItem("hx-history-timestamps", JSON.stringify(historyTimestamps)); } - function saveForHistory() { + function saveHistory() { var elt = getHistoryElement(); var pathAndSearch = location.pathname+location.search; - triggerEvent(getDocument().body, "historyUpdate.hx", {path:pathAndSearch}); + triggerEvent(getDocument().body, "historyUpdate.hx", {path:pathAndSearch, historyElement:elt}); history.replaceState({}, getDocument().title, window.location.href); - localStorage.setItem('hx-history-content-' + pathAndSearch, elt.innerHTML); + localStorage.setItem('hx-history:' + pathAndSearch, elt.innerHTML); bumpHistoryAccessDate(pathAndSearch); } - function initNewHistoryEntry(elt, url) { - if (shouldPush(elt)) { - history.pushState({}, "", url ); - saveForHistory(); - } + function pushUrlIntoHistory(url) { + history.pushState({}, "", url ); + } + + function settleImmediately(settleTasks) { + forEach(settleTasks, function (task) { + task.call(); + }); } function loadHistoryFromServer(pathAndSearch) { @@ -637,18 +641,18 @@ var HTMx = HTMx || (function () { if (this.status >= 200 && this.status < 400) { var fragment = makeFragment(this.response); fragment = fragment.querySelector('.hx-history-element') || fragment; - swapInnerHTML(getHistoryElement(), fragment); + settleImmediately(swapInnerHTML(getHistoryElement(), fragment)); } }; } function restoreHistory() { var pathAndSearch = location.pathname+location.search; - triggerEvent(getDocument().body, "historyUpdate.hx", {path:pathAndSearch}); - var content = localStorage.getItem('hx-history-content-' + pathAndSearch); + triggerEvent(getDocument().body, "historyRestore.hx", {path:pathAndSearch}); + var content = localStorage.getItem('hx-history:' + pathAndSearch); if (content) { bumpHistoryAccessDate(pathAndSearch); - swapInnerHTML(getHistoryElement(), makeFragment(content)); + settleImmediately(swapInnerHTML(getHistoryElement(), makeFragment(content))); } else { loadHistoryFromServer(pathAndSearch); } @@ -659,14 +663,6 @@ var HTMx = HTMx || (function () { (elt.tagName === "A" && getInternalData(elt).boosted); } - function snapshotForCurrentHistoryEntry(elt) { - if (shouldPush(elt)) { - // TODO event to allow de-initialization of HTML elements in target - saveForHistory(); - } - } - - function addRequestIndicatorClasses(elt) { mutateRequestIndicatorClasses(elt, "add"); } @@ -810,11 +806,14 @@ var HTMx = HTMx || (function () { if(!triggerEvent(elt, 'values.hx', {values: inputValues, target:target})) return endRequestLock(); // request type + var requestURL; if (verb === 'get') { var noValues = Object.keys(inputValues).length === 0; - xhr.open('GET', path + (noValues ? "" : "?" + urlEncode(inputValues)), true); + requestURL = path + (noValues ? "" : "?" + urlEncode(inputValues)); + xhr.open('GET', requestURL, true); } else { - xhr.open('POST', path, true); + requestURL = path; + xhr.open('POST', requestURL, true); setHeader(xhr,'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8', true); if (verb !== 'post') { setHeader(xhr, 'X-HTTP-Method-Override', verb.toUpperCase(), true); @@ -847,29 +846,57 @@ var HTMx = HTMx || (function () { xhr.onload = function () { try { if (!triggerEvent(elt, 'beforeOnLoad.hx', {xhr: xhr, target: target})) return; - snapshotForCurrentHistoryEntry(elt, path); - var trigger = this.getResponseHeader("X-HX-Trigger"); - handleTrigger(elt, trigger); - initNewHistoryEntry(elt, path); + + handleTrigger(elt, this.getResponseHeader("X-HX-Trigger")); + var pushedUrl = this.getResponseHeader("X-HX-Push") + + var shouldSaveHistory = shouldPush(elt) || pushedUrl; + if (this.status >= 200 && this.status < 400) { // don't process 'No Content' response if (this.status !== 204) { // Success! var resp = this.response; if (!triggerEvent(elt, 'beforeSwap.hx', {xhr: xhr, target: target})) return; + + // Save current page + if (shouldSaveHistory) { + saveHistory(); + } + target.classList.add("hx-swapping"); var doSwap = function () { try { - swapResponse(target, elt, resp, function () { - target.classList.remove("hx-swapping"); - setTimeout(saveForHistory, 200); - triggerEvent(elt, 'afterSwap.hx', {xhr: xhr, target: target}); - }); + var settleTasks = swapResponse(target, elt, resp); + target.classList.remove("hx-swapping"); + target.classList.add("hx-settling"); + triggerEvent(elt, 'afterSwap.hx', {xhr: xhr, target: target}); + + var doSettle = function(){ + forEach(settleTasks, function (settleTask) { + settleTask.call(); + }); + target.classList.remove("hx-settling"); + // push URL and save new page + if (shouldSaveHistory) { + pushUrlIntoHistory(pushedUrl || requestURL ); + saveHistory(); + } + triggerEvent(elt, 'afterSettle.hx', {xhr: xhr, target: target}); + } + + var settleDelayStr = getAttributeValue(elt, "hx-settle-delay") || "100ms"; + if (settleDelayStr) { + setTimeout(doSettle, parseInterval(settleDelayStr)) + } else { + doSettle(); + } } catch (e) { triggerEvent(elt, 'swapError.hx', {xhr: xhr, response: xhr.response, status: xhr.status, target: target}); throw e; } }; + var swapDelayStr = getAttributeValue(elt, "hx-swap-delay"); if (swapDelayStr) { setTimeout(doSwap, parseInterval(swapDelayStr)) diff --git a/test/scratch.html b/test/scratch.html index 81c03932..5cd61d7e 100644 --- a/test/scratch.html +++ b/test/scratch.html @@ -24,11 +24,13 @@