mirror of
https://github.com/bigskysoftware/htmx.git
synced 2026-01-21 16:16:05 +00:00
update web
This commit is contained in:
parent
7972c4ea5a
commit
0df6c98e4e
@ -10,7 +10,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
htmx.defineExtension('compat', {
|
||||
htmx.registerExtension('compat', {
|
||||
init: (internalAPI) => {
|
||||
api = internalAPI;
|
||||
|
||||
@ -24,14 +24,68 @@
|
||||
htmx.config.noSwap.push("4xx", "5xx");
|
||||
}
|
||||
},
|
||||
htmx_after_implicitInheritance : function(elt, detail) {
|
||||
// TODO - how should we alert users? collect a report? just log?
|
||||
// TODO - needs a config option to enable
|
||||
// Re-delegate new events to old event names for backwards compatibility
|
||||
htmx_after_implicitInheritance: function (elt, detail) {
|
||||
if (!htmx.config.compat?.suppressInheritanceLogs) {
|
||||
console.log("IMPLICIT INHERITANCE DETECTED, attribute: " + detail.name + ", elt: ", elt, ", inherited from: ", detail.parent)
|
||||
let evt = new CustomEvent("htmxImplicitInheritace", {
|
||||
detail,
|
||||
cancelable: true,
|
||||
bubbles : true,
|
||||
composed: true,
|
||||
});
|
||||
elt.dispatchEvent(evt)
|
||||
}
|
||||
},
|
||||
// TODO - catch all new events and redelegate to old event names as best we can
|
||||
// this can probably be done as a map... See www/content/migration-guide-htmx-4.md:94
|
||||
htmx_after_init: function (elt, detail) {
|
||||
maybeRetriggerEvent(elt, "htmx:afterOnLoad", detail);
|
||||
maybeRetriggerEvent(elt, "htmx:afterProcessNode", detail);
|
||||
maybeRetriggerEvent(elt, "htmx:load", detail);
|
||||
},
|
||||
htmx_after_request: function (elt, detail) {
|
||||
maybeRetriggerEvent(elt, "htmx:afterRequest", detail);
|
||||
},
|
||||
htmx_after_swap: function (elt, detail) {
|
||||
maybeRetriggerEvent(elt, "htmx:afterSettle", detail);
|
||||
maybeRetriggerEvent(elt, "htmx:afterSwap", detail);
|
||||
},
|
||||
htmx_before_cleanup: function (elt, detail) {
|
||||
maybeRetriggerEvent(elt, "htmx:beforeCleanupElement", detail);
|
||||
},
|
||||
htmx_before_history_update: function (elt, detail) {
|
||||
maybeRetriggerEvent(elt, "htmx:beforeHistoryUpdate", detail);
|
||||
maybeRetriggerEvent(elt, "htmx:beforeHistorySave", detail);
|
||||
},
|
||||
htmx_before_init: function (elt, detail) {
|
||||
maybeRetriggerEvent(elt, "htmx:beforeOnLoad", detail);
|
||||
},
|
||||
htmx_before_process: function (elt, detail) {
|
||||
maybeRetriggerEvent(elt, "htmx:beforeProcessNode", detail);
|
||||
},
|
||||
htmx_before_request: function (elt, detail) {
|
||||
maybeRetriggerEvent(elt, "htmx:beforeRequest", detail);
|
||||
maybeRetriggerEvent(elt, "htmx:beforeSend", detail);
|
||||
},
|
||||
htmx_before_swap: function (elt, detail) {
|
||||
maybeRetriggerEvent(elt, "htmx:beforeSwap", detail);
|
||||
},
|
||||
htmx_before_viewTransition: function (elt, detail) {
|
||||
maybeRetriggerEvent(elt, "htmx:beforeTransition", detail);
|
||||
},
|
||||
htmx_config_request: function (elt, detail) {
|
||||
maybeRetriggerEvent(elt, "htmx:configRequest", detail);
|
||||
},
|
||||
htmx_before_restore_history: function (elt, detail) {
|
||||
maybeRetriggerEvent(elt, "htmx:historyRestore", detail);
|
||||
},
|
||||
htmx_after_push_into_history: function (elt, detail) {
|
||||
maybeRetriggerEvent(elt, "htmx:pushedIntoHistory", detail);
|
||||
},
|
||||
htmx_after_replace_into_history: function (elt, detail) {
|
||||
maybeRetriggerEvent(elt, "htmx:replacedInHistory", detail);
|
||||
},
|
||||
htmx_error: function (elt, detail) {
|
||||
maybeRetriggerEvent(elt, "htmx:targetError", detail);
|
||||
},
|
||||
});
|
||||
})()
|
||||
Binary file not shown.
2
www/static/js/ext/hx-compat.min.js
vendored
2
www/static/js/ext/hx-compat.min.js
vendored
@ -1 +1 @@
|
||||
(()=>{let t;htmx.defineExtension("compat",{init:i=>{t=i,htmx.config.compat?.useExplicitInheritace||(htmx.config.implicitInheritance=!0),htmx.config.compat?.swapErrorResponseCodes||htmx.config.noSwap.push("4xx","5xx")},htmx_after_implicitInheritance:function(t,i){},htmx_after_init:function(t,i){!function(t,i,n){htmx.config.compat?.doNotTriggerOldEvents||htmx.trigger(t,i,n)}(t,"htmx:afterOnLoad",i)}})})();
|
||||
(()=>{let t;function e(t,e,o){htmx.config.compat?.doNotTriggerOldEvents||htmx.trigger(t,e,o)}htmx.registerExtension("compat",{init:e=>{t=e,htmx.config.compat?.useExplicitInheritace||(htmx.config.implicitInheritance=!0),htmx.config.compat?.swapErrorResponseCodes||htmx.config.noSwap.push("4xx","5xx")},htmx_after_implicitInheritance:function(t,e){if(!htmx.config.compat?.suppressInheritanceLogs){console.log("IMPLICIT INHERITANCE DETECTED, attribute: "+e.name+", elt: ",t,", inherited from: ",e.parent);let o=new CustomEvent("htmxImplicitInheritace",{detail:e,cancelable:!0,bubbles:!0,composed:!0});t.dispatchEvent(o)}},htmx_after_init:function(t,o){e(t,"htmx:afterOnLoad",o),e(t,"htmx:afterProcessNode",o),e(t,"htmx:load",o)},htmx_after_request:function(t,o){e(t,"htmx:afterRequest",o)},htmx_after_swap:function(t,o){e(t,"htmx:afterSettle",o),e(t,"htmx:afterSwap",o)},htmx_before_cleanup:function(t,o){e(t,"htmx:beforeCleanupElement",o)},htmx_before_history_update:function(t,o){e(t,"htmx:beforeHistoryUpdate",o),e(t,"htmx:beforeHistorySave",o)},htmx_before_init:function(t,o){e(t,"htmx:beforeOnLoad",o)},htmx_before_process:function(t,o){e(t,"htmx:beforeProcessNode",o)},htmx_before_request:function(t,o){e(t,"htmx:beforeRequest",o),e(t,"htmx:beforeSend",o)},htmx_before_swap:function(t,o){e(t,"htmx:beforeSwap",o)},htmx_before_viewTransition:function(t,o){e(t,"htmx:beforeTransition",o)},htmx_config_request:function(t,o){e(t,"htmx:configRequest",o)},htmx_before_restore_history:function(t,o){e(t,"htmx:historyRestore",o)},htmx_after_push_into_history:function(t,o){e(t,"htmx:pushedIntoHistory",o)},htmx_after_replace_into_history:function(t,o){e(t,"htmx:replacedInHistory",o)},htmx_error:function(t,o){e(t,"htmx:targetError",o)}})})();
|
||||
Binary file not shown.
@ -1 +1 @@
|
||||
{"version":3,"names":["api","htmx","defineExtension","init","internalAPI","config","compat","useExplicitInheritace","implicitInheritance","swapErrorResponseCodes","noSwap","push","htmx_after_implicitInheritance","elt","detail","htmx_after_init","evtName","doNotTriggerOldEvents","trigger","maybeRetriggerEvent"],"sources":["dist/ext/hx-compat.js"],"mappings":"AAAA,MAII,IAAIA,EAQJC,KAAKC,gBAAgB,SAAU,CAC3BC,KAAOC,IACHJ,EAAMI,EAGDH,KAAKI,OAAOC,QAAQC,wBACrBN,KAAKI,OAAOG,qBAAsB,GAIjCP,KAAKI,OAAOC,QAAQG,wBACrBR,KAAKI,OAAOK,OAAOC,KAAK,MAAO,QAGvCC,+BAAiC,SAASC,EAAKC,GAG/C,EAGAC,gBAAiB,SAAUF,EAAKC,IA1BpC,SAA6BD,EAAKG,EAASF,GAClCb,KAAKI,OAAOC,QAAQW,uBACrBhB,KAAKiB,QAAQL,EAAKG,EAASF,EAEnC,CAuBQK,CAAoBN,EAAK,mBAAoBC,EACjD,GAEP,EApCD","ignoreList":[]}
|
||||
{"version":3,"names":["api","maybeRetriggerEvent","elt","evtName","detail","htmx","config","compat","doNotTriggerOldEvents","trigger","registerExtension","init","internalAPI","useExplicitInheritace","implicitInheritance","swapErrorResponseCodes","noSwap","push","htmx_after_implicitInheritance","suppressInheritanceLogs","console","log","name","parent","evt","CustomEvent","cancelable","bubbles","composed","dispatchEvent","htmx_after_init","htmx_after_request","htmx_after_swap","htmx_before_cleanup","htmx_before_history_update","htmx_before_init","htmx_before_process","htmx_before_request","htmx_before_swap","htmx_before_viewTransition","htmx_config_request","htmx_before_restore_history","htmx_after_push_into_history","htmx_after_replace_into_history","htmx_error"],"sources":["dist/ext/hx-compat.js"],"mappings":"AAAA,MAII,IAAIA,EAEJ,SAASC,EAAoBC,EAAKC,EAASC,GAClCC,KAAKC,OAAOC,QAAQC,uBACrBH,KAAKI,QAAQP,EAAKC,EAASC,EAEnC,CAEAC,KAAKK,kBAAkB,SAAU,CAC7BC,KAAOC,IACHZ,EAAMY,EAGDP,KAAKC,OAAOC,QAAQM,wBACrBR,KAAKC,OAAOQ,qBAAsB,GAIjCT,KAAKC,OAAOC,QAAQQ,wBACrBV,KAAKC,OAAOU,OAAOC,KAAK,MAAO,QAIvCC,+BAAgC,SAAUhB,EAAKE,GAC3C,IAAKC,KAAKC,OAAOC,QAAQY,wBAAyB,CAC9CC,QAAQC,IAAI,6CAA+CjB,EAAOkB,KAAO,UAAWpB,EAAK,qBAAsBE,EAAOmB,QACtH,IAAIC,EAAM,IAAIC,YAAY,yBAA0B,CAChDrB,SACAsB,YAAY,EACZC,SAAU,EACVC,UAAU,IAEd1B,EAAI2B,cAAcL,EACtB,CACJ,EACAM,gBAAiB,SAAU5B,EAAKE,GAC5BH,EAAoBC,EAAK,mBAAoBE,GAC7CH,EAAoBC,EAAK,wBAAyBE,GAClDH,EAAoBC,EAAK,YAAaE,EAC1C,EACA2B,mBAAoB,SAAU7B,EAAKE,GAC/BH,EAAoBC,EAAK,oBAAqBE,EAClD,EACA4B,gBAAiB,SAAU9B,EAAKE,GAC5BH,EAAoBC,EAAK,mBAAoBE,GAC7CH,EAAoBC,EAAK,iBAAkBE,EAC/C,EACA6B,oBAAqB,SAAU/B,EAAKE,GAChCH,EAAoBC,EAAK,4BAA6BE,EAC1D,EACA8B,2BAA4B,SAAUhC,EAAKE,GACvCH,EAAoBC,EAAK,2BAA4BE,GACrDH,EAAoBC,EAAK,yBAA0BE,EACvD,EACA+B,iBAAkB,SAAUjC,EAAKE,GAC7BH,EAAoBC,EAAK,oBAAqBE,EAClD,EACAgC,oBAAqB,SAAUlC,EAAKE,GAChCH,EAAoBC,EAAK,yBAA0BE,EACvD,EACAiC,oBAAqB,SAAUnC,EAAKE,GAChCH,EAAoBC,EAAK,qBAAsBE,GAC/CH,EAAoBC,EAAK,kBAAmBE,EAChD,EACAkC,iBAAkB,SAAUpC,EAAKE,GAC7BH,EAAoBC,EAAK,kBAAmBE,EAChD,EACAmC,2BAA4B,SAAUrC,EAAKE,GACvCH,EAAoBC,EAAK,wBAAyBE,EACtD,EACAoC,oBAAqB,SAAUtC,EAAKE,GAChCH,EAAoBC,EAAK,qBAAsBE,EACnD,EACAqC,4BAA6B,SAAUvC,EAAKE,GACxCH,EAAoBC,EAAK,sBAAuBE,EACpD,EACAsC,6BAA8B,SAAUxC,EAAKE,GACzCH,EAAoBC,EAAK,yBAA0BE,EACvD,EACAuC,gCAAiC,SAAUzC,EAAKE,GAC5CH,EAAoBC,EAAK,yBAA0BE,EACvD,EACAwC,WAAY,SAAU1C,EAAKE,GACvBH,EAAoBC,EAAK,mBAAoBE,EACjD,GAEP,EA1FD","ignoreList":[]}
|
||||
129
www/static/js/ext/hx-head.js
Normal file
129
www/static/js/ext/hx-head.js
Normal file
@ -0,0 +1,129 @@
|
||||
//==========================================================
|
||||
// head-support.js
|
||||
//
|
||||
// An extension to add head tag merging.
|
||||
//==========================================================
|
||||
(function () {
|
||||
|
||||
let api
|
||||
|
||||
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
|
||||
let contentWithSvgsRemoved = newContent.replace(/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim, '')
|
||||
// extract head tag
|
||||
let headTag = contentWithSvgsRemoved.match(/(<head(\s[^>]*>|>)([\s\S]*?)<\/head>)/im)
|
||||
|
||||
// if the head tag exists...
|
||||
if (headTag) {
|
||||
|
||||
let added = []
|
||||
let removed = []
|
||||
let preserved = []
|
||||
let nodesToAppend = []
|
||||
|
||||
htmlDoc.innerHTML = headTag
|
||||
let newHeadTag = htmlDoc.querySelector("head")
|
||||
let currentHead = document.head
|
||||
|
||||
if (newHeadTag == null) {
|
||||
return
|
||||
}
|
||||
|
||||
// put all new head elements into a Map, by their outerHTML
|
||||
let srcToNewHeadNodes = new Map()
|
||||
for (const newHeadChild of newHeadTag.children) {
|
||||
srcToNewHeadNodes.set(newHeadChild.outerHTML, newHeadChild)
|
||||
}
|
||||
|
||||
// determine merge strategy
|
||||
let mergeStrategy = api.attributeValue(newHeadTag, "hx-head") || defaultMergeStrategy
|
||||
|
||||
// get the current head
|
||||
for (const currentHeadElt of currentHead.children) {
|
||||
|
||||
// If the current head element is in the map
|
||||
let inNewContent = srcToNewHeadNodes.has(currentHeadElt.outerHTML)
|
||||
let isReAppended = currentHeadElt.getAttribute("hx-head") === "re-eval"
|
||||
let isPreserved = api.attributeValue(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 (htmx.trigger(document.body, "htmx:before:head:remove", {headElement: currentHeadElt}) !== false) {
|
||||
removed.push(currentHeadElt)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Push the remaining 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)
|
||||
let newElt = document.createRange().createContextualFragment(newNode.outerHTML)
|
||||
log(newElt)
|
||||
if (htmx.trigger(document.body, "htmx:before:head:add", {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 (htmx.trigger(document.body, "htmx:before:head:remove", {headElement: removedElement}) !== false) {
|
||||
currentHead.removeChild(removedElement)
|
||||
}
|
||||
}
|
||||
|
||||
htmx.trigger(document.body, "htmx:after:head:merge", {
|
||||
added: added,
|
||||
kept: preserved,
|
||||
removed: removed
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
htmx.registerExtension("hx-head", {
|
||||
init: (internalAPI) => {
|
||||
api = internalAPI;
|
||||
},
|
||||
htmx_after_swap: (elt, detail) => {
|
||||
let ctx = detail.ctx
|
||||
let target = ctx.target
|
||||
// TODO - is there a better way to handle this? it used to be based on if the element was boosted
|
||||
let defaultMergeStrategy = target === document.body ? "merge" : "append";
|
||||
if (htmx.trigger(document.body, "htmx:before:head:merge", detail)) {
|
||||
mergeHead(ctx.text, defaultMergeStrategy)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
})()
|
||||
BIN
www/static/js/ext/hx-head.js.br
Normal file
BIN
www/static/js/ext/hx-head.js.br
Normal file
Binary file not shown.
1
www/static/js/ext/hx-head.min.js
vendored
Normal file
1
www/static/js/ext/hx-head.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
!function(){let e;function t(){}htmx.registerExtension("hx-head",{init:t=>{e=t},htmx_after_swap:(r,d)=>{let h=d.ctx,o=h.target===document.body?"merge":"append";htmx.trigger(document.body,"htmx:before:head:merge",d)&&function(r,d){if(r&&r.indexOf("<head")>-1){const h=document.createElement("html");let o=r.replace(/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim,"").match(/(<head(\s[^>]*>|>)([\s\S]*?)<\/head>)/im);if(o){let r=[],a=[],n=[],m=[];h.innerHTML=o;let u=h.querySelector("head"),l=document.head;if(null==u)return;let c=new Map;for(const e of u.children)c.set(e.outerHTML,e);let i=e.attributeValue(u,"hx-head")||d;for(const t of l.children){let r=c.has(t.outerHTML),d="re-eval"===t.getAttribute("hx-head"),h="true"===e.attributeValue(t,"hx-preserve");r||h?d?a.push(t):(c.delete(t.outerHTML),n.push(t)):"append"===i?d&&(a.push(t),m.push(t)):!1!==htmx.trigger(document.body,"htmx:before:head:remove",{headElement:t})&&a.push(t)}m.push(...c.values());for(const e of m){t();let d=document.createRange().createContextualFragment(e.outerHTML);t(),!1!==htmx.trigger(document.body,"htmx:before:head:add",{headElement:d})&&(l.appendChild(d),r.push(d))}for(const e of a)!1!==htmx.trigger(document.body,"htmx:before:head:remove",{headElement:e})&&l.removeChild(e);htmx.trigger(document.body,"htmx:after:head:merge",{added:r,kept:n,removed:a})}}}(h.text,o)}})}();
|
||||
BIN
www/static/js/ext/hx-head.min.js.br
Normal file
BIN
www/static/js/ext/hx-head.min.js.br
Normal file
Binary file not shown.
1
www/static/js/ext/hx-head.min.js.map
Normal file
1
www/static/js/ext/hx-head.min.js.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"names":["api","log","htmx","registerExtension","init","internalAPI","htmx_after_swap","elt","detail","ctx","defaultMergeStrategy","target","document","body","trigger","newContent","indexOf","htmlDoc","createElement","headTag","replace","match","added","removed","preserved","nodesToAppend","innerHTML","newHeadTag","querySelector","currentHead","head","srcToNewHeadNodes","Map","newHeadChild","children","set","outerHTML","mergeStrategy","attributeValue","currentHeadElt","inNewContent","has","isReAppended","getAttribute","isPreserved","push","delete","headElement","values","newNode","newElt","createRange","createContextualFragment","appendChild","removedElement","removeChild","kept","mergeHead","text"],"sources":["dist/ext/hx-head.js"],"mappings":"CAKA,WAEI,IAAIA,EAEJ,SAASC,IAET,CAsGAC,KAAKC,kBAAkB,UAAW,CAC9BC,KAAOC,IACHL,EAAMK,GAEVC,gBAAiB,CAACC,EAAKC,KACnB,IAAIC,EAAMD,EAAOC,IAGbC,EAFSD,EAAIE,SAEqBC,SAASC,KAAO,QAAU,SAC5DX,KAAKY,QAAQF,SAASC,KAAM,yBAA0BL,IA7GlE,SAAmBO,EAAYL,GAE3B,GAAIK,GAAcA,EAAWC,QAAQ,UAAY,EAAG,CAChD,MAAMC,EAAUL,SAASM,cAAc,QAEvC,IAEIC,EAFyBJ,EAAWK,QAAQ,uCAAwC,IAEnDC,MAAM,2CAG3C,GAAIF,EAAS,CAET,IAAIG,EAAQ,GACRC,EAAU,GACVC,EAAY,GACZC,EAAgB,GAEpBR,EAAQS,UAAYP,EACpB,IAAIQ,EAAaV,EAAQW,cAAc,QACnCC,EAAcjB,SAASkB,KAE3B,GAAkB,MAAdH,EACA,OAIJ,IAAII,EAAoB,IAAIC,IAC5B,IAAK,MAAMC,KAAgBN,EAAWO,SAClCH,EAAkBI,IAAIF,EAAaG,UAAWH,GAIlD,IAAII,EAAgBrC,EAAIsC,eAAeX,EAAY,YAAcjB,EAGjE,IAAK,MAAM6B,KAAkBV,EAAYK,SAAU,CAG/C,IAAIM,EAAeT,EAAkBU,IAAIF,EAAeH,WACpDM,EAA0D,YAA3CH,EAAeI,aAAa,WAC3CC,EAAoE,SAAtD5C,EAAIsC,eAAeC,EAAgB,eACjDC,GAAgBI,EACZF,EAEAnB,EAAQsB,KAAKN,IAIbR,EAAkBe,OAAOP,EAAeH,WACxCZ,EAAUqB,KAAKN,IAGG,WAAlBF,EAGIK,IACAnB,EAAQsB,KAAKN,GACbd,EAAcoB,KAAKN,KAIuE,IAA1FrC,KAAKY,QAAQF,SAASC,KAAM,0BAA2B,CAACkC,YAAaR,KACrEhB,EAAQsB,KAAKN,EAI7B,CAIAd,EAAcoB,QAAQd,EAAkBiB,UAGxC,IAAK,MAAMC,KAAWxB,EAAe,CACjCxB,IACA,IAAIiD,EAAStC,SAASuC,cAAcC,yBAAyBH,EAAQb,WACrEnC,KACmF,IAA/EC,KAAKY,QAAQF,SAASC,KAAM,uBAAwB,CAACkC,YAAaG,MAClErB,EAAYwB,YAAYH,GACxB5B,EAAMuB,KAAKK,GAEnB,CAIA,IAAK,MAAMI,KAAkB/B,GACqE,IAA1FrB,KAAKY,QAAQF,SAASC,KAAM,0BAA2B,CAACkC,YAAaO,KACrEzB,EAAY0B,YAAYD,GAIhCpD,KAAKY,QAAQF,SAASC,KAAM,wBAAyB,CACjDS,MAAOA,EACPkC,KAAMhC,EACND,QAASA,GAEjB,CACJ,CACJ,CAYYkC,CAAUhD,EAAIiD,KAAMhD,KAKnC,CA3HD","ignoreList":[]}
|
||||
@ -5,17 +5,26 @@
|
||||
let preloadSpec = api.attributeValue(elt, "hx-preload");
|
||||
if (!preloadSpec && !elt._htmx?.boosted) return;
|
||||
|
||||
let eventName;
|
||||
let timeout;
|
||||
let preloadEvents = []
|
||||
let timeout = 5000;
|
||||
if (preloadSpec) {
|
||||
let specs = api.parseTriggerSpecs(preloadSpec);
|
||||
if (specs.length === 0) return;
|
||||
let spec = specs[0];
|
||||
eventName = spec.name;
|
||||
timeout = spec.timeout ? htmx.parseInterval(spec.timeout) : 5000;
|
||||
for (const spec of specs) {
|
||||
preloadEvents.push(spec.name)
|
||||
if (spec.timeout) {
|
||||
timeout = htmx.parseInterval(spec.timeout)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eventName = htmx.config?.preload?.boostEvent || "mousedown"
|
||||
timeout = htmx.config?.preload?.boostTimeout ? htmx.parseInterval(htmx.config?.preload?.boostTimeout) : 5000;
|
||||
//only boosted links are supported
|
||||
if (elt.tagName === "A") {
|
||||
if(htmx.config?.preload?.boostTimeout) {
|
||||
timeout = htmx.parseInterval(htmx.config.preload.boostTimeout)
|
||||
}
|
||||
preloadEvents.push(htmx.config?.preload?.boostEvent || "mousedown");
|
||||
preloadEvents.push("touchstart");
|
||||
}
|
||||
}
|
||||
|
||||
let preloadListener = async (evt) => {
|
||||
@ -47,9 +56,11 @@
|
||||
delete elt._htmx.preload;
|
||||
}
|
||||
};
|
||||
elt.addEventListener(eventName, preloadListener);
|
||||
for (let eventName of preloadEvents) {
|
||||
elt.addEventListener(eventName, preloadListener);
|
||||
}
|
||||
elt._htmx.preloadListener = preloadListener;
|
||||
elt._htmx.preloadEvent = eventName;
|
||||
elt._htmx.preloadEvents = preloadEvents;
|
||||
}
|
||||
|
||||
htmx.registerExtension('preload', {
|
||||
@ -76,7 +87,9 @@
|
||||
|
||||
htmx_before_cleanup: (elt) => {
|
||||
if (elt._htmx?.preloadListener) {
|
||||
elt.removeEventListener(elt._htmx.preloadEvent, elt._htmx.preloadListener);
|
||||
for (let eventName of elt._htmx.preloadEvents) {
|
||||
elt.removeEventListener(eventName, elt._htmx.preloadListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Binary file not shown.
2
www/static/js/ext/hx-preload.min.js
vendored
2
www/static/js/ext/hx-preload.min.js
vendored
@ -1 +1 @@
|
||||
(()=>{let e;htmx.registerExtension("preload",{init:t=>{e=t},htmx_after_init:t=>{!function(t){let r,o,a=e.attributeValue(t,"hx-preload");if(!a&&!t._htmx?.boosted)return;if(a){let t=e.parseTriggerSpecs(a);if(0===t.length)return;let l=t[0];r=l.name,o=l.timeout?htmx.parseInterval(l.timeout):5e3}else r=htmx.config?.preload?.boostEvent||"mousedown",o=htmx.config?.preload?.boostTimeout?htmx.parseInterval(htmx.config?.preload?.boostTimeout):5e3;let l=async r=>{let{method:a}=e.determineMethodAndAction(t,r);if("GET"!==a)return;if(t._htmx?.preload)return;let l=e.createRequestContext(t,r),n=t.form||t.closest("form"),m=e.collectFormData(t,n,r.submitter);e.handleHxVals(t,m);let h=l.request.action.replace?.(/#.*$/,""),i=new URLSearchParams(m);i.size&&(h+=(/\?/.test(h)?"&":"?")+i),t._htmx.preload={prefetch:fetch(h,l.request),action:h,expiresAt:Date.now()+o};try{await t._htmx.preload.prefetch}catch(e){delete t._htmx.preload}};t.addEventListener(r,l),t._htmx.preloadListener=l,t._htmx.preloadEvent=r}(t)},htmx_before_request:(e,t)=>{let{ctx:r}=t;if(e._htmx?.preload&&e._htmx.preload.action===r.request.action&&Date.now()<e._htmx.preload.expiresAt){let t=e._htmx.preload.prefetch;r.fetch=()=>t,delete e._htmx.preload}else e._htmx&&delete e._htmx.preload},htmx_before_cleanup:e=>{e._htmx?.preloadListener&&e.removeEventListener(e._htmx.preloadEvent,e._htmx.preloadListener)}})})();
|
||||
(()=>{let e;htmx.registerExtension("preload",{init:t=>{e=t},htmx_after_init:t=>{!function(t){let r=e.attributeValue(t,"hx-preload");if(!r&&!t._htmx?.boosted)return;let o=[],a=5e3;if(r){let t=e.parseTriggerSpecs(r);if(0===t.length)return;for(const e of t)o.push(e.name),e.timeout&&(a=htmx.parseInterval(e.timeout))}else"A"===t.tagName&&(htmx.config?.preload?.boostTimeout&&(a=htmx.parseInterval(htmx.config.preload.boostTimeout)),o.push(htmx.config?.preload?.boostEvent||"mousedown"),o.push("touchstart"));let l=async r=>{let{method:o}=e.determineMethodAndAction(t,r);if("GET"!==o)return;if(t._htmx?.preload)return;let l=e.createRequestContext(t,r),n=t.form||t.closest("form"),h=e.collectFormData(t,n,r.submitter);e.handleHxVals(t,h);let m=l.request.action.replace?.(/#.*$/,""),s=new URLSearchParams(h);s.size&&(m+=(/\?/.test(m)?"&":"?")+s),t._htmx.preload={prefetch:fetch(m,l.request),action:m,expiresAt:Date.now()+a};try{await t._htmx.preload.prefetch}catch(e){delete t._htmx.preload}};for(let e of o)t.addEventListener(e,l);t._htmx.preloadListener=l,t._htmx.preloadEvents=o}(t)},htmx_before_request:(e,t)=>{let{ctx:r}=t;if(e._htmx?.preload&&e._htmx.preload.action===r.request.action&&Date.now()<e._htmx.preload.expiresAt){let t=e._htmx.preload.prefetch;r.fetch=()=>t,delete e._htmx.preload}else e._htmx&&delete e._htmx.preload},htmx_before_cleanup:e=>{if(e._htmx?.preloadListener)for(let t of e._htmx.preloadEvents)e.removeEventListener(t,e._htmx.preloadListener)}})})();
|
||||
Binary file not shown.
@ -1 +1 @@
|
||||
{"version":3,"names":["api","htmx","registerExtension","init","internalAPI","htmx_after_init","elt","eventName","timeout","preloadSpec","attributeValue","_htmx","boosted","specs","parseTriggerSpecs","length","spec","name","parseInterval","config","preload","boostEvent","boostTimeout","preloadListener","async","evt","method","determineMethodAndAction","ctx","createRequestContext","form","closest","body","collectFormData","submitter","handleHxVals","action","request","replace","params","URLSearchParams","size","test","prefetch","fetch","expiresAt","Date","now","error","addEventListener","preloadEvent","initializePreload","htmx_before_request","detail","htmx_before_cleanup","removeEventListener"],"sources":["dist/ext/hx-preload.js"],"mappings":"AAAA,MACI,IAAIA,EAqDJC,KAAKC,kBAAkB,UAAW,CAC9BC,KAAOC,IACHJ,EAAMI,GAGVC,gBAAkBC,KAxDtB,SAA2BA,GACvB,IAGIC,EACAC,EAJAC,EAAcT,EAAIU,eAAeJ,EAAK,cAC1C,IAAKG,IAAgBH,EAAIK,OAAOC,QAAS,OAIzC,GAAIH,EAAa,CACb,IAAII,EAAQb,EAAIc,kBAAkBL,GAClC,GAAqB,IAAjBI,EAAME,OAAc,OACxB,IAAIC,EAAOH,EAAM,GACjBN,EAAYS,EAAKC,KACjBT,EAAUQ,EAAKR,QAAUP,KAAKiB,cAAcF,EAAKR,SAAW,GAChE,MACID,EAAYN,KAAKkB,QAAQC,SAASC,YAAc,YAChDb,EAAUP,KAAKkB,QAAQC,SAASE,aAAerB,KAAKiB,cAAcjB,KAAKkB,QAAQC,SAASE,cAAgB,IAG5G,IAAIC,EAAkBC,MAAOC,IACzB,IAAIC,OAACA,GAAU1B,EAAI2B,yBAAyBrB,EAAKmB,GACjD,GAAe,QAAXC,EAAkB,OAEtB,GAAIpB,EAAIK,OAAOS,QAAS,OAExB,IAAIQ,EAAM5B,EAAI6B,qBAAqBvB,EAAKmB,GACpCK,EAAOxB,EAAIwB,MAAQxB,EAAIyB,QAAQ,QAC/BC,EAAOhC,EAAIiC,gBAAgB3B,EAAKwB,EAAML,EAAIS,WAC9ClC,EAAImC,aAAa7B,EAAK0B,GAEtB,IAAII,EAASR,EAAIS,QAAQD,OAAOE,UAAU,OAAQ,IAG9CC,EAAS,IAAIC,gBAAgBR,GAC7BO,EAAOE,OAAML,IAAW,KAAKM,KAAKN,GAAU,IAAM,KAAOG,GAE7DjC,EAAIK,MAAMS,QAAU,CAChBuB,SAAUC,MAAMR,EAAQR,EAAIS,SAC5BD,OAAQA,EACRS,UAAWC,KAAKC,MAAQvC,GAG5B,UACUF,EAAIK,MAAMS,QAAQuB,QAC5B,CAAE,MAAOK,UACE1C,EAAIK,MAAMS,OACrB,GAEJd,EAAI2C,iBAAiB1C,EAAWgB,GAChCjB,EAAIK,MAAMY,gBAAkBA,EAC5BjB,EAAIK,MAAMuC,aAAe3C,CAC7B,CAQQ4C,CAAkB7C,IAGtB8C,oBAAqB,CAAC9C,EAAK+C,KACvB,IAAIzB,IAACA,GAAOyB,EACZ,GAAI/C,EAAIK,OAAOS,SACXd,EAAIK,MAAMS,QAAQgB,SAAWR,EAAIS,QAAQD,QACzCU,KAAKC,MAAQzC,EAAIK,MAAMS,QAAQyB,UAAW,CAC1C,IAAIF,EAAWrC,EAAIK,MAAMS,QAAQuB,SACjCf,EAAIgB,MAAQ,IAAMD,SACXrC,EAAIK,MAAMS,OACrB,MACQd,EAAIK,cAAcL,EAAIK,MAAMS,SAIxCkC,oBAAsBhD,IACdA,EAAIK,OAAOY,iBACXjB,EAAIiD,oBAAoBjD,EAAIK,MAAMuC,aAAc5C,EAAIK,MAAMY,mBAIzE,EAlFD","ignoreList":[]}
|
||||
{"version":3,"names":["api","htmx","registerExtension","init","internalAPI","htmx_after_init","elt","preloadSpec","attributeValue","_htmx","boosted","preloadEvents","timeout","specs","parseTriggerSpecs","length","spec","push","name","parseInterval","tagName","config","preload","boostTimeout","boostEvent","preloadListener","async","evt","method","determineMethodAndAction","ctx","createRequestContext","form","closest","body","collectFormData","submitter","handleHxVals","action","request","replace","params","URLSearchParams","size","test","prefetch","fetch","expiresAt","Date","now","error","eventName","addEventListener","initializePreload","htmx_before_request","detail","htmx_before_cleanup","removeEventListener"],"sources":["dist/ext/hx-preload.js"],"mappings":"AAAA,MACI,IAAIA,EAgEJC,KAAKC,kBAAkB,UAAW,CAC9BC,KAAOC,IACHJ,EAAMI,GAGVC,gBAAkBC,KAnEtB,SAA2BA,GACvB,IAAIC,EAAcP,EAAIQ,eAAeF,EAAK,cAC1C,IAAKC,IAAgBD,EAAIG,OAAOC,QAAS,OAEzC,IAAIC,EAAgB,GAChBC,EAAU,IACd,GAAIL,EAAa,CACb,IAAIM,EAAQb,EAAIc,kBAAkBP,GAClC,GAAqB,IAAjBM,EAAME,OAAc,OACxB,IAAK,MAAMC,KAAQH,EACfF,EAAcM,KAAKD,EAAKE,MACpBF,EAAKJ,UACLA,EAAUX,KAAKkB,cAAcH,EAAKJ,SAG9C,KAEwB,MAAhBN,EAAIc,UACDnB,KAAKoB,QAAQC,SAASC,eACrBX,EAAUX,KAAKkB,cAAclB,KAAKoB,OAAOC,QAAQC,eAErDZ,EAAcM,KAAKhB,KAAKoB,QAAQC,SAASE,YAAc,aACvDb,EAAcM,KAAK,eAI3B,IAAIQ,EAAkBC,MAAOC,IACzB,IAAIC,OAACA,GAAU5B,EAAI6B,yBAAyBvB,EAAKqB,GACjD,GAAe,QAAXC,EAAkB,OAEtB,GAAItB,EAAIG,OAAOa,QAAS,OAExB,IAAIQ,EAAM9B,EAAI+B,qBAAqBzB,EAAKqB,GACpCK,EAAO1B,EAAI0B,MAAQ1B,EAAI2B,QAAQ,QAC/BC,EAAOlC,EAAImC,gBAAgB7B,EAAK0B,EAAML,EAAIS,WAC9CpC,EAAIqC,aAAa/B,EAAK4B,GAEtB,IAAII,EAASR,EAAIS,QAAQD,OAAOE,UAAU,OAAQ,IAG9CC,EAAS,IAAIC,gBAAgBR,GAC7BO,EAAOE,OAAML,IAAW,KAAKM,KAAKN,GAAU,IAAM,KAAOG,GAE7DnC,EAAIG,MAAMa,QAAU,CAChBuB,SAAUC,MAAMR,EAAQR,EAAIS,SAC5BD,OAAQA,EACRS,UAAWC,KAAKC,MAAQrC,GAG5B,UACUN,EAAIG,MAAMa,QAAQuB,QAC5B,CAAE,MAAOK,UACE5C,EAAIG,MAAMa,OACrB,GAEJ,IAAK,IAAI6B,KAAaxC,EAClBL,EAAI8C,iBAAiBD,EAAW1B,GAEpCnB,EAAIG,MAAMgB,gBAAkBA,EAC5BnB,EAAIG,MAAME,cAAgBA,CAC9B,CAQQ0C,CAAkB/C,IAGtBgD,oBAAqB,CAAChD,EAAKiD,KACvB,IAAIzB,IAACA,GAAOyB,EACZ,GAAIjD,EAAIG,OAAOa,SACXhB,EAAIG,MAAMa,QAAQgB,SAAWR,EAAIS,QAAQD,QACzCU,KAAKC,MAAQ3C,EAAIG,MAAMa,QAAQyB,UAAW,CAC1C,IAAIF,EAAWvC,EAAIG,MAAMa,QAAQuB,SACjCf,EAAIgB,MAAQ,IAAMD,SACXvC,EAAIG,MAAMa,OACrB,MACQhB,EAAIG,cAAcH,EAAIG,MAAMa,SAIxCkC,oBAAsBlD,IAClB,GAAIA,EAAIG,OAAOgB,gBACX,IAAK,IAAI0B,KAAa7C,EAAIG,MAAME,cAC5BL,EAAImD,oBAAoBN,EAAW7C,EAAIG,MAAMgB,mBAKhE,EA/FD","ignoreList":[]}
|
||||
@ -78,6 +78,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Close and remove listeners from old socket
|
||||
if (entry.socket) {
|
||||
let oldSocket = entry.socket;
|
||||
entry.socket = null;
|
||||
|
||||
oldSocket.onopen = null;
|
||||
oldSocket.onmessage = null;
|
||||
oldSocket.onclose = null;
|
||||
oldSocket.onerror = null;
|
||||
|
||||
try {
|
||||
if (oldSocket.readyState === WebSocket.OPEN || oldSocket.readyState === WebSocket.CONNECTING) {
|
||||
oldSocket.close();
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
try {
|
||||
entry.socket = new WebSocket(url);
|
||||
|
||||
@ -93,7 +110,10 @@
|
||||
handleMessage(entry, event);
|
||||
});
|
||||
|
||||
entry.socket.addEventListener('close', () => {
|
||||
entry.socket.addEventListener('close', (event) => {
|
||||
// Check if this socket is still the active one
|
||||
if (event.target !== entry.socket) return;
|
||||
|
||||
if (firstElement) {
|
||||
triggerEvent(firstElement, 'htmx:ws:close', { url });
|
||||
}
|
||||
@ -305,9 +325,9 @@
|
||||
|
||||
if (partials.length === 0) {
|
||||
// No partials, treat entire payload as content for element's target
|
||||
let target = resolveTarget(element);
|
||||
let target = resolveTarget(element, envelope.target);
|
||||
if (target) {
|
||||
swapContent(target, envelope.payload, element);
|
||||
swapContent(target, envelope.payload, element, envelope.swap);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -323,7 +343,13 @@
|
||||
});
|
||||
}
|
||||
|
||||
function resolveTarget(element) {
|
||||
function resolveTarget(element, envelopeTarget) {
|
||||
if (envelopeTarget) {
|
||||
if (envelopeTarget === 'this') {
|
||||
return element;
|
||||
}
|
||||
return document.querySelector(envelopeTarget);
|
||||
}
|
||||
let targetSelector = api.attributeValue(element, 'hx-target');
|
||||
if (targetSelector) {
|
||||
if (targetSelector === 'this') {
|
||||
@ -334,8 +360,8 @@
|
||||
return element;
|
||||
}
|
||||
|
||||
function swapContent(target, content, sourceElement) {
|
||||
let swapStyle = api.attributeValue(sourceElement, 'hx-swap') || htmx.config.defaultSwap;
|
||||
function swapContent(target, content, sourceElement, envelopeSwap) {
|
||||
let swapStyle = envelopeSwap || api.attributeValue(sourceElement, 'hx-swap') || htmx.config.defaultSwap;
|
||||
|
||||
// Parse swap style (just get the main style, ignore modifiers for now)
|
||||
let style = swapStyle.split(' ')[0];
|
||||
@ -549,7 +575,7 @@
|
||||
processNode(element);
|
||||
|
||||
// Process descendants
|
||||
element.querySelectorAll('[hx-ws\\:connect], [hx-ws-connect], [hx-ws\\:send], [hx-ws-send], [hx-ws], [ws-connect], [ws-send]').forEach(processNode);
|
||||
element.querySelectorAll('[hx-ws\\:connect], [hx-ws-connect], [hx-ws\\:send], [hx-ws-send], [hx-ws], [ws-connect], [ws-send]').forEach(processNode);
|
||||
},
|
||||
|
||||
htmx_before_cleanup: (element) => {
|
||||
|
||||
Binary file not shown.
2
www/static/js/ext/hx-ws.min.js
vendored
2
www/static/js/ext/hx-ws.min.js
vendored
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -80,7 +80,8 @@ var htmx = (() => {
|
||||
determineMethodAndAction: this.#determineMethodAndAction.bind(this),
|
||||
createRequestContext: this.#createRequestContext.bind(this),
|
||||
collectFormData: this.#collectFormData.bind(this),
|
||||
handleHxVals: this.#handleHxVals.bind(this)
|
||||
handleHxVals: this.#handleHxVals.bind(this),
|
||||
insertContent: this.#insertContent.bind(this)
|
||||
};
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
this.#initHistoryHandling();
|
||||
@ -90,10 +91,10 @@ var htmx = (() => {
|
||||
|
||||
#initHtmxConfig() {
|
||||
this.config = {
|
||||
version: '4.0.0-alpha3',
|
||||
version: '4.0.0-alpha5',
|
||||
logAll: false,
|
||||
prefix: "",
|
||||
transitions: true,
|
||||
transitions: false,
|
||||
history: true,
|
||||
historyReload: false,
|
||||
mode: 'same-origin',
|
||||
@ -115,19 +116,9 @@ var htmx = (() => {
|
||||
noSwap: [204, 304],
|
||||
implicitInheritance: false
|
||||
}
|
||||
let metaConfig = document.querySelector('meta[name="htmx:config"]');
|
||||
let metaConfig = document.querySelector('meta[name="htmx-config"]');
|
||||
if (metaConfig) {
|
||||
let content = metaConfig.content;
|
||||
let overrides = this.#parseConfig(content);
|
||||
// Deep merge nested config objects
|
||||
for (let key in overrides) {
|
||||
let val = overrides[key];
|
||||
if (val && typeof val === 'object' && !Array.isArray(val) && this.config[key]) {
|
||||
Object.assign(this.config[key], val);
|
||||
} else {
|
||||
this.config[key] = val;
|
||||
}
|
||||
}
|
||||
this.#mergeConfig(metaConfig.content, this.config);
|
||||
}
|
||||
this.#approvedExt = this.config.extensions;
|
||||
}
|
||||
@ -210,7 +201,7 @@ var htmx = (() => {
|
||||
if (parent) {
|
||||
let val = this.#attributeValue(parent, name, undefined, returnElt);
|
||||
if (!returnElt && val && this.config.implicitInheritance) {
|
||||
this.#triggerExtensions(elt, "htmx:after:implicitInheritance", {elt, parent})
|
||||
this.#triggerExtensions(elt, "htmx:after:implicitInheritance", {elt, name, parent})
|
||||
}
|
||||
return val;
|
||||
}
|
||||
@ -231,6 +222,19 @@ var htmx = (() => {
|
||||
}, {});
|
||||
}
|
||||
|
||||
#mergeConfig(configString, target) {
|
||||
let parsed = this.#parseConfig(configString);
|
||||
for (let key in parsed) {
|
||||
let val = parsed[key];
|
||||
if (val && typeof val === 'object' && !Array.isArray(val) && target[key]) {
|
||||
Object.assign(target[key], val);
|
||||
} else {
|
||||
target[key] = val;
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
#parseTriggerSpecs(spec) {
|
||||
return spec.split(',').map(s => {
|
||||
let m = s.match(/^\s*(\S+\[[^\]]*\]|\S+)\s*(.*?)\s*$/);
|
||||
@ -305,14 +309,14 @@ var htmx = (() => {
|
||||
status: "created",
|
||||
select: this.#attributeValue(sourceElement, "hx-select"),
|
||||
selectOOB: this.#attributeValue(sourceElement, "hx-select-oob"),
|
||||
target: this.#resolveTarget(sourceElement, this.#attributeValue(sourceElement, "hx-target")),
|
||||
swap: this.#attributeValue(sourceElement, "hx-swap", this.config.defaultSwap),
|
||||
target: this.#attributeValue(sourceElement, "hx-target"),
|
||||
swap: this.#attributeValue(sourceElement, "hx-swap") ?? this.config.defaultSwap,
|
||||
push: this.#attributeValue(sourceElement, "hx-push-url"),
|
||||
replace: this.#attributeValue(sourceElement, "hx-replace-url"),
|
||||
transition: this.config.transitions,
|
||||
confirm: this.#attributeValue(sourceElement, "hx-confirm"),
|
||||
request: {
|
||||
validate: "true" === this.#attributeValue(sourceElement, "hx-validate", sourceElement.matches('form') ? "true" : "false"),
|
||||
validate: "true" === this.#attributeValue(sourceElement, "hx-validate", sourceElement.matches('form') && !sourceElement.noValidate && !sourceEvent.submitter?.formNoValidate ? "true" : "false"),
|
||||
action: fullAction,
|
||||
anchor,
|
||||
method,
|
||||
@ -323,26 +327,18 @@ var htmx = (() => {
|
||||
mode: this.config.mode
|
||||
}
|
||||
};
|
||||
// Apply boost config overrides
|
||||
if (sourceElement._htmx?.boosted) {
|
||||
this.#mergeConfig(sourceElement._htmx.boosted, ctx);
|
||||
}
|
||||
ctx.target = this.#resolveTarget(sourceElement, ctx.target);
|
||||
|
||||
// Apply hx-config overrides
|
||||
let configAttr = this.#attributeValue(sourceElement, "hx-config");
|
||||
if (configAttr) {
|
||||
let configOverrides = this.#parseConfig(configAttr);
|
||||
let req = ctx.request;
|
||||
for (let key in configOverrides) {
|
||||
if (key.startsWith('+')) {
|
||||
let actualKey = key.substring(1);
|
||||
if (req[actualKey] && typeof req[actualKey] === 'object') {
|
||||
Object.assign(req[actualKey], configOverrides[key]);
|
||||
} else {
|
||||
req[actualKey] = configOverrides[key];
|
||||
}
|
||||
} else {
|
||||
req[key] = configOverrides[key];
|
||||
}
|
||||
}
|
||||
if (req.etag) {
|
||||
(sourceElement._htmx ||= {}).etag ||= req.etag
|
||||
this.#mergeConfig(configAttr, ctx.request);
|
||||
if (ctx.request.etag) {
|
||||
(sourceElement._htmx ||= {}).etag ||= ctx.request.etag
|
||||
}
|
||||
}
|
||||
if (sourceElement._htmx?.etag) {
|
||||
@ -351,10 +347,14 @@ var htmx = (() => {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
#buildIdentifier(elt) {
|
||||
return `${elt.tagName.toLowerCase()}${elt.id ? '#' + elt.id : ''}`;
|
||||
}
|
||||
|
||||
#determineHeaders(elt) {
|
||||
let headers = {
|
||||
"HX-Request": "true",
|
||||
"HX-Source": elt.id || elt.name,
|
||||
"HX-Source": this.#buildIdentifier(elt),
|
||||
"HX-Current-URL": location.href,
|
||||
"Accept": "text/html, text/event-stream"
|
||||
};
|
||||
@ -363,7 +363,7 @@ var htmx = (() => {
|
||||
}
|
||||
let headersAttribute = this.#attributeValue(elt, "hx-headers");
|
||||
if (headersAttribute) {
|
||||
Object.assign(headers, this.#parseConfig(headersAttribute));
|
||||
this.#mergeConfig(headersAttribute, headers);
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
@ -371,10 +371,9 @@ var htmx = (() => {
|
||||
#resolveTarget(elt, selector) {
|
||||
if (selector instanceof Element) {
|
||||
return selector;
|
||||
} else if (selector === 'this') {
|
||||
return this.#attributeValue(elt, "hx-target", undefined, true);
|
||||
} else if (selector != null) {
|
||||
return this.find(elt, selector);
|
||||
let thisElt = this.#attributeValue(elt, "hx-target", undefined, true);
|
||||
return this.#findAllExt(elt, selector, false, thisElt)[0];
|
||||
} else if (this.#isBoosted(elt)) {
|
||||
return document.body
|
||||
} else {
|
||||
@ -397,7 +396,8 @@ var htmx = (() => {
|
||||
|
||||
// Build request body
|
||||
let form = elt.form || elt.closest("form")
|
||||
let body = this.#collectFormData(elt, form, evt.submitter)
|
||||
let body = this.#collectFormData(elt, form, evt.submitter, ctx.request.validate)
|
||||
if (!body) return // Validation failed
|
||||
let valsResult = this.#handleHxVals(elt, body)
|
||||
if (valsResult) await valsResult // Only await if it returned a promise
|
||||
if (ctx.values) {
|
||||
@ -407,6 +407,12 @@ var htmx = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
// Add HX-Request-Type and HX-Target headers
|
||||
ctx.request.headers["HX-Request-Type"] = (ctx.target === document.body || ctx.select) ? "full" : "partial";
|
||||
if (ctx.target) {
|
||||
ctx.request.headers["HX-Target"] = this.#buildIdentifier(ctx.target);
|
||||
}
|
||||
|
||||
// Setup event-dependent request details
|
||||
Object.assign(ctx.request, {
|
||||
form,
|
||||
@ -416,7 +422,6 @@ var htmx = (() => {
|
||||
|
||||
if (!this.#trigger(elt, "htmx:config:request", {ctx: ctx})) return
|
||||
if (!this.#verbs.includes(ctx.request.method.toLowerCase())) return
|
||||
if (ctx.request.validate && ctx.request.form && !ctx.request.form.reportValidity()) return
|
||||
|
||||
let javascriptContent = this.#extractJavascriptContent(ctx.request.action);
|
||||
if (javascriptContent != null) {
|
||||
@ -452,10 +457,8 @@ var htmx = (() => {
|
||||
ctx.status = "issuing"
|
||||
this.#initTimeout(ctx);
|
||||
|
||||
let indicatorsSelector = this.#attributeValue(elt, "hx-indicator");
|
||||
let indicators = this.#showIndicators(elt, indicatorsSelector);
|
||||
let disableSelector = this.#attributeValue(elt, "hx-disable");
|
||||
let disableElements = this.#disableElements(elt, disableSelector);
|
||||
let indicators = this.#showIndicators(elt);
|
||||
let disableElements = this.#disableElements(elt);
|
||||
|
||||
try {
|
||||
// Handle confirmation
|
||||
@ -1001,8 +1004,9 @@ var htmx = (() => {
|
||||
}
|
||||
|
||||
#maybeBoost(elt) {
|
||||
if (this.#attributeValue(elt, "hx-boost") === "true" && this.#shouldBoost(elt)) {
|
||||
elt._htmx = {eventHandler: this.#createHtmxEventHandler(elt), requests: [], boosted: true}
|
||||
let boostValue = this.#attributeValue(elt, "hx-boost");
|
||||
if (boostValue && boostValue !== "false" && this.#shouldBoost(elt)) {
|
||||
elt._htmx = {eventHandler: this.#createHtmxEventHandler(elt), requests: [], boosted: boostValue}
|
||||
elt.setAttribute('data-htmx-powered', 'true');
|
||||
if (elt.matches('a') && !elt.hasAttribute("target")) {
|
||||
elt.addEventListener('click', (click) => {
|
||||
@ -1187,13 +1191,11 @@ var htmx = (() => {
|
||||
let type = templateElt.getAttribute('type');
|
||||
|
||||
if (type === 'partial') {
|
||||
let swapSpec = this.#parseSwapSpec(templateElt.getAttribute(this.#prefix('hx-swap')) || this.config.defaultSwap);
|
||||
|
||||
tasks.push({
|
||||
type: 'partial',
|
||||
fragment: templateElt.content.cloneNode(true),
|
||||
target: templateElt.getAttribute(this.#prefix('hx-target')),
|
||||
swapSpec,
|
||||
swapSpec: this.#parseSwapSpec(templateElt.getAttribute(this.#prefix('hx-swap')) || this.config.defaultSwap),
|
||||
sourceElement: ctx.sourceElement
|
||||
});
|
||||
} else {
|
||||
@ -1210,18 +1212,18 @@ var htmx = (() => {
|
||||
autofocus?.focus?.()
|
||||
}
|
||||
|
||||
#handleScroll(task) {
|
||||
if (task.swapSpec.scroll) {
|
||||
let target = task.swapSpec.scrollTarget ? this.#findExt(task.swapSpec.scrollTarget) : task.target;
|
||||
if (task.swapSpec.scroll === 'top') {
|
||||
target.scrollTop = 0;
|
||||
} else if (task.swapSpec.scroll === 'bottom'){
|
||||
target.scrollTop = target.scrollHeight;
|
||||
#handleScroll(swapSpec, target) {
|
||||
if (swapSpec.scroll) {
|
||||
let scrollTarget = swapSpec.scrollTarget ? this.#findExt(swapSpec.scrollTarget) : target;
|
||||
if (swapSpec.scroll === 'top') {
|
||||
scrollTarget.scrollTop = 0;
|
||||
} else if (swapSpec.scroll === 'bottom'){
|
||||
scrollTarget.scrollTop = scrollTarget.scrollHeight;
|
||||
}
|
||||
}
|
||||
if (task.swapSpec.show) {
|
||||
let target = task.swapSpec.showTarget ? this.#findExt(task.swapSpec.showTarget) : task.target;
|
||||
target.scrollIntoView(task.swapSpec.show === 'top')
|
||||
if (swapSpec.show) {
|
||||
let showTarget = swapSpec.showTarget ? this.#findExt(swapSpec.showTarget) : target;
|
||||
showTarget.scrollIntoView(swapSpec.show === 'top')
|
||||
}
|
||||
}
|
||||
|
||||
@ -1270,25 +1272,35 @@ var htmx = (() => {
|
||||
// TODO - can we remove this and just let the function complete?
|
||||
if (tasks.length === 0) return;
|
||||
|
||||
// Separate transition/nonTransition tasks
|
||||
let transitionTasks = tasks.filter(t => t.transition);
|
||||
let nonTransitionTasks = tasks.filter(t => !t.transition);
|
||||
|
||||
if(!this.#trigger(document, "htmx:before:swap", {ctx, tasks})){
|
||||
return
|
||||
}
|
||||
|
||||
// insert non-transition tasks immediately or with delay
|
||||
for (let task of nonTransitionTasks) {
|
||||
if (task.swapSpec?.swap) {
|
||||
setTimeout(() => this.#insertContent(task), this.parseInterval(task.swapSpec.swap));
|
||||
} else {
|
||||
// insert non-transition tasks immediately or with delay, collect transition tasks
|
||||
let transitionTasks = [];
|
||||
for (let task of tasks) {
|
||||
// OOB/partial tasks with swap delays should be non-transition (non-blocking)
|
||||
let swapDelay = task.swapSpec?.swap;
|
||||
if (!(task.swapSpec?.transition ?? mainSwap?.transition) || (swapDelay && task !== mainSwap)) {
|
||||
if (swapDelay) {
|
||||
if (task === mainSwap) {
|
||||
await this.timeout(swapDelay);
|
||||
} else {
|
||||
setTimeout(() => this.#insertContent(task), this.parseInterval(swapDelay));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
this.#insertContent(task)
|
||||
} else {
|
||||
transitionTasks.push(task);
|
||||
}
|
||||
}
|
||||
|
||||
// insert transition tasks in the transition queue
|
||||
if (transitionTasks.length > 0) {
|
||||
if (mainSwap?.transition && mainSwap?.swapSpec?.swap) {
|
||||
await this.timeout(mainSwap.swapSpec.swap);
|
||||
}
|
||||
let tasksWrapper = ()=> {
|
||||
for (let task of transitionTasks) {
|
||||
this.#insertContent(task)
|
||||
@ -1316,7 +1328,7 @@ var htmx = (() => {
|
||||
// Create main task if needed
|
||||
let swapSpec = this.#parseSwapSpec(ctx.swap || this.config.defaultSwap);
|
||||
// skip creating main swap if extracting partials resulted in empty response except for delete style
|
||||
if (swapSpec.style === 'delete' || /\S/.test(fragment.innerHTML || '') || !partialTasks.length) {
|
||||
if (swapSpec.style === 'delete' || fragment.childElementCount > 0 || /\S/.test(fragment.textContent) || !partialTasks.length) {
|
||||
if (ctx.select) {
|
||||
let selected = fragment.querySelectorAll(ctx.select);
|
||||
fragment = document.createDocumentFragment();
|
||||
@ -1343,8 +1355,10 @@ var htmx = (() => {
|
||||
target = document.querySelector(target);
|
||||
}
|
||||
if (!target) return;
|
||||
if (typeof swapSpec === 'string') {
|
||||
swapSpec = this.#parseSwapSpec(swapSpec);
|
||||
}
|
||||
if (swapSpec.strip && fragment.firstElementChild) {
|
||||
task.unstripped = fragment;
|
||||
fragment = document.createDocumentFragment();
|
||||
fragment.append(...(task.fragment.firstElementChild.content || task.fragment.firstElementChild).childNodes);
|
||||
}
|
||||
@ -1390,17 +1404,24 @@ var htmx = (() => {
|
||||
} else if (swapSpec.style === 'none') {
|
||||
return;
|
||||
} else {
|
||||
task.target = target;
|
||||
task.fragment = fragment;
|
||||
if (!this.#triggerExtensions(target, 'htmx:handle:swap', task)) return;
|
||||
throw new Error(`Unknown swap style: ${swapSpec.style}`);
|
||||
let methods = this.#extMethods.get('handle_swap')
|
||||
let handled = false;
|
||||
for (const method of methods) {
|
||||
if (method(swapSpec.style, target, fragment)) {
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!handled) {
|
||||
throw new Error(`Unknown swap style: ${swapSpec.style}`);
|
||||
}
|
||||
}
|
||||
this.#restorePreservedElements(pantry);
|
||||
for (const elt of newContent) {
|
||||
this.process(elt);
|
||||
this.#handleAutoFocus(elt);
|
||||
}
|
||||
this.#handleScroll(task);
|
||||
this.#handleScroll(swapSpec, target);
|
||||
}
|
||||
|
||||
#trigger(on, eventName, detail = {}, bubbles = true) {
|
||||
@ -1631,15 +1652,19 @@ var htmx = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
#showIndicators(elt, indicatorsSelector) {
|
||||
let indicatorElements = []
|
||||
if (indicatorsSelector) {
|
||||
indicatorElements = [elt, ...this.#queryEltAndDescendants(elt, indicatorsSelector)];
|
||||
for (const indicator of indicatorElements) {
|
||||
indicator._htmxReqCount ||= 0
|
||||
indicator._htmxReqCount++
|
||||
indicator.classList.add(this.config.requestClass)
|
||||
}
|
||||
#showIndicators(elt) {
|
||||
let indicatorsSelector = this.#attributeValue(elt, "hx-indicator");
|
||||
let indicatorElements;
|
||||
if (!indicatorsSelector) {
|
||||
indicatorElements = [elt]
|
||||
} else {
|
||||
let thisElt = this.#attributeValue(elt, "hx-indicator", undefined, true);
|
||||
indicatorElements = this.#findAllExt(elt, indicatorsSelector, false, thisElt);
|
||||
}
|
||||
for (const indicator of indicatorElements) {
|
||||
indicator._htmxReqCount ||= 0
|
||||
indicator._htmxReqCount++
|
||||
indicator.classList.add(this.config.requestClass)
|
||||
}
|
||||
return indicatorElements
|
||||
}
|
||||
@ -1656,7 +1681,8 @@ var htmx = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
#disableElements(elt, disabledSelector) {
|
||||
#disableElements(elt) {
|
||||
let disabledSelector = this.#attributeValue(elt, "hx-disable");
|
||||
let disabledElements = []
|
||||
if (disabledSelector) {
|
||||
disabledElements = this.#queryEltAndDescendants(elt, disabledSelector);
|
||||
@ -1681,10 +1707,13 @@ var htmx = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
#collectFormData(elt, form, submitter) {
|
||||
#collectFormData(elt, form, submitter, validate) {
|
||||
if (validate && form && !form.reportValidity()) return
|
||||
|
||||
let formData = form ? new FormData(form) : new FormData()
|
||||
let included = form ? new Set(form.elements) : new Set()
|
||||
if (!form && elt.name) {
|
||||
if (validate && elt.reportValidity && !elt.reportValidity()) return
|
||||
formData.append(elt.name, elt.value)
|
||||
included.add(elt);
|
||||
}
|
||||
@ -1694,8 +1723,8 @@ var htmx = (() => {
|
||||
}
|
||||
let includeSelector = this.#attributeValue(elt, "hx-include");
|
||||
if (includeSelector) {
|
||||
let includeNodes = this.#findAllExt(elt, includeSelector);
|
||||
for (let node of includeNodes) {
|
||||
for (let node of this.#findAllExt(elt, includeSelector)) {
|
||||
if (validate && node.reportValidity && !node.reportValidity()) return
|
||||
this.#addInputValues(node, included, formData);
|
||||
}
|
||||
}
|
||||
@ -1758,11 +1787,11 @@ var htmx = (() => {
|
||||
return s.startsWith('<') && s.endsWith('/>') ? s.slice(1, -2) : s;
|
||||
}
|
||||
|
||||
#findAllExt(eltOrSelector, maybeSelector, global) {
|
||||
#findAllExt(eltOrSelector, maybeSelector, global, thisElt) {
|
||||
let selector = maybeSelector ?? eltOrSelector;
|
||||
let elt = maybeSelector ? this.#normalizeElement(eltOrSelector) : document;
|
||||
if (selector.startsWith('global ')) {
|
||||
return this.#findAllExt(elt, selector.slice(7), true);
|
||||
return this.#findAllExt(elt, selector.slice(7), true, thisElt);
|
||||
}
|
||||
let parts = selector ? selector.replace(/<[^>]+\/>/g, m => m.replace(/,/g, '%2C'))
|
||||
.split(',').map(p => p.replace(/%2C/g, ',')) : [];
|
||||
@ -1774,7 +1803,9 @@ var htmx = (() => {
|
||||
if (selector.startsWith('closest ')) {
|
||||
item = elt.closest(selector.slice(8))
|
||||
} else if (selector.startsWith('find ')) {
|
||||
item = document.querySelector(elt, selector.slice(5))
|
||||
item = elt.querySelector(selector.slice(5))
|
||||
} else if (selector.startsWith('findAll ')) {
|
||||
result.push(...elt.querySelectorAll(selector.slice(8)))
|
||||
} else if (selector === 'next' || selector === 'nextElementSibling') {
|
||||
item = elt.nextElementSibling
|
||||
} else if (selector.startsWith('next ')) {
|
||||
@ -1789,10 +1820,10 @@ var htmx = (() => {
|
||||
item = window
|
||||
} else if (selector === 'body') {
|
||||
item = document.body
|
||||
} else if (selector === 'root') {
|
||||
item = this.#getRootNode(elt, !!global)
|
||||
} else if (selector === 'host') {
|
||||
item = (elt.getRootNode()).host
|
||||
} else if (selector === 'this') {
|
||||
item = thisElt || elt
|
||||
} else {
|
||||
unprocessedParts.push(selector)
|
||||
}
|
||||
@ -1836,7 +1867,7 @@ var htmx = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
#findExt(eltOrSelector, selector) {
|
||||
#findExt(eltOrSelector, selector, thisElt) {
|
||||
return this.#findAllExt(eltOrSelector, selector)[0]
|
||||
}
|
||||
|
||||
@ -1992,8 +2023,8 @@ var htmx = (() => {
|
||||
let type = newNode.nodeType;
|
||||
|
||||
if (type === 1) {
|
||||
let noMorph = this.config.morphIgnore || [];
|
||||
this.#copyAttributes(oldNode, newNode, noMorph);
|
||||
if (this.config.morphSkip && oldNode.matches?.(this.config.morphSkip)) return;
|
||||
this.#copyAttributes(oldNode, newNode);
|
||||
if (oldNode instanceof HTMLTextAreaElement && oldNode.defaultValue != newNode.defaultValue) {
|
||||
oldNode.value = newNode.value;
|
||||
}
|
||||
@ -2002,10 +2033,13 @@ var htmx = (() => {
|
||||
if ((type === 8 || type === 3) && oldNode.nodeValue !== newNode.nodeValue) {
|
||||
oldNode.nodeValue = newNode.nodeValue;
|
||||
}
|
||||
if (!oldNode.isEqualNode(newNode)) this.#morphChildren(ctx, oldNode, newNode);
|
||||
|
||||
let skipChildren = this.config.morphSkipChildren && oldNode.matches?.(this.config.morphSkipChildren);
|
||||
if (!skipChildren && !oldNode.isEqualNode(newNode)) this.#morphChildren(ctx, oldNode, newNode);
|
||||
}
|
||||
|
||||
#copyAttributes(destination, source, attributesToIgnore = []) {
|
||||
#copyAttributes(destination, source) {
|
||||
let attributesToIgnore = this.config.morphIgnore || [];
|
||||
for (const attr of source.attributes) {
|
||||
if (!attributesToIgnore.includes(attr.name) && destination.getAttribute(attr.name) !== attr.value) {
|
||||
destination.setAttribute(attr.name, attr.value);
|
||||
@ -2075,7 +2109,7 @@ var htmx = (() => {
|
||||
}
|
||||
let statusValue = this.#attributeValue(ctx.sourceElement, "hx-status:" + pattern);
|
||||
if (statusValue) {
|
||||
Object.assign(ctx, this.#parseConfig(statusValue));
|
||||
this.#mergeConfig(statusValue, ctx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
2
www/static/js/htmx.esm.min.js
vendored
2
www/static/js/htmx.esm.min.js
vendored
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -80,7 +80,8 @@ var htmx = (() => {
|
||||
determineMethodAndAction: this.#determineMethodAndAction.bind(this),
|
||||
createRequestContext: this.#createRequestContext.bind(this),
|
||||
collectFormData: this.#collectFormData.bind(this),
|
||||
handleHxVals: this.#handleHxVals.bind(this)
|
||||
handleHxVals: this.#handleHxVals.bind(this),
|
||||
insertContent: this.#insertContent.bind(this)
|
||||
};
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
this.#initHistoryHandling();
|
||||
@ -90,10 +91,10 @@ var htmx = (() => {
|
||||
|
||||
#initHtmxConfig() {
|
||||
this.config = {
|
||||
version: '4.0.0-alpha3',
|
||||
version: '4.0.0-alpha5',
|
||||
logAll: false,
|
||||
prefix: "",
|
||||
transitions: true,
|
||||
transitions: false,
|
||||
history: true,
|
||||
historyReload: false,
|
||||
mode: 'same-origin',
|
||||
@ -115,19 +116,9 @@ var htmx = (() => {
|
||||
noSwap: [204, 304],
|
||||
implicitInheritance: false
|
||||
}
|
||||
let metaConfig = document.querySelector('meta[name="htmx:config"]');
|
||||
let metaConfig = document.querySelector('meta[name="htmx-config"]');
|
||||
if (metaConfig) {
|
||||
let content = metaConfig.content;
|
||||
let overrides = this.#parseConfig(content);
|
||||
// Deep merge nested config objects
|
||||
for (let key in overrides) {
|
||||
let val = overrides[key];
|
||||
if (val && typeof val === 'object' && !Array.isArray(val) && this.config[key]) {
|
||||
Object.assign(this.config[key], val);
|
||||
} else {
|
||||
this.config[key] = val;
|
||||
}
|
||||
}
|
||||
this.#mergeConfig(metaConfig.content, this.config);
|
||||
}
|
||||
this.#approvedExt = this.config.extensions;
|
||||
}
|
||||
@ -210,7 +201,7 @@ var htmx = (() => {
|
||||
if (parent) {
|
||||
let val = this.#attributeValue(parent, name, undefined, returnElt);
|
||||
if (!returnElt && val && this.config.implicitInheritance) {
|
||||
this.#triggerExtensions(elt, "htmx:after:implicitInheritance", {elt, parent})
|
||||
this.#triggerExtensions(elt, "htmx:after:implicitInheritance", {elt, name, parent})
|
||||
}
|
||||
return val;
|
||||
}
|
||||
@ -231,6 +222,19 @@ var htmx = (() => {
|
||||
}, {});
|
||||
}
|
||||
|
||||
#mergeConfig(configString, target) {
|
||||
let parsed = this.#parseConfig(configString);
|
||||
for (let key in parsed) {
|
||||
let val = parsed[key];
|
||||
if (val && typeof val === 'object' && !Array.isArray(val) && target[key]) {
|
||||
Object.assign(target[key], val);
|
||||
} else {
|
||||
target[key] = val;
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
#parseTriggerSpecs(spec) {
|
||||
return spec.split(',').map(s => {
|
||||
let m = s.match(/^\s*(\S+\[[^\]]*\]|\S+)\s*(.*?)\s*$/);
|
||||
@ -305,14 +309,14 @@ var htmx = (() => {
|
||||
status: "created",
|
||||
select: this.#attributeValue(sourceElement, "hx-select"),
|
||||
selectOOB: this.#attributeValue(sourceElement, "hx-select-oob"),
|
||||
target: this.#resolveTarget(sourceElement, this.#attributeValue(sourceElement, "hx-target")),
|
||||
swap: this.#attributeValue(sourceElement, "hx-swap", this.config.defaultSwap),
|
||||
target: this.#attributeValue(sourceElement, "hx-target"),
|
||||
swap: this.#attributeValue(sourceElement, "hx-swap") ?? this.config.defaultSwap,
|
||||
push: this.#attributeValue(sourceElement, "hx-push-url"),
|
||||
replace: this.#attributeValue(sourceElement, "hx-replace-url"),
|
||||
transition: this.config.transitions,
|
||||
confirm: this.#attributeValue(sourceElement, "hx-confirm"),
|
||||
request: {
|
||||
validate: "true" === this.#attributeValue(sourceElement, "hx-validate", sourceElement.matches('form') ? "true" : "false"),
|
||||
validate: "true" === this.#attributeValue(sourceElement, "hx-validate", sourceElement.matches('form') && !sourceElement.noValidate && !sourceEvent.submitter?.formNoValidate ? "true" : "false"),
|
||||
action: fullAction,
|
||||
anchor,
|
||||
method,
|
||||
@ -323,26 +327,18 @@ var htmx = (() => {
|
||||
mode: this.config.mode
|
||||
}
|
||||
};
|
||||
// Apply boost config overrides
|
||||
if (sourceElement._htmx?.boosted) {
|
||||
this.#mergeConfig(sourceElement._htmx.boosted, ctx);
|
||||
}
|
||||
ctx.target = this.#resolveTarget(sourceElement, ctx.target);
|
||||
|
||||
// Apply hx-config overrides
|
||||
let configAttr = this.#attributeValue(sourceElement, "hx-config");
|
||||
if (configAttr) {
|
||||
let configOverrides = this.#parseConfig(configAttr);
|
||||
let req = ctx.request;
|
||||
for (let key in configOverrides) {
|
||||
if (key.startsWith('+')) {
|
||||
let actualKey = key.substring(1);
|
||||
if (req[actualKey] && typeof req[actualKey] === 'object') {
|
||||
Object.assign(req[actualKey], configOverrides[key]);
|
||||
} else {
|
||||
req[actualKey] = configOverrides[key];
|
||||
}
|
||||
} else {
|
||||
req[key] = configOverrides[key];
|
||||
}
|
||||
}
|
||||
if (req.etag) {
|
||||
(sourceElement._htmx ||= {}).etag ||= req.etag
|
||||
this.#mergeConfig(configAttr, ctx.request);
|
||||
if (ctx.request.etag) {
|
||||
(sourceElement._htmx ||= {}).etag ||= ctx.request.etag
|
||||
}
|
||||
}
|
||||
if (sourceElement._htmx?.etag) {
|
||||
@ -351,10 +347,14 @@ var htmx = (() => {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
#buildIdentifier(elt) {
|
||||
return `${elt.tagName.toLowerCase()}${elt.id ? '#' + elt.id : ''}`;
|
||||
}
|
||||
|
||||
#determineHeaders(elt) {
|
||||
let headers = {
|
||||
"HX-Request": "true",
|
||||
"HX-Source": elt.id || elt.name,
|
||||
"HX-Source": this.#buildIdentifier(elt),
|
||||
"HX-Current-URL": location.href,
|
||||
"Accept": "text/html, text/event-stream"
|
||||
};
|
||||
@ -363,7 +363,7 @@ var htmx = (() => {
|
||||
}
|
||||
let headersAttribute = this.#attributeValue(elt, "hx-headers");
|
||||
if (headersAttribute) {
|
||||
Object.assign(headers, this.#parseConfig(headersAttribute));
|
||||
this.#mergeConfig(headersAttribute, headers);
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
@ -371,10 +371,9 @@ var htmx = (() => {
|
||||
#resolveTarget(elt, selector) {
|
||||
if (selector instanceof Element) {
|
||||
return selector;
|
||||
} else if (selector === 'this') {
|
||||
return this.#attributeValue(elt, "hx-target", undefined, true);
|
||||
} else if (selector != null) {
|
||||
return this.find(elt, selector);
|
||||
let thisElt = this.#attributeValue(elt, "hx-target", undefined, true);
|
||||
return this.#findAllExt(elt, selector, false, thisElt)[0];
|
||||
} else if (this.#isBoosted(elt)) {
|
||||
return document.body
|
||||
} else {
|
||||
@ -397,7 +396,8 @@ var htmx = (() => {
|
||||
|
||||
// Build request body
|
||||
let form = elt.form || elt.closest("form")
|
||||
let body = this.#collectFormData(elt, form, evt.submitter)
|
||||
let body = this.#collectFormData(elt, form, evt.submitter, ctx.request.validate)
|
||||
if (!body) return // Validation failed
|
||||
let valsResult = this.#handleHxVals(elt, body)
|
||||
if (valsResult) await valsResult // Only await if it returned a promise
|
||||
if (ctx.values) {
|
||||
@ -407,6 +407,12 @@ var htmx = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
// Add HX-Request-Type and HX-Target headers
|
||||
ctx.request.headers["HX-Request-Type"] = (ctx.target === document.body || ctx.select) ? "full" : "partial";
|
||||
if (ctx.target) {
|
||||
ctx.request.headers["HX-Target"] = this.#buildIdentifier(ctx.target);
|
||||
}
|
||||
|
||||
// Setup event-dependent request details
|
||||
Object.assign(ctx.request, {
|
||||
form,
|
||||
@ -416,7 +422,6 @@ var htmx = (() => {
|
||||
|
||||
if (!this.#trigger(elt, "htmx:config:request", {ctx: ctx})) return
|
||||
if (!this.#verbs.includes(ctx.request.method.toLowerCase())) return
|
||||
if (ctx.request.validate && ctx.request.form && !ctx.request.form.reportValidity()) return
|
||||
|
||||
let javascriptContent = this.#extractJavascriptContent(ctx.request.action);
|
||||
if (javascriptContent != null) {
|
||||
@ -452,10 +457,8 @@ var htmx = (() => {
|
||||
ctx.status = "issuing"
|
||||
this.#initTimeout(ctx);
|
||||
|
||||
let indicatorsSelector = this.#attributeValue(elt, "hx-indicator");
|
||||
let indicators = this.#showIndicators(elt, indicatorsSelector);
|
||||
let disableSelector = this.#attributeValue(elt, "hx-disable");
|
||||
let disableElements = this.#disableElements(elt, disableSelector);
|
||||
let indicators = this.#showIndicators(elt);
|
||||
let disableElements = this.#disableElements(elt);
|
||||
|
||||
try {
|
||||
// Handle confirmation
|
||||
@ -1001,8 +1004,9 @@ var htmx = (() => {
|
||||
}
|
||||
|
||||
#maybeBoost(elt) {
|
||||
if (this.#attributeValue(elt, "hx-boost") === "true" && this.#shouldBoost(elt)) {
|
||||
elt._htmx = {eventHandler: this.#createHtmxEventHandler(elt), requests: [], boosted: true}
|
||||
let boostValue = this.#attributeValue(elt, "hx-boost");
|
||||
if (boostValue && boostValue !== "false" && this.#shouldBoost(elt)) {
|
||||
elt._htmx = {eventHandler: this.#createHtmxEventHandler(elt), requests: [], boosted: boostValue}
|
||||
elt.setAttribute('data-htmx-powered', 'true');
|
||||
if (elt.matches('a') && !elt.hasAttribute("target")) {
|
||||
elt.addEventListener('click', (click) => {
|
||||
@ -1187,13 +1191,11 @@ var htmx = (() => {
|
||||
let type = templateElt.getAttribute('type');
|
||||
|
||||
if (type === 'partial') {
|
||||
let swapSpec = this.#parseSwapSpec(templateElt.getAttribute(this.#prefix('hx-swap')) || this.config.defaultSwap);
|
||||
|
||||
tasks.push({
|
||||
type: 'partial',
|
||||
fragment: templateElt.content.cloneNode(true),
|
||||
target: templateElt.getAttribute(this.#prefix('hx-target')),
|
||||
swapSpec,
|
||||
swapSpec: this.#parseSwapSpec(templateElt.getAttribute(this.#prefix('hx-swap')) || this.config.defaultSwap),
|
||||
sourceElement: ctx.sourceElement
|
||||
});
|
||||
} else {
|
||||
@ -1210,18 +1212,18 @@ var htmx = (() => {
|
||||
autofocus?.focus?.()
|
||||
}
|
||||
|
||||
#handleScroll(task) {
|
||||
if (task.swapSpec.scroll) {
|
||||
let target = task.swapSpec.scrollTarget ? this.#findExt(task.swapSpec.scrollTarget) : task.target;
|
||||
if (task.swapSpec.scroll === 'top') {
|
||||
target.scrollTop = 0;
|
||||
} else if (task.swapSpec.scroll === 'bottom'){
|
||||
target.scrollTop = target.scrollHeight;
|
||||
#handleScroll(swapSpec, target) {
|
||||
if (swapSpec.scroll) {
|
||||
let scrollTarget = swapSpec.scrollTarget ? this.#findExt(swapSpec.scrollTarget) : target;
|
||||
if (swapSpec.scroll === 'top') {
|
||||
scrollTarget.scrollTop = 0;
|
||||
} else if (swapSpec.scroll === 'bottom'){
|
||||
scrollTarget.scrollTop = scrollTarget.scrollHeight;
|
||||
}
|
||||
}
|
||||
if (task.swapSpec.show) {
|
||||
let target = task.swapSpec.showTarget ? this.#findExt(task.swapSpec.showTarget) : task.target;
|
||||
target.scrollIntoView(task.swapSpec.show === 'top')
|
||||
if (swapSpec.show) {
|
||||
let showTarget = swapSpec.showTarget ? this.#findExt(swapSpec.showTarget) : target;
|
||||
showTarget.scrollIntoView(swapSpec.show === 'top')
|
||||
}
|
||||
}
|
||||
|
||||
@ -1270,25 +1272,35 @@ var htmx = (() => {
|
||||
// TODO - can we remove this and just let the function complete?
|
||||
if (tasks.length === 0) return;
|
||||
|
||||
// Separate transition/nonTransition tasks
|
||||
let transitionTasks = tasks.filter(t => t.transition);
|
||||
let nonTransitionTasks = tasks.filter(t => !t.transition);
|
||||
|
||||
if(!this.#trigger(document, "htmx:before:swap", {ctx, tasks})){
|
||||
return
|
||||
}
|
||||
|
||||
// insert non-transition tasks immediately or with delay
|
||||
for (let task of nonTransitionTasks) {
|
||||
if (task.swapSpec?.swap) {
|
||||
setTimeout(() => this.#insertContent(task), this.parseInterval(task.swapSpec.swap));
|
||||
} else {
|
||||
// insert non-transition tasks immediately or with delay, collect transition tasks
|
||||
let transitionTasks = [];
|
||||
for (let task of tasks) {
|
||||
// OOB/partial tasks with swap delays should be non-transition (non-blocking)
|
||||
let swapDelay = task.swapSpec?.swap;
|
||||
if (!(task.swapSpec?.transition ?? mainSwap?.transition) || (swapDelay && task !== mainSwap)) {
|
||||
if (swapDelay) {
|
||||
if (task === mainSwap) {
|
||||
await this.timeout(swapDelay);
|
||||
} else {
|
||||
setTimeout(() => this.#insertContent(task), this.parseInterval(swapDelay));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
this.#insertContent(task)
|
||||
} else {
|
||||
transitionTasks.push(task);
|
||||
}
|
||||
}
|
||||
|
||||
// insert transition tasks in the transition queue
|
||||
if (transitionTasks.length > 0) {
|
||||
if (mainSwap?.transition && mainSwap?.swapSpec?.swap) {
|
||||
await this.timeout(mainSwap.swapSpec.swap);
|
||||
}
|
||||
let tasksWrapper = ()=> {
|
||||
for (let task of transitionTasks) {
|
||||
this.#insertContent(task)
|
||||
@ -1316,7 +1328,7 @@ var htmx = (() => {
|
||||
// Create main task if needed
|
||||
let swapSpec = this.#parseSwapSpec(ctx.swap || this.config.defaultSwap);
|
||||
// skip creating main swap if extracting partials resulted in empty response except for delete style
|
||||
if (swapSpec.style === 'delete' || /\S/.test(fragment.innerHTML || '') || !partialTasks.length) {
|
||||
if (swapSpec.style === 'delete' || fragment.childElementCount > 0 || /\S/.test(fragment.textContent) || !partialTasks.length) {
|
||||
if (ctx.select) {
|
||||
let selected = fragment.querySelectorAll(ctx.select);
|
||||
fragment = document.createDocumentFragment();
|
||||
@ -1343,8 +1355,10 @@ var htmx = (() => {
|
||||
target = document.querySelector(target);
|
||||
}
|
||||
if (!target) return;
|
||||
if (typeof swapSpec === 'string') {
|
||||
swapSpec = this.#parseSwapSpec(swapSpec);
|
||||
}
|
||||
if (swapSpec.strip && fragment.firstElementChild) {
|
||||
task.unstripped = fragment;
|
||||
fragment = document.createDocumentFragment();
|
||||
fragment.append(...(task.fragment.firstElementChild.content || task.fragment.firstElementChild).childNodes);
|
||||
}
|
||||
@ -1390,17 +1404,24 @@ var htmx = (() => {
|
||||
} else if (swapSpec.style === 'none') {
|
||||
return;
|
||||
} else {
|
||||
task.target = target;
|
||||
task.fragment = fragment;
|
||||
if (!this.#triggerExtensions(target, 'htmx:handle:swap', task)) return;
|
||||
throw new Error(`Unknown swap style: ${swapSpec.style}`);
|
||||
let methods = this.#extMethods.get('handle_swap')
|
||||
let handled = false;
|
||||
for (const method of methods) {
|
||||
if (method(swapSpec.style, target, fragment)) {
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!handled) {
|
||||
throw new Error(`Unknown swap style: ${swapSpec.style}`);
|
||||
}
|
||||
}
|
||||
this.#restorePreservedElements(pantry);
|
||||
for (const elt of newContent) {
|
||||
this.process(elt);
|
||||
this.#handleAutoFocus(elt);
|
||||
}
|
||||
this.#handleScroll(task);
|
||||
this.#handleScroll(swapSpec, target);
|
||||
}
|
||||
|
||||
#trigger(on, eventName, detail = {}, bubbles = true) {
|
||||
@ -1631,15 +1652,19 @@ var htmx = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
#showIndicators(elt, indicatorsSelector) {
|
||||
let indicatorElements = []
|
||||
if (indicatorsSelector) {
|
||||
indicatorElements = [elt, ...this.#queryEltAndDescendants(elt, indicatorsSelector)];
|
||||
for (const indicator of indicatorElements) {
|
||||
indicator._htmxReqCount ||= 0
|
||||
indicator._htmxReqCount++
|
||||
indicator.classList.add(this.config.requestClass)
|
||||
}
|
||||
#showIndicators(elt) {
|
||||
let indicatorsSelector = this.#attributeValue(elt, "hx-indicator");
|
||||
let indicatorElements;
|
||||
if (!indicatorsSelector) {
|
||||
indicatorElements = [elt]
|
||||
} else {
|
||||
let thisElt = this.#attributeValue(elt, "hx-indicator", undefined, true);
|
||||
indicatorElements = this.#findAllExt(elt, indicatorsSelector, false, thisElt);
|
||||
}
|
||||
for (const indicator of indicatorElements) {
|
||||
indicator._htmxReqCount ||= 0
|
||||
indicator._htmxReqCount++
|
||||
indicator.classList.add(this.config.requestClass)
|
||||
}
|
||||
return indicatorElements
|
||||
}
|
||||
@ -1656,7 +1681,8 @@ var htmx = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
#disableElements(elt, disabledSelector) {
|
||||
#disableElements(elt) {
|
||||
let disabledSelector = this.#attributeValue(elt, "hx-disable");
|
||||
let disabledElements = []
|
||||
if (disabledSelector) {
|
||||
disabledElements = this.#queryEltAndDescendants(elt, disabledSelector);
|
||||
@ -1681,10 +1707,13 @@ var htmx = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
#collectFormData(elt, form, submitter) {
|
||||
#collectFormData(elt, form, submitter, validate) {
|
||||
if (validate && form && !form.reportValidity()) return
|
||||
|
||||
let formData = form ? new FormData(form) : new FormData()
|
||||
let included = form ? new Set(form.elements) : new Set()
|
||||
if (!form && elt.name) {
|
||||
if (validate && elt.reportValidity && !elt.reportValidity()) return
|
||||
formData.append(elt.name, elt.value)
|
||||
included.add(elt);
|
||||
}
|
||||
@ -1694,8 +1723,8 @@ var htmx = (() => {
|
||||
}
|
||||
let includeSelector = this.#attributeValue(elt, "hx-include");
|
||||
if (includeSelector) {
|
||||
let includeNodes = this.#findAllExt(elt, includeSelector);
|
||||
for (let node of includeNodes) {
|
||||
for (let node of this.#findAllExt(elt, includeSelector)) {
|
||||
if (validate && node.reportValidity && !node.reportValidity()) return
|
||||
this.#addInputValues(node, included, formData);
|
||||
}
|
||||
}
|
||||
@ -1758,11 +1787,11 @@ var htmx = (() => {
|
||||
return s.startsWith('<') && s.endsWith('/>') ? s.slice(1, -2) : s;
|
||||
}
|
||||
|
||||
#findAllExt(eltOrSelector, maybeSelector, global) {
|
||||
#findAllExt(eltOrSelector, maybeSelector, global, thisElt) {
|
||||
let selector = maybeSelector ?? eltOrSelector;
|
||||
let elt = maybeSelector ? this.#normalizeElement(eltOrSelector) : document;
|
||||
if (selector.startsWith('global ')) {
|
||||
return this.#findAllExt(elt, selector.slice(7), true);
|
||||
return this.#findAllExt(elt, selector.slice(7), true, thisElt);
|
||||
}
|
||||
let parts = selector ? selector.replace(/<[^>]+\/>/g, m => m.replace(/,/g, '%2C'))
|
||||
.split(',').map(p => p.replace(/%2C/g, ',')) : [];
|
||||
@ -1774,7 +1803,9 @@ var htmx = (() => {
|
||||
if (selector.startsWith('closest ')) {
|
||||
item = elt.closest(selector.slice(8))
|
||||
} else if (selector.startsWith('find ')) {
|
||||
item = document.querySelector(elt, selector.slice(5))
|
||||
item = elt.querySelector(selector.slice(5))
|
||||
} else if (selector.startsWith('findAll ')) {
|
||||
result.push(...elt.querySelectorAll(selector.slice(8)))
|
||||
} else if (selector === 'next' || selector === 'nextElementSibling') {
|
||||
item = elt.nextElementSibling
|
||||
} else if (selector.startsWith('next ')) {
|
||||
@ -1789,10 +1820,10 @@ var htmx = (() => {
|
||||
item = window
|
||||
} else if (selector === 'body') {
|
||||
item = document.body
|
||||
} else if (selector === 'root') {
|
||||
item = this.#getRootNode(elt, !!global)
|
||||
} else if (selector === 'host') {
|
||||
item = (elt.getRootNode()).host
|
||||
} else if (selector === 'this') {
|
||||
item = thisElt || elt
|
||||
} else {
|
||||
unprocessedParts.push(selector)
|
||||
}
|
||||
@ -1836,7 +1867,7 @@ var htmx = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
#findExt(eltOrSelector, selector) {
|
||||
#findExt(eltOrSelector, selector, thisElt) {
|
||||
return this.#findAllExt(eltOrSelector, selector)[0]
|
||||
}
|
||||
|
||||
@ -1992,8 +2023,8 @@ var htmx = (() => {
|
||||
let type = newNode.nodeType;
|
||||
|
||||
if (type === 1) {
|
||||
let noMorph = this.config.morphIgnore || [];
|
||||
this.#copyAttributes(oldNode, newNode, noMorph);
|
||||
if (this.config.morphSkip && oldNode.matches?.(this.config.morphSkip)) return;
|
||||
this.#copyAttributes(oldNode, newNode);
|
||||
if (oldNode instanceof HTMLTextAreaElement && oldNode.defaultValue != newNode.defaultValue) {
|
||||
oldNode.value = newNode.value;
|
||||
}
|
||||
@ -2002,10 +2033,13 @@ var htmx = (() => {
|
||||
if ((type === 8 || type === 3) && oldNode.nodeValue !== newNode.nodeValue) {
|
||||
oldNode.nodeValue = newNode.nodeValue;
|
||||
}
|
||||
if (!oldNode.isEqualNode(newNode)) this.#morphChildren(ctx, oldNode, newNode);
|
||||
|
||||
let skipChildren = this.config.morphSkipChildren && oldNode.matches?.(this.config.morphSkipChildren);
|
||||
if (!skipChildren && !oldNode.isEqualNode(newNode)) this.#morphChildren(ctx, oldNode, newNode);
|
||||
}
|
||||
|
||||
#copyAttributes(destination, source, attributesToIgnore = []) {
|
||||
#copyAttributes(destination, source) {
|
||||
let attributesToIgnore = this.config.morphIgnore || [];
|
||||
for (const attr of source.attributes) {
|
||||
if (!attributesToIgnore.includes(attr.name) && destination.getAttribute(attr.name) !== attr.value) {
|
||||
destination.setAttribute(attr.name, attr.value);
|
||||
@ -2075,7 +2109,7 @@ var htmx = (() => {
|
||||
}
|
||||
let statusValue = this.#attributeValue(ctx.sourceElement, "hx-status:" + pattern);
|
||||
if (statusValue) {
|
||||
Object.assign(ctx, this.#parseConfig(statusValue));
|
||||
this.#mergeConfig(statusValue, ctx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
2
www/static/js/htmx.min.js
vendored
2
www/static/js/htmx.min.js
vendored
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user