+//
+// jquery.xmlns.js: xml-namespace selector support for jQuery
+//
+// This plugin modifies the jQuery tag and attribute selectors to
+// support optional namespace specifiers as defined in CSS 3:
+//
+// $("elem") - matches 'elem' nodes in the default namespace
+// $("|elem") - matches 'elem' nodes that don't have a namespace
+// $("NS|elem") - matches 'elem' nodes in declared namespace 'NS'
+// $("*|elem") - matches 'elem' nodes in any namespace
+// $("NS|*") - matches any nodes in declared namespace 'NS'
+//
+// A similar synax is also supported for attribute selectors, but note
+// that the default namespace does *not* apply to attributes - a missing
+// or empty namespace selector selects only attributes with no namespace.
+//
+// In a slight break from the W3C standards, and with a nod to ease of
+// implementation, the empty namespace URI is treated as equivalent to
+// an unspecified namespace. Plenty of browsers seem to make the same
+// assumption...
+//
+// Namespace declarations live in the $.xmlns object, which is a simple
+// mapping from namespace ids to namespace URIs. The default namespace
+// is determined by the value associated with the empty string.
+//
+// $.xmlns.D = "DAV:"
+// $.xmlns.FOO = "http://www.foo.com/xmlns/foobar"
+// $.xmlns[""] = "http://www.example.com/new/default/namespace/"
+//
+// Unfortunately this is a global setting - I can't find a way to do
+// query-object-specific namespaces since the jQuery selector machinery
+// is stateless. However, you can use the 'xmlns' function to push and
+// pop namespace delcarations with ease:
+//
+// $().xmlns({D:"DAV:"}) // pushes the DAV: namespace
+// $().xmlns("DAV:") // makes DAV: the default namespace
+// $().xmlns(false) // pops the namespace we just pushed
+// $().xmlns(false) // pops again, returning to defaults
+//
+// To execute this as a kind of "transaction", pass a function as the
+// second argument. It will be executed in the context of the current
+// jQuery object:
+//
+// $().xmlns("DAV:",function() {
+// // The default namespace is DAV: within this function,
+// // but it is reset to the previous value on exit.
+// return this.find("response").each(...);
+// }).find("div")
+//
+// If you pass a string as a function, it will be executed against the
+// current jQuery object using find(); i.e. the following will find all
+// "href" elements in the "DAV:" namespace:
+//
+// $().xmlns("DAV:","href")
+//
+//
+// And finally, the legal stuff:
+//
+// Copyright (c) 2009, Ryan Kelly.
+// TAG and ATTR functions derived from jQuery's selector.js.
+// Dual licensed under the MIT and GPL licenses.
+// http://docs.jquery.com/License
+//
+
+(function($) {
+
+// Some common default namespaces, that are treated specially by browsers.
+//
+var default_xmlns = {
+ "xml": "http://www.w3.org/XML/1998/namespace",
+ "xmlns": "http://www.w3.org/2000/xmlns/",
+ "html": "http://www.w3.org/1999/xhtml/"
+};
+
+
+// A reverse mapping for common namespace prefixes.
+//
+var default_xmlns_rev = {}
+for(var k in default_xmlns) {
+ default_xmlns_rev[default_xmlns[k]] = k;
+}
+
+
+// $.xmlns is a mapping from namespace identifiers to namespace URIs.
+// The default default-namespace is "*", and we provide some additional
+// defaults that are specified in the XML Namespaces standard.
+//
+$.extend({xmlns: $.extend({},default_xmlns,{"":"*"})});
+
+
+// jQuery method to push/pop namespace declarations.
+//
+// If a single argument is specified:
+// * if it's a mapping, push those namespaces onto the stack
+// * if it's a string, push that as the default namespace
+// * if it evaluates to false, pop the latest namespace
+//
+// If two arguments are specified, the second is executed "transactionally"
+// using the namespace declarations found in the first. It can be either a
+// a selector string (in which case it is passed to this.find()) or a function
+// (in which case it is called in the context of the current jQuery object).
+// The given namespace mapping is automatically pushed before executing and
+// popped afterwards.
+//
+var xmlns_stack = [];
+$.fn.extend({xmlns: function(nsmap,func) {
+ if(typeof nsmap == "string") {
+ nsmap = {"":nsmap};
+ }
+ if(nsmap) {
+ xmlns_stack.push($.xmlns);
+ $.xmlns = $.extend({},$.xmlns,nsmap);
+ if(func !== undefined) {
+ if(typeof func == "string") {
+ return this.find(func).xmlns(undefined)
+ } else {
+ var self = this;
+ try {
+ self = func.call(this);
+ if(!self) {
+ self = this;
+ }
+ } finally {
+ self.xmlns(undefined);
+ }
+ return self
+ }
+ } else {
+ return this;
+ }
+ } else {
+ $.xmlns = (xmlns_stack ? xmlns_stack.pop() : {});
+ return this;
+ }
+}});
+
+
+// Convert a namespace prefix into a namespace URI, based
+// on the delcarations made in $.xmlns.
+//
+var getNamespaceURI = function(id) {
+ // No namespace id, use the default.
+ if(!id) {
+ return $.xmlns[""];
+ }
+ // Strip the pipe character from the specifier
+ id = id.substr(0,id.length-1);
+ // Certain special namespaces aren't mapped to a URI
+ if(id == "" || id == "*") {
+ return id;
+ }
+ var ns = $.xmlns[id];
+ if(typeof(ns) == "undefined") {
+ throw "Syntax error, undefined namespace prefix '" + id + "'";
+ }
+ return ns;
+};
+
+
+// Update the regex used by $.expr to parse selector components for a
+// particular type of selector (e.g. "TAG" or "ATTR").
+//
+// This logic is taken straight from the jQuery/Sizzle sources.
+//
+var setExprMatchRegex = function(type,regex) {
+ $.expr.match[type] = new RegExp(regex.source + /(?![^\[]*\])(?![^\(]*\))/.source);
+ if($.expr.leftMatch) {
+ $.expr.leftMatch[type] = new RegExp(/(^(?:.|\r|\n)*?)/.source + $.expr.match[type].source.replace(/\\(\d+)/g, function(all, num){
+ return "\\" + (num - 0 + 1);
+ }));
+ }
+}
+
+
+
+// Modify the TAG match regexp to include optional namespace selector.
+// This is basically (namespace|)?(tagname).
+//
+setExprMatchRegex("TAG",/^((?:((?:[\w\u00c0-\uFFFF\*_-]*\|)?)((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)))/);
+
+
+// Perform some capability-testing.
+//
+var div = document.createElement("div");
+
+// Sometimes getElementsByTagName("*") will return comment nodes,
+// which we will have to remove from the results.
+//
+var gebtn_yields_comments = false;
+div.appendChild(document.createComment(""));
+if(div.getElementsByTagName("*").length > 0) {
+ gebtn_yields_comments = true;
+}
+
+// Some browsers return node.localName in upper case, some in lower case.
+//
+var localname_is_uppercase = true;
+if(div.localName && div.localName == "div") {
+ localname_is_uppercase = false;
+}
+
+// Allow the testing div to be garbage-collected.
+//
+div = null;
+
+
+// Modify the TAG find function to account for a namespace selector.
+//
+$.expr.find.TAG = function(match,context,isXML) {
+ var ns = getNamespaceURI(match[2]);
+ var ln = match[3];
+ var res;
+ if(typeof context.getElementsByTagNameNS != "undefined") {
+ // Easy case - we have getElementsByTagNameNS
+ res = context.getElementsByTagNameNS(ns,ln);
+ } else if(typeof context.selectNodes != "undefined") {
+ // Use xpath if possible (not available on HTML DOM nodes in IE)
+ if(context.ownerDocument) {
+ context.ownerDocument.setProperty("SelectionLanguage","XPath");
+ } else {
+ context.setProperty("SelectionLanguage","XPath");
+ }
+ var predicate = "";
+ if(ns != "*") {
+ if(ln != "*") {
+ predicate="namespace-uri()='"+ns+"' and local-name()='"+ln+"'";
+ } else {
+ predicate="namespace-uri()='"+ns+"'";
+ }
+ } else {
+ if(ln != "*") {
+ predicate="local-name()='"+ln+"'";
+ }
+ }
+ if(predicate) {
+ res = context.selectNodes("descendant-or-self::*["+predicate+"]");
+ } else {
+ res = context.selectNodes("descendant-or-self::*");
+ }
+ } else {
+ // Otherwise, we need to simulate using getElementsByTagName
+ res = context.getElementsByTagName(ln);
+ if(gebtn_yields_comments && ln == "*") {
+ var tmp = [];
+ for(var i=0; res[i]; i++) {
+ if(res[i].nodeType == 1) {
+ tmp.push(res[i]);
+ }
+ }
+ res = tmp;
+ }
+ if(res && ns != "*") {
+ var tmp = [];
+ for(var i=0; res[i]; i++) {
+ if(res[i].namespaceURI == ns || res[i].tagUrn == ns) {
+ tmp.push(res[i]);
+ }
+ }
+ res = tmp;
+ }
+ }
+ return res;
+};
+
+
+// Check whether a node is part of an XML document.
+// Copied verbatim from jQuery sources, needed in TAG preFilter below.
+//
+var isXML = function(elem){
+ return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
+ !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
+};
+
+
+// Modify the TAG preFilter function to work with modified match regexp.
+// This normalises case of the tag name if we're in a HTML document.
+//
+$.expr.preFilter.TAG = function(match, curLoop, inplace, result, not, isXML) {
+ var ln = match[3];
+ if(!isXML) {
+ if(localname_is_uppercase) {
+ ln = ln.toUpperCase();
+ } else {
+ ln = ln.toLowerCase();
+ }
+ }
+ return [match[0],getNamespaceURI(match[2]),ln];
+};
+
+
+// Modify the TAG filter function to account for a namespace selector.
+//
+$.expr.filter.TAG = function(elem,match) {
+ var ns = match[1];
+ var ln = match[2];
+ var e_ns = elem.namespaceURI ? elem.namespaceURI : elem.tagUrn;
+ var e_ln = elem.localName ? elem.localName : elem.tagName;
+ if(ns == "*" || e_ns == ns || (ns == "" && !e_ns)) {
+ return ((ln == "*" && elem.nodeType == 1) || e_ln == ln);
+ }
+ return false;
+};
+
+
+// Modify the ATTR match regexp to extract a namespace selector.
+// This is basically ([namespace|])(attrname)(op)(quote)(pattern)(quote)
+//
+setExprMatchRegex("ATTR",/\[\s*((?:((?:[\w\u00c0-\uFFFF\*_-]*\|)?)((?:[\w\u00c0-\uFFFF_-]|\\.)+)))\s*(?:(\S?=)\s*(['"]*)(.*?)\5|)\s*\]/);
+
+
+// Modify the ATTR preFilter function to account for new regexp match groups,
+// and normalise the namespace URI.
+//
+$.expr.preFilter.ATTR = function(match, curLoop, inplace, result, not, isXML) {
+ var name = match[3].replace(/\\/g, "");
+ if(!isXML && $.expr.attrMap[name]) {
+ match[3] = $.expr.attrMap[name];
+ }
+ if( match[4] == "~=" ) {
+ match[6] = " " + match[6] + " ";
+ }
+ if(!match[2] || match[2] == "|") {
+ match[2] = "";
+ } else {
+ match[2] = getNamespaceURI(match[2]);
+ }
+ return match;
+};
+
+
+// Modify the ATTR filter function to account for namespace selector.
+// Unfortunately this means factoring out the attribute-checking code
+// into a separate function, since it might be called multiple times.
+//
+var filter_attr = function(result,type,check) {
+ var value = result + "";
+ return result == null ?
+ type === "!=" :
+ type === "=" ?
+ value === check :
+ type === "*=" ?
+ value.indexOf(check) >= 0 :
+ type === "~=" ?
+ (" " + value + " ").indexOf(check) >= 0 :
+ !check ?
+ value && result !== false :
+ type === "!=" ?
+ value != check :
+ type === "^=" ?
+ value.indexOf(check) === 0 :
+ type === "$=" ?
+ value.substr(value.length - check.length) === check :
+ type === "|=" ?
+ value === check || value.substr(0,check.length+1)===check+"-" :
+ false;
+}
+
+
+$.expr.filter.ATTR = function(elem, match) {
+ var ns = match[2];
+ var name = match[3];
+ var type = match[4];
+ var check = match[6];
+ var result;
+ // No namespace, just use ordinary attribute lookup.
+ if(ns == "") {
+ result = $.expr.attrHandle[name] ?
+ $.expr.attrHandle[name](elem) :
+ elem[name] != null ?
+ elem[name] :
+ elem.getAttribute(name);
+ return filter_attr(result,type,check);
+ }
+ // Directly use getAttributeNS if applicable and available
+ if(ns != "*" && typeof elem.getAttributeNS != "undefined") {
+ return filter_attr(elem.getAttributeNS(ns,name),type,check);
+ }
+ // Need to iterate over all attributes, either because we couldn't
+ // look it up or because we need to match all namespaces.
+ var attrs = elem.attributes;
+ for(var i=0; attrs[i]; i++) {
+ var ln = attrs[i].localName;
+ if(!ln) {
+ ln = attrs[i].nodeName
+ var idx = ln.indexOf(":");
+ if(idx >= 0) {
+ ln = ln.substr(idx+1);
+ }
+ }
+ if(ln == name) {
+ result = attrs[i].nodeValue;
+ if(ns == "*" || attrs[i].namespaceURI == ns) {
+ if(filter_attr(result,type,check)) {
+ return true;
+ }
+ }
+ if(attrs[i].namespaceURI === "" && attrs[i].prefix) {
+ if(attrs[i].prefix == default_xmlns_rev[ns]) {
+ if(filter_attr(result,type,check)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+};
+
+
+})(jQuery);
+