Class: Oga::XML::Element

Inherits:
Node
  • Object
show all
Includes:
Querying
Defined in:
lib/oga/xml/element.rb

Overview

Class that contains information about an XML element such as the name, attributes and child nodes.

Constant Summary collapse

XMLNS_PREFIX =

The attribute prefix/namespace used for registering element namespaces.

Returns:

  • (String)
'xmlns'.freeze

Instance Attribute Summary collapse

Attributes inherited from Node

#node_set

Instance Method Summary collapse

Methods included from Querying

#at_css, #at_xpath, #css, #xpath

Methods inherited from Node

#after, #before, #children, #children=, #next, #next_element, #parent, #previous, #previous_element, #remove, #root_node

Methods included from Traversal

#each_node

Constructor Details

#initialize(options = {}) ⇒ Element

Returns a new instance of Element

Parameters:

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :name (String)

    The name of the element.

  • :namespace_name (String)

    The name of the namespace.

  • :attributes (Array<Oga::XML::Attribute>)

    The attributes of the element as an Array.


45
46
47
48
49
50
51
52
53
54
55
# File 'lib/oga/xml/element.rb', line 45

def initialize(options = {})
  super

  @name           = options[:name]
  @namespace_name = options[:namespace_name]
  @attributes     = options[:attributes] || []
  @namespaces     = options[:namespaces] || {}

  link_attributes
  register_namespaces_from_attributes
end

Instance Attribute Details

#attributesArray<Oga::XML::Attribute>

The attributes of the element.

Returns:


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/oga/xml/element.rb', line 23

