1 /* 2 * jQuery RDF @VERSION 3 * 4 * Copyright (c) 2008,2009 Jeni Tennison 5 * Licensed under the MIT (MIT-LICENSE.txt) 6 * 7 * Depends: 8 * jquery.uri.js 9 * jquery.xmlns.js 10 * jquery.datatype.js 11 * jquery.curie.js 12 * jquery.json.js 13 */ 14 /** 15 * @fileOverview jQuery RDF 16 * @author <a href="mailto:jeni@jenitennison.com">Jeni Tennison</a> 17 * @copyright (c) 2008,2009 Jeni Tennison 18 * @license MIT license (MIT-LICENSE.txt) 19 * @version 1.0 20 */ 21 /** 22 * @exports $ as jQuery 23 */ 24 /** 25 * @ignore 26 */ 27 (function ($) { 28 var 29 memResource = {}, 30 memBlank = {}, 31 memLiteral = {}, 32 memTriple = {}, 33 memPattern = {}, 34 35 xsdNs = "http://www.w3.org/2001/XMLSchema#", 36 rdfNs = "http://www.w3.org/1999/02/22-rdf-syntax-ns#", 37 rdfsNs = "http://www.w3.org/2000/01/rdf-schema#", 38 39 uriRegex = /^<(([^>]|\\>)*)>$/, 40 literalRegex = /^("""((\\"|[^"])*)"""|"((\\"|[^"])*)")(@([a-z]+(-[a-z0-9]+)*)|\^\^(.+))?$/, 41 tripleRegex = /(("""((\\"|[^"])*)""")|("(\\"|[^"]|)*")|(<(\\>|[^>])*>)|\S)+/g, 42 43 blankNodeSeed = databankSeed = new Date().getTime() % 1000, 44 blankNodeID = function () { 45 blankNodeSeed += 1; 46 return 'b' + blankNodeSeed.toString(16); 47 }, 48 49 databankID = function () { 50 databankSeed += 1; 51 return 'data' + databankSeed.toString(16); 52 }, 53 databanks = {}, 54 55 documentQueue = {}, 56 57 subject = function (subject, opts) { 58 if (typeof subject === 'string') { 59 try { 60 return $.rdf.resource(subject, opts); 61 } catch (e) { 62 try { 63 return $.rdf.blank(subject, opts); 64 } catch (f) { 65 throw "Bad Triple: Subject " + subject + " is not a resource: " + f; 66 } 67 } 68 } else { 69 return subject; 70 } 71 }, 72 73 property = function (property, opts) { 74 if (property === 'a') { 75 return $.rdf.type; 76 } else if (typeof property === 'string') { 77 try { 78 return $.rdf.resource(property, opts); 79 } catch (e) { 80 throw "Bad Triple: Property " + property + " is not a resource: " + e; 81 } 82 } else { 83 return property; 84 } 85 }, 86 87 object = function (object, opts) { 88 if (typeof object === 'string') { 89 try { 90 return $.rdf.resource(object, opts); 91 } catch (e) { 92 try { 93 return $.rdf.blank(object, opts); 94 } catch (f) { 95 try { 96 return $.rdf.literal(object, opts); 97 } catch (g) { 98 throw "Bad Triple: Object " + object + " is not a resource or a literal " + g; 99 } 100 } 101 } 102 } else { 103 return object; 104 } 105 }, 106 107 testResource = function (resource, filter, existing) { 108 var variable; 109 if (typeof filter === 'string') { 110 variable = filter.substring(1); 111 if (existing[variable] && existing[variable] !== resource) { 112 return null; 113 } else { 114 existing[variable] = resource; 115 return existing; 116 } 117 } else if (filter === resource) { 118 return existing; 119 } else { 120 return null; 121 } 122 }, 123 124 findMatches = function (databank, pattern) { 125 if (databank.union === undefined) { 126 if (pattern.subject.type !== undefined) { 127 if (databank.subjectIndex[pattern.subject] === undefined) { 128 return []; 129 } 130 return $.map(databank.subjectIndex[pattern.subject], function (triple) { 131 var bindings = pattern.exec(triple); 132 return bindings === null ? null : { bindings: bindings, triples: [triple] }; 133 }); 134 } else if (pattern.object.type === 'uri' || pattern.object.type === 'bnode') { 135 if (databank.objectIndex[pattern.object] === undefined) { 136 return []; 137 } 138 return $.map(databank.objectIndex[pattern.object], function (triple) { 139 var bindings = pattern.exec(triple); 140 return bindings === null ? null : { bindings: bindings, triples: [triple] }; 141 }); 142 } else if (pattern.property.type !== undefined) { 143 if (databank.propertyIndex[pattern.property] === undefined) { 144 return []; 145 } 146 return $.map(databank.propertyIndex[pattern.property], function (triple) { 147 var bindings = pattern.exec(triple); 148 return bindings === null ? null : { bindings: bindings, triples: [triple] }; 149 }); 150 } 151 } 152 return $.map(databank.triples(), function (triple) { 153 var bindings = pattern.exec(triple); 154 return bindings === null ? null : { bindings: bindings, triples: [triple] }; 155 }); 156 }, 157 158 mergeMatches = function (existingMs, newMs, optional) { 159 return $.map(existingMs, function (existingM, i) { 160 var compatibleMs = $.map(newMs, function (newM) { 161 // For newM to be compatible with existingM, all the bindings 162 // in newM must either be the same as in existingM, or not 163 // exist in existingM 164 var k, b, isCompatible = true; 165 for (k in newM.bindings) { 166 b = newM.bindings[k]; 167 if (!(existingM.bindings[k] === undefined || 168 existingM.bindings[k] === b)) { 169 isCompatible = false; 170 break; 171 } 172 } 173 return isCompatible ? newM : null; 174 }); 175 if (compatibleMs.length > 0) { 176 return $.map(compatibleMs, function (compatibleM) { 177 return { 178 bindings: $.extend({}, existingM.bindings, compatibleM.bindings), 179 triples: unique(existingM.triples.concat(compatibleM.triples)) 180 }; 181 }); 182 } else { 183 return optional ? existingM : null; 184 } 185 }); 186 }, 187 188 registerQuery = function (databank, query) { 189 var s, p, o; 190 if (query.filterExp !== undefined && !$.isFunction(query.filterExp)) { 191 if (databank.union === undefined) { 192 s = typeof query.filterExp.subject === 'string' ? '' : query.filterExp.subject; 193 p = typeof query.filterExp.property === 'string' ? '' : query.filterExp.property; 194 o = typeof query.filterExp.object === 'string' ? '' : query.filterExp.object; 195 if (databank.queries[s] === undefined) { 196 databank.queries[s] = {}; 197 } 198 if (databank.queries[s][p] === undefined) { 199 databank.queries[s][p] = {}; 200 } 201 if (databank.queries[s][p][o] === undefined) { 202 databank.queries[s][p][o] = []; 203 } 204 databank.queries[s][p][o].push(query); 205 } else { 206 $.each(databank.union, function (i, databank) { 207 registerQuery(databank, query); 208 }); 209 } 210 } 211 }, 212 213 resetQuery = function (query) { 214 query.length = 0; 215 query.matches = []; 216 $.each(query.children, function (i, child) { 217 resetQuery(child); 218 }); 219 $.each(query.partOf, function (i, union) { 220 resetQuery(union); 221 }); 222 }, 223 224 updateQuery = function (query, matches) { 225 if (matches.length > 0) { 226 $.each(query.children, function (i, child) { 227 leftActivate(child, matches); 228 }); 229 $.each(query.partOf, function (i, union) { 230 updateQuery(union, matches); 231 }); 232 $.each(matches, function (i, match) { 233 query.matches.push(match); 234 Array.prototype.push.call(query, match.bindings); 235 }); 236 } 237 }, 238 239 filterMatches = function (matches, variables) { 240 var i, bindings, triples, j, k, variable, value, nvariables = variables.length, 241 newbindings, match = {}, keyobject = {}, keys = {}, filtered = []; 242 for (i = 0; i < matches.length; i += 1) { 243 bindings = matches[i].bindings; 244 triples = matches[i].triples; 245 keyobject = keys; 246 for (j = 0; j < nvariables; j += 1) { 247 variable = variables[j]; 248 value = bindings[variable]; 249 if (j === nvariables - 1) { 250 if (keyobject[value] === undefined) { 251 match = { bindings: {}, triples: triples }; 252 for (k = 0; k < nvariables; k += 1) { 253 match.bindings[variables[k]] = bindings[variables[k]]; 254 } 255 keyobject[value] = match; 256 filtered.push(match); 257 } else { 258 match = keyobject[value]; 259 match.triples = match.triples.concat(triples); 260 } 261 } else { 262 if (keyobject[value] === undefined) { 263 keyobject[value] = {}; 264 } 265 keyobject = keyobject[value]; 266 } 267 } 268 } 269 return filtered; 270 }, 271 272 renameMatches = function (matches, old) { 273 var i, match, newMatch, keys = {}, renamed = []; 274 for (i = 0; i < matches.length; i += 1) { 275 match = matches[i]; 276 if (keys[match.bindings[old]] === undefined) { 277 newMatch = { 278 bindings: { node: match.bindings[old] }, 279 triples: match.triples 280 }; 281 renamed.push(newMatch); 282 keys[match.bindings[old]] = newMatch; 283 } else { 284 newMatch = keys[match.bindings[old]]; 285 newMatch.triples = newMatch.triples.concat(match.triples); 286 } 287 } 288 return renamed; 289 }, 290 291 leftActivate = function (query, matches) { 292 var newMatches; 293 if (query.union === undefined) { 294 if (query.top || query.parent.top) { 295 newMatches = query.alphaMemory; 296 } else { 297 matches = matches || query.parent.matches; 298 if ($.isFunction(query.filterExp)) { 299 newMatches = $.map(matches, function (match, i) { 300 return query.filterExp.call(match.bindings, i, match.bindings, match.triples) ? match : null; 301 }); 302 } else if (query.filterExp !== undefined) { 303 newMatches = mergeMatches(matches, query.alphaMemory, query.filterExp.optional); 304 } else { 305 newMatches = matches; 306 } 307 } 308 } else { 309 newMatches = $.map(query.union, function (q) { 310 return q.matches; 311 }); 312 } 313 if (query.selections !== undefined) { 314 newMatches = filterMatches(newMatches, query.selections); 315 } else if (query.navigate !== undefined) { 316 newMatches = renameMatches(newMatches, query.navigate); 317 } 318 updateQuery(query, newMatches); 319 }, 320 321 rightActivate = function (query, match) { 322 var newMatches; 323 if (query.filterExp.optional) { 324 resetQuery(query); 325 leftActivate(query); 326 } else { 327 if (query.top || query.parent.top) { 328 newMatches = [match]; 329 } else { 330 newMatches = mergeMatches(query.parent.matches, [match], false); 331 } 332 updateQuery(query, newMatches); 333 } 334 }, 335 336 addToQuery = function (query, triple) { 337 var match, 338 bindings = query.filterExp.exec(triple); 339 if (bindings !== null) { 340 match = { triples: [triple], bindings: bindings }; 341 query.alphaMemory.push(match); 342 rightActivate(query, match); 343 } 344 }, 345 346 removeFromQuery = function (query, triple) { 347 query.alphaMemory.splice($.inArray(triple, query.alphaMemory), 1); 348 resetQuery(query); 349 leftActivate(query); 350 }, 351 352 addToQueries = function (queries, triple) { 353 $.each(queries, function (i, query) { 354 addToQuery(query, triple); 355 }); 356 }, 357 358 removeFromQueries = function (queries, triple) { 359 $.each(queries, function (i, query) { 360 removeFromQuery(query, triple); 361 }); 362 }, 363 364 addToDatabankQueries = function (databank, triple) { 365 var s = triple.subject, 366 p = triple.property, 367 o = triple.object; 368 if (databank.union === undefined) { 369 if (databank.queries[s] !== undefined) { 370 if (databank.queries[s][p] !== undefined) { 371 if (databank.queries[s][p][o] !== undefined) { 372 addToQueries(databank.queries[s][p][o], triple); 373 } 374 if (databank.queries[s][p][''] !== undefined) { 375 addToQueries(databank.queries[s][p][''], triple); 376 } 377 } 378 if (databank.queries[s][''] !== undefined) { 379 if (databank.queries[s][''][o] !== undefined) { 380 addToQueries(databank.queries[s][''][o], triple); 381 } 382 if (databank.queries[s][''][''] !== undefined) { 383 addToQueries(databank.queries[s][''][''], triple); 384 } 385 } 386 } 387 if (databank.queries[''] !== undefined) { 388 if (databank.queries[''][p] !== undefined) { 389 if (databank.queries[''][p][o] !== undefined) { 390 addToQueries(databank.queries[''][p][o], triple); 391 } 392 if (databank.queries[''][p][''] !== undefined) { 393 addToQueries(databank.queries[''][p][''], triple); 394 } 395 } 396 if (databank.queries[''][''] !== undefined) { 397 if (databank.queries[''][''][o] !== undefined) { 398 addToQueries(databank.queries[''][''][o], triple); 399 } 400 if (databank.queries[''][''][''] !== undefined) { 401 addToQueries(databank.queries[''][''][''], triple); 402 } 403 } 404 } 405 } else { 406 $.each(databank.union, function (i, databank) { 407 addToDatabankQueries(databank, triple); 408 }); 409 } 410 }, 411 412 removeFromDatabankQueries = function (databank, triple) { 413 var s = triple.subject, 414 p = triple.property, 415 o = triple.object; 416 if (databank.union === undefined) { 417 if (databank.queries[s] !== undefined) { 418 if (databank.queries[s][p] !== undefined) { 419 if (databank.queries[s][p][o] !== undefined) { 420 removeFromQueries(databank.queries[s][p][o], triple); 421 } 422 if (databank.queries[s][p][''] !== undefined) { 423 removeFromQueries(databank.queries[s][p][''], triple); 424 } 425 } 426 if (databank.queries[s][''] !== undefined) { 427 if (databank.queries[s][''][o] !== undefined) { 428 removeFromQueries(databank.queries[s][''][o], triple); 429 } 430 if (databank.queries[s][''][''] !== undefined) { 431 removeFromQueries(databank.queries[s][''][''], triple); 432 } 433 } 434 } 435 if (databank.queries[''] !== undefined) { 436 if (databank.queries[''][p] !== undefined) { 437 if (databank.queries[''][p][o] !== undefined) { 438 removeFromQueries(databank.queries[''][p][o], triple); 439 } 440 if (databank.queries[''][p][''] !== undefined) { 441 removeFromQueries(databank.queries[''][p][''], triple); 442 } 443 } 444 if (databank.queries[''][''] !== undefined) { 445 if (databank.queries[''][''][o] !== undefined) { 446 removeFromQueries(databank.queries[''][''][o], triple); 447 } 448 if (databank.queries[''][''][''] !== undefined) { 449 removeFromQueries(databank.queries[''][''][''], triple); 450 } 451 } 452 } 453 } else { 454 $.each(databank.union, function (i, databank) { 455 removeFromDatabankQueries(databank, triple); 456 }); 457 } 458 }, 459 460 group = function (bindings, variables, base) { 461 var variable = variables[0], grouped = {}, results = [], i, newbase; 462 base = base || {}; 463 if (variables.length === 0) { 464 for (i = 0; i < bindings.length; i += 1) { 465 for (v in bindings[i]) { 466 if (base[v] === undefined) { 467 base[v] = []; 468 } 469 if ($.isArray(base[v])) { 470 base[v].push(bindings[i][v]); 471 } 472 } 473 } 474 return [base]; 475 } 476 // collect together the grouped results 477 for (i = 0; i < bindings.length; i += 1) { 478 key = bindings[i][variable]; 479 if (grouped[key] === undefined) { 480 grouped[key] = []; 481 } 482 grouped[key].push(bindings[i]); 483 } 484 // call recursively on each group 485 variables = variables.splice(1, 1); 486 for (v in grouped) { 487 newbase = $.extend({}, base); 488 newbase[variable] = grouped[v][0][variable]; 489 results = results.concat(group(grouped[v], variables, newbase)); 490 } 491 return results; 492 }, 493 494 queue = function (databank, url, callbacks) { 495 if (documentQueue[databank.id] === undefined) { 496 documentQueue[databank.id] = {}; 497 } 498 if (documentQueue[databank.id][url] === undefined) { 499 documentQueue[databank.id][url] = callbacks; 500 return false; 501 } 502 return true; 503 }, 504 505 dequeue = function (databank, url, result, args) { 506 var callbacks = documentQueue[databank.id][url]; 507 if ($.isFunction(callbacks[result])) { 508 callbacks[result].call(databank, args); 509 } 510 documentQueue[databank.id][url] = undefined; 511 }, 512 513 unique = function( b ) { 514 var a = []; 515 var l = b.length; 516 for(var i=0; i<l; i++) { 517 for(var j=i+1; j<l; j++) { 518 // If b[i] is found later in the array 519 if (b[i] === b[j]) 520 j = ++i; 521 } 522 a.push(b[i]); 523 } 524 return a; 525 }; 526 527 528 $.typedValue.types['http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral'] = { 529 regex: /^.*$/m, 530 strip: false, 531 value: function (v) { 532 return v; 533 } 534 }; 535 536 /** 537 * <p>Creates a new jQuery.rdf object. This should be invoked as a method rather than constructed using new; indeed you will usually want to generate these objects using a method such as {@link jQuery#rdf} or {@link jQuery.rdf#where}.</p> 538 * @class <p>A jQuery.rdf object represents the results of a query over its {@link jQuery.rdf#databank}. The results of a query are a sequence of objects which represent the bindings of values to the variables used in filter expressions specified using {@link jQuery.rdf#where} or {@link jQuery.rdf#optional}. Each of the objects in this sequence has associated with it a set of triples that are the sources for the variable bindings, which you can get at using {@link jQuery.rdf#sources}.</p> 539 * <p>The {@link jQuery.rdf} object itself is a lot like a {@link jQuery} object. It has a {@link jQuery.rdf#length} and the individual matches can be accessed using <code>[<var>n</var>]</code>, but you can also iterate through the matches using {@link jQuery.rdf#map} or {@link jQuery.rdf#each}.</p> 540 * <p>{@link jQuery.rdf} is designed to mirror the functionality of <a href="http://www.w3.org/TR/rdf-sparql-query/">SPARQL</a> while providing an interface that's familiar and easy to use for jQuery programmers.</p> 541 * @param {Object} [options] 542 * @param {jQuery.rdf.databank} [options.databank] The databank that this query should operate over. 543 * @param {jQuery.rdf.triple[]} [options.triples] A set of triples over which the query operates; this is only used if options.databank isn't specified, in which case a new databank with these triples is generated. 544 * @param {Object} [options.namespaces] An object representing a set of namespace bindings. Rather than passing this in when you construct the {@link jQuery.rdf} instance, you will usually want to use the {@link jQuery.rdf#prefix} method. 545 * @param {String|jQuery.uri} [options.base] The base URI used to interpret any relative URIs used within the query. 546 * @returns {jQuery.rdf} 547 * @example rdf = jQuery.rdf(); 548 * @see jQuery#rdf 549 */ 550 $.rdf = function (options) { 551 return new $.rdf.fn.init(options); 552 }; 553 554 $.rdf.fn = $.rdf.prototype = { 555 /** 556 * The version of rdfQuery. 557 * @type String 558 */ 559 rdfquery: '1.1', 560 561 init: function (options) { 562 var databanks, i; 563 options = options || {}; 564 /* must specify either a parent or a union, otherwise it's the top */ 565 this.parent = options.parent; 566 this.union = options.union; 567 this.top = this.parent === undefined && this.union === undefined; 568 if (this.union === undefined) { 569 if (options.databank === undefined) { 570 /** 571 * The databank over which this query operates. 572 * @type jQuery.rdf.databank 573 */ 574 this.databank = this.parent === undefined ? $.rdf.databank(options.triples, options) : this.parent.databank; 575 } else { 576 this.databank = options.databank; 577 } 578 } else { 579 databanks = $.map(this.union, function (query) { 580 return query.databank; 581 }); 582 databanks = unique(databanks); 583 if (databanks[1] !== undefined) { 584 this.databank = $.rdf.databank(undefined, { union: databanks }); 585 } else { 586 this.databank = databanks[0]; 587 } 588 } 589 this.children = []; 590 this.partOf = []; 591 this.filterExp = options.filter; 592 this.selections = options.distinct; 593 this.navigate = options.navigate; 594 this.alphaMemory = []; 595 this.matches = []; 596 /** 597 * The number of matches represented by the {@link jQuery.rdf} object. 598 * @type Integer 599 */ 600 this.length = 0; 601 if (this.filterExp !== undefined) { 602 if (!$.isFunction(this.filterExp)) { 603 registerQuery(this.databank, this); 604 this.alphaMemory = findMatches(this.databank, this.filterExp); 605 } 606 } else if (options.nodes !== undefined) { 607 this.alphaMemory = []; 608 for (i = 0; i < options.nodes.length; i += 1) { 609 this.alphaMemory.push({ 610 bindings: { node: options.nodes[i] }, 611 triples: [] 612 }); 613 } 614 } 615 leftActivate(this); 616 return this; 617 }, 618 619 /** 620 * Sets or returns the base URI of the {@link jQuery.rdf#databank}. 621 * @param {String|jQuery.uri} [base] 622 * @returns A {@link jQuery.uri} if no base URI is specified, otherwise returns this {@link jQuery.rdf} object. 623 * @example baseURI = jQuery('html').rdf().base(); 624 * @example jQuery('html').rdf().base('http://www.example.org/'); 625 * @see jQuery.rdf.databank#base 626 */ 627 base: function (base) { 628 if (base === undefined) { 629 return this.databank.base(); 630 } else { 631 this.databank.base(base); 632 return this; 633 } 634 }, 635 636 /** 637 * Sets or returns a namespace binding on the {@link jQuery.rdf#databank}. 638 * @param {String} [prefix] 639 * @param {String} [namespace] 640 * @returns {Object|jQuery.uri|jQuery.rdf} If no prefix or namespace is specified, returns an object providing all namespace bindings on the {@link jQuery.rdf.databank}. If a prefix is specified without a namespace, returns the {@link jQuery.uri} associated with that prefix. Otherwise returns this {@link jQuery.rdf} object after setting the namespace binding. 641 * @example namespace = jQuery('html').rdf().prefix('foaf'); 642 * @example jQuery('html').rdf().prefix('foaf', 'http://xmlns.com/foaf/0.1/'); 643 * @see jQuery.rdf.databank#prefix 644 */ 645 prefix: function (prefix, namespace) { 646 if (namespace === undefined) { 647 return this.databank.prefix(prefix); 648 } else { 649 this.databank.prefix(prefix, namespace); 650 return this; 651 } 652 }, 653 654 /** 655 * Adds a triple to the {@link jQuery.rdf#databank} or another {@link jQuery.rdf} object to create a union. 656 * @param {String|jQuery.rdf.triple|jQuery.rdf.pattern|jQuery.rdf} triple The triple, {@link jQuery.rdf.pattern} or {@link jQuery.rdf} object to be added to this one. If the triple is a {@link jQuery.rdf} object, the two queries are unioned together. If the triple is a string, it's parsed as a {@link jQuery.rdf.pattern}. The pattern will be completed using the current matches on the {@link jQuery.rdf} object to create multiple triples, one for each set of bindings. 657 * @param {Object} [options] 658 * @param {Object} [options.namespaces] An object representing a set of namespace bindings used to interpret CURIEs within the triple. Defaults to the namespace bindings defined on the {@link jQuery.rdf#databank}. 659 * @param {String|jQuery.uri} [options.base] The base URI used to interpret any relative URIs used within the triple. Defaults to the base URI defined on the {@link jQuery.rdf#databank}. 660 * @returns {jQuery.rdf} This {@link jQuery.rdf} object. 661 * @example 662 * var rdf = $.rdf() 663 * .prefix('dc', ns.dc) 664 * .prefix('foaf', ns.foaf) 665 * .add('<photo1.jpg> dc:creator <http://www.blogger.com/profile/1109404> .') 666 * .add('<http://www.blogger.com/profile/1109404> foaf:img <photo1.jpg> .'); 667 * @example 668 * var rdfA = $.rdf() 669 * .prefix('dc', ns.dc) 670 * .add('<photo1.jpg> dc:creator "Jane"'); 671 * var rdfB = $.rdf() 672 * .prefix('foaf', ns.foaf) 673 * .add('<photo1.jpg> foaf:depicts "Jane"'); 674 * var rdf = rdfA.add(rdfB); 675 * @see jQuery.rdf.databank#add 676 */ 677 add: function (triple, options) { 678 var query, databank; 679 if (triple.rdfquery !== undefined) { 680 if (triple.top) { 681 databank = this.databank.add(triple.databank); 682 query = $.rdf({ parent: this.parent, databank: databank }); 683 return query; 684 } else if (this.top) { 685 databank = triple.databank.add(this.databank); 686 query = $.rdf({ parent: triple.parent, databank: databank }); 687 return query; 688 } else if (this.union === undefined) { 689 query = $.rdf({ union: [this, triple] }); 690 this.partOf.push(query); 691 triple.partOf.push(query); 692 return query; 693 } else { 694 this.union.push(triple); 695 triple.partOf.push(this); 696 } 697 } else { 698 if (typeof triple === 'string') { 699 options = $.extend({}, { base: this.base(), namespaces: this.prefix(), source: triple }, options); 700 triple = $.rdf.pattern(triple, options); 701 } 702 if (triple.isFixed()) { 703 this.databank.add(triple.triple(), options); 704 } else { 705 query = this; 706 this.each(function (i, data) { 707 var t = triple.triple(data); 708 if (t !== null) { 709 query.databank.add(t, options); 710 } 711 }); 712 } 713 } 714 return this; 715 }, 716 717 /** 718 * Removes a triple or several triples from the {@link jQuery.rdf#databank}. 719 * @param {String|jQuery.rdf.triple|jQuery.rdf.pattern} triple The triple to be removed, or a {@link jQuery.rdf.pattern} that matches the triples that should be removed. 720 * @param {Object} [options] 721 * @param {Object} [options.namespaces] An object representing a set of namespace bindings used to interpret any CURIEs within the triple or pattern. Defaults to the namespace bindings defined on the {@link jQuery.rdf#databank}. 722 * @param {String|jQuery.uri} [options.base] The base URI used to interpret any relative URIs used within the triple or pattern. Defaults to the base URI defined on the {@link jQuery.rdf#databank}. 723 * @returns {jQuery.rdf} The {@link jQuery.rdf} object itself. 724 * @example 725 * var rdf = $('html').rdf() 726 * .prefix('foaf', ns.foaf) 727 * .where('?person foaf:givenname ?gname') 728 * .where('?person foaf:family_name ?fname') 729 * .remove('?person foaf:family_name ?fname'); 730 * @see jQuery.rdf.databank#remove 731 */ 732 remove: function (triple, options) { 733 if (typeof triple === 'string') { 734 options = $.extend({}, { base: this.base(), namespaces: this.prefix() }, options); 735 triple = $.rdf.pattern(triple, options); 736 } 737 if (triple.isFixed()) { 738 this.databank.remove(triple.triple(), options); 739 } else { 740 query = this; 741 this.each(function (i, data) { 742 var t = triple.triple(data); 743 if (t !== null) { 744 query.databank.remove(t, options); 745 } 746 }); 747 } 748 return this; 749 }, 750 751 /** 752 * Loads some data into the {@link jQuery.rdf#databank} 753 * @param data 754 * @param {Object} [options] 755 * @see jQuery.rdf.databank#load 756 */ 757 load: function (data, options) { 758 var rdf = this, 759 options = options || {}, 760 success = options.success; 761 if (success !== undefined) { 762 options.success = function () { 763 success.call(rdf); 764 } 765 } 766 this.databank.load(data, options); 767 return this; 768 }, 769 770 /** 771 * Creates a new {@link jQuery.rdf} object whose databank contains all the triples in this object's databank except for those in the argument's databank. 772 * @param {jQuery.rdf} query 773 * @see jQuery.rdf.databank#except 774 */ 775 except: function (query) { 776 return $.rdf({ databank: this.databank.except(query.databank) }); 777 }, 778 779 /** 780 * Creates a new {@link jQuery.rdf} object that is the result of filtering the matches on this {@link jQuery.rdf} object based on the filter that's passed into it. 781 * @param {String|jQuery.rdf.pattern} filter An expression that filters the triples in the {@link jQuery.rdf#databank} to locate matches based on the matches on this {@link jQuery.rdf} object. If it's a string, the filter is parsed as a {@link jQuery.rdf.pattern}. 782 * @param {Object} [options] 783 * @param {Object} [options.namespaces] An object representing a set of namespace bindings used to interpret any CURIEs within the pattern. Defaults to the namespace bindings defined on the {@link jQuery.rdf#databank}. 784 * @param {String|jQuery.uri} [options.base] The base URI used to interpret any relative URIs used within the pattern. Defaults to the base URI defined on the {@link jQuery.rdf#databank}. 785 * @param {boolean} [options.optional] Not usually used (use {@link jQuery.rdf#optional} instead). 786 * @returns {jQuery.rdf} A new {@link jQuery.rdf} object whose {@link jQuery.rdf#parent} is this {@link jQuery.rdf}. 787 * @see jQuery.rdf#optional 788 * @see jQuery.rdf#filter 789 * @see jQuery.rdf#about 790 * @example 791 * var rdf = $.rdf() 792 * .prefix('foaf', ns.foaf) 793 * .add('_:a foaf:givenname "Alice" .') 794 * .add('_:a foaf:family_name "Hacker" .') 795 * .add('_:b foaf:givenname "Bob" .') 796 * .add('_:b foaf:family_name "Hacker" .') 797 * .where('?person foaf:family_name "Hacker"') 798 * .where('?person foaf:givenname "Bob"); 799 */ 800 where: function (filter, options) { 801 var query, base, namespaces, optional; 802 options = options || {}; 803 if (typeof filter === 'string') { 804 base = options.base || this.base(); 805 namespaces = $.extend({}, this.prefix(), options.namespaces || {}); 806 optional = options.optional || false; 807 filter = $.rdf.pattern(filter, { namespaces: namespaces, base: base, optional: optional }); 808 } 809 query = $.rdf($.extend({}, options, { parent: this, filter: filter })); 810 this.children.push(query); 811 return query; 812 }, 813 814 /** 815 * Creates a new {@link jQuery.rdf} object whose set of bindings might optionally include those based on the filter pattern. 816 * @param {String|jQuery.rdf.pattern} filter An pattern for a set of bindings that might be added to those in this {@link jQuery.rdf} object. 817 * @param {Object} [options] 818 * @param {Object} [options.namespaces] An object representing a set of namespace bindings used to interpret any CURIEs within the pattern. Defaults to the namespace bindings defined on the {@link jQuery.rdf#databank}. 819 * @param {String|jQuery.uri} [options.base] The base URI used to interpret any relative URIs used within the pattern. Defaults to the base URI defined on the {@link jQuery.rdf#databank}. 820 * @returns {jQuery.rdf} A new {@link jQuery.rdf} object whose {@link jQuery.rdf#parent} is this {@link jQuery.rdf}. 821 * @see jQuery.rdf#where 822 * @see jQuery.rdf#filter 823 * @see jQuery.rdf#about 824 * @example 825 * var rdf = $.rdf() 826 * .prefix('foaf', 'http://xmlns.com/foaf/0.1/') 827 * .prefix('rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#') 828 * .add('_:a rdf:type foaf:Person .') 829 * .add('_:a foaf:name "Alice" .') 830 * .add('_:a foaf:mbox <mailto:alice@example.com> .') 831 * .add('_:a foaf:mbox <mailto:alice@work.example> .') 832 * .add('_:b rdf:type foaf:Person .') 833 * .add('_:b foaf:name "Bob" .') 834 * .where('?x foaf:name ?name') 835 * .optional('?x foaf:mbox ?mbox'); 836 */ 837 optional: function (filter, options) { 838 return this.where(filter, $.extend({}, options || {}, { optional: true })); 839 }, 840 841 /** 842 * Creates a new {@link jQuery.rdf} object whose set of bindings include <code>property</code> and <code>value</code> for every triple that is about the specified resource. 843 * @param {String|jQuery.rdf.resource} resource The subject of the matching triples. 844 * @param {Object} [options] 845 * @param {Object} [options.namespaces] An object representing a set of namespace bindings used to interpret the resource if it's a CURIE. Defaults to the namespace bindings defined on the {@link jQuery.rdf#databank}. 846 * @param {String|jQuery.uri} [options.base] The base URI used to interpret the resource if it's a relative URI (wrapped in <code><</code> and <code>></code>). Defaults to the base URI defined on the {@link jQuery.rdf#databank}. 847 * @returns {jQuery.rdf} A new {@link jQuery.rdf} object whose {@link jQuery.rdf#parent} is this {@link jQuery.rdf}. 848 * @see jQuery.rdf#where 849 * @see jQuery.rdf#optional 850 * @see jQuery.rdf#filter 851 * @example 852 * var rdf = $.rdf() 853 * .prefix('dc', ns.dc) 854 * .prefix('foaf', ns.foaf) 855 * .add('<photo1.jpg> dc:creator <http://www.blogger.com/profile/1109404> .') 856 * .add('<http://www.blogger.com/profile/1109404> foaf:img <photo1.jpg> .') 857 * .add('<photo2.jpg> dc:creator <http://www.blogger.com/profile/1109404> .') 858 * .add('<http://www.blogger.com/profile/1109404> foaf:img <photo2.jpg> .') 859 * .about('<http://www.blogger.com/profile/1109404>'); 860 */ 861 about: function (resource, options) { 862 return this.where(resource + ' ?property ?value', options); 863 }, 864 865 /** 866 * Creates a new {@link jQuery.rdf} object whose set of bindings include only those that satisfy some arbitrary condition. There are two main ways to call this method: with two arguments in which case the first is a binding to be tested and the second represents a condition on the test, or with one argument which is a function that should return true for acceptable bindings. 867 * @param {Function|String} property <p>In the two-argument version, this is the name of a property to be tested against the condition specified in the second argument. In the one-argument version, this is a function in which <code>this</code> is an object whose properties are a set of {@link jQuery.rdf.resource}, {@link jQuery.rdf.literal} or {@link jQuery.rdf.blank} objects and whose arguments are:</p> 868 * <dl> 869 * <dt>i</dt> 870 * <dd>The index of the set of bindings amongst the other matches</dd> 871 * <dt>bindings</dt> 872 * <dd>An object representing the bindings (the same as <code>this</code>)</dd> 873 * <dt>triples</dt> 874 * <dd>The {@link jQuery.rdf.triple}s that underly this set of bindings</dd> 875 * </dl> 876 * @param {RegExp|String} condition In the two-argument version of this function, the condition that the property's must match. If it is a regular expression, the value must match the regular expression. If it is a {@link jQuery.rdf.literal}, the value of the literal must match the property's value. Otherwise, they must be the same resource. 877 * @returns {jQuery.rdf} A new {@link jQuery.rdf} object whose {@link jQuery.rdf#parent} is this {@link jQuery.rdf}. 878 * @see jQuery.rdf#where 879 * @see jQuery.rdf#optional 880 * @see jQuery.rdf#about 881 * @example 882 * var rdf = $.rdf() 883 * .prefix('foaf', 'http://xmlns.com/foaf/0.1/') 884 * .add('_:a foaf:surname "Jones" .') 885 * .add('_:b foaf:surname "Macnamara" .') 886 * .add('_:c foaf:surname "O\'Malley"') 887 * .add('_:d foaf:surname "MacFee"') 888 * .where('?person foaf:surname ?surname') 889 * .filter('surname', /^Ma?c/) 890 * .each(function () { scottish.push(this.surname.value); }) 891 * .end() 892 * .filter('surname', /^O'/) 893 * .each(function () { irish.push(this.surname.value); }) 894 * .end(); 895 * @example 896 * var rdf = $.rdf() 897 * .prefix('foaf', 'http://xmlns.com/foaf/0.1/') 898 * .add('_:a foaf:surname "Jones" .') 899 * .add('_:b foaf:surname "Macnamara" .') 900 * .add('_:c foaf:surname "O\'Malley"') 901 * .add('_:d foaf:surname "MacFee"') 902 * .where('?person foaf:surname ?surname') 903 * .filter(function () { return this.surname !== "Jones"; }) 904 */ 905 filter: function (property, condition) { 906 var func, query; 907 if (typeof property === 'string') { 908 if (condition.constructor === RegExp) { 909 /** @ignore func */ 910 func = function () { 911 return condition.test(this[property].value); 912 }; 913 } else { 914 func = function () { 915 return this[property].type === 'literal' ? this[property].value === condition : this[property] === condition; 916 }; 917 } 918 } else { 919 func = property; 920 } 921 query = $.rdf({ parent: this, filter: func }); 922 this.children.push(query); 923 return query; 924 }, 925 926 /** 927 * Creates a new {@link jQuery.rdf} object containing one binding for each selected resource. 928 * @param {String|Object} node The node to be selected. If this is a string beginning with a question mark the resources are those identified by the bindings of that value in the currently selected bindings. Otherwise, only the named resource is selected as the node. 929 * @returns {jQuery.rdf} A new {@link jQuery.rdf} object. 930 * @see jQuery.rdf#find 931 * @see jQuery.rdf#back 932 * @example 933 * // returns an rdfQuery object with a pointer to <http://example.com/aReallyGreatBook> 934 * var rdf = $('html').rdf() 935 * .node('<http://example.com/aReallyGreatBook>'); 936 */ 937 node: function (resource) { 938 var variable, query; 939 if (resource.toString().substring(0, 1) === '?') { 940 variable = resource.toString().substring(1); 941 query = $.rdf({ parent: this, navigate: variable }); 942 } else { 943 if (typeof resource === 'string') { 944 resource = object(resource, { namespaces: this.prefix(), base: this.base() }); 945 } 946 query = $.rdf({ parent: this, nodes: [resource] }); 947 } 948 this.children.push(query); 949 return query; 950 }, 951 952 /** 953 * Navigates from the resource identified by the 'node' binding to another node through the property passed as the argument. 954 * @param {String|Object} property The property whose value will be the new node. 955 * @returns {jQuery.rdf} A new {@link jQuery.rdf} object whose {@link jQuery.rdf#parent} is this {@link jQuery.rdf}. 956 * @see jQuery.rdf#back 957 * @see jQuery.rdf#node 958 * @example 959 * var creators = $('html').rdf() 960 * .node('<>') 961 * .find('dc:creator'); 962 */ 963 find: function (property) { 964 return this.where('?node ' + property + ' ?object', { navigate: 'object' }); 965 }, 966 967 /** 968 * Navigates from the resource identified by the 'node' binding to another node through the property passed as the argument, like {jQuery.rdf#find}, but backwards. 969 * @param {String|Object} property The property whose value will be the new node. 970 * @returns {jQuery.rdf} A new {@link jQuery.rdf} object whose {@link jQuery.rdf#parent} is this {@link jQuery.rdf}. 971 * @see jQuery.rdf#find 972 * @see jQuery.rdf#node 973 * @example 974 * var people = $('html').rdf() 975 * .node('foaf:Person') 976 * .back('rdf:type'); 977 */ 978 back: function (property) { 979 return this.where('?subject ' + property + ' ?node', { navigate: 'subject' }); 980 }, 981 982 /** 983 * Groups the bindings held by this {@link jQuery.rdf} object based on the values of the variables passed as the parameter. 984 * @param {String[]} [bindings] The variables to group by. The returned objects will contain all their current properties, but those aside from the specified variables will be arrays listing the relevant values. 985 * @returns {jQuery} A jQuery object containing objects representing the grouped bindings. 986 * @example 987 * // returns one object per person and groups all the names and all the emails together in arrays 988 * var grouped = rdf 989 * .where('?person foaf:name ?name') 990 * .where('?person foaf:email ?email') 991 * .group('person'); 992 * @example 993 * // returns one object per surname/firstname pair, with the person property being an array in the resulting objects 994 * var grouped = rdf 995 * .where('?person foaf:first_name ?forename') 996 * .where('?person foaf:givenname ?surname') 997 * .group(['surname', 'forename']); 998 */ 999 group: function (bindings) { 1000 var grouped = {}, results = [], i, key, v; 1001 if (!$.isArray(bindings)) { 1002 bindings = [bindings]; 1003 } 1004 return $(group(this, bindings)); 1005 }, 1006 1007 /** 1008 * Filters the variable bindings held by this {@link jQuery.rdf} object down to those listed in the bindings parameter. This mirrors the <a href="http://www.w3.org/TR/rdf-sparql-query/#select">SELECT</a> form in SPARQL. 1009 * @param {String[]} [bindings] The variables that you're interested in. The returned objects will only contain those variables. If bindings is undefined, you will get all the variable bindings in the returned objects. 1010 * @returns {Object[]} An array of objects with the properties named by the bindings parameter. 1011 * @example 1012 * var filtered = rdf 1013 * .where('?photo dc:creator ?creator') 1014 * .where('?creator foaf:img ?photo'); 1015 * var selected = rdf.select(['creator']); 1016 */ 1017 select: function (bindings) { 1018 var s = [], i, j; 1019 for (i = 0; i < this.length; i += 1) { 1020 if (bindings === undefined) { 1021 s[i] = this[i]; 1022 } else { 1023 s[i] = {}; 1024 for (j = 0; j < bindings.length; j += 1) { 1025 s[i][bindings[j]] = this[i][bindings[j]]; 1026 } 1027 } 1028 } 1029 return s; 1030 }, 1031 1032 /** 1033 * Provides <a href="http://n2.talis.com/wiki/Bounded_Descriptions_in_RDF#Simple_Concise_Bounded_Description">simple concise bounded descriptions</a> of the resources or bindings that are passed in the argument. This mirrors the <a href="http://www.w3.org/TR/rdf-sparql-query/#describe">DESCRIBE</a> form in SPARQL. 1034 * @param {(String|jQuery.rdf.resource)[]} bindings An array that can contain strings, {@link jQuery.rdf.resource}s or a mixture of the two. Any strings that begin with a question mark (<code>?</code>) are taken as variable names; each matching resource is described by the function. 1035 * @returns {jQuery} A {@link jQuery} object that contains {@link jQuery.rdf.triple}s that describe the listed resources. 1036 * @see jQuery.rdf.databank#describe 1037 * @example 1038 * $.rdf.dump($('html').rdf().describe(['<photo1.jpg>'])); 1039 * @example 1040 * $('html').rdf() 1041 * .where('?person foaf:img ?picture') 1042 * .describe(['?photo']) 1043 */ 1044 describe: function (bindings) { 1045 var i, j, binding, resources = []; 1046 for (i = 0; i < bindings.length; i += 1) { 1047 binding = bindings[i]; 1048 if (binding.substring(0, 1) === '?') { 1049 binding = binding.substring(1); 1050 for (j = 0; j < this.length; j += 1) { 1051 resources.push(this[j][binding]); 1052 } 1053 } else { 1054 resources.push(binding); 1055 } 1056 } 1057 return this.databank.describe(resources); 1058 }, 1059 1060 /** 1061 * Returns a new {@link jQuery.rdf} object that contains only one set of variable bindings. This is designed to mirror the <a href="http://docs.jquery.com/Traversing/eq#index">jQuery#eq</a> method. 1062 * @param {Integer} n The index number of the match that should be selected. 1063 * @returns {jQuery.rdf} A new {@link jQuery.rdf} object with just that match. 1064 * @example 1065 * var rdf = $.rdf() 1066 * .prefix('foaf', 'http://xmlns.com/foaf/0.1/') 1067 * .add('_:a foaf:name "Alice" .') 1068 * .add('_:a foaf:homepage <http://work.example.org/alice/> .') 1069 * .add('_:b foaf:name "Bob" .') 1070 * .add('_:b foaf:mbox <mailto:bob@work.example> .') 1071 * .where('?x foaf:name ?name') 1072 * .eq(1); 1073 */ 1074 eq: function (n) { 1075 return this.filter(function (i) { 1076 return i === n; 1077 }); 1078 }, 1079 1080 /** 1081 * Returns a {@link jQuery.rdf} object that includes no filtering (and therefore has no matches) over the {@link jQuery.rdf#databank}. 1082 * @returns {jQuery.rdf} An empty {@link jQuery.rdf} object. 1083 * @example 1084 * $('html').rdf() 1085 * .where('?person foaf:family_name "Hacker"') 1086 * .where('?person foaf:givenname "Alice"') 1087 * .each(...do something with Alice Hacker...) 1088 * .reset() 1089 * .where('?person foaf:family_name "Jones"') 1090 * .where('?person foaf:givenname "Bob"') 1091 * .each(...do something with Bob Jones...); 1092 */ 1093 reset: function () { 1094 var query = this; 1095 while (query.parent !== undefined) { 1096 query = query.parent; 1097 } 1098 return query; 1099 }, 1100 1101 /** 1102 * Returns the parent {@link jQuery.rdf} object, which is equivalent to undoing the most recent filtering operation (such as {@link jQuery.rdf#where} or {@link jQuery.rdf#filter}). This is designed to mirror the <a href="http://docs.jquery.com/Traversing/end">jQuery#end</a> method. 1103 * @returns {jQuery.rdf} 1104 * @example 1105 * $('html').rdf() 1106 * .where('?person foaf:family_name "Hacker"') 1107 * .where('?person foaf:givenname "Alice"') 1108 * .each(...do something with Alice Hacker...) 1109 * .end() 1110 * .where('?person foaf:givenname "Bob"') 1111 * .each(...do something with Bob Hacker...); 1112 */ 1113 end: function () { 1114 return this.parent; 1115 }, 1116 1117 /** 1118 * Returns the number of matches in this {@link jQuery.rdf} object (equivalent to {@link jQuery.rdf#length}). 1119 * @returns {Integer} The number of matches in this {@link jQuery.rdf} object. 1120 * @see jQuery.rdf#length 1121 */ 1122 size: function () { 1123 return this.length; 1124 }, 1125 1126 /** 1127 * Gets the triples that form the basis of the variable bindings that are the primary product of {@link jQuery.rdf}. Getting hold of the triples can be useful for understanding the facts that form the basis of the variable bindings. 1128 * @returns {jQuery} A {@link jQuery} object containing arrays of {@link jQuery.rdf.triple} objects. A {@link jQuery} object is returned so that you can easily iterate over the contents. 1129 * @example 1130 * $('html').rdf() 1131 * .where('?thing a foaf:Person') 1132 * .sources() 1133 * .each(function () { 1134 * ...do something with the array of triples... 1135 * }); 1136 */ 1137 sources: function () { 1138 return $($.map(this.matches, function (match) { 1139 // return an array-of-an-array because arrays automatically get expanded by $.map() 1140 return [match.triples]; 1141 })); 1142 }, 1143 1144 /** 1145 * Dumps the triples that form the basis of the variable bindings that are the primary product of {@link jQuery.rdf} into a format that can be shown to the user or sent to a server. 1146 * @param {Object} [options] Options that control the formatting of the triples. See {@link jQuery.rdf.dump} for details. 1147 * @see jQuery.rdf.dump 1148 */ 1149 dump: function (options) { 1150 var triples = $.map(this.matches, function (match) { 1151 return match.triples; 1152 }); 1153 options = $.extend({ namespaces: this.databank.namespaces, base: this.databank.base }, options || {}); 1154 return $.rdf.dump(triples, options); 1155 }, 1156 1157 /** 1158 * Either returns the item specified by the argument or turns the {@link jQuery.rdf} object into an array. This mirrors the <a href="http://docs.jquery.com/Core/get">jQuery#get</a> method. 1159 * @param {Integer} [num] The number of the item to be returned. 1160 * @returns {Object[]|Object} Returns either a single Object representing variable bindings or an array of such. 1161 * @example 1162 * $('html').rdf() 1163 * .where('?person a foaf:Person') 1164 * .get(0) 1165 * .subject 1166 * .value; 1167 */ 1168 get: function (num) { 1169 return (num === undefined) ? $.makeArray(this) : this[num]; 1170 }, 1171 1172 /** 1173 * Iterates over the matches held by the {@link jQuery.rdf} object and performs a function on each of them. This mirrors the <a href="http://docs.jquery.com/Core/each">jQuery#each</a> method. 1174 * @param {Function} callback A function that is called for each match on the {@link jQuery.rdf} object. Within the function, <code>this</code> is set to the object representing the variable bindings. The function can take up to three parameters: 1175 * <dl> 1176 * <dt>i</dt><dd>The index of the match amongst the other matches.</dd> 1177 * <dt>bindings</dt><dd>An object representing the variable bindings for the match, the same as <code>this</code>.</dd> 1178 * <dt>triples</dt><dd>An array of {@link jQuery.rdf.triple}s associated with the particular match.</dd> 1179 * </dl> 1180 * @returns {jQuery.rdf} The {@link jQuery.rdf} object. 1181 * @see jQuery.rdf#map 1182 * @example 1183 * var rdf = $('html').rdf() 1184 * .where('?photo dc:creator ?creator') 1185 * .where('?creator foaf:img ?photo') 1186 * .each(function () { 1187 * photos.push(this.photo.value); 1188 * }); 1189 */ 1190 each: function (callback) { 1191 $.each(this.matches, function (i, match) { 1192 callback.call(match.bindings, i, match.bindings, match.triples); 1193 }); 1194 return this; 1195 }, 1196 1197 /** 1198 * Iterates over the matches held by the {@link jQuery.rdf} object and creates a new {@link jQuery} object that holds the result of applying the passed function to each one. This mirrors the <a href="http://docs.jquery.com/Traversing/map">jQuery#map</a> method. 1199 * @param {Function} callback A function that is called for each match on the {@link jQuery.rdf} object. Within the function, <code>this</code> is set to the object representing the variable bindings. The function can take up to three parameters and should return some kind of value: 1200 * <dl> 1201 * <dt>i</dt><dd>The index of the match amongst the other matches.</dd> 1202 * <dt>bindings</dt><dd>An object representing the variable bindings for the match, the same as <code>this</code>.</dd> 1203 * <dt>triples</dt><dd>An array of {@link jQuery.rdf.triple}s associated with the particular match.</dd> 1204 * </dl> 1205 * @returns {jQuery} A jQuery object holding the results of the function for each of the matches on the original {@link jQuery.rdf} object. 1206 * @example 1207 * var photos = $('html').rdf() 1208 * .where('?photo dc:creator ?creator') 1209 * .where('?creator foaf:img ?photo') 1210 * .map(function () { 1211 * return this.photo.value; 1212 * }); 1213 */ 1214 map: function (callback) { 1215 return $($.map(this.matches, function (match, i) { 1216 // in the callback, "this" is the bindings, and the arguments are swapped from $.map() 1217 return callback.call(match.bindings, i, match.bindings, match.triples); 1218 })); 1219 }, 1220 1221 /** 1222 * Returns a {@link jQuery} object that wraps this {@link jQuery.rdf} object. 1223 * @returns {jQuery} 1224 */ 1225 jquery: function () { 1226 return $(this); 1227 } 1228 }; 1229 1230 $.rdf.fn.init.prototype = $.rdf.fn; 1231 1232 $.rdf.gleaners = []; 1233 $.rdf.parsers = {}; 1234 1235 /** 1236 * Dumps the triples passed as the first argument into a format that can be shown to the user or sent to a server. 1237 * @param {jQuery.rdf.triple[]} triples An array (or {@link jQuery} object) of {@link jQuery.rdf.triple}s. 1238 * @param {Object} [options] Options that control the format of the dump. 1239 * @param {String} [options.format='application/json'] The mime type of the format of the dump. The supported formats are: 1240 * <table> 1241 * <tr><th>mime type</th><th>description</th></tr> 1242 * <tr> 1243 * <td><code>application/json</code></td> 1244 * <td>An <a href="http://n2.talis.com/wiki/RDF_JSON_Specification">RDF/JSON</a> object</td> 1245 * </tr> 1246 * <tr> 1247 * <td><code>application/rdf+xml</code></td> 1248 * <td>An DOMDocument node holding XML in <a href="http://www.w3.org/TR/rdf-syntax-grammar/">RDF/XML syntax</a></td> 1249 * </tr> 1250 * <tr> 1251 * <td><code>text/turtle</code></td> 1252 * <td>A String holding a representation of the RDF in <a href="http://www.w3.org/TeamSubmission/turtle/">Turtle syntax</a></td> 1253 * </tr> 1254 * </table> 1255 * @param {Object} [options.namespaces={}] A set of namespace bindings used when mapping resource URIs to CURIEs or QNames (particularly in a RDF/XML serialisation). 1256 * @param {boolean} [options.serialize=false] If true, rather than creating an Object, the function will return a string which is ready to display or send to a server. 1257 * @param {boolean} [options.indent=false] If true, the serialised (RDF/XML) output has indentation added to it to make it more readable. 1258 * @returns {Object|String} The alternative representation of the triples. 1259 */ 1260 $.rdf.dump = function (triples, options) { 1261 var opts = $.extend({}, $.rdf.dump.defaults, options || {}), 1262 format = opts.format, 1263 serialize = opts.serialize, 1264 dump, parser, parsers; 1265 parser = $.rdf.parsers[format]; 1266 if (parser === undefined) { 1267 parsers = []; 1268 for (p in $.rdf.parsers) { 1269 parsers.push(p); 1270 } 1271 throw "Unrecognised dump format: " + format + ". Expected one of " + parsers.join(", "); 1272 } 1273 dump = parser.dump(triples, opts); 1274 return serialize ? parser.serialize(dump) : dump; 1275 }; 1276 1277 $.rdf.dump.defaults = { 1278 format: 'application/json', 1279 serialize: false, 1280 indent: false, 1281 namespaces: {} 1282 } 1283 1284 /** 1285 * Gleans RDF triples from the nodes held by the {@link jQuery} object, puts them into a {@link jQuery.rdf.databank} and returns a {@link jQuery.rdf} object that allows you to query and otherwise manipulate them. The mechanism for gleaning RDF triples from the web page depends on the rdfQuery modules that have been included. The core version of rdfQuery doesn't support any gleaners; other versions support a RDFa gleaner, and there are some modules available for common microformats. 1286 * @methodOf jQuery# 1287 * @name jQuery#rdf 1288 * @param {Function} [callback] A callback function that is called every time a triple is gleaned from the page. Within the function, <code>this</code> is set to the triple that has been located. The function can take up to two parameters: 1289 * <dl> 1290 * <dt>node</dt><dd>The node on which the triple has been found; should be the same as <code>this.source</code>.</dd> 1291 * <dt>triple</dt><dd>The triple that's been found; the same as <code>this</code>.</dd> 1292 * </dl> 1293 * The callback should return the triple or triples that should be added to the databank. This enables you to filter, extend or modify the contents of the databank itself, should you wish to. 1294 * @returns {jQuery.rdf} An empty query over the triples stored within the page. 1295 * @example $('#content').rdf().databank.dump(); 1296 */ 1297 $.fn.rdf = function (callback) { 1298 var triples = [], 1299 callback = callback || function () { return this; }; 1300 if ($(this)[0] && $(this)[0].nodeType === 9) { 1301 return $(this).children('*').rdf(callback); 1302 } else if ($(this).length > 0) { 1303 triples = $(this).map(function (i, elem) { 1304 return $.map($.rdf.gleaners, function (gleaner) { 1305 return gleaner.call($(elem), { callback: callback }); 1306 }); 1307 }); 1308 return $.rdf({ triples: triples, namespaces: $(this).xmlns() }); 1309 } else { 1310 return $.rdf(); 1311 } 1312 }; 1313 1314 $.extend($.expr[':'], { 1315 1316 about: function (a, i, m) { 1317 var j = $(a), 1318 resource = m[3] ? j.safeCurie(m[3]) : null, 1319 isAbout = false; 1320 $.each($.rdf.gleaners, function (i, gleaner) { 1321 isAbout = gleaner.call(j, { about: resource }); 1322 if (isAbout) { 1323 return null; 1324 } 1325 }); 1326 return isAbout; 1327 }, 1328 1329 type: function (a, i, m) { 1330 var j = $(a), 1331 type = m[3] ? j.curie(m[3]) : null, 1332 isType = false; 1333 $.each($.rdf.gleaners, function (i, gleaner) { 1334 if (gleaner.call(j, { type: type })) { 1335 isType = true; 1336 return null; 1337 } 1338 }); 1339 return isType; 1340 } 1341 1342 }); 1343 1344 /** 1345 * <p>Creates a new jQuery.rdf.databank object. This should be invoked as a method rather than constructed using new; indeed you will not usually want to generate these objects directly, but manipulate them through a {@link jQuery.rdf} object.</p> 1346 * @class Represents a triplestore, holding a bunch of {@link jQuery.rdf.triple}s. 1347 * @param {(String|jQuery.rdf.triple)[]} [triples=[]] An array of triples to store in the databank. 1348 * @param {Object} [options] Initialisation of the databank. 1349 * @param {Object} [options.namespaces] An object representing a set of namespace bindings used when interpreting the CURIEs in strings representing triples. Rather than passing this in when you construct the {@link jQuery.rdf.databank} instance, you will usually want to use the {@link jQuery.rdf.databank#prefix} method. 1350 * @param {String|jQuery.uri} [options.base] The base URI used to interpret any relative URIs used within the strings representing triples. 1351 * @returns {jQuery.rdf.databank} The newly-created databank. 1352 * @see jQuery.rdf 1353 */ 1354 $.rdf.databank = function (triples, options) { 1355 return new $.rdf.databank.fn.init(triples, options); 1356 }; 1357 1358 $.rdf.databank.fn = $.rdf.databank.prototype = { 1359 init: function (triples, options) { 1360 var i; 1361 triples = triples || []; 1362 options = options || {}; 1363 this.id = databankID(); 1364 databanks[this.id] = this; 1365 if (options.union === undefined) { 1366 this.queries = {}; 1367 this.tripleStore = []; 1368 this.subjectIndex = {}; 1369 this.propertyIndex = {}; 1370 this.objectIndex = {}; 1371 this.baseURI = options.base || $.uri.base(); 1372 this.namespaces = $.extend({}, options.namespaces || {}); 1373 for (i = 0; i < triples.length; i += 1) { 1374 this.add(triples[i]); 1375 } 1376 } else { 1377 this.union = options.union; 1378 } 1379 return this; 1380 }, 1381 1382 /** 1383 * Sets or returns the base URI of the {@link jQuery.rdf.databank}. 1384 * @param {String|jQuery.uri} [base] 1385 * @returns A {@link jQuery.uri} if no base URI is specified, otherwise returns this {@link jQuery.rdf.databank} object. 1386 * @see jQuery.rdf#base 1387 */ 1388 base: function (base) { 1389 if (this.union === undefined) { 1390 if (base === undefined) { 1391 return this.baseURI; 1392 } else { 1393 this.baseURI = base; 1394 return this; 1395 } 1396 } else if (base === undefined) { 1397 return this.union[0].base(); 1398 } else { 1399 $.each(this.union, function (i, databank) { 1400 databank.base(base); 1401 }); 1402 return this; 1403 } 1404 }, 1405 1406 /** 1407 * Sets or returns a namespace binding on the {@link jQuery.rdf.databank}. 1408 * @param {String} [prefix] 1409 * @param {String} [namespace] 1410 * @returns {Object|jQuery.uri|jQuery.rdf} If no prefix or namespace is specified, returns an object providing all namespace bindings on the {@link jQuery.rdf#databank}. If a prefix is specified without a namespace, returns the {@link jQuery.uri} associated with that prefix. Otherwise returns this {@link jQuery.rdf} object after setting the namespace binding. 1411 * @see jQuery.rdf#prefix 1412 */ 1413 prefix: function (prefix, uri) { 1414 var namespaces = {}; 1415 if (this.union === undefined) { 1416 if (prefix === undefined) { 1417 return this.namespaces; 1418 } else if (uri === undefined) { 1419 return this.namespaces[prefix]; 1420 } else { 1421 this.namespaces[prefix] = uri; 1422 return this; 1423 } 1424 } else if (uri === undefined) { 1425 $.each(this.union, function (i, databank) { 1426 $.extend(namespaces, databank.prefix()); 1427 }); 1428 if (prefix === undefined) { 1429 return namespaces; 1430 } else { 1431 return namespaces[prefix]; 1432 } 1433 } else { 1434 $.each(this.union, function (i, databank) { 1435 databank.prefix(prefix, uri); 1436 }); 1437 return this; 1438 } 1439 }, 1440 1441 /** 1442 * Adds a triple to the {@link jQuery.rdf.databank} or another {@link jQuery.rdf.databank} object to create a union. 1443 * @param {String|jQuery.rdf.triple|jQuery.rdf.databank} triple The triple or {@link jQuery.rdf.databank} object to be added to this one. If the triple is a {@link jQuery.rdf.databank} object, the two databanks are unioned together. If the triple is a string, it's parsed as a {@link jQuery.rdf.triple}. 1444 * @param {Object} [options] 1445 * @param {Object} [options.namespaces] An object representing a set of namespace bindings used to interpret CURIEs within the triple. Defaults to the namespace bindings defined on the {@link jQuery.rdf.databank}. 1446 * @param {String|jQuery.uri} [options.base] The base URI used to interpret any relative URIs used within the triple. Defaults to the base URI defined on the {@link jQuery.rdf.databank}. 1447 * @param {Integer} [options.depth] The number of links to traverse to gather more information about the subject, property and object of the triple. 1448 * @returns {jQuery.rdf.databank} This {@link jQuery.rdf.databank} object. 1449 * @see jQuery.rdf#add 1450 */ 1451 add: function (triple, options) { 1452 var base = (options && options.base) || this.base(), 1453 namespaces = $.extend({}, this.prefix(), (options && options.namespaces) || {}), 1454 depth = (options && options.depth) || $.rdf.databank.defaults.depth, 1455 proxy = (options && options.proxy) || $.rdf.databank.defaults.proxy, 1456 databank; 1457 if (triple === this) { 1458 return this; 1459 } else if (triple.subjectIndex !== undefined) { 1460 // merging two databanks 1461 if (this.union === undefined) { 1462 databank = $.rdf.databank(undefined, { union: [this, triple] }); 1463 return databank; 1464 } else { 1465 this.union.push(triple); 1466 return this; 1467 } 1468 } else { 1469 if (typeof triple === 'string') { 1470 triple = $.rdf.triple(triple, { namespaces: namespaces, base: base, source: triple }); 1471 } 1472 if (this.union === undefined) { 1473 if (this.subjectIndex[triple.subject] === undefined) { 1474 this.subjectIndex[triple.subject] = []; 1475 if (depth > 0 && triple.subject.type === 'uri') { 1476 this.load(triple.subject.value, { depth: depth - 1, proxy: proxy }); 1477 } 1478 } 1479 if (this.propertyIndex[triple.property] === undefined) { 1480 this.propertyIndex[triple.property] = []; 1481 if (depth > 0) { 1482 this.load(triple.property.value, { depth: depth - 1, proxy: proxy }); 1483 } 1484 } 1485 if ($.inArray(triple, this.subjectIndex[triple.subject]) === -1) { 1486 this.tripleStore.push(triple); 1487 this.subjectIndex[triple.subject].push(triple); 1488 this.propertyIndex[triple.property].push(triple); 1489 if (triple.object.type === 'uri' || triple.object.type === 'bnode') { 1490 if (this.objectIndex[triple.object] === undefined) { 1491 this.objectIndex[triple.object] = []; 1492 if (depth > 0 && triple.object.type === 'uri') { 1493 this.load(triple.object.value, { depth: depth - 1, proxy: proxy }); 1494 } 1495 } 1496 this.objectIndex[triple.object].push(triple); 1497 } 1498 addToDatabankQueries(this, triple); 1499 } 1500 } else { 1501 $.each(this.union, function (i, databank) { 1502 databank.add(triple); 1503 }); 1504 } 1505 return this; 1506 } 1507 }, 1508 1509 /** 1510 * Removes a triple from the {@link jQuery.rdf.databank}. 1511 * @param {String|jQuery.rdf.triple} triple The triple to be removed. 1512 * @param {Object} [options] 1513 * @param {Object} [options.namespaces] An object representing a set of namespace bindings used to interpret any CURIEs within the triple. Defaults to the namespace bindings defined on the {@link jQuery.rdf.databank}. 1514 * @param {String|jQuery.uri} [options.base] The base URI used to interpret any relative URIs used within the triple. Defaults to the base URI defined on the {@link jQuery.rdf.databank}. 1515 * @returns {jQuery.rdf.databank} The {@link jQuery.rdf.databank} object itself. 1516 * @see jQuery.rdf#remove 1517 */ 1518 remove: function (triple, options) { 1519 var base = (options && options.base) || this.base(), 1520 namespaces = $.extend({}, this.prefix(), (options && options.namespaces) || {}), 1521 striples, ptriples, otriples, 1522 databank; 1523 if (typeof triple === 'string') { 1524 triple = $.rdf.triple(triple, { namespaces: namespaces, base: base, source: triple }); 1525 } 1526 this.tripleStore.splice($.inArray(triple, this.tripleStore), 1); 1527 striples = this.subjectIndex[triple.subject]; 1528 if (striples !== undefined) { 1529 striples.splice($.inArray(triple, striples), 1); 1530 } 1531 ptriples = this.propertyIndex[triple.property]; 1532 if (ptriples !== undefined) { 1533 ptriples.splice($.inArray(triple, ptriples), 1); 1534 } 1535 if (triple.object.type === 'uri' || triple.object.type === 'bnode') { 1536 otriples = this.objectIndex[triple.object]; 1537 if (otriples !== undefined) { 1538 otriples.splice($.inArray(triple, otriples), 1); 1539 } 1540 } 1541 removeFromDatabankQueries(this, triple); 1542 return this; 1543 }, 1544 1545 /** 1546 * Creates a new databank containing all the triples in this {@link jQuery.rdf.databank} except those in the {@link jQuery.rdf.databank} passed as the argument. 1547 * @param {jQuery.rdf.databank} data The other {@link jQuery.rdf.databank} 1548 * @returns {jQuery.rdf.databank} A new {@link jQuery.rdf.databank} containing the triples in this {@link jQuery.rdf.databank} except for those in the data parameter. 1549 * @example 1550 * var old = $('html').rdf().databank; 1551 * ...some processing occurs... 1552 * var new = $('html').rdf().databank; 1553 * var added = new.except(old); 1554 * var removed = old.except(new); 1555 */ 1556 except: function (data) { 1557 var store = data.subjectIndex, 1558 diff = []; 1559 $.each(this.subjectIndex, function (s, ts) { 1560 var ots = store[s]; 1561 if (ots === undefined) { 1562 diff = diff.concat(ts); 1563 } else { 1564 $.each(ts, function (i, t) { 1565 if ($.inArray(t, ots) === -1) { 1566 diff.push(t); 1567 } 1568 }); 1569 } 1570 }); 1571 return $.rdf.databank(diff); 1572 }, 1573 1574 /** 1575 * Provides a {@link jQuery} object containing the triples held in this {@link jQuery.rdf.databank}. 1576 * @returns {jQuery} A {@link jQuery} object containing {@link jQuery.rdf.triple} objects. 1577 */ 1578 triples: function () { 1579 var s, triples = []; 1580 if (this.union === undefined) { 1581 triples = this.tripleStore; 1582 } else { 1583 $.each(this.union, function (i, databank) { 1584 triples = triples.concat(databank.triples().get()); 1585 }); 1586 triples = unique(triples); 1587 } 1588 return $(triples); 1589 }, 1590 1591 /** 1592 * Tells you how many triples the databank contains. 1593 * @returns {Integer} The number of triples in the {@link jQuery.rdf.databank}. 1594 * @example $('html').rdf().databank.size(); 1595 */ 1596 size: function () { 1597 return this.triples().length; 1598 }, 1599 1600 /** 1601 * Provides <a href="http://n2.talis.com/wiki/Bounded_Descriptions_in_RDF#Simple_Concise_Bounded_Description">simple concise bounded descriptions</a> of the resources that are passed in the argument. This mirrors the <a href="http://www.w3.org/TR/rdf-sparql-query/#describe">DESCRIBE</a> form in SPARQL. 1602 * @param {(String|jQuery.rdf.resource)[]} resources An array that can contain strings, {@link jQuery.rdf.resource}s or a mixture of the two. 1603 * @returns {jQuery} A {@link jQuery} object holding the {@link jQuery.rdf.triple}s that describe the listed resources. 1604 * @see jQuery.rdf#describe 1605 */ 1606 describe: function (resources) { 1607 var i, r, t, rhash = {}, triples = []; 1608 while (resources.length > 0) { 1609 r = resources.pop(); 1610 if (rhash[r] === undefined) { 1611 if (r.value === undefined) { 1612 r = $.rdf.resource(r); 1613 } 1614 if (this.subjectIndex[r] !== undefined) { 1615 for (i = 0; i < this.subjectIndex[r].length; i += 1) { 1616 t = this.subjectIndex[r][i]; 1617 triples.push(t); 1618 if (t.object.type === 'bnode') { 1619 resources.push(t.object); 1620 } 1621 } 1622 } 1623 if (this.objectIndex[r] !== undefined) { 1624 for (i = 0; i < this.objectIndex[r].length; i += 1) { 1625 t = this.objectIndex[r][i]; 1626 triples.push(t); 1627 if (t.subject.type === 'bnode') { 1628 resources.push(t.subject); 1629 } 1630 } 1631 } 1632 rhash[r] = true; 1633 } 1634 } 1635 return unique(triples); 1636 }, 1637 1638 /** 1639 * Dumps the triples in the databank into a format that can be shown to the user or sent to a server. 1640 * @param {Object} [options] Options that control the formatting of the triples. See {@link jQuery.rdf.dump} for details. 1641 * @returns {Object|Node|String} 1642 * @see jQuery.rdf.dump 1643 */ 1644 dump: function (options) { 1645 options = $.extend({ namespaces: this.namespaces, base: this.base }, options || {}); 1646 return $.rdf.dump(this.triples(), options); 1647 }, 1648 1649 /** 1650 * Loads some data into the databank. 1651 * @param {Node|Object|String} data If the data is a string and starts with 'http://' then it's taken to be a URI and data is loaded from that URI via the proxy specified in the options. If it doesn't start with 'http://' then it's taken to be a serialized version of some format capable of representing RDF, parsed and interpreted. If the data is a node, it's interpreted to be an <a href="http://www.w3.org/TR/rdf-syntax-grammar/">RDF/XML syntax</a> document and will be parsed as such. Otherwise, it's taken to be a <a href="http://n2.talis.com/wiki/RDF_JSON_Specification">RDF/JSON</a> object. 1652 * @param {Object} opts Options governing the loading of the data. 1653 * @param {String} [opts.format] The mime type of the format the data is in, particularly useful if you're supplying the data as a string. If unspecified, the data will be sniffed to see if it might be HTML, RDF/XML, RDF/JSON or Turtle. 1654 * @param {boolean} [opts.async=true] When loading data from a URI, this determines whether it will be done synchronously or asynchronously. 1655 * @param {Function} [opts.success] When loading data from a URI, a function that will be called after the data is successfully loaded. 1656 * @param {Function} [opts.error] When loading data from a URI, a function that will be called if there's an error when accessing the URI. 1657 * @param {String} [opts.proxy='http://www.jenitennison.com/rdfquery/proxy.php'] The URI for a server-side proxy through which the data can be accessed. This does not have to be hosted on the same server as this Javascript, the HTML page or the remote data. The proxy must accept id, url and depth parameters and respond with some Javascript that will invoke the {@link jQuery.rdf.databank.load} function. <a href="http://code.google.com/p/rdfquery/source/browse/#svn/trunk/proxies">Example proxies</a> that do the right thing are available. If you are intending to use this facility a lot, please do not use the default proxy. 1658 * @param {integer} [opts.depth=0] Triggers recursive loading of located resources, to the depth specified. This is useful for automatically populating a databank with linked data. 1659 * @returns {jQuery.rdf.databank} The {@link jQuery.rdf.databank} itself. 1660 * @see jQuery.rdf#load 1661 */ 1662 load: function (data, opts) { 1663 var i, triples, url, script, parser, docElem, 1664 format = (opts && opts.format), 1665 async = (opts && opts.async) || $.rdf.databank.defaults.async, 1666 success = (opts && opts.success) || $.rdf.databank.defaults.success, 1667 error = (opts && opts.error) || $.rdf.databank.defaults.error, 1668 proxy = (opts && opts.proxy) || $.rdf.databank.defaults.proxy, 1669 depth = (opts && opts.depth) || $.rdf.databank.defaults.depth; 1670 url = (typeof data === 'string' && data.substring(1, 7) === 'http://') ? $.uri(data) : data; 1671 if (url.scheme) { 1672 if (!queue(this, url, { success: success, error: error })) { 1673 script = '<script type="text/javascript" src="' + proxy + '?id=' + this.id + '&depth=' + depth + '&url=' + encodeURIComponent(url.resolve('').toString()) + '"></script>'; 1674 if (async) { 1675 setTimeout("$('head').append('" + script + "')", 0); 1676 } else { 1677 $('head').append(script); 1678 } 1679 } 1680 return this; 1681 } else { 1682 if (format === undefined) { 1683 if (typeof data === 'string') { 1684 if (data.substring(0, 1) === '{') { 1685 format = 'application/json'; 1686 } else if (data.substring(0, 14) === '<!DOCTYPE html' || data.substring(0, 5) === '<html') { 1687 format = 'application/xhtml+xml'; 1688 } else if (data.substring(0, 5) === '<?xml' || data.substring(0, 8) === '<rdf:RDF') { 1689 format = 'application/rdf+xml'; 1690 } else { 1691 format = 'text/turtle'; 1692 } 1693 } else if (data.documentElement || data.ownerDocument) { 1694 docElem = data.documentElement ? data.documentElement : data.ownerDocument.documentElement; 1695 if (docElem.nodeName === 'html') { 1696 format = 'application/xhtml+xml'; 1697 } else { 1698 format = 'application/rdf+xml'; 1699 } 1700 } else { 1701 format = 'application/json'; 1702 } 1703 } 1704 parser = $.rdf.parsers[format]; 1705 if (typeof data === 'string') { 1706 data = parser.parse(data); 1707 } 1708 triples = parser.triples(data); 1709 for (i = 0; i < triples.length; i += 1) { 1710 this.add(triples[i], opts); 1711 } 1712 return this; 1713 } 1714 }, 1715 1716 /** 1717 * Provides a string representation of the databank which simply specifies how many triples it contains. 1718 * @returns {String} 1719 */ 1720 toString: function () { 1721 return '[Databank with ' + this.size() + ' triples]'; 1722 } 1723 }; 1724 1725 $.rdf.databank.fn.init.prototype = $.rdf.databank.fn; 1726 1727 $.rdf.databank.defaults = { 1728 parse: false, 1729 async: true, 1730 success: null, 1731 error: null, 1732 depth: 0, 1733 proxy: 'http://www.jenitennison.com/rdfquery/proxy.php' 1734 }; 1735 1736 $.rdf.databank.load = function (id, url, doc, opts) { 1737 if (doc !== undefined) { 1738 databanks[id].load(doc, opts); 1739 } 1740 dequeue(databanks[id], url, (doc === undefined) ? 'error' : 'success', opts); 1741 }; 1742 1743 /** 1744 * <p>Creates a new jQuery.rdf.pattern object. This should be invoked as a method rather than constructed using new; indeed you will not usually want to generate these objects directly, since they are automatically created from strings where necessary, such as by {@link jQuery.rdf#where}.</p> 1745 * @class Represents a pattern that may or may not match a given {@link jQuery.rdf.triple}. 1746 * @param {String|jQuery.rdf.resource|jQuery.rdf.blank} subject The subject pattern, or a single string that defines the entire pattern. If the subject is specified as a string, it can be a fixed resource (<code><<var>uri</var>></code> or <code><var>curie</var></code>), a blank node (<code>_:<var>id</var></code>) or a variable placeholder (<code>?<var>name</var></code>). 1747 * @param {String|jQuery.rdf.resource} [property] The property pattern. If the property is specified as a string, it can be a fixed resource (<code><<var>uri</var>></code> or <code><var>curie</var></code>) or a variable placeholder (<code>?<var>name</var></code>). 1748 * @param {String|jQuery.rdf.resource|jQuery.rdf.blank|jQuery.rdf.literal} [value] The value pattern. If the property is specified as a string, it can be a fixed resource (<code><<var>uri</var>></code> or <code><var>curie</var></code>), a blank node (<code>_:<var>id</var></code>), a literal (<code>"<var>value</var>"</code>) or a variable placeholder (<code>?<var>name</var></code>). 1749 * @param {Object} [options] Initialisation of the pattern. 1750 * @param {Object} [options.namespaces] An object representing a set of namespace bindings used when interpreting the CURIEs in the subject, property and object. 1751 * @param {String|jQuery.uri} [options.base] The base URI used to interpret any relative URIs used within the subject, property and object. 1752 * @param {boolean} [options.optional] 1753 * @returns {jQuery.rdf.pattern} The newly-created pattern. 1754 * @throws {String} Errors if any of the strings are not in a recognised format. 1755 * @example pattern = $.rdf.pattern('?person', $.rdf.type, 'foaf:Person', { namespaces: { foaf: "http://xmlns.com/foaf/0.1/" }}); 1756 * @example 1757 * pattern = $.rdf.pattern('?person a foaf:Person', { 1758 * namespaces: { foaf: "http://xmlns.com/foaf/0.1/" }, 1759 * optional: true 1760 * }); 1761 * @see jQuery.rdf#where 1762 * @see jQuery.rdf.resource 1763 * @see jQuery.rdf.blank 1764 * @see jQuery.rdf.literal 1765 */ 1766 $.rdf.pattern = function (subject, property, object, options) { 1767 var pattern, m, optional; 1768 // using a two-argument version; first argument is a Turtle statement string 1769 if (object === undefined) { 1770 options = property || {}; 1771 m = $.trim(subject).match(tripleRegex); 1772 if (m.length === 3 || (m.length === 4 && m[3] === '.')) { 1773 subject = m[0]; 1774 property = m[1]; 1775 object = m[2]; 1776 } else { 1777 throw "Bad Pattern: Couldn't parse string " + subject; 1778 } 1779 optional = (options.optional === undefined) ? $.rdf.pattern.defaults.optional : options.optional; 1780 } 1781 if (memPattern[subject] && 1782 memPattern[subject][property] && 1783 memPattern[subject][property][object] && 1784 memPattern[subject][property][object][optional]) { 1785 return memPattern[subject][property][object][optional]; 1786 } 1787 pattern = new $.rdf.pattern.fn.init(subject, property, object, options); 1788 if (memPattern[pattern.subject] && 1789 memPattern[pattern.subject][pattern.property] && 1790 memPattern[pattern.subject][pattern.property][pattern.object] && 1791 memPattern[pattern.subject][pattern.property][pattern.object][pattern.optional]) { 1792 return memPattern[pattern.subject][pattern.property][pattern.object][pattern.optional]; 1793 } else { 1794 if (memPattern[pattern.subject] === undefined) { 1795 memPattern[pattern.subject] = {}; 1796 } 1797 if (memPattern[pattern.subject][pattern.property] === undefined) { 1798 memPattern[pattern.subject][pattern.property] = {}; 1799 } 1800 if (memPattern[pattern.subject][pattern.property][pattern.object] === undefined) { 1801 memPattern[pattern.subject][pattern.property][pattern.object] = {}; 1802 } 1803 memPattern[pattern.subject][pattern.property][pattern.object][pattern.optional] = pattern; 1804 return pattern; 1805 } 1806 }; 1807 1808 $.rdf.pattern.fn = $.rdf.pattern.prototype = { 1809 init: function (s, p, o, options) { 1810 var opts = $.extend({}, $.rdf.pattern.defaults, options); 1811 /** 1812 * The placeholder for the subject of triples matching against this pattern. 1813 * @type String|jQuery.rdf.resource|jQuery.rdf.blank 1814 */ 1815 this.subject = s.toString().substring(0, 1) === '?' ? s : subject(s, opts); 1816 /** 1817 * The placeholder for the property of triples matching against this pattern. 1818 * @type String|jQuery.rdf.resource 1819 */ 1820 this.property = p.toString().substring(0, 1) === '?' ? p : property(p, opts); 1821 /** 1822 * The placeholder for the object of triples matching against this pattern. 1823 * @type String|jQuery.rdf.resource|jQuery.rdf.blank|jQuery.rdf.literal 1824 */ 1825 this.object = o.toString().substring(0, 1) === '?' ? o : object(o, opts); 1826 /** 1827 * Whether the pattern should only optionally match against the triple 1828 * @type boolean 1829 */ 1830 this.optional = opts.optional; 1831 return this; 1832 }, 1833 1834 /** 1835 * Creates a new {@link jQuery.rdf.pattern} with any variable placeholders within this one's subject, property or object filled in with values from the bindings passed as the argument. 1836 * @param {Object} bindings An object holding the variable bindings to be used to replace any placeholders in the pattern. These bindings are of the type held by the {@link jQuery.rdf} object. 1837 * @returns {jQuery.rdf.pattern} A new {@link jQuery.rdf.pattern} object. 1838 * @example 1839 * pattern = $.rdf.pattern('?thing a ?class'); 1840 * // pattern2 matches all triples that indicate the classes of this page. 1841 * pattern2 = pattern.fill({ thing: $.rdf.resource('<>') }); 1842 */ 1843 fill: function (bindings) { 1844 var s = this.subject, 1845 p = this.property, 1846 o = this.object; 1847 if (typeof s === 'string' && bindings[s.substring(1)]) { 1848 s = bindings[s.substring(1)]; 1849 } 1850 if (typeof p === 'string' && bindings[p.substring(1)]) { 1851 p = bindings[p.substring(1)]; 1852 } 1853 if (typeof o === 'string' && bindings[o.substring(1)]) { 1854 o = bindings[o.substring(1)]; 1855 } 1856 return $.rdf.pattern(s, p, o, { optional: this.optional }); 1857 }, 1858 1859 /** 1860 * Creates a new Object holding variable bindings by matching the passed triple against this pattern. 1861 * @param {jQuery.rdf.triple} triple A {@link jQuery.rdf.triple} for this pattern to match against. 1862 * @returns {null|Object} An object containing the bindings of variables (as specified in this pattern) to values (as specified in the triple), or <code>null</code> if the triple doesn't match the pattern. 1863 * pattern = $.rdf.pattern('?thing a ?class'); 1864 * bindings = pattern.exec($.rdf.triple('<> a foaf:Person', { namespaces: ns })); 1865 * thing = bindings.thing; // the resource for this page 1866 * class = bindings.class; // a resource for foaf:Person 1867 */ 1868 exec: function (triple) { 1869 var binding = {}; 1870 binding = testResource(triple.subject, this.subject, binding); 1871 if (binding === null) { 1872 return null; 1873 } 1874 binding = testResource(triple.property, this.property, binding); 1875 if (binding === null) { 1876 return null; 1877 } 1878 binding = testResource(triple.object, this.object, binding); 1879 return binding; 1880 }, 1881 1882 /** 1883 * Tests whether this pattern has any variable placeholders in it or not. 1884 * @returns {boolean} True if the pattern doesn't contain any variable placeholders. 1885 * @example 1886 * $.rdf.pattern('?thing a ?class').isFixed(); // false 1887 * $.rdf.pattern('<> a foaf:Person', { namespaces: ns }).isFixed(); // true 1888 */ 1889 isFixed: function () { 1890 return typeof this.subject !== 'string' && 1891 typeof this.property !== 'string' && 1892 typeof this.object !== 'string'; 1893 }, 1894 1895 /** 1896 * Creates a new triple based on the bindings passed to the pattern, if possible. 1897 * @param {Object} bindings An object holding the variable bindings to be used to replace any placeholders in the pattern. These bindings are of the type held by the {@link jQuery.rdf} object. 1898 * @returns {null|jQuery.rdf.triple} A new {@link jQuery.rdf.triple} object, or null if not all the variable placeholders in the pattern are specified in the bindings. The {@link jQuery.rdf.triple#source} of the generated triple is set to the string value of this pattern. 1899 * @example 1900 * pattern = $.rdf.pattern('?thing a ?class'); 1901 * // triple is a new triple '<> a foaf:Person' 1902 * triple = pattern.triple({ 1903 * thing: $.rdf.resource('<>'), 1904 * class: $.rdf.resource('foaf:Person', { namespaces: ns }) 1905 * }); 1906 */ 1907 triple: function (bindings) { 1908 var t = this; 1909 if (!this.isFixed()) { 1910 t = this.fill(bindings); 1911 } 1912 if (t.isFixed()) { 1913 return $.rdf.triple(t.subject, t.property, t.object, { source: this.toString() }); 1914 } else { 1915 return null; 1916 } 1917 }, 1918 1919 /** 1920 * Returns a string representation of the pattern by concatenating the subject, property and object. 1921 * @returns {String} 1922 */ 1923 toString: function () { 1924 return this.subject + ' ' + this.property + ' ' + this.object; 1925 } 1926 }; 1927 1928 $.rdf.pattern.fn.init.prototype = $.rdf.pattern.fn; 1929 1930 $.rdf.pattern.defaults = { 1931 base: $.uri.base(), 1932 namespaces: {}, 1933 optional: false 1934 }; 1935 1936 /** 1937 * <p>Creates a new jQuery.rdf.triple object. This should be invoked as a method rather than constructed using new; indeed you will not usually want to generate these objects directly, since they are automatically created from strings where necessary, such as by {@link jQuery.rdf#add}.</p> 1938 * @class Represents an RDF triple. 1939 * @param {String|jQuery.rdf.resource|jQuery.rdf.blank} subject The subject of the triple, or a single string that defines the entire triple. If the subject is specified as a string, it can be a fixed resource (<code><<var>uri</var>></code> or <code><var>curie</var></code>) or a blank node (<code>_:<var>id</var></code>). 1940 * @param {String|jQuery.rdf.resource} [property] The property pattern. If the property is specified as a string, it must be a fixed resource (<code><<var>uri</var>></code> or <code><var>curie</var></code>). 1941 * @param {String|jQuery.rdf.resource|jQuery.rdf.blank|jQuery.rdf.literal} [value] The value pattern. If the property is specified as a string, it can be a fixed resource (<code><<var>uri</var>></code> or <code><var>curie</var></code>), a blank node (<code>_:<var>id</var></code>), or a literal (<code>"<var>value</var>"</code>). 1942 * @param {Object} [options] Initialisation of the triple. 1943 * @param {Object} [options.namespaces] An object representing a set of namespace bindings used when interpreting the CURIEs in the subject, property and object. 1944 * @param {String|jQuery.uri} [options.base] The base URI used to interpret any relative URIs used within the subject, property and object. 1945 * @returns {jQuery.rdf.triple} The newly-created triple. 1946 * @throws {String} Errors if any of the strings are not in a recognised format. 1947 * @example pattern = $.rdf.triple('<>', $.rdf.type, 'foaf:Person', { namespaces: { foaf: "http://xmlns.com/foaf/0.1/" }}); 1948 * @example 1949 * pattern = $.rdf.triple('<> a foaf:Person', { 1950 * namespaces: { foaf: "http://xmlns.com/foaf/0.1/" } 1951 * }); 1952 * @see jQuery.rdf#add 1953 * @see jQuery.rdf.resource 1954 * @see jQuery.rdf.blank 1955 * @see jQuery.rdf.literal 1956 */ 1957 $.rdf.triple = function (subject, property, object, options) { 1958 var triple, graph, m; 1959 // using a two-argument version; first argument is a Turtle statement string 1960 if (object === undefined) { 1961 options = property; 1962 m = $.trim(subject).match(tripleRegex); 1963 if (m.length === 3 || (m.length === 4 && m[3] === '.')) { 1964 subject = m[0]; 1965 property = m[1]; 1966 object = m[2]; 1967 } else { 1968 throw "Bad Triple: Couldn't parse string " + subject; 1969 } 1970 } 1971 graph = (options && options.graph) || ''; 1972 if (memTriple[graph] && 1973 memTriple[graph][subject] && 1974 memTriple[graph][subject][property] && 1975 memTriple[graph][subject][property][object]) { 1976 return memTriple[graph][subject][property][object]; 1977 } 1978 triple = new $.rdf.triple.fn.init(subject, property, object, options); 1979 graph = triple.graph || ''; 1980 if (memTriple[graph] && 1981 memTriple[graph][triple.subject] && 1982 memTriple[graph][triple.subject][triple.property] && 1983 memTriple[graph][triple.subject][triple.property][triple.object]) { 1984 return memTriple[graph][triple.subject][triple.property][triple.object]; 1985 } else { 1986 if (memTriple[graph] === undefined) { 1987 memTriple[graph] = {}; 1988 } 1989 if (memTriple[graph][triple.subject] === undefined) { 1990 memTriple[graph][triple.subject] = {}; 1991 } 1992 if (memTriple[graph][triple.subject][triple.property] === undefined) { 1993 memTriple[graph][triple.subject][triple.property] = {}; 1994 } 1995 memTriple[graph][triple.subject][triple.property][triple.object] = triple; 1996 return triple; 1997 } 1998 }; 1999 2000 $.rdf.triple.fn = $.rdf.triple.prototype = { 2001 init: function (s, p, o, options) { 2002 var opts; 2003 opts = $.extend({}, $.rdf.triple.defaults, options); 2004 /** 2005 * The subject of the triple. 2006 * @type jQuery.rdf.resource|jQuery.rdf.blank 2007 */ 2008 this.subject = subject(s, opts); 2009 /** 2010 * The property of the triple. 2011 * @type jQuery.rdf.resource 2012 */ 2013 this.property = property(p, opts); 2014 /** 2015 * The object of the triple. 2016 * @type jQuery.rdf.resource|jQuery.rdf.blank|jQuery.rdf.literal 2017 */ 2018 this.object = object(o, opts); 2019 /** 2020 * (Experimental) The named graph the triple belongs to. 2021 * @type jQuery.rdf.resource|jQuery.rdf.blank 2022 */ 2023 this.graph = opts.graph === undefined ? undefined : subject(opts.graph, opts); 2024 /** 2025 * The source of the triple, which might be a node within the page (if the RDF is generated from the page) or a string holding the pattern that generated the triple. 2026 */ 2027 this.source = opts.source; 2028 return this; 2029 }, 2030 2031 /** 2032 * Always returns true for triples. 2033 * @see jQuery.rdf.pattern#isFixed 2034 */ 2035 isFixed: function () { 2036 return true; 2037 }, 2038 2039 /** 2040 * Always returns this triple. 2041 * @see jQuery.rdf.pattern#triple 2042 */ 2043 triple: function (bindings) { 2044 return this; 2045 }, 2046 2047 /** 2048 * Returns a <a href="http://n2.talis.com/wiki/RDF_JSON_Specification">RDF/JSON</a> representation of this triple. 2049 * @returns {Object} 2050 */ 2051 dump: function () { 2052 var e = {}, 2053 s = this.subject.value.toString(), 2054 p = this.property.value.toString(); 2055 e[s] = {}; 2056 e[s][p] = this.object.dump(); 2057 return e; 2058 }, 2059 2060 /** 2061 * Returns a string representing this triple in Turtle format. 2062 * @returns {String} 2063 */ 2064 toString: function () { 2065 return this.subject + ' ' + this.property + ' ' + this.object + ' .'; 2066 } 2067 }; 2068 2069 $.rdf.triple.fn.init.prototype = $.rdf.triple.fn; 2070 2071 $.rdf.triple.defaults = { 2072 base: $.uri.base(), 2073 source: [document], 2074 namespaces: {} 2075 }; 2076 2077 /** 2078 * <p>Creates a new jQuery.rdf.resource object. This should be invoked as a method rather than constructed using new; indeed you will not usually want to generate these objects directly, since they are automatically created from strings where necessary, such as by {@link jQuery.rdf#add}.</p> 2079 * @class Represents an RDF resource. 2080 * @param {String|jQuery.uri} value The value of the resource. If it's a string it must be in the format <code><<var>uri</var>></code> or <code><var>curie</var></code>. 2081 * @param {Object} [options] Initialisation of the resource. 2082 * @param {Object} [options.namespaces] An object representing a set of namespace bindings used when interpreting the CURIE specifying the resource. 2083 * @param {String|jQuery.uri} [options.base] The base URI used to interpret any relative URIs used within the URI specifying the resource. 2084 * @returns {jQuery.rdf.resource} The newly-created resource. 2085 * @throws {String} Errors if the string is not in a recognised format. 2086 * @example thisPage = $.rdf.resource('<>'); 2087 * @example foaf.Person = $.rdf.resource('foaf:Person', { namespaces: ns }); 2088 * @see jQuery.rdf.pattern 2089 * @see jQuery.rdf.triple 2090 * @see jQuery.rdf.blank 2091 * @see jQuery.rdf.literal 2092 */ 2093 $.rdf.resource = function (value, options) { 2094 var resource; 2095 if (memResource[value]) { 2096 return memResource[value]; 2097 } 2098 resource = new $.rdf.resource.fn.init(value, options); 2099 if (memResource[resource]) { 2100 return memResource[resource]; 2101 } else { 2102 memResource[resource] = resource; 2103 return resource; 2104 } 2105 }; 2106 2107 $.rdf.resource.fn = $.rdf.resource.prototype = { 2108 /** 2109 * Always fixed to 'uri' for resources. 2110 * @type String 2111 */ 2112 type: 'uri', 2113 /** 2114 * The URI for the resource. 2115 * @type jQuery.rdf.uri 2116 */ 2117 value: undefined, 2118 2119 init: function (value, options) { 2120 var m, prefix, uri, opts; 2121 if (typeof value === 'string') { 2122 m = uriRegex.exec(value); 2123 opts = $.extend({}, $.rdf.resource.defaults, options); 2124 if (m !== null) { 2125 this.value = $.uri.resolve(m[1].replace(/\\>/g, '>'), opts.base); 2126 } else if (value.substring(0, 1) === ':') { 2127 uri = opts.namespaces['']; 2128 if (uri === undefined) { 2129 throw "Malformed Resource: No namespace binding for default namespace in " + value; 2130 } else { 2131 this.value = $.uri.resolve(uri + value.substring(1)); 2132 } 2133 } else if (value.substring(value.length - 1) === ':') { 2134 prefix = value.substring(0, value.length - 1); 2135 uri = opts.namespaces[prefix]; 2136 if (uri === undefined) { 2137 throw "Malformed Resource: No namespace binding for prefix " + prefix + " in " + value; 2138 } else { 2139 this.value = $.uri.resolve(uri); 2140 } 2141 } else { 2142 try { 2143 this.value = $.curie(value, { namespaces: opts.namespaces }); 2144 } catch (e) { 2145 throw "Malformed Resource: Bad format for resource " + e; 2146 } 2147 } 2148 } else { 2149 this.value = value; 2150 } 2151 return this; 2152 }, // end init 2153 2154 /** 2155 * Returns a <a href="http://n2.talis.com/wiki/RDF_JSON_Specification">RDF/JSON</a> representation of this triple. 2156 * @returns {Object} 2157 */ 2158 dump: function () { 2159 return { 2160 type: 'uri', 2161 value: this.value.toString() 2162 }; 2163 }, 2164 2165 /** 2166 * Returns a string representing this resource in Turtle format. 2167 * @returns {String} 2168 */ 2169 toString: function () { 2170 return '<' + this.value + '>'; 2171 } 2172 }; 2173 2174 $.rdf.resource.fn.init.prototype = $.rdf.resource.fn; 2175 2176 $.rdf.resource.defaults = { 2177 base: $.uri.base(), 2178 namespaces: {} 2179 }; 2180 2181 /** 2182 * A {@link jQuery.rdf.resource} for rdf:type 2183 * @constant 2184 * @type jQuery.rdf.resource 2185 */ 2186 $.rdf.type = $.rdf.resource('<' + rdfNs + 'type>'); 2187 /** 2188 * A {@link jQuery.rdf.resource} for rdfs:label 2189 * @constant 2190 * @type jQuery.rdf.resource 2191 */ 2192 $.rdf.label = $.rdf.resource('<' + rdfsNs + 'label>'); 2193 /** 2194 * A {@link jQuery.rdf.resource} for rdf:first 2195 * @constant 2196 * @type jQuery.rdf.resource 2197 */ 2198 $.rdf.first = $.rdf.resource('<' + rdfNs + 'first>'); 2199 /** 2200 * A {@link jQuery.rdf.resource} for rdf:rest 2201 * @constant 2202 * @type jQuery.rdf.resource 2203 */ 2204 $.rdf.rest = $.rdf.resource('<' + rdfNs + 'rest>'); 2205 /** 2206 * A {@link jQuery.rdf.resource} for rdf:nil 2207 * @constant 2208 * @type jQuery.rdf.resource 2209 */ 2210 $.rdf.nil = $.rdf.resource('<' + rdfNs + 'nil>'); 2211 /** 2212 * A {@link jQuery.rdf.resource} for rdf:subject 2213 * @constant 2214 * @type jQuery.rdf.resource 2215 */ 2216 $.rdf.subject = $.rdf.resource('<' + rdfNs + 'subject>'); 2217 /** 2218 * A {@link jQuery.rdf.resource} for rdf:property 2219 * @constant 2220 * @type jQuery.rdf.resource 2221 */ 2222 $.rdf.property = $.rdf.resource('<' + rdfNs + 'property>'); 2223 /** 2224 * A {@link jQuery.rdf.resource} for rdf:object 2225 * @constant 2226 * @type jQuery.rdf.resource 2227 */ 2228 $.rdf.object = $.rdf.resource('<' + rdfNs + 'object>'); 2229 2230 /** 2231 * <p>Creates a new jQuery.rdf.blank object. This should be invoked as a method rather than constructed using new; indeed you will not usually want to generate these objects directly, since they are automatically created from strings where necessary, such as by {@link jQuery.rdf#add}.</p> 2232 * @class Represents an RDF blank node. 2233 * @param {String} value A representation of the blank node in the format <code>_:<var>id</var></code> or <code>[]</code> (which automatically creates a new blank node with a unique ID). 2234 * @returns {jQuery.rdf.blank} The newly-created blank node. 2235 * @throws {String} Errors if the string is not in a recognised format. 2236 * @example newBlank = $.rdf.blank('[]'); 2237 * @example identifiedBlank = $.rdf.blank('_:fred'); 2238 * @see jQuery.rdf.pattern 2239 * @see jQuery.rdf.triple 2240 * @see jQuery.rdf.resource 2241 * @see jQuery.rdf.literal 2242 */ 2243 $.rdf.blank = function (value) { 2244 var blank; 2245 if (memBlank[value]) { 2246 return memBlank[value]; 2247 } 2248 blank = new $.rdf.blank.fn.init(value); 2249 if (memBlank[blank]) { 2250 return memBlank[blank]; 2251 } else { 2252 memBlank[blank] = blank; 2253 return blank; 2254 } 2255 }; 2256 2257 $.rdf.blank.fn = $.rdf.blank.prototype = { 2258 /** 2259 * Always fixed to 'bnode' for blank nodes. 2260 * @type String 2261 */ 2262 type: 'bnode', 2263 /** 2264 * The value of the blank node in the format <code>_:<var>id</var></code> 2265 * @type String 2266 */ 2267 value: undefined, 2268 /** 2269 * The id of the blank node. 2270 * @type String 2271 */ 2272 id: undefined, 2273 2274 init: function (value) { 2275 if (value === '[]') { 2276 this.id = blankNodeID(); 2277 this.value = '_:' + this.id; 2278 } else if (value.substring(0, 2) === '_:') { 2279 this.id = value.substring(2); 2280 this.value = value; 2281 } else { 2282 throw "Malformed Blank Node: " + value + " is not a legal format for a blank node"; 2283 } 2284 return this; 2285 }, 2286 2287 /** 2288 * Returns a <a href="http://n2.talis.com/wiki/RDF_JSON_Specification">RDF/JSON</a> representation of this blank node. 2289 * @returns {Object} 2290 */ 2291 dump: function () { 2292 return { 2293 type: 'bnode', 2294 value: this.value 2295 }; 2296 }, 2297 2298 /** 2299 * Returns the value this blank node. 2300 * @returns {String} 2301 */ 2302 toString: function () { 2303 return this.value; 2304 } 2305 }; 2306 2307 $.rdf.blank.fn.init.prototype = $.rdf.blank.fn; 2308 2309 /** 2310 * <p>Creates a new jQuery.rdf.literal object. This should be invoked as a method rather than constructed using new; indeed you will not usually want to generate these objects directly, since they are automatically created from strings where necessary, such as by {@link jQuery.rdf#add}.</p> 2311 * @class Represents an RDF literal. 2312 * @param {String|boolean|Number} value Either the value of the literal or a string representation of it. If the datatype or lang options are specified, the value is taken as given. Otherwise, if it's a Javascript boolean or numeric value, it is interpreted as a value with a xsd:boolean or xsd:double datatype. In all other cases it's interpreted as a literal as defined in <a href="http://www.w3.org/TeamSubmission/turtle/#literal">Turtle syntax</a>. 2313 * @param {Object} [options] Initialisation options for the literal. 2314 * @param {String} [options.datatype] The datatype for the literal. This should be a safe CURIE; in other words, it can be in the format <code><var>uri</var></code> or <code>[<var>curie</var>]</code>. Must not be specified if options.lang is also specified. 2315 * @param {String} [options.lang] The language for the literal. Must not be specified if options.datatype is also specified. 2316 * @param {Object} [options.namespaces] An object representing a set of namespace bindings used when interpreting a CURIE in the datatype. 2317 * @param {String|jQuery.uri} [options.base] The base URI used to interpret a relative URI in the datatype. 2318 * @returns {jQuery.rdf.literal} The newly-created literal. 2319 * @throws {String} Errors if the string is not in a recognised format or if both options.datatype and options.lang are specified. 2320 * @example trueLiteral = $.rdf.literal(true); 2321 * @example numericLiteral = $.rdf.literal(5); 2322 * @example dateLiteral = $.rdf.literal('"2009-07-13"^^xsd:date', { namespaces: ns }); 2323 * @see jQuery.rdf.pattern 2324 * @see jQuery.rdf.triple 2325 * @see jQuery.rdf.resource 2326 * @see jQuery.rdf.blank 2327 */ 2328 $.rdf.literal = function (value, options) { 2329 var literal; 2330 if (memLiteral[value]) { 2331 return memLiteral[value]; 2332 } 2333 literal = new $.rdf.literal.fn.init(value, options); 2334 if (memLiteral[literal]) { 2335 return memLiteral[literal]; 2336 } else { 2337 memLiteral[literal] = literal; 2338 return literal; 2339 } 2340 }; 2341 2342 $.rdf.literal.fn = $.rdf.literal.prototype = { 2343 /** 2344 * Always fixed to 'literal' for literals. 2345 * @type String 2346 */ 2347 type: 'literal', 2348 /** 2349 * The value of the literal as a string. 2350 * @type String 2351 */ 2352 value: undefined, 2353 /** 2354 * The language of the literal, if it has one; otherwise undefined. 2355 * @type String 2356 */ 2357 lang: undefined, 2358 /** 2359 * The datatype of the literal, if it has one; otherwise undefined. 2360 * @type jQuery.uri 2361 */ 2362 datatype: undefined, 2363 2364 init: function (value, options) { 2365 var 2366 m, datatype, 2367 opts = $.extend({}, $.rdf.literal.defaults, options); 2368 datatype = $.safeCurie(opts.datatype, { namespaces: opts.namespaces }); 2369 if (opts.lang !== undefined && opts.datatype !== undefined && datatype.toString() !== (rdfNs + 'XMLLiteral')) { 2370 throw "Malformed Literal: Cannot define both a language and a datatype for a literal (" + value + ")"; 2371 } 2372 if (opts.datatype !== undefined) { 2373 datatype = $.safeCurie(opts.datatype, { namespaces: opts.namespaces }); 2374 $.extend(this, $.typedValue(value.toString(), datatype)); 2375 if (datatype.toString() === rdfNs + 'XMLLiteral') { 2376 this.lang = opts.lang; 2377 } 2378 } else if (opts.lang !== undefined) { 2379 this.value = value.toString(); 2380 this.lang = opts.lang; 2381 } else if (typeof value === 'boolean') { 2382 $.extend(this, $.typedValue(value.toString(), xsdNs + 'boolean')); 2383 } else if (typeof value === 'number') { 2384 $.extend(this, $.typedValue(value.toString(), xsdNs + 'double')); 2385 } else if (value === 'true' || value === 'false') { 2386 $.extend(this, $.typedValue(value, xsdNs + 'boolean')); 2387 } else if ($.typedValue.valid(value, xsdNs + 'integer')) { 2388 $.extend(this, $.typedValue(value, xsdNs + 'integer')); 2389 } else if ($.typedValue.valid(value, xsdNs + 'decimal')) { 2390 $.extend(this, $.typedValue(value, xsdNs + 'decimal')); 2391 } else if ($.typedValue.valid(value, xsdNs + 'double') && 2392 !/^\s*([\-\+]?INF|NaN)\s*$/.test(value)) { // INF, -INF and NaN aren't valid literals in Turtle 2393 $.extend(this, $.typedValue(value, xsdNs + 'double')); 2394 } else { 2395 m = literalRegex.exec(value); 2396 if (m !== null) { 2397 this.value = (m[2] || m[4]).replace(/\\"/g, '"').replace(/\\n/g, '\n').replace(/\\t/g, '\t').replace(/\\r/g, '\r'); 2398 if (m[9]) { 2399 datatype = $.rdf.resource(m[9], opts); 2400 $.extend(this, $.typedValue(this.value, datatype.value)); 2401 } else if (m[7]) { 2402 this.lang = m[7]; 2403 } 2404 } else { 2405 throw "Malformed Literal: Couldn't recognise the value " + value; 2406 } 2407 } 2408 return this; 2409 }, // end init 2410 2411 /** 2412 * Returns a <a href="http://n2.talis.com/wiki/RDF_JSON_Specification">RDF/JSON</a> representation of this blank node. 2413 * @returns {Object} 2414 */ 2415 dump: function () { 2416 var e = { 2417 type: 'literal', 2418 value: this.value.toString() 2419 }; 2420 if (this.lang !== undefined) { 2421 e.lang = this.lang; 2422 } else if (this.datatype !== undefined) { 2423 e.datatype = this.datatype.toString(); 2424 } 2425 return e; 2426 }, 2427 2428 /** 2429 * Returns a string representing this resource in <a href="http://www.w3.org/TeamSubmission/turtle/#literal">Turtle format</a>. 2430 * @returns {String} 2431 */ 2432 toString: function () { 2433 var val = '"' + this.value + '"'; 2434 if (this.lang !== undefined) { 2435 val += '@' + this.lang; 2436 } else if (this.datatype !== undefined) { 2437 val += '^^<' + this.datatype + '>'; 2438 } 2439 return val; 2440 } 2441 }; 2442 2443 $.rdf.literal.fn.init.prototype = $.rdf.literal.fn; 2444 2445 $.rdf.literal.defaults = { 2446 base: $.uri.base(), 2447 namespaces: {}, 2448 datatype: undefined, 2449 lang: undefined 2450 }; 2451 2452 })(jQuery); 2453