xfa_layer.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * Javascript code in this page
  4. *
  5. * Copyright 2021 Mozilla Foundation
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. *
  19. * @licend The above is the entire license notice for the
  20. * Javascript code in this page
  21. */
  22. "use strict";
  23. Object.defineProperty(exports, "__esModule", {
  24. value: true
  25. });
  26. exports.XfaLayer = void 0;
  27. var _util = require("../shared/util.js");
  28. var _xfa_text = require("./xfa_text.js");
  29. class XfaLayer {
  30. static setupStorage(html, id, element, storage, intent) {
  31. const storedData = storage.getValue(id, {
  32. value: null
  33. });
  34. switch (element.name) {
  35. case "textarea":
  36. if (storedData.value !== null) {
  37. html.textContent = storedData.value;
  38. }
  39. if (intent === "print") {
  40. break;
  41. }
  42. html.addEventListener("input", event => {
  43. storage.setValue(id, {
  44. value: event.target.value
  45. });
  46. });
  47. break;
  48. case "input":
  49. if (element.attributes.type === "radio" || element.attributes.type === "checkbox") {
  50. if (storedData.value === element.attributes.xfaOn) {
  51. html.setAttribute("checked", true);
  52. } else if (storedData.value === element.attributes.xfaOff) {
  53. html.removeAttribute("checked");
  54. }
  55. if (intent === "print") {
  56. break;
  57. }
  58. html.addEventListener("change", event => {
  59. storage.setValue(id, {
  60. value: event.target.checked ? event.target.getAttribute("xfaOn") : event.target.getAttribute("xfaOff")
  61. });
  62. });
  63. } else {
  64. if (storedData.value !== null) {
  65. html.setAttribute("value", storedData.value);
  66. }
  67. if (intent === "print") {
  68. break;
  69. }
  70. html.addEventListener("input", event => {
  71. storage.setValue(id, {
  72. value: event.target.value
  73. });
  74. });
  75. }
  76. break;
  77. case "select":
  78. if (storedData.value !== null) {
  79. for (const option of element.children) {
  80. if (option.attributes.value === storedData.value) {
  81. option.attributes.selected = true;
  82. }
  83. }
  84. }
  85. html.addEventListener("input", event => {
  86. const options = event.target.options;
  87. const value = options.selectedIndex === -1 ? "" : options[options.selectedIndex].value;
  88. storage.setValue(id, {
  89. value
  90. });
  91. });
  92. break;
  93. }
  94. }
  95. static setAttributes({
  96. html,
  97. element,
  98. storage = null,
  99. intent,
  100. linkService
  101. }) {
  102. const {
  103. attributes
  104. } = element;
  105. const isHTMLAnchorElement = html instanceof HTMLAnchorElement;
  106. if (attributes.type === "radio") {
  107. attributes.name = `${attributes.name}-${intent}`;
  108. }
  109. for (const [key, value] of Object.entries(attributes)) {
  110. if (value === null || value === undefined || key === "dataId") {
  111. continue;
  112. }
  113. if (key !== "style") {
  114. if (key === "textContent") {
  115. html.textContent = value;
  116. } else if (key === "class") {
  117. html.setAttribute(key, value.join(" "));
  118. } else {
  119. if (isHTMLAnchorElement && (key === "href" || key === "newWindow")) {
  120. continue;
  121. }
  122. html.setAttribute(key, value);
  123. }
  124. } else {
  125. Object.assign(html.style, value);
  126. }
  127. }
  128. if (isHTMLAnchorElement) {
  129. if (!linkService.addLinkAttributes) {
  130. (0, _util.warn)("XfaLayer.setAttribute - missing `addLinkAttributes`-method on the `linkService`-instance.");
  131. }
  132. linkService.addLinkAttributes?.(html, attributes.href, attributes.newWindow);
  133. }
  134. if (storage && attributes.dataId) {
  135. this.setupStorage(html, attributes.dataId, element, storage);
  136. }
  137. }
  138. static render(parameters) {
  139. const storage = parameters.annotationStorage;
  140. const linkService = parameters.linkService;
  141. const root = parameters.xfa;
  142. const intent = parameters.intent || "display";
  143. const rootHtml = document.createElement(root.name);
  144. if (root.attributes) {
  145. this.setAttributes({
  146. html: rootHtml,
  147. element: root,
  148. intent,
  149. linkService
  150. });
  151. }
  152. const stack = [[root, -1, rootHtml]];
  153. const rootDiv = parameters.div;
  154. rootDiv.appendChild(rootHtml);
  155. const transform = `matrix(${parameters.viewport.transform.join(",")})`;
  156. rootDiv.style.transform = transform;
  157. rootDiv.setAttribute("class", "xfaLayer xfaFont");
  158. const textDivs = [];
  159. while (stack.length > 0) {
  160. const [parent, i, html] = stack[stack.length - 1];
  161. if (i + 1 === parent.children.length) {
  162. stack.pop();
  163. continue;
  164. }
  165. const child = parent.children[++stack[stack.length - 1][1]];
  166. if (child === null) {
  167. continue;
  168. }
  169. const {
  170. name
  171. } = child;
  172. if (name === "#text") {
  173. const node = document.createTextNode(child.value);
  174. textDivs.push(node);
  175. html.appendChild(node);
  176. continue;
  177. }
  178. let childHtml;
  179. if (child?.attributes?.xmlns) {
  180. childHtml = document.createElementNS(child.attributes.xmlns, name);
  181. } else {
  182. childHtml = document.createElement(name);
  183. }
  184. html.appendChild(childHtml);
  185. if (child.attributes) {
  186. this.setAttributes({
  187. html: childHtml,
  188. element: child,
  189. storage,
  190. intent,
  191. linkService
  192. });
  193. }
  194. if (child.children && child.children.length > 0) {
  195. stack.push([child, -1, childHtml]);
  196. } else if (child.value) {
  197. const node = document.createTextNode(child.value);
  198. if (_xfa_text.XfaText.shouldBuildText(name)) {
  199. textDivs.push(node);
  200. }
  201. childHtml.appendChild(node);
  202. }
  203. }
  204. for (const el of rootDiv.querySelectorAll(".xfaNonInteractive input, .xfaNonInteractive textarea")) {
  205. el.setAttribute("readOnly", true);
  206. }
  207. return {
  208. textDivs
  209. };
  210. }
  211. static update(parameters) {
  212. const transform = `matrix(${parameters.viewport.transform.join(",")})`;
  213. parameters.div.style.transform = transform;
  214. parameters.div.hidden = false;
  215. }
  216. }
  217. exports.XfaLayer = XfaLayer;