semantic_node.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.SemanticNode = void 0;
  4. const DomUtil = require("../common/dom_util.js");
  5. const semantic_attr_js_1 = require("./semantic_attr.js");
  6. const semantic_meaning_js_1 = require("./semantic_meaning.js");
  7. const SemanticUtil = require("./semantic_util.js");
  8. class SemanticNode {
  9. static fromXml(xml) {
  10. const id = parseInt(xml.getAttribute('id'), 10);
  11. const node = new SemanticNode(id);
  12. node.type = xml.tagName;
  13. SemanticNode.setAttribute(node, xml, 'role');
  14. SemanticNode.setAttribute(node, xml, 'font');
  15. SemanticNode.setAttribute(node, xml, 'embellished');
  16. SemanticNode.setAttribute(node, xml, 'fencepointer', 'fencePointer');
  17. if (xml.getAttribute('annotation')) {
  18. node.parseAnnotation(xml.getAttribute('annotation'));
  19. }
  20. SemanticUtil.addAttributes(node, xml);
  21. SemanticNode.processChildren(node, xml);
  22. return node;
  23. }
  24. static setAttribute(node, xml, attribute, opt_name) {
  25. opt_name = opt_name || attribute;
  26. const value = xml.getAttribute(attribute);
  27. if (value) {
  28. node[opt_name] = value;
  29. }
  30. }
  31. static processChildren(node, xml) {
  32. for (const child of DomUtil.toArray(xml.childNodes)) {
  33. if (child.nodeType === DomUtil.NodeType.TEXT_NODE) {
  34. node.textContent = child.textContent;
  35. continue;
  36. }
  37. const children = DomUtil.toArray(child.childNodes).map(SemanticNode.fromXml);
  38. children.forEach((x) => (x.parent = node));
  39. if (DomUtil.tagName(child) === 'CONTENT') {
  40. node.contentNodes = children;
  41. }
  42. else {
  43. node.childNodes = children;
  44. }
  45. }
  46. }
  47. constructor(id) {
  48. this.id = id;
  49. this.mathml = [];
  50. this.parent = null;
  51. this.type = semantic_meaning_js_1.SemanticType.UNKNOWN;
  52. this.role = semantic_meaning_js_1.SemanticRole.UNKNOWN;
  53. this.font = semantic_meaning_js_1.SemanticFont.UNKNOWN;
  54. this.embellished = null;
  55. this.fencePointer = '';
  56. this.childNodes = [];
  57. this.textContent = '';
  58. this.mathmlTree = null;
  59. this.contentNodes = [];
  60. this.annotation = {};
  61. this.attributes = {};
  62. this.nobreaking = false;
  63. }
  64. querySelectorAll(pred) {
  65. let result = [];
  66. for (let i = 0, child; (child = this.childNodes[i]); i++) {
  67. result = result.concat(child.querySelectorAll(pred));
  68. }
  69. for (let i = 0, content; (content = this.contentNodes[i]); i++) {
  70. result = result.concat(content.querySelectorAll(pred));
  71. }
  72. if (pred(this)) {
  73. result.unshift(this);
  74. }
  75. return result;
  76. }
  77. xml(xml, brief) {
  78. const xmlNodeList = function (tag, nodes) {
  79. const xmlNodes = nodes.map(function (x) {
  80. return x.xml(xml, brief);
  81. });
  82. const tagNode = xml.createElementNS('', tag);
  83. for (let i = 0, child; (child = xmlNodes[i]); i++) {
  84. tagNode.appendChild(child);
  85. }
  86. return tagNode;
  87. };
  88. const node = xml.createElementNS('', this.type);
  89. if (!brief) {
  90. this.xmlAttributes(node);
  91. }
  92. node.textContent = this.textContent;
  93. if (this.contentNodes.length > 0) {
  94. node.appendChild(xmlNodeList("content", this.contentNodes));
  95. }
  96. if (this.childNodes.length > 0) {
  97. node.appendChild(xmlNodeList("children", this.childNodes));
  98. }
  99. return node;
  100. }
  101. toString(brief = false) {
  102. const xml = DomUtil.parseInput('<snode/>');
  103. return DomUtil.serializeXml(this.xml(xml.ownerDocument, brief));
  104. }
  105. allAttributes() {
  106. const attributes = [];
  107. attributes.push(["role", this.role]);
  108. if (this.font !== semantic_meaning_js_1.SemanticFont.UNKNOWN) {
  109. attributes.push(["font", this.font]);
  110. }
  111. if (Object.keys(this.annotation).length) {
  112. attributes.push(["annotation", this.annotationXml()]);
  113. }
  114. if (this.embellished) {
  115. attributes.push(["embellished", this.embellished]);
  116. }
  117. if (this.fencePointer) {
  118. attributes.push(["fencepointer", this.fencePointer]);
  119. }
  120. attributes.push(["id", this.id.toString()]);
  121. return attributes;
  122. }
  123. annotationXml() {
  124. const result = [];
  125. for (const [key, val] of Object.entries(this.annotation)) {
  126. val.forEach((mean) => result.push(key + ':' + mean));
  127. }
  128. return result.join(';');
  129. }
  130. attributesXml() {
  131. const result = [];
  132. for (const [key, value] of Object.entries(this.attributes)) {
  133. result.push(key + ':' + SemanticNode.escapeValue(value));
  134. }
  135. return result.join(';');
  136. }
  137. toJson() {
  138. const json = {};
  139. json["type"] = this.type;
  140. const attributes = this.allAttributes();
  141. for (let i = 0, attr; (attr = attributes[i]); i++) {
  142. json[attr[0]] = attr[1].toString();
  143. }
  144. if (this.textContent) {
  145. json["$t"] = this.textContent;
  146. }
  147. if (this.childNodes.length) {
  148. json["children"] = this.childNodes.map(function (child) {
  149. return child.toJson();
  150. });
  151. }
  152. if (this.contentNodes.length) {
  153. json["content"] = this.contentNodes.map(function (child) {
  154. return child.toJson();
  155. });
  156. }
  157. return json;
  158. }
  159. updateContent(content, text) {
  160. const newContent = text
  161. ? content
  162. .replace(/^[ \f\n\r\t\v\u200b]*/, '')
  163. .replace(/[ \f\n\r\t\v\u200b]*$/, '')
  164. : content.trim();
  165. content = content && !newContent ? content : newContent;
  166. if (this.textContent === content) {
  167. return;
  168. }
  169. const meaning = semantic_attr_js_1.SemanticMap.Meaning.get(content.replace(/\s/g, ' '));
  170. this.textContent = content;
  171. this.role = meaning.role;
  172. this.type = meaning.type;
  173. this.font = meaning.font;
  174. }
  175. addMathmlNodes(mmlNodes) {
  176. for (let i = 0, mml; (mml = mmlNodes[i]); i++) {
  177. if (this.mathml.indexOf(mml) === -1) {
  178. this.mathml.push(mml);
  179. }
  180. }
  181. }
  182. appendChild(child) {
  183. this.childNodes.push(child);
  184. this.addMathmlNodes(child.mathml);
  185. child.parent = this;
  186. }
  187. replaceChild(oldNode, newNode) {
  188. const index = this.childNodes.indexOf(oldNode);
  189. if (index === -1) {
  190. return;
  191. }
  192. oldNode.parent = null;
  193. newNode.parent = this;
  194. this.childNodes[index] = newNode;
  195. const removeMathml = oldNode.mathml.filter(function (x) {
  196. return newNode.mathml.indexOf(x) === -1;
  197. });
  198. const addMathml = newNode.mathml.filter(function (x) {
  199. return oldNode.mathml.indexOf(x) === -1;
  200. });
  201. this.removeMathmlNodes(removeMathml);
  202. this.addMathmlNodes(addMathml);
  203. }
  204. appendContentNode(node) {
  205. if (node) {
  206. this.contentNodes.push(node);
  207. this.addMathmlNodes(node.mathml);
  208. node.parent = this;
  209. }
  210. }
  211. removeContentNode(node) {
  212. if (node) {
  213. const index = this.contentNodes.indexOf(node);
  214. if (index !== -1) {
  215. this.contentNodes.slice(index, 1);
  216. }
  217. }
  218. }
  219. equals(node) {
  220. if (!node) {
  221. return false;
  222. }
  223. if (this.type !== node.type ||
  224. this.role !== node.role ||
  225. this.textContent !== node.textContent ||
  226. this.childNodes.length !== node.childNodes.length ||
  227. this.contentNodes.length !== node.contentNodes.length) {
  228. return false;
  229. }
  230. for (let i = 0, node1, node2; (node1 = this.childNodes[i]), (node2 = node.childNodes[i]); i++) {
  231. if (!node1.equals(node2)) {
  232. return false;
  233. }
  234. }
  235. for (let i = 0, node1, node2; (node1 = this.contentNodes[i]), (node2 = node.contentNodes[i]); i++) {
  236. if (!node1.equals(node2)) {
  237. return false;
  238. }
  239. }
  240. return true;
  241. }
  242. displayTree() {
  243. console.info(this.displayTree_(0));
  244. }
  245. addAnnotation(domain, annotation) {
  246. if (annotation) {
  247. this.addAnnotation_(domain, annotation);
  248. }
  249. }
  250. getAnnotation(domain) {
  251. const content = this.annotation[domain];
  252. return content ? content : [];
  253. }
  254. hasAnnotation(domain, annotation) {
  255. const content = this.annotation[domain];
  256. if (!content) {
  257. return false;
  258. }
  259. return content.indexOf(annotation) !== -1;
  260. }
  261. parseAnnotation(stateStr) {
  262. const annotations = stateStr.split(';');
  263. for (let i = 0, l = annotations.length; i < l; i++) {
  264. const annotation = annotations[i].split(':');
  265. this.addAnnotation(annotation[0], annotation[1]);
  266. }
  267. }
  268. meaning() {
  269. return { type: this.type, role: this.role, font: this.font };
  270. }
  271. xmlAttributes(node) {
  272. const attributes = this.allAttributes();
  273. for (let i = 0, attr; (attr = attributes[i]); i++) {
  274. node.setAttribute(attr[0], attr[1]);
  275. }
  276. this.addExternalAttributes(node);
  277. }
  278. addExternalAttributes(node) {
  279. for (const [attr, val] of Object.entries(this.attributes)) {
  280. node.setAttribute(attr, val);
  281. }
  282. }
  283. static escapeValue(value) {
  284. return value.replace(/;/g, '\\0003B');
  285. }
  286. parseAttributes(stateStr) {
  287. if (!stateStr)
  288. return;
  289. const attributes = stateStr.split(';');
  290. for (let i = 0, l = attributes.length; i < l; i++) {
  291. const [key, ...values] = attributes[i].split(':');
  292. if (key) {
  293. this.attributes[key] = values.join('').replace(/\\0003B/g, ';');
  294. }
  295. }
  296. }
  297. removeMathmlNodes(mmlNodes) {
  298. const mmlList = this.mathml;
  299. for (let i = 0, mml; (mml = mmlNodes[i]); i++) {
  300. const index = mmlList.indexOf(mml);
  301. if (index !== -1) {
  302. mmlList.splice(index, 1);
  303. }
  304. }
  305. this.mathml = mmlList;
  306. }
  307. displayTree_(depth) {
  308. depth++;
  309. const depthString = Array(depth).join(' ');
  310. let result = '';
  311. result += '\n' + depthString + this.toString();
  312. result += '\n' + depthString + 'MathmlTree:';
  313. result += '\n' + depthString + this.mathmlTreeString();
  314. result += '\n' + depthString + 'MathML:';
  315. for (let i = 0, mml; (mml = this.mathml[i]); i++) {
  316. result += '\n' + depthString + mml.toString();
  317. }
  318. result += '\n' + depthString + 'Begin Content';
  319. this.contentNodes.forEach(function (x) {
  320. result += x.displayTree_(depth);
  321. });
  322. result += '\n' + depthString + 'End Content';
  323. result += '\n' + depthString + 'Begin Children';
  324. this.childNodes.forEach(function (x) {
  325. result += x.displayTree_(depth);
  326. });
  327. result += '\n' + depthString + 'End Children';
  328. return result;
  329. }
  330. mathmlTreeString() {
  331. return this.mathmlTree ? this.mathmlTree.toString() : 'EMPTY';
  332. }
  333. addAnnotation_(domain, annotation) {
  334. const content = this.annotation[domain];
  335. if (content && !content.includes(annotation)) {
  336. content.push(annotation);
  337. }
  338. else {
  339. this.annotation[domain] = [annotation];
  340. }
  341. }
  342. }
  343. exports.SemanticNode = SemanticNode;