nokolexbor 0.3.3 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/ext/nokolexbor/nl_attribute.c +201 -0
  3. data/ext/nokolexbor/nl_cdata.c +8 -0
  4. data/ext/nokolexbor/nl_comment.c +6 -0
  5. data/ext/nokolexbor/nl_document.c +53 -7
  6. data/ext/nokolexbor/nl_document_fragment.c +9 -0
  7. data/ext/nokolexbor/nl_error.c +21 -19
  8. data/ext/nokolexbor/nl_node.c +317 -48
  9. data/ext/nokolexbor/nl_node_set.c +56 -1
  10. data/ext/nokolexbor/nl_processing_instruction.c +6 -0
  11. data/ext/nokolexbor/nl_text.c +6 -0
  12. data/ext/nokolexbor/nokolexbor.c +1 -0
  13. data/ext/nokolexbor/nokolexbor.h +2 -0
  14. data/lib/nokolexbor/document.rb +52 -5
  15. data/lib/nokolexbor/document_fragment.rb +11 -0
  16. data/lib/nokolexbor/node.rb +370 -24
  17. data/lib/nokolexbor/node_set.rb +56 -0
  18. data/lib/nokolexbor/version.rb +1 -1
  19. data/lib/nokolexbor.rb +0 -1
  20. metadata +3 -25
  21. data/lib/nokolexbor/attribute.rb +0 -18
  22. data/vendor/lexbor/source/lexbor/encoding/base.h +0 -218
  23. data/vendor/lexbor/source/lexbor/encoding/big5.c +0 -42839
  24. data/vendor/lexbor/source/lexbor/encoding/config.cmake +0 -12
  25. data/vendor/lexbor/source/lexbor/encoding/const.h +0 -65
  26. data/vendor/lexbor/source/lexbor/encoding/decode.c +0 -3193
  27. data/vendor/lexbor/source/lexbor/encoding/decode.h +0 -370
  28. data/vendor/lexbor/source/lexbor/encoding/encode.c +0 -1931
  29. data/vendor/lexbor/source/lexbor/encoding/encode.h +0 -377
  30. data/vendor/lexbor/source/lexbor/encoding/encoding.c +0 -252
  31. data/vendor/lexbor/source/lexbor/encoding/encoding.h +0 -475
  32. data/vendor/lexbor/source/lexbor/encoding/euc_kr.c +0 -53883
  33. data/vendor/lexbor/source/lexbor/encoding/gb18030.c +0 -47905
  34. data/vendor/lexbor/source/lexbor/encoding/iso_2022_jp_katakana.c +0 -159
  35. data/vendor/lexbor/source/lexbor/encoding/jis0208.c +0 -22477
  36. data/vendor/lexbor/source/lexbor/encoding/jis0212.c +0 -15787
  37. data/vendor/lexbor/source/lexbor/encoding/multi.h +0 -53
  38. data/vendor/lexbor/source/lexbor/encoding/range.c +0 -71
  39. data/vendor/lexbor/source/lexbor/encoding/range.h +0 -34
  40. data/vendor/lexbor/source/lexbor/encoding/res.c +0 -222
  41. data/vendor/lexbor/source/lexbor/encoding/res.h +0 -34
  42. data/vendor/lexbor/source/lexbor/encoding/single.c +0 -13748
  43. data/vendor/lexbor/source/lexbor/encoding/single.h +0 -116
@@ -12,6 +12,7 @@ extern VALUE cNokolexborComment;
12
12
  extern VALUE cNokolexborProcessingInstruction;
13
13
  extern VALUE cNokolexborNodeSet;
14
14
  extern VALUE cNokolexborDocumentFragment;
15
+ extern VALUE cNokolexborAttribute;
15
16
  extern VALUE eLexborError;
16
17
  VALUE cNokolexborNode;
17
18
  VALUE cNokolexborElement;
@@ -31,8 +32,9 @@ nl_rb_node_create(lxb_dom_node_t *node, VALUE rb_document)
31
32
  case LXB_DOM_NODE_TYPE_ELEMENT:
