more release prep

This commit is contained in:
carson 2020-05-25 22:04:11 -07:00
parent 666b4cbaa8
commit ba03597a65
12 changed files with 313 additions and 125 deletions

82
dist/htmx.js vendored
View File

@ -368,23 +368,26 @@ return (function () {
});
}
function handleAttributes(parentNode, fragment, settleInfo) {
forEach(fragment.querySelectorAll("[id]"), function (newNode) {
var oldNode = parentNode.querySelector(newNode.tagName + "[id=" + newNode.id + "]")
if (oldNode && oldNode !== parentNode) {
var newAttributes = newNode.cloneNode();
cloneAttributes(newNode, oldNode);
settleInfo.tasks.push(function () {
cloneAttributes(newNode, newAttributes);
});
}
});
}
function insertNodesBefore(parentNode, insertBefore, fragment, settleInfo) {
handleAttributes(parentNode, fragment, settleInfo);
while(fragment.childNodes.length > 0){
var child = fragment.firstChild;
parentNode.insertBefore(child, insertBefore);
if (child.nodeType !== Node.TEXT_NODE) {
var newAttributes = null;
if (child.id) {
var originalNode = parentNode.querySelector(child.tagName + "[id=" + child.id + "]");
if (originalNode && originalNode !== parentNode) {
newAttributes = child.cloneNode();
cloneAttributes(child, originalNode);
}
}
settleInfo.tasks.push(function(){
if (newAttributes) {
cloneAttributes(child, newAttributes);
}
processNode(child);
triggerEvent(child, 'load.htmx', {});
});
@ -519,6 +522,8 @@ return (function () {
if (trigger === "every")
return {trigger: 'every', pollInterval: parseInterval(tokens[1])};
if (trigger.indexOf("sse:") === 0)
return {trigger: 'sse', sseEvent: trigger.substr(4)};
var triggerSpec = {trigger: trigger};
for (var i = 1; i < tokens.length; i++) {
@ -662,22 +667,18 @@ return (function () {
function processWebSocketInfo(elt, nodeData, info) {
var values = info.split(",");
for (var i = 0; i < values.length; i++) {
var value = removeWhiteSpace(values[i]);
if (value.indexOf("source:") === 0) {
processWebSocketSource(elt, value.substr(7));
var value = splitOnWhitespace(values[i]);
if (value[0] === "connect") {
processWebSocketSource(elt, value[1]);
}
if (value.indexOf("send:") === 0) {
processWebSocketSend(elt, value.substr(5));
if (value[0] === "send") {
processWebSocketSend(elt);
}
}
}
function processWebSocketSource(elt, wssSource) {
var detail = {
protocols:[]
};
triggerEvent(elt, "initWebSocket.htmx", detail);
var socket = new WebSocket("wss:" + wssSource, detail.protocols);
var socket = htmx.createWebSocket(wssSource);
socket.onerror = function (e) {
triggerErrorEvent(elt, "wsError.htmx", {error:e, socket:socket});
maybeCloseWebSocketSource(elt);
@ -712,13 +713,13 @@ return (function () {
}
}
function processWebSocketSend(elt, eventName) {
function processWebSocketSend(elt) {
var webSocketSourceElt = getClosestMatch(elt, function (parent) {
return getInternalData(parent).webSocket != null;
});
if (webSocketSourceElt) {
var webSocket = getInternalData(webSocketSourceElt).webSocket;
elt.addEventListener(eventName, function (evt) {
elt.addEventListener(getTriggerSpecs(elt)[0].trigger, function (evt) {
var headers = getHeaders(elt, webSocketSourceElt, null, elt);
var rawParameters = getInputValues(elt, 'post');
var filteredParameters = filterValues(rawParameters, elt);
@ -743,18 +744,15 @@ return (function () {
function processSSEInfo(elt, nodeData, info) {
var values = info.split(",");
for (var i = 0; i < values.length; i++) {
var value = removeWhiteSpace(values[i]);
if (value.indexOf("source:") === 0) {
processSSESource(elt, value.substr(7));
}
if (value.indexOf("trigger:") && nodeData.verb) {
processSSETrigger(elt, nodeData.verb, nodeData.path, value.substr(8));
var value = splitOnWhitespace(values[i]);
if (value[0] === "connect") {
processSSESource(elt, value[1]);
}
}
}
function processSSESource(elt, sseSrc) {
var source = new EventSource(sseSrc, detail.config);
var source = htmx.createEventSource(sseSrc);
source.onerror = function (e) {
triggerErrorEvent(elt, "sseError.htmx", {error:e, source:source});
maybeCloseSSESource(elt);
@ -769,7 +767,7 @@ return (function () {
if (sseSourceElt) {
var sseEventSource = getInternalData(sseSourceElt).sseEventSource;
var sseListener = function () {
if (!maybeCloseSSESource(sseEventSource)) {
if (!maybeCloseSSESource(sseSourceElt)) {
if (bodyContains(elt)) {
issueAjaxRequest(elt, verb, path);
} else {
@ -807,7 +805,9 @@ return (function () {
nodeData.path = path;
nodeData.verb = verb;
triggerSpecs.forEach(function(triggerSpec) {
if (triggerSpec.trigger === "revealed") {
if (triggerSpec.sseEvent) {
processSSETrigger(elt, verb, path, triggerSpec.sseEvent);
} else if (triggerSpec.trigger === "revealed") {
initScrollHandler();
maybeReveal(elt);
} else if (triggerSpec.trigger === "load") {
@ -835,13 +835,15 @@ return (function () {
if (!explicitAction && getClosestAttributeValue(elt, "hx-boost") === "true") {
boostElement(elt, nodeData, triggerSpecs);
}
var sseInfo = getAttributeValue(elt, 'hx-sse');
if (sseInfo) {
processSSEInfo(elt, nodeData, sseInfo);
}
var sseInfo = getAttributeValue(elt, 'hx-ws');
if (sseInfo) {
processWebSocketInfo(elt, nodeData, sseInfo);
var wsInfo = getAttributeValue(elt, 'hx-ws');
if (wsInfo) {
processWebSocketInfo(elt, nodeData, wsInfo);
}
triggerEvent(elt, "processedNode.htmx");
}
@ -1487,7 +1489,7 @@ return (function () {
function getMetaConfig() {
var element = getDocument().querySelector('meta[name="htmx-config"]');
if (element) {
return eval(element.content);
return JSON.parse(element.content);
} else {
return null;
}
@ -1539,7 +1541,13 @@ return (function () {
includeIndicatorStyles:true
},
parseInterval:parseInterval,
_:internalEval
_:internalEval,
createEventSource: function(url){
return new EventSource(url, {withCredentials:true})
},
createWebSocket: function(url){
return new WebSocket(url, []);
}
}
}
)()

2
dist/htmx.min.js vendored

File diff suppressed because one or more lines are too long

BIN
dist/htmx.min.js.gz vendored

Binary file not shown.

View File

@ -523,11 +523,10 @@ Htmx offers some officially supported extensions that are tested against the htm
| [`json-enc`](/extensions/json-enc) | use JSON encoding in the body of requests, rather than the default `x-www-form-urlencoded`
| [`morphdom-swap`](/extensions/morphdom-swap) | an extension for using the [morphdom](https://github.com/patrick-steele-idem/morphdom) library as the swapping mechanism in htmx.
| [`client-side-templates`](/extensions/client-side-templates) | support for client side template processing of JSON responses
| [`debug`](/extensions/debug) | an extension for debugging of a particular element using htmx
| [`path-deps`](/extensions/path-deps) | an extension for expressing path-based dependencies [similar to intercoolerjs](http://intercoolerjs.org/docs.html#dependencies)
| [`class-tools`](/extensions/class-tools) | an extension for manipulating timed addition and removal of classes on HTML elements
See the [references page](/reference#extensions) for a complete list.
See the [Extensions](/extensions#list) for a complete list.
## <a name="events"></a> [Events & Logging](#events)

View File

@ -47,3 +47,16 @@ would say:
Note that the `hx-ext` tag may be placed on parent elements if you want a plugin to apply to an entire swath of the dom,
and on the `body` tag for it to apply to all htmx requests.
## <a name="list"></a> [Extensions Reference](#list)
The following extensions that are tested and distributed with htmx:
| Extension | Description
|-----------|-------------
| [`json-enc`](/extensions/json-enc) | use JSON encoding in the body of requests, rather than the default `x-www-form-urlencoded`
| [`morphdom-swap`](/extensions/morphdom-swap) | an extension for using the [morphdom](https://github.com/patrick-steele-idem/morphdom) library as the swapping mechanism in htmx.
| [`client-side-templates`](/extensions/client-side-templates) | support for client side template processing of JSON responses
| [`debug`](/extensions/debug) | an extension for debugging of a particular element using htmx
| [`path-deps`](/extensions/path-deps) | an extension for expressing path-based dependencies [similar to intercoolerjs](http://intercoolerjs.org/docs.html#dependencies)
| [`class-tools`](/extensions/class-tools) | an extension for manipulating timed addition and removal of classes on HTML elements

View File

@ -368,23 +368,26 @@ return (function () {
});
}
function handleAttributes(parentNode, fragment, settleInfo) {
forEach(fragment.querySelectorAll("[id]"), function (newNode) {
var oldNode = parentNode.querySelector(newNode.tagName + "[id=" + newNode.id + "]")
if (oldNode && oldNode !== parentNode) {
var newAttributes = newNode.cloneNode();
cloneAttributes(newNode, oldNode);
settleInfo.tasks.push(function () {
cloneAttributes(newNode, newAttributes);
});
}
});
}
function insertNodesBefore(parentNode, insertBefore, fragment, settleInfo) {
handleAttributes(parentNode, fragment, settleInfo);
while(fragment.childNodes.length > 0){
var child = fragment.firstChild;
parentNode.insertBefore(child, insertBefore);
if (child.nodeType !== Node.TEXT_NODE) {
var newAttributes = null;
if (child.id) {
var originalNode = parentNode.querySelector(child.tagName + "[id=" + child.id + "]");
if (originalNode && originalNode !== parentNode) {
newAttributes = child.cloneNode();
cloneAttributes(child, originalNode);
}
}
settleInfo.tasks.push(function(){
if (newAttributes) {
cloneAttributes(child, newAttributes);
}
processNode(child);
triggerEvent(child, 'load.htmx', {});
});
@ -519,6 +522,8 @@ return (function () {
if (trigger === "every")
return {trigger: 'every', pollInterval: parseInterval(tokens[1])};
if (trigger.indexOf("sse:") === 0)
return {trigger: 'sse', sseEvent: trigger.substr(4)};
var triggerSpec = {trigger: trigger};
for (var i = 1; i < tokens.length; i++) {
@ -662,22 +667,18 @@ return (function () {
function processWebSocketInfo(elt, nodeData, info) {
var values = info.split(",");
for (var i = 0; i < values.length; i++) {
var value = removeWhiteSpace(values[i]);
if (value.indexOf("source:") === 0) {
processWebSocketSource(elt, value.substr(7));
var value = splitOnWhitespace(values[i]);
if (value[0] === "connect") {
processWebSocketSource(elt, value[1]);
}
if (value.indexOf("send:") === 0) {
processWebSocketSend(elt, value.substr(5));
if (value[0] === "send") {
processWebSocketSend(elt);
}
}
}
function processWebSocketSource(elt, wssSource) {
var detail = {
protocols:[]
};
triggerEvent(elt, "initWebSocket.htmx", detail);
var socket = new WebSocket("wss:" + wssSource, detail.protocols);
var socket = htmx.createWebSocket(wssSource);
socket.onerror = function (e) {
triggerErrorEvent(elt, "wsError.htmx", {error:e, socket:socket});
maybeCloseWebSocketSource(elt);
@ -712,13 +713,13 @@ return (function () {
}
}
function processWebSocketSend(elt, eventName) {
function processWebSocketSend(elt) {
var webSocketSourceElt = getClosestMatch(elt, function (parent) {
return getInternalData(parent).webSocket != null;
});
if (webSocketSourceElt) {
var webSocket = getInternalData(webSocketSourceElt).webSocket;
elt.addEventListener(eventName, function (evt) {
elt.addEventListener(getTriggerSpecs(elt)[0].trigger, function (evt) {
var headers = getHeaders(elt, webSocketSourceElt, null, elt);
var rawParameters = getInputValues(elt, 'post');
var filteredParameters = filterValues(rawParameters, elt);
@ -743,18 +744,15 @@ return (function () {
function processSSEInfo(elt, nodeData, info) {
var values = info.split(",");
for (var i = 0; i < values.length; i++) {
var value = removeWhiteSpace(values[i]);
if (value.indexOf("source:") === 0) {
processSSESource(elt, value.substr(7));
}
if (value.indexOf("trigger:") && nodeData.verb) {
processSSETrigger(elt, nodeData.verb, nodeData.path, value.substr(8));
var value = splitOnWhitespace(values[i]);
if (value[0] === "connect") {
processSSESource(elt, value[1]);
}
}
}
function processSSESource(elt, sseSrc) {
var source = new EventSource(sseSrc, detail.config);
var source = htmx.createEventSource(sseSrc);
source.onerror = function (e) {
triggerErrorEvent(elt, "sseError.htmx", {error:e, source:source});
maybeCloseSSESource(elt);
@ -769,7 +767,7 @@ return (function () {
if (sseSourceElt) {
var sseEventSource = getInternalData(sseSourceElt).sseEventSource;
var sseListener = function () {
if (!maybeCloseSSESource(sseEventSource)) {
if (!maybeCloseSSESource(sseSourceElt)) {
if (bodyContains(elt)) {
issueAjaxRequest(elt, verb, path);
} else {
@ -807,7 +805,9 @@ return (function () {
nodeData.path = path;
nodeData.verb = verb;
triggerSpecs.forEach(function(triggerSpec) {
if (triggerSpec.trigger === "revealed") {
if (triggerSpec.sseEvent) {
processSSETrigger(elt, verb, path, triggerSpec.sseEvent);
} else if (triggerSpec.trigger === "revealed") {
initScrollHandler();
maybeReveal(elt);
} else if (triggerSpec.trigger === "load") {
@ -835,13 +835,15 @@ return (function () {
if (!explicitAction && getClosestAttributeValue(elt, "hx-boost") === "true") {
boostElement(elt, nodeData, triggerSpecs);
}
var sseInfo = getAttributeValue(elt, 'hx-sse');
if (sseInfo) {
processSSEInfo(elt, nodeData, sseInfo);
}
var sseInfo = getAttributeValue(elt, 'hx-ws');
if (sseInfo) {
processWebSocketInfo(elt, nodeData, sseInfo);
var wsInfo = getAttributeValue(elt, 'hx-ws');
if (wsInfo) {
processWebSocketInfo(elt, nodeData, wsInfo);
}
triggerEvent(elt, "processedNode.htmx");
}
@ -1487,7 +1489,7 @@ return (function () {
function getMetaConfig() {
var element = getDocument().querySelector('meta[name="htmx-config"]');
if (element) {
return eval(element.content);
return JSON.parse(element.content);
} else {
return null;
}
@ -1539,7 +1541,13 @@ return (function () {
includeIndicatorStyles:true
},
parseInterval:parseInterval,
_:internalEval
_:internalEval,
createEventSource: function(url){
return new EventSource(url, {withCredentials:true})
},
createWebSocket: function(url){
return new WebSocket(url, []);
}
}
}
)()

View File

@ -93,13 +93,3 @@ title: </> htmx - Attributes
| [`swapError.htmx`](/events#swapError.htmx) | triggered when an error occurs during the swap phase
| [`targetError.htmx`](/events#targetError.htmx) | triggered when an invalid target is specified
## <a name="extensions"></a> [Extensions Reference](#extensions)
| Extension | Description
|-----------|-------------
| [`json-enc`](/extensions/json-enc) | use JSON encoding in the body of requests, rather than the default `x-www-form-urlencoded`
| [`morphdom-swap`](/extensions/morphdom-swap) | an extension for using the [morphdom](https://github.com/patrick-steele-idem/morphdom) library as the swapping mechanism in htmx.
| [`client-side-templates`](/extensions/client-side-templates) | support for client side template processing of JSON responses
| [`debug`](/extensions/debug) | an extension for debugging of a particular element using htmx
| [`path-deps`](/extensions/path-deps) | an extension for expressing path-based dependencies [similar to intercoolerjs](http://intercoolerjs.org/docs.html#dependencies)
| [`class-tools`](/extensions/class-tools) | an extension for manipulating timed addition and removal of classes on HTML elements

View File

@ -368,23 +368,26 @@ return (function () {
});
}
function handleAttributes(parentNode, fragment, settleInfo) {
forEach(fragment.querySelectorAll("[id]"), function (newNode) {
var oldNode = parentNode.querySelector(newNode.tagName + "[id=" + newNode.id + "]")
if (oldNode && oldNode !== parentNode) {
var newAttributes = newNode.cloneNode();
cloneAttributes(newNode, oldNode);
settleInfo.tasks.push(function () {
cloneAttributes(newNode, newAttributes);
});
}
});
}
function insertNodesBefore(parentNode, insertBefore, fragment, settleInfo) {
handleAttributes(parentNode, fragment, settleInfo);
while(fragment.childNodes.length > 0){
var child = fragment.firstChild;
parentNode.insertBefore(child, insertBefore);
if (child.nodeType !== Node.TEXT_NODE) {
var newAttributes = null;
if (child.id) {
var originalNode = parentNode.querySelector(child.tagName + "[id=" + child.id + "]");
if (originalNode && originalNode !== parentNode) {
newAttributes = child.cloneNode();
cloneAttributes(child, originalNode);
}
}
settleInfo.tasks.push(function(){
if (newAttributes) {
cloneAttributes(child, newAttributes);
}
processNode(child);
triggerEvent(child, 'load.htmx', {});
});
@ -519,6 +522,8 @@ return (function () {
if (trigger === "every")
return {trigger: 'every', pollInterval: parseInterval(tokens[1])};
if (trigger.indexOf("sse:") === 0)
return {trigger: 'sse', sseEvent: trigger.substr(4)};
var triggerSpec = {trigger: trigger};
for (var i = 1; i < tokens.length; i++) {
@ -662,22 +667,18 @@ return (function () {
function processWebSocketInfo(elt, nodeData, info) {
var values = info.split(",");
for (var i = 0; i < values.length; i++) {
var value = removeWhiteSpace(values[i]);
if (value.indexOf("source:") === 0) {
processWebSocketSource(elt, value.substr(7));
var value = splitOnWhitespace(values[i]);
if (value[0] === "connect") {
processWebSocketSource(elt, value[1]);
}
if (value.indexOf("send:") === 0) {
processWebSocketSend(elt, value.substr(5));
if (value[0] === "send") {
processWebSocketSend(elt);
}
}
}
function processWebSocketSource(elt, wssSource) {
var detail = {
protocols:[]
};
triggerEvent(elt, "initWebSocket.htmx", detail);
var socket = new WebSocket("wss:" + wssSource, detail.protocols);
var socket = htmx.createWebSocket(wssSource);
socket.onerror = function (e) {
triggerErrorEvent(elt, "wsError.htmx", {error:e, socket:socket});
maybeCloseWebSocketSource(elt);
@ -712,13 +713,13 @@ return (function () {
}
}
function processWebSocketSend(elt, eventName) {
function processWebSocketSend(elt) {
var webSocketSourceElt = getClosestMatch(elt, function (parent) {
return getInternalData(parent).webSocket != null;
});
if (webSocketSourceElt) {
var webSocket = getInternalData(webSocketSourceElt).webSocket;
elt.addEventListener(eventName, function (evt) {
elt.addEventListener(getTriggerSpecs(elt)[0].trigger, function (evt) {
var headers = getHeaders(elt, webSocketSourceElt, null, elt);
var rawParameters = getInputValues(elt, 'post');
var filteredParameters = filterValues(rawParameters, elt);
@ -743,18 +744,15 @@ return (function () {
function processSSEInfo(elt, nodeData, info) {
var values = info.split(",");
for (var i = 0; i < values.length; i++) {
var value = removeWhiteSpace(values[i]);
if (value.indexOf("source:") === 0) {
processSSESource(elt, value.substr(7));
}
if (value.indexOf("trigger:") && nodeData.verb) {
processSSETrigger(elt, nodeData.verb, nodeData.path, value.substr(8));
var value = splitOnWhitespace(values[i]);
if (value[0] === "connect") {
processSSESource(elt, value[1]);
}
}
}
function processSSESource(elt, sseSrc) {
var source = new EventSource(sseSrc, detail.config);
var source = htmx.createEventSource(sseSrc);
source.onerror = function (e) {
triggerErrorEvent(elt, "sseError.htmx", {error:e, source:source});
maybeCloseSSESource(elt);
@ -769,7 +767,7 @@ return (function () {
if (sseSourceElt) {
var sseEventSource = getInternalData(sseSourceElt).sseEventSource;
var sseListener = function () {
if (!maybeCloseSSESource(sseEventSource)) {
if (!maybeCloseSSESource(sseSourceElt)) {
if (bodyContains(elt)) {
issueAjaxRequest(elt, verb, path);
} else {
@ -807,7 +805,9 @@ return (function () {
nodeData.path = path;
nodeData.verb = verb;
triggerSpecs.forEach(function(triggerSpec) {
if (triggerSpec.trigger === "revealed") {
if (triggerSpec.sseEvent) {
processSSETrigger(elt, verb, path, triggerSpec.sseEvent);
} else if (triggerSpec.trigger === "revealed") {
initScrollHandler();
maybeReveal(elt);
} else if (triggerSpec.trigger === "load") {
@ -835,13 +835,15 @@ return (function () {
if (!explicitAction && getClosestAttributeValue(elt, "hx-boost") === "true") {
boostElement(elt, nodeData, triggerSpecs);
}
var sseInfo = getAttributeValue(elt, 'hx-sse');
if (sseInfo) {
processSSEInfo(elt, nodeData, sseInfo);
}
var sseInfo = getAttributeValue(elt, 'hx-ws');
if (sseInfo) {
processWebSocketInfo(elt, nodeData, sseInfo);
var wsInfo = getAttributeValue(elt, 'hx-ws');
if (wsInfo) {
processWebSocketInfo(elt, nodeData, wsInfo);
}
triggerEvent(elt, "processedNode.htmx");
}
@ -1487,7 +1489,7 @@ return (function () {
function getMetaConfig() {
var element = getDocument().querySelector('meta[name="htmx-config"]');
if (element) {
return eval(element.content);
return JSON.parse(element.content);
} else {
return null;
}
@ -1539,7 +1541,13 @@ return (function () {
includeIndicatorStyles:true
},
parseInterval:parseInterval,
_:internalEval
_:internalEval,
createEventSource: function(url){
return new EventSource(url, {withCredentials:true})
},
createWebSocket: function(url){
return new WebSocket(url, []);
}
}
}
)()

View File

@ -0,0 +1,95 @@
describe("hx-sse attribute", function() {
function mockEventSource() {
var listeners = {};
var mockEventSource = {
addEventListener : function(message, l) {
listeners[message] = l;
},
sendEvent : function(event) {
var listener = listeners[event];
if(listener){
listener();
}
},
};
return mockEventSource;
}
beforeEach(function () {
this.server = makeServer();
var eventSource = mockEventSource();
this.eventSource = eventSource;
clearWorkArea();
htmx.createEventSource = function(){ return eventSource };
});
afterEach(function () {
this.server.restore();
clearWorkArea();
});
it('handles basic sse triggering', function () {
this.server.respondWith("GET", "/d1", "div1 updated");
this.server.respondWith("GET", "/d2", "div2 updated");
var div = make('<div hx-sse="connect /foo">' +
'<div id="d1" hx-trigger="sse:e1" hx-get="/d1">div1</div>' +
'<div id="d2" hx-trigger="sse:e2" hx-get="/d2">div2</div>' +
'</div>');
this.eventSource.sendEvent("e1");
this.server.respond();
byId("d1").innerHTML.should.equal("div1 updated");
byId("d2").innerHTML.should.equal("div2");
this.eventSource.sendEvent("e2");
this.server.respond();
byId("d1").innerHTML.should.equal("div1 updated");
byId("d2").innerHTML.should.equal("div2 updated");
})
it('does not trigger events that arent named', function () {
this.server.respondWith("GET", "/d1", "div1 updated");
var div = make('<div hx-sse="connect /foo">' +
'<div id="d1" hx-trigger="sse:e1" hx-get="/d1">div1</div>' +
'</div>');
this.eventSource.sendEvent("foo");
this.server.respond();
byId("d1").innerHTML.should.equal("div1");
this.eventSource.sendEvent("e2");
this.server.respond();
byId("d1").innerHTML.should.equal("div1");
this.eventSource.sendEvent("e1");
this.server.respond();
byId("d1").innerHTML.should.equal("div1 updated");
})
it('does not trigger events not on decendents', function () {
this.server.respondWith("GET", "/d1", "div1 updated");
var div = make('<div hx-sse="connect /foo"></div>' +
'<div id="d1" hx-trigger="sse:e1" hx-get="/d1">div1</div>');
this.eventSource.sendEvent("foo");
this.server.respond();
byId("d1").innerHTML.should.equal("div1");
this.eventSource.sendEvent("e2");
this.server.respond();
byId("d1").innerHTML.should.equal("div1");
this.eventSource.sendEvent("e1");
this.server.respond();
byId("d1").innerHTML.should.equal("div1");
})
});

View File

@ -0,0 +1,51 @@
describe("hx-ws attribute", function() {
function mockWebsocket() {
var listener;
var lastSent;
var mockSocket = {
addEventListener : function(message, l) {
listener = l;
},
write : function(content) {
return listener({data:content});
},
send : function(data) {
lastSent = data;
},
getLastSent : function() {
return lastSent;
}
};
return mockSocket;
}
beforeEach(function () {
this.server = makeServer();
var socket = mockWebsocket();
this.socket = socket;
clearWorkArea();
htmx.createWebSocket = function(){ return socket };
});
afterEach(function () {
this.server.restore();
clearWorkArea();
});
it('handles a basic call back', function () {
var div = make('<div hx-ws="connect wss:/foo"><div id="d1">div1</div><div id="d2">div2</div></div>');
this.socket.write("<div id=\"d1\">replaced</div>")
byId("d1").innerHTML.should.equal("replaced");
byId("d2").innerHTML.should.equal("div2");
})
it('handles a basic send', function () {
var div = make('<div hx-ws="connect wss:/foo"><div hx-ws="send" id="d1">div1</div></div>');
byId("d1").click();
var lastSent = this.socket.getLastSent();
var data = JSON.parse(lastSent);
data.HEADERS["X-HX-Request"].should.equal("true");
})
});

View File

@ -305,6 +305,19 @@ describe("Core htmx AJAX Tests", function(){
div.innerHTML.should.equal("<div id=\"d1\">foo</div>");
});
it('properly settles attributes on interior elements', function(done)
{
this.server.respondWith("GET", "/test", "<div hx-get='/test'><div foo='bar' id='d1'></div></div>");
var div = make("<div hx-get='/test' hx-swap='outerHTML settle:10ms'><div id='d1'></div></div>");
div.click();
this.server.respond();
should.equal(byId("d1").getAttribute("foo"), null);
setTimeout(function () {
should.equal(byId("d1").getAttribute("foo"), "bar");
done();
}, 20);
});
it('properly handles checkbox inputs', function()
{
var values;

View File

@ -74,10 +74,13 @@
<script src="attributes/hx-post.js"></script>
<script src="attributes/hx-push-url.js"></script>
<script src="attributes/hx-put.js"></script>
<script src="attributes/hx-select.js"></script>
<script src="attributes/hx-sse.js"></script>
<script src="attributes/hx-swap-oob.js"></script>
<script src="attributes/hx-swap.js"></script>
<script src="attributes/hx-target.js"></script>
<script src="attributes/hx-trigger.js"></script>
<script src="attributes/hx-ws.js"></script>
<!-- extension tests -->
<script src="../src/ext/rails-method.js"></script>