mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-22 16:18:08 +02:00
AcroForm: Add support for ResetForm action
- it aims to fix #12721. - Thanks to PR #14023, we've now the fieldObjects in the annotation layer so we can easily map fields names on their id if needed. - Reset values in the storage, in the JS sandbox and in the visible html elements.
This commit is contained in:
parent
db7c91e7b1
commit
aecbd7cd89
7 changed files with 302 additions and 15 deletions
|
@ -1328,6 +1328,20 @@ class Catalog {
|
|||
const actionName = actionType.name;
|
||||
|
||||
switch (actionName) {
|
||||
case "ResetForm":
|
||||
const flags = action.get("Flags");
|
||||
const include = ((isNum(flags) ? flags : 0) & 1) === 0;
|
||||
const fields = [];
|
||||
const refs = [];
|
||||
for (const obj of action.get("Fields") || []) {
|
||||
if (isRef(obj)) {
|
||||
refs.push(obj.toString());
|
||||
} else if (isString(obj)) {
|
||||
fields.push(stringToPDFString(obj));
|
||||
}
|
||||
}
|
||||
resultObj.resetForm = { fields, refs, include };
|
||||
break;
|
||||
case "URI":
|
||||
url = action.get("URI");
|
||||
if (url instanceof Name) {
|
||||
|
@ -1405,11 +1419,7 @@ class Catalog {
|
|||
}
|
||||
/* falls through */
|
||||
default:
|
||||
if (
|
||||
actionName === "JavaScript" ||
|
||||
actionName === "ResetForm" ||
|
||||
actionName === "SubmitForm"
|
||||
) {
|
||||
if (actionName === "JavaScript" || actionName === "SubmitForm") {
|
||||
// Don't bother the user with a warning for actions that require
|
||||
// scripting support, since those will be handled separately.
|
||||
break;
|
||||
|
|
|
@ -429,6 +429,7 @@ class LinkAnnotationElement extends AnnotationElement {
|
|||
parameters.data.dest ||
|
||||
parameters.data.action ||
|
||||
parameters.data.isTooltipOnly ||
|
||||
parameters.data.resetForm ||
|
||||
(parameters.data.actions &&
|
||||
(parameters.data.actions.Action ||
|
||||
parameters.data.actions["Mouse Up"] ||
|
||||
|
@ -454,17 +455,25 @@ class LinkAnnotationElement extends AnnotationElement {
|
|||
this._bindNamedAction(link, data.action);
|
||||
} else if (data.dest) {
|
||||
this._bindLink(link, data.dest);
|
||||
} else if (
|
||||
data.actions &&
|
||||
(data.actions.Action ||
|
||||
data.actions["Mouse Up"] ||
|
||||
data.actions["Mouse Down"]) &&
|
||||
this.enableScripting &&
|
||||
this.hasJSActions
|
||||
) {
|
||||
this._bindJSAction(link, data);
|
||||
} else {
|
||||
this._bindLink(link, "");
|
||||
let hasClickAction = false;
|
||||
if (
|
||||
data.actions &&
|
||||
(data.actions.Action ||
|
||||
data.actions["Mouse Up"] ||
|
||||
data.actions["Mouse Down"]) &&
|
||||
this.enableScripting &&
|
||||
this.hasJSActions
|
||||
) {
|
||||
hasClickAction = true;
|
||||
this._bindJSAction(link, data);
|
||||
}
|
||||
|
||||
if (data.resetForm) {
|
||||
this._bindResetFormAction(link, data.resetForm);
|
||||
} else if (!hasClickAction) {
|
||||
this._bindLink(link, "");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.quadrilaterals) {
|
||||
|
@ -557,6 +566,106 @@ class LinkAnnotationElement extends AnnotationElement {
|
|||
}
|
||||
link.className = "internalLink";
|
||||
}
|
||||
|
||||
_bindResetFormAction(link, resetForm) {
|
||||
const otherClickAction = link.onclick;
|
||||
if (!otherClickAction) {
|
||||
link.href = this.linkService.getAnchorUrl("");
|
||||
}
|
||||
link.className = "internalLink";
|
||||
|
||||
if (!this._fieldObjects) {
|
||||
warn(
|
||||
`_bindResetFormAction - "resetForm" action not supported, ` +
|
||||
"ensure that the `fieldObjects` parameter is provided."
|
||||
);
|
||||
if (!otherClickAction) {
|
||||
link.onclick = () => false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
link.onclick = () => {
|
||||
if (otherClickAction) {
|
||||
otherClickAction();
|
||||
}
|
||||
|
||||
const {
|
||||
fields: resetFormFields,
|
||||
refs: resetFormRefs,
|
||||
include,
|
||||
} = resetForm;
|
||||
|
||||
const allFields = [];
|
||||
if (resetFormFields.length !== 0 || resetFormRefs.length !== 0) {
|
||||
const fieldIds = new Set(resetFormRefs);
|
||||
for (const fieldName of resetFormFields) {
|
||||
const fields = this._fieldObjects[fieldName] || [];
|
||||
for (const { id } of fields) {
|
||||
fieldIds.add(id);
|
||||
}
|
||||
}
|
||||
for (const fields of Object.values(this._fieldObjects)) {
|
||||
for (const field of fields) {
|
||||
if (fieldIds.has(field.id) === include) {
|
||||
allFields.push(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const fields of Object.values(this._fieldObjects)) {
|
||||
allFields.push(...fields);
|
||||
}
|
||||
}
|
||||
|
||||
const storage = this.annotationStorage;
|
||||
const allIds = [];
|
||||
for (const field of allFields) {
|
||||
const { id } = field;
|
||||
allIds.push(id);
|
||||
switch (field.type) {
|
||||
case "text": {
|
||||
const value = field.defaultValue || "";
|
||||
storage.setValue(id, { value, valueAsString: value });
|
||||
break;
|
||||
}
|
||||
case "checkbox":
|
||||
case "radiobutton": {
|
||||
const value = field.defaultValue === field.exportValues;
|
||||
storage.setValue(id, { value });
|
||||
break;
|
||||
}
|
||||
case "combobox":
|
||||
case "listbox": {
|
||||
const value = field.defaultValue || "";
|
||||
storage.setValue(id, { value });
|
||||
break;
|
||||
}
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
const domElement = document.getElementById(id);
|
||||
if (!domElement || !GetElementsByNameSet.has(domElement)) {
|
||||
continue;
|
||||
}
|
||||
domElement.dispatchEvent(new Event("resetform"));
|
||||
}
|
||||
|
||||
if (this.enableScripting) {
|
||||
// Update the values in the sandbox.
|
||||
this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
|
||||
source: this,
|
||||
detail: {
|
||||
id: "app",
|
||||
ids: allIds,
|
||||
name: "ResetForm",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class TextAnnotationElement extends AnnotationElement {
|
||||
|
@ -804,6 +913,12 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
|
|||
);
|
||||
});
|
||||
|
||||
element.addEventListener("resetform", event => {
|
||||
const defaultValue = this.data.defaultFieldValue || "";
|
||||
element.value = elementData.userValue = defaultValue;
|
||||
delete elementData.formattedValue;
|
||||
});
|
||||
|
||||
let blurListener = event => {
|
||||
if (elementData.formattedValue) {
|
||||
event.target.value = elementData.formattedValue;
|
||||
|
@ -1057,6 +1172,11 @@ class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement {
|
|||
storage.setValue(id, { value: checked });
|
||||
});
|
||||
|
||||
element.addEventListener("resetform", event => {
|
||||
const defaultValue = data.defaultFieldValue || "Off";
|
||||
event.target.checked = defaultValue === data.exportValue;
|
||||
});
|
||||
|
||||
if (this.enableScripting && this.hasJSActions) {
|
||||
element.addEventListener("updatefromsandbox", jsEvent => {
|
||||
const actions = {
|
||||
|
@ -1129,6 +1249,14 @@ class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement {
|
|||
storage.setValue(id, { value: checked });
|
||||
});
|
||||
|
||||
element.addEventListener("resetform", event => {
|
||||
const defaultValue = data.defaultFieldValue;
|
||||
event.target.checked =
|
||||
defaultValue !== null &&
|
||||
defaultValue !== undefined &&
|
||||
defaultValue === data.buttonValue;
|
||||
});
|
||||
|
||||
if (this.enableScripting && this.hasJSActions) {
|
||||
const pdfButtonValue = data.buttonValue;
|
||||
element.addEventListener("updatefromsandbox", jsEvent => {
|
||||
|
@ -1231,6 +1359,13 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
|
|||
}
|
||||
}
|
||||
|
||||
selectElement.addEventListener("resetform", event => {
|
||||
const defaultValue = this.data.defaultFieldValue;
|
||||
for (const option of selectElement.options) {
|
||||
option.selected = option.value === defaultValue;
|
||||
}
|
||||
});
|
||||
|
||||
// Insert the options into the choice field.
|
||||
for (const option of this.data.options) {
|
||||
const optionElement = document.createElement("option");
|
||||
|
|
|
@ -79,6 +79,13 @@ class EventDispatcher {
|
|||
baseEvent.actions,
|
||||
baseEvent.pageNumber
|
||||
);
|
||||
} else if (id === "app" && baseEvent.name === "ResetForm") {
|
||||
for (const fieldId of baseEvent.ids) {
|
||||
const obj = this._objects[fieldId];
|
||||
if (obj) {
|
||||
obj.obj._reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -476,6 +476,10 @@ class Field extends PDFObject {
|
|||
return false;
|
||||
}
|
||||
|
||||
_reset() {
|
||||
this.value = this.valueAsString = this.defaultValue;
|
||||
}
|
||||
|
||||
_runActions(event) {
|
||||
const eventName = event.name;
|
||||
if (!this._actions.has(eventName)) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue