Update to latest jsonld.js.
authorDave Longley <dlongley@digitalbazaar.com>
Wed, 06 Feb 2013 22:58:51 -0500
changeset 1192 07a1f5841486
parent 1191 83f71b5d3716
child 1193 ef71a98c2b3a
Update to latest jsonld.js.
playground/jsonld.js
--- a/playground/jsonld.js	Wed Feb 06 17:42:13 2013 -0500
+++ b/playground/jsonld.js	Wed Feb 06 22:58:51 2013 -0500
@@ -242,7 +242,7 @@
 
   // resolve all @context URLs in the input
   input = _clone(input);
-  _resolveContextUrls(input, options.resolver, function(err, input) {
+  _resolveContextUrls(input, options, function(err, input) {
     if(err) {
       return callback(err);
     }
@@ -253,7 +253,9 @@
         activeCtx, null, input, options, false);
 
       // blank nodes must all be renamed if any were renamed
-      // during expansion ... otherwise there may be conflicts
+      // during expansion ... otherwise there may be conflicts, this
+      // also means tracking all bnodes in case renaming must occur
+      // to avoid double-renaming
       /*if(activeCtx.namer && activeCtx.namer.counter > 0) {
         _labelBlankNodes(activeCtx.namer, expanded);
       }*/
@@ -773,7 +775,7 @@
  */
 jsonld.urlResolver = function(url, callback) {
   return callback(new JsonLdError(
-    'Could not resolve @context URL. URL resolution not implemented.',
+    'Could not resolve @context URL. URL derefencing not implemented.',
     'jsonld.ContextUrlError'));
 };
 
@@ -886,6 +888,7 @@
  */
 jsonld.urlResolvers['node'] = function() {
   var request = require('request');
+  var http = require('http');
   var cache = new jsonld.ContextCache();
   return function(url, callback) {
     var ctx = cache.get(url);
@@ -893,6 +896,12 @@
       return callback(null, ctx);
     }
     request(url, function(err, res, body) {
+      if(!err && res.statusCode >= 400) {
+        var statusText = http.STATUS_CODES[res.statusCode];
+        err = new JsonLdError(
+          'URL could not be dereferenced: ' + statusText,
+          'jsonld.InvalidUrl', {url: url, httpStatusCode: res.statusCode});
+      }
       if(!err) {
         cache.set(url, body);
       }
@@ -963,7 +972,7 @@
   if(_isObject(localCtx) && !('@context' in localCtx)) {
     localCtx = {'@context': localCtx};
   }
-  _resolveContextUrls(localCtx, options.resolver, function(err, ctx) {
+  _resolveContextUrls(localCtx, options, function(err, ctx) {
     if(err) {
       return callback(err);
     }
@@ -3648,7 +3657,7 @@
   else if(a === b) {
     return 0;
   }
-  return (a.length === b.length && a < b) ? -1 : 1;
+  return (a < b) ? -1 : 1;
 }
 
 /**
@@ -3693,13 +3702,16 @@
       var termInfo = typeOrLanguageValueMap[option];
 
       // see if a property generator matches
-      if(_isObject(parent) && termInfo.propertyGenerators) {
+      if(_isObject(parent)) {
         for(var pi = 0; pi < termInfo.propertyGenerators.length; ++pi) {
           var propertyGenerator = termInfo.propertyGenerators[pi];
           var iris = activeCtx.mappings[propertyGenerator]['@id'];
           var match = true;
-          for(var ii = 0; match && ii < iris.length; ++ii) {
-            match = (iris[ii] in parent);
+          for(var ii = 0; ii < iris.length; ++ii) {
+            if(!(iris[ii] in parent)) {
+              match = false;
+              break;
+            }
           }
           if(match) {
             term = propertyGenerator;
@@ -3906,7 +3918,7 @@
  * property.
  *
  * @param activeCtx the active context.
- * @param activeProperty the active property that points to the element.
+ * @param activeProperty the active property that points to the value.
  * @param value the value to compact.
  *
  * @return the compaction result.
@@ -4244,16 +4256,6 @@
     mapping['@language'] = language;
   }
 
-  // merge onto parent mapping if one exists for a prefix
-  if(prefix !== null && activeCtx.mappings[prefix]) {
-    // FIXME: check to see if additional clone is necessary here
-    var child = mapping;
-    mapping = _clone(activeCtx.mappings[prefix]);
-    for(var k in child) {
-      mapping[k] = child[k];
-    }
-  }
-
   // define term mapping
   activeCtx.mappings[term] = mapping;
   defined[term] = true;
@@ -4270,9 +4272,9 @@
  *          base: true to resolve against the base IRI, false not to.
  *          vocab: true to concatenate after @vocab, false not to.
  * @param localCtx the local context being processed (only given if called
- *          during document processing).
+ *          during context processing).
  * @param defined a map for tracking cycles in context definitions (only given
- *          if called during document processing).
+ *          if called during context processing).
  *
  * @return the expanded value.
  */
@@ -4379,6 +4381,15 @@
  * @return the absolute IRI.
  */
 function _prependBase(base, iri) {
+  // already an absolute IRI
+  if(iri.indexOf(':') !== -1) {
+    return iri;
+  }
+
+  if(_isString(base)) {
+    base = jsonld.url.parse(base || '');
+    base.pathname = base.pathname || '';
+  }
   var authority = (base.host || '');
   var rel = jsonld.url.parse(iri);
   rel.pathname = (rel.pathname || '');
@@ -4945,21 +4956,22 @@
  * @param urls a map of URLs (url => false/@contexts).
  * @param replace true to replace the URLs in the given input with the
  *           @contexts from the urls map, false not to.
+ * @param base the base IRI to use to resolve relative IRIs.
  *
  * @return true if new URLs to resolve were found, false if not.
  */
-function _findContextUrls(input, urls, replace) {
+function _findContextUrls(input, urls, replace, base) {
   var count = Object.keys(urls).length;
   if(_isArray(input)) {
     for(var i in input) {
-      _findContextUrls(input[i], urls, replace);
+      _findContextUrls(input[i], urls, replace, base);
     }
     return (count < Object.keys(urls).length);
   }
   else if(_isObject(input)) {
     for(var key in input) {
       if(key !== '@context') {
-        _findContextUrls(input[key], urls, replace);
+        _findContextUrls(input[key], urls, replace, base);
         continue;
       }
 
@@ -4972,6 +4984,7 @@
         for(var i = 0; i < length; ++i) {
           var _ctx = ctx[i];
           if(_isString(_ctx)) {
+            _ctx = _prependBase(base, _ctx);
             // replace w/@context if requested
             if(replace) {
               _ctx = urls[_ctx];
@@ -4994,6 +5007,7 @@
       }
       // string @context
       else if(_isString(ctx)) {
+        ctx = _prependBase(base, ctx);
         // replace w/@context if requested
         if(replace) {
           input[key] = urls[ctx];
@@ -5015,16 +5029,18 @@
  * with the JSON @context found at that URL.
  *
  * @param input the JSON-LD input with possible contexts.
- * @param resolver(url, callback(err, jsonCtx)) the URL resolver to use.
+ * @param options the options to use:
+ *          resolver(url, callback(err, jsonCtx)) the URL resolver to use.
  * @param callback(err, input) called once the operation completes.
  */
-function _resolveContextUrls(input, resolver, callback) {
+function _resolveContextUrls(input, options, callback) {
   // if any error occurs during URL resolution, quit
   var error = null;
   var regex = /(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
 
   // recursive resolver
-  var resolve = function(input, cycles, resolver, callback) {
+  var resolver = options.resolver;
+  var resolve = function(input, cycles, resolver, base, callback) {
     if(Object.keys(cycles).length > MAX_CONTEXT_URLS) {
       error = new JsonLdError(
         'Maximum number of @context URLs exceeded.',
@@ -5038,12 +5054,12 @@
     // finished will be called once the URL queue is empty
     var finished = function() {
       // replace all URLs in the input
-      _findContextUrls(input, urls, true);
+      _findContextUrls(input, urls, true, base);
       callback(null, input);
     };
 
     // find all URLs in the given input
-    if(!_findContextUrls(input, urls, false)) {
+    if(!_findContextUrls(input, urls, false, base)) {
       // no new URLs in input
       finished();
     }
@@ -5093,10 +5109,19 @@
           }
 
           // ensure ctx is an object
-          if(err || !_isObject(ctx)) {
+          if(err) {
             err = new JsonLdError(
-              'URL does not resolve to a valid JSON-LD object.',
-              'jsonld.InvalidUrl', {url: url});
+              'Derefencing a URL did not result in a valid JSON-LD object. ' +
+              'Possible causes are an inaccessible URL perhaps due to ' +
+              'a same-origin policy (ensure the server uses CORS if you are ' +
+              'using client-side JavaScript) or a non-JSON response.',
+              'jsonld.InvalidUrl', {url: url, cause: err});
+          }
+          else if(!_isObject(ctx)) {
+            err = new JsonLdError(
+              'Derefencing a URL did not result in a JSON object. The ' +
+              'response was valid JSON, but it was not a JSON object.',
+              'jsonld.InvalidUrl', {url: url, cause: err});
           }
           if(err) {
             error = err;
@@ -5109,7 +5134,7 @@
           }
 
           // recurse
-          resolve(ctx, _cycles, resolver, function(err, ctx) {
+          resolve(ctx, _cycles, resolver, url, function(err, ctx) {
             if(err) {
               return callback(err);
             }
@@ -5123,7 +5148,7 @@
       }(queue[i]));
     }
   };
-  resolve(input, {}, resolver, callback);
+  resolve(input, {}, resolver, options.base, callback);
 }
 
 // define js 1.8.5 Object.keys method if not present