| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700 |
- /* Copyright 2020 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- import { createActionsMap, FieldType, getFieldType } from "./common.js";
- import { Color } from "./color.js";
- import { PDFObject } from "./pdf_object.js";
- class Field extends PDFObject {
- constructor(data) {
- super(data);
- this.alignment = data.alignment || "left";
- this.borderStyle = data.borderStyle || "";
- this.buttonAlignX = data.buttonAlignX || 50;
- this.buttonAlignY = data.buttonAlignY || 50;
- this.buttonFitBounds = data.buttonFitBounds;
- this.buttonPosition = data.buttonPosition;
- this.buttonScaleHow = data.buttonScaleHow;
- this.ButtonScaleWhen = data.buttonScaleWhen;
- this.calcOrderIndex = data.calcOrderIndex;
- this.comb = data.comb;
- this.commitOnSelChange = data.commitOnSelChange;
- this.currentValueIndices = data.currentValueIndices;
- this.defaultStyle = data.defaultStyle;
- this.defaultValue = data.defaultValue;
- this.doNotScroll = data.doNotScroll;
- this.doNotSpellCheck = data.doNotSpellCheck;
- this.delay = data.delay;
- this.display = data.display;
- this.doc = data.doc.wrapped;
- this.editable = data.editable;
- this.exportValues = data.exportValues;
- this.fileSelect = data.fileSelect;
- this.hidden = data.hidden;
- this.highlight = data.highlight;
- this.lineWidth = data.lineWidth;
- this.multiline = data.multiline;
- this.multipleSelection = !!data.multipleSelection;
- this.name = data.name;
- this.password = data.password;
- this.print = data.print;
- this.radiosInUnison = data.radiosInUnison;
- this.readonly = data.readonly;
- this.rect = data.rect;
- this.required = data.required;
- this.richText = data.richText;
- this.richValue = data.richValue;
- this.style = data.style;
- this.submitName = data.submitName;
- this.textFont = data.textFont;
- this.textSize = data.textSize;
- this.type = data.type;
- this.userName = data.userName;
- // Private
- this._actions = createActionsMap(data.actions);
- this._browseForFileToSubmit = data.browseForFileToSubmit || null;
- this._buttonCaption = null;
- this._buttonIcon = null;
- this._charLimit = data.charLimit;
- this._children = null;
- this._currentValueIndices = data.currentValueIndices || 0;
- this._document = data.doc;
- this._fieldPath = data.fieldPath;
- this._fillColor = data.fillColor || ["T"];
- this._isChoice = Array.isArray(data.items);
- this._items = data.items || [];
- this._hasValue = data.hasOwnProperty("value");
- this._page = data.page || 0;
- this._strokeColor = data.strokeColor || ["G", 0];
- this._textColor = data.textColor || ["G", 0];
- this._value = null;
- this._kidIds = data.kidIds || null;
- this._fieldType = getFieldType(this._actions);
- this._siblings = data.siblings || null;
- this._rotation = data.rotation || 0;
- this._globalEval = data.globalEval;
- this._appObjects = data.appObjects;
- // The value is set depending on the field type.
- this.value = data.value || "";
- }
- get currentValueIndices() {
- if (!this._isChoice) {
- return 0;
- }
- return this._currentValueIndices;
- }
- set currentValueIndices(indices) {
- if (!this._isChoice) {
- return;
- }
- if (!Array.isArray(indices)) {
- indices = [indices];
- }
- if (
- !indices.every(
- i =>
- typeof i === "number" &&
- Number.isInteger(i) &&
- i >= 0 &&
- i < this.numItems
- )
- ) {
- return;
- }
- indices.sort();
- if (this.multipleSelection) {
- this._currentValueIndices = indices;
- this._value = [];
- indices.forEach(i => {
- this._value.push(this._items[i].displayValue);
- });
- } else {
- if (indices.length > 0) {
- indices = indices.splice(1, indices.length - 1);
- this._currentValueIndices = indices[0];
- this._value = this._items[this._currentValueIndices];
- }
- }
- this._send({ id: this._id, indices });
- }
- get fillColor() {
- return this._fillColor;
- }
- set fillColor(color) {
- if (Color._isValidColor(color)) {
- this._fillColor = color;
- }
- }
- get bgColor() {
- return this.fillColor;
- }
- set bgColor(color) {
- this.fillColor = color;
- }
- get charLimit() {
- return this._charLimit;
- }
- set charLimit(limit) {
- if (typeof limit !== "number") {
- throw new Error("Invalid argument value");
- }
- this._charLimit = Math.max(0, Math.floor(limit));
- }
- get numItems() {
- if (!this._isChoice) {
- throw new Error("Not a choice widget");
- }
- return this._items.length;
- }
- set numItems(_) {
- throw new Error("field.numItems is read-only");
- }
- get strokeColor() {
- return this._strokeColor;
- }
- set strokeColor(color) {
- if (Color._isValidColor(color)) {
- this._strokeColor = color;
- }
- }
- get borderColor() {
- return this.strokeColor;
- }
- set borderColor(color) {
- this.strokeColor = color;
- }
- get page() {
- return this._page;
- }
- set page(_) {
- throw new Error("field.page is read-only");
- }
- get rotation() {
- return this._rotation;
- }
- set rotation(angle) {
- angle = Math.floor(angle);
- if (angle % 90 !== 0) {
- throw new Error("Invalid rotation: must be a multiple of 90");
- }
- angle %= 360;
- if (angle < 0) {
- angle += 360;
- }
- this._rotation = angle;
- }
- get textColor() {
- return this._textColor;
- }
- set textColor(color) {
- if (Color._isValidColor(color)) {
- this._textColor = color;
- }
- }
- get fgColor() {
- return this.textColor;
- }
- set fgColor(color) {
- this.textColor = color;
- }
- get value() {
- return this._value;
- }
- set value(value) {
- if (this._isChoice) {
- this._setChoiceValue(value);
- return;
- }
- if (value === "") {
- this._value = "";
- } else if (typeof value === "string") {
- switch (this._fieldType) {
- case FieldType.none:
- this._value = !isNaN(value) ? parseFloat(value) : value;
- break;
- case FieldType.number:
- case FieldType.percent:
- const number = parseFloat(value);
- this._value = !isNaN(number) ? number : 0;
- break;
- default:
- this._value = value;
- }
- } else {
- this._value = value;
- }
- }
- _setChoiceValue(value) {
- if (this.multipleSelection) {
- if (!Array.isArray(value)) {
- value = [value];
- }
- const values = new Set(value);
- if (Array.isArray(this._currentValueIndices)) {
- this._currentValueIndices.length = 0;
- this._value.length = 0;
- } else {
- this._currentValueIndices = [];
- this._value = [];
- }
- this._items.forEach((item, i) => {
- if (values.has(item.exportValue)) {
- this._currentValueIndices.push(i);
- this._value.push(item.exportValue);
- }
- });
- } else {
- if (Array.isArray(value)) {
- value = value[0];
- }
- const index = this._items.findIndex(
- ({ exportValue }) => value === exportValue
- );
- if (index !== -1) {
- this._currentValueIndices = index;
- this._value = this._items[index].exportValue;
- }
- }
- }
- get valueAsString() {
- return (this._value ?? "").toString();
- }
- set valueAsString(_) {
- // Do nothing.
- }
- browseForFileToSubmit() {
- if (this._browseForFileToSubmit) {
- // TODO: implement this function on Firefox side
- // we can use nsIFilePicker but open method is async.
- // Maybe it's possible to use a html input (type=file) too.
- this._browseForFileToSubmit();
- }
- }
- buttonGetCaption(nFace = 0) {
- if (this._buttonCaption) {
- return this._buttonCaption[nFace];
- }
- return "";
- }
- buttonGetIcon(nFace = 0) {
- if (this._buttonIcon) {
- return this._buttonIcon[nFace];
- }
- return null;
- }
- buttonImportIcon(cPath = null, nPave = 0) {
- /* Not implemented */
- }
- buttonSetCaption(cCaption, nFace = 0) {
- if (!this._buttonCaption) {
- this._buttonCaption = ["", "", ""];
- }
- this._buttonCaption[nFace] = cCaption;
- // TODO: send to the annotation layer
- // Right now the button is drawn on the canvas using its appearance so
- // update the caption means redraw...
- // We should probably have an html button for this annotation.
- }
- buttonSetIcon(oIcon, nFace = 0) {
- if (!this._buttonIcon) {
- this._buttonIcon = [null, null, null];
- }
- this._buttonIcon[nFace] = oIcon;
- }
- checkThisBox(nWidget, bCheckIt = true) {}
- clearItems() {
- if (!this._isChoice) {
- throw new Error("Not a choice widget");
- }
- this._items = [];
- this._send({ id: this._id, clear: null });
- }
- deleteItemAt(nIdx = null) {
- if (!this._isChoice) {
- throw new Error("Not a choice widget");
- }
- if (!this.numItems) {
- return;
- }
- if (nIdx === null) {
- // Current selected item.
- nIdx = Array.isArray(this._currentValueIndices)
- ? this._currentValueIndices[0]
- : this._currentValueIndices;
- nIdx = nIdx || 0;
- }
- if (nIdx < 0 || nIdx >= this.numItems) {
- nIdx = this.numItems - 1;
- }
- this._items.splice(nIdx, 1);
- if (Array.isArray(this._currentValueIndices)) {
- let index = this._currentValueIndices.findIndex(i => i >= nIdx);
- if (index !== -1) {
- if (this._currentValueIndices[index] === nIdx) {
- this._currentValueIndices.splice(index, 1);
- }
- for (const ii = this._currentValueIndices.length; index < ii; index++) {
- --this._currentValueIndices[index];
- }
- }
- } else {
- if (this._currentValueIndices === nIdx) {
- this._currentValueIndices = this.numItems > 0 ? 0 : -1;
- } else if (this._currentValueIndices > nIdx) {
- --this._currentValueIndices;
- }
- }
- this._send({ id: this._id, remove: nIdx });
- }
- getItemAt(nIdx = -1, bExportValue = false) {
- if (!this._isChoice) {
- throw new Error("Not a choice widget");
- }
- if (nIdx < 0 || nIdx >= this.numItems) {
- nIdx = this.numItems - 1;
- }
- const item = this._items[nIdx];
- return bExportValue ? item.exportValue : item.displayValue;
- }
- getArray() {
- // Gets the array of terminal child fields (that is, fields that can have
- // a value for this Field object, the parent field).
- if (this._kidIds) {
- const array = [];
- const fillArrayWithKids = kidIds => {
- for (const id of kidIds) {
- const obj = this._appObjects[id];
- if (!obj) {
- continue;
- }
- if (obj.obj._hasValue) {
- array.push(obj.wrapped);
- }
- if (obj.obj._kidIds) {
- fillArrayWithKids(obj.obj._kidIds);
- }
- }
- };
- fillArrayWithKids(this._kidIds);
- return array;
- }
- if (this._children === null) {
- this._children = this._document.obj._getTerminalChildren(this._fieldPath);
- }
- return this._children;
- }
- getLock() {
- return undefined;
- }
- isBoxChecked(nWidget) {
- return false;
- }
- isDefaultChecked(nWidget) {
- return false;
- }
- insertItemAt(cName, cExport = undefined, nIdx = 0) {
- if (!this._isChoice) {
- throw new Error("Not a choice widget");
- }
- if (!cName) {
- return;
- }
- if (nIdx < 0 || nIdx > this.numItems) {
- nIdx = this.numItems;
- }
- if (this._items.some(({ displayValue }) => displayValue === cName)) {
- return;
- }
- if (cExport === undefined) {
- cExport = cName;
- }
- const data = { displayValue: cName, exportValue: cExport };
- this._items.splice(nIdx, 0, data);
- if (Array.isArray(this._currentValueIndices)) {
- let index = this._currentValueIndices.findIndex(i => i >= nIdx);
- if (index !== -1) {
- for (const ii = this._currentValueIndices.length; index < ii; index++) {
- ++this._currentValueIndices[index];
- }
- }
- } else if (this._currentValueIndices >= nIdx) {
- ++this._currentValueIndices;
- }
- this._send({ id: this._id, insert: { index: nIdx, ...data } });
- }
- setAction(cTrigger, cScript) {
- if (typeof cTrigger !== "string" || typeof cScript !== "string") {
- return;
- }
- if (!(cTrigger in this._actions)) {
- this._actions[cTrigger] = [];
- }
- this._actions[cTrigger].push(cScript);
- }
- setFocus() {
- this._send({ id: this._id, focus: true });
- }
- setItems(oArray) {
- if (!this._isChoice) {
- throw new Error("Not a choice widget");
- }
- this._items.length = 0;
- for (const element of oArray) {
- let displayValue, exportValue;
- if (Array.isArray(element)) {
- displayValue = element[0]?.toString() || "";
- exportValue = element[1]?.toString() || "";
- } else {
- displayValue = exportValue = element?.toString() || "";
- }
- this._items.push({ displayValue, exportValue });
- }
- this._currentValueIndices = 0;
- this._send({ id: this._id, items: this._items });
- }
- setLock() {}
- signatureGetModifications() {}
- signatureGetSeedValue() {}
- signatureInfo() {}
- signatureSetSeedValue() {}
- signatureSign() {}
- signatureValidate() {}
- _isButton() {
- return false;
- }
- _reset() {
- this.value = this.defaultValue;
- }
- _runActions(event) {
- const eventName = event.name;
- if (!this._actions.has(eventName)) {
- return false;
- }
- const actions = this._actions.get(eventName);
- try {
- for (const action of actions) {
- // Action evaluation must happen in the global scope
- this._globalEval(action);
- }
- } catch (error) {
- event.rc = false;
- throw error;
- }
- return true;
- }
- }
- class RadioButtonField extends Field {
- constructor(otherButtons, data) {
- super(data);
- this.exportValues = [this.exportValues];
- this._radioIds = [this._id];
- this._radioActions = [this._actions];
- for (const radioData of otherButtons) {
- this.exportValues.push(radioData.exportValues);
- this._radioIds.push(radioData.id);
- this._radioActions.push(createActionsMap(radioData.actions));
- if (this._value === radioData.exportValues) {
- this._id = radioData.id;
- }
- }
- this._hasBeenInitialized = true;
- this._value = data.value || "";
- }
- get value() {
- return this._value;
- }
- set value(value) {
- if (!this._hasBeenInitialized) {
- return;
- }
- if (value === null || value === undefined) {
- this._value = "";
- }
- const i = this.exportValues.indexOf(value);
- if (0 <= i && i < this._radioIds.length) {
- this._id = this._radioIds[i];
- this._value = value;
- } else if (value === "Off" && this._radioIds.length === 2) {
- const nextI = (1 + this._radioIds.indexOf(this._id)) % 2;
- this._id = this._radioIds[nextI];
- this._value = this.exportValues[nextI];
- }
- }
- checkThisBox(nWidget, bCheckIt = true) {
- if (nWidget < 0 || nWidget >= this._radioIds.length || !bCheckIt) {
- return;
- }
- this._id = this._radioIds[nWidget];
- this._value = this.exportValues[nWidget];
- this._send({ id: this._id, value: this._value });
- }
- isBoxChecked(nWidget) {
- return (
- nWidget >= 0 &&
- nWidget < this._radioIds.length &&
- this._id === this._radioIds[nWidget]
- );
- }
- isDefaultChecked(nWidget) {
- return (
- nWidget >= 0 &&
- nWidget < this.exportValues.length &&
- this.defaultValue === this.exportValues[nWidget]
- );
- }
- _getExportValue(state) {
- const i = this._radioIds.indexOf(this._id);
- return this.exportValues[i];
- }
- _runActions(event) {
- const i = this._radioIds.indexOf(this._id);
- this._actions = this._radioActions[i];
- return super._runActions(event);
- }
- _isButton() {
- return true;
- }
- }
- class CheckboxField extends RadioButtonField {
- get value() {
- return this._value;
- }
- set value(value) {
- if (!value || value === "Off") {
- this._value = "Off";
- } else {
- super.value = value;
- }
- }
- _getExportValue(state) {
- return state ? super._getExportValue(state) : "Off";
- }
- isBoxChecked(nWidget) {
- if (this._value === "Off") {
- return false;
- }
- return super.isBoxChecked(nWidget);
- }
- isDefaultChecked(nWidget) {
- if (this.defaultValue === "Off") {
- return this._value === "Off";
- }
- return super.isDefaultChecked(nWidget);
- }
- checkThisBox(nWidget, bCheckIt = true) {
- if (nWidget < 0 || nWidget >= this._radioIds.length) {
- return;
- }
- this._id = this._radioIds[nWidget];
- this._value = bCheckIt ? this.exportValues[nWidget] : "Off";
- this._send({ id: this._id, value: this._value });
- }
- }
- export { CheckboxField, Field, RadioButtonField };
|