diff --git a/src/htmx.js b/src/htmx.js
index 6f5b40c77..f5eb95aff 100644
--- a/src/htmx.js
+++ b/src/htmx.js
@@ -235,20 +235,6 @@ return (function () {
return matchesFunction && matchesFunction.call(elt, selector);
}
- /**
- * @param {string} str
- * @returns {string}
- */
- function getStartTag(str) {
- var tagMatcher = /<([a-z][^\/\0>\x20\t\r\n\f]*)/i
- var match = tagMatcher.exec( str );
- if (match) {
- return match[1].toLowerCase();
- } else {
- return "";
- }
- }
-
/**
*
* @param {string} resp
@@ -273,43 +259,123 @@ return (function () {
return responseNode;
}
- function aFullPageResponse(resp) {
- return resp.match(/
]*>|' : '' + tagName + '>';
+ var endTagIndex = resp.indexOf(endTag, lastIndex);
+
+ // append text nodes
+ if (nextIndex !== -1 && nextIndex < elementStartIndex) {
+ fragment.appendChild(parseHTML(resp.substring(nextIndex, elementStartIndex), 1));
+ }
+
+ // void element e.g.
+ if (endTagIndex === -1) {
+ var nextStartTagIndex = resp.slice(lastIndex).search(START_TAG_REGEX);
+ var elementEndIndex = nextStartTagIndex !== -1 ? lastIndex + nextStartTagIndex : resp.length;
+ var elementHtml = resp.substring(elementStartIndex, elementEndIndex);
+ fragment.appendChild(parseHTML(elementHtml, 1));
+ nextIndex = elementEndIndex;
+ continue;
+ }
+
+ var elementEndIndex = endTagIndex + endTag.length;
+ var elementHtml = resp.substring(elementStartIndex, elementEndIndex);
+
+ // move end tag index forward as many times as there are nested elements
+ var nestedCount = 0;
+ var li = 1;
+ while ((li = elementHtml.indexOf('<' + tagName, li)) !== -1) {
+ nestedCount++;
+ li++;
+ }
+ for (var i = 1; i < nestedCount; i++) {
+ endTagIndex = resp.indexOf(endTag, elementEndIndex);
+ if (endTagIndex == -1) {
+ break;
+ }
+ elementEndIndex = endTagIndex + endTag.length;
+ elementHtml = resp.substring(elementStartIndex, elementEndIndex);
+ }
+
+ var element;
+ switch (tagName) {
+ case "thead":
+ case "tbody":
+ case "tfoot":
+ case "colgroup":
+ case "caption":
+ element = parseHTML('', 2);
+ break;
+ case "col":
+ element = parseHTML("", 3);
+ break;
+ case "tr":
+ element = parseHTML("", 3);
+ break;
+ case "td":
+ case "th":
+ element = parseHTML("", 4);
+ break;
+ case "script":
+ element = parseHTML("" + elementHtml + "
", 2);
+ break;
+ default:
+ element = parseHTML(elementHtml, 1);
+ }
+
+ fragment.appendChild(element);
+ nextIndex = elementEndIndex;
}
+
+ // append trailing text nodes
+ if (elementEndIndex < resp.length) {
+ fragment.appendChild(parseHTML(resp.substring(elementEndIndex, resp.length), 1));
+ }
+
+ // @ts-ignore
+ return fragment;
}
}
diff --git a/test/attributes/hx-swap-oob.js b/test/attributes/hx-swap-oob.js
index e69c43e68..466d7f553 100644
--- a/test/attributes/hx-swap-oob.js
+++ b/test/attributes/hx-swap-oob.js
@@ -128,5 +128,41 @@ describe("hx-swap-oob attribute", function () {
this.server.respond();
should.equal(byId("d1"), null);
});
+
+ it('oob swap and table body can be combined', function()
+ {
+ this.server.respondWith("GET", "/test", 'Clicked
Row1 |
Row2 |
');
+
+ var table = make('');
+ make('Foo
')
+ table.click();
+ this.server.respond();
+ byId('tb').innerHTML.should.equal('Row1 |
Row2 |
');
+ byId('d1').innerHTML.should.equal('Clicked');
+ });
+
+ it('oob swap and table rows can be combined', function()
+ {
+ this.server.respondWith("GET", "/test", 'Row1 |
Row2 |
');
+
+ var table = make('');
+ make('Foo
')
+ table.click();
+ this.server.respond();
+ byId('tb').innerHTML.should.equal('Row1 |
Row2 |
');
+ byId('d1').innerHTML.should.equal('Clicked
');
+ });
+
+ it('oob swap and table columns can be combined', function()
+ {
+ this.server.respondWith("GET", "/test", 'Col1 | Col2 | ');
+
+ var table = make('');
+ make('Foo
')
+ table.click();
+ this.server.respond();
+ byId('tr').innerHTML.should.equal('Col1 | Col2 | ');
+ byId('d1').innerHTML.should.equal('Clicked
');
+ });
});
diff --git a/test/core/internals.js b/test/core/internals.js
index 1de1fb982..3b2f97b6f 100644
--- a/test/core/internals.js
+++ b/test/core/internals.js
@@ -15,10 +15,17 @@ describe("Core htmx internals Tests", function() {
//NB - the tag name should be the *parent* element hosting the HTML since we use the fragment children
// for the swap
- htmx._("makeFragment")(" | ").tagName.should.equal("TR");
- htmx._("makeFragment")("").tagName.should.equal("TABLE");
- htmx._("makeFragment")("").tagName.should.equal("COLGROUP");
- htmx._("makeFragment")("
").tagName.should.equal("TBODY");
+ htmx._("makeFragment")(" | ").firstElementChild.tagName.should.equal("TD");
+ htmx._("makeFragment")("").firstElementChild.tagName.should.equal("THEAD");
+ htmx._("makeFragment")("").firstElementChild.tagName.should.equal("COL");
+ htmx._("makeFragment")("
").firstElementChild.tagName.should.equal("TR");
+ })
+
+ it("makeFragment works with elements before table elements", function(){
+ var fragment = htmx._("makeFragment")(" | ");
+ fragment.children[0].tagName.should.equal("DIV");
+ fragment.children[1].tagName.should.equal("BR");
+ fragment.children[2].tagName.should.equal("TD");
})
it("makeFragment works with template wrapping", function(){
@@ -141,4 +148,4 @@ describe("Core htmx internals Tests", function() {
(value instanceof FormData).should.equal(true);
})
-});
\ No newline at end of file
+});