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     },
 33 
 34     rdfXMLLiteral = ns.rdf + 'XMLLiteral',
 35 
 36     rdfaCurieDefaults = $.fn.curie.defaults,
 37 
 38     attRegex = /\s([^ =]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^ >]+))/g,
 39 
 40     docResource = $.rdf.resource('<>'),
 41 
 42     parseEntities = function (string) {
 43       var result = "", m, entity;
 44       if (!/&/.test(string)) {
 45          return string;
 46       }
 47       while (string.length > 0) {
 48         m = /([^&]*)(&([^;]+);)(.*)/g.exec(string);
 49         if (m === null) {
 50           result += string;
 51           break;
 52         }
 53         result += m[1];
 54         entity = m[3];
 55         string = m[4];
 56         if (entity.charAt(0) === '#') {
 57           if (entity.charAt(1) === 'x') {
 58               result += String.fromCharCode(parseInt(entity.substring(2), 16));
 59           } else {
 60               result += String.fromCharCode(parseInt(entity.substring(1), 10));
 61           }
 62         } else {
 63           switch(entity) {
 64             case 'amp':
 65               result += '&';
 66               break;
 67             case 'nbsp':
 68               result += String.fromCharCode(160);
 69               break;
 70             case 'quot':
 71               result += '"';
 72               break;
 73             case 'apos':
 74               result += "'";
 75               break;
 76             default:
 77               result += '&' + entity + ';';
 78           }
 79         }
 80       }
 81       return result;
 82     },
 83 
 84     getAttributes = function (elem) {
 85       var i, e, a, tag, name, value, attMap, prefix,
 86         ns = {}, atts = {};
 87       e = elem[0];
 88       ns[':length'] = 0;
 89       if (e.attributes && e.attributes.getNamedItemNS) {
 90         attMap = e.attributes;
 91         for (i = 0; i < attMap.length; i += 1) {
 92           a = attMap[i];
 93           if (/^xmlns(:(.+))?$/.test(a.nodeName) && a.nodeValue !== '') {
 94             prefix = /^xmlns(:(.+))?$/.exec(a.nodeName)[2] || '';
 95             ns[prefix] = $.uri(a.nodeValue);
 96             ns[':length'] += 1;
 97           } else if (/rel|rev|lang|xml:lang/.test(a.nodeName)) {
 98             atts[a.nodeName] = a.nodeValue === '' ? undefined : a.nodeValue;
 99           } else if (/about|href|src|resource|property|typeof|content|datatype/.test(a.nodeName)) {
100             atts[a.nodeName] = a.nodeValue === null ? undefined : a.nodeValue;
101           }
102         }
103       } else {
104         tag = /<[^>]+>/.exec(e.outerHTML);
105         a = attRegex.exec(tag);
106         while (a !== null) {
107           name = a[1];
108           value = a[2] || a[3] || a[4];
109           if (/^xmlns/.test(name) && name !== 'xmlns:' && value !== '') {
110             prefix = /^xmlns(:(.+))?$/.exec(name)[2] || '';
111             ns[prefix] = $.uri(value);
112             ns[':length'] += 1;
113           } else if (/about|href|src|resource|property|typeof|content|datatype|rel|rev|lang|xml:lang/.test(name)) {
114             atts[name] = parseEntities(value);
115           }
116           a = attRegex.exec(tag);
117         }
118         attRegex.lastIndex = 0;
119       }
120       return { atts: atts, namespaces: ns };
121     },
122 
123     getAttribute = function (elem, attr) {
124       var val = elem[0].getAttribute(attr);
125       if (attr === 'rev' || attr === 'rel' || attr === 'lang' || attr === 'xml:lang') {
126         val = val === '' ? undefined : val;
127       }
128       return val === null ? undefined : val;
129     },
130 
131     resourceFromUri = function (uri) {
132       return $.rdf.resource(uri);
133     },
134 
135     resourceFromCurie = function (curie, elem, options) {
136       if (curie.substring(0, 2) === '_:') {
137         return $.rdf.blank(curie);
138       } else {
139         try {
140           return resourceFromUri($.curie(curie, options));
141         } catch (e) {
142           return undefined;
143         }
144       }
145     },
146 
147     resourceFromSafeCurie = function (safeCurie, elem, options) {
148       var m = /^\[([^\]]+)\]$/.exec(safeCurie),
149         base = options.base || elem.base();
150       return m ? resourceFromCurie(m[1], elem, options) : resourceFromUri($.uri(safeCurie, base));
151     },
152 
153     resourcesFromCuries = function (curies, elem, options) {
154       var i, resource, resources = [];
155       curies = curies && curies.split ? curies.split(/[ \t\n\r\x0C]+/g) : [];
156       for (i = 0; i < curies.length; i += 1) {
157         if (curies[i] !== '') {
158           resource = resourceFromCurie(curies[i], elem, options);
159           if (resource !== undefined) {
160             resources.push(resource);
161           }
162         }
163       }
164       return resources;
165     },
166 
167     removeCurie = function (curies, resource, options) {
168       var i, r, newCuries = [];
169       resource = resource.type === 'uri' ? resource : $.rdf.resource(resource, options);
170       curies = curies && curies.split ? curies.split(/\s+/) : [];
171       for (i = 0; i < curies.length; i += 1) {
172         if (curies[i] !== '') {
173           r = resourceFromCurie(curies[i], null, options);
174           if (r !== resource) {
175             newCuries.push(curies[i]);
176           }
177         }
178       }
179       return newCuries.reverse().join(' ');
180     },
181 
182     getObjectResource = function (elem, context, relation) {
183       var r, resource, atts, curieOptions;
184       context = context || {};
185       atts = context.atts || getAttributes(elem).atts;
186       r = relation === undefined ? atts.rel !== undefined || atts.rev !== undefined : relation;
187       resource = atts.resource;
188       resource = resource === undefined ? atts.href : resource;
189       if (resource === undefined) {
190         resource = r ? $.rdf.blank('[]') : resource;
191       } else {
192         curieOptions = context.curieOptions || $.extend({}, rdfaCurieDefaults, { namespaces: elem.xmlns() });
193         resource = resourceFromSafeCurie(resource, elem, curieOptions);
194       }
195       return resource;
196     },
197 
198     getSubject = function (elem, context, relation) {
199       var r, atts, curieOptions, subject, skip = false;
200       context = context || {};
201       atts = context.atts || getAttributes(elem).atts;
202       curieOptions = context.curieOptions || $.extend({}, rdfaCurieDefaults, { namespaces: elem.xmlns(), base: elem.base() });
203       r = relation === undefined ? atts.rel !== undefined || atts.rev !== undefined : relation;
204       if (atts.about !== undefined) {
205         subject = resourceFromSafeCurie(atts.about, elem, curieOptions);
206       }
207       if (subject === undefined && atts.src !== undefined) {
208         subject = resourceFromSafeCurie(atts.src, elem, curieOptions);
209       }
210       if (!r && subject === undefined && atts.resource !== undefined) {
211         subject = resourceFromSafeCurie(atts.resource, elem, curieOptions);
212       }
213       if (!r && subject === undefined && atts.href !== undefined) {
214         subject = resourceFromSafeCurie(atts.href, elem, curieOptions);
215       }
216       if (subject === undefined) {
217         if (/head|body/i.test(elem[0].nodeName)) {
218           subject = docResource;
219         } else if (atts['typeof'] !== undefined) {
220           subject = $.rdf.blank('[]');
221         } else if (elem[0].parentNode.nodeType === 1) {
222           subject = context.object || getObjectResource(elem.parent()) || getSubject(elem.parent()).subject;
223           skip = !r && atts.property === undefined;
224         } else {
225           subject = docResource;
226         }
227       }
228       return { subject: subject, skip: skip };
229     },
230 
231     getLang = function (elem, context) {
232       var lang;
233       context = context || {};
234       if (context.atts) {
235         lang = context.atts.lang;
236         lang = lang || context.atts['xml:lang'];
237       } else {
238         lang = elem[0].getAttribute('lang');
239         lang = (lang === null || lang === '') ? elem[0].getAttribute('xml:lang') : lang;
240         lang = (lang === null || lang === '') ? undefined : lang;
241       }
242       if (lang === undefined) {
243         if (context.lang) {
244           lang = context.lang;
245         } else {
246           if (elem[0].parentNode.nodeType === 1) {
247             lang = getLang(elem.parent());
248           }
249         }
250       }
251       return lang;
252     },
253 
254     entity = function (c) {
255       switch (c) {
256       case '<':
257         return '<';
258       case '"':
259         return '"';
260       case '&':
261         return '&';
262       }
263     },
264 
265     serialize = function (elem, ignoreNs) {
266       var i, string = '', atts, a, name, ns, tag;
267       elem.contents().each(function () {
268         var j = $(this),
269           e = j[0];
270         if (e.nodeType === 1) { // tests whether the node is an element
271           name = e.nodeName.toLowerCase();
272           string += '<' + name;
273           if (e.outerHTML) {
274             tag = /<[^>]+>/.exec(e.outerHTML);
275             a = attRegex.exec(tag);
276             while (a !== null) {
277               if (!/^jQuery/.test(a[1])) {
278                 string += ' ' + a[1] + '=';
279                 string += a[2] ? a[3] : '"' + a[1] + '"';
280               }
281               a = attRegex.exec(tag);
282             }
283             attRegex.lastIndex = 0;
284           } else {
285             atts = e.attributes;
286             for (i = 0; i < atts.length; i += 1) {
287               a = atts.item(i);
288               string += ' ' + a.nodeName + '="';
289               string += a.nodeValue.replace(/[<"&]/g, entity);
290               string += '"';
291             }
292           }
293           if (!ignoreNs) {
294             ns = j.xmlns('');
295             if (ns !== undefined && j.attr('xmlns') === undefined) {
296               string += ' xmlns="' + ns + '"';
297             }
298           }
299           string += '>';
300           string += serialize(j, true);
301           string += '</' + name + '>';
302         } else if (e.nodeType === 8) { // tests whether the node is a comment
303           string += '<!--';
304           string += e.nodeValue;
305           string += '-->';
306         } else {
307           string += e.nodeValue;
308         }
309       });
310       return string;
311     },
312 
313     rdfa = function (context) {
314       var i, subject, resource, lang, datatype, content,
315         types, object, triple, parent,
316         properties, rels, revs,
317         forward, backward,
318         triples = [],
319         attsAndNs, atts, namespaces, ns,
320         children = this.children();
321       context = context || {};
322       forward = context.forward || [];
323       backward = context.backward || [];
324       attsAndNs = getAttributes(this);
325       atts = attsAndNs.atts;
326       context.atts = atts;
327       namespaces = context.namespaces || this.xmlns();
328       if (attsAndNs.namespaces[':length'] > 0) {
329         namespaces = $.extend({}, namespaces);
330         for (ns in attsAndNs.namespaces) {
331           if (ns !== ':length') {
332             namespaces[ns] = attsAndNs.namespaces[ns];
333           }
334         }
335       }
336       context.curieOptions = $.extend({}, rdfaCurieDefaults, { namespaces: namespaces, base: this.base() });
337       subject = getSubject(this, context);
338       lang = getLang(this, context);
339       if (subject.skip) {
340         rels = context.forward;
341         revs = context.backward;
342         subject = context.subject;
343         resource = context.object;
344       } else {
345         subject = subject.subject;
346         if (forward.length > 0 || backward.length > 0) {
347           parent = context.subject || getSubject(this.parent()).subject;
348           for (i = 0; i < forward.length; i += 1) {
349             triple = $.rdf.triple(parent, forward[i], subject, { source: this[0] });
350             triples.push(triple);
351           }
352           for (i = 0; i < backward.length; i += 1) {
353             triple = $.rdf.triple(subject, backward[i], parent, { source: this[0] });
354             triples.push(triple);
355           }
356         }
357         resource = getObjectResource(this, context);
358         types = resourcesFromCuries(atts['typeof'], this, context.curieOptions);
359         for (i = 0; i < types.length; i += 1) {
360           triple = $.rdf.triple(subject, $.rdf.type, types[i], { source: this[0] });
361           triples.push(triple);
362         }
363         properties = resourcesFromCuries(atts.property, this, context.curieOptions);
364         if (properties.length > 0) {
365           datatype = atts.datatype;
366           content = atts.content;
367           if (datatype !== undefined && datatype !== '') {
368             datatype = $.curie(datatype, context.curieOptions);
369             if (datatype === rdfXMLLiteral) {
370               object = $.rdf.literal(serialize(this), { datatype: rdfXMLLiteral });
371             } else if (content !== undefined) {
372               object = $.rdf.literal(content, { datatype: datatype });
373             } else {
374               object = $.rdf.literal(this.text(), { datatype: datatype });
375             }
376           } else if (content !== undefined) {
377             if (lang === undefined) {
378               object = $.rdf.literal('"' + content + '"');
379             } else {
380               object = $.rdf.literal(content, { lang: lang });
381             }
382           } else if (children.length === 0 ||
383                      datatype === '') {
384             lang = getLang(this, context);
385             if (lang === undefined) {
386               object = $.rdf.literal('"' + this.text() + '"');
387             } else {
388               object = $.rdf.literal(this.text(), { lang: lang });
389             }
390           } else {
391             object = $.rdf.literal(serialize(this), { datatype: rdfXMLLiteral });
392           }
393           for (i = 0; i < properties.length; i += 1) {
394             triple = $.rdf.triple(subject, properties[i], object, { source: this[0] });
395             triples.push(triple);
396           }
397         }
398         rels = resourcesFromCuries(atts.rel, this, context.curieOptions);
399         revs = resourcesFromCuries(atts.rev, this, context.curieOptions);
400         if (atts.resource !== undefined || atts.href !== undefined) {
401           // make the triples immediately
402           if (rels !== undefined) {
403             for (i = 0; i < rels.length; i += 1) {
404               triple = $.rdf.triple(subject, rels[i], resource, { source: this[0] });
405               triples.push(triple);
406             }
407           }
408           rels = [];
409           if (revs !== undefined) {
410             for (i = 0; i < revs.length; i += 1) {
411               triple = $.rdf.triple(resource, revs[i], subject, { source: this[0] });
412               triples.push(triple);
413             }
414           }
415           revs = [];
416         }
417       }
418       children.each(function () {
419         triples = triples.concat(rdfa.call($(this), { forward: rels, backward: revs, subject: subject, object: resource || subject, lang: lang, namespaces: namespaces }));
420       });
421       return triples;
422     },
423 
424     gleaner = function (options) {
425       var type, atts;
426       if (options && options.about !== undefined) {
427         atts = getAttributes(this).atts;
428         if (options.about === null) {
429           return atts.property !== undefined ||
430                  atts.rel !== undefined ||
431                  atts.rev !== undefined ||
432                  atts['typeof'] !== undefined;
433         } else {
434           return getSubject(this, {atts: atts}).subject.value === options.about;
435         }
436       } else if (options && options.type !== undefined) {
437         type = getAttribute(this, 'typeof');
438         if (type !== undefined) {
439           return options.type === null ? true : this.curie(type) === options.type;
440         }
441         return false;
442       } else {
443         return rdfa.call(this);
444       }
445     },
446 
447     nsCounter = 1,
448 
449     createCurieAttr = function (elem, attr, uri) {
450       var m, curie, value;
451       try {
452         curie = elem.createCurie(uri);
453       } catch (e) {
454         if (uri.toString() === rdfXMLLiteral) {
455           elem.attr('xmlns:rdf', ns.rdf);
456           curie = 'rdf:XMLLiteral';
457         } else {
458           m = /^(.+[\/#])([^#]+)$/.exec(uri);
459           elem.attr('xmlns:ns' + nsCounter, m[1]);
460           curie = 'ns' + nsCounter + ':' + m[2];
461           nsCounter += 1;
462         }
463       }
464       value = getAttribute(elem, attr);
465       if (value !== undefined) {
466         if ($.inArray(curie, value.split(/\s+/)) === -1) {
467           elem.attr(attr, value + ' ' + curie);
468         }
469       } else {
470         elem.attr(attr, curie);
471       }
472     },
473 
474     createResourceAttr = function (elem, attr, resource) {
475       var ref;
476       if (resource.type === 'bnode') {
477         ref = '[_:' + resource.id + ']';
478       } else {
479         ref = $(elem).base().relative(resource.value);
480       }
481       elem.attr(attr, ref);
482     },
483 
484     createSubjectAttr = function (elem, subject) {
485       var s = getSubject(elem).subject;
486       if (subject !== s) {
487         createResourceAttr(elem, 'about', subject);
488       }
489       elem.removeData('rdfa.subject');
490     },
491 
492     createObjectAttr = function (elem, object) {
493       var o = getObjectResource(elem);
494       if (object !== o) {
495         createResourceAttr(elem, 'resource', object);
496       }
497       elem.removeData('rdfa.objectResource');
498     },
499 
500     resetLang = function (elem, lang) {
501       elem.wrapInner('<span></span>')
502         .children('span')
503         .attr('lang', lang);
504       return elem;
505     },
506 
507     addRDFa = function (triple) {
508       var hasContent, hasRelation, hasRDFa, overridableObject, span,
509         subject, sameSubject,
510         object, sameObject,
511         lang, content,
512         i, atts,
513         ns = this.xmlns();
514       span = this;
515       atts = getAttributes(this).atts;
516       if (typeof triple === 'string') {
517         triple = $.rdf.triple(triple, { namespaces: ns, base: this.base() });
518       } else if (triple.rdfquery) {
519         addRDFa.call(this, triple.sources().get(0));
520         return this;
521       } else if (triple.length) {
522         for (i = 0; i < triple.length; i += 1) {
523           addRDFa.call(this, triple[i]);
524         }
525         return this;
526       }
527       hasRelation = atts.rel !== undefined || atts.rev !== undefined;
528       hasRDFa = hasRelation || atts.property !== undefined || atts['typeof'] !== undefined;
529       if (triple.object.type !== 'literal') {
530         subject = getSubject(this, {atts: atts}, true).subject;
531         object = getObjectResource(this, {atts: atts}, true);
532         overridableObject = !hasRDFa && atts.resource === undefined;
533         sameSubject = subject === triple.subject;
534         sameObject = object === triple.object;
535         if (triple.property === $.rdf.type) {
536           if (sameSubject) {
537             createCurieAttr(this, 'typeof', triple.object.value);
538           } else if (hasRDFa) {
539             span = this.wrapInner('<span />').children('span');
540             createCurieAttr(span, 'typeof', triple.object.value);
541             if (object !== triple.subject) {
542               createSubjectAttr(span, triple.subject);
543             }
544           } else {
545             createCurieAttr(this, 'typeof', triple.object.value);
546             createSubjectAttr(this, triple.subject);
547           }
548         } else if (sameSubject) {
549           // use a rel
550           if (sameObject) {
551             createCurieAttr(this, 'rel', triple.property.value);
552           } else if (overridableObject || !hasRDFa) {
553             createCurieAttr(this, 'rel', triple.property.value);
554             createObjectAttr(this, triple.object);
555           } else {
556             span = this.wrap('<span />').parent();
557             createCurieAttr(span, 'rev', triple.property.value);
558             createSubjectAttr(span, triple.object);
559           }
560         } else if (subject === triple.object) {
561           if (object === triple.subject) {
562             // use a rev
563             createCurieAttr(this, 'rev', triple.property.value);
564           } else if (overridableObject || !hasRDFa) {
565             createCurieAttr(this, 'rev', triple.property.value);
566             createObjectAttr(this, triple.subject);
567           } else {
568             // wrap in a span with a rel
569             span = this.wrap('<span />').parent();
570             createCurieAttr(span, 'rel', triple.property.value);
571             createSubjectAttr(span, triple.subject);
572           }
573         } else if (sameObject) {
574           if (hasRDFa) {
575             // use a rev on a nested span
576             span = this.wrapInner('<span />').children('span');
577             createCurieAttr(span, 'rev', triple.property.value);
578             createObjectAttr(span, triple.subject);
579             span = span.wrapInner('<span />').children('span');
580             createSubjectAttr(span, triple.object);
581             span = this;
582           } else {
583             createSubjectAttr(this, triple.subject);
584             createCurieAttr(this, 'rel', triple.property.value);
585           }
586         } else if (object === triple.subject) {
587           if (hasRDFa) {
588             // wrap the contents in a span and use a rel
589             span = this.wrapInner('<span />').children('span');
590             createCurieAttr(span, 'rel', this.property.value);
591             createObjectAttr(span, triple.object);
592             span = span.wrapInner('<span />').children('span');
593             createSubjectAttr(span, object);
594             span = this;
595           } else {
596             // use a rev on this element
597             createSubjectAttr(this, triple.object);
598             createCurieAttr(this, 'rev', triple.property.value);
599           }
600         } else if (hasRDFa) {
601           span = this.wrapInner('<span />').children('span');
602           createCurieAttr(span, 'rel', triple.property.value);
603           createSubjectAttr(span, triple.subject);
604           createObjectAttr(span, triple.object);
605           if (span.children('*').length > 0) {
606             span = this.wrapInner('<span />').children('span');
607             createSubjectAttr(span, subject);
608           }
609           span = this;
610         } else {
611           createCurieAttr(span, 'rel', triple.property.value);
612           createSubjectAttr(this, triple.subject);
613           createObjectAttr(this, triple.object);
614           if (this.children('*').length > 0) {
615             span = this.wrapInner('<span />').children('span');
616             createSubjectAttr(span, subject);
617             span = this;
618           }
619         }
620       } else {
621         subject = getSubject(this, {atts: atts}).subject;
622         object = getObjectResource(this, {atts: atts});
623         sameSubject = subject === triple.subject;
624         hasContent = this.text() !== triple.object.value;
625         if (atts.property !== undefined) {
626           content = atts.content;
627           sameObject = content !== undefined ? content === triple.object.value : !hasContent;
628           if (sameSubject && sameObject) {
629             createCurieAttr(this, 'property', triple.property.value);
630           } else {
631             span = this.wrapInner('<span />').children('span');
632             return addRDFa.call(span, triple);
633           }
634         } else {
635           if (object === triple.subject) {
636             span = this.wrapInner('<span />').children('span');
637             return addRDFa.call(span, triple);
638           }
639           createCurieAttr(this, 'property', triple.property.value);
640           createSubjectAttr(this, triple.subject);
641           if (hasContent) {
642             if (triple.object.datatype && triple.object.datatype.toString() === rdfXMLLiteral) {
643               this.html(triple.object.value);
644             } else {
645               this.attr('content', triple.object.value);
646             }
647           }
648           lang = getLang(this);
649           if (triple.object.lang) {
650             if (lang !== triple.object.lang) {
651               this.attr('lang', triple.object.lang);
652               if (hasContent) {
653                 resetLang(this, lang);
654               }
655             }
656           } else if (triple.object.datatype) {
657             createCurieAttr(this, 'datatype', triple.object.datatype);
658           } else {
659             // the empty datatype ensures that any child elements that might be added won't mess up this triple
660             if (!hasContent) {
661               this.attr('datatype', '');
662             }
663             // the empty lang ensures that a language won't be assigned to the literal
664             if (lang !== undefined) {
665               this.attr('lang', '');
666               if (hasContent) {
667                 resetLang(this, lang);
668               }
669             }
670           }
671         }
672       }
673       this.parents().andSelf().trigger("rdfChange");
674       return span;
675     },
676 
677     removeRDFa = function (what) {
678       var span, atts, property, rel, rev, type,
679         ns = this.xmlns();
680       atts = getAttributes(this).atts;
681       if (what.length) {
682         for (i = 0; i < what.length; i += 1) {
683           removeRDFa.call(this, what[i]);
684         }
685         return this;
686       }
687       hasRelation = atts.rel !== undefined || atts.rev !== undefined;
688       hasRDFa = hasRelation || atts.property !== undefined || atts['typeof'] !== undefined;
689       if (hasRDFa) {
690         if (what.property !== undefined) {
691           if (atts.property !== undefined) {
692             property = removeCurie(atts.property, what.property, { namespaces: ns });
693             if (property === '') {
694               this.removeAttr('property');
695             } else {
696               this.attr('property', property);
697             }
698           }
699           if (atts.rel !== undefined) {
700             rel = removeCurie(atts.rel, what.property, { namespaces: ns });
701             if (rel === '') {
702               this.removeAttr('rel');
703             } else {
704               this.attr('rel', rel);
705             }
706           }
707           if (atts.rev !== undefined) {
708             rev = removeCurie(atts.rev, what.property, { namespaces: ns });
709             if (rev === '') {
710               this.removeAttr('rev');
711             } else {
712               this.attr('rev', rev);
713             }
714           }
715         }
716         if (what.type !== undefined) {
717           if (atts['typeof'] !== undefined) {
718             type = removeCurie(atts['typeof'], what.type, { namespaces: ns });
719             if (type === '') {
720               this.removeAttr('typeof');
721             } else {
722               this.attr('typeof', type);
723             }
724           }
725         }
726         if (atts.property === this.attr('property') && atts.rel === this.attr('rel') && atts.rev === this.attr('rev') && atts['typeof'] === this.attr('typeof')) {
727           return removeRDFa.call(this.parent(), what);
728         }
729       }
730       this.parents().andSelf().trigger("rdfChange");
731       return this;
732     };
733 
734   /**
735    * 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).
736    * @methodOf jQuery#
737    * @name jQuery#rdfa
738    * @param {jQuery.rdf.triple} [triple] The RDF triple to be added to each item in the jQuery selection. 
739    * @returns {jQuery.rdf}
740    * @example
741    * // Extract RDFa markup from all span elements contained inside #main
742    * rdf = $('#main > span').rdfa();
743    * @example
744    * // Add RDFa markup to a particular element
745    *  var span = $('#main > p > span');
746    *  span.rdfa('<> dc:date "2008-10-19"^^xsd:date .');
747    */
748   $.fn.rdfa = function (triple) {
749     if (triple === undefined) {
750       var triples = $.map($(this), function (elem) {
751         return rdfa.call($(elem));
752       });
753       return $.rdf({ triples: triples });
754     } else {
755       $(this).each(function () {
756         addRDFa.call($(this), triple);
757       });
758       return this;
759     }
760   };
761 
762   /**
763    * 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.
764    * @methodOf jQuery#
765    * @name jQuery#removeRdfa
766    * @param {Object|Object[]} triple The RDFa markup items to be removed
767    * from the items in the jQuery selection.
768    * @returns {jQuery} The original jQuery object.
769    * @example 
770    * // To remove a property resource or relation from an element 
771    * $('#main > p > a').removeRdfa({ property: "dc:creator" });
772    * @example
773    * // To remove a type from an element
774    * $('#main >p > a').removeRdfa({ type: "foaf:Person" });
775    * @example
776    * // To remove multiple triples from an element
777    * $('#main > p > a').removeRdfa([{ property: "foaf:depicts" }, { property: "dc:creator" }]);
778    */
779   $.fn.removeRdfa = function (triple) {
780     $(this).each(function () {
781       removeRDFa.call($(this), triple);
782     });
783     return this;
784   };
785 
786   $.rdf.gleaners.push(gleaner);
787 
788 })(jQuery);
789