1 /* 2 * jQuery RDFa @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.curie.js 11 * jquery.datatype.js 12 * jquery.rdf.js 13 */ 14 /** 15 * @fileOverview jQuery RDFa processing 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 * @requires jquery.uri.js 21 * @requires jquery.xmlns.js 22 * @requires jquery.curie.js 23 * @requires jquery.datatype.js 24 * @requires jquery.rdf.js 25 */ 26 (function ($) { 27 28 var 29 ns = { 30 rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", 31 xsd: "http://www.w3.org/2001/XMLSchema#", 32 xml: 'http://www.w3.org/XML/1998/namespace', 33 xmlns: 'http://www.w3.org/2000/xmlns/' 34 }, 35 36 rdfXMLLiteral = ns.rdf + 'XMLLiteral', 37 38 rdfaCurieDefaults = $.fn.curie.defaults, 39 relReserved = [ 40 'alternate', 'appendix', 'bookmark', 'cite', 'chapter', 'contents', 'copyright', 41 'first', 'glossary', 'help', 'icon', 'index', 'last', 'license', 'meta', 'next', 42 'p3pv1', 'prev', 'role', 'section', 'stylesheet', 'subsection', 'start', 'top', 'up' 43 ], 44 45 attRegex = /\s([^ =]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^ >]+))/g, 46 47 ncNameChar = '[-A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u10000-\uEFFFF\.0-9\u00B7\u0300-\u036F\u203F-\u2040]', 48 ncNameStartChar = '[\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u0131\u0134-\u013E\u0141-\u0148\u014A-\u017E\u0180-\u01C3\u01CD-\u01F0\u01F4-\u01F5\u01FA-\u0217\u0250-\u02A8\u02BB-\u02C1\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03D6\u03DA\u03DC\u03DE\u03E0\u03E2-\u03F3\u0401-\u040C\u040E-\u044F\u0451-\u045C\u045E-\u0481\u0490-\u04C4\u04C7-\u04C8\u04CB-\u04CC\u04D0-\u04EB\u04EE-\u04F5\u04F8-\u04F9\u0531-\u0556\u0559\u0561-\u0586\u05D0-\u05EA\u05F0-\u05F2\u0621-\u063A\u0641-\u064A\u0671-\u06B7\u06BA-\u06BE\u06C0-\u06CE\u06D0-\u06D3\u06D5\u06E5-\u06E6\u0905-\u0939\u093D\u0958-\u0961\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8B\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AE0\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B36-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB5\u0BB7-\u0BB9\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CDE\u0CE0-\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D60-\u0D61\u0E01-\u0E2E\u0E30\u0E32-\u0E33\u0E40-\u0E45\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EAE\u0EB0\u0EB2-\u0EB3\u0EBD\u0EC0-\u0EC4\u0F40-\u0F47\u0F49-\u0F69\u10A0-\u10C5\u10D0-\u10F6\u1100\u1102-\u1103\u1105-\u1107\u1109\u110B-\u110C\u110E-\u1112\u113C\u113E\u1140\u114C\u114E\u1150\u1154-\u1155\u1159\u115F-\u1161\u1163\u1165\u1167\u1169\u116D-\u116E\u1172-\u1173\u1175\u119E\u11A8\u11AB\u11AE-\u11AF\u11B7-\u11B8\u11BA\u11BC-\u11C2\u11EB\u11F0\u11F9\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2126\u212A-\u212B\u212E\u2180-\u2182\u3041-\u3094\u30A1-\u30FA\u3105-\u312C\uAC00-\uD7A3\u4E00-\u9FA5\u3007\u3021-\u3029_]', 49 ncNameRegex = new RegExp('^' + ncNameStartChar + ncNameChar + '*$'), 50 51 docResource = $.rdf.resource('<>'), 52 53 parseEntities = function (string) { 54 var result = "", m, entity; 55 if (!/&/.test(string)) { 56 return string; 57 } 58 while (string.length > 0) { 59 m = /([^&]*)(&([^;]+);)(.*)/g.exec(string); 60 if (m === null) { 61 result += string; 62 break; 63 } 64 result += m[1]; 65 entity = m[3]; 66 string = m[4]; 67 if (entity.charAt(0) === '#') { 68 if (entity.charAt(1) === 'x') { 69 result += String.fromCharCode(parseInt(entity.substring(2), 16)); 70 } else { 71 result += String.fromCharCode(parseInt(entity.substring(1), 10)); 72 } 73 } else { 74 switch(entity) { 75 case 'amp': 76 result += '&'; 77 break; 78 case 'nbsp': 79 result += String.fromCharCode(160); 80 break; 81 case 'quot': 82 result += '"'; 83 break; 84 case 'apos': 85 result += "'"; 86 break; 87 default: 88 result += '&' + entity + ';'; 89 } 90 } 91 } 92 return result; 93 }, 94 95 getAttributes = function (elem) { 96 var i, e, a, tag, name, value, attMap, prefix, 97 atts = {}, 98 nsMap = {}; 99 e = elem[0]; 100 nsMap[':length'] = 0; 101 if (e.attributes && e.attributes.getNamedItemNS) { 102 attMap = e.attributes; 103 for (i = 0; i < attMap.length; i += 1) { 104 a = attMap[i]; 105 if (/^xmlns(:(.+))?$/.test(a.nodeName) && a.nodeValue !== '') { 106 prefix = /^xmlns(:(.+))?$/.exec(a.nodeName)[2] || ''; 107 if (ncNameRegex.test(prefix) && (prefix !== 'xml' || a.nodeValue === ns.xml) && (a.nodeValue !== ns.xml || prefix === 'xml') && prefix !== 'xmlns' && a.nodeValue !== ns.xmlns) { 108 nsMap[prefix] = $.uri(a.nodeValue); 109 nsMap[':length'] += 1; 110 } 111 // } else if (/rel|rev|lang|xml:lang/.test(a.nodeName)) { 112 // atts[a.nodeName] = a.nodeValue === '' ? undefined : a.nodeValue; 113 } else if (/rel|rev|lang|xml:lang|about|href|src|resource|property|typeof|content|datatype/.test(a.nodeName)) { 114 atts[a.nodeName] = a.nodeValue === null ? undefined : a.nodeValue; 115 } 116 } 117 } else { 118 tag = /<[^>]+>/.exec(e.outerHTML); 119 a = attRegex.exec(tag); 120 while (a !== null) { 121 name = a[1]; 122 value = a[2] || a[3] || a[4]; 123 if (/^xmlns/.test(name) && name !== 'xmlns:' && value !== '') { 124 prefix = /^xmlns(:(.+))?$/.exec(name)[2] || ''; 125 if (ncNameRegex.test(prefix) && (prefix !== 'xml' || a.nodeValue === ns.xml) && (a.nodeValue !== ns.xml || prefix === 'xml') && prefix !== 'xmlns' && a.nodeValue !== ns.xmlns) { 126 nsMap[prefix] = $.uri(value); 127 nsMap[':length'] += 1; 128 } 129 } else if (/about|href|src|resource|property|typeof|content|datatype|rel|rev|lang|xml:lang/.test(name)) { 130 atts[name] = parseEntities(value); 131 } 132 a = attRegex.exec(tag); 133 } 134 attRegex.lastIndex = 0; 135 } 136 return { atts: atts, namespaces: nsMap }; 137 }, 138 139 getAttribute = function (elem, attr) { 140 var val = elem[0].getAttribute(attr); 141 // if (attr === 'rev' || attr === 'rel' || attr === 'lang' || attr === 'xml:lang') { 142 // val = val === '' ? undefined : val; 143 // } 144 return val === null ? undefined : val; 145 }, 146 147 resourceFromUri = function (uri) { 148 return $.rdf.resource(uri); 149 }, 150 151 resourceFromCurie = function (curie, elem, noblanks, options) { 152 if (curie.substring(0, 2) === '_:') { 153 if (noblanks) { 154 return undefined; 155 } 156 return $.rdf.blank(curie); 157 } else { 158 try { 159 return resourceFromUri($.curie(curie, options)); 160 } catch (e) { 161 return undefined; 162 } 163 } 164 }, 165 166 resourceFromSafeCurie = function (safeCurie, elem, options) { 167 var m = /^\[(.*)\]$/.exec(safeCurie), 168 base = options.base || elem.base(); 169 return m ? resourceFromCurie(m[1], elem, false, options) : resourceFromUri($.uri(safeCurie, base)); 170 }, 171 172 resourcesFromCuries = function (curies, elem, noblanks, options) { 173 var i, resource, resources = []; 174 curies = curies && curies.split ? curies.split(/[ \t\n\r\x0C]+/g) : []; 175 for (i = 0; i < curies.length; i += 1) { 176 if (curies[i] !== '') { 177 resource = resourceFromCurie(curies[i], elem, noblanks, options); 178 if (resource !== undefined) { 179 resources.push(resource); 180 } 181 } 182 } 183 return resources; 184 }, 185 186 removeCurie = function (curies, resource, options) { 187 var i, r, newCuries = []; 188 resource = resource.type === 'uri' ? resource : $.rdf.resource(resource, options); 189 curies = curies && curies.split ? curies.split(/\s+/) : []; 190 for (i = 0; i < curies.length; i += 1) { 191 if (curies[i] !== '') { 192 r = resourceFromCurie(curies[i], null, false, options); 193 if (r !== resource) { 194 newCuries.push(curies[i]); 195 } 196 } 197 } 198 return newCuries.reverse().join(' '); 199 }, 200 201 getObjectResource = function (elem, context, relation) { 202 var r, resource, atts, curieOptions; 203 context = context || {}; 204 atts = context.atts || getAttributes(elem).atts; 205 r = relation === undefined ? atts.rel !== undefined || atts.rev !== undefined : relation; 206 resource = atts.resource; 207 resource = resource === undefined ? atts.href : resource; 208 if (resource === undefined) { 209 resource = r ? $.rdf.blank('[]') : resource; 210 } else { 211 curieOptions = context.curieOptions || $.extend({}, rdfaCurieDefaults, { namespaces: elem.xmlns() }); 212 resource = resourceFromSafeCurie(resource, elem, curieOptions); 213 } 214 return resource; 215 }, 216 217 getSubject = function (elem, context, relation) { 218 var r, atts, curieOptions, subject, skip = false; 219 context = context || {}; 220 atts = context.atts || getAttributes(elem).atts; 221 curieOptions = context.curieOptions || $.extend({}, rdfaCurieDefaults, { namespaces: elem.xmlns(), base: elem.base() }); 222 r = relation === undefined ? atts.rel !== undefined || atts.rev !== undefined : relation; 223 if (atts.about !== undefined) { 224 subject = resourceFromSafeCurie(atts.about, elem, curieOptions); 225 } 226 if (subject === undefined && atts.src !== undefined) { 227 subject = resourceFromSafeCurie(atts.src, elem, curieOptions); 228 } 229 if (!r && subject === undefined && atts.resource !== undefined) { 230 subject = resourceFromSafeCurie(atts.resource, elem, curieOptions); 231 } 232 if (!r && subject === undefined && atts.href !== undefined) { 233 subject = resourceFromSafeCurie(atts.href, elem, curieOptions); 234 } 235 if (subject === undefined) { 236 if (/^(head|body)$/i.test(elem[0].nodeName)) { 237 subject = docResource; 238 } else if (atts['typeof'] !== undefined) { 239 subject = $.rdf.blank('[]'); 240 } else if (elem[0].parentNode.nodeType === 1) { 241 subject = context.object || getObjectResource(elem.parent()) || getSubject(elem.parent()).subject; 242 skip = !r && atts.property === undefined; 243 } else { 244 subject = docResource; 245 } 246 } 247 return { subject: subject, skip: skip }; 248 }, 249 250 getLang = function (elem, context) { 251 var lang; 252 context = context || {}; 253 if (context.atts) { 254 lang = context.atts.lang; 255 lang = lang || context.atts['xml:lang']; 256 } else { 257 lang = elem[0].getAttribute('lang'); 258 try { 259 lang = (lang === null || lang === '') ? elem[0].getAttribute('xml:lang') : lang; 260 } catch (e) { 261 } 262 lang = (lang === null || lang === '') ? undefined : lang; 263 } 264 if (lang === undefined) { 265 if (context.lang) { 266 lang = context.lang; 267 } else { 268 if (elem[0].parentNode.nodeType === 1) { 269 lang = getLang(elem.parent()); 270 } 271 } 272 } 273 return lang; 274 }, 275 276 entity = function (c) { 277 switch (c) { 278 case '<': 279 return '<'; 280 case '"': 281 return '"'; 282 case '&': 283 return '&'; 284 } 285 }, 286 287 serialize = function (elem, ignoreNs) { 288 var i, string = '', atts, a, name, ns, tag; 289 elem.contents().each(function () { 290 var j = $(this), 291 e = j[0]; 292 if (e.nodeType === 1) { // tests whether the node is an element 293 name = e.nodeName.toLowerCase(); 294 string += '<' + name; 295 if (e.outerHTML) { 296 tag = /<[^>]+>/.exec(e.outerHTML); 297 a = attRegex.exec(tag); 298 while (a !== null) { 299 if (!/^jQuery/.test(a[1])) { 300 string += ' ' + a[1] + '='; 301 string += a[2] ? a[3] : '"' + a[1] + '"'; 302 } 303 a = attRegex.exec(tag); 304 } 305 attRegex.lastIndex = 0; 306 } else { 307 atts = e.attributes; 308 for (i = 0; i < atts.length; i += 1) { 309 a = atts.item(i); 310 string += ' ' + a.nodeName + '="'; 311 string += a.nodeValue.replace(/[<"&]/g, entity); 312 string += '"'; 313 } 314 } 315 if (!ignoreNs) { 316 ns = j.xmlns(''); 317 if (ns !== undefined && j.attr('xmlns') === undefined) { 318 string += ' xmlns="' + ns + '"'; 319 } 320 } 321 string += '>'; 322 string += serialize(j, true); 323 string += '</' + name + '>'; 324 } else if (e.nodeType === 8) { // tests whether the node is a comment 325 string += '<!--'; 326 string += e.nodeValue; 327 string += '-->'; 328 } else { 329 string += e.nodeValue; 330 } 331 }); 332 return string; 333 }, 334 335 rdfa = function (context) { 336 var i, subject, resource, lang, datatype, content, text, 337 types, object, triple, parent, 338 properties, rels, revs, 339 forward, backward, 340 triples = [], 341 callback, relCurieOptions, 342 attsAndNs, atts, namespaces, ns, 343 children = this.children(); 344 context = context || {}; 345 forward = context.forward || []; 346 backward = context.backward || []; 347 callback = context.callback || function () { return this; }; 348 attsAndNs = getAttributes(this); 349 atts = attsAndNs.atts; 350 context.atts = atts; 351 namespaces = context.namespaces || this.xmlns(); 352 if (attsAndNs.namespaces[':length'] > 0) { 353 namespaces = $.extend({}, namespaces); 354 for (ns in attsAndNs.namespaces) { 355 if (ns !== ':length') { 356 namespaces[ns] = attsAndNs.namespaces[ns]; 357 } 358 } 359 } 360 context.curieOptions = $.extend({}, rdfaCurieDefaults, { reserved: [], namespaces: namespaces, base: this.base() }); 361 relCurieOptions = $.extend({}, context.curieOptions, { reserved: relReserved }); 362 subject = getSubject(this, context); 363 lang = getLang(this, context); 364 if (subject.skip) { 365 rels = context.forward; 366 revs = context.backward; 367 subject = context.subject; 368 resource = context.object; 369 } else { 370 subject = subject.subject; 371 if (forward.length > 0 || backward.length > 0) { 372 parent = context.subject || getSubject(this.parent()).subject; 373 for (i = 0; i < forward.length; i += 1) { 374 triple = $.rdf.triple(parent, forward[i], subject, { source: this[0] }); 375 triple = callback.call(triple, this.get(0), triple); 376 if (triple !== undefined && triple !== null) { 377 triples = triples.concat(triple); 378 } 379 } 380 for (i = 0; i < backward.length; i += 1) { 381 triple = $.rdf.triple(subject, backward[i], parent, { source: this[0] }); 382 triple = callback.call(triple, this.get(0), triple); 383 if (triple !== undefined && triple !== null) { 384 triples = triples.concat(triple); 385 } 386 } 387 } 388 resource = getObjectResource(this, context); 389 types = resourcesFromCuries(atts['typeof'], this, false, context.curieOptions); 390 for (i = 0; i < types.length; i += 1) { 391 triple = $.rdf.triple(subject, $.rdf.type, types[i], { source: this[0] }); 392 triple = callback.call(triple, this.get(0), triple); 393 if (triple !== undefined && triple !== null) { 394 triples = triples.concat(triple); 395 } 396 } 397 properties = resourcesFromCuries(atts.property, this, true, context.curieOptions); 398 if (properties.length > 0) { 399 datatype = atts.datatype; 400 content = atts.content; 401 text = this.text().replace(/"/g, '\\"'); 402 if (datatype !== undefined && datatype !== '') { 403 datatype = $.curie(datatype, context.curieOptions); 404 if (datatype.toString() === rdfXMLLiteral) { 405 object = $.rdf.literal(serialize(this), { datatype: rdfXMLLiteral }); 406 } else if (content !== undefined) { 407 object = $.rdf.literal(content, { datatype: datatype }); 408 } else { 409 object = $.rdf.literal(text, { datatype: datatype }); 410 } 411 } else if (content !== undefined) { 412 if (lang === undefined) { 413 object = $.rdf.literal('"' + content + '"'); 414 } else { 415 object = $.rdf.literal(content, { lang: lang }); 416 } 417 } else if (children.length === 0 || 418 datatype === '') { 419 lang = getLang(this, context); 420 if (lang === undefined) { 421 object = $.rdf.literal('"' + text + '"'); 422 } else { 423 object = $.rdf.literal(text, { lang: lang }); 424 } 425 } else { 426 object = $.rdf.literal(serialize(this), { datatype: rdfXMLLiteral }); 427 } 428 for (i = 0; i < properties.length; i += 1) { 429 triple = $.rdf.triple(subject, properties[i], object, { source: this[0] }); 430 triple = callback.call(triple, this.get(0), triple); 431 if (triple !== undefined && triple !== null) { 432 triples = triples.concat(triple); 433 } 434 } 435 } 436 rels = resourcesFromCuries(atts.rel, this, true, relCurieOptions); 437 revs = resourcesFromCuries(atts.rev, this, true, relCurieOptions); 438 if (atts.resource !== undefined || atts.href !== undefined) { 439 // make the triples immediately 440 if (rels !== undefined) { 441 for (i = 0; i < rels.length; i += 1) { 442 triple = $.rdf.triple(subject, rels[i], resource, { source: this[0] }); 443 triple = callback.call(triple, this.get(0), triple); 444 if (triple !== undefined && triple !== null) { 445 triples = triples.concat(triple); 446 } 447 } 448 } 449 rels = []; 450 if (revs !== undefined) { 451 for (i = 0; i < revs.length; i += 1) { 452 triple = $.rdf.triple(resource, revs[i], subject, { source: this[0] }); 453 triple = callback.call(triple, this.get(0), triple); 454 if (triple !== undefined && triple !== null) { 455 triples = triples.concat(triple); 456 } 457 } 458 } 459 revs = []; 460 } 461 } 462 children.each(function () { 463 triples = triples.concat(rdfa.call($(this), { forward: rels, backward: revs, subject: subject, object: resource || subject, lang: lang, namespaces: namespaces, callback: callback })); 464 }); 465 return triples; 466 }, 467 468 gleaner = function (options) { 469 var type, atts; 470 if (options && options.about !== undefined) { 471 atts = getAttributes(this).atts; 472 if (options.about === null) { 473 return atts.property !== undefined || 474 atts.rel !== undefined || 475 atts.rev !== undefined || 476 atts['typeof'] !== undefined; 477 } else { 478 return getSubject(this, {atts: atts}).subject.value === options.about; 479 } 480 } else if (options && options.type !== undefined) { 481 type = getAttribute(this, 'typeof'); 482 if (type !== undefined) { 483 return options.type === null ? true : this.curie(type) === options.type; 484 } 485 return false; 486 } else { 487 return rdfa.call(this, options); 488 } 489 }, 490 491 nsCounter = 1, 492 493 createCurieAttr = function (elem, attr, uri) { 494 var m, curie, value; 495 try { 496 curie = elem.createCurie(uri); 497 } catch (e) { 498 if (uri.toString() === rdfXMLLiteral) { 499 elem.attr('xmlns:rdf', ns.rdf); 500 curie = 'rdf:XMLLiteral'; 501 } else { 502 m = /^(.+[\/#])([^#]+)$/.exec(uri); 503 elem.attr('xmlns:ns' + nsCounter, m[1]); 504 curie = 'ns' + nsCounter + ':' + m[2]; 505 nsCounter += 1; 506 } 507 } 508 value = getAttribute(elem, attr); 509 if (value !== undefined) { 510 if ($.inArray(curie, value.split(/\s+/)) === -1) { 511 elem.attr(attr, value + ' ' + curie); 512 } 513 } else { 514 elem.attr(attr, curie); 515 } 516 }, 517 518 createResourceAttr = function (elem, attr, resource) { 519 var ref; 520 if (resource.type === 'bnode') { 521 ref = '[_:' + resource.id + ']'; 522 } else { 523 ref = $(elem).base().relative(resource.value); 524 } 525 elem.attr(attr, ref); 526 }, 527 528 createSubjectAttr = function (elem, subject) { 529 var s = getSubject(elem).subject; 530 if (subject !== s) { 531 createResourceAttr(elem, 'about', subject); 532 } 533 elem.removeData('rdfa.subject'); 534 }, 535 536 createObjectAttr = function (elem, object) { 537 var o = getObjectResource(elem); 538 if (object !== o) { 539 createResourceAttr(elem, 'resource', object); 540 } 541 elem.removeData('rdfa.objectResource'); 542 }, 543 544 resetLang = function (elem, lang) { 545 elem.wrapInner('<span></span>') 546 .children('span') 547 .attr('lang', lang); 548 return elem; 549 }, 550 551 addRDFa = function (triple) { 552 var hasContent, hasRelation, hasRDFa, overridableObject, span, 553 subject, sameSubject, 554 object, sameObject, 555 lang, content, 556 i, atts, 557 ns = this.xmlns(); 558 span = this; 559 atts = getAttributes(this).atts; 560 if (typeof triple === 'string') { 561 triple = $.rdf.triple(triple, { namespaces: ns, base: this.base() }); 562 } else if (triple.rdfquery) { 563 addRDFa.call(this, triple.sources().get(0)); 564 return this; 565 } else if (triple.length) { 566 for (i = 0; i < triple.length; i += 1) { 567 addRDFa.call(this, triple[i]); 568 } 569 return this; 570 } 571 hasRelation = atts.rel !== undefined || atts.rev !== undefined; 572 hasRDFa = hasRelation || atts.property !== undefined || atts['typeof'] !== undefined; 573 if (triple.object.type !== 'literal') { 574 subject = getSubject(this, {atts: atts}, true).subject; 575 object = getObjectResource(this, {atts: atts}, true); 576 overridableObject = !hasRDFa && atts.resource === undefined; 577 sameSubject = subject === triple.subject; 578 sameObject = object === triple.object; 579 if (triple.property === $.rdf.type) { 580 if (sameSubject) { 581 createCurieAttr(this, 'typeof', triple.object.value); 582 } else if (hasRDFa) { 583 span = this.wrapInner('<span />').children('span'); 584 createCurieAttr(span, 'typeof', triple.object.value); 585 if (object !== triple.subject) { 586 createSubjectAttr(span, triple.subject); 587 } 588 } else { 589 createCurieAttr(this, 'typeof', triple.object.value); 590 createSubjectAttr(this, triple.subject); 591 } 592 } else if (sameSubject) { 593 // use a rel 594 if (sameObject) { 595 createCurieAttr(this, 'rel', triple.property.value); 596 } else if (overridableObject || !hasRDFa) { 597 createCurieAttr(this, 'rel', triple.property.value); 598 createObjectAttr(this, triple.object); 599 } else { 600 span = this.wrap('<span />').parent(); 601 createCurieAttr(span, 'rev', triple.property.value); 602 createSubjectAttr(span, triple.object); 603 } 604 } else if (subject === triple.object) { 605 if (object === triple.subject) { 606 // use a rev 607 createCurieAttr(this, 'rev', triple.property.value); 608 } else if (overridableObject || !hasRDFa) { 609 createCurieAttr(this, 'rev', triple.property.value); 610 createObjectAttr(this, triple.subject); 611 } else { 612 // wrap in a span with a rel 613 span = this.wrap('<span />').parent(); 614 createCurieAttr(span, 'rel', triple.property.value); 615 createSubjectAttr(span, triple.subject); 616 } 617 } else if (sameObject) { 618 if (hasRDFa) { 619 // use a rev on a nested span 620 span = this.wrapInner('<span />').children('span'); 621 createCurieAttr(span, 'rev', triple.property.value); 622 createObjectAttr(span, triple.subject); 623 span = span.wrapInner('<span />').children('span'); 624 createSubjectAttr(span, triple.object); 625 span = this; 626 } else { 627 createSubjectAttr(this, triple.subject); 628 createCurieAttr(this, 'rel', triple.property.value); 629 } 630 } else if (object === triple.subject) { 631 if (hasRDFa) { 632 // wrap the contents in a span and use a rel 633 span = this.wrapInner('<span />').children('span'); 634 createCurieAttr(span, 'rel', this.property.value); 635 createObjectAttr(span, triple.object); 636 span = span.wrapInner('<span />').children('span'); 637 createSubjectAttr(span, object); 638 span = this; 639 } else { 640 // use a rev on this element 641 createSubjectAttr(this, triple.object); 642 createCurieAttr(this, 'rev', triple.property.value); 643 } 644 } else if (hasRDFa) { 645 span = this.wrapInner('<span />').children('span'); 646 createCurieAttr(span, 'rel', triple.property.value); 647 createSubjectAttr(span, triple.subject); 648 createObjectAttr(span, triple.object); 649 if (span.children('*').length > 0) { 650 span = this.wrapInner('<span />').children('span'); 651 createSubjectAttr(span, subject); 652 } 653 span = this; 654 } else { 655 createCurieAttr(span, 'rel', triple.property.value); 656 createSubjectAttr(this, triple.subject); 657 createObjectAttr(this, triple.object); 658 if (this.children('*').length > 0) { 659 span = this.wrapInner('<span />').children('span'); 660 createSubjectAttr(span, subject); 661 span = this; 662 } 663 } 664 } else { 665 subject = getSubject(this, {atts: atts}).subject; 666 object = getObjectResource(this, {atts: atts}); 667 sameSubject = subject === triple.subject; 668 hasContent = this.text() !== triple.object.value; 669 if (atts.property !== undefined) { 670 content = atts.content; 671 sameObject = content !== undefined ? content === triple.object.value : !hasContent; 672 if (sameSubject && sameObject) { 673 createCurieAttr(this, 'property', triple.property.value); 674 } else { 675 span = this.wrapInner('<span />').children('span'); 676 return addRDFa.call(span, triple); 677 } 678 } else { 679 if (object === triple.subject) { 680 span = this.wrapInner('<span />').children('span'); 681 return addRDFa.call(span, triple); 682 } 683 createCurieAttr(this, 'property', triple.property.value); 684 createSubjectAttr(this, triple.subject); 685 if (hasContent) { 686 if (triple.object.datatype && triple.object.datatype.toString() === rdfXMLLiteral) { 687 this.html(triple.object.value); 688 } else { 689 this.attr('content', triple.object.value); 690 } 691 } 692 lang = getLang(this); 693 if (triple.object.lang) { 694 if (lang !== triple.object.lang) { 695 this.attr('lang', triple.object.lang); 696 if (hasContent) { 697 resetLang(this, lang); 698 } 699 } 700 } else if (triple.object.datatype) { 701 createCurieAttr(this, 'datatype', triple.object.datatype); 702 } else { 703 // the empty datatype ensures that any child elements that might be added won't mess up this triple 704 if (!hasContent) { 705 this.attr('datatype', ''); 706 } 707 // the empty lang ensures that a language won't be assigned to the literal 708 if (lang !== undefined) { 709 this.attr('lang', ''); 710 if (hasContent) { 711 resetLang(this, lang); 712 } 713 } 714 } 715 } 716 } 717 this.parents().andSelf().trigger("rdfChange"); 718 return span; 719 }, 720 721 removeRDFa = function (what) { 722 var span, atts, property, rel, rev, type, 723 ns = this.xmlns(); 724 atts = getAttributes(this).atts; 725 if (what.length) { 726 for (i = 0; i < what.length; i += 1) { 727 removeRDFa.call(this, what[i]); 728 } 729 return this; 730 } 731 hasRelation = atts.rel !== undefined || atts.rev !== undefined; 732 hasRDFa = hasRelation || atts.property !== undefined || atts['typeof'] !== undefined; 733 if (hasRDFa) { 734 if (what.property !== undefined) { 735 if (atts.property !== undefined) { 736 property = removeCurie(atts.property, what.property, { namespaces: ns }); 737 if (property === '') { 738 this.removeAttr('property'); 739 } else { 740 this.attr('property', property); 741 } 742 } 743 if (atts.rel !== undefined) { 744 rel = removeCurie(atts.rel, what.property, { namespaces: ns }); 745 if (rel === '') { 746 this.removeAttr('rel'); 747 } else { 748 this.attr('rel', rel); 749 } 750 } 751 if (atts.rev !== undefined) { 752 rev = removeCurie(atts.rev, what.property, { namespaces: ns }); 753 if (rev === '') { 754 this.removeAttr('rev'); 755 } else { 756 this.attr('rev', rev); 757 } 758 } 759 } 760 if (what.type !== undefined) { 761 if (atts['typeof'] !== undefined) { 762 type = removeCurie(atts['typeof'], what.type, { namespaces: ns }); 763 if (type === '') { 764 this.removeAttr('typeof'); 765 } else { 766 this.attr('typeof', type); 767 } 768 } 769 } 770 if (atts.property === this.attr('property') && atts.rel === this.attr('rel') && atts.rev === this.attr('rev') && atts['typeof'] === this.attr('typeof')) { 771 return removeRDFa.call(this.parent(), what); 772 } 773 } 774 this.parents().andSelf().trigger("rdfChange"); 775 return this; 776 }; 777 778 /** 779 * Creates a {@link jQuery.rdf} object containing the RDF triples parsed from the RDFa found in the current jQuery selection or adds the specified triple as RDFa markup on each member of the current jQuery selection. To create an {@link jQuery.rdf} object, you will usually want to use {@link jQuery#rdf} instead, as this may perform other useful processing (such as of microformats used within the page). 780 * @methodOf jQuery# 781 * @name jQuery#rdfa 782 * @param {jQuery.rdf.triple} [triple] The RDF triple to be added to each item in the jQuery selection. 783 * @returns {jQuery.rdf} 784 * @example 785 * // Extract RDFa markup from all span elements contained inside #main 786 * rdf = $('#main > span').rdfa(); 787 * @example 788 * // Add RDFa markup to a particular element 789 * var span = $('#main > p > span'); 790 * span.rdfa('<> dc:date "2008-10-19"^^xsd:date .'); 791 */ 792 $.fn.rdfa = function (triple) { 793 if (triple === undefined) { 794 var triples = $.map($(this), function (elem) { 795 return rdfa.call($(elem)); 796 }); 797 return $.rdf({ triples: triples }); 798 } else { 799 $(this).each(function () { 800 addRDFa.call($(this), triple); 801 }); 802 return this; 803 } 804 }; 805 806 /** 807 * Removes the specified RDFa markup from each of the items in the current jQuery selection. The input parameter can be either an object or an array of objects. The objects can either have a <code>type</code> property, in which case the specified type is removed from the RDFa provided on the selected elements, or a <code>property</code> property, in which case the specified property is removed from the RDFa provided on the selected elements. 808 * @methodOf jQuery# 809 * @name jQuery#removeRdfa 810 * @param {Object|Object[]} triple The RDFa markup items to be removed 811 * from the items in the jQuery selection. 812 * @returns {jQuery} The original jQuery object. 813 * @example 814 * // To remove a property resource or relation from an element 815 * $('#main > p > a').removeRdfa({ property: "dc:creator" }); 816 * @example 817 * // To remove a type from an element 818 * $('#main >p > a').removeRdfa({ type: "foaf:Person" }); 819 * @example 820 * // To remove multiple triples from an element 821 * $('#main > p > a').removeRdfa([{ property: "foaf:depicts" }, { property: "dc:creator" }]); 822 */ 823 $.fn.removeRdfa = function (triple) { 824 $(this).each(function () { 825 removeRDFa.call($(this), triple); 826 }); 827 return this; 828 }; 829 830 $.rdf.gleaners.push(gleaner); 831 832 })(jQuery); 833