Update to latest jsonld.js (use new Promise API).
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/playground/Promise.js Mon Jul 08 14:51:22 2013 -0400
@@ -0,0 +1,415 @@
+// Copyright (C) 2013:
+// Alex Russell <slightlyoff@chromium.org>
+// Yehuda Katz
+//
+// Use of this source code is governed by
+// http://www.apache.org/licenses/LICENSE-2.0
+
+// FIXME(slightlyoff):
+// - Document "npm test"
+// - Change global name from "Promise" to something less conflicty
+(function(global, browserGlobal, underTest) {
+"use strict";
+
+// FIXME(slighltyoff):
+// * aggregates + tests
+// * check on fast-forwarding
+
+underTest = !!underTest;
+
+//
+// Async Utilities
+//
+
+// Borrowed from RSVP.js
+var async;
+
+var MutationObserver = browserGlobal.MutationObserver ||
+ browserGlobal.WebKitMutationObserver;
+var Promise;
+
+if (typeof process !== 'undefined' &&
+ {}.toString.call(process) === '[object process]') {
+ async = function(callback, binding) {
+ process.nextTick(function() {
+ callback.call(binding);
+ });
+ };
+} else if (MutationObserver) {
+ var queue = [];
+
+ var observer = new MutationObserver(function() {
+ var toProcess = queue.slice();
+ queue = [];
+ toProcess.forEach(function(tuple) {
+ tuple[0].call(tuple[1]);
+ });
+ });
+
+ var element = document.createElement('div');
+ observer.observe(element, { attributes: true });
+
+ // Chrome Memory Leak: https://bugs.webkit.org/show_bug.cgi?id=93661
+ window.addEventListener('unload', function(){
+ observer.disconnect();
+ observer = null;
+ });
+
+ async = function(callback, binding) {
+ queue.push([callback, binding]);
+ element.setAttribute('drainQueue', 'drainQueue');
+ };
+} else {
+ async = function(callback, binding) {
+ setTimeout(function() {
+ callback.call(binding);
+ }, 1);
+ };
+}
+
+//
+// Object Model Utilities
+//
+
+// defineProperties utilities
+var _readOnlyProperty = function(v) {
+ return {
+ enumerable: true,
+ configurable: false,
+ get: v
+ };
+};
+
+var _method = function(v, e, c, w) {
+ return {
+ enumerable: !!(e || 0),
+ configurable: !!(c || 1),
+ writable: !!(w || 1),
+ value: v || function() {}
+ };
+};
+
+var _pseudoPrivate = function(v) { return _method(v, 0, 1, 0); };
+var _public = function(v) { return _method(v, 1); };
+
+//
+// Promises Utilities
+//
+
+var isThenable = function(any) {
+ try {
+ var f = any.then;
+ if (typeof f == "function") {
+ return true;
+ }
+ } catch (e) { /*squelch*/ }
+ return false;
+};
+
+var AlreadyResolved = function(name) {
+ Error.call(this, name);
+};
+AlreadyResolved.prototype = Object.create(Error.prototype);
+
+var Backlog = function() {
+ var bl = [];
+ bl.pump = function(value) {
+ async(function() {
+ var l = bl.length;
+ var x = 0;
+ while(x < l) {
+ x++;
+ bl.shift()(value);
+ }
+ });
+ };
+ return bl;
+};
+
+//
+// Resolver Constuctor
+//
+
+var Resolver = function(future,
+ fulfillCallbacks,
+ rejectCallbacks,
+ setValue,
+ setError,
+ setState) {
+ var isResolved = false;
+
+ var resolver = this;
+ var fulfill = function(value) {
+ // console.log("queueing fulfill with:", value);
+ async(function() {
+ setState("fulfilled");
+ setValue(value);
+ // console.log("fulfilling with:", value);
+ fulfillCallbacks.pump(value);
+ });
+ };
+ var reject = function(reason) {
+ // console.log("queuing reject with:", reason);
+ async(function() {
+ setState("rejected");
+ setError(reason);
+ // console.log("rejecting with:", reason);
+ rejectCallbacks.pump(reason);
+ });
+ };
+ var resolve = function(value) {
+ if (isThenable(value)) {
+ value.then(resolve, reject);
+ return;
+ }
+ fulfill(value);
+ };
+ var ifNotResolved = function(func, name) {
+ return function(value) {
+ if (!isResolved) {
+ isResolved = true;
+ func(value);
+ } else {
+ if (typeof console != "undefined") {
+ console.error("Cannot resolve a Promise multiple times.");
+ }
+ }
+ };
+ };
+
+ // Indirectly resolves the Promise, chaining any passed Promise's resolution
+ this.resolve = ifNotResolved(resolve, "resolve");
+
+ // Directly fulfills the future, no matter what value's type is
+ this.fulfill = ifNotResolved(fulfill, "fulfill");
+
+ // Rejects the future
+ this.reject = ifNotResolved(reject, "reject");
+
+ this.cancel = function() { resolver.reject(new Error("Cancel")); };
+ this.timeout = function() { resolver.reject(new Error("Timeout")); };
+
+ if (underTest) {
+ Object.defineProperties(this, {
+ _isResolved: _readOnlyProperty(function() { return isResolved; }),
+ });
+ }
+
+ setState("pending");
+};
+
+//
+// Promise Constuctor
+//
+
+var Promise = function(init) {
+ var fulfillCallbacks = new Backlog();
+ var rejectCallbacks = new Backlog();
+ var value;
+ var error;
+ var state = "pending";
+
+ if (underTest) {
+ Object.defineProperties(this, {
+ _value: _readOnlyProperty(function() { return value; }),
+ _error: _readOnlyProperty(function() { return error; }),
+ _state: _readOnlyProperty(function() { return state; }),
+ });
+ }
+
+ Object.defineProperties(this, {
+ _addAcceptCallback: _pseudoPrivate(
+ function(cb) {
+ // console.log("adding fulfill callback:", cb);
+ fulfillCallbacks.push(cb);
+ if (state == "fulfilled") {
+ fulfillCallbacks.pump(value);
+ }
+ }
+ ),
+ _addRejectCallback: _pseudoPrivate(
+ function(cb) {
+ // console.log("adding reject callback:", cb);
+ rejectCallbacks.push(cb);
+ if (state == "rejected") {
+ rejectCallbacks.pump(error);
+ }
+ }
+ ),
+ });
+ var r = new Resolver(this,
+ fulfillCallbacks, rejectCallbacks,
+ function(v) { value = v; },
+ function(e) { error = e; },
+ function(s) { state = s; })
+ try {
+ if (init) { init(r); }
+ } catch(e) {
+ r.reject(e);
+ }
+};
+
+//
+// Consructor
+//
+
+var isCallback = function(any) {
+ return (typeof any == "function");
+};
+
+// Used in .then()
+var wrap = function(callback, resolver, disposition) {
+ if (!isCallback(callback)) {
+ // If we don't get a callback, we want to forward whatever resolution we get
+ return resolver[disposition].bind(resolver);
+ }
+
+ return function() {
+ try {
+ var r = callback.apply(null, arguments);
+ resolver.resolve(r);
+ } catch(e) {
+ // Exceptions reject the resolver
+ resolver.reject(e);
+ }
+ };
+};
+
+var addCallbacks = function(onfulfill, onreject, scope) {
+ if (isCallback(onfulfill)) {
+ scope._addAcceptCallback(onfulfill);
+ }
+ if (isCallback(onreject)) {
+ scope._addRejectCallback(onreject);
+ }
+ return scope;
+};
+
+//
+// Prototype properties
+//
+
+Promise.prototype = Object.create(null, {
+ "then": _public(function(onfulfill, onreject) {
+ // The logic here is:
+ // We return a new Promise whose resolution merges with the return from
+ // onfulfill() or onerror(). If onfulfill() returns a Promise, we forward
+ // the resolution of that future to the resolution of the returned
+ // Promise.
+ var f = this;
+ return new Promise(function(r) {
+ addCallbacks(wrap(onfulfill, r, "resolve"),
+ wrap(onreject, r, "reject"), f);
+ });
+ }),
+ "catch": _public(function(onreject) {
+ var f = this;
+ return new Promise(function(r) {
+ addCallbacks(null, wrap(onreject, r, "reject"), f);
+ });
+ }),
+});
+
+//
+// Statics
+//
+
+Promise.isThenable = isThenable;
+
+var toPromiseList = function(list) {
+ return Array.prototype.slice.call(list).map(Promise.resolve);
+};
+
+Promise.any = function(/*...futuresOrValues*/) {
+ var futures = toPromiseList(arguments);
+ return new Promise(function(r) {
+ if (!futures.length) {
+ r.reject("No futures passed to Promise.any()");
+ } else {
+ var resolved = false;
+ var firstSuccess = function(value) {
+ if (resolved) { return; }
+ resolved = true;
+ r.resolve(value);
+ };
+ var firstFailure = function(reason) {
+ if (resolved) { return; }
+ resolved = true;
+ r.reject(reason);
+ };
+ futures.forEach(function(f, idx) {
+ f.then(firstSuccess, firstFailure);
+ });
+ }
+ });
+};
+
+Promise.every = function(/*...futuresOrValues*/) {
+ var futures = toPromiseList(arguments);
+ return new Promise(function(r) {
+ if (!futures.length) {
+ r.reject("No futures passed to Promise.every()");
+ } else {
+ var values = new Array(futures.length);
+ var count = 0;
+ var accumulate = function(idx, v) {
+ count++;
+ values[idx] = v;
+ if (count == futures.length) {
+ r.resolve(values);
+ }
+ };
+ futures.forEach(function(f, idx) {
+ f.then(accumulate.bind(null, idx), r.reject);
+ });
+ }
+ });
+};
+
+Promise.some = function() {
+ var futures = toPromiseList(arguments);
+ return new Promise(function(r) {
+ if (!futures.length) {
+ r.reject("No futures passed to Promise.some()");
+ } else {
+ var count = 0;
+ var accumulateFailures = function(e) {
+ count++;
+ if (count == futures.length) {
+ r.reject();
+ }
+ };
+ futures.forEach(function(f, idx) {
+ f.then(r.resolve, accumulateFailures);
+ });
+ }
+ });
+};
+
+Promise.fulfill = function(value) {
+ return new Promise(function(r) {
+ r.fulfill(value);
+ });
+};
+
+Promise.resolve = function(value) {
+ return new Promise(function(r) {
+ r.resolve(value);
+ });
+};
+
+Promise.reject = function(reason) {
+ return new Promise(function(r) {
+ r.reject(reason);
+ });
+};
+
+//
+// Export
+//
+
+global.Promise = Promise;
+
+})(this,
+ (typeof window !== 'undefined') ? window : {},
+ this.runningUnderTest||false);
--- a/playground/index.html Mon Jul 08 14:09:59 2013 -0400
+++ b/playground/index.html Mon Jul 08 14:51:22 2013 -0400
@@ -29,7 +29,7 @@
<script type="text/javascript" src="../common/prettify.js"></script>
<script type="text/javascript" src="../common/lang-jsonld.js"></script>
<script type="text/javascript" src="../common/lang-nquads.js"></script>
- <script type="text/javascript" src="Future.js"></script>
+ <script type="text/javascript" src="Promise.js"></script>
<script type="text/javascript" src="jsonld.js"></script>
<script type="text/javascript" src="playground.js"></script>
<script type="text/javascript" src="playground-examples.js"></script>
--- a/playground/jsonld.js Mon Jul 08 14:09:59 2013 -0400
+++ b/playground/jsonld.js Mon Jul 08 14:51:22 2013 -0400
@@ -817,16 +817,16 @@
'implemented.', 'jsonld.DocumentUrlError'), url);
};
-/* Futures/Promises API */
-
-jsonld.futures = jsonld.promises = function() {
- var Future = _nodejs ? require('./Future') : global.Future;
+/* Promises API */
+
+jsonld.promises = function() {
+ var Promise = _nodejs ? require('./Promise').Promise : global.Promise;
var slice = Array.prototype.slice;
- // converts a node.js async op into a future w/boxed resolved value(s)
- function futurize(op) {
+ // converts a node.js async op into a promise w/boxed resolved value(s)
+ function promisify(op) {
var args = slice.call(arguments, 1);
- return new Future(function(resolver) {
+ return new Promise(function(resolver) {
op.apply(null, args.concat(function(err, value) {
if(err) {
resolver.reject(err);
@@ -861,7 +861,7 @@
if('loadDocument' in options) {
options.loadDocument = createDocumentLoader(options.loadDocument);
}
- return futurize.apply(null, [jsonld.expand].concat(slice.call(arguments)));
+ return promisify.apply(null, [jsonld.expand].concat(slice.call(arguments)));
};
api.compact = function(input, ctx) {
if(arguments.length < 2) {
@@ -877,7 +877,7 @@
callback(err, compacted);
});
};
- return futurize.apply(null, [compact].concat(slice.call(arguments)));
+ return promisify.apply(null, [compact].concat(slice.call(arguments)));
};
api.flatten = function(input) {
if(arguments.length < 1) {
@@ -887,7 +887,8 @@
if('loadDocument' in options) {
options.loadDocument = createDocumentLoader(options.loadDocument);
}
- return futurize.apply(null, [jsonld.flatten].concat(slice.call(arguments)));
+ return promisify.apply(
+ null, [jsonld.flatten].concat(slice.call(arguments)));
};
api.frame = function(input, frame) {
if(arguments.length < 2) {
@@ -897,13 +898,14 @@
if('loadDocument' in options) {
options.loadDocument = createDocumentLoader(options.loadDocument);
}
- return futurize.apply(null, [jsonld.frame].concat(slice.call(arguments)));
+ return promisify.apply(null, [jsonld.frame].concat(slice.call(arguments)));
};
api.fromRDF = function(dataset) {
if(arguments.length < 1) {
throw new TypeError('Could not convert from RDF, too few arguments.');
}
- return futurize.apply(null, [jsonld.fromRDF].concat(slice.call(arguments)));
+ return promisify.apply(
+ null, [jsonld.fromRDF].concat(slice.call(arguments)));
};
api.toRDF = function(input) {
if(arguments.length < 1) {
@@ -913,7 +915,7 @@
if('loadDocument' in options) {
options.loadDocument = createDocumentLoader(options.loadDocument);
}
- return futurize.apply(null, [jsonld.toRDF].concat(slice.call(arguments)));
+ return promisify.apply(null, [jsonld.toRDF].concat(slice.call(arguments)));
};
api.normalize = function(input) {
if(arguments.length < 1) {
@@ -923,7 +925,7 @@
if('loadDocument' in options) {
options.loadDocument = createDocumentLoader(options.loadDocument);
}
- return futurize.apply(
+ return promisify.apply(
null, [jsonld.normalize].concat(slice.call(arguments)));
};
return api;
@@ -932,7 +934,7 @@
/* WebIDL API */
function JsonLdProcessor() {}
-JsonLdProcessor.prototype = jsonld.futures();
+JsonLdProcessor.prototype = jsonld.promises();
JsonLdProcessor.prototype.toString = function() {
if(this instanceof JsonLdProcessor) {
return '[object JsonLdProcessor]';
--- a/playground/playground.js Mon Jul 08 14:09:59 2013 -0400
+++ b/playground/playground.js Mon Jul 08 14:51:22 2013 -0400
@@ -157,7 +157,6 @@
* @param ui the ui tab object that was selected
*/
playground.tabSelected = function(event, ui) {
- console.log('tab selected');
playground.activeTab = ui.tab.id;
if(ui.tab.id === 'tab-compacted' || ui.tab.id === 'tab-flattened' ||
ui.tab.id === 'tab-framed') {
@@ -191,21 +190,21 @@
};
/**
- * Returns a Future to performs the JSON-LD API action based on the active
+ * Returns a Promise to performs the JSON-LD API action based on the active
* tab.
*
* @param input the JSON-LD object input or null no error.
* @param param the JSON-LD param to use.
*/
playground.performAction = function(input, param) {
- return new Future(function(resolver) {
+ return new Promise(function(resolver) {
var processor = new jsonld.JsonLdProcessor();
// set base IRI
var options = {base: document.baseURI};
if(playground.activeTab === 'tab-compacted') {
- processor.compact(input, param, options).done(function(compacted) {
+ processor.compact(input, param, options).then(function(compacted) {
$('#compacted').html(js_beautify(
playground.htmlEscape(JSON.stringify(compacted)),
{'indent_size': 2}).replace(/\n/g, '<br>'));
@@ -213,7 +212,7 @@
}, resolver.reject);
}
else if(playground.activeTab === 'tab-expanded') {
- processor.expand(input, options).done(function(expanded) {
+ processor.expand(input, options).then(function(expanded) {
$('#expanded').html(js_beautify(
playground.htmlEscape(JSON.stringify(expanded)),
{'indent_size': 2}).replace(/\n/g, '<br>'));
@@ -221,7 +220,7 @@
}, resolver.reject);
}
else if(playground.activeTab === 'tab-flattened') {
- processor.flatten(input, param, options).done(function(flattened) {
+ processor.flatten(input, param, options).then(function(flattened) {
$('#flattened').html(js_beautify(
playground.htmlEscape(JSON.stringify(flattened)),
{'indent_size': 2}).replace(/\n/g, '<br>'));
@@ -229,7 +228,7 @@
}, resolver.reject);
}
else if(playground.activeTab === 'tab-framed') {
- processor.frame(input, param, options).done(function(framed) {
+ processor.frame(input, param, options).then(function(framed) {
$('#framed').html(js_beautify(
playground.htmlEscape(JSON.stringify(framed)),
{'indent_size': 2}).replace(/\n/g, '<br>'));
@@ -238,7 +237,7 @@
}
else if(playground.activeTab === 'tab-nquads') {
options.format = 'application/nquads';
- processor.toRDF(input, options).done(function(nquads) {
+ processor.toRDF(input, options).then(function(nquads) {
$('#nquads').html(
playground.htmlEscape(nquads).replace(/\n/g, '<br>'));
resolver.resolve();
@@ -246,7 +245,7 @@
}
else if(playground.activeTab === 'tab-normalized') {
options.format = 'application/nquads';
- processor.normalize(input, options).done(function(normalized) {
+ processor.normalize(input, options).then(function(normalized) {
$('#normalized').html(
playground.htmlEscape(normalized).replace(/\n/g, '<br>'));
resolver.resolve();
@@ -315,7 +314,7 @@
}
// no errors, perform the action and display the output
- playground.performAction(input, param).done(function() {
+ playground.performAction(input, param).then(function() {
// generate a link for current data
var link = '?json-ld=' + encodeURIComponent(JSON.stringify(input));
if($('#frame').val().length > 0) {