mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-09-28 21:41:40 +00:00
Two-fer:
* Implement `hx-vals` which is a JSON-only (and therefore safer) version of `hx-vars` * Port all internal evals to use `Function` instead May fix https://github.com/bigskysoftware/htmx/issues/213
This commit is contained in:
parent
2209c4bf6f
commit
5acd554a5c
99
dist/htmx.js
vendored
99
dist/htmx.js
vendored
@ -161,6 +161,8 @@ return (function () {
|
||||
case "td":
|
||||
case "th":
|
||||
return parseHTML("<table><tbody><tr>" + resp + "</tr></tbody></table>", 3);
|
||||
case "script":
|
||||
return parseHTML("<div>" + resp + "</div>", 1);
|
||||
default:
|
||||
return parseHTML(resp, 0);
|
||||
}
|
||||
@ -610,7 +612,19 @@ return (function () {
|
||||
}
|
||||
}
|
||||
|
||||
var TITLE_FINDER = /<title>([\s\S]+?)<\/title>/im;
|
||||
function findTitle(content) {
|
||||
var result = TITLE_FINDER.exec(content);
|
||||
if (result) {
|
||||
return result[1];
|
||||
}
|
||||
}
|
||||
|
||||
function selectAndSwap(swapStyle, target, elt, responseText, settleInfo) {
|
||||
var title = findTitle(responseText);
|
||||
if(title) {
|
||||
window.document.title = title;
|
||||
}
|
||||
var fragment = makeFragment(responseText);
|
||||
if (fragment) {
|
||||
handleOutOfBandSwaps(fragment, settleInfo);
|
||||
@ -686,7 +700,7 @@ return (function () {
|
||||
if (tokens[0] === '[') {
|
||||
tokens.shift();
|
||||
var bracketCount = 1;
|
||||
var conditionalSource = "(function(" + paramName + "){ return (";
|
||||
var conditionalSource = " return (function(" + paramName + "){ return (";
|
||||
var last = null;
|
||||
while (tokens.length > 0) {
|
||||
var token = tokens[0];
|
||||
@ -699,7 +713,7 @@ return (function () {
|
||||
tokens.shift();
|
||||
conditionalSource += ")})";
|
||||
try {
|
||||
var conditionFunction = eval(conditionalSource);
|
||||
var conditionFunction = Function(conditionalSource)();
|
||||
conditionFunction.source = conditionalSource;
|
||||
return conditionFunction;
|
||||
} catch (e) {
|
||||
@ -993,9 +1007,11 @@ return (function () {
|
||||
elt.addEventListener(getTriggerSpecs(elt)[0].trigger, function (evt) {
|
||||
var headers = getHeaders(elt, webSocketSourceElt, null, elt);
|
||||
var results = getInputValues(elt, 'post');
|
||||
var rawParameters = results.values;
|
||||
var errors = results.errors;
|
||||
var filteredParameters = filterValues(rawParameters, elt);
|
||||
var rawParameters = results.values;
|
||||
var expressionVars = getHXVarsForElement(elt);
|
||||
var allParameters = mergeObjects(rawParameters, expressionVars);
|
||||
var filteredParameters = filterValues(allParameters, elt);
|
||||
filteredParameters['HEADERS'] = headers;
|
||||
if (errors && errors.length > 0) {
|
||||
triggerEvent(elt, 'htmx:validation:halted', errors);
|
||||
@ -1165,9 +1181,14 @@ return (function () {
|
||||
});
|
||||
}
|
||||
|
||||
function isBoosted() {
|
||||
return document.querySelector("[hx-boost], [data-hx-boost]");
|
||||
}
|
||||
|
||||
function findElementsToProcess(elt) {
|
||||
if (elt.querySelectorAll) {
|
||||
var results = elt.querySelectorAll(VERB_SELECTOR + ", a, form, [hx-sse], [data-hx-sse], [hx-ws]," +
|
||||
var boostedElts = isBoosted() ? ", a, form" : "";
|
||||
var results = elt.querySelectorAll(VERB_SELECTOR + boostedElts + ", [hx-sse], [data-hx-sse], [hx-ws]," +
|
||||
" [data-hx-ws]");
|
||||
return results;
|
||||
} else {
|
||||
@ -1698,22 +1719,41 @@ return (function () {
|
||||
}
|
||||
}
|
||||
|
||||
function addExpressionVars(elt, rawParameters) {
|
||||
if (elt == null) {
|
||||
return;
|
||||
function getValuesForElement(elt, attr, strToValues, expressionVars) {
|
||||
if (expressionVars == null) {
|
||||
expressionVars = {};
|
||||
}
|
||||
var attributeValue = getAttributeValue(elt, "hx-vars");
|
||||
if (elt == null) {
|
||||
return expressionVars;
|
||||
}
|
||||
var attributeValue = getAttributeValue(elt, attr);
|
||||
if (attributeValue) {
|
||||
var varsValues = eval("({" + attributeValue + "})");
|
||||
var str = attributeValue.trim();
|
||||
if (str.indexOf('{') !== 0) {
|
||||
str = "{" + str + "}";
|
||||
}
|
||||
var varsValues = strToValues(str);
|
||||
for (var key in varsValues) {
|
||||
if (varsValues.hasOwnProperty(key)) {
|
||||
if (rawParameters[key] == null) {
|
||||
rawParameters[key] = varsValues[key];
|
||||
if (expressionVars[key] == null) {
|
||||
expressionVars[key] = varsValues[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
addExpressionVars(parentElt(elt), rawParameters);
|
||||
return getValuesForElement(parentElt(elt), attr, strToValues, expressionVars);
|
||||
}
|
||||
|
||||
function getHXVarsForElement(elt, expressionVars) {
|
||||
return getValuesForElement(elt, "hx-vars", function(valueStr){
|
||||
return Function("return (" + valueStr + ")")()
|
||||
}, expressionVars);
|
||||
}
|
||||
|
||||
function getHXValsForElement(elt, expressionVars) {
|
||||
return getValuesForElement(elt, "hx-vars", function(valueStr){
|
||||
return JSON.parse(expressionVars);
|
||||
}, expressionVars);
|
||||
}
|
||||
|
||||
function safelySetHeaderValue(xhr, header, headerValue) {
|
||||
@ -1745,6 +1785,10 @@ return (function () {
|
||||
}
|
||||
|
||||
function issueAjaxRequest(elt, verb, path, eventTarget, triggeringEvent) {
|
||||
if (!bodyContains(elt)) {
|
||||
console.log("Body does not contain", elt);
|
||||
return; // do not issue requests for elements removed from the DOM
|
||||
}
|
||||
var target = getTarget(elt);
|
||||
if (target == null) {
|
||||
triggerErrorEvent(elt, 'htmx:targetError', {target: getAttributeValue(elt, "hx-target")});
|
||||
@ -1783,10 +1827,11 @@ return (function () {
|
||||
|
||||
var headers = getHeaders(elt, target, promptResponse, eventTarget);
|
||||
var results = getInputValues(elt, verb);
|
||||
var rawParameters = results.values;
|
||||
var errors = results.errors;
|
||||
addExpressionVars(elt, rawParameters);
|
||||
var filteredParameters = filterValues(rawParameters, elt);
|
||||
var rawParameters = results.values;
|
||||
var expressionVars = getHXVarsForElement(elt);
|
||||
var allParameters = mergeObjects(rawParameters, expressionVars);
|
||||
var filteredParameters = filterValues(allParameters, elt);
|
||||
|
||||
if (verb !== 'get' && getClosestAttributeValue(elt, "hx-encoding") == null) {
|
||||
headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
|
||||
@ -1799,7 +1844,7 @@ return (function () {
|
||||
|
||||
var requestConfig = {
|
||||
parameters: filteredParameters,
|
||||
unfilteredParameters:rawParameters,
|
||||
unfilteredParameters: allParameters,
|
||||
headers:headers,
|
||||
target:target,
|
||||
verb:verb,
|
||||
@ -1863,7 +1908,19 @@ return (function () {
|
||||
}
|
||||
|
||||
if (hasHeader(xhr,/HX-Push:/i)) {
|
||||
var pushedUrl = this.getResponseHeader("HX-Push");
|
||||
var pushedUrl = xhr.getResponseHeader("HX-Push");
|
||||
}
|
||||
|
||||
if (hasHeader(xhr, /HX-Redirect:/i)) {
|
||||
window.location.href = xhr.getResponseHeader("HX-Redirect");
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasHeader(xhr,/HX-Refresh:/i)) {
|
||||
if ("true" === xhr.getResponseHeader("HX-Refresh")) {
|
||||
location.reload();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var shouldSaveHistory = shouldPush(elt) || pushedUrl;
|
||||
@ -1876,9 +1933,9 @@ return (function () {
|
||||
if (this.status !== 204) {
|
||||
if (!triggerEvent(target, 'htmx:beforeSwap', eventDetail)) return;
|
||||
|
||||
var resp = this.response;
|
||||
var serverResponse = this.response;
|
||||
withExtensions(elt, function(extension){
|
||||
resp = extension.transformResponse(resp, xhr, elt);
|
||||
serverResponse = extension.transformResponse(serverResponse, xhr, elt);
|
||||
});
|
||||
|
||||
// Save current page
|
||||
@ -1900,7 +1957,7 @@ return (function () {
|
||||
};
|
||||
|
||||
var settleInfo = makeSettleInfo(target);
|
||||
selectAndSwap(swapSpec.swapStyle, target, elt, resp, settleInfo);
|
||||
selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo);
|
||||
|
||||
if (selectionInfo.elt &&
|
||||
!bodyContains(selectionInfo.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.
35
src/htmx.js
35
src/htmx.js
@ -700,7 +700,7 @@ return (function () {
|
||||
if (tokens[0] === '[') {
|
||||
tokens.shift();
|
||||
var bracketCount = 1;
|
||||
var conditionalSource = "(function(" + paramName + "){ return (";
|
||||
var conditionalSource = " return (function(" + paramName + "){ return (";
|
||||
var last = null;
|
||||
while (tokens.length > 0) {
|
||||
var token = tokens[0];
|
||||
@ -713,7 +713,7 @@ return (function () {
|
||||
tokens.shift();
|
||||
conditionalSource += ")})";
|
||||
try {
|
||||
var conditionFunction = eval(conditionalSource);
|
||||
var conditionFunction = Function(conditionalSource)();
|
||||
conditionFunction.source = conditionalSource;
|
||||
return conditionFunction;
|
||||
} catch (e) {
|
||||
@ -1719,13 +1719,20 @@ return (function () {
|
||||
}
|
||||
}
|
||||
|
||||
function getExpressionVars(elt, expressionVars = []) {
|
||||
function getValuesForElement(elt, attr, strToValues, expressionVars) {
|
||||
if (expressionVars == null) {
|
||||
expressionVars = {};
|
||||
}
|
||||
if (elt == null) {
|
||||
return expressionVars;
|
||||
}
|
||||
var attributeValue = getAttributeValue(elt, "hx-vars");
|
||||
var attributeValue = getAttributeValue(elt, attr);
|
||||
if (attributeValue) {
|
||||
var varsValues = eval("({" + attributeValue + "})");
|
||||
var str = attributeValue.trim();
|
||||
if (str.indexOf('{') !== 0) {
|
||||
str = "{" + str + "}";
|
||||
}
|
||||
var varsValues = strToValues(str);
|
||||
for (var key in varsValues) {
|
||||
if (varsValues.hasOwnProperty(key)) {
|
||||
if (expressionVars[key] == null) {
|
||||
@ -1734,7 +1741,23 @@ return (function () {
|
||||
}
|
||||
}
|
||||
}
|
||||
return getExpressionVars(parentElt(elt), expressionVars);
|
||||
return getValuesForElement(parentElt(elt), attr, strToValues, expressionVars);
|
||||
}
|
||||
|
||||
function getHXVarsForElement(elt, expressionVars) {
|
||||
return getValuesForElement(elt, "hx-vars", function(valueStr){
|
||||
return Function("return (" + valueStr + ")")()
|
||||
}, expressionVars);
|
||||
}
|
||||
|
||||
function getHXValsForElement(elt, expressionVars) {
|
||||
return getValuesForElement(elt, "hx-vals", function(valueStr){
|
||||
return JSON.parse(valueStr);
|
||||
}, expressionVars);
|
||||
}
|
||||
|
||||
function getExpressionVars(elt) {
|
||||
return mergeObjects(getHXVarsForElement(elt), getHXValsForElement(elt));
|
||||
}
|
||||
|
||||
function safelySetHeaderValue(xhr, header, headerValue) {
|
||||
|
87
test/attributes/hx-vals.js
Normal file
87
test/attributes/hx-vals.js
Normal file
@ -0,0 +1,87 @@
|
||||
describe("hx-vals attribute", function() {
|
||||
beforeEach(function () {
|
||||
this.server = makeServer();
|
||||
clearWorkArea();
|
||||
});
|
||||
afterEach(function () {
|
||||
this.server.restore();
|
||||
clearWorkArea();
|
||||
});
|
||||
|
||||
it('basic hx-vals works', function () {
|
||||
this.server.respondWith("POST", "/vars", function (xhr) {
|
||||
var params = getParameters(xhr);
|
||||
params['i1'].should.equal("test");
|
||||
xhr.respond(200, {}, "Clicked!")
|
||||
});
|
||||
var div = make("<div hx-post='/vars' hx-vals='\"i1\":\"test\"'></div>")
|
||||
div.click();
|
||||
this.server.respond();
|
||||
div.innerHTML.should.equal("Clicked!");
|
||||
});
|
||||
|
||||
it('multiple hx-vals works', function () {
|
||||
this.server.respondWith("POST", "/vars", function (xhr) {
|
||||
var params = getParameters(xhr);
|
||||
params['v1'].should.equal("test");
|
||||
params['v2'].should.equal("42");
|
||||
xhr.respond(200, {}, "Clicked!")
|
||||
});
|
||||
var div = make("<div hx-post='/vars' hx-vals='\"v1\":\"test\", \"v2\":42'></div>")
|
||||
div.click();
|
||||
this.server.respond();
|
||||
div.innerHTML.should.equal("Clicked!");
|
||||
});
|
||||
|
||||
it('hx-vals can be on parents', function () {
|
||||
this.server.respondWith("POST", "/vars", function (xhr) {
|
||||
var params = getParameters(xhr);
|
||||
params['i1'].should.equal("test");
|
||||
xhr.respond(200, {}, "Clicked!")
|
||||
});
|
||||
make("<div hx-vals='\"i1\":\"test\"'><div id='d1' hx-post='/vars'></div></div>");
|
||||
var div = byId("d1");
|
||||
div.click();
|
||||
this.server.respond();
|
||||
div.innerHTML.should.equal("Clicked!");
|
||||
});
|
||||
|
||||
it('hx-vals can override parents', function () {
|
||||
this.server.respondWith("POST", "/vars", function (xhr) {
|
||||
var params = getParameters(xhr);
|
||||
params['i1'].should.equal("best");
|
||||
xhr.respond(200, {}, "Clicked!")
|
||||
});
|
||||
make("<div hx-vals='\"i1\":\"test\"'><div id='d1' hx-vals='\"i1\":\"best\"' hx-post='/vars'></div></div>");
|
||||
var div = byId("d1");
|
||||
div.click();
|
||||
this.server.respond();
|
||||
div.innerHTML.should.equal("Clicked!");
|
||||
});
|
||||
|
||||
it('hx-vals overrides inputs', function () {
|
||||
this.server.respondWith("POST", "/include", function (xhr) {
|
||||
var params = getParameters(xhr);
|
||||
params['i1'].should.equal("best");
|
||||
xhr.respond(200, {}, "Clicked!")
|
||||
});
|
||||
var div = make("<div hx-target='this'><input hx-post='/include' hx-vals='\"i1\":\"best\"' hx-trigger='click' id='i1' name='i1' value='test'/></div>")
|
||||
var input = byId("i1")
|
||||
input.click();
|
||||
this.server.respond();
|
||||
div.innerHTML.should.equal("Clicked!");
|
||||
});
|
||||
|
||||
it('hx-vals overrides hx-vars', function () {
|
||||
this.server.respondWith("POST", "/vars", function (xhr) {
|
||||
var params = getParameters(xhr);
|
||||
params['i1'].should.equal("test");
|
||||
xhr.respond(200, {}, "Clicked!")
|
||||
});
|
||||
var div = make("<div hx-post='/vars' hx-vals='\"i1\":\"test\"' hx-vars='\"i1\":\"best\"'></div>")
|
||||
div.click();
|
||||
this.server.respond();
|
||||
div.innerHTML.should.equal("Clicked!");
|
||||
});
|
||||
|
||||
});
|
@ -59,7 +59,7 @@ describe("hx-vars attribute", function() {
|
||||
div.innerHTML.should.equal("Clicked!");
|
||||
});
|
||||
|
||||
it('hx-vars override inputs', function () {
|
||||
it('hx-vars overrides inputs', function () {
|
||||
this.server.respondWith("POST", "/include", function (xhr) {
|
||||
var params = getParameters(xhr);
|
||||
params['i1'].should.equal("best");
|
||||
|
@ -97,6 +97,7 @@
|
||||
<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-vals.js"></script>
|
||||
<script src="attributes/hx-vars.js"></script>
|
||||
<script src="attributes/hx-ws.js"></script>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user