diff options
author | Alexander Petros <apetros15@gmail.com> | 2023-12-21 12:36:58 -0500 |
---|---|---|
committer | Alexander Petros <apetros15@gmail.com> | 2023-12-21 12:36:58 -0500 |
commit | e96037c4edfe331421d12e52f552d51fec533ad7 (patch) | |
tree | fa7edac7ab86a3dd9d57f9ff691393e9c2dd7b63 /dist | |
parent | 7e484f65a4c76bc454bbe3c0975866cbf6ab4b2b (diff) | |
download | htmx-e96037c4edfe331421d12e52f552d51fec533ad7.tar.gz htmx-e96037c4edfe331421d12e52f552d51fec533ad7.zip |
Remove extensions from dist
Diffstat (limited to 'dist')
-rw-r--r-- | dist/ext/ajax-header.js | 7 | ||||
-rw-r--r-- | dist/ext/alpine-morph.js | 16 | ||||
-rw-r--r-- | dist/ext/class-tools.js | 92 | ||||
-rw-r--r-- | dist/ext/client-side-templates.js | 96 | ||||
-rw-r--r-- | dist/ext/debug.js | 11 | ||||
-rw-r--r-- | dist/ext/disable-element.js | 18 | ||||
-rw-r--r-- | dist/ext/event-header.js | 37 | ||||
-rw-r--r-- | dist/ext/head-support.js | 141 | ||||
-rw-r--r-- | dist/ext/include-vals.js | 24 | ||||
-rw-r--r-- | dist/ext/json-enc.js | 12 | ||||
-rw-r--r-- | dist/ext/loading-states.js | 183 | ||||
-rw-r--r-- | dist/ext/method-override.js | 11 | ||||
-rw-r--r-- | dist/ext/morphdom-swap.js | 17 | ||||
-rw-r--r-- | dist/ext/multi-swap.js | 45 | ||||
-rw-r--r-- | dist/ext/path-deps.js | 60 | ||||
-rw-r--r-- | dist/ext/preload.js | 147 | ||||
-rw-r--r-- | dist/ext/rails-method.js | 10 | ||||
-rw-r--r-- | dist/ext/remove-me.js | 27 | ||||
-rw-r--r-- | dist/ext/response-targets.js | 130 | ||||
-rw-r--r-- | dist/ext/restored.js | 15 | ||||
-rw-r--r-- | dist/ext/sse.js | 322 | ||||
-rw-r--r-- | dist/ext/ws.js | 477 | ||||
-rw-r--r-- | dist/htmx.min.js.gz | bin | 14459 -> 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 Binary files differindex 2b30538a..e44d7c6a 100644 --- a/dist/htmx.min.js.gz +++ b/dist/htmx.min.js.gz |