diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a676501..d99472fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog +## [1.5.1] - 2021-?-?? + ## [1.5.0] - 2021-7-12 * Support tracking of button clicked during a form submission diff --git a/README.md b/README.md index e1c26051..716d416d 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ By removing these arbitrary constraints htmx completes HTML as a ```html - + ') + btn.click(); + this.server.respond(); + btn.innerText.should.equal("Clicked!"); + window.document.title.should.equal("htmx rocks!"); + }); + + it('svg title tags do not update title', function() + { + var originalTitle = window.document.title + this.server.respondWith("GET", "/test", function (xhr) { + xhr.respond(200, {}, "" + originalTitle + "UPDATE" + "Clicked!"); + }); + var btn = make('') + btn.click(); + this.server.respond(); + btn.innerText.should.equal("Clicked!"); + window.document.title.should.equal(originalTitle); + }); + + it('first title tag outside svg title tags does update title', function() + { + var originalTitle = window.document.title + var newTitle = originalTitle + "!!!"; + this.server.respondWith("GET", "/test", function (xhr) { + xhr.respond(200, {}, "" + newTitle + "fooClicked!x"); + }); + var btn = make('') + btn.click(); + this.server.respond(); + btn.innerText.should.equal("Clicked!"); + window.document.title.should.equal(newTitle); + }); + + it('title update does not URL escapte', function() + { + this.server.respondWith("GET", "/test", function (xhr) { + xhr.respond(200, {}, "</> htmx rocks!Clicked!"); + }); + var btn = make('') + btn.click(); + this.server.respond(); + btn.innerText.should.equal("Clicked!"); + window.document.title.should.equal(" htmx rocks!"); + }); + + +}) diff --git a/www/test/1.5.1/test/core/api.js b/www/test/1.5.1/test/core/api.js new file mode 100644 index 00000000..2f316b29 --- /dev/null +++ b/www/test/1.5.1/test/core/api.js @@ -0,0 +1,312 @@ +describe("Core htmx API test", function(){ + beforeEach(function() { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function() { + this.server.restore(); + clearWorkArea(); + }); + + it('onLoad is called... onLoad', function(){ + // also tests on/off + this.server.respondWith("GET", "/test", "
") + var helper = htmx.onLoad(function (elt) { + elt.setAttribute("foo", "bar"); + }); + try { + var div = make("
"); + div.click(); + this.server.respond(); + byId("d1").getAttribute("foo").should.equal("bar"); + } finally { + htmx.off("htmx:load", helper); + } + }); + + it('triggers properly', function () { + var div = make("
"); + var myEventCalled = false; + var detailStr = ""; + htmx.on("myEvent", function(evt){ + myEventCalled = true; + detailStr = evt.detail.str; + }) + htmx.trigger(div, "myEvent", {str:"foo"}) + + myEventCalled.should.equal(true); + detailStr.should.equal("foo"); + }); + + it('triggers properly w/ selector', function () { + var div = make("
"); + var myEventCalled = false; + var detailStr = ""; + htmx.on("myEvent", function(evt){ + myEventCalled = true; + detailStr = evt.detail.str; + }) + htmx.trigger("#div1", "myEvent", {str:"foo"}) + + myEventCalled.should.equal(true); + detailStr.should.equal("foo"); + }); + + it('triggers with no details properly', function () { + var div = make("
"); + var myEventCalled = false; + htmx.on("myEvent", function(evt){ + myEventCalled = true; + }) + htmx.trigger(div, "myEvent") + myEventCalled.should.equal(true); + }); + + it('should find properly', function(){ + var div = make("
"); + div.should.equal(htmx.find("#d1")); + div.should.equal(htmx.find(".c1")); + div.should.equal(htmx.find(".c2")); + div.should.equal(htmx.find(".c1.c2")); + }); + + it('should find properly from elt', function(){ + var div = make("
"); + htmx.find(div, "a").id.should.equal('a1'); + }); + + it('should find all properly', function(){ + var div = make("
"); + htmx.findAll(".c1").length.should.equal(3); + htmx.findAll(".c2").length.should.equal(2); + htmx.findAll(".c3").length.should.equal(1); + }); + + it('should find all properly from elt', function(){ + var div = make("
"); + htmx.findAll(div, ".c1").length.should.equal(3); + htmx.findAll(div, ".c2").length.should.equal(2); + htmx.findAll(div,".c3").length.should.equal(1); + }); + + it('should find closest element properly', function () { + var div = make("
"); + var a = htmx.find(div, "a"); + htmx.closest(a, "div").should.equal(div); + }); + + it('should remove element properly', function () { + var div = make("
"); + var a = htmx.find(div, "a"); + htmx.remove(a); + div.innerHTML.should.equal(""); + }); + + it('should remove element properly w/ selector', function () { + var div = make("
"); + var a = htmx.find(div, "a"); + htmx.remove("#a1"); + div.innerHTML.should.equal(""); + }); + + it('should add class properly', function () { + var div = make("
"); + div.classList.contains("foo").should.equal(false); + htmx.addClass(div, "foo"); + div.classList.contains("foo").should.equal(true); + }); + + + it('should add class properly w/ selector', function () { + var div = make("
"); + div.classList.contains("foo").should.equal(false); + htmx.addClass("#div1", "foo"); + div.classList.contains("foo").should.equal(true); + }); + + it('should add class properly after delay', function (done) { + var div = make("
"); + div.classList.contains("foo").should.equal(false); + htmx.addClass(div, "foo", 10); + div.classList.contains("foo").should.equal(false); + setTimeout(function () { + div.classList.contains("foo").should.equal(true); + done(); + }, 20); + }); + + it('should remove class properly', function () { + var div = make("
"); + htmx.addClass(div, "foo"); + div.classList.contains("foo").should.equal(true); + htmx.removeClass(div, "foo"); + div.classList.contains("foo").should.equal(false); + }); + + it('should remove class properly w/ selector', function () { + var div = make("
"); + htmx.addClass(div, "foo"); + div.classList.contains("foo").should.equal(true); + htmx.removeClass("#div1", "foo"); + div.classList.contains("foo").should.equal(false); + }); + + it('should add class properly after delay', function (done) { + var div = make("
"); + htmx.addClass(div, "foo"); + div.classList.contains("foo").should.equal(true); + htmx.removeClass(div, "foo", 10); + div.classList.contains("foo").should.equal(true); + setTimeout(function () { + div.classList.contains("foo").should.equal(false); + done(); + }, 20); + }); + + it('should toggle class properly', function () { + var div = make("
"); + div.classList.contains("foo").should.equal(false); + htmx.toggleClass(div, "foo"); + div.classList.contains("foo").should.equal(true); + htmx.toggleClass(div, "foo"); + div.classList.contains("foo").should.equal(false); + }); + + it('should toggle class properly w/ selector', function () { + var div = make("
"); + div.classList.contains("foo").should.equal(false); + htmx.toggleClass("#div1", "foo"); + div.classList.contains("foo").should.equal(true); + htmx.toggleClass("#div1", "foo"); + div.classList.contains("foo").should.equal(false); + }); + + it('should take class properly', function () { + var div1 = make("
"); + var div2 = make("
"); + var div3 = make("
"); + + div1.classList.contains("foo").should.equal(false); + div2.classList.contains("foo").should.equal(false); + div3.classList.contains("foo").should.equal(false); + + htmx.takeClass(div1, "foo"); + + div1.classList.contains("foo").should.equal(true); + div2.classList.contains("foo").should.equal(false); + div3.classList.contains("foo").should.equal(false); + + htmx.takeClass(div2, "foo"); + + div1.classList.contains("foo").should.equal(false); + div2.classList.contains("foo").should.equal(true); + div3.classList.contains("foo").should.equal(false); + + htmx.takeClass(div3, "foo"); + + div1.classList.contains("foo").should.equal(false); + div2.classList.contains("foo").should.equal(false); + div3.classList.contains("foo").should.equal(true); + }); + + it('should take class properly w/ selector', function () { + var div1 = make("
"); + var div2 = make("
"); + var div3 = make("
"); + + div1.classList.contains("foo").should.equal(false); + div2.classList.contains("foo").should.equal(false); + div3.classList.contains("foo").should.equal(false); + + htmx.takeClass("#div1", "foo"); + + div1.classList.contains("foo").should.equal(true); + div2.classList.contains("foo").should.equal(false); + div3.classList.contains("foo").should.equal(false); + + htmx.takeClass("#div2", "foo"); + + div1.classList.contains("foo").should.equal(false); + div2.classList.contains("foo").should.equal(true); + div3.classList.contains("foo").should.equal(false); + + htmx.takeClass("#div3", "foo"); + + div1.classList.contains("foo").should.equal(false); + div2.classList.contains("foo").should.equal(false); + div3.classList.contains("foo").should.equal(true); + }); + + it('logAll works', function () { + var initialLogger = htmx.config.logger + try { + htmx.logAll(); + } finally { + htmx.config.logger = initialLogger; + } + }); + + it('eval can be suppressed', function () { + var calledEvent = false; + var handler = htmx.on("htmx:evalDisallowedError", function(){ + calledEvent = true; + }); + try { + htmx.config.allowEval = false; + should.equal(htmx._("tokenizeString"), undefined); + } finally { + htmx.config.allowEval = true; + htmx.off("htmx:evalDisallowedError", handler); + } + calledEvent.should.equal(true); + }); + + it('ajax api works', function() + { + this.server.respondWith("GET", "/test", "foo!"); + var div = make("
"); + htmx.ajax("GET", "/test", div) + this.server.respond(); + div.innerHTML.should.equal("foo!"); + }); + + it('ajax api works by ID', function() + { + this.server.respondWith("GET", "/test", "foo!"); + var div = make("
"); + htmx.ajax("GET", "/test", "#d1") + this.server.respond(); + div.innerHTML.should.equal("foo!"); + }); + + it('ajax returns a promise', function(done) + { + // in IE we do not return a promise + if (typeof Promise !== "undefined") { + this.server.respondWith("GET", "/test", "foo!"); + var div = make("
"); + var promise = htmx.ajax("GET", "/test", "#d1"); + this.server.respond(); + div.innerHTML.should.equal("foo!"); + promise.then(function(){ + done(); + }) + } else { + done(); + } + }); + + it('ajax api can pass parameters', function() + { + this.server.respondWith("POST", "/test", function (xhr) { + var params = getParameters(xhr); + params['i1'].should.equal("test"); + xhr.respond(200, {}, "Clicked!") + }); + var div = make("
"); + htmx.ajax("POST", "/test", {target:"#d1", values:{i1: 'test'}}) + this.server.respond(); + div.innerHTML.should.equal("Clicked!"); + }); + +}) diff --git a/www/test/1.5.1/test/core/events.js b/www/test/1.5.1/test/core/events.js new file mode 100644 index 00000000..11cea12d --- /dev/null +++ b/www/test/1.5.1/test/core/events.js @@ -0,0 +1,448 @@ +describe("Core htmx Events", function() { + beforeEach(function () { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function () { + this.server.restore(); + clearWorkArea(); + }); + + it("htmx:load fires properly", function () { + var called = false; + var handler = htmx.on("htmx:load", function (evt) { + called = true; + }); + try { + this.server.respondWith("GET", "/test", ""); + this.server.respondWith("GET", "/test", "
"); + var div = make("
"); + div.click(); + this.server.respond(); + should.equal(called, true); + } finally { + htmx.off("htmx:load", handler); + } + }); + + it("htmx:configRequest allows attribute addition", function () { + var handler = htmx.on("htmx:configRequest", function (evt) { + evt.detail.parameters['param'] = "true"; + }); + try { + var param = null; + this.server.respondWith("POST", "/test", function (xhr) { + param = getParameters(xhr)['param']; + xhr.respond(200, {}, ""); + }); + var div = make("
"); + div.click(); + this.server.respond(); + param.should.equal("true"); + } finally { + htmx.off("htmx:configRequest", handler); + } + }); + + it("htmx:configRequest is also dispatched in kebab-case", function () { + var handler = htmx.on("htmx:config-request", function (evt) { + evt.detail.parameters['param'] = "true"; + }); + try { + var param = null; + this.server.respondWith("POST", "/test", function (xhr) { + param = getParameters(xhr)['param']; + xhr.respond(200, {}, ""); + }); + var div = make("
"); + div.click(); + this.server.respond(); + param.should.equal("true"); + } finally { + htmx.off("htmx:config-request", handler); + } + }); + + it("events are only dispatched once if kebab and camel case match", function () { + var invoked = 0; + var handler = htmx.on("custom", function () { + invoked = invoked + 1 + }); + try { + var div = make("
"); + htmx.trigger(div, "custom"); + invoked.should.equal(1); + } finally { + htmx.off("custom", handler); + } + }); + + it("htmx:configRequest allows attribute removal", function () { + var param = "foo"; + var handler = htmx.on("htmx:configRequest", function (evt) { + delete evt.detail.parameters['param']; + }); + try { + this.server.respondWith("POST", "/test", function (xhr) { + param = getParameters(xhr)['param']; + xhr.respond(200, {}, ""); + }); + var div = make("
"); + div.click(); + this.server.respond(); + should.equal(param, undefined); + } finally { + htmx.off("htmx:configRequest", handler); + } + }); + + it("htmx:configRequest allows header tweaking", function () { + var header = "foo"; + var handler = htmx.on("htmx:configRequest", function (evt) { + evt.detail.headers['X-My-Header'] = "bar"; + }); + try { + this.server.respondWith("POST", "/test", function (xhr) { + header = xhr.requestHeaders['X-My-Header']; + xhr.respond(200, {}, ""); + }); + var div = make("
"); + div.click(); + this.server.respond(); + should.equal(header, "bar"); + } finally { + htmx.off("htmx:configRequest", handler); + } + }); + + it("htmx:afterSwap is called when replacing outerHTML", function () { + var called = false; + var handler = htmx.on("htmx:afterSwap", function (evt) { + called = true; + }); + try { + this.server.respondWith("POST", "/test", function (xhr) { + xhr.respond(200, {}, ""); + }); + var div = make(""); + div.click(); + this.server.respond(); + should.equal(called, true); + } finally { + htmx.off("htmx:afterSwap", handler); + } + }); + + it("htmx:afterSettle is called once when replacing outerHTML", function () { + var called = 0; + var handler = htmx.on("htmx:afterSettle", function (evt) { + called++; + }); + try { + this.server.respondWith("POST", "/test", function (xhr) { + xhr.respond(200, {}, ""); + }); + var div = make(""); + div.click(); + this.server.respond(); + should.equal(called, 1); + } finally { + htmx.off("htmx:afterSettle", handler); + } + }); + + it("htmx:afterSettle is called once when replacing outerHTML with whitespace", function () { + var called = 0; + var handler = htmx.on("htmx:afterSettle", function (evt) { + called++; + }); + try { + this.server.respondWith("POST", "/test", function (xhr) { + xhr.respond(200, {}, "\n"); + }); + var div = make(""); + div.click(); + this.server.respond(); + should.equal(called, 1); + } finally { + htmx.off("htmx:afterSettle", handler); + } + }); + + it("htmx:afterSettle is called twice when replacing outerHTML with whitespace separated elements", function () { + var called = 0; + var handler = htmx.on("htmx:afterSettle", function (evt) { + called++; + }); + try { + this.server.respondWith("POST", "/test", function (xhr) { + xhr.respond(200, {}, "\n Foo"); + }); + var div = make(""); + div.click(); + this.server.respond(); + should.equal(called, 2); + } finally { + htmx.off("htmx:afterSettle", handler); + } + }); + + it("htmx:afterRequest is called after a successful request", function () { + var called = false; + var handler = htmx.on("htmx:afterRequest", function (evt) { + called = true; + }); + try { + this.server.respondWith("POST", "/test", function (xhr) { + xhr.respond(200, {}, ""); + }); + var div = make(""); + div.click(); + this.server.respond(); + should.equal(called, true); + } finally { + htmx.off("htmx:afterRequest", handler); + } + }); + + it("htmx:afterOnLoad is called after a successful request", function () { + var called = false; + var handler = htmx.on("htmx:afterOnLoad", function (evt) { + called = true; + }); + try { + this.server.respondWith("POST", "/test", function (xhr) { + xhr.respond(200, {}, ""); + }); + var div = make(""); + div.click(); + this.server.respond(); + should.equal(called, true); + } finally { + htmx.off("htmx:afterOnLoad", handler); + } + }); + + it("htmx:afterRequest is called after a failed request", function () { + var called = false; + var handler = htmx.on("htmx:afterRequest", function (evt) { + called = true; + }); + try { + this.server.respondWith("POST", "/test", function (xhr) { + xhr.respond(200, {}, ""); + }); + var div = make(""); + div.click(); + this.server.respond(); + should.equal(called, true); + } finally { + htmx.off("htmx:afterRequest", handler); + } + }); + + it("htmx:sendError is called after a failed request", function (done) { + var called = false; + var handler = htmx.on("htmx:sendError", function (evt) { + called = true; + }); + this.server.restore(); // turn off server mock so connection doesn't work + var div = make(""); + div.click(); + setTimeout(function () { + htmx.off("htmx:sendError", handler); + should.equal(called, true); + done(); + }, 30); + }); + + it("htmx:afterRequest is called when replacing outerHTML", function () { + var called = false; + var handler = htmx.on("htmx:afterRequest", function (evt) { + called = true; + }); + try { + this.server.respondWith("POST", "/test", function (xhr) { + xhr.respond(200, {}, ""); + }); + var div = make(""); + div.click(); + this.server.respond(); + should.equal(called, true); + } finally { + htmx.off("htmx:afterRequest", handler); + } + }); + + it("htmx:afterOnLoad is called when replacing outerHTML", function () { + var called = false; + var handler = htmx.on("htmx:afterOnLoad", function (evt) { + called = true; + }); + try { + this.server.respondWith("POST", "/test", function (xhr) { + xhr.respond(200, {}, ""); + }); + var div = make(""); + div.click(); + this.server.respond(); + should.equal(called, true); + } finally { + htmx.off("htmx:afterOnLoad", handler); + } + }); + + it("htmx:beforeProcessNode is called when replacing outerHTML", function () { + var called = false; + var handler = htmx.on("htmx:beforeProcessNode", function (evt) { + called = true; + }); + try { + this.server.respondWith("POST", "/test", function (xhr) { + xhr.respond(200, {}, ""); + }); + var div = make(""); + div.click(); + this.server.respond(); + should.equal(called, true); + } finally { + htmx.off("htmx:beforeProcessNode", handler); + } + }); + + it("htmx:beforeProcessNode allows htmx attribute tweaking", function () { + var called = false; + var handler = htmx.on("htmx:beforeProcessNode", function (evt) { + evt.target.setAttribute("hx-post", "/success") + called = true; + }); + try { + this.server.respondWith("POST", "/success", function (xhr) { + xhr.respond(200, {}, ""); + }); + var div = make(""); + div.click(); + this.server.respond(); + should.equal(called, true); + } finally { + htmx.off("htmx:beforeProcessNode", handler); + } + }); + + it("htmx:afterProcessNode is called after replacing outerHTML", function () { + var called = false; + var handler = htmx.on("htmx:afterProcessNode", function (evt) { + called = true; + }); + try { + this.server.respondWith("POST", "/test", function (xhr) { + xhr.respond(200, {}, ""); + }); + var div = make(""); + div.click(); + this.server.respond(); + should.equal(called, true); + } finally { + htmx.off("htmx:afterProcessNode", handler); + } + }); + + it("htmx:afterRequest is called when targeting a parent div", function () { + var called = false; + var handler = htmx.on("htmx:afterRequest", function (evt) { + called = true; + }); + try { + this.server.respondWith("POST", "/test", function (xhr) { + xhr.respond(200, {}, ""); + }); + var div = make("
"); + var button = byId('b1'); + button.click(); + this.server.respond(); + should.equal(called, true); + } finally { + htmx.off("htmx:afterRequest", handler); + } + }); + + it("adding an error in htmx:configRequest stops the request", function () { + try { + var handler = htmx.on("htmx:configRequest", function (evt) { + evt.detail.errors.push("An error"); + }); + var request = false; + this.server.respondWith("POST", "/test", function (xhr) { + request = true; + xhr.respond(200, {}, ""); + }); + var div = make(""); + div.click(); + this.server.respond(); + should.equal(request, false); + } finally { + htmx.off("htmx:configRequest", handler); + } + }); + + it("preventDefault() in htmx:configRequest stops the request", function () { + try { + var handler = htmx.on("htmx:configRequest", function (evt) { + evt.detail.errors.push("An error"); + }); + var request = false; + this.server.respondWith("POST", "/test", function (xhr) { + request = true; + xhr.respond(200, {}, ""); + }); + var div = make(""); + div.click(); + this.server.respond(); + should.equal(request, false); + } finally { + htmx.off("htmx:configRequest", handler); + } + }); + + it("preventDefault() in the htmx:beforeRequest event cancels the request", function () { + try { + var handler = htmx.on("htmx:beforeRequest", function (evt) { + evt.preventDefault(); + }); + var request = false; + this.server.respondWith("POST", "/test", function (xhr) { + request = true; + xhr.respond(200, {}, ""); + }); + var div = make(""); + div.click(); + this.server.respond(); + should.equal(request, false); + } finally { + htmx.off("htmx:beforeRequest", handler); + } + }); + + it("preventDefault() in the htmx:beforeOnLoad event cancels the swap", function () { + try { + var handler = htmx.on("htmx:beforeOnLoad", function (evt) { + evt.preventDefault(); + }); + var request = false; + this.server.respondWith("POST", "/test", function (xhr) { + request = true; + xhr.respond(200, {}, "Bar"); + }); + var div = make(""); + div.click(); + this.server.respond(); + should.equal(request, true); + div.innerText.should.equal("Foo"); + } finally { + htmx.off("htmx:beforeOnLoad", handler); + } + }); + + +}); + diff --git a/www/test/1.5.1/test/core/headers.js b/www/test/1.5.1/test/core/headers.js new file mode 100644 index 00000000..cb8bab7a --- /dev/null +++ b/www/test/1.5.1/test/core/headers.js @@ -0,0 +1,201 @@ +describe("Core htmx AJAX headers", function () { + beforeEach(function () { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function () { + this.server.restore(); + clearWorkArea(); + }); + + it("should include the HX-Request header", function () { + this.server.respondWith("GET", "/test", function (xhr) { + xhr.requestHeaders['HX-Request'].should.be.equal('true'); + xhr.respond(200, {}, ""); + }); + var div = make('
'); + div.click(); + this.server.respond(); + }) + + it("should include the HX-Trigger header", function () { + this.server.respondWith("GET", "/test", function (xhr) { + xhr.requestHeaders['HX-Trigger'].should.equal('d1'); + xhr.respond(200, {}, ""); + }); + var div = make('
'); + div.click(); + this.server.respond(); + }) + + it("should include the HX-Trigger-Name header", function () { + this.server.respondWith("GET", "/test", function (xhr) { + xhr.requestHeaders['HX-Trigger-Name'].should.equal('n1'); + xhr.respond(200, {}, ""); + }); + var div = make(''); + div.click(); + this.server.respond(); + }) + + it("should include the HX-Target header", function () { + this.server.respondWith("GET", "/test", function (xhr) { + xhr.requestHeaders['HX-Target'].should.equal('d1'); + xhr.respond(200, {}, ""); + }); + var div = make('
'); + div.click(); + this.server.respond(); + }) + + it("should handle simple string HX-Trigger response header properly", function () { + this.server.respondWith("GET", "/test", [200, {"HX-Trigger": "foo"}, ""]); + + var div = make('
'); + var invokedEvent = false; + div.addEventListener("foo", function (evt) { + invokedEvent = true; + }); + div.click(); + this.server.respond(); + invokedEvent.should.equal(true); + }) + + it("should handle dot path HX-Trigger response header properly", function () { + this.server.respondWith("GET", "/test", [200, {"HX-Trigger": "foo.bar"}, ""]); + + var div = make('
'); + var invokedEvent = false; + div.addEventListener("foo.bar", function (evt) { + invokedEvent = true; + }); + div.click(); + this.server.respond(); + invokedEvent.should.equal(true); + }) + + it("should handle simple string HX-Trigger response header in different case properly", function () { + this.server.respondWith("GET", "/test", [200, {"hx-trigger": "foo"}, ""]); + + var div = make('
'); + var invokedEvent = false; + div.addEventListener("foo", function (evt) { + invokedEvent = true; + }); + div.click(); + this.server.respond(); + invokedEvent.should.equal(true); + }) + + it("should handle a namespaced HX-Trigger response header properly", function () { + this.server.respondWith("GET", "/test", [200, {"HX-Trigger": "namespace:foo"}, ""]); + + var div = make('
'); + var invokedEvent = false; + div.addEventListener("namespace:foo", function (evt) { + invokedEvent = true; + }); + div.click(); + this.server.respond(); + invokedEvent.should.equal(true); + }) + + it("should handle basic JSON HX-Trigger response header properly", function () { + this.server.respondWith("GET", "/test", [200, {"HX-Trigger": "{\"foo\":null}"}, ""]); + + var div = make('
'); + var invokedEvent = false; + div.addEventListener("foo", function (evt) { + invokedEvent = true; + should.equal(null, evt.detail.value); + evt.detail.elt.should.equal(div); + }); + div.click(); + this.server.respond(); + invokedEvent.should.equal(true); + }) + + it("should handle JSON with array arg HX-Trigger response header properly", function () { + this.server.respondWith("GET", "/test", [200, {"HX-Trigger": "{\"foo\":[1, 2, 3]}"}, ""]); + + var div = make('
'); + var invokedEvent = false; + div.addEventListener("foo", function (evt) { + invokedEvent = true; + evt.detail.elt.should.equal(div); + evt.detail.value.should.deep.equal([1, 2, 3]); + }); + div.click(); + this.server.respond(); + invokedEvent.should.equal(true); + }) + + it("should handle JSON with array arg HX-Trigger response header properly", function () { + this.server.respondWith("GET", "/test", [200, {"HX-Trigger": "{\"foo\":{\"a\":1, \"b\":2}}"}, ""]); + + var div = make('
'); + var invokedEvent = false; + div.addEventListener("foo", function (evt) { + invokedEvent = true; + evt.detail.elt.should.equal(div); + evt.detail.a.should.equal(1); + evt.detail.b.should.equal(2); + }); + div.click(); + this.server.respond(); + invokedEvent.should.equal(true); + }) + + it("should survive malformed JSON in HX-Trigger response header", function () { + this.server.respondWith("GET", "/test", [200, {"HX-Trigger": "{not: valid}"}, ""]); + + var div = make('
'); + div.click(); + this.server.respond(); + }) + + it("should handle simple string HX-Trigger response header properly w/ outerHTML swap", function () { + this.server.respondWith("GET", "/test", [200, {"HX-Trigger": "foo"}, ""]); + + var div = make('
'); + var invokedEvent = false; + var handler = htmx.on('foo', function (evt) { + invokedEvent = true; + }); + div.click(); + this.server.respond(); + invokedEvent.should.equal(true); + htmx.off('foo', handler); + }) + + + it("should handle simple string HX-Trigger-After-Swap response header properly w/ outerHTML swap", function () { + this.server.respondWith("GET", "/test", [200, {"HX-Trigger-After-Swap": "foo"}, ""]); + + var div = make('
'); + var invokedEvent = false; + var handler = htmx.on('foo', function (evt) { + invokedEvent = true; + }); + div.click(); + this.server.respond(); + invokedEvent.should.equal(true); + htmx.off('foo', handler); + }) + + it("should handle simple string HX-Trigger-After-Settle response header properly w/ outerHTML swap", function () { + this.server.respondWith("GET", "/test", [200, {"HX-Trigger-After-Settle": "foo"}, ""]); + + var div = make('
'); + var invokedEvent = false; + var handler = htmx.on('foo', function (evt) { + invokedEvent = true; + }); + div.click(); + this.server.respond(); + invokedEvent.should.equal(true); + htmx.off('foo', handler); + }) + + +}); diff --git a/www/test/1.5.1/test/core/internals.js b/www/test/1.5.1/test/core/internals.js new file mode 100644 index 00000000..567f8c19 --- /dev/null +++ b/www/test/1.5.1/test/core/internals.js @@ -0,0 +1,101 @@ +describe("Core htmx internals Tests", function() { + + it("makeFragment works with janky stuff", function(){ + htmx._("makeFragment")("").tagName.should.equal("BODY"); + htmx._("makeFragment")("").tagName.should.equal("BODY"); + + //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"); + }) + + it("makeFragment works with template wrapping", function(){ + htmx.config.useTemplateFragments = true; + try { + htmx._("makeFragment")("").children.length.should.equal(0); + htmx._("makeFragment")("").children.length.should.equal(0); + + var fragment = htmx._("makeFragment")(""); + fragment.firstElementChild.tagName.should.equal("TD"); + + fragment = htmx._("makeFragment")(""); + fragment.firstElementChild.tagName.should.equal("THEAD"); + + fragment = htmx._("makeFragment")(""); + fragment.firstElementChild.tagName.should.equal("COL"); + + fragment = htmx._("makeFragment")(""); + fragment.firstElementChild.tagName.should.equal("TR"); + + } finally { + htmx.config.useTemplateFragments = false; + } + }) + + it("makeFragment works with template wrapping and funky combos", function(){ + htmx.config.useTemplateFragments = true; + try { + var fragment = htmx._("makeFragment")("
"); + fragment.children[0].tagName.should.equal("TD"); + fragment.children[1].tagName.should.equal("DIV"); + } finally { + htmx.config.useTemplateFragments = false; + } + }) + + it("set header works with non-ASCII values", function(){ + var xhr = new XMLHttpRequest(); + xhr.open("GET", "/dummy"); + htmx._("safelySetHeaderValue")(xhr, "Example", "привет"); + // unfortunately I can't test the value :/ + // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest + }) + + it("handles parseInterval correctly", function() { + chai.expect(htmx.parseInterval("1ms")).to.be.equal(1); + chai.expect(htmx.parseInterval("300ms")).to.be.equal(300); + chai.expect(htmx.parseInterval("1s")).to.be.equal(1000) + chai.expect(htmx.parseInterval("1.5s")).to.be.equal(1500) + chai.expect(htmx.parseInterval("2s")).to.be.equal(2000) + + chai.expect(htmx.parseInterval(null)).to.be.undefined + chai.expect(htmx.parseInterval("")).to.be.undefined + chai.expect(htmx.parseInterval("undefined")).to.be.undefined + chai.expect(htmx.parseInterval("true")).to.be.undefined + chai.expect(htmx.parseInterval("false")).to.be.undefined + }) + + it("tokenizes correctly", function() { + chai.expect(htmx._("tokenizeString")("a,")).to.be.deep.equal(['a', ',']); + chai.expect(htmx._("tokenizeString")("aa,")).to.be.deep.equal(['aa', ',']); + chai.expect(htmx._("tokenizeString")("aa,aa")).to.be.deep.equal(['aa', ',', 'aa']); + chai.expect(htmx._("tokenizeString")("aa.aa")).to.be.deep.equal(['aa', '.', 'aa']); + }) + + it("tags respond correctly to shouldCancel", function() { + var anchorThatShouldCancel = make(""); + htmx._("shouldCancel")(anchorThatShouldCancel).should.equal(true); + + var anchorThatShouldCancel = make(""); + htmx._("shouldCancel")(anchorThatShouldCancel).should.equal(true); + + var anchorThatShouldNotCancel = make(""); + htmx._("shouldCancel")(anchorThatShouldNotCancel).should.equal(false); + + var form = make("
"); + htmx._("shouldCancel")(form).should.equal(true); + + var form = make("
"); + var input = byId("i1"); + htmx._("shouldCancel")(input).should.equal(true); + + var form = make("
'); + var input = byId('i1'); + var button = byId('b1'); + button.focus(); + var vals = htmx._('getInputValues')(form).values; + vals['foo'].should.equal('bar'); + vals['do'].should.equal('rey'); + vals['btn'].should.equal('bar'); + }) + + it('form includes last focused submit', function () { + var form = make('
'); + var input = byId('i1'); + var button = byId('s1'); + button.focus(); + var vals = htmx._('getInputValues')(form).values; + vals['foo'].should.equal('bar'); + vals['do'].should.equal('rey'); + vals['s1'].should.equal('bar'); + }) + + it('form does not include button when focus is lost', function () { + var form = make('
'); + var input = byId('i1'); + var button = byId('s1'); + button.focus(); + input.focus(); + var vals = htmx._('getInputValues')(form).values; + vals['foo'].should.equal('bar'); + vals['do'].should.equal('rey'); + should.equal(vals['s1'], undefined); + }) + + it('form does not include button when focus is lost outside of form', function () { + var form = make('
'); + var anchor = make(''); + var button = byId('s1'); + button.focus(); + anchor.focus(); + var vals = htmx._('getInputValues')(form).values; + vals['foo'].should.equal('bar'); + vals['do'].should.equal('rey'); + should.equal(vals['s1'], undefined); + }) + +}); + diff --git a/www/test/1.5.1/test/core/perf.js b/www/test/1.5.1/test/core/perf.js new file mode 100644 index 00000000..c461aee1 --- /dev/null +++ b/www/test/1.5.1/test/core/perf.js @@ -0,0 +1,64 @@ +describe("Core htmx perf Tests", function() { + + var HTMX_HISTORY_CACHE_NAME = "htmx-history-cache"; + + beforeEach(function () { + this.server = makeServer(); + clearWorkArea(); + localStorage.removeItem(HTMX_HISTORY_CACHE_NAME); + }); + afterEach(function () { + this.server.restore(); + clearWorkArea(); + localStorage.removeItem(HTMX_HISTORY_CACHE_NAME); + }); + + function stringRepeat(str, num) { + num = Number(num); + + var result = ''; + while (true) { + if (num & 1) { // (1) + result += str; + } + num >>>= 1; // (2) + if (num <= 0) break; + str += str; + } + + return result; + } + + + it("history implementation should be fast", function(){ + // create an entry with a large content string (256k) and see how fast we can write and read it + // to local storage as a single entry + var entry = {url: stringRepeat("x", 32), content:stringRepeat("x", 256*1024)} + var array = []; + for (var i = 0; i < 10; i++) { + array.push(entry); + } + var start = performance.now(); + var string = JSON.stringify(array); + localStorage.setItem(HTMX_HISTORY_CACHE_NAME, string); + var reReadString = localStorage.getItem(HTMX_HISTORY_CACHE_NAME); + var finalJson = JSON.parse(reReadString); + var end = performance.now(); + var timeInMs = end - start; + chai.assert(timeInMs < 300, "Should take less than 300ms on most platforms"); + }) + + it("history snapshot cleaning should be fast", function(){ + // + var workArea = getWorkArea(); + var html = "
Yay, really large HTML documents are fun!
\n"; + html = stringRepeat(html, 5 * 1024); // ~350K in size, about the size of CNN's body tag :p + workArea.insertAdjacentHTML("beforeend", html) + var start = performance.now(); + htmx._("cleanInnerHtmlForHistory")(workArea); + var end = performance.now(); + var timeInMs = end - start; + chai.assert(timeInMs < 50, "Should take less than 50ms on most platforms"); + }) + +}) \ No newline at end of file diff --git a/www/test/1.5.1/test/core/regressions.js b/www/test/1.5.1/test/core/regressions.js new file mode 100644 index 00000000..a6bb4726 --- /dev/null +++ b/www/test/1.5.1/test/core/regressions.js @@ -0,0 +1,175 @@ +describe("Core htmx Regression Tests", function(){ + + beforeEach(function() { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function() { + this.server.restore(); + clearWorkArea(); + }); + + it('SVGs process properly in IE11', function() + { + var btn = make('\n' + + '\n' + + '\n' + + '\n' + + '') + }); + + it ('Handles https://github.com/bigskysoftware/htmx/issues/4 properly', function() { + this.server.respondWith("GET", "/index2a.php", + "
I came from message oob swap I should be second
" + + "
I came from a message2 oob swap I should be third but I am in the wrong spot
" + + "I'm page2 content (non-swap) I should be first") + + var h1 = make("" + + "
" + + "
" + + "
" + + "

Kutty CLICK ME

") + h1.click(); + this.server.respond(); + htmx.find("#page2").innerHTML.should.equal("I'm page2 content (non-swap) I should be first") + htmx.find("#message").innerHTML.should.equal("I came from message oob swap I should be second") + htmx.find("#message2").innerHTML.should.equal("I came from a message2 oob swap I should be third but I am in the wrong spot") + }); + + it ('Handles https://github.com/bigskysoftware/htmx/issues/33 "empty values" properly', function() { + this.server.respondWith("POST", "/htmx.php", function (xhr) { + xhr.respond(200, {}, xhr.requestBody); + }); + + var form = make('
\n' + + '\n' + + '\n' + + '
') + form.click(); + this.server.respond(); + form.innerHTML.should.equal("variable=") + }); + + it ('name=id doesnt cause an error', function(){ + this.server.respondWith("GET", "/test", "Foo
") + var div = make('
Get It
') + div.click(); + this.server.respond(); + div.innerText.should.contain("Foo") + }); + + it ('empty id doesnt cause an error', function(){ + this.server.respondWith("GET", "/test", "Foo\n
") + var div = make('
Get It
') + div.click(); + this.server.respond(); + div.innerText.should.contain("Foo") + }); + + it ('id with dot in value doesnt cause an error', function(){ + this.server.respondWith("GET", "/test", "Foo
"); + var div = make('
Get It
'); + div.click(); + this.server.respond(); + div.innerText.should.contain("Foo"); + }); + + it ('@ symbol in attributes does not break requests', function(){ + this.server.respondWith("GET", "/test", "
Foo
"); + var div = make('
Get It
'); + div.click(); + this.server.respond(); + byId("d1").getAttribute('@foo').should.equal('bar'); + }); + + it ('@ symbol in attributes does not break attribute settling requests', function(){ + this.server.respondWith("GET", "/test", "
Foo
"); + var div = make('
Foo
'); + div.click(); + this.server.respond(); + byId("d1").getAttribute('@foo').should.equal('bar'); + }); + + it ('selected element with ID does not cause NPE when it disappears', function(){ + this.server.respondWith("GET", "/test", "
Replaced
"); + var input = make(''); + input.focus(); + input.click(); + this.server.respond(); + byId("d1").innerText.should.equal('Replaced'); + }); + + it('does not submit with a false condition on a form', function() { + this.server.respondWith("POST", "/test", "Submitted"); + var defaultPrevented = false; + htmx.on("click", function(evt) { + defaultPrevented = evt.defaultPrevented; + }) + var form = make('
'); + form.click() + this.server.respond(); + defaultPrevented.should.equal(true); + }) + + it('two elements can listen for the same event on another element', function() { + this.server.respondWith("GET", "/test", "triggered"); + + make('
' + + '
'); + + + var div1 = byId("d1"); + var div2 = byId("d2"); + + document.body.click(); + this.server.respond(); + + div2.innerHTML.should.equal("triggered"); + div1.innerHTML.should.equal("triggered"); + }) + + it('a form can reset based on the htmx:afterRequest event', function() { + this.server.respondWith("POST", "/test", "posted"); + //htmx.logAll(); + + var form = make('
' + + ' ' + + ' ' + + '
'); + htmx.trigger(form, "htmx:load"); // have to manually trigger the load event for non-AJAX dynamic content + + var div1 = byId("d1"); + var input = byId("i1"); + input.value = "foo"; + var submit = byId("s1"); + + input.value.should.equal("foo"); + submit.click(); + this.server.respond(); + + div1.innerHTML.should.equal("posted"); + input.value.should.equal(""); // form should be reset + }) + + it('supports image maps', function() { + this.server.respondWith("GET", "/test", "triggered"); + + make('
' + + '
' + + ' ' + + '' + + ' ' + + ' Computer' + + ' ' + + '
'); + + var div1 = byId("d1"); + var area = document.getElementsByTagName('area')[0]; + + area.click(); + this.server.respond(); + + div1.innerHTML.should.equal("triggered"); + }) + +}) diff --git a/www/test/1.5.1/test/core/security.js b/www/test/1.5.1/test/core/security.js new file mode 100644 index 00000000..d4be7216 --- /dev/null +++ b/www/test/1.5.1/test/core/security.js @@ -0,0 +1,32 @@ +describe("security options", function() { + + beforeEach(function() { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function() { + this.server.restore(); + clearWorkArea(); + }); + + it("can disable a single elt", function(){ + this.server.respondWith("GET", "/test", "Clicked!"); + + var btn = make('') + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("Initial"); + }) + + it("can disable a parent elt", function(){ + this.server.respondWith("GET", "/test", "Clicked!"); + + var div = make('
') + var btn = byId("b1"); + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("Initial"); + }) + + +}); \ No newline at end of file diff --git a/www/test/1.5.1/test/core/tokenizer.js b/www/test/1.5.1/test/core/tokenizer.js new file mode 100644 index 00000000..3071ad9b --- /dev/null +++ b/www/test/1.5.1/test/core/tokenizer.js @@ -0,0 +1,48 @@ +describe("Core htmx tokenizer tests", function(){ + beforeEach(function() { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function() { + this.server.restore(); + clearWorkArea(); + }); + + function tokenize(str) { + return htmx._("tokenizeString")(str); + } + + function tokenizeTest(str, result) { + return tokenize(str).should.deep.equal(result); + } + + it('tokenizes properly', function() + { + tokenizeTest("", []); + tokenizeTest(" ", [" ", " "]); + tokenizeTest("(", ["("]); + tokenizeTest("()", ["(", ")"]); + tokenizeTest("(,)", ["(", ",", ")"]); + tokenizeTest(" ( ) ", [" ", "(", " ", ")", " "]); + tokenizeTest(" && ) ", [" ", "&", "&", " ", ")", " "]); + tokenizeTest(" && ) 'asdf'", [" ", "&", "&", " ", ")", " ", "'asdf'"]); + tokenizeTest(" && ) ',asdf'", [" ", "&", "&", " ", ")", " ", "',asdf'"]); + tokenizeTest('",asdf"', ['",asdf"']); + tokenizeTest('&& ) ",asdf"', ["&", "&", " ", ")", " ", '",asdf"']); + }); + + it('generates conditionals property', function() + { + var tokens = tokenize("[code==4||(code==5&&foo==true)]"); + var conditional = htmx._("maybeGenerateConditional")(null, tokens); + var func = eval(conditional); + func({code: 5, foo: true}).should.equal(true); + func({code: 5, foo: false}).should.equal(false); + func({code: 4, foo: false}).should.equal(true); + func({code: 3, foo: true}).should.equal(false); + }); + + + + +}) diff --git a/www/test/1.5.1/test/core/validation.js b/www/test/1.5.1/test/core/validation.js new file mode 100644 index 00000000..e7f34571 --- /dev/null +++ b/www/test/1.5.1/test/core/validation.js @@ -0,0 +1,159 @@ +describe("Core htmx client side validation tests", function(){ + beforeEach(function() { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function() { + this.server.restore(); + clearWorkArea(); + }); + + it('HTML5 required validation error prevents request', function() + { + this.server.respondWith("POST", "/test", "Clicked!"); + + var form = make('
' + + 'No Request' + + '' + + '
'); + form.textContent.should.equal("No Request"); + form.click(); + this.server.respond(); + form.textContent.should.equal("No Request"); + byId("i1").value = "foo"; + form.click(); + this.server.respond(); + form.textContent.should.equal("Clicked!"); + }); + + it('Novalidate skips form validation', function() + { + this.server.respondWith("POST", "/test", "Clicked!"); + + var form = make('
' + + 'No Request' + + '' + + '
'); + form.textContent.should.equal("No Request"); + form.click(); + this.server.respond(); + form.textContent.should.equal("Clicked!"); + }); + + it('Validation skipped for indirect form submission', function() + { + this.server.respondWith("POST", "/test", "Clicked!"); + + var form = make('
' + + 'No Request' + + '' + + '' + + '
'); + form.textContent.should.equal("No Request"); + byId("button").click(); + this.server.respond(); + form.textContent.should.equal("Clicked!"); + }); + + it('HTML5 pattern validation error prevents request', function() + { + this.server.respondWith("POST", "/test", "Clicked!"); + + var form = make('
' + + 'No Request' + + '' + + '
'); + byId("i1").value = "xyz"; + form.textContent.should.equal("No Request"); + form.click(); + this.server.respond(); + form.textContent.should.equal("No Request"); + byId("i1").value = "abc"; + form.click(); + this.server.respond(); + form.textContent.should.equal("Clicked!"); + }); + + it('Custom validation error prevents request', function() + { + this.server.respondWith("POST", "/test", "Clicked!"); + + var form = make('
' + + 'No Request' + + '' + + '
'); + byId("i1").setCustomValidity("Nope"); + form.textContent.should.equal("No Request"); + form.click(); + this.server.respond(); + form.textContent.should.equal("No Request"); + byId("i1").setCustomValidity(""); + form.click(); + this.server.respond(); + form.textContent.should.equal("Clicked!"); + }); + + it('hyperscript validation error prevents request', function() + { + this.server.respondWith("POST", "/test", "Clicked!"); + + var form = make('
' + + 'No Request' + + '' + + '
'); + htmx.trigger(form, "htmx:load"); + byId("i1").value = "boo"; + form.textContent.should.equal("No Request"); + form.click(); + this.server.respond(); + form.textContent.should.equal("No Request"); + byId("i1").value = "foo"; + form.click(); + this.server.respond(); + form.textContent.should.equal("Clicked!"); + }); + + it('calls htmx:validation:failed on failure', function() + { + var form = make('
' + + 'No Request' + + '' + + '
'); + var calledEvent = false; + var handler = htmx.on(form, "htmx:validation:failed", function(){ + calledEvent = true; + }); + try { + form.click(); + this.server.respond(); + } finally { + htmx.off(form, handler); + } + calledEvent.should.equal(true); + }); + + it('calls htmx:validation:halted on failure', function() + { + var form = make('
' + + 'No Request' + + '' + + '
'); + var errors = null; + var handler = htmx.on(form, "htmx:validation:halted", function(evt){ + errors = evt.detail.errors; + }); + try { + form.click(); + this.server.respond(); + } finally { + htmx.off(form, handler); + } + errors.length.should.equal(1); + byId("i1").should.equal(errors[0].elt); + errors[0].validity.valueMissing.should.equal(true); + }); + + + +}) diff --git a/www/test/1.5.1/test/core/verbs.js b/www/test/1.5.1/test/core/verbs.js new file mode 100644 index 00000000..1d0711c2 --- /dev/null +++ b/www/test/1.5.1/test/core/verbs.js @@ -0,0 +1,44 @@ +describe("Core htmx AJAX Verbs", function() { + beforeEach(function () { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function () { + this.server.restore(); + clearWorkArea(); + }); + + it('handles basic posts properly', function () { + this.server.respondWith("POST", "/test", "post"); + var div = make('
click me
'); + div.click(); + this.server.respond(); + div.innerHTML.should.equal("post"); + }) + + it('handles basic put properly', function () { + this.server.respondWith("PUT", "/test", "put"); + var div = make('
click me
'); + div.click(); + this.server.respond(); + div.innerHTML.should.equal("put"); + }) + + it('handles basic patch properly', function () { + this.server.respondWith("PATCH", "/test", "patch"); + var div = make('
click me
'); + div.click(); + this.server.respond(); + div.innerHTML.should.equal("patch"); + }) + + it('handles basic delete properly', function () { + this.server.respondWith("DELETE", "/test", "delete"); + var div = make('
click me
'); + div.click(); + this.server.respond(); + div.innerHTML.should.equal("delete"); + }) + +}); + diff --git a/www/test/1.5.1/test/ext/ajax-header.js b/www/test/1.5.1/test/ext/ajax-header.js new file mode 100644 index 00000000..0888ef3b --- /dev/null +++ b/www/test/1.5.1/test/ext/ajax-header.js @@ -0,0 +1,21 @@ +describe("ajax-header extension", function() { + beforeEach(function () { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function () { + this.server.restore(); + clearWorkArea(); + }); + + it('Sends the X-Requested-With header', function () { + this.server.respondWith("GET", "/test", function (xhr) { + xhr.respond(200, {}, xhr.requestHeaders['X-Requested-With']) + }); + var btn = make('') + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("XMLHttpRequest"); + }); + +}); \ No newline at end of file diff --git a/www/test/1.5.1/test/ext/bad-extension.js b/www/test/1.5.1/test/ext/bad-extension.js new file mode 100644 index 00000000..fcf6a438 --- /dev/null +++ b/www/test/1.5.1/test/ext/bad-extension.js @@ -0,0 +1,27 @@ +describe("bad extension", function() { + htmx.defineExtension("bad-extension", { + onEvent : function(name, evt) {throw "onEvent"}, + transformResponse : function(text, xhr, elt) {throw "transformRequest"}, + isInlineSwap : function(swapStyle) {throw "isInlineSwap"}, + handleSwap : function(swapStyle, target, fragment, settleInfo) {throw "handleSwap"}, + encodeParameters : function(xhr, parameters, elt) {throw "encodeParmeters"} + } + ) + beforeEach(function () { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function () { + this.server.restore(); + clearWorkArea(); + }); + + it('does not blow up rendering', function () { + this.server.respondWith("GET", "/test", "clicked!"); + var div = make('
Click Me!
') + div.click(); + this.server.respond(); + div.innerHTML.should.equal("clicked!"); + }); + +}); \ No newline at end of file diff --git a/www/test/1.5.1/test/ext/class-tools.js b/www/test/1.5.1/test/ext/class-tools.js new file mode 100644 index 00000000..0c5004b5 --- /dev/null +++ b/www/test/1.5.1/test/ext/class-tools.js @@ -0,0 +1,55 @@ +describe("class-tools extension", function(){ + beforeEach(function() { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function() { + this.server.restore(); + clearWorkArea(); + }); + + it('adds classes properly', function(done) + { + var div = make('
Click Me!
') + should.equal(div.classList.length, 0); + setTimeout(function(){ + should.equal(div.classList.contains("c1"), true); + done(); + }, 100); + }); + + it('removes classes properly', function(done) + { + var div = make('
Click Me!
') + should.equal(div.classList.contains("foo"), true); + should.equal(div.classList.contains("bar"), true); + setTimeout(function(){ + should.equal(div.classList.contains("foo"), true); + should.equal(div.classList.contains("bar"), false); + done(); + }, 100); + }); + + it('adds classes properly w/ data-* prefix', function(done) + { + var div = make('
Click Me!
') + should.equal(div.classList.length, 0); + setTimeout(function(){ + should.equal(div.classList.contains("c1"), true); + done(); + }, 100); + }); + + it('extension can be on parent', function(done) + { + var div = make('
Click Me!
') + should.equal(div.classList.length, 0); + setTimeout(function(){ + should.equal(div.classList.contains("c1"), false); + should.equal(byId("d1").classList.contains("c1"), true); + done(); + }, 100); + }); + + +}) diff --git a/www/test/1.5.1/test/ext/client-side-templates.js b/www/test/1.5.1/test/ext/client-side-templates.js new file mode 100644 index 00000000..29206d21 --- /dev/null +++ b/www/test/1.5.1/test/ext/client-side-templates.js @@ -0,0 +1,30 @@ +describe("client-side-templates extension", function() { + beforeEach(function () { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function () { + this.server.restore(); + clearWorkArea(); + }); + + it('works on basic mustache template', function () { + this.server.respondWith("GET", "/test", '{"foo":"bar"}'); + var btn = make('') + make('') + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("*bar*"); + }); + + it('works on basic handlebars template', function () { + this.server.respondWith("GET", "/test", '{"foo":"bar"}'); + var btn = make('') + Handlebars.partials["hb1"] = Handlebars.compile("*{{foo}}*"); + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("*bar*"); + }); + + +}); \ No newline at end of file diff --git a/www/test/1.5.1/test/ext/debug.js b/www/test/1.5.1/test/ext/debug.js new file mode 100644 index 00000000..4eeca295 --- /dev/null +++ b/www/test/1.5.1/test/ext/debug.js @@ -0,0 +1,19 @@ +describe("debug extension", function() { + beforeEach(function () { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function () { + this.server.restore(); + clearWorkArea(); + }); + + it('works on basic request', function () { + this.server.respondWith("GET", "/test", "Clicked!"); + var btn = make('') + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("Clicked!"); + }); + +}); \ No newline at end of file diff --git a/www/test/1.5.1/test/ext/event-header.js b/www/test/1.5.1/test/ext/event-header.js new file mode 100644 index 00000000..a5a66cf5 --- /dev/null +++ b/www/test/1.5.1/test/ext/event-header.js @@ -0,0 +1,23 @@ +describe("event-header extension", function() { + beforeEach(function () { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function () { + this.server.restore(); + clearWorkArea(); + }); + + it('Sends the Triggering-Event header', function () { + this.server.respondWith("GET", "/test", function (xhr) { + xhr.respond(200, {}, xhr.requestHeaders['Triggering-Event']) + }); + var btn = make('') + btn.click(); + this.server.respond(); + var json = JSON.parse(btn.innerText); + json.type.should.equal("click"); + json.target.should.equal("button"); + }); + +}); \ No newline at end of file diff --git a/www/test/1.5.1/test/ext/extension-swap.js b/www/test/1.5.1/test/ext/extension-swap.js new file mode 100644 index 00000000..0186b525 --- /dev/null +++ b/www/test/1.5.1/test/ext/extension-swap.js @@ -0,0 +1,60 @@ +describe("default extensions behavior", function() { + + var loadCalls, afterSwapCalls, afterSettleCalls; + + beforeEach(function () { + loadCalls = []; + this.server = makeServer(); + clearWorkArea(); + + htmx.defineExtension("ext-testswap", { + onEvent : function(name, evt) { + if (name === "htmx:load") { + loadCalls.push(evt.detail.elt); + } + }, + handleSwap: function (swapStyle, target, fragment, settleInfo) { + // simple outerHTML replacement for tests + var parentEl = target.parentElement; + parentEl.removeChild(target); + return [parentEl.appendChild(fragment)]; // return the newly added element + } + + }); + + }); + + afterEach(function () { + this.server.restore(); + clearWorkArea(); + htmx.removeExtension("ext-testswap"); + }); + + it('handleSwap: afterSwap and afterSettle triggered if extension defined on parent', function () { + this.server.respondWith("GET", "/test", ''); + var div = make('
'); + var btn = div.firstChild; + btn.click() + this.server.respond(); + loadCalls.length.should.equal(1); + loadCalls[0].textContent.should.equal('Clicked!'); // the new button is loaded + }); + + it('handleSwap: new content is handled by htmx', function() { + this.server.respondWith("GET", "/test", ''); + this.server.respondWith("GET", "/test-inner", 'Loaded!'); + make('
').querySelector('button').click(); + + this.server.respond(); // call /test via button trigger=click + var btn = byId('test-ext-testswap'); + btn.textContent.should.equal('Clicked!'); + loadCalls.length.should.equal(1); + loadCalls[0].textContent.should.equal('Clicked!'); // the new button is loaded + + this.server.respond(); // call /test-inner via span trigger=load + btn.textContent.should.equal("Clicked!Loaded!"); + loadCalls.length.should.equal(2); + loadCalls[1].textContent.should.equal('Loaded!'); // the new span is loaded + }); + +}); diff --git a/www/test/1.5.1/test/ext/hyperscript.js b/www/test/1.5.1/test/ext/hyperscript.js new file mode 100644 index 00000000..47e008ce --- /dev/null +++ b/www/test/1.5.1/test/ext/hyperscript.js @@ -0,0 +1,64 @@ +describe("hyperscript integration", function() { + beforeEach(function () { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function () { + this.server.restore(); + clearWorkArea(); + }); + + it('can trigger with a custom event', function () { + this.server.respondWith("GET", "/test", "Custom Event Sent!"); + var btn = make('') + htmx.trigger(btn, "htmx:load"); // have to manually trigger the load event for non-AJAX dynamic content + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("Custom Event Sent!"); + }); + + it('can handle htmx driven events', function () { + this.server.respondWith("GET", "/test", "Clicked!"); + var btn = make('') + htmx.trigger(btn, "htmx:load"); + btn.classList.contains("afterSettle").should.equal(false); + btn.click(); + this.server.respond(); + btn.classList.contains("afterSettle").should.equal(true); + }); + + it('can handle htmx error events', function () { + this.server.respondWith("GET", "/test", [404, {}, "Bad request"]); + var div = make('
') + var btn = make('') + htmx.trigger(btn, "htmx:load"); + btn.click(); + this.server.respond(); + div.innerHTML.should.equal("Response Status Error Code 404 from /test"); + }); + + it('hyperscript in non-htmx annotated nodes is evaluated', function () { + this.server.respondWith("GET", "/test", "
"); + var btn = make('') + btn.click(); + this.server.respond(); + var newDiv = byId("d1"); + newDiv.click(); + newDiv.innerText.should.equal("Clicked..."); + }); + + it('hyperscript removal example works', function (done) { + this.server.respondWith("GET", "/test", "
To Remove
"); + var btn = make('') + btn.click(); + this.server.respond(); + var newDiv = byId("d1"); + newDiv.innerText.should.equal("To Remove") + setTimeout(function(){ + newDiv = byId("d1"); + should.equal(newDiv, null); + done(); + }, 100); + }); + +}); \ No newline at end of file diff --git a/www/test/1.5.1/test/ext/include-vals.js b/www/test/1.5.1/test/ext/include-vals.js new file mode 100644 index 00000000..fc05a067 --- /dev/null +++ b/www/test/1.5.1/test/ext/include-vals.js @@ -0,0 +1,23 @@ +describe("include-vals extension", function() { + beforeEach(function () { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function () { + this.server.restore(); + clearWorkArea(); + }); + + it('Includes values properly', function () { + var params = {}; + this.server.respondWith("POST", "/test", function (xhr) { + params = getParameters(xhr); + xhr.respond(200, {}, "clicked"); + }); + var btn = make('') + btn.click(); + this.server.respond(); + params['foo'].should.equal("bar"); + }); + +}); \ No newline at end of file diff --git a/www/test/1.5.1/test/ext/json-enc.js b/www/test/1.5.1/test/ext/json-enc.js new file mode 100644 index 00000000..ed805a17 --- /dev/null +++ b/www/test/1.5.1/test/ext/json-enc.js @@ -0,0 +1,136 @@ +// +describe("json-enc extension", function() { + beforeEach(function () { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function () { + this.server.restore(); + clearWorkArea(); + }); + + it('handles basic post properly', function () { + var jsonResponseBody = JSON.stringify({}); + this.server.respondWith("POST", "/test", jsonResponseBody); + var div = make("
click me
"); + div.click(); + this.server.respond(); + this.server.lastRequest.response.should.equal("{}"); + }) + + it('handles basic put properly', function () { + var jsonResponseBody = JSON.stringify({}); + this.server.respondWith("PUT", "/test", jsonResponseBody); + var div = make('
click me
'); + div.click(); + this.server.respond(); + this.server.lastRequest.response.should.equal("{}"); + }) + + it('handles basic patch properly', function () { + var jsonResponseBody = JSON.stringify({}); + this.server.respondWith("PATCH", "/test", jsonResponseBody); + var div = make('
click me
'); + div.click(); + this.server.respond(); + this.server.lastRequest.response.should.equal("{}"); + }) + + it('handles basic delete properly', function () { + var jsonResponseBody = JSON.stringify({}); + this.server.respondWith("DELETE", "/test", jsonResponseBody); + var div = make('
click me
'); + div.click(); + this.server.respond(); + this.server.lastRequest.response.should.equal("{}"); + }) + + it('handles post with form parameters', function () { + + this.server.respondWith("POST", "/test", function (xhr) { + var values = JSON.parse(xhr.requestBody); + values.should.have.keys("username","password"); + values["username"].should.be.equal("joe"); + values["password"].should.be.equal("123456"); + var ans = { "passwordok": values["password"] == "123456"}; + xhr.respond(200, {}, JSON.stringify(ans)); + }); + + var html = make('
' + + ' ' + + ' ' + + ' '); + + byId("btnSubmit").click(); + this.server.respond(); + this.server.lastRequest.response.should.equal('{"passwordok":true}'); + }) + + it('handles put with form parameters', function () { + + this.server.respondWith("PUT", "/test", function (xhr) { + var values = JSON.parse(xhr.requestBody); + values.should.have.keys("username","password"); + values["username"].should.be.equal("joe"); + values["password"].should.be.equal("123456"); + var ans = { "passwordok": values["password"] == "123456"}; + xhr.respond(200, {}, JSON.stringify(ans)); + }); + + var html = make(' ' + + ' ' + + ' ' + + ' '); + + byId("btnSubmit").click(); + this.server.respond(); + this.server.lastRequest.response.should.equal('{"passwordok":true}'); + }) + + + it('handles patch with form parameters', function () { + + this.server.respondWith("PATCH", "/test", function (xhr) { + var values = JSON.parse(xhr.requestBody); + values.should.have.keys("username","password"); + values["username"].should.be.equal("joe"); + values["password"].should.be.equal("123456"); + var ans = { "passwordok": values["password"] == "123456"}; + xhr.respond(200, {}, JSON.stringify(ans)); + }); + + var html = make(' ' + + ' ' + + ' ' + + ' '); + + byId("btnSubmit").click(); + this.server.respond(); + this.server.lastRequest.response.should.equal('{"passwordok":true}'); + }) + + it('handles delete with form parameters', function () { + + this.server.respondWith("DELETE", "/test", function (xhr) { + var values = JSON.parse(xhr.requestBody); + values.should.have.keys("username","password"); + values["username"].should.be.equal("joe"); + values["password"].should.be.equal("123456"); + var ans = { "passwordok": values["password"] == "123456"}; + xhr.respond(200, {}, JSON.stringify(ans)); + }); + + var html = make(' ' + + ' ' + + ' ' + + ' '); + + byId("btnSubmit").click(); + this.server.respond(); + this.server.lastRequest.response.should.equal('{"passwordok":true}'); + }) + + + +}); + diff --git a/www/test/1.5.1/test/ext/method-override.js b/www/test/1.5.1/test/ext/method-override.js new file mode 100644 index 00000000..b0a21ea1 --- /dev/null +++ b/www/test/1.5.1/test/ext/method-override.js @@ -0,0 +1,53 @@ +describe("method-override extension", function(){ + beforeEach(function() { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function() { + this.server.restore(); + clearWorkArea(); + }); + + it('issues a DELETE request with proper headers', function() + { + this.server.respondWith("DELETE", "/test", function(xhr){ + xhr.requestHeaders['X-HTTP-Method-Override'].should.equal('DELETE'); + xhr.method.should.equal("POST") + xhr.respond(200, {}, "Deleted!"); + }); + + var btn = make('') + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("Deleted!"); + }); + + it('issues a PATCH request with proper headers', function() + { + this.server.respondWith("PATCH", "/test", function(xhr){ + xhr.requestHeaders['X-HTTP-Method-Override'].should.equal('PATCH'); + xhr.method.should.equal("POST") + xhr.respond(200, {}, "Patched!"); + }); + + var btn = make('') + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("Patched!"); + }); + + it('issues a PUT request with proper headers', function() + { + this.server.respondWith("PUT", "/test", function(xhr){ + xhr.requestHeaders['X-HTTP-Method-Override'].should.equal('PUT'); + xhr.method.should.equal("POST") + xhr.respond(200, {}, "Putted!"); + }); + + var btn = make('') + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("Putted!"); + }); + +}) diff --git a/www/test/1.5.1/test/ext/morphdom-swap.js b/www/test/1.5.1/test/ext/morphdom-swap.js new file mode 100644 index 00000000..439fa54c --- /dev/null +++ b/www/test/1.5.1/test/ext/morphdom-swap.js @@ -0,0 +1,40 @@ +describe("morphdom-swap extension", function() { + beforeEach(function () { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function () { + this.server.restore(); + clearWorkArea(); + }); + + it('works on basic request', function () { + this.server.respondWith("GET", "/test", "!"); + var btn = make('') + btn.click(); + should.equal(btn.getAttribute("hx-get"), "/test"); + this.server.respond(); + should.equal(btn.getAttribute("hx-get"), null); + btn.innerHTML.should.equal("Clicked!"); + }); + + it('works with htmx elements in new content', function () { + this.server.respondWith("GET", "/test", ''); + this.server.respondWith("GET", "/test-inner", 'Loaded!'); + var btn = make('
').querySelector('button'); + btn.click(); + this.server.respond(); // call /test via button trigger=click + this.server.respond(); // call /test-inner via span trigger=load + btn.innerHTML.should.equal("Clicked!Loaded!"); + }); + + it('works with hx-select', function () { + this.server.respondWith("GET", "/test", "!"); + var btn = make('') + btn.click(); + should.equal(btn.getAttribute("hx-get"), "/test"); + this.server.respond(); + should.equal(btn.getAttribute("hx-get"), null); + btn.innerHTML.should.equal("Clicked!"); + }); +}); diff --git a/www/test/1.5.1/test/ext/path-deps.js b/www/test/1.5.1/test/ext/path-deps.js new file mode 100644 index 00000000..9b1569ac --- /dev/null +++ b/www/test/1.5.1/test/ext/path-deps.js @@ -0,0 +1,173 @@ +describe("path-deps extension", function() { + beforeEach(function () { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function () { + this.server.restore(); + clearWorkArea(); + }); + + it('path-deps basic case works', function () { + this.server.respondWith("POST", "/test", "Clicked!"); + this.server.respondWith("GET", "/test2", "Deps fired!"); + var btn = make('') + var div = make('
FOO
') + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("Clicked!"); + div.innerHTML.should.equal("FOO"); + this.server.respond(); + div.innerHTML.should.equal("Deps fired!"); + }); + + it('path-deps works with trailing slash', function () { + this.server.respondWith("POST", "/test", "Clicked!"); + this.server.respondWith("GET", "/test2", "Deps fired!"); + var btn = make('') + var div = make('
FOO
') + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("Clicked!"); + div.innerHTML.should.equal("FOO"); + this.server.respond(); + div.innerHTML.should.equal("Deps fired!"); + }); + + it('path-deps GET does not trigger', function () { + this.server.respondWith("GET", "/test", "Clicked!"); + this.server.respondWith("GET", "/test2", "Deps fired!"); + var btn = make('') + var div = make('
FOO
') + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("Clicked!"); + div.innerHTML.should.equal("FOO"); + this.server.respond(); + div.innerHTML.should.equal("FOO"); + }); + + it('path-deps dont trigger on path mismatch', function () { + this.server.respondWith("POST", "/test", "Clicked!"); + this.server.respondWith("GET", "/test2", "Deps fired!"); + var btn = make('') + var div = make('
FOO
') + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("Clicked!"); + div.innerHTML.should.equal("FOO"); + this.server.respond(); + div.innerHTML.should.equal("FOO"); + }); + + it('path-deps dont trigger on path longer than request', function () { + this.server.respondWith("POST", "/test", "Clicked!"); + this.server.respondWith("GET", "/test2", "Deps fired!"); + var btn = make('') + var div = make('
FOO
') + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("Clicked!"); + div.innerHTML.should.equal("FOO"); + this.server.respond(); + div.innerHTML.should.equal("FOO"); + }); + + it('path-deps trigger on path shorter than request', function () { + this.server.respondWith("POST", "/test/child", "Clicked!"); + this.server.respondWith("GET", "/test2", "Deps fired!"); + var btn = make('') + var div = make('
FOO
') + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("Clicked!"); + div.innerHTML.should.equal("FOO"); + this.server.respond(); + div.innerHTML.should.equal("Deps fired!"); + }); + + it('path-deps trigger on *-at-start path', function () { + this.server.respondWith("POST", "/test/child/test", "Clicked!"); + this.server.respondWith("GET", "/test2", "Deps fired!"); + var btn = make('') + var div = make('
FOO
') + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("Clicked!"); + div.innerHTML.should.equal("FOO"); + this.server.respond(); + div.innerHTML.should.equal("Deps fired!"); + }); + + it('path-deps trigger on *-in-middle path', function () { + this.server.respondWith("POST", "/test/child/test", "Clicked!"); + this.server.respondWith("GET", "/test2", "Deps fired!"); + var btn = make('') + var div = make('
FOO
') + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("Clicked!"); + div.innerHTML.should.equal("FOO"); + this.server.respond(); + div.innerHTML.should.equal("Deps fired!"); + }); + + it('path-deps trigger on *-at-end path', function () { + this.server.respondWith("POST", "/test/child/test", "Clicked!"); + this.server.respondWith("GET", "/test2", "Deps fired!"); + var btn = make('') + var div = make('
FOO
') + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("Clicked!"); + div.innerHTML.should.equal("FOO"); + this.server.respond(); + div.innerHTML.should.equal("Deps fired!"); + }); + + it('path-deps trigger all *s path', function () { + this.server.respondWith("POST", "/test/child/test", "Clicked!"); + this.server.respondWith("GET", "/test2", "Deps fired!"); + var btn = make('') + var div = make('
FOO
') + btn.click(); + this.server.respond(); + btn.innerHTML.should.equal("Clicked!"); + div.innerHTML.should.equal("FOO"); + this.server.respond(); + div.innerHTML.should.equal("Deps fired!"); + }); + + it('path-deps api basic refresh case works', function () { + this.server.respondWith("GET", "/test", "Path deps fired!"); + var div = make('
FOO
') + PathDeps.refresh("/test"); + this.server.respond(); + div.innerHTML.should.equal("Path deps fired!"); + }); + + it('path-deps api parent path case works', function () { + this.server.respondWith("GET", "/test1", "Path deps 1 fired!"); + this.server.respondWith("GET", "/test2", "Path deps 2 fired!"); + var div = make('
FOO
') + var div2 = make('
BAR
') + PathDeps.refresh("/test/child"); + this.server.respond(); + div.innerHTML.should.equal("Path deps 1 fired!"); + this.server.respond(); + div2.innerHTML.should.equal("Path deps 2 fired!"); + }); + + it('path-deps replacing containing element fires event', function () { + this.server.respondWith("POST", "/test", "Clicked!"); + this.server.respondWith("GET", "/test2", "Deps fired!"); + var div1 = make('
') + var div2 = make('
FOO
') + byId("buttonSubmit").click(); + this.server.respond(); + div1.innerHTML.should.equal('Clicked!'); + div2.innerHTML.should.equal("FOO"); + this.server.respond(); + div2.innerHTML.should.equal("Deps fired!"); + }); +}); diff --git a/www/test/1.5.1/test/ext/remove-me.js b/www/test/1.5.1/test/ext/remove-me.js new file mode 100644 index 00000000..1d710944 --- /dev/null +++ b/www/test/1.5.1/test/ext/remove-me.js @@ -0,0 +1,43 @@ +describe("remove-me extension", function(){ + beforeEach(function() { + this.server = makeServer(); + clearWorkArea(); + }); + afterEach(function() { + this.server.restore(); + clearWorkArea(); + }); + + it('removes elements properly', function(done) + { + var div = make('
Click Me!
') + byId("d1").should.equal(div) + setTimeout(function(){ + should.equal(byId("d1"), null); + done(); + }, 40); + }); + + + it('removes properly w/ data-* prefix', function(done) + { + var div = make('
Click Me!
') + should.equal(div.classList.length, 0); + setTimeout(function(){ + should.equal(div.parentElement, null); + done(); + }, 100); + }); + + it('extension can be on parent', function(done) + { + var div = make('
Click Me!
') + should.equal(div.classList.length, 0); + setTimeout(function(){ + should.equal(byId("d1"), null); + done(); + }, 100); + }); + + +}) diff --git a/www/test/1.5.1/test/img/bars.svg b/www/test/1.5.1/test/img/bars.svg new file mode 100644 index 00000000..7cb07e65 --- /dev/null +++ b/www/test/1.5.1/test/img/bars.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/www/test/1.5.1/test/index.html b/www/test/1.5.1/test/index.html new file mode 100644 index 00000000..457b90c4 --- /dev/null +++ b/www/test/1.5.1/test/index.html @@ -0,0 +1,139 @@ + + + + Mocha Tests + + + + + + + + + + + +

htmx.js test suite

+ +

Scratch Page

+ + +

Manual Tests

+Here + +

Mocha Test Suite

+[ALL] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +Work Area +
+
+
+ + diff --git a/www/test/1.5.1/test/lib/_hyperscript.js b/www/test/1.5.1/test/lib/_hyperscript.js new file mode 100644 index 00000000..114c9574 --- /dev/null +++ b/www/test/1.5.1/test/lib/_hyperscript.js @@ -0,0 +1,6067 @@ +///========================================================================= +/// This module provides the core runtime and grammar for hyperscript +///========================================================================= +//AMD insanity + +/** @var {HyperscriptObject} _hyperscript */ + +(function (root, factory) { + if (typeof define === "function" && define.amd) { + // AMD. Register as an anonymous module. + define([], factory); + } else { + // Browser globals + root._hyperscript = factory(); + } +})(typeof self !== "undefined" ? self : this, function () { + "use strict"; + + //==================================================================== + // Utilities + //==================================================================== + + /** + * mergeObjects combines the keys from obj2 into obj2, then returns obj1 + * + * @param {object} obj1 + * @param {object} obj2 + * @returns object + */ + function mergeObjects(obj1, obj2) { + for (var key in obj2) { + if (obj2.hasOwnProperty(key)) { + obj1[key] = obj2[key]; + } + } + return obj1; + } + + function getOrInitObject(root, prop) { + var value = root[prop]; + if (value) { + return value; + } else { + var newObj = {}; + root[prop] = newObj; + return newObj; + } + } + + /** + * parseJSON parses a JSON string into a corresponding value. If the + * value passed in is not valid JSON, then it logs an error and returns `null`. + * + * @param {string} jString + * @returns any + */ + function parseJSON(jString) { + try { + return JSON.parse(jString); + } catch (error) { + logError(error); + return null; + } + } + + /** + * logError writes an error message to the Javascript console. It can take any + * value, but msg should commonly be a simple string. + * @param {*} msg + */ + function logError(msg) { + if (console.error) { + console.error(msg); + } else if (console.log) { + console.log("ERROR: ", msg); + } + } + + // TODO: JSDoc description of what's happening here + function varargConstructor(Cls, args) { + return new (Cls.bind.apply(Cls, [Cls].concat(args)))(); + } + + var globalScope = typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this; + + //==================================================================== + // Lexer + //==================================================================== + + /** @type LexerObject */ + var _lexer = (function () { + var OP_TABLE = { + "+": "PLUS", + "-": "MINUS", + "*": "MULTIPLY", + "/": "DIVIDE", + ".": "PERIOD", + "..": "ELLIPSIS", + "\\": "BACKSLASH", + ":": "COLON", + "%": "PERCENT", + "|": "PIPE", + "!": "EXCLAMATION", + "?": "QUESTION", + "#": "POUND", + "&": "AMPERSAND", + $: "DOLLAR", + ";": "SEMI", + ",": "COMMA", + "(": "L_PAREN", + ")": "R_PAREN", + "<": "L_ANG", + ">": "R_ANG", + "<=": "LTE_ANG", + ">=": "GTE_ANG", + "==": "EQ", + "===": "EQQ", + "!=": "NEQ", + "!==": "NEQQ", + "{": "L_BRACE", + "}": "R_BRACE", + "[": "L_BRACKET", + "]": "R_BRACKET", + "=": "EQUALS", + }; + + /** + * isValidCSSClassChar returns `true` if the provided character is valid in a CSS class. + * @param {string} c + * @returns boolean + */ + function isValidCSSClassChar(c) { + return isAlpha(c) || isNumeric(c) || c === "-" || c === "_" || c === ":"; + } + + /** + * isValidCSSIDChar returns `true` if the provided character is valid in a CSS ID + * @param {string} c + * @returns boolean + */ + function isValidCSSIDChar(c) { + return isAlpha(c) || isNumeric(c) || c === "-" || c === "_" || c === ":"; + } + + /** + * isWhitespace returns `true` if the provided character is whitespace. + * @param {string} c + * @returns boolean + */ + function isWhitespace(c) { + return c === " " || c === "\t" || isNewline(c); + } + + /** + * positionString returns a string representation of a Token's line and column details. + * @param {Token} token + * @returns string + */ + function positionString(token) { + return "[Line: " + token.line + ", Column: " + token.col + "]"; + } + + /** + * isNewline returns `true` if the provided character is a carrage return or newline + * @param {string} c + * @returns boolean + */ + function isNewline(c) { + return c === "\r" || c === "\n"; + } + + /** + * isNumeric returns `true` if the provided character is a number (0-9) + * @param {string} c + * @returns boolean + */ + function isNumeric(c) { + return c >= "0" && c <= "9"; + } + + /** + * isAlpha returns `true` if the provided character is a letter in the alphabet + * @param {string} c + * @returns boolean + */ + function isAlpha(c) { + return (c >= "a" && c <= "z") || (c >= "A" && c <= "Z"); + } + + /** + * @param {string} c + * @param {boolean} [dollarIsOp] + * @returns boolean + */ + function isIdentifierChar(c) { + return c === "_" || c === "$"; + } + + /** + * @param {string} c + * @returns boolean + */ + function isReservedChar(c) { + return c === "`" || c === "^"; + } + + /** + * @param {Token[]} tokens + * @param {Token[]} consumed + * @param {string} source + * @returns {TokensObject} + */ + function makeTokensObject(tokens, consumed, source) { + consumeWhitespace(); // consume initial whitespace + + /** @type Token | null */ + var _lastConsumed = null; + + function consumeWhitespace() { + while (token(0, true).type === "WHITESPACE") { + consumed.push(tokens.shift()); + } + } + + /** + * @param {Token[]} tokens + * @param {*} error + */ + function raiseError(tokens, error) { + _parser.raiseParseError(tokens, error); + } + + /** + * @param {string} value + * @returns {Token} + */ + function requireOpToken(value) { + var token = matchOpToken(value); + if (token) { + return token; + } else { + raiseError(this, "Expected '" + value + "' but found '" + currentToken().value + "'"); + } + } + + /** + * @param {string} op1 + * @param {string} [op2] + * @param {string} [op3] + * @returns {Token | void} + */ + function matchAnyOpToken(op1, op2, op3) { + for (var i = 0; i < arguments.length; i++) { + var opToken = arguments[i]; + var match = matchOpToken(opToken); + if (match) { + return match; + } + } + } + + /** + * @param {string} op1 + * @param {string} [op2] + * @param {string} [op3] + * @returns {Token | void} + */ + function matchAnyToken(op1, op2, op3) { + for (var i = 0; i < arguments.length; i++) { + var opToken = arguments[i]; + var match = matchToken(opToken); + if (match) { + return match; + } + } + } + + /** + * @param {string} value + * @returns {Token | void} + */ + function matchOpToken(value) { + if (currentToken() && currentToken().op && currentToken().value === value) { + return consumeToken(); + } + } + + /** + * @param {string} type1 + * @param {string} [type2] + * @param {string} [type3] + * @param {string} [type4] + * @returns {Token | void} + */ + function requireTokenType(type1, type2, type3, type4) { + var token = matchTokenType(type1, type2, type3, type4); + if (token) { + return token; + } else { + raiseError(this, "Expected one of " + JSON.stringify([type1, type2, type3])); + } + } + + /** + * @param {string} type1 + * @param {string} [type2] + * @param {string} [type3] + * @param {string} [type4] + * @returns {Token | void} + */ + function matchTokenType(type1, type2, type3, type4) { + if ( + currentToken() && + currentToken().type && + [type1, type2, type3, type4].indexOf(currentToken().type) >= 0 + ) { + return consumeToken(); + } + } + + /** + * @param {string} value + * @param {string} [type] + * @returns {Token} + */ + function requireToken(value, type) { + var token = matchToken(value, type); + if (token) { + return token; + } else { + raiseError(this, "Expected '" + value + "' but found '" + currentToken().value + "'"); + } + } + + /** + * @param {string} value + * @param {string} [type] + * @returns {Token | void} + */ + function matchToken(value, type) { + if (follows.indexOf(value) !== -1) { + return; // disallowed token here + } + var type = type || "IDENTIFIER"; + if (currentToken() && currentToken().value === value && currentToken().type === type) { + return consumeToken(); + } + } + + /** + * @returns {Token} + */ + function consumeToken() { + var match = tokens.shift(); + consumed.push(match); + _lastConsumed = match; + consumeWhitespace(); // consume any whitespace + return match; + } + + /** + * @param {string} value + * @param {string} [type] + * @returns {Token[]} + */ + function consumeUntil(value, type) { + /** @type Token[] */ + var tokenList = []; + var currentToken = token(0, true); + + while ( + (type == null || currentToken.type !== type) && + (value == null || currentToken.value !== value) && + currentToken.type !== "EOF" + ) { + var match = tokens.shift(); + consumed.push(match); + tokenList.push(currentToken); + currentToken = token(0, true); + } + consumeWhitespace(); // consume any whitespace + return tokenList; + } + + /** + * @returns {string} + */ + function lastWhitespace() { + if (consumed[consumed.length - 1] && consumed[consumed.length - 1].type === "WHITESPACE") { + return consumed[consumed.length - 1].value; + } else { + return ""; + } + } + + function consumeUntilWhitespace() { + return consumeUntil(null, "WHITESPACE"); + } + + /** + * @returns {boolean} + */ + function hasMore() { + return tokens.length > 0; + } + + /** + * @param {number} n + * @param {boolean} [dontIgnoreWhitespace] + * @returns {Token} + */ + function token(n, dontIgnoreWhitespace) { + var /**@type {Token}*/ token; + var i = 0; + do { + if (!dontIgnoreWhitespace) { + while (tokens[i] && tokens[i].type === "WHITESPACE") { + i++; + } + } + token = tokens[i]; + n--; + i++; + } while (n > -1); + if (token) { + return token; + } else { + return { + type: "EOF", + value: "<<>>", + }; + } + } + + /** + * @returns {Token} + */ + function currentToken() { + return token(0); + } + + /** + * @returns {Token | null} + */ + function lastMatch() { + return _lastConsumed; + } + + /** + * @returns {string} + */ + function sourceFor() { + return source.substring(this.startToken.start, this.endToken.end); + } + + /** + * @returns {string} + */ + function lineFor() { + return source.split("\n")[this.startToken.line - 1]; + } + + var follows = []; + + function pushFollow(str) { + follows.push(str); + } + + function popFollow() { + follows.pop(); + } + + function clearFollows() { + var tmp = follows; + follows = []; + return tmp; + } + + function restoreFollows(f) { + follows = f; + } + + /** @type {TokensObject} */ + return { + pushFollow: pushFollow, + popFollow: popFollow, + clearFollow: clearFollows, + restoreFollow: restoreFollows, + matchAnyToken: matchAnyToken, + matchAnyOpToken: matchAnyOpToken, + matchOpToken: matchOpToken, + requireOpToken: requireOpToken, + matchTokenType: matchTokenType, + requireTokenType: requireTokenType, + consumeToken: consumeToken, + matchToken: matchToken, + requireToken: requireToken, + list: tokens, + consumed: consumed, + source: source, + hasMore: hasMore, + currentToken: currentToken, + lastMatch: lastMatch, + token: token, + consumeUntil: consumeUntil, + consumeUntilWhitespace: consumeUntilWhitespace, + lastWhitespace: lastWhitespace, + sourceFor: sourceFor, + lineFor: lineFor, + }; + } + + /** + * @param {Token[]} tokens + * @returns {boolean} + */ + function isValidSingleQuoteStringStart(tokens) { + if (tokens.length > 0) { + var previousToken = tokens[tokens.length - 1]; + if ( + previousToken.type === "IDENTIFIER" || + previousToken.type === "CLASS_REF" || + previousToken.type === "ID_REF" + ) { + return false; + } + if (previousToken.op && (previousToken.value === ">" || previousToken.value === ")")) { + return false; + } + } + return true; + } + + /** + * @param {string} string + * @param {boolean} [template] + * @returns {TokensObject} + */ + function tokenize(string, template) { + var tokens = /** @type {Token[]}*/ []; + var source = string; + var position = 0; + var column = 0; + var line = 1; + var lastToken = ""; + var templateBraceCount = 0; + + function inTemplate() { + return template && templateBraceCount === 0; + } + + while (position < source.length) { + if (currentChar() === "-" && nextChar() === "-") { + consumeComment(); + } else { + if (isWhitespace(currentChar())) { + tokens.push(consumeWhitespace()); + } else if ( + !possiblePrecedingSymbol() && + currentChar() === "." && + (isAlpha(nextChar()) || nextChar() === "{") + ) { + tokens.push(consumeClassReference()); + } else if ( + !possiblePrecedingSymbol() && + currentChar() === "#" && + (isAlpha(nextChar()) || nextChar() === "{") + ) { + tokens.push(consumeIdReference()); + } else if (currentChar() === "[" && nextChar() === "@") { + tokens.push(consumeAttributeReference()); + } else if (currentChar() === "@") { + tokens.push(consumeShortAttributeReference()); + } else if (isAlpha(currentChar()) || (!inTemplate() && isIdentifierChar(currentChar()))) { + tokens.push(consumeIdentifier()); + } else if (isNumeric(currentChar())) { + tokens.push(consumeNumber()); + } else if (!inTemplate() && (currentChar() === '"' || currentChar() === "`")) { + tokens.push(consumeString()); + } else if (!inTemplate() && currentChar() === "'") { + if (isValidSingleQuoteStringStart(tokens)) { + tokens.push(consumeString()); + } else { + tokens.push(consumeOp()); + } + } else if (OP_TABLE[currentChar()]) { + if (lastToken === "$" && currentChar() === "{") { + templateBraceCount++; + } + if (currentChar() === "}") { + templateBraceCount--; + } + tokens.push(consumeOp()); + } else if (inTemplate() || isReservedChar(currentChar())) { + tokens.push(makeToken("RESERVED", consumeChar())); + } else { + if (position < source.length) { + throw Error("Unknown token: " + currentChar() + " "); + } + } + } + } + + return makeTokensObject(tokens, [], source); + + /** + * @param {string} [type] + * @param {string} [value] + * @returns {Token} + */ + function makeOpToken(type, value) { + var token = makeToken(type, value); + token.op = true; + return token; + } + + /** + * @param {string} [type] + * @param {string} [value] + * @returns {Token} + */ + function makeToken(type, value) { + return { + type: type, + value: value, + start: position, + end: position + 1, + column: column, + line: line, + }; + } + + function consumeComment() { + while (currentChar() && !isNewline(currentChar())) { + consumeChar(); + } + consumeChar(); + } + + /** + * @returns Token + */ + function consumeClassReference() { + var classRef = makeToken("CLASS_REF"); + var value = consumeChar(); + if (currentChar() === "{") { + classRef.template = true; + value += consumeChar(); + while (currentChar() && currentChar() !== "}") { + value += consumeChar(); + } + if (currentChar() !== "}") { + throw Error("Unterminated class reference"); + } else { + value += consumeChar(); // consume final curly + } + } else { + while (isValidCSSClassChar(currentChar())) { + value += consumeChar(); + } + } + classRef.value = value; + classRef.end = position; + return classRef; + } + + /** + * @returns Token + */ + function consumeAttributeReference() { + var attributeRef = makeToken("ATTRIBUTE_REF"); + var value = consumeChar(); + while (position < source.length && currentChar() !== "]") { + value += consumeChar(); + } + if (currentChar() === "]") { + value += consumeChar(); + } + attributeRef.value = value; + attributeRef.end = position; + return attributeRef; + } + + function consumeShortAttributeReference() { + var attributeRef = makeToken("ATTRIBUTE_REF"); + var value = consumeChar(); + while (isValidCSSIDChar(currentChar())) { + value += consumeChar(); + } + attributeRef.value = value; + attributeRef.end = position; + return attributeRef; + } + + /** + * @returns Token + */ + function consumeIdReference() { + var idRef = makeToken("ID_REF"); + var value = consumeChar(); + if (currentChar() === "{") { + idRef.template = true; + value += consumeChar(); + while (currentChar() && currentChar() !== "}") { + value += consumeChar(); + } + if (currentChar() !== "}") { + throw Error("Unterminated id reference"); + } else { + consumeChar(); // consume final quote + } + } else { + while (isValidCSSIDChar(currentChar())) { + value += consumeChar(); + } + } + idRef.value = value; + idRef.end = position; + return idRef; + } + + /** + * @returns Token + */ + function consumeIdentifier() { + var identifier = makeToken("IDENTIFIER"); + var value = consumeChar(); + while (isAlpha(currentChar()) || isIdentifierChar(currentChar())) { + value += consumeChar(); + } + identifier.value = value; + identifier.end = position; + return identifier; + } + + /** + * @returns Token + */ + function consumeNumber() { + var number = makeToken("NUMBER"); + var value = consumeChar(); + while (isNumeric(currentChar())) { + value += consumeChar(); + } + if (currentChar() === "." && isNumeric(nextChar())) { + value += consumeChar(); + } + while (isNumeric(currentChar())) { + value += consumeChar(); + } + number.value = value; + number.end = position; + return number; + } + + /** + * @returns Token + */ + function consumeOp() { + var op = makeOpToken(); + var value = consumeChar(); // consume leading char + while (currentChar() && OP_TABLE[value + currentChar()]) { + value += consumeChar(); + } + op.type = OP_TABLE[value]; + op.value = value; + op.end = position; + return op; + } + + /** + * @returns Token + */ + function consumeString() { + var string = makeToken("STRING"); + var startChar = consumeChar(); // consume leading quote + var value = ""; + while (currentChar() && currentChar() !== startChar) { + if (currentChar() === "\\") { + consumeChar(); // consume escape char and move on + } + value += consumeChar(); + } + if (currentChar() !== startChar) { + throw Error("Unterminated string at " + positionString(string)); + } else { + consumeChar(); // consume final quote + } + string.value = value; + string.end = position; + string.template = startChar === "`"; + return string; + } + + /** + * @returns string + */ + function currentChar() { + return source.charAt(position); + } + + /** + * @returns string + */ + function nextChar() { + return source.charAt(position + 1); + } + + /** + * @returns string + */ + function consumeChar() { + lastToken = currentChar(); + position++; + column++; + return lastToken; + } + + /** + * @returns boolean + */ + function possiblePrecedingSymbol() { + return ( + isAlpha(lastToken) || + isNumeric(lastToken) || + lastToken === ")" || + lastToken === "}" || + lastToken === "]" + ); + } + + /** + * @returns Token + */ + function consumeWhitespace() { + var whitespace = makeToken("WHITESPACE"); + var value = ""; + while (currentChar() && isWhitespace(currentChar())) { + if (isNewline(currentChar())) { + column = 0; + line++; + } + value += consumeChar(); + } + whitespace.value = value; + whitespace.end = position; + return whitespace; + } + } + + return { + tokenize: tokenize, + makeTokensObject: makeTokensObject, + }; + })(); + + //==================================================================== + // Parser + //==================================================================== + + /** @type ParserObject */ + var _parser = (function () { + /** @type {Object} */ + var GRAMMAR = {}; + + /** @type {Object} */ + var COMMANDS = {}; + + /** @type {Object} */ + var FEATURES = {}; + + var LEAF_EXPRESSIONS = []; + var INDIRECT_EXPRESSIONS = []; + + /** + * @param {*} parseElement + * @param {*} start + * @param {TokensObject} tokens + */ + function initElt(parseElement, start, tokens) { + parseElement.startToken = start; + parseElement.sourceFor = tokens.sourceFor; + parseElement.lineFor = tokens.lineFor; + parseElement.programSource = tokens.source; + } + + /** + * @param {string} type + * @param {TokensObject} tokens + * @param {*} root + * @returns GrammarElement + */ + function parseElement(type, tokens, root) { + var elementDefinition = GRAMMAR[type]; + if (elementDefinition) { + var start = tokens.currentToken(); + var parseElement = elementDefinition(_parser, _runtime, tokens, root); + if (parseElement) { + initElt(parseElement, start, tokens); + parseElement.endToken = parseElement.endToken || tokens.lastMatch(); + var root = parseElement.root; + while (root != null) { + initElt(root, start, tokens); + root = root.root; + } + } + return parseElement; + } + } + + /** + * @param {string} type + * @param {TokensObject} tokens + * @param {string} [message] + * @param {*} [root] + * @returns {GrammarElement} + */ + function requireElement(type, tokens, message, root) { + var result = parseElement(type, tokens, root); + return result || raiseParseError(tokens, message || "Expected " + type); + } + + /** + * @param {string[]} types + * @param {TokensObject} tokens + * @returns {GrammarElement} + */ + function parseAnyOf(types, tokens) { + for (var i = 0; i < types.length; i++) { + var type = types[i]; + var expression = parseElement(type, tokens); + if (expression) { + return expression; + } + } + } + + /** + * @param {string} name + * @param {GrammarDefinition} definition + */ + function addGrammarElement(name, definition) { + GRAMMAR[name] = definition; + } + + /** + * @param {string} keyword + * @param {GrammarDefinition} definition + */ + function addCommand(keyword, definition) { + var commandGrammarType = keyword + "Command"; + var commandDefinitionWrapper = function (parser, runtime, tokens) { + var commandElement = definition(parser, runtime, tokens); + if (commandElement) { + commandElement.type = commandGrammarType; + commandElement.execute = function (context) { + context.meta.command = commandElement; + return runtime.unifiedExec(this, context); + }; + return commandElement; + } + }; + GRAMMAR[commandGrammarType] = commandDefinitionWrapper; + COMMANDS[keyword] = commandDefinitionWrapper; + } + + /** + * @param {string} keyword + * @param {GrammarDefinition} definition + */ + function addFeature(keyword, definition) { + var featureGrammarType = keyword + "Feature"; + + /** @type {GrammarDefinition} */ + var featureDefinitionWrapper = function (parser, runtime, tokens) { + var featureElement = definition(parser, runtime, tokens); + if (featureElement) { + featureElement.keyword = keyword; + featureElement.type = featureGrammarType; + return featureElement; + } + }; + GRAMMAR[featureGrammarType] = featureDefinitionWrapper; + FEATURES[keyword] = featureDefinitionWrapper; + } + + /** + * @param {string} name + * @param {GrammarDefinition} definition + */ + function addLeafExpression(name, definition) { + LEAF_EXPRESSIONS.push(name); + addGrammarElement(name, definition); + } + + /** + * @param {string} name + * @param {GrammarDefinition} definition + */ + function addIndirectExpression(name, definition) { + INDIRECT_EXPRESSIONS.push(name); + addGrammarElement(name, definition); + } + + /* ============================================================================================ */ + /* Core hyperscript Grammar Elements */ + /* ============================================================================================ */ + addGrammarElement("feature", function (parser, runtime, tokens) { + if (tokens.matchOpToken("(")) { + var featureElement = parser.requireElement("feature", tokens); + tokens.requireOpToken(")"); + return featureElement; + } + + var featureDefinition = FEATURES[tokens.currentToken().value]; + if (featureDefinition) { + return featureDefinition(parser, runtime, tokens); + } + }); + + addGrammarElement("command", function (parser, runtime, tokens) { + if (tokens.matchOpToken("(")) { + var commandElement = parser.requireElement("command", tokens); + tokens.requireOpToken(")"); + return commandElement; + } + + var commandDefinition = COMMANDS[tokens.currentToken().value]; + if (commandDefinition) { + var commandElement = commandDefinition(parser, runtime, tokens); + } else if (tokens.currentToken().type === "IDENTIFIER" && tokens.token(1).value === "(") { + var commandElement = parser.requireElement("pseudoCommand", tokens); + } + if (commandElement) { + return parser.parseElement("indirectStatement", tokens, commandElement); + } + + return commandElement; + }); + + addGrammarElement("commandList", function (parser, runtime, tokens) { + var cmd = parser.parseElement("command", tokens); + if (cmd) { + tokens.matchToken("then"); + cmd.next = parser.parseElement("commandList", tokens); + return cmd; + } + }); + + addGrammarElement("leaf", function (parser, runtime, tokens) { + var result = parseAnyOf(LEAF_EXPRESSIONS, tokens); + // symbol is last so it doesn't consume any constants + if (result == null) { + return parseElement("symbol", tokens); + } + + return result; + }); + + addGrammarElement("indirectExpression", function (parser, runtime, tokens, root) { + for (var i = 0; i < INDIRECT_EXPRESSIONS.length; i++) { + var indirect = INDIRECT_EXPRESSIONS[i]; + root.endToken = tokens.lastMatch(); + var result = parser.parseElement(indirect, tokens, root); + if (result) { + return result; + } + } + return root; + }); + + addGrammarElement("indirectStatement", function (parser, runtime, tokens, root) { + if (tokens.matchToken("unless")) { + root.endToken = tokens.lastMatch(); + var conditional = parser.requireElement("expression", tokens); + var unless = { + type: "unlessStatementModifier", + args: [conditional], + op: function (context, conditional) { + if (conditional) { + return this.next; + } else { + return root; + } + }, + execute: function (context) { + return runtime.unifiedExec(this, context); + }, + }; + root.parent = unless; + return unless; + } + return root; + }); + + addGrammarElement("primaryExpression", function (parser, runtime, tokens) { + var leaf = parser.parseElement("leaf", tokens); + if (leaf) { + return parser.parseElement("indirectExpression", tokens, leaf); + } + parser.raiseParseError(tokens, "Unexpected value: " + tokens.currentToken().value); + }); + + /* ============================================================================================ */ + /* END Core hyperscript Grammar Elements */ + + /* ============================================================================================ */ + + /** + * + * @param {TokensObject} tokens + * @returns string + */ + function createParserContext(tokens) { + var currentToken = tokens.currentToken(); + var source = tokens.source; + var lines = source.split("\n"); + var line = currentToken && currentToken.line ? currentToken.line - 1 : lines.length - 1; + var contextLine = lines[line]; + var offset = currentToken && currentToken.line ? currentToken.column : contextLine.length - 1; + return contextLine + "\n" + " ".repeat(offset) + "^^\n\n"; + } + + /** + * @param {TokensObject} tokens + * @param {string} message + */ + function raiseParseError(tokens, message) { + message = + (message || "Unexpected Token : " + tokens.currentToken().value) + "\n\n" + createParserContext(tokens); + var error = new Error(message); + error["tokens"] = tokens; + throw error; + } + + /** + * @param {TokensObject} tokens + * @returns {GrammarElement} + */ + function parseHyperScript(tokens) { + var result = parseElement("hyperscript", tokens); + if (tokens.hasMore()) raiseParseError(tokens); + return result; + } + + /** + * @param {GrammarElement} elt + * @param {GrammarElement} parent + */ + function setParent(elt, parent) { + if (elt) { + elt.parent = parent; + setParent(elt.next, parent); + } + } + + /** + * @param {Token} token + * @returns {GrammarDefinition} + */ + function commandStart(token) { + return COMMANDS[token.value]; + } + + /** + * @param {Token} token + * @returns {GrammarDefinition} + */ + function featureStart(token) { + return FEATURES[token.value]; + } + + /** + * @param {Token} token + * @returns {true | void} + */ + function commandBoundary(token) { + if ( + token.value == "end" || + token.value == "then" || + token.value == "else" || + token.value == ")" || + commandStart(token) || + featureStart(token) || + token.type == "EOF" + ) { + return true; + } + } + + /** + * @param {TokensObject} tokens + * @returns {(string | Token)[]} + */ + function parseStringTemplate(tokens) { + /** @type (string | Token)[] */ + var returnArr = [""]; + do { + returnArr.push(tokens.lastWhitespace()); + if (tokens.currentToken().value === "$") { + tokens.consumeToken(); + var startingBrace = tokens.matchOpToken("{"); + returnArr.push(requireElement("expression", tokens)); + if (startingBrace) { + tokens.requireOpToken("}"); + } + returnArr.push(""); + } else if (tokens.currentToken().value === "\\") { + tokens.consumeToken(); // skip next + tokens.consumeToken(); + } else { + var token = tokens.consumeToken(); + returnArr[returnArr.length - 1] += token ? token.value : ""; + } + } while (tokens.hasMore()); + returnArr.push(tokens.lastWhitespace()); + return returnArr; + } + + // parser API + return { + setParent: setParent, + requireElement: requireElement, + parseElement: parseElement, + featureStart: featureStart, + commandStart: commandStart, + commandBoundary: commandBoundary, + parseAnyOf: parseAnyOf, + parseHyperScript: parseHyperScript, + raiseParseError: raiseParseError, + addGrammarElement: addGrammarElement, + addCommand: addCommand, + addFeature: addFeature, + addLeafExpression: addLeafExpression, + addIndirectExpression: addIndirectExpression, + parseStringTemplate: parseStringTemplate, + }; + })(); + + //==================================================================== + // Runtime + //==================================================================== + + /** @type ConversionMap */ + var CONVERSIONS = { + dynamicResolvers: /** @type DynamicConversionFunction[] */ [], + String: function (val) { + if (val.toString) { + return val.toString(); + } else { + return "" + val; + } + }, + Int: function (val) { + return parseInt(val); + }, + Float: function (val) { + return parseFloat(val); + }, + Number: function (val) { + console.log(val); + return Number(val); + }, + Date: function (val) { + return Date(val); + }, + Array: function (val) { + return Array.from(val); + }, + JSON: function (val) { + return JSON.stringify(val); + }, + Object: function (val) { + if (val instanceof String) { + val = val.toString(); + } + if (typeof val === "string") { + return JSON.parse(val); + } else { + return mergeObjects({}, val); + } + }, + }; + + /******************************************** + * RUNTIME OBJECT + ********************************************/ + + /** @type {RuntimeObject} */ + var _runtime = (function () { + /** + * @param {HTMLElement} elt + * @param {string} selector + * @returns boolean + */ + function matchesSelector(elt, selector) { + // noinspection JSUnresolvedVariable + var matchesFunction = + elt.matches || + elt.matchesSelector || + elt.msMatchesSelector || + elt.mozMatchesSelector || + elt.webkitMatchesSelector || + elt.oMatchesSelector; + return matchesFunction && matchesFunction.call(elt, selector); + } + + /** + * @param {string} eventName + * @param {Object} [detail] + * @returns {Event} + */ + function makeEvent(eventName, detail) { + var evt; + if (window.CustomEvent && typeof window.CustomEvent === "function") { + evt = new CustomEvent(eventName, { + bubbles: true, + cancelable: true, + detail: detail, + }); + } else { + evt = document.createEvent("CustomEvent"); + evt.initCustomEvent(eventName, true, true, detail); + } + return evt; + } + + /** + * @param {HTMLElement} elt + * @param {string} eventName + * @param {Object} [detail] + * @returns {boolean} + */ + function triggerEvent(elt, eventName, detail) { + detail = detail || {}; + detail["sentBy"] = elt; + var event = makeEvent(eventName, detail); + var eventResult = elt.dispatchEvent(event); + return eventResult; + } + + /** + * isArrayLike returns `true` if the provided value is an array or + * a NodeList (which is close enough to being an array for our purposes). + * + * @param {any} value + * @returns {value is Array | NodeList} + */ + function isArrayLike(value) { + return Array.isArray(value) || value instanceof NodeList; + } + + /** + * forEach executes the provided `func` on every item in the `value` array. + * if `value` is a single item (and not an array) then `func` is simply called + * once. If `value` is null, then no further actions are taken. + * + * @template T + * @param {NodeList | T | T[]} value + * @param {(item:Node | T) => void} func + */ + function forEach(value, func) { + if (value == null) { + // do nothing + } else if (isArrayLike(value)) { + for (var i = 0; i < value.length; i++) { + func(value[i]); + } + } else { + func(value); + } + } + + var ARRAY_SENTINEL = { array_sentinel: true }; + + function linearize(args) { + var arr = []; + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + if (Array.isArray(arg)) { + arr.push(ARRAY_SENTINEL); + for (var j = 0; j < arg.length; j++) { + arr.push(arg[j]); + } + arr.push(ARRAY_SENTINEL); + } else { + arr.push(arg); + } + } + return arr; + } + + function delinearize(values) { + var arr = []; + for (var i = 0; i < values.length; i++) { + var value = values[i]; + if (value === ARRAY_SENTINEL) { + value = values[++i]; + var valueArray = []; + arr.push(valueArray); + while (value !== ARRAY_SENTINEL) { + valueArray.push(value); + value = values[++i]; + } + } else { + arr.push(value); + } + } + return arr; + } + + function unwrapAsyncs(values) { + for (var i = 0; i < values.length; i++) { + var value = values[i]; + if (value.asyncWrapper) { + values[i] = value.value; + } + if (Array.isArray(value)) { + for (var j = 0; j < value.length; j++) { + var valueElement = value[j]; + if (valueElement.asyncWrapper) { + value[j] = valueElement.value; + } + } + } + } + } + + var HALT = { halt_flag: true }; + + /** + * @param {GrammarDefinition} command + * @param {Context} ctx + */ + function unifiedExec(command, ctx) { + while (true) { + try { + var next = unifiedEval(command, ctx); + } catch (e) { + _runtime.registerHyperTrace(ctx, e); + if (ctx.meta.errorHandler && !ctx.meta.handlingError) { + ctx.meta.handlingError = true; + ctx[ctx.meta.errorSymmbol] = e; + command = ctx.meta.errorHandler; + continue; + } else if (ctx.meta.reject) { + ctx.meta.reject(e); + next = HALT; + } else { + throw e; + } + } + if (next == null) { + console.error(command, " did not return a next element to execute! context: ", ctx); + return; + } else if (next.then) { + next.then(function (resolvedNext) { + unifiedExec(resolvedNext, ctx); + }).catch(function (reason) { + _runtime.registerHyperTrace(ctx, reason); + if (ctx.meta.errorHandler && !ctx.meta.handlingError) { + ctx.meta.handlingError = true; + ctx[ctx.meta.errorSymmbol] = reason; + unifiedExec(ctx.meta.errorHandler, ctx); + } else if (ctx.meta.reject) { + ctx.meta.reject(reason); + } else { + throw reason; + } + }); + return; + } else if (next === HALT) { + // done + return; + } else { + command = next; // move to the next command + } + } + } + + /** + * @param {*} parseElement + * @param {Context} ctx + * @returns {*} + */ + function unifiedEval(parseElement, ctx) { + /** @type any[] */ + var args = [ctx]; + var async = false; + var wrappedAsyncs = false; + + if (parseElement.args) { + for (var i = 0; i < parseElement.args.length; i++) { + var argument = parseElement.args[i]; + if (argument == null) { + args.push(null); + } else if (Array.isArray(argument)) { + var arr = []; + for (var j = 0; j < argument.length; j++) { + var element = argument[j]; + var value = element ? element.evaluate(ctx) : null; // OK + if (value) { + if (value.then) { + async = true; + } else if (value.asyncWrapper) { + wrappedAsyncs = true; + } + } + arr.push(value); + } + args.push(arr); + } else if (argument.evaluate) { + var value = argument.evaluate(ctx); // OK + if (value) { + if (value.then) { + async = true; + } else if (value.asyncWrapper) { + wrappedAsyncs = true; + } + } + args.push(value); + } else { + args.push(argument); + } + } + } + if (async) { + return new Promise(function (resolve, reject) { + var linearized = linearize(args); + Promise.all(linearized) + .then(function (values) { + values = delinearize(values); + if (wrappedAsyncs) { + unwrapAsyncs(values); + } + try { + var apply = parseElement.op.apply(parseElement, values); + resolve(apply); + } catch (e) { + reject(e); + } + }) + .catch(function (reason) { + if (ctx.meta.errorHandler && !ctx.meta.handlingError) { + ctx.meta.handlingError = true; + ctx[ctx.meta.errorSymmbol] = reason; + unifiedExec(ctx.meta.errorHandler, ctx); + } else if (ctx.meta.reject) { + ctx.meta.reject(reason); + } else { + // TODO: no meta context to reject with, trigger event? + } + }); + }); + } else { + if (wrappedAsyncs) { + unwrapAsyncs(args); + } + return parseElement.op.apply(parseElement, args); + } + } + + var _scriptAttrs = null; + + /** + * getAttributes returns the attribute name(s) to use when + * locating hyperscript scripts in a DOM element. If no value + * has been configured, it defaults to _hyperscript.config.attributes + * @returns string[] + */ + function getScriptAttributes() { + if (_scriptAttrs == null) { + _scriptAttrs = _hyperscript.config.attributes.replace(/ /g, "").split(","); + } + return _scriptAttrs; + } + + /** + * @param {HTMLElement} elt + * @returns {string | null} + */ + function getScript(elt) { + for (var i = 0; i < getScriptAttributes().length; i++) { + var scriptAttribute = getScriptAttributes()[i]; + if (elt.hasAttribute && elt.hasAttribute(scriptAttribute)) { + return elt.getAttribute(scriptAttribute); + } + } + if (elt["type"] === "text/hyperscript") { + return elt.innerText; + } + return null; + } + + /** + * @param {Object} owner + * @param {Context} ctx + */ + function addFeatures(owner, ctx) { + if (owner) { + if (owner.hyperscriptFeatures) { + mergeObjects(ctx, owner.hyperscriptFeatures); + } + addFeatures(owner.parentElement, ctx); + } + } + + /** + * @param {*} owner + * @param {*} feature + * @param {*} hyperscriptTarget + * @param {*} event + * @returns {Context} + */ + function makeContext(owner, feature, hyperscriptTarget, event) { + /** @type {Context} */ + var ctx = { + meta: { + parser: _parser, + lexer: _lexer, + runtime: _runtime, + owner: owner, + feature: feature, + iterators: {}, + }, + me: hyperscriptTarget, + event: event, + target: event ? event.target : null, + detail: event ? event.detail : null, + body: "document" in globalScope ? document.body : null, + }; + ctx.meta.ctx = ctx; + addFeatures(owner, ctx); + return ctx; + } + + /** + * @returns string + */ + function getScriptSelector() { + return getScriptAttributes() + .map(function (attribute) { + return "[" + attribute + "]"; + }) + .join(", "); + } + + /** + * @param {any} value + * @param {string} type + * @returns {any} + */ + function convertValue(value, type) { + var dynamicResolvers = CONVERSIONS.dynamicResolvers; + for (var i = 0; i < dynamicResolvers.length; i++) { + var dynamicResolver = dynamicResolvers[i]; + var converted = dynamicResolver(type, value); + if (converted !== undefined) { + return converted; + } + } + + if (value == null) { + return null; + } + var converter = CONVERSIONS[type]; + if (converter) { + return converter(value); + } + + throw "Unknown conversion : " + type; + } + + // TODO: There do not seem to be any references to this function. + // Is it still in use, or can it be removed? + function isType(o, type) { + return Object.prototype.toString.call(o) === "[object " + type + "]"; + } + + /** + * @param {string} src + * @returns {GrammarElement} + */ + function parse(src) { + var tokens = _lexer.tokenize(src); + if (_parser.commandStart(tokens.currentToken())) { + var commandList = _parser.parseElement("commandList", tokens); + var last = commandList; + while (last.next) { + last = last.next; + } + last.next = { + op: function () { + return HALT; + }, + }; + return commandList; + } else if (_parser.featureStart(tokens.currentToken())) { + var hyperscript = _parser.parseElement("hyperscript", tokens); + return hyperscript; + } else { + var expression = _parser.parseElement("expression", tokens); + return expression; + } + } + + /** + * @param {string} src + * @param {Context} ctx + * @returns {any} + */ + function evaluate(src, ctx) { + ctx = mergeObjects(makeContext(document.body, null, document.body, null), ctx || {}); + var element = parse(src); + if (element.execute) { + return element.execute(ctx); + } else if (element.apply) { + element.apply(document.body, null); + } else { + return element.evaluate(ctx); + } + } + + /** + * @param {HTMLElement} elt + */ + function processNode(elt) { + var selector = _runtime.getScriptSelector(); + if (matchesSelector(elt, selector)) { + initElement(elt, elt); + } + if (elt["type"] === "text/hyperscript") { + initElement(elt, document.body); + } + if (elt.querySelectorAll) { + forEach(elt.querySelectorAll(selector + ", [type='text/hyperscript']"), function (elt) { + initElement(elt, elt.type === "text/hyperscript" ? document.body : elt); + }); + } + } + + /** + * @param {HTMLElement} elt + * @param {HTMLElement} [target] + */ + function initElement(elt, target) { + if (elt.closest && elt.closest(_hyperscript.config.disableSelector)) { + return; + } + var internalData = getInternalData(elt); + if (!internalData.initialized) { + var src = getScript(elt); + if (src) { + try { + internalData.initialized = true; + internalData.script = src; + var tokens = _lexer.tokenize(src); + var hyperScript = _parser.parseHyperScript(tokens); + hyperScript.apply(target || elt, elt); + setTimeout(function () { + triggerEvent(target || elt, "load", { + hyperscript: true, + }); + }, 1); + } catch (e) { + _runtime.triggerEvent(elt, "exception", { + error: e, + }); + console.error( + "hyperscript errors were found on the following element:", + elt, + "\n\n", + e.message, + e.stack + ); + } + } + } + } + + /** + * @param {HTMLElement} elt + * @returns {Object} + */ + function getInternalData(elt) { + var dataProp = "hyperscript-internal-data"; + var data = elt[dataProp]; + if (!data) { + data = elt[dataProp] = {}; + } + return data; + } + + /** + * @param {any} value + * @param {string} typeString + * @param {boolean} [nullOk] + * @returns {boolean} + */ + function typeCheck(value, typeString, nullOk) { + if (value == null && nullOk) { + return true; + } + var typeName = Object.prototype.toString.call(value).slice(8, -1); + return typeName === typeString; + } + + function getElementScope(context) { + var elt = context.meta.owner; + if (elt) { + var internalData = getInternalData(elt); + var scopeName = "elementScope"; + if (context.meta.feature && context.meta.feature.behavior) { + scopeName = context.meta.feature.behavior + "Scope"; + } + var elementScope = getOrInitObject(internalData, scopeName); + return elementScope; + } else { + return {}; // no element, return empty scope + } + } + + /** + * @param {string} str + * @param {Context} context + * @returns {any} + */ + function resolveSymbol(str, context, type) { + if (str === "me" || str === "my" || str === "I") { + return context["me"]; + } + if (str === "it" || str === "its") { + return context["result"]; + } + if (str === "you" || str === "your" || str === "yourself") { + return context["beingTold"]; + } else { + if (type === "global") { + return globalScope[str]; + } else if (type === "element") { + var elementScope = getElementScope(context); + return elementScope[str]; + } else if (type === "local") { + return context[str]; + } else { + // meta scope (used for event conditionals) + if (context.meta && context.meta.context) { + var fromMetaContext = context.meta.context[str]; + if (typeof fromMetaContext !== "undefined") { + return fromMetaContext; + } + } + // local scope + var fromContext = context[str]; + if (typeof fromContext !== "undefined") { + return fromContext; + } else { + // element scope + var elementScope = getElementScope(context); + fromContext = elementScope[str]; + if (typeof fromContext !== "undefined") { + return fromContext; + } else { + // global scope + return globalScope[str]; + } + } + } + } + } + + function setSymbol(str, context, type, value) { + if (type === "global") { + globalScope[str] = value; + } else if (type === "element") { + var elementScope = getElementScope(context); + elementScope[str] = value; + } else if (type === "local") { + context[str] = value; + } else { + // local scope + var fromContext = context[str]; + if (typeof fromContext !== "undefined") { + context[str] = value; + } else { + // element scope + var elementScope = getElementScope(context); + fromContext = elementScope[str]; + if (typeof fromContext !== "undefined") { + elementScope[str] = value; + } else { + // global scope + fromContext = globalScope[str]; + if (typeof fromContext !== "undefined") { + globalScope[str] = value; + } else { + context[str] = value; + } + } + } + } + } + + /** + * @param {GrammarElement} command + * @param {Context} context + * @returns {undefined | GrammarElement} + */ + function findNext(command, context) { + if (command) { + if (command.resolveNext) { + return command.resolveNext(context); + } else if (command.next) { + return command.next; + } else { + return findNext(command.parent, context); + } + } + } + + /** + * @param {Object} root + * @param {string} property + * @param {boolean} attribute + * @returns {any} + */ + function resolveProperty(root, property, attribute) { + if (root != null) { + var val = attribute && root.getAttribute ? root.getAttribute(property) : root[property]; + if (typeof val !== "undefined") { + return val; + } + + if (isArrayLike(root)) { + // flat map + var result = []; + for (var i = 0; i < root.length; i++) { + var component = root[i]; + var componentValue = attribute ? component.getAttribute(property) : component[property]; + if (componentValue) { + result.push(componentValue); + } + } + return result; + } + } + } + + /** + * @param {Element} elt + * @param {string[]} nameSpace + * @param {string} name + * @param {any} value + */ + function assignToNamespace(elt, nameSpace, name, value) { + if (typeof document === "undefined" || elt === document.body) { + var root = globalScope; + } else { + var root = elt["hyperscriptFeatures"]; + if (root == null) { + root = {}; + elt["hyperscriptFeatures"] = root; + } + } + while (nameSpace.length > 0) { + var propertyName = nameSpace.shift(); + var newRoot = root[propertyName]; + if (newRoot == null) { + newRoot = {}; + root[propertyName] = newRoot; + } + root = newRoot; + } + + root[name] = value; + } + + function getHyperTrace(ctx, thrown) { + var trace = []; + var root = ctx; + while (root.meta.caller) { + root = root.meta.caller; + } + if (root.meta.traceMap) { + return root.meta.traceMap.get(thrown, trace); + } + } + + function registerHyperTrace(ctx, thrown) { + var trace = []; + var root = null; + while (ctx != null) { + trace.push(ctx); + root = ctx; + ctx = ctx.meta.caller; + } + if (root.meta.traceMap == null) { + root.meta.traceMap = new Map(); // TODO - WeakMap? + } + if (!root.meta.traceMap.get(thrown)) { + var traceEntry = { + trace: trace, + print: function (logger) { + logger = logger || console.error; + logger("hypertrace /// "); + var maxLen = 0; + for (var i = 0; i < trace.length; i++) { + maxLen = Math.max(maxLen, trace[i].meta.feature.displayName.length); + } + for (var i = 0; i < trace.length; i++) { + var traceElt = trace[i]; + logger( + " ->", + traceElt.meta.feature.displayName.padEnd(maxLen + 2), + "-", + traceElt.meta.owner + ); + } + }, + }; + root.meta.traceMap.set(thrown, traceEntry); + } + } + + /** + * @param {string} str + * @returns {string} + */ + function escapeSelector(str) { + return str.replace(/:/g, function (str) { + return "\\" + str; + }); + } + + /** + * @param {any} value + * @param {*} elt + */ + function nullCheck(value, elt) { + if (value == null) { + throw new Error(elt.sourceFor() + " is null"); + } + } + + /** + * @param {any} value + * @returns {boolean} + */ + function isEmpty(value) { + return value == undefined || value.length === 0; + } + + /** @type string | null */ + var hyperscriptUrl = "document" in globalScope ? document.currentScript.src : null; + + /** @type {RuntimeObject} */ + return { + typeCheck: typeCheck, + forEach: forEach, + triggerEvent: triggerEvent, + matchesSelector: matchesSelector, + getScript: getScript, + processNode: processNode, + evaluate: evaluate, + parse: parse, + getScriptSelector: getScriptSelector, + resolveSymbol: resolveSymbol, + setSymbol: setSymbol, + makeContext: makeContext, + findNext: findNext, + unifiedEval: unifiedEval, + convertValue: convertValue, + unifiedExec: unifiedExec, + resolveProperty: resolveProperty, + assignToNamespace: assignToNamespace, + registerHyperTrace: registerHyperTrace, + getHyperTrace: getHyperTrace, + getInternalData: getInternalData, + escapeSelector: escapeSelector, + nullCheck: nullCheck, + isEmpty: isEmpty, + hyperscriptUrl: hyperscriptUrl, + HALT: HALT, + }; + })(); + + //==================================================================== + // Grammar + //==================================================================== + { + _parser.addLeafExpression("parenthesized", function (parser, _runtime, tokens) { + if (tokens.matchOpToken("(")) { + var follows = tokens.clearFollow(); + try { + var expr = parser.requireElement("expression", tokens); + } finally { + tokens.restoreFollow(follows); + } + tokens.requireOpToken(")"); + return expr; + } + }); + + _parser.addLeafExpression("string", function (parser, runtime, tokens) { + var stringToken = tokens.matchTokenType("STRING"); + if (!stringToken) return; + var rawValue = stringToken.value; + if (stringToken.template) { + var innerTokens = _lexer.tokenize(rawValue, true); + var args = parser.parseStringTemplate(innerTokens); + } else { + var args = []; + } + return { + type: "string", + token: stringToken, + args: args, + op: function (context) { + var returnStr = ""; + for (var i = 1; i < arguments.length; i++) { + var val = arguments[i]; + if (val !== undefined) { + returnStr += val; + } + } + return returnStr; + }, + evaluate: function (context) { + if (args.length === 0) { + return rawValue; + } else { + return runtime.unifiedEval(this, context); + } + }, + }; + }); + + _parser.addGrammarElement("nakedString", function (parser, runtime, tokens) { + if (tokens.hasMore()) { + var tokenArr = tokens.consumeUntilWhitespace(); + tokens.matchTokenType("WHITESPACE"); + return { + type: "nakedString", + tokens: tokenArr, + evaluate: function (context) { + return tokenArr + .map(function (t) { + return t.value; + }) + .join(""); + }, + }; + } + }); + + _parser.addLeafExpression("number", function (parser, runtime, tokens) { + var number = tokens.matchTokenType("NUMBER"); + if (!number) return; + var numberToken = number; + var value = parseFloat(number.value); + return { + type: "number", + value: value, + numberToken: numberToken, + evaluate: function () { + return value; + }, + }; + }); + + _parser.addLeafExpression("idRef", function (parser, runtime, tokens) { + var elementId = tokens.matchTokenType("ID_REF"); + if (!elementId) return; + // TODO - unify these two expression types + if (elementId.template) { + var templateValue = elementId.value.substr(2, elementId.value.length - 2); + var innerTokens = _lexer.tokenize(templateValue); + var innerExpression = parser.requireElement("expression", innerTokens); + return { + type: "idRefTemplate", + args: [innerExpression], + op: function (context, arg) { + return context.me.getRootNode().getElementById(arg) || document.getElementById(arg); + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + } else { + return { + type: "idRef", + css: elementId.value, + value: elementId.value.substr(1), + evaluate: function (context) { + return ( + context.me.getRootNode().getElementById(this.value) || document.getElementById(this.value) + ); + }, + }; + } + }); + + _parser.addLeafExpression("classRef", function (parser, runtime, tokens) { + var classRef = tokens.matchTokenType("CLASS_REF"); + + if (!classRef) return; + + // TODO - unify these two expression types + if (classRef.template) { + var templateValue = classRef.value.substr(2, classRef.value.length - 2); + var innerTokens = _lexer.tokenize(templateValue); + var innerExpression = parser.requireElement("expression", innerTokens); + return { + type: "classRefTemplate", + args: [innerExpression], + op: function (context, arg) { + return document.querySelectorAll(runtime.escapeSelector("." + arg)); + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + } else { + return { + type: "classRef", + css: classRef.value, + className: function () { + return this.css.substr(1); + }, + evaluate: function (context) { + return context.me.getRootNode().querySelectorAll(runtime.escapeSelector(this.css)); + }, + }; + } + }); + + _parser.addLeafExpression("queryRef", function (parser, runtime, tokens) { + var queryStart = tokens.matchOpToken("<"); + if (!queryStart) return; + var queryTokens = tokens.consumeUntil("/"); + tokens.requireOpToken("/"); + tokens.requireOpToken(">"); + var queryValue = queryTokens + .map(function (t) { + if (t.type === "STRING") { + return '"' + t.value + '"'; + } else { + return t.value; + } + }) + .join(""); + + if (queryValue.indexOf("$") >= 0) { + var template = true; + var innerTokens = _lexer.tokenize(queryValue, true); + var args = parser.parseStringTemplate(innerTokens); + } + + return { + type: "queryRef", + css: queryValue, + args: args, + op: function (context, args) { + var query = queryValue; + var elements = []; + if (template) { + query = ""; + for (var i = 1; i < arguments.length; i++) { + var val = arguments[i]; + if (val) { + if (val instanceof Element) { + val.dataset.hsQueryId = elements.length; + query += "[data-hs-query-id='" + elements.length + "']"; + elements.push(val); + } else query += val; + } + } + } + var result = context.me.getRootNode().querySelectorAll(query); + runtime.forEach(elements, function (el) { el.removeAttribute("data-hs-query-id") }); + return result; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + _parser.addLeafExpression("attributeRef", function (parser, runtime, tokens) { + var attributeRef = tokens.matchTokenType("ATTRIBUTE_REF"); + if (!attributeRef) return; + var outerVal = attributeRef.value; + if (outerVal.indexOf("[") === 0) { + var innerValue = outerVal.substring(2, outerVal.length - 1); + } else { + var innerValue = outerVal.substring(1); + } + var css = "[" + innerValue + "]"; + var split = innerValue.split("="); + var name = split[0]; + var value = split[1]; + if (value) { + // strip quotes + if (value.indexOf('"') === 0) { + value = value.substring(1, value.length - 1); + } + } + return { + type: "attributeRef", + name: name, + css: css, + value: value, + op: function (context) { + var target = context.beingTold || context.me; + if (target) { + return target.getAttribute(name); + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + _parser.addGrammarElement("objectKey", function (parser, runtime, tokens) { + var token; + if ((token = tokens.matchTokenType("STRING"))) { + return { + type: "objectKey", + key: token.value, + evaluate: function () { + return this.key; + }, + }; + } else if (tokens.matchOpToken("[")) { + var expr = parser.parseElement("expression", tokens); + tokens.requireOpToken("]"); + return { + type: "objectKey", + expr: expr, + args: [expr], + op: function (ctx, expr) { + return expr; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + } else { + var key = ""; + do { + token = tokens.matchTokenType("IDENTIFIER") || tokens.matchOpToken("-"); + if (token) key += token.value; + } while (token); + return { + type: "objectKey", + key: key, + evaluate: function () { + return this.key; + }, + }; + } + }); + + _parser.addLeafExpression("objectLiteral", function (parser, runtime, tokens) { + if (!tokens.matchOpToken("{")) return; + var keyExpressions = []; + var valueExpressions = []; + if (!tokens.matchOpToken("}")) { + do { + var name = parser.requireElement("objectKey", tokens); + tokens.requireOpToken(":"); + var value = parser.requireElement("expression", tokens); + valueExpressions.push(value); + keyExpressions.push(name); + } while (tokens.matchOpToken(",")); + tokens.requireOpToken("}"); + } + return { + type: "objectLiteral", + args: [keyExpressions, valueExpressions], + op: function (context, keys, values) { + var returnVal = {}; + for (var i = 0; i < keys.length; i++) { + returnVal[keys[i]] = values[i]; + } + return returnVal; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + _parser.addGrammarElement("namedArgumentList", function (parser, runtime, tokens) { + if (!tokens.matchOpToken("(")) return; + var fields = []; + var valueExpressions = []; + if (!tokens.matchOpToken(")")) { + do { + var name = tokens.requireTokenType("IDENTIFIER"); + tokens.requireOpToken(":"); + var value = parser.requireElement("expression", tokens); + valueExpressions.push(value); + fields.push({ name: name, value: value }); + } while (tokens.matchOpToken(",")); + tokens.requireOpToken(")"); + } + return { + type: "namedArgumentList", + fields: fields, + args: [valueExpressions], + op: function (context, values) { + var returnVal = { _namedArgList_: true }; + for (var i = 0; i < values.length; i++) { + var field = fields[i]; + returnVal[field.name.value] = values[i]; + } + return returnVal; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + _parser.addGrammarElement("symbol", function (parser, runtime, tokens) { + var type = "default"; + if (tokens.matchToken("global")) { + type = "global"; + } else if (tokens.matchToken("element")) { + type = "element"; + // optional possessive + if (tokens.matchOpToken("'")) { + tokens.requireToken("s"); + } + } else if (tokens.matchToken("local")) { + type = "local"; + } + var identifier = tokens.matchTokenType("IDENTIFIER"); + if (identifier) { + return { + type: "symbol", + symbolType: type, + token: identifier, + name: identifier.value, + evaluate: function (context) { + return runtime.resolveSymbol(identifier.value, context, type); + }, + }; + } + }); + + _parser.addGrammarElement("implicitMeTarget", function (parser, runtime, tokens) { + return { + type: "implicitMeTarget", + evaluate: function (context) { + return context.beingTold || context.me; + }, + }; + }); + + _parser.addLeafExpression("boolean", function (parser, runtime, tokens) { + var booleanLiteral = tokens.matchToken("true") || tokens.matchToken("false"); + if (!booleanLiteral) return; + return { + type: "boolean", + evaluate: function (context) { + return booleanLiteral.value === "true"; + }, + }; + }); + + _parser.addLeafExpression("null", function (parser, runtime, tokens) { + if (tokens.matchToken("null")) { + return { + type: "null", + evaluate: function (context) { + return null; + }, + }; + } + }); + + _parser.addLeafExpression("arrayLiteral", function (parser, runtime, tokens) { + if (!tokens.matchOpToken("[")) return; + var values = []; + if (!tokens.matchOpToken("]")) { + do { + var expr = parser.requireElement("expression", tokens); + values.push(expr); + } while (tokens.matchOpToken(",")); + tokens.requireOpToken("]"); + } + return { + type: "arrayLiteral", + values: values, + args: [values], + op: function (context, values) { + return values; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + _parser.addLeafExpression("blockLiteral", function (parser, runtime, tokens) { + if (!tokens.matchOpToken("\\")) return; + var args = []; + var arg1 = tokens.matchTokenType("IDENTIFIER"); + if (arg1) { + args.push(arg1); + while (tokens.matchOpToken(",")) { + args.push(tokens.requireTokenType("IDENTIFIER")); + } + } + // TODO compound op token + tokens.requireOpToken("-"); + tokens.requireOpToken(">"); + var expr = parser.requireElement("expression", tokens); + return { + type: "blockLiteral", + args: args, + expr: expr, + evaluate: function (ctx) { + var returnFunc = function () { + //TODO - push scope + for (var i = 0; i < args.length; i++) { + ctx[args[i].value] = arguments[i]; + } + return expr.evaluate(ctx); //OK + }; + return returnFunc; + }, + }; + }); + + _parser.addGrammarElement("timeExpression", function (parser, runtime, tokens) { + var time = parser.requireElement("expression", tokens); + var factor = 1; + if (tokens.matchToken("s") || tokens.matchToken("seconds")) { + factor = 1000; + } else if (tokens.matchToken("ms") || tokens.matchToken("milliseconds")) { + // do nothing + } + return { + type: "timeExpression", + time: time, + factor: factor, + args: [time], + op: function (_context, val) { + return val * this.factor; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + _parser.addIndirectExpression("propertyAccess", function (parser, runtime, tokens, root) { + if (!tokens.matchOpToken(".")) return; + var prop = tokens.requireTokenType("IDENTIFIER"); + var propertyAccess = { + type: "propertyAccess", + root: root, + prop: prop, + args: [root], + op: function (_context, rootVal) { + var value = runtime.resolveProperty(rootVal, prop.value); + return value; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + return parser.parseElement("indirectExpression", tokens, propertyAccess); + }); + + _parser.addIndirectExpression("of", function (parser, runtime, tokens, root) { + if (!tokens.matchToken("of")) return; + var newRoot = parser.requireElement("expression", tokens); + // find the urroot + var childOfUrRoot = null; + var urRoot = root; + while (urRoot.root) { + childOfUrRoot = urRoot; + urRoot = urRoot.root; + } + if (urRoot.type !== "symbol" && urRoot.type !== "attributeRef") { + parser.raiseParseError(tokens, "Cannot take a property of a non-symbol: " + urRoot.type); + } + var attribute = urRoot.type === "attributeRef"; + var prop = urRoot.name; + var propertyAccess = { + type: "ofExpression", + prop: urRoot.token, + root: newRoot, + attribute: attribute, + expression: root, + args: [newRoot], + op: function (context, rootVal) { + return runtime.resolveProperty(rootVal, prop, attribute); + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + + if (urRoot.type === "attributeRef") { + propertyAccess.attribute = urRoot; + } + if (childOfUrRoot) { + childOfUrRoot.root = propertyAccess; + childOfUrRoot.args = [propertyAccess]; + } else { + root = propertyAccess; + } + + return parser.parseElement("indirectExpression", tokens, root); + }); + + _parser.addIndirectExpression("possessive", function (parser, runtime, tokens, root) { + if (parser.possessivesDisabled) { + return; + } + var apostrophe = tokens.matchOpToken("'"); + if ( + apostrophe || + (root.type === "symbol" && + (root.name === "my" || root.name === "its" || root.name === "your") && + tokens.currentToken().type === "IDENTIFIER") + ) { + if (apostrophe) { + tokens.requireToken("s"); + } + var attribute = parser.parseElement("attributeRef", tokens); + if (attribute == null) { + var prop = tokens.requireTokenType("IDENTIFIER"); + } + var propertyAccess = { + type: "possessive", + root: root, + attribute: attribute, + prop: prop, + args: [root], + op: function (context, rootVal) { + if (attribute) { + var value = runtime.resolveProperty(rootVal, attribute.name, true); + } else { + var value = runtime.resolveProperty(rootVal, prop.value, false); + } + return value; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + return parser.parseElement("indirectExpression", tokens, propertyAccess); + } + }); + + _parser.addIndirectExpression("inExpression", function (parser, runtime, tokens, root) { + if (!tokens.matchToken("in")) return; + if ((root.type !== "idRef" && root.type === "queryRef") || root.type === "classRef") { + var query = true; + } + var target = parser.requireElement("expression", tokens); + var propertyAccess = { + type: "inExpression", + root: root, + args: [query ? null : root, target], + op: function (context, rootVal, target) { + var returnArr = []; + if (query) { + runtime.forEach(target, function (targetElt) { + var results = targetElt.querySelectorAll(root.css); + for (var i = 0; i < results.length; i++) { + returnArr.push(results[i]); + } + }); + } else { + runtime.forEach(rootVal, function (rootElt) { + runtime.forEach(target, function (targetElt) { + if (rootElt === targetElt) { + returnArr.push(rootElt); + } + }); + }); + } + if (returnArr.length > 0) { + return returnArr; + } else { + return null; + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + return parser.parseElement("indirectExpression", tokens, propertyAccess); + }); + + _parser.addIndirectExpression("asExpression", function (parser, runtime, tokens, root) { + if (!tokens.matchToken("as")) return; + var conversion = parser.requireElement("dotOrColonPath", tokens).evaluate(); // OK No promise + var propertyAccess = { + type: "asExpression", + root: root, + args: [root], + op: function (context, rootVal) { + return runtime.convertValue(rootVal, conversion); + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + return parser.parseElement("indirectExpression", tokens, propertyAccess); + }); + + _parser.addIndirectExpression("functionCall", function (parser, runtime, tokens, root) { + if (!tokens.matchOpToken("(")) return; + var args = []; + if (!tokens.matchOpToken(")")) { + do { + args.push(parser.requireElement("expression", tokens)); + } while (tokens.matchOpToken(",")); + tokens.requireOpToken(")"); + } + + if (root.root) { + var functionCall = { + type: "functionCall", + root: root, + argExressions: args, + args: [root.root, args], + op: function (context, rootRoot, args) { + runtime.nullCheck(rootRoot, root.root); + var func = rootRoot[root.prop.value]; + runtime.nullCheck(func, root); + if (func.hyperfunc) { + args.push(context); + } + return func.apply(rootRoot, args); + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + } else { + var functionCall = { + type: "functionCall", + root: root, + argExressions: args, + args: [root, args], + op: function (context, func, argVals) { + runtime.nullCheck(func, root); + if (func.hyperfunc) { + argVals.push(context); + } + var apply = func.apply(null, argVals); + return apply; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + } + return parser.parseElement("indirectExpression", tokens, functionCall); + }); + + _parser.addIndirectExpression("attributeRefAccess", function (parser, runtime, tokens, root) { + var attribute = parser.parseElement("attributeRef", tokens); + if (!attribute) return; + var attributeAccess = { + type: "attributeRefAccess", + root: root, + attribute: attribute, + args: [root], + op: function (_ctx, rootVal) { + var value = runtime.resolveProperty(rootVal, attribute.name, true); + return value; + }, + evaluate: function (context) { + return _runtime.unifiedEval(this, context); + }, + }; + return attributeAccess; + }); + + _parser.addIndirectExpression("arrayIndex", function (parser, runtime, tokens, root) { + if (!tokens.matchOpToken("[")) return; + var andBefore = false; + var andAfter = false; + var firstIndex = null; + var secondIndex = null; + + if (tokens.matchOpToken("..")) { + andBefore = true; + firstIndex = parser.requireElement("expression", tokens); + } else { + firstIndex = parser.requireElement("expression", tokens); + + if (tokens.matchOpToken("..")) { + andAfter = true; + var current = tokens.currentToken(); + if (current.type !== "R_BRACKET") { + secondIndex = parser.parseElement("expression", tokens); + } + } + } + tokens.requireOpToken("]"); + + var arrayIndex = { + type: "arrayIndex", + root: root, + firstIndex: firstIndex, + secondIndex: secondIndex, + args: [root, firstIndex, secondIndex], + op: function (_ctx, root, firstIndex, secondIndex) { + if (andBefore) { + return root.slice(0, firstIndex + 1); // returns all items from beginning to firstIndex (inclusive) + } else if (andAfter) { + if (secondIndex != null) { + return root.slice(firstIndex, secondIndex + 1); // returns all items from firstIndex to secondIndex (inclusive) + } else { + return root.slice(firstIndex); // returns from firstIndex to end of array + } + } else { + return root[firstIndex]; + } + }, + evaluate: function (context) { + return _runtime.unifiedEval(this, context); + }, + }; + + return _parser.parseElement("indirectExpression", tokens, arrayIndex); + }); + + _parser.addGrammarElement("postfixExpression", function (parser, runtime, tokens) { + var root = parser.parseElement("primaryExpression", tokens); + if (tokens.matchOpToken(":")) { + var typeName = tokens.requireTokenType("IDENTIFIER"); + var nullOk = !tokens.matchOpToken("!"); + return { + type: "typeCheck", + typeName: typeName, + root: root, + nullOk: nullOk, + args: [root], + op: function (context, val) { + var passed = runtime.typeCheck(val, this.typeName.value, this.nullOk); + if (passed) { + return val; + } else { + throw new Error("Typecheck failed! Expected: " + this.typeName.value); + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + } else { + return root; + } + }); + + _parser.addGrammarElement("logicalNot", function (parser, runtime, tokens) { + if (!tokens.matchToken("not")) return; + var root = parser.requireElement("unaryExpression", tokens); + return { + type: "logicalNot", + root: root, + args: [root], + op: function (context, val) { + return !val; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + _parser.addGrammarElement("noExpression", function (parser, runtime, tokens) { + if (!tokens.matchToken("no")) return; + var root = parser.requireElement("unaryExpression", tokens); + return { + type: "noExpression", + root: root, + args: [root], + op: function (_context, val) { + return runtime.isEmpty(val); + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + _parser.addGrammarElement("negativeNumber", function (parser, runtime, tokens) { + if (!tokens.matchOpToken("-")) return; + var root = parser.requireElement("unaryExpression", tokens); + return { + type: "negativeNumber", + root: root, + args: [root], + op: function (context, value) { + return -1 * value; + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + _parser.addGrammarElement("unaryExpression", function (parser, runtime, tokens) { + return parser.parseAnyOf( + ["logicalNot", "positionalExpression", "noExpression", "negativeNumber", "postfixExpression"], + tokens + ); + }); + + _parser.addGrammarElement("positionalExpression", function (parser, runtime, tokens) { + var op = tokens.matchAnyToken("first", "last", "random"); + if (!op) return; + tokens.matchAnyToken("in", "from", "of"); + var rhs = parser.requireElement("unaryExpression", tokens); + return { + type: "positionalExpression", + rhs: rhs, + operator: op.value, + args: [rhs], + op: function (context, rhsVal) { + if (rhsVal && !Array.isArray(rhsVal)) { + if (rhsVal.children) { + rhsVal = rhsVal.children; + } else { + rhsVal = Array.from(rhsVal); + } + } + if (rhsVal) { + if (this.operator === "first") { + return rhsVal[0]; + } else if (this.operator === "last") { + return rhsVal[rhsVal.length - 1]; + } else if (this.operator === "random") { + return rhsVal[Math.floor(Math.random() * rhsVal.length)]; + } + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + }); + + _parser.addGrammarElement("mathOperator", function (parser, runtime, tokens) { + var expr = parser.parseElement("unaryExpression", tokens); + var mathOp, + initialMathOp = null; + mathOp = tokens.matchAnyOpToken("+", "-", "*", "/", "%"); + while (mathOp) { + initialMathOp = initialMathOp || mathOp; + var operator = mathOp.value; + if (initialMathOp.value !== operator) { + parser.raiseParseError(tokens, "You must parenthesize math operations with different operators"); + } + var rhs = parser.parseElement("unaryExpression", tokens); + expr = { + type: "mathOperator", + lhs: expr, + rhs: rhs, + operator: operator, + args: [expr, rhs], + op: function (context, lhsVal, rhsVal) { + if (this.operator === "+") { + return lhsVal + rhsVal; + } else if (this.operator === "-") { + return lhsVal - rhsVal; + } else if (this.operator === "*") { + return lhsVal * rhsVal; + } else if (this.operator === "/") { + return lhsVal / rhsVal; + } else if (this.operator === "%") { + return lhsVal % rhsVal; + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + mathOp = tokens.matchAnyOpToken("+", "-", "*", "/", "%"); + } + return expr; + }); + + _parser.addGrammarElement("mathExpression", function (parser, runtime, tokens) { + return parser.parseAnyOf(["mathOperator", "unaryExpression"], tokens); + }); + + _parser.addGrammarElement("comparisonOperator", function (parser, runtime, tokens) { + var expr = parser.parseElement("mathExpression", tokens); + var comparisonToken = tokens.matchAnyOpToken("<", ">", "<=", ">=", "==", "===", "!=", "!=="); + var comparisonStr = comparisonToken ? comparisonToken.value : null; + var hasRightValue = true; // By default, most comparisons require two values, but there are some exceptions. + var typeCheck = false; + + if (comparisonStr == null) { + if (tokens.matchToken("is") || tokens.matchToken("am")) { + if (tokens.matchToken("not")) { + if (tokens.matchToken("in")) { + comparisonStr = "not in"; + } else if (tokens.matchToken("a")) { + comparisonStr = "not a"; + typeCheck = true; + } else if (tokens.matchToken("empty")) { + comparisonStr = "not empty"; + hasRightValue = false; + } else { + comparisonStr = "!="; + } + } else if (tokens.matchToken("in")) { + comparisonStr = "in"; + } else if (tokens.matchToken("a")) { + comparisonStr = "a"; + typeCheck = true; + } else if (tokens.matchToken("empty")) { + comparisonStr = "empty"; + hasRightValue = false; + } else { + comparisonStr = "=="; + } + } else if (tokens.matchToken("matches") || tokens.matchToken("match")) { + comparisonStr = "match"; + } else if (tokens.matchToken("contains") || tokens.matchToken("contain")) { + comparisonStr = "contain"; + } else if (tokens.matchToken("do") || tokens.matchToken("does")) { + tokens.requireToken("not"); + if (tokens.matchToken("matches") || tokens.matchToken("match")) { + comparisonStr = "not match"; + } else if (tokens.matchToken("contains") || tokens.matchToken("contain")) { + comparisonStr = "not contain"; + } else { + parser.raiseParseError(tokens, "Expected matches or contains"); + } + } + } + + if (comparisonStr) { + // Do not allow chained comparisons, which is dumb + if (typeCheck) { + var typeName = tokens.requireTokenType("IDENTIFIER"); + var nullOk = !tokens.matchOpToken("!"); + } else if (hasRightValue) { + var rhs = parser.requireElement("mathExpression", tokens); + if (comparisonStr === "match" || comparisonStr === "not match") { + rhs = rhs.css ? rhs.css : rhs; + } + } + expr = { + type: "comparisonOperator", + operator: comparisonStr, + typeName: typeName, + nullOk: nullOk, + lhs: expr, + rhs: rhs, + args: [expr, rhs], + op: function (context, lhsVal, rhsVal) { + if (this.operator === "==") { + return lhsVal == rhsVal; + } else if (this.operator === "!=") { + return lhsVal != rhsVal; + } + if (this.operator === "in") { + return rhsVal != null && Array.from(rhsVal).indexOf(lhsVal) >= 0; + } + if (this.operator === "not in") { + return rhsVal == null || Array.from(rhsVal).indexOf(lhsVal) < 0; + } + if (this.operator === "match") { + return lhsVal != null && lhsVal.matches(rhsVal); + } + if (this.operator === "not match") { + return lhsVal == null || !lhsVal.matches(rhsVal); + } + if (this.operator === "contain") { + return lhsVal != null && lhsVal.contains(rhsVal); + } + if (this.operator === "not contain") { + return lhsVal == null || !lhsVal.contains(rhsVal); + } + if (this.operator === "===") { + return lhsVal === rhsVal; + } else if (this.operator === "!==") { + return lhsVal !== rhsVal; + } else if (this.operator === "<") { + return lhsVal < rhsVal; + } else if (this.operator === ">") { + return lhsVal > rhsVal; + } else if (this.operator === "<=") { + return lhsVal <= rhsVal; + } else if (this.operator === ">=") { + return lhsVal >= rhsVal; + } else if (this.operator === "empty") { + return runtime.isEmpty(lhsVal); + } else if (this.operator === "not empty") { + return !runtime.isEmpty(lhsVal); + } else if (this.operator === "a") { + return runtime.typeCheck(lhsVal, this.typeName.value, this.nullOk); + } else if (this.operator === "not a") { + return !runtime.typeCheck(lhsVal, this.typeName.value, this.nullOk); + } else { + throw "Unknown comparison : " + this.operator; + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + } + return expr; + }); + + _parser.addGrammarElement("comparisonExpression", function (parser, runtime, tokens) { + return parser.parseAnyOf(["comparisonOperator", "mathExpression"], tokens); + }); + + _parser.addGrammarElement("logicalOperator", function (parser, runtime, tokens) { + var expr = parser.parseElement("comparisonExpression", tokens); + var logicalOp, + initialLogicalOp = null; + logicalOp = tokens.matchToken("and") || tokens.matchToken("or"); + while (logicalOp) { + initialLogicalOp = initialLogicalOp || logicalOp; + if (initialLogicalOp.value !== logicalOp.value) { + parser.raiseParseError(tokens, "You must parenthesize logical operations with different operators"); + } + var rhs = parser.requireElement("comparisonExpression", tokens); + expr = { + type: "logicalOperator", + operator: logicalOp.value, + lhs: expr, + rhs: rhs, + args: [expr, rhs], + op: function (context, lhsVal, rhsVal) { + if (this.operator === "and") { + return lhsVal && rhsVal; + } else { + return lhsVal || rhsVal; + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + logicalOp = tokens.matchToken("and") || tokens.matchToken("or"); + } + return expr; + }); + + _parser.addGrammarElement("logicalExpression", function (parser, runtime, tokens) { + return parser.parseAnyOf(["logicalOperator", "mathExpression"], tokens); + }); + + _parser.addGrammarElement("asyncExpression", function (parser, runtime, tokens) { + if (tokens.matchToken("async")) { + var value = parser.requireElement("logicalExpression", tokens); + var expr = { + type: "asyncExpression", + value: value, + evaluate: function (context) { + return { + asyncWrapper: true, + value: this.value.evaluate(context), //OK + }; + }, + }; + return expr; + } else { + return parser.parseElement("logicalExpression", tokens); + } + }); + + _parser.addGrammarElement("expression", function (parser, runtime, tokens) { + tokens.matchToken("the"); // optional the + return parser.parseElement("asyncExpression", tokens); + }); + + _parser.addGrammarElement("assignableExpression", function (parser, runtime, tokens) { + tokens.matchToken("the"); // optional the + + // TODO obviously we need to generalize this as a left hand side / targetable concept + var expr = parser.parseElement("primaryExpression", tokens); + if ( + expr.type === "symbol" || + expr.type === "ofExpression" || + expr.type === "propertyAccess" || + expr.type === "attributeRefAccess" || + expr.type === "attributeRef" || + expr.type === "possessive" + ) { + return expr; + } else { + _parser.raiseParseError( + tokens, + "A target expression must be writable. The expression type '" + expr.type + "' is not." + ); + } + return expr; + }); + + _parser.addGrammarElement("hyperscript", function (parser, runtime, tokens) { + var features = []; + + if (tokens.hasMore()) { + while (parser.featureStart(tokens.currentToken()) || tokens.currentToken().value === "(") { + var feature = parser.requireElement("feature", tokens); + features.push(feature); + tokens.matchToken("end"); // optional end + } + } + return { + type: "hyperscript", + features: features, + apply: function (target, source, args) { + // no op + _runtime.forEach(features, function (feature) { + feature.install(target, source, args); + }); + }, + }; + }); + + var parseEventArgs = function (tokens) { + var args = []; + // handle argument list (look ahead 3) + if ( + tokens.token(0).value === "(" && + (tokens.token(1).value === ")" || tokens.token(2).value === "," || tokens.token(2).value === ")") + ) { + tokens.matchOpToken("("); + do { + args.push(tokens.requireTokenType("IDENTIFIER")); + } while (tokens.matchOpToken(",")); + tokens.requireOpToken(")"); + } + return args; + }; + + _parser.addFeature("on", function (parser, runtime, tokens) { + if (!tokens.matchToken("on")) return; + var every = false; + if (tokens.matchToken("every")) { + every = true; + } + var events = []; + var displayName = null; + do { + var on = parser.requireElement("eventName", tokens, "Expected event name"); + + var eventName = on.evaluate(); // OK No Promise + + if (displayName) { + displayName = displayName + " or " + eventName; + } else { + displayName = "on " + eventName; + } + var args = parseEventArgs(tokens); + + var filter = null; + if (tokens.matchOpToken("[")) { + filter = parser.requireElement("expression", tokens); + tokens.requireOpToken("]"); + } + + if (tokens.currentToken().type === "NUMBER") { + var startCountToken = tokens.consumeToken(); + var startCount = parseInt(startCountToken.value); + if (tokens.matchToken("to")) { + var endCountToken = tokens.consumeToken(); + var endCount = parseInt(endCountToken.value); + } else if (tokens.matchToken("and")) { + var unbounded = true; + tokens.requireToken("on"); + } + } + + if (eventName === "intersection") { + var intersectionSpec = {}; + if (tokens.matchToken("with")) { + intersectionSpec["with"] = parser.parseElement("expression", tokens).evaluate(); + } + if (tokens.matchToken("having")) { + do { + if (tokens.matchToken("margin")) { + intersectionSpec["rootMargin"] = parser.parseElement("stringLike", tokens).evaluate(); + } else if (tokens.matchToken("threshold")) { + intersectionSpec["threshold"] = parser.parseElement("expression", tokens).evaluate(); + } else { + parser.raiseParseError(tokens, "Unknown intersection config specification"); + } + } while (tokens.matchToken("and")); + } + } else if (eventName === "mutation") { + var mutationSpec = {}; + if (tokens.matchToken("of")) { + do { + if (tokens.matchToken("anything")) { + mutationSpec["attributes"] = true; + mutationSpec["subtree"] = true; + mutationSpec["characterData"] = true; + mutationSpec["childList"] = true; + } else if (tokens.matchToken("childList")) { + mutationSpec["childList"] = true; + } else if (tokens.matchToken("attributes")) { + mutationSpec["attributes"] = true; + mutationSpec["attributeOldValue"] = true; + } else if (tokens.matchToken("subtree")) { + mutationSpec["subtree"] = true; + } else if (tokens.matchToken("characterData")) { + mutationSpec["characterData"] = true; + mutationSpec["characterDataOldValue"] = true; + } else if (tokens.currentToken().type === "ATTRIBUTE_REF") { + var attribute = tokens.consumeToken(); + if (mutationSpec["attributeFilter"] == null) { + mutationSpec["attributeFilter"] = []; + } + if (attribute.value.indexOf("@") == 0) { + mutationSpec["attributeFilter"].push(attribute.value.substring(1)); + } else { + parser.raiseParseError( + tokens, + "Only shorthand attribute references are allowed here" + ); + } + } else { + parser.raiseParseError(tokens, "Unknown mutation config specification"); + } + } while (tokens.matchToken("or")); + } else { + mutationSpec["attributes"] = true; + mutationSpec["characterData"] = true; + mutationSpec["childList"] = true; + } + } + + var from = null; + var elsewhere = false; + if (tokens.matchToken("from")) { + if (tokens.matchToken("elsewhere")) { + elsewhere = true; + } else { + from = parser.parseElement("expression", tokens); + if (!from) { + parser.raiseParseError('Expected either target value or "elsewhere".', tokens); + } + } + } + // support both "elsewhere" and "from elsewhere" + if (from === null && elsewhere === false && tokens.matchToken("elsewhere")) { + elsewhere = true; + } + + if (tokens.matchToken("in")) { + var inExpr = parser.parseAnyOf(["idRef", "queryRef", "classRef"], tokens); + } + + if (tokens.matchToken("debounced")) { + tokens.requireToken("at"); + var timeExpr = parser.requireElement("timeExpression", tokens); + var debounceTime = timeExpr.evaluate({}); // OK No promise TODO make a literal time expr + } else if (tokens.matchToken("throttled")) { + tokens.requireToken("at"); + var timeExpr = parser.requireElement("timeExpression", tokens); + var throttleTime = timeExpr.evaluate({}); // OK No promise TODO make a literal time expr + } + + events.push({ + execCount: 0, + every: every, + on: eventName, + args: args, + filter: filter, + from: from, + inExpr: inExpr, + elsewhere: elsewhere, + startCount: startCount, + endCount: endCount, + unbounded: unbounded, + debounceTime: debounceTime, + throttleTime: throttleTime, + mutationSpec: mutationSpec, + intersectionSpec: intersectionSpec, + }); + } while (tokens.matchToken("or")); + + var queue = []; + var queueLast = true; + if (!every) { + if (tokens.matchToken("queue")) { + if (tokens.matchToken("all")) { + var queueAll = true; + var queueLast = false; + } else if (tokens.matchToken("first")) { + var queueFirst = true; + } else if (tokens.matchToken("none")) { + var queueNone = true; + } else { + tokens.requireToken("last"); + } + } + } + + var start = parser.requireElement("commandList", tokens); + + var implicitReturn = { + type: "implicitReturn", + op: function (context) { + // automatically resolve at the end of an event handler if nothing else does + context.meta.resolve(); + return runtime.HALT; + }, + execute: function (ctx) { + // do nothing + }, + }; + if (start) { + var end = start; + while (end.next) { + end = end.next; + } + end.next = implicitReturn; + } else { + start = implicitReturn; + } + + var onFeature = { + displayName: displayName, + events: events, + start: start, + every: every, + executing: false, + execCount: 0, + queue: queue, + execute: function (/** @type {Context} */ ctx) { + if (this.executing && this.every === false) { + if (queueNone || (queueFirst && queue.length > 0)) { + return; + } + if (queueLast) { + onFeature.queue.length = 0; + } + onFeature.queue.push(ctx); + return; + } + this.execCount++; + this.executing = true; + ctx.meta.resolve = function () { + onFeature.executing = false; + var queued = onFeature.queue.shift(); + if (queued) { + setTimeout(function () { + onFeature.execute(queued); + }, 1); + } + }; + ctx.meta.reject = function (err) { + console.error(err.message ? err.message : err); + var hypertrace = runtime.getHyperTrace(ctx, err); + if (hypertrace) { + hypertrace.print(); + } + runtime.triggerEvent(ctx.me, "exception", { + error: err, + }); + onFeature.executing = false; + var queued = onFeature.queue.shift(); + if (queued) { + setTimeout(function () { + onFeature.execute(queued); + }, 1); + } + }; + start.execute(ctx); + }, + install: function (elt, source) { + runtime.forEach(onFeature.events, function (eventSpec) { + var targets; + if (eventSpec.elsewhere) { + targets = [document]; + } else if (eventSpec.from) { + targets = eventSpec.from.evaluate({ + me: elt, + }); + } else { + targets = [elt]; + } + runtime.forEach(targets, function (target) { + // OK NO PROMISE + + var eventName = eventSpec.on; + if (eventSpec.mutationSpec) { + eventName = "hyperscript:mutation"; + var observer = new MutationObserver(function (mutationList, observer) { + console.log(target, mutationList); + if (!onFeature.executing) { + _runtime.triggerEvent(target, eventName, { + mutationList: mutationList, + observer: observer, + }); + } + }); + observer.observe(target, eventSpec.mutationSpec); + } + + if (eventSpec.intersectionSpec) { + eventName = "hyperscript:insersection"; + var observer = new IntersectionObserver(function (entries) { + _runtime.forEach(entries, function (entry) { + var detail = { + observer: observer, + }; + detail = mergeObjects(detail, entry); + detail["intersecting"] = entry.isIntersecting; + _runtime.triggerEvent(target, eventName, detail); + }); + }, eventSpec.intersectionSpec); + observer.observe(target); + } + + target.addEventListener(eventName, function listener(evt) { + // OK NO PROMISE + if (elt instanceof Node && target !== elt && elt.getRootNode() === null) { + target.removeEventListener(eventName, listener); + return; + } + + var ctx = runtime.makeContext(elt, onFeature, elt, evt); + if (eventSpec.elsewhere && elt.contains(evt.target)) { + return; + } + if (eventSpec.from) { + ctx.result = target; + } + + // establish context + runtime.forEach(eventSpec.args, function (arg) { + ctx[arg.value] = + ctx.event[arg.value] || (ctx.event.detail ? ctx.event.detail[arg.value] : null); + }); + + // apply filter + if (eventSpec.filter) { + var initialCtx = ctx.meta.context; + ctx.meta.context = ctx.event; + try { + var value = eventSpec.filter.evaluate(ctx); //OK NO PROMISE + if (value) { + // match the javascript semantics for if statements + } else { + return; + } + } finally { + ctx.meta.context = initialCtx; + } + } + + if (eventSpec.inExpr) { + var inElement = evt.target; + while (true) { + if (inElement.matches && inElement.matches(eventSpec.inExpr.css)) { + ctx.result = inElement; + break; + } else { + inElement = inElement.parentElement; + if (inElement == null) { + return; // no match found + } + } + } + } + + // verify counts + eventSpec.execCount++; + if (eventSpec.startCount) { + if (eventSpec.endCount) { + if ( + eventSpec.execCount < eventSpec.startCount || + eventSpec.execCount > eventSpec.endCount + ) { + return; + } + } else if (eventSpec.unbounded) { + if (eventSpec.execCount < eventSpec.startCount) { + return; + } + } else if (eventSpec.execCount !== eventSpec.startCount) { + return; + } + } + + //debounce + if (eventSpec.debounceTime) { + if (eventSpec.debounced) { + clearTimeout(eventSpec.debounced); + } + eventSpec.debounced = setTimeout(function () { + onFeature.execute(ctx); + }, eventSpec.debounceTime); + return; + } + + // throttle + if (eventSpec.throttleTime) { + if ( + eventSpec.lastExec && + Date.now() < eventSpec.lastExec + eventSpec.throttleTime + ) { + return; + } else { + eventSpec.lastExec = Date.now(); + } + } + + // apply execute + onFeature.execute(ctx); + }); + }); + }); + }, + }; + parser.setParent(start, onFeature); + return onFeature; + }); + + _parser.addFeature("def", function (parser, runtime, tokens) { + if (!tokens.matchToken("def")) return; + var functionName = parser.requireElement("dotOrColonPath", tokens); + var nameVal = functionName.evaluate(); // OK + var nameSpace = nameVal.split("."); + var funcName = nameSpace.pop(); + + var args = []; + if (tokens.matchOpToken("(")) { + if (tokens.matchOpToken(")")) { + // emtpy args list + } else { + do { + args.push(tokens.requireTokenType("IDENTIFIER")); + } while (tokens.matchOpToken(",")); + tokens.requireOpToken(")"); + } + } + + var start = parser.parseElement("commandList", tokens); + if (tokens.matchToken("catch")) { + var errorSymbol = tokens.requireTokenType("IDENTIFIER").value; + var errorHandler = parser.parseElement("commandList", tokens); + } + var functionFeature = { + displayName: + funcName + + "(" + + args + .map(function (arg) { + return arg.value; + }) + .join(", ") + + ")", + name: funcName, + args: args, + start: start, + errorHandler: errorHandler, + errorSymbol: errorSymbol, + install: function (target, source) { + var func = function () { + // null, worker + var ctx = runtime.makeContext(source, functionFeature, target, null); + + // install error handler if any + ctx.meta.errorHandler = errorHandler; + ctx.meta.errorSymmbol = errorSymbol; + + for (var i = 0; i < args.length; i++) { + var name = args[i]; + var argumentVal = arguments[i]; + if (name) { + ctx[name.value] = argumentVal; + } + } + ctx.meta.caller = arguments[args.length]; + if (ctx.meta.caller) { + ctx.meta.callingCommand = ctx.meta.caller.meta.command; + } + var resolve, + reject = null; + var promise = new Promise(function (theResolve, theReject) { + resolve = theResolve; + reject = theReject; + }); + start.execute(ctx); + if (ctx.meta.returned) { + return ctx.meta.returnValue; + } else { + ctx.meta.resolve = resolve; + ctx.meta.reject = reject; + return promise; + } + }; + func.hyperfunc = true; + func.hypername = nameVal; + runtime.assignToNamespace(target, nameSpace, funcName, func); + }, + }; + + var implicitReturn = { + type: "implicitReturn", + op: function (context) { + // automatically return at the end of the function if nothing else does + context.meta.returned = true; + if (context.meta.resolve) { + context.meta.resolve(); + } + return runtime.HALT; + }, + execute: function (context) { + // do nothing + }, + }; + // terminate body + if (start) { + var end = start; + while (end.next) { + end = end.next; + } + end.next = implicitReturn; + } else { + functionFeature.start = implicitReturn; + } + + // terminate error handler + if (errorHandler) { + var end = errorHandler; + while (end.next) { + end = end.next; + } + end.next = implicitReturn; + } + + parser.setParent(start, functionFeature); + return functionFeature; + }); + + _parser.addFeature("init", function (parser, runtime, tokens) { + if (!tokens.matchToken("init")) return; + + var start = parser.parseElement("commandList", tokens); + var initFeature = { + start: start, + install: function (target, source) { + setTimeout(function () { + start.execute(runtime.makeContext(target, this, target, null)); + }, 0); + }, + }; + + var implicitReturn = { + type: "implicitReturn", + op: function (context) { + return runtime.HALT; + }, + execute: function (context) { + // do nothing + }, + }; + // terminate body + if (start) { + var end = start; + while (end.next) { + end = end.next; + } + end.next = implicitReturn; + } else { + initFeature.start = implicitReturn; + } + parser.setParent(start, initFeature); + return initFeature; + }); + + _parser.addFeature("worker", function (parser, runtime, tokens) { + if (tokens.matchToken("worker")) { + parser.raiseParseError( + tokens, + "In order to use the 'worker' feature, include " + + "the _hyperscript worker plugin. See " + + "https://hyperscript.org/features/worker/ for " + + "more info." + ); + } + }); + + _parser.addFeature("behavior", function (parser, runtime, tokens) { + if (!tokens.matchToken("behavior")) return; + var path = parser.parseElement("dotOrColonPath", tokens).evaluate(); + var nameSpace = path.split("."); + var name = nameSpace.pop(); + + var formalParams = []; + if (tokens.matchOpToken("(") && !tokens.matchOpToken(")")) { + do { + formalParams.push(tokens.requireTokenType("IDENTIFIER").value); + } while (tokens.matchOpToken(",")); + tokens.requireOpToken(")"); + } + var hs = parser.parseElement("hyperscript", tokens); + for (var i = 0; i < hs.features.length; i++) { + var feature = hs.features[i]; + feature.behavior = path; + } + + return { + install: function (target, source) { + runtime.assignToNamespace( + globalScope.document && globalScope.document.body, + nameSpace, + name, + function (target, source, innerArgs) { + var internalData = runtime.getInternalData(target); + var elementScope = getOrInitObject(internalData, path + "Scope"); + for (var i = 0; i < formalParams.length; i++) { + elementScope[formalParams[i]] = innerArgs[formalParams[i]]; + } + hs.apply(target, source); + } + ); + }, + }; + }); + + _parser.addFeature("install", function (parser, runtime, tokens) { + if (!tokens.matchToken("install")) return; + var behaviorPath = parser.requireElement("dotOrColonPath", tokens).evaluate(); + var behaviorNamespace = behaviorPath.split("."); + var args = parser.parseElement("namedArgumentList", tokens); + + var installFeature; + return (installFeature = { + install: function (target, source) { + runtime.unifiedEval( + { + args: [args], + op: function (ctx, args) { + var behavior = globalScope; + for (var i = 0; i < behaviorNamespace.length; i++) { + behavior = behavior[behaviorNamespace[i]]; + if (typeof behavior !== "object" && typeof behavior !== "function") + throw new Error("No such behavior defined as " + behaviorPath); + } + + if (!(behavior instanceof Function)) + throw new Error(behaviorPath + " is not a behavior"); + + behavior(target, source, args); + }, + }, + runtime.makeContext(target, installFeature, target) + ); + }, + }); + }); + + _parser.addGrammarElement("jsBody", function (parser, runtime, tokens) { + var jsSourceStart = tokens.currentToken().start; + var jsLastToken = tokens.currentToken(); + + var funcNames = []; + var funcName = ""; + var expectFunctionDeclaration = false; + while (tokens.hasMore()) { + jsLastToken = tokens.consumeToken(); + var peek = tokens.currentToken(true); + if (peek.type === "IDENTIFIER" && peek.value === "end") { + break; + } + if (expectFunctionDeclaration) { + if (jsLastToken.type === "IDENTIFIER" || jsLastToken.type === "NUMBER") { + funcName += jsLastToken.value; + } else { + if (funcName !== "") funcNames.push(funcName); + funcName = ""; + expectFunctionDeclaration = false; + } + } else if (jsLastToken.type === "IDENTIFIER" && jsLastToken.value === "function") { + expectFunctionDeclaration = true; + } + } + var jsSourceEnd = jsLastToken.end + 1; + + return { + type: "jsBody", + exposedFunctionNames: funcNames, + jsSource: tokens.source.substring(jsSourceStart, jsSourceEnd), + }; + }); + + _parser.addFeature("js", function (parser, runtime, tokens) { + if (!tokens.matchToken("js")) return; + var jsBody = parser.parseElement("jsBody", tokens); + + var jsSource = + jsBody.jsSource + + "\nreturn { " + + jsBody.exposedFunctionNames + .map(function (name) { + return name + ":" + name; + }) + .join(",") + + " } "; + var func = new Function(jsSource); + + return { + jsSource: jsSource, + function: func, + exposedFunctionNames: jsBody.exposedFunctionNames, + install: function () { + mergeObjects(globalScope, func()); + }, + }; + }); + + _parser.addCommand("js", function (parser, runtime, tokens) { + if (!tokens.matchToken("js")) return; + // Parse inputs + var inputs = []; + if (tokens.matchOpToken("(")) { + if (tokens.matchOpToken(")")) { + // empty input list + } else { + do { + var inp = tokens.requireTokenType("IDENTIFIER"); + inputs.push(inp.value); + } while (tokens.matchOpToken(",")); + tokens.requireOpToken(")"); + } + } + + var jsBody = parser.parseElement("jsBody", tokens); + tokens.matchToken("end"); + + var func = varargConstructor(Function, inputs.concat([jsBody.jsSource])); + + return { + jsSource: jsBody.jsSource, + function: func, + inputs: inputs, + op: function (context) { + var args = []; + inputs.forEach(function (input) { + args.push(runtime.resolveSymbol(input, context)); + }); + var result = func.apply(globalScope, args); + if (result && typeof result.then === "function") { + return Promise(function (resolve) { + result.then(function (actualResult) { + context.result = actualResult; + resolve(runtime.findNext(this, context)); + }); + }); + } else { + context.result = result; + return runtime.findNext(this, context); + } + }, + }; + }); + + _parser.addCommand("async", function (parser, runtime, tokens) { + if (!tokens.matchToken("async")) return; + if (tokens.matchToken("do")) { + var body = parser.requireElement("commandList", tokens); + tokens.requireToken("end"); + } else { + var body = parser.requireElement("command", tokens); + } + return { + body: body, + op: function (context) { + setTimeout(function () { + body.execute(context); + }); + return runtime.findNext(this, context); + }, + }; + }); + + _parser.addCommand("tell", function (parser, runtime, tokens) { + var startToken = tokens.currentToken(); + if (!tokens.matchToken("tell")) return; + var value = parser.requireElement("expression", tokens); + var body = parser.requireElement("commandList", tokens); + if (tokens.hasMore()) { + tokens.requireToken("end"); + } + var slot = "tell_" + startToken.start; + var tellCmd = { + value: value, + body: body, + args: [value], + resolveNext: function (context) { + var iterator = context.meta.iterators[slot]; + if (iterator.index < iterator.value.length) { + context.beingTold = iterator.value[iterator.index++]; + return body; + } else { + // restore original me + context.beingTold = iterator.originalBeingTold; + if (this.next) { + return this.next; + } else { + return runtime.findNext(this.parent, context); + } + } + }, + op: function (context, value) { + if (value == null) { + value = []; + } else if (!(Array.isArray(value) || value instanceof NodeList)) { + value = [value]; + } + context.meta.iterators[slot] = { + originalBeingTold: context.beingTold, + index: 0, + value: value, + }; + return this.resolveNext(context); + }, + }; + parser.setParent(body, tellCmd); + return tellCmd; + }); + + _parser.addCommand("wait", function (parser, runtime, tokens) { + if (!tokens.matchToken("wait")) return; + // wait on event + if (tokens.matchToken("for")) { + tokens.matchToken("a"); // optional "a" + var events = []; + do { + events.push({ + name: _parser.requireElement("dotOrColonPath", tokens, "Expected event name").evaluate(), + args: parseEventArgs(tokens), + }); + } while (tokens.matchToken("or")); + + if (tokens.matchToken("from")) { + var on = parser.requireElement("expression", tokens); + } + + // wait on event + var waitCmd = { + event: events, + on: on, + args: [on], + op: function (context, on) { + var target = on ? on : context.me; + if (!(target instanceof EventTarget)) + throw new Error("Not a valid event target: " + this.on.sourceFor()); + return new Promise(function (resolve) { + var resolved = false; + runtime.forEach(events, function (eventInfo) { + var listener = function (event) { + context.result = event; + runtime.forEach(eventInfo.args, function (arg) { + context[arg.value] = + event[arg.value] || (event.detail ? event.detail[arg.value] : null); + }); + if (!resolved) { + resolved = true; + resolve(runtime.findNext(waitCmd, context)); + } + }; + target.addEventListener(eventInfo.name, listener, { once: true }); + }); + }); + }, + }; + } else { + if (tokens.matchToken("a")) { + tokens.requireToken("tick"); + time = 0; + } else { + var time = _parser.requireElement("timeExpression", tokens); + } + + var waitCmd = { + type: "waitCmd", + time: time, + args: [time], + op: function (context, timeValue) { + return new Promise(function (resolve) { + setTimeout(function () { + resolve(runtime.findNext(waitCmd, context)); + }, timeValue); + }); + }, + execute: function (context) { + return runtime.unifiedExec(this, context); + }, + }; + } + return waitCmd; + }); + + // TODO - colon path needs to eventually become part of ruby-style symbols + _parser.addGrammarElement("dotOrColonPath", function (parser, runtime, tokens) { + var root = tokens.matchTokenType("IDENTIFIER"); + if (root) { + var path = [root.value]; + + var separator = tokens.matchOpToken(".") || tokens.matchOpToken(":"); + if (separator) { + do { + path.push(tokens.requireTokenType("IDENTIFIER").value); + } while (tokens.matchOpToken(separator.value)); + } + + return { + type: "dotOrColonPath", + path: path, + evaluate: function () { + return path.join(separator ? separator.value : ""); + }, + }; + } + }); + + _parser.addGrammarElement("eventName", function (parser, runtime, tokens) { + var token; + if ((token = tokens.matchTokenType("STRING"))) { + return { + evaluate: function() { + return token.value; + }, + }; + } + + return parser.parseElement("dotOrColonPath", tokens); + }); + + _parser.addCommand("send", function (parser, runtime, tokens) { + if (!tokens.matchToken("send")) return; + var eventName = parser.requireElement("eventName", tokens); + + var details = parser.parseElement("namedArgumentList", tokens); + if (tokens.matchToken("to")) { + var to = parser.requireElement("expression", tokens); + } else { + var to = parser.requireElement("implicitMeTarget", tokens); + } + + var sendCmd = { + eventName: eventName, + details: details, + to: to, + args: [to, eventName, details], + op: function (context, to, eventName, details) { + runtime.forEach(to, function (target) { + runtime.triggerEvent(target, eventName, details ? details : {}); + }); + return runtime.findNext(sendCmd, context); + }, + }; + return sendCmd; + }); + + var parseReturnFunction = function (parser, runtime, tokens, returnAValue) { + if (returnAValue) { + var value = parser.requireElement("expression", tokens); + } + + var returnCmd = { + value: value, + args: [value], + op: function (context, value) { + var resolve = context.meta.resolve; + context.meta.returned = true; + if (resolve) { + if (value) { + resolve(value); + } else { + resolve(); + } + } else { + context.meta.returned = true; + context.meta.returnValue = value; + } + return runtime.HALT; + }, + }; + return returnCmd; + }; + + _parser.addCommand("return", function (parser, runtime, tokens) { + if (tokens.matchToken("return")) { + return parseReturnFunction(parser, runtime, tokens, true); + } + }); + + _parser.addCommand("exit", function (parser, runtime, tokens) { + if (tokens.matchToken("exit")) { + return parseReturnFunction(parser, runtime, tokens, false); + } + }); + + _parser.addCommand("halt", function (parser, runtime, tokens) { + if (tokens.matchToken("halt")) { + if (tokens.matchToken("the")) { + tokens.requireToken("event"); + // optional possessive + if (tokens.matchOpToken("'")) { + tokens.requireToken("s"); + } + var keepExecuting = true; + } + if (tokens.matchToken("bubbling")) { + var bubbling = true; + } else if (tokens.matchToken("default")) { + var haltDefault = true; + } + var exit = parseReturnFunction(parser, runtime, tokens, false); + + var haltCmd = { + keepExecuting: true, + bubbling: bubbling, + haltDefault: haltDefault, + exit: exit, + op: function (ctx) { + if (ctx.event) { + if (bubbling) { + ctx.event.stopPropagation(); + } else if (haltDefault) { + ctx.event.preventDefault(); + } else { + ctx.event.stopPropagation(); + ctx.event.preventDefault(); + } + if (keepExecuting) { + return runtime.findNext(this, ctx); + } else { + return exit; + } + } + }, + }; + return haltCmd; + } + }); + + _parser.addCommand("log", function (parser, runtime, tokens) { + if (!tokens.matchToken("log")) return; + var exprs = [parser.parseElement("expression", tokens)]; + while (tokens.matchOpToken(",")) { + exprs.push(parser.requireElement("expression", tokens)); + } + if (tokens.matchToken("with")) { + var withExpr = parser.requireElement("expression", tokens); + } + var logCmd = { + exprs: exprs, + withExpr: withExpr, + args: [withExpr, exprs], + op: function (ctx, withExpr, values) { + if (withExpr) { + withExpr.apply(null, values); + } else { + console.log.apply(null, values); + } + return runtime.findNext(this, ctx); + }, + }; + return logCmd; + }); + + _parser.addCommand("throw", function (parser, runtime, tokens) { + if (!tokens.matchToken("throw")) return; + var expr = parser.requireElement("expression", tokens); + var throwCmd = { + expr: expr, + args: [expr], + op: function (ctx, expr) { + runtime.registerHyperTrace(ctx, expr); + var reject = ctx.meta && ctx.meta.reject; + if (reject) { + reject(expr); + return runtime.HALT; + } else { + throw expr; + } + }, + }; + return throwCmd; + }); + + var parseCallOrGet = function (parser, runtime, tokens) { + var expr = parser.requireElement("expression", tokens); + var callCmd = { + expr: expr, + args: [expr], + op: function (context, result) { + context.result = result; + return runtime.findNext(callCmd, context); + }, + }; + return callCmd; + }; + _parser.addCommand("call", function (parser, runtime, tokens) { + if (!tokens.matchToken("call")) return; + var call = parseCallOrGet(parser, runtime, tokens); + if (call.expr && call.expr.type !== "functionCall") { + parser.raiseParseError(tokens, "Must be a function invocation"); + } + return call; + }); + _parser.addCommand("get", function (parser, runtime, tokens) { + if (tokens.matchToken("get")) { + return parseCallOrGet(parser, runtime, tokens); + } + }); + + _parser.addCommand("make", function (parser, runtime, tokens) { + if (!tokens.matchToken("make")) return; + tokens.matchToken("a") || tokens.matchToken("an"); + + var expr = parser.requireElement("expression", tokens); + + var args = []; + if (expr.type !== "queryRef" && tokens.matchToken("from")) { + do { + args.push(parser.requireElement("expression", tokens)); + } while (tokens.matchOpToken(",")); + } + + if (tokens.matchToken("called")) { + var name = tokens.requireTokenType("IDENTIFIER").value; + } + + if (expr.type === "queryRef") + return { + op: function (ctx) { + var match, + tagname = "div", + id, + classes = []; + var re = /(?:(^|#|\.)([^#\. ]+))/g; + while ((match = re.exec(expr.css))) { + if (match[1] === "") tagname = match[2].trim(); + else if (match[1] === "#") id = match[2].trim(); + else classes.push(match[2].trim()); + } + + var result = document.createElement(tagname); + if (id !== undefined) result.id = id; + for (var i = 0; i < classes.length; i++) { + var cls = classes[i]; + result.classList.add(cls) + } + + ctx.result = result; + if (name) ctx[name] = result; + + return runtime.findNext(this, ctx); + }, + }; + else + return { + args: [expr, args], + op: function (ctx, expr, args) { + ctx.result = varargConstructor(expr, args); + if (name) ctx[name] = ctx.result; + + return runtime.findNext(this, ctx); + }, + }; + }); + + _parser.addGrammarElement("pseudoCommand", function (parser, runtime, tokens) { + var expr = parser.requireElement("primaryExpression", tokens); + if (expr.type !== "functionCall" && expr.root.type !== "symbol") { + parser.raiseParseError("Implicit function calls must start with a simple function", tokens); + } + // optional "on", "with", or "to" + if (!tokens.matchAnyToken("to", "on", "with") && parser.commandBoundary(tokens.currentToken())) { + var target = parser.requireElement("implicitMeTarget", tokens); + } else { + var target = parser.requireElement("expression", tokens); + } + var functionName = expr.root.name; + var functionArgs = expr.argExressions; + + /** @type {GrammarElement} */ + var pseudoCommand = { + type: "pseudoCommand", + expr: expr, + args: [target, functionArgs], + op: function (context, target, args) { + var func = target[functionName]; + if (func.hyperfunc) { + args.push(context); + } + var result = func.apply(target, args); + context.result = result; + return runtime.findNext(pseudoCommand, context); + }, + execute: function (context) { + return runtime.unifiedExec(this, context); + }, + }; + + return pseudoCommand; + }); + + /** + * @param {ParserObject} parser + * @param {RuntimeObject} runtime + * @param {TokensObject} tokens + * @param {*} target + * @param {*} value + * @returns + */ + var makeSetter = function (parser, runtime, tokens, target, value) { + var symbolWrite = target.type === "symbol"; + var attributeWrite = target.type === "attributeRef"; + if (!attributeWrite && !symbolWrite && target.root == null) { + parser.raiseParseError(tokens, "Can only put directly into symbols, not references"); + } + + var root = null; + var prop = null; + if (symbolWrite) { + // root is null + } else if (attributeWrite) { + root = parser.requireElement("implicitMeTarget", tokens); + var attribute = target; + } else { + prop = target.prop ? target.prop.value : null; + var attribute = target.attribute; + root = target.root; + } + + /** @type {GrammarElement} */ + var setCmd = { + target: target, + symbolWrite: symbolWrite, + value: value, + args: [root, value], + op: function (context, root, valueToSet) { + if (symbolWrite) { + runtime.setSymbol(target.name, context, target.symbolType, valueToSet); + } else { + runtime.forEach(root, function (elt) { + if (attribute) { + if (valueToSet == null) { + elt.removeAttribute(attribute.name); + } else { + elt.setAttribute(attribute.name, valueToSet); + } + } else { + elt[prop] = valueToSet; + } + }); + } + return runtime.findNext(this, context); + }, + }; + return setCmd; + }; + + _parser.addCommand("default", function (parser, runtime, tokens) { + if (!tokens.matchToken("default")) return; + var target = parser.requireElement("assignableExpression", tokens); + tokens.requireToken("to"); + + var value = parser.requireElement("expression", tokens); + + /** @type {GrammarElement} */ + var setter = makeSetter(parser, runtime, tokens, target, value); + var defaultCmd = { + target: target, + value: value, + setter: setter, + args: [target], + op: function (context, target) { + if (target) { + return runtime.findNext(this, context); + } else { + return setter; + } + }, + }; + setter.parent = defaultCmd; + return defaultCmd; + }); + + _parser.addCommand("set", function (parser, runtime, tokens) { + if (!tokens.matchToken("set")) return; + if (tokens.currentToken().type === "L_BRACE") { + var obj = parser.requireElement("objectLiteral", tokens); + tokens.requireToken("on"); + var target = parser.requireElement("expression", tokens); + + return { + objectLiteral: obj, + target: target, + args: [obj, target], + op: function (ctx, obj, target) { + mergeObjects(target, obj); + return runtime.findNext(this, ctx); + }, + }; + } + + try { + tokens.pushFollow("to"); + var target = parser.requireElement("assignableExpression", tokens); + } finally { + tokens.popFollow(); + } + tokens.requireToken("to"); + var value = parser.requireElement("expression", tokens); + return makeSetter(parser, runtime, tokens, target, value); + }); + + _parser.addCommand("if", function (parser, runtime, tokens) { + if (!tokens.matchToken("if")) return; + var expr = parser.requireElement("expression", tokens); + tokens.matchToken("then"); // optional 'then' + var trueBranch = parser.parseElement("commandList", tokens); + if (tokens.matchToken("else")) { + var falseBranch = parser.parseElement("commandList", tokens); + } + if (tokens.hasMore()) { + tokens.requireToken("end"); + } + + /** @type {GrammarElement} */ + var ifCmd = { + expr: expr, + trueBranch: trueBranch, + falseBranch: falseBranch, + args: [expr], + op: function (context, exprValue) { + if (exprValue) { + return trueBranch; + } else if (falseBranch) { + return falseBranch; + } else { + return runtime.findNext(this, context); + } + }, + }; + parser.setParent(trueBranch, ifCmd); + parser.setParent(falseBranch, ifCmd); + return ifCmd; + }); + + var parseRepeatExpression = function (parser, tokens, runtime, startedWithForToken) { + var innerStartToken = tokens.currentToken(); + if (tokens.matchToken("for") || startedWithForToken) { + var identifierToken = tokens.requireTokenType("IDENTIFIER"); + var identifier = identifierToken.value; + tokens.requireToken("in"); + var expression = parser.requireElement("expression", tokens); + } else if (tokens.matchToken("in")) { + var identifier = "it"; + var expression = parser.requireElement("expression", tokens); + } else if (tokens.matchToken("while")) { + var whileExpr = parser.requireElement("expression", tokens); + } else if (tokens.matchToken("until")) { + var isUntil = true; + if (tokens.matchToken("event")) { + var evt = _parser.requireElement("dotOrColonPath", tokens, "Expected event name"); + if (tokens.matchToken("from")) { + var on = parser.requireElement("expression", tokens); + } + } else { + var whileExpr = parser.requireElement("expression", tokens); + } + } else if (tokens.matchTokenType("NUMBER")) { + var times = parseFloat(innerStartToken.value); + tokens.requireToken("times"); + } else { + tokens.matchToken("forever"); // consume optional forever + var forever = true; + } + + if (tokens.matchToken("index")) { + var identifierToken = tokens.requireTokenType("IDENTIFIER"); + var indexIdentifier = identifierToken.value; + } + + var loop = parser.parseElement("commandList", tokens); + if (loop && evt) { + // if this is an event based loop, wait a tick at the end of the loop so that + // events have a chance to trigger in the loop condition o_O))) + var last = loop; + while (last.next) { + last = last.next; + } + var waitATick = { + type: "waitATick", + op: function () { + return new Promise(function (resolve) { + setTimeout(function () { + resolve(runtime.findNext(waitATick)); + }, 0); + }); + }, + }; + last.next = waitATick; + } + if (tokens.hasMore()) { + tokens.requireToken("end"); + } + + if (identifier == null) { + identifier = "_implicit_repeat_" + innerStartToken.start; + var slot = identifier; + } else { + var slot = identifier + "_" + innerStartToken.start; + } + + var repeatCmd = { + identifier: identifier, + indexIdentifier: indexIdentifier, + slot: slot, + expression: expression, + forever: forever, + times: times, + until: isUntil, + event: evt, + on: on, + whileExpr: whileExpr, + resolveNext: function () { + return this; + }, + loop: loop, + args: [whileExpr], + op: function (context, whileValue) { + var iterator = context.meta.iterators[slot]; + var keepLooping = false; + if (this.forever) { + keepLooping = true; + } else if (this.until) { + if (evt) { + keepLooping = context.meta.iterators[slot].eventFired === false; + } else { + keepLooping = whileValue !== true; + } + } else if (whileValue) { + keepLooping = true; + } else if (times) { + keepLooping = iterator.index < this.times; + } else { + keepLooping = iterator.value !== null && iterator.index < iterator.value.length; + } + + if (keepLooping) { + if (iterator.value) { + context[identifier] = iterator.value[iterator.index]; + context.result = iterator.value[iterator.index]; + } else { + context.result = iterator.index; + } + if (indexIdentifier) { + context[indexIdentifier] = iterator.index; + } + iterator.index++; + return loop; + } else { + context.meta.iterators[slot] = null; + return runtime.findNext(this.parent, context); + } + }, + }; + parser.setParent(loop, repeatCmd); + var repeatInit = { + name: "repeatInit", + args: [expression, evt, on], + op: function (context, value, event, on) { + context.meta.iterators[slot] = { + index: 0, + value: value, + eventFired: false, + }; + if (evt) { + var target = on || context.me; + target.addEventListener( + event, + function (e) { + context.meta.iterators[slot].eventFired = true; + }, + { once: true } + ); + } + return repeatCmd; // continue to loop + }, + execute: function (context) { + return runtime.unifiedExec(this, context); + }, + }; + parser.setParent(repeatCmd, repeatInit); + return repeatInit; + }; + + _parser.addCommand("repeat", function (parser, runtime, tokens) { + if (tokens.matchToken("repeat")) { + return parseRepeatExpression(parser, tokens, runtime, false); + } + }); + + _parser.addCommand("for", function (parser, runtime, tokens) { + if (tokens.matchToken("for")) { + return parseRepeatExpression(parser, tokens, runtime, true); + } + }); + + _parser.addGrammarElement("stringLike", function (parser, runtime, tokens) { + return _parser.parseAnyOf(["string", "nakedString"], tokens); + }); + + _parser.addCommand("append", function (parser, runtime, tokens) { + if (!tokens.matchToken("append")) return; + var target = null; + var prop = null; + + var value = parser.requireElement("expression", tokens); + + if (tokens.matchToken("to")) { + target = parser.requireElement("expression", tokens); + } + + if (target == null) { + prop = "result"; + } else if (target.type === "symbol") { + prop = target.name; + } else if (target.type === "propertyAccess") { + prop = target.prop.value; + } else { + throw "Unable to append to " + target.type; + } + + return { + value: value, + target: target, + args: [value], + op: function (context, value) { + if (Array.isArray(context[prop])) { + context[prop].push(value); + } else if (context[prop] instanceof Element) { + if (typeof value == "string") { + context[prop].innerHTML += value; + } else { + throw "Don't know how to append non-strings to an HTML Element yet."; + } + } else { + context[prop] += value; + } + + return runtime.findNext(this, context); + }, + execute: function (context) { + return runtime.unifiedExec(this, context, value, target); + }, + }; + }); + + _parser.addCommand("increment", function (parser, runtime, tokens) { + if (!tokens.matchToken("increment")) return; + var amount; + + // This is optional. Defaults to "result" + var target = parser.parseElement("assignableExpression", tokens); + + // This is optional. Defaults to 1. + if (tokens.matchToken("by")) { + amount = parser.requireElement("expression", tokens); + } + + return { + target: target, + args: [target, amount], + op: function (context, targetValue, amount) { + targetValue = targetValue ? parseFloat(targetValue) : 0; + amount = amount ? parseFloat(amount) : 1; + var newValue = targetValue + amount; + var setter = makeSetter(parser, runtime, tokens, target, newValue); + context.result = newValue; + setter.parent = this; + return setter; + }, + execute: function (context) { + return runtime.unifiedExec(this, context, target, amount); + }, + }; + }); + + _parser.addCommand("decrement", function (parser, runtime, tokens) { + if (!tokens.matchToken("decrement")) return; + var amount; + + // This is optional. Defaults to "result" + var target = parser.parseElement("assignableExpression", tokens); + + // This is optional. Defaults to 1. + if (tokens.matchToken("by")) { + amount = parser.requireElement("expression", tokens); + } + + return { + target: target, + args: [target, amount], + op: function (context, targetValue, amount) { + targetValue = targetValue ? parseFloat(targetValue) : 0; + amount = amount ? parseFloat(amount) : 1; + var newValue = targetValue - amount; + var setter = makeSetter(parser, runtime, tokens, target, newValue); + context.result = newValue; + setter.parent = this; + return setter; + }, + execute: function (context) { + return runtime.unifiedExec(this, context, target, amount); + }, + }; + }); + + _parser.addCommand("fetch", function (parser, runtime, tokens) { + if (!tokens.matchToken("fetch")) return; + var url = parser.requireElement("stringLike", tokens); + var args = parser.parseElement("objectLiteral", tokens); + + var type = "text"; + var conversion; + if (tokens.matchToken("as")) { + if (tokens.matchToken("json")) { + type = "json"; + } else if (tokens.matchToken("response")) { + type = "response"; + } else if (tokens.matchToken("html")) { + type = "html"; + } else if (tokens.matchToken("text")) { + // default, ignore + } else { + conversion = parser.requireElement("dotOrColonPath", tokens).evaluate(); + } + } + + /** @type {GrammarElement} */ + var fetchCmd = { + url: url, + argExpressions: args, + args: [url, args], + op: function (context, url, args) { + return fetch(url, args) + .then(function (resp) { + if (type === "response") { + context.result = resp; + return runtime.findNext(fetchCmd, context); + } + if (type === "json") { + return resp.json().then(function (result) { + context.result = result; + return runtime.findNext(fetchCmd, context); + }); + } + return resp.text().then(function (result) { + if (conversion) result = runtime.convertValue(result, conversion); + + if (type === "html") result = runtime.convertValue(result, "Fragment"); + + context.result = result; + return runtime.findNext(fetchCmd, context); + }); + }) + .catch(function (reason) { + runtime.triggerEvent(context.me, "fetch:error", { + reason: reason, + }); + throw reason; + }); + }, + }; + return fetchCmd; + }); + } + + //==================================================================== + // Initialization + //==================================================================== + function ready(fn) { + if (document.readyState !== "loading") { + setTimeout(fn); + } else { + document.addEventListener("DOMContentLoaded", fn); + } + } + + function getMetaConfig() { + var element = document.querySelector('meta[name="htmx-config"]'); + if (element) { + return parseJSON(element.content); + } else { + return null; + } + } + + function mergeMetaConfig() { + var metaConfig = getMetaConfig(); + if (metaConfig) { + _hyperscript.config = mergeObjects(_hyperscript.config, metaConfig); + } + } + + if ("document" in globalScope) { + Promise.all( + Array.from(document.querySelectorAll("script[type='text/hyperscript'][src]")).map(function (script) { + return fetch(script.src) + .then(function (res) { + return res.text(); + }) + .then(function (code) { + return _runtime.evaluate(code); + }); + }) + ).then(function () { + ready(function () { + mergeMetaConfig(); + _runtime.processNode(document.documentElement); + document.addEventListener("htmx:load", function (evt) { + _runtime.processNode(evt.detail.elt); + }); + }); + }); + } + + //==================================================================== + // API + //==================================================================== + return mergeObjects( + function (str, ctx) { + return _runtime.evaluate(str, ctx); //OK + }, + { + internals: { + lexer: _lexer, + parser: _parser, + runtime: _runtime, + }, + addFeature: function (keyword, definition) { + _parser.addFeature(keyword, definition); + }, + addCommand: function (keyword, definition) { + _parser.addCommand(keyword, definition); + }, + addLeafExpression: function (name, definition) { + _parser.addLeafExpression(name, definition); + }, + addIndirectExpression: function (name, definition) { + _parser.addIndirectExpression(name, definition); + }, + evaluate: function (str, ctx) { + //OK + return _runtime.evaluate(str, ctx); //OK + }, + parse: function (str) { + //OK + return _runtime.parse(str); //OK + }, + processNode: function (elt) { + _runtime.processNode(elt); + }, + config: { + attributes: "_, script, data-script", + defaultTransition: "all 500ms ease-in", + disableSelector: "[disable-scripting], [data-disable-scripting]", + conversions: CONVERSIONS, + }, + } + ); +}); + +///========================================================================= +/// This module provides the core web functionality for hyperscript +///========================================================================= +(function () { + function mergeObjects(obj1, obj2) { + for (var key in obj2) { + if (obj2.hasOwnProperty(key)) { + obj1[key] = obj2[key]; + } + } + return obj1; + } + + _hyperscript.addCommand("settle", function (parser, runtime, tokens) { + if (tokens.matchToken("settle")) { + if (!parser.commandBoundary(tokens.currentToken())) { + var on = parser.requireElement("expression", tokens); + } else { + var on = parser.requireElement("implicitMeTarget", tokens); + } + + var settleCommand = { + type: "settleCmd", + args: [on], + op: function (context, on) { + var resolve = null; + var resolved = false; + var transitionStarted = false; + + var promise = new Promise(function (r) { + resolve = r; + }); + + // listen for a transition begin + on.addEventListener( + "transitionstart", + function () { + transitionStarted = true; + }, + { once: true } + ); + + // if no transition begins in 500ms, cancel + setTimeout(function () { + if (!transitionStarted && !resolved) { + resolve(runtime.findNext(settleCommand, context)); + } + }, 500); + + // continue on a transition emd + on.addEventListener( + "transitionend", + function () { + if (!resolved) { + resolve(runtime.findNext(settleCommand, context)); + } + }, + { once: true } + ); + return promise; + }, + execute: function (context) { + return runtime.unifiedExec(this, context); + }, + }; + return settleCommand; + } + }); + + _hyperscript.addCommand("add", function (parser, runtime, tokens) { + if (tokens.matchToken("add")) { + var classRef = parser.parseElement("classRef", tokens); + var attributeRef = null; + var cssDeclaration = null; + if (classRef == null) { + attributeRef = parser.parseElement("attributeRef", tokens); + if (attributeRef == null) { + cssDeclaration = parser.parseElement("objectLiteral", tokens); + if (cssDeclaration == null) { + parser.raiseParseError(tokens, "Expected either a class reference or attribute expression"); + } + } + } else { + var classRefs = [classRef]; + while ((classRef = parser.parseElement("classRef", tokens))) { + classRefs.push(classRef); + } + } + + if (tokens.matchToken("to")) { + var to = parser.requireElement("expression", tokens); + } else { + var to = parser.parseElement("implicitMeTarget", tokens); + } + + if (classRefs) { + var addCmd = { + classRefs: classRefs, + to: to, + args: [to], + op: function (context, to) { + runtime.forEach(classRefs, function (classRef) { + runtime.forEach(to, function (target) { + if (target instanceof Element) target.classList.add(classRef.className()); + }); + }); + return runtime.findNext(this, context); + }, + }; + } else if (attributeRef) { + var addCmd = { + type: "addCmd", + attributeRef: attributeRef, + to: to, + args: [to], + op: function (context, to, attrRef) { + runtime.forEach(to, function (target) { + target.setAttribute(attributeRef.name, attributeRef.value); + }); + return runtime.findNext(addCmd, context); + }, + execute: function (ctx) { + return runtime.unifiedExec(this, ctx); + }, + }; + } else { + var addCmd = { + type: "addCmd", + cssDeclaration: cssDeclaration, + to: to, + args: [to, cssDeclaration], + op: function (context, to, css) { + runtime.forEach(to, function (target) { + for (var key in css) { + if (css.hasOwnProperty(key)) { + target.style.setProperty(key, css[key]); + } + } + }); + return runtime.findNext(addCmd, context); + }, + execute: function (ctx) { + return runtime.unifiedExec(this, ctx); + }, + }; + } + return addCmd; + } + }); + + _hyperscript.addCommand("remove", function (parser, runtime, tokens) { + if (tokens.matchToken("remove")) { + var classRef = parser.parseElement("classRef", tokens); + var attributeRef = null; + var elementExpr = null; + if (classRef == null) { + attributeRef = parser.parseElement("attributeRef", tokens); + if (attributeRef == null) { + elementExpr = parser.parseElement("expression", tokens); + if (elementExpr == null) { + parser.raiseParseError( + tokens, + "Expected either a class reference, attribute expression or value expression" + ); + } + } + } else { + var classRefs = [classRef]; + while ((classRef = parser.parseElement("classRef", tokens))) { + classRefs.push(classRef); + } + } + + if (tokens.matchToken("from")) { + var from = parser.requireElement("expression", tokens); + } else { + var from = parser.requireElement("implicitMeTarget", tokens); + } + + if (elementExpr) { + var removeCmd = { + elementExpr: elementExpr, + from: from, + args: [elementExpr], + op: function (context, element) { + runtime.forEach(element, function (target) { + if (target.parentElement) { + target.parentElement.removeChild(target); + } + }); + return runtime.findNext(this, context); + }, + }; + } else { + var removeCmd = { + classRefs: classRefs, + attributeRef: attributeRef, + elementExpr: elementExpr, + from: from, + args: [from], + op: function (context, from) { + if (this.classRefs) { + runtime.forEach(classRefs, function (classRef) { + runtime.forEach(from, function (target) { + target.classList.remove(classRef.className()); + }); + }); + } else { + runtime.forEach(from, function (target) { + target.removeAttribute(attributeRef.name); + }); + } + return runtime.findNext(this, context); + }, + }; + } + return removeCmd; + } + }); + + _hyperscript.addCommand("toggle", function (parser, runtime, tokens) { + if (tokens.matchToken("toggle")) { + if (tokens.matchToken("between")) { + var between = true; + var classRef = parser.parseElement("classRef", tokens); + tokens.requireToken("and"); + var classRef2 = parser.requireElement("classRef", tokens); + } else { + var classRef = parser.parseElement("classRef", tokens); + var attributeRef = null; + if (classRef == null) { + attributeRef = parser.parseElement("attributeRef", tokens); + if (attributeRef == null) { + parser.raiseParseError(tokens, "Expected either a class reference or attribute expression"); + } + } else { + var classRefs = [classRef]; + while ((classRef = parser.parseElement("classRef", tokens))) { + classRefs.push(classRef); + } + } + } + + if (tokens.matchToken("on")) { + var on = parser.requireElement("expression", tokens); + } else { + var on = parser.requireElement("implicitMeTarget", tokens); + } + + if (tokens.matchToken("for")) { + var time = parser.requireElement("timeExpression", tokens); + } else if (tokens.matchToken("until")) { + var evt = parser.requireElement("dotOrColonPath", tokens, "Expected event name"); + if (tokens.matchToken("from")) { + var from = parser.requireElement("expression", tokens); + } + } + + var toggleCmd = { + classRef: classRef, + classRef2: classRef2, + classRefs: classRefs, + attributeRef: attributeRef, + on: on, + time: time, + evt: evt, + from: from, + toggle: function (on) { + if (between) { + runtime.forEach(on, function (target) { + if (target.classList.contains(classRef.className())) { + target.classList.remove(classRef.className()); + target.classList.add(classRef2.className()); + } else { + target.classList.add(classRef.className()); + target.classList.remove(classRef2.className()); + } + }); + } else if (this.classRefs) { + runtime.forEach(this.classRefs, function (classRef) { + runtime.forEach(on, function (target) { + target.classList.toggle(classRef.className()); + }); + }); + } else { + runtime.forEach(on, function (target) { + if (target.hasAttribute(attributeRef.name)) { + target.removeAttribute(attributeRef.name); + } else { + target.setAttribute(attributeRef.name, attributeRef.value); + } + }); + } + }, + args: [on, time, evt, from], + op: function (context, on, time, evt, from) { + if (time) { + return new Promise(function (resolve) { + toggleCmd.toggle(on); + setTimeout(function () { + toggleCmd.toggle(on); + resolve(runtime.findNext(toggleCmd, context)); + }, time); + }); + } else if (evt) { + return new Promise(function (resolve) { + var target = from || context.me; + target.addEventListener( + evt, + function () { + toggleCmd.toggle(on); + resolve(runtime.findNext(toggleCmd, context)); + }, + { once: true } + ); + toggleCmd.toggle(on); + }); + } else { + this.toggle(on); + return runtime.findNext(toggleCmd, context); + } + }, + }; + return toggleCmd; + } + }); + + var HIDE_SHOW_STRATEGIES = { + display: function (op, element, arg) { + if (arg) { + element.style.display = arg; + } else if (op === "hide") { + element.style.display = "none"; + } else { + element.style.display = "block"; + } + }, + visibility: function (op, element, arg) { + if (arg) { + element.style.visibility = arg; + } else if (op === "hide") { + element.style.visibility = "hidden"; + } else { + element.style.visibility = "visible"; + } + }, + opacity: function (op, element, arg) { + if (arg) { + element.style.opacity = arg; + } else if (op === "hide") { + element.style.opacity = "0"; + } else { + element.style.opacity = "1"; + } + }, + }; + + var parseShowHideTarget = function (parser, runtime, tokens) { + var target; + var currentTokenValue = tokens.currentToken(); + if (currentTokenValue.value === "with" || parser.commandBoundary(currentTokenValue)) { + target = parser.parseElement("implicitMeTarget", tokens); + } else { + target = parser.parseElement("expression", tokens); + } + return target; + }; + + var resolveStrategy = function (parser, tokens, name) { + var configDefault = _hyperscript.config.defaultHideShowStrategy; + var strategies = HIDE_SHOW_STRATEGIES; + if (_hyperscript.config.hideShowStrategies) { + strategies = mergeObjects(strategies, _hyperscript.config.hideShowStrategies); // merge in user provided strategies + } + name = name || configDefault || "display"; + var value = strategies[name]; + if (value == null) { + parser.raiseParseError(tokens, "Unknown show/hide strategy : " + name); + } + return value; + }; + + _hyperscript.addCommand("hide", function (parser, runtime, tokens) { + if (tokens.matchToken("hide")) { + var target = parseShowHideTarget(parser, runtime, tokens); + + var name = null; + if (tokens.matchToken("with")) { + name = tokens.requireTokenType("IDENTIFIER").value; + } + var hideShowStrategy = resolveStrategy(parser, tokens, name); + + return { + target: target, + args: [target], + op: function (ctx, target) { + runtime.forEach(target, function (elt) { + hideShowStrategy("hide", elt); + }); + return runtime.findNext(this, ctx); + }, + }; + } + }); + + _hyperscript.addCommand("show", function (parser, runtime, tokens) { + if (tokens.matchToken("show")) { + var target = parseShowHideTarget(parser, runtime, tokens); + + var name = null; + if (tokens.matchToken("with")) { + name = tokens.requireTokenType("IDENTIFIER").value; + } + var arg = null; + if (tokens.matchOpToken(":")) { + var tokenArr = tokens.consumeUntilWhitespace(); + tokens.matchTokenType("WHITESPACE"); + arg = tokenArr + .map(function (t) { + return t.value; + }) + .join(""); + } + var hideShowStrategy = resolveStrategy(parser, tokens, name); + + return { + target: target, + args: [target], + op: function (ctx, target) { + runtime.forEach(target, function (elt) { + hideShowStrategy("show", elt, arg); + }); + return runtime.findNext(this, ctx); + }, + }; + } + }); + + _hyperscript.addCommand("trigger", function (parser, runtime, tokens) { + if (tokens.matchToken("trigger")) { + var eventName = parser.requireElement("eventName", tokens); + var details = parser.parseElement("namedArgumentList", tokens); + + var triggerCmd = { + eventName: eventName, + details: details, + args: [eventName, details], + op: function (context, eventNameStr, details) { + runtime.triggerEvent(context.me, eventNameStr, details ? details : {}); + return runtime.findNext(triggerCmd, context); + }, + }; + return triggerCmd; + } + }); + + _hyperscript.addCommand("take", function (parser, runtime, tokens) { + if (tokens.matchToken("take")) { + var classRef = parser.parseElement("classRef", tokens); + + if (tokens.matchToken("from")) { + var from = parser.requireElement("expression", tokens); + } else { + var from = classRef; + } + + if (tokens.matchToken("for")) { + var forElt = parser.requireElement("expression", tokens); + } else { + var forElt = parser.requireElement("implicitMeTarget", tokens); + } + + var takeCmd = { + classRef: classRef, + from: from, + forElt: forElt, + args: [from, forElt], + op: function (context, from, forElt) { + var clazz = this.classRef.css.substr(1); + runtime.forEach(from, function (target) { + target.classList.remove(clazz); + }); + runtime.forEach(forElt, function (target) { + target.classList.add(clazz); + }); + return runtime.findNext(this, context); + }, + }; + return takeCmd; + } + }); + + function putInto(context, prop, valueToPut) { + if (prop) { + var value = context[prop]; + } else { + var value = context; + } + if (value instanceof Element || value instanceof HTMLDocument) { + while (value.firstChild) value.removeChild(value.firstChild); + value.append(_hyperscript.internals.runtime.convertValue(valueToPut, "Fragment")); + } else { + if (prop) { + context[prop] = valueToPut; + } else { + throw "Don't know how to put a value into " + typeof context; + } + } + } + + _hyperscript.addCommand("put", function (parser, runtime, tokens) { + if (tokens.matchToken("put")) { + var value = parser.requireElement("expression", tokens); + + var operationToken = tokens.matchAnyToken("into", "before", "after"); + + if (operationToken == null && tokens.matchToken("at")) { + operationToken = tokens.matchAnyToken("start", "end"); + tokens.requireToken("of"); + } + + if (operationToken == null) { + parser.raiseParseError(tokens, "Expected one of 'into', 'before', 'at start of', 'at end of', 'after'"); + } + var target = parser.requireElement("expression", tokens); + + var operation = operationToken.value; + + var symbolWrite = false; + var root = null; + var prop = null; + if (target.type === "propertyAccess" && operation === "into") { + prop = target.prop.value; + root = target.root; + } else if (target.type === "symbol" && operation === "into") { + symbolWrite = true; + prop = target.name; + } else if (target.type === "attributeRef" && operation === "into") { + var attributeWrite = true; + prop = target.name; + root = parser.requireElement("implicitMeTarget", tokens); + } else if (target.type === "attributeRefAccess" && operation === "into") { + var attributeWrite = true; + prop = target.attribute.name; + root = target.root; + } else { + root = target; + } + + var putCmd = { + target: target, + operation: operation, + symbolWrite: symbolWrite, + value: value, + args: [root, value], + op: function (context, root, valueToPut) { + if (symbolWrite) { + putInto(context, prop, valueToPut); + } else { + if (operation === "into") { + if (attributeWrite) { + runtime.forEach(root, function (elt) { + elt.setAttribute(prop, valueToPut); + }); + } else { + runtime.forEach(root, function (elt) { + putInto(elt, prop, valueToPut); + }); + } + } else { + var op = + operation === "before" + ? Element.prototype.before + : operation === "after" + ? Element.prototype.after + : operation === "start" + ? Element.prototype.prepend + : operation === "end" + ? Element.prototype.append + : "unreachable"; + + runtime.forEach(root, function (elt) { + op.call( + elt, + valueToPut instanceof Node + ? valueToPut + : runtime.convertValue(valueToPut, "Fragment") + ); + }); + } + } + return runtime.findNext(this, context); + }, + }; + return putCmd; + } + }); + + function parsePseudopossessiveTarget(parser, runtime, tokens) { + if ( + tokens.matchToken("the") || + tokens.matchToken("element") || + tokens.matchToken("elements") || + tokens.currentToken().type === "CLASS_REF" || + tokens.currentToken().type === "ID_REF" || + (tokens.currentToken().op && tokens.currentToken().value === "<") + ) { + parser.possessivesDisabled = true; + try { + var targets = parser.parseElement("expression", tokens); + } finally { + delete parser.possessivesDisabled; + } + // optional possessive + if (tokens.matchOpToken("'")) { + tokens.requireToken("s"); + } + } else if (tokens.currentToken().type === "IDENTIFIER" && tokens.currentToken().value === "its") { + var identifier = tokens.matchToken("its"); + var targets = { + type: "pseudopossessiveIts", + token: identifier, + name: identifier.value, + evaluate: function (context) { + return runtime.resolveSymbol("it", context); + }, + }; + } else { + tokens.matchToken("my") || tokens.matchToken("me"); // consume optional 'my' + var targets = parser.parseElement("implicitMeTarget", tokens); + } + return targets; + } + + _hyperscript.addCommand("transition", function (parser, runtime, tokens) { + if (tokens.matchToken("transition")) { + var targets = parsePseudopossessiveTarget(parser, runtime, tokens); + + var properties = []; + var from = []; + var to = []; + var currentToken = tokens.currentToken(); + while ( + !parser.commandBoundary(currentToken) && + currentToken.value !== "over" && + currentToken.value !== "using" + ) { + properties.push(parser.requireElement("stringLike", tokens)); + + if (tokens.matchToken("from")) { + from.push(parser.requireElement("stringLike", tokens)); + } else { + from.push(null); + } + tokens.requireToken("to"); + to.push(parser.requireElement("stringLike", tokens)); + currentToken = tokens.currentToken(); + } + if (tokens.matchToken("over")) { + var over = parser.requireElement("timeExpression", tokens); + } else if (tokens.matchToken("using")) { + var using = parser.requireElement("expression", tokens); + } + + var transition = { + to: to, + args: [targets, properties, from, to, using, over], + op: function (context, targets, properties, from, to, using, over) { + var promises = []; + runtime.forEach(targets, function (target) { + var promise = new Promise(function (resolve, reject) { + var initialTransition = target.style.transition; + if (over) { + target.style.transition = "all " + over + "ms ease-in"; + } else if (using) { + target.style.transition = using; + } else { + target.style.transition = _hyperscript.config.defaultTransition; + } + var internalData = runtime.getInternalData(target); + var computedStyles = getComputedStyle(target); + + var initialStyles = {}; + for (var i = 0; i < computedStyles.length; i++) { + var name = computedStyles[i]; + var initialValue = computedStyles[name]; + initialStyles[name] = initialValue; + } + + // store intitial values + if (!internalData.initalStyles) { + internalData.initalStyles = initialStyles; + } + + for (var i = 0; i < properties.length; i++) { + var property = properties[i]; + var fromVal = from[i]; + if (fromVal == "computed" || fromVal == null) { + target.style[property] = initialStyles[property]; + } else { + target.style[property] = fromVal; + } + } + // console.log("transition started", transition); + setTimeout(function () { + var autoProps = []; + for (var i = 0; i < properties.length; i++) { + var property = properties[i]; + var toVal = to[i]; + if (toVal == "initial") { + var propertyValue = internalData.initalStyles[property]; + target.style[property] = propertyValue; + } else { + target.style[property] = toVal; + } + // console.log("set", property, "to", target.style[property], "on", target, "value passed in : ", toVal); + } + target.addEventListener( + "transitionend", + function () { + // console.log("transition ended", transition); + target.style.transition = initialTransition; + resolve(); + }, + { once: true } + ); + }, 5); + }); + promises.push(promise); + }); + return Promise.all(promises).then(function () { + return runtime.findNext(transition, context); + }); + }, + }; + return transition; + } + }); + + _hyperscript.addCommand("measure", function (parser, runtime, tokens) { + if (!tokens.matchToken("measure")) return; + + var target = parsePseudopossessiveTarget(parser, runtime, tokens); + + var propsToMeasure = []; + if (!parser.commandBoundary(tokens.currentToken())) + do { + propsToMeasure.push(tokens.matchTokenType("IDENTIFIER").value); + } while (tokens.matchOpToken(",")); + + return { + properties: propsToMeasure, + args: [target], + op: function (ctx, target) { + if (0 in target) target = target[0]; // not measuring multiple elts + var rect = target.getBoundingClientRect(); + var scroll = { + top: target.scrollTop, + left: target.scrollLeft, + topMax: target.scrollTopMax, + leftMax: target.scrollLeftMax, + height: target.scrollHeight, + width: target.scrollWidth, + }; + + ctx.result = { + x: rect.x, + y: rect.y, + left: rect.left, + top: rect.top, + right: rect.right, + bottom: rect.bottom, + width: rect.width, + height: rect.height, + bounds: rect, + + scrollLeft: scroll.left, + scrollTop: scroll.top, + scrollLeftMax: scroll.leftMax, + scrollTopMax: scroll.topMax, + scrollWidth: scroll.width, + scrollHeight: scroll.height, + scroll: scroll, + }; + + runtime.forEach(propsToMeasure, function (prop) { + if (prop in ctx.result) ctx[prop] = ctx.result[prop]; + else throw "No such measurement as " + prop; + }); + + return runtime.findNext(this, ctx); + }, + }; + }); + + _hyperscript.addLeafExpression("closestExpr", function (parser, runtime, tokens) { + if (tokens.matchToken("closest")) { + if (tokens.matchToken("parent")) { + var parentSearch = true; + } + + var css = null; + if (tokens.currentToken().type === "ATTRIBUTE_REF") { + var attributeRef = parser.parseElement("attributeRefAccess", tokens, null); + css = "[" + attributeRef.attribute.name + "]"; + } + + if (css == null) { + var expr = parser.parseElement("expression", tokens); + if (expr.css == null) { + parser.raiseParseError(tokens, "Expected a CSS expression"); + } else { + css = expr.css; + } + } + + if (tokens.matchToken("to")) { + var to = parser.parseElement("expression", tokens); + } else { + var to = parser.parseElement("implicitMeTarget", tokens); + } + + var closestExpr = { + type: "closestExpr", + parentSearch: parentSearch, + expr: expr, + css: css, + to: to, + args: [to], + op: function (ctx, to) { + if (to == null || !(to instanceof Element)) { + return null; + } else { + if (parentSearch) { + var node = to.parentElement ? to.parentElement.closest(css) : null; + } else { + var node = to.closest(css); + } + return node; + } + }, + evaluate: function (context) { + return runtime.unifiedEval(this, context); + }, + }; + + if (attributeRef) { + attributeRef.root = closestExpr; + attributeRef.args = [closestExpr]; + return attributeRef; + } else { + return closestExpr; + } + } + }); + + _hyperscript.addCommand("go", function (parser, runtime, tokens) { + if (tokens.matchToken("go")) { + if (tokens.matchToken("back")) { + var back = true; + } else { + tokens.matchToken("to"); + if (tokens.matchToken("url")) { + var target = parser.requireElement("stringLike", tokens); + var url = true; + if (tokens.matchToken("in")) { + tokens.requireToken("new"); + tokens.requireToken("window"); + var newWindow = true; + } + } else { + tokens.matchToken("the"); // optional the + var verticalPosition = tokens.matchAnyToken("top", "bottom", "middle"); + var horizontalPosition = tokens.matchAnyToken("left", "center", "right"); + if (verticalPosition || horizontalPosition) { + tokens.requireToken("of"); + } + var target = parser.requireElement("expression", tokens); + var smoothness = tokens.matchAnyToken("smoothly", "instantly"); + + var scrollOptions = {}; + if (verticalPosition) { + if (verticalPosition.value === "top") { + scrollOptions.block = "start"; + } else if (verticalPosition.value === "bottom") { + scrollOptions.block = "end"; + } else if (verticalPosition.value === "middle") { + scrollOptions.block = "center"; + } + } + + if (horizontalPosition) { + if (horizontalPosition.value === "left") { + scrollOptions.inline = "start"; + } else if (horizontalPosition.value === "center") { + scrollOptions.inline = "center"; + } else if (horizontalPosition.value === "right") { + scrollOptions.inline = "end"; + } + } + + if (smoothness) { + if (smoothness.value === "smoothly") { + scrollOptions.behavior = "smooth"; + } else if (smoothness.value === "instantly") { + scrollOptions.behavior = "instant"; + } + } + } + } + + var goCmd = { + target: target, + args: [target], + op: function (ctx, to) { + if (back) { + window.history.back(); + } else if (url) { + if (to) { + if (to.indexOf("#") === 0 && !newWindow) { + window.location.href = to; + } else { + window.open(to, newWindow ? "_blank" : null); + } + } + } else { + runtime.forEach(to, function (target) { + target.scrollIntoView(scrollOptions); + }); + } + return runtime.findNext(goCmd); + }, + }; + return goCmd; + } + }); + + _hyperscript.config.conversions["Values"] = function (/** @type {Node | NodeList} */ node) { + /** @type Object */ + var result = {}; + + var forEach = _hyperscript.internals.runtime.forEach; + + forEach(node, function (/** @type HTMLInputElement */ node) { + // Try to get a value directly from this node + var input = getInputInfo(node); + + if (input !== undefined) { + result[input.name] = input.value; + return; + } + + // Otherwise, try to query all child elements of this node that *should* contain values. + if (node.querySelectorAll != undefined) { + var children = node.querySelectorAll("input,select,textarea"); + forEach(children, appendValue); + } + }); + + return result; + + /** + * @param {HTMLInputElement} node + */ + function appendValue(node) { + var info = getInputInfo(node); + + if (info == undefined) { + return; + } + + // If there is no value already stored in this space. + if (result[info.name] == undefined) { + result[info.name] = info.value; + return; + } + + if (Array.isArray(result[info.name]) && Array.isArray(info.value)) { + result[info.name] = [].concat(result[info.name], info.value); + return; + } + } + + /** + * @param {HTMLInputElement} node + * @returns {{name:string, value:string | string[]} | undefined} + */ + function getInputInfo(node) { + try { + /** @type {{name: string, value: string | string[]}}*/ + var result = { + name: node.name, + value: node.value, + }; + + if (result.name == undefined || result.value == undefined) { + return undefined; + } + + if (node.type == "radio" && node.checked == false) { + return undefined; + } + + if (node.type == "checkbox") { + if (node.checked == false) { + result.value = undefined; + } else if (typeof result.value === "string") { + result.value = [result.value]; + } + } + + if (node.type == "select-multiple") { + /** @type {NodeListOf} */ + var selected = node.querySelectorAll("option[selected]"); + + result.value = []; + for (var index = 0; index < selected.length; index++) { + result.value.push(selected[index].value); + } + } + return result; + } catch (e) { + return undefined; + } + } + }; + + _hyperscript.config.conversions["HTML"] = function (value) { + var toHTML = /** @returns {string}*/ function (/** @type any*/ value) { + if (value instanceof Array) { + return value + .map(function (item) { + return toHTML(item); + }) + .join(""); + } + + if (value instanceof HTMLElement) { + return value.outerHTML; + } + + if (value instanceof NodeList) { + var result = ""; + for (var i = 0; i < value.length; i++) { + var node = value[i]; + if (node instanceof HTMLElement) { + result += node.outerHTML; + } + } + return result; + } + + if (value.toString) { + return value.toString(); + } + + return ""; + }; + + return toHTML(value); + }; + + _hyperscript.config.conversions["Fragment"] = function (val) { + var frag = document.createDocumentFragment(); + _hyperscript.internals.runtime.forEach(val, function (val) { + if (val instanceof Node) frag.append(val); + else { + var temp = document.createElement("template"); + temp.innerHTML = val; + frag.append(temp.content); + } + }); + return frag; + }; +})(); diff --git a/www/test/1.5.1/test/lib/handlebars-v4.7.6.js b/www/test/1.5.1/test/lib/handlebars-v4.7.6.js new file mode 100644 index 00000000..05d9f614 --- /dev/null +++ b/www/test/1.5.1/test/lib/handlebars-v4.7.6.js @@ -0,0 +1,5210 @@ +/**! + + @license + handlebars v4.7.6 + +Copyright (C) 2011-2019 by Yehuda Katz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["Handlebars"] = factory(); + else + root["Handlebars"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _handlebarsRuntime = __webpack_require__(2); + + var _handlebarsRuntime2 = _interopRequireDefault(_handlebarsRuntime); + + // Compiler imports + + var _handlebarsCompilerAst = __webpack_require__(45); + + var _handlebarsCompilerAst2 = _interopRequireDefault(_handlebarsCompilerAst); + + var _handlebarsCompilerBase = __webpack_require__(46); + + var _handlebarsCompilerCompiler = __webpack_require__(51); + + var _handlebarsCompilerJavascriptCompiler = __webpack_require__(52); + + var _handlebarsCompilerJavascriptCompiler2 = _interopRequireDefault(_handlebarsCompilerJavascriptCompiler); + + var _handlebarsCompilerVisitor = __webpack_require__(49); + + var _handlebarsCompilerVisitor2 = _interopRequireDefault(_handlebarsCompilerVisitor); + + var _handlebarsNoConflict = __webpack_require__(44); + + var _handlebarsNoConflict2 = _interopRequireDefault(_handlebarsNoConflict); + + var _create = _handlebarsRuntime2['default'].create; + function create() { + var hb = _create(); + + hb.compile = function (input, options) { + return _handlebarsCompilerCompiler.compile(input, options, hb); + }; + hb.precompile = function (input, options) { + return _handlebarsCompilerCompiler.precompile(input, options, hb); + }; + + hb.AST = _handlebarsCompilerAst2['default']; + hb.Compiler = _handlebarsCompilerCompiler.Compiler; + hb.JavaScriptCompiler = _handlebarsCompilerJavascriptCompiler2['default']; + hb.Parser = _handlebarsCompilerBase.parser; + hb.parse = _handlebarsCompilerBase.parse; + hb.parseWithoutProcessing = _handlebarsCompilerBase.parseWithoutProcessing; + + return hb; + } + + var inst = create(); + inst.create = create; + + _handlebarsNoConflict2['default'](inst); + + inst.Visitor = _handlebarsCompilerVisitor2['default']; + + inst['default'] = inst; + + exports['default'] = inst; + module.exports = exports['default']; + +/***/ }), +/* 1 */ +/***/ (function(module, exports) { + + "use strict"; + + exports["default"] = function (obj) { + return obj && obj.__esModule ? obj : { + "default": obj + }; + }; + + exports.__esModule = true; + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireWildcard = __webpack_require__(3)['default']; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _handlebarsBase = __webpack_require__(4); + + var base = _interopRequireWildcard(_handlebarsBase); + + // Each of these augment the Handlebars object. No need to setup here. + // (This is done to easily share code between commonjs and browse envs) + + var _handlebarsSafeString = __webpack_require__(37); + + var _handlebarsSafeString2 = _interopRequireDefault(_handlebarsSafeString); + + var _handlebarsException = __webpack_require__(6); + + var _handlebarsException2 = _interopRequireDefault(_handlebarsException); + + var _handlebarsUtils = __webpack_require__(5); + + var Utils = _interopRequireWildcard(_handlebarsUtils); + + var _handlebarsRuntime = __webpack_require__(38); + + var runtime = _interopRequireWildcard(_handlebarsRuntime); + + var _handlebarsNoConflict = __webpack_require__(44); + + var _handlebarsNoConflict2 = _interopRequireDefault(_handlebarsNoConflict); + + // For compatibility and usage outside of module systems, make the Handlebars object a namespace + function create() { + var hb = new base.HandlebarsEnvironment(); + + Utils.extend(hb, base); + hb.SafeString = _handlebarsSafeString2['default']; + hb.Exception = _handlebarsException2['default']; + hb.Utils = Utils; + hb.escapeExpression = Utils.escapeExpression; + + hb.VM = runtime; + hb.template = function (spec) { + return runtime.template(spec, hb); + }; + + return hb; + } + + var inst = create(); + inst.create = create; + + _handlebarsNoConflict2['default'](inst); + + inst['default'] = inst; + + exports['default'] = inst; + module.exports = exports['default']; + +/***/ }), +/* 3 */ +/***/ (function(module, exports) { + + "use strict"; + + exports["default"] = function (obj) { + if (obj && obj.__esModule) { + return obj; + } else { + var newObj = {}; + + if (obj != null) { + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; + } + } + + newObj["default"] = obj; + return newObj; + } + }; + + exports.__esModule = true; + +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.HandlebarsEnvironment = HandlebarsEnvironment; + + var _utils = __webpack_require__(5); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + var _helpers = __webpack_require__(10); + + var _decorators = __webpack_require__(30); + + var _logger = __webpack_require__(32); + + var _logger2 = _interopRequireDefault(_logger); + + var _internalProtoAccess = __webpack_require__(33); + + var VERSION = '4.7.6'; + exports.VERSION = VERSION; + var COMPILER_REVISION = 8; + exports.COMPILER_REVISION = COMPILER_REVISION; + var LAST_COMPATIBLE_COMPILER_REVISION = 7; + + exports.LAST_COMPATIBLE_COMPILER_REVISION = LAST_COMPATIBLE_COMPILER_REVISION; + var REVISION_CHANGES = { + 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it + 2: '== 1.0.0-rc.3', + 3: '== 1.0.0-rc.4', + 4: '== 1.x.x', + 5: '== 2.0.0-alpha.x', + 6: '>= 2.0.0-beta.1', + 7: '>= 4.0.0 <4.3.0', + 8: '>= 4.3.0' + }; + + exports.REVISION_CHANGES = REVISION_CHANGES; + var objectType = '[object Object]'; + + function HandlebarsEnvironment(helpers, partials, decorators) { + this.helpers = helpers || {}; + this.partials = partials || {}; + this.decorators = decorators || {}; + + _helpers.registerDefaultHelpers(this); + _decorators.registerDefaultDecorators(this); + } + + HandlebarsEnvironment.prototype = { + constructor: HandlebarsEnvironment, + + logger: _logger2['default'], + log: _logger2['default'].log, + + registerHelper: function registerHelper(name, fn) { + if (_utils.toString.call(name) === objectType) { + if (fn) { + throw new _exception2['default']('Arg not supported with multiple helpers'); + } + _utils.extend(this.helpers, name); + } else { + this.helpers[name] = fn; + } + }, + unregisterHelper: function unregisterHelper(name) { + delete this.helpers[name]; + }, + + registerPartial: function registerPartial(name, partial) { + if (_utils.toString.call(name) === objectType) { + _utils.extend(this.partials, name); + } else { + if (typeof partial === 'undefined') { + throw new _exception2['default']('Attempting to register a partial called "' + name + '" as undefined'); + } + this.partials[name] = partial; + } + }, + unregisterPartial: function unregisterPartial(name) { + delete this.partials[name]; + }, + + registerDecorator: function registerDecorator(name, fn) { + if (_utils.toString.call(name) === objectType) { + if (fn) { + throw new _exception2['default']('Arg not supported with multiple decorators'); + } + _utils.extend(this.decorators, name); + } else { + this.decorators[name] = fn; + } + }, + unregisterDecorator: function unregisterDecorator(name) { + delete this.decorators[name]; + }, + /** + * Reset the memory of illegal property accesses that have already been logged. + * @deprecated should only be used in handlebars test-cases + */ + resetLoggedPropertyAccesses: function resetLoggedPropertyAccesses() { + _internalProtoAccess.resetLoggedProperties(); + } + }; + + var log = _logger2['default'].log; + + exports.log = log; + exports.createFrame = _utils.createFrame; + exports.logger = _logger2['default']; + +/***/ }), +/* 5 */ +/***/ (function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + exports.extend = extend; + exports.indexOf = indexOf; + exports.escapeExpression = escapeExpression; + exports.isEmpty = isEmpty; + exports.createFrame = createFrame; + exports.blockParams = blockParams; + exports.appendContextPath = appendContextPath; + var escape = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`', + '=': '=' + }; + + var badChars = /[&<>"'`=]/g, + possible = /[&<>"'`=]/; + + function escapeChar(chr) { + return escape[chr]; + } + + function extend(obj /* , ...source */) { + for (var i = 1; i < arguments.length; i++) { + for (var key in arguments[i]) { + if (Object.prototype.hasOwnProperty.call(arguments[i], key)) { + obj[key] = arguments[i][key]; + } + } + } + + return obj; + } + + var toString = Object.prototype.toString; + + exports.toString = toString; + // Sourced from lodash + // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt + /* eslint-disable func-style */ + var isFunction = function isFunction(value) { + return typeof value === 'function'; + }; + // fallback for older versions of Chrome and Safari + /* istanbul ignore next */ + if (isFunction(/x/)) { + exports.isFunction = isFunction = function (value) { + return typeof value === 'function' && toString.call(value) === '[object Function]'; + }; + } + exports.isFunction = isFunction; + + /* eslint-enable func-style */ + + /* istanbul ignore next */ + var isArray = Array.isArray || function (value) { + return value && typeof value === 'object' ? toString.call(value) === '[object Array]' : false; + }; + + exports.isArray = isArray; + // Older IE versions do not directly support indexOf so we must implement our own, sadly. + + function indexOf(array, value) { + for (var i = 0, len = array.length; i < len; i++) { + if (array[i] === value) { + return i; + } + } + return -1; + } + + function escapeExpression(string) { + if (typeof string !== 'string') { + // don't escape SafeStrings, since they're already safe + if (string && string.toHTML) { + return string.toHTML(); + } else if (string == null) { + return ''; + } else if (!string) { + return string + ''; + } + + // Force a string conversion as this will be done by the append regardless and + // the regex test will do this transparently behind the scenes, causing issues if + // an object's to string has escaped characters in it. + string = '' + string; + } + + if (!possible.test(string)) { + return string; + } + return string.replace(badChars, escapeChar); + } + + function isEmpty(value) { + if (!value && value !== 0) { + return true; + } else if (isArray(value) && value.length === 0) { + return true; + } else { + return false; + } + } + + function createFrame(object) { + var frame = extend({}, object); + frame._parent = object; + return frame; + } + + function blockParams(params, ids) { + params.path = ids; + return params; + } + + function appendContextPath(contextPath, id) { + return (contextPath ? contextPath + '.' : '') + id; + } + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _Object$defineProperty = __webpack_require__(7)['default']; + + exports.__esModule = true; + var errorProps = ['description', 'fileName', 'lineNumber', 'endLineNumber', 'message', 'name', 'number', 'stack']; + + function Exception(message, node) { + var loc = node && node.loc, + line = undefined, + endLineNumber = undefined, + column = undefined, + endColumn = undefined; + + if (loc) { + line = loc.start.line; + endLineNumber = loc.end.line; + column = loc.start.column; + endColumn = loc.end.column; + + message += ' - ' + line + ':' + column; + } + + var tmp = Error.prototype.constructor.call(this, message); + + // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. + for (var idx = 0; idx < errorProps.length; idx++) { + this[errorProps[idx]] = tmp[errorProps[idx]]; + } + + /* istanbul ignore else */ + if (Error.captureStackTrace) { + Error.captureStackTrace(this, Exception); + } + + try { + if (loc) { + this.lineNumber = line; + this.endLineNumber = endLineNumber; + + // Work around issue under safari where we can't directly set the column value + /* istanbul ignore next */ + if (_Object$defineProperty) { + Object.defineProperty(this, 'column', { + value: column, + enumerable: true + }); + Object.defineProperty(this, 'endColumn', { + value: endColumn, + enumerable: true + }); + } else { + this.column = column; + this.endColumn = endColumn; + } + } + } catch (nop) { + /* Ignore if the browser is very particular */ + } + } + + Exception.prototype = new Error(); + + exports['default'] = Exception; + module.exports = exports['default']; + +/***/ }), +/* 7 */ +/***/ (function(module, exports, __webpack_require__) { + + module.exports = { "default": __webpack_require__(8), __esModule: true }; + +/***/ }), +/* 8 */ +/***/ (function(module, exports, __webpack_require__) { + + var $ = __webpack_require__(9); + module.exports = function defineProperty(it, key, desc){ + return $.setDesc(it, key, desc); + }; + +/***/ }), +/* 9 */ +/***/ (function(module, exports) { + + var $Object = Object; + module.exports = { + create: $Object.create, + getProto: $Object.getPrototypeOf, + isEnum: {}.propertyIsEnumerable, + getDesc: $Object.getOwnPropertyDescriptor, + setDesc: $Object.defineProperty, + setDescs: $Object.defineProperties, + getKeys: $Object.keys, + getNames: $Object.getOwnPropertyNames, + getSymbols: $Object.getOwnPropertySymbols, + each: [].forEach + }; + +/***/ }), +/* 10 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.registerDefaultHelpers = registerDefaultHelpers; + exports.moveHelperToHooks = moveHelperToHooks; + + var _helpersBlockHelperMissing = __webpack_require__(11); + + var _helpersBlockHelperMissing2 = _interopRequireDefault(_helpersBlockHelperMissing); + + var _helpersEach = __webpack_require__(12); + + var _helpersEach2 = _interopRequireDefault(_helpersEach); + + var _helpersHelperMissing = __webpack_require__(25); + + var _helpersHelperMissing2 = _interopRequireDefault(_helpersHelperMissing); + + var _helpersIf = __webpack_require__(26); + + var _helpersIf2 = _interopRequireDefault(_helpersIf); + + var _helpersLog = __webpack_require__(27); + + var _helpersLog2 = _interopRequireDefault(_helpersLog); + + var _helpersLookup = __webpack_require__(28); + + var _helpersLookup2 = _interopRequireDefault(_helpersLookup); + + var _helpersWith = __webpack_require__(29); + + var _helpersWith2 = _interopRequireDefault(_helpersWith); + + function registerDefaultHelpers(instance) { + _helpersBlockHelperMissing2['default'](instance); + _helpersEach2['default'](instance); + _helpersHelperMissing2['default'](instance); + _helpersIf2['default'](instance); + _helpersLog2['default'](instance); + _helpersLookup2['default'](instance); + _helpersWith2['default'](instance); + } + + function moveHelperToHooks(instance, helperName, keepHelper) { + if (instance.helpers[helperName]) { + instance.hooks[helperName] = instance.helpers[helperName]; + if (!keepHelper) { + delete instance.helpers[helperName]; + } + } + } + +/***/ }), +/* 11 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + exports['default'] = function (instance) { + instance.registerHelper('blockHelperMissing', function (context, options) { + var inverse = options.inverse, + fn = options.fn; + + if (context === true) { + return fn(this); + } else if (context === false || context == null) { + return inverse(this); + } else if (_utils.isArray(context)) { + if (context.length > 0) { + if (options.ids) { + options.ids = [options.name]; + } + + return instance.helpers.each(context, options); + } else { + return inverse(this); + } + } else { + if (options.data && options.ids) { + var data = _utils.createFrame(options.data); + data.contextPath = _utils.appendContextPath(options.data.contextPath, options.name); + options = { data: data }; + } + + return fn(context, options); + } + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 12 */ +/***/ (function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {'use strict'; + + var _Object$keys = __webpack_require__(13)['default']; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + exports['default'] = function (instance) { + instance.registerHelper('each', function (context, options) { + if (!options) { + throw new _exception2['default']('Must pass iterator to #each'); + } + + var fn = options.fn, + inverse = options.inverse, + i = 0, + ret = '', + data = undefined, + contextPath = undefined; + + if (options.data && options.ids) { + contextPath = _utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.'; + } + + if (_utils.isFunction(context)) { + context = context.call(this); + } + + if (options.data) { + data = _utils.createFrame(options.data); + } + + function execIteration(field, index, last) { + if (data) { + data.key = field; + data.index = index; + data.first = index === 0; + data.last = !!last; + + if (contextPath) { + data.contextPath = contextPath + field; + } + } + + ret = ret + fn(context[field], { + data: data, + blockParams: _utils.blockParams([context[field], field], [contextPath + field, null]) + }); + } + + if (context && typeof context === 'object') { + if (_utils.isArray(context)) { + for (var j = context.length; i < j; i++) { + if (i in context) { + execIteration(i, i, i === context.length - 1); + } + } + } else if (global.Symbol && context[global.Symbol.iterator]) { + var newContext = []; + var iterator = context[global.Symbol.iterator](); + for (var it = iterator.next(); !it.done; it = iterator.next()) { + newContext.push(it.value); + } + context = newContext; + for (var j = context.length; i < j; i++) { + execIteration(i, i, i === context.length - 1); + } + } else { + (function () { + var priorKey = undefined; + + _Object$keys(context).forEach(function (key) { + // We're running the iterations one step out of sync so we can detect + // the last iteration without have to scan the object twice and create + // an itermediate keys array. + if (priorKey !== undefined) { + execIteration(priorKey, i - 1); + } + priorKey = key; + i++; + }); + if (priorKey !== undefined) { + execIteration(priorKey, i - 1, true); + } + })(); + } + } + + if (i === 0) { + ret = inverse(this); + } + + return ret; + }); + }; + + module.exports = exports['default']; + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }), +/* 13 */ +/***/ (function(module, exports, __webpack_require__) { + + module.exports = { "default": __webpack_require__(14), __esModule: true }; + +/***/ }), +/* 14 */ +/***/ (function(module, exports, __webpack_require__) { + + __webpack_require__(15); + module.exports = __webpack_require__(21).Object.keys; + +/***/ }), +/* 15 */ +/***/ (function(module, exports, __webpack_require__) { + + // 19.1.2.14 Object.keys(O) + var toObject = __webpack_require__(16); + + __webpack_require__(18)('keys', function($keys){ + return function keys(it){ + return $keys(toObject(it)); + }; + }); + +/***/ }), +/* 16 */ +/***/ (function(module, exports, __webpack_require__) { + + // 7.1.13 ToObject(argument) + var defined = __webpack_require__(17); + module.exports = function(it){ + return Object(defined(it)); + }; + +/***/ }), +/* 17 */ +/***/ (function(module, exports) { + + // 7.2.1 RequireObjectCoercible(argument) + module.exports = function(it){ + if(it == undefined)throw TypeError("Can't call method on " + it); + return it; + }; + +/***/ }), +/* 18 */ +/***/ (function(module, exports, __webpack_require__) { + + // most Object methods by ES6 should accept primitives + var $export = __webpack_require__(19) + , core = __webpack_require__(21) + , fails = __webpack_require__(24); + module.exports = function(KEY, exec){ + var fn = (core.Object || {})[KEY] || Object[KEY] + , exp = {}; + exp[KEY] = exec(fn); + $export($export.S + $export.F * fails(function(){ fn(1); }), 'Object', exp); + }; + +/***/ }), +/* 19 */ +/***/ (function(module, exports, __webpack_require__) { + + var global = __webpack_require__(20) + , core = __webpack_require__(21) + , ctx = __webpack_require__(22) + , PROTOTYPE = 'prototype'; + + var $export = function(type, name, source){ + var IS_FORCED = type & $export.F + , IS_GLOBAL = type & $export.G + , IS_STATIC = type & $export.S + , IS_PROTO = type & $export.P + , IS_BIND = type & $export.B + , IS_WRAP = type & $export.W + , exports = IS_GLOBAL ? core : core[name] || (core[name] = {}) + , target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE] + , key, own, out; + if(IS_GLOBAL)source = name; + for(key in source){ + // contains in native + own = !IS_FORCED && target && key in target; + if(own && key in exports)continue; + // export native or passed + out = own ? target[key] : source[key]; + // prevent global pollution for namespaces + exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key] + // bind timers to global for call from export context + : IS_BIND && own ? ctx(out, global) + // wrap global constructors for prevent change them in library + : IS_WRAP && target[key] == out ? (function(C){ + var F = function(param){ + return this instanceof C ? new C(param) : C(param); + }; + F[PROTOTYPE] = C[PROTOTYPE]; + return F; + // make static versions for prototype methods + })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out; + if(IS_PROTO)(exports[PROTOTYPE] || (exports[PROTOTYPE] = {}))[key] = out; + } + }; + // type bitmap + $export.F = 1; // forced + $export.G = 2; // global + $export.S = 4; // static + $export.P = 8; // proto + $export.B = 16; // bind + $export.W = 32; // wrap + module.exports = $export; + +/***/ }), +/* 20 */ +/***/ (function(module, exports) { + + // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 + var global = module.exports = typeof window != 'undefined' && window.Math == Math + ? window : typeof self != 'undefined' && self.Math == Math ? self : Function('return this')(); + if(typeof __g == 'number')__g = global; // eslint-disable-line no-undef + +/***/ }), +/* 21 */ +/***/ (function(module, exports) { + + var core = module.exports = {version: '1.2.6'}; + if(typeof __e == 'number')__e = core; // eslint-disable-line no-undef + +/***/ }), +/* 22 */ +/***/ (function(module, exports, __webpack_require__) { + + // optional / simple context binding + var aFunction = __webpack_require__(23); + module.exports = function(fn, that, length){ + aFunction(fn); + if(that === undefined)return fn; + switch(length){ + case 1: return function(a){ + return fn.call(that, a); + }; + case 2: return function(a, b){ + return fn.call(that, a, b); + }; + case 3: return function(a, b, c){ + return fn.call(that, a, b, c); + }; + } + return function(/* ...args */){ + return fn.apply(that, arguments); + }; + }; + +/***/ }), +/* 23 */ +/***/ (function(module, exports) { + + module.exports = function(it){ + if(typeof it != 'function')throw TypeError(it + ' is not a function!'); + return it; + }; + +/***/ }), +/* 24 */ +/***/ (function(module, exports) { + + module.exports = function(exec){ + try { + return !!exec(); + } catch(e){ + return true; + } + }; + +/***/ }), +/* 25 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + exports['default'] = function (instance) { + instance.registerHelper('helperMissing', function () /* [args, ]options */{ + if (arguments.length === 1) { + // A missing field in a {{foo}} construct. + return undefined; + } else { + // Someone is actually trying to call something, blow up. + throw new _exception2['default']('Missing helper: "' + arguments[arguments.length - 1].name + '"'); + } + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 26 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + exports['default'] = function (instance) { + instance.registerHelper('if', function (conditional, options) { + if (arguments.length != 2) { + throw new _exception2['default']('#if requires exactly one argument'); + } + if (_utils.isFunction(conditional)) { + conditional = conditional.call(this); + } + + // Default behavior is to render the positive path if the value is truthy and not empty. + // The `includeZero` option may be set to treat the condtional as purely not empty based on the + // behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative. + if (!options.hash.includeZero && !conditional || _utils.isEmpty(conditional)) { + return options.inverse(this); + } else { + return options.fn(this); + } + }); + + instance.registerHelper('unless', function (conditional, options) { + if (arguments.length != 2) { + throw new _exception2['default']('#unless requires exactly one argument'); + } + return instance.helpers['if'].call(this, conditional, { + fn: options.inverse, + inverse: options.fn, + hash: options.hash + }); + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 27 */ +/***/ (function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + + exports['default'] = function (instance) { + instance.registerHelper('log', function () /* message, options */{ + var args = [undefined], + options = arguments[arguments.length - 1]; + for (var i = 0; i < arguments.length - 1; i++) { + args.push(arguments[i]); + } + + var level = 1; + if (options.hash.level != null) { + level = options.hash.level; + } else if (options.data && options.data.level != null) { + level = options.data.level; + } + args[0] = level; + + instance.log.apply(instance, args); + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 28 */ +/***/ (function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + + exports['default'] = function (instance) { + instance.registerHelper('lookup', function (obj, field, options) { + if (!obj) { + // Note for 5.0: Change to "obj == null" in 5.0 + return obj; + } + return options.lookupProperty(obj, field); + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 29 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + exports['default'] = function (instance) { + instance.registerHelper('with', function (context, options) { + if (arguments.length != 2) { + throw new _exception2['default']('#with requires exactly one argument'); + } + if (_utils.isFunction(context)) { + context = context.call(this); + } + + var fn = options.fn; + + if (!_utils.isEmpty(context)) { + var data = options.data; + if (options.data && options.ids) { + data = _utils.createFrame(options.data); + data.contextPath = _utils.appendContextPath(options.data.contextPath, options.ids[0]); + } + + return fn(context, { + data: data, + blockParams: _utils.blockParams([context], [data && data.contextPath]) + }); + } else { + return options.inverse(this); + } + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 30 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.registerDefaultDecorators = registerDefaultDecorators; + + var _decoratorsInline = __webpack_require__(31); + + var _decoratorsInline2 = _interopRequireDefault(_decoratorsInline); + + function registerDefaultDecorators(instance) { + _decoratorsInline2['default'](instance); + } + +/***/ }), +/* 31 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + exports['default'] = function (instance) { + instance.registerDecorator('inline', function (fn, props, container, options) { + var ret = fn; + if (!props.partials) { + props.partials = {}; + ret = function (context, options) { + // Create a new partials stack frame prior to exec. + var original = container.partials; + container.partials = _utils.extend({}, original, props.partials); + var ret = fn(context, options); + container.partials = original; + return ret; + }; + } + + props.partials[options.args[0]] = options.fn; + + return ret; + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 32 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + var logger = { + methodMap: ['debug', 'info', 'warn', 'error'], + level: 'info', + + // Maps a given level value to the `methodMap` indexes above. + lookupLevel: function lookupLevel(level) { + if (typeof level === 'string') { + var levelMap = _utils.indexOf(logger.methodMap, level.toLowerCase()); + if (levelMap >= 0) { + level = levelMap; + } else { + level = parseInt(level, 10); + } + } + + return level; + }, + + // Can be overridden in the host environment + log: function log(level) { + level = logger.lookupLevel(level); + + if (typeof console !== 'undefined' && logger.lookupLevel(logger.level) <= level) { + var method = logger.methodMap[level]; + // eslint-disable-next-line no-console + if (!console[method]) { + method = 'log'; + } + + for (var _len = arguments.length, message = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + message[_key - 1] = arguments[_key]; + } + + console[method].apply(console, message); // eslint-disable-line no-console + } + } + }; + + exports['default'] = logger; + module.exports = exports['default']; + +/***/ }), +/* 33 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _Object$create = __webpack_require__(34)['default']; + + var _Object$keys = __webpack_require__(13)['default']; + + var _interopRequireWildcard = __webpack_require__(3)['default']; + + exports.__esModule = true; + exports.createProtoAccessControl = createProtoAccessControl; + exports.resultIsAllowed = resultIsAllowed; + exports.resetLoggedProperties = resetLoggedProperties; + + var _createNewLookupObject = __webpack_require__(36); + + var _logger = __webpack_require__(32); + + var logger = _interopRequireWildcard(_logger); + + var loggedProperties = _Object$create(null); + + function createProtoAccessControl(runtimeOptions) { + var defaultMethodWhiteList = _Object$create(null); + defaultMethodWhiteList['constructor'] = false; + defaultMethodWhiteList['__defineGetter__'] = false; + defaultMethodWhiteList['__defineSetter__'] = false; + defaultMethodWhiteList['__lookupGetter__'] = false; + + var defaultPropertyWhiteList = _Object$create(null); + // eslint-disable-next-line no-proto + defaultPropertyWhiteList['__proto__'] = false; + + return { + properties: { + whitelist: _createNewLookupObject.createNewLookupObject(defaultPropertyWhiteList, runtimeOptions.allowedProtoProperties), + defaultValue: runtimeOptions.allowProtoPropertiesByDefault + }, + methods: { + whitelist: _createNewLookupObject.createNewLookupObject(defaultMethodWhiteList, runtimeOptions.allowedProtoMethods), + defaultValue: runtimeOptions.allowProtoMethodsByDefault + } + }; + } + + function resultIsAllowed(result, protoAccessControl, propertyName) { + if (typeof result === 'function') { + return checkWhiteList(protoAccessControl.methods, propertyName); + } else { + return checkWhiteList(protoAccessControl.properties, propertyName); + } + } + + function checkWhiteList(protoAccessControlForType, propertyName) { + if (protoAccessControlForType.whitelist[propertyName] !== undefined) { + return protoAccessControlForType.whitelist[propertyName] === true; + } + if (protoAccessControlForType.defaultValue !== undefined) { + return protoAccessControlForType.defaultValue; + } + logUnexpecedPropertyAccessOnce(propertyName); + return false; + } + + function logUnexpecedPropertyAccessOnce(propertyName) { + if (loggedProperties[propertyName] !== true) { + loggedProperties[propertyName] = true; + logger.log('error', 'Handlebars: Access has been denied to resolve the property "' + propertyName + '" because it is not an "own property" of its parent.\n' + 'You can add a runtime option to disable the check or this warning:\n' + 'See https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access for details'); + } + } + + function resetLoggedProperties() { + _Object$keys(loggedProperties).forEach(function (propertyName) { + delete loggedProperties[propertyName]; + }); + } + +/***/ }), +/* 34 */ +/***/ (function(module, exports, __webpack_require__) { + + module.exports = { "default": __webpack_require__(35), __esModule: true }; + +/***/ }), +/* 35 */ +/***/ (function(module, exports, __webpack_require__) { + + var $ = __webpack_require__(9); + module.exports = function create(P, D){ + return $.create(P, D); + }; + +/***/ }), +/* 36 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _Object$create = __webpack_require__(34)['default']; + + exports.__esModule = true; + exports.createNewLookupObject = createNewLookupObject; + + var _utils = __webpack_require__(5); + + /** + * Create a new object with "null"-prototype to avoid truthy results on prototype properties. + * The resulting object can be used with "object[property]" to check if a property exists + * @param {...object} sources a varargs parameter of source objects that will be merged + * @returns {object} + */ + + function createNewLookupObject() { + for (var _len = arguments.length, sources = Array(_len), _key = 0; _key < _len; _key++) { + sources[_key] = arguments[_key]; + } + + return _utils.extend.apply(undefined, [_Object$create(null)].concat(sources)); + } + +/***/ }), +/* 37 */ +/***/ (function(module, exports) { + + // Build out our basic SafeString type + 'use strict'; + + exports.__esModule = true; + function SafeString(string) { + this.string = string; + } + + SafeString.prototype.toString = SafeString.prototype.toHTML = function () { + return '' + this.string; + }; + + exports['default'] = SafeString; + module.exports = exports['default']; + +/***/ }), +/* 38 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _Object$seal = __webpack_require__(39)['default']; + + var _Object$keys = __webpack_require__(13)['default']; + + var _interopRequireWildcard = __webpack_require__(3)['default']; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.checkRevision = checkRevision; + exports.template = template; + exports.wrapProgram = wrapProgram; + exports.resolvePartial = resolvePartial; + exports.invokePartial = invokePartial; + exports.noop = noop; + + var _utils = __webpack_require__(5); + + var Utils = _interopRequireWildcard(_utils); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + var _base = __webpack_require__(4); + + var _helpers = __webpack_require__(10); + + var _internalWrapHelper = __webpack_require__(43); + + var _internalProtoAccess = __webpack_require__(33); + + function checkRevision(compilerInfo) { + var compilerRevision = compilerInfo && compilerInfo[0] || 1, + currentRevision = _base.COMPILER_REVISION; + + if (compilerRevision >= _base.LAST_COMPATIBLE_COMPILER_REVISION && compilerRevision <= _base.COMPILER_REVISION) { + return; + } + + if (compilerRevision < _base.LAST_COMPATIBLE_COMPILER_REVISION) { + var runtimeVersions = _base.REVISION_CHANGES[currentRevision], + compilerVersions = _base.REVISION_CHANGES[compilerRevision]; + throw new _exception2['default']('Template was precompiled with an older version of Handlebars than the current runtime. ' + 'Please update your precompiler to a newer version (' + runtimeVersions + ') or downgrade your runtime to an older version (' + compilerVersions + ').'); + } else { + // Use the embedded version info since the runtime doesn't know about this revision yet + throw new _exception2['default']('Template was precompiled with a newer version of Handlebars than the current runtime. ' + 'Please update your runtime to a newer version (' + compilerInfo[1] + ').'); + } + } + + function template(templateSpec, env) { + /* istanbul ignore next */ + if (!env) { + throw new _exception2['default']('No environment passed to template'); + } + if (!templateSpec || !templateSpec.main) { + throw new _exception2['default']('Unknown template object: ' + typeof templateSpec); + } + + templateSpec.main.decorator = templateSpec.main_d; + + // Note: Using env.VM references rather than local var references throughout this section to allow + // for external users to override these as pseudo-supported APIs. + env.VM.checkRevision(templateSpec.compiler); + + // backwards compatibility for precompiled templates with compiler-version 7 (<4.3.0) + var templateWasPrecompiledWithCompilerV7 = templateSpec.compiler && templateSpec.compiler[0] === 7; + + function invokePartialWrapper(partial, context, options) { + if (options.hash) { + context = Utils.extend({}, context, options.hash); + if (options.ids) { + options.ids[0] = true; + } + } + partial = env.VM.resolvePartial.call(this, partial, context, options); + + var extendedOptions = Utils.extend({}, options, { + hooks: this.hooks, + protoAccessControl: this.protoAccessControl + }); + + var result = env.VM.invokePartial.call(this, partial, context, extendedOptions); + + if (result == null && env.compile) { + options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env); + result = options.partials[options.name](context, extendedOptions); + } + if (result != null) { + if (options.indent) { + var lines = result.split('\n'); + for (var i = 0, l = lines.length; i < l; i++) { + if (!lines[i] && i + 1 === l) { + break; + } + + lines[i] = options.indent + lines[i]; + } + result = lines.join('\n'); + } + return result; + } else { + throw new _exception2['default']('The partial ' + options.name + ' could not be compiled when running in runtime-only mode'); + } + } + + // Just add water + var container = { + strict: function strict(obj, name, loc) { + if (!obj || !(name in obj)) { + throw new _exception2['default']('"' + name + '" not defined in ' + obj, { + loc: loc + }); + } + return obj[name]; + }, + lookupProperty: function lookupProperty(parent, propertyName) { + var result = parent[propertyName]; + if (result == null) { + return result; + } + if (Object.prototype.hasOwnProperty.call(parent, propertyName)) { + return result; + } + + if (_internalProtoAccess.resultIsAllowed(result, container.protoAccessControl, propertyName)) { + return result; + } + return undefined; + }, + lookup: function lookup(depths, name) { + var len = depths.length; + for (var i = 0; i < len; i++) { + var result = depths[i] && container.lookupProperty(depths[i], name); + if (result != null) { + return depths[i][name]; + } + } + }, + lambda: function lambda(current, context) { + return typeof current === 'function' ? current.call(context) : current; + }, + + escapeExpression: Utils.escapeExpression, + invokePartial: invokePartialWrapper, + + fn: function fn(i) { + var ret = templateSpec[i]; + ret.decorator = templateSpec[i + '_d']; + return ret; + }, + + programs: [], + program: function program(i, data, declaredBlockParams, blockParams, depths) { + var programWrapper = this.programs[i], + fn = this.fn(i); + if (data || depths || blockParams || declaredBlockParams) { + programWrapper = wrapProgram(this, i, fn, data, declaredBlockParams, blockParams, depths); + } else if (!programWrapper) { + programWrapper = this.programs[i] = wrapProgram(this, i, fn); + } + return programWrapper; + }, + + data: function data(value, depth) { + while (value && depth--) { + value = value._parent; + } + return value; + }, + mergeIfNeeded: function mergeIfNeeded(param, common) { + var obj = param || common; + + if (param && common && param !== common) { + obj = Utils.extend({}, common, param); + } + + return obj; + }, + // An empty object to use as replacement for null-contexts + nullContext: _Object$seal({}), + + noop: env.VM.noop, + compilerInfo: templateSpec.compiler + }; + + function ret(context) { + var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + var data = options.data; + + ret._setup(options); + if (!options.partial && templateSpec.useData) { + data = initData(context, data); + } + var depths = undefined, + blockParams = templateSpec.useBlockParams ? [] : undefined; + if (templateSpec.useDepths) { + if (options.depths) { + depths = context != options.depths[0] ? [context].concat(options.depths) : options.depths; + } else { + depths = [context]; + } + } + + function main(context /*, options*/) { + return '' + templateSpec.main(container, context, container.helpers, container.partials, data, blockParams, depths); + } + + main = executeDecorators(templateSpec.main, main, container, options.depths || [], data, blockParams); + return main(context, options); + } + + ret.isTop = true; + + ret._setup = function (options) { + if (!options.partial) { + var mergedHelpers = Utils.extend({}, env.helpers, options.helpers); + wrapHelpersToPassLookupProperty(mergedHelpers, container); + container.helpers = mergedHelpers; + + if (templateSpec.usePartial) { + // Use mergeIfNeeded here to prevent compiling global partials multiple times + container.partials = container.mergeIfNeeded(options.partials, env.partials); + } + if (templateSpec.usePartial || templateSpec.useDecorators) { + container.decorators = Utils.extend({}, env.decorators, options.decorators); + } + + container.hooks = {}; + container.protoAccessControl = _internalProtoAccess.createProtoAccessControl(options); + + var keepHelperInHelpers = options.allowCallsToHelperMissing || templateWasPrecompiledWithCompilerV7; + _helpers.moveHelperToHooks(container, 'helperMissing', keepHelperInHelpers); + _helpers.moveHelperToHooks(container, 'blockHelperMissing', keepHelperInHelpers); + } else { + container.protoAccessControl = options.protoAccessControl; // internal option + container.helpers = options.helpers; + container.partials = options.partials; + container.decorators = options.decorators; + container.hooks = options.hooks; + } + }; + + ret._child = function (i, data, blockParams, depths) { + if (templateSpec.useBlockParams && !blockParams) { + throw new _exception2['default']('must pass block params'); + } + if (templateSpec.useDepths && !depths) { + throw new _exception2['default']('must pass parent depths'); + } + + return wrapProgram(container, i, templateSpec[i], data, 0, blockParams, depths); + }; + return ret; + } + + function wrapProgram(container, i, fn, data, declaredBlockParams, blockParams, depths) { + function prog(context) { + var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + var currentDepths = depths; + if (depths && context != depths[0] && !(context === container.nullContext && depths[0] === null)) { + currentDepths = [context].concat(depths); + } + + return fn(container, context, container.helpers, container.partials, options.data || data, blockParams && [options.blockParams].concat(blockParams), currentDepths); + } + + prog = executeDecorators(fn, prog, container, depths, data, blockParams); + + prog.program = i; + prog.depth = depths ? depths.length : 0; + prog.blockParams = declaredBlockParams || 0; + return prog; + } + + /** + * This is currently part of the official API, therefore implementation details should not be changed. + */ + + function resolvePartial(partial, context, options) { + if (!partial) { + if (options.name === '@partial-block') { + partial = options.data['partial-block']; + } else { + partial = options.partials[options.name]; + } + } else if (!partial.call && !options.name) { + // This is a dynamic partial that returned a string + options.name = partial; + partial = options.partials[partial]; + } + return partial; + } + + function invokePartial(partial, context, options) { + // Use the current closure context to save the partial-block if this partial + var currentPartialBlock = options.data && options.data['partial-block']; + options.partial = true; + if (options.ids) { + options.data.contextPath = options.ids[0] || options.data.contextPath; + } + + var partialBlock = undefined; + if (options.fn && options.fn !== noop) { + (function () { + options.data = _base.createFrame(options.data); + // Wrapper function to get access to currentPartialBlock from the closure + var fn = options.fn; + partialBlock = options.data['partial-block'] = function partialBlockWrapper(context) { + var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + // Restore the partial-block from the closure for the execution of the block + // i.e. the part inside the block of the partial call. + options.data = _base.createFrame(options.data); + options.data['partial-block'] = currentPartialBlock; + return fn(context, options); + }; + if (fn.partials) { + options.partials = Utils.extend({}, options.partials, fn.partials); + } + })(); + } + + if (partial === undefined && partialBlock) { + partial = partialBlock; + } + + if (partial === undefined) { + throw new _exception2['default']('The partial ' + options.name + ' could not be found'); + } else if (partial instanceof Function) { + return partial(context, options); + } + } + + function noop() { + return ''; + } + + function initData(context, data) { + if (!data || !('root' in data)) { + data = data ? _base.createFrame(data) : {}; + data.root = context; + } + return data; + } + + function executeDecorators(fn, prog, container, depths, data, blockParams) { + if (fn.decorator) { + var props = {}; + prog = fn.decorator(prog, props, container, depths && depths[0], data, blockParams, depths); + Utils.extend(prog, props); + } + return prog; + } + + function wrapHelpersToPassLookupProperty(mergedHelpers, container) { + _Object$keys(mergedHelpers).forEach(function (helperName) { + var helper = mergedHelpers[helperName]; + mergedHelpers[helperName] = passLookupPropertyOption(helper, container); + }); + } + + function passLookupPropertyOption(helper, container) { + var lookupProperty = container.lookupProperty; + return _internalWrapHelper.wrapHelper(helper, function (options) { + return Utils.extend({ lookupProperty: lookupProperty }, options); + }); + } + +/***/ }), +/* 39 */ +/***/ (function(module, exports, __webpack_require__) { + + module.exports = { "default": __webpack_require__(40), __esModule: true }; + +/***/ }), +/* 40 */ +/***/ (function(module, exports, __webpack_require__) { + + __webpack_require__(41); + module.exports = __webpack_require__(21).Object.seal; + +/***/ }), +/* 41 */ +/***/ (function(module, exports, __webpack_require__) { + + // 19.1.2.17 Object.seal(O) + var isObject = __webpack_require__(42); + + __webpack_require__(18)('seal', function($seal){ + return function seal(it){ + return $seal && isObject(it) ? $seal(it) : it; + }; + }); + +/***/ }), +/* 42 */ +/***/ (function(module, exports) { + + module.exports = function(it){ + return typeof it === 'object' ? it !== null : typeof it === 'function'; + }; + +/***/ }), +/* 43 */ +/***/ (function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + exports.wrapHelper = wrapHelper; + + function wrapHelper(helper, transformOptionsFn) { + if (typeof helper !== 'function') { + // This should not happen, but apparently it does in https://github.com/wycats/handlebars.js/issues/1639 + // We try to make the wrapper least-invasive by not wrapping it, if the helper is not a function. + return helper; + } + var wrapper = function wrapper() /* dynamic arguments */{ + var options = arguments[arguments.length - 1]; + arguments[arguments.length - 1] = transformOptionsFn(options); + return helper.apply(this, arguments); + }; + return wrapper; + } + +/***/ }), +/* 44 */ +/***/ (function(module, exports) { + + /* WEBPACK VAR INJECTION */(function(global) {'use strict'; + + exports.__esModule = true; + + exports['default'] = function (Handlebars) { + /* istanbul ignore next */ + var root = typeof global !== 'undefined' ? global : window, + $Handlebars = root.Handlebars; + /* istanbul ignore next */ + Handlebars.noConflict = function () { + if (root.Handlebars === Handlebars) { + root.Handlebars = $Handlebars; + } + return Handlebars; + }; + }; + + module.exports = exports['default']; + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }), +/* 45 */ +/***/ (function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + var AST = { + // Public API used to evaluate derived attributes regarding AST nodes + helpers: { + // a mustache is definitely a helper if: + // * it is an eligible helper, and + // * it has at least one parameter or hash segment + helperExpression: function helperExpression(node) { + return node.type === 'SubExpression' || (node.type === 'MustacheStatement' || node.type === 'BlockStatement') && !!(node.params && node.params.length || node.hash); + }, + + scopedId: function scopedId(path) { + return (/^\.|this\b/.test(path.original) + ); + }, + + // an ID is simple if it only has one part, and that part is not + // `..` or `this`. + simpleId: function simpleId(path) { + return path.parts.length === 1 && !AST.helpers.scopedId(path) && !path.depth; + } + } + }; + + // Must be exported as an object rather than the root of the module as the jison lexer + // must modify the object to operate properly. + exports['default'] = AST; + module.exports = exports['default']; + +/***/ }), +/* 46 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + var _interopRequireWildcard = __webpack_require__(3)['default']; + + exports.__esModule = true; + exports.parseWithoutProcessing = parseWithoutProcessing; + exports.parse = parse; + + var _parser = __webpack_require__(47); + + var _parser2 = _interopRequireDefault(_parser); + + var _whitespaceControl = __webpack_require__(48); + + var _whitespaceControl2 = _interopRequireDefault(_whitespaceControl); + + var _helpers = __webpack_require__(50); + + var Helpers = _interopRequireWildcard(_helpers); + + var _utils = __webpack_require__(5); + + exports.parser = _parser2['default']; + + var yy = {}; + _utils.extend(yy, Helpers); + + function parseWithoutProcessing(input, options) { + // Just return if an already-compiled AST was passed in. + if (input.type === 'Program') { + return input; + } + + _parser2['default'].yy = yy; + + // Altering the shared object here, but this is ok as parser is a sync operation + yy.locInfo = function (locInfo) { + return new yy.SourceLocation(options && options.srcName, locInfo); + }; + + var ast = _parser2['default'].parse(input); + + return ast; + } + + function parse(input, options) { + var ast = parseWithoutProcessing(input, options); + var strip = new _whitespaceControl2['default'](options); + + return strip.accept(ast); + } + +/***/ }), +/* 47 */ +/***/ (function(module, exports) { + + // File ignored in coverage tests via setting in .istanbul.yml + /* Jison generated parser */ + "use strict"; + + exports.__esModule = true; + var handlebars = (function () { + var parser = { trace: function trace() {}, + yy: {}, + symbols_: { "error": 2, "root": 3, "program": 4, "EOF": 5, "program_repetition0": 6, "statement": 7, "mustache": 8, "block": 9, "rawBlock": 10, "partial": 11, "partialBlock": 12, "content": 13, "COMMENT": 14, "CONTENT": 15, "openRawBlock": 16, "rawBlock_repetition0": 17, "END_RAW_BLOCK": 18, "OPEN_RAW_BLOCK": 19, "helperName": 20, "openRawBlock_repetition0": 21, "openRawBlock_option0": 22, "CLOSE_RAW_BLOCK": 23, "openBlock": 24, "block_option0": 25, "closeBlock": 26, "openInverse": 27, "block_option1": 28, "OPEN_BLOCK": 29, "openBlock_repetition0": 30, "openBlock_option0": 31, "openBlock_option1": 32, "CLOSE": 33, "OPEN_INVERSE": 34, "openInverse_repetition0": 35, "openInverse_option0": 36, "openInverse_option1": 37, "openInverseChain": 38, "OPEN_INVERSE_CHAIN": 39, "openInverseChain_repetition0": 40, "openInverseChain_option0": 41, "openInverseChain_option1": 42, "inverseAndProgram": 43, "INVERSE": 44, "inverseChain": 45, "inverseChain_option0": 46, "OPEN_ENDBLOCK": 47, "OPEN": 48, "mustache_repetition0": 49, "mustache_option0": 50, "OPEN_UNESCAPED": 51, "mustache_repetition1": 52, "mustache_option1": 53, "CLOSE_UNESCAPED": 54, "OPEN_PARTIAL": 55, "partialName": 56, "partial_repetition0": 57, "partial_option0": 58, "openPartialBlock": 59, "OPEN_PARTIAL_BLOCK": 60, "openPartialBlock_repetition0": 61, "openPartialBlock_option0": 62, "param": 63, "sexpr": 64, "OPEN_SEXPR": 65, "sexpr_repetition0": 66, "sexpr_option0": 67, "CLOSE_SEXPR": 68, "hash": 69, "hash_repetition_plus0": 70, "hashSegment": 71, "ID": 72, "EQUALS": 73, "blockParams": 74, "OPEN_BLOCK_PARAMS": 75, "blockParams_repetition_plus0": 76, "CLOSE_BLOCK_PARAMS": 77, "path": 78, "dataName": 79, "STRING": 80, "NUMBER": 81, "BOOLEAN": 82, "UNDEFINED": 83, "NULL": 84, "DATA": 85, "pathSegments": 86, "SEP": 87, "$accept": 0, "$end": 1 }, + terminals_: { 2: "error", 5: "EOF", 14: "COMMENT", 15: "CONTENT", 18: "END_RAW_BLOCK", 19: "OPEN_RAW_BLOCK", 23: "CLOSE_RAW_BLOCK", 29: "OPEN_BLOCK", 33: "CLOSE", 34: "OPEN_INVERSE", 39: "OPEN_INVERSE_CHAIN", 44: "INVERSE", 47: "OPEN_ENDBLOCK", 48: "OPEN", 51: "OPEN_UNESCAPED", 54: "CLOSE_UNESCAPED", 55: "OPEN_PARTIAL", 60: "OPEN_PARTIAL_BLOCK", 65: "OPEN_SEXPR", 68: "CLOSE_SEXPR", 72: "ID", 73: "EQUALS", 75: "OPEN_BLOCK_PARAMS", 77: "CLOSE_BLOCK_PARAMS", 80: "STRING", 81: "NUMBER", 82: "BOOLEAN", 83: "UNDEFINED", 84: "NULL", 85: "DATA", 87: "SEP" }, + productions_: [0, [3, 2], [4, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [13, 1], [10, 3], [16, 5], [9, 4], [9, 4], [24, 6], [27, 6], [38, 6], [43, 2], [45, 3], [45, 1], [26, 3], [8, 5], [8, 5], [11, 5], [12, 3], [59, 5], [63, 1], [63, 1], [64, 5], [69, 1], [71, 3], [74, 3], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [56, 1], [56, 1], [79, 2], [78, 1], [86, 3], [86, 1], [6, 0], [6, 2], [17, 0], [17, 2], [21, 0], [21, 2], [22, 0], [22, 1], [25, 0], [25, 1], [28, 0], [28, 1], [30, 0], [30, 2], [31, 0], [31, 1], [32, 0], [32, 1], [35, 0], [35, 2], [36, 0], [36, 1], [37, 0], [37, 1], [40, 0], [40, 2], [41, 0], [41, 1], [42, 0], [42, 1], [46, 0], [46, 1], [49, 0], [49, 2], [50, 0], [50, 1], [52, 0], [52, 2], [53, 0], [53, 1], [57, 0], [57, 2], [58, 0], [58, 1], [61, 0], [61, 2], [62, 0], [62, 1], [66, 0], [66, 2], [67, 0], [67, 1], [70, 1], [70, 2], [76, 1], [76, 2]], + performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$) { + + var $0 = $$.length - 1; + switch (yystate) { + case 1: + return $$[$0 - 1]; + break; + case 2: + this.$ = yy.prepareProgram($$[$0]); + break; + case 3: + this.$ = $$[$0]; + break; + case 4: + this.$ = $$[$0]; + break; + case 5: + this.$ = $$[$0]; + break; + case 6: + this.$ = $$[$0]; + break; + case 7: + this.$ = $$[$0]; + break; + case 8: + this.$ = $$[$0]; + break; + case 9: + this.$ = { + type: 'CommentStatement', + value: yy.stripComment($$[$0]), + strip: yy.stripFlags($$[$0], $$[$0]), + loc: yy.locInfo(this._$) + }; + + break; + case 10: + this.$ = { + type: 'ContentStatement', + original: $$[$0], + value: $$[$0], + loc: yy.locInfo(this._$) + }; + + break; + case 11: + this.$ = yy.prepareRawBlock($$[$0 - 2], $$[$0 - 1], $$[$0], this._$); + break; + case 12: + this.$ = { path: $$[$0 - 3], params: $$[$0 - 2], hash: $$[$0 - 1] }; + break; + case 13: + this.$ = yy.prepareBlock($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0], false, this._$); + break; + case 14: + this.$ = yy.prepareBlock($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0], true, this._$); + break; + case 15: + this.$ = { open: $$[$0 - 5], path: $$[$0 - 4], params: $$[$0 - 3], hash: $$[$0 - 2], blockParams: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 5], $$[$0]) }; + break; + case 16: + this.$ = { path: $$[$0 - 4], params: $$[$0 - 3], hash: $$[$0 - 2], blockParams: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 5], $$[$0]) }; + break; + case 17: + this.$ = { path: $$[$0 - 4], params: $$[$0 - 3], hash: $$[$0 - 2], blockParams: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 5], $$[$0]) }; + break; + case 18: + this.$ = { strip: yy.stripFlags($$[$0 - 1], $$[$0 - 1]), program: $$[$0] }; + break; + case 19: + var inverse = yy.prepareBlock($$[$0 - 2], $$[$0 - 1], $$[$0], $$[$0], false, this._$), + program = yy.prepareProgram([inverse], $$[$0 - 1].loc); + program.chained = true; + + this.$ = { strip: $$[$0 - 2].strip, program: program, chain: true }; + + break; + case 20: + this.$ = $$[$0]; + break; + case 21: + this.$ = { path: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 2], $$[$0]) }; + break; + case 22: + this.$ = yy.prepareMustache($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0 - 4], yy.stripFlags($$[$0 - 4], $$[$0]), this._$); + break; + case 23: + this.$ = yy.prepareMustache($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0 - 4], yy.stripFlags($$[$0 - 4], $$[$0]), this._$); + break; + case 24: + this.$ = { + type: 'PartialStatement', + name: $$[$0 - 3], + params: $$[$0 - 2], + hash: $$[$0 - 1], + indent: '', + strip: yy.stripFlags($$[$0 - 4], $$[$0]), + loc: yy.locInfo(this._$) + }; + + break; + case 25: + this.$ = yy.preparePartialBlock($$[$0 - 2], $$[$0 - 1], $$[$0], this._$); + break; + case 26: + this.$ = { path: $$[$0 - 3], params: $$[$0 - 2], hash: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 4], $$[$0]) }; + break; + case 27: + this.$ = $$[$0]; + break; + case 28: + this.$ = $$[$0]; + break; + case 29: + this.$ = { + type: 'SubExpression', + path: $$[$0 - 3], + params: $$[$0 - 2], + hash: $$[$0 - 1], + loc: yy.locInfo(this._$) + }; + + break; + case 30: + this.$ = { type: 'Hash', pairs: $$[$0], loc: yy.locInfo(this._$) }; + break; + case 31: + this.$ = { type: 'HashPair', key: yy.id($$[$0 - 2]), value: $$[$0], loc: yy.locInfo(this._$) }; + break; + case 32: + this.$ = yy.id($$[$0 - 1]); + break; + case 33: + this.$ = $$[$0]; + break; + case 34: + this.$ = $$[$0]; + break; + case 35: + this.$ = { type: 'StringLiteral', value: $$[$0], original: $$[$0], loc: yy.locInfo(this._$) }; + break; + case 36: + this.$ = { type: 'NumberLiteral', value: Number($$[$0]), original: Number($$[$0]), loc: yy.locInfo(this._$) }; + break; + case 37: + this.$ = { type: 'BooleanLiteral', value: $$[$0] === 'true', original: $$[$0] === 'true', loc: yy.locInfo(this._$) }; + break; + case 38: + this.$ = { type: 'UndefinedLiteral', original: undefined, value: undefined, loc: yy.locInfo(this._$) }; + break; + case 39: + this.$ = { type: 'NullLiteral', original: null, value: null, loc: yy.locInfo(this._$) }; + break; + case 40: + this.$ = $$[$0]; + break; + case 41: + this.$ = $$[$0]; + break; + case 42: + this.$ = yy.preparePath(true, $$[$0], this._$); + break; + case 43: + this.$ = yy.preparePath(false, $$[$0], this._$); + break; + case 44: + $$[$0 - 2].push({ part: yy.id($$[$0]), original: $$[$0], separator: $$[$0 - 1] });this.$ = $$[$0 - 2]; + break; + case 45: + this.$ = [{ part: yy.id($$[$0]), original: $$[$0] }]; + break; + case 46: + this.$ = []; + break; + case 47: + $$[$0 - 1].push($$[$0]); + break; + case 48: + this.$ = []; + break; + case 49: + $$[$0 - 1].push($$[$0]); + break; + case 50: + this.$ = []; + break; + case 51: + $$[$0 - 1].push($$[$0]); + break; + case 58: + this.$ = []; + break; + case 59: + $$[$0 - 1].push($$[$0]); + break; + case 64: + this.$ = []; + break; + case 65: + $$[$0 - 1].push($$[$0]); + break; + case 70: + this.$ = []; + break; + case 71: + $$[$0 - 1].push($$[$0]); + break; + case 78: + this.$ = []; + break; + case 79: + $$[$0 - 1].push($$[$0]); + break; + case 82: + this.$ = []; + break; + case 83: + $$[$0 - 1].push($$[$0]); + break; + case 86: + this.$ = []; + break; + case 87: + $$[$0 - 1].push($$[$0]); + break; + case 90: + this.$ = []; + break; + case 91: + $$[$0 - 1].push($$[$0]); + break; + case 94: + this.$ = []; + break; + case 95: + $$[$0 - 1].push($$[$0]); + break; + case 98: + this.$ = [$$[$0]]; + break; + case 99: + $$[$0 - 1].push($$[$0]); + break; + case 100: + this.$ = [$$[$0]]; + break; + case 101: + $$[$0 - 1].push($$[$0]); + break; + } + }, + table: [{ 3: 1, 4: 2, 5: [2, 46], 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 1: [3] }, { 5: [1, 4] }, { 5: [2, 2], 7: 5, 8: 6, 9: 7, 10: 8, 11: 9, 12: 10, 13: 11, 14: [1, 12], 15: [1, 20], 16: 17, 19: [1, 23], 24: 15, 27: 16, 29: [1, 21], 34: [1, 22], 39: [2, 2], 44: [2, 2], 47: [2, 2], 48: [1, 13], 51: [1, 14], 55: [1, 18], 59: 19, 60: [1, 24] }, { 1: [2, 1] }, { 5: [2, 47], 14: [2, 47], 15: [2, 47], 19: [2, 47], 29: [2, 47], 34: [2, 47], 39: [2, 47], 44: [2, 47], 47: [2, 47], 48: [2, 47], 51: [2, 47], 55: [2, 47], 60: [2, 47] }, { 5: [2, 3], 14: [2, 3], 15: [2, 3], 19: [2, 3], 29: [2, 3], 34: [2, 3], 39: [2, 3], 44: [2, 3], 47: [2, 3], 48: [2, 3], 51: [2, 3], 55: [2, 3], 60: [2, 3] }, { 5: [2, 4], 14: [2, 4], 15: [2, 4], 19: [2, 4], 29: [2, 4], 34: [2, 4], 39: [2, 4], 44: [2, 4], 47: [2, 4], 48: [2, 4], 51: [2, 4], 55: [2, 4], 60: [2, 4] }, { 5: [2, 5], 14: [2, 5], 15: [2, 5], 19: [2, 5], 29: [2, 5], 34: [2, 5], 39: [2, 5], 44: [2, 5], 47: [2, 5], 48: [2, 5], 51: [2, 5], 55: [2, 5], 60: [2, 5] }, { 5: [2, 6], 14: [2, 6], 15: [2, 6], 19: [2, 6], 29: [2, 6], 34: [2, 6], 39: [2, 6], 44: [2, 6], 47: [2, 6], 48: [2, 6], 51: [2, 6], 55: [2, 6], 60: [2, 6] }, { 5: [2, 7], 14: [2, 7], 15: [2, 7], 19: [2, 7], 29: [2, 7], 34: [2, 7], 39: [2, 7], 44: [2, 7], 47: [2, 7], 48: [2, 7], 51: [2, 7], 55: [2, 7], 60: [2, 7] }, { 5: [2, 8], 14: [2, 8], 15: [2, 8], 19: [2, 8], 29: [2, 8], 34: [2, 8], 39: [2, 8], 44: [2, 8], 47: [2, 8], 48: [2, 8], 51: [2, 8], 55: [2, 8], 60: [2, 8] }, { 5: [2, 9], 14: [2, 9], 15: [2, 9], 19: [2, 9], 29: [2, 9], 34: [2, 9], 39: [2, 9], 44: [2, 9], 47: [2, 9], 48: [2, 9], 51: [2, 9], 55: [2, 9], 60: [2, 9] }, { 20: 25, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 36, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 4: 37, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 39: [2, 46], 44: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 4: 38, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 44: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 15: [2, 48], 17: 39, 18: [2, 48] }, { 20: 41, 56: 40, 64: 42, 65: [1, 43], 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 4: 44, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 5: [2, 10], 14: [2, 10], 15: [2, 10], 18: [2, 10], 19: [2, 10], 29: [2, 10], 34: [2, 10], 39: [2, 10], 44: [2, 10], 47: [2, 10], 48: [2, 10], 51: [2, 10], 55: [2, 10], 60: [2, 10] }, { 20: 45, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 46, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 47, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 41, 56: 48, 64: 42, 65: [1, 43], 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 33: [2, 78], 49: 49, 65: [2, 78], 72: [2, 78], 80: [2, 78], 81: [2, 78], 82: [2, 78], 83: [2, 78], 84: [2, 78], 85: [2, 78] }, { 23: [2, 33], 33: [2, 33], 54: [2, 33], 65: [2, 33], 68: [2, 33], 72: [2, 33], 75: [2, 33], 80: [2, 33], 81: [2, 33], 82: [2, 33], 83: [2, 33], 84: [2, 33], 85: [2, 33] }, { 23: [2, 34], 33: [2, 34], 54: [2, 34], 65: [2, 34], 68: [2, 34], 72: [2, 34], 75: [2, 34], 80: [2, 34], 81: [2, 34], 82: [2, 34], 83: [2, 34], 84: [2, 34], 85: [2, 34] }, { 23: [2, 35], 33: [2, 35], 54: [2, 35], 65: [2, 35], 68: [2, 35], 72: [2, 35], 75: [2, 35], 80: [2, 35], 81: [2, 35], 82: [2, 35], 83: [2, 35], 84: [2, 35], 85: [2, 35] }, { 23: [2, 36], 33: [2, 36], 54: [2, 36], 65: [2, 36], 68: [2, 36], 72: [2, 36], 75: [2, 36], 80: [2, 36], 81: [2, 36], 82: [2, 36], 83: [2, 36], 84: [2, 36], 85: [2, 36] }, { 23: [2, 37], 33: [2, 37], 54: [2, 37], 65: [2, 37], 68: [2, 37], 72: [2, 37], 75: [2, 37], 80: [2, 37], 81: [2, 37], 82: [2, 37], 83: [2, 37], 84: [2, 37], 85: [2, 37] }, { 23: [2, 38], 33: [2, 38], 54: [2, 38], 65: [2, 38], 68: [2, 38], 72: [2, 38], 75: [2, 38], 80: [2, 38], 81: [2, 38], 82: [2, 38], 83: [2, 38], 84: [2, 38], 85: [2, 38] }, { 23: [2, 39], 33: [2, 39], 54: [2, 39], 65: [2, 39], 68: [2, 39], 72: [2, 39], 75: [2, 39], 80: [2, 39], 81: [2, 39], 82: [2, 39], 83: [2, 39], 84: [2, 39], 85: [2, 39] }, { 23: [2, 43], 33: [2, 43], 54: [2, 43], 65: [2, 43], 68: [2, 43], 72: [2, 43], 75: [2, 43], 80: [2, 43], 81: [2, 43], 82: [2, 43], 83: [2, 43], 84: [2, 43], 85: [2, 43], 87: [1, 50] }, { 72: [1, 35], 86: 51 }, { 23: [2, 45], 33: [2, 45], 54: [2, 45], 65: [2, 45], 68: [2, 45], 72: [2, 45], 75: [2, 45], 80: [2, 45], 81: [2, 45], 82: [2, 45], 83: [2, 45], 84: [2, 45], 85: [2, 45], 87: [2, 45] }, { 52: 52, 54: [2, 82], 65: [2, 82], 72: [2, 82], 80: [2, 82], 81: [2, 82], 82: [2, 82], 83: [2, 82], 84: [2, 82], 85: [2, 82] }, { 25: 53, 38: 55, 39: [1, 57], 43: 56, 44: [1, 58], 45: 54, 47: [2, 54] }, { 28: 59, 43: 60, 44: [1, 58], 47: [2, 56] }, { 13: 62, 15: [1, 20], 18: [1, 61] }, { 33: [2, 86], 57: 63, 65: [2, 86], 72: [2, 86], 80: [2, 86], 81: [2, 86], 82: [2, 86], 83: [2, 86], 84: [2, 86], 85: [2, 86] }, { 33: [2, 40], 65: [2, 40], 72: [2, 40], 80: [2, 40], 81: [2, 40], 82: [2, 40], 83: [2, 40], 84: [2, 40], 85: [2, 40] }, { 33: [2, 41], 65: [2, 41], 72: [2, 41], 80: [2, 41], 81: [2, 41], 82: [2, 41], 83: [2, 41], 84: [2, 41], 85: [2, 41] }, { 20: 64, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 26: 65, 47: [1, 66] }, { 30: 67, 33: [2, 58], 65: [2, 58], 72: [2, 58], 75: [2, 58], 80: [2, 58], 81: [2, 58], 82: [2, 58], 83: [2, 58], 84: [2, 58], 85: [2, 58] }, { 33: [2, 64], 35: 68, 65: [2, 64], 72: [2, 64], 75: [2, 64], 80: [2, 64], 81: [2, 64], 82: [2, 64], 83: [2, 64], 84: [2, 64], 85: [2, 64] }, { 21: 69, 23: [2, 50], 65: [2, 50], 72: [2, 50], 80: [2, 50], 81: [2, 50], 82: [2, 50], 83: [2, 50], 84: [2, 50], 85: [2, 50] }, { 33: [2, 90], 61: 70, 65: [2, 90], 72: [2, 90], 80: [2, 90], 81: [2, 90], 82: [2, 90], 83: [2, 90], 84: [2, 90], 85: [2, 90] }, { 20: 74, 33: [2, 80], 50: 71, 63: 72, 64: 75, 65: [1, 43], 69: 73, 70: 76, 71: 77, 72: [1, 78], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 72: [1, 79] }, { 23: [2, 42], 33: [2, 42], 54: [2, 42], 65: [2, 42], 68: [2, 42], 72: [2, 42], 75: [2, 42], 80: [2, 42], 81: [2, 42], 82: [2, 42], 83: [2, 42], 84: [2, 42], 85: [2, 42], 87: [1, 50] }, { 20: 74, 53: 80, 54: [2, 84], 63: 81, 64: 75, 65: [1, 43], 69: 82, 70: 76, 71: 77, 72: [1, 78], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 26: 83, 47: [1, 66] }, { 47: [2, 55] }, { 4: 84, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 39: [2, 46], 44: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 47: [2, 20] }, { 20: 85, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 4: 86, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 26: 87, 47: [1, 66] }, { 47: [2, 57] }, { 5: [2, 11], 14: [2, 11], 15: [2, 11], 19: [2, 11], 29: [2, 11], 34: [2, 11], 39: [2, 11], 44: [2, 11], 47: [2, 11], 48: [2, 11], 51: [2, 11], 55: [2, 11], 60: [2, 11] }, { 15: [2, 49], 18: [2, 49] }, { 20: 74, 33: [2, 88], 58: 88, 63: 89, 64: 75, 65: [1, 43], 69: 90, 70: 76, 71: 77, 72: [1, 78], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 65: [2, 94], 66: 91, 68: [2, 94], 72: [2, 94], 80: [2, 94], 81: [2, 94], 82: [2, 94], 83: [2, 94], 84: [2, 94], 85: [2, 94] }, { 5: [2, 25], 14: [2, 25], 15: [2, 25], 19: [2, 25], 29: [2, 25], 34: [2, 25], 39: [2, 25], 44: [2, 25], 47: [2, 25], 48: [2, 25], 51: [2, 25], 55: [2, 25], 60: [2, 25] }, { 20: 92, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 74, 31: 93, 33: [2, 60], 63: 94, 64: 75, 65: [1, 43], 69: 95, 70: 76, 71: 77, 72: [1, 78], 75: [2, 60], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 74, 33: [2, 66], 36: 96, 63: 97, 64: 75, 65: [1, 43], 69: 98, 70: 76, 71: 77, 72: [1, 78], 75: [2, 66], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 74, 22: 99, 23: [2, 52], 63: 100, 64: 75, 65: [1, 43], 69: 101, 70: 76, 71: 77, 72: [1, 78], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 74, 33: [2, 92], 62: 102, 63: 103, 64: 75, 65: [1, 43], 69: 104, 70: 76, 71: 77, 72: [1, 78], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 33: [1, 105] }, { 33: [2, 79], 65: [2, 79], 72: [2, 79], 80: [2, 79], 81: [2, 79], 82: [2, 79], 83: [2, 79], 84: [2, 79], 85: [2, 79] }, { 33: [2, 81] }, { 23: [2, 27], 33: [2, 27], 54: [2, 27], 65: [2, 27], 68: [2, 27], 72: [2, 27], 75: [2, 27], 80: [2, 27], 81: [2, 27], 82: [2, 27], 83: [2, 27], 84: [2, 27], 85: [2, 27] }, { 23: [2, 28], 33: [2, 28], 54: [2, 28], 65: [2, 28], 68: [2, 28], 72: [2, 28], 75: [2, 28], 80: [2, 28], 81: [2, 28], 82: [2, 28], 83: [2, 28], 84: [2, 28], 85: [2, 28] }, { 23: [2, 30], 33: [2, 30], 54: [2, 30], 68: [2, 30], 71: 106, 72: [1, 107], 75: [2, 30] }, { 23: [2, 98], 33: [2, 98], 54: [2, 98], 68: [2, 98], 72: [2, 98], 75: [2, 98] }, { 23: [2, 45], 33: [2, 45], 54: [2, 45], 65: [2, 45], 68: [2, 45], 72: [2, 45], 73: [1, 108], 75: [2, 45], 80: [2, 45], 81: [2, 45], 82: [2, 45], 83: [2, 45], 84: [2, 45], 85: [2, 45], 87: [2, 45] }, { 23: [2, 44], 33: [2, 44], 54: [2, 44], 65: [2, 44], 68: [2, 44], 72: [2, 44], 75: [2, 44], 80: [2, 44], 81: [2, 44], 82: [2, 44], 83: [2, 44], 84: [2, 44], 85: [2, 44], 87: [2, 44] }, { 54: [1, 109] }, { 54: [2, 83], 65: [2, 83], 72: [2, 83], 80: [2, 83], 81: [2, 83], 82: [2, 83], 83: [2, 83], 84: [2, 83], 85: [2, 83] }, { 54: [2, 85] }, { 5: [2, 13], 14: [2, 13], 15: [2, 13], 19: [2, 13], 29: [2, 13], 34: [2, 13], 39: [2, 13], 44: [2, 13], 47: [2, 13], 48: [2, 13], 51: [2, 13], 55: [2, 13], 60: [2, 13] }, { 38: 55, 39: [1, 57], 43: 56, 44: [1, 58], 45: 111, 46: 110, 47: [2, 76] }, { 33: [2, 70], 40: 112, 65: [2, 70], 72: [2, 70], 75: [2, 70], 80: [2, 70], 81: [2, 70], 82: [2, 70], 83: [2, 70], 84: [2, 70], 85: [2, 70] }, { 47: [2, 18] }, { 5: [2, 14], 14: [2, 14], 15: [2, 14], 19: [2, 14], 29: [2, 14], 34: [2, 14], 39: [2, 14], 44: [2, 14], 47: [2, 14], 48: [2, 14], 51: [2, 14], 55: [2, 14], 60: [2, 14] }, { 33: [1, 113] }, { 33: [2, 87], 65: [2, 87], 72: [2, 87], 80: [2, 87], 81: [2, 87], 82: [2, 87], 83: [2, 87], 84: [2, 87], 85: [2, 87] }, { 33: [2, 89] }, { 20: 74, 63: 115, 64: 75, 65: [1, 43], 67: 114, 68: [2, 96], 69: 116, 70: 76, 71: 77, 72: [1, 78], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 33: [1, 117] }, { 32: 118, 33: [2, 62], 74: 119, 75: [1, 120] }, { 33: [2, 59], 65: [2, 59], 72: [2, 59], 75: [2, 59], 80: [2, 59], 81: [2, 59], 82: [2, 59], 83: [2, 59], 84: [2, 59], 85: [2, 59] }, { 33: [2, 61], 75: [2, 61] }, { 33: [2, 68], 37: 121, 74: 122, 75: [1, 120] }, { 33: [2, 65], 65: [2, 65], 72: [2, 65], 75: [2, 65], 80: [2, 65], 81: [2, 65], 82: [2, 65], 83: [2, 65], 84: [2, 65], 85: [2, 65] }, { 33: [2, 67], 75: [2, 67] }, { 23: [1, 123] }, { 23: [2, 51], 65: [2, 51], 72: [2, 51], 80: [2, 51], 81: [2, 51], 82: [2, 51], 83: [2, 51], 84: [2, 51], 85: [2, 51] }, { 23: [2, 53] }, { 33: [1, 124] }, { 33: [2, 91], 65: [2, 91], 72: [2, 91], 80: [2, 91], 81: [2, 91], 82: [2, 91], 83: [2, 91], 84: [2, 91], 85: [2, 91] }, { 33: [2, 93] }, { 5: [2, 22], 14: [2, 22], 15: [2, 22], 19: [2, 22], 29: [2, 22], 34: [2, 22], 39: [2, 22], 44: [2, 22], 47: [2, 22], 48: [2, 22], 51: [2, 22], 55: [2, 22], 60: [2, 22] }, { 23: [2, 99], 33: [2, 99], 54: [2, 99], 68: [2, 99], 72: [2, 99], 75: [2, 99] }, { 73: [1, 108] }, { 20: 74, 63: 125, 64: 75, 65: [1, 43], 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 5: [2, 23], 14: [2, 23], 15: [2, 23], 19: [2, 23], 29: [2, 23], 34: [2, 23], 39: [2, 23], 44: [2, 23], 47: [2, 23], 48: [2, 23], 51: [2, 23], 55: [2, 23], 60: [2, 23] }, { 47: [2, 19] }, { 47: [2, 77] }, { 20: 74, 33: [2, 72], 41: 126, 63: 127, 64: 75, 65: [1, 43], 69: 128, 70: 76, 71: 77, 72: [1, 78], 75: [2, 72], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 5: [2, 24], 14: [2, 24], 15: [2, 24], 19: [2, 24], 29: [2, 24], 34: [2, 24], 39: [2, 24], 44: [2, 24], 47: [2, 24], 48: [2, 24], 51: [2, 24], 55: [2, 24], 60: [2, 24] }, { 68: [1, 129] }, { 65: [2, 95], 68: [2, 95], 72: [2, 95], 80: [2, 95], 81: [2, 95], 82: [2, 95], 83: [2, 95], 84: [2, 95], 85: [2, 95] }, { 68: [2, 97] }, { 5: [2, 21], 14: [2, 21], 15: [2, 21], 19: [2, 21], 29: [2, 21], 34: [2, 21], 39: [2, 21], 44: [2, 21], 47: [2, 21], 48: [2, 21], 51: [2, 21], 55: [2, 21], 60: [2, 21] }, { 33: [1, 130] }, { 33: [2, 63] }, { 72: [1, 132], 76: 131 }, { 33: [1, 133] }, { 33: [2, 69] }, { 15: [2, 12], 18: [2, 12] }, { 14: [2, 26], 15: [2, 26], 19: [2, 26], 29: [2, 26], 34: [2, 26], 47: [2, 26], 48: [2, 26], 51: [2, 26], 55: [2, 26], 60: [2, 26] }, { 23: [2, 31], 33: [2, 31], 54: [2, 31], 68: [2, 31], 72: [2, 31], 75: [2, 31] }, { 33: [2, 74], 42: 134, 74: 135, 75: [1, 120] }, { 33: [2, 71], 65: [2, 71], 72: [2, 71], 75: [2, 71], 80: [2, 71], 81: [2, 71], 82: [2, 71], 83: [2, 71], 84: [2, 71], 85: [2, 71] }, { 33: [2, 73], 75: [2, 73] }, { 23: [2, 29], 33: [2, 29], 54: [2, 29], 65: [2, 29], 68: [2, 29], 72: [2, 29], 75: [2, 29], 80: [2, 29], 81: [2, 29], 82: [2, 29], 83: [2, 29], 84: [2, 29], 85: [2, 29] }, { 14: [2, 15], 15: [2, 15], 19: [2, 15], 29: [2, 15], 34: [2, 15], 39: [2, 15], 44: [2, 15], 47: [2, 15], 48: [2, 15], 51: [2, 15], 55: [2, 15], 60: [2, 15] }, { 72: [1, 137], 77: [1, 136] }, { 72: [2, 100], 77: [2, 100] }, { 14: [2, 16], 15: [2, 16], 19: [2, 16], 29: [2, 16], 34: [2, 16], 44: [2, 16], 47: [2, 16], 48: [2, 16], 51: [2, 16], 55: [2, 16], 60: [2, 16] }, { 33: [1, 138] }, { 33: [2, 75] }, { 33: [2, 32] }, { 72: [2, 101], 77: [2, 101] }, { 14: [2, 17], 15: [2, 17], 19: [2, 17], 29: [2, 17], 34: [2, 17], 39: [2, 17], 44: [2, 17], 47: [2, 17], 48: [2, 17], 51: [2, 17], 55: [2, 17], 60: [2, 17] }], + defaultActions: { 4: [2, 1], 54: [2, 55], 56: [2, 20], 60: [2, 57], 73: [2, 81], 82: [2, 85], 86: [2, 18], 90: [2, 89], 101: [2, 53], 104: [2, 93], 110: [2, 19], 111: [2, 77], 116: [2, 97], 119: [2, 63], 122: [2, 69], 135: [2, 75], 136: [2, 32] }, + parseError: function parseError(str, hash) { + throw new Error(str); + }, + parse: function parse(input) { + var self = this, + stack = [0], + vstack = [null], + lstack = [], + table = this.table, + yytext = "", + yylineno = 0, + yyleng = 0, + recovering = 0, + TERROR = 2, + EOF = 1; + this.lexer.setInput(input); + this.lexer.yy = this.yy; + this.yy.lexer = this.lexer; + this.yy.parser = this; + if (typeof this.lexer.yylloc == "undefined") this.lexer.yylloc = {}; + var yyloc = this.lexer.yylloc; + lstack.push(yyloc); + var ranges = this.lexer.options && this.lexer.options.ranges; + if (typeof this.yy.parseError === "function") this.parseError = this.yy.parseError; + function popStack(n) { + stack.length = stack.length - 2 * n; + vstack.length = vstack.length - n; + lstack.length = lstack.length - n; + } + function lex() { + var token; + token = self.lexer.lex() || 1; + if (typeof token !== "number") { + token = self.symbols_[token] || token; + } + return token; + } + var symbol, + preErrorSymbol, + state, + action, + a, + r, + yyval = {}, + p, + len, + newState, + expected; + while (true) { + state = stack[stack.length - 1]; + if (this.defaultActions[state]) { + action = this.defaultActions[state]; + } else { + if (symbol === null || typeof symbol == "undefined") { + symbol = lex(); + } + action = table[state] && table[state][symbol]; + } + if (typeof action === "undefined" || !action.length || !action[0]) { + var errStr = ""; + if (!recovering) { + expected = []; + for (p in table[state]) if (this.terminals_[p] && p > 2) { + expected.push("'" + this.terminals_[p] + "'"); + } + if (this.lexer.showPosition) { + errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; + } else { + errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1 ? "end of input" : "'" + (this.terminals_[symbol] || symbol) + "'"); + } + this.parseError(errStr, { text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected }); + } + } + if (action[0] instanceof Array && action.length > 1) { + throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); + } + switch (action[0]) { + case 1: + stack.push(symbol); + vstack.push(this.lexer.yytext); + lstack.push(this.lexer.yylloc); + stack.push(action[1]); + symbol = null; + if (!preErrorSymbol) { + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + if (recovering > 0) recovering--; + } else { + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + case 2: + len = this.productions_[action[1]][1]; + yyval.$ = vstack[vstack.length - len]; + yyval._$ = { first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column }; + if (ranges) { + yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; + } + r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); + if (typeof r !== "undefined") { + return r; + } + if (len) { + stack = stack.slice(0, -1 * len * 2); + vstack = vstack.slice(0, -1 * len); + lstack = lstack.slice(0, -1 * len); + } + stack.push(this.productions_[action[1]][0]); + vstack.push(yyval.$); + lstack.push(yyval._$); + newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; + stack.push(newState); + break; + case 3: + return true; + } + } + return true; + } + }; + /* Jison generated lexer */ + var lexer = (function () { + var lexer = { EOF: 1, + parseError: function parseError(str, hash) { + if (this.yy.parser) { + this.yy.parser.parseError(str, hash); + } else { + throw new Error(str); + } + }, + setInput: function setInput(input) { + this._input = input; + this._more = this._less = this.done = false; + this.yylineno = this.yyleng = 0; + this.yytext = this.matched = this.match = ''; + this.conditionStack = ['INITIAL']; + this.yylloc = { first_line: 1, first_column: 0, last_line: 1, last_column: 0 }; + if (this.options.ranges) this.yylloc.range = [0, 0]; + this.offset = 0; + return this; + }, + input: function input() { + var ch = this._input[0]; + this.yytext += ch; + this.yyleng++; + this.offset++; + this.match += ch; + this.matched += ch; + var lines = ch.match(/(?:\r\n?|\n).*/g); + if (lines) { + this.yylineno++; + this.yylloc.last_line++; + } else { + this.yylloc.last_column++; + } + if (this.options.ranges) this.yylloc.range[1]++; + + this._input = this._input.slice(1); + return ch; + }, + unput: function unput(ch) { + var len = ch.length; + var lines = ch.split(/(?:\r\n?|\n)/g); + + this._input = ch + this._input; + this.yytext = this.yytext.substr(0, this.yytext.length - len - 1); + //this.yyleng -= len; + this.offset -= len; + var oldLines = this.match.split(/(?:\r\n?|\n)/g); + this.match = this.match.substr(0, this.match.length - 1); + this.matched = this.matched.substr(0, this.matched.length - 1); + + if (lines.length - 1) this.yylineno -= lines.length - 1; + var r = this.yylloc.range; + + this.yylloc = { first_line: this.yylloc.first_line, + last_line: this.yylineno + 1, + first_column: this.yylloc.first_column, + last_column: lines ? (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length : this.yylloc.first_column - len + }; + + if (this.options.ranges) { + this.yylloc.range = [r[0], r[0] + this.yyleng - len]; + } + return this; + }, + more: function more() { + this._more = true; + return this; + }, + less: function less(n) { + this.unput(this.match.slice(n)); + }, + pastInput: function pastInput() { + var past = this.matched.substr(0, this.matched.length - this.match.length); + return (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, ""); + }, + upcomingInput: function upcomingInput() { + var next = this.match; + if (next.length < 20) { + next += this._input.substr(0, 20 - next.length); + } + return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, ""); + }, + showPosition: function showPosition() { + var pre = this.pastInput(); + var c = new Array(pre.length + 1).join("-"); + return pre + this.upcomingInput() + "\n" + c + "^"; + }, + next: function next() { + if (this.done) { + return this.EOF; + } + if (!this._input) this.done = true; + + var token, match, tempMatch, index, col, lines; + if (!this._more) { + this.yytext = ''; + this.match = ''; + } + var rules = this._currentRules(); + for (var i = 0; i < rules.length; i++) { + tempMatch = this._input.match(this.rules[rules[i]]); + if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { + match = tempMatch; + index = i; + if (!this.options.flex) break; + } + } + if (match) { + lines = match[0].match(/(?:\r\n?|\n).*/g); + if (lines) this.yylineno += lines.length; + this.yylloc = { first_line: this.yylloc.last_line, + last_line: this.yylineno + 1, + first_column: this.yylloc.last_column, + last_column: lines ? lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length }; + this.yytext += match[0]; + this.match += match[0]; + this.matches = match; + this.yyleng = this.yytext.length; + if (this.options.ranges) { + this.yylloc.range = [this.offset, this.offset += this.yyleng]; + } + this._more = false; + this._input = this._input.slice(match[0].length); + this.matched += match[0]; + token = this.performAction.call(this, this.yy, this, rules[index], this.conditionStack[this.conditionStack.length - 1]); + if (this.done && this._input) this.done = false; + if (token) return token;else return; + } + if (this._input === "") { + return this.EOF; + } else { + return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), { text: "", token: null, line: this.yylineno }); + } + }, + lex: function lex() { + var r = this.next(); + if (typeof r !== 'undefined') { + return r; + } else { + return this.lex(); + } + }, + begin: function begin(condition) { + this.conditionStack.push(condition); + }, + popState: function popState() { + return this.conditionStack.pop(); + }, + _currentRules: function _currentRules() { + return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules; + }, + topState: function topState() { + return this.conditionStack[this.conditionStack.length - 2]; + }, + pushState: function begin(condition) { + this.begin(condition); + } }; + lexer.options = {}; + lexer.performAction = function anonymous(yy, yy_, $avoiding_name_collisions, YY_START) { + + function strip(start, end) { + return yy_.yytext = yy_.yytext.substring(start, yy_.yyleng - end + start); + } + + var YYSTATE = YY_START; + switch ($avoiding_name_collisions) { + case 0: + if (yy_.yytext.slice(-2) === "\\\\") { + strip(0, 1); + this.begin("mu"); + } else if (yy_.yytext.slice(-1) === "\\") { + strip(0, 1); + this.begin("emu"); + } else { + this.begin("mu"); + } + if (yy_.yytext) return 15; + + break; + case 1: + return 15; + break; + case 2: + this.popState(); + return 15; + + break; + case 3: + this.begin('raw');return 15; + break; + case 4: + this.popState(); + // Should be using `this.topState()` below, but it currently + // returns the second top instead of the first top. Opened an + // issue about it at https://github.com/zaach/jison/issues/291 + if (this.conditionStack[this.conditionStack.length - 1] === 'raw') { + return 15; + } else { + strip(5, 9); + return 'END_RAW_BLOCK'; + } + + break; + case 5: + return 15; + break; + case 6: + this.popState(); + return 14; + + break; + case 7: + return 65; + break; + case 8: + return 68; + break; + case 9: + return 19; + break; + case 10: + this.popState(); + this.begin('raw'); + return 23; + + break; + case 11: + return 55; + break; + case 12: + return 60; + break; + case 13: + return 29; + break; + case 14: + return 47; + break; + case 15: + this.popState();return 44; + break; + case 16: + this.popState();return 44; + break; + case 17: + return 34; + break; + case 18: + return 39; + break; + case 19: + return 51; + break; + case 20: + return 48; + break; + case 21: + this.unput(yy_.yytext); + this.popState(); + this.begin('com'); + + break; + case 22: + this.popState(); + return 14; + + break; + case 23: + return 48; + break; + case 24: + return 73; + break; + case 25: + return 72; + break; + case 26: + return 72; + break; + case 27: + return 87; + break; + case 28: + // ignore whitespace + break; + case 29: + this.popState();return 54; + break; + case 30: + this.popState();return 33; + break; + case 31: + yy_.yytext = strip(1, 2).replace(/\\"/g, '"');return 80; + break; + case 32: + yy_.yytext = strip(1, 2).replace(/\\'/g, "'");return 80; + break; + case 33: + return 85; + break; + case 34: + return 82; + break; + case 35: + return 82; + break; + case 36: + return 83; + break; + case 37: + return 84; + break; + case 38: + return 81; + break; + case 39: + return 75; + break; + case 40: + return 77; + break; + case 41: + return 72; + break; + case 42: + yy_.yytext = yy_.yytext.replace(/\\([\\\]])/g, '$1');return 72; + break; + case 43: + return 'INVALID'; + break; + case 44: + return 5; + break; + } + }; + lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/, /^(?:[^\x00]+)/, /^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/, /^(?:\{\{\{\{(?=[^\/]))/, /^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/, /^(?:[^\x00]+?(?=(\{\{\{\{)))/, /^(?:[\s\S]*?--(~)?\}\})/, /^(?:\()/, /^(?:\))/, /^(?:\{\{\{\{)/, /^(?:\}\}\}\})/, /^(?:\{\{(~)?>)/, /^(?:\{\{(~)?#>)/, /^(?:\{\{(~)?#\*?)/, /^(?:\{\{(~)?\/)/, /^(?:\{\{(~)?\^\s*(~)?\}\})/, /^(?:\{\{(~)?\s*else\s*(~)?\}\})/, /^(?:\{\{(~)?\^)/, /^(?:\{\{(~)?\s*else\b)/, /^(?:\{\{(~)?\{)/, /^(?:\{\{(~)?&)/, /^(?:\{\{(~)?!--)/, /^(?:\{\{(~)?![\s\S]*?\}\})/, /^(?:\{\{(~)?\*?)/, /^(?:=)/, /^(?:\.\.)/, /^(?:\.(?=([=~}\s\/.)|])))/, /^(?:[\/.])/, /^(?:\s+)/, /^(?:\}(~)?\}\})/, /^(?:(~)?\}\})/, /^(?:"(\\["]|[^"])*")/, /^(?:'(\\[']|[^'])*')/, /^(?:@)/, /^(?:true(?=([~}\s)])))/, /^(?:false(?=([~}\s)])))/, /^(?:undefined(?=([~}\s)])))/, /^(?:null(?=([~}\s)])))/, /^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/, /^(?:as\s+\|)/, /^(?:\|)/, /^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/, /^(?:\[(\\\]|[^\]])*\])/, /^(?:.)/, /^(?:$)/]; + lexer.conditions = { "mu": { "rules": [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44], "inclusive": false }, "emu": { "rules": [2], "inclusive": false }, "com": { "rules": [6], "inclusive": false }, "raw": { "rules": [3, 4, 5], "inclusive": false }, "INITIAL": { "rules": [0, 1, 44], "inclusive": true } }; + return lexer; + })(); + parser.lexer = lexer; + function Parser() { + this.yy = {}; + }Parser.prototype = parser;parser.Parser = Parser; + return new Parser(); + })();exports["default"] = handlebars; + module.exports = exports["default"]; + +/***/ }), +/* 48 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _visitor = __webpack_require__(49); + + var _visitor2 = _interopRequireDefault(_visitor); + + function WhitespaceControl() { + var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + + this.options = options; + } + WhitespaceControl.prototype = new _visitor2['default'](); + + WhitespaceControl.prototype.Program = function (program) { + var doStandalone = !this.options.ignoreStandalone; + + var isRoot = !this.isRootSeen; + this.isRootSeen = true; + + var body = program.body; + for (var i = 0, l = body.length; i < l; i++) { + var current = body[i], + strip = this.accept(current); + + if (!strip) { + continue; + } + + var _isPrevWhitespace = isPrevWhitespace(body, i, isRoot), + _isNextWhitespace = isNextWhitespace(body, i, isRoot), + openStandalone = strip.openStandalone && _isPrevWhitespace, + closeStandalone = strip.closeStandalone && _isNextWhitespace, + inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace; + + if (strip.close) { + omitRight(body, i, true); + } + if (strip.open) { + omitLeft(body, i, true); + } + + if (doStandalone && inlineStandalone) { + omitRight(body, i); + + if (omitLeft(body, i)) { + // If we are on a standalone node, save the indent info for partials + if (current.type === 'PartialStatement') { + // Pull out the whitespace from the final line + current.indent = /([ \t]+$)/.exec(body[i - 1].original)[1]; + } + } + } + if (doStandalone && openStandalone) { + omitRight((current.program || current.inverse).body); + + // Strip out the previous content node if it's whitespace only + omitLeft(body, i); + } + if (doStandalone && closeStandalone) { + // Always strip the next node + omitRight(body, i); + + omitLeft((current.inverse || current.program).body); + } + } + + return program; + }; + + WhitespaceControl.prototype.BlockStatement = WhitespaceControl.prototype.DecoratorBlock = WhitespaceControl.prototype.PartialBlockStatement = function (block) { + this.accept(block.program); + this.accept(block.inverse); + + // Find the inverse program that is involed with whitespace stripping. + var program = block.program || block.inverse, + inverse = block.program && block.inverse, + firstInverse = inverse, + lastInverse = inverse; + + if (inverse && inverse.chained) { + firstInverse = inverse.body[0].program; + + // Walk the inverse chain to find the last inverse that is actually in the chain. + while (lastInverse.chained) { + lastInverse = lastInverse.body[lastInverse.body.length - 1].program; + } + } + + var strip = { + open: block.openStrip.open, + close: block.closeStrip.close, + + // Determine the standalone candiacy. Basically flag our content as being possibly standalone + // so our parent can determine if we actually are standalone + openStandalone: isNextWhitespace(program.body), + closeStandalone: isPrevWhitespace((firstInverse || program).body) + }; + + if (block.openStrip.close) { + omitRight(program.body, null, true); + } + + if (inverse) { + var inverseStrip = block.inverseStrip; + + if (inverseStrip.open) { + omitLeft(program.body, null, true); + } + + if (inverseStrip.close) { + omitRight(firstInverse.body, null, true); + } + if (block.closeStrip.open) { + omitLeft(lastInverse.body, null, true); + } + + // Find standalone else statments + if (!this.options.ignoreStandalone && isPrevWhitespace(program.body) && isNextWhitespace(firstInverse.body)) { + omitLeft(program.body); + omitRight(firstInverse.body); + } + } else if (block.closeStrip.open) { + omitLeft(program.body, null, true); + } + + return strip; + }; + + WhitespaceControl.prototype.Decorator = WhitespaceControl.prototype.MustacheStatement = function (mustache) { + return mustache.strip; + }; + + WhitespaceControl.prototype.PartialStatement = WhitespaceControl.prototype.CommentStatement = function (node) { + /* istanbul ignore next */ + var strip = node.strip || {}; + return { + inlineStandalone: true, + open: strip.open, + close: strip.close + }; + }; + + function isPrevWhitespace(body, i, isRoot) { + if (i === undefined) { + i = body.length; + } + + // Nodes that end with newlines are considered whitespace (but are special + // cased for strip operations) + var prev = body[i - 1], + sibling = body[i - 2]; + if (!prev) { + return isRoot; + } + + if (prev.type === 'ContentStatement') { + return (sibling || !isRoot ? /\r?\n\s*?$/ : /(^|\r?\n)\s*?$/).test(prev.original); + } + } + function isNextWhitespace(body, i, isRoot) { + if (i === undefined) { + i = -1; + } + + var next = body[i + 1], + sibling = body[i + 2]; + if (!next) { + return isRoot; + } + + if (next.type === 'ContentStatement') { + return (sibling || !isRoot ? /^\s*?\r?\n/ : /^\s*?(\r?\n|$)/).test(next.original); + } + } + + // Marks the node to the right of the position as omitted. + // I.e. {{foo}}' ' will mark the ' ' node as omitted. + // + // If i is undefined, then the first child will be marked as such. + // + // If mulitple is truthy then all whitespace will be stripped out until non-whitespace + // content is met. + function omitRight(body, i, multiple) { + var current = body[i == null ? 0 : i + 1]; + if (!current || current.type !== 'ContentStatement' || !multiple && current.rightStripped) { + return; + } + + var original = current.value; + current.value = current.value.replace(multiple ? /^\s+/ : /^[ \t]*\r?\n?/, ''); + current.rightStripped = current.value !== original; + } + + // Marks the node to the left of the position as omitted. + // I.e. ' '{{foo}} will mark the ' ' node as omitted. + // + // If i is undefined then the last child will be marked as such. + // + // If mulitple is truthy then all whitespace will be stripped out until non-whitespace + // content is met. + function omitLeft(body, i, multiple) { + var current = body[i == null ? body.length - 1 : i - 1]; + if (!current || current.type !== 'ContentStatement' || !multiple && current.leftStripped) { + return; + } + + // We omit the last node if it's whitespace only and not preceded by a non-content node. + var original = current.value; + current.value = current.value.replace(multiple ? /\s+$/ : /[ \t]+$/, ''); + current.leftStripped = current.value !== original; + return current.leftStripped; + } + + exports['default'] = WhitespaceControl; + module.exports = exports['default']; + +/***/ }), +/* 49 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + function Visitor() { + this.parents = []; + } + + Visitor.prototype = { + constructor: Visitor, + mutating: false, + + // Visits a given value. If mutating, will replace the value if necessary. + acceptKey: function acceptKey(node, name) { + var value = this.accept(node[name]); + if (this.mutating) { + // Hacky sanity check: This may have a few false positives for type for the helper + // methods but will generally do the right thing without a lot of overhead. + if (value && !Visitor.prototype[value.type]) { + throw new _exception2['default']('Unexpected node type "' + value.type + '" found when accepting ' + name + ' on ' + node.type); + } + node[name] = value; + } + }, + + // Performs an accept operation with added sanity check to ensure + // required keys are not removed. + acceptRequired: function acceptRequired(node, name) { + this.acceptKey(node, name); + + if (!node[name]) { + throw new _exception2['default'](node.type + ' requires ' + name); + } + }, + + // Traverses a given array. If mutating, empty respnses will be removed + // for child elements. + acceptArray: function acceptArray(array) { + for (var i = 0, l = array.length; i < l; i++) { + this.acceptKey(array, i); + + if (!array[i]) { + array.splice(i, 1); + i--; + l--; + } + } + }, + + accept: function accept(object) { + if (!object) { + return; + } + + /* istanbul ignore next: Sanity code */ + if (!this[object.type]) { + throw new _exception2['default']('Unknown type: ' + object.type, object); + } + + if (this.current) { + this.parents.unshift(this.current); + } + this.current = object; + + var ret = this[object.type](object); + + this.current = this.parents.shift(); + + if (!this.mutating || ret) { + return ret; + } else if (ret !== false) { + return object; + } + }, + + Program: function Program(program) { + this.acceptArray(program.body); + }, + + MustacheStatement: visitSubExpression, + Decorator: visitSubExpression, + + BlockStatement: visitBlock, + DecoratorBlock: visitBlock, + + PartialStatement: visitPartial, + PartialBlockStatement: function PartialBlockStatement(partial) { + visitPartial.call(this, partial); + + this.acceptKey(partial, 'program'); + }, + + ContentStatement: function ContentStatement() /* content */{}, + CommentStatement: function CommentStatement() /* comment */{}, + + SubExpression: visitSubExpression, + + PathExpression: function PathExpression() /* path */{}, + + StringLiteral: function StringLiteral() /* string */{}, + NumberLiteral: function NumberLiteral() /* number */{}, + BooleanLiteral: function BooleanLiteral() /* bool */{}, + UndefinedLiteral: function UndefinedLiteral() /* literal */{}, + NullLiteral: function NullLiteral() /* literal */{}, + + Hash: function Hash(hash) { + this.acceptArray(hash.pairs); + }, + HashPair: function HashPair(pair) { + this.acceptRequired(pair, 'value'); + } + }; + + function visitSubExpression(mustache) { + this.acceptRequired(mustache, 'path'); + this.acceptArray(mustache.params); + this.acceptKey(mustache, 'hash'); + } + function visitBlock(block) { + visitSubExpression.call(this, block); + + this.acceptKey(block, 'program'); + this.acceptKey(block, 'inverse'); + } + function visitPartial(partial) { + this.acceptRequired(partial, 'name'); + this.acceptArray(partial.params); + this.acceptKey(partial, 'hash'); + } + + exports['default'] = Visitor; + module.exports = exports['default']; + +/***/ }), +/* 50 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.SourceLocation = SourceLocation; + exports.id = id; + exports.stripFlags = stripFlags; + exports.stripComment = stripComment; + exports.preparePath = preparePath; + exports.prepareMustache = prepareMustache; + exports.prepareRawBlock = prepareRawBlock; + exports.prepareBlock = prepareBlock; + exports.prepareProgram = prepareProgram; + exports.preparePartialBlock = preparePartialBlock; + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + function validateClose(open, close) { + close = close.path ? close.path.original : close; + + if (open.path.original !== close) { + var errorNode = { loc: open.path.loc }; + + throw new _exception2['default'](open.path.original + " doesn't match " + close, errorNode); + } + } + + function SourceLocation(source, locInfo) { + this.source = source; + this.start = { + line: locInfo.first_line, + column: locInfo.first_column + }; + this.end = { + line: locInfo.last_line, + column: locInfo.last_column + }; + } + + function id(token) { + if (/^\[.*\]$/.test(token)) { + return token.substring(1, token.length - 1); + } else { + return token; + } + } + + function stripFlags(open, close) { + return { + open: open.charAt(2) === '~', + close: close.charAt(close.length - 3) === '~' + }; + } + + function stripComment(comment) { + return comment.replace(/^\{\{~?!-?-?/, '').replace(/-?-?~?\}\}$/, ''); + } + + function preparePath(data, parts, loc) { + loc = this.locInfo(loc); + + var original = data ? '@' : '', + dig = [], + depth = 0; + + for (var i = 0, l = parts.length; i < l; i++) { + var part = parts[i].part, + + // If we have [] syntax then we do not treat path references as operators, + // i.e. foo.[this] resolves to approximately context.foo['this'] + isLiteral = parts[i].original !== part; + original += (parts[i].separator || '') + part; + + if (!isLiteral && (part === '..' || part === '.' || part === 'this')) { + if (dig.length > 0) { + throw new _exception2['default']('Invalid path: ' + original, { loc: loc }); + } else if (part === '..') { + depth++; + } + } else { + dig.push(part); + } + } + + return { + type: 'PathExpression', + data: data, + depth: depth, + parts: dig, + original: original, + loc: loc + }; + } + + function prepareMustache(path, params, hash, open, strip, locInfo) { + // Must use charAt to support IE pre-10 + var escapeFlag = open.charAt(3) || open.charAt(2), + escaped = escapeFlag !== '{' && escapeFlag !== '&'; + + var decorator = /\*/.test(open); + return { + type: decorator ? 'Decorator' : 'MustacheStatement', + path: path, + params: params, + hash: hash, + escaped: escaped, + strip: strip, + loc: this.locInfo(locInfo) + }; + } + + function prepareRawBlock(openRawBlock, contents, close, locInfo) { + validateClose(openRawBlock, close); + + locInfo = this.locInfo(locInfo); + var program = { + type: 'Program', + body: contents, + strip: {}, + loc: locInfo + }; + + return { + type: 'BlockStatement', + path: openRawBlock.path, + params: openRawBlock.params, + hash: openRawBlock.hash, + program: program, + openStrip: {}, + inverseStrip: {}, + closeStrip: {}, + loc: locInfo + }; + } + + function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) { + if (close && close.path) { + validateClose(openBlock, close); + } + + var decorator = /\*/.test(openBlock.open); + + program.blockParams = openBlock.blockParams; + + var inverse = undefined, + inverseStrip = undefined; + + if (inverseAndProgram) { + if (decorator) { + throw new _exception2['default']('Unexpected inverse block on decorator', inverseAndProgram); + } + + if (inverseAndProgram.chain) { + inverseAndProgram.program.body[0].closeStrip = close.strip; + } + + inverseStrip = inverseAndProgram.strip; + inverse = inverseAndProgram.program; + } + + if (inverted) { + inverted = inverse; + inverse = program; + program = inverted; + } + + return { + type: decorator ? 'DecoratorBlock' : 'BlockStatement', + path: openBlock.path, + params: openBlock.params, + hash: openBlock.hash, + program: program, + inverse: inverse, + openStrip: openBlock.strip, + inverseStrip: inverseStrip, + closeStrip: close && close.strip, + loc: this.locInfo(locInfo) + }; + } + + function prepareProgram(statements, loc) { + if (!loc && statements.length) { + var firstLoc = statements[0].loc, + lastLoc = statements[statements.length - 1].loc; + + /* istanbul ignore else */ + if (firstLoc && lastLoc) { + loc = { + source: firstLoc.source, + start: { + line: firstLoc.start.line, + column: firstLoc.start.column + }, + end: { + line: lastLoc.end.line, + column: lastLoc.end.column + } + }; + } + } + + return { + type: 'Program', + body: statements, + strip: {}, + loc: loc + }; + } + + function preparePartialBlock(open, program, close, locInfo) { + validateClose(open, close); + + return { + type: 'PartialBlockStatement', + name: open.path, + params: open.params, + hash: open.hash, + program: program, + openStrip: open.strip, + closeStrip: close && close.strip, + loc: this.locInfo(locInfo) + }; + } + +/***/ }), +/* 51 */ +/***/ (function(module, exports, __webpack_require__) { + + /* eslint-disable new-cap */ + + 'use strict'; + + var _Object$create = __webpack_require__(34)['default']; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.Compiler = Compiler; + exports.precompile = precompile; + exports.compile = compile; + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + var _utils = __webpack_require__(5); + + var _ast = __webpack_require__(45); + + var _ast2 = _interopRequireDefault(_ast); + + var slice = [].slice; + + function Compiler() {} + + // the foundHelper register will disambiguate helper lookup from finding a + // function in a context. This is necessary for mustache compatibility, which + // requires that context functions in blocks are evaluated by blockHelperMissing, + // and then proceed as if the resulting value was provided to blockHelperMissing. + + Compiler.prototype = { + compiler: Compiler, + + equals: function equals(other) { + var len = this.opcodes.length; + if (other.opcodes.length !== len) { + return false; + } + + for (var i = 0; i < len; i++) { + var opcode = this.opcodes[i], + otherOpcode = other.opcodes[i]; + if (opcode.opcode !== otherOpcode.opcode || !argEquals(opcode.args, otherOpcode.args)) { + return false; + } + } + + // We know that length is the same between the two arrays because they are directly tied + // to the opcode behavior above. + len = this.children.length; + for (var i = 0; i < len; i++) { + if (!this.children[i].equals(other.children[i])) { + return false; + } + } + + return true; + }, + + guid: 0, + + compile: function compile(program, options) { + this.sourceNode = []; + this.opcodes = []; + this.children = []; + this.options = options; + this.stringParams = options.stringParams; + this.trackIds = options.trackIds; + + options.blockParams = options.blockParams || []; + + options.knownHelpers = _utils.extend(_Object$create(null), { + helperMissing: true, + blockHelperMissing: true, + each: true, + 'if': true, + unless: true, + 'with': true, + log: true, + lookup: true + }, options.knownHelpers); + + return this.accept(program); + }, + + compileProgram: function compileProgram(program) { + var childCompiler = new this.compiler(), + // eslint-disable-line new-cap + result = childCompiler.compile(program, this.options), + guid = this.guid++; + + this.usePartial = this.usePartial || result.usePartial; + + this.children[guid] = result; + this.useDepths = this.useDepths || result.useDepths; + + return guid; + }, + + accept: function accept(node) { + /* istanbul ignore next: Sanity code */ + if (!this[node.type]) { + throw new _exception2['default']('Unknown type: ' + node.type, node); + } + + this.sourceNode.unshift(node); + var ret = this[node.type](node); + this.sourceNode.shift(); + return ret; + }, + + Program: function Program(program) { + this.options.blockParams.unshift(program.blockParams); + + var body = program.body, + bodyLength = body.length; + for (var i = 0; i < bodyLength; i++) { + this.accept(body[i]); + } + + this.options.blockParams.shift(); + + this.isSimple = bodyLength === 1; + this.blockParams = program.blockParams ? program.blockParams.length : 0; + + return this; + }, + + BlockStatement: function BlockStatement(block) { + transformLiteralToPath(block); + + var program = block.program, + inverse = block.inverse; + + program = program && this.compileProgram(program); + inverse = inverse && this.compileProgram(inverse); + + var type = this.classifySexpr(block); + + if (type === 'helper') { + this.helperSexpr(block, program, inverse); + } else if (type === 'simple') { + this.simpleSexpr(block); + + // now that the simple mustache is resolved, we need to + // evaluate it by executing `blockHelperMissing` + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + this.opcode('emptyHash'); + this.opcode('blockValue', block.path.original); + } else { + this.ambiguousSexpr(block, program, inverse); + + // now that the simple mustache is resolved, we need to + // evaluate it by executing `blockHelperMissing` + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + this.opcode('emptyHash'); + this.opcode('ambiguousBlockValue'); + } + + this.opcode('append'); + }, + + DecoratorBlock: function DecoratorBlock(decorator) { + var program = decorator.program && this.compileProgram(decorator.program); + var params = this.setupFullMustacheParams(decorator, program, undefined), + path = decorator.path; + + this.useDecorators = true; + this.opcode('registerDecorator', params.length, path.original); + }, + + PartialStatement: function PartialStatement(partial) { + this.usePartial = true; + + var program = partial.program; + if (program) { + program = this.compileProgram(partial.program); + } + + var params = partial.params; + if (params.length > 1) { + throw new _exception2['default']('Unsupported number of partial arguments: ' + params.length, partial); + } else if (!params.length) { + if (this.options.explicitPartialContext) { + this.opcode('pushLiteral', 'undefined'); + } else { + params.push({ type: 'PathExpression', parts: [], depth: 0 }); + } + } + + var partialName = partial.name.original, + isDynamic = partial.name.type === 'SubExpression'; + if (isDynamic) { + this.accept(partial.name); + } + + this.setupFullMustacheParams(partial, program, undefined, true); + + var indent = partial.indent || ''; + if (this.options.preventIndent && indent) { + this.opcode('appendContent', indent); + indent = ''; + } + + this.opcode('invokePartial', isDynamic, partialName, indent); + this.opcode('append'); + }, + PartialBlockStatement: function PartialBlockStatement(partialBlock) { + this.PartialStatement(partialBlock); + }, + + MustacheStatement: function MustacheStatement(mustache) { + this.SubExpression(mustache); + + if (mustache.escaped && !this.options.noEscape) { + this.opcode('appendEscaped'); + } else { + this.opcode('append'); + } + }, + Decorator: function Decorator(decorator) { + this.DecoratorBlock(decorator); + }, + + ContentStatement: function ContentStatement(content) { + if (content.value) { + this.opcode('appendContent', content.value); + } + }, + + CommentStatement: function CommentStatement() {}, + + SubExpression: function SubExpression(sexpr) { + transformLiteralToPath(sexpr); + var type = this.classifySexpr(sexpr); + + if (type === 'simple') { + this.simpleSexpr(sexpr); + } else if (type === 'helper') { + this.helperSexpr(sexpr); + } else { + this.ambiguousSexpr(sexpr); + } + }, + ambiguousSexpr: function ambiguousSexpr(sexpr, program, inverse) { + var path = sexpr.path, + name = path.parts[0], + isBlock = program != null || inverse != null; + + this.opcode('getContext', path.depth); + + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + + path.strict = true; + this.accept(path); + + this.opcode('invokeAmbiguous', name, isBlock); + }, + + simpleSexpr: function simpleSexpr(sexpr) { + var path = sexpr.path; + path.strict = true; + this.accept(path); + this.opcode('resolvePossibleLambda'); + }, + + helperSexpr: function helperSexpr(sexpr, program, inverse) { + var params = this.setupFullMustacheParams(sexpr, program, inverse), + path = sexpr.path, + name = path.parts[0]; + + if (this.options.knownHelpers[name]) { + this.opcode('invokeKnownHelper', params.length, name); + } else if (this.options.knownHelpersOnly) { + throw new _exception2['default']('You specified knownHelpersOnly, but used the unknown helper ' + name, sexpr); + } else { + path.strict = true; + path.falsy = true; + + this.accept(path); + this.opcode('invokeHelper', params.length, path.original, _ast2['default'].helpers.simpleId(path)); + } + }, + + PathExpression: function PathExpression(path) { + this.addDepth(path.depth); + this.opcode('getContext', path.depth); + + var name = path.parts[0], + scoped = _ast2['default'].helpers.scopedId(path), + blockParamId = !path.depth && !scoped && this.blockParamIndex(name); + + if (blockParamId) { + this.opcode('lookupBlockParam', blockParamId, path.parts); + } else if (!name) { + // Context reference, i.e. `{{foo .}}` or `{{foo ..}}` + this.opcode('pushContext'); + } else if (path.data) { + this.options.data = true; + this.opcode('lookupData', path.depth, path.parts, path.strict); + } else { + this.opcode('lookupOnContext', path.parts, path.falsy, path.strict, scoped); + } + }, + + StringLiteral: function StringLiteral(string) { + this.opcode('pushString', string.value); + }, + + NumberLiteral: function NumberLiteral(number) { + this.opcode('pushLiteral', number.value); + }, + + BooleanLiteral: function BooleanLiteral(bool) { + this.opcode('pushLiteral', bool.value); + }, + + UndefinedLiteral: function UndefinedLiteral() { + this.opcode('pushLiteral', 'undefined'); + }, + + NullLiteral: function NullLiteral() { + this.opcode('pushLiteral', 'null'); + }, + + Hash: function Hash(hash) { + var pairs = hash.pairs, + i = 0, + l = pairs.length; + + this.opcode('pushHash'); + + for (; i < l; i++) { + this.pushParam(pairs[i].value); + } + while (i--) { + this.opcode('assignToHash', pairs[i].key); + } + this.opcode('popHash'); + }, + + // HELPERS + opcode: function opcode(name) { + this.opcodes.push({ + opcode: name, + args: slice.call(arguments, 1), + loc: this.sourceNode[0].loc + }); + }, + + addDepth: function addDepth(depth) { + if (!depth) { + return; + } + + this.useDepths = true; + }, + + classifySexpr: function classifySexpr(sexpr) { + var isSimple = _ast2['default'].helpers.simpleId(sexpr.path); + + var isBlockParam = isSimple && !!this.blockParamIndex(sexpr.path.parts[0]); + + // a mustache is an eligible helper if: + // * its id is simple (a single part, not `this` or `..`) + var isHelper = !isBlockParam && _ast2['default'].helpers.helperExpression(sexpr); + + // if a mustache is an eligible helper but not a definite + // helper, it is ambiguous, and will be resolved in a later + // pass or at runtime. + var isEligible = !isBlockParam && (isHelper || isSimple); + + // if ambiguous, we can possibly resolve the ambiguity now + // An eligible helper is one that does not have a complex path, i.e. `this.foo`, `../foo` etc. + if (isEligible && !isHelper) { + var _name = sexpr.path.parts[0], + options = this.options; + if (options.knownHelpers[_name]) { + isHelper = true; + } else if (options.knownHelpersOnly) { + isEligible = false; + } + } + + if (isHelper) { + return 'helper'; + } else if (isEligible) { + return 'ambiguous'; + } else { + return 'simple'; + } + }, + + pushParams: function pushParams(params) { + for (var i = 0, l = params.length; i < l; i++) { + this.pushParam(params[i]); + } + }, + + pushParam: function pushParam(val) { + var value = val.value != null ? val.value : val.original || ''; + + if (this.stringParams) { + if (value.replace) { + value = value.replace(/^(\.?\.\/)*/g, '').replace(/\//g, '.'); + } + + if (val.depth) { + this.addDepth(val.depth); + } + this.opcode('getContext', val.depth || 0); + this.opcode('pushStringParam', value, val.type); + + if (val.type === 'SubExpression') { + // SubExpressions get evaluated and passed in + // in string params mode. + this.accept(val); + } + } else { + if (this.trackIds) { + var blockParamIndex = undefined; + if (val.parts && !_ast2['default'].helpers.scopedId(val) && !val.depth) { + blockParamIndex = this.blockParamIndex(val.parts[0]); + } + if (blockParamIndex) { + var blockParamChild = val.parts.slice(1).join('.'); + this.opcode('pushId', 'BlockParam', blockParamIndex, blockParamChild); + } else { + value = val.original || value; + if (value.replace) { + value = value.replace(/^this(?:\.|$)/, '').replace(/^\.\//, '').replace(/^\.$/, ''); + } + + this.opcode('pushId', val.type, value); + } + } + this.accept(val); + } + }, + + setupFullMustacheParams: function setupFullMustacheParams(sexpr, program, inverse, omitEmpty) { + var params = sexpr.params; + this.pushParams(params); + + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + + if (sexpr.hash) { + this.accept(sexpr.hash); + } else { + this.opcode('emptyHash', omitEmpty); + } + + return params; + }, + + blockParamIndex: function blockParamIndex(name) { + for (var depth = 0, len = this.options.blockParams.length; depth < len; depth++) { + var blockParams = this.options.blockParams[depth], + param = blockParams && _utils.indexOf(blockParams, name); + if (blockParams && param >= 0) { + return [depth, param]; + } + } + } + }; + + function precompile(input, options, env) { + if (input == null || typeof input !== 'string' && input.type !== 'Program') { + throw new _exception2['default']('You must pass a string or Handlebars AST to Handlebars.precompile. You passed ' + input); + } + + options = options || {}; + if (!('data' in options)) { + options.data = true; + } + if (options.compat) { + options.useDepths = true; + } + + var ast = env.parse(input, options), + environment = new env.Compiler().compile(ast, options); + return new env.JavaScriptCompiler().compile(environment, options); + } + + function compile(input, options, env) { + if (options === undefined) options = {}; + + if (input == null || typeof input !== 'string' && input.type !== 'Program') { + throw new _exception2['default']('You must pass a string or Handlebars AST to Handlebars.compile. You passed ' + input); + } + + options = _utils.extend({}, options); + if (!('data' in options)) { + options.data = true; + } + if (options.compat) { + options.useDepths = true; + } + + var compiled = undefined; + + function compileInput() { + var ast = env.parse(input, options), + environment = new env.Compiler().compile(ast, options), + templateSpec = new env.JavaScriptCompiler().compile(environment, options, undefined, true); + return env.template(templateSpec); + } + + // Template is only compiled on first use and cached after that point. + function ret(context, execOptions) { + if (!compiled) { + compiled = compileInput(); + } + return compiled.call(this, context, execOptions); + } + ret._setup = function (setupOptions) { + if (!compiled) { + compiled = compileInput(); + } + return compiled._setup(setupOptions); + }; + ret._child = function (i, data, blockParams, depths) { + if (!compiled) { + compiled = compileInput(); + } + return compiled._child(i, data, blockParams, depths); + }; + return ret; + } + + function argEquals(a, b) { + if (a === b) { + return true; + } + + if (_utils.isArray(a) && _utils.isArray(b) && a.length === b.length) { + for (var i = 0; i < a.length; i++) { + if (!argEquals(a[i], b[i])) { + return false; + } + } + return true; + } + } + + function transformLiteralToPath(sexpr) { + if (!sexpr.path.parts) { + var literal = sexpr.path; + // Casting to string here to make false and 0 literal values play nicely with the rest + // of the system. + sexpr.path = { + type: 'PathExpression', + data: false, + depth: 0, + parts: [literal.original + ''], + original: literal.original + '', + loc: literal.loc + }; + } + } + +/***/ }), +/* 52 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _Object$keys = __webpack_require__(13)['default']; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _base = __webpack_require__(4); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + var _utils = __webpack_require__(5); + + var _codeGen = __webpack_require__(53); + + var _codeGen2 = _interopRequireDefault(_codeGen); + + function Literal(value) { + this.value = value; + } + + function JavaScriptCompiler() {} + + JavaScriptCompiler.prototype = { + // PUBLIC API: You can override these methods in a subclass to provide + // alternative compiled forms for name lookup and buffering semantics + nameLookup: function nameLookup(parent, name /*, type */) { + return this.internalNameLookup(parent, name); + }, + depthedLookup: function depthedLookup(name) { + return [this.aliasable('container.lookup'), '(depths, "', name, '")']; + }, + + compilerInfo: function compilerInfo() { + var revision = _base.COMPILER_REVISION, + versions = _base.REVISION_CHANGES[revision]; + return [revision, versions]; + }, + + appendToBuffer: function appendToBuffer(source, location, explicit) { + // Force a source as this simplifies the merge logic. + if (!_utils.isArray(source)) { + source = [source]; + } + source = this.source.wrap(source, location); + + if (this.environment.isSimple) { + return ['return ', source, ';']; + } else if (explicit) { + // This is a case where the buffer operation occurs as a child of another + // construct, generally braces. We have to explicitly output these buffer + // operations to ensure that the emitted code goes in the correct location. + return ['buffer += ', source, ';']; + } else { + source.appendToBuffer = true; + return source; + } + }, + + initializeBuffer: function initializeBuffer() { + return this.quotedString(''); + }, + // END PUBLIC API + internalNameLookup: function internalNameLookup(parent, name) { + this.lookupPropertyFunctionIsUsed = true; + return ['lookupProperty(', parent, ',', JSON.stringify(name), ')']; + }, + + lookupPropertyFunctionIsUsed: false, + + compile: function compile(environment, options, context, asObject) { + this.environment = environment; + this.options = options; + this.stringParams = this.options.stringParams; + this.trackIds = this.options.trackIds; + this.precompile = !asObject; + + this.name = this.environment.name; + this.isChild = !!context; + this.context = context || { + decorators: [], + programs: [], + environments: [] + }; + + this.preamble(); + + this.stackSlot = 0; + this.stackVars = []; + this.aliases = {}; + this.registers = { list: [] }; + this.hashes = []; + this.compileStack = []; + this.inlineStack = []; + this.blockParams = []; + + this.compileChildren(environment, options); + + this.useDepths = this.useDepths || environment.useDepths || environment.useDecorators || this.options.compat; + this.useBlockParams = this.useBlockParams || environment.useBlockParams; + + var opcodes = environment.opcodes, + opcode = undefined, + firstLoc = undefined, + i = undefined, + l = undefined; + + for (i = 0, l = opcodes.length; i < l; i++) { + opcode = opcodes[i]; + + this.source.currentLocation = opcode.loc; + firstLoc = firstLoc || opcode.loc; + this[opcode.opcode].apply(this, opcode.args); + } + + // Flush any trailing content that might be pending. + this.source.currentLocation = firstLoc; + this.pushSource(''); + + /* istanbul ignore next */ + if (this.stackSlot || this.inlineStack.length || this.compileStack.length) { + throw new _exception2['default']('Compile completed with content left on stack'); + } + + if (!this.decorators.isEmpty()) { + this.useDecorators = true; + + this.decorators.prepend(['var decorators = container.decorators, ', this.lookupPropertyFunctionVarDeclaration(), ';\n']); + this.decorators.push('return fn;'); + + if (asObject) { + this.decorators = Function.apply(this, ['fn', 'props', 'container', 'depth0', 'data', 'blockParams', 'depths', this.decorators.merge()]); + } else { + this.decorators.prepend('function(fn, props, container, depth0, data, blockParams, depths) {\n'); + this.decorators.push('}\n'); + this.decorators = this.decorators.merge(); + } + } else { + this.decorators = undefined; + } + + var fn = this.createFunctionContext(asObject); + if (!this.isChild) { + var ret = { + compiler: this.compilerInfo(), + main: fn + }; + + if (this.decorators) { + ret.main_d = this.decorators; // eslint-disable-line camelcase + ret.useDecorators = true; + } + + var _context = this.context; + var programs = _context.programs; + var decorators = _context.decorators; + + for (i = 0, l = programs.length; i < l; i++) { + if (programs[i]) { + ret[i] = programs[i]; + if (decorators[i]) { + ret[i + '_d'] = decorators[i]; + ret.useDecorators = true; + } + } + } + + if (this.environment.usePartial) { + ret.usePartial = true; + } + if (this.options.data) { + ret.useData = true; + } + if (this.useDepths) { + ret.useDepths = true; + } + if (this.useBlockParams) { + ret.useBlockParams = true; + } + if (this.options.compat) { + ret.compat = true; + } + + if (!asObject) { + ret.compiler = JSON.stringify(ret.compiler); + + this.source.currentLocation = { start: { line: 1, column: 0 } }; + ret = this.objectLiteral(ret); + + if (options.srcName) { + ret = ret.toStringWithSourceMap({ file: options.destName }); + ret.map = ret.map && ret.map.toString(); + } else { + ret = ret.toString(); + } + } else { + ret.compilerOptions = this.options; + } + + return ret; + } else { + return fn; + } + }, + + preamble: function preamble() { + // track the last context pushed into place to allow skipping the + // getContext opcode when it would be a noop + this.lastContext = 0; + this.source = new _codeGen2['default'](this.options.srcName); + this.decorators = new _codeGen2['default'](this.options.srcName); + }, + + createFunctionContext: function createFunctionContext(asObject) { + // istanbul ignore next + + var _this = this; + + var varDeclarations = ''; + + var locals = this.stackVars.concat(this.registers.list); + if (locals.length > 0) { + varDeclarations += ', ' + locals.join(', '); + } + + // Generate minimizer alias mappings + // + // When using true SourceNodes, this will update all references to the given alias + // as the source nodes are reused in situ. For the non-source node compilation mode, + // aliases will not be used, but this case is already being run on the client and + // we aren't concern about minimizing the template size. + var aliasCount = 0; + _Object$keys(this.aliases).forEach(function (alias) { + var node = _this.aliases[alias]; + if (node.children && node.referenceCount > 1) { + varDeclarations += ', alias' + ++aliasCount + '=' + alias; + node.children[0] = 'alias' + aliasCount; + } + }); + + if (this.lookupPropertyFunctionIsUsed) { + varDeclarations += ', ' + this.lookupPropertyFunctionVarDeclaration(); + } + + var params = ['container', 'depth0', 'helpers', 'partials', 'data']; + + if (this.useBlockParams || this.useDepths) { + params.push('blockParams'); + } + if (this.useDepths) { + params.push('depths'); + } + + // Perform a second pass over the output to merge content when possible + var source = this.mergeSource(varDeclarations); + + if (asObject) { + params.push(source); + + return Function.apply(this, params); + } else { + return this.source.wrap(['function(', params.join(','), ') {\n ', source, '}']); + } + }, + mergeSource: function mergeSource(varDeclarations) { + var isSimple = this.environment.isSimple, + appendOnly = !this.forceBuffer, + appendFirst = undefined, + sourceSeen = undefined, + bufferStart = undefined, + bufferEnd = undefined; + this.source.each(function (line) { + if (line.appendToBuffer) { + if (bufferStart) { + line.prepend(' + '); + } else { + bufferStart = line; + } + bufferEnd = line; + } else { + if (bufferStart) { + if (!sourceSeen) { + appendFirst = true; + } else { + bufferStart.prepend('buffer += '); + } + bufferEnd.add(';'); + bufferStart = bufferEnd = undefined; + } + + sourceSeen = true; + if (!isSimple) { + appendOnly = false; + } + } + }); + + if (appendOnly) { + if (bufferStart) { + bufferStart.prepend('return '); + bufferEnd.add(';'); + } else if (!sourceSeen) { + this.source.push('return "";'); + } + } else { + varDeclarations += ', buffer = ' + (appendFirst ? '' : this.initializeBuffer()); + + if (bufferStart) { + bufferStart.prepend('return buffer + '); + bufferEnd.add(';'); + } else { + this.source.push('return buffer;'); + } + } + + if (varDeclarations) { + this.source.prepend('var ' + varDeclarations.substring(2) + (appendFirst ? '' : ';\n')); + } + + return this.source.merge(); + }, + + lookupPropertyFunctionVarDeclaration: function lookupPropertyFunctionVarDeclaration() { + return '\n lookupProperty = container.lookupProperty || function(parent, propertyName) {\n if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {\n return parent[propertyName];\n }\n return undefined\n }\n '.trim(); + }, + + // [blockValue] + // + // On stack, before: hash, inverse, program, value + // On stack, after: return value of blockHelperMissing + // + // The purpose of this opcode is to take a block of the form + // `{{#this.foo}}...{{/this.foo}}`, resolve the value of `foo`, and + // replace it on the stack with the result of properly + // invoking blockHelperMissing. + blockValue: function blockValue(name) { + var blockHelperMissing = this.aliasable('container.hooks.blockHelperMissing'), + params = [this.contextName(0)]; + this.setupHelperArgs(name, 0, params); + + var blockName = this.popStack(); + params.splice(1, 0, blockName); + + this.push(this.source.functionCall(blockHelperMissing, 'call', params)); + }, + + // [ambiguousBlockValue] + // + // On stack, before: hash, inverse, program, value + // Compiler value, before: lastHelper=value of last found helper, if any + // On stack, after, if no lastHelper: same as [blockValue] + // On stack, after, if lastHelper: value + ambiguousBlockValue: function ambiguousBlockValue() { + // We're being a bit cheeky and reusing the options value from the prior exec + var blockHelperMissing = this.aliasable('container.hooks.blockHelperMissing'), + params = [this.contextName(0)]; + this.setupHelperArgs('', 0, params, true); + + this.flushInline(); + + var current = this.topStack(); + params.splice(1, 0, current); + + this.pushSource(['if (!', this.lastHelper, ') { ', current, ' = ', this.source.functionCall(blockHelperMissing, 'call', params), '}']); + }, + + // [appendContent] + // + // On stack, before: ... + // On stack, after: ... + // + // Appends the string value of `content` to the current buffer + appendContent: function appendContent(content) { + if (this.pendingContent) { + content = this.pendingContent + content; + } else { + this.pendingLocation = this.source.currentLocation; + } + + this.pendingContent = content; + }, + + // [append] + // + // On stack, before: value, ... + // On stack, after: ... + // + // Coerces `value` to a String and appends it to the current buffer. + // + // If `value` is truthy, or 0, it is coerced into a string and appended + // Otherwise, the empty string is appended + append: function append() { + if (this.isInline()) { + this.replaceStack(function (current) { + return [' != null ? ', current, ' : ""']; + }); + + this.pushSource(this.appendToBuffer(this.popStack())); + } else { + var local = this.popStack(); + this.pushSource(['if (', local, ' != null) { ', this.appendToBuffer(local, undefined, true), ' }']); + if (this.environment.isSimple) { + this.pushSource(['else { ', this.appendToBuffer("''", undefined, true), ' }']); + } + } + }, + + // [appendEscaped] + // + // On stack, before: value, ... + // On stack, after: ... + // + // Escape `value` and append it to the buffer + appendEscaped: function appendEscaped() { + this.pushSource(this.appendToBuffer([this.aliasable('container.escapeExpression'), '(', this.popStack(), ')'])); + }, + + // [getContext] + // + // On stack, before: ... + // On stack, after: ... + // Compiler value, after: lastContext=depth + // + // Set the value of the `lastContext` compiler value to the depth + getContext: function getContext(depth) { + this.lastContext = depth; + }, + + // [pushContext] + // + // On stack, before: ... + // On stack, after: currentContext, ... + // + // Pushes the value of the current context onto the stack. + pushContext: function pushContext() { + this.pushStackLiteral(this.contextName(this.lastContext)); + }, + + // [lookupOnContext] + // + // On stack, before: ... + // On stack, after: currentContext[name], ... + // + // Looks up the value of `name` on the current context and pushes + // it onto the stack. + lookupOnContext: function lookupOnContext(parts, falsy, strict, scoped) { + var i = 0; + + if (!scoped && this.options.compat && !this.lastContext) { + // The depthed query is expected to handle the undefined logic for the root level that + // is implemented below, so we evaluate that directly in compat mode + this.push(this.depthedLookup(parts[i++])); + } else { + this.pushContext(); + } + + this.resolvePath('context', parts, i, falsy, strict); + }, + + // [lookupBlockParam] + // + // On stack, before: ... + // On stack, after: blockParam[name], ... + // + // Looks up the value of `parts` on the given block param and pushes + // it onto the stack. + lookupBlockParam: function lookupBlockParam(blockParamId, parts) { + this.useBlockParams = true; + + this.push(['blockParams[', blockParamId[0], '][', blockParamId[1], ']']); + this.resolvePath('context', parts, 1); + }, + + // [lookupData] + // + // On stack, before: ... + // On stack, after: data, ... + // + // Push the data lookup operator + lookupData: function lookupData(depth, parts, strict) { + if (!depth) { + this.pushStackLiteral('data'); + } else { + this.pushStackLiteral('container.data(data, ' + depth + ')'); + } + + this.resolvePath('data', parts, 0, true, strict); + }, + + resolvePath: function resolvePath(type, parts, i, falsy, strict) { + // istanbul ignore next + + var _this2 = this; + + if (this.options.strict || this.options.assumeObjects) { + this.push(strictLookup(this.options.strict && strict, this, parts, type)); + return; + } + + var len = parts.length; + for (; i < len; i++) { + /* eslint-disable no-loop-func */ + this.replaceStack(function (current) { + var lookup = _this2.nameLookup(current, parts[i], type); + // We want to ensure that zero and false are handled properly if the context (falsy flag) + // needs to have the special handling for these values. + if (!falsy) { + return [' != null ? ', lookup, ' : ', current]; + } else { + // Otherwise we can use generic falsy handling + return [' && ', lookup]; + } + }); + /* eslint-enable no-loop-func */ + } + }, + + // [resolvePossibleLambda] + // + // On stack, before: value, ... + // On stack, after: resolved value, ... + // + // If the `value` is a lambda, replace it on the stack by + // the return value of the lambda + resolvePossibleLambda: function resolvePossibleLambda() { + this.push([this.aliasable('container.lambda'), '(', this.popStack(), ', ', this.contextName(0), ')']); + }, + + // [pushStringParam] + // + // On stack, before: ... + // On stack, after: string, currentContext, ... + // + // This opcode is designed for use in string mode, which + // provides the string value of a parameter along with its + // depth rather than resolving it immediately. + pushStringParam: function pushStringParam(string, type) { + this.pushContext(); + this.pushString(type); + + // If it's a subexpression, the string result + // will be pushed after this opcode. + if (type !== 'SubExpression') { + if (typeof string === 'string') { + this.pushString(string); + } else { + this.pushStackLiteral(string); + } + } + }, + + emptyHash: function emptyHash(omitEmpty) { + if (this.trackIds) { + this.push('{}'); // hashIds + } + if (this.stringParams) { + this.push('{}'); // hashContexts + this.push('{}'); // hashTypes + } + this.pushStackLiteral(omitEmpty ? 'undefined' : '{}'); + }, + pushHash: function pushHash() { + if (this.hash) { + this.hashes.push(this.hash); + } + this.hash = { values: {}, types: [], contexts: [], ids: [] }; + }, + popHash: function popHash() { + var hash = this.hash; + this.hash = this.hashes.pop(); + + if (this.trackIds) { + this.push(this.objectLiteral(hash.ids)); + } + if (this.stringParams) { + this.push(this.objectLiteral(hash.contexts)); + this.push(this.objectLiteral(hash.types)); + } + + this.push(this.objectLiteral(hash.values)); + }, + + // [pushString] + // + // On stack, before: ... + // On stack, after: quotedString(string), ... + // + // Push a quoted version of `string` onto the stack + pushString: function pushString(string) { + this.pushStackLiteral(this.quotedString(string)); + }, + + // [pushLiteral] + // + // On stack, before: ... + // On stack, after: value, ... + // + // Pushes a value onto the stack. This operation prevents + // the compiler from creating a temporary variable to hold + // it. + pushLiteral: function pushLiteral(value) { + this.pushStackLiteral(value); + }, + + // [pushProgram] + // + // On stack, before: ... + // On stack, after: program(guid), ... + // + // Push a program expression onto the stack. This takes + // a compile-time guid and converts it into a runtime-accessible + // expression. + pushProgram: function pushProgram(guid) { + if (guid != null) { + this.pushStackLiteral(this.programExpression(guid)); + } else { + this.pushStackLiteral(null); + } + }, + + // [registerDecorator] + // + // On stack, before: hash, program, params..., ... + // On stack, after: ... + // + // Pops off the decorator's parameters, invokes the decorator, + // and inserts the decorator into the decorators list. + registerDecorator: function registerDecorator(paramSize, name) { + var foundDecorator = this.nameLookup('decorators', name, 'decorator'), + options = this.setupHelperArgs(name, paramSize); + + this.decorators.push(['fn = ', this.decorators.functionCall(foundDecorator, '', ['fn', 'props', 'container', options]), ' || fn;']); + }, + + // [invokeHelper] + // + // On stack, before: hash, inverse, program, params..., ... + // On stack, after: result of helper invocation + // + // Pops off the helper's parameters, invokes the helper, + // and pushes the helper's return value onto the stack. + // + // If the helper is not found, `helperMissing` is called. + invokeHelper: function invokeHelper(paramSize, name, isSimple) { + var nonHelper = this.popStack(), + helper = this.setupHelper(paramSize, name); + + var possibleFunctionCalls = []; + + if (isSimple) { + // direct call to helper + possibleFunctionCalls.push(helper.name); + } + // call a function from the input object + possibleFunctionCalls.push(nonHelper); + if (!this.options.strict) { + possibleFunctionCalls.push(this.aliasable('container.hooks.helperMissing')); + } + + var functionLookupCode = ['(', this.itemsSeparatedBy(possibleFunctionCalls, '||'), ')']; + var functionCall = this.source.functionCall(functionLookupCode, 'call', helper.callParams); + this.push(functionCall); + }, + + itemsSeparatedBy: function itemsSeparatedBy(items, separator) { + var result = []; + result.push(items[0]); + for (var i = 1; i < items.length; i++) { + result.push(separator, items[i]); + } + return result; + }, + // [invokeKnownHelper] + // + // On stack, before: hash, inverse, program, params..., ... + // On stack, after: result of helper invocation + // + // This operation is used when the helper is known to exist, + // so a `helperMissing` fallback is not required. + invokeKnownHelper: function invokeKnownHelper(paramSize, name) { + var helper = this.setupHelper(paramSize, name); + this.push(this.source.functionCall(helper.name, 'call', helper.callParams)); + }, + + // [invokeAmbiguous] + // + // On stack, before: hash, inverse, program, params..., ... + // On stack, after: result of disambiguation + // + // This operation is used when an expression like `{{foo}}` + // is provided, but we don't know at compile-time whether it + // is a helper or a path. + // + // This operation emits more code than the other options, + // and can be avoided by passing the `knownHelpers` and + // `knownHelpersOnly` flags at compile-time. + invokeAmbiguous: function invokeAmbiguous(name, helperCall) { + this.useRegister('helper'); + + var nonHelper = this.popStack(); + + this.emptyHash(); + var helper = this.setupHelper(0, name, helperCall); + + var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper'); + + var lookup = ['(', '(helper = ', helperName, ' || ', nonHelper, ')']; + if (!this.options.strict) { + lookup[0] = '(helper = '; + lookup.push(' != null ? helper : ', this.aliasable('container.hooks.helperMissing')); + } + + this.push(['(', lookup, helper.paramsInit ? ['),(', helper.paramsInit] : [], '),', '(typeof helper === ', this.aliasable('"function"'), ' ? ', this.source.functionCall('helper', 'call', helper.callParams), ' : helper))']); + }, + + // [invokePartial] + // + // On stack, before: context, ... + // On stack after: result of partial invocation + // + // This operation pops off a context, invokes a partial with that context, + // and pushes the result of the invocation back. + invokePartial: function invokePartial(isDynamic, name, indent) { + var params = [], + options = this.setupParams(name, 1, params); + + if (isDynamic) { + name = this.popStack(); + delete options.name; + } + + if (indent) { + options.indent = JSON.stringify(indent); + } + options.helpers = 'helpers'; + options.partials = 'partials'; + options.decorators = 'container.decorators'; + + if (!isDynamic) { + params.unshift(this.nameLookup('partials', name, 'partial')); + } else { + params.unshift(name); + } + + if (this.options.compat) { + options.depths = 'depths'; + } + options = this.objectLiteral(options); + params.push(options); + + this.push(this.source.functionCall('container.invokePartial', '', params)); + }, + + // [assignToHash] + // + // On stack, before: value, ..., hash, ... + // On stack, after: ..., hash, ... + // + // Pops a value off the stack and assigns it to the current hash + assignToHash: function assignToHash(key) { + var value = this.popStack(), + context = undefined, + type = undefined, + id = undefined; + + if (this.trackIds) { + id = this.popStack(); + } + if (this.stringParams) { + type = this.popStack(); + context = this.popStack(); + } + + var hash = this.hash; + if (context) { + hash.contexts[key] = context; + } + if (type) { + hash.types[key] = type; + } + if (id) { + hash.ids[key] = id; + } + hash.values[key] = value; + }, + + pushId: function pushId(type, name, child) { + if (type === 'BlockParam') { + this.pushStackLiteral('blockParams[' + name[0] + '].path[' + name[1] + ']' + (child ? ' + ' + JSON.stringify('.' + child) : '')); + } else if (type === 'PathExpression') { + this.pushString(name); + } else if (type === 'SubExpression') { + this.pushStackLiteral('true'); + } else { + this.pushStackLiteral('null'); + } + }, + + // HELPERS + + compiler: JavaScriptCompiler, + + compileChildren: function compileChildren(environment, options) { + var children = environment.children, + child = undefined, + compiler = undefined; + + for (var i = 0, l = children.length; i < l; i++) { + child = children[i]; + compiler = new this.compiler(); // eslint-disable-line new-cap + + var existing = this.matchExistingProgram(child); + + if (existing == null) { + this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children + var index = this.context.programs.length; + child.index = index; + child.name = 'program' + index; + this.context.programs[index] = compiler.compile(child, options, this.context, !this.precompile); + this.context.decorators[index] = compiler.decorators; + this.context.environments[index] = child; + + this.useDepths = this.useDepths || compiler.useDepths; + this.useBlockParams = this.useBlockParams || compiler.useBlockParams; + child.useDepths = this.useDepths; + child.useBlockParams = this.useBlockParams; + } else { + child.index = existing.index; + child.name = 'program' + existing.index; + + this.useDepths = this.useDepths || existing.useDepths; + this.useBlockParams = this.useBlockParams || existing.useBlockParams; + } + } + }, + matchExistingProgram: function matchExistingProgram(child) { + for (var i = 0, len = this.context.environments.length; i < len; i++) { + var environment = this.context.environments[i]; + if (environment && environment.equals(child)) { + return environment; + } + } + }, + + programExpression: function programExpression(guid) { + var child = this.environment.children[guid], + programParams = [child.index, 'data', child.blockParams]; + + if (this.useBlockParams || this.useDepths) { + programParams.push('blockParams'); + } + if (this.useDepths) { + programParams.push('depths'); + } + + return 'container.program(' + programParams.join(', ') + ')'; + }, + + useRegister: function useRegister(name) { + if (!this.registers[name]) { + this.registers[name] = true; + this.registers.list.push(name); + } + }, + + push: function push(expr) { + if (!(expr instanceof Literal)) { + expr = this.source.wrap(expr); + } + + this.inlineStack.push(expr); + return expr; + }, + + pushStackLiteral: function pushStackLiteral(item) { + this.push(new Literal(item)); + }, + + pushSource: function pushSource(source) { + if (this.pendingContent) { + this.source.push(this.appendToBuffer(this.source.quotedString(this.pendingContent), this.pendingLocation)); + this.pendingContent = undefined; + } + + if (source) { + this.source.push(source); + } + }, + + replaceStack: function replaceStack(callback) { + var prefix = ['('], + stack = undefined, + createdStack = undefined, + usedLiteral = undefined; + + /* istanbul ignore next */ + if (!this.isInline()) { + throw new _exception2['default']('replaceStack on non-inline'); + } + + // We want to merge the inline statement into the replacement statement via ',' + var top = this.popStack(true); + + if (top instanceof Literal) { + // Literals do not need to be inlined + stack = [top.value]; + prefix = ['(', stack]; + usedLiteral = true; + } else { + // Get or create the current stack name for use by the inline + createdStack = true; + var _name = this.incrStack(); + + prefix = ['((', this.push(_name), ' = ', top, ')']; + stack = this.topStack(); + } + + var item = callback.call(this, stack); + + if (!usedLiteral) { + this.popStack(); + } + if (createdStack) { + this.stackSlot--; + } + this.push(prefix.concat(item, ')')); + }, + + incrStack: function incrStack() { + this.stackSlot++; + if (this.stackSlot > this.stackVars.length) { + this.stackVars.push('stack' + this.stackSlot); + } + return this.topStackName(); + }, + topStackName: function topStackName() { + return 'stack' + this.stackSlot; + }, + flushInline: function flushInline() { + var inlineStack = this.inlineStack; + this.inlineStack = []; + for (var i = 0, len = inlineStack.length; i < len; i++) { + var entry = inlineStack[i]; + /* istanbul ignore if */ + if (entry instanceof Literal) { + this.compileStack.push(entry); + } else { + var stack = this.incrStack(); + this.pushSource([stack, ' = ', entry, ';']); + this.compileStack.push(stack); + } + } + }, + isInline: function isInline() { + return this.inlineStack.length; + }, + + popStack: function popStack(wrapped) { + var inline = this.isInline(), + item = (inline ? this.inlineStack : this.compileStack).pop(); + + if (!wrapped && item instanceof Literal) { + return item.value; + } else { + if (!inline) { + /* istanbul ignore next */ + if (!this.stackSlot) { + throw new _exception2['default']('Invalid stack pop'); + } + this.stackSlot--; + } + return item; + } + }, + + topStack: function topStack() { + var stack = this.isInline() ? this.inlineStack : this.compileStack, + item = stack[stack.length - 1]; + + /* istanbul ignore if */ + if (item instanceof Literal) { + return item.value; + } else { + return item; + } + }, + + contextName: function contextName(context) { + if (this.useDepths && context) { + return 'depths[' + context + ']'; + } else { + return 'depth' + context; + } + }, + + quotedString: function quotedString(str) { + return this.source.quotedString(str); + }, + + objectLiteral: function objectLiteral(obj) { + return this.source.objectLiteral(obj); + }, + + aliasable: function aliasable(name) { + var ret = this.aliases[name]; + if (ret) { + ret.referenceCount++; + return ret; + } + + ret = this.aliases[name] = this.source.wrap(name); + ret.aliasable = true; + ret.referenceCount = 1; + + return ret; + }, + + setupHelper: function setupHelper(paramSize, name, blockHelper) { + var params = [], + paramsInit = this.setupHelperArgs(name, paramSize, params, blockHelper); + var foundHelper = this.nameLookup('helpers', name, 'helper'), + callContext = this.aliasable(this.contextName(0) + ' != null ? ' + this.contextName(0) + ' : (container.nullContext || {})'); + + return { + params: params, + paramsInit: paramsInit, + name: foundHelper, + callParams: [callContext].concat(params) + }; + }, + + setupParams: function setupParams(helper, paramSize, params) { + var options = {}, + contexts = [], + types = [], + ids = [], + objectArgs = !params, + param = undefined; + + if (objectArgs) { + params = []; + } + + options.name = this.quotedString(helper); + options.hash = this.popStack(); + + if (this.trackIds) { + options.hashIds = this.popStack(); + } + if (this.stringParams) { + options.hashTypes = this.popStack(); + options.hashContexts = this.popStack(); + } + + var inverse = this.popStack(), + program = this.popStack(); + + // Avoid setting fn and inverse if neither are set. This allows + // helpers to do a check for `if (options.fn)` + if (program || inverse) { + options.fn = program || 'container.noop'; + options.inverse = inverse || 'container.noop'; + } + + // The parameters go on to the stack in order (making sure that they are evaluated in order) + // so we need to pop them off the stack in reverse order + var i = paramSize; + while (i--) { + param = this.popStack(); + params[i] = param; + + if (this.trackIds) { + ids[i] = this.popStack(); + } + if (this.stringParams) { + types[i] = this.popStack(); + contexts[i] = this.popStack(); + } + } + + if (objectArgs) { + options.args = this.source.generateArray(params); + } + + if (this.trackIds) { + options.ids = this.source.generateArray(ids); + } + if (this.stringParams) { + options.types = this.source.generateArray(types); + options.contexts = this.source.generateArray(contexts); + } + + if (this.options.data) { + options.data = 'data'; + } + if (this.useBlockParams) { + options.blockParams = 'blockParams'; + } + return options; + }, + + setupHelperArgs: function setupHelperArgs(helper, paramSize, params, useRegister) { + var options = this.setupParams(helper, paramSize, params); + options.loc = JSON.stringify(this.source.currentLocation); + options = this.objectLiteral(options); + if (useRegister) { + this.useRegister('options'); + params.push('options'); + return ['options=', options]; + } else if (params) { + params.push(options); + return ''; + } else { + return options; + } + } + }; + + (function () { + var reservedWords = ('break else new var' + ' case finally return void' + ' catch for switch while' + ' continue function this with' + ' default if throw' + ' delete in try' + ' do instanceof typeof' + ' abstract enum int short' + ' boolean export interface static' + ' byte extends long super' + ' char final native synchronized' + ' class float package throws' + ' const goto private transient' + ' debugger implements protected volatile' + ' double import public let yield await' + ' null true false').split(' '); + + var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {}; + + for (var i = 0, l = reservedWords.length; i < l; i++) { + compilerWords[reservedWords[i]] = true; + } + })(); + + /** + * @deprecated May be removed in the next major version + */ + JavaScriptCompiler.isValidJavaScriptVariableName = function (name) { + return !JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name); + }; + + function strictLookup(requireTerminal, compiler, parts, type) { + var stack = compiler.popStack(), + i = 0, + len = parts.length; + if (requireTerminal) { + len--; + } + + for (; i < len; i++) { + stack = compiler.nameLookup(stack, parts[i], type); + } + + if (requireTerminal) { + return [compiler.aliasable('container.strict'), '(', stack, ', ', compiler.quotedString(parts[i]), ', ', JSON.stringify(compiler.source.currentLocation), ' )']; + } else { + return stack; + } + } + + exports['default'] = JavaScriptCompiler; + module.exports = exports['default']; + +/***/ }), +/* 53 */ +/***/ (function(module, exports, __webpack_require__) { + + /* global define */ + 'use strict'; + + var _Object$keys = __webpack_require__(13)['default']; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + var SourceNode = undefined; + + try { + /* istanbul ignore next */ + if (false) { + // We don't support this in AMD environments. For these environments, we asusme that + // they are running on the browser and thus have no need for the source-map library. + var SourceMap = require('source-map'); + SourceNode = SourceMap.SourceNode; + } + } catch (err) {} + /* NOP */ + + /* istanbul ignore if: tested but not covered in istanbul due to dist build */ + if (!SourceNode) { + SourceNode = function (line, column, srcFile, chunks) { + this.src = ''; + if (chunks) { + this.add(chunks); + } + }; + /* istanbul ignore next */ + SourceNode.prototype = { + add: function add(chunks) { + if (_utils.isArray(chunks)) { + chunks = chunks.join(''); + } + this.src += chunks; + }, + prepend: function prepend(chunks) { + if (_utils.isArray(chunks)) { + chunks = chunks.join(''); + } + this.src = chunks + this.src; + }, + toStringWithSourceMap: function toStringWithSourceMap() { + return { code: this.toString() }; + }, + toString: function toString() { + return this.src; + } + }; + } + + function castChunk(chunk, codeGen, loc) { + if (_utils.isArray(chunk)) { + var ret = []; + + for (var i = 0, len = chunk.length; i < len; i++) { + ret.push(codeGen.wrap(chunk[i], loc)); + } + return ret; + } else if (typeof chunk === 'boolean' || typeof chunk === 'number') { + // Handle primitives that the SourceNode will throw up on + return chunk + ''; + } + return chunk; + } + + function CodeGen(srcFile) { + this.srcFile = srcFile; + this.source = []; + } + + CodeGen.prototype = { + isEmpty: function isEmpty() { + return !this.source.length; + }, + prepend: function prepend(source, loc) { + this.source.unshift(this.wrap(source, loc)); + }, + push: function push(source, loc) { + this.source.push(this.wrap(source, loc)); + }, + + merge: function merge() { + var source = this.empty(); + this.each(function (line) { + source.add([' ', line, '\n']); + }); + return source; + }, + + each: function each(iter) { + for (var i = 0, len = this.source.length; i < len; i++) { + iter(this.source[i]); + } + }, + + empty: function empty() { + var loc = this.currentLocation || { start: {} }; + return new SourceNode(loc.start.line, loc.start.column, this.srcFile); + }, + wrap: function wrap(chunk) { + var loc = arguments.length <= 1 || arguments[1] === undefined ? this.currentLocation || { start: {} } : arguments[1]; + + if (chunk instanceof SourceNode) { + return chunk; + } + + chunk = castChunk(chunk, this, loc); + + return new SourceNode(loc.start.line, loc.start.column, this.srcFile, chunk); + }, + + functionCall: function functionCall(fn, type, params) { + params = this.generateList(params); + return this.wrap([fn, type ? '.' + type + '(' : '(', params, ')']); + }, + + quotedString: function quotedString(str) { + return '"' + (str + '').replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 + .replace(/\u2029/g, '\\u2029') + '"'; + }, + + objectLiteral: function objectLiteral(obj) { + // istanbul ignore next + + var _this = this; + + var pairs = []; + + _Object$keys(obj).forEach(function (key) { + var value = castChunk(obj[key], _this); + if (value !== 'undefined') { + pairs.push([_this.quotedString(key), ':', value]); + } + }); + + var ret = this.generateList(pairs); + ret.prepend('{'); + ret.add('}'); + return ret; + }, + + generateList: function generateList(entries) { + var ret = this.empty(); + + for (var i = 0, len = entries.length; i < len; i++) { + if (i) { + ret.add(','); + } + + ret.add(castChunk(entries[i], this)); + } + + return ret; + }, + + generateArray: function generateArray(entries) { + var ret = this.generateList(entries); + ret.prepend('['); + ret.add(']'); + + return ret; + } + }; + + exports['default'] = CodeGen; + module.exports = exports['default']; + +/***/ }) +/******/ ]) +}); +; \ No newline at end of file diff --git a/www/test/1.5.1/test/lib/morphdom-umd.js b/www/test/1.5.1/test/lib/morphdom-umd.js new file mode 100644 index 00000000..21fdc3a9 --- /dev/null +++ b/www/test/1.5.1/test/lib/morphdom-umd.js @@ -0,0 +1,763 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.morphdom = factory()); +}(this, function () { 'use strict'; + + var DOCUMENT_FRAGMENT_NODE = 11; + + function morphAttrs(fromNode, toNode) { + var toNodeAttrs = toNode.attributes; + var attr; + var attrName; + var attrNamespaceURI; + var attrValue; + var fromValue; + + // document-fragments dont have attributes so lets not do anything + if (toNode.nodeType === DOCUMENT_FRAGMENT_NODE || fromNode.nodeType === DOCUMENT_FRAGMENT_NODE) { + return; + } + + // update attributes on original DOM element + for (var i = toNodeAttrs.length - 1; i >= 0; i--) { + attr = toNodeAttrs[i]; + attrName = attr.name; + attrNamespaceURI = attr.namespaceURI; + attrValue = attr.value; + + if (attrNamespaceURI) { + attrName = attr.localName || attrName; + fromValue = fromNode.getAttributeNS(attrNamespaceURI, attrName); + + if (fromValue !== attrValue) { + if (attr.prefix === 'xmlns'){ + attrName = attr.name; // It's not allowed to set an attribute with the XMLNS namespace without specifying the `xmlns` prefix + } + fromNode.setAttributeNS(attrNamespaceURI, attrName, attrValue); + } + } else { + fromValue = fromNode.getAttribute(attrName); + + if (fromValue !== attrValue) { + fromNode.setAttribute(attrName, attrValue); + } + } + } + + // Remove any extra attributes found on the original DOM element that + // weren't found on the target element. + var fromNodeAttrs = fromNode.attributes; + + for (var d = fromNodeAttrs.length - 1; d >= 0; d--) { + attr = fromNodeAttrs[d]; + attrName = attr.name; + attrNamespaceURI = attr.namespaceURI; + + if (attrNamespaceURI) { + attrName = attr.localName || attrName; + + if (!toNode.hasAttributeNS(attrNamespaceURI, attrName)) { + fromNode.removeAttributeNS(attrNamespaceURI, attrName); + } + } else { + if (!toNode.hasAttribute(attrName)) { + fromNode.removeAttribute(attrName); + } + } + } + } + + var range; // Create a range object for efficently rendering strings to elements. + var NS_XHTML = 'http://www.w3.org/1999/xhtml'; + + var doc = typeof document === 'undefined' ? undefined : document; + var HAS_TEMPLATE_SUPPORT = !!doc && 'content' in doc.createElement('template'); + var HAS_RANGE_SUPPORT = !!doc && doc.createRange && 'createContextualFragment' in doc.createRange(); + + function createFragmentFromTemplate(str) { + var template = doc.createElement('template'); + template.innerHTML = str; + return template.content.childNodes[0]; + } + + function createFragmentFromRange(str) { + if (!range) { + range = doc.createRange(); + range.selectNode(doc.body); + } + + var fragment = range.createContextualFragment(str); + return fragment.childNodes[0]; + } + + function createFragmentFromWrap(str) { + var fragment = doc.createElement('body'); + fragment.innerHTML = str; + return fragment.childNodes[0]; + } + + /** + * This is about the same + * var html = new DOMParser().parseFromString(str, 'text/html'); + * return html.body.firstChild; + * + * @method toElement + * @param {String} str + */ + function toElement(str) { + str = str.trim(); + if (HAS_TEMPLATE_SUPPORT) { + // avoid restrictions on content for things like `Hi` which + // createContextualFragment doesn't support + //