rdf-rdfa 0.3.7 → 0.3.8

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.
data/README CHANGED
@@ -11,7 +11,7 @@ RDF::RDFa parses [RDFa][RDFa 1.1 Core] into statements or triples.
11
11
  * Fully compliant RDFa 1.1 parser.
12
12
  * Template-based Writer to generate XHTML+RDFa.
13
13
  * Writer uses user-replacable [Haml][Haml]-based templates to generate RDFa.
14
- * Uses Nokogiri for parsing HTML/SVG
14
+ * If available, Uses Nokogiri for parsing HTML/SVG, falls back to REXML otherwise (and for JRuby)
15
15
  * [RDFa tests][RDFa-test-suite] use SPARQL for most tests due to Rasqal limitations. Other tests compare directly against N-triples.
16
16
 
17
17
  Install with 'gem install rdf-rdfa'
@@ -20,6 +20,10 @@ Install with 'gem install rdf-rdfa'
20
20
  RDFa is an evolving standard, undergoing some substantial recent changes partly due to perceived competition
21
21
  with Microdata. As a result, the RDF Webapps working group is currently looking at changes in the processing model for RDFa. These changes are now being tracked in {RDF::RDFa::Reader}:
22
22
 
23
+ #### RDFa 1.1 Lite
24
+ This version fully supports the limited syntax of [RDFa Lite 1.1][]. This includes the ability to use
25
+ @property exclusively.
26
+
23
27
  #### Remove RDFa Profiles
24
28
  RDFa Profiles were a mechanism added to allow groups of terms and prefixes to be defined in an external resource and loaded to affect the processing of an RDFa document. This introduced a problem for some implementations needing to perform a cross-origin GET in order to retrieve the profiles. The working group elected to drop support for user-defined RDFa Profiles (the default profiles defined by RDFa Core and host languages still apply) and replace it with an inference regime using vocabularies. Parsing of @profile has been removed from this version.
25
29
 
@@ -81,6 +85,66 @@ defines a playlist with an ordered set of tracks. RDFa adds the @inlist attribut
81
85
 
82
86
  This basically does the same thing, but places each track in an rdf:List in the defined order.
83
87
 
88
+ #### Property relations
89
+ The @property attribute has been updated to allow for creating URI references as well as object literals.
90
+
91
+ 1. If an element contains @property but no @rel, @datatype or @content and it contains a resource attribute (such as @href, @src, or @resource)
92
+ 1. Generate an IRI object. Furthermore, sub-elements do not chain, i.e., the subject in effect when the @property is processed is also in effect for sub-elements.
93
+ 2. Otherwise, generate a literal as before.
94
+
95
+ For example:
96
+
97
+ <a vocab="http://schema.org" property="url" href="http://example.com">
98
+ <span property="title">NBA Eastern Conference ...</span>
99
+ </a>
100
+
101
+ results in
102
+
103
+ <> schema:url <http://example.com>;
104
+ schema:title "NBA Eastern Conference".
105
+
106
+ #### Magnetic @about/@typeof
107
+ The @typeof attribute has changed; previously, it always created a new subject, either using a resource from @about, @resource and so forth. This has long been a source of errors for people using RDFa. The new rules cause @typeof to bind to a subject if used with @about, otherwise, to an object, if either used alone, or in combination with some other resource attribute (such as @href, @src or @resource).
108
+
109
+ For example:
110
+
111
+ <div typeof="foaf:Person" about="http://greggkellogg.net/foaf#me">
112
+ <p property="name">Gregg Kellogg</span>
113
+ <a rel="knows" typeof="foaf:Person" href="http://manu.sporny.org/#this">
114
+ <span property="name">Manu Sporny</span>
115
+ </a>
116
+ </div>
117
+
118
+ results in
119
+
120
+ <http://greggkellogg.net/foaf#me> a foaf:Person;
121
+ foaf:name "Gregg Kellogg";
122
+ foaf:knows <http://manu.sporny.org/#this> .
123
+ <http://manu.sporny.org/#this> a foaf:Person;
124
+ foaf:name "Manu Sporny" .
125
+
126
+ Note that if the explicit @href is not present, i.e.,
127
+
128
+ <div typeof="foaf:Person" about="http://greggkellogg.net/foaf#me">
129
+ <p property="name">Gregg Kellogg</span>
130
+ <a href="knows" typeof="foaf:Person">
131
+ <span property="name">Manu Sporny</span>
132
+ </a>
133
+ </div>
134
+
135
+ this results in
136
+
137
+ <http://greggkellogg.net/foaf#me> a foaf:Person;
138
+ foaf:name "Gregg Kellogg";
139
+ foaf:knows [
140
+ a foaf:Person;
141
+ foaf:name "Manu Sporny"
142
+ ].
143
+
144
+
145
+ #### Property chaining
146
+ If used without @rel, but with @typeof and a resource attribute, @property will cause chaining to another object just like @rel. The effect of this and other changes is to allow pretty much all RDFa to be marked up using just @property; @rel/@rev is no longer required. Although, @rel and @rev have useful features that @property does not, so it's worth keeping them in your toolkit.
147
+
84
148
  ## Usage
85
149
 
86
150
  ### Reading RDF data in the RDFa format
@@ -297,9 +361,11 @@ The template hash defines four Haml templates:
297
361
  }
298
362
 
299
363
  ## Dependencies
