--- a/playground/jsonld.js Thu Dec 15 16:55:21 2011 -0500
+++ b/playground/jsonld.js Tue Dec 20 00:07:26 2011 -0500
@@ -25,6 +25,27 @@
Exception = function(obj)
{
_setMembers(this, obj);
+ };
+
+ // define js 1.8.5 Object.keys method unless present
+ if(!Object.keys)
+ {
+ Object.keys = function(o)
+ {
+ if(o !== Object(o))
+ {
+ throw new TypeError('Object.keys called on non-object');
+ }
+ var rval = [];
+ for(var p in o)
+ {
+ if(Object.prototype.hasOwnProperty.call(o, p))
+ {
+ rval.push(p);
+ }
+ }
+ return rval;
+ }
}
}
// define node.js module
@@ -49,9 +70,9 @@
var xsd =
{
- boolean: ns.xsd + 'boolean',
- double: ns.xsd + 'double',
- integer: ns.xsd + 'integer'
+ 'boolean': ns.xsd + 'boolean',
+ 'double': ns.xsd + 'double',
+ 'integer': ns.xsd + 'integer'
};
/**
@@ -133,11 +154,9 @@
var rval =
{
- '@datatype': '@datatype',
- '@iri': '@iri',
+ '@id': '@id',
'@language': '@language',
'@literal': '@literal',
- '@subject': '@subject',
'@type': '@type'
};
@@ -147,8 +166,7 @@
var keywords = {};
for(var key in ctx)
{
- if(ctx[key].constructor === String &&
- ctx[key] in rval)
+ if(ctx[key].constructor === String && ctx[key] in rval)
{
keywords[ctx[key]] = key;
}
@@ -165,7 +183,32 @@
};
/**
- * Compacts an IRI into a term or CURIE if it can be. IRIs will not be
+ * Gets the iri associated with a term.
+ *
+ * @param ctx the context.
+ * @param term the term.
+ *
+ * @return the iri or NULL.
+ */
+var _getTermIri = function(ctx, term)
+{
+ var rval = null;
+ if(term in ctx)
+ {
+ if(ctx[term].constructor === String)
+ {
+ rval = ctx[term];
+ }
+ else if(ctx[term].constructor === Object && '@id' in ctx[term])
+ {
+ rval = ctx[term]['@id'];
+ }
+ }
+ return rval;
+};
+
+/**
+ * Compacts an IRI into a term or prefix if it can be. IRIs will not be
* compacted to relative IRIs if they match the given context's default
* vocabulary.
*
@@ -173,26 +216,26 @@
* @param iri the IRI to compact.
* @param usedCtx a context to update if a value was used from "ctx".
*
- * @return the compacted IRI as a term or CURIE or the original IRI.
+ * @return the compacted IRI as a term or prefix or the original IRI.
*/
var _compactIri = function(ctx, iri, usedCtx)
{
var rval = null;
// check the context for a term that could shorten the IRI
- // (give preference to terms over CURIEs)
+ // (give preference to terms over prefixes)
for(var key in ctx)
{
// skip special context keys (start with '@')
if(key.length > 0 && key[0] !== '@')
{
// compact to a term
- if(iri === ctx[key])
+ if(iri === _getTermIri(ctx, key))
{
rval = key;
if(usedCtx !== null)
{
- usedCtx[key] = ctx[key];
+ usedCtx[key] = _clone(ctx[key]);
}
break;
}
@@ -205,7 +248,7 @@
rval = _getKeywords(ctx)['@type'];
}
- // term not found, check the context for a CURIE prefix
+ // term not found, check the context for a prefix
if(rval === null)
{
for(var key in ctx)
@@ -214,18 +257,21 @@
if(key.length > 0 && key[0] !== '@')
{
// see if IRI begins with the next IRI from the context
- var ctxIri = ctx[key];
- var idx = iri.indexOf(ctxIri);
-
- // compact to a CURIE
- if(idx === 0 && iri.length > ctxIri.length)
+ var ctxIri = _getTermIri(ctx, key);
+ if(ctxIri !== null)
{
- rval = key + ':' + iri.substr(ctxIri.length);
- if(usedCtx !== null)
+ var idx = iri.indexOf(ctxIri);
+
+ // compact to a prefix
+ if(idx === 0 && iri.length > ctxIri.length)
{
- usedCtx[key] = ctxIri;
+ rval = key + ':' + iri.substr(ctxIri.length);
+ if(usedCtx !== null)
+ {
+ usedCtx[key] = _clone(ctx[key]);
+ }
+ break;
}
- break;
}
}
}
@@ -242,7 +288,7 @@
/**
* Expands a term into an absolute IRI. The term may be a regular term, a
- * CURIE, a relative IRI, or an absolute IRI. In any case, the associated
+ * prefix, a relative IRI, or an absolute IRI. In any case, the associated
* absolute IRI will be returned.
*
* @param ctx the context to use.
@@ -253,67 +299,52 @@
*/
var _expandTerm = function(ctx, term, usedCtx)
{
- var rval;
+ var rval = term;
// get JSON-LD keywords
var keywords = _getKeywords(ctx);
- // 1. If the property has a colon, then it is a CURIE or an absolute IRI:
+ // 1. If the property has a colon, it is a prefix or an absolute IRI:
var idx = term.indexOf(':');
- if(idx != -1)
+ if(idx !== -1)
{
- // get the potential CURIE prefix
+ // get the potential prefix
var prefix = term.substr(0, idx);
- // 1.1. See if the prefix is in the context:
+ // expand term if prefix is in context, otherwise leave it be
if(prefix in ctx)
{
// prefix found, expand property to absolute IRI
- rval = ctx[prefix] + term.substr(idx + 1);
+ var iri = _getTermIri(ctx, prefix);
+ rval = iri + term.substr(idx + 1);
if(usedCtx !== null)
{
- usedCtx[prefix] = ctx[prefix];
+ usedCtx[prefix] = _clone(ctx[prefix]);
}
}
- // 1.2. Prefix is not in context, property is already an absolute IRI:
- else
- {
- rval = term;
- }
}
// 2. If the property is in the context, then it's a term.
else if(term in ctx)
{
- rval = ctx[term];
+ rval = _getTermIri(ctx, term);
if(usedCtx !== null)
{
- usedCtx[term] = rval;
+ usedCtx[term] = _clone(ctx[term]);
}
}
- // 3. The property is the special-case @subject.
- else if(term === keywords['@subject'])
- {
- rval = '@subject';
- }
- // 4. The property is the special-case @type.
- else if(term === keywords['@type'])
- {
- rval = '@type';
- }
- // 5. The property is a relative IRI, prepend the default vocab.
+ // 3. The property is a keyword.
else
{
- rval = term;
- if('@vocab' in ctx)
+ for(var key in keywords)
{
- rval = ctx['@vocab'] + rval;
- if(usedCtx !== null)
+ if(term === keywords[key])
{
- usedCtx['@vocab'] = ctx['@vocab'];
+ rval = key;
+ break;
}
}
}
-
+
return rval;
};
@@ -326,27 +357,56 @@
*/
var _sortContextKeys = function(ctx)
{
+ // sort keys
var rval = {};
-
- // sort keys
var keys = Object.keys(ctx).sort();
for(var k in keys)
{
var key = keys[k];
- if(key !== '@coerce')
- {
- rval[key] = ctx[key];
- }
+ rval[key] = ctx[key];
}
- if('@coerce' in ctx)
+ return rval;
+};
+
+/**
+ * Gets whether or not a value is a reference to a subject (or a subject with
+ * no properties).
+ *
+ * @param value the value to check.
+ *
+ * @return true if the value is a reference to a subject, false if not.
+ */
+var _isReference = function(value)
+{
+ // Note: A value is a reference to a subject if all of these hold true:
+ // 1. It is an Object.
+ // 2. It is has an @id key.
+ // 3. It has only 1 key.
+ return (value !== null &&
+ value.constructor === Object &&
+ '@id' in value &&
+ Object.keys(value).length === 1);
+};
+
+/**
+ * Gets whether or not a value is a subject with properties.
+ *
+ * @param value the value to check.
+ *
+ * @return true if the value is a subject with properties, false if not.
+ */
+var _isSubject = function(value)
+{
+ var rval = false;
+
+ // Note: A value is a subject if all of these hold true:
+ // 1. It is an Object.
+ // 2. It is not a literal.
+ // 3. It has more than 1 key OR any existing key is not '@id'.
+ if(value !== null && value.constructor === Object && !('@literal' in value))
{
- rval['@coerce'] = {};
- keys = Object.keys(ctx['@coerce']).sort();
- for(var k in keys)
- {
- var key = keys[k];
- rval['@coerce'][key] = ctx['@coerce'][key];
- }
+ var keyCount = Object.keys(value).length;
+ rval = (keyCount > 1 || !('@id' in value));
}
return rval;
@@ -377,7 +437,7 @@
*/
jsonld.expand = function(input)
{
- return new Processor().expand({}, null, input, false);
+ return new Processor().expand({}, null, input);
};
/**
@@ -504,7 +564,7 @@
{
if(merged[mkey] === ctx2[key])
{
- // FIXME: update related @coerce rules
+ // FIXME: update related coerce rules
delete merged[mkey];
break;
}
@@ -515,27 +575,7 @@
// merge contexts
for(var key in ctx2)
{
- // skip @coerce, to be merged below
- if(key !== '@coerce')
- {
- merged[key] = _clone(ctx2[key]);
- }
- }
-
- // merge @coerce
- if('@coerce' in ctx2)
- {
- if(!('@coerce' in merged))
- {
- merged['@coerce'] = _clone(ctx2['@coerce']);
- }
- else
- {
- for(var key in ctx2['@coerce'])
- {
- merged['@coerce'][key] = ctx2['@coerce'][key];
- }
- }
+ merged[key] = _clone(ctx2[key]);
}
}
@@ -544,7 +584,7 @@
/**
* Expands a term into an absolute IRI. The term may be a regular term, a
- * CURIE, a relative IRI, or an absolute IRI. In any case, the associated
+ * prefix, a relative IRI, or an absolute IRI. In any case, the associated
* absolute IRI will be returned.
*
* @param ctx the context to use.
@@ -555,14 +595,14 @@
jsonld.expandTerm = _expandTerm;
/**
- * Compacts an IRI into a term or CURIE if it can be. IRIs will not be
+ * Compacts an IRI into a term or prefix if it can be. IRIs will not be
* compacted to relative IRIs if they match the given context's default
* vocabulary.
*
* @param ctx the context to use.
* @param iri the IRI to compact.
*
- * @return the compacted IRI as a term or CURIE or the original IRI.
+ * @return the compacted IRI as a term or prefix or the original IRI.
*/
jsonld.compactIri = function(ctx, iri)
{
@@ -603,7 +643,7 @@
var rval = null;
// normalize input
- normalized = jsonld.normalize(input);
+ var normalized = jsonld.normalize(input);
// setup default callback
callback = callback || null;
@@ -621,10 +661,10 @@
for(var i1 in normalized)
{
var e = normalized[i1];
- var s = e['@subject']['@iri'];
+ var s = e['@id'];
for(var p in e)
{
- if(p !== '@subject')
+ if(p !== '@id')
{
var obj = e[p];
if(obj.constructor !== Array)
@@ -791,7 +831,7 @@
};
/**
- * Recursively compacts a value. This method will compact IRIs to CURIEs or
+ * Recursively compacts a value. This method will compact IRIs to prefixes or
* terms and do reverse type coercion to compact a value.
*
* @param ctx the context to use.
@@ -826,16 +866,14 @@
// graph literal/disjoint graph
else if(
value.constructor === Object &&
- '@subject' in value && value['@subject'].constructor === Array)
+ '@id' in value && value['@id'].constructor === Array)
{
rval = {};
- rval[keywords['@subject']] = this.compact(
- ctx, property, value['@subject'], usedCtx);
+ rval[keywords['@id']] = this.compact(
+ ctx, property, value['@id'], usedCtx);
}
- // value has sub-properties if it doesn't define a literal or IRI value
- else if(
- value.constructor === Object &&
- !('@literal' in value) && !('@iri' in value))
+ // recurse if value is a subject
+ else if(_isSubject(value))
{
// recursively handle sub-properties that aren't a sub-context
rval = {};
@@ -849,7 +887,7 @@
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?
+ // change usedCtx to be built at the end of processing?
rval[p] = this.compact(ctx, key, value[key], usedCtx);
}
}
@@ -867,15 +905,15 @@
// type coercion can only occur if language is not specified
if(!('@language' in value))
{
- // datatype must match coerce type if specified
- if('@datatype' in value)
+ // type must match coerce type if specified
+ if('@type' in value)
{
- type = value['@datatype'];
+ type = value['@type'];
}
- // datatype is IRI
- else if('@iri' in value)
+ // type is ID (IRI)
+ else if('@id' in value)
{
- type = '@iri';
+ type = '@id';
}
// can be coerced to any type
else
@@ -892,7 +930,8 @@
// types that can be auto-coerced from a JSON-builtin
if(coerce === null &&
- (type === xsd.boolean || type === xsd.integer || type === xsd.double))
+ (type === xsd['boolean'] || type === xsd['integer'] ||
+ type === xsd['double']))
{
coerce = type;
}
@@ -913,7 +952,7 @@
else if(type !== coerce)
{
throw new Exception({
- message: 'Cannot coerce type because the datatype does ' +
+ message: 'Cannot coerce type because the type does ' +
'not match.',
type: type,
expected: coerce
@@ -924,9 +963,9 @@
{
if(value.constructor === Object)
{
- if('@iri' in value)
+ if('@id' in value)
{
- rval = value['@iri'];
+ rval = value['@id'];
}
else if('@literal' in value)
{
@@ -939,15 +978,15 @@
}
// do basic JSON types conversion
- if(coerce === xsd.boolean)
+ if(coerce === xsd['boolean'])
{
rval = (rval === 'true' || rval != 0);
}
- else if(coerce === xsd.double)
+ else if(coerce === xsd['double'])
{
rval = parseFloat(rval);
}
- else if(coerce === xsd.integer)
+ else if(coerce === xsd['integer'])
{
rval = parseInt(rval);
}
@@ -968,12 +1007,12 @@
}
// compact IRI
- if(type === '@iri')
+ if(type === '@id')
{
if(rval.constructor === Object)
{
- rval[keywords['@iri']] = _compactIri(
- ctx, rval[keywords['@iri']], usedCtx);
+ rval[keywords['@id']] = _compactIri(
+ ctx, rval[keywords['@id']], usedCtx);
}
else
{
@@ -992,11 +1031,10 @@
* @param ctx the context.
* @param property the property that points to the value, NULL for none.
* @param value the value to expand.
- * @param expandSubjects true to expand subjects (normalize), false not to.
*
* @return the expanded value.
*/
-Processor.prototype.expand = function(ctx, property, value, expandSubjects)
+Processor.prototype.expand = function(ctx, property, value)
{
var rval;
@@ -1019,7 +1057,7 @@
rval = [];
for(var i in value)
{
- rval.push(this.expand(ctx, property, value[i], expandSubjects));
+ rval.push(this.expand(ctx, property, value[i]));
}
}
else if(value.constructor === Object)
@@ -1030,50 +1068,22 @@
ctx = jsonld.mergeContexts(ctx, value['@context']);
}
- // get JSON-LD keywords
- var keywords = _getKeywords(ctx);
-
- // value has sub-properties if it doesn't define a literal or IRI value
- if(!(keywords['@literal'] in value || keywords['@iri'] in value))
+ // recursively handle sub-properties that aren't a sub-context
+ rval = {};
+ for(var key in value)
{
- // recursively handle sub-properties that aren't a sub-context
- rval = {};
- for(var key in value)
+ // preserve frame keywords
+ if(key === '@embed' || key === '@explicit' ||
+ key === '@default' || key === '@omitDefault')
{
- // preserve frame keywords
- if(key === '@embed' || key === '@explicit' ||
- key === '@default' || key === '@omitDefault')
- {
- _setProperty(rval, key, _clone(value[key]));
- }
- else if(key !== '@context')
- {
- // set object to expanded property
- _setProperty(
- rval, _expandTerm(ctx, key, null),
- this.expand(ctx, key, value[key], expandSubjects));
- }
+ _setProperty(rval, key, _clone(value[key]));
}
- }
- // only need to expand keywords
- else
- {
- rval = {};
- if(keywords['@iri'] in value)
+ else if(key !== '@context')
{
- rval['@iri'] = value[keywords['@iri']];
- }
- else
- {
- rval['@literal'] = value[keywords['@literal']];
- if(keywords['@language'] in value)
- {
- rval['@language'] = value[keywords['@language']];
- }
- else if(keywords['@datatype'] in value)
- {
- rval['@datatype'] = value[keywords['@datatype']];
- }
+ // set object to expanded property
+ _setProperty(
+ rval, _expandTerm(ctx, key, null),
+ this.expand(ctx, key, value[key]));
}
}
}
@@ -1091,34 +1101,38 @@
{
if(value.constructor === Boolean)
{
- coerce = xsd.boolean;
+ coerce = xsd['boolean'];
}
else if(('' + value).indexOf('.') == -1)
{
- coerce = xsd.integer;
+ coerce = xsd['integer'];
}
else
{
- coerce = xsd.double;
+ coerce = xsd['double'];
}
}
-
- // coerce to appropriate datatype, only expand subjects if requested
- if(coerce !== null &&
- (property !== keywords['@subject'] || expandSubjects))
+
+ // special-case expand @id and @type (skips '@id' expansion)
+ if(property === keywords['@id'] || property === keywords['@type'])
+ {
+ rval = _expandTerm(ctx, value, null);
+ }
+ // coerce to appropriate type
+ else if(coerce !== null)
{
rval = {};
- // expand IRI
- if(coerce === '@iri')
+ // expand ID (IRI)
+ if(coerce === '@id')
{
- rval['@iri'] = _expandTerm(ctx, value, null);
+ rval['@id'] = _expandTerm(ctx, value, null);
}
- // other datatype
+ // other type
else
{
- rval['@datatype'] = coerce;
- if(coerce === xsd.double)
+ rval['@type'] = coerce;
+ if(coerce === xsd['double'])
{
// do special JSON-LD double format
value = value.toExponential(6).replace(
@@ -1160,11 +1174,11 @@
};
// expand input
- var expanded = this.expand({}, null, input, true);
+ var expanded = this.expand({}, null, input);
// assign names to unnamed bnodes
this.nameBlankNodes(expanded);
-
+
// flatten
var subjects = {};
_flatten(null, null, expanded, subjects);
@@ -1189,7 +1203,7 @@
// sort output
rval.sort(function(a, b)
{
- return _compare(a['@subject']['@iri'], b['@subject']['@iri']);
+ return _compare(a['@id'], b['@id']);
});
}
@@ -1213,27 +1227,22 @@
var p = _expandTerm(ctx, property, null);
// built-in type coercion JSON-LD-isms
- if(p === '@subject' || p === '@type')
+ if(p === '@id' || p === '@type')
{
- rval = '@iri';
+ rval = '@id';
}
- // check type coercion for property
- else if('@coerce' in ctx)
+ else
{
- // look up compacted property in coercion map
+ // look up compacted property for a coercion type
p = _compactIri(ctx, p, null);
- if(p in ctx['@coerce'])
+ if(p in ctx && ctx[p].constructor === Object && '@type' in ctx[p])
{
// property found, return expanded type
- var type = ctx['@coerce'][p];
+ var type = ctx[p]['@type'];
rval = _expandTerm(ctx, type, usedCtx);
if(usedCtx !== null)
{
- if(!('@coerce' in usedCtx))
- {
- usedCtx['@coerce'] = {};
- }
- usedCtx['@coerce'][p] = type;
+ usedCtx[p] = _clone(ctx[p]);
}
}
}
@@ -1250,17 +1259,13 @@
{
// look for "_:" at the beginning of the subject
return (
- v.constructor === Object && '@subject' in v &&
- '@iri' in v['@subject'] && _isBlankNodeIri(v['@subject']['@iri']));
+ v.constructor === Object && '@id' in v && _isBlankNodeIri(v['@id']));
};
var _isBlankNode = function(v)
{
- // look for no subject or named blank node
- return (
- v.constructor === Object &&
- !('@iri' in v || '@literal' in v) &&
- (!('@subject' in v) || _isNamedBlankNode(v)));
+ // look for a subject with no ID or a blank node ID
+ return (_isSubject(v) && (!('@id' in v) || _isNamedBlankNode(v)));
};
/**
@@ -1356,16 +1361,16 @@
{
if('@literal' in o1)
{
- rval = _compareObjectKeys(o1, o2, '@datatype');
+ rval = _compareObjectKeys(o1, o2, '@type');
if(rval === 0)
{
rval = _compareObjectKeys(o1, o2, '@language');
}
}
- // both are '@iri' objects
+ // both are '@id' objects
else
{
- rval = _compare(o1['@iri'], o2['@iri']);
+ rval = _compare(o1['@id'], o2['@id']);
}
}
}
@@ -1394,59 +1399,56 @@
3.2.3. The bnode with the alphabetically-first string is first.
3.2.4. The bnode with a @literal is first.
3.2.5. The bnode with the alphabetically-first @literal is first.
- 3.2.6. The bnode with the alphabetically-first @datatype is first.
+ 3.2.6. The bnode with the alphabetically-first @type is first.
3.2.7. The bnode with a @language is first.
3.2.8. The bnode with the alphabetically-first @language is first.
- 3.2.9. The bnode with the alphabetically-first @iri is first.
+ 3.2.9. The bnode with the alphabetically-first @id is first.
*/
for(var p in a)
{
- // step #3.1
- var lenA = (a[p].constructor === Array) ? a[p].length : 1;
- var lenB = (b[p].constructor === Array) ? b[p].length : 1;
- rval = _compare(lenA, lenB);
-
- // step #3.2.1
- if(rval === 0)
+ // skip IDs (IRIs)
+ if(p !== '@id')
{
- // normalize objects to an array
- var objsA = a[p];
- var objsB = b[p];
- if(objsA.constructor !== Array)
+ // step #3.1
+ var lenA = (a[p].constructor === Array) ? a[p].length : 1;
+ var lenB = (b[p].constructor === Array) ? b[p].length : 1;
+ rval = _compare(lenA, lenB);
+
+ // step #3.2.1
+ if(rval === 0)
{
- objsA = [objsA];
- objsB = [objsB];
+ // normalize objects to an array
+ var objsA = a[p];
+ var objsB = b[p];
+ if(objsA.constructor !== Array)
+ {
+ objsA = [objsA];
+ objsB = [objsB];
+ }
+
+ // compare non-bnodes (remove bnodes from comparison)
+ objsA = objsA.filter(function(e) {return !_isNamedBlankNode(e);});
+ objsB = objsB.filter(function(e) {return !_isNamedBlankNode(e);});
+ rval = _compare(objsA.length, objsB.length);
}
- // filter non-bnodes (remove bnodes from comparison)
- objsA = objsA.filter(function(e) {
- return (e.constructor === String ||
- !('@iri' in e && _isBlankNodeIri(e['@iri'])));
- });
- objsB = objsB.filter(function(e) {
- return (e.constructor === String ||
- !('@iri' in e && _isBlankNodeIri(e['@iri'])));
- });
+ // steps #3.2.2-3.2.9
+ if(rval === 0)
+ {
+ objsA.sort(_compareObjects);
+ objsB.sort(_compareObjects);
+ for(var i = 0; i < objsA.length && rval === 0; ++i)
+ {
+ rval = _compareObjects(objsA[i], objsB[i]);
+ }
+ }
- rval = _compare(objsA.length, objsB.length);
- }
-
- // steps #3.2.2-3.2.9
- if(rval === 0)
- {
- objsA.sort(_compareObjects);
- objsB.sort(_compareObjects);
- for(var i = 0; i < objsA.length && rval === 0; ++i)
+ if(rval !== 0)
{
- rval = _compareObjects(objsA[i], objsB[i]);
+ break;
}
}
-
- if(rval !== 0)
- {
- break;
- }
}
return rval;
@@ -1504,17 +1506,17 @@
}
else if(input.constructor === Object)
{
- if('@subject' in input)
+ if('@id' in input)
{
- // graph literal
- if(input['@subject'].constructor == Array)
+ // graph literal/disjoint graph
+ if(input['@id'].constructor == Array)
{
- _collectSubjects(input['@subject'], subjects, bnodes);
+ _collectSubjects(input['@id'], subjects, bnodes);
}
// named subject
- else
+ else if(_isSubject(input))
{
- subjects[input['@subject']['@iri']] = input;
+ subjects[input['@id']] = input;
}
}
// unnamed blank node
@@ -1559,8 +1561,13 @@
}
else if(value.constructor === Object)
{
+ // already-expanded value or special-case reference-only @type
+ if('@literal' in value || parentProperty === '@type')
+ {
+ flattened = _clone(value);
+ }
// graph literal/disjoint graph
- if('@subject' in value && value['@subject'].constructor === Array)
+ else if(value['@id'].constructor === Array)
{
// cannot flatten embedded graph literals
if(parent !== null)
@@ -1571,44 +1578,36 @@
}
// top-level graph literal
- for(var key in value['@subject'])
+ for(var idx in value['@id'])
{
- _flatten(parent, parentProperty, value['@subject'][key], subjects);
+ _flatten(parent, parentProperty, value['@id'][idx], subjects);
}
}
- // already-expanded value
- else if('@literal' in value || '@iri' in value)
- {
- flattened = _clone(value);
- }
- // subject
+ // regular subject
else
{
// create or fetch existing subject
var subject;
- if(value['@subject']['@iri'] in subjects)
+ if(value['@id'] in subjects)
{
- // FIXME: '@subject' might be a graph literal (as {})
- subject = subjects[value['@subject']['@iri']];
+ // FIXME: '@id' might be a graph literal (as {})
+ subject = subjects[value['@id']];
}
else
{
- subject = {};
- if('@subject' in value)
- {
- // FIXME: '@subject' might be a graph literal (as {})
- subjects[value['@subject']['@iri']] = subject;
- }
+ // FIXME: '@id' might be a graph literal (as {})
+ subject = {'@id': value['@id']};
+ subjects[value['@id']] = subject;
}
- flattened = subject;
+ flattened = {'@id': subject['@id']};
// flatten embeds
for(var key in value)
{
var v = value[key];
- // drop null values
- if(v !== null)
+ // drop null values, skip @id (it is already set above)
+ if(v !== null && key !== '@id')
{
if(key in subject)
{
@@ -1622,7 +1621,7 @@
subject[key] = [];
}
- _flatten(subject[key], null, v, subjects);
+ _flatten(subject[key], key, v, subjects);
if(subject[key].length === 1)
{
// convert subject[key] to object if it has only 1
@@ -1641,25 +1640,16 @@
// add flattened value to parent
if(flattened !== null && parent !== null)
{
- // remove top-level '@subject' for subjects
- // 'http://mypredicate': {'@subject': {'@iri': 'http://mysubject'}}
- // becomes
- // 'http://mypredicate': {'@iri': 'http://mysubject'}
- if(flattened.constructor === Object && '@subject' in flattened)
- {
- flattened = flattened['@subject'];
- }
-
if(parent.constructor === Array)
{
// do not add duplicate IRIs for the same property
var duplicate = false;
- if(flattened.constructor === Object && '@iri' in flattened)
+ if(flattened.constructor === Object && '@id' in flattened)
{
duplicate = (parent.filter(function(e)
{
- return (e.constructor === Object && '@iri' in e &&
- e['@iri'] === flattened['@iri']);
+ return (e.constructor === Object && '@id' in e &&
+ e['@id'] === flattened['@id']);
}).length > 0);
}
if(!duplicate)
@@ -1694,14 +1684,11 @@
for(var i in bnodes)
{
var bnode = bnodes[i];
- if(!('@subject' in bnode))
+ if(!('@id' in bnode))
{
// generate names until one is unique
- while(ng.next() in subjects);
- bnode['@subject'] =
- {
- '@iri': ng.current()
- };
+ while(ng.next() in subjects){}
+ bnode['@id'] = ng.current();
subjects[ng.current()] = bnode;
}
}
@@ -1716,10 +1703,10 @@
*/
Processor.prototype.renameBlankNode = function(b, id)
{
- var old = b['@subject']['@iri'];
+ var old = b['@id'];
// update bnode IRI
- b['@subject']['@iri'] = id;
+ b['@id'] = id;
// update subjects map
var subjects = this.subjects;
@@ -1756,9 +1743,9 @@
for(var n in tmp)
{
if(tmp[n].constructor === Object &&
- '@iri' in tmp[n] && tmp[n]['@iri'] === old)
+ '@id' in tmp[n] && tmp[n]['@id'] === old)
{
- tmp[n]['@iri'] = id;
+ tmp[n]['@id'] = id;
}
}
}
@@ -1803,7 +1790,7 @@
var bnodes = [];
for(var i in input)
{
- var iri = input[i]['@subject']['@iri'];
+ var iri = input[i]['@id'];
subjects[iri] = input[i];
edges.refs[iri] =
{
@@ -1833,13 +1820,13 @@
for(var i in bnodes)
{
var bnode = bnodes[i];
- var iri = bnode['@subject']['@iri'];
+ var iri = bnode['@id'];
if(c14n.inNamespace(iri))
{
// generate names until one is unique
- while(ngTmp.next() in subjects);
+ while(ngTmp.next() in subjects){};
this.renameBlankNode(bnode, ngTmp.current());
- iri = bnode['@subject']['@iri'];
+ iri = bnode['@id'];
}
this.serializations[iri] =
{
@@ -1864,7 +1851,7 @@
// name all bnodes according to the first bnode's relation mappings
var bnode = bnodes.shift();
- var iri = bnode['@subject']['@iri'];
+ var iri = bnode['@id'];
var dirs = ['props', 'refs'];
for(var d in dirs)
{
@@ -1906,7 +1893,7 @@
for(var i in tmp)
{
var b = tmp[i];
- var iriB = b['@subject']['@iri'];
+ var iriB = b['@id'];
if(!c14n.inNamespace(iriB))
{
// mark serializations related to the named bnodes as dirty
@@ -2014,7 +2001,7 @@
var first = true;
for(var p in b)
{
- if(p !== '@subject')
+ if(p !== '@id')
{
if(first)
{
@@ -2035,16 +2022,16 @@
var o = objs[oi];
if(o.constructor === Object)
{
- // iri
- if('@iri' in o)
+ // ID (IRI)
+ if('@id' in o)
{
- if(_isBlankNodeIri(o['@iri']))
+ if(_isBlankNodeIri(o['@id']))
{
rval += '_:';
}
else
{
- rval += '<' + o['@iri'] + '>';
+ rval += '<' + o['@id'] + '>';
}
}
// literal
@@ -2052,10 +2039,10 @@
{
rval += '"' + o['@literal'] + '"';
- // datatype literal
- if('@datatype' in o)
+ // type literal
+ if('@type' in o)
{
- rval += '^^<' + o['@datatype'] + '>';
+ rval += '^^<' + o['@type'] + '>';
}
// language literal
else if('@language' in o)
@@ -2338,12 +2325,12 @@
{
if(!same)
{
- mapped[mb.mapNode(prev['@subject'])] = prev['@subject'];
+ mapped[mb.mapNode(prev['@id'])] = prev['@id'];
delete notMapped[i - 1];
}
if(i === notMapped.length - 1)
{
- mapped[mb.mapNode(curr['@subject'])];
+ mapped[mb.mapNode(curr['@id'])];
delete notMapped[i];
}
same = false;
@@ -2394,8 +2381,8 @@
var rval = 0;
// compare IRIs
- var iriA = a['@subject']['@iri'];
- var iriB = b['@subject']['@iri'];
+ var iriA = a['@id'];
+ var iriB = b['@id'];
if(iriA === iriB)
{
rval = 0;
@@ -2494,8 +2481,8 @@
// step #4
if(rval === 0)
{
- var edgesA = this.edges.refs[a['@subject']['@iri']].all;
- var edgesB = this.edges.refs[b['@subject']['@iri']].all;
+ var edgesA = this.edges.refs[a['@id']].all;
+ var edgesB = this.edges.refs[b['@id']].all;
rval = _compare(edgesA.length, edgesB.length);
}
@@ -2584,7 +2571,7 @@
var subject = this.subjects[iri];
for(var key in subject)
{
- if(key !== '@subject')
+ if(key !== '@id')
{
// normalize to array for single codepath
var object = subject[key];
@@ -2592,10 +2579,10 @@
for(var i in tmp)
{
var o = tmp[i];
- if(o.constructor === Object && '@iri' in o &&
- o['@iri'] in this.subjects)
+ if(o.constructor === Object && '@id' in o &&
+ o['@id'] in this.subjects)
{
- var objIri = o['@iri'];
+ var objIri = o['@id'];
// map object to this subject
refs[objIri].all.push({ s: iri, p: key });
@@ -2614,7 +2601,7 @@
{
refs[iri].all.sort(function(a, b) { return self.compareEdges(a, b); });
refs[iri].bnodes = refs[iri].all.filter(function(edge) {
- return _isBlankNodeIri(edge.s)
+ return _isBlankNodeIri(edge.s);
});
}
for(var iri in props)
@@ -2642,7 +2629,7 @@
// check if type(s) are specified in frame and input
var type = '@type';
if('@type' in frame &&
- input.constructor === Object && '@subject' in input && type in input)
+ input.constructor === Object && type in input)
{
var tmp = (input[type].constructor === Array) ?
input[type] : [input[type]];
@@ -2650,10 +2637,10 @@
frame[type] : [frame[type]];
for(var t = 0; t < types.length && !rval; ++t)
{
- type = types[t]['@iri'];
+ type = types[t];
for(var i in tmp)
{
- if(tmp[i]['@iri'] === type)
+ if(tmp[i] === type)
{
rval = true;
break;
@@ -2693,7 +2680,7 @@
rval = true;
}
// input must be a subject with all the given properties
- else if(input.constructor === Object && '@subject' in input)
+ else if(input.constructor === Object && '@id' in input)
{
rval = true;
for(var i in props)
@@ -2728,7 +2715,7 @@
subjects, value, frame, embeds, autoembed, parent, parentKey, options)
{
// get existing embed entry
- var iri = value['@subject']['@iri'];
+ var iri = value['@id'];
var embed = (iri in embeds) ? embeds[iri] : null;
// determine if value should be embedded or referenced,
@@ -2744,7 +2731,7 @@
if(!embedOn)
{
// not embedding, so only use subject IRI as reference
- value = value['@subject'];
+ value = {'@id': value['@id']};
}
else
{
@@ -2763,17 +2750,17 @@
var objs = embed.parent[embed.key];
for(var i in objs)
{
- if(objs[i].constructor === Object && '@subject' in objs[i] &&
- objs[i]['@subject']['@iri'] === iri)
+ if(objs[i].constructor === Object && '@id' in objs[i] &&
+ objs[i]['@id'] === iri)
{
- objs[i] = value['@subject'];
+ objs[i] = {'@id': value['@id']};
break;
}
}
}
else
{
- embed.parent[embed.key] = value['@subject'];
+ embed.parent[embed.key] = {'@id': value['@id']};
}
// recursively remove any dependent dangling embeds
@@ -2784,7 +2771,7 @@
{
i = iris[i];
if(i in embeds && embeds[i].parent !== null &&
- embeds[i].parent['@subject']['@iri'] === iri)
+ embeds[i].parent['@id'] === iri)
{
delete embeds[i];
removeDependents(i);
@@ -2800,15 +2787,15 @@
embed.key = parentKey;
// check explicit flag
- var explicitOn =
- frame['@explicit'] === true || options.defaults.explicitOn;
+ 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))
+ // do not remove @id or any frame key
+ if(key !== '@id' && !(key in frame))
{
delete value[key];
}
@@ -2819,9 +2806,9 @@
var keys = Object.keys(value);
for(i in keys)
{
- // skip keywords and type
+ // skip keywords
var key = keys[i];
- if(key.indexOf('@') !== 0 && key !== '@type')
+ if(key.indexOf('@') !== 0)
{
// get the subframe if available
if(key in frame)
@@ -2841,12 +2828,12 @@
var input = (v.constructor === Array) ? v : [v];
for(var n in input)
{
- // replace reference to subject w/subject
+ // replace reference to subject w/embedded subject
if(input[n].constructor === Object &&
- '@iri' in input[n] &&
- input[n]['@iri'] in subjects)
+ '@id' in input[n] &&
+ input[n]['@id'] in subjects)
{
- input[n] = subjects[input[n]['@iri']];
+ input[n] = subjects[input[n]['@id']];
}
}
value[key] = _frame(
@@ -2857,9 +2844,8 @@
// 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 !== '@type' &&
- (!(key in value) || value[key] === null))
+ // skip keywords and non-null keys in value
+ if(key.indexOf('@') !== 0 && (!(key in value) || value[key] === null))
{
var f = frame[key];
@@ -2878,8 +2864,8 @@
}
// determine if omit default is on
- var omitOn =
- f['@omitDefault'] === true || options.defaults.omitDefaultOn;
+ var omitOn = (
+ f['@omitDefault'] === true || options.defaults.omitDefaultOn);
if(!omitOn)
{
if('@default' in f)
@@ -2899,7 +2885,7 @@
}
return value;
-}
+};
/**
* Recursively frames the given input according to the given frame.
@@ -2976,7 +2962,7 @@
var value = values[i1][i2];
// if value is a subject, do subframing
- if(value.constructor === Object && '@subject' in value)
+ if(_isSubject(value))
{
value = _subframe(
subjects, value, frame, embeds, autoembed,
@@ -2990,9 +2976,8 @@
}
else
{
- // determine if value is a reference
- var isRef = (value !== null && value.constructor === Object &&
- '@iri' in value && value['@iri'] in embeds);
+ // determine if value is a reference to an embed
+ var isRef = (_isReference(value) && value['@id'] in embeds);
// push any value that isn't a parentless reference
if(!(parent === null && isRef))
@@ -3064,7 +3049,7 @@
var subjects = {};
for(var i in input)
{
- subjects[input[i]['@subject']['@iri']] = input[i];
+ subjects[input[i]['@id']] = input[i];
}
// frame input
--- a/playground/playground-examples.js Thu Dec 15 16:55:21 2011 -0500
+++ b/playground/playground-examples.js Tue Dec 20 00:07:26 2011 -0500
@@ -13,61 +13,55 @@
playground.frames = {};
// add the example of a Person
- playground.examples["Person"] =
- {
- "@context":
- {
+ playground.examples["Person"] = {
+ "@context": {
"name": "http://xmlns.com/foaf/0.1/name",
- "homepage": "http://xmlns.com/foaf/0.1/homepage",
- "xsd": "http://www.w3.org/2001/XMLSchema#",
- "@coerce":
- {
- "homepage": "@iri"
- }
+ "homepage": {
+ "@id": "http://xmlns.com/foaf/0.1/homepage",
+ "@type": "@id"
+ },
+ "xsd": "http://www.w3.org/2001/XMLSchema#"
},
"name": "Manu Sporny",
"homepage": "http://manu.sporny.org/"
};
// add the example of a Place
- playground.examples["Place"] =
- {
- "@context":
- {
+ playground.examples["Place"] = {
+ "@context": {
"name": "http://schema.org/name",
"description": "http://schema.org/description",
- "image": "http://schema.org/image",
+ "image": {
+ "@id": "http://schema.org/image",
+ "@type": "@id"
+ },
"geo": "http://schema.org/geo",
- "latitude": "http://schema.org/latitude",
- "longitude": "http://schema.org/longitude",
- "xsd": "http://www.w3.org/2001/XMLSchema#",
- "@coerce":
- {
- "image": "@iri",
- "latitude": "xsd:float",
- "longitude": "xsd:float"
- }
+ "latitude": {
+ "@id": "http://schema.org/latitude",
+ "@type": "xsd:float"
+ },
+ "longitude": {
+ "@id": "http://schema.org/longitude",
+ "@type": "xsd:float"
+ },
+ "xsd": "http://www.w3.org/2001/XMLSchema#"
},
"name": "The Empire State Building",
"description": "The Empire State Building is a 102-story landmark in New York City.",
"image": "http://www.civil.usherbrooke.ca/cours/gci215a/empire-state-building.jpg",
- "geo":
- {
+ "geo": {
"latitude": "40.75",
"longitude": "73.98"
}
};
// add the example of a Event
- playground.examples["Event"] =
- {
- "@context":
- {
+ playground.examples["Event"] = {
+ "@context": {
"ical": "http://www.w3.org/2002/12/cal/ical#",
"xsd": "http://www.w3.org/2001/XMLSchema#",
- "@coerce":
- {
- "ical:dtstart": "xsd:dateTime"
+ "ical:dtstart": {
+ "@type": "xsd:dateTime"
}
},
"ical:summary": "Lady Gaga Concert",
@@ -76,35 +70,36 @@
};
// add the example of a Product
- playground.examples["Product"] =
- {
- "@context":
- {
+ playground.examples["Product"] = {
+ "@context": {
"gr": "http://purl.org/goodrelations/v1#",
"pto": "http://www.productontology.org/id/",
"foaf": "http://xmlns.com/foaf/0.1/",
"xsd": "http://www.w3.org/2001/XMLSchema#",
- "@coerce":
- {
- "foaf:page": "@iri",
- "gr:acceptedPaymentMethods": "@iri",
- "gr:hasBusinessFunction": "@iri",
- "gr:hasCurrencyValue": "xsd:float"
+ "foaf:page": {
+ "@type": "@id"
+ },
+ "gr:acceptedPaymentMethods": {
+ "@type": "@id"
+ },
+ "gr:hasBusinessFunction": {
+ "@type": "@id"
+ },
+ "gr:hasCurrencyValue": {
+ "@type": "xsd:float"
}
},
- "@subject": "http://example.org/cars/for-sale#tesla",
+ "@id": "http://example.org/cars/for-sale#tesla",
"@type": "gr:Offering",
"gr:name": "Used Tesla Roadster",
"gr:description": "Need to sell fast and furiously",
"gr:hasBusinessFunction": "gr:Sell",
"gr:acceptedPaymentMethods": "gr:Cash",
- "gr:hasPriceSpecification":
- {
+ "gr:hasPriceSpecification": {
"gr:hasCurrencyValue": "85000",
"gr:hasCurrency": "USD"
},
- "gr:includes":
- {
+ "gr:includes": {
"@type": ["gr:Individual", "pto:Vehicle"],
"gr:name": "Tesla Roadster",
"foaf:page": "http://www.teslamotors.com/roadster"
@@ -112,101 +107,80 @@
};
// add the example of a Recipe
- playground.examples["Recipe"] =
- {
- "@context":
- {
+ playground.examples["Recipe"] = {
+ "@context": {
"name": "http://rdf.data-vocabulary.org/#name",
"ingredient": "http://rdf.data-vocabulary.org/#ingredients",
"yield": "http://rdf.data-vocabulary.org/#yield",
"instructions": "http://rdf.data-vocabulary.org/#instructions",
- "step": "http://rdf.data-vocabulary.org/#step",
+ "step": {
+ "@id": "http://rdf.data-vocabulary.org/#step",
+ "@type": "xsd:integer"
+ },
"description": "http://rdf.data-vocabulary.org/#description",
- "xsd": "http://www.w3.org/2001/XMLSchema#",
- "@coerce":
- {
- "step": "xsd:integer"
- }
+ "xsd": "http://www.w3.org/2001/XMLSchema#"
},
"name": "Mojito",
"ingredient": ["12 fresh mint leaves", "1/2 lime, juiced with pulp",
"1 tablespoons white sugar", "1 cup ice cubes",
"2 fluid ounces white rum", "1/2 cup club soda"],
"yield": "1 cocktail",
- "instructions" :
- [
- {
- "step": 1,
- "description": "Crush lime juice, mint and sugar together in glass."
- },
- {
- "step": 2,
- "description": "Fill glass to top with ice cubes."
- },
- {
- "step": 3,
- "description": "Pour white rum over ice."
- },
- {
- "step": 4,
- "description": "Fill the rest of glass with club soda, stir."
- },
- {
- "step": 5,
- "description": "Garnish with a lime wedge."
- }
- ]
+ "instructions" : [{
+ "step": 1,
+ "description": "Crush lime juice, mint and sugar together in glass."
+ }, {
+ "step": 2,
+ "description": "Fill glass to top with ice cubes."
+ }, {
+ "step": 3,
+ "description": "Pour white rum over ice."
+ }, {
+ "step": 4,
+ "description": "Fill the rest of glass with club soda, stir."
+ }, {
+ "step": 5,
+ "description": "Garnish with a lime wedge."
+ }]
};
// add the example of a Library
- playground.examples["Library"] =
- {
- "@context":
- {
- "@coerce":
- {
- "ex:contains": "@iri"
- },
+ playground.examples["Library"] = {
+ "@context": {
"dc": "http://purl.org/dc/elements/1.1/",
"ex": "http://example.org/vocab#",
- "xsd": "http://www.w3.org/2001/XMLSchema#"
+ "xsd": "http://www.w3.org/2001/XMLSchema#",
+ "ex:contains": {
+ "@type": "@id"
+ }
},
- "@subject": [
- {
- "@subject": "http://example.org/library",
- "@type": "ex:Library",
- "ex:contains": "http://example.org/library/the-republic"
- },
- {
- "@subject": "http://example.org/library/the-republic",
- "@type": "ex:Book",
- "dc:creator": "Plato",
- "dc:title": "The Republic",
- "ex:contains": "http://example.org/library/the-republic#introduction"
- },
- {
- "@subject": "http://example.org/library/the-republic#introduction",
- "@type": "ex:Chapter",
- "dc:description": "An introductory chapter on The Republic.",
- "dc:title": "The Introduction"
- }
- ]
+ "@id": [{
+ "@id": "http://example.org/library",
+ "@type": "ex:Library",
+ "ex:contains": "http://example.org/library/the-republic"
+ }, {
+ "@id": "http://example.org/library/the-republic",
+ "@type": "ex:Book",
+ "dc:creator": "Plato",
+ "dc:title": "The Republic",
+ "ex:contains": "http://example.org/library/the-republic#introduction"
+ }, {
+ "@id": "http://example.org/library/the-republic#introduction",
+ "@type": "ex:Chapter",
+ "dc:description": "An introductory chapter on The Republic.",
+ "dc:title": "The Introduction"
+ }]
};
// add the frame example of a Library
- playground.frames["Library"] =
- {
- "@context":
- {
+ playground.frames["Library"] = {
+ "@context": {
"dc": "http://purl.org/dc/elements/1.1/",
"ex": "http://example.org/vocab#"
},
"@type": "ex:Library",
- "ex:contains":
- {
+ "ex:contains": {
"@type": "ex:Book",
- "ex:contains":
- {
+ "ex:contains": {
"@type": "ex:Chapter"
}
}