release prep

This commit is contained in:
Carson Gross 2024-08-05 13:53:08 -06:00
parent b1d6135dca
commit 97b8c68dd3
14 changed files with 197 additions and 108 deletions

View File

@ -1,5 +1,8 @@
# Changelog
## [2.0.2] - 2024-07-12
## [2.0.1] - 2024-07-12
* Make the `/dist/htmx.esm.js` file the `main` file in `package.json` to make installing htmx smoother

View File

@ -32,7 +32,7 @@ By removing these arbitrary constraints htmx completes HTML as a
## quick start
```html
<script src="https://unpkg.com/htmx.org@2.0.1"></script>
<script src="https://unpkg.com/htmx.org@2.0.2"></script>
<!-- have a button POST a click via AJAX -->
<button hx-post="/clicked" hx-swap="outerHTML">
Click Me

71
dist/htmx.amd.js vendored
View File

@ -207,7 +207,7 @@ var htmx = (function() {
disableSelector: '[hx-disable], [data-hx-disable]',
/**
* @type {'auto' | 'instant' | 'smooth'}
* @default 'smooth'
* @default 'instant'
*/
scrollBehavior: 'instant',
/**
@ -278,7 +278,7 @@ var htmx = (function() {
parseInterval: null,
/** @type {typeof internalEval} */
_: null,
version: '2.0.1'
version: '2.0.2'
}
// Tsc madness part 2
htmx.onLoad = onLoadHelper
@ -773,13 +773,10 @@ var htmx = (function() {
function bodyContains(elt) {
// IE Fix
const rootNode = elt.getRootNode && elt.getRootNode()
if (getDocument().body === null) {
return false;
}
if (rootNode && rootNode instanceof window.ShadowRoot) {
return getDocument().body.contains(rootNode.host);
return getDocument().body.contains(rootNode.host)
} else {
return getDocument().body.contains(elt);
return getDocument().body.contains(elt)
}
}
@ -1643,13 +1640,13 @@ var htmx = (function() {
newElt = eltBeforeNewContent.nextSibling
}
settleInfo.elts = settleInfo.elts.filter(function(e) { return e !== target })
// scan through all newly added content and add all elements to the settle info so we trigger
// events properly on them
while (newElt && newElt !== target) {
if (newElt instanceof Element) {
settleInfo.elts.push(newElt)
newElt = newElt.nextElementSibling
} else {
newElt = null
}
newElt = newElt.nextSibling
}
cleanUpElement(target)
if (target instanceof Element) {
@ -1757,7 +1754,7 @@ var htmx = (function() {
try {
const newElements = ext.handleSwap(swapStyle, target, fragment, settleInfo)
if (newElements) {
if (typeof newElements.length !== 'undefined') {
if (Array.isArray(newElements)) {
// if handleSwap returns an array (like) of elements, we handle them
for (let j = 0; j < newElements.length; j++) {
const child = newElements[j]
@ -1785,7 +1782,8 @@ var htmx = (function() {
* @param {HtmxSettleInfo} settleInfo
*/
function findAndSwapOobElements(fragment, settleInfo) {
forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function(oobElement) {
var oobElts = findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]')
forEach(oobElts, function(oobElement) {
if (htmx.config.allowNestedOobSwaps || oobElement.parentElement === null) {
const oobValue = getAttributeValue(oobElement, 'hx-swap-oob')
if (oobValue != null) {
@ -1796,6 +1794,7 @@ var htmx = (function() {
oobElement.removeAttribute('data-hx-swap-oob')
}
})
return oobElts.length > 0
}
/**
@ -1857,9 +1856,8 @@ var htmx = (function() {
// oob swaps
findAndSwapOobElements(fragment, settleInfo)
forEach(findAll(fragment, 'template'), /** @param {HTMLTemplateElement} template */function(template) {
findAndSwapOobElements(template.content, settleInfo)
if (template.content.childElementCount === 0 && template.content.textContent.trim() === '') {
// Avoid polluting the DOM with empty templates that were only used to encapsulate oob swap
if (findAndSwapOobElements(template.content, settleInfo)) {
// Avoid polluting the DOM with empty templates that were only used to encapsulate oob swap
template.remove()
}
})
@ -1956,7 +1954,10 @@ var htmx = (function() {
for (const eventName in triggers) {
if (triggers.hasOwnProperty(eventName)) {
let detail = triggers[eventName]
if (!isRawObject(detail)) {
if (isRawObject(detail)) {
// @ts-ignore
elt = detail.target !== undefined ? detail.target : elt
} else {
detail = { value: detail }
}
triggerEvent(elt, eventName, detail)
@ -2277,7 +2278,7 @@ var htmx = (function() {
* @param {HtmxTriggerSpecification[]} triggerSpecs
*/
function boostElement(elt, nodeData, triggerSpecs) {
if ((elt instanceof HTMLAnchorElement && isLocalLink(elt) && (elt.target === '' || elt.target === '_self')) || elt.tagName === 'FORM') {
if ((elt instanceof HTMLAnchorElement && isLocalLink(elt) && (elt.target === '' || elt.target === '_self')) || (elt.tagName === 'FORM' && String(getRawAttribute(elt, 'method')).toLowerCase() !== 'dialog')) {
nodeData.boosted = true
let verb, path
if (elt.tagName === 'A') {
@ -2439,13 +2440,17 @@ var htmx = (function() {
if (triggerSpec.throttle > 0) {
if (!elementData.throttle) {
triggerEvent(elt, 'htmx:trigger')
handler(elt, evt)
elementData.throttle = getWindow().setTimeout(function() {
elementData.throttle = null
}, triggerSpec.throttle)
}
} else if (triggerSpec.delay > 0) {
elementData.delayed = getWindow().setTimeout(function() { handler(elt, evt) }, triggerSpec.delay)
elementData.delayed = getWindow().setTimeout(function() {
triggerEvent(elt, 'htmx:trigger')
handler(elt, evt)
}, triggerSpec.delay)
} else {
triggerEvent(elt, 'htmx:trigger')
handler(elt, evt)
@ -3063,6 +3068,10 @@ var htmx = (function() {
forEach(findAll(clone, '.' + className), function(child) {
removeClassFromElement(child, className)
})
// remove the disabled attribute for any element disabled due to an htmx request
forEach(findAll(clone, '[data-disabled-by-htmx]'), function(child) {
child.removeAttribute('disabled')
})
return clone.innerHTML
}
@ -3216,6 +3225,7 @@ var htmx = (function() {
const internalData = getInternalData(disabledElement)
internalData.requestCount = (internalData.requestCount || 0) + 1
disabledElement.setAttribute('disabled', '')
disabledElement.setAttribute('data-disabled-by-htmx', '')
})
return disabledElts
}
@ -3237,6 +3247,7 @@ var htmx = (function() {
internalData.requestCount = (internalData.requestCount || 0) - 1
if (internalData.requestCount === 0) {
disabledElement.removeAttribute('disabled')
disabledElement.removeAttribute('data-disabled-by-htmx')
}
})
}
@ -3386,10 +3397,10 @@ var htmx = (function() {
function overrideFormData(receiver, donor) {
for (const key of donor.keys()) {
receiver.delete(key)
donor.getAll(key).forEach(function(value) {
receiver.append(key, value)
})
}
donor.forEach(function(value, key) {
receiver.append(key, value)
})
return receiver
}
@ -3919,7 +3930,7 @@ var htmx = (function() {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key].forEach === 'function') {
obj[key].forEach(function(v) { formData.append(key, v) })
} else if (typeof obj[key] === 'object') {
} else if (typeof obj[key] === 'object' && !(obj[key] instanceof Blob)) {
formData.append(key, JSON.stringify(obj[key]))
} else {
formData.append(key, obj[key])
@ -4012,6 +4023,8 @@ var htmx = (function() {
target.delete(name)
if (typeof value.forEach === 'function') {
value.forEach(function(v) { target.append(name, v) })
} else if (typeof value === 'object' && !(value instanceof Blob)) {
target.append(name, JSON.stringify(value))
} else {
target.append(name, value)
}
@ -4347,7 +4360,9 @@ var htmx = (function() {
const hierarchy = hierarchyForElt(elt)
responseInfo.pathInfo.responsePath = getPathFromResponse(xhr)
responseHandler(elt, responseInfo)
removeRequestIndicators(indicators, disableElts)
if (responseInfo.keepIndicators !== true) {
removeRequestIndicators(indicators, disableElts)
}
triggerEvent(elt, 'htmx:afterRequest', responseInfo)
triggerEvent(elt, 'htmx:afterOnLoad', responseInfo)
// if the body no longer contains the element, trigger the event on the closest parent
@ -4587,12 +4602,14 @@ var htmx = (function() {
const shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && xhr.getResponseHeader('HX-Refresh') === 'true'
if (hasHeader(xhr, /HX-Redirect:/i)) {
responseInfo.keepIndicators = true
location.href = xhr.getResponseHeader('HX-Redirect')
shouldRefresh && location.reload()
return
}
if (shouldRefresh) {
responseInfo.keepIndicators = true
location.reload()
return
}
@ -5076,6 +5093,7 @@ var htmx = (function() {
* @property {{requestPath: string, finalRequestPath: string, responsePath: string|null, anchor: string}} pathInfo
* @property {boolean} [failed]
* @property {boolean} [successful]
* @property {boolean} [keepIndicators]
*/
/**
@ -5125,14 +5143,15 @@ var htmx = (function() {
*/
/**
* @see https://github.com/bigskysoftware/htmx-extensions/blob/main/README.md
* @typedef {Object} HtmxExtension
* @see https://htmx.org/extensions/#defining
* @property {(api: any) => void} init
* @property {(name: string, event: Event|CustomEvent) => boolean} onEvent
* @property {(text: string, xhr: XMLHttpRequest, elt: Element) => string} transformResponse
* @property {(swapStyle: HtmxSwapStyle) => boolean} isInlineSwap
* @property {(swapStyle: HtmxSwapStyle, target: Element, fragment: Node, settleInfo: HtmxSettleInfo) => boolean} handleSwap
* @property {(xhr: XMLHttpRequest, parameters: FormData, elt: Element) => *|string|null} encodeParameters
* @property {(swapStyle: HtmxSwapStyle, target: Node, fragment: Node, settleInfo: HtmxSettleInfo) => boolean|Node[]} handleSwap
* @property {(xhr: XMLHttpRequest, parameters: FormData, elt: Node) => *|string|null} encodeParameters
* @property {() => string[]|null} getSelectors
*/
return htmx
})

64
dist/htmx.cjs.js vendored
View File

@ -206,7 +206,7 @@ var htmx = (function() {
disableSelector: '[hx-disable], [data-hx-disable]',
/**
* @type {'auto' | 'instant' | 'smooth'}
* @default 'smooth'
* @default 'instant'
*/
scrollBehavior: 'instant',
/**
@ -277,7 +277,7 @@ var htmx = (function() {
parseInterval: null,
/** @type {typeof internalEval} */
_: null,
version: '2.0.1'
version: '2.0.2'
}
// Tsc madness part 2
htmx.onLoad = onLoadHelper
@ -1639,13 +1639,13 @@ var htmx = (function() {
newElt = eltBeforeNewContent.nextSibling
}
settleInfo.elts = settleInfo.elts.filter(function(e) { return e !== target })
// scan through all newly added content and add all elements to the settle info so we trigger
// events properly on them
while (newElt && newElt !== target) {
if (newElt instanceof Element) {
settleInfo.elts.push(newElt)
newElt = newElt.nextElementSibling
} else {
newElt = null
}
newElt = newElt.nextSibling
}
cleanUpElement(target)
if (target instanceof Element) {
@ -1753,7 +1753,7 @@ var htmx = (function() {
try {
const newElements = ext.handleSwap(swapStyle, target, fragment, settleInfo)
if (newElements) {
if (typeof newElements.length !== 'undefined') {
if (Array.isArray(newElements)) {
// if handleSwap returns an array (like) of elements, we handle them
for (let j = 0; j < newElements.length; j++) {
const child = newElements[j]
@ -1781,7 +1781,8 @@ var htmx = (function() {
* @param {HtmxSettleInfo} settleInfo
*/
function findAndSwapOobElements(fragment, settleInfo) {
forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function(oobElement) {
var oobElts = findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]')
forEach(oobElts, function(oobElement) {
if (htmx.config.allowNestedOobSwaps || oobElement.parentElement === null) {
const oobValue = getAttributeValue(oobElement, 'hx-swap-oob')
if (oobValue != null) {
@ -1792,6 +1793,7 @@ var htmx = (function() {
oobElement.removeAttribute('data-hx-swap-oob')
}
})
return oobElts.length > 0
}
/**
@ -1853,9 +1855,8 @@ var htmx = (function() {
// oob swaps
findAndSwapOobElements(fragment, settleInfo)
forEach(findAll(fragment, 'template'), /** @param {HTMLTemplateElement} template */function(template) {
findAndSwapOobElements(template.content, settleInfo)
if (template.content.childElementCount === 0 && template.content.textContent.trim() === '') {
// Avoid polluting the DOM with empty templates that were only used to encapsulate oob swap
if (findAndSwapOobElements(template.content, settleInfo)) {
// Avoid polluting the DOM with empty templates that were only used to encapsulate oob swap
template.remove()
}
})
@ -1952,7 +1953,10 @@ var htmx = (function() {
for (const eventName in triggers) {
if (triggers.hasOwnProperty(eventName)) {
let detail = triggers[eventName]
if (!isRawObject(detail)) {
if (isRawObject(detail)) {
// @ts-ignore
elt = detail.target !== undefined ? detail.target : elt
} else {
detail = { value: detail }
}
triggerEvent(elt, eventName, detail)
@ -2273,7 +2277,7 @@ var htmx = (function() {
* @param {HtmxTriggerSpecification[]} triggerSpecs
*/
function boostElement(elt, nodeData, triggerSpecs) {
if ((elt instanceof HTMLAnchorElement && isLocalLink(elt) && (elt.target === '' || elt.target === '_self')) || elt.tagName === 'FORM') {
if ((elt instanceof HTMLAnchorElement && isLocalLink(elt) && (elt.target === '' || elt.target === '_self')) || (elt.tagName === 'FORM' && String(getRawAttribute(elt, 'method')).toLowerCase() !== 'dialog')) {
nodeData.boosted = true
let verb, path
if (elt.tagName === 'A') {
@ -2435,13 +2439,17 @@ var htmx = (function() {
if (triggerSpec.throttle > 0) {
if (!elementData.throttle) {
triggerEvent(elt, 'htmx:trigger')
handler(elt, evt)
elementData.throttle = getWindow().setTimeout(function() {
elementData.throttle = null
}, triggerSpec.throttle)
}
} else if (triggerSpec.delay > 0) {
elementData.delayed = getWindow().setTimeout(function() { handler(elt, evt) }, triggerSpec.delay)
elementData.delayed = getWindow().setTimeout(function() {
triggerEvent(elt, 'htmx:trigger')
handler(elt, evt)
}, triggerSpec.delay)
} else {
triggerEvent(elt, 'htmx:trigger')
handler(elt, evt)
@ -3059,6 +3067,10 @@ var htmx = (function() {
forEach(findAll(clone, '.' + className), function(child) {
removeClassFromElement(child, className)
})
// remove the disabled attribute for any element disabled due to an htmx request
forEach(findAll(clone, '[data-disabled-by-htmx]'), function(child) {
child.removeAttribute('disabled')
})
return clone.innerHTML
}
@ -3212,6 +3224,7 @@ var htmx = (function() {
const internalData = getInternalData(disabledElement)
internalData.requestCount = (internalData.requestCount || 0) + 1
disabledElement.setAttribute('disabled', '')
disabledElement.setAttribute('data-disabled-by-htmx', '')
})
return disabledElts
}
@ -3233,6 +3246,7 @@ var htmx = (function() {
internalData.requestCount = (internalData.requestCount || 0) - 1
if (internalData.requestCount === 0) {
disabledElement.removeAttribute('disabled')
disabledElement.removeAttribute('data-disabled-by-htmx')
}
})
}
@ -3382,10 +3396,10 @@ var htmx = (function() {
function overrideFormData(receiver, donor) {
for (const key of donor.keys()) {
receiver.delete(key)
donor.getAll(key).forEach(function(value) {
receiver.append(key, value)
})
}
donor.forEach(function(value, key) {
receiver.append(key, value)
})
return receiver
}
@ -3915,7 +3929,7 @@ var htmx = (function() {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key].forEach === 'function') {
obj[key].forEach(function(v) { formData.append(key, v) })
} else if (typeof obj[key] === 'object') {
} else if (typeof obj[key] === 'object' && !(obj[key] instanceof Blob)) {
formData.append(key, JSON.stringify(obj[key]))
} else {
formData.append(key, obj[key])
@ -4008,6 +4022,8 @@ var htmx = (function() {
target.delete(name)
if (typeof value.forEach === 'function') {
value.forEach(function(v) { target.append(name, v) })
} else if (typeof value === 'object' && !(value instanceof Blob)) {
target.append(name, JSON.stringify(value))
} else {
target.append(name, value)
}
@ -4343,7 +4359,9 @@ var htmx = (function() {
const hierarchy = hierarchyForElt(elt)
responseInfo.pathInfo.responsePath = getPathFromResponse(xhr)
responseHandler(elt, responseInfo)
removeRequestIndicators(indicators, disableElts)
if (responseInfo.keepIndicators !== true) {
removeRequestIndicators(indicators, disableElts)
}
triggerEvent(elt, 'htmx:afterRequest', responseInfo)
triggerEvent(elt, 'htmx:afterOnLoad', responseInfo)
// if the body no longer contains the element, trigger the event on the closest parent
@ -4583,12 +4601,14 @@ var htmx = (function() {
const shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && xhr.getResponseHeader('HX-Refresh') === 'true'
if (hasHeader(xhr, /HX-Redirect:/i)) {
responseInfo.keepIndicators = true
location.href = xhr.getResponseHeader('HX-Redirect')
shouldRefresh && location.reload()
return
}
if (shouldRefresh) {
responseInfo.keepIndicators = true
location.reload()
return
}
@ -5072,6 +5092,7 @@ var htmx = (function() {
* @property {{requestPath: string, finalRequestPath: string, responsePath: string|null, anchor: string}} pathInfo
* @property {boolean} [failed]
* @property {boolean} [successful]
* @property {boolean} [keepIndicators]
*/
/**
@ -5121,13 +5142,14 @@ var htmx = (function() {
*/
/**
* @see https://github.com/bigskysoftware/htmx-extensions/blob/main/README.md
* @typedef {Object} HtmxExtension
* @see https://htmx.org/extensions/#defining
* @property {(api: any) => void} init
* @property {(name: string, event: Event|CustomEvent) => boolean} onEvent
* @property {(text: string, xhr: XMLHttpRequest, elt: Element) => string} transformResponse
* @property {(swapStyle: HtmxSwapStyle) => boolean} isInlineSwap
* @property {(swapStyle: HtmxSwapStyle, target: Element, fragment: Node, settleInfo: HtmxSettleInfo) => boolean} handleSwap
* @property {(xhr: XMLHttpRequest, parameters: FormData, elt: Element) => *|string|null} encodeParameters
* @property {(swapStyle: HtmxSwapStyle, target: Node, fragment: Node, settleInfo: HtmxSettleInfo) => boolean|Node[]} handleSwap
* @property {(xhr: XMLHttpRequest, parameters: FormData, elt: Node) => *|string|null} encodeParameters
* @property {() => string[]|null} getSelectors
*/
module.exports = htmx;

1
dist/htmx.esm.d.ts vendored
View File

@ -95,6 +95,7 @@ export type HtmxResponseInfo = {
};
failed?: boolean;
successful?: boolean;
keepIndicators?: boolean;
};
export type HtmxAjaxEtc = {
returnPromise?: boolean;

64
dist/htmx.esm.js vendored
View File

@ -206,7 +206,7 @@ var htmx = (function() {
disableSelector: '[hx-disable], [data-hx-disable]',
/**
* @type {'auto' | 'instant' | 'smooth'}
* @default 'smooth'
* @default 'instant'
*/
scrollBehavior: 'instant',
/**
@ -277,7 +277,7 @@ var htmx = (function() {
parseInterval: null,
/** @type {typeof internalEval} */
_: null,
version: '2.0.1'
version: '2.0.2'
}
// Tsc madness part 2
htmx.onLoad = onLoadHelper
@ -1639,13 +1639,13 @@ var htmx = (function() {
newElt = eltBeforeNewContent.nextSibling
}
settleInfo.elts = settleInfo.elts.filter(function(e) { return e !== target })
// scan through all newly added content and add all elements to the settle info so we trigger
// events properly on them
while (newElt && newElt !== target) {
if (newElt instanceof Element) {
settleInfo.elts.push(newElt)
newElt = newElt.nextElementSibling
} else {
newElt = null
}
newElt = newElt.nextSibling
}
cleanUpElement(target)
if (target instanceof Element) {
@ -1753,7 +1753,7 @@ var htmx = (function() {
try {
const newElements = ext.handleSwap(swapStyle, target, fragment, settleInfo)
if (newElements) {
if (typeof newElements.length !== 'undefined') {
if (Array.isArray(newElements)) {
// if handleSwap returns an array (like) of elements, we handle them
for (let j = 0; j < newElements.length; j++) {
const child = newElements[j]
@ -1781,7 +1781,8 @@ var htmx = (function() {
* @param {HtmxSettleInfo} settleInfo
*/
function findAndSwapOobElements(fragment, settleInfo) {
forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function(oobElement) {
var oobElts = findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]')
forEach(oobElts, function(oobElement) {
if (htmx.config.allowNestedOobSwaps || oobElement.parentElement === null) {
const oobValue = getAttributeValue(oobElement, 'hx-swap-oob')
if (oobValue != null) {
@ -1792,6 +1793,7 @@ var htmx = (function() {
oobElement.removeAttribute('data-hx-swap-oob')
}
})
return oobElts.length > 0
}
/**
@ -1853,9 +1855,8 @@ var htmx = (function() {
// oob swaps
findAndSwapOobElements(fragment, settleInfo)
forEach(findAll(fragment, 'template'), /** @param {HTMLTemplateElement} template */function(template) {
findAndSwapOobElements(template.content, settleInfo)
if (template.content.childElementCount === 0 && template.content.textContent.trim() === '') {
// Avoid polluting the DOM with empty templates that were only used to encapsulate oob swap
if (findAndSwapOobElements(template.content, settleInfo)) {
// Avoid polluting the DOM with empty templates that were only used to encapsulate oob swap
template.remove()
}
})
@ -1952,7 +1953,10 @@ var htmx = (function() {
for (const eventName in triggers) {
if (triggers.hasOwnProperty(eventName)) {
let detail = triggers[eventName]
if (!isRawObject(detail)) {
if (isRawObject(detail)) {
// @ts-ignore
elt = detail.target !== undefined ? detail.target : elt
} else {
detail = { value: detail }
}
triggerEvent(elt, eventName, detail)
@ -2273,7 +2277,7 @@ var htmx = (function() {
* @param {HtmxTriggerSpecification[]} triggerSpecs
*/
function boostElement(elt, nodeData, triggerSpecs) {
if ((elt instanceof HTMLAnchorElement && isLocalLink(elt) && (elt.target === '' || elt.target === '_self')) || elt.tagName === 'FORM') {
if ((elt instanceof HTMLAnchorElement && isLocalLink(elt) && (elt.target === '' || elt.target === '_self')) || (elt.tagName === 'FORM' && String(getRawAttribute(elt, 'method')).toLowerCase() !== 'dialog')) {
nodeData.boosted = true
let verb, path
if (elt.tagName === 'A') {
@ -2435,13 +2439,17 @@ var htmx = (function() {
if (triggerSpec.throttle > 0) {
if (!elementData.throttle) {
triggerEvent(elt, 'htmx:trigger')
handler(elt, evt)
elementData.throttle = getWindow().setTimeout(function() {
elementData.throttle = null
}, triggerSpec.throttle)
}
} else if (triggerSpec.delay > 0) {
elementData.delayed = getWindow().setTimeout(function() { handler(elt, evt) }, triggerSpec.delay)
elementData.delayed = getWindow().setTimeout(function() {
triggerEvent(elt, 'htmx:trigger')
handler(elt, evt)
}, triggerSpec.delay)
} else {
triggerEvent(elt, 'htmx:trigger')
handler(elt, evt)
@ -3059,6 +3067,10 @@ var htmx = (function() {
forEach(findAll(clone, '.' + className), function(child) {
removeClassFromElement(child, className)
})
// remove the disabled attribute for any element disabled due to an htmx request
forEach(findAll(clone, '[data-disabled-by-htmx]'), function(child) {
child.removeAttribute('disabled')
})
return clone.innerHTML
}
@ -3212,6 +3224,7 @@ var htmx = (function() {
const internalData = getInternalData(disabledElement)
internalData.requestCount = (internalData.requestCount || 0) + 1
disabledElement.setAttribute('disabled', '')
disabledElement.setAttribute('data-disabled-by-htmx', '')
})
return disabledElts
}
@ -3233,6 +3246,7 @@ var htmx = (function() {
internalData.requestCount = (internalData.requestCount || 0) - 1
if (internalData.requestCount === 0) {
disabledElement.removeAttribute('disabled')
disabledElement.removeAttribute('data-disabled-by-htmx')
}
})
}
@ -3382,10 +3396,10 @@ var htmx = (function() {
function overrideFormData(receiver, donor) {
for (const key of donor.keys()) {
receiver.delete(key)
donor.getAll(key).forEach(function(value) {
receiver.append(key, value)
})
}
donor.forEach(function(value, key) {
receiver.append(key, value)
})
return receiver
}
@ -3915,7 +3929,7 @@ var htmx = (function() {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key].forEach === 'function') {
obj[key].forEach(function(v) { formData.append(key, v) })
} else if (typeof obj[key] === 'object') {
} else if (typeof obj[key] === 'object' && !(obj[key] instanceof Blob)) {
formData.append(key, JSON.stringify(obj[key]))
} else {
formData.append(key, obj[key])
@ -4008,6 +4022,8 @@ var htmx = (function() {
target.delete(name)
if (typeof value.forEach === 'function') {
value.forEach(function(v) { target.append(name, v) })
} else if (typeof value === 'object' && !(value instanceof Blob)) {
target.append(name, JSON.stringify(value))
} else {
target.append(name, value)
}
@ -4343,7 +4359,9 @@ var htmx = (function() {
const hierarchy = hierarchyForElt(elt)
responseInfo.pathInfo.responsePath = getPathFromResponse(xhr)
responseHandler(elt, responseInfo)
removeRequestIndicators(indicators, disableElts)
if (responseInfo.keepIndicators !== true) {
removeRequestIndicators(indicators, disableElts)
}
triggerEvent(elt, 'htmx:afterRequest', responseInfo)
triggerEvent(elt, 'htmx:afterOnLoad', responseInfo)
// if the body no longer contains the element, trigger the event on the closest parent
@ -4583,12 +4601,14 @@ var htmx = (function() {
const shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && xhr.getResponseHeader('HX-Refresh') === 'true'
if (hasHeader(xhr, /HX-Redirect:/i)) {
responseInfo.keepIndicators = true
location.href = xhr.getResponseHeader('HX-Redirect')
shouldRefresh && location.reload()
return
}
if (shouldRefresh) {
responseInfo.keepIndicators = true
location.reload()
return
}
@ -5072,6 +5092,7 @@ var htmx = (function() {
* @property {{requestPath: string, finalRequestPath: string, responsePath: string|null, anchor: string}} pathInfo
* @property {boolean} [failed]
* @property {boolean} [successful]
* @property {boolean} [keepIndicators]
*/
/**
@ -5121,13 +5142,14 @@ var htmx = (function() {
*/
/**
* @see https://github.com/bigskysoftware/htmx-extensions/blob/main/README.md
* @typedef {Object} HtmxExtension
* @see https://htmx.org/extensions/#defining
* @property {(api: any) => void} init
* @property {(name: string, event: Event|CustomEvent) => boolean} onEvent
* @property {(text: string, xhr: XMLHttpRequest, elt: Element) => string} transformResponse
* @property {(swapStyle: HtmxSwapStyle) => boolean} isInlineSwap
* @property {(swapStyle: HtmxSwapStyle, target: Element, fragment: Node, settleInfo: HtmxSettleInfo) => boolean} handleSwap
* @property {(xhr: XMLHttpRequest, parameters: FormData, elt: Element) => *|string|null} encodeParameters
* @property {(swapStyle: HtmxSwapStyle, target: Node, fragment: Node, settleInfo: HtmxSettleInfo) => boolean|Node[]} handleSwap
* @property {(xhr: XMLHttpRequest, parameters: FormData, elt: Node) => *|string|null} encodeParameters
* @property {() => string[]|null} getSelectors
*/
export default htmx

64
dist/htmx.js vendored
View File

@ -206,7 +206,7 @@ var htmx = (function() {
disableSelector: '[hx-disable], [data-hx-disable]',
/**
* @type {'auto' | 'instant' | 'smooth'}
* @default 'smooth'
* @default 'instant'
*/
scrollBehavior: 'instant',
/**
@ -277,7 +277,7 @@ var htmx = (function() {
parseInterval: null,
/** @type {typeof internalEval} */
_: null,
version: '2.0.1'
version: '2.0.2'
}
// Tsc madness part 2
htmx.onLoad = onLoadHelper
@ -1639,13 +1639,13 @@ var htmx = (function() {
newElt = eltBeforeNewContent.nextSibling
}
settleInfo.elts = settleInfo.elts.filter(function(e) { return e !== target })
// scan through all newly added content and add all elements to the settle info so we trigger
// events properly on them
while (newElt && newElt !== target) {
if (newElt instanceof Element) {
settleInfo.elts.push(newElt)
newElt = newElt.nextElementSibling
} else {
newElt = null
}
newElt = newElt.nextSibling
}
cleanUpElement(target)
if (target instanceof Element) {
@ -1753,7 +1753,7 @@ var htmx = (function() {
try {
const newElements = ext.handleSwap(swapStyle, target, fragment, settleInfo)
if (newElements) {
if (typeof newElements.length !== 'undefined') {
if (Array.isArray(newElements)) {
// if handleSwap returns an array (like) of elements, we handle them
for (let j = 0; j < newElements.length; j++) {
const child = newElements[j]
@ -1781,7 +1781,8 @@ var htmx = (function() {
* @param {HtmxSettleInfo} settleInfo
*/
function findAndSwapOobElements(fragment, settleInfo) {
forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function(oobElement) {
var oobElts = findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]')
forEach(oobElts, function(oobElement) {
if (htmx.config.allowNestedOobSwaps || oobElement.parentElement === null) {
const oobValue = getAttributeValue(oobElement, 'hx-swap-oob')
if (oobValue != null) {
@ -1792,6 +1793,7 @@ var htmx = (function() {
oobElement.removeAttribute('data-hx-swap-oob')
}
})
return oobElts.length > 0
}
/**
@ -1853,9 +1855,8 @@ var htmx = (function() {
// oob swaps
findAndSwapOobElements(fragment, settleInfo)
forEach(findAll(fragment, 'template'), /** @param {HTMLTemplateElement} template */function(template) {
findAndSwapOobElements(template.content, settleInfo)
if (template.content.childElementCount === 0 && template.content.textContent.trim() === '') {
// Avoid polluting the DOM with empty templates that were only used to encapsulate oob swap
if (findAndSwapOobElements(template.content, settleInfo)) {
// Avoid polluting the DOM with empty templates that were only used to encapsulate oob swap
template.remove()
}
})
@ -1952,7 +1953,10 @@ var htmx = (function() {
for (const eventName in triggers) {
if (triggers.hasOwnProperty(eventName)) {
let detail = triggers[eventName]
if (!isRawObject(detail)) {
if (isRawObject(detail)) {
// @ts-ignore
elt = detail.target !== undefined ? detail.target : elt
} else {
detail = { value: detail }
}
triggerEvent(elt, eventName, detail)
@ -2273,7 +2277,7 @@ var htmx = (function() {
* @param {HtmxTriggerSpecification[]} triggerSpecs
*/
function boostElement(elt, nodeData, triggerSpecs) {
if ((elt instanceof HTMLAnchorElement && isLocalLink(elt) && (elt.target === '' || elt.target === '_self')) || elt.tagName === 'FORM') {
if ((elt instanceof HTMLAnchorElement && isLocalLink(elt) && (elt.target === '' || elt.target === '_self')) || (elt.tagName === 'FORM' && String(getRawAttribute(elt, 'method')).toLowerCase() !== 'dialog')) {
nodeData.boosted = true
let verb, path
if (elt.tagName === 'A') {
@ -2435,13 +2439,17 @@ var htmx = (function() {
if (triggerSpec.throttle > 0) {
if (!elementData.throttle) {
triggerEvent(elt, 'htmx:trigger')
handler(elt, evt)
elementData.throttle = getWindow().setTimeout(function() {
elementData.throttle = null
}, triggerSpec.throttle)
}
} else if (triggerSpec.delay > 0) {
elementData.delayed = getWindow().setTimeout(function() { handler(elt, evt) }, triggerSpec.delay)
elementData.delayed = getWindow().setTimeout(function() {
triggerEvent(elt, 'htmx:trigger')
handler(elt, evt)
}, triggerSpec.delay)
} else {
triggerEvent(elt, 'htmx:trigger')
handler(elt, evt)
@ -3059,6 +3067,10 @@ var htmx = (function() {
forEach(findAll(clone, '.' + className), function(child) {
removeClassFromElement(child, className)
})
// remove the disabled attribute for any element disabled due to an htmx request
forEach(findAll(clone, '[data-disabled-by-htmx]'), function(child) {
child.removeAttribute('disabled')
})
return clone.innerHTML
}
@ -3212,6 +3224,7 @@ var htmx = (function() {
const internalData = getInternalData(disabledElement)
internalData.requestCount = (internalData.requestCount || 0) + 1
disabledElement.setAttribute('disabled', '')
disabledElement.setAttribute('data-disabled-by-htmx', '')
})
return disabledElts
}
@ -3233,6 +3246,7 @@ var htmx = (function() {
internalData.requestCount = (internalData.requestCount || 0) - 1
if (internalData.requestCount === 0) {
disabledElement.removeAttribute('disabled')
disabledElement.removeAttribute('data-disabled-by-htmx')
}
})
}
@ -3382,10 +3396,10 @@ var htmx = (function() {
function overrideFormData(receiver, donor) {
for (const key of donor.keys()) {
receiver.delete(key)
donor.getAll(key).forEach(function(value) {
receiver.append(key, value)
})
}
donor.forEach(function(value, key) {
receiver.append(key, value)
})
return receiver
}
@ -3915,7 +3929,7 @@ var htmx = (function() {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key].forEach === 'function') {
obj[key].forEach(function(v) { formData.append(key, v) })
} else if (typeof obj[key] === 'object') {
} else if (typeof obj[key] === 'object' && !(obj[key] instanceof Blob)) {
formData.append(key, JSON.stringify(obj[key]))
} else {
formData.append(key, obj[key])
@ -4008,6 +4022,8 @@ var htmx = (function() {
target.delete(name)
if (typeof value.forEach === 'function') {
value.forEach(function(v) { target.append(name, v) })
} else if (typeof value === 'object' && !(value instanceof Blob)) {
target.append(name, JSON.stringify(value))
} else {
target.append(name, value)
}
@ -4343,7 +4359,9 @@ var htmx = (function() {
const hierarchy = hierarchyForElt(elt)
responseInfo.pathInfo.responsePath = getPathFromResponse(xhr)
responseHandler(elt, responseInfo)
removeRequestIndicators(indicators, disableElts)
if (responseInfo.keepIndicators !== true) {
removeRequestIndicators(indicators, disableElts)
}
triggerEvent(elt, 'htmx:afterRequest', responseInfo)
triggerEvent(elt, 'htmx:afterOnLoad', responseInfo)
// if the body no longer contains the element, trigger the event on the closest parent
@ -4583,12 +4601,14 @@ var htmx = (function() {
const shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && xhr.getResponseHeader('HX-Refresh') === 'true'
if (hasHeader(xhr, /HX-Redirect:/i)) {
responseInfo.keepIndicators = true
location.href = xhr.getResponseHeader('HX-Redirect')
shouldRefresh && location.reload()
return
}
if (shouldRefresh) {
responseInfo.keepIndicators = true
location.reload()
return
}
@ -5072,6 +5092,7 @@ var htmx = (function() {
* @property {{requestPath: string, finalRequestPath: string, responsePath: string|null, anchor: string}} pathInfo
* @property {boolean} [failed]
* @property {boolean} [successful]
* @property {boolean} [keepIndicators]
*/
/**
@ -5121,12 +5142,13 @@ var htmx = (function() {
*/
/**
* @see https://github.com/bigskysoftware/htmx-extensions/blob/main/README.md
* @typedef {Object} HtmxExtension
* @see https://htmx.org/extensions/#defining
* @property {(api: any) => void} init
* @property {(name: string, event: Event|CustomEvent) => boolean} onEvent
* @property {(text: string, xhr: XMLHttpRequest, elt: Element) => string} transformResponse
* @property {(swapStyle: HtmxSwapStyle) => boolean} isInlineSwap
* @property {(swapStyle: HtmxSwapStyle, target: Element, fragment: Node, settleInfo: HtmxSettleInfo) => boolean} handleSwap
* @property {(xhr: XMLHttpRequest, parameters: FormData, elt: Element) => *|string|null} encodeParameters
* @property {(swapStyle: HtmxSwapStyle, target: Node, fragment: Node, settleInfo: HtmxSettleInfo) => boolean|Node[]} handleSwap
* @property {(xhr: XMLHttpRequest, parameters: FormData, elt: Node) => *|string|null} encodeParameters
* @property {() => string[]|null} getSelectors
*/

2
dist/htmx.min.js vendored

File diff suppressed because one or more lines are too long

BIN
dist/htmx.min.js.gz vendored

Binary file not shown.

View File

@ -5,7 +5,7 @@
"AJAX",
"HTML"
],
"version": "2.0.1",
"version": "2.0.2",
"homepage": "https://htmx.org/",
"bugs": {
"url": "https://github.com/bigskysoftware/htmx/issues"

View File

@ -277,7 +277,7 @@ var htmx = (function() {
parseInterval: null,
/** @type {typeof internalEval} */
_: null,
version: '2.0.1'
version: '2.0.2'
}
// Tsc madness part 2
htmx.onLoad = onLoadHelper
@ -1781,7 +1781,7 @@ var htmx = (function() {
* @param {HtmxSettleInfo} settleInfo
*/
function findAndSwapOobElements(fragment, settleInfo) {
var oobElts = findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]');
var oobElts = findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]')
forEach(oobElts, function(oobElement) {
if (htmx.config.allowNestedOobSwaps || oobElement.parentElement === null) {
const oobValue = getAttributeValue(oobElement, 'hx-swap-oob')
@ -1793,7 +1793,7 @@ var htmx = (function() {
oobElement.removeAttribute('data-hx-swap-oob')
}
})
return oobElts.length > 0;
return oobElts.length > 0
}
/**
@ -3069,7 +3069,7 @@ var htmx = (function() {
})
// remove the disabled attribute for any element disabled due to an htmx request
forEach(findAll(clone, '[data-disabled-by-htmx]'), function(child) {
child.removeAttribute('disabled');
child.removeAttribute('disabled')
})
return clone.innerHTML
}
@ -4360,7 +4360,7 @@ var htmx = (function() {
responseInfo.pathInfo.responsePath = getPathFromResponse(xhr)
responseHandler(elt, responseInfo)
if (responseInfo.keepIndicators !== true) {
removeRequestIndicators(indicators, disableElts);
removeRequestIndicators(indicators, disableElts)
}
triggerEvent(elt, 'htmx:afterRequest', responseInfo)
triggerEvent(elt, 'htmx:afterOnLoad', responseInfo)
@ -4601,14 +4601,14 @@ var htmx = (function() {
const shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && xhr.getResponseHeader('HX-Refresh') === 'true'
if (hasHeader(xhr, /HX-Redirect:/i)) {
responseInfo.keepIndicators = true;
responseInfo.keepIndicators = true
location.href = xhr.getResponseHeader('HX-Redirect')
shouldRefresh && location.reload()
return
}
if (shouldRefresh) {
responseInfo.keepIndicators = true;
responseInfo.keepIndicators = true
location.reload()
return
}

View File

@ -181,11 +181,11 @@ describe('hx-swap-oob attribute', function() {
'Clicked<template><div hx-swap-oob="outerHTML" id="d1">Foo</div></template>')
var div = make('<button hx-get="/test" id="b1">Click Me</button>' +
'<div id="d1" ></div>')
var btn = byId('b1');
var btn = byId('b1')
btn.click()
this.server.respond()
should.equal(byId('b1').innerHTML, "Clicked")
should.equal(byId('d1').innerHTML, "Foo")
should.equal(byId('b1').innerHTML, 'Clicked')
should.equal(byId('d1').innerHTML, 'Foo')
})
it('oob swap keeps templates not used for oob swap encapsulation', function() {
@ -193,11 +193,11 @@ describe('hx-swap-oob attribute', function() {
'Clicked<template></template>')
var div = make('<button hx-get="/test" id="b1">Click Me</button>' +
'<div id="d1" ></div>')
var btn = byId('b1');
var btn = byId('b1')
btn.click()
this.server.respond()
should.equal(byId('b1').innerHTML, "Clicked<template></template>")
should.equal(byId('d1').innerHTML, "")
should.equal(byId('b1').innerHTML, 'Clicked<template></template>')
should.equal(byId('d1').innerHTML, '')
})
for (const config of [{ allowNestedOobSwaps: true }, { allowNestedOobSwaps: false }]) {

View File

@ -119,7 +119,7 @@ By removing these constraints, htmx completes HTML as a [hypertext](https://en.w
<h2>quick start</h2>
```html
<script src="https://unpkg.com/htmx.org@2.0.1"></script>
<script src="https://unpkg.com/htmx.org@2.0.2"></script>
<!-- have a button POST a click via AJAX -->
<button hx-post="/clicked" hx-swap="outerHTML">
Click Me