class Element < Node
  include Querying

  attr_accessor :name, :namespace_name, :attributes, :namespaces

  ##
  # The attribute prefix/namespace used for registering element namespaces.
  #
  # @return [String]
  #
  XMLNS_PREFIX = 'xmlns'.freeze

  ##
  # @param [Hash] options
  #
  # @option options [String] :name The name of the element.
  #
  # @option options [String] :namespace_name The name of the namespace.
  #
  # @option options [Array<Oga::XML::Attribute>] :attributes The attributes
  #  of the element as an Array.
  #
  def initialize(options = {})
    super

    @name           = options[:name]
    @namespace_name = options[:namespace_name]
    @attributes     = options[:attributes] || []
    @namespaces     = options[:namespaces] || {}

    link_attributes
    register_namespaces_from_attributes
  end

  ##
  # Returns an attribute matching the given name (with or without the
  # namespace).
  #
  # @example
  #  # find an attribute that only has the name "foo"
  #  attribute('foo')
  #
  #  # find an attribute with namespace "foo" and name bar"
  #  attribute('foo:bar')
  #
  # @param [String|Symbol] name The name (with or without the namespace)
  #  of the attribute.
  #
  # @return [Oga::XML::Attribute]
  #
  def attribute(name)
    name, ns = split_name(name)

    attributes.each do |attr|
      return attr if attribute_matches?(attr, ns, name)
    end

    return
  end

  alias_method :attr, :attribute

  ##
  # Returns the value of the given attribute.
  #
  # @example
  #  element.get('class') # => "container"
  #
  # @see [#attribute]
  #
  def get(name)
    found = attribute(name)

    return found ? found.value : nil
  end

  ##
  # Adds a new attribute to the element.
  #
  # @param [Oga::XML::Attribute] attribute
  #
  def add_attribute(attribute)
    attribute.element = self

    attributes << attribute
  end

  ##
  # Sets the value of an attribute to the given value. If the attribute does
  # not exist it is created automatically.
  #
  # @param [String] name The name of the attribute, optionally including the
  #  namespace.
  #
  # @param [String] value The new value of the attribute.
  #
  def set(name, value)
    found = attribute(name)

    if found
      found.value = value
    else
      if name.include?(':')
        ns, name = name.split(':')
      else
        ns = nil
      end

      attr = Attribute.new(
        :name           => name,
        :namespace_name => ns,
        :value          => value
      )

      add_attribute(attr)
    end
  end

  ##
  # Removes an attribute from the element.
  #
  # @param [String] name The name (optionally including namespace prefix)
  #  of the attribute to remove.
  #
  # @return [Oga::XML::Attribute]
  #
  def unset(name)
    found = attribute(name)

    return attributes.delete(found) if found
  end

  ##
  # Returns the namespace of the element.
  #
  # @return [Oga::XML::Namespace]
  #
  def namespace
    unless @namespace
      available  = available_namespaces
      @namespace = available[namespace_name] || available[XMLNS_PREFIX]
    end

    return @namespace
  end

  ##
  # Returns the text of all child nodes joined together.
  #
  # @return [String]
  #
  def text
    return children.text
  end

  ##
  # Returns the text of the current element only.
  #
  # @return [String]
  #
  def inner_text
    text = ''

    text_nodes.each do |node|
      text << node.text
    end

    return text
  end

  ##
  # Returns any {Oga::XML::Text} nodes that are a direct child of this
  # element.
  #
  # @return [Oga::XML::NodeSet]
  #
  def text_nodes
    nodes = NodeSet.new

    children.each do |child|
      nodes << child if child.is_a?(Text)
    end

    return nodes
  end

  ##
  # Sets the inner text of the current element to the given String.
  #
  # @param [String] text
  #
  def inner_text=(text)
    text_node = XML::Text.new(:text => text)
    @children = NodeSet.new([text_node])
  end

  ##
  # Converts the element and its child elements to XML.
  #
  # @return [String]
  #
  def to_xml
    if namespace_name
      full_name = "#{namespace_name}:#{name}"
    else
      full_name = name
    end

    body  = children.map(&:to_xml).join('')
    attrs = ''

    attributes.each do |attr|
      attrs << " #{attr.to_xml}"
    end

    if self_closing?
      return "<#{full_name}#{attrs} />"
    else
      return "<#{full_name}#{attrs}>#{body}</#{full_name}>"
    end
  end

  ##
  # @return [String]
  #
  def inspect
    segments = []

    [:name, :namespace, :attributes, :children].each do |attr|
      value = send(attr)

      if !value or (value.respond_to?(:empty?) and value.empty?)
        next
      end

      segments << "#{attr}: #{value.inspect}"
    end

    return "Element(#{segments.join(' ')})"
  end

  ##
  # Registers a new namespace for the current element and its child
  # elements.
  #
  # @param [String] name
  # @param [String] uri
  # @see [Oga::XML::Namespace#initialize]
  #
  def register_namespace(name, uri)
    if namespaces[name]
      raise ArgumentError, "The namespace #{name.inspect} already exists"
    end

    namespaces[name] = Namespace.new(:name => name, :uri => uri)
  end

  ##
  # Returns a Hash containing all the namespaces available to the current
  # element.
  #
  # @return [Hash]
  #
  def available_namespaces
    merged = namespaces.dup
    node   = parent

    while node && node.respond_to?(:namespaces)
      node.namespaces.each do |prefix, ns|
        merged[prefix] = ns unless merged[prefix]
      end

      node = node.parent
    end

    return merged
  end

  ##
  # Returns `true` if the element is a self-closing element.
  #
  # @return [TrueClass|FalseClass]
  #
  def self_closing?
    self_closing = children.empty?
    root         = root_node

    if root.is_a?(Document) and root.type == :html \
    and !HTML_VOID_ELEMENTS.include?(name)
      self_closing = false
    end

    return self_closing
  end

  private

  ##
  # Registers namespaces based on any "xmlns" attributes.
  #
  def register_namespaces_from_attributes
    attributes.each do |attr|
      # We're using `namespace_name` opposed to `namespace.name` as "xmlns"
      # is not a registered namespace.
      if attr.name == XMLNS_PREFIX or attr.namespace_name == XMLNS_PREFIX
        register_namespace(attr.name, attr.value)
      end
    end
  end

  ##
  # Links all attributes to the current element.
  #
  def link_attributes
    attributes.each do |attr|
      attr.element = self
    end
  end

  ##
  # @param [String] name
  # @return [Array]
  #
  def split_name(name)
    segments = name.to_s.split(':')

    return segments.pop, segments.pop
  end

  ##
  # @param [Oga::XML::Attribute] attr
  # @param [String] ns
  # @param [String] name
  # @return [TrueClass|FalseClass]
  #
  def attribute_matches?(attr, ns, name)
    name_matches = attr.name == name
    ns_matches   = false

    if ns
      ns_matches = attr.namespace.to_s == ns

    elsif name_matches and !attr.namespace
      ns_matches = true
    end

    return name_matches && ns_matches
  end
end

#nameString

The name of the element.

Returns:

  • (String)

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/oga/xml/element.rb', line 23

