diff --git a/sbgnviz.js b/sbgnviz.js index 819d923..fa7f98f 100644 --- a/sbgnviz.js +++ b/sbgnviz.js @@ -4848,6 +4848,7 @@ https.request = function (params, cb) { } },{"http":111}],14:[function(_dereq_,module,exports){ +/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ exports.read = function (buffer, offset, isLE, mLen, nBytes) { var e, m var eLen = (nBytes * 8) - mLen - 1 @@ -13192,12 +13193,12 @@ return factory; })(); -}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},"/node_modules\\jsonld\\js") +}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},"/node_modules/jsonld/js") },{"_process":47,"crypto":18,"es6-promise":10,"http":18,"jsonld-request":18,"pkginfo":18,"request":18,"util":18,"xmldom":18}],20:[function(_dereq_,module,exports){ -var pkg = _dereq_('./src/libsbgn'); - -module.exports = pkg; +var pkg = _dereq_('./src/libsbgn'); + +module.exports = pkg; },{"./src/libsbgn":31}],21:[function(_dereq_,module,exports){ // Generated by CoffeeScript 1.12.7 @@ -13851,3759 +13852,3776 @@ module.exports = pkg; }).call(this); },{"./builder":22,"./defaults":23,"./parser":24,"./processors":25}],27:[function(_dereq_,module,exports){ -var ns = {}; -var Issue = function Issues() { - this.text = {}; - this.pattern = {}; - this.role = {}; -} -Issue.prototype.setText = function(text) { - this.text = text; -}; -Issue.prototype.getText = function() { - return this.text; -}; -Issue.prototype.setPattern = function(pattern) { - this.pattern = pattern; -}; -Issue.prototype.getPattern = function() { - return this.pattern; -}; -Issue.prototype.setRole = function(role) { - this.role = role; -}; -Issue.prototype.getPattern = function() { - return this.role; -}; -ns.Issue = Issue; -module.exports = ns; +var ns = {}; +var Issue = function Issues() { + this.text = {}; + this.pattern = {}; + this.role = {}; +} +Issue.prototype.setText = function(text) { + this.text = text; +}; +Issue.prototype.getText = function() { + return this.text; +}; +Issue.prototype.setPattern = function(pattern) { + this.pattern = pattern; +}; +Issue.prototype.getPattern = function() { + return this.pattern; +}; +Issue.prototype.setRole = function(role) { + this.role = role; +}; +Issue.prototype.getPattern = function() { + return this.role; +}; +ns.Issue = Issue; +module.exports = ns; },{}],28:[function(_dereq_,module,exports){ -var N3 = _dereq_('n3'); - -var ns = {}; - -ns.prefixes = { rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - bqmodel: "http://biomodels.net/model-qualifiers/", - bqbiol: "http://biomodels.net/biology-qualifiers/", - sio: "http://semanticscience.org/resource/", - eisbm: "http://www.eisbm.org/"}; - -// pure shortcut function -ns.expandPrefix = function(prefix) { - return N3.Util.expandPrefixedName(prefix, ns.prefixes) -}; - -// 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); - -controlledVocabularyList = [ - "bqmodel:is", - "bqmodel:isDerivedFrom", - "bqmodel:isDescribedBy", - "bqmodel:isInstanceOf", - "bqmodel:hasInstance", - - "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", - - "sio:SIO_000223" -]; - -ns.isControlledVocabulary = {}; -for(var i=0; i 0; -}; - -ns.countBagElements = function(graph, subject) { - return graph.countTriples(subject, null, null) - 1; -}; - -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 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; -}; - -/** - * 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)]; - } -}; - -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; -}; - -/** - * 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]); - } -}; - +var N3 = _dereq_('n3'); + +var ns = {}; + +ns.prefixes = { rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + bqmodel: "http://biomodels.net/model-qualifiers/", + bqbiol: "http://biomodels.net/biology-qualifiers/", + sio: "http://semanticscience.org/resource/", + eisbm: "http://www.eisbm.org/"}; + +// pure shortcut function +ns.expandPrefix = function(prefix) { + return N3.Util.expandPrefixedName(prefix, ns.prefixes) +}; + +// 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); + +controlledVocabularyList = [ + "bqmodel:is", + "bqmodel:isDerivedFrom", + "bqmodel:isDescribedBy", + "bqmodel:isInstanceOf", + "bqmodel:hasInstance", + + "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", + + "sio:SIO_000223" +]; + +ns.isControlledVocabulary = {}; +for(var i=0; i 0; +}; + +ns.countBagElements = function(graph, subject) { + return graph.countTriples(subject, null, null) - 1; +}; + +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 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; +}; + +/** + * 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)]; + } +}; + +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; +}; + +/** + * 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]); + } +}; + module.exports = ns; },{"n3":34}],29:[function(_dereq_,module,exports){ -/** - * This submodule manages the annotations extension. It adds the ability to save semantic data into - * SBGN-ML in the form of RDF elements. Any SBGN element that can host an extension tag can also - * get RDF annotations. This means that almost every element can get annotated. - * - * The annotations here are intended to be used in two ways: - * - with controlled vocabulary and resources, as suggested by COMBINE, with the help of MIRIAM - * identifiers. - * - as a mean to attach arbitrary data in the form of key-value properties. - * - * # Controlled vocabulary - * - * The formal way of using annotations is to use specific vocabulary with specific identifiers to - * provide additional information that can not be conveyed otherwise through the SBGN format. - * See --link to combine qualifiers-- and --link to identifiers.org and MIRIAM--- - * This was also based on the annotation extension of SBML --link to annotation proposal for SBML-- - * - * - * See {@link Extension} for more general information on extensions in the SBGN-ML format. - * - * You can access the following classes like this: libsbgn.annot.Annotation - * - * @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 - */ -Annotation.prototype.setRdfElement = function(rdfElement) { - this.rdfElement = rdfElement; -}; - -Annotation.prototype.buildJsObj = function () { - var annotationJsonObj = {}; - - if(this.rdfElement != null) { - annotationJsonObj = this.rdfElement.buildJsObj(); - } - - return annotationJsonObj; -}; - -/** - * @return {string} - */ -Annotation.prototype.toXML = function() { - return utils.buildString({annotation: this.buildJsObj()}) -}; - -Annotation.fromXML = function (string) { - var annotation; - function fn (err, result) { - annotation = Annotation.fromObj(result); - }; - utils.parseStringKeepPrefix(string, fn); - return annotation; -}; - -Annotation.fromObj = function (jsObj) { - if (typeof jsObj.annotation == 'undefined') { - throw new Error("Bad XML provided, expected tagName annotation, got: " + Object.keys(jsObj)[0]); - } - - var annotation = new ns.Annotation(); - jsObj = jsObj.annotation; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return annotation; - } - - // children - if(jsObj['rdf:RDF']) { - var obj = {}; - obj['rdf:RDF'] = jsObj['rdf:RDF'][0]; - var rdf = ns.RdfElement.fromObj(obj); - annotation.setRdfElement(rdf); - } - - return annotation; -}; - -ns.Annotation = Annotation; -// ------- END ANNOTATION ------- - -// ------- STOREOBJECT ------- -var StoreObject = function (params) { - var params = checkParams(params, ['store']); - if (params.store) { - this.store = params.store; - } - else { - var store = N3.Store(); - store.addPrefixes(Util.prefixes); - this.store = store; - } -}; - -StoreObject.prototype.getCustomPropertiesOfId = function (id) { - return Util.getCustomPropertiesOfId(this.store, id); -}; - -StoreObject.prototype.getAllIds = function () { - return Util.getAllIds(this.store); -}; - -StoreObject.prototype.addCustomProperty = function (id, kvObject) { - return Util.addCustomProperty(this.store, id, kvObject); -}; - -StoreObject.prototype.getResourcesOfId = function(id) { - return Util.getResourcesOfId(this.store, id); -}; - -StoreObject.prototype.addResource = function (id, kvObject) { - return Util.addResource(this.store, id, kvObject); -}; - -ns.StoreObject = StoreObject; -// ------- END STOREOBJECT ------- - -// ------- GLOBALSTORE ------- -var GlobalRdfStore = function (params) { - ns.StoreObject.call(this, params); -}; -GlobalRdfStore.prototype = Object.create(ns.StoreObject.prototype); -GlobalRdfStore.prototype.constructor = GlobalRdfStore; - -GlobalRdfStore.prototype.load = function (annotations) { - for(var i=0; i<rd:RDFf> element. - * @class - */ -var RdfElement = function (params) { - ns.StoreObject.call(this, params); -}; -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; - } - - function replaceParseType(string) { - var regexp = / rdf:parseType="Resource"/g; - return string.replace(regexp, ''); - } - - function replaceSlashInID(string) { - return string.replace(new RegExp(/rdf:about="\//g), 'rdf:about="'); - } - - var result = replaceSlashInID(replaceParseType(replaceLi(replaceBag(serialize)))); - - return result; -}; - -/** - * @param {Element} xml - * @return {RdfElement} - */ -RdfElement.fromString = function (stringXml) { - - var rdfElement = new RdfElement(); - var graph = $rdf.graph(); - - // rdflib only accepts string as input, not xml elements - try { - $rdf.parse(stringXml, graph, RdfElement.uri, 'application/rdf+xml'); - } catch (err) { - console.log(err); - } - - // convert to turtle to feed to N3 - var turtle = $rdf.serialize($rdf.sym(RdfElement.uri), graph, undefined, 'text/turtle'); - - var parser = N3.Parser(); - var store = N3.Store(); - store.addPrefixes(Util.prefixes); - store.addTriples(parser.parse(turtle)); - - rdfElement.store = store; - - return rdfElement; -}; - -RdfElement.fromXML = function (string) { - var rdfElement; - function fn (err, result) { - rdfElement = RdfElement.fromObj(result); - }; - utils.parseStringKeepPrefix(string, fn); - return rdfElement; -}; - -RdfElement.prototype.buildJsObj = function () { - var rdfElementJsObj; - function fn (err, result) { - rdfElementJsObj = result; - }; - utils.parseStringKeepPrefix(this.toXML(), fn); - return rdfElementJsObj; -}; - -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]); - } - - var rdfElement = new ns.RdfElement(); - jsObj = jsObj['rdf:RDF']; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return rdfElement; - } - - var obj = {}; - obj['rdf:RDF'] = jsObj; - rdfElement = ns.RdfElement.fromString(utils.buildString(obj)); - - return rdfElement; -}; - -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()); -}; - -ns.RdfElement = RdfElement; -// ------- END RDFELEMENT ------- - - -ns.rdflib = $rdf; -ns.Util = Util; - -module.exports = ns; +/** + * This submodule manages the annotations extension. It adds the ability to save semantic data into + * SBGN-ML in the form of RDF elements. Any SBGN element that can host an extension tag can also + * get RDF annotations. This means that almost every element can get annotated. + * + * The annotations here are intended to be used in two ways: + * - with controlled vocabulary and resources, as suggested by COMBINE, with the help of MIRIAM + * identifiers. + * - as a mean to attach arbitrary data in the form of key-value properties. + * + * # Controlled vocabulary + * + * The formal way of using annotations is to use specific vocabulary with specific identifiers to + * provide additional information that can not be conveyed otherwise through the SBGN format. + * See --link to combine qualifiers-- and --link to identifiers.org and MIRIAM--- + * This was also based on the annotation extension of SBML --link to annotation proposal for SBML-- + * + * + * See {@link Extension} for more general information on extensions in the SBGN-ML format. + * + * You can access the following classes like this: libsbgn.annot.Annotation + * + * @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 + */ +Annotation.prototype.setRdfElement = function(rdfElement) { + this.rdfElement = rdfElement; +}; + +Annotation.prototype.buildJsObj = function () { + var annotationJsonObj = {}; + + if(this.rdfElement != null) { + annotationJsonObj = this.rdfElement.buildJsObj(); + } + + return annotationJsonObj; +}; + +/** + * @return {string} + */ +Annotation.prototype.toXML = function() { + return utils.buildString({annotation: this.buildJsObj()}) +}; + +Annotation.fromXML = function (string) { + var annotation; + function fn (err, result) { + annotation = Annotation.fromObj(result); + }; + utils.parseStringKeepPrefix(string, fn); + return annotation; +}; + +Annotation.fromObj = function (jsObj) { + if (typeof jsObj.annotation == 'undefined') { + throw new Error("Bad XML provided, expected tagName annotation, got: " + Object.keys(jsObj)[0]); + } + + var annotation = new ns.Annotation(); + jsObj = jsObj.annotation; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return annotation; + } + + // children + if(jsObj['rdf:RDF']) { + var obj = {}; + obj['rdf:RDF'] = jsObj['rdf:RDF'][0]; + var rdf = ns.RdfElement.fromObj(obj); + annotation.setRdfElement(rdf); + } + + return annotation; +}; + +ns.Annotation = Annotation; +// ------- END ANNOTATION ------- + +// ------- STOREOBJECT ------- +var StoreObject = function (params) { + var params = checkParams(params, ['store']); + if (params.store) { + this.store = params.store; + } + else { + var store = N3.Store(); + store.addPrefixes(Util.prefixes); + this.store = store; + } +}; + +StoreObject.prototype.getCustomPropertiesOfId = function (id) { + return Util.getCustomPropertiesOfId(this.store, id); +}; + +StoreObject.prototype.getAllIds = function () { + return Util.getAllIds(this.store); +}; + +StoreObject.prototype.addCustomProperty = function (id, kvObject) { + return Util.addCustomProperty(this.store, id, kvObject); +}; + +StoreObject.prototype.getResourcesOfId = function(id) { + return Util.getResourcesOfId(this.store, id); +}; + +StoreObject.prototype.addResource = function (id, kvObject) { + return Util.addResource(this.store, id, kvObject); +}; + +ns.StoreObject = StoreObject; +// ------- END STOREOBJECT ------- + +// ------- GLOBALSTORE ------- +var GlobalRdfStore = function (params) { + ns.StoreObject.call(this, params); +}; +GlobalRdfStore.prototype = Object.create(ns.StoreObject.prototype); +GlobalRdfStore.prototype.constructor = GlobalRdfStore; + +GlobalRdfStore.prototype.load = function (annotations) { + for(var i=0; i<rd:RDFf> element. + * @class + */ +var RdfElement = function (params) { + ns.StoreObject.call(this, params); +}; +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; + } + + function replaceParseType(string) { + var regexp = / rdf:parseType="Resource"/g; + return string.replace(regexp, ''); + } + + function replaceSlashInID(string) { + return string.replace(new RegExp(/rdf:about="\//g), 'rdf:about="'); + } + + var result = replaceSlashInID(replaceParseType(replaceLi(replaceBag(serialize)))); + + return result; +}; + +/** + * @param {Element} xml + * @return {RdfElement} + */ +RdfElement.fromString = function (stringXml) { + + var rdfElement = new RdfElement(); + var graph = $rdf.graph(); + + // rdflib only accepts string as input, not xml elements + try { + $rdf.parse(stringXml, graph, RdfElement.uri, 'application/rdf+xml'); + } catch (err) { + console.log(err); + } + + // convert to turtle to feed to N3 + var turtle = $rdf.serialize($rdf.sym(RdfElement.uri), graph, undefined, 'text/turtle'); + + var parser = N3.Parser(); + var store = N3.Store(); + store.addPrefixes(Util.prefixes); + store.addTriples(parser.parse(turtle)); + + rdfElement.store = store; + + return rdfElement; +}; + +RdfElement.fromXML = function (string) { + var rdfElement; + function fn (err, result) { + rdfElement = RdfElement.fromObj(result); + }; + utils.parseStringKeepPrefix(string, fn); + return rdfElement; +}; + +RdfElement.prototype.buildJsObj = function () { + var rdfElementJsObj; + function fn (err, result) { + rdfElementJsObj = result; + }; + utils.parseStringKeepPrefix(this.toXML(), fn); + return rdfElementJsObj; +}; + +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]); + } + + var rdfElement = new ns.RdfElement(); + jsObj = jsObj['rdf:RDF']; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return rdfElement; + } + + var obj = {}; + obj['rdf:RDF'] = jsObj; + rdfElement = ns.RdfElement.fromString(utils.buildString(obj)); + + return rdfElement; +}; + +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()); +}; + +ns.RdfElement = RdfElement; +// ------- END RDFELEMENT ------- + + +ns.rdflib = $rdf; +ns.Util = Util; + +module.exports = ns; + +},{"./annotation-utils":28,"./utilities":33,"n3":34,"rdflib":60}],30:[function(_dereq_,module,exports){ +/** + * This submodule contains the classes to manage the render extension's xml and some utility functions. + * It adds the ability to save the styles and colors used in an SBGN map, as features like background-color, + * border thickness or font properties are not part of the SBGN standard. + * + * 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 + * + * @module libsbgn-render + * @namespace libsbgn.render +*/ + +var utils = _dereq_('./utilities'); +var checkParams = utils.checkParams; +var xml2js = _dereq_('xml2js'); + +var ns = {}; + +ns.xmlns = "http://www.sbml.org/sbml/level3/version1/render/version1"; + +// ------- COLORDEFINITION ------- +/** + * Represents the <colorDefinition> element. + * @class + * @param {Object} params + * @param {string=} params.id + * @param {string=} params.value + */ +var ColorDefinition = function(params) { + var params = checkParams(params, ['id', 'value']); + this.id = params.id; + this.value = params.value; +}; + +/** + * @return {Object} - xml2js formatted object + */ +ColorDefinition.prototype.buildJsObj = function () { + var colordefObj = {}; + + // attributes + var attributes = {}; + if(this.id != null) { + attributes.id = this.id; + } + if(this.value != null) { + attributes.value = this.value; + } + utils.addAttributes(colordefObj, attributes); + return colordefObj; +}; + +/** + * @return {string} + */ +ColorDefinition.prototype.toXML = function () { + return utils.buildString({colorDefinition: this.buildJsObj()}) +}; + +/** + * @param {String} string + * @return {ColorDefinition} + */ +ColorDefinition.fromXML = function (string) { + var colorDefinition; + function fn (err, result) { + colorDefinition = ColorDefinition.fromObj(result); + }; + utils.parseString(string, fn); + return colorDefinition; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {ColorDefinition} + */ +ColorDefinition.fromObj = function (jsObj) { + var colorDefinitionNode = utils.getChildByNameIgnoringNamespace(jsObj, "colorDefinition"); + + if (colorDefinitionNode === null) { + throw new Error("Bad XML provided, expected tagName colorDefinition, got: " + Object.keys(jsObj)[0]); + } + + var colorDefinition = new ns.ColorDefinition(); + jsObj = colorDefinitionNode; + if (typeof jsObj != 'object') { // nothing inside, empty xml + return colorDefinition; + } + + if (jsObj.$) { // we have some attributes + var attributes = jsObj.$; + colorDefinition.id = utils.getChildByNameIgnoringNamespace(attributes, "id");; + colorDefinition.value = utils.getChildByNameIgnoringNamespace(attributes, "value"); + } + return colorDefinition; +}; + +ns.ColorDefinition = ColorDefinition; +// ------- END COLORDEFINITION ------- + +// ------- LISTOFCOLORDEFINITIONS ------- +/** + * Represents the <listOfColorDefinitions> element. + * @class + */ +var ListOfColorDefinitions = function () { + this.colorDefinitions = []; + this.colorIndex = {}; +}; + +/** + * @param {ColorDefinition} colorDefinition + */ +ListOfColorDefinitions.prototype.addColorDefinition = function (colorDefinition) { + this.colorDefinitions.push(colorDefinition); + this.colorIndex[colorDefinition.id] = colorDefinition.value; +}; + +/** + * Convenient method to get a color value directly. + * @param {string} id + * @return {string} + */ +ListOfColorDefinitions.prototype.getColorById = function (id) { + return this.colorIndex[id]; +}; + +/** + * Convenient method to get all the color values in the list. + * @return {string[]} + */ +ListOfColorDefinitions.prototype.getAllColors = function () { + return Object.values(this.colorIndex); +}; + +/** + * @return {Object} - xml2js formatted object + */ +ListOfColorDefinitions.prototype.buildJsObj = function () { + var listOfColorDefinitionsObj = {}; + + for(var i=0; i < this.colorDefinitions.length; i++) { + if (i==0) { + listOfColorDefinitionsObj.colorDefinition = []; + } + listOfColorDefinitionsObj.colorDefinition.push(this.colorDefinitions[i].buildJsObj()); + } + + return listOfColorDefinitionsObj; +}; + +/** + * @return {string} + */ +ListOfColorDefinitions.prototype.toXML = function () { + return utils.buildString({listOfColorDefinitions: this.buildJsObj()}) +}; + +/** + * @param {String} string + * @return {ListOfColorDefinitions} + */ +ListOfColorDefinitions.fromXML = function (string) { + var listOfColorDefinitions; + function fn (err, result) { + listOfColorDefinitions = ListOfColorDefinitions.fromObj(result); + }; + utils.parseString(string, fn); + return listOfColorDefinitions; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {ListOfColorDefinitions} + */ +ListOfColorDefinitions.fromObj = function (jsObj) { + var listOfColorDefinitionsNode = utils.getChildByNameIgnoringNamespace(jsObj, "listOfColorDefinitions"); + if (listOfColorDefinitionsNode === null) { + throw new Error("Bad XML provided, expected tagName listOfColorDefinitions, got: " + Object.keys(jsObj)[0]); + } + + var listOfColorDefinitions = new ns.ListOfColorDefinitions(); + jsObj = listOfColorDefinitionsNode; + if (typeof jsObj != 'object') { // nothing inside, empty xml + return listOfColorDefinitions; + } + + // children + var colorDefinitionNode = utils.getChildByNameIgnoringNamespace(jsObj, "colorDefinition"); + if (colorDefinitionNode) { + var colorDefinitions = colorDefinitionNode; + for (var i=0; i < colorDefinitions.length; i++) { + var colorDefinition = ns.ColorDefinition.fromObj({colorDefinition: colorDefinitions[i]}); + listOfColorDefinitions.addColorDefinition(colorDefinition); + } + } + + return listOfColorDefinitions; +}; + +ns.ListOfColorDefinitions = ListOfColorDefinitions; +// ------- END LISTOFCOLORDEFINITIONS ------- + +// ------- RENDERGROUP ------- +/** + * Represents the <g> element. + * @class + * @param {Object} params + * @param {string=} params.id + * @param {string=} params.fontSize + * @param {string=} params.fontFamily + * @param {string=} params.fontWeight + * @param {string=} params.fontStyle + * @param {string=} params.fontColor + * @param {string=} params.textAnchor + * @param {string=} params.vtextAnchor + * @param {string=} params.fill The element's background color + * @param {string=} params.stroke Border color for glyphs, line color for arcs. + * @param {string=} params.strokeWidth + * @param {string=} params.backgroundImage + * @param {string=} params.backgroundFit + * @param {string=} params.backgroundPosX + * @param {string=} params.backgroundPosY + * @param {string=} params.backgroundWidth + * @param {string=} params.backgroundHeight + * @param {string=} params.backgroundImageOpacity + * @param {string=} params.backgroundOpacity + */ +var RenderGroup = function (params) { + // each of those are optional, so test if it is defined is mandatory + var params = checkParams(params, ['fontSize', 'fontFamily', 'fontWeight', + 'fontStyle', 'fontColor', 'textAnchor', 'vtextAnchor', 'fill', 'id', 'stroke', 'strokeWidth', 'backgroundImage', + 'backgroundFit', 'backgroundPosX', 'backgroundPosY', 'backgroundWidth', 'backgroundHeight', 'backgroundImageOpacity','backgroundOpacity' ]); + // specific to renderGroup + this.fontSize = params.fontSize; + this.fontFamily = params.fontFamily; + this.fontWeight = params.fontWeight; + this.fontStyle = params.fontStyle; + this.fontColor = params.fontColor; + this.textAnchor = params.textAnchor; // probably useless + this.vtextAnchor = params.vtextAnchor; // probably useless + // from GraphicalPrimitive2D + this.fill = params.fill; // fill color + // from GraphicalPrimitive1D + this.id = params.id; + this.stroke = params.stroke; // stroke color + this.strokeWidth = params.strokeWidth; + this.backgroundImage = params.backgroundImage; + this.backgroundFit = params.backgroundFit; + this.backgroundPosX = params.backgroundPosX; + this.backgroundPosY = params.backgroundPosY; + this.backgroundWidth = params.backgroundWidth; + this.backgroundHeight = params.backgroundHeight; + this.backgroundImageOpacity = params.backgroundImageOpacity; + this.backgroundOpacity = params.backgroundOpacity; +}; + +/** + * @return {Object} - xml2js formatted object + */ +RenderGroup.prototype.buildJsObj = function () { + var renderGroupObj = {}; + + // attributes + var attributes = {}; + if(this.id != null) { + attributes.id = this.id; + } + if(this.fontSize != null) { + attributes.fontSize = this.fontSize; + } + if(this.fontFamily != null) { + attributes.fontFamily = this.fontFamily; + } + if(this.fontWeight != null) { + attributes.fontWeight = this.fontWeight; + } + if(this.fontStyle != null) { + attributes.fontStyle = this.fontStyle; + } + if (this.fontColor != null) { + attributes.fontColor = this.fontColor; + } + if(this.textAnchor != null) { + attributes.textAnchor = this.textAnchor; + } + if(this.vtextAnchor != null) { + attributes.vtextAnchor = this.vtextAnchor; + } + if(this.stroke != null) { + attributes.stroke = this.stroke; + } + if(this.strokeWidth != null) { + attributes.strokeWidth = this.strokeWidth; + } + if(this.fill != null) { + attributes.fill = this.fill; + } + if(this.backgroundImage != null) { + attributes.backgroundImage = this.backgroundImage; + } + if(this.backgroundFit != null) { + attributes.backgroundFit = this.backgroundFit; + } + if(this.backgroundPosX != null) { + attributes.backgroundPosX = this.backgroundPosX; + } + if(this.backgroundPosY != null) { + attributes.backgroundPosY = this.backgroundPosY; + } + if(this.backgroundWidth != null) { + attributes.backgroundWidth = this.backgroundWidth; + } + if(this.backgroundHeight != null) { + attributes.backgroundHeight = this.backgroundHeight; + } + if(this.backgroundImageOpacity != null) { + attributes.backgroundImageOpacity = this.backgroundImageOpacity; + } + if(this.backgroundOpacity != null) { + attributes.backgroundOpacity = this.backgroundOpacity; + } + utils.addAttributes(renderGroupObj, attributes); + return renderGroupObj; +}; + +/** + * @return {string} + */ +RenderGroup.prototype.toXML = function () { + return utils.buildString({g: this.buildJsObj()}) +}; + +/** + * @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; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {RenderGroup} + */ +RenderGroup.fromObj = function (jsObj) { + var gNode = utils.getChildByNameIgnoringNamespace(jsObj, "g"); + if (gNode === null) { + throw new Error("Bad XML provided, expected tagName g, got: " + Object.keys(jsObj)[0]); + } + + var g = new ns.RenderGroup(); + jsObj = gNode; + if (typeof jsObj != 'object') { // nothing inside, empty xml + return g; + } + + if(jsObj.$) { // we have some attributes + + var attributes = jsObj.$; + g.id = utils.getChildByNameIgnoringNamespace(attributes, "id"); + g.fontSize = utils.getChildByNameIgnoringNamespace(attributes, "fontSize"); + g.fontFamily = utils.getChildByNameIgnoringNamespace(attributes, "fontFamily"); + g.fontWeight = utils.getChildByNameIgnoringNamespace(attributes, "fontWeight"); + g.fontStyle = utils.getChildByNameIgnoringNamespace(attributes, "fontStyle"); + g.fontColor = utils.getChildByNameIgnoringNamespace(attributes, "fontColor"); + g.textAnchor = utils.getChildByNameIgnoringNamespace(attributes, "textAnchor"); + g.vtextAnchor = utils.getChildByNameIgnoringNamespace(attributes, "vtextAnchor"); + g.stroke = utils.getChildByNameIgnoringNamespace(attributes, "stroke"); + g.strokeWidth = utils.getChildByNameIgnoringNamespace(attributes, "strokeWidth"); + g.fill = utils.getChildByNameIgnoringNamespace(attributes, "fill"); + g.backgroundImage = utils.getChildByNameIgnoringNamespace(attributes, "backgroundImage"); + g.backgroundFit = utils.getChildByNameIgnoringNamespace(attributes, "backgroundFit"); + g.backgroundPosX = utils.getChildByNameIgnoringNamespace(attributes, "backgroundPosX"); + g.backgroundPosY = utils.getChildByNameIgnoringNamespace(attributes, "backgroundPosY"); + g.backgroundWidth = utils.getChildByNameIgnoringNamespace(attributes, "backgroundWidth"); + g.backgroundHeight = utils.getChildByNameIgnoringNamespace(attributes, "backgroundHeight"); + g.backgroundImageOpacity = utils.getChildByNameIgnoringNamespace(attributes, "backgroundImageOpacity"); + g.backgroundOpacity = utils.getChildByNameIgnoringNamespace(attributes, "backgroundOpacity"); + } + return g; +}; + +ns.RenderGroup = RenderGroup; +// ------- END RENDERGROUP ------- + +// ------- 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 + */ +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; +}; + +/** + * @param {RenderGroup} renderGroup + */ +Style.prototype.setRenderGroup = function (renderGroup) { + this.renderGroup = renderGroup; +}; + +/** + * @return {string[]} + */ +Style.prototype.getIdListAsArray = function () { + return this.idList.split(' '); +} + +/** + * @param {string[]} idArray + */ +Style.prototype.setIdListFromArray = function (idArray) { + this.idList = idArray.join(' '); +} + +/** + * 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 = {}; + + // attributes + var attributes = {}; + if(this.id != null) { + attributes.id = this.id; + } + if(this.name != null) { + attributes.name = this.name; + } + if(this.idList != null) { + attributes.idList = this.idList; + } + utils.addAttributes(styleObj, attributes); + + // children + if(this.renderGroup != null) { + styleObj.g = this.renderGroup.buildJsObj(); + } + return styleObj; +}; + +/** + * @return {string} + */ +Style.prototype.toXML = function () { + return utils.buildString({style: this.buildJsObj()}) +}; + +/** + * @param {String} string + * @return {Style} + */ +Style.fromXML = function (string) { + var style; + function fn (err, result) { + style = Style.fromObj(result); + }; + utils.parseString(string, fn); + return style; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {Style} + */ +Style.fromObj = function (jsObj) { + var styleNode = utils.getChildByNameIgnoringNamespace(jsObj, "style"); + if (styleNode === null) { + throw new Error("Bad XML provided, expected tagName style, got: " + Object.keys(jsObj)[0]); + } + + var style = new ns.Style(); + jsObj = styleNode; + if (typeof jsObj != 'object') { // nothing inside, empty xml + return style; + } + + if (jsObj.$) { // we have some attributes + var attributes = jsObj.$; + style.id = utils.getChildByNameIgnoringNamespace(attributes, "id"); + style.name = utils.getChildByNameIgnoringNamespace(attributes, "name"); + style.idList = utils.getChildByNameIgnoringNamespace(attributes, "idList"); + } + + // children + var gNode = utils.getChildByNameIgnoringNamespace(jsObj, "g"); + if(gNode) { + var g = ns.RenderGroup.fromObj({g: gNode[0]}); + style.setRenderGroup(g); + } + + return style; +}; + +ns.Style = Style; +// ------- END STYLE ------- + +// ------- LISTOFSTYLES ------- +/** + * Represents the <listOfStyles> element. + * @class + */ +var ListOfStyles = function() { + this.styles = []; +}; + +/** + * @param {Style} style + */ +ListOfStyles.prototype.addStyle = function (style) { + this.styles.push(style); +}; + +/** + * Convenience function returning a map of ids to their respective RenderGroup object, + * for all the styles. + * The style properties can then be directly accessed. Example: map[id].stroke + * @return {Object.} + */ +ListOfStyles.prototype.getStyleMap = function () { + var index = {}; + for(var i=0; i < this.styles.length; i++) { + var style = this.styles[i]; + var subIndex = style.getStyleMap(); + for(var id in subIndex) { + index[id] = subIndex[id]; + } + } + return index; +} + +/** + * @return {Object} - xml2js formatted object + */ +ListOfStyles.prototype.buildJsObj = function () { + var listOfStylesObj = {}; + + for(var i=0; i < this.styles.length; i++) { + if (i==0) { + listOfStylesObj.style = []; + } + listOfStylesObj.style.push(this.styles[i].buildJsObj()); + } + + return listOfStylesObj; +}; + +/** + * @return {string} + */ +ListOfStyles.prototype.toXML = function () { + return utils.buildString({listOfStyles: this.buildJsObj()}) +}; + +/** + * @param {String} string + * @return {ListOfStyles} + */ +ListOfStyles.fromXML = function (string) { + var listOfStyles; + function fn (err, result) { + listOfStyles = ListOfStyles.fromObj(result); + }; + utils.parseString(string, fn); + return listOfStyles; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {ListOfStyles} + */ +ListOfStyles.fromObj = function (jsObj) { + var listOfStylesNode = utils.getChildByNameIgnoringNamespace(jsObj, "listOfStyles"); + if (listOfStylesNode === null) { + throw new Error("Bad XML provided, expected tagName listOfStyles, got: " + Object.keys(jsObj)[0]); + } + + var listOfStyles = new ns.ListOfStyles(); + jsObj = listOfStylesNode; + if (typeof jsObj != 'object') { // nothing inside, empty xml + return listOfStyles; + } + + // children + var styles = utils.getChildByNameIgnoringNamespace(jsObj, "style"); + if (styles) { + for (var i = 0; i < styles.length; i++) { + var style = ns.Style.fromObj({style: styles[i]}); + listOfStyles.addStyle(style); + } + } + + return listOfStyles; +}; + +ns.ListOfStyles = ListOfStyles; +// ------- END LISTOFSTYLES ------- +// ------- BACKGROUNDIMAGE ------- +/** + * Represents the <backgroundImage> element. + * @class + * @param {Object} params + * @param {string=} params.id + * @param {string=} params.value + */ +var BackgroundImage = function(params) { + var params = checkParams(params, ['id', 'value']); + this.id = params.id; + this.value = params.value; +}; + +/** + * @return {Object} - xml2js formatted object + */ +BackgroundImage.prototype.buildJsObj = function () { + var bgImgObj = {}; + + // attributes + var attributes = {}; + if(this.id != null) { + attributes.id = this.id; + } + if(this.value != null) { + attributes.value = this.value; + } + utils.addAttributes(bgImgObj, attributes); + return bgImgObj; +}; + +/** + * @return {string} + */ +BackgroundImage.prototype.toXML = function () { + return utils.buildString({backgroundImage: this.buildJsObj()}) +}; + +/** + * @param {String} string + * @return {BackgroundImage} + */ +BackgroundImage.fromXML = function (string) { + var backgroundImage; + function fn (err, result) { + backgroundImage = BackgroundImage.fromObj(result); + }; + utils.parseString(string, fn); + return backgroundImage; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {BackgroundImage} + */ +BackgroundImage.fromObj = function (jsObj) { + if (typeof jsObj.backgroundImage == 'undefined') { + throw new Error("Bad XML provided, expected tagName backgroundImage, got: " + Object.keys(jsObj)[0]); + } + + var backgroundImage = new ns.BackgroundImage(); + jsObj = jsObj.backgroundImage; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return backgroundImage; + } + + if(jsObj.$) { // we have some attributes + var attributes = jsObj.$; + backgroundImage.id = attributes.id || null; + backgroundImage.value = attributes.value || null; + } + return backgroundImage; +}; + +ns.BackgroundImage = BackgroundImage; +// ------- END BACKGROUNDIMAGE ------- + +// ------- LISTOFBACKGROUNDIMAGES ------- +/** + * Represents the <listOfBackgroundImages> element. + * @class + */ +var ListOfBackgroundImages = function () { + this.backgroundImages = []; + this.imageIndex = {}; +}; + +/** + * @param {BackgroundImage} backgroundImage + */ +ListOfBackgroundImages.prototype.addBackgroundImage = function (backgroundImage) { + this.backgroundImages.push(backgroundImage); + this.imageIndex[backgroundImage.id] = backgroundImage.value; +}; + +/** + * Convenient method to get a background image value directly. + * @param {string} id + * @return {string} + */ +ListOfBackgroundImages.prototype.getBackgroundImageById = function (id) { + return this.imageIndex[id]; +}; + +/** + * Convenient method to get all the background image values in the list. + * @return {string[]} + */ +ListOfBackgroundImages.prototype.getAllImages = function () { + return Object.values(this.imageIndex); +}; + +/** + * @return {Object} - xml2js formatted object + */ +ListOfBackgroundImages.prototype.buildJsObj = function () { + var listOfBgImagesObj = {}; + + for(var i=0; i < this.backgroundImages.length; i++) { + if (i==0) { + listOfBgImagesObj.backgroundImage = []; + } + listOfBgImagesObj.backgroundImage.push(this.backgroundImages[i].buildJsObj()); + } + + return listOfBgImagesObj; +}; + +/** + * @return {string} + */ +ListOfBackgroundImages.prototype.toXML = function () { + return utils.buildString({listOfBackgroundImages: this.buildJsObj()}) +}; + +/** + * @param {String} string + * @return {ListOfBackgroundImages} + */ +ListOfBackgroundImages.fromXML = function (string) { + var listOfBackgroundImages; + function fn (err, result) { + listOfBackgroundImages = ListOfBackgroundImages.fromObj(result); + }; + utils.parseString(string, fn); + return listOfBackgroundImages; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {ListOfBackgroundImages} + */ +ListOfBackgroundImages.fromObj = function (jsObj) { + var listOfBackgroundImagesNode = utils.getChildByNameIgnoringNamespace(jsObj, "listOfBackgroundImages"); + if (listOfBackgroundImagesNode === null) { + throw new Error("Bad XML provided, expected tagName listOfBackgroundImages, got: " + Object.keys(jsObj)[0]); + } + + var listOfBackgroundImages = new ns.ListOfBackgroundImages(); + jsObj = listOfBackgroundImagesNode; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return listOfBackgroundImages; + } + + // children + if(jsObj.backgroundImage) { + var backgroundImages = jsObj.backgroundImage; + for (var i=0; i < backgroundImages.length; i++) { + var backgroundImage = ns.BackgroundImage.fromObj({backgroundImage: backgroundImages[i]}); + listOfBackgroundImages.addBackgroundImage(backgroundImage); + } + } + + return listOfBackgroundImages; +}; + +ns.ListOfBackgroundImages = ListOfBackgroundImages; +// ------- END LISTOFBACKGROUNDIMAGES ------- +// ------- RENDERINFORMATION ------- +/** + * Represents the <renderInformation> element. + * @class + * @param {Object} params + * @param {string=} params.id + * @param {string=} params.name + * @param {string=} params.programName + * @param {string=} params.programVersion + * @param {string=} params.backgroundColor + * @param {ListOfColorDefinitions=} params.listOfColorDefinitions + * @param {ListOfStyles=} params.listOfStyles + * @param {ListOfBackgroundImages=} params.listOfBackgroundImages + */ +var RenderInformation = function (params) { + var params = checkParams(params, ['id', 'name', 'programName', + 'programVersion', 'backgroundColor', 'listOfColorDefinitions', 'listOfStyles', 'listOfBackgroundImages']); + this.id = params.id; // required, rest is optional + this.name = params.name; + this.programName = params.programName; + this.programVersion = params.programVersion; + this.backgroundColor = params.backgroundColor; + this.listOfColorDefinitions = params.listOfColorDefinitions; + this.listOfStyles = params.listOfStyles; + this.listOfBackgroundImages = params.listOfBackgroundImages; +}; + +/** + * @param {ListOfColorDefinitions} listOfColorDefinitions + */ +RenderInformation.prototype.setListOfColorDefinitions = function(listOfColorDefinitions) { + this.listOfColorDefinitions = listOfColorDefinitions; +}; + +/** + * @param {ListOfStyles} listOfStyles + */ +RenderInformation.prototype.setListOfStyles = function(listOfStyles) { + this.listOfStyles = listOfStyles; +}; + +/** + * @param {ListOfBackgroundImages} listOfBackgroundImages + */ +RenderInformation.prototype.setListOfBackgroundImages = function(listOfBackgroundImages) { + this.listOfBackgroundImages = listOfBackgroundImages; +}; + +/** + * @return {Object} - xml2js formatted object + */ +RenderInformation.prototype.buildJsObj = function () { + var renderInformationObj = {}; + + // 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); + + // children + if(this.listOfColorDefinitions != null) { + renderInformationObj.listOfColorDefinitions = this.listOfColorDefinitions.buildJsObj(); + } + if(this.listOfBackgroundImages != null) { + renderInformationObj.listOfBackgroundImages = this.listOfBackgroundImages.buildJsObj(); + } + if(this.listOfStyles != null) { + renderInformationObj.listOfStyles = this.listOfStyles.buildJsObj(); + } + return renderInformationObj; +}; + +/** + * @return {string} + */ +RenderInformation.prototype.toXML = function() { + return utils.buildString({renderInformation: this.buildJsObj()}) +}; + +/** + * @param {String} string + * @return {RenderInformation} + */ +RenderInformation.fromXML = function (string) { + var renderInformation; + function fn (err, result) { + renderInformation = RenderInformation.fromObj(result); + }; + utils.parseString(string, fn); + return renderInformation; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {RenderInformation} + */ +RenderInformation.fromObj = function (jsObj) { + var renderInformationNode = utils.getChildByNameIgnoringNamespace(jsObj, "renderInformation"); + if (renderInformationNode === null) { + throw new Error("Bad XML provided, expected tagName renderInformation, got: " + Object.keys(jsObj)[0]); + } + + var renderInformation = new ns.RenderInformation(); + jsObj = renderInformationNode; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return renderInformation; + } + + if(jsObj.$) { // we have some attributes + var attributes = jsObj.$; + renderInformation.id = utils.getChildByNameIgnoringNamespace(attributes,"id"); + renderInformation.name = utils.getChildByNameIgnoringNamespace(attributes,"name"); + renderInformation.programName = utils.getChildByNameIgnoringNamespace(attributes,"programName"); + renderInformation.programVersion = utils.getChildByNameIgnoringNamespace(attributes,"programVersion"); + renderInformation.backgroundColor = utils.getChildByNameIgnoringNamespace(attributes,"backgroundColor"); + } + + // children + var listOfColorDefinitionsNode = utils.getChildByNameIgnoringNamespace(jsObj, "listOfColorDefinitions"); + if(listOfColorDefinitionsNode) { + var listOfColorDefinitions = ns.ListOfColorDefinitions.fromObj({listOfColorDefinitions: listOfColorDefinitionsNode}); + renderInformation.setListOfColorDefinitions(listOfColorDefinitions); + } + + var listOfStylesNode = utils.getChildByNameIgnoringNamespace(jsObj, "listOfStyles"); + if(listOfStylesNode) { + var listOfStyles = ns.ListOfStyles.fromObj({listOfStyles: listOfStylesNode}); + renderInformation.setListOfStyles(listOfStyles); + } + + var listOfBackgroundImagesNode = utils.getChildByNameIgnoringNamespace(jsObj, "listOfBackgroundImages"); + if(listOfBackgroundImagesNode) { + var listOfBackgroundImages = ns.ListOfBackgroundImages.fromObj({listOfBackgroundImages: listOfBackgroundImagesNode[0]}); + renderInformation.setListOfBackgroundImages(listOfBackgroundImages); + } + return renderInformation; +}; + +ns.RenderInformation = RenderInformation; +// ------- END RENDERINFORMATION ------- + +module.exports = ns; + +},{"./utilities":33,"xml2js":26}],31:[function(_dereq_,module,exports){ +/** + * The API contains two other submodules: {@link libsbgn.render} and {@link libsbgn.annot} + * @module libsbgn + * @namespace libsbgn +*/ + +var renderExt = _dereq_('./libsbgn-render'); +var annotExt = _dereq_('./libsbgn-annotations'); +var xml2js = _dereq_('xml2js'); +var utils = _dereq_('./utilities'); +var schematronValidator = _dereq_('./schematronValidator'); +var checkParams = utils.checkParams; + +var ns = {}; // namespace that encapsulates all exportable features + +ns.xmlns = "http://sbgn.org/libsbgn/0.3"; + +// ------- 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; +}; + +/** + * Allows inheriting objects to get an extension element. + * @param {Extension} extension + */ +SBGNBase.prototype.setExtension = function (extension) { + this.extension = extension; +}; + +/** + * Allows inheriting objects to get a notes element. + * @param {Notes} notes + */ +SBGNBase.prototype.setNotes = function (notes) { + this.notes = notes; +}; + +/** + * 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(); + } +}; + +/** + * Get the appropriate properties from jsObj. + * @param {Object} jsObj - xml2js formatted object + */ +SBGNBase.prototype.baseFromObj = function (jsObj) { + if (jsObj.extension) { + var extension = ns.Extension.fromObj({extension: jsObj.extension[0]}); + this.setExtension(extension); + } + if (jsObj.notes) { + var notes = ns.Notes.fromObj({notes: jsObj.notes[0]}); + this.setNotes(notes); + } +}; +ns.SBGNBase = SBGNBase; +// ------- END SBGNBase ------- + +// ------- SBGN ------- +/** + * Represents the <sbgn> element. + * @class + * @extends SBGNBase + * @param {Object} params + * @param {string=} params.xmlns + * @param {Map[]=} params.maps + */ +var Sbgn = function (params) { + ns.SBGNBase.call(this, params); + var params = checkParams(params, ['xmlns', 'maps']); + this.xmlns = params.xmlns; + this.maps = params.maps || []; +}; + +Sbgn.prototype = Object.create(ns.SBGNBase.prototype); +Sbgn.prototype.constructor = Sbgn; + +/** + * @param {Map} map + */ +Sbgn.prototype.addMap = function (map) { + this.maps.push(map); +}; + +/** + * @return {Object} - xml2js formatted object + */ +Sbgn.prototype.buildJsObj = function () { + var sbgnObj = {}; + + // attributes + var attributes = {}; + if(this.xmlns != null) { + attributes.xmlns = this.xmlns; + } + utils.addAttributes(sbgnObj, attributes); + + // 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; +}; + +/** + * @return {string} + */ +Sbgn.prototype.toXML = function () { + return utils.buildString({sbgn: this.buildJsObj()}); +}; + +/** + * @param {String} file + * @return {Issue[]} + */ +Sbgn.doValidation = function (file) { + return schematronValidator.doValidation(file); +}; + +/** + * @param {String} string + * @return {Sbgn} + */ +Sbgn.fromXML = function (string) { + var sbgn; + function fn (err, result) { + sbgn = Sbgn.fromObj(result); + } + 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; +}; +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 || []; +}; + +Map.prototype = Object.create(ns.SBGNBase.prototype); +Map.prototype.constructor = Map; + +/** + * @param {Glyph} glyph + */ +Map.prototype.addGlyph = function (glyph) { + this.glyphs.push(glyph); +}; + +/** + * @param {Arc} arc + */ +Map.prototype.addArc = function (arc) { + this.arcs.push(arc); +}; + +/** + * @param {Bbox} bbox + */ +Map.prototype.setBbox = function (bbox) { + this.bbox = bbox; +}; + +/** + * @param {Arcgroup} arc + */ +Map.prototype.addArcgroup = function (arcgroup) { + this.arcgroups.push(arcgroup); +}; + +/** + * @param {string} class_ + * @return {Gyph[]} + */ +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; +}; + +/** + * @return {Object} - xml2js formatted object + */ +Map.prototype.buildJsObj = function () { + var mapObj = {}; + + // 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); + + // 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; +}; + +/** + * @return {string} + */ +Map.prototype.toXML = function () { + return utils.buildString({map: this.buildJsObj()}); +}; + +/** + * @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; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {Map} + */ +Map.fromObj = function (jsObj) { + if (typeof jsObj.map == 'undefined') { + throw new Error("Bad XML provided, expected tagName map, got: " + Object.keys(jsObj)[0]); + } + + var map = new ns.Map(); + jsObj = jsObj.map; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return map; + } + + if(jsObj.$) { // we have some attributes + var attributes = jsObj.$; + map.id = attributes.id || null; + map.language = attributes.language || null; + map.version = attributes.version || null; + } + + if(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); + } + } + + map.baseFromObj(jsObj); + return map; +}; + +ns.Map = Map; +// ------- END MAP ------- + +// ------- 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 = {}; +}; + +/** + * @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; + } + } +}; + +/** + * @param {string} extensionName + * @return {boolean} + */ +Extension.prototype.has = function (extensionName) { + return this.list.hasOwnProperty(extensionName); +}; + +/** + * @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; + } +}; + +/** + * @return {Object} - xml2js formatted object + */ +Extension.prototype.buildJsObj = function () { + var extensionObj = {}; + + 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; +}; + +/** + * @return {string} + */ +Extension.prototype.toXML = function () { + return utils.buildString({extension: this.buildJsObj()}) +}; + +/** + * @param {String} string + * @return {Extension} + */ +Extension.fromXML = function (string) { + var extension; + function fn (err, result) { + extension = Extension.fromObj(result); + }; + utils.parseString(string, fn); + return extension; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {Extension} + */ +Extension.fromObj = function (jsObj) { + if (typeof jsObj.extension == 'undefined') { + throw new Error("Bad XML provided, expected tagName extension, got: " + Object.keys(jsObj)[0]); + } + + var extension = new Extension(); + jsObj = jsObj.extension; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return extension; + } + + //var children = Object.keys(jsObj); + for (var extName in jsObj) { + //var extName = Object.keys(jsObj[i])[0]; + var extJsObj = jsObj[extName]; + + //extension.add(extInstance); + if (extName === 'renderInformation' || + extName.endsWith(':renderInformation')) { + var renderInformation = renderExt.RenderInformation.fromObj({renderInformation: extJsObj[0]}); + extension.add(renderInformation); + } + else if (extName == 'annotation') { + var annotation = annotExt.Annotation.fromObj({annotation: extJsObj[0]}); + extension.add(annotation); + } + else { // unsupported extension, we still store the data as is + var unsupportedExt = {}; + unsupportedExt[extName] = extJsObj[0]; // make extension serialisable + var stringExt = utils.buildString(unsupportedExt); // serialise to string + extension.add(stringExt); // save it + } + } + + return extension; +}; + +ns.Extension = Extension; +// ------- END EXTENSION ------- + +// ------- NOTES ------- +/** + * Represents the <notes> element. + * Its single content attribute stores xhtml elements as string. + * @class + */ +var Notes = function () { + this.content = ""; +}; + +/** + * Overwrite the content. + * @param {String} string + */ +Notes.prototype.setContent = function (string) { + this.content = string; +}; + +/** + * @param {String} string + */ +Notes.prototype.appendContent = function (string) { + this.content += string; +}; + +/** + * @return {Object} - xml2js formatted object + */ +Notes.prototype.buildJsObj = function () { + + var parsedContent = ""; + if(this.content != "") { // xml2js refuses to parse empty strings + utils.parseString(this.content, function (err, result) { + parsedContent = result; + }); + } + + return parsedContent; +}; + +/** + * @return {string} + */ +Notes.prototype.toXML = function () { + return utils.buildString({notes: this.buildJsObj()}) +}; + +/** + * @param {String} string + * @return {Notes} + */ +Notes.fromXML = function (string) { + var notes; + function fn (err, result) { + notes = Notes.fromObj(result); + }; + utils.parseString(string, fn); + return notes; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {Notes} + */ +Notes.fromObj = function (jsObj) { + if (typeof jsObj.notes == 'undefined') { + throw new Error("Bad XML provided, expected tagName notes, got: " + Object.keys(jsObj)[0]); + } + + var notes = new Notes(); + jsObj = jsObj.notes; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return notes; + } + + 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 + + return notes; +}; + +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 {State=} params.state + * @param {Clone=} params.clone + * @param {Callout=} params.callout + * @param {Entity=} params.entity + * @param {Glyph[]=} params.glyphMembers + * @param {Port[]=} params.ports + */ +var Glyph = function (params) { + ns.SBGNBase.call(this, params); + var params = checkParams(params, ['id', 'class_', 'compartmentRef', 'compartmentOrder', 'mapRef', + 'tagRef', 'orientation', 'label', 'bbox', 'glyphMembers', 'ports', 'state', 'clone', 'entity', 'callout']); + this.id = params.id; + this.class_ = params.class_; + this.compartmentRef = params.compartmentRef; + this.compartmentOrder = parseFloat(params.compartmentOrder); + this.mapRef = params.mapRef; + this.tagRef = params.tagRef; + this.orientation = params.orientation; + + // 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 || []; +}; + +Glyph.prototype = Object.create(ns.SBGNBase.prototype); +Glyph.prototype.constructor = Glyph; + +/** + * @param {Label} label + */ +Glyph.prototype.setLabel = function (label) { + this.label = label; +}; + +/** + * @param {State} state + */ +Glyph.prototype.setState = function (state) { + this.state = state; +}; + +/** + * @param {Bbox} bbox + */ +Glyph.prototype.setBbox = function (bbox) { + this.bbox = bbox; +}; + +/** + * @param {Clone} clone + */ +Glyph.prototype.setClone = function (clone) { + this.clone = clone; +}; + +/** + * @param {Callout} callout + */ +Glyph.prototype.setCallout = function (callout) { + this.callout = callout; +}; + +/** + * @param {Entity} entity + */ +Glyph.prototype.setEntity = function (entity) { + this.entity = entity; +}; + +/** + * @param {Glyph} glyphMember + */ +Glyph.prototype.addGlyphMember = function (glyphMember) { + this.glyphMembers.push(glyphMember); +}; + +/** + * @param {Port} port + */ +Glyph.prototype.addPort = function (port) { + this.ports.push(port); +}; + +/** + * @return {Object} - xml2js formatted object + */ +Glyph.prototype.buildJsObj = function () { + var glyphObj = {}; + + // 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); + + // 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; +}; + +/** + * @return {string} + */ +Glyph.prototype.toXML = function () { + return utils.buildString({glyph: this.buildJsObj()}) +}; + +/** + * @param {String} string + * @return {Glyph} + */ +Glyph.fromXML = function (string) { + var glyph; + function fn (err, result) { + glyph = Glyph.fromObj(result); + }; + utils.parseString(string, fn); + return glyph; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {Glyph} + */ +Glyph.fromObj = function (jsObj) { + if (typeof jsObj.glyph == 'undefined') { + throw new Error("Bad XML provided, expected tagName glyph, got: " + Object.keys(jsObj)[0]); + } + + var 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; + } + + // children + if(jsObj.label) { + var label = ns.Label.fromObj({label: jsObj.label[0]}); + glyph.setLabel(label); + } + if(jsObj.state) { + var state = ns.State.fromObj({state: jsObj.state[0]}); + glyph.setState(state); + } + if(jsObj.clone) { + var clone = ns.Clone.fromObj({clone: jsObj.clone[0]}); + glyph.setClone(clone); + } + if(jsObj.callout) { + var callout = ns.Callout.fromObj({callout: jsObj.callout[0]}); + glyph.setCallout(callout); + } + if(jsObj.entity) { + var entity = ns.Entity.fromObj({entity: jsObj.entity[0]}); + glyph.setEntity(entity); + } + if(jsObj.bbox) { + var bbox = ns.Bbox.fromObj({bbox: jsObj.bbox[0]}); + glyph.setBbox(bbox); + } + + 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); + } + } + + glyph.baseFromObj(jsObj); + return glyph; +}; + +ns.Glyph = Glyph; +// ------- END GLYPH ------- + +// ------- 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; +}; + +Label.prototype = Object.create(ns.SBGNBase.prototype); +Label.prototype.constructor = ns.Label; + +/** + * @param {Bbox} bbox + */ +Label.prototype.setBbox = function (bbox) { + this.bbox = bbox; +}; + +/** + * @return {Object} - xml2js formatted object + */ +Label.prototype.buildJsObj = function () { + var labelObj = {}; + + // 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); + + this.baseToJsObj(labelObj); + if(this.bbox != null) { + labelObj.bbox = this.bbox.buildJsObj(); + } + return labelObj; +}; + +/** + * @return {string} + */ +Label.prototype.toXML = function () { + return utils.buildString({label: this.buildJsObj()}) +}; + +/** + * @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; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {Label} + */ +Label.fromObj = function (jsObj) { + if (typeof jsObj.label == 'undefined') { + throw new Error("Bad XML provided, expected tagName label, got: " + Object.keys(jsObj)[0]); + } + + var label = new ns.Label(); + jsObj = jsObj.label; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return label; + } + + if(jsObj.$) { // we have some attributes + var attributes = jsObj.$; + label.text = attributes.text || null; + } + + if(jsObj.bbox) { + var bbox = ns.Bbox.fromObj({bbox: jsObj.bbox[0]}); + label.setBbox(bbox); + } + label.baseFromObj(jsObj); + return label; +}; + +ns.Label = Label; +// ------- END LABEL ------- + +// ------- 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); +}; + +Bbox.prototype = Object.create(ns.SBGNBase.prototype); +Bbox.prototype.constructor = ns.Bbox; + +/** + * @return {Object} - xml2js formatted object + */ +Bbox.prototype.buildJsObj = function () { + var bboxObj = {}; + + // attributes + var attributes = {}; + if(!isNaN(this.x)) { + attributes.x = this.x; + } + if(!isNaN(this.y)) { + attributes.y = this.y; + } + if(!isNaN(this.w)) { + attributes.w = this.w; + } + if(!isNaN(this.h)) { + attributes.h = this.h; + } + utils.addAttributes(bboxObj, attributes); + this.baseToJsObj(bboxObj); + return bboxObj; +}; + +/** + * @return {string} + */ +Bbox.prototype.toXML = function () { + return utils.buildString({bbox: this.buildJsObj()}) +}; + +/** + * @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; +}; + +/** + * @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]); + } + + var bbox = new ns.Bbox(); + jsObj = jsObj.bbox; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return bbox; + } + + 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; +}; + +ns.Bbox = Bbox; +// ------- END BBOX ------- + +// ------- STATE ------- +/** + * Represents the <state> element. + * @class State + * @param {Object} params + * @param {string=} params.value + * @param {string=} params.variable + */ +var State = function (params) { + var params = checkParams(params, ['value', 'variable']); + this.value = params.value; + this.variable = params.variable; +}; + +/** + * @return {Object} - xml2js formatted object + */ +State.prototype.buildJsObj = function () { + var stateObj = {}; + + // 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 {string} + */ +State.prototype.toXML = function () { + return utils.buildString({state: this.buildJsObj()}) +}; + +/** + * @param {String} string + * @return {State} + */ +State.fromXML = function (string) { + var state; + function fn (err, result) { + state = State.fromObj(result); + }; + utils.parseString(string, fn); + return state; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {State} + */ +State.fromObj = function (jsObj) { + if (typeof jsObj.state == 'undefined') { + throw new Error("Bad XML provided, expected tagName state, got: " + Object.keys(jsObj)[0]); + } + + var state = new ns.State(); + jsObj = jsObj.state; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return state; + } + + if(jsObj.$) { // we have some attributes + var attributes = jsObj.$; + state.value = attributes.value || null; + state.variable = attributes.variable || null; + } + return state; +}; + +ns.State = State; +/** + * @class StateType + * @deprecated Replaced by State + */ +ns.StateType = State; +// ------- END STATE ------- + +// ------- CLONE ------- +/** + * Represents the <clone> element. + * @class Clone + * @param {Object} params + * @param {string=} params.label + */ +var Clone = function (params) { + var params = checkParams(params, ['label']); + this.label = params.label; +}; + +/** + * @param {Label} label + */ +Clone.prototype.setLabel = function (label) { + this.label = label; +}; + +/** + * @return {Object} - xml2js formatted object + */ +Clone.prototype.buildJsObj = function () { + var cloneObj = {}; + + // children + if(this.label != null) { + cloneObj.label = this.label.buildJsObj(); + } + return cloneObj; +}; + +/** + * @return {string} + */ +Clone.prototype.toXML = function () { + return utils.buildString({clone: this.buildJsObj()}) +}; + +/** + * @param {String} string + * @return {Clone} + */ +Clone.fromXML = function (string) { + var clone; + function fn (err, result) { + clone = Clone.fromObj(result); + }; + utils.parseString(string, fn); + return clone; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {Clone} + */ +Clone.fromObj = function (jsObj) { + if (typeof jsObj.clone == 'undefined') { + throw new Error("Bad XML provided, expected tagName clone, got: " + Object.keys(jsObj)[0]); + } + + var clone = new ns.Clone(); + jsObj = jsObj.clone; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return clone; + } + + // children + if(jsObj.label) { + var label = ns.Label.fromObj({label: jsObj.label[0]}); + clone.setLabel(label); + } + return clone; +}; + +ns.Clone = Clone; +/** + * @class CloneType + * @deprecated Replaced by Clone + */ +ns.CloneType = Clone; +// ------- END CLONE ------- + +// ------- ENTITYTYPE ------- +/** + * Represents the <entity> element. + * @class Entity + * @param {Object} params + * @param {string=} params.name + */ +var Entity = function (params) { + var params = checkParams(params, ['name']); + this.name = params.name; +}; + +/** + * @return {Object} - xml2js formatted object + */ +Entity.prototype.buildJsObj = function () { + var entityObj = {}; + + // attributes + var attributes = {}; + if(this.name != null) { + attributes.name = this.name; + } + utils.addAttributes(entityObj, attributes); + return entityObj; +}; + +/** + * @return {string} + */ +Entity.prototype.toXML = function () { + return utils.buildString({entity: this.buildJsObj()}) +}; + +/** + * @param {String} string + * @return {Entity} + */ +Entity.fromXML = function (string) { + var entity; + function fn (err, result) { + entity = Entity.fromObj(result); + }; + utils.parseString(string, fn); + return entity; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {Entity} + */ +Entity.fromObj = function (jsObj) { + if (typeof jsObj.entity == 'undefined') { + throw new Error("Bad XML provided, expected tagName entity, got: " + Object.keys(jsObj)[0]); + } + + var entity = new ns.Entity(); + jsObj = jsObj.entity; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return entity; + } + + if(jsObj.$) { // we have some attributes + var attributes = jsObj.$; + entity.name = attributes.name || null; + } + return entity; +}; + +ns.Entity = Entity; +/** + * @class EntityType + * @deprecated Replaced by Entity + */ +ns.EntityType = Entity; +// ------- END ENTITYTYPE ------- + +// ------- 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); +}; + +Port.prototype = Object.create(ns.SBGNBase.prototype); +Port.prototype.constructor = ns.Port; + +/** + * @return {Object} - xml2js formatted object + */ +Port.prototype.buildJsObj = function () { + var portObj = {}; + + // 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; +}; + +/** + * @return {string} + */ +Port.prototype.toXML = function () { + return utils.buildString({port: this.buildJsObj()}) +}; + +/** + * @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; +}; + +/** + * @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]); + } + + var port = new ns.Port(); + jsObj = jsObj.port; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return port; + } + + 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; +}; + +ns.Port = Port; +// ------- END PORT ------- + +// ------- ARC ------- +/** + * Represents the <arc> element. + * @class Arc + * @param {Object} params + * @param {string=} params.id + * @param {string=} params.class_ + * @param {string=} params.source + * @param {string=} params.target + * @param {Start=} params.start + * @param {End=} params.end + * @param {Next=} params.nexts + * @param {Glyph[]=} params.glyphs The arc's cardinality. Possibility to have more than one glyph is left open. + */ +var Arc = function (params) { + ns.SBGNBase.call(this, params); + var params = checkParams(params, ['id', 'class_', 'source', 'target', 'start', 'end', 'nexts', 'glyphs']); + this.id = params.id; + this.class_ = params.class_; + this.source = params.source; + this.target = params.target; + + this.start = params.start; + this.end = params.end; + this.nexts = params.nexts || []; + this.glyphs = params.glyphs || []; +}; + +Arc.prototype = Object.create(ns.SBGNBase.prototype); +Arc.prototype.constructor = ns.Arc; + +/** + * @param {Start} start + */ +Arc.prototype.setStart = function (start) { + this.start = start; +}; + +/** + * @param {End} end + */ +Arc.prototype.setEnd = function (end) { + this.end = end; +}; + +/** + * @param {Next} next + */ +Arc.prototype.addNext = function (next) { + this.nexts.push(next); +}; + +/** + * @param {Glyph} glyph + */ +Arc.prototype.addGlyph = function (glyph) { + this.glyphs.push(glyph); +}; + +/** + * @return {Object} - xml2js formatted object + */ +Arc.prototype.buildJsObj = function () { + var arcObj = {}; + + // 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); + + // 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; +}; + +/** + * @return {string} + */ +Arc.prototype.toXML = function () { + return utils.buildString({arc: this.buildJsObj()}) +}; + +/** + * @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} + */ +Arc.fromObj = function (jsObj) { + if (typeof jsObj.arc == 'undefined') { + throw new Error("Bad XML provided, expected tagName arc, got: " + Object.keys(jsObj)[0]); + } + + var arc = new ns.Arc(); + jsObj = jsObj.arc; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return arc; + } + + 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; + } + + // children + if(jsObj.start) { + var start = ns.Start.fromObj({start: jsObj.start[0]}); + arc.setStart(start); + } + if(jsObj.next) { + var nexts = jsObj.next; + for (var i=0; i < nexts.length; i++) { + var next = ns.Next.fromObj({next: nexts[i]}); + arc.addNext(next); + } + } + if(jsObj.end) { + var end = ns.End.fromObj({end: jsObj.end[0]}); + arc.setEnd(end); + } + if(jsObj.glyph) { + var glyphs = jsObj.glyph; + for (var i=0; i < glyphs.length; i++) { + var glyph = ns.Glyph.fromObj({glyph: glyphs[i]}); + arc.addGlyph(glyph); + } + } + + arc.baseFromObj(jsObj); + return arc; +}; + +ns.Arc = Arc; +// ------- END ARC ------- + +// ------- Start ------- +/** + * Represents the <start> element. + * @class Start + * @param {Object} params + * @param {string|number=} params.x + * @param {string|number=} params.y + */ +var Start = function (params) { + var params = checkParams(params, ['x', 'y']); + this.x = parseFloat(params.x); + this.y = parseFloat(params.y); +}; + +/** + * @return {Object} - xml2js formatted object + */ +Start.prototype.buildJsObj = function () { + var startObj = {}; + + // 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; +}; + +/** + * @return {string} + */ +Start.prototype.toXML = function () { + return utils.buildString({start: this.buildJsObj()}) +}; + +/** + * @param {String} string + * @return {Start} + */ +Start.fromXML = function (string) { + var start; + function fn (err, result) { + start = Start.fromObj(result); + }; + utils.parseString(string, fn); + return start; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {Start} + */ +Start.fromObj = function (jsObj) { + if (typeof jsObj.start == 'undefined') { + throw new Error("Bad XML provided, expected tagName start, got: " + Object.keys(jsObj)[0]); + } + + var start = new ns.Start(); + jsObj = jsObj.start; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return start; + } + + if(jsObj.$) { // we have some attributes + var attributes = jsObj.$; + start.x = parseFloat(attributes.x); + start.y = parseFloat(attributes.y); + } + return start; +}; + +ns.Start = Start; +/** + * @class StartType + * @deprecated Replaced by Start + */ +ns.StartType = Start; +// ------- END Start ------- + +// ------- End ------- +/** + * Represents the <end> element. + * @class End + * @param {Object} params + * @param {string|number=} params.x + * @param {string|number=} params.y + */ +var End = function (params) { + var params = checkParams(params, ['x', 'y']); + this.x = parseFloat(params.x); + this.y = parseFloat(params.y); +}; + +/** + * @return {Object} - xml2js formatted object + */ +End.prototype.buildJsObj = function () { + var endObj = {}; + + // 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; +}; + +/** + * @return {string} + */ +End.prototype.toXML = function () { + return utils.buildString({end: this.buildJsObj()}) +}; + +/** + * @param {String} string + * @return {End} + */ +End.fromXML = function (string) { + var end; + function fn (err, result) { + end = End.fromObj(result); + }; + utils.parseString(string, fn); + return end; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {End} + */ +End.fromObj = function (jsObj) { + if (typeof jsObj.end == 'undefined') { + throw new Error("Bad XML provided, expected tagName end, got: " + Object.keys(jsObj)[0]); + } + + var end = new ns.End(); + 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.End = End; +/** + * @class EndType + * @deprecated Replaced by End + */ +ns.EndType = End; +// ------- END End ------- + +// ------- Next ------- +/** + * Represents the <next> element. + * @class Next + * @param {Object} params + * @param {string|number=} params.x + * @param {string|number=} params.y + */ +var Next = function (params) { + var params = checkParams(params, ['x', 'y']); + this.x = parseFloat(params.x); + this.y = parseFloat(params.y); +}; + +/** + * @return {Object} - xml2js formatted object + */ +Next.prototype.buildJsObj = function () { + var nextObj = {}; + + // 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; +}; + +/** + * @return {string} + */ +Next.prototype.toXML = function () { + return utils.buildString({next: this.buildJsObj()}) +}; + +/** + * @param {String} string + * @return {Next} + */ +Next.fromXML = function (string) { + var next; + function fn (err, result) { + next = Next.fromObj(result); + }; + utils.parseString(string, fn); + return next; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {Next} + */ +Next.fromObj = function (jsObj) { + if (typeof jsObj.next == 'undefined') { + throw new Error("Bad XML provided, expected tagName next, got: " + Object.keys(jsObj)[0]); + } + + var next = new ns.Next(); + jsObj = jsObj.next; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return next; + } + + if(jsObj.$) { // we have some attributes + var attributes = jsObj.$; + next.x = parseFloat(attributes.x); + next.y = parseFloat(attributes.y); + } + return next; +}; + +ns.Next = Next; +/** + * @class NextType + * @deprecated Replaced by Next + */ +ns.NextType = Next; +// ------- END Next ------- + +// ------- 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; + +/** + * @return {Object} - xml2js formatted object + */ +Point.prototype.buildJsObj = function () { + var pointJsObj = {}; + + // 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; +}; + +/** + * @return {string} + */ +Point.prototype.toXML = function () { + return utils.buildString({point: this.buildJsObj()}) +}; + +/** + * @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]); + } + + var point = new ns.Point(); + jsObj = jsObj.point; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return point; + } + + if(jsObj.$) { // we have some attributes + var attributes = jsObj.$; + point.x = parseFloat(attributes.x); + point.y = parseFloat(attributes.y); + } + point.baseFromObj(jsObj); + return point; +}; + +ns.Point = Point; +// ------- END POINT ------- + +// ------- 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; +}; + +/** + * @param {Point} point + */ +Callout.prototype.setPoint = function(point) { + this.point = point; +}; + +/** + * @return {Object} - xml2js formatted object + */ +Callout.prototype.buildJsObj = function () { + var calloutObj = {}; + + // attributes + var attributes = {}; + if(this.target != null) { + attributes.target = this.target; + } + utils.addAttributes(calloutObj, attributes); + + // children + if(this.point != null) { + calloutObj.point = this.point.buildJsObj(); + } + return calloutObj; +}; + +/** + * @return {string} + */ +Callout.prototype.toXML = function () { + return utils.buildString({callout: this.buildJsObj()}) +}; + +/** + * @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; +}; + +/** + * @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]); + } + + var callout = new ns.Callout(); + jsObj = jsObj.callout; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return callout; + } + + if(jsObj.$) { // we have some attributes + var attributes = jsObj.$; + callout.target = attributes.target || null; + } + + // children + if(jsObj.point) { + var point = ns.Point.fromObj({point: jsObj.point[0]}); + callout.setPoint(point); + } + return callout; +}; + +ns.Callout = Callout; +// ------- END CALLOUT ------- + +// ------- ARCGROUP ------- +/** + * Represents the <arcgroup> element. + * @class + * @extends SBGNBase + * @param {Object} params + * @param {string=} params.class_ + * @param {Glyph[]=} params.glyphs + * @param {Arc[]=} params.arcs + */ +var Arcgroup = function (params) { + ns.SBGNBase.call(this, params); + var params = checkParams(params, ['class_', 'glyphs', 'arcs']); + this.class_ = params.class_; + this.glyphs = params.glyphs || []; + this.arcs = params.arcs || []; +}; + +Arcgroup.prototype = Object.create(ns.SBGNBase.prototype); +Arcgroup.prototype.constructor = Arcgroup; + +/** + * @param {Glyph} glyph + */ +Arcgroup.prototype.addGlyph = function (glyph) { + this.glyphs.push(glyph); +}; + +/** + * @param {Arc} arc + */ +Arcgroup.prototype.addArc = function (arc) { + this.arcs.push(arc); +}; + +/** + * @return {Object} - xml2js formatted object + */ +Arcgroup.prototype.buildJsObj = function () { + var arcgroupObj = {}; + + // attributes + var attributes = {}; + if(this.class_ != null) { + attributes.class = this.class_; + } + utils.addAttributes(arcgroupObj, attributes); + + // 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; +}; + +/** + * @return {string} + */ +Arcgroup.prototype.toXML = function () { + return utils.buildString({arcgroup: this.buildJsObj()}); +}; + +/** + * @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; +}; + +/** + * @param {Object} jsObj - xml2js formatted object + * @return {Arcgroup} + */ +Arcgroup.fromObj = function (jsObj) { + if (typeof jsObj.arcgroup == 'undefined') { + throw new Error("Bad XML provided, expected tagName arcgroup, got: " + Object.keys(jsObj)[0]); + } + + var arcgroup = new ns.Arcgroup(); + jsObj = jsObj.arcgroup; + if(typeof jsObj != 'object') { // nothing inside, empty xml + return arcgroup; + } + + if(jsObj.$) { // we have some attributes + var attributes = jsObj.$; + arcgroup.class_ = attributes.class || null; + } + + if(jsObj.glyph) { + var glyphs = jsObj.glyph; + for (var i=0; i < glyphs.length; i++) { + var glyph = ns.Glyph.fromObj({glyph: glyphs[i]}); + arcgroup.addGlyph(glyph); + } + } + if(jsObj.arc) { + var arcs = jsObj.arc; + for (var i=0; i < arcs.length; i++) { + var arc = ns.Arc.fromObj({arc: arcs[i]}); + arcgroup.addArc(arc); + } + } + + arcgroup.baseFromObj(jsObj); + return arcgroup; +}; + +ns.Arcgroup = Arcgroup; +// ------- END ARCGROUP ------- + +ns.render = renderExt; +ns.annot = annotExt; +ns.schematronValidator = schematronValidator; +module.exports = ns; -},{"./annotation-utils":28,"./utilities":33,"n3":34,"rdflib":60}],30:[function(_dereq_,module,exports){ -/** - * This submodule contains the classes to manage the render extension's xml and some utility functions. - * It adds the ability to save the styles and colors used in an SBGN map, as features like background-color, - * border thickness or font properties are not part of the SBGN standard. - * - * 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 - * - * @module libsbgn-render - * @namespace libsbgn.render -*/ - -var utils = _dereq_('./utilities'); -var checkParams = utils.checkParams; -var xml2js = _dereq_('xml2js'); - -var ns = {}; - -ns.xmlns = "http://www.sbml.org/sbml/level3/version1/render/version1"; - -// ------- COLORDEFINITION ------- -/** - * Represents the <colorDefinition> element. - * @class - * @param {Object} params - * @param {string=} params.id - * @param {string=} params.value - */ -var ColorDefinition = function(params) { - var params = checkParams(params, ['id', 'value']); - this.id = params.id; - this.value = params.value; -}; - -/** - * @return {Object} - xml2js formatted object - */ -ColorDefinition.prototype.buildJsObj = function () { - var colordefObj = {}; - - // attributes - var attributes = {}; - if(this.id != null) { - attributes.id = this.id; - } - if(this.value != null) { - attributes.value = this.value; - } - utils.addAttributes(colordefObj, attributes); - return colordefObj; -}; - -/** - * @return {string} - */ -ColorDefinition.prototype.toXML = function () { - return utils.buildString({colorDefinition: this.buildJsObj()}) -}; - -/** - * @param {String} string - * @return {ColorDefinition} - */ -ColorDefinition.fromXML = function (string) { - var colorDefinition; - function fn (err, result) { - colorDefinition = ColorDefinition.fromObj(result); - }; - utils.parseString(string, fn); - return colorDefinition; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {ColorDefinition} - */ -ColorDefinition.fromObj = function (jsObj) { - if (typeof jsObj.colorDefinition == 'undefined') { - throw new Error("Bad XML provided, expected tagName colorDefinition, got: " + Object.keys(jsObj)[0]); - } - - var colorDefinition = new ns.ColorDefinition(); - jsObj = jsObj.colorDefinition; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return colorDefinition; - } - - 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 = {}; -}; - -/** - * @param {ColorDefinition} colorDefinition - */ -ListOfColorDefinitions.prototype.addColorDefinition = function (colorDefinition) { - this.colorDefinitions.push(colorDefinition); - this.colorIndex[colorDefinition.id] = colorDefinition.value; -}; - -/** - * Convenient method to get a color value directly. - * @param {string} id - * @return {string} - */ -ListOfColorDefinitions.prototype.getColorById = function (id) { - return this.colorIndex[id]; -}; - -/** - * Convenient method to get all the color values in the list. - * @return {string[]} - */ -ListOfColorDefinitions.prototype.getAllColors = function () { - return Object.values(this.colorIndex); -}; - -/** - * @return {Object} - xml2js formatted object - */ -ListOfColorDefinitions.prototype.buildJsObj = function () { - var listOfColorDefinitionsObj = {}; - - for(var i=0; i < this.colorDefinitions.length; i++) { - if (i==0) { - listOfColorDefinitionsObj.colorDefinition = []; - } - listOfColorDefinitionsObj.colorDefinition.push(this.colorDefinitions[i].buildJsObj()); - } - - return listOfColorDefinitionsObj; -}; - -/** - * @return {string} - */ -ListOfColorDefinitions.prototype.toXML = function () { - return utils.buildString({listOfColorDefinitions: this.buildJsObj()}) -}; - -/** - * @param {String} string - * @return {ListOfColorDefinitions} - */ -ListOfColorDefinitions.fromXML = function (string) { - var listOfColorDefinitions; - function fn (err, result) { - listOfColorDefinitions = ListOfColorDefinitions.fromObj(result); - }; - utils.parseString(string, fn); - return listOfColorDefinitions; -}; - -/** - * @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]); - } - - var listOfColorDefinitions = new ns.ListOfColorDefinitions(); - jsObj = jsObj.listOfColorDefinitions; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return listOfColorDefinitions; - } - - // 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); - } - } - - return listOfColorDefinitions; -}; - -ns.ListOfColorDefinitions = ListOfColorDefinitions; -// ------- END LISTOFCOLORDEFINITIONS ------- - -// ------- RENDERGROUP ------- -/** - * Represents the <g> element. - * @class - * @param {Object} params - * @param {string=} params.id - * @param {string=} params.fontSize - * @param {string=} params.fontFamily - * @param {string=} params.fontWeight - * @param {string=} params.fontStyle - * @param {string=} params.fontColor - * @param {string=} params.textAnchor - * @param {string=} params.vtextAnchor - * @param {string=} params.fill The element's background color - * @param {string=} params.stroke Border color for glyphs, line color for arcs. - * @param {string=} params.strokeWidth - * @param {string=} params.backgroundImage - * @param {string=} params.backgroundFit - * @param {string=} params.backgroundPosX - * @param {string=} params.backgroundPosY - * @param {string=} params.backgroundWidth - * @param {string=} params.backgroundHeight - * @param {string=} params.backgroundImageOpacity - * @param {string=} params.backgroundOpacity - */ -var RenderGroup = function (params) { - // each of those are optional, so test if it is defined is mandatory - var params = checkParams(params, ['fontSize', 'fontFamily', 'fontWeight', - 'fontStyle', 'fontColor', 'textAnchor', 'vtextAnchor', 'fill', 'id', 'stroke', 'strokeWidth', 'backgroundImage', - 'backgroundFit', 'backgroundPosX', 'backgroundPosY', 'backgroundWidth', 'backgroundHeight', 'backgroundImageOpacity','backgroundOpacity' ]); - // specific to renderGroup - this.fontSize = params.fontSize; - this.fontFamily = params.fontFamily; - this.fontWeight = params.fontWeight; - this.fontStyle = params.fontStyle; - this.fontColor = params.fontColor; - this.textAnchor = params.textAnchor; // probably useless - this.vtextAnchor = params.vtextAnchor; // probably useless - // from GraphicalPrimitive2D - this.fill = params.fill; // fill color - // from GraphicalPrimitive1D - this.id = params.id; - this.stroke = params.stroke; // stroke color - this.strokeWidth = params.strokeWidth; - this.backgroundImage = params.backgroundImage; - this.backgroundFit = params.backgroundFit; - this.backgroundPosX = params.backgroundPosX; - this.backgroundPosY = params.backgroundPosY; - this.backgroundWidth = params.backgroundWidth; - this.backgroundHeight = params.backgroundHeight; - this.backgroundImageOpacity = params.backgroundImageOpacity; - this.backgroundOpacity = params.backgroundOpacity; -}; - -/** - * @return {Object} - xml2js formatted object - */ -RenderGroup.prototype.buildJsObj = function () { - var renderGroupObj = {}; - - // attributes - var attributes = {}; - if(this.id != null) { - attributes.id = this.id; - } - if(this.fontSize != null) { - attributes.fontSize = this.fontSize; - } - if(this.fontFamily != null) { - attributes.fontFamily = this.fontFamily; - } - if(this.fontWeight != null) { - attributes.fontWeight = this.fontWeight; - } - if(this.fontStyle != null) { - attributes.fontStyle = this.fontStyle; - } - if (this.fontColor != null) { - attributes.fontColor = this.fontColor; - } - if(this.textAnchor != null) { - attributes.textAnchor = this.textAnchor; - } - if(this.vtextAnchor != null) { - attributes.vtextAnchor = this.vtextAnchor; - } - if(this.stroke != null) { - attributes.stroke = this.stroke; - } - if(this.strokeWidth != null) { - attributes.strokeWidth = this.strokeWidth; - } - if(this.fill != null) { - attributes.fill = this.fill; - } - if(this.backgroundImage != null) { - attributes.backgroundImage = this.backgroundImage; - } - if(this.backgroundFit != null) { - attributes.backgroundFit = this.backgroundFit; - } - if(this.backgroundPosX != null) { - attributes.backgroundPosX = this.backgroundPosX; - } - if(this.backgroundPosY != null) { - attributes.backgroundPosY = this.backgroundPosY; - } - if(this.backgroundWidth != null) { - attributes.backgroundWidth = this.backgroundWidth; - } - if(this.backgroundHeight != null) { - attributes.backgroundHeight = this.backgroundHeight; - } - if(this.backgroundImageOpacity != null) { - attributes.backgroundImageOpacity = this.backgroundImageOpacity; - } - if(this.backgroundOpacity != null) { - attributes.backgroundOpacity = this.backgroundOpacity; - } - utils.addAttributes(renderGroupObj, attributes); - return renderGroupObj; -}; - -/** - * @return {string} - */ -RenderGroup.prototype.toXML = function () { - return utils.buildString({g: this.buildJsObj()}) -}; - -/** - * @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; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {RenderGroup} - */ -RenderGroup.fromObj = function (jsObj) { - if (typeof jsObj.g == 'undefined') { - throw new Error("Bad XML provided, expected tagName g, got: " + Object.keys(jsObj)[0]); - } - - var g = new ns.RenderGroup(); - jsObj = jsObj.g; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return g; - } - - 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.fontColor = attributes.fontColor || 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; - g.backgroundImage = attributes.backgroundImage || null; - g.backgroundFit = attributes.backgroundFit || null; - g.backgroundPosX = attributes.backgroundPosX || null; - g.backgroundPosY = attributes.backgroundPosY || null; - g.backgroundWidth = attributes.backgroundWidth || null; - g.backgroundHeight = attributes.backgroundHeight || null; - g.backgroundImageOpacity = attributes.backgroundImageOpacity || null; - g.backgroundOpacity = attributes.backgroundOpacity || null; - } - return g; -}; - -ns.RenderGroup = RenderGroup; -// ------- END RENDERGROUP ------- - -// ------- 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 - */ -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; -}; - -/** - * @param {RenderGroup} renderGroup - */ -Style.prototype.setRenderGroup = function (renderGroup) { - this.renderGroup = renderGroup; -}; - -/** - * @return {string[]} - */ -Style.prototype.getIdListAsArray = function () { - return this.idList.split(' '); -} - -/** - * @param {string[]} idArray - */ -Style.prototype.setIdListFromArray = function (idArray) { - this.idList = idArray.join(' '); -} - -/** - * 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 = {}; - - // attributes - var attributes = {}; - if(this.id != null) { - attributes.id = this.id; - } - if(this.name != null) { - attributes.name = this.name; - } - if(this.idList != null) { - attributes.idList = this.idList; - } - utils.addAttributes(styleObj, attributes); - - // children - if(this.renderGroup != null) { - styleObj.g = this.renderGroup.buildJsObj(); - } - return styleObj; -}; - -/** - * @return {string} - */ -Style.prototype.toXML = function () { - return utils.buildString({style: this.buildJsObj()}) -}; - -/** - * @param {String} string - * @return {Style} - */ -Style.fromXML = function (string) { - var style; - function fn (err, result) { - style = Style.fromObj(result); - }; - utils.parseString(string, fn); - return style; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Style} - */ -Style.fromObj = function (jsObj) { - if (typeof jsObj.style == 'undefined') { - throw new Error("Bad XML provided, expected tagName style, got: " + Object.keys(jsObj)[0]); - } - - var style = new ns.Style(); - jsObj = jsObj.style; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return style; - } - - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - style.id = attributes.id || null; - style.name = attributes.name || null; - style.idList = attributes.idList || null; - } - - // children - if(jsObj.g) { - var g = ns.RenderGroup.fromObj({g: jsObj.g[0]}); - style.setRenderGroup(g); - } - - return style; -}; - -ns.Style = Style; -// ------- END STYLE ------- - -// ------- LISTOFSTYLES ------- -/** - * Represents the <listOfStyles> element. - * @class - */ -var ListOfStyles = function() { - this.styles = []; -}; - -/** - * @param {Style} style - */ -ListOfStyles.prototype.addStyle = function (style) { - this.styles.push(style); -}; - -/** - * Convenience function returning a map of ids to their respective RenderGroup object, - * for all the styles. - * The style properties can then be directly accessed. Example: map[id].stroke - * @return {Object.} - */ -ListOfStyles.prototype.getStyleMap = function () { - var index = {}; - for(var i=0; i < this.styles.length; i++) { - var style = this.styles[i]; - var subIndex = style.getStyleMap(); - for(var id in subIndex) { - index[id] = subIndex[id]; - } - } - return index; -} - -/** - * @return {Object} - xml2js formatted object - */ -ListOfStyles.prototype.buildJsObj = function () { - var listOfStylesObj = {}; - - for(var i=0; i < this.styles.length; i++) { - if (i==0) { - listOfStylesObj.style = []; - } - listOfStylesObj.style.push(this.styles[i].buildJsObj()); - } - - return listOfStylesObj; -}; - -/** - * @return {string} - */ -ListOfStyles.prototype.toXML = function () { - return utils.buildString({listOfStyles: this.buildJsObj()}) -}; - -/** - * @param {String} string - * @return {ListOfStyles} - */ -ListOfStyles.fromXML = function (string) { - var listOfStyles; - function fn (err, result) { - listOfStyles = ListOfStyles.fromObj(result); - }; - utils.parseString(string, fn); - return listOfStyles; -}; - -/** - * @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 ------- -// ------- BACKGROUNDIMAGE ------- -/** - * Represents the <backgroundImage> element. - * @class - * @param {Object} params - * @param {string=} params.id - * @param {string=} params.value - */ -var BackgroundImage = function(params) { - var params = checkParams(params, ['id', 'value']); - this.id = params.id; - this.value = params.value; -}; - -/** - * @return {Object} - xml2js formatted object - */ -BackgroundImage.prototype.buildJsObj = function () { - var bgImgObj = {}; - - // attributes - var attributes = {}; - if(this.id != null) { - attributes.id = this.id; - } - if(this.value != null) { - attributes.value = this.value; - } - utils.addAttributes(bgImgObj, attributes); - return bgImgObj; -}; - -/** - * @return {string} - */ -BackgroundImage.prototype.toXML = function () { - return utils.buildString({backgroundImage: this.buildJsObj()}) -}; - -/** - * @param {String} string - * @return {BackgroundImage} - */ -BackgroundImage.fromXML = function (string) { - var backgroundImage; - function fn (err, result) { - backgroundImage = BackgroundImage.fromObj(result); - }; - utils.parseString(string, fn); - return backgroundImage; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {BackgroundImage} - */ -BackgroundImage.fromObj = function (jsObj) { - if (typeof jsObj.backgroundImage == 'undefined') { - throw new Error("Bad XML provided, expected tagName backgroundImage, got: " + Object.keys(jsObj)[0]); - } - - var backgroundImage = new ns.BackgroundImage(); - jsObj = jsObj.backgroundImage; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return backgroundImage; - } - - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - backgroundImage.id = attributes.id || null; - backgroundImage.value = attributes.value || null; - } - return backgroundImage; -}; - -ns.BackgroundImage = BackgroundImage; -// ------- END BACKGROUNDIMAGE ------- - -// ------- LISTOFBACKGROUNDIMAGES ------- -/** - * Represents the <listOfBackgroundImages> element. - * @class - */ -var ListOfBackgroundImages = function () { - this.backgroundImages = []; - this.imageIndex = {}; -}; - -/** - * @param {BackgroundImage} backgroundImage - */ -ListOfBackgroundImages.prototype.addBackgroundImage = function (backgroundImage) { - this.backgroundImages.push(backgroundImage); - this.imageIndex[backgroundImage.id] = backgroundImage.value; -}; - -/** - * Convenient method to get a background image value directly. - * @param {string} id - * @return {string} - */ -ListOfBackgroundImages.prototype.getBackgroundImageById = function (id) { - return this.imageIndex[id]; -}; - -/** - * Convenient method to get all the background image values in the list. - * @return {string[]} - */ -ListOfBackgroundImages.prototype.getAllImages = function () { - return Object.values(this.imageIndex); -}; - -/** - * @return {Object} - xml2js formatted object - */ -ListOfBackgroundImages.prototype.buildJsObj = function () { - var listOfBgImagesObj = {}; - - for(var i=0; i < this.backgroundImages.length; i++) { - if (i==0) { - listOfBgImagesObj.backgroundImage = []; - } - listOfBgImagesObj.backgroundImage.push(this.backgroundImages[i].buildJsObj()); - } - - return listOfBgImagesObj; -}; - -/** - * @return {string} - */ -ListOfBackgroundImages.prototype.toXML = function () { - return utils.buildString({listOfBackgroundImages: this.buildJsObj()}) -}; - -/** - * @param {String} string - * @return {ListOfBackgroundImages} - */ -ListOfBackgroundImages.fromXML = function (string) { - var listOfBackgroundImages; - function fn (err, result) { - listOfBackgroundImages = ListOfBackgroundImages.fromObj(result); - }; - utils.parseString(string, fn); - return listOfBackgroundImages; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {ListOfBackgroundImages} - */ -ListOfBackgroundImages.fromObj = function (jsObj) { - if (typeof jsObj.listOfBackgroundImages == 'undefined') { - throw new Error("Bad XML provided, expected tagName listOfBackgroundImages, got: " + Object.keys(jsObj)[0]); - } - - var listOfBackgroundImages = new ns.ListOfBackgroundImages(); - jsObj = jsObj.listOfBackgroundImages; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return listOfBackgroundImages; - } - - // children - if(jsObj.backgroundImage) { - var backgroundImages = jsObj.backgroundImage; - for (var i=0; i < backgroundImages.length; i++) { - var backgroundImage = ns.BackgroundImage.fromObj({backgroundImage: backgroundImages[i]}); - listOfBackgroundImages.addBackgroundImage(backgroundImage); - } - } - - return listOfBackgroundImages; -}; - -ns.ListOfBackgroundImages = ListOfBackgroundImages; -// ------- END LISTOFBACKGROUNDIMAGES ------- -// ------- RENDERINFORMATION ------- -/** - * Represents the <renderInformation> element. - * @class - * @param {Object} params - * @param {string=} params.id - * @param {string=} params.name - * @param {string=} params.programName - * @param {string=} params.programVersion - * @param {string=} params.backgroundColor - * @param {ListOfColorDefinitions=} params.listOfColorDefinitions - * @param {ListOfStyles=} params.listOfStyles - * @param {ListOfBackgroundImages=} params.listOfBackgroundImages - */ -var RenderInformation = function (params) { - var params = checkParams(params, ['id', 'name', 'programName', - 'programVersion', 'backgroundColor', 'listOfColorDefinitions', 'listOfStyles', 'listOfBackgroundImages']); - this.id = params.id; // required, rest is optional - this.name = params.name; - this.programName = params.programName; - this.programVersion = params.programVersion; - this.backgroundColor = params.backgroundColor; - this.listOfColorDefinitions = params.listOfColorDefinitions; - this.listOfStyles = params.listOfStyles; - this.listOfBackgroundImages = params.listOfBackgroundImages; -}; - -/** - * @param {ListOfColorDefinitions} listOfColorDefinitions - */ -RenderInformation.prototype.setListOfColorDefinitions = function(listOfColorDefinitions) { - this.listOfColorDefinitions = listOfColorDefinitions; -}; - -/** - * @param {ListOfStyles} listOfStyles - */ -RenderInformation.prototype.setListOfStyles = function(listOfStyles) { - this.listOfStyles = listOfStyles; -}; - -/** - * @param {ListOfBackgroundImages} listOfBackgroundImages - */ -RenderInformation.prototype.setListOfBackgroundImages = function(listOfBackgroundImages) { - this.listOfBackgroundImages = listOfBackgroundImages; -}; - -/** - * @return {Object} - xml2js formatted object - */ -RenderInformation.prototype.buildJsObj = function () { - var renderInformationObj = {}; - - // 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); - - // children - if(this.listOfColorDefinitions != null) { - renderInformationObj.listOfColorDefinitions = this.listOfColorDefinitions.buildJsObj(); - } - if(this.listOfBackgroundImages != null) { - renderInformationObj.listOfBackgroundImages = this.listOfBackgroundImages.buildJsObj(); - } - if(this.listOfStyles != null) { - renderInformationObj.listOfStyles = this.listOfStyles.buildJsObj(); - } - return renderInformationObj; -}; - -/** - * @return {string} - */ -RenderInformation.prototype.toXML = function() { - return utils.buildString({renderInformation: this.buildJsObj()}) -}; - -/** - * @param {String} string - * @return {RenderInformation} - */ -RenderInformation.fromXML = function (string) { - var renderInformation; - function fn (err, result) { - renderInformation = RenderInformation.fromObj(result); - }; - utils.parseString(string, fn); - return renderInformation; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {RenderInformation} - */ -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; - } - - 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; - } - - // 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); - } - if(jsObj.listOfBackgroundImages) { - var listOfBackgroundImages = ns.ListOfBackgroundImages.fromObj({listOfBackgroundImages: jsObj.listOfBackgroundImages[0]}); - renderInformation.setListOfBackgroundImages(listOfBackgroundImages); - } - return renderInformation; -}; - -ns.RenderInformation = RenderInformation; -// ------- END RENDERINFORMATION ------- - -module.exports = ns; -},{"./utilities":33,"xml2js":26}],31:[function(_dereq_,module,exports){ -/** - * The API contains two other submodules: {@link libsbgn.render} and {@link libsbgn.annot} - * @module libsbgn - * @namespace libsbgn -*/ - -var renderExt = _dereq_('./libsbgn-render'); -var annotExt = _dereq_('./libsbgn-annotations'); -var xml2js = _dereq_('xml2js'); -var utils = _dereq_('./utilities'); -var schematronValidator = _dereq_('./schematronValidator'); -var checkParams = utils.checkParams; - -var ns = {}; // namespace that encapsulates all exportable features - -ns.xmlns = "http://sbgn.org/libsbgn/0.3"; - -// ------- 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; -}; - -/** - * Allows inheriting objects to get an extension element. - * @param {Extension} extension - */ -SBGNBase.prototype.setExtension = function (extension) { - this.extension = extension; -}; - -/** - * Allows inheriting objects to get a notes element. - * @param {Notes} notes - */ -SBGNBase.prototype.setNotes = function (notes) { - this.notes = notes; -}; - -/** - * 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(); - } -}; - -/** - * Get the appropriate properties from jsObj. - * @param {Object} jsObj - xml2js formatted object - */ -SBGNBase.prototype.baseFromObj = function (jsObj) { - if (jsObj.extension) { - var extension = ns.Extension.fromObj({extension: jsObj.extension[0]}); - this.setExtension(extension); - } - if (jsObj.notes) { - var notes = ns.Notes.fromObj({notes: jsObj.notes[0]}); - this.setNotes(notes); - } -}; -ns.SBGNBase = SBGNBase; -// ------- END SBGNBase ------- - -// ------- SBGN ------- -/** - * Represents the <sbgn> element. - * @class - * @extends SBGNBase - * @param {Object} params - * @param {string=} params.xmlns - * @param {Map[]=} params.maps - */ -var Sbgn = function (params) { - ns.SBGNBase.call(this, params); - var params = checkParams(params, ['xmlns', 'maps']); - this.xmlns = params.xmlns; - this.maps = params.maps || []; -}; - -Sbgn.prototype = Object.create(ns.SBGNBase.prototype); -Sbgn.prototype.constructor = Sbgn; - -/** - * @param {Map} map - */ -Sbgn.prototype.addMap = function (map) { - this.maps.push(map); -}; - -/** - * @return {Object} - xml2js formatted object - */ -Sbgn.prototype.buildJsObj = function () { - var sbgnObj = {}; - - // attributes - var attributes = {}; - if(this.xmlns != null) { - attributes.xmlns = this.xmlns; - } - utils.addAttributes(sbgnObj, attributes); - - // 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; -}; - -/** - * @return {string} - */ -Sbgn.prototype.toXML = function () { - return utils.buildString({sbgn: this.buildJsObj()}); -}; - -/** - * @param {String} file - * @return {Issue[]} - */ -Sbgn.doValidation = function (file) { - return schematronValidator.doValidation(file); -}; - -/** - * @param {String} string - * @return {Sbgn} - */ -Sbgn.fromXML = function (string) { - var sbgn; - function fn (err, result) { - sbgn = Sbgn.fromObj(result); - } - 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; -}; -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 || []; -}; - -Map.prototype = Object.create(ns.SBGNBase.prototype); -Map.prototype.constructor = Map; - -/** - * @param {Glyph} glyph - */ -Map.prototype.addGlyph = function (glyph) { - this.glyphs.push(glyph); -}; - -/** - * @param {Arc} arc - */ -Map.prototype.addArc = function (arc) { - this.arcs.push(arc); -}; - -/** - * @param {Bbox} bbox - */ -Map.prototype.setBbox = function (bbox) { - this.bbox = bbox; -}; - -/** - * @param {Arcgroup} arc - */ -Map.prototype.addArcgroup = function (arcgroup) { - this.arcgroups.push(arcgroup); -}; - -/** - * @param {string} class_ - * @return {Gyph[]} - */ -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; -}; - -/** - * @return {Object} - xml2js formatted object - */ -Map.prototype.buildJsObj = function () { - var mapObj = {}; - - // 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); - - // 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; -}; - -/** - * @return {string} - */ -Map.prototype.toXML = function () { - return utils.buildString({map: this.buildJsObj()}); -}; - -/** - * @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; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Map} - */ -Map.fromObj = function (jsObj) { - if (typeof jsObj.map == 'undefined') { - throw new Error("Bad XML provided, expected tagName map, got: " + Object.keys(jsObj)[0]); - } - - var map = new ns.Map(); - jsObj = jsObj.map; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return map; - } - - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - map.id = attributes.id || null; - map.language = attributes.language || null; - map.version = attributes.version || null; - } - - if(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); - } - } - - map.baseFromObj(jsObj); - return map; -}; - -ns.Map = Map; -// ------- END MAP ------- - -// ------- 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 = {}; -}; - -/** - * @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; - } - } -}; - -/** - * @param {string} extensionName - * @return {boolean} - */ -Extension.prototype.has = function (extensionName) { - return this.list.hasOwnProperty(extensionName); -}; - -/** - * @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; - } -}; - -/** - * @return {Object} - xml2js formatted object - */ -Extension.prototype.buildJsObj = function () { - var extensionObj = {}; - - 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; -}; - -/** - * @return {string} - */ -Extension.prototype.toXML = function () { - return utils.buildString({extension: this.buildJsObj()}) -}; - -/** - * @param {String} string - * @return {Extension} - */ -Extension.fromXML = function (string) { - var extension; - function fn (err, result) { - extension = Extension.fromObj(result); - }; - utils.parseString(string, fn); - return extension; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Extension} - */ -Extension.fromObj = function (jsObj) { - if (typeof jsObj.extension == 'undefined') { - throw new Error("Bad XML provided, expected tagName extension, got: " + Object.keys(jsObj)[0]); - } - - var extension = new Extension(); - jsObj = jsObj.extension; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return extension; - } - - //var children = Object.keys(jsObj); - for (var extName in jsObj) { - //var extName = Object.keys(jsObj[i])[0]; - var extJsObj = jsObj[extName]; - - //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 extension; -}; - -ns.Extension = Extension; -// ------- END EXTENSION ------- - -// ------- NOTES ------- -/** - * Represents the <notes> element. - * Its single content attribute stores xhtml elements as string. - * @class - */ -var Notes = function () { - this.content = ""; -}; - -/** - * Overwrite the content. - * @param {String} string - */ -Notes.prototype.setContent = function (string) { - this.content = string; -}; - -/** - * @param {String} string - */ -Notes.prototype.appendContent = function (string) { - this.content += string; -}; - -/** - * @return {Object} - xml2js formatted object - */ -Notes.prototype.buildJsObj = function () { - - var parsedContent = ""; - if(this.content != "") { // xml2js refuses to parse empty strings - utils.parseString(this.content, function (err, result) { - parsedContent = result; - }); - } - - return parsedContent; -}; - -/** - * @return {string} - */ -Notes.prototype.toXML = function () { - return utils.buildString({notes: this.buildJsObj()}) -}; - -/** - * @param {String} string - * @return {Notes} - */ -Notes.fromXML = function (string) { - var notes; - function fn (err, result) { - notes = Notes.fromObj(result); - }; - utils.parseString(string, fn); - return notes; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Notes} - */ -Notes.fromObj = function (jsObj) { - if (typeof jsObj.notes == 'undefined') { - throw new Error("Bad XML provided, expected tagName notes, got: " + Object.keys(jsObj)[0]); - } - - var notes = new Notes(); - jsObj = jsObj.notes; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return notes; - } - - 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 - - return notes; -}; - -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 {State=} params.state - * @param {Clone=} params.clone - * @param {Callout=} params.callout - * @param {Entity=} params.entity - * @param {Glyph[]=} params.glyphMembers - * @param {Port[]=} params.ports - */ -var Glyph = function (params) { - ns.SBGNBase.call(this, params); - var params = checkParams(params, ['id', 'class_', 'compartmentRef', 'compartmentOrder', 'mapRef', - 'tagRef', 'orientation', 'label', 'bbox', 'glyphMembers', 'ports', 'state', 'clone', 'entity', 'callout']); - this.id = params.id; - this.class_ = params.class_; - this.compartmentRef = params.compartmentRef; - this.compartmentOrder = parseFloat(params.compartmentOrder); - this.mapRef = params.mapRef; - this.tagRef = params.tagRef; - this.orientation = params.orientation; - - // 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 || []; -}; - -Glyph.prototype = Object.create(ns.SBGNBase.prototype); -Glyph.prototype.constructor = Glyph; - -/** - * @param {Label} label - */ -Glyph.prototype.setLabel = function (label) { - this.label = label; -}; - -/** - * @param {State} state - */ -Glyph.prototype.setState = function (state) { - this.state = state; -}; - -/** - * @param {Bbox} bbox - */ -Glyph.prototype.setBbox = function (bbox) { - this.bbox = bbox; -}; - -/** - * @param {Clone} clone - */ -Glyph.prototype.setClone = function (clone) { - this.clone = clone; -}; - -/** - * @param {Callout} callout - */ -Glyph.prototype.setCallout = function (callout) { - this.callout = callout; -}; - -/** - * @param {Entity} entity - */ -Glyph.prototype.setEntity = function (entity) { - this.entity = entity; -}; - -/** - * @param {Glyph} glyphMember - */ -Glyph.prototype.addGlyphMember = function (glyphMember) { - this.glyphMembers.push(glyphMember); -}; - -/** - * @param {Port} port - */ -Glyph.prototype.addPort = function (port) { - this.ports.push(port); -}; - -/** - * @return {Object} - xml2js formatted object - */ -Glyph.prototype.buildJsObj = function () { - var glyphObj = {}; - - // 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); - - // 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; -}; - -/** - * @return {string} - */ -Glyph.prototype.toXML = function () { - return utils.buildString({glyph: this.buildJsObj()}) -}; - -/** - * @param {String} string - * @return {Glyph} - */ -Glyph.fromXML = function (string) { - var glyph; - function fn (err, result) { - glyph = Glyph.fromObj(result); - }; - utils.parseString(string, fn); - return glyph; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Glyph} - */ -Glyph.fromObj = function (jsObj) { - if (typeof jsObj.glyph == 'undefined') { - throw new Error("Bad XML provided, expected tagName glyph, got: " + Object.keys(jsObj)[0]); - } - - var 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; - } - - // children - if(jsObj.label) { - var label = ns.Label.fromObj({label: jsObj.label[0]}); - glyph.setLabel(label); - } - if(jsObj.state) { - var state = ns.State.fromObj({state: jsObj.state[0]}); - glyph.setState(state); - } - if(jsObj.clone) { - var clone = ns.Clone.fromObj({clone: jsObj.clone[0]}); - glyph.setClone(clone); - } - if(jsObj.callout) { - var callout = ns.Callout.fromObj({callout: jsObj.callout[0]}); - glyph.setCallout(callout); - } - if(jsObj.entity) { - var entity = ns.Entity.fromObj({entity: jsObj.entity[0]}); - glyph.setEntity(entity); - } - if(jsObj.bbox) { - var bbox = ns.Bbox.fromObj({bbox: jsObj.bbox[0]}); - glyph.setBbox(bbox); - } - - 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); - } - } - - glyph.baseFromObj(jsObj); - return glyph; -}; - -ns.Glyph = Glyph; -// ------- END GLYPH ------- - -// ------- 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; -}; - -Label.prototype = Object.create(ns.SBGNBase.prototype); -Label.prototype.constructor = ns.Label; - -/** - * @param {Bbox} bbox - */ -Label.prototype.setBbox = function (bbox) { - this.bbox = bbox; -}; - -/** - * @return {Object} - xml2js formatted object - */ -Label.prototype.buildJsObj = function () { - var labelObj = {}; - - // 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); - - this.baseToJsObj(labelObj); - if(this.bbox != null) { - labelObj.bbox = this.bbox.buildJsObj(); - } - return labelObj; -}; - -/** - * @return {string} - */ -Label.prototype.toXML = function () { - return utils.buildString({label: this.buildJsObj()}) -}; - -/** - * @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; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Label} - */ -Label.fromObj = function (jsObj) { - if (typeof jsObj.label == 'undefined') { - throw new Error("Bad XML provided, expected tagName label, got: " + Object.keys(jsObj)[0]); - } - - var label = new ns.Label(); - jsObj = jsObj.label; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return label; - } - - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - label.text = attributes.text || null; - } - - if(jsObj.bbox) { - var bbox = ns.Bbox.fromObj({bbox: jsObj.bbox[0]}); - label.setBbox(bbox); - } - label.baseFromObj(jsObj); - return label; -}; - -ns.Label = Label; -// ------- END LABEL ------- - -// ------- 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); -}; - -Bbox.prototype = Object.create(ns.SBGNBase.prototype); -Bbox.prototype.constructor = ns.Bbox; - -/** - * @return {Object} - xml2js formatted object - */ -Bbox.prototype.buildJsObj = function () { - var bboxObj = {}; - - // attributes - var attributes = {}; - if(!isNaN(this.x)) { - attributes.x = this.x; - } - if(!isNaN(this.y)) { - attributes.y = this.y; - } - if(!isNaN(this.w)) { - attributes.w = this.w; - } - if(!isNaN(this.h)) { - attributes.h = this.h; - } - utils.addAttributes(bboxObj, attributes); - this.baseToJsObj(bboxObj); - return bboxObj; -}; - -/** - * @return {string} - */ -Bbox.prototype.toXML = function () { - return utils.buildString({bbox: this.buildJsObj()}) -}; - -/** - * @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; -}; - -/** - * @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]); - } - - var bbox = new ns.Bbox(); - jsObj = jsObj.bbox; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return bbox; - } - - 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; -}; - -ns.Bbox = Bbox; -// ------- END BBOX ------- - -// ------- STATE ------- -/** - * Represents the <state> element. - * @class State - * @param {Object} params - * @param {string=} params.value - * @param {string=} params.variable - */ -var State = function (params) { - var params = checkParams(params, ['value', 'variable']); - this.value = params.value; - this.variable = params.variable; -}; - -/** - * @return {Object} - xml2js formatted object - */ -State.prototype.buildJsObj = function () { - var stateObj = {}; - - // 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 {string} - */ -State.prototype.toXML = function () { - return utils.buildString({state: this.buildJsObj()}) -}; - -/** - * @param {String} string - * @return {State} - */ -State.fromXML = function (string) { - var state; - function fn (err, result) { - state = State.fromObj(result); - }; - utils.parseString(string, fn); - return state; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {State} - */ -State.fromObj = function (jsObj) { - if (typeof jsObj.state == 'undefined') { - throw new Error("Bad XML provided, expected tagName state, got: " + Object.keys(jsObj)[0]); - } - - var state = new ns.State(); - jsObj = jsObj.state; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return state; - } - - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - state.value = attributes.value || null; - state.variable = attributes.variable || null; - } - return state; -}; - -ns.State = State; -/** - * @class StateType - * @deprecated Replaced by State - */ -ns.StateType = State; -// ------- END STATE ------- - -// ------- CLONE ------- -/** - * Represents the <clone> element. - * @class Clone - * @param {Object} params - * @param {string=} params.label - */ -var Clone = function (params) { - var params = checkParams(params, ['label']); - this.label = params.label; -}; - -/** - * @param {Label} label - */ -Clone.prototype.setLabel = function (label) { - this.label = label; -}; - -/** - * @return {Object} - xml2js formatted object - */ -Clone.prototype.buildJsObj = function () { - var cloneObj = {}; - - // children - if(this.label != null) { - cloneObj.label = this.label.buildJsObj(); - } - return cloneObj; -}; - -/** - * @return {string} - */ -Clone.prototype.toXML = function () { - return utils.buildString({clone: this.buildJsObj()}) -}; - -/** - * @param {String} string - * @return {Clone} - */ -Clone.fromXML = function (string) { - var clone; - function fn (err, result) { - clone = Clone.fromObj(result); - }; - utils.parseString(string, fn); - return clone; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Clone} - */ -Clone.fromObj = function (jsObj) { - if (typeof jsObj.clone == 'undefined') { - throw new Error("Bad XML provided, expected tagName clone, got: " + Object.keys(jsObj)[0]); - } - - var clone = new ns.Clone(); - jsObj = jsObj.clone; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return clone; - } - - // children - if(jsObj.label) { - var label = ns.Label.fromObj({label: jsObj.label[0]}); - clone.setLabel(label); - } - return clone; -}; - -ns.Clone = Clone; -/** - * @class CloneType - * @deprecated Replaced by Clone - */ -ns.CloneType = Clone; -// ------- END CLONE ------- - -// ------- ENTITYTYPE ------- -/** - * Represents the <entity> element. - * @class Entity - * @param {Object} params - * @param {string=} params.name - */ -var Entity = function (params) { - var params = checkParams(params, ['name']); - this.name = params.name; -}; - -/** - * @return {Object} - xml2js formatted object - */ -Entity.prototype.buildJsObj = function () { - var entityObj = {}; - - // attributes - var attributes = {}; - if(this.name != null) { - attributes.name = this.name; - } - utils.addAttributes(entityObj, attributes); - return entityObj; -}; - -/** - * @return {string} - */ -Entity.prototype.toXML = function () { - return utils.buildString({entity: this.buildJsObj()}) -}; - -/** - * @param {String} string - * @return {Entity} - */ -Entity.fromXML = function (string) { - var entity; - function fn (err, result) { - entity = Entity.fromObj(result); - }; - utils.parseString(string, fn); - return entity; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Entity} - */ -Entity.fromObj = function (jsObj) { - if (typeof jsObj.entity == 'undefined') { - throw new Error("Bad XML provided, expected tagName entity, got: " + Object.keys(jsObj)[0]); - } - - var entity = new ns.Entity(); - jsObj = jsObj.entity; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return entity; - } - - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - entity.name = attributes.name || null; - } - return entity; -}; - -ns.Entity = Entity; -/** - * @class EntityType - * @deprecated Replaced by Entity - */ -ns.EntityType = Entity; -// ------- END ENTITYTYPE ------- - -// ------- 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); -}; - -Port.prototype = Object.create(ns.SBGNBase.prototype); -Port.prototype.constructor = ns.Port; - -/** - * @return {Object} - xml2js formatted object - */ -Port.prototype.buildJsObj = function () { - var portObj = {}; - - // 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; -}; - -/** - * @return {string} - */ -Port.prototype.toXML = function () { - return utils.buildString({port: this.buildJsObj()}) -}; - -/** - * @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; -}; - -/** - * @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]); - } - - var port = new ns.Port(); - jsObj = jsObj.port; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return port; - } - - 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; -}; - -ns.Port = Port; -// ------- END PORT ------- - -// ------- ARC ------- -/** - * Represents the <arc> element. - * @class Arc - * @param {Object} params - * @param {string=} params.id - * @param {string=} params.class_ - * @param {string=} params.source - * @param {string=} params.target - * @param {Start=} params.start - * @param {End=} params.end - * @param {Next=} params.nexts - * @param {Glyph[]=} params.glyphs The arc's cardinality. Possibility to have more than one glyph is left open. - */ -var Arc = function (params) { - ns.SBGNBase.call(this, params); - var params = checkParams(params, ['id', 'class_', 'source', 'target', 'start', 'end', 'nexts', 'glyphs']); - this.id = params.id; - this.class_ = params.class_; - this.source = params.source; - this.target = params.target; - - this.start = params.start; - this.end = params.end; - this.nexts = params.nexts || []; - this.glyphs = params.glyphs || []; -}; - -Arc.prototype = Object.create(ns.SBGNBase.prototype); -Arc.prototype.constructor = ns.Arc; - -/** - * @param {Start} start - */ -Arc.prototype.setStart = function (start) { - this.start = start; -}; - -/** - * @param {End} end - */ -Arc.prototype.setEnd = function (end) { - this.end = end; -}; - -/** - * @param {Next} next - */ -Arc.prototype.addNext = function (next) { - this.nexts.push(next); -}; - -/** - * @param {Glyph} glyph - */ -Arc.prototype.addGlyph = function (glyph) { - this.glyphs.push(glyph); -}; - -/** - * @return {Object} - xml2js formatted object - */ -Arc.prototype.buildJsObj = function () { - var arcObj = {}; - - // 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); - - // 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; -}; - -/** - * @return {string} - */ -Arc.prototype.toXML = function () { - return utils.buildString({arc: this.buildJsObj()}) -}; - -/** - * @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} - */ -Arc.fromObj = function (jsObj) { - if (typeof jsObj.arc == 'undefined') { - throw new Error("Bad XML provided, expected tagName arc, got: " + Object.keys(jsObj)[0]); - } - - var arc = new ns.Arc(); - jsObj = jsObj.arc; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return arc; - } - - 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; - } - - // children - if(jsObj.start) { - var start = ns.Start.fromObj({start: jsObj.start[0]}); - arc.setStart(start); - } - if(jsObj.next) { - var nexts = jsObj.next; - for (var i=0; i < nexts.length; i++) { - var next = ns.Next.fromObj({next: nexts[i]}); - arc.addNext(next); - } - } - if(jsObj.end) { - var end = ns.End.fromObj({end: jsObj.end[0]}); - arc.setEnd(end); - } - if(jsObj.glyph) { - var glyphs = jsObj.glyph; - for (var i=0; i < glyphs.length; i++) { - var glyph = ns.Glyph.fromObj({glyph: glyphs[i]}); - arc.addGlyph(glyph); - } - } - - arc.baseFromObj(jsObj); - return arc; -}; - -ns.Arc = Arc; -// ------- END ARC ------- - -// ------- Start ------- -/** - * Represents the <start> element. - * @class Start - * @param {Object} params - * @param {string|number=} params.x - * @param {string|number=} params.y - */ -var Start = function (params) { - var params = checkParams(params, ['x', 'y']); - this.x = parseFloat(params.x); - this.y = parseFloat(params.y); -}; - -/** - * @return {Object} - xml2js formatted object - */ -Start.prototype.buildJsObj = function () { - var startObj = {}; - - // 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; -}; - -/** - * @return {string} - */ -Start.prototype.toXML = function () { - return utils.buildString({start: this.buildJsObj()}) -}; - -/** - * @param {String} string - * @return {Start} - */ -Start.fromXML = function (string) { - var start; - function fn (err, result) { - start = Start.fromObj(result); - }; - utils.parseString(string, fn); - return start; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Start} - */ -Start.fromObj = function (jsObj) { - if (typeof jsObj.start == 'undefined') { - throw new Error("Bad XML provided, expected tagName start, got: " + Object.keys(jsObj)[0]); - } - - var start = new ns.Start(); - jsObj = jsObj.start; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return start; - } - - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - start.x = parseFloat(attributes.x); - start.y = parseFloat(attributes.y); - } - return start; -}; - -ns.Start = Start; -/** - * @class StartType - * @deprecated Replaced by Start - */ -ns.StartType = Start; -// ------- END Start ------- - -// ------- End ------- -/** - * Represents the <end> element. - * @class End - * @param {Object} params - * @param {string|number=} params.x - * @param {string|number=} params.y - */ -var End = function (params) { - var params = checkParams(params, ['x', 'y']); - this.x = parseFloat(params.x); - this.y = parseFloat(params.y); -}; - -/** - * @return {Object} - xml2js formatted object - */ -End.prototype.buildJsObj = function () { - var endObj = {}; - - // 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; -}; - -/** - * @return {string} - */ -End.prototype.toXML = function () { - return utils.buildString({end: this.buildJsObj()}) -}; - -/** - * @param {String} string - * @return {End} - */ -End.fromXML = function (string) { - var end; - function fn (err, result) { - end = End.fromObj(result); - }; - utils.parseString(string, fn); - return end; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {End} - */ -End.fromObj = function (jsObj) { - if (typeof jsObj.end == 'undefined') { - throw new Error("Bad XML provided, expected tagName end, got: " + Object.keys(jsObj)[0]); - } - - var end = new ns.End(); - 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.End = End; -/** - * @class EndType - * @deprecated Replaced by End - */ -ns.EndType = End; -// ------- END End ------- - -// ------- Next ------- -/** - * Represents the <next> element. - * @class Next - * @param {Object} params - * @param {string|number=} params.x - * @param {string|number=} params.y - */ -var Next = function (params) { - var params = checkParams(params, ['x', 'y']); - this.x = parseFloat(params.x); - this.y = parseFloat(params.y); -}; - -/** - * @return {Object} - xml2js formatted object - */ -Next.prototype.buildJsObj = function () { - var nextObj = {}; - - // 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; -}; - -/** - * @return {string} - */ -Next.prototype.toXML = function () { - return utils.buildString({next: this.buildJsObj()}) -}; - -/** - * @param {String} string - * @return {Next} - */ -Next.fromXML = function (string) { - var next; - function fn (err, result) { - next = Next.fromObj(result); - }; - utils.parseString(string, fn); - return next; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Next} - */ -Next.fromObj = function (jsObj) { - if (typeof jsObj.next == 'undefined') { - throw new Error("Bad XML provided, expected tagName next, got: " + Object.keys(jsObj)[0]); - } - - var next = new ns.Next(); - jsObj = jsObj.next; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return next; - } - - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - next.x = parseFloat(attributes.x); - next.y = parseFloat(attributes.y); - } - return next; -}; - -ns.Next = Next; -/** - * @class NextType - * @deprecated Replaced by Next - */ -ns.NextType = Next; -// ------- END Next ------- - -// ------- 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; - -/** - * @return {Object} - xml2js formatted object - */ -Point.prototype.buildJsObj = function () { - var pointJsObj = {}; - - // 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; -}; - -/** - * @return {string} - */ -Point.prototype.toXML = function () { - return utils.buildString({point: this.buildJsObj()}) -}; - -/** - * @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]); - } - - var point = new ns.Point(); - jsObj = jsObj.point; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return point; - } - - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - point.x = parseFloat(attributes.x); - point.y = parseFloat(attributes.y); - } - point.baseFromObj(jsObj); - return point; -}; - -ns.Point = Point; -// ------- END POINT ------- - -// ------- 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; -}; - -/** - * @param {Point} point - */ -Callout.prototype.setPoint = function(point) { - this.point = point; -}; - -/** - * @return {Object} - xml2js formatted object - */ -Callout.prototype.buildJsObj = function () { - var calloutObj = {}; - - // attributes - var attributes = {}; - if(this.target != null) { - attributes.target = this.target; - } - utils.addAttributes(calloutObj, attributes); - - // children - if(this.point != null) { - calloutObj.point = this.point.buildJsObj(); - } - return calloutObj; -}; - -/** - * @return {string} - */ -Callout.prototype.toXML = function () { - return utils.buildString({callout: this.buildJsObj()}) -}; - -/** - * @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; -}; - -/** - * @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]); - } - - var callout = new ns.Callout(); - jsObj = jsObj.callout; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return callout; - } - - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - callout.target = attributes.target || null; - } - - // children - if(jsObj.point) { - var point = ns.Point.fromObj({point: jsObj.point[0]}); - callout.setPoint(point); - } - return callout; -}; - -ns.Callout = Callout; -// ------- END CALLOUT ------- - -// ------- ARCGROUP ------- -/** - * Represents the <arcgroup> element. - * @class - * @extends SBGNBase - * @param {Object} params - * @param {string=} params.class_ - * @param {Glyph[]=} params.glyphs - * @param {Arc[]=} params.arcs - */ -var Arcgroup = function (params) { - ns.SBGNBase.call(this, params); - var params = checkParams(params, ['class_', 'glyphs', 'arcs']); - this.class_ = params.class_; - this.glyphs = params.glyphs || []; - this.arcs = params.arcs || []; -}; - -Arcgroup.prototype = Object.create(ns.SBGNBase.prototype); -Arcgroup.prototype.constructor = Arcgroup; - -/** - * @param {Glyph} glyph - */ -Arcgroup.prototype.addGlyph = function (glyph) { - this.glyphs.push(glyph); -}; - -/** - * @param {Arc} arc - */ -Arcgroup.prototype.addArc = function (arc) { - this.arcs.push(arc); -}; - -/** - * @return {Object} - xml2js formatted object - */ -Arcgroup.prototype.buildJsObj = function () { - var arcgroupObj = {}; - - // attributes - var attributes = {}; - if(this.class_ != null) { - attributes.class = this.class_; - } - utils.addAttributes(arcgroupObj, attributes); - - // 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; -}; - -/** - * @return {string} - */ -Arcgroup.prototype.toXML = function () { - return utils.buildString({arcgroup: this.buildJsObj()}); -}; - -/** - * @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; -}; - -/** - * @param {Object} jsObj - xml2js formatted object - * @return {Arcgroup} - */ -Arcgroup.fromObj = function (jsObj) { - if (typeof jsObj.arcgroup == 'undefined') { - throw new Error("Bad XML provided, expected tagName arcgroup, got: " + Object.keys(jsObj)[0]); - } - - var arcgroup = new ns.Arcgroup(); - jsObj = jsObj.arcgroup; - if(typeof jsObj != 'object') { // nothing inside, empty xml - return arcgroup; - } - - if(jsObj.$) { // we have some attributes - var attributes = jsObj.$; - arcgroup.class_ = attributes.class || null; - } - - if(jsObj.glyph) { - var glyphs = jsObj.glyph; - for (var i=0; i < glyphs.length; i++) { - var glyph = ns.Glyph.fromObj({glyph: glyphs[i]}); - arcgroup.addGlyph(glyph); - } - } - if(jsObj.arc) { - var arcs = jsObj.arc; - for (var i=0; i < arcs.length; i++) { - var arc = ns.Arc.fromObj({arc: arcs[i]}); - arcgroup.addArc(arc); - } - } - - arcgroup.baseFromObj(jsObj); - return arcgroup; -}; - -ns.Arcgroup = Arcgroup; -// ------- END ARCGROUP ------- - -ns.render = renderExt; -ns.annot = annotExt; -ns.schematronValidator = schematronValidator; -module.exports = ns; - - },{"./libsbgn-annotations":29,"./libsbgn-render":30,"./schematronValidator":32,"./utilities":33,"xml2js":26}],32:[function(_dereq_,module,exports){ var ns = {}; @@ -17673,108 +17691,139 @@ function loadXMLDoc(filename) module.exports = ns; },{"./Issue":27,"xml2js":26}],33:[function(_dereq_,module,exports){ -var ns = {}; -var xml2js = _dereq_('xml2js'); - -/* - 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; -} - -ns.getFirstLevelByName = function (xmlObj, localName) { - var result = []; - for(var i=0; i 0 && len > maxKeys) { - len = maxKeys; - } + var len = qs.length; + // maxKeys <= 0 means that we should not limit keys count + if (maxKeys > 0 && len > maxKeys) { + len = maxKeys; + } + + for (var i = 0; i < len; ++i) { + var x = qs[i].replace(regexp, '%20'), + idx = x.indexOf(eq), + kstr, vstr, k, v; + + if (idx >= 0) { + kstr = x.substr(0, idx); + vstr = x.substr(idx + 1); + } else { + kstr = x; + vstr = ''; + } + + k = decodeURIComponent(kstr); + v = decodeURIComponent(vstr); + + if (!hasOwnProperty(obj, k)) { + obj[k] = v; + } else if (isArray(obj[k])) { + obj[k].push(v); + } else { + obj[k] = [obj[k], v]; + } + } + + return obj; +}; + +var isArray = Array.isArray || function (xs) { + return Object.prototype.toString.call(xs) === '[object Array]'; +}; + +},{}],49:[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 ''; + } +}; + +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); + + } + + 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]'; +}; + +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; +}; + +},{}],50:[function(_dereq_,module,exports){ +'use strict'; + +exports.decode = exports.parse = _dereq_('./decode'); +exports.encode = exports.stringify = _dereq_('./encode'); + +},{"./decode":48,"./encode":49}],51:[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":52,"./node":68}],52:[function(_dereq_,module,exports){ +'use strict'; + +var ClassOrder = { + 'Literal': 1, + 'Collection': 3, + 'Graph': 4, + 'NamedNode': 5, + 'BlankNode': 6, + 'Variable': 7 +}; + +module.exports = ClassOrder; +},{}],53:[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, [{ + key: 'append', + value: function append(element) { + return this.elements.push(element); + } + }, { + key: 'close', + value: function close() { + this.closed = true; + return this.closed; + } + }, { + key: 'shift', + value: function shift() { + return this.elements.shift(); + } + }, { + key: 'substitute', + value: function substitute(bindings) { + var elementsCopy = this.elements.map(function (ea) { + ea.substitute(bindings); + }); + return new Collection(elementsCopy); + } + }, { + key: 'toNT', + value: function toNT() { + return BlankNode.NTAnonymousNodePrefix + this.id; + } + }, { + key: 'toString', + value: function toString() { + return '(' + this.elements.join(' ') + ')'; + } + }, { + key: 'unshift', + value: function unshift(element) { + return this.elements.unshift(element); + } + }]); + + return Collection; +}(Node); + +Collection.termType = 'Collection'; +Collection.prototype.classOrder = ClassOrder['Collection']; +Collection.prototype.compareTerm = BlankNode.prototype.compareTerm; +Collection.prototype.isVar = 0; + +module.exports = Collection; +},{"./blank-node":51,"./class-order":52,"./node":68}],54:[function(_dereq_,module,exports){ +'use strict'; + +module.exports.convertToJson = convertToJson; +module.exports.convertToNQuads = convertToNQuads; + +var asyncLib = _dereq_('async'); // @@ Goal: remove this dependency +var jsonld = _dereq_('jsonld'); +var N3 = _dereq_('n3'); // @@ Goal: remove this dependency + +function convertToJson(n3String, jsonCallback) { + var jsonString; + var n3Parser = N3.Parser(); + var n3Writer = N3.Writer({ + format: 'N-Quads' + }); + asyncLib.waterfall([function (callback) { + n3Parser.parse(n3String, callback); + }, function (triple, prefix, callback) { + if (triple !== null) { + n3Writer.addTriple(triple); + } + if (typeof callback === 'function') { + n3Writer.end(callback); + } + }, function (result, callback) { + try { + jsonld.fromRDF(result, { + format: 'application/nquads' + }, callback); + } catch (err) { + callback(err); + } + }, function (json, callback) { + jsonString = JSON.stringify(json); + jsonCallback(null, jsonString); + }], function (err, result) { + jsonCallback(err, jsonString); + }); +} + +function convertToNQuads(n3String, nquadCallback) { + var nquadString; + var n3Parser = N3.Parser(); + var n3Writer = N3.Writer({ + format: 'N-Quads' + }); + asyncLib.waterfall([function (callback) { + n3Parser.parse(n3String, callback); + }, function (triple, prefix, callback) { + if (triple !== null) { + n3Writer.addTriple(triple); + } + if (typeof callback === 'function') { + n3Writer.end(callback); + } + }, function (result, callback) { + nquadString = result; + nquadCallback(null, nquadString); + }], function (err, result) { + nquadCallback(err, nquadString); + }); +} +},{"async":1,"jsonld":19,"n3":85}],55:[function(_dereq_,module,exports){ +'use strict'; + +var _indexedFormula = _dereq_('./indexed-formula'); + +var _indexedFormula2 = _interopRequireDefault(_indexedFormula); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var BlankNode = _dereq_('./blank-node'); +var Collection = _dereq_('./collection'); +var DefaultGraph = _dereq_('./default-graph'); +var Fetcher = _dereq_('./fetcher'); + +var Literal = _dereq_('./literal'); +var NamedNode = _dereq_('./named-node'); +var Statement = _dereq_('./statement'); +var Variable = _dereq_('./variable'); + +function blankNode(value) { + return new BlankNode(value); +} +function collection(elements) { + return new Collection(elements); +} +function defaultGraph() { + return new DefaultGraph(); +} +function fetcher(store, timeout, async) { + return new Fetcher(store, timeout, async); +} +function graph() { + return new _indexedFormula2.default(); +} +function lit(val, lang, dt) { + return new Literal('' + val, lang, dt); +} +function literal(value, languageOrDatatype) { + if (typeof languageOrDatatype === 'string') { + if (languageOrDatatype.indexOf(':') === -1) { + return new Literal(value, languageOrDatatype); + } else { + return new Literal(value, null, namedNode(languageOrDatatype)); + } + } else { + return new Literal(value, null, languageOrDatatype); + } +} +function namedNode(value) { + return new NamedNode(value); +} +function quad(subject, predicate, object, graph) { + graph = graph || new DefaultGraph(); + return new Statement(subject, predicate, object, graph); +} +function st(subject, predicate, object, graph) { + return new Statement(subject, predicate, object, graph); +} +function triple(subject, predicate, object) { + return quad(subject, predicate, object); +} +function variable(name) { + return new Variable(name); +} + +// rdfjs spec factory methods +module.exports.blankNode = blankNode; +module.exports.defaultGraph = defaultGraph; +module.exports.graph = graph; +module.exports.literal = literal; +module.exports.namedNode = namedNode; +module.exports.quad = quad; +module.exports.triple = triple; +module.exports.variable = variable; + +// rdflib only +module.exports.collection = collection; +module.exports.fetcher = fetcher; +module.exports.lit = lit; +module.exports.st = st; +},{"./blank-node":51,"./collection":53,"./default-graph":56,"./fetcher":58,"./indexed-formula":61,"./literal":63,"./named-node":66,"./statement":78,"./variable":83}],56:[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 Node = _dereq_('./node'); + +var DefaultGraph = function (_Node) { + _inherits(DefaultGraph, _Node); + + function DefaultGraph() { + _classCallCheck(this, DefaultGraph); + + var _this = _possibleConstructorReturn(this, (DefaultGraph.__proto__ || Object.getPrototypeOf(DefaultGraph)).call(this)); + + _this.termType = 'DefaultGraph'; + _this.value = ''; + return _this; + } + + _createClass(DefaultGraph, [{ + key: 'toCanonical', + value: function toCanonical() { + return this.value; + } + }]); + + return DefaultGraph; +}(Node); + +module.exports = DefaultGraph; +},{"./node":68}],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 Node = _dereq_('./node'); + +/** + * Singleton subclass of an empty Collection. + */ + +var Empty = function (_Node) { + _inherits(Empty, _Node); + + function Empty() { + _classCallCheck(this, Empty); + + var _this = _possibleConstructorReturn(this, (Empty.__proto__ || Object.getPrototypeOf(Empty)).call(this)); + + _this.termType = Empty.termType; + return _this; + } + + _createClass(Empty, [{ + key: 'toString', + value: function toString() { + return '()'; + } + }]); + + return Empty; +}(Node); + +Empty.termType = 'empty'; + +module.exports = Empty; +},{"./node":68}],58:[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; }; + +/* global $SolidTestEnvironment */ +/** + * + * Project: rdflib.js + * + * File: fetcher.js + * + * Description: contains functions for requesting/fetching/retracting + * This implements quite a lot of the web architecture. + * A fetcher is bound to a specific knowledge base graph, into which + * it loads stuff and into which it writes its metadata + * @@ The metadata should be optionally a separate graph + * + * - implements semantics of HTTP headers, Internet Content Types + * - selects parsers for rdf/xml, n3, rdfa, grddl + * + * Dependencies: + * + * needs: util.js uri.js term.js rdfparser.js rdfa.js n3parser.js + * identity.js sparql.js jsonparser.js + * + * Independent of jQuery + */ + +/** + * Things to test: callbacks on request, refresh, retract + * loading from HTTP, HTTPS, FTP, FILE, others? + * To do: + * Firing up a mail client for mid: (message:) URLs + */ +var log = _dereq_('./log'); +var N3Parser = _dereq_('./n3parser'); +var NamedNode = _dereq_('./named-node'); +var Namespace = _dereq_('./namespace'); +var rdfParse = _dereq_('./parse'); +var parseRDFaDOM = _dereq_('./rdfaparser').parseRDFaDOM; +var RDFParser = _dereq_('./rdfxmlparser'); +var Uri = _dereq_('./uri'); +var Util = _dereq_('./util'); +var serialize = _dereq_('./serialize'); + +var Parsable = { + 'text/n3': true, + 'text/turtle': true, + 'application/rdf+xml': true, + 'application/xhtml+xml': true, + 'text/html': true, + 'application/ld+json': true +}; + +var Fetcher = function Fetcher(store, timeout, async) { + this.store = store; + this.thisURI = 'http://dig.csail.mit.edu/2005/ajar/ajaw/rdf/sources.js' + '#SourceFetcher'; // -- Kenny + this.timeout = timeout || 30000; + this.async = async != null ? async : true; + this.appNode = this.store.bnode(); // Denoting this session + this.store.fetcher = this; // Bi-linked + this.requested = {}; + // this.requested[uri] states: + // undefined no record of web access or records reset + // true has been requested, XHR in progress + // 'done' received, Ok + // 403 HTTP status unauthorized + // 404 Ressource does not exist. Can be created etc. + // 'redirected' In attempt to counter CORS problems retried. + // other strings mean various other erros, such as parse errros. + // + this.redirectedTo = {}; // Wehn 'redireced' + this.fetchCallbacks = {}; // fetchCallbacks[uri].push(callback) + + this.nonexistant = {}; // keep track of explict 404s -> we can overwrite etc + this.lookedUp = {}; + this.handlers = []; + this.mediatypes = {}; + var sf = this; + var kb = this.store; + var ns = {}; // Convenience namespaces needed in this module: + // These are delibertely not exported as the user application should + // make its own list and not rely on the prefixes used here, + // and not be tempted to add to them, and them clash with those of another + // application. + ns.link = Namespace('http://www.w3.org/2007/ont/link#'); + ns.http = Namespace('http://www.w3.org/2007/ont/http#'); + ns.httph = Namespace('http://www.w3.org/2007/ont/httph#'); + ns.rdf = Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#'); + ns.rdfs = Namespace('http://www.w3.org/2000/01/rdf-schema#'); + ns.dc = Namespace('http://purl.org/dc/elements/1.1/'); + + sf.mediatypes['image/*'] = { + 'q': 0.9 + }; + + sf.mediatypes['*/*'] = { // Must allow access to random content + 'q': 0.1 + }; + + Fetcher.crossSiteProxy = function (uri) { + if (Fetcher.crossSiteProxyTemplate) { + return Fetcher.crossSiteProxyTemplate.replace('{uri}', encodeURIComponent(uri)); + } else { + return undefined; + } + }; + + Fetcher.RDFXMLHandler = function (args) { + if (args) { + this.dom = args[0]; + } + this.handlerFactory = function (xhr) { + xhr.handle = function (cb) { + // sf.addStatus(xhr.req, 'parsing soon as RDF/XML...') + var kb = sf.store; + if (!this.dom) this.dom = Util.parseXML(xhr.responseText); + var root = this.dom.documentElement; + if (root.nodeName === 'parsererror') { + // @@ Mozilla only See issue/issue110 + sf.failFetch(xhr, 'Badly formed XML in ' + xhr.resource.uri); // have to fail the request + throw new Error('Badly formed XML in ' + xhr.resource.uri); // @@ Add details + } + var parser = new RDFParser(kb); + try { + parser.parse(this.dom, xhr.original.uri, xhr.original); + } catch (e) { + sf.addStatus(xhr.req, 'Syntax error parsing RDF/XML! ' + e); + console.log('Syntax error parsing RDF/XML! ' + e); + } + if (!xhr.options.noMeta) { + kb.add(xhr.original, ns.rdf('type'), ns.link('RDFDocument'), sf.appNode); + } + cb(); + }; + }; + }; + Fetcher.RDFXMLHandler.toString = function () { + return 'RDFXMLHandler'; + }; + Fetcher.RDFXMLHandler.register = function (sf) { + sf.mediatypes['application/rdf+xml'] = { + 'q': 0.9 + }; + }; + Fetcher.RDFXMLHandler.pattern = new RegExp('application/rdf\\+xml'); + + // This would much better use on-board XSLT engine. @@ + /* deprocated 2016-02-17 timbl + Fetcher.doGRDDL = function(kb, doc, xslturi, xmluri) { + sf.requestURI('http://www.w3.org/2005/08/' + 'online_xslt/xslt?' + 'xslfile=' + escape(xslturi) + '&xmlfile=' + escape(xmluri), doc) + } + */ + Fetcher.XHTMLHandler = function (args) { + if (args) { + this.dom = args[0]; + } + this.handlerFactory = function (xhr) { + xhr.handle = function (cb) { + var relation, reverse; + if (!this.dom) { + this.dom = Util.parseXML(xhr.responseText); + } + var kb = sf.store; + + // dc:title + var title = this.dom.getElementsByTagName('title'); + if (title.length > 0) { + kb.add(xhr.resource, ns.dc('title'), kb.literal(title[0].textContent), xhr.resource); + // log.info("Inferring title of " + xhr.resource) + } + + // link rel + var links = this.dom.getElementsByTagName('link'); + for (var x = links.length - 1; x >= 0; x--) { + // @@ rev + relation = links[x].getAttribute('rel'); + reverse = false; + if (!relation) { + relation = links[x].getAttribute('rev'); + reverse = true; + } + if (relation) { + sf.linkData(xhr, relation, links[x].getAttribute('href'), xhr.resource, reverse); + } + } + + // Data Islands + + var scripts = this.dom.getElementsByTagName('script'); + for (var i = 0; i < scripts.length; i++) { + var contentType = scripts[i].getAttribute('type'); + if (Parsable[contentType]) { + rdfParse(scripts[i].textContent, kb, xhr.original.uri, contentType); + } + } + + if (!xhr.options.noMeta) { + kb.add(xhr.resource, ns.rdf('type'), ns.link('WebPage'), sf.appNode); + } + + if (!xhr.options.noRDFa && parseRDFaDOM) { + // enable by default + try { + parseRDFaDOM(this.dom, kb, xhr.original.uri); + } catch (e) { + var msg = 'Error trying to parse ' + xhr.resource + ' as RDFa:\n' + e + ':\n' + e.stack; + // dump(msg+"\n") + sf.failFetch(xhr, msg); + return; + } + } + cb(); // Fire done callbacks + }; + }; + }; + Fetcher.XHTMLHandler.toString = function () { + return 'XHTMLHandler'; + }; + Fetcher.XHTMLHandler.register = function (sf) { + sf.mediatypes['application/xhtml+xml'] = {}; + }; + Fetcher.XHTMLHandler.pattern = new RegExp('application/xhtml'); + + Fetcher.XMLHandler = function () { + this.handlerFactory = function (xhr) { + xhr.handle = function (cb) { + var dom = Util.parseXML(xhr.responseText); + + // XML Semantics defined by root element namespace + // figure out the root element + for (var c = 0; c < dom.childNodes.length; c++) { + // is this node an element? + if (dom.childNodes[c].nodeType === 1) { + // We've found the first element, it's the root + var ns = dom.childNodes[c].namespaceURI; + + // Is it RDF/XML? + if (ns && ns === ns['rdf']) { + sf.addStatus(xhr.req, 'Has XML root element in the RDF namespace, so assume RDF/XML.'); + sf.switchHandler('RDFXMLHandler', xhr, cb, [dom]); + return; + } + // it isn't RDF/XML or we can't tell + // Are there any GRDDL transforms for this namespace? + // @@ assumes ns documents have already been loaded + /* + var xforms = kb.each(kb.sym(ns), kb.sym("http://www.w3.org/2003/g/data-view#namespaceTransformation")) + for (var i = 0; i < xforms.length; i++) { + var xform = xforms[i] + // log.info(xhr.resource.uri + " namespace " + ns + " has GRDDL ns transform" + xform.uri) + Fetcher.doGRDDL(kb, xhr.resource, xform.uri, xhr.resource.uri) + } + */ + break; + } + } + + // Or it could be XHTML? + // Maybe it has an XHTML DOCTYPE? + if (dom.doctype) { + // log.info("We found a DOCTYPE in " + xhr.resource) + if (dom.doctype.name === 'html' && dom.doctype.publicId.match(/^-\/\/W3C\/\/DTD XHTML/) && dom.doctype.systemId.match(/http:\/\/www.w3.org\/TR\/xhtml/)) { + sf.addStatus(xhr.req, 'Has XHTML DOCTYPE. Switching to XHTML Handler.\n'); + sf.switchHandler('XHTMLHandler', xhr, cb); + return; + } + } + + // Or what about an XHTML namespace? + var html = dom.getElementsByTagName('html')[0]; + if (html) { + var xmlns = html.getAttribute('xmlns'); + if (xmlns && xmlns.match(/^http:\/\/www.w3.org\/1999\/xhtml/)) { + sf.addStatus(xhr.req, 'Has a default namespace for ' + 'XHTML. Switching to XHTMLHandler.\n'); + sf.switchHandler('XHTMLHandler', xhr, cb); + return; + } + } + + // At this point we should check the namespace document (cache it!) and + // look for a GRDDL transform + // @@ Get namespace document , parse it, look for grddl:namespaceTransform ?y + // Apply ?y to dom + // We give up. What dialect is this? + sf.failFetch(xhr, 'Unsupported dialect of XML: not RDF or XHTML namespace, etc.\n' + xhr.responseText.slice(0, 80)); + }; + }; + }; + + Fetcher.XMLHandler.toString = function () { + return 'XMLHandler'; + }; + Fetcher.XMLHandler.register = function (sf) { + sf.mediatypes['text/xml'] = { + 'q': 0.5 + }; + sf.mediatypes['application/xml'] = { + 'q': 0.5 + }; + }; + Fetcher.XMLHandler.pattern = new RegExp('(text|application)/(.*)xml'); + + Fetcher.HTMLHandler = function () { + this.handlerFactory = function (xhr) { + xhr.handle = function (cb) { + var rt = xhr.responseText; + // We only handle XHTML so we have to figure out if this is XML + // log.info("Sniffing HTML " + xhr.resource + " for XHTML.") + + if (rt.match(/\s*<\?xml\s+version\s*=[^<>]+\?>/)) { + sf.addStatus(xhr.req, "Has an XML declaration. We'll assume " + "it's XHTML as the content-type was text/html.\n"); + sf.switchHandler('XHTMLHandler', xhr, cb); + return; + } + + // DOCTYPE + // There is probably a smarter way to do this + if (rt.match(/.*/)) { + sf.addStatus(xhr.req, 'Has XHTML DOCTYPE. Switching to XHTMLHandler.\n'); + sf.switchHandler('XHTMLHandler', xhr, cb); + return; + } + + // xmlns + if (rt.match(/[^(/)) { + sf.addStatus(xhr.req, 'Has default namespace for XHTML, so switching to XHTMLHandler.\n'); + sf.switchHandler('XHTMLHandler', xhr, cb); + return; + } + + // dc:title //no need to escape '/' here + var titleMatch = new RegExp('([\\s\\S]+?)', 'im').exec(rt); + if (titleMatch) { + var kb = sf.store; + kb.add(xhr.resource, ns.dc('title'), kb.literal(titleMatch[1]), xhr.resource); // think about xml:lang later + kb.add(xhr.resource, ns.rdf('type'), ns.link('WebPage'), sf.appNode); + cb(); // doneFetch, not failed + return; + } + sf.addStatus(xhr.req, 'non-XML HTML document, not parsed for data.'); + sf.doneFetch(xhr); + // sf.failFetch(xhr, "Sorry, can't yet parse non-XML HTML") + }; + }; + }; + + Fetcher.HTMLHandler.toString = function () { + return 'HTMLHandler'; + }; + Fetcher.HTMLHandler.register = function (sf) { + sf.mediatypes['text/html'] = { + 'q': 0.9 + }; + }; + Fetcher.HTMLHandler.pattern = new RegExp('text/html'); + + Fetcher.TextHandler = function () { + this.handlerFactory = function (xhr) { + xhr.handle = function (cb) { + // We only speak dialects of XML right now. Is this XML? + var rt = xhr.responseText; + + // Look for an XML declaration + if (rt.match(/\s*<\?xml\s+version\s*=[^<>]+\?>/)) { + sf.addStatus(xhr.req, 'Warning: ' + xhr.resource + " has an XML declaration. We'll assume " + "it's XML but its content-type wasn't XML.\n"); + sf.switchHandler('XMLHandler', xhr, cb); + return; + } - for (var i = 0; i < len; ++i) { - var x = qs[i].replace(regexp, '%20'), - idx = x.indexOf(eq), - kstr, vstr, k, v; + // Look for an XML declaration + if (rt.slice(0, 500).match(/xmlns:/)) { + sf.addStatus(xhr.req, "May have an XML namespace. We'll assume " + "it's XML but its content-type wasn't XML.\n"); + sf.switchHandler('XMLHandler', xhr, cb); + return; + } - if (idx >= 0) { - kstr = x.substr(0, idx); - vstr = x.substr(idx + 1); - } else { - kstr = x; - vstr = ''; - } + // We give up finding semantics - this is not an error, just no data + sf.addStatus(xhr.req, 'Plain text document, no known RDF semantics.'); + sf.doneFetch(xhr); + // sf.failFetch(xhr, "unparseable - text/plain not visibly XML") + // dump(xhr.resource + " unparseable - text/plain not visibly XML, starts:\n" + rt.slice(0, 500)+"\n") + }; + }; + }; - k = decodeURIComponent(kstr); - v = decodeURIComponent(vstr); + Fetcher.TextHandler.toString = function () { + return 'TextHandler'; + }; + Fetcher.TextHandler.register = function (sf) { + sf.mediatypes['text/plain'] = { + 'q': 0.5 + }; + }; + Fetcher.TextHandler.pattern = new RegExp('text/plain'); - if (!hasOwnProperty(obj, k)) { - obj[k] = v; - } else if (isArray(obj[k])) { - obj[k].push(v); - } else { - obj[k] = [obj[k], v]; - } - } + Fetcher.N3Handler = function () { + this.handlerFactory = function (xhr) { + xhr.handle = function (cb) { + // Parse the text of this non-XML file - return obj; -}; + // console.log('web.js: Parsing as N3 ' + xhr.resource.uri + ' base: ' + xhr.original.uri) // @@@@ comment me out + // sf.addStatus(xhr.req, "N3 not parsed yet...") + var p = N3Parser(kb, kb, xhr.original.uri, xhr.original.uri, null, null, '', null); + // p.loadBuf(xhr.responseText) + try { + p.loadBuf(xhr.responseText); + } catch (e) { + var msg = 'Error trying to parse ' + xhr.resource + ' as Notation3:\n' + e + ':\n' + e.stack; + // dump(msg+"\n") + sf.failFetch(xhr, msg); + return; + } -var isArray = Array.isArray || function (xs) { - return Object.prototype.toString.call(xs) === '[object Array]'; -}; + sf.addStatus(xhr.req, 'N3 parsed: ' + p.statementCount + ' triples in ' + p.lines + ' lines.'); + sf.store.add(xhr.original, ns.rdf('type'), ns.link('RDFDocument'), sf.appNode); + // var args = [xhr.original.uri] // Other args needed ever? + sf.doneFetch(xhr); + }; + }; + }; -},{}],49:[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. + Fetcher.N3Handler.toString = function () { + return 'N3Handler'; + }; + Fetcher.N3Handler.register = function (sf) { + sf.mediatypes['text/n3'] = { + 'q': '1.0' + }; // as per 2008 spec + /* + sf.mediatypes['application/x-turtle'] = { + 'q': 1.0 + } // pre 2008 + */ + sf.mediatypes['text/turtle'] = { + 'q': 1.0 + }; // post 2008 + }; + Fetcher.N3Handler.pattern = new RegExp('(application|text)/(x-)?(rdf\\+)?(n3|turtle)'); -'use strict'; + Util.callbackify(this, ['request', 'recv', 'headers', 'load', 'fail', 'refresh', 'retract', 'done']); -var stringifyPrimitive = function(v) { - switch (typeof v) { - case 'string': - return v; + this.addHandler = function (handler) { + sf.handlers.push(handler); + handler.register(sf); + }; - case 'boolean': - return v ? 'true' : 'false'; + this.switchHandler = function (name, xhr, cb, args) { + var Handler = null; + for (var i = 0; i < this.handlers.length; i++) { + if ('' + this.handlers[i] === name) { + Handler = this.handlers[i]; + } + } + if (!Handler) { + throw new Error('web.js: switchHandler: name=' + name + ' , this.handlers =' + this.handlers + '\n' + 'switchHandler: switching to ' + Handler + '; sf=' + sf + '; typeof Fetcher=' + (typeof Fetcher === 'undefined' ? 'undefined' : _typeof(Fetcher)) + ';\n\t Fetcher.HTMLHandler=' + Fetcher.HTMLHandler + '\n' + '\n\tsf.handlers=' + sf.handlers + '\n'); + } + new Handler(args).handlerFactory(xhr); + xhr.handle(cb); + }; - case 'number': - return isFinite(v) ? v : ''; + this.addStatus = function (req, status) { + // + var now = new Date(); + status = '[' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds() + '.' + now.getMilliseconds() + '] ' + status; + // + var kb = this.store; + var s = kb.the(req, ns.link('status')); + if (s && s.append) { + s.append(kb.literal(status)); + } else { + log.warn('web.js: No list to add to: ' + s + ',' + status); // @@@ + } + }; - default: - return ''; - } -}; + // Record errors in the system on failure + // Returns xhr so can just do return this.failfetch(...) + this.failFetch = function (xhr, status) { + this.addStatus(xhr.req, status); + if (!xhr.options.noMeta) { + kb.add(xhr.original, ns.link('error'), status); + } + if (!xhr.resource.sameTerm(xhr.original)) { + console.log('@@ Recording failure original ' + xhr.original + '( as ' + xhr.resource + ') : ' + xhr.status); + } else { + console.log('@@ Recording failure for ' + xhr.original + ': ' + xhr.status); + } + this.requested[Uri.docpart(xhr.original.uri)] = xhr.status; // changed 2015 was false + while (this.fetchCallbacks[xhr.original.uri] && this.fetchCallbacks[xhr.original.uri].length) { + this.fetchCallbacks[xhr.original.uri].shift()(false, 'Fetch of <' + xhr.original.uri + '> failed: ' + status, xhr); + } + delete this.fetchCallbacks[xhr.original.uri]; + this.fireCallbacks('fail', [xhr.original.uri, status]); + xhr.abort(); + return xhr; + }; -module.exports = function(obj, sep, eq, name) { - sep = sep || '&'; - eq = eq || '='; - if (obj === null) { - obj = undefined; - } + // in the why part of the quad distinguish between HTML and HTTP header + // Reverse is set iif the link was rev= as opposed to rel= + this.linkData = function (xhr, rel, uri, why, reverse) { + if (!uri) return; + var predicate; + // See http://www.w3.org/TR/powder-dr/#httplink for describedby 2008-12-10 + var obj = kb.sym(Uri.join(uri, xhr.original.uri)); + if (rel === 'alternate' || rel === 'seeAlso' || rel === 'meta' || rel === 'describedby') { + if (obj.uri === xhr.original.uri) return; + predicate = ns.rdfs('seeAlso'); + } else if (rel === 'type') { + predicate = kb.sym('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'); + } else { + // See https://www.iana.org/assignments/link-relations/link-relations.xml + // Alas not yet in RDF yet for each predicate + /// encode space in e.g. rel="shortcut icon" + predicate = kb.sym(Uri.join(encodeURIComponent(rel), 'http://www.iana.org/assignments/link-relations/')); + } + if (reverse) { + kb.add(obj, predicate, xhr.original, why); + } else { + kb.add(xhr.original, predicate, obj, why); + } + }; - 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])); + this.parseLinkHeader = function (xhr, thisReq) { + var link; + try { + link = xhr.getResponseHeader('link'); // May crash from CORS error + } catch (e) {} + if (link) { + var linkexp = /<[^>]*>\s*(\s*;\s*[^\(\)<>@,;:"\/\[\]\?={} \t]+=(([^\(\)<>@,;:"\/\[\]\?={} \t]+)|("[^"]*")))*(,|$)/g; + var paramexp = /[^\(\)<>@,;:"\/\[\]\?={} \t]+=(([^\(\)<>@,;:"\/\[\]\?={} \t]+)|("[^"]*"))/g; + + var matches = link.match(linkexp); + for (var i = 0; i < matches.length; i++) { + var split = matches[i].split('>'); + var href = split[0].substring(1); + var ps = split[1]; + var s = ps.match(paramexp); + for (var j = 0; j < s.length; j++) { + var p = s[j]; + var paramsplit = p.split('='); + // var name = paramsplit[0] + var rel = paramsplit[1].replace(/["']/g, ''); // '" + this.linkData(xhr, rel, href, thisReq); + } } - }).join(sep); + } + }; - } + this.doneFetch = function (xhr) { + this.addStatus(xhr.req, 'Done.'); + this.requested[xhr.original.uri] = 'done'; // Kenny + while (this.fetchCallbacks[xhr.original.uri] && this.fetchCallbacks[xhr.original.uri].length) { + this.fetchCallbacks[xhr.original.uri].shift()(true, undefined, xhr); + } + delete this.fetchCallbacks[xhr.original.uri]; + this.fireCallbacks('done', [xhr.original.uri]); + }; + var handlerList = [Fetcher.RDFXMLHandler, Fetcher.XHTMLHandler, Fetcher.XMLHandler, Fetcher.HTMLHandler, Fetcher.TextHandler, Fetcher.N3Handler]; + handlerList.map(this.addHandler); + + /** Note two nodes are now smushed + ** + ** If only one was flagged as looked up, then + ** the new node is looked up again, which + ** will make sure all the URIs are dereferenced + */ + this.nowKnownAs = function (was, now) { + if (this.lookedUp[was.uri]) { + if (!this.lookedUp[now.uri]) this.lookUpThing(now, was); // @@@@ Transfer userCallback + } else if (this.lookedUp[now.uri]) { + if (!this.lookedUp[was.uri]) this.lookUpThing(was, now); + } + }; - if (!name) return ''; - return encodeURIComponent(stringifyPrimitive(name)) + eq + - encodeURIComponent(stringifyPrimitive(obj)); -}; + // Returns promise of XHR + // + // Writes back to the web what we have in the store for this uri + this.putBack = function (uri) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + uri = uri.uri || uri; // Accept object or string + var doc = new NamedNode(uri).doc(); // strip off # + options.data = serialize(doc, this.store, doc.uri, options.contentType || 'text/turtle'); + return this.webOperation('PUT', uri, options); + }; -var isArray = Array.isArray || function (xs) { - return Object.prototype.toString.call(xs) === '[object Array]'; -}; + // Returns promise of XHR + // + this.webOperation = function (method, uri) { + var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + uri = uri.uri || uri; + uri = this.proxyIfNecessary(uri); + var fetcher = this; + return new Promise(function (resolve, reject) { + var xhr = Util.XMLHTTPFactory(); + xhr.options = options; + xhr.original = fetcher.store.sym(uri); + if (!options.noMeta && typeof tabulator !== 'undefined') { + fetcher.saveRequestMetadata(xhr, fetcher.store, uri); + } + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + // NOte a 404 can be not afailure + var ok = !xhr.status || xhr.status >= 200 && xhr.status < 300; + if (!options.noMeta && typeof tabulator !== 'undefined') { + fetcher.saveResponseMetadata(xhr, fetcher.store); + } + if (ok) { + resolve(xhr); + } else { + reject(xhr.status + ' ' + xhr.statusText); + } + } + }; + xhr.open(method, uri, true); + xhr.setRequestHeader('Content-type', options.contentType || 'text/turtle'); + xhr.send(options.data ? options.data : undefined); + }); + }; -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; -} + this.webCopy = function (here, there, content_type) { + var fetcher = this; + here = here.uri || here; + return new Promise(function (resolve, reject) { + fetcher.webOperation('GET', here).then(function (xhr) { + fetcher.webOperation('PUT', // @@@ change to binary from text + there, { data: xhr.responseText, contentType: content_type }).then(function (xhr) { + resolve(xhr); + }).catch(function (e) { + reject(e); + }); + }).catch(function (e) { + reject(e); + }); + }); + }; -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; -}; + // Looks up something. + // + // Looks up all the URIs a things has. + // + // Parameters: + // + // term: canonical term for the thing whose URI is to be dereferenced + // rterm: the resource which refered to this (for tracking bad links) + // options: (old: force paraemter) or dictionary of options: + // force: Load the data even if loaded before + // oneDone: is called as callback(ok, errorbody, xhr) for each one + // allDone: is called as callback(ok, errorbody) for all of them + // Returns the number of URIs fetched + // + this.lookUpThing = function (term, rterm, options, oneDone, allDone) { + var uris = kb.uris(term); // Get all URIs + var success = true; + var errors = ''; + var outstanding = {}; + var force; + if (options === false || options === true) { + // Old signature + force = options; + options = { force: force }; + } else { + if (options === undefined) options = {}; + force = !!options.force; + } -},{}],50:[function(_dereq_,module,exports){ -'use strict'; + if (typeof uris !== 'undefined') { + for (var i = 0; i < uris.length; i++) { + var u = uris[i]; + outstanding[u] = true; + this.lookedUp[u] = true; + var sf = this; -exports.decode = exports.parse = _dereq_('./decode'); -exports.encode = exports.stringify = _dereq_('./encode'); + var requestOne = function requestOne(u1) { + sf.requestURI(Uri.docpart(u1), rterm, options, function (ok, body, xhr) { + if (ok) { + if (oneDone) oneDone(true, u1); + } else { + if (oneDone) oneDone(false, body); + success = false; + errors += body + '\n'; + } + delete outstanding[u]; + if (Object.keys(outstanding).length > 0) { + return; + } + if (allDone) { + allDone(success, errors); + } + }); + }; + requestOne(u); + } + } + return uris.length; + }; + + /* Promise-based load function + ** + ** NamedNode -> Promise of xhr + ** uri string -> Promise of xhr + ** Array of the above -> Promise of array of xhr + ** + ** @@ todo: If p1 is array then sequence or parallel fetch of all + */ + this.load = function (uri, options) { + var fetcher = this; + if (uri instanceof Array) { + var ps = uri.map(function (x) { + return fetcher.load(x); + }); + return Promise.all(ps); + } + uri = uri.uri || uri; // NamedNode or URI string + return new Promise(function (resolve, reject) { + fetcher.nowOrWhenFetched(uri, options, function (ok, message, xhr) { + if (ok) { + resolve(xhr); + } else { + reject(message); + } + }); + }); + }; + + /* Ask for a doc to be loaded if necessary then call back + ** + ** Changed 2013-08-20: Added (ok, errormessage) params to callback + ** + ** Calling methods: + ** nowOrWhenFetched (uri, userCallback) + ** nowOrWhenFetched (uri, options, userCallback) + ** nowOrWhenFetched (uri, referringTerm, userCallback, options) <-- old + ** nowOrWhenFetched (uri, referringTerm, userCallback) <-- old + ** + ** Options include: + ** referringTerm The docuemnt in which this link was found. + ** this is valuable when finding the source of bad URIs + ** force boolean. Never mind whether you have tried before, + ** load this from scratch. + ** forceContentType Override the incoming header to force the data to be + ** treaed as this content-type. + **/ + this.nowOrWhenFetched = function (uri, p2, userCallback, options) { + uri = uri.uri || uri; // allow symbol object or string to be passed + if (typeof p2 === 'function') { + options = {}; + userCallback = p2; + } else if (typeof p2 === 'undefined') {// original calling signature + // referingTerm = undefined + } else if (p2 instanceof NamedNode) { + // referingTerm = p2 + options = { referingTerm: p2 }; + } else { + options = p2; + } + + this.requestURI(uri, p2, options || {}, userCallback); + }; + + this.get = this.nowOrWhenFetched; + + // Look up response header + // + // Returns: a list of header values found in a stored HTTP response + // or [] if response was found but no header found + // or undefined if no response is available. + // + this.getHeader = function (doc, header) { + var kb = this.store; + var requests = kb.each(undefined, ns.link('requestedURI'), doc.uri); + for (var r = 0; r < requests.length; r++) { + var request = requests[r]; + if (request !== undefined) { + var response = kb.any(request, ns.link('response')); + if (request !== undefined) { + var results = kb.each(response, ns.httph(header.toLowerCase())); + if (results.length) { + return results.map(function (v) { + return v.value; + }); + } + return []; + } + } + } + return undefined; + }; + + this.proxyIfNecessary = function (uri) { + if (typeof tabulator !== 'undefined' && tabulator.isExtension) return uri; // Extenstion does not need proxy + + if (typeof $SolidTestEnvironment !== 'undefined' && $SolidTestEnvironment.localSiteMap) { + // nested dictionaries of URI parts from origin down + var hostpath = uri.split('/').slice(2); // the bit after the // + var lookup = function lookup(parts, index) { + var z = index[parts.shift()]; + if (!z) return null; + if (typeof z === 'string') { + return z + parts.join('/'); + } + if (!parts) return null; + return lookup(parts, z); + }; + var y = lookup(hostpath, $SolidTestEnvironment.localSiteMap); + if (y) { + return y; + } + } + // browser does 2014 on as https browser script not trusted + // If the web app origin is https: then the mixed content rules + // prevent it loading insecure http: stuff so we need proxy. + if (Fetcher.crossSiteProxyTemplate && typeof document !== 'undefined' && document.location && ('' + document.location).slice(0, 6) === 'https:' && // origin is secure + uri.slice(0, 5) === 'http:') { + // requested data is not + return Fetcher.crossSiteProxyTemplate.replace('{uri}', encodeURIComponent(uri)); + } + return uri; + }; + + this.saveRequestMetadata = function (xhr, kb, docuri) { + var request = kb.bnode(); + xhr.resource = kb.sym(docuri); + + xhr.req = request; + if (!xhr.options.noMeta) { + // Store no triples but do mind the bnode for req + var now = new Date(); + var timeNow = '[' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds() + '] '; + kb.add(request, ns.rdfs('label'), kb.literal(timeNow + ' Request for ' + docuri), this.appNode); + kb.add(request, ns.link('requestedURI'), kb.literal(docuri), this.appNode); + if (xhr.original && xhr.original.uri !== docuri) { + kb.add(request, ns.link('orginalURI'), kb.literal(xhr.original.uri), this.appNode); + } + kb.add(request, ns.link('status'), kb.collection(), this.appNode); + } + return request; + }; + + this.saveResponseMetadata = function (xhr, kb) { + var response = kb.bnode(); + + if (xhr.req) kb.add(xhr.req, ns.link('response'), response); + kb.add(response, ns.http('status'), kb.literal(xhr.status), response); + kb.add(response, ns.http('statusText'), kb.literal(xhr.statusText), response); + + xhr.headers = {}; + if (Uri.protocol(xhr.resource.uri) === 'http' || Uri.protocol(xhr.resource.uri) === 'https') { + xhr.headers = Util.getHTTPHeaders(xhr); + for (var h in xhr.headers) { + // trim below for Safari - adds a CR! + var value = xhr.headers[h].trim(); + var h2 = h.toLowerCase(); + kb.add(response, ns.httph(h2), value, response); + if (h2 === 'content-type') { + // Convert to RDF type + kb.add(xhr.resource, ns.rdf('type'), Util.mediaTypeClass(value), response); + } + } + } + return response; + }; + + /** Requests a document URI and arranges to load the document. + ** Parameters: + ** term: term for the thing whose URI is to be dereferenced + ** rterm: the resource which refered to this (for tracking bad links) + ** options: + ** force: Load the data even if loaded before + ** withCredentials: flag for XHR/CORS etc + ** userCallback: Called with (true) or (false, errorbody, {status: 400}) after load is done or failed + ** Return value: + ** The xhr object for the HTTP access + ** null if the protocol is not a look-up protocol, + ** or URI has already been loaded + */ + this.requestURI = function (docuri, rterm, options, userCallback) { + // sources_request_new + // Various calling conventions + docuri = docuri.uri || docuri; // NamedNode or string + docuri = docuri.split('#')[0]; + if (typeof options === 'boolean') { + options = { 'force': options }; // Ols dignature + } + if (typeof options === 'undefined') options = {}; + + var force = !!options.force; + var kb = this.store; + var args = arguments; + var baseURI = options.baseURI || docuri; // Preseve though proxying etc + options.userCallback = userCallback; + + var pcol = Uri.protocol(docuri); + if (pcol === 'tel' || pcol === 'mailto' || pcol === 'urn') { + // "No look-up operation on these, but they are not errors?" + console.log('Unsupported protocol in: ' + docuri); + return userCallback(false, 'Unsupported protocol', { 'status': 900 }) || undefined; + } + var docterm = kb.sym(docuri); + + var sta = this.getState(docuri); + if (!force) { + if (sta === 'fetched') { + return userCallback ? userCallback(true) : undefined; + } + if (sta === 'failed') { + return userCallback ? userCallback(false, 'Previously failed. ' + this.requested[docuri], { 'status': this.requested[docuri] }) : undefined; // An xhr standin + } + // if (sta === 'requested') return userCallback? userCallback(false, "Sorry already requested - pending already.", {'status': 999 }) : undefined + } else { + delete this.nonexistant[docuri]; + } + // @@ Should allow concurrent requests + + // If it is 'failed', then shoulkd we try again? I think so so an old error doens't get stuck + // if (sta === 'unrequested') + + this.fireCallbacks('request', args); // Kenny: fire 'request' callbacks here + // dump( "web.js: Requesting uri: " + docuri + "\n" ) + + if (userCallback) { + if (!this.fetchCallbacks[docuri]) { + this.fetchCallbacks[docuri] = [userCallback]; + } else { + this.fetchCallbacks[docuri].push(userCallback); + } + } + + if (this.requested[docuri] === true) { + return; // Don't ask again - wait for existing call + } else { + this.requested[docuri] = true; + } + + if (!options.noMeta && rterm && rterm.uri) { + kb.add(docterm.uri, ns.link('requestedBy'), rterm.uri, this.appNode); + } + + var xhr = Util.XMLHTTPFactory(); + var req = xhr.req = kb.bnode(); + xhr.original = kb.sym(baseURI); + // console.log('XHR original: ' + xhr.original) + xhr.options = options; + xhr.resource = docterm; // This might be proxified + var sf = this; + + var now = new Date(); + var timeNow = '[' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds() + '] '; + if (!options.noMeta) { + kb.add(req, ns.rdfs('label'), kb.literal(timeNow + ' Request for ' + docuri), this.appNode); + kb.add(req, ns.link('requestedURI'), kb.literal(docuri), this.appNode); + kb.add(req, ns.link('status'), kb.collection(), this.appNode); + } + + var checkCredentialsRetry = function checkCredentialsRetry() { + if (!xhr.withCredentials) return false; // not dealt with + + if (xhr.retriedWithCredentials) { + return true; + } + xhr.retriedWithCredentials = true; // protect against called twice + console.log('web: Retrying with no credentials for ' + xhr.resource); + xhr.abort(); + delete sf.requested[docuri]; // forget the original request happened + var newopt = {}; + for (var opt in options) { + // transfer baseURI etc + if (options.hasOwnProperty(opt)) { + newopt[opt] = options[opt]; + } + } + newopt.withCredentials = false; + sf.addStatus(xhr.req, 'Abort: Will retry with credentials SUPPRESSED to see if that helps'); + sf.requestURI(docuri, rterm, newopt, xhr.userCallback); // userCallback already registered (with where?) + return true; + }; + + var onerrorFactory = function onerrorFactory(xhr) { + return function (event) { + xhr.onErrorWasCalled = true; // debugging and may need it + if (typeof document !== 'undefined') { + // Mashup situation, not node etc + if (Fetcher.crossSiteProxyTemplate && document.location && !xhr.proxyUsed) { + var hostpart = Uri.hostpart; + var here = '' + document.location; + var uri = xhr.resource.uri; + if (hostpart(here) && hostpart(uri) && hostpart(here) !== hostpart(uri)) { + // If cross-site + if (xhr.status === 401 || xhr.status === 403 || xhr.status === 404) { + onreadystatechangeFactory(xhr)(); + } else { + // IT IS A PAIN THAT NO PROPER ERROR REPORTING + if (checkCredentialsRetry(xhr)) { + // If credentials flag set, retry without, + return; + } + // If it wasn't, or we already tried that + var newURI = Fetcher.crossSiteProxy(uri); + console.log('web: Direct failed so trying proxy ' + newURI); + sf.addStatus(xhr.req, 'BLOCKED -> Cross-site Proxy to <' + newURI + '>'); + if (xhr.aborted) return; + + var kb = sf.store; + var oldreq = xhr.req; + if (!xhr.options.noMeta) { + kb.add(oldreq, ns.http('redirectedTo'), kb.sym(newURI), oldreq); + } + xhr.abort(); + xhr.aborted = true; + + sf.addStatus(oldreq, 'redirected to new request'); // why + // the callback throws an exception when called from xhr.onerror (so removed) + // sf.fireCallbacks('done', args) // Are these args right? @@@ Not done yet! done means success + sf.requested[xhr.resource.uri] = 'redirected'; + sf.redirectedTo[xhr.resource.uri] = newURI; + + if (sf.fetchCallbacks[xhr.resource.uri]) { + if (!sf.fetchCallbacks[newURI]) { + sf.fetchCallbacks[newURI] = []; + } + sf.fetchCallbacks[newURI] === sf.fetchCallbacks[newURI].concat(sf.fetchCallbacks[xhr.resource.uri]); + delete sf.fetchCallbacks[xhr.resource.uri]; + } + + var xhr2 = sf.requestURI(newURI, xhr.resource, xhr.options, xhr.userCallback); + if (xhr2) { + xhr2.proxyUsed = true; // only try the proxy once + xhr2.original = xhr.original; + console.log('Proxying but original still ' + xhr2.original); + } + if (xhr2 && xhr2.req) { + if (!xhr.options.noMeta) { + kb.add(xhr.req, kb.sym('http://www.w3.org/2007/ont/link#redirectedRequest'), xhr2.req, sf.appNode); + } + return; + } + } + } + xhr.CORS_status = 999; + // xhr.status = 999 forbidden - read-only + } + } // mashu + }; // function of event + }; // onerrorFactory + + // Set up callbacks + var onreadystatechangeFactory = function onreadystatechangeFactory(xhr) { + return function () { + var handleResponse = function handleResponse() { + if (xhr.handleResponseDone) return; + xhr.handleResponseDone = true; + var handler = null; + var thisReq = xhr.req; // Might have changes by redirect + sf.fireCallbacks('recv', args); + var kb = sf.store; + sf.saveResponseMetadata(xhr, kb); + sf.fireCallbacks('headers', [{ uri: docuri, headers: xhr.headers }]); + + // Check for masked errors. + // For "security reasons" theboraser hides errors such as CORS errors from + // the calling code (2015). oneror() used to be called but is not now. + // + if (xhr.status === 0) { + console.log('Masked error - status 0 for ' + xhr.resource.uri); + if (checkCredentialsRetry(xhr)) { + // retry is could be credentials flag CORS issue + return; + } + xhr.CORS_status = 900; // unknown masked error + return; + } + if (xhr.status >= 400) { + // For extra dignostics, keep the reply + // @@@ 401 should cause a retry with credential son + // @@@ cache the credentials flag by host ???? + if (xhr.status === 404) { + kb.fetcher.nonexistant[xhr.resource.uri] = true; + } + if (xhr.responseText.length > 10) { + var response2 = kb.bnode(); + kb.add(response2, ns.http('content'), kb.literal(xhr.responseText), response2); + if (xhr.statusText) { + kb.add(response2, ns.http('statusText'), kb.literal(xhr.statusText), response2); + } + // dump("HTTP >= 400 responseText:\n"+xhr.responseText+"\n"); // @@@@ + } + sf.failFetch(xhr, 'HTTP error for ' + xhr.resource + ': ' + xhr.status + ' ' + xhr.statusText); + return; + } + + var loc = xhr.headers['content-location']; + + // deduce some things from the HTTP transaction + var addType = function addType(cla) { + // add type to all redirected resources too + var prev = thisReq; + if (loc) { + var docURI = kb.any(prev, ns.link('requestedURI')); + if (docURI !== loc) { + kb.add(kb.sym(loc), ns.rdf('type'), cla, sf.appNode); + } + } + for (;;) { + var doc = kb.any(prev, ns.link('requestedURI')); + if (doc && doc.value) { + kb.add(kb.sym(doc.value), ns.rdf('type'), cla, sf.appNode); + } // convert Literal + prev = kb.any(undefined, kb.sym('http://www.w3.org/2007/ont/link#redirectedRequest'), prev); + if (!prev) break; + var response = kb.any(prev, kb.sym('http://www.w3.org/2007/ont/link#response')); + if (!response) break; + var redirection = kb.any(response, kb.sym('http://www.w3.org/2007/ont/http#status')); + if (!redirection) break; + if (redirection !== '301' && redirection !== '302') break; + } + }; + // This is a minimal set to allow the use of damaged servers if necessary + var extensionToContentType = { + 'rdf': 'application/rdf+xml', 'owl': 'application/rdf+xml', + 'n3': 'text/n3', 'ttl': 'text/turtle', 'nt': 'text/n3', 'acl': 'text/n3', + 'html': 'text/html', + 'xml': 'text/xml' + }; + var guess; + if (xhr.status === 200) { + addType(ns.link('Document')); + var ct = xhr.headers['content-type']; + if (options.forceContentType) { + xhr.headers['content-type'] = options.forceContentType; + } + if (!ct || ct.indexOf('application/octet-stream') >= 0) { + guess = extensionToContentType[xhr.resource.uri.split('.').pop()]; + if (guess) { + xhr.headers['content-type'] = guess; + } + } + if (ct) { + if (ct.indexOf('image/') === 0 || ct.indexOf('application/pdf') === 0) addType(kb.sym('http://purl.org/dc/terms/Image')); + } + if (options.clearPreviousData) { + // Before we parse new data clear old but only on 200 + kb.removeDocument(xhr.resource); + } + } + // application/octet-stream; charset=utf-8 + + if (Uri.protocol(xhr.resource.uri) === 'file' || Uri.protocol(xhr.resource.uri) === 'chrome') { + if (options.forceContentType) { + xhr.headers['content-type'] = options.forceContentType; + } else { + guess = extensionToContentType[xhr.resource.uri.split('.').pop()]; + if (guess) { + xhr.headers['content-type'] = guess; + } else { + xhr.headers['content-type'] = 'text/xml'; + } + } + } + + // If we have alread got the thing at this location, abort + if (loc) { + var udoc = Uri.join(xhr.resource.uri, loc); + if (!force && udoc !== xhr.resource.uri && sf.requested[udoc] && sf.requested[udoc] === 'done') { + // we have already fetched this in fact. + // should we smush too? + // log.info("HTTP headers indicate we have already" + " retrieved " + xhr.resource + " as " + udoc + ". Aborting.") + sf.doneFetch(xhr); + xhr.abort(); + return; + } + sf.requested[udoc] = true; + } + + for (var x = 0; x < sf.handlers.length; x++) { + if (xhr.headers['content-type'] && xhr.headers['content-type'].match(sf.handlers[x].pattern)) { + handler = new sf.handlers[x](); + break; + } + } + + sf.parseLinkHeader(xhr, thisReq); + + if (handler) { + try { + handler.handlerFactory(xhr); + } catch (e) { + // Try to avoid silent errors + sf.failFetch(xhr, 'Exception handling content-type ' + xhr.headers['content-type'] + ' was: ' + e); + } + } else { + sf.doneFetch(xhr); // Not a problem, we just don't extract data. + /* + // sf.failFetch(xhr, "Unhandled content type: " + xhr.headers['content-type']+ + // ", readyState = "+xhr.readyState) + */ + return; + } + }; + + // DONE: 4 + // HEADERS_RECEIVED: 2 + // LOADING: 3 + // OPENED: 1 + // UNSENT: 0 + + // log.debug("web.js: XHR " + xhr.resource.uri + ' readyState='+xhr.readyState); // @@@@ comment me out + + switch (xhr.readyState) { + case 0: + var uri = xhr.resource.uri; + var newURI; + if (this.crossSiteProxyTemplate && typeof document !== 'undefined' && document.location) { + // In mashup situation + var hostpart = Uri.hostpart; + var here = '' + document.location; + if (hostpart(here) && hostpart(uri) && hostpart(here) !== hostpart(uri)) { + newURI = this.crossSiteProxyTemplate.replace('{uri}', encodeURIComponent(uri)); + sf.addStatus(xhr.req, 'BLOCKED -> Cross-site Proxy to <' + newURI + '>'); + if (xhr.aborted) return; + + var kb = sf.store; + var oldreq = xhr.req; + kb.add(oldreq, ns.http('redirectedTo'), kb.sym(newURI), oldreq); + + // //////////// Change the request node to a new one: @@@@@@@@@@@@ Duplicate? + var newreq = xhr.req = kb.bnode(); // Make NEW reqest for everything else + kb.add(oldreq, ns.http('redirectedRequest'), newreq, xhr.req); + + var now = new Date(); + var timeNow = '[' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds() + '] '; + kb.add(newreq, ns.rdfs('label'), kb.literal(timeNow + ' Request for ' + newURI), this.appNode); + kb.add(newreq, ns.link('status'), kb.collection(), this.appNode); + kb.add(newreq, ns.link('requestedURI'), kb.literal(newURI), this.appNode); + + var response = kb.bnode(); + kb.add(oldreq, ns.link('response'), response); + // kb.add(response, ns.http('status'), kb.literal(xhr.status), response) + // if (xhr.statusText) kb.add(response, ns.http('statusText'), kb.literal(xhr.statusText), response) + + xhr.abort(); + xhr.aborted = true; + xhr.redirected = true; + + sf.addStatus(oldreq, 'redirected XHR'); // why + + if (sf.fetchCallbacks[xhr.resource.uri]) { + if (!sf.fetchCallbacks[newURI]) { + sf.fetchCallbacks[newURI] = []; + } + sf.fetchCallbacks[newURI] === sf.fetchCallbacks[newURI].concat(sf.fetchCallbacks[xhr.resource.uri]); + delete sf.fetchCallbacks[xhr.resource.uri]; + } + + sf.fireCallbacks('redirected', args); // Are these args right? @@@ + sf.requested[xhr.resource.uri] = 'redirected'; + + var xhr2 = sf.requestURI(newURI, xhr.resource, xhr.options || {}, xhr.userCallback); + if (xhr2 && xhr2.req) { + kb.add(xhr.req, kb.sym('http://www.w3.org/2007/ont/link#redirectedRequest'), xhr2.req, sf.appNode); + return; + } + } + } + sf.failFetch(xhr, 'HTTP Blocked. (ReadyState 0) Cross-site violation for <' + docuri + '>'); + + break; + + case 3: + // Intermediate state -- 3 may OR MAY NOT be called, selon browser. + // handleResponse(); // In general it you can't do it yet as the headers are in but not the data + break; + case 4: + // Final state for this XHR but may be redirected + handleResponse(); + // Now handle + if (xhr.handle && xhr.responseText !== undefined) { + // can be validly zero length + if (sf.requested[xhr.resource.uri] === 'redirected') { + break; + } + sf.fireCallbacks('load', args); + xhr.handle(function () { + sf.doneFetch(xhr); + }); + } else { + if (xhr.redirected) { + sf.addStatus(xhr.req, 'Aborted and redirected to new request.'); + } else { + sf.addStatus(xhr.req, 'Fetch over. No data handled. Aborted = ' + xhr.aborted); + } + // sf.failFetch(xhr, "HTTP failed unusually. (no handler set) (x-site violation? no net?) for <"+ + // docuri+">") + } + break; + } // switch + }; + }; + + // Map the URI to a localhost proxy if we are running on localhost + // This is used for working offline, e.g. on planes. + // Is the script istelf is running in localhost, then access all data in a localhost mirror. + // Do not remove without checking with TimBL + var uri2 = docuri; + if (typeof tabulator !== 'undefined' && tabulator.preferences.get('offlineModeUsingLocalhost')) { + if (uri2.slice(0, 7) === 'http://' && uri2.slice(7, 17) !== 'localhost/') { + uri2 = 'http://localhost/' + uri2.slice(7); + log.warn('Localhost kludge for offline use: actually getting <' + uri2 + '>'); + } else { + // log.warn("Localhost kludge NOT USED <" + uri2 + ">") + } + } else {} + // log.warn("Localhost kludge OFF offline use: actually getting <" + uri2 + ">") + + // 2014 probelm: + // XMLHttpRequest cannot load http://www.w3.org/People/Berners-Lee/card. + // A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true. + // @ Many ontology files under http: and need CORS wildcard -> can't have withCredentials + + var withCredentials = uri2.slice(0, 6) === 'https:'; // @@ Kludge -- need for webid which typically is served from https + if (options.withCredentials !== undefined) { + withCredentials = options.withCredentials; + } + var actualProxyURI = this.proxyIfNecessary(uri2); + + // Setup the request + // var xhr + // xhr = Util.XMLHTTPFactory() + xhr.onerror = onerrorFactory(xhr); + xhr.onreadystatechange = onreadystatechangeFactory(xhr); + xhr.timeout = sf.timeout; + xhr.withCredentials = withCredentials; + xhr.actualProxyURI = actualProxyURI; + + xhr.req = req; + xhr.options = options; + xhr.options = options; + xhr.resource = docterm; + xhr.requestedURI = uri2; + + xhr.ontimeout = function () { + sf.failFetch(xhr, 'requestTimeout'); + }; + try { + xhr.open('GET', actualProxyURI, this.async); + } catch (er) { + return this.failFetch(xhr, 'XHR open for GET failed for <' + uri2 + '>:\n\t' + er); + } + if (force) { + // must happen after open + xhr.setRequestHeader('Cache-control', 'no-cache'); + } + + // Set redirect callback and request headers -- alas Firefox Extension Only + if (typeof tabulator !== 'undefined' && tabulator.isExtension && xhr.channel && (Uri.protocol(xhr.resource.uri) === 'http' || Uri.protocol(xhr.resource.uri) === 'https')) { + try { + xhr.channel.notificationCallbacks = { + getInterface: function getInterface(iid) { + if (iid.equals(Components.interfaces.nsIChannelEventSink)) { + return { + onChannelRedirect: function onChannelRedirect(oldC, newC, flags) { + if (xhr.aborted) return; + var kb = sf.store; + var newURI = newC.URI.spec; + var oldreq = xhr.req; + if (!xhr.options.noMeta) { + sf.addStatus(xhr.req, 'Redirected: ' + xhr.status + ' to <' + newURI + '>'); + kb.add(oldreq, ns.http('redirectedTo'), kb.sym(newURI), xhr.req); + + // //////////// Change the request node to a new one: @@@@@@@@@@@@ Duplicate code? + var newreq = xhr.req = kb.bnode(); // Make NEW reqest for everything else + kb.add(oldreq, ns.http('redirectedRequest'), newreq, this.appNode); + + var now = new Date(); + var timeNow = '[' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds() + '] '; + kb.add(newreq, ns.rdfs('label'), kb.literal(timeNow + ' Request for ' + newURI), this.appNode); + kb.add(newreq, ns.link('status'), kb.collection(), this.appNode); + kb.add(newreq, ns.link('requestedURI'), kb.literal(newURI), this.appNode); + // ///////////// + + // // log.info('@@ sources onChannelRedirect'+ + // "Redirected: "+ + // xhr.status + " to <" + newURI + ">"); //@@ + var response = kb.bnode(); + // kb.add(response, ns.http('location'), newURI, response); Not on this response + kb.add(oldreq, ns.link('response'), response); + kb.add(response, ns.http('status'), kb.literal(xhr.status), response); + if (xhr.statusText) kb.add(response, ns.http('statusText'), kb.literal(xhr.statusText), response); + } + if (xhr.status - 0 !== 303) kb.HTTPRedirects[xhr.resource.uri] = newURI; // same document as + if (xhr.status - 0 === 301 && rterm) { + // 301 Moved + var badDoc = Uri.docpart(rterm.uri); + var msg = 'Warning: ' + xhr.resource + ' has moved to <' + newURI + '>.'; + if (rterm) { + msg += ' Link in <' + badDoc + ' >should be changed'; + kb.add(badDoc, kb.sym('http://www.w3.org/2007/ont/link#warning'), msg, sf.appNode); + } + // dump(msg+"\n") + } + xhr.abort(); + xhr.aborted = true; + + if (sf.fetchCallbacks[xhr.resource.uri]) { + if (!sf.fetchCallbacks[newURI]) { + sf.fetchCallbacks[newURI] = []; + } + sf.fetchCallbacks[newURI] === sf.fetchCallbacks[newURI].concat(sf.fetchCallbacks[xhr.resource.uri]); + delete sf.fetchCallbacks[xhr.resource.uri]; + } + + sf.addStatus(oldreq, 'redirected'); // why + sf.fireCallbacks('redirected', args); // Are these args right? @@@ + sf.requested[xhr.resource.uri] = 'redirected'; + sf.redirectedTo[xhr.resource.uri] = newURI; + + var hash = newURI.indexOf('#'); + if (hash >= 0) { + if (!xhr.options.noMeta) { + kb.add(xhr.resource, kb.sym('http://www.w3.org/2007/ont/link#warning'), 'Warning: ' + xhr.resource + ' HTTP redirects to' + newURI + ' which should not contain a "#" sign'); + } + newURI = newURI.slice(0, hash); + } + var xhr2 = sf.requestURI(newURI, xhr.resource, xhr.options, xhr.userCallback); + if (xhr2 && xhr2.req && !options.noMeta) { + kb.add(xhr.req, kb.sym('http://www.w3.org/2007/ont/link#redirectedRequest'), xhr2.req, sf.appNode); + } + // else dump("No xhr.req available for redirect from "+xhr.resource+" to "+newURI+"\n") + }, + + // See https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIChannelEventSink + asyncOnChannelRedirect: function asyncOnChannelRedirect(oldC, newC, flags, callback) { + if (xhr.aborted) return; + var kb = sf.store; + var newURI = newC.URI.spec; + var oldreq = xhr.req; + sf.addStatus(xhr.req, 'Redirected: ' + xhr.status + ' to <' + newURI + '>'); + kb.add(oldreq, ns.http('redirectedTo'), kb.sym(newURI), xhr.req); + + // //////////// Change the request node to a new one: @@@@@@@@@@@@ Duplicate? + var newreq = xhr.req = kb.bnode(); // Make NEW reqest for everything else + // xhr.resource = docterm + // xhr.requestedURI = args[0] + + // kb.add(kb.sym(newURI), ns.link("request"), req, this.appNode) + kb.add(oldreq, ns.http('redirectedRequest'), newreq, xhr.req); + + var now = new Date(); + var timeNow = '[' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds() + '] '; + kb.add(newreq, ns.rdfs('label'), kb.literal(timeNow + ' Request for ' + newURI), this.appNode); + kb.add(newreq, ns.link('status'), kb.collection(), this.appNode); + kb.add(newreq, ns.link('requestedURI'), kb.literal(newURI), this.appNode); + // ///////////// + + // // log.info('@@ sources onChannelRedirect'+ + // "Redirected: "+ + // xhr.status + " to <" + newURI + ">"); //@@ + var response = kb.bnode(); + // kb.add(response, ns.http('location'), newURI, response); Not on this response + kb.add(oldreq, ns.link('response'), response); + kb.add(response, ns.http('status'), kb.literal(xhr.status), response); + if (xhr.statusText) kb.add(response, ns.http('statusText'), kb.literal(xhr.statusText), response); + + if (xhr.status - 0 !== 303) kb.HTTPRedirects[xhr.resource.uri] = newURI; // same document as + if (xhr.status - 0 === 301 && rterm) { + // 301 Moved + var badDoc = Uri.docpart(rterm.uri); + var msg = 'Warning: ' + xhr.resource + ' has moved to <' + newURI + '>.'; + if (rterm) { + msg += ' Link in <' + badDoc + ' >should be changed'; + kb.add(badDoc, kb.sym('http://www.w3.org/2007/ont/link#warning'), msg, sf.appNode); + } + // dump(msg+"\n") + } + xhr.abort(); + xhr.aborted = true; + + var hash = newURI.indexOf('#'); + if (hash >= 0) { + var msg2 = 'Warning: ' + xhr.resource + ' HTTP redirects to' + newURI + ' which do not normally contain a "#" sign'; + // dump(msg+"\n") + kb.add(xhr.resource, kb.sym('http://www.w3.org/2007/ont/link#warning'), msg2); + newURI = newURI.slice(0, hash); + } + /* + if (sf.fetchCallbacks[xhr.resource.uri]) { + if (!sf.fetchCallbacks[newURI]) { + sf.fetchCallbacks[newURI] = [] + } + sf.fetchCallbacks[newURI] = sf.fetchCallbacks[newURI].concat(sf.fetchCallbacks[xhr.resource.uri]) + delete sf.fetchCallbacks[xhr.resource.uri] + } + */ + sf.requested[xhr.resource.uri] = 'redirected'; + sf.redirectedTo[xhr.resource.uri] = newURI; + + var xhr2 = sf.requestURI(newURI, xhr.resource, xhr.options, xhr.userCallback); + if (xhr2) { + // may be no XHR is other URI already loaded + xhr2.original = xhr.original; // use this for finding base + if (xhr2.req) { + kb.add(xhr.req, kb.sym('http://www.w3.org/2007/ont/link#redirectedRequest'), xhr2.req, sf.appNode); + } + } + // else dump("No xhr.req available for redirect from "+xhr.resource+" to "+newURI+"\n") + } // asyncOnChannelRedirect + }; + } + return Components.results.NS_NOINTERFACE; + } + }; + } catch (err) { + return sf.failFetch(xhr, "@@ Couldn't set callback for redirects: " + err); + } // try + } // if Firefox extension + + try { + var acceptstring = ''; + for (var type in this.mediatypes) { + // var attrstring = '' + if (acceptstring !== '') { + acceptstring += ', '; + } + acceptstring += type; + for (var attr in this.mediatypes[type]) { + acceptstring += ';' + attr + '=' + this.mediatypes[type][attr]; + } + } + xhr.setRequestHeader('Accept', acceptstring); + this.addStatus(xhr.req, 'Accept: ' + acceptstring); + + // if (requester) { xhr.setRequestHeader('Referer',requester) } + } catch (err) { + throw new Error("Can't set Accept header: " + err); + } + + // Fire + try { + xhr.send(null); + } catch (er) { + return this.failFetch(xhr, 'XHR send failed:' + er); + } + setTimeout(function () { + if (xhr.readyState !== 4 && sf.isPending(xhr.resource.uri)) { + sf.failFetch(xhr, 'requestTimeout'); + } + }, this.timeout); + this.addStatus(xhr.req, 'HTTP Request sent.'); + return xhr; + }; // this.requestURI() + + this.objectRefresh = function (term) { + var uris = kb.uris(term); // Get all URIs + if (typeof uris !== 'undefined') { + for (var i = 0; i < uris.length; i++) { + this.refresh(this.store.sym(Uri.docpart(uris[i]))); + // what about rterm? + } + } + }; + + // deprecated -- use IndexedFormula.removeDocument(doc) + this.unload = function (term) { + this.store.removeMany(undefined, undefined, undefined, term); + delete this.requested[term.uri]; // So it can be loaded again + }; + + this.refresh = function (term, userCallback) { + // sources_refresh + this.fireCallbacks('refresh', arguments); + this.requestURI(term.uri, undefined, { force: true, clearPreviousData: true }, userCallback); + }; + + this.retract = function (term) { + // sources_retract + this.store.removeMany(undefined, undefined, undefined, term); + if (term.uri) { + delete this.requested[Uri.docpart(term.uri)]; + } + this.fireCallbacks('retract', arguments); + }; + + this.getState = function (docuri) { + if (typeof this.requested[docuri] === 'undefined') { + return 'unrequested'; + } else if (this.requested[docuri] === true) { + return 'requested'; + } else if (this.requested[docuri] === 'done') { + return 'fetched'; + } else if (this.requested[docuri] === 'redirected') { + return this.getState(this.redirectedTo[docuri]); + } else { + // An non-200 HTTP error status + return 'failed'; + } + }; + + // doing anyStatementMatching is wasting time + this.isPending = function (docuri) { + // sources_pending + // if it's not pending: false -> flailed 'done' -> done 'redirected' -> redirected + return this.requested[docuri] === true; + }; + // var updatesVia = new $rdf.UpdatesVia(this) // Subscribe to headers + // @@@@@@@@ This is turned off because it causes a websocket to be set up for ANY fetch + // whether we want to track it ot not. including ontologies loaed though the XSSproxy +}; // End of fetcher -},{"./decode":48,"./encode":49}],51:[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":52,"./node":68}],52:[function(_dereq_,module,exports){ -'use strict'; - -var ClassOrder = { - 'Literal': 1, - 'Collection': 3, - 'Graph': 4, - 'NamedNode': 5, - 'BlankNode': 6, - 'Variable': 7 -}; - -module.exports = ClassOrder; -},{}],53:[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, [{ - key: 'append', - value: function append(element) { - return this.elements.push(element); - } - }, { - key: 'close', - value: function close() { - this.closed = true; - return this.closed; - } - }, { - key: 'shift', - value: function shift() { - return this.elements.shift(); - } - }, { - key: 'substitute', - value: function substitute(bindings) { - var elementsCopy = this.elements.map(function (ea) { - ea.substitute(bindings); - }); - return new Collection(elementsCopy); - } - }, { - key: 'toNT', - value: function toNT() { - return BlankNode.NTAnonymousNodePrefix + this.id; - } - }, { - key: 'toString', - value: function toString() { - return '(' + this.elements.join(' ') + ')'; - } - }, { - key: 'unshift', - value: function unshift(element) { - return this.elements.unshift(element); - } - }]); - - return Collection; -}(Node); - -Collection.termType = 'Collection'; -Collection.prototype.classOrder = ClassOrder['Collection']; -Collection.prototype.compareTerm = BlankNode.prototype.compareTerm; -Collection.prototype.isVar = 0; - -module.exports = Collection; -},{"./blank-node":51,"./class-order":52,"./node":68}],54:[function(_dereq_,module,exports){ -'use strict'; - -module.exports.convertToJson = convertToJson; -module.exports.convertToNQuads = convertToNQuads; - -var asyncLib = _dereq_('async'); // @@ Goal: remove this dependency -var jsonld = _dereq_('jsonld'); -var N3 = _dereq_('n3'); // @@ Goal: remove this dependency - -function convertToJson(n3String, jsonCallback) { - var jsonString; - var n3Parser = N3.Parser(); - var n3Writer = N3.Writer({ - format: 'N-Quads' - }); - asyncLib.waterfall([function (callback) { - n3Parser.parse(n3String, callback); - }, function (triple, prefix, callback) { - if (triple !== null) { - n3Writer.addTriple(triple); - } - if (typeof callback === 'function') { - n3Writer.end(callback); - } - }, function (result, callback) { - try { - jsonld.fromRDF(result, { - format: 'application/nquads' - }, callback); - } catch (err) { - callback(err); - } - }, function (json, callback) { - jsonString = JSON.stringify(json); - jsonCallback(null, jsonString); - }], function (err, result) { - jsonCallback(err, jsonString); - }); -} - -function convertToNQuads(n3String, nquadCallback) { - var nquadString; - var n3Parser = N3.Parser(); - var n3Writer = N3.Writer({ - format: 'N-Quads' - }); - asyncLib.waterfall([function (callback) { - n3Parser.parse(n3String, callback); - }, function (triple, prefix, callback) { - if (triple !== null) { - n3Writer.addTriple(triple); - } - if (typeof callback === 'function') { - n3Writer.end(callback); - } - }, function (result, callback) { - nquadString = result; - nquadCallback(null, nquadString); - }], function (err, result) { - nquadCallback(err, nquadString); - }); -} -},{"async":1,"jsonld":19,"n3":85}],55:[function(_dereq_,module,exports){ -'use strict'; - -var _indexedFormula = _dereq_('./indexed-formula'); - -var _indexedFormula2 = _interopRequireDefault(_indexedFormula); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -var BlankNode = _dereq_('./blank-node'); -var Collection = _dereq_('./collection'); -var DefaultGraph = _dereq_('./default-graph'); -var Fetcher = _dereq_('./fetcher'); - -var Literal = _dereq_('./literal'); -var NamedNode = _dereq_('./named-node'); -var Statement = _dereq_('./statement'); -var Variable = _dereq_('./variable'); - -function blankNode(value) { - return new BlankNode(value); -} -function collection(elements) { - return new Collection(elements); -} -function defaultGraph() { - return new DefaultGraph(); -} -function fetcher(store, timeout, async) { - return new Fetcher(store, timeout, async); -} -function graph() { - return new _indexedFormula2.default(); -} -function lit(val, lang, dt) { - return new Literal('' + val, lang, dt); -} -function literal(value, languageOrDatatype) { - if (typeof languageOrDatatype === 'string') { - if (languageOrDatatype.indexOf(':') === -1) { - return new Literal(value, languageOrDatatype); - } else { - return new Literal(value, null, namedNode(languageOrDatatype)); - } - } else { - return new Literal(value, null, languageOrDatatype); - } -} -function namedNode(value) { - return new NamedNode(value); -} -function quad(subject, predicate, object, graph) { - graph = graph || new DefaultGraph(); - return new Statement(subject, predicate, object, graph); -} -function st(subject, predicate, object, graph) { - return new Statement(subject, predicate, object, graph); -} -function triple(subject, predicate, object) { - return quad(subject, predicate, object); -} -function variable(name) { - return new Variable(name); -} - -// rdfjs spec factory methods -module.exports.blankNode = blankNode; -module.exports.defaultGraph = defaultGraph; -module.exports.graph = graph; -module.exports.literal = literal; -module.exports.namedNode = namedNode; -module.exports.quad = quad; -module.exports.triple = triple; -module.exports.variable = variable; - -// rdflib only -module.exports.collection = collection; -module.exports.fetcher = fetcher; -module.exports.lit = lit; -module.exports.st = st; -},{"./blank-node":51,"./collection":53,"./default-graph":56,"./fetcher":58,"./indexed-formula":61,"./literal":63,"./named-node":66,"./statement":78,"./variable":83}],56:[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 Node = _dereq_('./node'); - -var DefaultGraph = function (_Node) { - _inherits(DefaultGraph, _Node); - - function DefaultGraph() { - _classCallCheck(this, DefaultGraph); - - var _this = _possibleConstructorReturn(this, (DefaultGraph.__proto__ || Object.getPrototypeOf(DefaultGraph)).call(this)); - - _this.termType = 'DefaultGraph'; - _this.value = ''; - return _this; - } - - _createClass(DefaultGraph, [{ - key: 'toCanonical', - value: function toCanonical() { - return this.value; - } - }]); - - return DefaultGraph; -}(Node); - -module.exports = DefaultGraph; -},{"./node":68}],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 Node = _dereq_('./node'); - -/** - * Singleton subclass of an empty Collection. - */ - -var Empty = function (_Node) { - _inherits(Empty, _Node); - - function Empty() { - _classCallCheck(this, Empty); - - var _this = _possibleConstructorReturn(this, (Empty.__proto__ || Object.getPrototypeOf(Empty)).call(this)); - - _this.termType = Empty.termType; - return _this; - } - - _createClass(Empty, [{ - key: 'toString', - value: function toString() { - return '()'; - } - }]); - - return Empty; -}(Node); - -Empty.termType = 'empty'; - -module.exports = Empty; -},{"./node":68}],58:[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; }; - -/* global $SolidTestEnvironment */ -/** - * - * Project: rdflib.js - * - * File: fetcher.js - * - * Description: contains functions for requesting/fetching/retracting - * This implements quite a lot of the web architecture. - * A fetcher is bound to a specific knowledge base graph, into which - * it loads stuff and into which it writes its metadata - * @@ The metadata should be optionally a separate graph - * - * - implements semantics of HTTP headers, Internet Content Types - * - selects parsers for rdf/xml, n3, rdfa, grddl - * - * Dependencies: - * - * needs: util.js uri.js term.js rdfparser.js rdfa.js n3parser.js - * identity.js sparql.js jsonparser.js - * - * Independent of jQuery - */ - -/** - * Things to test: callbacks on request, refresh, retract - * loading from HTTP, HTTPS, FTP, FILE, others? - * To do: - * Firing up a mail client for mid: (message:) URLs - */ -var log = _dereq_('./log'); -var N3Parser = _dereq_('./n3parser'); -var NamedNode = _dereq_('./named-node'); -var Namespace = _dereq_('./namespace'); -var rdfParse = _dereq_('./parse'); -var parseRDFaDOM = _dereq_('./rdfaparser').parseRDFaDOM; -var RDFParser = _dereq_('./rdfxmlparser'); -var Uri = _dereq_('./uri'); -var Util = _dereq_('./util'); -var serialize = _dereq_('./serialize'); - -var Parsable = { - 'text/n3': true, - 'text/turtle': true, - 'application/rdf+xml': true, - 'application/xhtml+xml': true, - 'text/html': true, - 'application/ld+json': true -}; - -var Fetcher = function Fetcher(store, timeout, async) { - this.store = store; - this.thisURI = 'http://dig.csail.mit.edu/2005/ajar/ajaw/rdf/sources.js' + '#SourceFetcher'; // -- Kenny - this.timeout = timeout || 30000; - this.async = async != null ? async : true; - this.appNode = this.store.bnode(); // Denoting this session - this.store.fetcher = this; // Bi-linked - this.requested = {}; - // this.requested[uri] states: - // undefined no record of web access or records reset - // true has been requested, XHR in progress - // 'done' received, Ok - // 403 HTTP status unauthorized - // 404 Ressource does not exist. Can be created etc. - // 'redirected' In attempt to counter CORS problems retried. - // other strings mean various other erros, such as parse errros. - // - this.redirectedTo = {}; // Wehn 'redireced' - this.fetchCallbacks = {}; // fetchCallbacks[uri].push(callback) - - this.nonexistant = {}; // keep track of explict 404s -> we can overwrite etc - this.lookedUp = {}; - this.handlers = []; - this.mediatypes = {}; - var sf = this; - var kb = this.store; - var ns = {}; // Convenience namespaces needed in this module: - // These are delibertely not exported as the user application should - // make its own list and not rely on the prefixes used here, - // and not be tempted to add to them, and them clash with those of another - // application. - ns.link = Namespace('http://www.w3.org/2007/ont/link#'); - ns.http = Namespace('http://www.w3.org/2007/ont/http#'); - ns.httph = Namespace('http://www.w3.org/2007/ont/httph#'); - ns.rdf = Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#'); - ns.rdfs = Namespace('http://www.w3.org/2000/01/rdf-schema#'); - ns.dc = Namespace('http://purl.org/dc/elements/1.1/'); - - sf.mediatypes['image/*'] = { - 'q': 0.9 - }; - - sf.mediatypes['*/*'] = { // Must allow access to random content - 'q': 0.1 - }; - - Fetcher.crossSiteProxy = function (uri) { - if (Fetcher.crossSiteProxyTemplate) { - return Fetcher.crossSiteProxyTemplate.replace('{uri}', encodeURIComponent(uri)); - } else { - return undefined; - } - }; - - Fetcher.RDFXMLHandler = function (args) { - if (args) { - this.dom = args[0]; - } - this.handlerFactory = function (xhr) { - xhr.handle = function (cb) { - // sf.addStatus(xhr.req, 'parsing soon as RDF/XML...') - var kb = sf.store; - if (!this.dom) this.dom = Util.parseXML(xhr.responseText); - var root = this.dom.documentElement; - if (root.nodeName === 'parsererror') { - // @@ Mozilla only See issue/issue110 - sf.failFetch(xhr, 'Badly formed XML in ' + xhr.resource.uri); // have to fail the request - throw new Error('Badly formed XML in ' + xhr.resource.uri); // @@ Add details - } - var parser = new RDFParser(kb); - try { - parser.parse(this.dom, xhr.original.uri, xhr.original); - } catch (e) { - sf.addStatus(xhr.req, 'Syntax error parsing RDF/XML! ' + e); - console.log('Syntax error parsing RDF/XML! ' + e); - } - if (!xhr.options.noMeta) { - kb.add(xhr.original, ns.rdf('type'), ns.link('RDFDocument'), sf.appNode); - } - cb(); - }; - }; - }; - Fetcher.RDFXMLHandler.toString = function () { - return 'RDFXMLHandler'; - }; - Fetcher.RDFXMLHandler.register = function (sf) { - sf.mediatypes['application/rdf+xml'] = { - 'q': 0.9 - }; - }; - Fetcher.RDFXMLHandler.pattern = new RegExp('application/rdf\\+xml'); - - // This would much better use on-board XSLT engine. @@ - /* deprocated 2016-02-17 timbl - Fetcher.doGRDDL = function(kb, doc, xslturi, xmluri) { - sf.requestURI('http://www.w3.org/2005/08/' + 'online_xslt/xslt?' + 'xslfile=' + escape(xslturi) + '&xmlfile=' + escape(xmluri), doc) - } - */ - Fetcher.XHTMLHandler = function (args) { - if (args) { - this.dom = args[0]; - } - this.handlerFactory = function (xhr) { - xhr.handle = function (cb) { - var relation, reverse; - if (!this.dom) { - this.dom = Util.parseXML(xhr.responseText); - } - var kb = sf.store; - - // dc:title - var title = this.dom.getElementsByTagName('title'); - if (title.length > 0) { - kb.add(xhr.resource, ns.dc('title'), kb.literal(title[0].textContent), xhr.resource); - // log.info("Inferring title of " + xhr.resource) - } - - // link rel - var links = this.dom.getElementsByTagName('link'); - for (var x = links.length - 1; x >= 0; x--) { - // @@ rev - relation = links[x].getAttribute('rel'); - reverse = false; - if (!relation) { - relation = links[x].getAttribute('rev'); - reverse = true; - } - if (relation) { - sf.linkData(xhr, relation, links[x].getAttribute('href'), xhr.resource, reverse); - } - } - - // Data Islands - - var scripts = this.dom.getElementsByTagName('script'); - for (var i = 0; i < scripts.length; i++) { - var contentType = scripts[i].getAttribute('type'); - if (Parsable[contentType]) { - rdfParse(scripts[i].textContent, kb, xhr.original.uri, contentType); - } - } - - if (!xhr.options.noMeta) { - kb.add(xhr.resource, ns.rdf('type'), ns.link('WebPage'), sf.appNode); - } - - if (!xhr.options.noRDFa && parseRDFaDOM) { - // enable by default - try { - parseRDFaDOM(this.dom, kb, xhr.original.uri); - } catch (e) { - var msg = 'Error trying to parse ' + xhr.resource + ' as RDFa:\n' + e + ':\n' + e.stack; - // dump(msg+"\n") - sf.failFetch(xhr, msg); - return; - } - } - cb(); // Fire done callbacks - }; - }; - }; - Fetcher.XHTMLHandler.toString = function () { - return 'XHTMLHandler'; - }; - Fetcher.XHTMLHandler.register = function (sf) { - sf.mediatypes['application/xhtml+xml'] = {}; - }; - Fetcher.XHTMLHandler.pattern = new RegExp('application/xhtml'); - - Fetcher.XMLHandler = function () { - this.handlerFactory = function (xhr) { - xhr.handle = function (cb) { - var dom = Util.parseXML(xhr.responseText); - - // XML Semantics defined by root element namespace - // figure out the root element - for (var c = 0; c < dom.childNodes.length; c++) { - // is this node an element? - if (dom.childNodes[c].nodeType === 1) { - // We've found the first element, it's the root - var ns = dom.childNodes[c].namespaceURI; - - // Is it RDF/XML? - if (ns && ns === ns['rdf']) { - sf.addStatus(xhr.req, 'Has XML root element in the RDF namespace, so assume RDF/XML.'); - sf.switchHandler('RDFXMLHandler', xhr, cb, [dom]); - return; - } - // it isn't RDF/XML or we can't tell - // Are there any GRDDL transforms for this namespace? - // @@ assumes ns documents have already been loaded - /* - var xforms = kb.each(kb.sym(ns), kb.sym("http://www.w3.org/2003/g/data-view#namespaceTransformation")) - for (var i = 0; i < xforms.length; i++) { - var xform = xforms[i] - // log.info(xhr.resource.uri + " namespace " + ns + " has GRDDL ns transform" + xform.uri) - Fetcher.doGRDDL(kb, xhr.resource, xform.uri, xhr.resource.uri) - } - */ - break; - } - } - - // Or it could be XHTML? - // Maybe it has an XHTML DOCTYPE? - if (dom.doctype) { - // log.info("We found a DOCTYPE in " + xhr.resource) - if (dom.doctype.name === 'html' && dom.doctype.publicId.match(/^-\/\/W3C\/\/DTD XHTML/) && dom.doctype.systemId.match(/http:\/\/www.w3.org\/TR\/xhtml/)) { - sf.addStatus(xhr.req, 'Has XHTML DOCTYPE. Switching to XHTML Handler.\n'); - sf.switchHandler('XHTMLHandler', xhr, cb); - return; - } - } - - // Or what about an XHTML namespace? - var html = dom.getElementsByTagName('html')[0]; - if (html) { - var xmlns = html.getAttribute('xmlns'); - if (xmlns && xmlns.match(/^http:\/\/www.w3.org\/1999\/xhtml/)) { - sf.addStatus(xhr.req, 'Has a default namespace for ' + 'XHTML. Switching to XHTMLHandler.\n'); - sf.switchHandler('XHTMLHandler', xhr, cb); - return; - } - } - - // At this point we should check the namespace document (cache it!) and - // look for a GRDDL transform - // @@ Get namespace document , parse it, look for grddl:namespaceTransform ?y - // Apply ?y to dom - // We give up. What dialect is this? - sf.failFetch(xhr, 'Unsupported dialect of XML: not RDF or XHTML namespace, etc.\n' + xhr.responseText.slice(0, 80)); - }; - }; - }; - - Fetcher.XMLHandler.toString = function () { - return 'XMLHandler'; - }; - Fetcher.XMLHandler.register = function (sf) { - sf.mediatypes['text/xml'] = { - 'q': 0.5 - }; - sf.mediatypes['application/xml'] = { - 'q': 0.5 - }; - }; - Fetcher.XMLHandler.pattern = new RegExp('(text|application)/(.*)xml'); - - Fetcher.HTMLHandler = function () { - this.handlerFactory = function (xhr) { - xhr.handle = function (cb) { - var rt = xhr.responseText; - // We only handle XHTML so we have to figure out if this is XML - // log.info("Sniffing HTML " + xhr.resource + " for XHTML.") - - if (rt.match(/\s*<\?xml\s+version\s*=[^<>]+\?>/)) { - sf.addStatus(xhr.req, "Has an XML declaration. We'll assume " + "it's XHTML as the content-type was text/html.\n"); - sf.switchHandler('XHTMLHandler', xhr, cb); - return; - } - - // DOCTYPE - // There is probably a smarter way to do this - if (rt.match(/.*/)) { - sf.addStatus(xhr.req, 'Has XHTML DOCTYPE. Switching to XHTMLHandler.\n'); - sf.switchHandler('XHTMLHandler', xhr, cb); - return; - } - - // xmlns - if (rt.match(/[^(/)) { - sf.addStatus(xhr.req, 'Has default namespace for XHTML, so switching to XHTMLHandler.\n'); - sf.switchHandler('XHTMLHandler', xhr, cb); - return; - } - - // dc:title //no need to escape '/' here - var titleMatch = new RegExp('([\\s\\S]+?)', 'im').exec(rt); - if (titleMatch) { - var kb = sf.store; - kb.add(xhr.resource, ns.dc('title'), kb.literal(titleMatch[1]), xhr.resource); // think about xml:lang later - kb.add(xhr.resource, ns.rdf('type'), ns.link('WebPage'), sf.appNode); - cb(); // doneFetch, not failed - return; - } - sf.addStatus(xhr.req, 'non-XML HTML document, not parsed for data.'); - sf.doneFetch(xhr); - // sf.failFetch(xhr, "Sorry, can't yet parse non-XML HTML") - }; - }; - }; - - Fetcher.HTMLHandler.toString = function () { - return 'HTMLHandler'; - }; - Fetcher.HTMLHandler.register = function (sf) { - sf.mediatypes['text/html'] = { - 'q': 0.9 - }; - }; - Fetcher.HTMLHandler.pattern = new RegExp('text/html'); - - Fetcher.TextHandler = function () { - this.handlerFactory = function (xhr) { - xhr.handle = function (cb) { - // We only speak dialects of XML right now. Is this XML? - var rt = xhr.responseText; - - // Look for an XML declaration - if (rt.match(/\s*<\?xml\s+version\s*=[^<>]+\?>/)) { - sf.addStatus(xhr.req, 'Warning: ' + xhr.resource + " has an XML declaration. We'll assume " + "it's XML but its content-type wasn't XML.\n"); - sf.switchHandler('XMLHandler', xhr, cb); - return; - } - - // Look for an XML declaration - if (rt.slice(0, 500).match(/xmlns:/)) { - sf.addStatus(xhr.req, "May have an XML namespace. We'll assume " + "it's XML but its content-type wasn't XML.\n"); - sf.switchHandler('XMLHandler', xhr, cb); - return; - } - - // We give up finding semantics - this is not an error, just no data - sf.addStatus(xhr.req, 'Plain text document, no known RDF semantics.'); - sf.doneFetch(xhr); - // sf.failFetch(xhr, "unparseable - text/plain not visibly XML") - // dump(xhr.resource + " unparseable - text/plain not visibly XML, starts:\n" + rt.slice(0, 500)+"\n") - }; - }; - }; - - Fetcher.TextHandler.toString = function () { - return 'TextHandler'; - }; - Fetcher.TextHandler.register = function (sf) { - sf.mediatypes['text/plain'] = { - 'q': 0.5 - }; - }; - Fetcher.TextHandler.pattern = new RegExp('text/plain'); - - Fetcher.N3Handler = function () { - this.handlerFactory = function (xhr) { - xhr.handle = function (cb) { - // Parse the text of this non-XML file - - // console.log('web.js: Parsing as N3 ' + xhr.resource.uri + ' base: ' + xhr.original.uri) // @@@@ comment me out - // sf.addStatus(xhr.req, "N3 not parsed yet...") - var p = N3Parser(kb, kb, xhr.original.uri, xhr.original.uri, null, null, '', null); - // p.loadBuf(xhr.responseText) - try { - p.loadBuf(xhr.responseText); - } catch (e) { - var msg = 'Error trying to parse ' + xhr.resource + ' as Notation3:\n' + e + ':\n' + e.stack; - // dump(msg+"\n") - sf.failFetch(xhr, msg); - return; - } - - sf.addStatus(xhr.req, 'N3 parsed: ' + p.statementCount + ' triples in ' + p.lines + ' lines.'); - sf.store.add(xhr.original, ns.rdf('type'), ns.link('RDFDocument'), sf.appNode); - // var args = [xhr.original.uri] // Other args needed ever? - sf.doneFetch(xhr); - }; - }; - }; - - Fetcher.N3Handler.toString = function () { - return 'N3Handler'; - }; - Fetcher.N3Handler.register = function (sf) { - sf.mediatypes['text/n3'] = { - 'q': '1.0' - }; // as per 2008 spec - /* - sf.mediatypes['application/x-turtle'] = { - 'q': 1.0 - } // pre 2008 - */ - sf.mediatypes['text/turtle'] = { - 'q': 1.0 - }; // post 2008 - }; - Fetcher.N3Handler.pattern = new RegExp('(application|text)/(x-)?(rdf\\+)?(n3|turtle)'); - - Util.callbackify(this, ['request', 'recv', 'headers', 'load', 'fail', 'refresh', 'retract', 'done']); - - this.addHandler = function (handler) { - sf.handlers.push(handler); - handler.register(sf); - }; - - this.switchHandler = function (name, xhr, cb, args) { - var Handler = null; - for (var i = 0; i < this.handlers.length; i++) { - if ('' + this.handlers[i] === name) { - Handler = this.handlers[i]; - } - } - if (!Handler) { - throw new Error('web.js: switchHandler: name=' + name + ' , this.handlers =' + this.handlers + '\n' + 'switchHandler: switching to ' + Handler + '; sf=' + sf + '; typeof Fetcher=' + (typeof Fetcher === 'undefined' ? 'undefined' : _typeof(Fetcher)) + ';\n\t Fetcher.HTMLHandler=' + Fetcher.HTMLHandler + '\n' + '\n\tsf.handlers=' + sf.handlers + '\n'); - } - new Handler(args).handlerFactory(xhr); - xhr.handle(cb); - }; - - this.addStatus = function (req, status) { - // - var now = new Date(); - status = '[' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds() + '.' + now.getMilliseconds() + '] ' + status; - // - var kb = this.store; - var s = kb.the(req, ns.link('status')); - if (s && s.append) { - s.append(kb.literal(status)); - } else { - log.warn('web.js: No list to add to: ' + s + ',' + status); // @@@ - } - }; - - // Record errors in the system on failure - // Returns xhr so can just do return this.failfetch(...) - this.failFetch = function (xhr, status) { - this.addStatus(xhr.req, status); - if (!xhr.options.noMeta) { - kb.add(xhr.original, ns.link('error'), status); - } - if (!xhr.resource.sameTerm(xhr.original)) { - console.log('@@ Recording failure original ' + xhr.original + '( as ' + xhr.resource + ') : ' + xhr.status); - } else { - console.log('@@ Recording failure for ' + xhr.original + ': ' + xhr.status); - } - this.requested[Uri.docpart(xhr.original.uri)] = xhr.status; // changed 2015 was false - while (this.fetchCallbacks[xhr.original.uri] && this.fetchCallbacks[xhr.original.uri].length) { - this.fetchCallbacks[xhr.original.uri].shift()(false, 'Fetch of <' + xhr.original.uri + '> failed: ' + status, xhr); - } - delete this.fetchCallbacks[xhr.original.uri]; - this.fireCallbacks('fail', [xhr.original.uri, status]); - xhr.abort(); - return xhr; - }; - - // in the why part of the quad distinguish between HTML and HTTP header - // Reverse is set iif the link was rev= as opposed to rel= - this.linkData = function (xhr, rel, uri, why, reverse) { - if (!uri) return; - var predicate; - // See http://www.w3.org/TR/powder-dr/#httplink for describedby 2008-12-10 - var obj = kb.sym(Uri.join(uri, xhr.original.uri)); - if (rel === 'alternate' || rel === 'seeAlso' || rel === 'meta' || rel === 'describedby') { - if (obj.uri === xhr.original.uri) return; - predicate = ns.rdfs('seeAlso'); - } else if (rel === 'type') { - predicate = kb.sym('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'); - } else { - // See https://www.iana.org/assignments/link-relations/link-relations.xml - // Alas not yet in RDF yet for each predicate - /// encode space in e.g. rel="shortcut icon" - predicate = kb.sym(Uri.join(encodeURIComponent(rel), 'http://www.iana.org/assignments/link-relations/')); - } - if (reverse) { - kb.add(obj, predicate, xhr.original, why); - } else { - kb.add(xhr.original, predicate, obj, why); - } - }; - - this.parseLinkHeader = function (xhr, thisReq) { - var link; - try { - link = xhr.getResponseHeader('link'); // May crash from CORS error - } catch (e) {} - if (link) { - var linkexp = /<[^>]*>\s*(\s*;\s*[^\(\)<>@,;:"\/\[\]\?={} \t]+=(([^\(\)<>@,;:"\/\[\]\?={} \t]+)|("[^"]*")))*(,|$)/g; - var paramexp = /[^\(\)<>@,;:"\/\[\]\?={} \t]+=(([^\(\)<>@,;:"\/\[\]\?={} \t]+)|("[^"]*"))/g; - - var matches = link.match(linkexp); - for (var i = 0; i < matches.length; i++) { - var split = matches[i].split('>'); - var href = split[0].substring(1); - var ps = split[1]; - var s = ps.match(paramexp); - for (var j = 0; j < s.length; j++) { - var p = s[j]; - var paramsplit = p.split('='); - // var name = paramsplit[0] - var rel = paramsplit[1].replace(/["']/g, ''); // '" - this.linkData(xhr, rel, href, thisReq); - } - } - } - }; - - this.doneFetch = function (xhr) { - this.addStatus(xhr.req, 'Done.'); - this.requested[xhr.original.uri] = 'done'; // Kenny - while (this.fetchCallbacks[xhr.original.uri] && this.fetchCallbacks[xhr.original.uri].length) { - this.fetchCallbacks[xhr.original.uri].shift()(true, undefined, xhr); - } - delete this.fetchCallbacks[xhr.original.uri]; - this.fireCallbacks('done', [xhr.original.uri]); - }; - var handlerList = [Fetcher.RDFXMLHandler, Fetcher.XHTMLHandler, Fetcher.XMLHandler, Fetcher.HTMLHandler, Fetcher.TextHandler, Fetcher.N3Handler]; - handlerList.map(this.addHandler); - - /** Note two nodes are now smushed - ** - ** If only one was flagged as looked up, then - ** the new node is looked up again, which - ** will make sure all the URIs are dereferenced - */ - this.nowKnownAs = function (was, now) { - if (this.lookedUp[was.uri]) { - if (!this.lookedUp[now.uri]) this.lookUpThing(now, was); // @@@@ Transfer userCallback - } else if (this.lookedUp[now.uri]) { - if (!this.lookedUp[was.uri]) this.lookUpThing(was, now); - } - }; - - // Returns promise of XHR - // - // Writes back to the web what we have in the store for this uri - this.putBack = function (uri) { - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - - uri = uri.uri || uri; // Accept object or string - var doc = new NamedNode(uri).doc(); // strip off # - options.data = serialize(doc, this.store, doc.uri, options.contentType || 'text/turtle'); - return this.webOperation('PUT', uri, options); - }; - - // Returns promise of XHR - // - this.webOperation = function (method, uri) { - var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - - uri = uri.uri || uri; - uri = this.proxyIfNecessary(uri); - var fetcher = this; - return new Promise(function (resolve, reject) { - var xhr = Util.XMLHTTPFactory(); - xhr.options = options; - xhr.original = fetcher.store.sym(uri); - if (!options.noMeta && typeof tabulator !== 'undefined') { - fetcher.saveRequestMetadata(xhr, fetcher.store, uri); - } - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - // NOte a 404 can be not afailure - var ok = !xhr.status || xhr.status >= 200 && xhr.status < 300; - if (!options.noMeta && typeof tabulator !== 'undefined') { - fetcher.saveResponseMetadata(xhr, fetcher.store); - } - if (ok) { - resolve(xhr); - } else { - reject(xhr.status + ' ' + xhr.statusText); - } - } - }; - xhr.open(method, uri, true); - xhr.setRequestHeader('Content-type', options.contentType || 'text/turtle'); - xhr.send(options.data ? options.data : undefined); - }); - }; - - this.webCopy = function (here, there, content_type) { - var fetcher = this; - here = here.uri || here; - return new Promise(function (resolve, reject) { - fetcher.webOperation('GET', here).then(function (xhr) { - fetcher.webOperation('PUT', // @@@ change to binary from text - there, { data: xhr.responseText, contentType: content_type }).then(function (xhr) { - resolve(xhr); - }).catch(function (e) { - reject(e); - }); - }).catch(function (e) { - reject(e); - }); - }); - }; - - // Looks up something. - // - // Looks up all the URIs a things has. - // - // Parameters: - // - // term: canonical term for the thing whose URI is to be dereferenced - // rterm: the resource which refered to this (for tracking bad links) - // options: (old: force paraemter) or dictionary of options: - // force: Load the data even if loaded before - // oneDone: is called as callback(ok, errorbody, xhr) for each one - // allDone: is called as callback(ok, errorbody) for all of them - // Returns the number of URIs fetched - // - this.lookUpThing = function (term, rterm, options, oneDone, allDone) { - var uris = kb.uris(term); // Get all URIs - var success = true; - var errors = ''; - var outstanding = {}; - var force; - if (options === false || options === true) { - // Old signature - force = options; - options = { force: force }; - } else { - if (options === undefined) options = {}; - force = !!options.force; - } - - if (typeof uris !== 'undefined') { - for (var i = 0; i < uris.length; i++) { - var u = uris[i]; - outstanding[u] = true; - this.lookedUp[u] = true; - var sf = this; - - var requestOne = function requestOne(u1) { - sf.requestURI(Uri.docpart(u1), rterm, options, function (ok, body, xhr) { - if (ok) { - if (oneDone) oneDone(true, u1); - } else { - if (oneDone) oneDone(false, body); - success = false; - errors += body + '\n'; - } - delete outstanding[u]; - if (Object.keys(outstanding).length > 0) { - return; - } - if (allDone) { - allDone(success, errors); - } - }); - }; - requestOne(u); - } - } - return uris.length; - }; - - /* Promise-based load function - ** - ** NamedNode -> Promise of xhr - ** uri string -> Promise of xhr - ** Array of the above -> Promise of array of xhr - ** - ** @@ todo: If p1 is array then sequence or parallel fetch of all - */ - this.load = function (uri, options) { - var fetcher = this; - if (uri instanceof Array) { - var ps = uri.map(function (x) { - return fetcher.load(x); - }); - return Promise.all(ps); - } - uri = uri.uri || uri; // NamedNode or URI string - return new Promise(function (resolve, reject) { - fetcher.nowOrWhenFetched(uri, options, function (ok, message, xhr) { - if (ok) { - resolve(xhr); - } else { - reject(message); - } - }); - }); - }; - - /* Ask for a doc to be loaded if necessary then call back - ** - ** Changed 2013-08-20: Added (ok, errormessage) params to callback - ** - ** Calling methods: - ** nowOrWhenFetched (uri, userCallback) - ** nowOrWhenFetched (uri, options, userCallback) - ** nowOrWhenFetched (uri, referringTerm, userCallback, options) <-- old - ** nowOrWhenFetched (uri, referringTerm, userCallback) <-- old - ** - ** Options include: - ** referringTerm The docuemnt in which this link was found. - ** this is valuable when finding the source of bad URIs - ** force boolean. Never mind whether you have tried before, - ** load this from scratch. - ** forceContentType Override the incoming header to force the data to be - ** treaed as this content-type. - **/ - this.nowOrWhenFetched = function (uri, p2, userCallback, options) { - uri = uri.uri || uri; // allow symbol object or string to be passed - if (typeof p2 === 'function') { - options = {}; - userCallback = p2; - } else if (typeof p2 === 'undefined') {// original calling signature - // referingTerm = undefined - } else if (p2 instanceof NamedNode) { - // referingTerm = p2 - options = { referingTerm: p2 }; - } else { - options = p2; - } - - this.requestURI(uri, p2, options || {}, userCallback); - }; - - this.get = this.nowOrWhenFetched; - - // Look up response header - // - // Returns: a list of header values found in a stored HTTP response - // or [] if response was found but no header found - // or undefined if no response is available. - // - this.getHeader = function (doc, header) { - var kb = this.store; - var requests = kb.each(undefined, ns.link('requestedURI'), doc.uri); - for (var r = 0; r < requests.length; r++) { - var request = requests[r]; - if (request !== undefined) { - var response = kb.any(request, ns.link('response')); - if (request !== undefined) { - var results = kb.each(response, ns.httph(header.toLowerCase())); - if (results.length) { - return results.map(function (v) { - return v.value; - }); - } - return []; - } - } - } - return undefined; - }; - - this.proxyIfNecessary = function (uri) { - if (typeof tabulator !== 'undefined' && tabulator.isExtension) return uri; // Extenstion does not need proxy - - if (typeof $SolidTestEnvironment !== 'undefined' && $SolidTestEnvironment.localSiteMap) { - // nested dictionaries of URI parts from origin down - var hostpath = uri.split('/').slice(2); // the bit after the // - var lookup = function lookup(parts, index) { - var z = index[parts.shift()]; - if (!z) return null; - if (typeof z === 'string') { - return z + parts.join('/'); - } - if (!parts) return null; - return lookup(parts, z); - }; - var y = lookup(hostpath, $SolidTestEnvironment.localSiteMap); - if (y) { - return y; - } - } - // browser does 2014 on as https browser script not trusted - // If the web app origin is https: then the mixed content rules - // prevent it loading insecure http: stuff so we need proxy. - if (Fetcher.crossSiteProxyTemplate && typeof document !== 'undefined' && document.location && ('' + document.location).slice(0, 6) === 'https:' && // origin is secure - uri.slice(0, 5) === 'http:') { - // requested data is not - return Fetcher.crossSiteProxyTemplate.replace('{uri}', encodeURIComponent(uri)); - } - return uri; - }; - - this.saveRequestMetadata = function (xhr, kb, docuri) { - var request = kb.bnode(); - xhr.resource = kb.sym(docuri); - - xhr.req = request; - if (!xhr.options.noMeta) { - // Store no triples but do mind the bnode for req - var now = new Date(); - var timeNow = '[' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds() + '] '; - kb.add(request, ns.rdfs('label'), kb.literal(timeNow + ' Request for ' + docuri), this.appNode); - kb.add(request, ns.link('requestedURI'), kb.literal(docuri), this.appNode); - if (xhr.original && xhr.original.uri !== docuri) { - kb.add(request, ns.link('orginalURI'), kb.literal(xhr.original.uri), this.appNode); - } - kb.add(request, ns.link('status'), kb.collection(), this.appNode); - } - return request; - }; - - this.saveResponseMetadata = function (xhr, kb) { - var response = kb.bnode(); - - if (xhr.req) kb.add(xhr.req, ns.link('response'), response); - kb.add(response, ns.http('status'), kb.literal(xhr.status), response); - kb.add(response, ns.http('statusText'), kb.literal(xhr.statusText), response); - - xhr.headers = {}; - if (Uri.protocol(xhr.resource.uri) === 'http' || Uri.protocol(xhr.resource.uri) === 'https') { - xhr.headers = Util.getHTTPHeaders(xhr); - for (var h in xhr.headers) { - // trim below for Safari - adds a CR! - var value = xhr.headers[h].trim(); - var h2 = h.toLowerCase(); - kb.add(response, ns.httph(h2), value, response); - if (h2 === 'content-type') { - // Convert to RDF type - kb.add(xhr.resource, ns.rdf('type'), Util.mediaTypeClass(value), response); - } - } - } - return response; - }; - - /** Requests a document URI and arranges to load the document. - ** Parameters: - ** term: term for the thing whose URI is to be dereferenced - ** rterm: the resource which refered to this (for tracking bad links) - ** options: - ** force: Load the data even if loaded before - ** withCredentials: flag for XHR/CORS etc - ** userCallback: Called with (true) or (false, errorbody, {status: 400}) after load is done or failed - ** Return value: - ** The xhr object for the HTTP access - ** null if the protocol is not a look-up protocol, - ** or URI has already been loaded - */ - this.requestURI = function (docuri, rterm, options, userCallback) { - // sources_request_new - // Various calling conventions - docuri = docuri.uri || docuri; // NamedNode or string - docuri = docuri.split('#')[0]; - if (typeof options === 'boolean') { - options = { 'force': options }; // Ols dignature - } - if (typeof options === 'undefined') options = {}; - - var force = !!options.force; - var kb = this.store; - var args = arguments; - var baseURI = options.baseURI || docuri; // Preseve though proxying etc - options.userCallback = userCallback; - - var pcol = Uri.protocol(docuri); - if (pcol === 'tel' || pcol === 'mailto' || pcol === 'urn') { - // "No look-up operation on these, but they are not errors?" - console.log('Unsupported protocol in: ' + docuri); - return userCallback(false, 'Unsupported protocol', { 'status': 900 }) || undefined; - } - var docterm = kb.sym(docuri); - - var sta = this.getState(docuri); - if (!force) { - if (sta === 'fetched') { - return userCallback ? userCallback(true) : undefined; - } - if (sta === 'failed') { - return userCallback ? userCallback(false, 'Previously failed. ' + this.requested[docuri], { 'status': this.requested[docuri] }) : undefined; // An xhr standin - } - // if (sta === 'requested') return userCallback? userCallback(false, "Sorry already requested - pending already.", {'status': 999 }) : undefined - } else { - delete this.nonexistant[docuri]; - } - // @@ Should allow concurrent requests - - // If it is 'failed', then shoulkd we try again? I think so so an old error doens't get stuck - // if (sta === 'unrequested') - - this.fireCallbacks('request', args); // Kenny: fire 'request' callbacks here - // dump( "web.js: Requesting uri: " + docuri + "\n" ) - - if (userCallback) { - if (!this.fetchCallbacks[docuri]) { - this.fetchCallbacks[docuri] = [userCallback]; - } else { - this.fetchCallbacks[docuri].push(userCallback); - } - } - - if (this.requested[docuri] === true) { - return; // Don't ask again - wait for existing call - } else { - this.requested[docuri] = true; - } - - if (!options.noMeta && rterm && rterm.uri) { - kb.add(docterm.uri, ns.link('requestedBy'), rterm.uri, this.appNode); - } - - var xhr = Util.XMLHTTPFactory(); - var req = xhr.req = kb.bnode(); - xhr.original = kb.sym(baseURI); - // console.log('XHR original: ' + xhr.original) - xhr.options = options; - xhr.resource = docterm; // This might be proxified - var sf = this; - - var now = new Date(); - var timeNow = '[' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds() + '] '; - if (!options.noMeta) { - kb.add(req, ns.rdfs('label'), kb.literal(timeNow + ' Request for ' + docuri), this.appNode); - kb.add(req, ns.link('requestedURI'), kb.literal(docuri), this.appNode); - kb.add(req, ns.link('status'), kb.collection(), this.appNode); - } - - var checkCredentialsRetry = function checkCredentialsRetry() { - if (!xhr.withCredentials) return false; // not dealt with - - if (xhr.retriedWithCredentials) { - return true; - } - xhr.retriedWithCredentials = true; // protect against called twice - console.log('web: Retrying with no credentials for ' + xhr.resource); - xhr.abort(); - delete sf.requested[docuri]; // forget the original request happened - var newopt = {}; - for (var opt in options) { - // transfer baseURI etc - if (options.hasOwnProperty(opt)) { - newopt[opt] = options[opt]; - } - } - newopt.withCredentials = false; - sf.addStatus(xhr.req, 'Abort: Will retry with credentials SUPPRESSED to see if that helps'); - sf.requestURI(docuri, rterm, newopt, xhr.userCallback); // userCallback already registered (with where?) - return true; - }; - - var onerrorFactory = function onerrorFactory(xhr) { - return function (event) { - xhr.onErrorWasCalled = true; // debugging and may need it - if (typeof document !== 'undefined') { - // Mashup situation, not node etc - if (Fetcher.crossSiteProxyTemplate && document.location && !xhr.proxyUsed) { - var hostpart = Uri.hostpart; - var here = '' + document.location; - var uri = xhr.resource.uri; - if (hostpart(here) && hostpart(uri) && hostpart(here) !== hostpart(uri)) { - // If cross-site - if (xhr.status === 401 || xhr.status === 403 || xhr.status === 404) { - onreadystatechangeFactory(xhr)(); - } else { - // IT IS A PAIN THAT NO PROPER ERROR REPORTING - if (checkCredentialsRetry(xhr)) { - // If credentials flag set, retry without, - return; - } - // If it wasn't, or we already tried that - var newURI = Fetcher.crossSiteProxy(uri); - console.log('web: Direct failed so trying proxy ' + newURI); - sf.addStatus(xhr.req, 'BLOCKED -> Cross-site Proxy to <' + newURI + '>'); - if (xhr.aborted) return; - - var kb = sf.store; - var oldreq = xhr.req; - if (!xhr.options.noMeta) { - kb.add(oldreq, ns.http('redirectedTo'), kb.sym(newURI), oldreq); - } - xhr.abort(); - xhr.aborted = true; - - sf.addStatus(oldreq, 'redirected to new request'); // why - // the callback throws an exception when called from xhr.onerror (so removed) - // sf.fireCallbacks('done', args) // Are these args right? @@@ Not done yet! done means success - sf.requested[xhr.resource.uri] = 'redirected'; - sf.redirectedTo[xhr.resource.uri] = newURI; - - if (sf.fetchCallbacks[xhr.resource.uri]) { - if (!sf.fetchCallbacks[newURI]) { - sf.fetchCallbacks[newURI] = []; - } - sf.fetchCallbacks[newURI] === sf.fetchCallbacks[newURI].concat(sf.fetchCallbacks[xhr.resource.uri]); - delete sf.fetchCallbacks[xhr.resource.uri]; - } - - var xhr2 = sf.requestURI(newURI, xhr.resource, xhr.options, xhr.userCallback); - if (xhr2) { - xhr2.proxyUsed = true; // only try the proxy once - xhr2.original = xhr.original; - console.log('Proxying but original still ' + xhr2.original); - } - if (xhr2 && xhr2.req) { - if (!xhr.options.noMeta) { - kb.add(xhr.req, kb.sym('http://www.w3.org/2007/ont/link#redirectedRequest'), xhr2.req, sf.appNode); - } - return; - } - } - } - xhr.CORS_status = 999; - // xhr.status = 999 forbidden - read-only - } - } // mashu - }; // function of event - }; // onerrorFactory - - // Set up callbacks - var onreadystatechangeFactory = function onreadystatechangeFactory(xhr) { - return function () { - var handleResponse = function handleResponse() { - if (xhr.handleResponseDone) return; - xhr.handleResponseDone = true; - var handler = null; - var thisReq = xhr.req; // Might have changes by redirect - sf.fireCallbacks('recv', args); - var kb = sf.store; - sf.saveResponseMetadata(xhr, kb); - sf.fireCallbacks('headers', [{ uri: docuri, headers: xhr.headers }]); - - // Check for masked errors. - // For "security reasons" theboraser hides errors such as CORS errors from - // the calling code (2015). oneror() used to be called but is not now. - // - if (xhr.status === 0) { - console.log('Masked error - status 0 for ' + xhr.resource.uri); - if (checkCredentialsRetry(xhr)) { - // retry is could be credentials flag CORS issue - return; - } - xhr.CORS_status = 900; // unknown masked error - return; - } - if (xhr.status >= 400) { - // For extra dignostics, keep the reply - // @@@ 401 should cause a retry with credential son - // @@@ cache the credentials flag by host ???? - if (xhr.status === 404) { - kb.fetcher.nonexistant[xhr.resource.uri] = true; - } - if (xhr.responseText.length > 10) { - var response2 = kb.bnode(); - kb.add(response2, ns.http('content'), kb.literal(xhr.responseText), response2); - if (xhr.statusText) { - kb.add(response2, ns.http('statusText'), kb.literal(xhr.statusText), response2); - } - // dump("HTTP >= 400 responseText:\n"+xhr.responseText+"\n"); // @@@@ - } - sf.failFetch(xhr, 'HTTP error for ' + xhr.resource + ': ' + xhr.status + ' ' + xhr.statusText); - return; - } - - var loc = xhr.headers['content-location']; - - // deduce some things from the HTTP transaction - var addType = function addType(cla) { - // add type to all redirected resources too - var prev = thisReq; - if (loc) { - var docURI = kb.any(prev, ns.link('requestedURI')); - if (docURI !== loc) { - kb.add(kb.sym(loc), ns.rdf('type'), cla, sf.appNode); - } - } - for (;;) { - var doc = kb.any(prev, ns.link('requestedURI')); - if (doc && doc.value) { - kb.add(kb.sym(doc.value), ns.rdf('type'), cla, sf.appNode); - } // convert Literal - prev = kb.any(undefined, kb.sym('http://www.w3.org/2007/ont/link#redirectedRequest'), prev); - if (!prev) break; - var response = kb.any(prev, kb.sym('http://www.w3.org/2007/ont/link#response')); - if (!response) break; - var redirection = kb.any(response, kb.sym('http://www.w3.org/2007/ont/http#status')); - if (!redirection) break; - if (redirection !== '301' && redirection !== '302') break; - } - }; - // This is a minimal set to allow the use of damaged servers if necessary - var extensionToContentType = { - 'rdf': 'application/rdf+xml', 'owl': 'application/rdf+xml', - 'n3': 'text/n3', 'ttl': 'text/turtle', 'nt': 'text/n3', 'acl': 'text/n3', - 'html': 'text/html', - 'xml': 'text/xml' - }; - var guess; - if (xhr.status === 200) { - addType(ns.link('Document')); - var ct = xhr.headers['content-type']; - if (options.forceContentType) { - xhr.headers['content-type'] = options.forceContentType; - } - if (!ct || ct.indexOf('application/octet-stream') >= 0) { - guess = extensionToContentType[xhr.resource.uri.split('.').pop()]; - if (guess) { - xhr.headers['content-type'] = guess; - } - } - if (ct) { - if (ct.indexOf('image/') === 0 || ct.indexOf('application/pdf') === 0) addType(kb.sym('http://purl.org/dc/terms/Image')); - } - if (options.clearPreviousData) { - // Before we parse new data clear old but only on 200 - kb.removeDocument(xhr.resource); - } - } - // application/octet-stream; charset=utf-8 - - if (Uri.protocol(xhr.resource.uri) === 'file' || Uri.protocol(xhr.resource.uri) === 'chrome') { - if (options.forceContentType) { - xhr.headers['content-type'] = options.forceContentType; - } else { - guess = extensionToContentType[xhr.resource.uri.split('.').pop()]; - if (guess) { - xhr.headers['content-type'] = guess; - } else { - xhr.headers['content-type'] = 'text/xml'; - } - } - } - - // If we have alread got the thing at this location, abort - if (loc) { - var udoc = Uri.join(xhr.resource.uri, loc); - if (!force && udoc !== xhr.resource.uri && sf.requested[udoc] && sf.requested[udoc] === 'done') { - // we have already fetched this in fact. - // should we smush too? - // log.info("HTTP headers indicate we have already" + " retrieved " + xhr.resource + " as " + udoc + ". Aborting.") - sf.doneFetch(xhr); - xhr.abort(); - return; - } - sf.requested[udoc] = true; - } - - for (var x = 0; x < sf.handlers.length; x++) { - if (xhr.headers['content-type'] && xhr.headers['content-type'].match(sf.handlers[x].pattern)) { - handler = new sf.handlers[x](); - break; - } - } - - sf.parseLinkHeader(xhr, thisReq); - - if (handler) { - try { - handler.handlerFactory(xhr); - } catch (e) { - // Try to avoid silent errors - sf.failFetch(xhr, 'Exception handling content-type ' + xhr.headers['content-type'] + ' was: ' + e); - } - } else { - sf.doneFetch(xhr); // Not a problem, we just don't extract data. - /* - // sf.failFetch(xhr, "Unhandled content type: " + xhr.headers['content-type']+ - // ", readyState = "+xhr.readyState) - */ - return; - } - }; - - // DONE: 4 - // HEADERS_RECEIVED: 2 - // LOADING: 3 - // OPENED: 1 - // UNSENT: 0 - - // log.debug("web.js: XHR " + xhr.resource.uri + ' readyState='+xhr.readyState); // @@@@ comment me out - - switch (xhr.readyState) { - case 0: - var uri = xhr.resource.uri; - var newURI; - if (this.crossSiteProxyTemplate && typeof document !== 'undefined' && document.location) { - // In mashup situation - var hostpart = Uri.hostpart; - var here = '' + document.location; - if (hostpart(here) && hostpart(uri) && hostpart(here) !== hostpart(uri)) { - newURI = this.crossSiteProxyTemplate.replace('{uri}', encodeURIComponent(uri)); - sf.addStatus(xhr.req, 'BLOCKED -> Cross-site Proxy to <' + newURI + '>'); - if (xhr.aborted) return; - - var kb = sf.store; - var oldreq = xhr.req; - kb.add(oldreq, ns.http('redirectedTo'), kb.sym(newURI), oldreq); - - // //////////// Change the request node to a new one: @@@@@@@@@@@@ Duplicate? - var newreq = xhr.req = kb.bnode(); // Make NEW reqest for everything else - kb.add(oldreq, ns.http('redirectedRequest'), newreq, xhr.req); - - var now = new Date(); - var timeNow = '[' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds() + '] '; - kb.add(newreq, ns.rdfs('label'), kb.literal(timeNow + ' Request for ' + newURI), this.appNode); - kb.add(newreq, ns.link('status'), kb.collection(), this.appNode); - kb.add(newreq, ns.link('requestedURI'), kb.literal(newURI), this.appNode); - - var response = kb.bnode(); - kb.add(oldreq, ns.link('response'), response); - // kb.add(response, ns.http('status'), kb.literal(xhr.status), response) - // if (xhr.statusText) kb.add(response, ns.http('statusText'), kb.literal(xhr.statusText), response) - - xhr.abort(); - xhr.aborted = true; - xhr.redirected = true; - - sf.addStatus(oldreq, 'redirected XHR'); // why - - if (sf.fetchCallbacks[xhr.resource.uri]) { - if (!sf.fetchCallbacks[newURI]) { - sf.fetchCallbacks[newURI] = []; - } - sf.fetchCallbacks[newURI] === sf.fetchCallbacks[newURI].concat(sf.fetchCallbacks[xhr.resource.uri]); - delete sf.fetchCallbacks[xhr.resource.uri]; - } - - sf.fireCallbacks('redirected', args); // Are these args right? @@@ - sf.requested[xhr.resource.uri] = 'redirected'; - - var xhr2 = sf.requestURI(newURI, xhr.resource, xhr.options || {}, xhr.userCallback); - if (xhr2 && xhr2.req) { - kb.add(xhr.req, kb.sym('http://www.w3.org/2007/ont/link#redirectedRequest'), xhr2.req, sf.appNode); - return; - } - } - } - sf.failFetch(xhr, 'HTTP Blocked. (ReadyState 0) Cross-site violation for <' + docuri + '>'); - - break; - - case 3: - // Intermediate state -- 3 may OR MAY NOT be called, selon browser. - // handleResponse(); // In general it you can't do it yet as the headers are in but not the data - break; - case 4: - // Final state for this XHR but may be redirected - handleResponse(); - // Now handle - if (xhr.handle && xhr.responseText !== undefined) { - // can be validly zero length - if (sf.requested[xhr.resource.uri] === 'redirected') { - break; - } - sf.fireCallbacks('load', args); - xhr.handle(function () { - sf.doneFetch(xhr); - }); - } else { - if (xhr.redirected) { - sf.addStatus(xhr.req, 'Aborted and redirected to new request.'); - } else { - sf.addStatus(xhr.req, 'Fetch over. No data handled. Aborted = ' + xhr.aborted); - } - // sf.failFetch(xhr, "HTTP failed unusually. (no handler set) (x-site violation? no net?) for <"+ - // docuri+">") - } - break; - } // switch - }; - }; - - // Map the URI to a localhost proxy if we are running on localhost - // This is used for working offline, e.g. on planes. - // Is the script istelf is running in localhost, then access all data in a localhost mirror. - // Do not remove without checking with TimBL - var uri2 = docuri; - if (typeof tabulator !== 'undefined' && tabulator.preferences.get('offlineModeUsingLocalhost')) { - if (uri2.slice(0, 7) === 'http://' && uri2.slice(7, 17) !== 'localhost/') { - uri2 = 'http://localhost/' + uri2.slice(7); - log.warn('Localhost kludge for offline use: actually getting <' + uri2 + '>'); - } else { - // log.warn("Localhost kludge NOT USED <" + uri2 + ">") - } - } else {} - // log.warn("Localhost kludge OFF offline use: actually getting <" + uri2 + ">") - - // 2014 probelm: - // XMLHttpRequest cannot load http://www.w3.org/People/Berners-Lee/card. - // A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true. - // @ Many ontology files under http: and need CORS wildcard -> can't have withCredentials - - var withCredentials = uri2.slice(0, 6) === 'https:'; // @@ Kludge -- need for webid which typically is served from https - if (options.withCredentials !== undefined) { - withCredentials = options.withCredentials; - } - var actualProxyURI = this.proxyIfNecessary(uri2); - - // Setup the request - // var xhr - // xhr = Util.XMLHTTPFactory() - xhr.onerror = onerrorFactory(xhr); - xhr.onreadystatechange = onreadystatechangeFactory(xhr); - xhr.timeout = sf.timeout; - xhr.withCredentials = withCredentials; - xhr.actualProxyURI = actualProxyURI; - - xhr.req = req; - xhr.options = options; - xhr.options = options; - xhr.resource = docterm; - xhr.requestedURI = uri2; - - xhr.ontimeout = function () { - sf.failFetch(xhr, 'requestTimeout'); - }; - try { - xhr.open('GET', actualProxyURI, this.async); - } catch (er) { - return this.failFetch(xhr, 'XHR open for GET failed for <' + uri2 + '>:\n\t' + er); - } - if (force) { - // must happen after open - xhr.setRequestHeader('Cache-control', 'no-cache'); - } - - // Set redirect callback and request headers -- alas Firefox Extension Only - if (typeof tabulator !== 'undefined' && tabulator.isExtension && xhr.channel && (Uri.protocol(xhr.resource.uri) === 'http' || Uri.protocol(xhr.resource.uri) === 'https')) { - try { - xhr.channel.notificationCallbacks = { - getInterface: function getInterface(iid) { - if (iid.equals(Components.interfaces.nsIChannelEventSink)) { - return { - onChannelRedirect: function onChannelRedirect(oldC, newC, flags) { - if (xhr.aborted) return; - var kb = sf.store; - var newURI = newC.URI.spec; - var oldreq = xhr.req; - if (!xhr.options.noMeta) { - sf.addStatus(xhr.req, 'Redirected: ' + xhr.status + ' to <' + newURI + '>'); - kb.add(oldreq, ns.http('redirectedTo'), kb.sym(newURI), xhr.req); - - // //////////// Change the request node to a new one: @@@@@@@@@@@@ Duplicate code? - var newreq = xhr.req = kb.bnode(); // Make NEW reqest for everything else - kb.add(oldreq, ns.http('redirectedRequest'), newreq, this.appNode); - - var now = new Date(); - var timeNow = '[' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds() + '] '; - kb.add(newreq, ns.rdfs('label'), kb.literal(timeNow + ' Request for ' + newURI), this.appNode); - kb.add(newreq, ns.link('status'), kb.collection(), this.appNode); - kb.add(newreq, ns.link('requestedURI'), kb.literal(newURI), this.appNode); - // ///////////// - - // // log.info('@@ sources onChannelRedirect'+ - // "Redirected: "+ - // xhr.status + " to <" + newURI + ">"); //@@ - var response = kb.bnode(); - // kb.add(response, ns.http('location'), newURI, response); Not on this response - kb.add(oldreq, ns.link('response'), response); - kb.add(response, ns.http('status'), kb.literal(xhr.status), response); - if (xhr.statusText) kb.add(response, ns.http('statusText'), kb.literal(xhr.statusText), response); - } - if (xhr.status - 0 !== 303) kb.HTTPRedirects[xhr.resource.uri] = newURI; // same document as - if (xhr.status - 0 === 301 && rterm) { - // 301 Moved - var badDoc = Uri.docpart(rterm.uri); - var msg = 'Warning: ' + xhr.resource + ' has moved to <' + newURI + '>.'; - if (rterm) { - msg += ' Link in <' + badDoc + ' >should be changed'; - kb.add(badDoc, kb.sym('http://www.w3.org/2007/ont/link#warning'), msg, sf.appNode); - } - // dump(msg+"\n") - } - xhr.abort(); - xhr.aborted = true; - - if (sf.fetchCallbacks[xhr.resource.uri]) { - if (!sf.fetchCallbacks[newURI]) { - sf.fetchCallbacks[newURI] = []; - } - sf.fetchCallbacks[newURI] === sf.fetchCallbacks[newURI].concat(sf.fetchCallbacks[xhr.resource.uri]); - delete sf.fetchCallbacks[xhr.resource.uri]; - } - - sf.addStatus(oldreq, 'redirected'); // why - sf.fireCallbacks('redirected', args); // Are these args right? @@@ - sf.requested[xhr.resource.uri] = 'redirected'; - sf.redirectedTo[xhr.resource.uri] = newURI; - - var hash = newURI.indexOf('#'); - if (hash >= 0) { - if (!xhr.options.noMeta) { - kb.add(xhr.resource, kb.sym('http://www.w3.org/2007/ont/link#warning'), 'Warning: ' + xhr.resource + ' HTTP redirects to' + newURI + ' which should not contain a "#" sign'); - } - newURI = newURI.slice(0, hash); - } - var xhr2 = sf.requestURI(newURI, xhr.resource, xhr.options, xhr.userCallback); - if (xhr2 && xhr2.req && !options.noMeta) { - kb.add(xhr.req, kb.sym('http://www.w3.org/2007/ont/link#redirectedRequest'), xhr2.req, sf.appNode); - } - // else dump("No xhr.req available for redirect from "+xhr.resource+" to "+newURI+"\n") - }, - - // See https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIChannelEventSink - asyncOnChannelRedirect: function asyncOnChannelRedirect(oldC, newC, flags, callback) { - if (xhr.aborted) return; - var kb = sf.store; - var newURI = newC.URI.spec; - var oldreq = xhr.req; - sf.addStatus(xhr.req, 'Redirected: ' + xhr.status + ' to <' + newURI + '>'); - kb.add(oldreq, ns.http('redirectedTo'), kb.sym(newURI), xhr.req); - - // //////////// Change the request node to a new one: @@@@@@@@@@@@ Duplicate? - var newreq = xhr.req = kb.bnode(); // Make NEW reqest for everything else - // xhr.resource = docterm - // xhr.requestedURI = args[0] - - // kb.add(kb.sym(newURI), ns.link("request"), req, this.appNode) - kb.add(oldreq, ns.http('redirectedRequest'), newreq, xhr.req); - - var now = new Date(); - var timeNow = '[' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds() + '] '; - kb.add(newreq, ns.rdfs('label'), kb.literal(timeNow + ' Request for ' + newURI), this.appNode); - kb.add(newreq, ns.link('status'), kb.collection(), this.appNode); - kb.add(newreq, ns.link('requestedURI'), kb.literal(newURI), this.appNode); - // ///////////// - - // // log.info('@@ sources onChannelRedirect'+ - // "Redirected: "+ - // xhr.status + " to <" + newURI + ">"); //@@ - var response = kb.bnode(); - // kb.add(response, ns.http('location'), newURI, response); Not on this response - kb.add(oldreq, ns.link('response'), response); - kb.add(response, ns.http('status'), kb.literal(xhr.status), response); - if (xhr.statusText) kb.add(response, ns.http('statusText'), kb.literal(xhr.statusText), response); - - if (xhr.status - 0 !== 303) kb.HTTPRedirects[xhr.resource.uri] = newURI; // same document as - if (xhr.status - 0 === 301 && rterm) { - // 301 Moved - var badDoc = Uri.docpart(rterm.uri); - var msg = 'Warning: ' + xhr.resource + ' has moved to <' + newURI + '>.'; - if (rterm) { - msg += ' Link in <' + badDoc + ' >should be changed'; - kb.add(badDoc, kb.sym('http://www.w3.org/2007/ont/link#warning'), msg, sf.appNode); - } - // dump(msg+"\n") - } - xhr.abort(); - xhr.aborted = true; - - var hash = newURI.indexOf('#'); - if (hash >= 0) { - var msg2 = 'Warning: ' + xhr.resource + ' HTTP redirects to' + newURI + ' which do not normally contain a "#" sign'; - // dump(msg+"\n") - kb.add(xhr.resource, kb.sym('http://www.w3.org/2007/ont/link#warning'), msg2); - newURI = newURI.slice(0, hash); - } - /* - if (sf.fetchCallbacks[xhr.resource.uri]) { - if (!sf.fetchCallbacks[newURI]) { - sf.fetchCallbacks[newURI] = [] - } - sf.fetchCallbacks[newURI] = sf.fetchCallbacks[newURI].concat(sf.fetchCallbacks[xhr.resource.uri]) - delete sf.fetchCallbacks[xhr.resource.uri] - } - */ - sf.requested[xhr.resource.uri] = 'redirected'; - sf.redirectedTo[xhr.resource.uri] = newURI; - - var xhr2 = sf.requestURI(newURI, xhr.resource, xhr.options, xhr.userCallback); - if (xhr2) { - // may be no XHR is other URI already loaded - xhr2.original = xhr.original; // use this for finding base - if (xhr2.req) { - kb.add(xhr.req, kb.sym('http://www.w3.org/2007/ont/link#redirectedRequest'), xhr2.req, sf.appNode); - } - } - // else dump("No xhr.req available for redirect from "+xhr.resource+" to "+newURI+"\n") - } // asyncOnChannelRedirect - }; - } - return Components.results.NS_NOINTERFACE; - } - }; - } catch (err) { - return sf.failFetch(xhr, "@@ Couldn't set callback for redirects: " + err); - } // try - } // if Firefox extension - - try { - var acceptstring = ''; - for (var type in this.mediatypes) { - // var attrstring = '' - if (acceptstring !== '') { - acceptstring += ', '; - } - acceptstring += type; - for (var attr in this.mediatypes[type]) { - acceptstring += ';' + attr + '=' + this.mediatypes[type][attr]; - } - } - xhr.setRequestHeader('Accept', acceptstring); - this.addStatus(xhr.req, 'Accept: ' + acceptstring); - - // if (requester) { xhr.setRequestHeader('Referer',requester) } - } catch (err) { - throw new Error("Can't set Accept header: " + err); - } - - // Fire - try { - xhr.send(null); - } catch (er) { - return this.failFetch(xhr, 'XHR send failed:' + er); - } - setTimeout(function () { - if (xhr.readyState !== 4 && sf.isPending(xhr.resource.uri)) { - sf.failFetch(xhr, 'requestTimeout'); - } - }, this.timeout); - this.addStatus(xhr.req, 'HTTP Request sent.'); - return xhr; - }; // this.requestURI() - - this.objectRefresh = function (term) { - var uris = kb.uris(term); // Get all URIs - if (typeof uris !== 'undefined') { - for (var i = 0; i < uris.length; i++) { - this.refresh(this.store.sym(Uri.docpart(uris[i]))); - // what about rterm? - } - } - }; - - // deprecated -- use IndexedFormula.removeDocument(doc) - this.unload = function (term) { - this.store.removeMany(undefined, undefined, undefined, term); - delete this.requested[term.uri]; // So it can be loaded again - }; - - this.refresh = function (term, userCallback) { - // sources_refresh - this.fireCallbacks('refresh', arguments); - this.requestURI(term.uri, undefined, { force: true, clearPreviousData: true }, userCallback); - }; - - this.retract = function (term) { - // sources_retract - this.store.removeMany(undefined, undefined, undefined, term); - if (term.uri) { - delete this.requested[Uri.docpart(term.uri)]; - } - this.fireCallbacks('retract', arguments); - }; - - this.getState = function (docuri) { - if (typeof this.requested[docuri] === 'undefined') { - return 'unrequested'; - } else if (this.requested[docuri] === true) { - return 'requested'; - } else if (this.requested[docuri] === 'done') { - return 'fetched'; - } else if (this.requested[docuri] === 'redirected') { - return this.getState(this.redirectedTo[docuri]); - } else { - // An non-200 HTTP error status - return 'failed'; - } - }; - - // doing anyStatementMatching is wasting time - this.isPending = function (docuri) { - // sources_pending - // if it's not pending: false -> flailed 'done' -> done 'redirected' -> redirected - return this.requested[docuri] === true; - }; - // var updatesVia = new $rdf.UpdatesVia(this) // Subscribe to headers - // @@@@@@@@ This is turned off because it causes a websocket to be set up for ANY fetch - // whether we want to track it ot not. including ontologies loaed though the XSSproxy -}; // End of fetcher - module.exports = Fetcher; },{"./log":64,"./n3parser":65,"./named-node":66,"./namespace":67,"./parse":69,"./rdfaparser":73,"./rdfxmlparser":74,"./serialize":75,"./uri":81,"./util":82}],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 Collection = _dereq_('./collection'); -var Literal = _dereq_('./literal'); -var log = _dereq_('./log'); -var NamedNode = _dereq_('./named-node'); -var Node = _dereq_('./node'); -var Serializer = _dereq_('./serialize'); -var Statement = _dereq_('./statement'); -var Variable = _dereq_('./variable'); - -var Formula = function (_Node) { - _inherits(Formula, _Node); - - function Formula(statements, constraints, initBindings, optional) { - _classCallCheck(this, Formula); - - var _this = _possibleConstructorReturn(this, (Formula.__proto__ || Object.getPrototypeOf(Formula)).call(this)); - - _this.termType = Formula.termType; - _this.statements = statements || []; - _this.constraints = constraints || []; - _this.initBindings = initBindings || []; - _this.optional = optional || []; - return _this; - } - - _createClass(Formula, [{ - key: 'add', - value: function add(s, p, o, g) { - return this.statements.push(new Statement(s, p, o, g)); - } - }, { - key: 'addStatement', - value: function addStatement(st) { - return this.statements.push(st); - } - }, { - key: 'bnode', - value: function bnode(id) { - return new BlankNode(id); - } - /** - * Finds the types in the list which have no *stored* subtypes - * These are a set of classes which provide by themselves complete - * information -- the other classes are redundant for those who - * know the class DAG. - */ - - }, { - key: 'bottomTypeURIs', - value: function bottomTypeURIs(types) { - var bots; - var bottom; - var elt; - var i; - var k; - var len; - var ref; - var subs; - var v; - bots = []; - for (k in types) { - if (!types.hasOwnProperty(k)) continue; - v = types[k]; - subs = this.each(void 0, this.sym('http://www.w3.org/2000/01/rdf-schema#subClassOf'), this.sym(k)); - bottom = true; - i = 0; - for (len = subs.length; i < len; i++) { - elt = subs[i]; - ref = elt.uri; - if (ref in types) { - // the subclass is one we know - bottom = false; - break; - } - } - if (bottom) { - bots[k] = v; - } - } - return bots; - } - }, { - key: 'collection', - value: function collection() { - return new Collection(); - } - }, { - key: 'each', - value: function each(s, p, o, g) { - var elt, i, l, m, q; - var len, len1, len2, len3; - var results = []; - var sts = this.statementsMatching(s, p, o, g, false); - if (s == null) { - for (i = 0, len = sts.length; i < len; i++) { - elt = sts[i]; - results.push(elt.subject); - } - } else if (p == null) { - for (l = 0, len1 = sts.length; l < len1; l++) { - elt = sts[l]; - results.push(elt.predicate); - } - } else if (o == null) { - for (m = 0, len2 = sts.length; m < len2; m++) { - elt = sts[m]; - results.push(elt.object); - } - } else if (g == null) { - for (q = 0, len3 = sts.length; q < len3; q++) { - elt = sts[q]; - results.push(elt.why); - } - } - return results; - } - }, { - key: 'equals', - value: function equals(other) { - if (!other) { - return false; - } - return this.hashString() === other.hashString(); - } - /* - For thisClass or any subclass, anything which has it is its type - or is the object of something which has the type as its range, or subject - of something which has the type as its domain - We don't bother doing subproperty (yet?)as it doesn't seeem to be used much. - Get all the Classes of which we can RDFS-infer the subject is a member - @returns a hash of URIs - */ - - /** - * For thisClass or any subclass, anything which has it is its type - * or is the object of something which has the type as its range, or subject - * of something which has the type as its domain - * We don't bother doing subproperty (yet?)as it doesn't seeem to be used - * much. - * Get all the Classes of which we can RDFS-infer the subject is a member - * @return a hash of URIs - */ - - }, { - key: 'findMembersNT', - value: function findMembersNT(thisClass) { - var i; - var l; - var len; - var len1; - var len2; - var len3; - var len4; - var m; - var members; - var pred; - var q; - var ref; - var ref1; - var ref2; - var ref3; - var ref4; - var ref5; - var seeds; - var st; - var t; - var u; - seeds = {}; - seeds[thisClass.toNT()] = true; - members = {}; - ref = this.transitiveClosure(seeds, this.sym('http://www.w3.org/2000/01/rdf-schema#subClassOf'), true); - for (t in ref) { - if (!ref.hasOwnProperty(t)) continue; - ref1 = this.statementsMatching(void 0, this.sym('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), this.fromNT(t)); - for (i = 0, len = ref1.length; i < len; i++) { - st = ref1[i]; - members[st.subject.toNT()] = st; - } - ref2 = this.each(void 0, this.sym('http://www.w3.org/2000/01/rdf-schema#domain'), this.fromNT(t)); - for (l = 0, len1 = ref2.length; l < len1; l++) { - pred = ref2[l]; - ref3 = this.statementsMatching(void 0, pred); - for (m = 0, len2 = ref3.length; m < len2; m++) { - st = ref3[m]; - members[st.subject.toNT()] = st; - } - } - ref4 = this.each(void 0, this.sym('http://www.w3.org/2000/01/rdf-schema#range'), this.fromNT(t)); - for (q = 0, len3 = ref4.length; q < len3; q++) { - pred = ref4[q]; - ref5 = this.statementsMatching(void 0, pred); - for (u = 0, len4 = ref5.length; u < len4; u++) { - st = ref5[u]; - members[st.object.toNT()] = st; - } - } - } - return members; - } - }, { - key: 'findMemberURIs', - value: function findMemberURIs(subject) { - return this.NTtoURI(this.findMembersNT(subject)); - } - /** - * Get all the Classes of which we can RDFS-infer the subject is a superclass - * Returns a hash table where key is NT of type and value is statement why we - * think so. - * Does NOT return terms, returns URI strings. - * We use NT representations in this version because they handle blank nodes. - */ - - }, { - key: 'findSubClassesNT', - value: function findSubClassesNT(subject) { - var types = {}; - types[subject.toNT()] = true; - return this.transitiveClosure(types, this.sym('http://www.w3.org/2000/01/rdf-schema#subClassOf'), true); - } - /** - * Get all the Classes of which we can RDFS-infer the subject is a subclass - * Returns a hash table where key is NT of type and value is statement why we - * think so. - * Does NOT return terms, returns URI strings. - * We use NT representations in this version because they handle blank nodes. - */ - - }, { - key: 'findSuperClassesNT', - value: function findSuperClassesNT(subject) { - var types = {}; - types[subject.toNT()] = true; - return this.transitiveClosure(types, this.sym('http://www.w3.org/2000/01/rdf-schema#subClassOf'), false); - } - /** - * Get all the Classes of which we can RDFS-infer the subject is a member - * todo: This will loop is there is a class subclass loop (Sublass loops are - * not illegal) - * Returns a hash table where key is NT of type and value is statement why we - * think so. - * Does NOT return terms, returns URI strings. - * We use NT representations in this version because they handle blank nodes. - */ - - }, { - key: 'findTypesNT', - value: function findTypesNT(subject) { - var domain; - var i; - var l; - var len; - var len1; - var len2; - var len3; - var m; - var q; - var range; - var rdftype; - var ref; - var ref1; - var ref2; - var ref3; - var st; - var types; - rdftype = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'; - types = []; - ref = this.statementsMatching(subject, void 0, void 0); - for (i = 0, len = ref.length; i < len; i++) { - st = ref[i]; - if (st.predicate.uri === rdftype) { - types[st.object.toNT()] = st; - } else { - ref1 = this.each(st.predicate, this.sym('http://www.w3.org/2000/01/rdf-schema#domain')); - for (l = 0, len1 = ref1.length; l < len1; l++) { - range = ref1[l]; - types[range.toNT()] = st; - } - } - } - ref2 = this.statementsMatching(void 0, void 0, subject); - for (m = 0, len2 = ref2.length; m < len2; m++) { - st = ref2[m]; - ref3 = this.each(st.predicate, this.sym('http://www.w3.org/2000/01/rdf-schema#range')); - for (q = 0, len3 = ref3.length; q < len3; q++) { - domain = ref3[q]; - types[domain.toNT()] = st; - } - } - return this.transitiveClosure(types, this.sym('http://www.w3.org/2000/01/rdf-schema#subClassOf'), false); - } - }, { - key: 'findTypeURIs', - value: function findTypeURIs(subject) { - return this.NTtoURI(this.findTypesNT(subject)); - } - // Trace the statements which connect directly, or through bnodes - // Returns an array of statements - // doc param may be null to search all documents in store - - }, { - key: 'connectedStatements', - value: function connectedStatements(subject, doc, excludePredicateURIs) { - excludePredicateURIs = excludePredicateURIs || []; - var todo = [subject]; - var done = []; - var doneArcs = []; - var result = []; - var self = this; - var follow = function follow(x) { - var queue = function queue(x) { - if (x.termType === 'BlankNode' && !done[x.value]) { - done[x.value] = true; - todo.push(x); - } - }; - var sts = self.statementsMatching(null, null, x, doc).concat(self.statementsMatching(x, null, null, doc)); - sts = sts.filter(function (st) { - if (excludePredicateURIs[st.predicate.uri]) return false; - var hash = st.toNT(); - if (doneArcs[hash]) return false; - doneArcs[hash] = true; - return true; - }); - sts.forEach(function (st, i) { - queue(st.subject); - queue(st.object); - }); - result = result.concat(sts); - }; - while (todo.length) { - follow(todo.shift()); - } - // console.log('' + result.length + ' statements about ' + subject) - return result; - } - }, { - key: 'formula', - value: function formula() { - return new Formula(); - } - /** - * Transforms an NTriples string format into a Node. - * The bnode bit should not be used on program-external values; designed - * for internal work such as storing a bnode id in an HTML attribute. - * This will only parse the strings generated by the vaious toNT() methods. - */ - - }, { - key: 'fromNT', - value: function fromNT(str) { - var dt, k, lang, x; - switch (str[0]) { - case '<': - return this.sym(str.slice(1, -1)); - case '"': - lang = void 0; - dt = void 0; - k = str.lastIndexOf('"'); - if (k < str.length - 1) { - if (str[k + 1] === '@') { - lang = str.slice(k + 2); - } else if (str.slice(k + 1, k + 3) === '^^') { - dt = this.fromNT(str.slice(k + 3)); - } else { - throw new Error("Can't convert string from NT: " + str); - } - } - str = str.slice(1, k); - str = str.replace(/\\"/g, '"'); - str = str.replace(/\\n/g, '\n'); - str = str.replace(/\\\\/g, '\\'); - return this.literal(str, lang, dt); - case '_': - x = new BlankNode(); - x.id = parseInt(str.slice(3), 10); - BlankNode.nextId--; - return x; - case '?': - return new Variable(str.slice(1)); - } - throw new Error("Can't convert from NT: " + str); - } - }, { - key: 'holds', - value: function holds(s, p, o, g) { - var i; - if (arguments.length === 1) { - if (!s) { - return true; - } - if (s instanceof Array) { - for (i = 0; i < s.length; i++) { - if (!this.holds(s[i])) { - return false; - } - } - return true; - } else if (s instanceof Statement) { - return this.holds(s.subject, s.predicate, s.object, s.why); - } else if (s.statements) { - return this.holds(s.statements); - } - } - - var st = this.anyStatementMatching(s, p, o, g); - return st != null; - } - }, { - key: 'holdsStatement', - value: function holdsStatement(st) { - return this.holds(st.subject, st.predicate, st.object, st.why); - } - }, { - key: 'list', - value: function list(values) { - var collection = new Collection(); - values.forEach(function (val) { - collection.append(val); - }); - return collection; - } - }, { - key: 'literal', - value: function literal(val, lang, dt) { - return new Literal('' + val, lang, dt); - } - /** - * transform a collection of NTriple URIs into their URI strings - * @param t some iterable colletion of NTriple URI strings - * @return a collection of the URIs as strings - * todo: explain why it is important to go through NT - */ - - }, { - key: 'NTtoURI', - value: function NTtoURI(t) { - var k, v; - var uris = {}; - for (k in t) { - if (!t.hasOwnProperty(k)) continue; - v = t[k]; - if (k[0] === '<') { - uris[k.slice(1, -1)] = v; - } - } - return uris; - } - }, { - key: 'serialize', - value: function serialize(base, contentType, provenance) { - var documentString; - var sts; - var sz; - sz = Serializer(this); - sz.suggestNamespaces(this.namespaces); - sz.setBase(base); - if (provenance) { - sts = this.statementsMatching(void 0, void 0, void 0, provenance); - } else { - sts = this.statements; - } - switch (contentType != null ? contentType : 'text/n3') { - case 'application/rdf+xml': - documentString = sz.statementsToXML(sts); - break; - case 'text/n3': - case 'text/turtle': - documentString = sz.statementsToN3(sts); - break; - default: - throw new Error('serialize: Content-type ' + contentType + ' not supported.'); - } - return documentString; - } - }, { - key: 'substitute', - value: function substitute(bindings) { - var statementsCopy = this.statements.map(function (ea) { - return ea.substitute(bindings); - }); - console.log('Formula subs statmnts:' + statementsCopy); - var y = new Formula(); - y.add(statementsCopy); - console.log('indexed-form subs formula:' + y); - return y; - } - }, { - key: 'sym', - value: function sym(uri, name) { - if (name) { - throw new Error('This feature (kb.sym with 2 args) is removed. Do not assume prefix mappings.'); - } - return new NamedNode(uri); - } - }, { - key: 'the', - value: function the(s, p, o, g) { - var x = this.any(s, p, o, g); - if (x == null) { - log.error('No value found for the() {' + s + ' ' + p + ' ' + o + '}.'); - } - return x; - } - /** - * RDFS Inference - * These are hand-written implementations of a backward-chaining reasoner - * over the RDFS axioms. - * @param seeds {Object} a hash of NTs of classes to start with - * @param predicate The property to trace though - * @param inverse trace inverse direction - */ - - }, { - key: 'transitiveClosure', - value: function transitiveClosure(seeds, predicate, inverse) { - var elt, i, len, s, sups, t; - var agenda = {}; - Object.assign(agenda, seeds); // make a copy - var done = {}; // classes we have looked up - while (true) { - t = function () { - for (var p in agenda) { - if (!agenda.hasOwnProperty(p)) continue; - return p; - } - }(); - if (t == null) { - return done; - } - sups = inverse ? this.each(void 0, predicate, this.fromNT(t)) : this.each(this.fromNT(t), predicate); - for (i = 0, len = sups.length; i < len; i++) { - elt = sups[i]; - s = elt.toNT(); - if (s in done) { - continue; - } - if (s in agenda) { - continue; - } - agenda[s] = agenda[t]; - } - done[t] = agenda[t]; - delete agenda[t]; - } - } - /** - * Finds the types in the list which have no *stored* supertypes - * We exclude the universal class, owl:Things and rdf:Resource, as it is - * information-free. - */ - - }, { - key: 'topTypeURIs', - value: function topTypeURIs(types) { - var i; - var j; - var k; - var len; - var n; - var ref; - var tops; - var v; - tops = []; - for (k in types) { - if (!types.hasOwnProperty(k)) continue; - v = types[k]; - n = 0; - ref = this.each(this.sym(k), this.sym('http://www.w3.org/2000/01/rdf-schema#subClassOf')); - for (i = 0, len = ref.length; i < len; i++) { - j = ref[i]; - if (j.uri !== 'http://www.w3.org/2000/01/rdf-schema#Resource') { - n++; - break; - } - } - if (!n) { - tops[k] = v; - } - } - if (tops['http://www.w3.org/2000/01/rdf-schema#Resource']) { - delete tops['http://www.w3.org/2000/01/rdf-schema#Resource']; - } - if (tops['http://www.w3.org/2002/07/owl#Thing']) { - delete tops['http://www.w3.org/2002/07/owl#Thing']; - } - return tops; - } - }, { - key: 'toString', - value: function toString() { - return '{' + this.statements.join('\n') + '}'; - } - }, { - key: 'whether', - value: function whether(s, p, o, g) { - return this.statementsMatching(s, p, o, g, false).length; - } - }]); - - return Formula; -}(Node); - -Formula.termType = 'Graph'; - -Formula.prototype.classOrder = ClassOrder['Graph']; -Formula.prototype.isVar = 0; - -Formula.prototype.ns = _dereq_('./namespace'); -Formula.prototype.variable = function (name) { - return new Variable(name); -}; - -module.exports = Formula; -},{"./blank-node":51,"./class-order":52,"./collection":53,"./literal":63,"./log":64,"./named-node":66,"./namespace":67,"./node":68,"./serialize":75,"./statement":78,"./variable":83}],60:[function(_dereq_,module,exports){ -'use strict'; - -var _indexedFormula = _dereq_('./indexed-formula'); - -var _indexedFormula2 = _interopRequireDefault(_indexedFormula); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -var $rdf = { - BlankNode: _dereq_('./blank-node'), - Collection: _dereq_('./collection'), - convert: _dereq_('./convert'), - DataFactory: _dereq_('./data-factory'), - Empty: _dereq_('./empty'), - Fetcher: _dereq_('./fetcher'), - Formula: _dereq_('./formula'), - IndexedFormula: _indexedFormula2.default, - jsonParser: _dereq_('./jsonparser'), - Literal: _dereq_('./literal'), - log: _dereq_('./log'), - N3Parser: _dereq_('./n3parser'), - NamedNode: _dereq_('./named-node'), - Namespace: _dereq_('./namespace'), - Node: _dereq_('./node'), - parse: _dereq_('./parse'), - Query: _dereq_('./query').Query, - queryToSPARQL: _dereq_('./query-to-sparql'), - RDFaProcessor: _dereq_('./rdfaparser'), - RDFParser: _dereq_('./rdfxmlparser'), - serialize: _dereq_('./serialize'), - Serializer: _dereq_('./serializer'), - SPARQLToQuery: _dereq_('./sparql-to-query'), - sparqlUpdateParser: _dereq_('./patch-parser'), - Statement: _dereq_('./statement'), - term: _dereq_('./node').fromValue, - UpdateManager: _dereq_('./update-manager'), - UpdatesSocket: _dereq_('./updates-via').UpdatesSocket, - UpdatesVia: _dereq_('./updates-via').UpdatesVia, - uri: _dereq_('./uri'), - Util: _dereq_('./util'), - Variable: _dereq_('./variable') -}; - -$rdf.NextId = $rdf.BlankNode.nextId; - -$rdf.fromNT = $rdf.Formula.prototype.fromNT; -$rdf.fetcher = $rdf.DataFactory.fetcher; -$rdf.graph = $rdf.DataFactory.graph; -$rdf.lit = $rdf.DataFactory.lit; -$rdf.st = $rdf.DataFactory.st; -$rdf.sym = $rdf.DataFactory.namedNode; -$rdf.variable = $rdf.DataFactory.variable; - -// RDFJS DataFactory interface -$rdf.blankNode = $rdf.DataFactory.blankNode; -$rdf.defaultGraph = $rdf.DataFactory.defaultGraph; -$rdf.literal = $rdf.DataFactory.literal; -$rdf.namedNode = $rdf.DataFactory.namedNode; -$rdf.quad = $rdf.DataFactory.quad; -$rdf.triple = $rdf.DataFactory.triple; - -module.exports = $rdf; -},{"./blank-node":51,"./collection":53,"./convert":54,"./data-factory":55,"./empty":57,"./fetcher":58,"./formula":59,"./indexed-formula":61,"./jsonparser":62,"./literal":63,"./log":64,"./n3parser":65,"./named-node":66,"./namespace":67,"./node":68,"./parse":69,"./patch-parser":70,"./query":72,"./query-to-sparql":71,"./rdfaparser":73,"./rdfxmlparser":74,"./serialize":75,"./serializer":76,"./sparql-to-query":77,"./statement":78,"./update-manager":79,"./updates-via":80,"./uri":81,"./util":82,"./variable":83}],61:[function(_dereq_,module,exports){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -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; } - -// Identity management and indexing for RDF -// -// This file provides IndexedFormula a formula (set of triples) which -// indexed by predicate, subject and object. -// -// It "smushes" (merges into a single node) things which are identical -// according to owl:sameAs or an owl:InverseFunctionalProperty -// or an owl:FunctionalProperty -// -// -// 2005-10 Written Tim Berners-Lee -// 2007 Changed so as not to munge statements from documents when smushing -// -// -/* jsl:option explicit */ -var ArrayIndexOf = _dereq_('./util').ArrayIndexOf; -var Formula = _dereq_('./formula'); -// const log = require('./log') -var RDFArrayRemove = _dereq_('./util').RDFArrayRemove; -var Statement = _dereq_('./statement'); -var Node = _dereq_('./node'); -var Variable = _dereq_('./variable'); - -var owl_ns = 'http://www.w3.org/2002/07/owl#'; -// var link_ns = 'http://www.w3.org/2007/ont/link#' - -// Handle Functional Property -function handle_FP(formula, subj, pred, obj) { - var o1 = formula.any(subj, pred, undefined); - if (!o1) { - return false; // First time with this value - } - // log.warn("Equating "+o1.uri+" and "+obj.uri + " because FP "+pred.uri); //@@ - formula.equate(o1, obj); - return true; -} // handle_FP - -// Handle Inverse Functional Property -function handle_IFP(formula, subj, pred, obj) { - var s1 = formula.any(undefined, pred, obj); - if (!s1) { - return false; // First time with this value - } - // log.warn("Equating "+s1.uri+" and "+subj.uri + " because IFP "+pred.uri); //@@ - formula.equate(s1, subj); - return true; -} // handle_IFP - -function handleRDFType(formula, subj, pred, obj, why) { - if (formula.typeCallback) { - formula.typeCallback(formula, obj, why); - } - - var x = formula.classActions[obj.hashString()]; - var done = false; - if (x) { - for (var i = 0; i < x.length; i++) { - done = done || x[i](formula, subj, pred, obj, why); - } - } - return done; // statement given is not needed if true -} - -var IndexedFormula = function (_Formula) { - _inherits(IndexedFormula, _Formula); - - // IN future - allow pass array of statements to constructor - function IndexedFormula(features) { - _classCallCheck(this, IndexedFormula); - - // this.statements = [] // As in Formula NO don't overwrite inherited - // this.optional = [] - - var _this = _possibleConstructorReturn(this, (IndexedFormula.__proto__ || Object.getPrototypeOf(IndexedFormula)).call(this)); - - _this.propertyActions = []; // Array of functions to call when getting statement with {s X o} - // maps to [f(F,s,p,o),...] - _this.classActions = []; // Array of functions to call when adding { s type X } - _this.redirections = []; // redirect to lexically smaller equivalent symbol - _this.aliases = []; // reverse mapping to redirection: aliases for this - _this.HTTPRedirects = []; // redirections we got from HTTP - _this.subjectIndex = []; // Array of statements with this X as subject - _this.predicateIndex = []; // Array of statements with this X as subject - _this.objectIndex = []; // Array of statements with this X as object - _this.whyIndex = []; // Array of statements with X as provenance - _this.index = [_this.subjectIndex, _this.predicateIndex, _this.objectIndex, _this.whyIndex]; - _this.namespaces = {}; // Dictionary of namespace prefixes - _this.features = features || ['sameAs', 'InverseFunctionalProperty', 'FunctionalProperty']; - _this.initPropertyActions(_this.features); - return _this; - } - - _createClass(IndexedFormula, [{ - key: 'substitute', - value: function substitute(bindings) { - var statementsCopy = this.statements.map(function (ea) { - return ea.substitute(bindings); - }); - // console.log('IndexedFormula subs statemnts:' + statementsCopy) - var y = new IndexedFormula(); - y.add(statementsCopy); - // console.log('indexed-form subs formula:' + y) - return y; - } - }, { - key: 'applyPatch', - value: function applyPatch(patch, target, patchCallback) { - // patchCallback(err) - var Query = _dereq_('./query').Query; - var targetKB = this; - var ds; - var binding = null; - - // /////////// Debug strings - /* - var bindingDebug = function (b) { - var str = '' - var v - for (v in b) { - if (b.hasOwnProperty(v)) { - str += ' ' + v + ' -> ' + b[v] - } - } - return str - } - */ - var doPatch = function doPatch(onDonePatch) { - if (patch['delete']) { - ds = patch['delete']; - // console.log(bindingDebug(binding)) - // console.log('ds before substitute: ' + ds) - if (binding) ds = ds.substitute(binding); - // console.log('applyPatch: delete: ' + ds) - ds = ds.statements; - var bad = []; - var ds2 = ds.map(function (st) { - // Find the actual statemnts in the store - var sts = targetKB.statementsMatching(st.subject, st.predicate, st.object, target); - if (sts.length === 0) { - // log.info("NOT FOUND deletable " + st) - bad.push(st); - return null; - } else { - // log.info("Found deletable " + st) - return sts[0]; - } - }); - if (bad.length) { - // console.log('Could not find to delete ' + bad.length + 'statements') - // console.log('despite ' + targetKB.statementsMatching(bad[0].subject, bad[0].predicate)[0]) - return patchCallback('Could not find to delete: ' + bad.join('\n or ')); - } - ds2.map(function (st) { - targetKB.remove(st); - }); - } - if (patch['insert']) { - // log.info("doPatch insert "+patch['insert']) - ds = patch['insert']; - if (binding) ds = ds.substitute(binding); - ds = ds.statements; - ds.map(function (st) { - st.why = target; - targetKB.add(st.subject, st.predicate, st.object, st.why); - }); - } - onDonePatch(); - }; - if (patch.where) { - // log.info("Processing WHERE: " + patch.where + '\n') - var query = new Query('patch'); - query.pat = patch.where; - query.pat.statements.map(function (st) { - st.why = target; - }); - - var bindingsFound = []; - - targetKB.query(query, function onBinding(binding) { - bindingsFound.push(binding); - // console.log(' got a binding: ' + bindingDebug(binding)) - }, targetKB.fetcher, function onDone() { - if (bindingsFound.length === 0) { - return patchCallback('No match found to be patched:' + patch.where); - } - if (bindingsFound.length > 1) { - return patchCallback('Patch ambiguous. No patch done.'); - } - binding = bindingsFound[0]; - doPatch(patchCallback); - }); - } else { - doPatch(patchCallback); - } - } - }, { - key: 'declareExistential', - value: function declareExistential(x) { - if (!this._existentialVariables) this._existentialVariables = []; - this._existentialVariables.push(x); - return x; - } - }, { - key: 'initPropertyActions', - value: function initPropertyActions(features) { - // If the predicate is #type, use handleRDFType to create a typeCallback on the object - this.propertyActions[''] = [handleRDFType]; - - // Assumption: these terms are not redirected @@fixme - if (ArrayIndexOf(features, 'sameAs') >= 0) { - this.propertyActions[''] = [function (formula, subj, pred, obj, why) { - // log.warn("Equating "+subj.uri+" sameAs "+obj.uri); //@@ - formula.equate(subj, obj); - return true; // true if statement given is NOT needed in the store - }]; // sameAs -> equate & don't add to index - } - if (ArrayIndexOf(features, 'InverseFunctionalProperty') >= 0) { - this.classActions['<' + owl_ns + 'InverseFunctionalProperty>'] = [function (formula, subj, pred, obj, addFn) { - // yes subj not pred! - return formula.newPropertyAction(subj, handle_IFP); - }]; // IFP -> handle_IFP, do add to index - } - if (ArrayIndexOf(features, 'FunctionalProperty') >= 0) { - this.classActions['<' + owl_ns + 'FunctionalProperty>'] = [function (formula, subj, proj, obj, addFn) { - return formula.newPropertyAction(subj, handle_FP); - }]; // FP => handleFP, do add to index - } - } - - /** - * Adds a triple to the store. - * Returns the statement added - * (would it be better to return the original formula for chaining?) - */ - - }, { - key: 'add', - value: function add(subj, pred, obj, why) { - var i; - if (arguments.length === 1) { - if (subj instanceof Array) { - for (i = 0; i < subj.length; i++) { - this.add(subj[i]); - } - } else if (subj instanceof Statement) { - this.add(subj.subject, subj.predicate, subj.object, subj.why); - } else if (subj instanceof IndexedFormula) { - this.add(subj.statements); - } - return this; - } - var actions; - var st; - if (!why) { - // system generated - why = this.fetcher ? this.fetcher.appNode : this.sym('chrome:theSession'); - } - subj = Node.fromValue(subj); - pred = Node.fromValue(pred); - obj = Node.fromValue(obj); - why = Node.fromValue(why); - if (this.predicateCallback) { - this.predicateCallback(this, pred, why); - } - // Action return true if the statement does not need to be added - var predHash = this.canon(pred).hashString(); - actions = this.propertyActions[predHash]; // Predicate hash - var done = false; - if (actions) { - // alert('type: '+typeof actions +' @@ actions='+actions) - for (i = 0; i < actions.length; i++) { - done = done || actions[i](this, subj, pred, obj, why); - } - } - if (this.holds(subj, pred, obj, why)) { - // Takes time but saves duplicates - // console.log('rdflib: Ignoring dup! {' + subj + ' ' + pred + ' ' + obj + ' ' + why + '}') - return null; // @@better to return self in all cases? - } - // If we are tracking provenance, every thing should be loaded into the store - // if (done) return new Statement(subj, pred, obj, why) - // Don't put it in the store - // still return this statement for owl:sameAs input - var hash = [this.canon(subj).hashString(), predHash, this.canon(obj).hashString(), this.canon(why).hashString()]; - st = new Statement(subj, pred, obj, why); - for (i = 0; i < 4; i++) { - var ix = this.index[i]; - var h = hash[i]; - if (!ix[h]) { - ix[h] = []; - } - ix[h].push(st); // Set of things with this as subject, etc - } - - // log.debug("ADDING {"+subj+" "+pred+" "+obj+"} "+why) - this.statements.push(st); - return st; - } - }, { - key: 'addAll', - value: function addAll(statements) { - var _this2 = this; - - statements.forEach(function (quad) { - _this2.add(quad.subject, quad.predicate, quad.object, quad.graph); - }); - } - }, { - key: 'any', - value: function any(s, p, o, g) { - var st = this.anyStatementMatching(s, p, o, g); - if (st == null) { - return void 0; - } else if (s == null) { - return st.subject; - } else if (p == null) { - return st.predicate; - } else if (o == null) { - return st.object; - } - return void 0; - } - }, { - key: 'anyValue', - value: function anyValue(s, p, o, g) { - var y = this.any(s, p, o, g); - return y ? y.value : void 0; - } - }, { - key: 'anyStatementMatching', - value: function anyStatementMatching(subj, pred, obj, why) { - var x = this.statementsMatching(subj, pred, obj, why, true); - if (!x || x.length === 0) { - return undefined; - } - return x[0]; - } - - /** - * Returns the symbol with canonical URI as smushed - */ - - }, { - key: 'canon', - value: function canon(term) { - if (!term) { - return term; - } - var y = this.redirections[term.hashString()]; - if (!y) { - return term; - } - return y; - } - }, { - key: 'check', - value: function check() { - this.checkStatementList(this.statements); - for (var p = 0; p < 4; p++) { - var ix = this.index[p]; - for (var key in ix) { - if (ix.hasOwnProperty(key)) { - this.checkStatementList(ix[key], p); - } - } - } - } - - /** - * Self-consistency checking for diagnostis only - * Is each statement properly indexed? - */ - - }, { - key: 'checkStatementList', - value: function checkStatementList(sts, from) { - var names = ['subject', 'predicate', 'object', 'why']; - var origin = ' found in ' + names[from] + ' index.'; - var st; - for (var j = 0; j < sts.length; j++) { - st = sts[j]; - var term = [st.subject, st.predicate, st.object, st.why]; - var arrayContains = function arrayContains(a, x) { - for (var i = 0; i < a.length; i++) { - if (a[i].subject.sameTerm(x.subject) && a[i].predicate.sameTerm(x.predicate) && a[i].object.sameTerm(x.object) && a[i].why.sameTerm(x.why)) { - return true; - } - } - }; - for (var p = 0; p < 4; p++) { - var c = this.canon(term[p]); - var h = c.hashString(); - if (!this.index[p][h]) { - // throw new Error('No ' + name[p] + ' index for statement ' + st + '@' + st.why + origin) - } else { - if (!arrayContains(this.index[p][h], st)) { - // throw new Error('Index for ' + name[p] + ' does not have statement ' + st + '@' + st.why + origin) - } - } - } - if (!arrayContains(this.statements, st)) { - throw new Error('Statement list does not statement ' + st + '@' + st.why + origin); - } - } - } - }, { - key: 'close', - value: function close() { - return this; - } - - /** - * replaces @template with @target and add appropriate triples (no triple - * removed) - * one-direction replication - * @method copyTo - */ - - }, { - key: 'copyTo', - value: function copyTo(template, target, flags) { - if (!flags) flags = []; - var statList = this.statementsMatching(template); - if (ArrayIndexOf(flags, 'two-direction') !== -1) { - statList.concat(this.statementsMatching(undefined, undefined, template)); - } - for (var i = 0; i < statList.length; i++) { - var st = statList[i]; - switch (st.object.termType) { - case 'NamedNode': - this.add(target, st.predicate, st.object); - break; - case 'Literal': - case 'BlankNode': - case 'Collection': - this.add(target, st.predicate, st.object.copy(this)); - } - if (ArrayIndexOf(flags, 'delete') !== -1) { - this.remove(st); - } - } - } - - /** - * simplify graph in store when we realize two identifiers are equivalent - * We replace the bigger with the smaller. - */ - - }, { - key: 'equate', - value: function equate(u1, u2) { - // log.warn("Equating "+u1+" and "+u2); // @@ - // @@JAMBO Must canonicalize the uris to prevent errors from a=b=c - // 03-21-2010 - u1 = this.canon(u1); - u2 = this.canon(u2); - var d = u1.compareTerm(u2); - if (!d) { - return true; // No information in {a = a} - } - // var big - // var small - if (d < 0) { - // u1 less than u2 - return this.replaceWith(u2, u1); - } else { - return this.replaceWith(u1, u2); - } - } - }, { - key: 'formula', - value: function formula(features) { - return new IndexedFormula(features); - } - - /** - * Returns the number of statements contained in this IndexedFormula. - * (Getter proxy to this.statements). - * Usage: - * ``` - * var kb = rdf.graph() - * kb.length // -> 0 - * ``` - * @return {Number} - */ - - }, { - key: 'match', - - - /** - * Returns any quads matching the given arguments. - * Standard RDFJS Taskforce method for Source objects, implemented as an - * alias to `statementsMatching()` - * @method match - * @param subject {Node|String|Object} - * @param predicate {Node|String|Object} - * @param object {Node|String|Object} - * @param graph {NamedNode|String} - */ - value: function match(subject, predicate, object, graph) { - return this.statementsMatching(Node.fromValue(subject), Node.fromValue(predicate), Node.fromValue(object), Node.fromValue(graph)); - } - - /** - * Find out whether a given URI is used as symbol in the formula - */ - - }, { - key: 'mentionsURI', - value: function mentionsURI(uri) { - var hash = '<' + uri + '>'; - return !!this.subjectIndex[hash] || !!this.objectIndex[hash] || !!this.predicateIndex[hash]; - } - - // Existentials are BNodes - something exists without naming - - }, { - key: 'newExistential', - value: function newExistential(uri) { - if (!uri) return this.bnode(); - var x = this.sym(uri); - return this.declareExistential(x); - } - }, { - key: 'newPropertyAction', - value: function newPropertyAction(pred, action) { - // log.debug("newPropertyAction: "+pred) - var hash = pred.hashString(); - if (!this.propertyActions[hash]) { - this.propertyActions[hash] = []; - } - this.propertyActions[hash].push(action); - // Now apply the function to to statements already in the store - var toBeFixed = this.statementsMatching(undefined, pred, undefined); - var done = false; - for (var i = 0; i < toBeFixed.length; i++) { - // NOT optimized - sort toBeFixed etc - done = done || action(this, toBeFixed[i].subject, pred, toBeFixed[i].object); - } - return done; - } - - // Universals are Variables - - }, { - key: 'newUniversal', - value: function newUniversal(uri) { - var x = this.sym(uri); - if (!this._universalVariables) this._universalVariables = []; - this._universalVariables.push(x); - return x; - } - - // convenience function used by N3 parser - - }, { - key: 'variable', - value: function variable(name) { - return new Variable(name); - } - - /** - * Find an unused id for a file being edited: return a symbol - * (Note: Slow iff a lot of them -- could be O(log(k)) ) - */ - - }, { - key: 'nextSymbol', - value: function nextSymbol(doc) { - for (var i = 0;; i++) { - var uri = doc.uri + '#n' + i; - if (!this.mentionsURI(uri)) return this.sym(uri); - } - } - }, { - key: 'query', - value: function query(myQuery, callback, fetcher, onDone) { - var indexedFormulaQuery = _dereq_('./query').indexedFormulaQuery; - return indexedFormulaQuery.call(this, myQuery, callback, fetcher, onDone); - } - - /** - * Finds a statement object and removes it - */ - - }, { - key: 'remove', - value: function remove(st) { - if (st instanceof Array) { - for (var i = 0; i < st.length; i++) { - this.remove(st[i]); - } - return this; - } - if (st instanceof IndexedFormula) { - return this.remove(st.statements); - } - var sts = this.statementsMatching(st.subject, st.predicate, st.object, st.why); - if (!sts.length) { - throw new Error('Statement to be removed is not on store: ' + st); - } - this.removeStatement(sts[0]); - return this; - } - - /** - * Removes all statemnts in a doc - */ - - }, { - key: 'removeDocument', - value: function removeDocument(doc) { - var sts = this.statementsMatching(undefined, undefined, undefined, doc).slice(); // Take a copy as this is the actual index - for (var i = 0; i < sts.length; i++) { - this.removeStatement(sts[i]); - } - return this; - } - - /** - * remove all statements matching args (within limit) * - */ - - }, { - key: 'removeMany', - value: function removeMany(subj, pred, obj, why, limit) { - // log.debug("entering removeMany w/ subj,pred,obj,why,limit = " + subj +", "+ pred+", " + obj+", " + why+", " + limit) - var sts = this.statementsMatching(subj, pred, obj, why, false); - // This is a subtle bug that occcured in updateCenter.js too. - // The fact is, this.statementsMatching returns this.whyIndex instead of a copy of it - // but for perfromance consideration, it's better to just do that - // so make a copy here. - var statements = []; - for (var i = 0; i < sts.length; i++) { - statements.push(sts[i]); - }if (limit) statements = statements.slice(0, limit); - for (i = 0; i < statements.length; i++) { - this.remove(statements[i]); - } - } - }, { - key: 'removeMatches', - value: function removeMatches(subject, predicate, object, why) { - this.removeStatements(this.statementsMatching(subject, predicate, object, why)); - return this; - } - - /** - * Remove a particular statement object from the store - * - * st a statement which is already in the store and indexed. - * Make sure you only use this for these. - * Otherwise, you should use remove() above. - */ - - }, { - key: 'removeStatement', - value: function removeStatement(st) { - // log.debug("entering remove w/ st=" + st) - var term = [st.subject, st.predicate, st.object, st.why]; - for (var p = 0; p < 4; p++) { - var c = this.canon(term[p]); - var h = c.hashString(); - if (!this.index[p][h]) { - // log.warn ("Statement removal: no index '+p+': "+st) - } else { - RDFArrayRemove(this.index[p][h], st); - } - } - RDFArrayRemove(this.statements, st); - return this; - } - }, { - key: 'removeStatements', - value: function removeStatements(sts) { - for (var i = 0; i < sts.length; i++) { - this.remove(sts[i]); - } - return this; - } - - /** - * Replace big with small, obsoleted with obsoleting. - */ - - }, { - key: 'replaceWith', - value: function replaceWith(big, small) { - // log.debug("Replacing "+big+" with "+small) // @@ - var oldhash = big.hashString(); - var newhash = small.hashString(); - var moveIndex = function moveIndex(ix) { - var oldlist = ix[oldhash]; - if (!oldlist) { - return; // none to move - } - var newlist = ix[newhash]; - if (!newlist) { - ix[newhash] = oldlist; - } else { - ix[newhash] = oldlist.concat(newlist); - } - delete ix[oldhash]; - }; - // the canonical one carries all the indexes - for (var i = 0; i < 4; i++) { - moveIndex(this.index[i]); - } - this.redirections[oldhash] = small; - if (big.uri) { - // @@JAMBO: must update redirections,aliases from sub-items, too. - if (!this.aliases[newhash]) { - this.aliases[newhash] = []; - } - this.aliases[newhash].push(big); // Back link - if (this.aliases[oldhash]) { - for (i = 0; i < this.aliases[oldhash].length; i++) { - this.redirections[this.aliases[oldhash][i].hashString()] = small; - this.aliases[newhash].push(this.aliases[oldhash][i]); - } - } - this.add(small, this.sym('http://www.w3.org/2007/ont/link#uri'), big.uri); - // If two things are equal, and one is requested, we should request the other. - if (this.fetcher) { - this.fetcher.nowKnownAs(big, small); - } - } - moveIndex(this.classActions); - moveIndex(this.propertyActions); - // log.debug("Equate done. "+big+" to be known as "+small) - return true; // true means the statement does not need to be put in - } - - /** - * Return all equivalent URIs by which this is known - */ - - }, { - key: 'allAliases', - value: function allAliases(x) { - var a = this.aliases[this.canon(x).hashString()] || []; - a.push(this.canon(x)); - return a; - } - - /** - * Compare by canonical URI as smushed - */ - - }, { - key: 'sameThings', - value: function sameThings(x, y) { - if (x.sameTerm(y)) { - return true; - } - var x1 = this.canon(x); - // alert('x1='+x1) - if (!x1) return false; - var y1 = this.canon(y); - // alert('y1='+y1); //@@ - if (!y1) return false; - return x1.uri === y1.uri; - } - }, { - key: 'setPrefixForURI', - value: function setPrefixForURI(prefix, nsuri) { - // TODO: This is a hack for our own issues, which ought to be fixed - // post-release - // See http://dig.csail.mit.edu/cgi-bin/roundup.cgi/$rdf/issue227 - if (prefix === 'tab' && this.namespaces['tab']) { - return; - } // There are files around with long badly generated prefixes like this - if (prefix.slice(0, 2) === 'ns' || prefix.slice(0, 7) === 'default') { - return; - } - this.namespaces[prefix] = nsuri; - } - - /** - * Return statements matching a pattern - * ALL CONVENIENCE LOOKUP FUNCTIONS RELY ON THIS! - */ - - }, { - key: 'statementsMatching', - value: function statementsMatching(subj, pred, obj, why, justOne) { - // log.debug("Matching {"+subj+" "+pred+" "+obj+"}") - var pat = [subj, pred, obj, why]; - var pattern = []; - var hash = []; - var wild = []; // wildcards - var given = []; // Not wild - var p; - var list; - for (p = 0; p < 4; p++) { - pattern[p] = this.canon(Node.fromValue(pat[p])); - if (!pattern[p]) { - wild.push(p); - } else { - given.push(p); - hash[p] = pattern[p].hashString(); - } - } - if (given.length === 0) { - return this.statements; - } - if (given.length === 1) { - // Easy too, we have an index for that - p = given[0]; - list = this.index[p][hash[p]]; - if (list && justOne) { - if (list.length > 1) { - list = list.slice(0, 1); - } - } - list = list || []; - return list; - } - // Now given.length is 2, 3 or 4. - // We hope that the scale-free nature of the data will mean we tend to get - // a short index in there somewhere! - var best = 1e10; // really bad - var best_i; - var i; - for (i = 0; i < given.length; i++) { - p = given[i]; // Which part we are dealing with - list = this.index[p][hash[p]]; - if (!list) { - return []; // No occurrences - } - if (list.length < best) { - best = list.length; - best_i = i; // (not p!) - } - } - // Ok, we have picked the shortest index but now we have to filter it - var best_p = given[best_i]; - var possibles = this.index[best_p][hash[best_p]]; - var check = given.slice(0, best_i).concat(given.slice(best_i + 1)); // remove best_i - var results = []; - var parts = ['subject', 'predicate', 'object', 'why']; - for (var j = 0; j < possibles.length; j++) { - var st = possibles[j]; - - for (i = 0; i < check.length; i++) { - // for each position to be checked - p = check[i]; - if (!this.canon(st[parts[p]]).sameTerm(pattern[p])) { - st = null; - break; - } - } - if (st != null) { - results.push(st); - if (justOne) break; - } - } - return results; - } - - /** - * A list of all the URIs by which this thing is known - */ - - }, { - key: 'uris', - value: function uris(term) { - var cterm = this.canon(term); - var terms = this.aliases[cterm.hashString()]; - if (!cterm.uri) return []; - var res = [cterm.uri]; - if (terms) { - for (var i = 0; i < terms.length; i++) { - res.push(terms[i].uri); - } - } - return res; - } - }, { - key: 'length', - get: function get() { - return this.statements.length; - } - }]); - - return IndexedFormula; -}(Formula); - -exports.default = IndexedFormula; - - +'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 Collection = _dereq_('./collection'); +var Literal = _dereq_('./literal'); +var log = _dereq_('./log'); +var NamedNode = _dereq_('./named-node'); +var Node = _dereq_('./node'); +var Serializer = _dereq_('./serialize'); +var Statement = _dereq_('./statement'); +var Variable = _dereq_('./variable'); + +var Formula = function (_Node) { + _inherits(Formula, _Node); + + function Formula(statements, constraints, initBindings, optional) { + _classCallCheck(this, Formula); + + var _this = _possibleConstructorReturn(this, (Formula.__proto__ || Object.getPrototypeOf(Formula)).call(this)); + + _this.termType = Formula.termType; + _this.statements = statements || []; + _this.constraints = constraints || []; + _this.initBindings = initBindings || []; + _this.optional = optional || []; + return _this; + } + + _createClass(Formula, [{ + key: 'add', + value: function add(s, p, o, g) { + return this.statements.push(new Statement(s, p, o, g)); + } + }, { + key: 'addStatement', + value: function addStatement(st) { + return this.statements.push(st); + } + }, { + key: 'bnode', + value: function bnode(id) { + return new BlankNode(id); + } + /** + * Finds the types in the list which have no *stored* subtypes + * These are a set of classes which provide by themselves complete + * information -- the other classes are redundant for those who + * know the class DAG. + */ + + }, { + key: 'bottomTypeURIs', + value: function bottomTypeURIs(types) { + var bots; + var bottom; + var elt; + var i; + var k; + var len; + var ref; + var subs; + var v; + bots = []; + for (k in types) { + if (!types.hasOwnProperty(k)) continue; + v = types[k]; + subs = this.each(void 0, this.sym('http://www.w3.org/2000/01/rdf-schema#subClassOf'), this.sym(k)); + bottom = true; + i = 0; + for (len = subs.length; i < len; i++) { + elt = subs[i]; + ref = elt.uri; + if (ref in types) { + // the subclass is one we know + bottom = false; + break; + } + } + if (bottom) { + bots[k] = v; + } + } + return bots; + } + }, { + key: 'collection', + value: function collection() { + return new Collection(); + } + }, { + key: 'each', + value: function each(s, p, o, g) { + var elt, i, l, m, q; + var len, len1, len2, len3; + var results = []; + var sts = this.statementsMatching(s, p, o, g, false); + if (s == null) { + for (i = 0, len = sts.length; i < len; i++) { + elt = sts[i]; + results.push(elt.subject); + } + } else if (p == null) { + for (l = 0, len1 = sts.length; l < len1; l++) { + elt = sts[l]; + results.push(elt.predicate); + } + } else if (o == null) { + for (m = 0, len2 = sts.length; m < len2; m++) { + elt = sts[m]; + results.push(elt.object); + } + } else if (g == null) { + for (q = 0, len3 = sts.length; q < len3; q++) { + elt = sts[q]; + results.push(elt.why); + } + } + return results; + } + }, { + key: 'equals', + value: function equals(other) { + if (!other) { + return false; + } + return this.hashString() === other.hashString(); + } + /* + For thisClass or any subclass, anything which has it is its type + or is the object of something which has the type as its range, or subject + of something which has the type as its domain + We don't bother doing subproperty (yet?)as it doesn't seeem to be used much. + Get all the Classes of which we can RDFS-infer the subject is a member + @returns a hash of URIs + */ + + /** + * For thisClass or any subclass, anything which has it is its type + * or is the object of something which has the type as its range, or subject + * of something which has the type as its domain + * We don't bother doing subproperty (yet?)as it doesn't seeem to be used + * much. + * Get all the Classes of which we can RDFS-infer the subject is a member + * @return a hash of URIs + */ + + }, { + key: 'findMembersNT', + value: function findMembersNT(thisClass) { + var i; + var l; + var len; + var len1; + var len2; + var len3; + var len4; + var m; + var members; + var pred; + var q; + var ref; + var ref1; + var ref2; + var ref3; + var ref4; + var ref5; + var seeds; + var st; + var t; + var u; + seeds = {}; + seeds[thisClass.toNT()] = true; + members = {}; + ref = this.transitiveClosure(seeds, this.sym('http://www.w3.org/2000/01/rdf-schema#subClassOf'), true); + for (t in ref) { + if (!ref.hasOwnProperty(t)) continue; + ref1 = this.statementsMatching(void 0, this.sym('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), this.fromNT(t)); + for (i = 0, len = ref1.length; i < len; i++) { + st = ref1[i]; + members[st.subject.toNT()] = st; + } + ref2 = this.each(void 0, this.sym('http://www.w3.org/2000/01/rdf-schema#domain'), this.fromNT(t)); + for (l = 0, len1 = ref2.length; l < len1; l++) { + pred = ref2[l]; + ref3 = this.statementsMatching(void 0, pred); + for (m = 0, len2 = ref3.length; m < len2; m++) { + st = ref3[m]; + members[st.subject.toNT()] = st; + } + } + ref4 = this.each(void 0, this.sym('http://www.w3.org/2000/01/rdf-schema#range'), this.fromNT(t)); + for (q = 0, len3 = ref4.length; q < len3; q++) { + pred = ref4[q]; + ref5 = this.statementsMatching(void 0, pred); + for (u = 0, len4 = ref5.length; u < len4; u++) { + st = ref5[u]; + members[st.object.toNT()] = st; + } + } + } + return members; + } + }, { + key: 'findMemberURIs', + value: function findMemberURIs(subject) { + return this.NTtoURI(this.findMembersNT(subject)); + } + /** + * Get all the Classes of which we can RDFS-infer the subject is a superclass + * Returns a hash table where key is NT of type and value is statement why we + * think so. + * Does NOT return terms, returns URI strings. + * We use NT representations in this version because they handle blank nodes. + */ + + }, { + key: 'findSubClassesNT', + value: function findSubClassesNT(subject) { + var types = {}; + types[subject.toNT()] = true; + return this.transitiveClosure(types, this.sym('http://www.w3.org/2000/01/rdf-schema#subClassOf'), true); + } + /** + * Get all the Classes of which we can RDFS-infer the subject is a subclass + * Returns a hash table where key is NT of type and value is statement why we + * think so. + * Does NOT return terms, returns URI strings. + * We use NT representations in this version because they handle blank nodes. + */ + + }, { + key: 'findSuperClassesNT', + value: function findSuperClassesNT(subject) { + var types = {}; + types[subject.toNT()] = true; + return this.transitiveClosure(types, this.sym('http://www.w3.org/2000/01/rdf-schema#subClassOf'), false); + } + /** + * Get all the Classes of which we can RDFS-infer the subject is a member + * todo: This will loop is there is a class subclass loop (Sublass loops are + * not illegal) + * Returns a hash table where key is NT of type and value is statement why we + * think so. + * Does NOT return terms, returns URI strings. + * We use NT representations in this version because they handle blank nodes. + */ + + }, { + key: 'findTypesNT', + value: function findTypesNT(subject) { + var domain; + var i; + var l; + var len; + var len1; + var len2; + var len3; + var m; + var q; + var range; + var rdftype; + var ref; + var ref1; + var ref2; + var ref3; + var st; + var types; + rdftype = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'; + types = []; + ref = this.statementsMatching(subject, void 0, void 0); + for (i = 0, len = ref.length; i < len; i++) { + st = ref[i]; + if (st.predicate.uri === rdftype) { + types[st.object.toNT()] = st; + } else { + ref1 = this.each(st.predicate, this.sym('http://www.w3.org/2000/01/rdf-schema#domain')); + for (l = 0, len1 = ref1.length; l < len1; l++) { + range = ref1[l]; + types[range.toNT()] = st; + } + } + } + ref2 = this.statementsMatching(void 0, void 0, subject); + for (m = 0, len2 = ref2.length; m < len2; m++) { + st = ref2[m]; + ref3 = this.each(st.predicate, this.sym('http://www.w3.org/2000/01/rdf-schema#range')); + for (q = 0, len3 = ref3.length; q < len3; q++) { + domain = ref3[q]; + types[domain.toNT()] = st; + } + } + return this.transitiveClosure(types, this.sym('http://www.w3.org/2000/01/rdf-schema#subClassOf'), false); + } + }, { + key: 'findTypeURIs', + value: function findTypeURIs(subject) { + return this.NTtoURI(this.findTypesNT(subject)); + } + // Trace the statements which connect directly, or through bnodes + // Returns an array of statements + // doc param may be null to search all documents in store + + }, { + key: 'connectedStatements', + value: function connectedStatements(subject, doc, excludePredicateURIs) { + excludePredicateURIs = excludePredicateURIs || []; + var todo = [subject]; + var done = []; + var doneArcs = []; + var result = []; + var self = this; + var follow = function follow(x) { + var queue = function queue(x) { + if (x.termType === 'BlankNode' && !done[x.value]) { + done[x.value] = true; + todo.push(x); + } + }; + var sts = self.statementsMatching(null, null, x, doc).concat(self.statementsMatching(x, null, null, doc)); + sts = sts.filter(function (st) { + if (excludePredicateURIs[st.predicate.uri]) return false; + var hash = st.toNT(); + if (doneArcs[hash]) return false; + doneArcs[hash] = true; + return true; + }); + sts.forEach(function (st, i) { + queue(st.subject); + queue(st.object); + }); + result = result.concat(sts); + }; + while (todo.length) { + follow(todo.shift()); + } + // console.log('' + result.length + ' statements about ' + subject) + return result; + } + }, { + key: 'formula', + value: function formula() { + return new Formula(); + } + /** + * Transforms an NTriples string format into a Node. + * The bnode bit should not be used on program-external values; designed + * for internal work such as storing a bnode id in an HTML attribute. + * This will only parse the strings generated by the vaious toNT() methods. + */ + + }, { + key: 'fromNT', + value: function fromNT(str) { + var dt, k, lang, x; + switch (str[0]) { + case '<': + return this.sym(str.slice(1, -1)); + case '"': + lang = void 0; + dt = void 0; + k = str.lastIndexOf('"'); + if (k < str.length - 1) { + if (str[k + 1] === '@') { + lang = str.slice(k + 2); + } else if (str.slice(k + 1, k + 3) === '^^') { + dt = this.fromNT(str.slice(k + 3)); + } else { + throw new Error("Can't convert string from NT: " + str); + } + } + str = str.slice(1, k); + str = str.replace(/\\"/g, '"'); + str = str.replace(/\\n/g, '\n'); + str = str.replace(/\\\\/g, '\\'); + return this.literal(str, lang, dt); + case '_': + x = new BlankNode(); + x.id = parseInt(str.slice(3), 10); + BlankNode.nextId--; + return x; + case '?': + return new Variable(str.slice(1)); + } + throw new Error("Can't convert from NT: " + str); + } + }, { + key: 'holds', + value: function holds(s, p, o, g) { + var i; + if (arguments.length === 1) { + if (!s) { + return true; + } + if (s instanceof Array) { + for (i = 0; i < s.length; i++) { + if (!this.holds(s[i])) { + return false; + } + } + return true; + } else if (s instanceof Statement) { + return this.holds(s.subject, s.predicate, s.object, s.why); + } else if (s.statements) { + return this.holds(s.statements); + } + } + + var st = this.anyStatementMatching(s, p, o, g); + return st != null; + } + }, { + key: 'holdsStatement', + value: function holdsStatement(st) { + return this.holds(st.subject, st.predicate, st.object, st.why); + } + }, { + key: 'list', + value: function list(values) { + var collection = new Collection(); + values.forEach(function (val) { + collection.append(val); + }); + return collection; + } + }, { + key: 'literal', + value: function literal(val, lang, dt) { + return new Literal('' + val, lang, dt); + } + /** + * transform a collection of NTriple URIs into their URI strings + * @param t some iterable colletion of NTriple URI strings + * @return a collection of the URIs as strings + * todo: explain why it is important to go through NT + */ + + }, { + key: 'NTtoURI', + value: function NTtoURI(t) { + var k, v; + var uris = {}; + for (k in t) { + if (!t.hasOwnProperty(k)) continue; + v = t[k]; + if (k[0] === '<') { + uris[k.slice(1, -1)] = v; + } + } + return uris; + } + }, { + key: 'serialize', + value: function serialize(base, contentType, provenance) { + var documentString; + var sts; + var sz; + sz = Serializer(this); + sz.suggestNamespaces(this.namespaces); + sz.setBase(base); + if (provenance) { + sts = this.statementsMatching(void 0, void 0, void 0, provenance); + } else { + sts = this.statements; + } + switch (contentType != null ? contentType : 'text/n3') { + case 'application/rdf+xml': + documentString = sz.statementsToXML(sts); + break; + case 'text/n3': + case 'text/turtle': + documentString = sz.statementsToN3(sts); + break; + default: + throw new Error('serialize: Content-type ' + contentType + ' not supported.'); + } + return documentString; + } + }, { + key: 'substitute', + value: function substitute(bindings) { + var statementsCopy = this.statements.map(function (ea) { + return ea.substitute(bindings); + }); + console.log('Formula subs statmnts:' + statementsCopy); + var y = new Formula(); + y.add(statementsCopy); + console.log('indexed-form subs formula:' + y); + return y; + } + }, { + key: 'sym', + value: function sym(uri, name) { + if (name) { + throw new Error('This feature (kb.sym with 2 args) is removed. Do not assume prefix mappings.'); + } + return new NamedNode(uri); + } + }, { + key: 'the', + value: function the(s, p, o, g) { + var x = this.any(s, p, o, g); + if (x == null) { + log.error('No value found for the() {' + s + ' ' + p + ' ' + o + '}.'); + } + return x; + } + /** + * RDFS Inference + * These are hand-written implementations of a backward-chaining reasoner + * over the RDFS axioms. + * @param seeds {Object} a hash of NTs of classes to start with + * @param predicate The property to trace though + * @param inverse trace inverse direction + */ + + }, { + key: 'transitiveClosure', + value: function transitiveClosure(seeds, predicate, inverse) { + var elt, i, len, s, sups, t; + var agenda = {}; + Object.assign(agenda, seeds); // make a copy + var done = {}; // classes we have looked up + while (true) { + t = function () { + for (var p in agenda) { + if (!agenda.hasOwnProperty(p)) continue; + return p; + } + }(); + if (t == null) { + return done; + } + sups = inverse ? this.each(void 0, predicate, this.fromNT(t)) : this.each(this.fromNT(t), predicate); + for (i = 0, len = sups.length; i < len; i++) { + elt = sups[i]; + s = elt.toNT(); + if (s in done) { + continue; + } + if (s in agenda) { + continue; + } + agenda[s] = agenda[t]; + } + done[t] = agenda[t]; + delete agenda[t]; + } + } + /** + * Finds the types in the list which have no *stored* supertypes + * We exclude the universal class, owl:Things and rdf:Resource, as it is + * information-free. + */ + + }, { + key: 'topTypeURIs', + value: function topTypeURIs(types) { + var i; + var j; + var k; + var len; + var n; + var ref; + var tops; + var v; + tops = []; + for (k in types) { + if (!types.hasOwnProperty(k)) continue; + v = types[k]; + n = 0; + ref = this.each(this.sym(k), this.sym('http://www.w3.org/2000/01/rdf-schema#subClassOf')); + for (i = 0, len = ref.length; i < len; i++) { + j = ref[i]; + if (j.uri !== 'http://www.w3.org/2000/01/rdf-schema#Resource') { + n++; + break; + } + } + if (!n) { + tops[k] = v; + } + } + if (tops['http://www.w3.org/2000/01/rdf-schema#Resource']) { + delete tops['http://www.w3.org/2000/01/rdf-schema#Resource']; + } + if (tops['http://www.w3.org/2002/07/owl#Thing']) { + delete tops['http://www.w3.org/2002/07/owl#Thing']; + } + return tops; + } + }, { + key: 'toString', + value: function toString() { + return '{' + this.statements.join('\n') + '}'; + } + }, { + key: 'whether', + value: function whether(s, p, o, g) { + return this.statementsMatching(s, p, o, g, false).length; + } + }]); + + return Formula; +}(Node); + +Formula.termType = 'Graph'; + +Formula.prototype.classOrder = ClassOrder['Graph']; +Formula.prototype.isVar = 0; + +Formula.prototype.ns = _dereq_('./namespace'); +Formula.prototype.variable = function (name) { + return new Variable(name); +}; + +module.exports = Formula; +},{"./blank-node":51,"./class-order":52,"./collection":53,"./literal":63,"./log":64,"./named-node":66,"./namespace":67,"./node":68,"./serialize":75,"./statement":78,"./variable":83}],60:[function(_dereq_,module,exports){ +'use strict'; + +var _indexedFormula = _dereq_('./indexed-formula'); + +var _indexedFormula2 = _interopRequireDefault(_indexedFormula); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var $rdf = { + BlankNode: _dereq_('./blank-node'), + Collection: _dereq_('./collection'), + convert: _dereq_('./convert'), + DataFactory: _dereq_('./data-factory'), + Empty: _dereq_('./empty'), + Fetcher: _dereq_('./fetcher'), + Formula: _dereq_('./formula'), + IndexedFormula: _indexedFormula2.default, + jsonParser: _dereq_('./jsonparser'), + Literal: _dereq_('./literal'), + log: _dereq_('./log'), + N3Parser: _dereq_('./n3parser'), + NamedNode: _dereq_('./named-node'), + Namespace: _dereq_('./namespace'), + Node: _dereq_('./node'), + parse: _dereq_('./parse'), + Query: _dereq_('./query').Query, + queryToSPARQL: _dereq_('./query-to-sparql'), + RDFaProcessor: _dereq_('./rdfaparser'), + RDFParser: _dereq_('./rdfxmlparser'), + serialize: _dereq_('./serialize'), + Serializer: _dereq_('./serializer'), + SPARQLToQuery: _dereq_('./sparql-to-query'), + sparqlUpdateParser: _dereq_('./patch-parser'), + Statement: _dereq_('./statement'), + term: _dereq_('./node').fromValue, + UpdateManager: _dereq_('./update-manager'), + UpdatesSocket: _dereq_('./updates-via').UpdatesSocket, + UpdatesVia: _dereq_('./updates-via').UpdatesVia, + uri: _dereq_('./uri'), + Util: _dereq_('./util'), + Variable: _dereq_('./variable') +}; + +$rdf.NextId = $rdf.BlankNode.nextId; + +$rdf.fromNT = $rdf.Formula.prototype.fromNT; +$rdf.fetcher = $rdf.DataFactory.fetcher; +$rdf.graph = $rdf.DataFactory.graph; +$rdf.lit = $rdf.DataFactory.lit; +$rdf.st = $rdf.DataFactory.st; +$rdf.sym = $rdf.DataFactory.namedNode; +$rdf.variable = $rdf.DataFactory.variable; + +// RDFJS DataFactory interface +$rdf.blankNode = $rdf.DataFactory.blankNode; +$rdf.defaultGraph = $rdf.DataFactory.defaultGraph; +$rdf.literal = $rdf.DataFactory.literal; +$rdf.namedNode = $rdf.DataFactory.namedNode; +$rdf.quad = $rdf.DataFactory.quad; +$rdf.triple = $rdf.DataFactory.triple; + +module.exports = $rdf; +},{"./blank-node":51,"./collection":53,"./convert":54,"./data-factory":55,"./empty":57,"./fetcher":58,"./formula":59,"./indexed-formula":61,"./jsonparser":62,"./literal":63,"./log":64,"./n3parser":65,"./named-node":66,"./namespace":67,"./node":68,"./parse":69,"./patch-parser":70,"./query":72,"./query-to-sparql":71,"./rdfaparser":73,"./rdfxmlparser":74,"./serialize":75,"./serializer":76,"./sparql-to-query":77,"./statement":78,"./update-manager":79,"./updates-via":80,"./uri":81,"./util":82,"./variable":83}],61:[function(_dereq_,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +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; } + +// Identity management and indexing for RDF +// +// This file provides IndexedFormula a formula (set of triples) which +// indexed by predicate, subject and object. +// +// It "smushes" (merges into a single node) things which are identical +// according to owl:sameAs or an owl:InverseFunctionalProperty +// or an owl:FunctionalProperty +// +// +// 2005-10 Written Tim Berners-Lee +// 2007 Changed so as not to munge statements from documents when smushing +// +// +/* jsl:option explicit */ +var ArrayIndexOf = _dereq_('./util').ArrayIndexOf; +var Formula = _dereq_('./formula'); +// const log = require('./log') +var RDFArrayRemove = _dereq_('./util').RDFArrayRemove; +var Statement = _dereq_('./statement'); +var Node = _dereq_('./node'); +var Variable = _dereq_('./variable'); + +var owl_ns = 'http://www.w3.org/2002/07/owl#'; +// var link_ns = 'http://www.w3.org/2007/ont/link#' + +// Handle Functional Property +function handle_FP(formula, subj, pred, obj) { + var o1 = formula.any(subj, pred, undefined); + if (!o1) { + return false; // First time with this value + } + // log.warn("Equating "+o1.uri+" and "+obj.uri + " because FP "+pred.uri); //@@ + formula.equate(o1, obj); + return true; +} // handle_FP + +// Handle Inverse Functional Property +function handle_IFP(formula, subj, pred, obj) { + var s1 = formula.any(undefined, pred, obj); + if (!s1) { + return false; // First time with this value + } + // log.warn("Equating "+s1.uri+" and "+subj.uri + " because IFP "+pred.uri); //@@ + formula.equate(s1, subj); + return true; +} // handle_IFP + +function handleRDFType(formula, subj, pred, obj, why) { + if (formula.typeCallback) { + formula.typeCallback(formula, obj, why); + } + + var x = formula.classActions[obj.hashString()]; + var done = false; + if (x) { + for (var i = 0; i < x.length; i++) { + done = done || x[i](formula, subj, pred, obj, why); + } + } + return done; // statement given is not needed if true +} + +var IndexedFormula = function (_Formula) { + _inherits(IndexedFormula, _Formula); + + // IN future - allow pass array of statements to constructor + function IndexedFormula(features) { + _classCallCheck(this, IndexedFormula); + + // this.statements = [] // As in Formula NO don't overwrite inherited + // this.optional = [] + + var _this = _possibleConstructorReturn(this, (IndexedFormula.__proto__ || Object.getPrototypeOf(IndexedFormula)).call(this)); + + _this.propertyActions = []; // Array of functions to call when getting statement with {s X o} + // maps to [f(F,s,p,o),...] + _this.classActions = []; // Array of functions to call when adding { s type X } + _this.redirections = []; // redirect to lexically smaller equivalent symbol + _this.aliases = []; // reverse mapping to redirection: aliases for this + _this.HTTPRedirects = []; // redirections we got from HTTP + _this.subjectIndex = []; // Array of statements with this X as subject + _this.predicateIndex = []; // Array of statements with this X as subject + _this.objectIndex = []; // Array of statements with this X as object + _this.whyIndex = []; // Array of statements with X as provenance + _this.index = [_this.subjectIndex, _this.predicateIndex, _this.objectIndex, _this.whyIndex]; + _this.namespaces = {}; // Dictionary of namespace prefixes + _this.features = features || ['sameAs', 'InverseFunctionalProperty', 'FunctionalProperty']; + _this.initPropertyActions(_this.features); + return _this; + } + + _createClass(IndexedFormula, [{ + key: 'substitute', + value: function substitute(bindings) { + var statementsCopy = this.statements.map(function (ea) { + return ea.substitute(bindings); + }); + // console.log('IndexedFormula subs statemnts:' + statementsCopy) + var y = new IndexedFormula(); + y.add(statementsCopy); + // console.log('indexed-form subs formula:' + y) + return y; + } + }, { + key: 'applyPatch', + value: function applyPatch(patch, target, patchCallback) { + // patchCallback(err) + var Query = _dereq_('./query').Query; + var targetKB = this; + var ds; + var binding = null; + + // /////////// Debug strings + /* + var bindingDebug = function (b) { + var str = '' + var v + for (v in b) { + if (b.hasOwnProperty(v)) { + str += ' ' + v + ' -> ' + b[v] + } + } + return str + } + */ + var doPatch = function doPatch(onDonePatch) { + if (patch['delete']) { + ds = patch['delete']; + // console.log(bindingDebug(binding)) + // console.log('ds before substitute: ' + ds) + if (binding) ds = ds.substitute(binding); + // console.log('applyPatch: delete: ' + ds) + ds = ds.statements; + var bad = []; + var ds2 = ds.map(function (st) { + // Find the actual statemnts in the store + var sts = targetKB.statementsMatching(st.subject, st.predicate, st.object, target); + if (sts.length === 0) { + // log.info("NOT FOUND deletable " + st) + bad.push(st); + return null; + } else { + // log.info("Found deletable " + st) + return sts[0]; + } + }); + if (bad.length) { + // console.log('Could not find to delete ' + bad.length + 'statements') + // console.log('despite ' + targetKB.statementsMatching(bad[0].subject, bad[0].predicate)[0]) + return patchCallback('Could not find to delete: ' + bad.join('\n or ')); + } + ds2.map(function (st) { + targetKB.remove(st); + }); + } + if (patch['insert']) { + // log.info("doPatch insert "+patch['insert']) + ds = patch['insert']; + if (binding) ds = ds.substitute(binding); + ds = ds.statements; + ds.map(function (st) { + st.why = target; + targetKB.add(st.subject, st.predicate, st.object, st.why); + }); + } + onDonePatch(); + }; + if (patch.where) { + // log.info("Processing WHERE: " + patch.where + '\n') + var query = new Query('patch'); + query.pat = patch.where; + query.pat.statements.map(function (st) { + st.why = target; + }); + + var bindingsFound = []; + + targetKB.query(query, function onBinding(binding) { + bindingsFound.push(binding); + // console.log(' got a binding: ' + bindingDebug(binding)) + }, targetKB.fetcher, function onDone() { + if (bindingsFound.length === 0) { + return patchCallback('No match found to be patched:' + patch.where); + } + if (bindingsFound.length > 1) { + return patchCallback('Patch ambiguous. No patch done.'); + } + binding = bindingsFound[0]; + doPatch(patchCallback); + }); + } else { + doPatch(patchCallback); + } + } + }, { + key: 'declareExistential', + value: function declareExistential(x) { + if (!this._existentialVariables) this._existentialVariables = []; + this._existentialVariables.push(x); + return x; + } + }, { + key: 'initPropertyActions', + value: function initPropertyActions(features) { + // If the predicate is #type, use handleRDFType to create a typeCallback on the object + this.propertyActions[''] = [handleRDFType]; + + // Assumption: these terms are not redirected @@fixme + if (ArrayIndexOf(features, 'sameAs') >= 0) { + this.propertyActions[''] = [function (formula, subj, pred, obj, why) { + // log.warn("Equating "+subj.uri+" sameAs "+obj.uri); //@@ + formula.equate(subj, obj); + return true; // true if statement given is NOT needed in the store + }]; // sameAs -> equate & don't add to index + } + if (ArrayIndexOf(features, 'InverseFunctionalProperty') >= 0) { + this.classActions['<' + owl_ns + 'InverseFunctionalProperty>'] = [function (formula, subj, pred, obj, addFn) { + // yes subj not pred! + return formula.newPropertyAction(subj, handle_IFP); + }]; // IFP -> handle_IFP, do add to index + } + if (ArrayIndexOf(features, 'FunctionalProperty') >= 0) { + this.classActions['<' + owl_ns + 'FunctionalProperty>'] = [function (formula, subj, proj, obj, addFn) { + return formula.newPropertyAction(subj, handle_FP); + }]; // FP => handleFP, do add to index + } + } + + /** + * Adds a triple to the store. + * Returns the statement added + * (would it be better to return the original formula for chaining?) + */ + + }, { + key: 'add', + value: function add(subj, pred, obj, why) { + var i; + if (arguments.length === 1) { + if (subj instanceof Array) { + for (i = 0; i < subj.length; i++) { + this.add(subj[i]); + } + } else if (subj instanceof Statement) { + this.add(subj.subject, subj.predicate, subj.object, subj.why); + } else if (subj instanceof IndexedFormula) { + this.add(subj.statements); + } + return this; + } + var actions; + var st; + if (!why) { + // system generated + why = this.fetcher ? this.fetcher.appNode : this.sym('chrome:theSession'); + } + subj = Node.fromValue(subj); + pred = Node.fromValue(pred); + obj = Node.fromValue(obj); + why = Node.fromValue(why); + if (this.predicateCallback) { + this.predicateCallback(this, pred, why); + } + // Action return true if the statement does not need to be added + var predHash = this.canon(pred).hashString(); + actions = this.propertyActions[predHash]; // Predicate hash + var done = false; + if (actions) { + // alert('type: '+typeof actions +' @@ actions='+actions) + for (i = 0; i < actions.length; i++) { + done = done || actions[i](this, subj, pred, obj, why); + } + } + if (this.holds(subj, pred, obj, why)) { + // Takes time but saves duplicates + // console.log('rdflib: Ignoring dup! {' + subj + ' ' + pred + ' ' + obj + ' ' + why + '}') + return null; // @@better to return self in all cases? + } + // If we are tracking provenance, every thing should be loaded into the store + // if (done) return new Statement(subj, pred, obj, why) + // Don't put it in the store + // still return this statement for owl:sameAs input + var hash = [this.canon(subj).hashString(), predHash, this.canon(obj).hashString(), this.canon(why).hashString()]; + st = new Statement(subj, pred, obj, why); + for (i = 0; i < 4; i++) { + var ix = this.index[i]; + var h = hash[i]; + if (!ix[h]) { + ix[h] = []; + } + ix[h].push(st); // Set of things with this as subject, etc + } + + // log.debug("ADDING {"+subj+" "+pred+" "+obj+"} "+why) + this.statements.push(st); + return st; + } + }, { + key: 'addAll', + value: function addAll(statements) { + var _this2 = this; + + statements.forEach(function (quad) { + _this2.add(quad.subject, quad.predicate, quad.object, quad.graph); + }); + } + }, { + key: 'any', + value: function any(s, p, o, g) { + var st = this.anyStatementMatching(s, p, o, g); + if (st == null) { + return void 0; + } else if (s == null) { + return st.subject; + } else if (p == null) { + return st.predicate; + } else if (o == null) { + return st.object; + } + return void 0; + } + }, { + key: 'anyValue', + value: function anyValue(s, p, o, g) { + var y = this.any(s, p, o, g); + return y ? y.value : void 0; + } + }, { + key: 'anyStatementMatching', + value: function anyStatementMatching(subj, pred, obj, why) { + var x = this.statementsMatching(subj, pred, obj, why, true); + if (!x || x.length === 0) { + return undefined; + } + return x[0]; + } + + /** + * Returns the symbol with canonical URI as smushed + */ + + }, { + key: 'canon', + value: function canon(term) { + if (!term) { + return term; + } + var y = this.redirections[term.hashString()]; + if (!y) { + return term; + } + return y; + } + }, { + key: 'check', + value: function check() { + this.checkStatementList(this.statements); + for (var p = 0; p < 4; p++) { + var ix = this.index[p]; + for (var key in ix) { + if (ix.hasOwnProperty(key)) { + this.checkStatementList(ix[key], p); + } + } + } + } + + /** + * Self-consistency checking for diagnostis only + * Is each statement properly indexed? + */ + + }, { + key: 'checkStatementList', + value: function checkStatementList(sts, from) { + var names = ['subject', 'predicate', 'object', 'why']; + var origin = ' found in ' + names[from] + ' index.'; + var st; + for (var j = 0; j < sts.length; j++) { + st = sts[j]; + var term = [st.subject, st.predicate, st.object, st.why]; + var arrayContains = function arrayContains(a, x) { + for (var i = 0; i < a.length; i++) { + if (a[i].subject.sameTerm(x.subject) && a[i].predicate.sameTerm(x.predicate) && a[i].object.sameTerm(x.object) && a[i].why.sameTerm(x.why)) { + return true; + } + } + }; + for (var p = 0; p < 4; p++) { + var c = this.canon(term[p]); + var h = c.hashString(); + if (!this.index[p][h]) { + // throw new Error('No ' + name[p] + ' index for statement ' + st + '@' + st.why + origin) + } else { + if (!arrayContains(this.index[p][h], st)) { + // throw new Error('Index for ' + name[p] + ' does not have statement ' + st + '@' + st.why + origin) + } + } + } + if (!arrayContains(this.statements, st)) { + throw new Error('Statement list does not statement ' + st + '@' + st.why + origin); + } + } + } + }, { + key: 'close', + value: function close() { + return this; + } + + /** + * replaces @template with @target and add appropriate triples (no triple + * removed) + * one-direction replication + * @method copyTo + */ + + }, { + key: 'copyTo', + value: function copyTo(template, target, flags) { + if (!flags) flags = []; + var statList = this.statementsMatching(template); + if (ArrayIndexOf(flags, 'two-direction') !== -1) { + statList.concat(this.statementsMatching(undefined, undefined, template)); + } + for (var i = 0; i < statList.length; i++) { + var st = statList[i]; + switch (st.object.termType) { + case 'NamedNode': + this.add(target, st.predicate, st.object); + break; + case 'Literal': + case 'BlankNode': + case 'Collection': + this.add(target, st.predicate, st.object.copy(this)); + } + if (ArrayIndexOf(flags, 'delete') !== -1) { + this.remove(st); + } + } + } + + /** + * simplify graph in store when we realize two identifiers are equivalent + * We replace the bigger with the smaller. + */ + + }, { + key: 'equate', + value: function equate(u1, u2) { + // log.warn("Equating "+u1+" and "+u2); // @@ + // @@JAMBO Must canonicalize the uris to prevent errors from a=b=c + // 03-21-2010 + u1 = this.canon(u1); + u2 = this.canon(u2); + var d = u1.compareTerm(u2); + if (!d) { + return true; // No information in {a = a} + } + // var big + // var small + if (d < 0) { + // u1 less than u2 + return this.replaceWith(u2, u1); + } else { + return this.replaceWith(u1, u2); + } + } + }, { + key: 'formula', + value: function formula(features) { + return new IndexedFormula(features); + } + + /** + * Returns the number of statements contained in this IndexedFormula. + * (Getter proxy to this.statements). + * Usage: + * ``` + * var kb = rdf.graph() + * kb.length // -> 0 + * ``` + * @return {Number} + */ + + }, { + key: 'match', + + + /** + * Returns any quads matching the given arguments. + * Standard RDFJS Taskforce method for Source objects, implemented as an + * alias to `statementsMatching()` + * @method match + * @param subject {Node|String|Object} + * @param predicate {Node|String|Object} + * @param object {Node|String|Object} + * @param graph {NamedNode|String} + */ + value: function match(subject, predicate, object, graph) { + return this.statementsMatching(Node.fromValue(subject), Node.fromValue(predicate), Node.fromValue(object), Node.fromValue(graph)); + } + + /** + * Find out whether a given URI is used as symbol in the formula + */ + + }, { + key: 'mentionsURI', + value: function mentionsURI(uri) { + var hash = '<' + uri + '>'; + return !!this.subjectIndex[hash] || !!this.objectIndex[hash] || !!this.predicateIndex[hash]; + } + + // Existentials are BNodes - something exists without naming + + }, { + key: 'newExistential', + value: function newExistential(uri) { + if (!uri) return this.bnode(); + var x = this.sym(uri); + return this.declareExistential(x); + } + }, { + key: 'newPropertyAction', + value: function newPropertyAction(pred, action) { + // log.debug("newPropertyAction: "+pred) + var hash = pred.hashString(); + if (!this.propertyActions[hash]) { + this.propertyActions[hash] = []; + } + this.propertyActions[hash].push(action); + // Now apply the function to to statements already in the store + var toBeFixed = this.statementsMatching(undefined, pred, undefined); + var done = false; + for (var i = 0; i < toBeFixed.length; i++) { + // NOT optimized - sort toBeFixed etc + done = done || action(this, toBeFixed[i].subject, pred, toBeFixed[i].object); + } + return done; + } + + // Universals are Variables + + }, { + key: 'newUniversal', + value: function newUniversal(uri) { + var x = this.sym(uri); + if (!this._universalVariables) this._universalVariables = []; + this._universalVariables.push(x); + return x; + } + + // convenience function used by N3 parser + + }, { + key: 'variable', + value: function variable(name) { + return new Variable(name); + } + + /** + * Find an unused id for a file being edited: return a symbol + * (Note: Slow iff a lot of them -- could be O(log(k)) ) + */ + + }, { + key: 'nextSymbol', + value: function nextSymbol(doc) { + for (var i = 0;; i++) { + var uri = doc.uri + '#n' + i; + if (!this.mentionsURI(uri)) return this.sym(uri); + } + } + }, { + key: 'query', + value: function query(myQuery, callback, fetcher, onDone) { + var indexedFormulaQuery = _dereq_('./query').indexedFormulaQuery; + return indexedFormulaQuery.call(this, myQuery, callback, fetcher, onDone); + } + + /** + * Finds a statement object and removes it + */ + + }, { + key: 'remove', + value: function remove(st) { + if (st instanceof Array) { + for (var i = 0; i < st.length; i++) { + this.remove(st[i]); + } + return this; + } + if (st instanceof IndexedFormula) { + return this.remove(st.statements); + } + var sts = this.statementsMatching(st.subject, st.predicate, st.object, st.why); + if (!sts.length) { + throw new Error('Statement to be removed is not on store: ' + st); + } + this.removeStatement(sts[0]); + return this; + } + + /** + * Removes all statemnts in a doc + */ + + }, { + key: 'removeDocument', + value: function removeDocument(doc) { + var sts = this.statementsMatching(undefined, undefined, undefined, doc).slice(); // Take a copy as this is the actual index + for (var i = 0; i < sts.length; i++) { + this.removeStatement(sts[i]); + } + return this; + } + + /** + * remove all statements matching args (within limit) * + */ + + }, { + key: 'removeMany', + value: function removeMany(subj, pred, obj, why, limit) { + // log.debug("entering removeMany w/ subj,pred,obj,why,limit = " + subj +", "+ pred+", " + obj+", " + why+", " + limit) + var sts = this.statementsMatching(subj, pred, obj, why, false); + // This is a subtle bug that occcured in updateCenter.js too. + // The fact is, this.statementsMatching returns this.whyIndex instead of a copy of it + // but for perfromance consideration, it's better to just do that + // so make a copy here. + var statements = []; + for (var i = 0; i < sts.length; i++) { + statements.push(sts[i]); + }if (limit) statements = statements.slice(0, limit); + for (i = 0; i < statements.length; i++) { + this.remove(statements[i]); + } + } + }, { + key: 'removeMatches', + value: function removeMatches(subject, predicate, object, why) { + this.removeStatements(this.statementsMatching(subject, predicate, object, why)); + return this; + } + + /** + * Remove a particular statement object from the store + * + * st a statement which is already in the store and indexed. + * Make sure you only use this for these. + * Otherwise, you should use remove() above. + */ + + }, { + key: 'removeStatement', + value: function removeStatement(st) { + // log.debug("entering remove w/ st=" + st) + var term = [st.subject, st.predicate, st.object, st.why]; + for (var p = 0; p < 4; p++) { + var c = this.canon(term[p]); + var h = c.hashString(); + if (!this.index[p][h]) { + // log.warn ("Statement removal: no index '+p+': "+st) + } else { + RDFArrayRemove(this.index[p][h], st); + } + } + RDFArrayRemove(this.statements, st); + return this; + } + }, { + key: 'removeStatements', + value: function removeStatements(sts) { + for (var i = 0; i < sts.length; i++) { + this.remove(sts[i]); + } + return this; + } + + /** + * Replace big with small, obsoleted with obsoleting. + */ + + }, { + key: 'replaceWith', + value: function replaceWith(big, small) { + // log.debug("Replacing "+big+" with "+small) // @@ + var oldhash = big.hashString(); + var newhash = small.hashString(); + var moveIndex = function moveIndex(ix) { + var oldlist = ix[oldhash]; + if (!oldlist) { + return; // none to move + } + var newlist = ix[newhash]; + if (!newlist) { + ix[newhash] = oldlist; + } else { + ix[newhash] = oldlist.concat(newlist); + } + delete ix[oldhash]; + }; + // the canonical one carries all the indexes + for (var i = 0; i < 4; i++) { + moveIndex(this.index[i]); + } + this.redirections[oldhash] = small; + if (big.uri) { + // @@JAMBO: must update redirections,aliases from sub-items, too. + if (!this.aliases[newhash]) { + this.aliases[newhash] = []; + } + this.aliases[newhash].push(big); // Back link + if (this.aliases[oldhash]) { + for (i = 0; i < this.aliases[oldhash].length; i++) { + this.redirections[this.aliases[oldhash][i].hashString()] = small; + this.aliases[newhash].push(this.aliases[oldhash][i]); + } + } + this.add(small, this.sym('http://www.w3.org/2007/ont/link#uri'), big.uri); + // If two things are equal, and one is requested, we should request the other. + if (this.fetcher) { + this.fetcher.nowKnownAs(big, small); + } + } + moveIndex(this.classActions); + moveIndex(this.propertyActions); + // log.debug("Equate done. "+big+" to be known as "+small) + return true; // true means the statement does not need to be put in + } + + /** + * Return all equivalent URIs by which this is known + */ + + }, { + key: 'allAliases', + value: function allAliases(x) { + var a = this.aliases[this.canon(x).hashString()] || []; + a.push(this.canon(x)); + return a; + } + + /** + * Compare by canonical URI as smushed + */ + + }, { + key: 'sameThings', + value: function sameThings(x, y) { + if (x.sameTerm(y)) { + return true; + } + var x1 = this.canon(x); + // alert('x1='+x1) + if (!x1) return false; + var y1 = this.canon(y); + // alert('y1='+y1); //@@ + if (!y1) return false; + return x1.uri === y1.uri; + } + }, { + key: 'setPrefixForURI', + value: function setPrefixForURI(prefix, nsuri) { + // TODO: This is a hack for our own issues, which ought to be fixed + // post-release + // See http://dig.csail.mit.edu/cgi-bin/roundup.cgi/$rdf/issue227 + if (prefix === 'tab' && this.namespaces['tab']) { + return; + } // There are files around with long badly generated prefixes like this + if (prefix.slice(0, 2) === 'ns' || prefix.slice(0, 7) === 'default') { + return; + } + this.namespaces[prefix] = nsuri; + } + + /** + * Return statements matching a pattern + * ALL CONVENIENCE LOOKUP FUNCTIONS RELY ON THIS! + */ + + }, { + key: 'statementsMatching', + value: function statementsMatching(subj, pred, obj, why, justOne) { + // log.debug("Matching {"+subj+" "+pred+" "+obj+"}") + var pat = [subj, pred, obj, why]; + var pattern = []; + var hash = []; + var wild = []; // wildcards + var given = []; // Not wild + var p; + var list; + for (p = 0; p < 4; p++) { + pattern[p] = this.canon(Node.fromValue(pat[p])); + if (!pattern[p]) { + wild.push(p); + } else { + given.push(p); + hash[p] = pattern[p].hashString(); + } + } + if (given.length === 0) { + return this.statements; + } + if (given.length === 1) { + // Easy too, we have an index for that + p = given[0]; + list = this.index[p][hash[p]]; + if (list && justOne) { + if (list.length > 1) { + list = list.slice(0, 1); + } + } + list = list || []; + return list; + } + // Now given.length is 2, 3 or 4. + // We hope that the scale-free nature of the data will mean we tend to get + // a short index in there somewhere! + var best = 1e10; // really bad + var best_i; + var i; + for (i = 0; i < given.length; i++) { + p = given[i]; // Which part we are dealing with + list = this.index[p][hash[p]]; + if (!list) { + return []; // No occurrences + } + if (list.length < best) { + best = list.length; + best_i = i; // (not p!) + } + } + // Ok, we have picked the shortest index but now we have to filter it + var best_p = given[best_i]; + var possibles = this.index[best_p][hash[best_p]]; + var check = given.slice(0, best_i).concat(given.slice(best_i + 1)); // remove best_i + var results = []; + var parts = ['subject', 'predicate', 'object', 'why']; + for (var j = 0; j < possibles.length; j++) { + var st = possibles[j]; + + for (i = 0; i < check.length; i++) { + // for each position to be checked + p = check[i]; + if (!this.canon(st[parts[p]]).sameTerm(pattern[p])) { + st = null; + break; + } + } + if (st != null) { + results.push(st); + if (justOne) break; + } + } + return results; + } + + /** + * A list of all the URIs by which this thing is known + */ + + }, { + key: 'uris', + value: function uris(term) { + var cterm = this.canon(term); + var terms = this.aliases[cterm.hashString()]; + if (!cterm.uri) return []; + var res = [cterm.uri]; + if (terms) { + for (var i = 0; i < terms.length; i++) { + res.push(terms[i].uri); + } + } + return res; + } + }, { + key: 'length', + get: function get() { + return this.statements.length; + } + }]); + + return IndexedFormula; +}(Formula); + +exports.default = IndexedFormula; + + IndexedFormula.handleRDFType = handleRDFType; },{"./formula":59,"./node":68,"./query":72,"./statement":78,"./util":82,"./variable":83}],62:[function(_dereq_,module,exports){ -'use strict'; - -var jsonParser = function () { - return { - parseJSON: function parseJSON(data, source, store) { - var subject, predicate, object; - var bnodes = {}; - var why = store.sym(source); - for (var x in data) { - if (x.indexOf('_:') === 0) { - if (bnodes[x]) { - subject = bnodes[x]; - } else { - subject = store.bnode(x); - bnodes[x] = subject; - } - } else { - subject = store.sym(x); - } - var preds = data[x]; - for (var y in preds) { - var objects = preds[y]; - predicate = store.sym(y); - for (var z in objects) { - var obj = objects[z]; - if (obj.type === 'uri') { - object = store.sym(obj.value); - store.add(subject, predicate, object, why); - } else if (obj.type === 'BlankNode') { - if (bnodes[obj.value]) { - object = bnodes[obj.value]; - } else { - object = store.bnode(obj.value); - bnodes[obj.value] = object; - } - store.add(subject, predicate, object, why); - } else if (obj.type === 'Literal') { - // var datatype - if (obj.datatype) { - object = store.literal(obj.value, undefined, store.sym(obj.datatype)); - } else if (obj.lang) { - object = store.literal(obj.value, obj.lang); - } else { - object = store.literal(obj.value); - } - store.add(subject, predicate, object, why); - } else { - throw new Error('error: unexpected termtype: ' + z.type); - } - } - } - } - } - }; -}(); - +'use strict'; + +var jsonParser = function () { + return { + parseJSON: function parseJSON(data, source, store) { + var subject, predicate, object; + var bnodes = {}; + var why = store.sym(source); + for (var x in data) { + if (x.indexOf('_:') === 0) { + if (bnodes[x]) { + subject = bnodes[x]; + } else { + subject = store.bnode(x); + bnodes[x] = subject; + } + } else { + subject = store.sym(x); + } + var preds = data[x]; + for (var y in preds) { + var objects = preds[y]; + predicate = store.sym(y); + for (var z in objects) { + var obj = objects[z]; + if (obj.type === 'uri') { + object = store.sym(obj.value); + store.add(subject, predicate, object, why); + } else if (obj.type === 'BlankNode') { + if (bnodes[obj.value]) { + object = bnodes[obj.value]; + } else { + object = store.bnode(obj.value); + bnodes[obj.value] = object; + } + store.add(subject, predicate, object, why); + } else if (obj.type === 'Literal') { + // var datatype + if (obj.datatype) { + object = store.literal(obj.value, undefined, store.sym(obj.datatype)); + } else if (obj.lang) { + object = store.literal(obj.value, obj.lang); + } else { + object = store.literal(obj.value); + } + store.add(subject, predicate, object, why); + } else { + throw new Error('error: unexpected termtype: ' + z.type); + } + } + } + } + } + }; +}(); + module.exports = jsonParser; },{}],63:[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; }; - -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 NamedNode = _dereq_('./named-node'); -var Node = _dereq_('./node'); -var XSD = _dereq_('./xsd'); - -var Literal = function (_Node) { - _inherits(Literal, _Node); - - function Literal(value, language, datatype) { - _classCallCheck(this, Literal); - - var _this = _possibleConstructorReturn(this, (Literal.__proto__ || Object.getPrototypeOf(Literal)).call(this)); - - _this.termType = Literal.termType; - _this.value = value; - if (language) { - _this.lang = language; - datatype = XSD.langString; - } - // If not specified, a literal has the implied XSD.string default datatype - if (datatype) { - _this.datatype = NamedNode.fromValue(datatype); - } - return _this; - } - - _createClass(Literal, [{ - key: 'copy', - value: function copy() { - return new Literal(this.value, this.lang, this.datatype); - } - }, { - key: 'equals', - value: function equals(other) { - if (!other) { - return false; - } - return this.termType === other.termType && this.value === other.value && this.language === other.language && (!this.datatype && !other.datatype || this.datatype && this.datatype.equals(other.datatype)); - } - }, { - key: 'toNT', - value: function toNT() { - if (typeof this.value === 'number') { - return this.toString(); - } else if (typeof this.value !== 'string') { - throw new Error('Value of RDF literal is not string or number: ' + this.value); - } - var str = this.value; - str = str.replace(/\\/g, '\\\\'); - str = str.replace(/\"/g, '\\"'); - str = str.replace(/\n/g, '\\n'); - str = '"' + str + '"'; - - if (this.language) { - str += '@' + this.language; - } else if (!this.datatype.equals(XSD.string)) { - // Only add datatype if it's not a string - str += '^^' + this.datatype.toCanonical(); - } - return str; - } - }, { - key: 'toString', - value: function toString() { - return '' + this.value; - } - /** - * @method fromBoolean - * @static - * @param value {Boolean} - * @return {Literal} - */ - - }, { - key: 'language', - get: function get() { - return this.lang; - }, - set: function set(language) { - this.lang = language || ''; - } - }], [{ - key: 'fromBoolean', - value: function fromBoolean(value) { - var strValue = value ? '1' : '0'; - return new Literal(strValue, null, XSD.boolean); - } - /** - * @method fromDate - * @static - * @param value {Date} - * @return {Literal} - */ - - }, { - key: 'fromDate', - value: function fromDate(value) { - if (!(value instanceof Date)) { - throw new TypeError('Invalid argument to Literal.fromDate()'); - } - var d2 = function d2(x) { - return ('' + (100 + x)).slice(1, 3); - }; - var date = '' + value.getUTCFullYear() + '-' + d2(value.getUTCMonth() + 1) + '-' + d2(value.getUTCDate()) + 'T' + d2(value.getUTCHours()) + ':' + d2(value.getUTCMinutes()) + ':' + d2(value.getUTCSeconds()) + 'Z'; - return new Literal(date, null, XSD.dateTime); - } - /** - * @method fromNumber - * @static - * @param value {Number} - * @return {Literal} - */ - - }, { - key: 'fromNumber', - value: function fromNumber(value) { - if (typeof value !== 'number') { - throw new TypeError('Invalid argument to Literal.fromNumber()'); - } - var datatype = void 0; - var strValue = value.toString(); - if (strValue.indexOf('e') < 0 && Math.abs(value) <= Number.MAX_SAFE_INTEGER) { - datatype = Number.isInteger(value) ? XSD.integer : XSD.decimal; - } else { - datatype = XSD.double; - } - return new Literal(strValue, null, datatype); - } - /** - * @method fromValue - * @param value - * @return {Literal} - */ - - }, { - key: 'fromValue', - value: function fromValue(value) { - if (typeof value === 'undefined' || value === null) { - return value; - } - if ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && value.termType) { - // this is a Node instance - return value; - } - switch (typeof value === 'undefined' ? 'undefined' : _typeof(value)) { - case 'object': - if (value instanceof Date) { - return Literal.fromDate(value); - } - case 'boolean': - return Literal.fromBoolean(value); - case 'number': - return Literal.fromNumber(value); - case 'string': - return new Literal(value); - } - throw new Error("Can't make literal from " + value + ' of type ' + (typeof value === 'undefined' ? 'undefined' : _typeof(value))); - } - }]); - - return Literal; -}(Node); - -Literal.termType = 'Literal'; -Literal.prototype.classOrder = ClassOrder['Literal']; -Literal.prototype.datatype = XSD.string; -Literal.prototype.lang = ''; -Literal.prototype.isVar = 0; - +'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; }; + +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 NamedNode = _dereq_('./named-node'); +var Node = _dereq_('./node'); +var XSD = _dereq_('./xsd'); + +var Literal = function (_Node) { + _inherits(Literal, _Node); + + function Literal(value, language, datatype) { + _classCallCheck(this, Literal); + + var _this = _possibleConstructorReturn(this, (Literal.__proto__ || Object.getPrototypeOf(Literal)).call(this)); + + _this.termType = Literal.termType; + _this.value = value; + if (language) { + _this.lang = language; + datatype = XSD.langString; + } + // If not specified, a literal has the implied XSD.string default datatype + if (datatype) { + _this.datatype = NamedNode.fromValue(datatype); + } + return _this; + } + + _createClass(Literal, [{ + key: 'copy', + value: function copy() { + return new Literal(this.value, this.lang, this.datatype); + } + }, { + key: 'equals', + value: function equals(other) { + if (!other) { + return false; + } + return this.termType === other.termType && this.value === other.value && this.language === other.language && (!this.datatype && !other.datatype || this.datatype && this.datatype.equals(other.datatype)); + } + }, { + key: 'toNT', + value: function toNT() { + if (typeof this.value === 'number') { + return this.toString(); + } else if (typeof this.value !== 'string') { + throw new Error('Value of RDF literal is not string or number: ' + this.value); + } + var str = this.value; + str = str.replace(/\\/g, '\\\\'); + str = str.replace(/\"/g, '\\"'); + str = str.replace(/\n/g, '\\n'); + str = '"' + str + '"'; + + if (this.language) { + str += '@' + this.language; + } else if (!this.datatype.equals(XSD.string)) { + // Only add datatype if it's not a string + str += '^^' + this.datatype.toCanonical(); + } + return str; + } + }, { + key: 'toString', + value: function toString() { + return '' + this.value; + } + /** + * @method fromBoolean + * @static + * @param value {Boolean} + * @return {Literal} + */ + + }, { + key: 'language', + get: function get() { + return this.lang; + }, + set: function set(language) { + this.lang = language || ''; + } + }], [{ + key: 'fromBoolean', + value: function fromBoolean(value) { + var strValue = value ? '1' : '0'; + return new Literal(strValue, null, XSD.boolean); + } + /** + * @method fromDate + * @static + * @param value {Date} + * @return {Literal} + */ + + }, { + key: 'fromDate', + value: function fromDate(value) { + if (!(value instanceof Date)) { + throw new TypeError('Invalid argument to Literal.fromDate()'); + } + var d2 = function d2(x) { + return ('' + (100 + x)).slice(1, 3); + }; + var date = '' + value.getUTCFullYear() + '-' + d2(value.getUTCMonth() + 1) + '-' + d2(value.getUTCDate()) + 'T' + d2(value.getUTCHours()) + ':' + d2(value.getUTCMinutes()) + ':' + d2(value.getUTCSeconds()) + 'Z'; + return new Literal(date, null, XSD.dateTime); + } + /** + * @method fromNumber + * @static + * @param value {Number} + * @return {Literal} + */ + + }, { + key: 'fromNumber', + value: function fromNumber(value) { + if (typeof value !== 'number') { + throw new TypeError('Invalid argument to Literal.fromNumber()'); + } + var datatype = void 0; + var strValue = value.toString(); + if (strValue.indexOf('e') < 0 && Math.abs(value) <= Number.MAX_SAFE_INTEGER) { + datatype = Number.isInteger(value) ? XSD.integer : XSD.decimal; + } else { + datatype = XSD.double; + } + return new Literal(strValue, null, datatype); + } + /** + * @method fromValue + * @param value + * @return {Literal} + */ + + }, { + key: 'fromValue', + value: function fromValue(value) { + if (typeof value === 'undefined' || value === null) { + return value; + } + if ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && value.termType) { + // this is a Node instance + return value; + } + switch (typeof value === 'undefined' ? 'undefined' : _typeof(value)) { + case 'object': + if (value instanceof Date) { + return Literal.fromDate(value); + } + case 'boolean': + return Literal.fromBoolean(value); + case 'number': + return Literal.fromNumber(value); + case 'string': + return new Literal(value); + } + throw new Error("Can't make literal from " + value + ' of type ' + (typeof value === 'undefined' ? 'undefined' : _typeof(value))); + } + }]); + + return Literal; +}(Node); + +Literal.termType = 'Literal'; +Literal.prototype.classOrder = ClassOrder['Literal']; +Literal.prototype.datatype = XSD.string; +Literal.prototype.lang = ''; +Literal.prototype.isVar = 0; + module.exports = Literal; },{"./class-order":52,"./named-node":66,"./node":68,"./xsd":84}],64:[function(_dereq_,module,exports){ -"use strict"; - -/** - * A Dummy log - * @module log - */ -module.exports = { - debug: function debug(x) { - return; - }, - warn: function warn(x) { - return; - }, - info: function info(x) { - return; - }, - error: function error(x) { - return; - }, - success: function success(x) { - return; - }, - msg: function msg(x) { - return; - } +"use strict"; + +/** + * A Dummy log + * @module log + */ +module.exports = { + debug: function debug(x) { + return; + }, + warn: function warn(x) { + return; + }, + info: function info(x) { + return; + }, + error: function error(x) { + return; + }, + success: function success(x) { + return; + }, + msg: function msg(x) { + return; + } }; },{}],65:[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; }; - -/** -* -* UTF-8 data encode / decode -* http://www.webtoolkit.info/ -* -**/ -var Uri = _dereq_('./uri'); -var ArrayIndexOf = _dereq_('./util').ArrayIndexOf; - -var N3Parser = function () { - - function hexify(str) { - // also used in parser - return encodeURI(str); - } - - var Utf8 = { - // public method for url encoding - encode: function encode(string) { - string = string.replace(/\r\n/g, "\n"); - var utftext = ""; - - for (var n = 0; n < string.length; n++) { - - var c = string.charCodeAt(n); - - if (c < 128) { - utftext += String.fromCharCode(c); - } else if (c > 127 && c < 2048) { - utftext += String.fromCharCode(c >> 6 | 192); - utftext += String.fromCharCode(c & 63 | 128); - } else { - utftext += String.fromCharCode(c >> 12 | 224); - utftext += String.fromCharCode(c >> 6 & 63 | 128); - utftext += String.fromCharCode(c & 63 | 128); - } - } - - return utftext; - }, - // public method for url decoding - decode: function decode(utftext) { - var string = ""; - var i = 0; - - while (i < utftext.length) { - - var c = utftext.charCodeAt(i); - if (c < 128) { - string += String.fromCharCode(c); - i++; - } else if (c > 191 && c < 224) { - string += String.fromCharCode((c & 31) << 6 | utftext.charCodeAt(i + 1) & 63); - i += 2; - } else { - string += String.fromCharCode((c & 15) << 12 | (utftext.charCodeAt(i + 1) & 63) << 6 | utftext.charCodeAt(i + 2) & 63); - i += 3; - } - } - return string; - } - }; // Things we need to define to make converted pythn code work in js - // environment of $rdf - - var RDFSink_forSomeSym = "http://www.w3.org/2000/10/swap/log#forSome"; - var RDFSink_forAllSym = "http://www.w3.org/2000/10/swap/log#forAll"; - var Logic_NS = "http://www.w3.org/2000/10/swap/log#"; - - // pyjs seems to reference runtime library which I didn't find - - var pyjslib_Tuple = function pyjslib_Tuple(theList) { - return theList; - }; - - var pyjslib_List = function pyjslib_List(theList) { - return theList; - }; - - var pyjslib_Dict = function pyjslib_Dict(listOfPairs) { - if (listOfPairs.length > 0) throw "missing.js: oops nnonempty dict not imp"; - return []; - }; - - var pyjslib_len = function pyjslib_len(s) { - return s.length; - }; - - var pyjslib_slice = function pyjslib_slice(str, i, j) { - if (typeof str.slice == 'undefined') throw '@@ mising.js: No .slice function for ' + str + ' of type ' + (typeof str === 'undefined' ? 'undefined' : _typeof(str)); - if (typeof j == 'undefined' || j == null) return str.slice(i); - return str.slice(i, j); // @ exactly the same spec? - }; - var StopIteration = Error('dummy error stop iteration'); - - var pyjslib_Iterator = function pyjslib_Iterator(theList) { - this.last = 0; - this.li = theList; - this.next = function () { - if (this.last == this.li.length) throw StopIteration; - return this.li[this.last++]; - }; - return this; - }; - - var ord = function ord(str) { - return str.charCodeAt(0); - }; - - var string_find = function string_find(str, s) { - return str.indexOf(s); - }; - - var assertFudge = function assertFudge(condition, desc) { - if (condition) return; - if (desc) throw "python Assertion failed: " + desc; - throw "(python) Assertion failed."; - }; - - var stringFromCharCode = function stringFromCharCode(uesc) { - return String.fromCharCode(uesc); - }; - - String.prototype.encode = function (encoding) { - if (encoding != 'utf-8') throw "UTF8_converter: can only do utf-8"; - return Utf8.encode(this); - }; - String.prototype.decode = function (encoding) { - if (encoding != 'utf-8') throw "UTF8_converter: can only do utf-8"; - //return Utf8.decode(this); - return this; - }; - - var uripath_join = function uripath_join(base, given) { - return Uri.join(given, base); // sad but true - }; - - var becauseSubexpression = null; // No reason needed - var diag_tracking = 0; - var diag_chatty_flag = 0; - var diag_progress = function diag_progress(str) {} /*$rdf.log.debug(str);*/ - - // why_BecauseOfData = function(doc, reason) { return doc }; - - - ;var RDF_type_URI = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"; - var DAML_sameAs_URI = "http://www.w3.org/2002/07/owl#sameAs"; - - /* - function SyntaxError(details) { - return new __SyntaxError(details); - } - */ - - function __SyntaxError(details) { - this.details = details; - } - - /* - - $Id: n3parser.js 14561 2008-02-23 06:37:26Z kennyluck $ - - HAND EDITED FOR CONVERSION TO JAVASCRIPT - - This module implements a Nptation3 parser, and the final - part of a notation3 serializer. - - See also: - - Notation 3 - http://www.w3.org/DesignIssues/Notation3 - - Closed World Machine - and RDF Processor - http://www.w3.org/2000/10/swap/cwm - - To DO: See also "@@" in comments - - - Clean up interfaces - ______________________________________________ - - Module originally by Dan Connolly, includeing notation3 - parser and RDF generator. TimBL added RDF stream model - and N3 generation, replaced stream model with use - of common store/formula API. Yosi Scharf developped - the module, including tests and test harness. - - */ - - var ADDED_HASH = "#"; - var LOG_implies_URI = "http://www.w3.org/2000/10/swap/log#implies"; - var INTEGER_DATATYPE = "http://www.w3.org/2001/XMLSchema#integer"; - var FLOAT_DATATYPE = "http://www.w3.org/2001/XMLSchema#double"; - var DECIMAL_DATATYPE = "http://www.w3.org/2001/XMLSchema#decimal"; - var DATE_DATATYPE = "http://www.w3.org/2001/XMLSchema#date"; - var DATETIME_DATATYPE = "http://www.w3.org/2001/XMLSchema#dateTime"; - var BOOLEAN_DATATYPE = "http://www.w3.org/2001/XMLSchema#boolean"; - var option_noregen = 0; - var _notQNameChars = "\t\r\n !\"#$%&'()*.,+/;<=>?@[\\]^`{|}~"; - var _notNameChars = _notQNameChars + ":"; - var _rdfns = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; - var N3CommentCharacter = "#"; - var eol = new RegExp("^[ \\t]*(#[^\\n]*)?\\r?\\n", 'g'); - var eof = new RegExp("^[ \\t]*(#[^\\n]*)?$", 'g'); - var ws = new RegExp("^[ \\t]*", 'g'); - var signed_integer = new RegExp("^[-+]?[0-9]+", 'g'); - var number_syntax = new RegExp("^([-+]?[0-9]+)(\\.[0-9]+)?(e[-+]?[0-9]+)?", 'g'); - var datetime_syntax = new RegExp('^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9](T[0-9][0-9]:[0-9][0-9](:[0-9][0-9](\\.[0-9]*)?)?)?Z?'); - - var digitstring = new RegExp("^[0-9]+", 'g'); - var interesting = new RegExp("[\\\\\\r\\n\\\"]", 'g'); - var langcode = new RegExp("^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*", 'g'); - function SinkParser(store, openFormula, thisDoc, baseURI, genPrefix, metaURI, flags, why) { - return new __SinkParser(store, openFormula, thisDoc, baseURI, genPrefix, metaURI, flags, why); - } - function __SinkParser(store, openFormula, thisDoc, baseURI, genPrefix, metaURI, flags, why) { - if (typeof openFormula == 'undefined') openFormula = null; - if (typeof thisDoc == 'undefined') thisDoc = ""; - if (typeof baseURI == 'undefined') baseURI = null; - if (typeof genPrefix == 'undefined') genPrefix = ""; - if (typeof metaURI == 'undefined') metaURI = null; - if (typeof flags == 'undefined') flags = ""; - if (typeof why == 'undefined') why = null; - /* - note: namespace names should *not* end in #; - the # will get added during qname processing */ - - this._bindings = new pyjslib_Dict([]); - this._flags = flags; - if (thisDoc != "") { - assertFudge(thisDoc.indexOf(":") >= 0, "Document URI not absolute: " + thisDoc); - this._bindings[""] = thisDoc + "#"; - } - this._store = store; - if (genPrefix) { - store.setGenPrefix(genPrefix); - } - this._thisDoc = thisDoc; - this.source = store.sym(thisDoc); - this.lines = 0; - this.statementCount = 0; - this.startOfLine = 0; - this.previousLine = 0; - this._genPrefix = genPrefix; - this.keywords = new pyjslib_List(["a", "this", "bind", "has", "is", "of", "true", "false"]); - this.keywordsSet = 0; - this._anonymousNodes = new pyjslib_Dict([]); - this._variables = new pyjslib_Dict([]); - this._parentVariables = new pyjslib_Dict([]); - this._reason = why; - this._reason2 = null; - if (diag_tracking) { - this._reason2 = why_BecauseOfData(store.sym(thisDoc), this._reason); - } - if (baseURI) { - this._baseURI = baseURI; - } else { - if (thisDoc) { - this._baseURI = thisDoc; - } else { - this._baseURI = null; - } - } - assertFudge(!this._baseURI || this._baseURI.indexOf(":") >= 0); - if (!this._genPrefix) { - if (this._thisDoc) { - this._genPrefix = this._thisDoc + "#_g"; - } else { - this._genPrefix = RDFSink_uniqueURI(); - } - } - if (openFormula == null) { - if (this._thisDoc) { - this._formula = store.formula(thisDoc + "#_formula"); - } else { - this._formula = store.formula(); - } - } else { - this._formula = openFormula; - } - this._context = this._formula; - this._parentContext = null; - } - __SinkParser.prototype.here = function (i) { - return this._genPrefix + "_L" + this.lines + "C" + (i - this.startOfLine + 1); - }; - __SinkParser.prototype.formula = function () { - return this._formula; - }; - __SinkParser.prototype.loadStream = function (stream) { - return this.loadBuf(stream.read()); - }; - __SinkParser.prototype.loadBuf = function (buf) { - /* - Parses a buffer and returns its top level formula*/ - - this.startDoc(); - this.feed(buf); - return this.endDoc(); - }; - __SinkParser.prototype.feed = function (octets) { - /* - Feed an octet stream tothe parser - if BadSyntax is raised, the string - passed in the exception object is the - remainder after any statements have been parsed. - So if there is more data to feed to the - parser, it should be straightforward to recover.*/ - - var str = octets.decode("utf-8"); - var i = 0; - while (i >= 0) { - var j = this.skipSpace(str, i); - if (j < 0) { - return; - } - var i = this.directiveOrStatement(str, j); - if (i < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, j, "expected directive or statement"); - } - } - }; - __SinkParser.prototype.directiveOrStatement = function (str, h) { - var i = this.skipSpace(str, h); - if (i < 0) { - return i; - } - var j = this.directive(str, i); - if (j >= 0) { - return this.checkDot(str, j); - } - var j = this.statement(str, i); - if (j >= 0) { - return this.checkDot(str, j); - } - return j; - }; - __SinkParser.prototype.tok = function (tok, str, i) { - /* - Check for keyword. Space must have been stripped on entry and - we must not be at end of file.*/ - var whitespace = "\t\n\v\f\r "; - if (pyjslib_slice(str, i, i + 1) == "@") { - var i = i + 1; - } else { - if (ArrayIndexOf(this.keywords, tok) < 0) { - return -1; - } - } - var k = i + pyjslib_len(tok); - if (pyjslib_slice(str, i, k) == tok && _notQNameChars.indexOf(str.charAt(k)) >= 0) { - return k; - } else { - return -1; - } - }; - __SinkParser.prototype.directive = function (str, i) { - var j = this.skipSpace(str, i); - if (j < 0) { - return j; - } - var res = new pyjslib_List([]); - var j = this.tok("bind", str, i); - if (j > 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "keyword bind is obsolete: use @prefix"); - } - var j = this.tok("keywords", str, i); - if (j > 0) { - var i = this.commaSeparatedList(str, j, res, false); - if (i < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "'@keywords' needs comma separated list of words"); - } - this.setKeywords(pyjslib_slice(res, null, null)); - if (diag_chatty_flag > 80) { - diag_progress("Keywords ", this.keywords); - } - return i; - } - var j = this.tok("forAll", str, i); - if (j > 0) { - var i = this.commaSeparatedList(str, j, res, true); - if (i < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "Bad variable list after @forAll"); - } - - var __x = new pyjslib_Iterator(res); - try { - while (true) { - var x = __x.next(); - - if (ArrayIndexOf(this._variables, x) < 0 || ArrayIndexOf(this._parentVariables, x) >= 0) { - this._variables[x] = this._context.newUniversal(x); - } - } - } catch (e) { - if (e != StopIteration) { - throw e; - } - } - - return i; - } - var j = this.tok("forSome", str, i); - if (j > 0) { - var i = this.commaSeparatedList(str, j, res, this.uri_ref2); - if (i < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "Bad variable list after @forSome"); - } - - var __x = new pyjslib_Iterator(res); - try { - while (true) { - var x = __x.next(); - - this._context.declareExistential(x); - } - } catch (e) { - if (e != StopIteration) { - throw e; - } - } - - return i; - } - var j = this.tok("prefix", str, i); - if (j >= 0) { - var t = new pyjslib_List([]); - var i = this.qname(str, j, t); - if (i < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, j, "expected qname after @prefix"); - } - var j = this.uri_ref2(str, i, t); - if (j < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "expected after @prefix _qname_"); - } - var ns = t[1].uri; - if (this._baseURI) { - var ns = uripath_join(this._baseURI, ns); - } else { - assertFudge(ns.indexOf(":") >= 0, "With no base URI, cannot handle relative URI for NS"); - } - assertFudge(ns.indexOf(":") >= 0); - this._bindings[t[0][0]] = ns; - - this.bind(t[0][0], hexify(ns)); - return j; - } - var j = this.tok("base", str, i); - if (j >= 0) { - var t = new pyjslib_List([]); - var i = this.uri_ref2(str, j, t); - if (i < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, j, "expected after @base "); - } - var ns = t[0].uri; - if (this._baseURI) { - var ns = uripath_join(this._baseURI, ns); - } else { - throw BadSyntax(this._thisDoc, this.lines, str, j, "With no previous base URI, cannot use relative URI in @base <" + ns + ">"); - } - assertFudge(ns.indexOf(":") >= 0); - this._baseURI = ns; - return i; - } - return -1; - }; - __SinkParser.prototype.bind = function (qn, uri) { - if (qn == "") {} else { - this._store.setPrefixForURI(qn, uri); - } - }; - __SinkParser.prototype.setKeywords = function (k) { - /* - Takes a list of strings*/ - - if (k == null) { - this.keywordsSet = 0; - } else { - this.keywords = k; - this.keywordsSet = 1; - } - }; - __SinkParser.prototype.startDoc = function () {}; - __SinkParser.prototype.endDoc = function () { - /* - Signal end of document and stop parsing. returns formula*/ - - return this._formula; - }; - __SinkParser.prototype.makeStatement = function (quad) { - quad[0].add(quad[2], quad[1], quad[3], this.source); - this.statementCount += 1; - }; - __SinkParser.prototype.statement = function (str, i) { - var r = new pyjslib_List([]); - var i = this.object(str, i, r); - if (i < 0) { - return i; - } - var j = this.property_list(str, i, r[0]); - if (j < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "expected propertylist"); - } - return j; - }; - __SinkParser.prototype.subject = function (str, i, res) { - return this.item(str, i, res); - }; - __SinkParser.prototype.verb = function (str, i, res) { - /* - has _prop_ - is _prop_ of - a - = - _prop_ - >- prop -> - <- prop -< - _operator_*/ - - var j = this.skipSpace(str, i); - if (j < 0) { - return j; - } - var r = new pyjslib_List([]); - var j = this.tok("has", str, i); - if (j >= 0) { - var i = this.prop(str, j, r); - if (i < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, j, "expected property after 'has'"); - } - res.push(new pyjslib_Tuple(["->", r[0]])); - return i; - } - var j = this.tok("is", str, i); - if (j >= 0) { - var i = this.prop(str, j, r); - if (i < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, j, "expected after 'is'"); - } - var j = this.skipSpace(str, i); - if (j < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "End of file found, expected property after 'is'"); - return j; - } - var i = j; - var j = this.tok("of", str, i); - if (j < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "expected 'of' after 'is' "); - } - res.push(new pyjslib_Tuple(["<-", r[0]])); - return j; - } - var j = this.tok("a", str, i); - if (j >= 0) { - res.push(new pyjslib_Tuple(["->", this._store.sym(RDF_type_URI)])); - return j; - } - if (pyjslib_slice(str, i, i + 2) == "<=") { - res.push(new pyjslib_Tuple(["<-", this._store.sym(Logic_NS + "implies")])); - return i + 2; - } - if (pyjslib_slice(str, i, i + 1) == "=") { - if (pyjslib_slice(str, i + 1, i + 2) == ">") { - res.push(new pyjslib_Tuple(["->", this._store.sym(Logic_NS + "implies")])); - return i + 2; - } - res.push(new pyjslib_Tuple(["->", this._store.sym(DAML_sameAs_URI)])); - return i + 1; - } - if (pyjslib_slice(str, i, i + 2) == ":=") { - res.push(new pyjslib_Tuple(["->", Logic_NS + "becomes"])); - return i + 2; - } - var j = this.prop(str, i, r); - if (j >= 0) { - res.push(new pyjslib_Tuple(["->", r[0]])); - return j; - } - if (pyjslib_slice(str, i, i + 2) == ">-" || pyjslib_slice(str, i, i + 2) == "<-") { - throw BadSyntax(this._thisDoc, this.lines, str, j, ">- ... -> syntax is obsolete."); - } - return -1; - }; - __SinkParser.prototype.prop = function (str, i, res) { - return this.item(str, i, res); - }; - __SinkParser.prototype.item = function (str, i, res) { - return this.path(str, i, res); - }; - __SinkParser.prototype.blankNode = function (uri) { - return this._context.bnode(uri, this._reason2); - }; - __SinkParser.prototype.path = function (str, i, res) { - /* - Parse the path production. - */ - - var j = this.nodeOrLiteral(str, i, res); - if (j < 0) { - return j; - } - while ("!^.".indexOf(pyjslib_slice(str, j, j + 1)) >= 0) { - var ch = pyjslib_slice(str, j, j + 1); - if (ch == ".") { - var ahead = pyjslib_slice(str, j + 1, j + 2); - if (!ahead || _notNameChars.indexOf(ahead) >= 0 && ":?<[{(".indexOf(ahead) < 0) { - break; - } - } - var subj = res.pop(); - var obj = this.blankNode(this.here(j)); - var j = this.node(str, j + 1, res); - if (j < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, j, "EOF found in middle of path syntax"); - } - var pred = res.pop(); - if (ch == "^") { - this.makeStatement(new pyjslib_Tuple([this._context, pred, obj, subj])); - } else { - this.makeStatement(new pyjslib_Tuple([this._context, pred, subj, obj])); - } - res.push(obj); - } - return j; - }; - __SinkParser.prototype.anonymousNode = function (ln) { - /* - Remember or generate a term for one of these _: anonymous nodes*/ - - var term = this._anonymousNodes[ln]; - if (term) { - return term; - } - var term = this._store.bnode(this._context, this._reason2); - this._anonymousNodes[ln] = term; - return term; - }; - __SinkParser.prototype.node = function (str, i, res, subjectAlready) { - if (typeof subjectAlready == 'undefined') subjectAlready = null; - /* - Parse the production. - Space is now skipped once at the beginning - instead of in multipe calls to self.skipSpace(). - */ - - var subj = subjectAlready; - var j = this.skipSpace(str, i); - if (j < 0) { - return j; - } - var i = j; - var ch = pyjslib_slice(str, i, i + 1); - if (ch == "[") { - var bnodeID = this.here(i); - var j = this.skipSpace(str, i + 1); - if (j < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "EOF after '['"); - } - if (pyjslib_slice(str, j, j + 1) == "=") { - var i = j + 1; - var objs = new pyjslib_List([]); - var j = this.objectList(str, i, objs); - - if (j >= 0) { - var subj = objs[0]; - if (pyjslib_len(objs) > 1) { - - var __obj = new pyjslib_Iterator(objs); - try { - while (true) { - var obj = __obj.next(); - - this.makeStatement(new pyjslib_Tuple([this._context, this._store.sym(DAML_sameAs_URI), subj, obj])); - } - } catch (e) { - if (e != StopIteration) { - throw e; - } - } - } - var j = this.skipSpace(str, j); - if (j < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "EOF when objectList expected after [ = "); - } - if (pyjslib_slice(str, j, j + 1) == ";") { - var j = j + 1; - } - } else { - throw BadSyntax(this._thisDoc, this.lines, str, i, "objectList expected after [= "); - } - } - if (subj == null) { - var subj = this.blankNode(bnodeID); - } - var i = this.property_list(str, j, subj); - if (i < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, j, "property_list expected"); - } - var j = this.skipSpace(str, i); - if (j < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "EOF when ']' expected after [ "); - } - if (pyjslib_slice(str, j, j + 1) != "]") { - throw BadSyntax(this._thisDoc, this.lines, str, j, "']' expected"); - } - res.push(subj); - return j + 1; - } - if (ch == "{") { - var ch2 = pyjslib_slice(str, i + 1, i + 2); - if (ch2 == "$") { - i += 1; - var j = i + 1; - var mylist = new pyjslib_List([]); - var first_run = true; - while (1) { - var i = this.skipSpace(str, j); - if (i < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "needed '$}', found end."); - } - if (pyjslib_slice(str, i, i + 2) == "$}") { - var j = i + 2; - break; - } - if (!first_run) { - if (pyjslib_slice(str, i, i + 1) == ",") { - i += 1; - } else { - throw BadSyntax(this._thisDoc, this.lines, str, i, "expected: ','"); - } - } else { - var first_run = false; - } - var item = new pyjslib_List([]); - var j = this.item(str, i, item); - if (j < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "expected item in set or '$}'"); - } - mylist.push(item[0]); - } - res.push(this._store.newSet(mylist, this._context)); - return j; - } else { - var j = i + 1; - var oldParentContext = this._parentContext; - this._parentContext = this._context; - var parentAnonymousNodes = this._anonymousNodes; - var grandParentVariables = this._parentVariables; - this._parentVariables = this._variables; - this._anonymousNodes = new pyjslib_Dict([]); - this._variables = this._variables.slice(); - var reason2 = this._reason2; - this._reason2 = becauseSubexpression; - if (subj == null) { - var subj = this._store.formula(); - } - this._context = subj; - while (1) { - var i = this.skipSpace(str, j); - if (i < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "needed '}', found end."); - } - if (pyjslib_slice(str, i, i + 1) == "}") { - var j = i + 1; - break; - } - var j = this.directiveOrStatement(str, i); - if (j < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "expected statement or '}'"); - } - } - this._anonymousNodes = parentAnonymousNodes; - this._variables = this._parentVariables; - this._parentVariables = grandParentVariables; - this._context = this._parentContext; - this._reason2 = reason2; - this._parentContext = oldParentContext; - res.push(subj.close()); - return j; - } - } - if (ch == "(") { - var thing_type = this._store.list; - var ch2 = pyjslib_slice(str, i + 1, i + 2); - if (ch2 == "$") { - var thing_type = this._store.newSet; - i += 1; - } - var j = i + 1; - var mylist = new pyjslib_List([]); - while (1) { - var i = this.skipSpace(str, j); - if (i < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "needed ')', found end."); - } - if (pyjslib_slice(str, i, i + 1) == ")") { - var j = i + 1; - break; - } - var item = new pyjslib_List([]); - var j = this.item(str, i, item); - if (j < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "expected item in list or ')'"); - } - mylist.push(item[0]); - } - res.push(thing_type(mylist, this._context)); - return j; - } - var j = this.tok("this", str, i); - if (j >= 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "Keyword 'this' was ancient N3. Now use @forSome and @forAll keywords."); - res.push(this._context); - return j; - } - var j = this.tok("true", str, i); - if (j >= 0) { - res.push(true); - return j; - } - var j = this.tok("false", str, i); - if (j >= 0) { - res.push(false); - return j; - } - if (subj == null) { - var j = this.uri_ref2(str, i, res); - if (j >= 0) { - return j; - } - } - return -1; - }; - __SinkParser.prototype.property_list = function (str, i, subj) { - /* - Parse property list - Leaves the terminating punctuation in the buffer - */ - - while (1) { - var j = this.skipSpace(str, i); - if (j < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "EOF found when expected verb in property list"); - return j; - } - if (pyjslib_slice(str, j, j + 2) == ":-") { - var i = j + 2; - var res = new pyjslib_List([]); - var j = this.node(str, i, res, subj); - if (j < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "bad {} or () or [] node after :- "); - } - var i = j; - continue; - } - var i = j; - var v = new pyjslib_List([]); - var j = this.verb(str, i, v); - if (j <= 0) { - return i; - } - var objs = new pyjslib_List([]); - var i = this.objectList(str, j, objs); - if (i < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, j, "objectList expected"); - } - - var __obj = new pyjslib_Iterator(objs); - try { - while (true) { - var obj = __obj.next(); - - var pairFudge = v[0]; - var dir = pairFudge[0]; - var sym = pairFudge[1]; - if (dir == "->") { - this.makeStatement(new pyjslib_Tuple([this._context, sym, subj, obj])); - } else { - this.makeStatement(new pyjslib_Tuple([this._context, sym, obj, subj])); - } - } - } catch (e) { - if (e != StopIteration) { - throw e; - } - } - - var j = this.skipSpace(str, i); - if (j < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, j, "EOF found in list of objects"); - return j; - } - if (pyjslib_slice(str, i, i + 1) != ";") { - return i; - } - var i = i + 1; - } - }; - __SinkParser.prototype.commaSeparatedList = function (str, j, res, ofUris) { - /* - return value: -1 bad syntax; >1 new position in str - res has things found appended - Used to use a final value of the function to be called, e.g. this.bareWord - but passing the function didn't work fo js converion pyjs - */ - - var i = this.skipSpace(str, j); - if (i < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "EOF found expecting comma sep list"); - return i; - } - if (str.charAt(i) == ".") { - return j; - } - if (ofUris) { - var i = this.uri_ref2(str, i, res); - } else { - var i = this.bareWord(str, i, res); - } - if (i < 0) { - return -1; - } - while (1) { - var j = this.skipSpace(str, i); - if (j < 0) { - return j; - } - var ch = pyjslib_slice(str, j, j + 1); - if (ch != ",") { - if (ch != ".") { - return -1; - } - return j; - } - if (ofUris) { - var i = this.uri_ref2(str, j + 1, res); - } else { - var i = this.bareWord(str, j + 1, res); - } - if (i < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "bad list content"); - return i; - } - } - }; - __SinkParser.prototype.objectList = function (str, i, res) { - var i = this.object(str, i, res); - if (i < 0) { - return -1; - } - while (1) { - var j = this.skipSpace(str, i); - if (j < 0) { - throw BadSyntax(this._thisDoc, this.lines, str, j, "EOF found after object"); - return j; - } - if (pyjslib_slice(str, j, j + 1) != ",") { - return j; - } - var i = this.object(str, j + 1, res); - if (i < 0) { - return i; - } - } - }; - __SinkParser.prototype.checkDot = function (str, i) { - var j = this.skipSpace(str, i); - if (j < 0) { - return j; - } - if (pyjslib_slice(str, j, j + 1) == ".") { - return j + 1; - } - if (pyjslib_slice(str, j, j + 1) == "}") { - return j; - } - if (pyjslib_slice(str, j, j + 1) == "]") { - return j; - } - throw BadSyntax(this._thisDoc, this.lines, str, j, "expected '.' or '}' or ']' at end of statement"); - return i; - }; - __SinkParser.prototype.uri_ref2 = function (str, i, res) { - /* - Generate uri from n3 representation. - Note that the RDF convention of directly concatenating - NS and local name is now used though I prefer inserting a '#' - to make the namesapces look more like what XML folks expect. - */ - - var qn = new pyjslib_List([]); - var j = this.qname(str, i, qn); - if (j >= 0) { - var pairFudge = qn[0]; - var pfx = pairFudge[0]; - var ln = pairFudge[1]; - if (pfx == null) { - assertFudge(0, "not used?"); - var ns = this._baseURI + ADDED_HASH; - } else { - var ns = this._bindings[pfx]; - if (!ns) { - if (pfx == "_") { - res.push(this.anonymousNode(ln)); - return j; - } - throw BadSyntax(this._thisDoc, this.lines, str, i, "Prefix " + pfx + " not bound."); - } - } - var symb = this._store.sym(ns + ln); - if (ArrayIndexOf(this._variables, symb) >= 0) { - res.push(this._variables[symb]); - } else { - res.push(symb); - } - return j; - } - var i = this.skipSpace(str, i); - if (i < 0) { - return -1; - } - if (str.charAt(i) == "?") { - var v = new pyjslib_List([]); - var j = this.variable(str, i, v); - if (j > 0) { - res.push(v[0]); - return j; - } - return -1; - } else if (str.charAt(i) == "<") { - var i = i + 1; - var st = i; - while (i < pyjslib_len(str)) { - if (str.charAt(i) == ">") { - var uref = pyjslib_slice(str, st, i); - if (this._baseURI) { - var uref = uripath_join(this._baseURI, uref); - } else { - assertFudge(uref.indexOf(":") >= 0, "With no base URI, cannot deal with relative URIs"); - } - if (pyjslib_slice(str, i - 1, i) == "#" && !(pyjslib_slice(uref, -1, null) == "#")) { - var uref = uref + "#"; - } - var symb = this._store.sym(uref); - if (ArrayIndexOf(this._variables, symb) >= 0) { - res.push(this._variables[symb]); - } else { - res.push(symb); - } - return i + 1; - } - var i = i + 1; - } - throw BadSyntax(this._thisDoc, this.lines, str, j, "unterminated URI reference"); - } else if (this.keywordsSet) { - var v = new pyjslib_List([]); - var j = this.bareWord(str, i, v); - if (j < 0) { - return -1; - } - if (ArrayIndexOf(this.keywords, v[0]) >= 0) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "Keyword \"" + v[0] + "\" not allowed here."); - } - res.push(this._store.sym(this._bindings[""] + v[0])); - return j; - } else { - return -1; - } - }; - __SinkParser.prototype.skipSpace = function (str, i) { - /* - Skip white space, newlines and comments. - return -1 if EOF, else position of first non-ws character*/ - - var whitespace = ' \n\r\t\f\x0B\xA0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u200B\u2028\u2029\u3000'; - for (var j = i ? i : 0; j < str.length; j++) { - var ch = str.charAt(j); - // console.log(" skipspace j= "+j + " i= " + i + " n= " + str.length); - // console.log(" skipspace ch <" + ch + ">"); - if (whitespace.indexOf(ch) < 0) { - //not ws - // console.log(" skipspace 2 ch <" + ch + ">"); - if (str.charAt(j) === '#') { - for (;; j++) { - // console.log(" skipspace2 j= "+j + " i= " + i + " n= " + str.length); - if (j === str.length) { - return -1; // EOF - } - if (str.charAt(j) === '\n') { - this.lines = this.lines + 1; - break; - } - }; - } else { - // Not hash - something interesting - // console.log(" skipspace 3 ch <" + ch + ">"); - return j; - } - } else { - // Whitespace - // console.log(" skipspace 5 ch <" + ch + ">"); - if (str.charAt(j) === '\n') { - this.lines = this.lines + 1; - } - } - } // next j - return -1; // EOF - }; - - __SinkParser.prototype.variable = function (str, i, res) { - /* - ?abc -> variable(:abc) - */ - - var j = this.skipSpace(str, i); - if (j < 0) { - return -1; - } - if (pyjslib_slice(str, j, j + 1) != "?") { - return -1; - } - var j = j + 1; - var i = j; - if ("0123456789-".indexOf(str.charAt(j)) >= 0) { - throw BadSyntax(this._thisDoc, this.lines, str, j, "Varible name can't start with '" + str.charAt(j) + "s'"); - return -1; - } - while (i < pyjslib_len(str) && _notNameChars.indexOf(str.charAt(i)) < 0) { - var i = i + 1; - } - if (this._parentContext == null) { - throw BadSyntax(this._thisDoc, this.lines, str, j, "Can't use ?xxx syntax for variable in outermost level: " + pyjslib_slice(str, j - 1, i)); - } - res.push(this._store.variable(pyjslib_slice(str, j, i))); - return i; - }; - __SinkParser.prototype.bareWord = function (str, i, res) { - /* - abc -> :abc - */ - - var j = this.skipSpace(str, i); - if (j < 0) { - return -1; - } - var ch = str.charAt(j); - if ("0123456789-".indexOf(ch) >= 0) { - return -1; - } - if (_notNameChars.indexOf(ch) >= 0) { - return -1; - } - var i = j; - while (i < pyjslib_len(str) && _notNameChars.indexOf(str.charAt(i)) < 0) { - var i = i + 1; - } - res.push(pyjslib_slice(str, j, i)); - return i; - }; - __SinkParser.prototype.qname = function (str, i, res) { - /* - xyz:def -> ('xyz', 'def') - If not in keywords and keywordsSet: def -> ('', 'def') - :def -> ('', 'def') - */ - - var i = this.skipSpace(str, i); - if (i < 0) { - return -1; - } - var c = str.charAt(i); - if ("0123456789-+".indexOf(c) >= 0) { - return -1; - } - if (_notNameChars.indexOf(c) < 0) { - var ln = c; - var i = i + 1; - while (i < pyjslib_len(str)) { - var c = str.charAt(i); - if (_notNameChars.indexOf(c) < 0) { - var ln = ln + c; - var i = i + 1; - } else { - break; - } - } - } else { - var ln = ""; - } - if (i < pyjslib_len(str) && str.charAt(i) == ":") { - var pfx = ln; - var i = i + 1; - var ln = ""; - while (i < pyjslib_len(str)) { - var c = str.charAt(i); - if (_notNameChars.indexOf(c) < 0) { - var ln = ln + c; - var i = i + 1; - } else { - break; - } - } - res.push(new pyjslib_Tuple([pfx, ln])); - return i; - } else { - if (ln && this.keywordsSet && ArrayIndexOf(this.keywords, ln) < 0) { - res.push(new pyjslib_Tuple(["", ln])); - return i; - } - return -1; - } - }; - __SinkParser.prototype.object = function (str, i, res) { - var j = this.subject(str, i, res); - if (j >= 0) { - return j; - } else { - var j = this.skipSpace(str, i); - if (j < 0) { - return -1; - } else { - var i = j; - } - if (str.charAt(i) == "\"") { - if (pyjslib_slice(str, i, i + 3) == "\"\"\"") { - var delim = "\"\"\""; - } else { - var delim = "\""; - } - var i = i + pyjslib_len(delim); - var pairFudge = this.strconst(str, i, delim); - var j = pairFudge[0]; - var s = pairFudge[1]; - res.push(this._store.literal(s)); - diag_progress("New string const ", s, j); - return j; - } else { - return -1; - } - } - }; - __SinkParser.prototype.nodeOrLiteral = function (str, i, res) { - var j = this.node(str, i, res); - if (j >= 0) { - return j; - } else { - var j = this.skipSpace(str, i); - if (j < 0) { - return -1; - } else { - var i = j; - } - var ch = str.charAt(i); - if ("-+0987654321".indexOf(ch) >= 0) { - - datetime_syntax.lastIndex = 0; - var m = datetime_syntax.exec(str.slice(i)); - if (m != null) { - // j = ( i + datetime_syntax.lastIndex ) ; - var val = m[0]; - j = i + val.length; - if (val.indexOf("T") >= 0) { - res.push(this._store.literal(val, undefined, this._store.sym(DATETIME_DATATYPE))); - } else { - res.push(this._store.literal(val, undefined, this._store.sym(DATE_DATATYPE))); - } - } else { - number_syntax.lastIndex = 0; - var m = number_syntax.exec(str.slice(i)); - if (m == null) { - throw BadSyntax(this._thisDoc, this.lines, str, i, "Bad number or date syntax"); - } - j = i + number_syntax.lastIndex; - var val = pyjslib_slice(str, i, j); - if (val.indexOf("e") >= 0) { - res.push(this._store.literal(parseFloat(val), undefined, this._store.sym(FLOAT_DATATYPE))); - } else if (pyjslib_slice(str, i, j).indexOf(".") >= 0) { - res.push(this._store.literal(parseFloat(val), undefined, this._store.sym(DECIMAL_DATATYPE))); - } else { - res.push(this._store.literal(parseInt(val), undefined, this._store.sym(INTEGER_DATATYPE))); - } - }; - return j; // Where we have got up to - } - if (str.charAt(i) == "\"") { - if (pyjslib_slice(str, i, i + 3) == "\"\"\"") { - var delim = "\"\"\""; - } else { - var delim = "\""; - } - var i = i + pyjslib_len(delim); - var dt = null; - var pairFudge = this.strconst(str, i, delim); - var j = pairFudge[0]; - var s = pairFudge[1]; - var lang = null; - if (pyjslib_slice(str, j, j + 1) == "@") { - langcode.lastIndex = 0; - - var m = langcode.exec(str.slice(j + 1)); - if (m == null) { - throw BadSyntax(this._thisDoc, startline, str, i, "Bad language code syntax on string literal, after @"); - } - var i = langcode.lastIndex + j + 1; - - var lang = pyjslib_slice(str, j + 1, i); - var j = i; - } - if (pyjslib_slice(str, j, j + 2) == "^^") { - var res2 = new pyjslib_List([]); - var j = this.uri_ref2(str, j + 2, res2); - var dt = res2[0]; - } - res.push(this._store.literal(s, lang, dt)); - return j; - } else { - return -1; - } - } - }; - __SinkParser.prototype.strconst = function (str, i, delim) { - /* - parse an N3 string constant delimited by delim. - return index, val - */ - - var j = i; - var ustr = ""; - var startline = this.lines; - while (j < pyjslib_len(str)) { - var i = j + pyjslib_len(delim); - if (pyjslib_slice(str, j, i) == delim) { - return new pyjslib_Tuple([i, ustr]); - } - if (str.charAt(j) == "\"") { - var ustr = ustr + "\""; - var j = j + 1; - continue; - } - interesting.lastIndex = 0; - var m = interesting.exec(str.slice(j)); - if (!m) { - throw BadSyntax(this._thisDoc, startline, str, j, "Closing quote missing in string at ^ in " + pyjslib_slice(str, j - 20, j) + "^" + pyjslib_slice(str, j, j + 20)); - } - var i = j + interesting.lastIndex - 1; - var ustr = ustr + pyjslib_slice(str, j, i); - var ch = str.charAt(i); - if (ch == "\"") { - var j = i; - continue; - } else if (ch == "\r") { - var j = i + 1; - continue; - } else if (ch == "\n") { - if (delim == "\"") { - throw BadSyntax(this._thisDoc, startline, str, i, "newline found in string literal"); - } - this.lines = this.lines + 1; - var ustr = ustr + ch; - var j = i + 1; - this.previousLine = this.startOfLine; - this.startOfLine = j; - } else if (ch == "\\") { - var j = i + 1; - var ch = pyjslib_slice(str, j, j + 1); - if (!ch) { - throw BadSyntax(this._thisDoc, startline, str, i, "unterminated string literal (2)"); - } - var k = string_find("abfrtvn\\\"", ch); - if (k >= 0) { - var uch = "\a\b\f\r\t\v\n\\\"".charAt(k); - var ustr = ustr + uch; - var j = j + 1; - } else if (ch == "u") { - var pairFudge = this.uEscape(str, j + 1, startline); - var j = pairFudge[0]; - var ch = pairFudge[1]; - var ustr = ustr + ch; - } else if (ch == "U") { - var pairFudge = this.UEscape(str, j + 1, startline); - var j = pairFudge[0]; - var ch = pairFudge[1]; - var ustr = ustr + ch; - } else { - throw BadSyntax(this._thisDoc, this.lines, str, i, "bad escape"); - } - } - } - throw BadSyntax(this._thisDoc, this.lines, str, i, "unterminated string literal"); - }; - __SinkParser.prototype.uEscape = function (str, i, startline) { - var j = i; - var count = 0; - var value = 0; - while (count < 4) { - var chFudge = pyjslib_slice(str, j, j + 1); - var ch = chFudge.toLowerCase(); - var j = j + 1; - if (ch == "") { - throw BadSyntax(this._thisDoc, startline, str, i, "unterminated string literal(3)"); - } - var k = string_find("0123456789abcdef", ch); - if (k < 0) { - throw BadSyntax(this._thisDoc, startline, str, i, "bad string literal hex escape"); - } - var value = value * 16 + k; - var count = count + 1; - } - var uch = String.fromCharCode(value); - return new pyjslib_Tuple([j, uch]); - }; - __SinkParser.prototype.UEscape = function (str, i, startline) { - var j = i; - var count = 0; - var value = '\\U'; - while (count < 8) { - var chFudge = pyjslib_slice(str, j, j + 1); - var ch = chFudge.toLowerCase(); - var j = j + 1; - if (ch == "") { - throw BadSyntax(this._thisDoc, startline, str, i, "unterminated string literal(3)"); - } - var k = string_find("0123456789abcdef", ch); - if (k < 0) { - throw BadSyntax(this._thisDoc, startline, str, i, "bad string literal hex escape"); - } - var value = value + ch; - var count = count + 1; - } - var uch = stringFromCharCode("0x" + pyjslib_slice(value, 2, 10) - 0); - return new pyjslib_Tuple([j, uch]); - }; - function OLD_BadSyntax(uri, lines, str, i, why) { - return new __OLD_BadSyntax(uri, lines, str, i, why); - } - function __OLD_BadSyntax(uri, lines, str, i, why) { - this._str = str.encode("utf-8"); - this._str = str; - this._i = i; - this._why = why; - this.lines = lines; - this._uri = uri; - } - __OLD_BadSyntax.prototype.toString = function () { - var str = this._str; - var i = this._i; - var st = 0; - if (i > 60) { - var pre = "..."; - var st = i - 60; - } else { - var pre = ""; - } - if (pyjslib_len(str) - i > 60) { - var post = "..."; - } else { - var post = ""; - } - return "Line %i of <%s>: Bad syntax (%s) at ^ in:\n\"%s%s^%s%s\"" % new pyjslib_Tuple([this.lines + 1, this._uri, this._why, pre, pyjslib_slice(str, st, i), pyjslib_slice(str, i, i + 60), post]); - }; - function BadSyntax(uri, lines, str, i, why) { - return "Line " + (lines + 1) + " of <" + uri + ">: Bad syntax: " + why + "\nat: \"" + pyjslib_slice(str, i, i + 30) + "\""; - } - - function stripCR(str) { - var res = ""; - - var __ch = new pyjslib_Iterator(str); - try { - while (true) { - var ch = __ch.next(); - - if (ch != "\r") { - var res = res + ch; - } - } - } catch (e) { - if (e != StopIteration) { - throw e; - } - } - - return res; - } - - function dummyWrite(x) {} - - return SinkParser; -}(); - +'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; }; + +/** +* +* UTF-8 data encode / decode +* http://www.webtoolkit.info/ +* +**/ +var Uri = _dereq_('./uri'); +var ArrayIndexOf = _dereq_('./util').ArrayIndexOf; + +var N3Parser = function () { + + function hexify(str) { + // also used in parser + return encodeURI(str); + } + + var Utf8 = { + // public method for url encoding + encode: function encode(string) { + string = string.replace(/\r\n/g, "\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + + var c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } else if (c > 127 && c < 2048) { + utftext += String.fromCharCode(c >> 6 | 192); + utftext += String.fromCharCode(c & 63 | 128); + } else { + utftext += String.fromCharCode(c >> 12 | 224); + utftext += String.fromCharCode(c >> 6 & 63 | 128); + utftext += String.fromCharCode(c & 63 | 128); + } + } + + return utftext; + }, + // public method for url decoding + decode: function decode(utftext) { + var string = ""; + var i = 0; + + while (i < utftext.length) { + + var c = utftext.charCodeAt(i); + if (c < 128) { + string += String.fromCharCode(c); + i++; + } else if (c > 191 && c < 224) { + string += String.fromCharCode((c & 31) << 6 | utftext.charCodeAt(i + 1) & 63); + i += 2; + } else { + string += String.fromCharCode((c & 15) << 12 | (utftext.charCodeAt(i + 1) & 63) << 6 | utftext.charCodeAt(i + 2) & 63); + i += 3; + } + } + return string; + } + }; // Things we need to define to make converted pythn code work in js + // environment of $rdf + + var RDFSink_forSomeSym = "http://www.w3.org/2000/10/swap/log#forSome"; + var RDFSink_forAllSym = "http://www.w3.org/2000/10/swap/log#forAll"; + var Logic_NS = "http://www.w3.org/2000/10/swap/log#"; + + // pyjs seems to reference runtime library which I didn't find + + var pyjslib_Tuple = function pyjslib_Tuple(theList) { + return theList; + }; + + var pyjslib_List = function pyjslib_List(theList) { + return theList; + }; + + var pyjslib_Dict = function pyjslib_Dict(listOfPairs) { + if (listOfPairs.length > 0) throw "missing.js: oops nnonempty dict not imp"; + return []; + }; + + var pyjslib_len = function pyjslib_len(s) { + return s.length; + }; + + var pyjslib_slice = function pyjslib_slice(str, i, j) { + if (typeof str.slice == 'undefined') throw '@@ mising.js: No .slice function for ' + str + ' of type ' + (typeof str === 'undefined' ? 'undefined' : _typeof(str)); + if (typeof j == 'undefined' || j == null) return str.slice(i); + return str.slice(i, j); // @ exactly the same spec? + }; + var StopIteration = Error('dummy error stop iteration'); + + var pyjslib_Iterator = function pyjslib_Iterator(theList) { + this.last = 0; + this.li = theList; + this.next = function () { + if (this.last == this.li.length) throw StopIteration; + return this.li[this.last++]; + }; + return this; + }; + + var ord = function ord(str) { + return str.charCodeAt(0); + }; + + var string_find = function string_find(str, s) { + return str.indexOf(s); + }; + + var assertFudge = function assertFudge(condition, desc) { + if (condition) return; + if (desc) throw "python Assertion failed: " + desc; + throw "(python) Assertion failed."; + }; + + var stringFromCharCode = function stringFromCharCode(uesc) { + return String.fromCharCode(uesc); + }; + + String.prototype.encode = function (encoding) { + if (encoding != 'utf-8') throw "UTF8_converter: can only do utf-8"; + return Utf8.encode(this); + }; + String.prototype.decode = function (encoding) { + if (encoding != 'utf-8') throw "UTF8_converter: can only do utf-8"; + //return Utf8.decode(this); + return this; + }; + + var uripath_join = function uripath_join(base, given) { + return Uri.join(given, base); // sad but true + }; + + var becauseSubexpression = null; // No reason needed + var diag_tracking = 0; + var diag_chatty_flag = 0; + var diag_progress = function diag_progress(str) {} /*$rdf.log.debug(str);*/ + + // why_BecauseOfData = function(doc, reason) { return doc }; + + + ;var RDF_type_URI = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"; + var DAML_sameAs_URI = "http://www.w3.org/2002/07/owl#sameAs"; + + /* + function SyntaxError(details) { + return new __SyntaxError(details); + } + */ + + function __SyntaxError(details) { + this.details = details; + } + + /* + + $Id: n3parser.js 14561 2008-02-23 06:37:26Z kennyluck $ + + HAND EDITED FOR CONVERSION TO JAVASCRIPT + + This module implements a Nptation3 parser, and the final + part of a notation3 serializer. + + See also: + + Notation 3 + http://www.w3.org/DesignIssues/Notation3 + + Closed World Machine - and RDF Processor + http://www.w3.org/2000/10/swap/cwm + + To DO: See also "@@" in comments + + - Clean up interfaces + ______________________________________________ + + Module originally by Dan Connolly, includeing notation3 + parser and RDF generator. TimBL added RDF stream model + and N3 generation, replaced stream model with use + of common store/formula API. Yosi Scharf developped + the module, including tests and test harness. + + */ + + var ADDED_HASH = "#"; + var LOG_implies_URI = "http://www.w3.org/2000/10/swap/log#implies"; + var INTEGER_DATATYPE = "http://www.w3.org/2001/XMLSchema#integer"; + var FLOAT_DATATYPE = "http://www.w3.org/2001/XMLSchema#double"; + var DECIMAL_DATATYPE = "http://www.w3.org/2001/XMLSchema#decimal"; + var DATE_DATATYPE = "http://www.w3.org/2001/XMLSchema#date"; + var DATETIME_DATATYPE = "http://www.w3.org/2001/XMLSchema#dateTime"; + var BOOLEAN_DATATYPE = "http://www.w3.org/2001/XMLSchema#boolean"; + var option_noregen = 0; + var _notQNameChars = "\t\r\n !\"#$%&'()*.,+/;<=>?@[\\]^`{|}~"; + var _notNameChars = _notQNameChars + ":"; + var _rdfns = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; + var N3CommentCharacter = "#"; + var eol = new RegExp("^[ \\t]*(#[^\\n]*)?\\r?\\n", 'g'); + var eof = new RegExp("^[ \\t]*(#[^\\n]*)?$", 'g'); + var ws = new RegExp("^[ \\t]*", 'g'); + var signed_integer = new RegExp("^[-+]?[0-9]+", 'g'); + var number_syntax = new RegExp("^([-+]?[0-9]+)(\\.[0-9]+)?(e[-+]?[0-9]+)?", 'g'); + var datetime_syntax = new RegExp('^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9](T[0-9][0-9]:[0-9][0-9](:[0-9][0-9](\\.[0-9]*)?)?)?Z?'); + + var digitstring = new RegExp("^[0-9]+", 'g'); + var interesting = new RegExp("[\\\\\\r\\n\\\"]", 'g'); + var langcode = new RegExp("^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*", 'g'); + function SinkParser(store, openFormula, thisDoc, baseURI, genPrefix, metaURI, flags, why) { + return new __SinkParser(store, openFormula, thisDoc, baseURI, genPrefix, metaURI, flags, why); + } + function __SinkParser(store, openFormula, thisDoc, baseURI, genPrefix, metaURI, flags, why) { + if (typeof openFormula == 'undefined') openFormula = null; + if (typeof thisDoc == 'undefined') thisDoc = ""; + if (typeof baseURI == 'undefined') baseURI = null; + if (typeof genPrefix == 'undefined') genPrefix = ""; + if (typeof metaURI == 'undefined') metaURI = null; + if (typeof flags == 'undefined') flags = ""; + if (typeof why == 'undefined') why = null; + /* + note: namespace names should *not* end in #; + the # will get added during qname processing */ + + this._bindings = new pyjslib_Dict([]); + this._flags = flags; + if (thisDoc != "") { + assertFudge(thisDoc.indexOf(":") >= 0, "Document URI not absolute: " + thisDoc); + this._bindings[""] = thisDoc + "#"; + } + this._store = store; + if (genPrefix) { + store.setGenPrefix(genPrefix); + } + this._thisDoc = thisDoc; + this.source = store.sym(thisDoc); + this.lines = 0; + this.statementCount = 0; + this.startOfLine = 0; + this.previousLine = 0; + this._genPrefix = genPrefix; + this.keywords = new pyjslib_List(["a", "this", "bind", "has", "is", "of", "true", "false"]); + this.keywordsSet = 0; + this._anonymousNodes = new pyjslib_Dict([]); + this._variables = new pyjslib_Dict([]); + this._parentVariables = new pyjslib_Dict([]); + this._reason = why; + this._reason2 = null; + if (diag_tracking) { + this._reason2 = why_BecauseOfData(store.sym(thisDoc), this._reason); + } + if (baseURI) { + this._baseURI = baseURI; + } else { + if (thisDoc) { + this._baseURI = thisDoc; + } else { + this._baseURI = null; + } + } + assertFudge(!this._baseURI || this._baseURI.indexOf(":") >= 0); + if (!this._genPrefix) { + if (this._thisDoc) { + this._genPrefix = this._thisDoc + "#_g"; + } else { + this._genPrefix = RDFSink_uniqueURI(); + } + } + if (openFormula == null) { + if (this._thisDoc) { + this._formula = store.formula(thisDoc + "#_formula"); + } else { + this._formula = store.formula(); + } + } else { + this._formula = openFormula; + } + this._context = this._formula; + this._parentContext = null; + } + __SinkParser.prototype.here = function (i) { + return this._genPrefix + "_L" + this.lines + "C" + (i - this.startOfLine + 1); + }; + __SinkParser.prototype.formula = function () { + return this._formula; + }; + __SinkParser.prototype.loadStream = function (stream) { + return this.loadBuf(stream.read()); + }; + __SinkParser.prototype.loadBuf = function (buf) { + /* + Parses a buffer and returns its top level formula*/ + + this.startDoc(); + this.feed(buf); + return this.endDoc(); + }; + __SinkParser.prototype.feed = function (octets) { + /* + Feed an octet stream tothe parser + if BadSyntax is raised, the string + passed in the exception object is the + remainder after any statements have been parsed. + So if there is more data to feed to the + parser, it should be straightforward to recover.*/ + + var str = octets.decode("utf-8"); + var i = 0; + while (i >= 0) { + var j = this.skipSpace(str, i); + if (j < 0) { + return; + } + var i = this.directiveOrStatement(str, j); + if (i < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, j, "expected directive or statement"); + } + } + }; + __SinkParser.prototype.directiveOrStatement = function (str, h) { + var i = this.skipSpace(str, h); + if (i < 0) { + return i; + } + var j = this.directive(str, i); + if (j >= 0) { + return this.checkDot(str, j); + } + var j = this.statement(str, i); + if (j >= 0) { + return this.checkDot(str, j); + } + return j; + }; + __SinkParser.prototype.tok = function (tok, str, i) { + /* + Check for keyword. Space must have been stripped on entry and + we must not be at end of file.*/ + var whitespace = "\t\n\v\f\r "; + if (pyjslib_slice(str, i, i + 1) == "@") { + var i = i + 1; + } else { + if (ArrayIndexOf(this.keywords, tok) < 0) { + return -1; + } + } + var k = i + pyjslib_len(tok); + if (pyjslib_slice(str, i, k) == tok && _notQNameChars.indexOf(str.charAt(k)) >= 0) { + return k; + } else { + return -1; + } + }; + __SinkParser.prototype.directive = function (str, i) { + var j = this.skipSpace(str, i); + if (j < 0) { + return j; + } + var res = new pyjslib_List([]); + var j = this.tok("bind", str, i); + if (j > 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "keyword bind is obsolete: use @prefix"); + } + var j = this.tok("keywords", str, i); + if (j > 0) { + var i = this.commaSeparatedList(str, j, res, false); + if (i < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "'@keywords' needs comma separated list of words"); + } + this.setKeywords(pyjslib_slice(res, null, null)); + if (diag_chatty_flag > 80) { + diag_progress("Keywords ", this.keywords); + } + return i; + } + var j = this.tok("forAll", str, i); + if (j > 0) { + var i = this.commaSeparatedList(str, j, res, true); + if (i < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "Bad variable list after @forAll"); + } + + var __x = new pyjslib_Iterator(res); + try { + while (true) { + var x = __x.next(); + + if (ArrayIndexOf(this._variables, x) < 0 || ArrayIndexOf(this._parentVariables, x) >= 0) { + this._variables[x] = this._context.newUniversal(x); + } + } + } catch (e) { + if (e != StopIteration) { + throw e; + } + } + + return i; + } + var j = this.tok("forSome", str, i); + if (j > 0) { + var i = this.commaSeparatedList(str, j, res, this.uri_ref2); + if (i < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "Bad variable list after @forSome"); + } + + var __x = new pyjslib_Iterator(res); + try { + while (true) { + var x = __x.next(); + + this._context.declareExistential(x); + } + } catch (e) { + if (e != StopIteration) { + throw e; + } + } + + return i; + } + var j = this.tok("prefix", str, i); + if (j >= 0) { + var t = new pyjslib_List([]); + var i = this.qname(str, j, t); + if (i < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, j, "expected qname after @prefix"); + } + var j = this.uri_ref2(str, i, t); + if (j < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "expected after @prefix _qname_"); + } + var ns = t[1].uri; + if (this._baseURI) { + var ns = uripath_join(this._baseURI, ns); + } else { + assertFudge(ns.indexOf(":") >= 0, "With no base URI, cannot handle relative URI for NS"); + } + assertFudge(ns.indexOf(":") >= 0); + this._bindings[t[0][0]] = ns; + + this.bind(t[0][0], hexify(ns)); + return j; + } + var j = this.tok("base", str, i); + if (j >= 0) { + var t = new pyjslib_List([]); + var i = this.uri_ref2(str, j, t); + if (i < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, j, "expected after @base "); + } + var ns = t[0].uri; + if (this._baseURI) { + var ns = uripath_join(this._baseURI, ns); + } else { + throw BadSyntax(this._thisDoc, this.lines, str, j, "With no previous base URI, cannot use relative URI in @base <" + ns + ">"); + } + assertFudge(ns.indexOf(":") >= 0); + this._baseURI = ns; + return i; + } + return -1; + }; + __SinkParser.prototype.bind = function (qn, uri) { + if (qn == "") {} else { + this._store.setPrefixForURI(qn, uri); + } + }; + __SinkParser.prototype.setKeywords = function (k) { + /* + Takes a list of strings*/ + + if (k == null) { + this.keywordsSet = 0; + } else { + this.keywords = k; + this.keywordsSet = 1; + } + }; + __SinkParser.prototype.startDoc = function () {}; + __SinkParser.prototype.endDoc = function () { + /* + Signal end of document and stop parsing. returns formula*/ + + return this._formula; + }; + __SinkParser.prototype.makeStatement = function (quad) { + quad[0].add(quad[2], quad[1], quad[3], this.source); + this.statementCount += 1; + }; + __SinkParser.prototype.statement = function (str, i) { + var r = new pyjslib_List([]); + var i = this.object(str, i, r); + if (i < 0) { + return i; + } + var j = this.property_list(str, i, r[0]); + if (j < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "expected propertylist"); + } + return j; + }; + __SinkParser.prototype.subject = function (str, i, res) { + return this.item(str, i, res); + }; + __SinkParser.prototype.verb = function (str, i, res) { + /* + has _prop_ + is _prop_ of + a + = + _prop_ + >- prop -> + <- prop -< + _operator_*/ + + var j = this.skipSpace(str, i); + if (j < 0) { + return j; + } + var r = new pyjslib_List([]); + var j = this.tok("has", str, i); + if (j >= 0) { + var i = this.prop(str, j, r); + if (i < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, j, "expected property after 'has'"); + } + res.push(new pyjslib_Tuple(["->", r[0]])); + return i; + } + var j = this.tok("is", str, i); + if (j >= 0) { + var i = this.prop(str, j, r); + if (i < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, j, "expected after 'is'"); + } + var j = this.skipSpace(str, i); + if (j < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "End of file found, expected property after 'is'"); + return j; + } + var i = j; + var j = this.tok("of", str, i); + if (j < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "expected 'of' after 'is' "); + } + res.push(new pyjslib_Tuple(["<-", r[0]])); + return j; + } + var j = this.tok("a", str, i); + if (j >= 0) { + res.push(new pyjslib_Tuple(["->", this._store.sym(RDF_type_URI)])); + return j; + } + if (pyjslib_slice(str, i, i + 2) == "<=") { + res.push(new pyjslib_Tuple(["<-", this._store.sym(Logic_NS + "implies")])); + return i + 2; + } + if (pyjslib_slice(str, i, i + 1) == "=") { + if (pyjslib_slice(str, i + 1, i + 2) == ">") { + res.push(new pyjslib_Tuple(["->", this._store.sym(Logic_NS + "implies")])); + return i + 2; + } + res.push(new pyjslib_Tuple(["->", this._store.sym(DAML_sameAs_URI)])); + return i + 1; + } + if (pyjslib_slice(str, i, i + 2) == ":=") { + res.push(new pyjslib_Tuple(["->", Logic_NS + "becomes"])); + return i + 2; + } + var j = this.prop(str, i, r); + if (j >= 0) { + res.push(new pyjslib_Tuple(["->", r[0]])); + return j; + } + if (pyjslib_slice(str, i, i + 2) == ">-" || pyjslib_slice(str, i, i + 2) == "<-") { + throw BadSyntax(this._thisDoc, this.lines, str, j, ">- ... -> syntax is obsolete."); + } + return -1; + }; + __SinkParser.prototype.prop = function (str, i, res) { + return this.item(str, i, res); + }; + __SinkParser.prototype.item = function (str, i, res) { + return this.path(str, i, res); + }; + __SinkParser.prototype.blankNode = function (uri) { + return this._context.bnode(uri, this._reason2); + }; + __SinkParser.prototype.path = function (str, i, res) { + /* + Parse the path production. + */ + + var j = this.nodeOrLiteral(str, i, res); + if (j < 0) { + return j; + } + while ("!^.".indexOf(pyjslib_slice(str, j, j + 1)) >= 0) { + var ch = pyjslib_slice(str, j, j + 1); + if (ch == ".") { + var ahead = pyjslib_slice(str, j + 1, j + 2); + if (!ahead || _notNameChars.indexOf(ahead) >= 0 && ":?<[{(".indexOf(ahead) < 0) { + break; + } + } + var subj = res.pop(); + var obj = this.blankNode(this.here(j)); + var j = this.node(str, j + 1, res); + if (j < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, j, "EOF found in middle of path syntax"); + } + var pred = res.pop(); + if (ch == "^") { + this.makeStatement(new pyjslib_Tuple([this._context, pred, obj, subj])); + } else { + this.makeStatement(new pyjslib_Tuple([this._context, pred, subj, obj])); + } + res.push(obj); + } + return j; + }; + __SinkParser.prototype.anonymousNode = function (ln) { + /* + Remember or generate a term for one of these _: anonymous nodes*/ + + var term = this._anonymousNodes[ln]; + if (term) { + return term; + } + var term = this._store.bnode(this._context, this._reason2); + this._anonymousNodes[ln] = term; + return term; + }; + __SinkParser.prototype.node = function (str, i, res, subjectAlready) { + if (typeof subjectAlready == 'undefined') subjectAlready = null; + /* + Parse the production. + Space is now skipped once at the beginning + instead of in multipe calls to self.skipSpace(). + */ + + var subj = subjectAlready; + var j = this.skipSpace(str, i); + if (j < 0) { + return j; + } + var i = j; + var ch = pyjslib_slice(str, i, i + 1); + if (ch == "[") { + var bnodeID = this.here(i); + var j = this.skipSpace(str, i + 1); + if (j < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "EOF after '['"); + } + if (pyjslib_slice(str, j, j + 1) == "=") { + var i = j + 1; + var objs = new pyjslib_List([]); + var j = this.objectList(str, i, objs); + + if (j >= 0) { + var subj = objs[0]; + if (pyjslib_len(objs) > 1) { + + var __obj = new pyjslib_Iterator(objs); + try { + while (true) { + var obj = __obj.next(); + + this.makeStatement(new pyjslib_Tuple([this._context, this._store.sym(DAML_sameAs_URI), subj, obj])); + } + } catch (e) { + if (e != StopIteration) { + throw e; + } + } + } + var j = this.skipSpace(str, j); + if (j < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "EOF when objectList expected after [ = "); + } + if (pyjslib_slice(str, j, j + 1) == ";") { + var j = j + 1; + } + } else { + throw BadSyntax(this._thisDoc, this.lines, str, i, "objectList expected after [= "); + } + } + if (subj == null) { + var subj = this.blankNode(bnodeID); + } + var i = this.property_list(str, j, subj); + if (i < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, j, "property_list expected"); + } + var j = this.skipSpace(str, i); + if (j < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "EOF when ']' expected after [ "); + } + if (pyjslib_slice(str, j, j + 1) != "]") { + throw BadSyntax(this._thisDoc, this.lines, str, j, "']' expected"); + } + res.push(subj); + return j + 1; + } + if (ch == "{") { + var ch2 = pyjslib_slice(str, i + 1, i + 2); + if (ch2 == "$") { + i += 1; + var j = i + 1; + var mylist = new pyjslib_List([]); + var first_run = true; + while (1) { + var i = this.skipSpace(str, j); + if (i < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "needed '$}', found end."); + } + if (pyjslib_slice(str, i, i + 2) == "$}") { + var j = i + 2; + break; + } + if (!first_run) { + if (pyjslib_slice(str, i, i + 1) == ",") { + i += 1; + } else { + throw BadSyntax(this._thisDoc, this.lines, str, i, "expected: ','"); + } + } else { + var first_run = false; + } + var item = new pyjslib_List([]); + var j = this.item(str, i, item); + if (j < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "expected item in set or '$}'"); + } + mylist.push(item[0]); + } + res.push(this._store.newSet(mylist, this._context)); + return j; + } else { + var j = i + 1; + var oldParentContext = this._parentContext; + this._parentContext = this._context; + var parentAnonymousNodes = this._anonymousNodes; + var grandParentVariables = this._parentVariables; + this._parentVariables = this._variables; + this._anonymousNodes = new pyjslib_Dict([]); + this._variables = this._variables.slice(); + var reason2 = this._reason2; + this._reason2 = becauseSubexpression; + if (subj == null) { + var subj = this._store.formula(); + } + this._context = subj; + while (1) { + var i = this.skipSpace(str, j); + if (i < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "needed '}', found end."); + } + if (pyjslib_slice(str, i, i + 1) == "}") { + var j = i + 1; + break; + } + var j = this.directiveOrStatement(str, i); + if (j < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "expected statement or '}'"); + } + } + this._anonymousNodes = parentAnonymousNodes; + this._variables = this._parentVariables; + this._parentVariables = grandParentVariables; + this._context = this._parentContext; + this._reason2 = reason2; + this._parentContext = oldParentContext; + res.push(subj.close()); + return j; + } + } + if (ch == "(") { + var thing_type = this._store.list; + var ch2 = pyjslib_slice(str, i + 1, i + 2); + if (ch2 == "$") { + var thing_type = this._store.newSet; + i += 1; + } + var j = i + 1; + var mylist = new pyjslib_List([]); + while (1) { + var i = this.skipSpace(str, j); + if (i < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "needed ')', found end."); + } + if (pyjslib_slice(str, i, i + 1) == ")") { + var j = i + 1; + break; + } + var item = new pyjslib_List([]); + var j = this.item(str, i, item); + if (j < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "expected item in list or ')'"); + } + mylist.push(item[0]); + } + res.push(thing_type(mylist, this._context)); + return j; + } + var j = this.tok("this", str, i); + if (j >= 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "Keyword 'this' was ancient N3. Now use @forSome and @forAll keywords."); + res.push(this._context); + return j; + } + var j = this.tok("true", str, i); + if (j >= 0) { + res.push(true); + return j; + } + var j = this.tok("false", str, i); + if (j >= 0) { + res.push(false); + return j; + } + if (subj == null) { + var j = this.uri_ref2(str, i, res); + if (j >= 0) { + return j; + } + } + return -1; + }; + __SinkParser.prototype.property_list = function (str, i, subj) { + /* + Parse property list + Leaves the terminating punctuation in the buffer + */ + + while (1) { + var j = this.skipSpace(str, i); + if (j < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "EOF found when expected verb in property list"); + return j; + } + if (pyjslib_slice(str, j, j + 2) == ":-") { + var i = j + 2; + var res = new pyjslib_List([]); + var j = this.node(str, i, res, subj); + if (j < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "bad {} or () or [] node after :- "); + } + var i = j; + continue; + } + var i = j; + var v = new pyjslib_List([]); + var j = this.verb(str, i, v); + if (j <= 0) { + return i; + } + var objs = new pyjslib_List([]); + var i = this.objectList(str, j, objs); + if (i < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, j, "objectList expected"); + } + + var __obj = new pyjslib_Iterator(objs); + try { + while (true) { + var obj = __obj.next(); + + var pairFudge = v[0]; + var dir = pairFudge[0]; + var sym = pairFudge[1]; + if (dir == "->") { + this.makeStatement(new pyjslib_Tuple([this._context, sym, subj, obj])); + } else { + this.makeStatement(new pyjslib_Tuple([this._context, sym, obj, subj])); + } + } + } catch (e) { + if (e != StopIteration) { + throw e; + } + } + + var j = this.skipSpace(str, i); + if (j < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, j, "EOF found in list of objects"); + return j; + } + if (pyjslib_slice(str, i, i + 1) != ";") { + return i; + } + var i = i + 1; + } + }; + __SinkParser.prototype.commaSeparatedList = function (str, j, res, ofUris) { + /* + return value: -1 bad syntax; >1 new position in str + res has things found appended + Used to use a final value of the function to be called, e.g. this.bareWord + but passing the function didn't work fo js converion pyjs + */ + + var i = this.skipSpace(str, j); + if (i < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "EOF found expecting comma sep list"); + return i; + } + if (str.charAt(i) == ".") { + return j; + } + if (ofUris) { + var i = this.uri_ref2(str, i, res); + } else { + var i = this.bareWord(str, i, res); + } + if (i < 0) { + return -1; + } + while (1) { + var j = this.skipSpace(str, i); + if (j < 0) { + return j; + } + var ch = pyjslib_slice(str, j, j + 1); + if (ch != ",") { + if (ch != ".") { + return -1; + } + return j; + } + if (ofUris) { + var i = this.uri_ref2(str, j + 1, res); + } else { + var i = this.bareWord(str, j + 1, res); + } + if (i < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "bad list content"); + return i; + } + } + }; + __SinkParser.prototype.objectList = function (str, i, res) { + var i = this.object(str, i, res); + if (i < 0) { + return -1; + } + while (1) { + var j = this.skipSpace(str, i); + if (j < 0) { + throw BadSyntax(this._thisDoc, this.lines, str, j, "EOF found after object"); + return j; + } + if (pyjslib_slice(str, j, j + 1) != ",") { + return j; + } + var i = this.object(str, j + 1, res); + if (i < 0) { + return i; + } + } + }; + __SinkParser.prototype.checkDot = function (str, i) { + var j = this.skipSpace(str, i); + if (j < 0) { + return j; + } + if (pyjslib_slice(str, j, j + 1) == ".") { + return j + 1; + } + if (pyjslib_slice(str, j, j + 1) == "}") { + return j; + } + if (pyjslib_slice(str, j, j + 1) == "]") { + return j; + } + throw BadSyntax(this._thisDoc, this.lines, str, j, "expected '.' or '}' or ']' at end of statement"); + return i; + }; + __SinkParser.prototype.uri_ref2 = function (str, i, res) { + /* + Generate uri from n3 representation. + Note that the RDF convention of directly concatenating + NS and local name is now used though I prefer inserting a '#' + to make the namesapces look more like what XML folks expect. + */ + + var qn = new pyjslib_List([]); + var j = this.qname(str, i, qn); + if (j >= 0) { + var pairFudge = qn[0]; + var pfx = pairFudge[0]; + var ln = pairFudge[1]; + if (pfx == null) { + assertFudge(0, "not used?"); + var ns = this._baseURI + ADDED_HASH; + } else { + var ns = this._bindings[pfx]; + if (!ns) { + if (pfx == "_") { + res.push(this.anonymousNode(ln)); + return j; + } + throw BadSyntax(this._thisDoc, this.lines, str, i, "Prefix " + pfx + " not bound."); + } + } + var symb = this._store.sym(ns + ln); + if (ArrayIndexOf(this._variables, symb) >= 0) { + res.push(this._variables[symb]); + } else { + res.push(symb); + } + return j; + } + var i = this.skipSpace(str, i); + if (i < 0) { + return -1; + } + if (str.charAt(i) == "?") { + var v = new pyjslib_List([]); + var j = this.variable(str, i, v); + if (j > 0) { + res.push(v[0]); + return j; + } + return -1; + } else if (str.charAt(i) == "<") { + var i = i + 1; + var st = i; + while (i < pyjslib_len(str)) { + if (str.charAt(i) == ">") { + var uref = pyjslib_slice(str, st, i); + if (this._baseURI) { + var uref = uripath_join(this._baseURI, uref); + } else { + assertFudge(uref.indexOf(":") >= 0, "With no base URI, cannot deal with relative URIs"); + } + if (pyjslib_slice(str, i - 1, i) == "#" && !(pyjslib_slice(uref, -1, null) == "#")) { + var uref = uref + "#"; + } + var symb = this._store.sym(uref); + if (ArrayIndexOf(this._variables, symb) >= 0) { + res.push(this._variables[symb]); + } else { + res.push(symb); + } + return i + 1; + } + var i = i + 1; + } + throw BadSyntax(this._thisDoc, this.lines, str, j, "unterminated URI reference"); + } else if (this.keywordsSet) { + var v = new pyjslib_List([]); + var j = this.bareWord(str, i, v); + if (j < 0) { + return -1; + } + if (ArrayIndexOf(this.keywords, v[0]) >= 0) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "Keyword \"" + v[0] + "\" not allowed here."); + } + res.push(this._store.sym(this._bindings[""] + v[0])); + return j; + } else { + return -1; + } + }; + __SinkParser.prototype.skipSpace = function (str, i) { + /* + Skip white space, newlines and comments. + return -1 if EOF, else position of first non-ws character*/ + + var whitespace = ' \n\r\t\f\x0B\xA0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u200B\u2028\u2029\u3000'; + for (var j = i ? i : 0; j < str.length; j++) { + var ch = str.charAt(j); + // console.log(" skipspace j= "+j + " i= " + i + " n= " + str.length); + // console.log(" skipspace ch <" + ch + ">"); + if (whitespace.indexOf(ch) < 0) { + //not ws + // console.log(" skipspace 2 ch <" + ch + ">"); + if (str.charAt(j) === '#') { + for (;; j++) { + // console.log(" skipspace2 j= "+j + " i= " + i + " n= " + str.length); + if (j === str.length) { + return -1; // EOF + } + if (str.charAt(j) === '\n') { + this.lines = this.lines + 1; + break; + } + }; + } else { + // Not hash - something interesting + // console.log(" skipspace 3 ch <" + ch + ">"); + return j; + } + } else { + // Whitespace + // console.log(" skipspace 5 ch <" + ch + ">"); + if (str.charAt(j) === '\n') { + this.lines = this.lines + 1; + } + } + } // next j + return -1; // EOF + }; + + __SinkParser.prototype.variable = function (str, i, res) { + /* + ?abc -> variable(:abc) + */ + + var j = this.skipSpace(str, i); + if (j < 0) { + return -1; + } + if (pyjslib_slice(str, j, j + 1) != "?") { + return -1; + } + var j = j + 1; + var i = j; + if ("0123456789-".indexOf(str.charAt(j)) >= 0) { + throw BadSyntax(this._thisDoc, this.lines, str, j, "Varible name can't start with '" + str.charAt(j) + "s'"); + return -1; + } + while (i < pyjslib_len(str) && _notNameChars.indexOf(str.charAt(i)) < 0) { + var i = i + 1; + } + if (this._parentContext == null) { + throw BadSyntax(this._thisDoc, this.lines, str, j, "Can't use ?xxx syntax for variable in outermost level: " + pyjslib_slice(str, j - 1, i)); + } + res.push(this._store.variable(pyjslib_slice(str, j, i))); + return i; + }; + __SinkParser.prototype.bareWord = function (str, i, res) { + /* + abc -> :abc + */ + + var j = this.skipSpace(str, i); + if (j < 0) { + return -1; + } + var ch = str.charAt(j); + if ("0123456789-".indexOf(ch) >= 0) { + return -1; + } + if (_notNameChars.indexOf(ch) >= 0) { + return -1; + } + var i = j; + while (i < pyjslib_len(str) && _notNameChars.indexOf(str.charAt(i)) < 0) { + var i = i + 1; + } + res.push(pyjslib_slice(str, j, i)); + return i; + }; + __SinkParser.prototype.qname = function (str, i, res) { + /* + xyz:def -> ('xyz', 'def') + If not in keywords and keywordsSet: def -> ('', 'def') + :def -> ('', 'def') + */ + + var i = this.skipSpace(str, i); + if (i < 0) { + return -1; + } + var c = str.charAt(i); + if ("0123456789-+".indexOf(c) >= 0) { + return -1; + } + if (_notNameChars.indexOf(c) < 0) { + var ln = c; + var i = i + 1; + while (i < pyjslib_len(str)) { + var c = str.charAt(i); + if (_notNameChars.indexOf(c) < 0) { + var ln = ln + c; + var i = i + 1; + } else { + break; + } + } + } else { + var ln = ""; + } + if (i < pyjslib_len(str) && str.charAt(i) == ":") { + var pfx = ln; + var i = i + 1; + var ln = ""; + while (i < pyjslib_len(str)) { + var c = str.charAt(i); + if (_notNameChars.indexOf(c) < 0) { + var ln = ln + c; + var i = i + 1; + } else { + break; + } + } + res.push(new pyjslib_Tuple([pfx, ln])); + return i; + } else { + if (ln && this.keywordsSet && ArrayIndexOf(this.keywords, ln) < 0) { + res.push(new pyjslib_Tuple(["", ln])); + return i; + } + return -1; + } + }; + __SinkParser.prototype.object = function (str, i, res) { + var j = this.subject(str, i, res); + if (j >= 0) { + return j; + } else { + var j = this.skipSpace(str, i); + if (j < 0) { + return -1; + } else { + var i = j; + } + if (str.charAt(i) == "\"") { + if (pyjslib_slice(str, i, i + 3) == "\"\"\"") { + var delim = "\"\"\""; + } else { + var delim = "\""; + } + var i = i + pyjslib_len(delim); + var pairFudge = this.strconst(str, i, delim); + var j = pairFudge[0]; + var s = pairFudge[1]; + res.push(this._store.literal(s)); + diag_progress("New string const ", s, j); + return j; + } else { + return -1; + } + } + }; + __SinkParser.prototype.nodeOrLiteral = function (str, i, res) { + var j = this.node(str, i, res); + if (j >= 0) { + return j; + } else { + var j = this.skipSpace(str, i); + if (j < 0) { + return -1; + } else { + var i = j; + } + var ch = str.charAt(i); + if ("-+0987654321".indexOf(ch) >= 0) { + + datetime_syntax.lastIndex = 0; + var m = datetime_syntax.exec(str.slice(i)); + if (m != null) { + // j = ( i + datetime_syntax.lastIndex ) ; + var val = m[0]; + j = i + val.length; + if (val.indexOf("T") >= 0) { + res.push(this._store.literal(val, undefined, this._store.sym(DATETIME_DATATYPE))); + } else { + res.push(this._store.literal(val, undefined, this._store.sym(DATE_DATATYPE))); + } + } else { + number_syntax.lastIndex = 0; + var m = number_syntax.exec(str.slice(i)); + if (m == null) { + throw BadSyntax(this._thisDoc, this.lines, str, i, "Bad number or date syntax"); + } + j = i + number_syntax.lastIndex; + var val = pyjslib_slice(str, i, j); + if (val.indexOf("e") >= 0) { + res.push(this._store.literal(parseFloat(val), undefined, this._store.sym(FLOAT_DATATYPE))); + } else if (pyjslib_slice(str, i, j).indexOf(".") >= 0) { + res.push(this._store.literal(parseFloat(val), undefined, this._store.sym(DECIMAL_DATATYPE))); + } else { + res.push(this._store.literal(parseInt(val), undefined, this._store.sym(INTEGER_DATATYPE))); + } + }; + return j; // Where we have got up to + } + if (str.charAt(i) == "\"") { + if (pyjslib_slice(str, i, i + 3) == "\"\"\"") { + var delim = "\"\"\""; + } else { + var delim = "\""; + } + var i = i + pyjslib_len(delim); + var dt = null; + var pairFudge = this.strconst(str, i, delim); + var j = pairFudge[0]; + var s = pairFudge[1]; + var lang = null; + if (pyjslib_slice(str, j, j + 1) == "@") { + langcode.lastIndex = 0; + + var m = langcode.exec(str.slice(j + 1)); + if (m == null) { + throw BadSyntax(this._thisDoc, startline, str, i, "Bad language code syntax on string literal, after @"); + } + var i = langcode.lastIndex + j + 1; + + var lang = pyjslib_slice(str, j + 1, i); + var j = i; + } + if (pyjslib_slice(str, j, j + 2) == "^^") { + var res2 = new pyjslib_List([]); + var j = this.uri_ref2(str, j + 2, res2); + var dt = res2[0]; + } + res.push(this._store.literal(s, lang, dt)); + return j; + } else { + return -1; + } + } + }; + __SinkParser.prototype.strconst = function (str, i, delim) { + /* + parse an N3 string constant delimited by delim. + return index, val + */ + + var j = i; + var ustr = ""; + var startline = this.lines; + while (j < pyjslib_len(str)) { + var i = j + pyjslib_len(delim); + if (pyjslib_slice(str, j, i) == delim) { + return new pyjslib_Tuple([i, ustr]); + } + if (str.charAt(j) == "\"") { + var ustr = ustr + "\""; + var j = j + 1; + continue; + } + interesting.lastIndex = 0; + var m = interesting.exec(str.slice(j)); + if (!m) { + throw BadSyntax(this._thisDoc, startline, str, j, "Closing quote missing in string at ^ in " + pyjslib_slice(str, j - 20, j) + "^" + pyjslib_slice(str, j, j + 20)); + } + var i = j + interesting.lastIndex - 1; + var ustr = ustr + pyjslib_slice(str, j, i); + var ch = str.charAt(i); + if (ch == "\"") { + var j = i; + continue; + } else if (ch == "\r") { + var j = i + 1; + continue; + } else if (ch == "\n") { + if (delim == "\"") { + throw BadSyntax(this._thisDoc, startline, str, i, "newline found in string literal"); + } + this.lines = this.lines + 1; + var ustr = ustr + ch; + var j = i + 1; + this.previousLine = this.startOfLine; + this.startOfLine = j; + } else if (ch == "\\") { + var j = i + 1; + var ch = pyjslib_slice(str, j, j + 1); + if (!ch) { + throw BadSyntax(this._thisDoc, startline, str, i, "unterminated string literal (2)"); + } + var k = string_find("abfrtvn\\\"", ch); + if (k >= 0) { + var uch = "\a\b\f\r\t\v\n\\\"".charAt(k); + var ustr = ustr + uch; + var j = j + 1; + } else if (ch == "u") { + var pairFudge = this.uEscape(str, j + 1, startline); + var j = pairFudge[0]; + var ch = pairFudge[1]; + var ustr = ustr + ch; + } else if (ch == "U") { + var pairFudge = this.UEscape(str, j + 1, startline); + var j = pairFudge[0]; + var ch = pairFudge[1]; + var ustr = ustr + ch; + } else { + throw BadSyntax(this._thisDoc, this.lines, str, i, "bad escape"); + } + } + } + throw BadSyntax(this._thisDoc, this.lines, str, i, "unterminated string literal"); + }; + __SinkParser.prototype.uEscape = function (str, i, startline) { + var j = i; + var count = 0; + var value = 0; + while (count < 4) { + var chFudge = pyjslib_slice(str, j, j + 1); + var ch = chFudge.toLowerCase(); + var j = j + 1; + if (ch == "") { + throw BadSyntax(this._thisDoc, startline, str, i, "unterminated string literal(3)"); + } + var k = string_find("0123456789abcdef", ch); + if (k < 0) { + throw BadSyntax(this._thisDoc, startline, str, i, "bad string literal hex escape"); + } + var value = value * 16 + k; + var count = count + 1; + } + var uch = String.fromCharCode(value); + return new pyjslib_Tuple([j, uch]); + }; + __SinkParser.prototype.UEscape = function (str, i, startline) { + var j = i; + var count = 0; + var value = '\\U'; + while (count < 8) { + var chFudge = pyjslib_slice(str, j, j + 1); + var ch = chFudge.toLowerCase(); + var j = j + 1; + if (ch == "") { + throw BadSyntax(this._thisDoc, startline, str, i, "unterminated string literal(3)"); + } + var k = string_find("0123456789abcdef", ch); + if (k < 0) { + throw BadSyntax(this._thisDoc, startline, str, i, "bad string literal hex escape"); + } + var value = value + ch; + var count = count + 1; + } + var uch = stringFromCharCode("0x" + pyjslib_slice(value, 2, 10) - 0); + return new pyjslib_Tuple([j, uch]); + }; + function OLD_BadSyntax(uri, lines, str, i, why) { + return new __OLD_BadSyntax(uri, lines, str, i, why); + } + function __OLD_BadSyntax(uri, lines, str, i, why) { + this._str = str.encode("utf-8"); + this._str = str; + this._i = i; + this._why = why; + this.lines = lines; + this._uri = uri; + } + __OLD_BadSyntax.prototype.toString = function () { + var str = this._str; + var i = this._i; + var st = 0; + if (i > 60) { + var pre = "..."; + var st = i - 60; + } else { + var pre = ""; + } + if (pyjslib_len(str) - i > 60) { + var post = "..."; + } else { + var post = ""; + } + return "Line %i of <%s>: Bad syntax (%s) at ^ in:\n\"%s%s^%s%s\"" % new pyjslib_Tuple([this.lines + 1, this._uri, this._why, pre, pyjslib_slice(str, st, i), pyjslib_slice(str, i, i + 60), post]); + }; + function BadSyntax(uri, lines, str, i, why) { + return "Line " + (lines + 1) + " of <" + uri + ">: Bad syntax: " + why + "\nat: \"" + pyjslib_slice(str, i, i + 30) + "\""; + } + + function stripCR(str) { + var res = ""; + + var __ch = new pyjslib_Iterator(str); + try { + while (true) { + var ch = __ch.next(); + + if (ch != "\r") { + var res = res + ch; + } + } + } catch (e) { + if (e != StopIteration) { + throw e; + } + } + + return res; + } + + function dummyWrite(x) {} + + return SinkParser; +}(); + module.exports = N3Parser; },{"./uri":81,"./util":82}],66:[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'); - -/** - * @class NamedNode - * @extends Node - */ - -var NamedNode = function (_Node) { - _inherits(NamedNode, _Node); - - /** - * @constructor - * @param iri {String} - */ - function NamedNode(iri) { - _classCallCheck(this, NamedNode); - - var _this = _possibleConstructorReturn(this, (NamedNode.__proto__ || Object.getPrototypeOf(NamedNode)).call(this)); - - _this.termType = NamedNode.termType; - if (!iri.includes(':')) { - throw new Error('NamedNode IRI "' + iri + '" must be absolute.'); - } - if (iri.includes(' ')) { - var message = 'Error: NamedNode IRI "' + iri + '" must not contain unencoded spaces.'; - throw new Error(message); - } - _this.value = iri; - return _this; - } - /** - * Returns an $rdf node for the containing directory, ending in slash. - */ - - - _createClass(NamedNode, [{ - key: 'dir', - value: function dir() { - var str = this.uri.split('#')[0]; - var p = str.slice(0, -1).lastIndexOf('/'); - var q = str.indexOf('//'); - if (q >= 0 && p < q + 2 || p < 0) return null; - return new NamedNode(str.slice(0, p + 1)); - } - /** - * Returns an NN for the whole web site, ending in slash. - * Contrast with the "origin" which does NOT have a trailing slash - */ - - }, { - key: 'site', - value: function site() { - var str = this.uri.split('#')[0]; - var p = str.indexOf('//'); - if (p < 0) throw new Error('This URI does not have a web site part (origin)'); - var q = str.indexOf('/', p + 2); - if (q < 0) throw new Error('This URI does not have a web site part. (origin)'); - return new NamedNode(str.slice(0, q + 1)); - } - }, { - key: 'doc', - value: function doc() { - if (this.uri.indexOf('#') < 0) { - return this; - } else { - return new NamedNode(this.uri.split('#')[0]); - } - } - }, { - key: 'toString', - value: function toString() { - return '<' + this.uri + '>'; - } - - /** - * Legacy getter and setter alias, node.uri - */ - - }, { - key: 'uri', - get: function get() { - return this.value; - }, - set: function set(uri) { - this.value = uri; - } - }], [{ - key: 'fromValue', - value: function fromValue(value) { - if (typeof value === 'undefined' || value === null) { - return value; - } - var isNode = value && value.termType; - if (isNode) { - return value; - } - return new NamedNode(value); - } - }]); - - return NamedNode; -}(Node); - -NamedNode.termType = 'NamedNode'; -NamedNode.prototype.classOrder = ClassOrder['NamedNode']; -NamedNode.prototype.isVar = 0; - +'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'); + +/** + * @class NamedNode + * @extends Node + */ + +var NamedNode = function (_Node) { + _inherits(NamedNode, _Node); + + /** + * @constructor + * @param iri {String} + */ + function NamedNode(iri) { + _classCallCheck(this, NamedNode); + + var _this = _possibleConstructorReturn(this, (NamedNode.__proto__ || Object.getPrototypeOf(NamedNode)).call(this)); + + _this.termType = NamedNode.termType; + if (!iri.includes(':')) { + throw new Error('NamedNode IRI "' + iri + '" must be absolute.'); + } + if (iri.includes(' ')) { + var message = 'Error: NamedNode IRI "' + iri + '" must not contain unencoded spaces.'; + throw new Error(message); + } + _this.value = iri; + return _this; + } + /** + * Returns an $rdf node for the containing directory, ending in slash. + */ + + + _createClass(NamedNode, [{ + key: 'dir', + value: function dir() { + var str = this.uri.split('#')[0]; + var p = str.slice(0, -1).lastIndexOf('/'); + var q = str.indexOf('//'); + if (q >= 0 && p < q + 2 || p < 0) return null; + return new NamedNode(str.slice(0, p + 1)); + } + /** + * Returns an NN for the whole web site, ending in slash. + * Contrast with the "origin" which does NOT have a trailing slash + */ + + }, { + key: 'site', + value: function site() { + var str = this.uri.split('#')[0]; + var p = str.indexOf('//'); + if (p < 0) throw new Error('This URI does not have a web site part (origin)'); + var q = str.indexOf('/', p + 2); + if (q < 0) throw new Error('This URI does not have a web site part. (origin)'); + return new NamedNode(str.slice(0, q + 1)); + } + }, { + key: 'doc', + value: function doc() { + if (this.uri.indexOf('#') < 0) { + return this; + } else { + return new NamedNode(this.uri.split('#')[0]); + } + } + }, { + key: 'toString', + value: function toString() { + return '<' + this.uri + '>'; + } + + /** + * Legacy getter and setter alias, node.uri + */ + + }, { + key: 'uri', + get: function get() { + return this.value; + }, + set: function set(uri) { + this.value = uri; + } + }], [{ + key: 'fromValue', + value: function fromValue(value) { + if (typeof value === 'undefined' || value === null) { + return value; + } + var isNode = value && value.termType; + if (isNode) { + return value; + } + return new NamedNode(value); + } + }]); + + return NamedNode; +}(Node); + +NamedNode.termType = 'NamedNode'; +NamedNode.prototype.classOrder = ClassOrder['NamedNode']; +NamedNode.prototype.isVar = 0; + module.exports = NamedNode; },{"./class-order":52,"./node":68}],67:[function(_dereq_,module,exports){ -'use strict'; - -var NamedNode = _dereq_('./named-node'); - -function Namespace(nsuri) { - return function (ln) { - return new NamedNode(nsuri + (ln || '')); - }; -} - +'use strict'; + +var NamedNode = _dereq_('./named-node'); + +function Namespace(nsuri) { + return function (ln) { + return new NamedNode(nsuri + (ln || '')); + }; +} + module.exports = Namespace; },{"./named-node":66}],68:[function(_dereq_,module,exports){ -'use strict'; -/** - * The superclass of all RDF Statement objects, that is - * NamedNode, Literal, BlankNode, etc. - * @class Node - */ - -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"); } } - -var Node = function () { - function Node() { - _classCallCheck(this, Node); - } - - _createClass(Node, [{ - key: 'substitute', - value: function substitute(bindings) { - console.log('@@@ node substitute' + this); - return this; - } - }, { - key: 'compareTerm', - value: function compareTerm(other) { - if (this.classOrder < other.classOrder) { - return -1; - } - if (this.classOrder > other.classOrder) { - return +1; - } - if (this.value < other.value) { - return -1; - } - if (this.value > other.value) { - return +1; - } - return 0; - } - }, { - key: 'equals', - value: function equals(other) { - if (!other) { - return false; - } - return this.termType === other.termType && this.value === other.value; - } - }, { - key: 'hashString', - value: function hashString() { - return this.toCanonical(); - } - }, { - key: 'sameTerm', - value: function sameTerm(other) { - return this.equals(other); - } - }, { - key: 'toCanonical', - value: function toCanonical() { - return this.toNT(); - } - }, { - key: 'toNT', - value: function toNT() { - return this.toString(); - } - }, { - key: 'toString', - value: function toString() { - throw new Error('Node.toString() is abstract - see the subclasses instead'); - } - }]); - - return Node; -}(); - -module.exports = Node; - -/** - * Creates an RDF Node from a native javascript value. - * RDF Nodes are returned unchanged, undefined returned as itself. - * @method fromValue - * @static - * @param value {Node|Date|String|Number|Boolean|Undefined} - * @return {Node|Collection} - */ -Node.fromValue = function fromValue(value) { - var Collection = _dereq_('./collection'); - var Literal = _dereq_('./literal'); - var NamedNode = _dereq_('./named-node'); - if (typeof value === 'undefined' || value === null) { - return value; - } - var isNode = value && value.termType; - if (isNode) { - // a Node subclass or a Collection - return value; - } - if (Array.isArray(value)) { - return new Collection(value); - } - return Literal.fromValue(value); +'use strict'; +/** + * The superclass of all RDF Statement objects, that is + * NamedNode, Literal, BlankNode, etc. + * @class Node + */ + +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"); } } + +var Node = function () { + function Node() { + _classCallCheck(this, Node); + } + + _createClass(Node, [{ + key: 'substitute', + value: function substitute(bindings) { + console.log('@@@ node substitute' + this); + return this; + } + }, { + key: 'compareTerm', + value: function compareTerm(other) { + if (this.classOrder < other.classOrder) { + return -1; + } + if (this.classOrder > other.classOrder) { + return +1; + } + if (this.value < other.value) { + return -1; + } + if (this.value > other.value) { + return +1; + } + return 0; + } + }, { + key: 'equals', + value: function equals(other) { + if (!other) { + return false; + } + return this.termType === other.termType && this.value === other.value; + } + }, { + key: 'hashString', + value: function hashString() { + return this.toCanonical(); + } + }, { + key: 'sameTerm', + value: function sameTerm(other) { + return this.equals(other); + } + }, { + key: 'toCanonical', + value: function toCanonical() { + return this.toNT(); + } + }, { + key: 'toNT', + value: function toNT() { + return this.toString(); + } + }, { + key: 'toString', + value: function toString() { + throw new Error('Node.toString() is abstract - see the subclasses instead'); + } + }]); + + return Node; +}(); + +module.exports = Node; + +/** + * Creates an RDF Node from a native javascript value. + * RDF Nodes are returned unchanged, undefined returned as itself. + * @method fromValue + * @static + * @param value {Node|Date|String|Number|Boolean|Undefined} + * @return {Node|Collection} + */ +Node.fromValue = function fromValue(value) { + var Collection = _dereq_('./collection'); + var Literal = _dereq_('./literal'); + var NamedNode = _dereq_('./named-node'); + if (typeof value === 'undefined' || value === null) { + return value; + } + var isNode = value && value.termType; + if (isNode) { + // a Node subclass or a Collection + return value; + } + if (Array.isArray(value)) { + return new Collection(value); + } + return Literal.fromValue(value); }; },{"./collection":53,"./literal":63,"./named-node":66}],69:[function(_dereq_,module,exports){ -'use strict'; - -module.exports = parse; - -var BlankNode = _dereq_('./blank-node'); -var jsonld = _dereq_('jsonld'); -var Literal = _dereq_('./literal'); -var N3 = _dereq_('n3'); // @@ Goal: remove this dependency -var N3Parser = _dereq_('./n3parser'); -var NamedNode = _dereq_('./named-node'); -var parseRDFaDOM = _dereq_('./rdfaparser').parseRDFaDOM; -var RDFParser = _dereq_('./rdfxmlparser'); -var sparqlUpdateParser = _dereq_('./patch-parser'); -var Util = _dereq_('./util'); - -/** - * Parse a string and put the result into the graph kb. - * Normal method is sync. - * Unfortunately jsdonld is currently written to need to be called async. - * Hence the mess below with executeCallback. - */ -function parse(str, kb, base, contentType, callback) { - contentType = contentType || 'text/turtle'; - try { - if (contentType === 'text/n3' || contentType === 'text/turtle') { - var p = N3Parser(kb, kb, base, base, null, null, '', null); - p.loadBuf(str); - executeCallback(); - } else if (contentType === 'application/rdf+xml') { - var parser = new RDFParser(kb); - parser.parse(Util.parseXML(str), base, kb.sym(base)); - executeCallback(); - } else if (contentType === 'application/xhtml+xml') { - parseRDFaDOM(Util.parseXML(str, { contentType: 'application/xhtml+xml' }), kb, base); - executeCallback(); - } else if (contentType === 'text/html') { - parseRDFaDOM(Util.parseXML(str, { contentType: 'text/html' }), kb, base); - executeCallback(); - } else if (contentType === 'application/sparql-update') { - // @@ we handle a subset - sparqlUpdateParser(str, kb, base); - executeCallback(); - } else if (contentType === 'application/ld+json' || contentType === 'application/nquads' || contentType === 'application/n-quads') { - var n3Parser = N3.Parser(); - var triples = []; - if (contentType === 'application/ld+json') { - var jsonDocument; - try { - jsonDocument = JSON.parse(str); - } catch (parseErr) { - callback(parseErr, null); - } - jsonld.toRDF(jsonDocument, { format: 'application/nquads' }, nquadCallback); - } else { - nquadCallback(null, str); - } - } else { - throw new Error("Don't know how to parse " + contentType + ' yet'); - } - } catch (e) { - executeErrorCallback(e); - } - - function executeCallback() { - if (callback) { - callback(null, kb); - } else { - return; - } - } - - function executeErrorCallback(e) { - if (contentType !== 'application/ld+json' || contentType !== 'application/nquads' || contentType !== 'application/n-quads') { - if (callback) { - callback(e, kb); - } else { - throw new Error('Error trying to parse <' + base + '> as ' + contentType + ':\n' + e + ':\n' + e.stack); - } - } - } - /* - function setJsonLdBase (doc, base) { - if (doc instanceof Array) { - return - } - if (!('@context' in doc)) { - doc['@context'] = {} - } - doc['@context']['@base'] = base - } - */ - function nquadCallback(err, nquads) { - if (err) { - callback(err, kb); - } - try { - n3Parser.parse(nquads, tripleCallback); - } catch (err) { - callback(err, kb); - } - } - - function tripleCallback(err, triple, prefixes) { - if (err) { - callback(err, kb); - } - if (triple) { - triples.push(triple); - } else { - for (var i = 0; i < triples.length; i++) { - addTriple(kb, triples[i]); - } - callback(null, kb); - } - } - - function addTriple(kb, triple) { - var subject = createTerm(triple.subject); - var predicate = createTerm(triple.predicate); - var object = createTerm(triple.object); - var why = null; - if (triple.graph) { - why = createTerm(triple.graph); - } - kb.add(subject, predicate, object, why); - } - - function createTerm(termString) { - var value; - if (N3.Util.isLiteral(termString)) { - value = N3.Util.getLiteralValue(termString); - var language = N3.Util.getLiteralLanguage(termString); - var datatype = new NamedNode(N3.Util.getLiteralType(termString)); - return new Literal(value, language, datatype); - } else if (N3.Util.isIRI(termString)) { - return new NamedNode(termString); - } else if (N3.Util.isBlank(termString)) { - value = termString.substring(2, termString.length); - return new BlankNode(value); - } else { - return null; - } - } +'use strict'; + +module.exports = parse; + +var BlankNode = _dereq_('./blank-node'); +var jsonld = _dereq_('jsonld'); +var Literal = _dereq_('./literal'); +var N3 = _dereq_('n3'); // @@ Goal: remove this dependency +var N3Parser = _dereq_('./n3parser'); +var NamedNode = _dereq_('./named-node'); +var parseRDFaDOM = _dereq_('./rdfaparser').parseRDFaDOM; +var RDFParser = _dereq_('./rdfxmlparser'); +var sparqlUpdateParser = _dereq_('./patch-parser'); +var Util = _dereq_('./util'); + +/** + * Parse a string and put the result into the graph kb. + * Normal method is sync. + * Unfortunately jsdonld is currently written to need to be called async. + * Hence the mess below with executeCallback. + */ +function parse(str, kb, base, contentType, callback) { + contentType = contentType || 'text/turtle'; + try { + if (contentType === 'text/n3' || contentType === 'text/turtle') { + var p = N3Parser(kb, kb, base, base, null, null, '', null); + p.loadBuf(str); + executeCallback(); + } else if (contentType === 'application/rdf+xml') { + var parser = new RDFParser(kb); + parser.parse(Util.parseXML(str), base, kb.sym(base)); + executeCallback(); + } else if (contentType === 'application/xhtml+xml') { + parseRDFaDOM(Util.parseXML(str, { contentType: 'application/xhtml+xml' }), kb, base); + executeCallback(); + } else if (contentType === 'text/html') { + parseRDFaDOM(Util.parseXML(str, { contentType: 'text/html' }), kb, base); + executeCallback(); + } else if (contentType === 'application/sparql-update') { + // @@ we handle a subset + sparqlUpdateParser(str, kb, base); + executeCallback(); + } else if (contentType === 'application/ld+json' || contentType === 'application/nquads' || contentType === 'application/n-quads') { + var n3Parser = N3.Parser(); + var triples = []; + if (contentType === 'application/ld+json') { + var jsonDocument; + try { + jsonDocument = JSON.parse(str); + } catch (parseErr) { + callback(parseErr, null); + } + jsonld.toRDF(jsonDocument, { format: 'application/nquads' }, nquadCallback); + } else { + nquadCallback(null, str); + } + } else { + throw new Error("Don't know how to parse " + contentType + ' yet'); + } + } catch (e) { + executeErrorCallback(e); + } + + function executeCallback() { + if (callback) { + callback(null, kb); + } else { + return; + } + } + + function executeErrorCallback(e) { + if (contentType !== 'application/ld+json' || contentType !== 'application/nquads' || contentType !== 'application/n-quads') { + if (callback) { + callback(e, kb); + } else { + throw new Error('Error trying to parse <' + base + '> as ' + contentType + ':\n' + e + ':\n' + e.stack); + } + } + } + /* + function setJsonLdBase (doc, base) { + if (doc instanceof Array) { + return + } + if (!('@context' in doc)) { + doc['@context'] = {} + } + doc['@context']['@base'] = base + } + */ + function nquadCallback(err, nquads) { + if (err) { + callback(err, kb); + } + try { + n3Parser.parse(nquads, tripleCallback); + } catch (err) { + callback(err, kb); + } + } + + function tripleCallback(err, triple, prefixes) { + if (err) { + callback(err, kb); + } + if (triple) { + triples.push(triple); + } else { + for (var i = 0; i < triples.length; i++) { + addTriple(kb, triples[i]); + } + callback(null, kb); + } + } + + function addTriple(kb, triple) { + var subject = createTerm(triple.subject); + var predicate = createTerm(triple.predicate); + var object = createTerm(triple.object); + var why = null; + if (triple.graph) { + why = createTerm(triple.graph); + } + kb.add(subject, predicate, object, why); + } + + function createTerm(termString) { + var value; + if (N3.Util.isLiteral(termString)) { + value = N3.Util.getLiteralValue(termString); + var language = N3.Util.getLiteralLanguage(termString); + var datatype = new NamedNode(N3.Util.getLiteralType(termString)); + return new Literal(value, language, datatype); + } else if (N3.Util.isIRI(termString)) { + return new NamedNode(termString); + } else if (N3.Util.isBlank(termString)) { + value = termString.substring(2, termString.length); + return new BlankNode(value); + } else { + return null; + } + } } },{"./blank-node":51,"./literal":63,"./n3parser":65,"./named-node":66,"./patch-parser":70,"./rdfaparser":73,"./rdfxmlparser":74,"./util":82,"jsonld":19,"n3":85}],70:[function(_dereq_,module,exports){ -'use strict'; - -// Parse a simple SPARL-Update subset syntax for patches. -// -// This parses -// WHERE {xxx} DELETE {yyy} INSERT DATA {zzz} -// (not necessarily in that order) -// as though it were the n3 -// <#query> patch:where {xxx}; patch:delete {yyy}; patch:insert {zzz}. -module.exports = sparqlUpdateParser; - -var N3Parser = _dereq_('./n3parser'); -var Namespace = _dereq_('./namespace'); - -function sparqlUpdateParser(str, kb, base) { - var i, j, k; - var keywords = ['INSERT', 'DELETE', 'WHERE']; - var SQNS = Namespace('http://www.w3.org/ns/pim/patch#'); - var p = N3Parser(kb, kb, base, base, null, null, '', null); - var clauses = {}; - - var badSyntax = function badSyntax(uri, lines, str, i, why) { - return 'Line ' + (lines + 1) + ' of <' + uri + '>: Bad syntax:\n ' + why + '\n at: "' + str.slice(i, i + 30) + '"'; - }; - - // var check = function (next, last, message) { - // if (next < 0) { - // throw badSyntax(p._thisDoc, p.lines, str, j, last, message) - // } - // return next - // } - i = 0; - var query = kb.sym(base + '#query'); // Invent a URI for the query - clauses['query'] = query; // A way of accessing it in its N3 model. - - while (true) { - // console.log("A Now at i = " + i) - j = p.skipSpace(str, i); - if (j < 0) { - return clauses; - } - // console.log("B After space at j= " + j) - if (str[j] === ';') { - i = p.skipSpace(str, j + 1); - if (i < 0) { - return clauses; // Allow end in a - } - j = i; - } - var found = false; - for (k = 0; k < keywords.length; k++) { - var key = keywords[k]; - if (str.slice(j, j + key.length) === key) { - i = p.skipSpace(str, j + key.length); - if (i < 0) { - throw badSyntax(p._thisDoc, p.lines, str, j + key.length, 'found EOF, needed {...} after ' + key); - } - if ((key === 'INSERT' || key === 'DELETE') && str.slice(i, i + 4) === 'DATA') { - // Some wanted 'DATA'. Whatever - j = p.skipSpace(str, i + 4); - if (j < 0) { - throw badSyntax(p._thisDoc, p.lines, str, i + 4, 'needed {...} after INSERT DATA ' + key); - } - i = j; - } - var res2 = []; - j = p.node(str, i, res2); // Parse all the complexity of the clause - - if (j < 0) { - throw badSyntax(p._thisDoc, p.lines, str, i, 'bad syntax or EOF in {...} after ' + key); - } - clauses[key.toLowerCase()] = res2[0]; - kb.add(query, SQNS(key.toLowerCase()), res2[0]); // , kb.sym(base) - // key is the keyword and res2 has the contents - found = true; - i = j; - } - } - if (!found && str.slice(j, j + 7) === '@prefix') { - i = p.directive(str, j); - if (i < 0) { - throw badSyntax(p._thisDoc, p.lines, str, i, 'bad syntax or EOF after @prefix '); - } - // console.log("P before dot i= " + i) - i = p.checkDot(str, i); - // console.log("Q after dot i= " + i) - found = true; - } - if (!found) { - // console.log("Bad syntax " + j) - throw badSyntax(p._thisDoc, p.lines, str, j, "Unknown syntax at start of statememt: '" + str.slice(j).slice(0, 20) + "'"); - } - } // while - // return clauses +'use strict'; + +// Parse a simple SPARL-Update subset syntax for patches. +// +// This parses +// WHERE {xxx} DELETE {yyy} INSERT DATA {zzz} +// (not necessarily in that order) +// as though it were the n3 +// <#query> patch:where {xxx}; patch:delete {yyy}; patch:insert {zzz}. +module.exports = sparqlUpdateParser; + +var N3Parser = _dereq_('./n3parser'); +var Namespace = _dereq_('./namespace'); + +function sparqlUpdateParser(str, kb, base) { + var i, j, k; + var keywords = ['INSERT', 'DELETE', 'WHERE']; + var SQNS = Namespace('http://www.w3.org/ns/pim/patch#'); + var p = N3Parser(kb, kb, base, base, null, null, '', null); + var clauses = {}; + + var badSyntax = function badSyntax(uri, lines, str, i, why) { + return 'Line ' + (lines + 1) + ' of <' + uri + '>: Bad syntax:\n ' + why + '\n at: "' + str.slice(i, i + 30) + '"'; + }; + + // var check = function (next, last, message) { + // if (next < 0) { + // throw badSyntax(p._thisDoc, p.lines, str, j, last, message) + // } + // return next + // } + i = 0; + var query = kb.sym(base + '#query'); // Invent a URI for the query + clauses['query'] = query; // A way of accessing it in its N3 model. + + while (true) { + // console.log("A Now at i = " + i) + j = p.skipSpace(str, i); + if (j < 0) { + return clauses; + } + // console.log("B After space at j= " + j) + if (str[j] === ';') { + i = p.skipSpace(str, j + 1); + if (i < 0) { + return clauses; // Allow end in a + } + j = i; + } + var found = false; + for (k = 0; k < keywords.length; k++) { + var key = keywords[k]; + if (str.slice(j, j + key.length) === key) { + i = p.skipSpace(str, j + key.length); + if (i < 0) { + throw badSyntax(p._thisDoc, p.lines, str, j + key.length, 'found EOF, needed {...} after ' + key); + } + if ((key === 'INSERT' || key === 'DELETE') && str.slice(i, i + 4) === 'DATA') { + // Some wanted 'DATA'. Whatever + j = p.skipSpace(str, i + 4); + if (j < 0) { + throw badSyntax(p._thisDoc, p.lines, str, i + 4, 'needed {...} after INSERT DATA ' + key); + } + i = j; + } + var res2 = []; + j = p.node(str, i, res2); // Parse all the complexity of the clause + + if (j < 0) { + throw badSyntax(p._thisDoc, p.lines, str, i, 'bad syntax or EOF in {...} after ' + key); + } + clauses[key.toLowerCase()] = res2[0]; + kb.add(query, SQNS(key.toLowerCase()), res2[0]); // , kb.sym(base) + // key is the keyword and res2 has the contents + found = true; + i = j; + } + } + if (!found && str.slice(j, j + 7) === '@prefix') { + i = p.directive(str, j); + if (i < 0) { + throw badSyntax(p._thisDoc, p.lines, str, i, 'bad syntax or EOF after @prefix '); + } + // console.log("P before dot i= " + i) + i = p.checkDot(str, i); + // console.log("Q after dot i= " + i) + found = true; + } + if (!found) { + // console.log("Bad syntax " + j) + throw badSyntax(p._thisDoc, p.lines, str, j, "Unknown syntax at start of statememt: '" + str.slice(j).slice(0, 20) + "'"); + } + } // while + // return clauses } },{"./n3parser":65,"./namespace":67}],71:[function(_dereq_,module,exports){ -'use strict'; - -var log = _dereq_('./log'); - -function queryToSPARQL(query) { - var indent = 0; - function getSelect(query) { - var str = addIndent() + 'SELECT '; - for (var i = 0; i < query.vars.length; i++) { - str += query.vars[i] + ' '; - } - str += '\n'; - return str; - } - - function getPattern(pat) { - var str = ''; - var st = pat.statements; - for (var x in st) { - log.debug('Found statement: ' + st); - str += addIndent() + st[x] + '\n'; - } - return str; - } - - function getConstraints(pat) { - var str = ''; - for (var v in pat.constraints) { - var foo = pat.constraints[v]; - str += addIndent() + 'FILTER ( ' + foo.describe(v) + ' ) ' + '\n'; - } - return str; - } - - function getOptionals(pat) { - var str = ''; - for (var x = 0; x < pat.optional.length; x++) { - // alert(pat.optional.termType) - log.debug('Found optional query'); - str += addIndent() + 'OPTIONAL { ' + '\n'; - indent++; - str += getPattern(pat.optional[x]); - str += getConstraints(pat.optional[x]); - str += getOptionals(pat.optional[x]); - indent--; - str += addIndent() + '}' + '\n'; - } - return str; - } - - function getWhere(pat) { - var str = addIndent() + 'WHERE \n' + '{ \n'; - indent++; - str += getPattern(pat); - str += getConstraints(pat); - str += getOptionals(pat); - indent--; - str += '}'; - return str; - } - - function addIndent() { - var str = ''; - for (var i = 0; i < indent; i++) { - str += ' '; - } - return str; - } - - function getSPARQL(query) { - return getSelect(query) + getWhere(query.pat); - } - - return getSPARQL(query); -} - +'use strict'; + +var log = _dereq_('./log'); + +function queryToSPARQL(query) { + var indent = 0; + function getSelect(query) { + var str = addIndent() + 'SELECT '; + for (var i = 0; i < query.vars.length; i++) { + str += query.vars[i] + ' '; + } + str += '\n'; + return str; + } + + function getPattern(pat) { + var str = ''; + var st = pat.statements; + for (var x in st) { + log.debug('Found statement: ' + st); + str += addIndent() + st[x] + '\n'; + } + return str; + } + + function getConstraints(pat) { + var str = ''; + for (var v in pat.constraints) { + var foo = pat.constraints[v]; + str += addIndent() + 'FILTER ( ' + foo.describe(v) + ' ) ' + '\n'; + } + return str; + } + + function getOptionals(pat) { + var str = ''; + for (var x = 0; x < pat.optional.length; x++) { + // alert(pat.optional.termType) + log.debug('Found optional query'); + str += addIndent() + 'OPTIONAL { ' + '\n'; + indent++; + str += getPattern(pat.optional[x]); + str += getConstraints(pat.optional[x]); + str += getOptionals(pat.optional[x]); + indent--; + str += addIndent() + '}' + '\n'; + } + return str; + } + + function getWhere(pat) { + var str = addIndent() + 'WHERE \n' + '{ \n'; + indent++; + str += getPattern(pat); + str += getConstraints(pat); + str += getOptionals(pat); + indent--; + str += '}'; + return str; + } + + function addIndent() { + var str = ''; + for (var i = 0; i < indent; i++) { + str += ' '; + } + return str; + } + + function getSPARQL(query) { + return getSelect(query) + getWhere(query.pat); + } + + return getSPARQL(query); +} + module.exports = queryToSPARQL; },{"./log":64}],72:[function(_dereq_,module,exports){ -'use strict'; - -var _indexedFormula = _dereq_('./indexed-formula'); - -var _indexedFormula2 = _interopRequireDefault(_indexedFormula); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Matching a formula against another formula -// Assync as well as Synchronously -// -// W3C open source licence 2005. -// -// This builds on term.js, match.js (and identity.js?) -// to allow a query of a formula. -// -// Here we introduce for the first time a subclass of term: variable. -// -// SVN ID: $Id: query.js 25116 2008-11-15 16:13:48Z timbl $ - -// Variable -// -// Compare with BlankNode. They are similar, but a variable -// stands for something whose value is to be returned. -// Also, users name variables and want the same name back when stuff is printed -/* jsl:option explicit*/ // Turn on JavaScriptLint variable declaration checking - -var log = _dereq_('./log'); -var docpart = _dereq_('./uri').docpart; - -/** - * Query class, for tracking queries the user has in the UI. - */ - -var Query = function Query(name, id) { - _classCallCheck(this, Query); - - this.pat = new _indexedFormula2.default(); // The pattern to search for - this.vars = []; // Used by UI code but not in query.js - // this.orderBy = [] // Not used yet - this.name = name; - this.id = id; -}; - -/** - * This function will match a pattern to the current kb - * - * The callback function is called whenever a match is found - * When fetcher is supplied this will be called to satisfy any resource requests - * currently not in the kb. The fetcher function needs to be defined manualy and - * should call $rdf.Util.AJAR_handleNewTerm to process the requested resource. - * - * @param myQuery, a knowledgebase containing a pattern to use as query - * @param callback, whenever the pattern in myQuery is met this is called with - * the new bindings as parameter - * @param fetcher, whenever a resource needs to be loaded this gets called IGNORED OBSOLETE - * f.fetecher is used as a Fetcher instance to do this. - * @param onDone callback when - */ - - -function indexedFormulaQuery(myQuery, callback, fetcher, onDone) { - // var kb = this - // /////////// Debug strings - var bindingDebug = function bindingDebug(b) { - var str = ''; - var v; - for (v in b) { - if (b.hasOwnProperty(v)) { - str += ' ' + v + ' -> ' + b[v]; - } - } - return str; - }; - - var bindingsDebug = function bindingsDebug(nbs) { - var str = 'Bindings: '; - var i; - var n = nbs.length; - for (i = 0; i < n; i++) { - str += bindingDebug(nbs[i][0]) + ';\n\t'; - } - return str; - }; // bindingsDebug - - // Unification: see also - // http://www.w3.org/2000/10/swap/term.py - // for similar things in python - // - // Unification finds all bindings such that when the binding is applied - // to one term it is equal to the other. - // Returns: a list of bindings, where a binding is an associative array - // mapping variuable to value. - - var unifyTerm = function unifyTerm(self, other, bindings, formula) { - var actual = bindings[self]; - if (actual === undefined) { - // Not mapped - if (self.isVar) { - /* if (self.isBlank) //bnodes are existential variables - { - if (self.toString() == other.toString()) return [[ [], null]] - else return [] - }*/ - var b = []; - b[self] = other; - return [[b, null]]; // Match - } - actual = self; - } - if (!actual.complexType) { - if (formula.redirections[actual]) { - actual = formula.redirections[actual]; - } - if (formula.redirections[other]) { - other = formula.redirections[other]; - } - if (actual.sameTerm(other)) { - return [[[], null]]; - } - return []; - } - if (self instanceof Array) { - if (!(other instanceof Array)) { - return []; - } - return unifyContents(self, other, bindings); - } - throw new Error('query.js: oops - code not written yet'); - // return undefined; // for lint - no jslint objects to unreachables - // return actual.unifyContents(other, bindings) - }; // unifyTerm - - var unifyContents = function unifyContents(self, other, bindings, formula) { - var nbs2; - if (self.length !== other.length) { - return []; // no way - } - if (!self.length) { - return [[[], null]]; // Success - } - var nbs = unifyTerm(self[0], other[0], bindings, formula); - if (nbs.length === 0) { - return nbs; - } - var res = []; - var i; - var n = nbs.length; - var nb; - var j; - var m; - var v; - var nb2; - var bindings2; - for (i = 0; i < n; i++) { - // for each possibility from the first term - nb = nbs[i][0]; // new bindings - bindings2 = []; - for (v in nb) { - if (nb.hasOwnProperty(v)) { - bindings2[v] = nb[v]; // copy - } - } - for (v in bindings) { - if (bindings.hasOwnProperty(v)) { - bindings2[v] = bindings[v]; // copy - } - } - nbs2 = unifyContents(self.slice(1), other.slice(1), bindings2, formula); - m = nbs2.length; - for (j = 0; j < m; j++) { - nb2 = nbs2[j][0]; // @@@@ no idea whether this is used or right - for (v in nb) { - if (nb.hasOwnProperty(v)) { - nb2[v] = nb[v]; - } - } - res.push([nb2, null]); - } - } - return res; - }; // unifyContents - - // Matching - // - // Matching finds all bindings such that when the binding is applied - // to one term it is equal to the other term. We only match formulae. - - /** if x is not in the bindings array, return the var; otherwise, return the bindings **/ - var bind = function bind(x, binding) { - var y = binding[x]; - if (y === undefined) { - return x; - } - return y; - }; - - // When there are OPTIONAL clauses, we must return bindings without them if none of them - // succeed. However, if any of them do succeed, we should not. (This is what branchCount() - // tracked. The problem currently is (2011/7) that when several optionals exist, and they - // all match, multiple sets of bindings are returned, each with one optional filled in.) - - var union = function union(a, b) { - var c = {}; - var x; - for (x in a) { - if (a.hasOwnProperty(x)) { - c[x] = a[x]; - } - } - for (x in b) { - if (b.hasOwnProperty(x)) { - c[x] = b[x]; - } - } - return c; - }; - - var OptionalBranchJunction = function OptionalBranchJunction(originalCallback, trunkBindings) { - this.trunkBindings = trunkBindings; - this.originalCallback = originalCallback; - this.branches = []; - // this.results = []; // result[i] is an array of bindings for branch i - // this.done = {}; // done[i] means all/any results are in for branch i - // this.count = {} - return this; - }; - - OptionalBranchJunction.prototype.checkAllDone = function () { - var i; - for (i = 0; i < this.branches.length; i++) { - if (!this.branches[i].done) { - return; - } - } - log.debug('OPTIONAL BIDNINGS ALL DONE:'); - this.doCallBacks(this.branches.length - 1, this.trunkBindings); - }; - // Recrursively generate the cross product of the bindings - OptionalBranchJunction.prototype.doCallBacks = function (b, bindings) { - var j; - if (b < 0) { - return this.originalCallback(bindings); - } - for (j = 0; j < this.branches[b].results.length; j++) { - this.doCallBacks(b - 1, union(bindings, this.branches[b].results[j])); - } - }; - - // A mandatory branch is the normal one, where callbacks - // are made immediately and no junction is needed. - // Might be useful for onFinsihed callback for query API. - var MandatoryBranch = function MandatoryBranch(callback, onDone) { - this.count = 0; - this.success = false; - this.done = false; - // this.results = [] - this.callback = callback; - this.onDone = onDone; - // this.junction = junction - // junction.branches.push(this) - return this; - }; - - MandatoryBranch.prototype.reportMatch = function (bindings) { - // log.error("@@@@ query.js 1"); // @@ - this.callback(bindings); - this.success = true; - }; - - MandatoryBranch.prototype.reportDone = function () { - this.done = true; - log.info('Mandatory query branch finished.***'); - if (this.onDone !== undefined) { - this.onDone(); - } - }; - - // An optional branch hoards its results. - var OptionalBranch = function OptionalBranch(junction) { - this.count = 0; - this.done = false; - this.results = []; - this.junction = junction; - junction.branches.push(this); - return this; - }; - - OptionalBranch.prototype.reportMatch = function (bindings) { - this.results.push(bindings); - }; - - OptionalBranch.prototype.reportDone = function () { - log.debug('Optional branch finished - results.length = ' + this.results.length); - if (this.results.length === 0) { - // This is what optional means: if no hits, - this.results.push({}); // mimic success, but with no bindings - log.debug("Optional branch FAILED - that's OK."); - } - this.done = true; - this.junction.checkAllDone(); - }; - - /** prepare -- sets the index of the item to the possible matches - * @param f - formula - * @param item - an Statement, possibly w/ vars in it - * @param bindings - - * @returns true if the query fails -- there are no items that match **/ - var prepare = function prepare(f, item, bindings) { - var t, terms, termIndex, i, ind; - item.nvars = 0; - item.index = null; - // if (!f.statements) log.warn("@@@ prepare: f is "+f) - // log.debug("Prepare: f has "+ f.statements.length) - // log.debug("Prepare: Kb size "+f.statements.length+" Preparing "+item) - - terms = [item.subject, item.predicate, item.object]; - ind = [f.subjectIndex, f.predicateIndex, f.objectIndex]; - for (i = 0; i < 3; i++) { - // alert("Prepare "+terms[i]+" "+(terms[i] in bindings)) - if (terms[i].isVar && !(bindings[terms[i]] !== undefined)) { - item.nvars++; - } else { - t = bind(terms[i], bindings); // returns the RDF binding if bound, otherwise itself - // if (terms[i]!=bind(terms[i],bindings) alert("Term: "+terms[i]+"Binding: "+bind(terms[i], bindings)) - if (f.redirections[t.hashString()]) { - t = f.redirections[t.hashString()]; // redirect - } - termIndex = ind[i][t.hashString()]; - - if (!termIndex) { - item.index = []; - return false; // Query line cannot match - } - if (item.index === null || item.index.length > termIndex.length) { - item.index = termIndex; - } - } - } - - if (item.index === null) { - // All 3 are variables? - item.index = f.statements; - } - return true; - }; // prepare - - /** sorting function -- negative if self is easier **/ - // We always prefer to start with a URI to be able to browse a graph - // this is why we put off items with more variables till later. - function easiestQuery(self, other) { - if (self.nvars !== other.nvars) { - return self.nvars - other.nvars; - } - return self.index.length - other.index.length; - } - - var match_index = 0; // index - /** matches a pattern formula against the knowledge base, e.g. to find matches for table-view - * - * @param f - knowledge base formula - * @param g - pattern formula (may have vars) - * @param bindingsSoFar - bindings accumulated in matching to date - * @param level - spaces to indent stuff also lets you know what level of recursion you're at - * @param fetcher - function (term, requestedBy) - myFetcher / AJAR_handleNewTerm / the sort - * @param localCallback - function(bindings, pattern, branch) called on sucess - * @returns nothing - * - * Will fetch linked data from the web iff the knowledge base an associated source fetcher (f.fetcher) - ***/ - var match = function match(f, g, bindingsSoFar, level, fetcher, localCallback, branch) { - log.debug('Match begins, Branch count now: ' + branch.count + ' for ' + branch.pattern_debug); - var sf = f.fetcher ? f.fetcher : null; - // log.debug("match: f has "+f.statements.length+", g has "+g.statements.length) - var pattern = g.statements; - if (pattern.length === 0) { - // when it's satisfied all the pattern triples - log.debug('FOUND MATCH WITH BINDINGS:' + bindingDebug(bindingsSoFar)); - if (g.optional.length === 0) { - branch.reportMatch(bindingsSoFar); - } else { - log.debug('OPTIONAL: ' + g.optional); - var junction = new OptionalBranchJunction(callback, bindingsSoFar); // @@ won't work with nested optionals? nest callbacks - var br = []; - var b; - for (b = 0; b < g.optional.length; b++) { - br[b] = new OptionalBranch(junction); // Allocate branches to prevent premature ending - br[b].pattern_debug = g.optional[b]; // for diagnotics only - } - for (b = 0; b < g.optional.length; b++) { - br[b].count = br[b].count + 1; // Count how many matches we have yet to complete - match(f, g.optional[b], bindingsSoFar, '', fetcher, callback, br[b]); - } - } - branch.count--; - log.debug('Match ends -- success , Branch count now: ' + branch.count + ' for ' + branch.pattern_debug); - return; // Success - } - - var item; - var i; - var n = pattern.length; - // log.debug(level + "Match "+n+" left, bs so far:"+bindingDebug(bindingsSoFar)) - - // Follow links from variables in query - if (sf) { - // Fetcher is used to fetch URIs, function first term is a URI term, second is the requester - var id = 'match' + match_index++; - var fetchResource = function fetchResource(requestedTerm, id) { - var docuri = requestedTerm.uri.split('#')[0]; - sf.nowOrWhenFetched(docuri, undefined, function (err, body, xhr) { - if (err) { - console.log('Error following link to <' + requestedTerm.uri + '> in query: ' + body); - } - match(f, g, bindingsSoFar, level, fetcher, // match not match2 to look up any others necessary. - localCallback, branch); - }); - /* - if( sf ) { - sf.addCallback('done', function(uri) { - if ((kb.canon(kb.sym(uri)).uri !== path) && (uri !== kb.canon(kb.sym(path)))) { - return true - } - return false - }) - } - fetcher(requestedTerm, id) - */ - }; - for (i = 0; i < n; i++) { - item = pattern[i]; // for each of the triples in the query - if (bindingsSoFar[item.subject] !== undefined && bindingsSoFar[item.subject].uri && sf && sf.getState(docpart(bindingsSoFar[item.subject].uri)) === 'unrequested') { - // fetch the subject info and return to id - fetchResource(bindingsSoFar[item.subject], id); - return; // only look up one per line this time, but we will come back again though match - } - if (bindingsSoFar[item.object] !== undefined && bindingsSoFar[item.object].uri && sf && sf.getState(docpart(bindingsSoFar[item.object].uri)) === 'unrequested') { - fetchResource(bindingsSoFar[item.object], id); - return; - } - } - } // if sf - match2(f, g, bindingsSoFar, level, fetcher, localCallback, branch); - return; - }; // match - - var constraintsSatisfied = function constraintsSatisfied(bindings, constraints) { - var res = true; - var x; - var test; - for (x in bindings) { - if (bindings.hasOwnProperty(x)) { - if (constraints[x]) { - test = constraints[x].test; - if (test && !test(bindings[x])) { - res = false; - } - } - } - } - return res; - }; - - /** match2 -- stuff after the fetch **/ - var match2 = function match2(f, g, bindingsSoFar, level, fetcher, callback, branch) { - // post fetch - var pattern = g.statements; - var n = pattern.length; - var i; - var k; - var nk; - var v; - var bindings2; - var newBindings1; - var item; - for (i = 0; i < n; i++) { - // For each statement left in the query, run prepare - item = pattern[i]; - log.info('match2: item=' + item + ', bindingsSoFar=' + bindingDebug(bindingsSoFar)); - prepare(f, item, bindingsSoFar); - } - pattern.sort(easiestQuery); - item = pattern[0]; - // log.debug("Sorted pattern:\n"+pattern) - var rest = f.formula(); - rest.optional = g.optional; - rest.constraints = g.constraints; - rest.statements = pattern.slice(1); // No indexes: we will not query g. - log.debug(level + 'match2 searching ' + item.index.length + ' for ' + item + '; bindings so far=' + bindingDebug(bindingsSoFar)); - // var results = [] - var c; - var nc = item.index.length; - var nbs1; - var st; - var onward = 0; - // var x - for (c = 0; c < nc; c++) { - // For each candidate statement - st = item.index[c]; // for each statement in the item's index, spawn a new match with that binding - nbs1 = unifyContents([item.subject, item.predicate, item.object], [st.subject, st.predicate, st.object], bindingsSoFar, f); - log.info(level + ' From first: ' + nbs1.length + ': ' + bindingsDebug(nbs1)); - nk = nbs1.length; - // branch.count += nk - // log.debug("Branch count bumped "+nk+" to: "+branch.count) - for (k = 0; k < nk; k++) { - // For each way that statement binds - bindings2 = []; - newBindings1 = nbs1[k][0]; - if (!constraintsSatisfied(newBindings1, g.constraints)) { - // branch.count-- - log.debug('Branch count CS: ' + branch.count); - } else { - for (v in newBindings1) { - if (newBindings1.hasOwnProperty(v)) { - bindings2[v] = newBindings1[v]; // copy - } - } - for (v in bindingsSoFar) { - if (bindingsSoFar.hasOwnProperty(v)) { - bindings2[v] = bindingsSoFar[v]; // copy - } - } - - branch.count++; // Count how many matches we have yet to complete - onward++; - match(f, rest, bindings2, level + ' ', fetcher, callback, branch); // call match - } - } - } - branch.count--; - if (onward === 0) { - log.debug('Match2 fails completely on ' + item); - } - log.debug('Match2 ends, Branch count: ' + branch.count + ' for ' + branch.pattern_debug); - if (branch.count === 0) { - log.debug('Branch finished.'); - branch.reportDone(); - } - }; // match2 - // ////////////////////////// Body of query() /////////////////////// - /* - if(!fetcher) { - fetcher=function (x, requestedBy) { - if (x === null) { - return - } - $rdf.Util.AJAR_handleNewTerm(kb, x, requestedBy) - } - } - */ - // prepare, oncallback: match1 - // match1: fetcher, oncallback: match2 - // match2, oncallback: populatetable - // log.debug("Query F length"+this.statements.length+" G="+myQuery) - var f = this; - log.debug('Query on ' + this.statements.length); - // kb.remoteQuery(myQuery,'http://jena.hpl.hp.com:3040/backstage',callback) - // return - var trunck = new MandatoryBranch(callback, onDone); - trunck.count++; // count one branch to complete at the moment - setTimeout(function () { - match(f, myQuery.pat, myQuery.pat.initBindings, '', fetcher, callback, trunck /* branch */); - }, 0); - - return; // returns nothing; callback does the work -} // query - -module.exports.Query = Query; +'use strict'; + +var _indexedFormula = _dereq_('./indexed-formula'); + +var _indexedFormula2 = _interopRequireDefault(_indexedFormula); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Matching a formula against another formula +// Assync as well as Synchronously +// +// W3C open source licence 2005. +// +// This builds on term.js, match.js (and identity.js?) +// to allow a query of a formula. +// +// Here we introduce for the first time a subclass of term: variable. +// +// SVN ID: $Id: query.js 25116 2008-11-15 16:13:48Z timbl $ + +// Variable +// +// Compare with BlankNode. They are similar, but a variable +// stands for something whose value is to be returned. +// Also, users name variables and want the same name back when stuff is printed +/* jsl:option explicit*/ // Turn on JavaScriptLint variable declaration checking + +var log = _dereq_('./log'); +var docpart = _dereq_('./uri').docpart; + +/** + * Query class, for tracking queries the user has in the UI. + */ + +var Query = function Query(name, id) { + _classCallCheck(this, Query); + + this.pat = new _indexedFormula2.default(); // The pattern to search for + this.vars = []; // Used by UI code but not in query.js + // this.orderBy = [] // Not used yet + this.name = name; + this.id = id; +}; + +/** + * This function will match a pattern to the current kb + * + * The callback function is called whenever a match is found + * When fetcher is supplied this will be called to satisfy any resource requests + * currently not in the kb. The fetcher function needs to be defined manualy and + * should call $rdf.Util.AJAR_handleNewTerm to process the requested resource. + * + * @param myQuery, a knowledgebase containing a pattern to use as query + * @param callback, whenever the pattern in myQuery is met this is called with + * the new bindings as parameter + * @param fetcher, whenever a resource needs to be loaded this gets called IGNORED OBSOLETE + * f.fetecher is used as a Fetcher instance to do this. + * @param onDone callback when + */ + + +function indexedFormulaQuery(myQuery, callback, fetcher, onDone) { + // var kb = this + // /////////// Debug strings + var bindingDebug = function bindingDebug(b) { + var str = ''; + var v; + for (v in b) { + if (b.hasOwnProperty(v)) { + str += ' ' + v + ' -> ' + b[v]; + } + } + return str; + }; + + var bindingsDebug = function bindingsDebug(nbs) { + var str = 'Bindings: '; + var i; + var n = nbs.length; + for (i = 0; i < n; i++) { + str += bindingDebug(nbs[i][0]) + ';\n\t'; + } + return str; + }; // bindingsDebug + + // Unification: see also + // http://www.w3.org/2000/10/swap/term.py + // for similar things in python + // + // Unification finds all bindings such that when the binding is applied + // to one term it is equal to the other. + // Returns: a list of bindings, where a binding is an associative array + // mapping variuable to value. + + var unifyTerm = function unifyTerm(self, other, bindings, formula) { + var actual = bindings[self]; + if (actual === undefined) { + // Not mapped + if (self.isVar) { + /* if (self.isBlank) //bnodes are existential variables + { + if (self.toString() == other.toString()) return [[ [], null]] + else return [] + }*/ + var b = []; + b[self] = other; + return [[b, null]]; // Match + } + actual = self; + } + if (!actual.complexType) { + if (formula.redirections[actual]) { + actual = formula.redirections[actual]; + } + if (formula.redirections[other]) { + other = formula.redirections[other]; + } + if (actual.sameTerm(other)) { + return [[[], null]]; + } + return []; + } + if (self instanceof Array) { + if (!(other instanceof Array)) { + return []; + } + return unifyContents(self, other, bindings); + } + throw new Error('query.js: oops - code not written yet'); + // return undefined; // for lint - no jslint objects to unreachables + // return actual.unifyContents(other, bindings) + }; // unifyTerm + + var unifyContents = function unifyContents(self, other, bindings, formula) { + var nbs2; + if (self.length !== other.length) { + return []; // no way + } + if (!self.length) { + return [[[], null]]; // Success + } + var nbs = unifyTerm(self[0], other[0], bindings, formula); + if (nbs.length === 0) { + return nbs; + } + var res = []; + var i; + var n = nbs.length; + var nb; + var j; + var m; + var v; + var nb2; + var bindings2; + for (i = 0; i < n; i++) { + // for each possibility from the first term + nb = nbs[i][0]; // new bindings + bindings2 = []; + for (v in nb) { + if (nb.hasOwnProperty(v)) { + bindings2[v] = nb[v]; // copy + } + } + for (v in bindings) { + if (bindings.hasOwnProperty(v)) { + bindings2[v] = bindings[v]; // copy + } + } + nbs2 = unifyContents(self.slice(1), other.slice(1), bindings2, formula); + m = nbs2.length; + for (j = 0; j < m; j++) { + nb2 = nbs2[j][0]; // @@@@ no idea whether this is used or right + for (v in nb) { + if (nb.hasOwnProperty(v)) { + nb2[v] = nb[v]; + } + } + res.push([nb2, null]); + } + } + return res; + }; // unifyContents + + // Matching + // + // Matching finds all bindings such that when the binding is applied + // to one term it is equal to the other term. We only match formulae. + + /** if x is not in the bindings array, return the var; otherwise, return the bindings **/ + var bind = function bind(x, binding) { + var y = binding[x]; + if (y === undefined) { + return x; + } + return y; + }; + + // When there are OPTIONAL clauses, we must return bindings without them if none of them + // succeed. However, if any of them do succeed, we should not. (This is what branchCount() + // tracked. The problem currently is (2011/7) that when several optionals exist, and they + // all match, multiple sets of bindings are returned, each with one optional filled in.) + + var union = function union(a, b) { + var c = {}; + var x; + for (x in a) { + if (a.hasOwnProperty(x)) { + c[x] = a[x]; + } + } + for (x in b) { + if (b.hasOwnProperty(x)) { + c[x] = b[x]; + } + } + return c; + }; + + var OptionalBranchJunction = function OptionalBranchJunction(originalCallback, trunkBindings) { + this.trunkBindings = trunkBindings; + this.originalCallback = originalCallback; + this.branches = []; + // this.results = []; // result[i] is an array of bindings for branch i + // this.done = {}; // done[i] means all/any results are in for branch i + // this.count = {} + return this; + }; + + OptionalBranchJunction.prototype.checkAllDone = function () { + var i; + for (i = 0; i < this.branches.length; i++) { + if (!this.branches[i].done) { + return; + } + } + log.debug('OPTIONAL BIDNINGS ALL DONE:'); + this.doCallBacks(this.branches.length - 1, this.trunkBindings); + }; + // Recrursively generate the cross product of the bindings + OptionalBranchJunction.prototype.doCallBacks = function (b, bindings) { + var j; + if (b < 0) { + return this.originalCallback(bindings); + } + for (j = 0; j < this.branches[b].results.length; j++) { + this.doCallBacks(b - 1, union(bindings, this.branches[b].results[j])); + } + }; + + // A mandatory branch is the normal one, where callbacks + // are made immediately and no junction is needed. + // Might be useful for onFinsihed callback for query API. + var MandatoryBranch = function MandatoryBranch(callback, onDone) { + this.count = 0; + this.success = false; + this.done = false; + // this.results = [] + this.callback = callback; + this.onDone = onDone; + // this.junction = junction + // junction.branches.push(this) + return this; + }; + + MandatoryBranch.prototype.reportMatch = function (bindings) { + // log.error("@@@@ query.js 1"); // @@ + this.callback(bindings); + this.success = true; + }; + + MandatoryBranch.prototype.reportDone = function () { + this.done = true; + log.info('Mandatory query branch finished.***'); + if (this.onDone !== undefined) { + this.onDone(); + } + }; + + // An optional branch hoards its results. + var OptionalBranch = function OptionalBranch(junction) { + this.count = 0; + this.done = false; + this.results = []; + this.junction = junction; + junction.branches.push(this); + return this; + }; + + OptionalBranch.prototype.reportMatch = function (bindings) { + this.results.push(bindings); + }; + + OptionalBranch.prototype.reportDone = function () { + log.debug('Optional branch finished - results.length = ' + this.results.length); + if (this.results.length === 0) { + // This is what optional means: if no hits, + this.results.push({}); // mimic success, but with no bindings + log.debug("Optional branch FAILED - that's OK."); + } + this.done = true; + this.junction.checkAllDone(); + }; + + /** prepare -- sets the index of the item to the possible matches + * @param f - formula + * @param item - an Statement, possibly w/ vars in it + * @param bindings - + * @returns true if the query fails -- there are no items that match **/ + var prepare = function prepare(f, item, bindings) { + var t, terms, termIndex, i, ind; + item.nvars = 0; + item.index = null; + // if (!f.statements) log.warn("@@@ prepare: f is "+f) + // log.debug("Prepare: f has "+ f.statements.length) + // log.debug("Prepare: Kb size "+f.statements.length+" Preparing "+item) + + terms = [item.subject, item.predicate, item.object]; + ind = [f.subjectIndex, f.predicateIndex, f.objectIndex]; + for (i = 0; i < 3; i++) { + // alert("Prepare "+terms[i]+" "+(terms[i] in bindings)) + if (terms[i].isVar && !(bindings[terms[i]] !== undefined)) { + item.nvars++; + } else { + t = bind(terms[i], bindings); // returns the RDF binding if bound, otherwise itself + // if (terms[i]!=bind(terms[i],bindings) alert("Term: "+terms[i]+"Binding: "+bind(terms[i], bindings)) + if (f.redirections[t.hashString()]) { + t = f.redirections[t.hashString()]; // redirect + } + termIndex = ind[i][t.hashString()]; + + if (!termIndex) { + item.index = []; + return false; // Query line cannot match + } + if (item.index === null || item.index.length > termIndex.length) { + item.index = termIndex; + } + } + } + + if (item.index === null) { + // All 3 are variables? + item.index = f.statements; + } + return true; + }; // prepare + + /** sorting function -- negative if self is easier **/ + // We always prefer to start with a URI to be able to browse a graph + // this is why we put off items with more variables till later. + function easiestQuery(self, other) { + if (self.nvars !== other.nvars) { + return self.nvars - other.nvars; + } + return self.index.length - other.index.length; + } + + var match_index = 0; // index + /** matches a pattern formula against the knowledge base, e.g. to find matches for table-view + * + * @param f - knowledge base formula + * @param g - pattern formula (may have vars) + * @param bindingsSoFar - bindings accumulated in matching to date + * @param level - spaces to indent stuff also lets you know what level of recursion you're at + * @param fetcher - function (term, requestedBy) - myFetcher / AJAR_handleNewTerm / the sort + * @param localCallback - function(bindings, pattern, branch) called on sucess + * @returns nothing + * + * Will fetch linked data from the web iff the knowledge base an associated source fetcher (f.fetcher) + ***/ + var match = function match(f, g, bindingsSoFar, level, fetcher, localCallback, branch) { + log.debug('Match begins, Branch count now: ' + branch.count + ' for ' + branch.pattern_debug); + var sf = f.fetcher ? f.fetcher : null; + // log.debug("match: f has "+f.statements.length+", g has "+g.statements.length) + var pattern = g.statements; + if (pattern.length === 0) { + // when it's satisfied all the pattern triples + log.debug('FOUND MATCH WITH BINDINGS:' + bindingDebug(bindingsSoFar)); + if (g.optional.length === 0) { + branch.reportMatch(bindingsSoFar); + } else { + log.debug('OPTIONAL: ' + g.optional); + var junction = new OptionalBranchJunction(callback, bindingsSoFar); // @@ won't work with nested optionals? nest callbacks + var br = []; + var b; + for (b = 0; b < g.optional.length; b++) { + br[b] = new OptionalBranch(junction); // Allocate branches to prevent premature ending + br[b].pattern_debug = g.optional[b]; // for diagnotics only + } + for (b = 0; b < g.optional.length; b++) { + br[b].count = br[b].count + 1; // Count how many matches we have yet to complete + match(f, g.optional[b], bindingsSoFar, '', fetcher, callback, br[b]); + } + } + branch.count--; + log.debug('Match ends -- success , Branch count now: ' + branch.count + ' for ' + branch.pattern_debug); + return; // Success + } + + var item; + var i; + var n = pattern.length; + // log.debug(level + "Match "+n+" left, bs so far:"+bindingDebug(bindingsSoFar)) + + // Follow links from variables in query + if (sf) { + // Fetcher is used to fetch URIs, function first term is a URI term, second is the requester + var id = 'match' + match_index++; + var fetchResource = function fetchResource(requestedTerm, id) { + var docuri = requestedTerm.uri.split('#')[0]; + sf.nowOrWhenFetched(docuri, undefined, function (err, body, xhr) { + if (err) { + console.log('Error following link to <' + requestedTerm.uri + '> in query: ' + body); + } + match(f, g, bindingsSoFar, level, fetcher, // match not match2 to look up any others necessary. + localCallback, branch); + }); + /* + if( sf ) { + sf.addCallback('done', function(uri) { + if ((kb.canon(kb.sym(uri)).uri !== path) && (uri !== kb.canon(kb.sym(path)))) { + return true + } + return false + }) + } + fetcher(requestedTerm, id) + */ + }; + for (i = 0; i < n; i++) { + item = pattern[i]; // for each of the triples in the query + if (bindingsSoFar[item.subject] !== undefined && bindingsSoFar[item.subject].uri && sf && sf.getState(docpart(bindingsSoFar[item.subject].uri)) === 'unrequested') { + // fetch the subject info and return to id + fetchResource(bindingsSoFar[item.subject], id); + return; // only look up one per line this time, but we will come back again though match + } + if (bindingsSoFar[item.object] !== undefined && bindingsSoFar[item.object].uri && sf && sf.getState(docpart(bindingsSoFar[item.object].uri)) === 'unrequested') { + fetchResource(bindingsSoFar[item.object], id); + return; + } + } + } // if sf + match2(f, g, bindingsSoFar, level, fetcher, localCallback, branch); + return; + }; // match + + var constraintsSatisfied = function constraintsSatisfied(bindings, constraints) { + var res = true; + var x; + var test; + for (x in bindings) { + if (bindings.hasOwnProperty(x)) { + if (constraints[x]) { + test = constraints[x].test; + if (test && !test(bindings[x])) { + res = false; + } + } + } + } + return res; + }; + + /** match2 -- stuff after the fetch **/ + var match2 = function match2(f, g, bindingsSoFar, level, fetcher, callback, branch) { + // post fetch + var pattern = g.statements; + var n = pattern.length; + var i; + var k; + var nk; + var v; + var bindings2; + var newBindings1; + var item; + for (i = 0; i < n; i++) { + // For each statement left in the query, run prepare + item = pattern[i]; + log.info('match2: item=' + item + ', bindingsSoFar=' + bindingDebug(bindingsSoFar)); + prepare(f, item, bindingsSoFar); + } + pattern.sort(easiestQuery); + item = pattern[0]; + // log.debug("Sorted pattern:\n"+pattern) + var rest = f.formula(); + rest.optional = g.optional; + rest.constraints = g.constraints; + rest.statements = pattern.slice(1); // No indexes: we will not query g. + log.debug(level + 'match2 searching ' + item.index.length + ' for ' + item + '; bindings so far=' + bindingDebug(bindingsSoFar)); + // var results = [] + var c; + var nc = item.index.length; + var nbs1; + var st; + var onward = 0; + // var x + for (c = 0; c < nc; c++) { + // For each candidate statement + st = item.index[c]; // for each statement in the item's index, spawn a new match with that binding + nbs1 = unifyContents([item.subject, item.predicate, item.object], [st.subject, st.predicate, st.object], bindingsSoFar, f); + log.info(level + ' From first: ' + nbs1.length + ': ' + bindingsDebug(nbs1)); + nk = nbs1.length; + // branch.count += nk + // log.debug("Branch count bumped "+nk+" to: "+branch.count) + for (k = 0; k < nk; k++) { + // For each way that statement binds + bindings2 = []; + newBindings1 = nbs1[k][0]; + if (!constraintsSatisfied(newBindings1, g.constraints)) { + // branch.count-- + log.debug('Branch count CS: ' + branch.count); + } else { + for (v in newBindings1) { + if (newBindings1.hasOwnProperty(v)) { + bindings2[v] = newBindings1[v]; // copy + } + } + for (v in bindingsSoFar) { + if (bindingsSoFar.hasOwnProperty(v)) { + bindings2[v] = bindingsSoFar[v]; // copy + } + } + + branch.count++; // Count how many matches we have yet to complete + onward++; + match(f, rest, bindings2, level + ' ', fetcher, callback, branch); // call match + } + } + } + branch.count--; + if (onward === 0) { + log.debug('Match2 fails completely on ' + item); + } + log.debug('Match2 ends, Branch count: ' + branch.count + ' for ' + branch.pattern_debug); + if (branch.count === 0) { + log.debug('Branch finished.'); + branch.reportDone(); + } + }; // match2 + // ////////////////////////// Body of query() /////////////////////// + /* + if(!fetcher) { + fetcher=function (x, requestedBy) { + if (x === null) { + return + } + $rdf.Util.AJAR_handleNewTerm(kb, x, requestedBy) + } + } + */ + // prepare, oncallback: match1 + // match1: fetcher, oncallback: match2 + // match2, oncallback: populatetable + // log.debug("Query F length"+this.statements.length+" G="+myQuery) + var f = this; + log.debug('Query on ' + this.statements.length); + // kb.remoteQuery(myQuery,'http://jena.hpl.hp.com:3040/backstage',callback) + // return + var trunck = new MandatoryBranch(callback, onDone); + trunck.count++; // count one branch to complete at the moment + setTimeout(function () { + match(f, myQuery.pat, myQuery.pat.initBindings, '', fetcher, callback, trunck /* branch */); + }, 0); + + return; // returns nothing; callback does the work +} // query + +module.exports.Query = Query; module.exports.indexedFormulaQuery = indexedFormulaQuery; },{"./indexed-formula":61,"./log":64,"./uri":81}],73:[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"); } } - -// RDFa Parser for rdflib.js - -// Originally by: Alex Milowski -// From https://github.com/alexmilowski/green-turtle -// Converted: timbl 2015-08-25 not yet working -// Added wrapper: csarven 2016-05-09 working - -// RDFaProcessor.prototype = new Object() // Was URIResolver - -// RDFaProcessor.prototype.constructor=RDFaProcessor - -// options.base = base URI not really an option, shopuld always be set. -// - -var BlankNode = _dereq_('./blank-node'); -var Literal = _dereq_('./literal'); -var rdf = _dereq_('./data-factory'); -var NamedNode = _dereq_('./named-node'); -var Uri = _dereq_('./uri'); -var Util = _dereq_('./util'); - -if (typeof Node === 'undefined') { - // @@@@@@ Global. Interface to xmldom. - var Node = { - ELEMENT_NODE: 1, - ATTRIBUTE_NODE: 2, - TEXT_NODE: 3, - CDATA_SECTION_NODE: 4, - ENTITY_REFERENCE_NODE: 5, - ENTITY_NODE: 6, - PROCESSING_INSTRUCTION_NODE: 7, - COMMENT_NODE: 8, - DOCUMENT_NODE: 9, - DOCUMENT_TYPE_NODE: 10, - DOCUMENT_FRAGMENT_NODE: 11, - NOTATION_NODE: 12 - }; -} - -var RDFaProcessor = function () { - function RDFaProcessor(kb, options) { - _classCallCheck(this, RDFaProcessor); - - this.options = options || {}; - this.kb = kb; - this.target = options.target || { - graph: { - subjects: {}, - prefixes: {}, - terms: {} - } - }; - // XXX: Added to track bnodes - this.blankNodes = []; - // XXX: Added for normalisation - this.htmlOptions = { - 'selfClosing': 'br img input area base basefont col colgroup source wbr isindex link meta param hr' - }; - this.theOne = '_:' + new Date().getTime(); - this.language = null; - this.vocabulary = null; - this.blankCounter = 0; - this.langAttributes = [{ namespaceURI: 'http://www.w3.org/XML/1998/namespace', localName: 'lang' }]; - this.inXHTMLMode = false; - this.absURIRE = /[\w\_\-]+:\S+/; - this.finishedHandlers = []; - this.init(); - } - - _createClass(RDFaProcessor, [{ - key: 'addTriple', - value: function addTriple(origin, subject, predicate, object) { - var su, ob, pr, or; - if (typeof subject === 'undefined') { - su = rdf.namedNode(this.options.base); - } else { - su = this.toRDFNodeObject(subject); - } - pr = this.toRDFNodeObject(predicate); - ob = this.toRDFNodeObject(object); - or = rdf.namedNode(this.options.base); - // console.log('Adding { ' + su + ' ' + pr + ' ' + ob + ' ' + or + ' }') - this.kb.add(su, pr, ob, or); - } - }, { - key: 'ancestorPath', - value: function ancestorPath(node) { - var path = ''; - while (node && node.nodeType !== Node.DOCUMENT_NODE) { - path = '/' + node.localName + path; - node = node.parentNode; - } - return path; - } - }, { - key: 'copyMappings', - value: function copyMappings(mappings) { - var newMappings = {}; - for (var k in mappings) { - newMappings[k] = mappings[k]; - } - return newMappings; - } - }, { - key: 'copyProperties', - value: function copyProperties() {} - }, { - key: 'deriveDateTimeType', - value: function deriveDateTimeType(value) { - for (var i = 0; i < RDFaProcessor.dateTimeTypes.length; i++) { - // console.log("Checking "+value+" against "+RDFaProcessor.dateTimeTypes[i].type) - var matched = RDFaProcessor.dateTimeTypes[i].pattern.exec(value); - if (matched && matched[0].length === value.length) { - // console.log("Matched!") - return RDFaProcessor.dateTimeTypes[i].type; - } - } - return null; - } - }, { - key: 'init', - value: function init() {} - }, { - key: 'newBlankNode', - value: function newBlankNode() { - this.blankCounter++; - return '_:' + this.blankCounter; - } - }, { - key: 'newSubjectOrigin', - value: function newSubjectOrigin(origin, subject) {} - }, { - key: 'parseCURIE', - value: function parseCURIE(value, prefixes, base) { - var colon = value.indexOf(':'); - var uri; - if (colon >= 0) { - var prefix = value.substring(0, colon); - if (prefix === '') { - // default prefix - uri = prefixes['']; - return uri ? uri + value.substring(colon + 1) : null; - } else if (prefix === '_') { - // blank node - return '_:' + value.substring(colon + 1); - } else if (RDFaProcessor.NCNAME.test(prefix)) { - uri = prefixes[prefix]; - if (uri) { - return uri + value.substring(colon + 1); - } - } - } - return null; - } - }, { - key: 'parseCURIEOrURI', - value: function parseCURIEOrURI(value, prefixes, base) { - var curie = this.parseCURIE(value, prefixes, base); - if (curie) { - return curie; - } - return this.resolveAndNormalize(base, value); - } - }, { - key: 'parsePredicate', - value: function parsePredicate(value, defaultVocabulary, terms, prefixes, base, ignoreTerms) { - if (value === '') { - return null; - } - var predicate = this.parseTermOrCURIEOrAbsURI(value, defaultVocabulary, ignoreTerms ? null : terms, prefixes, base); - if (predicate && predicate.indexOf('_:') === 0) { - return null; - } - return predicate; - } - }, { - key: 'parsePrefixMappings', - value: function parsePrefixMappings(str, target) { - var values = this.tokenize(str); - var prefix = null; - // var uri = null - for (var i = 0; i < values.length; i++) { - if (values[i][values[i].length - 1] === ':') { - prefix = values[i].substring(0, values[i].length - 1); - } else if (prefix) { - target[prefix] = this.options.base ? Uri.join(values[i], this.options.base) : values[i]; - prefix = null; - } - } - } - }, { - key: 'parseSafeCURIEOrCURIEOrURI', - value: function parseSafeCURIEOrCURIEOrURI(value, prefixes, base) { - value = this.trim(value); - if (value.charAt(0) === '[' && value.charAt(value.length - 1) === ']') { - value = value.substring(1, value.length - 1); - value = value.trim(value); - if (value.length === 0) { - return null; - } - if (value === '_:') { - // the one node - return this.theOne; - } - return this.parseCURIE(value, prefixes, base); - } else { - return this.parseCURIEOrURI(value, prefixes, base); - } - } - }, { - key: 'parseTermOrCURIEOrAbsURI', - value: function parseTermOrCURIEOrAbsURI(value, defaultVocabulary, terms, prefixes, base) { - // alert("Parsing "+value+" with default vocab "+defaultVocabulary) - value = this.trim(value); - var curie = this.parseCURIE(value, prefixes, base); - if (curie) { - return curie; - } else if (terms) { - if (defaultVocabulary && !this.absURIRE.exec(value)) { - return defaultVocabulary + value; - } - var term = terms[value]; - if (term) { - return term; - } - var lcvalue = value.toLowerCase(); - term = terms[lcvalue]; - if (term) { - return term; - } - } - if (this.absURIRE.exec(value)) { - return this.resolveAndNormalize(base, value); - } - return null; - } - }, { - key: 'parseTermOrCURIEOrURI', - value: function parseTermOrCURIEOrURI(value, defaultVocabulary, terms, prefixes, base) { - // alert("Parsing "+value+" with default vocab "+defaultVocabulary) - value = this.trim(value); - var curie = this.parseCURIE(value, prefixes, base); - if (curie) { - return curie; - } else { - var term = terms[value]; - if (term) { - return term; - } - var lcvalue = value.toLowerCase(); - term = terms[lcvalue]; - if (term) { - return term; - } - if (defaultVocabulary && !this.absURIRE.exec(value)) { - return defaultVocabulary + value; - } - } - return this.resolveAndNormalize(base, value); - } - }, { - key: 'parseURI', - value: function parseURI(uri) { - return uri; // We just use strings as URIs, not objects now. - } - }, { - key: 'process', - value: function process(node, options) { - /* - if (!window.console) { - window.console = { log: function() {} } - }*/ - var base; - if (node.nodeType === Node.DOCUMENT_NODE) { - base = node.baseURI; - node = node.documentElement; - node.baseURI = base; - this.setContext(node); - } else if (node.parentNode.nodeType === Node.DOCUMENT_NODE) { - this.setContext(node); - } - var queue = []; - // Fix for Firefox that includes the hash in the base URI - var removeHash = function removeHash(baseURI) { - // Fix for undefined baseURI property - if (!baseURI && options && options.baseURI) { - return options.baseURI; - } - - var hash = baseURI.indexOf('#'); - if (hash >= 0) { - baseURI = baseURI.substring(0, hash); - } - if (options && options.baseURIMap) { - baseURI = options.baseURIMap(baseURI); - } - return baseURI; - }; - queue.push({ current: node, - context: this.push(null, removeHash(node.baseURI)) - }); - while (queue.length > 0) { - var item = queue.shift(); - if (item.parent) { - // Sequence Step 14: list triple generation - if (item.context.parent && item.context.parent.listMapping === item.listMapping) { - // Skip a child context with exactly the same mapping - continue; - } - // console.log("Generating lists for "+item.subject+", tag "+item.parent.localName) - for (var _predicate in item.listMapping) { - var list = item.listMapping[_predicate]; - if (list.length === 0) { - this.addTriple(item.parent, item.subject, _predicate, { type: RDFaProcessor.objectURI, value: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil' }); - continue; - } - var bnodes = []; - for (var _i = 0; _i < list.length; _i++) { - bnodes.push(this.newBlankNode()); - // this.newSubject(item.parent,bnodes[i]) - } - for (var _i2 = 0; _i2 < bnodes.length; _i2++) { - this.addTriple(item.parent, bnodes[_i2], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', list[_i2]); - this.addTriple(item.parent, bnodes[_i2], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', { type: RDFaProcessor.objectURI, value: _i2 + 1 < bnodes.length ? bnodes[_i2 + 1] : 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil' }); - } - this.addTriple(item.parent, item.subject, _predicate, { type: RDFaProcessor.objectURI, value: bnodes[0] }); - } - continue; - } - var current = item.current; - var context = item.context; - // console.log("Tag: "+current.localName+", listMapping="+JSON.stringify(context.listMapping)) - // Sequence Step 1 - var skip = false; - var newSubject = null; - var currentObjectResource = null; - var typedResource = null; - var prefixes = context.prefixes; - var prefixesCopied = false; - var incomplete = []; - var listMapping = context.listMapping; - var listMappingDifferent = !context.parent; - var language = context.language; - var vocabulary = context.vocabulary; - // TODO: the "base" element may be used for HTML+RDFa 1.1 - base = this.parseURI(removeHash(current.baseURI)); - current.item = null; - // Sequence Step 2: set the default vocabulary - var vocabAtt = current.getAttributeNode('vocab'); - if (vocabAtt) { - var value = this.trim(vocabAtt.value); - if (value.length > 0) { - vocabulary = value; - var baseSubject = base.spec; - // this.newSubject(current,baseSubject) - this.addTriple(current, baseSubject, 'http://www.w3.org/ns/rdfa#usesVocabulary', { type: RDFaProcessor.objectURI, value: vocabulary }); - } else { - vocabulary = this.vocabulary; - } - } - // Sequence Step 3: IRI mappings - // handle xmlns attributes - for (var i = 0; i < current.attributes.length; i++) { - var att = current.attributes[i]; - // if (att.namespaceURI=="http://www.w3.org/2000/xmlns/") { - if (att.nodeName.charAt(0) === 'x' && att.nodeName.indexOf('xmlns:') === 0) { - if (!prefixesCopied) { - prefixes = this.copyMappings(prefixes); - prefixesCopied = true; - } - var prefix = att.nodeName.substring(6); - // TODO: resolve relative? - var ref = RDFaProcessor.trim(att.value); - prefixes[prefix] = this.options.base ? Uri.join(ref, this.options.base) : ref; - } - } - // Handle prefix mappings (@prefix) - var prefixAtt = current.getAttributeNode('prefix'); - if (prefixAtt) { - if (!prefixesCopied) { - prefixes = this.copyMappings(prefixes); - prefixesCopied = true; - } - this.parsePrefixMappings(prefixAtt.value, prefixes); - } - // Sequence Step 4: language - var xmlLangAtt = null; - for (var _i3 = 0; !xmlLangAtt && _i3 < this.langAttributes.length; _i3++) { - xmlLangAtt = current.getAttributeNodeNS(this.langAttributes[_i3].namespaceURI, this.langAttributes[_i3].localName); - } - if (xmlLangAtt) { - var _value = RDFaProcessor.trim(xmlLangAtt.value); - if (_value.length > 0) { - language = _value; - } else { - language = null; - } - } - var relAtt = current.getAttributeNode('rel'); - var revAtt = current.getAttributeNode('rev'); - var typeofAtt = current.getAttributeNode('typeof'); - var propertyAtt = current.getAttributeNode('property'); - var datatypeAtt = current.getAttributeNode('datatype'); - var datetimeAtt = this.inHTMLMode ? current.getAttributeNode('datetime') : null; - var contentAtt = current.getAttributeNode('content'); - var aboutAtt = current.getAttributeNode('about'); - var srcAtt = current.getAttributeNode('src'); - var resourceAtt = current.getAttributeNode('resource'); - var hrefAtt = current.getAttributeNode('href'); - var inlistAtt = current.getAttributeNode('inlist'); - var relAttPredicates = []; - var predicate, values; - if (relAtt) { - values = this.tokenize(relAtt.value); - for (var _i4 = 0; _i4 < values.length; _i4++) { - predicate = this.parsePredicate(values[_i4], vocabulary, context.terms, prefixes, base, this.inHTMLMode && propertyAtt !== null); - if (predicate) { - relAttPredicates.push(predicate); - } - } - } - var revAttPredicates = []; - if (revAtt) { - values = this.tokenize(revAtt.value); - for (var _i5 = 0; _i5 < values.length; _i5++) { - predicate = this.parsePredicate(values[_i5], vocabulary, context.terms, prefixes, base, this.inHTMLMode && propertyAtt); - if (predicate) { - revAttPredicates.push(predicate); - } - } - } - // Section 3.1, bullet 7 - if (this.inHTMLMode && (relAtt || revAtt) && propertyAtt) { - if (relAttPredicates.length === 0) { - relAtt = null; - } - if (revAttPredicates.length === 0) { - revAtt = null; - } - } - if (relAtt || revAtt) { - // Sequence Step 6: establish new subject and value - if (aboutAtt) { - newSubject = this.parseSafeCURIEOrCURIEOrURI(aboutAtt.value, prefixes, base); - } - if (typeofAtt) { - typedResource = newSubject; - } - if (!newSubject) { - if (current.parentNode.nodeType === Node.DOCUMENT_NODE) { - newSubject = removeHash(current.baseURI); - } else if (context.parentObject) { - // TODO: Verify: If the xml:base has been set and the parentObject is the baseURI of the parent, then the subject needs to be the new base URI - newSubject = removeHash(current.parentNode.baseURI) === context.parentObject ? removeHash(current.baseURI) : context.parentObject; - } - } - if (resourceAtt) { - currentObjectResource = this.parseSafeCURIEOrCURIEOrURI(resourceAtt.value, prefixes, base); - } - if (!currentObjectResource) { - if (hrefAtt) { - currentObjectResource = this.resolveAndNormalize(base, encodeURI(hrefAtt.value)); - } else if (srcAtt) { - currentObjectResource = this.resolveAndNormalize(base, encodeURI(srcAtt.value)); - } else if (typeofAtt && !aboutAtt && !(this.inXHTMLMode && (current.localName === 'head' || current.localName === 'body'))) { - currentObjectResource = this.newBlankNode(); - } - } - if (typeofAtt && !aboutAtt && this.inXHTMLMode && (current.localName === 'head' || current.localName === 'body')) { - typedResource = newSubject; - } else if (typeofAtt && !aboutAtt) { - typedResource = currentObjectResource; - } - } else if (propertyAtt && !contentAtt && !datatypeAtt) { - // Sequence Step 5.1: establish a new subject - if (aboutAtt) { - newSubject = this.parseSafeCURIEOrCURIEOrURI(aboutAtt.value, prefixes, base); - if (typeofAtt) { - typedResource = newSubject; - } - } - if (!newSubject && current.parentNode.nodeType === Node.DOCUMENT_NODE) { - newSubject = removeHash(current.baseURI); - if (typeofAtt) { - typedResource = newSubject; - } - } else if (!newSubject && context.parentObject) { - // TODO: Verify: If the xml:base has been set and the parentObject is the baseURI of the parent, then the subject needs to be the new base URI - newSubject = removeHash(current.parentNode.baseURI) === context.parentObject ? removeHash(current.baseURI) : context.parentObject; - } - if (typeofAtt && !typedResource) { - if (resourceAtt) { - typedResource = this.parseSafeCURIEOrCURIEOrURI(resourceAtt.value, prefixes, base); - } - if (!typedResource && hrefAtt) { - typedResource = this.resolveAndNormalize(base, encodeURI(hrefAtt.value)); - } - if (!typedResource && srcAtt) { - typedResource = this.resolveAndNormalize(base, encodeURI(srcAtt.value)); - } - if (!typedResource && (this.inXHTMLMode || this.inHTMLMode) && (current.localName === 'head' || current.localName === 'body')) { - typedResource = newSubject; - } - if (!typedResource) { - typedResource = this.newBlankNode(); - } - currentObjectResource = typedResource; - } - // console.log(current.localName+", newSubject="+newSubject+", typedResource="+typedResource+", currentObjectResource="+currentObjectResource) - } else { - // Sequence Step 5.2: establish a new subject - if (aboutAtt) { - newSubject = this.parseSafeCURIEOrCURIEOrURI(aboutAtt.value, prefixes, base); - } - if (!newSubject && resourceAtt) { - newSubject = this.parseSafeCURIEOrCURIEOrURI(resourceAtt.value, prefixes, base); - } - if (!newSubject && hrefAtt) { - newSubject = this.resolveAndNormalize(base, encodeURI(hrefAtt.value)); - } - if (!newSubject && srcAtt) { - newSubject = this.resolveAndNormalize(base, encodeURI(srcAtt.value)); - } - if (!newSubject) { - if (current.parentNode.nodeType === Node.DOCUMENT_NODE) { - newSubject = removeHash(current.baseURI); - } else if ((this.inXHTMLMode || this.inHTMLMode) && (current.localName === 'head' || current.localName === 'body')) { - newSubject = removeHash(current.parentNode.baseURI) === context.parentObject ? removeHash(current.baseURI) : context.parentObject; - } else if (typeofAtt) { - newSubject = this.newBlankNode(); - } else if (context.parentObject) { - // TODO: Verify: If the xml:base has been set and the parentObject is the baseURI of the parent, then the subject needs to be the new base URI - newSubject = removeHash(current.parentNode.baseURI) === context.parentObject ? removeHash(current.baseURI) : context.parentObject; - if (!propertyAtt) { - skip = true; - } - } - } - if (typeofAtt) { - typedResource = newSubject; - } - } - // console.log(current.tagName+": newSubject="+newSubject+", currentObjectResource="+currentObjectResource+", typedResource="+typedResource+", skip="+skip) - // var rdfaData = null - if (newSubject) { - // this.newSubject(current,newSubject) - if (aboutAtt || resourceAtt || typedResource) { - var id = newSubject; - if (typeofAtt && !aboutAtt && !resourceAtt && currentObjectResource) { - id = currentObjectResource; - } - // console.log("Setting data attribute for "+current.localName+" for subject "+id) - this.newSubjectOrigin(current, id); - } - } - // Sequence Step 7: generate type triple - if (typedResource) { - values = this.tokenize(typeofAtt.value); - for (var _i6 = 0; _i6 < values.length; _i6++) { - var object = this.parseTermOrCURIEOrAbsURI(values[_i6], vocabulary, context.terms, prefixes, base); - if (object) { - this.addTriple(current, typedResource, RDFaProcessor.typeURI, { type: RDFaProcessor.objectURI, value: object }); - } - } - } - // Sequence Step 8: new list mappings if there is a new subject - // console.log("Step 8: newSubject="+newSubject+", context.parentObject="+context.parentObject) - if (newSubject && newSubject !== context.parentObject) { - // console.log("Generating new list mapping for "+newSubject) - listMapping = {}; - listMappingDifferent = true; - } - // Sequence Step 9: generate object triple - if (currentObjectResource) { - if (relAtt && inlistAtt) { - for (var _i7 = 0; _i7 < relAttPredicates.length; _i7++) { - var _list = listMapping[relAttPredicates[_i7]]; - if (!_list) { - _list = []; - listMapping[relAttPredicates[_i7]] = _list; - } - _list.push({ type: RDFaProcessor.objectURI, value: currentObjectResource }); - } - } else if (relAtt) { - for (var _i8 = 0; _i8 < relAttPredicates.length; _i8++) { - this.addTriple(current, newSubject, relAttPredicates[_i8], { type: RDFaProcessor.objectURI, value: currentObjectResource }); - } - } - if (revAtt) { - for (var _i9 = 0; _i9 < revAttPredicates.length; _i9++) { - this.addTriple(current, currentObjectResource, revAttPredicates[_i9], { type: RDFaProcessor.objectURI, value: newSubject }); - } - } - } else { - // Sequence Step 10: incomplete triples - if (newSubject && !currentObjectResource && (relAtt || revAtt)) { - currentObjectResource = this.newBlankNode(); - // alert(current.tagName+": generated blank node, newSubject="+newSubject+" currentObjectResource="+currentObjectResource) - } - if (relAtt && inlistAtt) { - for (var _i10 = 0; _i10 < relAttPredicates.length; _i10++) { - var _list2 = listMapping[relAttPredicates[_i10]]; - if (!_list2) { - _list2 = []; - listMapping[predicate] = _list2; - } - // console.log("Adding incomplete list for "+predicate) - incomplete.push({ predicate: relAttPredicates[_i10], list: _list2 }); - } - } else if (relAtt) { - for (var _i11 = 0; _i11 < relAttPredicates.length; _i11++) { - incomplete.push({ predicate: relAttPredicates[_i11], forward: true }); - } - } - if (revAtt) { - for (var _i12 = 0; _i12 < revAttPredicates.length; _i12++) { - incomplete.push({ predicate: revAttPredicates[_i12], forward: false }); - } - } - } - // Step 11: Current property values - if (propertyAtt) { - var datatype = null; - var content = null; - if (datatypeAtt) { - datatype = datatypeAtt.value === '' ? RDFaProcessor.PlainLiteralURI : this.parseTermOrCURIEOrAbsURI(datatypeAtt.value, vocabulary, context.terms, prefixes, base); - if (datetimeAtt && !contentAtt) { - content = datetimeAtt.value; - } else { - content = datatype === RDFaProcessor.XMLLiteralURI || datatype === RDFaProcessor.HTMLLiteralURI ? null : contentAtt ? contentAtt.value : current.textContent; - } - } else if (contentAtt) { - datatype = RDFaProcessor.PlainLiteralURI; - content = contentAtt.value; - } else if (datetimeAtt) { - content = datetimeAtt.value; - datatype = RDFaProcessor.deriveDateTimeType(content); - if (!datatype) { - datatype = RDFaProcessor.PlainLiteralURI; - } - } else if (!relAtt && !revAtt) { - if (resourceAtt) { - content = this.parseSafeCURIEOrCURIEOrURI(resourceAtt.value, prefixes, base); - } - if (!content && hrefAtt) { - content = this.resolveAndNormalize(base, encodeURI(hrefAtt.value)); - } else if (!content && srcAtt) { - content = this.resolveAndNormalize(base, encodeURI(srcAtt.value)); - } - if (content) { - datatype = RDFaProcessor.objectURI; - } - } - if (!datatype) { - if (typeofAtt && !aboutAtt) { - datatype = RDFaProcessor.objectURI; - content = typedResource; - } else { - content = current.textContent; - if (this.inHTMLMode && current.localName === 'time') { - datatype = RDFaProcessor.deriveDateTimeType(content); - } - if (!datatype) { - datatype = RDFaProcessor.PlainLiteralURI; - } - } - } - values = this.tokenize(propertyAtt.value); - for (var _i13 = 0; _i13 < values.length; _i13++) { - var _predicate2 = this.parsePredicate(values[_i13], vocabulary, context.terms, prefixes, base); - if (_predicate2) { - if (inlistAtt) { - var _list3 = listMapping[_predicate2]; - if (!_list3) { - _list3 = []; - listMapping[_predicate2] = _list3; - } - _list3.push(datatype === RDFaProcessor.XMLLiteralURI || datatype === RDFaProcessor.HTMLLiteralURI ? { type: datatype, value: current.childNodes } : { type: datatype ? datatype : RDFaProcessor.PlainLiteralURI, value: content, language: language }); - } else { - if (datatype === RDFaProcessor.XMLLiteralURI || datatype === RDFaProcessor.HTMLLiteralURI) { - this.addTriple(current, newSubject, _predicate2, { type: datatype, value: current.childNodes }); - } else { - this.addTriple(current, newSubject, _predicate2, { type: datatype ? datatype : RDFaProcessor.PlainLiteralURI, value: content, language: language }); - // console.log(newSubject+" "+predicate+"="+content) - } - } - } - } - } - // Sequence Step 12: complete incomplete triples with new subject - if (newSubject && !skip) { - for (var _i14 = 0; _i14 < context.incomplete.length; _i14++) { - if (context.incomplete[_i14].list) { - // console.log("Adding subject "+newSubject+" to list for "+context.incomplete[i].predicate) - // TODO: it is unclear what to do here - context.incomplete[_i14].list.push({ type: RDFaProcessor.objectURI, value: newSubject }); - } else if (context.incomplete[_i14].forward) { - // console.log(current.tagName+": completing forward triple "+context.incomplete[i].predicate+" with object="+newSubject) - this.addTriple(current, context.subject, context.incomplete[_i14].predicate, { type: RDFaProcessor.objectURI, value: newSubject }); - } else { - // console.log(current.tagName+": completing reverse triple with object="+context.subject) - this.addTriple(current, newSubject, context.incomplete[_i14].predicate, { type: RDFaProcessor.objectURI, value: context.subject }); - } - } - } - var childContext = null; - var listSubject = newSubject; - if (skip) { - // TODO: should subject be null? - childContext = this.push(context, context.subject); - // TODO: should the entObject be passed along? If not, then intermediary children will keep properties from being associated with incomplete triples. - // TODO: Verify: if the current baseURI has changed and the parentObject is the parent's base URI, then the baseURI should change - childContext.parentObject = removeHash(current.parentNode.baseURI) === context.parentObject ? removeHash(current.baseURI) : context.parentObject; - childContext.incomplete = context.incomplete; - childContext.language = language; - childContext.prefixes = prefixes; - childContext.vocabulary = vocabulary; - } else { - childContext = this.push(context, newSubject); - childContext.parentObject = currentObjectResource ? currentObjectResource : newSubject ? newSubject : context.subject; - childContext.prefixes = prefixes; - childContext.incomplete = incomplete; - if (currentObjectResource) { - // console.log("Generating new list mapping for "+currentObjectResource) - listSubject = currentObjectResource; - listMapping = {}; - listMappingDifferent = true; - } - childContext.listMapping = listMapping; - childContext.language = language; - childContext.vocabulary = vocabulary; - } - if (listMappingDifferent) { - // console.log("Pushing list parent "+current.localName) - queue.unshift({ parent: current, context: context, subject: listSubject, listMapping: listMapping }); - } - for (var child = current.lastChild; child; child = child.previousSibling) { - if (child.nodeType === Node.ELEMENT_NODE) { - // console.log("Pushing child "+child.localName) - child.baseURI = current.baseURI; - queue.unshift({ current: child, context: childContext }); - } - } - } - if (this.inHTMLMode) { - this.copyProperties(); - } - for (var _i15 = 0; _i15 < this.finishedHandlers.length; _i15++) { - this.finishedHandlers[_i15](node); - } - } - }, { - key: 'push', - value: function push(parent, subject) { - return { - parent: parent, - subject: subject ? subject : parent ? parent.subject : null, - parentObject: null, - incomplete: [], - listMapping: parent ? parent.listMapping : {}, - language: parent ? parent.language : this.language, - prefixes: parent ? parent.prefixes : this.target.graph.prefixes, - terms: parent ? parent.terms : this.target.graph.terms, - vocabulary: parent ? parent.vocabulary : this.vocabulary - }; - } - }, { - key: 'resolveAndNormalize', - value: function resolveAndNormalize(base, uri) { - // console.log("Joining " + uri + " to " + base + " making " + Uri.join(uri, base)) - return Uri.join(uri, base); // @@ normalize? - } - }, { - key: 'setContext', - value: function setContext(node) { - // We only recognized XHTML+RDFa 1.1 if the version is set propertyly - if (node.localName === 'html' && node.getAttribute('version') === 'XHTML+RDFa 1.1') { - this.setXHTMLContext(); - } else if (node.localName === 'html' || node.namespaceURI === 'http://www.w3.org/1999/xhtml') { - if (typeof document !== 'undefined' && document.doctype) { - if (document.doctype.publicId === '-//W3C//DTD XHTML+RDFa 1.0//EN' && document.doctype.systemId === 'http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd') { - console.log('WARNING: RDF 1.0 is not supported. Defaulting to HTML5 mode.'); - this.setHTMLContext(); - } else if (document.doctype.publicId === '-//W3C//DTD XHTML+RDFa 1.1//EN' && document.doctype.systemId === 'http://www.w3.org/MarkUp/DTD/xhtml-rdfa-2.dtd') { - this.setXHTMLContext(); - } else { - this.setHTMLContext(); - } - } else { - this.setHTMLContext(); - } - } else { - this.setXMLContext(); - } - } - }, { - key: 'setHTMLContext', - value: function setHTMLContext() { - this.setInitialContext(); - this.langAttributes = [{ - namespaceURI: 'http://www.w3.org/XML/1998/namespace', - localName: 'lang' - }, { namespaceURI: null, localName: 'lang' }]; - this.inXHTMLMode = false; - this.inHTMLMode = true; - } - }, { - key: 'setInitialContext', - value: function setInitialContext() { - this.vocabulary = null; - // By default, the prefixes are terms are loaded to the RDFa 1.1. standard within the graph constructor - this.langAttributes = [{ - namespaceURI: 'http://www.w3.org/XML/1998/namespace', - localName: 'lang' - }]; - } - }, { - key: 'setXHTMLContext', - value: function setXHTMLContext() { - this.setInitialContext(); - this.inXHTMLMode = true; - this.inHTMLMode = false; - this.langAttributes = [{ - namespaceURI: 'http://www.w3.org/XML/1998/namespace', - localName: 'lang' }, { namespaceURI: null, localName: 'lang' }]; - // From http://www.w3.org/2011/rdfa-context/xhtml-rdfa-1.1 - this.target.graph.terms['alternate'] = 'http://www.w3.org/1999/xhtml/vocab#alternate'; - this.target.graph.terms['appendix'] = 'http://www.w3.org/1999/xhtml/vocab#appendix'; - this.target.graph.terms['bookmark'] = 'http://www.w3.org/1999/xhtml/vocab#bookmark'; - this.target.graph.terms['cite'] = 'http://www.w3.org/1999/xhtml/vocab#cite'; - this.target.graph.terms['chapter'] = 'http://www.w3.org/1999/xhtml/vocab#chapter'; - this.target.graph.terms['contents'] = 'http://www.w3.org/1999/xhtml/vocab#contents'; - this.target.graph.terms['copyright'] = 'http://www.w3.org/1999/xhtml/vocab#copyright'; - this.target.graph.terms['first'] = 'http://www.w3.org/1999/xhtml/vocab#first'; - this.target.graph.terms['glossary'] = 'http://www.w3.org/1999/xhtml/vocab#glossary'; - this.target.graph.terms['help'] = 'http://www.w3.org/1999/xhtml/vocab#help'; - this.target.graph.terms['icon'] = 'http://www.w3.org/1999/xhtml/vocab#icon'; - this.target.graph.terms['index'] = 'http://www.w3.org/1999/xhtml/vocab#index'; - this.target.graph.terms['last'] = 'http://www.w3.org/1999/xhtml/vocab#last'; - this.target.graph.terms['license'] = 'http://www.w3.org/1999/xhtml/vocab#license'; - this.target.graph.terms['meta'] = 'http://www.w3.org/1999/xhtml/vocab#meta'; - this.target.graph.terms['next'] = 'http://www.w3.org/1999/xhtml/vocab#next'; - this.target.graph.terms['prev'] = 'http://www.w3.org/1999/xhtml/vocab#prev'; - this.target.graph.terms['previous'] = 'http://www.w3.org/1999/xhtml/vocab#previous'; - this.target.graph.terms['section'] = 'http://www.w3.org/1999/xhtml/vocab#section'; - this.target.graph.terms['stylesheet'] = 'http://www.w3.org/1999/xhtml/vocab#stylesheet'; - this.target.graph.terms['subsection'] = 'http://www.w3.org/1999/xhtml/vocab#subsection'; - this.target.graph.terms['start'] = 'http://www.w3.org/1999/xhtml/vocab#start'; - this.target.graph.terms['top'] = 'http://www.w3.org/1999/xhtml/vocab#top'; - this.target.graph.terms['up'] = 'http://www.w3.org/1999/xhtml/vocab#up'; - this.target.graph.terms['p3pv1'] = 'http://www.w3.org/1999/xhtml/vocab#p3pv1'; - // other - this.target.graph.terms['related'] = 'http://www.w3.org/1999/xhtml/vocab#related'; - this.target.graph.terms['role'] = 'http://www.w3.org/1999/xhtml/vocab#role'; - this.target.graph.terms['transformation'] = 'http://www.w3.org/1999/xhtml/vocab#transformation'; - } - }, { - key: 'setXMLContext', - value: function setXMLContext() { - this.setInitialContext(); - this.inXHTMLMode = false; - this.inHTMLMode = false; - } - }, { - key: 'tokenize', - value: function tokenize(str) { - return this.trim(str).split(/\s+/); - } - }, { - key: 'toRDFNodeObject', - value: function toRDFNodeObject(x) { - if (typeof x === 'undefined') return undefined; - if (typeof x === 'string') { - if (x.substring(0, 2) === '_:') { - if (typeof this.blankNodes[x.substring(2)] === 'undefined') { - this.blankNodes[x.substring(2)] = new BlankNode(x.substring(2)); - } - return this.blankNodes[x.substring(2)]; - } - return rdf.namedNode(x); - } - switch (x.type) { - case RDFaProcessor.objectURI: - if (x.value.substring(0, 2) === '_:') { - if (typeof this.blankNodes[x.value.substring(2)] === 'undefined') { - this.blankNodes[x.value.substring(2)] = new BlankNode(x.value.substring(2)); - } - return this.blankNodes[x.value.substring(2)]; - } - return rdf.namedNode(x.value); - case RDFaProcessor.PlainLiteralURI: - return new Literal(x.value, x.language || ''); - case RDFaProcessor.XMLLiteralURI: - case RDFaProcessor.HTMLLiteralURI: - var string = ''; - Object.keys(x.value).forEach(function (i) { - string += Util.domToString(x.value[i], this.htmlOptions); - }); - return new Literal(string, '', new NamedNode(x.type)); - default: - return new Literal(x.value, '', new NamedNode(x.type)); - } - } - }, { - key: 'trim', - value: function trim(str) { - return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - } - }], [{ - key: 'parseRDFaDOM', - value: function parseRDFaDOM(dom, kb, base) { - var p = new RDFaProcessor(kb, { 'base': base }); - dom.baseURI = base; - p.process(dom); - } - }]); - - return RDFaProcessor; -}(); - -RDFaProcessor.XMLLiteralURI = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral'; -RDFaProcessor.HTMLLiteralURI = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#HTML'; -RDFaProcessor.PlainLiteralURI = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#PlainLiteral'; -RDFaProcessor.objectURI = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#object'; -RDFaProcessor.typeURI = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'; -RDFaProcessor.nameChar = '[-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\u10000-\uEFFFF.0-9\xB7\u0300-\u036F\u203F-\u2040]'; -RDFaProcessor.nameStartChar = '[A-Za-z\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u0131\u0134-\u013E\u0141-\u0148\u014A-\u017E\u0180-\u01C3\u01CD-\u01F0\u01F4-\u01F5\u01FA-\u0217\u0250-\u02A8\u02BB-\u02C1\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03D6\u03DA\u03DC\u03DE\u03E0\u03E2-\u03F3\u0401-\u040C\u040E-\u044F\u0451-\u045C\u045E-\u0481\u0490-\u04C4\u04C7-\u04C8\u04CB-\u04CC\u04D0-\u04EB\u04EE-\u04F5\u04F8-\u04F9\u0531-\u0556\u0559\u0561-\u0586\u05D0-\u05EA\u05F0-\u05F2\u0621-\u063A\u0641-\u064A\u0671-\u06B7\u06BA-\u06BE\u06C0-\u06CE\u06D0-\u06D3\u06D5\u06E5-\u06E6\u0905-\u0939\u093D\u0958-\u0961\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8B\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AE0\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B36-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB5\u0BB7-\u0BB9\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CDE\u0CE0-\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D60-\u0D61\u0E01-\u0E2E\u0E30\u0E32-\u0E33\u0E40-\u0E45\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EAE\u0EB0\u0EB2-\u0EB3\u0EBD\u0EC0-\u0EC4\u0F40-\u0F47\u0F49-\u0F69\u10A0-\u10C5\u10D0-\u10F6\u1100\u1102-\u1103\u1105-\u1107\u1109\u110B-\u110C\u110E-\u1112\u113C\u113E\u1140\u114C\u114E\u1150\u1154-\u1155\u1159\u115F-\u1161\u1163\u1165\u1167\u1169\u116D-\u116E\u1172-\u1173\u1175\u119E\u11A8\u11AB\u11AE-\u11AF\u11B7-\u11B8\u11BA\u11BC-\u11C2\u11EB\u11F0\u11F9\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2126\u212A-\u212B\u212E\u2180-\u2182\u3041-\u3094\u30A1-\u30FA\u3105-\u312C\uAC00-\uD7A3\u4E00-\u9FA5\u3007\u3021-\u3029_]'; -RDFaProcessor.NCNAME = new RegExp('^' + RDFaProcessor.nameStartChar + RDFaProcessor.nameChar + '*$'); - -/* -RDFaProcessor.prototype.resolveAndNormalize = function(base,href) { - var u = base.resolve(href) - var parsed = this.parseURI(u) - parsed.normalize() - return parsed.spec -} -*/ - -RDFaProcessor.dateTimeTypes = [{ pattern: /-?P(?:[0-9]+Y)?(?:[0-9]+M)?(?:[0-9]+D)?(?:T(?:[0-9]+H)?(?:[0-9]+M)?(?:[0-9]+(?:\.[0-9]+)?S)?)?/, - type: 'http://www.w3.org/2001/XMLSchema#duration' }, { pattern: /-?(?:[1-9][0-9][0-9][0-9]|0[1-9][0-9][0-9]|00[1-9][0-9]|000[1-9])-[0-9][0-9]-[0-9][0-9]T(?:[0-1][0-9]|2[0-4]):[0-5][0-9]:[0-5][0-9](?:\.[0-9]+)?(?:Z|[+\-][0-9][0-9]:[0-9][0-9])?/, - type: 'http://www.w3.org/2001/XMLSchema#dateTime' }, { pattern: /-?(?:[1-9][0-9][0-9][0-9]|0[1-9][0-9][0-9]|00[1-9][0-9]|000[1-9])-[0-9][0-9]-[0-9][0-9](?:Z|[+\-][0-9][0-9]:[0-9][0-9])?/, - type: 'http://www.w3.org/2001/XMLSchema#date' }, { pattern: /(?:[0-1][0-9]|2[0-4]):[0-5][0-9]:[0-5][0-9](?:\.[0-9]+)?(?:Z|[+\-][0-9][0-9]:[0-9][0-9])?/, - type: 'http://www.w3.org/2001/XMLSchema#time' }, { pattern: /-?(?:[1-9][0-9][0-9][0-9]|0[1-9][0-9][0-9]|00[1-9][0-9]|000[1-9])-[0-9][0-9]/, - type: 'http://www.w3.org/2001/XMLSchema#gYearMonth' }, { pattern: /-?[1-9][0-9][0-9][0-9]|0[1-9][0-9][0-9]|00[1-9][0-9]|000[1-9]/, - type: 'http://www.w3.org/2001/XMLSchema#gYear' }]; - +'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"); } } + +// RDFa Parser for rdflib.js + +// Originally by: Alex Milowski +// From https://github.com/alexmilowski/green-turtle +// Converted: timbl 2015-08-25 not yet working +// Added wrapper: csarven 2016-05-09 working + +// RDFaProcessor.prototype = new Object() // Was URIResolver + +// RDFaProcessor.prototype.constructor=RDFaProcessor + +// options.base = base URI not really an option, shopuld always be set. +// + +var BlankNode = _dereq_('./blank-node'); +var Literal = _dereq_('./literal'); +var rdf = _dereq_('./data-factory'); +var NamedNode = _dereq_('./named-node'); +var Uri = _dereq_('./uri'); +var Util = _dereq_('./util'); + +if (typeof Node === 'undefined') { + // @@@@@@ Global. Interface to xmldom. + var Node = { + ELEMENT_NODE: 1, + ATTRIBUTE_NODE: 2, + TEXT_NODE: 3, + CDATA_SECTION_NODE: 4, + ENTITY_REFERENCE_NODE: 5, + ENTITY_NODE: 6, + PROCESSING_INSTRUCTION_NODE: 7, + COMMENT_NODE: 8, + DOCUMENT_NODE: 9, + DOCUMENT_TYPE_NODE: 10, + DOCUMENT_FRAGMENT_NODE: 11, + NOTATION_NODE: 12 + }; +} + +var RDFaProcessor = function () { + function RDFaProcessor(kb, options) { + _classCallCheck(this, RDFaProcessor); + + this.options = options || {}; + this.kb = kb; + this.target = options.target || { + graph: { + subjects: {}, + prefixes: {}, + terms: {} + } + }; + // XXX: Added to track bnodes + this.blankNodes = []; + // XXX: Added for normalisation + this.htmlOptions = { + 'selfClosing': 'br img input area base basefont col colgroup source wbr isindex link meta param hr' + }; + this.theOne = '_:' + new Date().getTime(); + this.language = null; + this.vocabulary = null; + this.blankCounter = 0; + this.langAttributes = [{ namespaceURI: 'http://www.w3.org/XML/1998/namespace', localName: 'lang' }]; + this.inXHTMLMode = false; + this.absURIRE = /[\w\_\-]+:\S+/; + this.finishedHandlers = []; + this.init(); + } + + _createClass(RDFaProcessor, [{ + key: 'addTriple', + value: function addTriple(origin, subject, predicate, object) { + var su, ob, pr, or; + if (typeof subject === 'undefined') { + su = rdf.namedNode(this.options.base); + } else { + su = this.toRDFNodeObject(subject); + } + pr = this.toRDFNodeObject(predicate); + ob = this.toRDFNodeObject(object); + or = rdf.namedNode(this.options.base); + // console.log('Adding { ' + su + ' ' + pr + ' ' + ob + ' ' + or + ' }') + this.kb.add(su, pr, ob, or); + } + }, { + key: 'ancestorPath', + value: function ancestorPath(node) { + var path = ''; + while (node && node.nodeType !== Node.DOCUMENT_NODE) { + path = '/' + node.localName + path; + node = node.parentNode; + } + return path; + } + }, { + key: 'copyMappings', + value: function copyMappings(mappings) { + var newMappings = {}; + for (var k in mappings) { + newMappings[k] = mappings[k]; + } + return newMappings; + } + }, { + key: 'copyProperties', + value: function copyProperties() {} + }, { + key: 'deriveDateTimeType', + value: function deriveDateTimeType(value) { + for (var i = 0; i < RDFaProcessor.dateTimeTypes.length; i++) { + // console.log("Checking "+value+" against "+RDFaProcessor.dateTimeTypes[i].type) + var matched = RDFaProcessor.dateTimeTypes[i].pattern.exec(value); + if (matched && matched[0].length === value.length) { + // console.log("Matched!") + return RDFaProcessor.dateTimeTypes[i].type; + } + } + return null; + } + }, { + key: 'init', + value: function init() {} + }, { + key: 'newBlankNode', + value: function newBlankNode() { + this.blankCounter++; + return '_:' + this.blankCounter; + } + }, { + key: 'newSubjectOrigin', + value: function newSubjectOrigin(origin, subject) {} + }, { + key: 'parseCURIE', + value: function parseCURIE(value, prefixes, base) { + var colon = value.indexOf(':'); + var uri; + if (colon >= 0) { + var prefix = value.substring(0, colon); + if (prefix === '') { + // default prefix + uri = prefixes['']; + return uri ? uri + value.substring(colon + 1) : null; + } else if (prefix === '_') { + // blank node + return '_:' + value.substring(colon + 1); + } else if (RDFaProcessor.NCNAME.test(prefix)) { + uri = prefixes[prefix]; + if (uri) { + return uri + value.substring(colon + 1); + } + } + } + return null; + } + }, { + key: 'parseCURIEOrURI', + value: function parseCURIEOrURI(value, prefixes, base) { + var curie = this.parseCURIE(value, prefixes, base); + if (curie) { + return curie; + } + return this.resolveAndNormalize(base, value); + } + }, { + key: 'parsePredicate', + value: function parsePredicate(value, defaultVocabulary, terms, prefixes, base, ignoreTerms) { + if (value === '') { + return null; + } + var predicate = this.parseTermOrCURIEOrAbsURI(value, defaultVocabulary, ignoreTerms ? null : terms, prefixes, base); + if (predicate && predicate.indexOf('_:') === 0) { + return null; + } + return predicate; + } + }, { + key: 'parsePrefixMappings', + value: function parsePrefixMappings(str, target) { + var values = this.tokenize(str); + var prefix = null; + // var uri = null + for (var i = 0; i < values.length; i++) { + if (values[i][values[i].length - 1] === ':') { + prefix = values[i].substring(0, values[i].length - 1); + } else if (prefix) { + target[prefix] = this.options.base ? Uri.join(values[i], this.options.base) : values[i]; + prefix = null; + } + } + } + }, { + key: 'parseSafeCURIEOrCURIEOrURI', + value: function parseSafeCURIEOrCURIEOrURI(value, prefixes, base) { + value = this.trim(value); + if (value.charAt(0) === '[' && value.charAt(value.length - 1) === ']') { + value = value.substring(1, value.length - 1); + value = value.trim(value); + if (value.length === 0) { + return null; + } + if (value === '_:') { + // the one node + return this.theOne; + } + return this.parseCURIE(value, prefixes, base); + } else { + return this.parseCURIEOrURI(value, prefixes, base); + } + } + }, { + key: 'parseTermOrCURIEOrAbsURI', + value: function parseTermOrCURIEOrAbsURI(value, defaultVocabulary, terms, prefixes, base) { + // alert("Parsing "+value+" with default vocab "+defaultVocabulary) + value = this.trim(value); + var curie = this.parseCURIE(value, prefixes, base); + if (curie) { + return curie; + } else if (terms) { + if (defaultVocabulary && !this.absURIRE.exec(value)) { + return defaultVocabulary + value; + } + var term = terms[value]; + if (term) { + return term; + } + var lcvalue = value.toLowerCase(); + term = terms[lcvalue]; + if (term) { + return term; + } + } + if (this.absURIRE.exec(value)) { + return this.resolveAndNormalize(base, value); + } + return null; + } + }, { + key: 'parseTermOrCURIEOrURI', + value: function parseTermOrCURIEOrURI(value, defaultVocabulary, terms, prefixes, base) { + // alert("Parsing "+value+" with default vocab "+defaultVocabulary) + value = this.trim(value); + var curie = this.parseCURIE(value, prefixes, base); + if (curie) { + return curie; + } else { + var term = terms[value]; + if (term) { + return term; + } + var lcvalue = value.toLowerCase(); + term = terms[lcvalue]; + if (term) { + return term; + } + if (defaultVocabulary && !this.absURIRE.exec(value)) { + return defaultVocabulary + value; + } + } + return this.resolveAndNormalize(base, value); + } + }, { + key: 'parseURI', + value: function parseURI(uri) { + return uri; // We just use strings as URIs, not objects now. + } + }, { + key: 'process', + value: function process(node, options) { + /* + if (!window.console) { + window.console = { log: function() {} } + }*/ + var base; + if (node.nodeType === Node.DOCUMENT_NODE) { + base = node.baseURI; + node = node.documentElement; + node.baseURI = base; + this.setContext(node); + } else if (node.parentNode.nodeType === Node.DOCUMENT_NODE) { + this.setContext(node); + } + var queue = []; + // Fix for Firefox that includes the hash in the base URI + var removeHash = function removeHash(baseURI) { + // Fix for undefined baseURI property + if (!baseURI && options && options.baseURI) { + return options.baseURI; + } + + var hash = baseURI.indexOf('#'); + if (hash >= 0) { + baseURI = baseURI.substring(0, hash); + } + if (options && options.baseURIMap) { + baseURI = options.baseURIMap(baseURI); + } + return baseURI; + }; + queue.push({ current: node, + context: this.push(null, removeHash(node.baseURI)) + }); + while (queue.length > 0) { + var item = queue.shift(); + if (item.parent) { + // Sequence Step 14: list triple generation + if (item.context.parent && item.context.parent.listMapping === item.listMapping) { + // Skip a child context with exactly the same mapping + continue; + } + // console.log("Generating lists for "+item.subject+", tag "+item.parent.localName) + for (var _predicate in item.listMapping) { + var list = item.listMapping[_predicate]; + if (list.length === 0) { + this.addTriple(item.parent, item.subject, _predicate, { type: RDFaProcessor.objectURI, value: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil' }); + continue; + } + var bnodes = []; + for (var _i = 0; _i < list.length; _i++) { + bnodes.push(this.newBlankNode()); + // this.newSubject(item.parent,bnodes[i]) + } + for (var _i2 = 0; _i2 < bnodes.length; _i2++) { + this.addTriple(item.parent, bnodes[_i2], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', list[_i2]); + this.addTriple(item.parent, bnodes[_i2], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', { type: RDFaProcessor.objectURI, value: _i2 + 1 < bnodes.length ? bnodes[_i2 + 1] : 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil' }); + } + this.addTriple(item.parent, item.subject, _predicate, { type: RDFaProcessor.objectURI, value: bnodes[0] }); + } + continue; + } + var current = item.current; + var context = item.context; + // console.log("Tag: "+current.localName+", listMapping="+JSON.stringify(context.listMapping)) + // Sequence Step 1 + var skip = false; + var newSubject = null; + var currentObjectResource = null; + var typedResource = null; + var prefixes = context.prefixes; + var prefixesCopied = false; + var incomplete = []; + var listMapping = context.listMapping; + var listMappingDifferent = !context.parent; + var language = context.language; + var vocabulary = context.vocabulary; + // TODO: the "base" element may be used for HTML+RDFa 1.1 + base = this.parseURI(removeHash(current.baseURI)); + current.item = null; + // Sequence Step 2: set the default vocabulary + var vocabAtt = current.getAttributeNode('vocab'); + if (vocabAtt) { + var value = this.trim(vocabAtt.value); + if (value.length > 0) { + vocabulary = value; + var baseSubject = base.spec; + // this.newSubject(current,baseSubject) + this.addTriple(current, baseSubject, 'http://www.w3.org/ns/rdfa#usesVocabulary', { type: RDFaProcessor.objectURI, value: vocabulary }); + } else { + vocabulary = this.vocabulary; + } + } + // Sequence Step 3: IRI mappings + // handle xmlns attributes + for (var i = 0; i < current.attributes.length; i++) { + var att = current.attributes[i]; + // if (att.namespaceURI=="http://www.w3.org/2000/xmlns/") { + if (att.nodeName.charAt(0) === 'x' && att.nodeName.indexOf('xmlns:') === 0) { + if (!prefixesCopied) { + prefixes = this.copyMappings(prefixes); + prefixesCopied = true; + } + var prefix = att.nodeName.substring(6); + // TODO: resolve relative? + var ref = RDFaProcessor.trim(att.value); + prefixes[prefix] = this.options.base ? Uri.join(ref, this.options.base) : ref; + } + } + // Handle prefix mappings (@prefix) + var prefixAtt = current.getAttributeNode('prefix'); + if (prefixAtt) { + if (!prefixesCopied) { + prefixes = this.copyMappings(prefixes); + prefixesCopied = true; + } + this.parsePrefixMappings(prefixAtt.value, prefixes); + } + // Sequence Step 4: language + var xmlLangAtt = null; + for (var _i3 = 0; !xmlLangAtt && _i3 < this.langAttributes.length; _i3++) { + xmlLangAtt = current.getAttributeNodeNS(this.langAttributes[_i3].namespaceURI, this.langAttributes[_i3].localName); + } + if (xmlLangAtt) { + var _value = RDFaProcessor.trim(xmlLangAtt.value); + if (_value.length > 0) { + language = _value; + } else { + language = null; + } + } + var relAtt = current.getAttributeNode('rel'); + var revAtt = current.getAttributeNode('rev'); + var typeofAtt = current.getAttributeNode('typeof'); + var propertyAtt = current.getAttributeNode('property'); + var datatypeAtt = current.getAttributeNode('datatype'); + var datetimeAtt = this.inHTMLMode ? current.getAttributeNode('datetime') : null; + var contentAtt = current.getAttributeNode('content'); + var aboutAtt = current.getAttributeNode('about'); + var srcAtt = current.getAttributeNode('src'); + var resourceAtt = current.getAttributeNode('resource'); + var hrefAtt = current.getAttributeNode('href'); + var inlistAtt = current.getAttributeNode('inlist'); + var relAttPredicates = []; + var predicate, values; + if (relAtt) { + values = this.tokenize(relAtt.value); + for (var _i4 = 0; _i4 < values.length; _i4++) { + predicate = this.parsePredicate(values[_i4], vocabulary, context.terms, prefixes, base, this.inHTMLMode && propertyAtt !== null); + if (predicate) { + relAttPredicates.push(predicate); + } + } + } + var revAttPredicates = []; + if (revAtt) { + values = this.tokenize(revAtt.value); + for (var _i5 = 0; _i5 < values.length; _i5++) { + predicate = this.parsePredicate(values[_i5], vocabulary, context.terms, prefixes, base, this.inHTMLMode && propertyAtt); + if (predicate) { + revAttPredicates.push(predicate); + } + } + } + // Section 3.1, bullet 7 + if (this.inHTMLMode && (relAtt || revAtt) && propertyAtt) { + if (relAttPredicates.length === 0) { + relAtt = null; + } + if (revAttPredicates.length === 0) { + revAtt = null; + } + } + if (relAtt || revAtt) { + // Sequence Step 6: establish new subject and value + if (aboutAtt) { + newSubject = this.parseSafeCURIEOrCURIEOrURI(aboutAtt.value, prefixes, base); + } + if (typeofAtt) { + typedResource = newSubject; + } + if (!newSubject) { + if (current.parentNode.nodeType === Node.DOCUMENT_NODE) { + newSubject = removeHash(current.baseURI); + } else if (context.parentObject) { + // TODO: Verify: If the xml:base has been set and the parentObject is the baseURI of the parent, then the subject needs to be the new base URI + newSubject = removeHash(current.parentNode.baseURI) === context.parentObject ? removeHash(current.baseURI) : context.parentObject; + } + } + if (resourceAtt) { + currentObjectResource = this.parseSafeCURIEOrCURIEOrURI(resourceAtt.value, prefixes, base); + } + if (!currentObjectResource) { + if (hrefAtt) { + currentObjectResource = this.resolveAndNormalize(base, encodeURI(hrefAtt.value)); + } else if (srcAtt) { + currentObjectResource = this.resolveAndNormalize(base, encodeURI(srcAtt.value)); + } else if (typeofAtt && !aboutAtt && !(this.inXHTMLMode && (current.localName === 'head' || current.localName === 'body'))) { + currentObjectResource = this.newBlankNode(); + } + } + if (typeofAtt && !aboutAtt && this.inXHTMLMode && (current.localName === 'head' || current.localName === 'body')) { + typedResource = newSubject; + } else if (typeofAtt && !aboutAtt) { + typedResource = currentObjectResource; + } + } else if (propertyAtt && !contentAtt && !datatypeAtt) { + // Sequence Step 5.1: establish a new subject + if (aboutAtt) { + newSubject = this.parseSafeCURIEOrCURIEOrURI(aboutAtt.value, prefixes, base); + if (typeofAtt) { + typedResource = newSubject; + } + } + if (!newSubject && current.parentNode.nodeType === Node.DOCUMENT_NODE) { + newSubject = removeHash(current.baseURI); + if (typeofAtt) { + typedResource = newSubject; + } + } else if (!newSubject && context.parentObject) { + // TODO: Verify: If the xml:base has been set and the parentObject is the baseURI of the parent, then the subject needs to be the new base URI + newSubject = removeHash(current.parentNode.baseURI) === context.parentObject ? removeHash(current.baseURI) : context.parentObject; + } + if (typeofAtt && !typedResource) { + if (resourceAtt) { + typedResource = this.parseSafeCURIEOrCURIEOrURI(resourceAtt.value, prefixes, base); + } + if (!typedResource && hrefAtt) { + typedResource = this.resolveAndNormalize(base, encodeURI(hrefAtt.value)); + } + if (!typedResource && srcAtt) { + typedResource = this.resolveAndNormalize(base, encodeURI(srcAtt.value)); + } + if (!typedResource && (this.inXHTMLMode || this.inHTMLMode) && (current.localName === 'head' || current.localName === 'body')) { + typedResource = newSubject; + } + if (!typedResource) { + typedResource = this.newBlankNode(); + } + currentObjectResource = typedResource; + } + // console.log(current.localName+", newSubject="+newSubject+", typedResource="+typedResource+", currentObjectResource="+currentObjectResource) + } else { + // Sequence Step 5.2: establish a new subject + if (aboutAtt) { + newSubject = this.parseSafeCURIEOrCURIEOrURI(aboutAtt.value, prefixes, base); + } + if (!newSubject && resourceAtt) { + newSubject = this.parseSafeCURIEOrCURIEOrURI(resourceAtt.value, prefixes, base); + } + if (!newSubject && hrefAtt) { + newSubject = this.resolveAndNormalize(base, encodeURI(hrefAtt.value)); + } + if (!newSubject && srcAtt) { + newSubject = this.resolveAndNormalize(base, encodeURI(srcAtt.value)); + } + if (!newSubject) { + if (current.parentNode.nodeType === Node.DOCUMENT_NODE) { + newSubject = removeHash(current.baseURI); + } else if ((this.inXHTMLMode || this.inHTMLMode) && (current.localName === 'head' || current.localName === 'body')) { + newSubject = removeHash(current.parentNode.baseURI) === context.parentObject ? removeHash(current.baseURI) : context.parentObject; + } else if (typeofAtt) { + newSubject = this.newBlankNode(); + } else if (context.parentObject) { + // TODO: Verify: If the xml:base has been set and the parentObject is the baseURI of the parent, then the subject needs to be the new base URI + newSubject = removeHash(current.parentNode.baseURI) === context.parentObject ? removeHash(current.baseURI) : context.parentObject; + if (!propertyAtt) { + skip = true; + } + } + } + if (typeofAtt) { + typedResource = newSubject; + } + } + // console.log(current.tagName+": newSubject="+newSubject+", currentObjectResource="+currentObjectResource+", typedResource="+typedResource+", skip="+skip) + // var rdfaData = null + if (newSubject) { + // this.newSubject(current,newSubject) + if (aboutAtt || resourceAtt || typedResource) { + var id = newSubject; + if (typeofAtt && !aboutAtt && !resourceAtt && currentObjectResource) { + id = currentObjectResource; + } + // console.log("Setting data attribute for "+current.localName+" for subject "+id) + this.newSubjectOrigin(current, id); + } + } + // Sequence Step 7: generate type triple + if (typedResource) { + values = this.tokenize(typeofAtt.value); + for (var _i6 = 0; _i6 < values.length; _i6++) { + var object = this.parseTermOrCURIEOrAbsURI(values[_i6], vocabulary, context.terms, prefixes, base); + if (object) { + this.addTriple(current, typedResource, RDFaProcessor.typeURI, { type: RDFaProcessor.objectURI, value: object }); + } + } + } + // Sequence Step 8: new list mappings if there is a new subject + // console.log("Step 8: newSubject="+newSubject+", context.parentObject="+context.parentObject) + if (newSubject && newSubject !== context.parentObject) { + // console.log("Generating new list mapping for "+newSubject) + listMapping = {}; + listMappingDifferent = true; + } + // Sequence Step 9: generate object triple + if (currentObjectResource) { + if (relAtt && inlistAtt) { + for (var _i7 = 0; _i7 < relAttPredicates.length; _i7++) { + var _list = listMapping[relAttPredicates[_i7]]; + if (!_list) { + _list = []; + listMapping[relAttPredicates[_i7]] = _list; + } + _list.push({ type: RDFaProcessor.objectURI, value: currentObjectResource }); + } + } else if (relAtt) { + for (var _i8 = 0; _i8 < relAttPredicates.length; _i8++) { + this.addTriple(current, newSubject, relAttPredicates[_i8], { type: RDFaProcessor.objectURI, value: currentObjectResource }); + } + } + if (revAtt) { + for (var _i9 = 0; _i9 < revAttPredicates.length; _i9++) { + this.addTriple(current, currentObjectResource, revAttPredicates[_i9], { type: RDFaProcessor.objectURI, value: newSubject }); + } + } + } else { + // Sequence Step 10: incomplete triples + if (newSubject && !currentObjectResource && (relAtt || revAtt)) { + currentObjectResource = this.newBlankNode(); + // alert(current.tagName+": generated blank node, newSubject="+newSubject+" currentObjectResource="+currentObjectResource) + } + if (relAtt && inlistAtt) { + for (var _i10 = 0; _i10 < relAttPredicates.length; _i10++) { + var _list2 = listMapping[relAttPredicates[_i10]]; + if (!_list2) { + _list2 = []; + listMapping[predicate] = _list2; + } + // console.log("Adding incomplete list for "+predicate) + incomplete.push({ predicate: relAttPredicates[_i10], list: _list2 }); + } + } else if (relAtt) { + for (var _i11 = 0; _i11 < relAttPredicates.length; _i11++) { + incomplete.push({ predicate: relAttPredicates[_i11], forward: true }); + } + } + if (revAtt) { + for (var _i12 = 0; _i12 < revAttPredicates.length; _i12++) { + incomplete.push({ predicate: revAttPredicates[_i12], forward: false }); + } + } + } + // Step 11: Current property values + if (propertyAtt) { + var datatype = null; + var content = null; + if (datatypeAtt) { + datatype = datatypeAtt.value === '' ? RDFaProcessor.PlainLiteralURI : this.parseTermOrCURIEOrAbsURI(datatypeAtt.value, vocabulary, context.terms, prefixes, base); + if (datetimeAtt && !contentAtt) { + content = datetimeAtt.value; + } else { + content = datatype === RDFaProcessor.XMLLiteralURI || datatype === RDFaProcessor.HTMLLiteralURI ? null : contentAtt ? contentAtt.value : current.textContent; + } + } else if (contentAtt) { + datatype = RDFaProcessor.PlainLiteralURI; + content = contentAtt.value; + } else if (datetimeAtt) { + content = datetimeAtt.value; + datatype = RDFaProcessor.deriveDateTimeType(content); + if (!datatype) { + datatype = RDFaProcessor.PlainLiteralURI; + } + } else if (!relAtt && !revAtt) { + if (resourceAtt) { + content = this.parseSafeCURIEOrCURIEOrURI(resourceAtt.value, prefixes, base); + } + if (!content && hrefAtt) { + content = this.resolveAndNormalize(base, encodeURI(hrefAtt.value)); + } else if (!content && srcAtt) { + content = this.resolveAndNormalize(base, encodeURI(srcAtt.value)); + } + if (content) { + datatype = RDFaProcessor.objectURI; + } + } + if (!datatype) { + if (typeofAtt && !aboutAtt) { + datatype = RDFaProcessor.objectURI; + content = typedResource; + } else { + content = current.textContent; + if (this.inHTMLMode && current.localName === 'time') { + datatype = RDFaProcessor.deriveDateTimeType(content); + } + if (!datatype) { + datatype = RDFaProcessor.PlainLiteralURI; + } + } + } + values = this.tokenize(propertyAtt.value); + for (var _i13 = 0; _i13 < values.length; _i13++) { + var _predicate2 = this.parsePredicate(values[_i13], vocabulary, context.terms, prefixes, base); + if (_predicate2) { + if (inlistAtt) { + var _list3 = listMapping[_predicate2]; + if (!_list3) { + _list3 = []; + listMapping[_predicate2] = _list3; + } + _list3.push(datatype === RDFaProcessor.XMLLiteralURI || datatype === RDFaProcessor.HTMLLiteralURI ? { type: datatype, value: current.childNodes } : { type: datatype ? datatype : RDFaProcessor.PlainLiteralURI, value: content, language: language }); + } else { + if (datatype === RDFaProcessor.XMLLiteralURI || datatype === RDFaProcessor.HTMLLiteralURI) { + this.addTriple(current, newSubject, _predicate2, { type: datatype, value: current.childNodes }); + } else { + this.addTriple(current, newSubject, _predicate2, { type: datatype ? datatype : RDFaProcessor.PlainLiteralURI, value: content, language: language }); + // console.log(newSubject+" "+predicate+"="+content) + } + } + } + } + } + // Sequence Step 12: complete incomplete triples with new subject + if (newSubject && !skip) { + for (var _i14 = 0; _i14 < context.incomplete.length; _i14++) { + if (context.incomplete[_i14].list) { + // console.log("Adding subject "+newSubject+" to list for "+context.incomplete[i].predicate) + // TODO: it is unclear what to do here + context.incomplete[_i14].list.push({ type: RDFaProcessor.objectURI, value: newSubject }); + } else if (context.incomplete[_i14].forward) { + // console.log(current.tagName+": completing forward triple "+context.incomplete[i].predicate+" with object="+newSubject) + this.addTriple(current, context.subject, context.incomplete[_i14].predicate, { type: RDFaProcessor.objectURI, value: newSubject }); + } else { + // console.log(current.tagName+": completing reverse triple with object="+context.subject) + this.addTriple(current, newSubject, context.incomplete[_i14].predicate, { type: RDFaProcessor.objectURI, value: context.subject }); + } + } + } + var childContext = null; + var listSubject = newSubject; + if (skip) { + // TODO: should subject be null? + childContext = this.push(context, context.subject); + // TODO: should the entObject be passed along? If not, then intermediary children will keep properties from being associated with incomplete triples. + // TODO: Verify: if the current baseURI has changed and the parentObject is the parent's base URI, then the baseURI should change + childContext.parentObject = removeHash(current.parentNode.baseURI) === context.parentObject ? removeHash(current.baseURI) : context.parentObject; + childContext.incomplete = context.incomplete; + childContext.language = language; + childContext.prefixes = prefixes; + childContext.vocabulary = vocabulary; + } else { + childContext = this.push(context, newSubject); + childContext.parentObject = currentObjectResource ? currentObjectResource : newSubject ? newSubject : context.subject; + childContext.prefixes = prefixes; + childContext.incomplete = incomplete; + if (currentObjectResource) { + // console.log("Generating new list mapping for "+currentObjectResource) + listSubject = currentObjectResource; + listMapping = {}; + listMappingDifferent = true; + } + childContext.listMapping = listMapping; + childContext.language = language; + childContext.vocabulary = vocabulary; + } + if (listMappingDifferent) { + // console.log("Pushing list parent "+current.localName) + queue.unshift({ parent: current, context: context, subject: listSubject, listMapping: listMapping }); + } + for (var child = current.lastChild; child; child = child.previousSibling) { + if (child.nodeType === Node.ELEMENT_NODE) { + // console.log("Pushing child "+child.localName) + child.baseURI = current.baseURI; + queue.unshift({ current: child, context: childContext }); + } + } + } + if (this.inHTMLMode) { + this.copyProperties(); + } + for (var _i15 = 0; _i15 < this.finishedHandlers.length; _i15++) { + this.finishedHandlers[_i15](node); + } + } + }, { + key: 'push', + value: function push(parent, subject) { + return { + parent: parent, + subject: subject ? subject : parent ? parent.subject : null, + parentObject: null, + incomplete: [], + listMapping: parent ? parent.listMapping : {}, + language: parent ? parent.language : this.language, + prefixes: parent ? parent.prefixes : this.target.graph.prefixes, + terms: parent ? parent.terms : this.target.graph.terms, + vocabulary: parent ? parent.vocabulary : this.vocabulary + }; + } + }, { + key: 'resolveAndNormalize', + value: function resolveAndNormalize(base, uri) { + // console.log("Joining " + uri + " to " + base + " making " + Uri.join(uri, base)) + return Uri.join(uri, base); // @@ normalize? + } + }, { + key: 'setContext', + value: function setContext(node) { + // We only recognized XHTML+RDFa 1.1 if the version is set propertyly + if (node.localName === 'html' && node.getAttribute('version') === 'XHTML+RDFa 1.1') { + this.setXHTMLContext(); + } else if (node.localName === 'html' || node.namespaceURI === 'http://www.w3.org/1999/xhtml') { + if (typeof document !== 'undefined' && document.doctype) { + if (document.doctype.publicId === '-//W3C//DTD XHTML+RDFa 1.0//EN' && document.doctype.systemId === 'http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd') { + console.log('WARNING: RDF 1.0 is not supported. Defaulting to HTML5 mode.'); + this.setHTMLContext(); + } else if (document.doctype.publicId === '-//W3C//DTD XHTML+RDFa 1.1//EN' && document.doctype.systemId === 'http://www.w3.org/MarkUp/DTD/xhtml-rdfa-2.dtd') { + this.setXHTMLContext(); + } else { + this.setHTMLContext(); + } + } else { + this.setHTMLContext(); + } + } else { + this.setXMLContext(); + } + } + }, { + key: 'setHTMLContext', + value: function setHTMLContext() { + this.setInitialContext(); + this.langAttributes = [{ + namespaceURI: 'http://www.w3.org/XML/1998/namespace', + localName: 'lang' + }, { namespaceURI: null, localName: 'lang' }]; + this.inXHTMLMode = false; + this.inHTMLMode = true; + } + }, { + key: 'setInitialContext', + value: function setInitialContext() { + this.vocabulary = null; + // By default, the prefixes are terms are loaded to the RDFa 1.1. standard within the graph constructor + this.langAttributes = [{ + namespaceURI: 'http://www.w3.org/XML/1998/namespace', + localName: 'lang' + }]; + } + }, { + key: 'setXHTMLContext', + value: function setXHTMLContext() { + this.setInitialContext(); + this.inXHTMLMode = true; + this.inHTMLMode = false; + this.langAttributes = [{ + namespaceURI: 'http://www.w3.org/XML/1998/namespace', + localName: 'lang' }, { namespaceURI: null, localName: 'lang' }]; + // From http://www.w3.org/2011/rdfa-context/xhtml-rdfa-1.1 + this.target.graph.terms['alternate'] = 'http://www.w3.org/1999/xhtml/vocab#alternate'; + this.target.graph.terms['appendix'] = 'http://www.w3.org/1999/xhtml/vocab#appendix'; + this.target.graph.terms['bookmark'] = 'http://www.w3.org/1999/xhtml/vocab#bookmark'; + this.target.graph.terms['cite'] = 'http://www.w3.org/1999/xhtml/vocab#cite'; + this.target.graph.terms['chapter'] = 'http://www.w3.org/1999/xhtml/vocab#chapter'; + this.target.graph.terms['contents'] = 'http://www.w3.org/1999/xhtml/vocab#contents'; + this.target.graph.terms['copyright'] = 'http://www.w3.org/1999/xhtml/vocab#copyright'; + this.target.graph.terms['first'] = 'http://www.w3.org/1999/xhtml/vocab#first'; + this.target.graph.terms['glossary'] = 'http://www.w3.org/1999/xhtml/vocab#glossary'; + this.target.graph.terms['help'] = 'http://www.w3.org/1999/xhtml/vocab#help'; + this.target.graph.terms['icon'] = 'http://www.w3.org/1999/xhtml/vocab#icon'; + this.target.graph.terms['index'] = 'http://www.w3.org/1999/xhtml/vocab#index'; + this.target.graph.terms['last'] = 'http://www.w3.org/1999/xhtml/vocab#last'; + this.target.graph.terms['license'] = 'http://www.w3.org/1999/xhtml/vocab#license'; + this.target.graph.terms['meta'] = 'http://www.w3.org/1999/xhtml/vocab#meta'; + this.target.graph.terms['next'] = 'http://www.w3.org/1999/xhtml/vocab#next'; + this.target.graph.terms['prev'] = 'http://www.w3.org/1999/xhtml/vocab#prev'; + this.target.graph.terms['previous'] = 'http://www.w3.org/1999/xhtml/vocab#previous'; + this.target.graph.terms['section'] = 'http://www.w3.org/1999/xhtml/vocab#section'; + this.target.graph.terms['stylesheet'] = 'http://www.w3.org/1999/xhtml/vocab#stylesheet'; + this.target.graph.terms['subsection'] = 'http://www.w3.org/1999/xhtml/vocab#subsection'; + this.target.graph.terms['start'] = 'http://www.w3.org/1999/xhtml/vocab#start'; + this.target.graph.terms['top'] = 'http://www.w3.org/1999/xhtml/vocab#top'; + this.target.graph.terms['up'] = 'http://www.w3.org/1999/xhtml/vocab#up'; + this.target.graph.terms['p3pv1'] = 'http://www.w3.org/1999/xhtml/vocab#p3pv1'; + // other + this.target.graph.terms['related'] = 'http://www.w3.org/1999/xhtml/vocab#related'; + this.target.graph.terms['role'] = 'http://www.w3.org/1999/xhtml/vocab#role'; + this.target.graph.terms['transformation'] = 'http://www.w3.org/1999/xhtml/vocab#transformation'; + } + }, { + key: 'setXMLContext', + value: function setXMLContext() { + this.setInitialContext(); + this.inXHTMLMode = false; + this.inHTMLMode = false; + } + }, { + key: 'tokenize', + value: function tokenize(str) { + return this.trim(str).split(/\s+/); + } + }, { + key: 'toRDFNodeObject', + value: function toRDFNodeObject(x) { + if (typeof x === 'undefined') return undefined; + if (typeof x === 'string') { + if (x.substring(0, 2) === '_:') { + if (typeof this.blankNodes[x.substring(2)] === 'undefined') { + this.blankNodes[x.substring(2)] = new BlankNode(x.substring(2)); + } + return this.blankNodes[x.substring(2)]; + } + return rdf.namedNode(x); + } + switch (x.type) { + case RDFaProcessor.objectURI: + if (x.value.substring(0, 2) === '_:') { + if (typeof this.blankNodes[x.value.substring(2)] === 'undefined') { + this.blankNodes[x.value.substring(2)] = new BlankNode(x.value.substring(2)); + } + return this.blankNodes[x.value.substring(2)]; + } + return rdf.namedNode(x.value); + case RDFaProcessor.PlainLiteralURI: + return new Literal(x.value, x.language || ''); + case RDFaProcessor.XMLLiteralURI: + case RDFaProcessor.HTMLLiteralURI: + var string = ''; + Object.keys(x.value).forEach(function (i) { + string += Util.domToString(x.value[i], this.htmlOptions); + }); + return new Literal(string, '', new NamedNode(x.type)); + default: + return new Literal(x.value, '', new NamedNode(x.type)); + } + } + }, { + key: 'trim', + value: function trim(str) { + return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); + } + }], [{ + key: 'parseRDFaDOM', + value: function parseRDFaDOM(dom, kb, base) { + var p = new RDFaProcessor(kb, { 'base': base }); + dom.baseURI = base; + p.process(dom); + } + }]); + + return RDFaProcessor; +}(); + +RDFaProcessor.XMLLiteralURI = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral'; +RDFaProcessor.HTMLLiteralURI = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#HTML'; +RDFaProcessor.PlainLiteralURI = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#PlainLiteral'; +RDFaProcessor.objectURI = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#object'; +RDFaProcessor.typeURI = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'; +RDFaProcessor.nameChar = '[-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\u10000-\uEFFFF.0-9\xB7\u0300-\u036F\u203F-\u2040]'; +RDFaProcessor.nameStartChar = '[A-Za-z\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u0131\u0134-\u013E\u0141-\u0148\u014A-\u017E\u0180-\u01C3\u01CD-\u01F0\u01F4-\u01F5\u01FA-\u0217\u0250-\u02A8\u02BB-\u02C1\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03D6\u03DA\u03DC\u03DE\u03E0\u03E2-\u03F3\u0401-\u040C\u040E-\u044F\u0451-\u045C\u045E-\u0481\u0490-\u04C4\u04C7-\u04C8\u04CB-\u04CC\u04D0-\u04EB\u04EE-\u04F5\u04F8-\u04F9\u0531-\u0556\u0559\u0561-\u0586\u05D0-\u05EA\u05F0-\u05F2\u0621-\u063A\u0641-\u064A\u0671-\u06B7\u06BA-\u06BE\u06C0-\u06CE\u06D0-\u06D3\u06D5\u06E5-\u06E6\u0905-\u0939\u093D\u0958-\u0961\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8B\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AE0\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B36-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB5\u0BB7-\u0BB9\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CDE\u0CE0-\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D60-\u0D61\u0E01-\u0E2E\u0E30\u0E32-\u0E33\u0E40-\u0E45\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EAE\u0EB0\u0EB2-\u0EB3\u0EBD\u0EC0-\u0EC4\u0F40-\u0F47\u0F49-\u0F69\u10A0-\u10C5\u10D0-\u10F6\u1100\u1102-\u1103\u1105-\u1107\u1109\u110B-\u110C\u110E-\u1112\u113C\u113E\u1140\u114C\u114E\u1150\u1154-\u1155\u1159\u115F-\u1161\u1163\u1165\u1167\u1169\u116D-\u116E\u1172-\u1173\u1175\u119E\u11A8\u11AB\u11AE-\u11AF\u11B7-\u11B8\u11BA\u11BC-\u11C2\u11EB\u11F0\u11F9\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2126\u212A-\u212B\u212E\u2180-\u2182\u3041-\u3094\u30A1-\u30FA\u3105-\u312C\uAC00-\uD7A3\u4E00-\u9FA5\u3007\u3021-\u3029_]'; +RDFaProcessor.NCNAME = new RegExp('^' + RDFaProcessor.nameStartChar + RDFaProcessor.nameChar + '*$'); + +/* +RDFaProcessor.prototype.resolveAndNormalize = function(base,href) { + var u = base.resolve(href) + var parsed = this.parseURI(u) + parsed.normalize() + return parsed.spec +} +*/ + +RDFaProcessor.dateTimeTypes = [{ pattern: /-?P(?:[0-9]+Y)?(?:[0-9]+M)?(?:[0-9]+D)?(?:T(?:[0-9]+H)?(?:[0-9]+M)?(?:[0-9]+(?:\.[0-9]+)?S)?)?/, + type: 'http://www.w3.org/2001/XMLSchema#duration' }, { pattern: /-?(?:[1-9][0-9][0-9][0-9]|0[1-9][0-9][0-9]|00[1-9][0-9]|000[1-9])-[0-9][0-9]-[0-9][0-9]T(?:[0-1][0-9]|2[0-4]):[0-5][0-9]:[0-5][0-9](?:\.[0-9]+)?(?:Z|[+\-][0-9][0-9]:[0-9][0-9])?/, + type: 'http://www.w3.org/2001/XMLSchema#dateTime' }, { pattern: /-?(?:[1-9][0-9][0-9][0-9]|0[1-9][0-9][0-9]|00[1-9][0-9]|000[1-9])-[0-9][0-9]-[0-9][0-9](?:Z|[+\-][0-9][0-9]:[0-9][0-9])?/, + type: 'http://www.w3.org/2001/XMLSchema#date' }, { pattern: /(?:[0-1][0-9]|2[0-4]):[0-5][0-9]:[0-5][0-9](?:\.[0-9]+)?(?:Z|[+\-][0-9][0-9]:[0-9][0-9])?/, + type: 'http://www.w3.org/2001/XMLSchema#time' }, { pattern: /-?(?:[1-9][0-9][0-9][0-9]|0[1-9][0-9][0-9]|00[1-9][0-9]|000[1-9])-[0-9][0-9]/, + type: 'http://www.w3.org/2001/XMLSchema#gYearMonth' }, { pattern: /-?[1-9][0-9][0-9][0-9]|0[1-9][0-9][0-9]|00[1-9][0-9]|000[1-9]/, + type: 'http://www.w3.org/2001/XMLSchema#gYear' }]; + module.exports = RDFaProcessor; },{"./blank-node":51,"./data-factory":55,"./literal":63,"./named-node":66,"./uri":81,"./util":82}],74:[function(_dereq_,module,exports){ -'use strict'; - -/** - * @fileoverview - * RDF/XML PARSER - * - * Version 0.1 - * Parser believed to be in full positive RDF/XML parsing compliance - * with the possible exception of handling deprecated RDF attributes - * appropriately. Parser is believed to comply fully with other W3C - * and industry standards where appropriate (DOM, ECMAScript, &c.) - * - * Author: David Sheets - * - * W3C® SOFTWARE NOTICE AND LICENSE - * http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 - * This work (and included software, documentation such as READMEs, or - * other related items) is being provided by the copyright holders under - * the following license. By obtaining, using and/or copying this work, - * you (the licensee) agree that you have read, understood, and will - * comply with the following terms and conditions. - * - * Permission to copy, modify, and distribute this software and its - * documentation, with or without modification, for any purpose and - * without fee or royalty is hereby granted, provided that you include - * the following on ALL copies of the software and documentation or - * portions thereof, including modifications: - * - * 1. The full text of this NOTICE in a location viewable to users of - * the redistributed or derivative work. - * 2. Any pre-existing intellectual property disclaimers, notices, or terms and - * conditions. If none exist, the W3C Software Short Notice should be - * included (hypertext is preferred, text is permitted) within the body - * of any redistributed or derivative code. - * 3. Notice of any changes or modifications to the files, including the - * date changes were made. (We recommend you provide URIs to the location - * from which the code is derived.) - * - * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT - * HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS - * FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR - * DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, - * TRADEMARKS OR OTHER RIGHTS. - * - * COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL - * OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR - * DOCUMENTATION. - * - * The name and trademarks of copyright holders may NOT be used in - * advertising or publicity pertaining to the software without specific, - * written prior permission. Title to copyright in this software and any - * associated documentation will at all times remain with copyright - * holders. - */ -/** - * @class Class defining an RDFParser resource object tied to an RDFStore - * - * @author David Sheets - * @version 0.1 - * - * @constructor - * @param {RDFStore} store An RDFStore object - */ -var uriUtil = _dereq_('./uri'); - -var RDFParser = function RDFParser(store) { - var RDFParser = {}; - - /** Standard namespaces that we know how to handle @final - * @member RDFParser - */ - RDFParser.ns = { 'RDF': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'RDFS': 'http://www.w3.org/2000/01/rdf-schema#' }; - - /** DOM Level 2 node type magic numbers @final - * @member RDFParser - */ - RDFParser.nodeType = { 'ELEMENT': 1, 'ATTRIBUTE': 2, 'TEXT': 3, - 'CDATA_SECTION': 4, 'ENTITY_REFERENCE': 5, - 'ENTITY': 6, 'PROCESSING_INSTRUCTION': 7, - 'COMMENT': 8, 'DOCUMENT': 9, 'DOCUMENT_TYPE': 10, - 'DOCUMENT_FRAGMENT': 11, 'NOTATION': 12 }; - - /** - * Frame class for namespace and base URI lookups - * Base lookups will always resolve because the parser knows - * the default base. - * - * @private - */ - - this.frameFactory = function (parser, parent, element) { - return { 'NODE': 1, 'ARC': 2, 'parent': parent, 'parser': parser, 'store': parser.store, 'element': element, - 'lastChild': 0, 'base': null, 'lang': null, 'node': null, 'nodeType': null, 'listIndex': 1, 'rdfid': null, 'datatype': null, 'collection': false, /** Terminate the frame and notify the store that we're done */ - 'terminateFrame': function terminateFrame() { - if (this.collection) { - this.node.close(); - } - }, /** Add a symbol of a certain type to the this frame */'addSymbol': function addSymbol(type, uri) { - uri = uriUtil.join(uri, this.base); - this.node = this.store.sym(uri); - - this.nodeType = type; - }, /** Load any constructed triples into the store */'loadTriple': function loadTriple() { - if (this.parent.parent.collection) { - this.parent.parent.node.append(this.node); - } else { - this.store.add(this.parent.parent.node, this.parent.node, this.node, this.parser.why); - } - if (this.parent.rdfid != null) { - // reify - var triple = this.store.sym(uriUtil.join('#' + this.parent.rdfid, this.base)); - this.store.add(triple, this.store.sym(RDFParser.ns.RDF + 'type'), this.store.sym(RDFParser.ns.RDF + 'Statement'), this.parser.why); - this.store.add(triple, this.store.sym(RDFParser.ns.RDF + 'subject'), this.parent.parent.node, this.parser.why); - this.store.add(triple, this.store.sym(RDFParser.ns.RDF + 'predicate'), this.parent.node, this.parser.why); - - this.store.add(triple, this.store.sym(RDFParser.ns.RDF + 'object'), this.node, this.parser.why); - } - }, /** Check if it's OK to load a triple */'isTripleToLoad': function isTripleToLoad() { - return this.parent != null && this.parent.parent != null && this.nodeType === this.NODE && this.parent.nodeType === this.ARC && this.parent.parent.nodeType === this.NODE; - }, /** Add a symbolic node to this frame */'addNode': function addNode(uri) { - this.addSymbol(this.NODE, uri); - if (this.isTripleToLoad()) { - this.loadTriple(); - } - }, /** Add a collection node to this frame */'addCollection': function addCollection() { - this.nodeType = this.NODE; - this.node = this.store.collection(); - this.collection = true; - if (this.isTripleToLoad()) { - this.loadTriple(); - } - }, /** Add a collection arc to this frame */'addCollectionArc': function addCollectionArc() { - this.nodeType = this.ARC; - }, /** Add a bnode to this frame */'addBNode': function addBNode(id) { - if (id != null) { - if (this.parser.bnodes[id] != null) { - this.node = this.parser.bnodes[id]; - } else { - this.node = this.parser.bnodes[id] = this.store.bnode(); - } - } else { - this.node = this.store.bnode(); - } - this.nodeType = this.NODE; - if (this.isTripleToLoad()) { - this.loadTriple(); - } - }, /** Add an arc or property to this frame */'addArc': function addArc(uri) { - if (uri === RDFParser.ns.RDF + 'li') { - uri = RDFParser.ns.RDF + '_' + this.parent.listIndex; - this.parent.listIndex++; - } - - this.addSymbol(this.ARC, uri); - }, /** Add a literal to this frame */'addLiteral': function addLiteral(value) { - if (this.parent.datatype) { - this.node = this.store.literal(value, '', this.store.sym(this.parent.datatype)); - } else { - this.node = this.store.literal(value, this.lang); - } - this.nodeType = this.NODE; - if (this.isTripleToLoad()) { - this.loadTriple(); - } - } - }; - }; - - // from the OpenLayers source .. needed to get around IE problems. - this.getAttributeNodeNS = function (node, uri, name) { - var attributeNode = null; - if (node.getAttributeNodeNS) { - attributeNode = node.getAttributeNodeNS(uri, name); - } else { - var attributes = node.attributes; - var potentialNode, fullName; - for (var i = 0; i < attributes.length; ++i) { - potentialNode = attributes[i]; - if (potentialNode.namespaceURI === uri) { - fullName = potentialNode.prefix ? potentialNode.prefix + ':' + name : name; - if (fullName === potentialNode.nodeName) { - attributeNode = potentialNode; - break; - } - } - } - } - return attributeNode; - }; - - /** Our triple store reference @private */ - - this.store = store; /** Our identified blank nodes @private */ - this.bnodes = {}; /** A context for context-aware stores @private */ - this.why = null; /** Reification flag */ - this.reify = false; - - /** - * Build our initial scope frame and parse the DOM into triples - * @param {DOMTree} document The DOM to parse - * @param {String} base The base URL to use - * @param {Object} why The context to which this resource belongs - */ - - this.parse = function (document, base, why) { - var children = document.childNodes; // clean up for the next run - this.cleanParser(); // figure out the root element - var root; - if (document.nodeType === RDFParser.nodeType.DOCUMENT) { - for (var c = 0; c < children.length; c++) { - if (children[c].nodeType === RDFParser.nodeType.ELEMENT) { - root = children[c]; - break; - } - } - } else if (document.nodeType === RDFParser.nodeType.ELEMENT) { - root = document; - } else { - throw new Error("RDFParser: can't find root in " + base + '. Halting. '); - // return false - } - this.why = why; // our topmost frame - var f = this.frameFactory(this); - this.base = base; - f.base = base; - f.lang = null; // was '' but can't have langs like that 2015 (!) - this.parseDOM(this.buildFrame(f, root)); - return true; - }; - - this.parseDOM = function (frame) { - // a DOM utility function used in parsing - var rdfid; - var elementURI = function (el) { - var result = ''; - if (el.namespaceURI == null) { - throw new Error('RDF/XML syntax error: No namespace for ' + el.localName + ' in ' + this.base); - } - if (el.namespaceURI) { - result = result + el.namespaceURI; - } - if (el.localName) { - result = result + el.localName; - } else if (el.nodeName) { - if (el.nodeName.indexOf(':') >= 0) result = result + el.nodeName.split(':')[1];else result = result + el.nodeName; - } - return result; - }.bind(this); - var dig = true; // if we'll dig down in the tree on the next iter - while (frame.parent) { - var dom = frame.element; - var attrs = dom.attributes; - if (dom.nodeType === RDFParser.nodeType.TEXT || dom.nodeType === RDFParser.nodeType.CDATA_SECTION) { - // we have a literal - if (frame.parent.nodeType === frame.NODE) { - // must have had attributes, store as rdf:value - frame.addArc(RDFParser.ns.RDF + 'value'); - frame = this.buildFrame(frame); - } - frame.addLiteral(dom.nodeValue); - } else if (elementURI(dom) !== RDFParser.ns.RDF + 'RDF') { - // not root - if (frame.parent && frame.parent.collection) { - // we're a collection element - frame.addCollectionArc(); - frame = this.buildFrame(frame, frame.element); - frame.parent.element = null; - } - if (!frame.parent || !frame.parent.nodeType || frame.parent.nodeType === frame.ARC) { - // we need a node - var about = this.getAttributeNodeNS(dom, RDFParser.ns.RDF, 'about'); - rdfid = this.getAttributeNodeNS(dom, RDFParser.ns.RDF, 'ID'); - if (about && rdfid) { - throw new Error('RDFParser: ' + dom.nodeName + ' has both rdf:id and rdf:about.' + ' Halting. Only one of these' + ' properties may be specified on a' + ' node.'); - } - if (!about && rdfid) { - frame.addNode('#' + rdfid.nodeValue); - dom.removeAttributeNode(rdfid); - } else if (about == null && rdfid == null) { - var bnid = this.getAttributeNodeNS(dom, RDFParser.ns.RDF, 'nodeID'); - if (bnid) { - frame.addBNode(bnid.nodeValue); - dom.removeAttributeNode(bnid); - } else { - frame.addBNode(); - } - } else { - frame.addNode(about.nodeValue); - dom.removeAttributeNode(about); - } - // Typed nodes - var rdftype = this.getAttributeNodeNS(dom, RDFParser.ns.RDF, 'type'); - if (RDFParser.ns.RDF + 'Description' !== elementURI(dom)) { - rdftype = { 'nodeValue': elementURI(dom) }; - } - if (rdftype != null) { - this.store.add(frame.node, this.store.sym(RDFParser.ns.RDF + 'type'), this.store.sym(uriUtil.join(rdftype.nodeValue, frame.base)), this.why); - if (rdftype.nodeName) { - dom.removeAttributeNode(rdftype); - } - } - // Property Attributes - for (var x = attrs.length - 1; x >= 0; x--) { - this.store.add(frame.node, this.store.sym(elementURI(attrs[x])), this.store.literal(attrs[x].nodeValue, frame.lang), this.why); - } - } else { - // we should add an arc (or implicit bnode+arc) - frame.addArc(elementURI(dom)); // save the arc's rdf:ID if it has one - if (this.reify) { - rdfid = this.getAttributeNodeNS(dom, RDFParser.ns.RDF, 'ID'); - if (rdfid) { - frame.rdfid = rdfid.nodeValue; - dom.removeAttributeNode(rdfid); - } - } - var parsetype = this.getAttributeNodeNS(dom, RDFParser.ns.RDF, 'parseType'); - var datatype = this.getAttributeNodeNS(dom, RDFParser.ns.RDF, 'datatype'); - if (datatype) { - frame.datatype = datatype.nodeValue; - dom.removeAttributeNode(datatype); - } - if (parsetype) { - var nv = parsetype.nodeValue; - if (nv === 'Literal') { - frame.datatype = RDFParser.ns.RDF + 'XMLLiteral'; // (this.buildFrame(frame)).addLiteral(dom) - // should work but doesn't - frame = this.buildFrame(frame); - frame.addLiteral(dom); - dig = false; - } else if (nv === 'Resource') { - frame = this.buildFrame(frame, frame.element); - frame.parent.element = null; - frame.addBNode(); - } else if (nv === 'Collection') { - frame = this.buildFrame(frame, frame.element); - frame.parent.element = null; - frame.addCollection(); - } - dom.removeAttributeNode(parsetype); - } - if (attrs.length !== 0) { - var resource = this.getAttributeNodeNS(dom, RDFParser.ns.RDF, 'resource'); - var bnid2 = this.getAttributeNodeNS(dom, RDFParser.ns.RDF, 'nodeID'); - frame = this.buildFrame(frame); - if (resource) { - frame.addNode(resource.nodeValue); - dom.removeAttributeNode(resource); - } else { - if (bnid2) { - frame.addBNode(bnid2.nodeValue); - dom.removeAttributeNode(bnid2); - } else { - frame.addBNode(); - } - } - for (var x1 = attrs.length - 1; x1 >= 0; x1--) { - var f = this.buildFrame(frame); - f.addArc(elementURI(attrs[x1])); - if (elementURI(attrs[x1]) === RDFParser.ns.RDF + 'type') { - this.buildFrame(f).addNode(attrs[x1].nodeValue); - } else { - this.buildFrame(f).addLiteral(attrs[x1].nodeValue); - } - } - } else if (dom.childNodes.length === 0) { - this.buildFrame(frame).addLiteral(''); - } - } - } // rdf:RDF - // dig dug - dom = frame.element; - while (frame.parent) { - var pframe = frame; - while (dom == null) { - frame = frame.parent; - dom = frame.element; - } - var candidate = dom.childNodes && dom.childNodes[frame.lastChild]; - if (!candidate || !dig) { - frame.terminateFrame(); - if (!(frame = frame.parent)) { - break; - } // done - dom = frame.element; - dig = true; - } else if (candidate.nodeType !== RDFParser.nodeType.ELEMENT && candidate.nodeType !== RDFParser.nodeType.TEXT && candidate.nodeType !== RDFParser.nodeType.CDATA_SECTION || (candidate.nodeType === RDFParser.nodeType.TEXT || candidate.nodeType === RDFParser.nodeType.CDATA_SECTION) && dom.childNodes.length !== 1) { - frame.lastChild++; - } else { - // not a leaf - frame.lastChild++; - frame = this.buildFrame(pframe, dom.childNodes[frame.lastChild - 1]); - break; - } - } - } // while - }; - - /** - * Cleans out state from a previous parse run - * @private - */ - this.cleanParser = function () { - this.bnodes = {}; - this.why = null; - }; - - /** - * Builds scope frame - * @private - */ - this.buildFrame = function (parent, element) { - var frame = this.frameFactory(this, parent, element); - if (parent) { - frame.base = parent.base; - frame.lang = parent.lang; - } - if (!element || element.nodeType === RDFParser.nodeType.TEXT || element.nodeType === RDFParser.nodeType.CDATA_SECTION) { - return frame; - } - var attrs = element.attributes; - var base = element.getAttributeNode('xml:base'); - if (base != null) { - frame.base = base.nodeValue; - element.removeAttribute('xml:base'); - } - var lang = element.getAttributeNode('xml:lang'); - if (lang != null) { - frame.lang = lang.nodeValue; - element.removeAttribute('xml:lang'); - } - // remove all extraneous xml and xmlns attributes - for (var x = attrs.length - 1; x >= 0; x--) { - if (attrs[x].nodeName.substr(0, 3) === 'xml') { - if (attrs[x].name.slice(0, 6) === 'xmlns:') { - var uri = attrs[x].nodeValue; // alert('base for namespac attr:'+this.base) - if (this.base) uri = uriUtil.join(uri, this.base); - this.store.setPrefixForURI(attrs[x].name.slice(6), uri); - } - // alert('rdfparser: xml atribute: '+attrs[x].name) //@@ - element.removeAttributeNode(attrs[x]); - } - } - return frame; - }; -}; - +'use strict'; + +/** + * @fileoverview + * RDF/XML PARSER + * + * Version 0.1 + * Parser believed to be in full positive RDF/XML parsing compliance + * with the possible exception of handling deprecated RDF attributes + * appropriately. Parser is believed to comply fully with other W3C + * and industry standards where appropriate (DOM, ECMAScript, &c.) + * + * Author: David Sheets + * + * W3C® SOFTWARE NOTICE AND LICENSE + * http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 + * This work (and included software, documentation such as READMEs, or + * other related items) is being provided by the copyright holders under + * the following license. By obtaining, using and/or copying this work, + * you (the licensee) agree that you have read, understood, and will + * comply with the following terms and conditions. + * + * Permission to copy, modify, and distribute this software and its + * documentation, with or without modification, for any purpose and + * without fee or royalty is hereby granted, provided that you include + * the following on ALL copies of the software and documentation or + * portions thereof, including modifications: + * + * 1. The full text of this NOTICE in a location viewable to users of + * the redistributed or derivative work. + * 2. Any pre-existing intellectual property disclaimers, notices, or terms and + * conditions. If none exist, the W3C Software Short Notice should be + * included (hypertext is preferred, text is permitted) within the body + * of any redistributed or derivative code. + * 3. Notice of any changes or modifications to the files, including the + * date changes were made. (We recommend you provide URIs to the location + * from which the code is derived.) + * + * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT + * HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS + * FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR + * DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, + * TRADEMARKS OR OTHER RIGHTS. + * + * COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL + * OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR + * DOCUMENTATION. + * + * The name and trademarks of copyright holders may NOT be used in + * advertising or publicity pertaining to the software without specific, + * written prior permission. Title to copyright in this software and any + * associated documentation will at all times remain with copyright + * holders. + */ +/** + * @class Class defining an RDFParser resource object tied to an RDFStore + * + * @author David Sheets + * @version 0.1 + * + * @constructor + * @param {RDFStore} store An RDFStore object + */ +var uriUtil = _dereq_('./uri'); + +var RDFParser = function RDFParser(store) { + var RDFParser = {}; + + /** Standard namespaces that we know how to handle @final + * @member RDFParser + */ + RDFParser.ns = { 'RDF': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'RDFS': 'http://www.w3.org/2000/01/rdf-schema#' }; + + /** DOM Level 2 node type magic numbers @final + * @member RDFParser + */ + RDFParser.nodeType = { 'ELEMENT': 1, 'ATTRIBUTE': 2, 'TEXT': 3, + 'CDATA_SECTION': 4, 'ENTITY_REFERENCE': 5, + 'ENTITY': 6, 'PROCESSING_INSTRUCTION': 7, + 'COMMENT': 8, 'DOCUMENT': 9, 'DOCUMENT_TYPE': 10, + 'DOCUMENT_FRAGMENT': 11, 'NOTATION': 12 }; + + /** + * Frame class for namespace and base URI lookups + * Base lookups will always resolve because the parser knows + * the default base. + * + * @private + */ + + this.frameFactory = function (parser, parent, element) { + return { 'NODE': 1, 'ARC': 2, 'parent': parent, 'parser': parser, 'store': parser.store, 'element': element, + 'lastChild': 0, 'base': null, 'lang': null, 'node': null, 'nodeType': null, 'listIndex': 1, 'rdfid': null, 'datatype': null, 'collection': false, /** Terminate the frame and notify the store that we're done */ + 'terminateFrame': function terminateFrame() { + if (this.collection) { + this.node.close(); + } + }, /** Add a symbol of a certain type to the this frame */'addSymbol': function addSymbol(type, uri) { + uri = uriUtil.join(uri, this.base); + this.node = this.store.sym(uri); + + this.nodeType = type; + }, /** Load any constructed triples into the store */'loadTriple': function loadTriple() { + if (this.parent.parent.collection) { + this.parent.parent.node.append(this.node); + } else { + this.store.add(this.parent.parent.node, this.parent.node, this.node, this.parser.why); + } + if (this.parent.rdfid != null) { + // reify + var triple = this.store.sym(uriUtil.join('#' + this.parent.rdfid, this.base)); + this.store.add(triple, this.store.sym(RDFParser.ns.RDF + 'type'), this.store.sym(RDFParser.ns.RDF + 'Statement'), this.parser.why); + this.store.add(triple, this.store.sym(RDFParser.ns.RDF + 'subject'), this.parent.parent.node, this.parser.why); + this.store.add(triple, this.store.sym(RDFParser.ns.RDF + 'predicate'), this.parent.node, this.parser.why); + + this.store.add(triple, this.store.sym(RDFParser.ns.RDF + 'object'), this.node, this.parser.why); + } + }, /** Check if it's OK to load a triple */'isTripleToLoad': function isTripleToLoad() { + return this.parent != null && this.parent.parent != null && this.nodeType === this.NODE && this.parent.nodeType === this.ARC && this.parent.parent.nodeType === this.NODE; + }, /** Add a symbolic node to this frame */'addNode': function addNode(uri) { + this.addSymbol(this.NODE, uri); + if (this.isTripleToLoad()) { + this.loadTriple(); + } + }, /** Add a collection node to this frame */'addCollection': function addCollection() { + this.nodeType = this.NODE; + this.node = this.store.collection(); + this.collection = true; + if (this.isTripleToLoad()) { + this.loadTriple(); + } + }, /** Add a collection arc to this frame */'addCollectionArc': function addCollectionArc() { + this.nodeType = this.ARC; + }, /** Add a bnode to this frame */'addBNode': function addBNode(id) { + if (id != null) { + if (this.parser.bnodes[id] != null) { + this.node = this.parser.bnodes[id]; + } else { + this.node = this.parser.bnodes[id] = this.store.bnode(); + } + } else { + this.node = this.store.bnode(); + } + this.nodeType = this.NODE; + if (this.isTripleToLoad()) { + this.loadTriple(); + } + }, /** Add an arc or property to this frame */'addArc': function addArc(uri) { + if (uri === RDFParser.ns.RDF + 'li') { + uri = RDFParser.ns.RDF + '_' + this.parent.listIndex; + this.parent.listIndex++; + } + + this.addSymbol(this.ARC, uri); + }, /** Add a literal to this frame */'addLiteral': function addLiteral(value) { + if (this.parent.datatype) { + this.node = this.store.literal(value, '', this.store.sym(this.parent.datatype)); + } else { + this.node = this.store.literal(value, this.lang); + } + this.nodeType = this.NODE; + if (this.isTripleToLoad()) { + this.loadTriple(); + } + } + }; + }; + + // from the OpenLayers source .. needed to get around IE problems. + this.getAttributeNodeNS = function (node, uri, name) { + var attributeNode = null; + if (node.getAttributeNodeNS) { + attributeNode = node.getAttributeNodeNS(uri, name); + } else { + var attributes = node.attributes; + var potentialNode, fullName; + for (var i = 0; i < attributes.length; ++i) { + potentialNode = attributes[i]; + if (potentialNode.namespaceURI === uri) { + fullName = potentialNode.prefix ? potentialNode.prefix + ':' + name : name; + if (fullName === potentialNode.nodeName) { + attributeNode = potentialNode; + break; + } + } + } + } + return attributeNode; + }; + + /** Our triple store reference @private */ + + this.store = store; /** Our identified blank nodes @private */ + this.bnodes = {}; /** A context for context-aware stores @private */ + this.why = null; /** Reification flag */ + this.reify = false; + + /** + * Build our initial scope frame and parse the DOM into triples + * @param {DOMTree} document The DOM to parse + * @param {String} base The base URL to use + * @param {Object} why The context to which this resource belongs + */ + + this.parse = function (document, base, why) { + var children = document.childNodes; // clean up for the next run + this.cleanParser(); // figure out the root element + var root; + if (document.nodeType === RDFParser.nodeType.DOCUMENT) { + for (var c = 0; c < children.length; c++) { + if (children[c].nodeType === RDFParser.nodeType.ELEMENT) { + root = children[c]; + break; + } + } + } else if (document.nodeType === RDFParser.nodeType.ELEMENT) { + root = document; + } else { + throw new Error("RDFParser: can't find root in " + base + '. Halting. '); + // return false + } + this.why = why; // our topmost frame + var f = this.frameFactory(this); + this.base = base; + f.base = base; + f.lang = null; // was '' but can't have langs like that 2015 (!) + this.parseDOM(this.buildFrame(f, root)); + return true; + }; + + this.parseDOM = function (frame) { + // a DOM utility function used in parsing + var rdfid; + var elementURI = function (el) { + var result = ''; + if (el.namespaceURI == null) { + throw new Error('RDF/XML syntax error: No namespace for ' + el.localName + ' in ' + this.base); + } + if (el.namespaceURI) { + result = result + el.namespaceURI; + } + if (el.localName) { + result = result + el.localName; + } else if (el.nodeName) { + if (el.nodeName.indexOf(':') >= 0) result = result + el.nodeName.split(':')[1];else result = result + el.nodeName; + } + return result; + }.bind(this); + var dig = true; // if we'll dig down in the tree on the next iter + while (frame.parent) { + var dom = frame.element; + var attrs = dom.attributes; + if (dom.nodeType === RDFParser.nodeType.TEXT || dom.nodeType === RDFParser.nodeType.CDATA_SECTION) { + // we have a literal + if (frame.parent.nodeType === frame.NODE) { + // must have had attributes, store as rdf:value + frame.addArc(RDFParser.ns.RDF + 'value'); + frame = this.buildFrame(frame); + } + frame.addLiteral(dom.nodeValue); + } else if (elementURI(dom) !== RDFParser.ns.RDF + 'RDF') { + // not root + if (frame.parent && frame.parent.collection) { + // we're a collection element + frame.addCollectionArc(); + frame = this.buildFrame(frame, frame.element); + frame.parent.element = null; + } + if (!frame.parent || !frame.parent.nodeType || frame.parent.nodeType === frame.ARC) { + // we need a node + var about = this.getAttributeNodeNS(dom, RDFParser.ns.RDF, 'about'); + rdfid = this.getAttributeNodeNS(dom, RDFParser.ns.RDF, 'ID'); + if (about && rdfid) { + throw new Error('RDFParser: ' + dom.nodeName + ' has both rdf:id and rdf:about.' + ' Halting. Only one of these' + ' properties may be specified on a' + ' node.'); + } + if (!about && rdfid) { + frame.addNode('#' + rdfid.nodeValue); + dom.removeAttributeNode(rdfid); + } else if (about == null && rdfid == null) { + var bnid = this.getAttributeNodeNS(dom, RDFParser.ns.RDF, 'nodeID'); + if (bnid) { + frame.addBNode(bnid.nodeValue); + dom.removeAttributeNode(bnid); + } else { + frame.addBNode(); + } + } else { + frame.addNode(about.nodeValue); + dom.removeAttributeNode(about); + } + // Typed nodes + var rdftype = this.getAttributeNodeNS(dom, RDFParser.ns.RDF, 'type'); + if (RDFParser.ns.RDF + 'Description' !== elementURI(dom)) { + rdftype = { 'nodeValue': elementURI(dom) }; + } + if (rdftype != null) { + this.store.add(frame.node, this.store.sym(RDFParser.ns.RDF + 'type'), this.store.sym(uriUtil.join(rdftype.nodeValue, frame.base)), this.why); + if (rdftype.nodeName) { + dom.removeAttributeNode(rdftype); + } + } + // Property Attributes + for (var x = attrs.length - 1; x >= 0; x--) { + this.store.add(frame.node, this.store.sym(elementURI(attrs[x])), this.store.literal(attrs[x].nodeValue, frame.lang), this.why); + } + } else { + // we should add an arc (or implicit bnode+arc) + frame.addArc(elementURI(dom)); // save the arc's rdf:ID if it has one + if (this.reify) { + rdfid = this.getAttributeNodeNS(dom, RDFParser.ns.RDF, 'ID'); + if (rdfid) { + frame.rdfid = rdfid.nodeValue; + dom.removeAttributeNode(rdfid); + } + } + var parsetype = this.getAttributeNodeNS(dom, RDFParser.ns.RDF, 'parseType'); + var datatype = this.getAttributeNodeNS(dom, RDFParser.ns.RDF, 'datatype'); + if (datatype) { + frame.datatype = datatype.nodeValue; + dom.removeAttributeNode(datatype); + } + if (parsetype) { + var nv = parsetype.nodeValue; + if (nv === 'Literal') { + frame.datatype = RDFParser.ns.RDF + 'XMLLiteral'; // (this.buildFrame(frame)).addLiteral(dom) + // should work but doesn't + frame = this.buildFrame(frame); + frame.addLiteral(dom); + dig = false; + } else if (nv === 'Resource') { + frame = this.buildFrame(frame, frame.element); + frame.parent.element = null; + frame.addBNode(); + } else if (nv === 'Collection') { + frame = this.buildFrame(frame, frame.element); + frame.parent.element = null; + frame.addCollection(); + } + dom.removeAttributeNode(parsetype); + } + if (attrs.length !== 0) { + var resource = this.getAttributeNodeNS(dom, RDFParser.ns.RDF, 'resource'); + var bnid2 = this.getAttributeNodeNS(dom, RDFParser.ns.RDF, 'nodeID'); + frame = this.buildFrame(frame); + if (resource) { + frame.addNode(resource.nodeValue); + dom.removeAttributeNode(resource); + } else { + if (bnid2) { + frame.addBNode(bnid2.nodeValue); + dom.removeAttributeNode(bnid2); + } else { + frame.addBNode(); + } + } + for (var x1 = attrs.length - 1; x1 >= 0; x1--) { + var f = this.buildFrame(frame); + f.addArc(elementURI(attrs[x1])); + if (elementURI(attrs[x1]) === RDFParser.ns.RDF + 'type') { + this.buildFrame(f).addNode(attrs[x1].nodeValue); + } else { + this.buildFrame(f).addLiteral(attrs[x1].nodeValue); + } + } + } else if (dom.childNodes.length === 0) { + this.buildFrame(frame).addLiteral(''); + } + } + } // rdf:RDF + // dig dug + dom = frame.element; + while (frame.parent) { + var pframe = frame; + while (dom == null) { + frame = frame.parent; + dom = frame.element; + } + var candidate = dom.childNodes && dom.childNodes[frame.lastChild]; + if (!candidate || !dig) { + frame.terminateFrame(); + if (!(frame = frame.parent)) { + break; + } // done + dom = frame.element; + dig = true; + } else if (candidate.nodeType !== RDFParser.nodeType.ELEMENT && candidate.nodeType !== RDFParser.nodeType.TEXT && candidate.nodeType !== RDFParser.nodeType.CDATA_SECTION || (candidate.nodeType === RDFParser.nodeType.TEXT || candidate.nodeType === RDFParser.nodeType.CDATA_SECTION) && dom.childNodes.length !== 1) { + frame.lastChild++; + } else { + // not a leaf + frame.lastChild++; + frame = this.buildFrame(pframe, dom.childNodes[frame.lastChild - 1]); + break; + } + } + } // while + }; + + /** + * Cleans out state from a previous parse run + * @private + */ + this.cleanParser = function () { + this.bnodes = {}; + this.why = null; + }; + + /** + * Builds scope frame + * @private + */ + this.buildFrame = function (parent, element) { + var frame = this.frameFactory(this, parent, element); + if (parent) { + frame.base = parent.base; + frame.lang = parent.lang; + } + if (!element || element.nodeType === RDFParser.nodeType.TEXT || element.nodeType === RDFParser.nodeType.CDATA_SECTION) { + return frame; + } + var attrs = element.attributes; + var base = element.getAttributeNode('xml:base'); + if (base != null) { + frame.base = base.nodeValue; + element.removeAttribute('xml:base'); + } + var lang = element.getAttributeNode('xml:lang'); + if (lang != null) { + frame.lang = lang.nodeValue; + element.removeAttribute('xml:lang'); + } + // remove all extraneous xml and xmlns attributes + for (var x = attrs.length - 1; x >= 0; x--) { + if (attrs[x].nodeName.substr(0, 3) === 'xml') { + if (attrs[x].name.slice(0, 6) === 'xmlns:') { + var uri = attrs[x].nodeValue; // alert('base for namespac attr:'+this.base) + if (this.base) uri = uriUtil.join(uri, this.base); + this.store.setPrefixForURI(attrs[x].name.slice(6), uri); + } + // alert('rdfparser: xml atribute: '+attrs[x].name) //@@ + element.removeAttributeNode(attrs[x]); + } + } + return frame; + }; +}; + module.exports = RDFParser; },{"./uri":81}],75:[function(_dereq_,module,exports){ -'use strict'; - -module.exports = serialize; - -var convert = _dereq_('./convert'); -var Serializer = _dereq_('./serializer'); - -/** - * Serialize to the appropriate format - * @@ Currently NQuads and JSON/LD are deal with extrelemently inefficiently - * through mutiple conversions. - */ -function serialize(target, kb, base, contentType, callback, options) { - base = base || target.uri; - options = options || {}; - contentType = contentType || 'text/turtle'; // text/n3 if complex? - var documentString = null; - try { - var sz = Serializer(kb); - if (options.flags) sz.setFlags(options.flags); - var newSts = kb.statementsMatching(undefined, undefined, undefined, target); - var n3String; - sz.suggestNamespaces(kb.namespaces); - sz.setBase(base); - switch (contentType) { - case 'application/rdf+xml': - documentString = sz.statementsToXML(newSts); - return executeCallback(null, documentString); - case 'text/n3': - case 'application/n3': - // Legacy - documentString = sz.statementsToN3(newSts); - return executeCallback(null, documentString); - case 'text/turtle': - case 'application/x-turtle': - // Legacy - sz.setFlags('si'); // Suppress = for sameAs and => for implies - documentString = sz.statementsToN3(newSts); - return executeCallback(null, documentString); - case 'application/n-triples': - sz.setFlags('deinprstux'); // Suppress nice parts of N3 to make ntriples - documentString = sz.statementsToNTriples(newSts); - return executeCallback(null, documentString); - case 'application/ld+json': - sz.setFlags('deinprstux'); // Use adapters to connect to incmpatible parser - n3String = sz.statementsToNTriples(newSts); - // n3String = sz.statementsToN3(newSts) - convert.convertToJson(n3String, callback); - break; - case 'application/n-quads': - case 'application/nquads': - // @@@ just outpout the quads? Does not work for collections - sz.setFlags('deinprstux q'); // Suppress nice parts of N3 to make ntriples - documentString = sz.statementsToNTriples(newSts); // q in flag means actually quads - return executeCallback(null, documentString); - // n3String = sz.statementsToN3(newSts) - // documentString = convert.convertToNQuads(n3String, callback) - break; - default: - throw new Error('Serialize: Content-type ' + contentType + ' not supported for data write.'); - } - } catch (err) { - if (callback) { - return callback(err); - } - throw err; // Don't hide problems from caller in sync mode - } - - function executeCallback(err, result) { - if (callback) { - callback(err, result); - return; - } else { - return result; - } - } +'use strict'; + +module.exports = serialize; + +var convert = _dereq_('./convert'); +var Serializer = _dereq_('./serializer'); + +/** + * Serialize to the appropriate format + * @@ Currently NQuads and JSON/LD are deal with extrelemently inefficiently + * through mutiple conversions. + */ +function serialize(target, kb, base, contentType, callback, options) { + base = base || target.uri; + options = options || {}; + contentType = contentType || 'text/turtle'; // text/n3 if complex? + var documentString = null; + try { + var sz = Serializer(kb); + if (options.flags) sz.setFlags(options.flags); + var newSts = kb.statementsMatching(undefined, undefined, undefined, target); + var n3String; + sz.suggestNamespaces(kb.namespaces); + sz.setBase(base); + switch (contentType) { + case 'application/rdf+xml': + documentString = sz.statementsToXML(newSts); + return executeCallback(null, documentString); + case 'text/n3': + case 'application/n3': + // Legacy + documentString = sz.statementsToN3(newSts); + return executeCallback(null, documentString); + case 'text/turtle': + case 'application/x-turtle': + // Legacy + sz.setFlags('si'); // Suppress = for sameAs and => for implies + documentString = sz.statementsToN3(newSts); + return executeCallback(null, documentString); + case 'application/n-triples': + sz.setFlags('deinprstux'); // Suppress nice parts of N3 to make ntriples + documentString = sz.statementsToNTriples(newSts); + return executeCallback(null, documentString); + case 'application/ld+json': + sz.setFlags('deinprstux'); // Use adapters to connect to incmpatible parser + n3String = sz.statementsToNTriples(newSts); + // n3String = sz.statementsToN3(newSts) + convert.convertToJson(n3String, callback); + break; + case 'application/n-quads': + case 'application/nquads': + // @@@ just outpout the quads? Does not work for collections + sz.setFlags('deinprstux q'); // Suppress nice parts of N3 to make ntriples + documentString = sz.statementsToNTriples(newSts); // q in flag means actually quads + return executeCallback(null, documentString); + // n3String = sz.statementsToN3(newSts) + // documentString = convert.convertToNQuads(n3String, callback) + break; + default: + throw new Error('Serialize: Content-type ' + contentType + ' not supported for data write.'); + } + } catch (err) { + if (callback) { + return callback(err); + } + throw err; // Don't hide problems from caller in sync mode + } + + function executeCallback(err, result) { + if (callback) { + callback(err, result); + return; + } else { + return result; + } + } } },{"./convert":54,"./serializer":76}],76:[function(_dereq_,module,exports){ -'use strict'; - -/* Serialization of RDF Graphs -** -** Tim Berners-Lee 2006 -** This is was http://dig.csail.mit.edu/2005/ajar/ajaw/js/rdf/serialize.js -** This is or was https://github.com/linkeddata/rdflib.js/blob/master/src/serializer.js -** Licence: MIT -*/ -var NamedNode = _dereq_('./named-node'); -var BlankNode = _dereq_('./blank-node'); -var Uri = _dereq_('./uri'); -var Util = _dereq_('./util'); -var XSD = _dereq_('./xsd'); - -var Serializer = function () { - var __Serializer = function __Serializer(store) { - this.flags = ''; - this.base = null; - - this.prefixes = []; // suggested prefixes - this.namespaces = []; // complementary indexes - - this.suggestPrefix('rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'); // XML code assumes this! - this.suggestPrefix('xml', 'reserved:reservedForFutureUse'); // XML reserves xml: in the spec. - - this.namespacesUsed = []; // Count actually used and so needed in @prefixes - this.keywords = ['a']; // The only one we generate at the moment - this.prefixchars = 'abcdefghijklmnopqustuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - this.incoming = null; // Array not calculated yet - this.formulas = []; // remebering original formulae from hashes - this.store = store; - }; - - __Serializer.prototype.setBase = function (base) { - this.base = base;return this; - }; - - __Serializer.prototype.setFlags = function (flags) { - this.flags = flags || '';return this; - }; - - __Serializer.prototype.toStr = function (x) { - var s = x.toNT(); - if (x.termType === 'Graph') { - this.formulas[s] = x; // remember as reverse does not work - } - return s; - }; - - __Serializer.prototype.fromStr = function (s) { - if (s[0] === '{') { - var x = this.formulas[s]; - if (!x) console.log('No formula object for ' + s); - return x; - } - return this.store.fromNT(s); - }; - /* Accumulate Namespaces - ** - ** These are only hints. If two overlap, only one gets used - ** There is therefore no guarantee in general. - */ - __Serializer.prototype.suggestPrefix = function (prefix, uri) { - if (prefix.slice(0, 7) === 'default') return; // Try to weed these out - if (prefix.slice(0, 2) === 'ns') return; // From others inferior algos - if (!prefix || !uri) return; // empty strings not suitable - if (prefix in this.namespaces || uri in this.prefixes) return; // already used - this.prefixes[uri] = prefix; - this.namespaces[prefix] = uri; - }; - - // Takes a namespace -> prefix map - __Serializer.prototype.suggestNamespaces = function (namespaces) { - for (var px in namespaces) { - this.suggestPrefix(px, namespaces[px]); - } - return this; - }; - - __Serializer.prototype.checkIntegrity = function () { - var p, ns; - for (p in this.namespaces) { - if (this.prefixes[this.namespaces[p]] !== p) { - throw new Error('Serializer integity error 1: ' + p + ', ' + this.namespaces[p] + ', ' + this.prefixes[this.namespaces[p]] + '!'); - } - } - for (ns in this.prefixes) { - if (this.namespaces[this.prefixes[ns]] !== ns) { - throw new Error('Serializer integity error 2: ' + ns + ', ' + this.prefixs[ns] + ', ' + this.namespaces[this.prefixes[ns]] + '!'); - } - } - }; - - // Make up an unused prefix for a random namespace - __Serializer.prototype.makeUpPrefix = function (uri) { - var p = uri; - function canUseMethod(pp) { - if (!__Serializer.prototype.validPrefix.test(pp)) return false; // bad format - if (pp === 'ns') return false; // boring - if (pp in this.namespaces) return false; // already used - this.prefixes[uri] = pp; - this.namespaces[pp] = uri; - return pp; - } - var canUse = canUseMethod.bind(this); - - if ('#/'.indexOf(p[p.length - 1]) >= 0) p = p.slice(0, -1); - var slash = p.lastIndexOf('/'); - if (slash >= 0) p = p.slice(slash + 1); - var i = 0; - while (i < p.length) { - if (this.prefixchars.indexOf(p[i])) { - i++; - } else { - break; - } - } - p = p.slice(0, i); - - if (p.length < 6 && canUse(p)) return p; // exact is best - if (canUse(p.slice(0, 3))) return p.slice(0, 3); - if (canUse(p.slice(0, 2))) return p.slice(0, 2); - if (canUse(p.slice(0, 4))) return p.slice(0, 4); - if (canUse(p.slice(0, 1))) return p.slice(0, 1); - if (canUse(p.slice(0, 5))) return p.slice(0, 5); - if (!__Serializer.prototype.validPrefix.test(p)) { - p = 'n'; // Otherwise the loop below may never termimnate - } - for (var j = 0;; j++) { - if (canUse(p.slice(0, 3) + j)) return p.slice(0, 3) + j; - } - }; - - __Serializer.prototype.rootSubjects = function (sts) { - var incoming = {}; - var subjects = {}; - var allBnodes = {}; - - /* This scan is to find out which nodes will have to be the roots of trees - ** in the serialized form. This will be any symbols, and any bnodes - ** which hve more or less than one incoming arc, and any bnodes which have - ** one incoming arc but it is an uninterrupted loop of such nodes back to itself. - ** This should be kept linear time with repect to the number of statements. - ** Note it does not use any indexing of the store. - */ - for (var i = 0; i < sts.length; i++) { - var st = sts[i]; - var checkMentions = function checkMentions(x) { - if (!incoming.hasOwnProperty(x)) incoming[x] = []; - incoming[x].push(st.subject); // List of things which will cause this to be printed - }; - var st2 = [st.subject, st.predicate, st.object]; - st2.map(function (y) { - if (y.termType === 'BlankNode') { - allBnodes[y.toNT()] = true; - } else if (y.termType === 'Collection') { - y.elements.forEach(function (z) { - checkMentions(z); // bnodes in collections important - }); - } - }); - checkMentions(sts[i].object); - var ss = subjects[this.toStr(st.subject)]; // Statements with this as subject - if (!ss) ss = []; - ss.push(st); - subjects[this.toStr(st.subject)] = ss; // Make hash. @@ too slow for formula? - } - - var roots = []; - for (var xNT in subjects) { - if (!subjects.hasOwnProperty(xNT)) continue; - var y = this.fromStr(xNT); - if (y.termType !== 'BlankNode' || !incoming[y] || incoming[y].length !== 1) { - roots.push(y); - continue; - } - } - this.incoming = incoming; // Keep for serializing @@ Bug for nested formulas - - // Now do the scan using existing roots - var rootsHash = {}; - for (var k = 0; k < roots.length; k++) { - rootsHash[roots[k].toNT()] = true; - } - return { 'roots': roots, 'subjects': subjects, - 'rootsHash': rootsHash, 'incoming': incoming }; - }; - - // ////////////////////////////////////////////////////// - - __Serializer.prototype.toN3 = function (f) { - return this.statementsToN3(f.statements); - }; - - __Serializer.prototype._notQNameChars = '\t\r\n !"#$%&\'()*.,+/;<=>?@[\\]^`{|}~'; - __Serializer.prototype._notNameChars = __Serializer.prototype._notQNameChars + ':'; - - __Serializer.prototype.explicitURI = function (uri) { - if (this.flags.indexOf('r') < 0 && this.base) { - uri = Uri.refTo(this.base, uri); - } else if (this.flags.indexOf('u') >= 0) { - // Unicode encoding NTriples style - uri = backslashUify(uri); - } else { - uri = hexify(uri); - } - return '<' + uri + '>'; - }; - - __Serializer.prototype.statementsToNTriples = function (sts) { - var sorted = sts.slice(); - sorted.sort(); - var str = ''; - var rdfns = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; - var self = this; - var kb = this.store; - var termToNT = function termToNT(x) { - if (x.termType !== 'Collection') { - return self.atomicTermToN3(x); - } - var list = x.elements; - var rest = kb.sym(rdfns + 'nill'); - for (var i = list.length - 1; i >= 0; i--) { - var bnode = new BlankNode(); - str += termToNT(bnode) + ' ' + termToNT(kb.sym(rdfns + 'first')) + ' ' + termToNT(list[i]) + '.\n'; - str += termToNT(bnode) + ' ' + termToNT(kb.sym(rdfns + 'rest')) + ' ' + termToNT(rest) + '.\n'; - rest = bnode; - } - return self.atomicTermToN3(rest); - }; - for (var i = 0; i < sorted.length; i++) { - var st = sorted[i]; - var s = ''; - s += termToNT(st.subject) + ' '; - s += termToNT(st.predicate) + ' '; - s += termToNT(st.object) + ' '; - if (this.flags.indexOf('q') >= 0) { - // Do quads not nrtiples - s += termToNT(st.why) + ' '; - } - s += '.\n'; - str += s; - } - return str; - }; - - __Serializer.prototype.statementsToN3 = function (sts) { - var indent = 4; - var width = 80; - var kb = this.store; - // A URI Map alows us to put the type statemnts at the top. - var uriMap = { 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type': 'aaa:00' }; - var SPO = function SPO(x, y) { - // Do limited canonicalization of bnodes - return Util.heavyCompareSPO(x, y, kb, uriMap); - }; - sts.sort(SPO); - - if (this.base && !this.defaultNamespace) { - this.defaultNamespace = this.base + '#'; - } - - var predMap = {}; - if (this.flags.indexOf('s') < 0) { - predMap['http://www.w3.org/2002/07/owl#sameAs'] = '='; - } - if (this.flags.indexOf('t') < 0) { - predMap['http://www.w3.org/1999/02/22-rdf-syntax-ns#type'] = 'a'; - } - if (this.flags.indexOf('i') < 0) { - predMap['http://www.w3.org/2000/10/swap/log#implies'] = '=>'; - } - // //////////////////////// Arrange the bits of text - - var spaces = function spaces(n) { - var s = ''; - for (var i = 0; i < n; i++) { - s += ' '; - }return s; - }; - - var treeToLine = function treeToLine(tree) { - var str = ''; - for (var i = 0; i < tree.length; i++) { - var branch = tree[i]; - var s2 = typeof branch === 'string' ? branch : treeToLine(branch); - // Note the space before the dot in case statement ends 123. which is in fact allowed but be conservative. - if (i !== 0) { - var ch = str.slice(-1) || ' '; - if (s2 === ',' || s2 === ';') { - // no gap - } else if (s2 === '.' && !'0123456789.'.includes(ch)) {// no gap except after number - // no gap - } else { - str += ' '; // separate from previous token - } - } - str += s2; - } - return str; - }; - - // Convert a nested tree of lists and strings to a string - var treeToString = function treeToString(tree, level) { - var str = ''; - var lastLength = 100000; - if (level === undefined) level = -1; - for (var i = 0; i < tree.length; i++) { - var branch = tree[i]; - if (typeof branch !== 'string') { - var substr = treeToString(branch, level + 1); - if (substr.length < 10 * (width - indent * level) && substr.indexOf('"""') < 0) { - // Don't mess up multiline strings - var line = treeToLine(branch); - if (line.length < width - indent * level) { - branch = line; // Note! treat as string below - substr = ''; - } - } - if (substr) lastLength = 10000; - str += substr; - } - if (typeof branch === 'string') { - if (branch.length === 1 && str.slice(-1) === '\n') { - if (',.;'.indexOf(branch) >= 0) { - str = str.slice(0, -1) + branch + '\n'; // slip punct'n on end - lastLength += 1; - continue; - } - } - if (lastLength < indent * level + 4 || // if new line not necessary - lastLength + branch.length + 1 < width && ';.'.indexOf(str[str.length - 2]) < 0) { - // or the string fits on last line - str = str.slice(0, -1) + ' ' + branch + '\n'; // then continue on this line - lastLength += branch.length + 1; - } else { - var _line = spaces(indent * level) + branch; - str += _line + '\n'; - lastLength = _line.length; - if (level < 0) { - str += '\n'; // extra blank line - lastLength = 100000; // don't touch - } - } - } - } - return str; - }; - - // //////////////////////////////////////////// Structure for N3 - // Convert a set of statements into a nested tree of lists and strings - function statementListToTreeMethod(statements) { - var stats = this.rootSubjects(statements); - var roots = stats.roots; - var results = []; - for (var i = 0; i < roots.length; i++) { - var root = roots[i]; - results.push(subjectTree(root, stats)); - } - return results; - } - var statementListToTree = statementListToTreeMethod.bind(this); - - // The tree for a subject - function subjectTree(subject, stats) { - if (subject.termType === 'BlankNode' && !stats.incoming[subject]) { - return objectTree(subject, stats, true).concat(['.']); // Anonymous bnode subject - } - return [termToN3(subject, stats)].concat([propertyTree(subject, stats)]).concat(['.']); - } - // The property tree for a single subject or anonymous node - function propertyTreeMethod(subject, stats) { - var results = []; - var lastPred = null; - var sts = stats.subjects[this.toStr(subject)] || []; // relevant statements - if (typeof sts === 'undefined') { - throw new Error('Cant find statements for ' + subject); - } - - var objects = []; - for (var i = 0; i < sts.length; i++) { - var st = sts[i]; - if (st.predicate.uri === lastPred) { - objects.push(','); - } else { - if (lastPred) { - results = results.concat([objects]).concat([';']); - objects = []; - } - results.push(predMap[st.predicate.uri] ? predMap[st.predicate.uri] : termToN3(st.predicate, stats)); - } - lastPred = st.predicate.uri; - objects.push(objectTree(st.object, stats)); - } - results = results.concat([objects]); - return results; - } - var propertyTree = propertyTreeMethod.bind(this); - - function objectTreeMethod(obj, stats, force) { - if (obj.termType === 'BlankNode' && (force || stats.rootsHash[obj.toNT()] === undefined)) { - // if not a root - if (stats.subjects[this.toStr(obj)]) { - return ['[', propertyTree(obj, stats), ']']; - } else { - return '[]'; - } - } - return termToN3(obj, stats); - } - var objectTree = objectTreeMethod.bind(this); - - function termToN3Method(expr, stats) { - // - var i, res; - switch (expr.termType) { - case 'Graph': - res = ['{']; - res = res.concat(statementListToTree(expr.statements)); - return res.concat(['}']); - - case 'Collection': - res = ['(']; - for (i = 0; i < expr.elements.length; i++) { - res.push([objectTree(expr.elements[i], stats)]); - } - res.push(')'); - return res; - - default: - return this.atomicTermToN3(expr); - } - } - __Serializer.prototype.termToN3 = termToN3; - var termToN3 = termToN3Method.bind(this); - - function prefixDirectivesMethod() { - var str = ''; - if (this.defaultNamespace) { - str += '@prefix : ' + this.explicitURI(this.defaultNamespace) + '.\n'; - } - for (var ns in this.prefixes) { - if (!this.prefixes.hasOwnProperty(ns)) continue; - if (!this.namespacesUsed[ns]) continue; - str += '@prefix ' + this.prefixes[ns] + ': ' + this.explicitURI(ns) + '.\n'; - } - return str + '\n'; - } - var prefixDirectives = prefixDirectivesMethod.bind(this); - // Body of statementsToN3: - var tree = statementListToTree(sts); - return prefixDirectives() + treeToString(tree); - }; - // //////////////////////////////////////////// Atomic Terms - - // Deal with term level things and nesting with no bnode structure - __Serializer.prototype.atomicTermToN3 = function atomicTermToN3(expr, stats) { - switch (expr.termType) { - case 'BlankNode': - case 'Variable': - return expr.toNT(); - case 'Literal': - var val = expr.value.toString(); // should be a string already - if (expr.datatype && this.flags.indexOf('x') < 0) { - // Supress native numbers - switch (expr.datatype.uri) { - - case 'http://www.w3.org/2001/XMLSchema#integer': - return val; - - case 'http://www.w3.org/2001/XMLSchema#decimal': - // In urtle must have dot - if (val.indexOf('.') < 0) val += '.0'; - return val; - - case 'http://www.w3.org/2001/XMLSchema#double': - // Must force use of 'e' - if (val.indexOf('.') < 0) val += '.0'; - if (val.indexOf('e') < 0) val += 'e0'; - return val; - - case 'http://www.w3.org/2001/XMLSchema#boolean': - return expr.value ? 'true' : 'false'; - } - } - var str = this.stringToN3(expr.value); - if (expr.language) { - str += '@' + expr.language; - } else if (!expr.datatype.equals(XSD.string)) { - str += '^^' + this.atomicTermToN3(expr.datatype, stats); - } - return str; - case 'NamedNode': - return this.symbolToN3(expr); - default: - throw new Error('Internal: atomicTermToN3 cannot handle ' + expr + ' of termType: ' + expr.termType); - } - }; - - // stringToN3: String escaping for N3 - - __Serializer.prototype.validPrefix = new RegExp(/^[a-zA-Z][a-zA-Z0-9]*$/); - - __Serializer.prototype.forbidden1 = new RegExp(/[\\"\b\f\r\v\t\n\u0080-\uffff]/gm); - __Serializer.prototype.forbidden3 = new RegExp(/[\\"\b\f\r\v\u0080-\uffff]/gm); - __Serializer.prototype.stringToN3 = function stringToN3(str, flags) { - if (!flags) flags = 'e'; - var res = ''; - var i, j, k; - var delim; - var forbidden; - if (str.length > 20 && // Long enough to make sense - str.slice(-1) !== '"' && // corner case' - flags.indexOf('n') < 0 && ( // Force single line - str.indexOf('\n') > 0 || str.indexOf('"') > 0)) { - delim = '"""'; - forbidden = __Serializer.prototype.forbidden3; - } else { - delim = '"'; - forbidden = __Serializer.prototype.forbidden1; - } - for (i = 0; i < str.length;) { - forbidden.lastIndex = 0; - var m = forbidden.exec(str.slice(i)); - if (m == null) break; - j = i + forbidden.lastIndex - 1; - res += str.slice(i, j); - var ch = str[j]; - if (ch === '"' && delim === '"""' && str.slice(j, j + 3) !== '"""') { - res += ch; - } else { - k = '\b\f\r\t\v\n\\"'.indexOf(ch); // No escaping of bell (7)? - if (k >= 0) { - res += '\\' + 'bfrtvn\\"'[k]; - } else { - if (flags.indexOf('e') >= 0) { - // Unicode escaping in strings not unix style - res += '\\u' + ('000' + ch.charCodeAt(0).toString(16).toLowerCase()).slice(-4); - } else { - // no 'e' flag - res += ch; - } - } - } - i = j + 1; - } - return delim + res + str.slice(i) + delim; - }; - // A single symbol, either in <> or namespace notation - - __Serializer.prototype.symbolToN3 = function symbolToN3(x) { - // c.f. symbolString() in notation3.py - var uri = x.uri; - var j = uri.indexOf('#'); - if (j < 0 && this.flags.indexOf('/') < 0) { - j = uri.lastIndexOf('/'); - } - if (j >= 0 && this.flags.indexOf('p') < 0 && ( - // Can split at namespace but only if http[s]: URI or file: or ws[s] (why not others?) - uri.indexOf('http') === 0 || uri.indexOf('ws') === 0 || uri.indexOf('file') === 0)) { - var canSplit = true; - for (var k = j + 1; k < uri.length; k++) { - if (__Serializer.prototype._notNameChars.indexOf(uri[k]) >= 0) { - canSplit = false; - break; - } - } - /* - if (uri.slice(0, j + 1) === this.base + '#') { // base-relative - if (canSplit) { - return ':' + uri.slice(j + 1) // assume deafult ns is local - } else { - return '<#' + uri.slice(j + 1) + '>' - } - } - */ - if (canSplit) { - var localid = uri.slice(j + 1); - var namesp = uri.slice(0, j + 1); - if (this.defaultNamespace && this.defaultNamespace === namesp && this.flags.indexOf('d') < 0) { - // d -> suppress default - if (this.flags.indexOf('k') >= 0 && this.keyords.indexOf(localid) < 0) { - return localid; - } - return ':' + localid; - } - // this.checkIntegrity() // @@@ Remove when not testing - var prefix = this.prefixes[namesp]; - if (!prefix) prefix = this.makeUpPrefix(namesp); - if (prefix) { - this.namespacesUsed[namesp] = true; - return prefix + ':' + localid; - } - // Fall though if can't do qname - } - } - return this.explicitURI(uri); - }; - // String escaping utilities - - function hexify(str) { - // also used in parser - return encodeURI(str); - } - - function backslashUify(str) { - var res = ''; - var k; - for (var i = 0; i < str.length; i++) { - k = str.charCodeAt(i); - if (k > 65535) { - res += '\\U' + ('00000000' + k.toString(16)).slice(-8); // convert to upper? - } else if (k > 126) { - res += '\\u' + ('0000' + k.toString(16)).slice(-4); - } else { - res += str[i]; - } - } - return res; - } - - // /////////////////////////// Quad store serialization - - // @para. write - a function taking a single string to be output - // - __Serializer.prototype.writeStore = function (write) { - var kb = this.store; - var fetcher = kb.fetcher; - var session = fetcher && fetcher.appNode; - - // The core data - - var sources = this.store.index[3]; - for (var s in sources) { - // -> assume we can use -> as short for log:semantics - var source = kb.fromNT(s); - if (session && source.sameTerm(session)) continue; - write('\n' + this.atomicTermToN3(source) + ' ' + this.atomicTermToN3(kb.sym('http://www.w3.org/2000/10/swap/log#semantics')) + ' { ' + this.statementsToN3(kb.statementsMatching(undefined, undefined, undefined, source)) + ' }.\n'); - } - - // The metadata from HTTP interactions: - - kb.statementsMatching(undefined, kb.sym('http://www.w3.org/2007/ont/link#requestedURI')).map(function (st) { - write('\n<' + st.object.value + '> log:metadata {\n'); - var sts = kb.statementsMatching(undefined, undefined, undefined, st.subject); - write(this.statementsToN3(this.statementsToN3(sts))); - write('}.\n'); - }); - - // Inferences we have made ourselves not attributable to anyone else - - var metaSources = []; - if (session) metaSources.push(session); - var metadata = []; - metaSources.map(function (source) { - metadata = metadata.concat(kb.statementsMatching(undefined, undefined, undefined, source)); - }); - write(this.statementsToN3(metadata)); - }; - - // ////////////////////////////////////////////// XML serialization - - __Serializer.prototype.statementsToXML = function (sts) { - var indent = 4; - var width = 80; - - var namespaceCounts = []; // which have been used - namespaceCounts['http://www.w3.org/1999/02/22-rdf-syntax-ns#'] = true; - - var liPrefix = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#_'; // prefix for ordered list items - - // //////////////////////// Arrange the bits of XML text - - var spaces = function spaces(n) { - var s = ''; - for (var i = 0; i < n; i++) { - s += ' '; - }return s; - }; - - var XMLtreeToLine = function XMLtreeToLine(tree) { - var str = ''; - for (var i = 0; i < tree.length; i++) { - var branch = tree[i]; - var s2 = typeof branch === 'string' ? branch : XMLtreeToLine(branch); - str += s2; - } - return str; - }; - - // Convert a nested tree of lists and strings to a string - var XMLtreeToString = function XMLtreeToString(tree, level) { - var str = ''; - var line; - var lastLength = 100000; - if (!level) level = 0; - for (var i = 0; i < tree.length; i++) { - var branch = tree[i]; - if (typeof branch !== 'string') { - var substr = XMLtreeToString(branch, level + 1); - if (substr.length < 10 * (width - indent * level) && substr.indexOf('"""') < 0) { - // Don't mess up multiline strings - line = XMLtreeToLine(branch); - if (line.length < width - indent * level) { - branch = ' ' + line; // @@ Hack: treat as string below - substr = ''; - } - } - if (substr) lastLength = 10000; - str += substr; - } - if (typeof branch === 'string') { - if (lastLength < indent * level + 4) { - // continue - str = str.slice(0, -1) + ' ' + branch + '\n'; - lastLength += branch.length + 1; - } else { - line = spaces(indent * level) + branch; - str += line + '\n'; - lastLength = line.length; - } - } else {// not string - } - } - return str; - }; - - function statementListToXMLTreeMethod(statements) { - this.suggestPrefix('rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'); - var stats = this.rootSubjects(statements); - var roots = stats.roots; - var results = []; - for (var i = 0; i < roots.length; i++) { - var root = roots[i]; - results.push(subjectXMLTree(root, stats)); - } - return results; - } - var statementListToXMLTree = statementListToXMLTreeMethod.bind(this); - - function escapeForXML(str) { - if (typeof str === 'undefined') return '@@@undefined@@@@'; - return str.replace(/[&<"]/g, function (m) { - switch (m[0]) { - case '&': - return '&'; - case '<': - return '<'; - case '"': - return '"'; // ' - } - }); - } - - function relURIMethod(term) { - return escapeForXML(this.base ? Util.uri.refTo(this.base, term.uri) : term.uri); - } - var relURI = relURIMethod.bind(this); - - // The tree for a subject - function subjectXMLTreeMethod(subject, stats) { - var results = []; - var type, t, st, pred; - var sts = stats.subjects[this.toStr(subject)]; // relevant statements - if (typeof sts === 'undefined') { - // empty bnode - return propertyXMLTree(subject, stats); - } - - // Sort only on the predicate, leave the order at object - // level undisturbed. This leaves multilingual content in - // the order of entry (for partner literals), which helps - // readability. - // - // For the predicate sort, we attempt to split the uri - // as a hint to the sequence - sts.sort(function (a, b) { - var ap = a.predicate.uri; - var bp = b.predicate.uri; - if (ap.substring(0, liPrefix.length) === liPrefix || bp.substring(0, liPrefix.length) === liPrefix) { - // we're only interested in sorting list items - return ap.localeCompare(bp); - } - - var as = ap.substring(liPrefix.length); - var bs = bp.substring(liPrefix.length); - var an = parseInt(as, 10); - var bn = parseInt(bs, 10); - if (isNaN(an) || isNaN(bn) || an !== as || bn !== bs) { - // we only care about integers - return ap.localeCompare(bp); - } - - return an - bn; - }); - - for (var i = 0; i < sts.length; i++) { - st = sts[i]; - // look for a type - if (st.predicate.uri === 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' && !type && st.object.termType === 'symbol') { - type = st.object; - continue; // don't include it as a child element - } - - // see whether predicate can be replaced with "li" - pred = st.predicate; - if (pred.uri.substr(0, liPrefix.length) === liPrefix) { - var number = pred.uri.substr(liPrefix.length); - // make sure these are actually numeric list items - var intNumber = parseInt(number, 10); - if (number === intNumber.toString()) { - // was numeric; don't need to worry about ordering since we've already - // sorted the statements - pred = new NamedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#li'); - } - } - - t = qname(pred); - switch (st.object.termType) { - case 'BlankNode': - if (stats.incoming[st.object].length === 1) { - // there should always be something in the incoming array for a bnode - results = results.concat(['<' + t + ' rdf:parseType="Resource">', subjectXMLTree(st.object, stats), '']); - } else { - results = results.concat(['<' + t + ' rdf:nodeID="' + st.object.toNT().slice(2) + '"/>']); - } - break; - case 'NamedNode': - results = results.concat(['<' + t + ' rdf:resource="' + relURI(st.object) + '"/>']); - break; - case 'Literal': - results = results.concat(['<' + t + (st.object.datatype.equals(XSD.string) ? '' : ' rdf:datatype="' + escapeForXML(st.object.datatype.uri) + '"') + (st.object.language ? ' xml:lang="' + st.object.language + '"' : '') + '>' + escapeForXML(st.object.value) + '']); - break; - case 'Collection': - results = results.concat(['<' + t + ' rdf:parseType="Collection">', collectionXMLTree(st.object, stats), '']); - break; - default: - throw new Error("Can't serialize object of type " + st.object.termType + ' into XML'); - } // switch - } - - var tag = type ? qname(type) : 'rdf:Description'; - - var attrs = ''; - if (subject.termType === 'BlankNode') { - if (!stats.incoming[subject] || stats.incoming[subject].length !== 1) { - // not an anonymous bnode - attrs = ' rdf:nodeID="' + subject.toNT().slice(2) + '"'; - } - } else { - attrs = ' rdf:about="' + relURI(subject) + '"'; - } - - return ['<' + tag + attrs + '>'].concat([results]).concat(['']); - } - - var subjectXMLTree = subjectXMLTreeMethod.bind(this); - - function collectionXMLTree(subject, stats) { - var res = []; - for (var i = 0; i < subject.elements.length; i++) { - res.push(subjectXMLTree(subject.elements[i], stats)); - } - return res; - } - - // The property tree for a single subject or anonymos node - function propertyXMLTreeMethod(subject, stats) { - var results = []; - var sts = stats.subjects[this.toStr(subject)]; // relevant statements - if (!sts) return results; // No relevant statements - sts.sort(); - for (var i = 0; i < sts.length; i++) { - var st = sts[i]; - switch (st.object.termType) { - case 'BlankNode': - if (stats.rootsHash[st.object.toNT()]) { - // This bnode has been done as a root -- no content here @@ what bout first time - results = results.concat(['<' + qname(st.predicate) + ' rdf:nodeID="' + st.object.toNT().slice(2) + '">', '']); - } else { - results = results.concat(['<' + qname(st.predicate) + ' rdf:parseType="Resource">', propertyXMLTree(st.object, stats), '']); - } - break; - case 'NamedNode': - results = results.concat(['<' + qname(st.predicate) + ' rdf:resource="' + relURI(st.object) + '"/>']); - break; - case 'Literal': - results = results.concat(['<' + qname(st.predicate) + (st.object.datatype.equals(XSD.string) ? '' : ' rdf:datatype="' + escapeForXML(st.object.datatype.value) + '"') + (st.object.language ? ' xml:lang="' + st.object.language + '"' : '') + '>' + escapeForXML(st.object.value) + '']); - break; - case 'Collection': - results = results.concat(['<' + qname(st.predicate) + ' rdf:parseType="Collection">', collectionXMLTree(st.object, stats), '']); - break; - default: - throw new Error("Can't serialize object of type " + st.object.termType + ' into XML'); - } // switch - } - return results; - } - var propertyXMLTree = propertyXMLTreeMethod.bind(this); - - function qnameMethod(term) { - var uri = term.uri; - - var j = uri.indexOf('#'); - if (j < 0 && this.flags.indexOf('/') < 0) { - j = uri.lastIndexOf('/'); - } - if (j < 0) throw new Error('Cannot make qname out of <' + uri + '>'); - - for (var k = j + 1; k < uri.length; k++) { - if (__Serializer.prototype._notNameChars.indexOf(uri[k]) >= 0) { - throw new Error('Invalid character "' + uri[k] + '" cannot be in XML qname for URI: ' + uri); - } - } - var localid = uri.slice(j + 1); - var namesp = uri.slice(0, j + 1); - if (this.defaultNamespace && this.defaultNamespace === namesp && this.flags.indexOf('d') < 0) { - // d -> suppress default - return localid; - } - var prefix = this.prefixes[namesp]; - if (!prefix) prefix = this.makeUpPrefix(namesp); - namespaceCounts[namesp] = true; - return prefix + ':' + localid; - } - var qname = qnameMethod.bind(this); - - // Body of toXML: - - var tree = statementListToXMLTree(sts); - var str = '']; // @@ namespace declrations - return XMLtreeToString(tree2, -1); - }; // End @@ body - - var Serializer = function Serializer(store) { - return new __Serializer(store); - }; - return Serializer; -}(); - -module.exports = Serializer; -},{"./blank-node":51,"./named-node":66,"./uri":81,"./util":82,"./xsd":84}],77:[function(_dereq_,module,exports){ -'use strict'; - -// Converting between SPARQL queries and the $rdf query API -/* - -function SQuery () { - this.terms = [] - return this -} - -STerm.prototype.toString = STerm.val -SQuery.prototype.add = function (str) {this.terms.push()}*/ - -var log = _dereq_('./log'); -var Query = _dereq_('./query').Query; -// const Fetcher = require('./fetcher') - -/** - * @SPARQL: SPARQL text that is converted to a query object which is returned. - * @testMode: testing flag. Prevents loading of sources. - */ -function SPARQLToQuery(SPARQL, testMode, kb) { - // AJAR_ClearTable() - var variableHash = []; - function makeVar(name) { - if (variableHash[name]) { - return variableHash[name]; - } - var newVar = kb.variable(name); - variableHash[name] = newVar; - return newVar; - } - - // term type functions - function isRealText(term) { - return typeof term === 'string' && term.match(/[^ \n\t]/); - } - function isVar(term) { - return typeof term === 'string' && term.match(/^[\?\$]/); - } - function fixSymbolBrackets(term) { - if (typeof term === 'string') { - return term.replace(/^</, '<').replace(/>$/, '>'); - } else { - return term; - } - } - function isSymbol(term) { - return typeof term === 'string' && term.match(/^<[^>]*>$/); - } - function isBnode(term) { - return typeof term === 'string' && (term.match(/^_:/) || term.match(/^$/)); - } - function isPrefix(term) { - return typeof term === 'string' && term.match(/:$/); - } - function isPrefixedSymbol(term) { - return typeof term === 'string' && term.match(/^:|^[^_][^:]*:/); - } - function getPrefix(term) { - var a = term.split(':'); - return a[0]; - } - function getSuffix(term) { - var a = term.split(':'); - return a[1]; - } - function removeBrackets(term) { - if (isSymbol(term)) { - return term.slice(1, term.length - 1); - } else { - return term; - } - } - // takes a string and returns an array of strings and Literals in the place of literals - function parseLiterals(str) { - // var sin = (str.indexOf(/[ \n]\'/)==-1)?null:str.indexOf(/[ \n]\'/), doub = (str.indexOf(/[ \n]\"/)==-1)?null:str.indexOf(/[ \n]\"/) - var sin = str.indexOf("'") === -1 ? null : str.indexOf("'"); - var doub = str.indexOf('"') === -1 ? null : str.indexOf('"'); - // alert("S: "+sin+" D: "+doub) - if (!sin && !doub) { - var a = new Array(1); - a[0] = str; - return a; - } - var res = new Array(2); - var br; - var ind; - if (!sin || doub && doub < sin) { - br = '"'; - ind = doub; - } else if (!doub || sin && sin < doub) { - br = "'"; - ind = sin; - } else { - log.error('SQARQL QUERY OOPS!'); - return res; - } - res[0] = str.slice(0, ind); - var end = str.slice(ind + 1).indexOf(br); - if (end === -1) { - log.error('SPARQL parsing error: no matching parentheses in literal ' + str); - return str; - } - // alert(str.slice(end + ind + 2).match(/^\^\^/)) - var end2; - if (str.slice(end + ind + 2).match(/^\^\^/)) { - end2 = str.slice(end + ind + 2).indexOf(' '); - // alert(end2) - res[1] = kb.literal(str.slice(ind + 1, ind + 1 + end), '', kb.sym(removeBrackets(str.slice(ind + 4 + end, ind + 2 + end + end2)))); - // alert(res[1].datatype.uri) - res = res.concat(parseLiterals(str.slice(end + ind + 3 + end2))); - } else if (str.slice(end + ind + 2).match(/^@/)) { - end2 = str.slice(end + ind + 2).indexOf(' '); - // alert(end2) - res[1] = kb.literal(str.slice(ind + 1, ind + 1 + end), str.slice(ind + 3 + end, ind + 2 + end + end2), null); - // alert(res[1].datatype.uri) - res = res.concat(parseLiterals(str.slice(end + ind + 2 + end2))); - } else { - res[1] = kb.literal(str.slice(ind + 1, ind + 1 + end), '', null); - log.info('Literal found: ' + res[1]); - res = res.concat(parseLiterals(str.slice(end + ind + 2))); // finds any other literals - } - return res; - } - - function spaceDelimit(str) { - str = str.replace(/\(/g, ' ( ').replace(/\)/g, ' ) ').replace(//g, '> ').replace(/{/g, ' { ').replace(/}/g, ' } ').replace(/[\t\n\r]/g, ' ').replace(/; /g, ' ; ').replace(/\. /g, ' . ').replace(/, /g, ' , '); - log.info('New str into spaceDelimit: \n' + str); - var res = []; - var br = str.split(' '); - for (var x in br) { - if (isRealText(br[x])) { - res = res.concat(br[x]); - } - } - return res; - } - - function replaceKeywords(input) { - var strarr = input; - for (var x = 0; x < strarr.length; x++) { - if (strarr[x] === 'a') { - strarr[x] = ''; - } - if (strarr[x] === 'is' && strarr[x + 2] === 'of') { - strarr.splice(x, 1); - strarr.splice(x + 1, 1); - var s = strarr[x - 1]; - strarr[x - 1] = strarr[x + 1]; - strarr[x + 1] = s; - } - } - return strarr; - } - - function toTerms(input) { - var res = []; - for (var x = 0; x < input.length; x++) { - if (typeof input[x] !== 'string') { - res[x] = input[x]; - continue; - } - input[x] = fixSymbolBrackets(input[x]); - if (isVar(input[x])) { - res[x] = makeVar(input[x].slice(1)); - } else if (isBnode(input[x])) { - log.info(input[x] + ' was identified as a bnode.'); - res[x] = kb.bnode(); - } else if (isSymbol(input[x])) { - log.info(input[x] + ' was identified as a symbol.'); - res[x] = kb.sym(removeBrackets(input[x])); - } else if (isPrefixedSymbol(input[x])) { - log.info(input[x] + ' was identified as a prefixed symbol'); - if (prefixes[getPrefix(input[x])]) { - res[x] = kb.sym(input[x] = prefixes[getPrefix(input[x])] + getSuffix(input[x])); - } else { - log.error('SPARQL error: ' + input[x] + ' with prefix ' + getPrefix(input[x]) + ' does not have a correct prefix entry.'); - res[x] = input[x]; - } - } else { - res[x] = input[x]; - } - } - return res; - } - - function tokenize(str) { - var token1 = parseLiterals(str); - var token2 = []; - for (var x in token1) { - if (typeof token1[x] === 'string') { - token2 = token2.concat(spaceDelimit(token1[x])); - } else { - token2 = token2.concat(token1[x]); - } - } - token2 = replaceKeywords(token2); - log.info('SPARQL Tokens: ' + token2); - return token2; - } - - // CASE-INSENSITIVE - function arrayIndexOf(str, arr) { - for (var i = 0; i < arr.length; i++) { - if (typeof arr[i] !== 'string') { - continue; - } - if (arr[i].toLowerCase() === str.toLowerCase()) { - return i; - } - } - // log.warn("No instance of "+str+" in array "+arr) - return null; - } - - // CASE-INSENSITIVE - function arrayIndicesOf(str, arr) { - var ind = []; - for (var i = 0; i < arr.length; i++) { - if (typeof arr[i] !== 'string') { - continue; - } - if (arr[i].toLowerCase() === str.toLowerCase()) { - ind.push(i); - } - } - return ind; - } - - function setVars(input, query) { - log.info('SPARQL vars: ' + input); - for (var x in input) { - if (isVar(input[x])) { - log.info('Added ' + input[x] + ' to query variables from SPARQL'); - var v = makeVar(input[x].slice(1)); - query.vars.push(v); - v.label = input[x].slice(1); - } else { - log.warn('Incorrect SPARQL variable in SELECT: ' + input[x]); - } - } - } - - function getPrefixDeclarations(input) { - var prefInd = arrayIndicesOf('PREFIX', input); - var res = []; - for (var i in prefInd) { - var a = input[prefInd[i] + 1]; - var b = input[prefInd[i] + 2]; - if (!isPrefix(a)) { - log.error('Invalid SPARQL prefix: ' + a); - } else if (!isSymbol(b)) { - log.error('Invalid SPARQL symbol: ' + b); - } else { - log.info('Prefix found: ' + a + ' -> ' + b); - var pref = getPrefix(a); - var symbol = removeBrackets(b); - res[pref] = symbol; - } - } - return res; - } - - function getMatchingBracket(arr, open, close) { - log.info('Looking for a close bracket of type ' + close + ' in ' + arr); - var index = 0; - for (var i = 0; i < arr.length; i++) { - if (arr[i] === open) { - index++; - } - if (arr[i] === close) { - index--; - } - if (index < 0) { - return i; - } - } - log.error('Statement had no close parenthesis in SPARQL query'); - return 0; - } - - function constraintGreaterThan(value) { - this.describe = function (varstr) { - return varstr + ' > ' + value.toNT(); - }; - this.test = function (term) { - if (term.value.match(/[0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?/)) { - return parseFloat(term.value) > parseFloat(value); - } else { - return term.toNT() > value.toNT(); - } - }; - return this; - } - - function constraintLessThan(value) { - // this is not the recommended usage. Should only work on literal, numeric, dateTime - this.describe = function (varstr) { - return varstr + ' < ' + value.toNT(); - }; - this.test = function (term) { - // this.describe = function (varstr) { return varstr + " < "+value } - if (term.value.match(/[0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?/)) { - return parseFloat(term.value) < parseFloat(value); - } else { - return term.toNT() < value.toNT(); - } - }; - return this; - } - // This should only work on literals but doesn't. - function ConstraintEqualTo(value) { - this.describe = function (varstr) { - return varstr + ' = ' + value.toNT(); - }; - this.test = function (term) { - return value.sameTerm(term); - }; - return this; - } - - // value must be a literal - function ConstraintRegexp(value) { - this.describe = function (varstr) { - return "REGEXP( '" + value + "' , " + varstr + ' )'; - }; - this.test = function (term) { - var str = value; - // str = str.replace(/^//,"").replace(//$/,"") - var rg = new RegExp(str); - if (term.value) { - return rg.test(term.value); - } else { - return false; - } - }; - } - - function setConstraint(input, pat) { - if (input.length === 3 && input[0].termType === 'Variable' && (input[2].termType === 'NamedNode' || input[2].termType === 'Literal')) { - if (input[1] === '=') { - log.debug('Constraint added: ' + input); - pat.constraints[input[0]] = new ConstraintEqualTo(input[2]); - } else if (input[1] === '>') { - log.debug('Constraint added: ' + input); - pat.constraints[input[0]] = new ConstraintEqualTo(input[2]); - } else if (input[1] === '<') { - log.debug('Constraint added: ' + input); - pat.constraints[input[0]] = new ConstraintEqualTo(input[2]); - } else { - log.warn("I don't know how to handle the constraint: " + input); - } - } else if (input.length === 6 && typeof input[0] === 'string' && input[0].toLowerCase() === 'regexp' && input[1] === '(' && input[5] === ')' && input[3] === ',' && input[4].termType === 'Variable' && input[2].termType === 'Literal') { - log.debug('Constraint added: ' + input); - pat.constraints[input[4]] = new ConstraintRegexp(input[2].value); - } - // log.warn("I don't know how to handle the constraint: "+input) - // alert("length: "+input.length+" input 0 type: "+input[0].termType+" input 1: "+input[1]+" input[2] type: "+input[2].termType) - } - - function setOptional(terms, pat) { - log.debug('Optional query: ' + terms + ' not yet implemented.'); - var opt = kb.formula(); - setWhere(terms, opt); - pat.optional.push(opt); - } - - function setWhere(input, pat) { - var terms = toTerms(input); - var end; - log.debug('WHERE: ' + terms); - var opt; - // var opt = arrayIndicesOf("OPTIONAL",terms) - while (arrayIndexOf('OPTIONAL', terms)) { - opt = arrayIndexOf('OPTIONAL', terms); - log.debug('OPT: ' + opt + ' ' + terms[opt] + ' in ' + terms); - if (terms[opt + 1] !== '{') { - log.warn('Bad optional opening bracket in word ' + opt); - } - end = getMatchingBracket(terms.slice(opt + 2), '{', '}'); - if (end === -1) { - log.error('No matching bracket in word ' + opt); - } else { - setOptional(terms.slice(opt + 2, opt + 2 + end), pat); - // alert(pat.statements[0].toNT()) - opt = arrayIndexOf('OPTIONAL', terms); - end = getMatchingBracket(terms.slice(opt + 2), '{', '}'); - terms.splice(opt, end + 3); - } - } - log.debug('WHERE after optionals: ' + terms); - while (arrayIndexOf('FILTER', terms)) { - var filt = arrayIndexOf('FILTER', terms); - if (terms[filt + 1] !== '(') { - log.warn('Bad filter opening bracket in word ' + filt); - } - end = getMatchingBracket(terms.slice(filt + 2), '(', ')'); - if (end === -1) { - log.error('No matching bracket in word ' + filt); - } else { - setConstraint(terms.slice(filt + 2, filt + 2 + end), pat); - filt = arrayIndexOf('FILTER', terms); - end = getMatchingBracket(terms.slice(filt + 2), '(', ')'); - terms.splice(filt, end + 3); - } - } - log.debug('WHERE after filters and optionals: ' + terms); - extractStatements(terms, pat); - } - - function extractStatements(terms, formula) { - var arrayZero = new Array(1); - arrayZero[0] = -1; // this is just to add the beginning of the where to the periods index. - var per = arrayZero.concat(arrayIndicesOf('.', terms)); - var stat = []; - for (var x = 0; x < per.length - 1; x++) { - stat[x] = terms.slice(per[x] + 1, per[x + 1]); - } - // Now it's in an array of statements - for (x in stat) { - // THIS MUST BE CHANGED FOR COMMA, SEMICOLON - log.info('s+p+o ' + x + ' = ' + stat[x]); - var subj = stat[x][0]; - stat[x].splice(0, 1); - var sem = arrayZero.concat(arrayIndicesOf(';', stat[x])); - sem.push(stat[x].length); - var stat2 = []; - for (var y = 0; y < sem.length - 1; y++) { - stat2[y] = stat[x].slice(sem[y] + 1, sem[y + 1]); - } - for (x in stat2) { - log.info('p+o ' + x + ' = ' + stat[x]); - var pred = stat2[x][0]; - stat2[x].splice(0, 1); - var com = arrayZero.concat(arrayIndicesOf(',', stat2[x])); - com.push(stat2[x].length); - var stat3 = []; - for (y = 0; y < com.length - 1; y++) { - stat3[y] = stat2[x].slice(com[y] + 1, com[y + 1]); - } - for (x in stat3) { - var obj = stat3[x][0]; - log.info('Subj=' + subj + ' Pred=' + pred + ' Obj=' + obj); - formula.add(subj, pred, obj); - } - } - } - } - - // ******************************* Body of SPARQLToQuery ***************************// - log.info('SPARQL input: \n' + SPARQL); - var q = new Query(); - var sp = tokenize(SPARQL); // first tokenize everything - var prefixes = getPrefixDeclarations(sp); - if (!prefixes.rdf) { - prefixes.rdf = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; - } - if (!prefixes.rdfs) { - prefixes.rdfs = 'http://www.w3.org/2000/01/rdf-schema#'; - } - var selectLoc = arrayIndexOf('SELECT', sp); - var whereLoc = arrayIndexOf('WHERE', sp); - if (selectLoc < 0 || whereLoc < 0 || selectLoc > whereLoc) { - log.error('Invalid or nonexistent SELECT and WHERE tags in SPARQL query'); - return false; - } - setVars(sp.slice(selectLoc + 1, whereLoc), q); - - setWhere(sp.slice(whereLoc + 2, sp.length - 1), q.pat); - - if (testMode) { - return q; - } - - for (var x in q.pat.statements) { - var st = q.pat.statements[x]; - if (st.subject.termType === 'NamedNode') { - /* && sf.isPending(st.subject.uri) */ // This doesn't work. - // sf.requestURI(st.subject.uri,"sparql:"+st.subject) Kenny: I remove these two - if (kb.fetcher) { - kb.fetcher.lookUpThing(st.subject, 'sparql:' + st.subject); - } - } - if (st.object.termType === 'NamedNode') { - /* && sf.isPending(st.object.uri) */ - // sf.requestURI(st.object.uri,"sparql:"+st.object) - if (kb.fetcher) { - kb.fetcher.lookUpThing(st.object, 'sparql:' + st.object); - } - } - } - // alert(q.pat) - return q; - // checkVars() - // *******************************************************************// -} - +'use strict'; + +/* Serialization of RDF Graphs +** +** Tim Berners-Lee 2006 +** This is was http://dig.csail.mit.edu/2005/ajar/ajaw/js/rdf/serialize.js +** This is or was https://github.com/linkeddata/rdflib.js/blob/master/src/serializer.js +** Licence: MIT +*/ +var NamedNode = _dereq_('./named-node'); +var BlankNode = _dereq_('./blank-node'); +var Uri = _dereq_('./uri'); +var Util = _dereq_('./util'); +var XSD = _dereq_('./xsd'); + +var Serializer = function () { + var __Serializer = function __Serializer(store) { + this.flags = ''; + this.base = null; + + this.prefixes = []; // suggested prefixes + this.namespaces = []; // complementary indexes + + this.suggestPrefix('rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'); // XML code assumes this! + this.suggestPrefix('xml', 'reserved:reservedForFutureUse'); // XML reserves xml: in the spec. + + this.namespacesUsed = []; // Count actually used and so needed in @prefixes + this.keywords = ['a']; // The only one we generate at the moment + this.prefixchars = 'abcdefghijklmnopqustuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + this.incoming = null; // Array not calculated yet + this.formulas = []; // remebering original formulae from hashes + this.store = store; + }; + + __Serializer.prototype.setBase = function (base) { + this.base = base;return this; + }; + + __Serializer.prototype.setFlags = function (flags) { + this.flags = flags || '';return this; + }; + + __Serializer.prototype.toStr = function (x) { + var s = x.toNT(); + if (x.termType === 'Graph') { + this.formulas[s] = x; // remember as reverse does not work + } + return s; + }; + + __Serializer.prototype.fromStr = function (s) { + if (s[0] === '{') { + var x = this.formulas[s]; + if (!x) console.log('No formula object for ' + s); + return x; + } + return this.store.fromNT(s); + }; + /* Accumulate Namespaces + ** + ** These are only hints. If two overlap, only one gets used + ** There is therefore no guarantee in general. + */ + __Serializer.prototype.suggestPrefix = function (prefix, uri) { + if (prefix.slice(0, 7) === 'default') return; // Try to weed these out + if (prefix.slice(0, 2) === 'ns') return; // From others inferior algos + if (!prefix || !uri) return; // empty strings not suitable + if (prefix in this.namespaces || uri in this.prefixes) return; // already used + this.prefixes[uri] = prefix; + this.namespaces[prefix] = uri; + }; + + // Takes a namespace -> prefix map + __Serializer.prototype.suggestNamespaces = function (namespaces) { + for (var px in namespaces) { + this.suggestPrefix(px, namespaces[px]); + } + return this; + }; + + __Serializer.prototype.checkIntegrity = function () { + var p, ns; + for (p in this.namespaces) { + if (this.prefixes[this.namespaces[p]] !== p) { + throw new Error('Serializer integity error 1: ' + p + ', ' + this.namespaces[p] + ', ' + this.prefixes[this.namespaces[p]] + '!'); + } + } + for (ns in this.prefixes) { + if (this.namespaces[this.prefixes[ns]] !== ns) { + throw new Error('Serializer integity error 2: ' + ns + ', ' + this.prefixs[ns] + ', ' + this.namespaces[this.prefixes[ns]] + '!'); + } + } + }; + + // Make up an unused prefix for a random namespace + __Serializer.prototype.makeUpPrefix = function (uri) { + var p = uri; + function canUseMethod(pp) { + if (!__Serializer.prototype.validPrefix.test(pp)) return false; // bad format + if (pp === 'ns') return false; // boring + if (pp in this.namespaces) return false; // already used + this.prefixes[uri] = pp; + this.namespaces[pp] = uri; + return pp; + } + var canUse = canUseMethod.bind(this); + + if ('#/'.indexOf(p[p.length - 1]) >= 0) p = p.slice(0, -1); + var slash = p.lastIndexOf('/'); + if (slash >= 0) p = p.slice(slash + 1); + var i = 0; + while (i < p.length) { + if (this.prefixchars.indexOf(p[i])) { + i++; + } else { + break; + } + } + p = p.slice(0, i); + + if (p.length < 6 && canUse(p)) return p; // exact is best + if (canUse(p.slice(0, 3))) return p.slice(0, 3); + if (canUse(p.slice(0, 2))) return p.slice(0, 2); + if (canUse(p.slice(0, 4))) return p.slice(0, 4); + if (canUse(p.slice(0, 1))) return p.slice(0, 1); + if (canUse(p.slice(0, 5))) return p.slice(0, 5); + if (!__Serializer.prototype.validPrefix.test(p)) { + p = 'n'; // Otherwise the loop below may never termimnate + } + for (var j = 0;; j++) { + if (canUse(p.slice(0, 3) + j)) return p.slice(0, 3) + j; + } + }; + + __Serializer.prototype.rootSubjects = function (sts) { + var incoming = {}; + var subjects = {}; + var allBnodes = {}; + + /* This scan is to find out which nodes will have to be the roots of trees + ** in the serialized form. This will be any symbols, and any bnodes + ** which hve more or less than one incoming arc, and any bnodes which have + ** one incoming arc but it is an uninterrupted loop of such nodes back to itself. + ** This should be kept linear time with repect to the number of statements. + ** Note it does not use any indexing of the store. + */ + for (var i = 0; i < sts.length; i++) { + var st = sts[i]; + var checkMentions = function checkMentions(x) { + if (!incoming.hasOwnProperty(x)) incoming[x] = []; + incoming[x].push(st.subject); // List of things which will cause this to be printed + }; + var st2 = [st.subject, st.predicate, st.object]; + st2.map(function (y) { + if (y.termType === 'BlankNode') { + allBnodes[y.toNT()] = true; + } else if (y.termType === 'Collection') { + y.elements.forEach(function (z) { + checkMentions(z); // bnodes in collections important + }); + } + }); + checkMentions(sts[i].object); + var ss = subjects[this.toStr(st.subject)]; // Statements with this as subject + if (!ss) ss = []; + ss.push(st); + subjects[this.toStr(st.subject)] = ss; // Make hash. @@ too slow for formula? + } + + var roots = []; + for (var xNT in subjects) { + if (!subjects.hasOwnProperty(xNT)) continue; + var y = this.fromStr(xNT); + if (y.termType !== 'BlankNode' || !incoming[y] || incoming[y].length !== 1) { + roots.push(y); + continue; + } + } + this.incoming = incoming; // Keep for serializing @@ Bug for nested formulas + + // Now do the scan using existing roots + var rootsHash = {}; + for (var k = 0; k < roots.length; k++) { + rootsHash[roots[k].toNT()] = true; + } + return { 'roots': roots, 'subjects': subjects, + 'rootsHash': rootsHash, 'incoming': incoming }; + }; + + // ////////////////////////////////////////////////////// + + __Serializer.prototype.toN3 = function (f) { + return this.statementsToN3(f.statements); + }; + + __Serializer.prototype._notQNameChars = '\t\r\n !"#$%&\'()*.,+/;<=>?@[\\]^`{|}~'; + __Serializer.prototype._notNameChars = __Serializer.prototype._notQNameChars + ':'; + + __Serializer.prototype.explicitURI = function (uri) { + if (this.flags.indexOf('r') < 0 && this.base) { + uri = Uri.refTo(this.base, uri); + } else if (this.flags.indexOf('u') >= 0) { + // Unicode encoding NTriples style + uri = backslashUify(uri); + } else { + uri = hexify(uri); + } + return '<' + uri + '>'; + }; + + __Serializer.prototype.statementsToNTriples = function (sts) { + var sorted = sts.slice(); + sorted.sort(); + var str = ''; + var rdfns = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; + var self = this; + var kb = this.store; + var termToNT = function termToNT(x) { + if (x.termType !== 'Collection') { + return self.atomicTermToN3(x); + } + var list = x.elements; + var rest = kb.sym(rdfns + 'nill'); + for (var i = list.length - 1; i >= 0; i--) { + var bnode = new BlankNode(); + str += termToNT(bnode) + ' ' + termToNT(kb.sym(rdfns + 'first')) + ' ' + termToNT(list[i]) + '.\n'; + str += termToNT(bnode) + ' ' + termToNT(kb.sym(rdfns + 'rest')) + ' ' + termToNT(rest) + '.\n'; + rest = bnode; + } + return self.atomicTermToN3(rest); + }; + for (var i = 0; i < sorted.length; i++) { + var st = sorted[i]; + var s = ''; + s += termToNT(st.subject) + ' '; + s += termToNT(st.predicate) + ' '; + s += termToNT(st.object) + ' '; + if (this.flags.indexOf('q') >= 0) { + // Do quads not nrtiples + s += termToNT(st.why) + ' '; + } + s += '.\n'; + str += s; + } + return str; + }; + + __Serializer.prototype.statementsToN3 = function (sts) { + var indent = 4; + var width = 80; + var kb = this.store; + // A URI Map alows us to put the type statemnts at the top. + var uriMap = { 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type': 'aaa:00' }; + var SPO = function SPO(x, y) { + // Do limited canonicalization of bnodes + return Util.heavyCompareSPO(x, y, kb, uriMap); + }; + sts.sort(SPO); + + if (this.base && !this.defaultNamespace) { + this.defaultNamespace = this.base + '#'; + } + + var predMap = {}; + if (this.flags.indexOf('s') < 0) { + predMap['http://www.w3.org/2002/07/owl#sameAs'] = '='; + } + if (this.flags.indexOf('t') < 0) { + predMap['http://www.w3.org/1999/02/22-rdf-syntax-ns#type'] = 'a'; + } + if (this.flags.indexOf('i') < 0) { + predMap['http://www.w3.org/2000/10/swap/log#implies'] = '=>'; + } + // //////////////////////// Arrange the bits of text + + var spaces = function spaces(n) { + var s = ''; + for (var i = 0; i < n; i++) { + s += ' '; + }return s; + }; + + var treeToLine = function treeToLine(tree) { + var str = ''; + for (var i = 0; i < tree.length; i++) { + var branch = tree[i]; + var s2 = typeof branch === 'string' ? branch : treeToLine(branch); + // Note the space before the dot in case statement ends 123. which is in fact allowed but be conservative. + if (i !== 0) { + var ch = str.slice(-1) || ' '; + if (s2 === ',' || s2 === ';') { + // no gap + } else if (s2 === '.' && !'0123456789.'.includes(ch)) {// no gap except after number + // no gap + } else { + str += ' '; // separate from previous token + } + } + str += s2; + } + return str; + }; + + // Convert a nested tree of lists and strings to a string + var treeToString = function treeToString(tree, level) { + var str = ''; + var lastLength = 100000; + if (level === undefined) level = -1; + for (var i = 0; i < tree.length; i++) { + var branch = tree[i]; + if (typeof branch !== 'string') { + var substr = treeToString(branch, level + 1); + if (substr.length < 10 * (width - indent * level) && substr.indexOf('"""') < 0) { + // Don't mess up multiline strings + var line = treeToLine(branch); + if (line.length < width - indent * level) { + branch = line; // Note! treat as string below + substr = ''; + } + } + if (substr) lastLength = 10000; + str += substr; + } + if (typeof branch === 'string') { + if (branch.length === 1 && str.slice(-1) === '\n') { + if (',.;'.indexOf(branch) >= 0) { + str = str.slice(0, -1) + branch + '\n'; // slip punct'n on end + lastLength += 1; + continue; + } + } + if (lastLength < indent * level + 4 || // if new line not necessary + lastLength + branch.length + 1 < width && ';.'.indexOf(str[str.length - 2]) < 0) { + // or the string fits on last line + str = str.slice(0, -1) + ' ' + branch + '\n'; // then continue on this line + lastLength += branch.length + 1; + } else { + var _line = spaces(indent * level) + branch; + str += _line + '\n'; + lastLength = _line.length; + if (level < 0) { + str += '\n'; // extra blank line + lastLength = 100000; // don't touch + } + } + } + } + return str; + }; + + // //////////////////////////////////////////// Structure for N3 + // Convert a set of statements into a nested tree of lists and strings + function statementListToTreeMethod(statements) { + var stats = this.rootSubjects(statements); + var roots = stats.roots; + var results = []; + for (var i = 0; i < roots.length; i++) { + var root = roots[i]; + results.push(subjectTree(root, stats)); + } + return results; + } + var statementListToTree = statementListToTreeMethod.bind(this); + + // The tree for a subject + function subjectTree(subject, stats) { + if (subject.termType === 'BlankNode' && !stats.incoming[subject]) { + return objectTree(subject, stats, true).concat(['.']); // Anonymous bnode subject + } + return [termToN3(subject, stats)].concat([propertyTree(subject, stats)]).concat(['.']); + } + // The property tree for a single subject or anonymous node + function propertyTreeMethod(subject, stats) { + var results = []; + var lastPred = null; + var sts = stats.subjects[this.toStr(subject)] || []; // relevant statements + if (typeof sts === 'undefined') { + throw new Error('Cant find statements for ' + subject); + } + + var objects = []; + for (var i = 0; i < sts.length; i++) { + var st = sts[i]; + if (st.predicate.uri === lastPred) { + objects.push(','); + } else { + if (lastPred) { + results = results.concat([objects]).concat([';']); + objects = []; + } + results.push(predMap[st.predicate.uri] ? predMap[st.predicate.uri] : termToN3(st.predicate, stats)); + } + lastPred = st.predicate.uri; + objects.push(objectTree(st.object, stats)); + } + results = results.concat([objects]); + return results; + } + var propertyTree = propertyTreeMethod.bind(this); + + function objectTreeMethod(obj, stats, force) { + if (obj.termType === 'BlankNode' && (force || stats.rootsHash[obj.toNT()] === undefined)) { + // if not a root + if (stats.subjects[this.toStr(obj)]) { + return ['[', propertyTree(obj, stats), ']']; + } else { + return '[]'; + } + } + return termToN3(obj, stats); + } + var objectTree = objectTreeMethod.bind(this); + + function termToN3Method(expr, stats) { + // + var i, res; + switch (expr.termType) { + case 'Graph': + res = ['{']; + res = res.concat(statementListToTree(expr.statements)); + return res.concat(['}']); + + case 'Collection': + res = ['(']; + for (i = 0; i < expr.elements.length; i++) { + res.push([objectTree(expr.elements[i], stats)]); + } + res.push(')'); + return res; + + default: + return this.atomicTermToN3(expr); + } + } + __Serializer.prototype.termToN3 = termToN3; + var termToN3 = termToN3Method.bind(this); + + function prefixDirectivesMethod() { + var str = ''; + if (this.defaultNamespace) { + str += '@prefix : ' + this.explicitURI(this.defaultNamespace) + '.\n'; + } + for (var ns in this.prefixes) { + if (!this.prefixes.hasOwnProperty(ns)) continue; + if (!this.namespacesUsed[ns]) continue; + str += '@prefix ' + this.prefixes[ns] + ': ' + this.explicitURI(ns) + '.\n'; + } + return str + '\n'; + } + var prefixDirectives = prefixDirectivesMethod.bind(this); + // Body of statementsToN3: + var tree = statementListToTree(sts); + return prefixDirectives() + treeToString(tree); + }; + // //////////////////////////////////////////// Atomic Terms + + // Deal with term level things and nesting with no bnode structure + __Serializer.prototype.atomicTermToN3 = function atomicTermToN3(expr, stats) { + switch (expr.termType) { + case 'BlankNode': + case 'Variable': + return expr.toNT(); + case 'Literal': + var val = expr.value.toString(); // should be a string already + if (expr.datatype && this.flags.indexOf('x') < 0) { + // Supress native numbers + switch (expr.datatype.uri) { + + case 'http://www.w3.org/2001/XMLSchema#integer': + return val; + + case 'http://www.w3.org/2001/XMLSchema#decimal': + // In urtle must have dot + if (val.indexOf('.') < 0) val += '.0'; + return val; + + case 'http://www.w3.org/2001/XMLSchema#double': + // Must force use of 'e' + if (val.indexOf('.') < 0) val += '.0'; + if (val.indexOf('e') < 0) val += 'e0'; + return val; + + case 'http://www.w3.org/2001/XMLSchema#boolean': + return expr.value ? 'true' : 'false'; + } + } + var str = this.stringToN3(expr.value); + if (expr.language) { + str += '@' + expr.language; + } else if (!expr.datatype.equals(XSD.string)) { + str += '^^' + this.atomicTermToN3(expr.datatype, stats); + } + return str; + case 'NamedNode': + return this.symbolToN3(expr); + default: + throw new Error('Internal: atomicTermToN3 cannot handle ' + expr + ' of termType: ' + expr.termType); + } + }; + + // stringToN3: String escaping for N3 + + __Serializer.prototype.validPrefix = new RegExp(/^[a-zA-Z][a-zA-Z0-9]*$/); + + __Serializer.prototype.forbidden1 = new RegExp(/[\\"\b\f\r\v\t\n\u0080-\uffff]/gm); + __Serializer.prototype.forbidden3 = new RegExp(/[\\"\b\f\r\v\u0080-\uffff]/gm); + __Serializer.prototype.stringToN3 = function stringToN3(str, flags) { + if (!flags) flags = 'e'; + var res = ''; + var i, j, k; + var delim; + var forbidden; + if (str.length > 20 && // Long enough to make sense + str.slice(-1) !== '"' && // corner case' + flags.indexOf('n') < 0 && ( // Force single line + str.indexOf('\n') > 0 || str.indexOf('"') > 0)) { + delim = '"""'; + forbidden = __Serializer.prototype.forbidden3; + } else { + delim = '"'; + forbidden = __Serializer.prototype.forbidden1; + } + for (i = 0; i < str.length;) { + forbidden.lastIndex = 0; + var m = forbidden.exec(str.slice(i)); + if (m == null) break; + j = i + forbidden.lastIndex - 1; + res += str.slice(i, j); + var ch = str[j]; + if (ch === '"' && delim === '"""' && str.slice(j, j + 3) !== '"""') { + res += ch; + } else { + k = '\b\f\r\t\v\n\\"'.indexOf(ch); // No escaping of bell (7)? + if (k >= 0) { + res += '\\' + 'bfrtvn\\"'[k]; + } else { + if (flags.indexOf('e') >= 0) { + // Unicode escaping in strings not unix style + res += '\\u' + ('000' + ch.charCodeAt(0).toString(16).toLowerCase()).slice(-4); + } else { + // no 'e' flag + res += ch; + } + } + } + i = j + 1; + } + return delim + res + str.slice(i) + delim; + }; + // A single symbol, either in <> or namespace notation + + __Serializer.prototype.symbolToN3 = function symbolToN3(x) { + // c.f. symbolString() in notation3.py + var uri = x.uri; + var j = uri.indexOf('#'); + if (j < 0 && this.flags.indexOf('/') < 0) { + j = uri.lastIndexOf('/'); + } + if (j >= 0 && this.flags.indexOf('p') < 0 && ( + // Can split at namespace but only if http[s]: URI or file: or ws[s] (why not others?) + uri.indexOf('http') === 0 || uri.indexOf('ws') === 0 || uri.indexOf('file') === 0)) { + var canSplit = true; + for (var k = j + 1; k < uri.length; k++) { + if (__Serializer.prototype._notNameChars.indexOf(uri[k]) >= 0) { + canSplit = false; + break; + } + } + /* + if (uri.slice(0, j + 1) === this.base + '#') { // base-relative + if (canSplit) { + return ':' + uri.slice(j + 1) // assume deafult ns is local + } else { + return '<#' + uri.slice(j + 1) + '>' + } + } + */ + if (canSplit) { + var localid = uri.slice(j + 1); + var namesp = uri.slice(0, j + 1); + if (this.defaultNamespace && this.defaultNamespace === namesp && this.flags.indexOf('d') < 0) { + // d -> suppress default + if (this.flags.indexOf('k') >= 0 && this.keyords.indexOf(localid) < 0) { + return localid; + } + return ':' + localid; + } + // this.checkIntegrity() // @@@ Remove when not testing + var prefix = this.prefixes[namesp]; + if (!prefix) prefix = this.makeUpPrefix(namesp); + if (prefix) { + this.namespacesUsed[namesp] = true; + return prefix + ':' + localid; + } + // Fall though if can't do qname + } + } + return this.explicitURI(uri); + }; + // String escaping utilities + + function hexify(str) { + // also used in parser + return encodeURI(str); + } + + function backslashUify(str) { + var res = ''; + var k; + for (var i = 0; i < str.length; i++) { + k = str.charCodeAt(i); + if (k > 65535) { + res += '\\U' + ('00000000' + k.toString(16)).slice(-8); // convert to upper? + } else if (k > 126) { + res += '\\u' + ('0000' + k.toString(16)).slice(-4); + } else { + res += str[i]; + } + } + return res; + } + + // /////////////////////////// Quad store serialization + + // @para. write - a function taking a single string to be output + // + __Serializer.prototype.writeStore = function (write) { + var kb = this.store; + var fetcher = kb.fetcher; + var session = fetcher && fetcher.appNode; + + // The core data + + var sources = this.store.index[3]; + for (var s in sources) { + // -> assume we can use -> as short for log:semantics + var source = kb.fromNT(s); + if (session && source.sameTerm(session)) continue; + write('\n' + this.atomicTermToN3(source) + ' ' + this.atomicTermToN3(kb.sym('http://www.w3.org/2000/10/swap/log#semantics')) + ' { ' + this.statementsToN3(kb.statementsMatching(undefined, undefined, undefined, source)) + ' }.\n'); + } + + // The metadata from HTTP interactions: + + kb.statementsMatching(undefined, kb.sym('http://www.w3.org/2007/ont/link#requestedURI')).map(function (st) { + write('\n<' + st.object.value + '> log:metadata {\n'); + var sts = kb.statementsMatching(undefined, undefined, undefined, st.subject); + write(this.statementsToN3(this.statementsToN3(sts))); + write('}.\n'); + }); + + // Inferences we have made ourselves not attributable to anyone else + + var metaSources = []; + if (session) metaSources.push(session); + var metadata = []; + metaSources.map(function (source) { + metadata = metadata.concat(kb.statementsMatching(undefined, undefined, undefined, source)); + }); + write(this.statementsToN3(metadata)); + }; + + // ////////////////////////////////////////////// XML serialization + + __Serializer.prototype.statementsToXML = function (sts) { + var indent = 4; + var width = 80; + + var namespaceCounts = []; // which have been used + namespaceCounts['http://www.w3.org/1999/02/22-rdf-syntax-ns#'] = true; + + var liPrefix = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#_'; // prefix for ordered list items + + // //////////////////////// Arrange the bits of XML text + + var spaces = function spaces(n) { + var s = ''; + for (var i = 0; i < n; i++) { + s += ' '; + }return s; + }; + + var XMLtreeToLine = function XMLtreeToLine(tree) { + var str = ''; + for (var i = 0; i < tree.length; i++) { + var branch = tree[i]; + var s2 = typeof branch === 'string' ? branch : XMLtreeToLine(branch); + str += s2; + } + return str; + }; + + // Convert a nested tree of lists and strings to a string + var XMLtreeToString = function XMLtreeToString(tree, level) { + var str = ''; + var line; + var lastLength = 100000; + if (!level) level = 0; + for (var i = 0; i < tree.length; i++) { + var branch = tree[i]; + if (typeof branch !== 'string') { + var substr = XMLtreeToString(branch, level + 1); + if (substr.length < 10 * (width - indent * level) && substr.indexOf('"""') < 0) { + // Don't mess up multiline strings + line = XMLtreeToLine(branch); + if (line.length < width - indent * level) { + branch = ' ' + line; // @@ Hack: treat as string below + substr = ''; + } + } + if (substr) lastLength = 10000; + str += substr; + } + if (typeof branch === 'string') { + if (lastLength < indent * level + 4) { + // continue + str = str.slice(0, -1) + ' ' + branch + '\n'; + lastLength += branch.length + 1; + } else { + line = spaces(indent * level) + branch; + str += line + '\n'; + lastLength = line.length; + } + } else {// not string + } + } + return str; + }; + + function statementListToXMLTreeMethod(statements) { + this.suggestPrefix('rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'); + var stats = this.rootSubjects(statements); + var roots = stats.roots; + var results = []; + for (var i = 0; i < roots.length; i++) { + var root = roots[i]; + results.push(subjectXMLTree(root, stats)); + } + return results; + } + var statementListToXMLTree = statementListToXMLTreeMethod.bind(this); + + function escapeForXML(str) { + if (typeof str === 'undefined') return '@@@undefined@@@@'; + return str.replace(/[&<"]/g, function (m) { + switch (m[0]) { + case '&': + return '&'; + case '<': + return '<'; + case '"': + return '"'; // ' + } + }); + } + + function relURIMethod(term) { + return escapeForXML(this.base ? Util.uri.refTo(this.base, term.uri) : term.uri); + } + var relURI = relURIMethod.bind(this); + + // The tree for a subject + function subjectXMLTreeMethod(subject, stats) { + var results = []; + var type, t, st, pred; + var sts = stats.subjects[this.toStr(subject)]; // relevant statements + if (typeof sts === 'undefined') { + // empty bnode + return propertyXMLTree(subject, stats); + } + + // Sort only on the predicate, leave the order at object + // level undisturbed. This leaves multilingual content in + // the order of entry (for partner literals), which helps + // readability. + // + // For the predicate sort, we attempt to split the uri + // as a hint to the sequence + sts.sort(function (a, b) { + var ap = a.predicate.uri; + var bp = b.predicate.uri; + if (ap.substring(0, liPrefix.length) === liPrefix || bp.substring(0, liPrefix.length) === liPrefix) { + // we're only interested in sorting list items + return ap.localeCompare(bp); + } + + var as = ap.substring(liPrefix.length); + var bs = bp.substring(liPrefix.length); + var an = parseInt(as, 10); + var bn = parseInt(bs, 10); + if (isNaN(an) || isNaN(bn) || an !== as || bn !== bs) { + // we only care about integers + return ap.localeCompare(bp); + } + + return an - bn; + }); + + for (var i = 0; i < sts.length; i++) { + st = sts[i]; + // look for a type + if (st.predicate.uri === 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' && !type && st.object.termType === 'symbol') { + type = st.object; + continue; // don't include it as a child element + } + + // see whether predicate can be replaced with "li" + pred = st.predicate; + if (pred.uri.substr(0, liPrefix.length) === liPrefix) { + var number = pred.uri.substr(liPrefix.length); + // make sure these are actually numeric list items + var intNumber = parseInt(number, 10); + if (number === intNumber.toString()) { + // was numeric; don't need to worry about ordering since we've already + // sorted the statements + pred = new NamedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#li'); + } + } + + t = qname(pred); + switch (st.object.termType) { + case 'BlankNode': + if (stats.incoming[st.object].length === 1) { + // there should always be something in the incoming array for a bnode + results = results.concat(['<' + t + ' rdf:parseType="Resource">', subjectXMLTree(st.object, stats), '']); + } else { + results = results.concat(['<' + t + ' rdf:nodeID="' + st.object.toNT().slice(2) + '"/>']); + } + break; + case 'NamedNode': + results = results.concat(['<' + t + ' rdf:resource="' + relURI(st.object) + '"/>']); + break; + case 'Literal': + results = results.concat(['<' + t + (st.object.datatype.equals(XSD.string) ? '' : ' rdf:datatype="' + escapeForXML(st.object.datatype.uri) + '"') + (st.object.language ? ' xml:lang="' + st.object.language + '"' : '') + '>' + escapeForXML(st.object.value) + '']); + break; + case 'Collection': + results = results.concat(['<' + t + ' rdf:parseType="Collection">', collectionXMLTree(st.object, stats), '']); + break; + default: + throw new Error("Can't serialize object of type " + st.object.termType + ' into XML'); + } // switch + } + + var tag = type ? qname(type) : 'rdf:Description'; + + var attrs = ''; + if (subject.termType === 'BlankNode') { + if (!stats.incoming[subject] || stats.incoming[subject].length !== 1) { + // not an anonymous bnode + attrs = ' rdf:nodeID="' + subject.toNT().slice(2) + '"'; + } + } else { + attrs = ' rdf:about="' + relURI(subject) + '"'; + } + + return ['<' + tag + attrs + '>'].concat([results]).concat(['']); + } + + var subjectXMLTree = subjectXMLTreeMethod.bind(this); + + function collectionXMLTree(subject, stats) { + var res = []; + for (var i = 0; i < subject.elements.length; i++) { + res.push(subjectXMLTree(subject.elements[i], stats)); + } + return res; + } + + // The property tree for a single subject or anonymos node + function propertyXMLTreeMethod(subject, stats) { + var results = []; + var sts = stats.subjects[this.toStr(subject)]; // relevant statements + if (!sts) return results; // No relevant statements + sts.sort(); + for (var i = 0; i < sts.length; i++) { + var st = sts[i]; + switch (st.object.termType) { + case 'BlankNode': + if (stats.rootsHash[st.object.toNT()]) { + // This bnode has been done as a root -- no content here @@ what bout first time + results = results.concat(['<' + qname(st.predicate) + ' rdf:nodeID="' + st.object.toNT().slice(2) + '">', '']); + } else { + results = results.concat(['<' + qname(st.predicate) + ' rdf:parseType="Resource">', propertyXMLTree(st.object, stats), '']); + } + break; + case 'NamedNode': + results = results.concat(['<' + qname(st.predicate) + ' rdf:resource="' + relURI(st.object) + '"/>']); + break; + case 'Literal': + results = results.concat(['<' + qname(st.predicate) + (st.object.datatype.equals(XSD.string) ? '' : ' rdf:datatype="' + escapeForXML(st.object.datatype.value) + '"') + (st.object.language ? ' xml:lang="' + st.object.language + '"' : '') + '>' + escapeForXML(st.object.value) + '']); + break; + case 'Collection': + results = results.concat(['<' + qname(st.predicate) + ' rdf:parseType="Collection">', collectionXMLTree(st.object, stats), '']); + break; + default: + throw new Error("Can't serialize object of type " + st.object.termType + ' into XML'); + } // switch + } + return results; + } + var propertyXMLTree = propertyXMLTreeMethod.bind(this); + + function qnameMethod(term) { + var uri = term.uri; + + var j = uri.indexOf('#'); + if (j < 0 && this.flags.indexOf('/') < 0) { + j = uri.lastIndexOf('/'); + } + if (j < 0) throw new Error('Cannot make qname out of <' + uri + '>'); + + for (var k = j + 1; k < uri.length; k++) { + if (__Serializer.prototype._notNameChars.indexOf(uri[k]) >= 0) { + throw new Error('Invalid character "' + uri[k] + '" cannot be in XML qname for URI: ' + uri); + } + } + var localid = uri.slice(j + 1); + var namesp = uri.slice(0, j + 1); + if (this.defaultNamespace && this.defaultNamespace === namesp && this.flags.indexOf('d') < 0) { + // d -> suppress default + return localid; + } + var prefix = this.prefixes[namesp]; + if (!prefix) prefix = this.makeUpPrefix(namesp); + namespaceCounts[namesp] = true; + return prefix + ':' + localid; + } + var qname = qnameMethod.bind(this); + + // Body of toXML: + + var tree = statementListToXMLTree(sts); + var str = '']; // @@ namespace declrations + return XMLtreeToString(tree2, -1); + }; // End @@ body + + var Serializer = function Serializer(store) { + return new __Serializer(store); + }; + return Serializer; +}(); + +module.exports = Serializer; +},{"./blank-node":51,"./named-node":66,"./uri":81,"./util":82,"./xsd":84}],77:[function(_dereq_,module,exports){ +'use strict'; + +// Converting between SPARQL queries and the $rdf query API +/* + +function SQuery () { + this.terms = [] + return this +} + +STerm.prototype.toString = STerm.val +SQuery.prototype.add = function (str) {this.terms.push()}*/ + +var log = _dereq_('./log'); +var Query = _dereq_('./query').Query; +// const Fetcher = require('./fetcher') + +/** + * @SPARQL: SPARQL text that is converted to a query object which is returned. + * @testMode: testing flag. Prevents loading of sources. + */ +function SPARQLToQuery(SPARQL, testMode, kb) { + // AJAR_ClearTable() + var variableHash = []; + function makeVar(name) { + if (variableHash[name]) { + return variableHash[name]; + } + var newVar = kb.variable(name); + variableHash[name] = newVar; + return newVar; + } + + // term type functions + function isRealText(term) { + return typeof term === 'string' && term.match(/[^ \n\t]/); + } + function isVar(term) { + return typeof term === 'string' && term.match(/^[\?\$]/); + } + function fixSymbolBrackets(term) { + if (typeof term === 'string') { + return term.replace(/^</, '<').replace(/>$/, '>'); + } else { + return term; + } + } + function isSymbol(term) { + return typeof term === 'string' && term.match(/^<[^>]*>$/); + } + function isBnode(term) { + return typeof term === 'string' && (term.match(/^_:/) || term.match(/^$/)); + } + function isPrefix(term) { + return typeof term === 'string' && term.match(/:$/); + } + function isPrefixedSymbol(term) { + return typeof term === 'string' && term.match(/^:|^[^_][^:]*:/); + } + function getPrefix(term) { + var a = term.split(':'); + return a[0]; + } + function getSuffix(term) { + var a = term.split(':'); + return a[1]; + } + function removeBrackets(term) { + if (isSymbol(term)) { + return term.slice(1, term.length - 1); + } else { + return term; + } + } + // takes a string and returns an array of strings and Literals in the place of literals + function parseLiterals(str) { + // var sin = (str.indexOf(/[ \n]\'/)==-1)?null:str.indexOf(/[ \n]\'/), doub = (str.indexOf(/[ \n]\"/)==-1)?null:str.indexOf(/[ \n]\"/) + var sin = str.indexOf("'") === -1 ? null : str.indexOf("'"); + var doub = str.indexOf('"') === -1 ? null : str.indexOf('"'); + // alert("S: "+sin+" D: "+doub) + if (!sin && !doub) { + var a = new Array(1); + a[0] = str; + return a; + } + var res = new Array(2); + var br; + var ind; + if (!sin || doub && doub < sin) { + br = '"'; + ind = doub; + } else if (!doub || sin && sin < doub) { + br = "'"; + ind = sin; + } else { + log.error('SQARQL QUERY OOPS!'); + return res; + } + res[0] = str.slice(0, ind); + var end = str.slice(ind + 1).indexOf(br); + if (end === -1) { + log.error('SPARQL parsing error: no matching parentheses in literal ' + str); + return str; + } + // alert(str.slice(end + ind + 2).match(/^\^\^/)) + var end2; + if (str.slice(end + ind + 2).match(/^\^\^/)) { + end2 = str.slice(end + ind + 2).indexOf(' '); + // alert(end2) + res[1] = kb.literal(str.slice(ind + 1, ind + 1 + end), '', kb.sym(removeBrackets(str.slice(ind + 4 + end, ind + 2 + end + end2)))); + // alert(res[1].datatype.uri) + res = res.concat(parseLiterals(str.slice(end + ind + 3 + end2))); + } else if (str.slice(end + ind + 2).match(/^@/)) { + end2 = str.slice(end + ind + 2).indexOf(' '); + // alert(end2) + res[1] = kb.literal(str.slice(ind + 1, ind + 1 + end), str.slice(ind + 3 + end, ind + 2 + end + end2), null); + // alert(res[1].datatype.uri) + res = res.concat(parseLiterals(str.slice(end + ind + 2 + end2))); + } else { + res[1] = kb.literal(str.slice(ind + 1, ind + 1 + end), '', null); + log.info('Literal found: ' + res[1]); + res = res.concat(parseLiterals(str.slice(end + ind + 2))); // finds any other literals + } + return res; + } + + function spaceDelimit(str) { + str = str.replace(/\(/g, ' ( ').replace(/\)/g, ' ) ').replace(//g, '> ').replace(/{/g, ' { ').replace(/}/g, ' } ').replace(/[\t\n\r]/g, ' ').replace(/; /g, ' ; ').replace(/\. /g, ' . ').replace(/, /g, ' , '); + log.info('New str into spaceDelimit: \n' + str); + var res = []; + var br = str.split(' '); + for (var x in br) { + if (isRealText(br[x])) { + res = res.concat(br[x]); + } + } + return res; + } + + function replaceKeywords(input) { + var strarr = input; + for (var x = 0; x < strarr.length; x++) { + if (strarr[x] === 'a') { + strarr[x] = ''; + } + if (strarr[x] === 'is' && strarr[x + 2] === 'of') { + strarr.splice(x, 1); + strarr.splice(x + 1, 1); + var s = strarr[x - 1]; + strarr[x - 1] = strarr[x + 1]; + strarr[x + 1] = s; + } + } + return strarr; + } + + function toTerms(input) { + var res = []; + for (var x = 0; x < input.length; x++) { + if (typeof input[x] !== 'string') { + res[x] = input[x]; + continue; + } + input[x] = fixSymbolBrackets(input[x]); + if (isVar(input[x])) { + res[x] = makeVar(input[x].slice(1)); + } else if (isBnode(input[x])) { + log.info(input[x] + ' was identified as a bnode.'); + res[x] = kb.bnode(); + } else if (isSymbol(input[x])) { + log.info(input[x] + ' was identified as a symbol.'); + res[x] = kb.sym(removeBrackets(input[x])); + } else if (isPrefixedSymbol(input[x])) { + log.info(input[x] + ' was identified as a prefixed symbol'); + if (prefixes[getPrefix(input[x])]) { + res[x] = kb.sym(input[x] = prefixes[getPrefix(input[x])] + getSuffix(input[x])); + } else { + log.error('SPARQL error: ' + input[x] + ' with prefix ' + getPrefix(input[x]) + ' does not have a correct prefix entry.'); + res[x] = input[x]; + } + } else { + res[x] = input[x]; + } + } + return res; + } + + function tokenize(str) { + var token1 = parseLiterals(str); + var token2 = []; + for (var x in token1) { + if (typeof token1[x] === 'string') { + token2 = token2.concat(spaceDelimit(token1[x])); + } else { + token2 = token2.concat(token1[x]); + } + } + token2 = replaceKeywords(token2); + log.info('SPARQL Tokens: ' + token2); + return token2; + } + + // CASE-INSENSITIVE + function arrayIndexOf(str, arr) { + for (var i = 0; i < arr.length; i++) { + if (typeof arr[i] !== 'string') { + continue; + } + if (arr[i].toLowerCase() === str.toLowerCase()) { + return i; + } + } + // log.warn("No instance of "+str+" in array "+arr) + return null; + } + + // CASE-INSENSITIVE + function arrayIndicesOf(str, arr) { + var ind = []; + for (var i = 0; i < arr.length; i++) { + if (typeof arr[i] !== 'string') { + continue; + } + if (arr[i].toLowerCase() === str.toLowerCase()) { + ind.push(i); + } + } + return ind; + } + + function setVars(input, query) { + log.info('SPARQL vars: ' + input); + for (var x in input) { + if (isVar(input[x])) { + log.info('Added ' + input[x] + ' to query variables from SPARQL'); + var v = makeVar(input[x].slice(1)); + query.vars.push(v); + v.label = input[x].slice(1); + } else { + log.warn('Incorrect SPARQL variable in SELECT: ' + input[x]); + } + } + } + + function getPrefixDeclarations(input) { + var prefInd = arrayIndicesOf('PREFIX', input); + var res = []; + for (var i in prefInd) { + var a = input[prefInd[i] + 1]; + var b = input[prefInd[i] + 2]; + if (!isPrefix(a)) { + log.error('Invalid SPARQL prefix: ' + a); + } else if (!isSymbol(b)) { + log.error('Invalid SPARQL symbol: ' + b); + } else { + log.info('Prefix found: ' + a + ' -> ' + b); + var pref = getPrefix(a); + var symbol = removeBrackets(b); + res[pref] = symbol; + } + } + return res; + } + + function getMatchingBracket(arr, open, close) { + log.info('Looking for a close bracket of type ' + close + ' in ' + arr); + var index = 0; + for (var i = 0; i < arr.length; i++) { + if (arr[i] === open) { + index++; + } + if (arr[i] === close) { + index--; + } + if (index < 0) { + return i; + } + } + log.error('Statement had no close parenthesis in SPARQL query'); + return 0; + } + + function constraintGreaterThan(value) { + this.describe = function (varstr) { + return varstr + ' > ' + value.toNT(); + }; + this.test = function (term) { + if (term.value.match(/[0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?/)) { + return parseFloat(term.value) > parseFloat(value); + } else { + return term.toNT() > value.toNT(); + } + }; + return this; + } + + function constraintLessThan(value) { + // this is not the recommended usage. Should only work on literal, numeric, dateTime + this.describe = function (varstr) { + return varstr + ' < ' + value.toNT(); + }; + this.test = function (term) { + // this.describe = function (varstr) { return varstr + " < "+value } + if (term.value.match(/[0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?/)) { + return parseFloat(term.value) < parseFloat(value); + } else { + return term.toNT() < value.toNT(); + } + }; + return this; + } + // This should only work on literals but doesn't. + function ConstraintEqualTo(value) { + this.describe = function (varstr) { + return varstr + ' = ' + value.toNT(); + }; + this.test = function (term) { + return value.sameTerm(term); + }; + return this; + } + + // value must be a literal + function ConstraintRegexp(value) { + this.describe = function (varstr) { + return "REGEXP( '" + value + "' , " + varstr + ' )'; + }; + this.test = function (term) { + var str = value; + // str = str.replace(/^//,"").replace(//$/,"") + var rg = new RegExp(str); + if (term.value) { + return rg.test(term.value); + } else { + return false; + } + }; + } + + function setConstraint(input, pat) { + if (input.length === 3 && input[0].termType === 'Variable' && (input[2].termType === 'NamedNode' || input[2].termType === 'Literal')) { + if (input[1] === '=') { + log.debug('Constraint added: ' + input); + pat.constraints[input[0]] = new ConstraintEqualTo(input[2]); + } else if (input[1] === '>') { + log.debug('Constraint added: ' + input); + pat.constraints[input[0]] = new ConstraintEqualTo(input[2]); + } else if (input[1] === '<') { + log.debug('Constraint added: ' + input); + pat.constraints[input[0]] = new ConstraintEqualTo(input[2]); + } else { + log.warn("I don't know how to handle the constraint: " + input); + } + } else if (input.length === 6 && typeof input[0] === 'string' && input[0].toLowerCase() === 'regexp' && input[1] === '(' && input[5] === ')' && input[3] === ',' && input[4].termType === 'Variable' && input[2].termType === 'Literal') { + log.debug('Constraint added: ' + input); + pat.constraints[input[4]] = new ConstraintRegexp(input[2].value); + } + // log.warn("I don't know how to handle the constraint: "+input) + // alert("length: "+input.length+" input 0 type: "+input[0].termType+" input 1: "+input[1]+" input[2] type: "+input[2].termType) + } + + function setOptional(terms, pat) { + log.debug('Optional query: ' + terms + ' not yet implemented.'); + var opt = kb.formula(); + setWhere(terms, opt); + pat.optional.push(opt); + } + + function setWhere(input, pat) { + var terms = toTerms(input); + var end; + log.debug('WHERE: ' + terms); + var opt; + // var opt = arrayIndicesOf("OPTIONAL",terms) + while (arrayIndexOf('OPTIONAL', terms)) { + opt = arrayIndexOf('OPTIONAL', terms); + log.debug('OPT: ' + opt + ' ' + terms[opt] + ' in ' + terms); + if (terms[opt + 1] !== '{') { + log.warn('Bad optional opening bracket in word ' + opt); + } + end = getMatchingBracket(terms.slice(opt + 2), '{', '}'); + if (end === -1) { + log.error('No matching bracket in word ' + opt); + } else { + setOptional(terms.slice(opt + 2, opt + 2 + end), pat); + // alert(pat.statements[0].toNT()) + opt = arrayIndexOf('OPTIONAL', terms); + end = getMatchingBracket(terms.slice(opt + 2), '{', '}'); + terms.splice(opt, end + 3); + } + } + log.debug('WHERE after optionals: ' + terms); + while (arrayIndexOf('FILTER', terms)) { + var filt = arrayIndexOf('FILTER', terms); + if (terms[filt + 1] !== '(') { + log.warn('Bad filter opening bracket in word ' + filt); + } + end = getMatchingBracket(terms.slice(filt + 2), '(', ')'); + if (end === -1) { + log.error('No matching bracket in word ' + filt); + } else { + setConstraint(terms.slice(filt + 2, filt + 2 + end), pat); + filt = arrayIndexOf('FILTER', terms); + end = getMatchingBracket(terms.slice(filt + 2), '(', ')'); + terms.splice(filt, end + 3); + } + } + log.debug('WHERE after filters and optionals: ' + terms); + extractStatements(terms, pat); + } + + function extractStatements(terms, formula) { + var arrayZero = new Array(1); + arrayZero[0] = -1; // this is just to add the beginning of the where to the periods index. + var per = arrayZero.concat(arrayIndicesOf('.', terms)); + var stat = []; + for (var x = 0; x < per.length - 1; x++) { + stat[x] = terms.slice(per[x] + 1, per[x + 1]); + } + // Now it's in an array of statements + for (x in stat) { + // THIS MUST BE CHANGED FOR COMMA, SEMICOLON + log.info('s+p+o ' + x + ' = ' + stat[x]); + var subj = stat[x][0]; + stat[x].splice(0, 1); + var sem = arrayZero.concat(arrayIndicesOf(';', stat[x])); + sem.push(stat[x].length); + var stat2 = []; + for (var y = 0; y < sem.length - 1; y++) { + stat2[y] = stat[x].slice(sem[y] + 1, sem[y + 1]); + } + for (x in stat2) { + log.info('p+o ' + x + ' = ' + stat[x]); + var pred = stat2[x][0]; + stat2[x].splice(0, 1); + var com = arrayZero.concat(arrayIndicesOf(',', stat2[x])); + com.push(stat2[x].length); + var stat3 = []; + for (y = 0; y < com.length - 1; y++) { + stat3[y] = stat2[x].slice(com[y] + 1, com[y + 1]); + } + for (x in stat3) { + var obj = stat3[x][0]; + log.info('Subj=' + subj + ' Pred=' + pred + ' Obj=' + obj); + formula.add(subj, pred, obj); + } + } + } + } + + // ******************************* Body of SPARQLToQuery ***************************// + log.info('SPARQL input: \n' + SPARQL); + var q = new Query(); + var sp = tokenize(SPARQL); // first tokenize everything + var prefixes = getPrefixDeclarations(sp); + if (!prefixes.rdf) { + prefixes.rdf = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; + } + if (!prefixes.rdfs) { + prefixes.rdfs = 'http://www.w3.org/2000/01/rdf-schema#'; + } + var selectLoc = arrayIndexOf('SELECT', sp); + var whereLoc = arrayIndexOf('WHERE', sp); + if (selectLoc < 0 || whereLoc < 0 || selectLoc > whereLoc) { + log.error('Invalid or nonexistent SELECT and WHERE tags in SPARQL query'); + return false; + } + setVars(sp.slice(selectLoc + 1, whereLoc), q); + + setWhere(sp.slice(whereLoc + 2, sp.length - 1), q.pat); + + if (testMode) { + return q; + } + + for (var x in q.pat.statements) { + var st = q.pat.statements[x]; + if (st.subject.termType === 'NamedNode') { + /* && sf.isPending(st.subject.uri) */ // This doesn't work. + // sf.requestURI(st.subject.uri,"sparql:"+st.subject) Kenny: I remove these two + if (kb.fetcher) { + kb.fetcher.lookUpThing(st.subject, 'sparql:' + st.subject); + } + } + if (st.object.termType === 'NamedNode') { + /* && sf.isPending(st.object.uri) */ + // sf.requestURI(st.object.uri,"sparql:"+st.object) + if (kb.fetcher) { + kb.fetcher.lookUpThing(st.object, 'sparql:' + st.object); + } + } + } + // alert(q.pat) + return q; + // checkVars() + // *******************************************************************// +} + module.exports = SPARQLToQuery; },{"./log":64,"./query":72}],78:[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"); } } - -var Node = _dereq_('./node'); - -var Statement = function () { - function Statement(subject, predicate, object, graph) { - _classCallCheck(this, Statement); - - this.subject = Node.fromValue(subject); - this.predicate = Node.fromValue(predicate); - this.object = Node.fromValue(object); - this.why = graph; // property currently used by rdflib - } - - _createClass(Statement, [{ - key: 'equals', - value: function equals(other) { - return other.subject.equals(this.subject) && other.predicate.equals(this.predicate) && other.object.equals(this.object) && other.graph.equals(this.graph); - } - }, { - key: 'substitute', - value: function substitute(bindings) { - var y = new Statement(this.subject.substitute(bindings), this.predicate.substitute(bindings), this.object.substitute(bindings), this.why.substitute(bindings)); // 2016 - console.log('@@@ statement substitute:' + y); - return y; - } - }, { - key: 'toCanonical', - value: function toCanonical() { - var terms = [this.subject.toCanonical(), this.predicate.toCanonical(), this.object.toCanonical()]; - if (this.graph && this.graph.termType !== 'DefaultGraph') { - terms.push(this.graph.toCanonical()); - } - return terms.join(' ') + ' .'; - } - }, { - key: 'toNT', - value: function toNT() { - return [this.subject.toNT(), this.predicate.toNT(), this.object.toNT()].join(' ') + ' .'; - } - }, { - key: 'toString', - value: function toString() { - return this.toNT(); - } - }, { - key: 'graph', - get: function get() { - return this.why; - }, - set: function set(g) { - this.why = g; - } - }]); - - return Statement; -}(); - +'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"); } } + +var Node = _dereq_('./node'); + +var Statement = function () { + function Statement(subject, predicate, object, graph) { + _classCallCheck(this, Statement); + + this.subject = Node.fromValue(subject); + this.predicate = Node.fromValue(predicate); + this.object = Node.fromValue(object); + this.why = graph; // property currently used by rdflib + } + + _createClass(Statement, [{ + key: 'equals', + value: function equals(other) { + return other.subject.equals(this.subject) && other.predicate.equals(this.predicate) && other.object.equals(this.object) && other.graph.equals(this.graph); + } + }, { + key: 'substitute', + value: function substitute(bindings) { + var y = new Statement(this.subject.substitute(bindings), this.predicate.substitute(bindings), this.object.substitute(bindings), this.why.substitute(bindings)); // 2016 + console.log('@@@ statement substitute:' + y); + return y; + } + }, { + key: 'toCanonical', + value: function toCanonical() { + var terms = [this.subject.toCanonical(), this.predicate.toCanonical(), this.object.toCanonical()]; + if (this.graph && this.graph.termType !== 'DefaultGraph') { + terms.push(this.graph.toCanonical()); + } + return terms.join(' ') + ' .'; + } + }, { + key: 'toNT', + value: function toNT() { + return [this.subject.toNT(), this.predicate.toNT(), this.object.toNT()].join(' ') + ' .'; + } + }, { + key: 'toString', + value: function toString() { + return this.toNT(); + } + }, { + key: 'graph', + get: function get() { + return this.why; + }, + set: function set(g) { + this.why = g; + } + }]); + + return Statement; +}(); + module.exports = Statement; },{"./node":68}],79:[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; }; - -var _indexedFormula = _dereq_('./indexed-formula'); - -var _indexedFormula2 = _interopRequireDefault(_indexedFormula); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -// Joe Presbrey -// 2007-07-15 -// 2010-08-08 TimBL folded in Kenny's WEBDAV -// 2010-12-07 TimBL addred local file write code -var docpart = _dereq_('./uri').docpart; -var Fetcher = _dereq_('./fetcher'); -var graph = _dereq_('./data-factory').graph; - -var namedNode = _dereq_('./data-factory').namedNode; -var Namespace = _dereq_('./namespace'); -var Serializer = _dereq_('./serializer'); -var uriJoin = _dereq_('./uri').join; -var Util = _dereq_('./util'); - -var UpdateManager = function () { - var sparql = function sparql(store) { - this.store = store; - if (store.updater) { - throw new Error("You can't have two UpdateManagers for the same store"); - } - if (!store.fetcher) { - // The store must also/already have a fetcher - new Fetcher(store); - } - store.updater = this; - this.ifps = {}; - this.fps = {}; - this.ns = {}; - this.ns.link = Namespace('http://www.w3.org/2007/ont/link#'); - this.ns.http = Namespace('http://www.w3.org/2007/ont/http#'); - this.ns.httph = Namespace('http://www.w3.org/2007/ont/httph#'); - this.ns.ldp = Namespace('http://www.w3.org/ns/ldp#'); - this.ns.rdf = Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#'); - this.ns.rdfs = Namespace('http://www.w3.org/2000/01/rdf-schema#'); - this.ns.rdf = Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#'); - this.ns.owl = Namespace('http://www.w3.org/2002/07/owl#'); - - this.patchControl = []; // index of objects fro coordinating incomng and outgoing patches - }; - - sparql.prototype.patchControlFor = function (doc) { - if (!this.patchControl[doc.uri]) { - this.patchControl[doc.uri] = []; - } - return this.patchControl[doc.uri]; - }; - - // Returns The method string SPARQL or DAV or LOCALFILE or false if known, undefined if not known. - // - // Files have to have a specific annotaton that they are machine written, for safety. - // We don't actually check for write access on files. - // - sparql.prototype.editable = function (uri, kb) { - if (!uri) { - return false; // Eg subject is bnode, no known doc to write to - } - if (!kb) { - kb = this.store; - } - - if (uri.slice(0, 8) === 'file:///') { - if (kb.holds(kb.sym(uri), namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), namedNode('http://www.w3.org/2007/ont/link#MachineEditableDocument'))) { - return 'LOCALFILE'; - } - - var sts = kb.statementsMatching(kb.sym(uri), undefined, undefined); - - console.log('sparql.editable: Not MachineEditableDocument file ' + uri + '\n'); - console.log(sts.map(function (x) { - return x.toNT(); - }).join('\n')); - return false; - // @@ Would be nifty of course to see whether we actually have write acess first. - } - - var request; - var definitive = false; - var requests = kb.each(undefined, this.ns.link('requestedURI'), docpart(uri)); - - // Hack for the moment @@@@ 2016-02-12 - if (kb.holds(namedNode(uri), this.ns.rdf('type'), this.ns.ldp('Resource'))) { - return 'SPARQL'; - } - var i; - var method; - for (var r = 0; r < requests.length; r++) { - request = requests[r]; - if (request !== undefined) { - var response = kb.any(request, this.ns.link('response')); - if (request !== undefined) { - var acceptPatch = kb.each(response, this.ns.httph('accept-patch')); - if (acceptPatch.length) { - for (i = 0; i < acceptPatch.length; i++) { - method = acceptPatch[i].value.trim(); - if (method.indexOf('application/sparql-update') >= 0) return 'SPARQL'; - } - } - var author_via = kb.each(response, this.ns.httph('ms-author-via')); - if (author_via.length) { - for (i = 0; i < author_via.length; i++) { - method = author_via[i].value.trim(); - if (method.indexOf('SPARQL') >= 0) { - return 'SPARQL'; - } - if (method.indexOf('DAV') >= 0) { - return 'DAV'; - } - } - } - var status = kb.each(response, this.ns.http('status')); - if (status.length) { - for (i = 0; i < status.length; i++) { - if (status[i] === 200 || status[i] === 404) { - definitive = true; - // return false // A definitive answer - } - } - } - } else { - console.log('sparql.editable: No response for ' + uri + '\n'); - } - } - } - if (requests.length === 0) { - console.log('sparql.editable: No request for ' + uri + '\n'); - } else { - if (definitive) { - return false; // We have got a request and it did NOT say editable => not editable - } - } - console.log('sparql.editable: inconclusive for ' + uri + '\n'); - return undefined; // We don't know (yet) as we haven't had a response (yet) - }; - - // ///////// The identification of bnodes - - sparql.prototype.anonymize = function (obj) { - return obj.toNT().substr(0, 2) === '_:' && this._mentioned(obj) ? '?' + obj.toNT().substr(2) : obj.toNT(); - }; - - sparql.prototype.anonymizeNT = function (stmt) { - return this.anonymize(stmt.subject) + ' ' + this.anonymize(stmt.predicate) + ' ' + this.anonymize(stmt.object) + ' .'; - }; - - // A list of all bnodes occuring in a statement - sparql.prototype._statement_bnodes = function (st) { - return [st.subject, st.predicate, st.object].filter(function (x) { - return x.isBlank; - }); - }; - - // A list of all bnodes occuring in a list of statements - sparql.prototype._statement_array_bnodes = function (sts) { - var bnodes = []; - for (var i = 0; i < sts.length; i++) { - bnodes = bnodes.concat(this._statement_bnodes(sts[i])); - } - bnodes.sort(); // in place sort - result may have duplicates - var bnodes2 = []; - for (var j = 0; j < bnodes.length; j++) { - if (j === 0 || !bnodes[j].sameTerm(bnodes[j - 1])) { - bnodes2.push(bnodes[j]); - } - } - return bnodes2; - }; - - sparql.prototype._cache_ifps = function () { - // Make a cached list of [Inverse-]Functional properties - // Call this once before calling context_statements - this.ifps = {}; - var a = this.store.each(undefined, this.ns.rdf('type'), this.ns.owl('InverseFunctionalProperty')); - for (var i = 0; i < a.length; i++) { - this.ifps[a[i].uri] = true; - } - this.fps = {}; - a = this.store.each(undefined, this.ns.rdf('type'), this.ns.owl('FunctionalProperty')); - for (i = 0; i < a.length; i++) { - this.fps[a[i].uri] = true; - } - }; - - // Returns a context to bind a given node, up to a given depth - sparql.prototype._bnode_context2 = function (x, source, depth) { - // Return a list of statements which indirectly identify a node - // Depth > 1 if try further indirection. - // Return array of statements (possibly empty), or null if failure - var sts = this.store.statementsMatching(undefined, undefined, x, source); // incoming links - var y; - var res; - for (var i = 0; i < sts.length; i++) { - if (this.fps[sts[i].predicate.uri]) { - y = sts[i].subject; - if (!y.isBlank) { - return [sts[i]]; - } - if (depth) { - res = this._bnode_context2(y, source, depth - 1); - if (res) { - return res.concat([sts[i]]); - } - } - } - } - // outgoing links - sts = this.store.statementsMatching(x, undefined, undefined, source); - for (i = 0; i < sts.length; i++) { - if (this.ifps[sts[i].predicate.uri]) { - y = sts[i].object; - if (!y.isBlank) { - return [sts[i]]; - } - if (depth) { - res = this._bnode_context2(y, source, depth - 1); - if (res) { - return res.concat([sts[i]]); - } - } - } - } - return null; // Failure - }; - - // Returns the smallest context to bind a given single bnode - sparql.prototype._bnode_context_1 = function (x, source) { - // Return a list of statements which indirectly identify a node - // Breadth-first - var self = this; - for (var depth = 0; depth < 3; depth++) { - // Try simple first - var con = this._bnode_context2(x, source, depth); - if (con !== null) return con; - } - // If we can't guarantee unique with logic just send all info about node - return this.store.connectedStatements(x, source); // was: - // throw new Error('Unable to uniquely identify bnode: ' + x.toNT()) - }; - - sparql.prototype._mentioned = function (x) { - return this.store.statementsMatching(x).length !== 0 || // Don't pin fresh bnodes - this.store.statementsMatching(undefined, x).length !== 0 || this.store.statementsMatching(undefined, undefined, x).length !== 0; - }; - - sparql.prototype._bnode_context = function (bnodes, doc) { - var context = []; - if (bnodes.length) { - this._cache_ifps(); - for (var i = 0; i < bnodes.length; i++) { - // Does this occur in old graph? - var bnode = bnodes[i]; - if (!this._mentioned(bnode)) continue; - context = context.concat(this._bnode_context_1(bnode, doc)); - } - } - return context; - }; - - /* Weird code does not make sense -- some code corruption along the line -- st undefined -- weird - sparql.prototype._bnode_context = function(bnodes) { - var context = [] - if (bnodes.length) { - if (this.store.statementsMatching(st.subject.isBlank?undefined:st.subject, - st.predicate.isBlank?undefined:st.predicate, - st.object.isBlank?undefined:st.object, - st.why).length <= 1) { - context = context.concat(st) - } else { - this._cache_ifps() - for (x in bnodes) { - context = context.concat(this._bnode_context_1(bnodes[x], st.why)) - } - } - } - return context - } - */ - // Returns the best context for a single statement - sparql.prototype._statement_context = function (st) { - var bnodes = this._statement_bnodes(st); - return this._bnode_context(bnodes, st.why); - }; - - sparql.prototype._context_where = function (context) { - var sparql = this; - return !context || context.length === 0 ? '' : 'WHERE { ' + context.map(function (x) { - return sparql.anonymizeNT(x); - }).join('\n') + ' }\n'; - }; - - sparql.prototype._fire = function (uri, query, callback) { - if (!uri) { - throw new Error('No URI given for remote editing operation: ' + query); - } - console.log('sparql: sending update to <' + uri + '>'); - var xhr = Util.XMLHTTPFactory(); - xhr.options = {}; - - xhr.onreadystatechange = function () { - // dump("SPARQL update ready state for <"+uri+"> readyState="+xhr.readyState+"\n"+query+"\n") - if (xhr.readyState === 4) { - var success = !xhr.status || xhr.status >= 200 && xhr.status < 300; - if (!success) { - console.log('sparql: update failed for <' + uri + '> status=' + xhr.status + ', ' + xhr.statusText + ', body length=' + xhr.responseText.length + '\n for query: ' + query); - } else { - console.log('sparql: update Ok for <' + uri + '>'); - } - callback(uri, success, xhr.responseText, xhr); - } - }; - - xhr.open('PATCH', uri, true); // async=true - xhr.setRequestHeader('Content-type', 'application/sparql-update'); - xhr.send(query); - }; - - // This does NOT update the statement. - // It returns an object whcih includes - // function which can be used to change the object of the statement. - // - sparql.prototype.update_statement = function (statement) { - if (statement && !statement.why) { - return; - } - var sparql = this; - var context = this._statement_context(statement); - - return { - statement: statement ? [statement.subject, statement.predicate, statement.object, statement.why] : undefined, - statementNT: statement ? this.anonymizeNT(statement) : undefined, - where: sparql._context_where(context), - - set_object: function set_object(obj, callback) { - var query = this.where; - query += 'DELETE DATA { ' + this.statementNT + ' } ;\n'; - query += 'INSERT DATA { ' + this.anonymize(this.statement[0]) + ' ' + this.anonymize(this.statement[1]) + ' ' + this.anonymize(obj) + ' ' + ' . }\n'; - - sparql._fire(this.statement[3].uri, query, callback); - } - }; - }; - - sparql.prototype.insert_statement = function (st, callback) { - var st0 = st instanceof Array ? st[0] : st; - var query = this._context_where(this._statement_context(st0)); - - if (st instanceof Array) { - var stText = ''; - for (var i = 0; i < st.length; i++) { - stText += st[i] + '\n'; - }query += 'INSERT DATA { ' + stText + ' }\n'; - } else { - query += 'INSERT DATA { ' + this.anonymize(st.subject) + ' ' + this.anonymize(st.predicate) + ' ' + this.anonymize(st.object) + ' ' + ' . }\n'; - } - - this._fire(st0.why.uri, query, callback); - }; - - sparql.prototype.delete_statement = function (st, callback) { - var st0 = st instanceof Array ? st[0] : st; - var query = this._context_where(this._statement_context(st0)); - - if (st instanceof Array) { - var stText = ''; - for (var i = 0; i < st.length; i++) { - stText += st[i] + '\n'; - }query += 'DELETE DATA { ' + stText + ' }\n'; - } else { - query += 'DELETE DATA { ' + this.anonymize(st.subject) + ' ' + this.anonymize(st.predicate) + ' ' + this.anonymize(st.object) + ' ' + ' . }\n'; - } - - this._fire(st0.why.uri, query, callback); - }; - - // Request a now or future action to refresh changes coming downstream - // - // This is designed to allow the system to re-request the server version, - // when a websocket has pinged to say there are changes. - // If thewebsocket, by contrast, has sent a patch, then this may not be necessary. - // This may be called out of context so *this* cannot be used. - - sparql.prototype.requestDownstreamAction = function (doc, action) { - var control = this.patchControlFor(doc); - if (!control.pendingUpstream) { - action(doc); - } else { - if (control.downstreamAction) { - if (control.downstreamAction === action) { - return; - } else { - throw new Error("Can't wait for > 1 differnt downstream actions"); - } - } else { - control.downstreamAction = action; - } - } - }; - - // We want to start counting websockt notifications - // to distinguish the ones from others from our own. - sparql.prototype.clearUpstreamCount = function (doc) { - var control = this.patchControlFor(doc); - control.upstreamCount = 0; - }; - - sparql.prototype.getUpdatesVia = function (doc) { - var linkHeaders = this.store.fetcher.getHeader(doc, 'updates-via'); - if (!linkHeaders || !linkHeaders.length) return null; - return linkHeaders[0].trim(); - }; - - sparql.prototype.addDownstreamChangeListener = function (doc, listener) { - var control = this.patchControlFor(doc); - if (!control.downstreamChangeListeners) control.downstreamChangeListeners = []; - control.downstreamChangeListeners.push(listener); - var self = this; - this.setRefreshHandler(doc, function (doc) { - // a function not a method - self.reloadAndSync(doc); - }); - }; - - sparql.prototype.reloadAndSync = function (doc) { - var control = this.patchControlFor(doc); - var updater = this; - - if (control.reloading) { - console.log(' Already reloading - stop'); - return; // once only needed - } - control.reloading = true; - var retryTimeout = 1000; // ms - var tryReload = function tryReload() { - console.log('try reload - timeout = ' + retryTimeout); - updater.reload(updater.store, doc, function (ok, message, xhr) { - control.reloading = false; - if (ok) { - if (control.downstreamChangeListeners) { - for (var i = 0; i < control.downstreamChangeListeners.length; i++) { - console.log(' Calling downstream listener ' + i); - control.downstreamChangeListeners[i](); - } - } - } else { - if (xhr.status === 0) { - console.log('Network error refreshing the data. Retrying in ' + retryTimeout / 1000); - control.reloading = true; - retryTimeout = retryTimeout * 2; - setTimeout(tryReload, retryTimeout); - } else { - console.log('Error ' + xhr.status + 'refreshing the data:' + message + '. Stopped' + doc); - } - } - }); - }; - tryReload(); - }; - - // Set up websocket to listen on - // - // There is coordination between upstream changes and downstream ones - // so that a reload is not done in the middle of an upsteeam patch. - // If you usie this API then you get called when a change happens, and you - // have to reload the file yourself, and then refresh the UI. - // Alternative is addDownstreamChangeListener(), where you do not - // have to do the reload yourslf. Do mot mix them. - // - // kb contains the HTTP metadata from prefvious operations - // - sparql.prototype.setRefreshHandler = function (doc, handler) { - var wssURI = this.getUpdatesVia(doc); // relative - // var kb = this.store - var theHandler = handler; - var self = this; - var updater = this; - var retryTimeout = 1500; // *2 will be 3 Seconds, 6, 12, etc - var retries = 0; - - if (!wssURI) { - console.log('Server doies not support live updates thoughUpdates-Via :-('); - return false; - } - - wssURI = uriJoin(wssURI, doc.uri); - wssURI = wssURI.replace(/^http:/, 'ws:').replace(/^https:/, 'wss:'); - console.log('Web socket URI ' + wssURI); - - var openWebsocket = function openWebsocket() { - // From https://github.com/solid/solid-spec#live-updates - var socket; - if (typeof WebSocket !== 'undefined') { - socket = new WebSocket(wssURI); - } else if (typeof Services !== 'undefined') { - // Firefox add on http://stackoverflow.com/questions/24244886/is-websocket-supported-in-firefox-for-android-addons - socket = Services.wm.getMostRecentWindow('navigator:browser').WebSocket(wssURI); - } else if (typeof window !== 'undefined' && window.WebSocket) { - socket = window.WebSocket(wssURI); - } else { - console.log('Live update disabled, as WebSocket not supported by platform :-('); - return; - } - socket.onopen = function () { - console.log(' websocket open'); - retryTimeout = 1500; // reset timeout to fast on success - this.send('sub ' + doc.uri); - if (retries) { - console.log('Web socket has been down, better check for any news.'); - updater.requestDownstreamAction(doc, theHandler); - } - }; - var control = self.patchControlFor(doc); - control.upstreamCount = 0; - - // https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent - // - // 1000 CLOSE_NORMAL Normal closure; the connection successfully completed whatever purpose for which it was created. - // 1001 CLOSE_GOING_AWAY The endpoint is going away, either - // because of a server failure or because the browser is navigating away from the page that opened the connection. - // 1002 CLOSE_PROTOCOL_ERROR The endpoint is terminating the connection due to a protocol error. - // 1003 CLOSE_UNSUPPORTED The connection is being terminated because the endpoint - // received data of a type it cannot accept (for example, a text-only endpoint received binary data). - // 1004 Reserved. A meaning might be defined in the future. - // 1005 CLOSE_NO_STATUS Reserved. Indicates that no status code was provided even though one was expected. - // 1006 CLOSE_ABNORMAL Reserved. Used to indicate that a connection was closed abnormally ( - // - // - socket.onclose = function (event) { - console.log('*** Websocket closed with code ' + event.code + ", reason '" + event.reason + "' clean = " + event.clean); - retryTimeout *= 2; - retries += 1; - console.log('Retrying in ' + retryTimeout + 'ms'); // (ask user?) - setTimeout(function () { - console.log('Trying websocket again'); - openWebsocket(); - }, retryTimeout); - }; - socket.onmessage = function (msg) { - if (msg.data && msg.data.slice(0, 3) === 'pub') { - if ('upstreamCount' in control) { - control.upstreamCount -= 1; - if (control.upstreamCount >= 0) { - console.log('just an echo: ' + control.upstreamCount); - return; // Just an echo - } - } - console.log('Assume a real downstream change: ' + control.upstreamCount + ' -> 0'); - control.upstreamCount = 0; - self.requestDownstreamAction(doc, theHandler); - } - }; - }; // openWebsocket - openWebsocket(); - - return true; - }; - - // This high-level function updates the local store iff the web is changed successfully. - // - // - deletions, insertions may be undefined or single statements or lists or formulae. - // (may contain bnodes which can be indirectly identified by a where clause) - // - // - callback is called as callback(uri, success, errorbody) - // - sparql.prototype.update = function (deletions, insertions, callback) { - try { - var kb = this.store; - var ds = !deletions ? [] : deletions instanceof _indexedFormula2.default ? deletions.statements : deletions instanceof Array ? deletions : [deletions]; - var is = !insertions ? [] : insertions instanceof _indexedFormula2.default ? insertions.statements : insertions instanceof Array ? insertions : [insertions]; - if (!(ds instanceof Array)) { - throw new Error('Type Error ' + (typeof ds === 'undefined' ? 'undefined' : _typeof(ds)) + ': ' + ds); - } - if (!(is instanceof Array)) { - throw new Error('Type Error ' + (typeof is === 'undefined' ? 'undefined' : _typeof(is)) + ': ' + is); - } - if (ds.length === 0 && is.length === 0) { - return callback(null, true); // success -- nothing needed to be done. - } - var doc = ds.length ? ds[0].why : is[0].why; - var control = this.patchControlFor(doc); - var startTime = Date.now(); - - var props = ['subject', 'predicate', 'object', 'why']; - var verbs = ['insert', 'delete']; - var clauses = { 'delete': ds, 'insert': is }; - verbs.map(function (verb) { - clauses[verb].map(function (st) { - if (!doc.sameTerm(st.why)) { - throw new Error('update: destination ' + doc + ' inconsistent with delete quad ' + st.why); - } - props.map(function (prop) { - if (typeof st[prop] === 'undefined') { - throw new Error('update: undefined ' + prop + ' of statement.'); - } - }); - }); - }); - - var protocol = this.editable(doc.uri, kb); - if (!protocol) { - throw new Error("Can't make changes in uneditable " + doc); - } - var i; - var newSts; - var documentString; - var sz; - if (protocol.indexOf('SPARQL') >= 0) { - var bnodes = []; - if (ds.length) bnodes = this._statement_array_bnodes(ds); - if (is.length) bnodes = bnodes.concat(this._statement_array_bnodes(is)); - var context = this._bnode_context(bnodes, doc); - var whereClause = this._context_where(context); - var query = ''; - if (whereClause.length) { - // Is there a WHERE clause? - if (ds.length) { - query += 'DELETE { '; - for (i = 0; i < ds.length; i++) { - query += this.anonymizeNT(ds[i]) + '\n'; - } - query += ' }\n'; - } - if (is.length) { - query += 'INSERT { '; - for (i = 0; i < is.length; i++) { - query += this.anonymizeNT(is[i]) + '\n'; - } - query += ' }\n'; - } - query += whereClause; - } else { - // no where clause - if (ds.length) { - query += 'DELETE DATA { '; - for (i = 0; i < ds.length; i++) { - query += this.anonymizeNT(ds[i]) + '\n'; - } - query += ' } \n'; - } - if (is.length) { - if (ds.length) query += ' ; '; - query += 'INSERT DATA { '; - for (i = 0; i < is.length; i++) { - query += this.anonymizeNT(is[i]) + '\n'; - } - query += ' }\n'; - } - } - // Track pending upstream patches until they have fnished their callback - control.pendingUpstream = control.pendingUpstream ? control.pendingUpstream + 1 : 1; - if ('upstreamCount' in control) { - control.upstreamCount += 1; // count changes we originated ourselves - console.log('upstream count up to : ' + control.upstreamCount); - } - - this._fire(doc.uri, query, function (uri, success, body, xhr) { - xhr.elapsedTime_ms = Date.now() - startTime; - console.log(' sparql: Return ' + (success ? 'success' : 'FAILURE ' + xhr.status) + ' elapsed ' + xhr.elapsedTime_ms + 'ms'); - if (success) { - try { - kb.remove(ds); - } catch (e) { - success = false; - body = 'Remote Ok BUT error deleting ' + ds.length + ' from store!!! ' + e; - } // Add in any case -- help recover from weirdness?? - for (var i = 0; i < is.length; i++) { - kb.add(is[i].subject, is[i].predicate, is[i].object, doc); - } - } - - callback(uri, success, body, xhr); - control.pendingUpstream -= 1; - // When upstream patches have been sent, reload state if downstream waiting - if (control.pendingUpstream === 0 && control.downstreamAction) { - var downstreamAction = control.downstreamAction; - delete control.downstreamAction; - console.log('delayed downstream action:'); - downstreamAction(doc); - } - }); - } else if (protocol.indexOf('DAV') >= 0) { - // The code below is derived from Kenny's UpdateCenter.js - documentString; - var request = kb.any(doc, this.ns.link('request')); - if (!request) { - throw new Error('No record of our HTTP GET request for document: ' + doc); - } // should not happen - var response = kb.any(request, this.ns.link('response')); - if (!response) { - return null; // throw "No record HTTP GET response for document: "+doc - } - var content_type = kb.the(response, this.ns.httph('content-type')).value; - - // prepare contents of revised document - newSts = kb.statementsMatching(undefined, undefined, undefined, doc).slice(); // copy! - for (i = 0; i < ds.length; i++) { - Util.RDFArrayRemove(newSts, ds[i]); - } - for (i = 0; i < is.length; i++) { - newSts.push(is[i]); - } - - // serialize to te appropriate format - sz = Serializer(kb); - sz.suggestNamespaces(kb.namespaces); - sz.setBase(doc.uri); // ?? beware of this - kenny (why? tim) - switch (content_type) { - case 'application/rdf+xml': - documentString = sz.statementsToXML(newSts); - break; - case 'text/n3': - case 'text/turtle': - case 'application/x-turtle': // Legacy - case 'application/n3': - // Legacy - documentString = sz.statementsToN3(newSts); - break; - default: - throw new Error('Content-type ' + content_type + ' not supported for data write'); - } - - // Write the new version back - - var candidateTarget = kb.the(response, this.ns.httph('content-location')); - var targetURI; - if (candidateTarget) { - targetURI = uriJoin(candidateTarget.value, targetURI); - } - var xhr = Util.XMLHTTPFactory(); - xhr.options = {}; - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - // formula from sparqlUpdate.js, what about redirects? - var success = !xhr.status || xhr.status >= 200 && xhr.status < 300; - if (success) { - for (var i = 0; i < ds.length; i++) { - kb.remove(ds[i]); - } - for (i = 0; i < is.length; i++) { - kb.add(is[i].subject, is[i].predicate, is[i].object, doc); - } - } - callback(doc.uri, success, xhr.responseText); - } - }; - xhr.open('PUT', targetURI, true); - // assume the server does PUT content-negotiation. - xhr.setRequestHeader('Content-type', content_type); // OK? - xhr.send(documentString); - } else { - if (protocol.indexOf('LOCALFILE') >= 0) { - try { - console.log('Writing back to local file\n'); - // See http://simon-jung.blogspot.com/2007/10/firefox-extension-file-io.html - // prepare contents of revised document - newSts = kb.statementsMatching(undefined, undefined, undefined, doc).slice(); // copy! - for (i = 0; i < ds.length; i++) { - Util.RDFArrayRemove(newSts, ds[i]); - } - for (i = 0; i < is.length; i++) { - newSts.push(is[i]); - } - // serialize to the appropriate format - documentString; - sz = Serializer(kb); - sz.suggestNamespaces(kb.namespaces); - sz.setBase(doc.uri); // ?? beware of this - kenny (why? tim) - var dot = doc.uri.lastIndexOf('.'); - if (dot < 1) { - throw new Error('Rewriting file: No filename extension: ' + doc.uri); - } - var ext = doc.uri.slice(dot + 1); - switch (ext) { - case 'rdf': - case 'owl': // Just my experence ...@@ we should keep the format in which it was parsed - case 'xml': - documentString = sz.statementsToXML(newSts); - break; - case 'n3': - case 'nt': - case 'ttl': - documentString = sz.statementsToN3(newSts); - break; - default: - throw new Error('File extension .' + ext + ' not supported for data write'); - } - // Write the new version back - // create component for file writing - console.log('Writing back: <<<' + documentString + '>>>'); - var filename = doc.uri.slice(7); // chop off file:// leaving /path - // console.log("Writeback: Filename: "+filename+"\n") - var file = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile); - file.initWithPath(filename); - if (!file.exists()) { - throw new Error('Rewriting file <' + doc.uri + '> but it does not exist!'); - } - // { - // file.create( Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 420) - // } - // create file output stream and use write/create/truncate mode - // 0x02 writing, 0x08 create file, 0x20 truncate length if exist - var stream = Components.classes['@mozilla.org/network/file-output-stream;1'].createInstance(Components.interfaces.nsIFileOutputStream); - - // Various JS systems object to 0666 in struct mode as dangerous - stream.init(file, 0x02 | 0x08 | 0x20, parseInt('0666', 8), 0); - - // write data to file then close output stream - stream.write(documentString, documentString.length); - stream.close(); - - for (i = 0; i < ds.length; i++) { - kb.remove(ds[i]); - } - for (i = 0; i < is.length; i++) { - kb.add(is[i].subject, is[i].predicate, is[i].object, doc); - } - callback(doc.uri, true, ''); // success! - } catch (e) { - callback(doc.uri, false, 'Exception trying to write back file <' + doc.uri + '>\n' - // + tabulator.Util.stackString(e)) - ); - } - } else { - throw new Error("Unhandled edit method: '" + protocol + "' for " + doc); - } - } - } catch (e) { - callback(undefined, false, 'Exception in update: ' + e + '\n' + $rdf.Util.stackString(e)); - } - }; // wnd update - - // This suitable for an inital creation of a document - // - // data: string, or array of statements - // - sparql.prototype.put = function (doc, data, content_type, callback) { - var documentString; - var kb = this.store; - - if ((typeof data === 'undefined' ? 'undefined' : _typeof(data)) === _typeof('')) { - documentString = data; - } else { - // serialize to te appropriate format - var sz = Serializer(kb); - sz.suggestNamespaces(kb.namespaces); - sz.setBase(doc.uri); - switch (content_type) { - case 'application/rdf+xml': - documentString = sz.statementsToXML(data); - break; - case 'text/n3': - case 'text/turtle': - case 'application/x-turtle': // Legacy - case 'application/n3': - // Legacy - documentString = sz.statementsToN3(data); - break; - default: - throw new Error('Content-type ' + content_type + ' not supported for data PUT'); - } - } - var xhr = Util.XMLHTTPFactory(); - xhr.options = {}; - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - // formula from sparqlUpdate.js, what about redirects? - var success = !xhr.status || xhr.status >= 200 && xhr.status < 300; - if (success && typeof data !== 'string') { - data.map(function (st) { - kb.addStatement(st); - }); - // kb.fetcher.requested[doc.uri] = true // as though fetched - } - if (success) { - delete kb.fetcher.nonexistant[doc.uri]; - delete kb.fetcher.requested[doc.uri]; - // @@ later we can fake it has been requestd if put gives us the header sand we save them. - } - callback(doc.uri, success, xhr.responseText, xhr); - } - }; - xhr.open('PUT', doc.uri, true); - xhr.setRequestHeader('Content-type', content_type); - xhr.send(documentString); - }; - - // Reload a document. - // - // Fast and cheap, no metaata - // Measure times for the document - // Load it provisionally - // Don't delete the statemenst before the load, or it will leave a broken document - // in the meantime. - - sparql.prototype.reload = function (kb, doc, callback) { - var startTime = Date.now(); - // force sets no-cache and - kb.fetcher.nowOrWhenFetched(doc.uri, { force: true, noMeta: true, clearPreviousData: true }, function (ok, body, xhr) { - if (!ok) { - console.log(' ERROR reloading data: ' + body); - callback(false, 'Error reloading data: ' + body, xhr); - } else if (xhr.onErrorWasCalled || xhr.status !== 200) { - console.log(' Non-HTTP error reloading data! onErrorWasCalled=' + xhr.onErrorWasCalled + ' status: ' + xhr.status); - callback(false, 'Non-HTTP error reloading data: ' + body, xhr); - } else { - var elapsedTime_ms = Date.now() - startTime; - if (!doc.reloadTime_total) doc.reloadTime_total = 0; - if (!doc.reloadTime_count) doc.reloadTime_count = 0; - doc.reloadTime_total += elapsedTime_ms; - doc.reloadTime_count += 1; - console.log(' Fetch took ' + elapsedTime_ms + 'ms, av. of ' + doc.reloadTime_count + ' = ' + doc.reloadTime_total / doc.reloadTime_count + 'ms.'); - callback(true); - } - }); - }; - - sparql.prototype.oldReload = function (kb, doc, callback) { - var g2 = graph(); // A separate store to hold the data as we load it - var f2 = fetcher(g2); - var startTime = Date.now(); - // force sets no-cache and - f2.nowOrWhenFetched(doc.uri, { force: true, noMeta: true, clearPreviousData: true }, function (ok, body, xhr) { - if (!ok) { - console.log(' ERROR reloading data: ' + body); - callback(false, 'Error reloading data: ' + body, xhr); - } else if (xhr.onErrorWasCalled || xhr.status !== 200) { - console.log(' Non-HTTP error reloading data! onErrorWasCalled=' + xhr.onErrorWasCalled + ' status: ' + xhr.status); - callback(false, 'Non-HTTP error reloading data: ' + body, xhr); - } else { - var sts1 = kb.statementsMatching(undefined, undefined, undefined, doc).slice(); // Take a copy!! - var sts2 = g2.statementsMatching(undefined, undefined, undefined, doc).slice(); - console.log(' replacing ' + sts1.length + ' with ' + sts2.length + ' out of total statements ' + kb.statements.length); - kb.remove(sts1); - kb.add(sts2); - var elapsedTime_ms = Date.now() - startTime; - if (sts2.length === 0) { - console.log('????????????????? 0000000'); - } - if (!doc.reloadTime_total) doc.reloadTime_total = 0; - if (!doc.reloadTime_count) doc.reloadTime_count = 0; - doc.reloadTime_total += elapsedTime_ms; - doc.reloadTime_count += 1; - console.log(' fetch took ' + elapsedTime_ms + 'ms, av. of ' + doc.reloadTime_count + ' = ' + doc.reloadTime_total / doc.reloadTime_count + 'ms.'); - callback(true); - } - }); - }; - return sparql; -}(); - +'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; }; + +var _indexedFormula = _dereq_('./indexed-formula'); + +var _indexedFormula2 = _interopRequireDefault(_indexedFormula); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// Joe Presbrey +// 2007-07-15 +// 2010-08-08 TimBL folded in Kenny's WEBDAV +// 2010-12-07 TimBL addred local file write code +var docpart = _dereq_('./uri').docpart; +var Fetcher = _dereq_('./fetcher'); +var graph = _dereq_('./data-factory').graph; + +var namedNode = _dereq_('./data-factory').namedNode; +var Namespace = _dereq_('./namespace'); +var Serializer = _dereq_('./serializer'); +var uriJoin = _dereq_('./uri').join; +var Util = _dereq_('./util'); + +var UpdateManager = function () { + var sparql = function sparql(store) { + this.store = store; + if (store.updater) { + throw new Error("You can't have two UpdateManagers for the same store"); + } + if (!store.fetcher) { + // The store must also/already have a fetcher + new Fetcher(store); + } + store.updater = this; + this.ifps = {}; + this.fps = {}; + this.ns = {}; + this.ns.link = Namespace('http://www.w3.org/2007/ont/link#'); + this.ns.http = Namespace('http://www.w3.org/2007/ont/http#'); + this.ns.httph = Namespace('http://www.w3.org/2007/ont/httph#'); + this.ns.ldp = Namespace('http://www.w3.org/ns/ldp#'); + this.ns.rdf = Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#'); + this.ns.rdfs = Namespace('http://www.w3.org/2000/01/rdf-schema#'); + this.ns.rdf = Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#'); + this.ns.owl = Namespace('http://www.w3.org/2002/07/owl#'); + + this.patchControl = []; // index of objects fro coordinating incomng and outgoing patches + }; + + sparql.prototype.patchControlFor = function (doc) { + if (!this.patchControl[doc.uri]) { + this.patchControl[doc.uri] = []; + } + return this.patchControl[doc.uri]; + }; + + // Returns The method string SPARQL or DAV or LOCALFILE or false if known, undefined if not known. + // + // Files have to have a specific annotaton that they are machine written, for safety. + // We don't actually check for write access on files. + // + sparql.prototype.editable = function (uri, kb) { + if (!uri) { + return false; // Eg subject is bnode, no known doc to write to + } + if (!kb) { + kb = this.store; + } + + if (uri.slice(0, 8) === 'file:///') { + if (kb.holds(kb.sym(uri), namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), namedNode('http://www.w3.org/2007/ont/link#MachineEditableDocument'))) { + return 'LOCALFILE'; + } + + var sts = kb.statementsMatching(kb.sym(uri), undefined, undefined); + + console.log('sparql.editable: Not MachineEditableDocument file ' + uri + '\n'); + console.log(sts.map(function (x) { + return x.toNT(); + }).join('\n')); + return false; + // @@ Would be nifty of course to see whether we actually have write acess first. + } + + var request; + var definitive = false; + var requests = kb.each(undefined, this.ns.link('requestedURI'), docpart(uri)); + + // Hack for the moment @@@@ 2016-02-12 + if (kb.holds(namedNode(uri), this.ns.rdf('type'), this.ns.ldp('Resource'))) { + return 'SPARQL'; + } + var i; + var method; + for (var r = 0; r < requests.length; r++) { + request = requests[r]; + if (request !== undefined) { + var response = kb.any(request, this.ns.link('response')); + if (request !== undefined) { + var acceptPatch = kb.each(response, this.ns.httph('accept-patch')); + if (acceptPatch.length) { + for (i = 0; i < acceptPatch.length; i++) { + method = acceptPatch[i].value.trim(); + if (method.indexOf('application/sparql-update') >= 0) return 'SPARQL'; + } + } + var author_via = kb.each(response, this.ns.httph('ms-author-via')); + if (author_via.length) { + for (i = 0; i < author_via.length; i++) { + method = author_via[i].value.trim(); + if (method.indexOf('SPARQL') >= 0) { + return 'SPARQL'; + } + if (method.indexOf('DAV') >= 0) { + return 'DAV'; + } + } + } + var status = kb.each(response, this.ns.http('status')); + if (status.length) { + for (i = 0; i < status.length; i++) { + if (status[i] === 200 || status[i] === 404) { + definitive = true; + // return false // A definitive answer + } + } + } + } else { + console.log('sparql.editable: No response for ' + uri + '\n'); + } + } + } + if (requests.length === 0) { + console.log('sparql.editable: No request for ' + uri + '\n'); + } else { + if (definitive) { + return false; // We have got a request and it did NOT say editable => not editable + } + } + console.log('sparql.editable: inconclusive for ' + uri + '\n'); + return undefined; // We don't know (yet) as we haven't had a response (yet) + }; + + // ///////// The identification of bnodes + + sparql.prototype.anonymize = function (obj) { + return obj.toNT().substr(0, 2) === '_:' && this._mentioned(obj) ? '?' + obj.toNT().substr(2) : obj.toNT(); + }; + + sparql.prototype.anonymizeNT = function (stmt) { + return this.anonymize(stmt.subject) + ' ' + this.anonymize(stmt.predicate) + ' ' + this.anonymize(stmt.object) + ' .'; + }; + + // A list of all bnodes occuring in a statement + sparql.prototype._statement_bnodes = function (st) { + return [st.subject, st.predicate, st.object].filter(function (x) { + return x.isBlank; + }); + }; + + // A list of all bnodes occuring in a list of statements + sparql.prototype._statement_array_bnodes = function (sts) { + var bnodes = []; + for (var i = 0; i < sts.length; i++) { + bnodes = bnodes.concat(this._statement_bnodes(sts[i])); + } + bnodes.sort(); // in place sort - result may have duplicates + var bnodes2 = []; + for (var j = 0; j < bnodes.length; j++) { + if (j === 0 || !bnodes[j].sameTerm(bnodes[j - 1])) { + bnodes2.push(bnodes[j]); + } + } + return bnodes2; + }; + + sparql.prototype._cache_ifps = function () { + // Make a cached list of [Inverse-]Functional properties + // Call this once before calling context_statements + this.ifps = {}; + var a = this.store.each(undefined, this.ns.rdf('type'), this.ns.owl('InverseFunctionalProperty')); + for (var i = 0; i < a.length; i++) { + this.ifps[a[i].uri] = true; + } + this.fps = {}; + a = this.store.each(undefined, this.ns.rdf('type'), this.ns.owl('FunctionalProperty')); + for (i = 0; i < a.length; i++) { + this.fps[a[i].uri] = true; + } + }; + + // Returns a context to bind a given node, up to a given depth + sparql.prototype._bnode_context2 = function (x, source, depth) { + // Return a list of statements which indirectly identify a node + // Depth > 1 if try further indirection. + // Return array of statements (possibly empty), or null if failure + var sts = this.store.statementsMatching(undefined, undefined, x, source); // incoming links + var y; + var res; + for (var i = 0; i < sts.length; i++) { + if (this.fps[sts[i].predicate.uri]) { + y = sts[i].subject; + if (!y.isBlank) { + return [sts[i]]; + } + if (depth) { + res = this._bnode_context2(y, source, depth - 1); + if (res) { + return res.concat([sts[i]]); + } + } + } + } + // outgoing links + sts = this.store.statementsMatching(x, undefined, undefined, source); + for (i = 0; i < sts.length; i++) { + if (this.ifps[sts[i].predicate.uri]) { + y = sts[i].object; + if (!y.isBlank) { + return [sts[i]]; + } + if (depth) { + res = this._bnode_context2(y, source, depth - 1); + if (res) { + return res.concat([sts[i]]); + } + } + } + } + return null; // Failure + }; + + // Returns the smallest context to bind a given single bnode + sparql.prototype._bnode_context_1 = function (x, source) { + // Return a list of statements which indirectly identify a node + // Breadth-first + var self = this; + for (var depth = 0; depth < 3; depth++) { + // Try simple first + var con = this._bnode_context2(x, source, depth); + if (con !== null) return con; + } + // If we can't guarantee unique with logic just send all info about node + return this.store.connectedStatements(x, source); // was: + // throw new Error('Unable to uniquely identify bnode: ' + x.toNT()) + }; + + sparql.prototype._mentioned = function (x) { + return this.store.statementsMatching(x).length !== 0 || // Don't pin fresh bnodes + this.store.statementsMatching(undefined, x).length !== 0 || this.store.statementsMatching(undefined, undefined, x).length !== 0; + }; + + sparql.prototype._bnode_context = function (bnodes, doc) { + var context = []; + if (bnodes.length) { + this._cache_ifps(); + for (var i = 0; i < bnodes.length; i++) { + // Does this occur in old graph? + var bnode = bnodes[i]; + if (!this._mentioned(bnode)) continue; + context = context.concat(this._bnode_context_1(bnode, doc)); + } + } + return context; + }; + + /* Weird code does not make sense -- some code corruption along the line -- st undefined -- weird + sparql.prototype._bnode_context = function(bnodes) { + var context = [] + if (bnodes.length) { + if (this.store.statementsMatching(st.subject.isBlank?undefined:st.subject, + st.predicate.isBlank?undefined:st.predicate, + st.object.isBlank?undefined:st.object, + st.why).length <= 1) { + context = context.concat(st) + } else { + this._cache_ifps() + for (x in bnodes) { + context = context.concat(this._bnode_context_1(bnodes[x], st.why)) + } + } + } + return context + } + */ + // Returns the best context for a single statement + sparql.prototype._statement_context = function (st) { + var bnodes = this._statement_bnodes(st); + return this._bnode_context(bnodes, st.why); + }; + + sparql.prototype._context_where = function (context) { + var sparql = this; + return !context || context.length === 0 ? '' : 'WHERE { ' + context.map(function (x) { + return sparql.anonymizeNT(x); + }).join('\n') + ' }\n'; + }; + + sparql.prototype._fire = function (uri, query, callback) { + if (!uri) { + throw new Error('No URI given for remote editing operation: ' + query); + } + console.log('sparql: sending update to <' + uri + '>'); + var xhr = Util.XMLHTTPFactory(); + xhr.options = {}; + + xhr.onreadystatechange = function () { + // dump("SPARQL update ready state for <"+uri+"> readyState="+xhr.readyState+"\n"+query+"\n") + if (xhr.readyState === 4) { + var success = !xhr.status || xhr.status >= 200 && xhr.status < 300; + if (!success) { + console.log('sparql: update failed for <' + uri + '> status=' + xhr.status + ', ' + xhr.statusText + ', body length=' + xhr.responseText.length + '\n for query: ' + query); + } else { + console.log('sparql: update Ok for <' + uri + '>'); + } + callback(uri, success, xhr.responseText, xhr); + } + }; + + xhr.open('PATCH', uri, true); // async=true + xhr.setRequestHeader('Content-type', 'application/sparql-update'); + xhr.send(query); + }; + + // This does NOT update the statement. + // It returns an object whcih includes + // function which can be used to change the object of the statement. + // + sparql.prototype.update_statement = function (statement) { + if (statement && !statement.why) { + return; + } + var sparql = this; + var context = this._statement_context(statement); + + return { + statement: statement ? [statement.subject, statement.predicate, statement.object, statement.why] : undefined, + statementNT: statement ? this.anonymizeNT(statement) : undefined, + where: sparql._context_where(context), + + set_object: function set_object(obj, callback) { + var query = this.where; + query += 'DELETE DATA { ' + this.statementNT + ' } ;\n'; + query += 'INSERT DATA { ' + this.anonymize(this.statement[0]) + ' ' + this.anonymize(this.statement[1]) + ' ' + this.anonymize(obj) + ' ' + ' . }\n'; + + sparql._fire(this.statement[3].uri, query, callback); + } + }; + }; + + sparql.prototype.insert_statement = function (st, callback) { + var st0 = st instanceof Array ? st[0] : st; + var query = this._context_where(this._statement_context(st0)); + + if (st instanceof Array) { + var stText = ''; + for (var i = 0; i < st.length; i++) { + stText += st[i] + '\n'; + }query += 'INSERT DATA { ' + stText + ' }\n'; + } else { + query += 'INSERT DATA { ' + this.anonymize(st.subject) + ' ' + this.anonymize(st.predicate) + ' ' + this.anonymize(st.object) + ' ' + ' . }\n'; + } + + this._fire(st0.why.uri, query, callback); + }; + + sparql.prototype.delete_statement = function (st, callback) { + var st0 = st instanceof Array ? st[0] : st; + var query = this._context_where(this._statement_context(st0)); + + if (st instanceof Array) { + var stText = ''; + for (var i = 0; i < st.length; i++) { + stText += st[i] + '\n'; + }query += 'DELETE DATA { ' + stText + ' }\n'; + } else { + query += 'DELETE DATA { ' + this.anonymize(st.subject) + ' ' + this.anonymize(st.predicate) + ' ' + this.anonymize(st.object) + ' ' + ' . }\n'; + } + + this._fire(st0.why.uri, query, callback); + }; + + // Request a now or future action to refresh changes coming downstream + // + // This is designed to allow the system to re-request the server version, + // when a websocket has pinged to say there are changes. + // If thewebsocket, by contrast, has sent a patch, then this may not be necessary. + // This may be called out of context so *this* cannot be used. + + sparql.prototype.requestDownstreamAction = function (doc, action) { + var control = this.patchControlFor(doc); + if (!control.pendingUpstream) { + action(doc); + } else { + if (control.downstreamAction) { + if (control.downstreamAction === action) { + return; + } else { + throw new Error("Can't wait for > 1 differnt downstream actions"); + } + } else { + control.downstreamAction = action; + } + } + }; + + // We want to start counting websockt notifications + // to distinguish the ones from others from our own. + sparql.prototype.clearUpstreamCount = function (doc) { + var control = this.patchControlFor(doc); + control.upstreamCount = 0; + }; + + sparql.prototype.getUpdatesVia = function (doc) { + var linkHeaders = this.store.fetcher.getHeader(doc, 'updates-via'); + if (!linkHeaders || !linkHeaders.length) return null; + return linkHeaders[0].trim(); + }; + + sparql.prototype.addDownstreamChangeListener = function (doc, listener) { + var control = this.patchControlFor(doc); + if (!control.downstreamChangeListeners) control.downstreamChangeListeners = []; + control.downstreamChangeListeners.push(listener); + var self = this; + this.setRefreshHandler(doc, function (doc) { + // a function not a method + self.reloadAndSync(doc); + }); + }; + + sparql.prototype.reloadAndSync = function (doc) { + var control = this.patchControlFor(doc); + var updater = this; + + if (control.reloading) { + console.log(' Already reloading - stop'); + return; // once only needed + } + control.reloading = true; + var retryTimeout = 1000; // ms + var tryReload = function tryReload() { + console.log('try reload - timeout = ' + retryTimeout); + updater.reload(updater.store, doc, function (ok, message, xhr) { + control.reloading = false; + if (ok) { + if (control.downstreamChangeListeners) { + for (var i = 0; i < control.downstreamChangeListeners.length; i++) { + console.log(' Calling downstream listener ' + i); + control.downstreamChangeListeners[i](); + } + } + } else { + if (xhr.status === 0) { + console.log('Network error refreshing the data. Retrying in ' + retryTimeout / 1000); + control.reloading = true; + retryTimeout = retryTimeout * 2; + setTimeout(tryReload, retryTimeout); + } else { + console.log('Error ' + xhr.status + 'refreshing the data:' + message + '. Stopped' + doc); + } + } + }); + }; + tryReload(); + }; + + // Set up websocket to listen on + // + // There is coordination between upstream changes and downstream ones + // so that a reload is not done in the middle of an upsteeam patch. + // If you usie this API then you get called when a change happens, and you + // have to reload the file yourself, and then refresh the UI. + // Alternative is addDownstreamChangeListener(), where you do not + // have to do the reload yourslf. Do mot mix them. + // + // kb contains the HTTP metadata from prefvious operations + // + sparql.prototype.setRefreshHandler = function (doc, handler) { + var wssURI = this.getUpdatesVia(doc); // relative + // var kb = this.store + var theHandler = handler; + var self = this; + var updater = this; + var retryTimeout = 1500; // *2 will be 3 Seconds, 6, 12, etc + var retries = 0; + + if (!wssURI) { + console.log('Server doies not support live updates thoughUpdates-Via :-('); + return false; + } + + wssURI = uriJoin(wssURI, doc.uri); + wssURI = wssURI.replace(/^http:/, 'ws:').replace(/^https:/, 'wss:'); + console.log('Web socket URI ' + wssURI); + + var openWebsocket = function openWebsocket() { + // From https://github.com/solid/solid-spec#live-updates + var socket; + if (typeof WebSocket !== 'undefined') { + socket = new WebSocket(wssURI); + } else if (typeof Services !== 'undefined') { + // Firefox add on http://stackoverflow.com/questions/24244886/is-websocket-supported-in-firefox-for-android-addons + socket = Services.wm.getMostRecentWindow('navigator:browser').WebSocket(wssURI); + } else if (typeof window !== 'undefined' && window.WebSocket) { + socket = window.WebSocket(wssURI); + } else { + console.log('Live update disabled, as WebSocket not supported by platform :-('); + return; + } + socket.onopen = function () { + console.log(' websocket open'); + retryTimeout = 1500; // reset timeout to fast on success + this.send('sub ' + doc.uri); + if (retries) { + console.log('Web socket has been down, better check for any news.'); + updater.requestDownstreamAction(doc, theHandler); + } + }; + var control = self.patchControlFor(doc); + control.upstreamCount = 0; + + // https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent + // + // 1000 CLOSE_NORMAL Normal closure; the connection successfully completed whatever purpose for which it was created. + // 1001 CLOSE_GOING_AWAY The endpoint is going away, either + // because of a server failure or because the browser is navigating away from the page that opened the connection. + // 1002 CLOSE_PROTOCOL_ERROR The endpoint is terminating the connection due to a protocol error. + // 1003 CLOSE_UNSUPPORTED The connection is being terminated because the endpoint + // received data of a type it cannot accept (for example, a text-only endpoint received binary data). + // 1004 Reserved. A meaning might be defined in the future. + // 1005 CLOSE_NO_STATUS Reserved. Indicates that no status code was provided even though one was expected. + // 1006 CLOSE_ABNORMAL Reserved. Used to indicate that a connection was closed abnormally ( + // + // + socket.onclose = function (event) { + console.log('*** Websocket closed with code ' + event.code + ", reason '" + event.reason + "' clean = " + event.clean); + retryTimeout *= 2; + retries += 1; + console.log('Retrying in ' + retryTimeout + 'ms'); // (ask user?) + setTimeout(function () { + console.log('Trying websocket again'); + openWebsocket(); + }, retryTimeout); + }; + socket.onmessage = function (msg) { + if (msg.data && msg.data.slice(0, 3) === 'pub') { + if ('upstreamCount' in control) { + control.upstreamCount -= 1; + if (control.upstreamCount >= 0) { + console.log('just an echo: ' + control.upstreamCount); + return; // Just an echo + } + } + console.log('Assume a real downstream change: ' + control.upstreamCount + ' -> 0'); + control.upstreamCount = 0; + self.requestDownstreamAction(doc, theHandler); + } + }; + }; // openWebsocket + openWebsocket(); + + return true; + }; + + // This high-level function updates the local store iff the web is changed successfully. + // + // - deletions, insertions may be undefined or single statements or lists or formulae. + // (may contain bnodes which can be indirectly identified by a where clause) + // + // - callback is called as callback(uri, success, errorbody) + // + sparql.prototype.update = function (deletions, insertions, callback) { + try { + var kb = this.store; + var ds = !deletions ? [] : deletions instanceof _indexedFormula2.default ? deletions.statements : deletions instanceof Array ? deletions : [deletions]; + var is = !insertions ? [] : insertions instanceof _indexedFormula2.default ? insertions.statements : insertions instanceof Array ? insertions : [insertions]; + if (!(ds instanceof Array)) { + throw new Error('Type Error ' + (typeof ds === 'undefined' ? 'undefined' : _typeof(ds)) + ': ' + ds); + } + if (!(is instanceof Array)) { + throw new Error('Type Error ' + (typeof is === 'undefined' ? 'undefined' : _typeof(is)) + ': ' + is); + } + if (ds.length === 0 && is.length === 0) { + return callback(null, true); // success -- nothing needed to be done. + } + var doc = ds.length ? ds[0].why : is[0].why; + var control = this.patchControlFor(doc); + var startTime = Date.now(); + + var props = ['subject', 'predicate', 'object', 'why']; + var verbs = ['insert', 'delete']; + var clauses = { 'delete': ds, 'insert': is }; + verbs.map(function (verb) { + clauses[verb].map(function (st) { + if (!doc.sameTerm(st.why)) { + throw new Error('update: destination ' + doc + ' inconsistent with delete quad ' + st.why); + } + props.map(function (prop) { + if (typeof st[prop] === 'undefined') { + throw new Error('update: undefined ' + prop + ' of statement.'); + } + }); + }); + }); + + var protocol = this.editable(doc.uri, kb); + if (!protocol) { + throw new Error("Can't make changes in uneditable " + doc); + } + var i; + var newSts; + var documentString; + var sz; + if (protocol.indexOf('SPARQL') >= 0) { + var bnodes = []; + if (ds.length) bnodes = this._statement_array_bnodes(ds); + if (is.length) bnodes = bnodes.concat(this._statement_array_bnodes(is)); + var context = this._bnode_context(bnodes, doc); + var whereClause = this._context_where(context); + var query = ''; + if (whereClause.length) { + // Is there a WHERE clause? + if (ds.length) { + query += 'DELETE { '; + for (i = 0; i < ds.length; i++) { + query += this.anonymizeNT(ds[i]) + '\n'; + } + query += ' }\n'; + } + if (is.length) { + query += 'INSERT { '; + for (i = 0; i < is.length; i++) { + query += this.anonymizeNT(is[i]) + '\n'; + } + query += ' }\n'; + } + query += whereClause; + } else { + // no where clause + if (ds.length) { + query += 'DELETE DATA { '; + for (i = 0; i < ds.length; i++) { + query += this.anonymizeNT(ds[i]) + '\n'; + } + query += ' } \n'; + } + if (is.length) { + if (ds.length) query += ' ; '; + query += 'INSERT DATA { '; + for (i = 0; i < is.length; i++) { + query += this.anonymizeNT(is[i]) + '\n'; + } + query += ' }\n'; + } + } + // Track pending upstream patches until they have fnished their callback + control.pendingUpstream = control.pendingUpstream ? control.pendingUpstream + 1 : 1; + if ('upstreamCount' in control) { + control.upstreamCount += 1; // count changes we originated ourselves + console.log('upstream count up to : ' + control.upstreamCount); + } + + this._fire(doc.uri, query, function (uri, success, body, xhr) { + xhr.elapsedTime_ms = Date.now() - startTime; + console.log(' sparql: Return ' + (success ? 'success' : 'FAILURE ' + xhr.status) + ' elapsed ' + xhr.elapsedTime_ms + 'ms'); + if (success) { + try { + kb.remove(ds); + } catch (e) { + success = false; + body = 'Remote Ok BUT error deleting ' + ds.length + ' from store!!! ' + e; + } // Add in any case -- help recover from weirdness?? + for (var i = 0; i < is.length; i++) { + kb.add(is[i].subject, is[i].predicate, is[i].object, doc); + } + } + + callback(uri, success, body, xhr); + control.pendingUpstream -= 1; + // When upstream patches have been sent, reload state if downstream waiting + if (control.pendingUpstream === 0 && control.downstreamAction) { + var downstreamAction = control.downstreamAction; + delete control.downstreamAction; + console.log('delayed downstream action:'); + downstreamAction(doc); + } + }); + } else if (protocol.indexOf('DAV') >= 0) { + // The code below is derived from Kenny's UpdateCenter.js + documentString; + var request = kb.any(doc, this.ns.link('request')); + if (!request) { + throw new Error('No record of our HTTP GET request for document: ' + doc); + } // should not happen + var response = kb.any(request, this.ns.link('response')); + if (!response) { + return null; // throw "No record HTTP GET response for document: "+doc + } + var content_type = kb.the(response, this.ns.httph('content-type')).value; + + // prepare contents of revised document + newSts = kb.statementsMatching(undefined, undefined, undefined, doc).slice(); // copy! + for (i = 0; i < ds.length; i++) { + Util.RDFArrayRemove(newSts, ds[i]); + } + for (i = 0; i < is.length; i++) { + newSts.push(is[i]); + } + + // serialize to te appropriate format + sz = Serializer(kb); + sz.suggestNamespaces(kb.namespaces); + sz.setBase(doc.uri); // ?? beware of this - kenny (why? tim) + switch (content_type) { + case 'application/rdf+xml': + documentString = sz.statementsToXML(newSts); + break; + case 'text/n3': + case 'text/turtle': + case 'application/x-turtle': // Legacy + case 'application/n3': + // Legacy + documentString = sz.statementsToN3(newSts); + break; + default: + throw new Error('Content-type ' + content_type + ' not supported for data write'); + } + + // Write the new version back + + var candidateTarget = kb.the(response, this.ns.httph('content-location')); + var targetURI; + if (candidateTarget) { + targetURI = uriJoin(candidateTarget.value, targetURI); + } + var xhr = Util.XMLHTTPFactory(); + xhr.options = {}; + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + // formula from sparqlUpdate.js, what about redirects? + var success = !xhr.status || xhr.status >= 200 && xhr.status < 300; + if (success) { + for (var i = 0; i < ds.length; i++) { + kb.remove(ds[i]); + } + for (i = 0; i < is.length; i++) { + kb.add(is[i].subject, is[i].predicate, is[i].object, doc); + } + } + callback(doc.uri, success, xhr.responseText); + } + }; + xhr.open('PUT', targetURI, true); + // assume the server does PUT content-negotiation. + xhr.setRequestHeader('Content-type', content_type); // OK? + xhr.send(documentString); + } else { + if (protocol.indexOf('LOCALFILE') >= 0) { + try { + console.log('Writing back to local file\n'); + // See http://simon-jung.blogspot.com/2007/10/firefox-extension-file-io.html + // prepare contents of revised document + newSts = kb.statementsMatching(undefined, undefined, undefined, doc).slice(); // copy! + for (i = 0; i < ds.length; i++) { + Util.RDFArrayRemove(newSts, ds[i]); + } + for (i = 0; i < is.length; i++) { + newSts.push(is[i]); + } + // serialize to the appropriate format + documentString; + sz = Serializer(kb); + sz.suggestNamespaces(kb.namespaces); + sz.setBase(doc.uri); // ?? beware of this - kenny (why? tim) + var dot = doc.uri.lastIndexOf('.'); + if (dot < 1) { + throw new Error('Rewriting file: No filename extension: ' + doc.uri); + } + var ext = doc.uri.slice(dot + 1); + switch (ext) { + case 'rdf': + case 'owl': // Just my experence ...@@ we should keep the format in which it was parsed + case 'xml': + documentString = sz.statementsToXML(newSts); + break; + case 'n3': + case 'nt': + case 'ttl': + documentString = sz.statementsToN3(newSts); + break; + default: + throw new Error('File extension .' + ext + ' not supported for data write'); + } + // Write the new version back + // create component for file writing + console.log('Writing back: <<<' + documentString + '>>>'); + var filename = doc.uri.slice(7); // chop off file:// leaving /path + // console.log("Writeback: Filename: "+filename+"\n") + var file = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile); + file.initWithPath(filename); + if (!file.exists()) { + throw new Error('Rewriting file <' + doc.uri + '> but it does not exist!'); + } + // { + // file.create( Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 420) + // } + // create file output stream and use write/create/truncate mode + // 0x02 writing, 0x08 create file, 0x20 truncate length if exist + var stream = Components.classes['@mozilla.org/network/file-output-stream;1'].createInstance(Components.interfaces.nsIFileOutputStream); + + // Various JS systems object to 0666 in struct mode as dangerous + stream.init(file, 0x02 | 0x08 | 0x20, parseInt('0666', 8), 0); + + // write data to file then close output stream + stream.write(documentString, documentString.length); + stream.close(); + + for (i = 0; i < ds.length; i++) { + kb.remove(ds[i]); + } + for (i = 0; i < is.length; i++) { + kb.add(is[i].subject, is[i].predicate, is[i].object, doc); + } + callback(doc.uri, true, ''); // success! + } catch (e) { + callback(doc.uri, false, 'Exception trying to write back file <' + doc.uri + '>\n' + // + tabulator.Util.stackString(e)) + ); + } + } else { + throw new Error("Unhandled edit method: '" + protocol + "' for " + doc); + } + } + } catch (e) { + callback(undefined, false, 'Exception in update: ' + e + '\n' + $rdf.Util.stackString(e)); + } + }; // wnd update + + // This suitable for an inital creation of a document + // + // data: string, or array of statements + // + sparql.prototype.put = function (doc, data, content_type, callback) { + var documentString; + var kb = this.store; + + if ((typeof data === 'undefined' ? 'undefined' : _typeof(data)) === _typeof('')) { + documentString = data; + } else { + // serialize to te appropriate format + var sz = Serializer(kb); + sz.suggestNamespaces(kb.namespaces); + sz.setBase(doc.uri); + switch (content_type) { + case 'application/rdf+xml': + documentString = sz.statementsToXML(data); + break; + case 'text/n3': + case 'text/turtle': + case 'application/x-turtle': // Legacy + case 'application/n3': + // Legacy + documentString = sz.statementsToN3(data); + break; + default: + throw new Error('Content-type ' + content_type + ' not supported for data PUT'); + } + } + var xhr = Util.XMLHTTPFactory(); + xhr.options = {}; + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + // formula from sparqlUpdate.js, what about redirects? + var success = !xhr.status || xhr.status >= 200 && xhr.status < 300; + if (success && typeof data !== 'string') { + data.map(function (st) { + kb.addStatement(st); + }); + // kb.fetcher.requested[doc.uri] = true // as though fetched + } + if (success) { + delete kb.fetcher.nonexistant[doc.uri]; + delete kb.fetcher.requested[doc.uri]; + // @@ later we can fake it has been requestd if put gives us the header sand we save them. + } + callback(doc.uri, success, xhr.responseText, xhr); + } + }; + xhr.open('PUT', doc.uri, true); + xhr.setRequestHeader('Content-type', content_type); + xhr.send(documentString); + }; + + // Reload a document. + // + // Fast and cheap, no metaata + // Measure times for the document + // Load it provisionally + // Don't delete the statemenst before the load, or it will leave a broken document + // in the meantime. + + sparql.prototype.reload = function (kb, doc, callback) { + var startTime = Date.now(); + // force sets no-cache and + kb.fetcher.nowOrWhenFetched(doc.uri, { force: true, noMeta: true, clearPreviousData: true }, function (ok, body, xhr) { + if (!ok) { + console.log(' ERROR reloading data: ' + body); + callback(false, 'Error reloading data: ' + body, xhr); + } else if (xhr.onErrorWasCalled || xhr.status !== 200) { + console.log(' Non-HTTP error reloading data! onErrorWasCalled=' + xhr.onErrorWasCalled + ' status: ' + xhr.status); + callback(false, 'Non-HTTP error reloading data: ' + body, xhr); + } else { + var elapsedTime_ms = Date.now() - startTime; + if (!doc.reloadTime_total) doc.reloadTime_total = 0; + if (!doc.reloadTime_count) doc.reloadTime_count = 0; + doc.reloadTime_total += elapsedTime_ms; + doc.reloadTime_count += 1; + console.log(' Fetch took ' + elapsedTime_ms + 'ms, av. of ' + doc.reloadTime_count + ' = ' + doc.reloadTime_total / doc.reloadTime_count + 'ms.'); + callback(true); + } + }); + }; + + sparql.prototype.oldReload = function (kb, doc, callback) { + var g2 = graph(); // A separate store to hold the data as we load it + var f2 = fetcher(g2); + var startTime = Date.now(); + // force sets no-cache and + f2.nowOrWhenFetched(doc.uri, { force: true, noMeta: true, clearPreviousData: true }, function (ok, body, xhr) { + if (!ok) { + console.log(' ERROR reloading data: ' + body); + callback(false, 'Error reloading data: ' + body, xhr); + } else if (xhr.onErrorWasCalled || xhr.status !== 200) { + console.log(' Non-HTTP error reloading data! onErrorWasCalled=' + xhr.onErrorWasCalled + ' status: ' + xhr.status); + callback(false, 'Non-HTTP error reloading data: ' + body, xhr); + } else { + var sts1 = kb.statementsMatching(undefined, undefined, undefined, doc).slice(); // Take a copy!! + var sts2 = g2.statementsMatching(undefined, undefined, undefined, doc).slice(); + console.log(' replacing ' + sts1.length + ' with ' + sts2.length + ' out of total statements ' + kb.statements.length); + kb.remove(sts1); + kb.add(sts2); + var elapsedTime_ms = Date.now() - startTime; + if (sts2.length === 0) { + console.log('????????????????? 0000000'); + } + if (!doc.reloadTime_total) doc.reloadTime_total = 0; + if (!doc.reloadTime_count) doc.reloadTime_count = 0; + doc.reloadTime_total += elapsedTime_ms; + doc.reloadTime_count += 1; + console.log(' fetch took ' + elapsedTime_ms + 'ms, av. of ' + doc.reloadTime_count + ' = ' + doc.reloadTime_total / doc.reloadTime_count + 'ms.'); + callback(true); + } + }); + }; + return sparql; +}(); + module.exports = UpdateManager; },{"./data-factory":55,"./fetcher":58,"./indexed-formula":61,"./namespace":67,"./serializer":76,"./uri":81,"./util":82}],80:[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"); } } - -/* - * Updates-Via - */ -var namedNode = _dereq_('./data-factory').namedNode; - -var UpdatesSocket = function () { - function UpdatesSocket(parent, via) { - _classCallCheck(this, UpdatesSocket); - - this.parent = parent; - this.via = via; - this.connected = false; - this.pending = {}; - this.subscribed = {}; - this.socket = {}; - try { - this.socket = new WebSocket(via); - this.socket.onopen = this.onOpen; - this.socket.onclose = this.onClose; - this.socket.onmessage = this.onMessage; - this.socket.onerror = this.onError; - } catch (error) { - this.onError(error); - } - } - - _createClass(UpdatesSocket, [{ - key: '_decode', - value: function _decode(q) { - var elt; - var i; - var k; - var r; - var ref; - var ref1; - var v; - r = {}; - ref = function () { - var j, len, ref, results; - ref = q.split('&'); - results = []; - for (j = 0, len = ref.length; j < len; j++) { - elt = ref[j]; - results.push(elt.split('=')); - } - return results; - }(); - for (i in ref) { - elt = ref[i]; - ref1 = [decodeURIComponent(elt[0]), decodeURIComponent(elt[1])]; - k = ref1[0]; - v = ref1[1]; - if (r[k] == null) { - r[k] = []; - } - r[k].push(v); - } - return r; - } - }, { - key: '_send', - value: function _send(method, uri, data) { - var base, message; - message = [method, uri, data].join(' '); - return typeof (base = this.socket).send === 'function' ? base.send(message) : void 0; - } - }, { - key: '_subscribe', - value: function _subscribe(uri) { - this._send('sub', uri, ''); - this.subscribed[uri] = true; - return this.subscribed[uri]; - } - }, { - key: 'onClose', - value: function onClose(e) { - var uri; - this.connected = false; - for (uri in this.subscribed) { - this.pending[uri] = true; - } - this.subscribed = {}; - return this.subscribed; - } - }, { - key: 'onError', - value: function onError(e) { - throw new Error('onError' + e); - } - }, { - key: 'onMessage', - value: function onMessage(e) { - var base, message; - message = e.data.split(' '); - if (message[0] === 'ping') { - return typeof (base = this.socket).send === 'function' ? base.send('pong ' + message.slice(1).join(' ')) : void 0; - } else if (message[0] === 'pub') { - return this.parent.onUpdate(message[1], this._decode(message[2])); - } - } - }, { - key: 'onOpen', - value: function onOpen(e) { - var results, uri; - this.connected = true; - results = []; - for (uri in this.pending) { - delete this.pending[uri]; - results.push(this._subscribe(uri)); - } - return results; - } - }, { - key: 'subscribe', - value: function subscribe(uri) { - if (this.connected) { - return this._subscribe(uri); - } else { - this.pending[uri] = true; - return this.pending[uri]; - } - } - }]); - - return UpdatesSocket; -}(); - -var UpdatesVia = function () { - function UpdatesVia(fetcher) { - _classCallCheck(this, UpdatesVia); - - this.fetcher = fetcher; - this.graph = {}; - this.via = {}; - this.fetcher.addCallback('headers', this.onHeaders); - } - - _createClass(UpdatesVia, [{ - key: 'onHeaders', - value: function onHeaders(d) { - var etag, uri, via; - if (d.headers == null) { - return true; - } - if (typeof WebSocket === 'undefined' || WebSocket === null) { - return true; - } - etag = d.headers['etag']; - via = d.headers['updates-via']; - uri = d.uri; - if (etag && via) { - this.graph[uri] = { - etag: etag, - via: via - }; - this.register(via, uri); - } - return true; - } - }, { - key: 'onUpdate', - value: function onUpdate(uri, d) { - return this.fetcher.refresh(namedNode(uri)); - } - }, { - key: 'register', - value: function register(via, uri) { - if (this.via[via] == null) { - this.via[via] = new UpdatesSocket(this, via); - } - return this.via[via].subscribe(uri); - } - }]); - - return UpdatesVia; -}(); - -module.exports.UpdatesSocket = UpdatesSocket; +'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"); } } + +/* + * Updates-Via + */ +var namedNode = _dereq_('./data-factory').namedNode; + +var UpdatesSocket = function () { + function UpdatesSocket(parent, via) { + _classCallCheck(this, UpdatesSocket); + + this.parent = parent; + this.via = via; + this.connected = false; + this.pending = {}; + this.subscribed = {}; + this.socket = {}; + try { + this.socket = new WebSocket(via); + this.socket.onopen = this.onOpen; + this.socket.onclose = this.onClose; + this.socket.onmessage = this.onMessage; + this.socket.onerror = this.onError; + } catch (error) { + this.onError(error); + } + } + + _createClass(UpdatesSocket, [{ + key: '_decode', + value: function _decode(q) { + var elt; + var i; + var k; + var r; + var ref; + var ref1; + var v; + r = {}; + ref = function () { + var j, len, ref, results; + ref = q.split('&'); + results = []; + for (j = 0, len = ref.length; j < len; j++) { + elt = ref[j]; + results.push(elt.split('=')); + } + return results; + }(); + for (i in ref) { + elt = ref[i]; + ref1 = [decodeURIComponent(elt[0]), decodeURIComponent(elt[1])]; + k = ref1[0]; + v = ref1[1]; + if (r[k] == null) { + r[k] = []; + } + r[k].push(v); + } + return r; + } + }, { + key: '_send', + value: function _send(method, uri, data) { + var base, message; + message = [method, uri, data].join(' '); + return typeof (base = this.socket).send === 'function' ? base.send(message) : void 0; + } + }, { + key: '_subscribe', + value: function _subscribe(uri) { + this._send('sub', uri, ''); + this.subscribed[uri] = true; + return this.subscribed[uri]; + } + }, { + key: 'onClose', + value: function onClose(e) { + var uri; + this.connected = false; + for (uri in this.subscribed) { + this.pending[uri] = true; + } + this.subscribed = {}; + return this.subscribed; + } + }, { + key: 'onError', + value: function onError(e) { + throw new Error('onError' + e); + } + }, { + key: 'onMessage', + value: function onMessage(e) { + var base, message; + message = e.data.split(' '); + if (message[0] === 'ping') { + return typeof (base = this.socket).send === 'function' ? base.send('pong ' + message.slice(1).join(' ')) : void 0; + } else if (message[0] === 'pub') { + return this.parent.onUpdate(message[1], this._decode(message[2])); + } + } + }, { + key: 'onOpen', + value: function onOpen(e) { + var results, uri; + this.connected = true; + results = []; + for (uri in this.pending) { + delete this.pending[uri]; + results.push(this._subscribe(uri)); + } + return results; + } + }, { + key: 'subscribe', + value: function subscribe(uri) { + if (this.connected) { + return this._subscribe(uri); + } else { + this.pending[uri] = true; + return this.pending[uri]; + } + } + }]); + + return UpdatesSocket; +}(); + +var UpdatesVia = function () { + function UpdatesVia(fetcher) { + _classCallCheck(this, UpdatesVia); + + this.fetcher = fetcher; + this.graph = {}; + this.via = {}; + this.fetcher.addCallback('headers', this.onHeaders); + } + + _createClass(UpdatesVia, [{ + key: 'onHeaders', + value: function onHeaders(d) { + var etag, uri, via; + if (d.headers == null) { + return true; + } + if (typeof WebSocket === 'undefined' || WebSocket === null) { + return true; + } + etag = d.headers['etag']; + via = d.headers['updates-via']; + uri = d.uri; + if (etag && via) { + this.graph[uri] = { + etag: etag, + via: via + }; + this.register(via, uri); + } + return true; + } + }, { + key: 'onUpdate', + value: function onUpdate(uri, d) { + return this.fetcher.refresh(namedNode(uri)); + } + }, { + key: 'register', + value: function register(via, uri) { + if (this.via[via] == null) { + this.via[via] = new UpdatesSocket(this, via); + } + return this.via[via].subscribe(uri); + } + }]); + + return UpdatesVia; +}(); + +module.exports.UpdatesSocket = UpdatesSocket; module.exports.UpdatesVia = UpdatesVia; },{"./data-factory":55}],81:[function(_dereq_,module,exports){ -'use strict'; - -/* - * Implements URI-specific functions - * - * See RFC 2386 - * - * See also: - * http://www.w3.org/2005/10/ajaw/uri.js - * http://www.w3.org/2000/10/swap/uripath.py - * - */ -var alert = alert || console.log; - -module.exports.docpart = docpart; -module.exports.document = document; -module.exports.hostpart = hostpart; -module.exports.join = join; -module.exports.protocol = protocol; -module.exports.refTo = refTo; - -var NamedNode = _dereq_('./named-node'); - -function docpart(uri) { - var i; - i = uri.indexOf('#'); - if (i < 0) { - return uri; - } else { - return uri.slice(0, i); - } -} - -function document(x) { - return new NamedNode(docpart(x.uri)); -} - -function hostpart(u) { - var m = /[^\/]*\/\/([^\/]*)\//.exec(u); - if (m) { - return m[1]; - } else { - return ''; - } -} - -function join(given, base) { - var baseColon, baseScheme, baseSingle; - var colon, lastSlash, path; - var baseHash = base.indexOf('#'); - if (baseHash > 0) { - base = base.slice(0, baseHash); - } - if (given.length === 0) { - return base; - } - if (given.indexOf('#') === 0) { - return base + given; - } - colon = given.indexOf(':'); - if (colon >= 0) { - return given; - } - baseColon = base.indexOf(':'); - if (base.length === 0) { - return given; - } - if (baseColon < 0) { - alert('Invalid base: ' + base + ' in join with given: ' + given); - return given; - } - baseScheme = base.slice(0, +baseColon + 1 || 9e9); - if (given.indexOf('//') === 0) { - return baseScheme + given; - } - if (base.indexOf('//', baseColon) === baseColon + 1) { - baseSingle = base.indexOf('/', baseColon + 3); - if (baseSingle < 0) { - if (base.length - baseColon - 3 > 0) { - return base + '/' + given; - } else { - return baseScheme + given; - } - } - } else { - baseSingle = base.indexOf('/', baseColon + 1); - if (baseSingle < 0) { - if (base.length - baseColon - 1 > 0) { - return base + '/' + given; - } else { - return baseScheme + given; - } - } - } - if (given.indexOf('/') === 0) { - return base.slice(0, baseSingle) + given; - } - path = base.slice(baseSingle); - lastSlash = path.lastIndexOf('/'); - if (lastSlash < 0) { - return baseScheme + given; - } - if (lastSlash >= 0 && lastSlash < path.length - 1) { - path = path.slice(0, +lastSlash + 1 || 9e9); - } - path += given; - while (path.match(/[^\/]*\/\.\.\//)) { - path = path.replace(/[^\/]*\/\.\.\//, ''); - } - path = path.replace(/\.\//g, ''); - path = path.replace(/\/\.$/, '/'); - return base.slice(0, baseSingle) + path; -} - -function protocol(uri) { - var i; - i = uri.indexOf(':'); - if (i < 0) { - return null; - } else { - return uri.slice(0, i); - } -} - -function refTo(base, uri) { - var c, i, k, l, len, len1, n, o, p, q, ref, ref1, s; - var commonHost = new RegExp('^[-_a-zA-Z0-9.]+:(//[^/]*)?/[^/]*$'); - if (!base) { - return uri; - } - if (base === uri) { - return ''; - } - for (i = o = 0, len = uri.length; o < len; i = ++o) { - c = uri[i]; - if (c !== base[i]) { - break; - } - } - if (base.slice(0, i).match(commonHost)) { - k = uri.indexOf('//'); - if (k < 0) { - k = -2; - } - l = uri.indexOf('/', k + 2); - if (uri[l + 1] !== '/' && base[l + 1] !== '/' && uri.slice(0, l) === base.slice(0, l)) { - return uri.slice(l); - } - } - if (uri[i] === '#' && base.length === i) { - return uri.slice(i); - } - while (i > 0 && uri[i - 1] !== '/') { - i--; - } - if (i < 3) { - return uri; - } - if (base.indexOf('//', i - 2) > 0 || uri.indexOf('//', i - 2) > 0) { - return uri; - } - if (base.indexOf(':', i) > 0) { - return uri; - } - n = 0; - ref = base.slice(i); - for (p = 0, len1 = ref.length; p < len1; p++) { - c = ref[p]; - if (c === '/') { - n++; - } - } - if (n === 0 && i < uri.length && uri[i] === '#') { - return './' + uri.slice(i); - } - if (n === 0 && i === uri.length) { - return './'; - } - s = ''; - if (n > 0) { - for (q = 1, ref1 = n; ref1 >= 1 ? q <= ref1 : q >= ref1; ref1 >= 1 ? ++q : --q) { - s += '../'; - } - } - return s + uri.slice(i); +'use strict'; + +/* + * Implements URI-specific functions + * + * See RFC 2386 + * + * See also: + * http://www.w3.org/2005/10/ajaw/uri.js + * http://www.w3.org/2000/10/swap/uripath.py + * + */ +var alert = alert || console.log; + +module.exports.docpart = docpart; +module.exports.document = document; +module.exports.hostpart = hostpart; +module.exports.join = join; +module.exports.protocol = protocol; +module.exports.refTo = refTo; + +var NamedNode = _dereq_('./named-node'); + +function docpart(uri) { + var i; + i = uri.indexOf('#'); + if (i < 0) { + return uri; + } else { + return uri.slice(0, i); + } +} + +function document(x) { + return new NamedNode(docpart(x.uri)); +} + +function hostpart(u) { + var m = /[^\/]*\/\/([^\/]*)\//.exec(u); + if (m) { + return m[1]; + } else { + return ''; + } +} + +function join(given, base) { + var baseColon, baseScheme, baseSingle; + var colon, lastSlash, path; + var baseHash = base.indexOf('#'); + if (baseHash > 0) { + base = base.slice(0, baseHash); + } + if (given.length === 0) { + return base; + } + if (given.indexOf('#') === 0) { + return base + given; + } + colon = given.indexOf(':'); + if (colon >= 0) { + return given; + } + baseColon = base.indexOf(':'); + if (base.length === 0) { + return given; + } + if (baseColon < 0) { + alert('Invalid base: ' + base + ' in join with given: ' + given); + return given; + } + baseScheme = base.slice(0, +baseColon + 1 || 9e9); + if (given.indexOf('//') === 0) { + return baseScheme + given; + } + if (base.indexOf('//', baseColon) === baseColon + 1) { + baseSingle = base.indexOf('/', baseColon + 3); + if (baseSingle < 0) { + if (base.length - baseColon - 3 > 0) { + return base + '/' + given; + } else { + return baseScheme + given; + } + } + } else { + baseSingle = base.indexOf('/', baseColon + 1); + if (baseSingle < 0) { + if (base.length - baseColon - 1 > 0) { + return base + '/' + given; + } else { + return baseScheme + given; + } + } + } + if (given.indexOf('/') === 0) { + return base.slice(0, baseSingle) + given; + } + path = base.slice(baseSingle); + lastSlash = path.lastIndexOf('/'); + if (lastSlash < 0) { + return baseScheme + given; + } + if (lastSlash >= 0 && lastSlash < path.length - 1) { + path = path.slice(0, +lastSlash + 1 || 9e9); + } + path += given; + while (path.match(/[^\/]*\/\.\.\//)) { + path = path.replace(/[^\/]*\/\.\.\//, ''); + } + path = path.replace(/\.\//g, ''); + path = path.replace(/\/\.$/, '/'); + return base.slice(0, baseSingle) + path; +} + +function protocol(uri) { + var i; + i = uri.indexOf(':'); + if (i < 0) { + return null; + } else { + return uri.slice(0, i); + } +} + +function refTo(base, uri) { + var c, i, k, l, len, len1, n, o, p, q, ref, ref1, s; + var commonHost = new RegExp('^[-_a-zA-Z0-9.]+:(//[^/]*)?/[^/]*$'); + if (!base) { + return uri; + } + if (base === uri) { + return ''; + } + for (i = o = 0, len = uri.length; o < len; i = ++o) { + c = uri[i]; + if (c !== base[i]) { + break; + } + } + if (base.slice(0, i).match(commonHost)) { + k = uri.indexOf('//'); + if (k < 0) { + k = -2; + } + l = uri.indexOf('/', k + 2); + if (uri[l + 1] !== '/' && base[l + 1] !== '/' && uri.slice(0, l) === base.slice(0, l)) { + return uri.slice(l); + } + } + if (uri[i] === '#' && base.length === i) { + return uri.slice(i); + } + while (i > 0 && uri[i - 1] !== '/') { + i--; + } + if (i < 3) { + return uri; + } + if (base.indexOf('//', i - 2) > 0 || uri.indexOf('//', i - 2) > 0) { + return uri; + } + if (base.indexOf(':', i) > 0) { + return uri; + } + n = 0; + ref = base.slice(i); + for (p = 0, len1 = ref.length; p < len1; p++) { + c = ref[p]; + if (c === '/') { + n++; + } + } + if (n === 0 && i < uri.length && uri[i] === '#') { + return './' + uri.slice(i); + } + if (n === 0 && i === uri.length) { + return './'; + } + s = ''; + if (n > 0) { + for (q = 1, ref1 = n; ref1 >= 1 ? q <= ref1 : q >= ref1; ref1 >= 1 ? ++q : --q) { + s += '../'; + } + } + return s + uri.slice(i); } },{"./named-node":66}],82:[function(_dereq_,module,exports){ -'use strict'; - -/** - * Utility functions for $rdf - * @module util - */ -var docpart = _dereq_('./uri').docpart; -var log = _dereq_('./log'); -var NamedNode = _dereq_('./named-node'); - -module.exports.AJAR_handleNewTerm = ajarHandleNewTerm; -module.exports.ArrayIndexOf = arrayIndexOf; -module.exports.callbackify = callbackify; -module.exports.dtstamp = dtstamp; -module.exports.DOMParserFactory = domParser; -module.exports.domToString = domToString; -module.exports.dumpNode = dumpNode; -module.exports.getHTTPHeaders = getHTTPHeaders; -module.exports.heavyCompare = heavyCompare; -module.exports.heavyCompareSPO = heavyCompareSPO; -module.exports.output = output; -module.exports.parseXML = parseXML; -module.exports.RDFArrayRemove = rdfArrayRemove; -module.exports.stackString = stackString; -module.exports.string_startswith = stringStartsWith; -module.exports.string = {}; -module.exports.string.template = stringTemplate; -module.exports.uri = _dereq_('./uri'); // TODO: Remove this mixed usage -// module.exports.variablesIn = variablesIn -module.exports.XMLHTTPFactory = xhr; -module.exports.log = log; - -module.exports.mediaTypeClass = function (mediaType) { - mediaType = mediaType.split(';')[0].trim(); // remove media type parameters - return new NamedNode('http://www.w3.org/ns/iana/media-types/' + mediaType + '#Resource'); -}; - -/** - * Loads ontologies of the data we load (this is the callback from the kb to - * the fetcher). Exports as `AJAR_handleNewTerm` - */ -function ajarHandleNewTerm(kb, p, requestedBy) { - var sf = null; - if (typeof kb.fetcher !== 'undefined') { - sf = kb.fetcher; - } else { - return; - } - if (p.termType !== 'NamedNode') return; - var docuri = docpart(p.uri); - var fixuri; - if (p.uri.indexOf('#') < 0) { - // No hash - // @@ major hack for dbpedia Categories, which spread indefinitely - if (stringStartsWith(p.uri, 'http://dbpedia.org/resource/Category:')) return; - - /* - if (string_startswith(p.uri, 'http://xmlns.com/foaf/0.1/')) { - fixuri = "http://dig.csail.mit.edu/2005/ajar/ajaw/test/foaf" - // should give HTTP 303 to ontology -- now is :-) - } else - */ - if (stringStartsWith(p.uri, 'http://purl.org/dc/elements/1.1/') || stringStartsWith(p.uri, 'http://purl.org/dc/terms/')) { - fixuri = 'http://dublincore.org/2005/06/13/dcq'; - // dc fetched multiple times - } else if (stringStartsWith(p.uri, 'http://xmlns.com/wot/0.1/')) { - fixuri = 'http://xmlns.com/wot/0.1/index.rdf'; - } else if (stringStartsWith(p.uri, 'http://web.resource.org/cc/')) { - // log.warn("creative commons links to html instead of rdf. doesn't seem to content-negotiate.") - fixuri = 'http://web.resource.org/cc/schema.rdf'; - } - } - if (fixuri) { - docuri = fixuri; - } - if (sf && sf.getState(docuri) !== 'unrequested') return; - - if (fixuri) { - // only give warning once: else happens too often - log.warn('Assuming server still broken, faking redirect of <' + p.uri + '> to <' + docuri + '>'); - } - sf.requestURI(docuri, requestedBy); -} - -/** - * Exports as `ArrayIndexOf`. - */ -function arrayIndexOf(arr, item, i) { - i || (i = 0); - var length = arr.length; - if (i < 0) i = length + i; - for (; i < length; i++) { - if (arr[i] === item) { - return i; - } - } - return -1; -} - -/** - * Adds callback functionality to an object. - * Callback functions are indexed by a 'hook' string. - * They return true if they want to be called again. - * @method callbackify - * @param obj {Object} - * @param callbacks {Array} - */ -function callbackify(obj, callbacks) { - obj.callbacks = {}; - for (var x = callbacks.length - 1; x >= 0; x--) { - obj.callbacks[callbacks[x]] = []; - } - - obj.addHook = function (hook) { - if (!obj.callbacks[hook]) { - obj.callbacks[hook] = []; - } - }; - - obj.addCallback = function (hook, func) { - obj.callbacks[hook].push(func); - }; - - obj.removeCallback = function (hook, funcName) { - for (var i = 0; i < obj.callbacks[hook].length; i++) { - if (obj.callbacks[hook][i].name === funcName) { - obj.callbacks[hook].splice(i, 1); - return true; - } - } - return false; - }; - - obj.insertCallback = function (hook, func) { - obj.callbacks[hook].unshift(func); - }; - - obj.fireCallbacks = function (hook, args) { - var newCallbacks = []; - var replaceCallbacks = []; - var len = obj.callbacks[hook].length; - var x; - // log.info('!@$ Firing '+hook+' call back with length'+len) - for (x = len - 1; x >= 0; x--) { - // log.info('@@ Firing '+hook+' callback '+ obj.callbacks[hook][x]) - if (obj.callbacks[hook][x].apply(obj, args)) { - newCallbacks.push(obj.callbacks[hook][x]); - } - } - - for (x = newCallbacks.length - 1; x >= 0; x--) { - replaceCallbacks.push(newCallbacks[x]); - } - - for (x = len; x < obj.callbacks[hook].length; x++) { - replaceCallbacks.push(obj.callbacks[hook][x]); - } - - obj.callbacks[hook] = replaceCallbacks; - }; -} - -/** - * Returns a DOM parser based on current runtime environment. - * Exports as `DOMParserFactory` - */ -function domParser() { - if (tabulator && tabulator.isExtension) { - return Components.classes['@mozilla.org/xmlextras/domparser;1'].getService(Components.interfaces.nsIDOMParser); - } else if (window.DOMParser) { - return new DOMParser(); - } else if (window.ActiveXObject) { - return new ActiveXObject('Microsoft.XMLDOM'); - } else { - return false; - } -} - -// From https://github.com/linkeddata/dokieli -function domToString(node, options) { - options = options || {}; - var selfClosing = []; - if ('selfClosing' in options) { - options.selfClosing.split(' ').forEach(function (n) { - selfClosing[n] = true; - }); - } - var skipAttributes = []; - if ('skipAttributes' in options) { - options.skipAttributes.split(' ').forEach(function (n) { - skipAttributes[n] = true; - }); - } - return dumpNode(node, options, selfClosing, skipAttributes); -} - -function dumpNode(node, options, selfClosing, skipAttributes) { - var i; - var out = ''; - var noEsc = [false]; - if (typeof node.nodeType === 'undefined') return out; - if (node.nodeType === 1) { - if (node.hasAttribute('class') && 'classWithChildText' in options && node.matches(options.classWithChildText.class)) { - out += node.querySelector(options.classWithChildText.element).textContent; - } else if (!('skipNodeWithClass' in options && node.matches('.' + options.skipNodeWithClass))) { - var ename = node.nodeName.toLowerCase(); - out += '<' + ename; - - var attrList = []; - for (i = node.attributes.length - 1; i >= 0; i--) { - var atn = node.attributes[i]; - if (skipAttributes.length > 0 && skipAttributes[atn.name]) continue; - if (/^\d+$/.test(atn.name)) continue; - if (atn.name === 'class' && 'replaceClassItemWith' in options && atn.value.split(' ').indexOf(options.replaceClassItemWith.source) > -1) { - var re = new RegExp(options.replaceClassItemWith.source, 'g'); - atn.value = atn.value.replace(re, options.replaceClassItemWith.target).trim(); - } - if (!(atn.name === 'class' && 'skipClassWithValue' in options && options.skipClassWithValue === atn.value)) { - attrList.push(atn.name + '=\'' + atn.value.replace(/&/g, '&').replace(//g, '>').replace(/'/g, '"') + '\''); - } - } - if (attrList.length > 0) { - if ('sortAttributes' in options && options.sortAttributes) { - attrList.sort(function (a, b) { - return a.toLowerCase().localeCompare(b.toLowerCase()); - }); - } - out += ' ' + attrList.join(' '); - } - if (selfClosing[ename]) { - out += ' />'; - } else { - out += '>'; - out += ename === 'html' ? '\n ' : ''; - noEsc.push(ename === 'style' || ename === 'script'); - for (i = 0; i < node.childNodes.length; i++) { - out += dumpNode(node.childNodes[i]); - }noEsc.pop(); - out += ename === 'body' ? '' + '\n' : ''; - } - } - } else if (node.nodeType === 8) { - // FIXME: If comments are not tabbed in source, a new line is not prepended - out += ''; - } else if (node.nodeType === 3 || node.nodeType === 4) { - // XXX: Remove new lines which were added after DOM ready - var nl = node.nodeValue.replace(/\n+$/, ''); - out += noEsc[noEsc.length - 1] ? nl : nl.replace(/&/g, '&').replace(//g, '>'); - } else { - console.log('Warning; Cannot handle serialising nodes of type: ' + node.nodeType); - console.log(node); - } - return out; -} - -function dtstamp() { - var now = new Date(); - var year = now.getYear() + 1900; - var month = now.getMonth() + 1; - var day = now.getDate(); - var hour = now.getUTCHours(); - var minute = now.getUTCMinutes(); - var second = now.getSeconds(); - if (month < 10) month = '0' + month; - if (day < 10) day = '0' + day; - if (hour < 10) hour = '0' + hour; - if (minute < 10) minute = '0' + minute; - if (second < 10) second = '0' + second; - return year + '-' + month + '-' + day + 'T' + hour + ':' + minute + ':' + second + 'Z'; -} - -/** - * Returns a hashmap of HTTP headers and their values. - * @@ Bug: Assumes that each header only occurs once. - * Also note that a , in a header value is just the same as having two headers. - */ -function getHTTPHeaders(xhr) { - var lines = xhr.getAllResponseHeaders().split('\n'); - var headers = {}; - var last; - for (var x = 0; x < lines.length; x++) { - if (lines[x].length > 0) { - var pair = lines[x].split(': '); - if (typeof pair[1] === 'undefined') { - // continuation - headers[last] += '\n' + pair[0]; - } else { - last = pair[0].toLowerCase(); - headers[last] = pair[1]; - } - } - } - return headers; -} - -/** - * Compares statements (heavy comparison for repeatable canonical ordering) - */ -function heavyCompare(x, y, g, uriMap) { - var nonBlank = function nonBlank(x) { - return x.termType === 'BlankNode' ? null : x; - }; - var signature = function signature(x) { - var lis = g.statementsMatching(x).map(function (st) { - return '' + nonBlank(st.subject) + ' ' + nonBlank(st.predicate) + ' ' + nonBlank(st.object); - }).concat(g.statementsMatching(undefined, undefined, x).map(function (st) { - return '' + nonBlank(st.subject) + ' ' + nonBlank(st.predicate) + ' ' + nonBlank(st.object); - })); - lis.sort(); - return lis.join('\n'); - }; - if (x.termType === 'BlankNode' && y.termType === 'BlankNode') { - if (x.compareTerm(y) === 0) return 0; // Same - if (signature(x) > signature(y)) return +1; - if (signature(x) < signature(y)) return -1; - return x.compareTerm(y); // Too bad -- this order not canonical. - // throw "different bnodes indistinquishable for sorting" - } else { - if (uriMap && x.uri && y.uri) { - return (uriMap[x.uri] || x.uri).localeCompare(uriMap[y.uri] || y.uri); - } - return x.compareTerm(y); - } -} - -function heavyCompareSPO(x, y, g, uriMap) { - return heavyCompare(x.subject, y.subject, g, uriMap) || heavyCompare(x.predicate, y.predicate, g, uriMap) || heavyCompare(x.object, y.object, g, uriMap); -} - -/** - * Defines a simple debugging function - * @method output - * @param o {String} - */ -function output(o) { - var k = document.createElement('div'); - k.textContent = o; - document.body.appendChild(k); -} - -/** - * Returns a DOM from parsex XML. - */ -function parseXML(str, options) { - var dparser; - options = options || {}; - if (typeof tabulator !== 'undefined' && tabulator.isExtension) { - dparser = Components.classes['@mozilla.org/xmlextras/domparser;1'].getService(Components.interfaces.nsIDOMParser); - } else if (typeof module !== 'undefined' && module && module.exports) { - // Node.js - // var libxmljs = require('libxmljs'); // Was jsdom before 2012-01 then libxmljs but that nonstandard - // return libxmljs.parseXmlString(str) - - // var jsdom = require('jsdom'); 2012-01 though 2015-08 no worky with new Node - // var dom = jsdom.jsdom(str, undefined, {} );// html, level, options - - var DOMParser = _dereq_('xmldom').DOMParser; // 2015-08 on https://github.com/jindw/xmldom - var dom = new DOMParser().parseFromString(str, options.contentType || 'application/xhtml+xml'); - return dom; - } else { - if (typeof window !== 'undefined' && window.DOMParser) { - dparser = new window.DOMParser(); // seems to actually work - } else { - dparser = new DOMParser(); // Doc says this works - } - } - return dparser.parseFromString(str, 'application/xml'); -} - -/** - * Removes all statements equal to x from a - * Exports as `RDFArrayRemove` - */ -function rdfArrayRemove(a, x) { - for (var i = 0; i < a.length; i++) { - // TODO: This used to be the following, which didnt always work..why - // if(a[i] === x) - if (a[i].subject.sameTerm(x.subject) && a[i].predicate.sameTerm(x.predicate) && a[i].object.sameTerm(x.object) && a[i].why.sameTerm(x.why)) { - a.splice(i, 1); - return; - } - } - throw new Error('RDFArrayRemove: Array did not contain ' + x + ' ' + x.why); -} - -function stringStartsWith(str, pref) { - // missing library routines - return str.slice(0, pref.length) === pref; -} - -/** - * C++, python style %s -> subs - */ -function stringTemplate(base, subs) { - var baseA = base.split('%s'); - var result = ''; - for (var i = 0; i < subs.length; i++) { - subs[i] += ''; - result += baseA[i] + subs[i]; - } - return result + baseA.slice(subs.length).join(); -} - -// Stack dump on errors - to pass errors back - -function stackString(e) { - var str = '' + e + '\n'; - if (!e.stack) { - return str + 'No stack available.\n'; - } - var lines = e.stack.toString().split('\n'); - var toprint = []; - for (var i = 0; i < lines.length; i++) { - var line = lines[i]; - if (line.indexOf('ecmaunit.js') > -1) { - // remove useless bit of traceback - break; - } - if (line.charAt(0) == '(') { - line = 'function' + line; - } - var chunks = line.split('@'); - toprint.push(chunks); - } - // toprint.reverse(); No - I prefer the latest at the top by the error message -tbl - - for (var i = 0; i < toprint.length; i++) { - str += ' ' + toprint[i][1] + '\n ' + toprint[i][0]; - } - return str; -} - -/** - * Finds the variables in a graph (shallow). - * Note: UNUSED. - */ -// function variablesIn (g) { -// for (var i = 0; i < g.statements.length; i++) { -// var st = g.statatements[i] -// var vars = {} -// if (st.subject instanceof $rdf.Variable) { -// vars[st.subject.toNT()] = true -// } -// if (st.predicate instanceof $rdf.Variable) { -// vars[st.predicate.toNT()] = true -// } -// if (st.object instanceof $rdf.Variable) { -// vars[st.object.toNT()] = true -// } -// } -// return vars -// } - -/** - * Returns an XMLHttpRequest object for the appropriate current runtime - * environment. Exports as `XMLHTTPFactory` - */ -function xhr() { - var XMLHttpRequest; - // Running inside the Tabulator Firefox extension - if (typeof tabulator !== 'undefined' && tabulator.isExtension) { - // Cannot use XMLHttpRequest natively, must request it through SDK - return Components.classes['@mozilla.org/xmlextras/xmlhttprequest;1'].createInstance().QueryInterface(Components.interfaces.nsIXMLHttpRequest); - } else if (typeof window !== 'undefined' && 'XMLHttpRequest' in window) { - // Running inside the browser - XMLHttpRequest = window.XMLHttpRequest; - return new XMLHttpRequest(); - } else if (typeof module !== 'undefined' && module && module.exports) { - // Running in Node.js - XMLHttpRequest = _dereq_('xmlhttprequest').XMLHttpRequest; - return new XMLHttpRequest(); - } else if (window.ActiveXObject) { - try { - return new ActiveXObject('Msxml2.XMLHTTP'); - } catch (e) { - return new ActiveXObject('Microsoft.XMLHTTP'); - } - } else { - return false; - } +'use strict'; + +/** + * Utility functions for $rdf + * @module util + */ +var docpart = _dereq_('./uri').docpart; +var log = _dereq_('./log'); +var NamedNode = _dereq_('./named-node'); + +module.exports.AJAR_handleNewTerm = ajarHandleNewTerm; +module.exports.ArrayIndexOf = arrayIndexOf; +module.exports.callbackify = callbackify; +module.exports.dtstamp = dtstamp; +module.exports.DOMParserFactory = domParser; +module.exports.domToString = domToString; +module.exports.dumpNode = dumpNode; +module.exports.getHTTPHeaders = getHTTPHeaders; +module.exports.heavyCompare = heavyCompare; +module.exports.heavyCompareSPO = heavyCompareSPO; +module.exports.output = output; +module.exports.parseXML = parseXML; +module.exports.RDFArrayRemove = rdfArrayRemove; +module.exports.stackString = stackString; +module.exports.string_startswith = stringStartsWith; +module.exports.string = {}; +module.exports.string.template = stringTemplate; +module.exports.uri = _dereq_('./uri'); // TODO: Remove this mixed usage +// module.exports.variablesIn = variablesIn +module.exports.XMLHTTPFactory = xhr; +module.exports.log = log; + +module.exports.mediaTypeClass = function (mediaType) { + mediaType = mediaType.split(';')[0].trim(); // remove media type parameters + return new NamedNode('http://www.w3.org/ns/iana/media-types/' + mediaType + '#Resource'); +}; + +/** + * Loads ontologies of the data we load (this is the callback from the kb to + * the fetcher). Exports as `AJAR_handleNewTerm` + */ +function ajarHandleNewTerm(kb, p, requestedBy) { + var sf = null; + if (typeof kb.fetcher !== 'undefined') { + sf = kb.fetcher; + } else { + return; + } + if (p.termType !== 'NamedNode') return; + var docuri = docpart(p.uri); + var fixuri; + if (p.uri.indexOf('#') < 0) { + // No hash + // @@ major hack for dbpedia Categories, which spread indefinitely + if (stringStartsWith(p.uri, 'http://dbpedia.org/resource/Category:')) return; + + /* + if (string_startswith(p.uri, 'http://xmlns.com/foaf/0.1/')) { + fixuri = "http://dig.csail.mit.edu/2005/ajar/ajaw/test/foaf" + // should give HTTP 303 to ontology -- now is :-) + } else + */ + if (stringStartsWith(p.uri, 'http://purl.org/dc/elements/1.1/') || stringStartsWith(p.uri, 'http://purl.org/dc/terms/')) { + fixuri = 'http://dublincore.org/2005/06/13/dcq'; + // dc fetched multiple times + } else if (stringStartsWith(p.uri, 'http://xmlns.com/wot/0.1/')) { + fixuri = 'http://xmlns.com/wot/0.1/index.rdf'; + } else if (stringStartsWith(p.uri, 'http://web.resource.org/cc/')) { + // log.warn("creative commons links to html instead of rdf. doesn't seem to content-negotiate.") + fixuri = 'http://web.resource.org/cc/schema.rdf'; + } + } + if (fixuri) { + docuri = fixuri; + } + if (sf && sf.getState(docuri) !== 'unrequested') return; + + if (fixuri) { + // only give warning once: else happens too often + log.warn('Assuming server still broken, faking redirect of <' + p.uri + '> to <' + docuri + '>'); + } + sf.requestURI(docuri, requestedBy); +} + +/** + * Exports as `ArrayIndexOf`. + */ +function arrayIndexOf(arr, item, i) { + i || (i = 0); + var length = arr.length; + if (i < 0) i = length + i; + for (; i < length; i++) { + if (arr[i] === item) { + return i; + } + } + return -1; +} + +/** + * Adds callback functionality to an object. + * Callback functions are indexed by a 'hook' string. + * They return true if they want to be called again. + * @method callbackify + * @param obj {Object} + * @param callbacks {Array} + */ +function callbackify(obj, callbacks) { + obj.callbacks = {}; + for (var x = callbacks.length - 1; x >= 0; x--) { + obj.callbacks[callbacks[x]] = []; + } + + obj.addHook = function (hook) { + if (!obj.callbacks[hook]) { + obj.callbacks[hook] = []; + } + }; + + obj.addCallback = function (hook, func) { + obj.callbacks[hook].push(func); + }; + + obj.removeCallback = function (hook, funcName) { + for (var i = 0; i < obj.callbacks[hook].length; i++) { + if (obj.callbacks[hook][i].name === funcName) { + obj.callbacks[hook].splice(i, 1); + return true; + } + } + return false; + }; + + obj.insertCallback = function (hook, func) { + obj.callbacks[hook].unshift(func); + }; + + obj.fireCallbacks = function (hook, args) { + var newCallbacks = []; + var replaceCallbacks = []; + var len = obj.callbacks[hook].length; + var x; + // log.info('!@$ Firing '+hook+' call back with length'+len) + for (x = len - 1; x >= 0; x--) { + // log.info('@@ Firing '+hook+' callback '+ obj.callbacks[hook][x]) + if (obj.callbacks[hook][x].apply(obj, args)) { + newCallbacks.push(obj.callbacks[hook][x]); + } + } + + for (x = newCallbacks.length - 1; x >= 0; x--) { + replaceCallbacks.push(newCallbacks[x]); + } + + for (x = len; x < obj.callbacks[hook].length; x++) { + replaceCallbacks.push(obj.callbacks[hook][x]); + } + + obj.callbacks[hook] = replaceCallbacks; + }; +} + +/** + * Returns a DOM parser based on current runtime environment. + * Exports as `DOMParserFactory` + */ +function domParser() { + if (tabulator && tabulator.isExtension) { + return Components.classes['@mozilla.org/xmlextras/domparser;1'].getService(Components.interfaces.nsIDOMParser); + } else if (window.DOMParser) { + return new DOMParser(); + } else if (window.ActiveXObject) { + return new ActiveXObject('Microsoft.XMLDOM'); + } else { + return false; + } +} + +// From https://github.com/linkeddata/dokieli +function domToString(node, options) { + options = options || {}; + var selfClosing = []; + if ('selfClosing' in options) { + options.selfClosing.split(' ').forEach(function (n) { + selfClosing[n] = true; + }); + } + var skipAttributes = []; + if ('skipAttributes' in options) { + options.skipAttributes.split(' ').forEach(function (n) { + skipAttributes[n] = true; + }); + } + return dumpNode(node, options, selfClosing, skipAttributes); +} + +function dumpNode(node, options, selfClosing, skipAttributes) { + var i; + var out = ''; + var noEsc = [false]; + if (typeof node.nodeType === 'undefined') return out; + if (node.nodeType === 1) { + if (node.hasAttribute('class') && 'classWithChildText' in options && node.matches(options.classWithChildText.class)) { + out += node.querySelector(options.classWithChildText.element).textContent; + } else if (!('skipNodeWithClass' in options && node.matches('.' + options.skipNodeWithClass))) { + var ename = node.nodeName.toLowerCase(); + out += '<' + ename; + + var attrList = []; + for (i = node.attributes.length - 1; i >= 0; i--) { + var atn = node.attributes[i]; + if (skipAttributes.length > 0 && skipAttributes[atn.name]) continue; + if (/^\d+$/.test(atn.name)) continue; + if (atn.name === 'class' && 'replaceClassItemWith' in options && atn.value.split(' ').indexOf(options.replaceClassItemWith.source) > -1) { + var re = new RegExp(options.replaceClassItemWith.source, 'g'); + atn.value = atn.value.replace(re, options.replaceClassItemWith.target).trim(); + } + if (!(atn.name === 'class' && 'skipClassWithValue' in options && options.skipClassWithValue === atn.value)) { + attrList.push(atn.name + '=\'' + atn.value.replace(/&/g, '&').replace(//g, '>').replace(/'/g, '"') + '\''); + } + } + if (attrList.length > 0) { + if ('sortAttributes' in options && options.sortAttributes) { + attrList.sort(function (a, b) { + return a.toLowerCase().localeCompare(b.toLowerCase()); + }); + } + out += ' ' + attrList.join(' '); + } + if (selfClosing[ename]) { + out += ' />'; + } else { + out += '>'; + out += ename === 'html' ? '\n ' : ''; + noEsc.push(ename === 'style' || ename === 'script'); + for (i = 0; i < node.childNodes.length; i++) { + out += dumpNode(node.childNodes[i]); + }noEsc.pop(); + out += ename === 'body' ? '' + '\n' : ''; + } + } + } else if (node.nodeType === 8) { + // FIXME: If comments are not tabbed in source, a new line is not prepended + out += ''; + } else if (node.nodeType === 3 || node.nodeType === 4) { + // XXX: Remove new lines which were added after DOM ready + var nl = node.nodeValue.replace(/\n+$/, ''); + out += noEsc[noEsc.length - 1] ? nl : nl.replace(/&/g, '&').replace(//g, '>'); + } else { + console.log('Warning; Cannot handle serialising nodes of type: ' + node.nodeType); + console.log(node); + } + return out; +} + +function dtstamp() { + var now = new Date(); + var year = now.getYear() + 1900; + var month = now.getMonth() + 1; + var day = now.getDate(); + var hour = now.getUTCHours(); + var minute = now.getUTCMinutes(); + var second = now.getSeconds(); + if (month < 10) month = '0' + month; + if (day < 10) day = '0' + day; + if (hour < 10) hour = '0' + hour; + if (minute < 10) minute = '0' + minute; + if (second < 10) second = '0' + second; + return year + '-' + month + '-' + day + 'T' + hour + ':' + minute + ':' + second + 'Z'; +} + +/** + * Returns a hashmap of HTTP headers and their values. + * @@ Bug: Assumes that each header only occurs once. + * Also note that a , in a header value is just the same as having two headers. + */ +function getHTTPHeaders(xhr) { + var lines = xhr.getAllResponseHeaders().split('\n'); + var headers = {}; + var last; + for (var x = 0; x < lines.length; x++) { + if (lines[x].length > 0) { + var pair = lines[x].split(': '); + if (typeof pair[1] === 'undefined') { + // continuation + headers[last] += '\n' + pair[0]; + } else { + last = pair[0].toLowerCase(); + headers[last] = pair[1]; + } + } + } + return headers; +} + +/** + * Compares statements (heavy comparison for repeatable canonical ordering) + */ +function heavyCompare(x, y, g, uriMap) { + var nonBlank = function nonBlank(x) { + return x.termType === 'BlankNode' ? null : x; + }; + var signature = function signature(x) { + var lis = g.statementsMatching(x).map(function (st) { + return '' + nonBlank(st.subject) + ' ' + nonBlank(st.predicate) + ' ' + nonBlank(st.object); + }).concat(g.statementsMatching(undefined, undefined, x).map(function (st) { + return '' + nonBlank(st.subject) + ' ' + nonBlank(st.predicate) + ' ' + nonBlank(st.object); + })); + lis.sort(); + return lis.join('\n'); + }; + if (x.termType === 'BlankNode' && y.termType === 'BlankNode') { + if (x.compareTerm(y) === 0) return 0; // Same + if (signature(x) > signature(y)) return +1; + if (signature(x) < signature(y)) return -1; + return x.compareTerm(y); // Too bad -- this order not canonical. + // throw "different bnodes indistinquishable for sorting" + } else { + if (uriMap && x.uri && y.uri) { + return (uriMap[x.uri] || x.uri).localeCompare(uriMap[y.uri] || y.uri); + } + return x.compareTerm(y); + } +} + +function heavyCompareSPO(x, y, g, uriMap) { + return heavyCompare(x.subject, y.subject, g, uriMap) || heavyCompare(x.predicate, y.predicate, g, uriMap) || heavyCompare(x.object, y.object, g, uriMap); +} + +/** + * Defines a simple debugging function + * @method output + * @param o {String} + */ +function output(o) { + var k = document.createElement('div'); + k.textContent = o; + document.body.appendChild(k); +} + +/** + * Returns a DOM from parsex XML. + */ +function parseXML(str, options) { + var dparser; + options = options || {}; + if (typeof tabulator !== 'undefined' && tabulator.isExtension) { + dparser = Components.classes['@mozilla.org/xmlextras/domparser;1'].getService(Components.interfaces.nsIDOMParser); + } else if (typeof module !== 'undefined' && module && module.exports) { + // Node.js + // var libxmljs = require('libxmljs'); // Was jsdom before 2012-01 then libxmljs but that nonstandard + // return libxmljs.parseXmlString(str) + + // var jsdom = require('jsdom'); 2012-01 though 2015-08 no worky with new Node + // var dom = jsdom.jsdom(str, undefined, {} );// html, level, options + + var DOMParser = _dereq_('xmldom').DOMParser; // 2015-08 on https://github.com/jindw/xmldom + var dom = new DOMParser().parseFromString(str, options.contentType || 'application/xhtml+xml'); + return dom; + } else { + if (typeof window !== 'undefined' && window.DOMParser) { + dparser = new window.DOMParser(); // seems to actually work + } else { + dparser = new DOMParser(); // Doc says this works + } + } + return dparser.parseFromString(str, 'application/xml'); +} + +/** + * Removes all statements equal to x from a + * Exports as `RDFArrayRemove` + */ +function rdfArrayRemove(a, x) { + for (var i = 0; i < a.length; i++) { + // TODO: This used to be the following, which didnt always work..why + // if(a[i] === x) + if (a[i].subject.sameTerm(x.subject) && a[i].predicate.sameTerm(x.predicate) && a[i].object.sameTerm(x.object) && a[i].why.sameTerm(x.why)) { + a.splice(i, 1); + return; + } + } + throw new Error('RDFArrayRemove: Array did not contain ' + x + ' ' + x.why); +} + +function stringStartsWith(str, pref) { + // missing library routines + return str.slice(0, pref.length) === pref; +} + +/** + * C++, python style %s -> subs + */ +function stringTemplate(base, subs) { + var baseA = base.split('%s'); + var result = ''; + for (var i = 0; i < subs.length; i++) { + subs[i] += ''; + result += baseA[i] + subs[i]; + } + return result + baseA.slice(subs.length).join(); +} + +// Stack dump on errors - to pass errors back + +function stackString(e) { + var str = '' + e + '\n'; + if (!e.stack) { + return str + 'No stack available.\n'; + } + var lines = e.stack.toString().split('\n'); + var toprint = []; + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + if (line.indexOf('ecmaunit.js') > -1) { + // remove useless bit of traceback + break; + } + if (line.charAt(0) == '(') { + line = 'function' + line; + } + var chunks = line.split('@'); + toprint.push(chunks); + } + // toprint.reverse(); No - I prefer the latest at the top by the error message -tbl + + for (var i = 0; i < toprint.length; i++) { + str += ' ' + toprint[i][1] + '\n ' + toprint[i][0]; + } + return str; +} + +/** + * Finds the variables in a graph (shallow). + * Note: UNUSED. + */ +// function variablesIn (g) { +// for (var i = 0; i < g.statements.length; i++) { +// var st = g.statatements[i] +// var vars = {} +// if (st.subject instanceof $rdf.Variable) { +// vars[st.subject.toNT()] = true +// } +// if (st.predicate instanceof $rdf.Variable) { +// vars[st.predicate.toNT()] = true +// } +// if (st.object instanceof $rdf.Variable) { +// vars[st.object.toNT()] = true +// } +// } +// return vars +// } + +/** + * Returns an XMLHttpRequest object for the appropriate current runtime + * environment. Exports as `XMLHTTPFactory` + */ +function xhr() { + var XMLHttpRequest; + // Running inside the Tabulator Firefox extension + if (typeof tabulator !== 'undefined' && tabulator.isExtension) { + // Cannot use XMLHttpRequest natively, must request it through SDK + return Components.classes['@mozilla.org/xmlextras/xmlhttprequest;1'].createInstance().QueryInterface(Components.interfaces.nsIXMLHttpRequest); + } else if (typeof window !== 'undefined' && 'XMLHttpRequest' in window) { + // Running inside the browser + XMLHttpRequest = window.XMLHttpRequest; + return new XMLHttpRequest(); + } else if (typeof module !== 'undefined' && module && module.exports) { + // Running in Node.js + XMLHttpRequest = _dereq_('xmlhttprequest').XMLHttpRequest; + return new XMLHttpRequest(); + } else if (window.ActiveXObject) { + try { + return new ActiveXObject('Msxml2.XMLHTTP'); + } catch (e) { + return new ActiveXObject('Microsoft.XMLHTTP'); + } + } else { + return false; + } } },{"./log":64,"./named-node":66,"./uri":81,"xmldom":183,"xmlhttprequest":186}],83:[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 Uri = _dereq_('./uri'); - -/** - * Variables are placeholders used in patterns to be matched. - * In cwm they are symbols which are the formula's list of quantified variables. - * In sparql they are not visibly URIs. Here we compromise, by having - * a common special base URI for variables. Their names are uris, - * but the ? notation has an implicit base uri of 'varid:' - * @class Variable - */ - -var Variable = function (_Node) { - _inherits(Variable, _Node); - - function Variable() { - var name = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; - - _classCallCheck(this, Variable); - - var _this = _possibleConstructorReturn(this, (Variable.__proto__ || Object.getPrototypeOf(Variable)).call(this)); - - _this.termType = Variable.termType; - _this.value = name; - _this.base = 'varid:'; - _this.uri = Uri.join(name, _this.base); - return _this; - } - - _createClass(Variable, [{ - key: 'equals', - value: function equals(other) { - if (!other) { - return false; - } - return this.termType === other.termType && this.value === other.value; - } - }, { - key: 'hashString', - value: function hashString() { - return this.toString(); - } - }, { - key: 'substitute', - value: function substitute(bindings) { - var ref; - return (ref = bindings[this.toNT()]) != null ? ref : this; - } - }, { - key: 'toString', - value: function toString() { - if (this.uri.slice(0, this.base.length) === this.base) { - return '?' + this.uri.slice(this.base.length); - } - return '?' + this.uri; - } - }]); - - return Variable; -}(Node); - -Variable.termType = 'Variable'; -Variable.prototype.classOrder = ClassOrder['Variable']; -Variable.prototype.isVar = 1; - +'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 Uri = _dereq_('./uri'); + +/** + * Variables are placeholders used in patterns to be matched. + * In cwm they are symbols which are the formula's list of quantified variables. + * In sparql they are not visibly URIs. Here we compromise, by having + * a common special base URI for variables. Their names are uris, + * but the ? notation has an implicit base uri of 'varid:' + * @class Variable + */ + +var Variable = function (_Node) { + _inherits(Variable, _Node); + + function Variable() { + var name = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; + + _classCallCheck(this, Variable); + + var _this = _possibleConstructorReturn(this, (Variable.__proto__ || Object.getPrototypeOf(Variable)).call(this)); + + _this.termType = Variable.termType; + _this.value = name; + _this.base = 'varid:'; + _this.uri = Uri.join(name, _this.base); + return _this; + } + + _createClass(Variable, [{ + key: 'equals', + value: function equals(other) { + if (!other) { + return false; + } + return this.termType === other.termType && this.value === other.value; + } + }, { + key: 'hashString', + value: function hashString() { + return this.toString(); + } + }, { + key: 'substitute', + value: function substitute(bindings) { + var ref; + return (ref = bindings[this.toNT()]) != null ? ref : this; + } + }, { + key: 'toString', + value: function toString() { + if (this.uri.slice(0, this.base.length) === this.base) { + return '?' + this.uri.slice(this.base.length); + } + return '?' + this.uri; + } + }]); + + return Variable; +}(Node); + +Variable.termType = 'Variable'; +Variable.prototype.classOrder = ClassOrder['Variable']; +Variable.prototype.isVar = 1; + module.exports = Variable; },{"./class-order":52,"./node":68,"./uri":81}],84:[function(_dereq_,module,exports){ -'use strict'; - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -var NamedNode = _dereq_('./named-node'); - -var XSD = function XSD() { - _classCallCheck(this, XSD); -}; - -XSD.boolean = new NamedNode('http://www.w3.org/2001/XMLSchema#boolean'); -XSD.dateTime = new NamedNode('http://www.w3.org/2001/XMLSchema#dateTime'); -XSD.decimal = new NamedNode('http://www.w3.org/2001/XMLSchema#decimal'); -XSD.double = new NamedNode('http://www.w3.org/2001/XMLSchema#double'); -XSD.integer = new NamedNode('http://www.w3.org/2001/XMLSchema#integer'); -XSD.langString = new NamedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#langString'); -XSD.string = new NamedNode('http://www.w3.org/2001/XMLSchema#string'); - +'use strict'; + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var NamedNode = _dereq_('./named-node'); + +var XSD = function XSD() { + _classCallCheck(this, XSD); +}; + +XSD.boolean = new NamedNode('http://www.w3.org/2001/XMLSchema#boolean'); +XSD.dateTime = new NamedNode('http://www.w3.org/2001/XMLSchema#dateTime'); +XSD.decimal = new NamedNode('http://www.w3.org/2001/XMLSchema#decimal'); +XSD.double = new NamedNode('http://www.w3.org/2001/XMLSchema#double'); +XSD.integer = new NamedNode('http://www.w3.org/2001/XMLSchema#integer'); +XSD.langString = new NamedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#langString'); +XSD.string = new NamedNode('http://www.w3.org/2001/XMLSchema#string'); + module.exports = XSD; },{"./named-node":66}],85:[function(_dereq_,module,exports){ arguments[4][34][0].apply(exports,arguments) @@ -50649,13231 +50698,13220 @@ function split(source,start){ exports.XMLReader = XMLReader; -},{}],186:[function(_dereq_,module,exports){ -(function (process,Buffer){ -/** - * Wrapper for built-in http.js to emulate the browser XMLHttpRequest object. - * - * This can be used with JS designed for browsers to improve reuse of code and - * allow the use of existing libraries. - * - * Usage: include("XMLHttpRequest.js") and use XMLHttpRequest per W3C specs. - * - * @author Dan DeFelippi - * @contributor David Ellis - * @license MIT - */ +},{}],186:[function(_dereq_,module,exports){ +(function (process,Buffer){ +/** + * Wrapper for built-in http.js to emulate the browser XMLHttpRequest object. + * + * This can be used with JS designed for browsers to improve reuse of code and + * allow the use of existing libraries. + * + * Usage: include("XMLHttpRequest.js") and use XMLHttpRequest per W3C specs. + * + * @author Dan DeFelippi + * @contributor David Ellis + * @license MIT + */ + +var Url = _dereq_("url"); +var spawn = _dereq_("child_process").spawn; +var fs = _dereq_("fs"); + +exports.XMLHttpRequest = function() { + "use strict"; + + /** + * Private variables + */ + var self = this; + var http = _dereq_("http"); + var https = _dereq_("https"); + + // Holds http.js objects + var request; + var response; + + // Request settings + var settings = {}; + + // Disable header blacklist. + // Not part of XHR specs. + var disableHeaderCheck = false; + + // Set some default headers + var defaultHeaders = { + "User-Agent": "node-XMLHttpRequest", + "Accept": "*/*", + }; + + var headers = {}; + var headersCase = {}; + + // These headers are not user setable. + // The following are allowed but banned in the spec: + // * user-agent + var forbiddenRequestHeaders = [ + "accept-charset", + "accept-encoding", + "access-control-request-headers", + "access-control-request-method", + "connection", + "content-length", + "content-transfer-encoding", + "cookie", + "cookie2", + "date", + "expect", + "host", + "keep-alive", + "origin", + "referer", + "te", + "trailer", + "transfer-encoding", + "upgrade", + "via" + ]; + + // These request methods are not allowed + var forbiddenRequestMethods = [ + "TRACE", + "TRACK", + "CONNECT" + ]; + + // Send flag + var sendFlag = false; + // Error flag, used when errors occur or abort is called + var errorFlag = false; + + // Event listeners + var listeners = {}; + + /** + * Constants + */ + + this.UNSENT = 0; + this.OPENED = 1; + this.HEADERS_RECEIVED = 2; + this.LOADING = 3; + this.DONE = 4; + + /** + * Public vars + */ + + // Current state + this.readyState = this.UNSENT; + + // default ready state change handler in case one is not set or is set late + this.onreadystatechange = null; + + // Result & response + this.responseText = ""; + this.responseXML = ""; + this.status = null; + this.statusText = null; + + // Whether cross-site Access-Control requests should be made using + // credentials such as cookies or authorization headers + this.withCredentials = false; + + /** + * Private methods + */ + + /** + * Check if the specified header is allowed. + * + * @param string header Header to validate + * @return boolean False if not allowed, otherwise true + */ + var isAllowedHttpHeader = function(header) { + return disableHeaderCheck || (header && forbiddenRequestHeaders.indexOf(header.toLowerCase()) === -1); + }; + + /** + * Check if the specified method is allowed. + * + * @param string method Request method to validate + * @return boolean False if not allowed, otherwise true + */ + var isAllowedHttpMethod = function(method) { + return (method && forbiddenRequestMethods.indexOf(method) === -1); + }; + + /** + * Public methods + */ + + /** + * Open the connection. Currently supports local server requests. + * + * @param string method Connection method (eg GET, POST) + * @param string url URL for the connection. + * @param boolean async Asynchronous connection. Default is true. + * @param string user Username for basic authentication (optional) + * @param string password Password for basic authentication (optional) + */ + this.open = function(method, url, async, user, password) { + this.abort(); + errorFlag = false; + + // Check for valid request method + if (!isAllowedHttpMethod(method)) { + throw new Error("SecurityError: Request method not allowed"); + } + + settings = { + "method": method, + "url": url.toString(), + "async": (typeof async !== "boolean" ? true : async), + "user": user || null, + "password": password || null + }; + + setState(this.OPENED); + }; + + /** + * Disables or enables isAllowedHttpHeader() check the request. Enabled by default. + * This does not conform to the W3C spec. + * + * @param boolean state Enable or disable header checking. + */ + this.setDisableHeaderCheck = function(state) { + disableHeaderCheck = state; + }; + + /** + * Sets a header for the request or appends the value if one is already set. + * + * @param string header Header name + * @param string value Header value + */ + this.setRequestHeader = function(header, value) { + if (this.readyState !== this.OPENED) { + throw new Error("INVALID_STATE_ERR: setRequestHeader can only be called when state is OPEN"); + } + if (!isAllowedHttpHeader(header)) { + console.warn("Refused to set unsafe header \"" + header + "\""); + return; + } + if (sendFlag) { + throw new Error("INVALID_STATE_ERR: send flag is true"); + } + header = headersCase[header.toLowerCase()] || header; + headersCase[header.toLowerCase()] = header; + headers[header] = headers[header] ? headers[header] + ', ' + value : value; + }; + + /** + * Gets a header from the server response. + * + * @param string header Name of header to get. + * @return string Text of the header or null if it doesn't exist. + */ + this.getResponseHeader = function(header) { + if (typeof header === "string" + && this.readyState > this.OPENED + && response + && response.headers + && response.headers[header.toLowerCase()] + && !errorFlag + ) { + return response.headers[header.toLowerCase()]; + } + + return null; + }; + + /** + * Gets all the response headers. + * + * @return string A string with all response headers separated by CR+LF + */ + this.getAllResponseHeaders = function() { + if (this.readyState < this.HEADERS_RECEIVED || errorFlag) { + return ""; + } + var result = ""; + + for (var i in response.headers) { + // Cookie headers are excluded + if (i !== "set-cookie" && i !== "set-cookie2") { + result += i + ": " + response.headers[i] + "\r\n"; + } + } + return result.substr(0, result.length - 2); + }; + + /** + * Gets a request header + * + * @param string name Name of header to get + * @return string Returns the request header or empty string if not set + */ + this.getRequestHeader = function(name) { + if (typeof name === "string" && headersCase[name.toLowerCase()]) { + return headers[headersCase[name.toLowerCase()]]; + } + + return ""; + }; + + /** + * Sends the request to the server. + * + * @param string data Optional data to send as request body. + */ + this.send = function(data) { + if (this.readyState !== this.OPENED) { + throw new Error("INVALID_STATE_ERR: connection must be opened before send() is called"); + } + + if (sendFlag) { + throw new Error("INVALID_STATE_ERR: send has already been called"); + } + + var ssl = false, local = false; + var url = Url.parse(settings.url); + var host; + // Determine the server + switch (url.protocol) { + case "https:": + ssl = true; + // SSL & non-SSL both need host, no break here. + case "http:": + host = url.hostname; + break; + + case "file:": + local = true; + break; + + case undefined: + case null: + case "": + host = "localhost"; + break; + + default: + throw new Error("Protocol not supported."); + } + + // Load files off the local filesystem (file://) + if (local) { + if (settings.method !== "GET") { + throw new Error("XMLHttpRequest: Only GET method is supported"); + } + + if (settings.async) { + fs.readFile(url.pathname, "utf8", function(error, data) { + if (error) { + self.handleError(error); + } else { + self.status = 200; + self.responseText = data; + setState(self.DONE); + } + }); + } else { + try { + this.responseText = fs.readFileSync(url.pathname, "utf8"); + this.status = 200; + setState(self.DONE); + } catch(e) { + this.handleError(e); + } + } + + return; + } + + // Default to port 80. If accessing localhost on another port be sure + // to use http://localhost:port/path + var port = url.port || (ssl ? 443 : 80); + // Add query string if one is used + var uri = url.pathname + (url.search ? url.search : ""); + + // Set the defaults if they haven't been set + for (var name in defaultHeaders) { + if (!headersCase[name.toLowerCase()]) { + headers[name] = defaultHeaders[name]; + } + } + + // Set the Host header or the server may reject the request + headers.Host = host; + if (!((ssl && port === 443) || port === 80)) { + headers.Host += ":" + url.port; + } + + // Set Basic Auth if necessary + if (settings.user) { + if (typeof settings.password === "undefined") { + settings.password = ""; + } + var authBuf = new Buffer(settings.user + ":" + settings.password); + headers.Authorization = "Basic " + authBuf.toString("base64"); + } + + // Set content length header + if (settings.method === "GET" || settings.method === "HEAD") { + data = null; + } else if (data) { + headers["Content-Length"] = Buffer.isBuffer(data) ? data.length : Buffer.byteLength(data); + + if (!headers["Content-Type"]) { + headers["Content-Type"] = "text/plain;charset=UTF-8"; + } + } else if (settings.method === "POST") { + // For a post with no data set Content-Length: 0. + // This is required by buggy servers that don't meet the specs. + headers["Content-Length"] = 0; + } + + var options = { + host: host, + port: port, + path: uri, + method: settings.method, + headers: headers, + agent: false, + withCredentials: self.withCredentials + }; + + // Reset error flag + errorFlag = false; + + // Handle async requests + if (settings.async) { + // Use the proper protocol + var doRequest = ssl ? https.request : http.request; + + // Request is being sent, set send flag + sendFlag = true; + + // As per spec, this is called here for historical reasons. + self.dispatchEvent("readystatechange"); + + // Handler for the response + var responseHandler = function responseHandler(resp) { + // Set response var to the response we got back + // This is so it remains accessable outside this scope + response = resp; + // Check for redirect + // @TODO Prevent looped redirects + if (response.statusCode === 301 || response.statusCode === 302 || response.statusCode === 303 || response.statusCode === 307) { + // Change URL to the redirect location + settings.url = response.headers.location; + var url = Url.parse(settings.url); + // Set host var in case it's used later + host = url.hostname; + // Options for the new request + var newOptions = { + hostname: url.hostname, + port: url.port, + path: url.path, + method: response.statusCode === 303 ? "GET" : settings.method, + headers: headers, + withCredentials: self.withCredentials + }; + + // Issue the new request + request = doRequest(newOptions, responseHandler).on("error", errorHandler); + request.end(); + // @TODO Check if an XHR event needs to be fired here + return; + } + + response.setEncoding("utf8"); + + setState(self.HEADERS_RECEIVED); + self.status = response.statusCode; + + response.on("data", function(chunk) { + // Make sure there's some data + if (chunk) { + self.responseText += chunk; + } + // Don't emit state changes if the connection has been aborted. + if (sendFlag) { + setState(self.LOADING); + } + }); + + response.on("end", function() { + if (sendFlag) { + // Discard the end event if the connection has been aborted + setState(self.DONE); + sendFlag = false; + } + }); + + response.on("error", function(error) { + self.handleError(error); + }); + }; + + // Error handler for the request + var errorHandler = function errorHandler(error) { + self.handleError(error); + }; + + // Create the request + request = doRequest(options, responseHandler).on("error", errorHandler); + + // Node 0.4 and later won't accept empty data. Make sure it's needed. + if (data) { + request.write(data); + } + + request.end(); + + self.dispatchEvent("loadstart"); + } else { // Synchronous + // Create a temporary file for communication with the other Node process + var contentFile = ".node-xmlhttprequest-content-" + process.pid; + var syncFile = ".node-xmlhttprequest-sync-" + process.pid; + fs.writeFileSync(syncFile, "", "utf8"); + // The async request the other Node process executes + var execString = "var http = require('http'), https = require('https'), fs = require('fs');" + + "var doRequest = http" + (ssl ? "s" : "") + ".request;" + + "var options = " + JSON.stringify(options) + ";" + + "var responseText = '';" + + "var req = doRequest(options, function(response) {" + + "response.setEncoding('utf8');" + + "response.on('data', function(chunk) {" + + " responseText += chunk;" + + "});" + + "response.on('end', function() {" + + "fs.writeFileSync('" + contentFile + "', JSON.stringify({err: null, data: {statusCode: response.statusCode, headers: response.headers, text: responseText}}), 'utf8');" + + "fs.unlinkSync('" + syncFile + "');" + + "});" + + "response.on('error', function(error) {" + + "fs.writeFileSync('" + contentFile + "', JSON.stringify({err: error}), 'utf8');" + + "fs.unlinkSync('" + syncFile + "');" + + "});" + + "}).on('error', function(error) {" + + "fs.writeFileSync('" + contentFile + "', JSON.stringify({err: error}), 'utf8');" + + "fs.unlinkSync('" + syncFile + "');" + + "});" + + (data ? "req.write('" + JSON.stringify(data).slice(1,-1).replace(/'/g, "\\'") + "');":"") + + "req.end();"; + // Start the other Node Process, executing this string + var syncProc = spawn(process.argv[0], ["-e", execString]); + while(fs.existsSync(syncFile)) { + // Wait while the sync file is empty + } + var resp = JSON.parse(fs.readFileSync(contentFile, 'utf8')); + // Kill the child process once the file has data + syncProc.stdin.end(); + // Remove the temporary file + fs.unlinkSync(contentFile); + + if (resp.err) { + self.handleError(resp.err); + } else { + response = resp.data; + self.status = resp.data.statusCode; + self.responseText = resp.data.text; + setState(self.DONE); + } + } + }; + + /** + * Called when an error is encountered to deal with it. + */ + this.handleError = function(error) { + this.status = 0; + this.statusText = error; + this.responseText = error.stack; + errorFlag = true; + setState(this.DONE); + this.dispatchEvent('error'); + }; + + /** + * Aborts a request. + */ + this.abort = function() { + if (request) { + request.abort(); + request = null; + } + + headers = defaultHeaders; + this.status = 0; + this.responseText = ""; + this.responseXML = ""; + + errorFlag = true; + + if (this.readyState !== this.UNSENT + && (this.readyState !== this.OPENED || sendFlag) + && this.readyState !== this.DONE) { + sendFlag = false; + setState(this.DONE); + } + this.readyState = this.UNSENT; + this.dispatchEvent('abort'); + }; + + /** + * Adds an event listener. Preferred method of binding to events. + */ + this.addEventListener = function(event, callback) { + if (!(event in listeners)) { + listeners[event] = []; + } + // Currently allows duplicate callbacks. Should it? + listeners[event].push(callback); + }; + + /** + * Remove an event callback that has already been bound. + * Only works on the matching funciton, cannot be a copy. + */ + this.removeEventListener = function(event, callback) { + if (event in listeners) { + // Filter will return a new array with the callback removed + listeners[event] = listeners[event].filter(function(ev) { + return ev !== callback; + }); + } + }; + + /** + * Dispatch any events, including both "on" methods and events attached using addEventListener. + */ + this.dispatchEvent = function(event) { + if (typeof self["on" + event] === "function") { + self["on" + event](); + } + if (event in listeners) { + for (var i = 0, len = listeners[event].length; i < len; i++) { + listeners[event][i].call(self); + } + } + }; + + /** + * Changes readyState and calls onreadystatechange. + * + * @param int state New state + */ + var setState = function(state) { + if (state == self.LOADING || self.readyState !== state) { + self.readyState = state; + + if (settings.async || self.readyState < self.OPENED || self.readyState === self.DONE) { + self.dispatchEvent("readystatechange"); + } + + if (self.readyState === self.DONE && !errorFlag) { + self.dispatchEvent("load"); + // @TODO figure out InspectorInstrumentation::didLoadXHR(cookie) + self.dispatchEvent("loadend"); + } + } + }; +}; + +}).call(this,_dereq_('_process'),_dereq_("buffer").Buffer) + +},{"_process":47,"buffer":6,"child_process":4,"fs":4,"http":111,"https":13,"url":117}],187:[function(_dereq_,module,exports){ +module.exports = extend + +var hasOwnProperty = Object.prototype.hasOwnProperty; + +function extend() { + var target = {} + + for (var i = 0; i < arguments.length; i++) { + var source = arguments[i] + + for (var key in source) { + if (hasOwnProperty.call(source, key)) { + target[key] = source[key] + } + } + } + + return target +} + +},{}],188:[function(_dereq_,module,exports){ +module.exports={ + "name": "sbgnviz", + "version": "6.0.4", + "description": "SBGNPD visualization library", + "main": "src/index.js", + "licence": "LGPL-3.0", + "scripts": { + "test": "karma start --single-run --browsers ChromeHeadless karma.conf.js", + "build-sbgnviz-js": "gulp build", + "debug-js": "nodemon -e js --watch src -x \"npm run build-sbgnviz-js\"" + }, + "repository": { + "type": "git", + "url": "https://github.com/iVis-at-Bilkent/sbgnviz.js.git" + }, + "bugs": { + "url": "https://github.com/iVis-at-Bilkent/sbgnviz.js/issues" + }, + "homepage": "https://github.com/iVis-at-Bilkent/sbgnviz.js/", + "peerDependencies": { + "jquery": "^2.2.4", + "file-saver": "^2.0.2", + "cytoscape": "iVis-at-Bilkent/cytoscape.js#master", + "tippy.js": "^3.4.0" + }, + "dependencies": { + "libsbgn.js": "github:sbgn/libsbgn.js#master", + "pretty-data": "^0.40.0", + "xml2js": "^0.4.17" + }, + "devDependencies": { + "browserify": "^11.2.0", + "chai": "^4.3.4", + "gulp": "^3.9.1", + "gulp-derequire": "^2.1.0", + "gulp-jshint": "^1.12.0", + "gulp-prompt": "^0.1.2", + "gulp-replace": "^0.5.4", + "gulp-shell": "^0.5.2", + "gulp-util": "^3.0.8", + "jquery": "^2.2.4", + "jshint-stylish": "^2.0.1", + "karma": "^6.3.2", + "karma-browserify": "^8.0.0", + "karma-chai": "^0.1.0", + "karma-chrome-launcher": "^3.1.0", + "karma-mocha": "^2.0.1", + "mocha": "^8.3.2", + "node-notifier": "^4.3.1", + "run-sequence": "^1.2.2", + "vinyl-buffer": "^1.0.0", + "vinyl-source-stream": "^1.1.0" + } +} + +},{}],189:[function(_dereq_,module,exports){ +(function(){ + var sbgnviz = function(_options) { + + var param = {}; // The parameter to be passed to all utilities instances related to this sbgnviz instance + + var optionUtilities = _dereq_('./utilities/option-utilities-factory')(); + var options = optionUtilities.extendOptions(_options); + + var sbgnCyInstance = _dereq_('./sbgn-extensions/sbgn-cy-instance-factory')(); + + // Utilities whose functions will be exposed seperately + var uiUtilities = _dereq_('./utilities/ui-utilities-factory')(); + var fileUtilities = _dereq_('./utilities/file-utilities-factory')(); + var graphUtilities = _dereq_('./utilities/graph-utilities-factory')(); + var mainUtilities = _dereq_('./utilities/main-utilities-factory')(); + var keyboardInputUtilities = _dereq_('./utilities/keyboard-input-utilities-factory')(); // require keybord input utilities + var experimentalDataOverlay = _dereq_('./utilities/experimental-data-overlay')(); + // Utilities to be exposed as is + var elementUtilities = _dereq_('./utilities/element-utilities-factory')(); + var undoRedoActionFunctions = _dereq_('./utilities/undo-redo-action-functions-factory')(); + + // Other utilities + var jsonToSbgnmlConverter = _dereq_('./utilities/json-to-sbgnml-converter-factory')(); + var jsonToNwtConverter = _dereq_('./utilities/json-to-nwt-converter-factory')(); + var sbgnmlToJsonConverter = _dereq_('./utilities/sbgnml-to-json-converter-factory')(); + var nwtToJsonConverter = _dereq_('./utilities/nwt-to-json-converter-factory')(); + var tdToJsonConverter = _dereq_('./utilities/tab-delimited-to-json-converter-factory')(); + var sifToJsonConverter = _dereq_('./utilities/sif-to-json-converter-factory')(); + var jsonToSifConverter = _dereq_('./utilities/json-to-sif-converter-factory')(); + var classes = _dereq_('./utilities/classes'); + var tdParser = _dereq_('./utilities/tab-delimeted-parser'); + var layoutLoader = _dereq_('./utilities/layout-loader-factory')(); + var layoutToText = _dereq_('./utilities/layout-to-text-factory')(); + var cdToSbgnmlConverter = _dereq_('./utilities/cd-to-sbgnml-converter-factory')(); + var sbgnmlToCdConverter = _dereq_('./utilities/sbgnml-to-cd-converter-factory')(); + var sbgnmlToSbmlConverter = _dereq_('./utilities/sbgnml-to-sbml-converter-factory')(); + var sbmlToSbgnmlConverter = _dereq_('./utilities/sbml-to-sbgnml-converter-factory')(); + // Fill param object to use it utilities internally + + param.optionUtilities = optionUtilities; + param.sbgnCyInstance = sbgnCyInstance; + param.uiUtilities = uiUtilities; + param.fileUtilities = fileUtilities; + param.graphUtilities = graphUtilities; + param.mainUtilities = mainUtilities; + param.keyboardInputUtilities = keyboardInputUtilities; + param.elementUtilities = elementUtilities; + param.undoRedoActionFunctions = undoRedoActionFunctions; + param.jsonToSbgnmlConverter = jsonToSbgnmlConverter; + param.jsonToNwtConverter = jsonToNwtConverter; + param.sbgnmlToJsonConverter = sbgnmlToJsonConverter; + param.nwtToJsonConverter = nwtToJsonConverter; + param.tdToJsonConverter = tdToJsonConverter; + param.sifToJsonConverter = sifToJsonConverter; + param.classes = classes; + param.layoutLoader = layoutLoader; + param.layoutToText = layoutToText; + param.jsonToSifConverter = jsonToSifConverter; + param.cdToSbgnmlConverter = cdToSbgnmlConverter; + param.sbgnmlToCdConverter = sbgnmlToCdConverter; + param.sbgnmlToSbmlConverter = sbgnmlToSbmlConverter; + param.sbmlToSbgnmlConverter = sbmlToSbgnmlConverter; + param.experimentalDataOverlay = experimentalDataOverlay; + + // call constructors of objects with param + + sbgnCyInstance(param); + optionUtilities(param); + uiUtilities(param); + fileUtilities(param); + graphUtilities(param); + mainUtilities(param); + keyboardInputUtilities(param); + elementUtilities(param); + undoRedoActionFunctions(param); + jsonToSbgnmlConverter(param); + jsonToNwtConverter(param); + sbgnmlToJsonConverter(param); + nwtToJsonConverter(param); + tdToJsonConverter(param); + sifToJsonConverter(param); + layoutLoader(param); + layoutToText(param); + jsonToSifConverter(param); + cdToSbgnmlConverter(param); + sbgnmlToCdConverter(param); + sbgnmlToSbmlConverter(param); + sbmlToSbgnmlConverter(param); + experimentalDataOverlay(param); + + // set scratch pad for sbgnviz and init sbgnvizParams inside it + sbgnCyInstance.getCy().scratch('_sbgnviz', {}); + sbgnCyInstance.getCy().scratch('_sbgnviz').sbgnvizParams = param; + + // Expose the api + var api = {}; + + // Expose elementUtilities and undoRedoActionFunctions as is, most users will not need these + api.elementUtilities = elementUtilities; + api.undoRedoActionFunctions = undoRedoActionFunctions; + //api.experimentalDataOverlay = experimentalDataOverlay; + + //expose utility of experimental data-overlay + for (var prop in experimentalDataOverlay) { + api[prop] = experimentalDataOverlay[prop]; + } + + // Expose each main utility seperately + for (var prop in mainUtilities) { + api[prop] = mainUtilities[prop]; + } + + // Expose each file utility seperately + for (var prop in fileUtilities) { + api[prop] = fileUtilities[prop]; + } + + // Expose each file utility seperately + for (var prop in uiUtilities) { + api[prop] = uiUtilities[prop]; + } + + // Expose each sbgn graph utility seperately + for (var prop in graphUtilities) { + api[prop] = graphUtilities[prop]; + } + + // Expose get cy function to enable accessing related cy instance + api.getCy = sbgnCyInstance.getCy; + + // Expose some utilities directly here + api.classes = classes; + api.tdParser = tdParser; + + return api; + }; + + sbgnviz.validMapProperties = _dereq_('./utilities/validMapProperties'); + + sbgnviz.register = function (_libs) { + + var libs = {}; + libs.jQuery = _libs.jQuery || jQuery; + libs.cytoscape = _libs.cytoscape || cytoscape; + libs.saveAs = _libs.filesaver ? _libs.filesaver.saveAs : saveAs; + libs.tippy = _libs.tippy || Tippy; + + // Set the libraries to access them from any file + var libUtilities = _dereq_('./utilities/lib-utilities'); + libUtilities.setLibs(libs); + + var sbgnRenderer = _dereq_('./sbgn-extensions/sbgn-cy-renderer'); + sbgnRenderer(); + }; + + if ( typeof module !== 'undefined' && module.exports ) { + module.exports = sbgnviz; + } +})(); + +},{"./sbgn-extensions/sbgn-cy-instance-factory":190,"./sbgn-extensions/sbgn-cy-renderer":191,"./utilities/cd-to-sbgnml-converter-factory":192,"./utilities/classes":193,"./utilities/element-utilities-factory":194,"./utilities/experimental-data-overlay":195,"./utilities/file-utilities-factory":196,"./utilities/graph-utilities-factory":197,"./utilities/json-to-nwt-converter-factory":198,"./utilities/json-to-sbgnml-converter-factory":199,"./utilities/json-to-sif-converter-factory":200,"./utilities/keyboard-input-utilities-factory":201,"./utilities/layout-loader-factory":202,"./utilities/layout-to-text-factory":203,"./utilities/lib-utilities":204,"./utilities/main-utilities-factory":205,"./utilities/nwt-to-json-converter-factory":206,"./utilities/option-utilities-factory":207,"./utilities/sbgnml-to-cd-converter-factory":208,"./utilities/sbgnml-to-json-converter-factory":209,"./utilities/sbgnml-to-sbml-converter-factory":210,"./utilities/sbml-to-sbgnml-converter-factory":211,"./utilities/sif-to-json-converter-factory":212,"./utilities/tab-delimeted-parser":213,"./utilities/tab-delimited-to-json-converter-factory":214,"./utilities/ui-utilities-factory":216,"./utilities/undo-redo-action-functions-factory":217,"./utilities/validMapProperties":218}],190:[function(_dereq_,module,exports){ +var classes = _dereq_('../utilities/classes'); +var libs = _dereq_('../utilities/lib-utilities').getLibs(); +var jQuery = $ = libs.jQuery; +var cytoscape = libs.cytoscape; +var Tippy = libs.tippy; + +module.exports = function () { + + var elementUtilities, graphUtilities, mainUtilities, undoRedoActionFunctions, optionUtilities, experimentalDataOverlay; + var refreshPaddings, options, cy; + + var sbgnCyInstance = function (param) { + elementUtilities = param.elementUtilities; + graphUtilities = param.graphUtilities; + experimentalDataOverlay = param.experimentalDataOverlay; + mainUtilities = param.mainUtilities; + undoRedoActionFunctions = param.undoRedoActionFunctions; + refreshPaddings = graphUtilities.refreshPaddings.bind(graphUtilities); + + optionUtilities = param.optionUtilities; + options = optionUtilities.getOptions(); + + // cy = param.sbgnCyInstance.getCy(); + + /* + * Returns the coordinates of the point located on the given angle on the circle with the given centeral coordinates and radius. + */ + var getPointOnCircle = function(centerX, centerY, radius, angleInDegree) { + var angleInRadian = angleInDegree * ( Math.PI / 180 ); // Convert degree to radian + return { + x: radius * Math.cos(angleInRadian) + centerX, + y: -1 * radius * Math.sin(angleInRadian) + centerY // We multiply with -1 here because JS y coordinate sign is the oposite of the Mathamatical coordinates system + }; + }; + + /* + * Generates a polygon string approximating a circle with given center, radius, start, end angles and number of points to represent the circle + */ + var generateCircleString = function(centerX, centerY, radius, angleFrom, angleTo, numOfPoints) { + var circleStr = ""; + var stepSize = ( angleTo - angleFrom ) / numOfPoints; // We will increment the current angle by step size in each iteration + var currentAngle = angleFrom; // current angle will be updated in each iteration + + for ( var i = 0; i < numOfPoints; i++ ) { + var point = getPointOnCircle(centerX, centerY, radius, currentAngle); + currentAngle += stepSize; + circleStr += point.x + " " + point.y + " "; + } + + return circleStr; + }; + + /* + * Generates a string representing processes/logical operators with ports. + * lineHW: Half width of line through the circle to the intersection point + * shapeHW: Half width of the shape discluding the ports (It is radius for the circular shapes) + * type: Type of the shape discluding the ports. Options are 'circle', 'rectangle' + * orientation: Orientation of the ports Options are 'horizontal', 'vertical' + */ + + var generateShapeWithPortString = function(lineHW, shapeHW, type, orientation) { + var polygonStr; + var numOfPoints = 30; // Number of points that both halves of circle will have + if (orientation === 'horizontal') { + var abovePoints, belowPoints; + + if (type === 'circle') { + abovePoints = generateCircleString(0, 0, shapeHW, 180, 0, numOfPoints); + belowPoints = generateCircleString(0, 0, shapeHW, 360, 180, numOfPoints); + } + else if (type === 'rectangle') { + abovePoints = '-' + shapeHW + ' -' + shapeHW + ' ' + shapeHW + ' -' + shapeHW + ' '; + belowPoints = shapeHW + ' ' + shapeHW + ' -' + shapeHW + ' ' + shapeHW + ' '; + } + + polygonStr = "-1 -" + lineHW + " -" + shapeHW + " -" + lineHW + " "; + polygonStr += abovePoints; + polygonStr += shapeHW + " -" + lineHW + " 1 -" + lineHW + " 1 " + lineHW + " " + shapeHW + " " + lineHW + " "; + polygonStr += belowPoints; + polygonStr += "-" + shapeHW + " " + lineHW + " -1 " + lineHW; + } + else { + var leftPoints, rightPoints; + + if (type === 'circle') { + leftPoints = generateCircleString(0, 0, shapeHW, 90, 270, numOfPoints); + rightPoints = generateCircleString(0, 0, shapeHW, -90, 90, numOfPoints); + } + else if (type === 'rectangle') { + leftPoints = '-' + shapeHW + ' -' + shapeHW + ' -' + shapeHW + ' ' + shapeHW + ' '; + rightPoints = shapeHW + ' ' + shapeHW + ' ' + shapeHW + ' -' + shapeHW + ' '; + } + + polygonStr = "-" + lineHW + " -" + 1 + " -" + lineHW + " -" + shapeHW + " "; + polygonStr += leftPoints; + polygonStr += "-" + lineHW + " " + shapeHW + " -" + lineHW + " 1 " + lineHW + " 1 " + lineHW + " " + shapeHW + " "; + polygonStr += rightPoints; + polygonStr += lineHW + " -" + shapeHW + " " + lineHW + " -1"; + } + + return polygonStr; + }; + + // Note that in ChiSE this function is in a seperate file but in the viewer it has just 2 methods and so it is located in this file + function registerUndoRedoActions() { + // create or get the undo-redo instance + var ur = cy.undoRedo(); + + // register general actions + // register add remove actions + ur.action("deleteElesSimple", undoRedoActionFunctions.deleteElesSimple, undoRedoActionFunctions.restoreEles); + ur.action("deleteNodesSmart", undoRedoActionFunctions.deleteNodesSmart, undoRedoActionFunctions.restoreEles); + ur.action("setPortsOrdering", undoRedoActionFunctions.setPortsOrdering, undoRedoActionFunctions.setPortsOrdering); + + //experimental data ovarlay + ur.action("removeAll", undoRedoActionFunctions.removeAll, undoRedoActionFunctions.restoreAll); + ur.action("hideExperiment", undoRedoActionFunctions.hideExp, undoRedoActionFunctions.unhideExp); + ur.action("unhideExperiment", undoRedoActionFunctions.unhideExp, undoRedoActionFunctions.hideExp); + ur.action("hideFile", undoRedoActionFunctions.hideFile, undoRedoActionFunctions.hideFileUndo); + ur.action("unhideFile", undoRedoActionFunctions.unhideFile, undoRedoActionFunctions.unhideFileUndo); + ur.action("removeExperiment", undoRedoActionFunctions.removeExp, undoRedoActionFunctions.addExp); + ur.action("removeFile", undoRedoActionFunctions.removeFile, undoRedoActionFunctions.addFile); + ur.action("expButtonChange", undoRedoActionFunctions.expButtonChange, undoRedoActionFunctions.expButtonChange); + ur.action("fileButtonChangeHide", undoRedoActionFunctions.fileButtonChangeHide, undoRedoActionFunctions.fileButtonChangeUnHide); + ur.action("fileButtonChangeUnHide", undoRedoActionFunctions.fileButtonChangeUnHide, undoRedoActionFunctions.fileButtonChangeHide); + + //ur.action("expButtonUnhide", undoRedoActionFunctions.changeExpButton2, undoRedoActionFunctions.expButtonHide); + //ur.action("parseData", undoRedoActionFunctions.parseData, undoRedoActionFunctions.removeFile); + } + + function showTooltip(event) { + var node = event.target || event.cyTarget; + + + var canHaveTooltip = function( node ) { + return elementUtilities.isSIFNode(node) || node.data("tooltip") !==null; + } + + if (!canHaveTooltip(node)) { + return; + } + + var ref; // used only for positioning + var pos = event.position || event.cyPosition; + var pan = cy.pan(); + var zoom = cy.zoom(); + + var infobox = classes.AuxiliaryUnit.checkPoint(pos.x, pos.y, node, 0); + var tooltipContent; + + + if (!infobox) { + tooltipContent = node.data('tooltip'); + if ( tooltipContent == undefined || tooltipContent == '') { + return; + } + + ref = node.popperRef(); + } + else { + tooltipContent = infobox['tooltip']; + + if ( tooltipContent == undefined ) { + return; + } + + var modelPos = classes.AuxiliaryUnit.getAbsoluteCoord(infobox, cy); + var modelW = infobox.bbox.w; + var modelH = infobox.bbox.h; + var renderedW = modelW * zoom; + var renderedH = modelH * zoom; + modelPos.x -= modelW / 2; + modelPos.y -= modelH / 2; + var renderedPos = elementUtilities.convertToRenderedPosition(modelPos, pan, zoom); + + var renderedDims = { w: renderedW, h: renderedH }; + + ref = node.popperRef({ + renderedPosition: function() { + return renderedPos; + }, + renderedDimensions: function() { + return renderedDims; + } + }); + } + + + var placement = infobox ? infobox.anchorSide : 'bottom'; + var destroyTippy; + + var tippy = Tippy.one(ref, { + content: (() => { + var content = document.createElement('div'); + + content.style['font-size'] = 12 * zoom + 'px'; + content.innerHTML = tooltipContent; + + return content; + })(), + trigger: 'manual', + hideOnClick: true, + arrow: true, + placement, + onHidden: function() { + cy.off('pan zoom', destroyTippy); + node.off('position', destroyTippy); + cy.off('tapdrag', destroyTippy); + } + }); + + destroyTippy = function(){ + tippy.destroy(); + }; + + cy.on('pan zoom', destroyTippy); + node.on('position', destroyTippy); + cy.on('tapdrag', destroyTippy); + + setTimeout( () => tippy.show(),250 ); + } + + function bindCyEvents() { + + cy.on('tapdragover', 'node', function(event) { + var waitDuration = 200; + var nodeTapdragout; + var currEvent = event; + var node = currEvent.target || currEvent.cyTarget; + var inQueue = true; + + var clearNodeEvent = function() { + if ( nodeTapdragout ) { + node.off('tapdragout', nodeTapdragout); + } + + if ( nodeTapdrag ) { + node.off('tapdrag', nodeTapdrag); + } + }; + + var getShowTooltipAsycn = function() { + return setTimeout( function() { + showTooltip( currEvent ); + inQueue = false; + }, waitDuration ); + }; + + var showTooltipAsycn = getShowTooltipAsycn(); + + node.on('tapdragout', nodeTapdragout = function(e) { + clearTimeout( showTooltipAsycn ); + clearNodeEvent(); + }); + + node.on('tapdrag', nodeTapdrag = function(e) { + currEvent = e; + if (!inQueue) { + showTooltipAsycn = getShowTooltipAsycn(); + inQueue = true; + } + }); + }); + + cy.on('tapend', 'node', function (event) { + cy.style().update(); + }); + + cy.on("expandcollapse.beforecollapse", "node", function (event) { + var node = this; + //The children info of complex nodes should be shown when they are collapsed + if (node._private.data.class.startsWith("complex")) { + //The node is being collapsed store infolabel to use it later + var infoLabel = elementUtilities.getInfoLabel(node); + node._private.data.infoLabel = infoLabel; + } + }); + + cy.on("expandcollapse.aftercollapse", "node", function (event) { + var node = this; + // The width and height of just collapsed nodes should be 36, but they are supposed to be resizable. Therefore, we + // set their data('bbox') accordingly. We do not store their existing bbox.w and bbox.h because they have no significance for compounds (for now). + cy.startBatch(); + var bbox = node.data('bbox'); + bbox.w = 36; + bbox.h = 36; + node.data('bbox', bbox); + cy.endBatch(); + }); + + cy.on("expandcollapse.beforeexpand", "node", function (event) { + var node = this; + node.removeData("infoLabel"); + }); + + cy.on("expandcollapse.afterexpand", "node", function (event) { + var node = this; + cy.nodes().updateCompoundBounds(); + + if(!options.recalculateOnComplexityManagement){ + cy.style().update(); + } + //Don't show children info when the complex node is expanded + if (node._private.data.class.startsWith("complex")) { + node.removeStyle('content'); + } + }); + + cy.on("beforeDo", function (e, name, args) { + if(name == "layout" || name == "collapse" || name == "expand" || name == "collapseRecursively" || name == "expandRecursively" + || (name == "batch" && (args[0]['name'] == "thinBorder" || args[0]['name'] == "thickenBorder"))){ + var parents = cy.elements(":parent").jsons(); // parent nodes + var simples = cy.elements().not(":parent").jsons(); // simple nodes and edges + var allElements = parents.concat(simples); // all elements + args.allElements = allElements; + var ports = {}; + cy.nodes().forEach(function(node){ + if(elementUtilities.canHavePorts(node)){ + ports[node.id()] = JSON.parse(JSON.stringify(node.data("ports"))); + } + }); + args.ports = ports; + args.viewport = {pan: JSON.parse(JSON.stringify(cy.pan())), zoom: cy.zoom()}; + if(name == "layout") + mainUtilities.beforePerformLayout(); + } + }); + + cy.on("beforeRedo", function (e, name, args) { + if(name == "layout" || name == "collapse" || name == "expand" || name == "collapseRecursively" || name == "expandRecursively" + || (name == "batch" && (args[0]['name'] == "thinBorder" || args[0]['name'] == "thickenBorder"))){ + var parents = cy.elements(":parent").jsons(); // parent nodes + var simples = cy.elements().not(":parent").jsons(); // simple nodes and edges + var allElements = parents.concat(simples); // all elements + args.allElements2 = allElements; + var ports = {}; + cy.nodes().forEach(function(node){ + if(elementUtilities.canHavePorts(node)){ + ports[node.id()] = JSON.parse(JSON.stringify(node.data("ports"))); + } + }); + args.ports2 = ports; + args.viewport2 = {pan: JSON.parse(JSON.stringify(cy.pan())), zoom: cy.zoom()}; + } + }); + + cy.on("afterDo", function (e, name, args, res) { + if(name == "layout" || name == "collapse" || name == "expand" || name == "collapseRecursively" || name == "expandRecursively" + || (name == "batch" && (args[0]['name'] == "thinBorder" || args[0]['name'] == "thickenBorder"))){ + res.allElements = args.allElements; + res.ports = args.ports; + res.viewport = args.viewport; + } + }); + + cy.on("afterRedo", function (e, name, args, res) { + if(name == "layout" || name == "collapse" || name == "expand" || name == "collapseRecursively" || name == "expandRecursively" + || (name == "batch" && (args[0]['name'] == "thinBorder" || args[0]['name'] == "thickenBorder"))){ + res.allElements = args.allElements2; + res.ports = args.ports2; + res.viewport = args.viewport2; + cy.json({flatEles: true, elements: args.allElements}); + cy.batch(function(){ + cy.nodes().forEach(function(node){ + if(elementUtilities.canHavePorts(node)){ + node.data("ports", args.ports[node.id()]); + } + }); + }); + cy.pan(args.viewport["pan"]); + cy.zoom(args.viewport["zoom"]); + } + }); + + cy.on("beforeUndo", function (e, name, args) { + if(name == "layout" || name == "collapse" || name == "expand" || name == "collapseRecursively" || name == "expandRecursively" + || (name == "batch" && (args[0]['name'] == "thinBorder" || args[0]['name'] == "thickenBorder"))){ + var parents = cy.elements(":parent").jsons(); // parent nodes + var simples = cy.elements().not(":parent").jsons(); // simple nodes and edges + var allElements = parents.concat(simples); // all elements + args.allElements2 = allElements; + var ports = {}; + cy.nodes().forEach(function(node){ + if(elementUtilities.canHavePorts(node)){ + ports[node.id()] = JSON.parse(JSON.stringify(node.data("ports"))); + } + }); + args.ports2 = ports; + args.viewport2 = {pan: JSON.parse(JSON.stringify(cy.pan())), zoom: cy.zoom()}; + } + }); + + cy.on("afterUndo", function (e, name, args, res) { + if(name == "layout" || name == "collapse" || name == "expand" || name == "collapseRecursively" || name == "expandRecursively" + || (name == "batch" && (args[0]['name'] == "thinBorder" || args[0]['name'] == "thickenBorder"))){ + res.allElements = args.allElements2; + res.ports = args.ports2; + res.viewport = args.viewport2; + cy.json({flatEles: true, elements: args.allElements}); + cy.batch(function(){ + cy.nodes().forEach(function(node){ + if(elementUtilities.canHavePorts(node)){ + node.data("ports", args.ports[node.id()]); + } + }); + }); + cy.pan(args.viewport["pan"]); + cy.zoom(args.viewport["zoom"]); + } + }); + + cy.on('layoutstop', function (event) { + /* + * 'preset' layout is called to give the initial positions of nodes by sbgnviz. + * Seems like 'grid' layout is called by Cytoscape.js core in loading graphs. + * If the layout is not one of these (normally it is supposed to be 'cose-bilkent') + * and ports are enabled call 'elementUtilities.changePortsOrientationAfterLayout()' + */ + if (event.layout.options.name !== 'preset' && event.layout.options.name !== 'grid') + { + if (graphUtilities.portsEnabled === true) + { + elementUtilities.changePortsOrientationAfterLayout(); + } + } + }); + + $(document).on('updateGraphEnd', function(event, _cy, isLayoutRequired,callback) { + + // if the event is not triggered for this cy instance return directly + if ( _cy != cy ) { + return; + } + var setCompoundInfoboxes = function(node, isLayoutRequired,cyInstance){ + if(cyInstance == undefined ) return; + if(node.data().infoboxCalculated){ + return; + }else if(node.isParent()){ + node.children().forEach(function(childNode){ + setCompoundInfoboxes(childNode,isLayoutRequired,cyInstance); + }); + + } + + node.data("infoboxCalculated", true); + node.data('auxunitlayouts', {}); + // for each statesandinfos + + var correctInfoBoxCoord = true; + for(var i=0; i < node.data('statesandinfos').length; i++) { + var statesandinfos = node.data('statesandinfos')[i]; + var bbox = statesandinfos.bbox; + var infoBoxOnNode = classes.AuxiliaryUnit.setAnchorSide(statesandinfos, node); + correctInfoBoxCoord = correctInfoBoxCoord && infoBoxOnNode; + } + var statesToAdd = []; + for(var i=0; i < node.data('statesandinfos').length; i++) { + var statesandinfos = node.data('statesandinfos')[i]; + var bbox = statesandinfos.bbox; + + + if ((isLayoutRequired === undefined || !isLayoutRequired ) && correctInfoBoxCoord) { + classes.AuxiliaryUnit.setAnchorSide(statesandinfos, node); + //var fileLoadParam = {extraPadding: Number(node.data().originalPadding)}; + var cordResult = classes.AuxiliaryUnit.convertToRelativeCoord(statesandinfos, bbox.x+bbox.w/2, bbox.y+bbox.h/2, cyInstance, node) + statesandinfos.bbox.x = cordResult.x; + statesandinfos.bbox.y = cordResult.y; + statesandinfos.isDisplayed = true; + var location = statesandinfos.anchorSide; // top bottom right left + var layouts = node.data('auxunitlayouts'); + if(!layouts[location]) { // layout doesn't exist yet for this location + layouts[location] = classes.AuxUnitLayout.construct(node, location); + } + // populate the layout of this side + classes.AuxUnitLayout.addAuxUnit(layouts[location], cyInstance, statesandinfos, undefined, true); //positions are precomputed + } + else { + if(!node.data('auxunitlayouts')) { // ensure minimal initialization + node.data('auxunitlayouts', {}); + } + var location = classes.AuxUnitLayout.selectNextAvailable(node, cy); + if(!node.data('auxunitlayouts')[location]) { + node.data('auxunitlayouts')[location] = classes.AuxUnitLayout.construct(node, location); + } + var layout = node.data('auxunitlayouts')[location]; + statesandinfos.anchorSide = location; + switch(location) { + case "top": statesandinfos.bbox.y = 0; break; + case "bottom": statesandinfos.bbox.y = 100; break; + case "left": statesandinfos.bbox.x = 0; break; + case "right": statesandinfos.bbox.x = 100; break; + } + classes.AuxUnitLayout.addAuxUnit(layout, cyInstance, statesandinfos); + } + + } + + if (isLayoutRequired === true) { + var locations = classes.AuxUnitLayout.checkFit(node, cy); + if (locations !== undefined && locations.length > 0) { + classes.AuxUnitLayout.fitUnits(node, cy, locations); + } + } + + }; + // list all entitytypes andstore them in the global scratch + // only stateful EPN (complex, macromolecule or nucleic acid) are concerned + + // following is unapplied due to performance decreasing, adding something like 20% time on load + /*cy.startBatch(); + var entityHash = {}; + cy.nodes("[class='complex'], [class='macromolecule'], [class='nucleic acid feature']").forEach(function(node) { + // identify an entity by its label AND class + var label = node.data('label'); + var _class = node.data('class'); + var id=label+'-'+_class; + if(!entityHash.hasOwnProperty(id)) { // create entitytype if doesn't already exist + entityHash[id] = new classes.EntityType(id); + } + var currentEntityType = entityHash[id]; + currentEntityType.EPNs.push(node); // assigne the current element to its corresponding entitytype + + // collect all stateVariables of the current element, we need to assign StateVariableDefinitions to them + for(var i=0; i < node.data('statesandinfos').length; i++) { + var statesandinfos = node.data('statesandinfos')[i]; + if(statesandinfos instanceof classes.StateVariable) { // stateVariable found + var currentStateVariable = statesandinfos; + currentEntityType.assignStateVariable(currentStateVariable); + } + } + }); + cy.endBatch(); + cy.scratch('_sbgnviz', {SBGNEntityTypes: entityHash});*/ + + // assign statesandinfos to their layout + cy.style().update(); + // cy.startBatch(); + cy.nodes().forEach(function(node) { + setCompoundInfoboxes(node,isLayoutRequired,cy); + }); + + if(callback){ + callback(); + } + + //cy.endBatch(); + }); + } + + var selectionColor = '#d67614'; + var sbgnStyleSheet = cytoscape.stylesheet() + .selector("node") + .css({ + 'text-valign': 'center', + 'text-halign': 'center', + 'text-opacity': 1, + 'opacity': 1, + 'padding': 0 + }) + .selector("node[class]") + .css({ + 'shape': function (ele) { + return elementUtilities.getCyShape(ele); + }, + 'content': function (ele) { + return elementUtilities.getElementContent(ele); + }, + 'font-size': function (ele) { + // If node labels are expected to be adjusted automatically or element cannot have label + // or ele.data('font-size') is not defined return elementUtilities.getLabelTextSize() + // else return ele.data('font-size') + var opt = options.adjustNodeLabelFontSizeAutomatically; + var adjust = typeof opt === 'function' ? opt() : opt; + + if (!adjust && ele.data('font-size') != undefined) { + return ele.data('font-size'); + } + + return elementUtilities.getLabelTextSize(ele); + } + }) + .selector("node[class][font-family]") + .style({ + 'font-family': function( ele ) { + return ele.data('font-family'); + } + }) + .selector("node[class][font-style]") + .style({ + 'font-style': function( ele ) { + return ele.data('font-style') + } + }) + .selector("node[class][font-weight]") + .style({ + 'font-weight': function( ele ) { + return ele.data('font-weight'); + } + }) + .selector("node[class][color]") + .style({ + 'color': function( ele ) { + return ele.data('color'); + } + }) + .selector("node[class][background-color]") + .style({ + 'background-color': function( ele ) { + return ele.data('background-color'); + } + }) + .selector("node[class][background-opacity]") + .style({ + 'background-opacity': function( ele ) { + return ele.data('background-opacity'); + } + }) + .selector("node[class][border-width]") + .style({ + 'border-width': function( ele ) { + return ele.data('border-width'); + } + }) + .selector("node[class][border-color]") + .style({ + 'border-color': function( ele ) { + return ele.data('border-color'); + } + }) + .selector("node[class][text-wrap]") + .style({ + 'text-wrap': function (ele) { + var opt = options.fitLabelsToNodes; + var isFit = typeof opt === 'function' ? opt() : opt; + if (isFit) { + return 'ellipsis'; + } + return ele.data('text-wrap'); + } + }) + .selector("node") + .style({ + 'text-max-width': function (ele) { + var opt = options.fitLabelsToNodes; + var isFit = typeof opt === 'function' ? opt() : opt; + if (isFit) { + return ele.width(); + } + return '1000px'; + } + }) + .selector("edge[class][line-color]") + .style({ + 'line-color': function( ele ) { + return ele.data('line-color'); + }, + 'source-arrow-color': function( ele ) { + return ele.data('line-color'); + }, + 'target-arrow-color': function( ele ) { + return ele.data('line-color'); + } + }) + .selector("edge[class][width]") + .style({ + 'width': function( ele ) { + return ele.data('width'); + } + }) + .selector("node[class='association'],[class='dissociation'],[class='and'],[class='or'],[class='not'],[class='process'],[class='omitted process'],[class='uncertain process']") + .css({ + 'shape-polygon-points': function(ele) { + if (graphUtilities.portsEnabled === true && ele.data('ports').length === 2) { + // We assume that the ports of the edge are symetric according to the node center so just checking one port is enough for us + var port = ele.data('ports')[0]; + // If the ports are located above/below of the node then the orientation is 'vertical' else it is 'horizontal' + var orientation = port.x === 0 ? 'vertical' : 'horizontal'; + // The half width of the actual shape discluding the ports + var shapeHW = orientation === 'vertical' ? 50 / Math.abs(port.y) : 50 / Math.abs(port.x); + // Get the class of the node + var _class = ele.data('class'); + // If class is one of process, omitted process or uncertain process then the type of actual shape is 'rectangle' else it is 'circle' + var type = _class.endsWith('process') ? 'rectangle' : 'circle'; + + // Generate a polygon string with above parameters and return it + return generateShapeWithPortString(0.01, shapeHW, type, orientation); + } + + // This element is not expected to have a poygonial shape (Because it does not have 2 ports) just return a trivial string here not to have a run time bug + return '-1 -1 1 1 1 0'; + } + }) + .selector("node[class='perturbing agent']") + .css({ + 'shape-polygon-points': '-1, -1, -0.5, 0, -1, 1, 1, 1, 0.5, 0, 1, -1' + }) + .selector("node[class='tag']") + .css({ + 'shape-polygon-points': '-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1' + }) + .selector("node:parent[class^='complex']") // start with complex + .css({ + 'text-valign': 'bottom', + 'text-halign': 'center', + 'text-margin-y': elementUtilities.getComplexMargin, + 'padding': elementUtilities.getComplexPadding, + 'compound-sizing-wrt-labels' : 'exclude', + }) + .selector("node[class='compartment']") + .css({ + 'text-valign': 'bottom', + 'text-halign': 'center', + 'text-margin-y' : -1 * options.extraCompartmentPadding, + 'compound-sizing-wrt-labels' : 'exclude', + }) + .selector("node:parent[class='compartment']") + .css({ + 'padding': function() { + return graphUtilities.getCompoundPaddings() + options.extraCompartmentPadding; + } + }) + .selector("node[class='submap']") + .css({ + 'text-valign': 'bottom', + 'text-halign': 'center', + 'text-margin-y' : -1 * options.extraCompartmentPadding, + 'compound-sizing-wrt-labels' : 'exclude', + }) + .selector("node:parent[class='submap'],[class='topology group']") + .css({ + 'padding': function() { + return graphUtilities.getCompoundPaddings() + options.extraCompartmentPadding; + } + }) + .selector("node:childless[bbox]") + .css({ + 'width': 'data(bbox.w)', + 'height': 'data(bbox.h)' + }) + .selector("node:parent[minHeight]") + .css({ + 'min-height': function(ele) { + if (graphUtilities.compoundSizesConsidered) { + return ele.data('minHeight'); + } + + return 0; + } + }) + .selector("node:parent[minHeightBiasTop]") + .css({ + 'min-height-bias-top': function(ele) { + var min = parseFloat(ele.data('minHeightBiasTop')); + return (min >= 0 ? min : 100) + '%'; + } + }) + .selector("node:parent[minHeightBiasBottom]") + .css({ + 'min-height-bias-bottom': function(ele) { + var min = parseFloat(ele.data('minHeightBiasBottom')); + return (min >= 0 ? min : 100) + '%'; + } + }) + .selector("node:parent[minWidth]") + .css({ + 'min-width': function(ele) { + if (graphUtilities.compoundSizesConsidered) { + return ele.data('minWidth'); + } + + return 0; + } + }) + .selector("node:parent[minWidthBiasLeft]") + .css({ + 'min-width-bias-left': function(ele) { + var min = parseFloat(ele.data('minWidthBiasLeft')); + return (min >= 0 ? min : 100) + '%'; + } + }) + .selector("node:parent[minWidthBiasRight]") + .css({ + 'min-width-bias-right': function(ele) { + var min = parseFloat(ele.data('minWidthBiasRight')); + return (min >= 0 ? min : 100) + '%'; + } + }) + .selector("node.cy-expand-collapse-collapsed-node") + .css({ + 'border-style': 'dashed' + }) + .selector("node:selected") + .css({ + 'border-color': selectionColor, + 'target-arrow-color': '#000', + 'text-outline-color': '#000', + 'border-width': function(ele){ + return Math.max(parseFloat(ele.data('border-width')) + 2, 3); + } + }) + .selector("node:active") + .css({ + 'background-opacity': 0.7, 'overlay-color': selectionColor, + 'overlay-padding': '14' + }) + .selector("edge") + .css({ + 'curve-style': 'bezier', + 'target-arrow-fill': function(ele) { + return elementUtilities.getCyTargetArrowFill(ele); + }, + 'source-arrow-fill': 'hollow', + 'text-border-color': function (ele) { + if (ele.selected()) { + return selectionColor; + } + return ele.css('line-color'); + }, + 'color': function (ele) { + if (ele.selected()) { + return selectionColor; + } + return ele.css('line-color'); + }, + 'arrow-scale': 1.25 + }) + .selector("edge.cy-expand-collapse-meta-edge") + .css({ + 'line-color': '#C4C4C4', + 'source-arrow-color': '#C4C4C4', + 'target-arrow-color': '#C4C4C4' + }) + .selector("edge:selected") + .css({ + 'line-color': selectionColor, + 'source-arrow-color': selectionColor, + 'target-arrow-color': selectionColor, + 'width': function(ele){ + return Math.max(parseFloat(ele.data('width')) + 2, 3); + } + }) + .selector("edge:active") + .css({ + 'background-opacity': 0.7, 'overlay-color': selectionColor, + 'overlay-padding': '8' + }) + .selector("edge[cardinality > 0]") + .css({ + 'text-rotation': 'autorotate', + 'text-background-shape': 'rectangle', + 'text-border-opacity': '1', + 'text-border-width': '1', + 'text-background-color': 'white', + 'text-background-opacity': '1' + }) + .selector("edge[class='consumption'][cardinality > 0]") + .css({ + 'source-label': function (ele) { + return '' + ele.data('cardinality'); + }, + 'source-text-margin-y': '-10', + 'source-text-offset': function (ele) { + return elementUtilities.getCardinalityDistance(ele); + } + }) + .selector("edge[class='production'][cardinality > 0]") + .css({ + 'target-label': function (ele) { + return '' + ele.data('cardinality'); + }, + 'target-text-margin-y': '-10', + 'target-text-offset': function (ele) { + return elementUtilities.getCardinalityDistance(ele); + } + }) + .selector("edge[class]") + .css({ + 'target-arrow-shape': function (ele) { + return elementUtilities.getCyArrowShape(ele); + }, + 'source-arrow-shape': 'none', + 'source-endpoint': function(ele) { + return elementUtilities.getEndPoint(ele, 'source'); + }, + 'target-endpoint': function(ele) { + return elementUtilities.getEndPoint(ele, 'target'); + }, + 'line-style': function (ele) { + return elementUtilities.getArrayLineStyle(ele); + } + }) + .selector("core") + .css({ + 'selection-box-color': selectionColor, + 'selection-box-opacity': '0.2', 'selection-box-border-color': selectionColor + }); + + var sbgnNetworkContainer = $(options.networkContainerSelector); + + // create and init cytoscape: + cytoscape({ + container: sbgnNetworkContainer, + style: sbgnStyleSheet, + showOverlay: false, minZoom: 0.125, maxZoom: 16, + boxSelectionEnabled: true, + motionBlur: true, + wheelSensitivity: 0.1, + ready: function () { + cy = this; + // If undoable register undo/redo actions + if (options.undoable) { + registerUndoRedoActions(); + } + bindCyEvents(); + } + }); + }; + + sbgnCyInstance.getCy = function () { + return cy; + }; + + return sbgnCyInstance; +}; + +},{"../utilities/classes":193,"../utilities/lib-utilities":204}],191:[function(_dereq_,module,exports){ +/* + * Render sbgn specific shapes which are not supported by cytoscape.js core + */ + +var libs = _dereq_('../utilities/lib-utilities').getLibs(); +var jQuery = $ = libs.jQuery; +var cytoscape = libs.cytoscape; + +var cyMath = math = cytoscape.math; +var cyBaseNodeShapes = cytoscape.baseNodeShapes; +var cyStyleProperties = cytoscape.styleProperties; + +var classes = _dereq_('../utilities/classes'); + +module.exports = function () { + var $$ = cytoscape; + + /* + * Taken from cytoscape.js and modified so that it can be utilized from sbgnviz + * in a flexable way. It is needed because the sbgnviz shapes would need to stroke + * border more than once as they would have infoboxes, multimers etc. + * Extends the style properties of node with the given ones then strokes the border. + * Would needed to be slightly updated during cytoscape upgrades if related function in + * Cytoscape.js is updated. Information about where is the related function is located + * can be found in the file that list the changes done in ivis cytoscape fork. + */ + $$.sbgn.drawBorder = function({ context, node, borderWidth, borderColor, borderStyle, borderOpacity }) { + + borderWidth = borderWidth || ( node && parseFloat( node.css( 'border-width' ) ) ); + + if( borderWidth > 0 ){ + var parentOpacity = ( node && node.effectiveOpacity() ) || 1; + + borderStyle = borderStyle || ( node && node.css( 'border-style' ) ); + borderColor = borderColor || ( node && node.css( 'border-color' ) ); + borderOpacity = ( + borderOpacity || ( node && node.css( 'border-opacity' ) ) + ) * parentOpacity; + + var propsToRestore = [ 'lineWidth', 'lineCap', 'strokeStyle', 'globalAlpha' ]; + var initialProps = {}; + + propsToRestore.forEach( function( propName ) { + initialProps[ propName ] = context[ propName ]; + } ); + + context.lineWidth = borderWidth; + context.lineCap = 'butt'; + context.strokeStyle = borderColor; + context.globalAlpha = borderOpacity; + + if( context.setLineDash ){ // for very outofdate browsers + switch( borderStyle ){ + case 'dotted': + context.setLineDash( [ 1, 1 ] ); + break; + + case 'dashed': + context.setLineDash( [ 4, 2 ] ); + break; + + case 'solid': + case 'double': + context.setLineDash( [ ] ); + break; + } + } + + context.stroke(); + + if( borderStyle === 'double' ){ + context.lineWidth = borderWidth / 3; + + let gco = context.globalCompositeOperation; + context.globalCompositeOperation = 'destination-out'; + + context.stroke(); + + context.globalCompositeOperation = gco; + } + + // reset in case we changed the border style + if( context.setLineDash ){ // for very outofdate browsers + context.setLineDash( [ ] ); + } + + propsToRestore.forEach( function( propName ) { + context[ propName ] = initialProps[ propName ]; + } ); + } + }; + + // Taken from cytoscape.js and modified + var drawRoundRectanglePath = $$.sbgn.drawRoundRectanglePath = function( + context, x, y, width, height, radius ){ + + var halfWidth = width / 2; + var halfHeight = height / 2; + var cornerRadius = radius || cyMath.getRoundRectangleRadius( width, height ); + + if( context.beginPath ){ context.beginPath(); } + + // Start at top middle + context.moveTo( x, y - halfHeight ); + // Arc from middle top to right side + context.arcTo( x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius ); + // Arc from right side to bottom + context.arcTo( x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius ); + // Arc from bottom to left side + context.arcTo( x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius ); + // Arc from left side to topBorder + context.arcTo( x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius ); + // Join line + context.lineTo( x, y - halfHeight ); + + + context.closePath(); + }; + + // Taken from cytoscape.js + var drawPolygonPath = function( + context, x, y, width, height, points ){ + + var halfW = width / 2; + var halfH = height / 2; + + if( context.beginPath ){ context.beginPath(); } + + context.moveTo( x + halfW * points[0], y + halfH * points[1] ); + + for( var i = 1; i < points.length / 2; i++ ){ + context.lineTo( x + halfW * points[ i * 2], y + halfH * points[ i * 2 + 1] ); + } + + context.closePath(); + }; + + var sbgnShapes = $$.sbgn.sbgnShapes = { + 'source and sink': true, + 'nucleic acid feature': true, + 'complex': true, + 'macromolecule': true, + 'simple chemical': true, + 'biological activity': true, + 'compartment': true + }; + + var totallyOverridenNodeShapes = $$.sbgn.totallyOverridenNodeShapes = { + 'macromolecule': true, + 'nucleic acid feature': true, + 'simple chemical': true, + 'complex': true, + 'biological activity': true, + 'compartment': true + }; + + var canHaveInfoBoxShapes = $$.sbgn.canHaveInfoBoxShapes = { + 'simple chemical': true, + 'macromolecule': true, + 'nucleic acid feature': true, + 'complex': true, + 'biological activity': true, + 'compartment': true + }; + + var canBeMultimerShapes = $$.sbgn.canBeMultimerShapes = { + 'macromolecule': true, + 'complex': true, + 'nucleic acid feature': true, + 'simple chemical': true + }; + + cyMath.calculateDistance = function (point1, point2) { + var distance = Math.pow(point1[0] - point2[0], 2) + Math.pow(point1[1] - point2[1], 2); + return Math.sqrt(distance); + }; + + $$.sbgn.colors = { + clone: "#838383" + }; + + $$.sbgn.getDefaultComplexCornerLength = function() { + return 24; + }; + + $$.sbgn.drawStateAndInfos = function (node, context, centerX, centerY) { + var layouts = node.data('auxunitlayouts'); + + for (var side in layouts) { + var layout = layouts[side]; + classes.AuxUnitLayout.draw(layout, node.cy(), context); + } + context.beginPath(); + context.closePath(); + }; + + $$.sbgn.drawInfoBox = function(context, x, y, width, height, shapeName) { + switch (shapeName) { + case 'roundrectangle': + cyBaseNodeShapes['roundrectangle'].draw(context, x, y, width, height); + break; + case 'bottomroundrectangle': + $$.sbgn.drawBottomRoundRectangle(context, x, y, width, height); + break; + case 'ellipse': + cyBaseNodeShapes['ellipse'].draw(context, x, y, width, height); + break; + case 'complex': + $$.sbgn.drawComplex( context, x, y, width, height, height / 2 ); + break; + case 'perturbing agent': + var points = $$.sbgn.generatePerturbingAgentPoints(); + drawPolygonPath(context, x, y, width, height, points); + break; + case 'rectangle': + cyBaseNodeShapes['rectangle'].draw(context, x, y, width, height); + break; + case 'stadium': + $$.sbgn.drawRoundRectanglePath(context, x, y, width, height, Math.min(width / 2, height / 2, 15)); + break; + } + }; + + // $$.sbgn.AfShapeArgsFn = function (self){ + // return [self.bbox.w, self.bbox.h, classes.getAuxUnitClass(self).getParent(self).data("class")]; + // } + + + $$.sbgn.nucleicAcidCheckPoint = function (x, y, padding, width, height, centerX, centerY, points, cornerRadius) { + + //check rectangle at top + if (cyMath.pointInsidePolygon(x, y, points, + centerX, centerY - cornerRadius / 2, width, height - cornerRadius / 3, [0, -1], + padding)) { + return true; + } + + //check rectangle at bottom + if (cyMath.pointInsidePolygon(x, y, points, + centerX, centerY + height / 2 - cornerRadius / 2, width - 2 * cornerRadius, cornerRadius, [0, -1], + padding)) { + return true; + } + + //check ellipses + var checkInEllipse = function (x, y, centerX, centerY, width, height, padding) { + x -= centerX; + y -= centerY; + + x /= (width / 2 + padding); + y /= (height / 2 + padding); + + return (Math.pow(x, 2) + Math.pow(y, 2) <= 1); + } + + // Check bottom right quarter circle + if (checkInEllipse(x, y, + centerX + width / 2 - cornerRadius, + centerY + height / 2 - cornerRadius, + cornerRadius * 2, cornerRadius * 2, padding)) { + + return true; + } + + // Check bottom left quarter circle + if (checkInEllipse(x, y, + centerX - width / 2 + cornerRadius, + centerY + height / 2 - cornerRadius, + cornerRadius * 2, cornerRadius * 2, padding)) { + + return true; + } + + return false; + }; + + //we need to force opacity to 1 since we might have state and info boxes. + //having opaque nodes which have state and info boxes gives unpleasent results. + $$.sbgn.forceOpacityToOne = function (node, context) { + var parentOpacity = node.effectiveOpacity(); + if (parentOpacity === 0) { + return; + } + + context.fillStyle = "rgba(" + + node._private.style["background-color"].value[0] + "," + + node._private.style["background-color"].value[1] + "," + + node._private.style["background-color"].value[2] + "," + + (1 * node.css('opacity') * parentOpacity) + ")"; + }; + + $$.sbgn.drawSimpleChemicalPath = function ( + context, x, y, width, height) { + + var halfWidth = width / 2; + var halfHeight = height / 2; + //var cornerRadius = $$.math.getRoundRectangleRadius(width, height); + var cornerRadius = Math.min(halfWidth, halfHeight); + + context.beginPath(); + + // Start at top middle + context.moveTo(x, y-halfHeight); + // Arc from middle top to right side + context.arcTo(x+halfWidth, y-halfHeight, x+halfWidth, y, cornerRadius); + // Arc from right side to bottom + context.arcTo(x+halfWidth, y+halfHeight, x, y+halfHeight, cornerRadius); + // Arc from bottom to left side + context.arcTo(x-halfWidth, y+halfHeight, x-halfWidth, y, cornerRadius); + // Arc from left side to topBorder + context.arcTo(x-halfWidth, y-halfHeight, x, y-halfHeight, cornerRadius); + // Join line + context.lineTo(x, y-halfHeight); + + context.closePath(); + + }; + + $$.sbgn.drawSimpleChemical = function ( + context, x, y, width, height) { + $$.sbgn.drawSimpleChemicalPath(context, x, y, width, height); + context.fill(); + }; + + function simpleChemicalLeftClone(context, centerX, centerY, + width, height, cloneMarker, opacity) { + if (cloneMarker != null) { + var oldGlobalAlpha = context.globalAlpha; + context.globalAlpha = opacity; + var oldStyle = context.fillStyle; + context.fillStyle = $$.sbgn.colors.clone; + + context.beginPath(); + + var markerBeginX = centerX - width/2 * Math.sin(Math.PI / 3); + var markerBeginY = centerY + height/2 * Math.cos(Math.PI / 3); + var markerEndX = centerX; + var markerEndY = markerBeginY; + + context.moveTo(markerBeginX, markerBeginY); + context.lineTo(markerEndX, markerEndY); + context.arc(centerX, centerY, width/2, 3 * Math.PI / 6, 5 * Math.PI / 6); + + context.closePath(); + + context.fill(); + context.fillStyle = oldStyle; + context.globalAlpha = oldGlobalAlpha; + } + }; + + function simpleChemicalRightClone(context, centerX, centerY, + width, height, cloneMarker, opacity) { + if (cloneMarker != null) { + var oldGlobalAlpha = context.globalAlpha; + context.globalAlpha = opacity; + var oldStyle = context.fillStyle; + context.fillStyle = $$.sbgn.colors.clone; + + context.beginPath(); + + var markerBeginX = centerX; + var markerBeginY = centerY + height/2 * Math.cos(Math.PI / 3); + var markerEndX = centerX + width/2 * Math.sin(Math.PI / 3); + var markerEndY = markerBeginY; + + context.moveTo(markerBeginX, markerBeginY); + context.lineTo(markerEndX, markerEndY); + context.arc(centerX, centerY, width/2, Math.PI / 6, 3 * Math.PI / 6); + + context.closePath(); + + context.fill(); + context.fillStyle = oldStyle; + context.globalAlpha = oldGlobalAlpha; + } + }; + + $$.sbgn.drawEllipsePath = function (context, x, y, width, height) { + cyBaseNodeShapes['ellipse'].drawPath(context, x, y, width, height); + }; + + $$.sbgn.drawBarrel = function (context, x, y, width, height) { + cyBaseNodeShapes['barrel'].draw(context, x, y, width, height); + context.fill(); + }; + + $$.sbgn.drawBottomRoundRectangle = function (context, x, y, width, height) { + cyBaseNodeShapes['bottomroundrectangle'].draw(context, x, y, width, height); + context.fill(); + }; + + // The old draw implementation for nucleic acid feature + // now only used for clone marker drawing of nucleic acid feature + // and macromolecule shapes because 'bottomroundrectangle' function + // of cytoscape.js did not fit well for this purpose. + // Did not change the name yet directly as drawNucAcidFeatureClone etc. + // because it actually draws a nucleic acid feature in a different way. + $$.sbgn.drawNucAcidFeature2 = function (context, centerX, centerY, + width, height, cornerRadius) { + cornerRadius = cornerRadius || cyMath.getRoundRectangleRadius(width, height); + var halfWidth = width / 2; + var halfHeight = height / 2; + var left = centerX - halfWidth, right = centerX + halfWidth; + var bottom = centerY - halfHeight, top = centerY + halfHeight; + context.beginPath(); + + context.moveTo(left, bottom); + context.lineTo(right, bottom); + context.lineTo(right, centerY); + context.arcTo(right, top, centerX, top, cornerRadius); + context.arcTo(left, top, left, centerY, cornerRadius); + context.lineTo(left, bottom); + + context.closePath(); + context.fill(); + }; + + /* + * Code taken from https://jsperf.com/string-prototype-endswith + * Direct implementation seems to work better. + * Using this improves isMultimer() performance. + * Makes it take 0.1 or 0.2% less time from the whole + * loading process, down from ~0.4% initially. + */ + function endsWith(str, pattern) { + for (var i = pattern.length, l = str.length; i--;) { + if (str.charAt(--l) != pattern.charAt(i)) { + return false; + } + } + return true; + } + + $$.sbgn.isMultimer = function (node) { + var sbgnClass = node._private.data.class; + if (sbgnClass && endsWith(sbgnClass, "multimer")) + return true; + return false; + }; + + //this function is created to have same corner length when + //complex's width or height is changed + $$.sbgn.generateComplexShapePoints = function (cornerLength, width, height) { + //cp stands for corner proportion + var cpX = Math.min(cornerLength, 0.5 * width) / width; + var cpY = Math.min(cornerLength, 0.5 * height) / height; + + var complexPoints = [-1 + cpX, -1, -1, -1 + cpY, -1, 1 - cpY, -1 + cpX, + 1, 1 - cpX, 1, 1, 1 - cpY, 1, -1 + cpY, 1 - cpX, -1]; + + return complexPoints; + }; + + $$.sbgn.generatePerturbingAgentPoints = function() { + return [-1, -1, -0.5, 0, -1, 1, 1, 1, 0.5, 0, 1, -1]; + }; + + $$.sbgn.getDefaultMultimerPadding = function() { + return 5; + }; + + // draw background image of nodes + $$.sbgn.drawImage = function( context, imgObj ) { + if(imgObj){ + context.clip(); + context.drawImage(imgObj.img, 0, 0, imgObj.imgW, imgObj.imgH, imgObj.x, imgObj.y, imgObj.w, imgObj.h ); + context.restore(); + } + }; + + cyStyleProperties.types.nodeShape.enums.push( + 'source and sink', 'nucleic acid feature', 'complex', 'macromolecule', + 'simple chemical', 'biological activity', 'compartment' + ); + + $$.sbgn.registerSbgnNodeShapes = function () { + + function generateDrawFcn( { plainDrawFcn, extraDrawFcn, canBeMultimer, cloneMarkerFcn, + canHaveInfoBox, multimerPadding } ) { + + return function( context, node, imgObj ) { + + var borderWidth = parseFloat(node.css('border-width')); + var width = node.outerWidth() - borderWidth; + var height = node.outerHeight() - borderWidth; + var centerX = node._private.position.x; + var centerY = node._private.position.y; + var bgOpacity = node.css('background-opacity'); + var isCloned = cloneMarkerFcn != null && node._private.data.clonemarker; + + if ( canBeMultimer && $$.sbgn.isMultimer( node ) ) { + //add multimer shape + plainDrawFcn( context, centerX + multimerPadding, + centerY + multimerPadding, width, height ); + + $$.sbgn.drawBorder( { context, node } ); + + if ( extraDrawFcn ) { + extraDrawFcn( context, centerX + multimerPadding, + centerY + multimerPadding, width, height ); + + + $$.sbgn.drawBorder( { context, node } ); + } + + if ( isCloned ) { + cloneMarkerFcn(context, + centerX + multimerPadding, centerY + multimerPadding, + width - borderWidth, height - borderWidth, isCloned, true, bgOpacity); + } + } + + plainDrawFcn( context, centerX, centerY, width, height ); + + $$.sbgn.drawBorder( { context, node } ); + $$.sbgn.drawImage( context, imgObj ); + + if ( extraDrawFcn ) { + extraDrawFcn( context, centerX, centerY, width, height ); + + $$.sbgn.drawBorder( { context, node } ); + } + + if ( isCloned ) { + cloneMarkerFcn(context, centerX, centerY, width - borderWidth, + height - borderWidth, isCloned, false, bgOpacity); + } + + if ( canHaveInfoBox ) { + var oldStyle = context.fillStyle; + $$.sbgn.forceOpacityToOne(node, context); + $$.sbgn.drawStateAndInfos(node, context, centerX, centerY); + context.fillStyle = oldStyle; + } + }; + } + + function generateIntersectLineFcn( { plainIntersectLineFcn, canBeMultimer, cloneMarkerFcn, + canHaveInfoBox, multimerPadding } ) { + + return function( node, x, y ) { + var borderWidth = parseFloat(node.css('border-width')); + var padding = borderWidth / 2; + var width = node.outerWidth() - borderWidth; + var height = node.outerHeight() - borderWidth; + var centerX = node._private.position.x; + var centerY = node._private.position.y; + + var intersections = []; + + if ( canHaveInfoBox ) { + var stateAndInfoIntersectLines = $$.sbgn.intersectLineStateAndInfoBoxes( + node, x, y); + + intersections = intersections.concat( stateAndInfoIntersectLines ); + } + + var nodeIntersectLines = plainIntersectLineFcn(centerX, centerY, width, + height, x, y, padding); + + intersections = intersections.concat( nodeIntersectLines ); + + if ( canBeMultimer && $$.sbgn.isMultimer(node) ) { + var multimerIntersectionLines = plainIntersectLineFcn( + centerX + multimerPadding, centerY + multimerPadding, width, + height, x, y, padding); + + intersections = intersections.concat( multimerIntersectionLines ); + } + + return $$.sbgn.closestIntersectionPoint([x, y], intersections); + }; + } + + function generateCheckPointFcn( { plainCheckPointFcn, canBeMultimer, cloneMarkerFcn, + canHaveInfoBox, multimerPadding } ) { + + return function( x, y, node, threshold ) { + + threshold = threshold || 0; + var borderWidth = parseFloat(node.css('border-width')); + var width = node.outerWidth() - borderWidth + 2 * threshold; + var height = node.outerHeight() - borderWidth + 2 * threshold; + var centerX = node._private.position.x; + var centerY = node._private.position.y; + var padding = borderWidth / 2; + + var nodeCheck = function() { + return plainCheckPointFcn( x, y, padding, width, height, centerX, centerY ); + }; + + var stateAndInfoCheck = function() { + return canHaveInfoBox && $$.sbgn.checkPointStateAndInfoBoxes(x, y, node, threshold); + }; + + var multimerCheck = function() { + return canBeMultimer && $$.sbgn.isMultimer(node) + && plainCheckPointFcn( x, y, padding, width, height, + centerX + multimerPadding, + centerY + multimerPadding ); + }; + + return nodeCheck() || stateAndInfoCheck() || multimerCheck(); + }; + } + + var shapeNames = [ "simple chemical", "macromolecule", "complex", + "nucleic acid feature", "source and sink", "biological activity", + "compartment", "oldCompartment" + ]; + + shapeNames.forEach( function( shapeName ) { + var plainDrawFcn = $$.sbgn.plainDraw[ shapeName ]; + var plainIntersectLineFcn = $$.sbgn.plainIntersectLine[ shapeName ]; + var plainCheckPointFcn = $$.sbgn.plainCheckPoint[ shapeName ]; + var canBeMultimer = $$.sbgn.canBeMultimerShapes[ shapeName ]; + var cloneMarkerFcn = $$.sbgn.cloneMarker[ shapeName ]; + var canHaveInfoBox = $$.sbgn.canHaveInfoBoxShapes[ shapeName ]; + var multimerPadding = $$.sbgn.getDefaultMultimerPadding(); + var extraDrawFcn = $$.sbgn.extraDraw[ shapeName ]; + + var draw = generateDrawFcn( { plainDrawFcn, canBeMultimer, cloneMarkerFcn, + canHaveInfoBox, multimerPadding, extraDrawFcn + } ); + + var intersectLine = totallyOverridenNodeShapes[ shapeName ] ? + generateIntersectLineFcn( { plainIntersectLineFcn, canBeMultimer, cloneMarkerFcn, + canHaveInfoBox, multimerPadding + } ) : plainIntersectLineFcn; + + var checkPoint = totallyOverridenNodeShapes[ shapeName ] ? + generateCheckPointFcn( { plainCheckPointFcn, canBeMultimer, cloneMarkerFcn, + canHaveInfoBox, multimerPadding + } ) : plainCheckPointFcn; + + var shape = { draw, intersectLine, checkPoint, multimerPadding }; + + cyBaseNodeShapes[ shapeName ] = shape; + } ); + }; + + $$.sbgn.drawEllipse = function (context, x, y, width, height) { + //$$.sbgn.drawEllipsePath(context, x, y, width, height); + //context.fill(); + cyBaseNodeShapes['ellipse'].draw(context, x, y, width, height); + }; + + $$.sbgn.drawComplex = function( context, x, y, width, height, cornerLength ) { + cornerLength = cornerLength || $$.sbgn.getDefaultComplexCornerLength(); + var points = $$.sbgn.generateComplexShapePoints(cornerLength, width, height); + + drawPolygonPath(context, x, y, width, height, points); + + context.fill(); + }; + + $$.sbgn.drawCrossLine = function( context, x, y, width, height ) { + var points = cyMath.generateUnitNgonPoints(4, 0); + + context.beginPath(); + var scaleX = width * Math.sqrt(2) / 2, scaleY = height * Math.sqrt(2) / 2; + + context.moveTo(x + scaleX * points[2], y + scaleY * points[3]); + context.lineTo(x + scaleX * points[6], y + scaleY * points[7]); + context.closePath(); + }; + + $$.sbgn.drawBiologicalActivity = function( context, x, y, width, height ) { + var points = $$.sbgn.generateBiologicalActivityPoints(); + drawPolygonPath(context, + x, y, width, height, points); + context.fill(); + }; + + $$.sbgn.drawRoundRectangle = function( context, x, y, width, height ) { + drawRoundRectanglePath( context, x, y, width, height ); + context.fill(); + }; + + $$.sbgn.generateNucleicAcidPoints = function() { + return cyMath.generateUnitNgonPointsFitToSquare(4, 0); + }; + + $$.sbgn.generateBiologicalActivityPoints = function() { + return cyMath.generateUnitNgonPointsFitToSquare(4, 0); + }; + + $$.sbgn.generateCompartmentPoints = function() { + return math.generateUnitNgonPointsFitToSquare(4, 0); + }; + + $$.sbgn.plainDraw = { + "simple chemical": $$.sbgn.drawSimpleChemical, + "macromolecule": $$.sbgn.drawRoundRectangle, + "complex": $$.sbgn.drawComplex, + "nucleic acid feature": $$.sbgn.drawBottomRoundRectangle, + "source and sink": $$.sbgn.drawEllipse, + "biological activity": $$.sbgn.drawBiologicalActivity, + "compartment": $$.sbgn.drawBarrel, + "oldCompartment": $$.sbgn.drawRoundRectangle + }; + + // To define an extra drawing for the node that is rendered at the very end, + // even after the node background image is drawn. + // E.g. cross lines of "source and sink" nodes. + $$.sbgn.extraDraw = { + "source and sink": $$.sbgn.drawCrossLine + }; + + $$.sbgn.plainIntersectLine = { + "simple chemical": function( centerX, centerY, width, height, x, y, padding ) { + return cyBaseNodeShapes["ellipse"].intersectLine( centerX, centerY, width, height, x, y, padding ); + }, + "macromolecule": function( centerX, centerY, width, height, x, y, padding ) { + return $$.sbgn.roundRectangleIntersectLine( x, y, centerX, centerY, centerX, centerY, + width, height, + cyMath.getRoundRectangleRadius(width, height), padding + ); + }, + "complex": function( centerX, centerY, width, height, x, y, padding ) { + var points = $$.sbgn.generateComplexShapePoints( $$.sbgn.getDefaultComplexCornerLength(), width, height ); + return cyMath.polygonIntersectLine( + x, y, points, centerX, centerY, width / 2, height / 2, padding + ); + }, + "nucleic acid feature": function( centerX, centerY, width, height, x, y, padding ) { + return cyBaseNodeShapes["bottomroundrectangle"].intersectLine( centerX, centerY, width, height, x, y, padding ); + }, + "source and sink": function( centerX, centerY, width, height, x, y, padding ) { + return cyBaseNodeShapes["ellipse"].intersectLine( centerX, centerY, width, height, x, y, padding ); + }, + "biological activity": function( centerX, centerY, width, height, x, y, padding ) { + var points = $$.sbgn.generateBiologicalActivityPoints(); + return cyMath.polygonIntersectLine( + x, y, points, centerX, centerY, width / 2, height / 2, padding + ); + }, + "compartment": function( centerX, centerY, width, height, x, y, padding ) { + return cyBaseNodeShapes["barrel"].intersectLine( centerX, centerY, width, height, x, y, padding ); + }, + "oldCompartment": function( centerX, centerY, width, height, x, y, padding ) { + return cyMath.roundRectangleIntersectLine( + x, y, centerX, centerY, width, height, padding + ); + } + }; + + $$.sbgn.plainCheckPoint = { + "simple chemical": function( x, y, padding, width, height, centerX, centerY ) { + + var points = cyMath.generateUnitNgonPointsFitToSquare( 4, 0 ); + var halfWidth = width / 2; + var halfHeight = height / 2; + //var cornerRadius = $$.math.getRoundRectangleRadius(width, height); + var cornerRadius = Math.min(halfWidth, halfHeight); + //var cornerRadius = math.getRoundRectangleRadius( width, height ); + var diam = cornerRadius * 2; + + // Check hBox + if( cyMath.pointInsidePolygon( x, y, points, + centerX, centerY, width, height - diam, [0, -1], padding ) ){ + return true; + } + + // Check vBox + if( cyMath.pointInsidePolygon( x, y, points, + centerX, centerY, width - diam, height, [0, -1], padding ) ){ + return true; + } + + // Check top left quarter circle + if( cyMath.checkInEllipse( x, y, + diam, diam, + centerX - width / 2 + cornerRadius, + centerY - height / 2 + cornerRadius, + padding ) ){ + + return true; + } + + // Check top right quarter circle + if( cyMath.checkInEllipse( x, y, + diam, diam, + centerX + width / 2 - cornerRadius, + centerY - height / 2 + cornerRadius, + padding ) ){ + + return true; + } + + // Check bottom right quarter circle + if( cyMath.checkInEllipse( x, y, + diam, diam, + centerX + width / 2 - cornerRadius, + centerY + height / 2 - cornerRadius, + padding ) ){ + + return true; + } + + // Check bottom left quarter circle + if( cyMath.checkInEllipse( x, y, + diam, diam, + centerX - width / 2 + cornerRadius, + centerY + height / 2 - cornerRadius, + padding ) ){ + + return true; + } + return false; + //return cyBaseNodeShapes["ellipse"].checkPoint( x, y, padding, width, height, centerX, centerY ); + }, + "macromolecule": function( x, y, padding, width, height, centerX, centerY ) { + return cyBaseNodeShapes["roundrectangle"].checkPoint( x, y, padding, width, height, centerX, centerY ); + }, + "complex": function( x, y, padding, width, height, centerX, centerY ) { + var points = $$.sbgn.generateComplexShapePoints( $$.sbgn.getDefaultComplexCornerLength(), width, height ); + return cyMath.pointInsidePolygon( + x, y, points, centerX, centerY, width, height, [0, -1], padding); + }, + "nucleic acid feature": function( x, y, padding, width, height, centerX, centerY ) { + return cyBaseNodeShapes["bottomroundrectangle"].checkPoint( x, y, padding, width, height, centerX, centerY ); + }, + "source and sink": function( x, y, padding, width, height, centerX, centerY ) { + return cyBaseNodeShapes["ellipse"].checkPoint( x, y, padding, width, height, centerX, centerY ); + }, + "biological activity": function( x, y, padding, width, height, centerX, centerY ) { + return cyBaseNodeShapes["rectangle"].checkPoint( x, y, padding, width, height, centerX, centerY ); + }, + "compartment": function( x, y, padding, width, height, centerX, centerY ) { + return cyBaseNodeShapes["barrel"].checkPoint( x, y, padding, width, height, centerX, centerY ); + }, + "oldCompartment": function( x, y, padding, width, height, centerX, centerY ) { + return cyBaseNodeShapes["roundrectangle"].checkPoint( x, y, padding, width, height, centerX, centerY ); + } + }; + + $$.sbgn.cloneMarker = { + "simple chemical": function (context, centerX, centerY, + width, height, cloneMarker, isMultimer, opacity) { + if (cloneMarker != null) { + var cornerRadius = Math.min(width / 2, height / 2); + + var firstCircleCenterX = centerX - width / 2 + cornerRadius; + var firstCircleCenterY = centerY; + var secondCircleCenterX = centerX + width / 2 - cornerRadius; + var secondCircleCenterY = centerY; + 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; + var oldGlobalAlpha = context.globalAlpha; + context.globalAlpha = opacity; + + var recPoints = cyMath.generateUnitNgonPointsFitToSquare(4, 0); + var cloneX = centerX; + var cloneY = centerY + 3 / 4 * cornerRadius; + var cloneWidth = width - 2 * cornerRadius; + var cloneHeight = cornerRadius / 2; + + drawPolygonPath(context, cloneX, cloneY, cloneWidth, cloneHeight, recPoints); + context.fill(); + context.fillStyle = oldStyle; + context.globalAlpha = oldGlobalAlpha; + } + }, + "nucleic acid feature": function (context, centerX, centerY, + width, height, cloneMarker, isMultimer, opacity) { + if (cloneMarker != null) { + var cloneWidth = width; + var cloneHeight = height / 4; + var cloneX = centerX; + var cloneY = centerY + 3 * height / 8; + + var oldStyle = context.fillStyle; + context.fillStyle = $$.sbgn.colors.clone; + var oldGlobalAlpha = context.globalAlpha; + context.globalAlpha = opacity; + + var cornerRadius = cyMath.getRoundRectangleRadius(width, height); + + $$.sbgn.drawNucAcidFeature2(context, cloneX, cloneY, + cloneWidth, cloneHeight, cornerRadius); + + context.fillStyle = oldStyle; + context.globalAlpha = oldGlobalAlpha; + } + }, + "macromolecule": function (context, centerX, centerY, + width, height, cloneMarker, isMultimer, opacity) { + $$.sbgn.cloneMarker["nucleic acid feature"](context, centerX, centerY, + width, height, cloneMarker, isMultimer, opacity); + }, + "complex": function (context, centerX, centerY, + width, height, cloneMarker, isMultimer, opacity) { + if (cloneMarker != null) { + var cornerLength = $$.sbgn.getDefaultComplexCornerLength(); + 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; + var cloneY = centerY + height / 2 - cloneHeight / 2; + + var markerPoints = [-1, -1, 1, -1, 1 - cpX, 1, -1 + cpX, 1]; + + var oldStyle = context.fillStyle; + context.fillStyle = $$.sbgn.colors.clone; + var oldGlobalAlpha = context.globalAlpha; + context.globalAlpha = opacity; + + drawPolygonPath(context, + cloneX, cloneY, + cloneWidth, cloneHeight, markerPoints); + context.fill(); + + context.fillStyle = oldStyle; + context.globalAlpha = oldGlobalAlpha; + + } + } + }; + + $$.sbgn.closestIntersectionPoint = function (point, intersections) { + if (intersections.length <= 0) + return []; + + var closestIntersection = []; + var minDistance = Number.MAX_VALUE; + + for (var i = 0; i < intersections.length; i = i + 2) { + var checkPoint = [intersections[i], intersections[i + 1]]; + var distance = cyMath.calculateDistance(point, checkPoint); + + if (distance < minDistance) { + minDistance = distance; + closestIntersection = checkPoint; + } + } + + return closestIntersection; + }; + + $$.sbgn.nucleicAcidIntersectionLine = function (x, y, nodeX, nodeY, width, height, cornerRadius, padding) { + // var nodeX = node._private.position.x; + // var nodeY = node._private.position.y; + // var width = node.width(); + // var height = node.height(); + // var padding = parseInt(node.css('border-width')) / 2; + + var halfWidth = width / 2; + var halfHeight = height / 2; + + var straightLineIntersections; + + // Top segment, left to right + { + var topStartX = nodeX - halfWidth - padding; + var topStartY = nodeY - halfHeight - padding; + var topEndX = nodeX + halfWidth + padding; + var topEndY = topStartY; + + straightLineIntersections = cyMath.finiteLinesIntersect( + x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false); + + if (straightLineIntersections.length > 0) { + return straightLineIntersections; + } + } + + // Right segment, top to bottom + { + var rightStartX = nodeX + halfWidth + padding; + var rightStartY = nodeY - halfHeight - padding; + var rightEndX = rightStartX; + var rightEndY = nodeY + halfHeight - cornerRadius + padding; + + straightLineIntersections = cyMath.finiteLinesIntersect( + x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false); + + if (straightLineIntersections.length > 0) { + return straightLineIntersections; + } + } + + // Bottom segment, left to right + { + var bottomStartX = nodeX - halfWidth + cornerRadius - padding; + var bottomStartY = nodeY + halfHeight + padding; + var bottomEndX = nodeX + halfWidth - cornerRadius + padding; + var bottomEndY = bottomStartY; + + straightLineIntersections = cyMath.finiteLinesIntersect( + x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false); + + if (straightLineIntersections.length > 0) { + return straightLineIntersections; + } + } + + // Left segment, top to bottom + { + var leftStartX = nodeX - halfWidth - padding; + var leftStartY = nodeY - halfHeight - padding; + var leftEndX = leftStartX; + var leftEndY = nodeY + halfHeight - cornerRadius + padding; + + straightLineIntersections = cyMath.finiteLinesIntersect( + x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false); + + if (straightLineIntersections.length > 0) { + return straightLineIntersections; + } + } + + // Check intersections with arc segments, we have only two arcs for + //nucleic acid features + var arcIntersections; + + // Bottom Right + { + var bottomRightCenterX = nodeX + halfWidth - cornerRadius; + var bottomRightCenterY = nodeY + halfHeight - cornerRadius + arcIntersections = cyMath.intersectLineCircle( + x, y, nodeX, nodeY, + bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); + + // Ensure the intersection is on the desired quarter of the circle + if (arcIntersections.length > 0 + && arcIntersections[0] >= bottomRightCenterX + && arcIntersections[1] >= bottomRightCenterY) { + return [arcIntersections[0], arcIntersections[1]]; + } + } + + // Bottom Left + { + var bottomLeftCenterX = nodeX - halfWidth + cornerRadius; + var bottomLeftCenterY = nodeY + halfHeight - cornerRadius + arcIntersections = cyMath.intersectLineCircle( + x, y, nodeX, nodeY, + bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); + + // Ensure the intersection is on the desired quarter of the circle + if (arcIntersections.length > 0 + && arcIntersections[0] <= bottomLeftCenterX + && arcIntersections[1] >= bottomLeftCenterY) { + return [arcIntersections[0], arcIntersections[1]]; + } + } + return []; // if nothing + }; + + //this function gives the intersections of any line with the upper half of perturbing agent + $$.sbgn.perturbingAgentIntersectLine = function ( + x1, y1, x2, y2, nodeX, nodeY, width, height, padding) { + + var halfWidth = width / 2; + var halfHeight = height / 2; + + // Check intersections with straight line segments + var straightLineIntersections = []; + + // Top segment, left to right + { + var topStartX = nodeX - halfWidth - padding; + var topStartY = nodeY - halfHeight - padding; + var topEndX = nodeX + halfWidth + padding; + var topEndY = topStartY; + + var intersection = cyMath.finiteLinesIntersect( + x1, y1, x2, y2, topStartX, topStartY, topEndX, topEndY, false); + + if (intersection.length > 0) { + straightLineIntersections = straightLineIntersections.concat(intersection); + } + } + + // Right segment, top to bottom + { + var rightStartX = nodeX + halfWidth + padding; + var rightStartY = nodeY - halfHeight - padding; + var rightEndX = rightStartX - halfWidth/2; + var rightEndY = nodeY + padding; + + var intersection = cyMath.finiteLinesIntersect( + x1, y1, x2, y2, rightStartX, rightStartY, rightEndX, rightEndY, false); + + if (intersection.length > 0) { + straightLineIntersections = straightLineIntersections.concat(intersection); + } + } + + // Left segment, top to bottom + { + var leftStartX = nodeX - halfWidth - padding; + var leftStartY = nodeY - halfHeight - padding; + var leftEndX = leftStartX + halfWidth/2; + var leftEndY = nodeY + padding; + + var intersection = cyMath.finiteLinesIntersect( + x1, y1, x2, y2, leftStartX, leftStartY, leftEndX, leftEndY, false); + + if (intersection.length > 0) { + straightLineIntersections = straightLineIntersections.concat(intersection); + } + } + + return straightLineIntersections; + }; + + //this function gives the intersections of any line with a round rectangle + $$.sbgn.roundRectangleIntersectLine = function ( + x1, y1, x2, y2, nodeX, nodeY, width, height, cornerRadius, padding) { + + var halfWidth = width / 2; + var halfHeight = height / 2; + + // Check intersections with straight line segments + var straightLineIntersections = []; + // Top segment, left to right + { + var topStartX = nodeX - halfWidth + cornerRadius - padding; + var topStartY = nodeY - halfHeight - padding; + var topEndX = nodeX + halfWidth - cornerRadius + padding; + var topEndY = topStartY; + + var intersection = cyMath.finiteLinesIntersect( + x1, y1, x2, y2, topStartX, topStartY, topEndX, topEndY, false); + + if (intersection.length > 0) { + straightLineIntersections = straightLineIntersections.concat(intersection); + } + } + + // Right segment, top to bottom + { + var rightStartX = nodeX + halfWidth + padding; + var rightStartY = nodeY - halfHeight + cornerRadius - padding; + var rightEndX = rightStartX; + var rightEndY = nodeY + halfHeight - cornerRadius + padding; + + var intersection = cyMath.finiteLinesIntersect( + x1, y1, x2, y2, rightStartX, rightStartY, rightEndX, rightEndY, false); + + if (intersection.length > 0) { + straightLineIntersections = straightLineIntersections.concat(intersection); + } + } + + // Bottom segment, left to right + { + var bottomStartX = nodeX - halfWidth + cornerRadius - padding; + var bottomStartY = nodeY + halfHeight + padding; + var bottomEndX = nodeX + halfWidth - cornerRadius + padding; + var bottomEndY = bottomStartY; + + var intersection = cyMath.finiteLinesIntersect( + x1, y1, x2, y2, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false); + + if (intersection.length > 0) { + straightLineIntersections = straightLineIntersections.concat(intersection); + } + } + + // Left segment, top to bottom + { + var leftStartX = nodeX - halfWidth - padding; + var leftStartY = nodeY - halfHeight + cornerRadius - padding; + var leftEndX = leftStartX; + var leftEndY = nodeY + halfHeight - cornerRadius + padding; + + var intersection = cyMath.finiteLinesIntersect( + x1, y1, x2, y2, leftStartX, leftStartY, leftEndX, leftEndY, false); + + if (intersection.length > 0) { + straightLineIntersections = straightLineIntersections.concat(intersection); + } + } + + // Check intersections with arc segments + var arcIntersections; + + // Top Left + { + var topLeftCenterX = nodeX - halfWidth + cornerRadius; + var topLeftCenterY = nodeY - halfHeight + cornerRadius + arcIntersections = cyMath.intersectLineCircle( + x1, y1, x2, y2, + topLeftCenterX, topLeftCenterY, cornerRadius + padding); + + // Ensure the intersection is on the desired quarter of the circle + if (arcIntersections.length > 0 + && arcIntersections[0] <= topLeftCenterX + && arcIntersections[1] <= topLeftCenterY) { + straightLineIntersections = straightLineIntersections.concat(arcIntersections); + } + } + + // Top Right + { + var topRightCenterX = nodeX + halfWidth - cornerRadius; + var topRightCenterY = nodeY - halfHeight + cornerRadius + arcIntersections = cyMath.intersectLineCircle( + x1, y1, x2, y2, + topRightCenterX, topRightCenterY, cornerRadius + padding); + + // Ensure the intersection is on the desired quarter of the circle + if (arcIntersections.length > 0 + && arcIntersections[0] >= topRightCenterX + && arcIntersections[1] <= topRightCenterY) { + straightLineIntersections = straightLineIntersections.concat(arcIntersections); + } + } + + // Bottom Right + { + var bottomRightCenterX = nodeX + halfWidth - cornerRadius; + var bottomRightCenterY = nodeY + halfHeight - cornerRadius + arcIntersections = cyMath.intersectLineCircle( + x1, y1, x2, y2, + bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); + + // Ensure the intersection is on the desired quarter of the circle + if (arcIntersections.length > 0 + && arcIntersections[0] >= bottomRightCenterX + && arcIntersections[1] >= bottomRightCenterY) { + straightLineIntersections = straightLineIntersections.concat(arcIntersections); + } + } + + // Bottom Left + { + var bottomLeftCenterX = nodeX - halfWidth + cornerRadius; + var bottomLeftCenterY = nodeY + halfHeight - cornerRadius + arcIntersections = cyMath.intersectLineCircle( + x1, y1, x2, y2, + bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); + + // Ensure the intersection is on the desired quarter of the circle + if (arcIntersections.length > 0 + && arcIntersections[0] <= bottomLeftCenterX + && arcIntersections[1] >= bottomLeftCenterY) { + straightLineIntersections = straightLineIntersections.concat(arcIntersections); + } + } + + if (straightLineIntersections.length > 0) + return straightLineIntersections; + return []; // if nothing + }; + + $$.sbgn.intersectLineEllipse = function ( + x1, y1, x2, y2, centerX, centerY, width, height, padding) { + + var w = width / 2 + padding; + var h = height / 2 + padding; + var an = centerX; + var bn = centerY; + + var d = [x2 - x1, y2 - y1]; + + var m = d[1] / d[0]; + var n = -1 * m * x2 + y2; + var a = h * h + w * w * m * m; + var b = -2 * an * h * h + 2 * m * n * w * w - 2 * bn * m * w * w; + var c = an * an * h * h + n * n * w * w - 2 * bn * w * w * n + + bn * bn * w * w - h * h * w * w; + + var discriminant = b * b - 4 * a * c; + + if (discriminant < 0) { + return []; + } + + var t1 = (-b + Math.sqrt(discriminant)) / (2 * a); + var t2 = (-b - Math.sqrt(discriminant)) / (2 * a); + + var xMin = Math.min(t1, t2); + var xMax = Math.max(t1, t2); + + var yMin = m * xMin - m * x2 + y2; + var yMax = m * xMax - m * x2 + y2; + + return [xMin, yMin, xMax, yMax]; + }; + + $$.sbgn.intersectLineStateAndInfoBoxes = function (node, x, y) { + var centerX = node._private.position.x; + var centerY = node._private.position.y; + var padding = parseInt(node.css('border-width')) / 2; + + var stateAndInfos = node._private.data.statesandinfos; + + var intersections = []; + + for (var i = 0; i < stateAndInfos.length; i++) { + var state = stateAndInfos[i]; + + if ( !state.isDisplayed ) { + continue; + } + + var infoBoxWidth = state.bbox.w; + var infoBoxHeight = state.bbox.h; + + var currIntersections = null; + + if ( state.clazz == "state variable" ) { + var coord = classes.StateVariable.getAbsoluteCoord(state, node.cy()); + currIntersections = $$.sbgn.intersectLineEllipse(x, y, centerX, centerY, + coord.x, coord.y, infoBoxWidth, infoBoxHeight, padding); + } + else if ( state.clazz == "unit of information" ) { + var coord = classes.UnitOfInformation.getAbsoluteCoord(state, node.cy()); + if (node.data("class") == "BA macromolecule" || node.data("class") == "BA nucleic acid feature" + || node.data("class") == "BA complex"){ + currIntersections = $$.sbgn.roundRectangleIntersectLine(x, y, centerX, centerY, + coord.x, coord.y, infoBoxWidth, infoBoxHeight, 5, padding); + } + else if (node.data("class") == "BA unspecified entity"){ + currIntersections = $$.sbgn.intersectLineEllipse(x, y, centerX, centerY, + coord.x, coord.y, infoBoxWidth, infoBoxHeight, padding); + } + else if (node.data("class") == "BA simple chemical"){ + currIntersections = cyMath.intersectLineCircle( + x, y, + centerX, centerY, + coord.x, + coord.y, + infoBoxWidth / 4); + } + else if (node.data("class") == "BA perturbing agent"){ + currIntersections = $$.sbgn.perturbingAgentIntersectLine(x, y, centerX, centerY, + coord.x, coord.y, infoBoxWidth, infoBoxHeight, padding); + } + else { + currIntersections = $$.sbgn.roundRectangleIntersectLine(x, y, centerX, centerY, + coord.x, coord.y, infoBoxWidth, infoBoxHeight, 0, padding); + } + } + + intersections = intersections.concat( currIntersections ); + + } + + return intersections; + }; + + $$.sbgn.checkPointStateAndInfoBoxes = function (x, y, node, threshold) { + return classes.AuxiliaryUnit.checkPoint(x, y, node, threshold); + }; + + $$.sbgn.isNodeShapeTotallyOverriden = function (render, node) { + if (totallyOverridenNodeShapes[render.getNodeShape(node)]) { + return true; + } + + return false; + }; +}; + +},{"../utilities/classes":193,"../utilities/lib-utilities":204}],192:[function(_dereq_,module,exports){ + + +module.exports = function() { + + var jsonToSbgnml, elementUtilities, cy; + + function cdToSbgnml(param) { + jsonToSbgnml = param.jsonToSbgnmlConverter; + elementUtilities = param.elementUtilities; + cy = param.sbgnCyInstance.getCy(); + } + + + cdToSbgnml.convert = function (xml,callback) { + + $.ajax({ + type: 'post', + url: "http://web.newteditor.org:8080/cd2sbgnml", + data: xml, + success: function (data) { + callback(data); + }, + error: function (error) { + callback(null); + } + }) + } + + return cdToSbgnml; + + } + + + +},{}],193:[function(_dereq_,module,exports){ + +var libs = _dereq_('../utilities/lib-utilities').getLibs(); +var jQuery = $ = libs.jQuery; +var cytoscape = libs.cytoscape; +// var optionUtilities = require('./option-utilities'); +// var options = optionUtilities.getOptions(); +var truncate = _dereq_('./text-utilities').truncate; +// only functions not depending on the instances can be used in this way +// e.g. elementUtilities.generateStateVarId() +var elementUtilities = _dereq_('./element-utilities-factory')(); + +var ns = {}; + +// Keep in mind that for each method 'mainObj' parameter refers to the main object for which the operation will be done. +// It refers to the object that could be refered by 'this' while there was prototyping in these classes. +// For example AuxiliaryUnit.copy(mainObj, existingInstance, newParent, newId) copies the variable passed by 'mainObj' +// parameter and in this case 'mainObj' can be considered as `the object to be copied` + +// The old constructors are replaced by 'construct()' methods while removing prototyping from the classes. + +// 'AuxiliaryUnit' and 'AuxUnitLayout' objects keep the id of their parent nodes instead of the nodes themselves to avoid circular references. +// To maintain this property related methods to get and set parent nodes should be used instead of directly accessing the parent object. + +// Also, there is a parent-child relationship between the AuxiliaryUnit class and StateVariable and UnitOfInformation +// classes. While calling a method of AuxiliaryUnit class that method should be called from +// the actual class of related auxilary unit (Would be StateVariable or UnitOfInformation. This is needed to prevent conflictions when the +// methods of AuxiliaryUnit class is overriden by these classes). That class can be obtained by calling 'getAuxUnitClass(mainObj)' +// method for the auxilary unit object. + +var getAuxUnitClass = function(unit) { + // Unit parameter may pass the unit itself or the type of the unit check it + var unitType = typeof unit === 'string' ? unit : unit.clazz; + // Retrieve and return unit class according to the unit type + var className = unitType === 'state variable' ? 'StateVariable' : 'UnitOfInformation'; + return ns[className]; +}; + +ns.getAuxUnitClass = getAuxUnitClass; // Expose getAuxUnitClass method + +var AuxiliaryUnit = {}; + +// -------------- AuxiliaryUnit -------------- // +// constructs a new auxiliary unit object and returns it +AuxiliaryUnit.construct = function(parent) { + var obj = {}; + + AuxiliaryUnit.setParentRef(obj, parent); + + obj.id = null; + obj.bbox = null; + obj.anchorSide = null; + obj.isDisplayed = false; + obj.style = null; + + return obj; +}; + +AuxiliaryUnit.getParent = function(mainObj, cy) { + var parent = mainObj.parent; + // If parent variable stores the id of parent instead of the actual parent get the actual parent by id + if (typeof parent === 'string') { + return cy.getElementById(parent); + } + + return parent; +}; + +AuxiliaryUnit.setParentRef = function(mainObj, newParent) { + if (mainObj && newParent) { + // Reference to id instead of the node itself to avaoid circular reference + mainObj.parent = typeof newParent === 'string' ? newParent : newParent.id(); + } +} + +AuxiliaryUnit.checkPoint = function(x, y, node, threshold) { + var centerX = node._private.position.x; + var centerY = node._private.position.y; + var padding = parseInt(node.css('border-width')) / 2; + var stateAndInfos = node._private.data.statesandinfos; + var cyBaseNodeShapes = cytoscape.baseNodeShapes; +// threshold = parseFloat(threshold); + + for (var i = 0; i < stateAndInfos.length; i++) { + var state = stateAndInfos[i]; + + if (!state.isDisplayed) { + continue; + } + + var stateWidth = parseFloat(state.bbox.w) + threshold; + var stateHeight = parseFloat(state.bbox.h) + threshold; + var coord = AuxiliaryUnit.getAbsoluteCoord(state, node.cy()); + var stateCenterX = coord.x; + var stateCenterY = coord.y; + var checkPoint; + + if (state.clazz == "state variable") { + checkPoint = cyBaseNodeShapes["ellipse"].checkPoint( + x, y, padding, stateWidth, stateHeight, stateCenterX, stateCenterY); + } else if (state.clazz == "unit of information") { + checkPoint = cyBaseNodeShapes["roundrectangle"].checkPoint( + x, y, padding, stateWidth, stateHeight, stateCenterX, stateCenterY); + } + + if (checkPoint == true) { + return state; + } + } + + return null; +}; + +/* + * Return a new AuxiliaryUnit object. A new parent reference and new id can + * optionnally be passed. + */ +AuxiliaryUnit.copy = function (mainObj, cy, existingInstance, newParent, newId) { + var newUnit = existingInstance ? existingInstance : AuxiliaryUnit.construct(); + + var parentToSet = newParent || getAuxUnitClass(mainObj).getParent(mainObj, cy); + AuxiliaryUnit.setParentRef(newUnit, parentToSet); + + newUnit.id = newId ? newId : mainObj.id; + newUnit.bbox = jQuery.extend(true, {}, mainObj.bbox); + newUnit.coordType = mainObj.coordType; + newUnit.anchorSide = mainObj.anchorSide; + newUnit.isDisplayed = mainObj.isDisplayed; + newUnit.style = mainObj.style; + return newUnit; +}; + +// draw the auxiliary unit at its position +AuxiliaryUnit.draw = function(mainObj, cy, context) { + var unitClass = getAuxUnitClass(mainObj); + var coords = unitClass.getAbsoluteCoord(mainObj, cy); + + unitClass.drawShape(mainObj, cy, context, coords.x, coords.y); + if (unitClass.hasText(mainObj, cy)) { + unitClass.drawText(mainObj, cy, context, coords.x, coords.y); + } + mainObj.isDisplayed = true; +}; + +// to be implemented by children +AuxiliaryUnit.getText = function(mainObj, cy) { + throw new Error("Abstract method!"); +}; +AuxiliaryUnit.hasText = function(mainObj, cy) { + throw new Error("Abstract method!"); +}; +AuxiliaryUnit.drawShape = function(mainObj, cy, context, x, y) { + var style = mainObj.style; + cytoscape.sbgn.drawInfoBox(context, x, y, mainObj.bbox.w, mainObj.bbox.h, + style['shape-name']); + + var tmp_ctxt = context.fillStyle; + context.fillStyle = style['background-color']; + context.fill(); + context.fillStyle = tmp_ctxt; + + var parent = getAuxUnitClass(mainObj).getParent(mainObj, cy); + var borderStyle = style.dashed ? 'dashed' : undefined; + var borderWidth = style['border-width']; + // Selected nodes have a specific border color so infobox should have the same + // border color when the node is selected. May need to be updated if style of + // selected nodes is updated in a different way. + var borderColor = parent.selected() ? null : style['border-color']; + cytoscape.sbgn.drawBorder( { context, node: parent, borderStyle, borderColor, borderWidth } ); +}; + +// draw the statesOrInfo's label at given position +AuxiliaryUnit.drawText = function(mainObj, cy, context, centerX, centerY) { + // access the sbgnvizParams set for cy + var options = cy.scratch('_sbgnviz').sbgnvizParams.optionUtilities.getOptions(); + var unitClass = getAuxUnitClass(mainObj); + var parent = unitClass.getParent(mainObj, cy); + var style = mainObj.style; + + // part of : $$.sbgn.drawText(context, textProp); + // save style before modification + var oldFont = context.font; + var oldStyle = context.fillStyle; + var oldOpacity = context.globalAlpha; + + context.font = style['font-style'] + " " + style['font-weight'] + " " + + style['font-size'] + "px " + style['font-family']; + context.fillStyle = style['font-color']; + context.textAlign = "center"; + context.textBaseline = "middle"; + context.globalAlpha = parent.css('text-opacity') * parent.css('opacity'); // ? + + var text; + if(options.fitLabelsToInfoboxes()){ + // here we memoize the truncated text into _textCache, + // as it is not something that changes so much + text = unitClass.getText(mainObj, cy); + var key = text + context.font + mainObj.bbox.w; + if(mainObj._textCache && mainObj._textCache[key]) { + text = mainObj._textCache[key]; + } + else { + text = truncate(unitClass.getText(mainObj, cy), context.font, mainObj.bbox.w); + if(!mainObj._textCache) { + mainObj._textCache = {}; + } + mainObj._textCache[key] = text; + } + } + else { + text = unitClass.getText(mainObj, cy); + } + + context.fillText(text, centerX, centerY); + + // restore saved style + context.fillStyle = oldStyle; + context.font = oldFont; + context.globalAlpha = oldOpacity; +}; + +AuxiliaryUnit.getAbsoluteCoord = function(mainObj, cy) { + var parent = getAuxUnitClass(mainObj).getParent(mainObj, cy); + var position = parent.position(); + var padding = parent.padding(); + /* if(parent.data().complexCalculatedPadding){ + padding = Number(parent.data().complexCalculatedPadding); + //delete parent._private.data.complexCalculatedPadding; + }else{ + padding = parent.padding(); + } */ + var parentWidth = parent.width(); + var parentHeight = parent.height(); + var borderWidth = Number(parent.css("border-width").replace("px",""));//parent.data()['border-width']; + var position = parent.position(); + if (mainObj === undefined || parent === undefined || position === undefined) { + return; + } + var borderWidth = parent.data()["border-width"]; + if ( borderWidth === undefined) { + return; + } + + var absX , absY; + if (mainObj.anchorSide == "top" || mainObj.anchorSide == "bottom") { + + absX = ((mainObj.bbox.x * (parent.outerWidth() - borderWidth)) / 100) + (position.x - parentWidth/2 - padding); + absY = mainObj.anchorSide == "top" ? position.y - parentHeight/2 - padding : position.y + parentHeight/2 + padding ; + + + } + else { + absY = ((mainObj.bbox.y * (parent.outerHeight() - borderWidth)) / 100) + (position.y - parentHeight/2 - padding); + absX = mainObj.anchorSide == "left" ? position.x - parentWidth/2 - padding :position.x + parentWidth/2 + padding; + + } + + + // 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.convertToAbsoluteCoord = function(mainObj, relX, relY, cy) { + var parent = getAuxUnitClass(mainObj).getParent(mainObj, cy); + var position = parent.position(); + var padding = parent.padding(); + var parentWidth = parent.width(); + var parentHeight = parent.height(); + var borderWidth = Number(parent.css("border-width").replace("px","")); + + + var absX , absY; + if (mainObj.anchorSide == "top" || mainObj.anchorSide == "bottom") { + + absX = ((relX * (parent.outerWidth() - borderWidth)) / 100) + (position.x - parentWidth/2 - padding); + absY = mainObj.anchorSide == "top" ? position.y - parentHeight/2 - padding : position.y + parentHeight/2 + padding; + + + } + else { + absY = ((relY * (parent.outerHeight() - borderWidth)) / 100) + (position.y - parentHeight/2 - padding); + absX = mainObj.anchorSide == "left" ? position.x - parentWidth/2 - padding :position.x + parentWidth/2 + padding; + + } + + /* if (parent.data("class") == "compartment"){ + absX += parent.outerWidth() * 0.1; + }; */ + return {x: absX, y: absY}; +}; + +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 parentWidth = parent.width(); + var parentHeight = parent.height(); + var padding = parent.padding(); + /* if(parent.data().complexCalculatedPadding){ + padding = Number(parent.data().complexCalculatedPadding) + }else{ + padding = parent.padding(); + } */ + + + + + var borderWidth = Number(parent.css("border-width").replace("px","")); + + + /* if (parent.data("class") == "compartment"){ + absX -= parent.outerWidth() * 0.1; + } */ + + var relX , relY; + if (mainObj.anchorSide == "top" || mainObj.anchorSide == "bottom") { + + + relX = ((absX - (position.x - parentWidth/2 - padding)) * 100 )/ (parent.outerWidth() - borderWidth); + relY = mainObj.anchorSide == "top" ? 0 : 100; + } + else { + + relX = mainObj.anchorSide == "left" ? 0 : 100; + relY = ((absY - (position.y - parentHeight/2 - padding)) * 100) / (parent.outerHeight() - borderWidth); + + } + relX = relX < 0 ? 0 : relX; + relX = relX > 100 ? 100 : relX; + relY = relY < 0 ? 0 : relY; + relY = relY > 100 ? 100 : relY; + + + return {x: relX, y: relY}; + + +}; + +AuxiliaryUnit.setAnchorSide = function(mainObj, node) { + + var thisX = mainObj.bbox.x; + var thisY = mainObj.bbox.y; + var thisH = mainObj.bbox.h; + var thisW = mainObj.bbox.w; + var width, height, padding; + if(node.data("originalW")){ + width = node.data("originalW"); + padding = 0; + }else{ + width = node.width(); + padding = node.data('class') == "complex" ? Number(node.data().complexCalculatedPadding) : node.padding(); + } + + if(node.data("originalH")){ + height = node.data("originalH"); + padding = 0; + }else{ + height = node.height(); + padding = node.data('class') == "complex" ? Number(node.data().complexCalculatedPadding) : node.padding(); + } + + + + var parentX = (node.data('class') == "compartment" || node.data('class') == "complex") ? node.data('bbox').x : node.position().x; + var parentY = (node.data('class') == "compartment" || node.data('class') == "complex") ? node.data('bbox').y : node.position().y; + var parentX1 = Number((parentX - width / 2 - padding).toFixed(2)); + var parentX2 = Number((parentX+width/2 + padding).toFixed(2)); + var parentY1 = Number((parentY - height/ 2 - padding).toFixed(2)); + var parentY2 = Number((parentY + height/ 2+ padding).toFixed(2)); + var centerX = Number((thisX+thisW/2).toFixed(2)); + var centerY = Number((thisY+thisH/2).toFixed(2)); + + + if (centerY ==parentY1){ + mainObj.anchorSide = "top"; + } + else if (centerY == parentY2) { + mainObj.anchorSide = "bottom"; + } + else if(centerX == parentX1) { + mainObj.anchorSide = "left"; + } + else if((centerX <= (parentX2 + 2)) && (centerX >= (parentX2 - 2)) ){ + mainObj.anchorSide = "right"; + }else{ + return false; + } + + return true; + +}; + + + +AuxiliaryUnit.addToParent = function (mainObj, cy, parentNode, location, position, index) { + + // add state var to the parent's statesandinfos + if(typeof index != "undefined") { // specific index provided (for undo/redo consistency) + parentNode.data('statesandinfos').splice(index, 0, mainObj); + } + else { + parentNode.data('statesandinfos').push(mainObj); + } + + if(!parentNode.data('auxunitlayouts')) { // ensure minimal initialization + parentNode.data('auxunitlayouts', {}); + } + if(!location) { // location not provided, need to define it automatically + location = AuxUnitLayout.selectNextAvailable(parentNode, cy); + } + // here we are sure to have a location even if it was not provided as argument + // get or create the necessary layout + if(!parentNode.data('auxunitlayouts')[location]) { + parentNode.data('auxunitlayouts')[location] = AuxUnitLayout.construct(parentNode, location); + } + + var layout = parentNode.data('auxunitlayouts')[location]; + mainObj.anchorSide = location; + switch(location) { + case "top": mainObj.bbox.y = 0; break; + case "bottom": mainObj.bbox.y = 100; break; + case "left": mainObj.bbox.x = 0; break; + case "right": mainObj.bbox.x = 100; break; + } + // add stateVar to layout, precomputing of relative coords will be triggered accordingly + var insertedPosition = AuxUnitLayout.addAuxUnit(layout, cy, mainObj, position); + return insertedPosition; +} + +AuxiliaryUnit.removeFromParent = function (mainObj, cy) { + var parent = getAuxUnitClass(mainObj).getParent(mainObj, cy); + var parentLayout = parent.data('auxunitlayouts')[mainObj.anchorSide]; + AuxUnitLayout.removeAuxUnit(parentLayout, cy, mainObj); + if (AuxUnitLayout.isEmpty(parentLayout)){ + delete parent.data('auxunitlayouts')[mainObj.anchorSide]; + } + var statesandinfos = parent.data('statesandinfos'); + var index = statesandinfos.indexOf(mainObj); + statesandinfos.splice(index, 1); +}; + +AuxiliaryUnit.getPositionIndex = function(mainObj, cy) { + return getAuxUnitClass(mainObj).getParent(mainObj, cy).data('auxunitlayouts')[mainObj.anchorSide].units.indexOf(mainObj); +}; + +ns.AuxiliaryUnit = AuxiliaryUnit; +// -------------- END AuxiliaryUnit -------------- // + +// -------------- StateVariable -------------- // +/** + * parent has to be a stateful EPN (complex, macromolecule or nucleic acid) + */ + +var StateVariable = {}; + +// StateVariable extends AuxiliaryUnit by inheriting each static property of it +for (var prop in AuxiliaryUnit) { + StateVariable[prop] = AuxiliaryUnit[prop]; +} + +// Construct a state variable object by extending default behaviours of a AuxiliaryUnit object and returns that object +StateVariable.construct = function(value, stateVariableDefinition, parent, id) { + var obj = AuxiliaryUnit.construct(parent); + obj.id = id || elementUtilities.generateStateVarId(); + obj.state = {}; + obj.state.value = value; + obj.state.variable = null; + obj.stateVariableDefinition = stateVariableDefinition; + obj.clazz = "state variable"; + + return obj; +}; + +StateVariable.getText = function(mainObj) { + var stateValue = mainObj.state.value || ''; + var stateVariable = mainObj.state.variable ? "@" + mainObj.state.variable : ""; + + return stateValue + stateVariable; +}; + +StateVariable.hasText = function(mainObj) { + return (mainObj.state.value && mainObj.state.value != "") || (mainObj.state.variable && mainObj.state.variable != ""); +}; + +/*this function is called upon creation of state variable and it returns the location information of the added state variable +*/ +StateVariable.create = function(parentNode, cy, value, variable, bbox, location, position, style, index, id) { + // create the new state var of info + var stateVar = StateVariable.construct(); + StateVariable.setParentRef(stateVar, parentNode); + + stateVar.value = value; + stateVar.variable = variable; + stateVar.state = {value: value, variable: variable}; + stateVar.bbox = bbox; + stateVar.style = style; + if ( id ) { + stateVar.id = id; + } + // 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) { + var position = StateVariable.getPositionIndex(mainObj, cy); + var index = StateVariable.getParent(mainObj, cy).data('statesandinfos').indexOf(mainObj); + StateVariable.removeFromParent(mainObj, cy); + //console.log("after remove", this.parent.data('auxunitlayouts'), this.parent.data('statesandinfos')); + return { + clazz: "state variable", + state: { + value: mainObj.state.value, + variable: mainObj.state.variable + }, + bbox: { + w: mainObj.bbox.w, + h: mainObj.bbox.h + }, + location: mainObj.anchorSide, + position: position, + index: index, + style : mainObj.style + }; +}; + +StateVariable.copy = function(mainObj, cy, newParent, newId) { + var newStateVar = AuxiliaryUnit.copy(mainObj, cy, StateVariable.construct(), newParent, newId); + newStateVar.state = jQuery.extend(true, {}, mainObj.state); + newStateVar.stateVariableDefinition = mainObj.stateVariableDefinition; + newStateVar.clazz = mainObj.clazz; + return newStateVar; +}; + +ns.StateVariable = StateVariable; +// -------------- END StateVariable -------------- // + +// -------------- UnitOfInformation -------------- // +/** + * parent can be an EPN, compartment or subunit + */ + +var UnitOfInformation = {}; + +// UnitOfInformation extends AuxiliaryUnit by inheriting each static property of it +for (var prop in AuxiliaryUnit) { + UnitOfInformation[prop] = AuxiliaryUnit[prop]; +} + +// Constructs a UnitOfInformation object by extending properties of an AuxiliaryUnit object and return that object +UnitOfInformation.construct = function(value, parent, id) { + var obj = AuxiliaryUnit.construct(parent); + obj.id = id || elementUtilities.generateUnitOfInfoId(); + obj.label = {text: value}; // from legacy code, contains {text: } + obj.clazz = "unit of information"; + + return obj; +}; + +UnitOfInformation.getText = function(mainObj) { + return mainObj.label.text; +}; + +UnitOfInformation.hasText = function(mainObj) { + return mainObj.label.text && mainObj.label.text != ""; +}; + +/** + * Creates a unit of info and links everything accordingly + * @param parentNode - the cytoscape element hosting the unit of information + * @param value - its text + * @param [location] - the side where it will be placed top, bottom, right, left or undefined (auto placement) + * @param [position] - its position in the order of elements placed on the same location + * @param [index] - its index in the statesandinfos list + */ +UnitOfInformation.create = function (parentNode, cy, value, bbox, location, position, style, index, id) { + // create the new unit of info + var unit = UnitOfInformation.construct(value, parentNode); + unit.bbox = bbox; + unit.style = style; + if ( id ) { + unit.id = id; + } + + //console.log("will insert on", location, position); + position = UnitOfInformation.addToParent(unit, cy, parentNode, location, position, index); + + return { + index: UnitOfInformation.getParent(unit, cy).data('statesandinfos').indexOf(unit), + location: unit.anchorSide, + position: position + } +}; + +UnitOfInformation.remove = function (mainObj, cy) { + var position = UnitOfInformation.getPositionIndex(mainObj, cy); + var index = UnitOfInformation.getParent(mainObj, cy).data('statesandinfos').indexOf(mainObj); + UnitOfInformation.removeFromParent(mainObj, cy); + //console.log("after remove", this.parent.data('auxunitlayouts'), this.parent.data('statesandinfos')); + return { + clazz: "unit of information", + label: { + text: mainObj.label.text + }, + bbox: { + w: mainObj.bbox.w, + h: mainObj.bbox.h + }, + location: mainObj.anchorSide, + position: position, + index: index, + style: mainObj.style + }; +}; + +UnitOfInformation.copy = function(mainObj, cy, newParent, newId) { + var newUnitOfInfo = AuxiliaryUnit.copy(mainObj, cy, UnitOfInformation.construct(), newParent, newId); + newUnitOfInfo.label = jQuery.extend(true, {}, mainObj.label); + newUnitOfInfo.clazz = mainObj.clazz; + return newUnitOfInfo; +}; + +ns.UnitOfInformation = UnitOfInformation; +// -------------- END UnitOfInformation -------------- // + +// -------------- EntityType -------------- // +/** + * The type of the EPN, for example there can be severals myosin EPN, but only one myosin EntityType + * This class will hold the information regarding state variable, that are shared between all myosins + */ + +var EntityType = {}; + +// Constructs an EntityType object and returns it +EntityType.construct = function(name, EPN) { + var obj = {}; + obj.name = name; // normally the same as its EPNs + obj.stateVariableDefinitions = []; // 0 or many shared state definitions + obj.EPNs = []; // there should always be at least 1 element, else no reason to exist + return obj; +}; + +EntityType.createNewDefinitionFor = function (mainObj, stateVar) { + var newDefinition = StateVariableDefinition.construct(); + newDefinition.entityType = mainObj; + newDefinition.stateVariables.push(stateVar); + + stateVar.stateVariableDefinition = newDefinition; + stateVar.parent.data('entityType', mainObj); + mainObj.stateVariableDefinitions.push(newDefinition); +}; + +EntityType.assignStateVariable = function (mainObj, stateVar) { + // first trivial case, no stateDefinition yet for this entityType, so this is a new one + if (mainObj.stateVariableDefinitions.length == 0) { + EntityType.createNewDefinitionFor(mainObj, stateVar); + } + else { // if definitions are already present, we need to match those to the current stateVariable + for(var i=0; i < mainObj.stateVariableDefinitions.length; i++) { + var matchStateDef = mainObj.stateVariableDefinitions[i]; + if (StateVariableDefinition.matchStateVariable(matchStateDef, stateVar)){ + matchStateDef.stateVariables.push(stateVar); + stateVar.stateVariableDefinition = matchStateDef; + stateVar.parent.data('entityType', mainObj); + return; + } + } + // if nothing was matched among the current stateVarDef of this entityType, create new one + EntityType.createNewDefinitionFor(mainObj, stateVar); + } +}; + +ns.EntityType = EntityType; +// -------------- END EntityType -------------- // + +// -------------- StateVariableDefinition -------------- // +/** + * The state variable definition is something shared across different EPNs + * The concerned EPNs are linked through the entitype reference + */ + +var StateVariableDefinition = {}; + +// Constructs a new StateVariableDefinition object and returns it +StateVariableDefinition.construct = function(name, entityType) { + var obj = {}; + obj.name = name; + obj.entityType = entityType; // reference to owning entity type + obj.stateVariables = []; // there should always be at least 1 element, else no reason to exist + return obj; +}; + +/** + * returns an array of elements that share this state definition + */ +StateVariableDefinition.getConcernedEPNs = function(mainObj) { + return mainObj.entityType.EPNs; +}; + +/** + * Guess if the provided stateVariable belongs to this stateVarDefinition + * We consider it does, if either the statevar.value or statevar.variable are matching one + * if the statevar in the set of the StateVarDef + * This is because we normally compare only stateVariables from the same entityType + */ +StateVariableDefinition.matchStateVariable = function(mainObj, stateVar) { + for(var i=0; i < mainObj.stateVariables.length; i++) { + var matchStateVar = mainObj.stateVariables[i]; + // Don't match a stateVar against another one from the same element. + // If 2 statevar on the same element, then they have to belong to 2 different stateVarDefinitions + if(matchStateVar.parent === stateVar.parent) { + continue; + } + //console.log("try", [matchStateVar.value, matchStateVar.variable], [stateVar.value, stateVar.variable]); + // normal sure case. Example: + // P T134 - undefined T134 + // P undef - P undef + if (//(matchStateVar.value && stateVar.value && matchStateVar.value == stateVar.value ) || + (matchStateVar.variable && stateVar.variable && matchStateVar.variable == stateVar.variable)) { + return true; + } + // more subtle case, with empty stateVar. Look only at value and discard variable + // example: undef undef - P undef + else if ((!matchStateVar.variable && !stateVar.variable) && (matchStateVar.value || stateVar.value)) { + return true; + } + } + return false; +}; + +ns.StateVariableDefinition = StateVariableDefinition; +// -------------- END StateVariableDefinition -------------- // + +// -------------- AuxUnitLayout -------------- // +/** + * Responsible for laying out the auxiliary units contained on a same edge + */ + +var AuxUnitLayout = {}; + +AuxUnitLayout.construct = function(parentNode, location, alignment) { + var obj = {}; + obj.units = []; + obj.location = location; + obj.alignment = alignment || "left"; // this was intended to be used, but it isn't for now + AuxUnitLayout.setParentNodeRef(obj, parentNode); + + obj.renderLengthCache = []; + obj.lengthUsed = 0; + + // specific rules for the layout + if(parentNode.data('class') == "simple chemical") { + obj.outerMargin = 3; + } + + return obj; +}; + +AuxUnitLayout.getParentNode = function(mainObj, cy) { + //console.log(mainObj); + var parentNode = mainObj.parentNode; + + // If parentNode is id of parent node rather than being itself get the parent node by that id + if (typeof parentNode === 'string') { + return cy.getElementById(parentNode) + } + + return parentNode; +}; + +AuxUnitLayout.setParentNodeRef = function(mainObj, parentNode) { + if (mainObj && parentNode) { + // Keep id of parent node to avaoid circular references + mainObj.parentNode = typeof parentNode === 'string' ? parentNode : parentNode.id(); + } +} + +/** + * outerMargin: the left and right space left between the side of the node, and the first (and last) box + * unitGap: the space between the auxiliary units + * alwaysShowAuxUnits: bypasses any limit of units displayed, and prevent units from disappearing, + * forcing a minimum size for the node + * maxUnitDisplayed: show at most this amount of units, even when there is enough space + * + * 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 = 5; +AuxUnitLayout.unitGap = 5; +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) { + //AuxUnitLayout.precomputeCoords(mainObj, cy); +}; + +AuxUnitLayout.addAuxUnit = function(mainObj, cy, unit, position, preComputed) { + if(typeof position != "undefined") { + //console.log("add unit at positiion", position); + mainObj.units.splice(position, 0, unit); + } + else { + mainObj.units.push(unit); + position = mainObj.units.length - 1; + } + if (preComputed === undefined || preComputed === false) { + AuxUnitLayout.computeCoords(mainObj, cy, unit); + var parentNode = AuxUnitLayout.getParentNode(mainObj, cy); + var locations = AuxUnitLayout.checkFit(parentNode,cy); + if(locations.filter(function(loc){return loc == mainObj.location}).length > 0){ + AuxUnitLayout.fitUnits(parentNode,cy, [mainObj.location]); + } + + + } + //AuxUnitLayout.updateLengthCache(mainObj, cy); + //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 ? + return position; +}; + +AuxUnitLayout.computeCoords = function(mainObj, cy, unit){ + AuxUnitLayout.setDisplayedUnits(mainObj, cy); + var location = mainObj.location; + var node = AuxUnitLayout.getParentNode(mainObj, cy); + if (location === "top" || location === "bottom") { + var position = node.position(); + var parentWidth = node.data('bbox').w; + var padding = node.padding(); + var parentWidth = node.width(); + var parentHeight = node.height(); + var parentX1 = position.x - parentWidth/2 - padding; + var parentX2 = position.x + parentWidth/2 + padding; + var parentY1 = position.y - parentHeight/2 - padding; + var parentY2 = position.y + parentHeight/2 + padding; + + if (mainObj.units.length === 1) { + + var relativeCoords = AuxiliaryUnit.convertToRelativeCoord(unit, unit.bbox.w/2 + (parentX1) + AuxUnitLayout.getCurrentGap(location), (parentY1) + AuxUnitLayout.getCurrentGap(location), cy); + unit.bbox.x = relativeCoords.x ; + unit.bbox.y = relativeCoords.y; + } + else { + var lastUnit = mainObj.units[mainObj.units.length - 2];//Get the position of the last unit + var lastUnitAbsCord = AuxiliaryUnit.convertToAbsoluteCoord(lastUnit, lastUnit.bbox.x, lastUnit.bbox.y, cy); + var relativeCoords = AuxiliaryUnit.convertToRelativeCoord(unit, unit.bbox.w/2+ lastUnitAbsCord.x + lastUnit.bbox.w/2 + AuxUnitLayout.getCurrentGap(location), (parentY1) + AuxUnitLayout.getCurrentGap(location), cy); + unit.bbox.x = relativeCoords.x ; + unit.bbox.y = relativeCoords.y; + // unit.bbox.x = mainObj.units[lastUnit].bbox.x + mainObj.units[lastUnit].bbox.w/2 + unit.bbox.w/2 + AuxUnitLayout.getCurrentGap(location); + } + unit.bbox.y = (location === "top") ? 0 : 100; + }//We don't have the right or left addition cases yet +}; + +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); + 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); + + //TODO find a way to elimate this redundancy to update info-box positions + parentNode.data('border-width', parentNode.data('border-width')); +}; + +/** + * reorder boxes using their defined positions. From left to right and top to bottom. + * this ensures that their order in the layout's list corresponds to the reality of the map. + */ +AuxUnitLayout.reorderFromPositions = function(mainObj, cy) { + mainObj.units.sort(function(a, b) { + if(mainObj.location == "top" || mainObj.location == "bottom") { + if (a.bbox.x < b.bbox.x) { + return -1; + } + if (a.bbox.x > b.bbox.x) { + return 1; + } + } + else { + if (a.bbox.y < b.bbox.y) { + return -1; + } + if (a.bbox.y > b.bbox.y) { + return 1; + } + } + return 0; + }); + //console.log("units after reoarder", this.units); + /*AuxUnitLayout.updateLengthCache(mainObj, cy); + AuxUnitLayout.update(mainObj, cy, true);*/ +}; + +/** + * use a cached list to determine what is the length needed to draw x aux units. + * can then be compared against the parent node's dimensions, to decide how many + * aux units to draw. + */ +AuxUnitLayout.updateLengthCache = function(mainObj, cy) { + mainObj.renderLengthCache = [0]; + var previous = AuxUnitLayout.getOuterMargin(mainObj); + for(var i=0; i < mainObj.units.length; i++) { + var currentLength; + if(AuxUnitLayout.isTorB(mainObj)) { + currentLength = mainObj.units[i].bbox.w; + } + else { + currentLength = mainObj.units[i].bbox.h; + } + mainObj.renderLengthCache.push(previous + currentLength + AuxUnitLayout.getOuterMargin(mainObj)); + previous += currentLength + AuxUnitLayout.getUnitGap(mainObj); + } +}; + +/** + * Use the cached precomputed lengths to decide how many units we are capable of drawing, + * considering the size of the parent node. + * The number returned says: we are able to draw the N first units of the lists. + * Unused for now. + */ +AuxUnitLayout.getDrawableUnitAmount = function(mainObj) { + if(AuxUnitLayout.getAlwaysShowAuxUnits(mainObj)) { + // bypass all this + return mainObj.units.length; + } + + // get the length of the side on which we draw + var availableSpace; + if (AuxUnitLayout.isTorB(mainObj)) { + availableSpace = AuxUnitLayout.getParentNode(mainObj, cy).outerWidth(); + } + else { + availableSpace = AuxUnitLayout.getParentNode(mainObj, cy).outerHeight(); + } + // loop over the cached precomputed lengths + for(var i=0; i < mainObj.renderLengthCache.length; i++) { + if(mainObj.renderLengthCache[i] > availableSpace) { + // stop if we overflow + return i - 1; + } + } + return mainObj.units.length; +}; + +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(); + // due to corner of barrel shaped compartment decrease availableSpace -- no infobox on corners + if (AuxUnitLayout.getParentNode(mainObj, cy).data("class") == "compartment") + availableSpace *= 0.8; + } + else { + availableSpace = AuxUnitLayout.getParentNode(mainObj, cy).outerHeight(); + } + + // there is always n+1 elements in the cachedLength for n units + var alwaysShowAuxUnits = AuxUnitLayout.getAlwaysShowAuxUnits(mainObj); + var maxUnitDisplayed = AuxUnitLayout.getMaxUnitDisplayed(mainObj); + for(var i=0; i < mainObj.units.length; i++) { + if((mainObj.renderLengthCache[i+1] <= availableSpace // do we have enough space? + && (maxUnitDisplayed == -1 || i < maxUnitDisplayed)) // is there no limit? or are we under that limit? + || alwaysShowAuxUnits) { // do we always want to show everything regardless? + mainObj.units[i].isDisplayed = true; + } + else { + mainObj.units[i].isDisplayed = false; + } + } +}; + + +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.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.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.checkFit = function (node, cy, forceCheck){ + var fitLocations = []; + for(var location in node.data('auxunitlayouts')) { + if (forceCheck !== undefined && location !== forceCheck) { + continue; + } + if (AuxUnitLayout.getCurrentGap(location) < AuxUnitLayout.unitGap) { + fitLocations.push(location); + continue; + } + 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, cy); + var coordsLast = AuxiliaryUnit.convertToAbsoluteCoord(lastUnit, lastUnit.bbox.x, lastUnit.bbox.y, cy); + var gap = AuxUnitLayout.getCurrentGap(location); + var padding = node.padding(); + if (units.length > 0) { //For any case of removal + if (location === "top" || location === "bottom") { + var parentX1 = node.position().x - node.width()/2 - padding; + var parentX2 = node.position().x + node.width()/2 + padding; + var firstX1 = coordsFirst.x - firstUnit.bbox.w/2; + var lastX2 = coordsLast.x + lastUnit.bbox.w/2; + + if(parentX2 < lastX2 + gap){ + fitLocations.push(location) + } + /* if (parentX1 + gap > firstX1 || parentX2 - gap < lastX2) { + fitLocations.push(location); + } */ + } + else { + var parentY1 = node.position().y - node.height()/2 - padding; + var parentY2 = node.position().y + node.height()/2 + padding; + var firstY1 = coordsFirst.y - firstUnit.bbox.h/2; + var lastY2 = coordsLast.y + lastUnit.bbox.h/2; + if(parentY2 < lastY2 + gap){ + fitLocations.push(location) + } + /* if (parentY1 + gap > firstY1 || parentY2 - gap < lastY2) { + fitLocations.push(location); + } */ + } + } + } + return fitLocations; +}; + +AuxUnitLayout.setIdealGap = function(node, location){ + + var parentWidth = node.width(); + var parentHeight = node.height(); + var padding = node.padding(); + var position = node.position(); + var parentX1 = position.x - parentWidth/2 - padding; + var parentY1 = position.y - parentHeight/2 - padding; + var estimatedGap; + + var auxUnit = node.data('auxunitlayouts')[location]; + if (auxUnit === undefined) { + return 0; + } + if (auxUnit.units.length <= 0 || !auxUnit.units) { + return 0; + } + var units = auxUnit.units; + + if ( location === "top" || location === "bottom") { + usedLength = AuxUnitLayout.getUsedLengthTB(node, auxUnit); + var totalWidth = AuxUnitLayout.getUsedWidth(node, auxUnit); + estimatedGap = (parentWidth + 2* padding - totalWidth) / (units.length + 1); + if (estimatedGap > AuxUnitLayout.unitGap) { + estimatedGap = AuxUnitLayout.unitGap; + } + + //var firstPosition = AuxiliaryUnit.convertToRelativeCoord(units[0], unit[0].bbox.w/2 + (parentX1) + estimatedGap, (parentY1) + estimatedGap, undefined, node);//Position of the first unit + + var usedLength = estimatedGap; + for (var i = 0; i < units.length; i++) { + var relativeCord = AuxiliaryUnit.convertToRelativeCoord(units[i], parentX1 +usedLength + units[i].bbox.w/2, (parentY1) , undefined, node); + units[i].bbox.x = relativeCord.x; + units[i].bbox.y = relativeCord.y; + usedLength += units[i].bbox.w+ estimatedGap; + + } + AuxUnitLayout.setCurrentGap(location, estimatedGap); + } + else { + //Find total left length + usedLength = AuxUnitLayout.getUsedLengthLR(node, auxUnit); + //Compare the side lengths + var totalHeight = AuxUnitLayout.getUsedHeight(node, auxUnit); + estimatedGap = (parentHeight + 2* padding - totalHeight) / (units.length + 1); + if (estimatedGap > AuxUnitLayout.unitGap) { + estimatedGap = AuxUnitLayout.unitGap; + } + //Else scale by using available space, reducing margins and gaps. + //Check if new gap is enough to fit + var usedLength = estimatedGap; + for (var i = 0; i < units.length; i++) { + var relativeCord = AuxiliaryUnit.convertToRelativeCoord(units[i], parentX1 , (parentY1) + usedLength + units[i].bbox.h/2, undefined, node); + units[i].bbox.x = relativeCord.x; + units[i].bbox.y = relativeCord.y; + usedLength += units[i].bbox.h+ estimatedGap; + } + //AuxUnitLayout.currentLeftUnitGap = estimatedGap; + } + AuxUnitLayout.setCurrentGap(location, estimatedGap); + +} +AuxUnitLayout.fitUnits = function (node, cy, locations) { + + var parentWidth = node.width(); + var parentHeight = node.height(); + var padding = node.padding(); + var position = node.position(); + var parentX1 = position.x - parentWidth/2 - padding; + var parentX2 = position.x + parentWidth/2 + padding; + var parentY1 = position.y - parentHeight/2 - padding; + var parentY2 = position.y + parentHeight/2 + padding; + + //Get Parent node and find parent width + + var estimatedGap; + + for (var index = 0; index < locations.length; index++) { + var location = locations[index]; + var auxUnit = node.data('auxunitlayouts')[location]; + if (auxUnit === undefined) { + continue; + } + if (auxUnit.units.length <= 0 || !auxUnit.units) { + continue; + } + var units = auxUnit.units; + + if ( location === "top" || location === "bottom") { + usedLength = AuxUnitLayout.getUsedLengthTB(node, auxUnit); + var totalWidth = AuxUnitLayout.getUsedWidth(node, auxUnit); + estimatedGap = (parentWidth + 2*padding - totalWidth) / (units.length + 1); + if (estimatedGap > AuxUnitLayout.unitGap) { + estimatedGap = AuxUnitLayout.unitGap; + } + + //var firstPosition = AuxiliaryUnit.convertToRelativeCoord(units[0], unit[0].bbox.w/2 + (parentX1) + estimatedGap, (parentY1) + estimatedGap, undefined, node);//Position of the first unit + + var usedLength = estimatedGap; + for (var i = 0; i < units.length; i++) { + var relativeCord = AuxiliaryUnit.convertToRelativeCoord(units[i], parentX1 +usedLength + units[i].bbox.w/2, (parentY1) , undefined, node); + units[i].bbox.x = relativeCord.x; + units[i].bbox.y = relativeCord.y; + usedLength += units[i].bbox.w+ estimatedGap; + + } + AuxUnitLayout.setCurrentGap(location, estimatedGap); + } + else { + //Find total left length + usedLength = AuxUnitLayout.getUsedLengthLR(node, auxUnit); + //Compare the side lengths + var totalHeight = AuxUnitLayout.getUsedHeight(node, auxUnit); + estimatedGap = (parentHeight + 2*padding - totalHeight) / (units.length + 1); + if (estimatedGap > AuxUnitLayout.unitGap) { + estimatedGap = AuxUnitLayout.unitGap; + } + //Else scale by using available space, reducing margins and gaps. + //Check if new gap is enough to fit + var usedLength = estimatedGap; + for (var i = 0; i < units.length; i++) { + var relativeCord = AuxiliaryUnit.convertToRelativeCoord(units[i], parentX1 , (parentY1) + usedLength + units[i].bbox.h/2, undefined, node); + units[i].bbox.x = relativeCord.x; + units[i].bbox.y = relativeCord.y; + usedLength += units[i].bbox.h+ estimatedGap; + } + //AuxUnitLayout.currentLeftUnitGap = estimatedGap; + } + AuxUnitLayout.setCurrentGap(location, estimatedGap); + } + + //TODO find a way to elimate this redundancy to update info-box positions + node.data('border-width', node.data('border-width')); + +}; + + +// 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]; + if (auxUnit.coordType != "relativeToSide" || doForceUpdate) { + if (auxUnit.coordType == "relativeToCenter" || doForceUpdate) { + if(AuxUnitLayout.isTorB(mainObj)) { + //auxUnit.bbox.y = 0; + auxUnit.bbox.x = lengthUsed + auxUnit.bbox.w / 2; + } + else { + //auxUnit.bbox.x = 0; + auxUnit.bbox.y = lengthUsed + auxUnit.bbox.h / 2; + } + } + auxUnit.coordType = "relativeToSide"; + } + + if(AuxUnitLayout.isTorB(mainObj)) { + //auxUnit.bbox.y = 0; + lengthUsed += auxUnit.bbox.w + unitGap; + } + else { + //auxUnit.bbox.x = 0; + lengthUsed += auxUnit.bbox.h + unitGap; + } + + if(auxUnit.isDisplayed) { + finalLengthUsed = lengthUsed; + } + } + // adjust the length, should be composed of outerMargin on the end, not unitGap + finalLengthUsed = finalLengthUsed - unitGap + AuxUnitLayout.getOuterMargin(mainObj); + + mainObj.lengthUsed = finalLengthUsed; +}; + +AuxUnitLayout.draw = function (mainObj, cy, context) { + for(var i=0; i < mainObj.units.length; i++) { + var auxUnit = mainObj.units[i]; + getAuxUnitClass(auxUnit).draw(auxUnit, 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); + //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.isEmpty = function(mainObj) { + return mainObj.units.length == 0; +}; + +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; +}; + +//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. + */ +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"; + } + else if(!bottom || AuxUnitLayout.isEmpty(bottom)) { + resultLocation = "bottom"; + } + else { + // choose the side (top or bottom) that has the most space available to the right of the rightmost infobox + if(AuxUnitLayout.unitLength(top) <= AuxUnitLayout.unitLength(bottom)) { + resultLocation = "top"; + } + else { + resultLocation = "bottom"; + } + } + AuxUnitLayout.lastPos = resultLocation; //Set last used position + return resultLocation; +}; + +AuxUnitLayout.resizeParent = function (mainObj, cy, length) { + var parentNode = AuxUnitLayout.getParentNode(mainObj, cy); + if(AuxUnitLayout.isTorB(mainObj)) { + if(parentNode.data('bbox').w < length) { + cy.trigger("noderesize.resizestart", ["centerright", parentNode]); + parentNode.data('bbox').w = length; + cy.trigger("noderesize.resizeend", ["centerright", parentNode]); + } + } + else { + if(parentNode.data('bbox').h < length) { + cy.trigger("noderesize.resizestart", ["bottomcenter", parentNode]); + parentNode.data('bbox').h = length; + cy.trigger("noderesize.resizeend", ["bottomcenter", parentNode]); + } + } +}; + +AuxUnitLayout.isTorB = function (mainObj) { + return mainObj.location == "top" || mainObj.location == "bottom"; +}; + +AuxUnitLayout.isLorR = function (mainObj) { + return mainObj.location == "left" || mainObj.location == "right"; +}; + +AuxUnitLayout.setParentMinLength = function (mainObj, cy) { + var parentNode = AuxUnitLayout.getParentNode(mainObj, cy); + var parentLayouts = parentNode.data('auxunitlayouts'); + switch(mainObj.location) { + case "top": + var compareVal = parentLayouts.bottom ? parentLayouts.bottom.lengthUsed : 0; + break; + case "bottom": + var compareVal = parentLayouts.top ? parentLayouts.top.lengthUsed : 0; + break; + case "left": + var compareVal = parentLayouts.right ? parentLayouts.right.lengthUsed : 0; + break; + case "right": + var compareVal = parentLayouts.left ? parentLayouts.left.lengthUsed : 0; + break; + } + if(AuxUnitLayout.isTorB(mainObj)) { + parentNode.data('resizeMinWidth', Math.max(mainObj.lengthUsed, compareVal)); + } + else { + parentNode.data('resizeMinHeight', Math.max(mainObj.lengthUsed, compareVal)); + } +}; + +AuxUnitLayout.getOuterMargin = function (mainObj) { + if(typeof mainObj.outerMargin !== "undefined" && mainObj.outerMargin !== null) { + return mainObj.outerMargin; + } + else { + return AuxUnitLayout.outerMargin; + } +}; + +AuxUnitLayout.getUnitGap = function (mainObj) { + if(typeof mainObj.unitGap !== "undefined" && mainObj.unitGap !== null) { + return mainObj.unitGap; + } + else { + return AuxUnitLayout.unitGap; + } +}; + +AuxUnitLayout.getAlwaysShowAuxUnits = function (mainObj) { + if(typeof mainObj.alwaysShowAuxUnits !== "undefined" && mainObj.alwaysShowAuxUnits !== null) { + return mainObj.alwaysShowAuxUnits; + } + else { + return AuxUnitLayout.alwaysShowAuxUnits; + } +}; + +AuxUnitLayout.getMaxUnitDisplayed = function (mainObj) { + if(typeof mainObj.maxUnitDisplayed !== "undefined" && mainObj.maxUnitDisplayed !== null) { + return mainObj.maxUnitDisplayed; + } + else { + return AuxUnitLayout.maxUnitDisplayed; + } +}; + +/* + * Duplicate a layout. Doesn't copy the units attribute, reset it instead. + */ +AuxUnitLayout.copy = function(mainObj, cy, newParent) { + var newLayout = AuxUnitLayout.construct(newParent); + // Copying the same reference to units would be inconsistent. + // Duplicating owned units goes beyonnd the scope, because we need to assign + // ids that are tied to the global cound of units of a node. + // So duplicating units is something that should be properly done outside of this function. + // TODO that is a bit dirty, find a nice modular way to arrange that + newLayout.units = []; + newLayout.location = mainObj.location; + newLayout.alignment = mainObj.alignment; + AuxUnitLayout.setParentNodeRef(newLayout, newParent); + newLayout.renderLengthCache = mainObj.renderLengthCache; + newLayout.lengthUsed = mainObj.lengthUsed; + if(typeof mainObj.outerMargin !== "undefined") { + newLayout.outerMargin = mainObj.outerMargin; + } + if(typeof mainObj.unitGap !== "undefined") { + newLayout.unitGap = mainObj.unitGap; + } + if(typeof mainObj.alwaysShowAuxUnits !== "undefined") { + newLayout.alwaysShowAuxUnits = mainObj.alwaysShowAuxUnits; + } + if(typeof mainObj.maxUnitDisplayed !== "undefined") { + newLayout.maxUnitDisplayed = mainObj.maxUnitDisplayed; + } + return newLayout; +}; + +ns.AuxUnitLayout = AuxUnitLayout; +// -------------- END AuxUnitLayout -------------- // + +module.exports = ns; + +},{"../utilities/lib-utilities":204,"./element-utilities-factory":194,"./text-utilities":215}],194:[function(_dereq_,module,exports){ +/* + * Common utilities for elements includes both general utilities and sbgn specific utilities + */ + +var libUtilities = _dereq_('./lib-utilities'); +var textUtilities = _dereq_('./text-utilities'); +var libs = libUtilities.getLibs(); +var jQuery = $ = libs.jQuery; +var classes = _dereq_('./classes'); +module.exports = function () { + var optionUtilities, graphUtilities; + var options; + var cy; + + function elementUtilities (param) { + // Init params to be accessed by elementUtilities + optionUtilities = param.optionUtilities; + options = optionUtilities.getOptions(); + graphUtilities = param.graphUtilities; + cy = param.sbgnCyInstance.getCy(); + } + + var inArray = function( value, arr ) { + return $.inArray( value, arr ) !== -1; + }; + + // initialize map type + elementUtilities.mapType = 'PD'; + elementUtilities.fileFormat = undefined; + + elementUtilities.PD = {}; // namespace for all PD specific stuff + elementUtilities.AF = {}; // namespace for all AF specific stuff + elementUtilities.SIF = {}; // namespace for all SIF specific stuff + + elementUtilities.graphTopologyLocked = false; + + // see http://stackoverflow.com/a/8809472 + // we need to take care of our own IDs because the ones automatically generated by cytoscape (also UUID) + // don't comply with xsd:SID type that must not begin with a number + elementUtilities.generateUUID = function () { // Public Domain/MIT + var d = Date.now(); + if (typeof performance !== 'undefined' && typeof performance.now === 'function'){ + d += performance.now(); //use high-precision timer if available + } + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = (d + Math.random() * 16) % 16 | 0; + d = Math.floor(d / 16); + return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); + }); + }; + + elementUtilities.generateNodeId = function() { + return 'nwtN_' + elementUtilities.generateUUID(); + }; + + elementUtilities.generateEdgeId = function() { + return 'nwtE_' + elementUtilities.generateUUID(); + }; + + elementUtilities.generateStateVarId = function() { + return 'nwtSV_' + elementUtilities.generateUUID(); + }; + + elementUtilities.generateUnitOfInfoId = function() { + return 'nwtUOI_' + elementUtilities.generateUUID(); + }; + + /* + see http://journal.imbio.de/articles/pdf/jib-263.pdf p.41 <-- but beware, outdated + following tables have been updated with PD lvl1 v2.0 of November 7, 2016 working draft + only the following things have been changed from 2.0 (this version is not clear on connectivity): + - empty set has no limit on its edge count + - logic operators can be source and target + - limit of 1 catalysis and 1 necessary stimulation on a process + + for each edge class and nodeclass define 2 cases: + - node can be a source of this edge -> asSource + - node can be a target of this edge -> asTarget + for both cases, tells if it is allowed and what is the limit of edges allowed. + Limits can concern only this type of edge (maxEdge) or the total number of edges for this node (maxTotal). + Consider undefined things as false/unallowed -> whitelist behavior. + + the nodes/edges class listed below are those used in the program. + For instance "compartment" isn't a node in SBGN specs. + */ + elementUtilities.PD.connectivityConstraints = { + "consumption": { + "macromolecule": {asSource: {isAllowed: true}, asTarget: {}}, + "simple chemical": {asSource: {isAllowed: true}, asTarget: {}}, + "unspecified entity": {asSource: {isAllowed: true}, asTarget: {}}, + "complex": {asSource: {isAllowed: true}, asTarget: {}}, + "nucleic acid feature": {asSource: {isAllowed: true}, asTarget: {}}, + "compartment": {asSource: {}, asTarget: {}}, + "tag": {asSource: {}, asTarget: {}}, + "source and sink": {asSource: {isAllowed: true}, asTarget: {}}, + "perturbing agent": {asSource: {}, asTarget: {}}, + "submap": {asSource: {}, asTarget: {}}, + "process": {asSource: {}, asTarget: {isAllowed: true}}, + "omitted process": {asSource: {}, asTarget: {isAllowed: true}}, + "uncertain process": {asSource: {}, asTarget: {isAllowed: true}}, + "phenotype": {asSource: {}, asTarget: {}}, + "association": {asSource: {}, asTarget: {isAllowed: true}}, + "dissociation": {asSource: {}, asTarget: {isAllowed: true, maxEdge: 1, maxTotal: 1}}, + "and": {asSource: {}, asTarget: {}}, + "or": {asSource: {}, asTarget: {}}, + "not": {asSource: {}, asTarget: {}} + }, + "production": { + "macromolecule": {asSource: {}, asTarget: {isAllowed: true}}, + "simple chemical": {asSource: {}, asTarget: {isAllowed: true}}, + "unspecified entity": {asSource: {}, asTarget: {isAllowed: true}}, + "complex": {asSource: {}, asTarget: {isAllowed: true}}, + "nucleic acid feature": {asSource: {}, asTarget: {isAllowed: true}}, + "compartment": {asSource: {}, asTarget: {}}, + "tag": {asSource: {}, asTarget: {}}, + "source and sink": {asSource: {}, asTarget: {isAllowed: true}}, + "perturbing agent": {asSource: {}, asTarget: {}}, + "submap": {asSource: {}, asTarget: {}}, + "process": {asSource: {isAllowed: true}, asTarget: {}}, + "omitted process": {asSource: {isAllowed: true}, asTarget: {}}, + "uncertain process": {asSource: {isAllowed: true}, asTarget: {}}, + "phenotype": {asSource: {}, asTarget: {}}, + "association": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "dissociation": {asSource: {isAllowed: true}, asTarget: {}}, + "and": {asSource: {}, asTarget: {}}, + "or": {asSource: {}, asTarget: {}}, + "not": {asSource: {}, asTarget: {}} + }, + "modulation": { + "macromolecule": {asSource: {isAllowed: true}, asTarget: {}}, + "simple chemical": {asSource: {isAllowed: true}, asTarget: {}}, + "unspecified entity": {asSource: {isAllowed: true}, asTarget: {}}, + "complex": {asSource: {isAllowed: true}, asTarget: {}}, + "nucleic acid feature": {asSource: {isAllowed: true}, asTarget: {}}, + "compartment": {asSource: {}, asTarget: {}}, + "tag": {asSource: {}, asTarget: {}}, + "source and sink": {asSource: {isAllowed: true}, asTarget: {}}, + "perturbing agent": {asSource: {isAllowed: true}, asTarget: {}}, + "submap": {asSource: {}, asTarget: {}}, + "process": {asSource: {}, asTarget: {isAllowed: true}}, + "omitted process": {asSource: {}, asTarget: {isAllowed: true}}, + "uncertain process": {asSource: {}, asTarget: {isAllowed: true}}, + "phenotype": {asSource: {}, asTarget: {isAllowed: true}}, + "association": {asSource: {}, asTarget: {}}, + "dissociation": {asSource: {}, asTarget: {}}, + "and": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "or": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "not": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}} + }, + "stimulation": { + "macromolecule": {asSource: {isAllowed: true}, asTarget: {}}, + "simple chemical": {asSource: {isAllowed: true}, asTarget: {}}, + "unspecified entity": {asSource: {isAllowed: true}, asTarget: {}}, + "complex": {asSource: {isAllowed: true}, asTarget: {}}, + "nucleic acid feature": {asSource: {isAllowed: true}, asTarget: {}}, + "compartment": {asSource: {}, asTarget: {}}, + "tag": {asSource: {}, asTarget: {}}, + "source and sink": {asSource: {isAllowed: true}, asTarget: {}}, + "perturbing agent": {asSource: {isAllowed: true}, asTarget: {}}, + "submap": {asSource: {}, asTarget: {}}, + "process": {asSource: {}, asTarget: {isAllowed: true}}, + "omitted process": {asSource: {}, asTarget: {isAllowed: true}}, + "uncertain process": {asSource: {}, asTarget: {isAllowed: true}}, + "phenotype": {asSource: {}, asTarget: {isAllowed: true}}, + "association": {asSource: {}, asTarget: {}}, + "dissociation": {asSource: {}, asTarget: {}}, + "and": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "or": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "not": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}} + }, + "catalysis": { + "macromolecule": {asSource: {isAllowed: true}, asTarget: {}}, + "simple chemical": {asSource: {isAllowed: true}, asTarget: {}}, + "unspecified entity": {asSource: {isAllowed: true}, asTarget: {}}, + "complex": {asSource: {isAllowed: true}, asTarget: {}}, + "nucleic acid feature": {asSource: {}, asTarget: {}}, + "compartment": {asSource: {}, asTarget: {}}, + "tag": {asSource: {}, asTarget: {}}, + "source and sink": {asSource: {isAllowed: true}, asTarget: {}}, + "perturbing agent": {asSource: {}, asTarget: {}}, + "submap": {asSource: {}, asTarget: {}}, + "process": {asSource: {}, asTarget: {isAllowed: true, maxEdge: 1}}, + "omitted process": {asSource: {}, asTarget: {isAllowed: true, maxEdge: 1}}, + "uncertain process": {asSource: {}, asTarget: {isAllowed: true, maxEdge: 1}}, + "phenotype": {asSource: {}, asTarget: {isAllowed: true, maxEdge: 1}}, + "association": {asSource: {}, asTarget: {}}, + "dissociation": {asSource: {}, asTarget: {}}, + "and": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "or": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "not": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}} + }, + "inhibition": { + "macromolecule": {asSource: {isAllowed: true}, asTarget: {}}, + "simple chemical": {asSource: {isAllowed: true}, asTarget: {}}, + "unspecified entity": {asSource: {isAllowed: true}, asTarget: {}}, + "complex": {asSource: {isAllowed: true}, asTarget: {}}, + "nucleic acid feature": {asSource: {isAllowed: true}, asTarget: {}}, + "compartment": {asSource: {}, asTarget: {}}, + "tag": {asSource: {}, asTarget: {}}, + "source and sink": {asSource: {isAllowed: true}, asTarget: {}}, + "perturbing agent": {asSource: {isAllowed: true}, asTarget: {}}, + "submap": {asSource: {}, asTarget: {}}, + "process": {asSource: {}, asTarget: {isAllowed: true}}, + "omitted process": {asSource: {}, asTarget: {isAllowed: true}}, + "uncertain process": {asSource: {}, asTarget: {isAllowed: true}}, + "phenotype": {asSource: {}, asTarget: {isAllowed: true}}, + "association": {asSource: {}, asTarget: {}}, + "dissociation": {asSource: {}, asTarget: {}}, + "and": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "or": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "not": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}} + }, + "necessary stimulation": { + "macromolecule": {asSource: {isAllowed: true}, asTarget: {}}, + "simple chemical": {asSource: {isAllowed: true}, asTarget: {}}, + "unspecified entity": {asSource: {isAllowed: true}, asTarget: {}}, + "complex": {asSource: {isAllowed: true}, asTarget: {}}, + "nucleic acid feature": {asSource: {isAllowed: true}, asTarget: {}}, + "compartment": {asSource: {}, asTarget: {}}, + "tag": {asSource: {}, asTarget: {}}, + "source and sink": {asSource: {isAllowed: true}, asTarget: {}}, + "perturbing agent": {asSource: {isAllowed: true}, asTarget: {}}, + "submap": {asSource: {}, asTarget: {}}, + "process": {asSource: {}, asTarget: {isAllowed: true, maxEdge: 1}}, + "omitted process": {asSource: {}, asTarget: {isAllowed: true, maxEdge: 1}}, + "uncertain process": {asSource: {}, asTarget: {isAllowed: true, maxEdge: 1}}, + "phenotype": {asSource: {}, asTarget: {isAllowed: true, maxEdge: 1}}, + "association": {asSource: {}, asTarget: {}}, + "dissociation": {asSource: {}, asTarget: {}}, + "and": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "or": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "not": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + }, + "logic arc": { + "macromolecule": {asSource: {isAllowed: true}, asTarget: {}}, + "simple chemical": {asSource: {isAllowed: true}, asTarget: {}}, + "unspecified entity": {asSource: {isAllowed: true}, asTarget: {}}, + "complex": {asSource: {isAllowed: true}, asTarget: {}}, + "nucleic acid feature": {asSource: {isAllowed: true}, asTarget: {}}, + "compartment": {asSource: {}, asTarget: {}}, + "tag": {asSource: {}, asTarget: {}}, + "source and sink": {asSource: {isAllowed: true}, asTarget: {}}, + "perturbing agent": {asSource: {}, asTarget: {}}, + "submap": {asSource: {}, asTarget: {}}, + "process": {asSource: {}, asTarget: {}}, + "omitted process": {asSource: {}, asTarget: {}}, + "uncertain process": {asSource: {}, asTarget: {}}, + "phenotype": {asSource: {}, asTarget: {}}, + "association": {asSource: {}, asTarget: {}}, + "dissociation": {asSource: {}, asTarget: {}}, + "and": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {isAllowed: true}}, + "or": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {isAllowed: true}}, + "not": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {isAllowed: true, maxEdge: 1, maxTotal: 1}}, + }, + "equivalence arc": { + "macromolecule": {asSource: {isAllowed: true}, asTarget: {}}, + "simple chemical": {asSource: {isAllowed: true}, asTarget: {}}, + "unspecified entity": {asSource: {isAllowed: true}, asTarget: {}}, + "complex": {asSource: {isAllowed: true}, asTarget: {}}, + "nucleic acid feature": {asSource: {isAllowed: true}, asTarget: {}}, + "compartment": {asSource: {}, asTarget: {}}, + "tag": {asSource: {}, asTarget: {isAllowed: true}}, + "source and sink": {asSource: {}, asTarget: {}}, + "perturbing agent": {asSource: {}, asTarget: {}}, + "submap": {asSource: {}, asTarget: {isAllowed: true}}, + "process": {asSource: {}, asTarget: {}}, + "omitted process": {asSource: {}, asTarget: {}}, + "uncertain process": {asSource: {}, asTarget: {}}, + "phenotype": {asSource: {}, asTarget: {}}, + "association": {asSource: {}, asTarget: {}}, + "dissociation": {asSource: {}, asTarget: {}}, + "and": {asSource: {}, asTarget: {}}, + "or": {asSource: {}, asTarget: {}}, + "not": {asSource: {}, asTarget: {}} + } + }; + + /* AF node connectivity rules + * See: Systems Biology Graphical Notation: Activity Flow language Level 1, Version 1.2, Date: July 27, 2015 + * Section 3.3.1: Activity Nodes connectivity definition + * URL: https://doi.org/10.2390/biecoll-jib-2015-265 + */ + elementUtilities.AF.connectivityConstraints = { + "positive influence": { + "biological activity": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}}, + "phenotype": {asSource: {}, asTarget: {isAllowed: true}}, + "tag": {asSource: {}, asTarget: {}}, + "submap": {asSource: {}, asTarget: {}}, + "and": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "or": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "not": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "delay": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "compartment": {asSource: {}, asTarget: {}}, + }, + "negative influence": { + "biological activity": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}}, + "phenotype": {asSource: {}, asTarget: {isAllowed: true}}, + "tag": {asSource: {}, asTarget: {}}, + "submap": {asSource: {}, asTarget: {}}, + "and": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "or": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "not": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "delay": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "compartment": {asSource: {}, asTarget: {}}, + }, + "unknown influence": { + "biological activity": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}}, + "phenotype": {asSource: {}, asTarget: {isAllowed: true}}, + "tag": {asSource: {}, asTarget: {}}, + "submap": {asSource: {}, asTarget: {}}, + "and": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "or": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "not": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "delay": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "compartment": {asSource: {}, asTarget: {}}, + }, + "necessary stimulation": { + "biological activity": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}}, + "phenotype": {asSource: {}, asTarget: {isAllowed: true}}, + "tag": {asSource: {}, asTarget: {}}, + "submap": {asSource: {}, asTarget: {}}, + "and": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "or": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "not": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "delay": {asSource: {isAllowed: true, maxEdge: 1, maxTotal: 1}, asTarget: {}}, + "compartment": {asSource: {}, asTarget: {}}, + }, + "logic arc": { + "biological activity": {asSource: {isAllowed: true}, asTarget: {}}, + "phenotype": {asSource: {}, asTarget: {}}, + "tag": {asSource: {}, asTarget: {}}, + "submap": {asSource: {}, asTarget: {}}, + "and": {asSource: {}, asTarget: {isAllowed: true}}, + "or": {asSource: {}, asTarget: {isAllowed: true}}, + "not": {asSource: {}, asTarget: {isAllowed: true, maxEdge: 1, maxTotal: 1}}, + "delay": {asSource: {}, asTarget: {isAllowed: true, maxEdge: 1, maxTotal: 1}}, + "compartment": {asSource: {}, asTarget: {}}, + }, + "equivalence arc": { + "biological activity": {asSource: {isAllowed: true}, asTarget: {}}, + "phenotype": {asSource: {isAllowed: true}, asTarget: {}}, + "tag": {asSource: {}, asTarget: {isAllowed: true}}, + "submap": {asSource: {}, asTarget: {isAllowed: true}}, + "and": {asSource: {}, asTarget: {}}, + "or": {asSource: {}, asTarget: {}}, + "not": {asSource: {}, asTarget: {}}, + "delay": {asSource: {}, asTarget: {}}, + "compartment": {asSource: {}, asTarget: {}}, + }, + } + + elementUtilities.SIF.connectivityConstraints = { + "controls-state-change-of": { + "SIF macromolecule": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}}, + "SIF simple chemical": {asSource: {}, asTarget: {}} + }, + "controls-transport-of": { + "SIF macromolecule": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}}, + "SIF simple chemical": {asSource: {}, asTarget: {}} + }, + "controls-phosphorylation-of": { + "SIF macromolecule": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}}, + "SIF simple chemical": {asSource: {}, asTarget: {}} + }, + "controls-expression-of": { + "SIF macromolecule": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}}, + "SIF simple chemical": {asSource: {}, asTarget: {}} + }, + "catalysis-precedes": { + "SIF macromolecule": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}}, + "SIF simple chemical": {asSource: {}, asTarget: {}} + }, + "in-complex-with": { + "SIF macromolecule": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}}, + "SIF simple chemical": {asSource: {}, asTarget: {}} + }, + "interacts-with": { + "SIF macromolecule": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}}, + "SIF simple chemical": {asSource: {}, asTarget: {}} + }, + "neighbor-of": { + "SIF macromolecule": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}}, + "SIF simple chemical": {asSource: {}, asTarget: {}} + }, + "consumption-controled-by": { + "SIF macromolecule": {asSource: {}, asTarget: {isAllowed: true}}, + "SIF simple chemical": {asSource: {isAllowed: true}, asTarget: {}} + }, + "controls-production-of": { + "SIF macromolecule": {asSource: {isAllowed: true}, asTarget: {}}, + "SIF simple chemical": {asSource: {}, asTarget: {isAllowed: true}} + }, + "controls-transport-of-chemical": { + "SIF macromolecule": {asSource: {isAllowed: true}, asTarget: {}}, + "SIF simple chemical": {asSource: {}, asTarget: {isAllowed: true}} + }, + "chemical-affects": { + "SIF macromolecule": {asSource: {}, asTarget: {isAllowed: true}}, + "SIF simple chemical": {asSource: {isAllowed: true}, asTarget: {}} + }, + "reacts-with": { + "SIF macromolecule": {asSource: {}, asTarget: {}}, + "SIF simple chemical": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}} + }, + "used-to-produce": { + "SIF macromolecule": {asSource: {}, asTarget: {}}, + "SIF simple chemical": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}} + }, + "activates": { + "SIF macromolecule": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}}, + "SIF simple chemical": {asSource: {}, asTarget: {}} + }, + "inhibits": { + "SIF macromolecule": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}}, + "SIF simple chemical": {asSource: {}, asTarget: {}} + }, + "phosphorylates": { + "SIF macromolecule": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}}, + "SIF simple chemical": {asSource: {}, asTarget: {}} + }, + "dephosphorylates": { + "SIF macromolecule": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}}, + "SIF simple chemical": {asSource: {}, asTarget: {}} + }, + "upregulates-expression": { + "SIF macromolecule": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}}, + "SIF simple chemical": {asSource: {}, asTarget: {}} + }, + "downregulates-expression": { + "SIF macromolecule": {asSource: {isAllowed: true}, asTarget: {isAllowed: true}}, + "SIF simple chemical": {asSource: {}, asTarget: {}} + }, + }; + + elementUtilities.logicalOperatorTypes = ['and', 'or', 'not', 'delay']; + elementUtilities.processTypes = ['process', 'omitted process', 'uncertain process', + 'association', 'dissociation', 'phenotype']; + elementUtilities.biologicalActivityTypes = ['biological activity', 'BA plain', 'BA unspecified entity', + 'BA simple chemical', 'BA macromolecule', 'BA nucleic acid feature', + 'BA perturbing agent', 'BA complex']; + elementUtilities.epnTypes = ['macromolecule', 'nucleic acid feature', 'simple chemical', + 'source and sink', 'unspecified entity', 'perturbing agent', 'complex', + 'nucleic acid feature multimer', 'macromolecule multimer', 'simple chemical multimer', 'complex multimer']; + elementUtilities.sifTypes = ['SIF macromolecule', 'SIF simple chemical']; + elementUtilities.otherNodeTypes = ['compartment', 'tag', 'submap', 'topology group']; + + elementUtilities.nodeTypes = elementUtilities.epnTypes + .concat( elementUtilities.logicalOperatorTypes ) + .concat( elementUtilities.processTypes ) + .concat( elementUtilities.biologicalActivityTypes ) + .concat( elementUtilities.sifTypes ) + .concat( elementUtilities.otherNodeTypes ); + + elementUtilities.compoundNodeTypes = ['complex', 'compartment', 'submap']; + + elementUtilities.simpleNodeTypes = $(elementUtilities.nodeTypes) + .not(elementUtilities.compoundNodeTypes).get(); + + elementUtilities.sifEdgeTypes = ['neighbor-of', 'interacts-with', 'in-complex-with', + 'controls-state-change-of', 'controls-transport-of', 'controls-phosphorylation-of', + 'catalysis-precedes', 'controls-expression-of', 'consumption-controled-by', + 'controls-production-of', 'controls-transport-of-chemical', 'chemical-affects', + 'reacts-with', 'used-to-produce', 'phosphorylates', 'dephosphorylates', + 'upregulates-expression', 'downregulates-expression', 'activates', 'inhibits']; + + elementUtilities.edgeTypes = ['consumption', 'production', 'modulation', + 'stimulation', 'catalysis', 'inhibition', 'necessary stimulation', + 'logic arc', 'equivalence arc', 'unknown influence', 'positive influence', + 'negative influence', 'controls-state-change-of', + 'controls-transport-of', 'controls-phosphorylation-of', + 'controls-expression-of', 'catalysis-precedes', 'in-complex-with', + 'interacts-with', 'neighbor-of', 'consumption-controled-by', + 'controls-production-of', 'controls-transport-of-chemical', + 'chemical-affects', 'reacts-with', 'used-to-produce', + 'activates', 'inhibits', 'phosphorylates', 'dephosphorylates', + 'upregulates-expression', 'downregulates-expression' + ]; + + elementUtilities.undirectedEdgeTypes = ['in-complex-with', 'interacts-with', + 'neighbor-of', 'logic arc', 'equivalence arc']; + + elementUtilities.elementTypes = elementUtilities.nodeTypes + .concat( elementUtilities.edgeTypes ); + + /* + * Get sbgnclass of the given element. If the parameter is a string return it + * by assuming that it is the sbgnclass itself. + */ + elementUtilities.getSbgnClass = function( ele ) { + if ( ele == null ) { + return null; + } + + var sbgnclass = typeof ele === 'string' ? ele : ele.data('class'); + + return sbgnclass; + }; + + /* + * Get sbgn class omitting the multimer information + */ + elementUtilities.getPureSbgnClass = function( ele ) { + if ( ele == null ) { + return null; + } + + return elementUtilities.getSbgnClass( ele ).replace( ' multimer', '' ); + }; + + /* + * Returns if the elements with the given parent class can be parent of the elements with the given node class + */ + elementUtilities.isValidParent = function(_nodeClass, _parentClass, node) { + // If nodeClass and parentClass params are elements itselves instead of their class names handle it + var nodeClass = typeof _nodeClass !== 'string' ? _nodeClass.data('class') : _nodeClass; + var parentClass = _parentClass != undefined && typeof _parentClass !== 'string' ? _parentClass.data('class') : _parentClass; + + if (parentClass == undefined || parentClass === 'compartment' + || parentClass === 'submap') { // Compartments, submaps and the root can include any type of nodes + return true; + } + else if (parentClass.startsWith('complex') && (!node || node.connectedEdges().length == 0 // Complexes can only include EPNs which do not have edges + || elementUtilities.mapType == "HybridAny" ||elementUtilities.mapType == "HybridSbgn")) { // When map type is unknown, allow complexes to include EPNs with edges + return elementUtilities.isEPNClass(nodeClass); + } + + return false; // Currently just 'compartment' and 'complex' compounds are supported return false for any other parentClass + }; + + // Get common properties of given elements. Returns null if the given element list is empty or the + // property is not common for all elements. dataOrCss parameter specify whether to check the property on data or css. + // The default value for it is data. If propertyName parameter is given as a function instead of a string representing the + // property name then use what that function returns. + elementUtilities.getCommonProperty = function (elements, propertyName, dataOrCss) { + if (elements.length == 0) { + return null; + } + + var isFunction; + // If we are not comparing the properties directly users can specify a function as well + if (typeof propertyName === 'function') { + isFunction = true; + } + + // Use data as default + if (!isFunction && !dataOrCss) { + dataOrCss = 'data'; + } + + var getVal = function( index ) { + var val = isFunction ? propertyName(elements[index]) : elements[index][dataOrCss](propertyName); + return val; + } + + var value = getVal( 0 ); + + for (var i = 1; i < elements.length; i++) { + if ( getVal( i ) != value) { + return null; + } + } + + return value; + }; + + // Returns if the function returns a truthy value for all of the given elements. + elementUtilities.trueForAllElements = function (elements, fcn) { + for (var i = 0; i < elements.length; i++) { + if (!fcn(elements[i])) { + return false; + } + } + + return true; + }; + + // Returns whether the give element can have sbgncardinality + elementUtilities.canHaveSBGNCardinality = function (ele) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ) + + return sbgnclass == 'consumption' || sbgnclass == 'production'; + }; + + // Returns whether the give element can have sbgnlabel + elementUtilities.canHaveSBGNLabel = function (ele) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + + return sbgnclass != 'and' && sbgnclass != 'or' && sbgnclass != 'not' && sbgnclass != 'delay' + && sbgnclass != 'association' && sbgnclass != 'dissociation' && sbgnclass != 'source and sink' && !sbgnclass.endsWith('process'); + }; + + // Returns whether the give element have unit of information + elementUtilities.canHaveUnitOfInformation = function (ele) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + + if (sbgnclass == 'simple chemical' + || sbgnclass == 'macromolecule' || sbgnclass == 'nucleic acid feature' + || sbgnclass == 'complex' || sbgnclass == 'simple chemical multimer' + || sbgnclass == 'macromolecule multimer' || sbgnclass == 'nucleic acid feature multimer' + || sbgnclass == 'complex multimer' || (sbgnclass.startsWith('BA') && sbgnclass != "BA plain") + || sbgnclass == 'compartment' || sbgnclass == 'SIF macromolecule' || sbgnclass == 'SIF simple chemical') { + return true; + } + return false; + }; + + // Returns whether the given element can have more than one units of information + elementUtilities.canHaveMultipleUnitOfInformation = function (ele) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + return !sbgnclass.startsWith('BA'); + }; + + + // Returns whether the give element have state variable + elementUtilities.canHaveStateVariable = function (ele) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + + if (sbgnclass == 'macromolecule' || sbgnclass == 'nucleic acid feature' + || sbgnclass == 'complex' + || sbgnclass == 'macromolecule multimer' || sbgnclass == 'nucleic acid feature multimer' + || sbgnclass == 'complex multimer') { + return true; + } + return false; + }; + + // Returns whether the given ele should be square in shape + elementUtilities.mustBeSquare = function (ele) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + + return (sbgnclass.indexOf('process') != -1 || sbgnclass == 'source and sink' + || sbgnclass == 'and' || sbgnclass == 'or' || sbgnclass == 'not' + || sbgnclass == 'association' || sbgnclass == 'dissociation' || sbgnclass == 'delay'); + }; + + // Returns whether any of the given nodes must not be in square shape + elementUtilities.someMustNotBeSquare = function (nodes) { + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + if (!elementUtilities.mustBeSquare(node.data('class'))) { + return true; + } + } + + return false; + }; + + // Returns whether the gives element can be cloned + elementUtilities.canBeCloned = function (ele) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + + var list = { + 'unspecified entity': true, + 'macromolecule': true, + 'complex': true, + 'nucleic acid feature': true, + 'simple chemical': true, + 'perturbing agent': true + }; + + return list[sbgnclass] ? true : false; + }; + + // Returns whether the gives element can be cloned + elementUtilities.canBeMultimer = function (ele) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + + var list = { + 'macromolecule': true, + 'complex': true, + 'nucleic acid feature': true, + 'simple chemical': true + }; + + return list[sbgnclass] ? true : false; + }; + + elementUtilities.isBiologicalActivity = function( ele ) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + + return inArray( sbgnclass, elementUtilities.biologicalActivityTypes ); + }; + + elementUtilities.isSIFNode = function( ele ) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + + return inArray( sbgnclass, elementUtilities.sifTypes ); + }; + + elementUtilities.isSIFEdge = function( ele ) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + + return inArray( sbgnclass, elementUtilities.sifEdgeTypes ); + }; + + elementUtilities.isUndirectedEdge = function( ele ) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + + return inArray( sbgnclass, elementUtilities.undirectedEdgeTypes ); + }; + + elementUtilities.isDirectedEdge = function( ele ) { + return !elementUtilities.isUndirectedEdge( ele ); + }; + + // Returns whether the given element is an EPN + elementUtilities.isEPNClass = function (ele) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + + return inArray( sbgnclass, elementUtilities.epnTypes ); + }; + + // Returns whether the given element is a PN + elementUtilities.isPNClass = function (ele) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + + return inArray( sbgnclass, elementUtilities.processTypes ); + }; + + // Returns wether the given element or string is of the special empty set/source and sink class + elementUtilities.isEmptySetClass = function (ele) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + return sbgnclass == 'source and sink'; + }; + + // Returns whether the given element is a logical operator + elementUtilities.isLogicalOperator = function( ele ) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + return inArray( sbgnclass, elementUtilities.logicalOperatorTypes ); + }; + + // Returns whether the class of given element is a equivalance class + elementUtilities.convenientToEquivalence = function (ele) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + return (sbgnclass == 'tag' || sbgnclass == 'terminal'); + }; + + // Returns whether the class of given element is a modulation arc as defined in PD specs + elementUtilities.isModulationArcClass = function (ele) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + return (sbgnclass == 'modulation' + || sbgnclass == 'stimulation' || sbgnclass == 'catalysis' + || sbgnclass == 'inhibition' || sbgnclass == 'necessary stimulation'); + }; + + // Returns whether the class of given element is an arc of AF specs except logical arc + elementUtilities.isAFArcClass = function (ele) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + return (sbgnclass == 'positive influence' || sbgnclass == 'negative influence' + || sbgnclass == 'unknown influence' || sbgnclass == 'necessary stimulation'); + }; + + // Returns whether the given element or elements with the given class can have ports. + elementUtilities.canHavePorts = function (ele) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + return sbgnclass != 'phenotype' && sbgnclass != 'delay' + && ( elementUtilities.isLogicalOperator( sbgnclass ) + || elementUtilities.isPNClass( sbgnclass ) ); + }; + + // Section Start + // General Element Utilities + + //this method returns the nodes non of whose ancestors is not in given nodes + elementUtilities.getTopMostNodes = function (nodes) { + var nodesMap = {}; + for (var i = 0; i < nodes.length; i++) { + nodesMap[nodes[i].id()] = true; + } + var roots = nodes.filter(function (ele, i) { + if(typeof ele === "number") { + ele = i; + } + var parent = ele.parent()[0]; + while(parent != null){ + if(nodesMap[parent.id()]){ + return false; + } + parent = parent.parent()[0]; + } + return true; + }); + + return roots; + }; + + //This method checks if all of the given nodes have the same parent assuming that the size + //of nodes is not 0 + elementUtilities.allHaveTheSameParent = function (nodes) { + if (nodes.length == 0) { + return true; + } + var parent = nodes[0].data("parent"); + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + if (node.data("parent") != parent) { + return false; + } + } + return true; + }; + + elementUtilities.moveNodes = function(positionDiff, nodes, notCalcTopMostNodes) { + var topMostNodes = notCalcTopMostNodes ? nodes : this.getTopMostNodes(nodes); + for (var i = 0; i < topMostNodes.length; i++) { + var node = topMostNodes[i]; + var oldX = node.position("x"); + var oldY = node.position("y"); + node.position({ + x: oldX + positionDiff.x, + y: oldY + positionDiff.y + }); + var children = node.children(); + this.moveNodes(positionDiff, children, true); + } + }; + + elementUtilities.convertToModelPosition = function (renderedPosition) { + var pan = cy.pan(); + var zoom = cy.zoom(); + + var x = (renderedPosition.x - pan.x) / zoom; + var y = (renderedPosition.y - pan.y) / zoom; + + return { + x: x, + y: y + }; + }; + + elementUtilities.convertToRenderedPosition = function (modelPos, pan, zoom) { + pan = pan || cy.pan(); + zoom = zoom || cy.zoom(); + + var res = {}; + + ['x', 'y'].forEach( function(dim) { + res[dim] = modelPos[dim] * zoom + pan[dim]; + } ); + + return res; + }; + + // Section End + // General Element Utilities + + // Section Start + // Element Filtering Utilities + + // SBGN specific utilities + + elementUtilities.getProcessesOfSelected = function () { + var selectedEles = cy.elements(":selected"); + selectedEles = this.extendNodeList(selectedEles); + return selectedEles; + }; + + elementUtilities.getNeighboursOfSelected = function(){ + var selectedEles = cy.elements(":selected"); + var elesToHighlight = this.getNeighboursOfNodes(selectedEles); + return elesToHighlight; + }; + + elementUtilities.getNeighboursOfNodes = function(_nodes){ + var nodes = _nodes.nodes(); // Ensure that nodes list just include nodes + nodes = nodes.add(nodes.parents("node[class^='complex']")); + nodes = nodes.add(nodes.descendants()); + var neighborhoodEles = nodes.neighborhood(); + var elesToReturn = nodes.add(neighborhoodEles); + elesToReturn = elesToReturn.add(elesToReturn.descendants()); + return elesToReturn; + }; + + elementUtilities.extendNodeList = function (nodesToShow) { + var self = this; + //add children + nodesToShow = nodesToShow.add(nodesToShow.nodes().descendants()); + //add parents + nodesToShow = nodesToShow.add(nodesToShow.parents()); + //add complex children + nodesToShow = nodesToShow.add(nodesToShow.nodes("node[class^='complex']").descendants()); + + // var processes = nodesToShow.nodes("node[class='process']"); + // var nonProcesses = nodesToShow.nodes("node[class!='process']"); + // var neighborProcesses = nonProcesses.neighborhood("node[class='process']"); + + extendNodeTypes = ['process', 'omitted process', 'uncertain process', + 'association', 'dissociation', 'phenotype', 'and', 'or', 'not', 'delay']; + + //Here, logical operators are also considered as processes, since they also get inputs and outputs + var processes = nodesToShow.filter(function(ele, i){ + if(typeof ele === "number") { + ele = i; + } + return inArray(ele._private.data.class, extendNodeTypes); + }); + var nonProcesses = nodesToShow.filter(function(ele, i){ + if(typeof ele === "number") { + ele = i; + } + return !inArray(ele._private.data.class, extendNodeTypes); + }); + var neighborProcesses = nonProcesses.neighborhood().union(processes.neighborhood()).filter(function(ele, i){ + if(typeof ele === "number") { + ele = i; + } + return inArray(ele._private.data.class, extendNodeTypes); + }); + //For AF support, subject to change + var neighborNonProcesses = nonProcesses.union(nonProcesses.neighborhood(":hidden")).filter(function(ele, i){ + if(typeof ele === "number") { + ele = i; + } + return !inArray(ele._private.data.class, extendNodeTypes); + }); + + nodesToShow = nodesToShow.add(processes.neighborhood()); + nodesToShow = nodesToShow.add(neighborProcesses); + nodesToShow = nodesToShow.add(neighborProcesses.neighborhood()); + nodesToShow = nodesToShow.add(neighborNonProcesses); + + neighborProcesses.neighborhood().forEach(function(ele){ + if(inArray(ele._private.data.class, extendNodeTypes)) + { + nodesToShow = nodesToShow.add(ele.neighborhood()); + } + }); + + //add parents + nodesToShow = nodesToShow.add(nodesToShow.nodes().parents()); + //add children + nodesToShow = nodesToShow.add(nodesToShow.nodes("node[class^='complex']").descendants()); + + return nodesToShow; + }; + + elementUtilities.extendRemainingNodes = function (nodesToFilter, allNodes) { + nodesToFilter = this.extendNodeList(nodesToFilter); + var nodesToShow = allNodes.not(nodesToFilter); + nodesToShow = this.extendNodeList(nodesToShow); + return nodesToShow; + }; + + elementUtilities.getProcessesOfNodes = function(nodes) { + return this.extendNodeList(nodes); + }; + + // general utilities + + elementUtilities.noneIsNotHighlighted = function () { + var viewUtilities = cy.viewUtilities('get'); + var highlightClasses = viewUtilities.getAllHighlightClasses(); + for (var i = 0; i < highlightClasses.length; i++) { + if (cy.$('.' + highlightClasses[i]).is(':visible')) { + return false; + } + } + return true; + }; + + // Section End + // Element Filtering Utilities + + // Section Start + // Add remove utilities + + // SBGN specific utilities + + elementUtilities.deleteNodesSmart = function (_nodes) { + var nodes = _nodes.nodes(); // Ensure that nodes list just include nodes + + var allNodes = cy.nodes(); + cy.elements().unselect(); + var nodesToKeep = this.extendRemainingNodes(nodes, allNodes); + var nodesNotToKeep = allNodes.not(nodesToKeep); + return nodesNotToKeep.remove(); + }; + + elementUtilities.deleteElesSimple = function (eles) { + cy.elements().unselect(); + return eles.remove(); + }; + + // general utilities + + elementUtilities.restoreEles = function (eles) { + eles.restore(); + return eles; + }; + + // Section End + // Add remove utilities + + // Section Start + // Stylesheet helpers + + // SBGN specific utilities + + elementUtilities.getArrayLineStyle = function (ele) { + var sbgnclass = elementUtilities.getPureSbgnClass( ele ); + + switch (sbgnclass) { + case 'controls-expression-of': case 'upregulates-expression': + case 'downregulates-expression': + return 'dashed'; + default: + return 'solid'; + } + }; + + elementUtilities.getCyShape = function (ele) { + var _class = ele.data('class'); + // Get rid of rectangle postfix to have the actual node class + if (_class.endsWith(' multimer')) { + _class = _class.replace(' multimer', ''); + } + + if (_class == 'compartment') { + return 'compartment'; + } + if (_class == 'phenotype') { + return 'hexagon'; + } + if (_class == 'perturbing agent' || _class == 'tag') { + return 'polygon'; + } + if (_class == 'SIF macromolecule') { + return 'macromolecule'; + } + if (_class == 'SIF simple chemical') { + return 'simple chemical'; + } + + if (_class.startsWith('BA')){ + return 'biological activity'; + } + + if (_class == 'submap' || _class == 'topology group'){ + return 'rectangle'; + } + + // We need to define new node shapes with their class names for these nodes + if (_class == 'source and sink' || _class == 'nucleic acid feature' || _class == 'macromolecule' + || _class == 'simple chemical' || _class == 'complex' || _class == 'biological activity' ) { + return _class; + } + + // These shapes can have ports. If they have ports we represent them by polygons, else they are represented by ellipses or rectangles + // conditionally. + if ( this.canHavePorts(_class) ) { + + if (graphUtilities.portsEnabled === true && ele.data('ports').length === 2) { + return 'polygon'; // The node has ports represent it by polygon + } + else if (_class == 'process' || _class == 'omitted process' || _class == 'uncertain process') { + return 'rectangle'; // If node has no port and has one of these classes it should be in a rectangle shape + } + + return 'ellipse'; // Other nodes with no port should be in an ellipse shape + } + + // The remaining nodes are supposed to be in ellipse shape + return 'ellipse'; + }; + + elementUtilities.getCyTargetArrowFill = function(ele) { + var _class = ele.data('class'); + + if ( _class == 'inhibition' || _class == 'negative influence' || + _class == 'production' || elementUtilities.isSIFEdge( _class ) ) { + return 'filled'; + } + + return 'hollow'; + }; + + elementUtilities.getCyArrowShape = function(ele) { + var _class = ele.data('class'); + + switch ( _class ) { + case 'necessary stimulation': + return 'triangle-cross'; + case 'inhibition': case 'negative influence': case 'inhibits': + case 'downregulates-expression': case 'dephosphorylates': + return 'tee'; + case 'catalysis': + return 'circle'; + case 'stimulation': case 'production': case 'positive influence': + case 'activates': case 'phosphorylates': case 'upregulates-expression': + case 'controls-state-change-of': case 'chemical-affects': + case 'controls-transport-of': case 'controls-phosphorylation-of': + case 'controls-expression-of': case 'catalysis-precedes': + case 'consumption-controled-by': case 'controls-production-of': + case 'controls-transport-of-chemical': case 'used-to-produce': + return 'triangle'; + case 'modulation': case 'unknown influence': + return 'diamond'; + default: + return 'none'; + } + }; + + elementUtilities.getElementContent = function(ele) { + var _class = ele.data('class'); + + if (_class.endsWith(' multimer')) { + _class = _class.replace(' multimer', ''); + } + + var content = ""; + if (_class == 'macromolecule' || _class == 'simple chemical' + || _class == 'phenotype' + || _class == 'unspecified entity' || _class == 'nucleic acid feature' + || _class == 'perturbing agent' || _class == 'tag' + || _class == 'biological activity' || _class.startsWith('BA') + || _class == 'submap' || _class == 'SIF macromolecule' + || _class == 'SIF simple chemical') { + content = ele.data('label') ? ele.data('label') : ""; + } + else if(_class == 'compartment'){ + content = ele.data('label') ? ele.data('label') : ""; + } + else if(_class == 'complex'){ + if(ele.children().length == 0 || options.showComplexName){ + if(ele.data('label')){ + content = ele.data('label'); + } + else if(ele.data('infoLabel')){ + content = ele.data('infoLabel'); + } + else{ + content = ''; + } + } + else{ + content = ''; + } + } + else if (_class == 'and') { + content = 'AND'; + } + else if (_class == 'or') { + content = 'OR'; + } + else if (_class == 'not') { + content = 'NOT'; + } + else if (_class == 'omitted process') { + content = '\\\\'; + } + else if (_class == 'uncertain process') { + content = '?'; + } + else if (_class == 'dissociation') { + content = 'o'; + } + else if (_class == 'delay'){ + content = '\u03C4'; // tau + } + + var textWidth = ele.outerWidth() || ele.data('bbox').w; + + var textProp = { + label: content, + width: ( _class == 'perturbing agent' ? textWidth / 2 : textWidth) + }; + + return textProp.label; + }; + + elementUtilities.getLabelTextSize = function (ele) { + var _class = ele.data('class'); + // These types of nodes cannot have label but this is statement is needed as a workaround + if (_class === 'association') { + return 20; + } + + if (this.canHavePorts(_class)) { + var coeff = 1; // The dynamic label size coefficient for these pseudo labels, it is 1 for logical operators + + // Coeff is supposed to be 2 for dissociation and 1.5 for other processes + if (_class === 'dissociation') { + coeff = 2; + } + else if (_class.endsWith('process')) { + coeff = 1.5; + } + + var ports = ele.data('ports'); + + if (graphUtilities.portsEnabled === true && ports.length === 2) { + // We assume that the ports are symmetric to the node center so using just one of the ports is enough + var port = ports[0]; + var orientation = port.x === 0 ? 'vertical' : 'horizontal'; + // This is the ratio of the area occupied with ports over without ports + var ratio = orientation === 'vertical' ? Math.abs(port.y) / 50 : Math.abs(port.x) / 50; + coeff /= ratio; // Divide the coeff by ratio to fit into the bbox of the actual shape (discluding ports) + } + + return this.getDynamicLabelTextSize(ele, coeff); + } + + if (_class === 'delay'){ + return this.getDynamicLabelTextSize(ele, 2); + } + + return this.getDynamicLabelTextSize(ele); + }; + + elementUtilities.getStateVarShapeOptions = function(ele) { + if ( !elementUtilities.canHaveStateVariable( ele ) ) { + return null; + } + + return ['stadium']; + }; + + elementUtilities.getUnitOfInfoShapeOptions = function(ele) { + var type = elementUtilities.getPureSbgnClass(ele); + + if ( !elementUtilities.canHaveUnitOfInformation( type ) ) { + return null; + } + + var opts = null; + + if ( elementUtilities.isSIFNode( type ) ) { + opts = ['rectangle', 'stadium']; + } + else if ( elementUtilities.isBiologicalActivity( type ) ) { + switch (type) { + case 'BA macromolecule': + opts = ['roundrectangle']; + break; + case 'BA nucleic acid feature': + opts = ['bottomroundrectangle']; + break; + case 'BA unspecified entity': + opts = ['ellipse']; + break; + case 'BA complex': + opts = ['complex']; + break; + case 'BA perturbing agent': + opts = ['perturbing agent']; + break; + case 'BA simple chemical': + opts = ['stadium']; + break; + default: + break; + } + } + else { + opts = ['rectangle']; + } + + return opts; + }; + + elementUtilities.getCardinalityDistance = function (ele) { + var srcPos = ele.source().position(); + var tgtPos = ele.target().position(); + + var distance = Math.sqrt(Math.pow((srcPos.x - tgtPos.x), 2) + Math.pow((srcPos.y - tgtPos.y), 2)); + return distance * 0.15; + }; + + elementUtilities.getInfoLabel = function(node) { + /* Info label of a collapsed node cannot be changed if + * the node is collapsed return the already existing info label of it + */ + if (node._private.data.collapsedChildren != null) { + return node._private.data.infoLabel; + } + + /* + * If the node is simple then it's infolabel is equal to it's label + */ + if (node.children() == null || node.children().length == 0) { + return node._private.data.label; + } + + var children = node.children(); + var infoLabel = ""; + /* + * Get the info label of the given node by it's children info recursively + */ + for (var i = 0; i < children.length; i++) { + var child = children[i]; + var childInfo = this.getInfoLabel(child); + if (childInfo == null || childInfo == "") { + continue; + } + + if (infoLabel != "") { + infoLabel += ":"; + } + infoLabel += childInfo; + } + + //return info label + return infoLabel; + }; + + elementUtilities.getQtipContent = function(node) { + /* Check the label of the node if it is not valid + * then check the infolabel if it is also not valid do not show qtip + */ + var label = node.data('label'); + if (label == null || label == "") { + label = this.getInfoLabel(node); + } + if (label == null || label == "") { + return; + } + + var contentHtml = "" + label + ""; + var statesandinfos = node._private.data.statesandinfos; + for (var i = 0; i < statesandinfos.length; i++) { + var sbgnstateandinfo = statesandinfos[i]; + if (sbgnstateandinfo.clazz == "state variable") { + var value = sbgnstateandinfo.state.value; + var variable = sbgnstateandinfo.state.variable; + var stateLabel = (variable == null /*|| typeof stateVariable === undefined */) ? value : + value + "@" + variable; + if (stateLabel == null) { + stateLabel = ""; + } + contentHtml += "
" + stateLabel + "
"; + } + else if (sbgnstateandinfo.clazz == "unit of information") { + var stateLabel = sbgnstateandinfo.label.text; + if (stateLabel == null) { + stateLabel = ""; + } + contentHtml += "
" + stateLabel + "
"; + } + } + return contentHtml; + }; + + // general utilities + + elementUtilities.getDynamicLabelSizeCoefficient = function( dynamicLabelSize ) { + var map = { + 'small': 0.75, + 'regular': 1, + 'large': 1.25 + }; + + return map[ dynamicLabelSize ]; + }; + + elementUtilities.getDynamicLabelTextSize = function (ele, dynamicLabelSizeCoefficient) { + var sbgnclass, h; + + // ele can either be node itself or an object that has class and height fields + if ( ele.isNode && ele.isNode() ) { + sbgnclass = ele.data( 'class' ); + h = ele.height(); + } + else { + sbgnclass = ele[ 'class' ]; + h = ele[ 'height' ]; + } + + var dynamicLabelSize = options.dynamicLabelSize; + dynamicLabelSize = typeof dynamicLabelSize === 'function' ? dynamicLabelSize.call() : dynamicLabelSize; + + if (dynamicLabelSizeCoefficient === undefined) { + if (dynamicLabelSize == 'small') { + if (sbgnclass.startsWith("complex")) + return 10; + else if (sbgnclass == "compartment" || sbgnclass == "submap") + return 12; + } + else if (dynamicLabelSize == 'regular') { + if (sbgnclass.startsWith("complex")) + return 11; + else if (sbgnclass == "compartment" || sbgnclass == "submap") + return 14; + } + else if (dynamicLabelSize == 'large') { + if (sbgnclass.startsWith("complex")) + return 12; + else if (sbgnclass == "compartment" || sbgnclass == "submap") + return 16; + } + + dynamicLabelSizeCoefficient = elementUtilities.getDynamicLabelSizeCoefficient( dynamicLabelSize ); + } + + var textHeight = parseInt(h / 2.45) * dynamicLabelSizeCoefficient; + + return textHeight; + }; + + /* + * Get source/target end point of edge in 'x-value% y-value%' format. It returns 'outside-to-node' if there is no source/target port. + */ + elementUtilities.getEndPoint = function(edge, sourceOrTarget) { + var portId = sourceOrTarget === 'source' ? edge.data('portsource') : edge.data('porttarget'); + + if (portId == null || !graphUtilities.portsEnabled) { + return 'outside-to-node'; // If there is no portsource return the default value which is 'outside-to-node' + } + + var endNode = sourceOrTarget === 'source' ? edge.source() : edge.target(); + var ports = endNode.data('ports'); + var port; + for (var i = 0; i < ports.length; i++) { + if (ports[i].id === portId) { + port = ports[i]; + } + } + + if (port === undefined) { + return 'outside-to-node'; // If port is not found return the default value which is 'outside-to-node' + } + + var x, y; + // Note that for drawing ports we represent the whole shape by a polygon and ports are always 50% away from the node center + if (port.x != 0) { + x = Math.sign(port.x) * 50; + y = 0; + } + else { + x = 0; + y = Math.sign(port.y) * 50; + } + + return '' + x + '% ' + y + '%'; + }; + + /* + * Return ordering of ports of a node. + * Possible return values are 'L-to-R', 'R-to-L', 'T-to-B', 'B-to-T', 'none' + */ + elementUtilities.getPortsOrdering = function(node) { + // Return the cached portsordering if exists + if (node.data('portsordering')) { + return node.data('portsordering'); + } + + var ports = node.data('ports'); + if (ports.length !== 2) { + node.data('portsordering', 'none'); // Cache the ports ordering + return 'none'; // Nodes are supposed to have 2 nodes or none + } + + /* + * Retursn 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) { + for (var i = 0; i < edges.length; i++) { + if (edges[i].data('porttarget') === portId) { + return true; + } + } + + return false; + }; + + // If the ports are located above/below of the node then the orientation is 'vertical' else it is 'horizontal'. + var orientation = ports[0].x === 0 ? 'vertical' : 'horizontal'; + // We need the connected edges of the node to find out if a port is an input port or an output port + var connectedEdges = node.connectedEdges(); + + var portsordering; + if (orientation === 'horizontal') { + var leftPortId = ports[0].x < 0 ? ports[0].id : ports[1].id; // Left port is the port whose x value is negative + // If left port is port target for any of connected edges then the ordering is 'L-to-R' else it is 'R-to-L' + if (isPortTargetOfAnyEdge(connectedEdges, leftPortId)) { + portsordering = 'L-to-R'; + } + else { + portsordering = 'R-to-L'; + } + } + else { + var topPortId = ports[0].y < 0 ? ports[0].id : ports[1].id; // Top port is the port whose y value is negative + // If top port is port target for any of connected edges then the ordering is 'T-to-B' else it is 'B-to-T' + if (isPortTargetOfAnyEdge(connectedEdges, topPortId)) { + portsordering = 'T-to-B'; + } + else { + portsordering = 'B-to-T'; + } + } + + // Cache the portsordering and return it. + node.data('portsordering', portsordering); + return portsordering; + }; + + /* + * Sets the ordering of the given nodes. + * Ordering options are 'L-to-R', 'R-to-L', 'T-to-B', 'B-to-T', 'none'. + * If a node does not have any port before the operation and it is supposed to have some after operation the portDistance parameter is + * used to set the distance between the node center and the ports. The default port distance is 60. + */ + elementUtilities.setPortsOrdering = function( nodes, ordering, portDistance ) { + /* + * 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) { + for (var i = 0; i < edges.length; i++) { + if (edges[i].data('porttarget') === portId) { + return true; + } + } + + 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(); + + for ( var i = 0; i < nodes.length; i++ ) { + var node = nodes[i]; + var currentOrdering = this.getPortsOrdering(node); // The current ports ordering of the node + + // If the current ordering is already equal to the desired ordering pass this node directly + if ( ordering === currentOrdering ) { + continue; + } + + if ( ordering === 'none' ) { // If the ordering is 'none' remove the ports of the node + elementUtilities.removePorts(node); + } + else if ( currentOrdering === 'none' ) { // If the desired ordering is not 'none' but the current one is 'none' add ports with the given parameters. + elementUtilities.addPorts(node, ordering, portDistance); + } + else { // Else change the ordering by altering node 'ports' + var ports = node.data('ports'); // Ports of the node + // If currentOrdering is 'none' use the portDistance given by parameter else use the existing one + var dist = currentOrdering === 'none' ? portDistance : ( Math.abs( ports[0].x ) || Math.abs( ports[0].y ) ); + var connectedEdges = node.connectedEdges(); // The edges connected to the node + var portsource, porttarget; // The ports which are portsource/porttarget of the connected edges + + // Determine the portsource and porttarget + if ( isPortTargetOfAnyEdge(connectedEdges, ports[0].id) || isPortSourceOfAnyEdge(connectedEdges, ports[1].id) ) { + porttarget = ports[0]; + portsource = ports[1]; + } + else { + porttarget = ports[1]; + portsource = ports[0]; + } + + if ( ordering === 'L-to-R' ) { + // If ordering is 'L-to-R' the porttarget should be the left most port and the portsource should be the right most port + porttarget.x = -1 * dist; + portsource.x = dist; + porttarget.y = 0; + portsource.y = 0; + } + else if ( ordering === 'R-to-L' ) { + // If ordering is 'R-to-L' the porttarget should be the right most port and the portsource should be the left most port + porttarget.x = dist; + portsource.x = -1 * dist; + porttarget.y = 0; + portsource.y = 0; + } + else if ( ordering === 'T-to-B' ) { + // If ordering is 'T-to-B' the porttarget should be the top most port and the portsource should be the bottom most port + porttarget.x = 0; + portsource.x = 0; + porttarget.y = -1 * dist; + portsource.y = dist; + } + else { //if ordering is 'B-to-T' + // If ordering is 'B-to-T' the porttarget should be the bottom most port and the portsource should be the top most port + porttarget.x = 0; + portsource.x = 0; + porttarget.y = dist; + portsource.y = -1 * dist; + } + } + + node.data('ports', ports); // Reset the node ports + } + + nodes.data('portsordering', ordering); // Update the cached orderings of the nodes + cy.endBatch(); + }; + + /* + * Add ports to the given node, with given ordering and port distance. + */ + elementUtilities.addPorts = function(node, ordering, portDistance) { + var firstPortId = node.id() + ".1"; // Id of first port + var secondPortId = node.id() + ".2"; // Id of seconf port + // First port object x and y will be filled according to ordering, the first port is supposed to be the left most or the top most one + var firstPort = { id: firstPortId }; + // Second port object x and y will be filled according to ordering, the second port is supposed to be the right most or the bottom most one + var secondPort = { id: secondPortId }; + + // Complete port objects according to ordering + if ( ordering === 'L-to-R' || ordering === 'R-to-L' ) { + // If ordering is in horizontal axis first port is the left most one and the second port is the right most one + firstPort.x = -1 * portDistance; + secondPort.x = portDistance; + firstPort.y = 0; + secondPort.y = 0; + } + else { // If ordering is 'T-to-B' or 'B-to-T' + // If ordering is in vertical axis first port is the top most one and the second port is the bottom most one + firstPort.y = -1 * portDistance; + secondPort.y = portDistance; + firstPort.x = 0; + secondPort.x = 0; + } + + var fromLorT = ordering === 'L-to-R' || ordering === 'T-to-B'; // Check if ordering starts from left or top + var ports = [firstPort, secondPort]; // Ports array for the node + var connectedEdges = node.connectedEdges(); // The edges connected to the node + + cy.startBatch(); + + node.data('ports', ports); + + // Reset the portsource and porttarget for each edge connected to the node + for ( var i = 0; i < connectedEdges.length; i++ ) { + var edge = connectedEdges[i]; + var edgeClass = edge.data('class'); + /* + * If the node is the edge target we may need to set the porttarget of the edge to the input port of the node (First or second port accoring to the orientation) + * if it is the edge soruce we may need to set the portsource of the edge to the output port similarly. + * Note that if fron left or top (fromLorT) is true then the first port is the source port and second port is the target port, + * else it is vice versa. + * + */ + if ( edge.data('target') === node.id() ) { + if (edgeClass === 'production' || this.isModulationArcClass(edgeClass)) { + continue; // production or modulation type of edges cannot be connected to any port of target node (A production can have a process as target node but it is supposed to be connected to that node from its body, not from a port) + } + if ( fromLorT ) { + edge.data('porttarget', firstPortId); + } + else { + edge.data('porttarget', secondPortId); + } + } + else { + if (edgeClass === 'consumption') { + continue; // consumpiton edge cannot be connected to any port of source node + } + if ( fromLorT ) { + edge.data('portsource', secondPortId); + } + else { + edge.data('portsource', firstPortId); + } + } + } + + cy.endBatch(); + }; + + /* + * Remove the ports of the given node + */ + elementUtilities.removePorts = function(node) { + var connectedEdges = node.connectedEdges(); + var nodeId = node.id(); + + cy.startBatch(); + + // Reset portsource or porttarget of the connected edges to the node id + for ( var i = 0; i < connectedEdges.length; i++ ) { + var edge = connectedEdges[i]; + if ( edge.data('source') === nodeId ) { + edge.data('portsource', nodeId); + } + else { + edge.data('porttarget', nodeId); + } + } + + node.data('ports', []); // Clear ports data + + cy.endBatch(); + }; + + elementUtilities.changePortsOrientationAfterLayout = function() { + //Check all processes and logical operators with ports + cy.nodes().forEach(function(ele){ + if (ele.data('class') === 'process' || ele.data('class') === 'omitted process' || ele.data('class') === 'uncertain process' || ele.data('class') === 'association' || ele.data('class') === 'dissociation' || ele.data('class') === 'and' || ele.data('class') === 'or' || ele.data('class') === 'not') + { + if ( ele.data('ports').length === 2 ) + { + var bestOrientation = elementUtilities.changePortsOrientation(ele); + elementUtilities.setPortsOrdering(ele, bestOrientation); + // If improve-flow is checked we do the swaping of simple nodes with each other + var improveFlow = options.improveFlow; + improveFlow = typeof improveFlow === 'function' ? improveFlow.call() : improveFlow; + if (improveFlow) + { + elementUtilities.postChangePortsOrientation(ele, bestOrientation); + } + } + } + }); + cy.style().update(); + }; + + /* + Calculates the best orientation for an 'ele' with port (process or logical operator) and returns it. + */ + elementUtilities.changePortsOrientation = function(ele) { + var processId = ele.id(); + var orientation = {'L-to-R': 0, 'R-to-L' : 0, 'T-to-B' : 0, 'B-to-T' : 0}; + var targetingEdges = cy.edges("[target='"+processId+"']"); // Holds edges who have the input port as a target + var sourcingEdges = cy.edges("[source='"+processId+"']"); // Holds edges who have the output port as a source + // Checks if the ports belong to a process or logial operator, it does the calculations based on the edges connected to its ports + if (ele.data('class') === 'process' || ele.data('class') === 'omitted process' || ele.data('class') === 'uncertain process' || ele.data('class') === 'association' || ele.data('class') === 'dissociation') + { + targetingEdges.forEach(function(edge){ + if (edge.data('class') === 'consumption') + { + var source = cy.getElementById(edge.data('source')); //Holds the element from the other side of edge + var simple = false; //Checks if it is a simple node - connected with only 1 edge + if (source.connectedEdges().length === 1) + simple = true; + elementUtilities.calculateOrientationScore(ele, source, orientation, 'L-to-R', 'R-to-L', 'x', simple); + elementUtilities.calculateOrientationScore(ele, source, orientation, 'T-to-B', 'B-to-T', 'y', simple); + } + }); + sourcingEdges.forEach(function (edge) { + if (edge.data('class') === 'production') { + var target = cy.getElementById(edge.data('target')); + var simple = false; + if (target.connectedEdges().length === 1) + simple = true; + elementUtilities.calculateOrientationScore(ele, target, orientation, 'R-to-L', 'L-to-R', 'x', simple); + elementUtilities.calculateOrientationScore(ele, target, orientation, 'B-to-T', 'T-to-B', 'y', simple); + } + }); + } + else if (ele.data('class') === 'and' || ele.data('class') === 'or' || ele.data('class') === 'not') + { + targetingEdges.forEach(function(edge){ + if (edge.data('class') === 'logic arc') + { + var source = cy.getElementById(edge.data('source')); + var simple = false; + if (source.connectedEdges().length === 1) + simple = true; + elementUtilities.calculateOrientationScore(ele, source, orientation, 'L-to-R', 'R-to-L', 'x', simple); + elementUtilities.calculateOrientationScore(ele, source, orientation, 'T-to-B', 'B-to-T', 'y', simple); + } + }); + sourcingEdges.forEach(function (edge) { + if (edge.data('class') === 'modulation' || edge.data('class') === 'stimulation' || edge.data('class') === 'catalysis' || edge.data('class') === 'inhibition' || edge.data('class') === 'necessary stimulation' || edge.data('class') === 'logic arc') { + var target = cy.getElementById(edge.data('target')); + var simple = false; + if (target.connectedEdges().length === 1) + simple = true; + elementUtilities.calculateOrientationScore(ele, target, orientation, 'R-to-L', 'L-to-R', 'x', simple); + elementUtilities.calculateOrientationScore(ele, target, orientation, 'B-to-T', 'T-to-B', 'y', simple); + } + }); + } + //Calculates the best orientation from all orientation scores + var bestOrientation = "L-to-R"; + var bestScore = orientation['L-to-R'];//The score of the best orientation is always positive + for (var property in orientation) { + if (orientation[property] > bestScore) + { + bestScore = orientation[property]; + bestOrientation = property; + } + } + return bestOrientation; + }; + + /* + This function calculates the scores for each orientation + @param ele - is the node (process, logical operator) whose orientation will be changed. It can be process,omitted process, + uncertain process, association, dissociation, logical operator + @param other - is the other node, and based on its position scores are given to orientations + @param orientation - holds scores for each orientation + @param firstOrientation - can be L-to-R or T-to-B + @param oppositeOrientation - opposite of the upper orientation (R-to-L , B-to-T) + @param pos - can be 'x' or 'y' (based on vertical or horizontal direction of ports) + @param simple - checks if 'other' node is simple node (with degree 1) + */ + elementUtilities.calculateOrientationScore = function(ele, other, orientation, firstOrientation, oppositeOrientation, pos, simple) { + var coeff = 0.5; + var score = 2; + if (simple) + score = 1; // If it is a simple node, its score should affect less + var nodeWidthOrHeight = 0; + if (pos === 'x') + nodeWidthOrHeight = ele.width()/2; + else if (pos ==='y') + nodeWidthOrHeight = ele.height()/2; + if (other.position(pos) < ele.position(pos) - nodeWidthOrHeight) + { + orientation[firstOrientation] += score; + orientation[oppositeOrientation] -= score; + } + else if (other.position(pos) >= ele.position(pos) - nodeWidthOrHeight && other.position(pos) <= ele.position(pos) + nodeWidthOrHeight) + { + orientation[firstOrientation] += (ele.position(pos) - other.position(pos))/nodeWidthOrHeight*coeff; + orientation[oppositeOrientation] -= (ele.position(pos) - other.position(pos))/nodeWidthOrHeight*coeff; + } + else if (other.position(pos) > ele.position(pos) + nodeWidthOrHeight) + { + orientation[firstOrientation] -= score; + orientation[oppositeOrientation] += score; + } + }; + + /* + After a process is oriented, for each simple node that is on the wrong side of the port, + we try to find another simple node of degree 0 on the opposite side and swap them afterwards. + If from the opposide side we cannot find such a node then we try to swap it with an effector node of degree 1 + */ + elementUtilities.postChangePortsOrientation = function(ele, bestOrientation) { + var processId = ele.id(); + var inputPort = []; // Holds all simple nodes connected with input port + var outputPort = []; // Holds all simple nodes connected with output port + var notConnectedToPort = []; // Holds all simple nodes not connected with input or output port + var targetingEdges = cy.edges("[target='"+processId+"']"); + var sourcingEdges = cy.edges("[source='"+processId+"']"); + // Checks simple nodes and add them to one of the arrays mentioned above + if (ele.data('class') === 'process' || ele.data('class') === 'omitted process' || ele.data('class') === 'uncertain process' || ele.data('class') === 'association' || ele.data('class') === 'dissociation') + { + targetingEdges.forEach(function(edge){ + var source = cy.getElementById(edge.data('source')); + if(!source.isParent()){ + if (edge.data('class') === 'consumption') + { + elementUtilities.addSimpleNodeToArray(ele, source, bestOrientation, inputPort, "input"); + } + else + { + elementUtilities.addSimpleNodeToArray(ele, source, bestOrientation, notConnectedToPort, "notConnected"); + } + } + }); + sourcingEdges.forEach(function (edge) { + var target = cy.getElementById(edge.data('target')); + if(!target.isParent()){ + if (edge.data('class') === 'production') { + elementUtilities.addSimpleNodeToArray(ele, target, bestOrientation, outputPort, "output"); + } + else + { + elementUtilities.addSimpleNodeToArray(ele, target, bestOrientation, notConnectedToPort, "notConnected"); + } + } + }); + } + else if (ele.data('class') === 'and' || ele.data('class') === 'or' || ele.data('class') === 'not') + { + targetingEdges.forEach(function(edge){ + var source = cy.getElementById(edge.data('source')); + if(!source.isParent()){ + if (edge.data('class') === 'logic arc') + { + elementUtilities.addSimpleNodeToArray(ele, source, bestOrientation, inputPort, "input"); + } + else + { + elementUtilities.addSimpleNodeToArray(ele, source, bestOrientation, notConnectedToPort, "notConnected"); + } + } + }); + sourcingEdges.forEach(function (edge) { + var target = cy.getElementById(edge.data('target')); + if(!target.isParent()){ + if (edge.data('class') === 'modulation' || edge.data('class') === 'stimulation' || edge.data('class') === 'catalysis' || edge.data('class') === 'inhibition' || edge.data('class') === 'necessary stimulation' || edge.data('class') === 'logic arc') + { + elementUtilities.addSimpleNodeToArray(ele, target, bestOrientation, outputPort, "output"); + } + else + { + elementUtilities.addSimpleNodeToArray(ele, target, bestOrientation, notConnectedToPort, "notConnected"); + } + } + }); + } + //The arrays are sorted in order to keep the high priority of nodes positioned completely to the other side + inputPort.sort(function(a, b){return b.score - a.score}); + outputPort.sort(function(a, b){return b.score - a.score}); + notConnectedToPort.sort(function(a, b){return a.score - b.score}); + //First we check for direct swaping between nodes from different ports positioned to the wrong side + var minLength = inputPort.length; + if (outputPort.length < minLength) + minLength = outputPort.length; + for (i = 0; i < minLength; i++) + { + var inputPortEle = inputPort.pop(); + var outputPortEle = outputPort.pop(); + //Checks if free nodes belong to the same compound + var firstNode = cy.getElementById(inputPortEle.id); + var secondNode = cy.getElementById(outputPortEle.id); + if (firstNode.data('parent') !== secondNode.data('parent')) + { + continue; + } + elementUtilities.swapElements(inputPortEle, outputPortEle); + } + /* + After that we iterate over each element of effector nodes and see the scores it produces by swaping + with nodes connected to input or output ports + */ + for (i = notConnectedToPort.length -1; i >= 0 ; i--) + { + var effector = notConnectedToPort[i]; + if (outputPort.length > 0) + { + var firstOutput = outputPort[outputPort.length - 1]; + //Checks if free nodes belong to the same compound + var firstNode = cy.getElementById(effector.id); + var secondNode = cy.getElementById(firstOutput.id); + if (firstNode.data('parent') !== secondNode.data('parent')) + { + continue; + } + + elementUtilities.swapElements(effector, firstOutput); + var firstOutputScore = -elementUtilities.checkNegativeOrientationScore(ele, cy.getElementById(firstOutput.id), bestOrientation); + if ( firstOutputScore > firstOutput.score) + { + outputPort.pop(); + } + else + elementUtilities.swapElements(effector, firstOutput); //swap back + } + else if (inputPort.length > 0) + { + var firstInput = inputPort[inputPort.length - 1]; + //Checks if free nodes belong to the same compound + var firstNode = cy.getElementById(effector.id); + var secondNode = cy.getElementById(firstInput.id); + if (firstNode.data('parent') !== secondNode.data('parent')) + { + continue; + } + + elementUtilities.swapElements(effector, firstInput); + var firstInputScore = elementUtilities.checkNegativeOrientationScore(ele, cy.getElementById(firstInput.id), bestOrientation); + if ( firstInputScore > firstInput.score) + { + inputPort.pop(); + } + else + elementUtilities.swapElements(effector, firstInput); + } + } + }; + + /* + * Adds simple nodes when they have negative score to inputPort, outputPort or notConnectedPort arrays + * */ + elementUtilities.addSimpleNodeToArray = function(ele, other, orientation, array, connectedTo) { + if (other.connectedEdges().length === 1) + { + var nodeScore; + var obj = {}; + if (connectedTo === "notConnected") + { + nodeScore = Math.abs(elementUtilities.checkNegativeOrientationScore(ele, other, orientation)); + obj['id'] = other.id(); + obj['score'] = nodeScore; + array.push(obj); + } + else + { + if (connectedTo === "input") + nodeScore = elementUtilities.checkNegativeOrientationScore(ele, other, orientation); + else if (connectedTo === "output") + nodeScore = -elementUtilities.checkNegativeOrientationScore(ele, other, orientation); + if (nodeScore < 0) //if it is in the wrong side we add it to the input array + { + obj['id'] = other.id(); + obj['score'] = nodeScore; + array.push(obj); + } + } + } + }; + + /* + This function calculates the score of a node based on its position with respect to a process/logical operator + @param ele - is the node with the ports. It can be process,omitted process, + uncertain process, association, dissociation, logical operator + @param other - is the other node, and based on its position score of a node is calculated + @param orientation - A string which holds current best orientation + */ + elementUtilities.checkNegativeOrientationScore = function(ele, other, orientation) { + var coeff = 0.5; + var score = 1; + if (orientation === 'L-to-R' || orientation === 'R-to-L') + { + var nodeWidth = ele.width()/2; + if (other.position('x') < ele.position('x') - nodeWidth) + { + if (orientation === 'L-to-R') + return score; + else if (orientation === 'R-to-L') + return -score; + } + else if (other.position('x') >= ele.position('x') - nodeWidth && other.position('x') <= ele.position('x') + nodeWidth) + { + if (orientation === 'L-to-R') + return (ele.position('x') - other.position('x'))/nodeWidth*coeff; + else if (orientation === 'R-to-L') + return -(ele.position('x') - other.position('x'))/nodeWidth*coeff; + } + else if (other.position('x') > ele.position('x') + nodeWidth) + { + if (orientation === 'L-to-R') + return -score; + else if (orientation === 'R-to-L') + return score; + } + } + if (orientation === 'T-to-B' || orientation === 'B-to-T') + { + var nodeHeight = ele.height()/2; + if (other.position('y') < ele.position('y') - nodeHeight) + { + if (orientation === 'T-to-B') + return score; + else if (orientation === 'B-to-T') + return -score; + } + else if (other.position('y') >= ele.position('y') - nodeHeight && other.position('y') <= ele.position('y') + nodeHeight) + { + if (orientation === 'T-to-B') + return (ele.position('y') - other.position('y'))/nodeHeight*coeff; + else if (orientation === 'B-to-T') + return -(ele.position('y') - other.position('y'))/nodeHeight*coeff; + } + else if (other.position('y') > ele.position('y') + nodeHeight) + { + if (orientation === 'T-to-B') + return -score; + else if (orientation === 'B-to-T') + return score; + } + } + }; + + /* + Swaps the positions of 2 elements + */ + elementUtilities.swapElements = function(firstEle, secondEle) { + var firstNode = cy.getElementById(firstEle.id); + var secondNode = cy.getElementById(secondEle.id); + var tempx = firstNode.position('x'); + var tempy = firstNode.position('y'); + firstNode.position('x', secondNode.position('x')); + firstNode.position('y', secondNode.position('y')); + secondNode.position('x', tempx); + secondNode.position('y', tempy); + }; + + // used for handling the variable property of complexes + elementUtilities.getComplexPadding = function(ele) { + // this property needs to take into account: + // - presence of a label + // - option to display complex labels + // - presence of states and info box on the bottom + var padding = graphUtilities.getCompoundPaddings(); + padding = padding < 5 ? 5 : padding; + if (options.showComplexName && elementUtilities.getElementContent(ele)) { + padding += options.extraComplexPadding * 0.5; + // if there is something on the bottom side + + if (ele.data('auxunitlayouts') && ele.data('auxunitlayouts').bottom && ele.data('auxunitlayouts').bottom.units.length > 0) { + padding += options.extraComplexPadding * 0.5; + }else{ + + + for(var i=0; i < ele.data('statesandinfos').length; i++) { + var statesandinfos = ele.data('statesandinfos')[i]; + + var thisY = statesandinfos.bbox.y; + var thisH = statesandinfos.bbox.h; + var parentY = (ele.data('class') == "compartment" || ele.data('class') == "complex") ? ele.data('bbox').y : ele.position().y; + var height = ele.data("originalH") ? ele.data("originalH") : ele.height(); + var parentY2 = Number((parentY + height/ 2).toFixed(2)); + var centerY = Number((thisY+thisH/2).toFixed(2)); + if(centerY == parentY2){ + padding += options.extraComplexPadding * 0.5; + break; + } + } + + } + } + // for the case where the padding is the tightest, we need a bit of extra space + // to avoid touching the infoboxes of the complex + else { + if (ele.data('statesandinfos').length > 0) { + padding += 2; + } + } + return padding; + }; + + // used for handling the variable property of complexes + elementUtilities.getComplexMargin = function(ele) { + // this property needs to take into account: + // - presence of a label + // - option to display complex labels + // - presence of states and info box on the bottom + var margin = -1 * options.extraComplexPadding; + + if (options.showComplexName && + elementUtilities.getElementContent(ele) && + ele.data('auxunitlayouts') && // check if there is something on the bottom side + ele.data('auxunitlayouts').bottom && + ele.data('auxunitlayouts').bottom.units.length > 0) { + margin -= options.extraComplexPadding * 0.5; + } + + if (ele.css("font-size") == "14px") + margin -= 2; + + 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 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']); + // 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'; + } + + 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 + // Stylesheet helpers + + var defaultProperties = { + }; + + var getDefaultNodeProperties = function() { + return { + 'border-width': 1.25, + 'border-color': '#555555', + 'background-color': '#ffffff', + 'background-opacity': 1, + 'background-image-opacity': 1, + 'text-wrap': 'wrap' + }; + }; + + var getDefaultEdgeProperties = function() { + return { + 'line-color': '#555555', + 'width': 1.25 + }; + }; + + var getDefaultProcessSize = function() { + return { + width: 20, + height: 20 + }; + }; + + var getDefaultLogicalOperatorSize = function() { + return { + width: 30, + height: 30 + }; + }; + + var getDefaultBASize = function() { + return { + width: 60, + height: 30 + }; + }; + + var defaultSifEdgeColorMap = { + 'neighbor-of': '#FC002C', + 'interacts-with': '#B57261', + 'in-complex-with': '#B4987A', + 'controls-state-change-of': '#B4E1CD', + 'controls-transport-of': '#F0E7C8', + 'controls-phosphorylation-of': '#D970A1', + 'catalysis-precedes': '#8EC3ED', + 'controls-expression-of': '#6A0F30', + 'consumption-controled-by': '#A9A9F7', + 'controls-production-of': '#2D5207', + 'controls-transport-of-chemical': '#3F00FF', + 'chemical-affects': '#D95F23', + 'reacts-with': '#4E214B', + 'used-to-produce': '#FF2F07', + 'phosphorylates': '#62392D', + 'dephosphorylates': '#CC8E12', + 'upregulates-expression': '#32D849', + 'downregulates-expression': '#CC8E12', + 'activates': '#32D849', + 'inhibits': '#4886A5' + }; + + var defaultSizeMap = { + 'macromolecule': { + width: 60, + height: 30 + }, + 'nucleic acid feature': { + width: 60, + height: 30 + }, + 'simple chemical': { + width: 30, + height: 30 + }, + 'source and sink': { + width: 22, + height: 22 + }, + 'phenotype': { + width: 60, + height: 30 + }, + 'unspecified entity': { + width: 60, + height: 30 + }, + 'perturbing agent': { + width: 60, + height: 30 + }, + 'complex': { + width: 44, + height: 44 + }, + 'compartment': { + width: 80, + height: 80 + }, + 'submap': { + width: 80, + height: 80 + }, + 'tag': { + width: 35, + height: 35 + }, + 'SIF macromolecule': { + width: 60, + height: 30 + }, + 'SIF simple chemical': { + width: 30, + height: 30 + }, + 'topology group': { + width: 44, + height: 44 + } + }; + + elementUtilities.processTypes.forEach( function( type ) { + // phenotype has a different default size + if ( type == 'phenotype' ) { + return; + } + + defaultSizeMap[ type ] = getDefaultProcessSize(); + } ); + + elementUtilities.logicalOperatorTypes.forEach( function( type ) { + defaultSizeMap[ type ] = getDefaultLogicalOperatorSize(); + } ); + + elementUtilities.biologicalActivityTypes.forEach( function( type ) { + defaultSizeMap[ type ] = getDefaultBASize(); + } ); + + var getDefaultSize = function( type ) { + return defaultSizeMap[ type ]; + }; + + var getDefaultFontProperties = function() { + return { + 'font-size': 11, + 'font-family': 'Helvetica', + 'font-style': 'normal', + 'font-weight': 'normal', + 'color': '#000' + }; + }; + + var getDefaultInfoboxProperties = function( nodeClass, infoboxType ) { + return { + 'font-size': getDefaultInfoboxFontSize( nodeClass, infoboxType ), + 'font-family': 'Arial', + 'font-style': 'normal', + 'font-weight': 'normal', + 'font-color': '#0f0f0f', + 'border-width': 1, + 'border-color': '#555555', + 'background-color': '#ffffff', + 'shape-name': getDefaultInfoboxShapeName( nodeClass, infoboxType ), + 'width': getDefaultInfoboxSize( nodeClass, infoboxType ).w, + 'height': getDefaultInfoboxSize( nodeClass, infoboxType ).h + }; + }; + + var getDefaultInfoboxFontSize = function( nodeClass, infoboxType ) { + var fontsize = 9; + + if ( nodeClass === 'SIF macromolecule' || nodeClass === 'SIF simple chemical' ) { + fontsize = 11; + } + + return fontsize; + }; + + var getDefaultInfoboxSize = function( nodeClass, infoboxType ) { + var w = 12, h = 12; + + if ( nodeClass === 'SIF macromolecule' || nodeClass === 'SIF simple chemical' ) { + w = 15; + h = 15; + } + + return { w, h }; + }; + + var getDefaultInfoboxShapeName = function( nodeClass, infoboxType ) { + if ( infoboxType === 'state variable' ) { + return 'stadium'; + } + + if ( elementUtilities.isSIFNode( nodeClass ) ) { + if ( infoboxType === 'unit of information' ) { + if ( nodeClass == 'SIF macromolecule' ) { + return 'stadium'; + } + return 'rectangle'; + } + } + else { + var list = elementUtilities.getUnitOfInfoShapeOptions( nodeClass ); + return list[ 0 ]; + } + }; + + elementUtilities.nodeTypes.forEach( function( type ) { + defaultProperties[ type ] = $.extend( {}, getDefaultNodeProperties(), getDefaultSize( type ) ); + if (elementUtilities.canHaveStateVariable( type )) { + var props = getDefaultInfoboxProperties( type, 'state variable' ); + defaultProperties[ type ][ 'state variable' ] = props; + } + if (elementUtilities.canHaveUnitOfInformation( type )) { + var props = getDefaultInfoboxProperties( type, 'unit of information' ); + defaultProperties[ type ][ 'unit of information' ] = props; + } + } ); + + elementUtilities.compoundNodeTypes.forEach( function( type ) { + defaultProperties[ type ] = $.extend( defaultProperties[ type ], { + 'background-opacity': 0.5 + } ); + } ); + + $.extend( defaultProperties['association'], { + 'background-color': '#707070' + } ); + + elementUtilities.epnTypes + .concat( elementUtilities.sifTypes ) + .concat( elementUtilities.otherNodeTypes ) + .concat (elementUtilities.biologicalActivityTypes) + .concat( ['phenotype'] ) + .forEach( function( type ) { + $.extend( defaultProperties[ type ], getDefaultFontProperties() ); + } ); + + $.extend( defaultProperties['submap'], { + 'font-size': 14, + 'border-width': 2.25 + } ); + + $.extend( defaultProperties['compartment'], { + 'font-size': 14, + 'border-width': 3.25 + } ); + + elementUtilities.edgeTypes.forEach( function( type ) { + defaultProperties[ type ] = getDefaultEdgeProperties(); + + if ( defaultSifEdgeColorMap[ type ] ) { + defaultProperties[ type ][ 'line-color' ] = defaultSifEdgeColorMap[ type ]; + } + } ); + + function getProp( props, name ) { + var prop = props[ name ]; + + if ( typeof prop !== null && typeof prop === 'object' ) { + return $.extend( {}, prop ); + } + + return prop; + } + + function extendDataWithClassDefaults( data, className, propsToSkip ) { + if ( !className ) { + return; + } + + var defaultProps = elementUtilities.getDefaultProperties( className ); + + Object.keys( defaultProps ).forEach( function( name ) { + if ( !propsToSkip || !propsToSkip[ name ] ) { + data[ name ] = getProp( defaultProps, name ); + } + } ); + } + + elementUtilities.extendNodeDataWithClassDefaults = function( data, className ) { + // list of properties to skip + var propsToSkip = { + 'width': true, + 'height': true, + 'state variable': true, + 'unit of information': true, + 'multimer': true, + 'clonemarker': true, + 'ports-ordering': true + }; + + extendDataWithClassDefaults( data, className, propsToSkip ); + }; + + elementUtilities.extendEdgeDataWithClassDefaults = function( data, className ) { + extendDataWithClassDefaults( data, className ); + } + + // get infobox properties and filter the ones related to style only + elementUtilities.getDefaultInfoboxStyle = function( nodeClass, infoboxType ) { + var defaultProps = elementUtilities.getDefaultProperties( nodeClass ); + var infoboxStyle = $.extend( {}, defaultProps[ infoboxType ] ); + + // width and height are belonging to bbox object rather than style object + var nonStyleProps = [ 'width', 'height' ]; + + nonStyleProps.forEach( function( propName ) { + delete infoboxStyle[ propName ]; + } ); + + return infoboxStyle; + }; + + elementUtilities.getDefaultProperties = function( sbgnclass ) { + if ( sbgnclass == undefined ) { + return defaultProperties; + } + + var pureClass = elementUtilities.getPureSbgnClass( sbgnclass ); + + // init default properties for the class if not initialized yet + if ( defaultProperties[ pureClass ] == null ) { + defaultProperties[ pureClass ] = {}; + } + + return defaultProperties[ pureClass ]; + }; + + elementUtilities.setDefaultProperties = function( sbgnclass, props ) { + $.extend( elementUtilities.getDefaultProperties( sbgnclass ), props ); + }; + + elementUtilities.lockGraphTopology = function() { + elementUtilities.graphTopologyLocked = true; + if ( cy.expandCollapse ) { + cy.expandCollapse('get').disableCue(); + } + }; + + elementUtilities.unlockGraphTopology = function() { + elementUtilities.graphTopologyLocked = false; + if ( cy.expandCollapse ) { + cy.expandCollapse('get').enableCue(); + } + }; + + elementUtilities.isGraphTopologyLocked = function() { + return elementUtilities.graphTopologyLocked; + }; + + elementUtilities.languageToMapType = function(lang) { + switch (lang) { + case 'process description': + return 'PD'; + case 'activity flow': + return 'AF'; + case 'sif': + return 'SIF'; + case 'hybrid sbgn': + return 'HybridSbgn'; + default: + return 'HybridAny'; + } + }; + + elementUtilities.mapTypeToLanguage = function(mapType) { + switch (mapType) { + case 'PD': + return 'process description'; + case 'AF': + return 'activity flow'; + case 'SIF': + return 'sif'; + case 'HybridSbgn': + return 'hybrid sbgn'; + default: + return 'hybrid any'; + } + }; + + elementUtilities.getAllCollapsedChildrenRecursively = function(nodes) { + var expandCollapse = cy.expandCollapse('get'); + var collapsedChildren = cy.collection(); + var collapsedNodes = nodes.filter(".cy-expand-collapse-collapsed-node"); + collapsedNodes.forEach( function( n ) { + collapsedChildren = collapsedChildren.union(expandCollapse.getCollapsedChildrenRecursively(n)); + } ); + return collapsedChildren; + }; + + elementUtilities.getWidthByContent = function( content, fontFamily, fontSize, options ) { + return textUtilities.getWidthByContent( content, fontFamily, fontSize, options ); + }; + + return elementUtilities; +} + +},{"./classes":193,"./lib-utilities":204,"./text-utilities":215}],195:[function(_dereq_,module,exports){ + +module.exports = function () { + var cy; + var parsedDataMap; + var visibleDataMapByExp; + var groupedDataMap; + var visibleFiles; + var colorMap; + var allVis; + var fileDescription; + var fileTitle; + function experimentalDataOverlay(param) { + // Init + cy = param.sbgnCyInstance.getCy(); + parsedDataMap = {}; + visibleDataMapByExp = {}; + visibleFiles = {}; + groupedDataMap = {}; + colorMap = {}; + allVis = true; + + fileDescription = {}; + fileTitle = {}; + } + + experimentalDataOverlay.getName = function () { + return fname; + }; + + experimentalDataOverlay.getDesc = function () { + return fdesc; + }; + + experimentalDataOverlay.getGroupedDataMap = function () { + return groupedDataMap; + }; + + experimentalDataOverlay.getParsedDataMap = function () { + return parsedDataMap; + }; + + experimentalDataOverlay.getVisibleData = function () { + return visibleDataMapByExp; + }; + + experimentalDataOverlay.hideAll = function () { + var invisibleExp = {}; + var invisibleFile = {}; + allVis = false; + + for (let i in groupedDataMap) { + if (visibleFiles[i]) { + visibleFiles[i] = false; + invisibleFile[i] = false; + } + } + for (let fileName in groupedDataMap) { + for (let j = 0; j < groupedDataMap[fileName].length; j++) { + const expName = groupedDataMap[fileName][j]; + if (visibleDataMapByExp[fileName + '?' + expName] == undefined) + continue; + if (visibleDataMapByExp[fileName + '?' + expName] == true) { + invisibleExp[fileName + '?' + expName] = false; + visibleDataMapByExp[fileName + '?' + expName] = false; + } + } + } + + this.showData(); + params = {invisibleFile, invisibleExp}; + return params; + + }; + + experimentalDataOverlay.hideAllUndo = function (invisibleFile, invisibleExp) { + for (let j in invisibleFile) { + visibleFiles[j] = true; + } + for (let j in invisibleExp) { + visibleDataMapByExp[j] = true; + } + allVis = true; + this.showData(); + return; + }; + + experimentalDataOverlay.unhideAll = function () { + var visibleExp = {}; + var visibleFile = {}; + allVis = true; + for (let i in visibleFiles) { + if (!visibleFiles[i]) { + visibleFiles[i] = true; + visibleFile[i] = true; + } + } + for (let fileName in groupedDataMap) { + if (groupedDataMap[fileName] == undefined) { + return; + } + for (let j = 0; j < groupedDataMap[fileName].length; j++) { + const expName = groupedDataMap[fileName][j]; + if (visibleDataMapByExp[fileName + '?' + expName] == undefined) + continue; + if (visibleDataMapByExp[fileName + '?' + expName] == false) { + visibleExp[fileName + '?' + expName] = true; + visibleDataMapByExp[fileName + '?' + expName] = true; + } + } + } + this.showData(); + return {visibleExp, visibleFile}; + }; + + experimentalDataOverlay.unhideAllUndo = function (visibleFile, visibleExp) { + for (let j in visibleFile) { + visibleFiles[j] = false; + } + for (let j in visibleExp) { + visibleDataMapByExp[j] = false; + } + allVis = false; + this.showData(); + return; + }; + + experimentalDataOverlay.removeAll = function () { + var parsed = {}; + var visible = {}; + var grouped = {}; + var visiblef = {}; + + for (let i in parsedDataMap) { + if (!parsed[i]) { + parsed[i] = {}; + } + for (let j in parsedDataMap[i]) { + parsed[i][j] = parsedDataMap[i][j]; + } + } + for (let i in visibleDataMapByExp) { + visible[i] = visibleDataMapByExp[i]; + } + + for (let i in visibleFiles) { + visiblef[i] = visibleFiles[i]; + } + for (let i in groupedDataMap) { + if (!grouped[i]) { + grouped[i] = []; + } + for (let j in groupedDataMap[i]) { + grouped[i].push(groupedDataMap[i][j]); + } + } + parsedDataMap = {}; + visibleDataMapByExp = {}; + visibleFiles = {}; + groupedDataMap = {}; + this.showData(); + params = {parsed, visible, grouped, visiblef}; + return params; + }; + + experimentalDataOverlay.restoreAll = function (parsed, visible, grouped, visiblef) { + parsedDataMap = parsed; + visibleDataMapByExp = visible; + groupedDataMap = grouped; + visibleFiles = visiblef; + this.showData(); + param = {}; + return param; + }; + + experimentalDataOverlay.addExp = function (fileName, expName, isVisible, values, groupArray) { + visibleDataMapByExp[fileName + '?' + expName] = isVisible; + groupedDataMap[fileName] = groupArray; + + for (let i in values) { + if (values[i][fileName + '?' + expName] != undefined) + parsedDataMap[i][fileName + '?' + expName] = values[i][fileName + '?' + expName]; + } + + var params = {fileName, expName}; + this.showData(); + return params; + }; + + experimentalDataOverlay.addFile = function (fileName, parsed, visible, grouped, visiblef) { + parsedDataMap = parsed; + visibleDataMapByExp = visible; + groupedDataMap = grouped; + visibleFiles = visiblef; + this.showData(); + param = {fileName}; + return param; + }; + + experimentalDataOverlay.removeExp = function (fileName, expName) { + var isVisible = false; + var values = {}; + var groupArray = []; + + if (visibleDataMapByExp[fileName + '?' + expName] != undefined) { + isVisible = visibleDataMapByExp[fileName + '?' + expName]; + delete visibleDataMapByExp[fileName + '?' + expName]; + } + + if (groupedDataMap[fileName] != undefined) { + for (let i in groupedDataMap[fileName]) { + groupArray[i] = groupedDataMap[fileName][i]; + } + //groupArray = groupedDataMap[fileName]; + var index = groupedDataMap[fileName].indexOf(expName); + + if (index != -1) { + delete groupedDataMap[fileName][index]; + } + } + + for (let i in parsedDataMap) { + if (parsedDataMap[i][fileName + '?' + expName] != undefined) { + if (!(i in values)) { + values[i] = {}; + } + values[i][fileName + '?' + expName] = parsedDataMap[i][fileName + '?' + expName]; + delete parsedDataMap[i][fileName + '?' + expName]; + } + } + + if (groupedDataMap[fileName] != undefined) { + var count = 0; + for (let i = 0; i < groupedDataMap[fileName].length; i++) { + if (!groupedDataMap[fileName][i]) + count++; + } + if (count == groupedDataMap[fileName].length) { + delete groupedDataMap[fileName]; + } + } + params = {fileName, expName, isVisible, values, groupArray}; + this.showData(); + return params; + }; + + experimentalDataOverlay.removeFile = function (fileName) { + if (groupedDataMap[fileName] == undefined) { + return; + } + + var parsed = {}; + var visible = {}; + var grouped = {}; + var visiblef = {}; + + for (let i in parsedDataMap) { + if (!parsed[i]) { + parsed[i] = {}; + } + for (let j in parsedDataMap[i]) { + parsed[i][j] = parsedDataMap[i][j]; + } + } + for (let i in visibleDataMapByExp) { + visible[i] = visibleDataMapByExp[i]; + } + for (let i in visibleFiles) { + visiblef[i] = visibleFiles[i]; + } + for (let i in groupedDataMap) { + if (!grouped[i]) { + grouped[i] = []; + } + for (let j in groupedDataMap[i]) { + grouped[i].push(groupedDataMap[i][j]); + } + } + + for (let j = 0; j < groupedDataMap[fileName].length; j++) { + const expName = groupedDataMap[fileName][j]; + if (visibleDataMapByExp[fileName + '?' + expName] != undefined) + delete visibleDataMapByExp[fileName + '?' + expName]; + for (let i in parsedDataMap) { + delete parsedDataMap[i][fileName + '?' + expName]; + } + } + delete groupedDataMap[fileName]; + + var params = {fileName, parsed, visible, grouped, visiblef}; + this.showData(); + var k = 0; + for (let i in groupedDataMap) + { + k++; + } + if (k == 0) { + groupedDataMap = {}; + } + return params; + }; + + experimentalDataOverlay.hideExp = function (fileName, expName) { + if (visibleDataMapByExp[fileName + '?' + expName] == undefined) + return; + visibleDataMapByExp[fileName + '?' + expName] = false; + params = {fileName, expName}; + this.showData(); + return params; + } + + experimentalDataOverlay.hideFile = function (fileName) { + visibleFiles[fileName] = false; + var invisible = {}; + if (groupedDataMap[fileName] == undefined) { + return; + } + for (let j = 0; j < groupedDataMap[fileName].length; j++) { + const expName = groupedDataMap[fileName][j]; + if (visibleDataMapByExp[fileName + '?' + expName] == undefined) + continue; + if (visibleDataMapByExp[fileName + '?' + expName] == true) { + invisible[fileName + '?' + expName] = false; + visibleDataMapByExp[fileName + '?' + expName] = false; + } + } + + this.showData(); + params = {fileName, invisible}; + return params; + }; + + experimentalDataOverlay.hideFileUndo = function (fileName, invisible) { + if (visibleFiles[fileName] != undefined) + visibleFiles[fileName] = true; + else + return; + for (let j in invisible) { + visibleDataMapByExp[j] = true; + } + this.showData(); + return {fileName}; + }; + + experimentalDataOverlay.unhideExp = function (fileName, expName) { + if (visibleDataMapByExp[fileName + '?' + expName] == undefined) + return; + visibleDataMapByExp[fileName + '?' + expName] = true; + params = {fileName, expName}; + this.showData(); + return params; + }; + + experimentalDataOverlay.unhideFile = function (fileName) { + var visible = {}; + visibleFiles[fileName] = true; + if (groupedDataMap[fileName] == undefined) { + return; + } + for (let j = 0; j < groupedDataMap[fileName].length; j++) { + const expName = groupedDataMap[fileName][j]; + if (visibleDataMapByExp[fileName + '?' + expName] == undefined) + continue; + if (visibleDataMapByExp[fileName + '?' + expName] == false) { + visibleDataMapByExp[fileName + '?' + expName] = true; + visible[fileName + '?' + expName] = true; + } + } + this.showData(); + params = {fileName, visible}; + return params; + }; + + experimentalDataOverlay.unhideFileUndo = function (fileName, visible) { + visibleFiles[fileName] = false; + for (let j in visible) { + visibleDataMapByExp[j] = false; + } + this.showData(); + return {fileName}; + }; + + experimentalDataOverlay.countVisibleDataByExp = function () { + // Count the genomic data that will be displayed on nodes' body + let dataBoxCount = 0; + for (let exp in visibleDataMapByExp) { + if (visibleDataMapByExp[exp]) { + dataBoxCount++; + } + } + return dataBoxCount; + }; + + experimentalDataOverlay.generateSVGForNode = function (ele, tooltip) { + const dataBoxCount = this.countVisibleDataByExp(); + var values = []; + // Experimental data overlay part ! + // const dataURI = 'data:image/svg+xml;utf8,' + const svgNameSpace = 'http://www.w3.org/2000/svg'; + const nodeLabel = ele.data('label'); + const reqWidth = ele.outerWidth(); + const reqHeight = ele.outerHeight(); + const overlayRecBoxW = reqWidth; + const overlayRecBoxH = reqHeight; + const svg = document.createElementNS(svgNameSpace, 'svg'); + // It seems this should be set according to the node size ! + svg.setAttribute('width', reqWidth); + svg.setAttribute('height', reqHeight); + // This is important you need to include this to succesfully render in cytoscape.js! + svg.setAttribute('xmlns', svgNameSpace); + + // Overlay Data Rect + const overLayRectBBox = { + w: overlayRecBoxW, + h: overlayRecBoxH, + x: 0, + y: 0 + }; + + const frequencyData = parsedDataMap[nodeLabel]; + + let maxDataBoxCount = /*(genomicDataBoxCount > 3) ? 3:*/ dataBoxCount; + let counter = 0; + + for (let i in groupedDataMap) { + for (let j in groupedDataMap[i]) { + const fileName = i; + const expName = groupedDataMap[i][j]; + if (!visibleDataMapByExp[fileName + '?' + expName]) { + continue + } + + if (frequencyData[fileName + '?' + expName] !== undefined) { + values.push(frequencyData[fileName + '?' + expName]); + dataRectangleGenerator( + overLayRectBBox.x + + (counter * overLayRectBBox.w) / maxDataBoxCount, + overLayRectBBox.y, + overLayRectBBox.w / maxDataBoxCount, + overLayRectBBox.h, + frequencyData[fileName + '?' + expName], + svg, + fileName + ); + } else { + values.push("-"); + dataRectangleGenerator( + overLayRectBBox.x + + (counter * overLayRectBBox.w) / maxDataBoxCount, + overLayRectBBox.y, + overLayRectBBox.w / maxDataBoxCount, + overLayRectBBox.h, + null, + svg, + fileName + ); + } + + // draw separator line between data rectangles + if (counter < maxDataBoxCount - 1) { + const overlayRect = document.createElementNS(svgNameSpace, 'line'); + overlayRect.setAttribute('x1', overLayRectBBox.x + (counter * overLayRectBBox.w) / maxDataBoxCount + + overLayRectBBox.w / maxDataBoxCount); + overlayRect.setAttribute('y1', overLayRectBBox.y); + overlayRect.setAttribute('x2', overLayRectBBox.x + (counter * overLayRectBBox.w) / maxDataBoxCount + + overLayRectBBox.w / maxDataBoxCount); + overlayRect.setAttribute('y2', overLayRectBBox.y + overLayRectBBox.h); + overlayRect.setAttribute( + 'style', + 'stroke-width:1;stroke:rgb(85,85,85);' + ); + svg.appendChild(overlayRect); + } + counter++; + } + } + + tooltip.content = "(" + values.join(",") + ")"; + + function interpolateColor(color1, color2, factor) { + var result = color1.slice(); + for (var i = 0; i < 3; i++) { + result[i] = Math.round(result[i] + factor * (color2[i] - color1[i])); + } + return result; + }; + + function decideColor(percent, fileName) { + var sorted = []; + for (let i in colorMap[fileName]) { + sorted.push(i); + } + sorted.sort(); + + var prev = sorted[0]; + var next = sorted[sorted.length - 1]; + + if (percent < prev || percent > next) { + return ({r: 210, g: 210, b: 210}); + } + + for (let k in sorted) { + var i = sorted[k]; + if (i == percent) { + return ({r: colorMap[fileName][i][0], g: colorMap[fileName][i][1], b: colorMap[fileName][i][2]}); + } else if (i > percent) { + next = i; + break; + } else { + prev = i; + } + } + + var steps = 1 / (next - prev); + var res = interpolateColor(colorMap[fileName][prev], colorMap[fileName][next], steps * (percent - prev)); + + return ({r: res[0], g: res[1], b: res[2]}); + } + function dataRectangleGenerator(x, y, w, h, percent, parentSVG, fileName) { + let colorString = ''; + if (percent) { + var color = decideColor(parseInt(percent), fileName); + colorString = + 'rgb(' + + Math.round(color.r) + + ',' + + Math.round(color.g) + + ',' + + Math.round(color.b) + ')'; + // Rectangle Part + const overlayRect = document.createElementNS(svgNameSpace, 'rect'); + overlayRect.setAttribute('x', x); + overlayRect.setAttribute('y', y); + overlayRect.setAttribute('width', w); + overlayRect.setAttribute('height', h); + overlayRect.setAttribute('style', 'opacity:1;fill:' + colorString + ';'); + + parentSVG.appendChild(overlayRect); + } else { + colorString = 'rgb(210,210,210)'; + + // Rectangle Part + const overlayRect = document.createElementNS(svgNameSpace, 'rect'); + overlayRect.setAttribute('x', x); + overlayRect.setAttribute('y', y); + overlayRect.setAttribute('width', w); + overlayRect.setAttribute('height', h); + overlayRect.setAttribute('style', 'opacity:1;fill:' + colorString + ';'); + + parentSVG.appendChild(overlayRect); + } + } + + return svg; + }; + + experimentalDataOverlay.showData = function () { + const self = this; + var nodeCollection = cy.collection(); + var collapsedChildren = cy.expandCollapse('get').getAllCollapsedChildrenRecursively().filter("node"); + var collapsedChildrenNotParent = cy.collection(); + var parentSet = new Set(); // parent ids of collapsed children + collapsedChildren.forEach(function(node){ + parentSet.add(node.parent().id()); + }); + // filter parent nodes from collapsed children + collapsedChildren.forEach(function(node){ + if(!parentSet.has(node.id())){ // this means removed node is not parent + collapsedChildrenNotParent = collapsedChildrenNotParent.union(node); + } + }); + var expandableNodes = cy.expandCollapse('get').expandableNodes(); + nodeCollection = nodeCollection.union(cy.nodes()).union(collapsedChildrenNotParent).difference(expandableNodes); + cy.batch(function(){ + nodeCollection.forEach(function (node) { + const nodeLabel = node.data('label'); + var imageURI = 'data:image/svg+xml;utf8,'; + if (nodeLabel in parsedDataMap && !node.isParent()) { + + var tooltip = {content:''}; + imageURI = imageURI + encodeURIComponent(self.generateSVGForNode(node,tooltip).outerHTML); + + if(Object.keys(parsedDataMap[nodeLabel]).length > 0){ + // var tooltip = "(" + Object.values(parsedDataMap[nodeLabel]).join(",") + ")"; + node.data("tooltip",tooltip.content); + }else{ + node.data('tooltip',''); + } + node.data('background-image', imageURI), + node.data('background-position-x', '100%'); + node.data('background-position-y', '100%'); + node.data('background-width', '100%'); + node.data('background-height', '100%'); + node.data('background-fit', 'contain'); + node.data('background-image-opacity', '1'); + } else { + node.data('background-image', ""); + node.data('tooltip',''); + } + }); + }); + + }; + + experimentalDataOverlay.hexToRgb = function (hex) { + if (hex[0] == '#') { + hex = hex.substring(1); + } else { + return; + } + var bigint = parseInt(hex, 16); + var r = (bigint >> 16) & 255; + var g = (bigint >> 8) & 255; + var b = bigint & 255; + return [r, g, b]; + }; + + experimentalDataOverlay.isHex = function (hex) { + return typeof hex == 'string' + && hex.length == 7 + && !isNaN(Number('0x' + hex.substring(1))) + && hex[0] == '#'; + }; + + experimentalDataOverlay.parseData = function (data, fileName, errorCallback) { + parsedDataMap = parsedDataMap || {}; + visibleDataMapByExp = visibleDataMapByExp || {}; + groupedDataMap = groupedDataMap || {}; + colorMap = colorMap || {}; + const experiments = []; + var colors = {}; + + if (fileName in groupedDataMap) { + return; + } + + var parsed = {}; + var visible = {}; + var grouped = {}; + var visiblef = {}; + var colorm = {}; + var fileD = {}; + var fileN = {}; + + for (let i in parsedDataMap) { + if (!parsed[i]) { + parsed[i] = {}; + } + for (let j in parsedDataMap[i]) { + parsed[i][j] = parsedDataMap[i][j]; + } + } + for (let i in visibleDataMapByExp) { + visible[i] = visibleDataMapByExp[i]; + } + for (let i in visibleFiles) { + visiblef[i] = visibleFiles[i]; + } + for (let i in groupedDataMap) { + if (!grouped[i]) { + grouped[i] = []; + } + for (let j in groupedDataMap[i]) { + grouped[i].push(groupedDataMap[i][j]); + } + } + + for (let i in colorMap) { + colorm[i] = colorMap[i]; + } + + for (let i in visibleFiles) { + fileD[i] = fileDescription[i]; + } + + for (let i in visibleFiles) { + fileN[i] = fileTitle[i]; + } + + var intregex = "^(-?)(0|([1-9][0-9]*))(\\.[0-9]+)?$"; + var version = '1.0'; + var clr = false; + // By lines + const lines = data.split('\n'); + if (lines.length < 2) { + errorCallback(); + return "Error"; + } + var k = 0; + var upto = 4; + if (lines.length < 4) { + upto = lines.length; + } + for (let i = 0; i < upto; i++) { + if (lines[i].substring(0, 7) == 'version') { + k++; + const metaLines = lines[i].split('\t'); + if (metaLines[1] && metaLines[1].length > 1) { + version = metaLines[1]; + } else { + fileDescription = fileD; + fileTitle = fileN; + version = "1.0"; + colorMap = colorm; + errorCallback(); + return "Error"; + } + } + if (lines[i].substring(0, 4) == 'name') { + k++; + const metaLines = lines[i].split('\t'); + if (metaLines[1] && metaLines[1].length > 1) { + fileTitle[fileName] = metaLines[1]; + } else { + fileTitle = fileN; + fileDescription = fileD; + version = "1.0"; + colorMap = colorm; + errorCallback(); + return "Error"; + } + } + if (lines[i].substring(0, 11) == 'description') { + k++; + const metaLines = lines[i].split('\t'); + if (metaLines[1] && metaLines[1].length > 1) { + fileDescription[fileName] = metaLines[1]; + + } else { + fileTitle = fileN; + fileDescription = fileD; + version = "1.0"; + colorMap = colorm; + errorCallback(); + return "Error"; + } + } + if (lines[i].substring(0, 5) == 'color') { + clr = true; + k++; + const metaLines = lines[i].split('\t'); + if (metaLines.length <= 1 && metaLines.length % 2 == 0) { + errorCallback(); + return "Error"; + } + + for (let t = 1; t < metaLines.length - 1; t = t + 2) { + var hex = metaLines[t + 1]; + if (t == metaLines.length - 2) { + hex = hex.trim(); + } + if (metaLines[t] == "min" || metaLines[t] == "max") { + if (this.isHex(hex)) { + colors[(metaLines[t])] = this.hexToRgb(hex); + } + } else if (parseInt(metaLines[t]) != NaN) { + if (this.isHex(hex)) { + colors[parseInt(metaLines[t])] = this.hexToRgb(hex); + } else { + fileTitle = fileN; + fileDescription = fileD; + colorMap = colorm; + version = "1.0"; + errorCallback(); + return "Error"; + } + } else { + fileTitle = fileN; + fileDescription = fileD; + colorMap = colorm; + version = "1.0"; + errorCallback(); + return "Error"; + } + } + } + } + + //default colors + if (!clr) { + colors[-100] = this.hexToRgb('#0000ff'); + colors[100] = this.hexToRgb('#ff0000'); + colors[0] = this.hexToRgb('#ffffff'); + } + + // First line is meta data ! + const metaLineColumns = lines[k].split('\t'); + + // Parse experiment types + for (let i = 1; i < metaLineColumns.length; i++) { + if (i == metaLineColumns.length - 1) { + var trimmed = metaLineColumns[i].trim(); + experiments.push(trimmed); + } else + experiments.push(metaLineColumns[i]); + + visibleDataMapByExp[fileName + '?' + experiments[i - 1]] = true; + + if (groupedDataMap[fileName] === undefined) { + groupedDataMap[fileName] = []; + } + groupedDataMap[fileName].push(experiments[i - 1]); + } + + visibleFiles[fileName] = true; + + var min = Number.MAX_VALUE; + var max = Number.MIN_VALUE; + + // parse genomic data + for (let i = k + 1; i < lines.length; i++) { + // EOF check + if (lines[i].length === 0) { + break + } + // Split each line by tab and parse genomic data content + const lineContent = lines[i].split('\t'); + const eleSymbol = lineContent[0]; + + // If current gene entry is not in genomic data map create new map + if (!(eleSymbol in parsedDataMap)) { + parsedDataMap[eleSymbol] = {}; + } + + // Add each entry of genomic data + for (let j = 1; j < lineContent.length; j++) { + if (j == lineContent.length - 1) { + lineContent[j] = lineContent[j].trim(); + } + if (lineContent[j].match(intregex)) { + parsedDataMap[eleSymbol][fileName + '?' + experiments[j - 1]] = lineContent[j]; + } else { + parsedDataMap = parsed; + visibleDataMapByExp = visible; + groupedDataMap = grouped; + colorMap = colorm; + visibleFiles = visiblef; + fileTitle = fileN; + fileDescription = fileD; + version = "1.0"; + errorCallback(); + return "Error"; + } + if (lineContent[j] > max) { + max = lineContent[j]; + } + if (lineContent[j] < min) { + min = lineContent[j]; + } + } + } + + if (colors['min']) { + var colorvalue = colors['min']; + delete colors['min']; + colors[min] = colorvalue; + } + + if (colors['max']) { + var colorvalue = colors['max']; + delete colors['max']; + colors[max] = colorvalue; + } + + colorMap[fileName] = colors; + var params = {fileName}; + this.showData(); + return params; + }; + + experimentalDataOverlay.getExperimentalData = function () { + return { + visibleDataMapByExp: visibleDataMapByExp, + visibleFiles: visibleFiles, + fileDescription: fileDescription, + allVis: allVis, + fileTitle: fileTitle, + parsedDataMap:parsedDataMap + }; + }; + return experimentalDataOverlay; +}; + +},{}],196:[function(_dereq_,module,exports){ +/* +* File Utilities: To be used on read/write file operation +*/ + +var libUtilities = _dereq_('./lib-utilities'); +var libs = libUtilities.getLibs(); +var jQuery = $ = libs.jQuery; +var saveAs = libs.saveAs; +var textUtilities = _dereq_('./text-utilities'); + +module.exports = function () { + // Helper functions Start + // see http://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript + function b64toBlob(b64Data, contentType, sliceSize) { + contentType = contentType || ''; + sliceSize = sliceSize || 512; + + var byteCharacters = atob(b64Data); + var byteArrays = []; + + for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) { + var slice = byteCharacters.slice(offset, offset + sliceSize); + + var byteNumbers = new Array(slice.length); + for (var i = 0; i < slice.length; i++) { + byteNumbers[i] = slice.charCodeAt(i); + } + + var byteArray = new Uint8Array(byteNumbers); + + byteArrays.push(byteArray); + } + + var blob = new Blob(byteArrays, {type: contentType}); + return blob; + } + + function loadTextDoc(fullFilePath) { + if (window.XMLHttpRequest) { + xhttp = new XMLHttpRequest(); + } + else { + xhttp = new ActiveXObject("Microsoft.XMLHTTP"); + } + xhttp.overrideMimeType('application/text'); + xhttp.open("GET", fullFilePath, false); + xhttp.send(); + return xhttp.responseText; + } + + function loadXMLDoc(fullFilePath) { + if (window.XMLHttpRequest) { + xhttp = new XMLHttpRequest(); + } + else { + xhttp = new ActiveXObject("Microsoft.XMLHTTP"); + } + xhttp.overrideMimeType('application/xml'); + xhttp.open("GET", fullFilePath, false); + xhttp.send(); + return xhttp.responseXML; +} + + // Should this be exposed or should this be moved to the helper functions section? + function textToXmlObject(text) { + if (window.ActiveXObject) { + var doc = new ActiveXObject('Microsoft.XMLDOM'); + doc.async = 'false'; + doc.loadXML(text); + } else { + var parser = new DOMParser(); + var doc = parser.parseFromString(text, 'text/xml'); + } + return doc; + } + // Helper functions End + + var sbgnmlToJson, jsonToSbgnml, jsonToNwt, uiUtilities, tdToJson, + sifToJson, graphUtilities, layoutToText, nwtToJson, jsonToSif,sbgnmlToCd,cdToSbgnml,sbgnmlToSbml,sbmlToSbgnml; + var updateGraph; + var options, cy; + + function fileUtilities (param) { + sbgnmlToJson = param.sbgnmlToJsonConverter; + nwtToJson = param.nwtToJsonConverter; + jsonToSbgnml = param.jsonToSbgnmlConverter; + jsonToNwt = param.jsonToNwtConverter; + jsonToSif = param.jsonToSifConverter; + uiUtilities = param.uiUtilities; + tdToJson = param.tdToJsonConverter; + sifToJson = param.sifToJsonConverter; + layoutToText = param.layoutToText; + graphUtilities = param.graphUtilities; + updateGraph = graphUtilities.updateGraph.bind(graphUtilities); + options = param.optionUtilities.getOptions(); + cy = param.sbgnCyInstance.getCy(); + sbgnmlToCd = param.sbgnmlToCdConverter; + cdToSbgnml = param.cdToSbgnmlConverter; + sbgnmlToSbml = param.sbgnmlToSbmlConverter; + sbmlToSbgnml = param.sbmlToSbgnmlConverter; + } + + fileUtilities.loadXMLDoc = loadXMLDoc; + + fileUtilities.saveAsPng = function(filename) { + var pngContent = cy.png({scale: 3, full: true}); + + // 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"); + }; + + fileUtilities.saveAsJpg = function(filename) { + var jpgContent = cy.jpg({scale: 3, full: true}); + + // 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"); + }; + + fileUtilities.saveAsSvg = function(filename) { + var svgContent = cy.svg({scale: 1, full: true}); + saveAs(new Blob([svgContent], {type:"image/svg+xml;charset=utf-8"}), filename || "network.svg"); + }; + + fileUtilities.loadSample = function(filename, folderpath, callback) { + var file = (folderpath || 'sample-app/samples/') + filename; + + uiUtilities.startSpinner("load-spinner"); + // Users may want to do customized things while a sample is being loaded + // Trigger an event for this purpose and specify the 'filename' as an event parameter + $(document).trigger( "sbgnvizLoadSample", [ filename, cy ] ); // Aliases for sbgnvizLoadSampleStart + $(document).trigger( "sbgnvizLoadSampleStart", [ filename, cy ] ); + var text = loadTextDoc(file); + var matchResult = text.match(""); + if(matchResult != null){ + var renderInfoString = matchResult[0]; + var renderInfoStringCopy = (' ' + renderInfoString).slice(1); + const regex = /\s([\S]+)([\s]*)=/g; + var result; + var matches = []; + while(result = regex.exec(renderInfoString)) { + matches.push(result[0]); + }; + matches.forEach(function(match){ + renderInfoString = renderInfoString.replace(match , textUtilities.FromKebabToCamelCase(match)); + }); + text = text.replace(renderInfoStringCopy, renderInfoString); + } + var xmlObject = textToXmlObject(text); + setTimeout(function () { + updateGraph(nwtToJson.convert(xmlObject)); + fileUtilities.collapseMarkedNodes(); + uiUtilities.endSpinner("load-spinner"); + $(document).trigger( "sbgnvizLoadSampleEnd", [ filename, cy ] ); // Trigger an event signaling that a sample is loaded + if (typeof callback !== 'undefined') { + callback(); } + },0); + }; + + fileUtilities.loadSIFFile = function(file, layoutBy, callback) { + var convert = function( text ) { + return sifToJson.convert(text); + }; + + var runLayout = function() { + if ( layoutBy ) { + if ( typeof layoutBy === 'function' ) { + layoutBy(); + } + else { + var layout = cy.layout( layoutBy ); + + // for backward compatibility need to make this if check + if ( layout && layout.run ) { + layout.run(); + } + } + } + + cy.fit( cy.elements(":visible"), 20 ); + + }; + + fileUtilities.loadFile( file, convert, undefined, callback, undefined, runLayout ); + }; + + fileUtilities.loadTDFile = function functionName(file, callback) { + var convert = function( text ) { + return tdToJson.convert(text); + }; + + fileUtilities.loadFile( file, convert, undefined, callback ); + }; + + fileUtilities.loadSBGNMLFile = function(file, callback1, callback2) { + var convert = function( text ) { + return sbgnmlToJson.convert(textToXmlObject(text)); + }; + + fileUtilities.loadFile( file, convert, callback1, callback2, fileUtilities.collapseMarkedNodes ); + }; + + fileUtilities.loadNwtFile = function(file, callback1, callback2, urlParams) { + var convert = function( text ) { + return nwtToJson.convert(textToXmlObject(text), urlParams); + }; + + fileUtilities.loadFile( file, convert, callback1, callback2, fileUtilities.collapseMarkedNodes ); + }; + + // collapse the nodes whose collapse data field is set + fileUtilities.collapseMarkedNodes = function() { + // collapse nodes + var nodesToCollapse = cy.nodes("[collapse]"); + if (nodesToCollapse.length > 0 ){ + cy.expandCollapse('get').collapse(nodesToCollapse, {layoutBy: null}); + + nodesToCollapse.forEach(function(ele, i, eles){ + ele.position(ele.data("positionBeforeSaving")); + }); + nodesToCollapse.removeData("positionBeforeSaving"); + } + }; + + /* + callback is a function remotely defined to add specific behavior that isn't implemented here. + it is completely optional. + signature: callback(textXml) + */ + fileUtilities.loadFile = function(file, convertFcn, callback1, callback2, callback3, callback4) { + var self = this; + uiUtilities.startSpinner("load-file-spinner"); + + var textType = /text.*/; + + var reader = new FileReader(); + + reader.onload = function (e) { + var text = this.result; + var matchResult = text.match(""); + if(matchResult != null){ + var imagesElementMatch = text.match(""); + var imagesElement; + if(imagesElementMatch != null){ + imagesElement = imagesElementMatch[0]; + } + var renderInfoString = matchResult[0]; + var renderInfoStringCopy = (' ' + renderInfoString).slice(1); + const regex = /\s([\S]+)([\s]*)=/g; + var result; + var matches = []; + while(result = regex.exec(renderInfoString)) { + matches.push(result[0]); + }; + matches.forEach(function(match){ + renderInfoString = renderInfoString.replace(match , textUtilities.FromKebabToCamelCase(match)); + }); + text = text.replace(renderInfoStringCopy, renderInfoString); + var imagesElementMatchDirty = text.match(""); + if(imagesElementMatchDirty != null){ + text = text.replace(imagesElementMatchDirty[0],imagesElement); + } + } + + setTimeout(function () { + + if (typeof callback1 !== 'undefined') callback1(text); + + var cyGraph; + try { + cyGraph = convertFcn( text ); + // Users may want to do customized things while an external file is being loaded + // Trigger an event for this purpose and specify the 'filename' as an event parameter + $(document).trigger( "sbgnvizLoadFile", [ file.name, cy ] ); // Aliases for sbgnvizLoadFileStart + $(document).trigger( "sbgnvizLoadFileStart", [ file.name, cy ] ); + } + catch (err) { + uiUtilities.endSpinner("load-file-spinner"); + console.log(err); + if (typeof callback2 !== 'undefined') callback2(); + return; + } + + updateGraph(cyGraph); + + if (typeof callback3 !== 'undefined') { + callback3(); + } + + uiUtilities.endSpinner("load-file-spinner"); + $(document).trigger( "sbgnvizLoadFileEnd", [ file.name, cy ] ); // Trigger an event signaling that a file is loaded + + if (typeof callback4 !== 'undefined') { + callback4(); + } + }, 0); + }; + + reader.readAsText(file); + }; + + fileUtilities.loadSBGNMLText = async function(textData, tileInfoBoxes, filename, cy, urlParams){ + await updateGraph(sbgnmlToJson.convert(textToXmlObject(textData), urlParams), undefined, undefined, tileInfoBoxes); + await $(document).trigger("sbgnvizLoadFileEnd", [filename, cy]); + uiUtilities.endSpinner("load-file-spinner"); + + + }; + + // supported versions are either 0.2 or 0.3 + fileUtilities.saveAsSbgnml = function(filename, version, renderInfo, mapProperties, nodes, edges) { + var sbgnmlText = jsonToSbgnml.createSbgnml(filename, version, renderInfo, mapProperties, nodes, edges); + var blob = new Blob([sbgnmlText], { + type: "text/plain;charset=utf-8;", + }); + saveAs(blob, filename); + }; + + // supported versions are either 0.2 or 0.3 + fileUtilities.saveAsNwt = function(filename, version, renderInfo, mapProperties, nodes, edges) { + var sbgnmlText = jsonToNwt.createNwt(filename, version, renderInfo, mapProperties, nodes, edges); + var blob = new Blob([sbgnmlText], { + type: "text/plain;charset=utf-8;", + }); + saveAs(blob, filename); + }; + + fileUtilities.saveAsCellDesigner = function(filename, errorCallback){ + uiUtilities.startSpinner("load-spinner"); + var sbgnml = jsonToSbgnml.createSbgnml(); + this.convertSbgnmlToCD(sbgnml, function(data){ + if(data == null){ + errorCallback(); + }else{ + var blob = new Blob([data], { + type: "text/plain;charset=utf-8;", + }); + saveAs(blob, filename); + } + uiUtilities.endSpinner("load-spinner"); + + }); + + + } + + fileUtilities.loadCellDesigner = function(file, successCallback, errorCallback){ + var reader = new FileReader(); + + reader.onload = function (e) { + + this.convertCDToSbgnml(e.target.result, function(data){ + uiUtilities.endSpinner("load-spinner"); + if(data == null){ + errorCallback(); + }else{ + successCallback(data); + } + }); + }.bind(this); + uiUtilities.startSpinner("load-spinner"); + reader.readAsText(file); + } + + fileUtilities.saveAsSbml = function(filename,errorCallback){ + uiUtilities.startSpinner("load-spinner"); + var sbgnml = this.convertSbgn(); + + this.convertSbgnmlToSbml(sbgnml, function(data){ + + if(!data.result){ + errorCallback(sbgnml,data.error); + }else if( data.message.indexOf("Internal server error") !== -1) + { + errorCallback(sbgnml,data.message); + }else{ + var blob = new Blob([data.message], { + type: "text/plain;charset=utf-8;", + }); + saveAs(blob, filename); + + } + + uiUtilities.endSpinner("load-spinner"); + + }); + + } + + fileUtilities.loadSbml = function(file, successCallback, errorCallback){ + var reader = new FileReader(); + + reader.onload = function (e) { + + this.convertSbmlToSbgnml(e.target.result, function(data){ + if(data == null){ + errorCallback(); + }else{ + successCallback(data); + } + }); + }.bind(this); + reader.readAsText(file); + + } + + + fileUtilities.convertSbgn= function(filename, version, renderInfo, mapProperties, nodes, edges) { + var sbgnmlText = jsonToSbgnml.createSbgnml(filename, "plain", renderInfo, mapProperties, nodes, edges); + + return sbgnmlText; +}; + + fileUtilities.exportLayoutData = function(filename, byName) { + var layoutText = layoutToText.convert( byName ); + + var blob = new Blob([layoutText], { + type: "text/plain;charset=utf-8;", + }); + saveAs(blob, filename); + }; + + fileUtilities.saveAsPlainSif = function(filename) { + var text = jsonToSif.convert(); + + var blob = new Blob([text], { + type: "text/plain;charset=utf-8;", + }); + saveAs(blob, filename); + }; + + fileUtilities.convertSbgnmlTextToJson = function(sbgnmlText){ + return sbgnmlToJson.convert(textToXmlObject(sbgnmlText)); + }; + + fileUtilities.convertSifTextToJson = function(sifText){ + return sifToJson.convert(sifText); + }; + +fileUtilities.createJsonFromSBGN = function(){ + + + var sbgnmlText = jsonToSbgnml.createSbgnml(); + return sbgnmlToJson.convert(textToXmlObject(sbgnmlText)); +}; + +fileUtilities.createJsonFromSif = function(){ + + var sifText = jsonToSif.convert(); + return sifToJson.convert(sifText); + +}; + +fileUtilities.convertSbgnmlToCD = function(sbgnml, callback){ + + return sbgnmlToCd.convert(sbgnml,callback); +}; + +fileUtilities.convertSbgnmlToSbml = function(sbgnml, callback){ + + return sbgnmlToSbml.convert(sbgnml,callback); +}; + +fileUtilities.convertSbmlToSbgnml = function(sbml, callback){ + return sbmlToSbgnml.convert(sbml,callback); +} +fileUtilities.convertCDToSbgnml = function(xml,callback){ + return cdToSbgnml.convert(xml,callback); +} + + + return fileUtilities; +}; + +},{"./lib-utilities":204,"./text-utilities":215}],197:[function(_dereq_,module,exports){ +/* + * Common utilities for sbgnviz graphs + */ + +var classes = _dereq_('./classes'); +var libUtilities = _dereq_('./lib-utilities'); +var libs = libUtilities.getLibs(); +var jQuery = $ = libs.jQuery; + +module.exports = function () { + var optionUtilities; + var options, cy; + + function graphUtilities (param) { + optionUtilities = param.optionUtilities; + options = optionUtilities.getOptions(); + cy = param.sbgnCyInstance.getCy(); + } + + // TODO make these initial values user options instead of hardcoding them here + graphUtilities.portsEnabled = true; + graphUtilities.compoundSizesConsidered = true; + + graphUtilities.disablePorts = function() { + graphUtilities.portsEnabled = false; + + cy.style().update(); + }; + + graphUtilities.enablePorts = function() { + graphUtilities.portsEnabled = true; + + cy.style().update(); + }; + + graphUtilities.arePortsEnabled = function() { + return graphUtilities.portsEnabled; + }; + + graphUtilities.considerCompoundSizes = function() { + graphUtilities.compoundSizesConsidered = true; + cy.style().update(); + }; + + graphUtilities.omitCompoundSizes = function() { + graphUtilities.compoundSizesConsidered = false; + cy.style().update(); + }; + + graphUtilities.areCompoundSizesConsidered = function() { + return graphUtilities.compoundSizesConsidered == true; + }; + + graphUtilities.updateGraph = function(cyGraph, callback, layoutOptions, tileInfoBoxes) { + + + var isLayoutRequired; + if(layoutOptions === undefined){ + isLayoutRequired = false; + } + else{ + isLayoutRequired = true; + } + + $(document).trigger( "updateGraphStart", cy ); + // Reset undo/redo stack and buttons when a new graph is loaded + if (options.undoable) { + cy.undoRedo().reset(); + // this.resetUndoRedoButtons(); + } + + cy.startBatch(); + // clear data + cy.remove('*'); + cy.add(cyGraph); + + //add position information to data for preset layout + var positionMap = {}; + cy.nodes().forEach(function(node) { + var xPos = node.data('bbox').x; + var yPos = node.data('bbox').y; + positionMap[node.data('id')] = {'x': xPos, 'y': yPos}; + + // assign correct parents to info boxes + var statesandinfos = node.data('statesandinfos'); + for (var j=0; j < statesandinfos.length; j++) { + classes.getAuxUnitClass(statesandinfos[j]).setParentRef(statesandinfos[j], node); + } + }); + + + //this.refreshPaddings(); // Recalculates/refreshes the compound paddings + cy.endBatch(); + + if(isLayoutRequired) { + var preferences = {}; + if(cy.nodes().length > 3000 || cy.edges().length > 3000) { + preferences.quality = "draft"; + } + preferences.animate = false; + preferences.randomize = true; + preferences = $.extend({}, layoutOptions, preferences); + var layout = cy.layout(preferences); + } + else { + var layout = cy.layout({ + name: 'preset', + positions: positionMap, + fit: true, + padding: 20 + }); + } + + // Check this for cytoscape.js backward compatibility + if (layout && layout.run) { + layout.run(); + } -var Url = _dereq_("url"); -var spawn = _dereq_("child_process").spawn; -var fs = _dereq_("fs"); + var performLayout = function(){ + cy.fit( cy.elements(":visible"), 20 ) + }; + // Update the style + cy.style().update(); + // Initilize the bend points once the elements are created + if (cy.edgeEditing && cy.edgeEditing('initialized')) { + cy.edgeEditing('get').initBendPoints(cy.edges()); + } -exports.XMLHttpRequest = function() { - "use strict"; - /** - * Private variables - */ - var self = this; - var http = _dereq_("http"); - var https = _dereq_("https"); - // Holds http.js objects - var request; - var response; + $(document).trigger( "updateGraphEnd", [cy, (isLayoutRequired || tileInfoBoxes) , performLayout]); + if (callback) callback(); + }; - // Request settings - var settings = {}; + graphUtilities.calculatePaddings = function(paddingPercent) { + //As default use the compound padding value + if (!paddingPercent) { + var compoundPadding = options.compoundPadding; + paddingPercent = typeof compoundPadding === 'function' ? compoundPadding.call() : compoundPadding; + } - // Disable header blacklist. - // Not part of XHR specs. - var disableHeaderCheck = false; + var nodes = cy.nodes(); + var total = 0; + var numOfSimples = 0; + for (var i = 0; i < nodes.length; i++) { + var theNode = nodes[i]; + if (theNode.children() == null || theNode.children().length == 0) { + total += Number(theNode.width()); + total += Number(theNode.height()); + numOfSimples++; + } + } - // Set some default headers - var defaultHeaders = { - "User-Agent": "node-XMLHttpRequest", - "Accept": "*/*", + var calc_padding = (paddingPercent / 100) * Math.floor(total / (2 * numOfSimples)); + if (calc_padding < 5) { + calc_padding = 5; + } + + return calc_padding; }; - var headers = {}; - var headersCase = {}; + graphUtilities.recalculatePaddings = graphUtilities.refreshPaddings = function() { + // this.calculatedPaddings is not working here + // TODO: replace this reference with this.calculatedPaddings once the reason is figured out + //graphUtilities.calculatedPaddings = this.calculatePaddings(); + var compoundPadding = options.compoundPadding; + return ( typeof compoundPadding === 'function') ? compoundPadding.call() : compoundPadding + //return graphUtilities.calculatedPaddings; + }; - // These headers are not user setable. - // The following are allowed but banned in the spec: - // * user-agent - var forbiddenRequestHeaders = [ - "accept-charset", - "accept-encoding", - "access-control-request-headers", - "access-control-request-method", - "connection", - "content-length", - "content-transfer-encoding", - "cookie", - "cookie2", - "date", - "expect", - "host", - "keep-alive", - "origin", - "referer", - "te", - "trailer", - "transfer-encoding", - "upgrade", - "via" - ]; + graphUtilities.getCompoundPaddings = function() { + // Return calculated paddings in case of that data is invalid return 5 + var compoundPadding = options.compoundPadding; + return ( typeof compoundPadding === 'function') ? compoundPadding.call() : compoundPadding - // These request methods are not allowed - var forbiddenRequestMethods = [ - "TRACE", - "TRACK", - "CONNECT" - ]; + //return graphUtilities.calculatedPaddings || 5; + }; - // Send flag - var sendFlag = false; - // Error flag, used when errors occur or abort is called - var errorFlag = false; + return graphUtilities; +} - // Event listeners - var listeners = {}; +},{"./classes":193,"./lib-utilities":204}],198:[function(_dereq_,module,exports){ +module.exports = function() { - /** - * Constants + var jsonToSbgnml, elementUtilities, cy; + + function jsonToNwt(param) { + jsonToSbgnml = param.jsonToSbgnmlConverter; + elementUtilities = param.elementUtilities; + cy = param.sbgnCyInstance.getCy(); + } + + function setToStr(set) { + if (set) { + return Object.keys(set).join(';'); + } + + return null; + } + + var sifEdgePropHandlerMap = { + 'pcIDs': function(edge) { + return setToStr( edge.data('pcIDSet') ); + }, + 'siteLocations': function(edge) { + return setToStr( edge.data('siteLocSet') ); + } + }; + + var sifNodePropHandlerMap = { + 'tooltip': function(node) { + return node.data('tooltip'); + }, + 'infoboxes': function(node, obj) { + var sifInfoboxPropHandlerMap = { + 'tooltip': function(infobox) { + return infobox.tooltip; + } + }; + + var infoboxes = node.data('statesandinfos'); + var glyphs = obj.glyph; + infoboxes.forEach( function(infobox, i) { + Object.keys(sifInfoboxPropHandlerMap).forEach( function(propName) { + var val = sifInfoboxPropHandlerMap[propName](infobox); + if (val) { + glyphs[i][propName] = val; + } + } ); + } ); + } + }; + + // objects consist of arcs or gylphs + function extendObjectsData(objs, filterFcn, propHandlerMap) { + if ( !objs ) { + return; + } + + objs.forEach( function( obj ) { + if ( filterFcn( obj.$.class ) ) { + var ele = cy.getElementById( obj.$.id ); + Object.keys( propHandlerMap ).forEach( function( propName ) { + // does not have to return a value, maybe a void function as well + var val = propHandlerMap[ propName ]( ele, obj ); + if ( val ) { + obj[ propName ] = val; + } + } ); + } + } ); + } + + function extendStylesData(toExtend, extendFrom) { + if ( !toExtend || !extendFrom ) { + return; + } + + var styleNames = [ 'shapeName' ]; + var styleMap = {}; + + Object.keys( extendFrom ).forEach( function( key ) { + styleNames.forEach( function( name ) { + var el = extendFrom[ key ]; + var props = el && el.properties; + + if ( props && props[ name ] ) { + var val = props[ name ]; + var idList = el.idList; + + idList.forEach( function( id ) { + styleMap[ id ] = styleMap[ id ] || {}; + styleMap[ id ][ name ] = val; + } ); + } + } ); + } ); + + toExtend.forEach( function( style ) { + var idList = style.$.idList.split(' '); + + styleNames.forEach( function( name ) { + var val = null; + + idList.forEach( function( id ) { + var currVal = styleMap[ id ] && styleMap[ id ][ name ]; + if ( currVal === undefined ) { + return; + } + + if ( val == null ) { + val = currVal; + } + else if ( val !== currVal ) { + console.warn( 'Shape name of some glyphs defined multiple times in render information!' ); + } + } ); + + if ( val !== null ) { + style.g.$[ name ] = val; + } + } ); + } ); + } + + jsonToNwt.buildJsObj = function(filename, version, renderInfo, mapProperties, nodes, edges) { + var jsObj = jsonToSbgnml.buildJsObj(filename, version, renderInfo, mapProperties, nodes, edges); + + if ( elementUtilities.mapType !== 'PD' && elementUtilities.mapType !== 'AF' && elementUtilities.mapType !== 'HybridSbgn') { + var map = jsObj.map[0]; + + var arcs = map.arc; + var glyphs = map.glyph; + extendObjectsData(arcs, elementUtilities.isSIFEdge, sifEdgePropHandlerMap); + extendObjectsData(glyphs, elementUtilities.isSIFNode, sifNodePropHandlerMap); + + var jsObjStyles = ( map && map.extension && map.extension.renderInformation + && map.extension.renderInformation.listOfStyles ).style; + var appStyles = renderInfo && renderInfo.styles; + + extendStylesData(jsObjStyles, appStyles); + } + + return jsObj; + }; + + jsonToNwt.createNwt = function(filename, version, renderInfo, mapProperties, nodes, edges) { + var jsObj = jsonToNwt.buildJsObj(filename, version, renderInfo, mapProperties, nodes, edges); + return jsonToSbgnml.buildString({sbgn: jsObj}); + }; + + return jsonToNwt; +} + +},{}],199:[function(_dereq_,module,exports){ + +var libsbgnjs = _dereq_('libsbgn.js'); +var renderExtension = libsbgnjs.render; +var annot = libsbgnjs.annot; +var pkgVersion = _dereq_('../../package.json').version; // need info about sbgnviz to put in xml +var pkgName = _dereq_('../../package.json').name; +var prettyprint = _dereq_('pretty-data').pd; +var xml2js = _dereq_('xml2js'); +var mapPropertiesBuilder = new xml2js.Builder({rootName: "mapProperties"}); +var compoundExtensionBuilder = new xml2js.Builder({rootName: "extraInfo"}); +var textUtilities = _dereq_('./text-utilities'); + +module.exports = function () { + var elementUtilities, graphUtilities, experimentalDataOverlay; + var cy; + + /* + takes renderInfo as an optional argument. It contains all the information needed to save + the style and colors to the render extension. See newt/app-utilities getAllStyles() + Structure: { + background: the map background color, + colors: { + validXmlValue: color_id + ... + }, + styles: { + styleKey1: { + idList: list of the nodes ids that have this style + properties: { + fontSize: ... + fill: ... + ... + } + } + styleKey2: ... + ... + } + } + */ + function jsonToSbgnml (param) { + elementUtilities = param.elementUtilities; + graphUtilities = param.graphUtilities; + experimentalDataOverlay = param.experimentalDataOverlay; + cy = param.sbgnCyInstance.getCy(); + } + + /* + 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. */ + jsonToSbgnml.buildJsObj = function(filename, version, renderInfo, mapProperties, nodes, edges){ + var self = this; + var mapID = textUtilities.getXMLValidId(filename); + var hasExtension = false; + var hasRenderExtension = false; + var mapType = ( mapProperties && mapProperties.mapType ) || elementUtilities.mapType; + this.nodes = nodes || cy.nodes(); + this.edges = edges || cy.edges(); - this.UNSENT = 0; - this.OPENED = 1; - this.HEADERS_RECEIVED = 2; - this.LOADING = 3; - this.DONE = 4; + var collapsedChildren = elementUtilities.getAllCollapsedChildrenRecursively(this.nodes); + this.allCollapsedNodes = collapsedChildren.filter("node"); + this.allCollapsedEdges = collapsedChildren.filter("edge"); + + if (typeof renderInfo !== 'undefined') { + hasExtension = true; + hasRenderExtension = true; + } + + if(typeof version === 'undefined') { + // default if not specified + version = "0.3"; + } + + // check version validity + if(version !== "0.2" && version !== "0.3" && version !== "plain" && version !== "plain3") { + console.error("Invalid SBGN-ML version provided. Expected 0.2, 0.3, plain or plain3, got: " + version); + return "Error"; + } + + var mapLanguage = elementUtilities.mapTypeToLanguage(mapType); + + //add headers + xmlHeader = "\n"; + var versionNo; + if(version === "plain"){ + versionNo = "0.2"; + }else if(version === "plain3"){ + versionNo = "0.3"; + }else{ + versionNo = 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" || version ==="plain3") { + var map = new libsbgnjs.Map({language: mapLanguage, id: mapID}); + } + else if(version === "0.2" || version === "plain") { + var map = new libsbgnjs.Map({language: mapLanguage}); + } + + if (hasExtension) { // extension is there + var extension = new libsbgnjs.Extension(); + if (hasRenderExtension) { + extension.add(self.getRenderExtensionSbgnml(renderInfo)); + } + map.setExtension(extension); + if (mapProperties) { + delete mapProperties.experimentDescription; + var xml = mapPropertiesBuilder.buildObject(mapProperties); + map.extension.add(xml); + } + + } else if (mapProperties) { + map.setExtension(new libsbgnjs.Extension()); + map.extension.add(mapPropertiesBuilder.buildObject(mapProperties)); + } + + // get all glyphs + var glyphList = []; + // be careful that :visible is also used during recursive search of nodes + // in the getGlyphSbgnml function. If not set accordingly, discrepancies will occur. + var self = this; + this.nodes.each(function(ele, i){ + if(typeof ele === "number") { + ele = i; + } + if(jsonToSbgnml.childOfNone(ele, self.nodes)) + glyphList = glyphList.concat(self.getGlyphSbgnml(ele)); // returns potentially more than 1 glyph + }); + // add them to the map + for(var i=0; i"); + if(matchResult != null){ + var imagesElementMatch = xmlString.match(""); + var imagesElement; + if(imagesElementMatch != null){ + imagesElement = imagesElementMatch[0]; + } + var renderInfoString = matchResult[0]; + var renderInfoStringCopy = (' ' + renderInfoString).slice(1); + const regex = /\s([\S]+)([\s]*)=/g; + var result; + var matches = []; + while(result = regex.exec(renderInfoString)) { + matches.push(result[0]); + }; + matches.forEach(function(match){ + + if(match != " idList=") + renderInfoString = renderInfoString.replace(match , textUtilities.FromCamelToKebabCase(match)); + }); + + xmlString = xmlString.replace(renderInfoStringCopy, renderInfoString); + var imagesElementMatchDirty = xmlString.match(""); + if(imagesElementMatchDirty != null){ + xmlString = xmlString.replace(imagesElementMatchDirty[0],imagesElement); + } + } + + /* dirty hack needed to solve the newline char encoding problem + xml2js doesn't encode \n as we need to do it manually + */ + var re = /