Update to latest jsonld.js.
authorDave Longley <dlongley@digitalbazaar.com>
Wed, 26 Sep 2012 14:12:16 -0400
changeset 877 ed059e5064b4
parent 876 9f33e4579716
child 878 033b0b83d362
Update to latest jsonld.js.
playground/jsonld.js
--- a/playground/jsonld.js	Wed Sep 26 14:10:07 2012 -0400
+++ b/playground/jsonld.js	Wed Sep 26 14:12:16 2012 -0400
@@ -384,7 +384,11 @@
  * @param [options] the options to use:
  *          [format] the format if input is not an array:
  *            'application/nquads' for N-Quads (default).
- *          [notType] true to use rdf:type, false to use @type (default).
+ *          [useRdfType] true to use rdf:type, false to use @type
+ *            (default: false).
+ *          [useNativeTypes] true to convert XSD types into native types
+ *            (boolean, integer, double), false not to (default: true).
+ *
  * @param callback(err, output) called once the operation completes.
  */
 jsonld.fromRDF = function(statements) {
@@ -399,8 +403,11 @@
   callback = arguments[callbackArg];
 
   // set default options
-  if(!('notType' in options)) {
-    options.notType = false;
+  if(!('useRdfType' in options)) {
+    options.useRdfType = false;
+  }
+  if(!('useNativeTypes' in options)) {
+    options.useNativeTypes = true;
   }
 
   if(!('format' in options) && !_isArray(statements)) {
@@ -1003,6 +1010,7 @@
 var XSD_BOOLEAN = 'http://www.w3.org/2001/XMLSchema#boolean';
 var XSD_DOUBLE = 'http://www.w3.org/2001/XMLSchema#double';
 var XSD_INTEGER = 'http://www.w3.org/2001/XMLSchema#integer';
+var XSD_STRING = 'http://www.w3.org/2001/XMLSchema#string';
 
 var RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
 var RDF_FIRST = RDF + 'first';
@@ -1230,6 +1238,12 @@
  */
 Processor.prototype.expand = function(
   ctx, property, element, options, propertyIsList) {
+  if(typeof element === 'undefined') {
+    throw new JsonLdError(
+      'Invalid JSON-LD syntax; undefined element.',
+      'jsonld.SyntaxError');
+  }
+
   // recursively expand array
   if(_isArray(element)) {
     var rval = [];
@@ -1688,7 +1702,7 @@
         entry = listMap[s];
       }
       // set object value
-      entry.first = _rdfToObject(o);
+      entry.first = _rdfToObject(o, options.useNativeTypes);
       continue;
     }
 
@@ -1727,13 +1741,13 @@
     }
 
     // convert to @type unless options indicate to treat rdf:type as property
-    if(p === RDF_TYPE && !options.notType) {
+    if(p === RDF_TYPE && !options.useRdfType) {
       // add value of object as @type
       jsonld.addValue(value, '@type', o.nominalValue, {propertyIsArray: true});
     }
     else {
       // add property to value as needed
-      var object = _rdfToObject(o);
+      var object = _rdfToObject(o, options.useNativeTypes);
       jsonld.addValue(value, p, object, {propertyIsArray: true});
 
       // a bnode might be the beginning of a list, so add it to the list map
@@ -1950,33 +1964,33 @@
       if(_isBoolean(value) || _isNumber(value)) {
         // convert to XSD datatype
         if(_isBoolean(value)) {
-          value = String(value);
+          value = value.toString();
           datatype = datatype || XSD_BOOLEAN;
         }
         else if(_isDouble(value)) {
-          // printf('%1.15e') equivalent
-          value = value.toExponential(15).replace(
-            /(e(?:\+|-))([0-9])$/, '$10$2');
+          // canonical double representation
+          value = value.toExponential(15).replace(/(\d)0*e\+?/, '$1E');
           datatype = datatype || XSD_DOUBLE;
         }
         else {
-          value = String(value);
+          value = value.toFixed(0);
           datatype = datatype || XSD_INTEGER;
         }
       }
 
+      // default to xsd:string datatype
+      datatype = datatype || XSD_STRING;
+
       var object = {
         nominalValue: value,
         interfaceName: 'LiteralNode'
       };
 
-      if(datatype !== null) {
-        object.datatype = {
-          nominalValue: datatype,
-          interfaceName: 'IRI'
-        };
-      }
-      else if('@language' in element) {
+      object.datatype = {
+        nominalValue: datatype,
+        interfaceName: 'IRI'
+      };
+      if('@language' in element && datatype === XSD_STRING) {
         object.language = element['@language'];
       }
 
@@ -2091,10 +2105,11 @@
  * Converts an RDF statement object to a JSON-LD object.
  *
  * @param o the RDF statement object to convert.
+ * @param useNativeTypes true to output native types, false not to.
  *
  * @return the JSON-LD object.
  */
-function _rdfToObject(o) {
+function _rdfToObject(o, useNativeTypes) {
   // convert empty list
   if(o.interfaceName === 'IRI' && o.nominalValue === RDF_NIL) {
     return {'@list': []};
@@ -2110,22 +2125,39 @@
 
   // add datatype
   if('datatype' in o) {
-    /*
     var type = o.datatype.nominalValue;
-    // use native datatypes for certain xsd types
-    if(type === XSD_BOOLEAN) {
-      rval = !(rval['@value'] === 'false' || rval['@value'] === '0');
-    }
-    else if(type === XSD_INTEGER) {
-      rval = parseInt(rval['@value']);
-    }
-    else if(type === XSD_DOUBLE) {
-      rval = parseFloat(rval['@value']);
-    }*/
-    rval['@type'] = o.datatype.nominalValue;
+    if(useNativeTypes) {
+      // use native datatypes for certain xsd types
+      if(type === XSD_BOOLEAN) {
+        if(rval['@value'] === 'true') {
+          rval['@value'] = true;
+        }
+        else if(rval['@value'] === 'false') {
+          rval['@value'] = false;
+        }
+      }
+      else if(_isNumeric(rval['@value'])) {
+        if(type === XSD_INTEGER) {
+          var i = parseInt(rval['@value']);
+          if(i.toFixed(0) === rval['@value']) {
+            rval['@value'] = i;
+          }
+        }
+        else if(type === XSD_DOUBLE) {
+          rval['@value'] = parseFloat(rval['@value']);
+        }
+      }
+      // do not add xsd:string type
+      if(type !== XSD_STRING) {
+        rval['@type'] = type;
+      }
+    }
+    else {
+      rval['@type'] = type;
+    }
   }
   // add language
-  else if('language' in o) {
+  if('language' in o) {
     rval['@language'] = o.language;
   }
 
@@ -3115,6 +3147,19 @@
     }
   }
 
+  // no matching terms, use @vocab if available
+  if(terms.length === 0 && ctx['@vocab']) {
+    // determine if vocab is a prefix of the iri
+    var vocab = ctx['@vocab'];
+    if(iri.indexOf(vocab) === 0) {
+      // use suffix as relative iri if it is not a term in the active context
+      var suffix = iri.substr(vocab.length);
+      if(!(suffix in ctx.mappings)) {
+        return suffix;
+      }
+    }
+  }
+
   // no term matches, add possible CURIEs
   if(terms.length === 0) {
     for(var term in ctx.mappings) {
@@ -3136,7 +3181,7 @@
     }
   }
 
-  // no matching terms
+  // no matching terms,
   if(terms.length === 0) {
     // use iri
     return iri;
@@ -3187,6 +3232,30 @@
   var value = ctx[key];
 
   if(_isKeyword(key)) {
+    // support @vocab
+    if(key === '@vocab') {
+      if(value !== null && !_isString(value)) {
+        throw new JsonLdError(
+          'Invalid JSON-LD syntax; the value of "@vocab" in a ' +
+          '@context must be a string or null.',
+          'jsonld.SyntaxError', {context: ctx});
+      }
+      if(!_isAbsoluteIri(value)) {
+        throw new JsonLdError(
+          'Invalid JSON-LD syntax; the value of "@vocab" in a ' +
+          '@context must be an absolute IRI.',
+          'jsonld.SyntaxError', {context: ctx});
+      }
+      if(value === null) {
+        delete activeCtx['@vocab'];
+      }
+      else {
+        activeCtx['@vocab'] = value;
+      }
+      defined[key] = true;
+      return;
+    }
+
     // only @language is permitted
     if(key !== '@language') {
       throw new JsonLdError(
@@ -3415,8 +3484,14 @@
     return value;
   }
 
+  // prepend vocab
+  if(ctx['@vocab']) {
+    value = _prependBase(ctx['@vocab'], value);
+  }
   // prepend base
-  value = _prependBase(base, value);
+  else {
+    value = _prependBase(base, value);
+  }
 
   // value must now be an absolute IRI
   if(!_isAbsoluteIri(value)) {
@@ -3481,8 +3556,12 @@
     return term;
   }
 
+  // use vocab
+  if(ctx['@vocab']) {
+    term = _prependBase(ctx['@vocab'], term);
+  }
   // prepend base to term
-  if(!_isUndefined(base)) {
+  else if(!_isUndefined(base)) {
     term = _prependBase(base, term);
   }
 
@@ -3529,7 +3608,8 @@
       '@preserve': [],
       '@set': [],
       '@type': [],
-      '@value': []
+      '@value': [],
+      '@vocab': []
     }
   };
 }
@@ -3570,6 +3650,7 @@
     case '@set':
     case '@type':
     case '@value':
+    case '@vocab':
       return true;
     }
   }
@@ -3677,6 +3758,17 @@
 }
 
 /**
+ * Returns true if the given value is numeric.
+ *
+ * @param v the value to check.
+ *
+ * @return true if the value is numeric, false if not.
+ */
+function _isNumeric(v) {
+  return !isNaN(parseFloat(v)) && isFinite(v);
+}
+
+/**
  * Returns true if the given value is a Boolean.
  *
  * @param v the value to check.
@@ -4199,7 +4291,7 @@
       .replace(/\r/g, '\\r')
       .replace(/\"/g, '\\"');
     quad += '"' + escaped + '"';
-    if('datatype' in o) {
+    if('datatype' in o && o.datatype.nominalValue !== XSD_STRING) {
       quad += '^^<' + o.datatype.nominalValue + '>';
     }
     else if('language' in o) {