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(/]*>|' : ''; + 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('' + elementHtml + '
', 2); + break; + case "col": + element = parseHTML("" + elementHtml + "
", 3); + break; + case "tr": + element = parseHTML("" + elementHtml + "
", 3); + break; + case "td": + case "th": + element = parseHTML("" + elementHtml + "
", 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
Row1Row2'); + + var table = make('
'); + make('
Foo
') + table.click(); + this.server.respond(); + byId('tb').innerHTML.should.equal('Row1Row2'); + byId('d1').innerHTML.should.equal('Clicked'); + }); + + it('oob swap and table rows can be combined', function() + { + this.server.respondWith("GET", "/test", '
Clicked
Row1Row2'); + + var table = make('
'); + make('
Foo
') + table.click(); + this.server.respond(); + byId('tb').innerHTML.should.equal('Row1Row2'); + byId('d1').innerHTML.should.equal('
Clicked
'); + }); + + it('oob swap and table columns can be combined', function() + { + this.server.respondWith("GET", "/test", '
Clicked
Col1Col2'); + + var table = make('
'); + make('
Foo
') + table.click(); + this.server.respond(); + byId('tr').innerHTML.should.equal('Col1Col2'); + 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 +});