Update local jsonld.js to latest version.
authorDave Longley <dlongley@digitalbazaar.com>
Tue, 29 Jul 2014 13:45:51 -0400
changeset 2171 5818da89a6b6
parent 2170 5e345ac627c6
child 2172 113cd80d77e6
Update local jsonld.js to latest version.
playground/jsonld.js
--- a/playground/jsonld.js	Sun Jul 27 14:57:42 2014 -0700
+++ b/playground/jsonld.js	Tue Jul 29 13:45:51 2014 -0400
@@ -443,6 +443,7 @@
  *          [expandContext] a context to expand with.
  *          [embed] default @embed flag (default: true).
  *          [explicit] default @explicit flag (default: false).
+ *          [requireAll] default @requireAll flag (default: true).
  *          [omitDefault] default @omitDefault flag (default: false).
  *          [documentLoader(url, callback(err, remoteDoc))] the document loader.
  * @param callback(err, framed) called once the operation completes.
@@ -472,6 +473,9 @@
     options.embed = true;
   }
   options.explicit = options.explicit || false;
+  if(!('requireAll' in options)) {
+    options.requireAll = true;
+  }
   options.omitDefault = options.omitDefault || false;
 
   jsonld.nextTick(function() {
@@ -2866,6 +2870,8 @@
 Processor.prototype.fromRDF = function(dataset, options, callback) {
   var defaultGraph = {};
   var graphMap = {'@default': defaultGraph};
+  // TODO: seems like 'usages' could be replaced with this single map
+  var nodeReferences = {};
 
   for(var name in dataset) {
     var graph = dataset[name];
@@ -2905,6 +2911,8 @@
       // object may be an RDF list/partial list node but we can't know easily
       // until all triples are read
       if(objectIsId) {
+        jsonld.addValue(
+          nodeReferences, o.value, node['@id'], {propertyIsArray: true});
         var object = nodeMap[o.value];
         if(!('usages' in object)) {
           object.usages = [];
@@ -2938,13 +2946,16 @@
       var listNodes = [];
 
       // ensure node is a well-formed list node; it must:
-      // 1. Be used only once in a list.
+      // 1. Be referenced only once and used only once in a list.
       // 2. Have an array for rdf:first that has 1 item.
       // 3. Have an array for rdf:rest that has 1 item.
       // 4. Have no keys other than: @id, usages, rdf:first, rdf:rest, and,
       //   optionally, @type where the value is rdf:List.
       var nodeKeyCount = Object.keys(node).length;
-      while(property === RDF_REST && node.usages.length === 1 &&
+      while(property === RDF_REST &&
+        nodeReferences[node['@id']] &&
+        nodeReferences[node['@id']].length === 1 &&
+        node.usages.length === 1 &&
         _isArray(node[RDF_FIRST]) && node[RDF_FIRST].length === 1 &&
         _isArray(node[RDF_REST]) && node[RDF_REST].length === 1 &&
         (nodeKeyCount === 4 || (nodeKeyCount === 5 && _isArray(node['@type']) &&
@@ -3262,7 +3273,7 @@
  */
 function _expandValue(activeCtx, activeProperty, value) {
   // nothing to expand
-  if(value === null) {
+  if(value === null || value === undefined) {
     return null;
   }
 
@@ -3304,6 +3315,10 @@
       rval['@language'] = language;
     }
   }
+  // do conversion of values that aren't basic JSON types to strings
+  if(['boolean', 'number', 'string'].indexOf(typeof value) === -1) {
+    value = value.toString();
+  }
   rval['@value'] = value;
 
   return rval;
@@ -3946,13 +3961,19 @@
   _validateFrame(state, frame);
   frame = frame[0];
 
-  // filter out subjects that match the frame
-  var matches = _filterSubjects(state, subjects, frame);
-
   // get flags for current frame
   var options = state.options;
   var embedOn = _getFrameFlag(frame, options, 'embed');
   var explicitOn = _getFrameFlag(frame, options, 'explicit');
+  var requireAllOn = _getFrameFlag(frame, options, 'requireAll');
+  var flags = {
+    embed: embedOn,
+    explicit: explicitOn,
+    requireAll: requireAllOn
+  };
+
+  // filter out subjects that match the frame
+  var matches = _filterSubjects(state, subjects, frame, flags);
 
   // add matches to output
   var ids = Object.keys(matches).sort();
@@ -4003,97 +4024,98 @@
     // not embedding, add output without any other properties
     if(!embedOn) {
       _addFrameOutput(state, parent, property, output);
-    } else {
-      // add embed meta info
-      state.embeds[id] = embed;
-
-      // iterate over subject properties
-      var subject = matches[id];
-      var props = Object.keys(subject).sort();
-      for(var i = 0; i < props.length; i++) {
-        var prop = props[i];
-
-        // copy keywords to output
-        if(_isKeyword(prop)) {
-          output[prop] = _clone(subject[prop]);
-          continue;
+      continue;
+    }
+
+    // add embed meta info
+    state.embeds[id] = embed;
+
+    // iterate over subject properties
+    var subject = matches[id];
+    var props = Object.keys(subject).sort();
+    for(var i = 0; i < props.length; i++) {
+      var prop = props[i];
+
+      // copy keywords to output
+      if(_isKeyword(prop)) {
+        output[prop] = _clone(subject[prop]);
+        continue;
+      }
+
+      // if property isn't in the frame
+      if(!(prop in frame)) {
+        // if explicit is off, embed values
+        if(!explicitOn) {
+          _embedValues(state, subject, prop, output);
         }
-
-        // if property isn't in the frame
-        if(!(prop in frame)) {
-          // if explicit is off, embed values
-          if(!explicitOn) {
-            _embedValues(state, subject, prop, output);
+        continue;
+      }
+
+      // add objects
+      var objects = subject[prop];
+      for(var oi = 0; oi < objects.length; ++oi) {
+        var o = objects[oi];
+
+        // recurse into list
+        if(_isList(o)) {
+          // add empty list
+          var list = {'@list': []};
+          _addFrameOutput(state, output, prop, list);
+
+          // add list objects
+          var src = o['@list'];
+          for(var n in src) {
+            o = src[n];
+            if(_isSubjectReference(o)) {
+              // recurse into subject reference
+              _frame(state, [o['@id']], frame[prop][0]['@list'],
+              list, '@list');
+            } else {
+              // include other values automatically
+              _addFrameOutput(state, list, '@list', _clone(o));
+            }
           }
           continue;
         }
 
-        // add objects
-        var objects = subject[prop];
-        for(var oi = 0; oi < objects.length; ++oi) {
-          var o = objects[oi];
-
-          // recurse into list
-          if(_isList(o)) {
-            // add empty list
-            var list = {'@list': []};
-            _addFrameOutput(state, output, prop, list);
-
-            // add list objects
-            var src = o['@list'];
-            for(var n in src) {
-              o = src[n];
-              if(_isSubjectReference(o)) {
-                // recurse into subject reference
-                _frame(state, [o['@id']], frame[prop][0]['@list'],
-                list, '@list');
-              } else {
-                // include other values automatically
-                _addFrameOutput(state, list, '@list', _clone(o));
-              }
-            }
-            continue;
-          }
-
-          if(_isSubjectReference(o)) {
-            // recurse into subject reference
-            _frame(state, [o['@id']], frame[prop], output, prop);
-          } else {
-            // include other values automatically
-            _addFrameOutput(state, output, prop, _clone(o));
-          }
+        if(_isSubjectReference(o)) {
+          // recurse into subject reference
+          _frame(state, [o['@id']], frame[prop], output, prop);
+        } else {
+          // include other values automatically
+          _addFrameOutput(state, output, prop, _clone(o));
         }
       }
-
-      // handle defaults
-      var props = Object.keys(frame).sort();
-      for(var i = 0; i < props.length; ++i) {
-        var prop = props[i];
-
-        // skip keywords
-        if(_isKeyword(prop)) {
-          continue;
+    }
+
+    // handle defaults
+    var props = Object.keys(frame).sort();
+    for(var i = 0; i < props.length; ++i) {
+      var prop = props[i];
+
+      // skip keywords
+      if(_isKeyword(prop)) {
+        continue;
+      }
+
+      // if omit default is off, then include default values for properties
+      // that appear in the next frame but are not in the matching subject
+      var next = frame[prop][0];
+      var omitDefaultOn = _getFrameFlag(next, options, 'omitDefault');
+      if(!omitDefaultOn && !(prop in output)) {
+        var preserve = '@null';
+        if('@default' in next) {
+          preserve = _clone(next['@default']);
         }
-
-        // if omit default is off, then include default values for properties
-        // that appear in the next frame but are not in the matching subject
-        var next = frame[prop][0];
-        var omitDefaultOn = _getFrameFlag(next, options, 'omitDefault');
-        if(!omitDefaultOn && !(prop in output)) {
-          var preserve = '@null';
-          if('@default' in next) {
-            preserve = _clone(next['@default']);
-          }
-          if(!_isArray(preserve)) {
-            preserve = [preserve];
-          }
-          output[prop] = [{'@preserve': preserve}];
+        if(!_isArray(preserve)) {
+          preserve = [preserve];
         }
-      }
-
-      // add output to parent
-      _addFrameOutput(state, parent, property, output);
-    }
+        output[prop] = [{'@preserve': preserve}];
+      }
+    }
+
+    // add output to parent
+    _addFrameOutput(state, parent, property, output);
   }
 }
 
@@ -4131,16 +4153,17 @@
  * @param state the current framing state.
  * @param subjects the set of subjects to filter.
  * @param frame the parsed frame.
+ * @param flags the frame flags.
  *
  * @return all of the matched subjects.
  */
-function _filterSubjects(state, subjects, frame) {
+function _filterSubjects(state, subjects, frame, flags) {
   // filter subjects in @id order
   var rval = {};
   for(var i = 0; i < subjects.length; ++i) {
     var id = subjects[i];
     var subject = state.subjects[id];
-    if(_filterSubject(subject, frame)) {
+    if(_filterSubject(subject, frame, flags)) {
       rval[id] = subject;
     }
   }
@@ -4152,10 +4175,11 @@
  *
  * @param subject the subject to check.
  * @param frame the frame to check.
+ * @param flags the frame flags.
  *
  * @return true if the subject matches, false if not.
  */
-function _filterSubject(subject, frame) {
+function _filterSubject(subject, frame, flags) {
   // check @type (object value means 'any' type, fall through to ducktyping)
   if('@type' in frame &&
     !(frame['@type'].length === 1 && _isObject(frame['@type'][0]))) {
@@ -4170,13 +4194,48 @@
   }
 
   // check ducktype
+  var wildcard = true;
+  var matchesSome = false;
   for(var key in frame) {
-    // only not a duck if @id or non-keyword isn't in subject
-    if((key === '@id' || !_isKeyword(key)) && !(key in subject)) {
+    if(_isKeyword(key)) {
+      // skip [email protected] and [email protected]
+      if(key !== '@id' && key !== '@type') {
+        continue;
+      }
+      wildcard = false;
+
+      // check @id for a specific @id value
+      if(key === '@id' && _isString(frame[key])) {
+        if(subject[key] !== frame[key]) {
+          return false;
+        }
+        matchesSome = true;
+        continue;
+      }
+    }
+
+    wildcard = false;
+
+    if(key in subject) {
+      // frame[key] === [] means do not match if property is present
+      if(_isArray(frame[key]) && frame[key].length === 0 &&
+        subject[key] !== undefined) {
+        return false;
+      }
+      matchesSome = true;
+      continue;
+    }
+
+    // all properties must match to be a duck unless a @default is specified
+    var hasDefault = (_isArray(frame[key]) && _isObject(frame[key][0]) &&
+      '@default' in frame[key][0]);
+    if(flags.requireAll && !hasDefault) {
       return false;
     }
   }
-  return true;
+
+  // return true if wildcard or subject matches some properties
+  return wildcard || matchesSome;
 }
 
 /**
@@ -5337,6 +5396,7 @@
   case '@list':
   case '@omitDefault':
   case '@preserve':
+  case '@requireAll':
   case '@reverse':
   case '@set':
   case '@type':
@@ -5580,7 +5640,8 @@
 }
 
 /**
- * Clones an object, array, or string/number.
+ * Clones an object, array, or string/number. If a typed JavaScript object
+ * is given, such as a Date, it will be converted to a string.
  *
  * @param value the value to clone.
  *
@@ -5594,11 +5655,13 @@
       for(var i = 0; i < value.length; ++i) {
         rval[i] = _clone(value[i]);
       }
-    } else {
+    } else if(_isObject(value)) {
       rval = {};
       for(var key in value) {
         rval[key] = _clone(value[key]);
       }
+    } else {
+      rval = value.toString();
     }
     return rval;
   }