mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-10-02 15:25:26 +00:00
Update htmx to latest hyperscript, fix tests
This commit is contained in:
parent
9c592d4e2f
commit
45f3909b9c
12
src/htmx.js
12
src/htmx.js
@ -457,7 +457,7 @@ return (function () {
|
||||
processNode(child);
|
||||
processScripts(child);
|
||||
processFocus(child)
|
||||
triggerEvent(child, 'htmx:load', {});
|
||||
triggerEvent(child, 'htmx:load');
|
||||
};
|
||||
}
|
||||
|
||||
@ -978,14 +978,10 @@ return (function () {
|
||||
});
|
||||
}
|
||||
|
||||
function isHyperScriptAvailable() {
|
||||
return typeof _hyperscript !== "undefined";
|
||||
}
|
||||
|
||||
function findElementsToProcess(elt) {
|
||||
if (elt.querySelectorAll) {
|
||||
var results = elt.querySelectorAll(VERB_SELECTOR + ", a, form, [hx-sse], [data-hx-sse], [hx-ws]," +
|
||||
" [data-hx-ws], [_], [script], [data-script]");
|
||||
" [data-hx-ws]");
|
||||
return results;
|
||||
} else {
|
||||
return [];
|
||||
@ -997,10 +993,6 @@ return (function () {
|
||||
if (!nodeData.initialized) {
|
||||
nodeData.initialized = true;
|
||||
|
||||
if (isHyperScriptAvailable()) {
|
||||
_hyperscript.init(elt);
|
||||
}
|
||||
|
||||
if (elt.value) {
|
||||
nodeData.lastValue = elt.value;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ describe("hyperscript integration", function() {
|
||||
it('can trigger with a custom event', function () {
|
||||
this.server.respondWith("GET", "/test", "Custom Event Sent!");
|
||||
var btn = make('<button _="on click send customEvent" hx-trigger="customEvent" hx-get="/test">Click Me!</button>')
|
||||
htmx.trigger(btn, "htmx:load"); // have to manually trigger the load event for non-AJAX dynamic content
|
||||
btn.click();
|
||||
this.server.respond();
|
||||
btn.innerHTML.should.equal("Custom Event Sent!");
|
||||
@ -19,6 +20,7 @@ describe("hyperscript integration", function() {
|
||||
it('can handle htmx driven events', function () {
|
||||
this.server.respondWith("GET", "/test", "Clicked!");
|
||||
var btn = make('<button _="on htmx:afterSettle add .afterSettle" hx-get="/test">Click Me!</button>')
|
||||
htmx.trigger(btn, "htmx:load");
|
||||
btn.classList.contains("afterSettle").should.equal(false);
|
||||
btn.click();
|
||||
this.server.respond();
|
||||
@ -29,9 +31,20 @@ describe("hyperscript integration", function() {
|
||||
this.server.respondWith("GET", "/test", [404, {}, "Bad request"]);
|
||||
var div = make('<div id="d1"></div>')
|
||||
var btn = make('<button _="on htmx:error(errorInfo) put errorInfo.error into #d1.innerHTML" hx-get="/test">Click Me!</button>')
|
||||
htmx.trigger(btn, "htmx:load");
|
||||
btn.click();
|
||||
this.server.respond();
|
||||
div.innerHTML.should.equal("Response Status Error Code 404 from /test");
|
||||
});
|
||||
|
||||
it('hyperscript in non-htmx annotated nodes is evaluated', function () {
|
||||
this.server.respondWith("GET", "/test", "<div><div><div id='d1' _='on click put \"Clicked...\" into my.innerHTML'></div></div></div>");
|
||||
var btn = make('<button hx-get="/test">Click Me!</button>')
|
||||
btn.click();
|
||||
this.server.respond();
|
||||
var newDiv = byId("d1");
|
||||
newDiv.click();
|
||||
newDiv.innerText.should.equal("Clicked...");
|
||||
});
|
||||
|
||||
});
|
@ -92,9 +92,6 @@
|
||||
<!-- hyperscript integration -->
|
||||
<script src="lib/_hyperscript.js"></script>
|
||||
<script src="ext/hyperscript.js"></script>
|
||||
<script>
|
||||
_hyperscript.start();
|
||||
</script>
|
||||
|
||||
<!-- extension tests -->
|
||||
<script src="ext/extension-swap.js"></script>
|
||||
|
@ -11,9 +11,39 @@
|
||||
return (function () {
|
||||
'use strict';
|
||||
|
||||
//-----------------------------------------------
|
||||
//====================================================================
|
||||
// Utilities
|
||||
//====================================================================
|
||||
|
||||
function mergeObjects(obj1, obj2) {
|
||||
for (var key in obj2) {
|
||||
if (obj2.hasOwnProperty(key)) {
|
||||
obj1[key] = obj2[key];
|
||||
}
|
||||
}
|
||||
return obj1;
|
||||
}
|
||||
|
||||
function parseJSON(jString) {
|
||||
try {
|
||||
return JSON.parse(jString);
|
||||
} catch(error) {
|
||||
logError(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function logError(msg) {
|
||||
if(console.error) {
|
||||
console.error(msg);
|
||||
} else if (console.log) {
|
||||
console.log("ERROR: ", msg);
|
||||
}
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
// Lexer
|
||||
//-----------------------------------------------
|
||||
//====================================================================
|
||||
var _lexer = function () {
|
||||
var OP_TABLE = {
|
||||
'+': 'PLUS',
|
||||
@ -77,6 +107,10 @@
|
||||
(c >= 'A' && c <= 'Z');
|
||||
}
|
||||
|
||||
function isIdentifierChar(c) {
|
||||
return (c === "_" || c === "$");
|
||||
}
|
||||
|
||||
|
||||
function makeTokensObject(tokens, consumed, source) {
|
||||
|
||||
@ -205,7 +239,7 @@
|
||||
tokens.push(consumeClassReference());
|
||||
} else if (!possiblePrecedingSymbol() && currentChar() === "#" && isAlpha(nextChar())) {
|
||||
tokens.push(consumeIdReference());
|
||||
} else if (isAlpha(currentChar())) {
|
||||
} else if (isAlpha(currentChar()) || isIdentifierChar(currentChar())) {
|
||||
tokens.push(consumeIdentifier());
|
||||
} else if (isNumeric(currentChar())) {
|
||||
tokens.push(consumeNumber());
|
||||
@ -273,7 +307,7 @@
|
||||
function consumeIdentifier() {
|
||||
var identifier = makeToken("IDENTIFIER");
|
||||
var value = consumeChar();
|
||||
while (isAlpha(currentChar())) {
|
||||
while (isAlpha(currentChar()) || isIdentifierChar(currentChar())) {
|
||||
value += consumeChar();
|
||||
}
|
||||
identifier.value = value;
|
||||
@ -369,9 +403,9 @@
|
||||
}
|
||||
}();
|
||||
|
||||
//-----------------------------------------------
|
||||
//====================================================================
|
||||
// Parser
|
||||
//-----------------------------------------------
|
||||
//====================================================================
|
||||
var _parser = function () {
|
||||
|
||||
var GRAMMAR = {}
|
||||
@ -440,11 +474,10 @@
|
||||
}
|
||||
}();
|
||||
|
||||
//-----------------------------------------------
|
||||
//====================================================================
|
||||
// Runtime
|
||||
//-----------------------------------------------
|
||||
//====================================================================
|
||||
var _runtime = function () {
|
||||
var SCRIPT_ATTRIBUTES = ["_", "script", "data-script"];
|
||||
|
||||
function matchesSelector(elt, selector) {
|
||||
// noinspection JSUnresolvedVariable
|
||||
@ -509,9 +542,17 @@
|
||||
return last;
|
||||
}
|
||||
|
||||
var _scriptAttrs = null;
|
||||
function getScriptAttributes() {
|
||||
if (_scriptAttrs == null) {
|
||||
_scriptAttrs = _hyperscript.config.attributes.replace(/ /g,'').split(",")
|
||||
}
|
||||
return _scriptAttrs;
|
||||
}
|
||||
|
||||
function getScript(elt) {
|
||||
for (var i = 0; i < SCRIPT_ATTRIBUTES.length; i++) {
|
||||
var scriptAttribute = SCRIPT_ATTRIBUTES[i];
|
||||
for (var i = 0; i < getScriptAttributes().length; i++) {
|
||||
var scriptAttribute = getScriptAttributes()[i];
|
||||
if (elt.hasAttribute && elt.hasAttribute(scriptAttribute)) {
|
||||
return elt.getAttribute(scriptAttribute)
|
||||
}
|
||||
@ -525,12 +566,8 @@
|
||||
});
|
||||
}
|
||||
|
||||
function setScriptAttrs(values) {
|
||||
SCRIPT_ATTRIBUTES = values;
|
||||
}
|
||||
|
||||
function getScriptSelector() {
|
||||
return SCRIPT_ATTRIBUTES.map(function (attribute) {
|
||||
return getScriptAttributes().map(function (attribute) {
|
||||
return "[" + attribute + "]";
|
||||
}).join(", ");
|
||||
}
|
||||
@ -562,9 +599,23 @@
|
||||
return eval(evalString).apply(null, args);
|
||||
}
|
||||
|
||||
function processNode(elt) {
|
||||
var selector = _runtime.getScriptSelector();
|
||||
if (matchesSelector(elt, selector)) {
|
||||
initElement(elt);
|
||||
}
|
||||
forEach(elt.querySelectorAll(selector), function (elt) {
|
||||
initElement(elt);
|
||||
})
|
||||
}
|
||||
|
||||
function initElement(elt) {
|
||||
var internalData = getInternalData(elt);
|
||||
if (!internalData.initialized) {
|
||||
var src = getScript(elt);
|
||||
if (src) {
|
||||
internalData.initialized = true;
|
||||
internalData.script = src;
|
||||
var tokens = _lexer.tokenize(src);
|
||||
var hyperScript = _parser.parseHyperScript(tokens);
|
||||
var transpiled = _parser.transpile(hyperScript);
|
||||
@ -575,6 +626,17 @@
|
||||
hyperscriptObj.applyEventListenersTo(elt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getInternalData(elt) {
|
||||
var dataProp = 'hyperscript-internal-data';
|
||||
var data = elt[dataProp];
|
||||
if (!data) {
|
||||
data = elt[dataProp] = {};
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
function ajax(method, url, callback, data) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
@ -606,18 +668,17 @@
|
||||
matchesSelector: matchesSelector,
|
||||
getScript: getScript,
|
||||
applyEventListeners: applyEventListeners,
|
||||
setScriptAttrs: setScriptAttrs,
|
||||
initElement: initElement,
|
||||
processNode: processNode,
|
||||
evaluate: evaluate,
|
||||
getScriptSelector: getScriptSelector,
|
||||
ajax: ajax,
|
||||
}
|
||||
}();
|
||||
|
||||
//-----------------------------------------------
|
||||
// Expressions
|
||||
//-----------------------------------------------
|
||||
|
||||
//====================================================================
|
||||
// Grammar
|
||||
//====================================================================
|
||||
{
|
||||
_parser.addGrammarElement("parenthesized", function (parser, tokens) {
|
||||
if (tokens.matchOpToken('(')) {
|
||||
var expr = parser.parseElement("expression", tokens);
|
||||
@ -657,7 +718,9 @@
|
||||
type: "nakedString",
|
||||
tokens: tokenArr,
|
||||
transpile: function () {
|
||||
return "'" + tokenArr.map(function(t){return t.value}).join("") + "'";
|
||||
return "'" + tokenArr.map(function (t) {
|
||||
return t.value
|
||||
}).join("") + "'";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -872,7 +935,9 @@
|
||||
type: "arrayLiteral",
|
||||
values: values,
|
||||
transpile: function () {
|
||||
return "[" + values.map(function(v){ return parser.transpile(v) }).join(", ") + "]";
|
||||
return "[" + values.map(function (v) {
|
||||
return parser.transpile(v)
|
||||
}).join(", ") + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -931,10 +996,12 @@
|
||||
_parser.addGrammarElement("functionCall", function (parser, tokens, root) {
|
||||
if (tokens.matchOpToken("(")) {
|
||||
var args = [];
|
||||
if (!tokens.matchOpToken(')')) {
|
||||
do {
|
||||
args.push(parser.parseElement("expression", tokens));
|
||||
} while (tokens.matchOpToken(","))
|
||||
tokens.requireOpToken(")");
|
||||
}
|
||||
var functionCall = {
|
||||
type: "functionCall",
|
||||
root: root,
|
||||
@ -1169,7 +1236,6 @@
|
||||
};
|
||||
})
|
||||
|
||||
|
||||
_parser.addGrammarElement("eventListener", function (parser, tokens) {
|
||||
tokens.requireToken("on");
|
||||
var on = parser.parseElement("dotOrColonPath", tokens);
|
||||
@ -1204,7 +1270,9 @@
|
||||
"var my = me;\n" +
|
||||
"_hyperscript.runtime.forEach( " + parser.transpile(from) + ", function(target){\n" +
|
||||
" target.addEventListener('" + parser.transpile(on) + "', function(event){\n" +
|
||||
args.map(function(arg){return "var " + arg.value + " = event.detail." + arg.value + ";"}).join("\n") + "\n" +
|
||||
args.map(function (arg) {
|
||||
return "var " + arg.value + " = event.detail." + arg.value + ";"
|
||||
}).join("\n") + "\n" +
|
||||
parser.transpile(start) +
|
||||
" })\n" +
|
||||
"})\n" +
|
||||
@ -1645,45 +1713,67 @@
|
||||
};
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
//====================================================================
|
||||
// API
|
||||
//-----------------------------------------------
|
||||
//====================================================================
|
||||
|
||||
function start(scriptAttrs) {
|
||||
if (scriptAttrs) {
|
||||
_runtime.setScriptAttrs(scriptAttrs);
|
||||
}
|
||||
var fn = function () {
|
||||
var elements = document.querySelectorAll(_runtime.getScriptSelector());
|
||||
_runtime.forEach(elements, function (elt) {
|
||||
init(elt);
|
||||
})
|
||||
};
|
||||
if (document.readyState !== 'loading') {
|
||||
fn();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', fn);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function init(elt) {
|
||||
_runtime.initElement(elt);
|
||||
function processNode(elt) {
|
||||
_runtime.processNode(elt);
|
||||
}
|
||||
|
||||
function evaluate(str) {
|
||||
return _runtime.evaluate(str);
|
||||
}
|
||||
|
||||
return {
|
||||
//====================================================================
|
||||
// Initialization
|
||||
//====================================================================
|
||||
function ready(fn) {
|
||||
if (document.readyState !== 'loading') {
|
||||
fn();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', fn);
|
||||
}
|
||||
}
|
||||
|
||||
function getMetaConfig() {
|
||||
var element = document.querySelector('meta[name="htmx-config"]');
|
||||
if (element) {
|
||||
return parseJSON(element.content);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function mergeMetaConfig() {
|
||||
var metaConfig = getMetaConfig();
|
||||
if (metaConfig) {
|
||||
_hyperscript.config = mergeObjects(_hyperscript.config , metaConfig)
|
||||
}
|
||||
}
|
||||
|
||||
var _hyperscript = {
|
||||
lexer: _lexer,
|
||||
parser: _parser,
|
||||
runtime: _runtime,
|
||||
evaluate: evaluate,
|
||||
init: init,
|
||||
start: start
|
||||
processNode: processNode,
|
||||
config: {
|
||||
attributes : "_, script, data-script"
|
||||
}
|
||||
};
|
||||
|
||||
ready(function () {
|
||||
mergeMetaConfig();
|
||||
processNode(document.body);
|
||||
document.addEventListener("htmx:load", function(evt){
|
||||
processNode(evt.detail.elt);
|
||||
})
|
||||
})
|
||||
|
||||
return _hyperscript;
|
||||
}
|
||||
)()
|
||||
}));
|
Loading…
x
Reference in New Issue
Block a user