class Element < Node
  include Querying

  attr_accessor :name, :namespace_name, :attributes, :namespaces

  ##
  # The attribute prefix/namespace used for registering element namespaces.
  #
  # @return [String]
  #
  XMLNS_PREFIX = 'xmlns'.freeze

  ##
  # @param [Hash] options
  #
  # @option options [String] :name The name of the element.
  #
  # @option options [String] :namespace_name The name of the namespace.
  #
  # @option options [Array<Oga::XML::Attribute>] :attributes The attributes
  #  of the element as an Array.
  #
  def initialize(options = {})
    super

    @name           = options[:name]
    @namespace_name = options[:namespace_name]
    @attributes     = options[:attributes] || []
    @namespaces     = options[:namespaces] || {}

    link_attributes
    register_namespaces_from_attributes
  end

  ##
  # Returns an attribute matching the given name (with or without the
  # namespace).
  #
  # @example
  #  # find an attribute that only has the name "foo"
  #  attribute('foo')
  #
  #  # find an attribute with namespace "foo" and name bar"
  #  attribute('foo:bar')
  #
  # @param [String|Symbol] name The name (with or without the namespace)
  #  of the attribute.
  #
  # @return [Oga::XML::Attribute]
  #
  def attribute(name)
    name, ns = split_name(name)

    attributes.each do |attr|
      return attr if attribute_matches?(attr, ns, name)
    end

    return
  end

  alias_method :attr, :attribute

  ##
  # Returns the value of the given attribute.
  #
  # @example
  #  element.get('class') # => "container"
  #
  # @see [#attribute]
  #
  def get(name)
    found = attribute(name)

    return found ? found.value : nil
  end

  ##
  # Adds a new attribute to the element.
  #
  # @param [Oga::XML::Attribute] attribute
  #
  def add_attribute(attribute)
    attribute.element = self

    attributes << attribute
  end

  ##
  # Sets the value of an attribute to the given value. If the attribute does
  # not exist it is created automatically.
  #
  # @param [String] name The name of the attribute, optionally including the
  #  namespace.
  #
  # @param [String] value The new value of the attribute.
  #
  def set(name, value)
    found = attribute(name)

    if found
      found.value = value
    else
      if name.include?(':')
        ns, name = name.split(':')
      else
        ns = nil
      end

      attr = Attribute.new(
        :name           => name,
        :namespace_name => ns,
        :value          => value
      )

      add_attribute(attr)
    end
  end

  ##
  # Removes an attribute from the element.
  #
  # @param [String] name The name (optionally including namespace prefix)
  #  of the attribute to remove.
  #
  # @return [Oga::XML::Attribute]
  #
  def unset(name)
    found = attribute(name)

    return attributes.delete(found) if found
  end

  ##
  # Returns the namespace of the element.
  #
  # @return [Oga::XML::Namespace]
  #
  def namespace
    unless @namespace
      available  = available_namespaces
      @namespace = available[namespace_name] || available[XMLNS_PREFIX]
    end

    return @namespace
  end

  ##
  # Returns the text of all child nodes joined together.
  #
  # @return [String]
  #
  def text
    return children.text
  end

  ##
  # Returns the text of the current element only.
  #
  # @return [String]
  #
  def inner_text
    text = ''

    text_nodes.each do |node|
      text << node.text
    end

    return text
  end

  ##
  # Returns any {Oga::XML::Text} nodes that are a direct child of this
  # element.
  #
  # @return [Oga::XML::NodeSet]
  #
  def text_nodes
    nodes = NodeSet.new

    children.each do |child|
      nodes << child if child.is_a?(Text)
    end

    return nodes
  end

  ##
  # Sets the inner text of the current element to the given String.
  #
  # @param [String] text
  #
  def inner_text=(text)
    text_node = XML::Text.new(:text => text)
    @children = NodeSet.new([text_node])
  end

  ##
  # Converts the element and its child elements to XML.
  #
  # @return [String]
  #
  def to_xml
    if namespace_name
      full_name = "#{namespace_name}:#{name}"
    else
      full_name = name
    end

    body  = children.map(&:to_xml).join('')
    attrs = ''

    attributes.each do |attr|
      attrs << " #{attr.to_xml}"
    end

    if self_closing?
      return "<#{full_name}#{attrs} />"
    else
      return "<#{full_name}#{attrs}>#{body}</#{full_name}>"
    end
  end

  ##
  # @return [String]
  #
  def inspect
    segments = []

    [:name, :namespace, :attributes, :children].each do |attr|
      value = send(attr)

      if !value or (value.respond_to?(:empty?) and value.empty?)
        next
      end

      segments << "#{attr}: #{value.inspect}"
    end

    return "Element(#{segments.join(' ')})"
  end

  ##
  # Registers a new namespace for the current element and its child
  # elements.
  #
  # @param [String] name
  # @param [String] uri
  # @see [Oga::XML::Namespace#initialize]
  #
  def register_namespace(name, uri)
    if namespaces[name]
      raise ArgumentError, "The namespace #{name.inspect} already exists"
    end

    namespaces[name] = Namespace.new(:name => name, :uri => uri)
  end

  ##
  # Returns a Hash containing all the namespaces available to the current
  # element.
  #
  # @return [Hash]
  #
  def available_namespaces
    merged = namespaces.dup
    node   = parent

    while node && node.respond_to?(:namespaces)
      node.namespaces.each do |prefix, ns|
        merged[prefix] = ns unless merged[prefix]
      end

      node = node.parent
    end

    return merged
  end

  ##
  # Returns `true` if the element is a self-closing element.
  #
  # @return [TrueClass|FalseClass]
  #
  def self_closing?
    self_closing = children.empty?
    root         = root_node

    if root.is_a?(Document) and root.type == :html \
    and !HTML_VOID_ELEMENTS.include?(name)
      self_closing = false
    end

    return self_closing
  end

  private

  ##
  # Registers namespaces based on any "xmlns" attributes.
  #
  def register_namespaces_from_attributes
    attributes.each do |attr|
      # We're using `namespace_name` opposed to `namespace.name` as "xmlns"
      # is not a registered namespace.
      if attr.name == XMLNS_PREFIX or attr.namespace_name == XMLNS_PREFIX
        register_namespace(attr.name, attr.value)
      end
    end
  end

  ##
  # Links all attributes to the current element.
  #
  def link_attributes
    attributes.each do |attr|
      attr.element = self
    end
  end

  ##
  # @param [String] name
  # @return [Array]
  #
  def split_name(name)
    segments = name.to_s.split(':')

    return segments.pop, segments.pop
  end

  ##
  # @param [Oga::XML::Attribute] attr
  # @param [String] ns
  # @param [String] name
  # @return [TrueClass|FalseClass]
  #
  def attribute_matches?(attr, ns, name)
    name_matches = attr.name == name
    ns_matches   = false

    if ns
      ns_matches = attr.namespace.to_s == ns

    elsif name_matches and !attr.namespace
      ns_matches = true
    end

    return name_matches && ns_matches
  end