32
33
  rb_class = cNokolexborElement;
33
34
  break;
34
- // case LXB_DOM_NODE_TYPE_ATTRIBUTE:
35
- // break;
35
+ case LXB_DOM_NODE_TYPE_ATTRIBUTE:
36
+ rb_class = cNokolexborAttribute;
37
+ break;
36
38
  case LXB_DOM_NODE_TYPE_TEXT:
37
39
  rb_class = cNokolexborText;
38
40
  break;
@@ -49,8 +51,11 @@ nl_rb_node_create(lxb_dom_node_t *node, VALUE rb_document)
49
51
  case LXB_DOM_NODE_TYPE_COMMENT:
50
52
  rb_class = cNokolexborComment;
51
53
  break;
52
- // case LXB_DOM_NODE_TYPE_DOCUMENT:
53
- // break;
54
+ case LXB_DOM_NODE_TYPE_DOCUMENT:
55
+ if (nl_rb_node_unwrap(rb_document) != node) {
56
+ rb_raise(rb_eRuntimeError, "Unexpected node type: Document");
57
+ }
58
+ return rb_document;
54
59
  // case LXB_DOM_NODE_TYPE_DOCUMENT_TYPE:
55
60
  // break;
56
61
  case LXB_DOM_NODE_TYPE_DOCUMENT_FRAGMENT:
@@ -79,6 +84,23 @@ nl_rb_node_unwrap(VALUE rb_node)
79
84
  return node;
80
85
  }
81
86
 
87
+ /**
88
+ * call-seq:
89
+ * new(name, document) { |Node| ... } -> Node
90
+ *
91
+ * Create a new node with +name+ that belongs to +document+.
92
+ *
93
+ * If you intend to add a node to a document tree, it's likely that you will prefer one of the
94
+ * {Node} methods like {#add_child}, {#add_next_sibling}, {#replace}, etc. which will
95
+ * both create an element (or subtree) and place it in the document tree.
96
+ *
97
+ * Another alternative, if you are concerned about performance, is
98
+ * {Document#create_element} which accepts additional arguments for contents or
99
+ * attributes but (like this method) avoids parsing markup.
100
+ *
101
+ * @param name [String]
102
+ * @param document [Document] The document to which the the returned node will belong.
103
+ */
82
104
  static VALUE
83
105
  nl_node_new(int argc, VALUE *argv, VALUE klass)
84
106
  {
@@ -111,6 +133,68 @@ nl_node_new(int argc, VALUE *argv, VALUE klass)
111
133
  return rb_node;
112
134
  }
113
135
 
136
+ /**
137
+ * call-seq: attribute(name) → Attribute
138
+ *
139
+ * @return [Attribute] The attribute belonging to this node with name +name+.
140
+ */
141
+ static VALUE
142
+ nl_node_attribute(VALUE self, VALUE rb_name)
143
+ {
144
+ lxb_dom_node_t *node = nl_rb_node_unwrap(self);
145
+
146
+ const char *c_name = StringValuePtr(rb_name);
147
+ size_t name_len = RSTRING_LEN(rb_name);
148
+
149
+ if (node->type != LXB_DOM_NODE_TYPE_ELEMENT) {
150
+ return Qnil;
151
+ }
152
+
153
+ lxb_dom_attr_t *attr = lxb_dom_element_attr_by_name(lxb_dom_interface_element(node), (const lxb_char_t *)c_name, name_len);
154
+ if (attr == NULL) {
155
+ return Qnil;
156
+ }
157
+ if (attr->owner == NULL) {
158
+ attr->owner = lxb_dom_interface_element(node);
159
+ }
160
+ return nl_rb_node_create(attr, nl_rb_document_get(self));
161
+ }
162
+
163
+ /**
164
+ * @return [Array<Attribute>] An array of {Attribute} belonging to this node.
165
+ */
166
+ static VALUE
167
+ nl_node_attribute_nodes(VALUE self)
168
+ {
169
+ lxb_dom_node_t *node = nl_rb_node_unwrap(self);
170
+ VALUE ary = rb_ary_new();
171
+ if (node->type != LXB_DOM_NODE_TYPE_ELEMENT) {
172
+ return ary;
173
+ }
174
+
175
+ lxb_dom_attr_t *attr = lxb_dom_element_first_attribute(lxb_dom_interface_element(node));
176
+
177
+ if (attr == NULL) {
178
+ return ary;
179
+ }
180
+
181
+ VALUE rb_doc = nl_rb_document_get(self);
182
+ while (attr != NULL) {
183
+ if (attr->owner == NULL) {
184
+ attr->owner = lxb_dom_interface_element(node);
185
+ }
186
+ rb_ary_push(ary, nl_rb_node_create(attr, rb_doc));
187
+ attr = attr->next;
188
+ }
189
+
190
+ return ary;
191
+ }
192
+
193
+ /**
194
+ * @return [String]
195
+ * Contents of all the text nodes in this node's subtree, concatenated together into a single
196
+ * String.
197
+ */
114
198
  static VALUE
