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