mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-10-02 15:25:26 +00:00
Merge branch 'master' of https://github.com/bigskysoftware/htmx into ws-reconnect
This commit is contained in:
commit
fc57045198
57
src/htmx.js
57
src/htmx.js
@ -923,7 +923,7 @@ return (function () {
|
||||
function shouldCancel(elt) {
|
||||
return elt.tagName === "FORM" ||
|
||||
(matches(elt, 'input[type="submit"], button') && closest(elt, 'form') !== null) ||
|
||||
(elt.tagName === "A" && elt.href && elt.href.indexOf('#') !== 0);
|
||||
(elt.tagName === "A" && elt.href && elt.getAttribute('href').indexOf('#') !== 0);
|
||||
}
|
||||
|
||||
function ignoreBoostedAnchorCtrlClick(elt, evt) {
|
||||
@ -1053,8 +1053,13 @@ return (function () {
|
||||
}
|
||||
|
||||
function processWebSocketSource(elt, wssSource, retryCount) {
|
||||
if (wssSource.indexOf("ws:") !== 0 && wssSource.indexOf("wss:") !== 0) {
|
||||
wssSource = "wss:" + wssSource;
|
||||
if (wssSource.indexOf("/") == 0) { // complete absolute paths only
|
||||
var base_part = location.hostname + (location.port ? ':'+location.port: '');
|
||||
if (location.protocol == 'https:') {
|
||||
wssSource = "wss://" + base_part + wssSource;
|
||||
} else if (location.protocol == 'http:') {
|
||||
wssSource = "ws://" + base_part + wssSource;
|
||||
}
|
||||
}
|
||||
var socket = htmx.createWebSocket(wssSource);
|
||||
socket.onerror = function (e) {
|
||||
@ -1553,22 +1558,21 @@ return (function () {
|
||||
}
|
||||
|
||||
function addRequestIndicatorClasses(elt) {
|
||||
mutateRequestIndicatorClasses(elt, "add");
|
||||
}
|
||||
|
||||
function removeRequestIndicatorClasses(elt) {
|
||||
mutateRequestIndicatorClasses(elt, "remove");
|
||||
}
|
||||
|
||||
function mutateRequestIndicatorClasses(elt, action) {
|
||||
var indicator = getClosestAttributeValue(elt, 'hx-indicator');
|
||||
if (indicator) {
|
||||
var indicators = querySelectorAllExt(elt, indicator);
|
||||
} else {
|
||||
indicators = [elt];
|
||||
}
|
||||
forEach(indicators, function(ic) {
|
||||
ic.classList[action].call(ic.classList, htmx.config.requestClass);
|
||||
forEach(indicators, function (ic) {
|
||||
ic.classList["add"].call(ic.classList, htmx.config.requestClass);
|
||||
});
|
||||
return indicators;
|
||||
}
|
||||
|
||||
function removeRequestIndicatorClasses(indicators) {
|
||||
forEach(indicators, function (ic) {
|
||||
ic.classList["remove"].call(ic.classList, htmx.config.requestClass);
|
||||
});
|
||||
}
|
||||
|
||||
@ -2087,25 +2091,38 @@ return (function () {
|
||||
triggerErrorEvent(elt, 'htmx:onLoadError', mergeObjects({error:e}, responseInfo));
|
||||
throw e;
|
||||
} finally {
|
||||
removeRequestIndicatorClasses(elt);
|
||||
var finalElt = getInternalData(elt).replacedWith || elt;
|
||||
removeRequestIndicatorClasses(indicators);
|
||||
var finalElt = elt;
|
||||
if (!bodyContains(elt)) {
|
||||
finalElt = getInternalData(target).replacedWith || target;
|
||||
}
|
||||
triggerEvent(finalElt, 'htmx:afterRequest', responseInfo);
|
||||
triggerEvent(finalElt, 'htmx:afterOnLoad', responseInfo);
|
||||
endRequestLock();
|
||||
}
|
||||
}
|
||||
xhr.onerror = function () {
|
||||
removeRequestIndicatorClasses(elt);
|
||||
triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo);
|
||||
triggerErrorEvent(elt, 'htmx:sendError', responseInfo);
|
||||
removeRequestIndicatorClasses(indicators);
|
||||
var finalElt = elt;
|
||||
if (!bodyContains(elt)) {
|
||||
finalElt = getInternalData(target).replacedWith || target;
|
||||
}
|
||||
triggerErrorEvent(finalElt, 'htmx:afterRequest', responseInfo);
|
||||
triggerErrorEvent(finalElt, 'htmx:sendError', responseInfo);
|
||||
endRequestLock();
|
||||
}
|
||||
xhr.onabort = function() {
|
||||
removeRequestIndicatorClasses(elt);
|
||||
removeRequestIndicatorClasses(indicators);
|
||||
var finalElt = elt;
|
||||
if (!bodyContains(elt)) {
|
||||
finalElt = getInternalData(target).replacedWith || target;
|
||||
}
|
||||
triggerErrorEvent(finalElt, 'htmx:afterRequest', responseInfo);
|
||||
triggerErrorEvent(finalElt, 'htmx:sendAbort', responseInfo);
|
||||
endRequestLock();
|
||||
}
|
||||
if(!triggerEvent(elt, 'htmx:beforeRequest', responseInfo)) return endRequestLock();
|
||||
addRequestIndicatorClasses(elt);
|
||||
var indicators = addRequestIndicatorClasses(elt);
|
||||
|
||||
forEach(['loadstart', 'loadend', 'progress', 'abort'], function(eventName) {
|
||||
forEach([xhr, xhr.upload], function (target) {
|
||||
|
@ -62,4 +62,16 @@ describe("hx-indicator attribute", function(){
|
||||
btn.classList.contains("htmx-request").should.equal(false);
|
||||
div.classList.contains("htmx-request").should.equal(false);
|
||||
});
|
||||
|
||||
it('is removed when initiating element is removed from the DOM', function()
|
||||
{
|
||||
this.server.respondWith("GET", "/test", "Clicked!");
|
||||
var indicator = make('<div id="ind1">Indicator</div>')
|
||||
var div = make('<div id="d1" hx-target="this" hx-indicator="#ind1"><button id="b1" hx-get="/test">Click Me!</button></div>')
|
||||
var btn = byId("b1");
|
||||
btn.click();
|
||||
indicator.classList.contains("htmx-request").should.equal(true);
|
||||
this.server.respond();
|
||||
indicator.classList.contains("htmx-request").should.equal(false);
|
||||
});
|
||||
})
|
||||
|
@ -346,5 +346,25 @@ describe("Core htmx Events", function() {
|
||||
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, {}, "<button>Bar</button>");
|
||||
});
|
||||
var div = make("<div hx-target='this'><button id='b1' hx-post='/test' hx-swap='outerHTML'>Foo</button></div>");
|
||||
var button = byId('b1');
|
||||
button.click();
|
||||
this.server.respond();
|
||||
should.equal(called, true);
|
||||
} finally {
|
||||
htmx.off("htmx:afterRequest", handler);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
@ -29,25 +29,6 @@ describe("Core htmx perf Tests", function() {
|
||||
return result;
|
||||
}
|
||||
|
||||
it("DOM processing should be fast", function(){
|
||||
this.server.respondWith("GET", "/test", "Clicked!");
|
||||
|
||||
// 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 str = stringRepeat("<div>", 30) + stringRepeat("<div><div><span><button hx-get='/test'> Test Get Button </button></span></div></div>\n", 500) + stringRepeat("</div>", 30);
|
||||
var start = performance.now();
|
||||
var stuff = make(str);
|
||||
var end = performance.now();
|
||||
var timeInMs = end - start;
|
||||
|
||||
// make sure the DOM actually processed
|
||||
var firstBtn = stuff.querySelector("button");
|
||||
firstBtn.click();
|
||||
this.server.respond();
|
||||
firstBtn.innerHTML.should.equal("Clicked!");
|
||||
|
||||
chai.assert(timeInMs < 100, "Should take less than 100ms on most platforms, took: " + timeInMs + "ms");
|
||||
})
|
||||
|
||||
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
|
||||
|
@ -53,11 +53,14 @@
|
||||
</li>
|
||||
<li>
|
||||
<a href="manual/aborting-request.html">Aborting Request Test</a>
|
||||
</li>
|
||||
<li>
|
||||
</li>
|
||||
<li>
|
||||
<a href="manual/preload.html">Preload Extension</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="manual/manual-perf.html">Manual Perf Test</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Mocha Test Suite</h2>
|
||||
<a href="index.html">[ALL]</a>
|
||||
|
81
test/manual/manual-perf.html
Normal file
81
test/manual/manual-perf.html
Normal file
@ -0,0 +1,81 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Mocha Tests</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="../../node_modules/mocha/mocha.css" />
|
||||
</head>
|
||||
<body style="padding:20px;font-family: sans-serif">
|
||||
<div id="mocha"></div>
|
||||
<script src="../../node_modules/chai/chai.js"></script>
|
||||
<script src="../../node_modules/mocha/mocha.js"></script>
|
||||
<script src="../../node_modules/sinon/pkg/sinon.js"></script>
|
||||
<script src="../../src/htmx.js"></script>
|
||||
<script class="mocha-init">
|
||||
mocha.setup('bdd');
|
||||
mocha.checkLeaks();
|
||||
should = chai.should();
|
||||
</script>
|
||||
|
||||
<script src="../util/util.js"></script>
|
||||
<script>
|
||||
describe("Manual Perf Tests", function() {
|
||||
|
||||
beforeEach(function () {
|
||||
this.server = makeServer();
|
||||
clearWorkArea();
|
||||
});
|
||||
afterEach(function () {
|
||||
this.server.restore();
|
||||
clearWorkArea();
|
||||
});
|
||||
|
||||
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("DOM processing should be fast", function(){
|
||||
this.server.respondWith("GET", "/test", "Clicked!");
|
||||
|
||||
// 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 str = stringRepeat("<div>", 30) + stringRepeat("<div><div><span><button hx-get='/test'> Test Get Button </button></span></div></div>\n", 500) + stringRepeat("</div>", 30);
|
||||
var start = performance.now();
|
||||
var stuff = make(str);
|
||||
var end = performance.now();
|
||||
var timeInMs = end - start;
|
||||
|
||||
// make sure the DOM actually processed
|
||||
var firstBtn = stuff.querySelector("button");
|
||||
firstBtn.click();
|
||||
this.server.respond();
|
||||
firstBtn.innerHTML.should.equal("Clicked!");
|
||||
|
||||
chai.assert(timeInMs < 100, "Should take less than 100ms on most platforms, took: " + timeInMs + "ms");
|
||||
})
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<script class="mocha-exec">
|
||||
mocha.run();
|
||||
</script>
|
||||
<em>Work Area</em>
|
||||
<hr/>
|
||||
<div id="work-area" hx-history-elt>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -8,7 +8,8 @@ title: </> htmx - hx-ws
|
||||
The `hx-ws` allows you to work with [Web Sockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications)
|
||||
directly from HTML. The value of the attribute can be one or more of the following, separated by commas:
|
||||
|
||||
* `connect:<url>` or `connect:<prefix>:<url>` - A URL to establish an `WebSocket` connection against. Prefixes `ws` or `wss` can optionally be specified. If not specified, HTMX defaults to add the `wss` prefix to the url.
|
||||
* `connect:<url>` or `connect:<prefix>:<url>` - A URL to establish an `WebSocket` connection against.
|
||||
* Prefixes `ws` or `wss` can optionally be specified. If not specified, HTMX defaults to add the location's scheme-type, host and port to have browsers send cookies via websockets.
|
||||
* `send` - Sends a message to the nearest websocket based on the trigger value for the element (either the natural event
|
||||
of the event specified by [`hx-trigger`])
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user