mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-10-02 15:25:26 +00:00
release 0.1 prep
This commit is contained in:
parent
325cb29bf8
commit
1dbabdb4c4
184
dist/htmx.js
vendored
184
dist/htmx.js
vendored
@ -41,6 +41,7 @@ return (function () {
|
|||||||
requestClass:'htmx-request',
|
requestClass:'htmx-request',
|
||||||
settlingClass:'htmx-settling',
|
settlingClass:'htmx-settling',
|
||||||
swappingClass:'htmx-swapping',
|
swappingClass:'htmx-swapping',
|
||||||
|
attributesToSwizzle:["class", "style", "width", "height"]
|
||||||
},
|
},
|
||||||
parseInterval:parseInterval,
|
parseInterval:parseInterval,
|
||||||
_:internalEval,
|
_:internalEval,
|
||||||
@ -57,6 +58,8 @@ return (function () {
|
|||||||
return "[hx-" + verb + "], [data-hx-" + verb + "]"
|
return "[hx-" + verb + "], [data-hx-" + verb + "]"
|
||||||
}).join(", ");
|
}).join(", ");
|
||||||
|
|
||||||
|
var windowIsScrolling = false // used by initScrollHandler
|
||||||
|
|
||||||
//====================================================================
|
//====================================================================
|
||||||
// Utilities
|
// Utilities
|
||||||
//====================================================================
|
//====================================================================
|
||||||
@ -216,7 +219,7 @@ return (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function splitOnWhitespace(trigger) {
|
function splitOnWhitespace(trigger) {
|
||||||
return trigger.split(/\s+/);
|
return trigger.trim().split(/\s+/);
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergeObjects(obj1, obj2) {
|
function mergeObjects(obj1, obj2) {
|
||||||
@ -362,6 +365,8 @@ return (function () {
|
|||||||
return explicitTarget;
|
return explicitTarget;
|
||||||
} else if (targetStr.indexOf("closest ") === 0) {
|
} else if (targetStr.indexOf("closest ") === 0) {
|
||||||
return closest(elt, targetStr.substr(8));
|
return closest(elt, targetStr.substr(8));
|
||||||
|
} else if (targetStr.indexOf("find ") === 0) {
|
||||||
|
return find(elt, targetStr.substr(5));
|
||||||
} else {
|
} else {
|
||||||
return getDocument().querySelector(targetStr);
|
return getDocument().querySelector(targetStr);
|
||||||
}
|
}
|
||||||
@ -375,15 +380,24 @@ return (function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var EXCLUDED_ATTRIBUTES = ['id', 'value'];
|
function shouldSettleAttribute(name) {
|
||||||
|
var attributesToSwizzle = htmx.config.attributesToSwizzle;
|
||||||
|
for (var i = 0; i < attributesToSwizzle.length; i++) {
|
||||||
|
if (name === attributesToSwizzle[i]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function cloneAttributes(mergeTo, mergeFrom) {
|
function cloneAttributes(mergeTo, mergeFrom) {
|
||||||
forEach(mergeTo.attributes, function (attr) {
|
forEach(mergeTo.attributes, function (attr) {
|
||||||
if (!mergeFrom.hasAttribute(attr.name) && EXCLUDED_ATTRIBUTES.indexOf(attr.name) === -1) {
|
if (!mergeFrom.hasAttribute(attr.name) && shouldSettleAttribute(attr.name)) {
|
||||||
mergeTo.removeAttribute(attr.name)
|
mergeTo.removeAttribute(attr.name)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
forEach(mergeFrom.attributes, function (attr) {
|
forEach(mergeFrom.attributes, function (attr) {
|
||||||
if (EXCLUDED_ATTRIBUTES.indexOf(attr.name) === -1) {
|
if (shouldSettleAttribute(attr.name)) {
|
||||||
mergeTo.setAttribute(attr.name, attr.value);
|
mergeTo.setAttribute(attr.name, attr.value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -450,12 +464,21 @@ return (function () {
|
|||||||
|
|
||||||
function makeAjaxLoadTask(child) {
|
function makeAjaxLoadTask(child) {
|
||||||
return function () {
|
return function () {
|
||||||
processNode(child, true);
|
processNode(child);
|
||||||
processScripts(child);
|
processScripts(child);
|
||||||
triggerEvent(child, 'htmx:load', {});
|
processFocus(child)
|
||||||
|
triggerEvent(child, 'htmx:load');
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function processFocus(child) {
|
||||||
|
var autofocus = "[autofocus]";
|
||||||
|
var autoFocusedElt = matches(child, autofocus) ? child : child.querySelector(autofocus)
|
||||||
|
if (autoFocusedElt != null) {
|
||||||
|
autoFocusedElt.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function insertNodesBefore(parentNode, insertBefore, fragment, settleInfo) {
|
function insertNodesBefore(parentNode, insertBefore, fragment, settleInfo) {
|
||||||
handleAttributes(parentNode, fragment, settleInfo);
|
handleAttributes(parentNode, fragment, settleInfo);
|
||||||
while(fragment.childNodes.length > 0){
|
while(fragment.childNodes.length > 0){
|
||||||
@ -491,6 +514,7 @@ return (function () {
|
|||||||
} else {
|
} else {
|
||||||
var newElt = eltBeforeNewContent.nextSibling;
|
var newElt = eltBeforeNewContent.nextSibling;
|
||||||
}
|
}
|
||||||
|
getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later
|
||||||
while(newElt && newElt !== target) {
|
while(newElt && newElt !== target) {
|
||||||
settleInfo.elts.push(newElt);
|
settleInfo.elts.push(newElt);
|
||||||
newElt = newElt.nextSibling;
|
newElt = newElt.nextSibling;
|
||||||
@ -521,8 +545,10 @@ return (function () {
|
|||||||
insertNodesBefore(target, firstChild, fragment, settleInfo);
|
insertNodesBefore(target, firstChild, fragment, settleInfo);
|
||||||
if (firstChild) {
|
if (firstChild) {
|
||||||
while (firstChild.nextSibling) {
|
while (firstChild.nextSibling) {
|
||||||
|
closeConnections(firstChild.nextSibling)
|
||||||
target.removeChild(firstChild.nextSibling);
|
target.removeChild(firstChild.nextSibling);
|
||||||
}
|
}
|
||||||
|
closeConnections(firstChild)
|
||||||
target.removeChild(firstChild);
|
target.removeChild(firstChild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -760,12 +786,18 @@ return (function () {
|
|||||||
function initScrollHandler() {
|
function initScrollHandler() {
|
||||||
if (!window['htmxScrollHandler']) {
|
if (!window['htmxScrollHandler']) {
|
||||||
var scrollHandler = function() {
|
var scrollHandler = function() {
|
||||||
forEach(getDocument().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"), function (elt) {
|
windowIsScrolling = true
|
||||||
maybeReveal(elt);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
window['htmxScrollHandler'] = scrollHandler;
|
window['htmxScrollHandler'] = scrollHandler;
|
||||||
window.addEventListener("scroll", scrollHandler)
|
window.addEventListener("scroll", scrollHandler)
|
||||||
|
setInterval(function() {
|
||||||
|
if (windowIsScrolling) {
|
||||||
|
windowIsScrolling = false;
|
||||||
|
forEach(getDocument().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"), function (elt) {
|
||||||
|
maybeReveal(elt);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -778,9 +810,9 @@ return (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function processWebSocketInfo(elt, nodeData, info) {
|
function processWebSocketInfo(elt, nodeData, info) {
|
||||||
var values = info.split(",");
|
var values = splitOnWhitespace(info);
|
||||||
for (var i = 0; i < values.length; i++) {
|
for (var i = 0; i < values.length; i++) {
|
||||||
var value = splitOnWhitespace(values[i]);
|
var value = values[i].split(/:(.+)/);
|
||||||
if (value[0] === "connect") {
|
if (value[0] === "connect") {
|
||||||
processWebSocketSource(elt, value[1]);
|
processWebSocketSource(elt, value[1]);
|
||||||
}
|
}
|
||||||
@ -791,6 +823,9 @@ return (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function processWebSocketSource(elt, wssSource) {
|
function processWebSocketSource(elt, wssSource) {
|
||||||
|
if (wssSource.indexOf("ws:") !== 0 && wssSource.indexOf("wss:") !== 0) {
|
||||||
|
wssSource = "wss:" + wssSource;
|
||||||
|
}
|
||||||
var socket = htmx.createWebSocket(wssSource);
|
var socket = htmx.createWebSocket(wssSource);
|
||||||
socket.onerror = function (e) {
|
socket.onerror = function (e) {
|
||||||
triggerErrorEvent(elt, "htmx:wsError", {error:e, socket:socket});
|
triggerErrorEvent(elt, "htmx:wsError", {error:e, socket:socket});
|
||||||
@ -847,20 +882,21 @@ return (function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function maybeCloseSSESource(elt) {
|
//====================================================================
|
||||||
if (!bodyContains(elt)) {
|
// Server Sent Events
|
||||||
getInternalData(elt).sseEventSource.close();
|
//====================================================================
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function processSSEInfo(elt, nodeData, info) {
|
function processSSEInfo(elt, nodeData, info) {
|
||||||
var values = info.split(",");
|
var values = splitOnWhitespace(info);
|
||||||
for (var i = 0; i < values.length; i++) {
|
for (var i = 0; i < values.length; i++) {
|
||||||
var value = splitOnWhitespace(values[i]);
|
var value = values[i].split(/:(.+)/);
|
||||||
if (value[0] === "connect") {
|
if (value[0] === "connect") {
|
||||||
processSSESource(elt, value[1]);
|
processSSESource(elt, value[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((value[0] === "swap")) {
|
||||||
|
processSSESwap(elt, value[1])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -873,10 +909,41 @@ return (function () {
|
|||||||
getInternalData(elt).sseEventSource = source;
|
getInternalData(elt).sseEventSource = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function processSSESwap(elt, sseEventName) {
|
||||||
|
var sseSourceElt = getClosestMatch(elt, hasEventSource);
|
||||||
|
if (sseSourceElt) {
|
||||||
|
var sseEventSource = getInternalData(sseSourceElt).sseEventSource;
|
||||||
|
var sseListener = function (event) {
|
||||||
|
if (maybeCloseSSESource(sseSourceElt)) {
|
||||||
|
sseEventSource.removeEventListener(sseEventName, sseListener);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////
|
||||||
|
// TODO: merge this code with AJAX and WebSockets code in the future.
|
||||||
|
|
||||||
|
var response = event.data;
|
||||||
|
withExtensions(elt, function(extension){
|
||||||
|
response = extension.transformResponse(response, null, elt);
|
||||||
|
});
|
||||||
|
|
||||||
|
var swapSpec = getSwapSpecification(elt)
|
||||||
|
var target = getTarget(elt)
|
||||||
|
var settleInfo = makeSettleInfo(elt);
|
||||||
|
|
||||||
|
selectAndSwap(swapSpec.swapStyle, elt, target, response, settleInfo)
|
||||||
|
triggerEvent(elt, "htmx:sseMessage", event)
|
||||||
|
};
|
||||||
|
|
||||||
|
getInternalData(elt).sseListener = sseListener;
|
||||||
|
sseEventSource.addEventListener(sseEventName, sseListener);
|
||||||
|
} else {
|
||||||
|
triggerErrorEvent(elt, "htmx:noSSESourceError");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function processSSETrigger(elt, verb, path, sseEventName) {
|
function processSSETrigger(elt, verb, path, sseEventName) {
|
||||||
var sseSourceElt = getClosestMatch(elt, function (parent) {
|
var sseSourceElt = getClosestMatch(elt, hasEventSource);
|
||||||
return getInternalData(parent).sseEventSource != null;
|
|
||||||
});
|
|
||||||
if (sseSourceElt) {
|
if (sseSourceElt) {
|
||||||
var sseEventSource = getInternalData(sseSourceElt).sseEventSource;
|
var sseEventSource = getInternalData(sseSourceElt).sseEventSource;
|
||||||
var sseListener = function () {
|
var sseListener = function () {
|
||||||
@ -895,6 +962,19 @@ return (function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function maybeCloseSSESource(elt) {
|
||||||
|
if (!bodyContains(elt)) {
|
||||||
|
getInternalData(elt).sseEventSource.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasEventSource(node) {
|
||||||
|
return getInternalData(node).sseEventSource != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//====================================================================
|
||||||
|
|
||||||
function loadImmediately(elt, verb, path, nodeData, delay) {
|
function loadImmediately(elt, verb, path, nodeData, delay) {
|
||||||
var load = function(){
|
var load = function(){
|
||||||
if (!nodeData.loaded) {
|
if (!nodeData.loaded) {
|
||||||
@ -956,13 +1036,10 @@ return (function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function isHyperScriptAvailable() {
|
|
||||||
return typeof _hyperscript !== "undefined";
|
|
||||||
}
|
|
||||||
|
|
||||||
function findElementsToProcess(elt) {
|
function findElementsToProcess(elt) {
|
||||||
if (elt.querySelectorAll) {
|
if (elt.querySelectorAll) {
|
||||||
var results = elt.querySelectorAll(VERB_SELECTOR + ", a, form, [hx-sse], [data-hx-sse], [hx-ws], [data-hx-ws]");
|
var results = elt.querySelectorAll(VERB_SELECTOR + ", a, form, [hx-sse], [data-hx-sse], [hx-ws]," +
|
||||||
|
" [data-hx-ws]");
|
||||||
return results;
|
return results;
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
@ -974,10 +1051,6 @@ return (function () {
|
|||||||
if (!nodeData.initialized) {
|
if (!nodeData.initialized) {
|
||||||
nodeData.initialized = true;
|
nodeData.initialized = true;
|
||||||
|
|
||||||
if (isHyperScriptAvailable()) {
|
|
||||||
_hyperscript.init(elt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elt.value) {
|
if (elt.value) {
|
||||||
nodeData.lastValue = elt.value;
|
nodeData.lastValue = elt.value;
|
||||||
}
|
}
|
||||||
@ -1011,6 +1084,10 @@ return (function () {
|
|||||||
// Event/Log Support
|
// Event/Log Support
|
||||||
//====================================================================
|
//====================================================================
|
||||||
|
|
||||||
|
function kebabEventName(str) {
|
||||||
|
return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
function makeEvent(eventName, detail) {
|
function makeEvent(eventName, detail) {
|
||||||
var evt;
|
var evt;
|
||||||
if (window.CustomEvent && typeof window.CustomEvent === 'function') {
|
if (window.CustomEvent && typeof window.CustomEvent === 'function') {
|
||||||
@ -1062,6 +1139,10 @@ return (function () {
|
|||||||
triggerEvent(elt, "htmx:error", {errorInfo:detail})
|
triggerEvent(elt, "htmx:error", {errorInfo:detail})
|
||||||
}
|
}
|
||||||
var eventResult = elt.dispatchEvent(event);
|
var eventResult = elt.dispatchEvent(event);
|
||||||
|
if (eventResult) {
|
||||||
|
var kebabedEvent = makeEvent(kebabEventName(eventName), event.detail);
|
||||||
|
eventResult = eventResult && elt.dispatchEvent(kebabedEvent)
|
||||||
|
}
|
||||||
withExtensions(elt, function (extension) {
|
withExtensions(elt, function (extension) {
|
||||||
eventResult = eventResult && (extension.onEvent(eventName, event) !== false)
|
eventResult = eventResult && (extension.onEvent(eventName, event) !== false)
|
||||||
});
|
});
|
||||||
@ -1231,6 +1312,9 @@ return (function () {
|
|||||||
if (shouldInclude(elt)) {
|
if (shouldInclude(elt)) {
|
||||||
var name = getRawAttribute(elt,"name");
|
var name = getRawAttribute(elt,"name");
|
||||||
var value = elt.value;
|
var value = elt.value;
|
||||||
|
if (!!getRawAttribute(elt, 'multiple')) {
|
||||||
|
value = toArray(elt.querySelectorAll("option:checked")).map(function (e) { return e.value });
|
||||||
|
}
|
||||||
if (name != null && value != null) {
|
if (name != null && value != null) {
|
||||||
var current = values[name];
|
var current = values[name];
|
||||||
if(current) {
|
if(current) {
|
||||||
@ -1442,6 +1526,30 @@ return (function () {
|
|||||||
addExpressionVars(parentElt(elt), rawParameters);
|
addExpressionVars(parentElt(elt), rawParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function safelySetHeaderValue(xhr, header, headerValue) {
|
||||||
|
if (headerValue !== null) {
|
||||||
|
try {
|
||||||
|
xhr.setRequestHeader(header, headerValue);
|
||||||
|
} catch (e) {
|
||||||
|
// On an exception, try to set the header URI encoded instead
|
||||||
|
xhr.setRequestHeader(header, encodeURIComponent(headerValue));
|
||||||
|
xhr.setRequestHeader(header + "-URI-AutoEncoded", "true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getResponseURL(xhr) {
|
||||||
|
// NB: IE11 does not support this stuff
|
||||||
|
if (xhr.responseURL && typeof(URL) !== "undefined") {
|
||||||
|
try {
|
||||||
|
var url = new URL(xhr.responseURL);
|
||||||
|
return url.pathname + url.search;
|
||||||
|
} catch (e) {
|
||||||
|
triggerErrorEvent(getDocument().body, "htmx:badResponseUrl", {url: xhr.responseURL});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function issueAjaxRequest(elt, verb, path, eventTarget) {
|
function issueAjaxRequest(elt, verb, path, eventTarget) {
|
||||||
var target = getTarget(elt);
|
var target = getTarget(elt);
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
@ -1535,7 +1643,8 @@ return (function () {
|
|||||||
// request headers
|
// request headers
|
||||||
for (var header in headers) {
|
for (var header in headers) {
|
||||||
if (headers.hasOwnProperty(header)) {
|
if (headers.hasOwnProperty(header)) {
|
||||||
if (headers[header] !== null) xhr.setRequestHeader(header, headers[header]);
|
var headerValue = headers[header];
|
||||||
|
safelySetHeaderValue(xhr, header, headerValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1553,7 +1662,7 @@ return (function () {
|
|||||||
if (this.status === 286) {
|
if (this.status === 286) {
|
||||||
cancelPolling(elt);
|
cancelPolling(elt);
|
||||||
}
|
}
|
||||||
// don't process 'No Content' response
|
// don't process 'No Content'
|
||||||
if (this.status !== 204) {
|
if (this.status !== 204) {
|
||||||
if (!triggerEvent(target, 'htmx:beforeSwap', eventDetail)) return;
|
if (!triggerEvent(target, 'htmx:beforeSwap', eventDetail)) return;
|
||||||
|
|
||||||
@ -1613,7 +1722,7 @@ return (function () {
|
|||||||
});
|
});
|
||||||
// push URL and save new page
|
// push URL and save new page
|
||||||
if (shouldSaveHistory) {
|
if (shouldSaveHistory) {
|
||||||
var pathToPush = pushedUrl || getPushUrl(elt) || finalPathForGet || path;
|
var pathToPush = pushedUrl || getPushUrl(elt) || getResponseURL(xhr) || finalPathForGet || path;
|
||||||
pushUrlIntoHistory(pathToPush);
|
pushUrlIntoHistory(pathToPush);
|
||||||
triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path:pathToPush});
|
triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path:pathToPush});
|
||||||
}
|
}
|
||||||
@ -1645,8 +1754,9 @@ return (function () {
|
|||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
removeRequestIndicatorClasses(elt);
|
removeRequestIndicatorClasses(elt);
|
||||||
triggerEvent(elt, 'htmx:afterRequest', eventDetail);
|
var finalElt = getInternalData(elt).replacedWith || elt;
|
||||||
triggerEvent(elt, 'htmx:afterOnLoad', eventDetail);
|
triggerEvent(finalElt, 'htmx:afterRequest', eventDetail);
|
||||||
|
triggerEvent(finalElt, 'htmx:afterOnLoad', eventDetail);
|
||||||
endRequestLock();
|
endRequestLock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1747,7 +1857,7 @@ return (function () {
|
|||||||
mergeMetaConfig();
|
mergeMetaConfig();
|
||||||
insertIndicatorStyles();
|
insertIndicatorStyles();
|
||||||
var body = getDocument().body;
|
var body = getDocument().body;
|
||||||
processNode(body, true);
|
processNode(body);
|
||||||
triggerEvent(body, 'htmx:load', {});
|
triggerEvent(body, 'htmx:load', {});
|
||||||
window.onpopstate = function (event) {
|
window.onpopstate = function (event) {
|
||||||
if (event.state && event.state.htmx) {
|
if (event.state && event.state.htmx) {
|
||||||
|
2
dist/htmx.min.js
vendored
2
dist/htmx.min.js
vendored
File diff suppressed because one or more lines are too long
BIN
dist/htmx.min.js.gz
vendored
BIN
dist/htmx.min.js.gz
vendored
Binary file not shown.
184
www/js/htmx.js
184
www/js/htmx.js
@ -41,6 +41,7 @@ return (function () {
|
|||||||
requestClass:'htmx-request',
|
requestClass:'htmx-request',
|
||||||
settlingClass:'htmx-settling',
|
settlingClass:'htmx-settling',
|
||||||
swappingClass:'htmx-swapping',
|
swappingClass:'htmx-swapping',
|
||||||
|
attributesToSwizzle:["class", "style", "width", "height"]
|
||||||
},
|
},
|
||||||
parseInterval:parseInterval,
|
parseInterval:parseInterval,
|
||||||
_:internalEval,
|
_:internalEval,
|
||||||
@ -57,6 +58,8 @@ return (function () {
|
|||||||
return "[hx-" + verb + "], [data-hx-" + verb + "]"
|
return "[hx-" + verb + "], [data-hx-" + verb + "]"
|
||||||
}).join(", ");
|
}).join(", ");
|
||||||
|
|
||||||
|
var windowIsScrolling = false // used by initScrollHandler
|
||||||
|
|
||||||
//====================================================================
|
//====================================================================
|
||||||
// Utilities
|
// Utilities
|
||||||
//====================================================================
|
//====================================================================
|
||||||
@ -216,7 +219,7 @@ return (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function splitOnWhitespace(trigger) {
|
function splitOnWhitespace(trigger) {
|
||||||
return trigger.split(/\s+/);
|
return trigger.trim().split(/\s+/);
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergeObjects(obj1, obj2) {
|
function mergeObjects(obj1, obj2) {
|
||||||
@ -362,6 +365,8 @@ return (function () {
|
|||||||
return explicitTarget;
|
return explicitTarget;
|
||||||
} else if (targetStr.indexOf("closest ") === 0) {
|
} else if (targetStr.indexOf("closest ") === 0) {
|
||||||
return closest(elt, targetStr.substr(8));
|
return closest(elt, targetStr.substr(8));
|
||||||
|
} else if (targetStr.indexOf("find ") === 0) {
|
||||||
|
return find(elt, targetStr.substr(5));
|
||||||
} else {
|
} else {
|
||||||
return getDocument().querySelector(targetStr);
|
return getDocument().querySelector(targetStr);
|
||||||
}
|
}
|
||||||
@ -375,15 +380,24 @@ return (function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var EXCLUDED_ATTRIBUTES = ['id', 'value'];
|
function shouldSettleAttribute(name) {
|
||||||
|
var attributesToSwizzle = htmx.config.attributesToSwizzle;
|
||||||
|
for (var i = 0; i < attributesToSwizzle.length; i++) {
|
||||||
|
if (name === attributesToSwizzle[i]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function cloneAttributes(mergeTo, mergeFrom) {
|
function cloneAttributes(mergeTo, mergeFrom) {
|
||||||
forEach(mergeTo.attributes, function (attr) {
|
forEach(mergeTo.attributes, function (attr) {
|
||||||
if (!mergeFrom.hasAttribute(attr.name) && EXCLUDED_ATTRIBUTES.indexOf(attr.name) === -1) {
|
if (!mergeFrom.hasAttribute(attr.name) && shouldSettleAttribute(attr.name)) {
|
||||||
mergeTo.removeAttribute(attr.name)
|
mergeTo.removeAttribute(attr.name)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
forEach(mergeFrom.attributes, function (attr) {
|
forEach(mergeFrom.attributes, function (attr) {
|
||||||
if (EXCLUDED_ATTRIBUTES.indexOf(attr.name) === -1) {
|
if (shouldSettleAttribute(attr.name)) {
|
||||||
mergeTo.setAttribute(attr.name, attr.value);
|
mergeTo.setAttribute(attr.name, attr.value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -450,12 +464,21 @@ return (function () {
|
|||||||
|
|
||||||
function makeAjaxLoadTask(child) {
|
function makeAjaxLoadTask(child) {
|
||||||
return function () {
|
return function () {
|
||||||
processNode(child, true);
|
processNode(child);
|
||||||
processScripts(child);
|
processScripts(child);
|
||||||
triggerEvent(child, 'htmx:load', {});
|
processFocus(child)
|
||||||
|
triggerEvent(child, 'htmx:load');
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function processFocus(child) {
|
||||||
|
var autofocus = "[autofocus]";
|
||||||
|
var autoFocusedElt = matches(child, autofocus) ? child : child.querySelector(autofocus)
|
||||||
|
if (autoFocusedElt != null) {
|
||||||
|
autoFocusedElt.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function insertNodesBefore(parentNode, insertBefore, fragment, settleInfo) {
|
function insertNodesBefore(parentNode, insertBefore, fragment, settleInfo) {
|
||||||
handleAttributes(parentNode, fragment, settleInfo);
|
handleAttributes(parentNode, fragment, settleInfo);
|
||||||
while(fragment.childNodes.length > 0){
|
while(fragment.childNodes.length > 0){
|
||||||
@ -491,6 +514,7 @@ return (function () {
|
|||||||
} else {
|
} else {
|
||||||
var newElt = eltBeforeNewContent.nextSibling;
|
var newElt = eltBeforeNewContent.nextSibling;
|
||||||
}
|
}
|
||||||
|
getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later
|
||||||
while(newElt && newElt !== target) {
|
while(newElt && newElt !== target) {
|
||||||
settleInfo.elts.push(newElt);
|
settleInfo.elts.push(newElt);
|
||||||
newElt = newElt.nextSibling;
|
newElt = newElt.nextSibling;
|
||||||
@ -521,8 +545,10 @@ return (function () {
|
|||||||
insertNodesBefore(target, firstChild, fragment, settleInfo);
|
insertNodesBefore(target, firstChild, fragment, settleInfo);
|
||||||
if (firstChild) {
|
if (firstChild) {
|
||||||
while (firstChild.nextSibling) {
|
while (firstChild.nextSibling) {
|
||||||
|
closeConnections(firstChild.nextSibling)
|
||||||
target.removeChild(firstChild.nextSibling);
|
target.removeChild(firstChild.nextSibling);
|
||||||
}
|
}
|
||||||
|
closeConnections(firstChild)
|
||||||
target.removeChild(firstChild);
|
target.removeChild(firstChild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -760,12 +786,18 @@ return (function () {
|
|||||||
function initScrollHandler() {
|
function initScrollHandler() {
|
||||||
if (!window['htmxScrollHandler']) {
|
if (!window['htmxScrollHandler']) {
|
||||||
var scrollHandler = function() {
|
var scrollHandler = function() {
|
||||||
forEach(getDocument().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"), function (elt) {
|
windowIsScrolling = true
|
||||||
maybeReveal(elt);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
window['htmxScrollHandler'] = scrollHandler;
|
window['htmxScrollHandler'] = scrollHandler;
|
||||||
window.addEventListener("scroll", scrollHandler)
|
window.addEventListener("scroll", scrollHandler)
|
||||||
|
setInterval(function() {
|
||||||
|
if (windowIsScrolling) {
|
||||||
|
windowIsScrolling = false;
|
||||||
|
forEach(getDocument().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"), function (elt) {
|
||||||
|
maybeReveal(elt);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -778,9 +810,9 @@ return (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function processWebSocketInfo(elt, nodeData, info) {
|
function processWebSocketInfo(elt, nodeData, info) {
|
||||||
var values = info.split(",");
|
var values = splitOnWhitespace(info);
|
||||||
for (var i = 0; i < values.length; i++) {
|
for (var i = 0; i < values.length; i++) {
|
||||||
var value = splitOnWhitespace(values[i]);
|
var value = values[i].split(/:(.+)/);
|
||||||
if (value[0] === "connect") {
|
if (value[0] === "connect") {
|
||||||
processWebSocketSource(elt, value[1]);
|
processWebSocketSource(elt, value[1]);
|
||||||
}
|
}
|
||||||
@ -791,6 +823,9 @@ return (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function processWebSocketSource(elt, wssSource) {
|
function processWebSocketSource(elt, wssSource) {
|
||||||
|
if (wssSource.indexOf("ws:") !== 0 && wssSource.indexOf("wss:") !== 0) {
|
||||||
|
wssSource = "wss:" + wssSource;
|
||||||
|
}
|
||||||
var socket = htmx.createWebSocket(wssSource);
|
var socket = htmx.createWebSocket(wssSource);
|
||||||
socket.onerror = function (e) {
|
socket.onerror = function (e) {
|
||||||
triggerErrorEvent(elt, "htmx:wsError", {error:e, socket:socket});
|
triggerErrorEvent(elt, "htmx:wsError", {error:e, socket:socket});
|
||||||
@ -847,20 +882,21 @@ return (function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function maybeCloseSSESource(elt) {
|
//====================================================================
|
||||||
if (!bodyContains(elt)) {
|
// Server Sent Events
|
||||||
getInternalData(elt).sseEventSource.close();
|
//====================================================================
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function processSSEInfo(elt, nodeData, info) {
|
function processSSEInfo(elt, nodeData, info) {
|
||||||
var values = info.split(",");
|
var values = splitOnWhitespace(info);
|
||||||
for (var i = 0; i < values.length; i++) {
|
for (var i = 0; i < values.length; i++) {
|
||||||
var value = splitOnWhitespace(values[i]);
|
var value = values[i].split(/:(.+)/);
|
||||||
if (value[0] === "connect") {
|
if (value[0] === "connect") {
|
||||||
processSSESource(elt, value[1]);
|
processSSESource(elt, value[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((value[0] === "swap")) {
|
||||||
|
processSSESwap(elt, value[1])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -873,10 +909,41 @@ return (function () {
|
|||||||
getInternalData(elt).sseEventSource = source;
|
getInternalData(elt).sseEventSource = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function processSSESwap(elt, sseEventName) {
|
||||||
|
var sseSourceElt = getClosestMatch(elt, hasEventSource);
|
||||||
|
if (sseSourceElt) {
|
||||||
|
var sseEventSource = getInternalData(sseSourceElt).sseEventSource;
|
||||||
|
var sseListener = function (event) {
|
||||||
|
if (maybeCloseSSESource(sseSourceElt)) {
|
||||||
|
sseEventSource.removeEventListener(sseEventName, sseListener);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////
|
||||||
|
// TODO: merge this code with AJAX and WebSockets code in the future.
|
||||||
|
|
||||||
|
var response = event.data;
|
||||||
|
withExtensions(elt, function(extension){
|
||||||
|
response = extension.transformResponse(response, null, elt);
|
||||||
|
});
|
||||||
|
|
||||||
|
var swapSpec = getSwapSpecification(elt)
|
||||||
|
var target = getTarget(elt)
|
||||||
|
var settleInfo = makeSettleInfo(elt);
|
||||||
|
|
||||||
|
selectAndSwap(swapSpec.swapStyle, elt, target, response, settleInfo)
|
||||||
|
triggerEvent(elt, "htmx:sseMessage", event)
|
||||||
|
};
|
||||||
|
|
||||||
|
getInternalData(elt).sseListener = sseListener;
|
||||||
|
sseEventSource.addEventListener(sseEventName, sseListener);
|
||||||
|
} else {
|
||||||
|
triggerErrorEvent(elt, "htmx:noSSESourceError");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function processSSETrigger(elt, verb, path, sseEventName) {
|
function processSSETrigger(elt, verb, path, sseEventName) {
|
||||||
var sseSourceElt = getClosestMatch(elt, function (parent) {
|
var sseSourceElt = getClosestMatch(elt, hasEventSource);
|
||||||
return getInternalData(parent).sseEventSource != null;
|
|
||||||
});
|
|
||||||
if (sseSourceElt) {
|
if (sseSourceElt) {
|
||||||
var sseEventSource = getInternalData(sseSourceElt).sseEventSource;
|
var sseEventSource = getInternalData(sseSourceElt).sseEventSource;
|
||||||
var sseListener = function () {
|
var sseListener = function () {
|
||||||
@ -895,6 +962,19 @@ return (function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function maybeCloseSSESource(elt) {
|
||||||
|
if (!bodyContains(elt)) {
|
||||||
|
getInternalData(elt).sseEventSource.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasEventSource(node) {
|
||||||
|
return getInternalData(node).sseEventSource != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//====================================================================
|
||||||
|
|
||||||
function loadImmediately(elt, verb, path, nodeData, delay) {
|
function loadImmediately(elt, verb, path, nodeData, delay) {
|
||||||
var load = function(){
|
var load = function(){
|
||||||
if (!nodeData.loaded) {
|
if (!nodeData.loaded) {
|
||||||
@ -956,13 +1036,10 @@ return (function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function isHyperScriptAvailable() {
|
|
||||||
return typeof _hyperscript !== "undefined";
|
|
||||||
}
|
|
||||||
|
|
||||||
function findElementsToProcess(elt) {
|
function findElementsToProcess(elt) {
|
||||||
if (elt.querySelectorAll) {
|
if (elt.querySelectorAll) {
|
||||||
var results = elt.querySelectorAll(VERB_SELECTOR + ", a, form, [hx-sse], [data-hx-sse], [hx-ws], [data-hx-ws]");
|
var results = elt.querySelectorAll(VERB_SELECTOR + ", a, form, [hx-sse], [data-hx-sse], [hx-ws]," +
|
||||||
|
" [data-hx-ws]");
|
||||||
return results;
|
return results;
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
@ -974,10 +1051,6 @@ return (function () {
|
|||||||
if (!nodeData.initialized) {
|
if (!nodeData.initialized) {
|
||||||
nodeData.initialized = true;
|
nodeData.initialized = true;
|
||||||
|
|
||||||
if (isHyperScriptAvailable()) {
|
|
||||||
_hyperscript.init(elt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elt.value) {
|
if (elt.value) {
|
||||||
nodeData.lastValue = elt.value;
|
nodeData.lastValue = elt.value;
|
||||||
}
|
}
|
||||||
@ -1011,6 +1084,10 @@ return (function () {
|
|||||||
// Event/Log Support
|
// Event/Log Support
|
||||||
//====================================================================
|
//====================================================================
|
||||||
|
|
||||||
|
function kebabEventName(str) {
|
||||||
|
return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
function makeEvent(eventName, detail) {
|
function makeEvent(eventName, detail) {
|
||||||
var evt;
|
var evt;
|
||||||
if (window.CustomEvent && typeof window.CustomEvent === 'function') {
|
if (window.CustomEvent && typeof window.CustomEvent === 'function') {
|
||||||
@ -1062,6 +1139,10 @@ return (function () {
|
|||||||
triggerEvent(elt, "htmx:error", {errorInfo:detail})
|
triggerEvent(elt, "htmx:error", {errorInfo:detail})
|
||||||
}
|
}
|
||||||
var eventResult = elt.dispatchEvent(event);
|
var eventResult = elt.dispatchEvent(event);
|
||||||
|
if (eventResult) {
|
||||||
|
var kebabedEvent = makeEvent(kebabEventName(eventName), event.detail);
|
||||||
|
eventResult = eventResult && elt.dispatchEvent(kebabedEvent)
|
||||||
|
}
|
||||||
withExtensions(elt, function (extension) {
|
withExtensions(elt, function (extension) {
|
||||||
eventResult = eventResult && (extension.onEvent(eventName, event) !== false)
|
eventResult = eventResult && (extension.onEvent(eventName, event) !== false)
|
||||||
});
|
});
|
||||||
@ -1231,6 +1312,9 @@ return (function () {
|
|||||||
if (shouldInclude(elt)) {
|
if (shouldInclude(elt)) {
|
||||||
var name = getRawAttribute(elt,"name");
|
var name = getRawAttribute(elt,"name");
|
||||||
var value = elt.value;
|
var value = elt.value;
|
||||||
|
if (!!getRawAttribute(elt, 'multiple')) {
|
||||||
|
value = toArray(elt.querySelectorAll("option:checked")).map(function (e) { return e.value });
|
||||||
|
}
|
||||||
if (name != null && value != null) {
|
if (name != null && value != null) {
|
||||||
var current = values[name];
|
var current = values[name];
|
||||||
if(current) {
|
if(current) {
|
||||||
@ -1442,6 +1526,30 @@ return (function () {
|
|||||||
addExpressionVars(parentElt(elt), rawParameters);
|
addExpressionVars(parentElt(elt), rawParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function safelySetHeaderValue(xhr, header, headerValue) {
|
||||||
|
if (headerValue !== null) {
|
||||||
|
try {
|
||||||
|
xhr.setRequestHeader(header, headerValue);
|
||||||
|
} catch (e) {
|
||||||
|
// On an exception, try to set the header URI encoded instead
|
||||||
|
xhr.setRequestHeader(header, encodeURIComponent(headerValue));
|
||||||
|
xhr.setRequestHeader(header + "-URI-AutoEncoded", "true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getResponseURL(xhr) {
|
||||||
|
// NB: IE11 does not support this stuff
|
||||||
|
if (xhr.responseURL && typeof(URL) !== "undefined") {
|
||||||
|
try {
|
||||||
|
var url = new URL(xhr.responseURL);
|
||||||
|
return url.pathname + url.search;
|
||||||
|
} catch (e) {
|
||||||
|
triggerErrorEvent(getDocument().body, "htmx:badResponseUrl", {url: xhr.responseURL});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function issueAjaxRequest(elt, verb, path, eventTarget) {
|
function issueAjaxRequest(elt, verb, path, eventTarget) {
|
||||||
var target = getTarget(elt);
|
var target = getTarget(elt);
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
@ -1535,7 +1643,8 @@ return (function () {
|
|||||||
// request headers
|
// request headers
|
||||||
for (var header in headers) {
|
for (var header in headers) {
|
||||||
if (headers.hasOwnProperty(header)) {
|
if (headers.hasOwnProperty(header)) {
|
||||||
if (headers[header] !== null) xhr.setRequestHeader(header, headers[header]);
|
var headerValue = headers[header];
|
||||||
|
safelySetHeaderValue(xhr, header, headerValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1553,7 +1662,7 @@ return (function () {
|
|||||||
if (this.status === 286) {
|
if (this.status === 286) {
|
||||||
cancelPolling(elt);
|
cancelPolling(elt);
|
||||||
}
|
}
|
||||||
// don't process 'No Content' response
|
// don't process 'No Content'
|
||||||
if (this.status !== 204) {
|
if (this.status !== 204) {
|
||||||
if (!triggerEvent(target, 'htmx:beforeSwap', eventDetail)) return;
|
if (!triggerEvent(target, 'htmx:beforeSwap', eventDetail)) return;
|
||||||
|
|
||||||
@ -1613,7 +1722,7 @@ return (function () {
|
|||||||
});
|
});
|
||||||
// push URL and save new page
|
// push URL and save new page
|
||||||
if (shouldSaveHistory) {
|
if (shouldSaveHistory) {
|
||||||
var pathToPush = pushedUrl || getPushUrl(elt) || finalPathForGet || path;
|
var pathToPush = pushedUrl || getPushUrl(elt) || getResponseURL(xhr) || finalPathForGet || path;
|
||||||
pushUrlIntoHistory(pathToPush);
|
pushUrlIntoHistory(pathToPush);
|
||||||
triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path:pathToPush});
|
triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path:pathToPush});
|
||||||
}
|
}
|
||||||
@ -1645,8 +1754,9 @@ return (function () {
|
|||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
removeRequestIndicatorClasses(elt);
|
removeRequestIndicatorClasses(elt);
|
||||||
triggerEvent(elt, 'htmx:afterRequest', eventDetail);
|
var finalElt = getInternalData(elt).replacedWith || elt;
|
||||||
triggerEvent(elt, 'htmx:afterOnLoad', eventDetail);
|
triggerEvent(finalElt, 'htmx:afterRequest', eventDetail);
|
||||||
|
triggerEvent(finalElt, 'htmx:afterOnLoad', eventDetail);
|
||||||
endRequestLock();
|
endRequestLock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1747,7 +1857,7 @@ return (function () {
|
|||||||
mergeMetaConfig();
|
mergeMetaConfig();
|
||||||
insertIndicatorStyles();
|
insertIndicatorStyles();
|
||||||
var body = getDocument().body;
|
var body = getDocument().body;
|
||||||
processNode(body, true);
|
processNode(body);
|
||||||
triggerEvent(body, 'htmx:load', {});
|
triggerEvent(body, 'htmx:load', {});
|
||||||
window.onpopstate = function (event) {
|
window.onpopstate = function (event) {
|
||||||
if (event.state && event.state.htmx) {
|
if (event.state && event.state.htmx) {
|
||||||
|
@ -41,6 +41,7 @@ return (function () {
|
|||||||
requestClass:'htmx-request',
|
requestClass:'htmx-request',
|
||||||
settlingClass:'htmx-settling',
|
settlingClass:'htmx-settling',
|
||||||
swappingClass:'htmx-swapping',
|
swappingClass:'htmx-swapping',
|
||||||
|
attributesToSwizzle:["class", "style", "width", "height"]
|
||||||
},
|
},
|
||||||
parseInterval:parseInterval,
|
parseInterval:parseInterval,
|
||||||
_:internalEval,
|
_:internalEval,
|
||||||
@ -57,6 +58,8 @@ return (function () {
|
|||||||
return "[hx-" + verb + "], [data-hx-" + verb + "]"
|
return "[hx-" + verb + "], [data-hx-" + verb + "]"
|
||||||
}).join(", ");
|
}).join(", ");
|
||||||
|
|
||||||
|
var windowIsScrolling = false // used by initScrollHandler
|
||||||
|
|
||||||
//====================================================================
|
//====================================================================
|
||||||
// Utilities
|
// Utilities
|
||||||
//====================================================================
|
//====================================================================
|
||||||
@ -216,7 +219,7 @@ return (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function splitOnWhitespace(trigger) {
|
function splitOnWhitespace(trigger) {
|
||||||
return trigger.split(/\s+/);
|
return trigger.trim().split(/\s+/);
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergeObjects(obj1, obj2) {
|
function mergeObjects(obj1, obj2) {
|
||||||
@ -362,6 +365,8 @@ return (function () {
|
|||||||
return explicitTarget;
|
return explicitTarget;
|
||||||
} else if (targetStr.indexOf("closest ") === 0) {
|
} else if (targetStr.indexOf("closest ") === 0) {
|
||||||
return closest(elt, targetStr.substr(8));
|
return closest(elt, targetStr.substr(8));
|
||||||
|
} else if (targetStr.indexOf("find ") === 0) {
|
||||||
|
return find(elt, targetStr.substr(5));
|
||||||
} else {
|
} else {
|
||||||
return getDocument().querySelector(targetStr);
|
return getDocument().querySelector(targetStr);
|
||||||
}
|
}
|
||||||
@ -375,15 +380,24 @@ return (function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var EXCLUDED_ATTRIBUTES = ['id', 'value'];
|
function shouldSettleAttribute(name) {
|
||||||
|
var attributesToSwizzle = htmx.config.attributesToSwizzle;
|
||||||
|
for (var i = 0; i < attributesToSwizzle.length; i++) {
|
||||||
|
if (name === attributesToSwizzle[i]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function cloneAttributes(mergeTo, mergeFrom) {
|
function cloneAttributes(mergeTo, mergeFrom) {
|
||||||
forEach(mergeTo.attributes, function (attr) {
|
forEach(mergeTo.attributes, function (attr) {
|
||||||
if (!mergeFrom.hasAttribute(attr.name) && EXCLUDED_ATTRIBUTES.indexOf(attr.name) === -1) {
|
if (!mergeFrom.hasAttribute(attr.name) && shouldSettleAttribute(attr.name)) {
|
||||||
mergeTo.removeAttribute(attr.name)
|
mergeTo.removeAttribute(attr.name)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
forEach(mergeFrom.attributes, function (attr) {
|
forEach(mergeFrom.attributes, function (attr) {
|
||||||
if (EXCLUDED_ATTRIBUTES.indexOf(attr.name) === -1) {
|
if (shouldSettleAttribute(attr.name)) {
|
||||||
mergeTo.setAttribute(attr.name, attr.value);
|
mergeTo.setAttribute(attr.name, attr.value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -450,12 +464,21 @@ return (function () {
|
|||||||
|
|
||||||
function makeAjaxLoadTask(child) {
|
function makeAjaxLoadTask(child) {
|
||||||
return function () {
|
return function () {
|
||||||
processNode(child, true);
|
processNode(child);
|
||||||
processScripts(child);
|
processScripts(child);
|
||||||
triggerEvent(child, 'htmx:load', {});
|
processFocus(child)
|
||||||
|
triggerEvent(child, 'htmx:load');
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function processFocus(child) {
|
||||||
|
var autofocus = "[autofocus]";
|
||||||
|
var autoFocusedElt = matches(child, autofocus) ? child : child.querySelector(autofocus)
|
||||||
|
if (autoFocusedElt != null) {
|
||||||
|
autoFocusedElt.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function insertNodesBefore(parentNode, insertBefore, fragment, settleInfo) {
|
function insertNodesBefore(parentNode, insertBefore, fragment, settleInfo) {
|
||||||
handleAttributes(parentNode, fragment, settleInfo);
|
handleAttributes(parentNode, fragment, settleInfo);
|
||||||
while(fragment.childNodes.length > 0){
|
while(fragment.childNodes.length > 0){
|
||||||
@ -491,6 +514,7 @@ return (function () {
|
|||||||
} else {
|
} else {
|
||||||
var newElt = eltBeforeNewContent.nextSibling;
|
var newElt = eltBeforeNewContent.nextSibling;
|
||||||
}
|
}
|
||||||
|
getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later
|
||||||
while(newElt && newElt !== target) {
|
while(newElt && newElt !== target) {
|
||||||
settleInfo.elts.push(newElt);
|
settleInfo.elts.push(newElt);
|
||||||
newElt = newElt.nextSibling;
|
newElt = newElt.nextSibling;
|
||||||
@ -521,8 +545,10 @@ return (function () {
|
|||||||
insertNodesBefore(target, firstChild, fragment, settleInfo);
|
insertNodesBefore(target, firstChild, fragment, settleInfo);
|
||||||
if (firstChild) {
|
if (firstChild) {
|
||||||
while (firstChild.nextSibling) {
|
while (firstChild.nextSibling) {
|
||||||
|
closeConnections(firstChild.nextSibling)
|
||||||
target.removeChild(firstChild.nextSibling);
|
target.removeChild(firstChild.nextSibling);
|
||||||
}
|
}
|
||||||
|
closeConnections(firstChild)
|
||||||
target.removeChild(firstChild);
|
target.removeChild(firstChild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -760,12 +786,18 @@ return (function () {
|
|||||||
function initScrollHandler() {
|
function initScrollHandler() {
|
||||||
if (!window['htmxScrollHandler']) {
|
if (!window['htmxScrollHandler']) {
|
||||||
var scrollHandler = function() {
|
var scrollHandler = function() {
|
||||||
forEach(getDocument().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"), function (elt) {
|
windowIsScrolling = true
|
||||||
maybeReveal(elt);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
window['htmxScrollHandler'] = scrollHandler;
|
window['htmxScrollHandler'] = scrollHandler;
|
||||||
window.addEventListener("scroll", scrollHandler)
|
window.addEventListener("scroll", scrollHandler)
|
||||||
|
setInterval(function() {
|
||||||
|
if (windowIsScrolling) {
|
||||||
|
windowIsScrolling = false;
|
||||||
|
forEach(getDocument().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"), function (elt) {
|
||||||
|
maybeReveal(elt);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -778,9 +810,9 @@ return (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function processWebSocketInfo(elt, nodeData, info) {
|
function processWebSocketInfo(elt, nodeData, info) {
|
||||||
var values = info.split(",");
|
var values = splitOnWhitespace(info);
|
||||||
for (var i = 0; i < values.length; i++) {
|
for (var i = 0; i < values.length; i++) {
|
||||||
var value = splitOnWhitespace(values[i]);
|
var value = values[i].split(/:(.+)/);
|
||||||
if (value[0] === "connect") {
|
if (value[0] === "connect") {
|
||||||
processWebSocketSource(elt, value[1]);
|
processWebSocketSource(elt, value[1]);
|
||||||
}
|
}
|
||||||
@ -791,6 +823,9 @@ return (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function processWebSocketSource(elt, wssSource) {
|
function processWebSocketSource(elt, wssSource) {
|
||||||
|
if (wssSource.indexOf("ws:") !== 0 && wssSource.indexOf("wss:") !== 0) {
|
||||||
|
wssSource = "wss:" + wssSource;
|
||||||
|
}
|
||||||
var socket = htmx.createWebSocket(wssSource);
|
var socket = htmx.createWebSocket(wssSource);
|
||||||
socket.onerror = function (e) {
|
socket.onerror = function (e) {
|
||||||
triggerErrorEvent(elt, "htmx:wsError", {error:e, socket:socket});
|
triggerErrorEvent(elt, "htmx:wsError", {error:e, socket:socket});
|
||||||
@ -847,20 +882,21 @@ return (function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function maybeCloseSSESource(elt) {
|
//====================================================================
|
||||||
if (!bodyContains(elt)) {
|
// Server Sent Events
|
||||||
getInternalData(elt).sseEventSource.close();
|
//====================================================================
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function processSSEInfo(elt, nodeData, info) {
|
function processSSEInfo(elt, nodeData, info) {
|
||||||
var values = info.split(",");
|
var values = splitOnWhitespace(info);
|
||||||
for (var i = 0; i < values.length; i++) {
|
for (var i = 0; i < values.length; i++) {
|
||||||
var value = splitOnWhitespace(values[i]);
|
var value = values[i].split(/:(.+)/);
|
||||||
if (value[0] === "connect") {
|
if (value[0] === "connect") {
|
||||||
processSSESource(elt, value[1]);
|
processSSESource(elt, value[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((value[0] === "swap")) {
|
||||||
|
processSSESwap(elt, value[1])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -873,10 +909,41 @@ return (function () {
|
|||||||
getInternalData(elt).sseEventSource = source;
|
getInternalData(elt).sseEventSource = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function processSSESwap(elt, sseEventName) {
|
||||||
|
var sseSourceElt = getClosestMatch(elt, hasEventSource);
|
||||||
|
if (sseSourceElt) {
|
||||||
|
var sseEventSource = getInternalData(sseSourceElt).sseEventSource;
|
||||||
|
var sseListener = function (event) {
|
||||||
|
if (maybeCloseSSESource(sseSourceElt)) {
|
||||||
|
sseEventSource.removeEventListener(sseEventName, sseListener);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////
|
||||||
|
// TODO: merge this code with AJAX and WebSockets code in the future.
|
||||||
|
|
||||||
|
var response = event.data;
|
||||||
|
withExtensions(elt, function(extension){
|
||||||
|
response = extension.transformResponse(response, null, elt);
|
||||||
|
});
|
||||||
|
|
||||||
|
var swapSpec = getSwapSpecification(elt)
|
||||||
|
var target = getTarget(elt)
|
||||||
|
var settleInfo = makeSettleInfo(elt);
|
||||||
|
|
||||||
|
selectAndSwap(swapSpec.swapStyle, elt, target, response, settleInfo)
|
||||||
|
triggerEvent(elt, "htmx:sseMessage", event)
|
||||||
|
};
|
||||||
|
|
||||||
|
getInternalData(elt).sseListener = sseListener;
|
||||||
|
sseEventSource.addEventListener(sseEventName, sseListener);
|
||||||
|
} else {
|
||||||
|
triggerErrorEvent(elt, "htmx:noSSESourceError");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function processSSETrigger(elt, verb, path, sseEventName) {
|
function processSSETrigger(elt, verb, path, sseEventName) {
|
||||||
var sseSourceElt = getClosestMatch(elt, function (parent) {
|
var sseSourceElt = getClosestMatch(elt, hasEventSource);
|
||||||
return getInternalData(parent).sseEventSource != null;
|
|
||||||
});
|
|
||||||
if (sseSourceElt) {
|
if (sseSourceElt) {
|
||||||
var sseEventSource = getInternalData(sseSourceElt).sseEventSource;
|
var sseEventSource = getInternalData(sseSourceElt).sseEventSource;
|
||||||
var sseListener = function () {
|
var sseListener = function () {
|
||||||
@ -895,6 +962,19 @@ return (function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function maybeCloseSSESource(elt) {
|
||||||
|
if (!bodyContains(elt)) {
|
||||||
|
getInternalData(elt).sseEventSource.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasEventSource(node) {
|
||||||
|
return getInternalData(node).sseEventSource != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//====================================================================
|
||||||
|
|
||||||
function loadImmediately(elt, verb, path, nodeData, delay) {
|
function loadImmediately(elt, verb, path, nodeData, delay) {
|
||||||
var load = function(){
|
var load = function(){
|
||||||
if (!nodeData.loaded) {
|
if (!nodeData.loaded) {
|
||||||
@ -956,13 +1036,10 @@ return (function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function isHyperScriptAvailable() {
|
|
||||||
return typeof _hyperscript !== "undefined";
|
|
||||||
}
|
|
||||||
|
|
||||||
function findElementsToProcess(elt) {
|
function findElementsToProcess(elt) {
|
||||||
if (elt.querySelectorAll) {
|
if (elt.querySelectorAll) {
|
||||||
var results = elt.querySelectorAll(VERB_SELECTOR + ", a, form, [hx-sse], [data-hx-sse], [hx-ws], [data-hx-ws]");
|
var results = elt.querySelectorAll(VERB_SELECTOR + ", a, form, [hx-sse], [data-hx-sse], [hx-ws]," +
|
||||||
|
" [data-hx-ws]");
|
||||||
return results;
|
return results;
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
@ -974,10 +1051,6 @@ return (function () {
|
|||||||
if (!nodeData.initialized) {
|
if (!nodeData.initialized) {
|
||||||
nodeData.initialized = true;
|
nodeData.initialized = true;
|
||||||
|
|
||||||
if (isHyperScriptAvailable()) {
|
|
||||||
_hyperscript.init(elt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elt.value) {
|
if (elt.value) {
|
||||||
nodeData.lastValue = elt.value;
|
nodeData.lastValue = elt.value;
|
||||||
}
|
}
|
||||||
@ -1011,6 +1084,10 @@ return (function () {
|
|||||||
// Event/Log Support
|
// Event/Log Support
|
||||||
//====================================================================
|
//====================================================================
|
||||||
|
|
||||||
|
function kebabEventName(str) {
|
||||||
|
return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
function makeEvent(eventName, detail) {
|
function makeEvent(eventName, detail) {
|
||||||
var evt;
|
var evt;
|
||||||
if (window.CustomEvent && typeof window.CustomEvent === 'function') {
|
if (window.CustomEvent && typeof window.CustomEvent === 'function') {
|
||||||
@ -1062,6 +1139,10 @@ return (function () {
|
|||||||
triggerEvent(elt, "htmx:error", {errorInfo:detail})
|
triggerEvent(elt, "htmx:error", {errorInfo:detail})
|
||||||
}
|
}
|
||||||
var eventResult = elt.dispatchEvent(event);
|
var eventResult = elt.dispatchEvent(event);
|
||||||
|
if (eventResult) {
|
||||||
|
var kebabedEvent = makeEvent(kebabEventName(eventName), event.detail);
|
||||||
|
eventResult = eventResult && elt.dispatchEvent(kebabedEvent)
|
||||||
|
}
|
||||||
withExtensions(elt, function (extension) {
|
withExtensions(elt, function (extension) {
|
||||||
eventResult = eventResult && (extension.onEvent(eventName, event) !== false)
|
eventResult = eventResult && (extension.onEvent(eventName, event) !== false)
|
||||||
});
|
});
|
||||||
@ -1231,6 +1312,9 @@ return (function () {
|
|||||||
if (shouldInclude(elt)) {
|
if (shouldInclude(elt)) {
|
||||||
var name = getRawAttribute(elt,"name");
|
var name = getRawAttribute(elt,"name");
|
||||||
var value = elt.value;
|
var value = elt.value;
|
||||||
|
if (!!getRawAttribute(elt, 'multiple')) {
|
||||||
|
value = toArray(elt.querySelectorAll("option:checked")).map(function (e) { return e.value });
|
||||||
|
}
|
||||||
if (name != null && value != null) {
|
if (name != null && value != null) {
|
||||||
var current = values[name];
|
var current = values[name];
|
||||||
if(current) {
|
if(current) {
|
||||||
@ -1442,6 +1526,30 @@ return (function () {
|
|||||||
addExpressionVars(parentElt(elt), rawParameters);
|
addExpressionVars(parentElt(elt), rawParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function safelySetHeaderValue(xhr, header, headerValue) {
|
||||||
|
if (headerValue !== null) {
|
||||||
|
try {
|
||||||
|
xhr.setRequestHeader(header, headerValue);
|
||||||
|
} catch (e) {
|
||||||
|
// On an exception, try to set the header URI encoded instead
|
||||||
|
xhr.setRequestHeader(header, encodeURIComponent(headerValue));
|
||||||
|
xhr.setRequestHeader(header + "-URI-AutoEncoded", "true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getResponseURL(xhr) {
|
||||||
|
// NB: IE11 does not support this stuff
|
||||||
|
if (xhr.responseURL && typeof(URL) !== "undefined") {
|
||||||
|
try {
|
||||||
|
var url = new URL(xhr.responseURL);
|
||||||
|
return url.pathname + url.search;
|
||||||
|
} catch (e) {
|
||||||
|
triggerErrorEvent(getDocument().body, "htmx:badResponseUrl", {url: xhr.responseURL});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function issueAjaxRequest(elt, verb, path, eventTarget) {
|
function issueAjaxRequest(elt, verb, path, eventTarget) {
|
||||||
var target = getTarget(elt);
|
var target = getTarget(elt);
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
@ -1535,7 +1643,8 @@ return (function () {
|
|||||||
// request headers
|
// request headers
|
||||||
for (var header in headers) {
|
for (var header in headers) {
|
||||||
if (headers.hasOwnProperty(header)) {
|
if (headers.hasOwnProperty(header)) {
|
||||||
if (headers[header] !== null) xhr.setRequestHeader(header, headers[header]);
|
var headerValue = headers[header];
|
||||||
|
safelySetHeaderValue(xhr, header, headerValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1553,7 +1662,7 @@ return (function () {
|
|||||||
if (this.status === 286) {
|
if (this.status === 286) {
|
||||||
cancelPolling(elt);
|
cancelPolling(elt);
|
||||||
}
|
}
|
||||||
// don't process 'No Content' response
|
// don't process 'No Content'
|
||||||
if (this.status !== 204) {
|
if (this.status !== 204) {
|
||||||
if (!triggerEvent(target, 'htmx:beforeSwap', eventDetail)) return;
|
if (!triggerEvent(target, 'htmx:beforeSwap', eventDetail)) return;
|
||||||
|
|
||||||
@ -1613,7 +1722,7 @@ return (function () {
|
|||||||
});
|
});
|
||||||
// push URL and save new page
|
// push URL and save new page
|
||||||
if (shouldSaveHistory) {
|
if (shouldSaveHistory) {
|
||||||
var pathToPush = pushedUrl || getPushUrl(elt) || finalPathForGet || path;
|
var pathToPush = pushedUrl || getPushUrl(elt) || getResponseURL(xhr) || finalPathForGet || path;
|
||||||
pushUrlIntoHistory(pathToPush);
|
pushUrlIntoHistory(pathToPush);
|
||||||
triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path:pathToPush});
|
triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path:pathToPush});
|
||||||
}
|
}
|
||||||
@ -1645,8 +1754,9 @@ return (function () {
|
|||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
removeRequestIndicatorClasses(elt);
|
removeRequestIndicatorClasses(elt);
|
||||||
triggerEvent(elt, 'htmx:afterRequest', eventDetail);
|
var finalElt = getInternalData(elt).replacedWith || elt;
|
||||||
triggerEvent(elt, 'htmx:afterOnLoad', eventDetail);
|
triggerEvent(finalElt, 'htmx:afterRequest', eventDetail);
|
||||||
|
triggerEvent(finalElt, 'htmx:afterOnLoad', eventDetail);
|
||||||
endRequestLock();
|
endRequestLock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1747,7 +1857,7 @@ return (function () {
|
|||||||
mergeMetaConfig();
|
mergeMetaConfig();
|
||||||
insertIndicatorStyles();
|
insertIndicatorStyles();
|
||||||
var body = getDocument().body;
|
var body = getDocument().body;
|
||||||
processNode(body, true);
|
processNode(body);
|
||||||
triggerEvent(body, 'htmx:load', {});
|
triggerEvent(body, 'htmx:load', {});
|
||||||
window.onpopstate = function (event) {
|
window.onpopstate = function (event) {
|
||||||
if (event.state && event.state.htmx) {
|
if (event.state && event.state.htmx) {
|
||||||
|
@ -40,7 +40,7 @@ describe("hx-sse attribute", function() {
|
|||||||
this.server.respondWith("GET", "/d1", "div1 updated");
|
this.server.respondWith("GET", "/d1", "div1 updated");
|
||||||
this.server.respondWith("GET", "/d2", "div2 updated");
|
this.server.respondWith("GET", "/d2", "div2 updated");
|
||||||
|
|
||||||
var div = make('<div hx-sse="connect /foo">' +
|
var div = make('<div hx-sse="connect:/foo">' +
|
||||||
'<div id="d1" hx-trigger="sse:e1" hx-get="/d1">div1</div>' +
|
'<div id="d1" hx-trigger="sse:e1" hx-get="/d1">div1</div>' +
|
||||||
'<div id="d2" hx-trigger="sse:e2" hx-get="/d2">div2</div>' +
|
'<div id="d2" hx-trigger="sse:e2" hx-get="/d2">div2</div>' +
|
||||||
'</div>');
|
'</div>');
|
||||||
@ -60,7 +60,7 @@ describe("hx-sse attribute", function() {
|
|||||||
|
|
||||||
this.server.respondWith("GET", "/d1", "div1 updated");
|
this.server.respondWith("GET", "/d1", "div1 updated");
|
||||||
|
|
||||||
var div = make('<div hx-sse="connect /foo">' +
|
var div = make('<div hx-sse="connect:/foo">' +
|
||||||
'<div id="d1" hx-trigger="sse:e1" hx-get="/d1">div1</div>' +
|
'<div id="d1" hx-trigger="sse:e1" hx-get="/d1">div1</div>' +
|
||||||
'</div>');
|
'</div>');
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ describe("hx-sse attribute", function() {
|
|||||||
|
|
||||||
this.server.respondWith("GET", "/d1", "div1 updated");
|
this.server.respondWith("GET", "/d1", "div1 updated");
|
||||||
|
|
||||||
var div = make('<div hx-sse="connect /foo"></div>' +
|
var div = make('<div hx-sse="connect:/foo"></div>' +
|
||||||
'<div id="d1" hx-trigger="sse:e1" hx-get="/d1">div1</div>');
|
'<div id="d1" hx-trigger="sse:e1" hx-get="/d1">div1</div>');
|
||||||
|
|
||||||
this.eventSource.sendEvent("foo");
|
this.eventSource.sendEvent("foo");
|
||||||
@ -99,7 +99,7 @@ describe("hx-sse attribute", function() {
|
|||||||
|
|
||||||
it('is closed after removal', function () {
|
it('is closed after removal', function () {
|
||||||
this.server.respondWith("GET", "/test", "Clicked!");
|
this.server.respondWith("GET", "/test", "Clicked!");
|
||||||
var div = make('<div hx-get="/test" hx-swap="outerHTML" hx-sse="connect /foo">' +
|
var div = make('<div hx-get="/test" hx-swap="outerHTML" hx-sse="connect:/foo">' +
|
||||||
'<div id="d1" hx-trigger="sse:e1" hx-get="/d1">div1</div>' +
|
'<div id="d1" hx-trigger="sse:e1" hx-get="/d1">div1</div>' +
|
||||||
'</div>');
|
'</div>');
|
||||||
div.click();
|
div.click();
|
||||||
@ -108,7 +108,7 @@ describe("hx-sse attribute", function() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('is closed after removal with no close and activity', function () {
|
it('is closed after removal with no close and activity', function () {
|
||||||
var div = make('<div hx-get="/test" hx-swap="outerHTML" hx-sse="connect /foo">' +
|
var div = make('<div hx-get="/test" hx-swap="outerHTML" hx-sse="connect:/foo">' +
|
||||||
'<div id="d1" hx-trigger="sse:e1" hx-get="/d1">div1</div>' +
|
'<div id="d1" hx-trigger="sse:e1" hx-get="/d1">div1</div>' +
|
||||||
'</div>');
|
'</div>');
|
||||||
div.parentElement.removeChild(div);
|
div.parentElement.removeChild(div);
|
||||||
|
@ -47,6 +47,18 @@ describe("hx-target attribute", function(){
|
|||||||
this.server.respond();
|
this.server.respond();
|
||||||
div1.innerHTML.should.equal("Clicked!");
|
div1.innerHTML.should.equal("Clicked!");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('targets a `find` element properly', function()
|
||||||
|
{
|
||||||
|
this.server.respondWith("GET", "/test", "Clicked!");
|
||||||
|
var div1 = make('<div hx-target="find span" hx-get="/test">Click Me! <div><span id="s1"></span><span id="s2"></span></div></div>')
|
||||||
|
div1.click();
|
||||||
|
this.server.respond();
|
||||||
|
var span1 = byId("s1")
|
||||||
|
var span2 = byId("s2")
|
||||||
|
span1.innerHTML.should.equal("Clicked!");
|
||||||
|
span2.innerHTML.should.equal("");
|
||||||
|
});
|
||||||
|
|
||||||
it('targets an inner element properly', function()
|
it('targets an inner element properly', function()
|
||||||
{
|
{
|
||||||
|
@ -40,14 +40,14 @@ describe("hx-ws attribute", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('handles a basic call back', function () {
|
it('handles a basic call back', function () {
|
||||||
var div = make('<div hx-ws="connect wss:/foo"><div id="d1">div1</div><div id="d2">div2</div></div>');
|
var div = make('<div hx-ws="connect:/foo"><div id="d1">div1</div><div id="d2">div2</div></div>');
|
||||||
this.socket.write("<div id=\"d1\">replaced</div>")
|
this.socket.write("<div id=\"d1\">replaced</div>")
|
||||||
byId("d1").innerHTML.should.equal("replaced");
|
byId("d1").innerHTML.should.equal("replaced");
|
||||||
byId("d2").innerHTML.should.equal("div2");
|
byId("d2").innerHTML.should.equal("div2");
|
||||||
})
|
})
|
||||||
|
|
||||||
it('handles a basic send', function () {
|
it('handles a basic send', function () {
|
||||||
var div = make('<div hx-ws="connect wss:/foo"><div hx-ws="send" id="d1">div1</div></div>');
|
var div = make('<div hx-ws="connect:/foo"><div hx-ws="send" id="d1">div1</div></div>');
|
||||||
byId("d1").click();
|
byId("d1").click();
|
||||||
var lastSent = this.socket.getLastSent();
|
var lastSent = this.socket.getLastSent();
|
||||||
var data = JSON.parse(lastSent);
|
var data = JSON.parse(lastSent);
|
||||||
@ -56,14 +56,14 @@ describe("hx-ws attribute", function() {
|
|||||||
|
|
||||||
it('is closed after removal', function () {
|
it('is closed after removal', function () {
|
||||||
this.server.respondWith("GET", "/test", "Clicked!");
|
this.server.respondWith("GET", "/test", "Clicked!");
|
||||||
var div = make('<div hx-get="/test" hx-swap="outerHTML" hx-ws="connect wss:/foo"></div>');
|
var div = make('<div hx-get="/test" hx-swap="outerHTML" hx-ws="connect:wss:/foo"></div>');
|
||||||
div.click();
|
div.click();
|
||||||
this.server.respond();
|
this.server.respond();
|
||||||
this.socket.wasClosed().should.equal(true)
|
this.socket.wasClosed().should.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('is closed after removal with no close and activity', function () {
|
it('is closed after removal with no close and activity', function () {
|
||||||
var div = make('<div hx-ws="connect wss:/foo"></div>');
|
var div = make('<div hx-ws="connect:/foo"></div>');
|
||||||
div.parentElement.removeChild(div);
|
div.parentElement.removeChild(div);
|
||||||
this.socket.write("<div id=\"d1\">replaced</div>")
|
this.socket.write("<div id=\"d1\">replaced</div>")
|
||||||
this.socket.wasClosed().should.equal(true)
|
this.socket.wasClosed().should.equal(true)
|
||||||
|
@ -240,6 +240,36 @@ describe("Core htmx AJAX Tests", function(){
|
|||||||
btn.innerHTML.should.equal("Click Me!");
|
btn.innerHTML.should.equal("Click Me!");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('handles 304 NOT MODIFIED responses properly', function()
|
||||||
|
{
|
||||||
|
this.server.respondWith("GET", "/test-1", [200, {}, "Content for Tab 1"]);
|
||||||
|
this.server.respondWith("GET", "/test-2", [200, {}, "Content for Tab 2"]);
|
||||||
|
|
||||||
|
var target = make('<div id="target"></div>')
|
||||||
|
var btn1 = make('<button hx-get="/test-1" hx-target="#target">Tab 1</button>');
|
||||||
|
var btn2 = make('<button hx-get="/test-2" hx-target="#target">Tab 2</button>');
|
||||||
|
|
||||||
|
btn1.click();
|
||||||
|
target.innerHTML.should.equal("");
|
||||||
|
this.server.respond();
|
||||||
|
target.innerHTML.should.equal("Content for Tab 1");
|
||||||
|
|
||||||
|
btn2.click();
|
||||||
|
this.server.respond();
|
||||||
|
target.innerHTML.should.equal("Content for Tab 2");
|
||||||
|
|
||||||
|
this.server.respondWith("GET", "/test-1", [304, {}, "Content for Tab 1"]);
|
||||||
|
this.server.respondWith("GET", "/test-2", [304, {}, "Content for Tab 2"]);
|
||||||
|
|
||||||
|
btn1.click();
|
||||||
|
this.server.respond();
|
||||||
|
target.innerHTML.should.equal("Content for Tab 1");
|
||||||
|
|
||||||
|
btn2.click();
|
||||||
|
this.server.respond();
|
||||||
|
target.innerHTML.should.equal("Content for Tab 2");
|
||||||
|
});
|
||||||
|
|
||||||
it('handles hx-trigger with non-default value', function()
|
it('handles hx-trigger with non-default value', function()
|
||||||
{
|
{
|
||||||
this.server.respondWith("GET", "/test", "Clicked!");
|
this.server.respondWith("GET", "/test", "Clicked!");
|
||||||
@ -323,17 +353,49 @@ describe("Core htmx AJAX Tests", function(){
|
|||||||
|
|
||||||
it('properly settles attributes on interior elements', function(done)
|
it('properly settles attributes on interior elements', function(done)
|
||||||
{
|
{
|
||||||
this.server.respondWith("GET", "/test", "<div hx-get='/test'><div foo='bar' id='d1'></div></div>");
|
this.server.respondWith("GET", "/test", "<div hx-get='/test'><div width='bar' id='d1'></div></div>");
|
||||||
var div = make("<div hx-get='/test' hx-swap='outerHTML settle:10ms'><div id='d1'></div></div>");
|
var div = make("<div hx-get='/test' hx-swap='outerHTML settle:10ms'><div id='d1'></div></div>");
|
||||||
div.click();
|
div.click();
|
||||||
this.server.respond();
|
this.server.respond();
|
||||||
should.equal(byId("d1").getAttribute("foo"), null);
|
should.equal(byId("d1").getAttribute("width"), null);
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
should.equal(byId("d1").getAttribute("foo"), "bar");
|
should.equal(byId("d1").getAttribute("width"), "bar");
|
||||||
done();
|
done();
|
||||||
}, 20);
|
}, 20);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('properly handles multiple select input', function()
|
||||||
|
{
|
||||||
|
var values;
|
||||||
|
this.server.respondWith("Post", "/test", function (xhr) {
|
||||||
|
values = getParameters(xhr);
|
||||||
|
xhr.respond(204, {}, "");
|
||||||
|
});
|
||||||
|
|
||||||
|
var form = make('<form hx-post="/test" hx-trigger="click">' +
|
||||||
|
'<select id="multiSelect" name="multiSelect" multiple="multiple">'+
|
||||||
|
'<option id="m1" value="m1">m1</option>'+
|
||||||
|
'<option id="m2" value="m2">m2</option>'+
|
||||||
|
'<option id="m3" value="m3">m3</option>'+
|
||||||
|
'<option id="m4" value="m4">m4</option>'+
|
||||||
|
'</form>');
|
||||||
|
|
||||||
|
form.click();
|
||||||
|
this.server.respond();
|
||||||
|
values.should.deep.equal({});
|
||||||
|
|
||||||
|
byId("m1").selected = true;
|
||||||
|
form.click();
|
||||||
|
this.server.respond();
|
||||||
|
values.should.deep.equal({multiSelect:"m1"});
|
||||||
|
|
||||||
|
byId("m1").selected = true;
|
||||||
|
byId("m3").selected = true;
|
||||||
|
form.click();
|
||||||
|
this.server.respond();
|
||||||
|
values.should.deep.equal({multiSelect:["m1", "m3"]});
|
||||||
|
});
|
||||||
|
|
||||||
it('properly handles checkbox inputs', function()
|
it('properly handles checkbox inputs', function()
|
||||||
{
|
{
|
||||||
var values;
|
var values;
|
||||||
@ -487,5 +549,41 @@ describe("Core htmx AJAX Tests", function(){
|
|||||||
input.value.should.equal('bar');
|
input.value.should.equal('bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('autofocus attribute works properly', function()
|
||||||
|
{
|
||||||
|
this.server.respondWith("GET", "/test", "<input id='i2' value='bar' autofocus/>");
|
||||||
|
var input = make("<input id='i1' hx-get='/test' value='foo' hx-swap='afterend' hx-trigger='click'/>");
|
||||||
|
input.focus();
|
||||||
|
input.click();
|
||||||
|
document.activeElement.should.equal(input);
|
||||||
|
this.server.respond();
|
||||||
|
var input2 = byId('i2');
|
||||||
|
document.activeElement.should.equal(input2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('autofocus attribute works properly w/ child', function()
|
||||||
|
{
|
||||||
|
this.server.respondWith("GET", "/test", "<div><input id='i2' value='bar' autofocus/></div>");
|
||||||
|
var input = make("<input id='i1' hx-get='/test' value='foo' hx-swap='afterend' hx-trigger='click'/>");
|
||||||
|
input.focus();
|
||||||
|
input.click();
|
||||||
|
document.activeElement.should.equal(input);
|
||||||
|
this.server.respond();
|
||||||
|
var input2 = byId('i2');
|
||||||
|
document.activeElement.should.equal(input2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('autofocus attribute works properly w/ true value', function()
|
||||||
|
{
|
||||||
|
this.server.respondWith("GET", "/test", "<div><input id='i2' value='bar' autofocus='true'/></div>");
|
||||||
|
var input = make("<input id='i1' hx-get='/test' value='foo' hx-swap='afterend' hx-trigger='click'/>");
|
||||||
|
input.focus();
|
||||||
|
input.click();
|
||||||
|
document.activeElement.should.equal(input);
|
||||||
|
this.server.respond();
|
||||||
|
var input2 = byId('i2');
|
||||||
|
document.activeElement.should.equal(input2);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -44,6 +44,25 @@ describe("Core htmx Events", function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("htmx:configRequest is also dispatched in kebab-case", function () {
|
||||||
|
var handler = htmx.on("htmx:config-request", function (evt) {
|
||||||
|
evt.detail.parameters['param'] = "true";
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
var param = null;
|
||||||
|
this.server.respondWith("POST", "/test", function (xhr) {
|
||||||
|
param = getParameters(xhr)['param'];
|
||||||
|
xhr.respond(200, {}, "");
|
||||||
|
});
|
||||||
|
var div = make("<div hx-post='/test'></div>");
|
||||||
|
div.click();
|
||||||
|
this.server.respond();
|
||||||
|
param.should.equal("true");
|
||||||
|
} finally {
|
||||||
|
htmx.off("htmx:config-request", handler);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it("htmx:configRequest allows attribute removal", function () {
|
it("htmx:configRequest allows attribute removal", function () {
|
||||||
var param = "foo";
|
var param = "foo";
|
||||||
var handler = htmx.on("htmx:configRequest", function (evt) {
|
var handler = htmx.on("htmx:configRequest", function (evt) {
|
||||||
@ -172,23 +191,56 @@ describe("Core htmx Events", function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("htmx:sendError is called after a failed request", function () {
|
it("htmx:sendError is called after a failed request", function (done) {
|
||||||
var called = false;
|
var called = false;
|
||||||
var handler = htmx.on("htmx:sendError", function (evt) {
|
var handler = htmx.on("htmx:sendError", function (evt) {
|
||||||
called = true;
|
called = true;
|
||||||
});
|
});
|
||||||
|
this.server.restore(); // turn off server mock so connection doesn't work
|
||||||
|
var div = make("<button hx-post='file://foo'>Foo</button>");
|
||||||
|
div.click();
|
||||||
|
setTimeout(function () {
|
||||||
|
htmx.off("htmx:sendError", handler);
|
||||||
|
should.equal(called, true);
|
||||||
|
done();
|
||||||
|
}, 30);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("htmx:afterRequest is called when replacing outerHTML", function () {
|
||||||
|
var called = false;
|
||||||
|
var handler = htmx.on("htmx:afterRequest", function (evt) {
|
||||||
|
called = true;
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
this.server.respondWith("POST", "/test", function (xhr) {
|
this.server.respondWith("POST", "/test", function (xhr) {
|
||||||
xhr.respond(200, {}, "");
|
xhr.respond(200, {}, "<button>Bar</button>");
|
||||||
});
|
});
|
||||||
var div = make("<button hx-post='/test'>Foo</button>");
|
var div = make("<button hx-post='/test' hx-swap='outerHTML'>Foo</button>");
|
||||||
div.click();
|
div.click();
|
||||||
this.server.respond();
|
this.server.respond();
|
||||||
should.equal(called, true);
|
should.equal(called, true);
|
||||||
} finally {
|
} finally {
|
||||||
htmx.off("htmx:sendError", handler);
|
htmx.off("htmx:afterRequest", handler);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("htmx:afterOnLoad is called when replacing outerHTML", function () {
|
||||||
|
var called = false;
|
||||||
|
var handler = htmx.on("htmx:afterOnLoad", function (evt) {
|
||||||
|
called = true;
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
this.server.respondWith("POST", "/test", function (xhr) {
|
||||||
|
xhr.respond(200, {}, "<button>Bar</button>");
|
||||||
|
});
|
||||||
|
var div = make("<button hx-post='/test' hx-swap='outerHTML'>Foo</button>");
|
||||||
|
div.click();
|
||||||
|
this.server.respond();
|
||||||
|
should.equal(called, true);
|
||||||
|
} finally {
|
||||||
|
htmx.off("htmx:afterOnLoad", handler);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,12 +1,4 @@
|
|||||||
describe("Core htmx internals Tests", function() {
|
describe("Core htmx internals Tests", function() {
|
||||||
beforeEach(function () {
|
|
||||||
this.server = makeServer();
|
|
||||||
clearWorkArea();
|
|
||||||
});
|
|
||||||
afterEach(function () {
|
|
||||||
this.server.restore();
|
|
||||||
clearWorkArea();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("makeFragment works with janky stuff", function(){
|
it("makeFragment works with janky stuff", function(){
|
||||||
htmx._("makeFragment")("<html></html>").tagName.should.equal("BODY");
|
htmx._("makeFragment")("<html></html>").tagName.should.equal("BODY");
|
||||||
@ -20,4 +12,12 @@ describe("Core htmx internals Tests", function() {
|
|||||||
htmx._("makeFragment")("<tr></tr>").tagName.should.equal("TBODY");
|
htmx._("makeFragment")("<tr></tr>").tagName.should.equal("TBODY");
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("set header works with non-ASCII values", function(){
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("GET", "/dummy");
|
||||||
|
htmx._("safelySetHeaderValue")(xhr, "Example", "привет");
|
||||||
|
// unfortunately I can't test the value :/
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
|
||||||
|
})
|
||||||
|
|
||||||
});
|
});
|
@ -73,4 +73,20 @@ describe("Core htmx Regression Tests", function(){
|
|||||||
div.innerText.should.contain("Foo");
|
div.innerText.should.contain("Foo");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it ('@ symbol in attributes does not break requests', function(){
|
||||||
|
this.server.respondWith("GET", "/test", "<div id='d1' @foo='bar'>Foo</div>");
|
||||||
|
var div = make('<div hx-get="/test">Get It</div>');
|
||||||
|
div.click();
|
||||||
|
this.server.respond();
|
||||||
|
byId("d1").getAttribute('@foo').should.equal('bar');
|
||||||
|
});
|
||||||
|
|
||||||
|
it ('@ symbol in attributes does not break attribute swizzling requests', function(){
|
||||||
|
this.server.respondWith("GET", "/test", "<div id='d1' @foo='bar'>Foo</div>");
|
||||||
|
var div = make('<div hx-get="/test"><div id="d1">Foo</div></div>');
|
||||||
|
div.click();
|
||||||
|
this.server.respond();
|
||||||
|
byId("d1").getAttribute('@foo').should.equal('bar');
|
||||||
|
});
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -11,6 +11,7 @@ describe("hyperscript integration", function() {
|
|||||||
it('can trigger with a custom event', function () {
|
it('can trigger with a custom event', function () {
|
||||||
this.server.respondWith("GET", "/test", "Custom Event Sent!");
|
this.server.respondWith("GET", "/test", "Custom Event Sent!");
|
||||||
var btn = make('<button _="on click send customEvent" hx-trigger="customEvent" hx-get="/test">Click Me!</button>')
|
var btn = make('<button _="on click send customEvent" hx-trigger="customEvent" hx-get="/test">Click Me!</button>')
|
||||||
|
htmx.trigger(btn, "htmx:load"); // have to manually trigger the load event for non-AJAX dynamic content
|
||||||
btn.click();
|
btn.click();
|
||||||
this.server.respond();
|
this.server.respond();
|
||||||
btn.innerHTML.should.equal("Custom Event Sent!");
|
btn.innerHTML.should.equal("Custom Event Sent!");
|
||||||
@ -19,6 +20,7 @@ describe("hyperscript integration", function() {
|
|||||||
it('can handle htmx driven events', function () {
|
it('can handle htmx driven events', function () {
|
||||||
this.server.respondWith("GET", "/test", "Clicked!");
|
this.server.respondWith("GET", "/test", "Clicked!");
|
||||||
var btn = make('<button _="on htmx:afterSettle add .afterSettle" hx-get="/test">Click Me!</button>')
|
var btn = make('<button _="on htmx:afterSettle add .afterSettle" hx-get="/test">Click Me!</button>')
|
||||||
|
htmx.trigger(btn, "htmx:load");
|
||||||
btn.classList.contains("afterSettle").should.equal(false);
|
btn.classList.contains("afterSettle").should.equal(false);
|
||||||
btn.click();
|
btn.click();
|
||||||
this.server.respond();
|
this.server.respond();
|
||||||
@ -29,9 +31,20 @@ describe("hyperscript integration", function() {
|
|||||||
this.server.respondWith("GET", "/test", [404, {}, "Bad request"]);
|
this.server.respondWith("GET", "/test", [404, {}, "Bad request"]);
|
||||||
var div = make('<div id="d1"></div>')
|
var div = make('<div id="d1"></div>')
|
||||||
var btn = make('<button _="on htmx:error(errorInfo) put errorInfo.error into #d1.innerHTML" hx-get="/test">Click Me!</button>')
|
var btn = make('<button _="on htmx:error(errorInfo) put errorInfo.error into #d1.innerHTML" hx-get="/test">Click Me!</button>')
|
||||||
|
htmx.trigger(btn, "htmx:load");
|
||||||
btn.click();
|
btn.click();
|
||||||
this.server.respond();
|
this.server.respond();
|
||||||
div.innerHTML.should.equal("Response Status Error Code 404 from /test");
|
div.innerHTML.should.equal("Response Status Error Code 404 from /test");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('hyperscript in non-htmx annotated nodes is evaluated', function () {
|
||||||
|
this.server.respondWith("GET", "/test", "<div><div><div id='d1' _='on click put \"Clicked...\" into my.innerHTML'></div></div></div>");
|
||||||
|
var btn = make('<button hx-get="/test">Click Me!</button>')
|
||||||
|
btn.click();
|
||||||
|
this.server.respond();
|
||||||
|
var newDiv = byId("d1");
|
||||||
|
newDiv.click();
|
||||||
|
newDiv.innerText.should.equal("Clicked...");
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
@ -37,7 +37,19 @@
|
|||||||
<a href="manual/confirm-and-prompt.html">Confirm & Prompt Test</a>
|
<a href="manual/confirm-and-prompt.html">Confirm & Prompt Test</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="manual/scroll-tests.html">Scroll Test</a>
|
<a href="manual/scroll-test-startEnd.html">Scroll Test 1 - Start/End</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="manual/scroll-test-eventHandler.html">Scroll Test 2 - Event Handler</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="manual/sse.html">SSE - Multiple Event Sources - Single Event Name</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="manual/sse-multichannel.html">SSE - Single Event Source - Multiple Event Names</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="manual/sse-settle.html">SSE - Settling</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@ -89,9 +101,6 @@
|
|||||||
<!-- hyperscript integration -->
|
<!-- hyperscript integration -->
|
||||||
<script src="lib/_hyperscript.js"></script>
|
<script src="lib/_hyperscript.js"></script>
|
||||||
<script src="ext/hyperscript.js"></script>
|
<script src="ext/hyperscript.js"></script>
|
||||||
<script>
|
|
||||||
_hyperscript.start();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- extension tests -->
|
<!-- extension tests -->
|
||||||
<script src="ext/extension-swap.js"></script>
|
<script src="ext/extension-swap.js"></script>
|
||||||
|
File diff suppressed because it is too large
Load Diff
107
www/test/0.1.0/test/manual/scroll-test-eventHandler.html
Normal file
107
www/test/0.1.0/test/manual/scroll-test-eventHandler.html
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Test Scroll Event Handler</title>
|
||||||
|
<script src="../../node_modules/sinon/pkg/sinon.js"></script>
|
||||||
|
<script src="../util/util.js"></script>
|
||||||
|
<script src="../../src/htmx.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
server = makeServer();
|
||||||
|
server.autoRespond = true;
|
||||||
|
server.respondWith("GET", "/more_content", "Here is more content for this page, loaded 'remotely'.");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.panel {
|
||||||
|
height:200px;
|
||||||
|
background-color:#eee;
|
||||||
|
margin-bottom:20px;
|
||||||
|
padding:20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body style="padding:20px;font-family: sans-serif">
|
||||||
|
<h1>Scrolling Event Handler Tests</h1>
|
||||||
|
<p>You should be able to scroll this page at any speed and see HTML fragments loaded into the DIVs "remotely" without any hiccups.</p>
|
||||||
|
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
<div class="panel" hx-get="/more_content" hx-trigger="revealed"></div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
29
www/test/0.1.0/test/manual/scroll-test-startEnd.html
Normal file
29
www/test/0.1.0/test/manual/scroll-test-startEnd.html
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Test Scroll Behavior</title>
|
||||||
|
<script src="../../src/htmx.js"></script>
|
||||||
|
</head>
|
||||||
|
<body style="padding:20px;font-family: sans-serif">
|
||||||
|
<script src="../../node_modules/sinon/pkg/sinon.js"></script>
|
||||||
|
<script src="../../src/htmx.js"></script>
|
||||||
|
<script src="../util/util.js"></script>
|
||||||
|
<script>
|
||||||
|
server = makeServer();
|
||||||
|
server.autoRespond = true;
|
||||||
|
server.respondWith("GET", "/more_divs", "<div>More Content</div>");
|
||||||
|
</script>
|
||||||
|
<h1>Scrolling Start/End Tests</h1>
|
||||||
|
|
||||||
|
<h3>End</h3>
|
||||||
|
<div hx-get="/more_divs" hx-swap="beforeend scroll:bottom" style="height: 100px; overflow: scroll">
|
||||||
|
Click To Add Content...
|
||||||
|
</div>
|
||||||
|
<hr/>
|
||||||
|
<h3>Start</h3>
|
||||||
|
<div hx-get="/more_divs" hx-swap="beforeend scroll:top" style="height: 100px; overflow: scroll">
|
||||||
|
Click To Add Content...
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
41
www/test/0.1.0/test/manual/sse-multichannel.html
Normal file
41
www/test/0.1.0/test/manual/sse-multichannel.html
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="../../src/htmx.js"></script>
|
||||||
|
<script>
|
||||||
|
htmx.createEventSource = function(url){
|
||||||
|
return new EventSource(url, {withCredentials:false})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
*{
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #eeeeee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 10px;
|
||||||
|
border: solid 1px gray;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
background-color:white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bold {
|
||||||
|
font-weight:bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="page" hx-sse="connect:http://sseplaceholder.openfollow.org/posts.html?types=Event1%2cEvent2%2cEvent3%2cEvent4">
|
||||||
|
<h3>Multiple Listeners. message only</h3>
|
||||||
|
<div class="container" hx-sse="swap:Event1">Waiting for Posts in Event1 channel...</div>
|
||||||
|
<div class="container" hx-sse="swap:Event2">Waiting for Posts in Event2 channel...</div>
|
||||||
|
<div class="container" hx-sse="swap:Event3">Waiting for Posts in Event3 channel...</div>
|
||||||
|
<div class="container" hx-sse="swap:Event4">Waiting for Posts in Event4 channel...</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
49
www/test/0.1.0/test/manual/sse-settle.html
Normal file
49
www/test/0.1.0/test/manual/sse-settle.html
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="../../src/htmx.js"></script>
|
||||||
|
<script>
|
||||||
|
htmx.createEventSource = function(url){
|
||||||
|
return new EventSource(url, {withCredentials:false})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
*{
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #eeeeee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 10px;
|
||||||
|
border: solid 1px gray;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
background-color:white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.htmx-settling {
|
||||||
|
border:solid 3px red!important;
|
||||||
|
padding:8px!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bold {
|
||||||
|
font-weight:bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="page">
|
||||||
|
<h3>Settling Options</h3>
|
||||||
|
<div hx-sse="connect:http://sseplaceholder.openfollow.org/comments.html">
|
||||||
|
<div class="container" hx-sse="swap:message" hx-swap="innerHTML settle:100ms">Waiting for Comments...</div>
|
||||||
|
<div class="container" hx-sse="swap:message" hx-swap="innerHTML settle:200ms">Waiting for Comments...</div>
|
||||||
|
<div class="container" hx-sse="swap:message" hx-swap="innerHTML settle:300ms">Waiting for Comments...</div>
|
||||||
|
<div class="container" hx-sse="swap:message" hx-swap="innerHTML settle:400ms">Waiting for Comments...</div>
|
||||||
|
<div class="container" hx-sse="swap:message" hx-swap="innerHTML settle:500ms">Waiting for Comments...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
43
www/test/0.1.0/test/manual/sse.html
Normal file
43
www/test/0.1.0/test/manual/sse.html
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="../../src/htmx.js"></script>
|
||||||
|
<script>
|
||||||
|
// "withCredentials:false" is necessary to circumvent CORS restrictions
|
||||||
|
htmx.createEventSource = function(url){
|
||||||
|
return new EventSource(url, {withCredentials:false})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
*{
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #eeeeee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 10px;
|
||||||
|
border: solid 1px gray;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
background-color:white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bold {
|
||||||
|
font-weight:bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="page">
|
||||||
|
<h3>Multiple Listeners. message only</h3>
|
||||||
|
<div class="container" hx-sse="connect:http://sseplaceholder.openfollow.org/posts.html swap:message">Waiting for Posts...</div>
|
||||||
|
<div class="container" hx-sse="connect:http://sseplaceholder.openfollow.org/comments.html swap:message">Waiting for Comments...</div>
|
||||||
|
<div class="container" hx-sse="connect:http://sseplaceholder.openfollow.org/albums.html swap:message">Waiting for Albums...</div>
|
||||||
|
<div class="container" hx-sse="connect:http://sseplaceholder.openfollow.org/todos.html swap:message">Waiting for ToDos...</div>
|
||||||
|
<div class="container" hx-sse="connect:http://sseplaceholder.openfollow.org/users.html swap:message">Waiting for Users...</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
x
Reference in New Issue
Block a user