115
199
  nl_node_content(VALUE self)
116
200
  {
@@ -127,6 +211,12 @@ nl_node_content(VALUE self)
127
211
  return rb_str;
128
212
  }
129
213
 
214
+ /**
215
+ * Set the Node's content to a Text node containing +content+. The string gets XML escaped, not
216
+ * interpreted as markup.
217
+ *
218
+ * @return [String] +content+
219
+ */
130
220
  static VALUE
131
221
  nl_node_content_set(VALUE self, VALUE content)
132
222
  {
@@ -141,6 +231,15 @@ nl_node_content_set(VALUE self, VALUE content)
141
231
  return content;
142
232
  }
143
233
 
234
+ /**
235
+ * call-seq: [](name) -> String,nil
236
+ *
237
+ * Fetch an attribute from this node.
238
+ *
239
+ * @param name The name of the attribute.
240
+ *
241
+ * @return (String, nil) The value of the attribute +name+, or +nil+ if no matching attribute exists.
242
+ */
144
243
  static VALUE
145
244
  nl_node_get_attr(VALUE self, VALUE rb_attr)
146
245
  {
@@ -166,6 +265,16 @@ nl_node_get_attr(VALUE self, VALUE rb_attr)
166
265
  return rb_utf8_str_new((const char *)attr_value, attr_value_len);
167
266
  }
168
267
 
268
+ /**
269
+ * call-seq: []=(name, value) -> String,nil
270
+ *
271
+ * Update the attribute +name+ to +value+, or create the attribute if it does not exist.
272
+ *
273
+ * @param name The name of the attribute.
274
+ * @param value The value of the attribute.
275
+ *
276
+ * @return [String] +value+
277
+ */
169
278
  static VALUE
170
279
  nl_node_set_attr(VALUE self, VALUE rb_attr, VALUE rb_value)
171
280
  {
@@ -190,13 +299,22 @@ nl_node_set_attr(VALUE self, VALUE rb_attr, VALUE rb_value)
190
299
  return rb_value;
191
300
  }
192
301
 
302
+ /**
303
+ * call-seq: remove_attr(name) -> Boolean
304
+ *
305
+ * Remove the attribute named +name+.
306
+ *
307
+ * @param name [String]
308
+ *
309
+ * @return [Boolean] +true+ if removal success, +false+ if node is not an {Element}.
310
+ */
193
311
  static VALUE
194
312
  nl_node_remove_attr(VALUE self, VALUE rb_attr)
