rdf-rdfa 0.3.7 → 0.3.8

Sign up to get free protection for your applications and to get access to all the features.
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