1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.javascript.host.dom;
16
17 import java.io.Serializable;
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Objects;
23 import java.util.function.Supplier;
24
25 import org.htmlunit.SgmlPage;
26 import org.htmlunit.corejs.javascript.Context;
27 import org.htmlunit.corejs.javascript.Function;
28 import org.htmlunit.corejs.javascript.Scriptable;
29 import org.htmlunit.corejs.javascript.VarScope;
30 import org.htmlunit.html.DomDocumentFragment;
31 import org.htmlunit.html.DomElement;
32 import org.htmlunit.html.DomNode;
33 import org.htmlunit.html.HtmlElement;
34 import org.htmlunit.html.HtmlInlineFrame;
35 import org.htmlunit.javascript.HtmlUnitScriptable;
36 import org.htmlunit.javascript.JavaScriptEngine;
37 import org.htmlunit.javascript.configuration.JsxClass;
38 import org.htmlunit.javascript.configuration.JsxConstant;
39 import org.htmlunit.javascript.configuration.JsxConstructor;
40 import org.htmlunit.javascript.configuration.JsxFunction;
41 import org.htmlunit.javascript.configuration.JsxGetter;
42 import org.htmlunit.javascript.configuration.JsxSetter;
43 import org.htmlunit.javascript.host.Element;
44 import org.htmlunit.javascript.host.NamedNodeMap;
45 import org.htmlunit.javascript.host.event.EventTarget;
46 import org.htmlunit.javascript.host.html.HTMLCollection;
47 import org.htmlunit.javascript.host.html.HTMLDocument;
48 import org.htmlunit.javascript.host.html.HTMLHtmlElement;
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 @JsxClass
66 public class Node extends EventTarget {
67
68
69
70
71 @JsxConstant
72 public static final int ELEMENT_NODE = org.w3c.dom.Node.ELEMENT_NODE;
73
74
75
76
77 @JsxConstant
78 public static final int ATTRIBUTE_NODE = org.w3c.dom.Node.ATTRIBUTE_NODE;
79
80
81
82
83 @JsxConstant
84 public static final int TEXT_NODE = org.w3c.dom.Node.TEXT_NODE;
85
86
87
88
89 @JsxConstant
90 public static final int CDATA_SECTION_NODE = org.w3c.dom.Node.CDATA_SECTION_NODE;
91
92
93
94
95 @JsxConstant
96 public static final int ENTITY_REFERENCE_NODE = org.w3c.dom.Node.ENTITY_REFERENCE_NODE;
97
98
99
100
101 @JsxConstant
102 public static final int ENTITY_NODE = org.w3c.dom.Node.ENTITY_NODE;
103
104
105
106
107 @JsxConstant
108 public static final int PROCESSING_INSTRUCTION_NODE = org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE;
109
110
111
112
113 @JsxConstant
114 public static final int COMMENT_NODE = org.w3c.dom.Node.COMMENT_NODE;
115
116
117
118
119 @JsxConstant
120 public static final int DOCUMENT_NODE = org.w3c.dom.Node.DOCUMENT_NODE;
121
122
123
124
125 @JsxConstant
126 public static final int DOCUMENT_TYPE_NODE = org.w3c.dom.Node.DOCUMENT_TYPE_NODE;
127
128
129
130
131 @JsxConstant
132 public static final int DOCUMENT_FRAGMENT_NODE = org.w3c.dom.Node.DOCUMENT_FRAGMENT_NODE;
133
134
135
136
137 @JsxConstant
138 public static final int NOTATION_NODE = org.w3c.dom.Node.NOTATION_NODE;
139
140
141
142
143 @JsxConstant
144 public static final int DOCUMENT_POSITION_DISCONNECTED = org.w3c.dom.Node.DOCUMENT_POSITION_DISCONNECTED;
145
146
147
148
149 @JsxConstant
150 public static final int DOCUMENT_POSITION_PRECEDING = org.w3c.dom.Node.DOCUMENT_POSITION_PRECEDING;
151
152
153
154
155 @JsxConstant
156 public static final int DOCUMENT_POSITION_FOLLOWING = org.w3c.dom.Node.DOCUMENT_POSITION_FOLLOWING;
157
158
159
160
161 @JsxConstant
162 public static final int DOCUMENT_POSITION_CONTAINS = org.w3c.dom.Node.DOCUMENT_POSITION_CONTAINS;
163
164
165
166
167 @JsxConstant
168 public static final int DOCUMENT_POSITION_CONTAINED_BY = org.w3c.dom.Node.DOCUMENT_POSITION_CONTAINED_BY;
169
170
171
172
173 @JsxConstant
174 public static final int DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
175 = org.w3c.dom.Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
176
177
178 private NodeList childNodes_;
179
180
181
182
183 @Override
184 @JsxConstructor
185 public void jsConstructor() {
186 super.jsConstructor();
187 }
188
189
190
191
192
193 @JsxGetter
194 public int getNodeType() {
195 return getDomNodeOrDie().getNodeType();
196 }
197
198
199
200
201
202 @JsxGetter
203 public String getNodeName() {
204 return getDomNodeOrDie().getNodeName();
205 }
206
207
208
209
210
211 @JsxGetter
212 public String getNodeValue() {
213 return getDomNodeOrDie().getNodeValue();
214 }
215
216
217
218
219
220 @JsxSetter
221 public void setNodeValue(final String newValue) {
222 getDomNodeOrDie().setNodeValue(newValue);
223 }
224
225
226
227
228
229
230 @JsxFunction
231 public Node appendChild(final Object childObject) {
232 if (childObject instanceof Node childNode) {
233
234
235 if (!isNodeInsertable(childNode)) {
236 throw JavaScriptEngine.asJavaScriptException(
237 getWindow(),
238 "Node cannot be inserted at the specified point in the hierarchy",
239 DOMException.HIERARCHY_REQUEST_ERR);
240 }
241
242
243 final DomNode childDomNode = childNode.getDomNodeOrDie();
244
245
246 final DomNode parentNode = getDomNodeOrDie();
247
248
249 try {
250 parentNode.appendChild(childDomNode);
251 }
252 catch (final org.w3c.dom.DOMException e) {
253 throw JavaScriptEngine.asJavaScriptException(getWindow(), e.getMessage(), e.code);
254 }
255
256 initInlineFrameIfNeeded(childDomNode);
257 for (final HtmlElement htmlElement : childDomNode.getHtmlElementDescendants()) {
258 initInlineFrameIfNeeded(htmlElement);
259 }
260 return childNode;
261 }
262 return null;
263 }
264
265
266
267
268
269
270 private static void initInlineFrameIfNeeded(final DomNode childDomNode) {
271 if (childDomNode instanceof HtmlInlineFrame frame) {
272 if (DomElement.ATTRIBUTE_NOT_DEFINED == frame.getSrcAttribute()) {
273 frame.loadInnerPage();
274 }
275 }
276 }
277
278
279
280
281
282
283
284
285
286
287
288 @JsxFunction
289 public static Node insertBefore(final Context context, final VarScope scope,
290 final Scriptable thisObj, final Object[] args, final Function function) {
291 return ((Node) thisObj).insertBeforeImpl(args);
292 }
293
294
295
296
297
298
299
300 protected Node insertBeforeImpl(final Object[] args) {
301 if (args.length < 1) {
302 throw JavaScriptEngine.typeError(
303 "Failed to execute 'insertBefore' on 'Node': 2 arguments required, but only 0 present.");
304 }
305
306 final Object newChildObject = args[0];
307 final Object refChildObject;
308 if (args.length > 1) {
309 refChildObject = args[1];
310 }
311 else {
312 refChildObject = JavaScriptEngine.UNDEFINED;
313 }
314
315 if (newChildObject instanceof Node newChild) {
316
317
318 if (!isNodeInsertable(newChild)) {
319 throw JavaScriptEngine.asJavaScriptException(
320 getWindow(),
321 "Node cannot be inserted at the specified point in the hierarchy",
322 DOMException.HIERARCHY_REQUEST_ERR);
323 }
324
325 final DomNode newChildNode = newChild.getDomNodeOrDie();
326 if (newChildNode instanceof DomDocumentFragment fragment) {
327 for (final DomNode child : fragment.getChildren()) {
328 if (!isNodeInsertable(child.getScriptableObject())) {
329 throw JavaScriptEngine.asJavaScriptException(
330 getWindow(),
331 "Node cannot be inserted at the specified point in the hierarchy",
332 DOMException.HIERARCHY_REQUEST_ERR);
333 }
334 }
335 }
336
337
338 final DomNode refChildNode;
339 if (JavaScriptEngine.isUndefined(refChildObject)) {
340 if (args.length == 2) {
341 refChildNode = null;
342 }
343 else {
344 throw JavaScriptEngine.typeError(
345 "Failed to execute 'insertBefore' on 'Node': 2 arguments required, but only 1 present.");
346 }
347 }
348 else if (refChildObject == null) {
349 refChildNode = null;
350 }
351 else {
352 refChildNode = ((Node) refChildObject).getDomNodeOrDie();
353 }
354
355 final DomNode domNode = getDomNodeOrDie();
356
357 try {
358 domNode.insertBefore(newChildNode, refChildNode);
359 }
360 catch (final org.w3c.dom.DOMException e) {
361 throw JavaScriptEngine.asJavaScriptException(getWindow(), e.getMessage(), DOMException.NOT_FOUND_ERR);
362 }
363 return newChild;
364 }
365 return null;
366 }
367
368
369
370
371
372
373 private static boolean isNodeInsertable(final Node childObject) {
374 if (childObject instanceof HTMLHtmlElement) {
375 final DomNode domNode = childObject.getDomNodeOrDie();
376 return domNode.getPage().getDocumentElement() != domNode;
377 }
378 return true;
379 }
380
381
382
383
384
385 protected void remove() {
386 getDomNodeOrDie().remove();
387 }
388
389
390
391
392
393
394 @JsxFunction
395 public Node removeChild(final Object childObject) {
396 if (!(childObject instanceof Node childObjectNode)) {
397 return null;
398 }
399
400
401 final DomNode childDomNode = childObjectNode.getDomNodeOrDie();
402
403 if (!getDomNodeOrDie().isAncestorOf(childDomNode)) {
404 throw JavaScriptEngine.asJavaScriptException(
405 getWindow(),
406 "Failed to execute 'removeChild' on '"
407 + this + "': The node to be removed is not a child of this node.",
408 DOMException.NOT_FOUND_ERR);
409 }
410
411 childDomNode.remove();
412 return childObjectNode;
413 }
414
415
416
417
418
419
420
421 @JsxFunction
422 public Node replaceChild(final Object newChildObject, final Object oldChildObject) {
423 if (newChildObject instanceof DocumentFragment fragment) {
424 Node firstNode = null;
425
426 final Node oldChildNode = (Node) oldChildObject;
427 final Node refChildObject = oldChildNode.getNextSibling();
428 for (final DomNode node : fragment.getDomNodeOrDie().getChildren()) {
429 if (firstNode == null) {
430 replaceChild(node.getScriptableObject(), oldChildObject);
431 firstNode = node.getScriptableObject();
432 }
433 else {
434 insertBeforeImpl(new Object[] {node.getScriptableObject(), refChildObject});
435 }
436 }
437 if (firstNode == null) {
438 removeChild(oldChildObject);
439 }
440
441 return oldChildNode;
442 }
443
444 if (newChildObject instanceof Node newChild && oldChildObject instanceof Node oldChildNode) {
445
446
447 if (!isNodeInsertable(newChild)) {
448 throw JavaScriptEngine.asJavaScriptException(
449 getWindow(),
450 "Node cannot be inserted at the specified point in the hierarchy",
451 DOMException.HIERARCHY_REQUEST_ERR);
452 }
453
454
455 final DomNode newChildDomNode = newChild.getDomNodeOrDie();
456 final DomNode oldChildDomNode = oldChildNode.getDomNodeOrDie();
457
458
459 oldChildDomNode.replace(newChildDomNode);
460
461 return oldChildNode;
462 }
463
464 return null;
465 }
466
467
468
469
470
471
472
473
474
475
476 public static void moveBefore(final Context context, final VarScope scope,
477 final Scriptable thisObj, final Object[] args, final Function function) {
478 if (args.length < 2) {
479 throw JavaScriptEngine.typeError(
480 "Failed to execute 'moveBefore' on 'Element': 2 arguments required, but only 0 present.");
481 }
482
483 final Object movedNodeObject = args[0];
484 if (!(movedNodeObject instanceof Node)) {
485 throw JavaScriptEngine.typeError(
486 "Failed to execute 'moveBefore' on 'Element': parameter 1 is not of type 'Node'.");
487 }
488 final Object referenceNodeObject = args[1];
489 if (referenceNodeObject != null && !(referenceNodeObject instanceof Node)) {
490 throw JavaScriptEngine.typeError(
491 "Failed to execute 'moveBefore' on 'Element': parameter 2 is not of type 'Node'.");
492 }
493
494 final Node node = (Node) thisObj;
495 try {
496 if (referenceNodeObject == null) {
497 node.getDomNodeOrDie().moveBefore(((Node) movedNodeObject).getDomNodeOrDie(), null);
498 return;
499 }
500
501 node.getDomNodeOrDie().moveBefore(
502 ((Node) movedNodeObject).getDomNodeOrDie(), ((Node) referenceNodeObject).getDomNodeOrDie());
503 }
504 catch (final org.w3c.dom.DOMException e) {
505 throw JavaScriptEngine.asJavaScriptException(
506 node.getWindow(),
507 "Failed to execute 'moveChild' on '" + node + ": " + e.getMessage(),
508 e.code);
509 }
510 }
511
512
513
514
515
516
517 @JsxFunction
518 public Node cloneNode(final boolean deep) {
519 final DomNode domNode = getDomNodeOrDie();
520 final DomNode clonedNode = domNode.cloneNode(deep);
521
522 return getJavaScriptNode(clonedNode);
523 }
524
525
526
527
528
529
530
531
532 @JsxFunction
533 public boolean isEqualNode(final Node other) {
534 if (isSameNode(other)) {
535 return true;
536 }
537
538 if (other == null) {
539 return false;
540 }
541
542 if (!getClassName().equals(other.getClassName())) {
543 return false;
544 }
545
546 if (this instanceof DocumentType docType) {
547 final DocumentType otherDocType = (DocumentType) other;
548 if (!Objects.equals(docType.getName(), otherDocType.getName())
549 || !Objects.equals(docType.getPublicId(), otherDocType.getPublicId())
550 || !Objects.equals(docType.getSystemId(), otherDocType.getSystemId())) {
551 return false;
552 }
553
554 }
555 else if (this instanceof Element element) {
556 final Element otherElement = (Element) other;
557 if (!Objects.equals(element.getNodeName(), otherElement.getNodeName())
558 || !Objects.equals(element.getPrefix(), otherElement.getPrefix())
559 || !Objects.equals(element.getLocalName(), otherElement.getLocalName())) {
560 return false;
561 }
562
563 final NamedNodeMap attributesMap = element.getAttributes();
564 final NamedNodeMap otherAttributesMap = otherElement.getAttributes();
565 if (attributesMap != null || otherAttributesMap != null) {
566 if (attributesMap == null || otherAttributesMap == null) {
567 return false;
568 }
569
570 final int length = attributesMap.getLength();
571 if (length != otherAttributesMap.getLength()) {
572 return false;
573 }
574
575 final Map<String, Attr> name2Attributes = new HashMap<>();
576 for (int i = 0; i < length; i++) {
577 final Attr attribute = (Attr) attributesMap.item(i);
578 name2Attributes.put(attribute.getName(), attribute);
579 }
580
581 for (int i = 0; i < length; i++) {
582 final Attr otherAttribute = (Attr) otherAttributesMap.item(i);
583 final Attr attribute = name2Attributes.get(otherAttribute.getName());
584 if (attribute == null) {
585 return false;
586 }
587 if (!attribute.isEqualNode(otherAttribute)) {
588 return false;
589 }
590 }
591 }
592
593 }
594 else if (this instanceof Attr attr) {
595 final Attr otherAttr = (Attr) other;
596 if (!Objects.equals(attr.getName(), otherAttr.getName())
597 || !Objects.equals(attr.getLocalName(), otherAttr.getLocalName())
598 || !Objects.equals(attr.getValue(), otherAttr.getValue())) {
599 return false;
600 }
601
602 }
603 else if (this instanceof ProcessingInstruction instruction) {
604 final ProcessingInstruction otherInstruction = (ProcessingInstruction) other;
605 if (!Objects.equals(instruction.getTarget(), otherInstruction.getTarget())
606 || !Objects.equals(instruction.getData(), otherInstruction.getData())) {
607 return false;
608 }
609
610 }
611 else if (this instanceof Text || this instanceof Comment) {
612 final CharacterData data = (CharacterData) this;
613 final CharacterData otherData = (CharacterData) other;
614 if (!Objects.equals(data.getData(), otherData.getData())) {
615 return false;
616 }
617 }
618
619 final NodeList childNodes = getChildNodes();
620 final NodeList otherChildNodes = other.getChildNodes();
621 if (childNodes != null || otherChildNodes != null) {
622 if (childNodes == null || otherChildNodes == null) {
623 return false;
624 }
625
626 final int length = childNodes.getLength();
627 final int otherLength = otherChildNodes.getLength();
628 if (length != otherLength) {
629 return false;
630 }
631
632 for (int i = 0; i < length; i++) {
633 final Node childNode = (Node) childNodes.item(i);
634 final Node otherChildNode = (Node) otherChildNodes.item(i);
635 if (!childNode.isEqualNode(otherChildNode)) {
636 return false;
637 }
638 }
639 }
640
641 return true;
642 }
643
644
645
646
647
648
649
650
651
652
653
654
655 @JsxFunction
656 public boolean isSameNode(final Object other) {
657 return this == other;
658 }
659
660
661
662
663
664 @JsxFunction
665 public boolean hasChildNodes() {
666 return getDomNodeOrDie().getChildren().iterator().hasNext();
667 }
668
669
670
671
672
673
674
675 @JsxFunction
676 public String lookupPrefix(final String namespace) {
677 return null;
678 }
679
680
681
682
683
684 @JsxGetter
685 public NodeList getChildNodes() {
686 if (childNodes_ == null) {
687 final DomNode node = getDomNodeOrDie();
688 childNodes_ = new NodeList(node, false);
689 childNodes_.setElementsSupplier(
690 (Supplier<List<DomNode>> & Serializable)
691 () -> {
692 final List<DomNode> response = new ArrayList<>();
693 for (final DomNode child : node.getChildren()) {
694 response.add(child);
695 }
696
697 return response;
698 });
699 }
700 return childNodes_;
701 }
702
703
704
705
706
707 public final Node getParent() {
708 return getJavaScriptNode(getDomNodeOrDie().getParentNode());
709 }
710
711
712
713
714
715
716 @JsxGetter
717 public Object getParentNode() {
718 return getJavaScriptNode(getDomNodeOrDie().getParentNode());
719 }
720
721
722
723
724
725
726
727 @JsxGetter
728 public Node getNextSibling() {
729 return getJavaScriptNode(getDomNodeOrDie().getNextSibling());
730 }
731
732
733
734
735
736
737
738 @JsxGetter
739 public Node getPreviousSibling() {
740 return getJavaScriptNode(getDomNodeOrDie().getPreviousSibling());
741 }
742
743
744
745
746
747
748
749 @JsxGetter
750 public Node getFirstChild() {
751 return getJavaScriptNode(getDomNodeOrDie().getFirstChild());
752 }
753
754
755
756
757
758
759
760 @JsxGetter
761 public Node getLastChild() {
762 return getJavaScriptNode(getDomNodeOrDie().getLastChild());
763 }
764
765
766
767
768
769
770 protected Node getJavaScriptNode(final DomNode domNode) {
771 if (domNode == null) {
772 return null;
773 }
774 return (Node) getScriptableFor(domNode);
775 }
776
777
778
779
780
781 @JsxGetter
782 public HtmlUnitScriptable getOwnerDocument() {
783 final Object document = getDomNodeOrDie().getOwnerDocument();
784 if (document != null) {
785 return ((SgmlPage) document).getScriptableObject();
786 }
787 return null;
788 }
789
790
791
792
793
794 @JsxFunction
795 public Node getRootNode() {
796 Node parent = this;
797 while (parent != null) {
798 if (parent instanceof Document || parent instanceof DocumentFragment) {
799 return parent;
800 }
801 parent = parent.getParent();
802 }
803 return this;
804 }
805
806
807
808
809
810
811
812
813 @JsxFunction
814 public int compareDocumentPosition(final Object node) {
815 if (!(node instanceof Node)) {
816 throw JavaScriptEngine.typeError("Could not convert JavaScript argument arg 0");
817 }
818 return getDomNodeOrDie().compareDocumentPosition(((Node) node).getDomNodeOrDie());
819 }
820
821
822
823
824 @JsxFunction
825 public void normalize() {
826 getDomNodeOrDie().normalize();
827 }
828
829
830
831
832
833 @JsxGetter
834 public String getTextContent() {
835 return getDomNodeOrDie().getTextContent();
836 }
837
838
839
840
841
842 @JsxSetter
843 public void setTextContent(final Object value) {
844 getDomNodeOrDie().setTextContent(value == null ? null : JavaScriptEngine.toString(value));
845 }
846
847
848
849
850
851
852 @JsxGetter
853 public Element getParentElement() {
854 final Node parent = getParent();
855 if (!(parent instanceof Element)) {
856 return null;
857 }
858 return (Element) parent;
859 }
860
861
862
863
864
865
866 public NamedNodeMap getAttributes() {
867 return null;
868 }
869
870
871
872
873
874
875 @JsxFunction
876 public boolean contains(final Object element) {
877 if (element == null || JavaScriptEngine.isUndefined(element)) {
878 return false;
879 }
880
881 if (!(element instanceof Node parent)) {
882 throw JavaScriptEngine.reportRuntimeError("Could not convert JavaScript argument arg 0");
883 }
884
885 for ( ; parent != null; parent = parent.getParentElement()) {
886 if (this == parent) {
887 return true;
888 }
889 }
890 return false;
891 }
892
893
894
895
896
897 @JsxGetter
898 public String getBaseURI() {
899 return getDomNodeOrDie().getBaseURI();
900 }
901
902
903
904
905
906 public boolean hasAttributes() {
907 return getDomNodeOrDie().hasAttributes();
908 }
909
910
911
912
913
914 public String getPrefix() {
915 return getDomNodeOrDie().getPrefix();
916 }
917
918
919
920
921
922 public String getLocalName() {
923 return getDomNodeOrDie().getLocalName();
924 }
925
926
927
928
929
930 public String getNamespaceURI() {
931 return getDomNodeOrDie().getNamespaceURI();
932 }
933
934
935
936
937
938 protected int getChildElementCount() {
939 final DomNode domNode = getDomNodeOrDie();
940 if (domNode instanceof DomElement element) {
941 return element.getChildElementCount();
942 }
943
944 int counter = 0;
945 for (final DomNode child : getDomNodeOrDie().getChildren()) {
946 if (child != null) {
947 final HtmlUnitScriptable scriptable = child.getScriptableObject();
948 if (scriptable instanceof Element) {
949 counter++;
950 }
951 }
952 }
953 return counter;
954 }
955
956
957
958
959
960 protected Element getFirstElementChild() {
961 final DomNode domNode = getDomNodeOrDie();
962 if (domNode instanceof DomElement element) {
963 final DomElement child = element.getFirstElementChild();
964 if (child != null) {
965 return child.getScriptableObject();
966 }
967 return null;
968 }
969
970 for (final DomNode child : domNode.getChildren()) {
971 if (child != null) {
972 final HtmlUnitScriptable scriptable = child.getScriptableObject();
973 if (scriptable instanceof Element element) {
974 return element;
975 }
976 }
977 }
978 return null;
979 }
980
981
982
983
984
985 protected Element getLastElementChild() {
986 final DomNode domNode = getDomNodeOrDie();
987 if (domNode instanceof DomElement) {
988 final DomElement child = ((DomElement) getDomNodeOrDie()).getLastElementChild();
989 if (child != null) {
990 return child.getScriptableObject();
991 }
992 return null;
993 }
994
995 Element result = null;
996 for (final DomNode child : domNode.getChildren()) {
997 final HtmlUnitScriptable scriptable = child.getScriptableObject();
998 if (scriptable instanceof Element element) {
999 result = element;
1000 }
1001 }
1002 return result;
1003 }
1004
1005
1006
1007
1008
1009
1010 protected HTMLCollection getChildren() {
1011 final DomNode node = getDomNodeOrDie();
1012 final HTMLCollection childrenColl = new HTMLCollection(node, false);
1013 childrenColl.setElementsSupplier(
1014 (Supplier<List<DomNode>> & Serializable)
1015 () -> {
1016 final List<DomNode> children = new ArrayList<>();
1017 for (final DomNode domNode : node.getChildNodes()) {
1018 if (domNode instanceof DomElement) {
1019 children.add(domNode);
1020 }
1021 }
1022 return children;
1023 });
1024 return childrenColl;
1025 }
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035 protected static void after(final Context context, final Scriptable thisObj, final Object[] args,
1036 final Function function) {
1037 final DomNode thisDomNode = ((Node) thisObj).getDomNodeOrDie();
1038 final DomNode parentNode = thisDomNode.getParentNode();
1039 final DomNode nextSibling = thisDomNode.getNextSibling();
1040 for (final Object arg : args) {
1041 final Node node = toNodeOrTextNode((Node) thisObj, arg);
1042 final DomNode newNode = node.getDomNodeOrDie();
1043 if (nextSibling == null) {
1044 parentNode.appendChild(newNode);
1045 }
1046 else {
1047 nextSibling.insertBefore(newNode);
1048 }
1049 }
1050 }
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060 protected static void append(final Context context, final Scriptable thisObj, final Object[] args,
1061 final Function function) {
1062 if (!(thisObj instanceof Node thisNode)) {
1063 throw JavaScriptEngine.typeError("Illegal invocation");
1064 }
1065
1066 final DomNode thisDomNode = thisNode.getDomNodeOrDie();
1067
1068 for (final Object arg : args) {
1069 final Node node = toNodeOrTextNode(thisNode, arg);
1070 thisDomNode.appendChild(node.getDomNodeOrDie());
1071 }
1072 }
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082 protected static void prepend(final Context context, final Scriptable thisObj, final Object[] args,
1083 final Function function) {
1084 if (!(thisObj instanceof Node thisNode)) {
1085 throw JavaScriptEngine.typeError("Illegal invocation");
1086 }
1087
1088 final DomNode thisDomNode = thisNode.getDomNodeOrDie();
1089 final DomNode firstChild = thisDomNode.getFirstChild();
1090
1091 for (final Object arg : args) {
1092 final Node node = toNodeOrTextNode(thisNode, arg);
1093 final DomNode newNode = node.getDomNodeOrDie();
1094 if (firstChild == null) {
1095 thisDomNode.appendChild(newNode);
1096 }
1097 else {
1098 firstChild.insertBefore(newNode);
1099 }
1100 }
1101 }
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111 protected static void replaceChildren(final Context context, final Scriptable thisObj, final Object[] args,
1112 final Function function) {
1113 if (!(thisObj instanceof Node thisNode)) {
1114 throw JavaScriptEngine.typeError("Illegal invocation");
1115 }
1116
1117 final DomNode thisDomNode = thisNode.getDomNodeOrDie();
1118 thisDomNode.removeAllChildren();
1119
1120 for (final Object arg : args) {
1121 final Node node = toNodeOrTextNode(thisNode, arg);
1122 thisDomNode.appendChild(node.getDomNodeOrDie());
1123 }
1124 }
1125
1126 private static Node toNodeOrTextNode(final Node thisObj, final Object obj) {
1127 if (obj instanceof Node node) {
1128 return node;
1129 }
1130 return (Node)
1131 ((HTMLDocument) thisObj.getOwnerDocument()).createTextNode(JavaScriptEngine.toString(obj));
1132 }
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142 protected static void before(final Context context, final Scriptable thisObj, final Object[] args,
1143 final Function function) {
1144 for (final Object arg : args) {
1145 final Node node = toNodeOrTextNode((Node) thisObj, arg);
1146 ((Node) thisObj).getDomNodeOrDie().insertBefore(node.getDomNodeOrDie());
1147 }
1148 }
1149
1150
1151
1152
1153
1154
1155
1156
1157 protected static void replaceWith(final Context context, final Scriptable thisObj, final Object[] args,
1158 final Function function) {
1159 final DomNode thisDomNode = ((Node) thisObj).getDomNodeOrDie();
1160 final DomNode parentNode = thisDomNode.getParentNode();
1161
1162 if (args.length == 0) {
1163 parentNode.removeChild(thisDomNode);
1164 return;
1165 }
1166
1167 final DomNode nextSibling = thisDomNode.getNextSibling();
1168 boolean isFirst = true;
1169 for (final Object arg : args) {
1170 final DomNode newNode = toNodeOrTextNode((Node) thisObj, arg).getDomNodeOrDie();
1171 if (isFirst) {
1172 isFirst = false;
1173 thisDomNode.replace(newNode);
1174 }
1175 else {
1176 if (nextSibling == null) {
1177 parentNode.appendChild(newNode);
1178 }
1179 else {
1180 nextSibling.insertBefore(newNode);
1181 }
1182 }
1183 }
1184 }
1185 }