mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-09-30 14:31:47 +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',
|
||||
settlingClass:'htmx-settling',
|
||||
swappingClass:'htmx-swapping',
|
||||
attributesToSwizzle:["class", "style", "width", "height"]
|
||||
},
|
||||
parseInterval:parseInterval,
|
||||
_:internalEval,
|
||||
@ -57,6 +58,8 @@ return (function () {
|
||||
return "[hx-" + verb + "], [data-hx-" + verb + "]"
|
||||
}).join(", ");
|
||||
|
||||
var windowIsScrolling = false // used by initScrollHandler
|
||||
|
||||
//====================================================================
|
||||
// Utilities
|
||||
//====================================================================
|
||||
@ -216,7 +219,7 @@ return (function () {
|
||||
}
|
||||
|
||||
function splitOnWhitespace(trigger) {
|
||||
return trigger.split(/\s+/);
|
||||
return trigger.trim().split(/\s+/);
|
||||
}
|
||||
|
||||
function mergeObjects(obj1, obj2) {
|
||||
@ -362,6 +365,8 @@ return (function () {
|
||||
return explicitTarget;
|
||||
} else if (targetStr.indexOf("closest ") === 0) {
|
||||
return closest(elt, targetStr.substr(8));
|
||||
} else if (targetStr.indexOf("find ") === 0) {
|
||||
return find(elt, targetStr.substr(5));
|
||||
} else {
|
||||
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) {
|
||||
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)
|
||||
}
|
||||
});
|
||||
forEach(mergeFrom.attributes, function (attr) {
|
||||
if (EXCLUDED_ATTRIBUTES.indexOf(attr.name) === -1) {
|
||||
if (shouldSettleAttribute(attr.name)) {
|
||||
mergeTo.setAttribute(attr.name, attr.value);
|
||||
}
|
||||
});
|
||||
@ -450,12 +464,21 @@ return (function () {
|
||||
|
||||
function makeAjaxLoadTask(child) {
|
||||
return function () {
|
||||
processNode(child, true);
|
||||
processNode(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) {
|
||||
handleAttributes(parentNode, fragment, settleInfo);
|
||||
while(fragment.childNodes.length > 0){
|
||||
@ -491,6 +514,7 @@ return (function () {
|
||||
} else {
|
||||
var newElt = eltBeforeNewContent.nextSibling;
|
||||
}
|
||||
getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later
|
||||
while(newElt && newElt !== target) {
|
||||
settleInfo.elts.push(newElt);
|
||||
newElt = newElt.nextSibling;
|
||||
@ -521,8 +545,10 @@ return (function () {
|
||||
insertNodesBefore(target, firstChild, fragment, settleInfo);
|
||||
if (firstChild) {
|
||||
while (firstChild.nextSibling) {
|
||||
closeConnections(firstChild.nextSibling)
|
||||
target.removeChild(firstChild.nextSibling);
|
||||
}
|
||||
closeConnections(firstChild)
|
||||
target.removeChild(firstChild);
|
||||
}
|
||||
}
|
||||
@ -760,12 +786,18 @@ return (function () {
|
||||
function initScrollHandler() {
|
||||
if (!window['htmxScrollHandler']) {
|
||||
var scrollHandler = function() {
|
||||
forEach(getDocument().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"), function (elt) {
|
||||
maybeReveal(elt);
|
||||
});
|
||||
windowIsScrolling = true
|
||||
};
|
||||
window['htmxScrollHandler'] = 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) {
|
||||
var values = info.split(",");
|
||||
var values = splitOnWhitespace(info);
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
var value = splitOnWhitespace(values[i]);
|
||||
var value = values[i].split(/:(.+)/);
|
||||
if (value[0] === "connect") {
|
||||
processWebSocketSource(elt, value[1]);
|
||||
}
|
||||
@ -791,6 +823,9 @@ return (function () {
|
||||
}
|
||||
|
||||
function processWebSocketSource(elt, wssSource) {
|
||||
if (wssSource.indexOf("ws:") !== 0 && wssSource.indexOf("wss:") !== 0) {
|
||||
wssSource = "wss:" + wssSource;
|
||||
}
|
||||
var socket = htmx.createWebSocket(wssSource);
|
||||
socket.onerror = function (e) {
|
||||
triggerErrorEvent(elt, "htmx:wsError", {error:e, socket:socket});
|
||||
@ -847,20 +882,21 @@ return (function () {
|
||||
}
|
||||
}
|
||||
|
||||
function maybeCloseSSESource(elt) {
|
||||
if (!bodyContains(elt)) {
|
||||
getInternalData(elt).sseEventSource.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//====================================================================
|
||||
// Server Sent Events
|
||||
//====================================================================
|
||||
|
||||
function processSSEInfo(elt, nodeData, info) {
|
||||
var values = info.split(",");
|
||||
var values = splitOnWhitespace(info);
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
var value = splitOnWhitespace(values[i]);
|
||||
var value = values[i].split(/:(.+)/);
|
||||
if (value[0] === "connect") {
|
||||
processSSESource(elt, value[1]);
|
||||
}
|
||||
|
||||
if ((value[0] === "swap")) {
|
||||
processSSESwap(elt, value[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -873,10 +909,41 @@ return (function () {
|
||||
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) {
|
||||
var sseSourceElt = getClosestMatch(elt, function (parent) {
|
||||
return getInternalData(parent).sseEventSource != null;
|
||||
});
|
||||
var sseSourceElt = getClosestMatch(elt, hasEventSource);
|
||||
if (sseSourceElt) {
|
||||
var sseEventSource = getInternalData(sseSourceElt).sseEventSource;
|
||||
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) {
|
||||
var load = function(){
|
||||
if (!nodeData.loaded) {
|
||||
@ -956,13 +1036,10 @@ return (function () {
|
||||
});
|
||||
}
|
||||
|
||||
function isHyperScriptAvailable() {
|
||||
return typeof _hyperscript !== "undefined";
|
||||
}
|
||||
|
||||
function findElementsToProcess(elt) {
|
||||
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;
|
||||
} else {
|
||||
return [];
|
||||
@ -974,10 +1051,6 @@ return (function () {
|
||||
if (!nodeData.initialized) {
|
||||
nodeData.initialized = true;
|
||||
|
||||
if (isHyperScriptAvailable()) {
|
||||
_hyperscript.init(elt);
|
||||
}
|
||||
|
||||
if (elt.value) {
|
||||
nodeData.lastValue = elt.value;
|
||||
}
|
||||
@ -1011,6 +1084,10 @@ return (function () {
|
||||
// Event/Log Support
|
||||
//====================================================================
|
||||
|
||||
function kebabEventName(str) {
|
||||
return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
||||
}
|
||||
|
||||
function makeEvent(eventName, detail) {
|
||||
var evt;
|
||||
if (window.CustomEvent && typeof window.CustomEvent === 'function') {
|
||||
@ -1062,6 +1139,10 @@ return (function () {
|
||||
triggerEvent(elt, "htmx:error", {errorInfo:detail})
|
||||
}
|
||||
var eventResult = elt.dispatchEvent(event);
|
||||
if (eventResult) {
|
||||
var kebabedEvent = makeEvent(kebabEventName(eventName), event.detail);
|
||||
eventResult = eventResult && elt.dispatchEvent(kebabedEvent)
|
||||
}
|
||||
withExtensions(elt, function (extension) {
|
||||
eventResult = eventResult && (extension.onEvent(eventName, event) !== false)
|
||||
});
|
||||
@ -1231,6 +1312,9 @@ return (function () {
|
||||
if (shouldInclude(elt)) {
|
||||
var name = getRawAttribute(elt,"name");
|
||||
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) {
|
||||
var current = values[name];
|
||||
if(current) {
|
||||
@ -1442,6 +1526,30 @@ return (function () {
|
||||
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) {
|
||||
var target = getTarget(elt);
|
||||
if (target == null) {
|
||||
@ -1535,7 +1643,8 @@ return (function () {
|
||||
// request headers
|
||||
for (var header in headers) {
|
||||
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) {
|
||||
cancelPolling(elt);
|
||||
}
|
||||
// don't process 'No Content' response
|
||||
// don't process 'No Content'
|
||||
if (this.status !== 204) {
|
||||
if (!triggerEvent(target, 'htmx:beforeSwap', eventDetail)) return;
|
||||
|
||||
@ -1613,7 +1722,7 @@ return (function () {
|
||||
});
|
||||
// push URL and save new page
|
||||
if (shouldSaveHistory) {
|
||||
var pathToPush = pushedUrl || getPushUrl(elt) || finalPathForGet || path;
|
||||
var pathToPush = pushedUrl || getPushUrl(elt) || getResponseURL(xhr) || finalPathForGet || path;
|
||||
pushUrlIntoHistory(pathToPush);
|
||||
triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path:pathToPush});
|
||||
}
|
||||
@ -1645,8 +1754,9 @@ return (function () {
|
||||
throw e;
|
||||
} finally {
|
||||
removeRequestIndicatorClasses(elt);
|
||||
triggerEvent(elt, 'htmx:afterRequest', eventDetail);
|
||||
triggerEvent(elt, 'htmx:afterOnLoad', eventDetail);
|
||||
var finalElt = getInternalData(elt).replacedWith || elt;
|
||||
triggerEvent(finalElt, 'htmx:afterRequest', eventDetail);
|
||||
triggerEvent(finalElt, 'htmx:afterOnLoad', eventDetail);
|
||||
endRequestLock();
|
||||
}
|
||||
}
|
||||
@ -1747,7 +1857,7 @@ return (function () {
|
||||
mergeMetaConfig();
|
||||
insertIndicatorStyles();
|
||||
var body = getDocument().body;
|
||||
processNode(body, true);
|
||||
processNode(body);
|
||||
triggerEvent(body, 'htmx:load', {});
|
||||
window.onpopstate = function (event) {
|
||||
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',
|
||||
settlingClass:'htmx-settling',
|
||||
swappingClass:'htmx-swapping',
|
||||
attributesToSwizzle:["class", "style", "width", "height"]
|
||||
},
|
||||
parseInterval:parseInterval,
|
||||
_:internalEval,
|
||||
@ -57,6 +58,8 @@ return (function () {
|
||||
return "[hx-" + verb + "], [data-hx-" + verb + "]"
|
||||
}).join(", ");
|
||||
|
||||
var windowIsScrolling = false // used by initScrollHandler
|
||||
|
||||
//====================================================================
|
||||
// Utilities
|
||||
//====================================================================
|
||||
@ -216,7 +219,7 @@ return (function () {
|
||||
}
|
||||
|
||||
function splitOnWhitespace(trigger) {
|
||||
return trigger.split(/\s+/);
|
||||
return trigger.trim().split(/\s+/);
|
||||
}
|
||||
|
||||
function mergeObjects(obj1, obj2) {
|
||||
@ -362,6 +365,8 @@ return (function () {
|
||||
return explicitTarget;
|
||||
} else if (targetStr.indexOf("closest ") === 0) {
|
||||
return closest(elt, targetStr.substr(8));
|
||||
} else if (targetStr.indexOf("find ") === 0) {
|
||||
return find(elt, targetStr.substr(5));
|
||||
} else {
|
||||
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) {
|
||||
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)
|
||||
}
|
||||
});
|
||||
forEach(mergeFrom.attributes, function (attr) {
|
||||
if (EXCLUDED_ATTRIBUTES.indexOf(attr.name) === -1) {
|
||||
if (shouldSettleAttribute(attr.name)) {
|
||||
mergeTo.setAttribute(attr.name, attr.value);
|
||||
}
|
||||
});
|
||||
@ -450,12 +464,21 @@ return (function () {
|
||||
|
||||
function makeAjaxLoadTask(child) {
|
||||
return function () {
|
||||
processNode(child, true);
|
||||
processNode(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) {
|
||||
handleAttributes(parentNode, fragment, settleInfo);
|
||||
while(fragment.childNodes.length > 0){
|
||||
@ -491,6 +514,7 @@ return (function () {
|
||||
} else {
|
||||
var newElt = eltBeforeNewContent.nextSibling;
|
||||
}
|
||||
getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later
|
||||
while(newElt && newElt !== target) {
|
||||
settleInfo.elts.push(newElt);
|
||||
newElt = newElt.nextSibling;
|
||||
@ -521,8 +545,10 @@ return (function () {
|
||||
insertNodesBefore(target, firstChild, fragment, settleInfo);
|
||||
if (firstChild) {
|
||||
while (firstChild.nextSibling) {
|
||||
closeConnections(firstChild.nextSibling)
|
||||
target.removeChild(firstChild.nextSibling);
|
||||
}
|
||||
closeConnections(firstChild)
|
||||
target.removeChild(firstChild);
|
||||
}
|
||||
}
|
||||
@ -760,12 +786,18 @@ return (function () {
|
||||
function initScrollHandler() {
|
||||
if (!window['htmxScrollHandler']) {
|
||||
var scrollHandler = function() {
|
||||
forEach(getDocument().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"), function (elt) {
|
||||
maybeReveal(elt);
|
||||
});
|
||||
windowIsScrolling = true
|
||||
};
|
||||
window['htmxScrollHandler'] = 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) {
|
||||
var values = info.split(",");
|
||||
var values = splitOnWhitespace(info);
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
var value = splitOnWhitespace(values[i]);
|
||||
var value = values[i].split(/:(.+)/);
|
||||
if (value[0] === "connect") {
|
||||
processWebSocketSource(elt, value[1]);
|
||||
}
|
||||
@ -791,6 +823,9 @@ return (function () {
|
||||
}
|
||||
|
||||
function processWebSocketSource(elt, wssSource) {
|
||||
if (wssSource.indexOf("ws:") !== 0 && wssSource.indexOf("wss:") !== 0) {
|
||||
wssSource = "wss:" + wssSource;
|
||||
}
|
||||
var socket = htmx.createWebSocket(wssSource);
|
||||
socket.onerror = function (e) {
|
||||
triggerErrorEvent(elt, "htmx:wsError", {error:e, socket:socket});
|
||||
@ -847,20 +882,21 @@ return (function () {
|
||||
}
|
||||
}
|
||||
|
||||
function maybeCloseSSESource(elt) {
|
||||
if (!bodyContains(elt)) {
|
||||
getInternalData(elt).sseEventSource.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//====================================================================
|
||||
// Server Sent Events
|
||||
//====================================================================
|
||||
|
||||
function processSSEInfo(elt, nodeData, info) {
|
||||
var values = info.split(",");
|
||||
var values = splitOnWhitespace(info);
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
var value = splitOnWhitespace(values[i]);
|
||||
var value = values[i].split(/:(.+)/);
|
||||
if (value[0] === "connect") {
|
||||
processSSESource(elt, value[1]);
|
||||
}
|
||||
|
||||
if ((value[0] === "swap")) {
|
||||
processSSESwap(elt, value[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -873,10 +909,41 @@ return (function () {
|
||||
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) {
|
||||
var sseSourceElt = getClosestMatch(elt, function (parent) {
|
||||
return getInternalData(parent).sseEventSource != null;
|
||||
});
|
||||
var sseSourceElt = getClosestMatch(elt, hasEventSource);
|
||||
if (sseSourceElt) {
|
||||
var sseEventSource = getInternalData(sseSourceElt).sseEventSource;
|
||||
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) {
|
||||
var load = function(){
|
||||
if (!nodeData.loaded) {
|
||||
@ -956,13 +1036,10 @@ return (function () {
|
||||
});
|
||||
}
|
||||
|
||||
function isHyperScriptAvailable() {
|
||||
return typeof _hyperscript !== "undefined";
|
||||
}
|
||||
|
||||
function findElementsToProcess(elt) {
|
||||
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;
|
||||
} else {
|
||||
return [];
|
||||
@ -974,10 +1051,6 @@ return (function () {
|
||||
if (!nodeData.initialized) {
|
||||
nodeData.initialized = true;
|
||||
|
||||
if (isHyperScriptAvailable()) {
|
||||
_hyperscript.init(elt);
|
||||
}
|
||||
|
||||
if (elt.value) {
|
||||
nodeData.lastValue = elt.value;
|
||||
}
|
||||
@ -1011,6 +1084,10 @@ return (function () {
|
||||
// Event/Log Support
|
||||
//====================================================================
|
||||
|
||||
function kebabEventName(str) {
|
||||
return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
||||
}
|
||||
|
||||
function makeEvent(eventName, detail) {
|
||||
var evt;
|
||||
if (window.CustomEvent && typeof window.CustomEvent === 'function') {
|
||||
@ -1062,6 +1139,10 @@ return (function () {
|
||||
triggerEvent(elt, "htmx:error", {errorInfo:detail})
|
||||
}
|
||||
var eventResult = elt.dispatchEvent(event);
|
||||
if (eventResult) {
|
||||
var kebabedEvent = makeEvent(kebabEventName(eventName), event.detail);
|
||||
eventResult = eventResult && elt.dispatchEvent(kebabedEvent)
|
||||
}
|
||||
withExtensions(elt, function (extension) {
|
||||
eventResult = eventResult && (extension.onEvent(eventName, event) !== false)
|
||||
});
|
||||
@ -1231,6 +1312,9 @@ return (function () {
|
||||
if (shouldInclude(elt)) {
|
||||
var name = getRawAttribute(elt,"name");
|
||||
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) {
|
||||
var current = values[name];
|
||||
if(current) {
|
||||
@ -1442,6 +1526,30 @@ return (function () {
|
||||
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) {
|
||||
var target = getTarget(elt);
|
||||
if (target == null) {
|
||||
@ -1535,7 +1643,8 @@ return (function () {
|
||||
// request headers
|
||||
for (var header in headers) {
|
||||
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) {
|
||||
cancelPolling(elt);
|
||||
}
|
||||
// don't process 'No Content' response
|
||||
// don't process 'No Content'
|
||||
if (this.status !== 204) {
|
||||
if (!triggerEvent(target, 'htmx:beforeSwap', eventDetail)) return;
|
||||
|
||||
@ -1613,7 +1722,7 @@ return (function () {
|
||||
});
|
||||
// push URL and save new page
|
||||
if (shouldSaveHistory) {
|
||||
var pathToPush = pushedUrl || getPushUrl(elt) || finalPathForGet || path;
|
||||
var pathToPush = pushedUrl || getPushUrl(elt) || getResponseURL(xhr) || finalPathForGet || path;
|
||||
pushUrlIntoHistory(pathToPush);
|
||||
triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path:pathToPush});
|
||||
}
|
||||
@ -1645,8 +1754,9 @@ return (function () {
|
||||
throw e;
|
||||
} finally {
|
||||
removeRequestIndicatorClasses(elt);
|
||||
triggerEvent(elt, 'htmx:afterRequest', eventDetail);
|
||||
triggerEvent(elt, 'htmx:afterOnLoad', eventDetail);
|
||||
var finalElt = getInternalData(elt).replacedWith || elt;
|
||||
triggerEvent(finalElt, 'htmx:afterRequest', eventDetail);
|
||||
triggerEvent(finalElt, 'htmx:afterOnLoad', eventDetail);
|
||||
endRequestLock();
|
||||
}
|
||||
}
|
||||
@ -1747,7 +1857,7 @@ return (function () {
|
||||
mergeMetaConfig();
|
||||
insertIndicatorStyles();
|
||||
var body = getDocument().body;
|
||||
processNode(body, true);
|
||||
processNode(body);
|
||||
triggerEvent(body, 'htmx:load', {});
|
||||
window.onpopstate = function (event) {
|
||||
if (event.state && event.state.htmx) {
|
||||
|
@ -41,6 +41,7 @@ return (function () {
|
||||
requestClass:'htmx-request',
|
||||
settlingClass:'htmx-settling',
|
||||
swappingClass:'htmx-swapping',
|
||||
attributesToSwizzle:["class", "style", "width", "height"]
|
||||
},
|
||||
parseInterval:parseInterval,
|
||||
_:internalEval,
|
||||
@ -57,6 +58,8 @@ return (function () {
|
||||
return "[hx-" + verb + "], [data-hx-" + verb + "]"
|
||||
}).join(", ");
|
||||
|
||||
var windowIsScrolling = false // used by initScrollHandler
|
||||
|
||||
//====================================================================
|
||||
// Utilities
|
||||
//====================================================================
|
||||
@ -216,7 +219,7 @@ return (function () {
|
||||
}
|
||||
|
||||
function splitOnWhitespace(trigger) {
|
||||
return trigger.split(/\s+/);
|
||||
return trigger.trim().split(/\s+/);
|
||||
}
|
||||
|
||||
function mergeObjects(obj1, obj2) {
|
||||
@ -362,6 +365,8 @@ return (function () {
|
||||
return explicitTarget;
|
||||
} else if (targetStr.indexOf("closest ") === 0) {
|
||||
return closest(elt, targetStr.substr(8));
|
||||
} else if (targetStr.indexOf("find ") === 0) {
|
||||
return find(elt, targetStr.substr(5));
|
||||
} else {
|
||||
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) {
|
||||
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)
|
||||
}
|
||||
});
|
||||
forEach(mergeFrom.attributes, function (attr) {
|
||||
if (EXCLUDED_ATTRIBUTES.indexOf(attr.name) === -1) {
|
||||
if (shouldSettleAttribute(attr.name)) {
|
||||
mergeTo.setAttribute(attr.name, attr.value);
|
||||
}
|
||||
});
|
||||
@ -450,12 +464,21 @@ return (function () {
|
||||
|
||||
function makeAjaxLoadTask(child) {
|
||||
return function () {
|
||||
processNode(child, true);
|
||||
processNode(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) {
|
||||
handleAttributes(parentNode, fragment, settleInfo);
|
||||
while(fragment.childNodes.length > 0){
|
||||
@ -491,6 +514,7 @@ return (function () {
|
||||
} else {
|
||||
var newElt = eltBeforeNewContent.nextSibling;
|
||||
}
|
||||
getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later
|
||||
while(newElt && newElt !== target) {
|
||||
settleInfo.elts.push(newElt);
|
||||
newElt = newElt.nextSibling;
|
||||
@ -521,8 +545,10 @@ return (function () {
|
||||
insertNodesBefore(target, firstChild, fragment, settleInfo);
|
||||
if (firstChild) {
|
||||
while (firstChild.nextSibling) {
|
||||
closeConnections(firstChild.nextSibling)
|
||||
target.removeChild(firstChild.nextSibling);
|
||||
}
|
||||
closeConnections(firstChild)
|
||||
target.removeChild(firstChild);
|
||||
}
|
||||
}
|
||||
@ -760,12 +786,18 @@ return (function () {
|
||||
function initScrollHandler() {
|
||||
if (!window['htmxScrollHandler']) {
|
||||
var scrollHandler = function() {
|
||||
forEach(getDocument().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"), function (elt) {
|
||||
maybeReveal(elt);
|
||||
});
|
||||
windowIsScrolling = true
|
||||
};
|
||||
window['htmxScrollHandler'] = 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) {
|
||||
var values = info.split(",");
|
||||
var values = splitOnWhitespace(info);
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
var value = splitOnWhitespace(values[i]);
|
||||
var value = values[i].split(/:(.+)/);
|
||||
if (value[0] === "connect") {
|
||||
processWebSocketSource(elt, value[1]);
|
||||
}
|
||||
@ -791,6 +823,9 @@ return (function () {
|
||||
}
|
||||
|
||||
function processWebSocketSource(elt, wssSource) {
|
||||
if (wssSource.indexOf("ws:") !== 0 && wssSource.indexOf("wss:") !== 0) {
|
||||
wssSource = "wss:" + wssSource;
|
||||
}
|
||||
var socket = htmx.createWebSocket(wssSource);
|
||||
socket.onerror = function (e) {
|
||||
triggerErrorEvent(elt, "htmx:wsError", {error:e, socket:socket});
|
||||
@ -847,20 +882,21 @@ return (function () {
|
||||
}
|
||||
}
|
||||
|
||||
function maybeCloseSSESource(elt) {
|
||||
if (!bodyContains(elt)) {
|
||||
getInternalData(elt).sseEventSource.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//====================================================================
|
||||
// Server Sent Events
|
||||
//====================================================================
|
||||
|
||||
function processSSEInfo(elt, nodeData, info) {
|
||||
var values = info.split(",");
|
||||
var values = splitOnWhitespace(info);
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
var value = splitOnWhitespace(values[i]);
|
||||
var value = values[i].split(/:(.+)/);
|
||||
if (value[0] === "connect") {
|
||||
processSSESource(elt, value[1]);
|
||||
}
|
||||
|
||||
if ((value[0] === "swap")) {
|
||||
processSSESwap(elt, value[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -873,10 +909,41 @@ return (function () {
|
||||
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) {
|
||||
var sseSourceElt = getClosestMatch(elt, function (parent) {
|
||||
return getInternalData(parent).sseEventSource != null;
|
||||
});
|
||||
var sseSourceElt = getClosestMatch(elt, hasEventSource);
|
||||
if (sseSourceElt) {
|
||||
var sseEventSource = getInternalData(sseSourceElt).sseEventSource;
|
||||
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) {
|
||||
var load = function(){
|
||||
if (!nodeData.loaded) {
|
||||
@ -956,13 +1036,10 @@ return (function () {
|
||||
});
|
||||
}
|
||||
|
||||
function isHyperScriptAvailable() {
|
||||
return typeof _hyperscript !== "undefined";
|
||||
}
|
||||
|
||||
function findElementsToProcess(elt) {
|
||||
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;
|
||||
} else {
|
||||
return [];
|
||||
@ -974,10 +1051,6 @@ return (function () {
|
||||
if (!nodeData.initialized) {
|
||||
nodeData.initialized = true;
|
||||
|
||||
if (isHyperScriptAvailable()) {
|
||||
_hyperscript.init(elt);
|
||||
}
|
||||
|
||||
if (elt.value) {
|
||||
nodeData.lastValue = elt.value;
|
||||
}
|
||||
@ -1011,6 +1084,10 @@ return (function () {
|
||||
// Event/Log Support
|
||||
//====================================================================
|
||||
|
||||
function kebabEventName(str) {
|
||||
return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
||||
}
|
||||
|
||||
function makeEvent(eventName, detail) {
|
||||
var evt;
|
||||
if (window.CustomEvent && typeof window.CustomEvent === 'function') {
|
||||
@ -1062,6 +1139,10 @@ return (function () {
|
||||
triggerEvent(elt, "htmx:error", {errorInfo:detail})
|
||||
}
|
||||
var eventResult = elt.dispatchEvent(event);
|
||||
if (eventResult) {
|
||||
var kebabedEvent = makeEvent(kebabEventName(eventName), event.detail);
|
||||
eventResult = eventResult && elt.dispatchEvent(kebabedEvent)
|
||||
}
|
||||
withExtensions(elt, function (extension) {
|
||||
eventResult = eventResult && (extension.onEvent(eventName, event) !== false)
|
||||
});
|
||||
@ -1231,6 +1312,9 @@ return (function () {
|
||||
if (shouldInclude(elt)) {
|
||||
var name = getRawAttribute(elt,"name");
|
||||
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) {
|
||||
var current = values[name];
|
||||
if(current) {
|
||||
@ -1442,6 +1526,30 @@ return (function () {
|
||||
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) {
|
||||
var target = getTarget(elt);
|
||||
if (target == null) {
|
||||
@ -1535,7 +1643,8 @@ return (function () {
|
||||
// request headers
|
||||
for (var header in headers) {
|
||||
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) {
|
||||
cancelPolling(elt);
|
||||
}
|
||||
// don't process 'No Content' response
|
||||
// don't process 'No Content'
|
||||
if (this.status !== 204) {
|
||||
if (!triggerEvent(target, 'htmx:beforeSwap', eventDetail)) return;
|
||||
|
||||
@ -1613,7 +1722,7 @@ return (function () {
|
||||
});
|
||||
// push URL and save new page
|
||||
if (shouldSaveHistory) {
|
||||
var pathToPush = pushedUrl || getPushUrl(elt) || finalPathForGet || path;
|
||||
var pathToPush = pushedUrl || getPushUrl(elt) || getResponseURL(xhr) || finalPathForGet || path;
|
||||
pushUrlIntoHistory(pathToPush);
|
||||
triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path:pathToPush});
|
||||
}
|
||||
@ -1645,8 +1754,9 @@ return (function () {
|
||||
throw e;
|
||||
} finally {
|
||||
removeRequestIndicatorClasses(elt);
|
||||
triggerEvent(elt, 'htmx:afterRequest', eventDetail);
|
||||
triggerEvent(elt, 'htmx:afterOnLoad', eventDetail);
|
||||
var finalElt = getInternalData(elt).replacedWith || elt;
|
||||
triggerEvent(finalElt, 'htmx:afterRequest', eventDetail);
|
||||
triggerEvent(finalElt, 'htmx:afterOnLoad', eventDetail);
|
||||
endRequestLock();
|
||||
}
|
||||
}
|
||||
@ -1747,7 +1857,7 @@ return (function () {
|
||||
mergeMetaConfig();
|
||||
insertIndicatorStyles();
|
||||
var body = getDocument().body;
|
||||
processNode(body, true);
|
||||
processNode(body);
|
||||
triggerEvent(body, 'htmx:load', {});
|
||||
window.onpopstate = function (event) {
|
||||
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", "/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="d2" hx-trigger="sse:e2" hx-get="/d2">div2</div>' +
|
||||
'</div>');
|
||||
@ -60,7 +60,7 @@ describe("hx-sse attribute", function() {
|
||||
|
||||
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>');
|
||||
|
||||
@ -81,7 +81,7 @@ describe("hx-sse attribute", function() {
|
||||
|
||||
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>');
|
||||
|
||||
this.eventSource.sendEvent("foo");
|
||||
@ -99,7 +99,7 @@ describe("hx-sse attribute", function() {
|
||||
|
||||
it('is closed after removal', function () {
|
||||
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>');
|
||||
div.click();
|
||||
@ -108,7 +108,7 @@ describe("hx-sse attribute", 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>');
|
||||
div.parentElement.removeChild(div);
|
||||
|
@ -47,6 +47,18 @@ describe("hx-target attribute", function(){
|
||||
this.server.respond();
|
||||
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()
|
||||
{
|
||||
|
@ -40,14 +40,14 @@ describe("hx-ws attribute", 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>")
|
||||
byId("d1").innerHTML.should.equal("replaced");
|
||||
byId("d2").innerHTML.should.equal("div2");
|
||||
})
|
||||
|
||||
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();
|
||||
var lastSent = this.socket.getLastSent();
|
||||
var data = JSON.parse(lastSent);
|
||||
@ -56,14 +56,14 @@ describe("hx-ws attribute", function() {
|
||||
|
||||
it('is closed after removal', function () {
|
||||
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();
|
||||
this.server.respond();
|
||||
this.socket.wasClosed().should.equal(true)
|
||||
})
|
||||
|
||||
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);
|
||||
this.socket.write("<div id=\"d1\">replaced</div>")
|
||||
this.socket.wasClosed().should.equal(true)
|
||||
|
@ -240,6 +240,36 @@ describe("Core htmx AJAX Tests", function(){
|
||||
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()
|
||||
{
|
||||
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)
|
||||
{
|
||||
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>");
|
||||
div.click();
|
||||
this.server.respond();
|
||||
should.equal(byId("d1").getAttribute("foo"), null);
|
||||
should.equal(byId("d1").getAttribute("width"), null);
|
||||
setTimeout(function () {
|
||||
should.equal(byId("d1").getAttribute("foo"), "bar");
|
||||
should.equal(byId("d1").getAttribute("width"), "bar");
|
||||
done();
|
||||
}, 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()
|
||||
{
|
||||
var values;
|
||||
@ -487,5 +549,41 @@ describe("Core htmx AJAX Tests", function(){
|
||||
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 () {
|
||||
var param = "foo";
|
||||
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 handler = htmx.on("htmx:sendError", function (evt) {
|
||||
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 {
|
||||
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();
|
||||
this.server.respond();
|
||||
should.equal(called, true);
|
||||
} 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() {
|
||||
beforeEach(function () {
|
||||
this.server = makeServer();
|
||||
clearWorkArea();
|
||||
});
|
||||
afterEach(function () {
|
||||
this.server.restore();
|
||||
clearWorkArea();
|
||||
});
|
||||
|
||||
it("makeFragment works with janky stuff", function(){
|
||||
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");
|
||||
})
|
||||
|
||||
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");
|
||||
});
|
||||
|
||||
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 () {
|
||||
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>')
|
||||
htmx.trigger(btn, "htmx:load"); // have to manually trigger the load event for non-AJAX dynamic content
|
||||
btn.click();
|
||||
this.server.respond();
|
||||
btn.innerHTML.should.equal("Custom Event Sent!");
|
||||
@ -19,6 +20,7 @@ describe("hyperscript integration", function() {
|
||||
it('can handle htmx driven events', function () {
|
||||
this.server.respondWith("GET", "/test", "Clicked!");
|
||||
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.click();
|
||||
this.server.respond();
|
||||
@ -29,9 +31,20 @@ describe("hyperscript integration", function() {
|
||||
this.server.respondWith("GET", "/test", [404, {}, "Bad request"]);
|
||||
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>')
|
||||
htmx.trigger(btn, "htmx:load");
|
||||
btn.click();
|
||||
this.server.respond();
|
||||
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>
|
||||
</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>
|
||||
</ul>
|
||||
|
||||
@ -89,9 +101,6 @@
|
||||
<!-- hyperscript integration -->
|
||||
<script src="lib/_hyperscript.js"></script>
|
||||
<script src="ext/hyperscript.js"></script>
|
||||
<script>
|
||||
_hyperscript.start();
|
||||
</script>
|
||||
|
||||
<!-- extension tests -->
|
||||
<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