mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-10-02 23:35:13 +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) {
|
function shouldCancel(elt) {
|
||||||
return elt.tagName === "FORM" ||
|
return elt.tagName === "FORM" ||
|
||||||
(matches(elt, 'input[type="submit"], button') && closest(elt, 'form') !== null) ||
|
(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) {
|
function ignoreBoostedAnchorCtrlClick(elt, evt) {
|
||||||
@ -1053,8 +1053,13 @@ return (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function processWebSocketSource(elt, wssSource, retryCount) {
|
function processWebSocketSource(elt, wssSource, retryCount) {
|
||||||
if (wssSource.indexOf("ws:") !== 0 && wssSource.indexOf("wss:") !== 0) {
|
if (wssSource.indexOf("/") == 0) { // complete absolute paths only
|
||||||
wssSource = "wss:" + wssSource;
|
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);
|
var socket = htmx.createWebSocket(wssSource);
|
||||||
socket.onerror = function (e) {
|
socket.onerror = function (e) {
|
||||||
@ -1553,22 +1558,21 @@ return (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addRequestIndicatorClasses(elt) {
|
function addRequestIndicatorClasses(elt) {
|
||||||
mutateRequestIndicatorClasses(elt, "add");
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeRequestIndicatorClasses(elt) {
|
|
||||||
mutateRequestIndicatorClasses(elt, "remove");
|
|
||||||
}
|
|
||||||
|
|
||||||
function mutateRequestIndicatorClasses(elt, action) {
|
|
||||||
var indicator = getClosestAttributeValue(elt, 'hx-indicator');
|
var indicator = getClosestAttributeValue(elt, 'hx-indicator');
|
||||||
if (indicator) {
|
if (indicator) {
|
||||||
var indicators = querySelectorAllExt(elt, indicator);
|
var indicators = querySelectorAllExt(elt, indicator);
|
||||||
} else {
|
} else {
|
||||||
indicators = [elt];
|
indicators = [elt];
|
||||||
}
|
}
|
||||||
forEach(indicators, function(ic) {
|
forEach(indicators, function (ic) {
|
||||||
ic.classList[action].call(ic.classList, htmx.config.requestClass);
|
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));
|
triggerErrorEvent(elt, 'htmx:onLoadError', mergeObjects({error:e}, responseInfo));
|
||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
removeRequestIndicatorClasses(elt);
|
removeRequestIndicatorClasses(indicators);
|
||||||
var finalElt = getInternalData(elt).replacedWith || elt;
|
var finalElt = elt;
|
||||||
|
if (!bodyContains(elt)) {
|
||||||
|
finalElt = getInternalData(target).replacedWith || target;
|
||||||
|
}
|
||||||
triggerEvent(finalElt, 'htmx:afterRequest', responseInfo);
|
triggerEvent(finalElt, 'htmx:afterRequest', responseInfo);
|
||||||
triggerEvent(finalElt, 'htmx:afterOnLoad', responseInfo);
|
triggerEvent(finalElt, 'htmx:afterOnLoad', responseInfo);
|
||||||
endRequestLock();
|
endRequestLock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xhr.onerror = function () {
|
xhr.onerror = function () {
|
||||||
removeRequestIndicatorClasses(elt);
|
removeRequestIndicatorClasses(indicators);
|
||||||
triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo);
|
var finalElt = elt;
|
||||||
triggerErrorEvent(elt, 'htmx:sendError', responseInfo);
|
if (!bodyContains(elt)) {
|
||||||
|
finalElt = getInternalData(target).replacedWith || target;
|
||||||
|
}
|
||||||
|
triggerErrorEvent(finalElt, 'htmx:afterRequest', responseInfo);
|
||||||
|
triggerErrorEvent(finalElt, 'htmx:sendError', responseInfo);
|
||||||
endRequestLock();
|
endRequestLock();
|
||||||
}
|
}
|
||||||
xhr.onabort = function() {
|
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();
|
endRequestLock();
|
||||||
}
|
}
|
||||||
if(!triggerEvent(elt, 'htmx:beforeRequest', responseInfo)) return endRequestLock();
|
if(!triggerEvent(elt, 'htmx:beforeRequest', responseInfo)) return endRequestLock();
|
||||||
addRequestIndicatorClasses(elt);
|
var indicators = addRequestIndicatorClasses(elt);
|
||||||
|
|
||||||
forEach(['loadstart', 'loadend', 'progress', 'abort'], function(eventName) {
|
forEach(['loadstart', 'loadend', 'progress', 'abort'], function(eventName) {
|
||||||
forEach([xhr, xhr.upload], function (target) {
|
forEach([xhr, xhr.upload], function (target) {
|
||||||
|
@ -62,4 +62,16 @@ describe("hx-indicator attribute", function(){
|
|||||||
btn.classList.contains("htmx-request").should.equal(false);
|
btn.classList.contains("htmx-request").should.equal(false);
|
||||||
div.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);
|
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;
|
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(){
|
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
|
// 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>
|
||||||
<li>
|
<li>
|
||||||
<a href="manual/aborting-request.html">Aborting Request Test</a>
|
<a href="manual/aborting-request.html">Aborting Request Test</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="manual/preload.html">Preload Extension</a>
|
<a href="manual/preload.html">Preload Extension</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
<li>
|
||||||
|
<a href="manual/manual-perf.html">Manual Perf Test</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2>Mocha Test Suite</h2>
|
<h2>Mocha Test Suite</h2>
|
||||||
<a href="index.html">[ALL]</a>
|
<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)
|
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:
|
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
|
* `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`])
|
of the event specified by [`hx-trigger`])
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user