Skip to content

Commit ebdeaf4

Browse files
authored
Merge pull request #1175 from Patternslib/submit-button-fixes
Submit button fixes
2 parents 3119c8f + 6882f45 commit ebdeaf4

File tree

5 files changed

+122
-37
lines changed

5 files changed

+122
-37
lines changed

src/core/parser.js

+30-32
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,7 @@ class ArgumentParser {
3333
if (this.parameters[original]) {
3434
this.parameters[original].alias = alias;
3535
} else {
36-
throw (
37-
'Attempted to add an alias "' +
38-
alias +
39-
'" for a non-existing parser argument "' +
40-
original +
41-
'".'
42-
);
36+
throw `Attempted to add an alias "${alias}" for a non-existing parser argument "${original}".`;
4337
}
4438
}
4539

@@ -166,19 +160,19 @@ class ArgumentParser {
166160
} else if (typeof value === "number") {
167161
value = !!value;
168162
} else {
169-
throw "Cannot convert value for " + name + " to boolean";
163+
throw `Cannot convert value for ${name} to boolean.`;
170164
}
171165
break;
172166
case "number":
173167
if (typeof value === "string") {
174168
value = parseInt(value, 10);
175169
if (isNaN(value)) {
176-
throw "Cannot convert value for " + name + " to number";
170+
throw `Cannot convert value for ${name} to number.`;
177171
}
178172
} else if (typeof value === "boolean") {
179173
value = value + 0;
180174
} else {
181-
throw "Cannot convert value for " + name + " to number";
175+
throw `Cannot convert value for ${name} to number.`;
182176
}
183177
break;
184178
case "string":
@@ -188,28 +182,25 @@ class ArgumentParser {
188182
case "undefined":
189183
break;
190184
default:
191-
throw (
192-
"Do not know how to convert value for " +
193-
name +
194-
" to " +
185+
throw `Do not know how to convert value for ${name} of type ${typeof value} to ${
195186
spec.type
196-
);
187+
}.`;
197188
}
198189
} catch (e) {
199190
this.log.warn(e);
200191
return null;
201192
}
202193

203194
if (spec.choices && spec.choices.indexOf(value) === -1) {
204-
this.log.warn("Illegal value for " + name + ": " + value);
195+
this.log.warn(`Illegal value for ${name}: ${value}.`);
205196
return null;
206197
}
207198
return value;
208199
}
209200

210201
_set(opts, name, value) {
211202
if (!(name in this.parameters)) {
212-
this.log.debug("Ignoring value for unknown argument " + name);
203+
this.log.debug(`Ignoring value for unknown argument: ${name}.`);
213204
return;
214205
}
215206
const spec = this.parameters[name];
@@ -261,7 +252,7 @@ class ArgumentParser {
261252
}
262253
const matches = part.match(this.named_param_pattern);
263254
if (!matches) {
264-
this.log.warn("Invalid parameter: " + part + ": " + argstring);
255+
this.log.warn(`Invalid parameter: ${part}: ${argstring}.`);
265256
continue;
266257
}
267258
const name = matches[1];
@@ -280,7 +271,7 @@ class ArgumentParser {
280271
this._set(opts, name + "-" + field, subopt[field]);
281272
}
282273
} else {
283-
this.log.warn("Unknown named parameter " + matches[1]);
274+
this.log.warn(`Unknown named parameter: ${matches[1]}.`);
284275
continue;
285276
}
286277
}
@@ -320,7 +311,7 @@ class ArgumentParser {
320311
break;
321312
}
322313
}
323-
if (parts.length) this.log.warn("Ignore extra arguments: " + parts.join(" "));
314+
if (parts.length) this.log.warn(`Ignore extra arguments: ${parts.join(" ")}.`);
324315
return opts;
325316
}
326317

