pdfjs-dist
Version:
Generic build of Mozilla's PDF.js library.
450 lines (449 loc) • 15.6 kB
JavaScript
/**
* @licstart The following is the entire license notice for the
* JavaScript code in this page
*
* Copyright 2022 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.
*
* @licend The above is the entire license notice for the
* JavaScript code in this page
*/
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Binder = void 0;
var _xfa_object = require("./xfa_object.js");
var _template = require("./template.js");
var _som = require("./som.js");
var _namespaces = require("./namespaces.js");
var _util = require("../../shared/util.js");
const NS_DATASETS = _namespaces.NamespaceIds.datasets.id;
function createText(content) {
const node = new _template.Text({});
node[_xfa_object.$content] = content;
return node;
}
class Binder {
constructor(root) {
this.root = root;
this.datasets = root.datasets;
if (root.datasets && root.datasets.data) {
this.data = root.datasets.data;
} else {
this.data = new _xfa_object.XmlObject(_namespaces.NamespaceIds.datasets.id, "data");
}
this.emptyMerge = this.data[_xfa_object.$getChildren]().length === 0;
this.root.form = this.form = root.template[_xfa_object.$clone]();
}
_isConsumeData() {
return !this.emptyMerge && this._mergeMode;
}
_isMatchTemplate() {
return !this._isConsumeData();
}
bind() {
this._bindElement(this.form, this.data);
return this.form;
}
getData() {
return this.data;
}
_bindValue(formNode, data, picture) {
formNode[_xfa_object.$data] = data;
if (formNode[_xfa_object.$hasSettableValue]()) {
if (data[_xfa_object.$isDataValue]()) {
const value = data[_xfa_object.$getDataValue]();
formNode[_xfa_object.$setValue](createText(value));
} else if (formNode instanceof _template.Field && formNode.ui && formNode.ui.choiceList && formNode.ui.choiceList.open === "multiSelect") {
const value = data[_xfa_object.$getChildren]().map(child => child[_xfa_object.$content].trim()).join("\n");
formNode[_xfa_object.$setValue](createText(value));
} else if (this._isConsumeData()) {
(0, _util.warn)(`XFA - Nodes haven't the same type.`);
}
} else if (!data[_xfa_object.$isDataValue]() || this._isMatchTemplate()) {
this._bindElement(formNode, data);
} else {
(0, _util.warn)(`XFA - Nodes haven't the same type.`);
}
}
_findDataByNameToConsume(name, isValue, dataNode, global) {
if (!name) {
return null;
}
let generator, match;
for (let i = 0; i < 3; i++) {
generator = dataNode[_xfa_object.$getRealChildrenByNameIt](name, false, true);
while (true) {
match = generator.next().value;
if (!match) {
break;
}
if (isValue === match[_xfa_object.$isDataValue]()) {
return match;
}
}
if (dataNode[_xfa_object.$namespaceId] === _namespaces.NamespaceIds.datasets.id && dataNode[_xfa_object.$nodeName] === "data") {
break;
}
dataNode = dataNode[_xfa_object.$getParent]();
}
if (!global) {
return null;
}
generator = this.data[_xfa_object.$getRealChildrenByNameIt](name, true, false);
match = generator.next().value;
if (match) {
return match;
}
generator = this.data[_xfa_object.$getAttributeIt](name, true);
match = generator.next().value;
if (match && match[_xfa_object.$isDataValue]()) {
return match;
}
return null;
}
_setProperties(formNode, dataNode) {
if (!formNode.hasOwnProperty("setProperty")) {
return;
}
for (const {
ref,
target,
connection
} of formNode.setProperty.children) {
if (connection) {
continue;
}
if (!ref) {
continue;
}
const nodes = (0, _som.searchNode)(this.root, dataNode, ref, false, false);
if (!nodes) {
(0, _util.warn)(`XFA - Invalid reference: ${ref}.`);
continue;
}
const [node] = nodes;
if (!node[_xfa_object.$isDescendent](this.data)) {
(0, _util.warn)(`XFA - Invalid node: must be a data node.`);
continue;
}
const targetNodes = (0, _som.searchNode)(this.root, formNode, target, false, false);
if (!targetNodes) {
(0, _util.warn)(`XFA - Invalid target: ${target}.`);
continue;
}
const [targetNode] = targetNodes;
if (!targetNode[_xfa_object.$isDescendent](formNode)) {
(0, _util.warn)(`XFA - Invalid target: must be a property or subproperty.`);
continue;
}
const targetParent = targetNode[_xfa_object.$getParent]();
if (targetNode instanceof _template.SetProperty || targetParent instanceof _template.SetProperty) {
(0, _util.warn)(`XFA - Invalid target: cannot be a setProperty or one of its properties.`);
continue;
}
if (targetNode instanceof _template.BindItems || targetParent instanceof _template.BindItems) {
(0, _util.warn)(`XFA - Invalid target: cannot be a bindItems or one of its properties.`);
continue;
}
const content = node[_xfa_object.$text]();
const name = targetNode[_xfa_object.$nodeName];
if (targetNode instanceof _xfa_object.XFAAttribute) {
const attrs = Object.create(null);
attrs[name] = content;
const obj = Reflect.construct(Object.getPrototypeOf(targetParent).constructor, [attrs]);
targetParent[name] = obj[name];
continue;
}
if (!targetNode.hasOwnProperty(_xfa_object.$content)) {
(0, _util.warn)(`XFA - Invalid node to use in setProperty`);
continue;
}
targetNode[_xfa_object.$data] = node;
targetNode[_xfa_object.$content] = content;
targetNode[_xfa_object.$finalize]();
}
}
_bindItems(formNode, dataNode) {
if (!formNode.hasOwnProperty("items") || !formNode.hasOwnProperty("bindItems") || formNode.bindItems.isEmpty()) {
return;
}
for (const item of formNode.items.children) {
formNode[_xfa_object.$removeChild](item);
}
formNode.items.clear();
const labels = new _template.Items({});
const values = new _template.Items({});
formNode[_xfa_object.$appendChild](labels);
formNode.items.push(labels);
formNode[_xfa_object.$appendChild](values);
formNode.items.push(values);
for (const {
ref,
labelRef,
valueRef,
connection
} of formNode.bindItems.children) {
if (connection) {
continue;
}
if (!ref) {
continue;
}
const nodes = (0, _som.searchNode)(this.root, dataNode, ref, false, false);
if (!nodes) {
(0, _util.warn)(`XFA - Invalid reference: ${ref}.`);
continue;
}
for (const node of nodes) {
if (!node[_xfa_object.$isDescendent](this.datasets)) {
(0, _util.warn)(`XFA - Invalid ref (${ref}): must be a datasets child.`);
continue;
}
const labelNodes = (0, _som.searchNode)(this.root, node, labelRef, true, false);
if (!labelNodes) {
(0, _util.warn)(`XFA - Invalid label: ${labelRef}.`);
continue;
}
const [labelNode] = labelNodes;
if (!labelNode[_xfa_object.$isDescendent](this.datasets)) {
(0, _util.warn)(`XFA - Invalid label: must be a datasets child.`);
continue;
}
const valueNodes = (0, _som.searchNode)(this.root, node, valueRef, true, false);
if (!valueNodes) {
(0, _util.warn)(`XFA - Invalid value: ${valueRef}.`);
continue;
}
const [valueNode] = valueNodes;
if (!valueNode[_xfa_object.$isDescendent](this.datasets)) {
(0, _util.warn)(`XFA - Invalid value: must be a datasets child.`);
continue;
}
const label = createText(labelNode[_xfa_object.$text]());
const value = createText(valueNode[_xfa_object.$text]());
labels[_xfa_object.$appendChild](label);
labels.text.push(label);
values[_xfa_object.$appendChild](value);
values.text.push(value);
}
}
}
_bindOccurrences(formNode, matches, picture) {
let baseClone;
if (matches.length > 1) {
baseClone = formNode[_xfa_object.$clone]();
baseClone[_xfa_object.$removeChild](baseClone.occur);
baseClone.occur = null;
}
this._bindValue(formNode, matches[0], picture);
this._setProperties(formNode, matches[0]);
this._bindItems(formNode, matches[0]);
if (matches.length === 1) {
return;
}
const parent = formNode[_xfa_object.$getParent]();
const name = formNode[_xfa_object.$nodeName];
const pos = parent[_xfa_object.$indexOf](formNode);
for (let i = 1, ii = matches.length; i < ii; i++) {
const match = matches[i];
const clone = baseClone[_xfa_object.$clone]();
parent[name].push(clone);
parent[_xfa_object.$insertAt](pos + i, clone);
this._bindValue(clone, match, picture);
this._setProperties(clone, match);
this._bindItems(clone, match);
}
}
_createOccurrences(formNode) {
if (!this.emptyMerge) {
return;
}
const {
occur
} = formNode;
if (!occur || occur.initial <= 1) {
return;
}
const parent = formNode[_xfa_object.$getParent]();
const name = formNode[_xfa_object.$nodeName];
if (!(parent[name] instanceof _xfa_object.XFAObjectArray)) {
return;
}
let currentNumber;
if (formNode.name) {
currentNumber = parent[name].children.filter(e => e.name === formNode.name).length;
} else {
currentNumber = parent[name].children.length;
}
const pos = parent[_xfa_object.$indexOf](formNode) + 1;
const ii = occur.initial - currentNumber;
if (ii) {
const nodeClone = formNode[_xfa_object.$clone]();
nodeClone[_xfa_object.$removeChild](nodeClone.occur);
nodeClone.occur = null;
parent[name].push(nodeClone);
parent[_xfa_object.$insertAt](pos, nodeClone);
for (let i = 1; i < ii; i++) {
const clone = nodeClone[_xfa_object.$clone]();
parent[name].push(clone);
parent[_xfa_object.$insertAt](pos + i, clone);
}
}
}
_getOccurInfo(formNode) {
const {
name,
occur
} = formNode;
if (!occur || !name) {
return [1, 1];
}
const max = occur.max === -1 ? Infinity : occur.max;
return [occur.min, max];
}
_setAndBind(formNode, dataNode) {
this._setProperties(formNode, dataNode);
this._bindItems(formNode, dataNode);
this._bindElement(formNode, dataNode);
}
_bindElement(formNode, dataNode) {
const uselessNodes = [];
this._createOccurrences(formNode);
for (const child of formNode[_xfa_object.$getChildren]()) {
if (child[_xfa_object.$data]) {
continue;
}
if (this._mergeMode === undefined && child[_xfa_object.$nodeName] === "subform") {
this._mergeMode = child.mergeMode === "consumeData";
const dataChildren = dataNode[_xfa_object.$getChildren]();
if (dataChildren.length > 0) {
this._bindOccurrences(child, [dataChildren[0]], null);
} else if (this.emptyMerge) {
const nsId = dataNode[_xfa_object.$namespaceId] === NS_DATASETS ? -1 : dataNode[_xfa_object.$namespaceId];
const dataChild = child[_xfa_object.$data] = new _xfa_object.XmlObject(nsId, child.name || "root");
dataNode[_xfa_object.$appendChild](dataChild);
this._bindElement(child, dataChild);
}
continue;
}
if (!child[_xfa_object.$isBindable]()) {
continue;
}
let global = false;
let picture = null;
let ref = null;
let match = null;
if (child.bind) {
switch (child.bind.match) {
case "none":
this._setAndBind(child, dataNode);
continue;
case "global":
global = true;
break;
case "dataRef":
if (!child.bind.ref) {
(0, _util.warn)(`XFA - ref is empty in node ${child[_xfa_object.$nodeName]}.`);
this._setAndBind(child, dataNode);
continue;
}
ref = child.bind.ref;
break;
default:
break;
}
if (child.bind.picture) {
picture = child.bind.picture[_xfa_object.$content];
}
}
const [min, max] = this._getOccurInfo(child);
if (ref) {
match = (0, _som.searchNode)(this.root, dataNode, ref, true, false);
if (match === null) {
match = (0, _som.createDataNode)(this.data, dataNode, ref);
if (!match) {
continue;
}
if (this._isConsumeData()) {
match[_xfa_object.$consumed] = true;
}
this._setAndBind(child, match);
continue;
} else {
if (this._isConsumeData()) {
match = match.filter(node => !node[_xfa_object.$consumed]);
}
if (match.length > max) {
match = match.slice(0, max);
} else if (match.length === 0) {
match = null;
}
if (match && this._isConsumeData()) {
match.forEach(node => {
node[_xfa_object.$consumed] = true;
});
}
}
} else {
if (!child.name) {
this._setAndBind(child, dataNode);
continue;
}
if (this._isConsumeData()) {
const matches = [];
while (matches.length < max) {
const found = this._findDataByNameToConsume(child.name, child[_xfa_object.$hasSettableValue](), dataNode, global);
if (!found) {
break;
}
found[_xfa_object.$consumed] = true;
matches.push(found);
}
match = matches.length > 0 ? matches : null;
} else {
match = dataNode[_xfa_object.$getRealChildrenByNameIt](child.name, false, this.emptyMerge).next().value;
if (!match) {
if (min === 0) {
uselessNodes.push(child);
continue;
}
const nsId = dataNode[_xfa_object.$namespaceId] === NS_DATASETS ? -1 : dataNode[_xfa_object.$namespaceId];
match = child[_xfa_object.$data] = new _xfa_object.XmlObject(nsId, child.name);
if (this.emptyMerge) {
match[_xfa_object.$consumed] = true;
}
dataNode[_xfa_object.$appendChild](match);
this._setAndBind(child, match);
continue;
}
if (this.emptyMerge) {
match[_xfa_object.$consumed] = true;
}
match = [match];
}
}
if (match) {
this._bindOccurrences(child, match, picture);
} else if (min > 0) {
this._setAndBind(child, dataNode);
} else {
uselessNodes.push(child);
}
}
uselessNodes.forEach(node => node[_xfa_object.$getParent]()[_xfa_object.$removeChild](node));
}
}
exports.Binder = Binder;