nokolexbor 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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);