Updated to latest jsonld.js.
authorDave Longley <dlongley@digitalbazaar.com>
Fri, 14 Oct 2011 17:33:58 -0400
changeset 206 a15350bae3d2
parent 205 cedadb230ee6
child 213 3f3c47b2816d
Updated to latest jsonld.js.
playground/jsonld.js
--- a/playground/jsonld.js	Sun Sep 11 21:32:00 2011 -0400
+++ b/playground/jsonld.js	Fri Oct 14 17:33:58 2011 -0400
@@ -717,6 +717,8 @@
             var p = _compactIri(ctx, key, usedCtx);
             if(p !== key || !(p in rval))
             {
+               // FIXME: clean old values from the usedCtx here ... or just
+               // change usedCtx to be built at the end of processing? 
                rval[p] = this.compact(ctx, key, value[key], usedCtx);
             }
          }
@@ -2590,17 +2592,175 @@
 };
 
 /**
+ * Subframes a value.
+ * 
+ * @param subjects a map of subjects in the graph.
+ * @param value the value to subframe.
+ * @param frame the frame to use.
+ * @param embeds a map of previously embedded subjects, used to prevent cycles.
+ * @param autoembed true if auto-embed is on, false if not.
+ * @param parent the parent object.
+ * @param parentKey the parent key.
+ * @param options the framing options.
+ * 
+ * @return the framed input.
+ */
+var _subframe = function(
+   subjects, value, frame, embeds, autoembed, parent, parentKey, options)
+{
+   // get existing embed entry
+   var iri = value['@subject']['@iri'];
+   var embed = (iri in embeds) ? embeds[iri] : null;
+   
+   // determine if value should be embedded or referenced,
+   // embed is ON if:
+   // 1. The frame OR default option specifies @embed as ON, AND
+   // 2. There is no existing embed OR it is an autoembed, AND
+   //    autoembed mode is off.
+   var embedOn =
+      (frame['@embed'] === true || options.defaults.embedOn) &&
+      (embed === null || (embed.autoembed && !autoembed));
+   
+   if(!embedOn)
+   {
+      // not embedding, so only use subject IRI as reference
+      value = value['@subject'];
+   }
+   else
+   {
+      // create new embed entry
+      if(embed === null)
+      {
+         embed = {};
+         embeds[iri] = embed;
+      }
+      // replace the existing embed with a reference
+      else if(embed.parent !== null)
+      {
+         embed.parent[embed.key] = value['@subject'];
+      }
+      
+      // update embed entry
+      embed.autoembed = autoembed;
+      embed.parent = parent;
+      embed.key = parentKey;
+      
+      // check explicit flag
+      var explicitOn =
+         frame['@explicit'] === true || options.defaults.explicitOn;
+      if(explicitOn)
+      {
+         // remove keys from the value that aren't in the frame
+         for(key in value)
+         {
+            // do not remove @subject or any frame key
+            if(key !== '@subject' && !(key in frame))
+            {
+               delete value[key];
+            }
+         }
+      }
+      
+      // iterate over keys in value
+      for(key in value)
+      {
+         // skip keywords and type
+         if(key.indexOf('@') !== 0 && key !== ns.rdf + 'type')
+         {
+            // get the subframe if available
+            if(key in frame)
+            {
+               var f = frame[key];
+               var _autoembed = false;
+            }
+            // use a catch-all subframe to preserve data from graph
+            else
+            {
+               var f = (value[key].constructor === Array) ? [] : {};
+               var _autoembed = true;
+            }
+            
+            // build input and do recursion
+            var v = value[key];
+            var input = (v.constructor === Array) ? v : [v];
+            for(var n in input)
+            {
+               // replace reference to subject w/subject
+               if(input[n].constructor === Object &&
+                  '@iri' in input[n] &&
+                  input[n]['@iri'] in subjects)
+               {
+                  input[n] = subjects[input[n]['@iri']];
+               }
+            }
+            value[key] = _frame(
+               subjects, input, f, embeds, _autoembed, value, key, options);
+         }
+      }
+      
+      // iterate over frame keys to add any missing values
+      for(key in frame)
+      {
+         // skip keywords, type query, and non-null keys in value
+         if(key.indexOf('@') !== 0 && key !== ns.rdf + 'type' &&
+            (!(key in value) || value[key] === null))
+         {
+            var f = frame[key];
+            
+            // add empty array to value
+            if(f.constructor === Array)
+            {
+               value[key] = [];
+            }
+            // add default value to value
+            else
+            {
+               // use first subframe if frame is an array
+               if(f.constructor === Array)
+               {
+                  f = (f.length > 0) ? f[0] : {};
+               }
+               
+               // determine if omit default is on
+               var omitOn =
+                  f['@omitDefault'] === true || options.defaults.omitDefaultOn;
+               if(!omitOn)
+               {
+                  if('@default' in f)
+                  {
+                     // use specified default value
+                     value[key] = f['@default'];
+                  }
+                  else
+                  {
+                     // built-in default value is: null
+                     value[key] = null;
+                  }
+               }
+            }
+         }
+      }
+   }
+   
+   return value;
+}
+
+/**
  * Recursively frames the given input according to the given frame.
  * 
  * @param subjects a map of subjects in the graph.
  * @param input the input to frame.
  * @param frame the frame to use.
  * @param embeds a map of previously embedded subjects, used to prevent cycles.
+ * @param autoembed true if auto-embed is on, false if not.
+ * @param parent the parent object (for subframing), null for none.
+ * @param parentKey the parent key (for subframing), null for none.
  * @param options the framing options.
  * 
  * @return the framed input.
  */
