1 /*
  2  * jQuery CURIE @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  */
 10 /**
 11  * @fileOverview XML Namespace processing
 12  * @author <a href="mailto:jeni@jenitennison.com">Jeni Tennison</a>
 13  * @copyright (c) 2008,2009 Jeni Tennison
 14  * @license MIT license (MIT-LICENSE.txt)
 15  * @version 1.0
 16  * @requires jquery.uri.js
 17  */
 18 
 19 /*global jQuery */
 20 (function ($) {
 21 
 22   var 
 23     xmlnsRegex = /\sxmlns(?::([^ =]+))?\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
 24 
 25 /**
 26  * Returns the namespaces declared in the scope of the first selected element, or
 27  * adds a namespace declaration to all selected elements. Pass in no parameters
 28  * to return all namespaces bindings on the first selected element. If only 
 29  * the prefix parameter is specified, this method will return the namespace
 30  * URI that is bound to the specified prefix on the first element in the selection
 31  * If the prefix and uri parameters are both specified, this method will
 32  * add the binding of the specified prefix and namespace URI to all elements
 33  * in the selection.
 34  * @methodOf jQuery#
 35  * @name jQuery#xmlns
 36  * @param {String} [prefix] Restricts the namespaces returned to only the namespace with the specified namespace prefix.
 37  * @param {String|jQuery.uri} [uri] Adds a namespace declaration to the selected elements that maps the specified prefix to the specified namespace.
 38  * @param {Object} [inherited] A map of inherited namespace bindings.
 39  * @returns {Object|jQuery.uri|jQuery}
 40  * @example 
 41  * // Retrieve all of the namespace bindings on the HTML document element
 42  * var nsMap = $('html').xmlns();
 43  * @example
 44  * // Retrieve the namespace URI mapped to the 'dc' prefix on the HTML document element
 45  * var dcNamespace = $('html').xmlns('dc');
 46  * @example
 47  * // Create a namespace declaration that binds the 'dc' prefix to the URI 'http://purl.org/dc/elements/1.1/'
 48  * $('html').xmlns('dc', 'http://purl.org/dc/elements/1.1/');
 49  */
 50   $.fn.xmlns = function (prefix, uri, inherited) {
 51     var 
 52       elem = this.eq(0),
 53       ns = elem.data('xmlns'),
 54       e = elem[0], a, p, i,
 55       decl = prefix ? 'xmlns:' + prefix : 'xmlns',
 56       value,
 57       tag, found = false;
 58     if (uri === undefined) {
 59       if (prefix === undefined) { // get the in-scope declarations on the first element
 60         if (ns === undefined) {
 61           ns = {};
 62           if (e.attributes && e.attributes.getNamedItemNS) {
 63             for (i = 0; i < e.attributes.length; i += 1) {
 64               a = e.attributes[i];
 65               if (/^xmlns(:(.+))?$/.test(a.nodeName)) {
 66                 prefix = /^xmlns(:(.+))?$/.exec(a.nodeName)[2] || '';
 67                 value = a.nodeValue;
 68                 if (prefix === '' || value !== '') {
 69                   ns[prefix] = $.uri(a.nodeValue);
 70                   found = true;
 71                 }
 72               }
 73             }
 74           } else {
 75             tag = /<[^>]+>/.exec(e.outerHTML);
 76             a = xmlnsRegex.exec(tag);
 77             while (a !== null) {
 78               prefix = a[1] || '';
 79               value = a[2] || a[3];
 80               if (prefix === '' || value !== '') {
 81                 ns[prefix] = $.uri(a[2] || a[3]);
 82                 found = true;
 83               }
 84               a = xmlnsRegex.exec(tag);
 85             }
 86             xmlnsRegex.lastIndex = 0;
 87           }
 88           inherited = inherited || (e.parentNode.nodeType === 1 ? elem.parent().xmlns() : {});
 89           ns = found ? $.extend({}, inherited, ns) : inherited;
 90           elem.data('xmlns', ns);
 91         }
 92         return ns;
 93       } else if (typeof prefix === 'object') { // set the prefix mappings defined in the object
 94         for (p in prefix) {
 95           if (typeof prefix[p] === 'string') {
 96             this.xmlns(p, prefix[p]);
 97           }
 98         }
 99         this.find('*').andSelf().removeData('xmlns');
100         return this;
101       } else { // get the in-scope declaration associated with this prefix on the first element
102         if (ns === undefined) {
103           ns = elem.xmlns();
104         }
105         return ns[prefix];
106       }
107     } else { // set
108       this.find('*').andSelf().removeData('xmlns');
109       return this.attr(decl, uri);
110     }
111   };
112 
113 /**
114  * Removes one or more XML namespace bindings from the selected elements.
115  * @methodOf jQuery#
116  * @name jQuery#removeXmlns
117  * @param {String|Object|String[]} prefix The prefix(es) of the XML namespace bindings that are to be removed from the selected elements.
118  * @returns {jQuery} The original jQuery object.
119  * @example
120  * // Remove the foaf namespace declaration from the body element:
121  * $('body').removeXmlns('foaf');
122  * @example
123  * // Remove the foo and bar namespace declarations from all h2 elements
124  * $('h2').removeXmlns(['foo', 'bar']);
125  * @example
126  * // Remove the foo and bar namespace declarations from all h2 elements
127  * var namespaces = { foo : 'http://www.example.org/foo', bar : 'http://www.example.org/bar' };
128  * $('h2').removeXmlns(namespaces);
129  */
130   $.fn.removeXmlns = function (prefix) {
131     var decl, p, i;
132     if (typeof prefix === 'object') {
133       if (prefix.length === undefined) { // assume an object representing namespaces
134         for (p in prefix) {
135           if (typeof prefix[p] === 'string') {
136             this.removeXmlns(p);
137           }
138         }
139       } else { // it's an array
140         for (i = 0; i < prefix.length; i += 1) {
141           this.removeXmlns(prefix[i]);
142         }
143       }
144     } else {
145       decl = prefix ? 'xmlns:' + prefix : 'xmlns';
146       this.removeAttr(decl);
147     }
148     this.find('*').andSelf().removeData('xmlns');
149     return this;
150   };
151 
152   $.fn.qname = function (name) {
153     var m, prefix, namespace;
154     if (name === undefined) {
155       if (this[0].outerHTML === undefined) {
156         name = this[0].nodeName.toLowerCase();
157       } else {
158         name = /<([^ >]+)/.exec(this[0].outerHTML)[1].toLowerCase();
159       }
160     }
161     if (name === '?xml:namespace') {
162       // there's a prefix on the name, but we can't get at it
163       throw "XMLinHTML: Unable to get the prefix to resolve the name of this element";
164     }
165     m = /^(([^:]+):)?([^:]+)$/.exec(name);
166     prefix = m[2] || '';
167     namespace = this.xmlns(prefix);
168     if (namespace === undefined && prefix !== '') {
169       throw "MalformedQName: The prefix " + prefix + " is not declared";
170     }
171     return {
172       namespace: namespace,
173       localPart: m[3],
174       prefix: prefix,
175       name: name
176     };
177   };
178 
179 })(jQuery);
180