1 /* 2 * Dual-licensed under the MIT License & the Academic Free License v. 2.1. 3 * See the file LICENSE for more information. 4 * 5 * (c) 2007-2008 by Per Cederberg & Dynabyte AB. All rights reserved. 6 */ 7 8 // Check for loaded MochiKit 9 if (typeof(MochiKit) == "undefined") { 10 throw new ReferenceError("MochiKit must be loaded before loading this script"); 11 } 12 13 /** 14 * @name MochiKit.DOM 15 * @namespace Provides a painless DOM manipulation API. 16 */ 17 // Check for loaded MochiKit.DOM 18 if (typeof(MochiKit.DOM) == "undefined") { 19 throw new ReferenceError("MochiKit.DOM must be loaded before loading this script"); 20 } 21 22 MochiKit.DOM.NS = { 23 XHTML: "http://www.w3.org/1999/xhtml", 24 XLINK: "http://www.w3.org/1999/xlink", 25 SVG: "http://www.w3.org/2000/svg", 26 XUL: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" 27 }; 28 MochiKit.DOM.NS.HTML = [undefined, null, '', MochiKit.DOM.NS.XHTML]; 29 30 /** 31 * Returns true if the specified object looks like a DOM node. 32 * Otherwise, false will be returned. Any non-null object with a 33 * nodeType > 0 will be considered a DOM node by this function. 34 * 35 * @param {Object} obj the object to check 36 * 37 * @return {Boolean} true if the object looks like a DOM node, or 38 * false otherwise 39 */ 40 MochiKit.DOM.isDOM = function(obj) { 41 return typeof(obj) !== "undefined" && 42 typeof(obj.nodeType) === "number" && 43 obj.nodeType > 0; 44 } 45 46 /** 47 * Returns true if the specified object looks like an HTML or XHTML 48 * DOM node. Otherwise, false will be returned. Any non-null object 49 * with a nodeType > 0 will be considered a DOM node, but only those 50 * with a matching namespaceURI will be considered HTML DOM nodes. 51 * 52 * @param {Object} obj the object to check 53 * 54 * @return {Boolean} true if the object looks like an HTML DOM node, 55 * or false otherwise 56 */ 57 MochiKit.DOM.isHTML = function(obj) { 58 var ns = MochiKit.DOM.NS.HTML; 59 return MochiKit.DOM.isDOM(obj) && 60 MochiKit.Base.findIdentical(ns, obj.namespaceURI) >= 0; 61 } 62 63 /** 64 * Creates a programmers debug representation of a DOM node. This 65 * method is similar to MochiKit.DOM.emitHtml, except for that it 66 * does not recurse into child nodes. 67 * 68 * @param {Object} node the HTML DOM node 69 * 70 * @return {String} a debug representation of the DOM node 71 */ 72 MochiKit.DOM.reprDOM = function(node) { 73 if (node == null) { 74 return "null"; 75 } else if (typeof(node) === 'string') { 76 return node; 77 } else if (node.nodeType === 1) { // Node.ELEMENT_NODE 78 var res = "<" + node.tagName.toLowerCase(); 79 var attrs = MochiKit.Base.map(MochiKit.DOM.reprDOM, node.attributes); 80 res += attrs.join(""); 81 if (node.hasChildNodes()) { 82 res += " [" + node.childNodes.length + " child nodes]"; 83 } 84 res += "/>"; 85 return res; 86 } else if (node.nodeType === 2) { // Node.ATTRIBUTE_NODE 87 if (node.specified) { 88 return " " + node.name + '="' + 89 MochiKit.DOM.escapeHTML(node.value) + '"'; 90 } else { 91 return ""; 92 } 93 } else if (node.nodeType === 3) { // Node.TEXT_NODE 94 return MochiKit.DOM.escapeHTML(node.nodeValue); 95 } else { 96 return node.toString(); 97 } 98 } 99 100 /** 101 * Returns an array with DOM node attribute name and value pairs. 102 * The name and value pairs are also stored in arrays with two 103 * elements. 104 * 105 * @param {Object} node the HTML DOM node 106 * 107 * @return {Array} an array containing attribute name and value 108 * pairs (as arrays) 109 */ 110 MochiKit.DOM.attributeArrayNewImpl = function(node) { 111 var res = []; 112 node = MochiKit.DOM.getElement(node); 113 for (var i = 0; node != null && i < node.attributes.length; i++) { 114 var a = node.attributes[i]; 115 if (a.specified) { 116 res.push([a.name, a.value]); 117 } 118 } 119 return res; 120 } 121 122 /** 123 * Returns an immediate child node from a parent DOM node. This 124 * function handles the index range checks and finds the immediate 125 * child node if a descendant node is specified. 126 * 127 * @param {Node} parent the parent HTML DOM node 128 * @param {Number/Node} indexOrNode the child index or a descendant 129 * node 130 * 131 * @return {Node} the child HTML DOM node, or 132 * null if no matching node was found 133 */ 134 MochiKit.DOM.childNode = function(parent, indexOrNode) { 135 parent = MochiKit.DOM.getElement(parent); 136 if (typeof(indexOrNode) == "number") { 137 if (indexOrNode < 0 || indexOrNode >= parent.childNodes.length) { 138 return null; 139 } else { 140 return parent.childNodes[indexOrNode]; 141 } 142 } else { 143 var node = MochiKit.DOM.getElement(indexOrNode); 144 while (node != null && node !== parent && node.parentNode !== parent) { 145 node = node.parentNode; 146 } 147 return (node == null || node === parent) ? null : node; 148 } 149 } 150 151 /** 152 * Creates a DOM node with a namespace. 153 * 154 * @param {String} ns the DOM namespace 155 * @param {String} tag the DOM tag name 156 * @param {Object} [attrs] the node attributes, or null for none 157 * @param {Object} [...] the nodes or text to add as children 158 * 159 * @return {Object} the DOM node created 160 */ 161 MochiKit.DOM.createDOMExt = function(ns, tag, attrs/*, ...*/) { 162 var doc = MochiKit.DOM.currentDocument(); 163 var node = (ns) ? doc.createElementNS(ns, tag) : doc.createElement(tag); 164 MochiKit.DOM.updateNodeAttributes(node, attrs); 165 var children = MochiKit.Base.extend([], arguments, 3); 166 MochiKit.DOM.appendChildNodes(node, children); 167 return node; 168 } 169 170 /** 171 * Creates a DOM text node from the specified text. This is a 172 * convenience function for currentDocument().createTextNode, in 173 * order to be compatible with the withDocument() function. 174 * 175 * @param {String} text the text content 176 * 177 * @return {Object} the DOM text node created 178 */ 179 MochiKit.DOM.createTextNode = function(text) { 180 return MochiKit.DOM.currentDocument().createTextNode(text); 181 } 182 183 /** 184 * Returns a function for creating a specific kind of DOM nodes. The 185 * returned function will optionally require a sequence of non-null 186 * arguments that will be added as attributes to the node creation. 187 * The returned function will otherwise work similar to the 188 * createDOMExt() function, taking attributes and child nodes. 189 * 190 * @param {String} ns the DOM namespace, or null for HTML 191 * @param {String} tag the DOM tag name 192 * @param {Array} [args] the array with required arguments, or null 193 * for no required arguments 194 * @param {Object} [attrs] the default node attributes, or null for 195 * none 196 * @param {Object} [...] the default nodes or text to add as children 197 * 198 * @return {Function} the function that creates the DOM nodes 199 */ 200 MochiKit.DOM.createDOMFuncExt = function(ns, tag, args, attrs/*, ...*/) { 201 args = args || []; 202 attrs = attrs || {}; 203 var children = MochiKit.Base.extend([], arguments, 4); 204 return function(/*arg1, ..., argN, attrs, ...*/) { 205 var myAttrs = MochiKit.Base.update({}, attrs); 206 for (var pos = 0; pos < args.length; pos++) { 207 if (arguments[pos] == null) { 208 throw new Error("Argument '" + args[pos] + "' cannot be null"); 209 } 210 myAttrs[args[pos]] = arguments[pos]; 211 } 212 MochiKit.Base.update(myAttrs, arguments[args.length]); 213 var myChildren = MochiKit.Base.extend([], children); 214 MochiKit.Base.extend(myChildren, arguments, args.length + 1); 215 return MochiKit.DOM.createDOMExt(ns, tag, myAttrs, myChildren); 216 } 217 } 218 219 /** 220 * Blurs (unfocuses) a specified DOM node and all relevant child 221 * nodes. This function will recursively blur all A, BUTTON, INPUT, 222 * TEXTAREA and SELECT child nodes found. 223 * 224 * @param {Object} node the HTML DOM node 225 */ 226 MochiKit.DOM.blurAll = function(node) { 227 if (arguments.length <= 1) { 228 MochiKit.DOM.blurAll(node, "A", "BUTTON", "INPUT", "TEXTAREA", "SELECT"); 229 } else { 230 node.blur(); 231 for (var i = 1; i < arguments.length; i++) { 232 var nodes = node.getElementsByTagName(arguments[i]); 233 for (var j = 0; j < nodes.length; j++) { 234 nodes[j].blur(); 235 } 236 } 237 } 238 } 239