195
313
  {
196
314
  lxb_dom_node_t *node = nl_rb_node_unwrap(self);
197
315
 
198
316
  if (node->type != LXB_DOM_NODE_TYPE_ELEMENT) {
199
- return Qnil;
317
+ return Qfalse;
200
318
  }
201
319
 
202
320
  VALUE rb_attr_s = rb_String(rb_attr);
@@ -206,7 +324,12 @@ nl_node_remove_attr(VALUE self, VALUE rb_attr)
206
324
 
207
325
  lxb_dom_element_t *element = lxb_dom_interface_element(node);
208
326
 
209
- return lxb_dom_element_remove_attribute(element, (const lxb_char_t *)attr_c, attr_len) == LXB_STATUS_OK ? Qtrue : Qfalse;
327
+ lxb_status_t status = lxb_dom_element_remove_attribute(element, (const lxb_char_t *)attr_c, attr_len);
328
+ if (status != LXB_STATUS_OK) {
329
+ nl_raise_lexbor_error(status);
330
+ }
331
+
332
+ return Qtrue;
210
333
  }
211
334
 
212
335
  lxb_status_t
@@ -333,6 +456,11 @@ void nl_sort_nodes_if_necessary(VALUE selector, lxb_dom_document_t *doc, lexbor_
333
456
  }
334
457
  }
335
458
 
459
+ /**
460
+ * Internal implementation of {#at_css}
461
+ *
462
+ * @see #at_css
463
+ */
336
464
  static VALUE
337
465
  nl_node_at_css(VALUE self, VALUE selector)
338
466
  {
@@ -360,6 +488,11 @@ nl_node_at_css(VALUE self, VALUE selector)
360
488
  return ret;
361
489
  }
362
490
 
491
+ /**
492
+ * Internal implementation of {#css}
493
+ *
494
+ * @see #css
495
+ */
363
496
  static VALUE
364
497
  nl_node_css(VALUE self, VALUE selector)
365
498
  {
@@ -377,6 +510,11 @@ nl_node_css(VALUE self, VALUE selector)
377
510
  return nl_rb_node_set_create_with_data(array, nl_rb_document_get(self));
378
511
  }
379
512
 
513
+ /**
514
+ * Get the inner_html of this Node.
515
+ *
516
+ * @return [String]
517
+ */
380
518
  static VALUE
381
519
  nl_node_inner_html(int argc, VALUE *argv, VALUE self)
382
520
  {
@@ -414,6 +552,11 @@ nl_node_inner_html(int argc, VALUE *argv, VALUE self)
414
552
  return Qnil;
415
553
  }
416
554
 
555
+ /**
556
+ * Serialize this Node to HTML, also known as outer_html.
557
+ *
558
+ * @return [String]
559
+ */
417
560
  static VALUE
418
561
  nl_node_outer_html(int argc, VALUE *argv, VALUE self)
419
562
  {
@@ -451,6 +594,11 @@ nl_node_outer_html(int argc, VALUE *argv, VALUE self)
451
594
  return Qnil;
452
595
  }
453
596
 
597
+ /**
598
+ * call-seq: key?(name) -> Boolean
599
+ *
600
+ * @return true if +name+ is set.
601
+ */
454
602
  static VALUE
455
603
  nl_node_has_key(VALUE self, VALUE rb_attr)
456
604
  {
@@ -469,6 +617,11 @@ nl_node_has_key(VALUE self, VALUE rb_attr)
469
617
  return lxb_dom_element_has_attribute(element, (const lxb_char_t *)attr_c, attr_len) ? Qtrue : Qfalse;
470
618
  }
471
619
 
620
+ /**
621
+ * Get the attribute names of this Node.
622
+ *
623
+ * @return [Array<String>] An array of attribute names.
624
+ */
472
625
  static VALUE
473
626
  nl_node_keys(VALUE self)
474
627
  {
@@ -492,6 +645,11 @@ nl_node_keys(VALUE self)
492
645
  return ary_keys;
493
646
  }
494
647
 
648
+ /**
649
+ * Get the attribute values of this Node.
650
+ *
651
+ * @return [Array<String>] An array of attribute values.
652
+ */
495
653
  static VALUE
496
654
  nl_node_values(VALUE self)
497
655
  {
@@ -519,6 +677,11 @@ nl_node_values(VALUE self)
519
677
  return ary_values;
520
678
  }
521
679
 
