@@ -337,65 +337,72 @@ pub const Node = struct {
337337 // For now, it checks only if new nodes are not self.
338338 // TODO implements the others contraints.
339339 // see https://dom.spec.whatwg.org/#concept-node-tree
340- pub fn hierarchy (self : * parser.Node , nodes : []const * parser.Node ) ! bool {
341- if (nodes .len == 0 ) return true ;
342-
343- for (nodes ) | node | if (self == node ) return false ;
344-
340+ pub fn hierarchy (self : * parser.Node , nodes : []const NodeOrText ) bool {
341+ for (nodes ) | n | {
342+ if (n .is (self )) {
343+ return false ;
344+ }
345+ }
345346 return true ;
346347 }
347348
348- // TODO according with https://dom.spec.whatwg.org/#parentnode, the
349- // function must accept either node or string.
350- // blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114
351- pub fn prepend (self : * parser.Node , nodes : []const * parser.Node ) ! void {
352- if (nodes .len == 0 ) return ;
349+ pub fn prepend (self : * parser.Node , nodes : []const NodeOrText ) ! void {
350+ if (nodes .len == 0 ) {
351+ return ;
352+ }
353353
354354 // check hierarchy
355- if (! try hierarchy (self , nodes )) return parser .DOMError .HierarchyRequest ;
355+ if (! hierarchy (self , nodes )) {
356+ return parser .DOMError .HierarchyRequest ;
357+ }
358+
359+ const doc = (try parser .nodeOwnerDocument (self )) orelse return ;
356360
357- const first = try parser .nodeFirstChild (self );
358- if (first == null ) {
361+ if (try parser .nodeFirstChild (self )) | first | {
359362 for (nodes ) | node | {
360- _ = try parser .nodeAppendChild (self , node );
363+ _ = try parser .nodeInsertBefore (self , try node . toNode ( doc ), first );
361364 }
362365 return ;
363366 }
364367
365368 for (nodes ) | node | {
366- _ = try parser .nodeInsertBefore (self , node , first .? );
369+ _ = try parser .nodeAppendChild (self , try node . toNode ( doc ) );
367370 }
368371 }
369372
370- // TODO according with https://dom.spec.whatwg.org/#parentnode, the
371- // function must accept either node or string.
372- // blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114
373- pub fn append (self : * parser.Node , nodes : []const * parser.Node ) ! void {
374- if (nodes .len == 0 ) return ;
373+ pub fn append (self : * parser.Node , nodes : []const NodeOrText ) ! void {
374+ if (nodes .len == 0 ) {
375+ return ;
376+ }
375377
376378 // check hierarchy
377- if (! try hierarchy (self , nodes )) return parser .DOMError .HierarchyRequest ;
379+ if (! hierarchy (self , nodes )) {
380+ return parser .DOMError .HierarchyRequest ;
381+ }
378382
383+ const doc = (try parser .nodeOwnerDocument (self )) orelse return ;
379384 for (nodes ) | node | {
380- _ = try parser .nodeAppendChild (self , node );
385+ _ = try parser .nodeAppendChild (self , try node . toNode ( doc ) );
381386 }
382387 }
383388
384- // TODO according with https://dom.spec.whatwg.org/#parentnode, the
385- // function must accept either node or string.
386- // blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114
387- pub fn replaceChildren (self : * parser.Node , nodes : []const * parser.Node ) ! void {
388- if (nodes .len == 0 ) return ;
389+ pub fn replaceChildren (self : * parser.Node , nodes : []const NodeOrText ) ! void {
390+ if (nodes .len == 0 ) {
391+ return ;
392+ }
389393
390394 // check hierarchy
391- if (! try hierarchy (self , nodes )) return parser .DOMError .HierarchyRequest ;
395+ if (! hierarchy (self , nodes )) {
396+ return parser .DOMError .HierarchyRequest ;
397+ }
392398
393399 // remove existing children
394400 try removeChildren (self );
395401
402+ const doc = (try parser .nodeOwnerDocument (self )) orelse return ;
396403 // add new children
397404 for (nodes ) | node | {
398- _ = try parser .nodeAppendChild (self , node );
405+ _ = try parser .nodeAppendChild (self , try node . toNode ( doc ) );
399406 }
400407 }
401408
@@ -414,7 +421,87 @@ pub const Node = struct {
414421 }
415422 }
416423
417- pub fn deinit (_ : * parser.Node , _ : std.mem.Allocator ) void {}
424+ pub fn before (self : * parser.Node , nodes : []const NodeOrText ) ! void {
425+ const parent = try parser .nodeParentNode (self ) orelse return ;
426+ const doc = (try parser .nodeOwnerDocument (parent )) orelse return ;
427+
428+ var sibling : ? * parser.Node = self ;
429+ // have to find the first sibling that isn't in nodes
430+ CHECK : while (sibling ) | s | {
431+ for (nodes ) | n | {
432+ if (n .is (s )) {
433+ sibling = try parser .nodePreviousSibling (s );
434+ continue :CHECK ;
435+ }
436+ }
437+ break ;
438+ }
439+
440+ if (sibling == null ) {
441+ sibling = try parser .nodeFirstChild (parent );
442+ }
443+
444+ if (sibling ) | ref_node | {
445+ for (nodes ) | node | {
446+ _ = try parser .nodeInsertBefore (parent , try node .toNode (doc ), ref_node );
447+ }
448+ return ;
449+ }
450+
451+ return Node .prepend (self , nodes );
452+ }
453+
454+ pub fn after (self : * parser.Node , nodes : []const NodeOrText ) ! void {
455+ const parent = try parser .nodeParentNode (self ) orelse return ;
456+ const doc = (try parser .nodeOwnerDocument (parent )) orelse return ;
457+
458+ // have to find the first sibling that isn't in nodes
459+ var sibling = try parser .nodeNextSibling (self );
460+ CHECK : while (sibling ) | s | {
461+ for (nodes ) | n | {
462+ if (n .is (s )) {
463+ sibling = try parser .nodeNextSibling (s );
464+ continue :CHECK ;
465+ }
466+ }
467+ break ;
468+ }
469+
470+ if (sibling ) | ref_node | {
471+ for (nodes ) | node | {
472+ _ = try parser .nodeInsertBefore (parent , try node .toNode (doc ), ref_node );
473+ }
474+ return ;
475+ }
476+
477+ for (nodes ) | node | {
478+ _ = try parser .nodeAppendChild (parent , try node .toNode (doc ));
479+ }
480+ }
481+
482+ // A lot of functions take either a node or text input.
483+ // The text input is to be converted into a Text node.
484+ pub const NodeOrText = union (enum ) {
485+ text : []const u8 ,
486+ node : * parser.Node ,
487+
488+ fn toNode (self : NodeOrText , doc : * parser.Document ) ! * parser.Node {
489+ return switch (self ) {
490+ .node = > | n | n ,
491+ .text = > | txt | @ptrCast (try parser .documentCreateTextNode (doc , txt )),
492+ };
493+ }
494+
495+ // Whether the node represented by the NodeOrText is the same as the
496+ // given Node. Always false for text values as these represent as-of-yet
497+ // created Text nodes.
498+ fn is (self : NodeOrText , other : * parser.Node ) bool {
499+ return switch (self ) {
500+ .text = > false ,
501+ .node = > | n | n == other ,
502+ };
503+ }
504+ };
418505};
419506
420507const testing = @import ("../../testing.zig" );
0 commit comments