-var _frame = function(subjects, input, frame, embeds, options)
+var _frame = function(
+   subjects, input, frame, embeds, autoembed, parent, parentKey, options)
 {
    var rval = null;
    
@@ -2632,7 +2792,8 @@
       {
          throw {
             message: 'Invalid JSON-LD frame. ' +
-               'Frame must be an object or an array.'
+               'Frame must be an object or an array.',
+            frame: frame
          };
       }
       
@@ -2658,101 +2819,12 @@
          frame = frames[i1];
          var value = values[i1][i2];
          
-         // determine if value should be embedded or referenced
-         var embedOn = ('@embed' in frame) ?
-            frame['@embed'] : options.defaults.embedOn;
-         if(!embedOn)
-         {
-            // if value is a subject, only use subject IRI as reference 
-            if(value.constructor === Object && '@subject' in value)
-            {
-               value = value['@subject'];
-            }
-         }
-         else if(
-            value.constructor === Object &&
-            '@subject' in value && value['@subject']['@iri'] in embeds)
-         {
-            // TODO: possibly support multiple embeds in the future ... and
-            // instead only prevent cycles?
-            throw {
-               message: 'More than one embed of the same subject is not ' +
-                  'supported.',
-               subject: value['@subject']['@iri']
-            };
-         }
-         // if value is a subject, do embedding and subframing
-         else if(value.constructor === Object && '@subject' in value)
+         // if value is a subject, do subframing
+         if(value.constructor === Object && '@subject' in value)
          {
-            embeds[value['@subject']['@iri']] = true;
-            
-            // if explicit is on, remove keys from value that aren't in frame
-            var explicitOn = ('@explicit' in frame) ?
-               frame['@explicit'] : options.defaults.explicitOn;
-            if(explicitOn)
-            {
-               for(key in value)
-               {
-                  // do not remove subject or any key in the frame
-                  if(key !== '@subject' && !(key in frame))
-                  {
-                     delete value[key];
-                  }
-               }
-            }
-            
-            // iterate over frame keys to do subframing
-            for(key in frame)
-            {
-               // skip keywords and type query
-               if(key.indexOf('@') !== 0 && key !== ns.rdf + 'type')
-               {
-                  var f = frame[key];
-                  if(key in value)
-                  {
-                     // build input and do recursion
-                     var v = value[key];
-                     input = (v.constructor === Array) ? v : [v];
-                     for(var n in input)
-                     {
-                        // replace reference to subject w/subject
-                        if(input[n].constructor === Object &&
-                           '@iri' in input[n] && input[n]['@iri'] in subjects)
-                        {
-                           input[n] = subjects[input[n]['@iri']];
-                        }
-                     }
-                     value[key] = _frame(subjects, input, f, embeds, options);
-                  }
-                  else
-                  {
-                     // add empty array/null property to value
-                     value[key] = (f.constructor === Array) ? [] : null;
-                  }
-                  
-                  // handle setting default value
-                  if(value[key] === null)
-                  {
-                     // use first subframe if frame is an array
-                     if(f.constructor === Array)
-                     {
-                        f = (f.length > 0) ? f[0] : {};
-                     }
-                     
-                     // determine if omit default is on
-                     var omitOn = ('@omitDefault' in f) ?
-                        f['@omitDefault'] : options.defaults.omitDefaultOn;
-                     if(omitOn)
-                     {
-                        delete value[key];
-                     }
-                     else if('@default' in f)
-                     {
-                        value[key] = f['@default'];
-                     }
-                  }
-               }
-            }
+            value = _subframe(
+               subjects, value, frame, embeds, autoembed,
+               parent, parentKey, options);
          }
          
          // add value to output
@@ -2816,7 +2888,7 @@
    }
    
    // frame input
-   rval = _frame(subjects, input, frame, {}, options);
+   rval = _frame(subjects, input, frame, {}, false, null, null, options);
    
    // apply context
    if(ctx !== null && rval !== null)