mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-09-27 04:50:43 +00:00
prep for 1.9.11 release
This commit is contained in:
parent
ef791c51eb
commit
11799bca6c
39
dist/ext/sse.js
vendored
39
dist/ext/sse.js
vendored
@ -51,7 +51,6 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
||||
// Try to create EventSources when elements are processed
|
||||
case "htmx:afterProcessNode":
|
||||
ensureEventSourceOnElement(evt.target);
|
||||
registerSSE(evt.target);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -112,19 +111,18 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
||||
* @param {HTMLElement} elt
|
||||
*/
|
||||
function registerSSE(elt) {
|
||||
// Find closest existing event source
|
||||
var sourceElement = api.getClosestMatch(elt, hasEventSource);
|
||||
if (sourceElement == null) {
|
||||
// api.triggerErrorEvent(elt, "htmx:noSSESourceError")
|
||||
return null; // no eventsource in parentage, orphaned element
|
||||
}
|
||||
|
||||
// Set internalData and source
|
||||
var internalData = api.getInternalData(sourceElement);
|
||||
var source = internalData.sseEventSource;
|
||||
|
||||
// Add message handlers for every `sse-swap` attribute
|
||||
queryAttributeOnThisOrChildren(elt, "sse-swap").forEach(function(child) {
|
||||
queryAttributeOnThisOrChildren(elt, "sse-swap").forEach(function (child) {
|
||||
// Find closest existing event source
|
||||
var sourceElement = api.getClosestMatch(child, hasEventSource);
|
||||
if (sourceElement == null) {
|
||||
// api.triggerErrorEvent(elt, "htmx:noSSESourceError")
|
||||
return null; // no eventsource in parentage, orphaned element
|
||||
}
|
||||
|
||||
// Set internalData and source
|
||||
var internalData = api.getInternalData(sourceElement);
|
||||
var source = internalData.sseEventSource;
|
||||
|
||||
var sseSwapAttr = api.getAttributeValue(child, "sse-swap");
|
||||
if (sseSwapAttr) {
|
||||
@ -145,9 +143,13 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
||||
// If the body no longer contains the element, remove the listener
|
||||
if (!api.bodyContains(child)) {
|
||||
source.removeEventListener(sseEventName, listener);
|
||||
return;
|
||||
}
|
||||
|
||||
// swap the response into the DOM and trigger a notification
|
||||
if(!api.triggerEvent(elt, "htmx:sseBeforeMessage", event)) {
|
||||
return;
|
||||
}
|
||||
swap(child, event.data);
|
||||
api.triggerEvent(elt, "htmx:sseMessage", event);
|
||||
};
|
||||
@ -160,6 +162,16 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
||||
|
||||
// Add message handlers for every `hx-trigger="sse:*"` attribute
|
||||
queryAttributeOnThisOrChildren(elt, "hx-trigger").forEach(function(child) {
|
||||
// Find closest existing event source
|
||||
var sourceElement = api.getClosestMatch(child, hasEventSource);
|
||||
if (sourceElement == null) {
|
||||
// api.triggerErrorEvent(elt, "htmx:noSSESourceError")
|
||||
return null; // no eventsource in parentage, orphaned element
|
||||
}
|
||||
|
||||
// Set internalData and source
|
||||
var internalData = api.getInternalData(sourceElement);
|
||||
var source = internalData.sseEventSource;
|
||||
|
||||
var sseEventName = api.getAttributeValue(child, "hx-trigger");
|
||||
if (sseEventName == null) {
|
||||
@ -220,6 +232,7 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
||||
ensureEventSource(child, sseURL, retryCount);
|
||||
});
|
||||
|
||||
registerSSE(elt);
|
||||
}
|
||||
|
||||
function ensureEventSource(elt, url, retryCount) {
|
||||
|
55
dist/htmx.d.ts
vendored
55
dist/htmx.d.ts
vendored
@ -21,7 +21,7 @@ export function addClass(elt: Element, clazz: string, delay?: number): void;
|
||||
* @param element the element to target (defaults to the **body**)
|
||||
* @returns Promise that resolves immediately if no request is sent, or when the request is complete
|
||||
*/
|
||||
export function ajax(verb: string, path: string, element: Element): Promise<void>;
|
||||
export function ajax(verb: string, path: string, element?: Element): Promise<void>;
|
||||
|
||||
/**
|
||||
* Issues an htmx-style AJAX request
|
||||
@ -429,20 +429,57 @@ export interface HtmxConfig {
|
||||
* If set to true htmx will not update the title of the document when a title tag is found in new content
|
||||
* @default false
|
||||
*/
|
||||
ignoreTitle:? boolean;
|
||||
/**
|
||||
* The cache to store evaluated trigger specifications into.
|
||||
* You may define a simple object to use a never-clearing cache, or implement your own system using a [proxy object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
|
||||
* @default null
|
||||
*/
|
||||
triggerSpecsCache?: {[trigger: string]: HtmxTriggerSpecification[]};
|
||||
ignoreTitle?: boolean;
|
||||
}
|
||||
|
||||
export type HtmxEvent = "htmx:abort"
|
||||
| "htmx:afterOnLoad"
|
||||
| "htmx:afterProcessNode"
|
||||
| "htmx:afterRequest"
|
||||
| "htmx:afterSettle"
|
||||
| "htmx:afterSwap"
|
||||
| "htmx:beforeCleanupElement"
|
||||
| "htmx:beforeOnLoad"
|
||||
| "htmx:beforeProcessNode"
|
||||
| "htmx:beforeRequest"
|
||||
| "htmx:beforeSwap"
|
||||
| "htmx:beforeSend"
|
||||
| "htmx:configRequest"
|
||||
| "htmx:confirm"
|
||||
| "htmx:historyCacheError"
|
||||
| "htmx:historyCacheMiss"
|
||||
| "htmx:historyCacheMissError"
|
||||
| "htmx:historyCacheMissLoad"
|
||||
| "htmx:historyRestore"
|
||||
| "htmx:load"
|
||||
| "htmx:noSSESourceError"
|
||||
| "htmx:onLoadError"
|
||||
| "htmx:oobAfterSwap"
|
||||
| "htmx:oobBeforeSwap"
|
||||
| "htmx:oobErrorNoTarget"
|
||||
| "htmx:prompt"
|
||||
| "htmx:pushedIntoHistory"
|
||||
| "htmx:responseError"
|
||||
| "htmx:sendError"
|
||||
| "htmx:sseError"
|
||||
| "htmx:sseOpen"
|
||||
| "htmx:swapError"
|
||||
| "htmx:targetError"
|
||||
| "htmx:timeout"
|
||||
| "htmx:validation:validate"
|
||||
| "htmx:validation:failed"
|
||||
| "htmx:validation:halted"
|
||||
| "htmx:xhr:abort"
|
||||
| "htmx:xhr:loadend"
|
||||
| "htmx:xhr:loadstart"
|
||||
| "htmx:xhr:progress"
|
||||
;
|
||||
|
||||
/**
|
||||
* https://htmx.org/extensions/#defining
|
||||
*/
|
||||
export interface HtmxExtension {
|
||||
onEvent?: (name: string, evt: CustomEvent) => any;
|
||||
onEvent?: (name: HtmxEvent, evt: CustomEvent) => any;
|
||||
transformResponse?: (text: any, xhr: XMLHttpRequest, elt: any) => any;
|
||||
isInlineSwap?: (swapStyle: any) => any;
|
||||
handleSwap?: (swapStyle: any, target: any, fragment: any, settleInfo: any) => any;
|
||||
|
35
dist/htmx.js
vendored
35
dist/htmx.js
vendored
@ -89,7 +89,7 @@ return (function () {
|
||||
sock.binaryType = htmx.config.wsBinaryType;
|
||||
return sock;
|
||||
},
|
||||
version: "1.9.10"
|
||||
version: "1.9.11"
|
||||
};
|
||||
|
||||
/** @type {import("./htmx").HtmxInternalApi} */
|
||||
@ -309,10 +309,24 @@ return (function () {
|
||||
content = content.replace(HEAD_TAG_REGEX, '');
|
||||
}
|
||||
if (htmx.config.useTemplateFragments && partialResponse) {
|
||||
var documentFragment = parseHTML("<body><template>" + content + "</template></body>", 0);
|
||||
var fragment = parseHTML("<body><template>" + content + "</template></body>", 0);
|
||||
// @ts-ignore type mismatch between DocumentFragment and Element.
|
||||
// TODO: Are these close enough for htmx to use interchangeably?
|
||||
return documentFragment.querySelector('template').content;
|
||||
if (htmx.config.allowScriptTags) {
|
||||
// if there is a nonce set up, set it on the new script tags
|
||||
forEach(fragment.querySelectorAll("script"), function (script) {
|
||||
if (htmx.config.inlineScriptNonce) {
|
||||
script.nonce = htmx.config.inlineScriptNonce;
|
||||
}
|
||||
getInternalData(script).executed = true; // mark as executed due to template insertion semantics
|
||||
})
|
||||
} else {
|
||||
forEach(fragment.querySelectorAll("script"), function (script) {
|
||||
// remove all script tags if scripts are disabled
|
||||
removeElement(script);
|
||||
})
|
||||
}
|
||||
return fragment.querySelector('template').content;
|
||||
}
|
||||
switch (startTag) {
|
||||
case "thead":
|
||||
@ -1892,7 +1906,8 @@ return (function () {
|
||||
}
|
||||
|
||||
function evalScript(script) {
|
||||
if (htmx.config.allowScriptTags && (script.type === "text/javascript" || script.type === "module" || script.type === "") ) {
|
||||
if (!getInternalData(script).executed && htmx.config.allowScriptTags &&
|
||||
(script.type === "text/javascript" || script.type === "module" || script.type === "") ) {
|
||||
var newScript = getDocument().createElement("script");
|
||||
forEach(script.attributes, function (attr) {
|
||||
newScript.setAttribute(attr.name, attr.value);
|
||||
@ -1918,12 +1933,14 @@ return (function () {
|
||||
}
|
||||
|
||||
function processScripts(elt) {
|
||||
if (matches(elt, "script")) {
|
||||
evalScript(elt);
|
||||
if (!htmx.config.useTemplateFragments) {
|
||||
if (matches(elt, "script")) {
|
||||
evalScript(elt);
|
||||
}
|
||||
forEach(findAll(elt, "script"), function (script) {
|
||||
evalScript(script);
|
||||
});
|
||||
}
|
||||
forEach(findAll(elt, "script"), function (script) {
|
||||
evalScript(script);
|
||||
});
|
||||
}
|
||||
|
||||
function shouldProcessHxOn(elt) {
|
||||
|
2
dist/htmx.min.js
vendored
2
dist/htmx.min.js
vendored
File diff suppressed because one or more lines are too long
BIN
dist/htmx.min.js.gz
vendored
BIN
dist/htmx.min.js.gz
vendored
Binary file not shown.
6
www/static/node_modules/chai-dom/chai-dom.js
generated
vendored
6
www/static/node_modules/chai-dom/chai-dom.js
generated
vendored
@ -87,8 +87,8 @@
|
||||
, 'expected ' + elToString(el) + ' not to have class matching #{exp}'
|
||||
, className
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.assert(
|
||||
el.classList.contains(className)
|
||||
, 'expected ' + elToString(el) + ' to have class #{exp}'
|
||||
@ -350,7 +350,7 @@
|
||||
|
||||
chai.Assertion.addProperty('displayed', function() {
|
||||
var el = flag(this, 'object'),
|
||||
actual = document.body.contains(el) ? window.getComputedStyle(el).display : el.style.display
|
||||
actual = el.getRootNode({ composed: true }) === document ? window.getComputedStyle(el).display : el.style.display
|
||||
|
||||
this.assert(
|
||||
actual !== 'none'
|
||||
|
114
www/static/node_modules/chai/chai.js
generated
vendored
114
www/static/node_modules/chai/chai.js
generated
vendored
@ -14,7 +14,7 @@ var used = [];
|
||||
* Chai version
|
||||
*/
|
||||
|
||||
exports.version = '4.3.3';
|
||||
exports.version = '4.3.8';
|
||||
|
||||
/*!
|
||||
* Assertion Error
|
||||
@ -8608,6 +8608,7 @@ var config = require('../config');
|
||||
* messages or should be truncated.
|
||||
*
|
||||
* @param {Mixed} javascript object to inspect
|
||||
* @returns {string} stringified object
|
||||
* @name objDisplay
|
||||
* @namespace Utils
|
||||
* @api public
|
||||
@ -9062,7 +9063,7 @@ var flag = require('./flag');
|
||||
/**
|
||||
* ### .test(object, expression)
|
||||
*
|
||||
* Test and object for expression.
|
||||
* Test an object for expression.
|
||||
*
|
||||
* @param {Object} object (constructed Assertion)
|
||||
* @param {Arguments} chai.Assertion.prototype.assert arguments
|
||||
@ -9250,6 +9251,7 @@ AssertionError.prototype.toJSON = function (stack) {
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
var getFunctionName = require('get-func-name');
|
||||
/**
|
||||
* ### .checkError
|
||||
*
|
||||
@ -9329,34 +9331,6 @@ function compatibleMessage(thrown, errMatcher) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* ### .getFunctionName(constructorFn)
|
||||
*
|
||||
* Returns the name of a function.
|
||||
* This also includes a polyfill function if `constructorFn.name` is not defined.
|
||||
*
|
||||
* @name getFunctionName
|
||||
* @param {Function} constructorFn
|
||||
* @namespace Utils
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var functionNameMatch = /\s*function(?:\s|\s*\/\*[^(?:*\/)]+\*\/\s*)*([^\(\/]+)/;
|
||||
function getFunctionName(constructorFn) {
|
||||
var name = '';
|
||||
if (typeof constructorFn.name === 'undefined') {
|
||||
// Here we run a polyfill if constructorFn.name is not defined
|
||||
var match = String(constructorFn).match(functionNameMatch);
|
||||
if (match) {
|
||||
name = match[1];
|
||||
}
|
||||
} else {
|
||||
name = constructorFn.name;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* ### .getConstructorName(errorLike)
|
||||
*
|
||||
@ -9376,8 +9350,11 @@ function getConstructorName(errorLike) {
|
||||
// If `err` is not an instance of Error it is an error constructor itself or another function.
|
||||
// If we've got a common function we get its name, otherwise we may need to create a new instance
|
||||
// of the error just in case it's a poorly-constructed error. Please see chaijs/chai/issues/45 to know more.
|
||||
constructorName = getFunctionName(errorLike).trim() ||
|
||||
getFunctionName(new errorLike()); // eslint-disable-line new-cap
|
||||
constructorName = getFunctionName(errorLike);
|
||||
if (constructorName === '') {
|
||||
var newConstructorName = getFunctionName(new errorLike()); // eslint-disable-line new-cap
|
||||
constructorName = newConstructorName || constructorName;
|
||||
}
|
||||
}
|
||||
|
||||
return constructorName;
|
||||
@ -9415,7 +9392,7 @@ module.exports = {
|
||||
getConstructorName: getConstructorName,
|
||||
};
|
||||
|
||||
},{}],35:[function(require,module,exports){
|
||||
},{"get-func-name":36}],35:[function(require,module,exports){
|
||||
'use strict';
|
||||
/* globals Symbol: false, Uint8Array: false, WeakMap: false */
|
||||
/*!
|
||||
@ -9810,8 +9787,15 @@ function getEnumerableKeys(target) {
|
||||
return keys;
|
||||
}
|
||||
|
||||
function getNonEnumerableSymbols(target) {
|
||||
var keys = Object.getOwnPropertySymbols(target);
|
||||
function getEnumerableSymbols(target) {
|
||||
var keys = [];
|
||||
var allKeys = Object.getOwnPropertySymbols(target);
|
||||
for (var i = 0; i < allKeys.length; i += 1) {
|
||||
var key = allKeys[i];
|
||||
if (Object.getOwnPropertyDescriptor(target, key).enumerable) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
@ -9850,8 +9834,8 @@ function keysEqual(leftHandOperand, rightHandOperand, keys, options) {
|
||||
function objectEqual(leftHandOperand, rightHandOperand, options) {
|
||||
var leftHandKeys = getEnumerableKeys(leftHandOperand);
|
||||
var rightHandKeys = getEnumerableKeys(rightHandOperand);
|
||||
var leftHandSymbols = getNonEnumerableSymbols(leftHandOperand);
|
||||
var rightHandSymbols = getNonEnumerableSymbols(rightHandOperand);
|
||||
var leftHandSymbols = getEnumerableSymbols(leftHandOperand);
|
||||
var rightHandSymbols = getEnumerableSymbols(rightHandOperand);
|
||||
leftHandKeys = leftHandKeys.concat(leftHandSymbols);
|
||||
rightHandKeys = rightHandKeys.concat(rightHandSymbols);
|
||||
|
||||
@ -9927,6 +9911,7 @@ function mapSymbols(arr) {
|
||||
|
||||
var toString = Function.prototype.toString;
|
||||
var functionNameMatch = /\s*function(?:\s|\s*\/\*[^(?:*\/)]+\*\/\s*)*([^\s\(\/]+)/;
|
||||
var maxFunctionSourceLength = 512;
|
||||
function getFuncName(aFunc) {
|
||||
if (typeof aFunc !== 'function') {
|
||||
return null;
|
||||
@ -9934,8 +9919,15 @@ function getFuncName(aFunc) {
|
||||
|
||||
var name = '';
|
||||
if (typeof Function.prototype.name === 'undefined' && typeof aFunc.name === 'undefined') {
|
||||
// eslint-disable-next-line prefer-reflect
|
||||
var functionSource = toString.call(aFunc);
|
||||
// To avoid unconstrained resource consumption due to pathalogically large function names,
|
||||
// we limit the available return value to be less than 512 characters.
|
||||
if (functionSource.indexOf('(') > maxFunctionSourceLength) {
|
||||
return name;
|
||||
}
|
||||
// Here we run a polyfill if Function does not support the `name` property and if aFunc.name is not defined
|
||||
var match = toString.call(aFunc).match(functionNameMatch);
|
||||
var match = functionSource.match(functionNameMatch);
|
||||
if (match) {
|
||||
name = match[1];
|
||||
}
|
||||
@ -10331,9 +10323,15 @@ module.exports = getFuncName;
|
||||
}
|
||||
|
||||
function inspectDate(dateObject, options) {
|
||||
// If we need to - truncate the time portion, but never the date
|
||||
var split = dateObject.toJSON().split('T');
|
||||
var date = split[0];
|
||||
var stringRepresentation = dateObject.toJSON();
|
||||
|
||||
if (stringRepresentation === null) {
|
||||
return 'Invalid Date';
|
||||
}
|
||||
|
||||
var split = stringRepresentation.split('T');
|
||||
var date = split[0]; // If we need to - truncate the time portion, but never the date
|
||||
|
||||
return options.stylize("".concat(date, "T").concat(truncate(split[1], options.truncate - date.length - 1)), 'date');
|
||||
}
|
||||
|
||||
@ -10630,7 +10628,32 @@ module.exports = getFuncName;
|
||||
nodeInspect = false;
|
||||
}
|
||||
|
||||
var constructorMap = new WeakMap();
|
||||
function FakeMap() {
|
||||
// eslint-disable-next-line prefer-template
|
||||
this.key = 'chai/loupe__' + Math.random() + Date.now();
|
||||
}
|
||||
|
||||
FakeMap.prototype = {
|
||||
// eslint-disable-next-line object-shorthand
|
||||
get: function get(key) {
|
||||
return key[this.key];
|
||||
},
|
||||
// eslint-disable-next-line object-shorthand
|
||||
has: function has(key) {
|
||||
return this.key in key;
|
||||
},
|
||||
// eslint-disable-next-line object-shorthand
|
||||
set: function set(key, value) {
|
||||
if (Object.isExtensible(key)) {
|
||||
Object.defineProperty(key, this.key, {
|
||||
// eslint-disable-next-line object-shorthand
|
||||
value: value,
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
var constructorMap = new (typeof WeakMap === 'function' ? WeakMap : FakeMap)();
|
||||
var stringTagMap = {};
|
||||
var baseTypesMap = {
|
||||
undefined: function undefined$1(value, options) {
|
||||
@ -10764,6 +10787,11 @@ module.exports = getFuncName;
|
||||
} // If it is an object with an anonymous prototype, display it as an object.
|
||||
|
||||
|
||||
return inspectObject(value, options);
|
||||
} // last chance to check if it's an object
|
||||
|
||||
|
||||
if (value === Object(value)) {
|
||||
return inspectObject(value, options);
|
||||
} // We have run out of options! Just stringify the value
|
||||
|
||||
@ -10775,7 +10803,7 @@ module.exports = getFuncName;
|
||||
return false;
|
||||
}
|
||||
|
||||
constructorMap.add(constructor, inspector);
|
||||
constructorMap.set(constructor, inspector);
|
||||
return true;
|
||||
}
|
||||
function registerStringTag(stringTag, inspector) {
|
||||
|
139
www/static/node_modules/mocha/mocha.css
generated
vendored
139
www/static/node_modules/mocha/mocha.css
generated
vendored
@ -1,7 +1,73 @@
|
||||
@charset "utf-8";
|
||||
|
||||
:root {
|
||||
--mocha-color: #000;
|
||||
--mocha-bg-color: #fff;
|
||||
--mocha-pass-icon-color: #00d6b2;
|
||||
--mocha-pass-color: #fff;
|
||||
--mocha-pass-shadow-color: rgba(0,0,0,.2);
|
||||
--mocha-pass-mediump-color: #c09853;
|
||||
--mocha-pass-slow-color: #b94a48;
|
||||
--mocha-test-pending-color: #0b97c4;
|
||||
--mocha-test-pending-icon-color: #0b97c4;
|
||||
--mocha-test-fail-color: #c00;
|
||||
--mocha-test-fail-icon-color: #c00;
|
||||
--mocha-test-fail-pre-color: #000;
|
||||
--mocha-test-fail-pre-error-color: #c00;
|
||||
--mocha-test-html-error-color: #000;
|
||||
--mocha-box-shadow-color: #eee;
|
||||
--mocha-box-bottom-color: #ddd;
|
||||
--mocha-test-replay-color: #000;
|
||||
--mocha-test-replay-bg-color: #eee;
|
||||
--mocha-stats-color: #888;
|
||||
--mocha-stats-em-color: #000;
|
||||
--mocha-stats-hover-color: #eee;
|
||||
--mocha-error-color: #c00;
|
||||
|
||||
--mocha-code-comment: #ddd;
|
||||
--mocha-code-init: #2f6fad;
|
||||
--mocha-code-string: #5890ad;
|
||||
--mocha-code-keyword: #8a6343;
|
||||
--mocha-code-number: #2f6fad;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--mocha-color: #fff;
|
||||
--mocha-bg-color: #222;
|
||||
--mocha-pass-icon-color: #00d6b2;
|
||||
--mocha-pass-color: #222;
|
||||
--mocha-pass-shadow-color: rgba(255,255,255,.2);
|
||||
--mocha-pass-mediump-color: #f1be67;
|
||||
--mocha-pass-slow-color: #f49896;
|
||||
--mocha-test-pending-color: #0b97c4;
|
||||
--mocha-test-pending-icon-color: #0b97c4;
|
||||
--mocha-test-fail-color: #f44;
|
||||
--mocha-test-fail-icon-color: #f44;
|
||||
--mocha-test-fail-pre-color: #fff;
|
||||
--mocha-test-fail-pre-error-color: #f44;
|
||||
--mocha-test-html-error-color: #fff;
|
||||
--mocha-box-shadow-color: #444;
|
||||
--mocha-box-bottom-color: #555;
|
||||
--mocha-test-replay-color: #fff;
|
||||
--mocha-test-replay-bg-color: #444;
|
||||
--mocha-stats-color: #aaa;
|
||||
--mocha-stats-em-color: #fff;
|
||||
--mocha-stats-hover-color: #444;
|
||||
--mocha-error-color: #f44;
|
||||
|
||||
--mocha-code-comment: #ddd;
|
||||
--mocha-code-init: #9cc7f1;
|
||||
--mocha-code-string: #80d4ff;
|
||||
--mocha-code-keyword: #e3a470;
|
||||
--mocha-code-number: #4ca7ff;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin:0;
|
||||
background-color: var(--mocha-bg-color);
|
||||
color: var(--mocha-color);
|
||||
}
|
||||
|
||||
#mocha {
|
||||
@ -69,11 +135,11 @@ body {
|
||||
}
|
||||
|
||||
#mocha .test.pass.medium .duration {
|
||||
background: #c09853;
|
||||
background: var(--mocha-pass-mediump-color);
|
||||
}
|
||||
|
||||
#mocha .test.pass.slow .duration {
|
||||
background: #b94a48;
|
||||
background: var(--mocha-pass-slow-color);
|
||||
}
|
||||
|
||||
#mocha .test.pass::before {
|
||||
@ -82,17 +148,17 @@ body {
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
color: #00d6b2;
|
||||
color: var(--mocha-pass-icon-color);
|
||||
}
|
||||
|
||||
#mocha .test.pass .duration {
|
||||
font-size: 9px;
|
||||
margin-left: 5px;
|
||||
padding: 2px 5px;
|
||||
color: #fff;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
|
||||
color: var(--mocha-pass-color);
|
||||
-webkit-box-shadow: inset 0 1px 1px var(--mocha-pass-shadow-color);
|
||||
-moz-box-shadow: inset 0 1px 1px var(--mocha-pass-shadow-color);
|
||||
box-shadow: inset 0 1px 1px var(--mocha-pass-shadow-color);
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
-ms-border-radius: 5px;
|
||||
@ -105,20 +171,20 @@ body {
|
||||
}
|
||||
|
||||
#mocha .test.pending {
|
||||
color: #0b97c4;
|
||||
color: var(--mocha-test-pending-color);
|
||||
}
|
||||
|
||||
#mocha .test.pending::before {
|
||||
content: '◦';
|
||||
color: #0b97c4;
|
||||
color: var(--mocha-test-pending-icon-color);
|
||||
}
|
||||
|
||||
#mocha .test.fail {
|
||||
color: #c00;
|
||||
color: var(--mocha-test-fail-color);
|
||||
}
|
||||
|
||||
#mocha .test.fail pre {
|
||||
color: black;
|
||||
color: var(--mocha-test-fail-pre-color);
|
||||
}
|
||||
|
||||
#mocha .test.fail::before {
|
||||
@ -127,35 +193,35 @@ body {
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
color: #c00;
|
||||
color: var(--mocha-pass-icon-color);
|
||||
}
|
||||
|
||||
#mocha .test pre.error {
|
||||
color: #c00;
|
||||
color: var(--mocha-test-fail-pre-error-color);
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#mocha .test .html-error {
|
||||
overflow: auto;
|
||||
color: black;
|
||||
color: var(--mocha-test-html-error-color);
|
||||
display: block;
|
||||
float: left;
|
||||
clear: left;
|
||||
font: 12px/1.5 monaco, monospace;
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
border: 1px solid #eee;
|
||||
border: 1px solid var(--mocha-box-shadow-color);
|
||||
max-width: 85%; /*(1)*/
|
||||
max-width: -webkit-calc(100% - 42px);
|
||||
max-width: -moz-calc(100% - 42px);
|
||||
max-width: calc(100% - 42px); /*(2)*/
|
||||
max-height: 300px;
|
||||
word-wrap: break-word;
|
||||
border-bottom-color: #ddd;
|
||||
-webkit-box-shadow: 0 1px 3px #eee;
|
||||
-moz-box-shadow: 0 1px 3px #eee;
|
||||
box-shadow: 0 1px 3px #eee;
|
||||
border-bottom-color: var(--mocha-box-bottom-color);
|
||||
-webkit-box-shadow: 0 1px 3px var(--mocha-box-shadow-color);
|
||||
-moz-box-shadow: 0 1px 3px var(--mocha-box-shadow-color);
|
||||
box-shadow: 0 1px 3px var(--mocha-box-shadow-color);
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
@ -187,16 +253,16 @@ body {
|
||||
font: 12px/1.5 monaco, monospace;
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
border: 1px solid #eee;
|
||||
border: 1px solid var(--mocha-box-shadow-color);
|
||||
max-width: 85%; /*(1)*/
|
||||
max-width: -webkit-calc(100% - 42px);
|
||||
max-width: -moz-calc(100% - 42px);
|
||||
max-width: calc(100% - 42px); /*(2)*/
|
||||
word-wrap: break-word;
|
||||
border-bottom-color: #ddd;
|
||||
-webkit-box-shadow: 0 1px 3px #eee;
|
||||
-moz-box-shadow: 0 1px 3px #eee;
|
||||
box-shadow: 0 1px 3px #eee;
|
||||
border-bottom-color: var(--mocha-box-bottom-color);
|
||||
-webkit-box-shadow: 0 1px 3px var(--mocha-box-shadow-color);
|
||||
-moz-box-shadow: 0 1px 3px var(--mocha-box-shadow-color);
|
||||
box-shadow: 0 1px 3px var(--mocha-box-shadow-color);
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
@ -217,7 +283,7 @@ body {
|
||||
height: 15px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
background: #eee;
|
||||
background: var(--mocha-test-replay-bg-color);
|
||||
font-size: 15px;
|
||||
-webkit-border-radius: 15px;
|
||||
-moz-border-radius: 15px;
|
||||
@ -226,11 +292,12 @@ body {
|
||||
-moz-transition:opacity 200ms;
|
||||
-o-transition:opacity 200ms;
|
||||
transition: opacity 200ms;
|
||||
opacity: 0.3;
|
||||
color: #888;
|
||||
opacity: 0.7;
|
||||
color: var(--mocha-test-replay-color);
|
||||
}
|
||||
|
||||
#mocha .test:hover a.replay {
|
||||
box-shadow: 0 0 1px inset var(--mocha-test-replay-color);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@ -251,7 +318,7 @@ body {
|
||||
}
|
||||
|
||||
#mocha-error {
|
||||
color: #c00;
|
||||
color: var(--mocha-error-color);
|
||||
font-size: 1.5em;
|
||||
font-weight: 100;
|
||||
letter-spacing: 1px;
|
||||
@ -263,7 +330,7 @@ body {
|
||||
right: 10px;
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
color: #888;
|
||||
color: var(--mocha-stats-color);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@ -284,7 +351,7 @@ body {
|
||||
}
|
||||
|
||||
#mocha-stats em {
|
||||
color: black;
|
||||
color: var(--mocha-stats-em-color);
|
||||
}
|
||||
|
||||
#mocha-stats a {
|
||||
@ -293,7 +360,7 @@ body {
|
||||
}
|
||||
|
||||
#mocha-stats a:hover {
|
||||
border-bottom: 1px solid #eee;
|
||||
border-bottom: 1px solid var(--mocha-stats-hover-color);
|
||||
}
|
||||
|
||||
#mocha-stats li {
|
||||
@ -308,11 +375,11 @@ body {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
#mocha code .comment { color: #ddd; }
|
||||
#mocha code .init { color: #2f6fad; }
|
||||
#mocha code .string { color: #5890ad; }
|
||||
#mocha code .keyword { color: #8a6343; }
|
||||
#mocha code .number { color: #2f6fad; }
|
||||
#mocha code .comment { color: var(--mocha-code-comment); }
|
||||
#mocha code .init { color: var(--mocha-code-init); }
|
||||
#mocha code .string { color: var(--mocha-code-string); }
|
||||
#mocha code .keyword { color: var(--mocha-code-keyword); }
|
||||
#mocha code .number { color: var(--mocha-code-number); }
|
||||
|
||||
@media screen and (max-device-width: 480px) {
|
||||
#mocha {
|
||||
|
51873
www/static/node_modules/mocha/mocha.js
generated
vendored
51873
www/static/node_modules/mocha/mocha.js
generated
vendored
File diff suppressed because one or more lines are too long
8
www/static/node_modules/mock-socket/dist/mock-socket.js
generated
vendored
8
www/static/node_modules/mock-socket/dist/mock-socket.js
generated
vendored
@ -1485,6 +1485,9 @@ var WebSocket$1 = (function (EventTarget$$1) {
|
||||
* registered :-)
|
||||
*/
|
||||
delay(function delayCallback() {
|
||||
if (this.readyState !== WebSocket.CONNECTING) {
|
||||
return;
|
||||
}
|
||||
if (server) {
|
||||
if (
|
||||
server.options.verifyClient &&
|
||||
@ -1581,8 +1584,9 @@ var WebSocket$1 = (function (EventTarget$$1) {
|
||||
WebSocket.prototype.send = function send (data) {
|
||||
var this$1 = this;
|
||||
|
||||
if (this.readyState === WebSocket.CLOSING || this.readyState === WebSocket.CLOSED) {
|
||||
throw new Error('WebSocket is already in CLOSING or CLOSED state');
|
||||
if (this.readyState === WebSocket.CONNECTING) {
|
||||
// TODO: node>=17 replace with DOMException
|
||||
throw new Error("Failed to execute 'send' on 'WebSocket': Still in CONNECTING state");
|
||||
}
|
||||
|
||||
// TODO: handle bufferedAmount
|
||||
|
@ -51,7 +51,6 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
||||
// Try to create EventSources when elements are processed
|
||||
case "htmx:afterProcessNode":
|
||||
ensureEventSourceOnElement(evt.target);
|
||||
registerSSE(evt.target);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -112,19 +111,18 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
||||
* @param {HTMLElement} elt
|
||||
*/
|
||||
function registerSSE(elt) {
|
||||
// Find closest existing event source
|
||||
var sourceElement = api.getClosestMatch(elt, hasEventSource);
|
||||
if (sourceElement == null) {
|
||||
// api.triggerErrorEvent(elt, "htmx:noSSESourceError")
|
||||
return null; // no eventsource in parentage, orphaned element
|
||||
}
|
||||
|
||||
// Set internalData and source
|
||||
var internalData = api.getInternalData(sourceElement);
|
||||
var source = internalData.sseEventSource;
|
||||
|
||||
// Add message handlers for every `sse-swap` attribute
|
||||
queryAttributeOnThisOrChildren(elt, "sse-swap").forEach(function(child) {
|
||||
queryAttributeOnThisOrChildren(elt, "sse-swap").forEach(function (child) {
|
||||
// Find closest existing event source
|
||||
var sourceElement = api.getClosestMatch(child, hasEventSource);
|
||||
if (sourceElement == null) {
|
||||
// api.triggerErrorEvent(elt, "htmx:noSSESourceError")
|
||||
return null; // no eventsource in parentage, orphaned element
|
||||
}
|
||||
|
||||
// Set internalData and source
|
||||
var internalData = api.getInternalData(sourceElement);
|
||||
var source = internalData.sseEventSource;
|
||||
|
||||
var sseSwapAttr = api.getAttributeValue(child, "sse-swap");
|
||||
if (sseSwapAttr) {
|
||||
@ -145,9 +143,13 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
||||
// If the body no longer contains the element, remove the listener
|
||||
if (!api.bodyContains(child)) {
|
||||
source.removeEventListener(sseEventName, listener);
|
||||
return;
|
||||
}
|
||||
|
||||
// swap the response into the DOM and trigger a notification
|
||||
if(!api.triggerEvent(elt, "htmx:sseBeforeMessage", event)) {
|
||||
return;
|
||||
}
|
||||
swap(child, event.data);
|
||||
api.triggerEvent(elt, "htmx:sseMessage", event);
|
||||
};
|
||||
@ -160,6 +162,16 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
||||
|
||||
// Add message handlers for every `hx-trigger="sse:*"` attribute
|
||||
queryAttributeOnThisOrChildren(elt, "hx-trigger").forEach(function(child) {
|
||||
// Find closest existing event source
|
||||
var sourceElement = api.getClosestMatch(child, hasEventSource);
|
||||
if (sourceElement == null) {
|
||||
// api.triggerErrorEvent(elt, "htmx:noSSESourceError")
|
||||
return null; // no eventsource in parentage, orphaned element
|
||||
}
|
||||
|
||||
// Set internalData and source
|
||||
var internalData = api.getInternalData(sourceElement);
|
||||
var source = internalData.sseEventSource;
|
||||
|
||||
var sseEventName = api.getAttributeValue(child, "hx-trigger");
|
||||
if (sseEventName == null) {
|
||||
@ -220,6 +232,7 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
||||
ensureEventSource(child, sseURL, retryCount);
|
||||
});
|
||||
|
||||
registerSSE(elt);
|
||||
}
|
||||
|
||||
function ensureEventSource(elt, url, retryCount) {
|
||||
|
55
www/static/src/htmx.d.ts
vendored
55
www/static/src/htmx.d.ts
vendored
@ -21,7 +21,7 @@ export function addClass(elt: Element, clazz: string, delay?: number): void;
|
||||
* @param element the element to target (defaults to the **body**)
|
||||
* @returns Promise that resolves immediately if no request is sent, or when the request is complete
|
||||
*/
|
||||
export function ajax(verb: string, path: string, element: Element): Promise<void>;
|
||||
export function ajax(verb: string, path: string, element?: Element): Promise<void>;
|
||||
|
||||
/**
|
||||
* Issues an htmx-style AJAX request
|
||||
@ -429,20 +429,57 @@ export interface HtmxConfig {
|
||||
* If set to true htmx will not update the title of the document when a title tag is found in new content
|
||||
* @default false
|
||||
*/
|
||||
ignoreTitle:? boolean;
|
||||
/**
|
||||
* The cache to store evaluated trigger specifications into.
|
||||
* You may define a simple object to use a never-clearing cache, or implement your own system using a [proxy object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
|
||||
* @default null
|
||||
*/
|
||||
triggerSpecsCache?: {[trigger: string]: HtmxTriggerSpecification[]};
|
||||
ignoreTitle?: boolean;
|
||||
}
|
||||
|
||||
export type HtmxEvent = "htmx:abort"
|
||||
| "htmx:afterOnLoad"
|
||||
| "htmx:afterProcessNode"
|
||||
| "htmx:afterRequest"
|
||||
| "htmx:afterSettle"
|
||||
| "htmx:afterSwap"
|
||||
| "htmx:beforeCleanupElement"
|
||||
| "htmx:beforeOnLoad"
|
||||
| "htmx:beforeProcessNode"
|
||||
| "htmx:beforeRequest"
|
||||
| "htmx:beforeSwap"
|
||||
| "htmx:beforeSend"
|
||||
| "htmx:configRequest"
|
||||
| "htmx:confirm"
|
||||
| "htmx:historyCacheError"
|
||||
| "htmx:historyCacheMiss"
|
||||
| "htmx:historyCacheMissError"
|
||||
| "htmx:historyCacheMissLoad"
|
||||
| "htmx:historyRestore"
|
||||
| "htmx:load"
|
||||
| "htmx:noSSESourceError"
|
||||
| "htmx:onLoadError"
|
||||
| "htmx:oobAfterSwap"
|
||||
| "htmx:oobBeforeSwap"
|
||||
| "htmx:oobErrorNoTarget"
|
||||
| "htmx:prompt"
|
||||
| "htmx:pushedIntoHistory"
|
||||
| "htmx:responseError"
|
||||
| "htmx:sendError"
|
||||
| "htmx:sseError"
|
||||
| "htmx:sseOpen"
|
||||
| "htmx:swapError"
|
||||
| "htmx:targetError"
|
||||
| "htmx:timeout"
|
||||
| "htmx:validation:validate"
|
||||
| "htmx:validation:failed"
|
||||
| "htmx:validation:halted"
|
||||
| "htmx:xhr:abort"
|
||||
| "htmx:xhr:loadend"
|
||||
| "htmx:xhr:loadstart"
|
||||
| "htmx:xhr:progress"
|
||||
;
|
||||
|
||||
/**
|
||||
* https://htmx.org/extensions/#defining
|
||||
*/
|
||||
export interface HtmxExtension {
|
||||
onEvent?: (name: string, evt: CustomEvent) => any;
|
||||
onEvent?: (name: HtmxEvent, evt: CustomEvent) => any;
|
||||
transformResponse?: (text: any, xhr: XMLHttpRequest, elt: any) => any;
|
||||
isInlineSwap?: (swapStyle: any) => any;
|
||||
handleSwap?: (swapStyle: any, target: any, fragment: any, settleInfo: any) => any;
|
||||
|
@ -89,7 +89,7 @@ return (function () {
|
||||
sock.binaryType = htmx.config.wsBinaryType;
|
||||
return sock;
|
||||
},
|
||||
version: "1.9.10"
|
||||
version: "1.9.11"
|
||||
};
|
||||
|
||||
/** @type {import("./htmx").HtmxInternalApi} */
|
||||
@ -309,10 +309,24 @@ return (function () {
|
||||
content = content.replace(HEAD_TAG_REGEX, '');
|
||||
}
|
||||
if (htmx.config.useTemplateFragments && partialResponse) {
|
||||
var documentFragment = parseHTML("<body><template>" + content + "</template></body>", 0);
|
||||
var fragment = parseHTML("<body><template>" + content + "</template></body>", 0);
|
||||
// @ts-ignore type mismatch between DocumentFragment and Element.
|
||||
// TODO: Are these close enough for htmx to use interchangeably?
|
||||
return documentFragment.querySelector('template').content;
|
||||
if (htmx.config.allowScriptTags) {
|
||||
// if there is a nonce set up, set it on the new script tags
|
||||
forEach(fragment.querySelectorAll("script"), function (script) {
|
||||
if (htmx.config.inlineScriptNonce) {
|
||||
script.nonce = htmx.config.inlineScriptNonce;
|
||||
}
|
||||
getInternalData(script).executed = true; // mark as executed due to template insertion semantics
|
||||
})
|
||||
} else {
|
||||
forEach(fragment.querySelectorAll("script"), function (script) {
|
||||
// remove all script tags if scripts are disabled
|
||||
removeElement(script);
|
||||
})
|
||||
}
|
||||
return fragment.querySelector('template').content;
|
||||
}
|
||||
switch (startTag) {
|
||||
case "thead":
|
||||
@ -1892,7 +1906,8 @@ return (function () {
|
||||
}
|
||||
|
||||
function evalScript(script) {
|
||||
if (htmx.config.allowScriptTags && (script.type === "text/javascript" || script.type === "module" || script.type === "") ) {
|
||||
if (!getInternalData(script).executed && htmx.config.allowScriptTags &&
|
||||
(script.type === "text/javascript" || script.type === "module" || script.type === "") ) {
|
||||
var newScript = getDocument().createElement("script");
|
||||
forEach(script.attributes, function (attr) {
|
||||
newScript.setAttribute(attr.name, attr.value);
|
||||
@ -1918,12 +1933,14 @@ return (function () {
|
||||
}
|
||||
|
||||
function processScripts(elt) {
|
||||
if (matches(elt, "script")) {
|
||||
evalScript(elt);
|
||||
if (!htmx.config.useTemplateFragments) {
|
||||
if (matches(elt, "script")) {
|
||||
evalScript(elt);
|
||||
}
|
||||
forEach(findAll(elt, "script"), function (script) {
|
||||
evalScript(script);
|
||||
});
|
||||
}
|
||||
forEach(findAll(elt, "script"), function (script) {
|
||||
evalScript(script);
|
||||
});
|
||||
}
|
||||
|
||||
function shouldProcessHxOn(elt) {
|
||||
|
@ -13,19 +13,19 @@ describe("hx-sse attribute", function() {
|
||||
})
|
||||
},
|
||||
addEventListener: function(message, l) {
|
||||
if (listeners == undefined) {
|
||||
if (listeners[message] === undefined) {
|
||||
listeners[message] = [];
|
||||
}
|
||||
listeners[message].push(l)
|
||||
},
|
||||
sendEvent: function(eventName, data) {
|
||||
var listeners = listeners[eventName];
|
||||
if (listeners) {
|
||||
listeners.forEach(function(listener) {
|
||||
var eventListeners = listeners[eventName];
|
||||
if (eventListeners) {
|
||||
eventListeners.forEach(function(listener) {
|
||||
var event = htmx._("makeEvent")(eventName);
|
||||
event.data = data;
|
||||
listener(event);
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
close: function() {
|
||||
@ -47,6 +47,7 @@ describe("hx-sse attribute", function() {
|
||||
});
|
||||
afterEach(function() {
|
||||
this.server.restore();
|
||||
this.eventSource = mockEventSource();
|
||||
clearWorkArea();
|
||||
});
|
||||
|
||||
|
@ -212,4 +212,78 @@ describe("Core htmx Regression Tests", function(){
|
||||
var input = byId("id_email");
|
||||
input.value.should.equal("supertest@test.com");
|
||||
});
|
||||
|
||||
it("script tags only execute once", function(done) {
|
||||
window.i = 0; // set count to 0
|
||||
this.server.respondWith('GET', '/test', '<script>console.trace(); window.i++</script>') // increment the count by 1
|
||||
|
||||
// make a div w/ a short settle delay to make the problem more obvious
|
||||
var div = make('<div hx-get="/test" hx-swap="innerHTML settle:5ms"/>');
|
||||
div.click();
|
||||
this.server.respond()
|
||||
|
||||
setTimeout(function(){
|
||||
window.i.should.equal(1);
|
||||
delete window.i;
|
||||
done();
|
||||
}, 50)
|
||||
})
|
||||
|
||||
it("script tags only execute once when nested", function(done) {
|
||||
window.i = 0; // set count to 0
|
||||
this.server.respondWith('GET', '/test', '<p>foo</p><div><script>console.trace(); window.i++</script></div>') // increment the count by 1
|
||||
|
||||
// make a div w/ a short settle delay to make the problem more obvious
|
||||
var div = make('<div hx-get="/test" hx-swap="innerHTML settle:5ms"/>');
|
||||
div.click();
|
||||
this.server.respond()
|
||||
|
||||
setTimeout(function(){
|
||||
window.i.should.equal(1);
|
||||
delete window.i;
|
||||
done();
|
||||
}, 50)
|
||||
})
|
||||
|
||||
it("script tags only execute once using templates", function(done) {
|
||||
var oldUseTemplateFragmentsValue = htmx.config.useTemplateFragments
|
||||
htmx.config.useTemplateFragments = true
|
||||
|
||||
window.i = 0; // set count to 0
|
||||
this.server.respondWith('GET', '/test', '<script>console.trace(); window.i++</script>') // increment the count by 1
|
||||
|
||||
// make a div w/ a short settle delay to make the problem more obvious
|
||||
var div = make('<div hx-get="/test" hx-swap="innerHTML settle:5ms"/>');
|
||||
div.click();
|
||||
this.server.respond()
|
||||
|
||||
|
||||
setTimeout(function(){
|
||||
window.i.should.equal(1);
|
||||
delete window.i;
|
||||
htmx.config.useTemplateFragments = oldUseTemplateFragmentsValue
|
||||
done();
|
||||
}, 50)
|
||||
})
|
||||
|
||||
it("script tags only execute once when nested using templates", function(done) {
|
||||
var oldUseTemplateFragmentsValue = htmx.config.useTemplateFragments
|
||||
htmx.config.useTemplateFragments = true
|
||||
|
||||
window.i = 0; // set count to 0
|
||||
this.server.respondWith('GET', '/test', '<p>foo</p><div><script>console.trace(); window.i++</script></div>') // increment the count by 1
|
||||
|
||||
// make a div w/ a short settle delay to make the problem more obvious
|
||||
var div = make('<div hx-get="/test" hx-swap="innerHTML settle:5ms"/>');
|
||||
div.click();
|
||||
this.server.respond()
|
||||
|
||||
setTimeout(function(){
|
||||
window.i.should.equal(1);
|
||||
delete window.i;
|
||||
htmx.config.useTemplateFragments = oldUseTemplateFragmentsValue
|
||||
done();
|
||||
}, 50)
|
||||
})
|
||||
|
||||
});
|
||||
|
@ -1,5 +1,3 @@
|
||||
const { assert } = require("chai");
|
||||
|
||||
describe("sse extension", function() {
|
||||
|
||||
function mockEventSource() {
|
||||
@ -7,6 +5,7 @@ describe("sse extension", function() {
|
||||
var wasClosed = false;
|
||||
var url;
|
||||
var mockEventSource = {
|
||||
_listeners: listeners,
|
||||
removeEventListener: function(name, l) {
|
||||
listeners[name] = listeners[name].filter(function(elt, idx, arr) {
|
||||
if (arr[idx] === l) {
|
||||
@ -16,19 +15,19 @@ describe("sse extension", function() {
|
||||
})
|
||||
},
|
||||
addEventListener: function(message, l) {
|
||||
if (listeners == undefined) {
|
||||
if (listeners[message] === undefined) {
|
||||
listeners[message] = [];
|
||||
}
|
||||
listeners[message].push(l)
|
||||
},
|
||||
sendEvent: function(eventName, data) {
|
||||
var listeners = listeners[eventName];
|
||||
if (listeners) {
|
||||
listeners.forEach(function(listener) {
|
||||
var eventListeners = listeners[eventName];
|
||||
if (eventListeners) {
|
||||
eventListeners.forEach(function(listener) {
|
||||
var event = htmx._("makeEvent")(eventName);
|
||||
event.data = data;
|
||||
listener(event);
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
close: function() {
|
||||
@ -56,6 +55,7 @@ describe("sse extension", function() {
|
||||
});
|
||||
afterEach(function() {
|
||||
this.server.restore();
|
||||
this.eventSource = mockEventSource();
|
||||
clearWorkArea();
|
||||
});
|
||||
|
||||
@ -150,6 +150,16 @@ describe("sse extension", function() {
|
||||
this.eventSource.wasClosed().should.equal(true)
|
||||
})
|
||||
|
||||
it('is not listening for events after hx-swap element removed', function() {
|
||||
var div = make('<div hx-ext="sse" sse-connect="/foo">' +
|
||||
'<div id="d1" hx-swap="outerHTML" sse-swap="e1">div1</div>' +
|
||||
'</div>');
|
||||
this.eventSource._listeners["e1"].should.be.lengthOf(1)
|
||||
div.removeChild(byId("d1"));
|
||||
this.eventSource.sendEvent("e1", "Test")
|
||||
this.eventSource._listeners["e1"].should.be.empty
|
||||
})
|
||||
|
||||
// sse and hx-trigger handlers are distinct
|
||||
it('is closed after removal with no close and activity, sse-swap', function() {
|
||||
var div = make('<div hx-get="/test" hx-swap="outerHTML" hx-ext="sse" sse-connect="/foo">' +
|
||||
@ -191,7 +201,8 @@ describe("sse extension", function() {
|
||||
'<div id="d1" sse-connect="/event_stream" sse-swap="e1">div1</div>\n' +
|
||||
'</div>\n'
|
||||
)
|
||||
this.eventSource.url = "/event_stream"
|
||||
this.eventSource.sendEvent("e1", "Event 1")
|
||||
byId("d1").innerText.should.equal("Event 1")
|
||||
})
|
||||
|
||||
it('only adds sseEventSource to elements with sse-connect', function() {
|
||||
@ -228,5 +239,63 @@ describe("sse extension", function() {
|
||||
|
||||
})
|
||||
|
||||
it('raises htmx:sseBeforeMessage when receiving message from the server', function () {
|
||||
var myEventCalled = false;
|
||||
|
||||
function handle(evt) {
|
||||
myEventCalled = true;
|
||||
}
|
||||
|
||||
htmx.on("htmx:sseBeforeMessage", handle)
|
||||
|
||||
var div = make('<div hx-ext="sse" sse-connect="/event_stream" sse-swap="e1"></div>');
|
||||
|
||||
this.eventSource.sendEvent("e1", '<div id="d1"></div>')
|
||||
|
||||
myEventCalled.should.be.true;
|
||||
|
||||
htmx.off("htmx:sseBeforeMessage", handle)
|
||||
})
|
||||
|
||||
it('cancels swap when htmx:sseBeforeMessage was cancelled', function () {
|
||||
var myEventCalled = false;
|
||||
|
||||
function handle(evt) {
|
||||
myEventCalled = true;
|
||||
evt.preventDefault();
|
||||
}
|
||||
|
||||
htmx.on("htmx:sseBeforeMessage", handle)
|
||||
|
||||
var div = make('<div hx-ext="sse" sse-connect="/event_stream" sse-swap="e1"><div id="d1">div1</div></div>');
|
||||
|
||||
this.eventSource.sendEvent("e1", '<div id="d1">replaced</div>')
|
||||
|
||||
myEventCalled.should.be.true;
|
||||
|
||||
byId("d1").innerHTML.should.equal("div1");
|
||||
|
||||
htmx.off("htmx:sseBeforeMessage", handle)
|
||||
})
|
||||
|
||||
it('raises htmx:sseMessage when message was completely processed', function () {
|
||||
var myEventCalled = false;
|
||||
|
||||
function handle(evt) {
|
||||
myEventCalled = true;
|
||||
}
|
||||
|
||||
htmx.on("htmx:sseMessage", handle)
|
||||
|
||||
var div = make('<div hx-ext="sse" sse-connect="/event_stream" sse-swap="e1"><div id="d1">div1</div></div>');
|
||||
|
||||
this.eventSource.sendEvent("e1", '<div id="d1">replaced</div>')
|
||||
|
||||
myEventCalled.should.be.true;
|
||||
byId("d1").innerHTML.should.equal("replaced");
|
||||
|
||||
htmx.off("htmx:sseMessage", handle)
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
</ul>
|
||||
|
||||
<h2>Manual Tests</h2>
|
||||
<a href="manual">Here</a>
|
||||
<a href="manual/">Here</a>
|
||||
|
||||
<h2>Mocha Test Suite</h2>
|
||||
<a href="index.html">[ALL]</a>
|
||||
|
@ -89,7 +89,7 @@ return (function () {
|
||||
sock.binaryType = htmx.config.wsBinaryType;
|
||||
return sock;
|
||||
},
|
||||
version: "1.9.10"
|
||||
version: "1.9.11"
|
||||
};
|
||||
|
||||
/** @type {import("./htmx").HtmxInternalApi} */
|
||||
@ -309,10 +309,24 @@ return (function () {
|
||||
content = content.replace(HEAD_TAG_REGEX, '');
|
||||
}
|
||||
if (htmx.config.useTemplateFragments && partialResponse) {
|
||||
var documentFragment = parseHTML("<body><template>" + content + "</template></body>", 0);
|
||||
var fragment = parseHTML("<body><template>" + content + "</template></body>", 0);
|
||||
// @ts-ignore type mismatch between DocumentFragment and Element.
|
||||
// TODO: Are these close enough for htmx to use interchangeably?
|
||||
return documentFragment.querySelector('template').content;
|
||||
if (htmx.config.allowScriptTags) {
|
||||
// if there is a nonce set up, set it on the new script tags
|
||||
forEach(fragment.querySelectorAll("script"), function (script) {
|
||||
if (htmx.config.inlineScriptNonce) {
|
||||
script.nonce = htmx.config.inlineScriptNonce;
|
||||
}
|
||||
getInternalData(script).executed = true; // mark as executed due to template insertion semantics
|
||||
})
|
||||
} else {
|
||||
forEach(fragment.querySelectorAll("script"), function (script) {
|
||||
// remove all script tags if scripts are disabled
|
||||
removeElement(script);
|
||||
})
|
||||
}
|
||||
return fragment.querySelector('template').content;
|
||||
}
|
||||
switch (startTag) {
|
||||
case "thead":
|
||||
@ -1892,7 +1906,8 @@ return (function () {
|
||||
}
|
||||
|
||||
function evalScript(script) {
|
||||
if (htmx.config.allowScriptTags && (script.type === "text/javascript" || script.type === "module" || script.type === "") ) {
|
||||
if (!getInternalData(script).executed && htmx.config.allowScriptTags &&
|
||||
(script.type === "text/javascript" || script.type === "module" || script.type === "") ) {
|
||||
var newScript = getDocument().createElement("script");
|
||||
forEach(script.attributes, function (attr) {
|
||||
newScript.setAttribute(attr.name, attr.value);
|
||||
@ -1918,12 +1933,14 @@ return (function () {
|
||||
}
|
||||
|
||||
function processScripts(elt) {
|
||||
if (matches(elt, "script")) {
|
||||
evalScript(elt);
|
||||
if (!htmx.config.useTemplateFragments) {
|
||||
if (matches(elt, "script")) {
|
||||
evalScript(elt);
|
||||
}
|
||||
forEach(findAll(elt, "script"), function (script) {
|
||||
evalScript(script);
|
||||
});
|
||||
}
|
||||
forEach(findAll(elt, "script"), function (script) {
|
||||
evalScript(script);
|
||||
});
|
||||
}
|
||||
|
||||
function shouldProcessHxOn(elt) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user