364
+ * [Ruby](http://ruby-lang.org/) (>= 1.9) or (>= 1.8.1 with [Backports][])
300
365
  * [RDF.rb](http://rubygems.org/gems/rdf) (>= 0.3.1)
301
- * [Nokogiri](http://rubygems.org/gems/nokogiri) (>= 1.3.3)
302
366
  * [Haml](https://rubygems.org/gems/haml) (>= 3.0.0)
367
+ * [HTMLEntities](https://rubygems.org/gems/htmlentities) ('>= 4.3.0')
368
+ * Soft dependency on [Nokogiri](http://rubygems.org/gems/nokogiri) (>= 1.3.3)
303
369
 
304
370
  ## Documentation
305
371
  Full documentation available on [Rubydoc.info][RDFa doc]
@@ -313,6 +379,8 @@ Full documentation available on [Rubydoc.info][RDFa doc]
313
379
  * {RDF::RDFa::SVG}
314
380
  Asserts :svg format, image/svg+xml mime-type and .svg file extension.
315
381
  * {RDF::RDFa::Reader}
382
+ * {RDF::RDFa::Reader::Nokogiri}
383
+ * {RDF::RDFa::Reader::REXML}
316
384
  * {RDF::RDFa::Profile}
317
385
  * {RDF::RDFa::Writer}
318
386
 
@@ -373,6 +441,7 @@ see <http://unlicense.org/> or the accompanying {file:UNLICENSE} file.
373
441
  [YARD-GS]: http://rubydoc.info/docs/yard/file/docs/GettingStarted.md
374
442
  [PDD]: http://lists.w3.org/Archives/Public/public-rdf-ruby/2010May/0013.html
375
443
  [RDFa 1.1 Core]: http://www.w3.org/TR/2011/WD-rdfa-core-20110331/ "RDFa 1.1 Core"
444
+ [RDFa Lite 1.1]: http://www.w3.org/2010/02/rdfa/drafts/2011/ED-rdfa-lite-20111030/ "RDFa Lite 1.1"
376
445
  [XHTML+RDFa 1.1]: http://www.w3.org/TR/2011/WD-xhtml-rdfa-20110331/ "XHTML+RDFa 1.1"
377
446
  [RDFa-test-suite]: http://rdfa.digitalbazaar.com/test-suite/ "RDFa test suite"
378
447
  [RDFa doc]: http://rubydoc.info/github/gkellogg/rdf-rdfa/frames
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.7
1
+ 0.3.8
data/lib/rdf/rdfa.rb CHANGED
@@ -25,8 +25,6 @@ module RDF
25
25
  module RDFa
26
26
  require 'rdf/rdfa/format'
27
27
  require 'rdf/rdfa/vocab'
28
- require 'rdf/rdfa/patches/literal_hacks'
29
- require 'rdf/rdfa/patches/nokogiri_hacks'
30
28
  require 'rdf/rdfa/patches/string_hacks'
31
29
  autoload :Expansion, 'rdf/rdfa/expansion'
32
30
  autoload :Profile, 'rdf/rdfa/profile'
@@ -1,10 +1,24 @@
1
- require 'nokogiri' # FIXME: Implement using different modules as in RDF::TriX
1
+ begin
2
+ raise LoadError, "not with java" if RUBY_PLATFORM == "java"
3
+ require 'nokogiri'
4
+ rescue LoadError => e
5
+ :rexml
6
+ end
2
7
  require 'rdf/ntriples'
8
+ require 'rdf/xsd'
3
9
 
4
10
  module RDF::RDFa
5
11
  ##
6
12
  # An RDFa parser in Ruby
7
13
  #
14
+ # This class supports [Nokogiri][] for HTML
15
+ # processing, and will automatically select the most performant
16
+ # implementation (Nokogiri or LibXML) that is available. If need be, you
17
+ # can explicitly override the used implementation by passing in a
18
+ # `:library` option to `Reader.new` or `Reader.open`.
19
+ #
20
+ # [Nokogiri]: http://nokogiri.org/
21
+ #
8
22
  # Based on processing rules described here:
9
23
  # @see http://www.w3.org/TR/rdfa-syntax/#s_model RDFa 1.0
10
24
  # @see http://www.w3.org/TR/2011/WD-rdfa-core-20110331/ RDFa Core 1.1
@@ -174,7 +188,7 @@ module RDF::RDFa
174
188
 
175
189
  def inspect
176
190
  v = ['base', 'parent_subject', 'parent_object', 'language', 'default_vocabulary'].map do |a|
177
- "#{a}='#{self.send(a).inspect}'"
191
+ "#{a}=#{self.send(a).inspect}"
178
192
  end
179
193
  v << "uri_mappings[#{uri_mappings.keys.length}]"
180
194
  v << "incomplete_triples[#{incomplete_triples.length}]"
@@ -184,27 +198,22 @@ module RDF::RDFa
184
198
  end
185
199
  end
186
200
 
201
+ # Returns the XML implementation module for this reader instance.
202
+ #
203
+ # @attr_reader [Module]
204
+ attr_reader :implementation
205
+
187
206
  ##
188
207
  # Initializes the RDFa reader instance.
189
208
  #
190
- # @param [Nokogiri::HTML::Document, Nokogiri::XML::Document, IO, File, String] input
209
+ # @param [IO, File, String] input
191
210
  # the input stream to read
192
211
  # @param [Hash{Symbol => Object}] options
193
- # any additional options
194
- # @option options [Encoding] :encoding (Encoding::UTF_8)
195
- # the encoding of the input stream (Ruby 1.9+)
196
- # @option options [Boolean] :validate (false)
197
- # whether to validate the parsed statements and values
198
- # @option options [Boolean] :canonicalize (false)
199
- # whether to canonicalize parsed literals
212
+ # any additional options (see `RDF::Reader#initialize`)
213
+ # @option options [Symbol] :library
214
+ # One of :nokogiri or :rexml. If nil/unspecified uses :nokogiri if available, :rexml otherwise.
200
215
  # @option options [Boolean] :expand (false)
201
216
  # whether to perform RDFS expansion on the resulting graph
202
- # @option options [Boolean] :intern (true)
203
- # whether to intern all parsed URIs
204
- # @option options [Hash] :prefixes (Hash.new)
205
- # the prefix mappings to use (not supported by all readers)
206
- # @option options [#to_s] :base_uri (nil)
207
- # the base URI to use when resolving relative URIs
208
217
  # @option options [:xml1, :xhtml1, :xhtml5, :html4, :html5, :svg] :host_language (:xhtml1)
209
218
  # Host Language
210
219
  # @option options [:"rdfa1.0", :"rdfa1.1"] :version (:"rdfa1.1")
@@ -224,33 +233,33 @@ module RDF::RDFa
224
233
  super do
225
234
  @debug = options[:debug]
226
235
 
227
- detect_host_language_version(input, options)
228
-
229
236
  @processor_graph = options[:processor_graph]
230
237
 
231
- @doc = case input
232
- when Nokogiri::HTML::Document, Nokogiri::XML::Document
233
- input
234
- else
235
- # Try to detect charset from input
236
- options[:encoding] ||= input.charset if input.respond_to?(:charset)
237
-
238
- # Otherwise, default is utf-8
239
- options[:encoding] ||= 'utf-8'
240
-
241
- case @host_language
242
- when :html4, :html5
243
- Nokogiri::HTML.parse(input, base_uri.to_s, options[:encoding])
238
+ @library = case options[:library]
239
+ when nil
240
+ # Use Nokogiri when available, and REXML otherwise:
241
+ (defined?(::Nokogiri) && RUBY_PLATFORM != 'java') ? :nokogiri : :rexml
242
+ when :nokogiri, :rexml
243
+ options[:library]
244
244
  else
245
- Nokogiri::XML.parse(input, base_uri.to_s, options[:encoding])
246
- end
245
+ raise ArgumentError.new("expected :rexml or :nokogiri, but got #{options[:library].inspect}")
247
246
  end
248
-
249
- if ((@doc.nil? || @doc.root.nil?) && validate?)
247
+
248
+ require "rdf/rdfa/reader/#{@library}"
249
+ @implementation = case @library
250
+ when :nokogiri then Nokogiri
251
+ when :rexml then REXML
252
+ end
253
+ self.extend(@implementation)
254
+
255
+ detect_host_language_version(input, options)
256
+ initialize_xml(input, options) rescue raise RDF::ReaderError.new($!.message)
257
+
258
+ if (root.nil? && validate?)
250
259
  add_error(nil, "Empty document", RDF::RDFA.DocumentError)
251
260
  raise RDF::ReaderError, "Empty Document"
252
261
  end
253
- add_warning(nil, "Syntax errors:\n#{@doc.errors}", RDF::RDFA.DocumentError) if !@doc.errors.empty? && validate?
262
+ add_warning(nil, "Syntax errors:\n#{doc_errors}", RDF::RDFA.DocumentError) if !doc_errors.empty? && validate?
254
263
 
255
264
  # Section 4.2 RDFa Host Language Conformance
256
265
  #
@@ -276,96 +285,12 @@ module RDF::RDFa
276
285
  @host_defaults[:profiles] = [XML_RDFA_PROFILE, XHTML_RDFA_PROFILE]
277
286
  end
278
287
 
279
- add_info(@doc, "version = #{@version}, host_language = #{@host_language}")
288
+ add_info(@doc, "version = #{@version}, host_language = #{@host_language}, library = #{@library}")
280
289
 
281
290
  block.call(self) if block_given?
282
291
  end
283
292
  end
284
293
 
285
- # Determine the host language and/or version from options and the input document
286
- def detect_host_language_version(input, options)
287
- @host_language = options[:host_language] ? options[:host_language].to_sym : nil
288
- @version = options[:version] ? options[:version].to_sym : nil
289
- return if @host_language && @version
290
-
291
- # Snif version based on input
292
- case input
293
- when Nokogiri::XML::Document, Nokogiri::HTML::Document
294
- doc_type_string = input.doctype.to_s
295
- version_attr = input.root && input.root.attribute("version").to_s
296
- root_element = input.root.name.downcase
297
- root_namespace = input.root.namespace.to_s
298
- root_attrs = input.root.attributes
299
- content_type = case
300
- when root_element == "html" && input.is_a?(Nokogiri::HTML::Document)
301
- "text/html"
302
- when root_element == "html" && input.is_a?(Nokogiri::XML::Document)
303
- "application/xhtml+html"
304
- end
305
- else
306
- content_type = input.content_type if input.respond_to?(:content_type)
307
-
308
- # Determine from head of document
309
- head = if input.respond_to?(:read)
310
- input.rewind
311
- string = input.read(1000)
312
- input.rewind
313
- string.to_s
314
- else
315
- input.to_s[0..1000]
316
- end
317
-
318
- doc_type_string = head.match(%r(<!DOCTYPE[^>]*>)m).to_s
319
- root = head.match(%r(<[^!\?>]*>)m).to_s
320
- root_element = root.match(%r(^<(\S+)[ >])) ? $1 : ""
321
- version_attr = root.match(/version\s+=\s+(\S+)[\s">]/m) ? $1 : ""
322
- head_element = head.match(%r(<head.*<\/head>)mi)
323
- head_doc = Nokogiri::HTML.parse(head_element.to_s)
324
-
325
- # May determine content-type and/or charset from meta
326
- # Easist way is to parse head into a document and iterate
327
- # of CSS matches
328
- head_doc.css("meta").each do |e|
329
- if e.attr("http-equiv").to_s.downcase == 'content-type'
330
- content_type, e = e.attr("content").to_s.downcase.split(";")
331
- options[:encoding] = $1.downcase if e.to_s =~ /charset=([^\s]*)$/i
332
- elsif e.attr("charset")
333
- options[:encoding] = e.attr("charset").to_s.downcase
334
- end
335
- end
336
- end
337
-
338
- # Already using XML parser, determine from DOCTYPE and/or root element
339
- @version ||= :"rdfa1.0" if doc_type_string =~ /RDFa 1\.0/
340
- @version ||= :"rdfa1.0" if version_attr =~ /RDFa 1\.0/
341
- @version ||= :"rdfa1.1" if version_attr =~ /RDFa 1\.1/
342
- @version ||= :"rdfa1.1"
343
-
344
- @host_language ||= case content_type
345
- when "application/xml" then :xml1
346
- when "image/svg+xml" then :svg
347
- when "text/html"
348
- case doc_type_string
349
- when /html 4/i then :html4
350
- when /xhtml/i then :xhtml1
351
- when /html/i then :html5
352
- end
353
- when "application/xhtml+xml"
354
- case doc_type_string
355
- when /html 4/i then :html4
356
- when /xhtml/i then :xhtml1
357
- when /html/i then :xhtml5
358
- end
359
- else
360
- case root_element
361
- when /svg/i then :svg
362
- when /html/i then :html4
363
- end
364
- end
365
-
366
- @host_language ||= :xml1
367
- end
368
-
369
294
  ##
370
295
  # Iterates the given block for each RDF statement in the input.
371
296
  #
@@ -414,17 +339,14 @@ module RDF::RDFa
414
339
  @bnode_cache[value.to_s] ||= RDF::Node.new(value)
415
340
  end
416
341
 
417
- # Figure out the document path, if it is a Nokogiri::XML::Element or Attribute
342
+ # Figure out the document path, if it is an Element or Attribute
418
343
  def node_path(node)
419
- "<#{base_uri}>" + case node
420
- when Nokogiri::XML::Node then node.display_path
421
- else node.to_s
422
- end
344
+ "<#{base_uri}>#{node.respond_to?(:display_path) ? node.display_path : node}"
423
345
  end
424
346
 
425
347
  # Add debug event to debug array, if specified
426
348
  #
427
- # @param [Nokogiri::XML::Node, #to_s] node:: XML Node or string for showing context
349
+ # @param [#display_path, #to_s] node:: XML Node or string for showing context
428
350
  # @param [String] message::
429
351
  # @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
430
352
  def add_debug(node, message = "")
@@ -464,7 +386,7 @@ module RDF::RDFa
464
386
 
465
387
  # add a statement, object can be literal or URI or bnode
466
388
  #
467
- # @param [Nokogiri::XML::Node, #to_s] node:: XML Node or string for showing context
389
+ # @param [#display_path, #to_s] node:: XML Node or string for showing context
468
390
  # @param [RDF::URI, RDF::BNode] subject:: the subject of the statement
469
391
  # @param [RDF::URI] predicate:: the predicate of the statement
470
392
  # @param [URI, RDF::BNode, RDF::Literal] object:: the object of the statement
@@ -478,16 +400,7 @@ module RDF::RDFa
478
400
 
479
401
  # Parsing an RDFa document (this is *not* the recursive method)
480
402
  def parse_whole_document(doc, base)
481
- # find if the document has a base element
482
- case @host_language
483
- when :xhtml1, :xhtml5, :html4, :html5
484
- base_el = doc.at_css("html>head>base")
485
- base = base_el.attribute("href").to_s.split("#").first if base_el
486
- else
487
- xml_base = doc.root.attribute_with_ns("base", RDF::XML.to_s)
488
- base = xml_base if xml_base
489
- end
490
-
403
+ base = doc_base(base)
491
404
  if (base)
492
405
  # Strip any fragment from base
493
406
  base = base.to_s.split("#").first
@@ -500,8 +413,8 @@ module RDF::RDFa
500
413
 
501
414
  if @version != :"rdfa1.0"
502
415
  # Process default vocabularies
503
- process_profile(doc.root, @host_defaults[:profiles]) do |which, value|
504
- add_debug(doc.root) { "parse_whole_document, #{which}: #{value.inspect}"}
416
+ process_profile(root, @host_defaults[:profiles]) do |which, value|
417
+ add_debug(root) { "parse_whole_document, #{which}: #{value.inspect}"}
505
418
  case which
506
419
  when :uri_mappings then evaluation_context.uri_mappings.merge!(value)
507
420
  when :term_mappings then evaluation_context.term_mappings.merge!(value)
@@ -510,7 +423,7 @@ module RDF::RDFa
510
423
  end
511
424
  end
512
425
 
513
- traverse(doc.root, evaluation_context)
426
+ traverse(root, evaluation_context)
514
427
  add_debug("", "parse_whole_doc: traversal complete'")
515
428
  end
516
429
 
@@ -521,33 +434,33 @@ module RDF::RDFa
521
434
  profiles.
522
435
  map {|uri| uri(uri).normalize}.
523
436
  each do |uri|
524
- # Don't try to open ourselves!
525
- if base_uri == uri
526
- add_debug(element, "process_profile: skip recursive profile <#{uri}>")
527
- next
528
- end
437
+ # Don't try to open ourselves!
438
+ if base_uri == uri
439
+ add_debug(element) {"process_profile: skip recursive profile <#{uri}>"}
440
+ next
441
+ end
529
442
 
530
- old_debug = RDF::RDFa.debug?
531
- begin
532
- add_info(element, "process_profile: load <#{uri}>")
533
- RDF::RDFa.debug = false
534
- profile = Profile.find(uri)
535
- rescue Exception => e
536
- RDF::RDFa.debug = old_debug
537
- add_error(element, e.message, RDF::RDFA.ProfileReferenceError)
538
- raise # In case we're not in strict mode, we need to be sure processing stops
539
- ensure
540
- RDF::RDFa.debug = old_debug
541
- end
443
+ old_debug = RDF::RDFa.debug?
444
+ begin
445
+ add_info(element, "process_profile: load <#{uri}>")
446
+ RDF::RDFa.debug = false
447
+ profile = Profile.find(uri)
448
+ rescue Exception => e
449
+ RDF::RDFa.debug = old_debug
450
+ add_error(element, e.message, RDF::RDFA.ProfileReferenceError)
451
+ raise # In case we're not in strict mode, we need to be sure processing stops
452
+ ensure
453
+ RDF::RDFa.debug = old_debug
454
+ end
542
455
 
543
- # Add URI Mappings to prefixes
544
- profile.prefixes.each_pair do |prefix, value|
545
- prefix(prefix, value)
456
+ # Add URI Mappings to prefixes
457
+ profile.prefixes.each_pair do |prefix, value|
458
+ prefix(prefix, value)
459
+ end
460
+ yield :uri_mappings, profile.prefixes unless profile.prefixes.empty?
461
+ yield :term_mappings, profile.terms unless profile.terms.empty?
462
+ yield :default_vocabulary, profile.vocabulary if profile.vocabulary
546
463
  end
547
- yield :uri_mappings, profile.prefixes unless profile.prefixes.empty?
548
- yield :term_mappings, profile.terms unless profile.terms.empty?
549
- yield :default_vocabulary, profile.vocabulary if profile.vocabulary
550
- end
551
464
  end
552
465
 
553
466
  # Extract the XMLNS mappings from an element
@@ -558,15 +471,20 @@ module RDF::RDFa
558
471
  # and the URI is not processed in any way; in particular if it is a relative path it is
559
472
  # not resolved against the current base.
560
473
  ns_defs = {}
561
- element.namespace_definitions.each do |ns|
562
- ns_defs[ns.prefix] = ns.href.to_s
474
+ element.namespaces.each do |prefix, href|
475
+ prefix = nil if prefix == "xmlns"
476
+ add_debug("extract_mappings") { "ns: #{prefix}: #{href}"}
477
+ ns_defs[prefix] = href
563
478
  end
564
479
 
565
480
  # HTML parsing doesn't create namespace_definitions
566
481
  if ns_defs.empty?
567
482
  ns_defs = {}
568
- element.attributes.each do |k, v|
569
- ns_defs[$1] = v.to_s if k =~ /^xmlns(?:\:(.+))?/
483
+ element.attributes.each do |attr, href|
484
+ next unless attr =~ /^xmlns(?:\:(.+))?/
485
+ prefix = $1
486
+ add_debug("extract_mappings") { "ns(attr): #{prefix}: #{href}"}
487
+ ns_defs[prefix] = href.to_s
570
488
  end
571
489
  end
572
490
 
@@ -624,6 +542,7 @@ module RDF::RDFa
624
542
  recurse = true
625
543
  skip = false
626
544
  new_subject = nil
545
+ typed_resource = nil
627
546
  current_object_resource = nil
628
547
  uri_mappings = evaluation_context.uri_mappings.clone
629
548
  namespaces = evaluation_context.namespaces.clone
@@ -633,58 +552,39 @@ module RDF::RDFa
633
552
  default_vocabulary = evaluation_context.default_vocabulary
634
553
  list_mapping = evaluation_context.list_mapping
635
554
 
636
- # shortcut
637
- attrs = element.attributes
638
-
639
- about = attrs['about']
640
- src = attrs['src']
641
- resource = attrs['resource']
642
- href = attrs['href']
643
- vocab = attrs['vocab']
644
- xml_base = element.attribute_with_ns("base", RDF::XML.to_s)
555
+ xml_base = element.base
645
556
  base = xml_base.to_s if xml_base && ![:xhtml1, :xhtml5, :html4, :html5].include?(@host_language)
646
557
  base ||= evaluation_context.base
647
558
 
648
559
  # Pull out the attributes needed for the skip test.
649
- property = attrs['property'].to_s.strip if attrs['property']
650
- typeof = attrs['typeof'].to_s.strip if attrs.has_key?('typeof')
651
- datatype = attrs['datatype'].to_s if attrs['datatype']
652
- content = attrs['content'].to_s if attrs['content']
653
- rel = attrs['rel'].to_s.strip if attrs['rel']
654
- rev = attrs['rev'].to_s.strip if attrs['rev']
655
-
656
- # Lists:
657
- # @inlist
658
- # an attribute (value ignored) used to indicate that the object associated with a
659
- # @rel or @property attribute on the same element is to be added to the list
660
- # for that property. Causes a list to be created if it does not already exist.
661
- inlist = attrs['inlist'].to_s.strip if attrs.has_key?('inlist')
560
+ attrs = {}
561
+ %w(
562
+ about
563
+ content
564
+ data
565
+ datatype
566
+ datetime
567
+ href
568
+ inlist
569
+ property
570
+ rel
571
+ resource
572
+ rev
573
+ src
574
+ typeof
575
+ vocab
576
+ ).each do |a|
577
+ attrs[a.to_sym] = element.attributes[a].to_s.strip if element.attributes[a]
578
+ end
662
579
 
663
- add_debug(element) do
664
- attrs = {
665
- :about => about,
666
- :src => src,
667
- :resource => resource,
668
- :href => href,
669
- :vocab => vocab,
670
- :base => xml_base,
671
- :property => property,
672
- :typeof => typeof,
673
- :datatype => datatype,
674
- :rel => rel,
675
- :rev => rev,
676
- :inlist => inlist,
677
- }.select {|k,v| v}
678
-
679
- "attrs " + attrs.map {|a| "#{a.first}: #{a.last}"}.join(", ")
680
- end unless attrs.empty?
580
+ add_debug(element) {"attrs " + attrs.inspect} unless attrs.empty?
681
581
 
682
582
  # Default vocabulary [7.5 Step 2]
683
583
  # Next the current element is examined for any change to the default vocabulary via @vocab.
684
584
  # If @vocab is present and contains a value, its value updates the local default vocabulary.
685
585
  # If the value is empty, then the local default vocabulary must be reset to the Host Language defined default.
686
- unless vocab.nil?
687
- default_vocabulary = if vocab.to_s.empty?
586
+ if attrs[:vocab]
587
+ default_vocabulary = if attrs[:vocab].empty?
688
588
  # Set default_vocabulary to host language default
689
589
  add_debug(element) {
690
590
  "[Step 3] reset default_vocaulary to #{@host_defaults.fetch(:vocabulary, nil).inspect}"
@@ -692,9 +592,9 @@ module RDF::RDFa
692
592
  @host_defaults.fetch(:vocabulary, nil)
693
593
  else
694
594
  # Generate a triple indicating that the vocabulary is used
695
- add_triple(element, base, RDF::RDFA.hasVocabulary, uri(vocab))
595
+ add_triple(element, base, RDF::RDFA.hasVocabulary, uri(attrs[:vocab]))
696
596
 
697
- uri(vocab)
597
+ uri(attrs[:vocab])
698
598
  end
699
599
  add_debug(element) {
700
600
  "[Step 2] default_vocaulary: #{default_vocabulary.inspect}"
@@ -707,33 +607,17 @@ module RDF::RDFa
707
607
  extract_mappings(element, uri_mappings, namespaces)
708
608
 
709
609
  # Language information [7.5 Step 4]
710
- # From HTML5 [3.2.3.3]
711
- # If both the lang attribute in no namespace and the lang attribute in the XML namespace are set
712
- # on an element, user agents must use the lang attribute in the XML namespace, and the lang
713
- # attribute in no namespace must be ignored for the purposes of determining the element's
714
- # language.
715
- language = case
716
- when @doc.is_a?(Nokogiri::HTML::Document) && element.attributes["xml:lang"]
717
- element.attributes["xml:lang"].to_s
718
- when @doc.is_a?(Nokogiri::HTML::Document) && element.attributes["lang"]
719
- element.attributes["lang"].to_s
720
- when element.at_xpath("@xml:lang", "xml" => RDF::XML["uri"].to_s)
721
- element.at_xpath("@xml:lang", "xml" => RDF::XML["uri"].to_s).to_s
722
- when element.at_xpath("@lang")
723
- element.at_xpath("@lang").to_s
724
- else
725
- language
726
- end
610
+ language = element.language || language
727
611
  language = nil if language.to_s.empty?
728
- add_debug(element, "HTML5 [3.2.3.3] lang: #{language || 'nil'}") if language
612
+ add_debug(element) {"HTML5 [3.2.3.3] lang: #{language.inspect}"} if language
729
613
 
730
614
  # rels and revs
731
- rels = process_uris(element, rel, evaluation_context, base,
615
+ rels = process_uris(element, attrs[:rel], evaluation_context, base,
732
616
  :uri_mappings => uri_mappings,
733
617
  :term_mappings => term_mappings,
734
618
  :vocab => default_vocabulary,
735
619
  :restrictions => TERMorCURIEorAbsURI.fetch(@version, []))
736
- revs = process_uris(element, rev, evaluation_context, base,
620
+ revs = process_uris(element, attrs[:rev], evaluation_context, base,
737
621
  :uri_mappings => uri_mappings,
738
622
  :term_mappings => term_mappings,
739
623
  :vocab => default_vocabulary,
@@ -743,64 +627,152 @@ module RDF::RDFa
743
627
  "rels: #{rels.join(" ")}, revs: #{revs.join(" ")}"
744
628
  end unless (rels + revs).empty?
745
629
 
746
- if !(rel || rev)
630
+ if !(attrs[:rel] || attrs[:rev])
747
631
  # Establishing a new subject if no rel/rev [7.5 Step 5]
748
- # May not be valid, but can exist
749
- new_subject = if about
750
- process_uri(element, about, evaluation_context, base,
751
- :uri_mappings => uri_mappings,
752
- :restrictions => SafeCURIEorCURIEorURI.fetch(@version, []))
753
- elsif resource
754
- process_uri(element, resource, evaluation_context, base,
755
- :uri_mappings => uri_mappings,
756
- :restrictions => SafeCURIEorCURIEorURI.fetch(@version, []))
757
- elsif href
758
- process_uri(element, href, evaluation_context, base, :restrictions => [:uri])
759
- elsif src
760
- process_uri(element, src, evaluation_context, base, :restrictions => [:uri])
761
- end
632
+
633
+ if @version == :"rdfa1.0"
634
+ new_subject = if attrs[:about]
635
+ process_uri(element, attrs[:about], evaluation_context, base,
636
+ :uri_mappings => uri_mappings,
637
+ :restrictions => SafeCURIEorCURIEorURI.fetch(@version, []))
638
+ elsif attrs[:resource]
639
+ process_uri(element, attrs[:resource], evaluation_context, base,
640
+ :uri_mappings => uri_mappings,
641
+ :restrictions => SafeCURIEorCURIEorURI.fetch(@version, []))
642
+ elsif attrs[:href] || attrs[:src]
643
+ process_uri(element, (attrs[:href] || attrs[:src]), evaluation_context, base, :restrictions => [:uri])
644
+ end
762
645
 
763
- # If no URI is provided by a resource attribute, then the first match from the following rules
764
- # will apply:
765
- # if @typeof is present, then new subject is set to be a newly created bnode.
766
- # otherwise,
767
- # if parent object is present, new subject is set to the value of parent object.
768
- # Additionally, if @property is not present then the skip element flag is set to 'true';
769
- new_subject ||= if [:xhtml1, :xhtml5, :html4, :html5].include?(@host_language) && element.name =~ /^(head|body)$/
770
- # From XHTML+RDFa 1.1:
771
- # if no URI is provided, then first check to see if the element is the head or body element.
772
- # If it is, then act as if there is an empty @about present, and process it according to the rule for @about.
773
- uri(base)
774
- elsif element == @doc.root && base
775
- uri(base)
776
- elsif typeof
777
- RDF::Node.new
646
+ # If no URI is provided by a resource attribute, then the first match from the following rules
647
+ # will apply:
648
+ new_subject ||= if [:xhtml1, :xhtml5, :html4, :html5].include?(@host_language) && element.name =~ /^(head|body)$/
649
+ # From XHTML+RDFa 1.1:
650
+ # if no URI is provided, then first check to see if the element is the head or body element.
651
+ # If it is, then act as if there is an empty @about present, and process it according to the rule for @about.
652
+ uri(base)
653
+ elsif element == root && base
654
+ # if the element is the root element of the document, then act as if there is an empty @about present,
655
+ # and process it according to the rule for @about, above;
656
+ uri(base)
657
+ elsif attrs[:typeof]
658
+ RDF::Node.new
659
+ else
660
+ # otherwise, if parent object is present, new subject is set to the value of parent object.
661
+ skip = true unless attrs[:property]
662
+ evaluation_context.parent_object
663
+ end
664
+
665
+ # if the @typeof attribute is present, set typed resource to new subject
666
+ typed_resource = new_subject if attrs[:typeof]
778
667
  else
779
- # if it's null, it's null and nothing changes
780
- skip = true unless property
781
- evaluation_context.parent_object
668
+ # If the current element contains the @property attribute, but does not contain the @content or the @datatype attribute
669
+ if attrs[:property] && !(attrs[:content] || attrs[:datatype]) && evaluation_context.incomplete_triples.empty?
670
+ new_subject = process_uri(element, attrs[:about], evaluation_context, base,
671
+ :uri_mappings => uri_mappings,
672
+ :restrictions => SafeCURIEorCURIEorURI.fetch(@version, [])) if attrs[:about]
673
+
674
+ # if the @typeof attribute is present, set typed resource to new subject
675
+ typed_resource = new_subject if attrs[:typeof]
676
+
677
+ # If no URI is provided by a resource attribute, then the first match from the following rules
678
+ # will apply:
679
+ new_subject ||= if [:xhtml1, :xhtml5, :html4, :html5].include?(@host_language) && element.name =~ /^(head|body)$/
680
+ # From XHTML+RDFa 1.1:
681
+ # if no URI is provided, then first check to see if the element is the head or body element.
682
+ # If it is, then act as if there is an empty @about present, and process it according to the rule for @about.
683
+ uri(base)
684
+ elsif element == root && base
685
+ # if the element is the root element of the document, then act as if there is an empty @about present,
686
+ # and process it according to the rule for @about, above;
687
+ uri(base)
688
+ else
689
+ # otherwise, if parent object is present, new subject is set to the value of parent object.
690
+ evaluation_context.parent_object
691
+ end
692
+
693
+ if attrs[:typeof]
694
+ typed_resource ||= if attrs[:resource]
695
+ process_uri(element, attrs[:resource], evaluation_context, base,
696
+ :uri_mappings => uri_mappings,
697
+ :restrictions => SafeCURIEorCURIEorURI.fetch(@version, []))
698
+ elsif attrs[:href] || attrs[:src]
699
+ process_uri(element, (attrs[:href] || attrs[:src]), evaluation_context, base, :restrictions => [:uri])
700
+ else
701
+ # if none of these are present, the value of typed resource is set to a newly defined bnode.
702
+ RDF::Node.new
703
+ end
704
+
705
+ # The value of the current object resource is set to the value of typed resource.
706
+ current_object_resource = typed_resource
707
+ end
708
+ else
709
+ # otherwise (ie, the @property element is not present)
710
+
711
+ new_subject = if attrs[:about]
712
+ process_uri(element, attrs[:about], evaluation_context, base,
713
+ :uri_mappings => uri_mappings,
714
+ :restrictions => SafeCURIEorCURIEorURI.fetch(@version, []))
715
+ elsif attrs[:resource]
716
+ process_uri(element, attrs[:resource], evaluation_context, base,
717
+ :uri_mappings => uri_mappings,
718
+ :restrictions => SafeCURIEorCURIEorURI.fetch(@version, []))
719
+ elsif attrs[:href] || attrs[:src]
720
+ process_uri(element, (attrs[:href] || attrs[:src]), evaluation_context, base, :restrictions => [:uri])
721
+ end
722
+
723
+ # If no URI is provided by a resource attribute, then the first match from the following rules
724
+ # will apply:
725
+ new_subject ||= if [:xhtml1, :xhtml5, :html4, :html5].include?(@host_language) && element.name =~ /^(head|body)$/
726
+ # From XHTML+RDFa 1.1:
727
+ # if no URI is provided, then first check to see if the element is the head or body element.
728
+ # If it is, then act as if there is an empty @about present, and process it according to the rule for @about.
729
+ uri(base)
730
+ elsif element == root && base
731
+ # if the element is the root element of the document, then act as if there is an empty @about present,
732
+ # and process it according to the rule for @about, above;
733
+ uri(base)
734
+ elsif attrs[:typeof]
735
+ RDF::Node.new
736
+ else
737
+ # otherwise, if parent object is present, new subject is set to the value of parent object.
738
+ # Additionally, if @property is not present then the skip element flag is set to 'true'.
739
+ skip = true unless attrs[:property]
740
+ evaluation_context.parent_object
741
+ end
742
+
743
+ # if @typeof is present, set the typed resource to the value of new subject</code>
744
+ typed_resource ||= new_subject if attrs[:typeof]
745
+ end
782
746
  end
783
- add_debug(element, "[Step 5] new_subject: #{new_subject.to_ntriples rescue 'nil'}, skip = #{skip}")
747
+
748
+ add_debug(element) {
749
+ "[Step 5] new_subject: #{new_subject.to_ntriples rescue 'nil'}, " +
750
+ "typed_resource: #{typed_resource.to_ntriples rescue 'nil'}, " +
751
+ "skip = #{skip}"
752
+ }
784
753
  else
785
754
  # [7.5 Step 6]
786
755
  # If the current element does contain a @rel or @rev attribute, then the next step is to
787
756
  # establish both a value for new subject and a value for current object resource:
788
- new_subject = process_uri(element, about, evaluation_context, base,
757
+ new_subject = process_uri(element, attrs[:about], evaluation_context, base,
789
758
  :uri_mappings => uri_mappings,
790
759
  :restrictions => SafeCURIEorCURIEorURI.fetch(@version, []))
791
- new_subject ||= process_uri(element, src, evaluation_context, base,
760
+ new_subject ||= process_uri(element, attrs[:src], evaluation_context, base,
792
761
  :uri_mappings => uri_mappings,
793
762
  :restrictions => [:uri]) if @version == :"rdfa1.0"
794
763
 
764
+ # if the @typeof attribute is present, set typed resource to new subject
765
+ typed_resource = new_subject if attrs[:typeof]
766
+
795
767
  # If no URI is provided then the first match from the following rules will apply
796
- new_subject ||= if element == @doc.root && base
768
+ new_subject ||= if element == root && base
797
769
  uri(base)
798
770
  elsif [:xhtml1, :xhtml5, :html4, :html5].include?(@host_language) && element.name =~ /^(head|body)$/
799
771
  # From XHTML+RDFa 1.1:
800
772
  # if no URI is provided, then first check to see if the element is the head or body element.
801
773
  # If it is, then act as if there is an empty @about present, and process it according to the rule for @about.
802
774
  uri(base)
803
- elsif element.attributes['typeof']
775
+ elsif attrs[:typeof] && @version == :"rdfa1.0"
804
776
  RDF::Node.new
805
777
  else
806
778
  # if it's null, it's null and nothing changes
@@ -809,32 +781,49 @@ module RDF::RDFa
809
781
  end
810
782
 
811
783
  # Then the current object resource is set to the URI obtained from the first match from the following rules:
812
- current_object_resource = if resource
813
- process_uri(element, resource, evaluation_context, base,
784
+ current_object_resource = if attrs[:resource]
785
+ process_uri(element, attrs[:resource], evaluation_context, base,
814
786
  :uri_mappings => uri_mappings,
815
787
  :restrictions => SafeCURIEorCURIEorURI.fetch(@version, []))
816
- elsif href
817
- process_uri(element, href, evaluation_context, base,
788
+ elsif attrs[:href]
789
+ process_uri(element, attrs[:href], evaluation_context, base,
818
790
  :restrictions => [:uri])
819
- elsif src && @version != :"rdfa1.0"
820
- process_uri(element, src, evaluation_context, base,
791
+ elsif attrs[:src] && @version != :"rdfa1.0"
792
+ process_uri(element, attrs[:src], evaluation_context, base,
821
793
  :restrictions => [:uri])
794
+ elsif attrs[:typeof] && !attrs[:about] && @version != :"rdfa1.0"
795
+ # otherwise, if @typeof is present and @about is not and the incomplete triples
796
+ # within the current context is empty, use a newly created bnode
797
+ RDF::Node.new
822
798
  end
823
799
 
824
- add_debug(element, "[Step 6] new_subject: #{new_subject}, current_object_resource = #{current_object_resource.nil? ? 'nil' : current_object_resource}")
800
+ # and also set the value typed resource to this bnode
801
+ if attrs[:typeof]
802
+ if @version == :"rdfa1.0"
803
+ typed_resource = new_subject
804
+ else
805
+ typed_resource = current_object_resource if !attrs[:about]
806
+ end
807
+ end
808
+
809
+ add_debug(element) {
810
+ "[Step 6] new_subject: #{new_subject}, " +
811
+ "current_object_resource = #{current_object_resource.nil? ? 'nil' : current_object_resource} " +
812
+ "typed_resource: #{typed_resource.to_ntriples rescue 'nil'}, "
813
+ }
825
814
  end
826
815
 
827
816
  # Process @typeof if there is a subject [Step 7]
828
- if new_subject and typeof
817
+ if typed_resource
829
818
  # Typeof is TERMorCURIEorAbsURIs
830
- types = process_uris(element, typeof, evaluation_context, base,
819
+ types = process_uris(element, attrs[:typeof], evaluation_context, base,
831
820
  :uri_mappings => uri_mappings,
832
821
  :term_mappings => term_mappings,
833
822
  :vocab => default_vocabulary,
834
823
  :restrictions => TERMorCURIEorAbsURI.fetch(@version, []))
835
- add_debug(element, "typeof: #{typeof}")
824
+ add_debug(element, "[Step 7] typeof: #{attrs[:typeof]}")
836
825
  types.each do |one_type|
837
- add_triple(element, new_subject, RDF["type"], one_type)
826
+ add_triple(element, typed_resource, RDF["type"], one_type)
838
827
  end
839
828
  end
840
829
 
@@ -855,16 +844,17 @@ module RDF::RDFa
855
844
  #
856
845
  # If the current element has a @inlist attribute, add the property to the
857
846
  # list associated with that property, creating a new list if necessary.
858
- if new_subject and current_object_resource
847
+ if new_subject && current_object_resource && (attrs[:rel] || attrs[:rev])
848
+ add_debug(element) {"[Step 9] rels: #{rels.inspect} revs: #{revs.inspect}"}
859
849
  rels.each do |r|
860
- if inlist
850
+ if attrs[:inlist]
861
851
  # If the current list mapping does not contain a list associated with this IRI,
862
852
  # instantiate a new list
863
853
  unless list_mapping[r]
864
854
  list_mapping[r] = RDF::List.new
865
855
  add_debug(element) {"list(#{r}): create #{list_mapping[r].inspect}"}
866
856
  end
867
- add_debug(element, "[Step 9] add #{current_object_resource.to_ntriples} to #{r} #{list_mapping[r].inspect}")
857
+ add_debug(element) {"[Step 9] add #{current_object_resource.to_ntriples} to #{r} #{list_mapping[r].inspect}"}
868
858
  list_mapping[r] << current_object_resource
869
859
  else
870
860
  add_triple(element, new_subject, r, current_object_resource)
@@ -874,7 +864,7 @@ module RDF::RDFa
874
864
  revs.each do |r|
875
865
  add_triple(element, current_object_resource, r, new_subject)
876
866
  end
877
- elsif rel || rev
867
+ elsif attrs[:rel] || attrs[:rev]
878
868
  # Incomplete triples and bnode creation [Step 10]
879
869
  add_debug(element) {"[Step 10] incompletes: rels: #{rels}, revs: #{revs}"}
880
870
  current_object_resource = RDF::Node.new
@@ -884,7 +874,7 @@ module RDF::RDFa
884
874
  # lists: Save into list, don't generate triple
885
875
 
886
876
  rels.each do |r|
887
- if inlist
877
+ if attrs[:inlist]
888
878
  # If the current list mapping does not contain a list associated with this IRI,
889
879
  # instantiate a new list
890
880
  unless list_mapping[r]
@@ -906,8 +896,8 @@ module RDF::RDFa
906
896
  #
907
897
  # If the current element has a @inlist attribute, add the property to the
908
898
  # list associated with that property, creating a new list if necessary.
909
- if property
910
- properties = process_uris(element, property, evaluation_context, base,
899
+ if attrs[:property]
900
+ properties = process_uris(element, attrs[:property], evaluation_context, base,
911
901
  :uri_mappings => uri_mappings,
912
902
  :term_mappings => term_mappings,
913
903
  :vocab => default_vocabulary,
@@ -917,27 +907,23 @@ module RDF::RDFa
917
907
  if p.is_a?(RDF::URI)
918
908
  false
919
909
  else
920
- add_debug(element, "[Step 11] predicate #{p.to_ntriples} must be a URI")
910
+ add_warning(element, "[Step 11] predicate #{p.to_ntriples} must be a URI")
921
911
  true
922
912
  end
923
913
  end
924
914
 
925
- # get the literal datatype
926
- children_node_types = element.children.collect{|c| c.class}.uniq
927
-
928
- # the following 3 IF clauses should be mutually exclusive. Written as is to prevent extensive indentation.
929
- datatype = process_uri(element, datatype, evaluation_context, base,
915
+ datatype = process_uri(element, attrs[:datatype], evaluation_context, base,
930
916
  :uri_mappings => uri_mappings,
931
917
  :term_mappings => term_mappings,
932
918
  :vocab => default_vocabulary,
933
- :restrictions => TERMorCURIEorAbsURI.fetch(@version, [])) unless datatype.to_s.empty?
919
+ :restrictions => TERMorCURIEorAbsURI.fetch(@version, [])) unless attrs[:datatype].to_s.empty?
934
920
  begin
935
- current_object_literal = if !datatype.to_s.empty? && datatype.to_s != RDF.XMLLiteral.to_s
921
+ current_property_value = if datatype && datatype != RDF.XMLLiteral
936
922
  # typed literal
937
923
  add_debug(element, "[Step 11] typed literal (#{datatype})")
938
- RDF::Literal.new(content || element.inner_text.to_s, :datatype => datatype, :language => language, :validate => validate?, :canonicalize => canonicalize?)
924
+ RDF::Literal.new(attrs[:content] || element.inner_text.to_s, :datatype => datatype, :language => language, :validate => validate?, :canonicalize => canonicalize?)
939
925
  elsif @version == :"rdfa1.1"
940
- if datatype.to_s == RDF.XMLLiteral.to_s
926
+ if datatype == RDF.XMLLiteral
941
927
  # XML Literal
942
928
  add_debug(element) {"[Step 11(1.1)] XML Literal: #{element.inner_html}"}
943
929
 
@@ -948,35 +934,73 @@ module RDF::RDFa
948
934
  # generating the serialized element definition. For avoidance of doubt, any re-declarations on the
949
935
  # child node must take precedence over declarations that were active on the current node.
950
936
  begin
951
- RDF::Literal.new(element.inner_html,
952
- :datatype => RDF.XMLLiteral,
953
- :language => language,
954
- :namespaces => namespaces,
955
- :validate => validate?,
956
- :canonicalize => canonicalize?)
937
+ c14nxl = element.children.c14nxl(
938
+ :library => @library,
939
+ :language => language,
940
+ :namespaces => {nil => XHTML}.merge(namespaces))
941
+ RDF::Literal.new(c14nxl,
942
+ :library => @library,
943
+ :datatype => RDF.XMLLiteral,
944
+ :validate => validate?,
945
+ :canonicalize => canonicalize?)
957
946
  rescue ArgumentError => e
958
947
  add_error(element, e.message)
959
948
  end
949
+ elsif attrs[:datetime]
950
+ # Lexically scan value and assign appropriate type, otherwise, leave untyped
951
+ v = element.attribute('datetime').to_s
952
+ datatype = %w(Date Time DateTime Duration).map {|t| RDF::Literal.const_get(t)}.detect do |dt|
953
+ v.match(dt::GRAMMAR)
954
+ end || RDF::Literal
955
+ add_debug(element) {"[Step 11(1.1)] datetime literal: #{v.class}"}
956
+ datatype.new(v)
957
+ elsif attrs[:content]
958
+ # plain literal
959
+ add_debug(element, "[Step 11(1.1)] plain literal (content)")
960
+ RDF::Literal.new(attrs[:content], :language => language, :validate => validate?, :canonicalize => canonicalize?)
961
+ elsif attrs[:value]
962
+ # plain literal
963
+ add_debug(element, "[Step 11(1.1)] plain literal (value)")
964
+ RDF::Literal.new(attrs[:value], :validate => validate?, :canonicalize => canonicalize?)
965
+ elsif (attrs[:resource] || attrs[:href] || attrs[:src] || attrs[:data]) &&
966
+ !(attrs[:rel] || attrs[:rev]) &&
967
+ evaluation_context.incomplete_triples.empty? &&
968
+ @version != :"rdfa1.0"
969
+ if attrs[:resource]
970
+ add_debug(element, "[Step 11(1.1)] IRI literal (resource)")
971
+ process_uri(element, attrs[:resource], evaluation_context, base,
972
+ :uri_mappings => uri_mappings,
973
+ :restrictions => SafeCURIEorCURIEorURI.fetch(@version, []))
974
+ else
975
+ add_debug(element, "[Step 11(1.1)] IRI literal (href/src/data)")
976
+ process_uri(element, (attrs[:href] || attrs[:src] || attrs[:data]), evaluation_context, base, :restrictions => [:uri])
977
+ end
978
+ elsif typed_resource && !attrs[:about] && evaluation_context.incomplete_triples.empty? && @version != :"rdfa1.0"
979
+ add_debug(element, "[Step 11(1.1)] typed_resource")
980
+ typed_resource
960
981
  else
961
982
  # plain literal
962
- add_debug(element, "[Step 11(1.1)] plain literal")
963
- RDF::Literal.new(content || element.inner_text.to_s, :language => language, :validate => validate?, :canonicalize => canonicalize?)
983
+ add_debug(element, "[Step 11(1.1)] plain literal (inner text)")
984
+ RDF::Literal.new(element.inner_text.to_s, :language => language, :validate => validate?, :canonicalize => canonicalize?)
964
985
  end
965
986
  else
966
- if content || (children_node_types == [Nokogiri::XML::Text]) || (element.children.length == 0) || datatype == ""
987
+ if element.text_content? || (element.children.length == 0) || attrs[:datatype] == ""
967
988
  # plain literal
968
989
  add_debug(element, "[Step 11 (1.0)] plain literal")
969
- RDF::Literal.new(content || element.inner_text.to_s, :language => language, :validate => validate?, :canonicalize => canonicalize?)
970
- elsif children_node_types != [Nokogiri::XML::Text] and (datatype == nil or datatype.to_s == RDF.XMLLiteral.to_s)
990
+ RDF::Literal.new(attrs[:content] || element.inner_text.to_s, :language => language, :validate => validate?, :canonicalize => canonicalize?)
991
+ elsif !element.text_content? and (datatype == nil or datatype.to_s == RDF.XMLLiteral.to_s)
971
992
  # XML Literal
972
993
  add_debug(element) {"[Step 11 (1.0)] XML Literal: #{element.inner_html}"}
973
994
  recurse = false
974
- RDF::Literal.new(element.inner_html,
975
- :datatype => RDF.XMLLiteral,
976
- :language => language,
977
- :namespaces => namespaces,
978
- :validate => validate?,
979
- :canonicalize => canonicalize?)
995
+ c14nxl = element.children.c14nxl(
996
+ :library => @library,
997
+ :language => language,
998
+ :namespaces => {nil => XHTML}.merge(namespaces))
999
+ RDF::Literal.new(c14nxl,
1000
+ :library => @library,
1001
+ :datatype => RDF.XMLLiteral,
1002
+ :validate => validate?,
1003
+ :canonicalize => canonicalize?)
980
1004
  end
981
1005
  end
982
1006
  rescue ArgumentError => e
@@ -986,22 +1010,22 @@ module RDF::RDFa
986
1010
  # add each property
987
1011
  properties.each do |p|
988
1012
  # Lists: If element has an @inlist attribute, add the value to a list
989
- if inlist
1013
+ if attrs[:inlist]
990
1014
  # If the current list mapping does not contain a list associated with this IRI,
991
1015
  # instantiate a new list
992
1016
  unless list_mapping[p]
993
1017
  list_mapping[p] = RDF::List.new
994
1018
  add_debug(element) {"[Step 11] lists(#{p}): create #{list_mapping[p].inspect}"}
995
1019
  end
996
- add_debug(element) {"[Step 11] add #{current_object_literal.to_ntriples} to #{p.to_ntriples} #{list_mapping[p].inspect}"}
997
- list_mapping[p] << current_object_literal
1020
+ add_debug(element) {"[Step 11] add #{current_property_value.to_ntriples} to #{p.to_ntriples} #{list_mapping[p].inspect}"}
1021
+ list_mapping[p] << current_property_value
998
1022
  elsif new_subject
999
- add_triple(element, new_subject, p, current_object_literal)
1023
+ add_triple(element, new_subject, p, current_property_value)
1000
1024
  end
1001
1025
  end
1002
1026
  end
1003
1027
 
1004
- if not skip and new_subject && !evaluation_context.incomplete_triples.empty?
1028
+ if !skip and new_subject && !evaluation_context.incomplete_triples.empty?
1005
1029
  # Complete the incomplete triples from the evaluation context [Step 12]
1006
1030
  add_debug(element) do
1007
1031
  "[Step 12] complete incomplete triples: " +
@@ -1061,7 +1085,7 @@ module RDF::RDFa
1061
1085
 
1062
1086
  element.children.each do |child|
1063
1087
  # recurse only if it's an element
1064
- traverse(child, new_ec) if child.class == Nokogiri::XML::Element
1088
+ traverse(child, new_ec) if child.element?
1065
1089
  end
1066
1090
 
1067
1091
  # Step 14: after traversing through child elements, for each list associated with