Formalize settle concept

This commit is contained in:
carson 2020-05-05 04:17:30 -07:00
parent eb9ea0cbca
commit ab1668d68b
6 changed files with 238 additions and 181 deletions

View File

@ -17,6 +17,7 @@
* polling cancellation API 205 code * polling cancellation API 205 code
* meta config tag * meta config tag
* simple logging API * simple logging API
* hx-toggle-class
* Testing * Testing
* polling * polling

247
dist/htmx.js vendored
View File

@ -117,6 +117,10 @@ var HTMx = HTMx || (function () {
return getDocument().body.contains(elt); return getDocument().body.contains(elt);
} }
function concat(arr1, arr2) {
return arr1.concat(arr2);
}
//==================================================================== //====================================================================
// Node processing // Node processing
//==================================================================== //====================================================================
@ -152,19 +156,21 @@ var HTMx = HTMx || (function () {
} }
function handleOutOfBandSwaps(fragment) { function handleOutOfBandSwaps(fragment) {
var settleTasks = [];
forEach(fragment.children, function(child){ forEach(fragment.children, function(child){
if (getAttributeValue(child, "hx-swap-oob") === "true") { if (getAttributeValue(child, "hx-swap-oob") === "true") {
var target = getDocument().getElementById(child.id); var target = getDocument().getElementById(child.id);
if (target) { if (target) {
var fragment = new DocumentFragment() var fragment = new DocumentFragment()
fragment.append(child); fragment.append(child);
swapOuterHTML(target, fragment); settleTasks = settleTasks.concat(swapOuterHTML(target, fragment));
} else { } else {
child.parentNode.removeChild(child); child.parentNode.removeChild(child);
triggerEvent(getDocument().body, "oobErrorNoTarget.hx", {id:child.id, content:child}) triggerEvent(getDocument().body, "oobErrorNoTarget.hx", {id:child.id, content:child})
} }
} }
}) })
return settleTasks;
} }
function handleAttributes(parentNode, fragment) { function handleAttributes(parentNode, fragment) {
@ -179,11 +185,7 @@ var HTMx = HTMx || (function () {
}); });
} }
}); });
setTimeout(function () { return attributeSwaps;
forEach(attributeSwaps, function (swap) {
swap.call();
});
}, 100);
} }
function insertNodesBefore(parentNode, insertBefore, fragment) { function insertNodesBefore(parentNode, insertBefore, fragment) {
@ -200,32 +202,37 @@ var HTMx = HTMx || (function () {
function swapOuterHTML(target, fragment) { function swapOuterHTML(target, fragment) {
if (target.tagName === "BODY") { if (target.tagName === "BODY") {
swapInnerHTML(target, fragment); return swapInnerHTML(target, fragment);
} else { } else {
insertNodesBefore(parentElt(target), target, fragment); var settleTasks = insertNodesBefore(parentElt(target), target, fragment);
parentElt(target).removeChild(target); parentElt(target).removeChild(target);
return settleTasks;
} }
} }
function swapPrepend(target, fragment) { function swapPrepend(target, fragment) {
insertNodesBefore(target, target.firstChild, fragment); return insertNodesBefore(target, target.firstChild, fragment);
} }
function swapPrependBefore(target, fragment) { function swapPrependBefore(target, fragment) {
insertNodesBefore(parentElt(target), target, fragment); return insertNodesBefore(parentElt(target), target, fragment);
} }
function swapAppend(target, fragment) { function swapAppend(target, fragment) {
insertNodesBefore(target, null, fragment); return insertNodesBefore(target, null, fragment);
} }
function swapAppendAfter(target, fragment) { function swapAppendAfter(target, fragment) {
insertNodesBefore(parentElt(target), target.nextSibling, fragment); return insertNodesBefore(parentElt(target), target.nextSibling, fragment);
} }
function swapInnerHTML(target, fragment) { function swapInnerHTML(target, fragment) {
target.innerHTML = ""; var firstChild = target.firstChild;
insertNodesBefore(target, null, fragment); return insertNodesBefore(target, firstChild, fragment);
while (firstChild.nextSibling) {
target.removeChild(firstChild.nextSibling);
}
target.removeChild(firstChild);
} }
function maybeSelectFromResponse(elt, fragment) { function maybeSelectFromResponse(elt, fragment) {
@ -240,30 +247,20 @@ var HTMx = HTMx || (function () {
return fragment; return fragment;
} }
function swapResponse(target, elt, responseText, callBack) { function swapResponse(target, elt, responseText) {
var fragment = makeFragment(responseText); var fragment = makeFragment(responseText);
handleOutOfBandSwaps(fragment); var settleTasks = handleOutOfBandSwaps(fragment);
fragment = maybeSelectFromResponse(elt, fragment); fragment = maybeSelectFromResponse(elt, fragment);
var swapStyle = getClosestAttributeValue(elt, "hx-swap"); var swapStyle = getClosestAttributeValue(elt, "hx-swap");
if (swapStyle === "outerHTML") { switch(swapStyle) {
swapOuterHTML(target, fragment); case "outerHTML": return concat(settleTasks, swapOuterHTML(target, fragment));
} else if (swapStyle === "prepend") { case "prepend": return concat(settleTasks, swapPrepend(target, fragment));
swapPrepend(target, fragment); case "prependBefore": return concat(settleTasks, swapPrependBefore(target, fragment));
} else if (swapStyle === "prependBefore") { case "append": return concat(settleTasks, swapAppend(target, fragment));
swapPrependBefore(target, fragment); case "appendAfter": return concat(settleTasks, swapAppendAfter(target, fragment));
} else if (swapStyle === "append") { default: return concat(settleTasks, swapInnerHTML(target, fragment));
swapAppend(target, fragment);
} else if (swapStyle === "appendAfter") {
swapAppendAfter(target, fragment);
} else {
swapInnerHTML(target, fragment);
}
if(callBack) {
callBack.call();
} }
} }
@ -583,62 +580,78 @@ var HTMx = HTMx || (function () {
//==================================================================== //====================================================================
// History Support // History Support
//==================================================================== //====================================================================
function makeHistoryId() {
return Math.random().toString(36).substr(3, 9);
}
function getHistoryElement() { function getHistoryElement() {
var historyElt = getDocument().getElementsByClassName('hx-history-element'); var historyElt = getDocument().querySelector('.hx-history-element');
if (historyElt.length > 0) { return historyElt || getDocument().body;
return historyElt[0]; }
} else {
return 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) { function saveHistory() {
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() {
var elt = getHistoryElement(); var elt = getHistoryElement();
var historyData = getLocalHistoryData(); var pathAndSearch = location.pathname+location.search;
history.replaceState({"hx-history-key": historyData.current}, getDocument().title, window.location.href); triggerEvent(getDocument().body, "historyUpdate.hx", {path:pathAndSearch, historyElement:elt});
localStorage.setItem('hx-history-' + historyData.current, elt.innerHTML); history.replaceState({}, getDocument().title, window.location.href);
localStorage.setItem('hx-history-content-' + pathAndSearch, elt.innerHTML);
bumpHistoryAccessDate(pathAndSearch);
} }
function restoreHistory(data) { function pushUrlIntoHistory(url) {
var historyKey = data['hx-history-key']; history.pushState({}, "", url );
var content = localStorage.getItem('hx-history-' + historyKey); }
var elt = getHistoryElement();
swapInnerHTML(elt, makeFragment(content)); 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) { function shouldPush(elt) {
@ -646,21 +659,6 @@ var HTMx = HTMx || (function () {
(elt.tagName === "A" && getInternalData(elt).boosted); (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) { function addRequestIndicatorClasses(elt) {
mutateRequestIndicatorClasses(elt, "add"); mutateRequestIndicatorClasses(elt, "add");
} }
@ -804,11 +802,14 @@ var HTMx = HTMx || (function () {
if(!triggerEvent(elt, 'values.hx', {values: inputValues, target:target})) return endRequestLock(); if(!triggerEvent(elt, 'values.hx', {values: inputValues, target:target})) return endRequestLock();
// request type // request type
var requestURL;
if (verb === 'get') { if (verb === 'get') {
var noValues = Object.keys(inputValues).length === 0; 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 { } 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); setHeader(xhr,'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8', true);
if (verb !== 'post') { if (verb !== 'post') {
setHeader(xhr, 'X-HTTP-Method-Override', verb.toUpperCase(), true); setHeader(xhr, 'X-HTTP-Method-Override', verb.toUpperCase(), true);
@ -841,30 +842,56 @@ var HTMx = HTMx || (function () {
xhr.onload = function () { xhr.onload = function () {
try { try {
if (!triggerEvent(elt, 'beforeOnLoad.hx', {xhr: xhr, target: target})) return; if (!triggerEvent(elt, 'beforeOnLoad.hx', {xhr: xhr, target: target})) return;
snapshotForCurrentHistoryEntry(elt, path);
var trigger = this.getResponseHeader("X-HX-Trigger"); handleTrigger(elt, this.getResponseHeader("X-HX-Trigger"));
handleTrigger(elt, trigger); var pushedUrl = this.getResponseHeader("X-HX-Push")
initNewHistoryEntry(elt, path);
var shouldSaveHistory = shouldPush(elt) || pushedUrl;
if (this.status >= 200 && this.status < 400) { if (this.status >= 200 && this.status < 400) {
// don't process 'No Content' response // don't process 'No Content' response
if (this.status !== 204) { if (this.status !== 204) {
// Success! // Success!
var resp = this.response; var resp = this.response;
if (!triggerEvent(elt, 'beforeSwap.hx', {xhr: xhr, target: target})) return; if (!triggerEvent(elt, 'beforeSwap.hx', {xhr: xhr, target: target})) return;
// Save current page
if (shouldSaveHistory) {
saveHistory();
}
target.classList.add("hx-swapping"); target.classList.add("hx-swapping");
var doSwap = function () { var doSwap = function () {
try { try {
swapResponse(target, elt, resp, function () { var settleTasks = swapResponse(target, elt, resp);
target.classList.remove("hx-swapping"); target.classList.remove("hx-swapping");
updateCurrentHistoryContent(); target.classList.add("hx-settling");
triggerEvent(elt, 'afterSwap.hx', {xhr: xhr, target: target}); 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) { } catch (e) {
triggerEvent(elt, 'swapError.hx', {xhr: xhr, response: xhr.response, status: xhr.status, target: target}); triggerEvent(elt, 'swapError.hx', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
throw e; throw e;
} }
}; };
var swapDelayStr = getAttributeValue(elt, "hx-swap-delay");
var swapDelayStr = getAttributeValue(elt, "hx-swap-delay") || "100ms";
if (swapDelayStr) { if (swapDelayStr) {
setTimeout(doSwap, parseInterval(swapDelayStr)) setTimeout(doSwap, parseInterval(swapDelayStr))
} else { } else {
@ -908,7 +935,7 @@ var HTMx = HTMx || (function () {
ready(function () { ready(function () {
processNode(getDocument().body); processNode(getDocument().body);
window.onpopstate = function (event) { window.onpopstate = function (event) {
restoreHistory(event.state); restoreHistory();
}; };
}) })

2
dist/htmx.min.js vendored

File diff suppressed because one or more lines are too long

BIN
dist/htmx.min.js.gz vendored

Binary file not shown.

View File

@ -117,6 +117,10 @@ var HTMx = HTMx || (function () {
return getDocument().body.contains(elt); return getDocument().body.contains(elt);
} }
function concat(arr1, arr2) {
return arr1.concat(arr2);
}
//==================================================================== //====================================================================
// Node processing // Node processing
//==================================================================== //====================================================================
@ -152,19 +156,21 @@ var HTMx = HTMx || (function () {
} }
function handleOutOfBandSwaps(fragment) { function handleOutOfBandSwaps(fragment) {
var settleTasks = [];
forEach(fragment.children, function(child){ forEach(fragment.children, function(child){
if (getAttributeValue(child, "hx-swap-oob") === "true") { if (getAttributeValue(child, "hx-swap-oob") === "true") {
var target = getDocument().getElementById(child.id); var target = getDocument().getElementById(child.id);
if (target) { if (target) {
var fragment = new DocumentFragment() var fragment = new DocumentFragment()
fragment.append(child); fragment.append(child);
swapOuterHTML(target, fragment); settleTasks = settleTasks.concat(swapOuterHTML(target, fragment));
} else { } else {
child.parentNode.removeChild(child); child.parentNode.removeChild(child);
triggerEvent(getDocument().body, "oobErrorNoTarget.hx", {id:child.id, content:child}) triggerEvent(getDocument().body, "oobErrorNoTarget.hx", {id:child.id, content:child})
} }
} }
}) })
return settleTasks;
} }
function handleAttributes(parentNode, fragment) { function handleAttributes(parentNode, fragment) {
@ -179,15 +185,11 @@ var HTMx = HTMx || (function () {
}); });
} }
}); });
setTimeout(function () { return attributeSwaps;
forEach(attributeSwaps, function (swap) {
swap.call();
});
}, 100);
} }
function insertNodesBefore(parentNode, insertBefore, fragment) { function insertNodesBefore(parentNode, insertBefore, fragment) {
handleAttributes(parentNode, fragment); var settleTasks = handleAttributes(parentNode, fragment);
while(fragment.childNodes.length > 0){ while(fragment.childNodes.length > 0){
var child = fragment.firstChild; var child = fragment.firstChild;
parentNode.insertBefore(child, insertBefore); parentNode.insertBefore(child, insertBefore);
@ -196,36 +198,45 @@ var HTMx = HTMx || (function () {
processNode(child); processNode(child);
} }
} }
return settleTasks;
} }
function swapOuterHTML(target, fragment) { function swapOuterHTML(target, fragment) {
if (target.tagName === "BODY") { if (target.tagName === "BODY") {
swapInnerHTML(target, fragment); return swapInnerHTML(target, fragment);
} else { } else {
insertNodesBefore(parentElt(target), target, fragment); var settleTasks = insertNodesBefore(parentElt(target), target, fragment);
parentElt(target).removeChild(target); parentElt(target).removeChild(target);
return settleTasks;
} }
} }
function swapPrepend(target, fragment) { function swapPrepend(target, fragment) {
insertNodesBefore(target, target.firstChild, fragment); return insertNodesBefore(target, target.firstChild, fragment);
} }
function swapPrependBefore(target, fragment) { function swapPrependBefore(target, fragment) {
insertNodesBefore(parentElt(target), target, fragment); return insertNodesBefore(parentElt(target), target, fragment);
} }
function swapAppend(target, fragment) { function swapAppend(target, fragment) {
insertNodesBefore(target, null, fragment); return insertNodesBefore(target, null, fragment);
} }
function swapAppendAfter(target, fragment) { function swapAppendAfter(target, fragment) {
insertNodesBefore(parentElt(target), target.nextSibling, fragment); return insertNodesBefore(parentElt(target), target.nextSibling, fragment);
} }
function swapInnerHTML(target, fragment) { function swapInnerHTML(target, fragment) {
target.innerHTML = ""; var firstChild = target.firstChild;
insertNodesBefore(target, null, fragment); var settleTasks = insertNodesBefore(target, firstChild, fragment);
if (firstChild) {
while (firstChild.nextSibling) {
target.removeChild(firstChild.nextSibling);
}
target.removeChild(firstChild);
}
return settleTasks;
} }
function maybeSelectFromResponse(elt, fragment) { function maybeSelectFromResponse(elt, fragment) {
@ -240,30 +251,20 @@ var HTMx = HTMx || (function () {
return fragment; return fragment;
} }
function swapResponse(target, elt, responseText, callBack) { function swapResponse(target, elt, responseText) {
var fragment = makeFragment(responseText); var fragment = makeFragment(responseText);
handleOutOfBandSwaps(fragment); var settleTasks = handleOutOfBandSwaps(fragment);
fragment = maybeSelectFromResponse(elt, fragment); fragment = maybeSelectFromResponse(elt, fragment);
var swapStyle = getClosestAttributeValue(elt, "hx-swap"); var swapStyle = getClosestAttributeValue(elt, "hx-swap");
if (swapStyle === "outerHTML") { switch(swapStyle) {
swapOuterHTML(target, fragment); case "outerHTML": return concat(settleTasks, swapOuterHTML(target, fragment));
} else if (swapStyle === "prepend") { case "prepend": return concat(settleTasks, swapPrepend(target, fragment));
swapPrepend(target, fragment); case "prependBefore": return concat(settleTasks, swapPrependBefore(target, fragment));
} else if (swapStyle === "prependBefore") { case "append": return concat(settleTasks, swapAppend(target, fragment));
swapPrependBefore(target, fragment); case "appendAfter": return concat(settleTasks, swapAppendAfter(target, fragment));
} else if (swapStyle === "append") { default: return concat(settleTasks, swapInnerHTML(target, fragment));
swapAppend(target, fragment);
} else if (swapStyle === "appendAfter") {
swapAppendAfter(target, fragment);
} else {
swapInnerHTML(target, fragment);
}
if(callBack) {
callBack.call();
} }
} }
@ -604,7 +605,7 @@ var HTMx = HTMx || (function () {
function bumpHistoryAccessDate(pathAndSearch) { function bumpHistoryAccessDate(pathAndSearch) {
var historyTimestamps = JSON.parse(localStorage.getItem("hx-history-timestamps")) || {}; var historyTimestamps = JSON.parse(localStorage.getItem("hx-history-timestamps")) || {};
historyTimestamps[pathAndSearch] = Date.now; historyTimestamps[pathAndSearch] = Date.now();
var paths = Object.keys(historyTimestamps); var paths = Object.keys(historyTimestamps);
if (paths.length > 20) { if (paths.length > 20) {
purgeOldestPaths(paths, historyTimestamps); purgeOldestPaths(paths, historyTimestamps);
@ -612,20 +613,23 @@ var HTMx = HTMx || (function () {
localStorage.setItem("hx-history-timestamps", JSON.stringify(historyTimestamps)); localStorage.setItem("hx-history-timestamps", JSON.stringify(historyTimestamps));
} }
function saveForHistory() { function saveHistory() {
var elt = getHistoryElement(); var elt = getHistoryElement();
var pathAndSearch = location.pathname+location.search; 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); history.replaceState({}, getDocument().title, window.location.href);
localStorage.setItem('hx-history-content-' + pathAndSearch, elt.innerHTML); localStorage.setItem('hx-history:' + pathAndSearch, elt.innerHTML);
bumpHistoryAccessDate(pathAndSearch); bumpHistoryAccessDate(pathAndSearch);
} }
function initNewHistoryEntry(elt, url) { function pushUrlIntoHistory(url) {
if (shouldPush(elt)) { history.pushState({}, "", url );
history.pushState({}, "", url ); }
saveForHistory();
} function settleImmediately(settleTasks) {
forEach(settleTasks, function (task) {
task.call();
});
} }
function loadHistoryFromServer(pathAndSearch) { function loadHistoryFromServer(pathAndSearch) {
@ -637,18 +641,18 @@ var HTMx = HTMx || (function () {
if (this.status >= 200 && this.status < 400) { if (this.status >= 200 && this.status < 400) {
var fragment = makeFragment(this.response); var fragment = makeFragment(this.response);
fragment = fragment.querySelector('.hx-history-element') || fragment; fragment = fragment.querySelector('.hx-history-element') || fragment;
swapInnerHTML(getHistoryElement(), fragment); settleImmediately(swapInnerHTML(getHistoryElement(), fragment));
} }
}; };
} }
function restoreHistory() { function restoreHistory() {
var pathAndSearch = location.pathname+location.search; var pathAndSearch = location.pathname+location.search;
triggerEvent(getDocument().body, "historyUpdate.hx", {path:pathAndSearch}); triggerEvent(getDocument().body, "historyRestore.hx", {path:pathAndSearch});
var content = localStorage.getItem('hx-history-content-' + pathAndSearch); var content = localStorage.getItem('hx-history:' + pathAndSearch);
if (content) { if (content) {
bumpHistoryAccessDate(pathAndSearch); bumpHistoryAccessDate(pathAndSearch);
swapInnerHTML(getHistoryElement(), makeFragment(content)); settleImmediately(swapInnerHTML(getHistoryElement(), makeFragment(content)));
} else { } else {
loadHistoryFromServer(pathAndSearch); loadHistoryFromServer(pathAndSearch);
} }
@ -659,14 +663,6 @@ var HTMx = HTMx || (function () {
(elt.tagName === "A" && getInternalData(elt).boosted); (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) { function addRequestIndicatorClasses(elt) {
mutateRequestIndicatorClasses(elt, "add"); mutateRequestIndicatorClasses(elt, "add");
} }
@ -810,11 +806,14 @@ var HTMx = HTMx || (function () {
if(!triggerEvent(elt, 'values.hx', {values: inputValues, target:target})) return endRequestLock(); if(!triggerEvent(elt, 'values.hx', {values: inputValues, target:target})) return endRequestLock();
// request type // request type
var requestURL;
if (verb === 'get') { if (verb === 'get') {
var noValues = Object.keys(inputValues).length === 0; 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 { } 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); setHeader(xhr,'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8', true);
if (verb !== 'post') { if (verb !== 'post') {
setHeader(xhr, 'X-HTTP-Method-Override', verb.toUpperCase(), true); setHeader(xhr, 'X-HTTP-Method-Override', verb.toUpperCase(), true);
@ -847,29 +846,57 @@ var HTMx = HTMx || (function () {
xhr.onload = function () { xhr.onload = function () {
try { try {
if (!triggerEvent(elt, 'beforeOnLoad.hx', {xhr: xhr, target: target})) return; if (!triggerEvent(elt, 'beforeOnLoad.hx', {xhr: xhr, target: target})) return;
snapshotForCurrentHistoryEntry(elt, path);
var trigger = this.getResponseHeader("X-HX-Trigger"); handleTrigger(elt, this.getResponseHeader("X-HX-Trigger"));
handleTrigger(elt, trigger); var pushedUrl = this.getResponseHeader("X-HX-Push")
initNewHistoryEntry(elt, path);
var shouldSaveHistory = shouldPush(elt) || pushedUrl;
if (this.status >= 200 && this.status < 400) { if (this.status >= 200 && this.status < 400) {
// don't process 'No Content' response // don't process 'No Content' response
if (this.status !== 204) { if (this.status !== 204) {
// Success! // Success!
var resp = this.response; var resp = this.response;
if (!triggerEvent(elt, 'beforeSwap.hx', {xhr: xhr, target: target})) return; if (!triggerEvent(elt, 'beforeSwap.hx', {xhr: xhr, target: target})) return;
// Save current page
if (shouldSaveHistory) {
saveHistory();
}
target.classList.add("hx-swapping"); target.classList.add("hx-swapping");
var doSwap = function () { var doSwap = function () {
try { try {
swapResponse(target, elt, resp, function () { var settleTasks = swapResponse(target, elt, resp);
target.classList.remove("hx-swapping"); target.classList.remove("hx-swapping");
setTimeout(saveForHistory, 200); target.classList.add("hx-settling");
triggerEvent(elt, 'afterSwap.hx', {xhr: xhr, target: target}); 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) { } catch (e) {
triggerEvent(elt, 'swapError.hx', {xhr: xhr, response: xhr.response, status: xhr.status, target: target}); triggerEvent(elt, 'swapError.hx', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
throw e; throw e;
} }
}; };
var swapDelayStr = getAttributeValue(elt, "hx-swap-delay"); var swapDelayStr = getAttributeValue(elt, "hx-swap-delay");
if (swapDelayStr) { if (swapDelayStr) {
setTimeout(doSwap, parseInterval(swapDelayStr)) setTimeout(doSwap, parseInterval(swapDelayStr))

View File

@ -24,11 +24,13 @@
<script src="scratch_server.js"></script> <script src="scratch_server.js"></script>
<script> <script>
// this.server.respondWith("GET", "/test", "Clicked!"); this.server.respondWith("GET", "/test", '<a hx-get="/test2">Click Me</a>');
// var btn = make('<button hx-get="/test">Click Me!</button>') this.server.respondWith("GET", "/test2", "Clicked!");
this.server.respondWith("GET", "/test", '<div id="d1" style="color: red; margin: 100px">Foo</div>'); make('<div hx-get="/test">dd</div>')
make('<div hx-swap="outerHTML" hx-get="/test" hx-push-url="true" id="d1">Foo</div>');
// this.server.respondWith("GET", "/test", '<div id="d1" style="color: red; margin: 100px">Foo</div>');
// make('<div hx-swap="outerHTML" hx-get="/test" hx-push-url="true" id="d1">Foo</div>');
</script> </script>