htmx.org 1.9.10 → 2.0.0-alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,141 +0,0 @@
1
- //==========================================================
2
- // head-support.js
3
- //
4
- // An extension to htmx 1.0 to add head tag merging.
5
- //==========================================================
6
- (function(){
7
-
8
- var api = null;
9
-
10
- function log() {
11
- //console.log(arguments);
12
- }
13
-
14
- function mergeHead(newContent, defaultMergeStrategy) {
15
-
16
- if (newContent && newContent.indexOf('<head') > -1) {
17
- const htmlDoc = document.createElement("html");
18
- // remove svgs to avoid conflicts
19
- var contentWithSvgsRemoved = newContent.replace(/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim, '');
20
- // extract head tag
21
- var headTag = contentWithSvgsRemoved.match(/(<head(\s[^>]*>|>)([\s\S]*?)<\/head>)/im);
22
-
23
- // if the head tag exists...
24
- if (headTag) {
25
-
26
- var added = []
27
- var removed = []
28
- var preserved = []
29
- var nodesToAppend = []
30
-
31
- htmlDoc.innerHTML = headTag;
32
- var newHeadTag = htmlDoc.querySelector("head");
33
- var currentHead = document.head;
34
-
35
- if (newHeadTag == null) {
36
- return;
37
- } else {
38
- // put all new head elements into a Map, by their outerHTML
39
- var srcToNewHeadNodes = new Map();
40
- for (const newHeadChild of newHeadTag.children) {
41
- srcToNewHeadNodes.set(newHeadChild.outerHTML, newHeadChild);
42
- }
43
- }
44
-
45
-
46
-
47
- // determine merge strategy
48
- var mergeStrategy = api.getAttributeValue(newHeadTag, "hx-head") || defaultMergeStrategy;
49
-
50
- // get the current head
51
- for (const currentHeadElt of currentHead.children) {
52
-
53
- // If the current head element is in the map
54
- var inNewContent = srcToNewHeadNodes.has(currentHeadElt.outerHTML);
55
- var isReAppended = currentHeadElt.getAttribute("hx-head") === "re-eval";
56
- var isPreserved = api.getAttributeValue(currentHeadElt, "hx-preserve") === "true";
57
- if (inNewContent || isPreserved) {
58
- if (isReAppended) {
59
- // remove the current version and let the new version replace it and re-execute
60
- removed.push(currentHeadElt);
61
- } else {
62
- // this element already exists and should not be re-appended, so remove it from
63
- // the new content map, preserving it in the DOM
64
- srcToNewHeadNodes.delete(currentHeadElt.outerHTML);
65
- preserved.push(currentHeadElt);
66
- }
67
- } else {
68
- if (mergeStrategy === "append") {
69
- // we are appending and this existing element is not new content
70
- // so if and only if it is marked for re-append do we do anything
71
- if (isReAppended) {
72
- removed.push(currentHeadElt);
73
- nodesToAppend.push(currentHeadElt);
74
- }
75
- } else {
76
- // if this is a merge, we remove this content since it is not in the new head
77
- if (api.triggerEvent(document.body, "htmx:removingHeadElement", {headElement: currentHeadElt}) !== false) {
78
- removed.push(currentHeadElt);
79
- }
80
- }
81
- }
82
- }
83
-
84
- // Push the tremaining new head elements in the Map into the
85
- // nodes to append to the head tag
86
- nodesToAppend.push(...srcToNewHeadNodes.values());
87
- log("to append: ", nodesToAppend);
88
-
89
- for (const newNode of nodesToAppend) {
90
- log("adding: ", newNode);
91
- var newElt = document.createRange().createContextualFragment(newNode.outerHTML);
92
- log(newElt);
93
- if (api.triggerEvent(document.body, "htmx:addingHeadElement", {headElement: newElt}) !== false) {
94
- currentHead.appendChild(newElt);
95
- added.push(newElt);
96
- }
97
- }
98
-
99
- // remove all removed elements, after we have appended the new elements to avoid
100
- // additional network requests for things like style sheets
101
- for (const removedElement of removed) {
102
- if (api.triggerEvent(document.body, "htmx:removingHeadElement", {headElement: removedElement}) !== false) {
103
- currentHead.removeChild(removedElement);
104
- }
105
- }
106
-
107
- api.triggerEvent(document.body, "htmx:afterHeadMerge", {added: added, kept: preserved, removed: removed});
108
- }
109
- }
110
- }
111
-
112
- htmx.defineExtension("head-support", {
113
- init: function(apiRef) {
114
- // store a reference to the internal API.
115
- api = apiRef;
116
-
117
- htmx.on('htmx:afterSwap', function(evt){
118
- var serverResponse = evt.detail.xhr.response;
119
- if (api.triggerEvent(document.body, "htmx:beforeHeadMerge", evt.detail)) {
120
- mergeHead(serverResponse, evt.detail.boosted ? "merge" : "append");
121
- }
122
- })
123
-
124
- htmx.on('htmx:historyRestore', function(evt){
125
- if (api.triggerEvent(document.body, "htmx:beforeHeadMerge", evt.detail)) {
126
- if (evt.detail.cacheMiss) {
127
- mergeHead(evt.detail.serverResponse, "merge");
128
- } else {
129
- mergeHead(evt.detail.item.head, "merge");
130
- }
131
- }
132
- })
133
-
134
- htmx.on('htmx:historyItemCreated', function(evt){
135
- var historyItem = evt.detail.item;
136
- historyItem.head = document.head.outerHTML;
137
- })
138
- }
139
- });
140
-
141
- })()
@@ -1,24 +0,0 @@
1
- (function(){
2
-
3
- function mergeObjects(obj1, obj2) {
4
- for (var key in obj2) {
5
- if (obj2.hasOwnProperty(key)) {
6
- obj1[key] = obj2[key];
7
- }
8
- }
9
- return obj1;
10
- }
11
-
12
- htmx.defineExtension('include-vals', {
13
- onEvent: function (name, evt) {
14
- if (name === "htmx:configRequest") {
15
- var includeValsElt = htmx.closest(evt.detail.elt, "[include-vals],[data-include-vals]");
16
- if (includeValsElt) {
17
- var includeVals = includeValsElt.getAttribute("include-vals") || includeValsElt.getAttribute("data-include-vals");
18
- var valuesToInclude = eval("({" + includeVals + "})");
19
- mergeObjects(evt.detail.parameters, valuesToInclude);
20
- }
21
- }
22
- }
23
- });
24
- })();
@@ -1,12 +0,0 @@
1
- htmx.defineExtension('json-enc', {
2
- onEvent: function (name, evt) {
3
- if (name === "htmx:configRequest") {
4
- evt.detail.headers['Content-Type'] = "application/json";
5
- }
6
- },
7
-
8
- encodeParameters : function(xhr, parameters, elt) {
9
- xhr.overrideMimeType('text/json');
10
- return (JSON.stringify(parameters));
11
- }
12
- });
@@ -1,183 +0,0 @@
1
- ;(function () {
2
- let loadingStatesUndoQueue = []
3
-
4
- function loadingStateContainer(target) {
5
- return htmx.closest(target, '[data-loading-states]') || document.body
6
- }
7
-
8
- function mayProcessUndoCallback(target, callback) {
9
- if (document.body.contains(target)) {
10
- callback()
11
- }
12
- }
13
-
14
- function mayProcessLoadingStateByPath(elt, requestPath) {
15
- const pathElt = htmx.closest(elt, '[data-loading-path]')
16
- if (!pathElt) {
17
- return true
18
- }
19
-
20
- return pathElt.getAttribute('data-loading-path') === requestPath
21
- }
22
-
23
- function queueLoadingState(sourceElt, targetElt, doCallback, undoCallback) {
24
- const delayElt = htmx.closest(sourceElt, '[data-loading-delay]')
25
- if (delayElt) {
26
- const delayInMilliseconds =
27
- delayElt.getAttribute('data-loading-delay') || 200
28
- const timeout = setTimeout(function () {
29
- doCallback()
30
-
31
- loadingStatesUndoQueue.push(function () {
32
- mayProcessUndoCallback(targetElt, undoCallback)
33
- })
34
- }, delayInMilliseconds)
35
-
36
- loadingStatesUndoQueue.push(function () {
37
- mayProcessUndoCallback(targetElt, function () { clearTimeout(timeout) })
38
- })
39
- } else {
40
- doCallback()
41
- loadingStatesUndoQueue.push(function () {
42
- mayProcessUndoCallback(targetElt, undoCallback)
43
- })
44
- }
45
- }
46
-
47
- function getLoadingStateElts(loadingScope, type, path) {
48
- return Array.from(htmx.findAll(loadingScope, "[" + type + "]")).filter(
49
- function (elt) { return mayProcessLoadingStateByPath(elt, path) }
50
- )
51
- }
52
-
53
- function getLoadingTarget(elt) {
54
- if (elt.getAttribute('data-loading-target')) {
55
- return Array.from(
56
- htmx.findAll(elt.getAttribute('data-loading-target'))
57
- )
58
- }
59
- return [elt]
60
- }
61
-
62
- htmx.defineExtension('loading-states', {
63
- onEvent: function (name, evt) {
64
- if (name === 'htmx:beforeRequest') {
65
- const container = loadingStateContainer(evt.target)
66
-
67
- const loadingStateTypes = [
68
- 'data-loading',
69
- 'data-loading-class',
70
- 'data-loading-class-remove',
71
- 'data-loading-disable',
72
- 'data-loading-aria-busy',
73
- ]
74
-
75
- let loadingStateEltsByType = {}
76
-
77
- loadingStateTypes.forEach(function (type) {
78
- loadingStateEltsByType[type] = getLoadingStateElts(
79
- container,
80
- type,
81
- evt.detail.pathInfo.requestPath
82
- )
83
- })
84
-
85
- loadingStateEltsByType['data-loading'].forEach(function (sourceElt) {
86
- getLoadingTarget(sourceElt).forEach(function (targetElt) {
87
- queueLoadingState(
88
- sourceElt,
89
- targetElt,
90
- function () {
91
- targetElt.style.display =
92
- sourceElt.getAttribute('data-loading') ||
93
- 'inline-block' },
94
- function () { targetElt.style.display = 'none' }
95
- )
96
- })
97
- })
98
-
99
- loadingStateEltsByType['data-loading-class'].forEach(
100
- function (sourceElt) {
101
- const classNames = sourceElt
102
- .getAttribute('data-loading-class')
103
- .split(' ')
104
-
105
- getLoadingTarget(sourceElt).forEach(function (targetElt) {
106
- queueLoadingState(
107
- sourceElt,
108
- targetElt,
109
- function () {
110
- classNames.forEach(function (className) {
111
- targetElt.classList.add(className)
112
- })
113
- },
114
- function() {
115
- classNames.forEach(function (className) {
116
- targetElt.classList.remove(className)
117
- })
118
- }
119
- )
120
- })
121
- }
122
- )
123
-
124
- loadingStateEltsByType['data-loading-class-remove'].forEach(
125
- function (sourceElt) {
126
- const classNames = sourceElt
127
- .getAttribute('data-loading-class-remove')
128
- .split(' ')
129
-
130
- getLoadingTarget(sourceElt).forEach(function (targetElt) {
131
- queueLoadingState(
132
- sourceElt,
133
- targetElt,
134
- function () {
135
- classNames.forEach(function (className) {
136
- targetElt.classList.remove(className)
137
- })
138
- },
139
- function() {
140
- classNames.forEach(function (className) {
141
- targetElt.classList.add(className)
142
- })
143
- }
144
- )
145
- })
146
- }
147
- )
148
-
149
- loadingStateEltsByType['data-loading-disable'].forEach(
150
- function (sourceElt) {
151
- getLoadingTarget(sourceElt).forEach(function (targetElt) {
152
- queueLoadingState(
153
- sourceElt,
154
- targetElt,
155
- function() { targetElt.disabled = true },
156
- function() { targetElt.disabled = false }
157
- )
158
- })
159
- }
160
- )
161
-
162
- loadingStateEltsByType['data-loading-aria-busy'].forEach(
163
- function (sourceElt) {
164
- getLoadingTarget(sourceElt).forEach(function (targetElt) {
165
- queueLoadingState(
166
- sourceElt,
167
- targetElt,
168
- function () { targetElt.setAttribute("aria-busy", "true") },
169
- function () { targetElt.removeAttribute("aria-busy") }
170
- )
171
- })
172
- }
173
- )
174
- }
175
-
176
- if (name === 'htmx:beforeOnLoad') {
177
- while (loadingStatesUndoQueue.length > 0) {
178
- loadingStatesUndoQueue.shift()()
179
- }
180
- }
181
- },
182
- })
183
- })()
@@ -1,11 +0,0 @@
1
- htmx.defineExtension('method-override', {
2
- onEvent: function (name, evt) {
3
- if (name === "htmx:configRequest") {
4
- var method = evt.detail.verb;
5
- if (method !== "get" || method !== "post") {
6
- evt.detail.headers['X-HTTP-Method-Override'] = method.toUpperCase();
7
- evt.detail.verb = "post";
8
- }
9
- }
10
- }
11
- });
@@ -1,17 +0,0 @@
1
- htmx.defineExtension('morphdom-swap', {
2
- isInlineSwap: function(swapStyle) {
3
- return swapStyle === 'morphdom';
4
- },
5
- handleSwap: function (swapStyle, target, fragment) {
6
- if (swapStyle === 'morphdom') {
7
- if (fragment.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
8
- // IE11 doesn't support DocumentFragment.firstElementChild
9
- morphdom(target, fragment.firstElementChild || fragment.firstChild);
10
- return [target];
11
- } else {
12
- morphdom(target, fragment.outerHTML);
13
- return [target];
14
- }
15
- }
16
- }
17
- });
@@ -1,45 +0,0 @@
1
- (function () {
2
-
3
- /** @type {import("../htmx").HtmxInternalApi} */
4
- var api;
5
-
6
- htmx.defineExtension('multi-swap', {
7
- init: function (apiRef) {
8
- api = apiRef;
9
- },
10
- isInlineSwap: function (swapStyle) {
11
- return swapStyle.indexOf('multi:') === 0;
12
- },
13
- handleSwap: function (swapStyle, target, fragment, settleInfo) {
14
- if (swapStyle.indexOf('multi:') === 0) {
15
- var selectorToSwapStyle = {};
16
- var elements = swapStyle.replace(/^multi\s*:\s*/, '').split(/\s*,\s*/);
17
-
18
- elements.map(function (element) {
19
- var split = element.split(/\s*:\s*/);
20
- var elementSelector = split[0];
21
- var elementSwapStyle = typeof (split[1]) !== "undefined" ? split[1] : "innerHTML";
22
-
23
- if (elementSelector.charAt(0) !== '#') {
24
- console.error("HTMX multi-swap: unsupported selector '" + elementSelector + "'. Only ID selectors starting with '#' are supported.");
25
- return;
26
- }
27
-
28
- selectorToSwapStyle[elementSelector] = elementSwapStyle;
29
- });
30
-
31
- for (var selector in selectorToSwapStyle) {
32
- var swapStyle = selectorToSwapStyle[selector];
33
- var elementToSwap = fragment.querySelector(selector);
34
- if (elementToSwap) {
35
- api.oobSwap(swapStyle, elementToSwap, settleInfo);
36
- } else {
37
- console.warn("HTMX multi-swap: selector '" + selector + "' not found in source content.");
38
- }
39
- }
40
-
41
- return true;
42
- }
43
- }
44
- });
45
- })();
@@ -1,60 +0,0 @@
1
- (function(undefined){
2
- 'use strict';
3
-
4
- // Save a reference to the global object (window in the browser)
5
- var _root = this;
6
-
7
- function dependsOn(pathSpec, url) {
8
- if (pathSpec === "ignore") {
9
- return false;
10
- }
11
- var dependencyPath = pathSpec.split("/");
12
- var urlPath = url.split("/");
13
- for (var i = 0; i < urlPath.length; i++) {
14
- var dependencyElement = dependencyPath.shift();
15
- var pathElement = urlPath[i];
16
- if (dependencyElement !== pathElement && dependencyElement !== "*") {
17
- return false;
18
- }
19
- if (dependencyPath.length === 0 || (dependencyPath.length === 1 && dependencyPath[0] === "")) {
20
- return true;
21
- }
22
- }
23
- return false;
24
- }
25
-
26
- function refreshPath(path) {
27
- var eltsWithDeps = htmx.findAll("[path-deps]");
28
- for (var i = 0; i < eltsWithDeps.length; i++) {
29
- var elt = eltsWithDeps[i];
30
- if (dependsOn(elt.getAttribute('path-deps'), path)) {
31
- htmx.trigger(elt, "path-deps");
32
- }
33
- }
34
- }
35
-
36
- htmx.defineExtension('path-deps', {
37
- onEvent: function (name, evt) {
38
- if (name === "htmx:beforeOnLoad") {
39
- var config = evt.detail.requestConfig;
40
- // mutating call
41
- if (config.verb !== "get" && evt.target.getAttribute('path-deps') !== 'ignore') {
42
- refreshPath(config.path);
43
- }
44
- }
45
- }
46
- });
47
-
48
- /**
49
- * ********************
50
- * Expose functionality
51
- * ********************
52
- */
53
-
54
- _root.PathDeps = {
55
- refresh: function(path) {
56
- refreshPath(path);
57
- }
58
- };
59
-
60
- }).call(this);
@@ -1,147 +0,0 @@
1
- // This adds the "preload" extension to htmx. By default, this will
2
- // preload the targets of any tags with `href` or `hx-get` attributes
3
- // if they also have a `preload` attribute as well. See documentation
4
- // for more details
5
- htmx.defineExtension("preload", {
6
-
7
- onEvent: function(name, event) {
8
-
9
- // Only take actions on "htmx:afterProcessNode"
10
- if (name !== "htmx:afterProcessNode") {
11
- return;
12
- }
13
-
14
- // SOME HELPER FUNCTIONS WE'LL NEED ALONG THE WAY
15
-
16
- // attr gets the closest non-empty value from the attribute.
17
- var attr = function(node, property) {
18
- if (node == undefined) {return undefined;}
19
- return node.getAttribute(property) || node.getAttribute("data-" + property) || attr(node.parentElement, property)
20
- }
21
-
22
- // load handles the actual HTTP fetch, and uses htmx.ajax in cases where we're
23
- // preloading an htmx resource (this sends the same HTTP headers as a regular htmx request)
24
- var load = function(node) {
25
-
26
- // Called after a successful AJAX request, to mark the
27
- // content as loaded (and prevent additional AJAX calls.)
28
- var done = function(html) {
29
- if (!node.preloadAlways) {
30
- node.preloadState = "DONE"
31
- }
32
-
33
- if (attr(node, "preload-images") == "true") {
34
- document.createElement("div").innerHTML = html // create and populate a node to load linked resources, too.
35
- }
36
- }
37
-
38
- return function() {
39
-
40
- // If this value has already been loaded, then do not try again.
41
- if (node.preloadState !== "READY") {
42
- return;
43
- }
44
-
45
- // Special handling for HX-GET - use built-in htmx.ajax function
46
- // so that headers match other htmx requests, then set
47
- // node.preloadState = TRUE so that requests are not duplicated
48
- // in the future
49
- var hxGet = node.getAttribute("hx-get") || node.getAttribute("data-hx-get")
50
- if (hxGet) {
51
- htmx.ajax("GET", hxGet, {
52
- source: node,
53
- handler:function(elt, info) {
54
- done(info.xhr.responseText);
55
- }
56
- });
57
- return;
58
- }
59
-
60
- // Otherwise, perform a standard xhr request, then set
61
- // node.preloadState = TRUE so that requests are not duplicated
62
- // in the future.
63
- if (node.getAttribute("href")) {
64
- var r = new XMLHttpRequest();
65
- r.open("GET", node.getAttribute("href"));
66
- r.onload = function() {done(r.responseText);};
67
- r.send();
68
- return;
69
- }
70
- }
71
- }
72
-
73
- // This function processes a specific node and sets up event handlers.
74
- // We'll search for nodes and use it below.
75
- var init = function(node) {
76
-
77
- // If this node DOES NOT include a "GET" transaction, then there's nothing to do here.
78
- if (node.getAttribute("href") + node.getAttribute("hx-get") + node.getAttribute("data-hx-get") == "") {
79
- return;
80
- }
81
-
82
- // Guarantee that we only initialize each node once.
83
- if (node.preloadState !== undefined) {
84
- return;
85
- }
86
-
87
- // Get event name from config.
88
- var on = attr(node, "preload") || "mousedown"
89
- const always = on.indexOf("always") !== -1
90
- if (always) {
91
- on = on.replace('always', '').trim()
92
- }
93
-
94
- // FALL THROUGH to here means we need to add an EventListener
95
-
96
- // Apply the listener to the node
97
- node.addEventListener(on, function(evt) {
98
- if (node.preloadState === "PAUSE") { // Only add one event listener
99
- node.preloadState = "READY"; // Required for the `load` function to trigger
100
-
101
- // Special handling for "mouseover" events. Wait 100ms before triggering load.
102
- if (on === "mouseover") {
103
- window.setTimeout(load(node), 100);
104
- } else {
105
- load(node)() // all other events trigger immediately.
106
- }
107
- }
108
- })
109
-
110
- // Special handling for certain built-in event handlers
111
- switch (on) {
112
-
113
- case "mouseover":
114
- // Mirror `touchstart` events (fires immediately)
115
- node.addEventListener("touchstart", load(node));
116
-
117
- // WHhen the mouse leaves, immediately disable the preload
118
- node.addEventListener("mouseout", function(evt) {
119
- if ((evt.target === node) && (node.preloadState === "READY")) {
120
- node.preloadState = "PAUSE";
121
- }
122
- })
123
- break;
124
-
125
- case "mousedown":
126
- // Mirror `touchstart` events (fires immediately)
127
- node.addEventListener("touchstart", load(node));
128
- break;
129
- }
130
-
131
- // Mark the node as ready to run.
132
- node.preloadState = "PAUSE";
133
- node.preloadAlways = always;
134
- htmx.trigger(node, "preload:init") // This event can be used to load content immediately.
135
- }
136
-
137
- // Search for all child nodes that have a "preload" attribute
138
- event.target.querySelectorAll("[preload]").forEach(function(node) {
139
-
140
- // Initialize the node with the "preload" attribute
141
- init(node)
142
-
143
- // Initialize all child elements that are anchors or have `hx-get` (use with care)
144
- node.querySelectorAll("a,[hx-get],[data-hx-get]").forEach(init)
145
- })
146
- }
147
- })
@@ -1,10 +0,0 @@
1
- htmx.defineExtension('rails-method', {
2
- onEvent: function (name, evt) {
3
- if (name === "configRequest.htmx") {
4
- var methodOverride = evt.detail.headers['X-HTTP-Method-Override'];
5
- if (methodOverride) {
6
- evt.detail.parameters['_method'] = methodOverride;
7
- }
8
- }
9
- }
10
- });
@@ -1,27 +0,0 @@
1
- (function(){
2
- function maybeRemoveMe(elt) {
3
- var timing = elt.getAttribute("remove-me") || elt.getAttribute("data-remove-me");
4
- if (timing) {
5
- setTimeout(function () {
6
- elt.parentElement.removeChild(elt);
7
- }, htmx.parseInterval(timing));
8
- }
9
- }
10
-
11
- htmx.defineExtension('remove-me', {
12
- onEvent: function (name, evt) {
13
- if (name === "htmx:afterProcessNode") {
14
- var elt = evt.detail.elt;
15
- if (elt.getAttribute) {
16
- maybeRemoveMe(elt);
17
- if (elt.querySelectorAll) {
18
- var children = elt.querySelectorAll("[remove-me], [data-remove-me]");
19
- for (var i = 0; i < children.length; i++) {
20
- maybeRemoveMe(children[i]);
21
- }
22
- }
23
- }
24
- }
25
- }
26
- });
27
- })();