1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | (function(){
|
7 |
|
8 | var api = null;
|
9 |
|
10 | function log() {
|
11 |
|
12 | }
|
13 |
|
14 | function mergeHead(newContent, defaultMergeStrategy) {
|
15 |
|
16 | if (newContent && newContent.indexOf('<head') > -1) {
|
17 | const htmlDoc = document.createElement("html");
|
18 |
|
19 | var contentWithSvgsRemoved = newContent.replace(/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim, '');
|
20 |
|
21 | var headTag = contentWithSvgsRemoved.match(/(<head(\s[^>]*>|>)([\s\S]*?)<\/head>)/im);
|
22 |
|
23 |
|
24 | if (headTag) {
|
25 |
|
26 | var added = []
|
27 | var removed = []
|
28 | var preserved = []
|
29 | var nodesToAppend = []
|
30 |
|
31 | htmlDoc.innerHTML = headTag;
|
32 | var newHeadTag = htmlDoc.querySelector("head");
|
33 | var currentHead = document.head;
|
34 |
|
35 | if (newHeadTag == null) {
|
36 | return;
|
37 | } else {
|
38 |
|
39 | var srcToNewHeadNodes = new Map();
|
40 | for (const newHeadChild of newHeadTag.children) {
|
41 | srcToNewHeadNodes.set(newHeadChild.outerHTML, newHeadChild);
|
42 | }
|
43 | }
|
44 |
|
45 |
|
46 |
|
47 |
|
48 | var mergeStrategy = api.getAttributeValue(newHeadTag, "hx-head") || defaultMergeStrategy;
|
49 |
|
50 |
|
51 | for (const currentHeadElt of currentHead.children) {
|
52 |
|
53 |
|
54 | var inNewContent = srcToNewHeadNodes.has(currentHeadElt.outerHTML);
|
55 | var isReAppended = currentHeadElt.getAttribute("hx-head") === "re-eval";
|
56 | var isPreserved = api.getAttributeValue(currentHeadElt, "hx-preserve") === "true";
|
57 | if (inNewContent || isPreserved) {
|
58 | if (isReAppended) {
|
59 |
|
60 | removed.push(currentHeadElt);
|
61 | } else {
|
62 |
|
63 |
|
64 | srcToNewHeadNodes.delete(currentHeadElt.outerHTML);
|
65 | preserved.push(currentHeadElt);
|
66 | }
|
67 | } else {
|
68 | if (mergeStrategy === "append") {
|
69 |
|
70 |
|
71 | if (isReAppended) {
|
72 | removed.push(currentHeadElt);
|
73 | nodesToAppend.push(currentHeadElt);
|
74 | }
|
75 | } else {
|
76 |
|
77 | if (api.triggerEvent(document.body, "htmx:removingHeadElement", {headElement: currentHeadElt}) !== false) {
|
78 | removed.push(currentHeadElt);
|
79 | }
|
80 | }
|
81 | }
|
82 | }
|
83 |
|
84 |
|
85 |
|
86 | nodesToAppend.push(...srcToNewHeadNodes.values());
|
87 | log("to append: ", nodesToAppend);
|
88 |
|
89 | for (const newNode of nodesToAppend) {
|
90 | log("adding: ", newNode);
|
91 | var newElt = document.createRange().createContextualFragment(newNode.outerHTML);
|
92 | log(newElt);
|
93 | if (api.triggerEvent(document.body, "htmx:addingHeadElement", {headElement: newElt}) !== false) {
|
94 | currentHead.appendChild(newElt);
|
95 | added.push(newElt);
|
96 | }
|
97 | }
|
98 |
|
99 |
|
100 |
|
101 | for (const removedElement of removed) {
|
102 | if (api.triggerEvent(document.body, "htmx:removingHeadElement", {headElement: removedElement}) !== false) {
|
103 | currentHead.removeChild(removedElement);
|
104 | }
|
105 | }
|
106 |
|
107 | api.triggerEvent(document.body, "htmx:afterHeadMerge", {added: added, kept: preserved, removed: removed});
|
108 | }
|
109 | }
|
110 | }
|
111 |
|
112 | htmx.defineExtension("head-support", {
|
113 | init: function(apiRef) {
|
114 |
|
115 | api = apiRef;
|
116 |
|
117 | htmx.on('htmx:afterSwap', function(evt){
|
118 | var serverResponse = evt.detail.xhr.response;
|
119 | if (api.triggerEvent(document.body, "htmx:beforeHeadMerge", evt.detail)) {
|
120 | mergeHead(serverResponse, evt.detail.boosted ? "merge" : "append");
|
121 | }
|
122 | })
|
123 |
|
124 | htmx.on('htmx:historyRestore', function(evt){
|
125 | if (api.triggerEvent(document.body, "htmx:beforeHeadMerge", evt.detail)) {
|
126 | if (evt.detail.cacheMiss) {
|
127 | mergeHead(evt.detail.serverResponse, "merge");
|
128 | } else {
|
129 | mergeHead(evt.detail.item.head, "merge");
|
130 | }
|
131 | }
|
132 | })
|
133 |
|
134 | htmx.on('htmx:historyItemCreated', function(evt){
|
135 | var historyItem = evt.detail.item;
|
136 | historyItem.head = document.head.outerHTML;
|
137 | })
|
138 | }
|
139 | });
|
140 |
|
141 | })() |
\ | No newline at end of file |