michael@0: /*** michael@0: michael@0: MochiKit.DOM 1.4 michael@0: michael@0: See for documentation, downloads, license, etc. michael@0: michael@0: (c) 2005 Bob Ippolito. All rights Reserved. michael@0: michael@0: ***/ michael@0: michael@0: if (typeof(dojo) != 'undefined') { michael@0: dojo.provide("MochiKit.DOM"); michael@0: dojo.require("MochiKit.Base"); michael@0: } michael@0: if (typeof(JSAN) != 'undefined') { michael@0: JSAN.use("MochiKit.Base", []); michael@0: } michael@0: michael@0: try { michael@0: if (typeof(MochiKit.Base) == 'undefined') { michael@0: throw ""; michael@0: } michael@0: } catch (e) { michael@0: throw "MochiKit.DOM depends on MochiKit.Base!"; michael@0: } michael@0: michael@0: if (typeof(MochiKit.DOM) == 'undefined') { michael@0: MochiKit.DOM = {}; michael@0: } michael@0: michael@0: MochiKit.DOM.NAME = "MochiKit.DOM"; michael@0: MochiKit.DOM.VERSION = "1.4"; michael@0: MochiKit.DOM.__repr__ = function () { michael@0: return "[" + this.NAME + " " + this.VERSION + "]"; michael@0: }; michael@0: MochiKit.DOM.toString = function () { michael@0: return this.__repr__(); michael@0: }; michael@0: michael@0: MochiKit.DOM.EXPORT = [ michael@0: "removeEmptyTextNodes", michael@0: "formContents", michael@0: "currentWindow", michael@0: "currentDocument", michael@0: "withWindow", michael@0: "withDocument", michael@0: "registerDOMConverter", michael@0: "coerceToDOM", michael@0: "createDOM", michael@0: "createDOMFunc", michael@0: "isChildNode", michael@0: "getNodeAttribute", michael@0: "setNodeAttribute", michael@0: "updateNodeAttributes", michael@0: "appendChildNodes", michael@0: "replaceChildNodes", michael@0: "removeElement", michael@0: "swapDOM", michael@0: "BUTTON", michael@0: "TT", michael@0: "PRE", michael@0: "H1", michael@0: "H2", michael@0: "H3", michael@0: "BR", michael@0: "CANVAS", michael@0: "HR", michael@0: "LABEL", michael@0: "TEXTAREA", michael@0: "FORM", michael@0: "STRONG", michael@0: "SELECT", michael@0: "OPTION", michael@0: "OPTGROUP", michael@0: "LEGEND", michael@0: "FIELDSET", michael@0: "P", michael@0: "UL", michael@0: "OL", michael@0: "LI", michael@0: "TD", michael@0: "TR", michael@0: "THEAD", michael@0: "TBODY", michael@0: "TFOOT", michael@0: "TABLE", michael@0: "TH", michael@0: "INPUT", michael@0: "SPAN", michael@0: "A", michael@0: "DIV", michael@0: "IMG", michael@0: "getElement", michael@0: "$", michael@0: "getElementsByTagAndClassName", michael@0: "addToCallStack", michael@0: "addLoadEvent", michael@0: "focusOnLoad", michael@0: "setElementClass", michael@0: "toggleElementClass", michael@0: "addElementClass", michael@0: "removeElementClass", michael@0: "swapElementClass", michael@0: "hasElementClass", michael@0: "escapeHTML", michael@0: "toHTML", michael@0: "emitHTML", michael@0: "scrapeText" michael@0: ]; michael@0: michael@0: MochiKit.DOM.EXPORT_OK = [ michael@0: "domConverters" michael@0: ]; michael@0: michael@0: MochiKit.DOM.DEPRECATED = [ michael@0: ['computedStyle', 'MochiKit.Style.computedStyle', '1.4'], michael@0: /** @id MochiKit.DOM.elementDimensions */ michael@0: ['elementDimensions', 'MochiKit.Style.getElementDimensions', '1.4'], michael@0: /** @id MochiKit.DOM.elementPosition */ michael@0: ['elementPosition', 'MochiKit.Style.getElementPosition', '1.4'], michael@0: ['hideElement', 'MochiKit.Style.hideElement', '1.4'], michael@0: /** @id MochiKit.DOM.setElementDimensions */ michael@0: ['setElementDimensions', 'MochiKit.Style.setElementDimensions', '1.4'], michael@0: /** @id MochiKit.DOM.setElementPosition */ michael@0: ['setElementPosition', 'MochiKit.Style.setElementPosition', '1.4'], michael@0: ['setDisplayForElement', 'MochiKit.Style.setDisplayForElement', '1.4'], michael@0: /** @id MochiKit.DOM.setOpacity */ michael@0: ['setOpacity', 'MochiKit.Style.setOpacity', '1.4'], michael@0: ['showElement', 'MochiKit.Style.showElement', '1.4'], michael@0: /** @id MochiKit.DOM.Coordinates */ michael@0: ['Coordinates', 'MochiKit.Style.Coordinates', '1.4'], // FIXME: broken michael@0: /** @id MochiKit.DOM.Dimensions */ michael@0: ['Dimensions', 'MochiKit.Style.Dimensions', '1.4'] // FIXME: broken michael@0: ]; michael@0: michael@0: /** @id MochiKit.DOM.getViewportDimensions */ michael@0: MochiKit.DOM.getViewportDimensions = new Function('' + michael@0: 'if (!MochiKit["Style"]) {' + michael@0: ' throw new Error("This function has been deprecated and depends on MochiKit.Style.");' + michael@0: '}' + michael@0: 'return MochiKit.Style.getViewportDimensions.apply(this, arguments);'); michael@0: michael@0: MochiKit.Base.update(MochiKit.DOM, { michael@0: michael@0: /** @id MochiKit.DOM.currentWindow */ michael@0: currentWindow: function () { michael@0: return MochiKit.DOM._window; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.currentDocument */ michael@0: currentDocument: function () { michael@0: return MochiKit.DOM._document; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.withWindow */ michael@0: withWindow: function (win, func) { michael@0: var self = MochiKit.DOM; michael@0: var oldDoc = self._document; michael@0: var oldWin = self._win; michael@0: var rval; michael@0: try { michael@0: self._window = win; michael@0: self._document = win.document; michael@0: rval = func(); michael@0: } catch (e) { michael@0: self._window = oldWin; michael@0: self._document = oldDoc; michael@0: throw e; michael@0: } michael@0: self._window = oldWin; michael@0: self._document = oldDoc; michael@0: return rval; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.formContents */ michael@0: formContents: function (elem/* = document */) { michael@0: var names = []; michael@0: var values = []; michael@0: var m = MochiKit.Base; michael@0: var self = MochiKit.DOM; michael@0: if (typeof(elem) == "undefined" || elem === null) { michael@0: elem = self._document; michael@0: } else { michael@0: elem = self.getElement(elem); michael@0: } michael@0: m.nodeWalk(elem, function (elem) { michael@0: var name = elem.name; michael@0: if (m.isNotEmpty(name)) { michael@0: var tagName = elem.tagName.toUpperCase(); michael@0: if (tagName === "INPUT" michael@0: && (elem.type == "radio" || elem.type == "checkbox") michael@0: && !elem.checked michael@0: ) { michael@0: return null; michael@0: } michael@0: if (tagName === "SELECT") { michael@0: if (elem.type == "select-one") { michael@0: if (elem.selectedIndex >= 0) { michael@0: var opt = elem.options[elem.selectedIndex]; michael@0: names.push(name); michael@0: values.push(opt.value); michael@0: return null; michael@0: } michael@0: // no form elements? michael@0: names.push(name); michael@0: values.push(""); michael@0: return null; michael@0: } else { michael@0: var opts = elem.options; michael@0: if (!opts.length) { michael@0: names.push(name); michael@0: values.push(""); michael@0: return null; michael@0: } michael@0: for (var i = 0; i < opts.length; i++) { michael@0: var opt = opts[i]; michael@0: if (!opt.selected) { michael@0: continue; michael@0: } michael@0: names.push(name); michael@0: values.push(opt.value); michael@0: } michael@0: return null; michael@0: } michael@0: } michael@0: if (tagName === "FORM" || tagName === "P" || tagName === "SPAN" michael@0: || tagName === "DIV" michael@0: ) { michael@0: return elem.childNodes; michael@0: } michael@0: names.push(name); michael@0: values.push(elem.value || ''); michael@0: return null; michael@0: } michael@0: return elem.childNodes; michael@0: }); michael@0: return [names, values]; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.withDocument */ michael@0: withDocument: function (doc, func) { michael@0: var self = MochiKit.DOM; michael@0: var oldDoc = self._document; michael@0: var rval; michael@0: try { michael@0: self._document = doc; michael@0: rval = func(); michael@0: } catch (e) { michael@0: self._document = oldDoc; michael@0: throw e; michael@0: } michael@0: self._document = oldDoc; michael@0: return rval; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.registerDOMConverter */ michael@0: registerDOMConverter: function (name, check, wrap, /* optional */override) { michael@0: MochiKit.DOM.domConverters.register(name, check, wrap, override); michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.coerceToDOM */ michael@0: coerceToDOM: function (node, ctx) { michael@0: var m = MochiKit.Base; michael@0: var im = MochiKit.Iter; michael@0: var self = MochiKit.DOM; michael@0: if (im) { michael@0: var iter = im.iter; michael@0: var repeat = im.repeat; michael@0: var map = m.map; michael@0: } michael@0: var domConverters = self.domConverters; michael@0: var coerceToDOM = arguments.callee; michael@0: var NotFound = m.NotFound; michael@0: while (true) { michael@0: if (typeof(node) == 'undefined' || node === null) { michael@0: return null; michael@0: } michael@0: if (typeof(node.nodeType) != 'undefined' && node.nodeType > 0) { michael@0: return node; michael@0: } michael@0: if (typeof(node) == 'number' || typeof(node) == 'boolean') { michael@0: node = node.toString(); michael@0: // FALL THROUGH michael@0: } michael@0: if (typeof(node) == 'string') { michael@0: return self._document.createTextNode(node); michael@0: } michael@0: if (typeof(node.__dom__) == 'function') { michael@0: node = node.__dom__(ctx); michael@0: continue; michael@0: } michael@0: if (typeof(node.dom) == 'function') { michael@0: node = node.dom(ctx); michael@0: continue; michael@0: } michael@0: if (typeof(node) == 'function') { michael@0: node = node.apply(ctx, [ctx]); michael@0: continue; michael@0: } michael@0: michael@0: if (im) { michael@0: // iterable michael@0: var iterNodes = null; michael@0: try { michael@0: iterNodes = iter(node); michael@0: } catch (e) { michael@0: // pass michael@0: } michael@0: if (iterNodes) { michael@0: return map(coerceToDOM, iterNodes, repeat(ctx)); michael@0: } michael@0: } michael@0: michael@0: // adapter michael@0: try { michael@0: node = domConverters.match(node, ctx); michael@0: continue; michael@0: } catch (e) { michael@0: if (e != NotFound) { michael@0: throw e; michael@0: } michael@0: } michael@0: michael@0: // fallback michael@0: return self._document.createTextNode(node.toString()); michael@0: } michael@0: // mozilla warnings aren't too bright michael@0: return undefined; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.isChildNode */ michael@0: isChildNode: function (node, maybeparent) { michael@0: var self = MochiKit.DOM; michael@0: if (typeof(node) == "string") { michael@0: node = self.getElement(node); michael@0: } michael@0: if (typeof(maybeparent) == "string") { michael@0: maybeparent = self.getElement(maybeparent); michael@0: } michael@0: if (node === maybeparent) { michael@0: return true; michael@0: } michael@0: while (node && node.tagName.toUpperCase() != "BODY") { michael@0: node = node.parentNode; michael@0: if (node === maybeparent) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.setNodeAttribute */ michael@0: setNodeAttribute: function (node, attr, value) { michael@0: var o = {}; michael@0: o[attr] = value; michael@0: try { michael@0: return MochiKit.DOM.updateNodeAttributes(node, o); michael@0: } catch (e) { michael@0: // pass michael@0: } michael@0: return null; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.getNodeAttribute */ michael@0: getNodeAttribute: function (node, attr) { michael@0: var self = MochiKit.DOM; michael@0: var rename = self.attributeArray.renames[attr]; michael@0: node = self.getElement(node); michael@0: try { michael@0: if (rename) { michael@0: return node[rename]; michael@0: } michael@0: return node.getAttribute(attr); michael@0: } catch (e) { michael@0: // pass michael@0: } michael@0: return null; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.updateNodeAttributes */ michael@0: updateNodeAttributes: function (node, attrs) { michael@0: var elem = node; michael@0: var self = MochiKit.DOM; michael@0: if (typeof(node) == 'string') { michael@0: elem = self.getElement(node); michael@0: } michael@0: if (attrs) { michael@0: var updatetree = MochiKit.Base.updatetree; michael@0: if (self.attributeArray.compliant) { michael@0: // not IE, good. michael@0: for (var k in attrs) { michael@0: var v = attrs[k]; michael@0: if (typeof(v) == 'object' && typeof(elem[k]) == 'object') { michael@0: updatetree(elem[k], v); michael@0: } else if (k.substring(0, 2) == "on") { michael@0: if (typeof(v) == "string") { michael@0: v = new Function(v); michael@0: } michael@0: elem[k] = v; michael@0: } else { michael@0: elem.setAttribute(k, v); michael@0: } michael@0: } michael@0: } else { michael@0: // IE is insane in the membrane michael@0: var renames = self.attributeArray.renames; michael@0: for (k in attrs) { michael@0: v = attrs[k]; michael@0: var renamed = renames[k]; michael@0: if (k == "style" && typeof(v) == "string") { michael@0: elem.style.cssText = v; michael@0: } else if (typeof(renamed) == "string") { michael@0: elem[renamed] = v; michael@0: } else if (typeof(elem[k]) == 'object' michael@0: && typeof(v) == 'object') { michael@0: updatetree(elem[k], v); michael@0: } else if (k.substring(0, 2) == "on") { michael@0: if (typeof(v) == "string") { michael@0: v = new Function(v); michael@0: } michael@0: elem[k] = v; michael@0: } else { michael@0: elem.setAttribute(k, v); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return elem; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.appendChildNodes */ michael@0: appendChildNodes: function (node/*, nodes...*/) { michael@0: var elem = node; michael@0: var self = MochiKit.DOM; michael@0: if (typeof(node) == 'string') { michael@0: elem = self.getElement(node); michael@0: } michael@0: var nodeStack = [ michael@0: self.coerceToDOM( michael@0: MochiKit.Base.extend(null, arguments, 1), michael@0: elem michael@0: ) michael@0: ]; michael@0: var concat = MochiKit.Base.concat; michael@0: while (nodeStack.length) { michael@0: var n = nodeStack.shift(); michael@0: if (typeof(n) == 'undefined' || n === null) { michael@0: // pass michael@0: } else if (typeof(n.nodeType) == 'number') { michael@0: elem.appendChild(n); michael@0: } else { michael@0: nodeStack = concat(n, nodeStack); michael@0: } michael@0: } michael@0: return elem; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.replaceChildNodes */ michael@0: replaceChildNodes: function (node/*, nodes...*/) { michael@0: var elem = node; michael@0: var self = MochiKit.DOM; michael@0: if (typeof(node) == 'string') { michael@0: elem = self.getElement(node); michael@0: arguments[0] = elem; michael@0: } michael@0: var child; michael@0: while ((child = elem.firstChild)) { michael@0: elem.removeChild(child); michael@0: } michael@0: if (arguments.length < 2) { michael@0: return elem; michael@0: } else { michael@0: return self.appendChildNodes.apply(this, arguments); michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.createDOM */ michael@0: createDOM: function (name, attrs/*, nodes... */) { michael@0: var elem; michael@0: var self = MochiKit.DOM; michael@0: var m = MochiKit.Base; michael@0: if (typeof(attrs) == "string" || typeof(attrs) == "number") { michael@0: var args = m.extend([name, null], arguments, 1); michael@0: return arguments.callee.apply(this, args); michael@0: } michael@0: if (typeof(name) == 'string') { michael@0: // Internet Explorer is dumb michael@0: var xhtml = self._xhtml; michael@0: if (attrs && !self.attributeArray.compliant) { michael@0: // http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/name_2.asp michael@0: var contents = ""; michael@0: if ('name' in attrs) { michael@0: contents += ' name="' + self.escapeHTML(attrs.name) + '"'; michael@0: } michael@0: if (name == 'input' && 'type' in attrs) { michael@0: contents += ' type="' + self.escapeHTML(attrs.type) + '"'; michael@0: } michael@0: if (contents) { michael@0: name = "<" + name + contents + ">"; michael@0: xhtml = false; michael@0: } michael@0: } michael@0: var d = self._document; michael@0: if (xhtml && d === document) { michael@0: elem = d.createElementNS("http://www.w3.org/1999/xhtml", name); michael@0: } else { michael@0: elem = d.createElement(name); michael@0: } michael@0: } else { michael@0: elem = name; michael@0: } michael@0: if (attrs) { michael@0: self.updateNodeAttributes(elem, attrs); michael@0: } michael@0: if (arguments.length <= 2) { michael@0: return elem; michael@0: } else { michael@0: var args = m.extend([elem], arguments, 2); michael@0: return self.appendChildNodes.apply(this, args); michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.createDOMFunc */ michael@0: createDOMFunc: function (/* tag, attrs, *nodes */) { michael@0: var m = MochiKit.Base; michael@0: return m.partial.apply( michael@0: this, michael@0: m.extend([MochiKit.DOM.createDOM], arguments) michael@0: ); michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.removeElement */ michael@0: removeElement: function (elem) { michael@0: var e = MochiKit.DOM.getElement(elem); michael@0: e.parentNode.removeChild(e); michael@0: return e; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.swapDOM */ michael@0: swapDOM: function (dest, src) { michael@0: var self = MochiKit.DOM; michael@0: dest = self.getElement(dest); michael@0: var parent = dest.parentNode; michael@0: if (src) { michael@0: src = self.getElement(src); michael@0: parent.replaceChild(src, dest); michael@0: } else { michael@0: parent.removeChild(dest); michael@0: } michael@0: return src; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.getElement */ michael@0: getElement: function (id) { michael@0: var self = MochiKit.DOM; michael@0: if (arguments.length == 1) { michael@0: return ((typeof(id) == "string") ? michael@0: self._document.getElementById(id) : id); michael@0: } else { michael@0: return MochiKit.Base.map(self.getElement, arguments); michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.getElementsByTagAndClassName */ michael@0: getElementsByTagAndClassName: function (tagName, className, michael@0: /* optional */parent) { michael@0: var self = MochiKit.DOM; michael@0: if (typeof(tagName) == 'undefined' || tagName === null) { michael@0: tagName = '*'; michael@0: } michael@0: if (typeof(parent) == 'undefined' || parent === null) { michael@0: parent = self._document; michael@0: } michael@0: parent = self.getElement(parent); michael@0: var children = (parent.getElementsByTagName(tagName) michael@0: || self._document.all); michael@0: if (typeof(className) == 'undefined' || className === null) { michael@0: return MochiKit.Base.extend(null, children); michael@0: } michael@0: michael@0: var elements = []; michael@0: for (var i = 0; i < children.length; i++) { michael@0: var child = children[i]; michael@0: var cls = child.className; michael@0: if (!cls) { michael@0: continue; michael@0: } michael@0: var classNames = cls.split(' '); michael@0: for (var j = 0; j < classNames.length; j++) { michael@0: if (classNames[j] == className) { michael@0: elements.push(child); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return elements; michael@0: }, michael@0: michael@0: _newCallStack: function (path, once) { michael@0: var rval = function () { michael@0: var callStack = arguments.callee.callStack; michael@0: for (var i = 0; i < callStack.length; i++) { michael@0: if (callStack[i].apply(this, arguments) === false) { michael@0: break; michael@0: } michael@0: } michael@0: if (once) { michael@0: try { michael@0: this[path] = null; michael@0: } catch (e) { michael@0: // pass michael@0: } michael@0: } michael@0: }; michael@0: rval.callStack = []; michael@0: return rval; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.addToCallStack */ michael@0: addToCallStack: function (target, path, func, once) { michael@0: var self = MochiKit.DOM; michael@0: var existing = target[path]; michael@0: var regfunc = existing; michael@0: if (!(typeof(existing) == 'function' michael@0: && typeof(existing.callStack) == "object" michael@0: && existing.callStack !== null)) { michael@0: regfunc = self._newCallStack(path, once); michael@0: if (typeof(existing) == 'function') { michael@0: regfunc.callStack.push(existing); michael@0: } michael@0: target[path] = regfunc; michael@0: } michael@0: regfunc.callStack.push(func); michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.addLoadEvent */ michael@0: addLoadEvent: function (func) { michael@0: var self = MochiKit.DOM; michael@0: self.addToCallStack(self._window, "onload", func, true); michael@0: michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.focusOnLoad */ michael@0: focusOnLoad: function (element) { michael@0: var self = MochiKit.DOM; michael@0: self.addLoadEvent(function () { michael@0: element = self.getElement(element); michael@0: if (element) { michael@0: element.focus(); michael@0: } michael@0: }); michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.setElementClass */ michael@0: setElementClass: function (element, className) { michael@0: var self = MochiKit.DOM; michael@0: var obj = self.getElement(element); michael@0: if (self.attributeArray.compliant) { michael@0: obj.setAttribute("class", className); michael@0: } else { michael@0: obj.setAttribute("className", className); michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.toggleElementClass */ michael@0: toggleElementClass: function (className/*, element... */) { michael@0: var self = MochiKit.DOM; michael@0: for (var i = 1; i < arguments.length; i++) { michael@0: var obj = self.getElement(arguments[i]); michael@0: if (!self.addElementClass(obj, className)) { michael@0: self.removeElementClass(obj, className); michael@0: } michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.addElementClass */ michael@0: addElementClass: function (element, className) { michael@0: var self = MochiKit.DOM; michael@0: var obj = self.getElement(element); michael@0: var cls = obj.className; michael@0: // trivial case, no className yet michael@0: if (cls == undefined || cls.length === 0) { michael@0: self.setElementClass(obj, className); michael@0: return true; michael@0: } michael@0: // the other trivial case, already set as the only class michael@0: if (cls == className) { michael@0: return false; michael@0: } michael@0: var classes = cls.split(" "); michael@0: for (var i = 0; i < classes.length; i++) { michael@0: // already present michael@0: if (classes[i] == className) { michael@0: return false; michael@0: } michael@0: } michael@0: // append class michael@0: self.setElementClass(obj, cls + " " + className); michael@0: return true; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.removeElementClass */ michael@0: removeElementClass: function (element, className) { michael@0: var self = MochiKit.DOM; michael@0: var obj = self.getElement(element); michael@0: var cls = obj.className; michael@0: // trivial case, no className yet michael@0: if (cls == undefined || cls.length === 0) { michael@0: return false; michael@0: } michael@0: // other trivial case, set only to className michael@0: if (cls == className) { michael@0: self.setElementClass(obj, ""); michael@0: return true; michael@0: } michael@0: var classes = cls.split(" "); michael@0: for (var i = 0; i < classes.length; i++) { michael@0: // already present michael@0: if (classes[i] == className) { michael@0: // only check sane case where the class is used once michael@0: classes.splice(i, 1); michael@0: self.setElementClass(obj, classes.join(" ")); michael@0: return true; michael@0: } michael@0: } michael@0: // not found michael@0: return false; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.swapElementClass */ michael@0: swapElementClass: function (element, fromClass, toClass) { michael@0: var obj = MochiKit.DOM.getElement(element); michael@0: var res = MochiKit.DOM.removeElementClass(obj, fromClass); michael@0: if (res) { michael@0: MochiKit.DOM.addElementClass(obj, toClass); michael@0: } michael@0: return res; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.hasElementClass */ michael@0: hasElementClass: function (element, className/*...*/) { michael@0: var obj = MochiKit.DOM.getElement(element); michael@0: var cls = obj.className; michael@0: if (!cls) { michael@0: return false; michael@0: } michael@0: var classes = cls.split(" "); michael@0: for (var i = 1; i < arguments.length; i++) { michael@0: var good = false; michael@0: for (var j = 0; j < classes.length; j++) { michael@0: if (classes[j] == arguments[i]) { michael@0: good = true; michael@0: break; michael@0: } michael@0: } michael@0: if (!good) { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.escapeHTML */ michael@0: escapeHTML: function (s) { michael@0: return s.replace(/&/g, "&" michael@0: ).replace(/"/g, """ michael@0: ).replace(//g, ">"); michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.toHTML */ michael@0: toHTML: function (dom) { michael@0: return MochiKit.DOM.emitHTML(dom).join(""); michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.emitHTML */ michael@0: emitHTML: function (dom, /* optional */lst) { michael@0: if (typeof(lst) == 'undefined' || lst === null) { michael@0: lst = []; michael@0: } michael@0: // queue is the call stack, we're doing this non-recursively michael@0: var queue = [dom]; michael@0: var self = MochiKit.DOM; michael@0: var escapeHTML = self.escapeHTML; michael@0: var attributeArray = self.attributeArray; michael@0: while (queue.length) { michael@0: dom = queue.pop(); michael@0: if (typeof(dom) == 'string') { michael@0: lst.push(dom); michael@0: } else if (dom.nodeType == 1) { michael@0: // we're not using higher order stuff here michael@0: // because safari has heisenbugs.. argh. michael@0: // michael@0: // I think it might have something to do with michael@0: // garbage collection and function calls. michael@0: lst.push('<' + dom.tagName.toLowerCase()); michael@0: var attributes = []; michael@0: var domAttr = attributeArray(dom); michael@0: for (var i = 0; i < domAttr.length; i++) { michael@0: var a = domAttr[i]; michael@0: attributes.push([ michael@0: " ", michael@0: a.name, michael@0: '="', michael@0: escapeHTML(a.value), michael@0: '"' michael@0: ]); michael@0: } michael@0: attributes.sort(); michael@0: for (i = 0; i < attributes.length; i++) { michael@0: var attrs = attributes[i]; michael@0: for (var j = 0; j < attrs.length; j++) { michael@0: lst.push(attrs[j]); michael@0: } michael@0: } michael@0: if (dom.hasChildNodes()) { michael@0: lst.push(">"); michael@0: // queue is the FILO call stack, so we put the close tag michael@0: // on first michael@0: queue.push(""); michael@0: var cnodes = dom.childNodes; michael@0: for (i = cnodes.length - 1; i >= 0; i--) { michael@0: queue.push(cnodes[i]); michael@0: } michael@0: } else { michael@0: lst.push('/>'); michael@0: } michael@0: } else if (dom.nodeType == 3) { michael@0: lst.push(escapeHTML(dom.nodeValue)); michael@0: } michael@0: } michael@0: return lst; michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.scrapeText */ michael@0: scrapeText: function (node, /* optional */asArray) { michael@0: var rval = []; michael@0: (function (node) { michael@0: var cn = node.childNodes; michael@0: if (cn) { michael@0: for (var i = 0; i < cn.length; i++) { michael@0: arguments.callee.call(this, cn[i]); michael@0: } michael@0: } michael@0: var nodeValue = node.nodeValue; michael@0: if (typeof(nodeValue) == 'string') { michael@0: rval.push(nodeValue); michael@0: } michael@0: })(MochiKit.DOM.getElement(node)); michael@0: if (asArray) { michael@0: return rval; michael@0: } else { michael@0: return rval.join(""); michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.DOM.removeEmptyTextNodes */ michael@0: removeEmptyTextNodes: function (element) { michael@0: element = MochiKit.DOM.getElement(element); michael@0: for (var i = 0; i < element.childNodes.length; i++) { michael@0: var node = element.childNodes[i]; michael@0: if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) { michael@0: node.parentNode.removeChild(node); michael@0: } michael@0: } michael@0: }, michael@0: michael@0: __new__: function (win) { michael@0: michael@0: var m = MochiKit.Base; michael@0: if (typeof(document) != "undefined") { michael@0: this._document = document; michael@0: this._xhtml = michael@0: document.createElementNS && michael@0: document.createElement("testname").localName == "testname"; michael@0: } else if (MochiKit.MockDOM) { michael@0: this._document = MochiKit.MockDOM.document; michael@0: } michael@0: this._window = win; michael@0: michael@0: this.domConverters = new m.AdapterRegistry(); michael@0: michael@0: var __tmpElement = this._document.createElement("span"); michael@0: var attributeArray; michael@0: if (__tmpElement && __tmpElement.attributes && michael@0: __tmpElement.attributes.length > 0) { michael@0: // for braindead browsers (IE) that insert extra junk michael@0: var filter = m.filter; michael@0: attributeArray = function (node) { michael@0: return filter(attributeArray.ignoreAttrFilter, node.attributes); michael@0: }; michael@0: attributeArray.ignoreAttr = {}; michael@0: var attrs = __tmpElement.attributes; michael@0: var ignoreAttr = attributeArray.ignoreAttr; michael@0: for (var i = 0; i < attrs.length; i++) { michael@0: var a = attrs[i]; michael@0: ignoreAttr[a.name] = a.value; michael@0: } michael@0: attributeArray.ignoreAttrFilter = function (a) { michael@0: return (attributeArray.ignoreAttr[a.name] != a.value); michael@0: }; michael@0: attributeArray.compliant = false; michael@0: attributeArray.renames = { michael@0: "class": "className", michael@0: "checked": "defaultChecked", michael@0: "usemap": "useMap", michael@0: "for": "htmlFor", michael@0: "readonly": "readOnly", michael@0: "colspan": "colSpan", michael@0: "bgcolor": "bgColor" michael@0: }; michael@0: } else { michael@0: attributeArray = function (node) { michael@0: /*** michael@0: michael@0: Return an array of attributes for a given node, michael@0: filtering out attributes that don't belong for michael@0: that are inserted by "Certain Browsers". michael@0: michael@0: ***/ michael@0: return node.attributes; michael@0: }; michael@0: attributeArray.compliant = true; michael@0: attributeArray.renames = {}; michael@0: } michael@0: this.attributeArray = attributeArray; michael@0: michael@0: // FIXME: this really belongs in Base, and could probably be cleaner michael@0: var _deprecated = function(fromModule, arr) { michael@0: var modules = arr[1].split('.'); michael@0: var str = ''; michael@0: var obj = {}; michael@0: michael@0: str += 'if (!MochiKit.' + modules[1] + ') { throw new Error("'; michael@0: str += 'This function has been deprecated and depends on MochiKit.'; michael@0: str += modules[1] + '.");}'; michael@0: str += 'return MochiKit.' + modules[1] + '.' + arr[0]; michael@0: str += '.apply(this, arguments);'; michael@0: michael@0: obj[modules[2]] = new Function(str); michael@0: MochiKit.Base.update(MochiKit[fromModule], obj); michael@0: } michael@0: for (var i; i < MochiKit.DOM.DEPRECATED.length; i++) { michael@0: _deprecated('DOM', MochiKit.DOM.DEPRECATED[i]); michael@0: } michael@0: michael@0: // shorthand for createDOM syntax michael@0: var createDOMFunc = this.createDOMFunc; michael@0: /** @id MochiKit.DOM.UL */ michael@0: this.UL = createDOMFunc("ul"); michael@0: /** @id MochiKit.DOM.OL */ michael@0: this.OL = createDOMFunc("ol"); michael@0: /** @id MochiKit.DOM.LI */ michael@0: this.LI = createDOMFunc("li"); michael@0: /** @id MochiKit.DOM.TD */ michael@0: this.TD = createDOMFunc("td"); michael@0: /** @id MochiKit.DOM.TR */ michael@0: this.TR = createDOMFunc("tr"); michael@0: /** @id MochiKit.DOM.TBODY */ michael@0: this.TBODY = createDOMFunc("tbody"); michael@0: /** @id MochiKit.DOM.THEAD */ michael@0: this.THEAD = createDOMFunc("thead"); michael@0: /** @id MochiKit.DOM.TFOOT */ michael@0: this.TFOOT = createDOMFunc("tfoot"); michael@0: /** @id MochiKit.DOM.TABLE */ michael@0: this.TABLE = createDOMFunc("table"); michael@0: /** @id MochiKit.DOM.TH */ michael@0: this.TH = createDOMFunc("th"); michael@0: /** @id MochiKit.DOM.INPUT */ michael@0: this.INPUT = createDOMFunc("input"); michael@0: /** @id MochiKit.DOM.SPAN */ michael@0: this.SPAN = createDOMFunc("span"); michael@0: /** @id MochiKit.DOM.A */ michael@0: this.A = createDOMFunc("a"); michael@0: /** @id MochiKit.DOM.DIV */ michael@0: this.DIV = createDOMFunc("div"); michael@0: /** @id MochiKit.DOM.IMG */ michael@0: this.IMG = createDOMFunc("img"); michael@0: /** @id MochiKit.DOM.BUTTON */ michael@0: this.BUTTON = createDOMFunc("button"); michael@0: /** @id MochiKit.DOM.TT */ michael@0: this.TT = createDOMFunc("tt"); michael@0: /** @id MochiKit.DOM.PRE */ michael@0: this.PRE = createDOMFunc("pre"); michael@0: /** @id MochiKit.DOM.H1 */ michael@0: this.H1 = createDOMFunc("h1"); michael@0: /** @id MochiKit.DOM.H2 */ michael@0: this.H2 = createDOMFunc("h2"); michael@0: /** @id MochiKit.DOM.H3 */ michael@0: this.H3 = createDOMFunc("h3"); michael@0: /** @id MochiKit.DOM.BR */ michael@0: this.BR = createDOMFunc("br"); michael@0: /** @id MochiKit.DOM.HR */ michael@0: this.HR = createDOMFunc("hr"); michael@0: /** @id MochiKit.DOM.LABEL */ michael@0: this.LABEL = createDOMFunc("label"); michael@0: /** @id MochiKit.DOM.TEXTAREA */ michael@0: this.TEXTAREA = createDOMFunc("textarea"); michael@0: /** @id MochiKit.DOM.FORM */ michael@0: this.FORM = createDOMFunc("form"); michael@0: /** @id MochiKit.DOM.P */ michael@0: this.P = createDOMFunc("p"); michael@0: /** @id MochiKit.DOM.SELECT */ michael@0: this.SELECT = createDOMFunc("select"); michael@0: /** @id MochiKit.DOM.OPTION */ michael@0: this.OPTION = createDOMFunc("option"); michael@0: /** @id MochiKit.DOM.OPTGROUP */ michael@0: this.OPTGROUP = createDOMFunc("optgroup"); michael@0: /** @id MochiKit.DOM.LEGEND */ michael@0: this.LEGEND = createDOMFunc("legend"); michael@0: /** @id MochiKit.DOM.FIELDSET */ michael@0: this.FIELDSET = createDOMFunc("fieldset"); michael@0: /** @id MochiKit.DOM.STRONG */ michael@0: this.STRONG = createDOMFunc("strong"); michael@0: /** @id MochiKit.DOM.CANVAS */ michael@0: this.CANVAS = createDOMFunc("canvas"); michael@0: michael@0: /** @id MochiKit.DOM.$ */ michael@0: this.$ = this.getElement; michael@0: michael@0: this.EXPORT_TAGS = { michael@0: ":common": this.EXPORT, michael@0: ":all": m.concat(this.EXPORT, this.EXPORT_OK) michael@0: }; michael@0: michael@0: m.nameFunctions(this); michael@0: michael@0: } michael@0: }); michael@0: michael@0: michael@0: MochiKit.DOM.__new__(((typeof(window) == "undefined") ? this : window)); michael@0: michael@0: // michael@0: // XXX: Internet Explorer blows michael@0: // michael@0: if (MochiKit.__export__) { michael@0: withWindow = MochiKit.DOM.withWindow; michael@0: withDocument = MochiKit.DOM.withDocument; michael@0: } michael@0: michael@0: MochiKit.Base._exportSymbols(this, MochiKit.DOM);