end

#namespace_nameObject

Returns the value of attribute namespace_name


26
27
28
# File 'lib/oga/xml/element.rb', line 26

def namespace_name
  @namespace_name
end

#namespacesHash

The registered namespaces.

Returns:

  • (Hash)

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/oga/xml/element.rb', line 23

class Element < Node
  include Querying

  attr_accessor :name, :namespace_name, :attributes, :namespaces

  ##
  # The attribute prefix/namespace used for registering element namespaces.
  #
  # @return [String]
  #
  XMLNS_PREFIX = 'xmlns'.freeze

  ##
  # @param [Hash] options
  #
  # @option options [String] :name The name of the element.
  #
  # @option options [String] :namespace_name The name of the namespace.
  #
  # @option options [Array<Oga::XML::Attribute>] :attributes The attributes
  #  of the element as an Array.
  #
  def initialize(options = {})
    super

    @name           = options[:name]
    @namespace_name = options[:namespace_name]
    @attributes     = options[:attributes] || []
    @namespaces     = options[:namespaces] || {}

    link_attributes
    register_namespaces_from_attributes
  end

  ##
  # Returns an attribute matching the given name (with or without the
  # namespace).
  #
  # @example
  #  # find an attribute that only has the name "foo"
  #  attribute('foo')
  #
  #  # find an attribute with namespace "foo" and name bar"
  #  attribute('foo:bar')
  #
  # @param [String|Symbol] name The name (with or without the namespace)
  #  of the attribute.
  #
  # @return [Oga::XML::Attribute]
  #
  def attribute(name)
    name, ns = split_name(name)

    attributes.each do |attr|
      return attr if attribute_matches?(attr, ns, name)
    end

    return
  end

  alias_method :attr, :attribute

  ##
  # Returns the value of the given attribute.
  #
  # @example
  #  element.get('class') # => "container"
  #
  # @see [#attribute]
  #
  def get(name)
    found = attribute(name)

    return found ? found.value : nil
  end

  ##
  # Adds a new attribute to the element.
  #
  # @param [Oga::XML::Attribute] attribute
  #
  def add_attribute(attribute)
    attribute.element = self

    attributes << attribute
  end

  ##
  # Sets the value of an attribute to the given value. If the attribute does
  # not exist it is created automatically.
  #
  # @param [String] name The name of the attribute, optionally including the
  #  namespace.
  #
  # @param [String] value The new value of the attribute.
  #
  def set(name, value)
    found = attribute(name)

    if found
      found.value = value
    else
      if name.include?(':')
        ns, name = name.split(':')
      else
        ns = nil
      end

      attr = Attribute.new(
        :name           => name,
        :namespace_name => ns,
        :value          => value
      )

      add_attribute(attr)
    end
  end

  ##
  # Removes an attribute from the element.
  #
  # @param [String] name The name (optionally including namespace prefix)
  #  of the attribute to remove.
  #
  # @return [Oga::XML::Attribute]
  #
  def unset(name)
    found = attribute(name)

    return attributes.delete(found) if found
  end

  ##
  # Returns the namespace of the element.
  #
  # @return [Oga::XML::Namespace]
  #
  def namespace
    unless @namespace
      available  = available_namespaces
      @namespace = available[namespace_name] || available[XMLNS_PREFIX]
    end

    return @namespace
  end

  ##
  # Returns the text of all child nodes joined together.
  #
  # @return [String]
  #
  def text
    return children.text
  end

  ##
  # Returns the text of the current element only.
  #
  # @return [String]
  #
  def inner_text
    text = ''

    text_nodes.each do |node|
      text << node.text
    end

    return text
  end

  ##
  # Returns any {Oga::XML::Text} nodes that are a direct child of this
  # element.
  #
  # @return [Oga::XML::NodeSet]
  #
  def text_nodes
    nodes = NodeSet.new

    children.each do |child|
      nodes << child if child.is_a?(Text)
    end

    return nodes
  end

  ##
  # Sets the inner text of the current element to the given String.
  #
  # @param [String] text
  #
  def inner_text=(text)
    text_node = XML::Text.new(:text => text)
    @children = NodeSet.new([text_node])
  end

  ##
  # Converts the element and its child elements to XML.
  #
  # @return [String]
  #
  def to_xml
    if namespace_name
      full_name = "#{namespace_name}:#{name}"
    else
      full_name = name
    end

    body  = children.map(&:to_xml).join('')
    attrs = ''

    attributes.each do |attr|
      attrs << " #{attr.to_xml}"
    end

    if self_closing?
      return "<#{full_name}#{attrs} />"
    else
      return "<#{full_name}#{attrs}>#{body}</#{full_name}>"
    end
  end

  ##
  # @return [String]
  #
  def inspect
    segments = []

    [:name, :namespace, :attributes, :children].each do |attr|
      value = send(attr)

      if !value or (value.respond_to?(:empty?) and value.empty?)
        next
      end

      segments << "#{attr}: #{value.inspect}"
    end

    return "Element(#{segments.join(' ')})"
  end

  ##
  # Registers a new namespace for the current element and its child
  # elements.
  #
  # @param [String] name
  # @param [String] uri
  # @see [Oga::XML::Namespace#initialize]
  #
  def register_namespace(name, uri)
    if namespaces[name]
      raise ArgumentError, "The namespace #{name.inspect} already exists"
    end

    namespaces[name] = Namespace.new(:name => name, :uri => uri)
  end

  ##
  # Returns a Hash containing all the namespaces available to the current
  # element.
  #
  # @return [Hash]
  #
  def available_namespaces
    merged = namespaces.dup
    node   = parent

    while node && node.respond_to?(:namespaces)
      node.namespaces.each do |prefix, ns|
        merged[prefix] = ns unless merged[prefix]
      end

      node = node.parent
    end

    return merged
  end

  ##
  # Returns `true` if the element is a self-closing element.
  #
  # @return [TrueClass|FalseClass]
  #
  def self_closing?
    self_closing = children.empty?
    root         = root_node

    if root.is_a?(Document) and root.type == :html \
    and !HTML_VOID_ELEMENTS.include?(name)
      self_closing = false
    end

    return self_closing
  end

  private

  ##
  # Registers namespaces based on any "xmlns" attributes.
  #
  def register_namespaces_from_attributes
    attributes.each do |attr|
      # We're using `namespace_name` opposed to `namespace.name` as "xmlns"
      # is not a registered namespace.
      if attr.name == XMLNS_PREFIX or attr.namespace_name == XMLNS_PREFIX
        register_namespace(attr.name, attr.value)
      end
    end
  end

  ##
  # Links all attributes to the current element.
  #
  def link_attributes
    attributes.each do |attr|
      attr.element = self
    end
  end

  ##
  # @param [String] name
  # @return [Array]
  #
  def split_name(name)
    segments = name.to_s.split(':')

    return segments.pop, segments.pop
  end

  ##
  # @param [Oga::XML::Attribute] attr
  # @param [String] ns
  # @param [String] name
  # @return [TrueClass|FalseClass]
  #
  def attribute_matches?(attr, ns, name)
    name_matches = attr.name == name
    ns_matches   = false

    if ns
      ns_matches = attr.namespace.to_s == ns

    elsif name_matches and !attr.namespace
      ns_matches = true
    end

    return name_matches && ns_matches
  end