680
+ /**
681
+ * Get a hash of attribute names and values of this Node.
682
+ *
683
+ * @return [Hash{String => String}] A hash whose keys are attribute names and values are attribute values.
684
+ */
522
685
  static VALUE
523
686
  nl_node_attrs(VALUE self)
524
687
  {
@@ -547,6 +710,11 @@ nl_node_attrs(VALUE self)
547
710
  return rb_hash;
548
711
  }
549
712
 
713
+ /**
714
+ * Get the parent node.
715
+ *
716
+ * @return [Node,nil] The parent node
717
+ */
550
718
  static VALUE
551
719
  nl_node_parent(VALUE self)
552
720
  {
@@ -554,6 +722,11 @@ nl_node_parent(VALUE self)
554
722
  return node->parent ? nl_rb_node_create(node->parent, nl_rb_document_get(self)) : Qnil;
555
723
  }
556
724
 
725
+ /**
726
+ * Get the previous sibling node.
727
+ *
728
+ * @return [Node,nil] The previous sibling node
729
+ */
557
730
  static VALUE
558
731
  nl_node_previous(VALUE self)
559
732
  {
@@ -561,6 +734,11 @@ nl_node_previous(VALUE self)
561
734
  return node->prev ? nl_rb_node_create(node->prev, nl_rb_document_get(self)) : Qnil;
562
735
  }
563
736
 
737
+ /**
738
+ * Get the previous sibling element.
739
+ *
740
+ * @return [Element,nil] The previous sibling element
741
+ */
564
742
  static VALUE
565
743
  nl_node_previous_element(VALUE self)
566
744
  {
@@ -574,6 +752,11 @@ nl_node_previous_element(VALUE self)
574
752
  return Qnil;
575
753
  }
576
754
 
755
+ /**
756
+ * Get the next sibling node.
757
+ *
758
+ * @return [Node,nil] The previous sibling node
759
+ */
577
760
  static VALUE
578
761
  nl_node_next(VALUE self)
579
762
  {
@@ -581,6 +764,11 @@ nl_node_next(VALUE self)
581
764
  return node->next ? nl_rb_node_create(node->next, nl_rb_document_get(self)) : Qnil;
582
765
  }
583
766
 
767
+ /**
768
+ * Get the next sibling element.
769
+ *
770
+ * @return [Element,nil] The previous sibling element
771
+ */
584
772
  static VALUE
585
773
  nl_node_next_element(VALUE self)
586
774
  {
@@ -594,6 +782,11 @@ nl_node_next_element(VALUE self)
594
782
  return Qnil;
595
783
  }
596
784
 
785
+ /**
786
+ * Get the children of this node.
787
+ *
788
+ * @return [NodeSet] The set of this node's children.
789
+ */
597
790
  static VALUE
598
791
  nl_node_children(VALUE self)
599
792
  {
@@ -609,6 +802,11 @@ nl_node_children(VALUE self)
609
802
  return nl_rb_node_set_create_with_data(array, nl_rb_document_get(self));
610
803
  }
611
804
 
805
+ /**
806
+ * Get the first child of this node.
807
+ *
808
+ * @return [Node,nil] The first child.
809
+ */
612
810
  static VALUE
613
811
  nl_node_child(VALUE self)
614
812
  {
@@ -617,14 +815,26 @@ nl_node_child(VALUE self)
617
815
  return child ? nl_rb_node_create(child, nl_rb_document_get(self)) : Qnil;
618
816
  }
619
817
 
818
+ /**
819
+ * Remove this node from its current context.
820
+ *
821
+ * @return [Node] +self+, to support chaining of calls.
822
+ */
620
823
  static VALUE
621
824
  nl_node_remove(VALUE self)
622
825
  {
623
826
  lxb_dom_node_t *node = nl_rb_node_unwrap(self);
624
827
  lxb_dom_node_remove(node);
625
- return Qnil;
828
+ return self;
626
829
  }
627
830
 
831
+ /**
832
+ * Remove this node from its current context and free its allocated memory.
833
+ *
834
+ * @return [nil]
835
+ *
836
+ * @see #remove
837
+ */
628
838
  static VALUE
