From 39c913e4aa02ade43f7c29c6aefa255c37ccb967 Mon Sep 17 00:00:00 2001 From: royludo Date: Tue, 27 Feb 2018 16:04:06 +0100 Subject: [PATCH 01/29] implement sbgnml 0.2 export --- src/utilities/file-utilities-factory.js | 5 ++- .../json-to-sbgnml-converter-factory.js | 43 +++++++++++++++++-- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/utilities/file-utilities-factory.js b/src/utilities/file-utilities-factory.js index 72b3f93d..574de166 100644 --- a/src/utilities/file-utilities-factory.js +++ b/src/utilities/file-utilities-factory.js @@ -236,8 +236,9 @@ fileUtilities.loadTDFile = function(file, callback1){ }; - fileUtilities.saveAsSbgnml = function(filename, renderInfo, mapProperties) { - var sbgnmlText = jsonToSbgnml.createSbgnml(filename, renderInfo, mapProperties); + // supported versions are either 0.2 or 0.3 + fileUtilities.saveAsSbgnml = function(filename, version, renderInfo, mapProperties) { + var sbgnmlText = jsonToSbgnml.createSbgnml(filename, version, renderInfo, mapProperties); var blob = new Blob([sbgnmlText], { type: "text/plain;charset=utf-8;", }); diff --git a/src/utilities/json-to-sbgnml-converter-factory.js b/src/utilities/json-to-sbgnml-converter-factory.js index aa6d2818..4a84095e 100644 --- a/src/utilities/json-to-sbgnml-converter-factory.js +++ b/src/utilities/json-to-sbgnml-converter-factory.js @@ -42,7 +42,13 @@ module.exports = function () { cy = param.sbgnCyInstance.getCy(); } - jsonToSbgnml.createSbgnml = function(filename, renderInfo, mapProperties){ + /* + version is either 0.2 or 0.3, 0.3 used as default if none provided. + Only difference right now is that element doesn't have an id attribute in 0.2, and has on in 0.3. + Serious changes occur between the format version for submaps content. Those changes are not implemented yet. + TODO implement 0.3 changes when submap support is fully there. + */ + jsonToSbgnml.createSbgnml = function(filename, version, renderInfo, mapProperties){ var self = this; var mapID = textUtilities.getXMLValidId(filename); var hasExtension = false; @@ -55,6 +61,17 @@ module.exports = function () { hasRenderExtension = true; } + if(typeof version === 'undefined') { + // default if not specified + version = "0.3"; + } + + // check version validity + if(version !== "0.2" && version !== "0.3") { + console.error("Invalid SBGN-ML version provided. Expected 0.2 or 0.3, got: " + version); + return "Error"; + } + var mapLanguage; if(elementUtilities.mapType == "PD") { mapLanguage = "process description"; @@ -69,8 +86,16 @@ module.exports = function () { //add headers xmlHeader = "\n"; - var sbgn = new libsbgnjs.Sbgn({xmlns: 'http://sbgn.org/libsbgn/0.3'}); - var map = new libsbgnjs.Map({language: mapLanguage, id: mapID}); + var sbgn = new libsbgnjs.Sbgn({xmlns: 'http://sbgn.org/libsbgn/' + version}); + + var map; + if(version === "0.3") { + var map = new libsbgnjs.Map({language: mapLanguage, id: mapID}); + } + else if(version === "0.2") { + var map = new libsbgnjs.Map({language: mapLanguage}); + } + if (hasExtension) { // extension is there var extension = new libsbgnjs.Extension(); if (hasRenderExtension) { @@ -112,7 +137,17 @@ module.exports = function () { }); sbgn.addMap(map); - return prettyprint.xml(xmlHeader + sbgn.toXML()); + + /* + prettyprint puts a line break inside the root tag before the xmlns attribute. + This is perfecly valid, but Vanted doesn't like it and cannot load those files as is. + This line break is removed here to make Newt output directly compatible with Vanted. This issue will be reported + to the Vanted guys and hopefully fixed at some point. After that the following workaround can be removed. + */ + xmlbody = prettyprint.xml(sbgn.toXML()).replace(" Date: Tue, 27 Feb 2018 16:17:34 +0100 Subject: [PATCH 02/29] update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 965732c8..8739bf67 100644 --- a/README.md +++ b/README.md @@ -178,8 +178,8 @@ Loads the given sbgnml file. Optionally apply a callback function upon loading. `instance.loadSBGNMLText(textData)` Loads a graph from the given text data in sbgnml format. -`instance.saveAsSbgnml(filename)` -Exports the current graph to an sbgnml file with the given filename. +`instance.saveAsSbgnml(filename[, version])` +Exports the current graph to an sbgnml file with the given filename. A SBGN-ML version can be provided, either 0.2 or 0.3. No version defaults to 0.3. `instance.enablePorts()` Enable node ports. From fd78f082c253ed8160abd84de0bb9310ba5ba284 Mon Sep 17 00:00:00 2001 From: metincansiper Date: Fri, 2 Mar 2018 14:51:56 -0800 Subject: [PATCH 03/29] Expose sbgnviz.validMapProperties --- README.md | 3 +++ src/index.js | 2 ++ src/utilities/validMapProperties.js | 24 ++++++++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 src/utilities/validMapProperties.js diff --git a/README.md b/README.md index 8739bf67..7dc91641 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,9 @@ edge.data('bendPointPositions'); `sbgnviz.register(options)` Register with libraries before creating instances +`sbgnviz.validMapProperties` +A lookup object for valid map properties. + `var instance = sbgnviz(options)` Creates an extension instance with the given options diff --git a/src/index.js b/src/index.js index 88d36de3..ac00bc97 100644 --- a/src/index.js +++ b/src/index.js @@ -94,6 +94,8 @@ return api; }; + sbgnviz.validMapProperties = require('./utilities/validMapProperties'); + sbgnviz.register = function (_libs) { var libs = {}; diff --git a/src/utilities/validMapProperties.js b/src/utilities/validMapProperties.js new file mode 100644 index 00000000..74041f54 --- /dev/null +++ b/src/utilities/validMapProperties.js @@ -0,0 +1,24 @@ +/* +* Lookup object for valid map properties. +* In the future it could be combined with other similar properties/functions in a new file. +*/ +module.exports = { + compoundPadding: true, + extraCompartmentPadding: true, + extraComplexPadding: true, + arrowScale: true, + showComplexName: true, + dynamicLabelSize: true, + fitLabelsToNodes: true, + fitLabelsToInfoboxes: true, + rearrangeAfterExpandCollapse: true, + animateOnDrawingChanges: true, + adjustNodeLabelFontSizeAutomatically: true, + enablePorts: true, + allowCompoundNodeResize: true, + mapColorScheme: true, + defaultInfoboxHeight: true, + defaultInfoboxWidth: true, + mapName: true, + mapDescription: true +}; From 58f430f388501bc166066624e8a7361194b24bfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6kberk=20Karaca?= Date: Wed, 30 May 2018 16:03:24 +0300 Subject: [PATCH 04/29] Plain SBGN-ML support (#197) * Add plain sbgnml support * Fix extension condition * Fix version condition of createSbgnml function --- src/utilities/json-to-sbgnml-converter-factory.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/utilities/json-to-sbgnml-converter-factory.js b/src/utilities/json-to-sbgnml-converter-factory.js index 4a84095e..93b57656 100644 --- a/src/utilities/json-to-sbgnml-converter-factory.js +++ b/src/utilities/json-to-sbgnml-converter-factory.js @@ -43,7 +43,7 @@ module.exports = function () { } /* - version is either 0.2 or 0.3, 0.3 used as default if none provided. + version is either 0.2 or 0.3 or plain, 0.3 used as default if none provided. Only difference right now is that element doesn't have an id attribute in 0.2, and has on in 0.3. Serious changes occur between the format version for submaps content. Those changes are not implemented yet. TODO implement 0.3 changes when submap support is fully there. @@ -67,8 +67,8 @@ module.exports = function () { } // check version validity - if(version !== "0.2" && version !== "0.3") { - console.error("Invalid SBGN-ML version provided. Expected 0.2 or 0.3, got: " + version); + if(version !== "0.2" && version !== "0.3" && version !== "plain") { + console.error("Invalid SBGN-ML version provided. Expected 0.2, 0.3 or plain, got: " + version); return "Error"; } @@ -86,13 +86,13 @@ module.exports = function () { //add headers xmlHeader = "\n"; - var sbgn = new libsbgnjs.Sbgn({xmlns: 'http://sbgn.org/libsbgn/' + version}); + var sbgn = new libsbgnjs.Sbgn({xmlns: 'http://sbgn.org/libsbgn/' + (version === "plain") ? "0.2" : version}); var map; if(version === "0.3") { var map = new libsbgnjs.Map({language: mapLanguage, id: mapID}); } - else if(version === "0.2") { + else if(version === "0.2" || version === "plain") { var map = new libsbgnjs.Map({language: mapLanguage}); } @@ -125,6 +125,8 @@ module.exports = function () { }); // add them to the map for(var i=0; i Date: Thu, 31 May 2018 11:30:16 +0300 Subject: [PATCH 05/29] Fix xmlns url bug --- src/utilities/json-to-sbgnml-converter-factory.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utilities/json-to-sbgnml-converter-factory.js b/src/utilities/json-to-sbgnml-converter-factory.js index 93b57656..07a1f48d 100644 --- a/src/utilities/json-to-sbgnml-converter-factory.js +++ b/src/utilities/json-to-sbgnml-converter-factory.js @@ -86,7 +86,8 @@ module.exports = function () { //add headers xmlHeader = "\n"; - var sbgn = new libsbgnjs.Sbgn({xmlns: 'http://sbgn.org/libsbgn/' + (version === "plain") ? "0.2" : version}); + var versionNo = (version === "plain") ? "0.2" : version; + var sbgn = new libsbgnjs.Sbgn({xmlns: 'http://sbgn.org/libsbgn/' + versionNo}); var map; if(version === "0.3") { From 37813b1e48525d4e05509e8685bef602fdb738d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87and=C4=B1ro=C4=9Flu?= Date: Thu, 31 May 2018 12:15:09 +0300 Subject: [PATCH 06/29] Fix save as image empty response bug --- src/utilities/file-utilities-factory.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/utilities/file-utilities-factory.js b/src/utilities/file-utilities-factory.js index 574de166..3e43d023 100644 --- a/src/utilities/file-utilities-factory.js +++ b/src/utilities/file-utilities-factory.js @@ -82,6 +82,13 @@ module.exports = function () { // this is to remove the beginning of the pngContent: data:img/png;base64, var b64data = pngContent.substr(pngContent.indexOf(",") + 1); + + // lower quality when response is empty + if(!b64data || b64data === ""){ + pngContent = cy.png({maxWidth: 15000, maxHeight: 15000, full: true}); + b64data = pngContent.substr(pngContent.indexOf(",") + 1); + } + saveAs(b64toBlob(b64data, "image/png"), filename || "network.png"); }; @@ -90,6 +97,13 @@ module.exports = function () { // this is to remove the beginning of the pngContent: data:img/png;base64, var b64data = jpgContent.substr(jpgContent.indexOf(",") + 1); + + // lower quality when response is empty + if(!b64data || b64data === ""){ + jpgContent = cy.jpg({maxWidth: 15000, maxHeight: 15000, full: true}); + b64data = jpgContent.substr(jpgContent.indexOf(",") + 1); + } + saveAs(b64toBlob(b64data, "image/jpg"), filename || "network.jpg"); }; From 79d42d76e390e5e3823a21ada63847250d7cc059 Mon Sep 17 00:00:00 2001 From: kaansancak Date: Thu, 31 May 2018 16:21:00 +0300 Subject: [PATCH 07/29] Fix left to right orientation bug iVis-at-Bilkent/newt#260 --- src/utilities/element-utilities-factory.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/utilities/element-utilities-factory.js b/src/utilities/element-utilities-factory.js index 358db1b1..5b71ff3f 100644 --- a/src/utilities/element-utilities-factory.js +++ b/src/utilities/element-utilities-factory.js @@ -715,7 +715,7 @@ module.exports = function () { */ elementUtilities.setPortsOrdering = function( nodes, ordering, portDistance ) { /* - * Retursn if the given portId is porttarget of any of the given edges. + * Returns if the given portId is porttarget of any of the given edges. * These edges are expected to be the edges connected to the node associated with that port. */ var isPortTargetOfAnyEdge = function(edges, portId) { @@ -728,6 +728,21 @@ module.exports = function () { return false; }; + + /* + * Returns if the given portId is portsource of any of the given edges. + * These edges are expected to be the edges connected to the node associated with that port. + */ + var isPortSourceOfAnyEdge = function(edges, portId) { + for (var i = 0; i < edges.length; i++) { + if (edges[i].data('portsource') === portId) { + return true; + } + } + + return false; + }; + portDistance = portDistance ? portDistance : 70; // The default port distance is 60 cy.startBatch(); @@ -755,7 +770,7 @@ module.exports = function () { var portsource, porttarget; // The ports which are portsource/porttarget of the connected edges // Determine the portsource and porttarget - if ( isPortTargetOfAnyEdge(connectedEdges, ports[0].id) ) { + if ( isPortTargetOfAnyEdge(connectedEdges, ports[0].id) || isPortSourceOfAnyEdge(connectedEdges, ports[1].id) ) { porttarget = ports[0]; portsource = ports[1]; } From c440c9bfde2dd2000d73e0679326b2c0882492de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6kberk=20Karaca?= Date: Fri, 1 Jun 2018 15:46:33 +0300 Subject: [PATCH 08/29] Fix simple chemical clone marker area --- src/sbgn-extensions/sbgn-cy-renderer.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/sbgn-extensions/sbgn-cy-renderer.js b/src/sbgn-extensions/sbgn-cy-renderer.js index b5362083..34391bbc 100644 --- a/src/sbgn-extensions/sbgn-cy-renderer.js +++ b/src/sbgn-extensions/sbgn-cy-renderer.js @@ -1074,12 +1074,21 @@ module.exports = function () { var firstCircleCenterY = centerY; var secondCircleCenterX = centerX + width / 2 - cornerRadius; var secondCircleCenterY = centerY; - - simpleChemicalLeftClone(context, firstCircleCenterX, firstCircleCenterY, - 2 * cornerRadius, 2 * cornerRadius, cloneMarker, opacity); - - simpleChemicalRightClone(context, secondCircleCenterX, secondCircleCenterY, - 2 * cornerRadius, 2 * cornerRadius, cloneMarker, opacity); + var bottomCircleCenterX = centerX; + var bottomCircleCenterY = centerY + height/2 - cornerRadius; + + if (width < height) { + simpleChemicalLeftClone(context, bottomCircleCenterX, bottomCircleCenterY, + 2 * cornerRadius, 2 * cornerRadius, cloneMarker, opacity); + simpleChemicalRightClone(context, bottomCircleCenterX, bottomCircleCenterY, + 2 * cornerRadius, 2 * cornerRadius, cloneMarker, opacity); + } + else { + simpleChemicalLeftClone(context, firstCircleCenterX, firstCircleCenterY, + 2 * cornerRadius, 2 * cornerRadius, cloneMarker, opacity); + simpleChemicalRightClone(context, secondCircleCenterX, secondCircleCenterY, + 2 * cornerRadius, 2 * cornerRadius, cloneMarker, opacity); + } var oldStyle = context.fillStyle; context.fillStyle = $$.sbgn.colors.clone; From 5972d8b3ebee296b4d854cb6cc810dd0e194aeb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6kberk=20Karaca?= Date: Fri, 1 Jun 2018 18:39:02 +0300 Subject: [PATCH 09/29] Fix complex shape clone marker area --- src/sbgn-extensions/sbgn-cy-renderer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sbgn-extensions/sbgn-cy-renderer.js b/src/sbgn-extensions/sbgn-cy-renderer.js index 34391bbc..f207e011 100644 --- a/src/sbgn-extensions/sbgn-cy-renderer.js +++ b/src/sbgn-extensions/sbgn-cy-renderer.js @@ -1138,8 +1138,8 @@ module.exports = function () { complex: function (context, centerX, centerY, width, height, cornerLength, cloneMarker, isMultimer, opacity) { if (cloneMarker != null) { - var cpX = cornerLength / width; - var cpY = cornerLength / height; + var cpX = (width >= 50) ? cornerLength / width : cornerLength / 50; + var cpY = (height >= 50) ? cornerLength / height : cornerLength / 50; var cloneWidth = width; var cloneHeight = height * cpY / 2; var cloneX = centerX; From 1fe468980f7af6bb0718149c5606160e87ba9655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6kberk=20Karaca?= Date: Tue, 5 Jun 2018 12:06:13 +0300 Subject: [PATCH 10/29] Ignore bendpoints if source and target are the same node --- src/utilities/sbgnml-to-json-converter-factory.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/utilities/sbgnml-to-json-converter-factory.js b/src/utilities/sbgnml-to-json-converter-factory.js index c60bc756..dba902bb 100644 --- a/src/utilities/sbgnml-to-json-converter-factory.js +++ b/src/utilities/sbgnml-to-json-converter-factory.js @@ -561,7 +561,10 @@ module.exports = function () { var edgeObj = {}; var styleObj = {}; - var bendPointPositions = self.getArcBendPointPositions(ele); + var bendPointPositions = []; + if (sourceAndTarget.source !== sourceAndTarget.target) { + bendPointPositions = self.getArcBendPointPositions(ele); + } edgeObj.id = ele.id || undefined; edgeObj.class = ele.class_; From 622651688fb0c264221c085deda0a0626503c9b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87and=C4=B1ro=C4=9Flu?= Date: Tue, 5 Jun 2018 14:41:35 +0300 Subject: [PATCH 11/29] Enable load and save with background images --- src/utilities/file-utilities-factory.js | 21 +++++++++++++++++++ .../json-to-sbgnml-converter-factory.js | 19 ++++++++++++++++- .../sbgnml-to-json-converter-factory.js | 7 +++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/utilities/file-utilities-factory.js b/src/utilities/file-utilities-factory.js index 3e43d023..8983c52f 100644 --- a/src/utilities/file-utilities-factory.js +++ b/src/utilities/file-utilities-factory.js @@ -268,5 +268,26 @@ fileUtilities.loadTDFile = function(file, callback1){ return sbgnmlToJson.convert(textToXmlObject(sbgnmlText)); }; + fileUtilities.loadBackgroundImage = function(node, file) { + + var reader = new FileReader(); + reader.readAsDataURL(file); + + reader.onload = function (e) { + var img = reader.result; + if(img){ + var opt = { + 'background-image' : img, + 'background-fit' : 'contain', + 'background-image-opacity' : '0.7px' + }; + node.style(opt); + } + else{ + alert('Error: Image cannot be applied as background!'); + } + }; + }; + return fileUtilities; }; diff --git a/src/utilities/json-to-sbgnml-converter-factory.js b/src/utilities/json-to-sbgnml-converter-factory.js index 07a1f48d..e79996aa 100644 --- a/src/utilities/json-to-sbgnml-converter-factory.js +++ b/src/utilities/json-to-sbgnml-converter-factory.js @@ -328,12 +328,29 @@ module.exports = function () { hasNewtExt = true; } + // add info for background image + if(node._private.style['background-image']){ + var img = node._private.style['background-image']; + var fit = node._private.style['background-fit']; + var opacity = node._private.style['background-image-opacity']; + + sbgnvizExtString += ""+sbgnvizExtString+""); } - + // current glyph is done glyphList.push(glyph); diff --git a/src/utilities/sbgnml-to-json-converter-factory.js b/src/utilities/sbgnml-to-json-converter-factory.js index dba902bb..80ec497f 100644 --- a/src/utilities/sbgnml-to-json-converter-factory.js +++ b/src/utilities/sbgnml-to-json-converter-factory.js @@ -365,6 +365,13 @@ module.exports = function () { y: Number(result.sbgnviz.positionBeforeSaving[0].$.y)}; nodeObj.collapse = true; } + if (result.sbgnviz.backgroundImage){ + var bgImage = result.sbgnviz.backgroundImage[0].$; + + styleObj['background-image'] = bgImage.img; + styleObj['background-fit'] = bgImage.fit; + styleObj['background-image-opacity'] = bgImage.opacity; + } }); } From f76ccecf252901705149d8ed1f1899f2298f9719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87and=C4=B1ro=C4=9Flu?= Date: Wed, 6 Jun 2018 16:24:49 +0300 Subject: [PATCH 12/29] Fix background image bug --- src/sbgn-extensions/sbgn-cy-renderer.js | 73 ++++++++++++++++++++++--- src/utilities/file-utilities-factory.js | 2 +- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/src/sbgn-extensions/sbgn-cy-renderer.js b/src/sbgn-extensions/sbgn-cy-renderer.js index f207e011..57be8d3a 100644 --- a/src/sbgn-extensions/sbgn-cy-renderer.js +++ b/src/sbgn-extensions/sbgn-cy-renderer.js @@ -357,7 +357,7 @@ module.exports = function () { cyBaseNodeShapes["simple chemical"] = { multimerPadding: 5, - draw: function (context, node) { + draw: function (context, node, imgObj) { var centerX = node._private.position.x; var centerY = node._private.position.y; @@ -389,6 +389,13 @@ module.exports = function () { context.stroke(); + // draw background image + if(imgObj){ + context.clip(); + context.drawImage(imgObj.img, 0, 0, imgObj.imgW, imgObj.imgH, imgObj.x, imgObj.y, imgObj.w, imgObj.h ); + context.restore(); + } + $$.sbgn.cloneMarker.simpleChemical(context, centerX, centerY, width - padding, height - padding, cloneMarker, false, node.css('background-opacity')); @@ -460,7 +467,7 @@ module.exports = function () { cyBaseNodeShapes["macromolecule"] = { points: cyMath.generateUnitNgonPoints(4, 0), multimerPadding: 5, - draw: function (context, node) { + draw: function (context, node, imgObj) { var width = node.width(); var height = node.height(); var centerX = node._private.position.x; @@ -495,6 +502,13 @@ module.exports = function () { context.stroke(); + // draw background image + if(imgObj){ + context.clip(); + context.drawImage(imgObj.img, 0, 0, imgObj.imgW, imgObj.imgH, imgObj.x, imgObj.y, imgObj.w, imgObj.h ); + context.restore(); + } + $$.sbgn.cloneMarker.macromolecule(context, centerX, centerY, width, height, cloneMarker, false, node.css('background-opacity')); @@ -569,7 +583,7 @@ module.exports = function () { points: [], multimerPadding: 5, cornerLength: 24, - draw: function (context, node) { + draw: function (context, node, imgObj) { var width = node.outerWidth() - parseFloat(node.css('border-width')); var height = node.outerHeight()- parseFloat(node.css('border-width')); var centerX = node._private.position.x; @@ -580,7 +594,6 @@ module.exports = function () { var multimerPadding = cyBaseNodeShapes["complex"].multimerPadding; var cloneMarker = node._private.data.clonemarker; - cyBaseNodeShapes["complex"].points = $$.sbgn.generateComplexShapePoints(cornerLength, width, height); @@ -608,6 +621,13 @@ module.exports = function () { context.fill(); context.stroke(); + + // draw background image + if(imgObj){ + context.clip(); + context.drawImage(imgObj.img, 0, 0, imgObj.imgW, imgObj.imgH, imgObj.x, imgObj.y, imgObj.w, imgObj.h ); + context.restore(); + } $$.sbgn.cloneMarker.complex(context, centerX, centerY, width, height, cornerLength, cloneMarker, false, @@ -695,7 +715,7 @@ module.exports = function () { cyBaseNodeShapes["nucleic acid feature"] = { points: cyMath.generateUnitNgonPointsFitToSquare(4, 0), multimerPadding: 5, - draw: function (context, node) { + draw: function (context, node, imgObj) { var centerX = node._private.position.x; var centerY = node._private.position.y; ; @@ -727,6 +747,13 @@ module.exports = function () { centerY, cornerRadius); context.stroke(); + + // draw background image + if(imgObj){ + context.clip(); + context.drawImage(imgObj.img, 0, 0, imgObj.imgW, imgObj.imgH, imgObj.x, imgObj.y, imgObj.w, imgObj.h ); + context.restore(); + } $$.sbgn.cloneMarker.nucleicAcidFeature(context, centerX, centerY, width, height, cloneMarker, false, @@ -797,7 +824,7 @@ module.exports = function () { }; cyBaseNodeShapes["source and sink"] = { points: cyMath.generateUnitNgonPoints(4, 0), - draw: function (context, node) { + draw: function (context, node, imgObj) { var centerX = node._private.position.x; var centerY = node._private.position.y; @@ -811,6 +838,13 @@ module.exports = function () { width, height); context.stroke(); + + // draw background image + if(imgObj){ + context.clip(); + context.drawImage(imgObj.img, 0, 0, imgObj.imgW, imgObj.imgH, imgObj.x, imgObj.y, imgObj.w, imgObj.h ); + context.restore(); + } context.beginPath(); var scaleX = width * Math.sqrt(2) / 2, scaleY = height * Math.sqrt(2) / 2; @@ -828,7 +862,7 @@ module.exports = function () { }; cyBaseNodeShapes["biological activity"] = { points: cyMath.generateUnitNgonPointsFitToSquare(4, 0), - draw: function (context, node) { + draw: function (context, node, imgObj) { var width = node.width(); var height = node.height(); var centerX = node._private.position.x; @@ -842,6 +876,13 @@ module.exports = function () { context.fill(); context.stroke(); + + // draw background image + if(imgObj){ + context.clip(); + context.drawImage(imgObj.img, 0, 0, imgObj.imgW, imgObj.imgH, imgObj.x, imgObj.y, imgObj.w, imgObj.h ); + context.restore(); + } var oldStyle = context.fillStyle; $$.sbgn.forceOpacityToOne(node, context); @@ -889,7 +930,7 @@ module.exports = function () { name: 'compartment', points: math.generateUnitNgonPointsFitToSquare( 4, 0 ), - draw: function( context, node){ + draw: function( context, node, imgObj){ var padding = parseInt(node.css('border-width')); var width = node.outerWidth() - padding; var height = node.outerHeight() - padding; @@ -900,6 +941,13 @@ module.exports = function () { context.fill(); context.stroke(); + // draw background image + if(imgObj){ + context.clip(); + context.drawImage(imgObj.img, 0, 0, imgObj.imgW, imgObj.imgH, imgObj.x, imgObj.y, imgObj.w, imgObj.h ); + context.restore(); + } + var oldStyle = context.fillStyle; $$.sbgn.forceOpacityToOne(node, context); $$.sbgn.drawStateAndInfos(node, context, centerX, centerY); @@ -1000,7 +1048,7 @@ module.exports = function () { }; cyBaseNodeShapes["oldCompartment"] = { points: cyMath.generateUnitNgonPointsFitToSquare( 4, 0 ), - draw: function (context, node) { + draw: function (context, node, imgObj) { var padding = parseInt(node.css('border-width')); var width = node.outerWidth() - padding; var height = node.outerHeight() - padding; @@ -1014,6 +1062,13 @@ module.exports = function () { context.stroke(); + // draw background image + if(imgObj){ + context.clip(); + context.drawImage(imgObj.img, 0, 0, imgObj.imgW, imgObj.imgH, imgObj.x, imgObj.y, imgObj.w, imgObj.h ); + context.restore(); + } + var oldStyle = context.fillStyle; $$.sbgn.forceOpacityToOne(node, context); $$.sbgn.drawStateAndInfos(node, context, centerX, centerY); diff --git a/src/utilities/file-utilities-factory.js b/src/utilities/file-utilities-factory.js index 8983c52f..edbf0663 100644 --- a/src/utilities/file-utilities-factory.js +++ b/src/utilities/file-utilities-factory.js @@ -279,7 +279,7 @@ fileUtilities.loadTDFile = function(file, callback1){ var opt = { 'background-image' : img, 'background-fit' : 'contain', - 'background-image-opacity' : '0.7px' + 'background-image-opacity' : '1' }; node.style(opt); } From b6ed0d0de51f6e6ba6693222c45c18396e1c8af6 Mon Sep 17 00:00:00 2001 From: kaansancak Date: Thu, 7 Jun 2018 01:26:09 +0300 Subject: [PATCH 13/29] Implement scalability for infoboxes iVis-at-Bilkent/newt#240 --- src/utilities/classes.js | 224 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 214 insertions(+), 10 deletions(-) diff --git a/src/utilities/classes.js b/src/utilities/classes.js index 20307c09..5c245d82 100644 --- a/src/utilities/classes.js +++ b/src/utilities/classes.js @@ -344,12 +344,12 @@ StateVariable.create = function(parentNode, cy, value, variable, bbox, location, // link to layout position = StateVariable.addToParent(stateVar, cy, parentNode, location, position, index); - return { index: StateVariable.getParent(stateVar, cy).data('statesandinfos').indexOf(stateVar), location: stateVar.anchorSide, position: position } + }; StateVariable.remove = function (mainObj, cy) { @@ -639,10 +639,15 @@ AuxUnitLayout.setParentNodeRef = function(mainObj, parentNode) { * These options can be defined at the instance level. If it is found in an instance, then it * takes precedence. If not found, the following class' values are used. */ -AuxUnitLayout.outerMargin = 10; +AuxUnitLayout.outerMargin = 5; AuxUnitLayout.unitGap = 5; -AuxUnitLayout.alwaysShowAuxUnits = false; +AuxUnitLayout.currentTopUnitGap = 5; +AuxUnitLayout.currentBottomUnitGap = 5; +AuxUnitLayout.currentLeftUnitGap = 5; +AuxUnitLayout.currentRightUnitGap = 5; +AuxUnitLayout.alwaysShowAuxUnits = true; AuxUnitLayout.maxUnitDisplayed = -1; +AuxUnitLayout.lastPos = -1; AuxUnitLayout.update = function(mainObj, cy, doForceUpdate) { AuxUnitLayout.precomputeCoords(mainObj, cy, doForceUpdate); @@ -659,14 +664,15 @@ AuxUnitLayout.addAuxUnit = function(mainObj, cy, unit, position) { } AuxUnitLayout.updateLengthCache(mainObj, cy); - AuxUnitLayout.update(mainObj, cy, true); + AuxUnitLayout.update(mainObj, cy, true);/* if (AuxUnitLayout.getAlwaysShowAuxUnits(mainObj)) { // set a minimum size according to both sides on the same orientation AuxUnitLayout.setParentMinLength(mainObj, cy); // need to resize the parent in case the space was too small AuxUnitLayout.resizeParent(mainObj, cy, mainObj.lengthUsed); - } + }*/ //cy.style().update(); // <- was it really necessary ? + var parentNode = AuxUnitLayout.getParentNode(mainObj, cy); return position; }; @@ -674,11 +680,12 @@ AuxUnitLayout.removeAuxUnit = function(mainObj, cy, unit) { var index = mainObj.units.indexOf(unit); mainObj.units.splice(index, 1); AuxUnitLayout.updateLengthCache(mainObj, cy); - AuxUnitLayout.update(mainObj, cy, true); + AuxUnitLayout.update(mainObj, cy, true);/* if (AuxUnitLayout.getAlwaysShowAuxUnits(mainObj)) { // set a minimum size according to both sides on the same orientation AuxUnitLayout.setParentMinLength(mainObj, cy); - } + }*/ + var parentNode = AuxUnitLayout.getParentNode(mainObj, cy); cy.style().update(); }; @@ -764,6 +771,7 @@ AuxUnitLayout.getDrawableUnitAmount = function(mainObj) { AuxUnitLayout.setDisplayedUnits = function (mainObj, cy) { // get the length of the side on which we draw + var availableSpace; if (AuxUnitLayout.isTorB(mainObj)) { availableSpace = AuxUnitLayout.getParentNode(mainObj, cy).outerWidth(); @@ -790,13 +798,197 @@ AuxUnitLayout.setDisplayedUnits = function (mainObj, cy) { } }; + +AuxUnitLayout.getUsedWidth = function(node, tb){ + var units = tb.units; + var totalWidth = 0; + for (var i = 0; i < units.length; i++) { + totalWidth += units[i].bbox.w; + } + return totalWidth; +} + +AuxUnitLayout.getUsedHeight = function(node, tb){ + var units = tb.units; + var totalHeight = 0; + for (var i = 0; i < units.length; i++) { + totalHeight += units[i].bbox.h; + } + return totalHeight; +} + +AuxUnitLayout.getUsedLengthTB = function(node, tb){ + var units = tb.units; + return AuxUnitLayout.getUsedWidth(node, tb) + (units.length + 1) * AuxUnitLayout.unitGap; //One gap for leftmost outer margin +} + +AuxUnitLayout.getUsedLengthLR = function(node, tb){ + var units = tb.units; + return AuxUnitLayout.getUsedHeight(node, tb) + (units.length + 1) * AuxUnitLayout.unitGap; //One gap for leftmost outer margin +} + +AuxUnitLayout.fitUnits = function (node) { + var top = node.data('auxunitlayouts').top; + var bottom = node.data('auxunitlayouts').bottom; + var left = node.data('auxunitlayouts').left; + var right = node.data('auxunitlayouts').right; + //Get Parent node and find parent width + var parentWidth = node.data('bbox').w; + var parentHeight = node.data('bbox').h; + + var usedLength; + var totalWidth; + var estimatedGap; + var units; + var addedShift; + + if (top !== undefined) { + //If new unit adaed done reset unit gap + if(AuxUnitLayout.lastPos === "top"){ + AuxUnitLayout.currentTopUnitGap = AuxUnitLayout.unitGap; + AuxUnitLayout.lastPos = -1; + } + //Find total top length + usedLength = AuxUnitLayout.getUsedLengthTB(node, top); + units = top.units; + //Compare the side lengths + if (parentWidth < usedLength) { + //If there is not enough space + //Estimate new margin & gap + totalWidth = AuxUnitLayout.getUsedWidth(node, top); + estimatedGap = (parentWidth - totalWidth) / (units.length + 1); + //Else scale by using available space, reducing margins and gaps. + //Check if new gap is enough to fit + + units[0].bbox.x -= AuxUnitLayout.currentTopUnitGap - estimatedGap; + + for (var i = 1; i < units.length; i++) { //Calculate new margins + units[i].bbox.x = units[i-1].bbox.x + units[i-1].bbox.w/2 + units[i].bbox.w/2 + estimatedGap; + } + AuxUnitLayout.currentTopUnitGap = estimatedGap; + + + } + else if(AuxUnitLayout.currentTopUnitGap < AuxUnitLayout.unitGap){ + for (var i = 0; i < units.length; i++) { //Shift all units + units[i].bbox.x -= (AuxUnitLayout.currentTopUnitGap - AuxUnitLayout.unitGap) * (i+1); + } + AuxUnitLayout.currentTopUnitGap = AuxUnitLayout.unitGap; + } + } + + if (bottom !== undefined) { + //If new unit adaed done reset unit gap + if(AuxUnitLayout.lastPos === "bottom"){ + AuxUnitLayout.currentBottomUnitGap = AuxUnitLayout.unitGap; + AuxUnitLayout.lastPos = -1; + } + //Find total currentBottomUnitGap length + usedLength = AuxUnitLayout.getUsedLengthTB(node, bottom); + units = bottom.units; + //Compare the side lengths + if (parentWidth < usedLength) { + //If there is not enough space + //Estimate new margin & gap + totalWidth = AuxUnitLayout.getUsedWidth(node, bottom); + estimatedGap = (parentWidth - totalWidth) / (units.length + 1); + //Else scale by using available space, reducing margins and gaps. + //Check if new gap is enough to fit + + units[0].bbox.x -= AuxUnitLayout.currentBottomUnitGap - estimatedGap; + + for (var i = 1; i < units.length; i++) { //Shift all units + units[i].bbox.x = units[i-1].bbox.x + units[i-1].bbox.w/2 + units[i].bbox.w/2 + estimatedGap; + } + AuxUnitLayout.currentBottomUnitGap = estimatedGap; + + + } + else if(AuxUnitLayout.currentBottomUnitGap < AuxUnitLayout.unitGap){ + for (var i = 0; i < units.length; i++) { //Shift all units + units[i].bbox.x -= (AuxUnitLayout.currentBottomUnitGap - AuxUnitLayout.unitGap) * (i+1); + } + AuxUnitLayout.currentBottomUnitGap = AuxUnitLayout.unitGap; + } + } + + if (left !== undefined) { + //Find total left length + //If new unit adaed done reset unit gap + if(AuxUnitLayout.lastPos === "left"){ + AuxUnitLayout.currentLeftUnitGap = AuxUnitLayout.unitGap; + AuxUnitLayout.lastPos = -1; + } + usedLength = AuxUnitLayout.getUsedLengthLR(node, left); + units = left.units; + //Compare the side lengths + if (parentHeight < usedLength) { + //If there is not enough space + //Estimate new margin & gap + totalHeight = AuxUnitLayout.getUsedHeight(node, left); + estimatedGap = (parentHeight - totalHeight) / (units.length + 1); + //Else scale by using available space, reducing margins and gaps. + //Check if new gap is enough to fit + units[0].bbox.y -= AuxUnitLayout.currentLeftUnitGap - estimatedGap; + + for (var i = 1; i < units.length; i++) { //Shift all units + units[i].bbox.y = units[i-1].bbox.y + units[i-1].bbox.h/2 + units[i].bbox.h/2 + estimatedGap; + } + AuxUnitLayout.currentLeftUnitGap = estimatedGap; + + } + else if(AuxUnitLayout.currentLeftUnitGap < AuxUnitLayout.unitGap){ + for (var i = 0; i < units.length; i++) { //Shift all units + units[i].bbox.y -= (AuxUnitLayout.currentLeftUnitGap - AuxUnitLayout.unitGap) * (i+1); + } + AuxUnitLayout.currentLeftUnitGap = AuxUnitLayout.unitGap; + } + } + + if (right !== undefined) { + //Find total right length + usedLength = AuxUnitLayout.getUsedLengthLR(node, right); + units = right.units; + //If new unit adaed done reset unit gap + if(AuxUnitLayout.lastPos === "right"){ + AuxUnitLayout.currentRightUnitGap = AuxUnitLayout.unitGap; + AuxUnitLayout.lastPos = -1; + } + //Compare the side lengths + if (parentHeight < usedLength) { + //If there is not enough space + //Estimate new margin & gap + totalHeight = AuxUnitLayout.getUsedHeight(node, right); + estimatedGap = (parentHeight - totalHeight) / (units.length + 1); + //Else scale by using available space, reducing margins and gaps. + //Check if new gap is enough to fit + units[0].bbox.y -= AuxUnitLayout.currentRightUnitGap - estimatedGap; + + for (var i = 1; i < units.length; i++) { //Shift all units + units[i].bbox.y = units[i-1].bbox.y + units[i-1].bbox.h/2 + units[i].bbox.h/2 + estimatedGap; + } + AuxUnitLayout.currentRightUnitGap = estimatedGap; + + } + else if(AuxUnitLayout.currentRightUnitGap < AuxUnitLayout.unitGap){ + for (var i = 0; i < units.length; i++) { //Shift all units + units[i].bbox.y -= (AuxUnitLayout.currentRightUnitGap - AuxUnitLayout.unitGap) * (i+1); + } + AuxUnitLayout.currentRightUnitGap = AuxUnitLayout.unitGap; + } + } +}; + + +// Calculate total length used in a side // TODO find a way to refactor, remove ugliness of top-bottom/left-right. AuxUnitLayout.precomputeCoords = function (mainObj, cy, doForceUpdate) { AuxUnitLayout.setDisplayedUnits(mainObj, cy); - var lengthUsed = AuxUnitLayout.getOuterMargin(mainObj); var finalLengthUsed = lengthUsed; var unitGap = AuxUnitLayout.getUnitGap(mainObj); + var parentNode = AuxUnitLayout.getParentNode(mainObj, cy); + for(var i=0; i < mainObj.units.length; i++) { // change the coordinate system of the auxiliary unit according to the chosen layout var auxUnit = mainObj.units[i]; @@ -851,6 +1043,18 @@ AuxUnitLayout.unitCount = function(mainObj) { return mainObj.units.length; }; +AuxUnitLayout.unitLength = function(mainObj) { + var units = mainObj.units; + var rightMostPoint = 0; + for (var i = 0; i < units.length; i++) { + var box = units[i].bbox; + if (box.x + box.w / 2 > rightMostPoint){ + rightMostPoint = box.x + box.w / 2; + } + } + return rightMostPoint; +}; + /** * Auto choose the next layout. To add a new aux unit, for example. */ @@ -858,7 +1062,6 @@ AuxUnitLayout.selectNextAvailable = function(node) { var top = node.data('auxunitlayouts').top; var bottom = node.data('auxunitlayouts').bottom; var resultLocation = "top"; - // start by adding on top if free if(!top || AuxUnitLayout.isEmpty(top)) { resultLocation = "top"; @@ -868,13 +1071,14 @@ AuxUnitLayout.selectNextAvailable = function(node) { } else { // search for the side with the fewer units on it - if(AuxUnitLayout.unitCount(top) <= AuxUnitLayout.unitCount(bottom)) { + if(AuxUnitLayout.unitLength(top) <= AuxUnitLayout.unitLength(bottom)) { resultLocation = "top"; } else { resultLocation = "bottom"; } } + AuxUnitLayout.lastPos = resultLocation; //Set last used position return resultLocation; }; From 96e1b13c15a6fdc48e668392ee7b120680421955 Mon Sep 17 00:00:00 2001 From: kaansancak Date: Thu, 7 Jun 2018 11:25:06 +0300 Subject: [PATCH 14/29] Fix remove bug of fit units --- src/utilities/classes.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/utilities/classes.js b/src/utilities/classes.js index 5c245d82..d7e5e4f4 100644 --- a/src/utilities/classes.js +++ b/src/utilities/classes.js @@ -827,7 +827,10 @@ AuxUnitLayout.getUsedLengthLR = function(node, tb){ return AuxUnitLayout.getUsedHeight(node, tb) + (units.length + 1) * AuxUnitLayout.unitGap; //One gap for leftmost outer margin } -AuxUnitLayout.fitUnits = function (node) { +AuxUnitLayout.fitUnits = function (node, location) { + if (location !== undefined) { + AuxUnitLayout.lastPos = location; + } var top = node.data('auxunitlayouts').top; var bottom = node.data('auxunitlayouts').bottom; var left = node.data('auxunitlayouts').left; From 2e51ef8c23ec45a94f4ed569670d77957b5abbc6 Mon Sep 17 00:00:00 2001 From: kaansancak Date: Thu, 7 Jun 2018 14:39:29 +0300 Subject: [PATCH 15/29] Fix on load fit units bug --- src/utilities/classes.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/utilities/classes.js b/src/utilities/classes.js index d7e5e4f4..da48e579 100644 --- a/src/utilities/classes.js +++ b/src/utilities/classes.js @@ -828,6 +828,9 @@ AuxUnitLayout.getUsedLengthLR = function(node, tb){ } AuxUnitLayout.fitUnits = function (node, location) { + if (node.data('auxunitlayouts') === undefined) { + return; + } if (location !== undefined) { AuxUnitLayout.lastPos = location; } From 08f6c7dba76c2e63639e99a9ff45def5f59c9239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87and=C4=B1ro=C4=9Flu?= Date: Thu, 7 Jun 2018 18:22:44 +0300 Subject: [PATCH 16/29] Refactor clone marker implementation iVis-at-Bilkent/newt#267 --- .../sbgn-cy-instance-factory.js | 15 ---- src/utilities/element-utilities-factory.js | 83 +++++++++++++++++++ src/utilities/file-utilities-factory.js | 21 ----- 3 files changed, 83 insertions(+), 36 deletions(-) diff --git a/src/sbgn-extensions/sbgn-cy-instance-factory.js b/src/sbgn-extensions/sbgn-cy-instance-factory.js index 65b93ab2..4e6c388b 100644 --- a/src/sbgn-extensions/sbgn-cy-instance-factory.js +++ b/src/sbgn-extensions/sbgn-cy-instance-factory.js @@ -242,21 +242,6 @@ module.exports = function () { 'padding': 0, 'text-wrap': 'wrap' }) - .selector("node[?clonemarker][class='perturbing agent'],node[?clonemarker][class='unspecified entity']") - .css({ - 'background-image': "url('" + 'data:image/svg+xml;utf8,%3Csvg%20width%3D%22100%22%20height%3D%22100%22%20viewBox%3D%220%200%20100%20100%22%20style%3D%22fill%3Anone%3Bstroke%3Ablack%3Bstroke-width%3A0%3B%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%22100%22%20height%3D%22100%22%20style%3D%22fill%3A%23a9a9a9%22/%3E%20%3C/svg%3E' + "')", - 'background-position-x': '50%', - 'background-position-y': '100%', - 'background-width': '100%', - 'background-height': '25%', - 'background-fit': 'none', - 'background-image-opacity': function (ele) { - if (!ele.data('clonemarker')) { - return 0; - } - return ele.css('background-opacity'); - } - }) .selector("node[class]") .css({ 'shape': function (ele) { diff --git a/src/utilities/element-utilities-factory.js b/src/utilities/element-utilities-factory.js index 5b71ff3f..fe7a6b25 100644 --- a/src/utilities/element-utilities-factory.js +++ b/src/utilities/element-utilities-factory.js @@ -1330,6 +1330,89 @@ module.exports = function () { return margin; }; + + // Set clone marker status of given nodes to the given status. + elementUtilities.setCloneMarkerStatus = function (node, status) { + if (status) + node.data('clonemarker', true); + else + node.removeData('clonemarker'); + + if(node.data('class') !== "unspecified entity" && node.data('class') !== "perturbing agent") + return; + + var bgObj = { + 'background-image': 'data:image/svg+xml;utf8,%3Csvg%20width%3D%22100%22%20height%3D%22100%22%20viewBox%3D%220%200%20100%20100%22%20style%3D%22fill%3Anone%3Bstroke%3Ablack%3Bstroke-width%3A0%3B%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%22100%22%20height%3D%22100%22%20style%3D%22fill%3A%23a9a9a9%22/%3E%20%3C/svg%3E', + 'background-position-x': '50%', + 'background-position-y': '100%', + 'background-width': '100%', + 'background-height': '25%', + 'background-fit': 'none', + 'background-image-opacity': '0' + }; + + var style = node._private.style; + + var imgs = style['background-image'] ? style['background-image'].value : []; + var xPos = style['background-position-x'] ? style['background-position-x'].value : []; + var yPos = style['background-position-y'] ? style['background-position-y'].value : []; + var widths = style['background-width'] ? style['background-width'].value : []; + var heights = style['background-height'] ? style['background-height'].value : []; + var fits = style['background-fit'] ? style['background-fit'].value : []; + var opacities = style['background-image-opacity'] ? style['background-image-opacity'].value : []; + + concatUnitToValues(xPos, "%"); + concatUnitToValues(yPos, "%"); + concatUnitToValues(heights, "%"); + concatUnitToValues(widths, "%"); + + if(status){ + var index = imgs.indexOf(bgObj['background-image']); + // Already exists; Make opacity non-zero + if( index > -1) + opacities[index] = node.css('background-opacity'); + else{ + imgs.push(bgObj['background-image']); + xPos.push(bgObj['background-position-x']); + yPos.push(bgObj['background-position-y']); + widths.push(bgObj['background-width']); + heights.push(bgObj['background-height']); + fits.push(bgObj['background-fit']); + opacities.push(node.css('background-opacity')); + } + } + else{ + var index = imgs.indexOf(bgObj['background-image']); + // Already exists; Make opacity zero + if( index > -1) + opacities[index] = '0'; + } + + var newStyle = { + 'background-image': imgs, + 'background-position-x': xPos, + 'background-position-y': yPos, + 'background-width': widths, + 'background-height': heights, + 'background-fit': fits, + 'background-image-opacity': opacities + } + + node.style(newStyle); + + function concatUnitToValues(values, unit){ + if(!values || values.length == 0) + return; + + for(var i = 0; i < values.length; i++){ + if(values[i] && values[i] !== "" && values[i] !== "auto"){ + var tmp = '' + values[i]; + values[i] = tmp + unit; + } + } + } + }; + // Section End // Stylesheet helpers diff --git a/src/utilities/file-utilities-factory.js b/src/utilities/file-utilities-factory.js index edbf0663..3e43d023 100644 --- a/src/utilities/file-utilities-factory.js +++ b/src/utilities/file-utilities-factory.js @@ -268,26 +268,5 @@ fileUtilities.loadTDFile = function(file, callback1){ return sbgnmlToJson.convert(textToXmlObject(sbgnmlText)); }; - fileUtilities.loadBackgroundImage = function(node, file) { - - var reader = new FileReader(); - reader.readAsDataURL(file); - - reader.onload = function (e) { - var img = reader.result; - if(img){ - var opt = { - 'background-image' : img, - 'background-fit' : 'contain', - 'background-image-opacity' : '1' - }; - node.style(opt); - } - else{ - alert('Error: Image cannot be applied as background!'); - } - }; - }; - return fileUtilities; }; From 1806825376282b6972f46b0a0b0cf24428fc2f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87and=C4=B1ro=C4=9Flu?= Date: Fri, 8 Jun 2018 16:53:25 +0300 Subject: [PATCH 17/29] Fix background disappering when parent changes iVis-at-Bilkent/newt#271 --- src/utilities/element-utilities-factory.js | 1 + .../json-to-sbgnml-converter-factory.js | 19 ++++++++++--- .../sbgnml-to-json-converter-factory.js | 28 +++++++++++++++++-- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/utilities/element-utilities-factory.js b/src/utilities/element-utilities-factory.js index fe7a6b25..112c4ccc 100644 --- a/src/utilities/element-utilities-factory.js +++ b/src/utilities/element-utilities-factory.js @@ -1399,6 +1399,7 @@ module.exports = function () { } node.style(newStyle); + node.data('background-image', newStyle); function concatUnitToValues(values, unit){ if(!values || values.length == 0) diff --git a/src/utilities/json-to-sbgnml-converter-factory.js b/src/utilities/json-to-sbgnml-converter-factory.js index e79996aa..5e3b2504 100644 --- a/src/utilities/json-to-sbgnml-converter-factory.js +++ b/src/utilities/json-to-sbgnml-converter-factory.js @@ -333,14 +333,25 @@ module.exports = function () { var img = node._private.style['background-image']; var fit = node._private.style['background-fit']; var opacity = node._private.style['background-image-opacity']; + var x = node._private.style['background-position-x']; + var y = node._private.style['background-position-y']; + var width = node._private.style['background-width']; + var height = node._private.style['background-height']; sbgnvizExtString += " Date: Fri, 8 Jun 2018 18:40:37 +0300 Subject: [PATCH 18/29] Fix load and save with background images iVis-at-Bilkent/newt#266 --- .../json-to-sbgnml-converter-factory.js | 64 +++++++++++++++---- .../sbgnml-to-json-converter-factory.js | 28 ++++---- 2 files changed, 67 insertions(+), 25 deletions(-) diff --git a/src/utilities/json-to-sbgnml-converter-factory.js b/src/utilities/json-to-sbgnml-converter-factory.js index 5e3b2504..1382252b 100644 --- a/src/utilities/json-to-sbgnml-converter-factory.js +++ b/src/utilities/json-to-sbgnml-converter-factory.js @@ -329,7 +329,8 @@ module.exports = function () { } // add info for background image - if(node._private.style['background-image']){ + var bgObj = node._private.style['background-image']; + if(bgObj && bgObj.value && bgObj.value.length > 0){ var img = node._private.style['background-image']; var fit = node._private.style['background-fit']; var opacity = node._private.style['background-image-opacity']; @@ -339,18 +340,59 @@ module.exports = function () { var height = node._private.style['background-height']; sbgnvizExtString += " 0) sbgnvizExtString += " fit=\"" + fit.strValue + "\" "; - if(opacity) + if(opacity && opacity.value.length > 0) sbgnvizExtString += " opacity=\"" + opacity.strValue + "\" "; - if(x) - sbgnvizExtString += " x=\"" + x.strValue + "\" "; - if(y) - sbgnvizExtString += " y=\"" + y.strValue + "\" "; - if(width) - sbgnvizExtString += " width=\"" + width.strValue + "\" "; - if(heigth) - sbgnvizExtString += " height=\"" + height.strValue + "\" "; + if(x && x.value.length > 0){ + var units = x.units; + var tmp = ""; + for(var i = 0; i < units.length; i++){ + if(units[i] && units[i] !== "") + tmp += "" + x.value[i] + units[i] + " "; + else + tmp += "" + x.value[i] + " "; + + } + + sbgnvizExtString += " x=\"" + tmp + "\" "; + } + if(y && y.value.length > 0){ + var units = y.units; + var tmp = ""; + for(var i = 0; i < units.length; i++){ + if(units[i] && units[i] !== "") + tmp += "" + y.value[i] + units[i] + " "; + else + tmp += "" + y.value[i] + " "; + } + + sbgnvizExtString += " y=\"" + tmp + "\" "; + } + if(width && width.value.length > 0){ + var units = width.units; + var tmp = ""; + for(var i = 0; i < units.length; i++){ + if(units[i] && units[i] !== "") + tmp += "" + width.value[i] + units[i] + " "; + else + tmp += "" + width.value[i] + " "; + } + + sbgnvizExtString += " width=\"" + tmp + "\" "; + } + if(height && height.value.length > 0){ + var units = height.units; + var tmp = ""; + for(var i = 0; i < units.length; i++){ + if(units[i] && units[i] !== "") + tmp += "" + height.value[i] + units[i] + " "; + else + tmp += "" + height.value[i] + " "; + } + + sbgnvizExtString += " height=\"" + tmp + "\" "; + } sbgnvizExtString += "/>"; hasNewtExt = true; diff --git a/src/utilities/sbgnml-to-json-converter-factory.js b/src/utilities/sbgnml-to-json-converter-factory.js index ba4ac934..e30a0c06 100644 --- a/src/utilities/sbgnml-to-json-converter-factory.js +++ b/src/utilities/sbgnml-to-json-converter-factory.js @@ -368,13 +368,13 @@ module.exports = function () { if (result.sbgnviz.backgroundImage){ var bgImage = result.sbgnviz.backgroundImage[0].$; - var img = bgImage.img ? bgImage.img.split() : []; - var fit = bgImage.fit ? bgImage.fit.split() : []; - var opacity = bgImage.opacity ? bgImage.opacity.split() : []; - var x = bgImage.x ? bgImage.x.split() : []; - var y = bgImage.y ? bgImage.y.split() : []; - var width = bgImage.width ? bgImage.width.split() : []; - var height = bgImage.height ? bgImage.height.split() : []; + var img = bgImage.img ? bgImage.img : ""; + var fit = bgImage.fit ? bgImage.fit : ""; + var opacity = bgImage.opacity ? bgImage.opacity : ""; + var x = bgImage.x ? bgImage.x : ""; + var y = bgImage.y ? bgImage.y : ""; + var width = bgImage.width ? bgImage.width : ""; + var height = bgImage.height ? bgImage.height : ""; styleObj['background-image'] = img; styleObj['background-fit'] = fit; @@ -385,13 +385,13 @@ module.exports = function () { styleObj['background-height'] = height; nodeObj['background-image'] = { - 'background-image': img, - 'background-fit': fit, - 'background-image-opacity': opacity, - 'background-position-x': x, - 'background-position-y': y, - 'background-width': width, - 'background-height': height, + 'background-image': img.split(" "), + 'background-fit': fit.split(" "), + 'background-image-opacity': opacity.split(" "), + 'background-position-x': x.split(" "), + 'background-position-y': y.split(" "), + 'background-width': width.split(" "), + 'background-height': height.split(" "), }; } }); From cebd73cfe0ac72b95231c9f7bdf702afe4111b73 Mon Sep 17 00:00:00 2001 From: kaansancak Date: Tue, 12 Jun 2018 00:40:55 +0300 Subject: [PATCH 19/29] Add relocatable info-box support iVis-at-Bilkent/newt#265 --- src/utilities/classes.js | 128 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 3 deletions(-) diff --git a/src/utilities/classes.js b/src/utilities/classes.js index da48e579..ee8274ba 100644 --- a/src/utilities/classes.js +++ b/src/utilities/classes.js @@ -48,6 +48,7 @@ AuxiliaryUnit.construct = function(parent) { obj.coordType = "relativeToCenter"; obj.anchorSide = null; obj.isDisplayed = false; + obj.dashed = false; return obj; }; @@ -86,7 +87,7 @@ AuxiliaryUnit.copy = function (mainObj, cy, existingInstance, newParent, newId) newUnit.coordType = mainObj.coordType; newUnit.anchorSide = mainObj.anchorSide; newUnit.isDisplayed = mainObj.isDisplayed; - + newUnit.dashed = mainObj.dashed; return newUnit; }; @@ -94,7 +95,12 @@ AuxiliaryUnit.copy = function (mainObj, cy, existingInstance, newParent, newId) AuxiliaryUnit.draw = function(mainObj, cy, context) { var unitClass = getAuxUnitClass(mainObj); var coords = unitClass.getAbsoluteCoord(mainObj, cy); - + if (mainObj.dashed === true) { + context.setLineDash([2,2]); + } + else { + context.setLineDash([]); + } unitClass.drawShape(mainObj, cy, context, coords.x, coords.y); if (unitClass.hasText(mainObj, cy)) { unitClass.drawText(mainObj, cy, context, coords.x, coords.y); @@ -187,6 +193,57 @@ AuxiliaryUnit.getAbsoluteCoord = function(mainObj, cy) { } }; +AuxiliaryUnit.convertToAbsoluteCoord = function(mainObj, relX, relY, cy) { + var parent = getAuxUnitClass(mainObj).getParent(mainObj, cy); + if(mainObj.coordType == "relativeToCenter") { + var absX = relX * (parent.outerWidth() - parent._private.data['border-width']) / 100 + parent._private.position.x; + var absY = relY * (parent.outerHeight() - parent._private.data['border-width']) / 100 + parent._private.position.y; + return {x: absX, y: absY}; + } + else if(mainObj.coordType == "relativeToSide") { + if (mainObj.anchorSide == "top" || mainObj.anchorSide == "bottom") { + var absX = parent._private.position.x - (parent.outerWidth() - parent._private.data['border-width']) / 2 + relX; + var absY = relY * (parent.outerHeight() - parent._private.data['border-width']) / 100 + parent._private.position.y; + } + else { + var absY = parent._private.position.y - (parent.outerHeight() - parent._private.data['border-width']) / 2 + relY ; + var absX = relX * (parent.outerWidth() - parent._private.data['border-width']) / 100 + parent._private.position.x; + } + + // due to corner of barrel shaped compartment shift absX to right + if (parent.data("class") == "compartment"){ + absX += parent.outerWidth() * 0.1; + }; + return {x: absX, y: absY}; + } +}; + +AuxiliaryUnit.convertToRelativeCoord = function(mainObj, absX, absY, cy){ + var parent = getAuxUnitClass(mainObj).getParent(mainObj, cy); + // due to corner of barrel shaped compartment shift absX to right + + if(mainObj.coordType == "relativeToCenter") { + var relX = (absX - parent._private.position.x) * 100 / (parent.outerWidth() - parent._private.data['border-width']); + var relY = (absY - parent._private.position.y) * 100 / (parent.outerHeight() - parent._private.data['border-width']); + return {x: relX, y: relY}; + } + else if(mainObj.coordType == "relativeToSide") { + if (parent.data("class") == "compartment"){ + absX -= parent.outerWidth() * 0.1; + }; + if (mainObj.anchorSide == "top" || mainObj.anchorSide == "bottom") { + var relX = absX - parent._private.position.x + (parent.outerWidth() - parent._private.data['border-width']) / 2; + var relY = (absY - parent._private.position.y) * 100 / (parent.outerHeight() - parent._private.data['border-width']); + } + else { + var relY = absY - parent._private.position.y + (parent.outerHeight() - parent._private.data['border-width']) / 2; + var relX = (absX - parent._private.position.x) * 100 / (parent.outerWidth() - parent._private.data['border-width']); + } + + return {x: relX, y: relY}; + } +}; + AuxiliaryUnit.setAnchorSide = function(mainObj, parentBbox) { if(mainObj.coordType == "relativeToCenter") { var thisX = mainObj.bbox.x; @@ -251,6 +308,7 @@ AuxiliaryUnit.addToParent = function (mainObj, cy, parentNode, location, positio if(!parentNode.data('auxunitlayouts')[location]) { parentNode.data('auxunitlayouts')[location] = AuxUnitLayout.construct(parentNode, location); } + var layout = parentNode.data('auxunitlayouts')[location]; mainObj.anchorSide = location; switch(location) { @@ -841,7 +899,6 @@ AuxUnitLayout.fitUnits = function (node, location) { //Get Parent node and find parent width var parentWidth = node.data('bbox').w; var parentHeight = node.data('bbox').h; - var usedLength; var totalWidth; var estimatedGap; @@ -1041,6 +1098,54 @@ AuxUnitLayout.draw = function (mainObj, cy, context) { } }; +AuxUnitLayout.modifyUnits = function(parentNode, unit, oldLocation, cy){ + var location = unit.anchorSide; + var posX = unit.bbox.x; + var posY = unit.bbox.y; + if (!parentNode.data('auxunitlayouts')[oldLocation]) { + parentNode.data('auxunitlayouts')[oldLocation] = AuxUnitLayout.construct(parentNode, oldLocation); + } + var oldAuxUnit = parentNode.data('auxunitlayouts')[oldLocation]; + var deleteUnits = oldAuxUnit.units; + + //Delete from old location + var deleteIndex; + for (var i = 0; i < deleteUnits.length; i++) { + if(deleteUnits[i] === unit) { + deleteIndex = i; + break; + } + } + deleteUnits.splice(deleteIndex, 1); + AuxUnitLayout.updateLengthCache(oldAuxUnit, cy); + AuxUnitLayout.update(oldAuxUnit, cy, true); + AuxUnitLayout.fitUnits(parentNode, oldLocation); + //If new is not constructed contruct interval + if (!parentNode.data('auxunitlayouts')[location]) { + parentNode.data('auxunitlayouts')[location] = AuxUnitLayout.construct(parentNode, location); + } + var insertAuxUnit = insertUnits = parentNode.data('auxunitlayouts')[location]; + var insertUnits = insertAuxUnit.units; + + var index = 0; + //Insert into new unit array + if (location === "top" || location === "bottom") { + while ( insertUnits[index] !== undefined && posX > insertUnits[index].bbox.x) { + index++; + } + } + else { + while ( insertUnits[index] !== undefined && posY > insertUnits[index].bbox.y) { + index++; + } + } + insertUnits.splice(index, 0, unit); + + AuxUnitLayout.updateLengthCache(insertAuxUnit, cy); + AuxUnitLayout.update(insertAuxUnit, cy, true); + AuxUnitLayout.fitUnits(parentNode, location); +}; + AuxUnitLayout.isEmpty = function(mainObj) { return mainObj.units.length == 0; }; @@ -1061,6 +1166,23 @@ AuxUnitLayout.unitLength = function(mainObj) { return rightMostPoint; }; +//Get Unit Gaps +AuxUnitLayout.getCurrentTopGap = function(){ + return AuxUnitLayout.currentTopUnitGap; +} + +AuxUnitLayout.getCurrentBottomGap = function(){ + return AuxUnitLayout.currentBottomUnitGap; +} + +AuxUnitLayout.getCurrentLeftGap = function(){ + return AuxUnitLayout.currentLeftUnitGap; +} + +AuxUnitLayout.getCurrentRightGap = function(){ + return AuxUnitLayout.currentRightUnitGap; +} + /** * Auto choose the next layout. To add a new aux unit, for example. */ From 7c689882feb6dc12e36f7ed9baa7020929840866 Mon Sep 17 00:00:00 2001 From: kaansancak Date: Wed, 13 Jun 2018 01:13:15 +0300 Subject: [PATCH 20/29] Fixes iVis-at-Bilkent/newt#265 iVis-at-Bilkent/newt#290 iVis-at-Bilkent/newt#291 --- src/utilities/classes.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/utilities/classes.js b/src/utilities/classes.js index ee8274ba..328e5fdd 100644 --- a/src/utilities/classes.js +++ b/src/utilities/classes.js @@ -170,19 +170,27 @@ AuxiliaryUnit.drawText = function(mainObj, cy, context, centerX, centerY) { AuxiliaryUnit.getAbsoluteCoord = function(mainObj, cy) { var parent = getAuxUnitClass(mainObj).getParent(mainObj, cy); + var position = parent.position(); + if (mainObj === undefined || parent === undefined || position === undefined) { + return; + } + var borderWidth = parent.data()["border-width"]; + if ( borderWidth === undefined) { + return; + } if(mainObj.coordType == "relativeToCenter") { - var absX = mainObj.bbox.x * (parent.outerWidth() - parent._private.data['border-width']) / 100 + parent._private.position.x; - var absY = mainObj.bbox.y * (parent.outerHeight() - parent._private.data['border-width']) / 100 + parent._private.position.y; + var absX = mainObj.bbox.x * (parent.outerWidth() - borderWidth) / 100 + position.x; + var absY = mainObj.bbox.y * (parent.outerHeight() - borderWidth) / 100 + position.y; return {x: absX, y: absY}; } else if(mainObj.coordType == "relativeToSide") { if (mainObj.anchorSide == "top" || mainObj.anchorSide == "bottom") { - var absX = parent._private.position.x - (parent.outerWidth() - parent._private.data['border-width']) / 2 + mainObj.bbox.x; - var absY = mainObj.bbox.y * (parent.outerHeight() - parent._private.data['border-width']) / 100 + parent._private.position.y; + var absX = position.x - (parent.outerWidth() - borderWidth) / 2 + mainObj.bbox.x; + var absY = mainObj.bbox.y * (parent.outerHeight() - borderWidth) / 100 + position.y; } else { - var absY = parent._private.position.y - (parent.outerHeight() - parent._private.data['border-width']) / 2 + mainObj.bbox.y; - var absX = mainObj.bbox.x * (parent.outerWidth() - parent._private.data['border-width']) / 100 + parent._private.position.x; + var absY = position.y - (parent.outerHeight() - borderWidth) / 2 + mainObj.bbox.y; + var absX = mainObj.bbox.x * (parent.outerWidth() - borderWidth) / 100 + position.x; } // due to corner of barrel shaped compartment shift absX to right From c37b41b01e7673434dcc1f0a12c86bc92e2f9c29 Mon Sep 17 00:00:00 2001 From: kaansancak Date: Wed, 13 Jun 2018 15:48:08 +0300 Subject: [PATCH 21/29] iVis-at-Bilkent/newt#296 Fix Bug after save of overlapping information boxes --- .../sbgn-cy-instance-factory.js | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/sbgn-extensions/sbgn-cy-instance-factory.js b/src/sbgn-extensions/sbgn-cy-instance-factory.js index 4e6c388b..708e97ca 100644 --- a/src/sbgn-extensions/sbgn-cy-instance-factory.js +++ b/src/sbgn-extensions/sbgn-cy-instance-factory.js @@ -220,7 +220,32 @@ module.exports = function () { } // ensure that each layout has statesandinfos in correct order according to their initial positions for(var location in node.data('auxunitlayouts')) { - classes.AuxUnitLayout.reorderFromPositions(node.data('auxunitlayouts')[location], cy); + var unit = node.data('auxunitlayouts')[location]; + classes.AuxUnitLayout.reorderFromPositions(unit, cy); + var units = unit.units; + var coordsFirst = classes.AuxiliaryUnit.getAbsoluteCoord(units[0], cy); + var coordsLast = classes.AuxiliaryUnit.getAbsoluteCoord(units[units.length-1], cy); + var gap = classes.AuxUnitLayout.unitGap; + if (units.length > 0) { //For any case of removal + if (location === "top" || location === "bottom") { + var parentX1 = node.position().x - node.data("bbox").w/2; + var parentX2 = node.position().x + node.data("bbox").w/2; + var firstX1 = coordsFirst.x - units[0].bbox.w/2; + var lastX2 = coordsLast.x + units[units.length-1].bbox.w/2; + if (parentX1 + gap > firstX1 || parentX2 - gap < lastX2) { + classes.AuxUnitLayout.fitUnits(node, location); + } + } + else { + var parentY1 = node.position().y - node.data("bbox").w/2; + var parentY2 = node.position().y + node.data("bbox").w/2 ; + var firstY1 = coordsFirst.y - units[0].bbox.h/2; + var lastY2 = coordsLast.y + units[units.length-1].bbox.h/2; + if (parentY1 + gap > firstY1 || parentY2 - gap < lastY2) { + classes.AuxUnitLayout.fitUnits(node, location); + } + } + } } }); cy.endBatch(); From d1863efd8387cbb6ea44af584e95d9062cbb570f Mon Sep 17 00:00:00 2001 From: kaansancak Date: Fri, 15 Jun 2018 01:24:30 +0300 Subject: [PATCH 22/29] Change auto-adjustment of info-box positions iVis-at-Bilkent/newt#265 --- src/utilities/classes.js | 252 ++++++++++++++++++++++++++++++++++----- 1 file changed, 220 insertions(+), 32 deletions(-) diff --git a/src/utilities/classes.js b/src/utilities/classes.js index 328e5fdd..74ab6d51 100644 --- a/src/utilities/classes.js +++ b/src/utilities/classes.js @@ -201,21 +201,29 @@ AuxiliaryUnit.getAbsoluteCoord = function(mainObj, cy) { } }; -AuxiliaryUnit.convertToAbsoluteCoord = function(mainObj, relX, relY, cy) { - var parent = getAuxUnitClass(mainObj).getParent(mainObj, cy); +AuxiliaryUnit.convertToAbsoluteCoord = function(mainObj, relX, relY, cy, parentNode) { + if (parentNode !== undefined) { + var parent = parentNode; + } + else { + var parent = getAuxUnitClass(mainObj).getParent(mainObj, cy); + } + + var position = parent.position(); + var borderWidth = parent.data()['border-width']; if(mainObj.coordType == "relativeToCenter") { - var absX = relX * (parent.outerWidth() - parent._private.data['border-width']) / 100 + parent._private.position.x; - var absY = relY * (parent.outerHeight() - parent._private.data['border-width']) / 100 + parent._private.position.y; + var absX = relX * (parent.outerWidth() - borderWidth) / 100 + position.x; + var absY = relY * (parent.outerHeight() - borderWidth) / 100 + position.y; return {x: absX, y: absY}; } else if(mainObj.coordType == "relativeToSide") { if (mainObj.anchorSide == "top" || mainObj.anchorSide == "bottom") { - var absX = parent._private.position.x - (parent.outerWidth() - parent._private.data['border-width']) / 2 + relX; - var absY = relY * (parent.outerHeight() - parent._private.data['border-width']) / 100 + parent._private.position.y; + var absX = position.x - (parent.outerWidth() - borderWidth) / 2 + relX; + var absY = relY * (parent.outerHeight() - borderWidth) / 100 + position.y; } else { - var absY = parent._private.position.y - (parent.outerHeight() - parent._private.data['border-width']) / 2 + relY ; - var absX = relX * (parent.outerWidth() - parent._private.data['border-width']) / 100 + parent._private.position.x; + var absY = position.y - (parent.outerHeight() - borderWidth) / 2 + relY ; + var absX = relX * (parent.outerWidth() - borderWidth) / 100 + position.x; } // due to corner of barrel shaped compartment shift absX to right @@ -226,13 +234,18 @@ AuxiliaryUnit.convertToAbsoluteCoord = function(mainObj, relX, relY, cy) { } }; -AuxiliaryUnit.convertToRelativeCoord = function(mainObj, absX, absY, cy){ - var parent = getAuxUnitClass(mainObj).getParent(mainObj, cy); - // due to corner of barrel shaped compartment shift absX to right - +AuxiliaryUnit.convertToRelativeCoord = function(mainObj, absX, absY, cy, parentNode){ + if (parentNode !== undefined) { + var parent = parentNode; + } + else { + var parent = getAuxUnitClass(mainObj).getParent(mainObj, cy); + } + var position = parent.position(); + var borderWidth = parent.data()['border-width']; if(mainObj.coordType == "relativeToCenter") { - var relX = (absX - parent._private.position.x) * 100 / (parent.outerWidth() - parent._private.data['border-width']); - var relY = (absY - parent._private.position.y) * 100 / (parent.outerHeight() - parent._private.data['border-width']); + var relX = (absX - position.x) * 100 / (parent.outerWidth() - borderWidth); + var relY = (absY - position.y) * 100 / (parent.outerHeight() - borderWidth); return {x: relX, y: relY}; } else if(mainObj.coordType == "relativeToSide") { @@ -240,12 +253,12 @@ AuxiliaryUnit.convertToRelativeCoord = function(mainObj, absX, absY, cy){ absX -= parent.outerWidth() * 0.1; }; if (mainObj.anchorSide == "top" || mainObj.anchorSide == "bottom") { - var relX = absX - parent._private.position.x + (parent.outerWidth() - parent._private.data['border-width']) / 2; - var relY = (absY - parent._private.position.y) * 100 / (parent.outerHeight() - parent._private.data['border-width']); + var relX = absX - position.x + (parent.outerWidth() - borderWidth) / 2; + var relY = (absY - position.y) * 100 / (parent.outerHeight() - borderWidth); } else { - var relY = absY - parent._private.position.y + (parent.outerHeight() - parent._private.data['border-width']) / 2; - var relX = (absX - parent._private.position.x) * 100 / (parent.outerWidth() - parent._private.data['border-width']); + var relY = absY - position.y + (parent.outerHeight() - borderWidth) / 2; + var relX = (absX - position.x) * 100 / (parent.outerWidth() - borderWidth); } return {x: relX, y: relY}; @@ -746,7 +759,8 @@ AuxUnitLayout.removeAuxUnit = function(mainObj, cy, unit) { var index = mainObj.units.indexOf(unit); mainObj.units.splice(index, 1); AuxUnitLayout.updateLengthCache(mainObj, cy); - AuxUnitLayout.update(mainObj, cy, true);/* + //AuxUnitLayout.update(mainObj, cy, true); + /* if (AuxUnitLayout.getAlwaysShowAuxUnits(mainObj)) { // set a minimum size according to both sides on the same orientation AuxUnitLayout.setParentMinLength(mainObj, cy); @@ -872,7 +886,7 @@ AuxUnitLayout.getUsedWidth = function(node, tb){ totalWidth += units[i].bbox.w; } return totalWidth; -} +}; AuxUnitLayout.getUsedHeight = function(node, tb){ var units = tb.units; @@ -881,19 +895,190 @@ AuxUnitLayout.getUsedHeight = function(node, tb){ totalHeight += units[i].bbox.h; } return totalHeight; -} +}; AuxUnitLayout.getUsedLengthTB = function(node, tb){ var units = tb.units; return AuxUnitLayout.getUsedWidth(node, tb) + (units.length + 1) * AuxUnitLayout.unitGap; //One gap for leftmost outer margin -} +}; AuxUnitLayout.getUsedLengthLR = function(node, tb){ var units = tb.units; return AuxUnitLayout.getUsedHeight(node, tb) + (units.length + 1) * AuxUnitLayout.unitGap; //One gap for leftmost outer margin -} +}; + +AuxUnitLayout.checkOverlap = function(auxUnit, unit){ + var units = auxUnit.units; + for (var i = 0; i < units.length; i++) { + if(units[i] === unit){ + var index = i; + break; + } + } + if (unit.anchorSide === "top" || unit.anchorSide === "bottom") { + var unitX1 = unit.bbox.x - unit.bbox.w/2; + var unitX2 = unit.bbox.x + unit.bbox.w/2; + for (var i = 0; i < units.length; i++) { + if(units[i] === unit) { + continue; + } + var x1 = units[i].bbox.x - units[i].bbox.w/2; + var x2 = units[i].bbox.x + units[i].bbox.w/2; + if (i < index && (x2 > unitX1)) { + return true; + } + else if(i > index && (x1 < unitX2)){ + return true; + } + } + } + else { + var unitY1 = unit.bbox.y - unit.bbox.h/2; + var unitY2 = unit.bbox.y + unit.bbox.h/2; + for (var i = 0; i < units.length; i++) { + if(units[i] === unit) { + continue; + } + var y1 = units[i].bbox.y - units[i].bbox.h/2; + var y2 = units[i].bbox.y + units[i].bbox.h/2; + if (i < index && (y2 > unitY1)) { + return true; + } + else if(i > index && (y1 < unitY2)){ + return true; + } + } + } + return false; +}; + +AuxUnitLayout.checkFit = function (node){ + var fitLocations = []; + for(var location in node.data('auxunitlayouts')) { + var unit = node.data('auxunitlayouts')[location]; + var units = unit.units; + if (units.length === 0) { + continue; + } + var firstUnit = units[0]; + var lastUnit = units[units.length-1]; + var coordsFirst = AuxiliaryUnit.convertToAbsoluteCoord(firstUnit, firstUnit.bbox.x, firstUnit.bbox.y, undefined, node); + var coordsLast = AuxiliaryUnit.convertToAbsoluteCoord(lastUnit, lastUnit.bbox.x, lastUnit.bbox.y, undefined, node); + var gap = AuxUnitLayout.unitGap; + if (units.length > 0) { //For any case of removal + if (location === "top" || location === "bottom") { + var parentX1 = node.position().x - node.data("bbox").w/2; + var parentX2 = node.position().x + node.data("bbox").w/2; + var firstX1 = coordsFirst.x - firstUnit.bbox.w/2; + var lastX2 = coordsLast.x + lastUnit.bbox.w/2; + if (parentX1 + gap > firstX1 || parentX2 - gap < lastX2) { + fitLocations.push(location); + } + } + else { + var parentY1 = node.position().y - node.data("bbox").h/2; + var parentY2 = node.position().y + node.data("bbox").h/2 ; + var firstY1 = coordsFirst.y - firstUnit.bbox.h/2; + var lastY2 = coordsLast.y + lastUnit.bbox.h/2; + if (parentY1 + gap > firstY1 || parentY2 - gap < lastY2) { + fitLocations.push(location); + } + } + } + } + return fitLocations; +}; -AuxUnitLayout.fitUnits = function (node, location) { +AuxUnitLayout.getCurrentGap = function (location){ + if (location === "top") { + return AuxUnitLayout.currentTopUnitGap; + } + else if (location === "bottom") { + return AuxUnitLayout.currentBottomUnitGap; + } + else if (location === "right") { + return AuxUnitLayout.currentRightUnitGap; + } + else { + return AuxUnitLayout.currentLeftUnitGap; + } +}; + +AuxUnitLayout.setCurrentGap = function (location, value){ + if (location === "top") { + AuxUnitLayout.currentTopUnitGap = value; + } + else if (location === "bottom") { + AuxUnitLayout.currentBottomUnitGap = value; + } + else if (location === "right") { + AuxUnitLayout.currentRightUnitGap = value;; + } + else { + AuxUnitLayout.currentLeftUnitGap = value;; + } +}; + + + +AuxUnitLayout.fitUnits = function (node, fitLocation, forceFit) { + var fitLocations = AuxUnitLayout.checkFit(node); //Check which nodes are not fitting + if (forceFit !== undefined) { + fitLocations.push(forceFit) + } + for (var index = 0; index < fitLocations.length; index++) { + var location = fitLocations[index]; + var auxUnit = node.data('auxunitlayouts')[location]; + var units = auxUnit.units; + var unitGap = AuxUnitLayout.unitGap; + var parentWidth = node.data("bbox").w; + var parentHeight = node.data("bbox").h; + + if ( location === "top" || location === "bottom") { + var totalWidth = AuxUnitLayout.getUsedWidth(node, auxUnit); + var estimatedLength = AuxUnitLayout.getUsedLengthTB(node, auxUnit); + if (AuxUnitLayout.getCurrentGap(location) < unitGap && estimatedLength <= parentWidth) { + var estimatedGap = unitGap; //If default width is enough to fit nodes + } + else { + var estimatedGap = (parentWidth - totalWidth) / (units.length + 1); //Estimate new gap + if (estimatedGap > unitGap) { + estimatedGap = unitGap; + } + } + var firstPosition = AuxiliaryUnit.convertToRelativeCoord(units[0], node.position().x - parentWidth/2, node.position().y - parentHeight/2, undefined, node);//Position of the first unit + units[0].bbox.x = firstPosition.x + estimatedGap + units[0].bbox.w/2; + for (var i = 1; i < units.length; i++) { //Calculate new margins + units[i].bbox.x = units[i-1].bbox.x + units[i-1].bbox.w/2 + estimatedGap + units[i].bbox.w/2; + } + AuxUnitLayout.setCurrentGap(location, estimatedGap); + } + else { + + var totalHeight = AuxUnitLayout.getUsedHeight(node, auxUnit); + var estimatedLength = AuxUnitLayout.getUsedLengthLR(node, auxUnit); + if(AuxUnitLayout.getCurrentGap(location) <= unitGap && estimatedLength <= parentHeight) { + var estimatedGap = unitGap; + } + else { + var estimatedGap = (parentHeight - totalHeight) / (units.length + 1); + if (estimatedGap > unitGap) { + estimatedGap = unitGap; + } + } + var firstPosition = AuxiliaryUnit.convertToRelativeCoord(units[0], node.position().x - parentWidth/2, node.position().y - parentHeight/2, undefined, node);//Position of the first unit + units[0].bbox.y = firstPosition.y + estimatedGap + units[0].bbox.h/2; + for (var i = 1; i < units.length; i++) { //Calculate new margins + units[i].bbox.y = units[i-1].bbox.y + units[i-1].bbox.h/2 + estimatedGap + units[i].bbox.h/2; + } + AuxUnitLayout.setCurrentGap(location, estimatedGap); + } + } +}; + + + +AuxUnitLayout.setParentMinDimensions = function (node, location) { if (node.data('auxunitlayouts') === undefined) { return; } @@ -1126,8 +1311,7 @@ AuxUnitLayout.modifyUnits = function(parentNode, unit, oldLocation, cy){ } deleteUnits.splice(deleteIndex, 1); AuxUnitLayout.updateLengthCache(oldAuxUnit, cy); - AuxUnitLayout.update(oldAuxUnit, cy, true); - AuxUnitLayout.fitUnits(parentNode, oldLocation); + AuxUnitLayout.fitUnits(parentNode); //If new is not constructed contruct interval if (!parentNode.data('auxunitlayouts')[location]) { parentNode.data('auxunitlayouts')[location] = AuxUnitLayout.construct(parentNode, location); @@ -1150,8 +1334,12 @@ AuxUnitLayout.modifyUnits = function(parentNode, unit, oldLocation, cy){ insertUnits.splice(index, 0, unit); AuxUnitLayout.updateLengthCache(insertAuxUnit, cy); - AuxUnitLayout.update(insertAuxUnit, cy, true); - AuxUnitLayout.fitUnits(parentNode, location); + if (AuxUnitLayout.checkOverlap(insertAuxUnit, unit) === true) { + AuxUnitLayout.fitUnits(parentNode, undefined, unit.anchorSide); //If overlaps force fit the units + } + else { + AuxUnitLayout.fitUnits(parentNode); + } }; AuxUnitLayout.isEmpty = function(mainObj) { @@ -1177,19 +1365,19 @@ AuxUnitLayout.unitLength = function(mainObj) { //Get Unit Gaps AuxUnitLayout.getCurrentTopGap = function(){ return AuxUnitLayout.currentTopUnitGap; -} +}; AuxUnitLayout.getCurrentBottomGap = function(){ return AuxUnitLayout.currentBottomUnitGap; -} +}; AuxUnitLayout.getCurrentLeftGap = function(){ return AuxUnitLayout.currentLeftUnitGap; -} +}; AuxUnitLayout.getCurrentRightGap = function(){ return AuxUnitLayout.currentRightUnitGap; -} +}; /** * Auto choose the next layout. To add a new aux unit, for example. From cfbd7901901a5c9e02d0abae552a24896ee94d9e Mon Sep 17 00:00:00 2001 From: kaansancak Date: Fri, 15 Jun 2018 22:00:58 +0300 Subject: [PATCH 23/29] Revert "Change auto-adjustment of info-box positions iVis-at-Bilkent/newt#265" This reverts commit d1863efd8387cbb6ea44af584e95d9062cbb570f. --- src/utilities/classes.js | 252 +++++---------------------------------- 1 file changed, 32 insertions(+), 220 deletions(-) diff --git a/src/utilities/classes.js b/src/utilities/classes.js index 74ab6d51..328e5fdd 100644 --- a/src/utilities/classes.js +++ b/src/utilities/classes.js @@ -201,29 +201,21 @@ AuxiliaryUnit.getAbsoluteCoord = function(mainObj, cy) { } }; -AuxiliaryUnit.convertToAbsoluteCoord = function(mainObj, relX, relY, cy, parentNode) { - if (parentNode !== undefined) { - var parent = parentNode; - } - else { - var parent = getAuxUnitClass(mainObj).getParent(mainObj, cy); - } - - var position = parent.position(); - var borderWidth = parent.data()['border-width']; +AuxiliaryUnit.convertToAbsoluteCoord = function(mainObj, relX, relY, cy) { + var parent = getAuxUnitClass(mainObj).getParent(mainObj, cy); if(mainObj.coordType == "relativeToCenter") { - var absX = relX * (parent.outerWidth() - borderWidth) / 100 + position.x; - var absY = relY * (parent.outerHeight() - borderWidth) / 100 + position.y; + var absX = relX * (parent.outerWidth() - parent._private.data['border-width']) / 100 + parent._private.position.x; + var absY = relY * (parent.outerHeight() - parent._private.data['border-width']) / 100 + parent._private.position.y; return {x: absX, y: absY}; } else if(mainObj.coordType == "relativeToSide") { if (mainObj.anchorSide == "top" || mainObj.anchorSide == "bottom") { - var absX = position.x - (parent.outerWidth() - borderWidth) / 2 + relX; - var absY = relY * (parent.outerHeight() - borderWidth) / 100 + position.y; + var absX = parent._private.position.x - (parent.outerWidth() - parent._private.data['border-width']) / 2 + relX; + var absY = relY * (parent.outerHeight() - parent._private.data['border-width']) / 100 + parent._private.position.y; } else { - var absY = position.y - (parent.outerHeight() - borderWidth) / 2 + relY ; - var absX = relX * (parent.outerWidth() - borderWidth) / 100 + position.x; + var absY = parent._private.position.y - (parent.outerHeight() - parent._private.data['border-width']) / 2 + relY ; + var absX = relX * (parent.outerWidth() - parent._private.data['border-width']) / 100 + parent._private.position.x; } // due to corner of barrel shaped compartment shift absX to right @@ -234,18 +226,13 @@ AuxiliaryUnit.convertToAbsoluteCoord = function(mainObj, relX, relY, cy, parentN } }; -AuxiliaryUnit.convertToRelativeCoord = function(mainObj, absX, absY, cy, parentNode){ - if (parentNode !== undefined) { - var parent = parentNode; - } - else { - var parent = getAuxUnitClass(mainObj).getParent(mainObj, cy); - } - var position = parent.position(); - var borderWidth = parent.data()['border-width']; +AuxiliaryUnit.convertToRelativeCoord = function(mainObj, absX, absY, cy){ + var parent = getAuxUnitClass(mainObj).getParent(mainObj, cy); + // due to corner of barrel shaped compartment shift absX to right + if(mainObj.coordType == "relativeToCenter") { - var relX = (absX - position.x) * 100 / (parent.outerWidth() - borderWidth); - var relY = (absY - position.y) * 100 / (parent.outerHeight() - borderWidth); + var relX = (absX - parent._private.position.x) * 100 / (parent.outerWidth() - parent._private.data['border-width']); + var relY = (absY - parent._private.position.y) * 100 / (parent.outerHeight() - parent._private.data['border-width']); return {x: relX, y: relY}; } else if(mainObj.coordType == "relativeToSide") { @@ -253,12 +240,12 @@ AuxiliaryUnit.convertToRelativeCoord = function(mainObj, absX, absY, cy, parentN absX -= parent.outerWidth() * 0.1; }; if (mainObj.anchorSide == "top" || mainObj.anchorSide == "bottom") { - var relX = absX - position.x + (parent.outerWidth() - borderWidth) / 2; - var relY = (absY - position.y) * 100 / (parent.outerHeight() - borderWidth); + var relX = absX - parent._private.position.x + (parent.outerWidth() - parent._private.data['border-width']) / 2; + var relY = (absY - parent._private.position.y) * 100 / (parent.outerHeight() - parent._private.data['border-width']); } else { - var relY = absY - position.y + (parent.outerHeight() - borderWidth) / 2; - var relX = (absX - position.x) * 100 / (parent.outerWidth() - borderWidth); + var relY = absY - parent._private.position.y + (parent.outerHeight() - parent._private.data['border-width']) / 2; + var relX = (absX - parent._private.position.x) * 100 / (parent.outerWidth() - parent._private.data['border-width']); } return {x: relX, y: relY}; @@ -759,8 +746,7 @@ AuxUnitLayout.removeAuxUnit = function(mainObj, cy, unit) { var index = mainObj.units.indexOf(unit); mainObj.units.splice(index, 1); AuxUnitLayout.updateLengthCache(mainObj, cy); - //AuxUnitLayout.update(mainObj, cy, true); - /* + AuxUnitLayout.update(mainObj, cy, true);/* if (AuxUnitLayout.getAlwaysShowAuxUnits(mainObj)) { // set a minimum size according to both sides on the same orientation AuxUnitLayout.setParentMinLength(mainObj, cy); @@ -886,7 +872,7 @@ AuxUnitLayout.getUsedWidth = function(node, tb){ totalWidth += units[i].bbox.w; } return totalWidth; -}; +} AuxUnitLayout.getUsedHeight = function(node, tb){ var units = tb.units; @@ -895,190 +881,19 @@ AuxUnitLayout.getUsedHeight = function(node, tb){ totalHeight += units[i].bbox.h; } return totalHeight; -}; +} AuxUnitLayout.getUsedLengthTB = function(node, tb){ var units = tb.units; return AuxUnitLayout.getUsedWidth(node, tb) + (units.length + 1) * AuxUnitLayout.unitGap; //One gap for leftmost outer margin -}; +} AuxUnitLayout.getUsedLengthLR = function(node, tb){ var units = tb.units; return AuxUnitLayout.getUsedHeight(node, tb) + (units.length + 1) * AuxUnitLayout.unitGap; //One gap for leftmost outer margin -}; - -AuxUnitLayout.checkOverlap = function(auxUnit, unit){ - var units = auxUnit.units; - for (var i = 0; i < units.length; i++) { - if(units[i] === unit){ - var index = i; - break; - } - } - if (unit.anchorSide === "top" || unit.anchorSide === "bottom") { - var unitX1 = unit.bbox.x - unit.bbox.w/2; - var unitX2 = unit.bbox.x + unit.bbox.w/2; - for (var i = 0; i < units.length; i++) { - if(units[i] === unit) { - continue; - } - var x1 = units[i].bbox.x - units[i].bbox.w/2; - var x2 = units[i].bbox.x + units[i].bbox.w/2; - if (i < index && (x2 > unitX1)) { - return true; - } - else if(i > index && (x1 < unitX2)){ - return true; - } - } - } - else { - var unitY1 = unit.bbox.y - unit.bbox.h/2; - var unitY2 = unit.bbox.y + unit.bbox.h/2; - for (var i = 0; i < units.length; i++) { - if(units[i] === unit) { - continue; - } - var y1 = units[i].bbox.y - units[i].bbox.h/2; - var y2 = units[i].bbox.y + units[i].bbox.h/2; - if (i < index && (y2 > unitY1)) { - return true; - } - else if(i > index && (y1 < unitY2)){ - return true; - } - } - } - return false; -}; - -AuxUnitLayout.checkFit = function (node){ - var fitLocations = []; - for(var location in node.data('auxunitlayouts')) { - var unit = node.data('auxunitlayouts')[location]; - var units = unit.units; - if (units.length === 0) { - continue; - } - var firstUnit = units[0]; - var lastUnit = units[units.length-1]; - var coordsFirst = AuxiliaryUnit.convertToAbsoluteCoord(firstUnit, firstUnit.bbox.x, firstUnit.bbox.y, undefined, node); - var coordsLast = AuxiliaryUnit.convertToAbsoluteCoord(lastUnit, lastUnit.bbox.x, lastUnit.bbox.y, undefined, node); - var gap = AuxUnitLayout.unitGap; - if (units.length > 0) { //For any case of removal - if (location === "top" || location === "bottom") { - var parentX1 = node.position().x - node.data("bbox").w/2; - var parentX2 = node.position().x + node.data("bbox").w/2; - var firstX1 = coordsFirst.x - firstUnit.bbox.w/2; - var lastX2 = coordsLast.x + lastUnit.bbox.w/2; - if (parentX1 + gap > firstX1 || parentX2 - gap < lastX2) { - fitLocations.push(location); - } - } - else { - var parentY1 = node.position().y - node.data("bbox").h/2; - var parentY2 = node.position().y + node.data("bbox").h/2 ; - var firstY1 = coordsFirst.y - firstUnit.bbox.h/2; - var lastY2 = coordsLast.y + lastUnit.bbox.h/2; - if (parentY1 + gap > firstY1 || parentY2 - gap < lastY2) { - fitLocations.push(location); - } - } - } - } - return fitLocations; -}; - -AuxUnitLayout.getCurrentGap = function (location){ - if (location === "top") { - return AuxUnitLayout.currentTopUnitGap; - } - else if (location === "bottom") { - return AuxUnitLayout.currentBottomUnitGap; - } - else if (location === "right") { - return AuxUnitLayout.currentRightUnitGap; - } - else { - return AuxUnitLayout.currentLeftUnitGap; - } -}; - -AuxUnitLayout.setCurrentGap = function (location, value){ - if (location === "top") { - AuxUnitLayout.currentTopUnitGap = value; - } - else if (location === "bottom") { - AuxUnitLayout.currentBottomUnitGap = value; - } - else if (location === "right") { - AuxUnitLayout.currentRightUnitGap = value;; - } - else { - AuxUnitLayout.currentLeftUnitGap = value;; - } -}; - - - -AuxUnitLayout.fitUnits = function (node, fitLocation, forceFit) { - var fitLocations = AuxUnitLayout.checkFit(node); //Check which nodes are not fitting - if (forceFit !== undefined) { - fitLocations.push(forceFit) - } - for (var index = 0; index < fitLocations.length; index++) { - var location = fitLocations[index]; - var auxUnit = node.data('auxunitlayouts')[location]; - var units = auxUnit.units; - var unitGap = AuxUnitLayout.unitGap; - var parentWidth = node.data("bbox").w; - var parentHeight = node.data("bbox").h; - - if ( location === "top" || location === "bottom") { - var totalWidth = AuxUnitLayout.getUsedWidth(node, auxUnit); - var estimatedLength = AuxUnitLayout.getUsedLengthTB(node, auxUnit); - if (AuxUnitLayout.getCurrentGap(location) < unitGap && estimatedLength <= parentWidth) { - var estimatedGap = unitGap; //If default width is enough to fit nodes - } - else { - var estimatedGap = (parentWidth - totalWidth) / (units.length + 1); //Estimate new gap - if (estimatedGap > unitGap) { - estimatedGap = unitGap; - } - } - var firstPosition = AuxiliaryUnit.convertToRelativeCoord(units[0], node.position().x - parentWidth/2, node.position().y - parentHeight/2, undefined, node);//Position of the first unit - units[0].bbox.x = firstPosition.x + estimatedGap + units[0].bbox.w/2; - for (var i = 1; i < units.length; i++) { //Calculate new margins - units[i].bbox.x = units[i-1].bbox.x + units[i-1].bbox.w/2 + estimatedGap + units[i].bbox.w/2; - } - AuxUnitLayout.setCurrentGap(location, estimatedGap); - } - else { - - var totalHeight = AuxUnitLayout.getUsedHeight(node, auxUnit); - var estimatedLength = AuxUnitLayout.getUsedLengthLR(node, auxUnit); - if(AuxUnitLayout.getCurrentGap(location) <= unitGap && estimatedLength <= parentHeight) { - var estimatedGap = unitGap; - } - else { - var estimatedGap = (parentHeight - totalHeight) / (units.length + 1); - if (estimatedGap > unitGap) { - estimatedGap = unitGap; - } - } - var firstPosition = AuxiliaryUnit.convertToRelativeCoord(units[0], node.position().x - parentWidth/2, node.position().y - parentHeight/2, undefined, node);//Position of the first unit - units[0].bbox.y = firstPosition.y + estimatedGap + units[0].bbox.h/2; - for (var i = 1; i < units.length; i++) { //Calculate new margins - units[i].bbox.y = units[i-1].bbox.y + units[i-1].bbox.h/2 + estimatedGap + units[i].bbox.h/2; - } - AuxUnitLayout.setCurrentGap(location, estimatedGap); - } - } -}; - - +} -AuxUnitLayout.setParentMinDimensions = function (node, location) { +AuxUnitLayout.fitUnits = function (node, location) { if (node.data('auxunitlayouts') === undefined) { return; } @@ -1311,7 +1126,8 @@ AuxUnitLayout.modifyUnits = function(parentNode, unit, oldLocation, cy){ } deleteUnits.splice(deleteIndex, 1); AuxUnitLayout.updateLengthCache(oldAuxUnit, cy); - AuxUnitLayout.fitUnits(parentNode); + AuxUnitLayout.update(oldAuxUnit, cy, true); + AuxUnitLayout.fitUnits(parentNode, oldLocation); //If new is not constructed contruct interval if (!parentNode.data('auxunitlayouts')[location]) { parentNode.data('auxunitlayouts')[location] = AuxUnitLayout.construct(parentNode, location); @@ -1334,12 +1150,8 @@ AuxUnitLayout.modifyUnits = function(parentNode, unit, oldLocation, cy){ insertUnits.splice(index, 0, unit); AuxUnitLayout.updateLengthCache(insertAuxUnit, cy); - if (AuxUnitLayout.checkOverlap(insertAuxUnit, unit) === true) { - AuxUnitLayout.fitUnits(parentNode, undefined, unit.anchorSide); //If overlaps force fit the units - } - else { - AuxUnitLayout.fitUnits(parentNode); - } + AuxUnitLayout.update(insertAuxUnit, cy, true); + AuxUnitLayout.fitUnits(parentNode, location); }; AuxUnitLayout.isEmpty = function(mainObj) { @@ -1365,19 +1177,19 @@ AuxUnitLayout.unitLength = function(mainObj) { //Get Unit Gaps AuxUnitLayout.getCurrentTopGap = function(){ return AuxUnitLayout.currentTopUnitGap; -}; +} AuxUnitLayout.getCurrentBottomGap = function(){ return AuxUnitLayout.currentBottomUnitGap; -}; +} AuxUnitLayout.getCurrentLeftGap = function(){ return AuxUnitLayout.currentLeftUnitGap; -}; +} AuxUnitLayout.getCurrentRightGap = function(){ return AuxUnitLayout.currentRightUnitGap; -}; +} /** * Auto choose the next layout. To add a new aux unit, for example. From dad44f5af6f1dc54548b881f72b05d8b1e8d657b Mon Sep 17 00:00:00 2001 From: kaansancak Date: Mon, 18 Jun 2018 17:44:24 +0300 Subject: [PATCH 24/29] Change fit function iVis-at-Bilkent/newt#313 iVis-at-Bilkent/newt#317 --- src/utilities/classes.js | 224 ++++++++++++++------------------------- 1 file changed, 80 insertions(+), 144 deletions(-) diff --git a/src/utilities/classes.js b/src/utilities/classes.js index 328e5fdd..4e1459d6 100644 --- a/src/utilities/classes.js +++ b/src/utilities/classes.js @@ -226,13 +226,21 @@ AuxiliaryUnit.convertToAbsoluteCoord = function(mainObj, relX, relY, cy) { } }; -AuxiliaryUnit.convertToRelativeCoord = function(mainObj, absX, absY, cy){ - var parent = getAuxUnitClass(mainObj).getParent(mainObj, cy); - // due to corner of barrel shaped compartment shift absX to right - +AuxiliaryUnit.convertToRelativeCoord = function(mainObj, absX, absY, cy, parentNode){ + if (mainObj === undefined) { + return; + } + if (parentNode !== undefined) { + var parent = parentNode; + } + else { + var parent = getAuxUnitClass(mainObj).getParent(mainObj, cy); + } + var position = parent.position(); + var borderWidth = parent.data()['border-width']; if(mainObj.coordType == "relativeToCenter") { - var relX = (absX - parent._private.position.x) * 100 / (parent.outerWidth() - parent._private.data['border-width']); - var relY = (absY - parent._private.position.y) * 100 / (parent.outerHeight() - parent._private.data['border-width']); + var relX = (absX - position.x) * 100 / (parent.outerWidth() - borderWidth); + var relY = (absY - position.y) * 100 / (parent.outerHeight() - borderWidth); return {x: relX, y: relY}; } else if(mainObj.coordType == "relativeToSide") { @@ -240,12 +248,12 @@ AuxiliaryUnit.convertToRelativeCoord = function(mainObj, absX, absY, cy){ absX -= parent.outerWidth() * 0.1; }; if (mainObj.anchorSide == "top" || mainObj.anchorSide == "bottom") { - var relX = absX - parent._private.position.x + (parent.outerWidth() - parent._private.data['border-width']) / 2; - var relY = (absY - parent._private.position.y) * 100 / (parent.outerHeight() - parent._private.data['border-width']); + var relX = absX - position.x + (parent.outerWidth() - borderWidth) / 2; + var relY = (absY - position.y) * 100 / (parent.outerHeight() - borderWidth); } else { - var relY = absY - parent._private.position.y + (parent.outerHeight() - parent._private.data['border-width']) / 2; - var relX = (absX - parent._private.position.x) * 100 / (parent.outerWidth() - parent._private.data['border-width']); + var relY = absY - position.y + (parent.outerHeight() - borderWidth) / 2; + var relX = (absX - position.x) * 100 / (parent.outerWidth() - borderWidth); } return {x: relX, y: relY}; @@ -893,160 +901,88 @@ AuxUnitLayout.getUsedLengthLR = function(node, tb){ return AuxUnitLayout.getUsedHeight(node, tb) + (units.length + 1) * AuxUnitLayout.unitGap; //One gap for leftmost outer margin } -AuxUnitLayout.fitUnits = function (node, location) { +AuxUnitLayout.setCurrentGap = function (location, value){ + if (location === "top") { + AuxUnitLayout.currentTopUnitGap = value; + } + else if (location === "bottom") { + AuxUnitLayout.currentBottomUnitGap = value; + } + else if (location === "right") { + AuxUnitLayout.currentRightUnitGap = value; + } + else { + AuxUnitLayout.currentLeftUnitGap = value; + } +}; + +AuxUnitLayout.fitUnits = function (node) { if (node.data('auxunitlayouts') === undefined) { return; } - if (location !== undefined) { - AuxUnitLayout.lastPos = location; + if ((node.data("class") === "compartment" || node.data("class") === "complex") + && node._private.children !== undefined && node._private.children.length !== 0) { + var parentWidth = node._private.autoWidth; + var parentHeight = node._private.autoHeight; + var padding = node._private.autoPadding; } - var top = node.data('auxunitlayouts').top; - var bottom = node.data('auxunitlayouts').bottom; - var left = node.data('auxunitlayouts').left; - var right = node.data('auxunitlayouts').right; + else { + var parentWidth = node.data("bbox").w; + var parentHeight = node.data("bbox").h; + var padding = 0; + } + var position = node.position(); + var parentX1 = position.x - parentWidth/2; + var parentX2 = position.x + parentWidth/2; + var parentY1 = position.y - parentHeight/2; + var parentY2 = position.y + parentHeight/2; //Get Parent node and find parent width - var parentWidth = node.data('bbox').w; - var parentHeight = node.data('bbox').h; var usedLength; var totalWidth; var estimatedGap; var units; - var addedShift; - - if (top !== undefined) { - //If new unit adaed done reset unit gap - if(AuxUnitLayout.lastPos === "top"){ - AuxUnitLayout.currentTopUnitGap = AuxUnitLayout.unitGap; - AuxUnitLayout.lastPos = -1; - } - //Find total top length - usedLength = AuxUnitLayout.getUsedLengthTB(node, top); - units = top.units; - //Compare the side lengths - if (parentWidth < usedLength) { - //If there is not enough space - //Estimate new margin & gap - totalWidth = AuxUnitLayout.getUsedWidth(node, top); - estimatedGap = (parentWidth - totalWidth) / (units.length + 1); - //Else scale by using available space, reducing margins and gaps. - //Check if new gap is enough to fit - - units[0].bbox.x -= AuxUnitLayout.currentTopUnitGap - estimatedGap; - - for (var i = 1; i < units.length; i++) { //Calculate new margins - units[i].bbox.x = units[i-1].bbox.x + units[i-1].bbox.w/2 + units[i].bbox.w/2 + estimatedGap; - } - AuxUnitLayout.currentTopUnitGap = estimatedGap; - - - } - else if(AuxUnitLayout.currentTopUnitGap < AuxUnitLayout.unitGap){ - for (var i = 0; i < units.length; i++) { //Shift all units - units[i].bbox.x -= (AuxUnitLayout.currentTopUnitGap - AuxUnitLayout.unitGap) * (i+1); - } - AuxUnitLayout.currentTopUnitGap = AuxUnitLayout.unitGap; - } - } - - if (bottom !== undefined) { - //If new unit adaed done reset unit gap - if(AuxUnitLayout.lastPos === "bottom"){ - AuxUnitLayout.currentBottomUnitGap = AuxUnitLayout.unitGap; - AuxUnitLayout.lastPos = -1; + var locations = ["top", "bottom", "left", "right"]; + for (var index = 0; index < locations.length; index++) { + var location = locations[index]; + var auxUnit = node.data('auxunitlayouts')[location]; + if (auxUnit === undefined) { + continue; } - //Find total currentBottomUnitGap length - usedLength = AuxUnitLayout.getUsedLengthTB(node, bottom); - units = bottom.units; - //Compare the side lengths - if (parentWidth < usedLength) { - //If there is not enough space - //Estimate new margin & gap - totalWidth = AuxUnitLayout.getUsedWidth(node, bottom); + if ( location === "top" || location === "bottom") { + usedLength = AuxUnitLayout.getUsedLengthTB(node, auxUnit); + units = auxUnit.units; + var firstPosition = AuxiliaryUnit.convertToRelativeCoord(units[0], parentX1, parentY1, undefined, node);//Position of the first unit + totalWidth = AuxUnitLayout.getUsedWidth(node, auxUnit); estimatedGap = (parentWidth - totalWidth) / (units.length + 1); - //Else scale by using available space, reducing margins and gaps. - //Check if new gap is enough to fit - - units[0].bbox.x -= AuxUnitLayout.currentBottomUnitGap - estimatedGap; - - for (var i = 1; i < units.length; i++) { //Shift all units - units[i].bbox.x = units[i-1].bbox.x + units[i-1].bbox.w/2 + units[i].bbox.w/2 + estimatedGap; + if (estimatedGap > AuxUnitLayout.unitGap) { + estimatedGap = AuxUnitLayout.unitGap; } - AuxUnitLayout.currentBottomUnitGap = estimatedGap; - - - } - else if(AuxUnitLayout.currentBottomUnitGap < AuxUnitLayout.unitGap){ - for (var i = 0; i < units.length; i++) { //Shift all units - units[i].bbox.x -= (AuxUnitLayout.currentBottomUnitGap - AuxUnitLayout.unitGap) * (i+1); + units[0].bbox.x = firstPosition.x + estimatedGap + units[0].bbox.w/2; + for (var i = 1; i < units.length; i++) { //Calculate new margins + units[i].bbox.x = units[i-1].bbox.x + units[i-1].bbox.w/2 + estimatedGap + units[i].bbox.w/2; } - AuxUnitLayout.currentBottomUnitGap = AuxUnitLayout.unitGap; - } - } - - if (left !== undefined) { - //Find total left length - //If new unit adaed done reset unit gap - if(AuxUnitLayout.lastPos === "left"){ - AuxUnitLayout.currentLeftUnitGap = AuxUnitLayout.unitGap; - AuxUnitLayout.lastPos = -1; + AuxUnitLayout.setCurrentGap(location, estimatedGap); } - usedLength = AuxUnitLayout.getUsedLengthLR(node, left); - units = left.units; - //Compare the side lengths - if (parentHeight < usedLength) { - //If there is not enough space - //Estimate new margin & gap - totalHeight = AuxUnitLayout.getUsedHeight(node, left); + else { + //Find total left length + usedLength = AuxUnitLayout.getUsedLengthLR(node, auxUnit); + units = auxUnit.units; + var firstPosition = AuxiliaryUnit.convertToRelativeCoord(units[0], parentX1, parentY1, undefined, node);//Position of the first unit + //Compare the side lengths + totalHeight = AuxUnitLayout.getUsedHeight(node, auxUnit); estimatedGap = (parentHeight - totalHeight) / (units.length + 1); - //Else scale by using available space, reducing margins and gaps. - //Check if new gap is enough to fit - units[0].bbox.y -= AuxUnitLayout.currentLeftUnitGap - estimatedGap; - - for (var i = 1; i < units.length; i++) { //Shift all units - units[i].bbox.y = units[i-1].bbox.y + units[i-1].bbox.h/2 + units[i].bbox.h/2 + estimatedGap; - } - AuxUnitLayout.currentLeftUnitGap = estimatedGap; - - } - else if(AuxUnitLayout.currentLeftUnitGap < AuxUnitLayout.unitGap){ - for (var i = 0; i < units.length; i++) { //Shift all units - units[i].bbox.y -= (AuxUnitLayout.currentLeftUnitGap - AuxUnitLayout.unitGap) * (i+1); + if (estimatedGap > AuxUnitLayout.unitGap) { + estimatedGap = AuxUnitLayout.unitGap; } - AuxUnitLayout.currentLeftUnitGap = AuxUnitLayout.unitGap; - } - } - - if (right !== undefined) { - //Find total right length - usedLength = AuxUnitLayout.getUsedLengthLR(node, right); - units = right.units; - //If new unit adaed done reset unit gap - if(AuxUnitLayout.lastPos === "right"){ - AuxUnitLayout.currentRightUnitGap = AuxUnitLayout.unitGap; - AuxUnitLayout.lastPos = -1; - } - //Compare the side lengths - if (parentHeight < usedLength) { - //If there is not enough space - //Estimate new margin & gap - totalHeight = AuxUnitLayout.getUsedHeight(node, right); - estimatedGap = (parentHeight - totalHeight) / (units.length + 1); //Else scale by using available space, reducing margins and gaps. //Check if new gap is enough to fit - units[0].bbox.y -= AuxUnitLayout.currentRightUnitGap - estimatedGap; - - for (var i = 1; i < units.length; i++) { //Shift all units - units[i].bbox.y = units[i-1].bbox.y + units[i-1].bbox.h/2 + units[i].bbox.h/2 + estimatedGap; - } - AuxUnitLayout.currentRightUnitGap = estimatedGap; - - } - else if(AuxUnitLayout.currentRightUnitGap < AuxUnitLayout.unitGap){ - for (var i = 0; i < units.length; i++) { //Shift all units - units[i].bbox.y -= (AuxUnitLayout.currentRightUnitGap - AuxUnitLayout.unitGap) * (i+1); + units[0].bbox.y = firstPosition.y + estimatedGap + units[0].bbox.h/2; + for (var i = 1; i < units.length; i++) { //Calculate new margins + units[i].bbox.y = units[i-1].bbox.y + units[i-1].bbox.h/2 + estimatedGap + units[i].bbox.h/2; } - AuxUnitLayout.currentRightUnitGap = AuxUnitLayout.unitGap; + AuxUnitLayout.currentLeftUnitGap = estimatedGap; } + AuxUnitLayout.setCurrentGap(location, estimatedGap); } }; From ca7f7e18766a61b011d85e14bc0b08a8369a8af3 Mon Sep 17 00:00:00 2001 From: kaansancak Date: Tue, 19 Jun 2018 11:45:01 +0300 Subject: [PATCH 25/29] Fix relocation bug after https://github.com/iVis-at-Bilkent/sbgnviz.js/commit/dad44f5af6f1dc54548b881f72b05d8b1e8d657b --- src/utilities/classes.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/utilities/classes.js b/src/utilities/classes.js index 4e1459d6..796d6455 100644 --- a/src/utilities/classes.js +++ b/src/utilities/classes.js @@ -948,6 +948,9 @@ AuxUnitLayout.fitUnits = function (node) { if (auxUnit === undefined) { continue; } + if (auxUnit.units.length <= 0 || !auxUnit.units) { + continue; + } if ( location === "top" || location === "bottom") { usedLength = AuxUnitLayout.getUsedLengthTB(node, auxUnit); units = auxUnit.units; @@ -1063,7 +1066,6 @@ AuxUnitLayout.modifyUnits = function(parentNode, unit, oldLocation, cy){ deleteUnits.splice(deleteIndex, 1); AuxUnitLayout.updateLengthCache(oldAuxUnit, cy); AuxUnitLayout.update(oldAuxUnit, cy, true); - AuxUnitLayout.fitUnits(parentNode, oldLocation); //If new is not constructed contruct interval if (!parentNode.data('auxunitlayouts')[location]) { parentNode.data('auxunitlayouts')[location] = AuxUnitLayout.construct(parentNode, location); @@ -1087,7 +1089,7 @@ AuxUnitLayout.modifyUnits = function(parentNode, unit, oldLocation, cy){ AuxUnitLayout.updateLengthCache(insertAuxUnit, cy); AuxUnitLayout.update(insertAuxUnit, cy, true); - AuxUnitLayout.fitUnits(parentNode, location); + AuxUnitLayout.fitUnits(parentNode); }; AuxUnitLayout.isEmpty = function(mainObj) { From 8fcad6b96b28673d12f8042540584914245afef1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87and=C4=B1ro=C4=9Flu?= Date: Tue, 19 Jun 2018 12:17:36 +0300 Subject: [PATCH 26/29] Move background images to map extensions iVis-at-Bilkent/newt#308 --- src/utilities/element-utilities-factory.js | 52 +++-------- .../json-to-sbgnml-converter-factory.js | 87 ++++--------------- .../sbgnml-to-json-converter-factory.js | 74 +++++++++------- 3 files changed, 75 insertions(+), 138 deletions(-) diff --git a/src/utilities/element-utilities-factory.js b/src/utilities/element-utilities-factory.js index 112c4ccc..7e85548f 100644 --- a/src/utilities/element-utilities-factory.js +++ b/src/utilities/element-utilities-factory.js @@ -1351,20 +1351,13 @@ module.exports = function () { 'background-image-opacity': '0' }; - var style = node._private.style; - - var imgs = style['background-image'] ? style['background-image'].value : []; - var xPos = style['background-position-x'] ? style['background-position-x'].value : []; - var yPos = style['background-position-y'] ? style['background-position-y'].value : []; - var widths = style['background-width'] ? style['background-width'].value : []; - var heights = style['background-height'] ? style['background-height'].value : []; - var fits = style['background-fit'] ? style['background-fit'].value : []; - var opacities = style['background-image-opacity'] ? style['background-image-opacity'].value : []; - - concatUnitToValues(xPos, "%"); - concatUnitToValues(yPos, "%"); - concatUnitToValues(heights, "%"); - concatUnitToValues(widths, "%"); + var imgs = node.data('background-image') ? node.data('background-image').split(" ") : []; + var xPos = node.data('background-position-x') ? node.data('background-position-x').split(" ") : []; + var yPos = node.data('background-position-y') ? node.data('background-position-y').split(" ") : []; + var widths = node.data('background-width') ? node.data('background-width').split(" ") : []; + var heights = node.data('background-height') ? node.data('background-height').split(" ") : []; + var fits = node.data('background-fit') ? node.data('background-fit').split(" ") : []; + var opacities = node.data('background-image-opacity') ? ("" + node.data('background-image-opacity')).split(" ") : []; if(status){ var index = imgs.indexOf(bgObj['background-image']); @@ -1388,30 +1381,13 @@ module.exports = function () { opacities[index] = '0'; } - var newStyle = { - 'background-image': imgs, - 'background-position-x': xPos, - 'background-position-y': yPos, - 'background-width': widths, - 'background-height': heights, - 'background-fit': fits, - 'background-image-opacity': opacities - } - - node.style(newStyle); - node.data('background-image', newStyle); - - function concatUnitToValues(values, unit){ - if(!values || values.length == 0) - return; - - for(var i = 0; i < values.length; i++){ - if(values[i] && values[i] !== "" && values[i] !== "auto"){ - var tmp = '' + values[i]; - values[i] = tmp + unit; - } - } - } + node.data('background-image', imgs.join(" ")); + node.data('background-position-x', xPos.join(" ")); + node.data('background-position-y', yPos.join(" ")); + node.data('background-width', widths.join(" ")); + node.data('background-height', heights.join(" ")); + node.data('background-fit', fits.join(" ")); + node.data('background-image-opacity', opacities.join(" ")); }; // Section End diff --git a/src/utilities/json-to-sbgnml-converter-factory.js b/src/utilities/json-to-sbgnml-converter-factory.js index 1382252b..d5a68bc1 100644 --- a/src/utilities/json-to-sbgnml-converter-factory.js +++ b/src/utilities/json-to-sbgnml-converter-factory.js @@ -172,6 +172,14 @@ module.exports = function () { } renderInformation.setListOfColorDefinitions(listOfColorDefinitions); + // populate list of background images + var listOfBackgroundImages = new renderExtension.ListOfBackgroundImages(); + for (var img in renderInfo.images) { + var backgroundImage = new renderExtension.BackgroundImage({id: renderInfo.images[img], value: img}); + listOfBackgroundImages.addBackgroundImage(backgroundImage); + } + renderInformation.setListOfBackgroundImages(listOfBackgroundImages); + // populates styles var listOfStyles = new renderExtension.ListOfStyles(); for (var key in renderInfo.styles) { @@ -184,7 +192,14 @@ module.exports = function () { fontStyle: style.properties.fontStyle, fill: style.properties.fill, // fill color stroke: style.properties.stroke, // stroke color - strokeWidth: style.properties.strokeWidth + strokeWidth: style.properties.strokeWidth, + backgroundImage: style.properties.backgroundImage, + backgroundFit: style.properties.backgroundFit, + backgroundPosX: style.properties.backgroundPosX, + backgroundPosY: style.properties.backgroundPosY, + backgroundWidth: style.properties.backgroundWidth, + backgroundHeight: style.properties.backgroundHeight, + backgroundImageOpacity: style.properties.backgroundImageOpacity, }); xmlStyle.setRenderGroup(g); listOfStyles.addStyle(xmlStyle); @@ -328,76 +343,6 @@ module.exports = function () { hasNewtExt = true; } - // add info for background image - var bgObj = node._private.style['background-image']; - if(bgObj && bgObj.value && bgObj.value.length > 0){ - var img = node._private.style['background-image']; - var fit = node._private.style['background-fit']; - var opacity = node._private.style['background-image-opacity']; - var x = node._private.style['background-position-x']; - var y = node._private.style['background-position-y']; - var width = node._private.style['background-width']; - var height = node._private.style['background-height']; - - sbgnvizExtString += " 0) - sbgnvizExtString += " fit=\"" + fit.strValue + "\" "; - if(opacity && opacity.value.length > 0) - sbgnvizExtString += " opacity=\"" + opacity.strValue + "\" "; - if(x && x.value.length > 0){ - var units = x.units; - var tmp = ""; - for(var i = 0; i < units.length; i++){ - if(units[i] && units[i] !== "") - tmp += "" + x.value[i] + units[i] + " "; - else - tmp += "" + x.value[i] + " "; - - } - - sbgnvizExtString += " x=\"" + tmp + "\" "; - } - if(y && y.value.length > 0){ - var units = y.units; - var tmp = ""; - for(var i = 0; i < units.length; i++){ - if(units[i] && units[i] !== "") - tmp += "" + y.value[i] + units[i] + " "; - else - tmp += "" + y.value[i] + " "; - } - - sbgnvizExtString += " y=\"" + tmp + "\" "; - } - if(width && width.value.length > 0){ - var units = width.units; - var tmp = ""; - for(var i = 0; i < units.length; i++){ - if(units[i] && units[i] !== "") - tmp += "" + width.value[i] + units[i] + " "; - else - tmp += "" + width.value[i] + " "; - } - - sbgnvizExtString += " width=\"" + tmp + "\" "; - } - if(height && height.value.length > 0){ - var units = height.units; - var tmp = ""; - for(var i = 0; i < units.length; i++){ - if(units[i] && units[i] !== "") - tmp += "" + height.value[i] + units[i] + " "; - else - tmp += "" + height.value[i] + " "; - } - - sbgnvizExtString += " height=\"" + tmp + "\" "; - } - - sbgnvizExtString += "/>"; - hasNewtExt = true; - } - // add string to a new extension for this glyph if(hasNewtExt) { var extension = self.getOrCreateExtension(glyph); diff --git a/src/utilities/sbgnml-to-json-converter-factory.js b/src/utilities/sbgnml-to-json-converter-factory.js index e30a0c06..4001bc27 100644 --- a/src/utilities/sbgnml-to-json-converter-factory.js +++ b/src/utilities/sbgnml-to-json-converter-factory.js @@ -365,35 +365,6 @@ module.exports = function () { y: Number(result.sbgnviz.positionBeforeSaving[0].$.y)}; nodeObj.collapse = true; } - if (result.sbgnviz.backgroundImage){ - var bgImage = result.sbgnviz.backgroundImage[0].$; - - var img = bgImage.img ? bgImage.img : ""; - var fit = bgImage.fit ? bgImage.fit : ""; - var opacity = bgImage.opacity ? bgImage.opacity : ""; - var x = bgImage.x ? bgImage.x : ""; - var y = bgImage.y ? bgImage.y : ""; - var width = bgImage.width ? bgImage.width : ""; - var height = bgImage.height ? bgImage.height : ""; - - styleObj['background-image'] = img; - styleObj['background-fit'] = fit; - styleObj['background-image-opacity'] = opacity; - styleObj['background-position-x'] = x; - styleObj['background-position-y'] = y; - styleObj['background-width'] = width; - styleObj['background-height'] = height; - - nodeObj['background-image'] = { - 'background-image': img.split(" "), - 'background-fit': fit.split(" "), - 'background-image-opacity': opacity.split(" "), - 'background-position-x': x.split(" "), - 'background-position-y': y.split(" "), - 'background-width': width.split(" "), - 'background-height': height.split(" "), - }; - } }); } @@ -641,6 +612,12 @@ module.exports = function () { for (var i=0; i < colorList.length; i++) { colorIDToValue[colorList[i].id] = colorList[i].value; } + // get all background image id references to their value + var imageList = renderInformation.listOfBackgroundImages.backgroundImages; + var imageIDToValue = {}; + for (var i=0; i < imageList.length; i++) { + imageIDToValue[imageList[i].id] = imageList[i].value; + } // convert style list to elementId-indexed object pointing to style // also convert color references to color values @@ -657,6 +634,10 @@ module.exports = function () { if (renderGroup.fill != null) { renderGroup.fill = colorIDToValue[renderGroup.fill]; } + // convert background image references + if (renderGroup.backgroundImage != null) { + renderGroup.backgroundImage = imageIDToValue[renderGroup.backgroundImage]; + } var idList = style.idList.split(' '); for (var j=0; j < idList.length; j++) { @@ -731,6 +712,41 @@ module.exports = function () { if (vtextAnchor) { node.data['text-valign'] = vtextAnchor; } + + var backgroundImage = elementIDToStyle[node.data['id']].backgroundImage; + if (backgroundImage) { + node.data['background-image'] = backgroundImage; + } + + var backgroundFit = elementIDToStyle[node.data['id']].backgroundFit; + if (backgroundFit) { + node.data['background-fit'] = backgroundFit; + } + + var backgroundPosX = elementIDToStyle[node.data['id']].backgroundPosX; + if (backgroundPosX) { + node.data['background-position-x'] = backgroundPosX; + } + + var backgroundPosY = elementIDToStyle[node.data['id']].backgroundPosY; + if (backgroundPosY) { + node.data['background-position-y'] = backgroundPosY; + } + + var backgroundWidth = elementIDToStyle[node.data['id']].backgroundWidth; + if (backgroundWidth) { + node.data['background-width'] = backgroundWidth; + } + + var backgroundHeight = elementIDToStyle[node.data['id']].backgroundHeight; + if (backgroundHeight) { + node.data['background-height'] = backgroundHeight; + } + + var backgroundImageOpacity = elementIDToStyle[node.data['id']].backgroundImageOpacity; + if (backgroundImageOpacity) { + node.data['background-image-opacity'] = backgroundImageOpacity; + } } // do the same for edges From 75357b5764eb5482517d48860d9a110a9980d1b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87and=C4=B1ro=C4=9Flu?= Date: Tue, 19 Jun 2018 12:55:41 +0300 Subject: [PATCH 27/29] Update libsbgn.js dependency --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5e064d52..2804c6bb 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "cytoscape": "iVis-at-Bilkent/cytoscape.js#unstable" }, "dependencies": { - "libsbgn.js": "^0.1.3", + "libsbgn.js": "github:sbgn/libsbgn.js#master", "pretty-data": "^0.40.0", "xml2js": "^0.4.17" }, From 41b1e04c5e86c63fd67212a4cde2d8a3db578a35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87and=C4=B1ro=C4=9Flu?= Date: Tue, 19 Jun 2018 13:12:38 +0300 Subject: [PATCH 28/29] Fix load without background image bug --- src/utilities/sbgnml-to-json-converter-factory.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/utilities/sbgnml-to-json-converter-factory.js b/src/utilities/sbgnml-to-json-converter-factory.js index 4001bc27..da595bda 100644 --- a/src/utilities/sbgnml-to-json-converter-factory.js +++ b/src/utilities/sbgnml-to-json-converter-factory.js @@ -613,10 +613,12 @@ module.exports = function () { colorIDToValue[colorList[i].id] = colorList[i].value; } // get all background image id references to their value - var imageList = renderInformation.listOfBackgroundImages.backgroundImages; - var imageIDToValue = {}; - for (var i=0; i < imageList.length; i++) { - imageIDToValue[imageList[i].id] = imageList[i].value; + if(renderInformation.listOfBackgroundImages){ + var imageList = renderInformation.listOfBackgroundImages.backgroundImages; + var imageIDToValue = {}; + for (var i=0; i < imageList.length; i++) { + imageIDToValue[imageList[i].id] = imageList[i].value; + } } // convert style list to elementId-indexed object pointing to style @@ -635,7 +637,7 @@ module.exports = function () { renderGroup.fill = colorIDToValue[renderGroup.fill]; } // convert background image references - if (renderGroup.backgroundImage != null) { + if (renderGroup.backgroundImage != null && imageIDToValue) { renderGroup.backgroundImage = imageIDToValue[renderGroup.backgroundImage]; } From 66673d5b1114d35a62e51066ef511fdae0871f81 Mon Sep 17 00:00:00 2001 From: kinimesi Date: Tue, 19 Jun 2018 16:17:28 +0300 Subject: [PATCH 29/29] Rebuild --- sbgnviz.js | 55479 +++++++++++++++++++++++++++++---------------------- 1 file changed, 31260 insertions(+), 24219 deletions(-) diff --git a/sbgnviz.js b/sbgnviz.js index b2224547..d8045103 100644 --- a/sbgnviz.js +++ b/sbgnviz.js @@ -1126,137 +1126,11 @@ }).call(this,_dereq_('_process')) -},{"_process":43}],2:[function(_dereq_,module,exports){ -var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; - -;(function (exports) { - 'use strict'; - - var Arr = (typeof Uint8Array !== 'undefined') - ? Uint8Array - : Array - - var PLUS = '+'.charCodeAt(0) - var SLASH = '/'.charCodeAt(0) - var NUMBER = '0'.charCodeAt(0) - var LOWER = 'a'.charCodeAt(0) - var UPPER = 'A'.charCodeAt(0) - var PLUS_URL_SAFE = '-'.charCodeAt(0) - var SLASH_URL_SAFE = '_'.charCodeAt(0) - - function decode (elt) { - var code = elt.charCodeAt(0) - if (code === PLUS || - code === PLUS_URL_SAFE) - return 62 // '+' - if (code === SLASH || - code === SLASH_URL_SAFE) - return 63 // '/' - if (code < NUMBER) - return -1 //no match - if (code < NUMBER + 10) - return code - NUMBER + 26 + 26 - if (code < UPPER + 26) - return code - UPPER - if (code < LOWER + 26) - return code - LOWER + 26 - } - - function b64ToByteArray (b64) { - var i, j, l, tmp, placeHolders, arr - - if (b64.length % 4 > 0) { - throw new Error('Invalid string. Length must be a multiple of 4') - } - - // the number of equal signs (place holders) - // if there are two placeholders, than the two characters before it - // represent one byte - // if there is only one, then the three characters before it represent 2 bytes - // this is just a cheap hack to not do indexOf twice - var len = b64.length - placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0 - - // base64 is 4/3 + up to two characters of the original data - arr = new Arr(b64.length * 3 / 4 - placeHolders) - - // if there are placeholders, only get up to the last complete 4 chars - l = placeHolders > 0 ? b64.length - 4 : b64.length - - var L = 0 - - function push (v) { - arr[L++] = v - } - - for (i = 0, j = 0; i < l; i += 4, j += 3) { - tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3)) - push((tmp & 0xFF0000) >> 16) - push((tmp & 0xFF00) >> 8) - push(tmp & 0xFF) - } - - if (placeHolders === 2) { - tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4) - push(tmp & 0xFF) - } else if (placeHolders === 1) { - tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2) - push((tmp >> 8) & 0xFF) - push(tmp & 0xFF) - } - - return arr - } - - function uint8ToBase64 (uint8) { - var i, - extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes - output = "", - temp, length - - function encode (num) { - return lookup.charAt(num) - } - - function tripletToBase64 (num) { - return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F) - } - - // go through the array every three bytes, we'll deal with trailing stuff later - for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) { - temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) - output += tripletToBase64(temp) - } - - // pad the end with zeros, but make sure to not forget the extra bytes - switch (extraBytes) { - case 1: - temp = uint8[uint8.length - 1] - output += encode(temp >> 2) - output += encode((temp << 4) & 0x3F) - output += '==' - break - case 2: - temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]) - output += encode(temp >> 10) - output += encode((temp >> 4) & 0x3F) - output += encode((temp << 2) & 0x3F) - output += '=' - break - } - - return output - } - - exports.toByteArray = b64ToByteArray - exports.fromByteArray = uint8ToBase64 -}(typeof exports === 'undefined' ? (this.base64js = {}) : exports)) +},{"_process":12}],2:[function(_dereq_,module,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){ +arguments[4][2][0].apply(exports,arguments) +},{"dup":2}],4:[function(_dereq_,module,exports){ (function (global){ /*! * The buffer module from node.js, for the browser. @@ -2809,76 +2683,226 @@ function blitBuffer (src, dst, offset, length) { }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"base64-js":2,"ieee754":13,"isarray":6}],6:[function(_dereq_,module,exports){ +},{"base64-js":5,"ieee754":6,"isarray":7}],5:[function(_dereq_,module,exports){ +var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + +;(function (exports) { + 'use strict'; + + var Arr = (typeof Uint8Array !== 'undefined') + ? Uint8Array + : Array + + var PLUS = '+'.charCodeAt(0) + var SLASH = '/'.charCodeAt(0) + var NUMBER = '0'.charCodeAt(0) + var LOWER = 'a'.charCodeAt(0) + var UPPER = 'A'.charCodeAt(0) + var PLUS_URL_SAFE = '-'.charCodeAt(0) + var SLASH_URL_SAFE = '_'.charCodeAt(0) + + function decode (elt) { + var code = elt.charCodeAt(0) + if (code === PLUS || + code === PLUS_URL_SAFE) + return 62 // '+' + if (code === SLASH || + code === SLASH_URL_SAFE) + return 63 // '/' + if (code < NUMBER) + return -1 //no match + if (code < NUMBER + 10) + return code - NUMBER + 26 + 26 + if (code < UPPER + 26) + return code - UPPER + if (code < LOWER + 26) + return code - LOWER + 26 + } + + function b64ToByteArray (b64) { + var i, j, l, tmp, placeHolders, arr + + if (b64.length % 4 > 0) { + throw new Error('Invalid string. Length must be a multiple of 4') + } + + // the number of equal signs (place holders) + // if there are two placeholders, than the two characters before it + // represent one byte + // if there is only one, then the three characters before it represent 2 bytes + // this is just a cheap hack to not do indexOf twice + var len = b64.length + placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0 + + // base64 is 4/3 + up to two characters of the original data + arr = new Arr(b64.length * 3 / 4 - placeHolders) + + // if there are placeholders, only get up to the last complete 4 chars + l = placeHolders > 0 ? b64.length - 4 : b64.length + + var L = 0 + + function push (v) { + arr[L++] = v + } + + for (i = 0, j = 0; i < l; i += 4, j += 3) { + tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3)) + push((tmp & 0xFF0000) >> 16) + push((tmp & 0xFF00) >> 8) + push(tmp & 0xFF) + } + + if (placeHolders === 2) { + tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4) + push(tmp & 0xFF) + } else if (placeHolders === 1) { + tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2) + push((tmp >> 8) & 0xFF) + push(tmp & 0xFF) + } + + return arr + } + + function uint8ToBase64 (uint8) { + var i, + extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes + output = "", + temp, length + + function encode (num) { + return lookup.charAt(num) + } + + function tripletToBase64 (num) { + return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F) + } + + // go through the array every three bytes, we'll deal with trailing stuff later + for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) { + temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) + output += tripletToBase64(temp) + } + + // pad the end with zeros, but make sure to not forget the extra bytes + switch (extraBytes) { + case 1: + temp = uint8[uint8.length - 1] + output += encode(temp >> 2) + output += encode((temp << 4) & 0x3F) + output += '==' + break + case 2: + temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]) + output += encode(temp >> 10) + output += encode((temp >> 4) & 0x3F) + output += encode((temp << 2) & 0x3F) + output += '=' + break + } + + return output + } + + exports.toByteArray = b64ToByteArray + exports.fromByteArray = uint8ToBase64 +}(typeof exports === 'undefined' ? (this.base64js = {}) : exports)) + +},{}],6:[function(_dereq_,module,exports){ +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] + + i += d + + e = s & ((1 << (-nBits)) - 1) + s >>= (-nBits) + nBits += eLen + for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {} + + m = e & ((1 << (-nBits)) - 1) + e >>= (-nBits) + nBits += mLen + for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {} + + 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) +} + +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 + + value = Math.abs(value) + + 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 (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 + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} + + e = (e << mLen) | m + eLen += mLen + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} + + buffer[offset + i - d] |= s * 128 +} + +},{}],7:[function(_dereq_,module,exports){ var toString = {}.toString; module.exports = Array.isArray || function (arr) { return toString.call(arr) == '[object Array]'; }; -},{}],7:[function(_dereq_,module,exports){ -module.exports = { - "100": "Continue", - "101": "Switching Protocols", - "102": "Processing", - "200": "OK", - "201": "Created", - "202": "Accepted", - "203": "Non-Authoritative Information", - "204": "No Content", - "205": "Reset Content", - "206": "Partial Content", - "207": "Multi-Status", - "300": "Multiple Choices", - "301": "Moved Permanently", - "302": "Moved Temporarily", - "303": "See Other", - "304": "Not Modified", - "305": "Use Proxy", - "307": "Temporary Redirect", - "308": "Permanent Redirect", - "400": "Bad Request", - "401": "Unauthorized", - "402": "Payment Required", - "403": "Forbidden", - "404": "Not Found", - "405": "Method Not Allowed", - "406": "Not Acceptable", - "407": "Proxy Authentication Required", - "408": "Request Time-out", - "409": "Conflict", - "410": "Gone", - "411": "Length Required", - "412": "Precondition Failed", - "413": "Request Entity Too Large", - "414": "Request-URI Too Large", - "415": "Unsupported Media Type", - "416": "Requested Range Not Satisfiable", - "417": "Expectation Failed", - "418": "I'm a teapot", - "422": "Unprocessable Entity", - "423": "Locked", - "424": "Failed Dependency", - "425": "Unordered Collection", - "426": "Upgrade Required", - "428": "Precondition Required", - "429": "Too Many Requests", - "431": "Request Header Fields Too Large", - "500": "Internal Server Error", - "501": "Not Implemented", - "502": "Bad Gateway", - "503": "Service Unavailable", - "504": "Gateway Time-out", - "505": "HTTP Version Not Supported", - "506": "Variant Also Negotiates", - "507": "Insufficient Storage", - "509": "Bandwidth Limit Exceeded", - "510": "Not Extended", - "511": "Network Authentication Required" -} - },{}],8:[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 @@ -2900,1073 +2924,1162 @@ 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":16}],9:[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); - } - - function lib$es6$promise$$internal$$fulfill(promise, value) { - if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; } - - promise._result = value; - promise._state = lib$es6$promise$$internal$$FULFILLED; +},{}],9:[function(_dereq_,module,exports){ +var http = _dereq_('http'); - if (promise._subscribers.length !== 0) { - lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, promise); - } - } +var https = module.exports; - 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; +for (var key in http) { + if (http.hasOwnProperty(key)) https[key] = http[key]; +}; - lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publishRejection, 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$$subscribe(parent, child, onFulfillment, onRejection) { - var subscribers = parent._subscribers; - var length = subscribers.length; +},{"http":37}],10:[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 + } + }); + }; +} 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 + } +} - parent._onerror = null; +},{}],11:[function(_dereq_,module,exports){ +/*! + * Determine if an object is a Buffer + * + * @author Feross Aboukhadijeh + * @license MIT + */ - subscribers[length] = child; - subscribers[length + lib$es6$promise$$internal$$FULFILLED] = onFulfillment; - subscribers[length + lib$es6$promise$$internal$$REJECTED] = onRejection; +// 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) +} - if (length === 0 && parent._state) { - lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, parent); - } - } +function isBuffer (obj) { + return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) +} - function lib$es6$promise$$internal$$publish(promise) { - var subscribers = promise._subscribers; - var settled = promise._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)) +} - if (subscribers.length === 0) { return; } +},{}],12:[function(_dereq_,module,exports){ +// shim for using process in browser +var process = module.exports = {}; - var child, callback, detail = promise._result; +// cached from whatever global is present so that test runners that stub it +// don't break things. But we need to wrap it in a try catch in case it is +// wrapped in strict mode code which doesn't define any globals. It's inside a +// function because try/catches deoptimize in certain engines. - for (var i = 0; i < subscribers.length; i += 3) { - child = subscribers[i]; - callback = subscribers[i + settled]; +var cachedSetTimeout; +var cachedClearTimeout; - if (child) { - lib$es6$promise$$internal$$invokeCallback(settled, child, callback, detail); +function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); +} +function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); +} +(function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; } else { - callback(detail); + cachedSetTimeout = defaultSetTimout; } - } - - promise._subscribers.length = 0; - } - - function lib$es6$promise$$internal$$ErrorObject() { - this.error = null; - } - - var lib$es6$promise$$internal$$TRY_CATCH_ERROR = new lib$es6$promise$$internal$$ErrorObject(); - - 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; - } + } catch (e) { + cachedSetTimeout = defaultSetTimout; } - - 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; + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; } else { - succeeded = true; + cachedClearTimeout = defaultClearTimeout; } - - if (promise === value) { - lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$cannotReturnOwn()); - return; + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } +} ()) +function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); } + } - } else { - value = detail; - succeeded = true; - } - 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); - } +} +function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); } - - 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); - } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); } - - function lib$es6$promise$enumerator$$Enumerator(Constructor, input) { - var enumerator = this; - - enumerator._instanceConstructor = Constructor; - enumerator.promise = new Constructor(lib$es6$promise$$internal$$noop); - - if (enumerator._validateInput(input)) { - enumerator._input = input; - enumerator.length = input.length; - enumerator._remaining = input.length; - - enumerator._init(); - - 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); - } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); } - } else { - lib$es6$promise$$internal$$reject(enumerator.promise, enumerator._validationError()); - } } - lib$es6$promise$enumerator$$Enumerator.prototype._validateInput = function(input) { - return lib$es6$promise$utils$$isArray(input); - }; - - lib$es6$promise$enumerator$$Enumerator.prototype._validationError = function() { - return new Error('Array Methods must be provided an Array'); - }; - - lib$es6$promise$enumerator$$Enumerator.prototype._init = function() { - this._result = new Array(this.length); - }; - - var lib$es6$promise$enumerator$$default = lib$es6$promise$enumerator$$Enumerator; - lib$es6$promise$enumerator$$Enumerator.prototype._enumerate = function() { - var enumerator = this; - var length = enumerator.length; - var promise = enumerator.promise; - var input = enumerator._input; +} +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; - for (var i = 0; promise._state === lib$es6$promise$$internal$$PENDING && i < length; i++) { - enumerator._eachEntry(input[i], i); - } - }; +function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} - lib$es6$promise$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) { - var enumerator = this; - var c = enumerator._instanceConstructor; +function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; - 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); + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } } - } else { - enumerator._remaining--; - enumerator._result[i] = entry; - } - }; - - lib$es6$promise$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) { - var enumerator = this; - var promise = enumerator.promise; - - if (promise._state === lib$es6$promise$$internal$$PENDING) { - enumerator._remaining--; + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); +} - if (state === lib$es6$promise$$internal$$REJECTED) { - lib$es6$promise$$internal$$reject(promise, value); - } else { - enumerator._result[i] = value; +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; } - } - - 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; } - 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; + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; - var promise = new Constructor(lib$es6$promise$$internal$$noop); +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; - if (!lib$es6$promise$utils$$isArray(entries)) { - lib$es6$promise$$internal$$reject(promise, new TypeError('You must pass an array to race.')); - return promise; - } +function noop() {} - var length = entries.length; +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; +process.prependListener = noop; +process.prependOnceListener = noop; - function onFulfillment(value) { - lib$es6$promise$$internal$$resolve(promise, value); - } +process.listeners = function (name) { return [] } - function onRejection(reason) { - lib$es6$promise$$internal$$reject(promise, reason); - } +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; - 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); - } +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; - 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; +},{}],13:[function(_dereq_,module,exports){ +(function (global){ +/*! https://mths.be/punycode v1.4.1 by @mathias */ +;(function(root) { - if (object && typeof object === 'object' && object.constructor === Constructor) { - return object; - } + /** 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; + } - 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; + /** + * The `punycode` object. + * @name punycode + * @type Object + */ + var punycode, - var lib$es6$promise$promise$$counter = 0; + /** Highest positive signed 32-bit float value */ + maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1 - function lib$es6$promise$promise$$needsResolver() { - throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); - } + /** Bootstring parameters */ + base = 36, + tMin = 1, + tMax = 26, + skew = 38, + damp = 700, + initialBias = 72, + initialN = 128, // 0x80 + delimiter = '-', // '\x2D' - 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."); - } + /** Regular expressions */ + regexPunycode = /^xn--/, + regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars + regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators - 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. + /** 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' + }, - Terminology - ----------- + /** Convenience shortcuts */ + baseMinusTMin = base - tMin, + floor = Math.floor, + stringFromCharCode = String.fromCharCode, - - `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. + /** Temporary variable */ + key; - A promise can be in one of three states: pending, fulfilled, or rejected. - - 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. - - 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. + /*--------------------------------------------------------------------------*/ + /** + * 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]); + } - Basic Usage: - ------------ + /** + * 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; + } - ```js - var promise = new Promise(function(resolve, reject) { - // on success - resolve(value); + /** + * 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; + } - // on failure - reject(reason); - }); + /** + * 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; + } - promise.then(function(value) { - // on fulfillment - }, function(reason) { - // on rejection - }); - ``` + /** + * 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(''); + } - Advanced Usage: - --------------- + /** + * 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; + } - Promises shine when abstracting away asynchronous interactions such as - `XMLHttpRequest`s. + /** + * 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); + } - ```js - function getJSON(url) { - return new Promise(function(resolve, reject){ - var xhr = new XMLHttpRequest(); + /** + * 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)); + } - xhr.open('GET', url); - xhr.onreadystatechange = handler; - xhr.responseType = 'json'; - xhr.setRequestHeader('Accept', 'application/json'); - xhr.send(); + /** + * 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 handler() { - if (this.readyState === this.DONE) { - if (this.status === 200) { - resolve(this.response); - } else { - reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); - } - } - }; - }); - } + // 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. - getJSON('/posts.json').then(function(json) { - // on fulfillment - }, function(reason) { - // on rejection - }); - ``` + basic = input.lastIndexOf(delimiter); + if (basic < 0) { + basic = 0; + } - Unlike callbacks, promises are great composable primitives. + 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)); + } - ```js - Promise.all([ - getJSON('/posts'), - getJSON('/comments') - ]).then(function(values){ - values[0] // => postsJSON - values[1] // => commentsJSON + // Main decoding loop: start just after the last delimiter if any basic code + // points were copied; start at the beginning otherwise. - return values; - }); - ``` + for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) { - @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 = []; + // `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) { - if (lib$es6$promise$$internal$$noop !== resolver) { - if (!lib$es6$promise$utils$$isFunction(resolver)) { - lib$es6$promise$promise$$needsResolver(); - } + if (index >= inputLength) { + error('invalid-input'); + } - if (!(this instanceof lib$es6$promise$promise$$Promise)) { - lib$es6$promise$promise$$needsNew(); - } + digit = basicToDigit(input.charCodeAt(index++)); - lib$es6$promise$$internal$$initializePromise(this, resolver); - } - } + if (digit >= base || digit > floor((maxInt - i) / w)) { + error('overflow'); + } - 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; + i += digit * w; + t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); - lib$es6$promise$promise$$Promise.prototype = { - constructor: lib$es6$promise$promise$$Promise, + if (digit < t) { + break; + } - /** - 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. + baseMinusT = base - t; + if (w > floor(maxInt / baseMinusT)) { + error('overflow'); + } - ```js - findUser().then(function(user){ - // user is available - }, function(reason){ - // user is unavailable, and you are given the reason why - }); - ``` + w *= baseMinusT; - Chaining - -------- + } - 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. + out = output.length + 1; + bias = adapt(i - oldi, out, oldi == 0); - ```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'` - }); + // `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'); + } - 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. + n += floor(i / out); + i %= out; - ```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 - }); - ``` + // Insert `n` at position `i` of the output + output.splice(i++, 0, n); - Assimilation - ------------ + } - 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*. + return ucs2encode(output); + } - ```js - findUser().then(function (user) { - return findCommentsByAuthor(user); - }).then(function (comments) { - // The user's comments are now available - }); - ``` + /** + * 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; - If the assimliated promise rejects, then the downstream promise will also reject. + // Convert the input in UCS-2 to Unicode + input = ucs2decode(input); - ```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 - }); - ``` + // Cache the length + inputLength = input.length; - Simple Example - -------------- + // Initialize the state + n = initialN; + delta = 0; + bias = initialBias; - Synchronous Example + // Handle the basic code points + for (j = 0; j < inputLength; ++j) { + currentValue = input[j]; + if (currentValue < 0x80) { + output.push(stringFromCharCode(currentValue)); + } + } - ```javascript - var result; + handledCPCount = basicLength = output.length; - try { - result = findResult(); - // success - } catch(reason) { - // failure - } - ``` + // `handledCPCount` is the number of code points that have been handled; + // `basicLength` is the number of basic code points. - Errback Example + // Finish the basic string - if it is not empty - with a delimiter + if (basicLength) { + output.push(delimiter); + } - ```js - findResult(function(result, err){ - if (err) { - // failure - } else { - // success - } - }); - ``` + // Main encoding loop: + while (handledCPCount < inputLength) { - Promise Example; + // 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; + } + } - ```javascript - findResult().then(function(result){ - // success - }, function(reason){ - // failure - }); - ``` + // 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'); + } - Advanced Example - -------------- + delta += (m - n) * handledCPCountPlusOne; + n = m; - Synchronous Example + for (j = 0; j < inputLength; ++j) { + currentValue = input[j]; - ```javascript - var author, books; + if (currentValue < n && ++delta > maxInt) { + error('overflow'); + } - try { - author = findAuthor(); - books = findBooksByAuthor(author); - // success - } catch(reason) { - // failure - } - ``` - - Errback Example - - ```js - - function foundBooks(books) { + 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; + } + } - function failure(reason) { + ++delta; + ++n; - } + } + return output.join(''); + } - 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 - } - }); - ``` + /** + * 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; + }); + } - Promise Example; + /** + * 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; + }); + } - ```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; + /** 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 + }; - if (state === lib$es6$promise$$internal$$FULFILLED && !onFulfillment || state === lib$es6$promise$$internal$$REJECTED && !onRejection) { - return this; - } + /** 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; + } - var child = new this.constructor(lib$es6$promise$$internal$$noop); - var result = parent._result; +}(this)); - 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); - } +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - return child; - }, +},{}],14:[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. - /** - `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same - as the catch block of a try/catch statement. +'use strict'; - ```js - function findAuthor(){ - throw new Error('couldn't find that author'); - } +// If obj.hasOwnProperty has been overridden, then calling +// obj.hasOwnProperty(prop) will break. +// See: https://github.com/joyent/node/issues/1707 +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} - // synchronous - try { - findAuthor(); - } catch(reason) { - // something went wrong - } +module.exports = function(qs, sep, eq, options) { + sep = sep || '&'; + eq = eq || '='; + var obj = {}; - // async with promises - findAuthor().catch(function(reason){ - // something went wrong - }); - ``` + if (typeof qs !== 'string' || qs.length === 0) { + return obj; + } - @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; + var regexp = /\+/g; + qs = qs.split(sep); - 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 maxKeys = 1000; + if (options && typeof options.maxKeys === 'number') { + maxKeys = options.maxKeys; + } - var P = local.Promise; + var len = qs.length; + // maxKeys <= 0 means that we should not limit keys count + if (maxKeys > 0 && len > maxKeys) { + len = maxKeys; + } - if (P && Object.prototype.toString.call(P.resolve()) === '[object Promise]' && !P.cast) { - return; - } + for (var i = 0; i < len; ++i) { + var x = qs[i].replace(regexp, '%20'), + idx = x.indexOf(eq), + kstr, vstr, k, v; - local.Promise = lib$es6$promise$promise$$default; + if (idx >= 0) { + kstr = x.substr(0, idx); + vstr = x.substr(idx + 1); + } else { + kstr = x; + vstr = ''; } - var lib$es6$promise$polyfill$$default = lib$es6$promise$polyfill$$polyfill; - var lib$es6$promise$umd$$ES6Promise = { - 'Promise': lib$es6$promise$promise$$default, - 'polyfill': lib$es6$promise$polyfill$$default - }; + k = decodeURIComponent(kstr); + v = decodeURIComponent(vstr); - /* 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; + if (!hasOwnProperty(obj, k)) { + obj[k] = v; + } else if (isArray(obj[k])) { + obj[k].push(v); + } else { + obj[k] = [obj[k], v]; } + } - lib$es6$promise$polyfill$$default(); -}).call(this); - + return obj; +}; -}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +var isArray = Array.isArray || function (xs) { + return Object.prototype.toString.call(xs) === '[object Array]'; +}; -},{"_process":43}],10:[function(_dereq_,module,exports){ +},{}],15:[function(_dereq_,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -3988,17158 +4101,14786 @@ 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; +'use strict'; -// Backwards-compat with node 0.10.x -EventEmitter.EventEmitter = EventEmitter; +var stringifyPrimitive = function(v) { + switch (typeof v) { + case 'string': + return v; -EventEmitter.prototype._events = undefined; -EventEmitter.prototype._maxListeners = undefined; + case 'boolean': + return v ? 'true' : 'false'; -// 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; + case 'number': + return isFinite(v) ? v : ''; -// 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; + default: + return ''; + } }; -EventEmitter.prototype.emit = function(type) { - var er, handler, len, args, i, listeners; - - if (!this._events) - this._events = {}; +module.exports = function(obj, sep, eq, name) { + sep = sep || '&'; + eq = eq || '='; + if (obj === null) { + obj = undefined; + } - // 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 + if (typeof obj === 'object') { + return map(objectKeys(obj), function(k) { + var ks = encodeURIComponent(stringifyPrimitive(k)) + eq; + if (isArray(obj[k])) { + return map(obj[k], function(v) { + return ks + encodeURIComponent(stringifyPrimitive(v)); + }).join(sep); + } else { + return ks + encodeURIComponent(stringifyPrimitive(obj[k])); } - throw TypeError('Uncaught, unspecified "error" event.'); - } - } + }).join(sep); - handler = this._events[type]; + } - if (isUndefined(handler)) - return false; + if (!name) return ''; + return encodeURIComponent(stringifyPrimitive(name)) + eq + + encodeURIComponent(stringifyPrimitive(obj)); +}; - 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 isArray = Array.isArray || function (xs) { + return Object.prototype.toString.call(xs) === '[object Array]'; +}; - listeners = handler.slice(); - len = listeners.length; - for (i = 0; i < len; i++) - listeners[i].apply(this, args); +function map (xs, f) { + if (xs.map) return xs.map(f); + var res = []; + for (var i = 0; i < xs.length; i++) { + res.push(f(xs[i], i)); } + return res; +} - return true; +var objectKeys = Object.keys || function (obj) { + var res = []; + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) res.push(key); + } + return res; }; -EventEmitter.prototype.addListener = function(type, listener) { - var m; - - if (!isFunction(listener)) - throw TypeError('listener must be a function'); +},{}],16:[function(_dereq_,module,exports){ +'use strict'; - if (!this._events) - this._events = {}; +exports.decode = exports.parse = _dereq_('./decode'); +exports.encode = exports.stringify = _dereq_('./encode'); - // 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); +},{"./decode":14,"./encode":15}],17:[function(_dereq_,module,exports){ +module.exports = _dereq_('./lib/_stream_duplex.js'); - 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]; +},{"./lib/_stream_duplex.js":18}],18:[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. - // 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; - } +// 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. - 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(); - } - } - } +'use strict'; - return this; -}; +/**/ -EventEmitter.prototype.on = EventEmitter.prototype.addListener; +var processNextTick = _dereq_('process-nextick-args'); +/**/ -EventEmitter.prototype.once = function(type, listener) { - if (!isFunction(listener)) - throw TypeError('listener must be a function'); +/**/ +var objectKeys = Object.keys || function (obj) { + var keys = []; + for (var key in obj) { + keys.push(key); + }return keys; +}; +/**/ - var fired = false; +module.exports = Duplex; - function g() { - this.removeListener(type, g); +/**/ +var util = _dereq_('core-util-is'); +util.inherits = _dereq_('inherits'); +/**/ - if (!fired) { - fired = true; - listener.apply(this, arguments); - } - } +var Readable = _dereq_('./_stream_readable'); +var Writable = _dereq_('./_stream_writable'); - g.listener = listener; - this.on(type, g); +util.inherits(Duplex, Readable); - return this; -}; +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]; +} -// emits a 'removeListener' event iff the listener was removed -EventEmitter.prototype.removeListener = function(type, listener) { - var list, position, length, i; +function Duplex(options) { + if (!(this instanceof Duplex)) return new Duplex(options); - if (!isFunction(listener)) - throw TypeError('listener must be a function'); + Readable.call(this, options); + Writable.call(this, options); - if (!this._events || !this._events[type]) - return this; + if (options && options.readable === false) this.readable = false; - list = this._events[type]; - length = list.length; - position = -1; + if (options && options.writable === false) this.writable = false; - if (list === listener || - (isFunction(list.listener) && list.listener === listener)) { - delete this._events[type]; - if (this._events.removeListener) - this.emit('removeListener', type, listener); + this.allowHalfOpen = true; + if (options && options.allowHalfOpen === false) this.allowHalfOpen = false; - } else if (isObject(list)) { - for (i = length; i-- > 0;) { - if (list[i] === listener || - (list[i].listener && list[i].listener === listener)) { - position = i; - break; - } - } + this.once('end', onend); +} - if (position < 0) - return this; +// 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; - if (list.length === 1) { - list.length = 0; - delete this._events[type]; - } else { - list.splice(position, 1); + // no more data can be written. + // But allow more writes to happen in this tick. + processNextTick(onEndNT, this); +} + +function onEndNT(self) { + self.end(); +} + +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; } - if (this._events.removeListener) - this.emit('removeListener', type, listener); + // backward compatibility, the user is explicitly + // managing destroyed + this._readableState.destroyed = value; + this._writableState.destroyed = value; } +}); - return this; -}; - -EventEmitter.prototype.removeAllListeners = function(type) { - var key, listeners; +Duplex.prototype._destroy = function (err, cb) { + this.push(null); + this.end(); - if (!this._events) - return this; + processNextTick(cb, err); +}; - // 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 forEach(xs, f) { + for (var i = 0, l = xs.length; i < l; i++) { + f(xs[i], i); } +} +},{"./_stream_readable":20,"./_stream_writable":22,"core-util-is":26,"inherits":10,"process-nextick-args":28}],19:[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. - // 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; - } +// a passthrough stream. +// basically just the most minimal sort of Transform stream. +// Every written chunk gets output as-is. - listeners = this._events[type]; +'use strict'; - if (isFunction(listeners)) { - this.removeListener(type, listeners); - } else { - // LIFO order - while (listeners.length) - this.removeListener(type, listeners[listeners.length - 1]); - } - delete this._events[type]; +module.exports = PassThrough; - return this; -}; +var Transform = _dereq_('./_stream_transform'); -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; -}; +/**/ +var util = _dereq_('core-util-is'); +util.inherits = _dereq_('inherits'); +/**/ -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; -}; +util.inherits(PassThrough, Transform); -function isFunction(arg) { - return typeof arg === 'function'; -} +function PassThrough(options) { + if (!(this instanceof PassThrough)) return new PassThrough(options); -function isNumber(arg) { - return typeof arg === 'number'; + Transform.call(this, options); } -function isObject(arg) { - return typeof arg === 'object' && arg !== null; -} +PassThrough.prototype._transform = function (chunk, encoding, cb) { + cb(null, chunk); +}; +},{"./_stream_transform":21,"core-util-is":26,"inherits":10}],20:[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. -function isUndefined(arg) { - return arg === void 0; -} +'use strict'; -},{}],11:[function(_dereq_,module,exports){ +/**/ -var hasOwn = Object.prototype.hasOwnProperty; -var toString = Object.prototype.toString; +var processNextTick = _dereq_('process-nextick-args'); +/**/ -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); - } - } - } -}; +module.exports = Readable; +/**/ +var isArray = _dereq_('isarray'); +/**/ -},{}],12:[function(_dereq_,module,exports){ -var http = _dereq_('http'); +/**/ +var Duplex; +/**/ -var https = module.exports; +Readable.ReadableState = ReadableState; -for (var key in http) { - if (http.hasOwnProperty(key)) https[key] = http[key]; +/**/ +var EE = _dereq_('events').EventEmitter; + +var EElistenerCount = function (emitter, type) { + return emitter.listeners(type).length; }; +/**/ -https.request = function (params, cb) { - if (!params) params = {}; - params.scheme = 'https'; - params.protocol = 'https:'; - return http.request.call(this, params, cb); +/**/ +var Stream = _dereq_('./internal/streams/stream'); +/**/ + +// TODO(bmeurer): Change this back to const once hole checks are +// properly optimized away early in Ignition+TurboFan. +/**/ +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; +} +/**/ -},{"http":108}],13:[function(_dereq_,module,exports){ -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] +/**/ +var util = _dereq_('core-util-is'); +util.inherits = _dereq_('inherits'); +/**/ - i += d +/**/ +var debugUtil = _dereq_('util'); +var debug = void 0; +if (debugUtil && debugUtil.debuglog) { + debug = debugUtil.debuglog('stream'); +} else { + debug = function () {}; +} +/**/ - e = s & ((1 << (-nBits)) - 1) - s >>= (-nBits) - nBits += eLen - for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {} +var BufferList = _dereq_('./internal/streams/BufferList'); +var destroyImpl = _dereq_('./internal/streams/destroy'); +var StringDecoder; - m = e & ((1 << (-nBits)) - 1) - e >>= (-nBits) - nBits += mLen - for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {} +util.inherits(Readable, Stream); - if (e === 0) { - e = 1 - eBias - } else if (e === eMax) { - return m ? NaN : ((s ? -1 : 1) * Infinity) +var kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume']; + +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); } else { - m = m + Math.pow(2, mLen) - e = e - eBias + // 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]]; } - 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 +function ReadableState(options, stream) { + Duplex = Duplex || _dereq_('./_stream_duplex'); - value = Math.abs(value) + options = options || {}; - 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 - } + // 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 (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 - } - } + if (stream instanceof Duplex) this.objectMode = this.objectMode || !!options.readableObjectMode; - for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} + // 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 defaultHwm = this.objectMode ? 16 : 16 * 1024; + this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm; - e = (e << mLen) | m - eLen += mLen - for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} + // cast to ints. + this.highWaterMark = Math.floor(this.highWaterMark); - buffer[offset + i - d] |= s * 128 -} + // 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; -},{}],14:[function(_dereq_,module,exports){ + // 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; -var indexOf = [].indexOf; + // 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; -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; -}; -},{}],15:[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 - } - }); - }; -} 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 - } -} + // has it been destroyed + this.destroyed = false; -},{}],16:[function(_dereq_,module,exports){ -/*! - * Determine if an object is a Buffer - * - * @author Feross Aboukhadijeh - * @license MIT - */ + // 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'; -// 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) -} + // the number of writers that are awaiting a drain event in .pipe()s + this.awaitDrain = 0; -function isBuffer (obj) { - return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) -} + // if true, a maybeReadMore has been scheduled + this.readingMore = false; -// 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)) + 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; + } } -},{}],17:[function(_dereq_,module,exports){ -// Ignore module for browserify (see package.json) -},{}],18:[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() { +function Readable(options) { + Duplex = Duplex || _dereq_('./_stream_duplex'); -// 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 = $; - } - } -} + if (!(this instanceof Readable)) return new Readable(options); -// attaches jsonld API to the given object -var wrapper = function(jsonld) { + this._readableState = new ReadableState(options, this); -/* Core API */ + // legacy + this.readable = true; -/** - * 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.')); - }); - } + if (options) { + if (typeof options.read === 'function') this._read = options.read; - // get arguments - if(typeof options === 'function') { - callback = options; - options = {}; + if (typeof options.destroy === 'function') this._destroy = options.destroy; } - 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'})); - }); - } + Stream.call(this); +} - // nothing to compact - if(input === null) { - return jsonld.nextTick(function() { - callback(null, null); - }); - } +Object.defineProperty(Readable.prototype, 'destroyed', { + get: function () { + if (this._readableState === undefined) { + return false; + } + return this._readableState.destroyed; + }, + set: function (value) { + // we ignore the value if the stream + // has not been initialized yet + if (!this._readableState) { + return; + } - // 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; + // backward compatibility, the user is explicitly + // managing destroyed + this._readableState.destroyed = value; } +}); - var expand = function(input, options, callback) { - if(options.skipExpansion) { - return jsonld.nextTick(function() { - callback(null, input); - }); - } - 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.destroy = destroyImpl.destroy; +Readable.prototype._undestroy = destroyImpl.undestroy; +Readable.prototype._destroy = function (err, cb) { + this.push(null); + cb(err); +}; - // 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})); - } +// 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; - var compacted; - try { - // do compaction - compacted = new Processor().compact(activeCtx, null, expanded, options); - } catch(ex) { - return callback(ex); + if (!state.objectMode) { + if (typeof chunk === 'string') { + encoding = encoding || state.defaultEncoding; + if (encoding !== state.encoding) { + chunk = Buffer.from(chunk, encoding); + encoding = ''; } + skipChunkCheck = true; + } + } else { + skipChunkCheck = true; + } - cleanup(null, compacted, activeCtx, options); - }); - }); + return readableAddChunk(this, chunk, encoding, false, skipChunkCheck); +}; - // performs clean up after compaction - function cleanup(err, compacted, activeCtx, options) { - if(err) { - return callback(err); - } +// Unshift should *always* be something directly out of read() +Readable.prototype.unshift = function (chunk) { + return readableAddChunk(this, chunk, null, true, false); +}; - 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 = {}; +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); } - } else if(options.graph && _isObject(compacted)) { - // always use array if graph option is on - compacted = [compacted]; - } - - // follow @context key - if(_isObject(ctx) && '@context' in ctx) { - ctx = ctx['@context']; - } - // 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]); + 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; } + } - // remove array if only one context - var hasContext = (ctx.length > 0); - if(ctx.length === 1) { - ctx = ctx[0]; - } + return needMoreData(state); +} - // 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]; - } - } +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); - callback(null, compacted, activeCtx); + if (state.needReadable) emitReadable(stream); } -}; + maybeReadMore(stream, state); +} -/** - * 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 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; +} - // get arguments - if(typeof options === 'function') { - callback = options; - options = {}; - } - options = options || {}; +// 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); +} - // set default options - if(!('documentLoader' in options)) { - options.documentLoader = jsonld.loadDocument; - } - if(!('keepFreeFloatingNodes' in options)) { - options.keepFreeFloatingNodes = false; - } +Readable.prototype.isPaused = function () { + return this._readableState.flowing === false; +}; - 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}); - }); +// 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; +}; - 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); - } +// 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; +} - var expanded; - try { - var processor = new Processor(); - var activeCtx = _getInitialContext(options); - var document = input.document; - var remoteContext = input.remoteContext['@context']; +// 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; +} - // process optional expandContext - if(input.expandContext) { - activeCtx = processor.processContext( - activeCtx, input.expandContext['@context'], options); - } +// 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; - // process remote context from HTTP Link Header - if(remoteContext) { - activeCtx = processor.processContext( - activeCtx, remoteContext, options); - } + if (n !== 0) state.emittedReadable = false; - // expand document - expanded = processor.expand( - activeCtx, null, document, options, false); + // 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; + } - // 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 = []; - } + n = howMuchToRead(n, state); - // normalize to an array - if(!_isArray(expanded)) { - expanded = [expanded]; - } - } catch(ex) { - return callback(ex); - } - callback(null, expanded); - }); + // 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; } -}; -/** - * 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.')); - }); - } + // 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. - // get arguments - if(typeof options === 'function') { - callback = options; - options = {}; - } else if(typeof ctx === 'function') { - callback = ctx; - ctx = null; - options = {}; + // if we need a readable event, then we need to do some reading. + var doRead = state.needReadable; + debug('need readable', doRead); + + // 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); } - options = options || {}; - // set default options - if(!('base' in options)) { - options.base = (typeof input === 'string') ? input : ''; + // 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); } - if(!('documentLoader' in options)) { - options.documentLoader = jsonld.loadDocument; + + var ret; + if (n > 0) ret = fromList(n, state);else ret = null; + + if (ret === null) { + state.needReadable = true; + n = 0; + } else { + state.length -= n; } - // 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})); - } + 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; - var flattened; - try { - // do flattening - flattened = new Processor().flatten(_input); - } catch(ex) { - return callback(ex); - } + // If we tried to read() past the EOF, then emit end on the next tick. + if (nOrig !== n && state.ended) endReadable(this); + } - if(ctx === null) { - return callback(null, flattened); - } + if (ret !== null) this.emit('data', ret); - // 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); - }); - }); + return ret; }; -/** - * 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.')); - }); +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; - // get arguments - if(typeof options === 'function') { - callback = options; - options = {}; - } - options = options || {}; + // emit 'readable' now to make sure it gets picked up. + emitReadable(stream); +} - // set default options - if(!('base' in options)) { - options.base = (typeof input === 'string') ? input : ''; +// 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) processNextTick(emitReadable_, stream);else emitReadable_(stream); } - if(!('documentLoader' in options)) { - options.documentLoader = jsonld.loadDocument; +} + +function emitReadable_(stream) { + debug('emit readable'); + stream.emit('readable'); + flow(stream); +} + +// 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; + processNextTick(maybeReadMore_, stream, state); } - if(!('embed' in options)) { - options.embed = '@last'; +} + +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; } - options.explicit = options.explicit || false; - if(!('requireAll' in options)) { - options.requireAll = true; + state.readingMore = false; +} + +// 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')); +}; + +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; } - options.omitDefault = options.omitDefault || false; + state.pipesCount += 1; + debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts); - 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}); - }); + var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr; - 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 || {}; + var endFn = doEnd ? onend : unpipe; + if (state.endEmitted) processNextTick(endFn);else src.once('end', endFn); + + dest.on('unpipe', onunpipe); + function onunpipe(readable, unpipeInfo) { + debug('onunpipe'); + if (readable === src) { + if (unpipeInfo && unpipeInfo.hasUnpiped === false) { + unpipeInfo.hasUnpiped = true; + cleanup(); } - } else { - ctx = {}; } + } - // 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})); - } + function onend() { + debug('onend'); + dest.end(); + } - // 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})); - } + // 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); - var framed; - try { - // do framing - framed = new Processor().frame(expanded, expandedFrame, opts); - } catch(ex) { - return callback(ex); - } + 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); - // 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); - }); - }); - }); + 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(); } -}; -/** - * **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; + // 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(); + } } - 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 = {}; + // 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); } - options = options || {}; - // set default options - if(!('base' in options)) { - options.base = (typeof input === 'string') ? input : ''; + // 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(); } + dest.once('finish', onfinish); - // 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})); - } + function unpipe() { + debug('unpipe'); + src.unpipe(dest); + } - var flattened; - try { - // flatten the graph - flattened = new Processor().flatten(_input); - } catch(ex) { - return callback(ex); - } + // tell the dest that it's being piped to + dest.emit('pipe', src); - // 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]; + // start the flow if it hasn't been started already. + if (!state.flowing) { + debug('pipe resume'); + src.resume(); + } - var recurse = function(subject) { - // can't replace just a string - if(!_isObject(subject) && !_isArray(subject)) { - return; - } + return dest; +}; - // bottom out recursion on re-visit - if(_isObject(subject)) { - if(recurse.visited[subject['@id']]) { - return; - } - recurse.visited[subject['@id']] = true; - } +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); + } + }; +} - // each array element *or* object key - for(var k in subject) { - var obj = subject[k]; - var isid = (jsonld.getContextValue(ctx, k, '@type') === '@id'); +Readable.prototype.unpipe = function (dest) { + var state = this._readableState; + var unpipeInfo = { hasUnpiped: false }; - // can't replace a non-object or non-array unless it's an @id - if(!_isArray(obj) && !_isObject(obj) && !isid) { - continue; - } + // if we're not piping anywhere, then do nothing. + if (state.pipesCount === 0) return 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); + // 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; - 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 (!dest) dest = state.pipes; -/** - * 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.')); - }); + // got a match. + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; + if (dest) dest.emit('unpipe', this, unpipeInfo); + return this; } - // get arguments - if(typeof options === 'function') { - callback = options; - options = {}; - } - options = options || {}; + // slow case. multiple pipe destinations. - // 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; - } + if (!dest) { + // remove all. + var dests = state.pipes; + var len = state.pipesCount; + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; - 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); - }); + for (var i = 0; i < len; i++) { + dests[i].emit('unpipe', this, unpipeInfo); + }return this; } -}; -/** - * 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.')); - }); - } + // try to find the right one. + var index = indexOf(state.pipes, dest); + if (index === -1) return this; - // get arguments - if(typeof options === 'function') { - callback = options; - options = {}; - } - options = options || {}; + state.pipes.splice(index, 1); + state.pipesCount -= 1; + if (state.pipesCount === 1) state.pipes = state.pipes[0]; - // set default options - if(!('useRdfType' in options)) { - options.useRdfType = false; - } - if(!('useNativeTypes' in options)) { - options.useNativeTypes = false; - } + dest.emit('unpipe', this, unpipeInfo); - if(!('format' in options) && _isString(dataset)) { - // set default format to nquads - if(!('format' in options)) { - options.format = 'application/nquads'; - } - } + return this; +}; - 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; - }; - } +// 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); - 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); + 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) { + processNextTick(nReadingNextTick, this); + } else if (state.length) { + emitReadable(this); } - // parser is synchronous - fromRDF(dataset, options, callback); } + } - function fromRDF(dataset, options, callback) { - // convert from RDF - new Processor().fromRDF(dataset, options, callback); - } - }); + return res; }; +Readable.prototype.addListener = Readable.prototype.on; -/** - * 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 nReadingNextTick(self) { + debug('readable nexttick read 0'); + self.read(0); +} - // get arguments - if(typeof options === 'function') { - callback = options; - options = {}; +// 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); } - options = options || {}; + return this; +}; - // set default options - if(!('base' in options)) { - options.base = (typeof input === 'string') ? input : ''; - } - if(!('documentLoader' in options)) { - options.documentLoader = jsonld.loadDocument; +function resume(stream, state) { + if (!state.resumeScheduled) { + state.resumeScheduled = true; + processNextTick(resume_, stream, state); } +} - // 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})); - } +function resume_(stream, state) { + if (!state.reading) { + debug('resume read 0'); + stream.read(0); + } - 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); - }); -}; + state.resumeScheduled = false; + state.awaitDrain = 0; + stream.emit('resume'); + flow(stream); + if (state.flowing && !state.reading) stream.read(0); +} -/** - * **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.')); - }); +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; +}; - // get arguments - if(typeof options === 'function') { - callback = options; - options = {}; - } - options = options || {}; +function flow(stream) { + var state = stream._readableState; + debug('flow', state.flowing); + while (state.flowing && stream.read() !== null) {} +} - // set default options - if(!('base' in options)) { - options.base = (typeof input === 'string') ? input : ''; - } - if(!('documentLoader' in options)) { - options.documentLoader = jsonld.loadDocument; - } +// 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 state = this._readableState; + var paused = false; - // 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 self = this; + stream.on('end', function () { + debug('wrapped end'); + if (state.decoder && !state.ended) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) self.push(chunk); } - var nodeMap; - try { - nodeMap = new Processor().createNodeMap(_input, options); - } catch(ex) { - return callback(ex); - } + self.push(null); + }); - callback(null, nodeMap); + stream.on('data', function (chunk) { + debug('wrapped data'); + if (state.decoder) chunk = state.decoder.write(chunk); + + // don't skip over falsy values in objectMode + if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return; + + var ret = self.push(chunk); + if (!ret) { + paused = true; + stream.pause(); + } }); -}; -/** - * **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.')); - }); + // 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); + } } - // get arguments - if(typeof options === 'function') { - callback = options; - options = {}; - } else if(typeof ctx === 'function') { - callback = ctx; - ctx = null; - options = {}; + // proxy certain important events. + for (var n = 0; n < kProxyEvents.length; n++) { + stream.on(kProxyEvents[n], self.emit.bind(self, kProxyEvents[n])); } - options = options || {}; - // 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]; + // when we try to consume some more bytes, simply unpause the + // underlying stream. + self._read = function (n) { + debug('wrapped _read', n); + if (paused) { + paused = false; + stream.resume(); } - jsonld.expand(docs[i], opts, expandComplete); - } + }; - 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); - } - } + return self; +}; - function merge(expanded) { - var mergeNodes = true; - if('mergeNodes' in options) { - mergeNodes = options.mergeNodes; - } +// exposed for testing purposes only. +Readable._fromList = fromList; - var issuer = options.namer || options.issuer || new IdentifierIssuer('_:b'); - var graphs = {'@default': {}}; +// 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 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 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); + } - // 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); + return ret; +} - 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]; - } - } - } - } - } +// 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; +} - // add all non-default graphs to default graph - defaultGraph = _mergeNodeMaps(graphs); - } catch(ex) { - return callback(ex); +// 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; +} - // 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); +// 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 merged output.', - 'jsonld.MergeError', {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'); -/** - * 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); -}; + if (!state.endEmitted) { + state.ended = true; + processNextTick(endReadableNT, state, stream); + } +} -/** - * 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); -}; +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'); + } +} -/** - * 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}); +function forEach(xs, f) { + for (var i = 0, l = xs.length; i < l; i++) { + f(xs[i], i); } - return jsonld.promisify(function(callback) { - callback(err); - }); -}; +} -/** - * 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); +function indexOf(xs, x) { + for (var i = 0, l = xs.length; i < l; i++) { + if (xs[i] === x) return i; } -}; + return -1; +} +}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -/* Promises API */ +},{"./_stream_duplex":18,"./internal/streams/BufferList":23,"./internal/streams/destroy":24,"./internal/streams/stream":25,"_process":12,"core-util-is":26,"events":8,"inherits":10,"isarray":27,"process-nextick-args":28,"safe-buffer":29,"string_decoder/":30,"util":3}],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. -/** - * 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; +// 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. - // 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 = {}; - } +'use strict'; - // 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 +module.exports = Transform; - 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))); +var Duplex = _dereq_('./_stream_duplex'); + +/**/ +var util = _dereq_('core-util-is'); +util.inherits = _dereq_('inherits'); +/**/ + +util.inherits(Transform, Duplex); + +function TransformState(stream) { + this.afterTransform = function (er, data) { + return afterTransform(stream, er, data); }; - 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))); - }; - } + this.needTransform = false; + this.transforming = false; + this.writecb = null; + this.writechunk = null; + this.writeencoding = null; +} - 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; - } - } +function afterTransform(stream, er, data) { + var ts = stream._transformState; + ts.transforming = false; - return api; -}; + var cb = ts.writecb; -/** - * 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.'); - } + if (!cb) { + return stream.emit('error', new Error('write callback called multiple times')); } - 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); - } - })); - }); -}; -// extend jsonld.promises w/jsonld.js methods -jsonld.promises({api: jsonld.promises}); + ts.writechunk = null; + ts.writecb = null; -/* WebIDL API */ + if (data !== null && data !== undefined) stream.push(data); -function JsonLdProcessor() {} -JsonLdProcessor.prototype = jsonld.promises({version: 'json-ld-1.0'}); -JsonLdProcessor.prototype.toString = function() { - if(this instanceof JsonLdProcessor) { - return '[object JsonLdProcessor]'; + cb(er); + + var rs = stream._readableState; + rs.reading = false; + if (rs.needReadable || rs.length < rs.highWaterMark) { + stream._read(rs.highWaterMark); } - return '[object JsonLdProcessorPrototype]'; -}; -jsonld.JsonLdProcessor = JsonLdProcessor; +} -// 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; - } -} +function Transform(options) { + if (!(this instanceof Transform)) return new Transform(options); -if(canDefineProperty) { - Object.defineProperty(JsonLdProcessor, 'prototype', { - writable: false, - enumerable: false - }); - Object.defineProperty(JsonLdProcessor.prototype, 'constructor', { - writable: true, - enumerable: false, - configurable: true, - value: JsonLdProcessor - }); -} + Duplex.call(this, options); -// 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; - } -} + this._transformState = new TransformState(this); -/* Utility API */ + var stream = this; -// define setImmediate and nextTick -//// nextTick implementation with browser-compatible fallback //// -// from https://github.com/caolan/async/blob/master/lib/async.js + // start out asking for a readable event once data is transformed. + this._readableState.needReadable = true; -// capture the global reference to guard against fakeTimer mocks -var _setImmediate = typeof setImmediate === 'function' && setImmediate; + // 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; -var _delay = _setImmediate ? function(fn) { - // not a direct alias (for IE10 compatibility) - _setImmediate(fn); -} : function(fn) { - setTimeout(fn, 0); -}; + if (options) { + if (typeof options.transform === 'function') this._transform = options.transform; -if(typeof process === 'object' && typeof process.nextTick === 'function') { - jsonld.nextTick = process.nextTick; -} else { - jsonld.nextTick = _delay; + if (typeof options.flush === 'function') this._flush = options.flush; + } + + // When the writable side finishes, then flush out anything remaining. + this.once('prefinish', function () { + if (typeof this._flush === 'function') this._flush(function (er, data) { + done(stream, er, data); + });else done(stream); + }); } -jsonld.setImmediate = _setImmediate ? _delay : jsonld.nextTick; -/** - * 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; +Transform.prototype.push = function (chunk, encoding) { + this._transformState.needTransform = false; + return Duplex.prototype.push.call(this, chunk, encoding); }; -/** - * 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); +// 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'); }; -jsonld.RequestQueue.prototype.add = function(url, callback) { - var self = this; - // callback must be given if not using promises - if(!callback && !self._usePromise) { - throw new Error('callback must be specified.'); +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); } +}; - // 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); - }); - }); - } +// 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; - // callback-based API - if(url in self._requests) { - self._requests[url].push(callback); + if (ts.writechunk !== null && ts.writecb && !ts.transforming) { + ts.transforming = true; + this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); } 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); - } - }); + // 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; } }; -/** - * 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; - } - 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)}; -}; +Transform.prototype._destroy = function (err, cb) { + var _this = this; -/** - * 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; -}; -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); + Duplex.prototype._destroy.call(this, err, function (err2) { + cb(err2); + _this.emit('close'); + }); }; -/** - * Default JSON-LD cache. - */ -jsonld.cache = { - activeCtx: new jsonld.ActiveContextCache() -}; +function done(stream, er, data) { + if (er) return stream.emit('error', er); -/** - * Document loaders. - */ -jsonld.documentLoaders = {}; + if (data !== null && data !== undefined) stream.push(data); -/** - * 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 there's nothing in the write buffer, then that means + // that nothing more will ever be provided + var ws = stream._writableState; + var ts = stream._transformState; - // 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); + if (ws.length) throw new Error('Calling transform done when ws.length != 0'); - 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}; + if (ts.transforming) throw new Error('Calling transform done when still transforming'); - // 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; - } - } + return stream.push(null); +} +},{"./_stream_duplex":18,"core-util-is":26,"inherits":10}],22:[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. - 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}); - } - }); - } -}; +// A bit simpler than readable streams. +// Implement an async ._write(chunk, encoding, cb), and it'll handle all +// the drain event emission and buffering. -/** - * 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(); +'use strict'; - 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); - }); +/**/ - 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); +var processNextTick = _dereq_('process-nextick-args'); +/**/ - function handleResponse(err, res, body) { - doc = {contextUrl: null, documentUrl: url, document: body || null}; +module.exports = Writable; - // 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); - } +/* */ +function WriteReq(chunk, encoding, cb) { + this.chunk = chunk; + this.encoding = encoding; + this.callback = cb; + this.next = null; +} - // 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); - } - } -}; - -/** - * 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(); - - // 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); +// 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; - 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}); - } + this.next = null; + this.entry = null; + this.finish = function () { + onCorkedFinish(_this, state); + }; +} +/* */ - var doc = {contextUrl: null, documentUrl: url, document: req.response}; +/**/ +var asyncWrite = !process.browser && ['v0.10', 'v0.9.'].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : processNextTick; +/**/ - // 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; - } - } +/**/ +var Duplex; +/**/ - 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(); - } -}; +Writable.WritableState = WritableState; -/** - * 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}); - } +/**/ +var util = _dereq_('core-util-is'); +util.inherits = _dereq_('inherits'); +/**/ - // set document loader - jsonld.documentLoader = jsonld.documentLoaders[type].apply( - jsonld, Array.prototype.slice.call(arguments, 1)); +/**/ +var internalUtil = { + deprecate: _dereq_('util-deprecate') }; +/**/ -/** - * 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]; +/**/ +var Stream = _dereq_('./internal/streams/stream'); +/**/ - // set default options - if(!('base' in options)) { - options.base = ''; - } - if(!('documentLoader' in options)) { - options.documentLoader = jsonld.loadDocument; - } +/**/ +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; +} +/**/ - // return initial context early for null context - if(localCtx === null) { - return callback(null, _getInitialContext(options)); - } +var destroyImpl = _dereq_('./internal/streams/destroy'); - // 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); - }); -}; +util.inherits(Writable, Stream); -/** - * 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; -}; +function nop() {} -/** - * 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); - } - } - return rval; -}; +function WritableState(options, stream) { + Duplex = Duplex || _dereq_('./_stream_duplex'); -/** - * 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; - } - 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)); + // object stream flag to indicate whether or not this stream + // contains buffers or objects. + this.objectMode = !!options.objectMode; - // make property an array if value not present or always an array - if(!_isArray(subject[property]) && - (!hasValue || options.propertyIsArray)) { - subject[property] = [subject[property]]; - } + if (stream instanceof Duplex) this.objectMode = this.objectMode || !!options.writableObjectMode; - // add new value - if(!hasValue) { - subject[property].push(value); - } - } else { - // add new value as set or single value - subject[property] = options.propertyIsArray ? [value] : value; - } -}; + // 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 defaultHwm = this.objectMode ? 16 : 16 * 1024; + this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm; -/** - * 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; -}; + // cast to ints. + this.highWaterMark = Math.floor(this.highWaterMark); -/** - * Removes a property from a subject. - * - * @param subject the subject. - * @param property the property. - */ -jsonld.removeProperty = function(subject, property) { - delete subject[property]; -}; + // if _final has been called + this.finalCalled = false; -/** - * 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; - } + // 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; - // filter out value - var values = jsonld.getValues(subject, property).filter(function(e) { - return !jsonld.compareValues(e, value); - }); + // has it been destroyed + this.destroyed = false; - if(values.length === 0) { - jsonld.removeProperty(subject, property); - } else if(values.length === 1 && !options.propertyIsArray) { - subject[property] = values[0]; - } else { - subject[property] = values; - } -}; + // 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; -/** - * 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; - } + // 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'; - // 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; - } + // 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; - // 3. equal @ids - if(_isObject(v1) && ('@id' in v1) && _isObject(v2) && ('@id' in v2)) { - return v1['@id'] === v2['@id']; - } + // a flag to see when we're in the middle of a write. + this.writing = false; - return false; -}; + // when true all writes will be buffered until .uncork() call + this.corked = 0; -/** - * 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; + // 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; - // return null for invalid key - if(key === null) { - return rval; - } + // 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; - // get default language - if(type === '@language' && (type in ctx)) { - rval = ctx[type]; - } + // the callback that's passed to _write(chunk,cb) + this.onwrite = function (er) { + onwrite(stream, er); + }; - // get specific entry information - if(ctx.mappings[key]) { - var entry = ctx.mappings[key]; + // the callback that the user supplies to write(chunk,encoding,cb) + this.writecb = null; - if(_isUndefined(type)) { - // return whole entry - rval = entry; - } else if(type in entry) { - // return entry value for type - rval = entry[type]; - } - } + // the amount that is being written when _write is called. + this.writelen = 0; - return rval; -}; + this.bufferedRequest = null; + this.lastBufferedRequest = null; -/** Registered RDF dataset parsers hashed by content-type. */ -var _rdfParsers = {}; - -/** - * 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; -}; - -/** - * Unregisters an RDF dataset parser by content-type. - * - * @param contentType the content-type for the parser. - */ -jsonld.unregisterRDFParser = function(contentType) { - delete _rdfParsers[contentType]; -}; + // number of pending user-supplied write callbacks + // this must be 0 before 'finish' can be emitted + this.pendingcb = 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 - }; - } -} + // emit prefinish if the only thing we're waiting for is _write cbs + // This is relevant for synchronous Transform streams + this.prefinished = false; -// 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'; + // True if the error was already emitted and should not be thrown again + this.errorEmitted = false; -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'; + // count buffered requests + this.bufferedRequestCount = 0; -var LINK_HEADER_REL = 'http://www.w3.org/ns/json-ld#context'; -var MAX_CONTEXT_URLS = 10; + // allocate the first CorkedRequest, there is always + // one allocated and free to use, and we maintain at most two + this.corkedRequestsFree = new CorkedRequest(this); +} -/** - * 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; +WritableState.prototype.getBuffer = function getBuffer() { + var current = this.bufferedRequest; + var out = []; + while (current) { + out.push(current); + current = current.next; } - this.name = type || 'jsonld.Error'; - this.message = msg || 'An unspecified JSON-LD error occurred.'; - this.details = details || {}; + return out; }; -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; - } +(function () { + try { + Object.defineProperty(WritableState.prototype, 'buffer', { + get: internalUtil.deprecate(function () { + return this.getBuffer(); + }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') + }); + } catch (_) {} +})(); - // 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; - } - } - } +// 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; - // 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; + return object && object._writableState instanceof WritableState; } + }); +} else { + realHasInstance = function (object) { + return object instanceof this; + }; +} - // FIXME: avoid misuse of active property as an expanded property? - var insideReverse = (activeProperty === '@reverse'); - - var rval = {}; - - 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}); - } +function Writable(options) { + Duplex = Duplex || _dereq_('./_stream_duplex'); - // 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]; + // Writable ctor is applied to Duplexes, too. + // `realHasInstance` is necessary because using plain `instanceof` + // would return false, as no `_writableState` property is attached. - // compact @id and @type(s) - if(expandedProperty === '@id' || expandedProperty === '@type') { - var compactedValue; + // 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); + } - // 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})); - } - } + this._writableState = new WritableState(options, this); - // 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; - } + // legacy. + this.writable = true; - // handle @reverse - if(expandedProperty === '@reverse') { - // recursively compact expanded value - var compactedValue = this.compact( - activeCtx, '@reverse', expandedValue, options); + if (options) { + if (typeof options.write === 'function') this._write = options.write; - // 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]; - } - } + if (typeof options.writev === 'function') this._writev = options.writev; - if(Object.keys(compactedValue).length > 0) { - // use keyword alias and add value - var alias = _compactIri(activeCtx, expandedProperty); - jsonld.addValue(rval, alias, compactedValue); - } + if (typeof options.destroy === 'function') this._destroy = options.destroy; - continue; - } + if (typeof options.final === 'function') this._final = options.final; + } - // handle @index property - if(expandedProperty === '@index') { - // drop @index if inside an @index container - var container = jsonld.getContextValue( - activeCtx, activeProperty, '@container'); - if(container === '@index') { - continue; - } + Stream.call(this); +} - // use keyword alias and add value - var alias = _compactIri(activeCtx, expandedProperty); - jsonld.addValue(rval, alias, expandedValue); - continue; - } +// Otherwise people can pipe Writable streams, which is just wrong. +Writable.prototype.pipe = function () { + this.emit('error', new Error('Cannot pipe, not readable')); +}; - // 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; - } +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); + processNextTick(cb, er); +} - // Note: expanded value must be an array due to expansion algorithm. +// 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; - // preserve empty arrays - if(expandedValue.length === 0) { - var itemActiveProperty = _compactIri( - activeCtx, expandedProperty, expandedValue, {vocab: true}, - insideReverse); - jsonld.addValue( - rval, itemActiveProperty, expandedValue, {propertyIsArray: true}); - } + 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'); + } + if (er) { + stream.emit('error', er); + processNextTick(cb, er); + valid = false; + } + return valid; +} - // recusively process array values - for(var vi = 0; vi < expandedValue.length; ++vi) { - var expandedItem = expandedValue[vi]; +Writable.prototype.write = function (chunk, encoding, cb) { + var state = this._writableState; + var ret = false; + var isBuf = _isUint8Array(chunk) && !state.objectMode; - // compact property and get container type - var itemActiveProperty = _compactIri( - activeCtx, expandedProperty, expandedItem, {vocab: true}, - insideReverse); - var container = jsonld.getContextValue( - activeCtx, itemActiveProperty, '@container'); + if (isBuf && !Buffer.isBuffer(chunk)) { + chunk = _uint8ArrayToBuffer(chunk); + } - // get @list value if appropriate - var isList = _isList(expandedItem); - var list = null; - if(isList) { - list = expandedItem['@list']; - } + if (typeof encoding === 'function') { + cb = encoding; + encoding = null; + } - // recursively compact expanded item - var compactedItem = this.compact( - activeCtx, itemActiveProperty, isList ? list : expandedItem, options); + if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; - // handle @list - if(isList) { - // ensure @list value is an array - if(!_isArray(compactedItem)) { - compactedItem = [compactedItem]; - } + if (typeof cb !== 'function') cb = nop; - if(container !== '@list') { - // wrap using @list alias - var wrapper = {}; - wrapper[_compactIri(activeCtx, '@list')] = compactedItem; - compactedItem = wrapper; + if (state.ended) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { + state.pendingcb++; + ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); + } - // 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'}); - } - } + return ret; +}; - // 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 = {}; - } +Writable.prototype.cork = function () { + var state = this._writableState; - // if container is a language map, simplify compacted value to - // a simple string - if(container === '@language' && _isValue(compactedItem)) { - compactedItem = compactedItem['@value']; - } + state.corked++; +}; - // 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'); +Writable.prototype.uncork = function () { + var state = this._writableState; - // add compact value - jsonld.addValue( - rval, itemActiveProperty, compactedItem, - {propertyIsArray: isArray}); - } - } - } + if (state.corked) { + state.corked--; - return rval; + if (!state.writing && !state.corked && !state.finished && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); } - - // only primitives remain which are already compact - return element; }; -/** - * 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; +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; +}; - // nothing to expand - if(element === null || element === undefined) { - return null; +function decodeChunk(state, chunk, encoding) { + if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { + chunk = Buffer.from(chunk, encoding); } + return chunk; +} - 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; +// 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; } - - // expand element according to value expansion rules - return _expandValue(activeCtx, activeProperty, element); } + var len = state.objectMode ? 1 : chunk.length; - // 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); - } - } + state.length += len; + + var ret = state.length < state.highWaterMark; + // we must ensure that previous needDrain will not be reset to false. + if (!ret) state.needDrain = true; + + 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; } - return rval; + state.bufferedRequestCount += 1; + } else { + doWrite(stream, state, false, len, chunk, encoding, cb); } - // recursively expand object: + return ret; +} - // if element has a context, process it - if('@context' in element) { - activeCtx = self.processContext(activeCtx, element['@context'], options); - } +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; +} - // expand the active property - var expandedActiveProperty = _expandIri( - activeCtx, activeProperty, {vocab: true}); +function onwriteError(stream, state, sync, er, cb) { + --state.pendingcb; - 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; + if (sync) { + // defer the callback if we are being called synchronously + // to avoid piling up things on the stack + processNextTick(cb, er); + // this can emit finish, and it will always happen + // after error + processNextTick(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); + } +} - // skip @context - if(key === '@context') { - continue; - } +function onwriteStateUpdate(state) { + state.writing = false; + state.writecb = null; + state.length -= state.writelen; + state.writelen = 0; +} - // expand property - var expandedProperty = _expandIri(activeCtx, key, {vocab: true}); +function onwrite(stream, er) { + var state = stream._writableState; + var sync = state.sync; + var cb = state.writecb; - // drop non-absolute IRI keys that aren't keywords - if(expandedProperty === null || - !(_isAbsoluteIri(expandedProperty) || _isKeyword(expandedProperty))) { - continue; - } + onwriteStateUpdate(state); - 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}); - } - } + 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); - // 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}); - } + if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { + clearBuffer(stream, state); } - if(expandedProperty === '@type') { - _validateTypeValue(value); + if (sync) { + /**/ + asyncWrite(afterWrite, stream, state, finished, cb); + /**/ + } else { + afterWrite(stream, state, finished, cb); } + } +} - // @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}); - } +function afterWrite(stream, state, finished, cb) { + if (!finished) onwriteDrain(stream, state); + state.pendingcb--; + cb(); + finishMaybe(stream, state); +} - // @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}); - } +// 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'); + } +} - // @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(); - } +// if there's something in the buffer waiting, then process it +function clearBuffer(stream, state) { + state.bufferProcessing = true; + var entry = state.bufferedRequest; - // @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 (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; } + buffer.allBuffers = allBuffers; - // @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}); - } + doWrite(stream, state, true, state.length, buffer, '', holder.finish); - expandedValue = self.expand(activeCtx, '@reverse', value, 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 { + // 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; - // properties double-reversed - if('@reverse' in expandedValue) { - for(var property in expandedValue['@reverse']) { - jsonld.addValue( - rval, property, expandedValue['@reverse'][property], - {propertyIsArray: true}); - } + doWrite(stream, state, false, len, chunk, encoding, cb); + entry = entry.next; + // 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; } + } - // 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}); - } - } + if (entry === null) state.lastBufferedRequest = null; + } - continue; - } + state.bufferedRequestCount = 0; + state.bufferedRequest = entry; + state.bufferProcessing = false; +} - var container = jsonld.getContextValue(activeCtx, key, '@container'); +Writable.prototype._write = function (chunk, encoding, cb) { + cb(new Error('_write() is not implemented')); +}; - 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); - } - } +Writable.prototype._writev = null; - // drop null values if property is not @value - if(expandedValue === null && expandedProperty !== '@value') { - continue; - } +Writable.prototype.end = function (chunk, encoding, cb) { + var state = this._writableState; - // 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}; - } + if (typeof chunk === 'function') { + cb = chunk; + chunk = null; + encoding = null; + } else if (typeof encoding === 'function') { + cb = encoding; + encoding = null; + } - // 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; - } + if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); - // 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}); + // .end() fully uncorks + if (state.corked) { + state.corked = 1; + this.uncork(); } - // get property count on expanded output - keys = Object.keys(rval); - var count = keys.length; + // ignore unnecessary end() calls. + if (!state.ending && !state.finished) endWritable(this, state, cb); +}; - 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}); +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); } - // optimize away @set - if('@set' in rval) { - rval = rval['@set']; - keys = Object.keys(rval); - count = keys.length; + 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; + processNextTick(callFinal, stream, state); + } else { + state.prefinished = true; + stream.emit('prefinish'); } - } 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; +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; +} - 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); - - // add all non-default graphs to default graph - return _mergeNodeMaps(graphs); -}; +function endWritable(stream, state, cb) { + state.ending = true; + finishMaybe(stream, state); + if (cb) { + if (state.finished) processNextTick(cb);else stream.once('finish', cb); + } + state.ended = true; + stream.writable = false; +} -/** - * 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); +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; + } +} - // 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); +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 flattened; +}); + +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 : {}) -/** - * 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: {} - }; +},{"./_stream_duplex":18,"./internal/streams/destroy":24,"./internal/streams/stream":25,"_process":12,"core-util-is":26,"inherits":10,"process-nextick-args":28,"safe-buffer":29,"util-deprecate":31}],23:[function(_dereq_,module,exports){ +'use strict'; - // 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']; +/**/ - // frame the subjects - var framed = []; - _frame(state, Object.keys(state.subjects).sort(), frame, framed, null); - return framed; -}; +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } -/** - * 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(options.algorithm === 'URGNA2012') { - return new URGNA2012(options).main(dataset, callback); - } - callback(new Error( - 'Invalid RDF Dataset Normalization algorithm: ' + options.algorithm)); -}; +var Buffer = _dereq_('safe-buffer').Buffer; +/**/ -/** - * 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 = {}; +function copyBuffer(src, target, offset) { + src.copy(target, offset); +} - 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]; +module.exports = function () { + function BufferList() { + _classCallCheck(this, BufferList); - // get subject, predicate, object - var s = triple.subject.value; - var p = triple.predicate.value; - var o = triple.object; + this.head = null; + this.tail = null; + this.length = 0; + } - if(!(s in nodeMap)) { - nodeMap[s] = {'@id': s}; - } - var node = nodeMap[s]; + 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; + }; - var objectIsId = (o.type === 'IRI' || o.type === 'blank node'); - if(objectIsId && !(o.value in nodeMap)) { - nodeMap[o.value] = {'@id': o.value}; - } + 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; + }; - if(p === RDF_TYPE && !options.useRdfType && objectIsId) { - jsonld.addValue(node, '@type', o.value, {propertyIsArray: true}); - continue; - } + 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; + }; - var value = _RDFToObject(o, options.useNativeTypes); - jsonld.addValue(node, p, value, {propertyIsArray: true}); + BufferList.prototype.clear = function clear() { + this.head = this.tail = null; + this.length = 0; + }; - // 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 - }; - } - } + 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; + }; + + 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; + }; - // convert linked lists to @list arrays - for(var name in graphMap) { - var graphObject = graphMap[name]; + return BufferList; +}(); +},{"safe-buffer":29}],24:[function(_dereq_,module,exports){ +'use strict'; - // no @lists to be converted, continue - if(!(RDF_NIL in graphObject)) { - continue; - } +/**/ - // 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 = []; +var processNextTick = _dereq_('process-nextick-args'); +/**/ - // 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']); +// undocumented cb() API, needed for core, not for public API +function destroy(err, cb) { + var _this = this; - // 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; + var readableDestroyed = this._readableState && this._readableState.destroyed; + var writableDestroyed = this._writableState && this._writableState.destroyed; - // if node is not a blank node, then list head found - if(node['@id'].indexOf('_:') !== 0) { - break; - } - } + if (readableDestroyed || writableDestroyed) { + if (cb) { + cb(err); + } else if (err && (!this._writableState || !this._writableState.errorEmitted)) { + processNextTick(emitErrorNT, this, err); + } + return; + } - // 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; - } + // we set destroyed to true before firing error callbacks in order + // to make it re-entrance safe in case destroy() is called within callbacks - // preserve list head - head = graphObject[head['@id']][RDF_REST][0]; - list.pop(); - listNodes.pop(); - } + if (this._readableState) { + this._readableState.destroyed = true; + } - // transform list into @list object - delete head['@id']; - head['@list'] = list.reverse(); - for(var j = 0; j < listNodes.length; ++j) { - delete graphObject[listNodes[j]]; + // if this is a duplex stream mark the writable part as destroyed as well + if (this._writableState) { + this._writableState.destroyed = true; + } + + this._destroy(err || null, function (err) { + if (!cb && err) { + processNextTick(emitErrorNT, _this, err); + if (_this._writableState) { + _this._writableState.errorEmitted = true; } + } else if (cb) { + cb(err); } + }); +} - delete nil.usages; +function undestroy() { + if (this._readableState) { + this._readableState.destroyed = false; + this._readableState.reading = false; + this._readableState.ended = false; + this._readableState.endEmitted = false; } - 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); - } + if (this._writableState) { + this._writableState.destroyed = false; + this._writableState.ended = false; + this._writableState.ending = false; + this._writableState.finished = false; + this._writableState.errorEmitted = false; } +} - callback(null, result); +function emitErrorNT(self, err) { + self.emit('error', err); +} + +module.exports = { + destroy: destroy, + undestroy: undestroy }; +},{"process-nextick-args":28}],25:[function(_dereq_,module,exports){ +module.exports = _dereq_('events').EventEmitter; -/** - * 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); +},{"events":8}],26:[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. - 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; -}; +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. -/** - * 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']; +function isArray(arg) { + if (Array.isArray) { + return Array.isArray(arg); } - var ctxs = _isArray(localCtx) ? localCtx : [localCtx]; + return objectToString(arg) === '[object Array]'; +} +exports.isArray = isArray; - // no contexts in array, clone existing context - if(ctxs.length === 0) { - return activeCtx.clone(); - } +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; - // 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]; +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; - // reset to initial context - if(ctx === null) { - rval = activeCtx = _getInitialContext(options); - continue; - } +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; - // dereference @context key if present - if(_isObject(ctx) && '@context' in ctx) { - ctx = ctx['@context']; - } +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; - // 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 isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; - // get context from cache if available - if(jsonld.cache.activeCtx) { - var cached = jsonld.cache.activeCtx.get(activeCtx, ctx); - if(cached) { - rval = activeCtx = cached; - continue; - } - } +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; - // update active context and clone new one before updating - activeCtx = rval; - rval = rval.clone(); +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; - // define context mappings for keys in local context - var defined = {}; +function isRegExp(re) { + return objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; - // handle @base - if('@base' in ctx) { - var base = ctx['@base']; +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; - // 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}); - } +function isDate(d) { + return objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; - if(base !== null) { - base = jsonld.url.parse(base || ''); - } - rval['@base'] = base; - defined['@base'] = true; - } +function isError(e) { + return (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; - // 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; - } +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; - // 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 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; - // process all other keys - for(var key in ctx) { - _createTermDefinition(rval, ctx, key, defined); - } +exports.isBuffer = Buffer.isBuffer; - // cache result - if(jsonld.cache.activeCtx) { - jsonld.cache.activeCtx.set(activeCtx, ctx, rval); - } - } +function objectToString(o) { + return Object.prototype.toString.call(o); +} - return rval; -}; +}).call(this,{"isBuffer":_dereq_("../../../../insert-module-globals/node_modules/is-buffer/index.js")}) -/** - * 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() - }); +},{"../../../../insert-module-globals/node_modules/is-buffer/index.js":11}],27:[function(_dereq_,module,exports){ +arguments[4][7][0].apply(exports,arguments) +},{"dup":7}],28:[function(_dereq_,module,exports){ +(function (process){ +'use strict'; + +if (!process.version || + process.version.indexOf('v0.') === 0 || + process.version.indexOf('v1.') === 0 && process.version.indexOf('v1.8.') !== 0) { + module.exports = nextTick; +} else { + module.exports = process.nextTick; +} + +function nextTick(fn, arg1, arg2, arg3) { + if (typeof fn !== 'function') { + throw new TypeError('"callback" argument must be a function'); + } + var len = arguments.length; + var args, i; + switch (len) { + case 0: + case 1: + return process.nextTick(fn); + case 2: + return process.nextTick(function afterTickOne() { + fn.call(null, arg1); + }); + case 3: + return process.nextTick(function afterTickTwo() { + fn.call(null, arg1, arg2); + }); + case 4: + return process.nextTick(function afterTickThree() { + fn.call(null, arg1, arg2, arg3); + }); + default: + args = new Array(len - 1); + i = 0; + while (i < args.length) { + args[i++] = arguments[i]; } + return process.nextTick(function afterTick() { + fn.apply(null, args); + }); } - return rval; } -/** - * 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']); - } +}).call(this,_dereq_('_process')) - // 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]); - } - } +},{"_process":12}],29:[function(_dereq_,module,exports){ +/* eslint-disable node/no-deprecated-api */ +var buffer = _dereq_('buffer') +var Buffer = buffer.Buffer + +// 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 +} - return element; +function SafeBuffer (arg, encodingOrOffset, length) { + return Buffer(arg, encodingOrOffset, length) } -/** - * 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; - } +// Copy static methods from Buffer +copyProps(Buffer, SafeBuffer) - // 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}); +SafeBuffer.from = function (arg, encodingOrOffset, length) { + if (typeof arg === 'number') { + throw new TypeError('Argument must not be a number') } + return Buffer(arg, encodingOrOffset, length) +} - // get type definition from context - var type = jsonld.getContextValue(activeCtx, activeProperty, '@type'); - - // do @id expansion (automatic for @graph) - if(type === '@id' || (expandedProperty === '@graph' && _isString(value))) { - return {'@id': _expandIri(activeCtx, value, {base: true})}; +SafeBuffer.alloc = function (size, fill, encoding) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') } - // do @id expansion w/vocab - if(type === '@vocab') { - return {'@id': _expandIri(activeCtx, value, {vocab: true, base: true})}; + var buf = Buffer(size) + if (fill !== undefined) { + if (typeof encoding === 'string') { + buf.fill(fill, encoding) + } else { + buf.fill(fill) + } + } else { + buf.fill(0) } + return buf +} - // do not expand keyword values - if(_isKeyword(expandedProperty)) { - return value; +SafeBuffer.allocUnsafe = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') } + return Buffer(size) +} - var rval = {}; - - 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(); +SafeBuffer.allocUnsafeSlow = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') } - rval['@value'] = value; - - return rval; + return buffer.SlowBuffer(size) } -/** - * 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]; - - // RDF subject - var subject = {}; - subject.type = (id.indexOf('_:') === 0) ? 'blank node' : 'IRI'; - subject.value = id; - - // skip relative IRI subjects - if(!_isAbsoluteIri(id)) { - continue; - } - - // RDF predicate - var predicate = {}; - predicate.type = (property.indexOf('_:') === 0) ? 'blank node' : 'IRI'; - predicate.value = property; +},{"buffer":4}],30:[function(_dereq_,module,exports){ +'use strict'; - // skip relative IRI predicates - if(!_isAbsoluteIri(property)) { - continue; - } +var Buffer = _dereq_('safe-buffer').Buffer; - // skip blank node predicates unless producing generalized RDF - if(predicate.type === 'blank node' && !options.produceGeneralizedRdf) { - continue; - } +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; + } +}; - // 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}); - } - } - } +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; } -/** - * 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. - */ -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}; - - for(var i = 0; i < list.length; ++i) { - var item = list[i]; - - var blankNode = {type: 'blank node', value: issuer.getId()}; - triples.push({subject: subject, predicate: predicate, object: blankNode}); - - subject = blankNode; - predicate = first; - var object = _objectToRDF(item); - - // skip null objects (they are relative IRIs) - if(object) { - triples.push({subject: subject, predicate: predicate, object: object}); - } +// 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); +} - predicate = rest; +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 || ''; +}; - triples.push({subject: subject, predicate: predicate, object: nil}); -} +StringDecoder.prototype.end = utf8End; -/** - * 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 = {}; +// Returns only complete characters in a Buffer +StringDecoder.prototype.text = utf8Text; - // convert value object to RDF - if(_isValue(item)) { - object.type = 'literal'; - var value = item['@value']; - var datatype = item['@type'] || null; +// 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; +}; - // 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); +// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a +// continuation byte. +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 -1; +} + +// 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) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 2; + return nb; + } + if (--j < i) 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; +} + +// 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 +// UTF-8 replacement characters ('\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'.repeat(p); + } + if (self.lastNeed > 1 && buf.length > 1) { + if ((buf[1] & 0xC0) !== 0x80) { + self.lastNeed = 1; + return '\ufffd'.repeat(p + 1); + } + if (self.lastNeed > 2 && buf.length > 2) { + if ((buf[2] & 0xC0) !== 0x80) { + self.lastNeed = 2; + return '\ufffd'.repeat(p + 2); } - // 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; } +} - // skip relative IRIs - if(object.type === 'IRI' && !_isAbsoluteIri(object.value)) { - return null; +// 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; +} - return object; +// 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); +} + +// For UTF-8, a replacement character for each buffered byte of a (partial) +// character needs to be added to the output. +function utf8End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) return r + '\ufffd'.repeat(this.lastTotal - this.lastNeed); + return r; +} + +// 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); +} + +// 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; +} + +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); +} + +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; +} + +// Pass bytes on through for single-byte encodings (e.g. ascii, latin1, hex) +function simpleWrite(buf) { + return buf.toString(this.encoding); +} + +function simpleEnd(buf) { + return buf && buf.length ? this.write(buf) : ''; } +},{"safe-buffer":29}],31:[function(_dereq_,module,exports){ +(function (global){ /** - * Converts an RDF triple object to a JSON-LD object. + * Module exports. + */ + +module.exports = deprecate; + +/** + * Mark that a method should not be used. + * Returns a modified function which warns once by default. * - * @param o the RDF triple object to convert. - * @param useNativeTypes true to output native types, false not to. + * If `localStorage.noDeprecation = true` is set, then it is a no-op. * - * @return the JSON-LD object. + * If `localStorage.throwDeprecation = true` is set, then deprecated functions + * will throw an Error when invoked. + * + * If `localStorage.traceDeprecation = true` is set, then deprecated functions + * will invoke `console.trace()` instead of `console.error()`. + * + * @param {Function} fn - the function to deprecate + * @param {String} msg - the string to print to the console when `fn` is invoked + * @returns {Function} a new "deprecated" version of `fn` + * @api public */ -function _RDFToObject(o, useNativeTypes) { - // convert IRI/blank node object to JSON-LD - if(o.type === 'IRI' || o.type === 'blank node') { - return {'@id': o.value}; - } - // convert literal to JSON-LD - var rval = {'@value': o.value}; +function deprecate (fn, msg) { + if (config('noDeprecation')) { + return fn; + } - // 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; + var warned = false; + function deprecated() { + if (!warned) { + if (config('throwDeprecation')) { + throw new Error(msg); + } else if (config('traceDeprecation')) { + console.trace(msg); + } else { + console.warn(msg); } - } else if(type !== XSD_STRING) { - rval['@type'] = type; + warned = true; } + return fn.apply(this, arguments); } - return rval; + return deprecated; } /** - * Compares two RDF triples for equality. - * - * @param t1 the first triple. - * @param t2 the second triple. + * Checks `localStorage` for boolean values for the given `name`. * - * @return true if the triples are the same, false if not. + * @param {String} name + * @returns {Boolean} + * @api private */ -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) { + +function config (name) { + // accessing global.localStorage can trigger a DOMException in sandboxed iframes + try { + if (!global.localStorage) return false; + } catch (_) { return false; } - return true; + var val = global.localStorage[name]; + if (null == val) return false; + return String(val).toLowerCase() === 'true'; } -/////////////////////////////// DEFINE URDNA2015 ////////////////////////////// +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -var URDNA2015 = (function() { +},{}],32:[function(_dereq_,module,exports){ +module.exports = _dereq_('./readable').PassThrough -var POSITIONS = {'subject': 's', 'object': 'o', 'name': 'g'}; +},{"./readable":33}],33:[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'); -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; - } -}; +},{"./lib/_stream_duplex.js":18,"./lib/_stream_passthrough.js":19,"./lib/_stream_readable.js":20,"./lib/_stream_transform.js":21,"./lib/_stream_writable.js":22}],34:[function(_dereq_,module,exports){ +module.exports = _dereq_('./readable').Transform -// do some work in a time slice, but in serial -Normalize.prototype.doWork = function(fn, callback) { - var schedule = this.schedule; +},{"./readable":33}],35:[function(_dereq_,module,exports){ +module.exports = _dereq_('./lib/_stream_writable.js'); - if(schedule.totalDepth >= schedule.MAX_TOTAL_DEPTH) { - return callback(new Error( - 'Maximum total call stack depth exceeded; normalization aborting.')); - } +},{"./lib/_stream_writable.js":22}],36:[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 work() { - if(schedule.depth === schedule.MAX_DEPTH) { - // stack too deep, run on next tick - schedule.depth = 0; - schedule.running = false; - return jsonld.nextTick(work); - } +module.exports = Stream; - // 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; - } +var EE = _dereq_('events').EventEmitter; +var inherits = _dereq_('inherits'); - // 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); - }); - } +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'); - // not enough time left in this slice, run after letting browser - // do some other things - schedule.depth = 0; - schedule.running = false; - jsonld.setImmediate(work); - })(); -}; +// Backwards-compat with node 0.4.x +Stream.Stream = Stream; -// 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; - }; - } - (function iterate(err, result) { - if(err) { - return callback(err); - } - if(iterator()) { - return self.doWork(function() { - fn(iterator.value, iterator.key, iterate); - }); - } - callback(); - })(); -}; -// asynchronous waterfall -Normalize.prototype.waterfall = function(fns, callback) { - var self = this; - self.forEach(fns, function(fn, idx, callback) { - self.doWork(fn, callback); - }, callback); -}; +// old-style streams. Note that the pipe method (the only relevant +// part of this class) is overridden in the Readable class. -// 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); - })(); -}; +function Stream() { + EE.call(this); +} -// 4.4) Normalization Algorithm -Normalize.prototype.main = function(dataset, callback) { - var self = this; - self.schedule.start = new Date().getTime(); - var result; +Stream.prototype.pipe = function(dest, options) { + var source = this; - // 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})); + function ondata(chunk) { + if (dest.writable) { + if (false === dest.write(chunk) && source.pause) { + source.pause(); + } } } - // 1) Create the normalization state. + source.on('data', ondata); - // Note: Optimize by generating non-normalized blank node map concurrently. - var nonNormalized = {}; - - 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); + function ondrain() { + if (source.readable && source.resume) { + source.resume(); + } + } - // 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. + dest.on('drain', ondrain); - // 4) Initialize simple, a boolean flag, to true. - var simple = true; + // 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); + } - // 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; + var didOnEnd = false; + function onend() { + if (didOnEnd) return; + didOnEnd = true; - // 5.2) Clear hash to blank nodes map. - self.hashToBlankNodes = {}; + dest.end(); + } - 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(); - } - // 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); + function onclose() { + if (didOnEnd) return; + didOnEnd = true; - // 5.4.3) Remove identifier from non-normalized identifiers. - delete nonNormalized[id]; + if (typeof dest.destroy === 'function') dest.destroy(); + } - // 5.4.4) Remove hash from the hash to blank nodes map. - delete self.hashToBlankNodes[hash]; + // 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. + } + } - // 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 = []; + source.on('error', onerror); + dest.on('error', onerror); - // 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 all the event listeners that were added. + function cleanup() { + source.removeListener('data', ondata); + dest.removeListener('drain', ondrain); - // 6.2.2) Create temporary issuer, an identifier issuer - // initialized with the prefix _:b. - var issuer = new IdentifierIssuer('_:b'); + source.removeListener('end', onend); + source.removeListener('close', onclose); - // 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); + source.removeListener('error', onerror); + dest.removeListener('error', onerror); - // 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. */ + source.removeListener('end', cleanup); + source.removeListener('close', cleanup); - // 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(); + dest.removeListener('close', cleanup); + } - // 8) Return the normalized dataset. - if(self.options.format === 'application/nquads') { - result = normalized.join(''); - return callback(); - } + source.on('end', cleanup); + source.on('close', cleanup); - result = _parseNQuads(normalized.join('')); - callback(); - } - ], callback); - } - ], function(err) { - callback(err, result); - }); -}; + dest.on('close', cleanup); -// 4.6) Hash First Degree Quads -Normalize.prototype.hashFirstDegreeQuads = function(id, callback) { - var self = this; + dest.emit('pipe', source); - // return cached hash - var info = self.blankNodeInfo[id]; - if('hash' in info) { - return callback(null, info.hash); - } + // Allow for unix-like usage: A.pipe(B).pipe(C) + return dest; +}; - // 1) Initialize nquads to an empty list. It will be used to store quads in - // N-Quads format. - var nquads = []; +},{"events":8,"inherits":10,"readable-stream/duplex.js":17,"readable-stream/passthrough.js":32,"readable-stream/readable.js":33,"readable-stream/transform.js":34,"readable-stream/writable.js":35}],37:[function(_dereq_,module,exports){ +var ClientRequest = _dereq_('./lib/request') +var extend = _dereq_('xtend') +var statusCodes = _dereq_('builtin-status-codes') +var url = _dereq_('url') - // 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; +var http = exports - // 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: +http.request = function (opts, cb) { + if (typeof opts === 'string') + opts = url.parse(opts) + else + opts = extend(opts) - // 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(); + var protocol = opts.protocol || '' + var host = opts.hostname || opts.host + var port = opts.port + var path = opts.path || '/' - // 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); - }); -}; + // Necessary for IPv6 addresses + if (host && host.indexOf(':') !== -1) + host = '[' + host + ']' -// 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; -}; + // 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 || {} -// 4.7) Hash Related Blank Node -Normalize.prototype.hashRelatedBlankNode = function( - related, quad, issuer, position, callback) { - var self = this; + // Also valid opts.auth, opts.mode - // 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); - } + var req = new ClientRequest(opts) + if (cb) + req.on('response', cb) + return req +} - // 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); +http.get = function get (opts, cb) { + var req = http.request(opts, cb) + req.end() + return req +} - // 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)); - } +http.Agent = function () {} +http.Agent.defaultMaxSockets = 4 - // 4) Append identifier to input. - md.update(id); +http.STATUS_CODES = statusCodes - // 5) Return the hash that results from passing input through the hash - // algorithm. - return callback(null, md.digest()); - }); -}; +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":39,"builtin-status-codes":41,"url":48,"xtend":52}],38:[function(_dereq_,module,exports){ +(function (global){ +exports.fetch = isFunction(global.fetch) && isFunction(global.ReadableByteStream) -// helper for getting a related predicate -Normalize.prototype.getRelatedPredicate = function(quad) { - return '<' + quad.predicate.value + '>'; -}; +exports.blobConstructor = false +try { + new Blob([new ArrayBuffer(1)]) + exports.blobConstructor = true +} catch (e) {} -// 4.8) Hash N-Degree Quads -Normalize.prototype.hashNDegreeQuads = function(id, issuer, callback) { - var self = this; +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') - // 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. +function checkTypeSupport (type) { + try { + xhr.responseType = type + return xhr.responseType === type + } catch (e) {} + return false +} - // 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); +// 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) - // 5.2) Create a string chosen path. - var chosenPath = ''; +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) - // 5.3) Create an unset chosen issuer variable. - var chosenIssuer; +function isFunction (value) { + return typeof value === 'function' +} - // 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(); +xhr = null // Help gc - // 5.4.1) Create a copy of issuer, issuer copy. - var issuerCopy = issuer.clone(); +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - // 5.4.2) Create a string path. - var path = ''; +},{}],39:[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') - // 5.4.3) Create a recursion list, to store blank node identifiers - // that must be recursively processed by this algorithm. - var recursionList = []; +var IncomingMessage = response.IncomingMessage +var rStates = response.readyStates - 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); - } - - // 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); - } - - // 5.4.5.2) Use the Issue Identifier algorithm, passing issuer - // copy and related and append the result to path. - path += issuerCopy.getId(related); - - // 5.4.5.3) Append <, the hash in result, and > to path. - path += '<' + result.hash + '>'; - - // 5.4.5.4) Set issuer copy to the identifier issuer in - // result. - issuerCopy = result.issuer; +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' + } +} - // 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); - } +var ClientRequest = module.exports = function (opts) { + var self = this + stream.Writable.call(self) - // 5.5) Append chosen path to data to hash. - md.update(chosenPath); + 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]) + }) - // 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}); - }); -}; + 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) -// helper for creating hash to related blank nodes map -Normalize.prototype.createHashToRelated = function(id, issuer, callback) { - var self = this; + self.on('finish', function () { + self._onFinish() + }) +} - // 1) Create a hash to related blank nodes map for storing hashes that - // identify related blank nodes. - var hashToRelated = {}; +inherits(ClientRequest, stream.Writable) - // 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; +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 - // 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); - }); -}; + self._headers[lowerName] = { + name: name, + value: value + } +} -// 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); - } -}; +ClientRequest.prototype.getHeader = function (name) { + var self = this + return self._headers[name.toLowerCase()].value +} -return Normalize; +ClientRequest.prototype.removeHeader = function (name) { + var self = this + delete self._headers[name.toLowerCase()] +} -})(); // end of define URDNA2015 +ClientRequest.prototype._onFinish = function () { + var self = this -/////////////////////////////// DEFINE URGNA2012 ////////////////////////////// + if (self._destroyed) + return + var opts = self._opts -var URGNA2012 = (function() { + 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() + } + } -var Normalize = function(options) { - URDNA2015.call(this, options); - this.name = 'URGNA2012'; -}; -Normalize.prototype = new URDNA2015(); + if (self._mode === 'fetch') { + var headers = keys(headersObj).map(function (name) { + return [headersObj[name].name, headersObj[name].value] + }) -// 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; -}; + 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 + } -// helper for getting a related predicate -Normalize.prototype.getRelatedPredicate = function(quad) { - return quad.predicate.value; -}; + // Can't set responseType on really old browsers + if ('responseType' in xhr) + xhr.responseType = self._mode.split(':')[0] -// helper for creating hash to related blank nodes map -Normalize.prototype.createHashToRelated = function(id, issuer, callback) { - var self = this; + if ('withCredentials' in xhr) + xhr.withCredentials = !!opts.withCredentials - // 1) Create a hash to related blank nodes map for storing hashes that - // identify related blank nodes. - var hashToRelated = {}; + if (self._mode === 'text' && 'overrideMimeType' in xhr) + xhr.overrideMimeType('text/plain; charset=x-user-defined') - // 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; + foreach(keys(headersObj), function (name) { + xhr.setRequestHeader(headersObj[name].name, headersObj[name].value) + }) - // 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); - }); -}; + 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() + } + } -return Normalize; + xhr.onerror = function () { + if (self._destroyed) + return + self.emit('error', new Error('XHR error')) + } -})(); // end of define URGNA2012 + try { + xhr.send(body) + } catch (err) { + process.nextTick(function () { + self.emit('error', err) + }) + return + } + } +} /** - * 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. + * 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 _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; - } +function statusValid (xhr) { + try { + return (xhr.status !== null) + } catch (e) { + return false + } +} - // add non-object to list - if(!_isObject(input)) { - if(list) { - list.push(input); - } - return; - } +ClientRequest.prototype._onXHRProgress = function () { + var self = this - // 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; - } + if (!statusValid(self._xhr) || self._destroyed) + return - // Note: At this point, input must be a subject. + if (!self._response) + self._connect() - // 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); - } - } - } + self._response._onXHRProgress() +} - // get name for subject - if(_isUndefined(name)) { - name = _isBlankNode(input) ? issuer.getId(input['@id']) : input['@id']; - } +ClientRequest.prototype._connect = function () { + var self = this - // add subject reference to list - if(list) { - list.push({'@id': name}); - } + if (self._destroyed) + return - // 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]; + self._response = new IncomingMessage(self._xhr, self._fetchResponse, self._mode) + self.emit('response', self._response) +} - // skip @id - if(property === '@id') { - continue; - } +ClientRequest.prototype._write = function (chunk, encoding, cb) { + var self = this - // 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; - } + self._body.push(chunk) + cb() +} - // 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; - } +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 +} - // 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; - } - - // iterate over objects - var objects = input[property]; +ClientRequest.prototype.end = function (data, encoding, cb) { + var self = this + if (typeof data === 'function') { + cb = data + data = undefined + } - // if property is a bnode, assign it a new id - if(property.indexOf('_:') === 0) { - property = issuer.getId(property); - } + stream.Writable.prototype.end.call(self, data, encoding, cb) +} - // 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]; +ClientRequest.prototype.flushHeaders = function () {} +ClientRequest.prototype.setTimeout = function () {} +ClientRequest.prototype.setNoDelay = function () {} +ClientRequest.prototype.setSocketKeepAlive = function () {} - if(property === '@type') { - // rename @type blank nodes - o = (o.indexOf('_:') === 0) ? issuer.getId(o) : o; - } +// 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' +] - // 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']; +}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer) - // 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}); - } - } - } -} +},{"./capability":38,"./response":40,"_process":12,"buffer":4,"foreach":42,"indexof":43,"inherits":10,"object-keys":44,"stream":36}],40:[function(_dereq_,module,exports){ +(function (process,global,Buffer){ +var capability = _dereq_('./capability') +var foreach = _dereq_('foreach') +var inherits = _dereq_('inherits') +var stream = _dereq_('stream') -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; +var rStates = exports.readyStates = { + UNSENT: 0, + OPENED: 1, + HEADERS_RECEIVED: 2, + LOADING: 3, + DONE: 4 } -/** - * 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]; +var IncomingMessage = exports.IncomingMessage = function (xhr, response, mode) { + var self = this + stream.Readable.call(self) - // 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') - }; + self._mode = mode + self.headers = {} + self.rawHeaders = [] + self.trailers = {} + self.rawTrailers = [] - // filter out subjects that match the frame - var matches = _filterSubjects(state, subjects, frame, flags); + // 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') + }) + }) - // 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]; + if (mode === 'fetch') { + self._fetchResponse = response - 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 + 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]) + } - // add existing linked subject - _addFrameOutput(parent, property, state.link[id]); - continue; - } + // 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() - /* 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 = {}; - } + } else { + self._xhr = xhr + self._pos = 0 - // start output for subject - var output = {}; - output['@id'] = id; - state.link[id] = output; + 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]) + } + }) - // 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; - } + 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 + } + } +} - // 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}; - } +inherits(IncomingMessage, stream.Readable) - // push matching subject onto stack to enable circular embed checks - state.subjectStack.push(subject); +IncomingMessage.prototype._read = function () {} - // iterate over subject properties - var props = Object.keys(subject).sort(); - for(var i = 0; i < props.length; i++) { - var prop = props[i]; +IncomingMessage.prototype._onXHRProgress = function () { + var self = this - // copy keywords to output - if(_isKeyword(prop)) { - output[prop] = _clone(subject[prop]); - continue; - } + var xhr = self._xhr - // explicit is on and property isn't in the frame, skip processing - if(flags.explicit && !(prop in frame)) { - continue; - } + 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 - // add objects - var objects = subject[prop]; - for(var oi = 0; oi < objects.length; ++oi) { - var o = objects[oi]; + 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 + } - // recurse into list - if(_isList(o)) { - // add empty list - var list = {'@list': []}; - _addFrameOutput(output, prop, list); + // The ms-stream case handles end separately in reader.onload() + if (self._xhr.readyState === rStates.DONE && self._mode !== 'ms-stream') { + self.push(null) + } +} - // 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; - } +}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer) - 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)); - } - } - } +},{"./capability":38,"_process":12,"buffer":4,"foreach":42,"inherits":10,"stream":36}],41:[function(_dereq_,module,exports){ +module.exports = { + "100": "Continue", + "101": "Switching Protocols", + "102": "Processing", + "200": "OK", + "201": "Created", + "202": "Accepted", + "203": "Non-Authoritative Information", + "204": "No Content", + "205": "Reset Content", + "206": "Partial Content", + "207": "Multi-Status", + "300": "Multiple Choices", + "301": "Moved Permanently", + "302": "Moved Temporarily", + "303": "See Other", + "304": "Not Modified", + "305": "Use Proxy", + "307": "Temporary Redirect", + "308": "Permanent Redirect", + "400": "Bad Request", + "401": "Unauthorized", + "402": "Payment Required", + "403": "Forbidden", + "404": "Not Found", + "405": "Method Not Allowed", + "406": "Not Acceptable", + "407": "Proxy Authentication Required", + "408": "Request Time-out", + "409": "Conflict", + "410": "Gone", + "411": "Length Required", + "412": "Precondition Failed", + "413": "Request Entity Too Large", + "414": "Request-URI Too Large", + "415": "Unsupported Media Type", + "416": "Requested Range Not Satisfiable", + "417": "Expectation Failed", + "418": "I'm a teapot", + "422": "Unprocessable Entity", + "423": "Locked", + "424": "Failed Dependency", + "425": "Unordered Collection", + "426": "Upgrade Required", + "428": "Precondition Required", + "429": "Too Many Requests", + "431": "Request Header Fields Too Large", + "500": "Internal Server Error", + "501": "Not Implemented", + "502": "Bad Gateway", + "503": "Service Unavailable", + "504": "Gateway Time-out", + "505": "HTTP Version Not Supported", + "506": "Variant Also Negotiates", + "507": "Insufficient Storage", + "509": "Bandwidth Limit Exceeded", + "510": "Not Extended", + "511": "Network Authentication Required" +} - // handle defaults - var props = Object.keys(frame).sort(); - for(var i = 0; i < props.length; ++i) { - var prop = props[i]; +},{}],42:[function(_dereq_,module,exports){ - // skip keywords - if(_isKeyword(prop)) { - continue; - } +var hasOwn = Object.prototype.hasOwnProperty; +var toString = Object.prototype.toString; - // 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']); +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); } - if(!_isArray(preserve)) { - preserve = [preserve]; + } else { + for (var k in obj) { + if (hasOwn.call(obj, k)) { + fn.call(ctx, obj[k], k, obj); + } } - output[prop] = [{'@preserve': preserve}]; - } } +}; - // add output to parent - _addFrameOutput(parent, property, output); - // pop matching subject from circular ref-checking stack - state.subjectStack.pop(); +},{}],43:[function(_dereq_,module,exports){ + +var indexOf = [].indexOf; + +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; +}; +},{}],44:[function(_dereq_,module,exports){ +'use strict'; -/** - * 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]]; - } - } - return [frame]; -} +// modified from https://github.com/es-shims/es5-shim +var has = Object.prototype.hasOwnProperty; +var toStr = Object.prototype.toString; +var slice = Array.prototype.slice; +var isArgs = _dereq_('./isArguments'); +var isEnumerable = Object.prototype.propertyIsEnumerable; +var hasDontEnumBug = !isEnumerable.call({ toString: null }, 'toString'); +var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype'); +var dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' +]; +var equalsConstructorPrototype = function (o) { + var ctor = o.constructor; + return ctor && ctor.prototype === o; +}; +var excludedKeys = { + $console: true, + $external: true, + $frame: true, + $frameElement: true, + $frames: true, + $innerHeight: true, + $innerWidth: true, + $outerHeight: true, + $outerWidth: true, + $pageXOffset: true, + $pageYOffset: true, + $parent: true, + $scrollLeft: true, + $scrollTop: true, + $scrollX: true, + $scrollY: true, + $self: true, + $webkitIndexedDB: true, + $webkitStorageInfo: true, + $window: true +}; +var hasAutomationEqualityBug = (function () { + /* global window */ + if (typeof window === 'undefined') { return false; } + for (var k in window) { + try { + if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') { + try { + equalsConstructorPrototype(window[k]); + } catch (e) { + return true; + } + } + } catch (e) { + return true; + } + } + return false; +}()); +var equalsConstructorPrototypeIfNotBuggy = function (o) { + /* global window */ + if (typeof window === 'undefined' || !hasAutomationEqualityBug) { + return equalsConstructorPrototype(o); + } + try { + return equalsConstructorPrototype(o); + } catch (e) { + return false; + } +}; -/** - * 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; - } - } - return false; -} +var keysShim = function keys(object) { + var isObject = object !== null && typeof object === 'object'; + var isFunction = toStr.call(object) === '[object Function]'; + var isArguments = isArgs(object); + var isString = isObject && toStr.call(object) === '[object String]'; + var theKeys = []; -/** - * 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; -} + if (!isObject && !isFunction && !isArguments) { + throw new TypeError('Object.keys called on a non-object'); + } -/** - * 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 skipProto = hasProtoEnumBug && isFunction; + if (isString && object.length > 0 && !has.call(object, 0)) { + for (var i = 0; i < object.length; ++i) { + theKeys.push(String(i)); + } + } -/** - * 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 (isArguments && object.length > 0) { + for (var j = 0; j < object.length; ++j) { + theKeys.push(String(j)); + } + } else { + for (var name in object) { + if (!(skipProto && name === 'prototype') && has.call(object, name)) { + theKeys.push(String(name)); + } + } + } + + if (hasDontEnumBug) { + var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object); + + for (var k = 0; k < dontEnums.length; ++k) { + if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) { + theKeys.push(dontEnums[k]); + } + } + } + return theKeys; +}; + +keysShim.shim = function shimObjectKeys() { + if (Object.keys) { + var keysWorksWithArguments = (function () { + // Safari 5.0 bug + return (Object.keys(arguments) || '').length === 2; + }(1, 2)); + if (!keysWorksWithArguments) { + var originalKeys = Object.keys; + Object.keys = function keys(object) { + if (isArgs(object)) { + return originalKeys(slice.call(object)); + } else { + return originalKeys(object); + } + }; + } + } else { + Object.keys = keysShim; + } + return Object.keys || keysShim; +}; + +module.exports = keysShim; + +},{"./isArguments":45}],45:[function(_dereq_,module,exports){ +'use strict'; + +var toStr = Object.prototype.toString; + +module.exports = function isArguments(value) { + var str = toStr.call(value); + var isArgs = str === '[object Arguments]'; + if (!isArgs) { + isArgs = str !== '[object Array]' && + value !== null && + typeof value === 'object' && + typeof value.length === 'number' && + value.length >= 0 && + toStr.call(value.callee) === '[object Function]'; + } + return isArgs; +}; + +},{}],46:[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. + +var Buffer = _dereq_('buffer').Buffer; + +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; + } + } + + +function assertEncoding(encoding) { + if (encoding && !isBufferEncoding(encoding)) { + throw new Error('Unknown encoding: ' + encoding); } - 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; - } - } - return false; +// 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; } - // 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; - } - wildcard = false; + // 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; +}; - // check @id for a specific @id value - if(key === '@id' && _isString(frame[key])) { - if(subject[key] !== frame[key]) { - return false; - } - matchesSome = true; - continue; - } + +// 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; + + // add the new bytes to the char buffer + buffer.copy(this.charBuffer, this.charReceived, 0, available); + this.charReceived += available; + + if (this.charReceived < this.charLength) { + // still not enough chars in this buffer? wait for more ... + return ''; } - wildcard = false; + // remove bytes belonging to the current character from the buffer + buffer = buffer.slice(available, buffer.length); - 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; + // get the character that was split + charStr = this.charBuffer.slice(0, this.charLength).toString(this.encoding); + + // 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; - // 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 there are no more bytes in this buffer, just emit our char + if (buffer.length === 0) { + return charStr; } + break; } - // return true if wildcard or subject matches some properties - return wildcard || matchesSome; -} + // determine and set charLength / charReceived + this.detectIncompleteChar(buffer); -/** - * 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; + 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; + } - // create reference to replace embed - var subject = {'@id': id}; + charStr += buffer.toString(this.encoding, 0, end); - // 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; - } - } - } else { - // replace subject with reference - var useArray = _isArray(parent[property]); - jsonld.removeValue(parent, property, subject, {propertyIsArray: useArray}); - jsonld.addValue(parent, property, subject, {propertyIsArray: useArray}); + 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); } - // 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); - } - } - }; - removeDependents(id); -} + // or just emit the charStr + return charStr; +}; -/** - * 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); - } -} +// 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; -/** - * 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; - } - return input['@preserve']; - } + // 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]; - // skip @values - if(_isValue(input)) { - return input; - } + // See http://en.wikipedia.org/wiki/UTF-8#Description - // recurse through @lists - if(_isList(input)) { - input['@list'] = _removePreserve(ctx, input['@list'], options); - return input; + // 110XXXXX + if (i == 1 && c >> 5 == 0x06) { + this.charLength = 2; + break; } - // 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]; - } - } else { - // prevent circular visitation - options.link[id] = [input]; - } + // 1110XXXX + if (i <= 2 && c >> 4 == 0x0E) { + this.charLength = 3; + break; } - // 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; + // 11110XXX + if (i <= 3 && c >> 3 == 0x1E) { + this.charLength = 4; + break; } } - return input; -} + this.charReceived = i; +}; -/** - * Compares two strings first based on length and then lexicographically. - * - * @param a the first string. - * @param b the second string. - * - * @return -1 if a < b, 1 if a > b, 0 if a == b. - */ -function _compareShortestLeast(a, b) { - if(a.length < b.length) { - return -1; - } - if(b.length < a.length) { - return 1; - } - if(a === b) { - return 0; - } - return (a < b) ? -1 : 1; -} +StringDecoder.prototype.end = function(buffer) { + var res = ''; + if (buffer && buffer.length) + res = this.write(buffer); -/** - * 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'; + if (this.charReceived) { + var cr = this.charReceived; + var buf = this.charBuffer; + var enc = this.encoding; + res += buf.slice(0, cr).toString(enc); } - // preferences for the value of @type or @language - var prefs = []; + return res; +}; - // 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']); - } - } else { - prefs.push(typeOrLanguageValue); - } - prefs.push('@none'); +function passThroughWrite(buffer) { + return buffer.toString(this.encoding); +} - 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 utf16DetectIncompleteChar(buffer) { + this.charReceived = buffer.length % 2; + this.charLength = this.charReceived ? 2 : 0; +} - 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; - } +function base64DetectIncompleteChar(buffer) { + this.charReceived = buffer.length % 3; + this.charLength = this.charReceived ? 3 : 0; +} - // select term - return typeOrLanguageValueMap[pref]; - } - } +},{"buffer":4}],47:[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; - return null; +// DOM APIs, for completeness + +exports.setTimeout = function() { + return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout); +}; +exports.setInterval = function() { + return new Timeout(apply.call(setInterval, window, arguments), clearInterval); +}; +exports.clearTimeout = +exports.clearInterval = function(timeout) { timeout.close(); }; + +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); +}; -/** - * 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; - } +// Does not start the time, just sets up the members needed. +exports.enroll = function(item, msecs) { + clearTimeout(item._idleTimeoutId); + item._idleTimeout = msecs; +}; - // default value and parent to null - if(_isUndefined(value)) { - value = null; - } - // default reverse to false - if(_isUndefined(reverse)) { - reverse = false; - } - relativeTo = relativeTo || {}; +exports.unenroll = function(item) { + clearTimeout(item._idleTimeoutId); + item._idleTimeout = -1; +}; - var inverseCtx = activeCtx.getInverse(); +exports._unrefActive = exports.active = function(item) { + clearTimeout(item._idleTimeoutId); - // 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; + var msecs = item._idleTimeout; + if (msecs >= 0) { + item._idleTimeoutId = setTimeout(function onTimeout() { + if (item._onTimeout) + item._onTimeout(); + }, msecs); } +}; - // use inverse context to pick a term if iri is relative to vocab - if(relativeTo.vocab && iri in inverseCtx) { - var defaultLanguage = activeCtx['@language'] || '@none'; - - // prefer @index if available in value - var containers = []; - if(_isObject(value) && '@index' in value) { - containers.push('@index'); - } +// 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); - // defaults for term selection based on type/language - var typeOrLanguage = '@language'; - var typeOrLanguageValue = '@null'; + immediateIds[id] = true; - 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; - } - } 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']; - } + 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 { - typeOrLanguage = '@type'; - typeOrLanguageValue = '@id'; + fn.call(null); } - containers.push('@set'); - } - - // do term selection - containers.push('@none'); - var term = _selectTerm( - activeCtx, iri, value, containers, typeOrLanguage, typeOrLanguageValue); - if(term !== null) { - return term; + // Prevent ids from leaking + exports.clearImmediate(id); } - } + }); - // 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; - } - } - } - } + return id; +}; - // 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]); - } - } - // 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)); +exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function(id) { + delete immediateIds[id]; +}; +},{"process/browser.js":12}],48:[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. - // 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; - } - } - } +var punycode = _dereq_('punycode'); - // return chosen curie - if(choice !== null) { - return choice; - } +exports.parse = urlParse; +exports.resolve = urlResolve; +exports.resolveObject = urlResolveObject; +exports.format = urlFormat; - // compact IRI relative to base - if(!relativeTo.vocab) { - return _removeBase(activeCtx['@base'], iri); - } +exports.Url = Url; - // return IRI as is - return iri; +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; } -/** - * 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'); - - // 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']; - } - } +// Reference: RFC 3986, RFC 1808, RFC 2396 - // 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']; - } +// 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]*$/, - var rval = {}; + // RFC 2396: characters reserved for delimiting URLs. + // We actually just auto-escape these. + delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'], - // preserve @index - if(preserveIndex) { - rval[_compactIri(activeCtx, '@index')] = value['@index']; - } + // RFC 2396: characters not allowed for various reasons. + unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims), - 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']; - } - - // alias @value - rval[_compactIri(activeCtx, '@value')] = value['@value']; - - return rval; - } - - // 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'}); + // 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'); - // compact to scalar - if(type === '@id' || type === '@vocab' || expandedProperty === '@graph') { - return compacted; - } +function urlParse(url, parseQueryString, slashesDenoteHost) { + if (url && isObject(url) && url instanceof Url) return url; - var rval = {}; - rval[_compactIri(activeCtx, '@id')] = compacted; - return rval; + var u = new Url; + u.parse(url, parseQueryString, slashesDenoteHost); + return u; } -/** - * Creates a term definition during context processing. - * - * @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. - */ -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}); +Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) { + if (!isString(url)) { + throw new TypeError("Parameter 'url' must be a string, not " + typeof url); } - // now defining term - defined[term] = false; - - if(_isKeyword(term)) { - throw new JsonLdError( - 'Invalid JSON-LD syntax; keywords cannot be overridden.', - 'jsonld.SyntaxError', - {code: 'keyword redefinition', context: localCtx, term: term}); - } + var rest = url; - if(term === '') { - throw new JsonLdError( - 'Invalid JSON-LD syntax; a term cannot be an empty string.', - 'jsonld.SyntaxError', - {code: 'invalid term definition', context: localCtx}); - } + // trim before proceeding. + // This is to support parse stuff like " http://foo.com \n" + rest = rest.trim(); - // remove old mapping - if(activeCtx.mappings[term]) { - delete activeCtx.mappings[term]; + var proto = protocolPattern.exec(rest); + if (proto) { + proto = proto[0]; + var lowerProto = proto.toLowerCase(); + this.protocol = lowerProto; + rest = rest.substr(proto.length); } - // get context term value - var value = localCtx[term]; - - // clear context entry - if(value === null || (_isObject(value) && value['@id'] === null)) { - activeCtx.mappings[term] = null; - defined[term] = true; - return; + // 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; + } } - // convert short-hand value to object w/@id - if(_isString(value)) { - value = {'@id': value}; - } + if (!hostlessProtocol[proto] && + (slashes || (proto && !slashedProtocol[proto]))) { - 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}); - } + // 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 - // create new mapping - var mapping = activeCtx.mappings[term] = {}; - mapping.reverse = false; + // v0.12 TODO(isaacs): This is not quite how Chrome does things. + // Review our test case against browsers more comprehensively. - 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}); + // 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; } - // 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}); + // 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); } - 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}); + + // 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); } - 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; + + // 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; - // always compute whether term has a colon as an optimization for - // _compactIri - var colon = term.indexOf(':'); - mapping._termHasColon = (colon !== -1); + this.host = rest.slice(0, hostEnd); + rest = rest.slice(hostEnd); - 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); - } + // pull out port. + this.parseHost(); - 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}); + // we've indicated that there is a hostname, + // so even if it's empty, it has to be present. + this.hostname = this.hostname || ''; + + // 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] === ']'; + + // 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; + } + } } - // prepend vocab to term - mapping['@id'] = activeCtx['@vocab'] + term; } - } - - // IRI mapping now defined - defined[term] = true; - 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}); + if (this.hostname.length > hostnameMaxLen) { + this.hostname = ''; + } else { + // hostnames are always lower case. + this.hostname = this.hostname.toLowerCase(); } - 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}); + 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); } + this.hostname = newOut.join('.'); } - // add @type to mapping - mapping['@type'] = type; - } + var p = this.port ? ':' + this.port : ''; + var h = this.hostname || ''; + this.host = h + p; + this.href += this.host; - 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}); + // 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; + } } - - // add @container to mapping - mapping['@container'] = container; } - 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}); - } + // now rest is set to the post-host stuff. + // chop off any delim chars. + if (!unsafeProtocol[lowerProto]) { - // add @language to mapping - if(language !== null) { - language = language.toLowerCase(); + // 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); } - mapping['@language'] = language; } - // 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}); - } -} -/** - * 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; + // 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 = '/'; } - // ensure value is interpreted as a string - value = String(value); - - // define term dependency if not defined - if(localCtx && value in localCtx && defined[value] !== true) { - _createTermDefinition(activeCtx, localCtx, value, defined); + //to support http.request + if (this.pathname || this.search) { + var p = this.pathname || ''; + var s = this.search || ''; + this.path = p + s; } - relativeTo = relativeTo || {}; - if(relativeTo.vocab) { - var mapping = activeCtx.mappings[value]; + // finally, reconstruct the href based on what has been validated. + this.href = this.format(); + return this; +}; - // value is explicitly ignored with a null mapping - if(mapping === null) { - return null; - } +// 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(); +} - if(mapping) { - // value is a term - return mapping['@id']; - } +Url.prototype.format = function() { + var auth = this.auth || ''; + if (auth) { + auth = encodeURIComponent(auth); + auth = auth.replace(/%3A/i, ':'); + auth += '@'; } - // 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 protocol = this.protocol || '', + pathname = this.pathname || '', + hash = this.hash || '', + host = false, + query = ''; - // do not expand blank nodes (prefix of '_') or already-absolute - // IRIs (suffix of '//') - if(prefix === '_' || suffix.indexOf('//') === 0) { - return value; + 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; } + } - // prefix dependency not defined, define it - if(localCtx && prefix in localCtx) { - _createTermDefinition(activeCtx, localCtx, prefix, defined); - } + if (this.query && + isObject(this.query) && + Object.keys(this.query).length) { + query = querystring.stringify(this.query); + } - // use mapping if prefix is defined - var mapping = activeCtx.mappings[prefix]; - if(mapping) { - return mapping['@id'] + suffix; - } + var search = this.search || (query && ('?' + query)) || ''; - // already absolute IRI - return value; - } + if (protocol && protocol.substr(-1) !== ':') protocol += ':'; - // prepend vocab - if(relativeTo.vocab && '@vocab' in activeCtx) { - return activeCtx['@vocab'] + value; + // 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 = ''; } - // prepend base - var rval = value; - if(relativeTo.base) { - rval = jsonld.prependBase(activeCtx['@base'], rval); - } + if (hash && hash.charAt(0) !== '#') hash = '#' + hash; + if (search && search.charAt(0) !== '?') search = '?' + search; - return rval; + pathname = pathname.replace(/[?#]/g, function(match) { + return encodeURIComponent(match); + }); + search = search.replace('#', '%23'); + + return protocol + host + pathname + search + hash; +}; + +function urlResolve(source, relative) { + return urlParse(source, false, true).resolve(relative); } -function _prependBase(base, iri) { - // skip IRI processing - if(base === null) { - return iri; - } - // already an absolute IRI - if(iri.indexOf(':') !== -1) { - return iri; +Url.prototype.resolve = function(relative) { + return this.resolveObject(urlParse(relative, false, true)).format(); +}; + +function urlResolveObject(source, relative) { + if (!source) return relative; + return urlParse(source, false, true).resolveObject(relative); +} + +Url.prototype.resolveObject = function(relative) { + if (isString(relative)) { + var rel = new Url(); + rel.parse(relative, false, true); + relative = rel; } - // parse base if it is a string - if(_isString(base)) { - base = jsonld.url.parse(base || ''); + var result = new Url(); + Object.keys(this).forEach(function(k) { + result[k] = this[k]; + }, this); + + // hash is always overridden, no matter what. + // even href="" will remove it. + result.hash = relative.hash; + + // if the relative url is empty, then there's nothing left to do here. + if (relative.href === '') { + result.href = result.format(); + return result; } - // parse given IRI - var rel = jsonld.url.parse(iri); + // 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]; + }); - // per RFC3986 5.2.2 - var transform = { - protocol: base.protocol || '' - }; + //urlParse appends trailing / to urls like http://www.example.com + if (slashedProtocol[result.protocol] && + result.hostname && !result.pathname) { + result.path = result.pathname = '/'; + } - if(rel.authority !== null) { - transform.authority = rel.authority; - transform.path = rel.path; - transform.query = rel.query; - } else { - transform.authority = base.authority; + result.href = result.format(); + return result; + } - if(rel.path === '') { - transform.path = base.path; - if(rel.query !== null) { - transform.query = rel.query; - } else { - transform.query = base.query; - } + 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; + } + + 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 { - if(rel.path.indexOf('/') === 0) { - // IRI represents an absolute path - transform.path = rel.path; - } else { - // merge paths - var path = base.path; + result.pathname = relative.pathname; + } + 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; + } - // 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; - } + 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]; - transform.path = path; + // 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); + } + 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); } - transform.query = rel.query; + relative.host = null; } + mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === ''); } - // remove slashes and dots in path - transform.path = _removeDotSegments(transform.path, !!transform.authority); - - // 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; + 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(); + } + } + 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; } - // handle empty base - if(rval === '') { - rval = './'; + 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; } - return rval; -} + // 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 === ''); -/** - * 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; + // 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--; + } } - if(_isString(base)) { - base = jsonld.url.parse(base || ''); + // if the path is allowed to go above the root, restore leading ..s + if (!mustEndAbs && !removeAllDots) { + for (; up--; up) { + srcPath.unshift('..'); + } } - // 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 += '//'; + if (mustEndAbs && srcPath[0] !== '' && + (!srcPath[0] || srcPath[0].charAt(0) !== '/')) { + srcPath.unshift(''); } - // IRI not relative to base - if(iri.indexOf(root) !== 0) { - return iri; + if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) { + srcPath.push(''); } - // remove root from IRI and parse remainder - var rel = jsonld.url.parse(iri.substr(root.length)); + var isAbsolute = srcPath[0] === '' || + (srcPath[0] && srcPath[0].charAt(0) === '/'); - // 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; + // 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(); } - 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 += '../'; - } - } + mustEndAbs = mustEndAbs || (result.host && srcPath.length); - // prepend remaining segments - rval += iriSegments.join('/'); + if (mustEndAbs && !isAbsolute) { + srcPath.unshift(''); + } - // add query and hash - if(rel.query !== null) { - rval += '?' + rel.query; + if (!srcPath.length) { + result.pathname = null; + result.path = null; + } else { + result.pathname = srcPath.join('/'); } - if(rel.fragment !== null) { - rval += '#' + rel.fragment; + + //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; +}; - // handle empty base - if(rval === '') { - rval = './'; +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); + } + host = host.substr(0, host.length - port.length); } + if (host) this.hostname = host; +}; - return rval; +function isString(arg) { + return typeof arg === "string"; } -/** - * 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 - }; +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} - /** - * 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 isNull(arg) { + return arg === null; +} +function isNullOrUndefined(arg) { + return arg == null; +} - // lazily create inverse - if(activeCtx.inverse) { - return activeCtx.inverse; - } - var inverse = activeCtx.inverse = {}; +},{"punycode":13,"querystring":16}],49:[function(_dereq_,module,exports){ +arguments[4][10][0].apply(exports,arguments) +},{"dup":10}],50:[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'; +} +},{}],51:[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. - // variables for building fast CURIE map - var fastCurieMap = activeCtx.fastCurieMap = {}; - var irisToTerms = {}; - - // handle default language - var defaultLanguage = activeCtx['@language'] || '@none'; - - // 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; - } - - var container = mapping['@container'] || '@none'; - - // iterate over every IRI in the mapping - var ids = mapping['@id']; - if(!_isArray(ids)) { - ids = [ids]; - } - 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 = {}; - - 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); - } - - // add new entry - if(!entry[container]) { - entry[container] = { - '@language': {}, - '@type': {} - }; - } - entry = entry[container]; - - 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); +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(' '); + } - // add entries for no type and no language - _addPreferredTerm(mapping, term, entry['@type'], '@none'); - _addPreferredTerm(mapping, term, entry['@language'], '@none'); + 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; } - - // build fast CURIE map - for(var key in fastCurieMap) { - _buildIriMap(fastCurieMap, key, 1); + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); } + } + return str; +}; - return inverse; + +// 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); + }; } - /** - * 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] = {}; + if (process.noDeprecation === true) { + return fn; + } - 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]); + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); } else { - next[letter] = [entries[i]]; - } - } - - for(var key in next) { - if(key === '') { - continue; + console.error(msg); } - _buildIriMap(next, key, idx + 1); + warned = true; } + return fn.apply(this, arguments); } - /** - * 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; - } - } + return deprecated; +}; - /** - * 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']; + +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 child; } -} + return debugs[set]; +}; + /** - * Returns whether or not the given value is a keyword. - * - * @param v the value to check. + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. * - * @return true if the value is a keyword, false if not. + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. */ -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; +/* 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 false; + // 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; -/** - * 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]'); -} -/** - * 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; -} +// 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] +}; -/** - * 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); -} +// 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' +}; -/** - * 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; - } - // 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; - } - } - } +function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; - 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}); + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; } } -/** - * 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]'); -} -/** - * 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]'); +function stylizeNoColor(str, styleType) { + return str; } -/** - * 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; -} -/** - * 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); -} +function arrayToHash(array) { + var hash = {}; -/** - * 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]'); -} + array.forEach(function(val, idx) { + hash[val] = true; + }); -/** - * 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'); + return hash; } -/** - * 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)); + +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; } - return rval; -} -/** - * 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)); -} + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } -/** - * 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); -} + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); -/** - * 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); -} - -/** - * 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 (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); } - return rval; -} -/** - * 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; -} + // 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); + } -/** - * 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]); - } - } else { - rval = value.toString(); + // 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 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 base = '', array = false, braces = ['{', '}']; - // get @context - var ctx = input[key]; + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } - // 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; - } - } - } - return (count < Object.keys(urls).length); + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; } - return false; -} -/** - * 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; + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } - // 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); - } + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } - // for tracking the URLs to retrieve - var urls = {}; + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } - // 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); - }; + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } - // find all URLs in the given input - if(!_findContextUrls(input, urls, false, base)) { - // no new URLs in input - return finished(); + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); } + } - // queue all unretrieved URLs - var queue = []; - for(var url in urls) { - if(urls[url] === false) { - queue.push(url); - } - } + ctx.seen.push(value); - // 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 output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } - var ctx = remoteDoc ? remoteDoc.document : null; + ctx.seen.pop(); - // parse string context as JSON - if(!err && _isString(ctx)) { - try { - ctx = JSON.parse(ctx); - } catch(ex) { - err = ex; - } - } + return reduceToSingleString(output, base, braces); +} - // 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); - } - // use empty context if no @context key is present - if(!('@context' in ctx)) { - ctx = {'@context': {}}; - } else { - ctx = {'@context': ctx['@context']}; - } +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'); +} - // append context URL to context if given - if(remoteDoc.contextUrl) { - if(!_isArray(ctx['@context'])) { - ctx['@context'] = [ctx['@context']]; - } - ctx['@context'].push(remoteDoc.contextUrl); - } - // 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); +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; } -// 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'); + +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 { + output.push(''); } - var rval = []; - for(var p in o) { - if(Object.prototype.hasOwnProperty.call(o, p)) { - rval.push(p); - } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); } - return rval; - }; + }); + return output; } -/** - * 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 + '$'); - // define quad part regexes - var subject = '(?:' + iri + '|' + bnode + ')' + ws; - var property = iri + ws; - var object = '(?:' + iri + '|' + bnode + '|' + literal + ')' + wso; - var graphName = '(?:\\.|(?:(?:' + iri + '|' + bnode + ')' + wso + '\\.))'; +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'); + } + } + 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'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + 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'); + } + } - // full quad regex - var quad = new RegExp( - '^' + wso + subject + property + object + graphName + wso + comment + '$'); + return name + ': ' + str; +} - // build RDF dataset - var dataset = {}; - // 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++; +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); - // skip empty lines - if(empty.test(line)) { - continue; - } + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } - // parse quad - var match = line.match(quad); - if(match === null) { - throw new JsonLdError( - 'Error while parsing N-Quads; invalid quad.', - 'jsonld.ParseError', {line: lineNumber}); - } + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} - // create RDF triple - var triple = {}; - // get subject - if(!_isUndefined(match[1])) { - triple.subject = {type: 'IRI', value: match[1]}; - } else { - triple.subject = {type: 'blank node', value: match[2]}; - } +// 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; - // get predicate - triple.predicate = {type: 'IRI', value: match[3]}; +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; - // 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; - } - - // 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]; - } - - // 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; - } - } - if(unique) { - triples.push(triple); - } - } - } - - return dataset; +function isNull(arg) { + return arg === null; } +exports.isNull = isNull; -// register the N-Quads RDF parser -jsonld.registerRDFParser('application/nquads', _parseNQuads); - -/** - * 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(''); +function isNullOrUndefined(arg) { + return arg == null; } +exports.isNullOrUndefined = isNullOrUndefined; -/** - * 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; - } - - var quad = ''; +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; - // subject is an IRI - if(s.type === 'IRI') { - quad += '<' + s.value + '>'; - } else { - quad += s.value; - } - quad += ' '; +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; - // predicate is an IRI - if(p.type === 'IRI') { - quad += '<' + p.value + '>'; - } else { - quad += p.value; - } - quad += ' '; +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; - // 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 + '>'; - } - } +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; - // graph - if(g !== null && g !== undefined) { - if(g.indexOf('_:') !== 0) { - quad += ' <' + g + '>'; - } else { - quad += ' ' + g; - } - } +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; - quad += ' .\n'; - return quad; +function isObject(arg) { + return typeof arg === 'object' && arg !== null; } +exports.isObject = isObject; -/** - * 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'] = []; +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; - var subjects = data.getSubjects(); - for(var si = 0; si < subjects.length; ++si) { - var subject = subjects[si]; - if(subject === null) { - continue; - } +function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; - // 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]; +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; - // create RDF triple - var triple = {}; +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 subject - if(subject.indexOf('_:') === 0) { - triple.subject = {type: 'blank node', value: subject}; - } else { - triple.subject = {type: 'IRI', value: subject}; - } +exports.isBuffer = _dereq_('./support/isBuffer'); - // add predicate - if(predicate.indexOf('_:') === 0) { - triple.predicate = {type: 'blank node', value: predicate}; - } else { - triple.predicate = {type: 'IRI', value: predicate}; - } +function objectToString(o) { + return Object.prototype.toString.call(o); +} - // 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; - } - } - } - // add object - triple.object = {}; +function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); +} - // object is an IRI - if(object.type === RDF_OBJECT) { - if(object.value.indexOf('_:') === 0) { - triple.object.type = 'blank node'; - } else { - triple.object.type = 'IRI'; - } - } 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; - } - } - triple.object.value = value; - // add triple to dataset in default graph - dataset['@default'].push(triple); - } - } - } +var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; - return dataset; +// 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(' '); } -// register the RDFa API RDF parser -jsonld.registerRDFParser('rdfa-api', _parseRdfaApiData); - -/** - * 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; -/** - * 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; +// 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)); }; + /** - * Gets the new identifier for the given old identifier, where if no old - * identifier is given a new identifier will be generated. + * Inherit the prototype methods from one constructor into another. * - * @param [old] the old identifier to get the new identifier for. + * 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). * - * @return the new identifier. + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. */ -IdentifierIssuer.prototype.getId = function(old) { - // return existing old identifier - if(old && old in this.existing) { - return this.existing[old]; - } +exports.inherits = _dereq_('inherits'); - // get next identifier - var identifier = this.prefix + this.counter; - this.counter += 1; +exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; - // save mapping - if(old) { - this.existing[old] = identifier; + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; } - - return identifier; -}; -// alias -IdentifierIssuer.prototype.getName = IdentifierIssuer.prototype.getName; - -/** - * 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); + return origin; }; -// 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; - } -}; +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} -/** - * Returns true if there is another permutation. - * - * @return true if there is another permutation, false if not. - */ -Permutator.prototype.hasNext = function() { - return !this.done; -}; +}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -/** - * 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(); +},{"./support/isBuffer":50,"_process":12,"inherits":49}],52:[function(_dereq_,module,exports){ +module.exports = extend - /* Calculate the next permutation using the Steinhaus-Johnson-Trotter - permutation algorithm. */ +var hasOwnProperty = Object.prototype.hasOwnProperty; - // 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; - } - } +function extend() { + var target = {} - // 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; + for (var i = 0; i < arguments.length; i++) { + var source = arguments[i] - // 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]]; - } + for (var key in source) { + if (hasOwnProperty.call(source, key)) { + target[key] = source[key] + } + } } - } - return rval; -}; - -//////////////////////// DEFINE NORMALIZATION HASH API //////////////////////// + return target +} -/** - * Creates a new NormalizeHash for use by the given normalization algorithm. - * - * @param algorithm the RDF Dataset Normalization algorithm to use: - * 'URDNA2015' or 'URGNA2012'. +},{}],53:[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 */ -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(); -}; -// switch definition of NormalizeHash based on environment -(function(_nodejs) { +(function() { + "use strict"; + function lib$es6$promise$utils$$objectOrFunction(x) { + return typeof x === 'function' || (typeof x === 'object' && x !== null); + } -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'; + function lib$es6$promise$utils$$isFunction(x) { + return typeof x === 'function'; } - 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; -} - -// 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(); -}; -/////////////////////////// DEFINE MESSAGE DIGEST API ///////////////////////// + function lib$es6$promise$utils$$isMaybeThenable(x) { + return typeof x === 'object' && x !== null; + } -/** - * Creates a new MessageDigest. - * - * @param algorithm the algorithm to use. - */ -var MessageDigest = function(algorithm) { - if(!(this instanceof MessageDigest)) { - return new MessageDigest(algorithm); - } + 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; + } - this._algorithm = algorithm; + 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; - // 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; - } - n >>>= 1; - if(n > 0) { - c += c; + 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(); + } } } - } - // start digest automatically for first time - this.start(); -}; + function lib$es6$promise$asap$$setScheduler(scheduleFn) { + lib$es6$promise$asap$$customSchedulerFn = scheduleFn; + } -/** - * Starts the digest. - * - * @return this digest object. - */ -MessageDigest.prototype.start = function() { - // up to 56-bit message length for convenience - this.messageLength = 0; + function lib$es6$promise$asap$$setAsap(asapFn) { + lib$es6$promise$asap$$asap = asapFn; + } - // full message length - this.fullMessageLength = []; - var int32s = this._algorithm.messageLengthSize / 4; - for(var i = 0; i < int32s; ++i) { - this.fullMessageLength.push(0); - } + 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]'; - // input buffer - this._input = new MessageDigest.ByteBuffer(); + // test for web worker but not in IE10 + var lib$es6$promise$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' && + typeof importScripts !== 'undefined' && + typeof MessageChannel !== 'undefined'; - // get starting state - this.state = this._algorithm.start(); + // 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 this; -}; + // vertx + function lib$es6$promise$asap$$useVertxTimer() { + return function() { + lib$es6$promise$asap$$vertxNext(lib$es6$promise$asap$$flush); + }; + } -/** - * 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))); + 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 }); - // 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); - } + return function() { + node.data = (iterations = ++iterations % 2); + }; + } - // add bytes to input buffer - this._input.putBytes(msg.bytes()); + // 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); + }; + } - // digest blocks - while(this._input.length() >= this._algorithm.blockSize) { - this.state = this._algorithm.digest(this.state, this._input); - } + function lib$es6$promise$asap$$useSetTimeout() { + return function() { + setTimeout(lib$es6$promise$asap$$flush, 1); + }; + } - // compact input buffer every 2K or if empty - if(this._input.read > 2048 || this._input.length() === 0) { - this._input.compact(); - } + 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]; - return this; -}; + callback(arg); -/** - * 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. */ + lib$es6$promise$asap$$queue[i] = undefined; + lib$es6$promise$asap$$queue[i+1] = undefined; + } - /* 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. + lib$es6$promise$asap$$len = 0; + } - 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). + 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(); + } + } - 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. */ + 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(); + } - // create final block - var finalBlock = new MessageDigest.ByteBuffer(); - finalBlock.putBytes(this._input.bytes()); + function lib$es6$promise$$internal$$noop() {} - // compute remaining size to be digested (include message length size) - var remaining = ( - this.fullMessageLength[this.fullMessageLength.length - 1] + - this._algorithm.messageLengthSize); + var lib$es6$promise$$internal$$PENDING = void 0; + var lib$es6$promise$$internal$$FULFILLED = 1; + var lib$es6$promise$$internal$$REJECTED = 2; - // 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)); + var lib$es6$promise$$internal$$GET_THEN_ERROR = new lib$es6$promise$$internal$$ErrorObject(); - // 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)); - } + function lib$es6$promise$$internal$$selfFullfillment() { + return new TypeError("You cannot resolve a promise with itself"); + } - // write the length of the message (algorithm-specific) - this._algorithm.writeMessageLength(finalBlock, messageLength); + function lib$es6$promise$$internal$$cannotReturnOwn() { + return new TypeError('A promises callback cannot return that same promise.'); + } - // digest final block - var state = this._algorithm.digest(this.state.copy(), finalBlock); + 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; + } + } - // write state to buffer - var rval = new MessageDigest.ByteBuffer(); - state.write(rval); - return rval; -}; + function lib$es6$promise$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) { + try { + then.call(value, fulfillmentHandler, rejectionHandler); + } catch(e) { + return e; + } + } -/** - * 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 = ''; - } - this.read = 0; -}; + 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; -/** - * 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)); -}; + lib$es6$promise$$internal$$reject(promise, reason); + }, 'Settle: ' + (promise._label || ' unknown promise')); -/** - * 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; -}; + if (!sealed && error) { + sealed = true; + lib$es6$promise$$internal$$reject(promise, error); + } + }, promise); + } -/** - * Puts the given bytes into this buffer. - * - * @param bytes the bytes as a binary-encoded string. - */ -MessageDigest.ByteBuffer.prototype.putBytes = function(bytes) { - this.data += bytes; -}; + 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); + }); + } + } -/** - * 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); -}; + 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); -/** - * Gets the number of bytes in this buffer. - * - * @return the number of bytes in this buffer. - */ -MessageDigest.ByteBuffer.prototype.length = function() { - return this.data.length - this.read; -}; + 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); + } + } + } -/** - * Compacts this buffer. - */ -MessageDigest.ByteBuffer.prototype.compact = function() { - this.data = this.data.slice(this.read); - this.read = 0; -}; + 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); + } + } -/** - * Converts this buffer to a hexadecimal string. - * - * @return a hexadecimal string. - */ -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'; + function lib$es6$promise$$internal$$publishRejection(promise) { + if (promise._onerror) { + promise._onerror(promise._result); + } + + lib$es6$promise$$internal$$publish(promise); } - rval += b.toString(16); - } - return rval; -}; -///////////////////////////// DEFINE SHA-1 ALGORITHM ////////////////////////// + function lib$es6$promise$$internal$$fulfill(promise, value) { + if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; } -var sha1 = { - // used for word storage - _w: null -}; + promise._result = value; + promise._state = lib$es6$promise$$internal$$FULFILLED; -sha1.Algorithm = function() { - this.name = 'sha1', - this.blockSize = 64; - this.digestLength = 20; - this.messageLengthSize = 8; -}; + if (promise._subscribers.length !== 0) { + lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, promise); + } + } -sha1.Algorithm.prototype.start = function() { - if(!sha1._w) { - sha1._w = new Array(80); - } - return sha1._createState(); -}; + 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; -sha1.Algorithm.prototype.writeMessageLength = function( - finalBlock, messageLength) { - // message length is in bits and in big-endian order; simply append - finalBlock.putBytes(messageLength.bytes()); -}; + lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publishRejection, promise); + } -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; + function lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection) { + var subscribers = parent._subscribers; + var length = subscribers.length; - // 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 + parent._onerror = null; - // 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; - } - 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; - } - // 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; - } - 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; - } - // 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; - } - // 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; - } - - // 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; - - len -= 64; - } - - return s; -}; + subscribers[length] = child; + subscribers[length + lib$es6$promise$$internal$$FULFILLED] = onFulfillment; + subscribers[length + lib$es6$promise$$internal$$REJECTED] = onRejection; -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; -}; + if (length === 0 && parent._state) { + lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, parent); + } + } -//////////////////////////// DEFINE SHA-256 ALGORITHM ///////////////////////// + function lib$es6$promise$$internal$$publish(promise) { + var subscribers = promise._subscribers; + var settled = promise._state; -var sha256 = { - // shared state - _k: null, - _w: null -}; + if (subscribers.length === 0) { return; } -sha256.Algorithm = function() { - this.name = 'sha256', - this.blockSize = 64; - this.digestLength = 32; - this.messageLengthSize = 8; -}; + var child, callback, detail = promise._result; -sha256.Algorithm.prototype.start = function() { - if(!sha256._k) { - sha256._init(); - } - return sha256._createState(); -}; + for (var i = 0; i < subscribers.length; i += 3) { + child = subscribers[i]; + callback = subscribers[i + settled]; -sha256.Algorithm.prototype.writeMessageLength = function( - finalBlock, messageLength) { - // message length is in bits and in big-endian order; simply append - finalBlock.putBytes(messageLength.bytes()); -}; + if (child) { + lib$es6$promise$$internal$$invokeCallback(settled, child, callback, detail); + } else { + callback(detail); + } + } -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; + promise._subscribers.length = 0; } - // 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; + function lib$es6$promise$$internal$$ErrorObject() { + this.error = null; + } - // 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)); + var lib$es6$promise$$internal$$TRY_CATCH_ERROR = new lib$es6$promise$$internal$$ErrorObject(); - // 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 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; + } } - // 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; -}; + function lib$es6$promise$$internal$$invokeCallback(settled, promise, callback, detail) { + var hasCallback = lib$es6$promise$utils$$isFunction(callback), + value, error, succeeded, failed; -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; -}; + if (hasCallback) { + value = lib$es6$promise$$internal$$tryCatch(callback, detail); -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]; + if (value === lib$es6$promise$$internal$$TRY_CATCH_ERROR) { + failed = true; + error = value.error; + value = null; + } else { + succeeded = true; + } - // used for word storage - sha256._w = new Array(64); -}; + if (promise === value) { + lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$cannotReturnOwn()); + return; + } -})(_nodejs); // end definition of NormalizeHash + } else { + value = detail; + succeeded = true; + } -if(!XMLSerializer) { + 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); + } + } -var _defineXMLSerializer = function() { - XMLSerializer = _dereq_('xmldom').XMLSerializer; -}; + 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); + } + } -} // end _defineXMLSerializer + function lib$es6$promise$enumerator$$Enumerator(Constructor, input) { + var enumerator = this; -// 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]; - } - parsed.normalizedPath = _removeDotSegments(parsed.path, !!parsed.authority); - return parsed; -}; + enumerator._instanceConstructor = Constructor; + enumerator.promise = new Constructor(lib$es6$promise$$internal$$noop); -/** - * Removes dot segments from a URL path. - * - * @param path the path to remove dot segments from. - * @param hasAuthority true if the URL has an authority, false if not. - */ -function _removeDotSegments(path, hasAuthority) { - var rval = ''; + if (enumerator._validateInput(input)) { + enumerator._input = input; + enumerator.length = input.length; + enumerator._remaining = input.length; - if(path.indexOf('/') === 0) { - rval = '/'; - } + enumerator._init(); - // 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(); + 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 { - // leading relative URL '..' - output.push('..'); + lib$es6$promise$$internal$$reject(enumerator.promise, enumerator._validationError()); } - continue; } - output.push(input.shift()); - } - - 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'); -} + lib$es6$promise$enumerator$$Enumerator.prototype._validateInput = function(input) { + return lib$es6$promise$utils$$isArray(input); + }; -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}); - } - }; + lib$es6$promise$enumerator$$Enumerator.prototype._validationError = function() { + return new Error('Array Methods must be provided an Array'); + }; - // expose version - var _module = {exports: {}, filename: __dirname}; - _dereq_('pkginfo')(_module, 'version'); - jsonld.version = _module.exports.version; -} + lib$es6$promise$enumerator$$Enumerator.prototype._init = function() { + this._result = new Array(this.length); + }; -// end of jsonld API factory -return jsonld; -}; + var lib$es6$promise$enumerator$$default = lib$es6$promise$enumerator$$Enumerator; -// external APIs: + lib$es6$promise$enumerator$$Enumerator.prototype._enumerate = function() { + var enumerator = this; -// used to generate a new jsonld API instance -var factory = function() { - return wrapper(function() { - return factory(); - }); -}; + var length = enumerator.length; + var promise = enumerator.promise; + var input = enumerator._input; -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); + for (var i = 0; promise._state === lib$es6$promise$$internal$$PENDING && i < length; i++) { + enumerator._eachEntry(input[i], i); + } + }; - if(typeof _dereq_ === 'function' && - typeof module !== 'undefined' && module.exports) { - // export CommonJS/nodejs API - module.exports = factory; - } + lib$es6$promise$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) { + var enumerator = this; + var c = enumerator._instanceConstructor; - if(_browser) { - // export simple browser API - if(typeof jsonld === 'undefined') { - jsonld = jsonldjs = factory; - } else { - jsonldjs = factory; - } - } -} + 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; + } + }; -return factory; + lib$es6$promise$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) { + var enumerator = this; + var promise = enumerator.promise; -})(); + if (promise._state === lib$es6$promise$$internal$$PENDING) { + enumerator._remaining--; -}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},"/node_modules/jsonld/js") + if (state === lib$es6$promise$$internal$$REJECTED) { + lib$es6$promise$$internal$$reject(promise, value); + } else { + enumerator._result[i] = value; + } + } -},{"_process":43,"crypto":17,"es6-promise":9,"http":17,"jsonld-request":17,"pkginfo":17,"request":17,"util":17,"xmldom":17}],19:[function(_dereq_,module,exports){ -var pkg = _dereq_('./src/libsbgn'); + if (enumerator._remaining === 0) { + lib$es6$promise$$internal$$fulfill(promise, enumerator._result); + } + }; -module.exports = pkg; + lib$es6$promise$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) { + var enumerator = this; -},{"./src/libsbgn":29}],20:[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); - } else { - return str; + 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; } - }; - -}).call(this); - -},{}],21:[function(_dereq_,module,exports){ -// Generated by CoffeeScript 1.12.7 -(function() { - "use strict"; - var builder, defaults, escapeCDATA, requiresCDATA, wrapCDATA, - hasProp = {}.hasOwnProperty; - - builder = _dereq_('xmlbuilder'); + 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; - defaults = _dereq_('./defaults').defaults; + var promise = new Constructor(lib$es6$promise$$internal$$noop); - requiresCDATA = function(entry) { - return typeof entry === "string" && (entry.indexOf('&') >= 0 || entry.indexOf('>') >= 0 || entry.indexOf('<') >= 0); - }; + if (!lib$es6$promise$utils$$isArray(entries)) { + lib$es6$promise$$internal$$reject(promise, new TypeError('You must pass an array to race.')); + return promise; + } - wrapCDATA = function(entry) { - return ""; - }; + var length = entries.length; - escapeCDATA = function(entry) { - return entry.replace(']]>', ']]]]>'); - }; + function onFulfillment(value) { + lib$es6$promise$$internal$$resolve(promise, value); + } - 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; + function onRejection(reason) { + lib$es6$promise$$internal$$reject(promise, reason); } - for (key in opts) { - if (!hasProp.call(opts, key)) continue; - value = opts[key]; - this.options[key] = value; + + 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; } + 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; - 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; + if (object && typeof object === 'object' && object.constructor === Constructor) { + return object; } - 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); - }; - return Builder; + 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; - })(); + var lib$es6$promise$promise$$counter = 0; -}).call(this); + function lib$es6$promise$promise$$needsResolver() { + throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); + } -},{"./defaults":22,"xmlbuilder":146}],22:[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 + 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."); } - }; -}).call(this); + 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. -},{}],23:[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; + Terminology + ----------- - sax = _dereq_('sax'); + - `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. - events = _dereq_('events'); + A promise can be in one of three states: pending, fulfilled, or rejected. - bom = _dereq_('./bom'); + 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. - processors = _dereq_('./processors'); + 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. - setImmediate = _dereq_('timers').setImmediate; - defaults = _dereq_('./defaults').defaults; + Basic Usage: + ------------ - isEmpty = function(thing) { - return typeof thing === "object" && (thing != null) && Object.keys(thing).length === 0; - }; + ```js + var promise = new Promise(function(resolve, reject) { + // on success + resolve(value); - 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); - } - return item; - }; + // on failure + reject(reason); + }); - exports.Parser = (function(superClass) { - extend(Parser, superClass); + promise.then(function(value) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` - 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); - } - 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(); - } + Advanced Usage: + --------------- - 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); - } + Promises shine when abstracting away asynchronous interactions such as + `XMLHttpRequest`s. + + ```js + function getJSON(url) { + return new Promise(function(resolve, reject){ + var xhr = new XMLHttpRequest(); + + 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 + ']')); + } + } + }; + }); } - }; - Parser.prototype.assignOrPush = function(obj, key, newValue) { - if (!(key in obj)) { - if (!this.options.explicitArray) { - return obj[key] = newValue; - } else { - return obj[key] = [newValue]; + getJSON('/posts.json').then(function(json) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Unlike callbacks, promises are great composable primitives. + + ```js + Promise.all([ + getJSON('/posts'), + getJSON('/comments') + ]).then(function(values){ + values[0] // => postsJSON + values[1] // => commentsJSON + + return values; + }); + ``` + + @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(); } - } else { - if (!(obj[key] instanceof Array)) { - obj[key] = [obj[key]]; + + if (!(this instanceof lib$es6$promise$promise$$Promise)) { + lib$es6$promise$promise$$needsNew(); } - return obj[key].push(newValue); + + lib$es6$promise$$internal$$initializePromise(this, resolver); } - }; + } - 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 + 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; + + lib$es6$promise$promise$$Promise.prototype = { + constructor: lib$es6$promise$promise$$Promise, + + /** + 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. + + ```js + findUser().then(function(user){ + // user is available + }, function(reason){ + // user is unavailable, and you are given the reason why }); - 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; - } - } - }; + Chaining + -------- - return Parser; + 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. - })(events.EventEmitter); + ```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'` + }); - 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; - } - } else { - if (typeof a === 'function') { - cb = a; - } - options = {}; - } - parser = new exports.Parser(options); - return parser.parseString(str, cb); - }; + 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. -}).call(this); + ```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 + }); + ``` -},{"./bom":20,"./defaults":22,"./processors":24,"events":10,"sax":106,"timers":113}],24:[function(_dereq_,module,exports){ -// Generated by CoffeeScript 1.12.7 -(function() { - "use strict"; - var prefixMatch; + Assimilation + ------------ - prefixMatch = new RegExp(/(?!xmlns)^.*:/); + 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*. - exports.normalize = function(str) { - return str.toLowerCase(); - }; + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // The user's comments are now available + }); + ``` - exports.firstCharLowerCase = function(str) { - return str.charAt(0).toLowerCase() + str.slice(1); - }; + If the assimliated promise rejects, then the downstream promise will also reject. - exports.stripPrefix = function(str) { - return str.replace(prefixMatch, ''); - }; + ```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 + }); + ``` - exports.parseNumbers = function(str) { - if (!isNaN(str)) { - str = str % 1 === 0 ? parseInt(str, 10) : parseFloat(str); - } - return str; - }; + Simple Example + -------------- - exports.parseBooleans = function(str) { - if (/^(?:true|false)$/i.test(str)) { - str = str.toLowerCase() === 'true'; - } - return str; - }; + Synchronous Example -}).call(this); + ```javascript + var result; -},{}],25:[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; + try { + result = findResult(); + // success + } catch(reason) { + // failure + } + ``` - defaults = _dereq_('./defaults'); + Errback Example - builder = _dereq_('./builder'); + ```js + findResult(function(result, err){ + if (err) { + // failure + } else { + // success + } + }); + ``` - parser = _dereq_('./parser'); + Promise Example; - processors = _dereq_('./processors'); + ```javascript + findResult().then(function(result){ + // success + }, function(reason){ + // failure + }); + ``` - exports.defaults = defaults.defaults; + Advanced Example + -------------- - exports.processors = processors; + Synchronous Example - exports.ValidationError = (function(superClass) { - extend(ValidationError, superClass); + ```javascript + var author, books; - function ValidationError(message) { - this.message = message; - } + try { + author = findAuthor(); + books = findBooksByAuthor(author); + // success + } catch(reason) { + // failure + } + ``` - return ValidationError; + Errback Example - })(Error); + ```js - exports.Builder = builder.Builder; + function foundBooks(books) { - exports.Parser = parser.Parser; + } - exports.parseString = parser.parseString; + function failure(reason) { -}).call(this); + } -},{"./builder":21,"./defaults":22,"./parser":23,"./processors":24}],26:[function(_dereq_,module,exports){ -var N3 = _dereq_('n3'); + 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 + } + }); + ``` -var ns = {}; + Promise Example; -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/"}; + ```javascript + findAuthor(). + then(findBooksByAuthor). + then(function(books){ + // found books + }).catch(function(reason){ + // something went wrong + }); + ``` -// pure shortcut function -ns.expandPrefix = function(prefix) { - return N3.Util.expandPrefixedName(prefix, ns.prefixes) -}; + @method then + @param {Function} onFulfilled + @param {Function} onRejected + Useful for tooling. + @return {Promise} + */ + then: function(onFulfillment, onRejection) { + var parent = this; + var state = parent._state; -// 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); + if (state === lib$es6$promise$$internal$$FULFILLED && !onFulfillment || state === lib$es6$promise$$internal$$REJECTED && !onRejection) { + return this; + } -controlledVocabularyList = [ - "bqmodel:is", - "bqmodel:isDerivedFrom", - "bqmodel:isDescribedBy", - "bqmodel:isInstanceOf", - "bqmodel:hasInstance", + var child = new this.constructor(lib$es6$promise$$internal$$noop); + var result = parent._result; - "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", + 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); + } - "sio:SIO_000223" -]; + return child; + }, -ns.isControlledVocabulary = {}; -for(var i=0; i 0; -}; + 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'); + } + } -ns.countBagElements = function(graph, subject) { - return graph.countTriples(subject, null, null) - 1; -}; + var P = local.Promise; -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 (P && Object.prototype.toString.call(P.resolve()) === '[object Promise]' && !P.cast) { + return; + } - // 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; -}; + local.Promise = lib$es6$promise$promise$$default; + } + var lib$es6$promise$polyfill$$default = lib$es6$promise$polyfill$$polyfill; -/** - * 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 - */ -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)]; - } -}; + var lib$es6$promise$umd$$ES6Promise = { + 'Promise': lib$es6$promise$promise$$default, + 'polyfill': lib$es6$promise$polyfill$$default + }; -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; -}; + /* 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; + } -/** - * kvobject contains biology qualifier as key and miriam resource as value - */ -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]); - } -}; + lib$es6$promise$polyfill$$default(); +}).call(this); -module.exports = ns; -},{"n3":31}],27:[function(_dereq_,module,exports){ + +}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) + +},{"_process":12}],54:[function(_dereq_,module,exports){ +// Ignore module for browserify (see package.json) +},{}],55:[function(_dereq_,module,exports){ +(function (process,global,__dirname){ /** - * 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. + * A JavaScript implementation of the JSON-LD API. * - * 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. + * @author Dave Longley * - * # Controlled vocabulary + * @license BSD 3-Clause License + * Copyright (c) 2011-2015 Digital Bazaar, Inc. + * All rights reserved. * - * 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-- + * 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. * - * See {@link Extension} for more general information on extensions in the SBGN-ML format. + * 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. * - * You can access the following classes like this: libsbgn.annot.Annotation + * 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. * - * @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 - */ -var Annotation = function (params) { - var params = checkParams(params, ['rdfElement']); - this.rdfElement = params.rdfElement; -}; - -/** - * @param {RdfElement} rdfElement + * 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. */ -Annotation.prototype.setRdfElement = function(rdfElement) { - this.rdfElement = rdfElement; -}; +(function() { -Annotation.prototype.buildJsObj = function () { - var annotationJsonObj = {}; +// 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 = $; + } + } +} - if(this.rdfElement != null) { - annotationJsonObj = this.rdfElement.buildJsObj(); - } +// attaches jsonld API to the given object +var wrapper = function(jsonld) { - return annotationJsonObj; -}; +/* Core API */ /** - * @return {string} + * 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. */ -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; -}; +jsonld.compact = function(input, ctx, options, callback) { + if(arguments.length < 2) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not compact, too few arguments.')); + }); + } -Annotation.fromObj = function (jsObj) { - if (typeof jsObj.annotation == 'undefined') { - throw new Error("Bad XML provided, expected tagName annotation, got: " + Object.keys(jsObj)[0]); - } + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; - var annotation = new ns.Annotation(); - jsObj = jsObj.annotation; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return annotation; - } + if(ctx === null) { + return jsonld.nextTick(function() { + callback(new JsonLdError( + 'The compaction context must not be null.', + 'jsonld.CompactError', {code: 'invalid local context'})); + }); + } - // children - if(jsObj['rdf:RDF']) { - var obj = {}; - obj['rdf:RDF'] = jsObj['rdf:RDF'][0]; - var rdf = ns.RdfElement.fromObj(obj); - annotation.setRdfElement(rdf); - } + // nothing to compact + if(input === null) { + return jsonld.nextTick(function() { + callback(null, null); + }); + } - return annotation; -}; + // 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; + } -ns.Annotation = Annotation; -// ------- END ANNOTATION ------- + var expand = function(input, options, callback) { + if(options.skipExpansion) { + return jsonld.nextTick(function() { + callback(null, input); + }); + } + jsonld.expand(input, options, callback); + }; -// ------- 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; - } -}; + // 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})); + } -StoreObject.prototype.getCustomPropertiesOfId = function (id) { - return Util.getCustomPropertiesOfId(this.store, id); -}; + // 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})); + } -StoreObject.prototype.getAllIds = function () { - return Util.getAllIds(this.store); -}; + var compacted; + try { + // do compaction + compacted = new Processor().compact(activeCtx, null, expanded, options); + } catch(ex) { + return callback(ex); + } -StoreObject.prototype.addCustomProperty = function (id, kvObject) { - return Util.addCustomProperty(this.store, id, kvObject); -}; + cleanup(null, compacted, activeCtx, options); + }); + }); -StoreObject.prototype.getResourcesOfId = function(id) { - return Util.getResourcesOfId(this.store, id); -}; + // performs clean up after compaction + function cleanup(err, compacted, activeCtx, options) { + if(err) { + return callback(err); + } -StoreObject.prototype.addResource = function (id, kvObject) { - return Util.addResource(this.store, id, kvObject); -}; + 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]; + } -ns.StoreObject = StoreObject; -// ------- END STOREOBJECT ------- + // follow @context key + if(_isObject(ctx) && '@context' in ctx) { + ctx = ctx['@context']; + } -// ------- GLOBALSTORE ------- -var GlobalRdfStore = function (params) { - ns.StoreObject.call(this, params); -}; -GlobalRdfStore.prototype = Object.create(ns.StoreObject.prototype); -GlobalRdfStore.prototype.constructor = GlobalRdfStore; + // 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]); + } + } -GlobalRdfStore.prototype.load = function (annotations) { - for(var i=0; i<rd:RDFf> element. - * @class - */ -var RdfElement = function (params) { - ns.StoreObject.call(this, params); -}; -RdfElement.prototype = Object.create(ns.StoreObject.prototype); -RdfElement.prototype.constructor = RdfElement; - -RdfElement.uri = 'http://www.eisbm.org/'; - -/** - * @return {string} - */ -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; - } + // remove array if only one context + var hasContext = (ctx.length > 0); + if(ctx.length === 1) { + ctx = ctx[0]; + } - function replaceParseType(string) { - var regexp = / rdf:parseType="Resource"/g; - return string.replace(regexp, ''); - } + // 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]; + } + } - function replaceSlashInID(string) { - return string.replace(new RegExp(/rdf:about="\//g), 'rdf:about="'); - } - - var result = replaceSlashInID(replaceParseType(replaceLi(replaceBag(serialize)))); - - return result; + callback(null, compacted, activeCtx); + } }; /** - * @param {Element} xml - * @return {RdfElement} + * 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. */ -RdfElement.fromString = function (stringXml) { - - var rdfElement = new RdfElement(); - var graph = $rdf.graph(); +jsonld.expand = function(input, options, callback) { + if(arguments.length < 1) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not expand, too few arguments.')); + }); + } - // 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'); + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; - var parser = N3.Parser(); - var store = N3.Store(); - store.addPrefixes(Util.prefixes); - store.addTriples(parser.parse(turtle)); - - rdfElement.store = store; + // set default options + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } + if(!('keepFreeFloatingNodes' in options)) { + options.keepFreeFloatingNodes = false; + } - return rdfElement; -}; + 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}); + }); -RdfElement.fromXML = function (string) { - var rdfElement; - function fn (err, result) { - rdfElement = RdfElement.fromObj(result); + 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} }; - utils.parseStringKeepPrefix(string, fn); - return rdfElement; -}; + 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); + } -RdfElement.prototype.buildJsObj = function () { - var rdfElementJsObj; - function fn (err, result) { - rdfElementJsObj = result; - }; - utils.parseStringKeepPrefix(this.toXML(), fn); - return rdfElementJsObj; -}; + var expanded; + try { + var processor = new Processor(); + var activeCtx = _getInitialContext(options); + var document = input.document; + var remoteContext = input.remoteContext['@context']; -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]); - } + // process optional expandContext + if(input.expandContext) { + activeCtx = processor.processContext( + activeCtx, input.expandContext['@context'], options); + } - var rdfElement = new ns.RdfElement(); - jsObj = jsObj['rdf:RDF']; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return rdfElement; - } + // process remote context from HTTP Link Header + if(remoteContext) { + activeCtx = processor.processContext( + activeCtx, remoteContext, options); + } - var obj = {}; - obj['rdf:RDF'] = jsObj; - rdfElement = ns.RdfElement.fromString(utils.buildString(obj)); + // expand document + expanded = processor.expand( + activeCtx, null, document, options, false); - return rdfElement; -}; + // 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 = []; + } -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()); + // normalize to an array + if(!_isArray(expanded)) { + expanded = [expanded]; + } + } catch(ex) { + return callback(ex); + } + callback(null, expanded); + }); + } }; -ns.RdfElement = RdfElement; -// ------- END RDFELEMENT ------- - - -ns.rdflib = $rdf; -ns.Util = Util; - -module.exports = ns; - -},{"./annotation-utils":26,"./utilities":30,"n3":31,"rdflib":57}],28:[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. - * - * 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. - * - * See {@link Extension} for more general information on extensions in the SBGN-ML format. - * - * You can access the following classes like this: libsbgn.render.ColorDefinition + * Performs JSON-LD flattening. * - * @module libsbgn-render - * @namespace libsbgn.render -*/ - -var utils = _dereq_('./utilities'); -var checkParams = utils.checkParams; -var xml2js = _dereq_('xml2js'); - -var ns = {}; + * @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.')); + }); + } -ns.xmlns = "http://www.sbml.org/sbml/level3/version1/render/version1"; + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } else if(typeof ctx === 'function') { + callback = ctx; + ctx = null; + options = {}; + } + options = options || {}; -// ------- COLORDEFINITION ------- -/** - * Represents the <colorDefinition> element. - * @class - * @param {Object} params - * @param {string=} params.id - * @param {string=} params.value - */ -var ColorDefinition = function(params) { - var params = checkParams(params, ['id', 'value']); - this.id = params.id; - this.value = params.value; -}; + // set default options + if(!('base' in options)) { + options.base = (typeof input === 'string') ? input : ''; + } + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } -/** - * @return {Object} - xml2js formatted object - */ -ColorDefinition.prototype.buildJsObj = function () { - var colordefObj = {}; + // 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})); + } - // 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; -}; + var flattened; + try { + // do flattening + flattened = new Processor().flatten(_input); + } catch(ex) { + return callback(ex); + } -/** - * @return {string} - */ -ColorDefinition.prototype.toXML = function () { - return utils.buildString({colorDefinition: this.buildJsObj()}) -}; + if(ctx === null) { + return callback(null, flattened); + } -/** - * @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; + // 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); + }); + }); }; /** - * @param {Object} jsObj - xml2js formatted object - * @return {ColorDefinition} + * 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. */ -ColorDefinition.fromObj = function (jsObj) { - if (typeof jsObj.colorDefinition == 'undefined') { - throw new Error("Bad XML provided, expected tagName colorDefinition, got: " + Object.keys(jsObj)[0]); - } +jsonld.frame = function(input, frame, options, callback) { + if(arguments.length < 2) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not frame, too few arguments.')); + }); + } - var colorDefinition = new ns.ColorDefinition(); - jsObj = jsObj.colorDefinition; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return colorDefinition; - } + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - colorDefinition.id = attributes.id || null; - colorDefinition.value = attributes.value || null; - } - return colorDefinition; -}; - -ns.ColorDefinition = ColorDefinition; -// ------- END COLORDEFINITION ------- - -// ------- LISTOFCOLORDEFINITIONS ------- -/** - * Represents the <listOfColorDefinitions> element. - * @class - */ -var ListOfColorDefinitions = function () { - this.colorDefinitions = []; - this.colorIndex = {}; -}; + // 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; -/** - * @param {ColorDefinition} colorDefinition - */ -ListOfColorDefinitions.prototype.addColorDefinition = function (colorDefinition) { - this.colorDefinitions.push(colorDefinition); - this.colorIndex[colorDefinition.id] = colorDefinition.value; -}; + 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}); + }); -/** - * Convenient method to get a color value directly. - * @param {string} id - * @return {string} - */ -ListOfColorDefinitions.prototype.getColorById = function (id) { - return this.colorIndex[id]; -}; + 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 = {}; + } -/** - * Convenient method to get all the color values in the list. - * @return {string[]} - */ -ListOfColorDefinitions.prototype.getAllColors = function () { - return Object.values(this.colorIndex); -}; + // 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})); + } -/** - * @return {Object} - xml2js formatted object - */ -ListOfColorDefinitions.prototype.buildJsObj = function () { - var listOfColorDefinitionsObj = {}; + // 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})); + } - for(var i=0; i < this.colorDefinitions.length; i++) { - if (i==0) { - listOfColorDefinitionsObj.colorDefinition = []; - } - listOfColorDefinitionsObj.colorDefinition.push(this.colorDefinitions[i].buildJsObj()); - } + var framed; + try { + // do framing + framed = new Processor().frame(expanded, expandedFrame, opts); + } catch(ex) { + return callback(ex); + } - return listOfColorDefinitionsObj; + // 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); + }); + }); + }); + } }; /** - * @return {string} + * **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. */ -ListOfColorDefinitions.prototype.toXML = function () { - return utils.buildString({listOfColorDefinitions: this.buildJsObj()}) +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); }; /** - * @param {String} string - * @return {ListOfColorDefinitions} + * **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. */ -ListOfColorDefinitions.fromXML = function (string) { - var listOfColorDefinitions; - function fn (err, result) { - listOfColorDefinitions = ListOfColorDefinitions.fromObj(result); - }; - utils.parseString(string, fn); - return listOfColorDefinitions; -}; +jsonld.objectify = function(input, ctx, options, callback) { + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; -/** - * @param {Object} jsObj - xml2js formatted object - * @return {ListOfColorDefinitions} - */ -ListOfColorDefinitions.fromObj = function (jsObj) { - if (typeof jsObj.listOfColorDefinitions == 'undefined') { - throw new Error("Bad XML provided, expected tagName listOfColorDefinitions, got: " + Object.keys(jsObj)[0]); - } + // set default options + if(!('base' in options)) { + options.base = (typeof input === 'string') ? input : ''; + } + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } - var listOfColorDefinitions = new ns.ListOfColorDefinitions(); - jsObj = jsObj.listOfColorDefinitions; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return listOfColorDefinitions; - } + // 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})); + } - // children - if(jsObj.colorDefinition) { - var colorDefinitions = jsObj.colorDefinition; - for (var i=0; i < colorDefinitions.length; i++) { - var colorDefinition = ns.ColorDefinition.fromObj({colorDefinition: colorDefinitions[i]}); - listOfColorDefinitions.addColorDefinition(colorDefinition); - } - } + var flattened; + try { + // flatten the graph + flattened = new Processor().flatten(_input); + } catch(ex) { + return callback(ex); + } - return listOfColorDefinitions; -}; + // 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]; -ns.ListOfColorDefinitions = ListOfColorDefinitions; -// ------- END LISTOFCOLORDEFINITIONS ------- + var recurse = function(subject) { + // can't replace just a string + if(!_isObject(subject) && !_isArray(subject)) { + return; + } -// ------- 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.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 - */ -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', 'textAnchor', 'vtextAnchor', 'fill', 'id', 'stroke', 'strokeWidth']); - // specific to renderGroup - this.fontSize = params.fontSize; - this.fontFamily = params.fontFamily; - this.fontWeight = params.fontWeight; - this.fontStyle = params.fontStyle; - 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; -}; + // bottom out recursion on re-visit + if(_isObject(subject)) { + if(recurse.visited[subject['@id']]) { + return; + } + recurse.visited[subject['@id']] = true; + } -/** - * @return {Object} - xml2js formatted object - */ -RenderGroup.prototype.buildJsObj = function () { - var renderGroupObj = {}; + // each array element *or* object key + for(var k in subject) { + var obj = subject[k]; + var isid = (jsonld.getContextValue(ctx, k, '@type') === '@id'); - // 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.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; - } - utils.addAttributes(renderGroupObj, attributes); - return renderGroupObj; -}; + // can't replace a non-object or non-array unless it's an @id + if(!_isArray(obj) && !_isObject(obj) && !isid) { + continue; + } -/** - * @return {string} - */ -RenderGroup.prototype.toXML = function () { - return utils.buildString({g: this.buildJsObj()}) -}; + 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); -/** - * @param {String} string - * @return {RenderGroup} - */ -RenderGroup.fromXML = function (string) { - var g; - function fn (err, result) { - g = RenderGroup.fromObj(result); - }; - utils.parseString(string, fn); - return g; + 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); + }); + }); }; /** - * @param {Object} jsObj - xml2js formatted object - * @return {RenderGroup} + * 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. */ -RenderGroup.fromObj = function (jsObj) { - if (typeof jsObj.g == 'undefined') { - throw new Error("Bad XML provided, expected tagName g, got: " + Object.keys(jsObj)[0]); - } +jsonld.normalize = function(input, options, callback) { + if(arguments.length < 1) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not normalize, too few arguments.')); + }); + } - var g = new ns.RenderGroup(); - jsObj = jsObj.g; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return g; - } + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - g.id = attributes.id || null; - g.fontSize = attributes.fontSize || null; - g.fontFamily = attributes.fontFamily || null; - g.fontWeight = attributes.fontWeight || null; - g.fontStyle = attributes.fontStyle || null; - g.textAnchor = attributes.textAnchor || null; - g.vtextAnchor = attributes.vtextAnchor || null; - g.stroke = attributes.stroke || null; - g.strokeWidth = attributes.strokeWidth || null; - g.fill = attributes.fill || null; - } - return g; -}; + // 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; + } -ns.RenderGroup = RenderGroup; -// ------- END RENDERGROUP ------- + 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); + }); + } +}; -// ------- 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 + * 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. */ -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.fromRDF = function(dataset, options, callback) { + if(arguments.length < 1) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not convert from RDF, too few arguments.')); + }); + } -/** - * @param {RenderGroup} renderGroup - */ -Style.prototype.setRenderGroup = function (renderGroup) { - this.renderGroup = renderGroup; -}; + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; -/** - * @return {string[]} - */ -Style.prototype.getIdListAsArray = function () { - return this.idList.split(' '); -} + // set default options + if(!('useRdfType' in options)) { + options.useRdfType = false; + } + if(!('useNativeTypes' in options)) { + options.useNativeTypes = false; + } -/** - * @param {string[]} idArray - */ -Style.prototype.setIdListFromArray = function (idArray) { - this.idList = idArray.join(' '); -} + if(!('format' in options) && _isString(dataset)) { + // set default format to nquads + if(!('format' in options)) { + options.format = 'application/nquads'; + } + } -/** - * 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; -}; -/** - * @return {Object} - xml2js formatted object - */ -Style.prototype.buildJsObj = function () { - var styleObj = {}; + 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; + }; + } - // 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); + 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); + } - // children - if(this.renderGroup != null) { - styleObj.g = this.renderGroup.buildJsObj(); - } - return styleObj; + function fromRDF(dataset, options, callback) { + // convert from RDF + new Processor().fromRDF(dataset, options, callback); + } + }); }; /** - * @return {string} + * 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. */ -Style.prototype.toXML = function () { - return utils.buildString({style: this.buildJsObj()}) +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.')); + }); + } + + // get arguments + 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; + } + + // 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})); + } + + 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); + }); }; /** - * @param {String} string - * @return {Style} + * **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. */ -Style.fromXML = function (string) { - var style; - function fn (err, result) { - style = Style.fromObj(result); - }; - utils.parseString(string, fn); - return style; +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.')); + }); + } + + // get arguments + 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; + } + + // 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 nodeMap; + try { + nodeMap = new Processor().createNodeMap(_input, options); + } catch(ex) { + return callback(ex); + } + + callback(null, nodeMap); + }); }; /** - * @param {Object} jsObj - xml2js formatted object - * @return {Style} + * **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. */ -Style.fromObj = function (jsObj) { - if (typeof jsObj.style == 'undefined') { - throw new Error("Bad XML provided, expected tagName style, got: " + Object.keys(jsObj)[0]); - } +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.')); + }); + } - var style = new ns.Style(); - jsObj = jsObj.style; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return style; - } + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } else if(typeof ctx === 'function') { + callback = ctx; + ctx = null; + options = {}; + } + options = options || {}; - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - style.id = attributes.id || null; - style.name = attributes.name || null; - style.idList = attributes.idList || null; - } + // 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); + } - // children - if(jsObj.g) { - var g = ns.RenderGroup.fromObj({g: jsObj.g[0]}); - style.setRenderGroup(g); - } + 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); + } + } - return style; -}; + function merge(expanded) { + var mergeNodes = true; + if('mergeNodes' in options) { + mergeNodes = options.mergeNodes; + } -ns.Style = Style; -// ------- END STYLE ------- + var issuer = options.namer || options.issuer || new IdentifierIssuer('_:b'); + var graphs = {'@default': {}}; -// ------- LISTOFSTYLES ------- -/** - * Represents the <listOfStyles> element. - * @class - */ -var ListOfStyles = function() { - this.styles = []; -}; + 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 + '-') + }); -/** - * @param {Style} style - */ -ListOfStyles.prototype.addStyle = function (style) { - this.styles.push(style); -}; + // 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); -/** - * 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; -} + 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]; + } + } + } + } + } -/** - * @return {Object} - xml2js formatted object - */ -ListOfStyles.prototype.buildJsObj = function () { - var listOfStylesObj = {}; + // add all non-default graphs to default graph + defaultGraph = _mergeNodeMaps(graphs); + } catch(ex) { + return callback(ex); + } - for(var i=0; i < this.styles.length; i++) { - if (i==0) { - listOfStylesObj.style = []; - } - listOfStylesObj.style.push(this.styles[i].buildJsObj()); - } + // 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 listOfStylesObj; + if(ctx === null) { + return callback(null, flattened); + } + + // 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); + }); + } }; /** - * @return {string} + * 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). */ -ListOfStyles.prototype.toXML = function () { - return utils.buildString({listOfStyles: this.buildJsObj()}) +jsonld.relabelBlankNodes = function(input, options) { + options = options || {}; + var issuer = options.namer || options.issuer || new IdentifierIssuer('_:b'); + return _labelBlankNodes(issuer, input); }; /** - * @param {String} string - * @return {ListOfStyles} + * Prepends a base IRI to the given relative IRI. + * + * @param base the base IRI. + * @param iri the relative IRI. + * + * @return the absolute IRI. */ -ListOfStyles.fromXML = function (string) { - var listOfStyles; - function fn (err, result) { - listOfStyles = ListOfStyles.fromObj(result); - }; - utils.parseString(string, fn); - return listOfStyles; +jsonld.prependBase = function(base, iri) { + return _prependBase(base, iri); }; /** - * @param {Object} jsObj - xml2js formatted object - * @return {ListOfStyles} - */ -ListOfStyles.fromObj = function (jsObj) { - if (typeof jsObj.listOfStyles == 'undefined') { - throw new Error("Bad XML provided, expected tagName listOfStyles, got: " + Object.keys(jsObj)[0]); - } - - var listOfStyles = new ns.ListOfStyles(); - jsObj = jsObj.listOfStyles; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return listOfStyles; - } - - // children - if(jsObj.style) { - var styles = jsObj.style; - for (var i=0; i < styles.length; i++) { - var style = ns.Style.fromObj({style: styles[i]}); - listOfStyles.addStyle(style); - } - } - - return listOfStyles; -}; - -ns.ListOfStyles = ListOfStyles; -// ------- END LISTOFSTYLES ------- - -// ------- 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 + * 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. */ -var RenderInformation = function (params) { - var params = checkParams(params, ['id', 'name', 'programName', - 'programVersion', 'backgroundColor', 'listOfColorDefinitions', 'listOfStyles']); - 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; +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 {ListOfColorDefinitions} listOfColorDefinitions + * Deprecated default document loader. Use or override jsonld.documentLoader + * instead. */ -RenderInformation.prototype.setListOfColorDefinitions = function(listOfColorDefinitions) { - this.listOfColorDefinitions = listOfColorDefinitions; +jsonld.loadDocument = function(url, callback) { + var promise = jsonld.documentLoader(url, callback); + if(promise && 'then' in promise) { + promise.then(callback.bind(null, null), callback); + } }; -/** - * @param {ListOfStyles} listOfStyles - */ -RenderInformation.prototype.setListOfStyles = function(listOfStyles) { - this.listOfStyles = listOfStyles; -}; +/* Promises API */ /** - * @return {Object} - xml2js formatted object + * 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. */ -RenderInformation.prototype.buildJsObj = function () { - var renderInformationObj = {}; +jsonld.promises = function(options) { + options = options || {}; + var slice = Array.prototype.slice; + var promisify = jsonld.promisify; - // 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); + // 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 = {}; + } - // children - if(this.listOfColorDefinitions != null) { - renderInformationObj.listOfColorDefinitions = this.listOfColorDefinitions.buildJsObj(); - } - if(this.listOfStyles != null) { - renderInformationObj.listOfStyles = this.listOfStyles.buildJsObj(); - } - return renderInformationObj; -}; + // 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 -/** - * @return {string} - */ -RenderInformation.prototype.toXML = function() { - return utils.buildString({renderInformation: this.buildJsObj()}) -}; + 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))); + }; -/** - * @param {String} string - * @return {RenderInformation} - */ -RenderInformation.fromXML = function (string) { - var renderInformation; - function fn (err, result) { - renderInformation = RenderInformation.fromObj(result); + 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))); }; - utils.parseString(string, fn); - return renderInformation; + 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))); + }; + } + + 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; + } + } + + return api; }; /** - * @param {Object} jsObj - xml2js formatted object - * @return {RenderInformation} + * Converts a node.js async op into a promise w/boxed resolved value(s). + * + * @param op the operation to convert. + * + * @return the promise. */ -RenderInformation.fromObj = function (jsObj) { - if (typeof jsObj.renderInformation == 'undefined') { - throw new Error("Bad XML provided, expected tagName renderInformation, got: " + Object.keys(jsObj)[0]); - } - - var renderInformation = new ns.RenderInformation(); - jsObj = jsObj.renderInformation; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return renderInformation; - } +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); + } + })); + }); +}; - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - renderInformation.id = attributes.id || null; - renderInformation.name = attributes.name || null; - renderInformation.programName = attributes.programName || null; - renderInformation.programVersion = attributes.programVersion || null; - renderInformation.backgroundColor = attributes.backgroundColor || null; - } +// extend jsonld.promises w/jsonld.js methods +jsonld.promises({api: jsonld.promises}); - // children - if(jsObj.listOfColorDefinitions) { - var listOfColorDefinitions = ns.ListOfColorDefinitions.fromObj({listOfColorDefinitions: jsObj.listOfColorDefinitions[0]}); - renderInformation.setListOfColorDefinitions(listOfColorDefinitions); - } - if(jsObj.listOfStyles) { - var listOfStyles = ns.ListOfStyles.fromObj({listOfStyles: jsObj.listOfStyles[0]}); - renderInformation.setListOfStyles(listOfStyles); - } +/* WebIDL API */ - return renderInformation; +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; -ns.RenderInformation = RenderInformation; -// ------- END RENDERINFORMATION ------- - -module.exports = ns; -},{"./utilities":30,"xml2js":25}],29:[function(_dereq_,module,exports){ -/** - * The API contains two other submodules: {@link libsbgn.render} and {@link libsbgn.annot} - * @module libsbgn - * @namespace libsbgn -*/ +// 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; + } +} -var renderExt = _dereq_('./libsbgn-render'); -var annotExt = _dereq_('./libsbgn-annotations'); -var xml2js = _dereq_('xml2js'); -var utils = _dereq_('./utilities'); -var checkParams = utils.checkParams; +if(canDefineProperty) { + Object.defineProperty(JsonLdProcessor, 'prototype', { + writable: false, + enumerable: false + }); + Object.defineProperty(JsonLdProcessor.prototype, 'constructor', { + writable: true, + enumerable: false, + configurable: true, + value: JsonLdProcessor + }); +} -var ns = {}; // namespace that encapsulates all exportable features +// 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; + } +} -ns.xmlns = "http://sbgn.org/libsbgn/0.3"; +/* Utility API */ -// ------- 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; -}; +// define setImmediate and nextTick +//// nextTick implementation with browser-compatible fallback //// +// from https://github.com/caolan/async/blob/master/lib/async.js -/** - * Allows inheriting objects to get an extension element. - * @param {Extension} extension - */ -SBGNBase.prototype.setExtension = function (extension) { - this.extension = extension; -}; +// capture the global reference to guard against fakeTimer mocks +var _setImmediate = typeof setImmediate === 'function' && setImmediate; -/** - * Allows inheriting objects to get a notes element. - * @param {Notes} notes - */ -SBGNBase.prototype.setNotes = function (notes) { - this.notes = notes; +var _delay = _setImmediate ? function(fn) { + // not a direct alias (for IE10 compatibility) + _setImmediate(fn); +} : function(fn) { + setTimeout(fn, 0); }; -/** - * 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(typeof process === 'object' && typeof process.nextTick === 'function') { + jsonld.nextTick = process.nextTick; +} else { + jsonld.nextTick = _delay; +} +jsonld.setImmediate = _setImmediate ? _delay : jsonld.nextTick; /** - * Get the appropriate properties from jsObj. - * @param {Object} jsObj - xml2js formatted object + * 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. */ -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); - } +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; }; -ns.SBGNBase = SBGNBase; -// ------- END SBGNBase ------- -// ------- SBGN ------- /** - * Represents the <sbgn> element. - * @class - * @extends SBGNBase - * @param {Object} params - * @param {string=} params.xmlns - * @param {Map[]=} params.maps + * Creates a simple queue for requesting documents. */ -var Sbgn = function (params) { - ns.SBGNBase.call(this, params); - var params = checkParams(params, ['xmlns', 'maps']); - this.xmlns = params.xmlns; - this.maps = params.maps || []; +jsonld.RequestQueue = function() { + this._requests = {}; }; - -Sbgn.prototype = Object.create(ns.SBGNBase.prototype); -Sbgn.prototype.constructor = Sbgn; - -/** - * @param {Map} map - */ -Sbgn.prototype.addMap = function (map) { - this.maps.push(map); +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; -/** - * @return {Object} - xml2js formatted object - */ -Sbgn.prototype.buildJsObj = function () { - var sbgnObj = {}; + // callback must be given if not using promises + if(!callback && !self._usePromise) { + throw new Error('callback must be specified.'); + } - // attributes - var attributes = {}; - if(this.xmlns != null) { - attributes.xmlns = this.xmlns; - } - utils.addAttributes(sbgnObj, attributes); + // 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); + }); + }); + } - // 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; + // 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); + } + }); + } }; /** - * @return {string} + * 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. */ -Sbgn.prototype.toXML = function () { - return utils.buildString({sbgn: this.buildJsObj()}); +jsonld.DocumentCache = function(size) { + this.order = []; + this.cache = {}; + this.size = size || 50; + this.expires = 30 * 1000; }; - -/** - * @param {String} string - * @return {Sbgn} - */ -Sbgn.fromXML = function (string) { - var sbgn; - function fn (err, result) { - sbgn = Sbgn.fromObj(result); +jsonld.DocumentCache.prototype.get = function(url) { + if(url in this.cache) { + var entry = this.cache[url]; + if(entry.expires >= +new Date()) { + return entry.ctx; } - 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]); - } - - var sbgn = new ns.Sbgn(); - jsObj = jsObj.sbgn; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return sbgn; - } - - if(jsObj.$) { // we have some atributes - var attributes = jsObj.$; - sbgn.xmlns = attributes.xmlns || null; - - // 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"); - } - } - } - - 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); - } - } - - sbgn.baseFromObj(jsObj); // call to parent class - return sbgn; + delete this.cache[url]; + this.order.splice(this.order.indexOf(url), 1); + } + return null; }; -ns.Sbgn = Sbgn; -// ------- END SBGN ------- - -// ------- 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 || []; +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)}; }; -Map.prototype = Object.create(ns.SBGNBase.prototype); -Map.prototype.constructor = Map; - /** - * @param {Glyph} glyph + * Creates an active context cache. + * + * @param size the maximum size of the cache. */ -Map.prototype.addGlyph = function (glyph) { - this.glyphs.push(glyph); +jsonld.ActiveContextCache = function(size) { + this.order = []; + this.cache = {}; + this.size = size || 100; }; - -/** - * @param {Arc} arc - */ -Map.prototype.addArc = function (arc) { - this.arcs.push(arc); +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; }; - -/** - * @param {Bbox} bbox - */ -Map.prototype.setBbox = function (bbox) { - this.bbox = bbox; +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); }; /** - * @param {Arcgroup} arc + * Default JSON-LD cache. */ -Map.prototype.addArcgroup = function (arcgroup) { - this.arcgroups.push(arcgroup); +jsonld.cache = { + activeCtx: new jsonld.ActiveContextCache() }; /** - * @param {string} class_ - * @return {Gyph[]} + * Document loaders. */ -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; -}; +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. */ -Map.prototype.buildJsObj = function () { - var mapObj = {}; +jsonld.documentLoaders.jquery = function($, options) { + options = options || {}; + var queue = new jsonld.RequestQueue(); - // 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); + // 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); - // 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; -}; + 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} - */ -Map.prototype.toXML = function () { - return utils.buildString({map: 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 {Map} - */ -Map.fromXML = function (string) { - var map; - function fn (err, result) { - map = Map.fromObj(result); - }; - utils.parseString(string, fn); - return map; + 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 {Map} + * 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. */ -Map.fromObj = function (jsObj) { - if (typeof jsObj.map == 'undefined') { - throw new Error("Bad XML provided, expected tagName map, got: " + Object.keys(jsObj)[0]); - } - - var map = new ns.Map(); - jsObj = jsObj.map; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return map; - } +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(); - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - map.id = attributes.id || null; - map.language = attributes.language || null; - map.version = attributes.version || null; - } + 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); + }); - 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); - } - } + 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); - map.baseFromObj(jsObj); - return map; -}; + function handleResponse(err, res, body) { + doc = {contextUrl: null, documentUrl: url, document: body || null}; -ns.Map = Map; -// ------- END MAP ------- + // 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); + } -// ------- 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 = {}; -}; + // 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; + } + } -/** - * @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; - } - } + // 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); + } + } }; /** - * @param {string} extensionName - * @return {boolean} + * 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. */ -Extension.prototype.has = function (extensionName) { - return this.list.hasOwnProperty(extensionName); -}; +jsonld.documentLoaders.xhr = function(options) { + options = options || {}; + var rlink = /(^|(\r\n))link:/i; + var queue = new jsonld.RequestQueue(); -/** - * @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; - } -}; + // 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 {Object} - xml2js formatted object - */ -Extension.prototype.buildJsObj = function () { - var extensionObj = {}; + 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}); + } - 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; -}; + var doc = {contextUrl: null, documentUrl: url, document: req.response}; -/** - * @return {string} - */ -Extension.prototype.toXML = function () { - return utils.buildString({extension: this.buildJsObj()}) + // 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 {Extension} + * 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. */ -Extension.fromXML = function (string) { - var extension; - function fn (err, result) { - extension = Extension.fromObj(result); - }; - utils.parseString(string, fn); - return extension; +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 {Extension} + * 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. */ -Extension.fromObj = function (jsObj) { - if (typeof jsObj.extension == 'undefined') { - throw new Error("Bad XML provided, expected tagName extension, got: " + Object.keys(jsObj)[0]); - } - - var extension = new Extension(); - jsObj = jsObj.extension; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return extension; - } +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 children = Object.keys(jsObj); - for (var extName in jsObj) { - //var extName = Object.keys(jsObj[i])[0]; - var extJsObj = jsObj[extName]; + // set default options + if(!('base' in options)) { + options.base = ''; + } + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } - //extension.add(extInstance); - if (extName == '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 - } - } + // return initial context early for null context + if(localCtx === null) { + return callback(null, _getInitialContext(options)); + } - return extension; + // 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); + }); }; -ns.Extension = Extension; -// ------- END EXTENSION ------- - -// ------- NOTES ------- /** - * Represents the <notes> element. - * Its single content attribute stores xhtml elements as string. - * @class + * 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 Notes = function () { - this.content = ""; +jsonld.hasProperty = function(subject, property) { + var rval = false; + if(property in subject) { + var value = subject[property]; + rval = (!_isArray(value) || value.length > 0); + } + return rval; }; /** - * Overwrite the content. - * @param {String} string + * 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. */ -Notes.prototype.setContent = function (string) { - this.content = string; +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; }; /** - * @param {String} 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). */ -Notes.prototype.appendContent = function (string) { - this.content += string; -}; +jsonld.addValue = function(subject, property, value, options) { + options = options || {}; + if(!('propertyIsArray' in options)) { + options.propertyIsArray = false; + } + if(!('allowDuplicate' in options)) { + options.allowDuplicate = true; + } -/** - * @return {Object} - xml2js formatted object - */ -Notes.prototype.buildJsObj = function () { + 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)); - var parsedContent = ""; - if(this.content != "") { // xml2js refuses to parse empty strings - utils.parseString(this.content, function (err, result) { - parsedContent = result; - }); - } + // make property an array if value not present or always an array + if(!_isArray(subject[property]) && + (!hasValue || options.propertyIsArray)) { + subject[property] = [subject[property]]; + } - return parsedContent; + // 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 {string} + * 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. */ -Notes.prototype.toXML = function () { - return utils.buildString({notes: this.buildJsObj()}) +jsonld.getValues = function(subject, property) { + var rval = subject[property] || []; + if(!_isArray(rval)) { + rval = [rval]; + } + return rval; }; /** - * @param {String} string - * @return {Notes} + * Removes a property from a subject. + * + * @param subject the subject. + * @param property the property. */ -Notes.fromXML = function (string) { - var notes; - function fn (err, result) { - notes = Notes.fromObj(result); - }; - utils.parseString(string, fn); - return notes; +jsonld.removeProperty = function(subject, property) { + delete subject[property]; }; /** - * @param {Object} jsObj - xml2js formatted object - * @return {Notes} + * 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). */ -Notes.fromObj = function (jsObj) { - if (typeof jsObj.notes == 'undefined') { - throw new Error("Bad XML provided, expected tagName notes, got: " + Object.keys(jsObj)[0]); - } - - var notes = new Notes(); - jsObj = jsObj.notes; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return notes; - } +jsonld.removeValue = function(subject, property, value, options) { + options = options || {}; + if(!('propertyIsArray' in options)) { + options.propertyIsArray = false; + } - 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 + // filter out value + var values = jsonld.getValues(subject, property).filter(function(e) { + return !jsonld.compareValues(e, value); + }); - return notes; + if(values.length === 0) { + jsonld.removeProperty(subject, property); + } else if(values.length === 1 && !options.propertyIsArray) { + subject[property] = values[0]; + } else { + subject[property] = values; + } }; -ns.Notes = Notes; -// ------- END NOTES ------- - -// ------- 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 {StateType=} params.state - * @param {CloneType=} params.clone - * @param {Callout=} params.callout - * @param {EntityType=} params.entity - * @param {Glyph[]=} params.glyphMembers - * @param {Port[]=} params.ports + * 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. */ -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; +jsonld.compareValues = function(v1, v2) { + // 1. equal primitives + if(v1 === v2) { + return 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 || []; -}; + // 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; + } -Glyph.prototype = Object.create(ns.SBGNBase.prototype); -Glyph.prototype.constructor = Glyph; + // 3. equal @ids + if(_isObject(v1) && ('@id' in v1) && _isObject(v2) && ('@id' in v2)) { + return v1['@id'] === v2['@id']; + } -/** - * @param {Label} label - */ -Glyph.prototype.setLabel = function (label) { - this.label = label; + return false; }; /** - * @param {StateType} state + * 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. */ -Glyph.prototype.setState = function (state) { - this.state = state; -}; +jsonld.getContextValue = function(ctx, key, type) { + var rval = null; -/** - * @param {Bbox} bbox - */ -Glyph.prototype.setBbox = function (bbox) { - this.bbox = bbox; -}; + // return null for invalid key + if(key === null) { + return rval; + } -/** - * @param {CloneType} clone - */ -Glyph.prototype.setClone = function (clone) { - this.clone = clone; -}; + // get default language + if(type === '@language' && (type in ctx)) { + rval = ctx[type]; + } -/** - * @param {Callout} callout - */ -Glyph.prototype.setCallout = function (callout) { - this.callout = callout; -}; + // get specific entry information + if(ctx.mappings[key]) { + var entry = ctx.mappings[key]; -/** - * @param {EntityType} entity - */ -Glyph.prototype.setEntity = function (entity) { - this.entity = entity; + 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 = {}; + /** - * @param {Glyph} glyphMember + * 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). */ -Glyph.prototype.addGlyphMember = function (glyphMember) { - this.glyphMembers.push(glyphMember); +jsonld.registerRDFParser = function(contentType, parser) { + _rdfParsers[contentType] = parser; }; /** - * @param {Port} port + * Unregisters an RDF dataset parser by content-type. + * + * @param contentType the content-type for the parser. */ -Glyph.prototype.addPort = function (port) { - this.ports.push(port); +jsonld.unregisterRDFParser = function(contentType) { + delete _rdfParsers[contentType]; }; -/** - * @return {Object} - xml2js formatted object - */ -Glyph.prototype.buildJsObj = function () { - var glyphObj = {}; +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 + }; + } +} - // 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); +// 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 - 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; -}; +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; /** - * @return {string} + * A JSON-LD Error. + * + * @param msg the error message. + * @param type the error type. + * @param details the error details. */ -Glyph.prototype.toXML = function () { - return utils.buildString({glyph: this.buildJsObj()}) +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(); +} /** - * @param {String} string - * @return {Glyph} + * Constructs a new JSON-LD Processor. */ -Glyph.fromXML = function (string) { - var glyph; - function fn (err, result) { - glyph = Glyph.fromObj(result); - }; - utils.parseString(string, fn); - return glyph; -}; +var Processor = function() {}; /** - * @param {Object} jsObj - xml2js formatted object - * @return {Glyph} + * 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. */ -Glyph.fromObj = function (jsObj) { - if (typeof jsObj.glyph == 'undefined') { - throw new Error("Bad XML provided, expected tagName glyph, got: " + Object.keys(jsObj)[0]); - } - - var glyph = new ns.Glyph(); - jsObj = jsObj.glyph; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return glyph; - } - - 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; - } +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; + } - // children - if(jsObj.label) { - var label = ns.Label.fromObj({label: jsObj.label[0]}); - glyph.setLabel(label); - } - if(jsObj.state) { - var state = ns.StateType.fromObj({state: jsObj.state[0]}); - glyph.setState(state); - } - if(jsObj.clone) { - var clone = ns.CloneType.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.EntityType.fromObj({entity: jsObj.entity[0]}); - glyph.setEntity(entity); - } - if(jsObj.bbox) { - var bbox = ns.Bbox.fromObj({bbox: jsObj.bbox[0]}); - glyph.setBbox(bbox); - } + // 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; + } + } + } - 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); - } - } + // 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; + } - glyph.baseFromObj(jsObj); - return glyph; -}; + // FIXME: avoid misuse of active property as an expanded property? + var insideReverse = (activeProperty === '@reverse'); -ns.Glyph = Glyph; -// ------- END GLYPH ------- + var rval = {}; -// ------- 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; -}; + 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}); + } -Label.prototype = Object.create(ns.SBGNBase.prototype); -Label.prototype.constructor = ns.Label; + // 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 {Bbox} bbox - */ -Label.prototype.setBbox = function (bbox) { - this.bbox = bbox; -}; + // compact @id and @type(s) + if(expandedProperty === '@id' || expandedProperty === '@type') { + var compactedValue; -/** - * @return {Object} - xml2js formatted object - */ -Label.prototype.buildJsObj = function () { - var labelObj = {}; + // 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})); + } + } - // 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); + // 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; + } - this.baseToJsObj(labelObj); - if(this.bbox != null) { - labelObj.bbox = this.bbox.buildJsObj(); - } - return labelObj; -}; + // handle @reverse + if(expandedProperty === '@reverse') { + // recursively compact expanded value + var compactedValue = this.compact( + activeCtx, '@reverse', expandedValue, options); -/** - * @return {string} - */ -Label.prototype.toXML = function () { - return utils.buildString({label: this.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]; + } + } -/** - * @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; -}; + if(Object.keys(compactedValue).length > 0) { + // use keyword alias and add value + var alias = _compactIri(activeCtx, expandedProperty); + jsonld.addValue(rval, alias, compactedValue); + } -/** - * @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]); - } + continue; + } - var label = new ns.Label(); - jsObj = jsObj.label; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return label; - } + // 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(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - label.text = attributes.text || null; - } + // use keyword alias and add value + var alias = _compactIri(activeCtx, expandedProperty); + jsonld.addValue(rval, alias, expandedValue); + continue; + } - if(jsObj.bbox) { - var bbox = ns.Bbox.fromObj({bbox: jsObj.bbox[0]}); - label.setBbox(bbox); - } - label.baseFromObj(jsObj); - return label; -}; + // 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; + } -ns.Label = Label; -// ------- END LABEL ------- + // Note: expanded value must be an array due to expansion algorithm. -// ------- 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 - */ -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); -}; + // preserve empty arrays + if(expandedValue.length === 0) { + var itemActiveProperty = _compactIri( + activeCtx, expandedProperty, expandedValue, {vocab: true}, + insideReverse); + jsonld.addValue( + rval, itemActiveProperty, expandedValue, {propertyIsArray: true}); + } -Bbox.prototype = Object.create(ns.SBGNBase.prototype); -Bbox.prototype.constructor = ns.Bbox; + // recusively process array values + for(var vi = 0; vi < expandedValue.length; ++vi) { + var expandedItem = expandedValue[vi]; -/** - * @return {Object} - xml2js formatted object - */ -Bbox.prototype.buildJsObj = function () { - var bboxObj = {}; + // compact property and get container type + var itemActiveProperty = _compactIri( + activeCtx, expandedProperty, expandedItem, {vocab: true}, + insideReverse); + var container = jsonld.getContextValue( + activeCtx, itemActiveProperty, '@container'); - // 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; -}; + // get @list value if appropriate + var isList = _isList(expandedItem); + var list = null; + if(isList) { + list = expandedItem['@list']; + } -/** - * @return {string} - */ -Bbox.prototype.toXML = function () { - return utils.buildString({bbox: this.buildJsObj()}) -}; + // recursively compact expanded item + var compactedItem = this.compact( + activeCtx, itemActiveProperty, isList ? list : expandedItem, options); -/** - * @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; -}; + // handle @list + if(isList) { + // ensure @list value is an array + if(!_isArray(compactedItem)) { + compactedItem = [compactedItem]; + } -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Bbox} - */ -Bbox.fromObj = function (jsObj) { - if (typeof jsObj.bbox == 'undefined') { - throw new Error("Bad XML provided, expected tagName bbox, got: " + Object.keys(jsObj)[0]); - } + if(container !== '@list') { + // wrap using @list alias + var wrapper = {}; + wrapper[_compactIri(activeCtx, '@list')] = compactedItem; + compactedItem = wrapper; - var bbox = new ns.Bbox(); - jsObj = jsObj.bbox; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return bbox; - } + // 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'}); + } + } - 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; -}; + // 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 = {}; + } -ns.Bbox = Bbox; -// ------- END BBOX ------- + // if container is a language map, simplify compacted value to + // a simple string + if(container === '@language' && _isValue(compactedItem)) { + compactedItem = compactedItem['@value']; + } -// ------- STATE ------- -/** - * Represents the <state> element. - * @class StateType - * @param {Object} params - * @param {string=} params.value - * @param {string=} params.variable - */ -var StateType = function (params) { - var params = checkParams(params, ['value', 'variable']); - this.value = params.value; - this.variable = params.variable; -}; + // 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'); -/** - * @return {Object} - xml2js formatted object - */ -StateType.prototype.buildJsObj = function () { - var stateObj = {}; + // add compact value + jsonld.addValue( + rval, itemActiveProperty, compactedItem, + {propertyIsArray: isArray}); + } + } + } - // 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; -}; + return rval; + } -/** - * @return {string} - */ -StateType.prototype.toXML = function () { - return utils.buildString({state: this.buildJsObj()}) + // only primitives remain which are already compact + return element; }; /** - * @param {String} string - * @return {StateType} + * 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. */ -StateType.fromXML = function (string) { - var state; - function fn (err, result) { - state = StateType.fromObj(result); - }; - utils.parseString(string, fn); - return state; -}; +Processor.prototype.expand = function( + activeCtx, activeProperty, element, options, insideList) { + var self = this; -/** - * @param {Object} jsObj - xml2js formatted object - * @return {StateType} - */ -StateType.fromObj = function (jsObj) { - if (typeof jsObj.state == 'undefined') { - throw new Error("Bad XML provided, expected tagName state, got: " + Object.keys(jsObj)[0]); - } + // nothing to expand + if(element === null || element === undefined) { + return null; + } - var state = new ns.StateType(); - jsObj = jsObj.state; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return state; - } + 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; + } - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - state.value = attributes.value || null; - state.variable = attributes.variable || null; - } - return state; -}; + // expand element according to value expansion rules + return _expandValue(activeCtx, activeProperty, element); + } -ns.StateType = StateType; -// ------- END STATE ------- + // 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; + } -// ------- CLONE ------- -/** - * Represents the <clone> element. - * @class CloneType - * @param {Object} params - * @param {string=} params.label - */ -var CloneType = function (params) { - var params = checkParams(params, ['label']); - this.label = params.label; -}; + // recursively expand object: -/** - * @param {Label} label - */ -CloneType.prototype.setLabel = function (label) { - this.label = label; -}; + // if element has a context, process it + if('@context' in element) { + activeCtx = self.processContext(activeCtx, element['@context'], options); + } -/** - * @return {Object} - xml2js formatted object - */ -CloneType.prototype.buildJsObj = function () { - var cloneObj = {}; + // expand the active property + var expandedActiveProperty = _expandIri( + activeCtx, activeProperty, {vocab: true}); - // children - if(this.label != null) { - cloneObj.label = this.label.buildJsObj(); - } - return cloneObj; -}; + 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; -/** - * @return {string} - */ -CloneType.prototype.toXML = function () { - return utils.buildString({clone: this.buildJsObj()}) -}; + // skip @context + if(key === '@context') { + continue; + } -/** - * @param {String} string - * @return {CloneType} - */ -CloneType.fromXML = function (string) { - var clone; - function fn (err, result) { - clone = CloneType.fromObj(result); - }; - utils.parseString(string, fn); - return clone; -}; + // expand property + var expandedProperty = _expandIri(activeCtx, key, {vocab: true}); -/** - * @param {Object} jsObj - xml2js formatted object - * @return {CloneType} - */ -CloneType.fromObj = function (jsObj) { - if (typeof jsObj.clone == 'undefined') { - throw new Error("Bad XML provided, expected tagName clone, got: " + Object.keys(jsObj)[0]); - } + // drop non-absolute IRI keys that aren't keywords + if(expandedProperty === null || + !(_isAbsoluteIri(expandedProperty) || _isKeyword(expandedProperty))) { + continue; + } - var clone = new ns.CloneType(); - jsObj = jsObj.clone; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return clone; - } + 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 - if(jsObj.label) { - var label = ns.Label.fromObj({label: jsObj.label[0]}); - clone.setLabel(label); - } - return clone; -}; + // 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}); + } + } -ns.CloneType = CloneType; -// ------- END CLONE ------- + if(expandedProperty === '@type') { + _validateTypeValue(value); + } -// ------- ENTITYTYPE ------- -/** - * Represents the <entity> element. - * @class EntityType - * @param {Object} params - * @param {string=} params.name - */ -var EntityType = function (params) { - var params = checkParams(params, ['name']); - this.name = params.name; -}; + // @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}); + } -/** - * @return {Object} - xml2js formatted object - */ -EntityType.prototype.buildJsObj = function () { - var entityObj = {}; + // @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}); + } - // attributes - var attributes = {}; - if(this.name != null) { - attributes.name = this.name; - } - utils.addAttributes(entityObj, attributes); - return entityObj; -}; - -/** - * @return {string} - */ -EntityType.prototype.toXML = function () { - return utils.buildString({entity: this.buildJsObj()}) -}; - -/** - * @param {String} string - * @return {EntityType} - */ -EntityType.fromXML = function (string) { - var entity; - function fn (err, result) { - entity = EntityType.fromObj(result); - }; - utils.parseString(string, fn); - return entity; -}; + // @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(); + } -/** - * @param {Object} jsObj - xml2js formatted object - * @return {EntityType} - */ -EntityType.fromObj = function (jsObj) { - if (typeof jsObj.entity == 'undefined') { - throw new Error("Bad XML provided, expected tagName entity, got: " + Object.keys(jsObj)[0]); - } + // @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}); + } + } - var entity = new ns.EntityType(); - jsObj = jsObj.entity; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return entity; - } + // @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}); + } - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - entity.name = attributes.name || null; - } - return entity; -}; + expandedValue = self.expand(activeCtx, '@reverse', value, options); -ns.EntityType = EntityType; -// ------- END ENTITYTYPE ------- + // properties double-reversed + if('@reverse' in expandedValue) { + for(var property in expandedValue['@reverse']) { + jsonld.addValue( + rval, property, expandedValue['@reverse'][property], + {propertyIsArray: true}); + } + } -// ------- 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); -}; + // 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}); + } + } -Port.prototype = Object.create(ns.SBGNBase.prototype); -Port.prototype.constructor = ns.Port; + continue; + } -/** - * @return {Object} - xml2js formatted object - */ -Port.prototype.buildJsObj = function () { - var portObj = {}; + var container = jsonld.getContextValue(activeCtx, key, '@container'); - // 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; -}; + 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); + } + } -/** - * @return {string} - */ -Port.prototype.toXML = function () { - return utils.buildString({port: this.buildJsObj()}) -}; + // drop null values if property is not @value + if(expandedValue === null && expandedProperty !== '@value') { + continue; + } -/** - * @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; -}; + // 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}; + } -/** - * @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]); - } + // 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; + } - var port = new ns.Port(); - jsObj = jsObj.port; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return port; - } + // 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}); + } - 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; -}; + // get property count on expanded output + keys = Object.keys(rval); + var count = keys.length; -ns.Port = Port; -// ------- END PORT ------- + 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; + } -// ------- 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 {StartType=} params.start - * @param {EndType=} params.end - * @param {NextType=} 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; + // 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; + } + } - this.start = params.start; - this.end = params.end; - this.nexts = params.nexts || []; - this.glyphs = params.glyphs || []; + return rval; }; -Arc.prototype = Object.create(ns.SBGNBase.prototype); -Arc.prototype.constructor = ns.Arc; - /** - * @param {StartType} start + * 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. */ -Arc.prototype.setStart = function (start) { - this.start = start; -}; +Processor.prototype.createNodeMap = function(input, options) { + options = options || {}; -/** - * @param {EndType} end - */ -Arc.prototype.setEnd = function (end) { - this.end = end; -}; + // 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); -/** - * @param {NextType} next - */ -Arc.prototype.addNext = function (next) { - this.nexts.push(next); + // add all non-default graphs to default graph + return _mergeNodeMaps(graphs); }; /** - * @param {Glyph} glyph + * Performs JSON-LD flattening. + * + * @param input the expanded JSON-LD to flatten. + * + * @return the flattened output. */ -Arc.prototype.addGlyph = function (glyph) { - this.glyphs.push(glyph); +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. */ -Arc.prototype.buildJsObj = function () { - var arcObj = {}; +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.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); + // 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(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; + // 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. */ -Arc.prototype.toXML = function () { - return utils.buildString({arc: 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 {Arc} - */ -Arc.fromXML = function (string) { - var arc; - function fn (err, result) { - arc = Arc.fromObj(result); - }; - utils.parseString(string, fn); - return arc; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Arc} + * 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. */ -Arc.fromObj = function (jsObj) { - if (typeof jsObj.arc == 'undefined') { - throw new Error("Bad XML provided, expected tagName arc, got: " + Object.keys(jsObj)[0]); - } +Processor.prototype.fromRDF = function(dataset, options, callback) { + var defaultGraph = {}; + var graphMap = {'@default': defaultGraph}; + var referencedOnce = {}; - var arc = new ns.Arc(); - jsObj = jsObj.arc; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return arc; - } + 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]; - 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; - } + // get subject, predicate, object + var s = triple.subject.value; + var p = triple.predicate.value; + var o = triple.object; - // children - if(jsObj.start) { - var start = ns.StartType.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.NextType.fromObj({next: nexts[i]}); - arc.addNext(next); - } - } - if(jsObj.end) { - var end = ns.EndType.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); - } - } + if(!(s in nodeMap)) { + nodeMap[s] = {'@id': s}; + } + var node = nodeMap[s]; - arc.baseFromObj(jsObj); - return arc; -}; + var objectIsId = (o.type === 'IRI' || o.type === 'blank node'); + if(objectIsId && !(o.value in nodeMap)) { + nodeMap[o.value] = {'@id': o.value}; + } -ns.Arc = Arc; -// ------- END ARC ------- + if(p === RDF_TYPE && !options.useRdfType && objectIsId) { + jsonld.addValue(node, '@type', o.value, {propertyIsArray: true}); + continue; + } -// ------- STARTTYPE ------- -/** - * Represents the <start> element. - * @class StartType - * @param {Object} params - * @param {string|number=} params.x - * @param {string|number=} params.y - */ -var StartType = function (params) { - var params = checkParams(params, ['x', 'y']); - this.x = parseFloat(params.x); - this.y = parseFloat(params.y); -}; + var value = _RDFToObject(o, options.useNativeTypes); + jsonld.addValue(node, p, value, {propertyIsArray: true}); -/** - * @return {Object} - xml2js formatted object - */ -StartType.prototype.buildJsObj = function () { - var startObj = {}; + // 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 + }; + } + } + } + } - // 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; -}; + // convert linked lists to @list arrays + for(var name in graphMap) { + var graphObject = graphMap[name]; -/** - * @return {string} - */ -StartType.prototype.toXML = function () { - return utils.buildString({start: this.buildJsObj()}) -}; + // no @lists to be converted, continue + if(!(RDF_NIL in graphObject)) { + continue; + } -/** - * @param {String} string - * @return {StartType} - */ -StartType.fromXML = function (string) { - var start; - function fn (err, result) { - start = StartType.fromObj(result); - }; - utils.parseString(string, fn); - return start; -}; + // 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 = []; -/** - * @param {Object} jsObj - xml2js formatted object - * @return {StartType} - */ -StartType.fromObj = function (jsObj) { - if (typeof jsObj.start == 'undefined') { - throw new Error("Bad XML provided, expected tagName start, got: " + Object.keys(jsObj)[0]); - } + // 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']); - var start = new ns.StartType(); - jsObj = jsObj.start; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return start; - } + // 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; - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - start.x = parseFloat(attributes.x); - start.y = parseFloat(attributes.y); - } - return start; -}; + // if node is not a blank node, then list head found + if(node['@id'].indexOf('_:') !== 0) { + break; + } + } -ns.StartType = StartType; -// ------- END STARTTYPE ------- + // 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; + } -// ------- ENDTYPE ------- -/** - * Represents the <end> element. - * @class EndType - * @param {Object} params - * @param {string|number=} params.x - * @param {string|number=} params.y - */ -var EndType = function (params) { - var params = checkParams(params, ['x', 'y']); - this.x = parseFloat(params.x); - this.y = parseFloat(params.y); -}; + // preserve list head + head = graphObject[head['@id']][RDF_REST][0]; + list.pop(); + listNodes.pop(); + } -/** - * @return {Object} - xml2js formatted object - */ -EndType.prototype.buildJsObj = function () { - var endObj = {}; + // transform list into @list object + delete head['@id']; + head['@list'] = list.reverse(); + for(var j = 0; j < listNodes.length; ++j) { + delete graphObject[listNodes[j]]; + } + } - // 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; -}; + delete nil.usages; + } -/** - * @return {string} - */ -EndType.prototype.toXML = function () { - return utils.buildString({end: this.buildJsObj()}) -}; + 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); + } + } -/** - * @param {String} string - * @return {EndType} - */ -EndType.fromXML = function (string) { - var end; - function fn (err, result) { - end = EndType.fromObj(result); - }; - utils.parseString(string, fn); - return end; + callback(null, result); }; /** - * @param {Object} jsObj - xml2js formatted object - * @return {EndType} + * 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. */ -EndType.fromObj = function (jsObj) { - if (typeof jsObj.end == 'undefined') { - throw new Error("Bad XML provided, expected tagName end, got: " + Object.keys(jsObj)[0]); - } - - var end = new ns.EndType(); - jsObj = jsObj.end; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return end; - } - - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - end.x = parseFloat(attributes.x); - end.y = parseFloat(attributes.y); - } - return end; -}; - -ns.EndType = EndType; -// ------- END ENDTYPE ------- +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); -// ------- NEXTTYPE ------- -/** - * Represents the <next> element. - * @class NextType - * @param {Object} params - * @param {string|number=} params.x - * @param {string|number=} params.y - */ -var NextType = function (params) { - var params = checkParams(params, ['x', 'y']); - this.x = parseFloat(params.x); - this.y = parseFloat(params.y); + 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; }; /** - * @return {Object} - xml2js formatted object + * 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. */ -NextType.prototype.buildJsObj = function () { - var nextObj = {}; +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]; - // 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; -}; + // no contexts in array, clone existing context + if(ctxs.length === 0) { + return activeCtx.clone(); + } -/** - * @return {string} - */ -NextType.prototype.toXML = function () { - return utils.buildString({next: this.buildJsObj()}) -}; + // 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]; -/** - * @param {String} string - * @return {NextType} - */ -NextType.fromXML = function (string) { - var next; - function fn (err, result) { - next = NextType.fromObj(result); - }; - utils.parseString(string, fn); - return next; -}; + // reset to initial context + if(ctx === null) { + rval = activeCtx = _getInitialContext(options); + continue; + } -/** - * @param {Object} jsObj - xml2js formatted object - * @return {NextType} - */ -NextType.fromObj = function (jsObj) { - if (typeof jsObj.next == 'undefined') { - throw new Error("Bad XML provided, expected tagName next, got: " + Object.keys(jsObj)[0]); - } + // dereference @context key if present + if(_isObject(ctx) && '@context' in ctx) { + ctx = ctx['@context']; + } - var next = new ns.NextType(); - jsObj = jsObj.next; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return next; - } + // 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}); + } - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - next.x = parseFloat(attributes.x); - next.y = parseFloat(attributes.y); - } - return next; -}; + // get context from cache if available + if(jsonld.cache.activeCtx) { + var cached = jsonld.cache.activeCtx.get(activeCtx, ctx); + if(cached) { + rval = activeCtx = cached; + continue; + } + } -ns.NextType = NextType; -// ------- END NEXTTYPE ------- + // update active context and clone new one before updating + activeCtx = rval; + rval = rval.clone(); -// ------- POINT ------- -/** - * Represents the <point> element. - * @class Point - * @param {Object} params - * @param {string|number=} params.x - * @param {string|number=} params.y - */ -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; + // define context mappings for keys in local context + var defined = {}; -/** - * @return {Object} - xml2js formatted object - */ -Point.prototype.buildJsObj = function () { - var pointJsObj = {}; + // handle @base + if('@base' in ctx) { + var base = ctx['@base']; - // 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; -}; + // 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}); + } -/** - * @return {string} - */ -Point.prototype.toXML = function () { - return utils.buildString({point: this.buildJsObj()}) -}; + if(base !== null) { + base = jsonld.url.parse(base || ''); + } + rval['@base'] = base; + defined['@base'] = true; + } -/** - * @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; -}; - -/** - * @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]); - } + // 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 point = new ns.Point(); - jsObj = jsObj.point; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return point; - } + // 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; + } - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - point.x = parseFloat(attributes.x); - point.y = parseFloat(attributes.y); - } - point.baseFromObj(jsObj); - return point; -}; + // process all other keys + for(var key in ctx) { + _createTermDefinition(rval, ctx, key, defined); + } -ns.Point = Point; -// ------- END POINT ------- + // cache result + if(jsonld.cache.activeCtx) { + jsonld.cache.activeCtx.set(activeCtx, ctx, rval); + } + } -// ------- 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; + return rval; }; /** - * @param {Point} point + * Expands a language map. + * + * @param languageMap the language map to expand. + * + * @return the expanded language map. */ -Callout.prototype.setPoint = function(point) { - this.point = point; -}; +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; +} /** - * @return {Object} - xml2js formatted object + * 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. */ -Callout.prototype.buildJsObj = function () { - var calloutObj = {}; +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']); + } - // attributes - var attributes = {}; - if(this.target != null) { - attributes.target = this.target; - } - utils.addAttributes(calloutObj, attributes); + // 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]); + } + } + } - // children - if(this.point != null) { - calloutObj.point = this.point.buildJsObj(); - } - return calloutObj; -}; + return element; +} /** - * @return {string} + * 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. */ -Callout.prototype.toXML = function () { - return utils.buildString({callout: this.buildJsObj()}) -}; +function _expandValue(activeCtx, activeProperty, value) { + // nothing to expand + if(value === null || value === undefined) { + return null; + } -/** - * @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; -}; + // 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}); + } -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Callout} - */ -Callout.fromObj = function (jsObj) { - if (typeof jsObj.callout == 'undefined') { - throw new Error("Bad XML provided, expected tagName callout, got: " + Object.keys(jsObj)[0]); - } + // get type definition from context + var type = jsonld.getContextValue(activeCtx, activeProperty, '@type'); - var callout = new ns.Callout(); - jsObj = jsObj.callout; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return callout; - } + // 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})}; + } - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - callout.target = attributes.target || null; - } + // do not expand keyword values + if(_isKeyword(expandedProperty)) { + return value; + } - // children - if(jsObj.point) { - var point = ns.Point.fromObj({point: jsObj.point[0]}); - callout.setPoint(point); - } - return callout; -}; + var rval = {}; -ns.Callout = Callout; -// ------- END CALLOUT ------- + 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; + + return rval; +} -// ------- ARCGROUP ------- /** - * Represents the <arcgroup> element. - * @class - * @extends SBGNBase - * @param {Object} params - * @param {string=} params.class_ - * @param {Glyph[]=} params.glyphs - * @param {Arc[]=} params.arcs + * 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. */ -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 || []; -}; +function _graphToRDF(graph, issuer, options) { + var rval = []; -Arcgroup.prototype = Object.create(ns.SBGNBase.prototype); -Arcgroup.prototype.constructor = Arcgroup; + 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; + } -/** - * @param {Glyph} glyph - */ -Arcgroup.prototype.addGlyph = function (glyph) { - this.glyphs.push(glyph); -}; + for(var ii = 0; ii < items.length; ++ii) { + var item = items[ii]; -/** - * @param {Arc} arc - */ -Arcgroup.prototype.addArc = function (arc) { - this.arcs.push(arc); -}; + // RDF subject + var subject = {}; + subject.type = (id.indexOf('_:') === 0) ? 'blank node' : 'IRI'; + subject.value = id; -/** - * @return {Object} - xml2js formatted object - */ -Arcgroup.prototype.buildJsObj = function () { - var arcgroupObj = {}; + // skip relative IRI subjects + if(!_isAbsoluteIri(id)) { + continue; + } - // attributes - var attributes = {}; - if(this.class_ != null) { - attributes.class = this.class_; - } - utils.addAttributes(arcgroupObj, attributes); + // RDF predicate + var predicate = {}; + predicate.type = (property.indexOf('_:') === 0) ? 'blank node' : 'IRI'; + predicate.value = property; - // 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; -}; + // skip relative IRI predicates + if(!_isAbsoluteIri(property)) { + continue; + } -/** - * @return {string} - */ -Arcgroup.prototype.toXML = function () { - return utils.buildString({arcgroup: this.buildJsObj()}); -}; + // skip blank node predicates unless producing generalized RDF + if(predicate.type === 'blank node' && !options.produceGeneralizedRdf) { + 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; -}; + // 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}); + } + } + } + } + } + + return rval; +} /** - * @param {Object} jsObj - xml2js formatted object - * @return {Arcgroup} + * 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. */ -Arcgroup.fromObj = function (jsObj) { - if (typeof jsObj.arcgroup == 'undefined') { - throw new Error("Bad XML provided, expected tagName arcgroup, got: " + Object.keys(jsObj)[0]); - } - - var arcgroup = new ns.Arcgroup(); - jsObj = jsObj.arcgroup; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return arcgroup; - } +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}; - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - arcgroup.class_ = attributes.class || null; - } + for(var i = 0; i < list.length; ++i) { + var item = list[i]; - 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); - } - } + var blankNode = {type: 'blank node', value: issuer.getId()}; + triples.push({subject: subject, predicate: predicate, object: blankNode}); - arcgroup.baseFromObj(jsObj); - return arcgroup; -}; + subject = blankNode; + predicate = first; + var object = _objectToRDF(item); -ns.Arcgroup = Arcgroup; -// ------- END ARCGROUP ------- + // skip null objects (they are relative IRIs) + if(object) { + triples.push({subject: subject, predicate: predicate, object: object}); + } -ns.render = renderExt; -ns.annot = annotExt; -module.exports = ns; -},{"./libsbgn-annotations":27,"./libsbgn-render":28,"./utilities":30,"xml2js":25}],30:[function(_dereq_,module,exports){ -var ns = {}; -var xml2js = _dereq_('xml2js'); + predicate = rest; + } -/* - guarantees to return an object with given args being set to null if not present, other args returned as is -*/ -ns.checkParams = function (params, names) { - if (typeof params == "undefined" || params == null) { - params = {}; - } - if (typeof params != 'object') { - throw new Error("Bad params. Object with named parameters must be passed."); - } - for(var i=0; i < names.length; i++) { - var argName = names[i]; - if (typeof params[argName] == 'undefined') { - params[argName] = null; - } - } - return params; + triples.push({subject: subject, predicate: predicate, object: nil}); } -ns.getFirstLevelByName = function (xmlObj, localName) { - var result = []; - for(var i=0; i\\"\{\}\|\^\`]/; +var POSITIONS = {'subject': 's', 'object': 'o', 'name': 'g'}; -// ## Constructor -function N3Lexer(options) { - if (!(this instanceof N3Lexer)) - return new N3Lexer(options); +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; + } +}; - // In line mode (N-Triples or N-Quads), only simple features may be parsed - if (options.lineMode) { - // Don't tokenize special literals - this._tripleQuotedString = this._number = this._boolean = /$0^/; - // Swap the tokenize method for a restricted version - var self = this; - this._tokenize = this.tokenize; - this.tokenize = function (input, callback) { - this._tokenize(input, function (error, token) { - if (!error && /^(?:IRI|blank|literal|langcode|typeIRI|\.|eof)$/.test(token.type)) - callback && callback(error, token); - else - callback && callback(error || self._syntaxError(token.type, callback = null)); - }); - }; +// do some work in a time slice, but in serial +Normalize.prototype.doWork = function(fn, callback) { + var schedule = this.schedule; + + if(schedule.totalDepth >= schedule.MAX_TOTAL_DEPTH) { + return callback(new Error( + 'Maximum total call stack depth exceeded; normalization aborting.')); } - // Enable N3 functionality by default - this._n3Mode = options.n3 !== false; - // Disable comment tokens by default - this._comments = !!options.comments; -} -N3Lexer.prototype = { - // ## Regular expressions - // It's slightly faster to have these as properties than as in-scope variables + (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); + } - _iri: /^<((?:[^ <>{}\\]|\\[uU])+)>[ \t]*/, // IRI with escape sequences; needs sanity check after unescaping - _unescapedIri: /^<([^\x00-\x20<>\\"\{\}\|\^\`]*)>[ \t]*/, // IRI without escape sequences; no unescaping - _unescapedString: /^"[^"\\\r\n]+"/, // non-empty string without escape sequences - _singleQuotedString: /^"(?:[^"\\\r\n]|\\.)*"(?=[^"])|^'(?:[^'\\\r\n]|\\.)*'(?=[^'])/, - _tripleQuotedString: /^""("[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*")""|^''('[^'\\]*(?:(?:\\.|'(?!''))[^'\\]*)*')''/, - _langcode: /^@([a-z]+(?:-[a-z0-9]+)*)(?=[^a-z0-9\-])/i, - _prefix: /^((?:[A-Za-z\xc0-\xd6\xd8-\xf6\xf8-\u02ff\u0370-\u037d\u037f-\u1fff\u200c\u200d\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])(?:\.?[\-0-9A-Z_a-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u037f-\u1fff\u200c\u200d\u203f\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])*)?:(?=[#\s<])/, - _prefixed: /^((?:[A-Za-z\xc0-\xd6\xd8-\xf6\xf8-\u02ff\u0370-\u037d\u037f-\u1fff\u200c\u200d\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])(?:\.?[\-0-9A-Z_a-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u037f-\u1fff\u200c\u200d\u203f\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])*)?:((?:(?:[0-:A-Z_a-z\xc0-\xd6\xd8-\xf6\xf8-\u02ff\u0370-\u037d\u037f-\u1fff\u200c\u200d\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff]|%[0-9a-fA-F]{2}|\\[!#-\/;=?\-@_~])(?:(?:[\.\-0-:A-Z_a-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u037f-\u1fff\u200c\u200d\u203f\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff]|%[0-9a-fA-F]{2}|\\[!#-\/;=?\-@_~])*(?:[\-0-:A-Z_a-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u037f-\u1fff\u200c\u200d\u203f\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff]|%[0-9a-fA-F]{2}|\\[!#-\/;=?\-@_~]))?)?)(?:[ \t]+|(?=\.?[,;!\^\s#()\[\]\{\}"'<]))/, - _variable: /^\?(?:(?:[A-Z_a-z\xc0-\xd6\xd8-\xf6\xf8-\u02ff\u0370-\u037d\u037f-\u1fff\u200c\u200d\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])(?:[\-0-:A-Z_a-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u037f-\u1fff\u200c\u200d\u203f\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])*)(?=[.,;!\^\s#()\[\]\{\}"'<])/, - _blank: /^_:((?:[0-9A-Z_a-z\xc0-\xd6\xd8-\xf6\xf8-\u02ff\u0370-\u037d\u037f-\u1fff\u200c\u200d\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])(?:\.?[\-0-9A-Z_a-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u037f-\u1fff\u200c\u200d\u203f\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])*)(?:[ \t]+|(?=\.?[,;:\s#()\[\]\{\}"'<]))/, - _number: /^[\-+]?(?:\d+\.?\d*([eE](?:[\-\+])?\d+)|\d*\.?\d+)(?=\.?[,;:\s#()\[\]\{\}"'<])/, - _boolean: /^(?:true|false)(?=[.,;\s#()\[\]\{\}"'<])/, - _keyword: /^@[a-z]+(?=[\s#<:])/i, - _sparqlKeyword: /^(?:PREFIX|BASE|GRAPH)(?=[\s#<])/i, - _shortPredicates: /^a(?=\s+|<)/, - _newline: /^[ \t]*(?:#[^\n\r]*)?(?:\r\n|\n|\r)[ \t]*/, - _comment: /#([^\n\r]*)/, - _whitespace: /^[ \t]+/, - _endOfFile: /^(?:#[^\n\r]*)?$/, + // 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; + } - // ## Private methods + // 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); + }); + } - // ### `_tokenizeToEnd` tokenizes as for as possible, emitting tokens through the callback - _tokenizeToEnd: function (callback, inputFinished) { - // Continue parsing as far as possible; the loop will return eventually - var input = this._input, outputComments = this._comments; - while (true) { - // Count and skip whitespace lines - var whiteSpaceMatch, comment; - while (whiteSpaceMatch = this._newline.exec(input)) { - // Try to find a comment - if (outputComments && (comment = this._comment.exec(whiteSpaceMatch[0]))) - callback(null, { line: this._line, type: 'comment', value: comment[1], prefix: '' }); - // Advance the input - input = input.substr(whiteSpaceMatch[0].length, input.length); - this._line++; - } - // Skip whitespace on current line - if (whiteSpaceMatch = this._whitespace.exec(input)) - input = input.substr(whiteSpaceMatch[0].length, input.length); + // not enough time left in this slice, run after letting browser + // do some other things + schedule.depth = 0; + schedule.running = false; + jsonld.setImmediate(work); + })(); +}; - // Stop for now if we're at the end - if (this._endOfFile.test(input)) { - // If the input is finished, emit EOF - if (inputFinished) { - // Try to find a final comment - if (outputComments && (comment = this._comment.exec(input))) - callback(null, { line: this._line, type: 'comment', value: comment[1], prefix: '' }); - callback(input = null, { line: this._line, type: 'eof', value: '', prefix: '' }); - } - return this._input = input; +// 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; + }; + } - // Look for specific token types based on the first character - var line = this._line, type = '', value = '', prefix = '', - firstChar = input[0], match = null, matchLength = 0, unescaped, inconclusive = false; - switch (firstChar) { - case '^': - // We need at least 3 tokens lookahead to distinguish ^^ and ^^pre:fixed - if (input.length < 3) - break; - // Try to match a type - else if (input[1] === '^') { - this._previousMarker = '^^'; - // Move to type IRI or prefixed name - input = input.substr(2); - if (input[0] !== '<') { - inconclusive = true; - break; - } - } - // If no type, it must be a path expression - else { - if (this._n3Mode) { - matchLength = 1; - type = '^'; - } - break; - } - // Fall through in case the type is an IRI - case '<': - // Try to find a full IRI without escape sequences - if (match = this._unescapedIri.exec(input)) - type = 'IRI', value = match[1]; - // Try to find a full IRI with escape sequences - else if (match = this._iri.exec(input)) { - unescaped = this._unescape(match[1]); - if (unescaped === null || illegalIriChars.test(unescaped)) - return reportSyntaxError(this); - type = 'IRI', value = unescaped; - } - // Try to find a backwards implication arrow - else if (this._n3Mode && input.length > 1 && input[1] === '=') - type = 'inverse', matchLength = 2, value = 'http://www.w3.org/2000/10/swap/log#implies'; - break; + (function iterate(err, result) { + if(err) { + return callback(err); + } + if(iterator()) { + return self.doWork(function() { + fn(iterator.value, iterator.key, iterate); + }); + } + callback(); + })(); +}; - case '_': - // Try to find a blank node. Since it can contain (but not end with) a dot, - // we always need a non-dot character before deciding it is a blank node. - // Therefore, try inserting a space if we're at the end of the input. - if ((match = this._blank.exec(input)) || - inputFinished && (match = this._blank.exec(input + ' '))) - type = 'blank', prefix = '_', value = match[1]; - break; +// asynchronous waterfall +Normalize.prototype.waterfall = function(fns, callback) { + var self = this; + self.forEach(fns, function(fn, idx, callback) { + self.doWork(fn, callback); + }, callback); +}; - case '"': - case "'": - // Try to find a non-empty double-quoted literal without escape sequences - if (match = this._unescapedString.exec(input)) - type = 'literal', value = match[0]; - // Try to find any other literal wrapped in a pair of single or double quotes - else if (match = this._singleQuotedString.exec(input)) { - unescaped = this._unescape(match[0]); - if (unescaped === null) - return reportSyntaxError(this); - type = 'literal', value = unescaped.replace(/^'|'$/g, '"'); - } - // Try to find a literal wrapped in three pairs of single or double quotes - else if (match = this._tripleQuotedString.exec(input)) { - unescaped = match[1] || match[2]; - // Count the newlines and advance line counter - this._line += unescaped.split(/\r\n|\r|\n/).length - 1; - unescaped = this._unescape(unescaped); - if (unescaped === null) - return reportSyntaxError(this); - type = 'literal', value = unescaped.replace(/^'|'$/g, '"'); - } - break; +// 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); + })(); +}; - case '?': - // Try to find a variable - if (this._n3Mode && (match = this._variable.exec(input))) - type = 'var', value = match[0]; - break; +// 4.4) Normalization Algorithm +Normalize.prototype.main = function(dataset, callback) { + var self = this; + self.schedule.start = new Date().getTime(); + var result; - case '@': - // Try to find a language code - if (this._previousMarker === 'literal' && (match = this._langcode.exec(input))) - type = 'langcode', value = match[1]; - // Try to find a keyword - else if (match = this._keyword.exec(input)) - type = match[0]; - 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})); + } + } - case '.': - // Try to find a dot as punctuation - if (input.length === 1 ? inputFinished : (input[1] < '0' || input[1] > '9')) { - type = '.'; - matchLength = 1; - break; - } - // Fall through to numerical case (could be a decimal dot) + // 1) Create the normalization state. - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '+': - case '-': - // Try to find a number. Since it can contain (but not end with) a dot, - // we always need a non-dot character before deciding it is a number. - // Therefore, try inserting a space if we're at the end of the input. - if (match = this._number.exec(input) || - inputFinished && (match = this._number.exec(input + ' '))) { - type = 'literal'; - value = '"' + match[0] + '"^^http://www.w3.org/2001/XMLSchema#' + - (match[1] ? 'double' : (/^[+\-]?\d+$/.test(match[0]) ? 'integer' : 'decimal')); - } - break; + // Note: Optimize by generating non-normalized blank node map concurrently. + var nonNormalized = {}; - case 'B': - case 'b': - case 'p': - case 'P': - case 'G': - case 'g': - // Try to find a SPARQL-style keyword - if (match = this._sparqlKeyword.exec(input)) - type = match[0].toUpperCase(); - else - inconclusive = true; - break; + 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); - case 'f': - case 't': - // Try to match a boolean - if (match = this._boolean.exec(input)) - type = 'literal', value = '"' + match[0] + '"^^http://www.w3.org/2001/XMLSchema#boolean'; - else - inconclusive = true; - break; + // 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. - case 'a': - // Try to find an abbreviated predicate - if (match = this._shortPredicates.exec(input)) - type = 'abbreviation', value = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'; - else - inconclusive = true; - break; + // 4) Initialize simple, a boolean flag, to true. + var simple = true; - case '=': - // Try to find an implication arrow or equals sign - if (this._n3Mode && input.length > 1) { - type = 'abbreviation'; - if (input[1] !== '>') - matchLength = 1, value = 'http://www.w3.org/2002/07/owl#sameAs'; - else - matchLength = 2, value = 'http://www.w3.org/2000/10/swap/log#implies'; - } - break; + // 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; - case '!': - if (!this._n3Mode) - break; - case ',': - case ';': - case '[': - case ']': - case '(': - case ')': - case '{': - case '}': - // The next token is punctuation - matchLength = 1; - type = firstChar; - break; + // 5.2) Clear hash to blank nodes map. + self.hashToBlankNodes = {}; - default: - inconclusive = true; - } + 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(); + } - // Some first characters do not allow an immediate decision, so inspect more - if (inconclusive) { - // Try to find a prefix - if ((this._previousMarker === '@prefix' || this._previousMarker === 'PREFIX') && - (match = this._prefix.exec(input))) - type = 'prefix', value = match[1] || ''; - // Try to find a prefixed name. Since it can contain (but not end with) a dot, - // we always need a non-dot character before deciding it is a prefixed name. - // Therefore, try inserting a space if we're at the end of the input. - else if ((match = this._prefixed.exec(input)) || - inputFinished && (match = this._prefixed.exec(input + ' '))) - type = 'prefixed', prefix = match[1] || '', value = this._unescape(match[2]); - } + // 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); - // A type token is special: it can only be emitted after an IRI or prefixed name is read - if (this._previousMarker === '^^') { - switch (type) { - case 'prefixed': type = 'type'; break; - case 'IRI': type = 'typeIRI'; break; - default: type = ''; - } - } + // 5.4.3) Remove identifier from non-normalized identifiers. + delete nonNormalized[id]; - // What if nothing of the above was found? - if (!type) { - // We could be in streaming mode, and then we just wait for more input to arrive. - // Otherwise, a syntax error has occurred in the input. - // One exception: error on an unaccounted linebreak (= not inside a triple-quoted literal). - if (inputFinished || (!/^'''|^"""/.test(input) && /\n|\r/.test(input))) - return reportSyntaxError(this); - else - return this._input = input; - } + // 5.4.4) Remove hash from the hash to blank nodes map. + delete self.hashToBlankNodes[hash]; - // Emit the parsed token - var token = { line: line, type: type, value: value, prefix: prefix }; - callback(null, token); - this.previousToken = token; - this._previousMarker = type; - // Advance to next part to tokenize - input = input.substr(matchLength || match[0].length, input.length); - } + // 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 = []; - // Signals the syntax error through the callback - function reportSyntaxError(self) { callback(self._syntaxError(/^\S*/.exec(input)[0])); } - }, + // 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(); + } - // ### `_unescape` replaces N3 escape codes by their corresponding characters - _unescape: function (item) { - try { - return item.replace(escapeSequence, function (sequence, unicode4, unicode8, escapedChar) { - var charCode; - if (unicode4) { - charCode = parseInt(unicode4, 16); - if (isNaN(charCode)) throw new Error(); // can never happen (regex), but helps performance - return fromCharCode(charCode); - } - else if (unicode8) { - charCode = parseInt(unicode8, 16); - if (isNaN(charCode)) throw new Error(); // can never happen (regex), but helps performance - if (charCode <= 0xFFFF) return fromCharCode(charCode); - return fromCharCode(0xD800 + ((charCode -= 0x10000) / 0x400), 0xDC00 + (charCode & 0x3FF)); - } - else { - var replacement = escapeReplacements[escapedChar]; - if (!replacement) - throw new Error(); - return replacement; - } - }); - } - catch (error) { return null; } - }, + // 6.2.2) Create temporary issuer, an identifier issuer + // initialized with the prefix _:b. + var issuer = new IdentifierIssuer('_:b'); - // ### `_syntaxError` creates a syntax error for the given issue - _syntaxError: function (issue) { - this._input = null; - var err = new Error('Unexpected "' + issue + '" on line ' + this._line + '.'); - err.context = { - token: undefined, - line: this._line, - previousToken: this.previousToken, - }; - return err; - }, + // 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); + // 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. */ - // ## Public methods + // 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(); - // ### `tokenize` starts the transformation of an N3 document into an array of tokens. - // The input can be a string or a stream. - tokenize: function (input, callback) { - var self = this; - this._line = 1; + // 8) Return the normalized dataset. + if(self.options.format === 'application/nquads') { + result = normalized.join(''); + return callback(); + } - // If the input is a string, continuously emit tokens through the callback until the end - if (typeof input === 'string') { - this._input = input; - // If a callback was passed, asynchronously call it - if (typeof callback === 'function') - immediately(function () { self._tokenizeToEnd(callback, true); }); - // If no callback was passed, tokenize synchronously and return - else { - var tokens = [], error; - this._tokenizeToEnd(function (e, t) { e ? (error = e) : tokens.push(t); }, true); - if (error) throw error; - return tokens; - } - } - // Otherwise, the input must be a stream - else { - this._input = ''; - if (typeof input.setEncoding === 'function') - input.setEncoding('utf8'); - // Adds the data chunk to the buffer and parses as far as possible - input.on('data', function (data) { - if (self._input !== null) { - self._input += data; - self._tokenizeToEnd(callback, false); + result = _parseNQuads(normalized.join('')); + callback(); } - }); - // Parses until the end - input.on('end', function () { - if (self._input !== null) - self._tokenizeToEnd(callback, true); - }); - input.on('error', callback); + ], callback); } - }, + ], function(err) { + callback(err, result); + }); }; -// ## Exports -module.exports = N3Lexer; - -},{}],33:[function(_dereq_,module,exports){ -// **N3Parser** parses N3 documents. -var N3Lexer = _dereq_('./N3Lexer'); +// 4.6) Hash First Degree Quads +Normalize.prototype.hashFirstDegreeQuads = function(id, callback) { + var self = this; -var RDF_PREFIX = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', - RDF_NIL = RDF_PREFIX + 'nil', - RDF_FIRST = RDF_PREFIX + 'first', - RDF_REST = RDF_PREFIX + 'rest'; + // return cached hash + var info = self.blankNodeInfo[id]; + if('hash' in info) { + return callback(null, info.hash); + } -var QUANTIFIERS_GRAPH = 'urn:n3:quantifiers'; + // 1) Initialize nquads to an empty list. It will be used to store quads in + // N-Quads format. + var nquads = []; -var absoluteIRI = /^[a-z][a-z0-9+.-]*:/i, - schemeAuthority = /^(?:([a-z][a-z0-9+.-]*:))?(?:\/\/[^\/]*)?/i, - dotSegments = /(?:^|\/)\.\.?(?:$|[\/#?])/; + // 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; -// The next ID for new blank nodes -var blankNodePrefix = 0, blankNodeCount = 0; + // 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: -// ## Constructor -function N3Parser(options) { - if (!(this instanceof N3Parser)) - return new N3Parser(options); - this._contextStack = []; - this._graph = null; + // 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(); - // Set the document IRI - options = options || {}; - this._setBase(options.documentIRI); + // 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); + }); +}; - // Set supported features depending on the format - var format = (typeof options.format === 'string') ? - options.format.match(/\w*$/)[0].toLowerCase() : '', - isTurtle = format === 'turtle', isTriG = format === 'trig', - isNTriples = /triple/.test(format), isNQuads = /quad/.test(format), - isN3 = this._n3Mode = /n3/.test(format), - isLineMode = isNTriples || isNQuads; - if (!(this._supportsNamedGraphs = !(isTurtle || isN3))) - this._readPredicateOrNamedGraph = this._readPredicate; - this._supportsQuads = !(isTurtle || isTriG || isNTriples || isN3); - // Disable relative IRIs in N-Triples or N-Quads mode - if (isLineMode) { - this._base = ''; - this._resolveIRI = function (token) { - this._error('Disallowed relative IRI', token); - return this._callback = noop, this._subject = null; - }; +// helper for modifying component during Hash First Degree Quads +Normalize.prototype.modifyFirstDegreeComponent = function(id, component) { + if(component.type !== 'blank node') { + return component; } - this._blankNodePrefix = typeof options.blankNodePrefix !== 'string' ? '' : - '_:' + options.blankNodePrefix.replace(/^_:/, ''); - this._lexer = options.lexer || new N3Lexer({ lineMode: isLineMode, n3: isN3 }); - // Disable explicit quantifiers by default - this._explicitQuantifiers = !!options.explicitQuantifiers; -} - -// ## Private class methods - -// ### `_resetBlankNodeIds` restarts blank node identification -N3Parser._resetBlankNodeIds = function () { - blankNodePrefix = blankNodeCount = 0; + component = _clone(component); + component.value = (component.value === id ? '_:a' : '_:z'); + return component; }; -N3Parser.prototype = { - // ## Private methods +// 4.7) Hash Related Blank Node +Normalize.prototype.hashRelatedBlankNode = function( + related, quad, issuer, position, callback) { + var self = this; - // ### `_setBase` sets the base IRI to resolve relative IRIs - _setBase: function (baseIRI) { - if (!baseIRI) - this._base = null; - else { - // Remove fragment if present - var fragmentPos = baseIRI.indexOf('#'); - if (fragmentPos >= 0) - baseIRI = baseIRI.substr(0, fragmentPos); - // Set base IRI and its components - this._base = baseIRI; - this._basePath = baseIRI.indexOf('/') < 0 ? baseIRI : - baseIRI.replace(/[^\/?]*(?:\?.*)?$/, ''); - baseIRI = baseIRI.match(schemeAuthority); - this._baseRoot = baseIRI[0]; - this._baseScheme = baseIRI[1]; - } - }, - - // ### `_saveContext` stores the current parsing context - // when entering a new scope (list, blank node, formula) - _saveContext: function (type, graph, subject, predicate, object) { - var n3Mode = this._n3Mode; - this._contextStack.push({ - subject: subject, predicate: predicate, object: object, - graph: graph, type: type, - inverse: n3Mode ? this._inversePredicate : false, - blankPrefix: n3Mode ? this._prefixes._ : '', - quantified: n3Mode ? this._quantified : null, - }); - // The settings below only apply to N3 streams - if (n3Mode) { - // Every new scope resets the predicate direction - this._inversePredicate = false; - // In N3, blank nodes are scoped to a formula - // (using a dot as separator, as a blank node label cannot start with it) - this._prefixes._ = this._graph + '.'; - // Quantifiers are scoped to a formula - this._quantified = Object.create(this._quantified); + // 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(); + }); } - }, - - // ### `_restoreContext` restores the parent context - // when leaving a scope (list, blank node, formula) - _restoreContext: function () { - var context = this._contextStack.pop(), n3Mode = this._n3Mode; - this._subject = context.subject; - this._predicate = context.predicate; - this._object = context.object; - this._graph = context.graph; - // The settings below only apply to N3 streams - if (n3Mode) { - this._inversePredicate = context.inverse; - this._prefixes._ = context.blankPrefix; - this._quantified = context.quantified; + ], function(err) { + if(err) { + return callback(err); } - }, - // ### `_readInTopContext` reads a token when in the top context - _readInTopContext: function (token) { - switch (token.type) { - // If an EOF token arrives in the top context, signal that we're done - case 'eof': - if (this._graph !== null) - return this._error('Unclosed graph', token); - delete this._prefixes._; - return this._callback(null, null, this._prefixes); - // It could be a prefix declaration - case 'PREFIX': - this._sparqlStyle = true; - case '@prefix': - return this._readPrefix; - // It could be a base declaration - case 'BASE': - this._sparqlStyle = true; - case '@base': - return this._readBaseIRI; - // It could be a graph - case '{': - if (this._supportsNamedGraphs) { - this._graph = ''; - this._subject = null; - return this._readSubject; - } - case 'GRAPH': - if (this._supportsNamedGraphs) - return this._readNamedGraphLabel; - // Otherwise, the next token must be a subject - default: - return this._readSubject(token); - } - }, + // 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); - // ### `_readEntity` reads an IRI, prefixed name, blank node, or variable - _readEntity: function (token, quantifier) { - var value; - switch (token.type) { - // Read a relative or absolute IRI - case 'IRI': - case 'typeIRI': - value = (this._base === null || absoluteIRI.test(token.value)) ? - token.value : this._resolveIRI(token); - break; - // Read a blank node or prefixed name - case 'type': - case 'blank': - case 'prefixed': - var prefix = this._prefixes[token.prefix]; - if (prefix === undefined) - return this._error('Undefined prefix "' + token.prefix + ':"', token); - value = prefix + token.value; - break; - // Read a variable - case 'var': - return token.value; - // Everything else is not an entity - default: - return this._error('Expected entity but got ' + token.type, token); + // 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)); } - // In N3 mode, replace the entity if it is quantified - if (!quantifier && this._n3Mode && (value in this._quantified)) - value = this._quantified[value]; - return value; - }, - // ### `_readSubject` reads a triple's subject - _readSubject: function (token) { - this._predicate = null; - switch (token.type) { - case '[': - // Start a new triple with a new blank node as subject - this._saveContext('blank', this._graph, - this._subject = '_:b' + blankNodeCount++, null, null); - return this._readBlankNodeHead; - case '(': - // Start a new list - this._saveContext('list', this._graph, RDF_NIL, null, null); - this._subject = null; - return this._readListItem; - case '{': - // Start a new formula - if (!this._n3Mode) - return this._error('Unexpected graph', token); - this._saveContext('formula', this._graph, - this._graph = '_:b' + blankNodeCount++, null, null); - return this._readSubject; - case '}': - // No subject; the graph in which we are reading is closed instead - return this._readPunctuation(token); - case '@forSome': - if (!this._n3Mode) - return this._error('Unexpected "@forSome"', token); - this._subject = null; - this._predicate = 'http://www.w3.org/2000/10/swap/reify#forSome'; - this._quantifiedPrefix = '_:b'; - return this._readQuantifierList; - case '@forAll': - if (!this._n3Mode) - return this._error('Unexpected "@forAll"', token); - this._subject = null; - this._predicate = 'http://www.w3.org/2000/10/swap/reify#forAll'; - this._quantifiedPrefix = '?b-'; - return this._readQuantifierList; - default: - // Read the subject entity - if ((this._subject = this._readEntity(token)) === undefined) - return; - // In N3 mode, the subject might be a path - if (this._n3Mode) - return this._getPathReader(this._readPredicateOrNamedGraph); - } + // 4) Append identifier to input. + md.update(id); - // The next token must be a predicate, - // or, if the subject was actually a graph IRI, a named graph - return this._readPredicateOrNamedGraph; - }, + // 5) Return the hash that results from passing input through the hash + // algorithm. + return callback(null, md.digest()); + }); +}; - // ### `_readPredicate` reads a triple's predicate - _readPredicate: function (token) { - var type = token.type; - switch (type) { - case 'inverse': - this._inversePredicate = true; - case 'abbreviation': - this._predicate = token.value; - break; - case '.': - case ']': - case '}': - // Expected predicate didn't come, must have been trailing semicolon - if (this._predicate === null) - return this._error('Unexpected ' + type, token); - this._subject = null; - return type === ']' ? this._readBlankNodeTail(token) : this._readPunctuation(token); - case ';': - // Extra semicolons can be safely ignored - return this._readPredicate; - case 'blank': - if (!this._n3Mode) - return this._error('Disallowed blank node as predicate', token); - default: - if ((this._predicate = this._readEntity(token)) === undefined) - return; - } - // The next token must be an object - return this._readObject; - }, +// helper for getting a related predicate +Normalize.prototype.getRelatedPredicate = function(quad) { + return '<' + quad.predicate.value + '>'; +}; - // ### `_readObject` reads a triple's object - _readObject: function (token) { - switch (token.type) { - case 'literal': - this._object = token.value; - return this._readDataTypeOrLang; - case '[': - // Start a new triple with a new blank node as subject - this._saveContext('blank', this._graph, this._subject, this._predicate, - this._subject = '_:b' + blankNodeCount++); - return this._readBlankNodeHead; - case '(': - // Start a new list - this._saveContext('list', this._graph, this._subject, this._predicate, RDF_NIL); - this._subject = null; - return this._readListItem; - case '{': - // Start a new formula - if (!this._n3Mode) - return this._error('Unexpected graph', token); - this._saveContext('formula', this._graph, this._subject, this._predicate, - this._graph = '_:b' + blankNodeCount++); - return this._readSubject; - default: - // Read the object entity - if ((this._object = this._readEntity(token)) === undefined) - return; - // In N3 mode, the object might be a path - if (this._n3Mode) - return this._getPathReader(this._getContextEndReader()); - } - return this._getContextEndReader(); - }, +// 4.8) Hash N-Degree Quads +Normalize.prototype.hashNDegreeQuads = function(id, issuer, callback) { + var self = this; - // ### `_readPredicateOrNamedGraph` reads a triple's predicate, or a named graph - _readPredicateOrNamedGraph: function (token) { - return token.type === '{' ? this._readGraph(token) : this._readPredicate(token); - }, + // 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. - // ### `_readGraph` reads a graph - _readGraph: function (token) { - if (token.type !== '{') - return this._error('Expected graph but got ' + token.type, token); - // The "subject" we read is actually the GRAPH's label - this._graph = this._subject, this._subject = null; - return this._readSubject; - }, + // 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); - // ### `_readBlankNodeHead` reads the head of a blank node - _readBlankNodeHead: function (token) { - if (token.type === ']') { - this._subject = null; - return this._readBlankNodeTail(token); - } - else { - this._predicate = null; - return this._readPredicate(token); - } - }, + // 5.2) Create a string chosen path. + var chosenPath = ''; - // ### `_readBlankNodeTail` reads the end of a blank node - _readBlankNodeTail: function (token) { - if (token.type !== ']') - return this._readBlankNodePunctuation(token); + // 5.3) Create an unset chosen issuer variable. + var chosenIssuer; - // Store blank node triple - if (this._subject !== null) - this._triple(this._subject, this._predicate, this._object, this._graph); + // 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(); - // Restore the parent context containing this blank node - var empty = this._predicate === null; - this._restoreContext(); - // If the blank node was the subject, continue reading the predicate - if (this._object === null) - // If the blank node was empty, it could be a named graph label - return empty ? this._readPredicateOrNamedGraph : this._readPredicateAfterBlank; - // If the blank node was the object, restore previous context and read punctuation - else - return this._getContextEndReader(); - }, + // 5.4.1) Create a copy of issuer, issuer copy. + var issuerCopy = issuer.clone(); - // ### `_readPredicateAfterBlank` reads a predicate after an anonymous blank node - _readPredicateAfterBlank: function (token) { - // If a dot follows a blank node in top context, there is no predicate - if (token.type === '.' && !this._contextStack.length) { - this._subject = null; // cancel the current triple - return this._readPunctuation(token); - } - return this._readPredicate(token); - }, + // 5.4.2) Create a string path. + var path = ''; - // ### `_readListItem` reads items from a list - _readListItem: function (token) { - var item = null, // The item of the list - list = null, // The list itself - prevList = this._subject, // The previous list that contains this list - stack = this._contextStack, // The stack of parent contexts - parent = stack[stack.length - 1], // The parent containing the current list - next = this._readListItem, // The next function to execute - itemComplete = true; // Whether the item has been read fully + // 5.4.3) Create a recursion list, to store blank node identifiers + // that must be recursively processed by this algorithm. + var recursionList = []; - switch (token.type) { - case '[': - // Stack the current list triple and start a new triple with a blank node as subject - this._saveContext('blank', this._graph, list = '_:b' + blankNodeCount++, - RDF_FIRST, this._subject = item = '_:b' + blankNodeCount++); - next = this._readBlankNodeHead; - break; - case '(': - // Stack the current list triple and start a new list - this._saveContext('list', this._graph, list = '_:b' + blankNodeCount++, - RDF_FIRST, RDF_NIL); - this._subject = null; - break; - case ')': - // Closing the list; restore the parent context - this._restoreContext(); - // If this list is contained within a parent list, return the membership triple here. - // This will be ` rdf:first .`. - if (stack.length !== 0 && stack[stack.length - 1].type === 'list') - this._triple(this._subject, this._predicate, this._object, this._graph); - // Was this list the parent's subject? - if (this._predicate === null) { - // The next token is the predicate - next = this._readPredicate; - // No list tail if this was an empty list - if (this._subject === RDF_NIL) - return next; - } - // The list was in the parent context's object - else { - next = this._getContextEndReader(); - // No list tail if this was an empty list - if (this._object === RDF_NIL) - return next; - } - // Close the list by making the head nil - list = RDF_NIL; - break; - case 'literal': - item = token.value; - itemComplete = false; // Can still have a datatype or language - next = this._readListItemDataTypeOrLang; - break; - default: - if ((item = this._readEntity(token)) === undefined) - return; - } + 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); + } - // Create a new blank node if no item head was assigned yet - if (list === null) - this._subject = list = '_:b' + blankNodeCount++; + // 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); + } - // Is this the first element of the list? - if (prevList === null) { - // This list is either the subject or the object of its parent - if (parent.predicate === null) - parent.subject = list; - else - parent.object = list; - } - else { - // Continue the previous list with the current list - this._triple(prevList, RDF_REST, list, this._graph); - } - // Add the item's value - if (item !== null) { - // In N3 mode, the item might be a path - if (this._n3Mode && (token.type === 'IRI' || token.type === 'prefixed')) { - // Create a new context to add the item's path - this._saveContext('item', this._graph, list, RDF_FIRST, item); - this._subject = item, this._predicate = null; - // _readPath will restore the context and output the item - return this._getPathReader(this._readListItem); - } - // Output the item if it is complete - if (itemComplete) - this._triple(list, RDF_FIRST, item, this._graph); - // Otherwise, save it for completion - else - this._object = item; - } - return next; - }, + // 5.4.5.2) Use the Issue Identifier algorithm, passing issuer + // copy and related and append the result to path. + path += issuerCopy.getId(related); - // ### `_readDataTypeOrLang` reads an _optional_ data type or language - _readDataTypeOrLang: function (token) { - return this._completeLiteral(token, false); - }, + // 5.4.5.3) Append <, the hash in result, and > to path. + path += '<' + result.hash + '>'; - // ### `_readListItemDataTypeOrLang` reads an _optional_ data type or language in a list - _readListItemDataTypeOrLang: function (token) { - return this._completeLiteral(token, true); - }, + // 5.4.5.4) Set issuer copy to the identifier issuer in + // result. + issuerCopy = result.issuer; - // ### `_completeLiteral` completes the object with a data type or language - _completeLiteral: function (token, listItem) { - var suffix = false; - switch (token.type) { - // Add a "^^type" suffix for types (IRIs and blank nodes) - case 'type': - case 'typeIRI': - suffix = true; - this._object += '^^' + this._readEntity(token); - break; - // Add an "@lang" suffix for language tags - case 'langcode': - suffix = true; - this._object += '@' + token.value.toLowerCase(); - break; - } - // If this literal was part of a list, write the item - // (we could also check the context stack, but passing in a flag is faster) - if (listItem) - this._triple(this._subject, RDF_FIRST, this._object, this._graph); - // Continue with the rest of the input - if (suffix) - return this._getContextEndReader(); - else { - this._readCallback = this._getContextEndReader(); - return this._readCallback(token); + // 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); + } + + // 5.5) Append chosen path to data to hash. + md.update(chosenPath); + + // 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}); + }); +}; - // ### `_readFormulaTail` reads the end of a formula - _readFormulaTail: function (token) { - if (token.type !== '}') - return this._readPunctuation(token); +// helper for creating hash to related blank nodes map +Normalize.prototype.createHashToRelated = function(id, issuer, callback) { + var self = this; - // Store the last triple of the formula - if (this._subject !== null) - this._triple(this._subject, this._predicate, this._object, this._graph); + // 1) Create a hash to related blank nodes map for storing hashes that + // identify related blank nodes. + var hashToRelated = {}; - // Restore the parent context containing this formula - this._restoreContext(); - // If the formula was the subject, continue reading the predicate. - // If the formula was the object, read punctuation. - return this._object === null ? this._readPredicate : this._getContextEndReader(); - }, + // 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; - // ### `_readPunctuation` reads punctuation between triples or triple parts - _readPunctuation: function (token) { - var next, subject = this._subject, graph = this._graph, - inversePredicate = this._inversePredicate; - switch (token.type) { - // A closing brace ends a graph - case '}': - if (this._graph === null) - return this._error('Unexpected graph closing', token); - if (this._n3Mode) - return this._readFormulaTail(token); - this._graph = null; - // A dot just ends the statement, without sharing anything with the next - case '.': - this._subject = null; - next = this._contextStack.length ? this._readSubject : this._readInTopContext; - if (inversePredicate) this._inversePredicate = false; - break; - // Semicolon means the subject is shared; predicate and object are different - case ';': - next = this._readPredicate; - break; - // Comma means both the subject and predicate are shared; the object is different - case ',': - next = this._readObject; - break; - default: - // An entity means this is a quad (only allowed if not already inside a graph) - if (this._supportsQuads && this._graph === null && (graph = this._readEntity(token)) !== undefined) { - next = this._readQuadPunctuation; - break; + // 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(); } - return this._error('Expected punctuation to follow "' + this._object + '"', token); - } - // A triple has been completed now, so return it - if (subject !== null) { - var predicate = this._predicate, object = this._object; - if (!inversePredicate) - this._triple(subject, predicate, object, graph); - else - this._triple(object, predicate, subject, graph); - } - return next; - }, + // 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); + }); +}; - // ### `_readBlankNodePunctuation` reads punctuation in a blank node - _readBlankNodePunctuation: function (token) { - var next; - switch (token.type) { - // Semicolon means the subject is shared; predicate and object are different - case ';': - next = this._readPredicate; - break; - // Comma means both the subject and predicate are shared; the object is different - case ',': - next = this._readObject; - break; - default: - return this._error('Expected punctuation to follow "' + this._object + '"', token); +// 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; } - // A triple has been completed now, so return it - this._triple(this._subject, this._predicate, this._object, this._graph); - return next; - }, + op(quad[key], key, quad); + } +}; - // ### `_readQuadPunctuation` reads punctuation after a quad - _readQuadPunctuation: function (token) { - if (token.type !== '.') - return this._error('Expected dot to follow quad', token); - return this._readInTopContext; - }, +return Normalize; - // ### `_readPrefix` reads the prefix of a prefix declaration - _readPrefix: function (token) { - if (token.type !== 'prefix') - return this._error('Expected prefix to follow @prefix', token); - this._prefix = token.value; - return this._readPrefixIRI; - }, +})(); // end of define URDNA2015 - // ### `_readPrefixIRI` reads the IRI of a prefix declaration - _readPrefixIRI: function (token) { - if (token.type !== 'IRI') - return this._error('Expected IRI to follow prefix "' + this._prefix + ':"', token); - var prefixIRI = this._readEntity(token); - this._prefixes[this._prefix] = prefixIRI; - this._prefixCallback(this._prefix, prefixIRI); - return this._readDeclarationPunctuation; - }, +/////////////////////////////// DEFINE URGNA2012 ////////////////////////////// - // ### `_readBaseIRI` reads the IRI of a base declaration - _readBaseIRI: function (token) { - if (token.type !== 'IRI') - return this._error('Expected IRI to follow base declaration', token); - this._setBase(this._base === null || absoluteIRI.test(token.value) ? - token.value : this._resolveIRI(token)); - return this._readDeclarationPunctuation; - }, +var URGNA2012 = (function() { - // ### `_readNamedGraphLabel` reads the label of a named graph - _readNamedGraphLabel: function (token) { - switch (token.type) { - case 'IRI': - case 'blank': - case 'prefixed': - return this._readSubject(token), this._readGraph; - case '[': - return this._readNamedGraphBlankLabel; - default: - return this._error('Invalid graph label', token); - } - }, +var Normalize = function(options) { + URDNA2015.call(this, options); + this.name = 'URGNA2012'; +}; +Normalize.prototype = new URDNA2015(); - // ### `_readNamedGraphLabel` reads a blank node label of a named graph - _readNamedGraphBlankLabel: function (token) { - if (token.type !== ']') - return this._error('Invalid graph label', token); - this._subject = '_:b' + blankNodeCount++; - return this._readGraph; - }, +// 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; +}; - // ### `_readDeclarationPunctuation` reads the punctuation of a declaration - _readDeclarationPunctuation: function (token) { - // SPARQL-style declarations don't have punctuation - if (this._sparqlStyle) { - this._sparqlStyle = false; - return this._readInTopContext(token); +// 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; + + // 1) Create a hash to related blank nodes map for storing hashes that + // identify related blank nodes. + var hashToRelated = {}; + + // 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); + }); +}; - if (token.type !== '.') - return this._error('Expected declaration to end with a dot', token); - return this._readInTopContext; - }, +return Normalize; - // Reads a list of quantified symbols from a @forSome or @forAll statement - _readQuantifierList: function (token) { - var entity; - switch (token.type) { - case 'IRI': - case 'prefixed': - if ((entity = this._readEntity(token, true)) !== undefined) - break; - default: - return this._error('Unexpected ' + token.type, token); +})(); // end of define URGNA2012 + +/** + * 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); } - // Without explicit quantifiers, map entities to a quantified entity - if (!this._explicitQuantifiers) - this._quantified[entity] = this._quantifiedPrefix + blankNodeCount++; - // With explicit quantifiers, output the reified quantifier - else { - // If this is the first item, start a new quantifier list - if (this._subject === null) - this._triple(this._graph || '', this._predicate, - this._subject = '_:b' + blankNodeCount++, QUANTIFIERS_GRAPH); - // Otherwise, continue the previous list - else - this._triple(this._subject, RDF_REST, - this._subject = '_:b' + blankNodeCount++, QUANTIFIERS_GRAPH); - // Output the list item - this._triple(this._subject, RDF_FIRST, entity, QUANTIFIERS_GRAPH); + return; + } + + // add non-object to list + if(!_isObject(input)) { + if(list) { + list.push(input); } - return this._readQuantifierPunctuation; - }, + return; + } - // Reads punctuation from a @forSome or @forAll statement - _readQuantifierPunctuation: function (token) { - // Read more quantifiers - if (token.type === ',') - return this._readQuantifierList; - // End of the quantifier list - else { - // With explicit quantifiers, close the quantifier list - if (this._explicitQuantifiers) { - this._triple(this._subject, RDF_REST, RDF_NIL, QUANTIFIERS_GRAPH); - this._subject = null; + // 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); } - // Read a dot - this._readCallback = this._getContextEndReader(); - return this._readCallback(token); } - }, + if(list) { + list.push(input); + } + return; + } - // ### `_getPathReader` reads a potential path and then resumes with the given function - _getPathReader: function (afterPath) { - this._afterPath = afterPath; - return this._readPath; - }, + // Note: At this point, input must be a subject. - // ### `_readPath` reads a potential path - _readPath: function (token) { - switch (token.type) { - // Forward path - case '!': return this._readForwardPath; - // Backward path - case '^': return this._readBackwardPath; - // Not a path; resume reading where we left off - default: - var stack = this._contextStack, parent = stack.length && stack[stack.length - 1]; - // If we were reading a list item, we still need to output it - if (parent && parent.type === 'item') { - // The list item is the remaining subejct after reading the path - var item = this._subject; - // Switch back to the context of the list - this._restoreContext(); - // Output the list item - this._triple(this._subject, RDF_FIRST, item, this._graph); + // 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); } - return this._afterPath(token); } - }, + } - // ### `_readForwardPath` reads a '!' path - _readForwardPath: function (token) { - var subject, predicate, object = '_:b' + blankNodeCount++; - // The next token is the predicate - if ((predicate = this._readEntity(token)) === undefined) - return; - // If we were reading a subject, replace the subject by the path's object - if (this._predicate === null) - subject = this._subject, this._subject = object; - // If we were reading an object, replace the subject by the path's object - else - subject = this._object, this._object = object; - // Emit the path's current triple and read its next section - this._triple(subject, predicate, object, this._graph); - return this._readPath; - }, + // get name for subject + if(_isUndefined(name)) { + name = _isBlankNode(input) ? issuer.getId(input['@id']) : input['@id']; + } - // ### `_readBackwardPath` reads a '^' path - _readBackwardPath: function (token) { - var subject = '_:b' + blankNodeCount++, predicate, object; - // The next token is the predicate - if ((predicate = this._readEntity(token)) === undefined) - return; - // If we were reading a subject, replace the subject by the path's subject - if (this._predicate === null) - object = this._subject, this._subject = subject; - // If we were reading an object, replace the subject by the path's subject - else - object = this._object, this._object = subject; - // Emit the path's current triple and read its next section - this._triple(subject, predicate, object, this._graph); - return this._readPath; - }, + // add subject reference to list + if(list) { + list.push({'@id': name}); + } - // ### `_getContextEndReader` gets the next reader function at the end of a context - _getContextEndReader: function () { - var contextStack = this._contextStack; - if (!contextStack.length) - return this._readPunctuation; + // 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]; - switch (contextStack[contextStack.length - 1].type) { - case 'blank': - return this._readBlankNodeTail; - case 'list': - return this._readListItem; - case 'formula': - return this._readFormulaTail; + // skip @id + if(property === '@id') { + continue; } - }, - - // ### `_triple` emits a triple through the callback - _triple: function (subject, predicate, object, graph) { - this._callback(null, - { subject: subject, predicate: predicate, object: object, graph: graph || '' }); - }, - - // ### `_error` emits an error message through the callback - _error: function (message, token) { - var err = new Error(message + ' on line ' + token.line + '.'); - err.context = { - token: token, - line: token.line, - previousToken: this._lexer.previousToken, - }; - this._callback(err); - }, - // ### `_resolveIRI` resolves a relative IRI token against the base path, - // assuming that a base path has been set and that the IRI is indeed relative - _resolveIRI: function (token) { - var iri = token.value; - switch (iri[0]) { - // An empty relative IRI indicates the base IRI - case undefined: return this._base; - // Resolve relative fragment IRIs against the base IRI - case '#': return this._base + iri; - // Resolve relative query string IRIs by replacing the query string - case '?': return this._base.replace(/(?:\?.*)?$/, iri); - // Resolve root-relative IRIs at the root of the base IRI - case '/': - // Resolve scheme-relative IRIs to the scheme - return (iri[1] === '/' ? this._baseScheme : this._baseRoot) + this._removeDotSegments(iri); - // Resolve all other IRIs at the base IRI's path - default: - return this._removeDotSegments(this._basePath + iri); + // 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; } - }, - // ### `_removeDotSegments` resolves './' and '../' path segments in an IRI as per RFC3986 - _removeDotSegments: function (iri) { - // Don't modify the IRI if it does not contain any dot segments - if (!dotSegments.test(iri)) - return iri; - - // Start with an imaginary slash before the IRI in order to resolve trailing './' and '../' - var result = '', length = iri.length, i = -1, pathStart = -1, segmentStart = 0, next = '/'; + // 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; + } - while (i < length) { - switch (next) { - // The path starts with the first slash after the authority - case ':': - if (pathStart < 0) { - // Skip two slashes before the authority - if (iri[++i] === '/' && iri[++i] === '/') - // Skip to slash after the authority - while ((pathStart = i + 1) < length && iri[pathStart] !== '/') - i = pathStart; - } - break; - // Don't modify a query string or fragment - case '?': - case '#': - i = length; - break; - // Handle '/.' or '/..' path segments - case '/': - if (iri[i + 1] === '.') { - next = iri[++i + 1]; - switch (next) { - // Remove a '/.' segment - case '/': - result += iri.substring(segmentStart, i - 1); - segmentStart = i + 1; - break; - // Remove a trailing '/.' segment - case undefined: - case '?': - case '#': - return result + iri.substring(segmentStart, i) + iri.substr(i + 1); - // Remove a '/..' segment - case '.': - next = iri[++i + 1]; - if (next === undefined || next === '/' || next === '?' || next === '#') { - result += iri.substring(segmentStart, i - 2); - // Try to remove the parent path from result - if ((segmentStart = result.lastIndexOf('/')) >= pathStart) - result = result.substr(0, segmentStart); - // Remove a trailing '/..' segment - if (next !== '/') - return result + '/' + iri.substr(i + 1); - segmentStart = i + 1; - } - } - } + // 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}); } - next = iri[++i]; + subject[property] = input[property]; + continue; } - return result + iri.substring(segmentStart); - }, - // ## Public methods + // iterate over objects + var objects = input[property]; - // ### `parse` parses the N3 input and emits each parsed triple through the callback - parse: function (input, tripleCallback, prefixCallback) { - var self = this; - // The read callback is the next function to be executed when a token arrives. - // We start reading in the top context. - this._readCallback = this._readInTopContext; - this._sparqlStyle = false; - this._prefixes = Object.create(null); - this._prefixes._ = this._blankNodePrefix || '_:b' + blankNodePrefix++ + '_'; - this._prefixCallback = prefixCallback || noop; - this._inversePredicate = false; - this._quantified = Object.create(null); + // if property is a bnode, assign it a new id + if(property.indexOf('_:') === 0) { + property = issuer.getId(property); + } - // Parse synchronously if no triple callback is given - if (!tripleCallback) { - var triples = [], error; - this._callback = function (e, t) { e ? (error = e) : t && triples.push(t); }; - this._lexer.tokenize(input).every(function (token) { - return self._readCallback = self._readCallback(token); - }); - if (error) throw error; - return triples; + // 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]; - // Parse asynchronously otherwise, executing the read callback when a token arrives - this._callback = tripleCallback; - this._lexer.tokenize(input, function (error, token) { - if (error !== null) - self._callback(error), self._callback = noop; - else if (self._readCallback) - self._readCallback = self._readCallback(token); - }); - }, -}; + if(property === '@type') { + // rename @type blank nodes + o = (o.indexOf('_:') === 0) ? issuer.getId(o) : o; + } -// The empty function -function noop() {} + // 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']; -// ## Exports -module.exports = N3Parser; + // 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}); + } + } + } +} -},{"./N3Lexer":32}],34:[function(_dereq_,module,exports){ -// **N3Store** objects store N3 triples by graph in memory. +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; +} -var expandPrefixedName = _dereq_('./N3Util').expandPrefixedName; +/** + * 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]; -// ## Constructor -function N3Store(triples, options) { - if (!(this instanceof N3Store)) - return new N3Store(triples, options); + // 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') + }; - // The number of triples is initially zero - this._size = 0; - // `_graphs` contains subject, predicate, and object indexes per graph - this._graphs = Object.create(null); - // `_ids` maps entities such as `http://xmlns.com/foaf/0.1/name` to numbers, - // saving memory by using only numbers as keys in `_graphs` - this._id = 0; - this._ids = Object.create(null); - this._ids['><'] = 0; // dummy entry, so the first actual key is non-zero - this._entities = Object.create(null); // inverse of `_ids` - // `_blankNodeIndex` is the index of the last automatically named blank node - this._blankNodeIndex = 0; + // filter out subjects that match the frame + var matches = _filterSubjects(state, subjects, frame, flags); - // Shift parameters if `triples` is not given - if (!options && triples && !triples[0]) - options = triples, triples = null; - options = options || {}; + // 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]; - // Add triples and prefixes if passed - this._prefixes = Object.create(null); - if (options.prefixes) - this.addPrefixes(options.prefixes); - if (triples) - this.addTriples(triples); -} + 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 -N3Store.prototype = { - // ## Public properties + // add existing linked subject + _addFrameOutput(parent, property, state.link[id]); + continue; + } - // ### `size` returns the number of triples in the store - get size() { - // Return the triple count if if was cached - var size = this._size; - if (size !== null) - return size; + /* 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 = {}; + } - // Calculate the number of triples by counting to the deepest level - size = 0; - var graphs = this._graphs, subjects, subject; - for (var graphKey in graphs) - for (var subjectKey in (subjects = graphs[graphKey].subjects)) - for (var predicateKey in (subject = subjects[subjectKey])) - size += Object.keys(subject[predicateKey]).length; - return this._size = size; - }, + // start output for subject + var output = {}; + output['@id'] = id; + state.link[id] = output; - // ## Private methods + // 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; + } - // ### `_addToIndex` adds a triple to a three-layered index. - // Returns if the index has changed, if the entry did not already exist. - _addToIndex: function (index0, key0, key1, key2) { - // Create layers as necessary - var index1 = index0[key0] || (index0[key0] = {}); - var index2 = index1[key1] || (index1[key1] = {}); - // Setting the key to _any_ value signals the presence of the triple - var existed = key2 in index2; - if (!existed) - index2[key2] = null; - return !existed; - }, + // 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}; + } - // ### `_removeFromIndex` removes a triple from a three-layered index - _removeFromIndex: function (index0, key0, key1, key2) { - // Remove the triple from the index - var index1 = index0[key0], index2 = index1[key1], key; - delete index2[key2]; + // push matching subject onto stack to enable circular embed checks + state.subjectStack.push(subject); - // Remove intermediary index layers if they are empty - for (key in index2) return; - delete index1[key1]; - for (key in index1) return; - delete index0[key0]; - }, + // iterate over subject properties + var props = Object.keys(subject).sort(); + for(var i = 0; i < props.length; i++) { + var prop = props[i]; - // ### `_findInIndex` finds a set of triples in a three-layered index. - // The index base is `index0` and the keys at each level are `key0`, `key1`, and `key2`. - // Any of these keys can be undefined, which is interpreted as a wildcard. - // `name0`, `name1`, and `name2` are the names of the keys at each level, - // used when reconstructing the resulting triple - // (for instance: _subject_, _predicate_, and _object_). - // Finally, `graph` will be the graph of the created triples. - // If `callback` is given, each result is passed through it - // and iteration halts when it returns truthy for any triple. - // If instead `array` is given, each result is added to the array. - _findInIndex: function (index0, key0, key1, key2, name0, name1, name2, graph, callback, array) { - var tmp, index1, index2, varCount = !key0 + !key1 + !key2, - // depending on the number of variables, keys or reverse index are faster - entityKeys = varCount > 1 ? Object.keys(this._ids) : this._entities; + // copy keywords to output + if(_isKeyword(prop)) { + output[prop] = _clone(subject[prop]); + continue; + } - // If a key is specified, use only that part of index 0. - if (key0) (tmp = index0, index0 = {})[key0] = tmp[key0]; - for (var value0 in index0) { - var entity0 = entityKeys[value0]; + // explicit is on and property isn't in the frame, skip processing + if(flags.explicit && !(prop in frame)) { + continue; + } - if (index1 = index0[value0]) { - // If a key is specified, use only that part of index 1. - if (key1) (tmp = index1, index1 = {})[key1] = tmp[key1]; - for (var value1 in index1) { - var entity1 = entityKeys[value1]; + // add objects + var objects = subject[prop]; + for(var oi = 0; oi < objects.length; ++oi) { + var o = objects[oi]; - if (index2 = index1[value1]) { - // If a key is specified, use only that part of index 2, if it exists. - var values = key2 ? (key2 in index2 ? [key2] : []) : Object.keys(index2); - // Create triples for all items found in index 2. - for (var l = values.length - 1; l >= 0; l--) { - var result = { subject: '', predicate: '', object: '', graph: graph }; - result[name0] = entity0; - result[name1] = entity1; - result[name2] = entityKeys[values[l]]; - if (array) - array.push(result); - else if (callback(result)) - return true; + // recurse into list + if(_isList(o)) { + // add empty list + var list = {'@list': []}; + _addFrameOutput(output, prop, list); + + // 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; + } + + 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)); } } } - return array; - }, - // ### `_loop` executes the callback on all keys of index 0 - _loop: function (index0, callback) { - for (var key0 in index0) - callback(key0); - }, + // handle defaults + var props = Object.keys(frame).sort(); + for(var i = 0; i < props.length; ++i) { + var prop = props[i]; - // ### `_loopByKey0` executes the callback on all keys of a certain entry in index 0 - _loopByKey0: function (index0, key0, callback) { - var index1, key1; - if (index1 = index0[key0]) { - for (key1 in index1) - callback(key1); - } - }, + // skip keywords + if(_isKeyword(prop)) { + continue; + } - // ### `_loopByKey1` executes the callback on given keys of all entries in index 0 - _loopByKey1: function (index0, key1, callback) { - var key0, index1; - for (key0 in index0) { - index1 = index0[key0]; - if (index1[key1]) - callback(key0); + // 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}]; + } } - }, - // ### `_loopBy2Keys` executes the callback on given keys of certain entries in index 2 - _loopBy2Keys: function (index0, key0, key1, callback) { - var index1, index2, key2; - if ((index1 = index0[key0]) && (index2 = index1[key1])) { - for (key2 in index2) - callback(key2); - } - }, + // add output to parent + _addFrameOutput(parent, property, output); - // ### `_countInIndex` counts matching triples in a three-layered index. - // The index base is `index0` and the keys at each level are `key0`, `key1`, and `key2`. - // Any of these keys can be undefined, which is interpreted as a wildcard. - _countInIndex: function (index0, key0, key1, key2) { - var count = 0, tmp, index1, index2; + // pop matching subject from circular ref-checking stack + state.subjectStack.pop(); + } +} - // If a key is specified, count only that part of index 0 - if (key0) (tmp = index0, index0 = {})[key0] = tmp[key0]; - for (var value0 in index0) { - if (index1 = index0[value0]) { - // If a key is specified, count only that part of index 1 - if (key1) (tmp = index1, index1 = {})[key1] = tmp[key1]; - for (var value1 in index1) { - if (index2 = index1[value1]) { - // If a key is specified, count the triple if it exists - if (key2) (key2 in index2) && count++; - // Otherwise, count all triples - else count += Object.keys(index2).length; - } - } - } +/** + * 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]]; } - return count; - }, - - // ### `_getGraphs` returns an array with the given graph, - // or all graphs if the argument is null or undefined. - _getGraphs: function (graph) { - if (!isString(graph)) - return this._graphs; - var graphs = {}; - graphs[graph] = this._graphs[graph]; - return graphs; - }, - - // ### `_uniqueEntities` returns a function that accepts an entity ID - // and passes the corresponding entity to callback if it hasn't occurred before. - _uniqueEntities: function (callback) { - var uniqueIds = Object.create(null), entities = this._entities; - return function (id) { - if (!(id in uniqueIds)) { - uniqueIds[id] = true; - callback(entities[id]); - } - }; - }, - - // ## Public methods + } + return [frame]; +} - // ### `addTriple` adds a new N3 triple to the store. - // Returns if the triple index has changed, if the triple did not already exist. - addTriple: function (subject, predicate, object, graph) { - // Shift arguments if a triple object is given instead of components - if (!predicate) - graph = subject.graph, object = subject.object, - predicate = subject.predicate, subject = subject.subject; +/** + * 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; + } + } + return false; +} - // Find the graph that will contain the triple - graph = graph || ''; - var graphItem = this._graphs[graph]; - // Create the graph if it doesn't exist yet - if (!graphItem) { - graphItem = this._graphs[graph] = { subjects: {}, predicates: {}, objects: {} }; - // Freezing a graph helps subsequent `add` performance, - // and properties will never be modified anyway - Object.freeze(graphItem); +/** + * 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; +} - // Since entities can often be long IRIs, we avoid storing them in every index. - // Instead, we have a separate index that maps entities to numbers, - // which are then used as keys in the other indexes. - var ids = this._ids; - var entities = this._entities; - subject = ids[subject] || (ids[entities[++this._id] = subject] = this._id); - predicate = ids[predicate] || (ids[entities[++this._id] = predicate] = this._id); - object = ids[object] || (ids[entities[++this._id] = object] = this._id); +/** + * 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 changed = this._addToIndex(graphItem.subjects, subject, predicate, object); - this._addToIndex(graphItem.predicates, predicate, object, subject); - this._addToIndex(graphItem.objects, object, subject, predicate); +/** + * 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; + } + } + return rval; +} - // The cached triple count is now invalid - this._size = null; - return changed; - }, +/** + * 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; + } + } + return false; + } - // ### `addTriples` adds multiple N3 triples to the store - addTriples: function (triples) { - for (var i = triples.length - 1; i >= 0; i--) - this.addTriple(triples[i]); - }, + // 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; + } + wildcard = false; - // ### `addPrefix` adds support for querying with the given prefix - addPrefix: function (prefix, iri) { - this._prefixes[prefix] = iri; - }, + // check @id for a specific @id value + if(key === '@id' && _isString(frame[key])) { + if(subject[key] !== frame[key]) { + return false; + } + matchesSome = true; + continue; + } + } - // ### `addPrefixes` adds support for querying with the given prefixes - addPrefixes: function (prefixes) { - for (var prefix in prefixes) - this.addPrefix(prefix, prefixes[prefix]); - }, + wildcard = false; - // ### `removeTriple` removes an N3 triple from the store if it exists - removeTriple: function (subject, predicate, object, graph) { - // Shift arguments if a triple object is given instead of components - if (!predicate) - graph = subject.graph, object = subject.object, - predicate = subject.predicate, subject = subject.subject; - graph = graph || ''; + 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; + } - // Find internal identifiers for all components - // and verify the triple exists. - var graphItem, ids = this._ids, graphs = this._graphs, subjects, predicates; - if (!(subject = ids[subject]) || !(predicate = ids[predicate]) || - !(object = ids[object]) || !(graphItem = graphs[graph]) || - !(subjects = graphItem.subjects[subject]) || - !(predicates = subjects[predicate]) || - !(object in predicates)) + // 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; + } + } - // Remove it from all indexes - this._removeFromIndex(graphItem.subjects, subject, predicate, object); - this._removeFromIndex(graphItem.predicates, predicate, object, subject); - this._removeFromIndex(graphItem.objects, object, subject, predicate); - if (this._size !== null) this._size--; + // return true if wildcard or subject matches some properties + return wildcard || matchesSome; +} - // Remove the graph if it is empty - for (subject in graphItem.subjects) return true; - delete graphs[graph]; - return true; - }, +/** + * 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; - // ### `removeTriples` removes multiple N3 triples from the store - removeTriples: function (triples) { - for (var i = triples.length - 1; i >= 0; i--) - this.removeTriple(triples[i]); - }, + // create reference to replace embed + var subject = {'@id': id}; - // ### `getTriples` returns an array of triples matching a pattern, expanding prefixes as necessary. - // Setting any field to `undefined` or `null` indicates a wildcard. - getTriples: function (subject, predicate, object, graph) { - var prefixes = this._prefixes; - return this.getTriplesByIRI( - expandPrefixedName(subject, prefixes), - expandPrefixedName(predicate, prefixes), - expandPrefixedName(object, prefixes), - expandPrefixedName(graph, prefixes) - ); - }, + // 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; + } + } + } else { + // replace subject with reference + var useArray = _isArray(parent[property]); + jsonld.removeValue(parent, property, subject, {propertyIsArray: useArray}); + jsonld.addValue(parent, property, subject, {propertyIsArray: useArray}); + } - // ### `getTriplesByIRI` returns an array of triples matching a pattern. - // Setting any field to `undefined` or `null` indicates a wildcard. - getTriplesByIRI: function (subject, predicate, object, graph) { - var quads = [], graphs = this._getGraphs(graph), content, - ids = this._ids, subjectId, predicateId, objectId; + // 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); + } + } + }; + removeDependents(id); +} - // Translate IRIs to internal index keys. - if (isString(subject) && !(subjectId = ids[subject]) || - isString(predicate) && !(predicateId = ids[predicate]) || - isString(object) && !(objectId = ids[object])) - return quads; +/** + * 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); + } +} - for (var graphId in graphs) { - // Only if the specified graph contains triples, there can be results - if (content = graphs[graphId]) { - // Choose the optimal index, based on what fields are present - if (subjectId) { - if (objectId) - // If subject and object are given, the object index will be the fastest - this._findInIndex(content.objects, objectId, subjectId, predicateId, - 'object', 'subject', 'predicate', graphId, null, quads); - else - // If only subject and possibly predicate are given, the subject index will be the fastest - this._findInIndex(content.subjects, subjectId, predicateId, null, - 'subject', 'predicate', 'object', graphId, null, quads); - } - else if (predicateId) - // If only predicate and possibly object are given, the predicate index will be the fastest - this._findInIndex(content.predicates, predicateId, objectId, null, - 'predicate', 'object', 'subject', graphId, null, quads); - else if (objectId) - // If only object is given, the object index will be the fastest - this._findInIndex(content.objects, objectId, null, null, - 'object', 'subject', 'predicate', graphId, null, quads); - else - // If nothing is given, iterate subjects and predicates first - this._findInIndex(content.subjects, null, null, null, - 'subject', 'predicate', 'object', graphId, null, quads); +/** + * 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); } } - return quads; - }, - - // ### `countTriples` returns the number of triples matching a pattern, expanding prefixes as necessary. - // Setting any field to `undefined` or `null` indicates a wildcard. - countTriples: function (subject, predicate, object, graph) { - var prefixes = this._prefixes; - return this.countTriplesByIRI( - expandPrefixedName(subject, prefixes), - expandPrefixedName(predicate, prefixes), - expandPrefixedName(object, prefixes), - expandPrefixedName(graph, prefixes) - ); - }, + input = output; + } else if(_isObject(input)) { + // remove @preserve + if('@preserve' in input) { + if(input['@preserve'] === '@null') { + return null; + } + return input['@preserve']; + } - // ### `countTriplesByIRI` returns the number of triples matching a pattern. - // Setting any field to `undefined` or `null` indicates a wildcard. - countTriplesByIRI: function (subject, predicate, object, graph) { - var count = 0, graphs = this._getGraphs(graph), content, - ids = this._ids, subjectId, predicateId, objectId; + // skip @values + if(_isValue(input)) { + return input; + } - // Translate IRIs to internal index keys. - if (isString(subject) && !(subjectId = ids[subject]) || - isString(predicate) && !(predicateId = ids[predicate]) || - isString(object) && !(objectId = ids[object])) - return 0; + // recurse through @lists + if(_isList(input)) { + input['@list'] = _removePreserve(ctx, input['@list'], options); + return input; + } - for (var graphId in graphs) { - // Only if the specified graph contains triples, there can be results - if (content = graphs[graphId]) { - // Choose the optimal index, based on what fields are present - if (subject) { - if (object) - // If subject and object are given, the object index will be the fastest - count += this._countInIndex(content.objects, objectId, subjectId, predicateId); - else - // If only subject and possibly predicate are given, the subject index will be the fastest - count += this._countInIndex(content.subjects, subjectId, predicateId, objectId); - } - else if (predicate) { - // If only predicate and possibly object are given, the predicate index will be the fastest - count += this._countInIndex(content.predicates, predicateId, objectId, subjectId); - } - else { - // If only object is possibly given, the object index will be the fastest - count += this._countInIndex(content.objects, objectId, subjectId, predicateId); + // 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]; } + } else { + // prevent circular visitation + options.link[id] = [input]; } } - return count; - }, - // ### `forEach` executes the callback on all triples. - // Setting any field to `undefined` or `null` indicates a wildcard. - forEach: function (callback, subject, predicate, object, graph) { - var prefixes = this._prefixes; - this.forEachByIRI( - callback, - expandPrefixedName(subject, prefixes), - expandPrefixedName(predicate, prefixes), - expandPrefixedName(object, prefixes), - expandPrefixedName(graph, prefixes) - ); - }, + // 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 input; +} - // ### `forEachByIRI` executes the callback on all triples. - // Setting any field to `undefined` or `null` indicates a wildcard. - forEachByIRI: function (callback, subject, predicate, object, graph) { - this.someByIRI(function (quad) { - callback(quad); - return false; - }, subject, predicate, object, graph); - }, - - // ### `every` executes the callback on all triples, - // and returns `true` if it returns truthy for all them. - // Setting any field to `undefined` or `null` indicates a wildcard. - every: function (callback, subject, predicate, object, graph) { - var prefixes = this._prefixes; - return this.everyByIRI( - callback, - expandPrefixedName(subject, prefixes), - expandPrefixedName(predicate, prefixes), - expandPrefixedName(object, prefixes), - expandPrefixedName(graph, prefixes) - ); - }, +/** + * Compares two strings first based on length and then lexicographically. + * + * @param a the first string. + * @param b the second string. + * + * @return -1 if a < b, 1 if a > b, 0 if a == b. + */ +function _compareShortestLeast(a, b) { + if(a.length < b.length) { + return -1; + } + if(b.length < a.length) { + return 1; + } + if(a === b) { + return 0; + } + return (a < b) ? -1 : 1; +} - // ### `everyByIRI` executes the callback on all triples, - // and returns `true` if it returns truthy for all them. - // Setting any field to `undefined` or `null` indicates a wildcard. - everyByIRI: function (callback, subject, predicate, object, graph) { - var some = false; - var every = !this.someByIRI(function (quad) { - some = true; - return !callback(quad); - }, subject, predicate, object, graph); - return some && every; - }, +/** + * 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'; + } - // ### `some` executes the callback on all triples, - // and returns `true` if it returns truthy for any of them. - // Setting any field to `undefined` or `null` indicates a wildcard. - some: function (callback, subject, predicate, object, graph) { - var prefixes = this._prefixes; - return this.someByIRI( - callback, - expandPrefixedName(subject, prefixes), - expandPrefixedName(predicate, prefixes), - expandPrefixedName(object, prefixes), - expandPrefixedName(graph, prefixes) - ); - }, + // preferences for the value of @type or @language + var prefs = []; - // ### `someByIRI` executes the callback on all triples, - // and returns `true` if it returns truthy for any of them. - // Setting any field to `undefined` or `null` indicates a wildcard. - someByIRI: function (callback, subject, predicate, object, graph) { - var graphs = this._getGraphs(graph), content, - ids = this._ids, subjectId, predicateId, objectId; + // 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']); + } + } else { + prefs.push(typeOrLanguageValue); + } + prefs.push('@none'); - // Translate IRIs to internal index keys. - if (isString(subject) && !(subjectId = ids[subject]) || - isString(predicate) && !(predicateId = ids[predicate]) || - isString(object) && !(objectId = ids[object])) - return false; + 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; + } - for (var graphId in graphs) { - // Only if the specified graph contains triples, there can be result - if (content = graphs[graphId]) { - // Choose the optimal index, based on what fields are present - if (subjectId) { - if (objectId) { - // If subject and object are given, the object index will be the fastest - if (this._findInIndex(content.objects, objectId, subjectId, predicateId, - 'object', 'subject', 'predicate', graphId, callback, null)) - return true; - } - else - // If only subject and possibly predicate are given, the subject index will be the fastest - if (this._findInIndex(content.subjects, subjectId, predicateId, null, - 'subject', 'predicate', 'object', graphId, callback, null)) - return true; - } - else if (predicateId) { - // If only predicate and possibly object are given, the predicate index will be the fastest - if (this._findInIndex(content.predicates, predicateId, objectId, null, - 'predicate', 'object', 'subject', graphId, callback, null)) { - return true; - } - } - else if (objectId) { - // If only object is given, the object index will be the fastest - if (this._findInIndex(content.objects, objectId, null, null, - 'object', 'subject', 'predicate', graphId, callback, null)) { - return true; - } - } - else - // If nothing is given, iterate subjects and predicates first - if (this._findInIndex(content.subjects, null, null, null, - 'subject', 'predicate', 'object', graphId, callback, null)) { - return true; - } + 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; } - } - return false; - }, - // ### `getSubjects` returns all subjects that match the pattern. - // Setting any field to `undefined` or `null` indicates a wildcard. - getSubjects: function (predicate, object, graph) { - var prefixes = this._prefixes; - return this.getSubjectsByIRI( - expandPrefixedName(predicate, prefixes), - expandPrefixedName(object, prefixes), - expandPrefixedName(graph, prefixes) - ); - }, + // select term + return typeOrLanguageValueMap[pref]; + } + } - // ### `getSubjectsByIRI` returns all subjects that match the pattern. - // Setting any field to `undefined` or `null` indicates a wildcard. - getSubjectsByIRI: function (predicate, object, graph) { - var results = []; - this.forSubjectsByIRI(function (s) { results.push(s); }, predicate, object, graph); - return results; - }, + return null; +} - // ### `forSubjects` executes the callback on all subjects that match the pattern. - // Setting any field to `undefined` or `null` indicates a wildcard. - forSubjects: function (callback, predicate, object, graph) { - var prefixes = this._prefixes; - this.forSubjectsByIRI( - callback, - expandPrefixedName(predicate, prefixes), - expandPrefixedName(object, prefixes), - expandPrefixedName(graph, prefixes) - ); - }, +/** + * 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; + } - // ### `forSubjectsByIRI` executes the callback on all subjects that match the pattern. - // Setting any field to `undefined` or `null` indicates a wildcard. - forSubjectsByIRI: function (callback, predicate, object, graph) { - var ids = this._ids, graphs = this._getGraphs(graph), content, predicateId, objectId; - callback = this._uniqueEntities(callback); + // default value and parent to null + if(_isUndefined(value)) { + value = null; + } + // default reverse to false + if(_isUndefined(reverse)) { + reverse = false; + } + relativeTo = relativeTo || {}; - // Translate IRIs to internal index keys. - if (isString(predicate) && !(predicateId = ids[predicate]) || - isString(object) && !(objectId = ids[object])) - return; + var inverseCtx = activeCtx.getInverse(); - for (graph in graphs) { - // Only if the specified graph contains triples, there can be results - if (content = graphs[graph]) { - // Choose optimal index based on which fields are wildcards - if (predicateId) { - if (objectId) - // If predicate and object are given, the POS index is best. - this._loopBy2Keys(content.predicates, predicateId, objectId, callback); - else - // If only predicate is given, the SPO index is best. - this._loopByKey1(content.subjects, predicateId, callback); - } - else if (objectId) - // If only object is given, the OSP index is best. - this._loopByKey0(content.objects, objectId, callback); - else - // If no params given, iterate all the subjects - this._loop(content.subjects, callback); - } + // 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; + } - // ### `getPredicates` returns all predicates that match the pattern. - // Setting any field to `undefined` or `null` indicates a wildcard. - getPredicates: function (subject, object, graph) { - var prefixes = this._prefixes; - return this.getPredicatesByIRI( - expandPrefixedName(subject, prefixes), - expandPrefixedName(object, prefixes), - expandPrefixedName(graph, prefixes) - ); - }, + // use inverse context to pick a term if iri is relative to vocab + if(relativeTo.vocab && iri in inverseCtx) { + var defaultLanguage = activeCtx['@language'] || '@none'; - // ### `getPredicatesByIRI` returns all predicates that match the pattern. - // Setting any field to `undefined` or `null` indicates a wildcard. - getPredicatesByIRI: function (subject, object, graph) { - var results = []; - this.forPredicatesByIRI(function (p) { results.push(p); }, subject, object, graph); - return results; - }, + // prefer @index if available in value + var containers = []; + if(_isObject(value) && '@index' in value) { + containers.push('@index'); + } - // ### `forPredicates` executes the callback on all predicates that match the pattern. - // Setting any field to `undefined` or `null` indicates a wildcard. - forPredicates: function (callback, subject, object, graph) { - var prefixes = this._prefixes; - this.forPredicatesByIRI( - callback, - expandPrefixedName(subject, prefixes), - expandPrefixedName(object, prefixes), - expandPrefixedName(graph, prefixes) - ); - }, + // defaults for term selection based on type/language + var typeOrLanguage = '@language'; + var typeOrLanguageValue = '@null'; - // ### `forPredicatesByIRI` executes the callback on all predicates that match the pattern. - // Setting any field to `undefined` or `null` indicates a wildcard. - forPredicatesByIRI: function (callback, subject, object, graph) { - var ids = this._ids, graphs = this._getGraphs(graph), content, subjectId, objectId; - callback = this._uniqueEntities(callback); + 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; + } + } 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'); + } - // Translate IRIs to internal index keys. - if (isString(subject) && !(subjectId = ids[subject]) || - isString(object) && !(objectId = ids[object])) - return; + // do term selection + containers.push('@none'); + var term = _selectTerm( + activeCtx, iri, value, containers, typeOrLanguage, typeOrLanguageValue); + if(term !== null) { + return term; + } + } - for (graph in graphs) { - // Only if the specified graph contains triples, there can be results - if (content = graphs[graph]) { - // Choose optimal index based on which fields are wildcards - if (subjectId) { - if (objectId) - // If subject and object are given, the OSP index is best. - this._loopBy2Keys(content.objects, objectId, subjectId, callback); - else - // If only subject is given, the SPO index is best. - this._loopByKey0(content.subjects, subjectId, callback); + // 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; } - else if (objectId) - // If only object is given, the POS index is best. - this._loopByKey1(content.predicates, objectId, callback); - else - // If no params given, iterate all the predicates. - this._loop(content.predicates, callback); } } - }, + } - // ### `getObjects` returns all objects that match the pattern. - // Setting any field to `undefined` or `null` indicates a wildcard. - getObjects: function (subject, predicate, graph) { - var prefixes = this._prefixes; - return this.getObjectsByIRI( - expandPrefixedName(subject, prefixes), - expandPrefixedName(predicate, prefixes), - expandPrefixedName(graph, prefixes) - ); - }, + // 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]); + } + } + // 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)); - // ### `getObjectsByIRI` returns all objects that match the pattern. - // Setting any field to `undefined` or `null` indicates a wildcard. - getObjectsByIRI: function (subject, predicate, graph) { - var results = []; - this.forObjectsByIRI(function (o) { results.push(o); }, subject, predicate, graph); - return results; - }, + // 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; + } + } + } - // ### `forObjects` executes the callback on all objects that match the pattern. - // Setting any field to `undefined` or `null` indicates a wildcard. - forObjects: function (callback, subject, predicate, graph) { - var prefixes = this._prefixes; - this.forObjectsByIRI( - callback, - expandPrefixedName(subject, prefixes), - expandPrefixedName(predicate, prefixes), - expandPrefixedName(graph, prefixes) - ); - }, + // return chosen curie + if(choice !== null) { + return choice; + } - // ### `forObjectsByIRI` executes the callback on all objects that match the pattern. - // Setting any field to `undefined` or `null` indicates a wildcard. - forObjectsByIRI: function (callback, subject, predicate, graph) { - var ids = this._ids, graphs = this._getGraphs(graph), content, subjectId, predicateId; - callback = this._uniqueEntities(callback); + // compact IRI relative to base + if(!relativeTo.vocab) { + return _removeBase(activeCtx['@base'], iri); + } - // Translate IRIs to internal index keys. - if (isString(subject) && !(subjectId = ids[subject]) || - isString(predicate) && !(predicateId = ids[predicate])) - return; + // return IRI as is + return iri; +} - for (graph in graphs) { - // Only if the specified graph contains triples, there can be results - if (content = graphs[graph]) { - // Choose optimal index based on which fields are wildcards - if (subjectId) { - if (predicateId) - // If subject and predicate are given, the SPO index is best. - this._loopBy2Keys(content.subjects, subjectId, predicateId, callback); - else - // If only subject is given, the OSP index is best. - this._loopByKey1(content.objects, subjectId, callback); - } - else if (predicateId) - // If only predicate is given, the POS index is best. - this._loopByKey0(content.predicates, predicateId, callback); - else - // If no params given, iterate all the objects. - this._loop(content.objects, callback); - } - } - }, - - // ### `getGraphs` returns all graphs that match the pattern. - // Setting any field to `undefined` or `null` indicates a wildcard. - getGraphs: function (subject, predicate, object) { - var prefixes = this._prefixes; - return this.getGraphsByIRI( - expandPrefixedName(subject, prefixes), - expandPrefixedName(predicate, prefixes), - expandPrefixedName(object, prefixes) - ); - }, - - // ### `getGraphsByIRI` returns all graphs that match the pattern. - // Setting any field to `undefined` or `null` indicates a wildcard. - getGraphsByIRI: function (subject, predicate, object) { - var results = []; - this.forGraphsByIRI(function (g) { results.push(g); }, subject, predicate, object); - return results; - }, +/** + * 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'); - // ### `forGraphs` executes the callback on all graphs that match the pattern. - // Setting any field to `undefined` or `null` indicates a wildcard. - forGraphs: function (callback, subject, predicate, object) { - var prefixes = this._prefixes; - this.forGraphsByIRI( - callback, - expandPrefixedName(subject, prefixes), - expandPrefixedName(predicate, prefixes), - expandPrefixedName(object, prefixes) - ); - }, + // whether or not the value has an @index that must be preserved + var preserveIndex = (('@index' in value) && + container !== '@index'); - // ### `forGraphsByIRI` executes the callback on all graphs that match the pattern. - // Setting any field to `undefined` or `null` indicates a wildcard. - forGraphsByIRI: function (callback, subject, predicate, object) { - for (var graph in this._graphs) { - this.someByIRI(function (quad) { - callback(quad.graph); - return true; // Halt iteration of some() - }, subject, predicate, object, graph); + // 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']; + } } - }, - // ### `createBlankNode` creates a new blank node, returning its name - createBlankNode: function (suggestedName) { - var name, index; - // Generate a name based on the suggested name - if (suggestedName) { - name = suggestedName = '_:' + suggestedName, index = 1; - while (this._ids[name]) - name = suggestedName + index++; - } - // Generate a generic blank node name - else { - do { name = '_:b' + this._blankNodeIndex++; } - while (this._ids[name]); + // 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']; } - // Add the blank node to the entities, avoiding the generation of duplicates - this._ids[name] = ++this._id; - this._entities[this._id] = name; - return name; - }, -}; - -// Determines whether the argument is a string -function isString(s) { - return typeof s === 'string' || s instanceof String; -} - -// ## Exports -module.exports = N3Store; - -},{"./N3Util":37}],35:[function(_dereq_,module,exports){ -// **N3StreamParser** parses an N3 stream into a triple stream. -var Transform = _dereq_('stream').Transform, - util = _dereq_('util'), - N3Parser = _dereq_('./N3Parser.js'); - -// ## Constructor -function N3StreamParser(options) { - if (!(this instanceof N3StreamParser)) - return new N3StreamParser(options); - - // Initialize Transform base class - Transform.call(this, { decodeStrings: true }); - this._readableState.objectMode = true; - // Set up parser - var self = this, parser = new N3Parser(options), onData, onEnd; - // Pass dummy stream to obtain `data` and `end` callbacks - parser.parse({ - on: function (event, cb) { - switch (event) { - case 'data': onData = cb; break; - case 'end': onEnd = cb; break; - } - }, - }, - // Handle triples by pushing them down the pipeline - function (error, t) { error && self.emit('error', error) || t && self.push(t); }, - // Emit prefixes through the `prefix` event - function (prefix, uri) { self.emit('prefix', prefix, uri); }); + var rval = {}; - // Implement Transform methods through parser callbacks - this._transform = function (chunk, encoding, done) { onData(chunk); done(); }; - this._flush = function (done) { onEnd(); done(); }; -} -util.inherits(N3StreamParser, Transform); + // preserve @index + if(preserveIndex) { + rval[_compactIri(activeCtx, '@index')] = value['@index']; + } -// ## Exports -module.exports = N3StreamParser; + 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']; + } -},{"./N3Parser.js":33,"stream":107,"util":118}],36:[function(_dereq_,module,exports){ -// **N3StreamWriter** serializes a triple stream into an N3 stream. -var Transform = _dereq_('stream').Transform, - util = _dereq_('util'), - N3Writer = _dereq_('./N3Writer.js'); + // alias @value + rval[_compactIri(activeCtx, '@value')] = value['@value']; -// ## Constructor -function N3StreamWriter(options) { - if (!(this instanceof N3StreamWriter)) - return new N3StreamWriter(options); + return rval; + } - // Initialize Transform base class - Transform.call(this, { encoding: 'utf8' }); - this._writableState.objectMode = true; + // 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'}); - // Set up writer with a dummy stream object - var self = this; - var writer = new N3Writer({ - write: function (chunk, encoding, callback) { self.push(chunk); callback && callback(); }, - end: function (callback) { self.push(null); callback && callback(); }, - }, options); + // compact to scalar + if(type === '@id' || type === '@vocab' || expandedProperty === '@graph') { + return compacted; + } - // Implement Transform methods on top of writer - this._transform = function (triple, encoding, done) { writer.addTriple(triple, done); }; - this._flush = function (done) { writer.end(done); }; + var rval = {}; + rval[_compactIri(activeCtx, '@id')] = compacted; + return rval; } -util.inherits(N3StreamWriter, Transform); - -// ## Exports -module.exports = N3StreamWriter; - -},{"./N3Writer.js":38,"stream":107,"util":118}],37:[function(_dereq_,module,exports){ -// **N3Util** provides N3 utility functions. -var Xsd = 'http://www.w3.org/2001/XMLSchema#'; -var XsdString = Xsd + 'string'; -var XsdInteger = Xsd + 'integer'; -var XsdDouble = Xsd + 'double'; -var XsdBoolean = Xsd + 'boolean'; -var RdfLangString = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString'; - -var N3Util = { - // Tests whether the given entity (triple object) represents an IRI in the N3 library - isIRI: function (entity) { - if (typeof entity !== 'string') - return false; - else if (entity.length === 0) - return true; - else { - var firstChar = entity[0]; - return firstChar !== '"' && firstChar !== '_'; +/** + * Creates a term definition during context processing. + * + * @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. + */ +function _createTermDefinition(activeCtx, localCtx, term, defined) { + if(term in defined) { + // term already defined + if(defined[term]) { + return; } - }, - - // Tests whether the given entity (triple object) represents a literal in the N3 library - isLiteral: function (entity) { - return typeof entity === 'string' && entity[0] === '"'; - }, + // cycle detected + throw new JsonLdError( + 'Cyclical context definition detected.', + 'jsonld.CyclicalContext', + {code: 'cyclic IRI mapping', context: localCtx, term: term}); + } - // Tests whether the given entity (triple object) represents a blank node in the N3 library - isBlank: function (entity) { - return typeof entity === 'string' && entity.substr(0, 2) === '_:'; - }, + // now defining term + defined[term] = false; - // Tests whether the given entity represents the default graph - isDefaultGraph: function (entity) { - return !entity; - }, + if(_isKeyword(term)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; keywords cannot be overridden.', + 'jsonld.SyntaxError', + {code: 'keyword redefinition', context: localCtx, term: term}); + } - // Tests whether the given triple is in the default graph - inDefaultGraph: function (triple) { - return !triple.graph; - }, + if(term === '') { + throw new JsonLdError( + 'Invalid JSON-LD syntax; a term cannot be an empty string.', + 'jsonld.SyntaxError', + {code: 'invalid term definition', context: localCtx}); + } - // Gets the string value of a literal in the N3 library - getLiteralValue: function (literal) { - var match = /^"([^]*)"/.exec(literal); - if (!match) - throw new Error(literal + ' is not a literal'); - return match[1]; - }, + // remove old mapping + if(activeCtx.mappings[term]) { + delete activeCtx.mappings[term]; + } - // Gets the type of a literal in the N3 library - getLiteralType: function (literal) { - var match = /^"[^]*"(?:\^\^([^"]+)|(@)[^@"]+)?$/.exec(literal); - if (!match) - throw new Error(literal + ' is not a literal'); - return match[1] || (match[2] ? RdfLangString : XsdString); - }, + // get context term value + var value = localCtx[term]; - // Gets the language of a literal in the N3 library - getLiteralLanguage: function (literal) { - var match = /^"[^]*"(?:@([^@"]+)|\^\^[^"]+)?$/.exec(literal); - if (!match) - throw new Error(literal + ' is not a literal'); - return match[1] ? match[1].toLowerCase() : ''; - }, + // clear context entry + if(value === null || (_isObject(value) && value['@id'] === null)) { + activeCtx.mappings[term] = null; + defined[term] = true; + return; + } - // Tests whether the given entity (triple object) represents a prefixed name - isPrefixedName: function (entity) { - return typeof entity === 'string' && /^[^:\/"']*:[^:\/"']+$/.test(entity); - }, + // convert short-hand value to object w/@id + if(_isString(value)) { + value = {'@id': value}; + } - // Expands the prefixed name to a full IRI (also when it occurs as a literal's type) - expandPrefixedName: function (prefixedName, prefixes) { - var match = /(?:^|"\^\^)([^:\/#"'\^_]*):[^\/]*$/.exec(prefixedName), prefix, base, index; - if (match) - prefix = match[1], base = prefixes[prefix], index = match.index; - if (base === undefined) - return prefixedName; + 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}); + } - // The match index is non-zero when expanding a literal's type - return index === 0 ? base + prefixedName.substr(prefix.length + 1) - : prefixedName.substr(0, index + 3) + - base + prefixedName.substr(index + prefix.length + 4); - }, + // create new mapping + var mapping = activeCtx.mappings[term] = {}; + mapping.reverse = false; - // Creates an IRI in N3.js representation - createIRI: function (iri) { - return iri && iri[0] === '"' ? N3Util.getLiteralValue(iri) : iri; - }, + 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}); + } - // Creates a literal in N3.js representation - createLiteral: function (value, modifier) { - if (!modifier) { - switch (typeof value) { - case 'boolean': - modifier = XsdBoolean; - break; - case 'number': - if (isFinite(value)) - modifier = value % 1 === 0 ? XsdInteger : XsdDouble; - else { - modifier = XsdDouble; - if (!isNaN(value)) - value = value > 0 ? 'INF' : '-INF'; - } - break; - default: - return '"' + value + '"'; + // 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; } - return '"' + value + - (/^[a-z]+(-[a-z0-9]+)*$/i.test(modifier) ? '"@' + modifier.toLowerCase() - : '"^^' + modifier); - }, + } - // Creates a function that prepends the given IRI to a local name - prefix: function (iri) { - return N3Util.prefixes({ '': iri })(''); - }, + // always compute whether term has a colon as an optimization for + // _compactIri + var colon = term.indexOf(':'); + mapping._termHasColon = (colon !== -1); - // Creates a function that allows registering and expanding prefixes - prefixes: function (defaultPrefixes) { - // Add all of the default prefixes - var prefixes = Object.create(null); - for (var prefix in defaultPrefixes) - processPrefix(prefix, defaultPrefixes[prefix]); + 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); + } - // Registers a new prefix (if an IRI was specified) - // or retrieves a function that expands an existing prefix (if no IRI was specified) - function processPrefix(prefix, iri) { - // Create a new prefix if an IRI is specified or the prefix doesn't exist - if (iri || !(prefix in prefixes)) { - var cache = Object.create(null); - iri = iri || ''; - // Create a function that expands the prefix - prefixes[prefix] = function (localName) { - return cache[localName] || (cache[localName] = iri + localName); - }; + 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; } - return prefixes[prefix]; + } 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; } - return processPrefix; - }, -}; + } -// ## Exports -module.exports = N3Util; + // IRI mapping now defined + defined[term] = true; -},{}],38:[function(_dereq_,module,exports){ -// **N3Writer** writes N3 documents. + 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}); + } -// Matches a literal as represented in memory by the N3 library -var N3LiteralMatcher = /^"([^]*)"(?:\^\^(.+)|@([\-a-z]+))?$/i; + 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}); + } + } -// rdf:type predicate (for 'a' abbreviation) -var RDF_PREFIX = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', - RDF_TYPE = RDF_PREFIX + 'type'; + // add @type to mapping + mapping['@type'] = type; + } -// Characters in literals that require escaping -var escape = /["\\\t\n\r\b\f\u0000-\u0019\ud800-\udbff]/, - escapeAll = /["\\\t\n\r\b\f\u0000-\u0019]|[\ud800-\udbff][\udc00-\udfff]/g, - escapedCharacters = { - '\\': '\\\\', '"': '\\"', '\t': '\\t', - '\n': '\\n', '\r': '\\r', '\b': '\\b', '\f': '\\f', - }; + 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}); + } -// ## Constructor -function N3Writer(outputStream, options) { - if (!(this instanceof N3Writer)) - return new N3Writer(outputStream, options); + // add @container to mapping + mapping['@container'] = container; + } - // Shift arguments if the first argument is not a stream - if (outputStream && typeof outputStream.write !== 'function') - options = outputStream, outputStream = null; - options = options || {}; + 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}); + } - // If no output stream given, send the output as string through the end callback - if (!outputStream) { - var output = ''; - this._outputStream = { - write: function (chunk, encoding, done) { output += chunk; done && done(); }, - end: function (done) { done && done(null, output); }, - }; - this._endStream = true; + // add @language to mapping + if(language !== null) { + language = language.toLowerCase(); + } + mapping['@language'] = language; } - else { - this._outputStream = outputStream; - this._endStream = options.end === undefined ? true : !!options.end; + + // 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}); } +} - // Initialize writer, depending on the format - this._subject = null; - if (!(/triple|quad/i).test(options.format)) { - this._graph = ''; - this._prefixIRIs = Object.create(null); - options.prefixes && this.addPrefixes(options.prefixes); +/** + * 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; } - else { - this._writeTriple = this._writeTripleLine; + + // ensure value is interpreted as a string + value = String(value); + + // define term dependency if not defined + if(localCtx && value in localCtx && defined[value] !== true) { + _createTermDefinition(activeCtx, localCtx, value, defined); } -} -N3Writer.prototype = { - // ## Private methods + relativeTo = relativeTo || {}; + if(relativeTo.vocab) { + var mapping = activeCtx.mappings[value]; - // ### `_write` writes the argument to the output stream - _write: function (string, callback) { - this._outputStream.write(string, 'utf8', callback); - }, + // value is explicitly ignored with a null mapping + if(mapping === null) { + return null; + } - // ### `_writeTriple` writes the triple to the output stream - _writeTriple: function (subject, predicate, object, graph, done) { - try { - // Write the graph's label if it has changed - if (this._graph !== graph) { - // Close the previous graph and start the new one - this._write((this._subject === null ? '' : (this._graph ? '\n}\n' : '.\n')) + - (graph ? this._encodeIriOrBlankNode(graph) + ' {\n' : '')); - this._subject = null; - // Don't treat identical blank nodes as repeating graphs - this._graph = graph[0] !== '[' ? graph : ']'; - } - // Don't repeat the subject if it's the same - if (this._subject === subject) { - // Don't repeat the predicate if it's the same - if (this._predicate === predicate) - this._write(', ' + this._encodeObject(object), done); - // Same subject, different predicate - else - this._write(';\n ' + - this._encodePredicate(this._predicate = predicate) + ' ' + - this._encodeObject(object), done); - } - // Different subject; write the whole triple - else - this._write((this._subject === null ? '' : '.\n') + - this._encodeSubject(this._subject = subject) + ' ' + - this._encodePredicate(this._predicate = predicate) + ' ' + - this._encodeObject(object), done); + if(mapping) { + // value is a term + return mapping['@id']; } - catch (error) { done && done(error); } - }, + } - // ### `_writeTripleLine` writes the triple or quad to the output stream as a single line - _writeTripleLine: function (subject, predicate, object, graph, done) { - // Write the triple without prefixes - delete this._prefixMatch; - try { this._write(this.tripleToString(subject, predicate, object, graph), done); } - catch (error) { done && done(error); } - }, + // split value into prefix:suffix + var colon = value.indexOf(':'); + if(colon !== -1) { + var prefix = value.substr(0, colon); + var suffix = value.substr(colon + 1); - // ### `tripleToString` serializes a triple or quad as a string - tripleToString: function (subject, predicate, object, graph) { - return this._encodeIriOrBlankNode(subject) + ' ' + - this._encodeIriOrBlankNode(predicate) + ' ' + - this._encodeObject(object) + - (graph ? ' ' + this._encodeIriOrBlankNode(graph) + '.\n' : '.\n'); - }, + // do not expand blank nodes (prefix of '_') or already-absolute + // IRIs (suffix of '//') + if(prefix === '_' || suffix.indexOf('//') === 0) { + return value; + } - // ### `triplesToString` serializes an array of triples or quads as a string - triplesToString: function (triples) { - return triples.map(function (t) { - return this.tripleToString(t.subject, t.predicate, t.object, t.graph); - }, this).join(''); - }, + // prefix dependency not defined, define it + if(localCtx && prefix in localCtx) { + _createTermDefinition(activeCtx, localCtx, prefix, defined); + } - // ### `_encodeIriOrBlankNode` represents an IRI or blank node - _encodeIriOrBlankNode: function (entity) { - // A blank node or list is represented as-is - var firstChar = entity[0]; - if (firstChar === '[' || firstChar === '(' || firstChar === '_' && entity[1] === ':') - return entity; - // Escape special characters - if (escape.test(entity)) - entity = entity.replace(escapeAll, characterReplacer); - // Try to represent the IRI as prefixed name - var prefixMatch = this._prefixRegex.exec(entity); - return !prefixMatch ? '<' + entity + '>' : - (!prefixMatch[1] ? entity : this._prefixIRIs[prefixMatch[1]] + prefixMatch[2]); - }, + // use mapping if prefix is defined + var mapping = activeCtx.mappings[prefix]; + if(mapping) { + return mapping['@id'] + suffix; + } - // ### `_encodeLiteral` represents a literal - _encodeLiteral: function (value, type, language) { - // Escape special characters - if (escape.test(value)) - value = value.replace(escapeAll, characterReplacer); - // Write the literal, possibly with type or language - if (language) - return '"' + value + '"@' + language; - else if (type) - return '"' + value + '"^^' + this._encodeIriOrBlankNode(type); - else - return '"' + value + '"'; - }, + // already absolute IRI + return value; + } - // ### `_encodeSubject` represents a subject - _encodeSubject: function (subject) { - if (subject[0] === '"') - throw new Error('A literal as subject is not allowed: ' + subject); - // Don't treat identical blank nodes as repeating subjects - if (subject[0] === '[') - this._subject = ']'; - return this._encodeIriOrBlankNode(subject); - }, + // prepend vocab + if(relativeTo.vocab && '@vocab' in activeCtx) { + return activeCtx['@vocab'] + value; + } - // ### `_encodePredicate` represents a predicate - _encodePredicate: function (predicate) { - if (predicate[0] === '"') - throw new Error('A literal as predicate is not allowed: ' + predicate); - return predicate === RDF_TYPE ? 'a' : this._encodeIriOrBlankNode(predicate); - }, + // prepend base + var rval = value; + if(relativeTo.base) { + rval = jsonld.prependBase(activeCtx['@base'], rval); + } - // ### `_encodeObject` represents an object - _encodeObject: function (object) { - // Represent an IRI or blank node - if (object[0] !== '"') - return this._encodeIriOrBlankNode(object); - // Represent a literal - var match = N3LiteralMatcher.exec(object); - if (!match) throw new Error('Invalid literal: ' + object); - return this._encodeLiteral(match[1], match[2], match[3]); - }, + return rval; +} - // ### `_blockedWrite` replaces `_write` after the writer has been closed - _blockedWrite: function () { - throw new Error('Cannot write because the writer has been closed.'); - }, +function _prependBase(base, iri) { + // skip IRI processing + if(base === null) { + return iri; + } + // already an absolute IRI + if(iri.indexOf(':') !== -1) { + return iri; + } - // ### `addTriple` adds the triple to the output stream - addTriple: function (subject, predicate, object, graph, done) { - // The triple was given as a triple object, so shift parameters - if (object === undefined) - this._writeTriple(subject.subject, subject.predicate, subject.object, - subject.graph || '', predicate); - // The optional `graph` parameter was not provided - else if (typeof graph !== 'string') - this._writeTriple(subject, predicate, object, '', graph); - // The `graph` parameter was provided - else - this._writeTriple(subject, predicate, object, graph, done); - }, + // parse base if it is a string + if(_isString(base)) { + base = jsonld.url.parse(base || ''); + } - // ### `addTriples` adds the triples to the output stream - addTriples: function (triples) { - for (var i = 0; i < triples.length; i++) - this.addTriple(triples[i]); - }, + // parse given IRI + var rel = jsonld.url.parse(iri); - // ### `addPrefix` adds the prefix to the output stream - addPrefix: function (prefix, iri, done) { - var prefixes = {}; - prefixes[prefix] = iri; - this.addPrefixes(prefixes, done); - }, + // per RFC3986 5.2.2 + var transform = { + protocol: base.protocol || '' + }; - // ### `addPrefixes` adds the prefixes to the output stream - addPrefixes: function (prefixes, done) { - // Add all useful prefixes - var prefixIRIs = this._prefixIRIs, hasPrefixes = false; - for (var prefix in prefixes) { - // Verify whether the prefix can be used and does not exist yet - var iri = prefixes[prefix]; - if (/[#\/]$/.test(iri) && prefixIRIs[iri] !== (prefix += ':')) { - hasPrefixes = true; - prefixIRIs[iri] = prefix; - // Finish a possible pending triple - if (this._subject !== null) { - this._write(this._graph ? '\n}\n' : '.\n'); - this._subject = null, this._graph = ''; - } - // Write prefix - this._write('@prefix ' + prefix + ' <' + iri + '>.\n'); - } - } - // Recreate the prefix matcher - if (hasPrefixes) { - var IRIlist = '', prefixList = ''; - for (var prefixIRI in prefixIRIs) { - IRIlist += IRIlist ? '|' + prefixIRI : prefixIRI; - prefixList += (prefixList ? '|' : '') + prefixIRIs[prefixIRI]; - } - IRIlist = IRIlist.replace(/[\]\/\(\)\*\+\?\.\\\$]/g, '\\$&'); - this._prefixRegex = new RegExp('^(?:' + prefixList + ')[^\/]*$|' + - '^(' + IRIlist + ')([a-zA-Z][\\-_a-zA-Z0-9]*)$'); - } - // End a prefix block with a newline - this._write(hasPrefixes ? '\n' : '', done); - }, + if(rel.authority !== null) { + transform.authority = rel.authority; + transform.path = rel.path; + transform.query = rel.query; + } else { + transform.authority = base.authority; - // ### `blank` creates a blank node with the given content - blank: function (predicate, object) { - var children = predicate, child, length; - // Empty blank node - if (predicate === undefined) - children = []; - // Blank node passed as blank("predicate", "object") - else if (typeof predicate === 'string') - children = [{ predicate: predicate, object: object }]; - // Blank node passed as blank({ predicate: predicate, object: object }) - else if (!('length' in predicate)) - children = [predicate]; + if(rel.path === '') { + transform.path = base.path; + if(rel.query !== null) { + transform.query = rel.query; + } else { + transform.query = base.query; + } + } else { + if(rel.path.indexOf('/') === 0) { + // IRI represents an absolute path + transform.path = rel.path; + } else { + // merge paths + var path = base.path; - switch (length = children.length) { - // Generate an empty blank node - case 0: - return '[]'; - // Generate a non-nested one-triple blank node - case 1: - child = children[0]; - if (child.object[0] !== '[') - return '[ ' + this._encodePredicate(child.predicate) + ' ' + - this._encodeObject(child.object) + ' ]'; - // Generate a multi-triple or nested blank node - default: - var contents = '['; - // Write all triples in order - for (var i = 0; i < length; i++) { - child = children[i]; - // Write only the object is the predicate is the same as the previous - if (child.predicate === predicate) - contents += ', ' + this._encodeObject(child.object); - // Otherwise, write the predicate and the object - else { - contents += (i ? ';\n ' : '\n ') + - this._encodePredicate(child.predicate) + ' ' + - this._encodeObject(child.object); - predicate = child.predicate; + // 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; } + + transform.path = path; } - return contents + '\n]'; + transform.query = rel.query; } - }, + } - // ### `list` creates a list node with the given content - list: function (elements) { - var length = elements && elements.length || 0, contents = new Array(length); - for (var i = 0; i < length; i++) - contents[i] = this._encodeObject(elements[i]); - return '(' + contents.join(' ') + ')'; - }, + // remove slashes and dots in path + transform.path = _removeDotSegments(transform.path, !!transform.authority); - // ### `_prefixRegex` matches a prefixed name or IRI that begins with one of the added prefixes - _prefixRegex: /$0^/, + // 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; + } - // ### `end` signals the end of the output stream - end: function (done) { - // Finish a possible pending triple - if (this._subject !== null) { - this._write(this._graph ? '\n}\n' : '.\n'); - this._subject = null; - } - // Disallow further writing - this._write = this._blockedWrite; + // handle empty base + if(rval === '') { + rval = './'; + } - // Try to end the underlying stream, ensuring done is called exactly one time - var singleDone = done && function (error, result) { singleDone = null, done(error, result); }; - if (this._endStream) { - try { return this._outputStream.end(singleDone); } - catch (error) { /* error closing stream */ } - } - singleDone && singleDone(); - }, -}; + return rval; +} -// Replaces a character by its escaped version -function characterReplacer(character) { - // Replace a single character by its escaped version - var result = escapedCharacters[character]; - if (result === undefined) { - // Replace a single character with its 4-bit unicode escape sequence - if (character.length === 1) { - result = character.charCodeAt(0).toString(16); - result = '\\u0000'.substr(0, 6 - result.length) + result; - } - // Replace a surrogate pair with its 8-bit unicode escape sequence - else { - result = ((character.charCodeAt(0) - 0xD800) * 0x400 + - character.charCodeAt(1) + 0x2400).toString(16); - result = '\\U00000000'.substr(0, 10 - result.length) + result; - } +/** + * 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; } - return result; -} -// ## Exports -module.exports = N3Writer; + if(_isString(base)) { + base = jsonld.url.parse(base || ''); + } -},{}],39:[function(_dereq_,module,exports){ -'use strict'; + // 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 += '//'; + } -// modified from https://github.com/es-shims/es5-shim -var has = Object.prototype.hasOwnProperty; -var toStr = Object.prototype.toString; -var slice = Array.prototype.slice; -var isArgs = _dereq_('./isArguments'); -var isEnumerable = Object.prototype.propertyIsEnumerable; -var hasDontEnumBug = !isEnumerable.call({ toString: null }, 'toString'); -var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype'); -var dontEnums = [ - 'toString', - 'toLocaleString', - 'valueOf', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'constructor' -]; -var equalsConstructorPrototype = function (o) { - var ctor = o.constructor; - return ctor && ctor.prototype === o; -}; -var excludedKeys = { - $console: true, - $external: true, - $frame: true, - $frameElement: true, - $frames: true, - $innerHeight: true, - $innerWidth: true, - $outerHeight: true, - $outerWidth: true, - $pageXOffset: true, - $pageYOffset: true, - $parent: true, - $scrollLeft: true, - $scrollTop: true, - $scrollX: true, - $scrollY: true, - $self: true, - $webkitIndexedDB: true, - $webkitStorageInfo: true, - $window: true -}; -var hasAutomationEqualityBug = (function () { - /* global window */ - if (typeof window === 'undefined') { return false; } - for (var k in window) { - try { - if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') { - try { - equalsConstructorPrototype(window[k]); - } catch (e) { - return true; - } - } - } catch (e) { - return true; - } - } - return false; -}()); -var equalsConstructorPrototypeIfNotBuggy = function (o) { - /* global window */ - if (typeof window === 'undefined' || !hasAutomationEqualityBug) { - return equalsConstructorPrototype(o); - } - try { - return equalsConstructorPrototype(o); - } catch (e) { - return false; - } -}; - -var keysShim = function keys(object) { - var isObject = object !== null && typeof object === 'object'; - var isFunction = toStr.call(object) === '[object Function]'; - var isArguments = isArgs(object); - var isString = isObject && toStr.call(object) === '[object String]'; - var theKeys = []; + // IRI not relative to base + if(iri.indexOf(root) !== 0) { + return iri; + } - if (!isObject && !isFunction && !isArguments) { - throw new TypeError('Object.keys called on a non-object'); - } + // remove root from IRI and parse remainder + var rel = jsonld.url.parse(iri.substr(root.length)); - var skipProto = hasProtoEnumBug && isFunction; - if (isString && object.length > 0 && !has.call(object, 0)) { - for (var i = 0; i < object.length; ++i) { - theKeys.push(String(i)); - } - } + // 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; + } + baseSegments.shift(); + iriSegments.shift(); + } - if (isArguments && object.length > 0) { - for (var j = 0; j < object.length; ++j) { - theKeys.push(String(j)); - } - } else { - for (var name in object) { - if (!(skipProto && name === 'prototype') && has.call(object, name)) { - theKeys.push(String(name)); - } - } - } + // 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 += '../'; + } + } - if (hasDontEnumBug) { - var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object); + // prepend remaining segments + rval += iriSegments.join('/'); - for (var k = 0; k < dontEnums.length; ++k) { - if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) { - theKeys.push(dontEnums[k]); - } - } - } - return theKeys; -}; + // add query and hash + if(rel.query !== null) { + rval += '?' + rel.query; + } + if(rel.fragment !== null) { + rval += '#' + rel.fragment; + } -keysShim.shim = function shimObjectKeys() { - if (Object.keys) { - var keysWorksWithArguments = (function () { - // Safari 5.0 bug - return (Object.keys(arguments) || '').length === 2; - }(1, 2)); - if (!keysWorksWithArguments) { - var originalKeys = Object.keys; - Object.keys = function keys(object) { - if (isArgs(object)) { - return originalKeys(slice.call(object)); - } else { - return originalKeys(object); - } - }; - } - } else { - Object.keys = keysShim; - } - return Object.keys || keysShim; -}; + // handle empty base + if(rval === '') { + rval = './'; + } -module.exports = keysShim; + return rval; +} -},{"./isArguments":40}],40:[function(_dereq_,module,exports){ -'use strict'; +/** + * 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 + }; -var toStr = Object.prototype.toString; + /** + * 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; -module.exports = function isArguments(value) { - var str = toStr.call(value); - var isArgs = str === '[object Arguments]'; - if (!isArgs) { - isArgs = str !== '[object Array]' && - value !== null && - typeof value === 'object' && - typeof value.length === 'number' && - value.length >= 0 && - toStr.call(value.callee) === '[object Function]'; - } - return isArgs; -}; + // lazily create inverse + if(activeCtx.inverse) { + return activeCtx.inverse; + } + var inverse = activeCtx.inverse = {}; -},{}],41:[function(_dereq_,module,exports){ -/** -* pretty-data - nodejs plugin to pretty-print or minify data in XML, JSON and CSS formats. -* -* Version - 0.40.0 -* Copyright (c) 2012 Vadim Kiryukhin -* vkiryukhin @ gmail.com -* http://www.eslinstructor.net/pretty-data/ -* -* Dual licensed under the MIT and GPL licenses: -* http://www.opensource.org/licenses/mit-license.php -* http://www.gnu.org/licenses/gpl.html -* -* pd.xml(data ) - pretty print XML; -* pd.json(data) - pretty print JSON; -* pd.css(data ) - pretty print CSS; -* pd.sql(data) - pretty print SQL; -* -* pd.xmlmin(data [, preserveComments] ) - minify XML; -* pd.jsonmin(data) - minify JSON; -* pd.cssmin(data [, preserveComments] ) - minify CSS; -* pd.sqlmin(data) - minify SQL; -* -* PARAMETERS: -* -* @data - String; XML, JSON, CSS or SQL text to beautify; -* @preserveComments - Bool (optional, used in minxml and mincss only); -* Set this flag to true to prevent removing comments from @text; -* @Return - String; -* -* USAGE: -* -* var pd = require('pretty-data').pd; -* -* var xml_pp = pd.xml(xml_text); -* var xml_min = pd.xmlmin(xml_text [,true]); -* var json_pp = pd.json(json_text); -* var json_min = pd.jsonmin(json_text); -* var css_pp = pd.css(css_text); -* var css_min = pd.cssmin(css_text [, true]); -* var sql_pp = pd.sql(sql_text); -* var sql_min = pd.sqlmin(sql_text); -* -* TEST: -* comp-name:pretty-data$ node ./test/test_xml -* comp-name:pretty-data$ node ./test/test_json -* comp-name:pretty-data$ node ./test/test_css -* comp-name:pretty-data$ node ./test/test_sql -*/ + // variables for building fast CURIE map + var fastCurieMap = activeCtx.fastCurieMap = {}; + var irisToTerms = {}; + // handle default language + var defaultLanguage = activeCtx['@language'] || '@none'; -function pp() { - this.shift = ['\n']; // array of shifts - this.step = ' ', // 2 spaces - maxdeep = 100, // nesting level - ix = 0; + // 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; + } - // initialize array with shifts // - for(ix=0;ix\s{0,}<") - .replace(/ or -1) { - str += this.shift[deep]+ar[ix]; - inComment = true; - // end comment or // - if(ar[ix].search(/-->/) > -1 || ar[ix].search(/\]>/) > -1 || ar[ix].search(/!DOCTYPE/) > -1 ) { - inComment = false; - } - } else - // end comment or // - if(ar[ix].search(/-->/) > -1 || ar[ix].search(/\]>/) > -1) { - str += ar[ix]; - inComment = false; - } else - // // - if( /^<\w/.exec(ar[ix-1]) && /^<\/\w/.exec(ar[ix]) && - /^<[\w:\-\.\,]+/.exec(ar[ix-1]) == /^<\/[\w:\-\.\,]+/.exec(ar[ix])[0].replace('/','')) { - str += ar[ix]; - if(!inComment) deep--; - } else - // // - if(ar[ix].search(/<\w/) > -1 && ar[ix].search(/<\//) == -1 && ar[ix].search(/\/>/) == -1 ) { - str = !inComment ? str += this.shift[deep++]+ar[ix] : str += ar[ix]; - } else - // ... // - if(ar[ix].search(/<\w/) > -1 && ar[ix].search(/<\//) > -1) { - str = !inComment ? str += this.shift[deep]+ar[ix] : str += ar[ix]; - } else - // // - if(ar[ix].search(/<\//) > -1) { - str = !inComment ? str += this.shift[--deep]+ar[ix] : str += ar[ix]; - } else - // // - if(ar[ix].search(/\/>/) > -1 ) { - str = !inComment ? str += this.shift[deep]+ar[ix] : str += ar[ix]; - } else - // // - if(ar[ix].search(/<\?/) > -1) { - str += this.shift[deep]+ar[ix]; - } else - // xmlns // - if( ar[ix].search(/xmlns\:/) > -1 || ar[ix].search(/xmlns\=/) > -1) { - str += this.shift[deep]+ar[ix]; - } - - else { - str += ar[ix]; - } - } - - return (str[0] == '\n') ? str.slice(1) : str; -} + // add new entry + if(!entry[container]) { + entry[container] = { + '@language': {}, + '@type': {} + }; + } + entry = entry[container]; -// ----------------------- JSON section ---------------------------------------------------- + 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); -pp.prototype.json = function(text) { + // add entries for no type and no language + _addPreferredTerm(mapping, term, entry['@type'], '@none'); + _addPreferredTerm(mapping, term, entry['@language'], '@none'); + } + } + } - if ( typeof text === "string" ) { - return JSON.stringify(JSON.parse(text), null, this.step); - } - if ( typeof text === "object" ) { - return JSON.stringify(text, null, this.step); - } - return null; -} + // build fast CURIE map + for(var key in fastCurieMap) { + _buildIriMap(fastCurieMap, key, 1); + } -// ----------------------- CSS section ---------------------------------------------------- + return inverse; + } -pp.prototype.css = function(text) { + /** + * 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 ar = text.replace(/\s{1,}/g,' ') - .replace(/\{/g,"{~::~") - .replace(/\}/g,"~::~}~::~") - .replace(/\;/g,";~::~") - .replace(/\/\*/g,"~::~/*") - .replace(/\*\//g,"*/~::~") - .replace(/~::~\s{0,}~::~/g,"~::~") - .split('~::~'), - len = ar.length, - deep = 0, - str = '', - ix = 0; - - for(ix=0;ix= iri.length) { + letter = ''; + } else { + letter = iri[idx]; + } + if(letter in next) { + next[letter].push(entries[i]); + } else { + next[letter] = [entries[i]]; + } + } - if( /\{/.exec(ar[ix])) { - str += this.shift[deep++]+ar[ix]; - } else - if( /\}/.exec(ar[ix])) { - str += this.shift[--deep]+ar[ix]; - } else - if( /\*\\/.exec(ar[ix])) { - str += this.shift[deep]+ar[ix]; - } - else { - str += this.shift[deep]+ar[ix]; - } - } - return str.replace(/^\n{1,}/,''); -} + for(var key in next) { + if(key === '') { + continue; + } + _buildIriMap(next, key, idx + 1); + } + } -// ----------------------- SQL section ---------------------------------------------------- + /** + * 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; + } + } -function isSubquery(str, parenthesisLevel) { - return parenthesisLevel - (str.replace(/\(/g,'').length - str.replace(/\)/g,'').length ) + /** + * 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']; + } + return child; + } } -function split_sql(str, tab) { - - return str.replace(/\s{1,}/g," ") +/** + * 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; +} - .replace(/ AND /ig,"~::~"+tab+tab+"AND ") - .replace(/ BETWEEN /ig,"~::~"+tab+"BETWEEN ") - .replace(/ CASE /ig,"~::~"+tab+"CASE ") - .replace(/ ELSE /ig,"~::~"+tab+"ELSE ") - .replace(/ END /ig,"~::~"+tab+"END ") - .replace(/ FROM /ig,"~::~FROM ") - .replace(/ GROUP\s{1,}BY/ig,"~::~GROUP BY ") - .replace(/ HAVING /ig,"~::~HAVING ") - //.replace(/ IN /ig,"~::~"+tab+"IN ") - .replace(/ IN /ig," IN ") - .replace(/ JOIN /ig,"~::~JOIN ") - .replace(/ CROSS~::~{1,}JOIN /ig,"~::~CROSS JOIN ") - .replace(/ INNER~::~{1,}JOIN /ig,"~::~INNER JOIN ") - .replace(/ LEFT~::~{1,}JOIN /ig,"~::~LEFT JOIN ") - .replace(/ RIGHT~::~{1,}JOIN /ig,"~::~RIGHT JOIN ") - .replace(/ ON /ig,"~::~"+tab+"ON ") - .replace(/ OR /ig,"~::~"+tab+tab+"OR ") - .replace(/ ORDER\s{1,}BY/ig,"~::~ORDER BY ") - .replace(/ OVER /ig,"~::~"+tab+"OVER ") - .replace(/\(\s{0,}SELECT /ig,"~::~(SELECT ") - .replace(/\)\s{0,}SELECT /ig,")~::~SELECT ") - .replace(/ THEN /ig," THEN~::~"+tab+"") - .replace(/ UNION /ig,"~::~UNION~::~") - .replace(/ USING /ig,"~::~USING ") - .replace(/ WHEN /ig,"~::~"+tab+"WHEN ") - .replace(/ WHERE /ig,"~::~WHERE ") - .replace(/ WITH /ig,"~::~WITH ") - //.replace(/\,\s{0,}\(/ig,",~::~( ") - //.replace(/\,/ig,",~::~"+tab+tab+"") - .replace(/ ALL /ig," ALL ") - .replace(/ AS /ig," AS ") - .replace(/ ASC /ig," ASC ") - .replace(/ DESC /ig," DESC ") - .replace(/ DISTINCT /ig," DISTINCT ") - .replace(/ EXISTS /ig," EXISTS ") - .replace(/ NOT /ig," NOT ") - .replace(/ NULL /ig," NULL ") - .replace(/ LIKE /ig," LIKE ") - .replace(/\s{0,}SELECT /ig,"SELECT ") - .replace(/~::~{1,}/g,"~::~") - .split('~::~'); +/** + * 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]'); } -pp.prototype.sql = function(text) { +/** + * 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; +} - var ar_by_quote = text.replace(/\s{1,}/g," ") - .replace(/\'/ig,"~::~\'") - .split('~::~'), - len = ar_by_quote.length, - ar = [], - deep = 0, - tab = this.step,//+this.step, - inComment = true, - inQuote = false, - parenthesisLevel = 0, - str = '', - ix = 0; +/** + * 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); +} - for(ix=0;ix/g,""); - return str.replace(/>\s{0,}<"); +/** + * 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'); } -pp.prototype.jsonmin = function(text) { - - return text.replace(/\s{0,}\{\s{0,}/g,"{") - .replace(/\s{0,}\[$/g,"[") - .replace(/\[\s{0,}/g,"[") - .replace(/:\s{0,}\[/g,':[') - .replace(/\s{0,}\}\s{0,}/g,"}") - .replace(/\s{0,}\]\s{0,}/g,"]") - .replace(/\"\s{0,}\,/g,'",') - .replace(/\,\s{0,}\"/g,',"') - .replace(/\"\s{0,}:/g,'":') - .replace(/:\s{0,}\"/g,':"') - .replace(/:\s{0,}\[/g,':[') - .replace(/\,\s{0,}\[/g,',[') - .replace(/\,\s{2,}/g,', ') - .replace(/\]\s{0,},\s{0,}\[/g,'],['); +/** + * 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; } -pp.prototype.cssmin = function(text, preserveComments) { - - var str = preserveComments ? text - : text.replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+\//g,"") ; - return str.replace(/\s{1,}/g,' ') - .replace(/\{\s{1,}/g,"{") - .replace(/\}\s{1,}/g,"}") - .replace(/\;\s{1,}/g,";") - .replace(/\/\*\s{1,}/g,"/*") - .replace(/\*\/\s{1,}/g,"*/"); -} +/** + * 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)); +} -pp.prototype.sqlmin = function(text) { - return text.replace(/\s{1,}/g," ").replace(/\s{1,}\(/,"(").replace(/\s{1,}\)/,")"); +/** + * 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); } -// -------------------------------------------------------------------------------------------- +/** + * 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); +} -exports.pd= new pp; +/** + * 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))); + } + } + return rval; +} +/** + * 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; +} +/** + * 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]); + } + } 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; + } + // get @context + var ctx = input[key]; + // 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; + } + } + } + return (count < Object.keys(urls).length); + } + return false; +} +/** + * 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; + // 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); + } + // 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); + }; + // find all URLs in the given input + if(!_findContextUrls(input, urls, false, base)) { + // no new URLs in input + return finished(); + } -},{}],42:[function(_dereq_,module,exports){ -(function (process){ -'use strict'; + // queue all unretrieved URLs + var queue = []; + for(var url in urls) { + if(urls[url] === false) { + queue.push(url); + } + } -if (!process.version || - process.version.indexOf('v0.') === 0 || - process.version.indexOf('v1.') === 0 && process.version.indexOf('v1.8.') !== 0) { - module.exports = nextTick; -} else { - module.exports = process.nextTick; -} + // 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; + } -function nextTick(fn, arg1, arg2, arg3) { - if (typeof fn !== 'function') { - throw new TypeError('"callback" argument must be a function'); - } - var len = arguments.length; - var args, i; - switch (len) { - case 0: - case 1: - return process.nextTick(fn); - case 2: - return process.nextTick(function afterTickOne() { - fn.call(null, arg1); - }); - case 3: - return process.nextTick(function afterTickTwo() { - fn.call(null, arg1, arg2); - }); - case 4: - return process.nextTick(function afterTickThree() { - fn.call(null, arg1, arg2, arg3); - }); - default: - args = new Array(len - 1); - i = 0; - while (i < args.length) { - args[i++] = arguments[i]; - } - return process.nextTick(function afterTick() { - fn.apply(null, args); - }); - } -} + var ctx = remoteDoc ? remoteDoc.document : null; -}).call(this,_dereq_('_process')) + // parse string context as JSON + if(!err && _isString(ctx)) { + try { + ctx = JSON.parse(ctx); + } catch(ex) { + err = ex; + } + } -},{"_process":43}],43:[function(_dereq_,module,exports){ -// shim for using process in browser -var process = module.exports = {}; + // 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); + } -// cached from whatever global is present so that test runners that stub it -// don't break things. But we need to wrap it in a try catch in case it is -// wrapped in strict mode code which doesn't define any globals. It's inside a -// function because try/catches deoptimize in certain engines. + // use empty context if no @context key is present + if(!('@context' in ctx)) { + ctx = {'@context': {}}; + } else { + ctx = {'@context': ctx['@context']}; + } -var cachedSetTimeout; -var cachedClearTimeout; + // append context URL to context if given + if(remoteDoc.contextUrl) { + if(!_isArray(ctx['@context'])) { + ctx['@context'] = [ctx['@context']]; + } + ctx['@context'].push(remoteDoc.contextUrl); + } -function defaultSetTimout() { - throw new Error('setTimeout has not been defined'); -} -function defaultClearTimeout () { - throw new Error('clearTimeout has not been defined'); -} -(function () { - try { - if (typeof setTimeout === 'function') { - cachedSetTimeout = setTimeout; - } else { - cachedSetTimeout = defaultSetTimout; - } - } catch (e) { - cachedSetTimeout = defaultSetTimout; - } - try { - if (typeof clearTimeout === 'function') { - cachedClearTimeout = clearTimeout; - } else { - cachedClearTimeout = defaultClearTimeout; + // 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); } - } catch (e) { - cachedClearTimeout = defaultClearTimeout; - } -} ()) -function runTimeout(fun) { - if (cachedSetTimeout === setTimeout) { - //normal enviroments in sane situations - return setTimeout(fun, 0); + }(queue[i])); } - // if setTimeout wasn't available but was latter defined - if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { - cachedSetTimeout = setTimeout; - return setTimeout(fun, 0); + }; + 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'); } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedSetTimeout(fun, 0); - } catch(e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedSetTimeout.call(null, fun, 0); - } catch(e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error - return cachedSetTimeout.call(this, fun, 0); - } + var rval = []; + for(var p in o) { + if(Object.prototype.hasOwnProperty.call(o, p)) { + rval.push(p); + } } + return rval; + }; +} +/** + * 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 + '$'); -} -function runClearTimeout(marker) { - if (cachedClearTimeout === clearTimeout) { - //normal enviroments in sane situations - return clearTimeout(marker); - } - // if clearTimeout wasn't available but was latter defined - if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { - cachedClearTimeout = clearTimeout; - return clearTimeout(marker); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedClearTimeout(marker); - } catch (e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedClearTimeout.call(null, marker); - } catch (e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. - // Some versions of I.E. have different rules for clearTimeout vs setTimeout - return cachedClearTimeout.call(this, marker); - } - } + // define quad part regexes + var subject = '(?:' + iri + '|' + bnode + ')' + ws; + var property = iri + ws; + var object = '(?:' + iri + '|' + bnode + '|' + literal + ')' + wso; + var graphName = '(?:\\.|(?:(?:' + iri + '|' + bnode + ')' + wso + '\\.))'; + // full quad regex + var quad = new RegExp( + '^' + wso + subject + property + object + graphName + wso + comment + '$'); + // build RDF dataset + var dataset = {}; -} -var queue = []; -var draining = false; -var currentQueue; -var queueIndex = -1; + // 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++; -function cleanUpNextTick() { - if (!draining || !currentQueue) { - return; + // skip empty lines + if(empty.test(line)) { + continue; } - draining = false; - if (currentQueue.length) { - queue = currentQueue.concat(queue); - } else { - queueIndex = -1; + + // parse quad + var match = line.match(quad); + if(match === null) { + throw new JsonLdError( + 'Error while parsing N-Quads; invalid quad.', + 'jsonld.ParseError', {line: lineNumber}); } - if (queue.length) { - drainQueue(); + + // create RDF triple + var triple = {}; + + // get subject + if(!_isUndefined(match[1])) { + triple.subject = {type: 'IRI', value: match[1]}; + } else { + triple.subject = {type: 'blank node', value: match[2]}; } -} -function drainQueue() { - if (draining) { - return; + // get predicate + triple.predicate = {type: 'IRI', value: match[3]}; + + // 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; } - var timeout = runTimeout(cleanUpNextTick); - draining = true; - var len = queue.length; - while(len) { - currentQueue = queue; - queue = []; - while (++queueIndex < len) { - if (currentQueue) { - currentQueue[queueIndex].run(); - } - } - queueIndex = -1; - len = queue.length; + // 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]; } - currentQueue = null; - draining = false; - runClearTimeout(timeout); -} -process.nextTick = function (fun) { - var args = new Array(arguments.length - 1); - if (arguments.length > 1) { - for (var i = 1; i < arguments.length; i++) { - args[i - 1] = arguments[i]; + // 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; } + } + if(unique) { + triples.push(triple); + } } - queue.push(new Item(fun, args)); - if (queue.length === 1 && !draining) { - runTimeout(drainQueue); - } -}; + } -// v8 likes predictible objects -function Item(fun, array) { - this.fun = fun; - this.array = array; + return dataset; } -Item.prototype.run = function () { - this.fun.apply(null, this.array); -}; -process.title = 'browser'; -process.browser = true; -process.env = {}; -process.argv = []; -process.version = ''; // empty string to avoid regexp issues -process.versions = {}; -function noop() {} - -process.on = noop; -process.addListener = noop; -process.once = noop; -process.off = noop; -process.removeListener = noop; -process.removeAllListeners = noop; -process.emit = noop; -process.prependListener = noop; -process.prependOnceListener = noop; +// register the N-Quads RDF parser +jsonld.registerRDFParser('application/nquads', _parseNQuads); -process.listeners = function (name) { return [] } +/** + * 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(''); +} -process.binding = function (name) { - throw new Error('process.binding is not supported'); -}; +/** + * 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; + } -process.cwd = function () { return '/' }; -process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); -}; -process.umask = function() { return 0; }; + var quad = ''; -},{}],44:[function(_dereq_,module,exports){ -(function (global){ -/*! https://mths.be/punycode v1.4.1 by @mathias */ -;(function(root) { + // subject is an IRI + if(s.type === 'IRI') { + quad += '<' + s.value + '>'; + } else { + quad += s.value; + } + quad += ' '; - /** 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; - } + // predicate is an IRI + if(p.type === 'IRI') { + quad += '<' + p.value + '>'; + } else { + quad += p.value; + } + quad += ' '; - /** - * The `punycode` object. - * @name punycode - * @type Object - */ - var punycode, + // 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 + '>'; + } + } - /** Highest positive signed 32-bit float value */ - maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1 + // graph + if(g !== null && g !== undefined) { + if(g.indexOf('_:') !== 0) { + quad += ' <' + g + '>'; + } else { + quad += ' ' + g; + } + } - /** Bootstring parameters */ - base = 36, - tMin = 1, - tMax = 26, - skew = 38, - damp = 700, - initialBias = 72, - initialN = 128, // 0x80 - delimiter = '-', // '\x2D' + quad += ' .\n'; + return quad; +} - /** Regular expressions */ - regexPunycode = /^xn--/, - regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars - regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators - - /** 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' - }, - - /** Convenience shortcuts */ - baseMinusTMin = base - tMin, - floor = Math.floor, - stringFromCharCode = String.fromCharCode, - - /** Temporary variable */ - key; - - /*--------------------------------------------------------------------------*/ +/** + * 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'] = []; - /** - * 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]); - } + var subjects = data.getSubjects(); + for(var si = 0; si < subjects.length; ++si) { + var subject = subjects[si]; + if(subject === null) { + continue; + } - /** - * 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; - } + // 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]; - /** - * 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; - } + // create RDF triple + var triple = {}; - /** - * 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; - } + // add subject + if(subject.indexOf('_:') === 0) { + triple.subject = {type: 'blank node', value: subject}; + } else { + triple.subject = {type: 'IRI', value: subject}; + } - /** - * 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(''); - } + // add predicate + if(predicate.indexOf('_:') === 0) { + triple.predicate = {type: 'blank node', value: predicate}; + } else { + triple.predicate = {type: 'IRI', value: predicate}; + } - /** - * 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; - } + // 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; + } + } + } - /** - * 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); - } + // add object + triple.object = {}; - /** - * 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)); - } + // object is an IRI + if(object.type === RDF_OBJECT) { + if(object.value.indexOf('_:') === 0) { + triple.object.type = 'blank node'; + } else { + triple.object.type = 'IRI'; + } + } 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; + } + } + triple.object.value = value; - /** - * 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; + // add triple to dataset in default graph + dataset['@default'].push(triple); + } + } + } - // 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. + return dataset; +} - basic = input.lastIndexOf(delimiter); - if (basic < 0) { - basic = 0; - } +// register the RDFa API RDF parser +jsonld.registerRDFParser('rdfa-api', _parseRdfaApiData); - 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)); - } +/** + * 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; - // Main decoding loop: start just after the last delimiter if any basic code - // points were copied; start at the beginning otherwise. +/** + * 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; +}; - for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) { +/** + * 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]; + } - // `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) { + // get next identifier + var identifier = this.prefix + this.counter; + this.counter += 1; - if (index >= inputLength) { - error('invalid-input'); - } + // save mapping + if(old) { + this.existing[old] = identifier; + } - digit = basicToDigit(input.charCodeAt(index++)); + return identifier; +}; +// alias +IdentifierIssuer.prototype.getName = IdentifierIssuer.prototype.getName; - if (digit >= base || digit > floor((maxInt - i) / w)) { - error('overflow'); - } +/** + * 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; - i += digit * w; - t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); +/** + * 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; + } +}; - if (digit < t) { - break; - } +/** + * Returns true if there is another permutation. + * + * @return true if there is another permutation, false if not. + */ +Permutator.prototype.hasNext = function() { + return !this.done; +}; - baseMinusT = base - t; - if (w > floor(maxInt / baseMinusT)) { - error('overflow'); - } +/** + * 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(); - w *= baseMinusT; + /* Calculate the next permutation using the Steinhaus-Johnson-Trotter + permutation algorithm. */ - } + // 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; + } + } - out = output.length + 1; - bias = adapt(i - oldi, out, oldi == 0); + // 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; - // `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'); - } + // 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]]; + } + } + } - n += floor(i / out); - i %= out; + return rval; +}; - // Insert `n` at position `i` of the output - output.splice(i++, 0, n); +//////////////////////// DEFINE NORMALIZATION HASH API //////////////////////// - } +/** + * 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(); +}; - return ucs2encode(output); - } +// switch definition of NormalizeHash based on environment +(function(_nodejs) { - /** - * 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; +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; +} - // Convert the input in UCS-2 to Unicode - input = ucs2decode(input); +// 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(); +}; - // Cache the length - inputLength = input.length; +/////////////////////////// DEFINE MESSAGE DIGEST API ///////////////////////// - // Initialize the state - n = initialN; - delta = 0; - bias = initialBias; +/** + * Creates a new MessageDigest. + * + * @param algorithm the algorithm to use. + */ +var MessageDigest = function(algorithm) { + if(!(this instanceof MessageDigest)) { + return new MessageDigest(algorithm); + } - // Handle the basic code points - for (j = 0; j < inputLength; ++j) { - currentValue = input[j]; - if (currentValue < 0x80) { - output.push(stringFromCharCode(currentValue)); - } - } + this._algorithm = algorithm; - handledCPCount = basicLength = output.length; + // 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; + } + n >>>= 1; + if(n > 0) { + c += c; + } + } + } - // `handledCPCount` is the number of code points that have been handled; - // `basicLength` is the number of basic code points. + // start digest automatically for first time + this.start(); +}; - // Finish the basic string - if it is not empty - with a delimiter - if (basicLength) { - output.push(delimiter); - } +/** + * Starts the digest. + * + * @return this digest object. + */ +MessageDigest.prototype.start = function() { + // up to 56-bit message length for convenience + this.messageLength = 0; - // Main encoding loop: - while (handledCPCount < inputLength) { + // full message length + this.fullMessageLength = []; + var int32s = this._algorithm.messageLengthSize / 4; + for(var i = 0; i < int32s; ++i) { + this.fullMessageLength.push(0); + } - // 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; - } - } + // input buffer + this._input = new MessageDigest.ByteBuffer(); - // 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'); - } + // get starting state + this.state = this._algorithm.start(); - delta += (m - n) * handledCPCountPlusOne; - n = m; + return this; +}; - for (j = 0; j < inputLength; ++j) { - currentValue = input[j]; +/** + * 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))); - if (currentValue < n && ++delta > maxInt) { - error('overflow'); - } + // 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); + } - 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); - } + // add bytes to input buffer + this._input.putBytes(msg.bytes()); - output.push(stringFromCharCode(digitToBasic(q, 0))); - bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); - delta = 0; - ++handledCPCount; - } - } + // digest blocks + while(this._input.length() >= this._algorithm.blockSize) { + this.state = this._algorithm.digest(this.state, this._input); + } - ++delta; - ++n; + // compact input buffer every 2K or if empty + if(this._input.read > 2048 || this._input.length() === 0) { + this._input.compact(); + } - } - return output.join(''); - } + return this; +}; - /** - * 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; - }); - } +/** + * 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. */ - /** - * 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; - }); - } + /* 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. - /*--------------------------------------------------------------------------*/ + 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). - /** 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 - }; + 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. */ - /** 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; - } + // create final block + var finalBlock = new MessageDigest.ByteBuffer(); + finalBlock.putBytes(this._input.bytes()); -}(this)); + // compute remaining size to be digested (include message length size) + var remaining = ( + this.fullMessageLength[this.fullMessageLength.length - 1] + + this._algorithm.messageLengthSize); -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) + // 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)); -},{}],45:[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. + // 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)); + } -'use strict'; + // write the length of the message (algorithm-specific) + this._algorithm.writeMessageLength(finalBlock, messageLength); -// If obj.hasOwnProperty has been overridden, then calling -// obj.hasOwnProperty(prop) will break. -// See: https://github.com/joyent/node/issues/1707 -function hasOwnProperty(obj, prop) { - return Object.prototype.hasOwnProperty.call(obj, prop); -} + // digest final block + var state = this._algorithm.digest(this.state.copy(), finalBlock); -module.exports = function(qs, sep, eq, options) { - sep = sep || '&'; - eq = eq || '='; - var obj = {}; + // write state to buffer + var rval = new MessageDigest.ByteBuffer(); + state.write(rval); + return rval; +}; - if (typeof qs !== 'string' || qs.length === 0) { - return obj; +/** + * 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 = ''; } + this.read = 0; +}; - var regexp = /\+/g; - qs = qs.split(sep); +/** + * 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)); +}; - var maxKeys = 1000; - if (options && typeof options.maxKeys === 'number') { - maxKeys = options.maxKeys; - } +/** + * 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; +}; - var len = qs.length; - // maxKeys <= 0 means that we should not limit keys count - if (maxKeys > 0 && len > maxKeys) { - len = maxKeys; - } +/** + * Puts the given bytes into this buffer. + * + * @param bytes the bytes as a binary-encoded string. + */ +MessageDigest.ByteBuffer.prototype.putBytes = function(bytes) { + this.data += bytes; +}; - for (var i = 0; i < len; ++i) { - var x = qs[i].replace(regexp, '%20'), - idx = x.indexOf(eq), - kstr, vstr, k, v; +/** + * 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); +}; - if (idx >= 0) { - kstr = x.substr(0, idx); - vstr = x.substr(idx + 1); - } else { - kstr = x; - vstr = ''; - } +/** + * Gets the number of bytes in this buffer. + * + * @return the number of bytes in this buffer. + */ +MessageDigest.ByteBuffer.prototype.length = function() { + return this.data.length - this.read; +}; - k = decodeURIComponent(kstr); - v = decodeURIComponent(vstr); +/** + * Compacts this buffer. + */ +MessageDigest.ByteBuffer.prototype.compact = function() { + this.data = this.data.slice(this.read); + this.read = 0; +}; - if (!hasOwnProperty(obj, k)) { - obj[k] = v; - } else if (isArray(obj[k])) { - obj[k].push(v); - } else { - obj[k] = [obj[k], v]; +/** + * Converts this buffer to a hexadecimal string. + * + * @return a hexadecimal string. + */ +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); } - - return obj; + return rval; }; -var isArray = Array.isArray || function (xs) { - return Object.prototype.toString.call(xs) === '[object Array]'; +///////////////////////////// DEFINE SHA-1 ALGORITHM ////////////////////////// + +var sha1 = { + // used for word storage + _w: null }; -},{}],46:[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. - -'use strict'; - -var stringifyPrimitive = function(v) { - switch (typeof v) { - case 'string': - return v; - - case 'boolean': - return v ? 'true' : 'false'; - - case 'number': - return isFinite(v) ? v : ''; - - default: - return ''; - } +sha1.Algorithm = function() { + this.name = 'sha1', + this.blockSize = 64; + this.digestLength = 20; + this.messageLengthSize = 8; }; -module.exports = function(obj, sep, eq, name) { - sep = sep || '&'; - eq = eq || '='; - if (obj === null) { - obj = undefined; - } - - if (typeof obj === 'object') { - return map(objectKeys(obj), function(k) { - var ks = encodeURIComponent(stringifyPrimitive(k)) + eq; - if (isArray(obj[k])) { - return map(obj[k], function(v) { - return ks + encodeURIComponent(stringifyPrimitive(v)); - }).join(sep); - } else { - return ks + encodeURIComponent(stringifyPrimitive(obj[k])); - } - }).join(sep); - +sha1.Algorithm.prototype.start = function() { + if(!sha1._w) { + sha1._w = new Array(80); } - - if (!name) return ''; - return encodeURIComponent(stringifyPrimitive(name)) + eq + - encodeURIComponent(stringifyPrimitive(obj)); -}; - -var isArray = Array.isArray || function (xs) { - return Object.prototype.toString.call(xs) === '[object Array]'; + return sha1._createState(); }; -function map (xs, f) { - if (xs.map) return xs.map(f); - var res = []; - for (var i = 0; i < xs.length; i++) { - res.push(f(xs[i], i)); - } - return res; -} - -var objectKeys = Object.keys || function (obj) { - var res = []; - for (var key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) res.push(key); - } - return res; +sha1.Algorithm.prototype.writeMessageLength = function( + finalBlock, messageLength) { + // message length is in bits and in big-endian order; simply append + finalBlock.putBytes(messageLength.bytes()); }; -},{}],47:[function(_dereq_,module,exports){ -'use strict'; - -exports.decode = exports.parse = _dereq_('./decode'); -exports.encode = exports.stringify = _dereq_('./encode'); +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; -},{"./decode":45,"./encode":46}],48:[function(_dereq_,module,exports){ -'use strict'; + // 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 -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + // 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; + } + 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; + } + // 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; + } + 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; + } + // 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; + } + // 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; + } -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + // 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; -function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + len -= 64; + } -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + return s; +}; -var ClassOrder = _dereq_('./class-order'); -var Node = _dereq_('./node'); +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; +}; -var BlankNode = function (_Node) { - _inherits(BlankNode, _Node); +//////////////////////////// DEFINE SHA-256 ALGORITHM ///////////////////////// - function BlankNode(id) { - _classCallCheck(this, BlankNode); +var sha256 = { + // shared state + _k: null, + _w: null +}; - var _this = _possibleConstructorReturn(this, (BlankNode.__proto__ || Object.getPrototypeOf(BlankNode)).call(this)); +sha256.Algorithm = function() { + this.name = 'sha256', + this.blockSize = 64; + this.digestLength = 32; + this.messageLengthSize = 8; +}; - _this.termType = BlankNode.termType; - _this.id = BlankNode.nextId++; - _this.value = id || _this.id.toString(); - return _this; +sha256.Algorithm.prototype.start = function() { + if(!sha256._k) { + sha256._init(); } + return sha256._createState(); +}; - _createClass(BlankNode, [{ - key: 'compareTerm', - value: function compareTerm(other) { - if (this.classOrder < other.classOrder) { - return -1; - } - if (this.classOrder > other.classOrder) { - return +1; - } - if (this.id < other.id) { - return -1; - } - if (this.id > other.id) { - return +1; - } - return 0; - } - }, { - key: 'copy', - value: function copy(formula) { - // depends on the formula - var bnodeNew = new BlankNode(); - formula.copyTo(this, bnodeNew); - return bnodeNew; - } - }, { - key: 'toCanonical', - value: function toCanonical() { - return '_:' + this.value; +sha256.Algorithm.prototype.writeMessageLength = function( + finalBlock, messageLength) { + // message length is in bits and in big-endian order; simply append + finalBlock.putBytes(messageLength.bytes()); +}; + +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(); } - }, { - key: 'toString', - value: function toString() { - return BlankNode.NTAnonymousNodePrefix + this.id; + 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; } - }]); - return BlankNode; -}(Node); + // 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; -BlankNode.nextId = 0; -BlankNode.termType = 'BlankNode'; -BlankNode.NTAnonymousNodePrefix = '_:n'; -BlankNode.prototype.classOrder = ClassOrder['BlankNode']; -BlankNode.prototype.isBlank = 1; -BlankNode.prototype.isVar = 1; + // 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)); -module.exports = BlankNode; -},{"./class-order":49,"./node":65}],49:[function(_dereq_,module,exports){ -'use strict'; + // 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; + } -var ClassOrder = { - 'Literal': 1, - 'Collection': 3, - 'Graph': 4, - 'NamedNode': 5, - 'BlankNode': 6, - 'Variable': 7 + // 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; }; -module.exports = ClassOrder; -},{}],50:[function(_dereq_,module,exports){ -'use strict'; +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; +}; -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); +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]; -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + // used for word storage + sha256._w = new Array(64); +}; -function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } +})(_nodejs); // end definition of NormalizeHash -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } +if(!XMLSerializer) { -var BlankNode = _dereq_('./blank-node'); -var ClassOrder = _dereq_('./class-order'); -var Node = _dereq_('./node'); +var _defineXMLSerializer = function() { + XMLSerializer = _dereq_('xmldom').XMLSerializer; +}; -var Collection = function (_Node) { - _inherits(Collection, _Node); +} // end _defineXMLSerializer - function Collection(initial) { - _classCallCheck(this, Collection); +// 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]; + } + parsed.normalizedPath = _removeDotSegments(parsed.path, !!parsed.authority); + return parsed; +}; - var _this = _possibleConstructorReturn(this, (Collection.__proto__ || Object.getPrototypeOf(Collection)).call(this)); +/** + * Removes dot segments from a URL path. + * + * @param path the path to remove dot segments from. + * @param hasAuthority true if the URL has an authority, false if not. + */ +function _removeDotSegments(path, hasAuthority) { + var rval = ''; - _this.termType = Collection.termType; - _this.id = BlankNode.nextId++; - _this.elements = []; - _this.closed = false; - if (initial && initial.length > 0) { - initial.forEach(function (element) { - _this.elements.push(Node.fromValue(element)); - }); - } - return _this; + if(path.indexOf('/') === 0) { + rval = '/'; + } + + // 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()); + } + + 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'); +} + +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}); + } + }; + + // expose version + var _module = {exports: {}, filename: __dirname}; + _dereq_('pkginfo')(_module, 'version'); + jsonld.version = _module.exports.version; +} + +// end of jsonld API factory +return jsonld; +}; + +// external APIs: + +// used to generate a new jsonld API instance +var factory = function() { + return wrapper(function() { + return factory(); + }); +}; + +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; + } + + if(_browser) { + // export simple browser API + if(typeof jsonld === 'undefined') { + jsonld = jsonldjs = factory; + } else { + jsonldjs = factory; + } + } +} + +return factory; + +})(); + +}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},"/node_modules/jsonld/js") + +},{"_process":12,"crypto":54,"es6-promise":53,"http":54,"jsonld-request":54,"pkginfo":54,"request":54,"util":54,"xmldom":54}],56:[function(_dereq_,module,exports){ +var pkg = _dereq_('./src/libsbgn'); + +module.exports = pkg; + +},{"./src/libsbgn":130}],57:[function(_dereq_,module,exports){ +'use strict'; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var ClassOrder = _dereq_('./class-order'); +var Node = _dereq_('./node'); + +var BlankNode = function (_Node) { + _inherits(BlankNode, _Node); + + function BlankNode(id) { + _classCallCheck(this, BlankNode); + + var _this = _possibleConstructorReturn(this, (BlankNode.__proto__ || Object.getPrototypeOf(BlankNode)).call(this)); + + _this.termType = BlankNode.termType; + _this.id = BlankNode.nextId++; + _this.value = id || _this.id.toString(); + return _this; + } + + _createClass(BlankNode, [{ + key: 'compareTerm', + value: function compareTerm(other) { + if (this.classOrder < other.classOrder) { + return -1; + } + if (this.classOrder > other.classOrder) { + return +1; + } + if (this.id < other.id) { + return -1; + } + if (this.id > other.id) { + return +1; + } + return 0; + } + }, { + key: 'copy', + value: function copy(formula) { + // depends on the formula + var bnodeNew = new BlankNode(); + formula.copyTo(this, bnodeNew); + return bnodeNew; + } + }, { + key: 'toCanonical', + value: function toCanonical() { + return '_:' + this.value; + } + }, { + key: 'toString', + value: function toString() { + return BlankNode.NTAnonymousNodePrefix + this.id; + } + }]); + + return BlankNode; +}(Node); + +BlankNode.nextId = 0; +BlankNode.termType = 'BlankNode'; +BlankNode.NTAnonymousNodePrefix = '_:n'; +BlankNode.prototype.classOrder = ClassOrder['BlankNode']; +BlankNode.prototype.isBlank = 1; +BlankNode.prototype.isVar = 1; + +module.exports = BlankNode; +},{"./class-order":58,"./node":74}],58:[function(_dereq_,module,exports){ +'use strict'; + +var ClassOrder = { + 'Literal': 1, + 'Collection': 3, + 'Graph': 4, + 'NamedNode': 5, + 'BlankNode': 6, + 'Variable': 7 +}; + +module.exports = ClassOrder; +},{}],59:[function(_dereq_,module,exports){ +'use strict'; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var BlankNode = _dereq_('./blank-node'); +var ClassOrder = _dereq_('./class-order'); +var Node = _dereq_('./node'); + +var Collection = function (_Node) { + _inherits(Collection, _Node); + + function Collection(initial) { + _classCallCheck(this, Collection); + + var _this = _possibleConstructorReturn(this, (Collection.__proto__ || Object.getPrototypeOf(Collection)).call(this)); + + _this.termType = Collection.termType; + _this.id = BlankNode.nextId++; + _this.elements = []; + _this.closed = false; + if (initial && initial.length > 0) { + initial.forEach(function (element) { + _this.elements.push(Node.fromValue(element)); + }); + } + return _this; } _createClass(Collection, [{ @@ -21192,7 +18933,7 @@ Collection.prototype.compareTerm = BlankNode.prototype.compareTerm; Collection.prototype.isVar = 0; module.exports = Collection; -},{"./blank-node":48,"./class-order":49,"./node":65}],51:[function(_dereq_,module,exports){ +},{"./blank-node":57,"./class-order":58,"./node":74}],60:[function(_dereq_,module,exports){ 'use strict'; module.exports.convertToJson = convertToJson; @@ -21255,7 +18996,7 @@ function convertToNQuads(n3String, nquadCallback) { nquadCallback(err, nquadString); }); } -},{"async":1,"jsonld":18,"n3":82}],52:[function(_dereq_,module,exports){ +},{"async":1,"jsonld":55,"n3":91}],61:[function(_dereq_,module,exports){ 'use strict'; var _indexedFormula = _dereq_('./indexed-formula'); @@ -21335,7 +19076,7 @@ module.exports.collection = collection; module.exports.fetcher = fetcher; module.exports.lit = lit; module.exports.st = st; -},{"./blank-node":48,"./collection":50,"./default-graph":53,"./fetcher":55,"./indexed-formula":58,"./literal":60,"./named-node":63,"./statement":75,"./variable":80}],53:[function(_dereq_,module,exports){ +},{"./blank-node":57,"./collection":59,"./default-graph":62,"./fetcher":64,"./indexed-formula":67,"./literal":69,"./named-node":72,"./statement":84,"./variable":89}],62:[function(_dereq_,module,exports){ 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); @@ -21372,7 +19113,7 @@ var DefaultGraph = function (_Node) { }(Node); module.exports = DefaultGraph; -},{"./node":65}],54:[function(_dereq_,module,exports){ +},{"./node":74}],63:[function(_dereq_,module,exports){ 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); @@ -21414,7 +19155,7 @@ var Empty = function (_Node) { Empty.termType = 'empty'; module.exports = Empty; -},{"./node":65}],55:[function(_dereq_,module,exports){ +},{"./node":74}],64:[function(_dereq_,module,exports){ 'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; @@ -23024,7 +20765,7 @@ var Fetcher = function Fetcher(store, timeout, async) { }; // End of fetcher module.exports = Fetcher; -},{"./log":61,"./n3parser":62,"./named-node":63,"./namespace":64,"./parse":66,"./rdfaparser":70,"./rdfxmlparser":71,"./serialize":72,"./uri":78,"./util":79}],56:[function(_dereq_,module,exports){ +},{"./log":70,"./n3parser":71,"./named-node":72,"./namespace":73,"./parse":75,"./rdfaparser":79,"./rdfxmlparser":80,"./serialize":81,"./uri":87,"./util":88}],65:[function(_dereq_,module,exports){ 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); @@ -23650,7 +21391,7 @@ Formula.prototype.variable = function (name) { }; module.exports = Formula; -},{"./blank-node":48,"./class-order":49,"./collection":50,"./literal":60,"./log":61,"./named-node":63,"./namespace":64,"./node":65,"./serialize":72,"./statement":75,"./variable":80}],57:[function(_dereq_,module,exports){ +},{"./blank-node":57,"./class-order":58,"./collection":59,"./literal":69,"./log":70,"./named-node":72,"./namespace":73,"./node":74,"./serialize":81,"./statement":84,"./variable":89}],66:[function(_dereq_,module,exports){ 'use strict'; var _indexedFormula = _dereq_('./indexed-formula'); @@ -23713,7 +21454,7 @@ $rdf.quad = $rdf.DataFactory.quad; $rdf.triple = $rdf.DataFactory.triple; module.exports = $rdf; -},{"./blank-node":48,"./collection":50,"./convert":51,"./data-factory":52,"./empty":54,"./fetcher":55,"./formula":56,"./indexed-formula":58,"./jsonparser":59,"./literal":60,"./log":61,"./n3parser":62,"./named-node":63,"./namespace":64,"./node":65,"./parse":66,"./patch-parser":67,"./query":69,"./query-to-sparql":68,"./rdfaparser":70,"./rdfxmlparser":71,"./serialize":72,"./serializer":73,"./sparql-to-query":74,"./statement":75,"./update-manager":76,"./updates-via":77,"./uri":78,"./util":79,"./variable":80}],58:[function(_dereq_,module,exports){ +},{"./blank-node":57,"./collection":59,"./convert":60,"./data-factory":61,"./empty":63,"./fetcher":64,"./formula":65,"./indexed-formula":67,"./jsonparser":68,"./literal":69,"./log":70,"./n3parser":71,"./named-node":72,"./namespace":73,"./node":74,"./parse":75,"./patch-parser":76,"./query":78,"./query-to-sparql":77,"./rdfaparser":79,"./rdfxmlparser":80,"./serialize":81,"./serializer":82,"./sparql-to-query":83,"./statement":84,"./update-manager":85,"./updates-via":86,"./uri":87,"./util":88,"./variable":89}],67:[function(_dereq_,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { @@ -24621,7 +22362,7 @@ exports.default = IndexedFormula; IndexedFormula.handleRDFType = handleRDFType; -},{"./formula":56,"./node":65,"./query":69,"./statement":75,"./util":79,"./variable":80}],59:[function(_dereq_,module,exports){ +},{"./formula":65,"./node":74,"./query":78,"./statement":84,"./util":88,"./variable":89}],68:[function(_dereq_,module,exports){ 'use strict'; var jsonParser = function () { @@ -24679,7 +22420,7 @@ var jsonParser = function () { }(); module.exports = jsonParser; -},{}],60:[function(_dereq_,module,exports){ +},{}],69:[function(_dereq_,module,exports){ 'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; @@ -24862,7 +22603,7 @@ Literal.prototype.lang = ''; Literal.prototype.isVar = 0; module.exports = Literal; -},{"./class-order":49,"./named-node":63,"./node":65,"./xsd":81}],61:[function(_dereq_,module,exports){ +},{"./class-order":58,"./named-node":72,"./node":74,"./xsd":90}],70:[function(_dereq_,module,exports){ "use strict"; /** @@ -24889,7 +22630,7 @@ module.exports = { return; } }; -},{}],62:[function(_dereq_,module,exports){ +},{}],71:[function(_dereq_,module,exports){ 'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; @@ -26383,7 +24124,7 @@ var N3Parser = function () { }(); module.exports = N3Parser; -},{"./uri":78,"./util":79}],63:[function(_dereq_,module,exports){ +},{"./uri":87,"./util":88}],72:[function(_dereq_,module,exports){ 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); @@ -26503,7 +24244,7 @@ NamedNode.prototype.classOrder = ClassOrder['NamedNode']; NamedNode.prototype.isVar = 0; module.exports = NamedNode; -},{"./class-order":49,"./node":65}],64:[function(_dereq_,module,exports){ +},{"./class-order":58,"./node":74}],73:[function(_dereq_,module,exports){ 'use strict'; var NamedNode = _dereq_('./named-node'); @@ -26515,7 +24256,7 @@ function Namespace(nsuri) { } module.exports = Namespace; -},{"./named-node":63}],65:[function(_dereq_,module,exports){ +},{"./named-node":72}],74:[function(_dereq_,module,exports){ 'use strict'; /** * The superclass of all RDF Statement objects, that is @@ -26620,7 +24361,7 @@ Node.fromValue = function fromValue(value) { } return Literal.fromValue(value); }; -},{"./collection":50,"./literal":60,"./named-node":63}],66:[function(_dereq_,module,exports){ +},{"./collection":59,"./literal":69,"./named-node":72}],75:[function(_dereq_,module,exports){ 'use strict'; module.exports = parse; @@ -26765,7 +24506,7 @@ function parse(str, kb, base, contentType, callback) { } } } -},{"./blank-node":48,"./literal":60,"./n3parser":62,"./named-node":63,"./patch-parser":67,"./rdfaparser":70,"./rdfxmlparser":71,"./util":79,"jsonld":18,"n3":82}],67:[function(_dereq_,module,exports){ +},{"./blank-node":57,"./literal":69,"./n3parser":71,"./named-node":72,"./patch-parser":76,"./rdfaparser":79,"./rdfxmlparser":80,"./util":88,"jsonld":55,"n3":91}],76:[function(_dereq_,module,exports){ 'use strict'; // Parse a simple SPARL-Update subset syntax for patches. @@ -26861,7 +24602,7 @@ function sparqlUpdateParser(str, kb, base) { } // while // return clauses } -},{"./n3parser":62,"./namespace":64}],68:[function(_dereq_,module,exports){ +},{"./n3parser":71,"./namespace":73}],77:[function(_dereq_,module,exports){ 'use strict'; var log = _dereq_('./log'); @@ -26939,7 +24680,7 @@ function queryToSPARQL(query) { } module.exports = queryToSPARQL; -},{"./log":61}],69:[function(_dereq_,module,exports){ +},{"./log":70}],78:[function(_dereq_,module,exports){ 'use strict'; var _indexedFormula = _dereq_('./indexed-formula'); @@ -27509,7 +25250,7 @@ function indexedFormulaQuery(myQuery, callback, fetcher, onDone) { module.exports.Query = Query; module.exports.indexedFormulaQuery = indexedFormulaQuery; -},{"./indexed-formula":58,"./log":61,"./uri":78}],70:[function(_dereq_,module,exports){ +},{"./indexed-formula":67,"./log":70,"./uri":87}],79:[function(_dereq_,module,exports){ 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); @@ -28463,7 +26204,7 @@ RDFaProcessor.dateTimeTypes = [{ pattern: /-?P(?:[0-9]+Y)?(?:[0-9]+M)?(?:[0-9]+D type: 'http://www.w3.org/2001/XMLSchema#gYear' }]; module.exports = RDFaProcessor; -},{"./blank-node":48,"./data-factory":52,"./literal":60,"./named-node":63,"./uri":78,"./util":79}],71:[function(_dereq_,module,exports){ +},{"./blank-node":57,"./data-factory":61,"./literal":69,"./named-node":72,"./uri":87,"./util":88}],80:[function(_dereq_,module,exports){ 'use strict'; /** @@ -28912,7 +26653,7 @@ var RDFParser = function RDFParser(store) { }; module.exports = RDFParser; -},{"./uri":78}],72:[function(_dereq_,module,exports){ +},{"./uri":87}],81:[function(_dereq_,module,exports){ 'use strict'; module.exports = serialize; @@ -28990,7 +26731,7 @@ function serialize(target, kb, base, contentType, callback, options) { } } } -},{"./convert":51,"./serializer":73}],73:[function(_dereq_,module,exports){ +},{"./convert":60,"./serializer":82}],82:[function(_dereq_,module,exports){ 'use strict'; /* Serialization of RDF Graphs @@ -29945,7 +27686,7 @@ var Serializer = function () { }(); module.exports = Serializer; -},{"./blank-node":48,"./named-node":63,"./uri":78,"./util":79,"./xsd":81}],74:[function(_dereq_,module,exports){ +},{"./blank-node":57,"./named-node":72,"./uri":87,"./util":88,"./xsd":90}],83:[function(_dereq_,module,exports){ 'use strict'; // Converting between SPARQL queries and the $rdf query API @@ -30445,7 +28186,7 @@ function SPARQLToQuery(SPARQL, testMode, kb) { } module.exports = SPARQLToQuery; -},{"./log":61,"./query":69}],75:[function(_dereq_,module,exports){ +},{"./log":70,"./query":78}],84:[function(_dereq_,module,exports){ 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); @@ -30509,7 +28250,7 @@ var Statement = function () { }(); module.exports = Statement; -},{"./node":65}],76:[function(_dereq_,module,exports){ +},{"./node":74}],85:[function(_dereq_,module,exports){ 'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; @@ -31469,7 +29210,7 @@ var UpdateManager = function () { }(); module.exports = UpdateManager; -},{"./data-factory":52,"./fetcher":55,"./indexed-formula":58,"./namespace":64,"./serializer":73,"./uri":78,"./util":79}],77:[function(_dereq_,module,exports){ +},{"./data-factory":61,"./fetcher":64,"./indexed-formula":67,"./namespace":73,"./serializer":82,"./uri":87,"./util":88}],86:[function(_dereq_,module,exports){ 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); @@ -31655,7 +29396,7 @@ var UpdatesVia = function () { module.exports.UpdatesSocket = UpdatesSocket; module.exports.UpdatesVia = UpdatesVia; -},{"./data-factory":52}],78:[function(_dereq_,module,exports){ +},{"./data-factory":61}],87:[function(_dereq_,module,exports){ 'use strict'; /* @@ -31842,7 +29583,7 @@ function refTo(base, uri) { } return s + uri.slice(i); } -},{"./named-node":63}],79:[function(_dereq_,module,exports){ +},{"./named-node":72}],88:[function(_dereq_,module,exports){ 'use strict'; /** @@ -32324,7 +30065,7 @@ function xhr() { return false; } } -},{"./log":61,"./named-node":63,"./uri":78,"xmldom":147,"xmlhttprequest":150}],80:[function(_dereq_,module,exports){ +},{"./log":70,"./named-node":72,"./uri":87,"xmldom":302,"xmlhttprequest":305}],89:[function(_dereq_,module,exports){ 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); @@ -32402,7 +30143,7 @@ Variable.prototype.classOrder = ClassOrder['Variable']; Variable.prototype.isVar = 1; module.exports = Variable; -},{"./class-order":49,"./node":65,"./uri":78}],81:[function(_dereq_,module,exports){ +},{"./class-order":58,"./node":74,"./uri":87}],90:[function(_dereq_,module,exports){ 'use strict'; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } @@ -32422,9 +30163,35 @@ XSD.langString = new NamedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#langS XSD.string = new NamedNode('http://www.w3.org/2001/XMLSchema#string'); module.exports = XSD; -},{"./named-node":63}],82:[function(_dereq_,module,exports){ -arguments[4][31][0].apply(exports,arguments) -},{"./lib/N3Lexer":83,"./lib/N3Parser":84,"./lib/N3Store":85,"./lib/N3StreamParser":86,"./lib/N3StreamWriter":87,"./lib/N3Util":88,"./lib/N3Writer":89,"dup":31}],83:[function(_dereq_,module,exports){ +},{"./named-node":72}],91:[function(_dereq_,module,exports){ +// Replace local require by a lazy loader +var globalRequire = _dereq_; +_dereq_ = function () {}; + +// Expose submodules +var exports = module.exports = { + Lexer: _dereq_('./lib/N3Lexer'), + Parser: _dereq_('./lib/N3Parser'), + Writer: _dereq_('./lib/N3Writer'), + Store: _dereq_('./lib/N3Store'), + StreamParser: _dereq_('./lib/N3StreamParser'), + StreamWriter: _dereq_('./lib/N3StreamWriter'), + Util: _dereq_('./lib/N3Util'), +}; + +// Load submodules on first access +Object.keys(exports).forEach(function (submodule) { + Object.defineProperty(exports, submodule, { + configurable: true, + enumerable: true, + get: function () { + delete exports[submodule]; + return exports[submodule] = globalRequire('./lib/N3' + submodule); + }, + }); +}); + +},{"./lib/N3Lexer":92,"./lib/N3Parser":93,"./lib/N3Store":94,"./lib/N3StreamParser":95,"./lib/N3StreamWriter":96,"./lib/N3Util":97,"./lib/N3Writer":98}],92:[function(_dereq_,module,exports){ // **N3Lexer** tokenizes N3 documents. var fromCharCode = String.fromCharCode; var immediately = typeof setImmediate === 'function' ? setImmediate : @@ -32785,7 +30552,7 @@ N3Lexer.prototype = { // Export the `N3Lexer` class as a whole. module.exports = N3Lexer; -},{}],84:[function(_dereq_,module,exports){ +},{}],93:[function(_dereq_,module,exports){ // **N3Parser** parses N3 documents. var N3Lexer = _dereq_('./N3Lexer'); @@ -33487,7 +31254,7 @@ function noop() {} // Export the `N3Parser` class as a whole. module.exports = N3Parser; -},{"./N3Lexer":83}],85:[function(_dereq_,module,exports){ +},{"./N3Lexer":92}],94:[function(_dereq_,module,exports){ // **N3Store** objects store N3 triples by graph in memory. var expandPrefixedName = _dereq_('./N3Util').expandPrefixedName; @@ -33846,7 +31613,7 @@ N3Store.prototype = { // Export the `N3Store` class as a whole. module.exports = N3Store; -},{"./N3Util":88}],86:[function(_dereq_,module,exports){ +},{"./N3Util":97}],95:[function(_dereq_,module,exports){ // **N3StreamParser** parses an N3 stream into a triple stream var Transform = _dereq_('stream').Transform, util = _dereq_('util'), @@ -33882,7 +31649,7 @@ util.inherits(N3StreamParser, Transform); // Export the `N3StreamParser` class as a whole. module.exports = N3StreamParser; -},{"./N3Parser.js":84,"stream":107,"util":118}],87:[function(_dereq_,module,exports){ +},{"./N3Parser.js":93,"stream":36,"util":51}],96:[function(_dereq_,module,exports){ // **N3StreamWriter** serializes a triple stream into an N3 stream var Transform = _dereq_('stream').Transform, util = _dereq_('util'), @@ -33914,7 +31681,7 @@ util.inherits(N3StreamWriter, Transform); // Export the `N3StreamWriter` class as a whole. module.exports = N3StreamWriter; -},{"./N3Writer.js":89,"stream":107,"util":118}],88:[function(_dereq_,module,exports){ +},{"./N3Writer.js":98,"stream":36,"util":51}],97:[function(_dereq_,module,exports){ // **N3Util** provides N3 utility functions var Xsd = 'http://www.w3.org/2001/XMLSchema#'; @@ -34032,7 +31799,7 @@ function applyToThis(f) { // Expose N3Util, attaching all functions to it module.exports = addN3Util(addN3Util); -},{}],89:[function(_dereq_,module,exports){ +},{}],98:[function(_dereq_,module,exports){ // **N3Writer** writes N3 documents. // Matches a literal as represented in memory by the N3 library @@ -34362,9118 +32129,17640 @@ function characterReplacer(character) { // Export the `N3Writer` class as a whole. module.exports = N3Writer; -},{}],90:[function(_dereq_,module,exports){ -module.exports = _dereq_('./lib/_stream_duplex.js'); - -},{"./lib/_stream_duplex.js":91}],91:[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. - -// 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. +},{}],99:[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); + } else { + return str; + } + }; -'use strict'; +}).call(this); -/**/ +},{}],100:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + "use strict"; + var builder, defaults, escapeCDATA, requiresCDATA, wrapCDATA, + hasProp = {}.hasOwnProperty; -var processNextTick = _dereq_('process-nextick-args'); -/**/ + builder = _dereq_('xmlbuilder'); -/**/ -var objectKeys = Object.keys || function (obj) { - var keys = []; - for (var key in obj) { - keys.push(key); - }return keys; -}; -/**/ + defaults = _dereq_('./defaults').defaults; -module.exports = Duplex; + requiresCDATA = function(entry) { + return typeof entry === "string" && (entry.indexOf('&') >= 0 || entry.indexOf('>') >= 0 || entry.indexOf('<') >= 0); + }; -/**/ -var util = _dereq_('core-util-is'); -util.inherits = _dereq_('inherits'); -/**/ + wrapCDATA = function(entry) { + return ""; + }; -var Readable = _dereq_('./_stream_readable'); -var Writable = _dereq_('./_stream_writable'); + escapeCDATA = function(entry) { + return entry.replace(']]>', ']]]]>'); + }; -util.inherits(Duplex, Readable); + 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; + } + } -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]; -} + 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; + } + 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); + }; -function Duplex(options) { - if (!(this instanceof Duplex)) return new Duplex(options); + return Builder; - Readable.call(this, options); - Writable.call(this, options); + })(); - if (options && options.readable === false) this.readable = false; +}).call(this); - if (options && options.writable === false) this.writable = false; +},{"./defaults":101,"xmlbuilder":126}],101:[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 + } + }; - this.allowHalfOpen = true; - if (options && options.allowHalfOpen === false) this.allowHalfOpen = false; +}).call(this); - this.once('end', onend); -} +},{}],102:[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; -// 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; + sax = _dereq_('sax'); - // no more data can be written. - // But allow more writes to happen in this tick. - processNextTick(onEndNT, this); -} + events = _dereq_('events'); -function onEndNT(self) { - self.end(); -} + bom = _dereq_('./bom'); -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; - } + processors = _dereq_('./processors'); - // backward compatibility, the user is explicitly - // managing destroyed - this._readableState.destroyed = value; - this._writableState.destroyed = value; - } -}); + setImmediate = _dereq_('timers').setImmediate; -Duplex.prototype._destroy = function (err, cb) { - this.push(null); - this.end(); + defaults = _dereq_('./defaults').defaults; - processNextTick(cb, err); -}; + isEmpty = function(thing) { + return typeof thing === "object" && (thing != null) && Object.keys(thing).length === 0; + }; -function forEach(xs, f) { - for (var i = 0, l = xs.length; i < l; i++) { - f(xs[i], i); - } -} -},{"./_stream_readable":93,"./_stream_writable":95,"core-util-is":8,"inherits":15,"process-nextick-args":42}],92:[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. + 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); + } + return item; + }; -// a passthrough stream. -// basically just the most minimal sort of Transform stream. -// Every written chunk gets output as-is. + exports.Parser = (function(superClass) { + extend(Parser, superClass); -'use strict'; + 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); + } + 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(); + } -module.exports = PassThrough; + 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); + } + } + }; -var Transform = _dereq_('./_stream_transform'); + 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]]; + } + return obj[key].push(newValue); + } + }; -/**/ -var util = _dereq_('core-util-is'); -util.inherits = _dereq_('inherits'); -/**/ + 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 + }); + 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); + }; -util.inherits(PassThrough, Transform); + 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; + } + } + }; -function PassThrough(options) { - if (!(this instanceof PassThrough)) return new PassThrough(options); + return Parser; - Transform.call(this, options); -} + })(events.EventEmitter); -PassThrough.prototype._transform = function (chunk, encoding, cb) { - cb(null, chunk); -}; -},{"./_stream_transform":94,"core-util-is":8,"inherits":15}],93:[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. + 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; + } + } else { + if (typeof a === 'function') { + cb = a; + } + options = {}; + } + parser = new exports.Parser(options); + return parser.parseString(str, cb); + }; -'use strict'; +}).call(this); -/**/ +},{"./bom":99,"./defaults":101,"./processors":103,"events":8,"sax":281,"timers":47}],103:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + "use strict"; + var prefixMatch; -var processNextTick = _dereq_('process-nextick-args'); -/**/ + prefixMatch = new RegExp(/(?!xmlns)^.*:/); -module.exports = Readable; + exports.normalize = function(str) { + return str.toLowerCase(); + }; -/**/ -var isArray = _dereq_('isarray'); -/**/ + exports.firstCharLowerCase = function(str) { + return str.charAt(0).toLowerCase() + str.slice(1); + }; -/**/ -var Duplex; -/**/ + exports.stripPrefix = function(str) { + return str.replace(prefixMatch, ''); + }; -Readable.ReadableState = ReadableState; + exports.parseNumbers = function(str) { + if (!isNaN(str)) { + str = str % 1 === 0 ? parseInt(str, 10) : parseFloat(str); + } + return str; + }; -/**/ -var EE = _dereq_('events').EventEmitter; + exports.parseBooleans = function(str) { + if (/^(?:true|false)$/i.test(str)) { + str = str.toLowerCase() === 'true'; + } + return str; + }; -var EElistenerCount = function (emitter, type) { - return emitter.listeners(type).length; -}; -/**/ +}).call(this); -/**/ -var Stream = _dereq_('./internal/streams/stream'); -/**/ +},{}],104:[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; -// TODO(bmeurer): Change this back to const once hole checks are -// properly optimized away early in Ignition+TurboFan. -/**/ -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; -} -/**/ + defaults = _dereq_('./defaults'); -/**/ -var util = _dereq_('core-util-is'); -util.inherits = _dereq_('inherits'); -/**/ + builder = _dereq_('./builder'); -/**/ -var debugUtil = _dereq_('util'); -var debug = void 0; -if (debugUtil && debugUtil.debuglog) { - debug = debugUtil.debuglog('stream'); -} else { - debug = function () {}; -} -/**/ + parser = _dereq_('./parser'); -var BufferList = _dereq_('./internal/streams/BufferList'); -var destroyImpl = _dereq_('./internal/streams/destroy'); -var StringDecoder; + processors = _dereq_('./processors'); -util.inherits(Readable, Stream); + exports.defaults = defaults.defaults; -var kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume']; + exports.processors = processors; -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); - } else { - // 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]]; - } -} + exports.ValidationError = (function(superClass) { + extend(ValidationError, superClass); -function ReadableState(options, stream) { - Duplex = Duplex || _dereq_('./_stream_duplex'); + function ValidationError(message) { + this.message = message; + } - options = options || {}; + return ValidationError; - // 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; + })(Error); - if (stream instanceof Duplex) this.objectMode = this.objectMode || !!options.readableObjectMode; + exports.Builder = builder.Builder; - // 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 defaultHwm = this.objectMode ? 16 : 16 * 1024; - this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm; + exports.Parser = parser.Parser; - // cast to ints. - this.highWaterMark = Math.floor(this.highWaterMark); + exports.parseString = parser.parseString; - // 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; +}).call(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; +},{"./builder":100,"./defaults":101,"./parser":102,"./processors":103}],105:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var assign, isArray, isEmpty, isFunction, isObject, isPlainObject, + slice = [].slice, + hasProp = {}.hasOwnProperty; - // 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; + assign = function() { + var i, key, len, source, sources, target; + target = arguments[0], sources = 2 <= arguments.length ? slice.call(arguments, 1) : []; + if (isFunction(Object.assign)) { + Object.assign.apply(null, arguments); + } else { + for (i = 0, len = sources.length; i < len; i++) { + source = sources[i]; + if (source != null) { + for (key in source) { + if (!hasProp.call(source, key)) continue; + target[key] = source[key]; + } + } + } + } + return target; + }; - // has it been destroyed - this.destroyed = false; + isFunction = function(val) { + return !!val && Object.prototype.toString.call(val) === '[object Function]'; + }; - // 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'; + isObject = function(val) { + var ref; + return !!val && ((ref = typeof val) === 'function' || ref === 'object'); + }; - // the number of writers that are awaiting a drain event in .pipe()s - this.awaitDrain = 0; + isArray = function(val) { + if (isFunction(Array.isArray)) { + return Array.isArray(val); + } else { + return Object.prototype.toString.call(val) === '[object Array]'; + } + }; - // if true, a maybeReadMore has been scheduled - this.readingMore = false; + isEmpty = function(val) { + var key; + if (isArray(val)) { + return !val.length; + } else { + for (key in val) { + if (!hasProp.call(val, key)) continue; + return false; + } + return true; + } + }; - 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; - } -} + isPlainObject = function(val) { + var ctor, proto; + return isObject(val) && (proto = Object.getPrototypeOf(val)) && (ctor = proto.constructor) && (typeof ctor === 'function') && (ctor instanceof ctor) && (Function.prototype.toString.call(ctor) === Function.prototype.toString.call(Object)); + }; -function Readable(options) { - Duplex = Duplex || _dereq_('./_stream_duplex'); + module.exports.assign = assign; - if (!(this instanceof Readable)) return new Readable(options); + module.exports.isFunction = isFunction; - this._readableState = new ReadableState(options, this); + module.exports.isObject = isObject; - // legacy - this.readable = true; + module.exports.isArray = isArray; - if (options) { - if (typeof options.read === 'function') this._read = options.read; + module.exports.isEmpty = isEmpty; - if (typeof options.destroy === 'function') this._destroy = options.destroy; - } + module.exports.isPlainObject = isPlainObject; - Stream.call(this); -} +}).call(this); -Object.defineProperty(Readable.prototype, 'destroyed', { - get: function () { - if (this._readableState === undefined) { - return false; - } - return this._readableState.destroyed; - }, - set: function (value) { - // we ignore the value if the stream - // has not been initialized yet - if (!this._readableState) { - return; +},{}],106:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var XMLAttribute; + + module.exports = XMLAttribute = (function() { + function XMLAttribute(parent, name, value) { + this.options = parent.options; + this.stringify = parent.stringify; + if (name == null) { + throw new Error("Missing attribute name of element " + parent.name); + } + if (value == null) { + throw new Error("Missing attribute value for attribute " + name + " of element " + parent.name); + } + this.name = this.stringify.attName(name); + this.value = this.stringify.attValue(value); } - // backward compatibility, the user is explicitly - // managing destroyed - this._readableState.destroyed = value; - } -}); + XMLAttribute.prototype.clone = function() { + return Object.create(this); + }; -Readable.prototype.destroy = destroyImpl.destroy; -Readable.prototype._undestroy = destroyImpl.undestroy; -Readable.prototype._destroy = function (err, cb) { - this.push(null); - cb(err); -}; + XMLAttribute.prototype.toString = function(options) { + return this.options.writer.set(options).attribute(this); + }; -// 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; + return XMLAttribute; - if (!state.objectMode) { - if (typeof chunk === 'string') { - encoding = encoding || state.defaultEncoding; - if (encoding !== state.encoding) { - chunk = Buffer.from(chunk, encoding); - encoding = ''; - } - skipChunkCheck = true; - } - } else { - skipChunkCheck = true; - } + })(); - return readableAddChunk(this, chunk, encoding, false, skipChunkCheck); -}; +}).call(this); -// Unshift should *always* be something directly out of read() -Readable.prototype.unshift = function (chunk) { - return readableAddChunk(this, chunk, null, true, false); -}; +},{}],107:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var XMLCData, XMLNode, + 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; -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); - } + XMLNode = _dereq_('./XMLNode'); - 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); - } + module.exports = XMLCData = (function(superClass) { + extend(XMLCData, superClass); + + function XMLCData(parent, text) { + XMLCData.__super__.constructor.call(this, parent); + if (text == null) { + throw new Error("Missing CDATA text"); } - } else if (!addToFront) { - state.reading = false; + this.text = this.stringify.cdata(text); } - } - return needMoreData(state); -} + XMLCData.prototype.clone = function() { + return Object.create(this); + }; -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); + XMLCData.prototype.toString = function(options) { + return this.options.writer.set(options).cdata(this); + }; - if (state.needReadable) emitReadable(stream); - } - maybeReadMore(stream, state); -} + return XMLCData; -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; -} + })(XMLNode); -// 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); -} +}).call(this); -Readable.prototype.isPaused = function () { - return this._readableState.flowing === false; -}; +},{"./XMLNode":118}],108:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var XMLComment, XMLNode, + 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; -// 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; -}; + XMLNode = _dereq_('./XMLNode'); -// 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; -} + module.exports = XMLComment = (function(superClass) { + extend(XMLComment, superClass); -// 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; -} + function XMLComment(parent, text) { + XMLComment.__super__.constructor.call(this, parent); + if (text == null) { + throw new Error("Missing comment text"); + } + this.text = this.stringify.comment(text); + } -// 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; + XMLComment.prototype.clone = function() { + return Object.create(this); + }; - if (n !== 0) state.emittedReadable = false; + XMLComment.prototype.toString = function(options) { + return this.options.writer.set(options).comment(this); + }; - // 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; - } + return XMLComment; - n = howMuchToRead(n, state); + })(XMLNode); - // 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; - } +}).call(this); - // 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. +},{"./XMLNode":118}],109:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var XMLDTDAttList, XMLNode, + 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; - // if we need a readable event, then we need to do some reading. - var doRead = state.needReadable; - debug('need readable', doRead); + XMLNode = _dereq_('./XMLNode'); - // 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); - } + module.exports = XMLDTDAttList = (function(superClass) { + extend(XMLDTDAttList, superClass); - // 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); - } + function XMLDTDAttList(parent, elementName, attributeName, attributeType, defaultValueType, defaultValue) { + XMLDTDAttList.__super__.constructor.call(this, parent); + if (elementName == null) { + throw new Error("Missing DTD element name"); + } + if (attributeName == null) { + throw new Error("Missing DTD attribute name"); + } + if (!attributeType) { + throw new Error("Missing DTD attribute type"); + } + if (!defaultValueType) { + throw new Error("Missing DTD attribute default"); + } + if (defaultValueType.indexOf('#') !== 0) { + defaultValueType = '#' + defaultValueType; + } + if (!defaultValueType.match(/^(#REQUIRED|#IMPLIED|#FIXED|#DEFAULT)$/)) { + throw new Error("Invalid default value type; expected: #REQUIRED, #IMPLIED, #FIXED or #DEFAULT"); + } + if (defaultValue && !defaultValueType.match(/^(#FIXED|#DEFAULT)$/)) { + throw new Error("Default value only applies to #FIXED or #DEFAULT"); + } + this.elementName = this.stringify.eleName(elementName); + this.attributeName = this.stringify.attName(attributeName); + this.attributeType = this.stringify.dtdAttType(attributeType); + this.defaultValue = this.stringify.dtdAttDefault(defaultValue); + this.defaultValueType = defaultValueType; + } - var ret; - if (n > 0) ret = fromList(n, state);else ret = null; + XMLDTDAttList.prototype.toString = function(options) { + return this.options.writer.set(options).dtdAttList(this); + }; - if (ret === null) { - state.needReadable = true; - n = 0; - } else { - state.length -= n; - } + return XMLDTDAttList; - 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; - - // If we tried to read() past the EOF, then emit end on the next tick. - if (nOrig !== n && state.ended) endReadable(this); - } + })(XMLNode); - if (ret !== null) this.emit('data', ret); +}).call(this); - return ret; -}; +},{"./XMLNode":118}],110:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var XMLDTDElement, XMLNode, + 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; -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; + XMLNode = _dereq_('./XMLNode'); - // emit 'readable' now to make sure it gets picked up. - emitReadable(stream); -} + module.exports = XMLDTDElement = (function(superClass) { + extend(XMLDTDElement, superClass); -// 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) processNextTick(emitReadable_, stream);else emitReadable_(stream); - } -} + function XMLDTDElement(parent, name, value) { + XMLDTDElement.__super__.constructor.call(this, parent); + if (name == null) { + throw new Error("Missing DTD element name"); + } + if (!value) { + value = '(#PCDATA)'; + } + if (Array.isArray(value)) { + value = '(' + value.join(',') + ')'; + } + this.name = this.stringify.eleName(name); + this.value = this.stringify.dtdElementValue(value); + } -function emitReadable_(stream) { - debug('emit readable'); - stream.emit('readable'); - flow(stream); -} + XMLDTDElement.prototype.toString = function(options) { + return this.options.writer.set(options).dtdElement(this); + }; -// 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; - processNextTick(maybeReadMore_, stream, state); - } -} + return XMLDTDElement; -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; -} + })(XMLNode); -// 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')); -}; +}).call(this); -Readable.prototype.pipe = function (dest, pipeOpts) { - var src = this; - var state = this._readableState; +},{"./XMLNode":118}],111:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var XMLDTDEntity, XMLNode, isObject, + 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; - 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); + isObject = _dereq_('./Utility').isObject; - var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr; + XMLNode = _dereq_('./XMLNode'); - var endFn = doEnd ? onend : unpipe; - if (state.endEmitted) processNextTick(endFn);else src.once('end', endFn); + module.exports = XMLDTDEntity = (function(superClass) { + extend(XMLDTDEntity, superClass); - dest.on('unpipe', onunpipe); - function onunpipe(readable, unpipeInfo) { - debug('onunpipe'); - if (readable === src) { - if (unpipeInfo && unpipeInfo.hasUnpiped === false) { - unpipeInfo.hasUnpiped = true; - cleanup(); + function XMLDTDEntity(parent, pe, name, value) { + XMLDTDEntity.__super__.constructor.call(this, parent); + if (name == null) { + throw new Error("Missing entity name"); + } + if (value == null) { + throw new Error("Missing entity value"); + } + this.pe = !!pe; + this.name = this.stringify.eleName(name); + if (!isObject(value)) { + this.value = this.stringify.dtdEntityValue(value); + } else { + if (!value.pubID && !value.sysID) { + throw new Error("Public and/or system identifiers are required for an external entity"); + } + if (value.pubID && !value.sysID) { + throw new Error("System identifier is required for a public external entity"); + } + if (value.pubID != null) { + this.pubID = this.stringify.dtdPubID(value.pubID); + } + if (value.sysID != null) { + this.sysID = this.stringify.dtdSysID(value.sysID); + } + if (value.nData != null) { + this.nData = this.stringify.dtdNData(value.nData); + } + if (this.pe && this.nData) { + throw new Error("Notation declaration is not allowed in a parameter entity"); + } } } - } - function onend() { - debug('onend'); - dest.end(); - } + XMLDTDEntity.prototype.toString = function(options) { + return this.options.writer.set(options).dtdEntity(this); + }; - // 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); + return XMLDTDEntity; - 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); + })(XMLNode); - cleanedUp = true; +}).call(this); - // 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(); - } +},{"./Utility":105,"./XMLNode":118}],112:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var XMLDTDNotation, XMLNode, + 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; - // 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; + XMLNode = _dereq_('./XMLNode'); + + module.exports = XMLDTDNotation = (function(superClass) { + extend(XMLDTDNotation, superClass); + + function XMLDTDNotation(parent, name, value) { + XMLDTDNotation.__super__.constructor.call(this, parent); + if (name == null) { + throw new Error("Missing notation name"); + } + if (!value.pubID && !value.sysID) { + throw new Error("Public or system identifiers are required for an external entity"); + } + this.name = this.stringify.eleName(name); + if (value.pubID != null) { + this.pubID = this.stringify.dtdPubID(value.pubID); + } + if (value.sysID != null) { + this.sysID = this.stringify.dtdSysID(value.sysID); } - src.pause(); } - } - // 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); - } + XMLDTDNotation.prototype.toString = function(options) { + return this.options.writer.set(options).dtdNotation(this); + }; - // Make sure our error handler is attached before userland ones. - prependListener(dest, 'error', onerror); + return XMLDTDNotation; - // Both close and finish should trigger unpipe, but only once. - function onclose() { - dest.removeListener('finish', onfinish); - unpipe(); - } - dest.once('close', onclose); - function onfinish() { - debug('onfinish'); - dest.removeListener('close', onclose); - unpipe(); - } - dest.once('finish', onfinish); + })(XMLNode); - function unpipe() { - debug('unpipe'); - src.unpipe(dest); - } +}).call(this); - // tell the dest that it's being piped to - dest.emit('pipe', src); +},{"./XMLNode":118}],113:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var XMLDeclaration, XMLNode, isObject, + 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; - // start the flow if it hasn't been started already. - if (!state.flowing) { - debug('pipe resume'); - src.resume(); - } + isObject = _dereq_('./Utility').isObject; - return dest; -}; + XMLNode = _dereq_('./XMLNode'); -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); - } - }; -} + module.exports = XMLDeclaration = (function(superClass) { + extend(XMLDeclaration, superClass); -Readable.prototype.unpipe = function (dest) { - var state = this._readableState; - var unpipeInfo = { hasUnpiped: false }; + function XMLDeclaration(parent, version, encoding, standalone) { + var ref; + XMLDeclaration.__super__.constructor.call(this, parent); + if (isObject(version)) { + ref = version, version = ref.version, encoding = ref.encoding, standalone = ref.standalone; + } + if (!version) { + version = '1.0'; + } + this.version = this.stringify.xmlVersion(version); + if (encoding != null) { + this.encoding = this.stringify.xmlEncoding(encoding); + } + if (standalone != null) { + this.standalone = this.stringify.xmlStandalone(standalone); + } + } - // if we're not piping anywhere, then do nothing. - if (state.pipesCount === 0) return this; + XMLDeclaration.prototype.toString = function(options) { + return this.options.writer.set(options).declaration(this); + }; - // 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; + return XMLDeclaration; - if (!dest) dest = state.pipes; + })(XMLNode); - // got a match. - state.pipes = null; - state.pipesCount = 0; - state.flowing = false; - if (dest) dest.emit('unpipe', this, unpipeInfo); - return this; - } +}).call(this); - // slow case. multiple pipe destinations. +},{"./Utility":105,"./XMLNode":118}],114:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var XMLDTDAttList, XMLDTDElement, XMLDTDEntity, XMLDTDNotation, XMLDocType, XMLNode, isObject, + 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; - if (!dest) { - // remove all. - var dests = state.pipes; - var len = state.pipesCount; - state.pipes = null; - state.pipesCount = 0; - state.flowing = false; + isObject = _dereq_('./Utility').isObject; - for (var i = 0; i < len; i++) { - dests[i].emit('unpipe', this, unpipeInfo); - }return this; - } + XMLNode = _dereq_('./XMLNode'); - // try to find the right one. - var index = indexOf(state.pipes, dest); - if (index === -1) return this; + XMLDTDAttList = _dereq_('./XMLDTDAttList'); - state.pipes.splice(index, 1); - state.pipesCount -= 1; - if (state.pipesCount === 1) state.pipes = state.pipes[0]; + XMLDTDEntity = _dereq_('./XMLDTDEntity'); - dest.emit('unpipe', this, unpipeInfo); + XMLDTDElement = _dereq_('./XMLDTDElement'); - return this; -}; + XMLDTDNotation = _dereq_('./XMLDTDNotation'); -// 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); + module.exports = XMLDocType = (function(superClass) { + extend(XMLDocType, superClass); - 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) { - processNextTick(nReadingNextTick, this); - } else if (state.length) { - emitReadable(this); + function XMLDocType(parent, pubID, sysID) { + var ref, ref1; + XMLDocType.__super__.constructor.call(this, parent); + this.documentObject = parent; + if (isObject(pubID)) { + ref = pubID, pubID = ref.pubID, sysID = ref.sysID; + } + if (sysID == null) { + ref1 = [pubID, sysID], sysID = ref1[0], pubID = ref1[1]; + } + if (pubID != null) { + this.pubID = this.stringify.dtdPubID(pubID); + } + if (sysID != null) { + this.sysID = this.stringify.dtdSysID(sysID); } } - } - return res; -}; -Readable.prototype.addListener = Readable.prototype.on; - -function nReadingNextTick(self) { - debug('readable nexttick read 0'); - self.read(0); -} + XMLDocType.prototype.element = function(name, value) { + var child; + child = new XMLDTDElement(this, name, value); + this.children.push(child); + return this; + }; -// 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; -}; + XMLDocType.prototype.attList = function(elementName, attributeName, attributeType, defaultValueType, defaultValue) { + var child; + child = new XMLDTDAttList(this, elementName, attributeName, attributeType, defaultValueType, defaultValue); + this.children.push(child); + return this; + }; -function resume(stream, state) { - if (!state.resumeScheduled) { - state.resumeScheduled = true; - processNextTick(resume_, stream, state); - } -} + XMLDocType.prototype.entity = function(name, value) { + var child; + child = new XMLDTDEntity(this, false, name, value); + this.children.push(child); + return this; + }; -function resume_(stream, state) { - if (!state.reading) { - debug('resume read 0'); - stream.read(0); - } + XMLDocType.prototype.pEntity = function(name, value) { + var child; + child = new XMLDTDEntity(this, true, name, value); + this.children.push(child); + return this; + }; - state.resumeScheduled = false; - state.awaitDrain = 0; - stream.emit('resume'); - flow(stream); - if (state.flowing && !state.reading) stream.read(0); -} + XMLDocType.prototype.notation = function(name, value) { + var child; + child = new XMLDTDNotation(this, name, value); + this.children.push(child); + return this; + }; -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; -}; + XMLDocType.prototype.toString = function(options) { + return this.options.writer.set(options).docType(this); + }; -function flow(stream) { - var state = stream._readableState; - debug('flow', state.flowing); - while (state.flowing && stream.read() !== null) {} -} + XMLDocType.prototype.ele = function(name, value) { + return this.element(name, value); + }; -// 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 state = this._readableState; - var paused = false; + XMLDocType.prototype.att = function(elementName, attributeName, attributeType, defaultValueType, defaultValue) { + return this.attList(elementName, attributeName, attributeType, defaultValueType, defaultValue); + }; - var self = this; - stream.on('end', function () { - debug('wrapped end'); - if (state.decoder && !state.ended) { - var chunk = state.decoder.end(); - if (chunk && chunk.length) self.push(chunk); - } - - self.push(null); - }); + XMLDocType.prototype.ent = function(name, value) { + return this.entity(name, value); + }; - stream.on('data', function (chunk) { - debug('wrapped data'); - if (state.decoder) chunk = state.decoder.write(chunk); + XMLDocType.prototype.pent = function(name, value) { + return this.pEntity(name, value); + }; - // don't skip over falsy values in objectMode - if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return; + XMLDocType.prototype.not = function(name, value) { + return this.notation(name, value); + }; - var ret = self.push(chunk); - if (!ret) { - paused = true; - stream.pause(); - } - }); + XMLDocType.prototype.up = function() { + return this.root() || this.documentObject; + }; - // 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); - } - } + return XMLDocType; - // proxy certain important events. - for (var n = 0; n < kProxyEvents.length; n++) { - stream.on(kProxyEvents[n], self.emit.bind(self, kProxyEvents[n])); - } + })(XMLNode); - // when we try to consume some more bytes, simply unpause the - // underlying stream. - self._read = function (n) { - debug('wrapped _read', n); - if (paused) { - paused = false; - stream.resume(); - } - }; +}).call(this); - return self; -}; +},{"./Utility":105,"./XMLDTDAttList":109,"./XMLDTDElement":110,"./XMLDTDEntity":111,"./XMLDTDNotation":112,"./XMLNode":118}],115:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var XMLDocument, XMLNode, XMLStringWriter, XMLStringifier, isPlainObject, + 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; -// exposed for testing purposes only. -Readable._fromList = fromList; + isPlainObject = _dereq_('./Utility').isPlainObject; -// 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; + XMLNode = _dereq_('./XMLNode'); - 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); - } + XMLStringifier = _dereq_('./XMLStringifier'); - return ret; -} + XMLStringWriter = _dereq_('./XMLStringWriter'); -// 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; -} + module.exports = XMLDocument = (function(superClass) { + extend(XMLDocument, superClass); -// 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); + function XMLDocument(options) { + XMLDocument.__super__.constructor.call(this, null); + options || (options = {}); + if (!options.writer) { + options.writer = new XMLStringWriter(); } - break; + this.options = options; + this.stringify = new XMLStringifier(options); + this.isDocument = true; } - ++c; - } - list.length -= c; - return ret; -} -// 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); + XMLDocument.prototype.end = function(writer) { + var writerOptions; + if (!writer) { + writer = this.options.writer; + } else if (isPlainObject(writer)) { + writerOptions = writer; + writer = this.options.writer.set(writerOptions); } - break; - } - ++c; - } - list.length -= c; - return ret; -} - -function endReadable(stream) { - var state = stream._readableState; + return writer.document(this); + }; - // 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'); + XMLDocument.prototype.toString = function(options) { + return this.options.writer.set(options).document(this); + }; - if (!state.endEmitted) { - state.ended = true; - processNextTick(endReadableNT, state, stream); - } -} + return XMLDocument; -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'); - } -} + })(XMLNode); -function forEach(xs, f) { - for (var i = 0, l = xs.length; i < l; i++) { - f(xs[i], i); - } -} +}).call(this); -function indexOf(xs, x) { - for (var i = 0, l = xs.length; i < l; i++) { - if (xs[i] === x) return i; - } - return -1; -} -}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./Utility":105,"./XMLNode":118,"./XMLStringWriter":122,"./XMLStringifier":123}],116:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var XMLAttribute, XMLCData, XMLComment, XMLDTDAttList, XMLDTDElement, XMLDTDEntity, XMLDTDNotation, XMLDeclaration, XMLDocType, XMLDocumentCB, XMLElement, XMLProcessingInstruction, XMLRaw, XMLStringWriter, XMLStringifier, XMLText, isFunction, isObject, isPlainObject, ref, + hasProp = {}.hasOwnProperty; -},{"./_stream_duplex":91,"./internal/streams/BufferList":96,"./internal/streams/destroy":97,"./internal/streams/stream":98,"_process":43,"core-util-is":8,"events":10,"inherits":15,"isarray":99,"process-nextick-args":42,"safe-buffer":105,"string_decoder/":100,"util":3}],94:[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. + ref = _dereq_('./Utility'), isObject = ref.isObject, isFunction = ref.isFunction, isPlainObject = ref.isPlainObject; -// 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. + XMLElement = _dereq_('./XMLElement'); -'use strict'; + XMLCData = _dereq_('./XMLCData'); -module.exports = Transform; + XMLComment = _dereq_('./XMLComment'); -var Duplex = _dereq_('./_stream_duplex'); + XMLRaw = _dereq_('./XMLRaw'); -/**/ -var util = _dereq_('core-util-is'); -util.inherits = _dereq_('inherits'); -/**/ + XMLText = _dereq_('./XMLText'); -util.inherits(Transform, Duplex); + XMLProcessingInstruction = _dereq_('./XMLProcessingInstruction'); -function TransformState(stream) { - this.afterTransform = function (er, data) { - return afterTransform(stream, er, data); - }; + XMLDeclaration = _dereq_('./XMLDeclaration'); - this.needTransform = false; - this.transforming = false; - this.writecb = null; - this.writechunk = null; - this.writeencoding = null; -} + XMLDocType = _dereq_('./XMLDocType'); -function afterTransform(stream, er, data) { - var ts = stream._transformState; - ts.transforming = false; + XMLDTDAttList = _dereq_('./XMLDTDAttList'); - var cb = ts.writecb; + XMLDTDEntity = _dereq_('./XMLDTDEntity'); - if (!cb) { - return stream.emit('error', new Error('write callback called multiple times')); - } + XMLDTDElement = _dereq_('./XMLDTDElement'); - ts.writechunk = null; - ts.writecb = null; + XMLDTDNotation = _dereq_('./XMLDTDNotation'); - if (data !== null && data !== undefined) stream.push(data); + XMLAttribute = _dereq_('./XMLAttribute'); - cb(er); + XMLStringifier = _dereq_('./XMLStringifier'); - var rs = stream._readableState; - rs.reading = false; - if (rs.needReadable || rs.length < rs.highWaterMark) { - stream._read(rs.highWaterMark); - } -} + XMLStringWriter = _dereq_('./XMLStringWriter'); -function Transform(options) { - if (!(this instanceof Transform)) return new Transform(options); + module.exports = XMLDocumentCB = (function() { + function XMLDocumentCB(options, onData, onEnd) { + var writerOptions; + options || (options = {}); + if (!options.writer) { + options.writer = new XMLStringWriter(options); + } else if (isPlainObject(options.writer)) { + writerOptions = options.writer; + options.writer = new XMLStringWriter(writerOptions); + } + this.options = options; + this.writer = options.writer; + this.stringify = new XMLStringifier(options); + this.onDataCallback = onData || function() {}; + this.onEndCallback = onEnd || function() {}; + this.currentNode = null; + this.currentLevel = -1; + this.openTags = {}; + this.documentStarted = false; + this.documentCompleted = false; + this.root = null; + } - Duplex.call(this, options); + XMLDocumentCB.prototype.node = function(name, attributes, text) { + var ref1; + if (name == null) { + throw new Error("Missing node name"); + } + if (this.root && this.currentLevel === -1) { + throw new Error("Document can only have one root node"); + } + this.openCurrent(); + name = name.valueOf(); + if (attributes == null) { + attributes = {}; + } + attributes = attributes.valueOf(); + if (!isObject(attributes)) { + ref1 = [attributes, text], text = ref1[0], attributes = ref1[1]; + } + this.currentNode = new XMLElement(this, name, attributes); + this.currentNode.children = false; + this.currentLevel++; + this.openTags[this.currentLevel] = this.currentNode; + if (text != null) { + this.text(text); + } + return this; + }; - this._transformState = new TransformState(this); + XMLDocumentCB.prototype.element = function(name, attributes, text) { + if (this.currentNode && this.currentNode instanceof XMLDocType) { + return this.dtdElement.apply(this, arguments); + } else { + return this.node(name, attributes, text); + } + }; - var stream = this; + XMLDocumentCB.prototype.attribute = function(name, value) { + var attName, attValue; + if (!this.currentNode || this.currentNode.children) { + throw new Error("att() can only be used immediately after an ele() call in callback mode"); + } + if (name != null) { + name = name.valueOf(); + } + if (isObject(name)) { + for (attName in name) { + if (!hasProp.call(name, attName)) continue; + attValue = name[attName]; + this.attribute(attName, attValue); + } + } else { + if (isFunction(value)) { + value = value.apply(); + } + if (!this.options.skipNullAttributes || (value != null)) { + this.currentNode.attributes[name] = new XMLAttribute(this, name, value); + } + } + return this; + }; - // start out asking for a readable event once data is transformed. - this._readableState.needReadable = true; + XMLDocumentCB.prototype.text = function(value) { + var node; + this.openCurrent(); + node = new XMLText(this, value); + this.onData(this.writer.text(node, this.currentLevel + 1)); + return this; + }; - // 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; + XMLDocumentCB.prototype.cdata = function(value) { + var node; + this.openCurrent(); + node = new XMLCData(this, value); + this.onData(this.writer.cdata(node, this.currentLevel + 1)); + return this; + }; - if (options) { - if (typeof options.transform === 'function') this._transform = options.transform; + XMLDocumentCB.prototype.comment = function(value) { + var node; + this.openCurrent(); + node = new XMLComment(this, value); + this.onData(this.writer.comment(node, this.currentLevel + 1)); + return this; + }; - if (typeof options.flush === 'function') this._flush = options.flush; - } + XMLDocumentCB.prototype.raw = function(value) { + var node; + this.openCurrent(); + node = new XMLRaw(this, value); + this.onData(this.writer.raw(node, this.currentLevel + 1)); + return this; + }; - // When the writable side finishes, then flush out anything remaining. - this.once('prefinish', function () { - if (typeof this._flush === 'function') this._flush(function (er, data) { - done(stream, er, data); - });else done(stream); - }); -} + XMLDocumentCB.prototype.instruction = function(target, value) { + var i, insTarget, insValue, len, node; + this.openCurrent(); + if (target != null) { + target = target.valueOf(); + } + if (value != null) { + value = value.valueOf(); + } + if (Array.isArray(target)) { + for (i = 0, len = target.length; i < len; i++) { + insTarget = target[i]; + this.instruction(insTarget); + } + } else if (isObject(target)) { + for (insTarget in target) { + if (!hasProp.call(target, insTarget)) continue; + insValue = target[insTarget]; + this.instruction(insTarget, insValue); + } + } else { + if (isFunction(value)) { + value = value.apply(); + } + node = new XMLProcessingInstruction(this, target, value); + this.onData(this.writer.processingInstruction(node, this.currentLevel + 1)); + } + return this; + }; -Transform.prototype.push = function (chunk, encoding) { - this._transformState.needTransform = false; - return Duplex.prototype.push.call(this, chunk, encoding); -}; + XMLDocumentCB.prototype.declaration = function(version, encoding, standalone) { + var node; + this.openCurrent(); + if (this.documentStarted) { + throw new Error("declaration() must be the first node"); + } + node = new XMLDeclaration(this, version, encoding, standalone); + this.onData(this.writer.declaration(node, this.currentLevel + 1)); + return this; + }; -// 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'); -}; + XMLDocumentCB.prototype.doctype = function(root, pubID, sysID) { + this.openCurrent(); + if (root == null) { + throw new Error("Missing root node name"); + } + if (this.root) { + throw new Error("dtd() must come before the root node"); + } + this.currentNode = new XMLDocType(this, pubID, sysID); + this.currentNode.rootNodeName = root; + this.currentNode.children = false; + this.currentLevel++; + this.openTags[this.currentLevel] = this.currentNode; + return this; + }; -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); - } -}; + XMLDocumentCB.prototype.dtdElement = function(name, value) { + var node; + this.openCurrent(); + node = new XMLDTDElement(this, name, value); + this.onData(this.writer.dtdElement(node, this.currentLevel + 1)); + return this; + }; -// 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; + XMLDocumentCB.prototype.attList = function(elementName, attributeName, attributeType, defaultValueType, defaultValue) { + var node; + this.openCurrent(); + node = new XMLDTDAttList(this, elementName, attributeName, attributeType, defaultValueType, defaultValue); + this.onData(this.writer.dtdAttList(node, this.currentLevel + 1)); + return this; + }; - 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; - } -}; + XMLDocumentCB.prototype.entity = function(name, value) { + var node; + this.openCurrent(); + node = new XMLDTDEntity(this, false, name, value); + this.onData(this.writer.dtdEntity(node, this.currentLevel + 1)); + return this; + }; -Transform.prototype._destroy = function (err, cb) { - var _this = this; + XMLDocumentCB.prototype.pEntity = function(name, value) { + var node; + this.openCurrent(); + node = new XMLDTDEntity(this, true, name, value); + this.onData(this.writer.dtdEntity(node, this.currentLevel + 1)); + return this; + }; - Duplex.prototype._destroy.call(this, err, function (err2) { - cb(err2); - _this.emit('close'); - }); -}; + XMLDocumentCB.prototype.notation = function(name, value) { + var node; + this.openCurrent(); + node = new XMLDTDNotation(this, name, value); + this.onData(this.writer.dtdNotation(node, this.currentLevel + 1)); + return this; + }; -function done(stream, er, data) { - if (er) return stream.emit('error', er); + XMLDocumentCB.prototype.up = function() { + if (this.currentLevel < 0) { + throw new Error("The document node has no parent"); + } + if (this.currentNode) { + if (this.currentNode.children) { + this.closeNode(this.currentNode); + } else { + this.openNode(this.currentNode); + } + this.currentNode = null; + } else { + this.closeNode(this.openTags[this.currentLevel]); + } + delete this.openTags[this.currentLevel]; + this.currentLevel--; + return this; + }; - if (data !== null && data !== undefined) stream.push(data); + XMLDocumentCB.prototype.end = function() { + while (this.currentLevel >= 0) { + this.up(); + } + return this.onEnd(); + }; - // if there's nothing in the write buffer, then that means - // that nothing more will ever be provided - var ws = stream._writableState; - var ts = stream._transformState; + XMLDocumentCB.prototype.openCurrent = function() { + if (this.currentNode) { + this.currentNode.children = true; + return this.openNode(this.currentNode); + } + }; - if (ws.length) throw new Error('Calling transform done when ws.length != 0'); + XMLDocumentCB.prototype.openNode = function(node) { + if (!node.isOpen) { + if (!this.root && this.currentLevel === 0 && node instanceof XMLElement) { + this.root = node; + } + this.onData(this.writer.openNode(node, this.currentLevel)); + return node.isOpen = true; + } + }; - if (ts.transforming) throw new Error('Calling transform done when still transforming'); + XMLDocumentCB.prototype.closeNode = function(node) { + if (!node.isClosed) { + this.onData(this.writer.closeNode(node, this.currentLevel)); + return node.isClosed = true; + } + }; - return stream.push(null); -} -},{"./_stream_duplex":91,"core-util-is":8,"inherits":15}],95:[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. + XMLDocumentCB.prototype.onData = function(chunk) { + this.documentStarted = true; + return this.onDataCallback(chunk); + }; -// A bit simpler than readable streams. -// Implement an async ._write(chunk, encoding, cb), and it'll handle all -// the drain event emission and buffering. + XMLDocumentCB.prototype.onEnd = function() { + this.documentCompleted = true; + return this.onEndCallback(); + }; -'use strict'; + XMLDocumentCB.prototype.ele = function() { + return this.element.apply(this, arguments); + }; -/**/ + XMLDocumentCB.prototype.nod = function(name, attributes, text) { + return this.node(name, attributes, text); + }; -var processNextTick = _dereq_('process-nextick-args'); -/**/ + XMLDocumentCB.prototype.txt = function(value) { + return this.text(value); + }; -module.exports = Writable; + XMLDocumentCB.prototype.dat = function(value) { + return this.cdata(value); + }; -/* */ -function WriteReq(chunk, encoding, cb) { - this.chunk = chunk; - this.encoding = encoding; - this.callback = cb; - this.next = null; -} + XMLDocumentCB.prototype.com = function(value) { + return this.comment(value); + }; -// 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; + XMLDocumentCB.prototype.ins = function(target, value) { + return this.instruction(target, value); + }; - this.next = null; - this.entry = null; - this.finish = function () { - onCorkedFinish(_this, state); - }; -} -/* */ + XMLDocumentCB.prototype.dec = function(version, encoding, standalone) { + return this.declaration(version, encoding, standalone); + }; -/**/ -var asyncWrite = !process.browser && ['v0.10', 'v0.9.'].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : processNextTick; -/**/ + XMLDocumentCB.prototype.dtd = function(root, pubID, sysID) { + return this.doctype(root, pubID, sysID); + }; -/**/ -var Duplex; -/**/ + XMLDocumentCB.prototype.e = function(name, attributes, text) { + return this.element(name, attributes, text); + }; -Writable.WritableState = WritableState; + XMLDocumentCB.prototype.n = function(name, attributes, text) { + return this.node(name, attributes, text); + }; -/**/ -var util = _dereq_('core-util-is'); -util.inherits = _dereq_('inherits'); -/**/ + XMLDocumentCB.prototype.t = function(value) { + return this.text(value); + }; -/**/ -var internalUtil = { - deprecate: _dereq_('util-deprecate') -}; -/**/ + XMLDocumentCB.prototype.d = function(value) { + return this.cdata(value); + }; -/**/ -var Stream = _dereq_('./internal/streams/stream'); -/**/ + XMLDocumentCB.prototype.c = function(value) { + return this.comment(value); + }; -/**/ -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; -} -/**/ + XMLDocumentCB.prototype.r = function(value) { + return this.raw(value); + }; -var destroyImpl = _dereq_('./internal/streams/destroy'); + XMLDocumentCB.prototype.i = function(target, value) { + return this.instruction(target, value); + }; -util.inherits(Writable, Stream); + XMLDocumentCB.prototype.att = function() { + if (this.currentNode && this.currentNode instanceof XMLDocType) { + return this.attList.apply(this, arguments); + } else { + return this.attribute.apply(this, arguments); + } + }; -function nop() {} + XMLDocumentCB.prototype.a = function() { + if (this.currentNode && this.currentNode instanceof XMLDocType) { + return this.attList.apply(this, arguments); + } else { + return this.attribute.apply(this, arguments); + } + }; -function WritableState(options, stream) { - Duplex = Duplex || _dereq_('./_stream_duplex'); + XMLDocumentCB.prototype.ent = function(name, value) { + return this.entity(name, value); + }; - options = options || {}; + XMLDocumentCB.prototype.pent = function(name, value) { + return this.pEntity(name, value); + }; - // object stream flag to indicate whether or not this stream - // contains buffers or objects. - this.objectMode = !!options.objectMode; + XMLDocumentCB.prototype.not = function(name, value) { + return this.notation(name, value); + }; - if (stream instanceof Duplex) this.objectMode = this.objectMode || !!options.writableObjectMode; + return XMLDocumentCB; - // 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 defaultHwm = this.objectMode ? 16 : 16 * 1024; - this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm; + })(); - // cast to ints. - this.highWaterMark = Math.floor(this.highWaterMark); +}).call(this); - // if _final has been called - this.finalCalled = false; +},{"./Utility":105,"./XMLAttribute":106,"./XMLCData":107,"./XMLComment":108,"./XMLDTDAttList":109,"./XMLDTDElement":110,"./XMLDTDEntity":111,"./XMLDTDNotation":112,"./XMLDeclaration":113,"./XMLDocType":114,"./XMLElement":117,"./XMLProcessingInstruction":119,"./XMLRaw":120,"./XMLStringWriter":122,"./XMLStringifier":123,"./XMLText":124}],117:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var XMLAttribute, XMLElement, XMLNode, isFunction, isObject, ref, + 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; - // 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; + ref = _dereq_('./Utility'), isObject = ref.isObject, isFunction = ref.isFunction; - // has it been destroyed - this.destroyed = false; + XMLNode = _dereq_('./XMLNode'); - // 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; + XMLAttribute = _dereq_('./XMLAttribute'); - // 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'; + module.exports = XMLElement = (function(superClass) { + extend(XMLElement, superClass); - // 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; + function XMLElement(parent, name, attributes) { + XMLElement.__super__.constructor.call(this, parent); + if (name == null) { + throw new Error("Missing element name"); + } + this.name = this.stringify.eleName(name); + this.attributes = {}; + if (attributes != null) { + this.attribute(attributes); + } + if (parent.isDocument) { + this.isRoot = true; + this.documentObject = parent; + parent.rootObject = this; + } + } - // a flag to see when we're in the middle of a write. - this.writing = false; + XMLElement.prototype.clone = function() { + var att, attName, clonedSelf, ref1; + clonedSelf = Object.create(this); + if (clonedSelf.isRoot) { + clonedSelf.documentObject = null; + } + clonedSelf.attributes = {}; + ref1 = this.attributes; + for (attName in ref1) { + if (!hasProp.call(ref1, attName)) continue; + att = ref1[attName]; + clonedSelf.attributes[attName] = att.clone(); + } + clonedSelf.children = []; + this.children.forEach(function(child) { + var clonedChild; + clonedChild = child.clone(); + clonedChild.parent = clonedSelf; + return clonedSelf.children.push(clonedChild); + }); + return clonedSelf; + }; - // when true all writes will be buffered until .uncork() call - this.corked = 0; + XMLElement.prototype.attribute = function(name, value) { + var attName, attValue; + if (name != null) { + name = name.valueOf(); + } + if (isObject(name)) { + for (attName in name) { + if (!hasProp.call(name, attName)) continue; + attValue = name[attName]; + this.attribute(attName, attValue); + } + } else { + if (isFunction(value)) { + value = value.apply(); + } + if (!this.options.skipNullAttributes || (value != null)) { + this.attributes[name] = new XMLAttribute(this, name, value); + } + } + return this; + }; - // 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; + XMLElement.prototype.removeAttribute = function(name) { + var attName, i, len; + if (name == null) { + throw new Error("Missing attribute name"); + } + name = name.valueOf(); + if (Array.isArray(name)) { + for (i = 0, len = name.length; i < len; i++) { + attName = name[i]; + delete this.attributes[attName]; + } + } else { + delete this.attributes[name]; + } + return this; + }; - // 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; + XMLElement.prototype.toString = function(options) { + return this.options.writer.set(options).element(this); + }; - // the callback that's passed to _write(chunk,cb) - this.onwrite = function (er) { - onwrite(stream, er); - }; + XMLElement.prototype.att = function(name, value) { + return this.attribute(name, value); + }; - // the callback that the user supplies to write(chunk,encoding,cb) - this.writecb = null; + XMLElement.prototype.a = function(name, value) { + return this.attribute(name, value); + }; - // the amount that is being written when _write is called. - this.writelen = 0; + return XMLElement; - this.bufferedRequest = null; - this.lastBufferedRequest = null; + })(XMLNode); - // number of pending user-supplied write callbacks - // this must be 0 before 'finish' can be emitted - this.pendingcb = 0; +}).call(this); - // emit prefinish if the only thing we're waiting for is _write cbs - // This is relevant for synchronous Transform streams - this.prefinished = false; +},{"./Utility":105,"./XMLAttribute":106,"./XMLNode":118}],118:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var XMLCData, XMLComment, XMLDeclaration, XMLDocType, XMLElement, XMLNode, XMLProcessingInstruction, XMLRaw, XMLText, isEmpty, isFunction, isObject, ref, + hasProp = {}.hasOwnProperty; - // True if the error was already emitted and should not be thrown again - this.errorEmitted = false; + ref = _dereq_('./Utility'), isObject = ref.isObject, isFunction = ref.isFunction, isEmpty = ref.isEmpty; - // count buffered requests - this.bufferedRequestCount = 0; + XMLElement = null; - // allocate the first CorkedRequest, there is always - // one allocated and free to use, and we maintain at most two - this.corkedRequestsFree = new CorkedRequest(this); -} + XMLCData = null; -WritableState.prototype.getBuffer = function getBuffer() { - var current = this.bufferedRequest; - var out = []; - while (current) { - out.push(current); - current = current.next; - } - return out; -}; + XMLComment = null; -(function () { - try { - Object.defineProperty(WritableState.prototype, 'buffer', { - get: internalUtil.deprecate(function () { - return this.getBuffer(); - }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') - }); - } catch (_) {} -})(); + XMLDeclaration = null; -// 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; + XMLDocType = null; - return object && object._writableState instanceof WritableState; - } - }); -} else { - realHasInstance = function (object) { - return object instanceof this; - }; -} + XMLRaw = null; -function Writable(options) { - Duplex = Duplex || _dereq_('./_stream_duplex'); + XMLText = null; - // Writable ctor is applied to Duplexes, too. - // `realHasInstance` is necessary because using plain `instanceof` - // would return false, as no `_writableState` property is attached. + XMLProcessingInstruction = null; - // 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); - } + module.exports = XMLNode = (function() { + function XMLNode(parent) { + this.parent = parent; + if (this.parent) { + this.options = this.parent.options; + this.stringify = this.parent.stringify; + } + this.children = []; + if (!XMLElement) { + XMLElement = _dereq_('./XMLElement'); + XMLCData = _dereq_('./XMLCData'); + XMLComment = _dereq_('./XMLComment'); + XMLDeclaration = _dereq_('./XMLDeclaration'); + XMLDocType = _dereq_('./XMLDocType'); + XMLRaw = _dereq_('./XMLRaw'); + XMLText = _dereq_('./XMLText'); + XMLProcessingInstruction = _dereq_('./XMLProcessingInstruction'); + } + } - this._writableState = new WritableState(options, this); + XMLNode.prototype.element = function(name, attributes, text) { + var childNode, item, j, k, key, lastChild, len, len1, ref1, val; + lastChild = null; + if (attributes == null) { + attributes = {}; + } + attributes = attributes.valueOf(); + if (!isObject(attributes)) { + ref1 = [attributes, text], text = ref1[0], attributes = ref1[1]; + } + if (name != null) { + name = name.valueOf(); + } + if (Array.isArray(name)) { + for (j = 0, len = name.length; j < len; j++) { + item = name[j]; + lastChild = this.element(item); + } + } else if (isFunction(name)) { + lastChild = this.element(name.apply()); + } else if (isObject(name)) { + for (key in name) { + if (!hasProp.call(name, key)) continue; + val = name[key]; + if (isFunction(val)) { + val = val.apply(); + } + if ((isObject(val)) && (isEmpty(val))) { + val = null; + } + if (!this.options.ignoreDecorators && this.stringify.convertAttKey && key.indexOf(this.stringify.convertAttKey) === 0) { + lastChild = this.attribute(key.substr(this.stringify.convertAttKey.length), val); + } else if (!this.options.separateArrayItems && Array.isArray(val)) { + for (k = 0, len1 = val.length; k < len1; k++) { + item = val[k]; + childNode = {}; + childNode[key] = item; + lastChild = this.element(childNode); + } + } else if (isObject(val)) { + lastChild = this.element(key); + lastChild.element(val); + } else { + lastChild = this.element(key, val); + } + } + } else { + if (!this.options.ignoreDecorators && this.stringify.convertTextKey && name.indexOf(this.stringify.convertTextKey) === 0) { + lastChild = this.text(text); + } else if (!this.options.ignoreDecorators && this.stringify.convertCDataKey && name.indexOf(this.stringify.convertCDataKey) === 0) { + lastChild = this.cdata(text); + } else if (!this.options.ignoreDecorators && this.stringify.convertCommentKey && name.indexOf(this.stringify.convertCommentKey) === 0) { + lastChild = this.comment(text); + } else if (!this.options.ignoreDecorators && this.stringify.convertRawKey && name.indexOf(this.stringify.convertRawKey) === 0) { + lastChild = this.raw(text); + } else if (!this.options.ignoreDecorators && this.stringify.convertPIKey && name.indexOf(this.stringify.convertPIKey) === 0) { + lastChild = this.instruction(name.substr(this.stringify.convertPIKey.length), text); + } else { + lastChild = this.node(name, attributes, text); + } + } + if (lastChild == null) { + throw new Error("Could not create any elements with: " + name); + } + return lastChild; + }; - // legacy. - this.writable = true; + XMLNode.prototype.insertBefore = function(name, attributes, text) { + var child, i, removed; + if (this.isRoot) { + throw new Error("Cannot insert elements at root level"); + } + i = this.parent.children.indexOf(this); + removed = this.parent.children.splice(i); + child = this.parent.element(name, attributes, text); + Array.prototype.push.apply(this.parent.children, removed); + return child; + }; - if (options) { - if (typeof options.write === 'function') this._write = options.write; + XMLNode.prototype.insertAfter = function(name, attributes, text) { + var child, i, removed; + if (this.isRoot) { + throw new Error("Cannot insert elements at root level"); + } + i = this.parent.children.indexOf(this); + removed = this.parent.children.splice(i + 1); + child = this.parent.element(name, attributes, text); + Array.prototype.push.apply(this.parent.children, removed); + return child; + }; - if (typeof options.writev === 'function') this._writev = options.writev; + XMLNode.prototype.remove = function() { + var i, ref1; + if (this.isRoot) { + throw new Error("Cannot remove the root element"); + } + i = this.parent.children.indexOf(this); + [].splice.apply(this.parent.children, [i, i - i + 1].concat(ref1 = [])), ref1; + return this.parent; + }; - if (typeof options.destroy === 'function') this._destroy = options.destroy; + XMLNode.prototype.node = function(name, attributes, text) { + var child, ref1; + if (name != null) { + name = name.valueOf(); + } + attributes || (attributes = {}); + attributes = attributes.valueOf(); + if (!isObject(attributes)) { + ref1 = [attributes, text], text = ref1[0], attributes = ref1[1]; + } + child = new XMLElement(this, name, attributes); + if (text != null) { + child.text(text); + } + this.children.push(child); + return child; + }; - if (typeof options.final === 'function') this._final = options.final; - } + XMLNode.prototype.text = function(value) { + var child; + child = new XMLText(this, value); + this.children.push(child); + return this; + }; - Stream.call(this); -} + XMLNode.prototype.cdata = function(value) { + var child; + child = new XMLCData(this, value); + this.children.push(child); + return this; + }; -// Otherwise people can pipe Writable streams, which is just wrong. -Writable.prototype.pipe = function () { - this.emit('error', new Error('Cannot pipe, not readable')); -}; + XMLNode.prototype.comment = function(value) { + var child; + child = new XMLComment(this, value); + this.children.push(child); + return this; + }; -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); - processNextTick(cb, er); -} + XMLNode.prototype.commentBefore = function(value) { + var child, i, removed; + i = this.parent.children.indexOf(this); + removed = this.parent.children.splice(i); + child = this.parent.comment(value); + Array.prototype.push.apply(this.parent.children, removed); + return this; + }; -// 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; + XMLNode.prototype.commentAfter = function(value) { + var child, i, removed; + i = this.parent.children.indexOf(this); + removed = this.parent.children.splice(i + 1); + child = this.parent.comment(value); + Array.prototype.push.apply(this.parent.children, removed); + return this; + }; - 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'); - } - if (er) { - stream.emit('error', er); - processNextTick(cb, er); - valid = false; - } - return valid; -} + XMLNode.prototype.raw = function(value) { + var child; + child = new XMLRaw(this, value); + this.children.push(child); + return this; + }; -Writable.prototype.write = function (chunk, encoding, cb) { - var state = this._writableState; - var ret = false; - var isBuf = _isUint8Array(chunk) && !state.objectMode; + XMLNode.prototype.instruction = function(target, value) { + var insTarget, insValue, instruction, j, len; + if (target != null) { + target = target.valueOf(); + } + if (value != null) { + value = value.valueOf(); + } + if (Array.isArray(target)) { + for (j = 0, len = target.length; j < len; j++) { + insTarget = target[j]; + this.instruction(insTarget); + } + } else if (isObject(target)) { + for (insTarget in target) { + if (!hasProp.call(target, insTarget)) continue; + insValue = target[insTarget]; + this.instruction(insTarget, insValue); + } + } else { + if (isFunction(value)) { + value = value.apply(); + } + instruction = new XMLProcessingInstruction(this, target, value); + this.children.push(instruction); + } + return this; + }; - if (isBuf && !Buffer.isBuffer(chunk)) { - chunk = _uint8ArrayToBuffer(chunk); - } + XMLNode.prototype.instructionBefore = function(target, value) { + var child, i, removed; + i = this.parent.children.indexOf(this); + removed = this.parent.children.splice(i); + child = this.parent.instruction(target, value); + Array.prototype.push.apply(this.parent.children, removed); + return this; + }; - if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } + XMLNode.prototype.instructionAfter = function(target, value) { + var child, i, removed; + i = this.parent.children.indexOf(this); + removed = this.parent.children.splice(i + 1); + child = this.parent.instruction(target, value); + Array.prototype.push.apply(this.parent.children, removed); + return this; + }; - if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; + XMLNode.prototype.declaration = function(version, encoding, standalone) { + var doc, xmldec; + doc = this.document(); + xmldec = new XMLDeclaration(doc, version, encoding, standalone); + if (doc.children[0] instanceof XMLDeclaration) { + doc.children[0] = xmldec; + } else { + doc.children.unshift(xmldec); + } + return doc.root() || doc; + }; - if (typeof cb !== 'function') cb = nop; + XMLNode.prototype.doctype = function(pubID, sysID) { + var child, doc, doctype, i, j, k, len, len1, ref1, ref2; + doc = this.document(); + doctype = new XMLDocType(doc, pubID, sysID); + ref1 = doc.children; + for (i = j = 0, len = ref1.length; j < len; i = ++j) { + child = ref1[i]; + if (child instanceof XMLDocType) { + doc.children[i] = doctype; + return doctype; + } + } + ref2 = doc.children; + for (i = k = 0, len1 = ref2.length; k < len1; i = ++k) { + child = ref2[i]; + if (child.isRoot) { + doc.children.splice(i, 0, doctype); + return doctype; + } + } + doc.children.push(doctype); + return doctype; + }; - if (state.ended) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { - state.pendingcb++; - ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); - } + XMLNode.prototype.up = function() { + if (this.isRoot) { + throw new Error("The root node has no parent. Use doc() if you need to get the document object."); + } + return this.parent; + }; - return ret; -}; + XMLNode.prototype.root = function() { + var node; + node = this; + while (node) { + if (node.isDocument) { + return node.rootObject; + } else if (node.isRoot) { + return node; + } else { + node = node.parent; + } + } + }; -Writable.prototype.cork = function () { - var state = this._writableState; + XMLNode.prototype.document = function() { + var node; + node = this; + while (node) { + if (node.isDocument) { + return node; + } else { + node = node.parent; + } + } + }; - state.corked++; -}; + XMLNode.prototype.end = function(options) { + return this.document().end(options); + }; -Writable.prototype.uncork = function () { - var state = this._writableState; + XMLNode.prototype.prev = function() { + var i; + i = this.parent.children.indexOf(this); + if (i < 1) { + throw new Error("Already at the first node"); + } + return this.parent.children[i - 1]; + }; - if (state.corked) { - state.corked--; + XMLNode.prototype.next = function() { + var i; + i = this.parent.children.indexOf(this); + if (i === -1 || i === this.parent.children.length - 1) { + throw new Error("Already at the last node"); + } + return this.parent.children[i + 1]; + }; - if (!state.writing && !state.corked && !state.finished && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); - } -}; + XMLNode.prototype.importDocument = function(doc) { + var clonedRoot; + clonedRoot = doc.root().clone(); + clonedRoot.parent = this; + clonedRoot.isRoot = false; + this.children.push(clonedRoot); + return this; + }; -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; -}; + XMLNode.prototype.ele = function(name, attributes, text) { + return this.element(name, attributes, text); + }; -function decodeChunk(state, chunk, encoding) { - if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { - chunk = Buffer.from(chunk, encoding); - } - return chunk; -} + XMLNode.prototype.nod = function(name, attributes, text) { + return this.node(name, attributes, text); + }; -// 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 len = state.objectMode ? 1 : chunk.length; + XMLNode.prototype.txt = function(value) { + return this.text(value); + }; - state.length += len; + XMLNode.prototype.dat = function(value) { + return this.cdata(value); + }; - var ret = state.length < state.highWaterMark; - // we must ensure that previous needDrain will not be reset to false. - if (!ret) state.needDrain = true; + XMLNode.prototype.com = function(value) { + return this.comment(value); + }; - if (state.writing || state.corked) { - var last = state.lastBufferedRequest; - state.lastBufferedRequest = { - chunk: chunk, - encoding: encoding, - isBuf: isBuf, - callback: cb, - next: null + XMLNode.prototype.ins = function(target, value) { + return this.instruction(target, value); }; - if (last) { - last.next = state.lastBufferedRequest; - } else { - state.bufferedRequest = state.lastBufferedRequest; - } - state.bufferedRequestCount += 1; - } else { - doWrite(stream, state, false, len, chunk, encoding, cb); - } - return ret; -} + XMLNode.prototype.doc = function() { + return this.document(); + }; -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; -} + XMLNode.prototype.dec = function(version, encoding, standalone) { + return this.declaration(version, encoding, standalone); + }; -function onwriteError(stream, state, sync, er, cb) { - --state.pendingcb; + XMLNode.prototype.dtd = function(pubID, sysID) { + return this.doctype(pubID, sysID); + }; - if (sync) { - // defer the callback if we are being called synchronously - // to avoid piling up things on the stack - processNextTick(cb, er); - // this can emit finish, and it will always happen - // after error - processNextTick(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); - } -} + XMLNode.prototype.e = function(name, attributes, text) { + return this.element(name, attributes, text); + }; -function onwriteStateUpdate(state) { - state.writing = false; - state.writecb = null; - state.length -= state.writelen; - state.writelen = 0; -} + XMLNode.prototype.n = function(name, attributes, text) { + return this.node(name, attributes, text); + }; -function onwrite(stream, er) { - var state = stream._writableState; - var sync = state.sync; - var cb = state.writecb; + XMLNode.prototype.t = function(value) { + return this.text(value); + }; - onwriteStateUpdate(state); + XMLNode.prototype.d = function(value) { + return this.cdata(value); + }; - 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); + XMLNode.prototype.c = function(value) { + return this.comment(value); + }; - if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { - clearBuffer(stream, state); - } + XMLNode.prototype.r = function(value) { + return this.raw(value); + }; - if (sync) { - /**/ - asyncWrite(afterWrite, stream, state, finished, cb); - /**/ - } else { - afterWrite(stream, state, finished, cb); - } - } -} + XMLNode.prototype.i = function(target, value) { + return this.instruction(target, value); + }; -function afterWrite(stream, state, finished, cb) { - if (!finished) onwriteDrain(stream, state); - state.pendingcb--; - cb(); - finishMaybe(stream, state); -} + XMLNode.prototype.u = function() { + return this.up(); + }; -// 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'); - } -} + XMLNode.prototype.importXMLBuilder = function(doc) { + return this.importDocument(doc); + }; -// if there's something in the buffer waiting, then process it -function clearBuffer(stream, state) { - state.bufferProcessing = true; - var entry = state.bufferedRequest; + return XMLNode; - 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; - } - buffer.allBuffers = allBuffers; +}).call(this); - doWrite(stream, state, true, state.length, buffer, '', holder.finish); +},{"./Utility":105,"./XMLCData":107,"./XMLComment":108,"./XMLDeclaration":113,"./XMLDocType":114,"./XMLElement":117,"./XMLProcessingInstruction":119,"./XMLRaw":120,"./XMLText":124}],119:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var XMLNode, XMLProcessingInstruction, + 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; - // 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 { - // 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; + XMLNode = _dereq_('./XMLNode'); - doWrite(stream, state, false, len, chunk, encoding, cb); - entry = entry.next; - // 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; + module.exports = XMLProcessingInstruction = (function(superClass) { + extend(XMLProcessingInstruction, superClass); + + function XMLProcessingInstruction(parent, target, value) { + XMLProcessingInstruction.__super__.constructor.call(this, parent); + if (target == null) { + throw new Error("Missing instruction target"); + } + this.target = this.stringify.insTarget(target); + if (value) { + this.value = this.stringify.insValue(value); } } - if (entry === null) state.lastBufferedRequest = null; - } - - state.bufferedRequestCount = 0; - state.bufferedRequest = entry; - state.bufferProcessing = false; -} - -Writable.prototype._write = function (chunk, encoding, cb) { - cb(new Error('_write() is not implemented')); -}; - -Writable.prototype._writev = null; - -Writable.prototype.end = function (chunk, encoding, cb) { - var state = this._writableState; - - if (typeof chunk === 'function') { - cb = chunk; - chunk = null; - encoding = null; - } else if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } + XMLProcessingInstruction.prototype.clone = function() { + return Object.create(this); + }; - if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); + XMLProcessingInstruction.prototype.toString = function(options) { + return this.options.writer.set(options).processingInstruction(this); + }; - // .end() fully uncorks - if (state.corked) { - state.corked = 1; - this.uncork(); - } + return XMLProcessingInstruction; - // ignore unnecessary end() calls. - if (!state.ending && !state.finished) endWritable(this, state, cb); -}; + })(XMLNode); -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; - processNextTick(callFinal, stream, state); - } else { - state.prefinished = true; - stream.emit('prefinish'); - } - } -} +}).call(this); -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; -} +},{"./XMLNode":118}],120:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var XMLNode, XMLRaw, + 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; -function endWritable(stream, state, cb) { - state.ending = true; - finishMaybe(stream, state); - if (cb) { - if (state.finished) processNextTick(cb);else stream.once('finish', cb); - } - state.ended = true; - stream.writable = false; -} + XMLNode = _dereq_('./XMLNode'); -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; - } -} + module.exports = XMLRaw = (function(superClass) { + extend(XMLRaw, superClass); -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; + function XMLRaw(parent, text) { + XMLRaw.__super__.constructor.call(this, parent); + if (text == null) { + throw new Error("Missing raw text"); + } + this.value = this.stringify.raw(text); } - // backward compatibility, the user is explicitly - // managing destroyed - this._writableState.destroyed = value; - } -}); + XMLRaw.prototype.clone = function() { + return Object.create(this); + }; -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 : {}) + XMLRaw.prototype.toString = function(options) { + return this.options.writer.set(options).raw(this); + }; -},{"./_stream_duplex":91,"./internal/streams/destroy":97,"./internal/streams/stream":98,"_process":43,"core-util-is":8,"inherits":15,"process-nextick-args":42,"safe-buffer":105,"util-deprecate":115}],96:[function(_dereq_,module,exports){ -'use strict'; + return XMLRaw; -/**/ + })(XMLNode); -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +}).call(this); -var Buffer = _dereq_('safe-buffer').Buffer; -/**/ +},{"./XMLNode":118}],121:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var XMLCData, XMLComment, XMLDTDAttList, XMLDTDElement, XMLDTDEntity, XMLDTDNotation, XMLDeclaration, XMLDocType, XMLElement, XMLProcessingInstruction, XMLRaw, XMLStreamWriter, XMLText, XMLWriterBase, + 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; -function copyBuffer(src, target, offset) { - src.copy(target, offset); -} + XMLDeclaration = _dereq_('./XMLDeclaration'); -module.exports = function () { - function BufferList() { - _classCallCheck(this, BufferList); + XMLDocType = _dereq_('./XMLDocType'); - this.head = null; - this.tail = null; - this.length = 0; - } + XMLCData = _dereq_('./XMLCData'); - 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; - }; + XMLComment = _dereq_('./XMLComment'); - 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; - }; + XMLElement = _dereq_('./XMLElement'); - 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; - }; + XMLRaw = _dereq_('./XMLRaw'); - BufferList.prototype.clear = function clear() { - this.head = this.tail = null; - this.length = 0; - }; + XMLText = _dereq_('./XMLText'); - 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; - }; + XMLProcessingInstruction = _dereq_('./XMLProcessingInstruction'); - 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; - }; + XMLDTDAttList = _dereq_('./XMLDTDAttList'); - return BufferList; -}(); -},{"safe-buffer":105}],97:[function(_dereq_,module,exports){ -'use strict'; + XMLDTDElement = _dereq_('./XMLDTDElement'); -/**/ + XMLDTDEntity = _dereq_('./XMLDTDEntity'); -var processNextTick = _dereq_('process-nextick-args'); -/**/ + XMLDTDNotation = _dereq_('./XMLDTDNotation'); -// undocumented cb() API, needed for core, not for public API -function destroy(err, cb) { - var _this = this; + XMLWriterBase = _dereq_('./XMLWriterBase'); - var readableDestroyed = this._readableState && this._readableState.destroyed; - var writableDestroyed = this._writableState && this._writableState.destroyed; + module.exports = XMLStreamWriter = (function(superClass) { + extend(XMLStreamWriter, superClass); - if (readableDestroyed || writableDestroyed) { - if (cb) { - cb(err); - } else if (err && (!this._writableState || !this._writableState.errorEmitted)) { - processNextTick(emitErrorNT, this, err); + function XMLStreamWriter(stream, options) { + XMLStreamWriter.__super__.constructor.call(this, options); + this.stream = stream; } - return; - } - - // we set destroyed to true before firing error callbacks in order - // to make it re-entrance safe in case destroy() is called within callbacks - - if (this._readableState) { - this._readableState.destroyed = true; - } - // if this is a duplex stream mark the writable part as destroyed as well - if (this._writableState) { - this._writableState.destroyed = true; - } - - this._destroy(err || null, function (err) { - if (!cb && err) { - processNextTick(emitErrorNT, _this, err); - if (_this._writableState) { - _this._writableState.errorEmitted = true; + XMLStreamWriter.prototype.document = function(doc) { + var child, i, j, len, len1, ref, ref1, results; + ref = doc.children; + for (i = 0, len = ref.length; i < len; i++) { + child = ref[i]; + child.isLastRootNode = false; } - } else if (cb) { - cb(err); - } - }); -} - -function undestroy() { - if (this._readableState) { - this._readableState.destroyed = false; - this._readableState.reading = false; - this._readableState.ended = false; - this._readableState.endEmitted = false; - } - - if (this._writableState) { - this._writableState.destroyed = false; - this._writableState.ended = false; - this._writableState.ending = false; - this._writableState.finished = false; - this._writableState.errorEmitted = false; - } -} - -function emitErrorNT(self, err) { - self.emit('error', err); -} + doc.children[doc.children.length - 1].isLastRootNode = true; + ref1 = doc.children; + results = []; + for (j = 0, len1 = ref1.length; j < len1; j++) { + child = ref1[j]; + switch (false) { + case !(child instanceof XMLDeclaration): + results.push(this.declaration(child)); + break; + case !(child instanceof XMLDocType): + results.push(this.docType(child)); + break; + case !(child instanceof XMLComment): + results.push(this.comment(child)); + break; + case !(child instanceof XMLProcessingInstruction): + results.push(this.processingInstruction(child)); + break; + default: + results.push(this.element(child)); + } + } + return results; + }; -module.exports = { - destroy: destroy, - undestroy: undestroy -}; -},{"process-nextick-args":42}],98:[function(_dereq_,module,exports){ -module.exports = _dereq_('events').EventEmitter; + XMLStreamWriter.prototype.attribute = function(att) { + return this.stream.write(' ' + att.name + '="' + att.value + '"'); + }; -},{"events":10}],99:[function(_dereq_,module,exports){ -arguments[4][6][0].apply(exports,arguments) -},{"dup":6}],100:[function(_dereq_,module,exports){ -'use strict'; + XMLStreamWriter.prototype.cdata = function(node, level) { + return this.stream.write(this.space(level) + '' + this.endline(node)); + }; -var Buffer = _dereq_('safe-buffer').Buffer; + XMLStreamWriter.prototype.comment = function(node, level) { + return this.stream.write(this.space(level) + '' + this.endline(node)); + }; -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; - } -}; + XMLStreamWriter.prototype.declaration = function(node, level) { + this.stream.write(this.space(level)); + this.stream.write(''); + return this.stream.write(this.endline(node)); + }; -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; - } - } -}; + XMLStreamWriter.prototype.docType = function(node, level) { + var child, i, len, ref; + level || (level = 0); + this.stream.write(this.space(level)); + this.stream.write(' 0) { + this.stream.write(' ['); + this.stream.write(this.endline(node)); + ref = node.children; + for (i = 0, len = ref.length; i < len; i++) { + child = ref[i]; + switch (false) { + case !(child instanceof XMLDTDAttList): + this.dtdAttList(child, level + 1); + break; + case !(child instanceof XMLDTDElement): + this.dtdElement(child, level + 1); + break; + case !(child instanceof XMLDTDEntity): + this.dtdEntity(child, level + 1); + break; + case !(child instanceof XMLDTDNotation): + this.dtdNotation(child, level + 1); + break; + case !(child instanceof XMLCData): + this.cdata(child, level + 1); + break; + case !(child instanceof XMLComment): + this.comment(child, level + 1); + break; + case !(child instanceof XMLProcessingInstruction): + this.processingInstruction(child, level + 1); + break; + default: + throw new Error("Unknown DTD node type: " + child.constructor.name); + } + } + this.stream.write(']'); + } + this.stream.write(this.spacebeforeslash + '>'); + return this.stream.write(this.endline(node)); + }; -// 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; -} + XMLStreamWriter.prototype.element = function(node, level) { + var att, child, i, len, name, ref, ref1, space; + level || (level = 0); + space = this.space(level); + this.stream.write(space + '<' + node.name); + ref = node.attributes; + for (name in ref) { + if (!hasProp.call(ref, name)) continue; + att = ref[name]; + this.attribute(att); + } + if (node.children.length === 0 || node.children.every(function(e) { + return e.value === ''; + })) { + if (this.allowEmpty) { + this.stream.write('>'); + } else { + this.stream.write(this.spacebeforeslash + '/>'); + } + } else if (this.pretty && node.children.length === 1 && (node.children[0].value != null)) { + this.stream.write('>'); + this.stream.write(node.children[0].value); + this.stream.write(''); + } else { + this.stream.write('>' + this.newline); + ref1 = node.children; + for (i = 0, len = ref1.length; i < len; i++) { + child = ref1[i]; + switch (false) { + case !(child instanceof XMLCData): + this.cdata(child, level + 1); + break; + case !(child instanceof XMLComment): + this.comment(child, level + 1); + break; + case !(child instanceof XMLElement): + this.element(child, level + 1); + break; + case !(child instanceof XMLRaw): + this.raw(child, level + 1); + break; + case !(child instanceof XMLText): + this.text(child, level + 1); + break; + case !(child instanceof XMLProcessingInstruction): + this.processingInstruction(child, level + 1); + break; + default: + throw new Error("Unknown XML node type: " + child.constructor.name); + } + } + this.stream.write(space + ''); + } + return this.stream.write(this.endline(node)); + }; -// 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); -} + XMLStreamWriter.prototype.processingInstruction = function(node, level) { + this.stream.write(this.space(level) + '' + this.endline(node)); + }; -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 || ''; -}; + XMLStreamWriter.prototype.raw = function(node, level) { + return this.stream.write(this.space(level) + node.value + this.endline(node)); + }; -StringDecoder.prototype.end = utf8End; + XMLStreamWriter.prototype.text = function(node, level) { + return this.stream.write(this.space(level) + node.value + this.endline(node)); + }; -// Returns only complete characters in a Buffer -StringDecoder.prototype.text = utf8Text; + XMLStreamWriter.prototype.dtdAttList = function(node, level) { + this.stream.write(this.space(level) + '' + this.endline(node)); + }; -// 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; -}; + XMLStreamWriter.prototype.dtdElement = function(node, level) { + this.stream.write(this.space(level) + '' + this.endline(node)); + }; -// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a -// continuation byte. -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 -1; -} + XMLStreamWriter.prototype.dtdEntity = function(node, level) { + this.stream.write(this.space(level) + '' + this.endline(node)); + }; -// 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) return 0; - nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) self.lastNeed = nb - 2; - return nb; - } - if (--j < i) 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; -} + XMLStreamWriter.prototype.dtdNotation = function(node, level) { + this.stream.write(this.space(level) + '' + this.endline(node)); + }; -// 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 -// UTF-8 replacement characters ('\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'.repeat(p); - } - if (self.lastNeed > 1 && buf.length > 1) { - if ((buf[1] & 0xC0) !== 0x80) { - self.lastNeed = 1; - return '\ufffd'.repeat(p + 1); - } - if (self.lastNeed > 2 && buf.length > 2) { - if ((buf[2] & 0xC0) !== 0x80) { - self.lastNeed = 2; - return '\ufffd'.repeat(p + 2); + XMLStreamWriter.prototype.endline = function(node) { + if (!node.isLastRootNode) { + return this.newline; + } else { + return ''; } - } - } -} + }; -// 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; -} + return XMLStreamWriter; -// 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); -} + })(XMLWriterBase); -// For UTF-8, a replacement character for each buffered byte of a (partial) -// character needs to be added to the output. -function utf8End(buf) { - var r = buf && buf.length ? this.write(buf) : ''; - if (this.lastNeed) return r + '\ufffd'.repeat(this.lastTotal - this.lastNeed); - return r; -} +}).call(this); -// 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); -} +},{"./XMLCData":107,"./XMLComment":108,"./XMLDTDAttList":109,"./XMLDTDElement":110,"./XMLDTDEntity":111,"./XMLDTDNotation":112,"./XMLDeclaration":113,"./XMLDocType":114,"./XMLElement":117,"./XMLProcessingInstruction":119,"./XMLRaw":120,"./XMLText":124,"./XMLWriterBase":125}],122:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var XMLCData, XMLComment, XMLDTDAttList, XMLDTDElement, XMLDTDEntity, XMLDTDNotation, XMLDeclaration, XMLDocType, XMLElement, XMLProcessingInstruction, XMLRaw, XMLStringWriter, XMLText, XMLWriterBase, + 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; -// 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; -} + XMLDeclaration = _dereq_('./XMLDeclaration'); -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); -} + XMLDocType = _dereq_('./XMLDocType'); -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; -} + XMLCData = _dereq_('./XMLCData'); -// Pass bytes on through for single-byte encodings (e.g. ascii, latin1, hex) -function simpleWrite(buf) { - return buf.toString(this.encoding); -} + XMLComment = _dereq_('./XMLComment'); -function simpleEnd(buf) { - return buf && buf.length ? this.write(buf) : ''; -} -},{"safe-buffer":105}],101:[function(_dereq_,module,exports){ -module.exports = _dereq_('./readable').PassThrough + XMLElement = _dereq_('./XMLElement'); -},{"./readable":102}],102:[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'); + XMLRaw = _dereq_('./XMLRaw'); -},{"./lib/_stream_duplex.js":91,"./lib/_stream_passthrough.js":92,"./lib/_stream_readable.js":93,"./lib/_stream_transform.js":94,"./lib/_stream_writable.js":95}],103:[function(_dereq_,module,exports){ -module.exports = _dereq_('./readable').Transform + XMLText = _dereq_('./XMLText'); -},{"./readable":102}],104:[function(_dereq_,module,exports){ -module.exports = _dereq_('./lib/_stream_writable.js'); + XMLProcessingInstruction = _dereq_('./XMLProcessingInstruction'); -},{"./lib/_stream_writable.js":95}],105:[function(_dereq_,module,exports){ -/* eslint-disable node/no-deprecated-api */ -var buffer = _dereq_('buffer') -var Buffer = buffer.Buffer + XMLDTDAttList = _dereq_('./XMLDTDAttList'); -// 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 -} + XMLDTDElement = _dereq_('./XMLDTDElement'); -function SafeBuffer (arg, encodingOrOffset, length) { - return Buffer(arg, encodingOrOffset, length) -} + XMLDTDEntity = _dereq_('./XMLDTDEntity'); -// Copy static methods from Buffer -copyProps(Buffer, SafeBuffer) + XMLDTDNotation = _dereq_('./XMLDTDNotation'); -SafeBuffer.from = function (arg, encodingOrOffset, length) { - if (typeof arg === 'number') { - throw new TypeError('Argument must not be a number') - } - return Buffer(arg, encodingOrOffset, length) -} + XMLWriterBase = _dereq_('./XMLWriterBase'); -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 { - buf.fill(fill) + module.exports = XMLStringWriter = (function(superClass) { + extend(XMLStringWriter, superClass); + + function XMLStringWriter(options) { + XMLStringWriter.__super__.constructor.call(this, options); } - } else { - buf.fill(0) - } - return buf -} -SafeBuffer.allocUnsafe = function (size) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - return Buffer(size) -} + XMLStringWriter.prototype.document = function(doc) { + var child, i, len, r, ref; + this.textispresent = false; + r = ''; + ref = doc.children; + for (i = 0, len = ref.length; i < len; i++) { + child = ref[i]; + r += (function() { + switch (false) { + case !(child instanceof XMLDeclaration): + return this.declaration(child); + case !(child instanceof XMLDocType): + return this.docType(child); + case !(child instanceof XMLComment): + return this.comment(child); + case !(child instanceof XMLProcessingInstruction): + return this.processingInstruction(child); + default: + return this.element(child, 0); + } + }).call(this); + } + if (this.pretty && r.slice(-this.newline.length) === this.newline) { + r = r.slice(0, -this.newline.length); + } + return r; + }; -SafeBuffer.allocUnsafeSlow = function (size) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - return buffer.SlowBuffer(size) -} + XMLStringWriter.prototype.attribute = function(att) { + return ' ' + att.name + '="' + att.value + '"'; + }; -},{"buffer":5}],106:[function(_dereq_,module,exports){ -(function (Buffer){ -;(function (sax) { // wrapper for non-node envs - sax.parser = function (strict, opt) { return new SAXParser(strict, opt) } - sax.SAXParser = SAXParser - sax.SAXStream = SAXStream - sax.createStream = createStream + XMLStringWriter.prototype.cdata = function(node, level) { + return this.space(level) + '' + this.newline; + }; - // When we pass the MAX_BUFFER_LENGTH position, start checking for buffer overruns. - // When we check, schedule the next check for MAX_BUFFER_LENGTH - (max(buffer lengths)), - // since that's the earliest that a buffer overrun could occur. This way, checks are - // as rare as required, but as often as necessary to ensure never crossing this bound. - // Furthermore, buffers are only tested at most once per write(), so passing a very - // large string into write() might have undesirable effects, but this is manageable by - // the caller, so it is assumed to be safe. Thus, a call to write() may, in the extreme - // edge case, result in creating at most one complete copy of the string passed in. - // Set to Infinity to have unlimited buffers. - sax.MAX_BUFFER_LENGTH = 64 * 1024 - - var buffers = [ - 'comment', 'sgmlDecl', 'textNode', 'tagName', 'doctype', - 'procInstName', 'procInstBody', 'entity', 'attribName', - 'attribValue', 'cdata', 'script' - ] + XMLStringWriter.prototype.comment = function(node, level) { + return this.space(level) + '' + this.newline; + }; - sax.EVENTS = [ - 'text', - 'processinginstruction', - 'sgmldeclaration', - 'doctype', - 'comment', - 'opentagstart', - 'attribute', - 'opentag', - 'closetag', - 'opencdata', - 'cdata', - 'closecdata', - 'error', - 'end', - 'ready', - 'script', - 'opennamespace', - 'closenamespace' - ] + XMLStringWriter.prototype.declaration = function(node, level) { + var r; + r = this.space(level); + r += ''; + r += this.newline; + return r; + }; - function SAXParser (strict, opt) { - if (!(this instanceof SAXParser)) { - return new SAXParser(strict, opt) - } + XMLStringWriter.prototype.docType = function(node, level) { + var child, i, len, r, ref; + level || (level = 0); + r = this.space(level); + r += ' 0) { + r += ' ['; + r += this.newline; + ref = node.children; + for (i = 0, len = ref.length; i < len; i++) { + child = ref[i]; + r += (function() { + switch (false) { + case !(child instanceof XMLDTDAttList): + return this.dtdAttList(child, level + 1); + case !(child instanceof XMLDTDElement): + return this.dtdElement(child, level + 1); + case !(child instanceof XMLDTDEntity): + return this.dtdEntity(child, level + 1); + case !(child instanceof XMLDTDNotation): + return this.dtdNotation(child, level + 1); + case !(child instanceof XMLCData): + return this.cdata(child, level + 1); + case !(child instanceof XMLComment): + return this.comment(child, level + 1); + case !(child instanceof XMLProcessingInstruction): + return this.processingInstruction(child, level + 1); + default: + throw new Error("Unknown DTD node type: " + child.constructor.name); + } + }).call(this); + } + r += ']'; + } + r += this.spacebeforeslash + '>'; + r += this.newline; + return r; + }; - var parser = this - clearBuffers(parser) - parser.q = parser.c = '' - parser.bufferCheckPosition = sax.MAX_BUFFER_LENGTH - parser.opt = opt || {} - parser.opt.lowercase = parser.opt.lowercase || parser.opt.lowercasetags - parser.looseCase = parser.opt.lowercase ? 'toLowerCase' : 'toUpperCase' - parser.tags = [] - parser.closed = parser.closedRoot = parser.sawRoot = false - parser.tag = parser.error = null - parser.strict = !!strict - parser.noscript = !!(strict || parser.opt.noscript) - parser.state = S.BEGIN - parser.strictEntities = parser.opt.strictEntities - parser.ENTITIES = parser.strictEntities ? Object.create(sax.XML_ENTITIES) : Object.create(sax.ENTITIES) - parser.attribList = [] + XMLStringWriter.prototype.element = function(node, level) { + var att, child, i, j, len, len1, name, r, ref, ref1, ref2, space, textispresentwasset; + level || (level = 0); + textispresentwasset = false; + if (this.textispresent) { + this.newline = ''; + this.pretty = false; + } else { + this.newline = this.newlinedefault; + this.pretty = this.prettydefault; + } + space = this.space(level); + r = ''; + r += space + '<' + node.name; + ref = node.attributes; + for (name in ref) { + if (!hasProp.call(ref, name)) continue; + att = ref[name]; + r += this.attribute(att); + } + if (node.children.length === 0 || node.children.every(function(e) { + return e.value === ''; + })) { + if (this.allowEmpty) { + r += '>' + this.newline; + } else { + r += this.spacebeforeslash + '/>' + this.newline; + } + } else if (this.pretty && node.children.length === 1 && (node.children[0].value != null)) { + r += '>'; + r += node.children[0].value; + r += '' + this.newline; + } else { + if (this.dontprettytextnodes) { + ref1 = node.children; + for (i = 0, len = ref1.length; i < len; i++) { + child = ref1[i]; + if (child.value != null) { + this.textispresent++; + textispresentwasset = true; + break; + } + } + } + if (this.textispresent) { + this.newline = ''; + this.pretty = false; + space = this.space(level); + } + r += '>' + this.newline; + ref2 = node.children; + for (j = 0, len1 = ref2.length; j < len1; j++) { + child = ref2[j]; + r += (function() { + switch (false) { + case !(child instanceof XMLCData): + return this.cdata(child, level + 1); + case !(child instanceof XMLComment): + return this.comment(child, level + 1); + case !(child instanceof XMLElement): + return this.element(child, level + 1); + case !(child instanceof XMLRaw): + return this.raw(child, level + 1); + case !(child instanceof XMLText): + return this.text(child, level + 1); + case !(child instanceof XMLProcessingInstruction): + return this.processingInstruction(child, level + 1); + default: + throw new Error("Unknown XML node type: " + child.constructor.name); + } + }).call(this); + } + if (textispresentwasset) { + this.textispresent--; + } + if (!this.textispresent) { + this.newline = this.newlinedefault; + this.pretty = this.prettydefault; + } + r += space + '' + this.newline; + } + return r; + }; - // namespaces form a prototype chain. - // it always points at the current tag, - // which protos to its parent tag. - if (parser.opt.xmlns) { - parser.ns = Object.create(rootNS) - } + XMLStringWriter.prototype.processingInstruction = function(node, level) { + var r; + r = this.space(level) + '' + this.newline; + return r; + }; - // mostly just for error reporting - parser.trackPosition = parser.opt.position !== false - if (parser.trackPosition) { - parser.position = parser.line = parser.column = 0 - } - emit(parser, 'onready') - } + XMLStringWriter.prototype.raw = function(node, level) { + return this.space(level) + node.value + this.newline; + }; - if (!Object.create) { - Object.create = function (o) { - function F () {} - F.prototype = o - var newf = new F() - return newf - } - } + XMLStringWriter.prototype.text = function(node, level) { + return this.space(level) + node.value + this.newline; + }; - if (!Object.keys) { - Object.keys = function (o) { - var a = [] - for (var i in o) if (o.hasOwnProperty(i)) a.push(i) - return a - } - } + XMLStringWriter.prototype.dtdAttList = function(node, level) { + var r; + r = this.space(level) + '' + this.newline; + return r; + }; - function checkBufferLength (parser) { - var maxAllowed = Math.max(sax.MAX_BUFFER_LENGTH, 10) - var maxActual = 0 - for (var i = 0, l = buffers.length; i < l; i++) { - var len = parser[buffers[i]].length - if (len > maxAllowed) { - // Text/cdata nodes can get big, and since they're buffered, - // we can get here under normal conditions. - // Avoid issues by emitting the text node now, - // so at least it won't get any bigger. - switch (buffers[i]) { - case 'textNode': - closeText(parser) - break + XMLStringWriter.prototype.dtdElement = function(node, level) { + return this.space(level) + '' + this.newline; + }; - case 'cdata': - emitNode(parser, 'oncdata', parser.cdata) - parser.cdata = '' - break + XMLStringWriter.prototype.dtdEntity = function(node, level) { + var r; + r = this.space(level) + '' + this.newline; + return r; + }; - case 'script': - emitNode(parser, 'onscript', parser.script) - parser.script = '' - break + XMLStringWriter.prototype.dtdNotation = function(node, level) { + var r; + r = this.space(level) + '' + this.newline; + return r; + }; - default: - error(parser, 'Max buffer length exceeded: ' + buffers[i]) + XMLStringWriter.prototype.openNode = function(node, level) { + var att, name, r, ref; + level || (level = 0); + if (node instanceof XMLElement) { + r = this.space(level) + '<' + node.name; + ref = node.attributes; + for (name in ref) { + if (!hasProp.call(ref, name)) continue; + att = ref[name]; + r += this.attribute(att); + } + r += (node.children ? '>' : '/>') + this.newline; + return r; + } else { + r = this.space(level) + '') + this.newline; + return r; } - maxActual = Math.max(maxActual, len) - } - // schedule the next check for the earliest possible buffer overrun. - var m = sax.MAX_BUFFER_LENGTH - maxActual - parser.bufferCheckPosition = m + parser.position - } - - function clearBuffers (parser) { - for (var i = 0, l = buffers.length; i < l; i++) { - parser[buffers[i]] = '' - } - } + }; - function flushBuffers (parser) { - closeText(parser) - if (parser.cdata !== '') { - emitNode(parser, 'oncdata', parser.cdata) - parser.cdata = '' - } - if (parser.script !== '') { - emitNode(parser, 'onscript', parser.script) - parser.script = '' - } - } + XMLStringWriter.prototype.closeNode = function(node, level) { + level || (level = 0); + switch (false) { + case !(node instanceof XMLElement): + return this.space(level) + '' + this.newline; + case !(node instanceof XMLDocType): + return this.space(level) + ']>' + this.newline; + } + }; - SAXParser.prototype = { - end: function () { end(this) }, - write: write, - resume: function () { this.error = null; return this }, - close: function () { return this.write(null) }, - flush: function () { flushBuffers(this) } - } + return XMLStringWriter; - var Stream - try { - Stream = _dereq_('stream').Stream - } catch (ex) { - Stream = function () {} - } + })(XMLWriterBase); - var streamWraps = sax.EVENTS.filter(function (ev) { - return ev !== 'error' && ev !== 'end' - }) +}).call(this); - function createStream (strict, opt) { - return new SAXStream(strict, opt) - } +},{"./XMLCData":107,"./XMLComment":108,"./XMLDTDAttList":109,"./XMLDTDElement":110,"./XMLDTDEntity":111,"./XMLDTDNotation":112,"./XMLDeclaration":113,"./XMLDocType":114,"./XMLElement":117,"./XMLProcessingInstruction":119,"./XMLRaw":120,"./XMLText":124,"./XMLWriterBase":125}],123:[function(_dereq_,module,exports){ +// Generated by CoffeeScript 1.12.7 +(function() { + var XMLStringifier, + bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + hasProp = {}.hasOwnProperty; - function SAXStream (strict, opt) { - if (!(this instanceof SAXStream)) { - return new SAXStream(strict, opt) + module.exports = XMLStringifier = (function() { + function XMLStringifier(options) { + this.assertLegalChar = bind(this.assertLegalChar, this); + var key, ref, value; + options || (options = {}); + this.noDoubleEncoding = options.noDoubleEncoding; + ref = options.stringify || {}; + for (key in ref) { + if (!hasProp.call(ref, key)) continue; + value = ref[key]; + this[key] = value; + } } - Stream.apply(this) + XMLStringifier.prototype.eleName = function(val) { + val = '' + val || ''; + return this.assertLegalChar(val); + }; - this._parser = new SAXParser(strict, opt) - this.writable = true - this.readable = true + XMLStringifier.prototype.eleText = function(val) { + val = '' + val || ''; + return this.assertLegalChar(this.elEscape(val)); + }; - var me = this + XMLStringifier.prototype.cdata = function(val) { + val = '' + val || ''; + val = val.replace(']]>', ']]]]>'); + return this.assertLegalChar(val); + }; - this._parser.onend = function () { - me.emit('end') - } + XMLStringifier.prototype.comment = function(val) { + val = '' + val || ''; + if (val.match(/--/)) { + throw new Error("Comment text cannot contain double-hypen: " + val); + } + return this.assertLegalChar(val); + }; - this._parser.onerror = function (er) { - me.emit('error', er) + XMLStringifier.prototype.raw = function(val) { + return '' + val || ''; + }; - // if didn't throw, then means error was handled. - // go ahead and clear error, so we can write again. - me._parser.error = null - } + XMLStringifier.prototype.attName = function(val) { + return val = '' + val || ''; + }; - this._decoder = null + XMLStringifier.prototype.attValue = function(val) { + val = '' + val || ''; + return this.attEscape(val); + }; - streamWraps.forEach(function (ev) { - Object.defineProperty(me, 'on' + ev, { - get: function () { - return me._parser['on' + ev] - }, - set: function (h) { - if (!h) { - me.removeAllListeners(ev) - me._parser['on' + ev] = h - return h - } - me.on(ev, h) - }, - enumerable: true, - configurable: false - }) - }) - } + XMLStringifier.prototype.insTarget = function(val) { + return '' + val || ''; + }; - SAXStream.prototype = Object.create(Stream.prototype, { - constructor: { - value: SAXStream - } - }) + XMLStringifier.prototype.insValue = function(val) { + val = '' + val || ''; + if (val.match(/\?>/)) { + throw new Error("Invalid processing instruction value: " + val); + } + return val; + }; - SAXStream.prototype.write = function (data) { - if (typeof Buffer === 'function' && - typeof Buffer.isBuffer === 'function' && - Buffer.isBuffer(data)) { - if (!this._decoder) { - var SD = _dereq_('string_decoder').StringDecoder - this._decoder = new SD('utf8') + XMLStringifier.prototype.xmlVersion = function(val) { + val = '' + val || ''; + if (!val.match(/1\.[0-9]+/)) { + throw new Error("Invalid version number: " + val); } - data = this._decoder.write(data) - } + return val; + }; - this._parser.write(data.toString()) - this.emit('data', data) - return true - } + XMLStringifier.prototype.xmlEncoding = function(val) { + val = '' + val || ''; + if (!val.match(/^[A-Za-z](?:[A-Za-z0-9._-])*$/)) { + throw new Error("Invalid encoding: " + val); + } + return val; + }; - SAXStream.prototype.end = function (chunk) { - if (chunk && chunk.length) { - this.write(chunk) - } - this._parser.end() - return true - } - - SAXStream.prototype.on = function (ev, handler) { - var me = this - if (!me._parser['on' + ev] && streamWraps.indexOf(ev) !== -1) { - me._parser['on' + ev] = function () { - var args = arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments) - args.splice(0, 0, ev) - me.emit.apply(me, args) + XMLStringifier.prototype.xmlStandalone = function(val) { + if (val) { + return "yes"; + } else { + return "no"; } - } + }; - return Stream.prototype.on.call(me, ev, handler) - } + XMLStringifier.prototype.dtdPubID = function(val) { + return '' + val || ''; + }; - // this really needs to be replaced with character classes. - // XML allows all manner of ridiculous numbers and digits. - var CDATA = '[CDATA[' - var DOCTYPE = 'DOCTYPE' - var XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace' - var XMLNS_NAMESPACE = 'http://www.w3.org/2000/xmlns/' - var rootNS = { xml: XML_NAMESPACE, xmlns: XMLNS_NAMESPACE } + XMLStringifier.prototype.dtdSysID = function(val) { + return '' + val || ''; + }; - // http://www.w3.org/TR/REC-xml/#NT-NameStartChar - // This implementation works on strings, a single character at a time - // as such, it cannot ever support astral-plane characters (10000-EFFFF) - // without a significant breaking change to either this parser, or the - // JavaScript language. Implementation of an emoji-capable xml parser - // is left as an exercise for the reader. - var nameStart = /[:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/ + XMLStringifier.prototype.dtdElementValue = function(val) { + return '' + val || ''; + }; - var nameBody = /[:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u00B7\u0300-\u036F\u203F-\u2040.\d-]/ + XMLStringifier.prototype.dtdAttType = function(val) { + return '' + val || ''; + }; - var entityStart = /[#:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/ - var entityBody = /[#:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u00B7\u0300-\u036F\u203F-\u2040.\d-]/ + XMLStringifier.prototype.dtdAttDefault = function(val) { + if (val != null) { + return '' + val || ''; + } else { + return val; + } + }; - function isWhitespace (c) { - return c === ' ' || c === '\n' || c === '\r' || c === '\t' - } + XMLStringifier.prototype.dtdEntityValue = function(val) { + return '' + val || ''; + }; - function isQuote (c) { - return c === '"' || c === '\'' - } + XMLStringifier.prototype.dtdNData = function(val) { + return '' + val || ''; + }; - function isAttribEnd (c) { - return c === '>' || isWhitespace(c) - } + XMLStringifier.prototype.convertAttKey = '@'; - function isMatch (regex, c) { - return regex.test(c) - } + XMLStringifier.prototype.convertPIKey = '?'; - function notMatch (regex, c) { - return !isMatch(regex, c) - } + XMLStringifier.prototype.convertTextKey = '#text'; - var S = 0 - sax.STATE = { - BEGIN: S++, // leading byte order mark or whitespace - BEGIN_WHITESPACE: S++, // leading whitespace - TEXT: S++, // general stuff - TEXT_ENTITY: S++, // & and such. - OPEN_WAKA: S++, // < - SGML_DECL: S++, // - SCRIPT: S++, //