end

Instance Method Details

#add_attribute(attribute) ⇒ Object

Adds a new attribute to the element.

Parameters:


104
105
106
107
108
# File 'lib/oga/xml/element.rb', line 104

def add_attribute(attribute)
  attribute.element = self

  attributes << attribute
end

#attribute(name) ⇒ Oga::XML::Attribute Also known as: attr

Returns an attribute matching the given name (with or without the namespace).

Examples:

# find an attribute that only has the name "foo"
attribute('foo')

# find an attribute with namespace "foo" and name bar"
attribute('foo:bar')

Parameters:

  • name (String|Symbol)

    The name (with or without the namespace) of the attribute.

Returns:


73
74
75
76
77
78
79
80
81
# File 'lib/oga/xml/element.rb', line 73

def attribute(name)
  name, ns = split_name(name)

  attributes.each do |attr|
    return attr if attribute_matches?(attr, ns, name)
  end

  return
end

#attribute_matches?(attr, ns, name) ⇒ TrueClass|FalseClass (private)

Parameters:

Returns:

  • (TrueClass|FalseClass)

358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/oga/xml/element.rb', line 358

def attribute_matches?(attr, ns, name)
  name_matches = attr.name == name
  ns_matches   = false

  if ns
    ns_matches = attr.namespace.to_s == ns

  elsif name_matches and !attr.namespace
    ns_matches = true
  end

  return name_matches && ns_matches
