From 39e0de0a3ccf8a1dcb03a0a99badf7d6195ca38f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hasan=20Balc=C4=B1?= Date: Tue, 5 Jul 2022 16:25:43 +0300 Subject: [PATCH] Update on infer nesting on load and performance improvements --- sbgnviz.js | 69044 ++++++++-------- .../sbgn-cy-instance-factory.js | 14 +- src/utilities/graph-utilities-factory.js | 26 + 3 files changed, 34572 insertions(+), 34512 deletions(-) diff --git a/sbgnviz.js b/sbgnviz.js index fa7f98f1..b665dff6 100644 --- a/sbgnviz.js +++ b/sbgnviz.js @@ -1126,7 +1126,11 @@ }).call(this,_dereq_('_process')) -},{"_process":47}],2:[function(_dereq_,module,exports){ +},{"_process":74}],2:[function(_dereq_,module,exports){ + +},{}],3:[function(_dereq_,module,exports){ +arguments[4][2][0].apply(exports,arguments) +},{"dup":2}],4:[function(_dereq_,module,exports){ var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; ;(function (exports) { @@ -1252,808 +1256,266 @@ var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; exports.fromByteArray = uint8ToBase64 }(typeof exports === 'undefined' ? (this.base64js = {}) : exports)) -},{}],3:[function(_dereq_,module,exports){ - -},{}],4:[function(_dereq_,module,exports){ -arguments[4][3][0].apply(exports,arguments) -},{"dup":3}],5:[function(_dereq_,module,exports){ +},{}],5:[function(_dereq_,module,exports){ (function (global){ -/*! https://mths.be/punycode v1.4.1 by @mathias */ -;(function(root) { +/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ +/* eslint-disable no-proto */ - /** Detect free variables */ - var freeExports = typeof exports == 'object' && exports && - !exports.nodeType && exports; - var freeModule = typeof module == 'object' && module && - !module.nodeType && module; - var freeGlobal = typeof global == 'object' && global; - if ( - freeGlobal.global === freeGlobal || - freeGlobal.window === freeGlobal || - freeGlobal.self === freeGlobal - ) { - root = freeGlobal; - } +'use strict' - /** - * The `punycode` object. - * @name punycode - * @type Object - */ - var punycode, +var base64 = _dereq_('base64-js') +var ieee754 = _dereq_('ieee754') +var isArray = _dereq_('isarray') - /** Highest positive signed 32-bit float value */ - maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1 +exports.Buffer = Buffer +exports.SlowBuffer = SlowBuffer +exports.INSPECT_MAX_BYTES = 50 +Buffer.poolSize = 8192 // not used by this implementation - /** Bootstring parameters */ - base = 36, - tMin = 1, - tMax = 26, - skew = 38, - damp = 700, - initialBias = 72, - initialN = 128, // 0x80 - delimiter = '-', // '\x2D' +var rootParent = {} - /** Regular expressions */ - regexPunycode = /^xn--/, - regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars - regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators +/** + * If `Buffer.TYPED_ARRAY_SUPPORT`: + * === true Use Uint8Array implementation (fastest) + * === false Use Object implementation (most compatible, even IE6) + * + * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, + * Opera 11.6+, iOS 4.2+. + * + * Due to various browser bugs, sometimes the Object implementation will be used even + * when the browser supports typed arrays. + * + * Note: + * + * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances, + * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438. + * + * - Safari 5-7 lacks support for changing the `Object.prototype.constructor` property + * on objects. + * + * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function. + * + * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of + * incorrect length in some situations. - /** Error messages */ - errors = { - 'overflow': 'Overflow: input needs wider integers to process', - 'not-basic': 'Illegal input >= 0x80 (not a basic code point)', - 'invalid-input': 'Invalid input' - }, + * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they + * get the Object implementation, which is slower but behaves correctly. + */ +Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined + ? global.TYPED_ARRAY_SUPPORT + : typedArraySupport() - /** Convenience shortcuts */ - baseMinusTMin = base - tMin, - floor = Math.floor, - stringFromCharCode = String.fromCharCode, +function typedArraySupport () { + function Bar () {} + try { + var arr = new Uint8Array(1) + arr.foo = function () { return 42 } + arr.constructor = Bar + return arr.foo() === 42 && // typed array instances can be augmented + arr.constructor === Bar && // constructor can be set + typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray` + arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray` + } catch (e) { + return false + } +} - /** Temporary variable */ - key; +function kMaxLength () { + return Buffer.TYPED_ARRAY_SUPPORT + ? 0x7fffffff + : 0x3fffffff +} - /*--------------------------------------------------------------------------*/ +/** + * Class: Buffer + * ============= + * + * The Buffer constructor returns instances of `Uint8Array` that are augmented + * with function properties for all the node `Buffer` API functions. We use + * `Uint8Array` so that square bracket notation works as expected -- it returns + * a single octet. + * + * By augmenting the instances, we can avoid modifying the `Uint8Array` + * prototype. + */ +function Buffer (arg) { + if (!(this instanceof Buffer)) { + // Avoid going through an ArgumentsAdaptorTrampoline in the common case. + if (arguments.length > 1) return new Buffer(arg, arguments[1]) + return new Buffer(arg) + } - /** - * A generic error utility function. - * @private - * @param {String} type The error type. - * @returns {Error} Throws a `RangeError` with the applicable error message. - */ - function error(type) { - throw new RangeError(errors[type]); - } + if (!Buffer.TYPED_ARRAY_SUPPORT) { + this.length = 0 + this.parent = undefined + } - /** - * A generic `Array#map` utility function. - * @private - * @param {Array} array The array to iterate over. - * @param {Function} callback The function that gets called for every array - * item. - * @returns {Array} A new array of values returned by the callback function. - */ - function map(array, fn) { - var length = array.length; - var result = []; - while (length--) { - result[length] = fn(array[length]); - } - return result; - } + // Common case. + if (typeof arg === 'number') { + return fromNumber(this, arg) + } - /** - * A simple `Array#map`-like wrapper to work with domain name strings or email - * addresses. - * @private - * @param {String} domain The domain name or email address. - * @param {Function} callback The function that gets called for every - * character. - * @returns {Array} A new string of characters returned by the callback - * function. - */ - function mapDomain(string, fn) { - var parts = string.split('@'); - var result = ''; - if (parts.length > 1) { - // In email addresses, only the domain name should be punycoded. Leave - // the local part (i.e. everything up to `@`) intact. - result = parts[0] + '@'; - string = parts[1]; - } - // Avoid `split(regex)` for IE8 compatibility. See #17. - string = string.replace(regexSeparators, '\x2E'); - var labels = string.split('.'); - var encoded = map(labels, fn).join('.'); - return result + encoded; - } + // Slightly less common case. + if (typeof arg === 'string') { + return fromString(this, arg, arguments.length > 1 ? arguments[1] : 'utf8') + } - /** - * Creates an array containing the numeric code points of each Unicode - * character in the string. While JavaScript uses UCS-2 internally, - * this function will convert a pair of surrogate halves (each of which - * UCS-2 exposes as separate characters) into a single code point, - * matching UTF-16. - * @see `punycode.ucs2.encode` - * @see - * @memberOf punycode.ucs2 - * @name decode - * @param {String} string The Unicode input string (UCS-2). - * @returns {Array} The new array of code points. - */ - function ucs2decode(string) { - var output = [], - counter = 0, - length = string.length, - value, - extra; - while (counter < length) { - value = string.charCodeAt(counter++); - if (value >= 0xD800 && value <= 0xDBFF && counter < length) { - // high surrogate, and there is a next character - extra = string.charCodeAt(counter++); - if ((extra & 0xFC00) == 0xDC00) { // low surrogate - output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); - } else { - // unmatched surrogate; only append this code unit, in case the next - // code unit is the high surrogate of a surrogate pair - output.push(value); - counter--; - } - } else { - output.push(value); - } - } - return output; - } + // Unusual. + return fromObject(this, arg) +} - /** - * Creates a string based on an array of numeric code points. - * @see `punycode.ucs2.decode` - * @memberOf punycode.ucs2 - * @name encode - * @param {Array} codePoints The array of numeric code points. - * @returns {String} The new Unicode string (UCS-2). - */ - function ucs2encode(array) { - return map(array, function(value) { - var output = ''; - if (value > 0xFFFF) { - value -= 0x10000; - output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); - value = 0xDC00 | value & 0x3FF; - } - output += stringFromCharCode(value); - return output; - }).join(''); - } +function fromNumber (that, length) { + that = allocate(that, length < 0 ? 0 : checked(length) | 0) + if (!Buffer.TYPED_ARRAY_SUPPORT) { + for (var i = 0; i < length; i++) { + that[i] = 0 + } + } + return that +} - /** - * Converts a basic code point into a digit/integer. - * @see `digitToBasic()` - * @private - * @param {Number} codePoint The basic numeric code point value. - * @returns {Number} The numeric value of a basic code point (for use in - * representing integers) in the range `0` to `base - 1`, or `base` if - * the code point does not represent a value. - */ - function basicToDigit(codePoint) { - if (codePoint - 48 < 10) { - return codePoint - 22; - } - if (codePoint - 65 < 26) { - return codePoint - 65; - } - if (codePoint - 97 < 26) { - return codePoint - 97; - } - return base; - } +function fromString (that, string, encoding) { + if (typeof encoding !== 'string' || encoding === '') encoding = 'utf8' - /** - * Converts a digit/integer into a basic code point. - * @see `basicToDigit()` - * @private - * @param {Number} digit The numeric value of a basic code point. - * @returns {Number} The basic code point whose value (when used for - * representing integers) is `digit`, which needs to be in the range - * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is - * used; else, the lowercase form is used. The behavior is undefined - * if `flag` is non-zero and `digit` has no uppercase form. - */ - function digitToBasic(digit, flag) { - // 0..25 map to ASCII a..z or A..Z - // 26..35 map to ASCII 0..9 - return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5); - } + // Assumption: byteLength() return value is always < kMaxLength. + var length = byteLength(string, encoding) | 0 + that = allocate(that, length) - /** - * Bias adaptation function as per section 3.4 of RFC 3492. - * https://tools.ietf.org/html/rfc3492#section-3.4 - * @private - */ - function adapt(delta, numPoints, firstTime) { - var k = 0; - delta = firstTime ? floor(delta / damp) : delta >> 1; - delta += floor(delta / numPoints); - for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) { - delta = floor(delta / baseMinusTMin); - } - return floor(k + (baseMinusTMin + 1) * delta / (delta + skew)); - } + that.write(string, encoding) + return that +} - /** - * Converts a Punycode string of ASCII-only symbols to a string of Unicode - * symbols. - * @memberOf punycode - * @param {String} input The Punycode string of ASCII-only symbols. - * @returns {String} The resulting string of Unicode symbols. - */ - function decode(input) { - // Don't use UCS-2 - var output = [], - inputLength = input.length, - out, - i = 0, - n = initialN, - bias = initialBias, - basic, - j, - index, - oldi, - w, - k, - digit, - t, - /** Cached calculation results */ - baseMinusT; +function fromObject (that, object) { + if (Buffer.isBuffer(object)) return fromBuffer(that, object) - // Handle the basic code points: let `basic` be the number of input code - // points before the last delimiter, or `0` if there is none, then copy - // the first basic code points to the output. + if (isArray(object)) return fromArray(that, object) - basic = input.lastIndexOf(delimiter); - if (basic < 0) { - basic = 0; - } + if (object == null) { + throw new TypeError('must start with number, buffer, array or string') + } - for (j = 0; j < basic; ++j) { - // if it's not a basic code point - if (input.charCodeAt(j) >= 0x80) { - error('not-basic'); - } - output.push(input.charCodeAt(j)); - } + if (typeof ArrayBuffer !== 'undefined') { + if (object.buffer instanceof ArrayBuffer) { + return fromTypedArray(that, object) + } + if (object instanceof ArrayBuffer) { + return fromArrayBuffer(that, object) + } + } - // Main decoding loop: start just after the last delimiter if any basic code - // points were copied; start at the beginning otherwise. + if (object.length) return fromArrayLike(that, object) - for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) { + return fromJsonObject(that, object) +} - // `index` is the index of the next character to be consumed. - // Decode a generalized variable-length integer into `delta`, - // which gets added to `i`. The overflow checking is easier - // if we increase `i` as we go, then subtract off its starting - // value at the end to obtain `delta`. - for (oldi = i, w = 1, k = base; /* no condition */; k += base) { +function fromBuffer (that, buffer) { + var length = checked(buffer.length) | 0 + that = allocate(that, length) + buffer.copy(that, 0, 0, length) + return that +} - if (index >= inputLength) { - error('invalid-input'); - } +function fromArray (that, array) { + var length = checked(array.length) | 0 + that = allocate(that, length) + for (var i = 0; i < length; i += 1) { + that[i] = array[i] & 255 + } + return that +} - digit = basicToDigit(input.charCodeAt(index++)); +// Duplicate of fromArray() to keep fromArray() monomorphic. +function fromTypedArray (that, array) { + var length = checked(array.length) | 0 + that = allocate(that, length) + // Truncating the elements is probably not what people expect from typed + // arrays with BYTES_PER_ELEMENT > 1 but it's compatible with the behavior + // of the old Buffer constructor. + for (var i = 0; i < length; i += 1) { + that[i] = array[i] & 255 + } + return that +} - if (digit >= base || digit > floor((maxInt - i) / w)) { - error('overflow'); - } +function fromArrayBuffer (that, array) { + if (Buffer.TYPED_ARRAY_SUPPORT) { + // Return an augmented `Uint8Array` instance, for best performance + array.byteLength + that = Buffer._augment(new Uint8Array(array)) + } else { + // Fallback: Return an object instance of the Buffer class + that = fromTypedArray(that, new Uint8Array(array)) + } + return that +} - i += digit * w; - t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); +function fromArrayLike (that, array) { + var length = checked(array.length) | 0 + that = allocate(that, length) + for (var i = 0; i < length; i += 1) { + that[i] = array[i] & 255 + } + return that +} - if (digit < t) { - break; - } +// Deserialize { type: 'Buffer', data: [1,2,3,...] } into a Buffer object. +// Returns a zero-length buffer for inputs that don't conform to the spec. +function fromJsonObject (that, object) { + var array + var length = 0 - baseMinusT = base - t; - if (w > floor(maxInt / baseMinusT)) { - error('overflow'); - } + if (object.type === 'Buffer' && isArray(object.data)) { + array = object.data + length = checked(array.length) | 0 + } + that = allocate(that, length) - w *= baseMinusT; + for (var i = 0; i < length; i += 1) { + that[i] = array[i] & 255 + } + return that +} - } +if (Buffer.TYPED_ARRAY_SUPPORT) { + Buffer.prototype.__proto__ = Uint8Array.prototype + Buffer.__proto__ = Uint8Array +} else { + // pre-set for values that may exist in the future + Buffer.prototype.length = undefined + Buffer.prototype.parent = undefined +} - out = output.length + 1; - bias = adapt(i - oldi, out, oldi == 0); +function allocate (that, length) { + if (Buffer.TYPED_ARRAY_SUPPORT) { + // Return an augmented `Uint8Array` instance, for best performance + that = Buffer._augment(new Uint8Array(length)) + that.__proto__ = Buffer.prototype + } else { + // Fallback: Return an object instance of the Buffer class + that.length = length + that._isBuffer = true + } - // `i` was supposed to wrap around from `out` to `0`, - // incrementing `n` each time, so we'll fix that now: - if (floor(i / out) > maxInt - n) { - error('overflow'); - } + var fromPool = length !== 0 && length <= Buffer.poolSize >>> 1 + if (fromPool) that.parent = rootParent - n += floor(i / out); - i %= out; + return that +} - // Insert `n` at position `i` of the output - output.splice(i++, 0, n); - - } - - return ucs2encode(output); - } - - /** - * Converts a string of Unicode symbols (e.g. a domain name label) to a - * Punycode string of ASCII-only symbols. - * @memberOf punycode - * @param {String} input The string of Unicode symbols. - * @returns {String} The resulting Punycode string of ASCII-only symbols. - */ - function encode(input) { - var n, - delta, - handledCPCount, - basicLength, - bias, - j, - m, - q, - k, - t, - currentValue, - output = [], - /** `inputLength` will hold the number of code points in `input`. */ - inputLength, - /** Cached calculation results */ - handledCPCountPlusOne, - baseMinusT, - qMinusT; - - // Convert the input in UCS-2 to Unicode - input = ucs2decode(input); - - // Cache the length - inputLength = input.length; - - // Initialize the state - n = initialN; - delta = 0; - bias = initialBias; - - // Handle the basic code points - for (j = 0; j < inputLength; ++j) { - currentValue = input[j]; - if (currentValue < 0x80) { - output.push(stringFromCharCode(currentValue)); - } - } - - handledCPCount = basicLength = output.length; - - // `handledCPCount` is the number of code points that have been handled; - // `basicLength` is the number of basic code points. - - // Finish the basic string - if it is not empty - with a delimiter - if (basicLength) { - output.push(delimiter); - } - - // Main encoding loop: - while (handledCPCount < inputLength) { - - // All non-basic code points < n have been handled already. Find the next - // larger one: - for (m = maxInt, j = 0; j < inputLength; ++j) { - currentValue = input[j]; - if (currentValue >= n && currentValue < m) { - m = currentValue; - } - } - - // Increase `delta` enough to advance the decoder's state to , - // but guard against overflow - handledCPCountPlusOne = handledCPCount + 1; - if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) { - error('overflow'); - } - - delta += (m - n) * handledCPCountPlusOne; - n = m; - - for (j = 0; j < inputLength; ++j) { - currentValue = input[j]; - - if (currentValue < n && ++delta > maxInt) { - error('overflow'); - } - - if (currentValue == n) { - // Represent delta as a generalized variable-length integer - for (q = delta, k = base; /* no condition */; k += base) { - t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); - if (q < t) { - break; - } - qMinusT = q - t; - baseMinusT = base - t; - output.push( - stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0)) - ); - q = floor(qMinusT / baseMinusT); - } - - output.push(stringFromCharCode(digitToBasic(q, 0))); - bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); - delta = 0; - ++handledCPCount; - } - } - - ++delta; - ++n; - - } - return output.join(''); - } - - /** - * Converts a Punycode string representing a domain name or an email address - * to Unicode. Only the Punycoded parts of the input will be converted, i.e. - * it doesn't matter if you call it on a string that has already been - * converted to Unicode. - * @memberOf punycode - * @param {String} input The Punycoded domain name or email address to - * convert to Unicode. - * @returns {String} The Unicode representation of the given Punycode - * string. - */ - function toUnicode(input) { - return mapDomain(input, function(string) { - return regexPunycode.test(string) - ? decode(string.slice(4).toLowerCase()) - : string; - }); - } - - /** - * Converts a Unicode string representing a domain name or an email address to - * Punycode. Only the non-ASCII parts of the domain name will be converted, - * i.e. it doesn't matter if you call it with a domain that's already in - * ASCII. - * @memberOf punycode - * @param {String} input The domain name or email address to convert, as a - * Unicode string. - * @returns {String} The Punycode representation of the given domain name or - * email address. - */ - function toASCII(input) { - return mapDomain(input, function(string) { - return regexNonASCII.test(string) - ? 'xn--' + encode(string) - : string; - }); - } - - /*--------------------------------------------------------------------------*/ - - /** Define the public API */ - punycode = { - /** - * A string representing the current Punycode.js version number. - * @memberOf punycode - * @type String - */ - 'version': '1.4.1', - /** - * An object of methods to convert from JavaScript's internal character - * representation (UCS-2) to Unicode code points, and back. - * @see - * @memberOf punycode - * @type Object - */ - 'ucs2': { - 'decode': ucs2decode, - 'encode': ucs2encode - }, - 'decode': decode, - 'encode': encode, - 'toASCII': toASCII, - 'toUnicode': toUnicode - }; - - /** Expose `punycode` */ - // Some AMD build optimizers, like r.js, check for specific condition patterns - // like the following: - if ( - typeof define == 'function' && - typeof define.amd == 'object' && - define.amd - ) { - define('punycode', function() { - return punycode; - }); - } else if (freeExports && freeModule) { - if (module.exports == freeExports) { - // in Node.js, io.js, or RingoJS v0.8.0+ - freeModule.exports = punycode; - } else { - // in Narwhal or RingoJS v0.7.0- - for (key in punycode) { - punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]); - } - } - } else { - // in Rhino or a web browser - root.punycode = punycode; - } - -}(this)); - -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{}],6:[function(_dereq_,module,exports){ -(function (global){ -/*! - * The buffer module from node.js, for the browser. - * - * @author Feross Aboukhadijeh - * @license MIT - */ -/* eslint-disable no-proto */ - -'use strict' - -var base64 = _dereq_('base64-js') -var ieee754 = _dereq_('ieee754') -var isArray = _dereq_('isarray') - -exports.Buffer = Buffer -exports.SlowBuffer = SlowBuffer -exports.INSPECT_MAX_BYTES = 50 -Buffer.poolSize = 8192 // not used by this implementation - -var rootParent = {} - -/** - * If `Buffer.TYPED_ARRAY_SUPPORT`: - * === true Use Uint8Array implementation (fastest) - * === false Use Object implementation (most compatible, even IE6) - * - * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, - * Opera 11.6+, iOS 4.2+. - * - * Due to various browser bugs, sometimes the Object implementation will be used even - * when the browser supports typed arrays. - * - * Note: - * - * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances, - * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438. - * - * - Safari 5-7 lacks support for changing the `Object.prototype.constructor` property - * on objects. - * - * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function. - * - * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of - * incorrect length in some situations. - - * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they - * get the Object implementation, which is slower but behaves correctly. - */ -Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined - ? global.TYPED_ARRAY_SUPPORT - : typedArraySupport() - -function typedArraySupport () { - function Bar () {} - try { - var arr = new Uint8Array(1) - arr.foo = function () { return 42 } - arr.constructor = Bar - return arr.foo() === 42 && // typed array instances can be augmented - arr.constructor === Bar && // constructor can be set - typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray` - arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray` - } catch (e) { - return false - } -} - -function kMaxLength () { - return Buffer.TYPED_ARRAY_SUPPORT - ? 0x7fffffff - : 0x3fffffff -} - -/** - * Class: Buffer - * ============= - * - * The Buffer constructor returns instances of `Uint8Array` that are augmented - * with function properties for all the node `Buffer` API functions. We use - * `Uint8Array` so that square bracket notation works as expected -- it returns - * a single octet. - * - * By augmenting the instances, we can avoid modifying the `Uint8Array` - * prototype. - */ -function Buffer (arg) { - if (!(this instanceof Buffer)) { - // Avoid going through an ArgumentsAdaptorTrampoline in the common case. - if (arguments.length > 1) return new Buffer(arg, arguments[1]) - return new Buffer(arg) - } - - if (!Buffer.TYPED_ARRAY_SUPPORT) { - this.length = 0 - this.parent = undefined - } - - // Common case. - if (typeof arg === 'number') { - return fromNumber(this, arg) - } - - // Slightly less common case. - if (typeof arg === 'string') { - return fromString(this, arg, arguments.length > 1 ? arguments[1] : 'utf8') - } - - // Unusual. - return fromObject(this, arg) -} - -function fromNumber (that, length) { - that = allocate(that, length < 0 ? 0 : checked(length) | 0) - if (!Buffer.TYPED_ARRAY_SUPPORT) { - for (var i = 0; i < length; i++) { - that[i] = 0 - } - } - return that -} - -function fromString (that, string, encoding) { - if (typeof encoding !== 'string' || encoding === '') encoding = 'utf8' - - // Assumption: byteLength() return value is always < kMaxLength. - var length = byteLength(string, encoding) | 0 - that = allocate(that, length) - - that.write(string, encoding) - return that -} - -function fromObject (that, object) { - if (Buffer.isBuffer(object)) return fromBuffer(that, object) - - if (isArray(object)) return fromArray(that, object) - - if (object == null) { - throw new TypeError('must start with number, buffer, array or string') - } - - if (typeof ArrayBuffer !== 'undefined') { - if (object.buffer instanceof ArrayBuffer) { - return fromTypedArray(that, object) - } - if (object instanceof ArrayBuffer) { - return fromArrayBuffer(that, object) - } - } - - if (object.length) return fromArrayLike(that, object) - - return fromJsonObject(that, object) -} - -function fromBuffer (that, buffer) { - var length = checked(buffer.length) | 0 - that = allocate(that, length) - buffer.copy(that, 0, 0, length) - return that -} - -function fromArray (that, array) { - var length = checked(array.length) | 0 - that = allocate(that, length) - for (var i = 0; i < length; i += 1) { - that[i] = array[i] & 255 - } - return that -} - -// Duplicate of fromArray() to keep fromArray() monomorphic. -function fromTypedArray (that, array) { - var length = checked(array.length) | 0 - that = allocate(that, length) - // Truncating the elements is probably not what people expect from typed - // arrays with BYTES_PER_ELEMENT > 1 but it's compatible with the behavior - // of the old Buffer constructor. - for (var i = 0; i < length; i += 1) { - that[i] = array[i] & 255 - } - return that -} - -function fromArrayBuffer (that, array) { - if (Buffer.TYPED_ARRAY_SUPPORT) { - // Return an augmented `Uint8Array` instance, for best performance - array.byteLength - that = Buffer._augment(new Uint8Array(array)) - } else { - // Fallback: Return an object instance of the Buffer class - that = fromTypedArray(that, new Uint8Array(array)) - } - return that -} - -function fromArrayLike (that, array) { - var length = checked(array.length) | 0 - that = allocate(that, length) - for (var i = 0; i < length; i += 1) { - that[i] = array[i] & 255 - } - return that -} - -// Deserialize { type: 'Buffer', data: [1,2,3,...] } into a Buffer object. -// Returns a zero-length buffer for inputs that don't conform to the spec. -function fromJsonObject (that, object) { - var array - var length = 0 - - if (object.type === 'Buffer' && isArray(object.data)) { - array = object.data - length = checked(array.length) | 0 - } - that = allocate(that, length) - - for (var i = 0; i < length; i += 1) { - that[i] = array[i] & 255 - } - return that -} - -if (Buffer.TYPED_ARRAY_SUPPORT) { - Buffer.prototype.__proto__ = Uint8Array.prototype - Buffer.__proto__ = Uint8Array -} else { - // pre-set for values that may exist in the future - Buffer.prototype.length = undefined - Buffer.prototype.parent = undefined -} - -function allocate (that, length) { - if (Buffer.TYPED_ARRAY_SUPPORT) { - // Return an augmented `Uint8Array` instance, for best performance - that = Buffer._augment(new Uint8Array(length)) - that.__proto__ = Buffer.prototype - } else { - // Fallback: Return an object instance of the Buffer class - that.length = length - that._isBuffer = true - } - - var fromPool = length !== 0 && length <= Buffer.poolSize >>> 1 - if (fromPool) that.parent = rootParent - - return that -} - -function checked (length) { - // Note: cannot use `length < kMaxLength` here because that fails when - // length is NaN (which is otherwise coerced to zero.) - if (length >= kMaxLength()) { - throw new RangeError('Attempt to allocate Buffer larger than maximum ' + - 'size: 0x' + kMaxLength().toString(16) + ' bytes') - } - return length | 0 -} +function checked (length) { + // Note: cannot use `length < kMaxLength` here because that fails when + // length is NaN (which is otherwise coerced to zero.) + if (length >= kMaxLength()) { + throw new RangeError('Attempt to allocate Buffer larger than maximum ' + + 'size: 0x' + kMaxLength().toString(16) + ' bytes') + } + return length | 0 +} function SlowBuffer (subject, encoding) { if (!(this instanceof SlowBuffer)) return new SlowBuffer(subject, encoding) @@ -3347,14 +2809,14 @@ function blitBuffer (src, dst, offset, length) { }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"base64-js":2,"ieee754":14,"isarray":7}],7:[function(_dereq_,module,exports){ +},{"base64-js":4,"ieee754":41,"isarray":6}],6:[function(_dereq_,module,exports){ var toString = {}.toString; module.exports = Array.isArray || function (arr) { return toString.call(arr) == '[object Array]'; }; -},{}],8:[function(_dereq_,module,exports){ +},{}],7:[function(_dereq_,module,exports){ module.exports = { "100": "Continue", "101": "Switching Protocols", @@ -3415,8 +2877,7 @@ module.exports = { "511": "Network Authentication Required" } -},{}],9:[function(_dereq_,module,exports){ -(function (Buffer){ +},{}],8:[function(_dereq_,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -3438,1073 +2899,1026 @@ module.exports = { // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -// NOTE: These type checking functions intentionally don't use `instanceof` -// because it is fragile and can be easily faked with `Object.create()`. - -function isArray(arg) { - if (Array.isArray) { - return Array.isArray(arg); - } - return objectToString(arg) === '[object Array]'; -} -exports.isArray = isArray; - -function isBoolean(arg) { - return typeof arg === 'boolean'; -} -exports.isBoolean = isBoolean; - -function isNull(arg) { - return arg === null; -} -exports.isNull = isNull; - -function isNullOrUndefined(arg) { - return arg == null; +function EventEmitter() { + this._events = this._events || {}; + this._maxListeners = this._maxListeners || undefined; } -exports.isNullOrUndefined = isNullOrUndefined; +module.exports = EventEmitter; -function isNumber(arg) { - return typeof arg === 'number'; -} -exports.isNumber = isNumber; +// Backwards-compat with node 0.10.x +EventEmitter.EventEmitter = EventEmitter; -function isString(arg) { - return typeof arg === 'string'; -} -exports.isString = isString; +EventEmitter.prototype._events = undefined; +EventEmitter.prototype._maxListeners = undefined; -function isSymbol(arg) { - return typeof arg === 'symbol'; -} -exports.isSymbol = isSymbol; +// By default EventEmitters will print a warning if more than 10 listeners are +// added to it. This is a useful default which helps finding memory leaks. +EventEmitter.defaultMaxListeners = 10; -function isUndefined(arg) { - return arg === void 0; -} -exports.isUndefined = isUndefined; +// Obviously not all Emitters should be limited to 10. This function allows +// that to be increased. Set to zero for unlimited. +EventEmitter.prototype.setMaxListeners = function(n) { + if (!isNumber(n) || n < 0 || isNaN(n)) + throw TypeError('n must be a positive number'); + this._maxListeners = n; + return this; +}; -function isRegExp(re) { - return objectToString(re) === '[object RegExp]'; -} -exports.isRegExp = isRegExp; +EventEmitter.prototype.emit = function(type) { + var er, handler, len, args, i, listeners; -function isObject(arg) { - return typeof arg === 'object' && arg !== null; -} -exports.isObject = isObject; + if (!this._events) + this._events = {}; -function isDate(d) { - return objectToString(d) === '[object Date]'; -} -exports.isDate = isDate; + // If there is no 'error' event listener then throw. + if (type === 'error') { + if (!this._events.error || + (isObject(this._events.error) && !this._events.error.length)) { + er = arguments[1]; + if (er instanceof Error) { + throw er; // Unhandled 'error' event + } + throw TypeError('Uncaught, unspecified "error" event.'); + } + } -function isError(e) { - return (objectToString(e) === '[object Error]' || e instanceof Error); -} -exports.isError = isError; + handler = this._events[type]; -function isFunction(arg) { - return typeof arg === 'function'; -} -exports.isFunction = isFunction; + if (isUndefined(handler)) + return false; -function isPrimitive(arg) { - return arg === null || - typeof arg === 'boolean' || - typeof arg === 'number' || - typeof arg === 'string' || - typeof arg === 'symbol' || // ES6 symbol - typeof arg === 'undefined'; -} -exports.isPrimitive = isPrimitive; + if (isFunction(handler)) { + switch (arguments.length) { + // fast cases + case 1: + handler.call(this); + break; + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + // slower + default: + len = arguments.length; + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; + handler.apply(this, args); + } + } else if (isObject(handler)) { + len = arguments.length; + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; -exports.isBuffer = Buffer.isBuffer; + listeners = handler.slice(); + len = listeners.length; + for (i = 0; i < len; i++) + listeners[i].apply(this, args); + } -function objectToString(o) { - return Object.prototype.toString.call(o); -} + return true; +}; -}).call(this,{"isBuffer":_dereq_("../../is-buffer/index.js")}) +EventEmitter.prototype.addListener = function(type, listener) { + var m; -},{"../../is-buffer/index.js":17}],10:[function(_dereq_,module,exports){ -(function (process,global){ -/*! - * @overview es6-promise - a tiny implementation of Promises/A+. - * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) - * @license Licensed under MIT license - * See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE - * @version 2.3.0 - */ + if (!isFunction(listener)) + throw TypeError('listener must be a function'); -(function() { - "use strict"; - function lib$es6$promise$utils$$objectOrFunction(x) { - return typeof x === 'function' || (typeof x === 'object' && x !== null); - } + if (!this._events) + this._events = {}; - function lib$es6$promise$utils$$isFunction(x) { - return typeof x === 'function'; - } + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (this._events.newListener) + this.emit('newListener', type, + isFunction(listener.listener) ? + listener.listener : listener); - function lib$es6$promise$utils$$isMaybeThenable(x) { - return typeof x === 'object' && x !== null; - } + if (!this._events[type]) + // Optimize the case of one listener. Don't need the extra array object. + this._events[type] = listener; + else if (isObject(this._events[type])) + // If we've already got an array, just append. + this._events[type].push(listener); + else + // Adding the second element, need to change to array. + this._events[type] = [this._events[type], listener]; - var lib$es6$promise$utils$$_isArray; - if (!Array.isArray) { - lib$es6$promise$utils$$_isArray = function (x) { - return Object.prototype.toString.call(x) === '[object Array]'; - }; + // Check for listener leak + if (isObject(this._events[type]) && !this._events[type].warned) { + var m; + if (!isUndefined(this._maxListeners)) { + m = this._maxListeners; } else { - lib$es6$promise$utils$$_isArray = Array.isArray; + m = EventEmitter.defaultMaxListeners; } - var lib$es6$promise$utils$$isArray = lib$es6$promise$utils$$_isArray; - var lib$es6$promise$asap$$len = 0; - var lib$es6$promise$asap$$toString = {}.toString; - var lib$es6$promise$asap$$vertxNext; - var lib$es6$promise$asap$$customSchedulerFn; - - var lib$es6$promise$asap$$asap = function asap(callback, arg) { - lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len] = callback; - lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len + 1] = arg; - lib$es6$promise$asap$$len += 2; - if (lib$es6$promise$asap$$len === 2) { - // If len is 2, that means that we need to schedule an async flush. - // If additional callbacks are queued before the queue is flushed, they - // will be processed by this flush that we are scheduling. - if (lib$es6$promise$asap$$customSchedulerFn) { - lib$es6$promise$asap$$customSchedulerFn(lib$es6$promise$asap$$flush); - } else { - lib$es6$promise$asap$$scheduleFlush(); - } + if (m && m > 0 && this._events[type].length > m) { + this._events[type].warned = true; + console.error('(node) warning: possible EventEmitter memory ' + + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + this._events[type].length); + if (typeof console.trace === 'function') { + // not supported in IE 10 + console.trace(); } } + } - function lib$es6$promise$asap$$setScheduler(scheduleFn) { - lib$es6$promise$asap$$customSchedulerFn = scheduleFn; - } + return this; +}; - function lib$es6$promise$asap$$setAsap(asapFn) { - lib$es6$promise$asap$$asap = asapFn; - } +EventEmitter.prototype.on = EventEmitter.prototype.addListener; - var lib$es6$promise$asap$$browserWindow = (typeof window !== 'undefined') ? window : undefined; - var lib$es6$promise$asap$$browserGlobal = lib$es6$promise$asap$$browserWindow || {}; - var lib$es6$promise$asap$$BrowserMutationObserver = lib$es6$promise$asap$$browserGlobal.MutationObserver || lib$es6$promise$asap$$browserGlobal.WebKitMutationObserver; - var lib$es6$promise$asap$$isNode = typeof process !== 'undefined' && {}.toString.call(process) === '[object process]'; +EventEmitter.prototype.once = function(type, listener) { + if (!isFunction(listener)) + throw TypeError('listener must be a function'); - // test for web worker but not in IE10 - var lib$es6$promise$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' && - typeof importScripts !== 'undefined' && - typeof MessageChannel !== 'undefined'; + var fired = false; - // node - function lib$es6$promise$asap$$useNextTick() { - var nextTick = process.nextTick; - // node version 0.10.x displays a deprecation warning when nextTick is used recursively - // setImmediate should be used instead instead - var version = process.versions.node.match(/^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$/); - if (Array.isArray(version) && version[1] === '0' && version[2] === '10') { - nextTick = setImmediate; - } - return function() { - nextTick(lib$es6$promise$asap$$flush); - }; - } + function g() { + this.removeListener(type, g); - // vertx - function lib$es6$promise$asap$$useVertxTimer() { - return function() { - lib$es6$promise$asap$$vertxNext(lib$es6$promise$asap$$flush); - }; + if (!fired) { + fired = true; + listener.apply(this, arguments); } + } - function lib$es6$promise$asap$$useMutationObserver() { - var iterations = 0; - var observer = new lib$es6$promise$asap$$BrowserMutationObserver(lib$es6$promise$asap$$flush); - var node = document.createTextNode(''); - observer.observe(node, { characterData: true }); - - return function() { - node.data = (iterations = ++iterations % 2); - }; - } + g.listener = listener; + this.on(type, g); - // web worker - function lib$es6$promise$asap$$useMessageChannel() { - var channel = new MessageChannel(); - channel.port1.onmessage = lib$es6$promise$asap$$flush; - return function () { - channel.port2.postMessage(0); - }; - } + return this; +}; - function lib$es6$promise$asap$$useSetTimeout() { - return function() { - setTimeout(lib$es6$promise$asap$$flush, 1); - }; - } +// emits a 'removeListener' event iff the listener was removed +EventEmitter.prototype.removeListener = function(type, listener) { + var list, position, length, i; - var lib$es6$promise$asap$$queue = new Array(1000); - function lib$es6$promise$asap$$flush() { - for (var i = 0; i < lib$es6$promise$asap$$len; i+=2) { - var callback = lib$es6$promise$asap$$queue[i]; - var arg = lib$es6$promise$asap$$queue[i+1]; + if (!isFunction(listener)) + throw TypeError('listener must be a function'); - callback(arg); + if (!this._events || !this._events[type]) + return this; - lib$es6$promise$asap$$queue[i] = undefined; - lib$es6$promise$asap$$queue[i+1] = undefined; - } + list = this._events[type]; + length = list.length; + position = -1; - lib$es6$promise$asap$$len = 0; - } + if (list === listener || + (isFunction(list.listener) && list.listener === listener)) { + delete this._events[type]; + if (this._events.removeListener) + this.emit('removeListener', type, listener); - function lib$es6$promise$asap$$attemptVertex() { - try { - var r = _dereq_; - var vertx = r('vertx'); - lib$es6$promise$asap$$vertxNext = vertx.runOnLoop || vertx.runOnContext; - return lib$es6$promise$asap$$useVertxTimer(); - } catch(e) { - return lib$es6$promise$asap$$useSetTimeout(); + } else if (isObject(list)) { + for (i = length; i-- > 0;) { + if (list[i] === listener || + (list[i].listener && list[i].listener === listener)) { + position = i; + break; } } - var lib$es6$promise$asap$$scheduleFlush; - // Decide what async method to use to triggering processing of queued callbacks: - if (lib$es6$promise$asap$$isNode) { - lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useNextTick(); - } else if (lib$es6$promise$asap$$BrowserMutationObserver) { - lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMutationObserver(); - } else if (lib$es6$promise$asap$$isWorker) { - lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMessageChannel(); - } else if (lib$es6$promise$asap$$browserWindow === undefined && typeof _dereq_ === 'function') { - lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$attemptVertex(); + if (position < 0) + return this; + + if (list.length === 1) { + list.length = 0; + delete this._events[type]; } else { - lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useSetTimeout(); + list.splice(position, 1); } - function lib$es6$promise$$internal$$noop() {} + if (this._events.removeListener) + this.emit('removeListener', type, listener); + } - var lib$es6$promise$$internal$$PENDING = void 0; - var lib$es6$promise$$internal$$FULFILLED = 1; - var lib$es6$promise$$internal$$REJECTED = 2; + return this; +}; - var lib$es6$promise$$internal$$GET_THEN_ERROR = new lib$es6$promise$$internal$$ErrorObject(); +EventEmitter.prototype.removeAllListeners = function(type) { + var key, listeners; - function lib$es6$promise$$internal$$selfFullfillment() { - return new TypeError("You cannot resolve a promise with itself"); - } + if (!this._events) + return this; - function lib$es6$promise$$internal$$cannotReturnOwn() { - return new TypeError('A promises callback cannot return that same promise.'); - } + // not listening for removeListener, no need to emit + if (!this._events.removeListener) { + if (arguments.length === 0) + this._events = {}; + else if (this._events[type]) + delete this._events[type]; + return this; + } - function lib$es6$promise$$internal$$getThen(promise) { - try { - return promise.then; - } catch(error) { - lib$es6$promise$$internal$$GET_THEN_ERROR.error = error; - return lib$es6$promise$$internal$$GET_THEN_ERROR; - } + // emit removeListener for all listeners on all events + if (arguments.length === 0) { + for (key in this._events) { + if (key === 'removeListener') continue; + this.removeAllListeners(key); } + this.removeAllListeners('removeListener'); + this._events = {}; + return this; + } - function lib$es6$promise$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) { - try { - then.call(value, fulfillmentHandler, rejectionHandler); - } catch(e) { - return e; - } - } + listeners = this._events[type]; - function lib$es6$promise$$internal$$handleForeignThenable(promise, thenable, then) { - lib$es6$promise$asap$$asap(function(promise) { - var sealed = false; - var error = lib$es6$promise$$internal$$tryThen(then, thenable, function(value) { - if (sealed) { return; } - sealed = true; - if (thenable !== value) { - lib$es6$promise$$internal$$resolve(promise, value); - } else { - lib$es6$promise$$internal$$fulfill(promise, value); - } - }, function(reason) { - if (sealed) { return; } - sealed = true; + if (isFunction(listeners)) { + this.removeListener(type, listeners); + } else { + // LIFO order + while (listeners.length) + this.removeListener(type, listeners[listeners.length - 1]); + } + delete this._events[type]; - lib$es6$promise$$internal$$reject(promise, reason); - }, 'Settle: ' + (promise._label || ' unknown promise')); + return this; +}; - if (!sealed && error) { - sealed = true; - lib$es6$promise$$internal$$reject(promise, error); - } - }, promise); - } +EventEmitter.prototype.listeners = function(type) { + var ret; + if (!this._events || !this._events[type]) + ret = []; + else if (isFunction(this._events[type])) + ret = [this._events[type]]; + else + ret = this._events[type].slice(); + return ret; +}; - function lib$es6$promise$$internal$$handleOwnThenable(promise, thenable) { - if (thenable._state === lib$es6$promise$$internal$$FULFILLED) { - lib$es6$promise$$internal$$fulfill(promise, thenable._result); - } else if (thenable._state === lib$es6$promise$$internal$$REJECTED) { - lib$es6$promise$$internal$$reject(promise, thenable._result); - } else { - lib$es6$promise$$internal$$subscribe(thenable, undefined, function(value) { - lib$es6$promise$$internal$$resolve(promise, value); - }, function(reason) { - lib$es6$promise$$internal$$reject(promise, reason); - }); - } - } +EventEmitter.listenerCount = function(emitter, type) { + var ret; + if (!emitter._events || !emitter._events[type]) + ret = 0; + else if (isFunction(emitter._events[type])) + ret = 1; + else + ret = emitter._events[type].length; + return ret; +}; - function lib$es6$promise$$internal$$handleMaybeThenable(promise, maybeThenable) { - if (maybeThenable.constructor === promise.constructor) { - lib$es6$promise$$internal$$handleOwnThenable(promise, maybeThenable); - } else { - var then = lib$es6$promise$$internal$$getThen(maybeThenable); +function isFunction(arg) { + return typeof arg === 'function'; +} - if (then === lib$es6$promise$$internal$$GET_THEN_ERROR) { - lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$GET_THEN_ERROR.error); - } else if (then === undefined) { - lib$es6$promise$$internal$$fulfill(promise, maybeThenable); - } else if (lib$es6$promise$utils$$isFunction(then)) { - lib$es6$promise$$internal$$handleForeignThenable(promise, maybeThenable, then); - } else { - lib$es6$promise$$internal$$fulfill(promise, maybeThenable); - } - } - } +function isNumber(arg) { + return typeof arg === 'number'; +} - function lib$es6$promise$$internal$$resolve(promise, value) { - if (promise === value) { - lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$selfFullfillment()); - } else if (lib$es6$promise$utils$$objectOrFunction(value)) { - lib$es6$promise$$internal$$handleMaybeThenable(promise, value); - } else { - lib$es6$promise$$internal$$fulfill(promise, value); - } - } +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} - function lib$es6$promise$$internal$$publishRejection(promise) { - if (promise._onerror) { - promise._onerror(promise._result); - } +function isUndefined(arg) { + return arg === void 0; +} - lib$es6$promise$$internal$$publish(promise); - } +},{}],9:[function(_dereq_,module,exports){ +var http = _dereq_('http'); - function lib$es6$promise$$internal$$fulfill(promise, value) { - if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; } +var https = module.exports; - promise._result = value; - promise._state = lib$es6$promise$$internal$$FULFILLED; +for (var key in http) { + if (http.hasOwnProperty(key)) https[key] = http[key]; +}; - if (promise._subscribers.length !== 0) { - lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, promise); - } - } +https.request = function (params, cb) { + if (!params) params = {}; + params.scheme = 'https'; + params.protocol = 'https:'; + return http.request.call(this, params, cb); +} - function lib$es6$promise$$internal$$reject(promise, reason) { - if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; } - promise._state = lib$es6$promise$$internal$$REJECTED; - promise._result = reason; +},{"http":28}],10:[function(_dereq_,module,exports){ +(function (global){ +/*! https://mths.be/punycode v1.4.1 by @mathias */ +;(function(root) { - lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publishRejection, promise); - } + /** Detect free variables */ + var freeExports = typeof exports == 'object' && exports && + !exports.nodeType && exports; + var freeModule = typeof module == 'object' && module && + !module.nodeType && module; + var freeGlobal = typeof global == 'object' && global; + if ( + freeGlobal.global === freeGlobal || + freeGlobal.window === freeGlobal || + freeGlobal.self === freeGlobal + ) { + root = freeGlobal; + } - function lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection) { - var subscribers = parent._subscribers; - var length = subscribers.length; + /** + * The `punycode` object. + * @name punycode + * @type Object + */ + var punycode, - parent._onerror = null; + /** Highest positive signed 32-bit float value */ + maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1 - subscribers[length] = child; - subscribers[length + lib$es6$promise$$internal$$FULFILLED] = onFulfillment; - subscribers[length + lib$es6$promise$$internal$$REJECTED] = onRejection; + /** Bootstring parameters */ + base = 36, + tMin = 1, + tMax = 26, + skew = 38, + damp = 700, + initialBias = 72, + initialN = 128, // 0x80 + delimiter = '-', // '\x2D' - if (length === 0 && parent._state) { - lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, parent); - } - } + /** Regular expressions */ + regexPunycode = /^xn--/, + regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars + regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators - function lib$es6$promise$$internal$$publish(promise) { - var subscribers = promise._subscribers; - var settled = promise._state; + /** Error messages */ + errors = { + 'overflow': 'Overflow: input needs wider integers to process', + 'not-basic': 'Illegal input >= 0x80 (not a basic code point)', + 'invalid-input': 'Invalid input' + }, - if (subscribers.length === 0) { return; } + /** Convenience shortcuts */ + baseMinusTMin = base - tMin, + floor = Math.floor, + stringFromCharCode = String.fromCharCode, - var child, callback, detail = promise._result; + /** Temporary variable */ + key; - for (var i = 0; i < subscribers.length; i += 3) { - child = subscribers[i]; - callback = subscribers[i + settled]; + /*--------------------------------------------------------------------------*/ - if (child) { - lib$es6$promise$$internal$$invokeCallback(settled, child, callback, detail); - } else { - callback(detail); - } - } + /** + * A generic error utility function. + * @private + * @param {String} type The error type. + * @returns {Error} Throws a `RangeError` with the applicable error message. + */ + function error(type) { + throw new RangeError(errors[type]); + } - promise._subscribers.length = 0; - } + /** + * A generic `Array#map` utility function. + * @private + * @param {Array} array The array to iterate over. + * @param {Function} callback The function that gets called for every array + * item. + * @returns {Array} A new array of values returned by the callback function. + */ + function map(array, fn) { + var length = array.length; + var result = []; + while (length--) { + result[length] = fn(array[length]); + } + return result; + } - function lib$es6$promise$$internal$$ErrorObject() { - this.error = null; - } + /** + * A simple `Array#map`-like wrapper to work with domain name strings or email + * addresses. + * @private + * @param {String} domain The domain name or email address. + * @param {Function} callback The function that gets called for every + * character. + * @returns {Array} A new string of characters returned by the callback + * function. + */ + function mapDomain(string, fn) { + var parts = string.split('@'); + var result = ''; + if (parts.length > 1) { + // In email addresses, only the domain name should be punycoded. Leave + // the local part (i.e. everything up to `@`) intact. + result = parts[0] + '@'; + string = parts[1]; + } + // Avoid `split(regex)` for IE8 compatibility. See #17. + string = string.replace(regexSeparators, '\x2E'); + var labels = string.split('.'); + var encoded = map(labels, fn).join('.'); + return result + encoded; + } - var lib$es6$promise$$internal$$TRY_CATCH_ERROR = new lib$es6$promise$$internal$$ErrorObject(); + /** + * Creates an array containing the numeric code points of each Unicode + * character in the string. While JavaScript uses UCS-2 internally, + * this function will convert a pair of surrogate halves (each of which + * UCS-2 exposes as separate characters) into a single code point, + * matching UTF-16. + * @see `punycode.ucs2.encode` + * @see + * @memberOf punycode.ucs2 + * @name decode + * @param {String} string The Unicode input string (UCS-2). + * @returns {Array} The new array of code points. + */ + function ucs2decode(string) { + var output = [], + counter = 0, + length = string.length, + value, + extra; + while (counter < length) { + value = string.charCodeAt(counter++); + if (value >= 0xD800 && value <= 0xDBFF && counter < length) { + // high surrogate, and there is a next character + extra = string.charCodeAt(counter++); + if ((extra & 0xFC00) == 0xDC00) { // low surrogate + output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); + } else { + // unmatched surrogate; only append this code unit, in case the next + // code unit is the high surrogate of a surrogate pair + output.push(value); + counter--; + } + } else { + output.push(value); + } + } + return output; + } - function lib$es6$promise$$internal$$tryCatch(callback, detail) { - try { - return callback(detail); - } catch(e) { - lib$es6$promise$$internal$$TRY_CATCH_ERROR.error = e; - return lib$es6$promise$$internal$$TRY_CATCH_ERROR; - } - } + /** + * Creates a string based on an array of numeric code points. + * @see `punycode.ucs2.decode` + * @memberOf punycode.ucs2 + * @name encode + * @param {Array} codePoints The array of numeric code points. + * @returns {String} The new Unicode string (UCS-2). + */ + function ucs2encode(array) { + return map(array, function(value) { + var output = ''; + if (value > 0xFFFF) { + value -= 0x10000; + output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); + value = 0xDC00 | value & 0x3FF; + } + output += stringFromCharCode(value); + return output; + }).join(''); + } - function lib$es6$promise$$internal$$invokeCallback(settled, promise, callback, detail) { - var hasCallback = lib$es6$promise$utils$$isFunction(callback), - value, error, succeeded, failed; + /** + * Converts a basic code point into a digit/integer. + * @see `digitToBasic()` + * @private + * @param {Number} codePoint The basic numeric code point value. + * @returns {Number} The numeric value of a basic code point (for use in + * representing integers) in the range `0` to `base - 1`, or `base` if + * the code point does not represent a value. + */ + function basicToDigit(codePoint) { + if (codePoint - 48 < 10) { + return codePoint - 22; + } + if (codePoint - 65 < 26) { + return codePoint - 65; + } + if (codePoint - 97 < 26) { + return codePoint - 97; + } + return base; + } - if (hasCallback) { - value = lib$es6$promise$$internal$$tryCatch(callback, detail); + /** + * Converts a digit/integer into a basic code point. + * @see `basicToDigit()` + * @private + * @param {Number} digit The numeric value of a basic code point. + * @returns {Number} The basic code point whose value (when used for + * representing integers) is `digit`, which needs to be in the range + * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is + * used; else, the lowercase form is used. The behavior is undefined + * if `flag` is non-zero and `digit` has no uppercase form. + */ + function digitToBasic(digit, flag) { + // 0..25 map to ASCII a..z or A..Z + // 26..35 map to ASCII 0..9 + return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5); + } - if (value === lib$es6$promise$$internal$$TRY_CATCH_ERROR) { - failed = true; - error = value.error; - value = null; - } else { - succeeded = true; - } + /** + * Bias adaptation function as per section 3.4 of RFC 3492. + * https://tools.ietf.org/html/rfc3492#section-3.4 + * @private + */ + function adapt(delta, numPoints, firstTime) { + var k = 0; + delta = firstTime ? floor(delta / damp) : delta >> 1; + delta += floor(delta / numPoints); + for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) { + delta = floor(delta / baseMinusTMin); + } + return floor(k + (baseMinusTMin + 1) * delta / (delta + skew)); + } - if (promise === value) { - lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$cannotReturnOwn()); - return; - } + /** + * Converts a Punycode string of ASCII-only symbols to a string of Unicode + * symbols. + * @memberOf punycode + * @param {String} input The Punycode string of ASCII-only symbols. + * @returns {String} The resulting string of Unicode symbols. + */ + function decode(input) { + // Don't use UCS-2 + var output = [], + inputLength = input.length, + out, + i = 0, + n = initialN, + bias = initialBias, + basic, + j, + index, + oldi, + w, + k, + digit, + t, + /** Cached calculation results */ + baseMinusT; - } else { - value = detail; - succeeded = true; - } + // Handle the basic code points: let `basic` be the number of input code + // points before the last delimiter, or `0` if there is none, then copy + // the first basic code points to the output. - if (promise._state !== lib$es6$promise$$internal$$PENDING) { - // noop - } else if (hasCallback && succeeded) { - lib$es6$promise$$internal$$resolve(promise, value); - } else if (failed) { - lib$es6$promise$$internal$$reject(promise, error); - } else if (settled === lib$es6$promise$$internal$$FULFILLED) { - lib$es6$promise$$internal$$fulfill(promise, value); - } else if (settled === lib$es6$promise$$internal$$REJECTED) { - lib$es6$promise$$internal$$reject(promise, value); - } - } + basic = input.lastIndexOf(delimiter); + if (basic < 0) { + basic = 0; + } - function lib$es6$promise$$internal$$initializePromise(promise, resolver) { - try { - resolver(function resolvePromise(value){ - lib$es6$promise$$internal$$resolve(promise, value); - }, function rejectPromise(reason) { - lib$es6$promise$$internal$$reject(promise, reason); - }); - } catch(e) { - lib$es6$promise$$internal$$reject(promise, e); - } - } + for (j = 0; j < basic; ++j) { + // if it's not a basic code point + if (input.charCodeAt(j) >= 0x80) { + error('not-basic'); + } + output.push(input.charCodeAt(j)); + } - function lib$es6$promise$enumerator$$Enumerator(Constructor, input) { - var enumerator = this; + // Main decoding loop: start just after the last delimiter if any basic code + // points were copied; start at the beginning otherwise. - enumerator._instanceConstructor = Constructor; - enumerator.promise = new Constructor(lib$es6$promise$$internal$$noop); + for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) { - if (enumerator._validateInput(input)) { - enumerator._input = input; - enumerator.length = input.length; - enumerator._remaining = input.length; + // `index` is the index of the next character to be consumed. + // Decode a generalized variable-length integer into `delta`, + // which gets added to `i`. The overflow checking is easier + // if we increase `i` as we go, then subtract off its starting + // value at the end to obtain `delta`. + for (oldi = i, w = 1, k = base; /* no condition */; k += base) { - enumerator._init(); + if (index >= inputLength) { + error('invalid-input'); + } - if (enumerator.length === 0) { - lib$es6$promise$$internal$$fulfill(enumerator.promise, enumerator._result); - } else { - enumerator.length = enumerator.length || 0; - enumerator._enumerate(); - if (enumerator._remaining === 0) { - lib$es6$promise$$internal$$fulfill(enumerator.promise, enumerator._result); - } - } - } else { - lib$es6$promise$$internal$$reject(enumerator.promise, enumerator._validationError()); - } - } + digit = basicToDigit(input.charCodeAt(index++)); - lib$es6$promise$enumerator$$Enumerator.prototype._validateInput = function(input) { - return lib$es6$promise$utils$$isArray(input); - }; + if (digit >= base || digit > floor((maxInt - i) / w)) { + error('overflow'); + } - lib$es6$promise$enumerator$$Enumerator.prototype._validationError = function() { - return new Error('Array Methods must be provided an Array'); - }; + i += digit * w; + t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); - lib$es6$promise$enumerator$$Enumerator.prototype._init = function() { - this._result = new Array(this.length); - }; + if (digit < t) { + break; + } - var lib$es6$promise$enumerator$$default = lib$es6$promise$enumerator$$Enumerator; + baseMinusT = base - t; + if (w > floor(maxInt / baseMinusT)) { + error('overflow'); + } - lib$es6$promise$enumerator$$Enumerator.prototype._enumerate = function() { - var enumerator = this; + w *= baseMinusT; - var length = enumerator.length; - var promise = enumerator.promise; - var input = enumerator._input; + } - for (var i = 0; promise._state === lib$es6$promise$$internal$$PENDING && i < length; i++) { - enumerator._eachEntry(input[i], i); - } - }; + out = output.length + 1; + bias = adapt(i - oldi, out, oldi == 0); - lib$es6$promise$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) { - var enumerator = this; - var c = enumerator._instanceConstructor; + // `i` was supposed to wrap around from `out` to `0`, + // incrementing `n` each time, so we'll fix that now: + if (floor(i / out) > maxInt - n) { + error('overflow'); + } - if (lib$es6$promise$utils$$isMaybeThenable(entry)) { - if (entry.constructor === c && entry._state !== lib$es6$promise$$internal$$PENDING) { - entry._onerror = null; - enumerator._settledAt(entry._state, i, entry._result); - } else { - enumerator._willSettleAt(c.resolve(entry), i); - } - } else { - enumerator._remaining--; - enumerator._result[i] = entry; - } - }; + n += floor(i / out); + i %= out; - lib$es6$promise$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) { - var enumerator = this; - var promise = enumerator.promise; + // Insert `n` at position `i` of the output + output.splice(i++, 0, n); - if (promise._state === lib$es6$promise$$internal$$PENDING) { - enumerator._remaining--; + } - if (state === lib$es6$promise$$internal$$REJECTED) { - lib$es6$promise$$internal$$reject(promise, value); - } else { - enumerator._result[i] = value; - } - } + return ucs2encode(output); + } - if (enumerator._remaining === 0) { - lib$es6$promise$$internal$$fulfill(promise, enumerator._result); - } - }; + /** + * Converts a string of Unicode symbols (e.g. a domain name label) to a + * Punycode string of ASCII-only symbols. + * @memberOf punycode + * @param {String} input The string of Unicode symbols. + * @returns {String} The resulting Punycode string of ASCII-only symbols. + */ + function encode(input) { + var n, + delta, + handledCPCount, + basicLength, + bias, + j, + m, + q, + k, + t, + currentValue, + output = [], + /** `inputLength` will hold the number of code points in `input`. */ + inputLength, + /** Cached calculation results */ + handledCPCountPlusOne, + baseMinusT, + qMinusT; - lib$es6$promise$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) { - var enumerator = this; + // Convert the input in UCS-2 to Unicode + input = ucs2decode(input); - lib$es6$promise$$internal$$subscribe(promise, undefined, function(value) { - enumerator._settledAt(lib$es6$promise$$internal$$FULFILLED, i, value); - }, function(reason) { - enumerator._settledAt(lib$es6$promise$$internal$$REJECTED, i, reason); - }); - }; - function lib$es6$promise$promise$all$$all(entries) { - return new lib$es6$promise$enumerator$$default(this, entries).promise; - } - var lib$es6$promise$promise$all$$default = lib$es6$promise$promise$all$$all; - function lib$es6$promise$promise$race$$race(entries) { - /*jshint validthis:true */ - var Constructor = this; + // Cache the length + inputLength = input.length; - var promise = new Constructor(lib$es6$promise$$internal$$noop); + // Initialize the state + n = initialN; + delta = 0; + bias = initialBias; - if (!lib$es6$promise$utils$$isArray(entries)) { - lib$es6$promise$$internal$$reject(promise, new TypeError('You must pass an array to race.')); - return promise; - } + // Handle the basic code points + for (j = 0; j < inputLength; ++j) { + currentValue = input[j]; + if (currentValue < 0x80) { + output.push(stringFromCharCode(currentValue)); + } + } - var length = entries.length; + handledCPCount = basicLength = output.length; - function onFulfillment(value) { - lib$es6$promise$$internal$$resolve(promise, value); - } + // `handledCPCount` is the number of code points that have been handled; + // `basicLength` is the number of basic code points. - function onRejection(reason) { - lib$es6$promise$$internal$$reject(promise, reason); - } + // Finish the basic string - if it is not empty - with a delimiter + if (basicLength) { + output.push(delimiter); + } - for (var i = 0; promise._state === lib$es6$promise$$internal$$PENDING && i < length; i++) { - lib$es6$promise$$internal$$subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection); - } + // Main encoding loop: + while (handledCPCount < inputLength) { - return promise; - } - var lib$es6$promise$promise$race$$default = lib$es6$promise$promise$race$$race; - function lib$es6$promise$promise$resolve$$resolve(object) { - /*jshint validthis:true */ - var Constructor = this; + // All non-basic code points < n have been handled already. Find the next + // larger one: + for (m = maxInt, j = 0; j < inputLength; ++j) { + currentValue = input[j]; + if (currentValue >= n && currentValue < m) { + m = currentValue; + } + } - if (object && typeof object === 'object' && object.constructor === Constructor) { - return object; - } + // Increase `delta` enough to advance the decoder's state to , + // but guard against overflow + handledCPCountPlusOne = handledCPCount + 1; + if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) { + error('overflow'); + } - var promise = new Constructor(lib$es6$promise$$internal$$noop); - lib$es6$promise$$internal$$resolve(promise, object); - return promise; - } - var lib$es6$promise$promise$resolve$$default = lib$es6$promise$promise$resolve$$resolve; - function lib$es6$promise$promise$reject$$reject(reason) { - /*jshint validthis:true */ - var Constructor = this; - var promise = new Constructor(lib$es6$promise$$internal$$noop); - lib$es6$promise$$internal$$reject(promise, reason); - return promise; - } - var lib$es6$promise$promise$reject$$default = lib$es6$promise$promise$reject$$reject; + delta += (m - n) * handledCPCountPlusOne; + n = m; - var lib$es6$promise$promise$$counter = 0; + for (j = 0; j < inputLength; ++j) { + currentValue = input[j]; - function lib$es6$promise$promise$$needsResolver() { - throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); - } + if (currentValue < n && ++delta > maxInt) { + error('overflow'); + } - function lib$es6$promise$promise$$needsNew() { - throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); - } + if (currentValue == n) { + // Represent delta as a generalized variable-length integer + for (q = delta, k = base; /* no condition */; k += base) { + t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + if (q < t) { + break; + } + qMinusT = q - t; + baseMinusT = base - t; + output.push( + stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0)) + ); + q = floor(qMinusT / baseMinusT); + } - var lib$es6$promise$promise$$default = lib$es6$promise$promise$$Promise; - /** - Promise objects represent the eventual result of an asynchronous operation. The - primary way of interacting with a promise is through its `then` method, which - registers callbacks to receive either a promise's eventual value or the reason - why the promise cannot be fulfilled. + output.push(stringFromCharCode(digitToBasic(q, 0))); + bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); + delta = 0; + ++handledCPCount; + } + } - Terminology - ----------- + ++delta; + ++n; - - `promise` is an object or function with a `then` method whose behavior conforms to this specification. - - `thenable` is an object or function that defines a `then` method. - - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). - - `exception` is a value that is thrown using the throw statement. - - `reason` is a value that indicates why a promise was rejected. - - `settled` the final resting state of a promise, fulfilled or rejected. + } + return output.join(''); + } - A promise can be in one of three states: pending, fulfilled, or rejected. + /** + * Converts a Punycode string representing a domain name or an email address + * to Unicode. Only the Punycoded parts of the input will be converted, i.e. + * it doesn't matter if you call it on a string that has already been + * converted to Unicode. + * @memberOf punycode + * @param {String} input The Punycoded domain name or email address to + * convert to Unicode. + * @returns {String} The Unicode representation of the given Punycode + * string. + */ + function toUnicode(input) { + return mapDomain(input, function(string) { + return regexPunycode.test(string) + ? decode(string.slice(4).toLowerCase()) + : string; + }); + } - Promises that are fulfilled have a fulfillment value and are in the fulfilled - state. Promises that are rejected have a rejection reason and are in the - rejected state. A fulfillment value is never a thenable. + /** + * Converts a Unicode string representing a domain name or an email address to + * Punycode. Only the non-ASCII parts of the domain name will be converted, + * i.e. it doesn't matter if you call it with a domain that's already in + * ASCII. + * @memberOf punycode + * @param {String} input The domain name or email address to convert, as a + * Unicode string. + * @returns {String} The Punycode representation of the given domain name or + * email address. + */ + function toASCII(input) { + return mapDomain(input, function(string) { + return regexNonASCII.test(string) + ? 'xn--' + encode(string) + : string; + }); + } - Promises can also be said to *resolve* a value. If this value is also a - promise, then the original promise's settled state will match the value's - settled state. So a promise that *resolves* a promise that rejects will - itself reject, and a promise that *resolves* a promise that fulfills will - itself fulfill. + /*--------------------------------------------------------------------------*/ + /** Define the public API */ + punycode = { + /** + * A string representing the current Punycode.js version number. + * @memberOf punycode + * @type String + */ + 'version': '1.4.1', + /** + * An object of methods to convert from JavaScript's internal character + * representation (UCS-2) to Unicode code points, and back. + * @see + * @memberOf punycode + * @type Object + */ + 'ucs2': { + 'decode': ucs2decode, + 'encode': ucs2encode + }, + 'decode': decode, + 'encode': encode, + 'toASCII': toASCII, + 'toUnicode': toUnicode + }; - Basic Usage: - ------------ + /** Expose `punycode` */ + // Some AMD build optimizers, like r.js, check for specific condition patterns + // like the following: + if ( + typeof define == 'function' && + typeof define.amd == 'object' && + define.amd + ) { + define('punycode', function() { + return punycode; + }); + } else if (freeExports && freeModule) { + if (module.exports == freeExports) { + // in Node.js, io.js, or RingoJS v0.8.0+ + freeModule.exports = punycode; + } else { + // in Narwhal or RingoJS v0.7.0- + for (key in punycode) { + punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]); + } + } + } else { + // in Rhino or a web browser + root.punycode = punycode; + } - ```js - var promise = new Promise(function(resolve, reject) { - // on success - resolve(value); +}(this)); - // on failure - reject(reason); - }); +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - promise.then(function(value) { - // on fulfillment - }, function(reason) { - // on rejection - }); - ``` +},{}],11:[function(_dereq_,module,exports){ +module.exports = _dereq_('./lib/_stream_duplex.js'); - Advanced Usage: - --------------- +},{"./lib/_stream_duplex.js":12}],12:[function(_dereq_,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. - Promises shine when abstracting away asynchronous interactions such as - `XMLHttpRequest`s. +// a duplex stream is just a stream that is both readable and writable. +// Since JS doesn't have multiple prototypal inheritance, this class +// prototypally inherits from Readable, and then parasitically from +// Writable. - ```js - function getJSON(url) { - return new Promise(function(resolve, reject){ - var xhr = new XMLHttpRequest(); +'use strict'; - xhr.open('GET', url); - xhr.onreadystatechange = handler; - xhr.responseType = 'json'; - xhr.setRequestHeader('Accept', 'application/json'); - xhr.send(); +/**/ - function handler() { - if (this.readyState === this.DONE) { - if (this.status === 200) { - resolve(this.response); - } else { - reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); - } - } - }; - }); - } +var pna = _dereq_('process-nextick-args'); +/**/ - getJSON('/posts.json').then(function(json) { - // on fulfillment - }, function(reason) { - // on rejection - }); - ``` +/**/ +var objectKeys = Object.keys || function (obj) { + var keys = []; + for (var key in obj) { + keys.push(key); + }return keys; +}; +/**/ - Unlike callbacks, promises are great composable primitives. +module.exports = Duplex; - ```js - Promise.all([ - getJSON('/posts'), - getJSON('/comments') - ]).then(function(values){ - values[0] // => postsJSON - values[1] // => commentsJSON +/**/ +var util = Object.create(_dereq_('core-util-is')); +util.inherits = _dereq_('inherits'); +/**/ - return values; - }); - ``` +var Readable = _dereq_('./_stream_readable'); +var Writable = _dereq_('./_stream_writable'); - @class Promise - @param {function} resolver - Useful for tooling. - @constructor - */ - function lib$es6$promise$promise$$Promise(resolver) { - this._id = lib$es6$promise$promise$$counter++; - this._state = undefined; - this._result = undefined; - this._subscribers = []; +util.inherits(Duplex, Readable); - if (lib$es6$promise$$internal$$noop !== resolver) { - if (!lib$es6$promise$utils$$isFunction(resolver)) { - lib$es6$promise$promise$$needsResolver(); - } +{ + // avoid scope creep, the keys array can then be collected + var keys = objectKeys(Writable.prototype); + for (var v = 0; v < keys.length; v++) { + var method = keys[v]; + if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method]; + } +} - if (!(this instanceof lib$es6$promise$promise$$Promise)) { - lib$es6$promise$promise$$needsNew(); - } +function Duplex(options) { + if (!(this instanceof Duplex)) return new Duplex(options); - lib$es6$promise$$internal$$initializePromise(this, resolver); - } - } + Readable.call(this, options); + Writable.call(this, options); - lib$es6$promise$promise$$Promise.all = lib$es6$promise$promise$all$$default; - lib$es6$promise$promise$$Promise.race = lib$es6$promise$promise$race$$default; - lib$es6$promise$promise$$Promise.resolve = lib$es6$promise$promise$resolve$$default; - lib$es6$promise$promise$$Promise.reject = lib$es6$promise$promise$reject$$default; - lib$es6$promise$promise$$Promise._setScheduler = lib$es6$promise$asap$$setScheduler; - lib$es6$promise$promise$$Promise._setAsap = lib$es6$promise$asap$$setAsap; - lib$es6$promise$promise$$Promise._asap = lib$es6$promise$asap$$asap; + if (options && options.readable === false) this.readable = false; - lib$es6$promise$promise$$Promise.prototype = { - constructor: lib$es6$promise$promise$$Promise, + if (options && options.writable === false) this.writable = false; - /** - The primary way of interacting with a promise is through its `then` method, - which registers callbacks to receive either a promise's eventual value or the - reason why the promise cannot be fulfilled. + this.allowHalfOpen = true; + if (options && options.allowHalfOpen === false) this.allowHalfOpen = false; - ```js - findUser().then(function(user){ - // user is available - }, function(reason){ - // user is unavailable, and you are given the reason why - }); - ``` + this.once('end', onend); +} - Chaining - -------- +Object.defineProperty(Duplex.prototype, 'writableHighWaterMark', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function () { + return this._writableState.highWaterMark; + } +}); - The return value of `then` is itself a promise. This second, 'downstream' - promise is resolved with the return value of the first promise's fulfillment - or rejection handler, or rejected if the handler throws an exception. +// the no-half-open enforcer +function onend() { + // if we allow half-open state, or if the writable side ended, + // then we're ok. + if (this.allowHalfOpen || this._writableState.ended) return; - ```js - findUser().then(function (user) { - return user.name; - }, function (reason) { - return 'default name'; - }).then(function (userName) { - // If `findUser` fulfilled, `userName` will be the user's name, otherwise it - // will be `'default name'` - }); + // no more data can be written. + // But allow more writes to happen in this tick. + pna.nextTick(onEndNT, this); +} - findUser().then(function (user) { - throw new Error('Found user, but still unhappy'); - }, function (reason) { - throw new Error('`findUser` rejected and we're unhappy'); - }).then(function (value) { - // never reached - }, function (reason) { - // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. - // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. - }); - ``` - If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. +function onEndNT(self) { + self.end(); +} - ```js - findUser().then(function (user) { - throw new PedagogicalException('Upstream error'); - }).then(function (value) { - // never reached - }).then(function (value) { - // never reached - }, function (reason) { - // The `PedgagocialException` is propagated all the way down to here - }); - ``` +Object.defineProperty(Duplex.prototype, 'destroyed', { + get: function () { + if (this._readableState === undefined || this._writableState === undefined) { + return false; + } + return this._readableState.destroyed && this._writableState.destroyed; + }, + set: function (value) { + // we ignore the value if the stream + // has not been initialized yet + if (this._readableState === undefined || this._writableState === undefined) { + return; + } - Assimilation - ------------ + // backward compatibility, the user is explicitly + // managing destroyed + this._readableState.destroyed = value; + this._writableState.destroyed = value; + } +}); - Sometimes the value you want to propagate to a downstream promise can only be - retrieved asynchronously. This can be achieved by returning a promise in the - fulfillment or rejection handler. The downstream promise will then be pending - until the returned promise is settled. This is called *assimilation*. +Duplex.prototype._destroy = function (err, cb) { + this.push(null); + this.end(); - ```js - findUser().then(function (user) { - return findCommentsByAuthor(user); - }).then(function (comments) { - // The user's comments are now available - }); - ``` + pna.nextTick(cb, err); +}; +},{"./_stream_readable":14,"./_stream_writable":16,"core-util-is":38,"inherits":43,"process-nextick-args":73}],13:[function(_dereq_,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. - If the assimliated promise rejects, then the downstream promise will also reject. +// a passthrough stream. +// basically just the most minimal sort of Transform stream. +// Every written chunk gets output as-is. - ```js - findUser().then(function (user) { - return findCommentsByAuthor(user); - }).then(function (comments) { - // If `findCommentsByAuthor` fulfills, we'll have the value here - }, function (reason) { - // If `findCommentsByAuthor` rejects, we'll have the reason here - }); - ``` - - Simple Example - -------------- - - Synchronous Example - - ```javascript - var result; - - try { - result = findResult(); - // success - } catch(reason) { - // failure - } - ``` - - Errback Example - - ```js - findResult(function(result, err){ - if (err) { - // failure - } else { - // success - } - }); - ``` - - Promise Example; - - ```javascript - findResult().then(function(result){ - // success - }, function(reason){ - // failure - }); - ``` - - Advanced Example - -------------- - - Synchronous Example - - ```javascript - var author, books; - - try { - author = findAuthor(); - books = findBooksByAuthor(author); - // success - } catch(reason) { - // failure - } - ``` - - Errback Example - - ```js - - function foundBooks(books) { - - } - - function failure(reason) { - - } - - findAuthor(function(author, err){ - if (err) { - failure(err); - // failure - } else { - try { - findBoooksByAuthor(author, function(books, err) { - if (err) { - failure(err); - } else { - try { - foundBooks(books); - } catch(reason) { - failure(reason); - } - } - }); - } catch(error) { - failure(err); - } - // success - } - }); - ``` - - Promise Example; - - ```javascript - findAuthor(). - then(findBooksByAuthor). - then(function(books){ - // found books - }).catch(function(reason){ - // something went wrong - }); - ``` - - @method then - @param {Function} onFulfilled - @param {Function} onRejected - Useful for tooling. - @return {Promise} - */ - then: function(onFulfillment, onRejection) { - var parent = this; - var state = parent._state; - - if (state === lib$es6$promise$$internal$$FULFILLED && !onFulfillment || state === lib$es6$promise$$internal$$REJECTED && !onRejection) { - return this; - } - - var child = new this.constructor(lib$es6$promise$$internal$$noop); - var result = parent._result; - - if (state) { - var callback = arguments[state - 1]; - lib$es6$promise$asap$$asap(function(){ - lib$es6$promise$$internal$$invokeCallback(state, child, callback, result); - }); - } else { - lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection); - } - - return child; - }, - - /** - `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same - as the catch block of a try/catch statement. - - ```js - function findAuthor(){ - throw new Error('couldn't find that author'); - } - - // synchronous - try { - findAuthor(); - } catch(reason) { - // something went wrong - } - - // async with promises - findAuthor().catch(function(reason){ - // something went wrong - }); - ``` - - @method catch - @param {Function} onRejection - Useful for tooling. - @return {Promise} - */ - 'catch': function(onRejection) { - return this.then(null, onRejection); - } - }; - function lib$es6$promise$polyfill$$polyfill() { - var local; - - if (typeof global !== 'undefined') { - local = global; - } else if (typeof self !== 'undefined') { - local = self; - } else { - try { - local = Function('return this')(); - } catch (e) { - throw new Error('polyfill failed because global object is unavailable in this environment'); - } - } - - var P = local.Promise; - - if (P && Object.prototype.toString.call(P.resolve()) === '[object Promise]' && !P.cast) { - return; - } +'use strict'; - local.Promise = lib$es6$promise$promise$$default; - } - var lib$es6$promise$polyfill$$default = lib$es6$promise$polyfill$$polyfill; +module.exports = PassThrough; - var lib$es6$promise$umd$$ES6Promise = { - 'Promise': lib$es6$promise$promise$$default, - 'polyfill': lib$es6$promise$polyfill$$default - }; +var Transform = _dereq_('./_stream_transform'); - /* global define:true module:true window: true */ - if (typeof define === 'function' && define['amd']) { - define(function() { return lib$es6$promise$umd$$ES6Promise; }); - } else if (typeof module !== 'undefined' && module['exports']) { - module['exports'] = lib$es6$promise$umd$$ES6Promise; - } else if (typeof this !== 'undefined') { - this['ES6Promise'] = lib$es6$promise$umd$$ES6Promise; - } +/**/ +var util = Object.create(_dereq_('core-util-is')); +util.inherits = _dereq_('inherits'); +/**/ - lib$es6$promise$polyfill$$default(); -}).call(this); +util.inherits(PassThrough, Transform); +function PassThrough(options) { + if (!(this instanceof PassThrough)) return new PassThrough(options); -}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) + Transform.call(this, options); +} -},{"_process":47}],11:[function(_dereq_,module,exports){ +PassThrough.prototype._transform = function (chunk, encoding, cb) { + cb(null, chunk); +}; +},{"./_stream_transform":15,"core-util-is":38,"inherits":43}],14:[function(_dereq_,module,exports){ +(function (process,global){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -4526,36940 +3940,37526 @@ function objectToString(o) { // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -function EventEmitter() { - this._events = this._events || {}; - this._maxListeners = this._maxListeners || undefined; -} -module.exports = EventEmitter; - -// Backwards-compat with node 0.10.x -EventEmitter.EventEmitter = EventEmitter; +'use strict'; -EventEmitter.prototype._events = undefined; -EventEmitter.prototype._maxListeners = undefined; +/**/ -// By default EventEmitters will print a warning if more than 10 listeners are -// added to it. This is a useful default which helps finding memory leaks. -EventEmitter.defaultMaxListeners = 10; +var pna = _dereq_('process-nextick-args'); +/**/ -// Obviously not all Emitters should be limited to 10. This function allows -// that to be increased. Set to zero for unlimited. -EventEmitter.prototype.setMaxListeners = function(n) { - if (!isNumber(n) || n < 0 || isNaN(n)) - throw TypeError('n must be a positive number'); - this._maxListeners = n; - return this; -}; +module.exports = Readable; -EventEmitter.prototype.emit = function(type) { - var er, handler, len, args, i, listeners; +/**/ +var isArray = _dereq_('isarray'); +/**/ - if (!this._events) - this._events = {}; +/**/ +var Duplex; +/**/ - // If there is no 'error' event listener then throw. - if (type === 'error') { - if (!this._events.error || - (isObject(this._events.error) && !this._events.error.length)) { - er = arguments[1]; - if (er instanceof Error) { - throw er; // Unhandled 'error' event - } - throw TypeError('Uncaught, unspecified "error" event.'); - } - } +Readable.ReadableState = ReadableState; - handler = this._events[type]; +/**/ +var EE = _dereq_('events').EventEmitter; - if (isUndefined(handler)) - return false; +var EElistenerCount = function (emitter, type) { + return emitter.listeners(type).length; +}; +/**/ - if (isFunction(handler)) { - switch (arguments.length) { - // fast cases - case 1: - handler.call(this); - break; - case 2: - handler.call(this, arguments[1]); - break; - case 3: - handler.call(this, arguments[1], arguments[2]); - break; - // slower - default: - len = arguments.length; - args = new Array(len - 1); - for (i = 1; i < len; i++) - args[i - 1] = arguments[i]; - handler.apply(this, args); - } - } else if (isObject(handler)) { - len = arguments.length; - args = new Array(len - 1); - for (i = 1; i < len; i++) - args[i - 1] = arguments[i]; +/**/ +var Stream = _dereq_('./internal/streams/stream'); +/**/ - listeners = handler.slice(); - len = listeners.length; - for (i = 0; i < len; i++) - listeners[i].apply(this, args); - } +/**/ - return true; -}; +var Buffer = _dereq_('safe-buffer').Buffer; +var OurUint8Array = global.Uint8Array || function () {}; +function _uint8ArrayToBuffer(chunk) { + return Buffer.from(chunk); +} +function _isUint8Array(obj) { + return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; +} -EventEmitter.prototype.addListener = function(type, listener) { - var m; +/**/ - if (!isFunction(listener)) - throw TypeError('listener must be a function'); +/**/ +var util = Object.create(_dereq_('core-util-is')); +util.inherits = _dereq_('inherits'); +/**/ - if (!this._events) - this._events = {}; +/**/ +var debugUtil = _dereq_('util'); +var debug = void 0; +if (debugUtil && debugUtil.debuglog) { + debug = debugUtil.debuglog('stream'); +} else { + debug = function () {}; +} +/**/ - // To avoid recursion in the case that type === "newListener"! Before - // adding it to the listeners, first emit "newListener". - if (this._events.newListener) - this.emit('newListener', type, - isFunction(listener.listener) ? - listener.listener : listener); +var BufferList = _dereq_('./internal/streams/BufferList'); +var destroyImpl = _dereq_('./internal/streams/destroy'); +var StringDecoder; - if (!this._events[type]) - // Optimize the case of one listener. Don't need the extra array object. - this._events[type] = listener; - else if (isObject(this._events[type])) - // If we've already got an array, just append. - this._events[type].push(listener); - else - // Adding the second element, need to change to array. - this._events[type] = [this._events[type], listener]; +util.inherits(Readable, Stream); - // Check for listener leak - if (isObject(this._events[type]) && !this._events[type].warned) { - var m; - if (!isUndefined(this._maxListeners)) { - m = this._maxListeners; - } else { - m = EventEmitter.defaultMaxListeners; - } +var kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume']; - if (m && m > 0 && this._events[type].length > m) { - this._events[type].warned = true; - console.error('(node) warning: possible EventEmitter memory ' + - 'leak detected. %d listeners added. ' + - 'Use emitter.setMaxListeners() to increase limit.', - this._events[type].length); - if (typeof console.trace === 'function') { - // not supported in IE 10 - console.trace(); - } - } - } +function prependListener(emitter, event, fn) { + // Sadly this is not cacheable as some libraries bundle their own + // event emitter implementation with them. + if (typeof emitter.prependListener === 'function') return emitter.prependListener(event, fn); - return this; -}; + // This is a hack to make sure that our error handler is attached before any + // userland ones. NEVER DO THIS. This is here only because this code needs + // to continue to work with older versions of Node.js that do not include + // the prependListener() method. The goal is to eventually remove this hack. + if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]]; +} -EventEmitter.prototype.on = EventEmitter.prototype.addListener; +function ReadableState(options, stream) { + Duplex = Duplex || _dereq_('./_stream_duplex'); -EventEmitter.prototype.once = function(type, listener) { - if (!isFunction(listener)) - throw TypeError('listener must be a function'); + options = options || {}; - var fired = false; + // Duplex streams are both readable and writable, but share + // the same options object. + // However, some cases require setting options to different + // values for the readable and the writable sides of the duplex stream. + // These options can be provided separately as readableXXX and writableXXX. + var isDuplex = stream instanceof Duplex; - function g() { - this.removeListener(type, g); + // object stream flag. Used to make read(n) ignore n and to + // make all the buffer merging and length checks go away + this.objectMode = !!options.objectMode; - if (!fired) { - fired = true; - listener.apply(this, arguments); - } - } + if (isDuplex) this.objectMode = this.objectMode || !!options.readableObjectMode; - g.listener = listener; - this.on(type, g); + // the point at which it stops calling _read() to fill the buffer + // Note: 0 is a valid value, means "don't call _read preemptively ever" + var hwm = options.highWaterMark; + var readableHwm = options.readableHighWaterMark; + var defaultHwm = this.objectMode ? 16 : 16 * 1024; - return this; -}; + if (hwm || hwm === 0) this.highWaterMark = hwm;else if (isDuplex && (readableHwm || readableHwm === 0)) this.highWaterMark = readableHwm;else this.highWaterMark = defaultHwm; -// emits a 'removeListener' event iff the listener was removed -EventEmitter.prototype.removeListener = function(type, listener) { - var list, position, length, i; + // cast to ints. + this.highWaterMark = Math.floor(this.highWaterMark); - if (!isFunction(listener)) - throw TypeError('listener must be a function'); + // A linked list is used to store data chunks instead of an array because the + // linked list can remove elements from the beginning faster than + // array.shift() + this.buffer = new BufferList(); + this.length = 0; + this.pipes = null; + this.pipesCount = 0; + this.flowing = null; + this.ended = false; + this.endEmitted = false; + this.reading = false; - if (!this._events || !this._events[type]) - return this; + // a flag to be able to tell if the event 'readable'/'data' is emitted + // immediately, or on a later tick. We set this to true at first, because + // any actions that shouldn't happen until "later" should generally also + // not happen before the first read call. + this.sync = true; - list = this._events[type]; - length = list.length; - position = -1; + // whenever we return null, then we set a flag to say + // that we're awaiting a 'readable' event emission. + this.needReadable = false; + this.emittedReadable = false; + this.readableListening = false; + this.resumeScheduled = false; - if (list === listener || - (isFunction(list.listener) && list.listener === listener)) { - delete this._events[type]; - if (this._events.removeListener) - this.emit('removeListener', type, listener); + // has it been destroyed + this.destroyed = false; - } else if (isObject(list)) { - for (i = length; i-- > 0;) { - if (list[i] === listener || - (list[i].listener && list[i].listener === listener)) { - position = i; - break; - } - } + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + this.defaultEncoding = options.defaultEncoding || 'utf8'; - if (position < 0) - return this; + // the number of writers that are awaiting a drain event in .pipe()s + this.awaitDrain = 0; - if (list.length === 1) { - list.length = 0; - delete this._events[type]; - } else { - list.splice(position, 1); - } + // if true, a maybeReadMore has been scheduled + this.readingMore = false; - if (this._events.removeListener) - this.emit('removeListener', type, listener); + this.decoder = null; + this.encoding = null; + if (options.encoding) { + if (!StringDecoder) StringDecoder = _dereq_('string_decoder/').StringDecoder; + this.decoder = new StringDecoder(options.encoding); + this.encoding = options.encoding; } +} - return this; -}; +function Readable(options) { + Duplex = Duplex || _dereq_('./_stream_duplex'); -EventEmitter.prototype.removeAllListeners = function(type) { - var key, listeners; + if (!(this instanceof Readable)) return new Readable(options); - if (!this._events) - return this; + this._readableState = new ReadableState(options, this); - // not listening for removeListener, no need to emit - if (!this._events.removeListener) { - if (arguments.length === 0) - this._events = {}; - else if (this._events[type]) - delete this._events[type]; - return this; + // legacy + this.readable = true; + + if (options) { + if (typeof options.read === 'function') this._read = options.read; + + if (typeof options.destroy === 'function') this._destroy = options.destroy; } - // emit removeListener for all listeners on all events - if (arguments.length === 0) { - for (key in this._events) { - if (key === 'removeListener') continue; - this.removeAllListeners(key); + Stream.call(this); +} + +Object.defineProperty(Readable.prototype, 'destroyed', { + get: function () { + if (this._readableState === undefined) { + return false; } - this.removeAllListeners('removeListener'); - this._events = {}; - return this; + return this._readableState.destroyed; + }, + set: function (value) { + // we ignore the value if the stream + // has not been initialized yet + if (!this._readableState) { + return; + } + + // backward compatibility, the user is explicitly + // managing destroyed + this._readableState.destroyed = value; } +}); - listeners = this._events[type]; +Readable.prototype.destroy = destroyImpl.destroy; +Readable.prototype._undestroy = destroyImpl.undestroy; +Readable.prototype._destroy = function (err, cb) { + this.push(null); + cb(err); +}; - if (isFunction(listeners)) { - this.removeListener(type, listeners); +// Manually shove something into the read() buffer. +// This returns true if the highWaterMark has not been hit yet, +// similar to how Writable.write() returns true if you should +// write() some more. +Readable.prototype.push = function (chunk, encoding) { + var state = this._readableState; + var skipChunkCheck; + + if (!state.objectMode) { + if (typeof chunk === 'string') { + encoding = encoding || state.defaultEncoding; + if (encoding !== state.encoding) { + chunk = Buffer.from(chunk, encoding); + encoding = ''; + } + skipChunkCheck = true; + } } else { - // LIFO order - while (listeners.length) - this.removeListener(type, listeners[listeners.length - 1]); + skipChunkCheck = true; } - delete this._events[type]; - return this; + return readableAddChunk(this, chunk, encoding, false, skipChunkCheck); }; -EventEmitter.prototype.listeners = function(type) { - var ret; - if (!this._events || !this._events[type]) - ret = []; - else if (isFunction(this._events[type])) - ret = [this._events[type]]; - else - ret = this._events[type].slice(); - return ret; +// Unshift should *always* be something directly out of read() +Readable.prototype.unshift = function (chunk) { + return readableAddChunk(this, chunk, null, true, false); }; -EventEmitter.listenerCount = function(emitter, type) { - var ret; - if (!emitter._events || !emitter._events[type]) - ret = 0; - else if (isFunction(emitter._events[type])) - ret = 1; - else - ret = emitter._events[type].length; - return ret; -}; +function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) { + var state = stream._readableState; + if (chunk === null) { + state.reading = false; + onEofChunk(stream, state); + } else { + var er; + if (!skipChunkCheck) er = chunkInvalid(state, chunk); + if (er) { + stream.emit('error', er); + } else if (state.objectMode || chunk && chunk.length > 0) { + if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) { + chunk = _uint8ArrayToBuffer(chunk); + } -function isFunction(arg) { - return typeof arg === 'function'; -} + if (addToFront) { + if (state.endEmitted) stream.emit('error', new Error('stream.unshift() after end event'));else addChunk(stream, state, chunk, true); + } else if (state.ended) { + stream.emit('error', new Error('stream.push() after EOF')); + } else { + state.reading = false; + if (state.decoder && !encoding) { + chunk = state.decoder.write(chunk); + if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state); + } else { + addChunk(stream, state, chunk, false); + } + } + } else if (!addToFront) { + state.reading = false; + } + } -function isNumber(arg) { - return typeof arg === 'number'; + return needMoreData(state); } -function isObject(arg) { - return typeof arg === 'object' && arg !== null; -} +function addChunk(stream, state, chunk, addToFront) { + if (state.flowing && state.length === 0 && !state.sync) { + stream.emit('data', chunk); + stream.read(0); + } else { + // update the buffer info. + state.length += state.objectMode ? 1 : chunk.length; + if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk); -function isUndefined(arg) { - return arg === void 0; + if (state.needReadable) emitReadable(stream); + } + maybeReadMore(stream, state); } -},{}],12:[function(_dereq_,module,exports){ +function chunkInvalid(state, chunk) { + var er; + if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { + er = new TypeError('Invalid non-string/buffer chunk'); + } + return er; +} -var hasOwn = Object.prototype.hasOwnProperty; -var toString = Object.prototype.toString; +// if it's past the high water mark, we can push in some more. +// Also, if we have no data yet, we can stand some +// more bytes. This is to work around cases where hwm=0, +// such as the repl. Also, if the push() triggered a +// readable event, and the user called read(largeNumber) such that +// needReadable was set, then we ought to push more, so that another +// 'readable' event will be triggered. +function needMoreData(state) { + return !state.ended && (state.needReadable || state.length < state.highWaterMark || state.length === 0); +} -module.exports = function forEach (obj, fn, ctx) { - if (toString.call(fn) !== '[object Function]') { - throw new TypeError('iterator must be a function'); - } - var l = obj.length; - if (l === +l) { - for (var i = 0; i < l; i++) { - fn.call(ctx, obj[i], i, obj); - } - } else { - for (var k in obj) { - if (hasOwn.call(obj, k)) { - fn.call(ctx, obj[k], k, obj); - } - } - } +Readable.prototype.isPaused = function () { + return this._readableState.flowing === false; }; - -},{}],13:[function(_dereq_,module,exports){ -var http = _dereq_('http'); - -var https = module.exports; - -for (var key in http) { - if (http.hasOwnProperty(key)) https[key] = http[key]; +// backwards compatibility. +Readable.prototype.setEncoding = function (enc) { + if (!StringDecoder) StringDecoder = _dereq_('string_decoder/').StringDecoder; + this._readableState.decoder = new StringDecoder(enc); + this._readableState.encoding = enc; + return this; }; -https.request = function (params, cb) { - if (!params) params = {}; - params.scheme = 'https'; - params.protocol = 'https:'; - return http.request.call(this, params, cb); +// Don't raise the hwm > 8MB +var MAX_HWM = 0x800000; +function computeNewHighWaterMark(n) { + if (n >= MAX_HWM) { + n = MAX_HWM; + } else { + // Get the next highest power of 2 to prevent increasing hwm excessively in + // tiny amounts + n--; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + n++; + } + return n; } -},{"http":111}],14:[function(_dereq_,module,exports){ -/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ -exports.read = function (buffer, offset, isLE, mLen, nBytes) { - var e, m - var eLen = (nBytes * 8) - mLen - 1 - var eMax = (1 << eLen) - 1 - var eBias = eMax >> 1 - var nBits = -7 - var i = isLE ? (nBytes - 1) : 0 - var d = isLE ? -1 : 1 - var s = buffer[offset + i] +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function howMuchToRead(n, state) { + if (n <= 0 || state.length === 0 && state.ended) return 0; + if (state.objectMode) return 1; + if (n !== n) { + // Only flow one buffer at a time + if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length; + } + // If we're asking for more than the current hwm, then raise the hwm. + if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n); + if (n <= state.length) return n; + // Don't have enough + if (!state.ended) { + state.needReadable = true; + return 0; + } + return state.length; +} - i += d +// you can override either this method, or the async _read(n) below. +Readable.prototype.read = function (n) { + debug('read', n); + n = parseInt(n, 10); + var state = this._readableState; + var nOrig = n; - e = s & ((1 << (-nBits)) - 1) - s >>= (-nBits) - nBits += eLen - for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {} + if (n !== 0) state.emittedReadable = false; - m = e & ((1 << (-nBits)) - 1) - e >>= (-nBits) - nBits += mLen - for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {} + // if we're doing read(0) to trigger a readable event, but we + // already have a bunch of data in the buffer, then just trigger + // the 'readable' event and move on. + if (n === 0 && state.needReadable && (state.length >= state.highWaterMark || state.ended)) { + debug('read: emitReadable', state.length, state.ended); + if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this); + return null; + } - if (e === 0) { - e = 1 - eBias - } else if (e === eMax) { - return m ? NaN : ((s ? -1 : 1) * Infinity) - } else { - m = m + Math.pow(2, mLen) - e = e - eBias + n = howMuchToRead(n, state); + + // if we've ended, and we're now clear, then finish it up. + if (n === 0 && state.ended) { + if (state.length === 0) endReadable(this); + return null; } - return (s ? -1 : 1) * m * Math.pow(2, e - mLen) -} -exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { - var e, m, c - var eLen = (nBytes * 8) - mLen - 1 - var eMax = (1 << eLen) - 1 - var eBias = eMax >> 1 - var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) - var i = isLE ? 0 : (nBytes - 1) - var d = isLE ? 1 : -1 - var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 + // All the actual chunk generation logic needs to be + // *below* the call to _read. The reason is that in certain + // synthetic stream cases, such as passthrough streams, _read + // may be a completely synchronous operation which may change + // the state of the read buffer, providing enough data when + // before there was *not* enough. + // + // So, the steps are: + // 1. Figure out what the state of things will be after we do + // a read from the buffer. + // + // 2. If that resulting state will trigger a _read, then call _read. + // Note that this may be asynchronous, or synchronous. Yes, it is + // deeply ugly to write APIs this way, but that still doesn't mean + // that the Readable class should behave improperly, as streams are + // designed to be sync/async agnostic. + // Take note if the _read call is sync or async (ie, if the read call + // has returned yet), so that we know whether or not it's safe to emit + // 'readable' etc. + // + // 3. Actually pull the requested chunks out of the buffer and return. - value = Math.abs(value) + // if we need a readable event, then we need to do some reading. + var doRead = state.needReadable; + debug('need readable', doRead); - if (isNaN(value) || value === Infinity) { - m = isNaN(value) ? 1 : 0 - e = eMax - } else { - e = Math.floor(Math.log(value) / Math.LN2) - if (value * (c = Math.pow(2, -e)) < 1) { - e-- - c *= 2 - } - if (e + eBias >= 1) { - value += rt / c - } else { - value += rt * Math.pow(2, 1 - eBias) - } - if (value * c >= 2) { - e++ - c /= 2 - } + // if we currently have less than the highWaterMark, then also read some + if (state.length === 0 || state.length - n < state.highWaterMark) { + doRead = true; + debug('length less than watermark', doRead); + } - if (e + eBias >= eMax) { - m = 0 - e = eMax - } else if (e + eBias >= 1) { - m = ((value * c) - 1) * Math.pow(2, mLen) - e = e + eBias - } else { - m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) - e = 0 - } + // however, if we've ended, then there's no point, and if we're already + // reading, then it's unnecessary. + if (state.ended || state.reading) { + doRead = false; + debug('reading or ended', doRead); + } else if (doRead) { + debug('do read'); + state.reading = true; + state.sync = true; + // if the length is currently zero, then we *need* a readable event. + if (state.length === 0) state.needReadable = true; + // call internal read method + this._read(state.highWaterMark); + state.sync = false; + // If _read pushed data synchronously, then `reading` will be false, + // and we need to re-evaluate how much data we can return to the user. + if (!state.reading) n = howMuchToRead(nOrig, state); } - for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} + var ret; + if (n > 0) ret = fromList(n, state);else ret = null; - e = (e << mLen) | m - eLen += mLen - for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} + if (ret === null) { + state.needReadable = true; + n = 0; + } else { + state.length -= n; + } - buffer[offset + i - d] |= s * 128 -} + if (state.length === 0) { + // If we have nothing in the buffer, then we want to know + // as soon as we *do* get something into the buffer. + if (!state.ended) state.needReadable = true; -},{}],15:[function(_dereq_,module,exports){ + // If we tried to read() past the EOF, then emit end on the next tick. + if (nOrig !== n && state.ended) endReadable(this); + } -var indexOf = [].indexOf; + if (ret !== null) this.emit('data', ret); -module.exports = function(arr, obj){ - if (indexOf) return arr.indexOf(obj); - for (var i = 0; i < arr.length; ++i) { - if (arr[i] === obj) return i; - } - return -1; + return ret; }; -},{}],16:[function(_dereq_,module,exports){ -if (typeof Object.create === 'function') { - // implementation from standard node.js 'util' module - module.exports = function inherits(ctor, superCtor) { - if (superCtor) { - ctor.super_ = superCtor - ctor.prototype = Object.create(superCtor.prototype, { - constructor: { - value: ctor, - enumerable: false, - writable: true, - configurable: true - } - }) - } - }; -} else { - // old school shim for old browsers - module.exports = function inherits(ctor, superCtor) { - if (superCtor) { - ctor.super_ = superCtor - var TempCtor = function () {} - TempCtor.prototype = superCtor.prototype - ctor.prototype = new TempCtor() - ctor.prototype.constructor = ctor + +function onEofChunk(stream, state) { + if (state.ended) return; + if (state.decoder) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) { + state.buffer.push(chunk); + state.length += state.objectMode ? 1 : chunk.length; } } + state.ended = true; + + // emit 'readable' now to make sure it gets picked up. + emitReadable(stream); } -},{}],17:[function(_dereq_,module,exports){ -/*! - * Determine if an object is a Buffer - * - * @author Feross Aboukhadijeh - * @license MIT - */ +// Don't emit readable right away in sync mode, because this can trigger +// another read() call => stack overflow. This way, it might trigger +// a nextTick recursion warning, but that's not so bad. +function emitReadable(stream) { + var state = stream._readableState; + state.needReadable = false; + if (!state.emittedReadable) { + debug('emitReadable', state.flowing); + state.emittedReadable = true; + if (state.sync) pna.nextTick(emitReadable_, stream);else emitReadable_(stream); + } +} -// The _isBuffer check is for Safari 5-7 support, because it's missing -// Object.prototype.constructor. Remove this eventually -module.exports = function (obj) { - return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer) +function emitReadable_(stream) { + debug('emit readable'); + stream.emit('readable'); + flow(stream); } -function isBuffer (obj) { - return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) +// at this point, the user has presumably seen the 'readable' event, +// and called read() to consume some data. that may have triggered +// in turn another _read(n) call, in which case reading = true if +// it's in progress. +// However, if we're not ended, or reading, and the length < hwm, +// then go ahead and try to read some more preemptively. +function maybeReadMore(stream, state) { + if (!state.readingMore) { + state.readingMore = true; + pna.nextTick(maybeReadMore_, stream, state); + } } -// For Node v0.10 support. Remove this eventually. -function isSlowBuffer (obj) { - return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0)) +function maybeReadMore_(stream, state) { + var len = state.length; + while (!state.reading && !state.flowing && !state.ended && state.length < state.highWaterMark) { + debug('maybeReadMore read 0'); + stream.read(0); + if (len === state.length) + // didn't get any data, stop spinning. + break;else len = state.length; + } + state.readingMore = false; } -},{}],18:[function(_dereq_,module,exports){ -// Ignore module for browserify (see package.json) -},{}],19:[function(_dereq_,module,exports){ -(function (process,global,__dirname){ -/** - * A JavaScript implementation of the JSON-LD API. - * - * @author Dave Longley - * - * @license BSD 3-Clause License - * Copyright (c) 2011-2015 Digital Bazaar, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of the Digital Bazaar, Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -(function() { +// abstract method. to be overridden in specific implementation classes. +// call cb(er, data) where data is <= n in length. +// for virtual (non-string, non-buffer) streams, "length" is somewhat +// arbitrary, and perhaps not very meaningful. +Readable.prototype._read = function (n) { + this.emit('error', new Error('_read() is not implemented')); +}; -// determine if in-browser or using node.js -var _nodejs = ( - typeof process !== 'undefined' && process.versions && process.versions.node); -var _browser = !_nodejs && - (typeof window !== 'undefined' || typeof self !== 'undefined'); -if(_browser) { - if(typeof global === 'undefined') { - if(typeof window !== 'undefined') { - global = window; - } else if(typeof self !== 'undefined') { - global = self; - } else if(typeof $ !== 'undefined') { - global = $; - } +Readable.prototype.pipe = function (dest, pipeOpts) { + var src = this; + var state = this._readableState; + + switch (state.pipesCount) { + case 0: + state.pipes = dest; + break; + case 1: + state.pipes = [state.pipes, dest]; + break; + default: + state.pipes.push(dest); + break; } -} + state.pipesCount += 1; + debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts); -// attaches jsonld API to the given object -var wrapper = function(jsonld) { + var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr; -/* Core API */ + var endFn = doEnd ? onend : unpipe; + if (state.endEmitted) pna.nextTick(endFn);else src.once('end', endFn); -/** - * Performs JSON-LD compaction. - * - * @param input the JSON-LD input to compact. - * @param ctx the context to compact with. - * @param [options] options to use: - * [base] the base IRI to use. - * [compactArrays] true to compact arrays to single values when - * appropriate, false not to (default: true). - * [graph] true to always output a top-level graph (default: false). - * [expandContext] a context to expand with. - * [skipExpansion] true to assume the input is expanded and skip - * expansion, false not to, defaults to false. - * [documentLoader(url, callback(err, remoteDoc))] the document loader. - * @param callback(err, compacted, ctx) called once the operation completes. - */ -jsonld.compact = function(input, ctx, options, callback) { - if(arguments.length < 2) { - return jsonld.nextTick(function() { - callback(new TypeError('Could not compact, too few arguments.')); - }); + dest.on('unpipe', onunpipe); + function onunpipe(readable, unpipeInfo) { + debug('onunpipe'); + if (readable === src) { + if (unpipeInfo && unpipeInfo.hasUnpiped === false) { + unpipeInfo.hasUnpiped = true; + cleanup(); + } + } } - // get arguments - if(typeof options === 'function') { - callback = options; - options = {}; + function onend() { + debug('onend'); + dest.end(); } - options = options || {}; - if(ctx === null) { - return jsonld.nextTick(function() { - callback(new JsonLdError( - 'The compaction context must not be null.', - 'jsonld.CompactError', {code: 'invalid local context'})); - }); - } + // when the dest drains, it reduces the awaitDrain counter + // on the source. This would be more elegant with a .once() + // handler in flow(), but adding and removing repeatedly is + // too slow. + var ondrain = pipeOnDrain(src); + dest.on('drain', ondrain); - // nothing to compact - if(input === null) { - return jsonld.nextTick(function() { - callback(null, null); - }); - } + var cleanedUp = false; + function cleanup() { + debug('cleanup'); + // cleanup event handlers once the pipe is broken + dest.removeListener('close', onclose); + dest.removeListener('finish', onfinish); + dest.removeListener('drain', ondrain); + dest.removeListener('error', onerror); + dest.removeListener('unpipe', onunpipe); + src.removeListener('end', onend); + src.removeListener('end', unpipe); + src.removeListener('data', ondata); - // set default options - if(!('base' in options)) { - options.base = (typeof input === 'string') ? input : ''; + cleanedUp = true; + + // if the reader is waiting for a drain event from this + // specific writer, then it would cause it to never start + // flowing again. + // So, if this is awaiting a drain, then we just call it now. + // If we don't know, then assume that we are waiting for one. + if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain(); } - if(!('compactArrays' in options)) { - options.compactArrays = true; + + // If the user pushes more data while we're writing to dest then we'll end up + // in ondata again. However, we only want to increase awaitDrain once because + // dest will only emit one 'drain' event for the multiple writes. + // => Introduce a guard on increasing awaitDrain. + var increasedAwaitDrain = false; + src.on('data', ondata); + function ondata(chunk) { + debug('ondata'); + increasedAwaitDrain = false; + var ret = dest.write(chunk); + if (false === ret && !increasedAwaitDrain) { + // If the user unpiped during `dest.write()`, it is possible + // to get stuck in a permanently paused state if that write + // also returned false. + // => Check whether `dest` is still a piping destination. + if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) { + debug('false write response, pause', src._readableState.awaitDrain); + src._readableState.awaitDrain++; + increasedAwaitDrain = true; + } + src.pause(); + } } - if(!('graph' in options)) { - options.graph = false; + + // if the dest has an error, then stop piping into it. + // however, don't suppress the throwing behavior for this. + function onerror(er) { + debug('onerror', er); + unpipe(); + dest.removeListener('error', onerror); + if (EElistenerCount(dest, 'error') === 0) dest.emit('error', er); } - if(!('skipExpansion' in options)) { - options.skipExpansion = false; + + // Make sure our error handler is attached before userland ones. + prependListener(dest, 'error', onerror); + + // Both close and finish should trigger unpipe, but only once. + function onclose() { + dest.removeListener('finish', onfinish); + unpipe(); } - if(!('documentLoader' in options)) { - options.documentLoader = jsonld.loadDocument; + dest.once('close', onclose); + function onfinish() { + debug('onfinish'); + dest.removeListener('close', onclose); + unpipe(); } - if(!('link' in options)) { - options.link = false; + dest.once('finish', onfinish); + + function unpipe() { + debug('unpipe'); + src.unpipe(dest); } - if(options.link) { - // force skip expansion when linking, "link" is not part of the public - // API, it should only be called from framing - options.skipExpansion = true; + + // tell the dest that it's being piped to + dest.emit('pipe', src); + + // start the flow if it hasn't been started already. + if (!state.flowing) { + debug('pipe resume'); + src.resume(); } - var expand = function(input, options, callback) { - if(options.skipExpansion) { - return jsonld.nextTick(function() { - callback(null, input); - }); + return dest; +}; + +function pipeOnDrain(src) { + return function () { + var state = src._readableState; + debug('pipeOnDrain', state.awaitDrain); + if (state.awaitDrain) state.awaitDrain--; + if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) { + state.flowing = true; + flow(src); } - jsonld.expand(input, options, callback); }; +} - // expand input then do compaction - expand(input, options, function(err, expanded) { - if(err) { - return callback(new JsonLdError( - 'Could not expand input before compaction.', - 'jsonld.CompactError', {cause: err})); - } +Readable.prototype.unpipe = function (dest) { + var state = this._readableState; + var unpipeInfo = { hasUnpiped: false }; - // process context - var activeCtx = _getInitialContext(options); - jsonld.processContext(activeCtx, ctx, options, function(err, activeCtx) { - if(err) { - return callback(new JsonLdError( - 'Could not process context before compaction.', - 'jsonld.CompactError', {cause: err})); - } + // if we're not piping anywhere, then do nothing. + if (state.pipesCount === 0) return this; - var compacted; - try { - // do compaction - compacted = new Processor().compact(activeCtx, null, expanded, options); - } catch(ex) { - return callback(ex); - } + // just one destination. most common case. + if (state.pipesCount === 1) { + // passed in one, but it's not the right one. + if (dest && dest !== state.pipes) return this; - cleanup(null, compacted, activeCtx, options); - }); - }); + if (!dest) dest = state.pipes; - // performs clean up after compaction - function cleanup(err, compacted, activeCtx, options) { - if(err) { - return callback(err); - } + // got a match. + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; + if (dest) dest.emit('unpipe', this, unpipeInfo); + return this; + } - if(options.compactArrays && !options.graph && _isArray(compacted)) { - if(compacted.length === 1) { - // simplify to a single item - compacted = compacted[0]; - } else if(compacted.length === 0) { - // simplify to an empty object - compacted = {}; - } - } else if(options.graph && _isObject(compacted)) { - // always use array if graph option is on - compacted = [compacted]; - } + // slow case. multiple pipe destinations. - // follow @context key - if(_isObject(ctx) && '@context' in ctx) { - ctx = ctx['@context']; - } + if (!dest) { + // remove all. + var dests = state.pipes; + var len = state.pipesCount; + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; - // build output context - ctx = _clone(ctx); - if(!_isArray(ctx)) { - ctx = [ctx]; - } - // remove empty contexts - var tmp = ctx; - ctx = []; - for(var i = 0; i < tmp.length; ++i) { - if(!_isObject(tmp[i]) || Object.keys(tmp[i]).length > 0) { - ctx.push(tmp[i]); - } - } + for (var i = 0; i < len; i++) { + dests[i].emit('unpipe', this, unpipeInfo); + }return this; + } - // remove array if only one context - var hasContext = (ctx.length > 0); - if(ctx.length === 1) { - ctx = ctx[0]; - } + // try to find the right one. + var index = indexOf(state.pipes, dest); + if (index === -1) return this; - // add context and/or @graph - if(_isArray(compacted)) { - // use '@graph' keyword - var kwgraph = _compactIri(activeCtx, '@graph'); - var graph = compacted; - compacted = {}; - if(hasContext) { - compacted['@context'] = ctx; - } - compacted[kwgraph] = graph; - } else if(_isObject(compacted) && hasContext) { - // reorder keys so @context is first - var graph = compacted; - compacted = {'@context': ctx}; - for(var key in graph) { - compacted[key] = graph[key]; + state.pipes.splice(index, 1); + state.pipesCount -= 1; + if (state.pipesCount === 1) state.pipes = state.pipes[0]; + + dest.emit('unpipe', this, unpipeInfo); + + return this; +}; + +// set up data events if they are asked for +// Ensure readable listeners eventually get something +Readable.prototype.on = function (ev, fn) { + var res = Stream.prototype.on.call(this, ev, fn); + + if (ev === 'data') { + // Start flowing on next tick if stream isn't explicitly paused + if (this._readableState.flowing !== false) this.resume(); + } else if (ev === 'readable') { + var state = this._readableState; + if (!state.endEmitted && !state.readableListening) { + state.readableListening = state.needReadable = true; + state.emittedReadable = false; + if (!state.reading) { + pna.nextTick(nReadingNextTick, this); + } else if (state.length) { + emitReadable(this); } } - - callback(null, compacted, activeCtx); } + + return res; }; +Readable.prototype.addListener = Readable.prototype.on; -/** - * Performs JSON-LD expansion. - * - * @param input the JSON-LD input to expand. - * @param [options] the options to use: - * [base] the base IRI to use. - * [expandContext] a context to expand with. - * [keepFreeFloatingNodes] true to keep free-floating nodes, - * false not to, defaults to false. - * [documentLoader(url, callback(err, remoteDoc))] the document loader. - * @param callback(err, expanded) called once the operation completes. - */ -jsonld.expand = function(input, options, callback) { - if(arguments.length < 1) { - return jsonld.nextTick(function() { - callback(new TypeError('Could not expand, too few arguments.')); - }); +function nReadingNextTick(self) { + debug('readable nexttick read 0'); + self.read(0); +} + +// pause() and resume() are remnants of the legacy readable stream API +// If the user uses them, then switch into old mode. +Readable.prototype.resume = function () { + var state = this._readableState; + if (!state.flowing) { + debug('resume'); + state.flowing = true; + resume(this, state); } + return this; +}; - // get arguments - if(typeof options === 'function') { - callback = options; - options = {}; +function resume(stream, state) { + if (!state.resumeScheduled) { + state.resumeScheduled = true; + pna.nextTick(resume_, stream, state); } - options = options || {}; +} - // set default options - if(!('documentLoader' in options)) { - options.documentLoader = jsonld.loadDocument; +function resume_(stream, state) { + if (!state.reading) { + debug('resume read 0'); + stream.read(0); } - if(!('keepFreeFloatingNodes' in options)) { - options.keepFreeFloatingNodes = false; + + state.resumeScheduled = false; + state.awaitDrain = 0; + stream.emit('resume'); + flow(stream); + if (state.flowing && !state.reading) stream.read(0); +} + +Readable.prototype.pause = function () { + debug('call pause flowing=%j', this._readableState.flowing); + if (false !== this._readableState.flowing) { + debug('pause'); + this._readableState.flowing = false; + this.emit('pause'); } + return this; +}; - jsonld.nextTick(function() { - // if input is a string, attempt to dereference remote document - if(typeof input === 'string') { - var done = function(err, remoteDoc) { - if(err) { - return callback(err); - } - try { - if(!remoteDoc.document) { - throw new JsonLdError( - 'No remote document found at the given URL.', - 'jsonld.NullRemoteDocument'); - } - if(typeof remoteDoc.document === 'string') { - remoteDoc.document = JSON.parse(remoteDoc.document); - } - } catch(ex) { - return callback(new JsonLdError( - 'Could not retrieve a JSON-LD document from the URL. URL ' + - 'dereferencing not implemented.', 'jsonld.LoadDocumentError', { - code: 'loading document failed', - cause: ex, - remoteDoc: remoteDoc - })); - } - expand(remoteDoc); - }; - var promise = options.documentLoader(input, done); - if(promise && 'then' in promise) { - promise.then(done.bind(null, null), done); - } - return; +function flow(stream) { + var state = stream._readableState; + debug('flow', state.flowing); + while (state.flowing && stream.read() !== null) {} +} + +// wrap an old-style stream as the async data source. +// This is *not* part of the readable stream interface. +// It is an ugly unfortunate mess of history. +Readable.prototype.wrap = function (stream) { + var _this = this; + + var state = this._readableState; + var paused = false; + + stream.on('end', function () { + debug('wrapped end'); + if (state.decoder && !state.ended) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) _this.push(chunk); } - // nothing to load - expand({contextUrl: null, documentUrl: null, document: input}); + + _this.push(null); }); - function expand(remoteDoc) { - // set default base - if(!('base' in options)) { - options.base = remoteDoc.documentUrl || ''; - } - // build meta-object and retrieve all @context URLs - var input = { - document: _clone(remoteDoc.document), - remoteContext: {'@context': remoteDoc.contextUrl} - }; - if('expandContext' in options) { - var expandContext = _clone(options.expandContext); - if(typeof expandContext === 'object' && '@context' in expandContext) { - input.expandContext = expandContext; - } else { - input.expandContext = {'@context': expandContext}; - } - } - _retrieveContextUrls(input, options, function(err, input) { - if(err) { - return callback(err); - } + stream.on('data', function (chunk) { + debug('wrapped data'); + if (state.decoder) chunk = state.decoder.write(chunk); - var expanded; - try { - var processor = new Processor(); - var activeCtx = _getInitialContext(options); - var document = input.document; - var remoteContext = input.remoteContext['@context']; + // don't skip over falsy values in objectMode + if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return; - // process optional expandContext - if(input.expandContext) { - activeCtx = processor.processContext( - activeCtx, input.expandContext['@context'], options); - } + var ret = _this.push(chunk); + if (!ret) { + paused = true; + stream.pause(); + } + }); - // process remote context from HTTP Link Header - if(remoteContext) { - activeCtx = processor.processContext( - activeCtx, remoteContext, options); - } + // proxy all the other methods. + // important when wrapping filters and duplexes. + for (var i in stream) { + if (this[i] === undefined && typeof stream[i] === 'function') { + this[i] = function (method) { + return function () { + return stream[method].apply(stream, arguments); + }; + }(i); + } + } - // expand document - expanded = processor.expand( - activeCtx, null, document, options, false); + // proxy certain important events. + for (var n = 0; n < kProxyEvents.length; n++) { + stream.on(kProxyEvents[n], this.emit.bind(this, kProxyEvents[n])); + } - // optimize away @graph with no other properties - if(_isObject(expanded) && ('@graph' in expanded) && - Object.keys(expanded).length === 1) { - expanded = expanded['@graph']; - } else if(expanded === null) { - expanded = []; - } + // when we try to consume some more bytes, simply unpause the + // underlying stream. + this._read = function (n) { + debug('wrapped _read', n); + if (paused) { + paused = false; + stream.resume(); + } + }; - // normalize to an array - if(!_isArray(expanded)) { - expanded = [expanded]; - } - } catch(ex) { - return callback(ex); - } - callback(null, expanded); - }); - } + return this; }; -/** - * Performs JSON-LD flattening. - * - * @param input the JSON-LD to flatten. - * @param ctx the context to use to compact the flattened output, or null. - * @param [options] the options to use: - * [base] the base IRI to use. - * [expandContext] a context to expand with. - * [documentLoader(url, callback(err, remoteDoc))] the document loader. - * @param callback(err, flattened) called once the operation completes. - */ -jsonld.flatten = function(input, ctx, options, callback) { - if(arguments.length < 1) { - return jsonld.nextTick(function() { - callback(new TypeError('Could not flatten, too few arguments.')); - }); +Object.defineProperty(Readable.prototype, 'readableHighWaterMark', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function () { + return this._readableState.highWaterMark; } +}); - // get arguments - if(typeof options === 'function') { - callback = options; - options = {}; - } else if(typeof ctx === 'function') { - callback = ctx; - ctx = null; - options = {}; - } - options = options || {}; +// exposed for testing purposes only. +Readable._fromList = fromList; - // set default options - if(!('base' in options)) { - options.base = (typeof input === 'string') ? input : ''; +// Pluck off n bytes from an array of buffers. +// Length is the combined lengths of all the buffers in the list. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function fromList(n, state) { + // nothing buffered + if (state.length === 0) return null; + + var ret; + if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) { + // read it all, truncate the list + if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.head.data;else ret = state.buffer.concat(state.length); + state.buffer.clear(); + } else { + // read part of list + ret = fromListPartial(n, state.buffer, state.decoder); } - if(!('documentLoader' in options)) { - options.documentLoader = jsonld.loadDocument; + + return ret; +} + +// Extracts only enough buffered data to satisfy the amount requested. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function fromListPartial(n, list, hasStrings) { + var ret; + if (n < list.head.data.length) { + // slice is the same for buffers and strings + ret = list.head.data.slice(0, n); + list.head.data = list.head.data.slice(n); + } else if (n === list.head.data.length) { + // first chunk is a perfect match + ret = list.shift(); + } else { + // result spans more than one buffer + ret = hasStrings ? copyFromBufferString(n, list) : copyFromBuffer(n, list); } + return ret; +} - // expand input - jsonld.expand(input, options, function(err, _input) { - if(err) { - return callback(new JsonLdError( - 'Could not expand input before flattening.', - 'jsonld.FlattenError', {cause: err})); +// Copies a specified amount of characters from the list of buffered data +// chunks. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function copyFromBufferString(n, list) { + var p = list.head; + var c = 1; + var ret = p.data; + n -= ret.length; + while (p = p.next) { + var str = p.data; + var nb = n > str.length ? str.length : n; + if (nb === str.length) ret += str;else ret += str.slice(0, n); + n -= nb; + if (n === 0) { + if (nb === str.length) { + ++c; + if (p.next) list.head = p.next;else list.head = list.tail = null; + } else { + list.head = p; + p.data = str.slice(nb); + } + break; } + ++c; + } + list.length -= c; + return ret; +} - var flattened; - try { - // do flattening - flattened = new Processor().flatten(_input); - } catch(ex) { - return callback(ex); +// Copies a specified amount of bytes from the list of buffered data chunks. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function copyFromBuffer(n, list) { + var ret = Buffer.allocUnsafe(n); + var p = list.head; + var c = 1; + p.data.copy(ret); + n -= p.data.length; + while (p = p.next) { + var buf = p.data; + var nb = n > buf.length ? buf.length : n; + buf.copy(ret, ret.length - n, 0, nb); + n -= nb; + if (n === 0) { + if (nb === buf.length) { + ++c; + if (p.next) list.head = p.next;else list.head = list.tail = null; + } else { + list.head = p; + p.data = buf.slice(nb); + } + break; } + ++c; + } + list.length -= c; + return ret; +} - if(ctx === null) { - return callback(null, flattened); - } +function endReadable(stream) { + var state = stream._readableState; - // compact result (force @graph option to true, skip expansion) - options.graph = true; - options.skipExpansion = true; - jsonld.compact(flattened, ctx, options, function(err, compacted) { - if(err) { - return callback(new JsonLdError( - 'Could not compact flattened output.', - 'jsonld.FlattenError', {cause: err})); - } - callback(null, compacted); - }); - }); -}; + // If we get here before consuming all the bytes, then that is a + // bug in node. Should never happen. + if (state.length > 0) throw new Error('"endReadable()" called on non-empty stream'); -/** - * Performs JSON-LD framing. - * - * @param input the JSON-LD input to frame. - * @param frame the JSON-LD frame to use. - * @param [options] the framing options. - * [base] the base IRI to use. - * [expandContext] a context to expand with. - * [embed] default @embed flag: '@last', '@always', '@never', '@link' - * (default: '@last'). - * [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. - */ -jsonld.frame = function(input, frame, options, callback) { - if(arguments.length < 2) { - return jsonld.nextTick(function() { - callback(new TypeError('Could not frame, too few arguments.')); - }); + if (!state.endEmitted) { + state.ended = true; + pna.nextTick(endReadableNT, state, stream); } +} - // get arguments - if(typeof options === 'function') { - callback = options; - options = {}; +function endReadableNT(state, stream) { + // Check that we didn't get one last unshift. + if (!state.endEmitted && state.length === 0) { + state.endEmitted = true; + stream.readable = false; + stream.emit('end'); } - options = options || {}; +} - // set default options - if(!('base' in options)) { - options.base = (typeof input === 'string') ? input : ''; - } - if(!('documentLoader' in options)) { - options.documentLoader = jsonld.loadDocument; - } - if(!('embed' in options)) { - options.embed = '@last'; - } - options.explicit = options.explicit || false; - if(!('requireAll' in options)) { - options.requireAll = true; +function indexOf(xs, x) { + for (var i = 0, l = xs.length; i < l; i++) { + if (xs[i] === x) return i; } - options.omitDefault = options.omitDefault || false; + return -1; +} +}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - jsonld.nextTick(function() { - // if frame is a string, attempt to dereference remote document - if(typeof frame === 'string') { - var done = function(err, remoteDoc) { - if(err) { - return callback(err); - } - try { - if(!remoteDoc.document) { - throw new JsonLdError( - 'No remote document found at the given URL.', - 'jsonld.NullRemoteDocument'); - } - if(typeof remoteDoc.document === 'string') { - remoteDoc.document = JSON.parse(remoteDoc.document); - } - } catch(ex) { - return callback(new JsonLdError( - 'Could not retrieve a JSON-LD document from the URL. URL ' + - 'dereferencing not implemented.', 'jsonld.LoadDocumentError', { - code: 'loading document failed', - cause: ex, - remoteDoc: remoteDoc - })); - } - doFrame(remoteDoc); - }; - var promise = options.documentLoader(frame, done); - if(promise && 'then' in promise) { - promise.then(done.bind(null, null), done); - } - return; - } - // nothing to load - doFrame({contextUrl: null, documentUrl: null, document: frame}); - }); +},{"./_stream_duplex":12,"./internal/streams/BufferList":17,"./internal/streams/destroy":18,"./internal/streams/stream":19,"_process":74,"core-util-is":38,"events":8,"inherits":43,"isarray":20,"process-nextick-args":73,"safe-buffer":26,"string_decoder/":21,"util":2}],15:[function(_dereq_,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. - function doFrame(remoteFrame) { - // preserve frame context and add any Link header context - var frame = remoteFrame.document; - var ctx; - if(frame) { - ctx = frame['@context']; - if(remoteFrame.contextUrl) { - if(!ctx) { - ctx = remoteFrame.contextUrl; - } else if(_isArray(ctx)) { - ctx.push(remoteFrame.contextUrl); - } else { - ctx = [ctx, remoteFrame.contextUrl]; - } - frame['@context'] = ctx; - } else { - ctx = ctx || {}; - } - } else { - ctx = {}; - } +// a transform stream is a readable/writable stream where you do +// something with the data. Sometimes it's called a "filter", +// but that's not a great name for it, since that implies a thing where +// some bits pass through, and others are simply ignored. (That would +// be a valid example of a transform, of course.) +// +// While the output is causally related to the input, it's not a +// necessarily symmetric or synchronous transformation. For example, +// a zlib stream might take multiple plain-text writes(), and then +// emit a single compressed chunk some time in the future. +// +// Here's how this works: +// +// The Transform stream has all the aspects of the readable and writable +// stream classes. When you write(chunk), that calls _write(chunk,cb) +// internally, and returns false if there's a lot of pending writes +// buffered up. When you call read(), that calls _read(n) until +// there's enough pending readable data buffered up. +// +// In a transform stream, the written data is placed in a buffer. When +// _read(n) is called, it transforms the queued up data, calling the +// buffered _write cb's as it consumes chunks. If consuming a single +// written chunk would result in multiple output chunks, then the first +// outputted bit calls the readcb, and subsequent chunks just go into +// the read buffer, and will cause it to emit 'readable' if necessary. +// +// This way, back-pressure is actually determined by the reading side, +// since _read has to be called to start processing a new chunk. However, +// a pathological inflate type of transform can cause excessive buffering +// here. For example, imagine a stream where every byte of input is +// interpreted as an integer from 0-255, and then results in that many +// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in +// 1kb of data being output. In this case, you could write a very small +// amount of input, and end up with a very large amount of output. In +// such a pathological inflating mechanism, there'd be no way to tell +// the system to stop doing the transform. A single 4MB write could +// cause the system to run out of memory. +// +// However, even in such a pathological case, only a single written chunk +// would be consumed, and then the rest would wait (un-transformed) until +// the results of the previous transformed chunk were consumed. - // expand input - jsonld.expand(input, options, function(err, expanded) { - if(err) { - return callback(new JsonLdError( - 'Could not expand input before framing.', - 'jsonld.FrameError', {cause: err})); - } +'use strict'; - // expand frame - var opts = _clone(options); - opts.isFrame = true; - opts.keepFreeFloatingNodes = true; - jsonld.expand(frame, opts, function(err, expandedFrame) { - if(err) { - return callback(new JsonLdError( - 'Could not expand frame before framing.', - 'jsonld.FrameError', {cause: err})); - } +module.exports = Transform; - var framed; - try { - // do framing - framed = new Processor().frame(expanded, expandedFrame, opts); - } catch(ex) { - return callback(ex); - } +var Duplex = _dereq_('./_stream_duplex'); - // compact result (force @graph option to true, skip expansion, - // check for linked embeds) - opts.graph = true; - opts.skipExpansion = true; - opts.link = {}; - jsonld.compact(framed, ctx, opts, function(err, compacted, ctx) { - if(err) { - return callback(new JsonLdError( - 'Could not compact framed output.', - 'jsonld.FrameError', {cause: err})); - } - // get graph alias - var graph = _compactIri(ctx, '@graph'); - // remove @preserve from results - opts.link = {}; - compacted[graph] = _removePreserve(ctx, compacted[graph], opts); - callback(null, compacted); - }); - }); - }); - } -}; +/**/ +var util = Object.create(_dereq_('core-util-is')); +util.inherits = _dereq_('inherits'); +/**/ -/** - * **Experimental** - * - * Links a JSON-LD document's nodes in memory. - * - * @param input the JSON-LD document to link. - * @param ctx the JSON-LD context to apply. - * @param [options] the options to use: - * [base] the base IRI to use. - * [expandContext] a context to expand with. - * [documentLoader(url, callback(err, remoteDoc))] the document loader. - * @param callback(err, linked) called once the operation completes. - */ -jsonld.link = function(input, ctx, options, callback) { - // API matches running frame with a wildcard frame and embed: '@link' - // get arguments - var frame = {}; - if(ctx) { - frame['@context'] = ctx; +util.inherits(Transform, Duplex); + +function afterTransform(er, data) { + var ts = this._transformState; + ts.transforming = false; + + var cb = ts.writecb; + + if (!cb) { + return this.emit('error', new Error('write callback called multiple times')); } - frame['@embed'] = '@link'; - jsonld.frame(input, frame, options, callback); -}; -/** - * **Deprecated** - * - * Performs JSON-LD objectification. - * - * @param input the JSON-LD document to objectify. - * @param ctx the JSON-LD context to apply. - * @param [options] the options to use: - * [base] the base IRI to use. - * [expandContext] a context to expand with. - * [documentLoader(url, callback(err, remoteDoc))] the document loader. - * @param callback(err, linked) called once the operation completes. - */ -jsonld.objectify = function(input, ctx, options, callback) { - if(typeof options === 'function') { - callback = options; - options = {}; - } - options = options || {}; - - // set default options - if(!('base' in options)) { - options.base = (typeof input === 'string') ? input : ''; - } - if(!('documentLoader' in options)) { - options.documentLoader = jsonld.loadDocument; - } + ts.writechunk = null; + ts.writecb = null; - // expand input - jsonld.expand(input, options, function(err, _input) { - if(err) { - return callback(new JsonLdError( - 'Could not expand input before linking.', - 'jsonld.LinkError', {cause: err})); - } + if (data != null) // single equals check for both `null` and `undefined` + this.push(data); - var flattened; - try { - // flatten the graph - flattened = new Processor().flatten(_input); - } catch(ex) { - return callback(ex); - } + cb(er); - // compact result (force @graph option to true, skip expansion) - options.graph = true; - options.skipExpansion = true; - jsonld.compact(flattened, ctx, options, function(err, compacted, ctx) { - if(err) { - return callback(new JsonLdError( - 'Could not compact flattened output before linking.', - 'jsonld.LinkError', {cause: err})); - } - // get graph alias - var graph = _compactIri(ctx, '@graph'); - var top = compacted[graph][0]; + var rs = this._readableState; + rs.reading = false; + if (rs.needReadable || rs.length < rs.highWaterMark) { + this._read(rs.highWaterMark); + } +} - var recurse = function(subject) { - // can't replace just a string - if(!_isObject(subject) && !_isArray(subject)) { - return; - } +function Transform(options) { + if (!(this instanceof Transform)) return new Transform(options); - // bottom out recursion on re-visit - if(_isObject(subject)) { - if(recurse.visited[subject['@id']]) { - return; - } - recurse.visited[subject['@id']] = true; - } + Duplex.call(this, options); - // each array element *or* object key - for(var k in subject) { - var obj = subject[k]; - var isid = (jsonld.getContextValue(ctx, k, '@type') === '@id'); + this._transformState = { + afterTransform: afterTransform.bind(this), + needTransform: false, + transforming: false, + writecb: null, + writechunk: null, + writeencoding: null + }; - // can't replace a non-object or non-array unless it's an @id - if(!_isArray(obj) && !_isObject(obj) && !isid) { - continue; - } + // start out asking for a readable event once data is transformed. + this._readableState.needReadable = true; - if(_isString(obj) && isid) { - subject[k] = obj = top[obj]; - recurse(obj); - } else if(_isArray(obj)) { - for(var i = 0; i < obj.length; ++i) { - if(_isString(obj[i]) && isid) { - obj[i] = top[obj[i]]; - } else if(_isObject(obj[i]) && '@id' in obj[i]) { - obj[i] = top[obj[i]['@id']]; - } - recurse(obj[i]); - } - } else if(_isObject(obj)) { - var sid = obj['@id']; - subject[k] = obj = top[sid]; - recurse(obj); - } - } - }; - recurse.visited = {}; - recurse(top); + // we have implemented the _read method, and done the other things + // that Readable wants before the first _read call, so unset the + // sync guard flag. + this._readableState.sync = false; - compacted.of_type = {}; - for(var s in top) { - if(!('@type' in top[s])) { - continue; - } - var types = top[s]['@type']; - if(!_isArray(types)) { - types = [types]; - } - for(var t = 0; t < types.length; ++t) { - if(!(types[t] in compacted.of_type)) { - compacted.of_type[types[t]] = []; - } - compacted.of_type[types[t]].push(top[s]); - } - } - callback(null, compacted); - }); - }); -}; + if (options) { + if (typeof options.transform === 'function') this._transform = options.transform; -/** - * Performs RDF dataset normalization on the given input. The input is JSON-LD - * unless the 'inputFormat' option is used. The output is an RDF dataset - * unless the 'format' option is used. - * - * @param input the input to normalize as JSON-LD or as a format specified by - * the 'inputFormat' option. - * @param [options] the options to use: - * [algorithm] the normalization algorithm to use, `URDNA2015` or - * `URGNA2012` (default: `URGNA2012`). - * [base] the base IRI to use. - * [expandContext] a context to expand with. - * [inputFormat] the format if input is not JSON-LD: - * 'application/nquads' for N-Quads. - * [format] the format if output is a string: - * 'application/nquads' for N-Quads. - * [documentLoader(url, callback(err, remoteDoc))] the document loader. - * @param callback(err, normalized) called once the operation completes. - */ -jsonld.normalize = function(input, options, callback) { - if(arguments.length < 1) { - return jsonld.nextTick(function() { - callback(new TypeError('Could not normalize, too few arguments.')); - }); + if (typeof options.flush === 'function') this._flush = options.flush; } - // get arguments - if(typeof options === 'function') { - callback = options; - options = {}; - } - options = options || {}; + // When the writable side finishes, then flush out anything remaining. + this.on('prefinish', prefinish); +} - // set default options - if(!('algorithm' in options)) { - options.algorithm = 'URGNA2012'; - } - if(!('base' in options)) { - options.base = (typeof input === 'string') ? input : ''; - } - if(!('documentLoader' in options)) { - options.documentLoader = jsonld.loadDocument; - } +function prefinish() { + var _this = this; - if('inputFormat' in options) { - if(options.inputFormat !== 'application/nquads') { - return callback(new JsonLdError( - 'Unknown normalization input format.', - 'jsonld.NormalizeError')); - } - var parsedInput = _parseNQuads(input); - // do normalization - new Processor().normalize(parsedInput, options, callback); - } else { - // convert to RDF dataset then do normalization - var opts = _clone(options); - delete opts.format; - opts.produceGeneralizedRdf = false; - jsonld.toRDF(input, opts, function(err, dataset) { - if(err) { - return callback(new JsonLdError( - 'Could not convert input to RDF dataset before normalization.', - 'jsonld.NormalizeError', {cause: err})); - } - // do normalization - new Processor().normalize(dataset, options, callback); + if (typeof this._flush === 'function') { + this._flush(function (er, data) { + done(_this, er, data); }); + } else { + done(this, null, null); } +} + +Transform.prototype.push = function (chunk, encoding) { + this._transformState.needTransform = false; + return Duplex.prototype.push.call(this, chunk, encoding); }; -/** - * Converts an RDF dataset to JSON-LD. - * - * @param dataset a serialized string of RDF in a format specified by the - * format option or an RDF dataset to convert. - * @param [options] the options to use: - * [format] the format if dataset param must first be parsed: - * 'application/nquads' for N-Quads (default). - * [rdfParser] a custom RDF-parser to use to parse the dataset. - * [useRdfType] true to use rdf:type, false to use @type - * (default: false). - * [useNativeTypes] true to convert XSD types into native types - * (boolean, integer, double), false not to (default: false). - * @param callback(err, output) called once the operation completes. - */ -jsonld.fromRDF = function(dataset, options, callback) { - if(arguments.length < 1) { - return jsonld.nextTick(function() { - callback(new TypeError('Could not convert from RDF, too few arguments.')); - }); - } +// This is the part where you do stuff! +// override this function in implementation classes. +// 'chunk' is an input chunk. +// +// Call `push(newChunk)` to pass along transformed output +// to the readable side. You may call 'push' zero or more times. +// +// Call `cb(err)` when you are done with this chunk. If you pass +// an error, then that'll put the hurt on the whole operation. If you +// never call cb(), then you'll never get another chunk. +Transform.prototype._transform = function (chunk, encoding, cb) { + throw new Error('_transform() is not implemented'); +}; - // get arguments - if(typeof options === 'function') { - callback = options; - options = {}; +Transform.prototype._write = function (chunk, encoding, cb) { + var ts = this._transformState; + ts.writecb = cb; + ts.writechunk = chunk; + ts.writeencoding = encoding; + if (!ts.transforming) { + var rs = this._readableState; + if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark); } - options = options || {}; +}; - // set default options - if(!('useRdfType' in options)) { - options.useRdfType = false; - } - if(!('useNativeTypes' in options)) { - options.useNativeTypes = false; - } +// Doesn't matter what the args are here. +// _transform does all the work. +// That we got here means that the readable side wants more data. +Transform.prototype._read = function (n) { + var ts = this._transformState; - if(!('format' in options) && _isString(dataset)) { - // set default format to nquads - if(!('format' in options)) { - options.format = 'application/nquads'; - } + if (ts.writechunk !== null && ts.writecb && !ts.transforming) { + ts.transforming = true; + this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); + } else { + // mark that we need a transform, so that any data that comes in + // will get processed, now that we've asked for it. + ts.needTransform = true; } +}; - jsonld.nextTick(function() { - // handle special format - var rdfParser; - if(options.format) { - // check supported formats - rdfParser = options.rdfParser || _rdfParsers[options.format]; - if(!rdfParser) { - return callback(new JsonLdError( - 'Unknown input format.', - 'jsonld.UnknownFormat', {format: options.format})); - } - } else { - // no-op parser, assume dataset already parsed - rdfParser = function() { - return dataset; - }; - } - - var callbackCalled = false; - try { - // rdf parser may be async or sync, always pass callback - dataset = rdfParser(dataset, function(err, dataset) { - callbackCalled = true; - if(err) { - return callback(err); - } - fromRDF(dataset, options, callback); - }); - } catch(e) { - if(!callbackCalled) { - return callback(e); - } - throw e; - } - // handle synchronous or promise-based parser - if(dataset) { - // if dataset is actually a promise - if('then' in dataset) { - return dataset.then(function(dataset) { - fromRDF(dataset, options, callback); - }, callback); - } - // parser is synchronous - fromRDF(dataset, options, callback); - } +Transform.prototype._destroy = function (err, cb) { + var _this2 = this; - function fromRDF(dataset, options, callback) { - // convert from RDF - new Processor().fromRDF(dataset, options, callback); - } + Duplex.prototype._destroy.call(this, err, function (err2) { + cb(err2); + _this2.emit('close'); }); }; -/** - * Outputs the RDF dataset found in the given JSON-LD object. - * - * @param input the JSON-LD input. - * @param [options] the options to use: - * [base] the base IRI to use. - * [expandContext] a context to expand with. - * [format] the format to use to output a string: - * 'application/nquads' for N-Quads. - * [produceGeneralizedRdf] true to output generalized RDF, false - * to produce only standard RDF (default: false). - * [documentLoader(url, callback(err, remoteDoc))] the document loader. - * @param callback(err, dataset) called once the operation completes. - */ -jsonld.toRDF = function(input, options, callback) { - if(arguments.length < 1) { - return jsonld.nextTick(function() { - callback(new TypeError('Could not convert to RDF, too few arguments.')); - }); - } +function done(stream, er, data) { + if (er) return stream.emit('error', er); - // get arguments - if(typeof options === 'function') { - callback = options; - options = {}; - } - options = options || {}; + if (data != null) // single equals check for both `null` and `undefined` + stream.push(data); - // set default options - if(!('base' in options)) { - options.base = (typeof input === 'string') ? input : ''; - } - if(!('documentLoader' in options)) { - options.documentLoader = jsonld.loadDocument; - } + // if there's nothing in the write buffer, then that means + // that nothing more will ever be provided + if (stream._writableState.length) throw new Error('Calling transform done when ws.length != 0'); - // expand input - jsonld.expand(input, options, function(err, expanded) { - if(err) { - return callback(new JsonLdError( - 'Could not expand input before serialization to RDF.', - 'jsonld.RdfError', {cause: err})); - } + if (stream._transformState.transforming) throw new Error('Calling transform done when still transforming'); - var dataset; - try { - // output RDF dataset - dataset = Processor.prototype.toRDF(expanded, options); - if(options.format) { - if(options.format === 'application/nquads') { - return callback(null, _toNQuads(dataset)); - } - throw new JsonLdError( - 'Unknown output format.', - 'jsonld.UnknownFormat', {format: options.format}); - } - } catch(ex) { - return callback(ex); - } - callback(null, dataset); - }); -}; + return stream.push(null); +} +},{"./_stream_duplex":12,"core-util-is":38,"inherits":43}],16:[function(_dereq_,module,exports){ +(function (process,global){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. -/** - * **Experimental** - * - * Recursively flattens the nodes in the given JSON-LD input into a map of - * node ID => node. - * - * @param input the JSON-LD input. - * @param [options] the options to use: - * [base] the base IRI to use. - * [expandContext] a context to expand with. - * [issuer] a jsonld.IdentifierIssuer to use to label blank nodes. - * [namer] (deprecated) - * [documentLoader(url, callback(err, remoteDoc))] the document loader. - * @param callback(err, nodeMap) called once the operation completes. - */ -jsonld.createNodeMap = function(input, options, callback) { - if(arguments.length < 1) { - return jsonld.nextTick(function() { - callback(new TypeError('Could not create node map, too few arguments.')); - }); - } +// A bit simpler than readable streams. +// Implement an async ._write(chunk, encoding, cb), and it'll handle all +// the drain event emission and buffering. - // get arguments - if(typeof options === 'function') { - callback = options; - options = {}; - } - options = options || {}; +'use strict'; - // set default options - if(!('base' in options)) { - options.base = (typeof input === 'string') ? input : ''; - } - if(!('documentLoader' in options)) { - options.documentLoader = jsonld.loadDocument; - } +/**/ - // expand input - jsonld.expand(input, options, function(err, _input) { - if(err) { - return callback(new JsonLdError( - 'Could not expand input before creating node map.', - 'jsonld.CreateNodeMapError', {cause: err})); - } +var pna = _dereq_('process-nextick-args'); +/**/ - var nodeMap; - try { - nodeMap = new Processor().createNodeMap(_input, options); - } catch(ex) { - return callback(ex); - } +module.exports = Writable; - callback(null, nodeMap); - }); -}; +/* */ +function WriteReq(chunk, encoding, cb) { + this.chunk = chunk; + this.encoding = encoding; + this.callback = cb; + this.next = null; +} -/** - * **Experimental** - * - * Merges two or more JSON-LD documents into a single flattened document. - * - * @param docs the JSON-LD documents to merge together. - * @param ctx the context to use to compact the merged result, or null. - * @param [options] the options to use: - * [base] the base IRI to use. - * [expandContext] a context to expand with. - * [issuer] a jsonld.IdentifierIssuer to use to label blank nodes. - * [namer] (deprecated). - * [mergeNodes] true to merge properties for nodes with the same ID, - * false to ignore new properties for nodes with the same ID once - * the ID has been defined; note that this may not prevent merging - * new properties where a node is in the `object` position - * (default: true). - * [documentLoader(url, callback(err, remoteDoc))] the document loader. - * @param callback(err, merged) called once the operation completes. - */ -jsonld.merge = function(docs, ctx, options, callback) { - if(arguments.length < 1) { - return jsonld.nextTick(function() { - callback(new TypeError('Could not merge, too few arguments.')); - }); - } - if(!_isArray(docs)) { - return jsonld.nextTick(function() { - callback(new TypeError('Could not merge, "docs" must be an array.')); - }); - } +// It seems a linked list but it is not +// there will be only 2 of these for each stream +function CorkedRequest(state) { + var _this = this; - // get arguments - if(typeof options === 'function') { - callback = options; - options = {}; - } else if(typeof ctx === 'function') { - callback = ctx; - ctx = null; - options = {}; - } - options = options || {}; + this.next = null; + this.entry = null; + this.finish = function () { + onCorkedFinish(_this, state); + }; +} +/* */ - // expand all documents - var expanded = []; - var error = null; - var count = docs.length; - for(var i = 0; i < docs.length; ++i) { - var opts = {}; - for(var key in options) { - opts[key] = options[key]; - } - jsonld.expand(docs[i], opts, expandComplete); - } +/**/ +var asyncWrite = !process.browser && ['v0.10', 'v0.9.'].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : pna.nextTick; +/**/ - function expandComplete(err, _input) { - if(error) { - return; - } - if(err) { - error = err; - return callback(new JsonLdError( - 'Could not expand input before flattening.', - 'jsonld.FlattenError', {cause: err})); - } - expanded.push(_input); - if(--count === 0) { - merge(expanded); - } - } +/**/ +var Duplex; +/**/ - function merge(expanded) { - var mergeNodes = true; - if('mergeNodes' in options) { - mergeNodes = options.mergeNodes; - } +Writable.WritableState = WritableState; - var issuer = options.namer || options.issuer || new IdentifierIssuer('_:b'); - var graphs = {'@default': {}}; +/**/ +var util = Object.create(_dereq_('core-util-is')); +util.inherits = _dereq_('inherits'); +/**/ - var defaultGraph; - try { - for(var i = 0; i < expanded.length; ++i) { - // uniquely relabel blank nodes - var doc = expanded[i]; - doc = jsonld.relabelBlankNodes(doc, { - issuer: new IdentifierIssuer('_:b' + i + '-') - }); +/**/ +var internalUtil = { + deprecate: _dereq_('util-deprecate') +}; +/**/ - // add nodes to the shared node map graphs if merging nodes, to a - // separate graph set if not - var _graphs = (mergeNodes || i === 0) ? graphs : {'@default': {}}; - _createNodeMap(doc, _graphs, '@default', issuer); +/**/ +var Stream = _dereq_('./internal/streams/stream'); +/**/ - if(_graphs !== graphs) { - // merge document graphs but don't merge existing nodes - for(var graphName in _graphs) { - var _nodeMap = _graphs[graphName]; - if(!(graphName in graphs)) { - graphs[graphName] = _nodeMap; - continue; - } - var nodeMap = graphs[graphName]; - for(var key in _nodeMap) { - if(!(key in nodeMap)) { - nodeMap[key] = _nodeMap[key]; - } - } - } - } - } +/**/ - // add all non-default graphs to default graph - defaultGraph = _mergeNodeMaps(graphs); - } catch(ex) { - return callback(ex); - } +var Buffer = _dereq_('safe-buffer').Buffer; +var OurUint8Array = global.Uint8Array || function () {}; +function _uint8ArrayToBuffer(chunk) { + return Buffer.from(chunk); +} +function _isUint8Array(obj) { + return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; +} - // produce flattened output - var flattened = []; - var keys = Object.keys(defaultGraph).sort(); - for(var ki = 0; ki < keys.length; ++ki) { - var node = defaultGraph[keys[ki]]; - // only add full subjects to top-level - if(!_isSubjectReference(node)) { - flattened.push(node); - } - } +/**/ - if(ctx === null) { - return callback(null, flattened); - } +var destroyImpl = _dereq_('./internal/streams/destroy'); - // compact result (force @graph option to true, skip expansion) - options.graph = true; - options.skipExpansion = true; - jsonld.compact(flattened, ctx, options, function(err, compacted) { - if(err) { - return callback(new JsonLdError( - 'Could not compact merged output.', - 'jsonld.MergeError', {cause: err})); - } - callback(null, compacted); - }); - } -}; +util.inherits(Writable, Stream); + +function nop() {} + +function WritableState(options, stream) { + Duplex = Duplex || _dereq_('./_stream_duplex'); -/** - * Relabels all blank nodes in the given JSON-LD input. - * - * @param input the JSON-LD input. - * @param [options] the options to use: - * [issuer] a jsonld.IdentifierIssuer to use to label blank nodes. - * [namer] (deprecated). - */ -jsonld.relabelBlankNodes = function(input, options) { options = options || {}; - var issuer = options.namer || options.issuer || new IdentifierIssuer('_:b'); - return _labelBlankNodes(issuer, input); -}; -/** - * Prepends a base IRI to the given relative IRI. - * - * @param base the base IRI. - * @param iri the relative IRI. - * - * @return the absolute IRI. - */ -jsonld.prependBase = function(base, iri) { - return _prependBase(base, iri); -}; + // Duplex streams are both readable and writable, but share + // the same options object. + // However, some cases require setting options to different + // values for the readable and the writable sides of the duplex stream. + // These options can be provided separately as readableXXX and writableXXX. + var isDuplex = stream instanceof Duplex; -/** - * The default document loader for external documents. If the environment - * is node.js, a callback-continuation-style document loader is used; otherwise, - * a promises-style document loader is used. - * - * @param url the URL to load. - * @param callback(err, remoteDoc) called once the operation completes, - * if using a non-promises API. - * - * @return a promise, if using a promises API. - */ -jsonld.documentLoader = function(url, callback) { - var err = new JsonLdError( - 'Could not retrieve a JSON-LD document from the URL. URL ' + - 'dereferencing not implemented.', 'jsonld.LoadDocumentError', - {code: 'loading document failed'}); - if(_nodejs) { - return callback(err, {contextUrl: null, documentUrl: url, document: null}); - } - return jsonld.promisify(function(callback) { - callback(err); - }); -}; + // object stream flag to indicate whether or not this stream + // contains buffers or objects. + this.objectMode = !!options.objectMode; -/** - * Deprecated default document loader. Use or override jsonld.documentLoader - * instead. - */ -jsonld.loadDocument = function(url, callback) { - var promise = jsonld.documentLoader(url, callback); - if(promise && 'then' in promise) { - promise.then(callback.bind(null, null), callback); - } -}; + if (isDuplex) this.objectMode = this.objectMode || !!options.writableObjectMode; -/* Promises API */ + // the point at which write() starts returning false + // Note: 0 is a valid value, means that we always return false if + // the entire buffer is not flushed immediately on write() + var hwm = options.highWaterMark; + var writableHwm = options.writableHighWaterMark; + var defaultHwm = this.objectMode ? 16 : 16 * 1024; -/** - * Creates a new promises API object. - * - * @param [options] the options to use: - * [api] an object to attach the API to. - * [version] 'json-ld-1.0' to output a standard JSON-LD 1.0 promises - * API, 'jsonld.js' to output the same with augmented proprietary - * methods (default: 'jsonld.js') - * - * @return the promises API object. - */ -jsonld.promises = function(options) { - options = options || {}; - var slice = Array.prototype.slice; - var promisify = jsonld.promisify; + if (hwm || hwm === 0) this.highWaterMark = hwm;else if (isDuplex && (writableHwm || writableHwm === 0)) this.highWaterMark = writableHwm;else this.highWaterMark = defaultHwm; - // handle 'api' option as version, set defaults - var api = options.api || {}; - var version = options.version || 'jsonld.js'; - if(typeof options.api === 'string') { - if(!options.version) { - version = options.api; - } - api = {}; - } + // cast to ints. + this.highWaterMark = Math.floor(this.highWaterMark); - // The Web IDL test harness will check the number of parameters defined in - // the functions below. The number of parameters must exactly match the - // required (non-optional) parameters of the JsonLdProcessor interface as - // defined here: - // https://www.w3.org/TR/json-ld-api/#the-jsonldprocessor-interface + // if _final has been called + this.finalCalled = false; - api.expand = function(input) { - if(arguments.length < 1) { - throw new TypeError('Could not expand, too few arguments.'); - } - return promisify.apply(null, [jsonld.expand].concat(slice.call(arguments))); - }; - api.compact = function(input, ctx) { - if(arguments.length < 2) { - throw new TypeError('Could not compact, too few arguments.'); - } - var compact = function(input, ctx, options, callback) { - if(typeof options === 'function') { - callback = options; - options = {}; - } - options = options || {}; - // ensure only one value is returned in callback - jsonld.compact(input, ctx, options, function(err, compacted) { - callback(err, compacted); - }); - }; - return promisify.apply(null, [compact].concat(slice.call(arguments))); - }; - api.flatten = function(input) { - if(arguments.length < 1) { - throw new TypeError('Could not flatten, too few arguments.'); - } - return promisify.apply( - null, [jsonld.flatten].concat(slice.call(arguments))); - }; - api.frame = function(input, frame) { - if(arguments.length < 2) { - throw new TypeError('Could not frame, too few 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 promisify.apply( - null, [jsonld.fromRDF].concat(slice.call(arguments))); - }; - api.toRDF = function(input) { - if(arguments.length < 1) { - throw new TypeError('Could not convert to RDF, too few arguments.'); - } - return promisify.apply(null, [jsonld.toRDF].concat(slice.call(arguments))); - }; - api.normalize = function(input) { - if(arguments.length < 1) { - throw new TypeError('Could not normalize, too few arguments.'); - } - return promisify.apply( - null, [jsonld.normalize].concat(slice.call(arguments))); - }; + // drain event flag. + this.needDrain = false; + // at the start of calling end() + this.ending = false; + // when end() has been called, and returned + this.ended = false; + // when 'finish' is emitted + this.finished = false; - if(version === 'jsonld.js') { - api.link = function(input, ctx) { - if(arguments.length < 2) { - throw new TypeError('Could not link, too few arguments.'); - } - return promisify.apply( - null, [jsonld.link].concat(slice.call(arguments))); - }; - api.objectify = function(input) { - return promisify.apply( - null, [jsonld.objectify].concat(slice.call(arguments))); - }; - api.createNodeMap = function(input) { - return promisify.apply( - null, [jsonld.createNodeMap].concat(slice.call(arguments))); - }; - api.merge = function(input) { - return promisify.apply( - null, [jsonld.merge].concat(slice.call(arguments))); - }; - } + // has it been destroyed + this.destroyed = false; - try { - jsonld.Promise = global.Promise || _dereq_('es6-promise').Promise; - } catch(e) { - var f = function() { - throw new Error('Unable to find a Promise implementation.'); - }; - for(var method in api) { - api[method] = f; - } - } + // should we decode strings into buffers before passing to _write? + // this is here so that some node-core streams can optimize string + // handling at a lower level. + var noDecode = options.decodeStrings === false; + this.decodeStrings = !noDecode; - return api; -}; + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + this.defaultEncoding = options.defaultEncoding || 'utf8'; -/** - * Converts a node.js async op into a promise w/boxed resolved value(s). - * - * @param op the operation to convert. - * - * @return the promise. - */ -jsonld.promisify = function(op) { - if(!jsonld.Promise) { - try { - jsonld.Promise = global.Promise || _dereq_('es6-promise').Promise; - } catch(e) { - throw new Error('Unable to find a Promise implementation.'); - } - } - var args = Array.prototype.slice.call(arguments, 1); - return new jsonld.Promise(function(resolve, reject) { - op.apply(null, args.concat(function(err, value) { - if(!err) { - resolve(value); - } else { - reject(err); - } - })); - }); -}; + // not an actual buffer we keep track of, but a measurement + // of how much we're waiting to get pushed to some underlying + // socket or file. + this.length = 0; -// extend jsonld.promises w/jsonld.js methods -jsonld.promises({api: jsonld.promises}); + // a flag to see when we're in the middle of a write. + this.writing = false; -/* WebIDL API */ + // when true all writes will be buffered until .uncork() call + this.corked = 0; -function JsonLdProcessor() {} -JsonLdProcessor.prototype = jsonld.promises({version: 'json-ld-1.0'}); -JsonLdProcessor.prototype.toString = function() { - if(this instanceof JsonLdProcessor) { - return '[object JsonLdProcessor]'; - } - return '[object JsonLdProcessorPrototype]'; -}; -jsonld.JsonLdProcessor = JsonLdProcessor; + // a flag to be able to tell if the onwrite cb is called immediately, + // or on a later tick. We set this to true at first, because any + // actions that shouldn't happen until "later" should generally also + // not happen before the first write call. + this.sync = true; -// IE8 has Object.defineProperty but it only -// works on DOM nodes -- so feature detection -// requires try/catch :-( -var canDefineProperty = !!Object.defineProperty; -if(canDefineProperty) { - try { - Object.defineProperty({}, 'x', {}); - } catch(e) { - canDefineProperty = false; - } -} + // a flag to know if we're processing previously buffered items, which + // may call the _write() callback in the same tick, so that we don't + // end up in an overlapped onwrite situation. + this.bufferProcessing = false; -if(canDefineProperty) { - Object.defineProperty(JsonLdProcessor, 'prototype', { - writable: false, - enumerable: false - }); - Object.defineProperty(JsonLdProcessor.prototype, 'constructor', { - writable: true, - enumerable: false, - configurable: true, - value: JsonLdProcessor - }); -} + // the callback that's passed to _write(chunk,cb) + this.onwrite = function (er) { + onwrite(stream, er); + }; -// setup browser global JsonLdProcessor -if(_browser && typeof global.JsonLdProcessor === 'undefined') { - if(canDefineProperty) { - Object.defineProperty(global, 'JsonLdProcessor', { - writable: true, - enumerable: false, - configurable: true, - value: JsonLdProcessor - }); - } else { - global.JsonLdProcessor = JsonLdProcessor; - } -} + // the callback that the user supplies to write(chunk,encoding,cb) + this.writecb = null; -/* Utility API */ + // the amount that is being written when _write is called. + this.writelen = 0; -// define setImmediate and nextTick -//// nextTick implementation with browser-compatible fallback //// -// from https://github.com/caolan/async/blob/master/lib/async.js + this.bufferedRequest = null; + this.lastBufferedRequest = null; -// capture the global reference to guard against fakeTimer mocks -var _setImmediate = typeof setImmediate === 'function' && setImmediate; + // number of pending user-supplied write callbacks + // this must be 0 before 'finish' can be emitted + this.pendingcb = 0; -var _delay = _setImmediate ? function(fn) { - // not a direct alias (for IE10 compatibility) - _setImmediate(fn); -} : function(fn) { - setTimeout(fn, 0); -}; + // emit prefinish if the only thing we're waiting for is _write cbs + // This is relevant for synchronous Transform streams + this.prefinished = false; -if(typeof process === 'object' && typeof process.nextTick === 'function') { - jsonld.nextTick = process.nextTick; -} else { - jsonld.nextTick = _delay; -} -jsonld.setImmediate = _setImmediate ? _delay : jsonld.nextTick; + // True if the error was already emitted and should not be thrown again + this.errorEmitted = false; -/** - * Parses a link header. The results will be key'd by the value of "rel". - * - * Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" - * - * Parses as: { - * 'http://www.w3.org/ns/json-ld#context': { - * target: http://json-ld.org/contexts/person.jsonld, - * type: 'application/ld+json' - * } - * } - * - * If there is more than one "rel" with the same IRI, then entries in the - * resulting map for that "rel" will be arrays. - * - * @param header the link header to parse. - */ -jsonld.parseLinkHeader = function(header) { - var rval = {}; - // split on unbracketed/unquoted commas - var entries = header.match(/(?:<[^>]*?>|"[^"]*?"|[^,])+/g); - var rLinkHeader = /\s*<([^>]*?)>\s*(?:;\s*(.*))?/; - for(var i = 0; i < entries.length; ++i) { - var match = entries[i].match(rLinkHeader); - if(!match) { - continue; - } - var result = {target: match[1]}; - var params = match[2]; - var rParams = /(.*?)=(?:(?:"([^"]*?)")|([^"]*?))\s*(?:(?:;\s*)|$)/g; - while(match = rParams.exec(params)) { - result[match[1]] = (match[2] === undefined) ? match[3] : match[2]; - } - var rel = result['rel'] || ''; - if(_isArray(rval[rel])) { - rval[rel].push(result); - } else if(rel in rval) { - rval[rel] = [rval[rel], result]; - } else { - rval[rel] = result; - } - } - return rval; -}; + // count buffered requests + this.bufferedRequestCount = 0; -/** - * Creates a simple queue for requesting documents. - */ -jsonld.RequestQueue = function() { - this._requests = {}; -}; -jsonld.RequestQueue.prototype.wrapLoader = function(loader) { - this._loader = loader; - this._usePromise = (loader.length === 1); - return this.add.bind(this); -}; -jsonld.RequestQueue.prototype.add = function(url, callback) { - var self = this; + // allocate the first CorkedRequest, there is always + // one allocated and free to use, and we maintain at most two + this.corkedRequestsFree = new CorkedRequest(this); +} - // callback must be given if not using promises - if(!callback && !self._usePromise) { - throw new Error('callback must be specified.'); +WritableState.prototype.getBuffer = function getBuffer() { + var current = this.bufferedRequest; + var out = []; + while (current) { + out.push(current); + current = current.next; } + return out; +}; - // Promise-based API - if(self._usePromise) { - return new jsonld.Promise(function(resolve, reject) { - var load = self._requests[url]; - if(!load) { - // load URL then remove from queue - load = self._requests[url] = self._loader(url) - .then(function(remoteDoc) { - delete self._requests[url]; - return remoteDoc; - }).catch(function(err) { - delete self._requests[url]; - throw err; - }); - } - // resolve/reject promise once URL has been loaded - load.then(function(remoteDoc) { - resolve(remoteDoc); - }).catch(function(err) { - reject(err); - }); +(function () { + try { + Object.defineProperty(WritableState.prototype, 'buffer', { + get: internalUtil.deprecate(function () { + return this.getBuffer(); + }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') }); - } + } catch (_) {} +})(); - // callback-based API - if(url in self._requests) { - self._requests[url].push(callback); - } else { - self._requests[url] = [callback]; - self._loader(url, function(err, remoteDoc) { - var callbacks = self._requests[url]; - delete self._requests[url]; - for(var i = 0; i < callbacks.length; ++i) { - callbacks[i](err, remoteDoc); - } - }); - } -}; +// Test _writableState for inheritance to account for Duplex streams, +// whose prototype chain only points to Readable. +var realHasInstance; +if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') { + realHasInstance = Function.prototype[Symbol.hasInstance]; + Object.defineProperty(Writable, Symbol.hasInstance, { + value: function (object) { + if (realHasInstance.call(this, object)) return true; + if (this !== Writable) return false; -/** - * Creates a simple document cache that retains documents for a short - * period of time. - * - * FIXME: Implement simple HTTP caching instead. - * - * @param size the maximum size of the cache. - */ -jsonld.DocumentCache = function(size) { - this.order = []; - this.cache = {}; - this.size = size || 50; - this.expires = 30 * 1000; -}; -jsonld.DocumentCache.prototype.get = function(url) { - if(url in this.cache) { - var entry = this.cache[url]; - if(entry.expires >= +new Date()) { - return entry.ctx; + return object && object._writableState instanceof WritableState; } - delete this.cache[url]; - this.order.splice(this.order.indexOf(url), 1); + }); +} else { + realHasInstance = function (object) { + return object instanceof this; + }; +} + +function Writable(options) { + Duplex = Duplex || _dereq_('./_stream_duplex'); + + // Writable ctor is applied to Duplexes, too. + // `realHasInstance` is necessary because using plain `instanceof` + // would return false, as no `_writableState` property is attached. + + // Trying to use the custom `instanceof` for Writable here will also break the + // Node.js LazyTransform implementation, which has a non-trivial getter for + // `_writableState` that would lead to infinite recursion. + if (!realHasInstance.call(Writable, this) && !(this instanceof Duplex)) { + return new Writable(options); } - return null; -}; -jsonld.DocumentCache.prototype.set = function(url, ctx) { - if(this.order.length === this.size) { - delete this.cache[this.order.shift()]; + + this._writableState = new WritableState(options, this); + + // legacy. + this.writable = true; + + if (options) { + if (typeof options.write === 'function') this._write = options.write; + + if (typeof options.writev === 'function') this._writev = options.writev; + + if (typeof options.destroy === 'function') this._destroy = options.destroy; + + if (typeof options.final === 'function') this._final = options.final; } - this.order.push(url); - this.cache[url] = {ctx: ctx, expires: (+new Date() + this.expires)}; -}; -/** - * Creates an active context cache. - * - * @param size the maximum size of the cache. - */ -jsonld.ActiveContextCache = function(size) { - this.order = []; - this.cache = {}; - this.size = size || 100; + Stream.call(this); +} + +// Otherwise people can pipe Writable streams, which is just wrong. +Writable.prototype.pipe = function () { + this.emit('error', new Error('Cannot pipe, not readable')); }; -jsonld.ActiveContextCache.prototype.get = function(activeCtx, localCtx) { - var key1 = JSON.stringify(activeCtx); - var key2 = JSON.stringify(localCtx); - var level1 = this.cache[key1]; - if(level1 && key2 in level1) { - return level1[key2]; + +function writeAfterEnd(stream, cb) { + var er = new Error('write after end'); + // TODO: defer error events consistently everywhere, not just the cb + stream.emit('error', er); + pna.nextTick(cb, er); +} + +// Checks that a user-supplied chunk is valid, especially for the particular +// mode the stream is in. Currently this means that `null` is never accepted +// and undefined/non-string values are only allowed in object mode. +function validChunk(stream, state, chunk, cb) { + var valid = true; + var er = false; + + if (chunk === null) { + er = new TypeError('May not write null values to stream'); + } else if (typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { + er = new TypeError('Invalid non-string/buffer chunk'); } - return null; -}; -jsonld.ActiveContextCache.prototype.set = function( - activeCtx, localCtx, result) { - if(this.order.length === this.size) { - var entry = this.order.shift(); - delete this.cache[entry.activeCtx][entry.localCtx]; + if (er) { + stream.emit('error', er); + pna.nextTick(cb, er); + valid = false; } - var key1 = JSON.stringify(activeCtx); - var key2 = JSON.stringify(localCtx); - this.order.push({activeCtx: key1, localCtx: key2}); - if(!(key1 in this.cache)) { - this.cache[key1] = {}; + return valid; +} + +Writable.prototype.write = function (chunk, encoding, cb) { + var state = this._writableState; + var ret = false; + var isBuf = !state.objectMode && _isUint8Array(chunk); + + if (isBuf && !Buffer.isBuffer(chunk)) { + chunk = _uint8ArrayToBuffer(chunk); } - this.cache[key1][key2] = _clone(result); -}; -/** - * Default JSON-LD cache. - */ -jsonld.cache = { - activeCtx: new jsonld.ActiveContextCache() -}; + if (typeof encoding === 'function') { + cb = encoding; + encoding = null; + } -/** - * Document loaders. - */ -jsonld.documentLoaders = {}; + if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; -/** - * Creates a built-in jquery document loader. - * - * @param $ the jquery instance to use. - * @param options the options to use: - * secure: require all URLs to use HTTPS. - * usePromise: true to use a promises API, false for a - * callback-continuation-style API; defaults to true if Promise - * is globally defined, false if not. - * - * @return the jquery document loader. - */ -jsonld.documentLoaders.jquery = function($, options) { - options = options || {}; - var queue = new jsonld.RequestQueue(); + if (typeof cb !== 'function') cb = nop; - // use option or, by default, use Promise when its defined - var usePromise = ('usePromise' in options ? - options.usePromise : (typeof Promise !== 'undefined')); - if(usePromise) { - return queue.wrapLoader(function(url) { - return jsonld.promisify(loader, url); - }); + if (state.ended) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { + state.pendingcb++; + ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); } - return queue.wrapLoader(loader); - function loader(url, callback) { - if(url.indexOf('http:') !== 0 && url.indexOf('https:') !== 0) { - return callback(new JsonLdError( - 'URL could not be dereferenced; only "http" and "https" URLs are ' + - 'supported.', - 'jsonld.InvalidUrl', {code: 'loading document failed', url: url}), - {contextUrl: null, documentUrl: url, document: null}); - } - if(options.secure && url.indexOf('https') !== 0) { - return callback(new JsonLdError( - 'URL could not be dereferenced; secure mode is enabled and ' + - 'the URL\'s scheme is not "https".', - 'jsonld.InvalidUrl', {code: 'loading document failed', url: url}), - {contextUrl: null, documentUrl: url, document: null}); - } - $.ajax({ - url: url, - accepts: { - json: 'application/ld+json, application/json' - }, - // ensure Accept header is very specific for JSON-LD/JSON - headers: { - 'Accept': 'application/ld+json, application/json' - }, - dataType: 'json', - crossDomain: true, - success: function(data, textStatus, jqXHR) { - var doc = {contextUrl: null, documentUrl: url, document: data}; + return ret; +}; - // handle Link Header - var contentType = jqXHR.getResponseHeader('Content-Type'); - var linkHeader = jqXHR.getResponseHeader('Link'); - if(linkHeader && contentType !== 'application/ld+json') { - // only 1 related link header permitted - linkHeader = jsonld.parseLinkHeader(linkHeader)[LINK_HEADER_REL]; - if(_isArray(linkHeader)) { - return callback(new JsonLdError( - 'URL could not be dereferenced, it has more than one ' + - 'associated HTTP Link Header.', - 'jsonld.InvalidUrl', - {code: 'multiple context link headers', url: url}), doc); - } - if(linkHeader) { - doc.contextUrl = linkHeader.target; - } - } +Writable.prototype.cork = function () { + var state = this._writableState; - callback(null, doc); - }, - error: function(jqXHR, textStatus, err) { - callback(new JsonLdError( - 'URL could not be dereferenced, an error occurred.', - 'jsonld.LoadDocumentError', - {code: 'loading document failed', url: url, cause: err}), - {contextUrl: null, documentUrl: url, document: null}); - } - }); + state.corked++; +}; + +Writable.prototype.uncork = function () { + var state = this._writableState; + + if (state.corked) { + state.corked--; + + if (!state.writing && !state.corked && !state.finished && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); } }; -/** - * Creates a built-in node document loader. - * - * @param options the options to use: - * secure: require all URLs to use HTTPS. - * strictSSL: true to require SSL certificates to be valid, - * false not to (default: true). - * maxRedirects: the maximum number of redirects to permit, none by - * default. - * request: the object which will make the request, default is - * provided by `https://www.npmjs.com/package/request`. - * headers: an array of headers which will be passed as request - * headers for the requested document. Accept is not allowed. - * usePromise: true to use a promises API, false for a - * callback-continuation-style API; false by default. - * - * @return the node document loader. - */ -jsonld.documentLoaders.node = function(options) { - options = options || {}; - var strictSSL = ('strictSSL' in options) ? options.strictSSL : true; - var maxRedirects = ('maxRedirects' in options) ? options.maxRedirects : -1; - var request = ('request' in options) ? options.request : _dereq_('request'); - var acceptHeader = 'application/ld+json, application/json'; - var http = _dereq_('http'); - // TODO: disable cache until HTTP caching implemented - //var cache = new jsonld.DocumentCache(); +Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { + // node::ParseEncoding() requires lower case. + if (typeof encoding === 'string') encoding = encoding.toLowerCase(); + if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new TypeError('Unknown encoding: ' + encoding); + this._writableState.defaultEncoding = encoding; + return this; +}; - var queue = new jsonld.RequestQueue(); - if(options.usePromise) { - return queue.wrapLoader(function(url) { - return jsonld.promisify(loadDocument, url, []); - }); +function decodeChunk(state, chunk, encoding) { + if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { + chunk = Buffer.from(chunk, encoding); } - var headers = options.headers || {}; - if('Accept' in headers || 'accept' in headers) { - throw new RangeError( - 'Accept header may not be specified as an option; only "' + - acceptHeader + '" is supported.'); + return chunk; +} + +Object.defineProperty(Writable.prototype, 'writableHighWaterMark', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function () { + return this._writableState.highWaterMark; } - return queue.wrapLoader(function(url, callback) { - loadDocument(url, [], callback); - }); +}); - function loadDocument(url, redirects, callback) { - if(url.indexOf('http:') !== 0 && url.indexOf('https:') !== 0) { - return callback(new JsonLdError( - 'URL could not be dereferenced; only "http" and "https" URLs are ' + - 'supported.', - 'jsonld.InvalidUrl', {code: 'loading document failed', url: url}), - {contextUrl: null, documentUrl: url, document: null}); - } - if(options.secure && url.indexOf('https') !== 0) { - return callback(new JsonLdError( - 'URL could not be dereferenced; secure mode is enabled and ' + - 'the URL\'s scheme is not "https".', - 'jsonld.InvalidUrl', {code: 'loading document failed', url: url}), - {contextUrl: null, documentUrl: url, document: null}); - } - // TODO: disable cache until HTTP caching implemented - var doc = null;//cache.get(url); - if(doc !== null) { - return callback(null, doc); +// if we're already writing something, then just put this +// in the queue, and wait our turn. Otherwise, call _write +// If we return false, then we need a drain event, so set that flag. +function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { + if (!isBuf) { + var newChunk = decodeChunk(state, chunk, encoding); + if (chunk !== newChunk) { + isBuf = true; + encoding = 'buffer'; + chunk = newChunk; } - var headers = {'Accept': acceptHeader}; - for(var k in options.headers) { headers[k] = options.headers[k]; } - request({ - url: url, - headers: headers, - strictSSL: strictSSL, - followRedirect: false - }, handleResponse); - - function handleResponse(err, res, body) { - doc = {contextUrl: null, documentUrl: url, document: body || null}; + } + var len = state.objectMode ? 1 : chunk.length; - // handle error - if(err) { - return callback(new JsonLdError( - 'URL could not be dereferenced, an error occurred.', - 'jsonld.LoadDocumentError', - {code: 'loading document failed', url: url, cause: err}), doc); - } - var statusText = http.STATUS_CODES[res.statusCode]; - if(res.statusCode >= 400) { - return callback(new JsonLdError( - 'URL could not be dereferenced: ' + statusText, - 'jsonld.InvalidUrl', { - code: 'loading document failed', - url: url, - httpStatusCode: res.statusCode - }), doc); - } + state.length += len; - // handle Link Header - if(res.headers.link && - res.headers['content-type'] !== 'application/ld+json') { - // only 1 related link header permitted - var linkHeader = jsonld.parseLinkHeader( - res.headers.link)[LINK_HEADER_REL]; - if(_isArray(linkHeader)) { - return callback(new JsonLdError( - 'URL could not be dereferenced, it has more than one associated ' + - 'HTTP Link Header.', - 'jsonld.InvalidUrl', - {code: 'multiple context link headers', url: url}), doc); - } - if(linkHeader) { - doc.contextUrl = linkHeader.target; - } - } + var ret = state.length < state.highWaterMark; + // we must ensure that previous needDrain will not be reset to false. + if (!ret) state.needDrain = true; - // handle redirect - if(res.statusCode >= 300 && res.statusCode < 400 && - res.headers.location) { - if(redirects.length === maxRedirects) { - return callback(new JsonLdError( - 'URL could not be dereferenced; there were too many redirects.', - 'jsonld.TooManyRedirects', { - code: 'loading document failed', - url: url, - httpStatusCode: res.statusCode, - redirects: redirects - }), doc); - } - if(redirects.indexOf(url) !== -1) { - return callback(new JsonLdError( - 'URL could not be dereferenced; infinite redirection was detected.', - 'jsonld.InfiniteRedirectDetected', { - code: 'recursive context inclusion', - url: url, - httpStatusCode: res.statusCode, - redirects: redirects - }), doc); - } - redirects.push(url); - return loadDocument(res.headers.location, redirects, callback); - } - // cache for each redirected URL - redirects.push(url); - // TODO: disable cache until HTTP caching implemented - /*for(var i = 0; i < redirects.length; ++i) { - cache.set( - redirects[i], - {contextUrl: null, documentUrl: redirects[i], document: body}); - }*/ - callback(err, doc); + if (state.writing || state.corked) { + var last = state.lastBufferedRequest; + state.lastBufferedRequest = { + chunk: chunk, + encoding: encoding, + isBuf: isBuf, + callback: cb, + next: null + }; + if (last) { + last.next = state.lastBufferedRequest; + } else { + state.bufferedRequest = state.lastBufferedRequest; } + state.bufferedRequestCount += 1; + } else { + doWrite(stream, state, false, len, chunk, encoding, cb); } -}; -/** - * Creates a built-in XMLHttpRequest document loader. - * - * @param options the options to use: - * secure: require all URLs to use HTTPS. - * usePromise: true to use a promises API, false for a - * callback-continuation-style API; defaults to true if Promise - * is globally defined, false if not. - * [xhr]: the XMLHttpRequest API to use. - * - * @return the XMLHttpRequest document loader. - */ -jsonld.documentLoaders.xhr = function(options) { - options = options || {}; - var rlink = /(^|(\r\n))link:/i; - var queue = new jsonld.RequestQueue(); + return ret; +} - // use option or, by default, use Promise when its defined - var usePromise = ('usePromise' in options ? - options.usePromise : (typeof Promise !== 'undefined')); - if(usePromise) { - return queue.wrapLoader(function(url) { - return jsonld.promisify(loader, url); - }); - } - return queue.wrapLoader(loader); +function doWrite(stream, state, writev, len, chunk, encoding, cb) { + state.writelen = len; + state.writecb = cb; + state.writing = true; + state.sync = true; + if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite); + state.sync = false; +} - function loader(url, callback) { - if(url.indexOf('http:') !== 0 && url.indexOf('https:') !== 0) { - return callback(new JsonLdError( - 'URL could not be dereferenced; only "http" and "https" URLs are ' + - 'supported.', - 'jsonld.InvalidUrl', {code: 'loading document failed', url: url}), - {contextUrl: null, documentUrl: url, document: null}); - } - if(options.secure && url.indexOf('https') !== 0) { - return callback(new JsonLdError( - 'URL could not be dereferenced; secure mode is enabled and ' + - 'the URL\'s scheme is not "https".', - 'jsonld.InvalidUrl', {code: 'loading document failed', url: url}), - {contextUrl: null, documentUrl: url, document: null}); - } - var xhr = options.xhr || XMLHttpRequest; - var req = new xhr(); - req.onload = function() { - if(req.status >= 400) { - return callback(new JsonLdError( - 'URL could not be dereferenced: ' + req.statusText, - 'jsonld.LoadDocumentError', { - code: 'loading document failed', - url: url, - httpStatusCode: req.status - }), {contextUrl: null, documentUrl: url, document: null}); - } +function onwriteError(stream, state, sync, er, cb) { + --state.pendingcb; - var doc = {contextUrl: null, documentUrl: url, document: req.response}; + if (sync) { + // defer the callback if we are being called synchronously + // to avoid piling up things on the stack + pna.nextTick(cb, er); + // this can emit finish, and it will always happen + // after error + pna.nextTick(finishMaybe, stream, state); + stream._writableState.errorEmitted = true; + stream.emit('error', er); + } else { + // the caller expect this to happen before if + // it is async + cb(er); + stream._writableState.errorEmitted = true; + stream.emit('error', er); + // this can emit finish, but finish must + // always follow error + finishMaybe(stream, state); + } +} - // handle Link Header (avoid unsafe header warning by existence testing) - var contentType = req.getResponseHeader('Content-Type'); - var linkHeader; - if(rlink.test(req.getAllResponseHeaders())) { - linkHeader = req.getResponseHeader('Link'); - } - if(linkHeader && contentType !== 'application/ld+json') { - // only 1 related link header permitted - linkHeader = jsonld.parseLinkHeader(linkHeader)[LINK_HEADER_REL]; - if(_isArray(linkHeader)) { - return callback(new JsonLdError( - 'URL could not be dereferenced, it has more than one ' + - 'associated HTTP Link Header.', - 'jsonld.InvalidUrl', - {code: 'multiple context link headers', url: url}), doc); - } - if(linkHeader) { - doc.contextUrl = linkHeader.target; - } - } +function onwriteStateUpdate(state) { + state.writing = false; + state.writecb = null; + state.length -= state.writelen; + state.writelen = 0; +} - callback(null, doc); - }; - req.onerror = function() { - callback(new JsonLdError( - 'URL could not be dereferenced, an error occurred.', - 'jsonld.LoadDocumentError', - {code: 'loading document failed', url: url}), - {contextUrl: null, documentUrl: url, document: null}); - }; - req.open('GET', url, true); - req.setRequestHeader('Accept', 'application/ld+json, application/json'); - req.send(); - } -}; +function onwrite(stream, er) { + var state = stream._writableState; + var sync = state.sync; + var cb = state.writecb; -/** - * Assigns the default document loader for external document URLs to a built-in - * default. Supported types currently include: 'jquery' and 'node'. - * - * To use the jquery document loader, the first parameter must be a reference - * to the main jquery object. - * - * @param type the type to set. - * @param [params] the parameters required to use the document loader. - */ -jsonld.useDocumentLoader = function(type) { - if(!(type in jsonld.documentLoaders)) { - throw new JsonLdError( - 'Unknown document loader type: "' + type + '"', - 'jsonld.UnknownDocumentLoader', - {type: type}); - } + onwriteStateUpdate(state); - // set document loader - jsonld.documentLoader = jsonld.documentLoaders[type].apply( - jsonld, Array.prototype.slice.call(arguments, 1)); -}; + if (er) onwriteError(stream, state, sync, er, cb);else { + // Check if we're actually ready to finish, but don't emit yet + var finished = needFinish(state); -/** - * Processes a local context, resolving any URLs as necessary, and returns a - * new active context in its callback. - * - * @param activeCtx the current active context. - * @param localCtx the local context to process. - * @param [options] the options to use: - * [documentLoader(url, callback(err, remoteDoc))] the document loader. - * @param callback(err, ctx) called once the operation completes. - */ -jsonld.processContext = function(activeCtx, localCtx) { - // get arguments - var options = {}; - var callbackArg = 2; - if(arguments.length > 3) { - options = arguments[2] || {}; - callbackArg += 1; - } - var callback = arguments[callbackArg]; + if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { + clearBuffer(stream, state); + } - // set default options - if(!('base' in options)) { - options.base = ''; - } - if(!('documentLoader' in options)) { - options.documentLoader = jsonld.loadDocument; + if (sync) { + /**/ + asyncWrite(afterWrite, stream, state, finished, cb); + /**/ + } else { + afterWrite(stream, state, finished, cb); + } } +} - // return initial context early for null context - if(localCtx === null) { - return callback(null, _getInitialContext(options)); - } +function afterWrite(stream, state, finished, cb) { + if (!finished) onwriteDrain(stream, state); + state.pendingcb--; + cb(); + finishMaybe(stream, state); +} - // retrieve URLs in localCtx - localCtx = _clone(localCtx); - if(!(_isObject(localCtx) && '@context' in localCtx)) { - localCtx = {'@context': localCtx}; +// Must force callback to be called on nextTick, so that we don't +// emit 'drain' before the write() consumer gets the 'false' return +// value, and has a chance to attach a 'drain' listener. +function onwriteDrain(stream, state) { + if (state.length === 0 && state.needDrain) { + state.needDrain = false; + stream.emit('drain'); } - _retrieveContextUrls(localCtx, options, function(err, ctx) { - if(err) { - return callback(err); - } - try { - // process context - ctx = new Processor().processContext(activeCtx, ctx, options); - } catch(ex) { - return callback(ex); - } - callback(null, ctx); - }); -}; +} -/** - * Returns true if the given subject has the given property. - * - * @param subject the subject to check. - * @param property the property to look for. - * - * @return true if the subject has the given property, false if not. - */ -jsonld.hasProperty = function(subject, property) { - var rval = false; - if(property in subject) { - var value = subject[property]; - rval = (!_isArray(value) || value.length > 0); - } - return rval; -}; +// if there's something in the buffer waiting, then process it +function clearBuffer(stream, state) { + state.bufferProcessing = true; + var entry = state.bufferedRequest; -/** - * Determines if the given value is a property of the given subject. - * - * @param subject the subject to check. - * @param property the property to check. - * @param value the value to check. - * - * @return true if the value exists, false if not. - */ -jsonld.hasValue = function(subject, property, value) { - var rval = false; - if(jsonld.hasProperty(subject, property)) { - var val = subject[property]; - var isList = _isList(val); - if(_isArray(val) || isList) { - if(isList) { - val = val['@list']; - } - for(var i = 0; i < val.length; ++i) { - if(jsonld.compareValues(value, val[i])) { - rval = true; - break; - } - } - } else if(!_isArray(value)) { - // avoid matching the set of values with an array value parameter - rval = jsonld.compareValues(value, val); + if (stream._writev && entry && entry.next) { + // Fast case, write everything using _writev() + var l = state.bufferedRequestCount; + var buffer = new Array(l); + var holder = state.corkedRequestsFree; + holder.entry = entry; + + var count = 0; + var allBuffers = true; + while (entry) { + buffer[count] = entry; + if (!entry.isBuf) allBuffers = false; + entry = entry.next; + count += 1; } - } - return rval; -}; + buffer.allBuffers = allBuffers; -/** - * Adds a value to a subject. If the value is an array, all values in the - * array will be added. - * - * @param subject the subject to add the value to. - * @param property the property that relates the value to the subject. - * @param value the value to add. - * @param [options] the options to use: - * [propertyIsArray] true if the property is always an array, false - * if not (default: false). - * [allowDuplicate] true to allow duplicates, false not to (uses a - * simple shallow comparison of subject ID or value) (default: true). - */ -jsonld.addValue = function(subject, property, value, options) { - options = options || {}; - if(!('propertyIsArray' in options)) { - options.propertyIsArray = false; - } - if(!('allowDuplicate' in options)) { - options.allowDuplicate = true; - } + doWrite(stream, state, true, state.length, buffer, '', holder.finish); - if(_isArray(value)) { - if(value.length === 0 && options.propertyIsArray && - !(property in subject)) { - subject[property] = []; - } - for(var i = 0; i < value.length; ++i) { - jsonld.addValue(subject, property, value[i], options); + // doWrite is almost always async, defer these to save a bit of time + // as the hot path ends with doWrite + state.pendingcb++; + state.lastBufferedRequest = null; + if (holder.next) { + state.corkedRequestsFree = holder.next; + holder.next = null; + } else { + state.corkedRequestsFree = new CorkedRequest(state); } - } else if(property in subject) { - // check if subject already has value if duplicates not allowed - var hasValue = (!options.allowDuplicate && - jsonld.hasValue(subject, property, value)); + state.bufferedRequestCount = 0; + } else { + // Slow case, write chunks one-by-one + while (entry) { + var chunk = entry.chunk; + var encoding = entry.encoding; + var cb = entry.callback; + var len = state.objectMode ? 1 : chunk.length; - // make property an array if value not present or always an array - if(!_isArray(subject[property]) && - (!hasValue || options.propertyIsArray)) { - subject[property] = [subject[property]]; + doWrite(stream, state, false, len, chunk, encoding, cb); + entry = entry.next; + state.bufferedRequestCount--; + // if we didn't call the onwrite immediately, then + // it means that we need to wait until it does. + // also, that means that the chunk and cb are currently + // being processed, so move the buffer counter past them. + if (state.writing) { + break; + } } - // add new value - if(!hasValue) { - subject[property].push(value); - } - } else { - // add new value as set or single value - subject[property] = options.propertyIsArray ? [value] : value; + if (entry === null) state.lastBufferedRequest = null; } -}; -/** - * Gets all of the values for a subject's property as an array. - * - * @param subject the subject. - * @param property the property. - * - * @return all of the values for a subject's property as an array. - */ -jsonld.getValues = function(subject, property) { - var rval = subject[property] || []; - if(!_isArray(rval)) { - rval = [rval]; - } - return rval; -}; + state.bufferedRequest = entry; + state.bufferProcessing = false; +} -/** - * Removes a property from a subject. - * - * @param subject the subject. - * @param property the property. - */ -jsonld.removeProperty = function(subject, property) { - delete subject[property]; +Writable.prototype._write = function (chunk, encoding, cb) { + cb(new Error('_write() is not implemented')); }; -/** - * Removes a value from a subject. - * - * @param subject the subject. - * @param property the property that relates the value to the subject. - * @param value the value to remove. - * @param [options] the options to use: - * [propertyIsArray] true if the property is always an array, false - * if not (default: false). - */ -jsonld.removeValue = function(subject, property, value, options) { - options = options || {}; - if(!('propertyIsArray' in options)) { - options.propertyIsArray = false; - } - - // filter out value - var values = jsonld.getValues(subject, property).filter(function(e) { - return !jsonld.compareValues(e, value); - }); +Writable.prototype._writev = null; - if(values.length === 0) { - jsonld.removeProperty(subject, property); - } else if(values.length === 1 && !options.propertyIsArray) { - subject[property] = values[0]; - } else { - subject[property] = values; - } -}; +Writable.prototype.end = function (chunk, encoding, cb) { + var state = this._writableState; -/** - * Compares two JSON-LD values for equality. Two JSON-LD values will be - * considered equal if: - * - * 1. They are both primitives of the same type and value. - * 2. They are both @values with the same @value, @type, @language, - * and @index, OR - * 3. They both have @ids they are the same. - * - * @param v1 the first value. - * @param v2 the second value. - * - * @return true if v1 and v2 are considered equal, false if not. - */ -jsonld.compareValues = function(v1, v2) { - // 1. equal primitives - if(v1 === v2) { - return true; + if (typeof chunk === 'function') { + cb = chunk; + chunk = null; + encoding = null; + } else if (typeof encoding === 'function') { + cb = encoding; + encoding = null; } - // 2. equal @values - if(_isValue(v1) && _isValue(v2) && - v1['@value'] === v2['@value'] && - v1['@type'] === v2['@type'] && - v1['@language'] === v2['@language'] && - v1['@index'] === v2['@index']) { - return true; - } + if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); - // 3. equal @ids - if(_isObject(v1) && ('@id' in v1) && _isObject(v2) && ('@id' in v2)) { - return v1['@id'] === v2['@id']; + // .end() fully uncorks + if (state.corked) { + state.corked = 1; + this.uncork(); } - return false; + // ignore unnecessary end() calls. + if (!state.ending && !state.finished) endWritable(this, state, cb); }; -/** - * Gets the value for the given active context key and type, null if none is - * set. - * - * @param ctx the active context. - * @param key the context key. - * @param [type] the type of value to get (eg: '@id', '@type'), if not - * specified gets the entire entry for a key, null if not found. - * - * @return the value. - */ -jsonld.getContextValue = function(ctx, key, type) { - var rval = null; +function needFinish(state) { + return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing; +} +function callFinal(stream, state) { + stream._final(function (err) { + state.pendingcb--; + if (err) { + stream.emit('error', err); + } + state.prefinished = true; + stream.emit('prefinish'); + finishMaybe(stream, state); + }); +} +function prefinish(stream, state) { + if (!state.prefinished && !state.finalCalled) { + if (typeof stream._final === 'function') { + state.pendingcb++; + state.finalCalled = true; + pna.nextTick(callFinal, stream, state); + } else { + state.prefinished = true; + stream.emit('prefinish'); + } + } +} - // return null for invalid key - if(key === null) { - return rval; +function finishMaybe(stream, state) { + var need = needFinish(state); + if (need) { + prefinish(stream, state); + if (state.pendingcb === 0) { + state.finished = true; + stream.emit('finish'); + } } + return need; +} - // get default language - if(type === '@language' && (type in ctx)) { - rval = ctx[type]; +function endWritable(stream, state, cb) { + state.ending = true; + finishMaybe(stream, state); + if (cb) { + if (state.finished) pna.nextTick(cb);else stream.once('finish', cb); } + state.ended = true; + stream.writable = false; +} - // get specific entry information - if(ctx.mappings[key]) { - var entry = ctx.mappings[key]; +function onCorkedFinish(corkReq, state, err) { + var entry = corkReq.entry; + corkReq.entry = null; + while (entry) { + var cb = entry.callback; + state.pendingcb--; + cb(err); + entry = entry.next; + } + if (state.corkedRequestsFree) { + state.corkedRequestsFree.next = corkReq; + } else { + state.corkedRequestsFree = corkReq; + } +} - if(_isUndefined(type)) { - // return whole entry - rval = entry; - } else if(type in entry) { - // return entry value for type - rval = entry[type]; +Object.defineProperty(Writable.prototype, 'destroyed', { + get: function () { + if (this._writableState === undefined) { + return false; + } + return this._writableState.destroyed; + }, + set: function (value) { + // we ignore the value if the stream + // has not been initialized yet + if (!this._writableState) { + return; } + + // backward compatibility, the user is explicitly + // managing destroyed + this._writableState.destroyed = value; } +}); - return rval; +Writable.prototype.destroy = destroyImpl.destroy; +Writable.prototype._undestroy = destroyImpl.undestroy; +Writable.prototype._destroy = function (err, cb) { + this.end(); + cb(err); }; +}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -/** Registered RDF dataset parsers hashed by content-type. */ -var _rdfParsers = {}; +},{"./_stream_duplex":12,"./internal/streams/destroy":18,"./internal/streams/stream":19,"_process":74,"core-util-is":38,"inherits":43,"process-nextick-args":73,"safe-buffer":26,"util-deprecate":121}],17:[function(_dereq_,module,exports){ +'use strict'; -/** - * Registers an RDF dataset parser by content-type, for use with - * jsonld.fromRDF. An RDF dataset parser will always be given two parameters, - * a string of input and a callback. An RDF dataset parser can be synchronous - * or asynchronous. - * - * If the parser function returns undefined or null then it will be assumed to - * be asynchronous w/a continuation-passing style and the callback parameter - * given to the parser MUST be invoked. - * - * If it returns a Promise, then it will be assumed to be asynchronous, but the - * callback parameter MUST NOT be invoked. It should instead be ignored. - * - * If it returns an RDF dataset, it will be assumed to be synchronous and the - * callback parameter MUST NOT be invoked. It should instead be ignored. - * - * @param contentType the content-type for the parser. - * @param parser(input, callback(err, dataset)) the parser function (takes a - * string as a parameter and either returns null/undefined and uses - * the given callback, returns a Promise, or returns an RDF dataset). - */ -jsonld.registerRDFParser = function(contentType, parser) { - _rdfParsers[contentType] = parser; -}; +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } -/** - * Unregisters an RDF dataset parser by content-type. - * - * @param contentType the content-type for the parser. - */ -jsonld.unregisterRDFParser = function(contentType) { - delete _rdfParsers[contentType]; -}; +var Buffer = _dereq_('safe-buffer').Buffer; +var util = _dereq_('util'); -if(_nodejs) { - // needed for serialization of XML literals - if(typeof XMLSerializer === 'undefined') { - var XMLSerializer = null; - } - if(typeof Node === 'undefined') { - var Node = { - ELEMENT_NODE: 1, - ATTRIBUTE_NODE: 2, - TEXT_NODE: 3, - CDATA_SECTION_NODE: 4, - ENTITY_REFERENCE_NODE: 5, - ENTITY_NODE: 6, - PROCESSING_INSTRUCTION_NODE: 7, - COMMENT_NODE: 8, - DOCUMENT_NODE: 9, - DOCUMENT_TYPE_NODE: 10, - DOCUMENT_FRAGMENT_NODE: 11, - NOTATION_NODE:12 - }; - } +function copyBuffer(src, target, offset) { + src.copy(target, offset); } -// constants -var XSD_BOOLEAN = 'http://www.w3.org/2001/XMLSchema#boolean'; -var XSD_DOUBLE = 'http://www.w3.org/2001/XMLSchema#double'; -var XSD_INTEGER = 'http://www.w3.org/2001/XMLSchema#integer'; -var XSD_STRING = 'http://www.w3.org/2001/XMLSchema#string'; - -var RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; -var RDF_LIST = RDF + 'List'; -var RDF_FIRST = RDF + 'first'; -var RDF_REST = RDF + 'rest'; -var RDF_NIL = RDF + 'nil'; -var RDF_TYPE = RDF + 'type'; -var RDF_PLAIN_LITERAL = RDF + 'PlainLiteral'; -var RDF_XML_LITERAL = RDF + 'XMLLiteral'; -var RDF_OBJECT = RDF + 'object'; -var RDF_LANGSTRING = RDF + 'langString'; - -var LINK_HEADER_REL = 'http://www.w3.org/ns/json-ld#context'; -var MAX_CONTEXT_URLS = 10; +module.exports = function () { + function BufferList() { + _classCallCheck(this, BufferList); -/** - * A JSON-LD Error. - * - * @param msg the error message. - * @param type the error type. - * @param details the error details. - */ -var JsonLdError = function(msg, type, details) { - if(_nodejs) { - Error.call(this); - Error.captureStackTrace(this, this.constructor); - } else if(typeof Error !== 'undefined') { - this.stack = (new Error()).stack; + this.head = null; + this.tail = null; + this.length = 0; } - this.name = type || 'jsonld.Error'; - this.message = msg || 'An unspecified JSON-LD error occurred.'; - this.details = details || {}; -}; -if(_nodejs) { - _dereq_('util').inherits(JsonLdError, Error); -} else if(typeof Error !== 'undefined') { - JsonLdError.prototype = new Error(); -} - -/** - * Constructs a new JSON-LD Processor. - */ -var Processor = function() {}; -/** - * Recursively compacts an element using the given active context. All values - * must be in expanded form before this method is called. - * - * @param activeCtx the active context to use. - * @param activeProperty the compacted property associated with the element - * to compact, null for none. - * @param element the element to compact. - * @param options the compaction options. - * - * @return the compacted value. - */ -Processor.prototype.compact = function( - activeCtx, activeProperty, element, options) { - // recursively compact array - if(_isArray(element)) { - var rval = []; - for(var i = 0; i < element.length; ++i) { - // compact, dropping any null values - var compacted = this.compact( - activeCtx, activeProperty, element[i], options); - if(compacted !== null) { - rval.push(compacted); - } - } - if(options.compactArrays && rval.length === 1) { - // use single element if no container is specified - var container = jsonld.getContextValue( - activeCtx, activeProperty, '@container'); - if(container === null) { - rval = rval[0]; - } - } - return rval; - } + BufferList.prototype.push = function push(v) { + var entry = { data: v, next: null }; + if (this.length > 0) this.tail.next = entry;else this.head = entry; + this.tail = entry; + ++this.length; + }; - // recursively compact object - if(_isObject(element)) { - if(options.link && '@id' in element && element['@id'] in options.link) { - // check for a linked element to reuse - var linked = options.link[element['@id']]; - for(var i = 0; i < linked.length; ++i) { - if(linked[i].expanded === element) { - return linked[i].compacted; - } - } - } + BufferList.prototype.unshift = function unshift(v) { + var entry = { data: v, next: this.head }; + if (this.length === 0) this.tail = entry; + this.head = entry; + ++this.length; + }; - // do value compaction on @values and subject references - if(_isValue(element) || _isSubjectReference(element)) { - var rval = _compactValue(activeCtx, activeProperty, element); - if(options.link && _isSubjectReference(element)) { - // store linked element - if(!(element['@id'] in options.link)) { - options.link[element['@id']] = []; - } - options.link[element['@id']].push({expanded: element, compacted: rval}); - } - return rval; - } + BufferList.prototype.shift = function shift() { + if (this.length === 0) return; + var ret = this.head.data; + if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next; + --this.length; + return ret; + }; - // FIXME: avoid misuse of active property as an expanded property? - var insideReverse = (activeProperty === '@reverse'); + BufferList.prototype.clear = function clear() { + this.head = this.tail = null; + this.length = 0; + }; - var rval = {}; + BufferList.prototype.join = function join(s) { + if (this.length === 0) return ''; + var p = this.head; + var ret = '' + p.data; + while (p = p.next) { + ret += s + p.data; + }return ret; + }; - if(options.link && '@id' in element) { - // store linked element - if(!(element['@id'] in options.link)) { - options.link[element['@id']] = []; - } - options.link[element['@id']].push({expanded: element, compacted: rval}); + BufferList.prototype.concat = function concat(n) { + if (this.length === 0) return Buffer.alloc(0); + if (this.length === 1) return this.head.data; + var ret = Buffer.allocUnsafe(n >>> 0); + var p = this.head; + var i = 0; + while (p) { + copyBuffer(p.data, ret, i); + i += p.data.length; + p = p.next; } + return ret; + }; - // process element keys in order - var keys = Object.keys(element).sort(); - for(var ki = 0; ki < keys.length; ++ki) { - var expandedProperty = keys[ki]; - var expandedValue = element[expandedProperty]; - - // compact @id and @type(s) - if(expandedProperty === '@id' || expandedProperty === '@type') { - var compactedValue; - - // compact single @id - if(_isString(expandedValue)) { - compactedValue = _compactIri( - activeCtx, expandedValue, null, - {vocab: (expandedProperty === '@type')}); - } else { - // expanded value must be a @type array - compactedValue = []; - for(var vi = 0; vi < expandedValue.length; ++vi) { - compactedValue.push(_compactIri( - activeCtx, expandedValue[vi], null, {vocab: true})); - } - } + return BufferList; +}(); - // use keyword alias and add value - var alias = _compactIri(activeCtx, expandedProperty); - var isArray = (_isArray(compactedValue) && expandedValue.length === 0); - jsonld.addValue( - rval, alias, compactedValue, {propertyIsArray: isArray}); - continue; - } +if (util && util.inspect && util.inspect.custom) { + module.exports.prototype[util.inspect.custom] = function () { + var obj = util.inspect({ length: this.length }); + return this.constructor.name + ' ' + obj; + }; +} +},{"safe-buffer":26,"util":2}],18:[function(_dereq_,module,exports){ +'use strict'; - // handle @reverse - if(expandedProperty === '@reverse') { - // recursively compact expanded value - var compactedValue = this.compact( - activeCtx, '@reverse', expandedValue, options); +/**/ - // handle double-reversed properties - for(var compactedProperty in compactedValue) { - if(activeCtx.mappings[compactedProperty] && - activeCtx.mappings[compactedProperty].reverse) { - var value = compactedValue[compactedProperty]; - var container = jsonld.getContextValue( - activeCtx, compactedProperty, '@container'); - var useArray = (container === '@set' || !options.compactArrays); - jsonld.addValue( - rval, compactedProperty, value, {propertyIsArray: useArray}); - delete compactedValue[compactedProperty]; - } - } +var pna = _dereq_('process-nextick-args'); +/**/ - if(Object.keys(compactedValue).length > 0) { - // use keyword alias and add value - var alias = _compactIri(activeCtx, expandedProperty); - jsonld.addValue(rval, alias, compactedValue); - } +// undocumented cb() API, needed for core, not for public API +function destroy(err, cb) { + var _this = this; - continue; - } + var readableDestroyed = this._readableState && this._readableState.destroyed; + var writableDestroyed = this._writableState && this._writableState.destroyed; - // handle @index property - if(expandedProperty === '@index') { - // drop @index if inside an @index container - var container = jsonld.getContextValue( - activeCtx, activeProperty, '@container'); - if(container === '@index') { - continue; - } + if (readableDestroyed || writableDestroyed) { + if (cb) { + cb(err); + } else if (err && (!this._writableState || !this._writableState.errorEmitted)) { + pna.nextTick(emitErrorNT, this, err); + } + return this; + } - // use keyword alias and add value - var alias = _compactIri(activeCtx, expandedProperty); - jsonld.addValue(rval, alias, expandedValue); - continue; - } + // we set destroyed to true before firing error callbacks in order + // to make it re-entrance safe in case destroy() is called within callbacks - // skip array processing for keywords that aren't @graph or @list - if(expandedProperty !== '@graph' && expandedProperty !== '@list' && - _isKeyword(expandedProperty)) { - // use keyword alias and add value as is - var alias = _compactIri(activeCtx, expandedProperty); - jsonld.addValue(rval, alias, expandedValue); - continue; - } + if (this._readableState) { + this._readableState.destroyed = true; + } - // Note: expanded value must be an array due to expansion algorithm. + // if this is a duplex stream mark the writable part as destroyed as well + if (this._writableState) { + this._writableState.destroyed = true; + } - // preserve empty arrays - if(expandedValue.length === 0) { - var itemActiveProperty = _compactIri( - activeCtx, expandedProperty, expandedValue, {vocab: true}, - insideReverse); - jsonld.addValue( - rval, itemActiveProperty, expandedValue, {propertyIsArray: true}); + this._destroy(err || null, function (err) { + if (!cb && err) { + pna.nextTick(emitErrorNT, _this, err); + if (_this._writableState) { + _this._writableState.errorEmitted = true; } + } else if (cb) { + cb(err); + } + }); - // recusively process array values - for(var vi = 0; vi < expandedValue.length; ++vi) { - var expandedItem = expandedValue[vi]; + return this; +} - // compact property and get container type - var itemActiveProperty = _compactIri( - activeCtx, expandedProperty, expandedItem, {vocab: true}, - insideReverse); - var container = jsonld.getContextValue( - activeCtx, itemActiveProperty, '@container'); +function undestroy() { + if (this._readableState) { + this._readableState.destroyed = false; + this._readableState.reading = false; + this._readableState.ended = false; + this._readableState.endEmitted = false; + } - // get @list value if appropriate - var isList = _isList(expandedItem); - var list = null; - if(isList) { - list = expandedItem['@list']; - } + if (this._writableState) { + this._writableState.destroyed = false; + this._writableState.ended = false; + this._writableState.ending = false; + this._writableState.finished = false; + this._writableState.errorEmitted = false; + } +} - // recursively compact expanded item - var compactedItem = this.compact( - activeCtx, itemActiveProperty, isList ? list : expandedItem, options); +function emitErrorNT(self, err) { + self.emit('error', err); +} - // handle @list - if(isList) { - // ensure @list value is an array - if(!_isArray(compactedItem)) { - compactedItem = [compactedItem]; - } +module.exports = { + destroy: destroy, + undestroy: undestroy +}; +},{"process-nextick-args":73}],19:[function(_dereq_,module,exports){ +module.exports = _dereq_('events').EventEmitter; - if(container !== '@list') { - // wrap using @list alias - var wrapper = {}; - wrapper[_compactIri(activeCtx, '@list')] = compactedItem; - compactedItem = wrapper; +},{"events":8}],20:[function(_dereq_,module,exports){ +arguments[4][6][0].apply(exports,arguments) +},{"dup":6}],21:[function(_dereq_,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. - // include @index from expanded @list, if any - if('@index' in expandedItem) { - compactedItem[_compactIri(activeCtx, '@index')] = - expandedItem['@index']; - } - } else if(itemActiveProperty in rval) { - // can't use @list container for more than 1 list - throw new JsonLdError( - 'JSON-LD compact error; property has a "@list" @container ' + - 'rule but there is more than a single @list that matches ' + - 'the compacted term in the document. Compaction might mix ' + - 'unwanted items into the list.', - 'jsonld.SyntaxError', {code: 'compaction to list of lists'}); - } - } +'use strict'; - // handle language and index maps - if(container === '@language' || container === '@index') { - // get or create the map object - var mapObject; - if(itemActiveProperty in rval) { - mapObject = rval[itemActiveProperty]; - } else { - rval[itemActiveProperty] = mapObject = {}; - } +/**/ - // if container is a language map, simplify compacted value to - // a simple string - if(container === '@language' && _isValue(compactedItem)) { - compactedItem = compactedItem['@value']; - } +var Buffer = _dereq_('safe-buffer').Buffer; +/**/ - // add compact value to map object using key from expanded value - // based on the container type - jsonld.addValue(mapObject, expandedItem[container], compactedItem); - } else { - // use an array if: compactArrays flag is false, - // @container is @set or @list , value is an empty - // array, or key is @graph - var isArray = (!options.compactArrays || container === '@set' || - container === '@list' || - (_isArray(compactedItem) && compactedItem.length === 0) || - expandedProperty === '@list' || expandedProperty === '@graph'); +var isEncoding = Buffer.isEncoding || function (encoding) { + encoding = '' + encoding; + switch (encoding && encoding.toLowerCase()) { + case 'hex':case 'utf8':case 'utf-8':case 'ascii':case 'binary':case 'base64':case 'ucs2':case 'ucs-2':case 'utf16le':case 'utf-16le':case 'raw': + return true; + default: + return false; + } +}; - // add compact value - jsonld.addValue( - rval, itemActiveProperty, compactedItem, - {propertyIsArray: isArray}); - } - } +function _normalizeEncoding(enc) { + if (!enc) return 'utf8'; + var retried; + while (true) { + switch (enc) { + case 'utf8': + case 'utf-8': + return 'utf8'; + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return 'utf16le'; + case 'latin1': + case 'binary': + return 'latin1'; + case 'base64': + case 'ascii': + case 'hex': + return enc; + default: + if (retried) return; // undefined + enc = ('' + enc).toLowerCase(); + retried = true; } + } +}; - return rval; +// Do not cache `Buffer.isEncoding` when checking encoding names as some +// modules monkey-patch it to support additional encodings +function normalizeEncoding(enc) { + var nenc = _normalizeEncoding(enc); + if (typeof nenc !== 'string' && (Buffer.isEncoding === isEncoding || !isEncoding(enc))) throw new Error('Unknown encoding: ' + enc); + return nenc || enc; +} + +// StringDecoder provides an interface for efficiently splitting a series of +// buffers into a series of JS strings without breaking apart multi-byte +// characters. +exports.StringDecoder = StringDecoder; +function StringDecoder(encoding) { + this.encoding = normalizeEncoding(encoding); + var nb; + switch (this.encoding) { + case 'utf16le': + this.text = utf16Text; + this.end = utf16End; + nb = 4; + break; + case 'utf8': + this.fillLast = utf8FillLast; + nb = 4; + break; + case 'base64': + this.text = base64Text; + this.end = base64End; + nb = 3; + break; + default: + this.write = simpleWrite; + this.end = simpleEnd; + return; } + this.lastNeed = 0; + this.lastTotal = 0; + this.lastChar = Buffer.allocUnsafe(nb); +} - // only primitives remain which are already compact - return element; +StringDecoder.prototype.write = function (buf) { + if (buf.length === 0) return ''; + var r; + var i; + if (this.lastNeed) { + r = this.fillLast(buf); + if (r === undefined) return ''; + i = this.lastNeed; + this.lastNeed = 0; + } else { + i = 0; + } + if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i); + return r || ''; }; -/** - * Recursively expands an element using the given context. Any context in - * the element will be removed. All context URLs must have been retrieved - * before calling this method. - * - * @param activeCtx the context to use. - * @param activeProperty the property for the element, null for none. - * @param element the element to expand. - * @param options the expansion options. - * @param insideList true if the element is a list, false if not. - * - * @return the expanded value. - */ -Processor.prototype.expand = function( - activeCtx, activeProperty, element, options, insideList) { - var self = this; +StringDecoder.prototype.end = utf8End; - // nothing to expand - if(element === null || element === undefined) { - return null; +// Returns only complete characters in a Buffer +StringDecoder.prototype.text = utf8Text; + +// Attempts to complete a partial non-UTF-8 character using bytes from a Buffer +StringDecoder.prototype.fillLast = function (buf) { + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); } + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); + this.lastNeed -= buf.length; +}; - if(!_isArray(element) && !_isObject(element)) { - // drop free-floating scalars that are not in lists - if(!insideList && (activeProperty === null || - _expandIri(activeCtx, activeProperty, {vocab: true}) === '@graph')) { - return null; - } +// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a +// continuation byte. If an invalid byte is detected, -2 is returned. +function utf8CheckByte(byte) { + if (byte <= 0x7F) return 0;else if (byte >> 5 === 0x06) return 2;else if (byte >> 4 === 0x0E) return 3;else if (byte >> 3 === 0x1E) return 4; + return byte >> 6 === 0x02 ? -1 : -2; +} - // expand element according to value expansion rules - return _expandValue(activeCtx, activeProperty, element); +// Checks at most 3 bytes at the end of a Buffer in order to detect an +// incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) +// needed to complete the UTF-8 character (if applicable) are returned. +function utf8CheckIncomplete(self, buf, i) { + var j = buf.length - 1; + if (j < i) return 0; + var nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 1; + return nb; + } + if (--j < i || nb === -2) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 2; + return nb; + } + if (--j < i || nb === -2) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) { + if (nb === 2) nb = 0;else self.lastNeed = nb - 3; + } + return nb; } + return 0; +} - // recursively expand array - if(_isArray(element)) { - var rval = []; - var container = jsonld.getContextValue( - activeCtx, activeProperty, '@container'); - insideList = insideList || container === '@list'; - for(var i = 0; i < element.length; ++i) { - // expand element - var e = self.expand(activeCtx, activeProperty, element[i], options); - if(insideList && (_isArray(e) || _isList(e))) { - // lists of lists are illegal - throw new JsonLdError( - 'Invalid JSON-LD syntax; lists of lists are not permitted.', - 'jsonld.SyntaxError', {code: 'list of lists'}); - } - // drop null values - if(e !== null) { - if(_isArray(e)) { - rval = rval.concat(e); - } else { - rval.push(e); - } +// Validates as many continuation bytes for a multi-byte UTF-8 character as +// needed or are available. If we see a non-continuation byte where we expect +// one, we "replace" the validated continuation bytes we've seen so far with +// a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding +// behavior. The continuation byte check is included three times in the case +// where all of the continuation bytes for a character exist in the same buffer. +// It is also done this way as a slight performance increase instead of using a +// loop. +function utf8CheckExtraBytes(self, buf, p) { + if ((buf[0] & 0xC0) !== 0x80) { + self.lastNeed = 0; + return '\ufffd'; + } + if (self.lastNeed > 1 && buf.length > 1) { + if ((buf[1] & 0xC0) !== 0x80) { + self.lastNeed = 1; + return '\ufffd'; + } + if (self.lastNeed > 2 && buf.length > 2) { + if ((buf[2] & 0xC0) !== 0x80) { + self.lastNeed = 2; + return '\ufffd'; } } - return rval; } +} - // recursively expand object: - - // if element has a context, process it - if('@context' in element) { - activeCtx = self.processContext(activeCtx, element['@context'], options); +// Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. +function utf8FillLast(buf) { + var p = this.lastTotal - this.lastNeed; + var r = utf8CheckExtraBytes(this, buf, p); + if (r !== undefined) return r; + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, p, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); } + buf.copy(this.lastChar, p, 0, buf.length); + this.lastNeed -= buf.length; +} - // expand the active property - var expandedActiveProperty = _expandIri( - activeCtx, activeProperty, {vocab: true}); +// Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a +// partial character, the character's bytes are buffered until the required +// number of bytes are available. +function utf8Text(buf, i) { + var total = utf8CheckIncomplete(this, buf, i); + if (!this.lastNeed) return buf.toString('utf8', i); + this.lastTotal = total; + var end = buf.length - (total - this.lastNeed); + buf.copy(this.lastChar, 0, end); + return buf.toString('utf8', i, end); +} - var rval = {}; - var keys = Object.keys(element).sort(); - for(var ki = 0; ki < keys.length; ++ki) { - var key = keys[ki]; - var value = element[key]; - var expandedValue; +// For UTF-8, a replacement character is added when ending on a partial +// character. +function utf8End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) return r + '\ufffd'; + return r; +} - // skip @context - if(key === '@context') { - continue; +// UTF-16LE typically needs two bytes per character, but even if we have an even +// number of bytes available, we need to check if we end on a leading/high +// surrogate. In that case, we need to wait for the next two bytes in order to +// decode the last character properly. +function utf16Text(buf, i) { + if ((buf.length - i) % 2 === 0) { + var r = buf.toString('utf16le', i); + if (r) { + var c = r.charCodeAt(r.length - 1); + if (c >= 0xD800 && c <= 0xDBFF) { + this.lastNeed = 2; + this.lastTotal = 4; + this.lastChar[0] = buf[buf.length - 2]; + this.lastChar[1] = buf[buf.length - 1]; + return r.slice(0, -1); + } } + return r; + } + this.lastNeed = 1; + this.lastTotal = 2; + this.lastChar[0] = buf[buf.length - 1]; + return buf.toString('utf16le', i, buf.length - 1); +} - // expand property - var expandedProperty = _expandIri(activeCtx, key, {vocab: true}); +// For UTF-16LE we do not explicitly append special replacement characters if we +// end on a partial character, we simply let v8 handle that. +function utf16End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) { + var end = this.lastTotal - this.lastNeed; + return r + this.lastChar.toString('utf16le', 0, end); + } + return r; +} - // drop non-absolute IRI keys that aren't keywords - if(expandedProperty === null || - !(_isAbsoluteIri(expandedProperty) || _isKeyword(expandedProperty))) { - continue; - } +function base64Text(buf, i) { + var n = (buf.length - i) % 3; + if (n === 0) return buf.toString('base64', i); + this.lastNeed = 3 - n; + this.lastTotal = 3; + if (n === 1) { + this.lastChar[0] = buf[buf.length - 1]; + } else { + this.lastChar[0] = buf[buf.length - 2]; + this.lastChar[1] = buf[buf.length - 1]; + } + return buf.toString('base64', i, buf.length - n); +} - if(_isKeyword(expandedProperty)) { - if(expandedActiveProperty === '@reverse') { - throw new JsonLdError( - 'Invalid JSON-LD syntax; a keyword cannot be used as a @reverse ' + - 'property.', 'jsonld.SyntaxError', - {code: 'invalid reverse property map', value: value}); - } - if(expandedProperty in rval) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; colliding keywords detected.', - 'jsonld.SyntaxError', - {code: 'colliding keywords', keyword: expandedProperty}); - } - } +function base64End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) return r + this.lastChar.toString('base64', 0, 3 - this.lastNeed); + return r; +} - // syntax error if @id is not a string - if(expandedProperty === '@id' && !_isString(value)) { - if(!options.isFrame) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; "@id" value must a string.', - 'jsonld.SyntaxError', {code: 'invalid @id value', value: value}); - } - if(!_isObject(value)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; "@id" value must be a string or an ' + - 'object.', 'jsonld.SyntaxError', - {code: 'invalid @id value', value: value}); - } - } +// Pass bytes on through for single-byte encodings (e.g. ascii, latin1, hex) +function simpleWrite(buf) { + return buf.toString(this.encoding); +} - if(expandedProperty === '@type') { - _validateTypeValue(value); - } +function simpleEnd(buf) { + return buf && buf.length ? this.write(buf) : ''; +} +},{"safe-buffer":26}],22:[function(_dereq_,module,exports){ +module.exports = _dereq_('./readable').PassThrough - // @graph must be an array or an object - if(expandedProperty === '@graph' && - !(_isObject(value) || _isArray(value))) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; "@graph" value must not be an ' + - 'object or an array.', - 'jsonld.SyntaxError', {code: 'invalid @graph value', value: value}); - } +},{"./readable":23}],23:[function(_dereq_,module,exports){ +exports = module.exports = _dereq_('./lib/_stream_readable.js'); +exports.Stream = exports; +exports.Readable = exports; +exports.Writable = _dereq_('./lib/_stream_writable.js'); +exports.Duplex = _dereq_('./lib/_stream_duplex.js'); +exports.Transform = _dereq_('./lib/_stream_transform.js'); +exports.PassThrough = _dereq_('./lib/_stream_passthrough.js'); - // @value must not be an object or an array - if(expandedProperty === '@value' && - (_isObject(value) || _isArray(value))) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; "@value" value must not be an ' + - 'object or an array.', - 'jsonld.SyntaxError', - {code: 'invalid value object value', value: value}); - } +},{"./lib/_stream_duplex.js":12,"./lib/_stream_passthrough.js":13,"./lib/_stream_readable.js":14,"./lib/_stream_transform.js":15,"./lib/_stream_writable.js":16}],24:[function(_dereq_,module,exports){ +module.exports = _dereq_('./readable').Transform - // @language must be a string - if(expandedProperty === '@language') { - if(value === null) { - // drop null @language values, they expand as if they didn't exist - continue; - } - if(!_isString(value)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; "@language" value must be a string.', - 'jsonld.SyntaxError', - {code: 'invalid language-tagged string', value: value}); - } - // ensure language value is lowercase - value = value.toLowerCase(); - } +},{"./readable":23}],25:[function(_dereq_,module,exports){ +module.exports = _dereq_('./lib/_stream_writable.js'); - // @index must be a string - if(expandedProperty === '@index') { - if(!_isString(value)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; "@index" value must be a string.', - 'jsonld.SyntaxError', - {code: 'invalid @index value', value: value}); - } - } +},{"./lib/_stream_writable.js":16}],26:[function(_dereq_,module,exports){ +/* eslint-disable node/no-deprecated-api */ +var buffer = _dereq_('buffer') +var Buffer = buffer.Buffer - // @reverse must be an object - if(expandedProperty === '@reverse') { - if(!_isObject(value)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; "@reverse" value must be an object.', - 'jsonld.SyntaxError', {code: 'invalid @reverse value', value: value}); - } +// alternative to using Object.keys for old browsers +function copyProps (src, dst) { + for (var key in src) { + dst[key] = src[key] + } +} +if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { + module.exports = buffer +} else { + // Copy properties from require('buffer') + copyProps(buffer, exports) + exports.Buffer = SafeBuffer +} - expandedValue = self.expand(activeCtx, '@reverse', value, options); +function SafeBuffer (arg, encodingOrOffset, length) { + return Buffer(arg, encodingOrOffset, length) +} - // properties double-reversed - if('@reverse' in expandedValue) { - for(var property in expandedValue['@reverse']) { - jsonld.addValue( - rval, property, expandedValue['@reverse'][property], - {propertyIsArray: true}); - } - } +// Copy static methods from Buffer +copyProps(Buffer, SafeBuffer) - // FIXME: can this be merged with code below to simplify? - // merge in all reversed properties - var reverseMap = rval['@reverse'] || null; - for(var property in expandedValue) { - if(property === '@reverse') { - continue; - } - if(reverseMap === null) { - reverseMap = rval['@reverse'] = {}; - } - jsonld.addValue(reverseMap, property, [], {propertyIsArray: true}); - var items = expandedValue[property]; - for(var ii = 0; ii < items.length; ++ii) { - var item = items[ii]; - if(_isValue(item) || _isList(item)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; "@reverse" value must not be a ' + - '@value or an @list.', 'jsonld.SyntaxError', - {code: 'invalid reverse property value', value: expandedValue}); - } - jsonld.addValue( - reverseMap, property, item, {propertyIsArray: true}); - } - } - - continue; - } - - var container = jsonld.getContextValue(activeCtx, key, '@container'); +SafeBuffer.from = function (arg, encodingOrOffset, length) { + if (typeof arg === 'number') { + throw new TypeError('Argument must not be a number') + } + return Buffer(arg, encodingOrOffset, length) +} - if(container === '@language' && _isObject(value)) { - // handle language map container (skip if value is not an object) - expandedValue = _expandLanguageMap(value); - } else if(container === '@index' && _isObject(value)) { - // handle index container (skip if value is not an object) - expandedValue = (function _expandIndexMap(activeProperty) { - var rval = []; - var keys = Object.keys(value).sort(); - for(var ki = 0; ki < keys.length; ++ki) { - var key = keys[ki]; - var val = value[key]; - if(!_isArray(val)) { - val = [val]; - } - val = self.expand(activeCtx, activeProperty, val, options, false); - for(var vi = 0; vi < val.length; ++vi) { - var item = val[vi]; - if(!('@index' in item)) { - item['@index'] = key; - } - rval.push(item); - } - } - return rval; - })(key); +SafeBuffer.alloc = function (size, fill, encoding) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + var buf = Buffer(size) + if (fill !== undefined) { + if (typeof encoding === 'string') { + buf.fill(fill, encoding) } else { - // recurse into @list or @set - var isList = (expandedProperty === '@list'); - if(isList || expandedProperty === '@set') { - var nextActiveProperty = activeProperty; - if(isList && expandedActiveProperty === '@graph') { - nextActiveProperty = null; - } - expandedValue = self.expand( - activeCtx, nextActiveProperty, value, options, isList); - if(isList && _isList(expandedValue)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; lists of lists are not permitted.', - 'jsonld.SyntaxError', {code: 'list of lists'}); - } - } else { - // recursively expand value with key as new active property - expandedValue = self.expand(activeCtx, key, value, options, false); - } + buf.fill(fill) } + } else { + buf.fill(0) + } + return buf +} - // drop null values if property is not @value - if(expandedValue === null && expandedProperty !== '@value') { - continue; - } +SafeBuffer.allocUnsafe = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return Buffer(size) +} - // convert expanded value to @list if container specifies it - if(expandedProperty !== '@list' && !_isList(expandedValue) && - container === '@list') { - // ensure expanded value is an array - expandedValue = (_isArray(expandedValue) ? - expandedValue : [expandedValue]); - expandedValue = {'@list': expandedValue}; - } +SafeBuffer.allocUnsafeSlow = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return buffer.SlowBuffer(size) +} - // FIXME: can this be merged with code above to simplify? - // merge in reverse properties - if(activeCtx.mappings[key] && activeCtx.mappings[key].reverse) { - var reverseMap = rval['@reverse'] = rval['@reverse'] || {}; - if(!_isArray(expandedValue)) { - expandedValue = [expandedValue]; - } - for(var ii = 0; ii < expandedValue.length; ++ii) { - var item = expandedValue[ii]; - if(_isValue(item) || _isList(item)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; "@reverse" value must not be a ' + - '@value or an @list.', 'jsonld.SyntaxError', - {code: 'invalid reverse property value', value: expandedValue}); - } - jsonld.addValue( - reverseMap, expandedProperty, item, {propertyIsArray: true}); - } - continue; - } +},{"buffer":5}],27:[function(_dereq_,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. - // add value for property - // use an array except for certain keywords - var useArray = - ['@index', '@id', '@type', '@value', '@language'].indexOf( - expandedProperty) === -1; - jsonld.addValue( - rval, expandedProperty, expandedValue, {propertyIsArray: useArray}); - } +module.exports = Stream; - // get property count on expanded output - keys = Object.keys(rval); - var count = keys.length; +var EE = _dereq_('events').EventEmitter; +var inherits = _dereq_('inherits'); - if('@value' in rval) { - // @value must only have @language or @type - if('@type' in rval && '@language' in rval) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; an element containing "@value" may not ' + - 'contain both "@type" and "@language".', - 'jsonld.SyntaxError', {code: 'invalid value object', element: rval}); - } - var validCount = count - 1; - if('@type' in rval) { - validCount -= 1; - } - if('@index' in rval) { - validCount -= 1; - } - if('@language' in rval) { - validCount -= 1; - } - if(validCount !== 0) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; an element containing "@value" may only ' + - 'have an "@index" property and at most one other property ' + - 'which can be "@type" or "@language".', - 'jsonld.SyntaxError', {code: 'invalid value object', element: rval}); - } - // drop null @values - if(rval['@value'] === null) { - rval = null; - } else if('@language' in rval && !_isString(rval['@value'])) { - // if @language is present, @value must be a string - throw new JsonLdError( - 'Invalid JSON-LD syntax; only strings may be language-tagged.', - 'jsonld.SyntaxError', - {code: 'invalid language-tagged value', element: rval}); - } else if('@type' in rval && (!_isAbsoluteIri(rval['@type']) || - rval['@type'].indexOf('_:') === 0)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; an element containing "@value" and "@type" ' + - 'must have an absolute IRI for the value of "@type".', - 'jsonld.SyntaxError', {code: 'invalid typed value', element: rval}); - } - } else if('@type' in rval && !_isArray(rval['@type'])) { - // convert @type to an array - rval['@type'] = [rval['@type']]; - } else if('@set' in rval || '@list' in rval) { - // handle @set and @list - if(count > 1 && !(count === 2 && '@index' in rval)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; if an element has the property "@set" ' + - 'or "@list", then it can have at most one other property that is ' + - '"@index".', 'jsonld.SyntaxError', - {code: 'invalid set or list object', element: rval}); - } - // optimize away @set - if('@set' in rval) { - rval = rval['@set']; - keys = Object.keys(rval); - count = keys.length; - } - } else if(count === 1 && '@language' in rval) { - // drop objects with only @language - rval = null; - } +inherits(Stream, EE); +Stream.Readable = _dereq_('readable-stream/readable.js'); +Stream.Writable = _dereq_('readable-stream/writable.js'); +Stream.Duplex = _dereq_('readable-stream/duplex.js'); +Stream.Transform = _dereq_('readable-stream/transform.js'); +Stream.PassThrough = _dereq_('readable-stream/passthrough.js'); - // drop certain top-level objects that do not occur in lists - if(_isObject(rval) && - !options.keepFreeFloatingNodes && !insideList && - (activeProperty === null || expandedActiveProperty === '@graph')) { - // drop empty object, top-level @value/@list, or object with only @id - if(count === 0 || '@value' in rval || '@list' in rval || - (count === 1 && '@id' in rval)) { - rval = null; - } - } +// Backwards-compat with node 0.4.x +Stream.Stream = Stream; - return rval; -}; -/** - * Creates a JSON-LD node map (node ID => node). - * - * @param input the expanded JSON-LD to create a node map of. - * @param [options] the options to use: - * [issuer] a jsonld.IdentifierIssuer to use to label blank nodes. - * [namer] (deprecated). - * - * @return the node map. - */ -Processor.prototype.createNodeMap = function(input, options) { - options = options || {}; - // produce a map of all subjects and name each bnode - var issuer = options.namer || options.issuer || new IdentifierIssuer('_:b'); - var graphs = {'@default': {}}; - _createNodeMap(input, graphs, '@default', issuer); +// old-style streams. Note that the pipe method (the only relevant +// part of this class) is overridden in the Readable class. - // add all non-default graphs to default graph - return _mergeNodeMaps(graphs); -}; +function Stream() { + EE.call(this); +} -/** - * Performs JSON-LD flattening. - * - * @param input the expanded JSON-LD to flatten. - * - * @return the flattened output. - */ -Processor.prototype.flatten = function(input) { - var defaultGraph = this.createNodeMap(input); +Stream.prototype.pipe = function(dest, options) { + var source = this; - // produce flattened output - var flattened = []; - var keys = Object.keys(defaultGraph).sort(); - for(var ki = 0; ki < keys.length; ++ki) { - var node = defaultGraph[keys[ki]]; - // only add full subjects to top-level - if(!_isSubjectReference(node)) { - flattened.push(node); + function ondata(chunk) { + if (dest.writable) { + if (false === dest.write(chunk) && source.pause) { + source.pause(); + } } } - return flattened; -}; -/** - * Performs JSON-LD framing. - * - * @param input the expanded JSON-LD to frame. - * @param frame the expanded JSON-LD frame to use. - * @param options the framing options. - * - * @return the framed output. - */ -Processor.prototype.frame = function(input, frame, options) { - // create framing state - var state = { - options: options, - graphs: {'@default': {}, '@merged': {}}, - subjectStack: [], - link: {} - }; + source.on('data', ondata); - // produce a map of all graphs and name each bnode - // FIXME: currently uses subjects from @merged graph only - var issuer = new IdentifierIssuer('_:b'); - _createNodeMap(input, state.graphs, '@merged', issuer); - state.subjects = state.graphs['@merged']; + function ondrain() { + if (source.readable && source.resume) { + source.resume(); + } + } - // frame the subjects - var framed = []; - _frame(state, Object.keys(state.subjects).sort(), frame, framed, null); - return framed; -}; + dest.on('drain', ondrain); -/** - * Performs normalization on the given RDF dataset. - * - * @param dataset the RDF dataset to normalize. - * @param options the normalization options. - * @param callback(err, normalized) called once the operation completes. - */ -Processor.prototype.normalize = function(dataset, options, callback) { - if(options.algorithm === 'URDNA2015') { - return new URDNA2015(options).main(dataset, callback); + // If the 'end' option is not supplied, dest.end() will be called when + // source gets the 'end' or 'close' events. Only dest.end() once. + if (!dest._isStdio && (!options || options.end !== false)) { + source.on('end', onend); + source.on('close', onclose); } - if(options.algorithm === 'URGNA2012') { - return new URGNA2012(options).main(dataset, callback); + + var didOnEnd = false; + function onend() { + if (didOnEnd) return; + didOnEnd = true; + + dest.end(); } - callback(new Error( - 'Invalid RDF Dataset Normalization algorithm: ' + options.algorithm)); -}; -/** - * Converts an RDF dataset to JSON-LD. - * - * @param dataset the RDF dataset. - * @param options the RDF serialization options. - * @param callback(err, output) called once the operation completes. - */ -Processor.prototype.fromRDF = function(dataset, options, callback) { - var defaultGraph = {}; - var graphMap = {'@default': defaultGraph}; - var referencedOnce = {}; - for(var name in dataset) { - var graph = dataset[name]; - if(!(name in graphMap)) { - graphMap[name] = {}; - } - if(name !== '@default' && !(name in defaultGraph)) { - defaultGraph[name] = {'@id': name}; + function onclose() { + if (didOnEnd) return; + didOnEnd = true; + + if (typeof dest.destroy === 'function') dest.destroy(); + } + + // don't leave dangling pipes when there are errors. + function onerror(er) { + cleanup(); + if (EE.listenerCount(this, 'error') === 0) { + throw er; // Unhandled stream error in pipe. } - var nodeMap = graphMap[name]; - for(var ti = 0; ti < graph.length; ++ti) { - var triple = graph[ti]; + } - // get subject, predicate, object - var s = triple.subject.value; - var p = triple.predicate.value; - var o = triple.object; + source.on('error', onerror); + dest.on('error', onerror); - if(!(s in nodeMap)) { - nodeMap[s] = {'@id': s}; - } - var node = nodeMap[s]; + // remove all the event listeners that were added. + function cleanup() { + source.removeListener('data', ondata); + dest.removeListener('drain', ondrain); - var objectIsId = (o.type === 'IRI' || o.type === 'blank node'); - if(objectIsId && !(o.value in nodeMap)) { - nodeMap[o.value] = {'@id': o.value}; - } + source.removeListener('end', onend); + source.removeListener('close', onclose); - if(p === RDF_TYPE && !options.useRdfType && objectIsId) { - jsonld.addValue(node, '@type', o.value, {propertyIsArray: true}); - continue; - } + source.removeListener('error', onerror); + dest.removeListener('error', onerror); - var value = _RDFToObject(o, options.useNativeTypes); - jsonld.addValue(node, p, value, {propertyIsArray: true}); + source.removeListener('end', cleanup); + source.removeListener('close', cleanup); - // object may be an RDF list/partial list node but we can't know easily - // until all triples are read - if(objectIsId) { - if(o.value === RDF_NIL) { - // track rdf:nil uniquely per graph - var object = nodeMap[o.value]; - if(!('usages' in object)) { - object.usages = []; - } - object.usages.push({ - node: node, - property: p, - value: value - }); - } else if(o.value in referencedOnce) { - // object referenced more than once - referencedOnce[o.value] = false; - } else { - // keep track of single reference - referencedOnce[o.value] = { - node: node, - property: p, - value: value - }; - } - } - } + dest.removeListener('close', cleanup); } - // convert linked lists to @list arrays - for(var name in graphMap) { - var graphObject = graphMap[name]; + source.on('end', cleanup); + source.on('close', cleanup); - // no @lists to be converted, continue - if(!(RDF_NIL in graphObject)) { - continue; - } + dest.on('close', cleanup); - // iterate backwards through each RDF list - var nil = graphObject[RDF_NIL]; - for(var i = 0; i < nil.usages.length; ++i) { - var usage = nil.usages[i]; - var node = usage.node; - var property = usage.property; - var head = usage.value; - var list = []; - var listNodes = []; + dest.emit('pipe', source); - // ensure node is a well-formed list node; it must: - // 1. Be referenced only once. - // 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, rdf:first, rdf:rest, and, - // optionally, @type where the value is rdf:List. - var nodeKeyCount = Object.keys(node).length; - while(property === RDF_REST && - _isObject(referencedOnce[node['@id']]) && - _isArray(node[RDF_FIRST]) && node[RDF_FIRST].length === 1 && - _isArray(node[RDF_REST]) && node[RDF_REST].length === 1 && - (nodeKeyCount === 3 || (nodeKeyCount === 4 && _isArray(node['@type']) && - node['@type'].length === 1 && node['@type'][0] === RDF_LIST))) { - list.push(node[RDF_FIRST][0]); - listNodes.push(node['@id']); + // Allow for unix-like usage: A.pipe(B).pipe(C) + return dest; +}; - // get next node, moving backwards through list - usage = referencedOnce[node['@id']]; - node = usage.node; - property = usage.property; - head = usage.value; - nodeKeyCount = Object.keys(node).length; +},{"events":8,"inherits":43,"readable-stream/duplex.js":11,"readable-stream/passthrough.js":22,"readable-stream/readable.js":23,"readable-stream/transform.js":24,"readable-stream/writable.js":25}],28:[function(_dereq_,module,exports){ +var ClientRequest = _dereq_('./lib/request') +var extend = _dereq_('xtend') +var statusCodes = _dereq_('builtin-status-codes') +var url = _dereq_('url') - // if node is not a blank node, then list head found - if(node['@id'].indexOf('_:') !== 0) { - break; - } - } +var http = exports - // the list is nested in another list - if(property === RDF_FIRST) { - // empty list - if(node['@id'] === RDF_NIL) { - // can't convert rdf:nil to a @list object because it would - // result in a list of lists which isn't supported - continue; - } - - // preserve list head - head = graphObject[head['@id']][RDF_REST][0]; - list.pop(); - listNodes.pop(); - } +http.request = function (opts, cb) { + if (typeof opts === 'string') + opts = url.parse(opts) + else + opts = extend(opts) - // transform list into @list object - delete head['@id']; - head['@list'] = list.reverse(); - for(var j = 0; j < listNodes.length; ++j) { - delete graphObject[listNodes[j]]; - } - } + var protocol = opts.protocol || '' + var host = opts.hostname || opts.host + var port = opts.port + var path = opts.path || '/' - delete nil.usages; - } + // Necessary for IPv6 addresses + if (host && host.indexOf(':') !== -1) + host = '[' + host + ']' - var result = []; - var subjects = Object.keys(defaultGraph).sort(); - for(var i = 0; i < subjects.length; ++i) { - var subject = subjects[i]; - var node = defaultGraph[subject]; - if(subject in graphMap) { - var graph = node['@graph'] = []; - var graphObject = graphMap[subject]; - var subjects_ = Object.keys(graphObject).sort(); - for(var si = 0; si < subjects_.length; ++si) { - var node_ = graphObject[subjects_[si]]; - // only add full subjects to top-level - if(!_isSubjectReference(node_)) { - graph.push(node_); - } - } - } - // only add full subjects to top-level - if(!_isSubjectReference(node)) { - result.push(node); - } - } + // This may be a relative url. The browser should always be able to interpret it correctly. + opts.url = (host ? (protocol + '//' + host) : '') + (port ? ':' + port : '') + path + opts.method = (opts.method || 'GET').toUpperCase() + opts.headers = opts.headers || {} - callback(null, result); -}; + // Also valid opts.auth, opts.mode -/** - * Outputs an RDF dataset for the expanded JSON-LD input. - * - * @param input the expanded JSON-LD input. - * @param options the RDF serialization options. - * - * @return the RDF dataset. - */ -Processor.prototype.toRDF = function(input, options) { - // create node map for default graph (and any named graphs) - var issuer = new IdentifierIssuer('_:b'); - var nodeMap = {'@default': {}}; - _createNodeMap(input, nodeMap, '@default', issuer); + var req = new ClientRequest(opts) + if (cb) + req.on('response', cb) + return req +} - var dataset = {}; - var graphNames = Object.keys(nodeMap).sort(); - for(var i = 0; i < graphNames.length; ++i) { - var graphName = graphNames[i]; - // skip relative IRIs - if(graphName === '@default' || _isAbsoluteIri(graphName)) { - dataset[graphName] = _graphToRDF(nodeMap[graphName], issuer, options); - } - } - return dataset; -}; +http.get = function get (opts, cb) { + var req = http.request(opts, cb) + req.end() + return req +} -/** - * Processes a local context and returns a new active context. - * - * @param activeCtx the current active context. - * @param localCtx the local context to process. - * @param options the context processing options. - * - * @return the new active context. - */ -Processor.prototype.processContext = function(activeCtx, localCtx, options) { - // normalize local context to an array of @context objects - if(_isObject(localCtx) && '@context' in localCtx && - _isArray(localCtx['@context'])) { - localCtx = localCtx['@context']; - } - var ctxs = _isArray(localCtx) ? localCtx : [localCtx]; +http.Agent = function () {} +http.Agent.defaultMaxSockets = 4 - // no contexts in array, clone existing context - if(ctxs.length === 0) { - return activeCtx.clone(); - } +http.STATUS_CODES = statusCodes - // process each context in order, update active context - // on each iteration to ensure proper caching - var rval = activeCtx; - for(var i = 0; i < ctxs.length; ++i) { - var ctx = ctxs[i]; +http.METHODS = [ + 'CHECKOUT', + 'CONNECT', + 'COPY', + 'DELETE', + 'GET', + 'HEAD', + 'LOCK', + 'M-SEARCH', + 'MERGE', + 'MKACTIVITY', + 'MKCOL', + 'MOVE', + 'NOTIFY', + 'OPTIONS', + 'PATCH', + 'POST', + 'PROPFIND', + 'PROPPATCH', + 'PURGE', + 'PUT', + 'REPORT', + 'SEARCH', + 'SUBSCRIBE', + 'TRACE', + 'UNLOCK', + 'UNSUBSCRIBE' +] +},{"./lib/request":30,"builtin-status-codes":7,"url":34,"xtend":187}],29:[function(_dereq_,module,exports){ +(function (global){ +exports.fetch = isFunction(global.fetch) && isFunction(global.ReadableByteStream) - // reset to initial context - if(ctx === null) { - rval = activeCtx = _getInitialContext(options); - continue; - } +exports.blobConstructor = false +try { + new Blob([new ArrayBuffer(1)]) + exports.blobConstructor = true +} catch (e) {} - // dereference @context key if present - if(_isObject(ctx) && '@context' in ctx) { - ctx = ctx['@context']; - } +var xhr = new global.XMLHttpRequest() +// If location.host is empty, e.g. if this page/worker was loaded +// from a Blob, then use example.com to avoid an error +xhr.open('GET', global.location.host ? '/' : 'https://example.com') - // context must be an object by now, all URLs retrieved before this call - if(!_isObject(ctx)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; @context must be an object.', - 'jsonld.SyntaxError', {code: 'invalid local context', context: ctx}); - } +function checkTypeSupport (type) { + try { + xhr.responseType = type + return xhr.responseType === type + } catch (e) {} + return false +} - // get context from cache if available - if(jsonld.cache.activeCtx) { - var cached = jsonld.cache.activeCtx.get(activeCtx, ctx); - if(cached) { - rval = activeCtx = cached; - continue; - } - } +// For some strange reason, Safari 7.0 reports typeof global.ArrayBuffer === 'object'. +// Safari 7.1 appears to have fixed this bug. +var haveArrayBuffer = typeof global.ArrayBuffer !== 'undefined' +var haveSlice = haveArrayBuffer && isFunction(global.ArrayBuffer.prototype.slice) - // update active context and clone new one before updating - activeCtx = rval; - rval = rval.clone(); +exports.arraybuffer = haveArrayBuffer && checkTypeSupport('arraybuffer') +// These next two tests unavoidably show warnings in Chrome. Since fetch will always +// be used if it's available, just return false for these to avoid the warnings. +exports.msstream = !exports.fetch && haveSlice && checkTypeSupport('ms-stream') +exports.mozchunkedarraybuffer = !exports.fetch && haveArrayBuffer && + checkTypeSupport('moz-chunked-arraybuffer') +exports.overrideMimeType = isFunction(xhr.overrideMimeType) +exports.vbArray = isFunction(global.VBArray) - // define context mappings for keys in local context - var defined = {}; +function isFunction (value) { + return typeof value === 'function' +} - // handle @base - if('@base' in ctx) { - var base = ctx['@base']; +xhr = null // Help gc - // clear base - if(base === null) { - base = null; - } else if(!_isString(base)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; the value of "@base" in a ' + - '@context must be a string or null.', - 'jsonld.SyntaxError', {code: 'invalid base IRI', context: ctx}); - } else if(base !== '' && !_isAbsoluteIri(base)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; the value of "@base" in a ' + - '@context must be an absolute IRI or the empty string.', - 'jsonld.SyntaxError', {code: 'invalid base IRI', context: ctx}); - } +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - if(base !== null) { - base = jsonld.url.parse(base || ''); - } - rval['@base'] = base; - defined['@base'] = true; - } +},{}],30:[function(_dereq_,module,exports){ +(function (process,global,Buffer){ +// var Base64 = require('Base64') +var capability = _dereq_('./capability') +var foreach = _dereq_('foreach') +var indexOf = _dereq_('indexof') +var inherits = _dereq_('inherits') +var keys = _dereq_('object-keys') +var response = _dereq_('./response') +var stream = _dereq_('stream') - // handle @vocab - if('@vocab' in ctx) { - var value = ctx['@vocab']; - if(value === null) { - delete rval['@vocab']; - } else if(!_isString(value)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; the value of "@vocab" in a ' + - '@context must be a string or null.', - 'jsonld.SyntaxError', {code: 'invalid vocab mapping', context: ctx}); - } else if(!_isAbsoluteIri(value)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; the value of "@vocab" in a ' + - '@context must be an absolute IRI.', - 'jsonld.SyntaxError', {code: 'invalid vocab mapping', context: ctx}); - } else { - rval['@vocab'] = value; - } - defined['@vocab'] = true; - } +var IncomingMessage = response.IncomingMessage +var rStates = response.readyStates - // handle @language - if('@language' in ctx) { - var value = ctx['@language']; - if(value === null) { - delete rval['@language']; - } else if(!_isString(value)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; the value of "@language" in a ' + - '@context must be a string or null.', - 'jsonld.SyntaxError', - {code: 'invalid default language', context: ctx}); - } else { - rval['@language'] = value.toLowerCase(); - } - defined['@language'] = true; - } +function decideMode (preferBinary) { + if (capability.fetch) { + return 'fetch' + } else if (capability.mozchunkedarraybuffer) { + return 'moz-chunked-arraybuffer' + } else if (capability.msstream) { + return 'ms-stream' + } else if (capability.arraybuffer && preferBinary) { + return 'arraybuffer' + } else if (capability.vbArray && preferBinary) { + return 'text:vbarray' + } else { + return 'text' + } +} - // process all other keys - for(var key in ctx) { - _createTermDefinition(rval, ctx, key, defined); - } +var ClientRequest = module.exports = function (opts) { + var self = this + stream.Writable.call(self) - // cache result - if(jsonld.cache.activeCtx) { - jsonld.cache.activeCtx.set(activeCtx, ctx, rval); - } - } + self._opts = opts + self._body = [] + self._headers = {} + if (opts.auth) + self.setHeader('Authorization', 'Basic ' + new Buffer(opts.auth).toString('base64')) + foreach(keys(opts.headers), function (name) { + self.setHeader(name, opts.headers[name]) + }) - return rval; -}; + var preferBinary + if (opts.mode === 'prefer-streaming') { + // If streaming is a high priority but binary compatibility and + // the accuracy of the 'content-type' header aren't + preferBinary = false + } else if (opts.mode === 'allow-wrong-content-type') { + // If streaming is more important than preserving the 'content-type' header + preferBinary = !capability.overrideMimeType + } else if (!opts.mode || opts.mode === 'default' || opts.mode === 'prefer-fast') { + // Use binary if text streaming may corrupt data or the content-type header, or for speed + preferBinary = true + } else { + throw new Error('Invalid value for opts.mode') + } + self._mode = decideMode(preferBinary) -/** - * Expands a language map. - * - * @param languageMap the language map to expand. - * - * @return the expanded language map. - */ -function _expandLanguageMap(languageMap) { - var rval = []; - var keys = Object.keys(languageMap).sort(); - for(var ki = 0; ki < keys.length; ++ki) { - var key = keys[ki]; - var val = languageMap[key]; - if(!_isArray(val)) { - val = [val]; - } - for(var vi = 0; vi < val.length; ++vi) { - var item = val[vi]; - if(item === null) { - // null values are allowed (8.5) but ignored (3.1) - continue; - } - if(!_isString(item)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; language map values must be strings.', - 'jsonld.SyntaxError', - {code: 'invalid language map value', languageMap: languageMap}); - } - rval.push({ - '@value': item, - '@language': key.toLowerCase() - }); - } - } - return rval; + self.on('finish', function () { + self._onFinish() + }) } -/** - * Labels the blank nodes in the given value using the given IdentifierIssuer. - * - * @param issuer the IdentifierIssuer to use. - * @param element the element with blank nodes to rename. - * - * @return the element. - */ -function _labelBlankNodes(issuer, element) { - if(_isArray(element)) { - for(var i = 0; i < element.length; ++i) { - element[i] = _labelBlankNodes(issuer, element[i]); - } - } else if(_isList(element)) { - element['@list'] = _labelBlankNodes(issuer, element['@list']); - } else if(_isObject(element)) { - // relabel blank node - if(_isBlankNode(element)) { - element['@id'] = issuer.getId(element['@id']); - } +inherits(ClientRequest, stream.Writable) - // recursively apply to all keys - var keys = Object.keys(element).sort(); - for(var ki = 0; ki < keys.length; ++ki) { - var key = keys[ki]; - if(key !== '@id') { - element[key] = _labelBlankNodes(issuer, element[key]); - } - } - } +ClientRequest.prototype.setHeader = function (name, value) { + var self = this + var lowerName = name.toLowerCase() + // This check is not necessary, but it prevents warnings from browsers about setting unsafe + // headers. To be honest I'm not entirely sure hiding these warnings is a good thing, but + // http-browserify did it, so I will too. + if (indexOf(unsafeHeaders, lowerName) !== -1) + return - return element; + self._headers[lowerName] = { + name: name, + value: value + } } -/** - * Expands the given value by using the coercion and keyword rules in the - * given context. - * - * @param activeCtx the active context to use. - * @param activeProperty the active property the value is associated with. - * @param value the value to expand. - * - * @return the expanded value. - */ -function _expandValue(activeCtx, activeProperty, value) { - // nothing to expand - if(value === null || value === undefined) { - return null; - } - - // special-case expand @id and @type (skips '@id' expansion) - var expandedProperty = _expandIri(activeCtx, activeProperty, {vocab: true}); - if(expandedProperty === '@id') { - return _expandIri(activeCtx, value, {base: true}); - } else if(expandedProperty === '@type') { - return _expandIri(activeCtx, value, {vocab: true, base: true}); - } - - // get type definition from context - var type = jsonld.getContextValue(activeCtx, activeProperty, '@type'); +ClientRequest.prototype.getHeader = function (name) { + var self = this + return self._headers[name.toLowerCase()].value +} - // do @id expansion (automatic for @graph) - if(type === '@id' || (expandedProperty === '@graph' && _isString(value))) { - return {'@id': _expandIri(activeCtx, value, {base: true})}; - } - // do @id expansion w/vocab - if(type === '@vocab') { - return {'@id': _expandIri(activeCtx, value, {vocab: true, base: true})}; - } +ClientRequest.prototype.removeHeader = function (name) { + var self = this + delete self._headers[name.toLowerCase()] +} - // do not expand keyword values - if(_isKeyword(expandedProperty)) { - return value; - } +ClientRequest.prototype._onFinish = function () { + var self = this - var rval = {}; + if (self._destroyed) + return + var opts = self._opts - if(type !== null) { - // other type - rval['@type'] = type; - } else if(_isString(value)) { - // check for language tagging for strings - var language = jsonld.getContextValue( - activeCtx, activeProperty, '@language'); - if(language !== null) { - 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; + var headersObj = self._headers + var body + if (opts.method === 'POST' || opts.method === 'PUT') { + if (capability.blobConstructor) { + body = new global.Blob(self._body.map(function (buffer) { + return buffer.toArrayBuffer() + }), { + type: (headersObj['content-type'] || {}).value || '' + }) + } else { + // get utf8 string + body = Buffer.concat(self._body).toString() + } + } - return rval; -} + if (self._mode === 'fetch') { + var headers = keys(headersObj).map(function (name) { + return [headersObj[name].name, headersObj[name].value] + }) -/** - * Creates an array of RDF triples for the given graph. - * - * @param graph the graph to create RDF triples for. - * @param issuer a IdentifierIssuer for assigning blank node names. - * @param options the RDF serialization options. - * - * @return the array of RDF triples for the given graph. - */ -function _graphToRDF(graph, issuer, options) { - var rval = []; - - var ids = Object.keys(graph).sort(); - for(var i = 0; i < ids.length; ++i) { - var id = ids[i]; - var node = graph[id]; - var properties = Object.keys(node).sort(); - for(var pi = 0; pi < properties.length; ++pi) { - var property = properties[pi]; - var items = node[property]; - if(property === '@type') { - property = RDF_TYPE; - } else if(_isKeyword(property)) { - continue; - } - - for(var ii = 0; ii < items.length; ++ii) { - var item = items[ii]; + global.fetch(self._opts.url, { + method: self._opts.method, + headers: headers, + body: body, + mode: 'cors', + credentials: opts.withCredentials ? 'include' : 'same-origin' + }).then(function (response) { + self._fetchResponse = response + self._connect() + }).then(undefined, function (reason) { + self.emit('error', reason) + }) + } else { + var xhr = self._xhr = new global.XMLHttpRequest() + try { + xhr.open(self._opts.method, self._opts.url, true) + } catch (err) { + process.nextTick(function () { + self.emit('error', err) + }) + return + } - // RDF subject - var subject = {}; - subject.type = (id.indexOf('_:') === 0) ? 'blank node' : 'IRI'; - subject.value = id; + // Can't set responseType on really old browsers + if ('responseType' in xhr) + xhr.responseType = self._mode.split(':')[0] - // skip relative IRI subjects - if(!_isAbsoluteIri(id)) { - continue; - } + if ('withCredentials' in xhr) + xhr.withCredentials = !!opts.withCredentials - // RDF predicate - var predicate = {}; - predicate.type = (property.indexOf('_:') === 0) ? 'blank node' : 'IRI'; - predicate.value = property; + if (self._mode === 'text' && 'overrideMimeType' in xhr) + xhr.overrideMimeType('text/plain; charset=x-user-defined') - // skip relative IRI predicates - if(!_isAbsoluteIri(property)) { - continue; - } + foreach(keys(headersObj), function (name) { + xhr.setRequestHeader(headersObj[name].name, headersObj[name].value) + }) - // skip blank node predicates unless producing generalized RDF - if(predicate.type === 'blank node' && !options.produceGeneralizedRdf) { - continue; - } + self._response = null + xhr.onreadystatechange = function () { + switch (xhr.readyState) { + case rStates.LOADING: + case rStates.DONE: + self._onXHRProgress() + break + } + } + // Necessary for streaming in Firefox, since xhr.response is ONLY defined + // in onprogress, not in onreadystatechange with xhr.readyState = 3 + if (self._mode === 'moz-chunked-arraybuffer') { + xhr.onprogress = function () { + self._onXHRProgress() + } + } - // convert @list to triples - if(_isList(item)) { - _listToRDF(item['@list'], issuer, subject, predicate, rval); - } else { - // convert value or node object to triple - var object = _objectToRDF(item); - // skip null objects (they are relative IRIs) - if(object) { - rval.push({subject: subject, predicate: predicate, object: object}); - } - } - } - } - } + xhr.onerror = function () { + if (self._destroyed) + return + self.emit('error', new Error('XHR error')) + } - return rval; + try { + xhr.send(body) + } catch (err) { + process.nextTick(function () { + self.emit('error', err) + }) + return + } + } } /** - * Converts a @list value into linked list of blank node RDF triples - * (an RDF collection). - * - * @param list the @list value. - * @param issuer a IdentifierIssuer for assigning blank node names. - * @param subject the subject for the head of the list. - * @param predicate the predicate for the head of the list. - * @param triples the array of triples to append to. + * Checks if xhr.status is readable. Even though the spec says it should + * be available in readyState 3, accessing it throws an exception in IE8 */ -function _listToRDF(list, issuer, subject, predicate, triples) { - var first = {type: 'IRI', value: RDF_FIRST}; - var rest = {type: 'IRI', value: RDF_REST}; - var nil = {type: 'IRI', value: RDF_NIL}; +function statusValid (xhr) { + try { + return (xhr.status !== null) + } catch (e) { + return false + } +} - for(var i = 0; i < list.length; ++i) { - var item = list[i]; +ClientRequest.prototype._onXHRProgress = function () { + var self = this - var blankNode = {type: 'blank node', value: issuer.getId()}; - triples.push({subject: subject, predicate: predicate, object: blankNode}); + if (!statusValid(self._xhr) || self._destroyed) + return - subject = blankNode; - predicate = first; - var object = _objectToRDF(item); + if (!self._response) + self._connect() - // skip null objects (they are relative IRIs) - if(object) { - triples.push({subject: subject, predicate: predicate, object: object}); - } + self._response._onXHRProgress() +} - predicate = rest; - } +ClientRequest.prototype._connect = function () { + var self = this - triples.push({subject: subject, predicate: predicate, object: nil}); + if (self._destroyed) + return + + self._response = new IncomingMessage(self._xhr, self._fetchResponse, self._mode) + self.emit('response', self._response) } -/** - * Converts a JSON-LD value object to an RDF literal or a JSON-LD string or - * node object to an RDF resource. - * - * @param item the JSON-LD value or node object. - * - * @return the RDF literal or RDF resource. - */ -function _objectToRDF(item) { - var object = {}; +ClientRequest.prototype._write = function (chunk, encoding, cb) { + var self = this - // convert value object to RDF - if(_isValue(item)) { - object.type = 'literal'; - var value = item['@value']; - var datatype = item['@type'] || null; + self._body.push(chunk) + cb() +} - // convert to XSD datatypes as appropriate - if(_isBoolean(value)) { - object.value = value.toString(); - object.datatype = datatype || XSD_BOOLEAN; - } else if(_isDouble(value) || datatype === XSD_DOUBLE) { - if(!_isDouble(value)) { - value = parseFloat(value); - } - // canonical double representation - object.value = value.toExponential(15).replace(/(\d)0*e\+?/, '$1E'); - object.datatype = datatype || XSD_DOUBLE; - } else if(_isNumber(value)) { - object.value = value.toFixed(0); - object.datatype = datatype || XSD_INTEGER; - } else if('@language' in item) { - object.value = value; - object.datatype = datatype || RDF_LANGSTRING; - object.language = item['@language']; - } else { - object.value = value; - object.datatype = datatype || XSD_STRING; - } - } else { - // convert string/node object to RDF - var id = _isObject(item) ? item['@id'] : item; - object.type = (id.indexOf('_:') === 0) ? 'blank node' : 'IRI'; - object.value = id; - } +ClientRequest.prototype.abort = ClientRequest.prototype.destroy = function () { + var self = this + self._destroyed = true + if (self._response) + self._response._destroyed = true + if (self._xhr) + self._xhr.abort() + // Currently, there isn't a way to truly abort a fetch. + // If you like bikeshedding, see https://github.com/whatwg/fetch/issues/27 +} - // skip relative IRIs - if(object.type === 'IRI' && !_isAbsoluteIri(object.value)) { - return null; - } +ClientRequest.prototype.end = function (data, encoding, cb) { + var self = this + if (typeof data === 'function') { + cb = data + data = undefined + } - return object; + stream.Writable.prototype.end.call(self, data, encoding, cb) } -/** - * Converts an RDF triple object to a JSON-LD object. - * - * @param o the RDF triple object to convert. - * @param useNativeTypes true to output native types, false not to. - * - * @return the JSON-LD object. - */ -function _RDFToObject(o, useNativeTypes) { - // convert IRI/blank node object to JSON-LD - if(o.type === 'IRI' || o.type === 'blank node') { - return {'@id': o.value}; - } +ClientRequest.prototype.flushHeaders = function () {} +ClientRequest.prototype.setTimeout = function () {} +ClientRequest.prototype.setNoDelay = function () {} +ClientRequest.prototype.setSocketKeepAlive = function () {} - // convert literal to JSON-LD - var rval = {'@value': o.value}; +// Taken from http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader%28%29-method +var unsafeHeaders = [ + 'accept-charset', + 'accept-encoding', + 'access-control-request-headers', + 'access-control-request-method', + 'connection', + 'content-length', + 'cookie', + 'cookie2', + 'date', + 'dnt', + 'expect', + 'host', + 'keep-alive', + 'origin', + 'referer', + 'te', + 'trailer', + 'transfer-encoding', + 'upgrade', + 'user-agent', + 'via' +] - // add language - if(o.language) { - rval['@language'] = o.language; - } else { - var type = o.datatype; - if(!type) { - type = XSD_STRING; - } - // use native types for certain xsd types - if(useNativeTypes) { - if(type === XSD_BOOLEAN) { - if(rval['@value'] === 'true') { - rval['@value'] = true; - } else if(rval['@value'] === 'false') { - rval['@value'] = false; - } - } else if(_isNumeric(rval['@value'])) { - if(type === XSD_INTEGER) { - var i = parseInt(rval['@value'], 10); - if(i.toFixed(0) === rval['@value']) { - rval['@value'] = i; - } - } else if(type === XSD_DOUBLE) { - rval['@value'] = parseFloat(rval['@value']); - } - } - // do not add native type - if([XSD_BOOLEAN, XSD_INTEGER, XSD_DOUBLE, XSD_STRING] - .indexOf(type) === -1) { - rval['@type'] = type; - } - } else if(type !== XSD_STRING) { - rval['@type'] = type; - } - } +}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer) - return rval; -} +},{"./capability":29,"./response":31,"_process":74,"buffer":5,"foreach":40,"indexof":42,"inherits":43,"object-keys":70,"stream":27}],31:[function(_dereq_,module,exports){ +(function (process,global,Buffer){ +var capability = _dereq_('./capability') +var foreach = _dereq_('foreach') +var inherits = _dereq_('inherits') +var stream = _dereq_('stream') -/** - * Compares two RDF triples for equality. - * - * @param t1 the first triple. - * @param t2 the second triple. - * - * @return true if the triples are the same, false if not. - */ -function _compareRDFTriples(t1, t2) { - var attrs = ['subject', 'predicate', 'object']; - for(var i = 0; i < attrs.length; ++i) { - var attr = attrs[i]; - if(t1[attr].type !== t2[attr].type || t1[attr].value !== t2[attr].value) { - return false; - } - } - if(t1.object.language !== t2.object.language) { - return false; - } - if(t1.object.datatype !== t2.object.datatype) { - return false; - } - return true; +var rStates = exports.readyStates = { + UNSENT: 0, + OPENED: 1, + HEADERS_RECEIVED: 2, + LOADING: 3, + DONE: 4 } -/////////////////////////////// DEFINE URDNA2015 ////////////////////////////// +var IncomingMessage = exports.IncomingMessage = function (xhr, response, mode) { + var self = this + stream.Readable.call(self) -var URDNA2015 = (function() { + self._mode = mode + self.headers = {} + self.rawHeaders = [] + self.trailers = {} + self.rawTrailers = [] -var POSITIONS = {'subject': 's', 'object': 'o', 'name': 'g'}; + // Fake the 'close' event, but only once 'end' fires + self.on('end', function () { + // The nextTick is necessary to prevent the 'request' module from causing an infinite loop + process.nextTick(function () { + self.emit('close') + }) + }) -var Normalize = function(options) { - options = options || {}; - this.name = 'URDNA2015'; - this.options = options; - this.blankNodeInfo = {}; - this.hashToBlankNodes = {}; - this.canonicalIssuer = new IdentifierIssuer('_:c14n'); - this.quads = []; - this.schedule = {}; - if('maxCallStackDepth' in options) { - this.schedule.MAX_DEPTH = options.maxCallStackDepth; - } else { - this.schedule.MAX_DEPTH = 500; - } - if('maxTotalCallStackDepth' in options) { - this.schedule.MAX_TOTAL_DEPTH = options.maxCallStackDepth; - } else { - this.schedule.MAX_TOTAL_DEPTH = 0xFFFFFFFF; - } - this.schedule.depth = 0; - this.schedule.totalDepth = 0; - if('timeSlice' in options) { - this.schedule.timeSlice = options.timeSlice; - } else { - // milliseconds - this.schedule.timeSlice = 10; - } -}; + if (mode === 'fetch') { + self._fetchResponse = response -// do some work in a time slice, but in serial -Normalize.prototype.doWork = function(fn, callback) { - var schedule = this.schedule; + self.statusCode = response.status + self.statusMessage = response.statusText + // backwards compatible version of for ( of ): + // for (var ,_i,_it = [Symbol.iterator](); = (_i = _it.next()).value,!_i.done;) + for (var header, _i, _it = response.headers[Symbol.iterator](); header = (_i = _it.next()).value, !_i.done;) { + self.headers[header[0].toLowerCase()] = header[1] + self.rawHeaders.push(header[0], header[1]) + } - if(schedule.totalDepth >= schedule.MAX_TOTAL_DEPTH) { - return callback(new Error( - 'Maximum total call stack depth exceeded; normalization aborting.')); - } + // TODO: this doesn't respect backpressure. Once WritableStream is available, this can be fixed + var reader = response.body.getReader() + function read () { + reader.read().then(function (result) { + if (self._destroyed) + return + if (result.done) { + self.push(null) + return + } + self.push(new Buffer(result.value)) + read() + }) + } + read() - (function work() { - if(schedule.depth === schedule.MAX_DEPTH) { - // stack too deep, run on next tick - schedule.depth = 0; - schedule.running = false; - return jsonld.nextTick(work); - } + } else { + self._xhr = xhr + self._pos = 0 - // if not yet running, force run - var now = new Date().getTime(); - if(!schedule.running) { - schedule.start = new Date().getTime(); - schedule.deadline = schedule.start + schedule.timeSlice; - } + self.statusCode = xhr.status + self.statusMessage = xhr.statusText + var headers = xhr.getAllResponseHeaders().split(/\r?\n/) + foreach(headers, function (header) { + var matches = header.match(/^([^:]+):\s*(.*)/) + if (matches) { + var key = matches[1].toLowerCase() + if (self.headers[key] !== undefined) + self.headers[key] += ', ' + matches[2] + else + self.headers[key] = matches[2] + self.rawHeaders.push(matches[1], matches[2]) + } + }) - // TODO: should also include an estimate of expectedWorkTime - if(now < schedule.deadline) { - schedule.running = true; - schedule.depth++; - schedule.totalDepth++; - return fn(function(err, result) { - schedule.depth--; - schedule.totalDepth--; - callback(err, result); - }); - } + self._charset = 'x-user-defined' + if (!capability.overrideMimeType) { + var mimeType = self.rawHeaders['mime-type'] + if (mimeType) { + var charsetMatch = mimeType.match(/;\s*charset=([^;])(;|$)/) + if (charsetMatch) { + self._charset = charsetMatch[1].toLowerCase() + } + } + if (!self._charset) + self._charset = 'utf-8' // best guess + } + } +} - // not enough time left in this slice, run after letting browser - // do some other things - schedule.depth = 0; - schedule.running = false; - jsonld.setImmediate(work); - })(); -}; +inherits(IncomingMessage, stream.Readable) -// asynchronously loop -Normalize.prototype.forEach = function(iterable, fn, callback) { - var self = this; - var iterator; - var idx = 0; - var length; - if(_isArray(iterable)) { - length = iterable.length; - iterator = function() { - if(idx === length) { - return false; - } - iterator.value = iterable[idx++]; - iterator.key = idx; - return true; - }; - } else { - var keys = Object.keys(iterable); - length = keys.length; - iterator = function() { - if(idx === length) { - return false; - } - iterator.key = keys[idx++]; - iterator.value = iterable[iterator.key]; - return true; - }; - } +IncomingMessage.prototype._read = function () {} - (function iterate(err, result) { - if(err) { - return callback(err); - } - if(iterator()) { - return self.doWork(function() { - fn(iterator.value, iterator.key, iterate); - }); - } - callback(); - })(); -}; +IncomingMessage.prototype._onXHRProgress = function () { + var self = this -// asynchronous waterfall -Normalize.prototype.waterfall = function(fns, callback) { - var self = this; - self.forEach(fns, function(fn, idx, callback) { - self.doWork(fn, callback); - }, callback); -}; + var xhr = self._xhr -// asynchronous while -Normalize.prototype.whilst = function(condition, fn, callback) { - var self = this; - (function loop(err) { - if(err) { - return callback(err); - } - if(!condition()) { - return callback(); - } - self.doWork(fn, loop); - })(); -}; + var response = null + switch (self._mode) { + case 'text:vbarray': // For IE9 + if (xhr.readyState !== rStates.DONE) + break + try { + // This fails in IE8 + response = new global.VBArray(xhr.responseBody).toArray() + } catch (e) {} + if (response !== null) { + self.push(new Buffer(response)) + break + } + // Falls through in IE8 + case 'text': + try { // This will fail when readyState = 3 in IE9. Switch mode and wait for readyState = 4 + response = xhr.responseText + } catch (e) { + self._mode = 'text:vbarray' + break + } + if (response.length > self._pos) { + var newData = response.substr(self._pos) + if (self._charset === 'x-user-defined') { + var buffer = new Buffer(newData.length) + for (var i = 0; i < newData.length; i++) + buffer[i] = newData.charCodeAt(i) & 0xff -// 4.4) Normalization Algorithm -Normalize.prototype.main = function(dataset, callback) { - var self = this; - self.schedule.start = new Date().getTime(); - var result; + self.push(buffer) + } else { + self.push(newData, self._charset) + } + self._pos = response.length + } + break + case 'arraybuffer': + if (xhr.readyState !== rStates.DONE) + break + response = xhr.response + self.push(new Buffer(new Uint8Array(response))) + break + case 'moz-chunked-arraybuffer': // take whole + response = xhr.response + if (xhr.readyState !== rStates.LOADING || !response) + break + self.push(new Buffer(new Uint8Array(response))) + break + case 'ms-stream': + response = xhr.response + if (xhr.readyState !== rStates.LOADING) + break + var reader = new global.MSStreamReader() + reader.onprogress = function () { + if (reader.result.byteLength > self._pos) { + self.push(new Buffer(new Uint8Array(reader.result.slice(self._pos)))) + self._pos = reader.result.byteLength + } + } + reader.onload = function () { + self.push(null) + } + // reader.onerror = ??? // TODO: this + reader.readAsArrayBuffer(response) + break + } - // handle invalid output format - if(self.options.format) { - if(self.options.format !== 'application/nquads') { - return callback(new JsonLdError( - 'Unknown output format.', - 'jsonld.UnknownFormat', {format: self.options.format})); - } - } + // The ms-stream case handles end separately in reader.onload() + if (self._xhr.readyState === rStates.DONE && self._mode !== 'ms-stream') { + self.push(null) + } +} - // 1) Create the normalization state. +}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer) - // Note: Optimize by generating non-normalized blank node map concurrently. - var nonNormalized = {}; +},{"./capability":29,"_process":74,"buffer":5,"foreach":40,"inherits":43,"stream":27}],32:[function(_dereq_,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. - self.waterfall([ - function(callback) { - // 2) For every quad in input dataset: - self.forEach(dataset, function(triples, graphName, callback) { - if(graphName === '@default') { - graphName = null; - } - self.forEach(triples, function(quad, idx, callback) { - if(graphName !== null) { - if(graphName.indexOf('_:') === 0) { - quad.name = {type: 'blank node', value: graphName}; - } else { - quad.name = {type: 'IRI', value: graphName}; - } - } - self.quads.push(quad); +var Buffer = _dereq_('buffer').Buffer; - // 2.1) For each blank node that occurs in the quad, add a reference - // to the quad using the blank node identifier in the blank node to - // quads map, creating a new entry if necessary. - self.forEachComponent(quad, function(component) { - if(component.type !== 'blank node') { - return; - } - var id = component.value; - if(id in self.blankNodeInfo) { - self.blankNodeInfo[id].quads.push(quad); - } else { - nonNormalized[id] = true; - self.blankNodeInfo[id] = {quads: [quad]}; - } - }); - callback(); - }, callback); - }, callback); - }, - function(callback) { - // 3) Create a list of non-normalized blank node identifiers - // non-normalized identifiers and populate it using the keys from the - // blank node to quads map. - // Note: We use a map here and it was generated during step 2. +var isBufferEncoding = Buffer.isEncoding + || function(encoding) { + switch (encoding && encoding.toLowerCase()) { + case 'hex': case 'utf8': case 'utf-8': case 'ascii': case 'binary': case 'base64': case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': case 'raw': return true; + default: return false; + } + } - // 4) Initialize simple, a boolean flag, to true. - var simple = true; - // 5) While simple is true, issue canonical identifiers for blank nodes: - self.whilst(function() { return simple; }, function(callback) { - // 5.1) Set simple to false. - simple = false; +function assertEncoding(encoding) { + if (encoding && !isBufferEncoding(encoding)) { + throw new Error('Unknown encoding: ' + encoding); + } +} - // 5.2) Clear hash to blank nodes map. - self.hashToBlankNodes = {}; +// StringDecoder provides an interface for efficiently splitting a series of +// buffers into a series of JS strings without breaking apart multi-byte +// characters. CESU-8 is handled as part of the UTF-8 encoding. +// +// @TODO Handling all encodings inside a single object makes it very difficult +// to reason about this code, so it should be split up in the future. +// @TODO There should be a utf8-strict encoding that rejects invalid UTF-8 code +// points as used by CESU-8. +var StringDecoder = exports.StringDecoder = function(encoding) { + this.encoding = (encoding || 'utf8').toLowerCase().replace(/[-_]/, ''); + assertEncoding(encoding); + switch (this.encoding) { + case 'utf8': + // CESU-8 represents each of Surrogate Pair by 3-bytes + this.surrogateSize = 3; + break; + case 'ucs2': + case 'utf16le': + // UTF-16 represents each of Surrogate Pair by 2-bytes + this.surrogateSize = 2; + this.detectIncompleteChar = utf16DetectIncompleteChar; + break; + case 'base64': + // Base-64 stores 3 bytes in 4 chars, and pads the remainder. + this.surrogateSize = 3; + this.detectIncompleteChar = base64DetectIncompleteChar; + break; + default: + this.write = passThroughWrite; + return; + } - self.waterfall([ - function(callback) { - // 5.3) For each blank node identifier identifier in non-normalized - // identifiers: - self.forEach(nonNormalized, function(value, id, callback) { - // 5.3.1) Create a hash, hash, according to the Hash First Degree - // Quads algorithm. - self.hashFirstDegreeQuads(id, function(err, hash) { - if(err) { - return callback(err); - } - // 5.3.2) Add hash and identifier to hash to blank nodes map, - // creating a new entry if necessary. - if(hash in self.hashToBlankNodes) { - self.hashToBlankNodes[hash].push(id); - } else { - self.hashToBlankNodes[hash] = [id]; - } - callback(); - }); - }, callback); - }, - function(callback) { - // 5.4) For each hash to identifier list mapping in hash to blank - // nodes map, lexicographically-sorted by hash: - var hashes = Object.keys(self.hashToBlankNodes).sort(); - self.forEach(hashes, function(hash, i, callback) { - // 5.4.1) If the length of identifier list is greater than 1, - // continue to the next mapping. - var idList = self.hashToBlankNodes[hash]; - if(idList.length > 1) { - return callback(); - } + // Enough space to store all bytes of a single character. UTF-8 needs 4 + // bytes, but CESU-8 may require up to 6 (3 bytes per surrogate). + this.charBuffer = new Buffer(6); + // Number of bytes received for the current incomplete multi-byte character. + this.charReceived = 0; + // Number of bytes expected for the current incomplete multi-byte character. + this.charLength = 0; +}; - // 5.4.2) Use the Issue Identifier algorithm, passing canonical - // issuer and the single blank node identifier in identifier - // list, identifier, to issue a canonical replacement identifier - // for identifier. - // TODO: consider changing `getId` to `issue` - var id = idList[0]; - self.canonicalIssuer.getId(id); - // 5.4.3) Remove identifier from non-normalized identifiers. - delete nonNormalized[id]; +// write decodes the given buffer and returns it as JS string that is +// guaranteed to not contain any partial multi-byte characters. Any partial +// character found at the end of the buffer is buffered up, and will be +// returned when calling write again with the remaining bytes. +// +// Note: Converting a Buffer containing an orphan surrogate to a String +// currently works, but converting a String to a Buffer (via `new Buffer`, or +// Buffer#write) will replace incomplete surrogates with the unicode +// replacement character. See https://codereview.chromium.org/121173009/ . +StringDecoder.prototype.write = function(buffer) { + var charStr = ''; + // if our last write ended with an incomplete multibyte character + while (this.charLength) { + // determine how many remaining bytes this buffer has to offer for this char + var available = (buffer.length >= this.charLength - this.charReceived) ? + this.charLength - this.charReceived : + buffer.length; - // 5.4.4) Remove hash from the hash to blank nodes map. - delete self.hashToBlankNodes[hash]; + // add the new bytes to the char buffer + buffer.copy(this.charBuffer, this.charReceived, 0, available); + this.charReceived += available; - // 5.4.5) Set simple to true. - simple = true; - callback(); - }, callback); - } - ], callback); - }, callback); - }, - function(callback) { - // 6) For each hash to identifier list mapping in hash to blank nodes map, - // lexicographically-sorted by hash: - var hashes = Object.keys(self.hashToBlankNodes).sort(); - self.forEach(hashes, function(hash, idx, callback) { - // 6.1) Create hash path list where each item will be a result of - // running the Hash N-Degree Quads algorithm. - var hashPathList = []; + if (this.charReceived < this.charLength) { + // still not enough chars in this buffer? wait for more ... + return ''; + } - // 6.2) For each blank node identifier identifier in identifier list: - var idList = self.hashToBlankNodes[hash]; - self.waterfall([ - function(callback) { - self.forEach(idList, function(id, idx, callback) { - // 6.2.1) If a canonical identifier has already been issued for - // identifier, continue to the next identifier. - if(self.canonicalIssuer.hasId(id)) { - return callback(); - } + // remove bytes belonging to the current character from the buffer + buffer = buffer.slice(available, buffer.length); - // 6.2.2) Create temporary issuer, an identifier issuer - // initialized with the prefix _:b. - var issuer = new IdentifierIssuer('_:b'); + // get the character that was split + charStr = this.charBuffer.slice(0, this.charLength).toString(this.encoding); - // 6.2.3) Use the Issue Identifier algorithm, passing temporary - // issuer and identifier, to issue a new temporary blank node - // identifier for identifier. - issuer.getId(id); + // CESU-8: lead surrogate (D800-DBFF) is also the incomplete character + var charCode = charStr.charCodeAt(charStr.length - 1); + if (charCode >= 0xD800 && charCode <= 0xDBFF) { + this.charLength += this.surrogateSize; + charStr = ''; + continue; + } + this.charReceived = this.charLength = 0; - // 6.2.4) Run the Hash N-Degree Quads algorithm, passing - // temporary issuer, and append the result to the hash path list. - self.hashNDegreeQuads(id, issuer, function(err, result) { - if(err) { - return callback(err); - } - hashPathList.push(result); - callback(); - }); - }, callback); - }, - function(callback) { - // 6.3) For each result in the hash path list, - // lexicographically-sorted by the hash in result: - hashPathList.sort(function(a, b) { - return (a.hash < b.hash) ? -1 : ((a.hash > b.hash) ? 1 : 0); - }); - self.forEach(hashPathList, function(result, idx, callback) { - // 6.3.1) For each blank node identifier, existing identifier, - // that was issued a temporary identifier by identifier issuer - // in result, issue a canonical identifier, in the same order, - // using the Issue Identifier algorithm, passing canonical - // issuer and existing identifier. - for(var existing in result.issuer.existing) { - self.canonicalIssuer.getId(existing); - } - callback(); - }, callback); - } - ], callback); - }, callback); - }, function(callback) { - /* Note: At this point all blank nodes in the set of RDF quads have been - assigned canonical identifiers, which have been stored in the canonical - issuer. Here each quad is updated by assigning each of its blank nodes - its new identifier. */ + // if there are no more bytes in this buffer, just emit our char + if (buffer.length === 0) { + return charStr; + } + break; + } - // 7) For each quad, quad, in input dataset: - var normalized = []; - self.waterfall([ - function(callback) { - self.forEach(self.quads, function(quad, idx, callback) { - // 7.1) Create a copy, quad copy, of quad and replace any existing - // blank node identifiers using the canonical identifiers - // previously issued by canonical issuer. - // Note: We optimize away the copy here. - self.forEachComponent(quad, function(component) { - if(component.type === 'blank node' && - component.value.indexOf(self.canonicalIssuer.prefix) !== 0) { - component.value = self.canonicalIssuer.getId(component.value); - } - }); - // 7.2) Add quad copy to the normalized dataset. - normalized.push(_toNQuad(quad)); - callback(); - }, callback); - }, - function(callback) { - // sort normalized output - normalized.sort(); + // determine and set charLength / charReceived + this.detectIncompleteChar(buffer); - // 8) Return the normalized dataset. - if(self.options.format === 'application/nquads') { - result = normalized.join(''); - return callback(); - } + var end = buffer.length; + if (this.charLength) { + // buffer the incomplete character bytes we got + buffer.copy(this.charBuffer, 0, buffer.length - this.charReceived, end); + end -= this.charReceived; + } - result = _parseNQuads(normalized.join('')); - callback(); - } - ], callback); - } - ], function(err) { - callback(err, result); - }); + charStr += buffer.toString(this.encoding, 0, end); + + var end = charStr.length - 1; + var charCode = charStr.charCodeAt(end); + // CESU-8: lead surrogate (D800-DBFF) is also the incomplete character + if (charCode >= 0xD800 && charCode <= 0xDBFF) { + var size = this.surrogateSize; + this.charLength += size; + this.charReceived += size; + this.charBuffer.copy(this.charBuffer, size, 0, size); + buffer.copy(this.charBuffer, 0, 0, size); + return charStr.substring(0, end); + } + + // or just emit the charStr + return charStr; }; -// 4.6) Hash First Degree Quads -Normalize.prototype.hashFirstDegreeQuads = function(id, callback) { - var self = this; +// detectIncompleteChar determines if there is an incomplete UTF-8 character at +// the end of the given buffer. If so, it sets this.charLength to the byte +// length that character, and sets this.charReceived to the number of bytes +// that are available for this character. +StringDecoder.prototype.detectIncompleteChar = function(buffer) { + // determine how many bytes we have to check at the end of this buffer + var i = (buffer.length >= 3) ? 3 : buffer.length; - // return cached hash - var info = self.blankNodeInfo[id]; - if('hash' in info) { - return callback(null, info.hash); - } + // Figure out if one of the last i bytes of our buffer announces an + // incomplete char. + for (; i > 0; i--) { + var c = buffer[buffer.length - i]; - // 1) Initialize nquads to an empty list. It will be used to store quads in - // N-Quads format. - var nquads = []; + // See http://en.wikipedia.org/wiki/UTF-8#Description - // 2) Get the list of quads quads associated with the reference blank node - // identifier in the blank node to quads map. - var quads = info.quads; + // 110XXXXX + if (i == 1 && c >> 5 == 0x06) { + this.charLength = 2; + break; + } - // 3) For each quad quad in quads: - self.forEach(quads, function(quad, idx, callback) { - // 3.1) Serialize the quad in N-Quads format with the following special - // rule: + // 1110XXXX + if (i <= 2 && c >> 4 == 0x0E) { + this.charLength = 3; + break; + } - // 3.1.1) If any component in quad is an blank node, then serialize it - // using a special identifier as follows: - var copy = {predicate: quad.predicate}; - self.forEachComponent(quad, function(component, key) { - // 3.1.2) If the blank node's existing blank node identifier matches the - // reference blank node identifier then use the blank node identifier _:a, - // otherwise, use the blank node identifier _:z. - copy[key] = self.modifyFirstDegreeComponent(id, component, key); - }); - nquads.push(_toNQuad(copy)); - callback(); - }, function(err) { - if(err) { - return callback(err); + // 11110XXX + if (i <= 3 && c >> 3 == 0x1E) { + this.charLength = 4; + break; } - // 4) Sort nquads in lexicographical order. - nquads.sort(); - - // 5) Return the hash that results from passing the sorted, joined nquads - // through the hash algorithm. - info.hash = NormalizeHash.hashNQuads(self.name, nquads); - callback(null, info.hash); - }); + } + this.charReceived = i; }; -// helper for modifying component during Hash First Degree Quads -Normalize.prototype.modifyFirstDegreeComponent = function(id, component) { - if(component.type !== 'blank node') { - return component; +StringDecoder.prototype.end = function(buffer) { + var res = ''; + if (buffer && buffer.length) + res = this.write(buffer); + + if (this.charReceived) { + var cr = this.charReceived; + var buf = this.charBuffer; + var enc = this.encoding; + res += buf.slice(0, cr).toString(enc); } - component = _clone(component); - component.value = (component.value === id ? '_:a' : '_:z'); - return component; + + return res; }; -// 4.7) Hash Related Blank Node -Normalize.prototype.hashRelatedBlankNode = function( - related, quad, issuer, position, callback) { - var self = this; +function passThroughWrite(buffer) { + return buffer.toString(this.encoding); +} - // 1) Set the identifier to use for related, preferring first the canonical - // identifier for related if issued, second the identifier issued by issuer - // if issued, and last, if necessary, the result of the Hash First Degree - // Quads algorithm, passing related. - var id; - self.waterfall([ - function(callback) { - if(self.canonicalIssuer.hasId(related)) { - id = self.canonicalIssuer.getId(related); - return callback(); - } - if(issuer.hasId(related)) { - id = issuer.getId(related); - return callback(); - } - self.hashFirstDegreeQuads(related, function(err, hash) { - if(err) { - return callback(err); - } - id = hash; - callback(); - }); - } - ], function(err) { - if(err) { - return callback(err); - } +function utf16DetectIncompleteChar(buffer) { + this.charReceived = buffer.length % 2; + this.charLength = this.charReceived ? 2 : 0; +} - // 2) Initialize a string input to the value of position. - // Note: We use a hash object instead. - var md = new NormalizeHash(self.name); - md.update(position); +function base64DetectIncompleteChar(buffer) { + this.charReceived = buffer.length % 3; + this.charLength = this.charReceived ? 3 : 0; +} - // 3) If position is not g, append <, the value of the predicate in quad, - // and > to input. - if(position !== 'g') { - md.update(self.getRelatedPredicate(quad)); - } +},{"buffer":5}],33:[function(_dereq_,module,exports){ +var nextTick = _dereq_('process/browser.js').nextTick; +var apply = Function.prototype.apply; +var slice = Array.prototype.slice; +var immediateIds = {}; +var nextImmediateId = 0; - // 4) Append identifier to input. - md.update(id); +// DOM APIs, for completeness - // 5) Return the hash that results from passing input through the hash - // algorithm. - return callback(null, md.digest()); - }); +exports.setTimeout = function() { + return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout); }; - -// helper for getting a related predicate -Normalize.prototype.getRelatedPredicate = function(quad) { - return '<' + quad.predicate.value + '>'; +exports.setInterval = function() { + return new Timeout(apply.call(setInterval, window, arguments), clearInterval); }; +exports.clearTimeout = +exports.clearInterval = function(timeout) { timeout.close(); }; -// 4.8) Hash N-Degree Quads -Normalize.prototype.hashNDegreeQuads = function(id, issuer, callback) { - var self = this; +function Timeout(id, clearFn) { + this._id = id; + this._clearFn = clearFn; +} +Timeout.prototype.unref = Timeout.prototype.ref = function() {}; +Timeout.prototype.close = function() { + this._clearFn.call(window, this._id); +}; - // 1) Create a hash to related blank nodes map for storing hashes that - // identify related blank nodes. - // Note: 2) and 3) handled within `createHashToRelated` - var hashToRelated; - var md = new NormalizeHash(self.name); - self.waterfall([ - function(callback) { - self.createHashToRelated(id, issuer, function(err, result) { - if(err) { - return callback(err); - } - hashToRelated = result; - callback(); - }); - }, - function(callback) { - // 4) Create an empty string, data to hash. - // Note: We created a hash object `md` above instead. +// Does not start the time, just sets up the members needed. +exports.enroll = function(item, msecs) { + clearTimeout(item._idleTimeoutId); + item._idleTimeout = msecs; +}; - // 5) For each related hash to blank node list mapping in hash to related - // blank nodes map, sorted lexicographically by related hash: - var hashes = Object.keys(hashToRelated).sort(); - self.forEach(hashes, function(hash, idx, callback) { - // 5.1) Append the related hash to the data to hash. - md.update(hash); +exports.unenroll = function(item) { + clearTimeout(item._idleTimeoutId); + item._idleTimeout = -1; +}; - // 5.2) Create a string chosen path. - var chosenPath = ''; +exports._unrefActive = exports.active = function(item) { + clearTimeout(item._idleTimeoutId); - // 5.3) Create an unset chosen issuer variable. - var chosenIssuer; + var msecs = item._idleTimeout; + if (msecs >= 0) { + item._idleTimeoutId = setTimeout(function onTimeout() { + if (item._onTimeout) + item._onTimeout(); + }, msecs); + } +}; - // 5.4) For each permutation of blank node list: - var permutator = new Permutator(hashToRelated[hash]); - self.whilst( - function() { return permutator.hasNext(); }, - function(nextPermutation) { - var permutation = permutator.next(); +// That's not how node.js implements it but the exposed api is the same. +exports.setImmediate = typeof setImmediate === "function" ? setImmediate : function(fn) { + var id = nextImmediateId++; + var args = arguments.length < 2 ? false : slice.call(arguments, 1); - // 5.4.1) Create a copy of issuer, issuer copy. - var issuerCopy = issuer.clone(); + immediateIds[id] = true; - // 5.4.2) Create a string path. - var path = ''; + nextTick(function onNextTick() { + if (immediateIds[id]) { + // fn.call() is faster so we optimize for the common use-case + // @see http://jsperf.com/call-apply-segu + if (args) { + fn.apply(null, args); + } else { + fn.call(null); + } + // Prevent ids from leaking + exports.clearImmediate(id); + } + }); - // 5.4.3) Create a recursion list, to store blank node identifiers - // that must be recursively processed by this algorithm. - var recursionList = []; + return id; +}; - self.waterfall([ - function(callback) { - // 5.4.4) For each related in permutation: - self.forEach(permutation, function(related, idx, callback) { - // 5.4.4.1) If a canonical identifier has been issued for - // related, append it to path. - if(self.canonicalIssuer.hasId(related)) { - path += self.canonicalIssuer.getId(related); - } else { - // 5.4.4.2) Otherwise: - // 5.4.4.2.1) If issuer copy has not issued an identifier for - // related, append related to recursion list. - if(!issuerCopy.hasId(related)) { - recursionList.push(related); - } - // 5.4.4.2.2) Use the Issue Identifier algorithm, passing - // issuer copy and related and append the result to path. - path += issuerCopy.getId(related); - } +exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function(id) { + delete immediateIds[id]; +}; +},{"process/browser.js":74}],34:[function(_dereq_,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. - // 5.4.4.3) If chosen path is not empty and the length of path - // is greater than or equal to the length of chosen path and - // path is lexicographically greater than chosen path, then - // skip to the next permutation. - if(chosenPath.length !== 0 && - path.length >= chosenPath.length && path > chosenPath) { - // FIXME: may cause inaccurate total depth calculation - return nextPermutation(); - } - callback(); - }, callback); - }, - function(callback) { - // 5.4.5) For each related in recursion list: - self.forEach(recursionList, function(related, idx, callback) { - // 5.4.5.1) Set result to the result of recursively executing - // the Hash N-Degree Quads algorithm, passing related for - // identifier and issuer copy for path identifier issuer. - self.hashNDegreeQuads( - related, issuerCopy, function(err, result) { - if(err) { - return callback(err); - } +var punycode = _dereq_('punycode'); - // 5.4.5.2) Use the Issue Identifier algorithm, passing issuer - // copy and related and append the result to path. - path += issuerCopy.getId(related); +exports.parse = urlParse; +exports.resolve = urlResolve; +exports.resolveObject = urlResolveObject; +exports.format = urlFormat; - // 5.4.5.3) Append <, the hash in result, and > to path. - path += '<' + result.hash + '>'; +exports.Url = Url; - // 5.4.5.4) Set issuer copy to the identifier issuer in - // result. - issuerCopy = result.issuer; +function Url() { + this.protocol = null; + this.slashes = null; + this.auth = null; + this.host = null; + this.port = null; + this.hostname = null; + this.hash = null; + this.search = null; + this.query = null; + this.pathname = null; + this.path = null; + this.href = null; +} - // 5.4.5.5) If chosen path is not empty and the length of path - // is greater than or equal to the length of chosen path and - // path is lexicographically greater than chosen path, then - // skip to the next permutation. - if(chosenPath.length !== 0 && - path.length >= chosenPath.length && path > chosenPath) { - // FIXME: may cause inaccurate total depth calculation - return nextPermutation(); - } - callback(); - }); - }, callback); - }, - function(callback) { - // 5.4.6) If chosen path is empty or path is lexicographically - // less than chosen path, set chosen path to path and chosen - // issuer to issuer copy. - if(chosenPath.length === 0 || path < chosenPath) { - chosenPath = path; - chosenIssuer = issuerCopy; - } - callback(); - } - ], nextPermutation); - }, function(err) { - if(err) { - return callback(err); - } +// Reference: RFC 3986, RFC 1808, RFC 2396 - // 5.5) Append chosen path to data to hash. - md.update(chosenPath); +// define these here so at least they only have to be +// compiled once on the first module load. +var protocolPattern = /^([a-z0-9.+-]+:)/i, + portPattern = /:[0-9]*$/, - // 5.6) Replace issuer, by reference, with chosen issuer. - issuer = chosenIssuer; - callback(); - }); - }, callback); - } - ], function(err) { - // 6) Return issuer and the hash that results from passing data to hash - // through the hash algorithm. - callback(err, {hash: md.digest(), issuer: issuer}); - }); -}; + // RFC 2396: characters reserved for delimiting URLs. + // We actually just auto-escape these. + delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'], -// helper for creating hash to related blank nodes map -Normalize.prototype.createHashToRelated = function(id, issuer, callback) { - var self = this; + // RFC 2396: characters not allowed for various reasons. + unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims), - // 1) Create a hash to related blank nodes map for storing hashes that - // identify related blank nodes. - var hashToRelated = {}; + // Allowed by RFCs, but cause of XSS attacks. Always escape these. + autoEscape = ['\''].concat(unwise), + // Characters that are never ever allowed in a hostname. + // Note that any invalid chars are also handled, but these + // are the ones that are *expected* to be seen, so we fast-path + // them. + nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape), + hostEndingChars = ['/', '?', '#'], + hostnameMaxLen = 255, + hostnamePartPattern = /^[a-z0-9A-Z_-]{0,63}$/, + hostnamePartStart = /^([a-z0-9A-Z_-]{0,63})(.*)$/, + // protocols that can allow "unsafe" and "unwise" chars. + unsafeProtocol = { + 'javascript': true, + 'javascript:': true + }, + // protocols that never have a hostname. + hostlessProtocol = { + 'javascript': true, + 'javascript:': true + }, + // protocols that always contain a // bit. + slashedProtocol = { + 'http': true, + 'https': true, + 'ftp': true, + 'gopher': true, + 'file': true, + 'http:': true, + 'https:': true, + 'ftp:': true, + 'gopher:': true, + 'file:': true + }, + querystring = _dereq_('querystring'); - // 2) Get a reference, quads, to the list of quads in the blank node to - // quads map for the key identifier. - var quads = self.blankNodeInfo[id].quads; +function urlParse(url, parseQueryString, slashesDenoteHost) { + if (url && isObject(url) && url instanceof Url) return url; - // 3) For each quad in quads: - self.forEach(quads, function(quad, idx, callback) { - // 3.1) For each component in quad, if component is the subject, object, - // and graph name and it is a blank node that is not identified by - // identifier: - self.forEach(quad, function(component, key, callback) { - if(key === 'predicate' || - !(component.type === 'blank node' && component.value !== id)) { - return callback(); - } - // 3.1.1) Set hash to the result of the Hash Related Blank Node - // algorithm, passing the blank node identifier for component as - // related, quad, path identifier issuer as issuer, and position as - // either s, o, or g based on whether component is a subject, object, - // graph name, respectively. - var related = component.value; - var position = POSITIONS[key]; - self.hashRelatedBlankNode( - related, quad, issuer, position, function(err, hash) { - if(err) { - return callback(err); - } - // 3.1.2) Add a mapping of hash to the blank node identifier for - // component to hash to related blank nodes map, adding an entry as - // necessary. - if(hash in hashToRelated) { - hashToRelated[hash].push(related); - } else { - hashToRelated[hash] = [related]; - } - callback(); - }); - }, callback); - }, function(err) { - callback(err, hashToRelated); - }); -}; + var u = new Url; + u.parse(url, parseQueryString, slashesDenoteHost); + return u; +} -// helper that iterates over quad components (skips predicate) -Normalize.prototype.forEachComponent = function(quad, op) { - for(var key in quad) { - // skip `predicate` - if(key === 'predicate') { - continue; - } - op(quad[key], key, quad); +Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) { + if (!isString(url)) { + throw new TypeError("Parameter 'url' must be a string, not " + typeof url); } -}; - -return Normalize; - -})(); // end of define URDNA2015 - -/////////////////////////////// DEFINE URGNA2012 ////////////////////////////// -var URGNA2012 = (function() { + var rest = url; -var Normalize = function(options) { - URDNA2015.call(this, options); - this.name = 'URGNA2012'; -}; -Normalize.prototype = new URDNA2015(); + // trim before proceeding. + // This is to support parse stuff like " http://foo.com \n" + rest = rest.trim(); -// helper for modifying component during Hash First Degree Quads -Normalize.prototype.modifyFirstDegreeComponent = function(id, component, key) { - if(component.type !== 'blank node') { - return component; - } - component = _clone(component); - if(key === 'name') { - component.value = '_:g'; - } else { - component.value = (component.value === id ? '_:a' : '_:z'); + var proto = protocolPattern.exec(rest); + if (proto) { + proto = proto[0]; + var lowerProto = proto.toLowerCase(); + this.protocol = lowerProto; + rest = rest.substr(proto.length); } - return component; -}; - -// helper for getting a related predicate -Normalize.prototype.getRelatedPredicate = function(quad) { - return quad.predicate.value; -}; -// helper for creating hash to related blank nodes map -Normalize.prototype.createHashToRelated = function(id, issuer, callback) { - var self = this; + // figure out if it's got a host + // user@server is *always* interpreted as a hostname, and url + // resolution will treat //foo/bar as host=foo,path=bar because that's + // how the browser resolves relative URLs. + if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) { + var slashes = rest.substr(0, 2) === '//'; + if (slashes && !(proto && hostlessProtocol[proto])) { + rest = rest.substr(2); + this.slashes = true; + } + } - // 1) Create a hash to related blank nodes map for storing hashes that - // identify related blank nodes. - var hashToRelated = {}; + if (!hostlessProtocol[proto] && + (slashes || (proto && !slashedProtocol[proto]))) { - // 2) Get a reference, quads, to the list of quads in the blank node to - // quads map for the key identifier. - var quads = self.blankNodeInfo[id].quads; - - // 3) For each quad in quads: - self.forEach(quads, function(quad, idx, callback) { - // 3.1) If the quad's subject is a blank node that does not match - // identifier, set hash to the result of the Hash Related Blank Node - // algorithm, passing the blank node identifier for subject as related, - // quad, path identifier issuer as issuer, and p as position. - var position; - var related; - if(quad.subject.type === 'blank node' && quad.subject.value !== id) { - related = quad.subject.value; - position = 'p'; - } else if(quad.object.type === 'blank node' && quad.object.value !== id) { - // 3.2) Otherwise, if quad's object is a blank node that does not match - // identifier, to the result of the Hash Related Blank Node algorithm, - // passing the blank node identifier for object as related, quad, path - // identifier issuer as issuer, and r as position. - related = quad.object.value; - position = 'r'; - } else { - // 3.3) Otherwise, continue to the next quad. - return callback(); - } - // 3.4) Add a mapping of hash to the blank node identifier for the - // component that matched (subject or object) to hash to related blank - // nodes map, adding an entry as necessary. - self.hashRelatedBlankNode( - related, quad, issuer, position, function(err, hash) { - if(hash in hashToRelated) { - hashToRelated[hash].push(related); - } else { - hashToRelated[hash] = [related]; - } - callback(); - }); - }, function(err) { - callback(err, hashToRelated); - }); -}; - -return Normalize; + // there's a hostname. + // the first instance of /, ?, ;, or # ends the host. + // + // If there is an @ in the hostname, then non-host chars *are* allowed + // to the left of the last @ sign, unless some host-ending character + // comes *before* the @-sign. + // URLs are obnoxious. + // + // ex: + // http://a@b@c/ => user:a@b host:c + // http://a@b?@c => user:a host:c path:/?@c -})(); // end of define URGNA2012 + // v0.12 TODO(isaacs): This is not quite how Chrome does things. + // Review our test case against browsers more comprehensively. -/** - * Recursively flattens the subjects in the given JSON-LD expanded input - * into a node map. - * - * @param input the JSON-LD expanded input. - * @param graphs a map of graph name to subject map. - * @param graph the name of the current graph. - * @param issuer the blank node identifier issuer. - * @param name the name assigned to the current input if it is a bnode. - * @param list the list to append to, null for none. - */ -function _createNodeMap(input, graphs, graph, issuer, name, list) { - // recurse through array - if(_isArray(input)) { - for(var i = 0; i < input.length; ++i) { - _createNodeMap(input[i], graphs, graph, issuer, undefined, list); + // find the first instance of any hostEndingChars + var hostEnd = -1; + for (var i = 0; i < hostEndingChars.length; i++) { + var hec = rest.indexOf(hostEndingChars[i]); + if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) + hostEnd = hec; } - return; - } - // add non-object to list - if(!_isObject(input)) { - if(list) { - list.push(input); + // at this point, either we have an explicit point where the + // auth portion cannot go past, or the last @ char is the decider. + var auth, atSign; + if (hostEnd === -1) { + // atSign can be anywhere. + atSign = rest.lastIndexOf('@'); + } else { + // atSign must be in auth portion. + // http://a@b/c@d => host:b auth:a path:/c@d + atSign = rest.lastIndexOf('@', hostEnd); } - return; - } - // add values to list - if(_isValue(input)) { - if('@type' in input) { - var type = input['@type']; - // rename @type blank node - if(type.indexOf('_:') === 0) { - input['@type'] = type = issuer.getId(type); - } - } - if(list) { - list.push(input); + // Now we have a portion which is definitely the auth. + // Pull that off. + if (atSign !== -1) { + auth = rest.slice(0, atSign); + rest = rest.slice(atSign + 1); + this.auth = decodeURIComponent(auth); } - return; - } - - // Note: At this point, input must be a subject. - // spec requires @type to be named first, so assign names early - if('@type' in input) { - var types = input['@type']; - for(var i = 0; i < types.length; ++i) { - var type = types[i]; - if(type.indexOf('_:') === 0) { - issuer.getId(type); - } + // the host is the remaining to the left of the first non-host char + hostEnd = -1; + for (var i = 0; i < nonHostChars.length; i++) { + var hec = rest.indexOf(nonHostChars[i]); + if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) + hostEnd = hec; } - } + // if we still have not hit it, then the entire thing is a host. + if (hostEnd === -1) + hostEnd = rest.length; - // get name for subject - if(_isUndefined(name)) { - name = _isBlankNode(input) ? issuer.getId(input['@id']) : input['@id']; - } + this.host = rest.slice(0, hostEnd); + rest = rest.slice(hostEnd); - // add subject reference to list - if(list) { - list.push({'@id': name}); - } + // pull out port. + this.parseHost(); - // create new subject or merge into existing one - var subjects = graphs[graph]; - var subject = subjects[name] = subjects[name] || {}; - subject['@id'] = name; - var properties = Object.keys(input).sort(); - for(var pi = 0; pi < properties.length; ++pi) { - var property = properties[pi]; + // we've indicated that there is a hostname, + // so even if it's empty, it has to be present. + this.hostname = this.hostname || ''; - // skip @id - if(property === '@id') { - continue; - } + // if hostname begins with [ and ends with ] + // assume that it's an IPv6 address. + var ipv6Hostname = this.hostname[0] === '[' && + this.hostname[this.hostname.length - 1] === ']'; - // handle reverse properties - if(property === '@reverse') { - var referencedNode = {'@id': name}; - var reverseMap = input['@reverse']; - for(var reverseProperty in reverseMap) { - var items = reverseMap[reverseProperty]; - for(var ii = 0; ii < items.length; ++ii) { - var item = items[ii]; - var itemName = item['@id']; - if(_isBlankNode(item)) { - itemName = issuer.getId(itemName); + // validate a little. + if (!ipv6Hostname) { + var hostparts = this.hostname.split(/\./); + for (var i = 0, l = hostparts.length; i < l; i++) { + var part = hostparts[i]; + if (!part) continue; + if (!part.match(hostnamePartPattern)) { + var newpart = ''; + for (var j = 0, k = part.length; j < k; j++) { + if (part.charCodeAt(j) > 127) { + // we replace non-ASCII char with a temporary placeholder + // we need this to make sure size of hostname is not + // broken by replacing non-ASCII by nothing + newpart += 'x'; + } else { + newpart += part[j]; + } + } + // we test again with ASCII char only + if (!newpart.match(hostnamePartPattern)) { + var validParts = hostparts.slice(0, i); + var notHost = hostparts.slice(i + 1); + var bit = part.match(hostnamePartStart); + if (bit) { + validParts.push(bit[1]); + notHost.unshift(bit[2]); + } + if (notHost.length) { + rest = '/' + notHost.join('.') + rest; + } + this.hostname = validParts.join('.'); + break; } - _createNodeMap(item, graphs, graph, issuer, itemName); - jsonld.addValue( - subjects[itemName], reverseProperty, referencedNode, - {propertyIsArray: true, allowDuplicate: false}); } } - continue; } - // recurse into graph - if(property === '@graph') { - // add graph subjects map entry - if(!(name in graphs)) { - graphs[name] = {}; - } - var g = (graph === '@merged') ? graph : name; - _createNodeMap(input[property], graphs, g, issuer); - continue; + if (this.hostname.length > hostnameMaxLen) { + this.hostname = ''; + } else { + // hostnames are always lower case. + this.hostname = this.hostname.toLowerCase(); } - // copy non-@type keywords - if(property !== '@type' && _isKeyword(property)) { - if(property === '@index' && property in subject && - (input[property] !== subject[property] || - input[property]['@id'] !== subject[property]['@id'])) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; conflicting @index property detected.', - 'jsonld.SyntaxError', - {code: 'conflicting indexes', subject: subject}); + if (!ipv6Hostname) { + // IDNA Support: Returns a puny coded representation of "domain". + // It only converts the part of the domain name that + // has non ASCII characters. I.e. it dosent matter if + // you call it with a domain that already is in ASCII. + var domainArray = this.hostname.split('.'); + var newOut = []; + for (var i = 0; i < domainArray.length; ++i) { + var s = domainArray[i]; + newOut.push(s.match(/[^A-Za-z0-9_-]/) ? + 'xn--' + punycode.encode(s) : s); } - subject[property] = input[property]; - continue; - } - - // iterate over objects - var objects = input[property]; - - // if property is a bnode, assign it a new id - if(property.indexOf('_:') === 0) { - property = issuer.getId(property); + this.hostname = newOut.join('.'); } - // ensure property is added for empty arrays - if(objects.length === 0) { - jsonld.addValue(subject, property, [], {propertyIsArray: true}); - continue; - } - for(var oi = 0; oi < objects.length; ++oi) { - var o = objects[oi]; + var p = this.port ? ':' + this.port : ''; + var h = this.hostname || ''; + this.host = h + p; + this.href += this.host; - if(property === '@type') { - // rename @type blank nodes - o = (o.indexOf('_:') === 0) ? issuer.getId(o) : o; + // strip [ and ] from the hostname + // the host field still retains them, though + if (ipv6Hostname) { + this.hostname = this.hostname.substr(1, this.hostname.length - 2); + if (rest[0] !== '/') { + rest = '/' + rest; } + } + } - // handle embedded subject or subject reference - if(_isSubject(o) || _isSubjectReference(o)) { - // relabel blank node @id - var id = _isBlankNode(o) ? issuer.getId(o['@id']) : o['@id']; + // now rest is set to the post-host stuff. + // chop off any delim chars. + if (!unsafeProtocol[lowerProto]) { - // add reference and recurse - jsonld.addValue( - subject, property, {'@id': id}, - {propertyIsArray: true, allowDuplicate: false}); - _createNodeMap(o, graphs, graph, issuer, id); - } else if(_isList(o)) { - // handle @list - var _list = []; - _createNodeMap(o['@list'], graphs, graph, issuer, name, _list); - o = {'@list': _list}; - jsonld.addValue( - subject, property, o, - {propertyIsArray: true, allowDuplicate: false}); - } else { - // handle @value - _createNodeMap(o, graphs, graph, issuer, name); - jsonld.addValue( - subject, property, o, {propertyIsArray: true, allowDuplicate: false}); + // First, make 100% sure that any "autoEscape" chars get + // escaped, even if encodeURIComponent doesn't think they + // need to be. + for (var i = 0, l = autoEscape.length; i < l; i++) { + var ae = autoEscape[i]; + var esc = encodeURIComponent(ae); + if (esc === ae) { + esc = escape(ae); } + rest = rest.split(ae).join(esc); } } -} -function _mergeNodeMaps(graphs) { - // add all non-default graphs to default graph - var defaultGraph = graphs['@default']; - var graphNames = Object.keys(graphs).sort(); - for(var i = 0; i < graphNames.length; ++i) { - var graphName = graphNames[i]; - if(graphName === '@default') { - continue; - } - var nodeMap = graphs[graphName]; - var subject = defaultGraph[graphName]; - if(!subject) { - defaultGraph[graphName] = subject = { - '@id': graphName, - '@graph': [] - }; - } else if(!('@graph' in subject)) { - subject['@graph'] = []; - } - var graph = subject['@graph']; - var ids = Object.keys(nodeMap).sort(); - for(var ii = 0; ii < ids.length; ++ii) { - var node = nodeMap[ids[ii]]; - // only add full subjects - if(!_isSubjectReference(node)) { - graph.push(node); - } + + // chop off from the tail first. + var hash = rest.indexOf('#'); + if (hash !== -1) { + // got a fragment string. + this.hash = rest.substr(hash); + rest = rest.slice(0, hash); + } + var qm = rest.indexOf('?'); + if (qm !== -1) { + this.search = rest.substr(qm); + this.query = rest.substr(qm + 1); + if (parseQueryString) { + this.query = querystring.parse(this.query); } + rest = rest.slice(0, qm); + } else if (parseQueryString) { + // no query string, but parseQueryString still requested + this.search = ''; + this.query = {}; + } + if (rest) this.pathname = rest; + if (slashedProtocol[lowerProto] && + this.hostname && !this.pathname) { + this.pathname = '/'; } - return defaultGraph; -} -/** - * Frames subjects according to the given frame. - * - * @param state the current framing state. - * @param subjects the subjects to filter. - * @param frame the frame. - * @param parent the parent subject or top-level array. - * @param property the parent property, initialized to null. - */ -function _frame(state, subjects, frame, parent, property) { - // validate the frame - _validateFrame(frame); - frame = frame[0]; + //to support http.request + if (this.pathname || this.search) { + var p = this.pathname || ''; + var s = this.search || ''; + this.path = p + s; + } - // get flags for current frame - var options = state.options; - var flags = { - embed: _getFrameFlag(frame, options, 'embed'), - explicit: _getFrameFlag(frame, options, 'explicit'), - requireAll: _getFrameFlag(frame, options, 'requireAll') - }; + // finally, reconstruct the href based on what has been validated. + this.href = this.format(); + return this; +}; - // filter out subjects that match the frame - var matches = _filterSubjects(state, subjects, frame, flags); +// format a parsed object into a url string +function urlFormat(obj) { + // ensure it's an object, and not a string url. + // If it's an obj, this is a no-op. + // this way, you can call url_format() on strings + // to clean up potentially wonky urls. + if (isString(obj)) obj = urlParse(obj); + if (!(obj instanceof Url)) return Url.prototype.format.call(obj); + return obj.format(); +} - // add matches to output - var ids = Object.keys(matches).sort(); - for(var idx = 0; idx < ids.length; ++idx) { - var id = ids[idx]; - var subject = matches[id]; +Url.prototype.format = function() { + var auth = this.auth || ''; + if (auth) { + auth = encodeURIComponent(auth); + auth = auth.replace(/%3A/i, ':'); + auth += '@'; + } - if(flags.embed === '@link' && id in state.link) { - // TODO: may want to also match an existing linked subject against - // the current frame ... so different frames could produce different - // subjects that are only shared in-memory when the frames are the same + var protocol = this.protocol || '', + pathname = this.pathname || '', + hash = this.hash || '', + host = false, + query = ''; - // add existing linked subject - _addFrameOutput(parent, property, state.link[id]); - continue; + if (this.host) { + host = auth + this.host; + } else if (this.hostname) { + host = auth + (this.hostname.indexOf(':') === -1 ? + this.hostname : + '[' + this.hostname + ']'); + if (this.port) { + host += ':' + this.port; } + } - /* Note: In order to treat each top-level match as a compartmentalized - result, clear the unique embedded subjects map when the property is null, - which only occurs at the top-level. */ - if(property === null) { - state.uniqueEmbeds = {}; - } + if (this.query && + isObject(this.query) && + Object.keys(this.query).length) { + query = querystring.stringify(this.query); + } - // start output for subject - var output = {}; - output['@id'] = id; - state.link[id] = output; + var search = this.search || (query && ('?' + query)) || ''; - // if embed is @never or if a circular reference would be created by an - // embed, the subject cannot be embedded, just add the reference; - // note that a circular reference won't occur when the embed flag is - // `@link` as the above check will short-circuit before reaching this point - if(flags.embed === '@never' || - _createsCircularReference(subject, state.subjectStack)) { - _addFrameOutput(parent, property, output); - continue; - } + if (protocol && protocol.substr(-1) !== ':') protocol += ':'; - // if only the last match should be embedded - if(flags.embed === '@last') { - // remove any existing embed - if(id in state.uniqueEmbeds) { - _removeEmbed(state, id); - } - state.uniqueEmbeds[id] = {parent: parent, property: property}; - } + // only the slashedProtocols get the //. Not mailto:, xmpp:, etc. + // unless they had them to begin with. + if (this.slashes || + (!protocol || slashedProtocol[protocol]) && host !== false) { + host = '//' + (host || ''); + if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname; + } else if (!host) { + host = ''; + } - // push matching subject onto stack to enable circular embed checks - state.subjectStack.push(subject); + if (hash && hash.charAt(0) !== '#') hash = '#' + hash; + if (search && search.charAt(0) !== '?') search = '?' + search; - // iterate over subject properties - var props = Object.keys(subject).sort(); - for(var i = 0; i < props.length; i++) { - var prop = props[i]; + pathname = pathname.replace(/[?#]/g, function(match) { + return encodeURIComponent(match); + }); + search = search.replace('#', '%23'); - // copy keywords to output - if(_isKeyword(prop)) { - output[prop] = _clone(subject[prop]); - continue; - } + return protocol + host + pathname + search + hash; +}; - // explicit is on and property isn't in the frame, skip processing - if(flags.explicit && !(prop in frame)) { - continue; - } +function urlResolve(source, relative) { + return urlParse(source, false, true).resolve(relative); +} - // add objects - var objects = subject[prop]; - for(var oi = 0; oi < objects.length; ++oi) { - var o = objects[oi]; +Url.prototype.resolve = function(relative) { + return this.resolveObject(urlParse(relative, false, true)).format(); +}; - // recurse into list - if(_isList(o)) { - // add empty list - var list = {'@list': []}; - _addFrameOutput(output, prop, list); +function urlResolveObject(source, relative) { + if (!source) return relative; + return urlParse(source, false, true).resolveObject(relative); +} - // add list objects - var src = o['@list']; - for(var n in src) { - o = src[n]; - if(_isSubjectReference(o)) { - var subframe = (prop in frame ? - frame[prop][0]['@list'] : _createImplicitFrame(flags)); - // recurse into subject reference - _frame(state, [o['@id']], subframe, list, '@list'); - } else { - // include other values automatically - _addFrameOutput(list, '@list', _clone(o)); - } - } - continue; - } +Url.prototype.resolveObject = function(relative) { + if (isString(relative)) { + var rel = new Url(); + rel.parse(relative, false, true); + relative = rel; + } - if(_isSubjectReference(o)) { - // recurse into subject reference - var subframe = (prop in frame ? - frame[prop] : _createImplicitFrame(flags)); - _frame(state, [o['@id']], subframe, output, prop); - } else { - // include other values automatically - _addFrameOutput(output, prop, _clone(o)); - } - } - } + var result = new Url(); + Object.keys(this).forEach(function(k) { + result[k] = this[k]; + }, this); - // handle defaults - var props = Object.keys(frame).sort(); - for(var i = 0; i < props.length; ++i) { - var prop = props[i]; + // hash is always overridden, no matter what. + // even href="" will remove it. + result.hash = relative.hash; - // skip keywords - if(_isKeyword(prop)) { - continue; - } + // if the relative url is empty, then there's nothing left to do here. + if (relative.href === '') { + result.href = result.format(); + return result; + } - // 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}]; - } - } + // hrefs like //foo/bar always cut to the protocol. + if (relative.slashes && !relative.protocol) { + // take everything except the protocol from relative + Object.keys(relative).forEach(function(k) { + if (k !== 'protocol') + result[k] = relative[k]; + }); - // add output to parent - _addFrameOutput(parent, property, output); + //urlParse appends trailing / to urls like http://www.example.com + if (slashedProtocol[result.protocol] && + result.hostname && !result.pathname) { + result.path = result.pathname = '/'; + } - // pop matching subject from circular ref-checking stack - state.subjectStack.pop(); + result.href = result.format(); + return result; } -} -/** - * Creates an implicit frame when recursing through subject matches. If - * a frame doesn't have an explicit frame for a particular property, then - * a wildcard child frame will be created that uses the same flags that the - * parent frame used. - * - * @param flags the current framing flags. - * - * @return the implicit frame. - */ -function _createImplicitFrame(flags) { - var frame = {}; - for(var key in flags) { - if(flags[key] !== undefined) { - frame['@' + key] = [flags[key]]; + if (relative.protocol && relative.protocol !== result.protocol) { + // if it's a known url protocol, then changing + // the protocol does weird things + // first, if it's not file:, then we MUST have a host, + // and if there was a path + // to begin with, then we MUST have a path. + // if it is file:, then the host is dropped, + // because that's known to be hostless. + // anything else is assumed to be absolute. + if (!slashedProtocol[relative.protocol]) { + Object.keys(relative).forEach(function(k) { + result[k] = relative[k]; + }); + result.href = result.format(); + return result; } - } - return [frame]; -} -/** - * Checks the current subject stack to see if embedding the given subject - * would cause a circular reference. - * - * @param subjectToEmbed the subject to embed. - * @param subjectStack the current stack of subjects. - * - * @return true if a circular reference would be created, false if not. - */ -function _createsCircularReference(subjectToEmbed, subjectStack) { - for(var i = subjectStack.length - 1; i >= 0; --i) { - if(subjectStack[i]['@id'] === subjectToEmbed['@id']) { - return true; + result.protocol = relative.protocol; + if (!relative.host && !hostlessProtocol[relative.protocol]) { + var relPath = (relative.pathname || '').split('/'); + while (relPath.length && !(relative.host = relPath.shift())); + if (!relative.host) relative.host = ''; + if (!relative.hostname) relative.hostname = ''; + if (relPath[0] !== '') relPath.unshift(''); + if (relPath.length < 2) relPath.unshift(''); + result.pathname = relPath.join('/'); + } else { + result.pathname = relative.pathname; } - } - return false; -} - -/** - * Gets the frame flag value for the given flag name. - * - * @param frame the frame. - * @param options the framing options. - * @param name the flag name. - * - * @return the flag value. - */ -function _getFrameFlag(frame, options, name) { - var flag = '@' + name; - var rval = (flag in frame ? frame[flag][0] : options[name]); - if(name === 'embed') { - // default is "@last" - // backwards-compatibility support for "embed" maps: - // true => "@last" - // false => "@never" - if(rval === true) { - rval = '@last'; - } else if(rval === false) { - rval = '@never'; - } else if(rval !== '@always' && rval !== '@never' && rval !== '@link') { - rval = '@last'; + result.search = relative.search; + result.query = relative.query; + result.host = relative.host || ''; + result.auth = relative.auth; + result.hostname = relative.hostname || relative.host; + result.port = relative.port; + // to support http.request + if (result.pathname || result.search) { + var p = result.pathname || ''; + var s = result.search || ''; + result.path = p + s; } + result.slashes = result.slashes || relative.slashes; + result.href = result.format(); + return result; } - return rval; -} -/** - * Validates a JSON-LD frame, throwing an exception if the frame is invalid. - * - * @param frame the frame to validate. - */ -function _validateFrame(frame) { - if(!_isArray(frame) || frame.length !== 1 || !_isObject(frame[0])) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; a JSON-LD frame must be a single object.', - 'jsonld.SyntaxError', {frame: frame}); - } -} + var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'), + isRelAbs = ( + relative.host || + relative.pathname && relative.pathname.charAt(0) === '/' + ), + mustEndAbs = (isRelAbs || isSourceAbs || + (result.host && relative.pathname)), + removeAllDots = mustEndAbs, + srcPath = result.pathname && result.pathname.split('/') || [], + relPath = relative.pathname && relative.pathname.split('/') || [], + psychotic = result.protocol && !slashedProtocol[result.protocol]; -/** - * Returns a map of all of the subjects that match a parsed frame. - * - * @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, 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, flags)) { - rval[id] = subject; + // if the url is a non-slashed url, then relative + // links like ../.. should be able + // to crawl up to the hostname, as well. This is strange. + // result.protocol has already been set by now. + // Later on, put the first path part into the host field. + if (psychotic) { + result.hostname = ''; + result.port = null; + if (result.host) { + if (srcPath[0] === '') srcPath[0] = result.host; + else srcPath.unshift(result.host); } - } - return rval; -} - -/** - * Returns true if the given subject matches the given frame. - * - * @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, flags) { - // check @type (object value means 'any' type, fall through to ducktyping) - if('@type' in frame && - !(frame['@type'].length === 1 && _isObject(frame['@type'][0]))) { - var types = frame['@type']; - for(var i = 0; i < types.length; ++i) { - // any matching @type is a match - if(jsonld.hasValue(subject, '@type', types[i])) { - return true; + result.host = ''; + if (relative.protocol) { + relative.hostname = null; + relative.port = null; + if (relative.host) { + if (relPath[0] === '') relPath[0] = relative.host; + else relPath.unshift(relative.host); } + relative.host = null; } - return false; + mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === ''); } - // check ducktype - var wildcard = true; - var matchesSome = false; - for(var key in frame) { - if(_isKeyword(key)) { - // skip non-@id and non-@type - if(key !== '@id' && key !== '@type') { - continue; + if (isRelAbs) { + // it's absolute. + result.host = (relative.host || relative.host === '') ? + relative.host : result.host; + result.hostname = (relative.hostname || relative.hostname === '') ? + relative.hostname : result.hostname; + result.search = relative.search; + result.query = relative.query; + srcPath = relPath; + // fall through to the dot-handling below. + } else if (relPath.length) { + // it's relative + // throw away the existing file, and take the new path instead. + if (!srcPath) srcPath = []; + srcPath.pop(); + srcPath = srcPath.concat(relPath); + result.search = relative.search; + result.query = relative.query; + } else if (!isNullOrUndefined(relative.search)) { + // just pull out the search. + // like href='?foo'. + // Put this after the other two cases because it simplifies the booleans + if (psychotic) { + result.hostname = result.host = srcPath.shift(); + //occationaly the auth can get stuck only in host + //this especialy happens in cases like + //url.resolveObject('mailto:local1@domain1', 'local2@domain2') + var authInHost = result.host && result.host.indexOf('@') > 0 ? + result.host.split('@') : false; + if (authInHost) { + result.auth = authInHost.shift(); + result.host = result.hostname = authInHost.shift(); } - wildcard = false; + } + result.search = relative.search; + result.query = relative.query; + //to support http.request + if (!isNull(result.pathname) || !isNull(result.search)) { + result.path = (result.pathname ? result.pathname : '') + + (result.search ? result.search : ''); + } + result.href = result.format(); + return result; + } - // check @id for a specific @id value - if(key === '@id' && _isString(frame[key])) { - if(subject[key] !== frame[key]) { - return false; - } - matchesSome = true; - continue; - } + if (!srcPath.length) { + // no path at all. easy. + // we've already handled the other stuff above. + result.pathname = null; + //to support http.request + if (result.search) { + result.path = '/' + result.search; + } else { + result.path = null; } + result.href = result.format(); + return result; + } - wildcard = false; + // if a url ENDs in . or .., then it must get a trailing slash. + // however, if it ends in anything else non-slashy, + // then it must NOT get a trailing slash. + var last = srcPath.slice(-1)[0]; + var hasTrailingSlash = ( + (result.host || relative.host) && (last === '.' || last === '..') || + last === ''); - 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; + // strip single dots, resolve double dots to parent dir + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = srcPath.length; i >= 0; i--) { + last = srcPath[i]; + if (last == '.') { + srcPath.splice(i, 1); + } else if (last === '..') { + srcPath.splice(i, 1); + up++; + } else if (up) { + srcPath.splice(i, 1); + up--; } + } - // 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; + // if the path is allowed to go above the root, restore leading ..s + if (!mustEndAbs && !removeAllDots) { + for (; up--; up) { + srcPath.unshift('..'); } } - // return true if wildcard or subject matches some properties - return wildcard || matchesSome; -} + if (mustEndAbs && srcPath[0] !== '' && + (!srcPath[0] || srcPath[0].charAt(0) !== '/')) { + srcPath.unshift(''); + } -/** - * Removes an existing embed. - * - * @param state the current framing state. - * @param id the @id of the embed to remove. - */ -function _removeEmbed(state, id) { - // get existing embed - var embeds = state.uniqueEmbeds; - var embed = embeds[id]; - var parent = embed.parent; - var property = embed.property; + if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) { + srcPath.push(''); + } - // create reference to replace embed - var subject = {'@id': id}; + var isAbsolute = srcPath[0] === '' || + (srcPath[0] && srcPath[0].charAt(0) === '/'); - // remove existing embed - if(_isArray(parent)) { - // replace subject with reference - for(var i = 0; i < parent.length; ++i) { - if(jsonld.compareValues(parent[i], subject)) { - parent[i] = subject; - break; - } + // put the host back + if (psychotic) { + result.hostname = result.host = isAbsolute ? '' : + srcPath.length ? srcPath.shift() : ''; + //occationaly the auth can get stuck only in host + //this especialy happens in cases like + //url.resolveObject('mailto:local1@domain1', 'local2@domain2') + var authInHost = result.host && result.host.indexOf('@') > 0 ? + result.host.split('@') : false; + if (authInHost) { + result.auth = authInHost.shift(); + result.host = result.hostname = authInHost.shift(); } + } + + mustEndAbs = mustEndAbs || (result.host && srcPath.length); + + if (mustEndAbs && !isAbsolute) { + srcPath.unshift(''); + } + + if (!srcPath.length) { + result.pathname = null; + result.path = null; } else { - // replace subject with reference - var useArray = _isArray(parent[property]); - jsonld.removeValue(parent, property, subject, {propertyIsArray: useArray}); - jsonld.addValue(parent, property, subject, {propertyIsArray: useArray}); + result.pathname = srcPath.join('/'); } - // recursively remove dependent dangling embeds - var removeDependents = function(id) { - // get embed keys as a separate array to enable deleting keys in map - var ids = Object.keys(embeds); - for(var i = 0; i < ids.length; ++i) { - var next = ids[i]; - if(next in embeds && _isObject(embeds[next].parent) && - embeds[next].parent['@id'] === id) { - delete embeds[next]; - removeDependents(next); - } + //to support request.http + if (!isNull(result.pathname) || !isNull(result.search)) { + result.path = (result.pathname ? result.pathname : '') + + (result.search ? result.search : ''); + } + result.auth = relative.auth || result.auth; + result.slashes = result.slashes || relative.slashes; + result.href = result.format(); + return result; +}; + +Url.prototype.parseHost = function() { + var host = this.host; + var port = portPattern.exec(host); + if (port) { + port = port[0]; + if (port !== ':') { + this.port = port.substr(1); } - }; - removeDependents(id); + host = host.substr(0, host.length - port.length); + } + if (host) this.hostname = host; +}; + +function isString(arg) { + return typeof arg === "string"; } -/** - * Adds framing output to the given parent. - * - * @param parent the parent to add to. - * @param property the parent property. - * @param output the output to add. - */ -function _addFrameOutput(parent, property, output) { - if(_isObject(parent)) { - jsonld.addValue(parent, property, output, {propertyIsArray: true}); - } else { - parent.push(output); - } +function isObject(arg) { + return typeof arg === 'object' && arg !== null; } -/** - * Removes the @preserve keywords as the last step of the framing algorithm. - * - * @param ctx the active context used to compact the input. - * @param input the framed, compacted output. - * @param options the compaction options used. - * - * @return the resulting output. - */ -function _removePreserve(ctx, input, options) { - // recurse through arrays - if(_isArray(input)) { - var output = []; - for(var i = 0; i < input.length; ++i) { - var result = _removePreserve(ctx, input[i], options); - // drop nulls from arrays - if(result !== null) { - output.push(result); - } - } - input = output; - } else if(_isObject(input)) { - // remove @preserve - if('@preserve' in input) { - if(input['@preserve'] === '@null') { - return null; +function isNull(arg) { + return arg === null; +} +function isNullOrUndefined(arg) { + return arg == null; +} + +},{"punycode":10,"querystring":77}],35:[function(_dereq_,module,exports){ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true } - return input['@preserve']; - } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} - // skip @values - if(_isValue(input)) { - return input; - } +},{}],36:[function(_dereq_,module,exports){ +module.exports = function isBuffer(arg) { + return arg && typeof arg === 'object' + && typeof arg.copy === 'function' + && typeof arg.fill === 'function' + && typeof arg.readUInt8 === 'function'; +} +},{}],37:[function(_dereq_,module,exports){ +(function (process,global){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. - // recurse through @lists - if(_isList(input)) { - input['@list'] = _removePreserve(ctx, input['@list'], options); - return input; +var formatRegExp = /%[sdj%]/g; +exports.format = function(f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); } + return objects.join(' '); + } - // handle in-memory linked nodes - var idAlias = _compactIri(ctx, '@id'); - if(idAlias in input) { - var id = input[idAlias]; - if(id in options.link) { - var idx = options.link[id].indexOf(input); - if(idx === -1) { - // prevent circular visitation - options.link[id].push(input); - } else { - // already visited - return options.link[id][idx]; + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (x === '%%') return '%'; + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': + try { + return JSON.stringify(args[i++]); + } catch (_) { + return '[Circular]'; } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; +}; + + +// Mark that a method should not be used. +// Returns a modified function which warns once by default. +// If --no-deprecation is set, then it is a no-op. +exports.deprecate = function(fn, msg) { + // Allow for deprecating things in the process of starting up. + if (isUndefined(global.process)) { + return function() { + return exports.deprecate(fn, msg).apply(this, arguments); + }; + } + + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); } else { - // prevent circular visitation - options.link[id] = [input]; + console.error(msg); } + warned = true; } + return fn.apply(this, arguments); + } - // recurse through properties - for(var prop in input) { - var result = _removePreserve(ctx, input[prop], options); - var container = jsonld.getContextValue(ctx, prop, '@container'); - if(options.compactArrays && _isArray(result) && result.length === 1 && - container === null) { - result = result[0]; - } - input[prop] = result; + return deprecated; +}; + + +var debugs = {}; +var debugEnviron; +exports.debuglog = function(set) { + if (isUndefined(debugEnviron)) + debugEnviron = process.env.NODE_DEBUG || ''; + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function() { + var msg = exports.format.apply(exports, arguments); + console.error('%s %d: %s', set, pid, msg); + }; + } else { + debugs[set] = function() {}; } } - return input; -} + return debugs[set]; +}; + /** - * Compares two strings first based on length and then lexicographically. - * - * @param a the first string. - * @param b the second string. + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. * - * @return -1 if a < b, 1 if a > b, 0 if a == b. + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. */ -function _compareShortestLeast(a, b) { - if(a.length < b.length) { - return -1; - } - if(b.length < a.length) { - return 1; - } - if(a === b) { - return 0; +/* legacy: obj, showHidden, depth, colors*/ +function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); } - return (a < b) ? -1 : 1; + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); } +exports.inspect = inspect; -/** - * Picks the preferred compaction term from the given inverse context entry. - * - * @param activeCtx the active context. - * @param iri the IRI to pick the term for. - * @param value the value to pick the term for. - * @param containers the preferred containers. - * @param typeOrLanguage either '@type' or '@language'. - * @param typeOrLanguageValue the preferred value for '@type' or '@language'. - * - * @return the preferred term. - */ -function _selectTerm( - activeCtx, iri, value, containers, typeOrLanguage, typeOrLanguageValue) { - if(typeOrLanguageValue === null) { - typeOrLanguageValue = '@null'; - } - // preferences for the value of @type or @language - var prefs = []; +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +inspect.colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] +}; - // determine prefs for @id based on whether or not value compacts to a term - if((typeOrLanguageValue === '@id' || typeOrLanguageValue === '@reverse') && - _isSubjectReference(value)) { - // prefer @reverse first - if(typeOrLanguageValue === '@reverse') { - prefs.push('@reverse'); - } - // try to compact value to a term - var term = _compactIri(activeCtx, value['@id'], null, {vocab: true}); - if(term in activeCtx.mappings && - activeCtx.mappings[term] && - activeCtx.mappings[term]['@id'] === value['@id']) { - // prefer @vocab - prefs.push.apply(prefs, ['@vocab', '@id']); - } else { - // prefer @id - prefs.push.apply(prefs, ['@id', '@vocab']); - } +// Don't use 'blue' not visible on cmd.exe +inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' +}; + + +function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; } else { - prefs.push(typeOrLanguageValue); + return str; } - prefs.push('@none'); +} - var containerMap = activeCtx.inverse[iri]; - for(var ci = 0; ci < containers.length; ++ci) { - // if container not available in the map, continue - var container = containers[ci]; - if(!(container in containerMap)) { - continue; + +function stylizeNoColor(str, styleType) { + return str; +} + + +function arrayToHash(array) { + var hash = {}; + + array.forEach(function(val, idx) { + hash[val] = true; + }); + + return hash; +} + + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); } + return ret; + } - var typeOrLanguageValueMap = containerMap[container][typeOrLanguage]; - for(var pi = 0; pi < prefs.length; ++pi) { - // if type/language option not available in the map, continue - var pref = prefs[pi]; - if(!(pref in typeOrLanguageValueMap)) { - continue; - } + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } - // select term - return typeOrLanguageValueMap[pref]; + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if (isError(value) + && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); } } - return null; -} + var base = '', array = false, braces = ['{', '}']; -/** - * Compacts an IRI or keyword into a term or prefix if it can be. If the - * IRI has an associated value it may be passed. - * - * @param activeCtx the active context to use. - * @param iri the IRI to compact. - * @param value the value to check or null. - * @param relativeTo options for how to compact IRIs: - * vocab: true to split after @vocab, false not to. - * @param reverse true if a reverse property is being compacted, false if not. - * - * @return the compacted term, prefix, keyword alias, or the original IRI. - */ -function _compactIri(activeCtx, iri, value, relativeTo, reverse) { - // can't compact null - if(iri === null) { - return iri; + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; } - // default value and parent to null - if(_isUndefined(value)) { - value = null; + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; } - // default reverse to false - if(_isUndefined(reverse)) { - reverse = false; + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); } - relativeTo = relativeTo || {}; - var inverseCtx = activeCtx.getInverse(); + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } - // if term is a keyword, it can only be compacted to a simple alias - if(_isKeyword(iri)) { - if(iri in inverseCtx) { - return inverseCtx[iri]['@none']['@type']['@none']; - } - return iri; + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); } - // use inverse context to pick a term if iri is relative to vocab - if(relativeTo.vocab && iri in inverseCtx) { - var defaultLanguage = activeCtx['@language'] || '@none'; + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } - // prefer @index if available in value - var containers = []; - if(_isObject(value) && '@index' in value) { - containers.push('@index'); + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); } + } - // defaults for term selection based on type/language - var typeOrLanguage = '@language'; - var typeOrLanguageValue = '@null'; + ctx.seen.push(value); - if(reverse) { - typeOrLanguage = '@type'; - typeOrLanguageValue = '@reverse'; - containers.push('@set'); - } else if(_isList(value)) { - // choose the most specific term that works for all elements in @list - // only select @list containers if @index is NOT in value - if(!('@index' in value)) { - containers.push('@list'); - } - var list = value['@list']; - var commonLanguage = (list.length === 0) ? defaultLanguage : null; - var commonType = null; - for(var i = 0; i < list.length; ++i) { - var item = list[i]; - var itemLanguage = '@none'; - var itemType = '@none'; - if(_isValue(item)) { - if('@language' in item) { - itemLanguage = item['@language']; - } else if('@type' in item) { - itemType = item['@type']; - } else { - // plain literal - itemLanguage = '@null'; - } - } else { - itemType = '@id'; - } - if(commonLanguage === null) { - commonLanguage = itemLanguage; - } else if(itemLanguage !== commonLanguage && _isValue(item)) { - commonLanguage = '@none'; - } - if(commonType === null) { - commonType = itemType; - } else if(itemType !== commonType) { - commonType = '@none'; - } - // there are different languages and types in the list, so choose - // the most generic term, no need to keep iterating the list - if(commonLanguage === '@none' && commonType === '@none') { - break; - } - } - commonLanguage = commonLanguage || '@none'; - commonType = commonType || '@none'; - if(commonType !== '@none') { - typeOrLanguage = '@type'; - typeOrLanguageValue = commonType; - } else { - typeOrLanguageValue = commonLanguage; - } + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + + +function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); } else { - if(_isValue(value)) { - if('@language' in value && !('@index' in value)) { - containers.push('@language'); - typeOrLanguageValue = value['@language']; - } else if('@type' in value) { - typeOrLanguage = '@type'; - typeOrLanguageValue = value['@type']; - } - } else { - typeOrLanguage = '@type'; - typeOrLanguageValue = '@id'; - } - containers.push('@set'); + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); } + }); + return output; +} - // do term selection - containers.push('@none'); - var term = _selectTerm( - activeCtx, iri, value, containers, typeOrLanguage, typeOrLanguageValue); - if(term !== null) { - return term; + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); } } - - // no term match, use @vocab if available - if(relativeTo.vocab) { - if('@vocab' in activeCtx) { - // determine if vocab is a prefix of the iri - var vocab = activeCtx['@vocab']; - if(iri.indexOf(vocab) === 0 && iri !== vocab) { - // use suffix as relative iri if it is not a term in the active context - var suffix = iri.substr(vocab.length); - if(!(suffix in activeCtx.mappings)) { - return suffix; + if (!hasOwnProperty(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); } } + } else { + str = ctx.stylize('[Circular]', 'special'); } } - - // no term or @vocab match, check for possible CURIEs - var choice = null; - var idx = 0; - var partialMatches = []; - var iriMap = activeCtx.fastCurieMap; - // check for partial matches of against `iri`, which means look until - // iri.length - 1, not full length - var maxPartialLength = iri.length - 1; - for(; idx < maxPartialLength && iri[idx] in iriMap; ++idx) { - iriMap = iriMap[iri[idx]]; - if('' in iriMap) { - partialMatches.push(iriMap[''][0]); + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; } - } - // check partial matches in reverse order to prefer longest ones first - for(var i = partialMatches.length - 1; i >= 0; --i) { - var entry = partialMatches[i]; - var terms = entry.terms; - for(var ti = 0; ti < terms.length; ++ti) { - // a CURIE is usable if: - // 1. it has no mapping, OR - // 2. value is null, which means we're not compacting an @value, AND - // the mapping matches the IRI - var curie = terms[ti] + ':' + iri.substr(entry.iri.length); - var isUsableCurie = (!(curie in activeCtx.mappings) || - (value === null && activeCtx.mappings[curie]['@id'] === iri)); - - // select curie if it is shorter or the same length but lexicographically - // less than the current choice - if(isUsableCurie && (choice === null || - _compareShortestLeast(curie, choice) < 0)) { - choice = curie; - } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); } } - // return chosen curie - if(choice !== null) { - return choice; - } + return name + ': ' + str; +} - // compact IRI relative to base - if(!relativeTo.vocab) { - return _removeBase(activeCtx['@base'], iri); + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; } - // return IRI as is - return iri; + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; } -/** - * Performs value compaction on an object with '@value' or '@id' as the only - * property. - * - * @param activeCtx the active context. - * @param activeProperty the active property that points to the value. - * @param value the value to compact. - * - * @return the compaction result. - */ -function _compactValue(activeCtx, activeProperty, value) { - // value is a @value - if(_isValue(value)) { - // get context rules - var type = jsonld.getContextValue(activeCtx, activeProperty, '@type'); - var language = jsonld.getContextValue( - activeCtx, activeProperty, '@language'); - var container = jsonld.getContextValue( - activeCtx, activeProperty, '@container'); - // whether or not the value has an @index that must be preserved - var preserveIndex = (('@index' in value) && - container !== '@index'); +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return Array.isArray(ar); +} +exports.isArray = isArray; - // if there's no @index to preserve ... - if(!preserveIndex) { - // matching @type or @language specified in context, compact value - if(value['@type'] === type || value['@language'] === language) { - return value['@value']; - } - } +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; - // return just the value of @value if all are true: - // 1. @value is the only key or @index isn't being preserved - // 2. there is no default language or @value is not a string or - // the key has a mapping with a null @language - var keyCount = Object.keys(value).length; - var isValueOnlyKey = (keyCount === 1 || - (keyCount === 2 && ('@index' in value) && !preserveIndex)); - var hasDefaultLanguage = ('@language' in activeCtx); - var isValueString = _isString(value['@value']); - var hasNullMapping = (activeCtx.mappings[activeProperty] && - activeCtx.mappings[activeProperty]['@language'] === null); - if(isValueOnlyKey && - (!hasDefaultLanguage || !isValueString || hasNullMapping)) { - return value['@value']; - } +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; - var rval = {}; +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; - // preserve @index - if(preserveIndex) { - rval[_compactIri(activeCtx, '@index')] = value['@index']; - } +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; - if('@type' in value) { - // compact @type IRI - rval[_compactIri(activeCtx, '@type')] = _compactIri( - activeCtx, value['@type'], null, {vocab: true}); - } else if('@language' in value) { - // alias @language - rval[_compactIri(activeCtx, '@language')] = value['@language']; - } +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; - // alias @value - rval[_compactIri(activeCtx, '@value')] = value['@value']; +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; - return rval; - } +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; - // value is a subject reference - var expandedProperty = _expandIri(activeCtx, activeProperty, {vocab: true}); - var type = jsonld.getContextValue(activeCtx, activeProperty, '@type'); - var compacted = _compactIri( - activeCtx, value['@id'], null, {vocab: type === '@vocab'}); +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; - // compact to scalar - if(type === '@id' || type === '@vocab' || expandedProperty === '@graph') { - return compacted; - } +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; - var rval = {}; - rval[_compactIri(activeCtx, '@id')] = compacted; - return rval; +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = _dereq_('./support/isBuffer'); + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + + +function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); +} + + +var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; + +// 26 Feb 16:19:34 +function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); } + +// log is just a thin wrapper to console.log that prepends a timestamp +exports.log = function() { + console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); +}; + + /** - * Creates a term definition during context processing. + * Inherit the prototype methods from one constructor into another. * - * @param activeCtx the current active context. - * @param localCtx the local context being processed. - * @param term the term in the local context to define the mapping for. - * @param defined a map of defining/defined keys to detect cycles and prevent - * double definitions. + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. */ -function _createTermDefinition(activeCtx, localCtx, term, defined) { - if(term in defined) { - // term already defined - if(defined[term]) { - return; - } - // cycle detected - throw new JsonLdError( - 'Cyclical context definition detected.', - 'jsonld.CyclicalContext', - {code: 'cyclic IRI mapping', context: localCtx, term: term}); - } - - // now defining term - defined[term] = false; +exports.inherits = _dereq_('inherits'); - if(_isKeyword(term)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; keywords cannot be overridden.', - 'jsonld.SyntaxError', - {code: 'keyword redefinition', context: localCtx, term: term}); - } +exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; - if(term === '') { - throw new JsonLdError( - 'Invalid JSON-LD syntax; a term cannot be an empty string.', - 'jsonld.SyntaxError', - {code: 'invalid term definition', context: localCtx}); + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; } + return origin; +}; - // remove old mapping - if(activeCtx.mappings[term]) { - delete activeCtx.mappings[term]; - } +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} - // get context term value - var value = localCtx[term]; +}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - // clear context entry - if(value === null || (_isObject(value) && value['@id'] === null)) { - activeCtx.mappings[term] = null; - defined[term] = true; - return; - } +},{"./support/isBuffer":36,"_process":74,"inherits":35}],38:[function(_dereq_,module,exports){ +(function (Buffer){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. - // convert short-hand value to object w/@id - if(_isString(value)) { - value = {'@id': value}; - } +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. - if(!_isObject(value)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; @context property values must be ' + - 'strings or objects.', - 'jsonld.SyntaxError', - {code: 'invalid term definition', context: localCtx}); +function isArray(arg) { + if (Array.isArray) { + return Array.isArray(arg); } + return objectToString(arg) === '[object Array]'; +} +exports.isArray = isArray; - // create new mapping - var mapping = activeCtx.mappings[term] = {}; - mapping.reverse = false; +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; - if('@reverse' in value) { - if('@id' in value) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; a @reverse term definition must not ' + - 'contain @id.', 'jsonld.SyntaxError', - {code: 'invalid reverse property', context: localCtx}); - } - var reverse = value['@reverse']; - if(!_isString(reverse)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; a @context @reverse value must be a string.', - 'jsonld.SyntaxError', {code: 'invalid IRI mapping', context: localCtx}); - } +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; - // expand and add @id mapping - var id = _expandIri( - activeCtx, reverse, {vocab: true, base: false}, localCtx, defined); - if(!_isAbsoluteIri(id)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; a @context @reverse value must be an ' + - 'absolute IRI or a blank node identifier.', - 'jsonld.SyntaxError', {code: 'invalid IRI mapping', context: localCtx}); - } - mapping['@id'] = id; - mapping.reverse = true; - } else if('@id' in value) { - var id = value['@id']; - if(!_isString(id)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; a @context @id value must be an array ' + - 'of strings or a string.', - 'jsonld.SyntaxError', {code: 'invalid IRI mapping', context: localCtx}); - } - if(id !== term) { - // expand and add @id mapping - id = _expandIri( - activeCtx, id, {vocab: true, base: false}, localCtx, defined); - if(!_isAbsoluteIri(id) && !_isKeyword(id)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; a @context @id value must be an ' + - 'absolute IRI, a blank node identifier, or a keyword.', - 'jsonld.SyntaxError', - {code: 'invalid IRI mapping', context: localCtx}); - } - mapping['@id'] = id; - } - } +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; - // always compute whether term has a colon as an optimization for - // _compactIri - var colon = term.indexOf(':'); - mapping._termHasColon = (colon !== -1); +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; - if(!('@id' in mapping)) { - // see if the term has a prefix - if(mapping._termHasColon) { - var prefix = term.substr(0, colon); - if(prefix in localCtx) { - // define parent prefix - _createTermDefinition(activeCtx, localCtx, prefix, defined); - } +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; - if(activeCtx.mappings[prefix]) { - // set @id based on prefix parent - var suffix = term.substr(colon + 1); - mapping['@id'] = activeCtx.mappings[prefix]['@id'] + suffix; - } else { - // term is an absolute IRI - mapping['@id'] = term; - } - } else { - // non-IRIs *must* define @ids if @vocab is not available - if(!('@vocab' in activeCtx)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; @context terms must define an @id.', - 'jsonld.SyntaxError', - {code: 'invalid IRI mapping', context: localCtx, term: term}); - } - // prepend vocab to term - mapping['@id'] = activeCtx['@vocab'] + term; - } - } +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; - // IRI mapping now defined - defined[term] = true; +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; - if('@type' in value) { - var type = value['@type']; - if(!_isString(type)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; an @context @type values must be a string.', - 'jsonld.SyntaxError', - {code: 'invalid type mapping', context: localCtx}); - } +function isRegExp(re) { + return objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; - if(type !== '@id' && type !== '@vocab') { - // expand @type to full IRI - type = _expandIri( - activeCtx, type, {vocab: true, base: false}, localCtx, defined); - if(!_isAbsoluteIri(type)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; an @context @type value must be an ' + - 'absolute IRI.', - 'jsonld.SyntaxError', - {code: 'invalid type mapping', context: localCtx}); - } - if(type.indexOf('_:') === 0) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; an @context @type values must be an IRI, ' + - 'not a blank node identifier.', - 'jsonld.SyntaxError', - {code: 'invalid type mapping', context: localCtx}); - } - } +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; - // add @type to mapping - mapping['@type'] = type; - } +function isDate(d) { + return objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; - if('@container' in value) { - var container = value['@container']; - if(container !== '@list' && container !== '@set' && - container !== '@index' && container !== '@language') { - throw new JsonLdError( - 'Invalid JSON-LD syntax; @context @container value must be ' + - 'one of the following: @list, @set, @index, or @language.', - 'jsonld.SyntaxError', - {code: 'invalid container mapping', context: localCtx}); - } - if(mapping.reverse && container !== '@index' && container !== '@set' && - container !== null) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; @context @container value for a @reverse ' + - 'type definition must be @index or @set.', 'jsonld.SyntaxError', - {code: 'invalid reverse property', context: localCtx}); - } +function isError(e) { + return (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; - // add @container to mapping - mapping['@container'] = container; - } +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; - if('@language' in value && !('@type' in value)) { - var language = value['@language']; - if(language !== null && !_isString(language)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; @context @language value must be ' + - 'a string or null.', 'jsonld.SyntaxError', - {code: 'invalid language mapping', context: localCtx}); - } +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; - // add @language to mapping - if(language !== null) { - language = language.toLowerCase(); - } - mapping['@language'] = language; - } +exports.isBuffer = Buffer.isBuffer; - // disallow aliasing @context and @preserve - var id = mapping['@id']; - if(id === '@context' || id === '@preserve') { - throw new JsonLdError( - 'Invalid JSON-LD syntax; @context and @preserve cannot be aliased.', - 'jsonld.SyntaxError', {code: 'invalid keyword alias', context: localCtx}); - } +function objectToString(o) { + return Object.prototype.toString.call(o); } -/** - * Expands a string to a full IRI. The string may be a term, a prefix, a - * relative IRI, or an absolute IRI. The associated absolute IRI will be - * returned. - * - * @param activeCtx the current active context. - * @param value the string to expand. - * @param relativeTo options for how to resolve relative IRIs: - * 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 context processing). - * @param defined a map for tracking cycles in context definitions (only given - * if called during context processing). - * - * @return the expanded value. - */ -function _expandIri(activeCtx, value, relativeTo, localCtx, defined) { - // already expanded - if(value === null || _isKeyword(value)) { - return value; - } +}).call(this,{"isBuffer":_dereq_("../../is-buffer/index.js")}) - // ensure value is interpreted as a string - value = String(value); +},{"../../is-buffer/index.js":44}],39:[function(_dereq_,module,exports){ +(function (process,global){ +/*! + * @overview es6-promise - a tiny implementation of Promises/A+. + * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) + * @license Licensed under MIT license + * See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE + * @version 2.3.0 + */ - // define term dependency if not defined - if(localCtx && value in localCtx && defined[value] !== true) { - _createTermDefinition(activeCtx, localCtx, value, defined); - } +(function() { + "use strict"; + function lib$es6$promise$utils$$objectOrFunction(x) { + return typeof x === 'function' || (typeof x === 'object' && x !== null); + } - relativeTo = relativeTo || {}; - if(relativeTo.vocab) { - var mapping = activeCtx.mappings[value]; + function lib$es6$promise$utils$$isFunction(x) { + return typeof x === 'function'; + } - // value is explicitly ignored with a null mapping - if(mapping === null) { - return null; + function lib$es6$promise$utils$$isMaybeThenable(x) { + return typeof x === 'object' && x !== null; } - if(mapping) { - // value is a term - return mapping['@id']; + var lib$es6$promise$utils$$_isArray; + if (!Array.isArray) { + lib$es6$promise$utils$$_isArray = function (x) { + return Object.prototype.toString.call(x) === '[object Array]'; + }; + } else { + lib$es6$promise$utils$$_isArray = Array.isArray; } - } - // split value into prefix:suffix - var colon = value.indexOf(':'); - if(colon !== -1) { - var prefix = value.substr(0, colon); - var suffix = value.substr(colon + 1); + var lib$es6$promise$utils$$isArray = lib$es6$promise$utils$$_isArray; + var lib$es6$promise$asap$$len = 0; + var lib$es6$promise$asap$$toString = {}.toString; + var lib$es6$promise$asap$$vertxNext; + var lib$es6$promise$asap$$customSchedulerFn; - // do not expand blank nodes (prefix of '_') or already-absolute - // IRIs (suffix of '//') - if(prefix === '_' || suffix.indexOf('//') === 0) { - return value; + var lib$es6$promise$asap$$asap = function asap(callback, arg) { + lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len] = callback; + lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len + 1] = arg; + lib$es6$promise$asap$$len += 2; + if (lib$es6$promise$asap$$len === 2) { + // If len is 2, that means that we need to schedule an async flush. + // If additional callbacks are queued before the queue is flushed, they + // will be processed by this flush that we are scheduling. + if (lib$es6$promise$asap$$customSchedulerFn) { + lib$es6$promise$asap$$customSchedulerFn(lib$es6$promise$asap$$flush); + } else { + lib$es6$promise$asap$$scheduleFlush(); + } + } } - // prefix dependency not defined, define it - if(localCtx && prefix in localCtx) { - _createTermDefinition(activeCtx, localCtx, prefix, defined); + function lib$es6$promise$asap$$setScheduler(scheduleFn) { + lib$es6$promise$asap$$customSchedulerFn = scheduleFn; } - // use mapping if prefix is defined - var mapping = activeCtx.mappings[prefix]; - if(mapping) { - return mapping['@id'] + suffix; + function lib$es6$promise$asap$$setAsap(asapFn) { + lib$es6$promise$asap$$asap = asapFn; } - // already absolute IRI - return value; - } + var lib$es6$promise$asap$$browserWindow = (typeof window !== 'undefined') ? window : undefined; + var lib$es6$promise$asap$$browserGlobal = lib$es6$promise$asap$$browserWindow || {}; + var lib$es6$promise$asap$$BrowserMutationObserver = lib$es6$promise$asap$$browserGlobal.MutationObserver || lib$es6$promise$asap$$browserGlobal.WebKitMutationObserver; + var lib$es6$promise$asap$$isNode = typeof process !== 'undefined' && {}.toString.call(process) === '[object process]'; - // prepend vocab - if(relativeTo.vocab && '@vocab' in activeCtx) { - return activeCtx['@vocab'] + value; - } + // test for web worker but not in IE10 + var lib$es6$promise$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' && + typeof importScripts !== 'undefined' && + typeof MessageChannel !== 'undefined'; - // prepend base - var rval = value; - if(relativeTo.base) { - rval = jsonld.prependBase(activeCtx['@base'], rval); - } + // node + function lib$es6$promise$asap$$useNextTick() { + var nextTick = process.nextTick; + // node version 0.10.x displays a deprecation warning when nextTick is used recursively + // setImmediate should be used instead instead + var version = process.versions.node.match(/^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$/); + if (Array.isArray(version) && version[1] === '0' && version[2] === '10') { + nextTick = setImmediate; + } + return function() { + nextTick(lib$es6$promise$asap$$flush); + }; + } - return rval; -} + // vertx + function lib$es6$promise$asap$$useVertxTimer() { + return function() { + lib$es6$promise$asap$$vertxNext(lib$es6$promise$asap$$flush); + }; + } -function _prependBase(base, iri) { - // skip IRI processing - if(base === null) { - return iri; - } - // already an absolute IRI - if(iri.indexOf(':') !== -1) { - return iri; - } + function lib$es6$promise$asap$$useMutationObserver() { + var iterations = 0; + var observer = new lib$es6$promise$asap$$BrowserMutationObserver(lib$es6$promise$asap$$flush); + var node = document.createTextNode(''); + observer.observe(node, { characterData: true }); - // parse base if it is a string - if(_isString(base)) { - base = jsonld.url.parse(base || ''); - } + return function() { + node.data = (iterations = ++iterations % 2); + }; + } - // parse given IRI - var rel = jsonld.url.parse(iri); + // web worker + function lib$es6$promise$asap$$useMessageChannel() { + var channel = new MessageChannel(); + channel.port1.onmessage = lib$es6$promise$asap$$flush; + return function () { + channel.port2.postMessage(0); + }; + } - // per RFC3986 5.2.2 - var transform = { - protocol: base.protocol || '' - }; + function lib$es6$promise$asap$$useSetTimeout() { + return function() { + setTimeout(lib$es6$promise$asap$$flush, 1); + }; + } - if(rel.authority !== null) { - transform.authority = rel.authority; - transform.path = rel.path; - transform.query = rel.query; - } else { - transform.authority = base.authority; + var lib$es6$promise$asap$$queue = new Array(1000); + function lib$es6$promise$asap$$flush() { + for (var i = 0; i < lib$es6$promise$asap$$len; i+=2) { + var callback = lib$es6$promise$asap$$queue[i]; + var arg = lib$es6$promise$asap$$queue[i+1]; - if(rel.path === '') { - transform.path = base.path; - if(rel.query !== null) { - transform.query = rel.query; - } else { - transform.query = base.query; + callback(arg); + + lib$es6$promise$asap$$queue[i] = undefined; + lib$es6$promise$asap$$queue[i+1] = undefined; } - } else { - if(rel.path.indexOf('/') === 0) { - // IRI represents an absolute path - transform.path = rel.path; - } else { - // merge paths - var path = base.path; - // append relative path to the end of the last directory from base - if(rel.path !== '') { - path = path.substr(0, path.lastIndexOf('/') + 1); - if(path.length > 0 && path.substr(-1) !== '/') { - path += '/'; - } - path += rel.path; - } + lib$es6$promise$asap$$len = 0; + } - transform.path = path; + function lib$es6$promise$asap$$attemptVertex() { + try { + var r = _dereq_; + var vertx = r('vertx'); + lib$es6$promise$asap$$vertxNext = vertx.runOnLoop || vertx.runOnContext; + return lib$es6$promise$asap$$useVertxTimer(); + } catch(e) { + return lib$es6$promise$asap$$useSetTimeout(); } - transform.query = rel.query; } - } - // remove slashes and dots in path - transform.path = _removeDotSegments(transform.path, !!transform.authority); + var lib$es6$promise$asap$$scheduleFlush; + // Decide what async method to use to triggering processing of queued callbacks: + if (lib$es6$promise$asap$$isNode) { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useNextTick(); + } else if (lib$es6$promise$asap$$BrowserMutationObserver) { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMutationObserver(); + } else if (lib$es6$promise$asap$$isWorker) { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMessageChannel(); + } else if (lib$es6$promise$asap$$browserWindow === undefined && typeof _dereq_ === 'function') { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$attemptVertex(); + } else { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useSetTimeout(); + } - // construct URL - var rval = transform.protocol; - if(transform.authority !== null) { - rval += '//' + transform.authority; - } - rval += transform.path; - if(transform.query !== null) { - rval += '?' + transform.query; - } - if(rel.fragment !== null) { - rval += '#' + rel.fragment; - } + function lib$es6$promise$$internal$$noop() {} - // handle empty base - if(rval === '') { - rval = './'; - } + var lib$es6$promise$$internal$$PENDING = void 0; + var lib$es6$promise$$internal$$FULFILLED = 1; + var lib$es6$promise$$internal$$REJECTED = 2; - return rval; -} + var lib$es6$promise$$internal$$GET_THEN_ERROR = new lib$es6$promise$$internal$$ErrorObject(); -/** - * Removes a base IRI from the given absolute IRI. - * - * @param base the base IRI. - * @param iri the absolute IRI. - * - * @return the relative IRI if relative to base, otherwise the absolute IRI. - */ -function _removeBase(base, iri) { - // skip IRI processing - if(base === null) { - return iri; - } + function lib$es6$promise$$internal$$selfFullfillment() { + return new TypeError("You cannot resolve a promise with itself"); + } - if(_isString(base)) { - base = jsonld.url.parse(base || ''); - } + function lib$es6$promise$$internal$$cannotReturnOwn() { + return new TypeError('A promises callback cannot return that same promise.'); + } - // establish base root - var root = ''; - if(base.href !== '') { - root += (base.protocol || '') + '//' + (base.authority || ''); - } else if(iri.indexOf('//')) { - // support network-path reference with empty base - root += '//'; - } + function lib$es6$promise$$internal$$getThen(promise) { + try { + return promise.then; + } catch(error) { + lib$es6$promise$$internal$$GET_THEN_ERROR.error = error; + return lib$es6$promise$$internal$$GET_THEN_ERROR; + } + } - // IRI not relative to base - if(iri.indexOf(root) !== 0) { - return iri; - } + function lib$es6$promise$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) { + try { + then.call(value, fulfillmentHandler, rejectionHandler); + } catch(e) { + return e; + } + } - // remove root from IRI and parse remainder - var rel = jsonld.url.parse(iri.substr(root.length)); + function lib$es6$promise$$internal$$handleForeignThenable(promise, thenable, then) { + lib$es6$promise$asap$$asap(function(promise) { + var sealed = false; + var error = lib$es6$promise$$internal$$tryThen(then, thenable, function(value) { + if (sealed) { return; } + sealed = true; + if (thenable !== value) { + lib$es6$promise$$internal$$resolve(promise, value); + } else { + lib$es6$promise$$internal$$fulfill(promise, value); + } + }, function(reason) { + if (sealed) { return; } + sealed = true; - // remove path segments that match (do not remove last segment unless there - // is a hash or query) - var baseSegments = base.normalizedPath.split('/'); - var iriSegments = rel.normalizedPath.split('/'); - var last = (rel.fragment || rel.query) ? 0 : 1; - while(baseSegments.length > 0 && iriSegments.length > last) { - if(baseSegments[0] !== iriSegments[0]) { - break; + lib$es6$promise$$internal$$reject(promise, reason); + }, 'Settle: ' + (promise._label || ' unknown promise')); + + if (!sealed && error) { + sealed = true; + lib$es6$promise$$internal$$reject(promise, error); + } + }, promise); } - baseSegments.shift(); - iriSegments.shift(); - } - // use '../' for each non-matching base segment - var rval = ''; - if(baseSegments.length > 0) { - // don't count the last segment (if it ends with '/' last path doesn't - // count and if it doesn't end with '/' it isn't a path) - baseSegments.pop(); - for(var i = 0; i < baseSegments.length; ++i) { - rval += '../'; + function lib$es6$promise$$internal$$handleOwnThenable(promise, thenable) { + if (thenable._state === lib$es6$promise$$internal$$FULFILLED) { + lib$es6$promise$$internal$$fulfill(promise, thenable._result); + } else if (thenable._state === lib$es6$promise$$internal$$REJECTED) { + lib$es6$promise$$internal$$reject(promise, thenable._result); + } else { + lib$es6$promise$$internal$$subscribe(thenable, undefined, function(value) { + lib$es6$promise$$internal$$resolve(promise, value); + }, function(reason) { + lib$es6$promise$$internal$$reject(promise, reason); + }); + } } - } - // prepend remaining segments - rval += iriSegments.join('/'); + function lib$es6$promise$$internal$$handleMaybeThenable(promise, maybeThenable) { + if (maybeThenable.constructor === promise.constructor) { + lib$es6$promise$$internal$$handleOwnThenable(promise, maybeThenable); + } else { + var then = lib$es6$promise$$internal$$getThen(maybeThenable); - // add query and hash - if(rel.query !== null) { - rval += '?' + rel.query; - } - if(rel.fragment !== null) { - rval += '#' + rel.fragment; - } + if (then === lib$es6$promise$$internal$$GET_THEN_ERROR) { + lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$GET_THEN_ERROR.error); + } else if (then === undefined) { + lib$es6$promise$$internal$$fulfill(promise, maybeThenable); + } else if (lib$es6$promise$utils$$isFunction(then)) { + lib$es6$promise$$internal$$handleForeignThenable(promise, maybeThenable, then); + } else { + lib$es6$promise$$internal$$fulfill(promise, maybeThenable); + } + } + } - // handle empty base - if(rval === '') { - rval = './'; - } + function lib$es6$promise$$internal$$resolve(promise, value) { + if (promise === value) { + lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$selfFullfillment()); + } else if (lib$es6$promise$utils$$objectOrFunction(value)) { + lib$es6$promise$$internal$$handleMaybeThenable(promise, value); + } else { + lib$es6$promise$$internal$$fulfill(promise, value); + } + } - return rval; -} + function lib$es6$promise$$internal$$publishRejection(promise) { + if (promise._onerror) { + promise._onerror(promise._result); + } -/** - * Gets the initial context. - * - * @param options the options to use: - * [base] the document base IRI. - * - * @return the initial context. - */ -function _getInitialContext(options) { - var base = jsonld.url.parse(options.base || ''); - return { - '@base': base, - mappings: {}, - inverse: null, - getInverse: _createInverseContext, - clone: _cloneActiveContext - }; + lib$es6$promise$$internal$$publish(promise); + } - /** - * Generates an inverse context for use in the compaction algorithm, if - * not already generated for the given active context. - * - * @return the inverse context. - */ - function _createInverseContext() { - var activeCtx = this; + function lib$es6$promise$$internal$$fulfill(promise, value) { + if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; } - // lazily create inverse - if(activeCtx.inverse) { - return activeCtx.inverse; + promise._result = value; + promise._state = lib$es6$promise$$internal$$FULFILLED; + + if (promise._subscribers.length !== 0) { + lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, promise); + } } - var inverse = activeCtx.inverse = {}; - // variables for building fast CURIE map - var fastCurieMap = activeCtx.fastCurieMap = {}; - var irisToTerms = {}; + function lib$es6$promise$$internal$$reject(promise, reason) { + if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; } + promise._state = lib$es6$promise$$internal$$REJECTED; + promise._result = reason; - // handle default language - var defaultLanguage = activeCtx['@language'] || '@none'; + lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publishRejection, promise); + } - // create term selections for each mapping in the context, ordered by - // shortest and then lexicographically least - var mappings = activeCtx.mappings; - var terms = Object.keys(mappings).sort(_compareShortestLeast); - for(var i = 0; i < terms.length; ++i) { - var term = terms[i]; - var mapping = mappings[term]; - if(mapping === null) { - continue; - } + function lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection) { + var subscribers = parent._subscribers; + var length = subscribers.length; - var container = mapping['@container'] || '@none'; + parent._onerror = null; - // iterate over every IRI in the mapping - var ids = mapping['@id']; - if(!_isArray(ids)) { - ids = [ids]; + subscribers[length] = child; + subscribers[length + lib$es6$promise$$internal$$FULFILLED] = onFulfillment; + subscribers[length + lib$es6$promise$$internal$$REJECTED] = onRejection; + + if (length === 0 && parent._state) { + lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, parent); } - for(var ii = 0; ii < ids.length; ++ii) { - var iri = ids[ii]; - var entry = inverse[iri]; - var isKeyword = _isKeyword(iri); + } - if(!entry) { - // initialize entry - inverse[iri] = entry = {}; + function lib$es6$promise$$internal$$publish(promise) { + var subscribers = promise._subscribers; + var settled = promise._state; - if(!isKeyword && !mapping._termHasColon) { - // init IRI to term map and fast CURIE prefixes - irisToTerms[iri] = [term]; - var fastCurieEntry = {iri: iri, terms: irisToTerms[iri]}; - if(iri[0] in fastCurieMap) { - fastCurieMap[iri[0]].push(fastCurieEntry); - } else { - fastCurieMap[iri[0]] = [fastCurieEntry]; - } - } - } else if(!isKeyword && !mapping._termHasColon) { - // add IRI to term match - irisToTerms[iri].push(term); - } + if (subscribers.length === 0) { return; } - // add new entry - if(!entry[container]) { - entry[container] = { - '@language': {}, - '@type': {} - }; - } - entry = entry[container]; + var child, callback, detail = promise._result; - if(mapping.reverse) { - // term is preferred for values using @reverse - _addPreferredTerm(mapping, term, entry['@type'], '@reverse'); - } else if('@type' in mapping) { - // term is preferred for values using specific type - _addPreferredTerm(mapping, term, entry['@type'], mapping['@type']); - } else if('@language' in mapping) { - // term is preferred for values using specific language - var language = mapping['@language'] || '@null'; - _addPreferredTerm(mapping, term, entry['@language'], language); - } else { - // term is preferred for values w/default language or no type and - // no language - // add an entry for the default language - _addPreferredTerm(mapping, term, entry['@language'], defaultLanguage); + for (var i = 0; i < subscribers.length; i += 3) { + child = subscribers[i]; + callback = subscribers[i + settled]; - // add entries for no type and no language - _addPreferredTerm(mapping, term, entry['@type'], '@none'); - _addPreferredTerm(mapping, term, entry['@language'], '@none'); + if (child) { + lib$es6$promise$$internal$$invokeCallback(settled, child, callback, detail); + } else { + callback(detail); } } - } - // build fast CURIE map - for(var key in fastCurieMap) { - _buildIriMap(fastCurieMap, key, 1); + promise._subscribers.length = 0; } - return inverse; - } + function lib$es6$promise$$internal$$ErrorObject() { + this.error = null; + } - /** - * Runs a recursive algorithm to build a lookup map for quickly finding - * potential CURIEs. - * - * @param iriMap the map to build. - * @param key the current key in the map to work on. - * @param idx the index into the IRI to compare. - */ - function _buildIriMap(iriMap, key, idx) { - var entries = iriMap[key]; - var next = iriMap[key] = {}; + var lib$es6$promise$$internal$$TRY_CATCH_ERROR = new lib$es6$promise$$internal$$ErrorObject(); - var iri; - var letter; - for(var i = 0; i < entries.length; ++i) { - iri = entries[i].iri; - if(idx >= iri.length) { - letter = ''; - } else { - letter = iri[idx]; - } - if(letter in next) { - next[letter].push(entries[i]); - } else { - next[letter] = [entries[i]]; + function lib$es6$promise$$internal$$tryCatch(callback, detail) { + try { + return callback(detail); + } catch(e) { + lib$es6$promise$$internal$$TRY_CATCH_ERROR.error = e; + return lib$es6$promise$$internal$$TRY_CATCH_ERROR; } } - for(var key in next) { - if(key === '') { - continue; + function lib$es6$promise$$internal$$invokeCallback(settled, promise, callback, detail) { + var hasCallback = lib$es6$promise$utils$$isFunction(callback), + value, error, succeeded, failed; + + if (hasCallback) { + value = lib$es6$promise$$internal$$tryCatch(callback, detail); + + if (value === lib$es6$promise$$internal$$TRY_CATCH_ERROR) { + failed = true; + error = value.error; + value = null; + } else { + succeeded = true; + } + + if (promise === value) { + lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$cannotReturnOwn()); + return; + } + + } else { + value = detail; + succeeded = true; } - _buildIriMap(next, key, idx + 1); - } - } - /** - * Adds the term for the given entry if not already added. - * - * @param mapping the term mapping. - * @param term the term to add. - * @param entry the inverse context typeOrLanguage entry to add to. - * @param typeOrLanguageValue the key in the entry to add to. - */ - function _addPreferredTerm(mapping, term, entry, typeOrLanguageValue) { - if(!(typeOrLanguageValue in entry)) { - entry[typeOrLanguageValue] = term; + if (promise._state !== lib$es6$promise$$internal$$PENDING) { + // noop + } else if (hasCallback && succeeded) { + lib$es6$promise$$internal$$resolve(promise, value); + } else if (failed) { + lib$es6$promise$$internal$$reject(promise, error); + } else if (settled === lib$es6$promise$$internal$$FULFILLED) { + lib$es6$promise$$internal$$fulfill(promise, value); + } else if (settled === lib$es6$promise$$internal$$REJECTED) { + lib$es6$promise$$internal$$reject(promise, value); + } } - } - /** - * Clones an active context, creating a child active context. - * - * @return a clone (child) of the active context. - */ - function _cloneActiveContext() { - var child = {}; - child['@base'] = this['@base']; - child.mappings = _clone(this.mappings); - child.clone = this.clone; - child.inverse = null; - child.getInverse = this.getInverse; - if('@language' in this) { - child['@language'] = this['@language']; - } - if('@vocab' in this) { - child['@vocab'] = this['@vocab']; + function lib$es6$promise$$internal$$initializePromise(promise, resolver) { + try { + resolver(function resolvePromise(value){ + lib$es6$promise$$internal$$resolve(promise, value); + }, function rejectPromise(reason) { + lib$es6$promise$$internal$$reject(promise, reason); + }); + } catch(e) { + lib$es6$promise$$internal$$reject(promise, e); + } } - return child; - } -} - -/** - * Returns whether or not the given value is a keyword. - * - * @param v the value to check. - * - * @return true if the value is a keyword, false if not. - */ -function _isKeyword(v) { - if(!_isString(v)) { - return false; - } - switch(v) { - case '@base': - case '@context': - case '@container': - case '@default': - case '@embed': - case '@explicit': - case '@graph': - case '@id': - case '@index': - case '@language': - case '@list': - case '@omitDefault': - case '@preserve': - case '@requireAll': - case '@reverse': - case '@set': - case '@type': - case '@value': - case '@vocab': - return true; - } - return false; -} -/** - * Returns true if the given value is an Object. - * - * @param v the value to check. - * - * @return true if the value is an Object, false if not. - */ -function _isObject(v) { - return (Object.prototype.toString.call(v) === '[object Object]'); -} + function lib$es6$promise$enumerator$$Enumerator(Constructor, input) { + var enumerator = this; -/** - * Returns true if the given value is an empty Object. - * - * @param v the value to check. - * - * @return true if the value is an empty Object, false if not. - */ -function _isEmptyObject(v) { - return _isObject(v) && Object.keys(v).length === 0; -} + enumerator._instanceConstructor = Constructor; + enumerator.promise = new Constructor(lib$es6$promise$$internal$$noop); -/** - * Returns true if the given value is an Array. - * - * @param v the value to check. - * - * @return true if the value is an Array, false if not. - */ -function _isArray(v) { - return Array.isArray(v); -} + if (enumerator._validateInput(input)) { + enumerator._input = input; + enumerator.length = input.length; + enumerator._remaining = input.length; -/** - * Throws an exception if the given value is not a valid @type value. - * - * @param v the value to check. - */ -function _validateTypeValue(v) { - // can be a string or an empty object - if(_isString(v) || _isEmptyObject(v)) { - return; - } + enumerator._init(); - // must be an array - var isValid = false; - if(_isArray(v)) { - // must contain only strings - isValid = true; - for(var i = 0; i < v.length; ++i) { - if(!(_isString(v[i]))) { - isValid = false; - break; + if (enumerator.length === 0) { + lib$es6$promise$$internal$$fulfill(enumerator.promise, enumerator._result); + } else { + enumerator.length = enumerator.length || 0; + enumerator._enumerate(); + if (enumerator._remaining === 0) { + lib$es6$promise$$internal$$fulfill(enumerator.promise, enumerator._result); + } + } + } else { + lib$es6$promise$$internal$$reject(enumerator.promise, enumerator._validationError()); } } - } - if(!isValid) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; "@type" value must a string, an array of ' + - 'strings, or an empty object.', 'jsonld.SyntaxError', - {code: 'invalid type value', value: v}); - } -} + lib$es6$promise$enumerator$$Enumerator.prototype._validateInput = function(input) { + return lib$es6$promise$utils$$isArray(input); + }; -/** - * Returns true if the given value is a String. - * - * @param v the value to check. - * - * @return true if the value is a String, false if not. - */ -function _isString(v) { - return (typeof v === 'string' || - Object.prototype.toString.call(v) === '[object String]'); -} + lib$es6$promise$enumerator$$Enumerator.prototype._validationError = function() { + return new Error('Array Methods must be provided an Array'); + }; -/** - * Returns true if the given value is a Number. - * - * @param v the value to check. - * - * @return true if the value is a Number, false if not. - */ -function _isNumber(v) { - return (typeof v === 'number' || - Object.prototype.toString.call(v) === '[object Number]'); -} + lib$es6$promise$enumerator$$Enumerator.prototype._init = function() { + this._result = new Array(this.length); + }; -/** - * Returns true if the given value is a double. - * - * @param v the value to check. - * - * @return true if the value is a double, false if not. - */ -function _isDouble(v) { - return _isNumber(v) && String(v).indexOf('.') !== -1; -} + var lib$es6$promise$enumerator$$default = lib$es6$promise$enumerator$$Enumerator; -/** - * Returns true if the given value is numeric. - * - * @param v the value to check. - * - * @return true if the value is numeric, false if not. - */ -function _isNumeric(v) { - return !isNaN(parseFloat(v)) && isFinite(v); -} + lib$es6$promise$enumerator$$Enumerator.prototype._enumerate = function() { + var enumerator = this; -/** - * Returns true if the given value is a Boolean. - * - * @param v the value to check. - * - * @return true if the value is a Boolean, false if not. - */ -function _isBoolean(v) { - return (typeof v === 'boolean' || - Object.prototype.toString.call(v) === '[object Boolean]'); -} + var length = enumerator.length; + var promise = enumerator.promise; + var input = enumerator._input; -/** - * Returns true if the given value is undefined. - * - * @param v the value to check. - * - * @return true if the value is undefined, false if not. - */ -function _isUndefined(v) { - return (typeof v === 'undefined'); -} + for (var i = 0; promise._state === lib$es6$promise$$internal$$PENDING && i < length; i++) { + enumerator._eachEntry(input[i], i); + } + }; -/** - * Returns true if the given value is a subject with properties. - * - * @param v the value to check. - * - * @return true if the value is a subject with properties, false if not. - */ -function _isSubject(v) { - // Note: A value is a subject if all of these hold true: - // 1. It is an Object. - // 2. It is not a @value, @set, or @list. - // 3. It has more than 1 key OR any existing key is not @id. - var rval = false; - if(_isObject(v) && - !(('@value' in v) || ('@set' in v) || ('@list' in v))) { - var keyCount = Object.keys(v).length; - rval = (keyCount > 1 || !('@id' in v)); - } - return rval; -} + lib$es6$promise$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) { + var enumerator = this; + var c = enumerator._instanceConstructor; -/** - * Returns true if the given value is a subject reference. - * - * @param v the value to check. - * - * @return true if the value is a subject reference, false if not. - */ -function _isSubjectReference(v) { - // Note: A value is a subject reference if all of these hold true: - // 1. It is an Object. - // 2. It has a single key: @id. - return (_isObject(v) && Object.keys(v).length === 1 && ('@id' in v)); -} + if (lib$es6$promise$utils$$isMaybeThenable(entry)) { + if (entry.constructor === c && entry._state !== lib$es6$promise$$internal$$PENDING) { + entry._onerror = null; + enumerator._settledAt(entry._state, i, entry._result); + } else { + enumerator._willSettleAt(c.resolve(entry), i); + } + } else { + enumerator._remaining--; + enumerator._result[i] = entry; + } + }; -/** - * Returns true if the given value is a @value. - * - * @param v the value to check. - * - * @return true if the value is a @value, false if not. - */ -function _isValue(v) { - // Note: A value is a @value if all of these hold true: - // 1. It is an Object. - // 2. It has the @value property. - return _isObject(v) && ('@value' in v); -} + lib$es6$promise$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) { + var enumerator = this; + var promise = enumerator.promise; -/** - * Returns true if the given value is a @list. - * - * @param v the value to check. - * - * @return true if the value is a @list, false if not. - */ -function _isList(v) { - // Note: A value is a @list if all of these hold true: - // 1. It is an Object. - // 2. It has the @list property. - return _isObject(v) && ('@list' in v); -} + if (promise._state === lib$es6$promise$$internal$$PENDING) { + enumerator._remaining--; -/** - * Returns true if the given value is a blank node. - * - * @param v the value to check. - * - * @return true if the value is a blank node, false if not. - */ -function _isBlankNode(v) { - // Note: A value is a blank node if all of these hold true: - // 1. It is an Object. - // 2. If it has an @id key its value begins with '_:'. - // 3. It has no keys OR is not a @value, @set, or @list. - var rval = false; - if(_isObject(v)) { - if('@id' in v) { - rval = (v['@id'].indexOf('_:') === 0); - } else { - rval = (Object.keys(v).length === 0 || - !(('@value' in v) || ('@set' in v) || ('@list' in v))); + if (state === lib$es6$promise$$internal$$REJECTED) { + lib$es6$promise$$internal$$reject(promise, value); + } else { + enumerator._result[i] = value; + } + } + + if (enumerator._remaining === 0) { + lib$es6$promise$$internal$$fulfill(promise, enumerator._result); + } + }; + + lib$es6$promise$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) { + var enumerator = this; + + lib$es6$promise$$internal$$subscribe(promise, undefined, function(value) { + enumerator._settledAt(lib$es6$promise$$internal$$FULFILLED, i, value); + }, function(reason) { + enumerator._settledAt(lib$es6$promise$$internal$$REJECTED, i, reason); + }); + }; + function lib$es6$promise$promise$all$$all(entries) { + return new lib$es6$promise$enumerator$$default(this, entries).promise; } - } - return rval; -} + var lib$es6$promise$promise$all$$default = lib$es6$promise$promise$all$$all; + function lib$es6$promise$promise$race$$race(entries) { + /*jshint validthis:true */ + var Constructor = this; -/** - * Returns true if the given value is an absolute IRI, false if not. - * - * @param v the value to check. - * - * @return true if the value is an absolute IRI, false if not. - */ -function _isAbsoluteIri(v) { - return _isString(v) && v.indexOf(':') !== -1; -} + var promise = new Constructor(lib$es6$promise$$internal$$noop); -/** - * 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. - * - * @return the cloned value. - */ -function _clone(value) { - if(value && typeof value === 'object') { - var rval; - if(_isArray(value)) { - rval = []; - for(var i = 0; i < value.length; ++i) { - rval[i] = _clone(value[i]); - } - } else if(_isObject(value)) { - rval = {}; - for(var key in value) { - rval[key] = _clone(value[key]); + if (!lib$es6$promise$utils$$isArray(entries)) { + lib$es6$promise$$internal$$reject(promise, new TypeError('You must pass an array to race.')); + return promise; } - } else { - rval = value.toString(); - } - return rval; - } - return value; -} -/** - * Finds all @context URLs in the given JSON-LD input. - * - * @param input the JSON-LD input. - * @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 retrieve were found, false if not. - */ -function _findContextUrls(input, urls, replace, base) { - var count = Object.keys(urls).length; - if(_isArray(input)) { - for(var i = 0; i < input.length; ++i) { - _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, base); - continue; + var length = entries.length; + + function onFulfillment(value) { + lib$es6$promise$$internal$$resolve(promise, value); } - // get @context - var ctx = input[key]; + function onRejection(reason) { + lib$es6$promise$$internal$$reject(promise, reason); + } - // array @context - if(_isArray(ctx)) { - var length = ctx.length; - for(var i = 0; i < length; ++i) { - var _ctx = ctx[i]; - if(_isString(_ctx)) { - _ctx = jsonld.prependBase(base, _ctx); - // replace w/@context if requested - if(replace) { - _ctx = urls[_ctx]; - if(_isArray(_ctx)) { - // add flattened context - Array.prototype.splice.apply(ctx, [i, 1].concat(_ctx)); - i += _ctx.length - 1; - length = ctx.length; - } else { - ctx[i] = _ctx; - } - } else if(!(_ctx in urls)) { - // @context URL found - urls[_ctx] = false; - } - } - } - } else if(_isString(ctx)) { - // string @context - ctx = jsonld.prependBase(base, ctx); - // replace w/@context if requested - if(replace) { - input[key] = urls[ctx]; - } else if(!(ctx in urls)) { - // @context URL found - urls[ctx] = false; - } + for (var i = 0; promise._state === lib$es6$promise$$internal$$PENDING && i < length; i++) { + lib$es6$promise$$internal$$subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection); } + + return promise; } - return (count < Object.keys(urls).length); - } - return false; -} + var lib$es6$promise$promise$race$$default = lib$es6$promise$promise$race$$race; + function lib$es6$promise$promise$resolve$$resolve(object) { + /*jshint validthis:true */ + var Constructor = this; -/** - * Retrieves external @context URLs using the given document loader. Every - * instance of @context in the input that refers to a URL will be replaced - * with the JSON @context found at that URL. - * - * @param input the JSON-LD input with possible contexts. - * @param options the options to use: - * documentLoader(url, callback(err, remoteDoc)) the document loader. - * @param callback(err, input) called once the operation completes. - */ -function _retrieveContextUrls(input, options, callback) { - // if any error occurs during URL resolution, quit - var error = null; + if (object && typeof object === 'object' && object.constructor === Constructor) { + return object; + } - // recursive document loader - var documentLoader = options.documentLoader; - var retrieve = function(input, cycles, documentLoader, base, callback) { - if(Object.keys(cycles).length > MAX_CONTEXT_URLS) { - error = new JsonLdError( - 'Maximum number of @context URLs exceeded.', - 'jsonld.ContextUrlError', - {code: 'loading remote context failed', max: MAX_CONTEXT_URLS}); - return callback(error); + var promise = new Constructor(lib$es6$promise$$internal$$noop); + lib$es6$promise$$internal$$resolve(promise, object); + return promise; } + var lib$es6$promise$promise$resolve$$default = lib$es6$promise$promise$resolve$$resolve; + function lib$es6$promise$promise$reject$$reject(reason) { + /*jshint validthis:true */ + var Constructor = this; + var promise = new Constructor(lib$es6$promise$$internal$$noop); + lib$es6$promise$$internal$$reject(promise, reason); + return promise; + } + var lib$es6$promise$promise$reject$$default = lib$es6$promise$promise$reject$$reject; - // for tracking the URLs to retrieve - var urls = {}; - - // finished will be called once the URL queue is empty - var finished = function() { - // replace all URLs in the input - _findContextUrls(input, urls, true, base); - callback(null, input); - }; + var lib$es6$promise$promise$$counter = 0; - // find all URLs in the given input - if(!_findContextUrls(input, urls, false, base)) { - // no new URLs in input - return finished(); + function lib$es6$promise$promise$$needsResolver() { + throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); } - // queue all unretrieved URLs - var queue = []; - for(var url in urls) { - if(urls[url] === false) { - queue.push(url); - } + function lib$es6$promise$promise$$needsNew() { + throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); } - // retrieve URLs in queue - var count = queue.length; - for(var i = 0; i < queue.length; ++i) { - (function(url) { - // check for context URL cycle - if(url in cycles) { - error = new JsonLdError( - 'Cyclical @context URLs detected.', - 'jsonld.ContextUrlError', - {code: 'recursive context inclusion', url: url}); - return callback(error); - } - var _cycles = _clone(cycles); - _cycles[url] = true; - var done = function(err, remoteDoc) { - // short-circuit if there was an error with another URL - if(error) { - return; - } + var lib$es6$promise$promise$$default = lib$es6$promise$promise$$Promise; + /** + Promise objects represent the eventual result of an asynchronous operation. The + primary way of interacting with a promise is through its `then` method, which + registers callbacks to receive either a promise's eventual value or the reason + why the promise cannot be fulfilled. - var ctx = remoteDoc ? remoteDoc.document : null; + Terminology + ----------- - // parse string context as JSON - if(!err && _isString(ctx)) { - try { - ctx = JSON.parse(ctx); - } catch(ex) { - err = ex; - } - } + - `promise` is an object or function with a `then` method whose behavior conforms to this specification. + - `thenable` is an object or function that defines a `then` method. + - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). + - `exception` is a value that is thrown using the throw statement. + - `reason` is a value that indicates why a promise was rejected. + - `settled` the final resting state of a promise, fulfilled or rejected. - // ensure ctx is an object - if(err) { - err = new JsonLdError( - 'Dereferencing 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), too many redirects, a ' + - 'non-JSON response, or more than one HTTP Link Header was ' + - 'provided for a remote context.', - 'jsonld.InvalidUrl', - {code: 'loading remote context failed', url: url, cause: err}); - } else if(!_isObject(ctx)) { - err = new JsonLdError( - 'Dereferencing a URL did not result in a JSON object. The ' + - 'response was valid JSON, but it was not a JSON object.', - 'jsonld.InvalidUrl', - {code: 'invalid remote context', url: url, cause: err}); - } - if(err) { - error = err; - return callback(error); - } + A promise can be in one of three states: pending, fulfilled, or rejected. - // use empty context if no @context key is present - if(!('@context' in ctx)) { - ctx = {'@context': {}}; - } else { - ctx = {'@context': ctx['@context']}; - } + Promises that are fulfilled have a fulfillment value and are in the fulfilled + state. Promises that are rejected have a rejection reason and are in the + rejected state. A fulfillment value is never a thenable. - // append context URL to context if given - if(remoteDoc.contextUrl) { - if(!_isArray(ctx['@context'])) { - ctx['@context'] = [ctx['@context']]; - } - ctx['@context'].push(remoteDoc.contextUrl); - } + Promises can also be said to *resolve* a value. If this value is also a + promise, then the original promise's settled state will match the value's + settled state. So a promise that *resolves* a promise that rejects will + itself reject, and a promise that *resolves* a promise that fulfills will + itself fulfill. - // recurse - retrieve(ctx, _cycles, documentLoader, url, function(err, ctx) { - if(err) { - return callback(err); - } - urls[url] = ctx['@context']; - count -= 1; - if(count === 0) { - finished(); - } - }); - }; - var promise = documentLoader(url, done); - if(promise && 'then' in promise) { - promise.then(done.bind(null, null), done); - } - }(queue[i])); - } - }; - retrieve(input, {}, documentLoader, options.base, callback); -} -// define js 1.8.5 Object.keys method if not 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; - }; -} + Basic Usage: + ------------ -/** - * Parses RDF in the form of N-Quads. - * - * @param input the N-Quads input to parse. - * - * @return an RDF dataset. - */ -function _parseNQuads(input) { - // define partial regexes - var iri = '(?:<([^:]+:[^>]*)>)'; - var bnode = '(_:(?:[A-Za-z0-9]+))'; - var plain = '"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"'; - var datatype = '(?:\\^\\^' + iri + ')'; - var language = '(?:@([a-z]+(?:-[a-z0-9]+)*))'; - var literal = '(?:' + plain + '(?:' + datatype + '|' + language + ')?)'; - var comment = '(?:#.*)?'; - var ws = '[ \\t]+'; - var wso = '[ \\t]*'; - var eoln = /(?:\r\n)|(?:\n)|(?:\r)/g; - var empty = new RegExp('^' + wso + comment + '$'); + ```js + var promise = new Promise(function(resolve, reject) { + // on success + resolve(value); - // define quad part regexes - var subject = '(?:' + iri + '|' + bnode + ')' + ws; - var property = iri + ws; - var object = '(?:' + iri + '|' + bnode + '|' + literal + ')' + wso; - var graphName = '(?:\\.|(?:(?:' + iri + '|' + bnode + ')' + wso + '\\.))'; + // on failure + reject(reason); + }); - // full quad regex - var quad = new RegExp( - '^' + wso + subject + property + object + graphName + wso + comment + '$'); + promise.then(function(value) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` - // build RDF dataset - var dataset = {}; + Advanced Usage: + --------------- - // split N-Quad input into lines - var lines = input.split(eoln); - var lineNumber = 0; - for(var li = 0; li < lines.length; ++li) { - var line = lines[li]; - lineNumber++; + Promises shine when abstracting away asynchronous interactions such as + `XMLHttpRequest`s. - // skip empty lines - if(empty.test(line)) { - continue; - } + ```js + function getJSON(url) { + return new Promise(function(resolve, reject){ + var xhr = new XMLHttpRequest(); - // parse quad - var match = line.match(quad); - if(match === null) { - throw new JsonLdError( - 'Error while parsing N-Quads; invalid quad.', - 'jsonld.ParseError', {line: lineNumber}); - } + xhr.open('GET', url); + xhr.onreadystatechange = handler; + xhr.responseType = 'json'; + xhr.setRequestHeader('Accept', 'application/json'); + xhr.send(); - // create RDF triple - var triple = {}; + function handler() { + if (this.readyState === this.DONE) { + if (this.status === 200) { + resolve(this.response); + } else { + reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); + } + } + }; + }); + } - // get subject - if(!_isUndefined(match[1])) { - triple.subject = {type: 'IRI', value: match[1]}; - } else { - triple.subject = {type: 'blank node', value: match[2]}; - } + getJSON('/posts.json').then(function(json) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` - // get predicate - triple.predicate = {type: 'IRI', value: match[3]}; + Unlike callbacks, promises are great composable primitives. - // get object - if(!_isUndefined(match[4])) { - triple.object = {type: 'IRI', value: match[4]}; - } else if(!_isUndefined(match[5])) { - triple.object = {type: 'blank node', value: match[5]}; - } else { - triple.object = {type: 'literal'}; - if(!_isUndefined(match[7])) { - triple.object.datatype = match[7]; - } else if(!_isUndefined(match[8])) { - triple.object.datatype = RDF_LANGSTRING; - triple.object.language = match[8]; - } else { - triple.object.datatype = XSD_STRING; - } - var unescaped = match[6] - .replace(/\\"/g, '"') - .replace(/\\t/g, '\t') - .replace(/\\n/g, '\n') - .replace(/\\r/g, '\r') - .replace(/\\\\/g, '\\'); - triple.object.value = unescaped; - } + ```js + Promise.all([ + getJSON('/posts'), + getJSON('/comments') + ]).then(function(values){ + values[0] // => postsJSON + values[1] // => commentsJSON - // get graph name ('@default' is used for the default graph) - var name = '@default'; - if(!_isUndefined(match[9])) { - name = match[9]; - } else if(!_isUndefined(match[10])) { - name = match[10]; - } + return values; + }); + ``` - // initialize graph in dataset - if(!(name in dataset)) { - dataset[name] = [triple]; - } else { - // add triple if unique to its graph - var unique = true; - var triples = dataset[name]; - for(var ti = 0; unique && ti < triples.length; ++ti) { - if(_compareRDFTriples(triples[ti], triple)) { - unique = false; + @class Promise + @param {function} resolver + Useful for tooling. + @constructor + */ + function lib$es6$promise$promise$$Promise(resolver) { + this._id = lib$es6$promise$promise$$counter++; + this._state = undefined; + this._result = undefined; + this._subscribers = []; + + if (lib$es6$promise$$internal$$noop !== resolver) { + if (!lib$es6$promise$utils$$isFunction(resolver)) { + lib$es6$promise$promise$$needsResolver(); } - } - if(unique) { - triples.push(triple); + + if (!(this instanceof lib$es6$promise$promise$$Promise)) { + lib$es6$promise$promise$$needsNew(); + } + + lib$es6$promise$$internal$$initializePromise(this, resolver); } } - } - return dataset; -} + lib$es6$promise$promise$$Promise.all = lib$es6$promise$promise$all$$default; + lib$es6$promise$promise$$Promise.race = lib$es6$promise$promise$race$$default; + lib$es6$promise$promise$$Promise.resolve = lib$es6$promise$promise$resolve$$default; + lib$es6$promise$promise$$Promise.reject = lib$es6$promise$promise$reject$$default; + lib$es6$promise$promise$$Promise._setScheduler = lib$es6$promise$asap$$setScheduler; + lib$es6$promise$promise$$Promise._setAsap = lib$es6$promise$asap$$setAsap; + lib$es6$promise$promise$$Promise._asap = lib$es6$promise$asap$$asap; -// register the N-Quads RDF parser -jsonld.registerRDFParser('application/nquads', _parseNQuads); + lib$es6$promise$promise$$Promise.prototype = { + constructor: lib$es6$promise$promise$$Promise, -/** - * Converts an RDF dataset to N-Quads. - * - * @param dataset the RDF dataset to convert. - * - * @return the N-Quads string. - */ -function _toNQuads(dataset) { - var quads = []; - for(var graphName in dataset) { - var triples = dataset[graphName]; - for(var ti = 0; ti < triples.length; ++ti) { - var triple = triples[ti]; - if(graphName === '@default') { - graphName = null; - } - quads.push(_toNQuad(triple, graphName)); - } - } - return quads.sort().join(''); -} + /** + The primary way of interacting with a promise is through its `then` method, + which registers callbacks to receive either a promise's eventual value or the + reason why the promise cannot be fulfilled. -/** - * Converts an RDF triple and graph name to an N-Quad string (a single quad). - * - * @param triple the RDF triple or quad to convert (a triple or quad may be - * passed, if a triple is passed then `graphName` should be given - * to specify the name of the graph the triple is in, `null` for - * the default graph). - * @param graphName the name of the graph containing the triple, null for - * the default graph. - * - * @return the N-Quad string. - */ -function _toNQuad(triple, graphName) { - var s = triple.subject; - var p = triple.predicate; - var o = triple.object; - var g = graphName || null; - if('name' in triple && triple.name) { - g = triple.name.value; - } + ```js + findUser().then(function(user){ + // user is available + }, function(reason){ + // user is unavailable, and you are given the reason why + }); + ``` - var quad = ''; + Chaining + -------- - // subject is an IRI - if(s.type === 'IRI') { - quad += '<' + s.value + '>'; - } else { - quad += s.value; - } - quad += ' '; + The return value of `then` is itself a promise. This second, 'downstream' + promise is resolved with the return value of the first promise's fulfillment + or rejection handler, or rejected if the handler throws an exception. - // predicate is an IRI - if(p.type === 'IRI') { - quad += '<' + p.value + '>'; - } else { - quad += p.value; - } - quad += ' '; + ```js + findUser().then(function (user) { + return user.name; + }, function (reason) { + return 'default name'; + }).then(function (userName) { + // If `findUser` fulfilled, `userName` will be the user's name, otherwise it + // will be `'default name'` + }); - // object is IRI, bnode, or literal - if(o.type === 'IRI') { - quad += '<' + o.value + '>'; - } else if(o.type === 'blank node') { - quad += o.value; - } else { - var escaped = o.value - .replace(/\\/g, '\\\\') - .replace(/\t/g, '\\t') - .replace(/\n/g, '\\n') - .replace(/\r/g, '\\r') - .replace(/\"/g, '\\"'); - quad += '"' + escaped + '"'; - if(o.datatype === RDF_LANGSTRING) { - if(o.language) { - quad += '@' + o.language; - } - } else if(o.datatype !== XSD_STRING) { - quad += '^^<' + o.datatype + '>'; - } - } + findUser().then(function (user) { + throw new Error('Found user, but still unhappy'); + }, function (reason) { + throw new Error('`findUser` rejected and we're unhappy'); + }).then(function (value) { + // never reached + }, function (reason) { + // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. + // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. + }); + ``` + If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. - // graph - if(g !== null && g !== undefined) { - if(g.indexOf('_:') !== 0) { - quad += ' <' + g + '>'; - } else { - quad += ' ' + g; - } - } + ```js + findUser().then(function (user) { + throw new PedagogicalException('Upstream error'); + }).then(function (value) { + // never reached + }).then(function (value) { + // never reached + }, function (reason) { + // The `PedgagocialException` is propagated all the way down to here + }); + ``` - quad += ' .\n'; - return quad; -} + Assimilation + ------------ -/** - * Parses the RDF dataset found via the data object from the RDFa API. - * - * @param data the RDFa API data object. - * - * @return the RDF dataset. - */ -function _parseRdfaApiData(data) { - var dataset = {}; - dataset['@default'] = []; + Sometimes the value you want to propagate to a downstream promise can only be + retrieved asynchronously. This can be achieved by returning a promise in the + fulfillment or rejection handler. The downstream promise will then be pending + until the returned promise is settled. This is called *assimilation*. - var subjects = data.getSubjects(); - for(var si = 0; si < subjects.length; ++si) { - var subject = subjects[si]; - if(subject === null) { - continue; - } + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // The user's comments are now available + }); + ``` - // get all related triples - var triples = data.getSubjectTriples(subject); - if(triples === null) { - continue; - } - var predicates = triples.predicates; - for(var predicate in predicates) { - // iterate over objects - var objects = predicates[predicate].objects; - for(var oi = 0; oi < objects.length; ++oi) { - var object = objects[oi]; + If the assimliated promise rejects, then the downstream promise will also reject. - // create RDF triple - var triple = {}; + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // If `findCommentsByAuthor` fulfills, we'll have the value here + }, function (reason) { + // If `findCommentsByAuthor` rejects, we'll have the reason here + }); + ``` - // add subject - if(subject.indexOf('_:') === 0) { - triple.subject = {type: 'blank node', value: subject}; - } else { - triple.subject = {type: 'IRI', value: subject}; - } + Simple Example + -------------- - // add predicate - if(predicate.indexOf('_:') === 0) { - triple.predicate = {type: 'blank node', value: predicate}; - } else { - triple.predicate = {type: 'IRI', value: predicate}; - } + Synchronous Example - // serialize XML literal - var value = object.value; - if(object.type === RDF_XML_LITERAL) { - // initialize XMLSerializer - if(!XMLSerializer) { - _defineXMLSerializer(); - } - var serializer = new XMLSerializer(); - value = ''; - for(var x = 0; x < object.value.length; x++) { - if(object.value[x].nodeType === Node.ELEMENT_NODE) { - value += serializer.serializeToString(object.value[x]); - } else if(object.value[x].nodeType === Node.TEXT_NODE) { - value += object.value[x].nodeValue; - } - } - } + ```javascript + var result; - // add object - triple.object = {}; + try { + result = findResult(); + // success + } catch(reason) { + // failure + } + ``` - // object is an IRI - if(object.type === RDF_OBJECT) { - if(object.value.indexOf('_:') === 0) { - triple.object.type = 'blank node'; - } else { - triple.object.type = 'IRI'; - } + Errback Example + + ```js + findResult(function(result, err){ + if (err) { + // failure } else { - // object is a literal - triple.object.type = 'literal'; - if(object.type === RDF_PLAIN_LITERAL) { - if(object.language) { - triple.object.datatype = RDF_LANGSTRING; - triple.object.language = object.language; - } else { - triple.object.datatype = XSD_STRING; - } - } else { - triple.object.datatype = object.type; - } + // success } - triple.object.value = value; + }); + ``` - // add triple to dataset in default graph - dataset['@default'].push(triple); - } - } - } + Promise Example; - return dataset; -} + ```javascript + findResult().then(function(result){ + // success + }, function(reason){ + // failure + }); + ``` -// register the RDFa API RDF parser -jsonld.registerRDFParser('rdfa-api', _parseRdfaApiData); + Advanced Example + -------------- -/** - * Creates a new IdentifierIssuer. A IdentifierIssuer issues unique - * identifiers, keeping track of any previously issued identifiers. - * - * @param prefix the prefix to use (''). - */ -function IdentifierIssuer(prefix) { - this.prefix = prefix; - this.counter = 0; - this.existing = {}; -} -jsonld.IdentifierIssuer = IdentifierIssuer; -// backwards-compability -jsonld.UniqueNamer = IdentifierIssuer; + Synchronous Example -/** - * Copies this IdentifierIssuer. - * - * @return a copy of this IdentifierIssuer. - */ -IdentifierIssuer.prototype.clone = function() { - var copy = new IdentifierIssuer(this.prefix); - copy.counter = this.counter; - copy.existing = _clone(this.existing); - return copy; -}; + ```javascript + var author, books; -/** - * Gets the new identifier for the given old identifier, where if no old - * identifier is given a new identifier will be generated. - * - * @param [old] the old identifier to get the new identifier for. - * - * @return the new identifier. - */ -IdentifierIssuer.prototype.getId = function(old) { - // return existing old identifier - if(old && old in this.existing) { - return this.existing[old]; - } + try { + author = findAuthor(); + books = findBooksByAuthor(author); + // success + } catch(reason) { + // failure + } + ``` - // get next identifier - var identifier = this.prefix + this.counter; - this.counter += 1; + Errback Example - // save mapping - if(old) { - this.existing[old] = identifier; - } + ```js - return identifier; -}; -// alias -IdentifierIssuer.prototype.getName = IdentifierIssuer.prototype.getName; + function foundBooks(books) { -/** - * Returns true if the given old identifer has already been assigned a new - * identifier. - * - * @param old the old identifier to check. - * - * @return true if the old identifier has been assigned a new identifier, false - * if not. - */ -IdentifierIssuer.prototype.hasId = function(old) { - return (old in this.existing); -}; -// alias -IdentifierIssuer.prototype.isNamed = IdentifierIssuer.prototype.hasId; + } -/** - * A Permutator iterates over all possible permutations of the given array - * of elements. - * - * @param list the array of elements to iterate over. - */ -var Permutator = function(list) { - // original array - this.list = list.sort(); - // indicates whether there are more permutations - this.done = false; - // directional info for permutation algorithm - this.left = {}; - for(var i = 0; i < list.length; ++i) { - this.left[list[i]] = true; - } -}; - -/** - * Returns true if there is another permutation. - * - * @return true if there is another permutation, false if not. - */ -Permutator.prototype.hasNext = function() { - return !this.done; -}; + function failure(reason) { -/** - * Gets the next permutation. Call hasNext() to ensure there is another one - * first. - * - * @return the next permutation. - */ -Permutator.prototype.next = function() { - // copy current permutation - var rval = this.list.slice(); + } - /* Calculate the next permutation using the Steinhaus-Johnson-Trotter - permutation algorithm. */ + findAuthor(function(author, err){ + if (err) { + failure(err); + // failure + } else { + try { + findBoooksByAuthor(author, function(books, err) { + if (err) { + failure(err); + } else { + try { + foundBooks(books); + } catch(reason) { + failure(reason); + } + } + }); + } catch(error) { + failure(err); + } + // success + } + }); + ``` - // get largest mobile element k - // (mobile: element is greater than the one it is looking at) - var k = null; - var pos = 0; - var length = this.list.length; - for(var i = 0; i < length; ++i) { - var element = this.list[i]; - var left = this.left[element]; - if((k === null || element > k) && - ((left && i > 0 && element > this.list[i - 1]) || - (!left && i < (length - 1) && element > this.list[i + 1]))) { - k = element; - pos = i; - } - } + Promise Example; - // no more permutations - if(k === null) { - this.done = true; - } else { - // swap k and the element it is looking at - var swap = this.left[k] ? pos - 1 : pos + 1; - this.list[pos] = this.list[swap]; - this.list[swap] = k; + ```javascript + findAuthor(). + then(findBooksByAuthor). + then(function(books){ + // found books + }).catch(function(reason){ + // something went wrong + }); + ``` - // reverse the direction of all elements larger than k - for(var i = 0; i < length; ++i) { - if(this.list[i] > k) { - this.left[this.list[i]] = !this.left[this.list[i]]; - } - } - } + @method then + @param {Function} onFulfilled + @param {Function} onRejected + Useful for tooling. + @return {Promise} + */ + then: function(onFulfillment, onRejection) { + var parent = this; + var state = parent._state; - return rval; -}; + if (state === lib$es6$promise$$internal$$FULFILLED && !onFulfillment || state === lib$es6$promise$$internal$$REJECTED && !onRejection) { + return this; + } -//////////////////////// DEFINE NORMALIZATION HASH API //////////////////////// + var child = new this.constructor(lib$es6$promise$$internal$$noop); + var result = parent._result; -/** - * Creates a new NormalizeHash for use by the given normalization algorithm. - * - * @param algorithm the RDF Dataset Normalization algorithm to use: - * 'URDNA2015' or 'URGNA2012'. - */ -var NormalizeHash = function(algorithm) { - if(!(this instanceof NormalizeHash)) { - return new NormalizeHash(algorithm); - } - if(['URDNA2015', 'URGNA2012'].indexOf(algorithm) === -1) { - throw new Error( - 'Invalid RDF Dataset Normalization algorithm: ' + algorithm); - } - NormalizeHash._init.call(this, algorithm); -}; -NormalizeHash.hashNQuads = function(algorithm, nquads) { - var md = new NormalizeHash(algorithm); - for(var i = 0; i < nquads.length; ++i) { - md.update(nquads[i]); - } - return md.digest(); -}; + if (state) { + var callback = arguments[state - 1]; + lib$es6$promise$asap$$asap(function(){ + lib$es6$promise$$internal$$invokeCallback(state, child, callback, result); + }); + } else { + lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection); + } -// switch definition of NormalizeHash based on environment -(function(_nodejs) { + return child; + }, -if(_nodejs) { - // define NormalizeHash using native crypto lib - var crypto = _dereq_('crypto'); - NormalizeHash._init = function(algorithm) { - if(algorithm === 'URDNA2015') { - algorithm = 'sha256'; - } else { - // assume URGNA2012 - algorithm = 'sha1'; - } - this.md = crypto.createHash(algorithm); - }; - NormalizeHash.prototype.update = function(msg) { - return this.md.update(msg, 'utf8'); - }; - NormalizeHash.prototype.digest = function() { - return this.md.digest('hex'); - }; - return; -} + /** + `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same + as the catch block of a try/catch statement. -// define NormalizeHash using JavaScript -NormalizeHash._init = function(algorithm) { - if(algorithm === 'URDNA2015') { - algorithm = new sha256.Algorithm(); - } else { - // assume URGNA2012 - algorithm = new sha1.Algorithm(); - } - this.md = new MessageDigest(algorithm); -}; -NormalizeHash.prototype.update = function(msg) { - return this.md.update(msg); -}; -NormalizeHash.prototype.digest = function() { - return this.md.digest().toHex(); -}; + ```js + function findAuthor(){ + throw new Error('couldn't find that author'); + } -/////////////////////////// DEFINE MESSAGE DIGEST API ///////////////////////// + // synchronous + try { + findAuthor(); + } catch(reason) { + // something went wrong + } -/** - * Creates a new MessageDigest. - * - * @param algorithm the algorithm to use. - */ -var MessageDigest = function(algorithm) { - if(!(this instanceof MessageDigest)) { - return new MessageDigest(algorithm); - } + // async with promises + findAuthor().catch(function(reason){ + // something went wrong + }); + ``` - this._algorithm = algorithm; + @method catch + @param {Function} onRejection + Useful for tooling. + @return {Promise} + */ + 'catch': function(onRejection) { + return this.then(null, onRejection); + } + }; + function lib$es6$promise$polyfill$$polyfill() { + var local; - // create shared padding as needed - if(!MessageDigest._padding || - MessageDigest._padding.length < this._algorithm.blockSize) { - MessageDigest._padding = String.fromCharCode(128); - var c = String.fromCharCode(0x00); - var n = 64; - while(n > 0) { - if(n & 1) { - MessageDigest._padding += c; + if (typeof global !== 'undefined') { + local = global; + } else if (typeof self !== 'undefined') { + local = self; + } else { + try { + local = Function('return this')(); + } catch (e) { + throw new Error('polyfill failed because global object is unavailable in this environment'); + } } - n >>>= 1; - if(n > 0) { - c += c; + + var P = local.Promise; + + if (P && Object.prototype.toString.call(P.resolve()) === '[object Promise]' && !P.cast) { + return; } + + local.Promise = lib$es6$promise$promise$$default; } - } + var lib$es6$promise$polyfill$$default = lib$es6$promise$polyfill$$polyfill; - // start digest automatically for first time - this.start(); -}; + var lib$es6$promise$umd$$ES6Promise = { + 'Promise': lib$es6$promise$promise$$default, + 'polyfill': lib$es6$promise$polyfill$$default + }; -/** - * Starts the digest. - * - * @return this digest object. - */ -MessageDigest.prototype.start = function() { - // up to 56-bit message length for convenience - this.messageLength = 0; + /* global define:true module:true window: true */ + if (typeof define === 'function' && define['amd']) { + define(function() { return lib$es6$promise$umd$$ES6Promise; }); + } else if (typeof module !== 'undefined' && module['exports']) { + module['exports'] = lib$es6$promise$umd$$ES6Promise; + } else if (typeof this !== 'undefined') { + this['ES6Promise'] = lib$es6$promise$umd$$ES6Promise; + } - // full message length - this.fullMessageLength = []; - var int32s = this._algorithm.messageLengthSize / 4; - for(var i = 0; i < int32s; ++i) { - this.fullMessageLength.push(0); - } + lib$es6$promise$polyfill$$default(); +}).call(this); - // input buffer - this._input = new MessageDigest.ByteBuffer(); - // get starting state - this.state = this._algorithm.start(); +}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - return this; -}; +},{"_process":74}],40:[function(_dereq_,module,exports){ -/** - * Updates the digest with the given message input. The input must be - * a string of characters. - * - * @param msg the message input to update with (ByteBuffer or string). - * - * @return this digest object. - */ -MessageDigest.prototype.update = function(msg) { - // encode message as a UTF-8 encoded binary string - msg = new MessageDigest.ByteBuffer(unescape(encodeURIComponent(msg))); +var hasOwn = Object.prototype.hasOwnProperty; +var toString = Object.prototype.toString; - // update message length - this.messageLength += msg.length(); - var len = msg.length(); - len = [(len / 0x100000000) >>> 0, len >>> 0]; - for(var i = this.fullMessageLength.length - 1; i >= 0; --i) { - this.fullMessageLength[i] += len[1]; - len[1] = len[0] + ((this.fullMessageLength[i] / 0x100000000) >>> 0); - this.fullMessageLength[i] = this.fullMessageLength[i] >>> 0; - len[0] = ((len[1] / 0x100000000) >>> 0); - } +module.exports = function forEach (obj, fn, ctx) { + if (toString.call(fn) !== '[object Function]') { + throw new TypeError('iterator must be a function'); + } + var l = obj.length; + if (l === +l) { + for (var i = 0; i < l; i++) { + fn.call(ctx, obj[i], i, obj); + } + } else { + for (var k in obj) { + if (hasOwn.call(obj, k)) { + fn.call(ctx, obj[k], k, obj); + } + } + } +}; - // add bytes to input buffer - this._input.putBytes(msg.bytes()); - // digest blocks - while(this._input.length() >= this._algorithm.blockSize) { - this.state = this._algorithm.digest(this.state, this._input); - } +},{}],41:[function(_dereq_,module,exports){ +/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ +exports.read = function (buffer, offset, isLE, mLen, nBytes) { + var e, m + var eLen = (nBytes * 8) - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var nBits = -7 + var i = isLE ? (nBytes - 1) : 0 + var d = isLE ? -1 : 1 + var s = buffer[offset + i] - // compact input buffer every 2K or if empty - if(this._input.read > 2048 || this._input.length() === 0) { - this._input.compact(); - } + i += d - return this; -}; + e = s & ((1 << (-nBits)) - 1) + s >>= (-nBits) + nBits += eLen + for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {} -/** - * Produces the digest. - * - * @return a byte buffer containing the digest value. - */ -MessageDigest.prototype.digest = function() { - /* Note: Here we copy the remaining bytes in the input buffer and add the - appropriate padding. Then we do the final update on a copy of the state so - that if the user wants to get intermediate digests they can do so. */ + m = e & ((1 << (-nBits)) - 1) + e >>= (-nBits) + nBits += mLen + for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {} - /* Determine the number of bytes that must be added to the message to - ensure its length is appropriately congruent. In other words, the data to - be digested must be a multiple of `blockSize`. This data includes the - message, some padding, and the length of the message. Since the length of - the message will be encoded as `messageLengthSize` bytes, that means that - the last segment of the data must have `blockSize` - `messageLengthSize` - bytes of message and padding. Therefore, the length of the message plus the - padding must be congruent to X mod `blockSize` because - `blockSize` - `messageLengthSize` = X. + if (e === 0) { + e = 1 - eBias + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity) + } else { + m = m + Math.pow(2, mLen) + e = e - eBias + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen) +} - For example, SHA-1 is congruent to 448 mod 512 and SHA-512 is congruent to - 896 mod 1024. SHA-1 uses a `blockSize` of 64 bytes (512 bits) and a - `messageLengthSize` of 8 bytes (64 bits). SHA-512 uses a `blockSize` of - 128 bytes (1024 bits) and a `messageLengthSize` of 16 bytes (128 bits). +exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { + var e, m, c + var eLen = (nBytes * 8) - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) + var i = isLE ? 0 : (nBytes - 1) + var d = isLE ? 1 : -1 + var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 - In order to fill up the message length it must be filled with padding that - begins with 1 bit followed by all 0 bits. Padding must *always* be present, - so if the message length is already congruent, then `blockSize` padding bits - must be added. */ + value = Math.abs(value) - // create final block - var finalBlock = new MessageDigest.ByteBuffer(); - finalBlock.putBytes(this._input.bytes()); + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0 + e = eMax + } else { + e = Math.floor(Math.log(value) / Math.LN2) + if (value * (c = Math.pow(2, -e)) < 1) { + e-- + c *= 2 + } + if (e + eBias >= 1) { + value += rt / c + } else { + value += rt * Math.pow(2, 1 - eBias) + } + if (value * c >= 2) { + e++ + c /= 2 + } - // compute remaining size to be digested (include message length size) - var remaining = ( - this.fullMessageLength[this.fullMessageLength.length - 1] + - this._algorithm.messageLengthSize); + if (e + eBias >= eMax) { + m = 0 + e = eMax + } else if (e + eBias >= 1) { + m = ((value * c) - 1) * Math.pow(2, mLen) + e = e + eBias + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) + e = 0 + } + } - // add padding for overflow blockSize - overflow - // _padding starts with 1 byte with first bit is set (byte value 128), then - // there may be up to (blockSize - 1) other pad bytes - var overflow = remaining & (this._algorithm.blockSize - 1); - finalBlock.putBytes(MessageDigest._padding.substr( - 0, this._algorithm.blockSize - overflow)); + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} - // serialize message length in bits in big-endian order; since length - // is stored in bytes we multiply by 8 (left shift by 3 and merge in - // remainder from ) - var messageLength = new MessageDigest.ByteBuffer(); - for(var i = 0; i < this.fullMessageLength.length; ++i) { - messageLength.putInt32((this.fullMessageLength[i] << 3) | - (this.fullMessageLength[i + 1] >>> 28)); - } + e = (e << mLen) | m + eLen += mLen + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} - // write the length of the message (algorithm-specific) - this._algorithm.writeMessageLength(finalBlock, messageLength); + buffer[offset + i - d] |= s * 128 +} - // digest final block - var state = this._algorithm.digest(this.state.copy(), finalBlock); +},{}],42:[function(_dereq_,module,exports){ - // write state to buffer - var rval = new MessageDigest.ByteBuffer(); - state.write(rval); - return rval; -}; +var indexOf = [].indexOf; -/** - * Creates a simple byte buffer for message digest operations. - * - * @param data the data to put in the buffer. - */ -MessageDigest.ByteBuffer = function(data) { - if(typeof data === 'string') { - this.data = data; - } else { - this.data = ''; +module.exports = function(arr, obj){ + if (indexOf) return arr.indexOf(obj); + for (var i = 0; i < arr.length; ++i) { + if (arr[i] === obj) return i; } - this.read = 0; -}; - -/** - * Puts a 32-bit integer into this buffer in big-endian order. - * - * @param i the 32-bit integer. - */ -MessageDigest.ByteBuffer.prototype.putInt32 = function(i) { - this.data += ( - String.fromCharCode(i >> 24 & 0xFF) + - String.fromCharCode(i >> 16 & 0xFF) + - String.fromCharCode(i >> 8 & 0xFF) + - String.fromCharCode(i & 0xFF)); -}; - -/** - * Gets a 32-bit integer from this buffer in big-endian order and - * advances the read pointer by 4. - * - * @return the word. - */ -MessageDigest.ByteBuffer.prototype.getInt32 = function() { - var rval = ( - this.data.charCodeAt(this.read) << 24 ^ - this.data.charCodeAt(this.read + 1) << 16 ^ - this.data.charCodeAt(this.read + 2) << 8 ^ - this.data.charCodeAt(this.read + 3)); - this.read += 4; - return rval; + return -1; }; +},{}],43:[function(_dereq_,module,exports){ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + if (superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }) + } + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + if (superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } + } +} -/** - * Puts the given bytes into this buffer. +},{}],44:[function(_dereq_,module,exports){ +/*! + * Determine if an object is a Buffer * - * @param bytes the bytes as a binary-encoded string. + * @author Feross Aboukhadijeh + * @license MIT */ -MessageDigest.ByteBuffer.prototype.putBytes = function(bytes) { - this.data += bytes; -}; -/** - * Gets the bytes in this buffer. - * - * @return a string full of UTF-8 encoded characters. - */ -MessageDigest.ByteBuffer.prototype.bytes = function() { - return this.data.slice(this.read); -}; +// The _isBuffer check is for Safari 5-7 support, because it's missing +// Object.prototype.constructor. Remove this eventually +module.exports = function (obj) { + return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer) +} + +function isBuffer (obj) { + return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) +} +// For Node v0.10 support. Remove this eventually. +function isSlowBuffer (obj) { + return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0)) +} + +},{}],45:[function(_dereq_,module,exports){ +// Ignore module for browserify (see package.json) +},{}],46:[function(_dereq_,module,exports){ +(function (process,global,__dirname){ /** - * Gets the number of bytes in this buffer. + * A JavaScript implementation of the JSON-LD API. * - * @return the number of bytes in this buffer. + * @author Dave Longley + * + * @license BSD 3-Clause License + * Copyright (c) 2011-2015 Digital Bazaar, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of the Digital Bazaar, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -MessageDigest.ByteBuffer.prototype.length = function() { - return this.data.length - this.read; -}; +(function() { -/** - * Compacts this buffer. - */ -MessageDigest.ByteBuffer.prototype.compact = function() { - this.data = this.data.slice(this.read); - this.read = 0; -}; +// determine if in-browser or using node.js +var _nodejs = ( + typeof process !== 'undefined' && process.versions && process.versions.node); +var _browser = !_nodejs && + (typeof window !== 'undefined' || typeof self !== 'undefined'); +if(_browser) { + if(typeof global === 'undefined') { + if(typeof window !== 'undefined') { + global = window; + } else if(typeof self !== 'undefined') { + global = self; + } else if(typeof $ !== 'undefined') { + global = $; + } + } +} + +// attaches jsonld API to the given object +var wrapper = function(jsonld) { + +/* Core API */ /** - * Converts this buffer to a hexadecimal string. + * Performs JSON-LD compaction. * - * @return a hexadecimal string. + * @param input the JSON-LD input to compact. + * @param ctx the context to compact with. + * @param [options] options to use: + * [base] the base IRI to use. + * [compactArrays] true to compact arrays to single values when + * appropriate, false not to (default: true). + * [graph] true to always output a top-level graph (default: false). + * [expandContext] a context to expand with. + * [skipExpansion] true to assume the input is expanded and skip + * expansion, false not to, defaults to false. + * [documentLoader(url, callback(err, remoteDoc))] the document loader. + * @param callback(err, compacted, ctx) called once the operation completes. */ -MessageDigest.ByteBuffer.prototype.toHex = function() { - var rval = ''; - for(var i = this.read; i < this.data.length; ++i) { - var b = this.data.charCodeAt(i); - if(b < 16) { - rval += '0'; - } - rval += b.toString(16); +jsonld.compact = function(input, ctx, options, callback) { + if(arguments.length < 2) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not compact, too few arguments.')); + }); } - return rval; -}; -///////////////////////////// DEFINE SHA-1 ALGORITHM ////////////////////////// + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; -var sha1 = { - // used for word storage - _w: null -}; + if(ctx === null) { + return jsonld.nextTick(function() { + callback(new JsonLdError( + 'The compaction context must not be null.', + 'jsonld.CompactError', {code: 'invalid local context'})); + }); + } -sha1.Algorithm = function() { - this.name = 'sha1', - this.blockSize = 64; - this.digestLength = 20; - this.messageLengthSize = 8; -}; + // nothing to compact + if(input === null) { + return jsonld.nextTick(function() { + callback(null, null); + }); + } -sha1.Algorithm.prototype.start = function() { - if(!sha1._w) { - sha1._w = new Array(80); + // set default options + if(!('base' in options)) { + options.base = (typeof input === 'string') ? input : ''; + } + if(!('compactArrays' in options)) { + options.compactArrays = true; + } + if(!('graph' in options)) { + options.graph = false; + } + if(!('skipExpansion' in options)) { + options.skipExpansion = false; + } + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } + if(!('link' in options)) { + options.link = false; + } + if(options.link) { + // force skip expansion when linking, "link" is not part of the public + // API, it should only be called from framing + options.skipExpansion = true; } - return sha1._createState(); -}; -sha1.Algorithm.prototype.writeMessageLength = function( - finalBlock, messageLength) { - // message length is in bits and in big-endian order; simply append - finalBlock.putBytes(messageLength.bytes()); -}; + var expand = function(input, options, callback) { + if(options.skipExpansion) { + return jsonld.nextTick(function() { + callback(null, input); + }); + } + jsonld.expand(input, options, callback); + }; -sha1.Algorithm.prototype.digest = function(s, input) { - // consume 512 bit (64 byte) chunks - var t, a, b, c, d, e, f, i; - var len = input.length(); - var _w = sha1._w; - while(len >= 64) { - // initialize hash value for this chunk - a = s.h0; - b = s.h1; - c = s.h2; - d = s.h3; - e = s.h4; + // expand input then do compaction + expand(input, options, function(err, expanded) { + if(err) { + return callback(new JsonLdError( + 'Could not expand input before compaction.', + 'jsonld.CompactError', {cause: err})); + } - // the _w array will be populated with sixteen 32-bit big-endian words - // and then extended into 80 32-bit words according to SHA-1 algorithm - // and for 32-79 using Max Locktyukhin's optimization + // process context + var activeCtx = _getInitialContext(options); + jsonld.processContext(activeCtx, ctx, options, function(err, activeCtx) { + if(err) { + return callback(new JsonLdError( + 'Could not process context before compaction.', + 'jsonld.CompactError', {cause: err})); + } - // round 1 - for(i = 0; i < 16; ++i) { - t = input.getInt32(); - _w[i] = t; - f = d ^ (b & (c ^ d)); - t = ((a << 5) | (a >>> 27)) + f + e + 0x5A827999 + t; - e = d; - d = c; - c = (b << 30) | (b >>> 2); - b = a; - a = t; + var compacted; + try { + // do compaction + compacted = new Processor().compact(activeCtx, null, expanded, options); + } catch(ex) { + return callback(ex); + } + + cleanup(null, compacted, activeCtx, options); + }); + }); + + // performs clean up after compaction + function cleanup(err, compacted, activeCtx, options) { + if(err) { + return callback(err); } - for(; i < 20; ++i) { - t = (_w[i - 3] ^ _w[i - 8] ^ _w[i - 14] ^ _w[i - 16]); - t = (t << 1) | (t >>> 31); - _w[i] = t; - f = d ^ (b & (c ^ d)); - t = ((a << 5) | (a >>> 27)) + f + e + 0x5A827999 + t; - e = d; - d = c; - c = (b << 30) | (b >>> 2); - b = a; - a = t; + + if(options.compactArrays && !options.graph && _isArray(compacted)) { + if(compacted.length === 1) { + // simplify to a single item + compacted = compacted[0]; + } else if(compacted.length === 0) { + // simplify to an empty object + compacted = {}; + } + } else if(options.graph && _isObject(compacted)) { + // always use array if graph option is on + compacted = [compacted]; } - // round 2 - for(; i < 32; ++i) { - t = (_w[i - 3] ^ _w[i - 8] ^ _w[i - 14] ^ _w[i - 16]); - t = (t << 1) | (t >>> 31); - _w[i] = t; - f = b ^ c ^ d; - t = ((a << 5) | (a >>> 27)) + f + e + 0x6ED9EBA1 + t; - e = d; - d = c; - c = (b << 30) | (b >>> 2); - b = a; - a = t; + + // follow @context key + if(_isObject(ctx) && '@context' in ctx) { + ctx = ctx['@context']; } - for(; i < 40; ++i) { - t = (_w[i - 6] ^ _w[i - 16] ^ _w[i - 28] ^ _w[i - 32]); - t = (t << 2) | (t >>> 30); - _w[i] = t; - f = b ^ c ^ d; - t = ((a << 5) | (a >>> 27)) + f + e + 0x6ED9EBA1 + t; - e = d; - d = c; - c = (b << 30) | (b >>> 2); - b = a; - a = t; + + // build output context + ctx = _clone(ctx); + if(!_isArray(ctx)) { + ctx = [ctx]; } - // round 3 - for(; i < 60; ++i) { - t = (_w[i - 6] ^ _w[i - 16] ^ _w[i - 28] ^ _w[i - 32]); - t = (t << 2) | (t >>> 30); - _w[i] = t; - f = (b & c) | (d & (b ^ c)); - t = ((a << 5) | (a >>> 27)) + f + e + 0x8F1BBCDC + t; - e = d; - d = c; - c = (b << 30) | (b >>> 2); - b = a; - a = t; + // remove empty contexts + var tmp = ctx; + ctx = []; + for(var i = 0; i < tmp.length; ++i) { + if(!_isObject(tmp[i]) || Object.keys(tmp[i]).length > 0) { + ctx.push(tmp[i]); + } } - // round 4 - for(; i < 80; ++i) { - t = (_w[i - 6] ^ _w[i - 16] ^ _w[i - 28] ^ _w[i - 32]); - t = (t << 2) | (t >>> 30); - _w[i] = t; - f = b ^ c ^ d; - t = ((a << 5) | (a >>> 27)) + f + e + 0xCA62C1D6 + t; - e = d; - d = c; - c = (b << 30) | (b >>> 2); - b = a; - a = t; + + // remove array if only one context + var hasContext = (ctx.length > 0); + if(ctx.length === 1) { + ctx = ctx[0]; } - // update hash state - s.h0 = (s.h0 + a) | 0; - s.h1 = (s.h1 + b) | 0; - s.h2 = (s.h2 + c) | 0; - s.h3 = (s.h3 + d) | 0; - s.h4 = (s.h4 + e) | 0; + // add context and/or @graph + if(_isArray(compacted)) { + // use '@graph' keyword + var kwgraph = _compactIri(activeCtx, '@graph'); + var graph = compacted; + compacted = {}; + if(hasContext) { + compacted['@context'] = ctx; + } + compacted[kwgraph] = graph; + } else if(_isObject(compacted) && hasContext) { + // reorder keys so @context is first + var graph = compacted; + compacted = {'@context': ctx}; + for(var key in graph) { + compacted[key] = graph[key]; + } + } - len -= 64; + callback(null, compacted, activeCtx); } - - return s; -}; - -sha1._createState = function() { - var state = { - h0: 0x67452301, - h1: 0xEFCDAB89, - h2: 0x98BADCFE, - h3: 0x10325476, - h4: 0xC3D2E1F0 - }; - state.copy = function() { - var rval = sha1._createState(); - rval.h0 = state.h0; - rval.h1 = state.h1; - rval.h2 = state.h2; - rval.h3 = state.h3; - rval.h4 = state.h4; - return rval; - }; - state.write = function(buffer) { - buffer.putInt32(state.h0); - buffer.putInt32(state.h1); - buffer.putInt32(state.h2); - buffer.putInt32(state.h3); - buffer.putInt32(state.h4); - }; - return state; -}; - -//////////////////////////// DEFINE SHA-256 ALGORITHM ///////////////////////// - -var sha256 = { - // shared state - _k: null, - _w: null }; -sha256.Algorithm = function() { - this.name = 'sha256', - this.blockSize = 64; - this.digestLength = 32; - this.messageLengthSize = 8; -}; +/** + * Performs JSON-LD expansion. + * + * @param input the JSON-LD input to expand. + * @param [options] the options to use: + * [base] the base IRI to use. + * [expandContext] a context to expand with. + * [keepFreeFloatingNodes] true to keep free-floating nodes, + * false not to, defaults to false. + * [documentLoader(url, callback(err, remoteDoc))] the document loader. + * @param callback(err, expanded) called once the operation completes. + */ +jsonld.expand = function(input, options, callback) { + if(arguments.length < 1) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not expand, too few arguments.')); + }); + } -sha256.Algorithm.prototype.start = function() { - if(!sha256._k) { - sha256._init(); + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; } - return sha256._createState(); -}; + options = options || {}; -sha256.Algorithm.prototype.writeMessageLength = function( - finalBlock, messageLength) { - // message length is in bits and in big-endian order; simply append - finalBlock.putBytes(messageLength.bytes()); -}; + // set default options + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } + if(!('keepFreeFloatingNodes' in options)) { + options.keepFreeFloatingNodes = false; + } -sha256.Algorithm.prototype.digest = function(s, input) { - // consume 512 bit (64 byte) chunks - var t1, t2, s0, s1, ch, maj, i, a, b, c, d, e, f, g, h; - var len = input.length(); - var _k = sha256._k; - var _w = sha256._w; - while(len >= 64) { - // the w array will be populated with sixteen 32-bit big-endian words - // and then extended into 64 32-bit words according to SHA-256 - for(i = 0; i < 16; ++i) { - _w[i] = input.getInt32(); - } - for(; i < 64; ++i) { - // XOR word 2 words ago rot right 17, rot right 19, shft right 10 - t1 = _w[i - 2]; - t1 = - ((t1 >>> 17) | (t1 << 15)) ^ - ((t1 >>> 19) | (t1 << 13)) ^ - (t1 >>> 10); - // XOR word 15 words ago rot right 7, rot right 18, shft right 3 - t2 = _w[i - 15]; - t2 = - ((t2 >>> 7) | (t2 << 25)) ^ - ((t2 >>> 18) | (t2 << 14)) ^ - (t2 >>> 3); - // sum(t1, word 7 ago, t2, word 16 ago) modulo 2^32 - _w[i] = (t1 + _w[i - 7] + t2 + _w[i - 16]) | 0; + jsonld.nextTick(function() { + // if input is a string, attempt to dereference remote document + if(typeof input === 'string') { + var done = function(err, remoteDoc) { + if(err) { + return callback(err); + } + try { + if(!remoteDoc.document) { + throw new JsonLdError( + 'No remote document found at the given URL.', + 'jsonld.NullRemoteDocument'); + } + if(typeof remoteDoc.document === 'string') { + remoteDoc.document = JSON.parse(remoteDoc.document); + } + } catch(ex) { + return callback(new JsonLdError( + 'Could not retrieve a JSON-LD document from the URL. URL ' + + 'dereferencing not implemented.', 'jsonld.LoadDocumentError', { + code: 'loading document failed', + cause: ex, + remoteDoc: remoteDoc + })); + } + expand(remoteDoc); + }; + var promise = options.documentLoader(input, done); + if(promise && 'then' in promise) { + promise.then(done.bind(null, null), done); + } + return; } + // nothing to load + expand({contextUrl: null, documentUrl: null, document: input}); + }); - // initialize hash value for this chunk - a = s.h0; - b = s.h1; - c = s.h2; - d = s.h3; - e = s.h4; - f = s.h5; - g = s.h6; - h = s.h7; - - // round function - for(i = 0; i < 64; ++i) { - // Sum1(e) - s1 = - ((e >>> 6) | (e << 26)) ^ - ((e >>> 11) | (e << 21)) ^ - ((e >>> 25) | (e << 7)); - // Ch(e, f, g) (optimized the same way as SHA-1) - ch = g ^ (e & (f ^ g)); - // Sum0(a) - s0 = - ((a >>> 2) | (a << 30)) ^ - ((a >>> 13) | (a << 19)) ^ - ((a >>> 22) | (a << 10)); - // Maj(a, b, c) (optimized the same way as SHA-1) - maj = (a & b) | (c & (a ^ b)); - - // main algorithm - t1 = h + s1 + ch + _k[i] + _w[i]; - t2 = s0 + maj; - h = g; - g = f; - f = e; - e = (d + t1) | 0; - d = c; - c = b; - b = a; - a = (t1 + t2) | 0; + function expand(remoteDoc) { + // set default base + if(!('base' in options)) { + options.base = remoteDoc.documentUrl || ''; } + // build meta-object and retrieve all @context URLs + var input = { + document: _clone(remoteDoc.document), + remoteContext: {'@context': remoteDoc.contextUrl} + }; + if('expandContext' in options) { + var expandContext = _clone(options.expandContext); + if(typeof expandContext === 'object' && '@context' in expandContext) { + input.expandContext = expandContext; + } else { + input.expandContext = {'@context': expandContext}; + } + } + _retrieveContextUrls(input, options, function(err, input) { + if(err) { + return callback(err); + } - // update hash state - s.h0 = (s.h0 + a) | 0; - s.h1 = (s.h1 + b) | 0; - s.h2 = (s.h2 + c) | 0; - s.h3 = (s.h3 + d) | 0; - s.h4 = (s.h4 + e) | 0; - s.h5 = (s.h5 + f) | 0; - s.h6 = (s.h6 + g) | 0; - s.h7 = (s.h7 + h) | 0; - len -= 64; - } - - return s; -}; - -sha256._createState = function() { - var state = { - h0: 0x6A09E667, - h1: 0xBB67AE85, - h2: 0x3C6EF372, - h3: 0xA54FF53A, - h4: 0x510E527F, - h5: 0x9B05688C, - h6: 0x1F83D9AB, - h7: 0x5BE0CD19 - }; - state.copy = function() { - var rval = sha256._createState(); - rval.h0 = state.h0; - rval.h1 = state.h1; - rval.h2 = state.h2; - rval.h3 = state.h3; - rval.h4 = state.h4; - rval.h5 = state.h5; - rval.h6 = state.h6; - rval.h7 = state.h7; - return rval; - }; - state.write = function(buffer) { - buffer.putInt32(state.h0); - buffer.putInt32(state.h1); - buffer.putInt32(state.h2); - buffer.putInt32(state.h3); - buffer.putInt32(state.h4); - buffer.putInt32(state.h5); - buffer.putInt32(state.h6); - buffer.putInt32(state.h7); - }; - return state; -}; - -sha256._init = function() { - // create K table for SHA-256 - sha256._k = [ - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2]; - - // used for word storage - sha256._w = new Array(64); -}; + var expanded; + try { + var processor = new Processor(); + var activeCtx = _getInitialContext(options); + var document = input.document; + var remoteContext = input.remoteContext['@context']; -})(_nodejs); // end definition of NormalizeHash + // process optional expandContext + if(input.expandContext) { + activeCtx = processor.processContext( + activeCtx, input.expandContext['@context'], options); + } -if(!XMLSerializer) { + // process remote context from HTTP Link Header + if(remoteContext) { + activeCtx = processor.processContext( + activeCtx, remoteContext, options); + } -var _defineXMLSerializer = function() { - XMLSerializer = _dereq_('xmldom').XMLSerializer; -}; + // expand document + expanded = processor.expand( + activeCtx, null, document, options, false); -} // end _defineXMLSerializer + // optimize away @graph with no other properties + if(_isObject(expanded) && ('@graph' in expanded) && + Object.keys(expanded).length === 1) { + expanded = expanded['@graph']; + } else if(expanded === null) { + expanded = []; + } -// define URL parser -// parseUri 1.2.2 -// (c) Steven Levithan -// MIT License -// with local jsonld.js modifications -jsonld.url = {}; -jsonld.url.parsers = { - simple: { - // RFC 3986 basic parts - keys: ['href','scheme','authority','path','query','fragment'], - regex: /^(?:([^:\/?#]+):)?(?:\/\/([^\/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?/ - }, - full: { - keys: ['href','protocol','scheme','authority','auth','user','password','hostname','port','path','directory','file','query','fragment'], - regex: /^(([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?(?:(((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/ - } -}; -jsonld.url.parse = function(str, parser) { - var parsed = {}; - var o = jsonld.url.parsers[parser || 'full']; - var m = o.regex.exec(str); - var i = o.keys.length; - while(i--) { - parsed[o.keys[i]] = (m[i] === undefined) ? null : m[i]; + // normalize to an array + if(!_isArray(expanded)) { + expanded = [expanded]; + } + } catch(ex) { + return callback(ex); + } + callback(null, expanded); + }); } - parsed.normalizedPath = _removeDotSegments(parsed.path, !!parsed.authority); - return parsed; }; /** - * Removes dot segments from a URL path. + * Performs JSON-LD flattening. * - * @param path the path to remove dot segments from. - * @param hasAuthority true if the URL has an authority, false if not. + * @param input the JSON-LD to flatten. + * @param ctx the context to use to compact the flattened output, or null. + * @param [options] the options to use: + * [base] the base IRI to use. + * [expandContext] a context to expand with. + * [documentLoader(url, callback(err, remoteDoc))] the document loader. + * @param callback(err, flattened) called once the operation completes. */ -function _removeDotSegments(path, hasAuthority) { - var rval = ''; - - if(path.indexOf('/') === 0) { - rval = '/'; +jsonld.flatten = function(input, ctx, options, callback) { + if(arguments.length < 1) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not flatten, too few arguments.')); + }); } - // RFC 3986 5.2.4 (reworked) - var input = path.split('/'); - var output = []; - while(input.length > 0) { - if(input[0] === '.' || (input[0] === '' && input.length > 1)) { - input.shift(); - continue; - } - if(input[0] === '..') { - input.shift(); - if(hasAuthority || - (output.length > 0 && output[output.length - 1] !== '..')) { - output.pop(); - } else { - // leading relative URL '..' - output.push('..'); - } - continue; - } - output.push(input.shift()); + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } else if(typeof ctx === 'function') { + callback = ctx; + ctx = null; + options = {}; } + options = options || {}; - return rval + output.join('/'); -} - -if(_nodejs) { - // use node document loader by default - jsonld.useDocumentLoader('node'); -} else if(typeof XMLHttpRequest !== 'undefined') { - // use xhr document loader by default - jsonld.useDocumentLoader('xhr'); -} + // set default options + if(!('base' in options)) { + options.base = (typeof input === 'string') ? input : ''; + } + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } -if(_nodejs) { - jsonld.use = function(extension) { - switch(extension) { - // TODO: Deprecated as of 0.4.0. Remove at some point. - case 'request': - // use node JSON-LD request extension - jsonld.request = _dereq_('jsonld-request'); - break; - default: - throw new JsonLdError( - 'Unknown extension.', - 'jsonld.UnknownExtension', {extension: extension}); + // expand input + jsonld.expand(input, options, function(err, _input) { + if(err) { + return callback(new JsonLdError( + 'Could not expand input before flattening.', + 'jsonld.FlattenError', {cause: err})); } - }; - - // expose version - var _module = {exports: {}, filename: __dirname}; - _dereq_('pkginfo')(_module, 'version'); - jsonld.version = _module.exports.version; -} -// end of jsonld API factory -return jsonld; -}; + var flattened; + try { + // do flattening + flattened = new Processor().flatten(_input); + } catch(ex) { + return callback(ex); + } -// external APIs: + if(ctx === null) { + return callback(null, flattened); + } -// used to generate a new jsonld API instance -var factory = function() { - return wrapper(function() { - return factory(); + // compact result (force @graph option to true, skip expansion) + options.graph = true; + options.skipExpansion = true; + jsonld.compact(flattened, ctx, options, function(err, compacted) { + if(err) { + return callback(new JsonLdError( + 'Could not compact flattened output.', + 'jsonld.FlattenError', {cause: err})); + } + callback(null, compacted); + }); }); }; -if(!_nodejs && (typeof define === 'function' && define.amd)) { - // export AMD API - define([], function() { - // now that module is defined, wrap main jsonld API instance - wrapper(factory); - return factory; - }); -} else { - // wrap the main jsonld API instance - wrapper(factory); - - if(typeof _dereq_ === 'function' && - typeof module !== 'undefined' && module.exports) { - // export CommonJS/nodejs API - module.exports = factory; +/** + * Performs JSON-LD framing. + * + * @param input the JSON-LD input to frame. + * @param frame the JSON-LD frame to use. + * @param [options] the framing options. + * [base] the base IRI to use. + * [expandContext] a context to expand with. + * [embed] default @embed flag: '@last', '@always', '@never', '@link' + * (default: '@last'). + * [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. + */ +jsonld.frame = function(input, frame, options, callback) { + if(arguments.length < 2) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not frame, too few arguments.')); + }); } - if(_browser) { - // export simple browser API - if(typeof jsonld === 'undefined') { - jsonld = jsonldjs = factory; - } else { - jsonldjs = factory; - } + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; } -} - -return factory; - -})(); - -}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},"/node_modules/jsonld/js") + options = options || {}; -},{"_process":47,"crypto":18,"es6-promise":10,"http":18,"jsonld-request":18,"pkginfo":18,"request":18,"util":18,"xmldom":18}],20:[function(_dereq_,module,exports){ -var pkg = _dereq_('./src/libsbgn'); + // set default options + if(!('base' in options)) { + options.base = (typeof input === 'string') ? input : ''; + } + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } + if(!('embed' in options)) { + options.embed = '@last'; + } + options.explicit = options.explicit || false; + if(!('requireAll' in options)) { + options.requireAll = true; + } + options.omitDefault = options.omitDefault || false; -module.exports = pkg; + jsonld.nextTick(function() { + // if frame is a string, attempt to dereference remote document + if(typeof frame === 'string') { + var done = function(err, remoteDoc) { + if(err) { + return callback(err); + } + try { + if(!remoteDoc.document) { + throw new JsonLdError( + 'No remote document found at the given URL.', + 'jsonld.NullRemoteDocument'); + } + if(typeof remoteDoc.document === 'string') { + remoteDoc.document = JSON.parse(remoteDoc.document); + } + } catch(ex) { + return callback(new JsonLdError( + 'Could not retrieve a JSON-LD document from the URL. URL ' + + 'dereferencing not implemented.', 'jsonld.LoadDocumentError', { + code: 'loading document failed', + cause: ex, + remoteDoc: remoteDoc + })); + } + doFrame(remoteDoc); + }; + var promise = options.documentLoader(frame, done); + if(promise && 'then' in promise) { + promise.then(done.bind(null, null), done); + } + return; + } + // nothing to load + doFrame({contextUrl: null, documentUrl: null, document: frame}); + }); -},{"./src/libsbgn":31}],21:[function(_dereq_,module,exports){ -// Generated by CoffeeScript 1.12.7 -(function() { - "use strict"; - exports.stripBOM = function(str) { - if (str[0] === '\uFEFF') { - return str.substring(1); + function doFrame(remoteFrame) { + // preserve frame context and add any Link header context + var frame = remoteFrame.document; + var ctx; + if(frame) { + ctx = frame['@context']; + if(remoteFrame.contextUrl) { + if(!ctx) { + ctx = remoteFrame.contextUrl; + } else if(_isArray(ctx)) { + ctx.push(remoteFrame.contextUrl); + } else { + ctx = [ctx, remoteFrame.contextUrl]; + } + frame['@context'] = ctx; + } else { + ctx = ctx || {}; + } } else { - return str; + ctx = {}; } - }; -}).call(this); + // expand input + jsonld.expand(input, options, function(err, expanded) { + if(err) { + return callback(new JsonLdError( + 'Could not expand input before framing.', + 'jsonld.FrameError', {cause: err})); + } -},{}],22:[function(_dereq_,module,exports){ -// Generated by CoffeeScript 1.12.7 -(function() { - "use strict"; - var builder, defaults, escapeCDATA, requiresCDATA, wrapCDATA, - hasProp = {}.hasOwnProperty; + // expand frame + var opts = _clone(options); + opts.isFrame = true; + opts.keepFreeFloatingNodes = true; + jsonld.expand(frame, opts, function(err, expandedFrame) { + if(err) { + return callback(new JsonLdError( + 'Could not expand frame before framing.', + 'jsonld.FrameError', {cause: err})); + } - builder = _dereq_('xmlbuilder'); + var framed; + try { + // do framing + framed = new Processor().frame(expanded, expandedFrame, opts); + } catch(ex) { + return callback(ex); + } - defaults = _dereq_('./defaults').defaults; + // compact result (force @graph option to true, skip expansion, + // check for linked embeds) + opts.graph = true; + opts.skipExpansion = true; + opts.link = {}; + jsonld.compact(framed, ctx, opts, function(err, compacted, ctx) { + if(err) { + return callback(new JsonLdError( + 'Could not compact framed output.', + 'jsonld.FrameError', {cause: err})); + } + // get graph alias + var graph = _compactIri(ctx, '@graph'); + // remove @preserve from results + opts.link = {}; + compacted[graph] = _removePreserve(ctx, compacted[graph], opts); + callback(null, compacted); + }); + }); + }); + } +}; - requiresCDATA = function(entry) { - return typeof entry === "string" && (entry.indexOf('&') >= 0 || entry.indexOf('>') >= 0 || entry.indexOf('<') >= 0); - }; +/** + * **Experimental** + * + * Links a JSON-LD document's nodes in memory. + * + * @param input the JSON-LD document to link. + * @param ctx the JSON-LD context to apply. + * @param [options] the options to use: + * [base] the base IRI to use. + * [expandContext] a context to expand with. + * [documentLoader(url, callback(err, remoteDoc))] the document loader. + * @param callback(err, linked) called once the operation completes. + */ +jsonld.link = function(input, ctx, options, callback) { + // API matches running frame with a wildcard frame and embed: '@link' + // get arguments + var frame = {}; + if(ctx) { + frame['@context'] = ctx; + } + frame['@embed'] = '@link'; + jsonld.frame(input, frame, options, callback); +}; - wrapCDATA = function(entry) { - return ""; - }; +/** + * **Deprecated** + * + * Performs JSON-LD objectification. + * + * @param input the JSON-LD document to objectify. + * @param ctx the JSON-LD context to apply. + * @param [options] the options to use: + * [base] the base IRI to use. + * [expandContext] a context to expand with. + * [documentLoader(url, callback(err, remoteDoc))] the document loader. + * @param callback(err, linked) called once the operation completes. + */ +jsonld.objectify = function(input, ctx, options, callback) { + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; - escapeCDATA = function(entry) { - return entry.replace(']]>', ']]]]>'); - }; + // set default options + if(!('base' in options)) { + options.base = (typeof input === 'string') ? input : ''; + } + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } - exports.Builder = (function() { - function Builder(opts) { - var key, ref, value; - this.options = {}; - ref = defaults["0.2"]; - for (key in ref) { - if (!hasProp.call(ref, key)) continue; - value = ref[key]; - this.options[key] = value; - } - for (key in opts) { - if (!hasProp.call(opts, key)) continue; - value = opts[key]; - this.options[key] = value; - } + // expand input + jsonld.expand(input, options, function(err, _input) { + if(err) { + return callback(new JsonLdError( + 'Could not expand input before linking.', + 'jsonld.LinkError', {cause: err})); } - Builder.prototype.buildObject = function(rootObj) { - var attrkey, charkey, render, rootElement, rootName; - attrkey = this.options.attrkey; - charkey = this.options.charkey; - if ((Object.keys(rootObj).length === 1) && (this.options.rootName === defaults['0.2'].rootName)) { - rootName = Object.keys(rootObj)[0]; - rootObj = rootObj[rootName]; - } else { - rootName = this.options.rootName; + var flattened; + try { + // flatten the graph + flattened = new Processor().flatten(_input); + } catch(ex) { + return callback(ex); + } + + // compact result (force @graph option to true, skip expansion) + options.graph = true; + options.skipExpansion = true; + jsonld.compact(flattened, ctx, options, function(err, compacted, ctx) { + if(err) { + return callback(new JsonLdError( + 'Could not compact flattened output before linking.', + 'jsonld.LinkError', {cause: err})); } - render = (function(_this) { - return function(element, obj) { - var attr, child, entry, index, key, value; - if (typeof obj !== 'object') { - if (_this.options.cdata && requiresCDATA(obj)) { - element.raw(wrapCDATA(obj)); - } else { - element.txt(obj); - } - } else if (Array.isArray(obj)) { - for (index in obj) { - if (!hasProp.call(obj, index)) continue; - child = obj[index]; - for (key in child) { - entry = child[key]; - element = render(element.ele(key), entry).up(); - } - } - } else { - for (key in obj) { - if (!hasProp.call(obj, key)) continue; - child = obj[key]; - if (key === attrkey) { - if (typeof child === "object") { - for (attr in child) { - value = child[attr]; - element = element.att(attr, value); - } - } - } else if (key === charkey) { - if (_this.options.cdata && requiresCDATA(child)) { - element = element.raw(wrapCDATA(child)); - } else { - element = element.txt(child); - } - } else if (Array.isArray(child)) { - for (index in child) { - if (!hasProp.call(child, index)) continue; - entry = child[index]; - if (typeof entry === 'string') { - if (_this.options.cdata && requiresCDATA(entry)) { - element = element.ele(key).raw(wrapCDATA(entry)).up(); - } else { - element = element.ele(key, entry).up(); - } - } else { - element = render(element.ele(key), entry).up(); - } - } - } else if (typeof child === "object") { - element = render(element.ele(key), child).up(); - } else { - if (typeof child === 'string' && _this.options.cdata && requiresCDATA(child)) { - element = element.ele(key).raw(wrapCDATA(child)).up(); - } else { - if (child == null) { - child = ''; - } - element = element.ele(key, child.toString()).up(); - } - } - } - } - return element; - }; - })(this); - rootElement = builder.create(rootName, this.options.xmldec, this.options.doctype, { - headless: this.options.headless, - allowSurrogateChars: this.options.allowSurrogateChars - }); - return render(rootElement, rootObj).end(this.options.renderOpts); - }; + // get graph alias + var graph = _compactIri(ctx, '@graph'); + var top = compacted[graph][0]; - return Builder; + var recurse = function(subject) { + // can't replace just a string + if(!_isObject(subject) && !_isArray(subject)) { + return; + } - })(); + // bottom out recursion on re-visit + if(_isObject(subject)) { + if(recurse.visited[subject['@id']]) { + return; + } + recurse.visited[subject['@id']] = true; + } -}).call(this); + // each array element *or* object key + for(var k in subject) { + var obj = subject[k]; + var isid = (jsonld.getContextValue(ctx, k, '@type') === '@id'); -},{"./defaults":23,"xmlbuilder":182}],23:[function(_dereq_,module,exports){ -// Generated by CoffeeScript 1.12.7 -(function() { - exports.defaults = { - "0.1": { - explicitCharkey: false, - trim: true, - normalize: true, - normalizeTags: false, - attrkey: "@", - charkey: "#", - explicitArray: false, - ignoreAttrs: false, - mergeAttrs: false, - explicitRoot: false, - validator: null, - xmlns: false, - explicitChildren: false, - childkey: '@@', - charsAsChildren: false, - includeWhiteChars: false, - async: false, - strict: true, - attrNameProcessors: null, - attrValueProcessors: null, - tagNameProcessors: null, - valueProcessors: null, - emptyTag: '' - }, - "0.2": { - explicitCharkey: false, - trim: false, - normalize: false, - normalizeTags: false, - attrkey: "$", - charkey: "_", - explicitArray: true, - ignoreAttrs: false, - mergeAttrs: false, - explicitRoot: true, - validator: null, - xmlns: false, - explicitChildren: false, - preserveChildrenOrder: false, - childkey: '$$', - charsAsChildren: false, - includeWhiteChars: false, - async: false, - strict: true, - attrNameProcessors: null, - attrValueProcessors: null, - tagNameProcessors: null, - valueProcessors: null, - rootName: 'root', - xmldec: { - 'version': '1.0', - 'encoding': 'UTF-8', - 'standalone': true - }, - doctype: null, - renderOpts: { - 'pretty': true, - 'indent': ' ', - 'newline': '\n' - }, - headless: false, - chunkSize: 10000, - emptyTag: '', - cdata: false - } - }; + // can't replace a non-object or non-array unless it's an @id + if(!_isArray(obj) && !_isObject(obj) && !isid) { + continue; + } -}).call(this); + if(_isString(obj) && isid) { + subject[k] = obj = top[obj]; + recurse(obj); + } else if(_isArray(obj)) { + for(var i = 0; i < obj.length; ++i) { + if(_isString(obj[i]) && isid) { + obj[i] = top[obj[i]]; + } else if(_isObject(obj[i]) && '@id' in obj[i]) { + obj[i] = top[obj[i]['@id']]; + } + recurse(obj[i]); + } + } else if(_isObject(obj)) { + var sid = obj['@id']; + subject[k] = obj = top[sid]; + recurse(obj); + } + } + }; + recurse.visited = {}; + recurse(top); -},{}],24:[function(_dereq_,module,exports){ -// Generated by CoffeeScript 1.12.7 -(function() { - "use strict"; - var bom, defaults, events, isEmpty, processItem, processors, sax, setImmediate, - bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, - extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - hasProp = {}.hasOwnProperty; + compacted.of_type = {}; + for(var s in top) { + if(!('@type' in top[s])) { + continue; + } + var types = top[s]['@type']; + if(!_isArray(types)) { + types = [types]; + } + for(var t = 0; t < types.length; ++t) { + if(!(types[t] in compacted.of_type)) { + compacted.of_type[types[t]] = []; + } + compacted.of_type[types[t]].push(top[s]); + } + } + callback(null, compacted); + }); + }); +}; - sax = _dereq_('sax'); +/** + * Performs RDF dataset normalization on the given input. The input is JSON-LD + * unless the 'inputFormat' option is used. The output is an RDF dataset + * unless the 'format' option is used. + * + * @param input the input to normalize as JSON-LD or as a format specified by + * the 'inputFormat' option. + * @param [options] the options to use: + * [algorithm] the normalization algorithm to use, `URDNA2015` or + * `URGNA2012` (default: `URGNA2012`). + * [base] the base IRI to use. + * [expandContext] a context to expand with. + * [inputFormat] the format if input is not JSON-LD: + * 'application/nquads' for N-Quads. + * [format] the format if output is a string: + * 'application/nquads' for N-Quads. + * [documentLoader(url, callback(err, remoteDoc))] the document loader. + * @param callback(err, normalized) called once the operation completes. + */ +jsonld.normalize = function(input, options, callback) { + if(arguments.length < 1) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not normalize, too few arguments.')); + }); + } - events = _dereq_('events'); + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; - bom = _dereq_('./bom'); + // set default options + if(!('algorithm' in options)) { + options.algorithm = 'URGNA2012'; + } + if(!('base' in options)) { + options.base = (typeof input === 'string') ? input : ''; + } + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } - processors = _dereq_('./processors'); + if('inputFormat' in options) { + if(options.inputFormat !== 'application/nquads') { + return callback(new JsonLdError( + 'Unknown normalization input format.', + 'jsonld.NormalizeError')); + } + var parsedInput = _parseNQuads(input); + // do normalization + new Processor().normalize(parsedInput, options, callback); + } else { + // convert to RDF dataset then do normalization + var opts = _clone(options); + delete opts.format; + opts.produceGeneralizedRdf = false; + jsonld.toRDF(input, opts, function(err, dataset) { + if(err) { + return callback(new JsonLdError( + 'Could not convert input to RDF dataset before normalization.', + 'jsonld.NormalizeError', {cause: err})); + } + // do normalization + new Processor().normalize(dataset, options, callback); + }); + } +}; - setImmediate = _dereq_('timers').setImmediate; +/** + * Converts an RDF dataset to JSON-LD. + * + * @param dataset a serialized string of RDF in a format specified by the + * format option or an RDF dataset to convert. + * @param [options] the options to use: + * [format] the format if dataset param must first be parsed: + * 'application/nquads' for N-Quads (default). + * [rdfParser] a custom RDF-parser to use to parse the dataset. + * [useRdfType] true to use rdf:type, false to use @type + * (default: false). + * [useNativeTypes] true to convert XSD types into native types + * (boolean, integer, double), false not to (default: false). + * @param callback(err, output) called once the operation completes. + */ +jsonld.fromRDF = function(dataset, options, callback) { + if(arguments.length < 1) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not convert from RDF, too few arguments.')); + }); + } - defaults = _dereq_('./defaults').defaults; + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; - isEmpty = function(thing) { - return typeof thing === "object" && (thing != null) && Object.keys(thing).length === 0; - }; + // set default options + if(!('useRdfType' in options)) { + options.useRdfType = false; + } + if(!('useNativeTypes' in options)) { + options.useNativeTypes = false; + } - processItem = function(processors, item, key) { - var i, len, process; - for (i = 0, len = processors.length; i < len; i++) { - process = processors[i]; - item = process(item, key); + if(!('format' in options) && _isString(dataset)) { + // set default format to nquads + if(!('format' in options)) { + options.format = 'application/nquads'; } - return item; - }; - - exports.Parser = (function(superClass) { - extend(Parser, superClass); + } - function Parser(opts) { - this.parseString = bind(this.parseString, this); - this.reset = bind(this.reset, this); - this.assignOrPush = bind(this.assignOrPush, this); - this.processAsync = bind(this.processAsync, this); - var key, ref, value; - if (!(this instanceof exports.Parser)) { - return new exports.Parser(opts); + jsonld.nextTick(function() { + // handle special format + var rdfParser; + if(options.format) { + // check supported formats + rdfParser = options.rdfParser || _rdfParsers[options.format]; + if(!rdfParser) { + return callback(new JsonLdError( + 'Unknown input format.', + 'jsonld.UnknownFormat', {format: options.format})); } - this.options = {}; - ref = defaults["0.2"]; - for (key in ref) { - if (!hasProp.call(ref, key)) continue; - value = ref[key]; - this.options[key] = value; - } - for (key in opts) { - if (!hasProp.call(opts, key)) continue; - value = opts[key]; - this.options[key] = value; - } - if (this.options.xmlns) { - this.options.xmlnskey = this.options.attrkey + "ns"; - } - if (this.options.normalizeTags) { - if (!this.options.tagNameProcessors) { - this.options.tagNameProcessors = []; - } - this.options.tagNameProcessors.unshift(processors.normalize); - } - this.reset(); + } else { + // no-op parser, assume dataset already parsed + rdfParser = function() { + return dataset; + }; } - Parser.prototype.processAsync = function() { - var chunk, err; - try { - if (this.remaining.length <= this.options.chunkSize) { - chunk = this.remaining; - this.remaining = ''; - this.saxParser = this.saxParser.write(chunk); - return this.saxParser.close(); - } else { - chunk = this.remaining.substr(0, this.options.chunkSize); - this.remaining = this.remaining.substr(this.options.chunkSize, this.remaining.length); - this.saxParser = this.saxParser.write(chunk); - return setImmediate(this.processAsync); - } - } catch (error1) { - err = error1; - if (!this.saxParser.errThrown) { - this.saxParser.errThrown = true; - return this.emit(err); - } - } - }; - - Parser.prototype.assignOrPush = function(obj, key, newValue) { - if (!(key in obj)) { - if (!this.options.explicitArray) { - return obj[key] = newValue; - } else { - return obj[key] = [newValue]; - } - } else { - if (!(obj[key] instanceof Array)) { - obj[key] = [obj[key]]; + var callbackCalled = false; + try { + // rdf parser may be async or sync, always pass callback + dataset = rdfParser(dataset, function(err, dataset) { + callbackCalled = true; + if(err) { + return callback(err); } - return obj[key].push(newValue); - } - }; - - Parser.prototype.reset = function() { - var attrkey, charkey, ontext, stack; - this.removeAllListeners(); - this.saxParser = sax.parser(this.options.strict, { - trim: false, - normalize: false, - xmlns: this.options.xmlns + fromRDF(dataset, options, callback); }); - this.saxParser.errThrown = false; - this.saxParser.onerror = (function(_this) { - return function(error) { - _this.saxParser.resume(); - if (!_this.saxParser.errThrown) { - _this.saxParser.errThrown = true; - return _this.emit("error", error); - } - }; - })(this); - this.saxParser.onend = (function(_this) { - return function() { - if (!_this.saxParser.ended) { - _this.saxParser.ended = true; - return _this.emit("end", _this.resultObject); - } - }; - })(this); - this.saxParser.ended = false; - this.EXPLICIT_CHARKEY = this.options.explicitCharkey; - this.resultObject = null; - stack = []; - attrkey = this.options.attrkey; - charkey = this.options.charkey; - this.saxParser.onopentag = (function(_this) { - return function(node) { - var key, newValue, obj, processedKey, ref; - obj = {}; - obj[charkey] = ""; - if (!_this.options.ignoreAttrs) { - ref = node.attributes; - for (key in ref) { - if (!hasProp.call(ref, key)) continue; - if (!(attrkey in obj) && !_this.options.mergeAttrs) { - obj[attrkey] = {}; - } - newValue = _this.options.attrValueProcessors ? processItem(_this.options.attrValueProcessors, node.attributes[key], key) : node.attributes[key]; - processedKey = _this.options.attrNameProcessors ? processItem(_this.options.attrNameProcessors, key) : key; - if (_this.options.mergeAttrs) { - _this.assignOrPush(obj, processedKey, newValue); - } else { - obj[attrkey][processedKey] = newValue; - } - } - } - obj["#name"] = _this.options.tagNameProcessors ? processItem(_this.options.tagNameProcessors, node.name) : node.name; - if (_this.options.xmlns) { - obj[_this.options.xmlnskey] = { - uri: node.uri, - local: node.local - }; - } - return stack.push(obj); - }; - })(this); - this.saxParser.onclosetag = (function(_this) { - return function() { - var cdata, emptyStr, key, node, nodeName, obj, objClone, old, s, xpath; - obj = stack.pop(); - nodeName = obj["#name"]; - if (!_this.options.explicitChildren || !_this.options.preserveChildrenOrder) { - delete obj["#name"]; - } - if (obj.cdata === true) { - cdata = obj.cdata; - delete obj.cdata; - } - s = stack[stack.length - 1]; - if (obj[charkey].match(/^\s*$/) && !cdata) { - emptyStr = obj[charkey]; - delete obj[charkey]; - } else { - if (_this.options.trim) { - obj[charkey] = obj[charkey].trim(); - } - if (_this.options.normalize) { - obj[charkey] = obj[charkey].replace(/\s{2,}/g, " ").trim(); - } - obj[charkey] = _this.options.valueProcessors ? processItem(_this.options.valueProcessors, obj[charkey], nodeName) : obj[charkey]; - if (Object.keys(obj).length === 1 && charkey in obj && !_this.EXPLICIT_CHARKEY) { - obj = obj[charkey]; - } - } - if (isEmpty(obj)) { - obj = _this.options.emptyTag !== '' ? _this.options.emptyTag : emptyStr; - } - if (_this.options.validator != null) { - xpath = "/" + ((function() { - var i, len, results; - results = []; - for (i = 0, len = stack.length; i < len; i++) { - node = stack[i]; - results.push(node["#name"]); - } - return results; - })()).concat(nodeName).join("/"); - (function() { - var err; - try { - return obj = _this.options.validator(xpath, s && s[nodeName], obj); - } catch (error1) { - err = error1; - return _this.emit("error", err); - } - })(); - } - if (_this.options.explicitChildren && !_this.options.mergeAttrs && typeof obj === 'object') { - if (!_this.options.preserveChildrenOrder) { - node = {}; - if (_this.options.attrkey in obj) { - node[_this.options.attrkey] = obj[_this.options.attrkey]; - delete obj[_this.options.attrkey]; - } - if (!_this.options.charsAsChildren && _this.options.charkey in obj) { - node[_this.options.charkey] = obj[_this.options.charkey]; - delete obj[_this.options.charkey]; - } - if (Object.getOwnPropertyNames(obj).length > 0) { - node[_this.options.childkey] = obj; - } - obj = node; - } else if (s) { - s[_this.options.childkey] = s[_this.options.childkey] || []; - objClone = {}; - for (key in obj) { - if (!hasProp.call(obj, key)) continue; - objClone[key] = obj[key]; - } - s[_this.options.childkey].push(objClone); - delete obj["#name"]; - if (Object.keys(obj).length === 1 && charkey in obj && !_this.EXPLICIT_CHARKEY) { - obj = obj[charkey]; - } - } - } - if (stack.length > 0) { - return _this.assignOrPush(s, nodeName, obj); - } else { - if (_this.options.explicitRoot) { - old = obj; - obj = {}; - obj[nodeName] = old; - } - _this.resultObject = obj; - _this.saxParser.ended = true; - return _this.emit("end", _this.resultObject); - } - }; - })(this); - ontext = (function(_this) { - return function(text) { - var charChild, s; - s = stack[stack.length - 1]; - if (s) { - s[charkey] += text; - if (_this.options.explicitChildren && _this.options.preserveChildrenOrder && _this.options.charsAsChildren && (_this.options.includeWhiteChars || text.replace(/\\n/g, '').trim() !== '')) { - s[_this.options.childkey] = s[_this.options.childkey] || []; - charChild = { - '#name': '__text__' - }; - charChild[charkey] = text; - if (_this.options.normalize) { - charChild[charkey] = charChild[charkey].replace(/\s{2,}/g, " ").trim(); - } - s[_this.options.childkey].push(charChild); - } - return s; - } - }; - })(this); - this.saxParser.ontext = ontext; - return this.saxParser.oncdata = (function(_this) { - return function(text) { - var s; - s = ontext(text); - if (s) { - return s.cdata = true; - } - }; - })(this); - }; - - Parser.prototype.parseString = function(str, cb) { - var err; - if ((cb != null) && typeof cb === "function") { - this.on("end", function(result) { - this.reset(); - return cb(null, result); - }); - this.on("error", function(err) { - this.reset(); - return cb(err); - }); - } - try { - str = str.toString(); - if (str.trim() === '') { - this.emit('error', new Error("Empty string is not valid XML")); - return; - } - str = bom.stripBOM(str); - if (this.options.async) { - this.remaining = str; - setImmediate(this.processAsync); - return this.saxParser; - } - return this.saxParser.write(str).close(); - } catch (error1) { - err = error1; - if (!(this.saxParser.errThrown || this.saxParser.ended)) { - this.emit('error', err); - return this.saxParser.errThrown = true; - } else if (this.saxParser.ended) { - throw err; - } - } - }; - - return Parser; - - })(events.EventEmitter); - - exports.parseString = function(str, a, b) { - var cb, options, parser; - if (b != null) { - if (typeof b === 'function') { - cb = b; - } - if (typeof a === 'object') { - options = a; + } catch(e) { + if(!callbackCalled) { + return callback(e); } - } else { - if (typeof a === 'function') { - cb = a; + throw e; + } + // handle synchronous or promise-based parser + if(dataset) { + // if dataset is actually a promise + if('then' in dataset) { + return dataset.then(function(dataset) { + fromRDF(dataset, options, callback); + }, callback); } - options = {}; + // parser is synchronous + fromRDF(dataset, options, callback); } - parser = new exports.Parser(options); - return parser.parseString(str, cb); - }; - -}).call(this); -},{"./bom":21,"./defaults":23,"./processors":25,"events":11,"sax":109,"timers":116}],25:[function(_dereq_,module,exports){ -// Generated by CoffeeScript 1.12.7 -(function() { - "use strict"; - var prefixMatch; - - prefixMatch = new RegExp(/(?!xmlns)^.*:/); + function fromRDF(dataset, options, callback) { + // convert from RDF + new Processor().fromRDF(dataset, options, callback); + } + }); +}; - exports.normalize = function(str) { - return str.toLowerCase(); - }; +/** + * Outputs the RDF dataset found in the given JSON-LD object. + * + * @param input the JSON-LD input. + * @param [options] the options to use: + * [base] the base IRI to use. + * [expandContext] a context to expand with. + * [format] the format to use to output a string: + * 'application/nquads' for N-Quads. + * [produceGeneralizedRdf] true to output generalized RDF, false + * to produce only standard RDF (default: false). + * [documentLoader(url, callback(err, remoteDoc))] the document loader. + * @param callback(err, dataset) called once the operation completes. + */ +jsonld.toRDF = function(input, options, callback) { + if(arguments.length < 1) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not convert to RDF, too few arguments.')); + }); + } - exports.firstCharLowerCase = function(str) { - return str.charAt(0).toLowerCase() + str.slice(1); - }; + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; - exports.stripPrefix = function(str) { - return str.replace(prefixMatch, ''); - }; + // set default options + if(!('base' in options)) { + options.base = (typeof input === 'string') ? input : ''; + } + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } - exports.parseNumbers = function(str) { - if (!isNaN(str)) { - str = str % 1 === 0 ? parseInt(str, 10) : parseFloat(str); + // expand input + jsonld.expand(input, options, function(err, expanded) { + if(err) { + return callback(new JsonLdError( + 'Could not expand input before serialization to RDF.', + 'jsonld.RdfError', {cause: err})); } - return str; - }; - exports.parseBooleans = function(str) { - if (/^(?:true|false)$/i.test(str)) { - str = str.toLowerCase() === 'true'; + var dataset; + try { + // output RDF dataset + dataset = Processor.prototype.toRDF(expanded, options); + if(options.format) { + if(options.format === 'application/nquads') { + return callback(null, _toNQuads(dataset)); + } + throw new JsonLdError( + 'Unknown output format.', + 'jsonld.UnknownFormat', {format: options.format}); + } + } catch(ex) { + return callback(ex); } - return str; - }; - -}).call(this); - -},{}],26:[function(_dereq_,module,exports){ -// Generated by CoffeeScript 1.12.7 -(function() { - "use strict"; - var builder, defaults, parser, processors, - extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - hasProp = {}.hasOwnProperty; - - defaults = _dereq_('./defaults'); - - builder = _dereq_('./builder'); - - parser = _dereq_('./parser'); - - processors = _dereq_('./processors'); + callback(null, dataset); + }); +}; - exports.defaults = defaults.defaults; +/** + * **Experimental** + * + * Recursively flattens the nodes in the given JSON-LD input into a map of + * node ID => node. + * + * @param input the JSON-LD input. + * @param [options] the options to use: + * [base] the base IRI to use. + * [expandContext] a context to expand with. + * [issuer] a jsonld.IdentifierIssuer to use to label blank nodes. + * [namer] (deprecated) + * [documentLoader(url, callback(err, remoteDoc))] the document loader. + * @param callback(err, nodeMap) called once the operation completes. + */ +jsonld.createNodeMap = function(input, options, callback) { + if(arguments.length < 1) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not create node map, too few arguments.')); + }); + } - exports.processors = processors; + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; - exports.ValidationError = (function(superClass) { - extend(ValidationError, superClass); + // set default options + if(!('base' in options)) { + options.base = (typeof input === 'string') ? input : ''; + } + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } - function ValidationError(message) { - this.message = message; + // expand input + jsonld.expand(input, options, function(err, _input) { + if(err) { + return callback(new JsonLdError( + 'Could not expand input before creating node map.', + 'jsonld.CreateNodeMapError', {cause: err})); } - return ValidationError; - - })(Error); - - exports.Builder = builder.Builder; - - exports.Parser = parser.Parser; - - exports.parseString = parser.parseString; - -}).call(this); + var nodeMap; + try { + nodeMap = new Processor().createNodeMap(_input, options); + } catch(ex) { + return callback(ex); + } -},{"./builder":22,"./defaults":23,"./parser":24,"./processors":25}],27:[function(_dereq_,module,exports){ -var ns = {}; -var Issue = function Issues() { - this.text = {}; - this.pattern = {}; - this.role = {}; -} -Issue.prototype.setText = function(text) { - this.text = text; -}; -Issue.prototype.getText = function() { - return this.text; -}; -Issue.prototype.setPattern = function(pattern) { - this.pattern = pattern; -}; -Issue.prototype.getPattern = function() { - return this.pattern; -}; -Issue.prototype.setRole = function(role) { - this.role = role; -}; -Issue.prototype.getPattern = function() { - return this.role; -}; -ns.Issue = Issue; -module.exports = ns; - -},{}],28:[function(_dereq_,module,exports){ -var N3 = _dereq_('n3'); - -var ns = {}; - -ns.prefixes = { rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - bqmodel: "http://biomodels.net/model-qualifiers/", - bqbiol: "http://biomodels.net/biology-qualifiers/", - sio: "http://semanticscience.org/resource/", - eisbm: "http://www.eisbm.org/"}; - -// pure shortcut function -ns.expandPrefix = function(prefix) { - return N3.Util.expandPrefixedName(prefix, ns.prefixes) + callback(null, nodeMap); + }); }; -// commonly used strings -str_sio223 = "sio:SIO_000223"; -str_sio223exp = ns.expandPrefix(str_sio223); -str_sio116 = "sio:SIO_000116"; -str_sio116exp = ns.expandPrefix(str_sio116); -str_rdfvalue = "rdf:value"; -str_rdfvalueexp = ns.expandPrefix(str_rdfvalue); -str_rdftype = "rdf:type"; -str_rdftypeexp = ns.expandPrefix(str_rdftype); -str_rdfbag = "rdf:Bag"; -str_rdfbagexp = ns.expandPrefix(str_rdfbag); +/** + * **Experimental** + * + * Merges two or more JSON-LD documents into a single flattened document. + * + * @param docs the JSON-LD documents to merge together. + * @param ctx the context to use to compact the merged result, or null. + * @param [options] the options to use: + * [base] the base IRI to use. + * [expandContext] a context to expand with. + * [issuer] a jsonld.IdentifierIssuer to use to label blank nodes. + * [namer] (deprecated). + * [mergeNodes] true to merge properties for nodes with the same ID, + * false to ignore new properties for nodes with the same ID once + * the ID has been defined; note that this may not prevent merging + * new properties where a node is in the `object` position + * (default: true). + * [documentLoader(url, callback(err, remoteDoc))] the document loader. + * @param callback(err, merged) called once the operation completes. + */ +jsonld.merge = function(docs, ctx, options, callback) { + if(arguments.length < 1) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not merge, too few arguments.')); + }); + } + if(!_isArray(docs)) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not merge, "docs" must be an array.')); + }); + } -controlledVocabularyList = [ - "bqmodel:is", - "bqmodel:isDerivedFrom", - "bqmodel:isDescribedBy", - "bqmodel:isInstanceOf", - "bqmodel:hasInstance", + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } else if(typeof ctx === 'function') { + callback = ctx; + ctx = null; + options = {}; + } + options = options || {}; - "bqbiol:is", - "bqbiol:encodes", - "bqbiol:hasPart", - "bqbiol:hasProperty", - "bqbiol:hasVersion", - "bqbiol:isDescribedBy", - "bqbiol:isEncodedBy", - "bqbiol:isHomologTo", - "bqbiol:isPartOf", - "bqbiol:isPropertyOf", - "bqbiol:isVersionOf", - "bqbiol:occursIn", - "bqbiol:hasTaxon", + // expand all documents + var expanded = []; + var error = null; + var count = docs.length; + for(var i = 0; i < docs.length; ++i) { + var opts = {}; + for(var key in options) { + opts[key] = options[key]; + } + jsonld.expand(docs[i], opts, expandComplete); + } - "sio:SIO_000223" -]; + function expandComplete(err, _input) { + if(error) { + return; + } + if(err) { + error = err; + return callback(new JsonLdError( + 'Could not expand input before flattening.', + 'jsonld.FlattenError', {cause: err})); + } + expanded.push(_input); + if(--count === 0) { + merge(expanded); + } + } -ns.isControlledVocabulary = {}; -for(var i=0; i 0; -}; + // add all non-default graphs to default graph + defaultGraph = _mergeNodeMaps(graphs); + } catch(ex) { + return callback(ex); + } -ns.countBagElements = function(graph, subject) { - return graph.countTriples(subject, null, null) - 1; -}; + // produce flattened output + var flattened = []; + var keys = Object.keys(defaultGraph).sort(); + for(var ki = 0; ki < keys.length; ++ki) { + var node = defaultGraph[keys[ki]]; + // only add full subjects to top-level + if(!_isSubjectReference(node)) { + flattened.push(node); + } + } -ns.getResourcesOfId = function(graph, id) { - var result = {}; - graph.forEach(function(init_triple){ // iterate over all id relationships - // we want everything that is not a simpel key/value property - if(init_triple.predicate != str_sio223exp) { - var relation = init_triple.predicate; - // initialize relation array if never encountered before - if(!result.hasOwnProperty(relation)) { - result[relation] = []; - } + if(ctx === null) { + return callback(null, flattened); + } - // if multiple resources specified, or a single element with several attributes, - // blank node is involved, possibly with a bag attribute - if(N3.Util.isBlank(init_triple.object)) { - var resourceContainer = init_triple.object; - graph.forEach(function(triple){ // iterate over the elements of the relationship - // relationship may be a bag, and thus contains undesirable rdf:type bag line - if(triple.object != str_rdfbagexp) { - var resource = triple.object; - result[relation].push(resource); - } - }, resourceContainer, null, null); - } - else { - // simple case, no bag, only 1 resource is linked with 1 attribute - var resource = init_triple.object; - result[relation].push(resource); - } - } - }, id, null, null); - return result; + // compact result (force @graph option to true, skip expansion) + options.graph = true; + options.skipExpansion = true; + jsonld.compact(flattened, ctx, options, function(err, compacted) { + if(err) { + return callback(new JsonLdError( + 'Could not compact merged output.', + 'jsonld.MergeError', {cause: err})); + } + callback(null, compacted); + }); + } }; /** - * returns the id of a newly created blank node representing the HasProperty predicate - * if one already exists, returns its id - * returns array, potentially several SIO223 present + * Relabels all blank nodes in the given JSON-LD input. + * + * @param input the JSON-LD input. + * @param [options] the options to use: + * [issuer] a jsonld.IdentifierIssuer to use to label blank nodes. + * [namer] (deprecated). */ -ns.getRelationship = function (graph, id, relationship) { - if (ns.hasRelationship(graph, id, relationship)) { - var object = graph.getObjects(id, relationship, null)[0]; // careful here - if (!N3.Util.isBlank(object)){ - // object of relationship isn't a bag. Need to turn it into a bag. - var newBag = ns.createBag(graph, id, relationship); - graph.addTriple(id, relationship, newBag); - graph.addTriple(newBag, ns.expandPrefix("rdf:_1"), object); - return [newBag]; - } - else { - return graph.getObjects(id, relationship, null); - } - } - else { - return [ns.createBag(graph, id, relationship)]; - } -}; - -ns.createBag = function (graph, id, relationship) { - var newBlank = graph.createBlankNode(); - graph.addTriple(id, ns.expandPrefix(relationship), newBlank); - graph.addTriple(newBlank, str_rdftypeexp, str_rdfbagexp); - return newBlank; +jsonld.relabelBlankNodes = function(input, options) { + options = options || {}; + var issuer = options.namer || options.issuer || new IdentifierIssuer('_:b'); + return _labelBlankNodes(issuer, input); }; /** - * kvobject contains biology qualifier as key and miriam resource as value + * Prepends a base IRI to the given relative IRI. + * + * @param base the base IRI. + * @param iri the relative IRI. + * + * @return the absolute IRI. */ -ns.addResource = function (graph, id, kvObject) { - for(var relation in kvObject) { - //console.log("relation", relation); - var relationElement = ns.getRelationship(graph, id, relation)[0]; // doesn't matter if more than one - //console.log("after get relation",relationElement, graph.getTriples(id, relation)); - //console.log("after get realtion", graph.getTriples()); - // using elemnt count as index may be dangerous if previous manipulation of - // the elements has happened. Like removing one. - var propIndex = ns.countBagElements(graph, relationElement) + 1; - //console.log("elements in bag:", propIndex); - //console.log("new blank node", graph.getTriples()); - //console.log("Will add", relationElement, ns.expandPrefix("rdf:_"+propIndex), kvObject[relation]); - graph.addTriple(relationElement, ns.expandPrefix("rdf:_"+propIndex), kvObject[relation]); - //console.log("end result", graph.getTriples()); - //console.log("added", relation, kvObject[relation]); - } +jsonld.prependBase = function(base, iri) { + return _prependBase(base, iri); }; -module.exports = ns; -},{"n3":34}],29:[function(_dereq_,module,exports){ /** - * This submodule manages the annotations extension. It adds the ability to save semantic data into - * SBGN-ML in the form of RDF elements. Any SBGN element that can host an extension tag can also - * get RDF annotations. This means that almost every element can get annotated. - * - * The annotations here are intended to be used in two ways: - * - with controlled vocabulary and resources, as suggested by COMBINE, with the help of MIRIAM - * identifiers. - * - as a mean to attach arbitrary data in the form of key-value properties. - * - * # Controlled vocabulary - * - * The formal way of using annotations is to use specific vocabulary with specific identifiers to - * provide additional information that can not be conveyed otherwise through the SBGN format. - * See --link to combine qualifiers-- and --link to identifiers.org and MIRIAM--- - * This was also based on the annotation extension of SBML --link to annotation proposal for SBML-- - * - * - * See {@link Extension} for more general information on extensions in the SBGN-ML format. + * The default document loader for external documents. If the environment + * is node.js, a callback-continuation-style document loader is used; otherwise, + * a promises-style document loader is used. * - * You can access the following classes like this: libsbgn.annot.Annotation + * @param url the URL to load. + * @param callback(err, remoteDoc) called once the operation completes, + * if using a non-promises API. * - * @module libsbgn-annotations - * @namespace libsbgn.annot -*/ - -var checkParams = _dereq_('./utilities').checkParams; -var $rdf = _dereq_('rdflib'); -var N3 = _dereq_('n3'); -var Util = _dereq_('./annotation-utils'); -var utils = _dereq_('./utilities'); - -var ns = {}; - -//ns.xmlns = "http://www.sbml.org/sbml/level3/version1/render/version1"; - -// ------- ANNOTATION ------- -/** - * Represents the <annotation> element. - * @class - * @param {Object} params - * @param {RdfElement=} params.rdfElement + * @return a promise, if using a promises API. */ -var Annotation = function (params) { - var params = checkParams(params, ['rdfElement']); - this.rdfElement = params.rdfElement; +jsonld.documentLoader = function(url, callback) { + var err = new JsonLdError( + 'Could not retrieve a JSON-LD document from the URL. URL ' + + 'dereferencing not implemented.', 'jsonld.LoadDocumentError', + {code: 'loading document failed'}); + if(_nodejs) { + return callback(err, {contextUrl: null, documentUrl: url, document: null}); + } + return jsonld.promisify(function(callback) { + callback(err); + }); }; /** - * @param {RdfElement} rdfElement + * Deprecated default document loader. Use or override jsonld.documentLoader + * instead. */ -Annotation.prototype.setRdfElement = function(rdfElement) { - this.rdfElement = rdfElement; +jsonld.loadDocument = function(url, callback) { + var promise = jsonld.documentLoader(url, callback); + if(promise && 'then' in promise) { + promise.then(callback.bind(null, null), callback); + } }; -Annotation.prototype.buildJsObj = function () { - var annotationJsonObj = {}; - - if(this.rdfElement != null) { - annotationJsonObj = this.rdfElement.buildJsObj(); - } - - return annotationJsonObj; -}; +/* Promises API */ /** - * @return {string} + * Creates a new promises API object. + * + * @param [options] the options to use: + * [api] an object to attach the API to. + * [version] 'json-ld-1.0' to output a standard JSON-LD 1.0 promises + * API, 'jsonld.js' to output the same with augmented proprietary + * methods (default: 'jsonld.js') + * + * @return the promises API object. */ -Annotation.prototype.toXML = function() { - return utils.buildString({annotation: this.buildJsObj()}) -}; - -Annotation.fromXML = function (string) { - var annotation; - function fn (err, result) { - annotation = Annotation.fromObj(result); - }; - utils.parseStringKeepPrefix(string, fn); - return annotation; -}; - -Annotation.fromObj = function (jsObj) { - if (typeof jsObj.annotation == 'undefined') { - throw new Error("Bad XML provided, expected tagName annotation, got: " + Object.keys(jsObj)[0]); - } - - var annotation = new ns.Annotation(); - jsObj = jsObj.annotation; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return annotation; - } - - // children - if(jsObj['rdf:RDF']) { - var obj = {}; - obj['rdf:RDF'] = jsObj['rdf:RDF'][0]; - var rdf = ns.RdfElement.fromObj(obj); - annotation.setRdfElement(rdf); - } +jsonld.promises = function(options) { + options = options || {}; + var slice = Array.prototype.slice; + var promisify = jsonld.promisify; - return annotation; -}; + // handle 'api' option as version, set defaults + var api = options.api || {}; + var version = options.version || 'jsonld.js'; + if(typeof options.api === 'string') { + if(!options.version) { + version = options.api; + } + api = {}; + } -ns.Annotation = Annotation; -// ------- END ANNOTATION ------- + // The Web IDL test harness will check the number of parameters defined in + // the functions below. The number of parameters must exactly match the + // required (non-optional) parameters of the JsonLdProcessor interface as + // defined here: + // https://www.w3.org/TR/json-ld-api/#the-jsonldprocessor-interface -// ------- STOREOBJECT ------- -var StoreObject = function (params) { - var params = checkParams(params, ['store']); - if (params.store) { - this.store = params.store; - } - else { - var store = N3.Store(); - store.addPrefixes(Util.prefixes); - this.store = store; - } -}; - -StoreObject.prototype.getCustomPropertiesOfId = function (id) { - return Util.getCustomPropertiesOfId(this.store, id); -}; - -StoreObject.prototype.getAllIds = function () { - return Util.getAllIds(this.store); -}; - -StoreObject.prototype.addCustomProperty = function (id, kvObject) { - return Util.addCustomProperty(this.store, id, kvObject); -}; - -StoreObject.prototype.getResourcesOfId = function(id) { - return Util.getResourcesOfId(this.store, id); -}; - -StoreObject.prototype.addResource = function (id, kvObject) { - return Util.addResource(this.store, id, kvObject); -}; - -ns.StoreObject = StoreObject; -// ------- END STOREOBJECT ------- - -// ------- GLOBALSTORE ------- -var GlobalRdfStore = function (params) { - ns.StoreObject.call(this, params); -}; -GlobalRdfStore.prototype = Object.create(ns.StoreObject.prototype); -GlobalRdfStore.prototype.constructor = GlobalRdfStore; - -GlobalRdfStore.prototype.load = function (annotations) { - for(var i=0; i<rd:RDFf> element. - * @class - */ -var RdfElement = function (params) { - ns.StoreObject.call(this, params); + return api; }; -RdfElement.prototype = Object.create(ns.StoreObject.prototype); -RdfElement.prototype.constructor = RdfElement; - -RdfElement.uri = 'http://www.eisbm.org/'; /** - * @return {string} + * Converts a node.js async op into a promise w/boxed resolved value(s). + * + * @param op the operation to convert. + * + * @return the promise. */ -RdfElement.prototype.toXML = function() { - /* - Add some functions to the writer object of N3 - Those functions will allow us to serialize triples synchronously. - Without it, we would be forced to use the asynchronous functions. - */ - function addSimpleWrite (writer) { - // replicates the writer._write function but returns a string - writer.simpleWriteTriple = function (subject, predicate, object, graph) { - return this._encodeIriOrBlankNode(subject) + ' ' + - this._encodeIriOrBlankNode(predicate) + ' ' + - this._encodeObject(object) + - (graph ? ' ' + this._encodeIriOrBlankNode(graph) + '.\n' : '.\n') - }; - // allows to provide an array of triples and concatenate their serialized strings - writer.simpleWriteTriples = function (array) { - var stringN3 = ''; - for (var i=0; i[\s\S]*?<(\w+):SIO_000116>([\s\S]*?)<\/\2:SIO_000116>[\s\S]*?([\s\S]*?)<\/rdf:value>[\s\S]*?<\/rdf:li>/g; - var result = string.replace(regexpLi, ''); - return result; - } - - function replaceBag(string) { - // regexp will spot a transformed bag and capture its content - var regexpBag = /(([\s\S]*?)[\s\S]*?<\/rdf:Description>)/g; - var result1 = string.replace(regexpBag, '$2'); - var result2 = result1.replace(/ <\/rdf:Bag>/g, ''); - return result2; - } - - function replaceParseType(string) { - var regexp = / rdf:parseType="Resource"/g; - return string.replace(regexp, ''); - } - - function replaceSlashInID(string) { - return string.replace(new RegExp(/rdf:about="\//g), 'rdf:about="'); - } - - var result = replaceSlashInID(replaceParseType(replaceLi(replaceBag(serialize)))); - - return result; +jsonld.promisify = function(op) { + if(!jsonld.Promise) { + try { + jsonld.Promise = global.Promise || _dereq_('es6-promise').Promise; + } catch(e) { + throw new Error('Unable to find a Promise implementation.'); + } + } + var args = Array.prototype.slice.call(arguments, 1); + return new jsonld.Promise(function(resolve, reject) { + op.apply(null, args.concat(function(err, value) { + if(!err) { + resolve(value); + } else { + reject(err); + } + })); + }); }; -/** - * @param {Element} xml - * @return {RdfElement} - */ -RdfElement.fromString = function (stringXml) { - - var rdfElement = new RdfElement(); - var graph = $rdf.graph(); - - // rdflib only accepts string as input, not xml elements - try { - $rdf.parse(stringXml, graph, RdfElement.uri, 'application/rdf+xml'); - } catch (err) { - console.log(err); - } - - // convert to turtle to feed to N3 - var turtle = $rdf.serialize($rdf.sym(RdfElement.uri), graph, undefined, 'text/turtle'); +// extend jsonld.promises w/jsonld.js methods +jsonld.promises({api: jsonld.promises}); - var parser = N3.Parser(); - var store = N3.Store(); - store.addPrefixes(Util.prefixes); - store.addTriples(parser.parse(turtle)); - - rdfElement.store = store; +/* WebIDL API */ - return rdfElement; +function JsonLdProcessor() {} +JsonLdProcessor.prototype = jsonld.promises({version: 'json-ld-1.0'}); +JsonLdProcessor.prototype.toString = function() { + if(this instanceof JsonLdProcessor) { + return '[object JsonLdProcessor]'; + } + return '[object JsonLdProcessorPrototype]'; }; +jsonld.JsonLdProcessor = JsonLdProcessor; -RdfElement.fromXML = function (string) { - var rdfElement; - function fn (err, result) { - rdfElement = RdfElement.fromObj(result); - }; - utils.parseStringKeepPrefix(string, fn); - return rdfElement; -}; +// IE8 has Object.defineProperty but it only +// works on DOM nodes -- so feature detection +// requires try/catch :-( +var canDefineProperty = !!Object.defineProperty; +if(canDefineProperty) { + try { + Object.defineProperty({}, 'x', {}); + } catch(e) { + canDefineProperty = false; + } +} -RdfElement.prototype.buildJsObj = function () { - var rdfElementJsObj; - function fn (err, result) { - rdfElementJsObj = result; - }; - utils.parseStringKeepPrefix(this.toXML(), fn); - return rdfElementJsObj; -}; +if(canDefineProperty) { + Object.defineProperty(JsonLdProcessor, 'prototype', { + writable: false, + enumerable: false + }); + Object.defineProperty(JsonLdProcessor.prototype, 'constructor', { + writable: true, + enumerable: false, + configurable: true, + value: JsonLdProcessor + }); +} -RdfElement.fromObj = function (jsObj) { - if (typeof jsObj['rdf:RDF'] == 'undefined') { - throw new Error("Bad XML provided, expected tagName rdf:RDF, got: " + Object.keys(jsObj)[0]); - } +// setup browser global JsonLdProcessor +if(_browser && typeof global.JsonLdProcessor === 'undefined') { + if(canDefineProperty) { + Object.defineProperty(global, 'JsonLdProcessor', { + writable: true, + enumerable: false, + configurable: true, + value: JsonLdProcessor + }); + } else { + global.JsonLdProcessor = JsonLdProcessor; + } +} - var rdfElement = new ns.RdfElement(); - jsObj = jsObj['rdf:RDF']; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return rdfElement; - } +/* Utility API */ - var obj = {}; - obj['rdf:RDF'] = jsObj; - rdfElement = ns.RdfElement.fromString(utils.buildString(obj)); +// define setImmediate and nextTick +//// nextTick implementation with browser-compatible fallback //// +// from https://github.com/caolan/async/blob/master/lib/async.js - return rdfElement; -}; +// capture the global reference to guard against fakeTimer mocks +var _setImmediate = typeof setImmediate === 'function' && setImmediate; -RdfElement.prototype.test = function() { - //console.log(this.store); - //console.log(this.store.getTriples("http://local/anID000001", null, null)); - console.log("expand prefix shortcut", Util.expandPrefix("sio:SIO_000116")); - console.log("all properties of id", this.getCustomPropertiesOfId("http://local/anID000001")); - console.log("all ids", this.getAllIds()); +var _delay = _setImmediate ? function(fn) { + // not a direct alias (for IE10 compatibility) + _setImmediate(fn); +} : function(fn) { + setTimeout(fn, 0); }; -ns.RdfElement = RdfElement; -// ------- END RDFELEMENT ------- - - -ns.rdflib = $rdf; -ns.Util = Util; - -module.exports = ns; +if(typeof process === 'object' && typeof process.nextTick === 'function') { + jsonld.nextTick = process.nextTick; +} else { + jsonld.nextTick = _delay; +} +jsonld.setImmediate = _setImmediate ? _delay : jsonld.nextTick; -},{"./annotation-utils":28,"./utilities":33,"n3":34,"rdflib":60}],30:[function(_dereq_,module,exports){ /** - * This submodule contains the classes to manage the render extension's xml and some utility functions. - * It adds the ability to save the styles and colors used in an SBGN map, as features like background-color, - * border thickness or font properties are not part of the SBGN standard. + * Parses a link header. The results will be key'd by the value of "rel". * - * It is loosely based on the {@link http://sbml.org/Documents/Specifications/SBML_Level_3/Packages/render|render extension of the SBML format}. - * A subset of this specification has been adapted for SBGN-ML integration. + * Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" * - * See {@link Extension} for more general information on extensions in the SBGN-ML format. + * Parses as: { + * 'http://www.w3.org/ns/json-ld#context': { + * target: http://json-ld.org/contexts/person.jsonld, + * type: 'application/ld+json' + * } + * } * - * You can access the following classes like this: libsbgn.render.ColorDefinition + * If there is more than one "rel" with the same IRI, then entries in the + * resulting map for that "rel" will be arrays. * - * @module libsbgn-render - * @namespace libsbgn.render -*/ - -var utils = _dereq_('./utilities'); -var checkParams = utils.checkParams; -var xml2js = _dereq_('xml2js'); - -var ns = {}; - -ns.xmlns = "http://www.sbml.org/sbml/level3/version1/render/version1"; - -// ------- COLORDEFINITION ------- -/** - * Represents the <colorDefinition> element. - * @class - * @param {Object} params - * @param {string=} params.id - * @param {string=} params.value + * @param header the link header to parse. */ -var ColorDefinition = function(params) { - var params = checkParams(params, ['id', 'value']); - this.id = params.id; - this.value = params.value; +jsonld.parseLinkHeader = function(header) { + var rval = {}; + // split on unbracketed/unquoted commas + var entries = header.match(/(?:<[^>]*?>|"[^"]*?"|[^,])+/g); + var rLinkHeader = /\s*<([^>]*?)>\s*(?:;\s*(.*))?/; + for(var i = 0; i < entries.length; ++i) { + var match = entries[i].match(rLinkHeader); + if(!match) { + continue; + } + var result = {target: match[1]}; + var params = match[2]; + var rParams = /(.*?)=(?:(?:"([^"]*?)")|([^"]*?))\s*(?:(?:;\s*)|$)/g; + while(match = rParams.exec(params)) { + result[match[1]] = (match[2] === undefined) ? match[3] : match[2]; + } + var rel = result['rel'] || ''; + if(_isArray(rval[rel])) { + rval[rel].push(result); + } else if(rel in rval) { + rval[rel] = [rval[rel], result]; + } else { + rval[rel] = result; + } + } + return rval; }; /** - * @return {Object} - xml2js formatted object + * Creates a simple queue for requesting documents. */ -ColorDefinition.prototype.buildJsObj = function () { - var colordefObj = {}; - - // attributes - var attributes = {}; - if(this.id != null) { - attributes.id = this.id; - } - if(this.value != null) { - attributes.value = this.value; - } - utils.addAttributes(colordefObj, attributes); - return colordefObj; +jsonld.RequestQueue = function() { + this._requests = {}; }; - -/** - * @return {string} - */ -ColorDefinition.prototype.toXML = function () { - return utils.buildString({colorDefinition: this.buildJsObj()}) +jsonld.RequestQueue.prototype.wrapLoader = function(loader) { + this._loader = loader; + this._usePromise = (loader.length === 1); + return this.add.bind(this); }; +jsonld.RequestQueue.prototype.add = function(url, callback) { + var self = this; -/** - * @param {String} string - * @return {ColorDefinition} - */ -ColorDefinition.fromXML = function (string) { - var colorDefinition; - function fn (err, result) { - colorDefinition = ColorDefinition.fromObj(result); - }; - utils.parseString(string, fn); - return colorDefinition; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {ColorDefinition} - */ -ColorDefinition.fromObj = function (jsObj) { - var colorDefinitionNode = utils.getChildByNameIgnoringNamespace(jsObj, "colorDefinition"); - - if (colorDefinitionNode === null) { - throw new Error("Bad XML provided, expected tagName colorDefinition, got: " + Object.keys(jsObj)[0]); - } + // callback must be given if not using promises + if(!callback && !self._usePromise) { + throw new Error('callback must be specified.'); + } - var colorDefinition = new ns.ColorDefinition(); - jsObj = colorDefinitionNode; - if (typeof jsObj != 'object') { // nothing inside, empty xml - return colorDefinition; - } + // Promise-based API + if(self._usePromise) { + return new jsonld.Promise(function(resolve, reject) { + var load = self._requests[url]; + if(!load) { + // load URL then remove from queue + load = self._requests[url] = self._loader(url) + .then(function(remoteDoc) { + delete self._requests[url]; + return remoteDoc; + }).catch(function(err) { + delete self._requests[url]; + throw err; + }); + } + // resolve/reject promise once URL has been loaded + load.then(function(remoteDoc) { + resolve(remoteDoc); + }).catch(function(err) { + reject(err); + }); + }); + } - if (jsObj.$) { // we have some attributes - var attributes = jsObj.$; - colorDefinition.id = utils.getChildByNameIgnoringNamespace(attributes, "id");; - colorDefinition.value = utils.getChildByNameIgnoringNamespace(attributes, "value"); - } - return colorDefinition; + // callback-based API + if(url in self._requests) { + self._requests[url].push(callback); + } else { + self._requests[url] = [callback]; + self._loader(url, function(err, remoteDoc) { + var callbacks = self._requests[url]; + delete self._requests[url]; + for(var i = 0; i < callbacks.length; ++i) { + callbacks[i](err, remoteDoc); + } + }); + } }; -ns.ColorDefinition = ColorDefinition; -// ------- END COLORDEFINITION ------- - -// ------- LISTOFCOLORDEFINITIONS ------- /** - * Represents the <listOfColorDefinitions> element. - * @class + * Creates a simple document cache that retains documents for a short + * period of time. + * + * FIXME: Implement simple HTTP caching instead. + * + * @param size the maximum size of the cache. */ -var ListOfColorDefinitions = function () { - this.colorDefinitions = []; - this.colorIndex = {}; +jsonld.DocumentCache = function(size) { + this.order = []; + this.cache = {}; + this.size = size || 50; + this.expires = 30 * 1000; +}; +jsonld.DocumentCache.prototype.get = function(url) { + if(url in this.cache) { + var entry = this.cache[url]; + if(entry.expires >= +new Date()) { + return entry.ctx; + } + delete this.cache[url]; + this.order.splice(this.order.indexOf(url), 1); + } + return null; +}; +jsonld.DocumentCache.prototype.set = function(url, ctx) { + if(this.order.length === this.size) { + delete this.cache[this.order.shift()]; + } + this.order.push(url); + this.cache[url] = {ctx: ctx, expires: (+new Date() + this.expires)}; }; /** - * @param {ColorDefinition} colorDefinition + * Creates an active context cache. + * + * @param size the maximum size of the cache. */ -ListOfColorDefinitions.prototype.addColorDefinition = function (colorDefinition) { - this.colorDefinitions.push(colorDefinition); - this.colorIndex[colorDefinition.id] = colorDefinition.value; +jsonld.ActiveContextCache = function(size) { + this.order = []; + this.cache = {}; + this.size = size || 100; +}; +jsonld.ActiveContextCache.prototype.get = function(activeCtx, localCtx) { + var key1 = JSON.stringify(activeCtx); + var key2 = JSON.stringify(localCtx); + var level1 = this.cache[key1]; + if(level1 && key2 in level1) { + return level1[key2]; + } + return null; +}; +jsonld.ActiveContextCache.prototype.set = function( + activeCtx, localCtx, result) { + if(this.order.length === this.size) { + var entry = this.order.shift(); + delete this.cache[entry.activeCtx][entry.localCtx]; + } + var key1 = JSON.stringify(activeCtx); + var key2 = JSON.stringify(localCtx); + this.order.push({activeCtx: key1, localCtx: key2}); + if(!(key1 in this.cache)) { + this.cache[key1] = {}; + } + this.cache[key1][key2] = _clone(result); }; /** - * Convenient method to get a color value directly. - * @param {string} id - * @return {string} + * Default JSON-LD cache. */ -ListOfColorDefinitions.prototype.getColorById = function (id) { - return this.colorIndex[id]; +jsonld.cache = { + activeCtx: new jsonld.ActiveContextCache() }; /** - * Convenient method to get all the color values in the list. - * @return {string[]} + * Document loaders. */ -ListOfColorDefinitions.prototype.getAllColors = function () { - return Object.values(this.colorIndex); -}; +jsonld.documentLoaders = {}; /** - * @return {Object} - xml2js formatted object + * Creates a built-in jquery document loader. + * + * @param $ the jquery instance to use. + * @param options the options to use: + * secure: require all URLs to use HTTPS. + * usePromise: true to use a promises API, false for a + * callback-continuation-style API; defaults to true if Promise + * is globally defined, false if not. + * + * @return the jquery document loader. */ -ListOfColorDefinitions.prototype.buildJsObj = function () { - var listOfColorDefinitionsObj = {}; +jsonld.documentLoaders.jquery = function($, options) { + options = options || {}; + var queue = new jsonld.RequestQueue(); - for(var i=0; i < this.colorDefinitions.length; i++) { - if (i==0) { - listOfColorDefinitionsObj.colorDefinition = []; - } - listOfColorDefinitionsObj.colorDefinition.push(this.colorDefinitions[i].buildJsObj()); - } + // use option or, by default, use Promise when its defined + var usePromise = ('usePromise' in options ? + options.usePromise : (typeof Promise !== 'undefined')); + if(usePromise) { + return queue.wrapLoader(function(url) { + return jsonld.promisify(loader, url); + }); + } + return queue.wrapLoader(loader); - return listOfColorDefinitionsObj; -}; + function loader(url, callback) { + if(url.indexOf('http:') !== 0 && url.indexOf('https:') !== 0) { + return callback(new JsonLdError( + 'URL could not be dereferenced; only "http" and "https" URLs are ' + + 'supported.', + 'jsonld.InvalidUrl', {code: 'loading document failed', url: url}), + {contextUrl: null, documentUrl: url, document: null}); + } + if(options.secure && url.indexOf('https') !== 0) { + return callback(new JsonLdError( + 'URL could not be dereferenced; secure mode is enabled and ' + + 'the URL\'s scheme is not "https".', + 'jsonld.InvalidUrl', {code: 'loading document failed', url: url}), + {contextUrl: null, documentUrl: url, document: null}); + } + $.ajax({ + url: url, + accepts: { + json: 'application/ld+json, application/json' + }, + // ensure Accept header is very specific for JSON-LD/JSON + headers: { + 'Accept': 'application/ld+json, application/json' + }, + dataType: 'json', + crossDomain: true, + success: function(data, textStatus, jqXHR) { + var doc = {contextUrl: null, documentUrl: url, document: data}; -/** - * @return {string} - */ -ListOfColorDefinitions.prototype.toXML = function () { - return utils.buildString({listOfColorDefinitions: this.buildJsObj()}) -}; + // handle Link Header + var contentType = jqXHR.getResponseHeader('Content-Type'); + var linkHeader = jqXHR.getResponseHeader('Link'); + if(linkHeader && contentType !== 'application/ld+json') { + // only 1 related link header permitted + linkHeader = jsonld.parseLinkHeader(linkHeader)[LINK_HEADER_REL]; + if(_isArray(linkHeader)) { + return callback(new JsonLdError( + 'URL could not be dereferenced, it has more than one ' + + 'associated HTTP Link Header.', + 'jsonld.InvalidUrl', + {code: 'multiple context link headers', url: url}), doc); + } + if(linkHeader) { + doc.contextUrl = linkHeader.target; + } + } -/** - * @param {String} string - * @return {ListOfColorDefinitions} - */ -ListOfColorDefinitions.fromXML = function (string) { - var listOfColorDefinitions; - function fn (err, result) { - listOfColorDefinitions = ListOfColorDefinitions.fromObj(result); - }; - utils.parseString(string, fn); - return listOfColorDefinitions; + callback(null, doc); + }, + error: function(jqXHR, textStatus, err) { + callback(new JsonLdError( + 'URL could not be dereferenced, an error occurred.', + 'jsonld.LoadDocumentError', + {code: 'loading document failed', url: url, cause: err}), + {contextUrl: null, documentUrl: url, document: null}); + } + }); + } }; /** - * @param {Object} jsObj - xml2js formatted object - * @return {ListOfColorDefinitions} + * Creates a built-in node document loader. + * + * @param options the options to use: + * secure: require all URLs to use HTTPS. + * strictSSL: true to require SSL certificates to be valid, + * false not to (default: true). + * maxRedirects: the maximum number of redirects to permit, none by + * default. + * request: the object which will make the request, default is + * provided by `https://www.npmjs.com/package/request`. + * headers: an array of headers which will be passed as request + * headers for the requested document. Accept is not allowed. + * usePromise: true to use a promises API, false for a + * callback-continuation-style API; false by default. + * + * @return the node document loader. */ -ListOfColorDefinitions.fromObj = function (jsObj) { - var listOfColorDefinitionsNode = utils.getChildByNameIgnoringNamespace(jsObj, "listOfColorDefinitions"); - if (listOfColorDefinitionsNode === null) { - throw new Error("Bad XML provided, expected tagName listOfColorDefinitions, got: " + Object.keys(jsObj)[0]); - } +jsonld.documentLoaders.node = function(options) { + options = options || {}; + var strictSSL = ('strictSSL' in options) ? options.strictSSL : true; + var maxRedirects = ('maxRedirects' in options) ? options.maxRedirects : -1; + var request = ('request' in options) ? options.request : _dereq_('request'); + var acceptHeader = 'application/ld+json, application/json'; + var http = _dereq_('http'); + // TODO: disable cache until HTTP caching implemented + //var cache = new jsonld.DocumentCache(); - var listOfColorDefinitions = new ns.ListOfColorDefinitions(); - jsObj = listOfColorDefinitionsNode; - if (typeof jsObj != 'object') { // nothing inside, empty xml - return listOfColorDefinitions; - } + var queue = new jsonld.RequestQueue(); + if(options.usePromise) { + return queue.wrapLoader(function(url) { + return jsonld.promisify(loadDocument, url, []); + }); + } + var headers = options.headers || {}; + if('Accept' in headers || 'accept' in headers) { + throw new RangeError( + 'Accept header may not be specified as an option; only "' + + acceptHeader + '" is supported.'); + } + return queue.wrapLoader(function(url, callback) { + loadDocument(url, [], callback); + }); - // children - var colorDefinitionNode = utils.getChildByNameIgnoringNamespace(jsObj, "colorDefinition"); - if (colorDefinitionNode) { - var colorDefinitions = colorDefinitionNode; - for (var i=0; i < colorDefinitions.length; i++) { - var colorDefinition = ns.ColorDefinition.fromObj({colorDefinition: colorDefinitions[i]}); - listOfColorDefinitions.addColorDefinition(colorDefinition); - } - } + function loadDocument(url, redirects, callback) { + if(url.indexOf('http:') !== 0 && url.indexOf('https:') !== 0) { + return callback(new JsonLdError( + 'URL could not be dereferenced; only "http" and "https" URLs are ' + + 'supported.', + 'jsonld.InvalidUrl', {code: 'loading document failed', url: url}), + {contextUrl: null, documentUrl: url, document: null}); + } + if(options.secure && url.indexOf('https') !== 0) { + return callback(new JsonLdError( + 'URL could not be dereferenced; secure mode is enabled and ' + + 'the URL\'s scheme is not "https".', + 'jsonld.InvalidUrl', {code: 'loading document failed', url: url}), + {contextUrl: null, documentUrl: url, document: null}); + } + // TODO: disable cache until HTTP caching implemented + var doc = null;//cache.get(url); + if(doc !== null) { + return callback(null, doc); + } + var headers = {'Accept': acceptHeader}; + for(var k in options.headers) { headers[k] = options.headers[k]; } + request({ + url: url, + headers: headers, + strictSSL: strictSSL, + followRedirect: false + }, handleResponse); - return listOfColorDefinitions; -}; + function handleResponse(err, res, body) { + doc = {contextUrl: null, documentUrl: url, document: body || null}; -ns.ListOfColorDefinitions = ListOfColorDefinitions; -// ------- END LISTOFCOLORDEFINITIONS ------- + // handle error + if(err) { + return callback(new JsonLdError( + 'URL could not be dereferenced, an error occurred.', + 'jsonld.LoadDocumentError', + {code: 'loading document failed', url: url, cause: err}), doc); + } + var statusText = http.STATUS_CODES[res.statusCode]; + if(res.statusCode >= 400) { + return callback(new JsonLdError( + 'URL could not be dereferenced: ' + statusText, + 'jsonld.InvalidUrl', { + code: 'loading document failed', + url: url, + httpStatusCode: res.statusCode + }), doc); + } -// ------- RENDERGROUP ------- -/** - * Represents the <g> element. - * @class - * @param {Object} params - * @param {string=} params.id - * @param {string=} params.fontSize - * @param {string=} params.fontFamily - * @param {string=} params.fontWeight - * @param {string=} params.fontStyle - * @param {string=} params.fontColor - * @param {string=} params.textAnchor - * @param {string=} params.vtextAnchor - * @param {string=} params.fill The element's background color - * @param {string=} params.stroke Border color for glyphs, line color for arcs. - * @param {string=} params.strokeWidth - * @param {string=} params.backgroundImage - * @param {string=} params.backgroundFit - * @param {string=} params.backgroundPosX - * @param {string=} params.backgroundPosY - * @param {string=} params.backgroundWidth - * @param {string=} params.backgroundHeight - * @param {string=} params.backgroundImageOpacity - * @param {string=} params.backgroundOpacity - */ -var RenderGroup = function (params) { - // each of those are optional, so test if it is defined is mandatory - var params = checkParams(params, ['fontSize', 'fontFamily', 'fontWeight', - 'fontStyle', 'fontColor', 'textAnchor', 'vtextAnchor', 'fill', 'id', 'stroke', 'strokeWidth', 'backgroundImage', - 'backgroundFit', 'backgroundPosX', 'backgroundPosY', 'backgroundWidth', 'backgroundHeight', 'backgroundImageOpacity','backgroundOpacity' ]); - // specific to renderGroup - this.fontSize = params.fontSize; - this.fontFamily = params.fontFamily; - this.fontWeight = params.fontWeight; - this.fontStyle = params.fontStyle; - this.fontColor = params.fontColor; - this.textAnchor = params.textAnchor; // probably useless - this.vtextAnchor = params.vtextAnchor; // probably useless - // from GraphicalPrimitive2D - this.fill = params.fill; // fill color - // from GraphicalPrimitive1D - this.id = params.id; - this.stroke = params.stroke; // stroke color - this.strokeWidth = params.strokeWidth; - this.backgroundImage = params.backgroundImage; - this.backgroundFit = params.backgroundFit; - this.backgroundPosX = params.backgroundPosX; - this.backgroundPosY = params.backgroundPosY; - this.backgroundWidth = params.backgroundWidth; - this.backgroundHeight = params.backgroundHeight; - this.backgroundImageOpacity = params.backgroundImageOpacity; - this.backgroundOpacity = params.backgroundOpacity; + // handle Link Header + if(res.headers.link && + res.headers['content-type'] !== 'application/ld+json') { + // only 1 related link header permitted + var linkHeader = jsonld.parseLinkHeader( + res.headers.link)[LINK_HEADER_REL]; + if(_isArray(linkHeader)) { + return callback(new JsonLdError( + 'URL could not be dereferenced, it has more than one associated ' + + 'HTTP Link Header.', + 'jsonld.InvalidUrl', + {code: 'multiple context link headers', url: url}), doc); + } + if(linkHeader) { + doc.contextUrl = linkHeader.target; + } + } + + // handle redirect + if(res.statusCode >= 300 && res.statusCode < 400 && + res.headers.location) { + if(redirects.length === maxRedirects) { + return callback(new JsonLdError( + 'URL could not be dereferenced; there were too many redirects.', + 'jsonld.TooManyRedirects', { + code: 'loading document failed', + url: url, + httpStatusCode: res.statusCode, + redirects: redirects + }), doc); + } + if(redirects.indexOf(url) !== -1) { + return callback(new JsonLdError( + 'URL could not be dereferenced; infinite redirection was detected.', + 'jsonld.InfiniteRedirectDetected', { + code: 'recursive context inclusion', + url: url, + httpStatusCode: res.statusCode, + redirects: redirects + }), doc); + } + redirects.push(url); + return loadDocument(res.headers.location, redirects, callback); + } + // cache for each redirected URL + redirects.push(url); + // TODO: disable cache until HTTP caching implemented + /*for(var i = 0; i < redirects.length; ++i) { + cache.set( + redirects[i], + {contextUrl: null, documentUrl: redirects[i], document: body}); + }*/ + callback(err, doc); + } + } }; /** - * @return {Object} - xml2js formatted object + * Creates a built-in XMLHttpRequest document loader. + * + * @param options the options to use: + * secure: require all URLs to use HTTPS. + * usePromise: true to use a promises API, false for a + * callback-continuation-style API; defaults to true if Promise + * is globally defined, false if not. + * [xhr]: the XMLHttpRequest API to use. + * + * @return the XMLHttpRequest document loader. */ -RenderGroup.prototype.buildJsObj = function () { - var renderGroupObj = {}; +jsonld.documentLoaders.xhr = function(options) { + options = options || {}; + var rlink = /(^|(\r\n))link:/i; + var queue = new jsonld.RequestQueue(); - // attributes - var attributes = {}; - if(this.id != null) { - attributes.id = this.id; - } - if(this.fontSize != null) { - attributes.fontSize = this.fontSize; - } - if(this.fontFamily != null) { - attributes.fontFamily = this.fontFamily; - } - if(this.fontWeight != null) { - attributes.fontWeight = this.fontWeight; - } - if(this.fontStyle != null) { - attributes.fontStyle = this.fontStyle; - } - if (this.fontColor != null) { - attributes.fontColor = this.fontColor; - } - if(this.textAnchor != null) { - attributes.textAnchor = this.textAnchor; - } - if(this.vtextAnchor != null) { - attributes.vtextAnchor = this.vtextAnchor; - } - if(this.stroke != null) { - attributes.stroke = this.stroke; - } - if(this.strokeWidth != null) { - attributes.strokeWidth = this.strokeWidth; - } - if(this.fill != null) { - attributes.fill = this.fill; - } - if(this.backgroundImage != null) { - attributes.backgroundImage = this.backgroundImage; - } - if(this.backgroundFit != null) { - attributes.backgroundFit = this.backgroundFit; - } - if(this.backgroundPosX != null) { - attributes.backgroundPosX = this.backgroundPosX; - } - if(this.backgroundPosY != null) { - attributes.backgroundPosY = this.backgroundPosY; - } - if(this.backgroundWidth != null) { - attributes.backgroundWidth = this.backgroundWidth; - } - if(this.backgroundHeight != null) { - attributes.backgroundHeight = this.backgroundHeight; - } - if(this.backgroundImageOpacity != null) { - attributes.backgroundImageOpacity = this.backgroundImageOpacity; - } - if(this.backgroundOpacity != null) { - attributes.backgroundOpacity = this.backgroundOpacity; - } - utils.addAttributes(renderGroupObj, attributes); - return renderGroupObj; -}; + // use option or, by default, use Promise when its defined + var usePromise = ('usePromise' in options ? + options.usePromise : (typeof Promise !== 'undefined')); + if(usePromise) { + return queue.wrapLoader(function(url) { + return jsonld.promisify(loader, url); + }); + } + return queue.wrapLoader(loader); -/** - * @return {string} - */ -RenderGroup.prototype.toXML = function () { - return utils.buildString({g: this.buildJsObj()}) + function loader(url, callback) { + if(url.indexOf('http:') !== 0 && url.indexOf('https:') !== 0) { + return callback(new JsonLdError( + 'URL could not be dereferenced; only "http" and "https" URLs are ' + + 'supported.', + 'jsonld.InvalidUrl', {code: 'loading document failed', url: url}), + {contextUrl: null, documentUrl: url, document: null}); + } + if(options.secure && url.indexOf('https') !== 0) { + return callback(new JsonLdError( + 'URL could not be dereferenced; secure mode is enabled and ' + + 'the URL\'s scheme is not "https".', + 'jsonld.InvalidUrl', {code: 'loading document failed', url: url}), + {contextUrl: null, documentUrl: url, document: null}); + } + var xhr = options.xhr || XMLHttpRequest; + var req = new xhr(); + req.onload = function() { + if(req.status >= 400) { + return callback(new JsonLdError( + 'URL could not be dereferenced: ' + req.statusText, + 'jsonld.LoadDocumentError', { + code: 'loading document failed', + url: url, + httpStatusCode: req.status + }), {contextUrl: null, documentUrl: url, document: null}); + } + + var doc = {contextUrl: null, documentUrl: url, document: req.response}; + + // handle Link Header (avoid unsafe header warning by existence testing) + var contentType = req.getResponseHeader('Content-Type'); + var linkHeader; + if(rlink.test(req.getAllResponseHeaders())) { + linkHeader = req.getResponseHeader('Link'); + } + if(linkHeader && contentType !== 'application/ld+json') { + // only 1 related link header permitted + linkHeader = jsonld.parseLinkHeader(linkHeader)[LINK_HEADER_REL]; + if(_isArray(linkHeader)) { + return callback(new JsonLdError( + 'URL could not be dereferenced, it has more than one ' + + 'associated HTTP Link Header.', + 'jsonld.InvalidUrl', + {code: 'multiple context link headers', url: url}), doc); + } + if(linkHeader) { + doc.contextUrl = linkHeader.target; + } + } + + callback(null, doc); + }; + req.onerror = function() { + callback(new JsonLdError( + 'URL could not be dereferenced, an error occurred.', + 'jsonld.LoadDocumentError', + {code: 'loading document failed', url: url}), + {contextUrl: null, documentUrl: url, document: null}); + }; + req.open('GET', url, true); + req.setRequestHeader('Accept', 'application/ld+json, application/json'); + req.send(); + } }; /** - * @param {String} string - * @return {RenderGroup} + * Assigns the default document loader for external document URLs to a built-in + * default. Supported types currently include: 'jquery' and 'node'. + * + * To use the jquery document loader, the first parameter must be a reference + * to the main jquery object. + * + * @param type the type to set. + * @param [params] the parameters required to use the document loader. */ -RenderGroup.fromXML = function (string) { - var g; - function fn (err, result) { - g = RenderGroup.fromObj(result); - }; - utils.parseString(string, fn); - return g; +jsonld.useDocumentLoader = function(type) { + if(!(type in jsonld.documentLoaders)) { + throw new JsonLdError( + 'Unknown document loader type: "' + type + '"', + 'jsonld.UnknownDocumentLoader', + {type: type}); + } + + // set document loader + jsonld.documentLoader = jsonld.documentLoaders[type].apply( + jsonld, Array.prototype.slice.call(arguments, 1)); }; /** - * @param {Object} jsObj - xml2js formatted object - * @return {RenderGroup} + * Processes a local context, resolving any URLs as necessary, and returns a + * new active context in its callback. + * + * @param activeCtx the current active context. + * @param localCtx the local context to process. + * @param [options] the options to use: + * [documentLoader(url, callback(err, remoteDoc))] the document loader. + * @param callback(err, ctx) called once the operation completes. */ -RenderGroup.fromObj = function (jsObj) { - var gNode = utils.getChildByNameIgnoringNamespace(jsObj, "g"); - if (gNode === null) { - throw new Error("Bad XML provided, expected tagName g, got: " + Object.keys(jsObj)[0]); - } +jsonld.processContext = function(activeCtx, localCtx) { + // get arguments + var options = {}; + var callbackArg = 2; + if(arguments.length > 3) { + options = arguments[2] || {}; + callbackArg += 1; + } + var callback = arguments[callbackArg]; - var g = new ns.RenderGroup(); - jsObj = gNode; - if (typeof jsObj != 'object') { // nothing inside, empty xml - return g; - } + // set default options + if(!('base' in options)) { + options.base = ''; + } + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } - if(jsObj.$) { // we have some attributes - - var attributes = jsObj.$; - g.id = utils.getChildByNameIgnoringNamespace(attributes, "id"); - g.fontSize = utils.getChildByNameIgnoringNamespace(attributes, "fontSize"); - g.fontFamily = utils.getChildByNameIgnoringNamespace(attributes, "fontFamily"); - g.fontWeight = utils.getChildByNameIgnoringNamespace(attributes, "fontWeight"); - g.fontStyle = utils.getChildByNameIgnoringNamespace(attributes, "fontStyle"); - g.fontColor = utils.getChildByNameIgnoringNamespace(attributes, "fontColor"); - g.textAnchor = utils.getChildByNameIgnoringNamespace(attributes, "textAnchor"); - g.vtextAnchor = utils.getChildByNameIgnoringNamespace(attributes, "vtextAnchor"); - g.stroke = utils.getChildByNameIgnoringNamespace(attributes, "stroke"); - g.strokeWidth = utils.getChildByNameIgnoringNamespace(attributes, "strokeWidth"); - g.fill = utils.getChildByNameIgnoringNamespace(attributes, "fill"); - g.backgroundImage = utils.getChildByNameIgnoringNamespace(attributes, "backgroundImage"); - g.backgroundFit = utils.getChildByNameIgnoringNamespace(attributes, "backgroundFit"); - g.backgroundPosX = utils.getChildByNameIgnoringNamespace(attributes, "backgroundPosX"); - g.backgroundPosY = utils.getChildByNameIgnoringNamespace(attributes, "backgroundPosY"); - g.backgroundWidth = utils.getChildByNameIgnoringNamespace(attributes, "backgroundWidth"); - g.backgroundHeight = utils.getChildByNameIgnoringNamespace(attributes, "backgroundHeight"); - g.backgroundImageOpacity = utils.getChildByNameIgnoringNamespace(attributes, "backgroundImageOpacity"); - g.backgroundOpacity = utils.getChildByNameIgnoringNamespace(attributes, "backgroundOpacity"); - } - return g; -}; + // return initial context early for null context + if(localCtx === null) { + return callback(null, _getInitialContext(options)); + } -ns.RenderGroup = RenderGroup; -// ------- END RENDERGROUP ------- + // retrieve URLs in localCtx + localCtx = _clone(localCtx); + if(!(_isObject(localCtx) && '@context' in localCtx)) { + localCtx = {'@context': localCtx}; + } + _retrieveContextUrls(localCtx, options, function(err, ctx) { + if(err) { + return callback(err); + } + try { + // process context + ctx = new Processor().processContext(activeCtx, ctx, options); + } catch(ex) { + return callback(ex); + } + callback(null, ctx); + }); +}; -// ------- STYLE ------- /** - * Represents the <style> element. - * @class - * @param {Object} params - * @param {string=} params.id - * @param {string=} params.name - * @param {string=} params.idList - * @param {RenderGroup=} params.renderGroup + * Returns true if the given subject has the given property. + * + * @param subject the subject to check. + * @param property the property to look for. + * + * @return true if the subject has the given property, false if not. */ -var Style = function(params) { - var params = checkParams(params, ['id', 'name', 'idList', 'renderGroup']); - this.id = params.id; - this.name = params.name; - this.idList = params.idList; // TODO add utility functions to manage this (should be array) - this.renderGroup = params.renderGroup; +jsonld.hasProperty = function(subject, property) { + var rval = false; + if(property in subject) { + var value = subject[property]; + rval = (!_isArray(value) || value.length > 0); + } + return rval; }; /** - * @param {RenderGroup} renderGroup + * Determines if the given value is a property of the given subject. + * + * @param subject the subject to check. + * @param property the property to check. + * @param value the value to check. + * + * @return true if the value exists, false if not. */ -Style.prototype.setRenderGroup = function (renderGroup) { - this.renderGroup = renderGroup; +jsonld.hasValue = function(subject, property, value) { + var rval = false; + if(jsonld.hasProperty(subject, property)) { + var val = subject[property]; + var isList = _isList(val); + if(_isArray(val) || isList) { + if(isList) { + val = val['@list']; + } + for(var i = 0; i < val.length; ++i) { + if(jsonld.compareValues(value, val[i])) { + rval = true; + break; + } + } + } else if(!_isArray(value)) { + // avoid matching the set of values with an array value parameter + rval = jsonld.compareValues(value, val); + } + } + return rval; }; /** - * @return {string[]} + * Adds a value to a subject. If the value is an array, all values in the + * array will be added. + * + * @param subject the subject to add the value to. + * @param property the property that relates the value to the subject. + * @param value the value to add. + * @param [options] the options to use: + * [propertyIsArray] true if the property is always an array, false + * if not (default: false). + * [allowDuplicate] true to allow duplicates, false not to (uses a + * simple shallow comparison of subject ID or value) (default: true). */ -Style.prototype.getIdListAsArray = function () { - return this.idList.split(' '); -} +jsonld.addValue = function(subject, property, value, options) { + options = options || {}; + if(!('propertyIsArray' in options)) { + options.propertyIsArray = false; + } + if(!('allowDuplicate' in options)) { + options.allowDuplicate = true; + } -/** - * @param {string[]} idArray - */ -Style.prototype.setIdListFromArray = function (idArray) { - this.idList = idArray.join(' '); -} + if(_isArray(value)) { + if(value.length === 0 && options.propertyIsArray && + !(property in subject)) { + subject[property] = []; + } + for(var i = 0; i < value.length; ++i) { + jsonld.addValue(subject, property, value[i], options); + } + } else if(property in subject) { + // check if subject already has value if duplicates not allowed + var hasValue = (!options.allowDuplicate && + jsonld.hasValue(subject, property, value)); -/** - * Convenience function returning a map of ids to their respective RenderGroup object. - * The style properties can then be directly accessed. Example: map[id].stroke - * @return {Object.} - */ -Style.prototype.getStyleMap = function () { - var index = {}; - var ids = this.getIdListAsArray(); - for(var i=0; i < ids.length; i++) { - var id = ids[i]; - index[id] = this.renderGroup; - } - return index; + // make property an array if value not present or always an array + if(!_isArray(subject[property]) && + (!hasValue || options.propertyIsArray)) { + subject[property] = [subject[property]]; + } + + // add new value + if(!hasValue) { + subject[property].push(value); + } + } else { + // add new value as set or single value + subject[property] = options.propertyIsArray ? [value] : value; + } }; + /** - * @return {Object} - xml2js formatted object + * Gets all of the values for a subject's property as an array. + * + * @param subject the subject. + * @param property the property. + * + * @return all of the values for a subject's property as an array. */ -Style.prototype.buildJsObj = function () { - var styleObj = {}; - - // attributes - var attributes = {}; - if(this.id != null) { - attributes.id = this.id; - } - if(this.name != null) { - attributes.name = this.name; - } - if(this.idList != null) { - attributes.idList = this.idList; - } - utils.addAttributes(styleObj, attributes); - - // children - if(this.renderGroup != null) { - styleObj.g = this.renderGroup.buildJsObj(); - } - return styleObj; +jsonld.getValues = function(subject, property) { + var rval = subject[property] || []; + if(!_isArray(rval)) { + rval = [rval]; + } + return rval; }; /** - * @return {string} + * Removes a property from a subject. + * + * @param subject the subject. + * @param property the property. */ -Style.prototype.toXML = function () { - return utils.buildString({style: this.buildJsObj()}) +jsonld.removeProperty = function(subject, property) { + delete subject[property]; }; /** - * @param {String} string - * @return {Style} + * Removes a value from a subject. + * + * @param subject the subject. + * @param property the property that relates the value to the subject. + * @param value the value to remove. + * @param [options] the options to use: + * [propertyIsArray] true if the property is always an array, false + * if not (default: false). */ -Style.fromXML = function (string) { - var style; - function fn (err, result) { - style = Style.fromObj(result); - }; - utils.parseString(string, fn); - return style; +jsonld.removeValue = function(subject, property, value, options) { + options = options || {}; + if(!('propertyIsArray' in options)) { + options.propertyIsArray = false; + } + + // filter out value + var values = jsonld.getValues(subject, property).filter(function(e) { + return !jsonld.compareValues(e, value); + }); + + if(values.length === 0) { + jsonld.removeProperty(subject, property); + } else if(values.length === 1 && !options.propertyIsArray) { + subject[property] = values[0]; + } else { + subject[property] = values; + } }; /** - * @param {Object} jsObj - xml2js formatted object - * @return {Style} + * Compares two JSON-LD values for equality. Two JSON-LD values will be + * considered equal if: + * + * 1. They are both primitives of the same type and value. + * 2. They are both @values with the same @value, @type, @language, + * and @index, OR + * 3. They both have @ids they are the same. + * + * @param v1 the first value. + * @param v2 the second value. + * + * @return true if v1 and v2 are considered equal, false if not. */ -Style.fromObj = function (jsObj) { - var styleNode = utils.getChildByNameIgnoringNamespace(jsObj, "style"); - if (styleNode === null) { - throw new Error("Bad XML provided, expected tagName style, got: " + Object.keys(jsObj)[0]); - } - - var style = new ns.Style(); - jsObj = styleNode; - if (typeof jsObj != 'object') { // nothing inside, empty xml - return style; - } +jsonld.compareValues = function(v1, v2) { + // 1. equal primitives + if(v1 === v2) { + return true; + } - if (jsObj.$) { // we have some attributes - var attributes = jsObj.$; - style.id = utils.getChildByNameIgnoringNamespace(attributes, "id"); - style.name = utils.getChildByNameIgnoringNamespace(attributes, "name"); - style.idList = utils.getChildByNameIgnoringNamespace(attributes, "idList"); - } + // 2. equal @values + if(_isValue(v1) && _isValue(v2) && + v1['@value'] === v2['@value'] && + v1['@type'] === v2['@type'] && + v1['@language'] === v2['@language'] && + v1['@index'] === v2['@index']) { + return true; + } - // children - var gNode = utils.getChildByNameIgnoringNamespace(jsObj, "g"); - if(gNode) { - var g = ns.RenderGroup.fromObj({g: gNode[0]}); - style.setRenderGroup(g); - } + // 3. equal @ids + if(_isObject(v1) && ('@id' in v1) && _isObject(v2) && ('@id' in v2)) { + return v1['@id'] === v2['@id']; + } - return style; + return false; }; -ns.Style = Style; -// ------- END STYLE ------- - -// ------- LISTOFSTYLES ------- /** - * Represents the <listOfStyles> element. - * @class - */ -var ListOfStyles = function() { - this.styles = []; -}; - -/** - * @param {Style} style + * Gets the value for the given active context key and type, null if none is + * set. + * + * @param ctx the active context. + * @param key the context key. + * @param [type] the type of value to get (eg: '@id', '@type'), if not + * specified gets the entire entry for a key, null if not found. + * + * @return the value. */ -ListOfStyles.prototype.addStyle = function (style) { - this.styles.push(style); -}; +jsonld.getContextValue = function(ctx, key, type) { + var rval = null; -/** - * Convenience function returning a map of ids to their respective RenderGroup object, - * for all the styles. - * The style properties can then be directly accessed. Example: map[id].stroke - * @return {Object.} - */ -ListOfStyles.prototype.getStyleMap = function () { - var index = {}; - for(var i=0; i < this.styles.length; i++) { - var style = this.styles[i]; - var subIndex = style.getStyleMap(); - for(var id in subIndex) { - index[id] = subIndex[id]; - } - } - return index; -} + // return null for invalid key + if(key === null) { + return rval; + } -/** - * @return {Object} - xml2js formatted object - */ -ListOfStyles.prototype.buildJsObj = function () { - var listOfStylesObj = {}; + // get default language + if(type === '@language' && (type in ctx)) { + rval = ctx[type]; + } - for(var i=0; i < this.styles.length; i++) { - if (i==0) { - listOfStylesObj.style = []; - } - listOfStylesObj.style.push(this.styles[i].buildJsObj()); - } + // get specific entry information + if(ctx.mappings[key]) { + var entry = ctx.mappings[key]; - return listOfStylesObj; + if(_isUndefined(type)) { + // return whole entry + rval = entry; + } else if(type in entry) { + // return entry value for type + rval = entry[type]; + } + } + + return rval; }; +/** Registered RDF dataset parsers hashed by content-type. */ +var _rdfParsers = {}; + /** - * @return {string} + * Registers an RDF dataset parser by content-type, for use with + * jsonld.fromRDF. An RDF dataset parser will always be given two parameters, + * a string of input and a callback. An RDF dataset parser can be synchronous + * or asynchronous. + * + * If the parser function returns undefined or null then it will be assumed to + * be asynchronous w/a continuation-passing style and the callback parameter + * given to the parser MUST be invoked. + * + * If it returns a Promise, then it will be assumed to be asynchronous, but the + * callback parameter MUST NOT be invoked. It should instead be ignored. + * + * If it returns an RDF dataset, it will be assumed to be synchronous and the + * callback parameter MUST NOT be invoked. It should instead be ignored. + * + * @param contentType the content-type for the parser. + * @param parser(input, callback(err, dataset)) the parser function (takes a + * string as a parameter and either returns null/undefined and uses + * the given callback, returns a Promise, or returns an RDF dataset). */ -ListOfStyles.prototype.toXML = function () { - return utils.buildString({listOfStyles: this.buildJsObj()}) +jsonld.registerRDFParser = function(contentType, parser) { + _rdfParsers[contentType] = parser; }; /** - * @param {String} string - * @return {ListOfStyles} + * Unregisters an RDF dataset parser by content-type. + * + * @param contentType the content-type for the parser. */ -ListOfStyles.fromXML = function (string) { - var listOfStyles; - function fn (err, result) { - listOfStyles = ListOfStyles.fromObj(result); - }; - utils.parseString(string, fn); - return listOfStyles; +jsonld.unregisterRDFParser = function(contentType) { + delete _rdfParsers[contentType]; }; -/** - * @param {Object} jsObj - xml2js formatted object - * @return {ListOfStyles} - */ -ListOfStyles.fromObj = function (jsObj) { - var listOfStylesNode = utils.getChildByNameIgnoringNamespace(jsObj, "listOfStyles"); - if (listOfStylesNode === null) { - throw new Error("Bad XML provided, expected tagName listOfStyles, got: " + Object.keys(jsObj)[0]); - } +if(_nodejs) { + // needed for serialization of XML literals + if(typeof XMLSerializer === 'undefined') { + var XMLSerializer = null; + } + if(typeof Node === 'undefined') { + var Node = { + ELEMENT_NODE: 1, + ATTRIBUTE_NODE: 2, + TEXT_NODE: 3, + CDATA_SECTION_NODE: 4, + ENTITY_REFERENCE_NODE: 5, + ENTITY_NODE: 6, + PROCESSING_INSTRUCTION_NODE: 7, + COMMENT_NODE: 8, + DOCUMENT_NODE: 9, + DOCUMENT_TYPE_NODE: 10, + DOCUMENT_FRAGMENT_NODE: 11, + NOTATION_NODE:12 + }; + } +} - var listOfStyles = new ns.ListOfStyles(); - jsObj = listOfStylesNode; - if (typeof jsObj != 'object') { // nothing inside, empty xml - return listOfStyles; - } +// constants +var XSD_BOOLEAN = 'http://www.w3.org/2001/XMLSchema#boolean'; +var XSD_DOUBLE = 'http://www.w3.org/2001/XMLSchema#double'; +var XSD_INTEGER = 'http://www.w3.org/2001/XMLSchema#integer'; +var XSD_STRING = 'http://www.w3.org/2001/XMLSchema#string'; - // children - var styles = utils.getChildByNameIgnoringNamespace(jsObj, "style"); - if (styles) { - for (var i = 0; i < styles.length; i++) { - var style = ns.Style.fromObj({style: styles[i]}); - listOfStyles.addStyle(style); - } - } +var RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; +var RDF_LIST = RDF + 'List'; +var RDF_FIRST = RDF + 'first'; +var RDF_REST = RDF + 'rest'; +var RDF_NIL = RDF + 'nil'; +var RDF_TYPE = RDF + 'type'; +var RDF_PLAIN_LITERAL = RDF + 'PlainLiteral'; +var RDF_XML_LITERAL = RDF + 'XMLLiteral'; +var RDF_OBJECT = RDF + 'object'; +var RDF_LANGSTRING = RDF + 'langString'; - return listOfStyles; -}; +var LINK_HEADER_REL = 'http://www.w3.org/ns/json-ld#context'; +var MAX_CONTEXT_URLS = 10; -ns.ListOfStyles = ListOfStyles; -// ------- END LISTOFSTYLES ------- -// ------- BACKGROUNDIMAGE ------- /** - * Represents the <backgroundImage> element. - * @class - * @param {Object} params - * @param {string=} params.id - * @param {string=} params.value + * A JSON-LD Error. + * + * @param msg the error message. + * @param type the error type. + * @param details the error details. */ -var BackgroundImage = function(params) { - var params = checkParams(params, ['id', 'value']); - this.id = params.id; - this.value = params.value; +var JsonLdError = function(msg, type, details) { + if(_nodejs) { + Error.call(this); + Error.captureStackTrace(this, this.constructor); + } else if(typeof Error !== 'undefined') { + this.stack = (new Error()).stack; + } + this.name = type || 'jsonld.Error'; + this.message = msg || 'An unspecified JSON-LD error occurred.'; + this.details = details || {}; }; +if(_nodejs) { + _dereq_('util').inherits(JsonLdError, Error); +} else if(typeof Error !== 'undefined') { + JsonLdError.prototype = new Error(); +} /** - * @return {Object} - xml2js formatted object + * Constructs a new JSON-LD Processor. */ -BackgroundImage.prototype.buildJsObj = function () { - var bgImgObj = {}; - - // attributes - var attributes = {}; - if(this.id != null) { - attributes.id = this.id; - } - if(this.value != null) { - attributes.value = this.value; - } - utils.addAttributes(bgImgObj, attributes); - return bgImgObj; -}; +var Processor = function() {}; /** - * @return {string} + * Recursively compacts an element using the given active context. All values + * must be in expanded form before this method is called. + * + * @param activeCtx the active context to use. + * @param activeProperty the compacted property associated with the element + * to compact, null for none. + * @param element the element to compact. + * @param options the compaction options. + * + * @return the compacted value. */ -BackgroundImage.prototype.toXML = function () { - return utils.buildString({backgroundImage: this.buildJsObj()}) -}; +Processor.prototype.compact = function( + activeCtx, activeProperty, element, options) { + // recursively compact array + if(_isArray(element)) { + var rval = []; + for(var i = 0; i < element.length; ++i) { + // compact, dropping any null values + var compacted = this.compact( + activeCtx, activeProperty, element[i], options); + if(compacted !== null) { + rval.push(compacted); + } + } + if(options.compactArrays && rval.length === 1) { + // use single element if no container is specified + var container = jsonld.getContextValue( + activeCtx, activeProperty, '@container'); + if(container === null) { + rval = rval[0]; + } + } + return rval; + } -/** - * @param {String} string - * @return {BackgroundImage} - */ -BackgroundImage.fromXML = function (string) { - var backgroundImage; - function fn (err, result) { - backgroundImage = BackgroundImage.fromObj(result); - }; - utils.parseString(string, fn); - return backgroundImage; -}; + // recursively compact object + if(_isObject(element)) { + if(options.link && '@id' in element && element['@id'] in options.link) { + // check for a linked element to reuse + var linked = options.link[element['@id']]; + for(var i = 0; i < linked.length; ++i) { + if(linked[i].expanded === element) { + return linked[i].compacted; + } + } + } -/** - * @param {Object} jsObj - xml2js formatted object - * @return {BackgroundImage} - */ -BackgroundImage.fromObj = function (jsObj) { - if (typeof jsObj.backgroundImage == 'undefined') { - throw new Error("Bad XML provided, expected tagName backgroundImage, got: " + Object.keys(jsObj)[0]); - } + // do value compaction on @values and subject references + if(_isValue(element) || _isSubjectReference(element)) { + var rval = _compactValue(activeCtx, activeProperty, element); + if(options.link && _isSubjectReference(element)) { + // store linked element + if(!(element['@id'] in options.link)) { + options.link[element['@id']] = []; + } + options.link[element['@id']].push({expanded: element, compacted: rval}); + } + return rval; + } - var backgroundImage = new ns.BackgroundImage(); - jsObj = jsObj.backgroundImage; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return backgroundImage; - } + // FIXME: avoid misuse of active property as an expanded property? + var insideReverse = (activeProperty === '@reverse'); - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - backgroundImage.id = attributes.id || null; - backgroundImage.value = attributes.value || null; - } - return backgroundImage; -}; + var rval = {}; -ns.BackgroundImage = BackgroundImage; -// ------- END BACKGROUNDIMAGE ------- + if(options.link && '@id' in element) { + // store linked element + if(!(element['@id'] in options.link)) { + options.link[element['@id']] = []; + } + options.link[element['@id']].push({expanded: element, compacted: rval}); + } -// ------- LISTOFBACKGROUNDIMAGES ------- -/** - * Represents the <listOfBackgroundImages> element. - * @class - */ -var ListOfBackgroundImages = function () { - this.backgroundImages = []; - this.imageIndex = {}; -}; + // process element keys in order + var keys = Object.keys(element).sort(); + for(var ki = 0; ki < keys.length; ++ki) { + var expandedProperty = keys[ki]; + var expandedValue = element[expandedProperty]; -/** - * @param {BackgroundImage} backgroundImage - */ -ListOfBackgroundImages.prototype.addBackgroundImage = function (backgroundImage) { - this.backgroundImages.push(backgroundImage); - this.imageIndex[backgroundImage.id] = backgroundImage.value; -}; + // compact @id and @type(s) + if(expandedProperty === '@id' || expandedProperty === '@type') { + var compactedValue; -/** - * Convenient method to get a background image value directly. - * @param {string} id - * @return {string} - */ -ListOfBackgroundImages.prototype.getBackgroundImageById = function (id) { - return this.imageIndex[id]; -}; + // compact single @id + if(_isString(expandedValue)) { + compactedValue = _compactIri( + activeCtx, expandedValue, null, + {vocab: (expandedProperty === '@type')}); + } else { + // expanded value must be a @type array + compactedValue = []; + for(var vi = 0; vi < expandedValue.length; ++vi) { + compactedValue.push(_compactIri( + activeCtx, expandedValue[vi], null, {vocab: true})); + } + } -/** - * Convenient method to get all the background image values in the list. - * @return {string[]} - */ -ListOfBackgroundImages.prototype.getAllImages = function () { - return Object.values(this.imageIndex); -}; + // use keyword alias and add value + var alias = _compactIri(activeCtx, expandedProperty); + var isArray = (_isArray(compactedValue) && expandedValue.length === 0); + jsonld.addValue( + rval, alias, compactedValue, {propertyIsArray: isArray}); + continue; + } -/** - * @return {Object} - xml2js formatted object - */ -ListOfBackgroundImages.prototype.buildJsObj = function () { - var listOfBgImagesObj = {}; + // handle @reverse + if(expandedProperty === '@reverse') { + // recursively compact expanded value + var compactedValue = this.compact( + activeCtx, '@reverse', expandedValue, options); - for(var i=0; i < this.backgroundImages.length; i++) { - if (i==0) { - listOfBgImagesObj.backgroundImage = []; - } - listOfBgImagesObj.backgroundImage.push(this.backgroundImages[i].buildJsObj()); - } + // handle double-reversed properties + for(var compactedProperty in compactedValue) { + if(activeCtx.mappings[compactedProperty] && + activeCtx.mappings[compactedProperty].reverse) { + var value = compactedValue[compactedProperty]; + var container = jsonld.getContextValue( + activeCtx, compactedProperty, '@container'); + var useArray = (container === '@set' || !options.compactArrays); + jsonld.addValue( + rval, compactedProperty, value, {propertyIsArray: useArray}); + delete compactedValue[compactedProperty]; + } + } - return listOfBgImagesObj; -}; + if(Object.keys(compactedValue).length > 0) { + // use keyword alias and add value + var alias = _compactIri(activeCtx, expandedProperty); + jsonld.addValue(rval, alias, compactedValue); + } -/** - * @return {string} - */ -ListOfBackgroundImages.prototype.toXML = function () { - return utils.buildString({listOfBackgroundImages: this.buildJsObj()}) -}; + continue; + } -/** - * @param {String} string - * @return {ListOfBackgroundImages} - */ -ListOfBackgroundImages.fromXML = function (string) { - var listOfBackgroundImages; - function fn (err, result) { - listOfBackgroundImages = ListOfBackgroundImages.fromObj(result); - }; - utils.parseString(string, fn); - return listOfBackgroundImages; -}; + // handle @index property + if(expandedProperty === '@index') { + // drop @index if inside an @index container + var container = jsonld.getContextValue( + activeCtx, activeProperty, '@container'); + if(container === '@index') { + continue; + } -/** - * @param {Object} jsObj - xml2js formatted object - * @return {ListOfBackgroundImages} - */ -ListOfBackgroundImages.fromObj = function (jsObj) { - var listOfBackgroundImagesNode = utils.getChildByNameIgnoringNamespace(jsObj, "listOfBackgroundImages"); - if (listOfBackgroundImagesNode === null) { - throw new Error("Bad XML provided, expected tagName listOfBackgroundImages, got: " + Object.keys(jsObj)[0]); - } + // use keyword alias and add value + var alias = _compactIri(activeCtx, expandedProperty); + jsonld.addValue(rval, alias, expandedValue); + continue; + } - var listOfBackgroundImages = new ns.ListOfBackgroundImages(); - jsObj = listOfBackgroundImagesNode; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return listOfBackgroundImages; - } + // skip array processing for keywords that aren't @graph or @list + if(expandedProperty !== '@graph' && expandedProperty !== '@list' && + _isKeyword(expandedProperty)) { + // use keyword alias and add value as is + var alias = _compactIri(activeCtx, expandedProperty); + jsonld.addValue(rval, alias, expandedValue); + continue; + } - // children - if(jsObj.backgroundImage) { - var backgroundImages = jsObj.backgroundImage; - for (var i=0; i < backgroundImages.length; i++) { - var backgroundImage = ns.BackgroundImage.fromObj({backgroundImage: backgroundImages[i]}); - listOfBackgroundImages.addBackgroundImage(backgroundImage); - } - } + // Note: expanded value must be an array due to expansion algorithm. - return listOfBackgroundImages; -}; + // preserve empty arrays + if(expandedValue.length === 0) { + var itemActiveProperty = _compactIri( + activeCtx, expandedProperty, expandedValue, {vocab: true}, + insideReverse); + jsonld.addValue( + rval, itemActiveProperty, expandedValue, {propertyIsArray: true}); + } -ns.ListOfBackgroundImages = ListOfBackgroundImages; -// ------- END LISTOFBACKGROUNDIMAGES ------- -// ------- RENDERINFORMATION ------- -/** - * Represents the <renderInformation> element. - * @class - * @param {Object} params - * @param {string=} params.id - * @param {string=} params.name - * @param {string=} params.programName - * @param {string=} params.programVersion - * @param {string=} params.backgroundColor - * @param {ListOfColorDefinitions=} params.listOfColorDefinitions - * @param {ListOfStyles=} params.listOfStyles - * @param {ListOfBackgroundImages=} params.listOfBackgroundImages - */ -var RenderInformation = function (params) { - var params = checkParams(params, ['id', 'name', 'programName', - 'programVersion', 'backgroundColor', 'listOfColorDefinitions', 'listOfStyles', 'listOfBackgroundImages']); - this.id = params.id; // required, rest is optional - this.name = params.name; - this.programName = params.programName; - this.programVersion = params.programVersion; - this.backgroundColor = params.backgroundColor; - this.listOfColorDefinitions = params.listOfColorDefinitions; - this.listOfStyles = params.listOfStyles; - this.listOfBackgroundImages = params.listOfBackgroundImages; -}; - -/** - * @param {ListOfColorDefinitions} listOfColorDefinitions - */ -RenderInformation.prototype.setListOfColorDefinitions = function(listOfColorDefinitions) { - this.listOfColorDefinitions = listOfColorDefinitions; -}; - -/** - * @param {ListOfStyles} listOfStyles - */ -RenderInformation.prototype.setListOfStyles = function(listOfStyles) { - this.listOfStyles = listOfStyles; -}; + // recusively process array values + for(var vi = 0; vi < expandedValue.length; ++vi) { + var expandedItem = expandedValue[vi]; -/** - * @param {ListOfBackgroundImages} listOfBackgroundImages - */ -RenderInformation.prototype.setListOfBackgroundImages = function(listOfBackgroundImages) { - this.listOfBackgroundImages = listOfBackgroundImages; -}; + // compact property and get container type + var itemActiveProperty = _compactIri( + activeCtx, expandedProperty, expandedItem, {vocab: true}, + insideReverse); + var container = jsonld.getContextValue( + activeCtx, itemActiveProperty, '@container'); -/** - * @return {Object} - xml2js formatted object - */ -RenderInformation.prototype.buildJsObj = function () { - var renderInformationObj = {}; + // get @list value if appropriate + var isList = _isList(expandedItem); + var list = null; + if(isList) { + list = expandedItem['@list']; + } - // attributes - var attributes = {}; - attributes.xmlns = ns.xmlns; - if(this.id != null) { - attributes.id = this.id; - } - if(this.name != null) { - attributes.name = this.name; - } - if(this.programName != null) { - attributes.programName = this.programName; - } - if(this.programVersion != null) { - attributes.programVersion = this.programVersion; - } - if(this.backgroundColor != null) { - attributes.backgroundColor = this.backgroundColor; - } - utils.addAttributes(renderInformationObj, attributes); + // recursively compact expanded item + var compactedItem = this.compact( + activeCtx, itemActiveProperty, isList ? list : expandedItem, options); - // children - if(this.listOfColorDefinitions != null) { - renderInformationObj.listOfColorDefinitions = this.listOfColorDefinitions.buildJsObj(); - } - if(this.listOfBackgroundImages != null) { - renderInformationObj.listOfBackgroundImages = this.listOfBackgroundImages.buildJsObj(); - } - if(this.listOfStyles != null) { - renderInformationObj.listOfStyles = this.listOfStyles.buildJsObj(); - } - return renderInformationObj; -}; + // handle @list + if(isList) { + // ensure @list value is an array + if(!_isArray(compactedItem)) { + compactedItem = [compactedItem]; + } -/** - * @return {string} - */ -RenderInformation.prototype.toXML = function() { - return utils.buildString({renderInformation: this.buildJsObj()}) -}; + if(container !== '@list') { + // wrap using @list alias + var wrapper = {}; + wrapper[_compactIri(activeCtx, '@list')] = compactedItem; + compactedItem = wrapper; -/** - * @param {String} string - * @return {RenderInformation} - */ -RenderInformation.fromXML = function (string) { - var renderInformation; - function fn (err, result) { - renderInformation = RenderInformation.fromObj(result); - }; - utils.parseString(string, fn); - return renderInformation; -}; + // include @index from expanded @list, if any + if('@index' in expandedItem) { + compactedItem[_compactIri(activeCtx, '@index')] = + expandedItem['@index']; + } + } else if(itemActiveProperty in rval) { + // can't use @list container for more than 1 list + throw new JsonLdError( + 'JSON-LD compact error; property has a "@list" @container ' + + 'rule but there is more than a single @list that matches ' + + 'the compacted term in the document. Compaction might mix ' + + 'unwanted items into the list.', + 'jsonld.SyntaxError', {code: 'compaction to list of lists'}); + } + } -/** - * @param {Object} jsObj - xml2js formatted object - * @return {RenderInformation} - */ -RenderInformation.fromObj = function (jsObj) { - var renderInformationNode = utils.getChildByNameIgnoringNamespace(jsObj, "renderInformation"); - if (renderInformationNode === null) { - throw new Error("Bad XML provided, expected tagName renderInformation, got: " + Object.keys(jsObj)[0]); - } + // handle language and index maps + if(container === '@language' || container === '@index') { + // get or create the map object + var mapObject; + if(itemActiveProperty in rval) { + mapObject = rval[itemActiveProperty]; + } else { + rval[itemActiveProperty] = mapObject = {}; + } - var renderInformation = new ns.RenderInformation(); - jsObj = renderInformationNode; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return renderInformation; - } + // if container is a language map, simplify compacted value to + // a simple string + if(container === '@language' && _isValue(compactedItem)) { + compactedItem = compactedItem['@value']; + } - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - renderInformation.id = utils.getChildByNameIgnoringNamespace(attributes,"id"); - renderInformation.name = utils.getChildByNameIgnoringNamespace(attributes,"name"); - renderInformation.programName = utils.getChildByNameIgnoringNamespace(attributes,"programName"); - renderInformation.programVersion = utils.getChildByNameIgnoringNamespace(attributes,"programVersion"); - renderInformation.backgroundColor = utils.getChildByNameIgnoringNamespace(attributes,"backgroundColor"); - } + // add compact value to map object using key from expanded value + // based on the container type + jsonld.addValue(mapObject, expandedItem[container], compactedItem); + } else { + // use an array if: compactArrays flag is false, + // @container is @set or @list , value is an empty + // array, or key is @graph + var isArray = (!options.compactArrays || container === '@set' || + container === '@list' || + (_isArray(compactedItem) && compactedItem.length === 0) || + expandedProperty === '@list' || expandedProperty === '@graph'); - // children - var listOfColorDefinitionsNode = utils.getChildByNameIgnoringNamespace(jsObj, "listOfColorDefinitions"); - if(listOfColorDefinitionsNode) { - var listOfColorDefinitions = ns.ListOfColorDefinitions.fromObj({listOfColorDefinitions: listOfColorDefinitionsNode}); - renderInformation.setListOfColorDefinitions(listOfColorDefinitions); - } + // add compact value + jsonld.addValue( + rval, itemActiveProperty, compactedItem, + {propertyIsArray: isArray}); + } + } + } - var listOfStylesNode = utils.getChildByNameIgnoringNamespace(jsObj, "listOfStyles"); - if(listOfStylesNode) { - var listOfStyles = ns.ListOfStyles.fromObj({listOfStyles: listOfStylesNode}); - renderInformation.setListOfStyles(listOfStyles); - } + return rval; + } - var listOfBackgroundImagesNode = utils.getChildByNameIgnoringNamespace(jsObj, "listOfBackgroundImages"); - if(listOfBackgroundImagesNode) { - var listOfBackgroundImages = ns.ListOfBackgroundImages.fromObj({listOfBackgroundImages: listOfBackgroundImagesNode[0]}); - renderInformation.setListOfBackgroundImages(listOfBackgroundImages); - } - return renderInformation; + // only primitives remain which are already compact + return element; }; -ns.RenderInformation = RenderInformation; -// ------- END RENDERINFORMATION ------- - -module.exports = ns; - -},{"./utilities":33,"xml2js":26}],31:[function(_dereq_,module,exports){ /** - * The API contains two other submodules: {@link libsbgn.render} and {@link libsbgn.annot} - * @module libsbgn - * @namespace libsbgn -*/ - -var renderExt = _dereq_('./libsbgn-render'); -var annotExt = _dereq_('./libsbgn-annotations'); -var xml2js = _dereq_('xml2js'); -var utils = _dereq_('./utilities'); -var schematronValidator = _dereq_('./schematronValidator'); -var checkParams = utils.checkParams; + * Recursively expands an element using the given context. Any context in + * the element will be removed. All context URLs must have been retrieved + * before calling this method. + * + * @param activeCtx the context to use. + * @param activeProperty the property for the element, null for none. + * @param element the element to expand. + * @param options the expansion options. + * @param insideList true if the element is a list, false if not. + * + * @return the expanded value. + */ +Processor.prototype.expand = function( + activeCtx, activeProperty, element, options, insideList) { + var self = this; -var ns = {}; // namespace that encapsulates all exportable features + // nothing to expand + if(element === null || element === undefined) { + return null; + } -ns.xmlns = "http://sbgn.org/libsbgn/0.3"; + if(!_isArray(element) && !_isObject(element)) { + // drop free-floating scalars that are not in lists + if(!insideList && (activeProperty === null || + _expandIri(activeCtx, activeProperty, {vocab: true}) === '@graph')) { + return null; + } -// ------- SBGNBase ------- -/** - * Parent class for several sbgn elements. Used to provide extension and notes element. - * End users don't need to interact with it. It can be safely ignored. - * @class - * @param {Object} params - * @param {Extension=} params.extension - * @param {Notes=} params.notes - */ -var SBGNBase = function (params) { - var params = checkParams(params, ['extension', 'notes']); - this.extension = params.extension; - this.notes = params.notes; -}; + // expand element according to value expansion rules + return _expandValue(activeCtx, activeProperty, element); + } -/** - * Allows inheriting objects to get an extension element. - * @param {Extension} extension - */ -SBGNBase.prototype.setExtension = function (extension) { - this.extension = extension; -}; + // recursively expand array + if(_isArray(element)) { + var rval = []; + var container = jsonld.getContextValue( + activeCtx, activeProperty, '@container'); + insideList = insideList || container === '@list'; + for(var i = 0; i < element.length; ++i) { + // expand element + var e = self.expand(activeCtx, activeProperty, element[i], options); + if(insideList && (_isArray(e) || _isList(e))) { + // lists of lists are illegal + throw new JsonLdError( + 'Invalid JSON-LD syntax; lists of lists are not permitted.', + 'jsonld.SyntaxError', {code: 'list of lists'}); + } + // drop null values + if(e !== null) { + if(_isArray(e)) { + rval = rval.concat(e); + } else { + rval.push(e); + } + } + } + return rval; + } -/** - * Allows inheriting objects to get a notes element. - * @param {Notes} notes - */ -SBGNBase.prototype.setNotes = function (notes) { - this.notes = notes; -}; + // recursively expand object: -/** - * Add the appropriate properties to jsObj. - * @param {Object} jsObj - xml2js formatted object - */ -SBGNBase.prototype.baseToJsObj = function (jsObj) { - if(this.extension != null) { - jsObj.extension = this.extension.buildJsObj(); - } - if(this.notes != null) { - jsObj.notes = this.notes.buildJsObj(); - } -}; + // if element has a context, process it + if('@context' in element) { + activeCtx = self.processContext(activeCtx, element['@context'], options); + } -/** - * Get the appropriate properties from jsObj. - * @param {Object} jsObj - xml2js formatted object - */ -SBGNBase.prototype.baseFromObj = function (jsObj) { - if (jsObj.extension) { - var extension = ns.Extension.fromObj({extension: jsObj.extension[0]}); - this.setExtension(extension); - } - if (jsObj.notes) { - var notes = ns.Notes.fromObj({notes: jsObj.notes[0]}); - this.setNotes(notes); - } -}; -ns.SBGNBase = SBGNBase; -// ------- END SBGNBase ------- + // expand the active property + var expandedActiveProperty = _expandIri( + activeCtx, activeProperty, {vocab: true}); -// ------- SBGN ------- -/** - * Represents the <sbgn> element. - * @class - * @extends SBGNBase - * @param {Object} params - * @param {string=} params.xmlns - * @param {Map[]=} params.maps - */ -var Sbgn = function (params) { - ns.SBGNBase.call(this, params); - var params = checkParams(params, ['xmlns', 'maps']); - this.xmlns = params.xmlns; - this.maps = params.maps || []; -}; + var rval = {}; + var keys = Object.keys(element).sort(); + for(var ki = 0; ki < keys.length; ++ki) { + var key = keys[ki]; + var value = element[key]; + var expandedValue; -Sbgn.prototype = Object.create(ns.SBGNBase.prototype); -Sbgn.prototype.constructor = Sbgn; + // skip @context + if(key === '@context') { + continue; + } -/** - * @param {Map} map - */ -Sbgn.prototype.addMap = function (map) { - this.maps.push(map); -}; + // expand property + var expandedProperty = _expandIri(activeCtx, key, {vocab: true}); -/** - * @return {Object} - xml2js formatted object - */ -Sbgn.prototype.buildJsObj = function () { - var sbgnObj = {}; + // drop non-absolute IRI keys that aren't keywords + if(expandedProperty === null || + !(_isAbsoluteIri(expandedProperty) || _isKeyword(expandedProperty))) { + continue; + } - // attributes - var attributes = {}; - if(this.xmlns != null) { - attributes.xmlns = this.xmlns; - } - utils.addAttributes(sbgnObj, attributes); + if(_isKeyword(expandedProperty)) { + if(expandedActiveProperty === '@reverse') { + throw new JsonLdError( + 'Invalid JSON-LD syntax; a keyword cannot be used as a @reverse ' + + 'property.', 'jsonld.SyntaxError', + {code: 'invalid reverse property map', value: value}); + } + if(expandedProperty in rval) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; colliding keywords detected.', + 'jsonld.SyntaxError', + {code: 'colliding keywords', keyword: expandedProperty}); + } + } - // children - this.baseToJsObj(sbgnObj); - for(var i=0; i < this.maps.length; i++) { - if (i==0) { - sbgnObj.map = []; - } - sbgnObj.map.push(this.maps[i].buildJsObj()); - } - return sbgnObj; -}; + // syntax error if @id is not a string + if(expandedProperty === '@id' && !_isString(value)) { + if(!options.isFrame) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@id" value must a string.', + 'jsonld.SyntaxError', {code: 'invalid @id value', value: value}); + } + if(!_isObject(value)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@id" value must be a string or an ' + + 'object.', 'jsonld.SyntaxError', + {code: 'invalid @id value', value: value}); + } + } -/** - * @return {string} - */ -Sbgn.prototype.toXML = function () { - return utils.buildString({sbgn: this.buildJsObj()}); -}; + if(expandedProperty === '@type') { + _validateTypeValue(value); + } -/** - * @param {String} file - * @return {Issue[]} - */ -Sbgn.doValidation = function (file) { - return schematronValidator.doValidation(file); -}; + // @graph must be an array or an object + if(expandedProperty === '@graph' && + !(_isObject(value) || _isArray(value))) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@graph" value must not be an ' + + 'object or an array.', + 'jsonld.SyntaxError', {code: 'invalid @graph value', value: value}); + } -/** - * @param {String} string - * @return {Sbgn} - */ -Sbgn.fromXML = function (string) { - var sbgn; - function fn (err, result) { - sbgn = Sbgn.fromObj(result); + // @value must not be an object or an array + if(expandedProperty === '@value' && + (_isObject(value) || _isArray(value))) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@value" value must not be an ' + + 'object or an array.', + 'jsonld.SyntaxError', + {code: 'invalid value object value', value: value}); } - utils.parseString(string, fn); - return sbgn; -}; -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Sbgn} - */ -Sbgn.fromObj = function (jsObj) { - if (typeof jsObj.sbgn == 'undefined') { - throw new Error("Bad XML provided, expected tagName sbgn, got: " + Object.keys(jsObj)[0]); - } + // @language must be a string + if(expandedProperty === '@language') { + if(value === null) { + // drop null @language values, they expand as if they didn't exist + continue; + } + if(!_isString(value)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@language" value must be a string.', + 'jsonld.SyntaxError', + {code: 'invalid language-tagged string', value: value}); + } + // ensure language value is lowercase + value = value.toLowerCase(); + } - var sbgn = new ns.Sbgn(); - jsObj = jsObj.sbgn; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return sbgn; - } + // @index must be a string + if(expandedProperty === '@index') { + if(!_isString(value)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@index" value must be a string.', + 'jsonld.SyntaxError', + {code: 'invalid @index value', value: value}); + } + } - if(jsObj.$) { // we have some atributes - var attributes = jsObj.$; - sbgn.xmlns = attributes.xmlns || null; + // @reverse must be an object + if(expandedProperty === '@reverse') { + if(!_isObject(value)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@reverse" value must be an object.', + 'jsonld.SyntaxError', {code: 'invalid @reverse value', value: value}); + } - // getting attribute with 'xmlns' doesn't work if some namespace is defined like 'xmlns:sbgn' - // so if there is some attribute there, and we didn't find the xmlns directly, we need to into it - if(!sbgn.xmlns && Object.keys(attributes).length > 0) { - // sbgn is not supposed to have any other attribute than an xmlns, so we assume the first attr is the xmlns - var key = Object.keys(attributes)[0]; - if(key.startsWith('xmlns')) { - sbgn.xmlns = attributes[key]; - sbgn.namespacePrefix = key.replace('xmlns:', ''); - } - else { - throw new Error("Couldn't find xmlns definition in sbgn element"); - } - } - } + expandedValue = self.expand(activeCtx, '@reverse', value, options); - if(jsObj.map) { - var maps = jsObj.map; - for (var i=0; i < maps.length; i++) { - var map = ns.Map.fromObj({map: maps[i]}); - sbgn.addMap(map); - } - } + // properties double-reversed + if('@reverse' in expandedValue) { + for(var property in expandedValue['@reverse']) { + jsonld.addValue( + rval, property, expandedValue['@reverse'][property], + {propertyIsArray: true}); + } + } - sbgn.baseFromObj(jsObj); // call to parent class - return sbgn; -}; -ns.Sbgn = Sbgn; -// ------- END SBGN ------- + // FIXME: can this be merged with code below to simplify? + // merge in all reversed properties + var reverseMap = rval['@reverse'] || null; + for(var property in expandedValue) { + if(property === '@reverse') { + continue; + } + if(reverseMap === null) { + reverseMap = rval['@reverse'] = {}; + } + jsonld.addValue(reverseMap, property, [], {propertyIsArray: true}); + var items = expandedValue[property]; + for(var ii = 0; ii < items.length; ++ii) { + var item = items[ii]; + if(_isValue(item) || _isList(item)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@reverse" value must not be a ' + + '@value or an @list.', 'jsonld.SyntaxError', + {code: 'invalid reverse property value', value: expandedValue}); + } + jsonld.addValue( + reverseMap, property, item, {propertyIsArray: true}); + } + } -// ------- MAP ------- -/** - * Represents the <map> element. - * @class - * @extends SBGNBase - * @param {Object} params - * @param {string=} params.id - * @param {string=} params.language - * @param {string=} params.version - * @param {Glyph[]=} params.glyphs - * @param {Arc[]=} params.arcs - * @param {Bbox=} params.bbox - * @param {Arcgroup[]=} params.arcgroups - */ -var Map = function (params) { - ns.SBGNBase.call(this, params); - var params = checkParams(params, ['id', 'language', 'version', 'glyphs', 'arcs', 'bbox', 'arcgroups']); - this.id = params.id; - this.language = params.language; - this.version = params.version; - this.bbox = params.bbox; - this.glyphs = params.glyphs || []; - this.arcs = params.arcs || []; - this.arcgroups = params.arcgroups || []; -}; + continue; + } -Map.prototype = Object.create(ns.SBGNBase.prototype); -Map.prototype.constructor = Map; + var container = jsonld.getContextValue(activeCtx, key, '@container'); -/** - * @param {Glyph} glyph - */ -Map.prototype.addGlyph = function (glyph) { - this.glyphs.push(glyph); -}; + if(container === '@language' && _isObject(value)) { + // handle language map container (skip if value is not an object) + expandedValue = _expandLanguageMap(value); + } else if(container === '@index' && _isObject(value)) { + // handle index container (skip if value is not an object) + expandedValue = (function _expandIndexMap(activeProperty) { + var rval = []; + var keys = Object.keys(value).sort(); + for(var ki = 0; ki < keys.length; ++ki) { + var key = keys[ki]; + var val = value[key]; + if(!_isArray(val)) { + val = [val]; + } + val = self.expand(activeCtx, activeProperty, val, options, false); + for(var vi = 0; vi < val.length; ++vi) { + var item = val[vi]; + if(!('@index' in item)) { + item['@index'] = key; + } + rval.push(item); + } + } + return rval; + })(key); + } else { + // recurse into @list or @set + var isList = (expandedProperty === '@list'); + if(isList || expandedProperty === '@set') { + var nextActiveProperty = activeProperty; + if(isList && expandedActiveProperty === '@graph') { + nextActiveProperty = null; + } + expandedValue = self.expand( + activeCtx, nextActiveProperty, value, options, isList); + if(isList && _isList(expandedValue)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; lists of lists are not permitted.', + 'jsonld.SyntaxError', {code: 'list of lists'}); + } + } else { + // recursively expand value with key as new active property + expandedValue = self.expand(activeCtx, key, value, options, false); + } + } -/** - * @param {Arc} arc - */ -Map.prototype.addArc = function (arc) { - this.arcs.push(arc); -}; + // drop null values if property is not @value + if(expandedValue === null && expandedProperty !== '@value') { + continue; + } -/** - * @param {Bbox} bbox - */ -Map.prototype.setBbox = function (bbox) { - this.bbox = bbox; + // convert expanded value to @list if container specifies it + if(expandedProperty !== '@list' && !_isList(expandedValue) && + container === '@list') { + // ensure expanded value is an array + expandedValue = (_isArray(expandedValue) ? + expandedValue : [expandedValue]); + expandedValue = {'@list': expandedValue}; + } + + // FIXME: can this be merged with code above to simplify? + // merge in reverse properties + if(activeCtx.mappings[key] && activeCtx.mappings[key].reverse) { + var reverseMap = rval['@reverse'] = rval['@reverse'] || {}; + if(!_isArray(expandedValue)) { + expandedValue = [expandedValue]; + } + for(var ii = 0; ii < expandedValue.length; ++ii) { + var item = expandedValue[ii]; + if(_isValue(item) || _isList(item)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@reverse" value must not be a ' + + '@value or an @list.', 'jsonld.SyntaxError', + {code: 'invalid reverse property value', value: expandedValue}); + } + jsonld.addValue( + reverseMap, expandedProperty, item, {propertyIsArray: true}); + } + continue; + } + + // add value for property + // use an array except for certain keywords + var useArray = + ['@index', '@id', '@type', '@value', '@language'].indexOf( + expandedProperty) === -1; + jsonld.addValue( + rval, expandedProperty, expandedValue, {propertyIsArray: useArray}); + } + + // get property count on expanded output + keys = Object.keys(rval); + var count = keys.length; + + if('@value' in rval) { + // @value must only have @language or @type + if('@type' in rval && '@language' in rval) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; an element containing "@value" may not ' + + 'contain both "@type" and "@language".', + 'jsonld.SyntaxError', {code: 'invalid value object', element: rval}); + } + var validCount = count - 1; + if('@type' in rval) { + validCount -= 1; + } + if('@index' in rval) { + validCount -= 1; + } + if('@language' in rval) { + validCount -= 1; + } + if(validCount !== 0) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; an element containing "@value" may only ' + + 'have an "@index" property and at most one other property ' + + 'which can be "@type" or "@language".', + 'jsonld.SyntaxError', {code: 'invalid value object', element: rval}); + } + // drop null @values + if(rval['@value'] === null) { + rval = null; + } else if('@language' in rval && !_isString(rval['@value'])) { + // if @language is present, @value must be a string + throw new JsonLdError( + 'Invalid JSON-LD syntax; only strings may be language-tagged.', + 'jsonld.SyntaxError', + {code: 'invalid language-tagged value', element: rval}); + } else if('@type' in rval && (!_isAbsoluteIri(rval['@type']) || + rval['@type'].indexOf('_:') === 0)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; an element containing "@value" and "@type" ' + + 'must have an absolute IRI for the value of "@type".', + 'jsonld.SyntaxError', {code: 'invalid typed value', element: rval}); + } + } else if('@type' in rval && !_isArray(rval['@type'])) { + // convert @type to an array + rval['@type'] = [rval['@type']]; + } else if('@set' in rval || '@list' in rval) { + // handle @set and @list + if(count > 1 && !(count === 2 && '@index' in rval)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; if an element has the property "@set" ' + + 'or "@list", then it can have at most one other property that is ' + + '"@index".', 'jsonld.SyntaxError', + {code: 'invalid set or list object', element: rval}); + } + // optimize away @set + if('@set' in rval) { + rval = rval['@set']; + keys = Object.keys(rval); + count = keys.length; + } + } else if(count === 1 && '@language' in rval) { + // drop objects with only @language + rval = null; + } + + // drop certain top-level objects that do not occur in lists + if(_isObject(rval) && + !options.keepFreeFloatingNodes && !insideList && + (activeProperty === null || expandedActiveProperty === '@graph')) { + // drop empty object, top-level @value/@list, or object with only @id + if(count === 0 || '@value' in rval || '@list' in rval || + (count === 1 && '@id' in rval)) { + rval = null; + } + } + + return rval; }; /** - * @param {Arcgroup} arc + * Creates a JSON-LD node map (node ID => node). + * + * @param input the expanded JSON-LD to create a node map of. + * @param [options] the options to use: + * [issuer] a jsonld.IdentifierIssuer to use to label blank nodes. + * [namer] (deprecated). + * + * @return the node map. */ -Map.prototype.addArcgroup = function (arcgroup) { - this.arcgroups.push(arcgroup); +Processor.prototype.createNodeMap = function(input, options) { + options = options || {}; + + // produce a map of all subjects and name each bnode + var issuer = options.namer || options.issuer || new IdentifierIssuer('_:b'); + var graphs = {'@default': {}}; + _createNodeMap(input, graphs, '@default', issuer); + + // add all non-default graphs to default graph + return _mergeNodeMaps(graphs); }; /** - * @param {string} class_ - * @return {Gyph[]} + * Performs JSON-LD flattening. + * + * @param input the expanded JSON-LD to flatten. + * + * @return the flattened output. */ -Map.prototype.getGlyphsByClass = function (class_) { - var resultGlyphs = []; - for(var i=0; i < this.glyphs.length; i++) { - var glyph = this.glyphs[i]; - if(glyph.class_ == class_) { - resultGlyphs.push(glyph); - } - } - return resultGlyphs; +Processor.prototype.flatten = function(input) { + var defaultGraph = this.createNodeMap(input); + + // produce flattened output + var flattened = []; + var keys = Object.keys(defaultGraph).sort(); + for(var ki = 0; ki < keys.length; ++ki) { + var node = defaultGraph[keys[ki]]; + // only add full subjects to top-level + if(!_isSubjectReference(node)) { + flattened.push(node); + } + } + return flattened; }; /** - * @return {Object} - xml2js formatted object + * Performs JSON-LD framing. + * + * @param input the expanded JSON-LD to frame. + * @param frame the expanded JSON-LD frame to use. + * @param options the framing options. + * + * @return the framed output. */ -Map.prototype.buildJsObj = function () { - var mapObj = {}; +Processor.prototype.frame = function(input, frame, options) { + // create framing state + var state = { + options: options, + graphs: {'@default': {}, '@merged': {}}, + subjectStack: [], + link: {} + }; - // attributes - var attributes = {}; - if(this.id != null) { - attributes.id = this.id; - } - if(this.language != null) { - attributes.language = this.language; - } - if(this.version != null) { - attributes.version = this.version; - } - utils.addAttributes(mapObj, attributes); + // produce a map of all graphs and name each bnode + // FIXME: currently uses subjects from @merged graph only + var issuer = new IdentifierIssuer('_:b'); + _createNodeMap(input, state.graphs, '@merged', issuer); + state.subjects = state.graphs['@merged']; - // children - this.baseToJsObj(mapObj); - if(this.bbox != null) { - mapObj.bbox = this.bbox.buildJsObj(); - } - for(var i=0; i < this.glyphs.length; i++) { - if (i==0) { - mapObj.glyph = []; - } - mapObj.glyph.push(this.glyphs[i].buildJsObj()); - } - for(var i=0; i < this.arcs.length; i++) { - if (i==0) { - mapObj.arc = []; - } - mapObj.arc.push(this.arcs[i].buildJsObj()); - } - for(var i=0; i < this.arcgroups.length; i++) { - if (i==0) { - mapObj.arcgroup = []; - } - mapObj.arcgroup.push(this.arcgroups[i].buildJsObj()); - } - return mapObj; + // frame the subjects + var framed = []; + _frame(state, Object.keys(state.subjects).sort(), frame, framed, null); + return framed; }; /** - * @return {string} + * Performs normalization on the given RDF dataset. + * + * @param dataset the RDF dataset to normalize. + * @param options the normalization options. + * @param callback(err, normalized) called once the operation completes. */ -Map.prototype.toXML = function () { - return utils.buildString({map: this.buildJsObj()}); +Processor.prototype.normalize = function(dataset, options, callback) { + if(options.algorithm === 'URDNA2015') { + return new URDNA2015(options).main(dataset, callback); + } + if(options.algorithm === 'URGNA2012') { + return new URGNA2012(options).main(dataset, callback); + } + callback(new Error( + 'Invalid RDF Dataset Normalization algorithm: ' + options.algorithm)); }; /** - * @param {String} string - * @return {Map} + * Converts an RDF dataset to JSON-LD. + * + * @param dataset the RDF dataset. + * @param options the RDF serialization options. + * @param callback(err, output) called once the operation completes. */ -Map.fromXML = function (string) { - var map; - function fn (err, result) { - map = Map.fromObj(result); - }; - utils.parseString(string, fn); - return map; -}; +Processor.prototype.fromRDF = function(dataset, options, callback) { + var defaultGraph = {}; + var graphMap = {'@default': defaultGraph}; + var referencedOnce = {}; -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Map} - */ -Map.fromObj = function (jsObj) { - if (typeof jsObj.map == 'undefined') { - throw new Error("Bad XML provided, expected tagName map, got: " + Object.keys(jsObj)[0]); - } + for(var name in dataset) { + var graph = dataset[name]; + if(!(name in graphMap)) { + graphMap[name] = {}; + } + if(name !== '@default' && !(name in defaultGraph)) { + defaultGraph[name] = {'@id': name}; + } + var nodeMap = graphMap[name]; + for(var ti = 0; ti < graph.length; ++ti) { + var triple = graph[ti]; - var map = new ns.Map(); - jsObj = jsObj.map; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return map; - } + // get subject, predicate, object + var s = triple.subject.value; + var p = triple.predicate.value; + var o = triple.object; - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - map.id = attributes.id || null; - map.language = attributes.language || null; - map.version = attributes.version || null; - } + if(!(s in nodeMap)) { + nodeMap[s] = {'@id': s}; + } + var node = nodeMap[s]; - if(jsObj.bbox) { - var bbox = ns.Bbox.fromObj({bbox: jsObj.bbox[0]}); - map.setBbox(bbox); - } - if(jsObj.glyph) { - var glyphs = jsObj.glyph; - for (var i=0; i < glyphs.length; i++) { - var glyph = ns.Glyph.fromObj({glyph: glyphs[i]}); - map.addGlyph(glyph); - } - } - if(jsObj.arc) { - var arcs = jsObj.arc; - for (var i=0; i < arcs.length; i++) { - var arc = ns.Arc.fromObj({arc: arcs[i]}); - map.addArc(arc); - } - } - if(jsObj.arcgroup) { - var arcgroups = jsObj.arcgroup; - for (var i=0; i < arcgroups.length; i++) { - var arcgroup = ns.Arcgroup.fromObj({arcgroup: arcgroups[i]}); - map.addArcgroup(arcgroup); - } - } + var objectIsId = (o.type === 'IRI' || o.type === 'blank node'); + if(objectIsId && !(o.value in nodeMap)) { + nodeMap[o.value] = {'@id': o.value}; + } - map.baseFromObj(jsObj); - return map; -}; + if(p === RDF_TYPE && !options.useRdfType && objectIsId) { + jsonld.addValue(node, '@type', o.value, {propertyIsArray: true}); + continue; + } -ns.Map = Map; -// ------- END MAP ------- + var value = _RDFToObject(o, options.useNativeTypes); + jsonld.addValue(node, p, value, {propertyIsArray: true}); -// ------- EXTENSION ------- -/** - * Represents the <extension> element. - * @class - */ -var Extension = function () { - // consider first order children, add them with their tagname as property of this object - // store string if no supported parsing (unrecognized extensions) - // else store instance of the extension - this.list = {}; -}; + // object may be an RDF list/partial list node but we can't know easily + // until all triples are read + if(objectIsId) { + if(o.value === RDF_NIL) { + // track rdf:nil uniquely per graph + var object = nodeMap[o.value]; + if(!('usages' in object)) { + object.usages = []; + } + object.usages.push({ + node: node, + property: p, + value: value + }); + } else if(o.value in referencedOnce) { + // object referenced more than once + referencedOnce[o.value] = false; + } else { + // keep track of single reference + referencedOnce[o.value] = { + node: node, + property: p, + value: value + }; + } + } + } + } -/** - * @param {String|render.RenderInformation|annot.Annotation} extension - */ -Extension.prototype.add = function (extension) { - if (extension instanceof renderExt.RenderInformation) { - this.list['renderInformation'] = extension; - } - else if (extension instanceof annotExt.Annotation) { - this.list['annotation'] = extension; - } - else if(typeof extension == "string") { - var parsedAsObj; - function fn (err, result) { - parsedAsObj = result; - }; - utils.parseString(extension, fn); - var name = Object.keys(parsedAsObj)[0]; - if(name == "renderInformation") { - var renderInformation = renderExt.RenderInformation.fromXML(extension); - this.list['renderInformation'] = renderInformation; - } - else if(name == "annotation") { - var annotation = annotExt.Annotation.fromXML(extension); - this.list['annotation'] = renderInformation; - } - else { - this.list[name] = extension; - } - } -}; + // convert linked lists to @list arrays + for(var name in graphMap) { + var graphObject = graphMap[name]; -/** - * @param {string} extensionName - * @return {boolean} - */ -Extension.prototype.has = function (extensionName) { - return this.list.hasOwnProperty(extensionName); -}; + // no @lists to be converted, continue + if(!(RDF_NIL in graphObject)) { + continue; + } -/** - * @param {string} extensionName - * @return {String|render.RenderInformation|annot.Annotation} - */ -Extension.prototype.get = function (extensionName) { - if (this.has(extensionName)) { - return this.list[extensionName]; - } - else { - return null; - } -}; + // iterate backwards through each RDF list + var nil = graphObject[RDF_NIL]; + for(var i = 0; i < nil.usages.length; ++i) { + var usage = nil.usages[i]; + var node = usage.node; + var property = usage.property; + var head = usage.value; + var list = []; + var listNodes = []; -/** - * @return {Object} - xml2js formatted object - */ -Extension.prototype.buildJsObj = function () { - var extensionObj = {}; + // ensure node is a well-formed list node; it must: + // 1. Be referenced only once. + // 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, rdf:first, rdf:rest, and, + // optionally, @type where the value is rdf:List. + var nodeKeyCount = Object.keys(node).length; + while(property === RDF_REST && + _isObject(referencedOnce[node['@id']]) && + _isArray(node[RDF_FIRST]) && node[RDF_FIRST].length === 1 && + _isArray(node[RDF_REST]) && node[RDF_REST].length === 1 && + (nodeKeyCount === 3 || (nodeKeyCount === 4 && _isArray(node['@type']) && + node['@type'].length === 1 && node['@type'][0] === RDF_LIST))) { + list.push(node[RDF_FIRST][0]); + listNodes.push(node['@id']); - for (var extInstance in this.list) { - if (extInstance == "renderInformation") { - extensionObj.renderInformation = this.get(extInstance).buildJsObj(); - } - else if (extInstance == "annotation") { - extensionObj.annotation = this.get(extInstance).buildJsObj(); - } - else { - // unsupported extensions are stored as is, as xml string - // we need to parse it to build the js object - var unsupportedExtObj; - function fn (err, result) { - unsupportedExtObj = result; - }; - utils.parseString(this.get(extInstance), fn); - extensionObj[extInstance] = unsupportedExtObj[extInstance]; - } - } - return extensionObj; -}; + // get next node, moving backwards through list + usage = referencedOnce[node['@id']]; + node = usage.node; + property = usage.property; + head = usage.value; + nodeKeyCount = Object.keys(node).length; -/** - * @return {string} - */ -Extension.prototype.toXML = function () { - return utils.buildString({extension: this.buildJsObj()}) -}; + // if node is not a blank node, then list head found + if(node['@id'].indexOf('_:') !== 0) { + break; + } + } -/** - * @param {String} string - * @return {Extension} - */ -Extension.fromXML = function (string) { - var extension; - function fn (err, result) { - extension = Extension.fromObj(result); - }; - utils.parseString(string, fn); - return extension; -}; + // the list is nested in another list + if(property === RDF_FIRST) { + // empty list + if(node['@id'] === RDF_NIL) { + // can't convert rdf:nil to a @list object because it would + // result in a list of lists which isn't supported + continue; + } -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Extension} - */ -Extension.fromObj = function (jsObj) { - if (typeof jsObj.extension == 'undefined') { - throw new Error("Bad XML provided, expected tagName extension, got: " + Object.keys(jsObj)[0]); - } + // preserve list head + head = graphObject[head['@id']][RDF_REST][0]; + list.pop(); + listNodes.pop(); + } - var extension = new Extension(); - jsObj = jsObj.extension; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return extension; - } + // transform list into @list object + delete head['@id']; + head['@list'] = list.reverse(); + for(var j = 0; j < listNodes.length; ++j) { + delete graphObject[listNodes[j]]; + } + } - //var children = Object.keys(jsObj); - for (var extName in jsObj) { - //var extName = Object.keys(jsObj[i])[0]; - var extJsObj = jsObj[extName]; + delete nil.usages; + } - //extension.add(extInstance); - if (extName === 'renderInformation' || - extName.endsWith(':renderInformation')) { - var renderInformation = renderExt.RenderInformation.fromObj({renderInformation: extJsObj[0]}); - extension.add(renderInformation); - } - else if (extName == 'annotation') { - var annotation = annotExt.Annotation.fromObj({annotation: extJsObj[0]}); - extension.add(annotation); - } - else { // unsupported extension, we still store the data as is - var unsupportedExt = {}; - unsupportedExt[extName] = extJsObj[0]; // make extension serialisable - var stringExt = utils.buildString(unsupportedExt); // serialise to string - extension.add(stringExt); // save it - } - } + var result = []; + var subjects = Object.keys(defaultGraph).sort(); + for(var i = 0; i < subjects.length; ++i) { + var subject = subjects[i]; + var node = defaultGraph[subject]; + if(subject in graphMap) { + var graph = node['@graph'] = []; + var graphObject = graphMap[subject]; + var subjects_ = Object.keys(graphObject).sort(); + for(var si = 0; si < subjects_.length; ++si) { + var node_ = graphObject[subjects_[si]]; + // only add full subjects to top-level + if(!_isSubjectReference(node_)) { + graph.push(node_); + } + } + } + // only add full subjects to top-level + if(!_isSubjectReference(node)) { + result.push(node); + } + } - return extension; + callback(null, result); }; -ns.Extension = Extension; -// ------- END EXTENSION ------- - -// ------- NOTES ------- /** - * Represents the <notes> element. - * Its single content attribute stores xhtml elements as string. - * @class + * Outputs an RDF dataset for the expanded JSON-LD input. + * + * @param input the expanded JSON-LD input. + * @param options the RDF serialization options. + * + * @return the RDF dataset. */ -var Notes = function () { - this.content = ""; -}; +Processor.prototype.toRDF = function(input, options) { + // create node map for default graph (and any named graphs) + var issuer = new IdentifierIssuer('_:b'); + var nodeMap = {'@default': {}}; + _createNodeMap(input, nodeMap, '@default', issuer); -/** - * Overwrite the content. - * @param {String} string - */ -Notes.prototype.setContent = function (string) { - this.content = string; + var dataset = {}; + var graphNames = Object.keys(nodeMap).sort(); + for(var i = 0; i < graphNames.length; ++i) { + var graphName = graphNames[i]; + // skip relative IRIs + if(graphName === '@default' || _isAbsoluteIri(graphName)) { + dataset[graphName] = _graphToRDF(nodeMap[graphName], issuer, options); + } + } + return dataset; }; /** - * @param {String} string + * Processes a local context and returns a new active context. + * + * @param activeCtx the current active context. + * @param localCtx the local context to process. + * @param options the context processing options. + * + * @return the new active context. */ -Notes.prototype.appendContent = function (string) { - this.content += string; -}; +Processor.prototype.processContext = function(activeCtx, localCtx, options) { + // normalize local context to an array of @context objects + if(_isObject(localCtx) && '@context' in localCtx && + _isArray(localCtx['@context'])) { + localCtx = localCtx['@context']; + } + var ctxs = _isArray(localCtx) ? localCtx : [localCtx]; -/** - * @return {Object} - xml2js formatted object - */ -Notes.prototype.buildJsObj = function () { + // no contexts in array, clone existing context + if(ctxs.length === 0) { + return activeCtx.clone(); + } - var parsedContent = ""; - if(this.content != "") { // xml2js refuses to parse empty strings - utils.parseString(this.content, function (err, result) { - parsedContent = result; - }); - } + // process each context in order, update active context + // on each iteration to ensure proper caching + var rval = activeCtx; + for(var i = 0; i < ctxs.length; ++i) { + var ctx = ctxs[i]; - return parsedContent; -}; + // reset to initial context + if(ctx === null) { + rval = activeCtx = _getInitialContext(options); + continue; + } -/** - * @return {string} - */ -Notes.prototype.toXML = function () { - return utils.buildString({notes: this.buildJsObj()}) -}; + // dereference @context key if present + if(_isObject(ctx) && '@context' in ctx) { + ctx = ctx['@context']; + } -/** - * @param {String} string - * @return {Notes} - */ -Notes.fromXML = function (string) { - var notes; - function fn (err, result) { - notes = Notes.fromObj(result); - }; - utils.parseString(string, fn); - return notes; -}; + // context must be an object by now, all URLs retrieved before this call + if(!_isObject(ctx)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; @context must be an object.', + 'jsonld.SyntaxError', {code: 'invalid local context', context: ctx}); + } -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Notes} - */ -Notes.fromObj = function (jsObj) { - if (typeof jsObj.notes == 'undefined') { - throw new Error("Bad XML provided, expected tagName notes, got: " + Object.keys(jsObj)[0]); - } + // get context from cache if available + if(jsonld.cache.activeCtx) { + var cached = jsonld.cache.activeCtx.get(activeCtx, ctx); + if(cached) { + rval = activeCtx = cached; + continue; + } + } - var notes = new Notes(); - jsObj = jsObj.notes; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return notes; - } + // update active context and clone new one before updating + activeCtx = rval; + rval = rval.clone(); - var stringExt = utils.buildString({notes: jsObj}); // serialise to string - // xml2js does weird things when you just want to serialize the content - // need to include the root to get it properly, and then remove it in the result string. - stringExt = stringExt.replace('', ''); - stringExt = stringExt.replace('', ''); - notes.content = stringExt; // save it + // define context mappings for keys in local context + var defined = {}; - return notes; -}; + // handle @base + if('@base' in ctx) { + var base = ctx['@base']; -ns.Notes = Notes; -// ------- END NOTES ------- + // clear base + if(base === null) { + base = null; + } else if(!_isString(base)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; the value of "@base" in a ' + + '@context must be a string or null.', + 'jsonld.SyntaxError', {code: 'invalid base IRI', context: ctx}); + } else if(base !== '' && !_isAbsoluteIri(base)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; the value of "@base" in a ' + + '@context must be an absolute IRI or the empty string.', + 'jsonld.SyntaxError', {code: 'invalid base IRI', context: ctx}); + } -// ------- GLYPH ------- -/** - * Represents the <glyph> element. - * @class Glyph - * @extends SBGNBase - * @param {Object} params - * @param {string=} params.id - * @param {string=} params.class_ - * @param {string=} params.compartmentRef - * @param {string|number=} params.compartmentOrder - * @param {string=} params.mapRef - * @param {string=} params.tagRef - * @param {string=} params.orientation - * @param {Label=} params.label - * @param {Bbox=} params.bbox - * @param {State=} params.state - * @param {Clone=} params.clone - * @param {Callout=} params.callout - * @param {Entity=} params.entity - * @param {Glyph[]=} params.glyphMembers - * @param {Port[]=} params.ports - */ -var Glyph = function (params) { - ns.SBGNBase.call(this, params); - var params = checkParams(params, ['id', 'class_', 'compartmentRef', 'compartmentOrder', 'mapRef', - 'tagRef', 'orientation', 'label', 'bbox', 'glyphMembers', 'ports', 'state', 'clone', 'entity', 'callout']); - this.id = params.id; - this.class_ = params.class_; - this.compartmentRef = params.compartmentRef; - this.compartmentOrder = parseFloat(params.compartmentOrder); - this.mapRef = params.mapRef; - this.tagRef = params.tagRef; - this.orientation = params.orientation; + if(base !== null) { + base = jsonld.url.parse(base || ''); + } + rval['@base'] = base; + defined['@base'] = true; + } - // children - this.label = params.label; - this.state = params.state; - this.clone = params.clone; - this.callout = params.callout; - this.entity = params.entity; - this.bbox = params.bbox; - this.glyphMembers = params.glyphMembers || []; // case of complex, can have arbitrary list of nested glyphs - this.ports = params.ports || []; -}; + // handle @vocab + if('@vocab' in ctx) { + var value = ctx['@vocab']; + if(value === null) { + delete rval['@vocab']; + } else if(!_isString(value)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; the value of "@vocab" in a ' + + '@context must be a string or null.', + 'jsonld.SyntaxError', {code: 'invalid vocab mapping', context: ctx}); + } else if(!_isAbsoluteIri(value)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; the value of "@vocab" in a ' + + '@context must be an absolute IRI.', + 'jsonld.SyntaxError', {code: 'invalid vocab mapping', context: ctx}); + } else { + rval['@vocab'] = value; + } + defined['@vocab'] = true; + } -Glyph.prototype = Object.create(ns.SBGNBase.prototype); -Glyph.prototype.constructor = Glyph; + // handle @language + if('@language' in ctx) { + var value = ctx['@language']; + if(value === null) { + delete rval['@language']; + } else if(!_isString(value)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; the value of "@language" in a ' + + '@context must be a string or null.', + 'jsonld.SyntaxError', + {code: 'invalid default language', context: ctx}); + } else { + rval['@language'] = value.toLowerCase(); + } + defined['@language'] = true; + } -/** - * @param {Label} label - */ -Glyph.prototype.setLabel = function (label) { - this.label = label; -}; + // process all other keys + for(var key in ctx) { + _createTermDefinition(rval, ctx, key, defined); + } -/** - * @param {State} state - */ -Glyph.prototype.setState = function (state) { - this.state = state; -}; - -/** - * @param {Bbox} bbox - */ -Glyph.prototype.setBbox = function (bbox) { - this.bbox = bbox; -}; + // cache result + if(jsonld.cache.activeCtx) { + jsonld.cache.activeCtx.set(activeCtx, ctx, rval); + } + } -/** - * @param {Clone} clone - */ -Glyph.prototype.setClone = function (clone) { - this.clone = clone; + return rval; }; /** - * @param {Callout} callout + * Expands a language map. + * + * @param languageMap the language map to expand. + * + * @return the expanded language map. */ -Glyph.prototype.setCallout = function (callout) { - this.callout = callout; -}; +function _expandLanguageMap(languageMap) { + var rval = []; + var keys = Object.keys(languageMap).sort(); + for(var ki = 0; ki < keys.length; ++ki) { + var key = keys[ki]; + var val = languageMap[key]; + if(!_isArray(val)) { + val = [val]; + } + for(var vi = 0; vi < val.length; ++vi) { + var item = val[vi]; + if(item === null) { + // null values are allowed (8.5) but ignored (3.1) + continue; + } + if(!_isString(item)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; language map values must be strings.', + 'jsonld.SyntaxError', + {code: 'invalid language map value', languageMap: languageMap}); + } + rval.push({ + '@value': item, + '@language': key.toLowerCase() + }); + } + } + return rval; +} /** - * @param {Entity} entity + * Labels the blank nodes in the given value using the given IdentifierIssuer. + * + * @param issuer the IdentifierIssuer to use. + * @param element the element with blank nodes to rename. + * + * @return the element. */ -Glyph.prototype.setEntity = function (entity) { - this.entity = entity; -}; +function _labelBlankNodes(issuer, element) { + if(_isArray(element)) { + for(var i = 0; i < element.length; ++i) { + element[i] = _labelBlankNodes(issuer, element[i]); + } + } else if(_isList(element)) { + element['@list'] = _labelBlankNodes(issuer, element['@list']); + } else if(_isObject(element)) { + // relabel blank node + if(_isBlankNode(element)) { + element['@id'] = issuer.getId(element['@id']); + } -/** - * @param {Glyph} glyphMember - */ -Glyph.prototype.addGlyphMember = function (glyphMember) { - this.glyphMembers.push(glyphMember); -}; + // recursively apply to all keys + var keys = Object.keys(element).sort(); + for(var ki = 0; ki < keys.length; ++ki) { + var key = keys[ki]; + if(key !== '@id') { + element[key] = _labelBlankNodes(issuer, element[key]); + } + } + } -/** - * @param {Port} port - */ -Glyph.prototype.addPort = function (port) { - this.ports.push(port); -}; + return element; +} /** - * @return {Object} - xml2js formatted object + * Expands the given value by using the coercion and keyword rules in the + * given context. + * + * @param activeCtx the active context to use. + * @param activeProperty the active property the value is associated with. + * @param value the value to expand. + * + * @return the expanded value. */ -Glyph.prototype.buildJsObj = function () { - var glyphObj = {}; +function _expandValue(activeCtx, activeProperty, value) { + // nothing to expand + if(value === null || value === undefined) { + return null; + } - // attributes - var attributes = {}; - if(this.id != null) { - attributes.id = this.id; - } - if(this.class_ != null) { - attributes.class = this.class_; - } - if(this.compartmentRef != null) { - attributes.compartmentRef = this.compartmentRef; - } - if(!isNaN(this.compartmentOrder)) { - attributes.compartmentOrder = this.compartmentOrder; - } - if(this.mapRef != null) { - attributes.mapRef = this.mapRef; - } - if(this.tagRef != null) { - attributes.tagRef = this.tagRef; - } - if(this.orientation != null) { - attributes.orientation = this.orientation; - } - utils.addAttributes(glyphObj, attributes); + // special-case expand @id and @type (skips '@id' expansion) + var expandedProperty = _expandIri(activeCtx, activeProperty, {vocab: true}); + if(expandedProperty === '@id') { + return _expandIri(activeCtx, value, {base: true}); + } else if(expandedProperty === '@type') { + return _expandIri(activeCtx, value, {vocab: true, base: true}); + } - // children - this.baseToJsObj(glyphObj); - if(this.label != null) { - glyphObj.label = this.label.buildJsObj(); - } - if(this.state != null) { - glyphObj.state = this.state.buildJsObj(); - } - if(this.clone != null) { - glyphObj.clone = this.clone.buildJsObj(); - } - if(this.callout != null) { - glyphObj.callout = this.callout.buildJsObj(); - } - if(this.entity != null) { - glyphObj.entity = this.entity.buildJsObj(); - } - if(this.bbox != null) { - glyphObj.bbox = this.bbox.buildJsObj(); - } - for(var i=0; i < this.glyphMembers.length; i++) { - if (i==0) { - glyphObj.glyph = []; - } - glyphObj.glyph.push(this.glyphMembers[i].buildJsObj()); - } - for(var i=0; i < this.ports.length; i++) { - if (i==0) { - glyphObj.port = []; - } - glyphObj.port.push(this.ports[i].buildJsObj()); - } - return glyphObj; -}; + // get type definition from context + var type = jsonld.getContextValue(activeCtx, activeProperty, '@type'); -/** - * @return {string} - */ -Glyph.prototype.toXML = function () { - return utils.buildString({glyph: this.buildJsObj()}) -}; + // do @id expansion (automatic for @graph) + if(type === '@id' || (expandedProperty === '@graph' && _isString(value))) { + return {'@id': _expandIri(activeCtx, value, {base: true})}; + } + // do @id expansion w/vocab + if(type === '@vocab') { + return {'@id': _expandIri(activeCtx, value, {vocab: true, base: true})}; + } -/** - * @param {String} string - * @return {Glyph} - */ -Glyph.fromXML = function (string) { - var glyph; - function fn (err, result) { - glyph = Glyph.fromObj(result); - }; - utils.parseString(string, fn); - return glyph; -}; + // do not expand keyword values + if(_isKeyword(expandedProperty)) { + return value; + } -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Glyph} - */ -Glyph.fromObj = function (jsObj) { - if (typeof jsObj.glyph == 'undefined') { - throw new Error("Bad XML provided, expected tagName glyph, got: " + Object.keys(jsObj)[0]); - } + var rval = {}; - var glyph = new ns.Glyph(); - jsObj = jsObj.glyph; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return glyph; - } + if(type !== null) { + // other type + rval['@type'] = type; + } else if(_isString(value)) { + // check for language tagging for strings + var language = jsonld.getContextValue( + activeCtx, activeProperty, '@language'); + if(language !== null) { + 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; - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - glyph.id = attributes.id || null; - glyph.class_ = attributes.class || null; - glyph.compartmentRef = attributes.compartmentRef || null; - glyph.compartmentOrder = parseFloat(attributes.compartmentOrder); - glyph.mapRef = attributes.mapRef || null; - glyph.tagRef = attributes.tagRef || null; - glyph.orientation = attributes.orientation || null; - } + return rval; +} - // children - if(jsObj.label) { - var label = ns.Label.fromObj({label: jsObj.label[0]}); - glyph.setLabel(label); - } - if(jsObj.state) { - var state = ns.State.fromObj({state: jsObj.state[0]}); - glyph.setState(state); - } - if(jsObj.clone) { - var clone = ns.Clone.fromObj({clone: jsObj.clone[0]}); - glyph.setClone(clone); - } - if(jsObj.callout) { - var callout = ns.Callout.fromObj({callout: jsObj.callout[0]}); - glyph.setCallout(callout); - } - if(jsObj.entity) { - var entity = ns.Entity.fromObj({entity: jsObj.entity[0]}); - glyph.setEntity(entity); - } - if(jsObj.bbox) { - var bbox = ns.Bbox.fromObj({bbox: jsObj.bbox[0]}); - glyph.setBbox(bbox); - } +/** + * Creates an array of RDF triples for the given graph. + * + * @param graph the graph to create RDF triples for. + * @param issuer a IdentifierIssuer for assigning blank node names. + * @param options the RDF serialization options. + * + * @return the array of RDF triples for the given graph. + */ +function _graphToRDF(graph, issuer, options) { + var rval = []; - if(jsObj.glyph) { - var glyphs = jsObj.glyph; - for (var i=0; i < glyphs.length; i++) { - var glyphMember = ns.Glyph.fromObj({glyph: glyphs[i]}); - glyph.addGlyphMember(glyphMember); - } - } - if(jsObj.port) { - var ports = jsObj.port; - for (var i=0; i < ports.length; i++) { - var port = ns.Port.fromObj({port: ports[i]}); - glyph.addPort(port); - } - } + var ids = Object.keys(graph).sort(); + for(var i = 0; i < ids.length; ++i) { + var id = ids[i]; + var node = graph[id]; + var properties = Object.keys(node).sort(); + for(var pi = 0; pi < properties.length; ++pi) { + var property = properties[pi]; + var items = node[property]; + if(property === '@type') { + property = RDF_TYPE; + } else if(_isKeyword(property)) { + continue; + } - glyph.baseFromObj(jsObj); - return glyph; -}; + for(var ii = 0; ii < items.length; ++ii) { + var item = items[ii]; -ns.Glyph = Glyph; -// ------- END GLYPH ------- + // RDF subject + var subject = {}; + subject.type = (id.indexOf('_:') === 0) ? 'blank node' : 'IRI'; + subject.value = id; -// ------- LABEL ------- -/** - * Represents the <label> element. - * @class Label - * @extends SBGNBase - * @param {Object} params - * @param {string=} params.text - * @param {Bbox=} params.bbox - */ -var Label = function (params) { - ns.SBGNBase.call(this, params); - var params = checkParams(params, ['text', 'bbox']); - this.text = params.text; - this.bbox = params.bbox; -}; + // skip relative IRI subjects + if(!_isAbsoluteIri(id)) { + continue; + } -Label.prototype = Object.create(ns.SBGNBase.prototype); -Label.prototype.constructor = ns.Label; + // RDF predicate + var predicate = {}; + predicate.type = (property.indexOf('_:') === 0) ? 'blank node' : 'IRI'; + predicate.value = property; -/** - * @param {Bbox} bbox - */ -Label.prototype.setBbox = function (bbox) { - this.bbox = bbox; -}; + // skip relative IRI predicates + if(!_isAbsoluteIri(property)) { + continue; + } -/** - * @return {Object} - xml2js formatted object - */ -Label.prototype.buildJsObj = function () { - var labelObj = {}; + // skip blank node predicates unless producing generalized RDF + if(predicate.type === 'blank node' && !options.produceGeneralizedRdf) { + continue; + } - // attributes - var attributes = {}; - if(this.text != null) { - attributes.text = this.text; - } - else { // text is a required attribute - attributes.text = ""; - } - // ensure encoding of line breaks is always respected - //attributes.text = attributes.text.replace('\n', '\n'); //' '); - utils.addAttributes(labelObj, attributes); + // convert @list to triples + if(_isList(item)) { + _listToRDF(item['@list'], issuer, subject, predicate, rval); + } else { + // convert value or node object to triple + var object = _objectToRDF(item); + // skip null objects (they are relative IRIs) + if(object) { + rval.push({subject: subject, predicate: predicate, object: object}); + } + } + } + } + } - this.baseToJsObj(labelObj); - if(this.bbox != null) { - labelObj.bbox = this.bbox.buildJsObj(); - } - return labelObj; -}; + return rval; +} /** - * @return {string} + * Converts a @list value into linked list of blank node RDF triples + * (an RDF collection). + * + * @param list the @list value. + * @param issuer a IdentifierIssuer for assigning blank node names. + * @param subject the subject for the head of the list. + * @param predicate the predicate for the head of the list. + * @param triples the array of triples to append to. */ -Label.prototype.toXML = function () { - return utils.buildString({label: this.buildJsObj()}) -}; +function _listToRDF(list, issuer, subject, predicate, triples) { + var first = {type: 'IRI', value: RDF_FIRST}; + var rest = {type: 'IRI', value: RDF_REST}; + var nil = {type: 'IRI', value: RDF_NIL}; -/** - * @param {String} string - * @return {Label} - */ -Label.fromXML = function (string) { - var label; - function fn (err, result) { - label = Label.fromObj(result); - }; - utils.parseString(string, fn); - return label; -}; + for(var i = 0; i < list.length; ++i) { + var item = list[i]; -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Label} - */ -Label.fromObj = function (jsObj) { - if (typeof jsObj.label == 'undefined') { - throw new Error("Bad XML provided, expected tagName label, got: " + Object.keys(jsObj)[0]); - } + var blankNode = {type: 'blank node', value: issuer.getId()}; + triples.push({subject: subject, predicate: predicate, object: blankNode}); - var label = new ns.Label(); - jsObj = jsObj.label; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return label; - } + subject = blankNode; + predicate = first; + var object = _objectToRDF(item); - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - label.text = attributes.text || null; - } + // skip null objects (they are relative IRIs) + if(object) { + triples.push({subject: subject, predicate: predicate, object: object}); + } - if(jsObj.bbox) { - var bbox = ns.Bbox.fromObj({bbox: jsObj.bbox[0]}); - label.setBbox(bbox); - } - label.baseFromObj(jsObj); - return label; -}; + predicate = rest; + } -ns.Label = Label; -// ------- END LABEL ------- + triples.push({subject: subject, predicate: predicate, object: nil}); +} -// ------- BBOX ------- /** - * Represents the <bbox> element. - * @class Bbox - * @extends SBGNBase - * @param {Object} params - * @param {string|number=} params.x - * @param {string|number=} params.y - * @param {string|number=} params.w - * @param {string|number=} params.h + * Converts a JSON-LD value object to an RDF literal or a JSON-LD string or + * node object to an RDF resource. + * + * @param item the JSON-LD value or node object. + * + * @return the RDF literal or RDF resource. */ -var Bbox = function (params) { - ns.SBGNBase.call(this, params); - var params = checkParams(params, ['x', 'y', 'w', 'h']); - this.x = parseFloat(params.x); - this.y = parseFloat(params.y); - this.w = parseFloat(params.w); - this.h = parseFloat(params.h); -}; +function _objectToRDF(item) { + var object = {}; -Bbox.prototype = Object.create(ns.SBGNBase.prototype); -Bbox.prototype.constructor = ns.Bbox; + // convert value object to RDF + if(_isValue(item)) { + object.type = 'literal'; + var value = item['@value']; + var datatype = item['@type'] || null; -/** - * @return {Object} - xml2js formatted object - */ -Bbox.prototype.buildJsObj = function () { - var bboxObj = {}; - - // attributes - var attributes = {}; - if(!isNaN(this.x)) { - attributes.x = this.x; - } - if(!isNaN(this.y)) { - attributes.y = this.y; - } - if(!isNaN(this.w)) { - attributes.w = this.w; - } - if(!isNaN(this.h)) { - attributes.h = this.h; - } - utils.addAttributes(bboxObj, attributes); - this.baseToJsObj(bboxObj); - return bboxObj; -}; + // convert to XSD datatypes as appropriate + if(_isBoolean(value)) { + object.value = value.toString(); + object.datatype = datatype || XSD_BOOLEAN; + } else if(_isDouble(value) || datatype === XSD_DOUBLE) { + if(!_isDouble(value)) { + value = parseFloat(value); + } + // canonical double representation + object.value = value.toExponential(15).replace(/(\d)0*e\+?/, '$1E'); + object.datatype = datatype || XSD_DOUBLE; + } else if(_isNumber(value)) { + object.value = value.toFixed(0); + object.datatype = datatype || XSD_INTEGER; + } else if('@language' in item) { + object.value = value; + object.datatype = datatype || RDF_LANGSTRING; + object.language = item['@language']; + } else { + object.value = value; + object.datatype = datatype || XSD_STRING; + } + } else { + // convert string/node object to RDF + var id = _isObject(item) ? item['@id'] : item; + object.type = (id.indexOf('_:') === 0) ? 'blank node' : 'IRI'; + object.value = id; + } -/** - * @return {string} - */ -Bbox.prototype.toXML = function () { - return utils.buildString({bbox: this.buildJsObj()}) -}; + // skip relative IRIs + if(object.type === 'IRI' && !_isAbsoluteIri(object.value)) { + return null; + } -/** - * @param {String} string - * @return {Bbox} - */ -Bbox.fromXML = function (string) { - var bbox; - function fn (err, result) { - bbox = Bbox.fromObj(result); - }; - utils.parseString(string, fn); - return bbox; -}; + return object; +} /** - * @param {Object} jsObj - xml2js formatted object - * @return {Bbox} + * Converts an RDF triple object to a JSON-LD object. + * + * @param o the RDF triple object to convert. + * @param useNativeTypes true to output native types, false not to. + * + * @return the JSON-LD object. */ -Bbox.fromObj = function (jsObj) { - if (typeof jsObj.bbox == 'undefined') { - throw new Error("Bad XML provided, expected tagName bbox, got: " + Object.keys(jsObj)[0]); - } +function _RDFToObject(o, useNativeTypes) { + // convert IRI/blank node object to JSON-LD + if(o.type === 'IRI' || o.type === 'blank node') { + return {'@id': o.value}; + } - var bbox = new ns.Bbox(); - jsObj = jsObj.bbox; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return bbox; - } + // convert literal to JSON-LD + var rval = {'@value': o.value}; - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - bbox.x = parseFloat(attributes.x); - bbox.y = parseFloat(attributes.y); - bbox.w = parseFloat(attributes.w); - bbox.h = parseFloat(attributes.h); - } - bbox.baseFromObj(jsObj); - return bbox; -}; + // add language + if(o.language) { + rval['@language'] = o.language; + } else { + var type = o.datatype; + if(!type) { + type = XSD_STRING; + } + // use native types for certain xsd types + if(useNativeTypes) { + if(type === XSD_BOOLEAN) { + if(rval['@value'] === 'true') { + rval['@value'] = true; + } else if(rval['@value'] === 'false') { + rval['@value'] = false; + } + } else if(_isNumeric(rval['@value'])) { + if(type === XSD_INTEGER) { + var i = parseInt(rval['@value'], 10); + if(i.toFixed(0) === rval['@value']) { + rval['@value'] = i; + } + } else if(type === XSD_DOUBLE) { + rval['@value'] = parseFloat(rval['@value']); + } + } + // do not add native type + if([XSD_BOOLEAN, XSD_INTEGER, XSD_DOUBLE, XSD_STRING] + .indexOf(type) === -1) { + rval['@type'] = type; + } + } else if(type !== XSD_STRING) { + rval['@type'] = type; + } + } -ns.Bbox = Bbox; -// ------- END BBOX ------- + return rval; +} -// ------- STATE ------- /** - * Represents the <state> element. - * @class State - * @param {Object} params - * @param {string=} params.value - * @param {string=} params.variable + * Compares two RDF triples for equality. + * + * @param t1 the first triple. + * @param t2 the second triple. + * + * @return true if the triples are the same, false if not. */ -var State = function (params) { - var params = checkParams(params, ['value', 'variable']); - this.value = params.value; - this.variable = params.variable; -}; +function _compareRDFTriples(t1, t2) { + var attrs = ['subject', 'predicate', 'object']; + for(var i = 0; i < attrs.length; ++i) { + var attr = attrs[i]; + if(t1[attr].type !== t2[attr].type || t1[attr].value !== t2[attr].value) { + return false; + } + } + if(t1.object.language !== t2.object.language) { + return false; + } + if(t1.object.datatype !== t2.object.datatype) { + return false; + } + return true; +} -/** - * @return {Object} - xml2js formatted object - */ -State.prototype.buildJsObj = function () { - var stateObj = {}; +/////////////////////////////// DEFINE URDNA2015 ////////////////////////////// - // attributes - var attributes = {}; - if(this.value != null) { - attributes.value = this.value; - } - if(this.variable != null) { - attributes.variable = this.variable; - } - utils.addAttributes(stateObj, attributes); - return stateObj; -}; +var URDNA2015 = (function() { -/** - * @return {string} - */ -State.prototype.toXML = function () { - return utils.buildString({state: this.buildJsObj()}) -}; +var POSITIONS = {'subject': 's', 'object': 'o', 'name': 'g'}; -/** - * @param {String} string - * @return {State} - */ -State.fromXML = function (string) { - var state; - function fn (err, result) { - state = State.fromObj(result); - }; - utils.parseString(string, fn); - return state; +var Normalize = function(options) { + options = options || {}; + this.name = 'URDNA2015'; + this.options = options; + this.blankNodeInfo = {}; + this.hashToBlankNodes = {}; + this.canonicalIssuer = new IdentifierIssuer('_:c14n'); + this.quads = []; + this.schedule = {}; + if('maxCallStackDepth' in options) { + this.schedule.MAX_DEPTH = options.maxCallStackDepth; + } else { + this.schedule.MAX_DEPTH = 500; + } + if('maxTotalCallStackDepth' in options) { + this.schedule.MAX_TOTAL_DEPTH = options.maxCallStackDepth; + } else { + this.schedule.MAX_TOTAL_DEPTH = 0xFFFFFFFF; + } + this.schedule.depth = 0; + this.schedule.totalDepth = 0; + if('timeSlice' in options) { + this.schedule.timeSlice = options.timeSlice; + } else { + // milliseconds + this.schedule.timeSlice = 10; + } }; -/** - * @param {Object} jsObj - xml2js formatted object - * @return {State} - */ -State.fromObj = function (jsObj) { - if (typeof jsObj.state == 'undefined') { - throw new Error("Bad XML provided, expected tagName state, got: " + Object.keys(jsObj)[0]); - } +// do some work in a time slice, but in serial +Normalize.prototype.doWork = function(fn, callback) { + var schedule = this.schedule; - var state = new ns.State(); - jsObj = jsObj.state; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return state; - } + if(schedule.totalDepth >= schedule.MAX_TOTAL_DEPTH) { + return callback(new Error( + 'Maximum total call stack depth exceeded; normalization aborting.')); + } - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - state.value = attributes.value || null; - state.variable = attributes.variable || null; - } - return state; -}; + (function work() { + if(schedule.depth === schedule.MAX_DEPTH) { + // stack too deep, run on next tick + schedule.depth = 0; + schedule.running = false; + return jsonld.nextTick(work); + } -ns.State = State; -/** - * @class StateType - * @deprecated Replaced by State - */ -ns.StateType = State; -// ------- END STATE ------- + // if not yet running, force run + var now = new Date().getTime(); + if(!schedule.running) { + schedule.start = new Date().getTime(); + schedule.deadline = schedule.start + schedule.timeSlice; + } -// ------- CLONE ------- -/** - * Represents the <clone> element. - * @class Clone - * @param {Object} params - * @param {string=} params.label - */ -var Clone = function (params) { - var params = checkParams(params, ['label']); - this.label = params.label; -}; + // TODO: should also include an estimate of expectedWorkTime + if(now < schedule.deadline) { + schedule.running = true; + schedule.depth++; + schedule.totalDepth++; + return fn(function(err, result) { + schedule.depth--; + schedule.totalDepth--; + callback(err, result); + }); + } -/** - * @param {Label} label - */ -Clone.prototype.setLabel = function (label) { - this.label = label; + // not enough time left in this slice, run after letting browser + // do some other things + schedule.depth = 0; + schedule.running = false; + jsonld.setImmediate(work); + })(); }; -/** - * @return {Object} - xml2js formatted object - */ -Clone.prototype.buildJsObj = function () { - var cloneObj = {}; +// asynchronously loop +Normalize.prototype.forEach = function(iterable, fn, callback) { + var self = this; + var iterator; + var idx = 0; + var length; + if(_isArray(iterable)) { + length = iterable.length; + iterator = function() { + if(idx === length) { + return false; + } + iterator.value = iterable[idx++]; + iterator.key = idx; + return true; + }; + } else { + var keys = Object.keys(iterable); + length = keys.length; + iterator = function() { + if(idx === length) { + return false; + } + iterator.key = keys[idx++]; + iterator.value = iterable[iterator.key]; + return true; + }; + } - // children - if(this.label != null) { - cloneObj.label = this.label.buildJsObj(); - } - return cloneObj; + (function iterate(err, result) { + if(err) { + return callback(err); + } + if(iterator()) { + return self.doWork(function() { + fn(iterator.value, iterator.key, iterate); + }); + } + callback(); + })(); }; -/** - * @return {string} - */ -Clone.prototype.toXML = function () { - return utils.buildString({clone: this.buildJsObj()}) +// asynchronous waterfall +Normalize.prototype.waterfall = function(fns, callback) { + var self = this; + self.forEach(fns, function(fn, idx, callback) { + self.doWork(fn, callback); + }, callback); }; -/** - * @param {String} string - * @return {Clone} - */ -Clone.fromXML = function (string) { - var clone; - function fn (err, result) { - clone = Clone.fromObj(result); - }; - utils.parseString(string, fn); - return clone; +// asynchronous while +Normalize.prototype.whilst = function(condition, fn, callback) { + var self = this; + (function loop(err) { + if(err) { + return callback(err); + } + if(!condition()) { + return callback(); + } + self.doWork(fn, loop); + })(); }; -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Clone} - */ -Clone.fromObj = function (jsObj) { - if (typeof jsObj.clone == 'undefined') { - throw new Error("Bad XML provided, expected tagName clone, got: " + Object.keys(jsObj)[0]); - } +// 4.4) Normalization Algorithm +Normalize.prototype.main = function(dataset, callback) { + var self = this; + self.schedule.start = new Date().getTime(); + var result; - var clone = new ns.Clone(); - jsObj = jsObj.clone; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return clone; - } + // handle invalid output format + if(self.options.format) { + if(self.options.format !== 'application/nquads') { + return callback(new JsonLdError( + 'Unknown output format.', + 'jsonld.UnknownFormat', {format: self.options.format})); + } + } - // children - if(jsObj.label) { - var label = ns.Label.fromObj({label: jsObj.label[0]}); - clone.setLabel(label); - } - return clone; -}; + // 1) Create the normalization state. -ns.Clone = Clone; -/** - * @class CloneType - * @deprecated Replaced by Clone - */ -ns.CloneType = Clone; -// ------- END CLONE ------- + // Note: Optimize by generating non-normalized blank node map concurrently. + var nonNormalized = {}; -// ------- ENTITYTYPE ------- -/** - * Represents the <entity> element. - * @class Entity - * @param {Object} params - * @param {string=} params.name - */ -var Entity = function (params) { - var params = checkParams(params, ['name']); - this.name = params.name; -}; + self.waterfall([ + function(callback) { + // 2) For every quad in input dataset: + self.forEach(dataset, function(triples, graphName, callback) { + if(graphName === '@default') { + graphName = null; + } + self.forEach(triples, function(quad, idx, callback) { + if(graphName !== null) { + if(graphName.indexOf('_:') === 0) { + quad.name = {type: 'blank node', value: graphName}; + } else { + quad.name = {type: 'IRI', value: graphName}; + } + } + self.quads.push(quad); -/** - * @return {Object} - xml2js formatted object - */ -Entity.prototype.buildJsObj = function () { - var entityObj = {}; + // 2.1) For each blank node that occurs in the quad, add a reference + // to the quad using the blank node identifier in the blank node to + // quads map, creating a new entry if necessary. + self.forEachComponent(quad, function(component) { + if(component.type !== 'blank node') { + return; + } + var id = component.value; + if(id in self.blankNodeInfo) { + self.blankNodeInfo[id].quads.push(quad); + } else { + nonNormalized[id] = true; + self.blankNodeInfo[id] = {quads: [quad]}; + } + }); + callback(); + }, callback); + }, callback); + }, + function(callback) { + // 3) Create a list of non-normalized blank node identifiers + // non-normalized identifiers and populate it using the keys from the + // blank node to quads map. + // Note: We use a map here and it was generated during step 2. - // attributes - var attributes = {}; - if(this.name != null) { - attributes.name = this.name; - } - utils.addAttributes(entityObj, attributes); - return entityObj; -}; - -/** - * @return {string} - */ -Entity.prototype.toXML = function () { - return utils.buildString({entity: this.buildJsObj()}) -}; + // 4) Initialize simple, a boolean flag, to true. + var simple = true; -/** - * @param {String} string - * @return {Entity} - */ -Entity.fromXML = function (string) { - var entity; - function fn (err, result) { - entity = Entity.fromObj(result); - }; - utils.parseString(string, fn); - return entity; -}; + // 5) While simple is true, issue canonical identifiers for blank nodes: + self.whilst(function() { return simple; }, function(callback) { + // 5.1) Set simple to false. + simple = false; -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Entity} - */ -Entity.fromObj = function (jsObj) { - if (typeof jsObj.entity == 'undefined') { - throw new Error("Bad XML provided, expected tagName entity, got: " + Object.keys(jsObj)[0]); - } + // 5.2) Clear hash to blank nodes map. + self.hashToBlankNodes = {}; - var entity = new ns.Entity(); - jsObj = jsObj.entity; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return entity; - } + self.waterfall([ + function(callback) { + // 5.3) For each blank node identifier identifier in non-normalized + // identifiers: + self.forEach(nonNormalized, function(value, id, callback) { + // 5.3.1) Create a hash, hash, according to the Hash First Degree + // Quads algorithm. + self.hashFirstDegreeQuads(id, function(err, hash) { + if(err) { + return callback(err); + } + // 5.3.2) Add hash and identifier to hash to blank nodes map, + // creating a new entry if necessary. + if(hash in self.hashToBlankNodes) { + self.hashToBlankNodes[hash].push(id); + } else { + self.hashToBlankNodes[hash] = [id]; + } + callback(); + }); + }, callback); + }, + function(callback) { + // 5.4) For each hash to identifier list mapping in hash to blank + // nodes map, lexicographically-sorted by hash: + var hashes = Object.keys(self.hashToBlankNodes).sort(); + self.forEach(hashes, function(hash, i, callback) { + // 5.4.1) If the length of identifier list is greater than 1, + // continue to the next mapping. + var idList = self.hashToBlankNodes[hash]; + if(idList.length > 1) { + return callback(); + } - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - entity.name = attributes.name || null; - } - return entity; -}; + // 5.4.2) Use the Issue Identifier algorithm, passing canonical + // issuer and the single blank node identifier in identifier + // list, identifier, to issue a canonical replacement identifier + // for identifier. + // TODO: consider changing `getId` to `issue` + var id = idList[0]; + self.canonicalIssuer.getId(id); -ns.Entity = Entity; -/** - * @class EntityType - * @deprecated Replaced by Entity - */ -ns.EntityType = Entity; -// ------- END ENTITYTYPE ------- + // 5.4.3) Remove identifier from non-normalized identifiers. + delete nonNormalized[id]; -// ------- PORT ------- -/** - * Represents the <port> element. - * @class Port - * @param {Object} params - * @param {string=} params.id - * @param {string|number=} params.x - * @param {string|number=} params.y - */ -var Port = function (params) { - ns.SBGNBase.call(this, params); - var params = checkParams(params, ['id', 'x', 'y']); - this.id = params.id; - this.x = parseFloat(params.x); - this.y = parseFloat(params.y); -}; + // 5.4.4) Remove hash from the hash to blank nodes map. + delete self.hashToBlankNodes[hash]; -Port.prototype = Object.create(ns.SBGNBase.prototype); -Port.prototype.constructor = ns.Port; + // 5.4.5) Set simple to true. + simple = true; + callback(); + }, callback); + } + ], callback); + }, callback); + }, + function(callback) { + // 6) For each hash to identifier list mapping in hash to blank nodes map, + // lexicographically-sorted by hash: + var hashes = Object.keys(self.hashToBlankNodes).sort(); + self.forEach(hashes, function(hash, idx, callback) { + // 6.1) Create hash path list where each item will be a result of + // running the Hash N-Degree Quads algorithm. + var hashPathList = []; -/** - * @return {Object} - xml2js formatted object - */ -Port.prototype.buildJsObj = function () { - var portObj = {}; + // 6.2) For each blank node identifier identifier in identifier list: + var idList = self.hashToBlankNodes[hash]; + self.waterfall([ + function(callback) { + self.forEach(idList, function(id, idx, callback) { + // 6.2.1) If a canonical identifier has already been issued for + // identifier, continue to the next identifier. + if(self.canonicalIssuer.hasId(id)) { + return callback(); + } - // attributes - var attributes = {}; - if(this.id != null) { - attributes.id = this.id; - } - if(!isNaN(this.x)) { - attributes.x = this.x; - } - if(!isNaN(this.y)) { - attributes.y = this.y; - } - utils.addAttributes(portObj, attributes); - this.baseToJsObj(portObj); - return portObj; -}; + // 6.2.2) Create temporary issuer, an identifier issuer + // initialized with the prefix _:b. + var issuer = new IdentifierIssuer('_:b'); -/** - * @return {string} - */ -Port.prototype.toXML = function () { - return utils.buildString({port: this.buildJsObj()}) -}; + // 6.2.3) Use the Issue Identifier algorithm, passing temporary + // issuer and identifier, to issue a new temporary blank node + // identifier for identifier. + issuer.getId(id); -/** - * @param {String} string - * @return {Port} - */ -Port.fromXML = function (string) { - var port; - function fn (err, result) { - port = Port.fromObj(result); - }; - utils.parseString(string, fn); - return port; -}; + // 6.2.4) Run the Hash N-Degree Quads algorithm, passing + // temporary issuer, and append the result to the hash path list. + self.hashNDegreeQuads(id, issuer, function(err, result) { + if(err) { + return callback(err); + } + hashPathList.push(result); + callback(); + }); + }, callback); + }, + function(callback) { + // 6.3) For each result in the hash path list, + // lexicographically-sorted by the hash in result: + hashPathList.sort(function(a, b) { + return (a.hash < b.hash) ? -1 : ((a.hash > b.hash) ? 1 : 0); + }); + self.forEach(hashPathList, function(result, idx, callback) { + // 6.3.1) For each blank node identifier, existing identifier, + // that was issued a temporary identifier by identifier issuer + // in result, issue a canonical identifier, in the same order, + // using the Issue Identifier algorithm, passing canonical + // issuer and existing identifier. + for(var existing in result.issuer.existing) { + self.canonicalIssuer.getId(existing); + } + callback(); + }, callback); + } + ], callback); + }, callback); + }, function(callback) { + /* Note: At this point all blank nodes in the set of RDF quads have been + assigned canonical identifiers, which have been stored in the canonical + issuer. Here each quad is updated by assigning each of its blank nodes + its new identifier. */ -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Port} - */ -Port.fromObj = function (jsObj) { - if (typeof jsObj.port == 'undefined') { - throw new Error("Bad XML provided, expected tagName port, got: " + Object.keys(jsObj)[0]); - } + // 7) For each quad, quad, in input dataset: + var normalized = []; + self.waterfall([ + function(callback) { + self.forEach(self.quads, function(quad, idx, callback) { + // 7.1) Create a copy, quad copy, of quad and replace any existing + // blank node identifiers using the canonical identifiers + // previously issued by canonical issuer. + // Note: We optimize away the copy here. + self.forEachComponent(quad, function(component) { + if(component.type === 'blank node' && + component.value.indexOf(self.canonicalIssuer.prefix) !== 0) { + component.value = self.canonicalIssuer.getId(component.value); + } + }); + // 7.2) Add quad copy to the normalized dataset. + normalized.push(_toNQuad(quad)); + callback(); + }, callback); + }, + function(callback) { + // sort normalized output + normalized.sort(); - var port = new ns.Port(); - jsObj = jsObj.port; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return port; - } + // 8) Return the normalized dataset. + if(self.options.format === 'application/nquads') { + result = normalized.join(''); + return callback(); + } - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - port.x = parseFloat(attributes.x); - port.y = parseFloat(attributes.y); - port.id = attributes.id || null; - } - port.baseFromObj(jsObj); - return port; + result = _parseNQuads(normalized.join('')); + callback(); + } + ], callback); + } + ], function(err) { + callback(err, result); + }); }; -ns.Port = Port; -// ------- END PORT ------- +// 4.6) Hash First Degree Quads +Normalize.prototype.hashFirstDegreeQuads = function(id, callback) { + var self = this; -// ------- ARC ------- -/** - * Represents the <arc> element. - * @class Arc - * @param {Object} params - * @param {string=} params.id - * @param {string=} params.class_ - * @param {string=} params.source - * @param {string=} params.target - * @param {Start=} params.start - * @param {End=} params.end - * @param {Next=} params.nexts - * @param {Glyph[]=} params.glyphs The arc's cardinality. Possibility to have more than one glyph is left open. - */ -var Arc = function (params) { - ns.SBGNBase.call(this, params); - var params = checkParams(params, ['id', 'class_', 'source', 'target', 'start', 'end', 'nexts', 'glyphs']); - this.id = params.id; - this.class_ = params.class_; - this.source = params.source; - this.target = params.target; + // return cached hash + var info = self.blankNodeInfo[id]; + if('hash' in info) { + return callback(null, info.hash); + } - this.start = params.start; - this.end = params.end; - this.nexts = params.nexts || []; - this.glyphs = params.glyphs || []; -}; + // 1) Initialize nquads to an empty list. It will be used to store quads in + // N-Quads format. + var nquads = []; -Arc.prototype = Object.create(ns.SBGNBase.prototype); -Arc.prototype.constructor = ns.Arc; + // 2) Get the list of quads quads associated with the reference blank node + // identifier in the blank node to quads map. + var quads = info.quads; -/** - * @param {Start} start - */ -Arc.prototype.setStart = function (start) { - this.start = start; -}; + // 3) For each quad quad in quads: + self.forEach(quads, function(quad, idx, callback) { + // 3.1) Serialize the quad in N-Quads format with the following special + // rule: -/** - * @param {End} end - */ -Arc.prototype.setEnd = function (end) { - this.end = end; -}; + // 3.1.1) If any component in quad is an blank node, then serialize it + // using a special identifier as follows: + var copy = {predicate: quad.predicate}; + self.forEachComponent(quad, function(component, key) { + // 3.1.2) If the blank node's existing blank node identifier matches the + // reference blank node identifier then use the blank node identifier _:a, + // otherwise, use the blank node identifier _:z. + copy[key] = self.modifyFirstDegreeComponent(id, component, key); + }); + nquads.push(_toNQuad(copy)); + callback(); + }, function(err) { + if(err) { + return callback(err); + } + // 4) Sort nquads in lexicographical order. + nquads.sort(); -/** - * @param {Next} next - */ -Arc.prototype.addNext = function (next) { - this.nexts.push(next); + // 5) Return the hash that results from passing the sorted, joined nquads + // through the hash algorithm. + info.hash = NormalizeHash.hashNQuads(self.name, nquads); + callback(null, info.hash); + }); }; -/** - * @param {Glyph} glyph - */ -Arc.prototype.addGlyph = function (glyph) { - this.glyphs.push(glyph); +// helper for modifying component during Hash First Degree Quads +Normalize.prototype.modifyFirstDegreeComponent = function(id, component) { + if(component.type !== 'blank node') { + return component; + } + component = _clone(component); + component.value = (component.value === id ? '_:a' : '_:z'); + return component; }; -/** - * @return {Object} - xml2js formatted object - */ -Arc.prototype.buildJsObj = function () { - var arcObj = {}; +// 4.7) Hash Related Blank Node +Normalize.prototype.hashRelatedBlankNode = function( + related, quad, issuer, position, callback) { + var self = this; - // attributes - var attributes = {}; - if(this.id != null) { - attributes.id = this.id; - } - if(this.class_ != null) { - attributes.class = this.class_; - } - if(this.source != null) { - attributes.source = this.source; - } - if(this.target != null) { - attributes.target = this.target; - } - utils.addAttributes(arcObj, attributes); + // 1) Set the identifier to use for related, preferring first the canonical + // identifier for related if issued, second the identifier issued by issuer + // if issued, and last, if necessary, the result of the Hash First Degree + // Quads algorithm, passing related. + var id; + self.waterfall([ + function(callback) { + if(self.canonicalIssuer.hasId(related)) { + id = self.canonicalIssuer.getId(related); + return callback(); + } + if(issuer.hasId(related)) { + id = issuer.getId(related); + return callback(); + } + self.hashFirstDegreeQuads(related, function(err, hash) { + if(err) { + return callback(err); + } + id = hash; + callback(); + }); + } + ], function(err) { + if(err) { + return callback(err); + } - // children - this.baseToJsObj(arcObj); - for(var i=0; i < this.glyphs.length; i++) { - if (i==0) { - arcObj.glyph = []; - } - arcObj.glyph.push(this.glyphs[i].buildJsObj()); - } - if(this.start != null) { - arcObj.start = this.start.buildJsObj(); - } - if(this.state != null) { - arcObj.state = this.state.buildJsObj(); - } - for(var i=0; i < this.nexts.length; i++) { - if (i==0) { - arcObj.next = []; - } - arcObj.next.push(this.nexts[i].buildJsObj()); - } - if(this.end != null) { - arcObj.end = this.end.buildJsObj(); - } - return arcObj; -}; + // 2) Initialize a string input to the value of position. + // Note: We use a hash object instead. + var md = new NormalizeHash(self.name); + md.update(position); -/** - * @return {string} - */ -Arc.prototype.toXML = function () { - return utils.buildString({arc: this.buildJsObj()}) -}; + // 3) If position is not g, append <, the value of the predicate in quad, + // and > to input. + if(position !== 'g') { + md.update(self.getRelatedPredicate(quad)); + } -/** - * @param {String} string - * @return {Arc} - */ -Arc.fromXML = function (string) { - var arc; - function fn (err, result) { - arc = Arc.fromObj(result); - }; - utils.parseString(string, fn); - return arc; + // 4) Append identifier to input. + md.update(id); + + // 5) Return the hash that results from passing input through the hash + // algorithm. + return callback(null, md.digest()); + }); }; -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Arc} - */ -Arc.fromObj = function (jsObj) { - if (typeof jsObj.arc == 'undefined') { - throw new Error("Bad XML provided, expected tagName arc, got: " + Object.keys(jsObj)[0]); - } +// helper for getting a related predicate +Normalize.prototype.getRelatedPredicate = function(quad) { + return '<' + quad.predicate.value + '>'; +}; - var arc = new ns.Arc(); - jsObj = jsObj.arc; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return arc; - } +// 4.8) Hash N-Degree Quads +Normalize.prototype.hashNDegreeQuads = function(id, issuer, callback) { + var self = this; - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - arc.id = attributes.id || null; - arc.class_ = attributes.class || null; - arc.source = attributes.source || null; - arc.target = attributes.target || null; - } + // 1) Create a hash to related blank nodes map for storing hashes that + // identify related blank nodes. + // Note: 2) and 3) handled within `createHashToRelated` + var hashToRelated; + var md = new NormalizeHash(self.name); + self.waterfall([ + function(callback) { + self.createHashToRelated(id, issuer, function(err, result) { + if(err) { + return callback(err); + } + hashToRelated = result; + callback(); + }); + }, + function(callback) { + // 4) Create an empty string, data to hash. + // Note: We created a hash object `md` above instead. - // children - if(jsObj.start) { - var start = ns.Start.fromObj({start: jsObj.start[0]}); - arc.setStart(start); - } - if(jsObj.next) { - var nexts = jsObj.next; - for (var i=0; i < nexts.length; i++) { - var next = ns.Next.fromObj({next: nexts[i]}); - arc.addNext(next); - } - } - if(jsObj.end) { - var end = ns.End.fromObj({end: jsObj.end[0]}); - arc.setEnd(end); - } - if(jsObj.glyph) { - var glyphs = jsObj.glyph; - for (var i=0; i < glyphs.length; i++) { - var glyph = ns.Glyph.fromObj({glyph: glyphs[i]}); - arc.addGlyph(glyph); - } - } + // 5) For each related hash to blank node list mapping in hash to related + // blank nodes map, sorted lexicographically by related hash: + var hashes = Object.keys(hashToRelated).sort(); + self.forEach(hashes, function(hash, idx, callback) { + // 5.1) Append the related hash to the data to hash. + md.update(hash); - arc.baseFromObj(jsObj); - return arc; -}; + // 5.2) Create a string chosen path. + var chosenPath = ''; -ns.Arc = Arc; -// ------- END ARC ------- + // 5.3) Create an unset chosen issuer variable. + var chosenIssuer; -// ------- Start ------- -/** - * Represents the <start> element. - * @class Start - * @param {Object} params - * @param {string|number=} params.x - * @param {string|number=} params.y - */ -var Start = function (params) { - var params = checkParams(params, ['x', 'y']); - this.x = parseFloat(params.x); - this.y = parseFloat(params.y); -}; + // 5.4) For each permutation of blank node list: + var permutator = new Permutator(hashToRelated[hash]); + self.whilst( + function() { return permutator.hasNext(); }, + function(nextPermutation) { + var permutation = permutator.next(); -/** - * @return {Object} - xml2js formatted object - */ -Start.prototype.buildJsObj = function () { - var startObj = {}; + // 5.4.1) Create a copy of issuer, issuer copy. + var issuerCopy = issuer.clone(); - // attributes - var attributes = {}; - if(!isNaN(this.x)) { - attributes.x = this.x; - } - if(!isNaN(this.y)) { - attributes.y = this.y; - } - utils.addAttributes(startObj, attributes); - return startObj; -}; + // 5.4.2) Create a string path. + var path = ''; -/** - * @return {string} - */ -Start.prototype.toXML = function () { - return utils.buildString({start: this.buildJsObj()}) -}; + // 5.4.3) Create a recursion list, to store blank node identifiers + // that must be recursively processed by this algorithm. + var recursionList = []; -/** - * @param {String} string - * @return {Start} - */ -Start.fromXML = function (string) { - var start; - function fn (err, result) { - start = Start.fromObj(result); - }; - utils.parseString(string, fn); - return start; -}; + self.waterfall([ + function(callback) { + // 5.4.4) For each related in permutation: + self.forEach(permutation, function(related, idx, callback) { + // 5.4.4.1) If a canonical identifier has been issued for + // related, append it to path. + if(self.canonicalIssuer.hasId(related)) { + path += self.canonicalIssuer.getId(related); + } else { + // 5.4.4.2) Otherwise: + // 5.4.4.2.1) If issuer copy has not issued an identifier for + // related, append related to recursion list. + if(!issuerCopy.hasId(related)) { + recursionList.push(related); + } + // 5.4.4.2.2) Use the Issue Identifier algorithm, passing + // issuer copy and related and append the result to path. + path += issuerCopy.getId(related); + } -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Start} - */ -Start.fromObj = function (jsObj) { - if (typeof jsObj.start == 'undefined') { - throw new Error("Bad XML provided, expected tagName start, got: " + Object.keys(jsObj)[0]); - } + // 5.4.4.3) If chosen path is not empty and the length of path + // is greater than or equal to the length of chosen path and + // path is lexicographically greater than chosen path, then + // skip to the next permutation. + if(chosenPath.length !== 0 && + path.length >= chosenPath.length && path > chosenPath) { + // FIXME: may cause inaccurate total depth calculation + return nextPermutation(); + } + callback(); + }, callback); + }, + function(callback) { + // 5.4.5) For each related in recursion list: + self.forEach(recursionList, function(related, idx, callback) { + // 5.4.5.1) Set result to the result of recursively executing + // the Hash N-Degree Quads algorithm, passing related for + // identifier and issuer copy for path identifier issuer. + self.hashNDegreeQuads( + related, issuerCopy, function(err, result) { + if(err) { + return callback(err); + } - var start = new ns.Start(); - jsObj = jsObj.start; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return start; - } + // 5.4.5.2) Use the Issue Identifier algorithm, passing issuer + // copy and related and append the result to path. + path += issuerCopy.getId(related); - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - start.x = parseFloat(attributes.x); - start.y = parseFloat(attributes.y); - } - return start; -}; + // 5.4.5.3) Append <, the hash in result, and > to path. + path += '<' + result.hash + '>'; -ns.Start = Start; -/** - * @class StartType - * @deprecated Replaced by Start - */ -ns.StartType = Start; -// ------- END Start ------- + // 5.4.5.4) Set issuer copy to the identifier issuer in + // result. + issuerCopy = result.issuer; -// ------- End ------- -/** - * Represents the <end> element. - * @class End - * @param {Object} params - * @param {string|number=} params.x - * @param {string|number=} params.y - */ -var End = function (params) { - var params = checkParams(params, ['x', 'y']); - this.x = parseFloat(params.x); - this.y = parseFloat(params.y); -}; + // 5.4.5.5) If chosen path is not empty and the length of path + // is greater than or equal to the length of chosen path and + // path is lexicographically greater than chosen path, then + // skip to the next permutation. + if(chosenPath.length !== 0 && + path.length >= chosenPath.length && path > chosenPath) { + // FIXME: may cause inaccurate total depth calculation + return nextPermutation(); + } + callback(); + }); + }, callback); + }, + function(callback) { + // 5.4.6) If chosen path is empty or path is lexicographically + // less than chosen path, set chosen path to path and chosen + // issuer to issuer copy. + if(chosenPath.length === 0 || path < chosenPath) { + chosenPath = path; + chosenIssuer = issuerCopy; + } + callback(); + } + ], nextPermutation); + }, function(err) { + if(err) { + return callback(err); + } -/** - * @return {Object} - xml2js formatted object - */ -End.prototype.buildJsObj = function () { - var endObj = {}; + // 5.5) Append chosen path to data to hash. + md.update(chosenPath); - // attributes - var attributes = {}; - if(!isNaN(this.x)) { - attributes.x = this.x; - } - if(!isNaN(this.y)) { - attributes.y = this.y; - } - utils.addAttributes(endObj, attributes); - return endObj; + // 5.6) Replace issuer, by reference, with chosen issuer. + issuer = chosenIssuer; + callback(); + }); + }, callback); + } + ], function(err) { + // 6) Return issuer and the hash that results from passing data to hash + // through the hash algorithm. + callback(err, {hash: md.digest(), issuer: issuer}); + }); }; -/** - * @return {string} - */ -End.prototype.toXML = function () { - return utils.buildString({end: this.buildJsObj()}) -}; +// helper for creating hash to related blank nodes map +Normalize.prototype.createHashToRelated = function(id, issuer, callback) { + var self = this; -/** - * @param {String} string - * @return {End} - */ -End.fromXML = function (string) { - var end; - function fn (err, result) { - end = End.fromObj(result); - }; - utils.parseString(string, fn); - return end; -}; + // 1) Create a hash to related blank nodes map for storing hashes that + // identify related blank nodes. + var hashToRelated = {}; -/** - * @param {Object} jsObj - xml2js formatted object - * @return {End} - */ -End.fromObj = function (jsObj) { - if (typeof jsObj.end == 'undefined') { - throw new Error("Bad XML provided, expected tagName end, got: " + Object.keys(jsObj)[0]); - } + // 2) Get a reference, quads, to the list of quads in the blank node to + // quads map for the key identifier. + var quads = self.blankNodeInfo[id].quads; - var end = new ns.End(); - jsObj = jsObj.end; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return end; - } + // 3) For each quad in quads: + self.forEach(quads, function(quad, idx, callback) { + // 3.1) For each component in quad, if component is the subject, object, + // and graph name and it is a blank node that is not identified by + // identifier: + self.forEach(quad, function(component, key, callback) { + if(key === 'predicate' || + !(component.type === 'blank node' && component.value !== id)) { + return callback(); + } + // 3.1.1) Set hash to the result of the Hash Related Blank Node + // algorithm, passing the blank node identifier for component as + // related, quad, path identifier issuer as issuer, and position as + // either s, o, or g based on whether component is a subject, object, + // graph name, respectively. + var related = component.value; + var position = POSITIONS[key]; + self.hashRelatedBlankNode( + related, quad, issuer, position, function(err, hash) { + if(err) { + return callback(err); + } + // 3.1.2) Add a mapping of hash to the blank node identifier for + // component to hash to related blank nodes map, adding an entry as + // necessary. + if(hash in hashToRelated) { + hashToRelated[hash].push(related); + } else { + hashToRelated[hash] = [related]; + } + callback(); + }); + }, callback); + }, function(err) { + callback(err, hashToRelated); + }); +}; - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - end.x = parseFloat(attributes.x); - end.y = parseFloat(attributes.y); - } - return end; +// helper that iterates over quad components (skips predicate) +Normalize.prototype.forEachComponent = function(quad, op) { + for(var key in quad) { + // skip `predicate` + if(key === 'predicate') { + continue; + } + op(quad[key], key, quad); + } }; -ns.End = End; -/** - * @class EndType - * @deprecated Replaced by End - */ -ns.EndType = End; -// ------- END End ------- +return Normalize; -// ------- Next ------- -/** - * Represents the <next> element. - * @class Next - * @param {Object} params - * @param {string|number=} params.x - * @param {string|number=} params.y - */ -var Next = function (params) { - var params = checkParams(params, ['x', 'y']); - this.x = parseFloat(params.x); - this.y = parseFloat(params.y); -}; +})(); // end of define URDNA2015 -/** - * @return {Object} - xml2js formatted object - */ -Next.prototype.buildJsObj = function () { - var nextObj = {}; +/////////////////////////////// DEFINE URGNA2012 ////////////////////////////// - // attributes - var attributes = {}; - if(!isNaN(this.x)) { - attributes.x = this.x; - } - if(!isNaN(this.y)) { - attributes.y = this.y; - } - utils.addAttributes(nextObj, attributes); - return nextObj; +var URGNA2012 = (function() { + +var Normalize = function(options) { + URDNA2015.call(this, options); + this.name = 'URGNA2012'; }; +Normalize.prototype = new URDNA2015(); -/** - * @return {string} - */ -Next.prototype.toXML = function () { - return utils.buildString({next: this.buildJsObj()}) +// helper for modifying component during Hash First Degree Quads +Normalize.prototype.modifyFirstDegreeComponent = function(id, component, key) { + if(component.type !== 'blank node') { + return component; + } + component = _clone(component); + if(key === 'name') { + component.value = '_:g'; + } else { + component.value = (component.value === id ? '_:a' : '_:z'); + } + return component; }; -/** - * @param {String} string - * @return {Next} - */ -Next.fromXML = function (string) { - var next; - function fn (err, result) { - next = Next.fromObj(result); - }; - utils.parseString(string, fn); - return next; +// helper for getting a related predicate +Normalize.prototype.getRelatedPredicate = function(quad) { + return quad.predicate.value; }; -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Next} - */ -Next.fromObj = function (jsObj) { - if (typeof jsObj.next == 'undefined') { - throw new Error("Bad XML provided, expected tagName next, got: " + Object.keys(jsObj)[0]); - } +// helper for creating hash to related blank nodes map +Normalize.prototype.createHashToRelated = function(id, issuer, callback) { + var self = this; - var next = new ns.Next(); - jsObj = jsObj.next; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return next; - } + // 1) Create a hash to related blank nodes map for storing hashes that + // identify related blank nodes. + var hashToRelated = {}; - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - next.x = parseFloat(attributes.x); - next.y = parseFloat(attributes.y); - } - return next; + // 2) Get a reference, quads, to the list of quads in the blank node to + // quads map for the key identifier. + var quads = self.blankNodeInfo[id].quads; + + // 3) For each quad in quads: + self.forEach(quads, function(quad, idx, callback) { + // 3.1) If the quad's subject is a blank node that does not match + // identifier, set hash to the result of the Hash Related Blank Node + // algorithm, passing the blank node identifier for subject as related, + // quad, path identifier issuer as issuer, and p as position. + var position; + var related; + if(quad.subject.type === 'blank node' && quad.subject.value !== id) { + related = quad.subject.value; + position = 'p'; + } else if(quad.object.type === 'blank node' && quad.object.value !== id) { + // 3.2) Otherwise, if quad's object is a blank node that does not match + // identifier, to the result of the Hash Related Blank Node algorithm, + // passing the blank node identifier for object as related, quad, path + // identifier issuer as issuer, and r as position. + related = quad.object.value; + position = 'r'; + } else { + // 3.3) Otherwise, continue to the next quad. + return callback(); + } + // 3.4) Add a mapping of hash to the blank node identifier for the + // component that matched (subject or object) to hash to related blank + // nodes map, adding an entry as necessary. + self.hashRelatedBlankNode( + related, quad, issuer, position, function(err, hash) { + if(hash in hashToRelated) { + hashToRelated[hash].push(related); + } else { + hashToRelated[hash] = [related]; + } + callback(); + }); + }, function(err) { + callback(err, hashToRelated); + }); }; -ns.Next = Next; -/** - * @class NextType - * @deprecated Replaced by Next - */ -ns.NextType = Next; -// ------- END Next ------- +return Normalize; + +})(); // end of define URGNA2012 -// ------- POINT ------- /** - * Represents the <point> element. - * @class Point - * @param {Object} params - * @param {string|number=} params.x - * @param {string|number=} params.y + * Recursively flattens the subjects in the given JSON-LD expanded input + * into a node map. + * + * @param input the JSON-LD expanded input. + * @param graphs a map of graph name to subject map. + * @param graph the name of the current graph. + * @param issuer the blank node identifier issuer. + * @param name the name assigned to the current input if it is a bnode. + * @param list the list to append to, null for none. */ -var Point = function (params) { - ns.SBGNBase.call(this, params); - var params = checkParams(params, ['x', 'y']); - this.x = parseFloat(params.x); - this.y = parseFloat(params.y); -}; -Point.prototype = Object.create(ns.SBGNBase.prototype); -Point.prototype.constructor = Point; +function _createNodeMap(input, graphs, graph, issuer, name, list) { + // recurse through array + if(_isArray(input)) { + for(var i = 0; i < input.length; ++i) { + _createNodeMap(input[i], graphs, graph, issuer, undefined, list); + } + return; + } -/** - * @return {Object} - xml2js formatted object - */ -Point.prototype.buildJsObj = function () { - var pointJsObj = {}; + // add non-object to list + if(!_isObject(input)) { + if(list) { + list.push(input); + } + return; + } - // attributes - var attributes = {}; - if(!isNaN(this.x)) { - attributes.x = this.x; - } - if(!isNaN(this.y)) { - attributes.y = this.y; - } - utils.addAttributes(pointJsObj, attributes); - this.baseToJsObj(pointJsObj); - return pointJsObj; -}; + // add values to list + if(_isValue(input)) { + if('@type' in input) { + var type = input['@type']; + // rename @type blank node + if(type.indexOf('_:') === 0) { + input['@type'] = type = issuer.getId(type); + } + } + if(list) { + list.push(input); + } + return; + } -/** - * @return {string} - */ -Point.prototype.toXML = function () { - return utils.buildString({point: this.buildJsObj()}) -}; + // Note: At this point, input must be a subject. -/** - * @param {String} string - * @return {Point} - */ -Point.fromXML = function (string) { - var point; - function fn (err, result) { - point = Point.fromObj(result); - }; - utils.parseString(string, fn); - return point; -}; + // spec requires @type to be named first, so assign names early + if('@type' in input) { + var types = input['@type']; + for(var i = 0; i < types.length; ++i) { + var type = types[i]; + if(type.indexOf('_:') === 0) { + issuer.getId(type); + } + } + } -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Point} - */ -Point.fromObj = function (jsObj) { - if (typeof jsObj.point == 'undefined') { - throw new Error("Bad XML provided, expected tagName point, got: " + Object.keys(jsObj)[0]); - } + // get name for subject + if(_isUndefined(name)) { + name = _isBlankNode(input) ? issuer.getId(input['@id']) : input['@id']; + } - var point = new ns.Point(); - jsObj = jsObj.point; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return point; - } + // add subject reference to list + if(list) { + list.push({'@id': name}); + } - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - point.x = parseFloat(attributes.x); - point.y = parseFloat(attributes.y); - } - point.baseFromObj(jsObj); - return point; -}; + // create new subject or merge into existing one + var subjects = graphs[graph]; + var subject = subjects[name] = subjects[name] || {}; + subject['@id'] = name; + var properties = Object.keys(input).sort(); + for(var pi = 0; pi < properties.length; ++pi) { + var property = properties[pi]; -ns.Point = Point; -// ------- END POINT ------- + // skip @id + if(property === '@id') { + continue; + } -// ------- CALLOUT ------- -/** - * Represents the <callout> element. - * @class Callout - * @param {Object} params - * @param {string=} params.target - * @param {Point=} params.point - */ -var Callout = function (params) { - var params = checkParams(params, ['target', 'point']); - this.target = params.target; - this.point = params.point; -}; + // handle reverse properties + if(property === '@reverse') { + var referencedNode = {'@id': name}; + var reverseMap = input['@reverse']; + for(var reverseProperty in reverseMap) { + var items = reverseMap[reverseProperty]; + for(var ii = 0; ii < items.length; ++ii) { + var item = items[ii]; + var itemName = item['@id']; + if(_isBlankNode(item)) { + itemName = issuer.getId(itemName); + } + _createNodeMap(item, graphs, graph, issuer, itemName); + jsonld.addValue( + subjects[itemName], reverseProperty, referencedNode, + {propertyIsArray: true, allowDuplicate: false}); + } + } + continue; + } -/** - * @param {Point} point - */ -Callout.prototype.setPoint = function(point) { - this.point = point; -}; + // recurse into graph + if(property === '@graph') { + // add graph subjects map entry + if(!(name in graphs)) { + graphs[name] = {}; + } + var g = (graph === '@merged') ? graph : name; + _createNodeMap(input[property], graphs, g, issuer); + continue; + } -/** - * @return {Object} - xml2js formatted object - */ -Callout.prototype.buildJsObj = function () { - var calloutObj = {}; + // copy non-@type keywords + if(property !== '@type' && _isKeyword(property)) { + if(property === '@index' && property in subject && + (input[property] !== subject[property] || + input[property]['@id'] !== subject[property]['@id'])) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; conflicting @index property detected.', + 'jsonld.SyntaxError', + {code: 'conflicting indexes', subject: subject}); + } + subject[property] = input[property]; + continue; + } - // attributes - var attributes = {}; - if(this.target != null) { - attributes.target = this.target; - } - utils.addAttributes(calloutObj, attributes); + // iterate over objects + var objects = input[property]; - // children - if(this.point != null) { - calloutObj.point = this.point.buildJsObj(); - } - return calloutObj; -}; + // if property is a bnode, assign it a new id + if(property.indexOf('_:') === 0) { + property = issuer.getId(property); + } -/** - * @return {string} - */ -Callout.prototype.toXML = function () { - return utils.buildString({callout: this.buildJsObj()}) -}; + // ensure property is added for empty arrays + if(objects.length === 0) { + jsonld.addValue(subject, property, [], {propertyIsArray: true}); + continue; + } + for(var oi = 0; oi < objects.length; ++oi) { + var o = objects[oi]; -/** - * @param {String} string - * @return {Callout} - */ -Callout.fromXML = function (string) { - var callout; - function fn (err, result) { - callout = Callout.fromObj(result); - }; - utils.parseString(string, fn); - return callout; -}; + if(property === '@type') { + // rename @type blank nodes + o = (o.indexOf('_:') === 0) ? issuer.getId(o) : o; + } + + // handle embedded subject or subject reference + if(_isSubject(o) || _isSubjectReference(o)) { + // relabel blank node @id + var id = _isBlankNode(o) ? issuer.getId(o['@id']) : o['@id']; + + // add reference and recurse + jsonld.addValue( + subject, property, {'@id': id}, + {propertyIsArray: true, allowDuplicate: false}); + _createNodeMap(o, graphs, graph, issuer, id); + } else if(_isList(o)) { + // handle @list + var _list = []; + _createNodeMap(o['@list'], graphs, graph, issuer, name, _list); + o = {'@list': _list}; + jsonld.addValue( + subject, property, o, + {propertyIsArray: true, allowDuplicate: false}); + } else { + // handle @value + _createNodeMap(o, graphs, graph, issuer, name); + jsonld.addValue( + subject, property, o, {propertyIsArray: true, allowDuplicate: false}); + } + } + } +} + +function _mergeNodeMaps(graphs) { + // add all non-default graphs to default graph + var defaultGraph = graphs['@default']; + var graphNames = Object.keys(graphs).sort(); + for(var i = 0; i < graphNames.length; ++i) { + var graphName = graphNames[i]; + if(graphName === '@default') { + continue; + } + var nodeMap = graphs[graphName]; + var subject = defaultGraph[graphName]; + if(!subject) { + defaultGraph[graphName] = subject = { + '@id': graphName, + '@graph': [] + }; + } else if(!('@graph' in subject)) { + subject['@graph'] = []; + } + var graph = subject['@graph']; + var ids = Object.keys(nodeMap).sort(); + for(var ii = 0; ii < ids.length; ++ii) { + var node = nodeMap[ids[ii]]; + // only add full subjects + if(!_isSubjectReference(node)) { + graph.push(node); + } + } + } + return defaultGraph; +} /** - * @param {Object} jsObj - xml2js formatted object - * @return {Callout} + * Frames subjects according to the given frame. + * + * @param state the current framing state. + * @param subjects the subjects to filter. + * @param frame the frame. + * @param parent the parent subject or top-level array. + * @param property the parent property, initialized to null. */ -Callout.fromObj = function (jsObj) { - if (typeof jsObj.callout == 'undefined') { - throw new Error("Bad XML provided, expected tagName callout, got: " + Object.keys(jsObj)[0]); - } +function _frame(state, subjects, frame, parent, property) { + // validate the frame + _validateFrame(frame); + frame = frame[0]; - var callout = new ns.Callout(); - jsObj = jsObj.callout; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return callout; - } + // get flags for current frame + var options = state.options; + var flags = { + embed: _getFrameFlag(frame, options, 'embed'), + explicit: _getFrameFlag(frame, options, 'explicit'), + requireAll: _getFrameFlag(frame, options, 'requireAll') + }; - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - callout.target = attributes.target || null; - } + // filter out subjects that match the frame + var matches = _filterSubjects(state, subjects, frame, flags); - // children - if(jsObj.point) { - var point = ns.Point.fromObj({point: jsObj.point[0]}); - callout.setPoint(point); - } - return callout; -}; + // add matches to output + var ids = Object.keys(matches).sort(); + for(var idx = 0; idx < ids.length; ++idx) { + var id = ids[idx]; + var subject = matches[id]; -ns.Callout = Callout; -// ------- END CALLOUT ------- + if(flags.embed === '@link' && id in state.link) { + // TODO: may want to also match an existing linked subject against + // the current frame ... so different frames could produce different + // subjects that are only shared in-memory when the frames are the same -// ------- ARCGROUP ------- -/** - * Represents the <arcgroup> element. - * @class - * @extends SBGNBase - * @param {Object} params - * @param {string=} params.class_ - * @param {Glyph[]=} params.glyphs - * @param {Arc[]=} params.arcs - */ -var Arcgroup = function (params) { - ns.SBGNBase.call(this, params); - var params = checkParams(params, ['class_', 'glyphs', 'arcs']); - this.class_ = params.class_; - this.glyphs = params.glyphs || []; - this.arcs = params.arcs || []; -}; + // add existing linked subject + _addFrameOutput(parent, property, state.link[id]); + continue; + } -Arcgroup.prototype = Object.create(ns.SBGNBase.prototype); -Arcgroup.prototype.constructor = Arcgroup; + /* Note: In order to treat each top-level match as a compartmentalized + result, clear the unique embedded subjects map when the property is null, + which only occurs at the top-level. */ + if(property === null) { + state.uniqueEmbeds = {}; + } -/** - * @param {Glyph} glyph - */ -Arcgroup.prototype.addGlyph = function (glyph) { - this.glyphs.push(glyph); -}; + // start output for subject + var output = {}; + output['@id'] = id; + state.link[id] = output; -/** - * @param {Arc} arc - */ -Arcgroup.prototype.addArc = function (arc) { - this.arcs.push(arc); -}; + // if embed is @never or if a circular reference would be created by an + // embed, the subject cannot be embedded, just add the reference; + // note that a circular reference won't occur when the embed flag is + // `@link` as the above check will short-circuit before reaching this point + if(flags.embed === '@never' || + _createsCircularReference(subject, state.subjectStack)) { + _addFrameOutput(parent, property, output); + continue; + } -/** - * @return {Object} - xml2js formatted object - */ -Arcgroup.prototype.buildJsObj = function () { - var arcgroupObj = {}; + // if only the last match should be embedded + if(flags.embed === '@last') { + // remove any existing embed + if(id in state.uniqueEmbeds) { + _removeEmbed(state, id); + } + state.uniqueEmbeds[id] = {parent: parent, property: property}; + } - // attributes - var attributes = {}; - if(this.class_ != null) { - attributes.class = this.class_; - } - utils.addAttributes(arcgroupObj, attributes); + // push matching subject onto stack to enable circular embed checks + state.subjectStack.push(subject); - // children - this.baseToJsObj(arcgroupObj); - for(var i=0; i < this.glyphs.length; i++) { - if (i==0) { - arcgroupObj.glyph = []; - } - arcgroupObj.glyph.push(this.glyphs[i].buildJsObj()); - } - for(var i=0; i < this.arcs.length; i++) { - if (i==0) { - arcgroupObj.arc = []; - } - arcgroupObj.arc.push(this.arcs[i].buildJsObj()); - } - return arcgroupObj; -}; + // iterate over subject properties + var props = Object.keys(subject).sort(); + for(var i = 0; i < props.length; i++) { + var prop = props[i]; -/** - * @return {string} - */ -Arcgroup.prototype.toXML = function () { - return utils.buildString({arcgroup: this.buildJsObj()}); -}; + // copy keywords to output + if(_isKeyword(prop)) { + output[prop] = _clone(subject[prop]); + continue; + } -/** - * @param {String} string - * @return {Arcgroup} - */ -Arcgroup.fromXML = function (string) { - var arcgroup; - function fn (err, result) { - arcgroup = Arcgroup.fromObj(result); - }; - utils.parseString(string, fn); - return arcgroup; -}; + // explicit is on and property isn't in the frame, skip processing + if(flags.explicit && !(prop in frame)) { + continue; + } -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Arcgroup} - */ -Arcgroup.fromObj = function (jsObj) { - if (typeof jsObj.arcgroup == 'undefined') { - throw new Error("Bad XML provided, expected tagName arcgroup, got: " + Object.keys(jsObj)[0]); - } + // add objects + var objects = subject[prop]; + for(var oi = 0; oi < objects.length; ++oi) { + var o = objects[oi]; - var arcgroup = new ns.Arcgroup(); - jsObj = jsObj.arcgroup; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return arcgroup; - } + // recurse into list + if(_isList(o)) { + // add empty list + var list = {'@list': []}; + _addFrameOutput(output, prop, list); - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - arcgroup.class_ = attributes.class || null; - } - - if(jsObj.glyph) { - var glyphs = jsObj.glyph; - for (var i=0; i < glyphs.length; i++) { - var glyph = ns.Glyph.fromObj({glyph: glyphs[i]}); - arcgroup.addGlyph(glyph); - } - } - if(jsObj.arc) { - var arcs = jsObj.arc; - for (var i=0; i < arcs.length; i++) { - var arc = ns.Arc.fromObj({arc: arcs[i]}); - arcgroup.addArc(arc); - } - } - - arcgroup.baseFromObj(jsObj); - return arcgroup; -}; - -ns.Arcgroup = Arcgroup; -// ------- END ARCGROUP ------- + // add list objects + var src = o['@list']; + for(var n in src) { + o = src[n]; + if(_isSubjectReference(o)) { + var subframe = (prop in frame ? + frame[prop][0]['@list'] : _createImplicitFrame(flags)); + // recurse into subject reference + _frame(state, [o['@id']], subframe, list, '@list'); + } else { + // include other values automatically + _addFrameOutput(list, '@list', _clone(o)); + } + } + continue; + } -ns.render = renderExt; -ns.annot = annotExt; -ns.schematronValidator = schematronValidator; -module.exports = ns; + if(_isSubjectReference(o)) { + // recurse into subject reference + var subframe = (prop in frame ? + frame[prop] : _createImplicitFrame(flags)); + _frame(state, [o['@id']], subframe, output, prop); + } else { + // include other values automatically + _addFrameOutput(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; + } -},{"./libsbgn-annotations":29,"./libsbgn-render":30,"./schematronValidator":32,"./utilities":33,"xml2js":26}],32:[function(_dereq_,module,exports){ -var ns = {}; -var xml2js = _dereq_('xml2js'); -var Issue = _dereq_('./Issue').Issue; -ns.doValidation = function(file) { - try { - var isoContent= loadXMLDoc("templatelibsbgn.xslt"); - file = file.replace('libsbgn/0.3', 'libsbgn/0.2'); - var xml = new DOMParser().parseFromString(file, "text/xml"); - var xsltProcessor = new XSLTProcessor(); - var result ; - if (window.ActiveXObject || xhttp.responseType == "msxml-document") - { - result = xml.transformNode(xsl); - } - // code for Chrome, Firefox, Opera, etc. - else if (document.implementation && document.implementation.createDocument) - { - xsltProcessor = new XSLTProcessor(); - xsltProcessor.importStylesheet(isoContent); - result = xsltProcessor.transformToFragment(xml, document); - } - var tmp = document.createElement("div"); - tmp.appendChild(result); - result = tmp.innerHTML; - var parseString = xml2js.parseString; - var parsedResult; - parseString(result, function (err, data) { - parsedResult = data; - }); - var errors = []; - if(parsedResult["svrl:schematron-output"]["svrl:failed-assert"] == undefined) - return errors; - var errCount= parsedResult["svrl:schematron-output"]["svrl:failed-assert"].length; - for(var i=0;i= 0; --i) { + if(subjectStack[i]['@id'] === subjectToEmbed['@id']) { + return true; + } + } + return false; } -ns.parseStringKeepPrefix = function (string, fn) { - var parser = new xml2js.Parser({ - attrValueProcessors: [xml2js.processors.parseNumbers, xml2js.processors.parseBooleans] - }); - parser.parseString(string, fn); -}; +/** + * Gets the frame flag value for the given flag name. + * + * @param frame the frame. + * @param options the framing options. + * @param name the flag name. + * + * @return the flag value. + */ +function _getFrameFlag(frame, options, name) { + var flag = '@' + name; + var rval = (flag in frame ? frame[flag][0] : options[name]); + if(name === 'embed') { + // default is "@last" + // backwards-compatibility support for "embed" maps: + // true => "@last" + // false => "@never" + if(rval === true) { + rval = '@last'; + } else if(rval === false) { + rval = '@never'; + } else if(rval !== '@always' && rval !== '@never' && rval !== '@link') { + rval = '@last'; + } + } + return rval; +} -ns.buildString = function (obj) { - var xmlString = new xml2js.Builder({ - headless: true, - renderOpts: {pretty: false} - }).buildObject(obj); +/** + * Validates a JSON-LD frame, throwing an exception if the frame is invalid. + * + * @param frame the frame to validate. + */ +function _validateFrame(frame) { + if(!_isArray(frame) || frame.length !== 1 || !_isObject(frame[0])) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; a JSON-LD frame must be a single object.', + 'jsonld.SyntaxError', {frame: frame}); + } +} - /* dirty hack needed to solve the newline char encoding problem - xml2js doesn't encode \n as we need to do it manually - */ - var re = /