Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update parseInterval to handle "0" correctly #1835

Merged
merged 6 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 15 additions & 13 deletions src/htmx.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,18 +131,20 @@ return (function () {

function parseInterval(str) {
if (str == undefined) {
return undefined
return undefined;
}

let interval = NaN;
if (str.slice(-2) == "ms") {
return parseFloat(str.slice(0,-2)) || undefined
}
if (str.slice(-1) == "s") {
return (parseFloat(str.slice(0,-1)) * 1000) || undefined
}
if (str.slice(-1) == "m") {
return (parseFloat(str.slice(0,-1)) * 1000 * 60) || undefined
interval = parseFloat(str.slice(0, -2));
} else if (str.slice(-1) == "s") {
interval = parseFloat(str.slice(0, -1)) * 1000;
} else if (str.slice(-1) == "m") {
interval = parseFloat(str.slice(0, -1)) * 1000 * 60;
} else {
interval = parseFloat(str);
}
return parseFloat(str) || undefined
return isNaN(interval) ? undefined : interval;
}

/**
Expand Down Expand Up @@ -1482,14 +1484,14 @@ return (function () {
return;
}

if (triggerSpec.throttle) {
if (triggerSpec.throttle > 0) {
if (!elementData.throttle) {
handler(elt, evt);
elementData.throttle = setTimeout(function () {
elementData.throttle = null;
}, triggerSpec.throttle);
}
} else if (triggerSpec.delay) {
} else if (triggerSpec.delay > 0) {
elementData.delayed = setTimeout(function() { handler(elt, evt) }, triggerSpec.delay);
} else {
triggerEvent(elt, 'htmx:trigger')
Expand Down Expand Up @@ -1766,7 +1768,7 @@ return (function () {
handler(elt);
}
}
if (delay) {
if (delay > 0) {
setTimeout(load, delay);
} else {
load();
Expand Down Expand Up @@ -1825,7 +1827,7 @@ return (function () {
if (!maybeFilterEvent(triggerSpec, elt, makeEvent("load", {elt: elt}))) {
loadImmediately(elt, handler, nodeData, triggerSpec.delay);
}
} else if (triggerSpec.pollInterval) {
} else if (triggerSpec.pollInterval !== undefined) {
nodeData.polling = true;
processPolling(elt, handler, triggerSpec);
} else {
Expand Down
49 changes: 47 additions & 2 deletions test/attributes/hx-swap.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,27 +197,43 @@ describe("hx-swap attribute", function(){
swapSpec(make("<div hx-swap='innerHTML'/>")).swapDelay.should.equal(0)
swapSpec(make("<div hx-swap='innerHTML'/>")).settleDelay.should.equal(0) // set to 0 in tests
swapSpec(make("<div hx-swap='innerHTML swap:10'/>")).swapDelay.should.equal(10)
swapSpec(make("<div hx-swap='innerHTML swap:0'/>")).swapDelay.should.equal(0)
swapSpec(make("<div hx-swap='innerHTML swap:0ms'/>")).swapDelay.should.equal(0)
swapSpec(make("<div hx-swap='innerHTML settle:10'/>")).settleDelay.should.equal(10)
swapSpec(make("<div hx-swap='innerHTML settle:0'/>")).settleDelay.should.equal(0)
swapSpec(make("<div hx-swap='innerHTML settle:0s'/>")).settleDelay.should.equal(0)
swapSpec(make("<div hx-swap='innerHTML swap:10 settle:11'/>")).swapDelay.should.equal(10)
swapSpec(make("<div hx-swap='innerHTML swap:10 settle:11'/>")).settleDelay.should.equal(11)
swapSpec(make("<div hx-swap='innerHTML settle:11 swap:10'/>")).swapDelay.should.equal(10)
swapSpec(make("<div hx-swap='innerHTML settle:11 swap:10'/>")).settleDelay.should.equal(11)
swapSpec(make("<div hx-swap='innerHTML settle:0 swap:0'/>")).settleDelay.should.equal(0)
swapSpec(make("<div hx-swap='innerHTML settle:0 swap:0'/>")).settleDelay.should.equal(0)
swapSpec(make("<div hx-swap='innerHTML settle:0s swap:0ms'/>")).settleDelay.should.equal(0)
swapSpec(make("<div hx-swap='innerHTML settle:0s swap:0ms'/>")).settleDelay.should.equal(0)
swapSpec(make("<div hx-swap='innerHTML nonsense settle:11 swap:10'/>")).settleDelay.should.equal(11)
swapSpec(make("<div hx-swap='innerHTML nonsense settle:11 swap:10 '/>")).settleDelay.should.equal(11)

swapSpec(make("<div hx-swap='swap:10'/>")).swapStyle.should.equal("innerHTML")
swapSpec(make("<div hx-swap='swap:10'/>")).swapDelay.should.equal(10)
swapSpec(make("<div hx-swap='swap:0'/>")).swapDelay.should.equal(0);
swapSpec(make("<div hx-swap='swap:0s'/>")).swapDelay.should.equal(0);

swapSpec(make("<div hx-swap='settle:10'/>")).swapStyle.should.equal("innerHTML")
swapSpec(make("<div hx-swap='settle:10'/>")).settleDelay.should.equal(10)

swapSpec(make("<div hx-swap='settle:0'/>")).settleDelay.should.equal(0)
swapSpec(make("<div hx-swap='settle:0s'/>")).settleDelay.should.equal(0)

swapSpec(make("<div hx-swap='swap:10 settle:11'/>")).swapStyle.should.equal("innerHTML")
swapSpec(make("<div hx-swap='swap:10 settle:11'/>")).swapDelay.should.equal(10)
swapSpec(make("<div hx-swap='swap:10 settle:11'/>")).settleDelay.should.equal(11)
swapSpec(make("<div hx-swap='swap:0s settle:0'/>")).swapDelay.should.equal(0)
swapSpec(make("<div hx-swap='swap:0s settle:0'/>")).settleDelay.should.equal(0)

swapSpec(make("<div hx-swap='settle:11 swap:10'/>")).swapStyle.should.equal("innerHTML")
swapSpec(make("<div hx-swap='settle:11 swap:10'/>")).swapDelay.should.equal(10)
swapSpec(make("<div hx-swap='settle:11 swap:10'/>")).settleDelay.should.equal(11)
swapSpec(make("<div hx-swap='settle:0s swap:10'/>")).swapDelay.should.equal(10)
swapSpec(make("<div hx-swap='settle:0s swap:10'/>")).settleDelay.should.equal(0)

swapSpec(make("<div hx-swap='customstyle settle:11 swap:10'/>")).swapStyle.should.equal("customstyle")
})
Expand All @@ -234,6 +250,17 @@ describe("hx-swap attribute", function(){
}, 30);
});

it("works immediately with no swap delay", function (done) {
this.server.respondWith("GET", "/test", "Clicked!");
var div = make(
"<div hx-get='/test' hx-swap='innerHTML swap:0ms'></div>"
);
div.click();
this.server.respond();
div.innerText.should.equal("Clicked!");
done();
});

it('works with a settle delay', function(done) {
this.server.respondWith("GET", "/test", "<div id='d1' class='foo' hx-get='/test' hx-swap='outerHTML settle:10ms'></div>");
var div = make("<div id='d1' hx-get='/test' hx-swap='outerHTML settle:10ms'></div>");
Expand All @@ -246,6 +273,24 @@ describe("hx-swap attribute", function(){
}, 30);
});

it("works with no settle delay", function (done) {
this.server.respondWith(
"GET",
"/test",
"<div id='d1' class='foo' hx-get='/test' hx-swap='outerHTML settle:0ms'></div>"
);
var div = make(
"<div id='d1' hx-get='/test' hx-swap='outerHTML settle:0ms'></div>"
);
div.click();
this.server.respond();
div.classList.contains("foo").should.equal(false);
setTimeout(function () {
byId("d1").classList.contains("foo").should.equal(true);
done();
}, 30);
});

it('swap outerHTML properly w/ data-* prefix', function()
{
this.server.respondWith("GET", "/test", '<a id="a1" data-hx-get="/test2">Click Me</a>');
Expand Down
84 changes: 82 additions & 2 deletions test/attributes/hx-trigger.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,14 +211,20 @@ describe("hx-trigger attribute", function(){
var specExamples = {
"": [{trigger: 'click'}],
"every 1s": [{trigger: 'every', pollInterval: 1000}],
"every 0s": [{trigger: 'every', pollInterval: 0}],
"every 0ms": [{trigger: 'every', pollInterval: 0}],
"click": [{trigger: 'click'}],
"customEvent": [{trigger: 'customEvent'}],
"event changed": [{trigger: 'event', changed: true}],
"event once": [{trigger: 'event', once: true}],
"event delay:1s": [{trigger: 'event', delay: 1000}],
"event throttle:1s": [{trigger: 'event', throttle: 1000}],
"event delay:1s, foo": [{trigger: 'event', delay: 1000}, {trigger: 'foo'}],
"event throttle:0s": [{trigger: 'event', throttle: 0}],
"event throttle:0ms": [{trigger: 'event', throttle: 0}],
"event throttle:1s, foo": [{trigger: 'event', throttle: 1000}, {trigger: 'foo'}],
"event delay:1s": [{trigger: 'event', delay: 1000}],
"event delay:1s, foo": [{trigger: 'event', delay: 1000}, {trigger: 'foo'}],
"event delay:0s, foo": [{trigger: 'event', delay: 0}, {trigger: 'foo'}],
"event delay:0ms, foo": [{trigger: 'event', delay: 0}, {trigger: 'foo'}],
"event changed once delay:1s": [{trigger: 'event', changed: true, once: true, delay: 1000}],
"event1,event2": [{trigger: 'event1'}, {trigger: 'event2'}],
"event1, event2": [{trigger: 'event1'}, {trigger: 'event2'}],
Expand Down Expand Up @@ -398,6 +404,18 @@ describe("hx-trigger attribute", function(){
}, 100);
})

// Don't actually do this!
it("polling works every 0ms", function (done) {
this.server.respondWith("GET", "/test", "Called!");
var div = make('<div hx-get="/test" hx-trigger="every 0ms">Not Called</div>');
this.server.autoRespond = true;
this.server.autoRespondAfter = 0;
setTimeout(function () {
div.innerHTML.should.equal("Called!");
done();
}, 100);
});

it('bad condition issues error', function(){
this.server.respondWith("GET", "/test", "Called!");
var div = make('<div hx-get="/test" hx-trigger="evt[a.b]">Not Called</div>');
Expand Down Expand Up @@ -648,6 +666,37 @@ describe("hx-trigger attribute", function(){
}, 50);
});

it("A throttle of 0 does not multiple requests from happening", function (done) {
var requests = 0;
var server = this.server;
server.respondWith("GET", "/test", function (xhr) {
requests++;
xhr.respond(200, {}, "Requests: " + requests);
});
server.respondWith("GET", "/bar", "bar");
var div = make(
"<div hx-trigger='click throttle:0ms' hx-get='/test'></div>"
);

div.click();
server.respond();
div.innerText.should.equal("Requests: 1");

div.click();
server.respond();
div.innerText.should.equal("Requests: 2");

div.click();
server.respond();
div.innerText.should.equal("Requests: 3");

div.click();
server.respond();
div.innerText.should.equal("Requests: 4");

done()
});

it('delay delays the request', function(done)
{
var requests = 0;
Expand Down Expand Up @@ -684,6 +733,37 @@ describe("hx-trigger attribute", function(){
}, 50);
});

it("A 0 delay does not delay the request", function (done) {
var requests = 0;
this.server.respondWith("GET", "/test", function (xhr) {
requests++;
xhr.respond(200, {}, "Requests: " + requests);
});
this.server.respondWith("GET", "/bar", "bar");
var div = make(
"<div hx-trigger='click delay:0ms' hx-get='/test'></div>"
);

div.click();
this.server.respond();
div.innerText.should.equal("Requests: 1");

div.click();
this.server.respond();
div.innerText.should.equal("Requests: 2");

div.click();
this.server.respond();
div.innerText.should.equal("Requests: 3");

div.click();
this.server.respond();
div.innerText.should.equal("Requests: 4");

done();
});


it('requests are queued with last one winning by default', function()
{
var requests = 0;
Expand Down
11 changes: 8 additions & 3 deletions test/core/internals.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,14 @@ describe("Core htmx internals Tests", function() {
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("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("0ms")).to.be.equal(0);
chai.expect(htmx.parseInterval("0s")).to.be.equal(0);
chai.expect(htmx.parseInterval("0m")).to.be.equal(0);
chai.expect(htmx.parseInterval("0")).to.be.equal(0);
chai.expect(htmx.parseInterval("5")).to.be.equal(5);

chai.expect(htmx.parseInterval(null)).to.be.undefined
chai.expect(htmx.parseInterval("")).to.be.undefined
Expand Down