629
839
  nl_node_destroy(VALUE self)
630
840
  {
@@ -633,6 +843,9 @@ nl_node_destroy(VALUE self)
633
843
  return Qnil;
634
844
  }
635
845
 
846
+ /**
847
+ * @return [Boolean] true if this Node is equal to +other+.
848
+ */
636
849
  static VALUE
637
850
  nl_node_equals(VALUE self, VALUE other)
638
851
  {
@@ -651,6 +864,11 @@ lxb_dom_node_name_qualified(lxb_dom_node_t *node, size_t *len)
651
864
  return lxb_dom_node_name(node, len);
652
865
  }
653
866
 
867
+ /**
868
+ * Get the name of this Node
869
+ *
870
+ * @return [String] The name of this Node
871
+ */
654
872
  static VALUE
655
873
  nl_node_name(VALUE self)
656
874
  {
@@ -682,6 +900,13 @@ nl_node_parse_fragment(lxb_dom_document_t *doc, lxb_dom_element_t *element, lxb_
682
900
  return frag_root;
683
901
  }
684
902
 
903
+ /**
904
+ * Parse +html+ as a document fragment within the context of
905
+ * *this* node.
906
+ *
907
+ * @param html [String] The fragment to be parsed.
908
+ * @return [NodeSet] The {NodeSet} containing the parsed nodes.
909
+ */
685
910
  static VALUE
686
911
  nl_node_parse(VALUE self, VALUE html)
687
912
  {
@@ -701,38 +926,48 @@ nl_node_parse(VALUE self, VALUE html)
701
926
  return nl_rb_node_set_create_with_data(array, nl_rb_document_get(self));
702
927
  }
703
928
 
929
+ typedef void (*lxb_dom_node_add_nodes_to_f)(lxb_dom_node_t *, lxb_dom_node_t *);
930
+
704
931
  static VALUE
705
- nl_node_add_sibling(VALUE self, VALUE next_or_previous, VALUE new)
932
+ nl_node_add_nodes(VALUE self, VALUE new, lxb_dom_node_add_nodes_to_f add_to, bool operate_on_new_node)
706
933
  {
707
934
  lxb_dom_node_t *node = nl_rb_node_unwrap(self);
708
935
  lxb_dom_document_t *doc = node->owner_document;
709
936
 
710
- int insert_after;
711
- if (rb_eql(rb_String(next_or_previous), rb_str_new_literal("next"))) {
712
- insert_after = 1;
713
- } else if (rb_eql(rb_String(next_or_previous), rb_str_new_literal("previous"))) {
714
- insert_after = 0;
715
- } else {
716
- rb_raise(rb_eArgError, "Unsupported inserting position");
717
- }
718
-
719
- if (TYPE(new) == T_STRING) {
720
- lxb_dom_node_t *frag_root = nl_node_parse_fragment(doc, NULL, (lxb_char_t *)RSTRING_PTR(new), RSTRING_LEN(new));
937
+ if (TYPE(new) == T_STRING || rb_obj_is_kind_of(new, cNokolexborDocumentFragment)) {
938
+ lxb_dom_node_t *frag_root = (TYPE(new) == T_STRING) ? nl_node_parse_fragment(doc, NULL, (lxb_char_t *)RSTRING_PTR(new), RSTRING_LEN(new))
939
+ : nl_rb_node_unwrap(new);
721
940
  lexbor_array_t *array = lexbor_array_create();
722
941
 
942
+ lxb_dom_node_t *last_node = node;
723
943
  while (frag_root->first_child != NULL) {
724
944
  lxb_dom_node_t *child = frag_root->first_child;
725
945
  lxb_dom_node_remove(child);
726
- insert_after ? lxb_dom_node_insert_after(node, child) : lxb_dom_node_insert_before(node, child);
946
+ operate_on_new_node ? add_to(last_node, child) : add_to(node, child);
947
+ last_node = child;
727
948
  lexbor_array_push(array, child);
728
949
  }
729
- lxb_dom_node_destroy(frag_root);
950
+ if (TYPE(new) == T_STRING) {
951
+ lxb_dom_node_destroy(frag_root);
952
+ }
730
953
  return nl_rb_node_set_create_with_data(array, nl_rb_document_get(self));
731
954
 
955
+ } else if (rb_obj_is_kind_of(new, cNokolexborNodeSet)) {
956
+ lexbor_array_t *node_array = nl_rb_node_set_unwrap(new);
957
+
958
+ lxb_dom_node_t *last_node = node;
959
+ for (size_t i = 0; i < node_array->length; i++) {
960
+ lxb_dom_node_t *child = (lxb_dom_node_t *)node_array->list[i];
961
+ lxb_dom_node_remove(child);
962
+ operate_on_new_node ? add_to(last_node, child) : add_to(node, child);
963
+ last_node = child;
964
+ }
965
+ return new;
966
+
732
967
  } else if (rb_obj_is_kind_of(new, cNokolexborNode)) {
733
968
  lxb_dom_node_t *node_new = nl_rb_node_unwrap(new);
734
969
  lxb_dom_node_remove(node_new);
735
- insert_after ? lxb_dom_node_insert_after(node, node_new) : lxb_dom_node_insert_before(node, node_new);
970
+ add_to(node, node_new);
736
971
  return new;
737
972
 
738
973
  } else {
@@ -741,43 +976,57 @@ nl_node_add_sibling(VALUE self, VALUE next_or_previous, VALUE new)
741
976
  return Qnil;
742
977
  }
743
978
 
979
+ /**
980
+ * Insert +node_or_tags+ before or after this Node (as a sibling).
981
+ *
982
+ * @see #add_previous_sibling
983
+ * @see #add_next_sibling
984
+ *
985
+ * @return [Node,NodeSet] The reparented {Node} (if +node_or_tags+ is a {Node}), or {NodeSet} (if +node_or_tags+ is a {DocumentFragment}, {NodeSet}, or {String}).
986
+ */
744
987
  static VALUE
745
- nl_node_add_child(VALUE self, VALUE new)
988
+ nl_node_add_sibling(VALUE self, VALUE next_or_previous, VALUE new)
746
989
  {
747
- lxb_dom_node_t *node = nl_rb_node_unwrap(self);
748
- lxb_dom_document_t *doc = node->owner_document;
749
-
750
- if (TYPE(new) == T_STRING) {
751
- lxb_dom_node_t *frag_root = nl_node_parse_fragment(doc, NULL, (lxb_char_t *)RSTRING_PTR(new), RSTRING_LEN(new));
752
- lexbor_array_t *array = lexbor_array_create();
753
-
754
- while (frag_root->first_child != NULL) {
755
- lxb_dom_node_t *child = frag_root->first_child;
756
- lxb_dom_node_remove(child);
757
- lxb_dom_node_insert_child(node, child);
758
- lexbor_array_push(array, child);
759
- }
760
- lxb_dom_node_destroy(frag_root);
761
- return nl_rb_node_set_create_with_data(array, nl_rb_document_get(self));
762
-
763
- } else if (rb_obj_is_kind_of(new, cNokolexborNode)) {
764
- lxb_dom_node_t *node_new = nl_rb_node_unwrap(new);
765
- lxb_dom_node_remove(node_new);
766
- lxb_dom_node_insert_child(node, node_new);
767
- return new;
768
-
990
+ bool insert_after;
991
+ if (rb_eql(rb_String(next_or_previous), rb_str_new_literal("next"))) {
992
+ insert_after = true;
993
+ } else if (rb_eql(rb_String(next_or_previous), rb_str_new_literal("previous"))) {
994
+ insert_after = false;
769
995
  } else {
770
- rb_raise(rb_eArgError, "Unsupported node type");
996
+ rb_raise(rb_eArgError, "Unsupported inserting position");
771
997
  }
772
- return Qnil;
998
+
999
+ return insert_after ? nl_node_add_nodes(self, new, lxb_dom_node_insert_after, true)
1000
+ : nl_node_add_nodes(self, new, lxb_dom_node_insert_before, false);
773
1001
  }
774
1002
 
1003
+ /**
1004
+ * Add +new+ as a child of this Node.
1005
+ *
1006
+ * @param new [Node, DocumentFragment, NodeSet, String] The node to be added.
1007
+ *
1008
+ * @return [Node,NodeSet] The reparented {Node} (if +new+ is a {Node}), or {NodeSet} (if +new+ is a {DocumentFragment}, {NodeSet}, or {String}).
1009
+ */
1010
+ static VALUE
1011
+ nl_node_add_child(VALUE self, VALUE new)
1012
+ {
1013
+ return nl_node_add_nodes(self, new, lxb_dom_node_insert_child, false);
1014
+ }
1015
+
1016
+ /**
1017
+ * Get the type of this Node
1018
+ *
1019
+ * @return {Integer}
1020
+ */
775
1021
  static VALUE
776
1022
  nl_node_get_type(VALUE self)
777
1023
  {
778
1024
  return INT2NUM(nl_rb_node_unwrap(self)->type);
779
1025
  }
780
1026
 
1027
+ /**
1028
+ * @return [Element] The first child Node that is an element.
1029
+ */
781
1030
  static VALUE
782
1031
  nl_node_first_element_child(VALUE self)
783
1032
  {
@@ -805,6 +1054,9 @@ nl_node_first_element_child(VALUE self)
805
1054
  return Qnil;
806
1055
  }
807
1056
 
1057
+ /**
1058
+ * @return [Element] The last child Node that is an element.
1059
+ */
808
1060
  static VALUE
809
1061
  nl_node_last_element_child(VALUE self)
810
1062
  {
@@ -832,11 +1084,26 @@ nl_node_last_element_child(VALUE self)
832
1084
  return Qnil;
833
1085
  }
834
1086
 
1087
+ /**
1088
+ * Copy this node.
1089
+ *
1090
+ * @return [Node] The new {Node}.
1091
+ */
835
1092
  static VALUE
836
1093
  nl_node_clone(VALUE self)
837
1094
  {
838
1095
  lxb_dom_node_t *node = nl_rb_node_unwrap(self);
839
- lxb_dom_node_t *clone = lxb_dom_node_clone(node, 1);
1096
+ lxb_dom_node_t *clone;
1097
+
1098
+ switch (node->type) {
1099
+ case LXB_DOM_NODE_TYPE_ATTRIBUTE:
1100
+ clone = (lxb_dom_node_t *)lxb_dom_attr_interface_clone(node->owner_document, lxb_dom_interface_attr(node));
1101
+ case LXB_DOM_NODE_TYPE_CDATA_SECTION:
1102
+ clone = (lxb_dom_node_t *)lxb_dom_cdata_section_interface_clone(node->owner_document, lxb_dom_interface_cdata_section(node));
1103
+ default:
1104
+ clone = lxb_dom_node_clone(node, true);
1105
+ break;
1106
+ }
840
1107
  return nl_rb_node_create(clone, nl_rb_document_get(self));
841
1108
  }
842
1109
 
@@ -849,6 +1116,8 @@ void Init_nl_node(void)
849
1116
  cNokolexborCharacterData = rb_define_class_under(mNokolexbor, "CharacterData", cNokolexborNode);
850
1117
 
851
1118
  rb_define_singleton_method(cNokolexborNode, "new", nl_node_new, -1);
1119
+ rb_define_method(cNokolexborNode, "attribute", nl_node_attribute, 1);
1120
+ rb_define_method(cNokolexborNode, "attribute_nodes", nl_node_attribute_nodes, 0);
852
1121
  rb_define_method(cNokolexborNode, "content", nl_node_content, 0);
853
1122
  rb_define_method(cNokolexborNode, "content=", nl_node_content_set, 1);
854
1123
  rb_define_method(cNokolexborNode, "[]", nl_node_get_attr, 1);