View File

@ -121,13 +121,13 @@ The fastest way to get going with htmx is to load it via a CDN. You can simply a
your head tag and get going:
```html
<script src="https://unpkg.com/htmx.org@2.0.1" integrity="sha384-QWGpdj554B4ETpJJC9z+ZHJcA/i59TyjxEPXiiUgN2WmTyV5OEZWCD6gQhgkdpB/" crossorigin="anonymous"></script>
<script src="https://unpkg.com/htmx.org@2.0.2" integrity="sha384-QWGpdj554B4ETpJJC9z+ZHJcA/i59TyjxEPXiiUgN2WmTyV5OEZWCD6gQhgkdpB/" crossorigin="anonymous"></script>
```
An unminified version is also available for debugging as well:
```html
<script src="https://unpkg.com/htmx.org@2.0.1/dist/htmx.js" integrity="sha384-gpIh5aLQ0qmX8kZdyhsd6jA24uKLkqIr1WAGtantR4KsS97l/NRBvh8/8OYGThAf" crossorigin="anonymous"></script>
<script src="https://unpkg.com/htmx.org@2.02/dist/htmx.js" integrity="sha384-gpIh5aLQ0qmX8kZdyhsd6jA24uKLkqIr1WAGtantR4KsS97l/NRBvh8/8OYGThAf" crossorigin="anonymous"></script>
```
While the CDN approach is extremely simple, you may want to consider