版博士V2.0程序
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

223 строки
7.5 KiB

  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. const NodeTypeEnum_1 = __importDefault(require("./NodeTypeEnum"));
  7. /**
  8. * Node utility.
  9. */
  10. class NodeUtility {
  11. /**
  12. * Returns boolean indicating if nodeB is an inclusive ancestor of nodeA.
  13. *
  14. * Based on:
  15. * https://github.com/jsdom/jsdom/blob/master/lib/jsdom/living/helpers/node.js
  16. *
  17. * @see https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor
  18. * @param ancestorNode Ancestor node.
  19. * @param referenceNode Reference node.
  20. * @returns "true" if following.
  21. */
  22. static isInclusiveAncestor(ancestorNode, referenceNode) {
  23. let parent = referenceNode;
  24. while (parent) {
  25. if (ancestorNode === parent) {
  26. return true;
  27. }
  28. parent = parent.parentNode;
  29. }
  30. return false;
  31. }
  32. /**
  33. * Returns boolean indicating if nodeB is following nodeA in the document tree.
  34. *
  35. * Based on:
  36. * https://github.com/jsdom/jsdom/blob/master/lib/jsdom/living/helpers/node.js
  37. *
  38. * @see https://dom.spec.whatwg.org/#concept-tree-following
  39. * @param nodeA Node A.
  40. * @param nodeB Node B.
  41. * @returns "true" if following.
  42. */
  43. static isFollowing(nodeA, nodeB) {
  44. if (nodeA === nodeB) {
  45. return false;
  46. }
  47. let current = nodeB;
  48. while (current) {
  49. current = this.following(current);
  50. if (current === nodeA) {
  51. return true;
  52. }
  53. }
  54. return false;
  55. }
  56. /**
  57. * Node length.
  58. *
  59. * Based on:
  60. * https://github.com/jsdom/jsdom/blob/master/lib/jsdom/living/helpers/node.js
  61. *
  62. * @see https://dom.spec.whatwg.org/#concept-node-length
  63. * @param node Node.
  64. * @returns Node length.
  65. */
  66. static getNodeLength(node) {
  67. switch (node.nodeType) {
  68. case NodeTypeEnum_1.default.documentTypeNode:
  69. return 0;
  70. case NodeTypeEnum_1.default.textNode:
  71. case NodeTypeEnum_1.default.processingInstructionNode:
  72. case NodeTypeEnum_1.default.commentNode:
  73. return node.data.length;
  74. default:
  75. return node.childNodes.length;
  76. }
  77. }
  78. /**
  79. * Returns boolean indicating if nodeB is following nodeA in the document tree.
  80. *
  81. * Based on:
  82. * https://github.com/jsdom/js-symbol-tree/blob/master/lib/SymbolTree.js#L220
  83. *
  84. * @param node Node.
  85. * @param [root] Root.
  86. * @returns Following node.
  87. */
  88. static following(node, root) {
  89. const firstChild = node.firstChild;
  90. if (firstChild) {
  91. return firstChild;
  92. }
  93. let current = node;
  94. while (current) {
  95. if (current === root) {
  96. return null;
  97. }
  98. const nextSibling = current.nextSibling;
  99. if (nextSibling) {
  100. return nextSibling;
  101. }
  102. current = current.parentNode;
  103. }
  104. return null;
  105. }
  106. /**
  107. * Returns the next sibling or parents sibling.
  108. *
  109. * @param node Node.
  110. * @returns Next decentant node.
  111. */
  112. static nextDecendantNode(node) {
  113. while (node && !node.nextSibling) {
  114. node = node.parentNode;
  115. }
  116. if (!node) {
  117. return null;
  118. }
  119. return node.nextSibling;
  120. }
  121. /**
  122. * Needed by https://dom.spec.whatwg.org/#concept-node-equals
  123. *
  124. * @param elementA
  125. * @param elementB
  126. */
  127. static attributeListsEqual(elementA, elementB) {
  128. const listA = Object.values(elementA['_attributes']);
  129. const listB = Object.values(elementB['_attributes']);
  130. const lengthA = listA.length;
  131. const lengthB = listB.length;
  132. if (lengthA !== lengthB) {
  133. return false;
  134. }
  135. for (let i = 0; i < lengthA; ++i) {
  136. const attrA = listA[i];
  137. if (!listB.some((attrB) => {
  138. return ((typeof attrA === 'number' && typeof attrB === 'number' && attrA === attrB) ||
  139. (typeof attrA === 'object' &&
  140. typeof attrB === 'object' &&
  141. NodeUtility.isEqualNode(attrA, attrB)));
  142. })) {
  143. return false;
  144. }
  145. }
  146. return true;
  147. }
  148. /**
  149. * Check if node nodeA equals node nodeB.
  150. * Reference: https://dom.spec.whatwg.org/#concept-node-equals
  151. *
  152. * @param nodeA Node A.
  153. * @param nodeB Node B.
  154. */
  155. static isEqualNode(nodeA, nodeB) {
  156. if (nodeA.nodeType !== nodeB.nodeType) {
  157. return false;
  158. }
  159. switch (nodeA.nodeType) {
  160. case NodeTypeEnum_1.default.documentTypeNode:
  161. const documentTypeA = nodeA;
  162. const documentTypeB = nodeB;
  163. if (documentTypeA.name !== documentTypeB.name ||
  164. documentTypeA.publicId !== documentTypeB.publicId ||
  165. documentTypeA.systemId !== documentTypeB.systemId) {
  166. return false;
  167. }
  168. break;
  169. case NodeTypeEnum_1.default.elementNode:
  170. const elementA = nodeA;
  171. const elementB = nodeB;
  172. if (elementA.namespaceURI !== elementB.namespaceURI ||
  173. elementA.prefix !== elementB.prefix ||
  174. elementA.localName !== elementB.localName ||
  175. elementA.attributes.length !== elementB.attributes.length) {
  176. return false;
  177. }
  178. break;
  179. case NodeTypeEnum_1.default.attributeNode:
  180. const attributeA = nodeA;
  181. const attributeB = nodeB;
  182. if (attributeA.namespaceURI !== attributeB.namespaceURI ||
  183. attributeA.localName !== attributeB.localName ||
  184. attributeA.value !== attributeB.value) {
  185. return false;
  186. }
  187. break;
  188. case NodeTypeEnum_1.default.processingInstructionNode:
  189. const processingInstructionA = nodeA;
  190. const processingInstructionB = nodeB;
  191. if (processingInstructionA.target !== processingInstructionB.target ||
  192. processingInstructionA.data !== processingInstructionB.data) {
  193. return false;
  194. }
  195. break;
  196. case NodeTypeEnum_1.default.textNode:
  197. case NodeTypeEnum_1.default.commentNode:
  198. const textOrCommentA = nodeA;
  199. const textOrCommentB = nodeB;
  200. if (textOrCommentA.data !== textOrCommentB.data) {
  201. return false;
  202. }
  203. break;
  204. }
  205. if (nodeA.nodeType === NodeTypeEnum_1.default.elementNode &&
  206. !NodeUtility.attributeListsEqual(nodeA, nodeB)) {
  207. return false;
  208. }
  209. if (nodeA.childNodes.length !== nodeB.childNodes.length) {
  210. return false;
  211. }
  212. for (let i = 0; i < nodeA.childNodes.length; i++) {
  213. const childNodeA = nodeA.childNodes[i];
  214. const childNodeB = nodeB.childNodes[i];
  215. if (!NodeUtility.isEqualNode(childNodeA, childNodeB)) {
  216. return false;
  217. }
  218. }
  219. return true;
  220. }
  221. }
  222. exports.default = NodeUtility;
  223. //# sourceMappingURL=NodeUtility.js.map