end

#available_namespacesHash

Returns a Hash containing all the namespaces available to the current element.

Returns:

  • (Hash)

286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/oga/xml/element.rb', line 286

def available_namespaces
  merged = namespaces.dup
  node   = parent

  while node && node.respond_to?(:namespaces)
    node.namespaces.each do |prefix, ns|
      merged[prefix] = ns unless merged[prefix]
    end

    node = node.parent
  end

  return merged
end

#get(name) ⇒ Object

Returns the value of the given attribute.

Examples:

element.get('class') # => "container"

See Also:

  • Oga::XML::Element.[[#attribute]

93
94
95
96
97
# File 'lib/oga/xml/element.rb', line 93

def get(name)
  found = attribute(name)

  return found ? found.value : nil
end

#inner_textString

Returns the text of the current element only.

Returns:

  • (String)

183
184
185
186
187
188
189
190
191
# File 'lib/oga/xml/element.rb', line 183

def inner_text
  text = ''

  text_nodes.each do |node|
    text << node.text
  end

  return text
end

#inner_text=(text) ⇒ Object

Sets the inner text of the current element to the given String.

Parameters:

  • text (String)

214
215
216
217
# File 'lib/oga/xml/element.rb', line 214

def inner_text=(text)
  text_node = XML::Text.new(:text => text)
  @children = NodeSet.new([text_node])
end

#inspectString

Returns:

  • (String)

248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/oga/xml/element.rb', line 248

def inspect
  segments = []

  [:name, :namespace, :attributes, :children].each do |attr|
    value = send(attr)

    if !value or (value.respond_to?(:empty?) and value.empty?)
      next
    end

    segments << "#{attr}: #{value.inspect}"
  end

  return "Element(#{segments.join(' ')})"
end

Links all attributes to the current element.


336
337
338
339
340
# File 'lib/oga/xml/element.rb', line 336

def link_attributes
  attributes.each do |attr|
    attr.element = self
  end
end

#namespaceOga::XML::Namespace

Returns the namespace of the element.

Returns:


160
161
162
163
164
165
166
167
# File 'lib/oga/xml/element.rb', line 160

def namespace
  unless @namespace
    available  = available_namespaces
    @namespace = available[namespace_name] || available[XMLNS_PREFIX]
  end

  return @namespace
end

#register_namespace(name, uri) ⇒ Object

Registers a new namespace for the current element and its child elements.

Parameters:

  • name (String)
  • uri (String)

See Also:

  • Oga::XML::Element.[Oga[Oga::XML[Oga::XML::Namespace[Oga::XML::Namespace#initialize]

272
273
274
275
276
277
278
# File 'lib/oga/xml/element.rb', line 272

def register_namespace(name, uri)
  if namespaces[name]
    raise ArgumentError, "The namespace #{name.inspect} already exists"
  end

  namespaces[name] = Namespace.new(:name => name, :uri => uri)
end

#register_namespaces_from_attributesObject (private)

Registers namespaces based on any "xmlns" attributes.


323
324
325
326
327
328
329
330
331
# File 'lib/oga/xml/element.rb', line 323

def register_namespaces_from_attributes
  attributes.each do |attr|
    # We're using `namespace_name` opposed to `namespace.name` as "xmlns"
    # is not a registered namespace.
    if attr.name == XMLNS_PREFIX or attr.namespace_name == XMLNS_PREFIX
      register_namespace(attr.name, attr.value)
    end
  end
end

#self_closing?TrueClass|FalseClass

Returns true if the element is a self-closing element.

Returns:

  • (TrueClass|FalseClass)

306
307
308
309
310
311
312
313
314
315
316
# File 'lib/oga/xml/element.rb', line 306

def self_closing?
  self_closing = children.empty?
  root         = root_node

  if root.is_a?(Document) and root.type == :html \
  and !HTML_VOID_ELEMENTS.include?(name)
    self_closing = false
  end

  return self_closing
end

#set(name, value) ⇒ Object

Sets the value of an attribute to the given value. If the attribute does not exist it is created automatically.

Parameters:

  • name (String)

    The name of the attribute, optionally including the namespace.

  • value (String)

    The new value of the attribute.


119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/oga/xml/element.rb', line 119

def set(name, value)
  found = attribute(name)

  if found
    found.value = value
  else
    if name.include?(':')
      ns, name = name.split(':')
    else
      ns = nil
    end

    attr = Attribute.new(
      :name           => name,
      :namespace_name => ns,
      :value          => value
    )

    add_attribute(attr)
  end
end

#split_name(name) ⇒ Array (private)

Parameters:

  • name (String)

Returns:

  • (Array)

346
347
348
349
350
# File 'lib/oga/xml/element.rb', line 346

def split_name(name)
  segments = name.to_s.split(':')

  return segments.pop, segments.pop
end

#textString

Returns the text of all child nodes joined together.

Returns:

  • (String)

174
175
176
# File 'lib/oga/xml/element.rb', line 174

def text
  return children.text
end

#text_nodesOga::XML::NodeSet

Returns any Text nodes that are a direct child of this element.

Returns:


199
200
201
202
203
204
205
206
207
# File 'lib/oga/xml/element.rb', line 199

def text_nodes
  nodes = NodeSet.new

  children.each do |child|
    nodes << child if child.is_a?(Text)
  end

  return nodes
end

#to_xmlString

Converts the element and its child elements to XML.

Returns:

  • (String)

224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/oga/xml/element.rb', line 224

def to_xml
  if namespace_name
    full_name = "#{namespace_name}:#{name}"
  else
    full_name = name
  end

  body  = children.map(&:to_xml).join('')
  attrs = ''

  attributes.each do |attr|
    attrs << " #{attr.to_xml}"
  end

  if self_closing?
    return "<#{full_name}#{attrs} />"
  else
    return "<#{full_name}#{attrs}>#{body}</#{full_name}>"
  end
end

#unset(name) ⇒ Oga::XML::Attribute

Removes an attribute from the element.

Parameters:

  • name (String)

    The name (optionally including namespace prefix) of the attribute to remove.

Returns:


149
150
151
152
153
# File 'lib/oga/xml/element.rb', line 149

def unset(name)
  found = attribute(name)

  return attributes.delete(found) if found
end