@@ -332,7 +323,7 @@ class ArgumentParser {
332323
try {
333324
return JSON.parse(parameter);
334325
} catch (e) {
335-
this.log.warn("Invalid JSON argument found: " + parameter);
326+
this.log.warn(`Invalid JSON argument found: ${parameter}.`);
336327
}
337328
}
338329
if (parameter.match(this.named_param_pattern)) {
@@ -352,41 +343,50 @@ class ArgumentParser {
352343

353344
_defaults($el) {
354345
const result = {};
355-
for (const name in this.parameters)
356-
if (typeof this.parameters[name].value === "function")
346+
for (const name in this.parameters) {
347+
if (typeof this.parameters[name].value === "function") {
357348
try {
358349
result[name] = this.parameters[name].value($el, name);
359350
this.parameters[name].type = typeof result[name];
360351
} catch (e) {
361-
this.log.error("Default function for " + name + " failed.");
352+
this.log.error(`Default function for ${name} failed.`);
362353
}
363-
else result[name] = this.parameters[name].value;
354+
} else {
355+
result[name] = this.parameters[name].value;
356+
}
357+
}
364358
return result;
365359
}
366360

367361
_cleanupOptions(options, group_options = true) {
368362
// Resolve references
369363
for (const name of Object.keys(options)) {
370364
const spec = this.parameters[name];
371-
if (spec === undefined) continue;
365+
if (spec === undefined) {
366+
continue;
367+
}
372368

373369
if (
374370
options[name] === spec.value &&
375371
typeof spec.value === "string" &&
376372
spec.value.slice(0, 1) === "$"
377-
)
373+
) {
378374
options[name] = options[spec.value.slice(1)];
375+
}
379376
}
380377
if (group_options) {
381378
// Move options into groups and do renames
382379
for (const name of Object.keys(options)) {
383380
const spec = this.parameters[name];
384381
let target;
385-
if (spec === undefined) continue;
382+
if (spec === undefined) {
383+
continue;
384+
}
386385

387386
if (spec.group) {
388-
if (typeof options[spec.group] !== "object")
387+
if (typeof options[spec.group] !== "object") {
389388
options[spec.group] = {};
389+
}
390390
target = options[spec.group];
391391
} else {
392392
target = options;
@@ -430,9 +430,7 @@ class ArgumentParser {
430430
) {
431431
$possible_config_providers = $el;
432432
} else {
433-
$possible_config_providers = $el
434-
.parents("[" + this.attribute + "]")
435-
.addBack();
433+
$possible_config_providers = $el.parents(`[${this.attribute}]`).addBack();
436434
}
437435

438436
for (const provider of $possible_config_providers) {

src/pat/ajax/ajax.js

+8-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,14 @@ const log = logging.getLogger("pat.ajax");
1414
export const parser = new Parser("ajax");
1515
parser.addArgument("accept", "text/html");
1616
parser.addArgument("url", function ($el) {
17-
return (
18-
$el.is("a") ? $el.attr("href") : $el.is("form") ? $el.attr("action") : ""
19-
).split("#")[0];
17+
const el = $el[0];
18+
const value =
19+
el.tagName === "A"
20+
? el.getAttribute("href")
21+
: el.tagName === "FORM"
22+
? el.getAttribute("action")
23+
: "";
24+
return (value || "").split("#")[0];
2025
});
2126
parser.addArgument("browser-cache", "no-cache", ["cache", "no-cache"]); // Cache ajax requests
2227

src/pat/ajax/ajax.test.js

+12
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,18 @@ describe("pat-ajax", function () {
131131
const ajaxargs = $.ajax.mock.calls[$.ajax.mock.calls.length - 1][0];
132132
expect(ajaxargs.url).toEqual("else.html");
133133
});
134+
it("Does not break with missing anchor-href", async function () {
135+
document.body.innerHTML = `<a class="pat-ajax"/>`;
136+
jest.spyOn(pattern.parser.log, "error");
137+
pattern.parser.parse(document.body.querySelector(".pat-ajax"));
138+
expect(pattern.parser.log.error).not.toHaveBeenCalled();
139+
});
140+
it("Does not break with missing form-action", async function () {
141+
document.body.innerHTML = `<form class="pat-ajax"/>`;
142+
jest.spyOn(pattern.parser.log, "error");
143+
pattern.parser.parse(document.body.querySelector(".pat-ajax"));
144+
expect(pattern.parser.log.error).not.toHaveBeenCalled();
145+
});
134146

135147
// Accept
136148
it("Default accept header", function () {

src/pat/inject/inject.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,17 @@ const inject = {
9797
// setup event handlers
9898
if ($el.is("form")) {
9999
$el.on("submit.pat-inject", this.onTrigger.bind(this))
100-
.on("click.pat-inject", "[type=submit]", ajax.onClickSubmit)
101100
.on(
102101
"click.pat-inject",
103-
"[type=submit][formaction], [type=image][formaction]",
102+
`[type=submit]:not([formaction]),
103+
button:not([formaction]):not([type=button])`,
104+
ajax.onClickSubmit
105+
)
106+
.on(
107+
"click.pat-inject",
108+
`[type=submit][formaction],
109+
[type=image][formaction],
110+
button[formaction]:not([type=button])`,
104111
this.onFormActionSubmit.bind(this)
105112
);
106113
} else if ($el.is(".pat-subform")) {

src/pat/inject/inject.test.js

+63
Original file line numberDiff line numberDiff line change
@@ -1221,6 +1221,69 @@ describe("pat-inject", function () {
12211221
"other"
12221222
);
12231223
});
1224+
1225+
it("9.2.5.10 - does not call ajax.onClickSubmit twice.", async function () {
1226+
document.body.innerHTML = `
1227+
<form class="pat-inject">
1228+
<button type="submit" formaction="test.cgi"/>
1229+
</form>
1230+
`;
1231+
1232+
const pat_ajax = (await import("../ajax/ajax.js")).default;
1233+
jest.spyOn(pat_ajax, "onClickSubmit");
1234+
1235+
const form = document.querySelector("form");
1236+
const button = form.querySelector("button");
1237+
1238+
pattern.init($(form));
1239+
button.click();
1240+
1241+
expect(pat_ajax.onClickSubmit).toHaveBeenCalledTimes(1);
1242+
});
1243+
});
1244+
1245+
describe("9.2.6 - Support submit buttons without type attribute ...", () => {
1246+
it("9.2.6.1 - ... without a formaction atttribute.", async function () {
1247+
document.body.innerHTML = `
1248+
<form class="pat-inject" action="test.cgi">
1249+
<button/>
1250+
</form>
1251+
`;
1252+
1253+
const pat_ajax = (await import("../ajax/ajax.js")).default;
1254+
jest.spyOn(pat_ajax, "onClickSubmit");
1255+
jest.spyOn(pattern, "onFormActionSubmit");
1256+
1257+
const form = document.querySelector("form");
1258+
const button = form.querySelector("button");
1259+
1260+
pattern.init($(form));
1261+
button.click();
1262+
1263+
expect(pat_ajax.onClickSubmit).toHaveBeenCalledTimes(1);
1264+
expect(pattern.onFormActionSubmit).toHaveBeenCalledTimes(0);
1265+
});
1266+
1267+
it("9.2.6.1 - ... with a formaction atttribute.", async function () {
1268+
document.body.innerHTML = `
1269+
<form class="pat-inject">
1270+
<button formaction="test.cgi"/>
1271+
</form>
1272+
`;
1273+
1274+
const pat_ajax = (await import("../ajax/ajax.js")).default;
1275+
jest.spyOn(pat_ajax, "onClickSubmit");
1276+
jest.spyOn(pattern, "onFormActionSubmit");
1277+
1278+
const form = document.querySelector("form");
1279+
const button = form.querySelector("button");
1280+
1281+
pattern.init($(form));
1282+
button.click();
1283+
1284+
expect(pat_ajax.onClickSubmit).toHaveBeenCalledTimes(1);
1285+
expect(pattern.onFormActionSubmit).toHaveBeenCalledTimes(1);
1286+
});
12241287
});
12251288
});
12261289
});

0 commit comments

Comments
 (0)