summaryrefslogtreecommitdiffstatshomepage
path: root/dist
diff options
context:
space:
mode:
authorAlexander Petros <apetros15@gmail.com>2023-12-21 12:36:58 -0500
committerAlexander Petros <apetros15@gmail.com>2023-12-21 12:36:58 -0500
commite96037c4edfe331421d12e52f552d51fec533ad7 (patch)
treefa7edac7ab86a3dd9d57f9ff691393e9c2dd7b63 /dist
parent7e484f65a4c76bc454bbe3c0975866cbf6ab4b2b (diff)
downloadhtmx-e96037c4edfe331421d12e52f552d51fec533ad7.tar.gz
htmx-e96037c4edfe331421d12e52f552d51fec533ad7.zip
Remove extensions from dist
Diffstat (limited to 'dist')
-rw-r--r--dist/ext/ajax-header.js7
-rw-r--r--dist/ext/alpine-morph.js16
-rw-r--r--dist/ext/class-tools.js92
-rw-r--r--dist/ext/client-side-templates.js96
-rw-r--r--dist/ext/debug.js11
-rw-r--r--dist/ext/disable-element.js18
-rw-r--r--dist/ext/event-header.js37
-rw-r--r--dist/ext/head-support.js141
-rw-r--r--dist/ext/include-vals.js24
-rw-r--r--dist/ext/json-enc.js12
-rw-r--r--dist/ext/loading-states.js183
-rw-r--r--dist/ext/method-override.js11
-rw-r--r--dist/ext/morphdom-swap.js17
-rw-r--r--dist/ext/multi-swap.js45
-rw-r--r--dist/ext/path-deps.js60
-rw-r--r--dist/ext/preload.js147
-rw-r--r--dist/ext/rails-method.js10
-rw-r--r--dist/ext/remove-me.js27
-rw-r--r--dist/ext/response-targets.js130
-rw-r--r--dist/ext/restored.js15
-rw-r--r--dist/ext/sse.js322
-rw-r--r--dist/ext/ws.js477
-rw-r--r--dist/htmx.min.js.gzbin14459 -> 14459 bytes
23 files changed, 0 insertions, 1898 deletions
diff --git a/dist/ext/ajax-header.js b/dist/ext/ajax-header.js
deleted file mode 100644
index 5c6221bf..00000000
--- a/dist/ext/ajax-header.js
+++ /dev/null
@@ -1,7 +0,0 @@
-htmx.defineExtension('ajax-header', {
- onEvent: function (name, evt) {
- if (name === "htmx:configRequest") {
- evt.detail.headers['X-Requested-With'] = 'XMLHttpRequest';
- }
- }
-}); \ No newline at end of file
diff --git a/dist/ext/alpine-morph.js b/dist/ext/alpine-morph.js
deleted file mode 100644
index 1872daec..00000000
--- a/dist/ext/alpine-morph.js
+++ /dev/null
@@ -1,16 +0,0 @@
-htmx.defineExtension('alpine-morph', {
- isInlineSwap: function (swapStyle) {
- return swapStyle === 'morph';
- },
- handleSwap: function (swapStyle, target, fragment) {
- if (swapStyle === 'morph') {
- if (fragment.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
- Alpine.morph(target, fragment.firstElementChild);
- return [target];
- } else {
- Alpine.morph(target, fragment.outerHTML);
- return [target];
- }
- }
- }
-}); \ No newline at end of file
diff --git a/dist/ext/class-tools.js b/dist/ext/class-tools.js
deleted file mode 100644
index 1cf4b426..00000000
--- a/dist/ext/class-tools.js
+++ /dev/null
@@ -1,92 +0,0 @@
-(function () {
-
- function splitOnWhitespace(trigger) {
- return trigger.split(/\s+/);
- }
-
- function parseClassOperation(trimmedValue) {
- var split = splitOnWhitespace(trimmedValue);
- if (split.length > 1) {
- var operation = split[0];
- var classDef = split[1].trim();
- var cssClass;
- var delay;
- if (classDef.indexOf(":") > 0) {
- var splitCssClass = classDef.split(':');
- cssClass = splitCssClass[0];
- delay = htmx.parseInterval(splitCssClass[1]);
- } else {
- cssClass = classDef;
- delay = 100;
- }
- return {
- operation: operation,
- cssClass: cssClass,
- delay: delay
- }
- } else {
- return null;
- }
- }
-
- function performOperation(elt, classOperation, classList, currentRunTime) {
- setTimeout(function () {
- elt.classList[classOperation.operation].call(elt.classList, classOperation.cssClass);
- }, currentRunTime)
- }
-
- function toggleOperation(elt, classOperation, classList, currentRunTime) {
- setTimeout(function () {
- setInterval(function () {
- elt.classList[classOperation.operation].call(elt.classList, classOperation.cssClass);
- }, classOperation.delay);
- }, currentRunTime)
- }
-
- function processClassList(elt, classList) {
- var runs = classList.split("&");
- for (var i = 0; i < runs.length; i++) {
- var run = runs[i];
- var currentRunTime = 0;
- var classOperations = run.split(",");
- for (var j = 0; j < classOperations.length; j++) {
- var value = classOperations[j];
- var trimmedValue = value.trim();
- var classOperation = parseClassOperation(trimmedValue);
- if (classOperation) {
- if (classOperation.operation === "toggle") {
- toggleOperation(elt, classOperation, classList, currentRunTime);
- currentRunTime = currentRunTime + classOperation.delay;
- } else {
- currentRunTime = currentRunTime + classOperation.delay;
- performOperation(elt, classOperation, classList, currentRunTime);
- }
- }
- }
- }
- }
-
- function maybeProcessClasses(elt) {
- if (elt.getAttribute) {
- var classList = elt.getAttribute("classes") || elt.getAttribute("data-classes");
- if (classList) {
- processClassList(elt, classList);
- }
- }
- }
-
- htmx.defineExtension('class-tools', {
- onEvent: function (name, evt) {
- if (name === "htmx:afterProcessNode") {
- var elt = evt.detail.elt;
- maybeProcessClasses(elt);
- if (elt.querySelectorAll) {
- var children = elt.querySelectorAll("[classes], [data-classes]");
- for (var i = 0; i < children.length; i++) {
- maybeProcessClasses(children[i]);
- }
- }
- }
- }
- });
-})(); \ No newline at end of file
diff --git a/dist/ext/client-side-templates.js b/dist/ext/client-side-templates.js
deleted file mode 100644
index 2bace419..00000000
--- a/dist/ext/client-side-templates.js
+++ /dev/null
@@ -1,96 +0,0 @@
-htmx.defineExtension('client-side-templates', {
- transformResponse : function(text, xhr, elt) {
-
- var mustacheTemplate = htmx.closest(elt, "[mustache-template]");
- if (mustacheTemplate) {
- var data = JSON.parse(text);
- var templateId = mustacheTemplate.getAttribute('mustache-template');
- var template = htmx.find("#" + templateId);
- if (template) {
- return Mustache.render(template.innerHTML, data);
- } else {
- throw "Unknown mustache template: " + templateId;
- }
- }
-
- var mustacheArrayTemplate = htmx.closest(elt, "[mustache-array-template]");
- if (mustacheArrayTemplate) {
- var data = JSON.parse(text);
- var templateId = mustacheArrayTemplate.getAttribute('mustache-array-template');
- var template = htmx.find("#" + templateId);
- if (template) {
- return Mustache.render(template.innerHTML, {"data": data });
- } else {
- throw "Unknown mustache template: " + templateId;
- }
- }
-
- var handlebarsTemplate = htmx.closest(elt, "[handlebars-template]");
- if (handlebarsTemplate) {
- var data = JSON.parse(text);
- var templateId = handlebarsTemplate.getAttribute('handlebars-template');
- var templateElement = htmx.find('#' + templateId).innerHTML;
- var renderTemplate = Handlebars.compile(templateElement);
- if (renderTemplate) {
- return renderTemplate(data);
- } else {
- throw "Unknown handlebars template: " + templateId;
- }
- }
-
- var handlebarsArrayTemplate = htmx.closest(elt, "[handlebars-array-template]");
- if (handlebarsArrayTemplate) {
- var data = JSON.parse(text);
- var templateId = handlebarsArrayTemplate.getAttribute('handlebars-array-template');
- var templateElement = htmx.find('#' + templateId).innerHTML;
- var renderTemplate = Handlebars.compile(templateElement);
- if (renderTemplate) {
- return renderTemplate(data);
- } else {
- throw "Unknown handlebars template: " + templateId;
- }
- }
-
- var nunjucksTemplate = htmx.closest(elt, "[nunjucks-template]");
- if (nunjucksTemplate) {
- var data = JSON.parse(text);
- var templateName = nunjucksTemplate.getAttribute('nunjucks-template');
- var template = htmx.find('#' + templateName);
- if (template) {
- return nunjucks.renderString(template.innerHTML, data);
- } else {
- return nunjucks.render(templateName, data);
- }
- }
-
- var xsltTemplate = htmx.closest(elt, "[xslt-template]");
- if (xsltTemplate) {
- var templateId = xsltTemplate.getAttribute('xslt-template');
- var template = htmx.find("#" + templateId);
- if (template) {
- var content = template.innerHTML ? new DOMParser().parseFromString(template.innerHTML, 'application/xml')
- : template.contentDocument;
- var processor = new XSLTProcessor();
- processor.importStylesheet(content);
- var data = new DOMParser().parseFromString(text, "application/xml");
- var frag = processor.transformToFragment(data, document);
- return new XMLSerializer().serializeToString(frag);
- } else {
- throw "Unknown XSLT template: " + templateId;
- }
- }
-
- var nunjucksArrayTemplate = htmx.closest(elt, "[nunjucks-array-template]");
- if (nunjucksArrayTemplate) {
- var data = JSON.parse(text);
- var templateName = nunjucksArrayTemplate.getAttribute('nunjucks-array-template');
- var template = htmx.find('#' + templateName);
- if (template) {
- return nunjucks.renderString(template.innerHTML, {"data": data});
- } else {
- return nunjucks.render(templateName, {"data": data});
- }
- }
- return text;
- }
-});
diff --git a/dist/ext/debug.js b/dist/ext/debug.js
deleted file mode 100644
index 861ee743..00000000
--- a/dist/ext/debug.js
+++ /dev/null
@@ -1,11 +0,0 @@
-htmx.defineExtension('debug', {
- onEvent: function (name, evt) {
- if (console.debug) {
- console.debug(name, evt);
- } else if (console) {
- console.log("DEBUG:", name, evt);
- } else {
- throw "NO CONSOLE SUPPORTED"
- }
- }
-});
diff --git a/dist/ext/disable-element.js b/dist/ext/disable-element.js
deleted file mode 100644
index 07bef62d..00000000
--- a/dist/ext/disable-element.js
+++ /dev/null
@@ -1,18 +0,0 @@
-"use strict";
-
-// Disable Submit Button
-htmx.defineExtension('disable-element', {
- onEvent: function (name, evt) {
- let elt = evt.detail.elt;
- let target = elt.getAttribute("hx-disable-element");
- let targetElements = (target == "self") ? [ elt ] : document.querySelectorAll(target);
-
- for (var i = 0; i < targetElements.length; i++) {
- if (name === "htmx:beforeRequest" && targetElements[i]) {
- targetElements[i].disabled = true;
- } else if (name == "htmx:afterRequest" && targetElements[i]) {
- targetElements[i].disabled = false;
- }
- }
- }
-}); \ No newline at end of file
diff --git a/dist/ext/event-header.js b/dist/ext/event-header.js
deleted file mode 100644
index c7d29334..00000000
--- a/dist/ext/event-header.js
+++ /dev/null
@@ -1,37 +0,0 @@
-(function(){
- function stringifyEvent(event) {
- var obj = {};
- for (var key in event) {
- obj[key] = event[key];
- }
- return JSON.stringify(obj, function(key, value){
- if(value instanceof Node){
- var nodeRep = value.tagName;
- if (nodeRep) {
- nodeRep = nodeRep.toLowerCase();
- if(value.id){
- nodeRep += "#" + value.id;
- }
- if(value.classList && value.classList.length){
- nodeRep += "." + value.classList.toString().replace(" ", ".")
- }
- return nodeRep;
- } else {
- return "Node"
- }
- }
- if (value instanceof Window) return 'Window';
- return value;
- });
- }
-
- htmx.defineExtension('event-header', {
- onEvent: function (name, evt) {
- if (name === "htmx:configRequest") {
- if (evt.detail.triggeringEvent) {
- evt.detail.headers['Triggering-Event'] = stringifyEvent(evt.detail.triggeringEvent);
- }
- }
- }
- });
-})();
diff --git a/dist/ext/head-support.js b/dist/ext/head-support.js
deleted file mode 100644
index 67cfc692..00000000
--- a/dist/ext/head-support.js
+++ /dev/null
@@ -1,141 +0,0 @@
-//==========================================================
-// head-support.js
-//
-// An extension to htmx 1.0 to add head tag merging.
-//==========================================================
-(function(){
-
- var api = null;
-
- function log() {
- //console.log(arguments);
- }
-
- function mergeHead(newContent, defaultMergeStrategy) {
-
- if (newContent && newContent.indexOf('<head') > -1) {
- const htmlDoc = document.createElement("html");
- // remove svgs to avoid conflicts
- var contentWithSvgsRemoved = newContent.replace(/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim, '');
- // extract head tag
- var headTag = contentWithSvgsRemoved.match(/(<head(\s[^>]*>|>)([\s\S]*?)<\/head>)/im);
-
- // if the head tag exists...
- if (headTag) {
-
- var added = []
- var removed = []
- var preserved = []
- var nodesToAppend = []
-
- htmlDoc.innerHTML = headTag;
- var newHeadTag = htmlDoc.querySelector("head");
- var currentHead = document.head;
-
- if (newHeadTag == null) {
- return;
- } else {
- // put all new head elements into a Map, by their outerHTML
- var srcToNewHeadNodes = new Map();
- for (const newHeadChild of newHeadTag.children) {
- srcToNewHeadNodes.set(newHeadChild.outerHTML, newHeadChild);
- }
- }
-
-
-
- // determine merge strategy
- var mergeStrategy = api.getAttributeValue(newHeadTag, "hx-head") || defaultMergeStrategy;
-
- // get the current head
- for (const currentHeadElt of currentHead.children) {
-
- // If the current head element is in the map
- var inNewContent = srcToNewHeadNodes.has(currentHeadElt.outerHTML);
- var isReAppended = currentHeadElt.getAttribute("hx-head") === "re-eval";
- var isPreserved = api.getAttributeValue(currentHeadElt, "hx-preserve") === "true";
- if (inNewContent || isPreserved) {
- if (isReAppended) {
- // remove the current version and let the new version replace it and re-execute
- removed.push(currentHeadElt);
- } else {
- // this element already exists and should not be re-appended, so remove it from
- // the new content map, preserving it in the DOM
- srcToNewHeadNodes.delete(currentHeadElt.outerHTML);
- preserved.push(currentHeadElt);
- }
- } else {
- if (mergeStrategy === "append") {
- // we are appending and this existing element is not new content
- // so if and only if it is marked for re-append do we do anything
- if (isReAppended) {
- removed.push(currentHeadElt);
- nodesToAppend.push(currentHeadElt);
- }
- } else {
- // if this is a merge, we remove this content since it is not in the new head
- if (api.triggerEvent(document.body, "htmx:removingHeadElement", {headElement: currentHeadElt}) !== false) {
- removed.push(currentHeadElt);
- }
- }
- }
- }
-
- // Push the tremaining new head elements in the Map into the
- // nodes to append to the head tag
- nodesToAppend.push(...srcToNewHeadNodes.values());
- log("to append: ", nodesToAppend);
-
- for (const newNode of nodesToAppend) {
- log("adding: ", newNode);
- var newElt = document.createRange().createContextualFragment(newNode.outerHTML);
- log(newElt);
- if (api.triggerEvent(document.body, "htmx:addingHeadElement", {headElement: newElt}) !== false) {
- currentHead.appendChild(newElt);
- added.push(newElt);
- }
- }
-
- // remove all removed elements, after we have appended the new elements to avoid
- // additional network requests for things like style sheets
- for (const removedElement of removed) {
- if (api.triggerEvent(document.body, "htmx:removingHeadElement", {headElement: removedElement}) !== false) {
- currentHead.removeChild(removedElement);
- }
- }
-
- api.triggerEvent(document.body, "htmx:afterHeadMerge", {added: added, kept: preserved, removed: removed});
- }
- }
- }
-
- htmx.defineExtension("head-support", {
- init: function(apiRef) {
- // store a reference to the internal API.
- api = apiRef;
-
- htmx.on('htmx:afterSwap', function(evt){
- var serverResponse = evt.detail.xhr.response;
- if (api.triggerEvent(document.body, "htmx:beforeHeadMerge", evt.detail)) {
- mergeHead(serverResponse, evt.detail.boosted ? "merge" : "append");
- }
- })
-
- htmx.on('htmx:historyRestore', function(evt){
- if (api.triggerEvent(document.body, "htmx:beforeHeadMerge", evt.detail)) {
- if (evt.detail.cacheMiss) {
- mergeHead(evt.detail.serverResponse, "merge");
- } else {
- mergeHead(evt.detail.item.head, "merge");
- }
- }
- })
-
- htmx.on('htmx:historyItemCreated', function(evt){
- var historyItem = evt.detail.item;
- historyItem.head = document.head.outerHTML;
- })
- }
- });
-
-})() \ No newline at end of file
diff --git a/dist/ext/include-vals.js b/dist/ext/include-vals.js
deleted file mode 100644
index d8f5ce4f..00000000
--- a/dist/ext/include-vals.js
+++ /dev/null
@@ -1,24 +0,0 @@
-(function(){
-
- function mergeObjects(obj1, obj2) {
- for (var key in obj2) {
- if (obj2.hasOwnProperty(key)) {
- obj1[key] = obj2[key];
- }
- }
- return obj1;
- }
-
- htmx.defineExtension('include-vals', {
- onEvent: function (name, evt) {
- if (name === "htmx:configRequest") {
- var includeValsElt = htmx.closest(evt.detail.elt, "[include-vals],[data-include-vals]");
- if (includeValsElt) {
- var includeVals = includeValsElt.getAttribute("include-vals") || includeValsElt.getAttribute("data-include-vals");
- var valuesToInclude = eval("({" + includeVals + "})");
- mergeObjects(evt.detail.parameters, valuesToInclude);
- }
- }
- }
- });
-})();
diff --git a/dist/ext/json-enc.js b/dist/ext/json-enc.js
deleted file mode 100644
index db0aa36f..00000000
--- a/dist/ext/json-enc.js
+++ /dev/null
@@ -1,12 +0,0 @@
-htmx.defineExtension('json-enc', {
- onEvent: function (name, evt) {
- if (name === "htmx:configRequest") {
- evt.detail.headers['Content-Type'] = "application/json";
- }
- },
-
- encodeParameters : function(xhr, parameters, elt) {
- xhr.overrideMimeType('text/json');
- return (JSON.stringify(parameters));
- }
-}); \ No newline at end of file
diff --git a/dist/ext/loading-states.js b/dist/ext/loading-states.js
deleted file mode 100644
index c8ab51da..00000000
--- a/dist/ext/loading-states.js
+++ /dev/null
@@ -1,183 +0,0 @@
-;(function () {
- let loadingStatesUndoQueue = []
-
- function loadingStateContainer(target) {
- return htmx.closest(target, '[data-loading-states]') || document.body
- }
-
- function mayProcessUndoCallback(target, callback) {
- if (document.body.contains(target)) {
- callback()
- }
- }
-
- function mayProcessLoadingStateByPath(elt, requestPath) {
- const pathElt = htmx.closest(elt, '[data-loading-path]')
- if (!pathElt) {
- return true
- }
-
- return pathElt.getAttribute('data-loading-path') === requestPath
- }
-
- function queueLoadingState(sourceElt, targetElt, doCallback, undoCallback) {
- const delayElt = htmx.closest(sourceElt, '[data-loading-delay]')
- if (delayElt) {
- const delayInMilliseconds =
- delayElt.getAttribute('data-loading-delay') || 200
- const timeout = setTimeout(function () {
- doCallback()
-
- loadingStatesUndoQueue.push(function () {
- mayProcessUndoCallback(targetElt, undoCallback)
- })
- }, delayInMilliseconds)
-
- loadingStatesUndoQueue.push(function () {
- mayProcessUndoCallback(targetElt, function () { clearTimeout(timeout) })
- })
- } else {
- doCallback()
- loadingStatesUndoQueue.push(function () {
- mayProcessUndoCallback(targetElt, undoCallback)
- })
- }
- }
-
- function getLoadingStateElts(loadingScope, type, path) {
- return Array.from(htmx.findAll(loadingScope, "[" + type + "]")).filter(
- function (elt) { return mayProcessLoadingStateByPath(elt, path) }
- )
- }
-
- function getLoadingTarget(elt) {
- if (elt.getAttribute('data-loading-target')) {
- return Array.from(
- htmx.findAll(elt.getAttribute('data-loading-target'))
- )
- }
- return [elt]
- }
-
- htmx.defineExtension('loading-states', {
- onEvent: function (name, evt) {
- if (name === 'htmx:beforeRequest') {
- const container = loadingStateContainer(evt.target)
-
- const loadingStateTypes = [
- 'data-loading',
- 'data-loading-class',
- 'data-loading-class-remove',
- 'data-loading-disable',
- 'data-loading-aria-busy',
- ]
-
- let loadingStateEltsByType = {}
-
- loadingStateTypes.forEach(function (type) {
- loadingStateEltsByType[type] = getLoadingStateElts(
- container,
- type,
- evt.detail.pathInfo.requestPath
- )
- })
-
- loadingStateEltsByType['data-loading'].forEach(function (sourceElt) {
- getLoadingTarget(sourceElt).forEach(function (targetElt) {
- queueLoadingState(
- sourceElt,
- targetElt,
- function () {
- targetElt.style.display =
- sourceElt.getAttribute('data-loading') ||
- 'inline-block' },
- function () { targetElt.style.display = 'none' }
- )
- })
- })
-
- loadingStateEltsByType['data-loading-class'].forEach(
- function (sourceElt) {
- const classNames = sourceElt
- .getAttribute('data-loading-class')
- .split(' ')
-
- getLoadingTarget(sourceElt).forEach(function (targetElt) {
- queueLoadingState(
- sourceElt,
- targetElt,
- function () {
- classNames.forEach(function (className) {
- targetElt.classList.add(className)
- })
- },
- function() {
- classNames.forEach(function (className) {
- targetElt.classList.remove(className)
- })
- }
- )
- })
- }
- )
-
- loadingStateEltsByType['data-loading-class-remove'].forEach(
- function (sourceElt) {
- const classNames = sourceElt
- .getAttribute('data-loading-class-remove')
- .split(' ')
-
- getLoadingTarget(sourceElt).forEach(function (targetElt) {
- queueLoadingState(
- sourceElt,
- targetElt,
- function () {
- classNames.forEach(function (className) {
- targetElt.classList.remove(className)
- })
- },
- function() {
- classNames.forEach(function (className) {
- targetElt.classList.add(className)
- })
- }
- )
- })
- }
- )
-
- loadingStateEltsByType['data-loading-disable'].forEach(
- function (sourceElt) {
- getLoadingTarget(sourceElt).forEach(function (targetElt) {
- queueLoadingState(
- sourceElt,
- targetElt,
- function() { targetElt.disabled = true },
- function() { targetElt.disabled = false }
- )
- })
- }
- )
-
- loadingStateEltsByType['data-loading-aria-busy'].forEach(
- function (sourceElt) {
- getLoadingTarget(sourceElt).forEach(function (targetElt) {
- queueLoadingState(
- sourceElt,
- targetElt,
- function () { targetElt.setAttribute("aria-busy", "true") },
- function () { targetElt.removeAttribute("aria-busy") }
- )
- })
- }
- )
- }
-
- if (name === 'htmx:beforeOnLoad') {
- while (loadingStatesUndoQueue.length > 0) {
- loadingStatesUndoQueue.shift()()
- }
- }
- },
- })
-})()
diff --git a/dist/ext/method-override.js b/dist/ext/method-override.js
deleted file mode 100644
index 2e3504cb..00000000
--- a/dist/ext/method-override.js
+++ /dev/null
@@ -1,11 +0,0 @@
-htmx.defineExtension('method-override', {
- onEvent: function (name, evt) {
- if (name === "htmx:configRequest") {
- var method = evt.detail.verb;
- if (method !== "get" || method !== "post") {
- evt.detail.headers['X-HTTP-Method-Override'] = method.toUpperCase();
- evt.detail.verb = "post";
- }
- }
- }
-});
diff --git a/dist/ext/morphdom-swap.js b/dist/ext/morphdom-swap.js
deleted file mode 100644
index a1e53ce9..00000000
--- a/dist/ext/morphdom-swap.js
+++ /dev/null
@@ -1,17 +0,0 @@
-htmx.defineExtension('morphdom-swap', {
- isInlineSwap: function(swapStyle) {
- return swapStyle === 'morphdom';
- },
- handleSwap: function (swapStyle, target, fragment) {
- if (swapStyle === 'morphdom') {
- if (fragment.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
- // IE11 doesn't support DocumentFragment.firstElementChild
- morphdom(target, fragment.firstElementChild || fragment.firstChild);
- return [target];
- } else {
- morphdom(target, fragment.outerHTML);
- return [target];
- }
- }
- }
-});
diff --git a/dist/ext/multi-swap.js b/dist/ext/multi-swap.js
deleted file mode 100644
index f38f5b06..00000000
--- a/dist/ext/multi-swap.js
+++ /dev/null
@@ -1,45 +0,0 @@
-(function () {
-
- /** @type {import("../htmx").HtmxInternalApi} */
- var api;
-
- htmx.defineExtension('multi-swap', {
- init: function (apiRef) {
- api = apiRef;
- },
- isInlineSwap: function (swapStyle) {
- return swapStyle.indexOf('multi:') === 0;
- },
- handleSwap: function (swapStyle, target, fragment, settleInfo) {
- if (swapStyle.indexOf('multi:') === 0) {
- var selectorToSwapStyle = {};
- var elements = swapStyle.replace(/^multi\s*:\s*/, '').split(/\s*,\s*/);
-
- elements.map(function (element) {
- var split = element.split(/\s*:\s*/);
- var elementSelector = split[0];
- var elementSwapStyle = typeof (split[1]) !== "undefined" ? split[1] : "innerHTML";
-
- if (elementSelector.charAt(0) !== '#') {
- console.error("HTMX multi-swap: unsupported selector '" + elementSelector + "'. Only ID selectors starting with '#' are supported.");
- return;
- }
-
- selectorToSwapStyle[elementSelector] = elementSwapStyle;
- });
-
- for (var selector in selectorToSwapStyle) {
- var swapStyle = selectorToSwapStyle[selector];
- var elementToSwap = fragment.querySelector(selector);
- if (elementToSwap) {
- api.oobSwap(swapStyle, elementToSwap, settleInfo);
- } else {
- console.warn("HTMX multi-swap: selector '" + selector + "' not found in source content.");
- }
- }
-
- return true;
- }
- }
- });
-})(); \ No newline at end of file
diff --git a/dist/ext/path-deps.js b/dist/ext/path-deps.js
deleted file mode 100644
index 4987e50e..00000000
--- a/dist/ext/path-deps.js
+++ /dev/null
@@ -1,60 +0,0 @@
-(function(undefined){
- 'use strict';
-
- // Save a reference to the global object (window in the browser)
- var _root = this;
-
- function dependsOn(pathSpec, url) {
- if (pathSpec === "ignore") {
- return false;
- }
- var dependencyPath = pathSpec.split("/");
- var urlPath = url.split("/");
- for (var i = 0; i < urlPath.length; i++) {
- var dependencyElement = dependencyPath.shift();
- var pathElement = urlPath[i];
- if (dependencyElement !== pathElement && dependencyElement !== "*") {
- return false;
- }
- if (dependencyPath.length === 0 || (dependencyPath.length === 1 && dependencyPath[0] === "")) {
- return true;
- }
- }
- return false;
- }
-
- function refreshPath(path) {
- var eltsWithDeps = htmx.findAll("[path-deps]");
- for (var i = 0; i < eltsWithDeps.length; i++) {
- var elt = eltsWithDeps[i];
- if (dependsOn(elt.getAttribute('path-deps'), path)) {
- htmx.trigger(elt, "path-deps");
- }
- }
- }
-
- htmx.defineExtension('path-deps', {
- onEvent: function (name, evt) {
- if (name === "htmx:beforeOnLoad") {
- var config = evt.detail.requestConfig;
- // mutating call
- if (config.verb !== "get" && evt.target.getAttribute('path-deps') !== 'ignore') {
- refreshPath(config.path);
- }
- }
- }
- });
-
- /**
- * ********************
- * Expose functionality
- * ********************
- */
-
- _root.PathDeps = {
- refresh: function(path) {
- refreshPath(path);
- }
- };
-
-}).call(this);
diff --git a/dist/ext/preload.js b/dist/ext/preload.js
deleted file mode 100644
index a7493703..00000000
--- a/dist/ext/preload.js
+++ /dev/null
@@ -1,147 +0,0 @@
-// This adds the "preload" extension to htmx. By default, this will
-// preload the targets of any tags with `href` or `hx-get` attributes
-// if they also have a `preload` attribute as well. See documentation
-// for more details
-htmx.defineExtension("preload", {
-
- onEvent: function(name, event) {
-
- // Only take actions on "htmx:afterProcessNode"
- if (name !== "htmx:afterProcessNode") {
- return;
- }
-
- // SOME HELPER FUNCTIONS WE'LL NEED ALONG THE WAY
-
- // attr gets the closest non-empty value from the attribute.
- var attr = function(node, property) {
- if (node == undefined) {return undefined;}
- return node.getAttribute(property) || node.getAttribute("data-" + property) || attr(node.parentElement, property)
- }
-
- // load handles the actual HTTP fetch, and uses htmx.ajax in cases where we're
- // preloading an htmx resource (this sends the same HTTP headers as a regular htmx request)
- var load = function(node) {
-
- // Called after a successful AJAX request, to mark the
- // content as loaded (and prevent additional AJAX calls.)
- var done = function(html) {
- if (!node.preloadAlways) {
- node.preloadState = "DONE"
- }
-
- if (attr(node, "preload-images") == "true") {
- document.createElement("div").innerHTML = html // create and populate a node to load linked resources, too.
- }
- }
-
- return function() {
-
- // If this value has already been loaded, then do not try again.
- if (node.preloadState !== "READY") {
- return;
- }
-
- // Special handling for HX-GET - use built-in htmx.ajax function
- // so that headers match other htmx requests, then set
- // node.preloadState = TRUE so that requests are not duplicated
- // in the future
- var hxGet = node.getAttribute("hx-get") || node.getAttribute("data-hx-get")
- if (hxGet) {
- htmx.ajax("GET", hxGet, {
- source: node,
- handler:function(elt, info) {
- done(info.xhr.responseText);
- }
- });
- return;
- }
-
- // Otherwise, perform a standard xhr request, then set
- // node.preloadState = TRUE so that requests are not duplicated
- // in the future.
- if (node.getAttribute("href")) {
- var r = new XMLHttpRequest();
- r.open("GET", node.getAttribute("href"));
- r.onload = function() {done(r.responseText);};
- r.send();
- return;
- }
- }
- }
-
- // This function processes a specific node and sets up event handlers.
- // We'll search for nodes and use it below.
- var init = function(node) {
-
- // If this node DOES NOT include a "GET" transaction, then there's nothing to do here.
- if (node.getAttribute("href") + node.getAttribute("hx-get") + node.getAttribute("data-hx-get") == "") {
- return;
- }
-
- // Guarantee that we only initialize each node once.
- if (node.preloadState !== undefined) {
- return;
- }
-
- // Get event name from config.
- var on = attr(node, "preload") || "mousedown"
- const always = on.indexOf("always") !== -1
- if (always) {
- on = on.replace('always', '').trim()
- }
-
- // FALL THROUGH to here means we need to add an EventListener
-
- // Apply the listener to the node
- node.addEventListener(on, function(evt) {
- if (node.preloadState === "PAUSE") { // Only add one event listener
- node.preloadState = "READY"; // Required for the `load` function to trigger
-
- // Special handling for "mouseover" events. Wait 100ms before triggering load.
- if (on === "mouseover") {
- window.setTimeout(load(node), 100);
- } else {
- load(node)() // all other events trigger immediately.
- }
- }
- })
-
- // Special handling for certain built-in event handlers
- switch (on) {
-
- case "mouseover":
- // Mirror `touchstart` events (fires immediately)
- node.addEventListener("touchstart", load(node));
-
- // WHhen the mouse leaves, immediately disable the preload
- node.addEventListener("mouseout", function(evt) {
- if ((evt.target === node) && (node.preloadState === "READY")) {
- node.preloadState = "PAUSE";
- }
- })
- break;
-
- case "mousedown":
- // Mirror `touchstart` events (fires immediately)
- node.addEventListener("touchstart", load(node));
- break;
- }
-
- // Mark the node as ready to run.
- node.preloadState = "PAUSE";
- node.preloadAlways = always;
- htmx.trigger(node, "preload:init") // This event can be used to load content immediately.
- }
-
- // Search for all child nodes that have a "preload" attribute
- event.target.querySelectorAll("[preload]").forEach(function(node) {
-
- // Initialize the node with the "preload" attribute
- init(node)
-
- // Initialize all child elements that are anchors or have `hx-get` (use with care)
- node.querySelectorAll("a,[hx-get],[data-hx-get]").forEach(init)
- })
- }
-})
diff --git a/dist/ext/rails-method.js b/dist/ext/rails-method.js
deleted file mode 100644
index 5d48eccc..00000000
--- a/dist/ext/rails-method.js
+++ /dev/null
@@ -1,10 +0,0 @@
-htmx.defineExtension('rails-method', {
- onEvent: function (name, evt) {
- if (name === "configRequest.htmx") {
- var methodOverride = evt.detail.headers['X-HTTP-Method-Override'];
- if (methodOverride) {
- evt.detail.parameters['_method'] = methodOverride;
- }
- }
- }
-});
diff --git a/dist/ext/remove-me.js b/dist/ext/remove-me.js
deleted file mode 100644
index 42be9932..00000000
--- a/dist/ext/remove-me.js
+++ /dev/null
@@ -1,27 +0,0 @@
-(function(){
- function maybeRemoveMe(elt) {
- var timing = elt.getAttribute("remove-me") || elt.getAttribute("data-remove-me");
- if (timing) {
- setTimeout(function () {
- elt.parentElement.removeChild(elt);
- }, htmx.parseInterval(timing));
- }
- }
-
- htmx.defineExtension('remove-me', {
- onEvent: function (name, evt) {
- if (name === "htmx:afterProcessNode") {
- var elt = evt.detail.elt;
- if (elt.getAttribute) {
- maybeRemoveMe(elt);
- if (elt.querySelectorAll) {
- var children = elt.querySelectorAll("[remove-me], [data-remove-me]");
- for (var i = 0; i < children.length; i++) {
- maybeRemoveMe(children[i]);
- }
- }
- }
- }
- }
- });
-})();
diff --git a/dist/ext/response-targets.js b/dist/ext/response-targets.js
deleted file mode 100644
index dd6fd418..00000000
--- a/dist/ext/response-targets.js
+++ /dev/null
@@ -1,130 +0,0 @@
-(function(){
-
- /** @type {import("../htmx").HtmxInternalApi} */
- var api;
-
- var attrPrefix = 'hx-target-';
-
- // IE11 doesn't support string.startsWith
- function startsWith(str, prefix) {
- return str.substring(0, prefix.length) === prefix
- }
-
- /**
- * @param {HTMLElement} elt
- * @param {number} respCode
- * @returns {HTMLElement | null}
- */
- function getRespCodeTarget(elt, respCodeNumber) {
- if (!elt || !respCodeNumber) return null;
-
- var respCode = respCodeNumber.toString();
-
- // '*' is the original syntax, as the obvious character for a wildcard.
- // The 'x' alternative was added for maximum compatibility with HTML
- // templating engines, due to ambiguity around which characters are
- // supported in HTML attributes.
- //
- // Start with the most specific possible attribute and generalize from
- // there.
- var attrPossibilities = [
- respCode,
-
- respCode.substr(0, 2) + '*',
- respCode.substr(0, 2) + 'x',
-
- respCode.substr(0, 1) + '*',
- respCode.substr(0, 1) + 'x',
- respCode.substr(0, 1) + '**',
- respCode.substr(0, 1) + 'xx',
-
- '*',
- 'x',
- '***',
- 'xxx',
- ];
- if (startsWith(respCode, '4') || startsWith(respCode, '5')) {
- attrPossibilities.push('error');
- }
-
- for (var i = 0; i < attrPossibilities.length; i++) {
- var attr = attrPrefix + attrPossibilities[i];
- var attrValue = api.getClosestAttributeValue(elt, attr);
- if (attrValue) {
- if (attrValue === "this") {
- return api.findThisElement(elt, attr);
- } else {
- return api.querySelectorExt(elt, attrValue);
- }
- }
- }
-
- return null;
- }
-
- /** @param {Event} evt */
- function handleErrorFlag(evt) {
- if (evt.detail.isError) {
- if (htmx.config.responseTargetUnsetsError) {
- evt.detail.isError = false;
- }
- } else if (htmx.config.responseTargetSetsError) {
- evt.detail.isError = true;
- }
- }
-
- htmx.defineExtension('response-targets', {
-
- /** @param {import("../htmx").HtmxInternalApi} apiRef */
- init: function (apiRef) {
- api = apiRef;
-
- if (htmx.config.responseTargetUnsetsError === undefined) {
- htmx.config.responseTargetUnsetsError = true;
- }
- if (htmx.config.responseTargetSetsError === undefined) {
- htmx.config.responseTargetSetsError = false;
- }
- if (htmx.config.responseTargetPrefersExisting === undefined) {
- htmx.config.responseTargetPrefersExisting = false;
- }
- if (htmx.config.responseTargetPrefersRetargetHeader === undefined) {
- htmx.config.responseTargetPrefersRetargetHeader = true;
- }
- },
-
- /**
- * @param {string} name
- * @param {Event} evt
- */
- onEvent: function (name, evt) {
- if (name === "htmx:beforeSwap" &&
- evt.detail.xhr &&
- evt.detail.xhr.status !== 200) {
- if (evt.detail.target) {
- if (htmx.config.responseTargetPrefersExisting) {
- evt.detail.shouldSwap = true;
- handleErrorFlag(evt);
- return true;
- }
- if (htmx.config.responseTargetPrefersRetargetHeader &&
- evt.detail.xhr.getAllResponseHeaders().match(/HX-Retarget:/i)) {
- evt.detail.shouldSwap = true;
- handleErrorFlag(evt);
- return true;
- }
- }
- if (!evt.detail.requestConfig) {
- return true;
- }
- var target = getRespCodeTarget(evt.detail.requestConfig.elt, evt.detail.xhr.status);
- if (target) {
- handleErrorFlag(evt);
- evt.detail.shouldSwap = true;
- evt.detail.target = target;
- }
- return true;
- }
- }
- });
-})();
diff --git a/dist/ext/restored.js b/dist/ext/restored.js
deleted file mode 100644
index 6f65267c..00000000
--- a/dist/ext/restored.js
+++ /dev/null
@@ -1,15 +0,0 @@
-htmx.defineExtension('restored', {
- onEvent : function(name, evt) {
- if (name === 'htmx:restored'){
- var restoredElts = evt.detail.document.querySelectorAll(
- "[hx-trigger='restored'],[data-hx-trigger='restored']"
- );
- // need a better way to do this, would prefer to just trigger from evt.detail.elt
- var foundElt = Array.from(restoredElts).find(
- (x) => (x.outerHTML === evt.detail.elt.outerHTML)
- );
- var restoredEvent = evt.detail.triggerEvent(foundElt, 'restored');
- }
- return;
- }
-}) \ No newline at end of file
diff --git a/dist/ext/sse.js b/dist/ext/sse.js
deleted file mode 100644
index 75c875a1..00000000
--- a/dist/ext/sse.js
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
-Server Sent Events Extension
-============================
-This extension adds support for Server Sent Events to htmx. See /www/extensions/sse.md for usage instructions.
-
-*/
-
-(function(){
-
- /** @type {import("../htmx").HtmxInternalApi} */
- var api;
-
- htmx.defineExtension("sse", {
-
- /**
- * Init saves the provided reference to the internal HTMX API.
- *
- * @param {import("../htmx").HtmxInternalApi} api
- * @returns void
- */
- init: function(apiRef) {
- // store a reference to the internal API.
- api = apiRef;
-
- // set a function in the public API for creating new EventSource objects
- if (htmx.createEventSource == undefined) {
- htmx.createEventSource = createEventSource;
- }
- },
-
- /**
- * onEvent handles all events passed to this extension.
- *
- * @param {string} name
- * @param {Event} evt
- * @returns void
- */
- onEvent: function(name, evt) {
-
- switch (name) {
-
- // Try to remove remove an EventSource when elements are removed
- case "htmx:beforeCleanupElement":
- var internalData = api.getInternalData(evt.target)
- if (internalData.sseEventSource) {
- internalData.sseEventSource.close();
- }
- return;
-
- // Try to create EventSources when elements are processed
- case "htmx:afterProcessNode":
- createEventSourceOnElement(evt.target);
- }
- }
- });
-
- ///////////////////////////////////////////////
- // HELPER FUNCTIONS
- ///////////////////////////////////////////////
-
-
- /**
- * createEventSource is the default method for creating new EventSource objects.
- * it is hoisted into htmx.config.createEventSource to be overridden by the user, if needed.
- *
- * @param {string} url
- * @returns EventSource
- */
- function createEventSource(url) {
- return new EventSource(url, {withCredentials:true});
- }
-
- function splitOnWhitespace(trigger) {
- return trigger.trim().split(/\s+/);
- }
-
- function getLegacySSEURL(elt) {
- var legacySSEValue = api.getAttributeValue(elt, "hx-sse");
- if (legacySSEValue) {
- var values = splitOnWhitespace(legacySSEValue);
- for (var i = 0; i < values.length; i++) {
- var value = values[i].split(/:(.+)/);
- if (value[0] === "connect") {
- return value[1];
- }
- }
- }
- }
-
- function getLegacySSESwaps(elt) {
- var legacySSEValue = api.getAttributeValue(elt, "hx-sse");
- var returnArr = [];
- if (legacySSEValue) {
- var values = splitOnWhitespace(legacySSEValue);
- for (var i = 0; i < values.length; i++) {
- var value = values[i].split(/:(.+)/);
- if (value[0] === "swap") {
- returnArr.push(value[1]);
- }
- }
- }
- return returnArr;
- }
-
- /**
- * createEventSourceOnElement creates a new EventSource connection on the provided element.
- * If a usable EventSource already exists, then it is returned. If not, then a new EventSource
- * is created and stored in the element's internalData.
- * @param {HTMLElement} elt
- * @param {number} retryCount
- * @returns {EventSource | null}
- */
- function createEventSourceOnElement(elt, retryCount) {
-
- if (elt == null) {
- return null;
- }
-
- var internalData = api.getInternalData(elt);
-
- // get URL from element's attribute
- var sseURL = api.getAttributeValue(elt, "sse-connect");
-
-
- if (sseURL == undefined) {
- var legacyURL = getLegacySSEURL(elt)
- if (legacyURL) {
- sseURL = legacyURL;
- } else {
- return null;
- }
- }
-
- // Connect to the EventSource
- var source = htmx.createEventSource(sseURL);
- internalData.sseEventSource = source;
-
- // Create event handlers
- source.onerror = function (err) {
-
- // Log an error event
- api.triggerErrorEvent(elt, "htmx:sseError", {error:err, source:source});
-
- // If parent no longer exists in the document, then clean up this EventSource
- if (maybeCloseSSESource(elt)) {
- return;
- }
-
- // Otherwise, try to reconnect the EventSource
- if (source.readyState === EventSource.CLOSED) {
- retryCount = retryCount || 0;
- var timeout = Math.random() * (2 ^ retryCount) * 500;
- window.setTimeout(function() {
- createEventSourceOnElement(elt, Math.min(7, retryCount+1));
- }, timeout);
- }
- };
-
- source.onopen = function (evt) {
- api.triggerEvent(elt, "htmx:sseOpen", {source: source});
- }
-
- // Add message handlers for every `sse-swap` attribute
- queryAttributeOnThisOrChildren(elt, "sse-swap").forEach(function(child) {
-
- var sseSwapAttr = api.getAttributeValue(child, "sse-swap");
- if (sseSwapAttr) {
- var sseEventNames = sseSwapAttr.split(",");
- } else {
- var sseEventNames = getLegacySSESwaps(child);
- }
-
- for (var i = 0 ; i < sseEventNames.length ; i++) {
- var sseEventName = sseEventNames[i].trim();
- var listener = function(event) {
-
- // If the parent is missing then close SSE and remove listener
- if (maybeCloseSSESource(elt)) {
- source.removeEventListener(sseEventName, listener);
- return;
- }
-
- // swap the response into the DOM and trigger a notification
- swap(child, event.data);
- api.triggerEvent(elt, "htmx:sseMessage", event);
- };
-
- // Register the new listener
- api.getInternalData(elt).sseEventListener = listener;
- source.addEventListener(sseEventName, listener);
- }
- });
-
- // Add message handlers for every `hx-trigger="sse:*"` attribute
- queryAttributeOnThisOrChildren(elt, "hx-trigger").forEach(function(child) {
-
- var sseEventName = api.getAttributeValue(child, "hx-trigger");
- if (sseEventName == null) {
- return;
- }
-
- // Only process hx-triggers for events with the "sse:" prefix
- if (sseEventName.slice(0, 4) != "sse:") {
- return;
- }
-
- var listener = function(event) {
-
- // If parent is missing, then close SSE and remove listener
- if (maybeCloseSSESource(elt)) {
- source.removeEventListener(sseEventName, listener);
- return;
- }
-
- // Trigger events to be handled by the rest of htmx
- htmx.trigger(child, sseEventName, event);
- htmx.trigger(child, "htmx:sseMessage", event);
- }
-
- // Register the new listener
- api.getInternalData(elt).sseEventListener = listener;
- source.addEventListener(sseEventName.slice(4), listener);
- });
- }
-
- /**
- * maybeCloseSSESource confirms that the parent element still exists.
- * If not, then any associated SSE source is closed and the function returns true.
- *
- * @param {HTMLElement} elt
- * @returns boolean
- */
- function maybeCloseSSESource(elt) {
- if (!api.bodyContains(elt)) {
- var source = api.getInternalData(elt).sseEventSource;
- if (source != undefined) {
- source.close();
- // source = null
- return true;
- }
- }
- return false;
- }
-
- /**
- * queryAttributeOnThisOrChildren returns all nodes that contain the requested attributeName, INCLUDING THE PROVIDED ROOT ELEMENT.
- *
- * @param {HTMLElement} elt
- * @param {string} attributeName
- */
- function queryAttributeOnThisOrChildren(elt, attributeName) {
-
- var result = [];
-
- // If the parent element also contains the requested attribute, then add it to the results too.
- if (api.hasAttribute(elt, attributeName) || api.hasAttribute(elt, "hx-sse")) {
- result.push(elt);
- }
-
- // Search all child nodes that match the requested attribute
- elt.querySelectorAll("[" + attributeName + "], [data-" + attributeName + "], [hx-sse], [data-hx-sse]").forEach(function(node) {
- result.push(node);
- });
-
- return result;
- }
-
- /**
- * @param {HTMLElement} elt
- * @param {string} content
- */
- function swap(elt, content) {
-
- api.withExtensions(elt, function(extension) {
- content = extension.transformResponse(content, null, elt);
- });
-
- var swapSpec = api.getSwapSpecification(elt);
- var target = api.getTarget(elt);
- var settleInfo = api.makeSettleInfo(elt);
-
- api.selectAndSwap(swapSpec.swapStyle, target, elt, content, settleInfo);
-
- settleInfo.elts.forEach(function (elt) {
- if (elt.classList) {
- elt.classList.add(htmx.config.settlingClass);
- }
- api.triggerEvent(elt, 'htmx:beforeSettle');
- });
-
- // Handle settle tasks (with delay if requested)
- if (swapSpec.settleDelay > 0) {
- setTimeout(doSettle(settleInfo), swapSpec.settleDelay);
- } else {
- doSettle(settleInfo)();
- }
- }
-
- /**
- * doSettle mirrors much of the functionality in htmx that
- * settles elements after their content has been swapped.
- * TODO: this should be published by htmx, and not duplicated here
- * @param {import("../htmx").HtmxSettleInfo} settleInfo
- * @returns () => void
- */
- function doSettle(settleInfo) {
-
- return function() {
- settleInfo.tasks.forEach(function (task) {
- task.call();
- });
-
- settleInfo.elts.forEach(function (elt) {
- if (elt.classList) {
- elt.classList.remove(htmx.config.settlingClass);
- }
- api.triggerEvent(elt, 'htmx:afterSettle');
- });
- }
- }
-
-})(); \ No newline at end of file
diff --git a/dist/ext/ws.js b/dist/ext/ws.js
deleted file mode 100644
index 05be1eca..00000000
--- a/dist/ext/ws.js
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
-WebSockets Extension
-============================
-This extension adds support for WebSockets to htmx. See /www/extensions/ws.md for usage instructions.
-*/
-
-(function () {
-
- /** @type {import("../htmx").HtmxInternalApi} */
- var api;
-
- htmx.defineExtension("ws", {
-
- /**
- * init is called once, when this extension is first registered.
- * @param {import("../htmx").HtmxInternalApi} apiRef
- */
- init: function (apiRef) {
-
- // Store reference to internal API
- api = apiRef;
-
- // Default function for creating new EventSource objects
- if (!htmx.createWebSocket) {
- htmx.createWebSocket = createWebSocket;
- }
-
- // Default setting for reconnect delay
- if (!htmx.config.wsReconnectDelay) {
- htmx.config.wsReconnectDelay = "full-jitter";
- }
- },
-
- /**
- * onEvent handles all events passed to this extension.
- *
- * @param {string} name
- * @param {Event} evt
- */
- onEvent: function (name, evt) {
-
- switch (name) {
-
- // Try to close the socket when elements are removed
- case "htmx:beforeCleanupElement":
-
- var internalData = api.getInternalData(evt.target)
-
- if (internalData.webSocket) {
- internalData.webSocket.close();
- }
- return;
-
- // Try to create websockets when elements are processed
- case "htmx:beforeProcessNode":
- var parent = evt.target;
-
- forEach(queryAttributeOnThisOrChildren(parent, "ws-connect"), function (child) {
- ensureWebSocket(child)
- });
- forEach(queryAttributeOnThisOrChildren(parent, "ws-send"), function (child) {
- ensureWebSocketSend(child)
- });
- }
- }
- });
-
- function splitOnWhitespace(trigger) {
- return trigger.trim().split(/\s+/);
- }
-
- function getLegacyWebsocketURL(elt) {
- var legacySSEValue = api.getAttributeValue(elt, "hx-ws");
- if (legacySSEValue) {
- var values = splitOnWhitespace(legacySSEValue);
- for (var i = 0; i < values.length; i++) {
- var value = values[i].split(/:(.+)/);
- if (value[0] === "connect") {
- return value[1];
- }
- }
- }
- }
-
- /**
- * ensureWebSocket creates a new WebSocket on the designated element, using
- * the element's "ws-connect" attribute.
- * @param {HTMLElement} socketElt
- * @returns
- */
- function ensureWebSocket(socketElt) {
-
- // If the element containing the WebSocket connection no longer exists, then
- // do not connect/reconnect the WebSocket.
- if (!api.bodyContains(socketElt)) {
- return;
- }
-
- // Get the source straight from the element's value
- var wssSource = api.getAttributeValue(socketElt, "ws-connect")
-
- if (wssSource == null || wssSource === "") {
- var legacySource = getLegacyWebsocketURL(socketElt);
- if (legacySource == null) {
- return;
- } else {
- wssSource = legacySource;
- }
- }
-
- // Guarantee that the wssSource value is a fully qualified URL
- if (wssSource.indexOf("/") === 0) {
- var base_part = location.hostname + (location.port ? ':' + location.port : '');
- if (location.protocol === 'https:') {
- wssSource = "wss://" + base_part + wssSource;
- } else if (location.protocol === 'http:') {
- wssSource = "ws://" + base_part + wssSource;
- }
- }
-
- var socketWrapper = createWebsocketWrapper(socketElt, function () {
- return htmx.createWebSocket(wssSource)
- });
-
- socketWrapper.addEventListener('message', function (event) {
- if (maybeCloseWebSocketSource(socketElt)) {
- return;
- }
-
- var response = event.data;
- if (!api.triggerEvent(socketElt, "htmx:wsBeforeMessage", {
- message: response,
- socketWrapper: socketWrapper.publicInterface
- })) {
- return;
- }
-
- api.withExtensions(socketElt, function (extension) {
- response = extension.transformResponse(response, null, socketElt);
- });
-
- var settleInfo = api.makeSettleInfo(socketElt);
- var fragment = api.makeFragment(response);
-
- if (fragment.children.length) {
- var children = Array.from(fragment.children);
- for (var i = 0; i < children.length; i++) {
- api.oobSwap(api.getAttributeValue(children[i], "hx-swap-oob") || "true", children[i], settleInfo);
- }
- }
-
- api.settleImmediately(settleInfo.tasks);
- api.triggerEvent(socketElt, "htmx:wsAfterMessage", { message: response, socketWrapper: socketWrapper.publicInterface })
- });
-
- // Put the WebSocket into the HTML Element's custom data.
- api.getInternalData(socketElt).webSocket = socketWrapper;
- }
-
- /**
- * @typedef {Object} WebSocketWrapper
- * @property {WebSocket} socket
- * @property {Array<{message: string, sendElt: Element}>} messageQueue
- * @property {number} retryCount
- * @property {(message: string, sendElt: Element) => void} sendImmediately sendImmediately sends message regardless of websocket connection state
- * @property {(message: string, sendElt: Element) => void} send
- * @property {(event: string, handler: Function) => void} addEventListener
- * @property {() => void} handleQueuedMessages
- * @property {() => void} init
- * @property {() => void} close
- */
- /**
- *
- * @param socketElt
- * @param socketFunc
- * @returns {WebSocketWrapper}
- */
- function createWebsocketWrapper(socketElt, socketFunc) {
- var wrapper = {
- socket: null,
- messageQueue: [],
- retryCount: 0,
-
- /** @type {Object<string, Function[]>} */
- events: {},
-
- addEventListener: function (event, handler) {
- if (this.socket) {
- this.socket.addEventListener(event, handler);
- }
-
- if (!this.events[event]) {
- this.events[event] = [];
- }
-
- this.events[event].push(handler);
- },
-
- sendImmediately: function (message, sendElt) {
- if (!this.socket) {
- api.triggerErrorEvent()
- }
- if (!sendElt || api.triggerEvent(sendElt, 'htmx:wsBeforeSend', {
- message: message,
- socketWrapper: this.publicInterface
- })) {
- this.socket.send(message);
- sendElt && api.triggerEvent(sendElt, 'htmx:wsAfterSend', {
- message: message,
- socketWrapper: this.publicInterface
- })
- }
- },
-
- send: function (message, sendElt) {
- if (this.socket.readyState !== this.socket.OPEN) {
- this.messageQueue.push({ message: message, sendElt: sendElt });
- } else {
- this.sendImmediately(message, sendElt);
- }
- },
-
- handleQueuedMessages: function () {
- while (this.messageQueue.length > 0) {
- var queuedItem = this.messageQueue[0]
- if (this.socket.readyState === this.socket.OPEN) {
- this.sendImmediately(queuedItem.message, queuedItem.sendElt);
- this.messageQueue.shift();
- } else {
- break;
- }
- }
- },
-
- init: function () {
- if (this.socket && this.socket.readyState === this.socket.OPEN) {
- // Close discarded socket
- this.socket.close()
- }
-
- // Create a new WebSocket and event handlers
- /** @type {WebSocket} */
- var socket = socketFunc();
-
- // The event.type detail is added for interface conformance with the
- // other two lifecycle events (open and close) so a single handler method
- // can handle them polymorphically, if required.
- api.triggerEvent(socketElt, "htmx:wsConnecting", { event: { type: 'connecting' } });
-
- this.socket = socket;
-
- socket.onopen = function (e) {
- wrapper.retryCount = 0;
- api.triggerEvent(socketElt, "htmx:wsOpen", { event: e, socketWrapper: wrapper.publicInterface });
- wrapper.handleQueuedMessages();
- }
-
- socket.onclose = function (e) {
- // If socket should not be connected, stop further attempts to establish connection
- // If Abnormal Closure/Service Restart/Try Again Later, then set a timer to reconnect after a pause.
- if (!maybeCloseWebSocketSource(socketElt) && [1006, 1012, 1013].indexOf(e.code) >= 0) {
- var delay = getWebSocketReconnectDelay(wrapper.retryCount);
- setTimeout(function () {
- wrapper.retryCount += 1;
- wrapper.init();
- }, delay);
- }
-
- // Notify client code that connection has been closed. Client code can inspect `event` field
- // to determine whether closure has been valid or abnormal
- api.triggerEvent(socketElt, "htmx:wsClose", { event: e, socketWrapper: wrapper.publicInterface })
- };
-
- socket.onerror = function (e) {
- api.triggerErrorEvent(socketElt, "htmx:wsError", { error: e, socketWrapper: wrapper });
- maybeCloseWebSocketSource(socketElt);
- };
-
- var events = this.events;
- Object.keys(events).forEach(function (k) {
- events[k].forEach(function (e) {
- socket.addEventListener(k, e);
- })
- });
- },
-
- close: function () {
- this.socket.close()
- }
- }
-
- wrapper.init();
-
- wrapper.publicInterface = {
- send: wrapper.send.bind(wrapper),
- sendImmediately: wrapper.sendImmediately.bind(wrapper),
- queue: wrapper.messageQueue
- };
-
- return wrapper;
- }
-
- /**
- * ensureWebSocketSend attaches trigger handles to elements with
- * "ws-send" attribute
- * @param {HTMLElement} elt
- */
- function ensureWebSocketSend(elt) {
- var legacyAttribute = api.getAttributeValue(elt, "hx-ws");
- if (legacyAttribute && legacyAttribute !== 'send') {
- return;
- }
-
- var webSocketParent = api.getClosestMatch(elt, hasWebSocket)
- processWebSocketSend(webSocketParent, elt);
- }
-
- /**
- * hasWebSocket function checks if a node has webSocket instance attached
- * @param {HTMLElement} node
- * @returns {boolean}
- */
- function hasWebSocket(node) {
- return api.getInternalData(node).webSocket != null;
- }
-
- /**
- * processWebSocketSend adds event listeners to the <form> element so that
- * messages can be sent to the WebSocket server when the form is submitted.
- * @param {HTMLElement} socketElt
- * @param {HTMLElement} sendElt
- */
- function processWebSocketSend(socketElt, sendElt) {
- var nodeData = api.getInternalData(sendElt);
- var triggerSpecs = api.getTriggerSpecs(sendElt);
- triggerSpecs.forEach(function (ts) {
- api.addTriggerHandler(sendElt, ts, nodeData, function (elt, evt) {
- if (maybeCloseWebSocketSource(socketElt)) {
- return;
- }
-
- /** @type {WebSocketWrapper} */
- var socketWrapper = api.getInternalData(socketElt).webSocket;
- var headers = api.getHeaders(sendElt, api.getTarget(sendElt));
- var results = api.getInputValues(sendElt, 'post');
- var errors = results.errors;
- var rawParameters = results.values;
- var expressionVars = api.getExpressionVars(sendElt);
- var allParameters = api.mergeObjects(rawParameters, expressionVars);
- var filteredParameters = api.filterValues(allParameters, sendElt);
-
- var sendConfig = {
- parameters: filteredParameters,
- unfilteredParameters: allParameters,
- headers: headers,
- errors: errors,
-
- triggeringEvent: evt,
- messageBody: undefined,
- socketWrapper: socketWrapper.publicInterface
- };
-
- if (!api.triggerEvent(elt, 'htmx:wsConfigSend', sendConfig)) {
- return;
- }
-
- if (errors && errors.length > 0) {
- api.triggerEvent(elt, 'htmx:validation:halted', errors);
- return;
- }
-
- var body = sendConfig.messageBody;
- if (body === undefined) {
- var toSend = Object.assign({}, sendConfig.parameters);
- if (sendConfig.headers)
- toSend['HEADERS'] = headers;
- body = JSON.stringify(toSend);
- }
-
- socketWrapper.send(body, elt);
-
- if (evt && api.shouldCancel(evt, elt)) {
- evt.preventDefault();
- }
- });
- });
- }
-
- /**
- * getWebSocketReconnectDelay is the default easing function for WebSocket reconnects.
- * @param {number} retryCount // The number of retries that have already taken place
- * @returns {number}
- */
- function getWebSocketReconnectDelay(retryCount) {
-
- /** @type {"full-jitter" | ((retryCount:number) => number)} */
- var delay = htmx.config.wsReconnectDelay;
- if (typeof delay === 'function') {
- return delay(retryCount);
- }
- if (delay === 'full-jitter') {
- var exp = Math.min(retryCount, 6);
- var maxDelay = 1000 * Math.pow(2, exp);
- return maxDelay * Math.random();
- }
-
- logError('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"');
- }
-
- /**
- * maybeCloseWebSocketSource checks to the if the element that created the WebSocket
- * still exists in the DOM. If NOT, then the WebSocket is closed and this function
- * returns TRUE. If the element DOES EXIST, then no action is taken, and this function
- * returns FALSE.
- *
- * @param {*} elt
- * @returns
- */
- function maybeCloseWebSocketSource(elt) {
- if (!api.bodyContains(elt)) {
- api.getInternalData(elt).webSocket.close();
- return true;
- }
- return false;
- }
-
- /**
- * createWebSocket is the default method for creating new WebSocket objects.
- * it is hoisted into htmx.createWebSocket to be overridden by the user, if needed.
- *
- * @param {string} url
- * @returns WebSocket
- */
- function createWebSocket(url) {
- var sock = new WebSocket(url, []);
- sock.binaryType = htmx.config.wsBinaryType;
- return sock;
- }
-
- /**
- * queryAttributeOnThisOrChildren returns all nodes that contain the requested attributeName, INCLUDING THE PROVIDED ROOT ELEMENT.
- *
- * @param {HTMLElement} elt
- * @param {string} attributeName
- */
- function queryAttributeOnThisOrChildren(elt, attributeName) {
-
- var result = []
-
- // If the parent element also contains the requested attribute, then add it to the results too.
- if (api.hasAttribute(elt, attributeName) || api.hasAttribute(elt, "hx-ws")) {
- result.push(elt);
- }
-
- // Search all child nodes that match the requested attribute
- elt.querySelectorAll("[" + attributeName + "], [data-" + attributeName + "], [data-hx-ws], [hx-ws]").forEach(function (node) {
- result.push(node)
- })
-
- return result
- }
-
- /**
- * @template T
- * @param {T[]} arr
- * @param {(T) => void} func
- */
- function forEach(arr, func) {
- if (arr) {
- for (var i = 0; i < arr.length; i++) {
- func(arr[i]);
- }
- }
- }
-
-})();
-
diff --git a/dist/htmx.min.js.gz b/dist/htmx.min.js.gz
index 2b30538a..e44d7c6a 100644
--- a/dist/htmx.min.js.gz
+++ b/dist/htmx.min.js.gz
Binary files differ