merge
[dygraphs.git] / mochikit_v14 / MochiKit / DOM.js
1 /***
2
3 MochiKit.DOM 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005 Bob Ippolito. All rights Reserved.
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide("MochiKit.DOM");
13 dojo.require("MochiKit.Base");
14 }
15 if (typeof(JSAN) != 'undefined') {
16 JSAN.use("MochiKit.Base", []);
17 }
18
19 try {
20 if (typeof(MochiKit.Base) == 'undefined') {
21 throw "";
22 }
23 } catch (e) {
24 throw "MochiKit.DOM depends on MochiKit.Base!";
25 }
26
27 if (typeof(MochiKit.DOM) == 'undefined') {
28 MochiKit.DOM = {};
29 }
30
31 MochiKit.DOM.NAME = "MochiKit.DOM";
32 MochiKit.DOM.VERSION = "1.4";
33 MochiKit.DOM.__repr__ = function () {
34 return "[" + this.NAME + " " + this.VERSION + "]";
35 };
36 MochiKit.DOM.toString = function () {
37 return this.__repr__();
38 };
39
40 MochiKit.DOM.EXPORT = [
41 "removeEmptyTextNodes",
42 "formContents",
43 "currentWindow",
44 "currentDocument",
45 "withWindow",
46 "withDocument",
47 "registerDOMConverter",
48 "coerceToDOM",
49 "createDOM",
50 "createDOMFunc",
51 "isChildNode",
52 "getNodeAttribute",
53 "removeNodeAttribute",
54 "setNodeAttribute",
55 "updateNodeAttributes",
56 "appendChildNodes",
57 "insertSiblingNodesAfter",
58 "insertSiblingNodesBefore",
59 "replaceChildNodes",
60 "removeElement",
61 "swapDOM",
62 "BUTTON",
63 "TT",
64 "PRE",
65 "H1",
66 "H2",
67 "H3",
68 "BR",
69 "CANVAS",
70 "HR",
71 "LABEL",
72 "TEXTAREA",
73 "FORM",
74 "STRONG",
75 "SELECT",
76 "OPTION",
77 "OPTGROUP",
78 "LEGEND",
79 "FIELDSET",
80 "P",
81 "UL",
82 "OL",
83 "LI",
84 "TD",
85 "TR",
86 "THEAD",
87 "TBODY",
88 "TFOOT",
89 "TABLE",
90 "TH",
91 "INPUT",
92 "SPAN",
93 "A",
94 "DIV",
95 "IMG",
96 "getElement",
97 "$",
98 "getElementsByTagAndClassName",
99 "addToCallStack",
100 "addLoadEvent",
101 "focusOnLoad",
102 "setElementClass",
103 "toggleElementClass",
104 "addElementClass",
105 "removeElementClass",
106 "swapElementClass",
107 "hasElementClass",
108 "escapeHTML",
109 "toHTML",
110 "emitHTML",
111 "scrapeText",
112 "isParent",
113 "getFirstParentByTagAndClassName",
114 "makeClipping",
115 "undoClipping",
116 "makePositioned",
117 "undoPositioned",
118 "getFirstElementByTagAndClassName"
119 ];
120
121 MochiKit.DOM.EXPORT_OK = [
122 "domConverters"
123 ];
124
125 MochiKit.DOM.DEPRECATED = [
126 ['computedStyle', 'MochiKit.Style.getStyle', '1.4'],
127 /** @id MochiKit.DOM.elementDimensions */
128 ['elementDimensions', 'MochiKit.Style.getElementDimensions', '1.4'],
129 /** @id MochiKit.DOM.elementPosition */
130 ['elementPosition', 'MochiKit.Style.getElementPosition', '1.4'],
131 ['hideElement', 'MochiKit.Style.hideElement', '1.4'],
132 /** @id MochiKit.DOM.setElementDimensions */
133 ['setElementDimensions', 'MochiKit.Style.setElementDimensions', '1.4'],
134 /** @id MochiKit.DOM.setElementPosition */
135 ['setElementPosition', 'MochiKit.Style.setElementPosition', '1.4'],
136 ['setDisplayForElement', 'MochiKit.Style.setDisplayForElement', '1.4'],
137 /** @id MochiKit.DOM.setOpacity */
138 ['setOpacity', 'MochiKit.Style.setOpacity', '1.4'],
139 ['showElement', 'MochiKit.Style.showElement', '1.4'],
140 /** @id MochiKit.DOM.Coordinates */
141 ['Coordinates', 'MochiKit.Style.Coordinates', '1.4'], // FIXME: broken
142 /** @id MochiKit.DOM.Dimensions */
143 ['Dimensions', 'MochiKit.Style.Dimensions', '1.4'] // FIXME: broken
144 ];
145
146 /** @id MochiKit.DOM.getViewportDimensions */
147 MochiKit.DOM.getViewportDimensions = new Function('' +
148 'if (!MochiKit["Style"]) {' +
149 ' throw new Error("This function has been deprecated and depends on MochiKit.Style.");' +
150 '}' +
151 'return MochiKit.Style.getViewportDimensions.apply(this, arguments);');
152
153 MochiKit.Base.update(MochiKit.DOM, {
154
155 /** @id MochiKit.DOM.currentWindow */
156 currentWindow: function () {
157 return MochiKit.DOM._window;
158 },
159
160 /** @id MochiKit.DOM.currentDocument */
161 currentDocument: function () {
162 return MochiKit.DOM._document;
163 },
164
165 /** @id MochiKit.DOM.withWindow */
166 withWindow: function (win, func) {
167 var self = MochiKit.DOM;
168 var oldDoc = self._document;
169 var oldWin = self._window;
170 var rval;
171 try {
172 self._window = win;
173 self._document = win.document;
174 rval = func();
175 } catch (e) {
176 self._window = oldWin;
177 self._document = oldDoc;
178 throw e;
179 }
180 self._window = oldWin;
181 self._document = oldDoc;
182 return rval;
183 },
184
185 /** @id MochiKit.DOM.formContents */
186 formContents: function (elem/* = document.body */) {
187 var names = [];
188 var values = [];
189 var m = MochiKit.Base;
190 var self = MochiKit.DOM;
191 if (typeof(elem) == "undefined" || elem === null) {
192 elem = self._document.body;
193 } else {
194 elem = self.getElement(elem);
195 }
196 m.nodeWalk(elem, function (elem) {
197 var name = elem.name;
198 if (m.isNotEmpty(name)) {
199 var tagName = elem.tagName.toUpperCase();
200 if (tagName === "INPUT"
201 && (elem.type == "radio" || elem.type == "checkbox")
202 && !elem.checked
203 ) {
204 return null;
205 }
206 if (tagName === "SELECT") {
207 if (elem.type == "select-one") {
208 if (elem.selectedIndex >= 0) {
209 var opt = elem.options[elem.selectedIndex];
210 var v = opt.value;
211 if (!v) {
212 var h = opt.outerHTML;
213 // internet explorer sure does suck.
214 if (h && !h.match(/^[^>]+\svalue\s*=/i)) {
215 v = opt.text;
216 }
217 }
218 names.push(name);
219 values.push(v);
220 return null;
221 }
222 // no form elements?
223 names.push(name);
224 values.push("");
225 return null;
226 } else {
227 var opts = elem.options;
228 if (!opts.length) {
229 names.push(name);
230 values.push("");
231 return null;
232 }
233 for (var i = 0; i < opts.length; i++) {
234 var opt = opts[i];
235 if (!opt.selected) {
236 continue;
237 }
238 var v = opt.value;
239 if (!v) {
240 var h = opt.outerHTML;
241 // internet explorer sure does suck.
242 if (h && !h.match(/^[^>]+\svalue\s*=/i)) {
243 v = opt.text;
244 }
245 }
246 names.push(name);
247 values.push(v);
248 }
249 return null;
250 }
251 }
252 if (tagName === "FORM" || tagName === "P" || tagName === "SPAN"
253 || tagName === "DIV"
254 ) {
255 return elem.childNodes;
256 }
257 names.push(name);
258 values.push(elem.value || '');
259 return null;
260 }
261 return elem.childNodes;
262 });
263 return [names, values];
264 },
265
266 /** @id MochiKit.DOM.withDocument */
267 withDocument: function (doc, func) {
268 var self = MochiKit.DOM;
269 var oldDoc = self._document;
270 var rval;
271 try {
272 self._document = doc;
273 rval = func();
274 } catch (e) {
275 self._document = oldDoc;
276 throw e;
277 }
278 self._document = oldDoc;
279 return rval;
280 },
281
282 /** @id MochiKit.DOM.registerDOMConverter */
283 registerDOMConverter: function (name, check, wrap, /* optional */override) {
284 MochiKit.DOM.domConverters.register(name, check, wrap, override);
285 },
286
287 /** @id MochiKit.DOM.coerceToDOM */
288 coerceToDOM: function (node, ctx) {
289 var m = MochiKit.Base;
290 var im = MochiKit.Iter;
291 var self = MochiKit.DOM;
292 if (im) {
293 var iter = im.iter;
294 var repeat = im.repeat;
295 var map = m.map;
296 }
297 var domConverters = self.domConverters;
298 var coerceToDOM = arguments.callee;
299 var NotFound = m.NotFound;
300 while (true) {
301 if (typeof(node) == 'undefined' || node === null) {
302 return null;
303 }
304 // this is a safari childNodes object, avoiding crashes w/ attr
305 // lookup
306 if (typeof(node) == "function" &&
307 typeof(node.length) == "number" &&
308 !(node instanceof Function)) {
309 node = im.list(node);
310 }
311 if (typeof(node.nodeType) != 'undefined' && node.nodeType > 0) {
312 return node;
313 }
314 if (typeof(node) == 'number' || typeof(node) == 'boolean') {
315 node = node.toString();
316 // FALL THROUGH
317 }
318 if (typeof(node) == 'string') {
319 return self._document.createTextNode(node);
320 }
321 if (typeof(node.__dom__) == 'function') {
322 node = node.__dom__(ctx);
323 continue;
324 }
325 if (typeof(node.dom) == 'function') {
326 node = node.dom(ctx);
327 continue;
328 }
329 if (typeof(node) == 'function') {
330 node = node.apply(ctx, [ctx]);
331 continue;
332 }
333
334 if (im) {
335 // iterable
336 var iterNodes = null;
337 try {
338 iterNodes = iter(node);
339 } catch (e) {
340 // pass
341 }
342 if (iterNodes) {
343 return map(coerceToDOM, iterNodes, repeat(ctx));
344 }
345 }
346
347 // adapter
348 try {
349 node = domConverters.match(node, ctx);
350 continue;
351 } catch (e) {
352 if (e != NotFound) {
353 throw e;
354 }
355 }
356
357 // fallback
358 return self._document.createTextNode(node.toString());
359 }
360 // mozilla warnings aren't too bright
361 return undefined;
362 },
363
364 /** @id MochiKit.DOM.isChildNode */
365 isChildNode: function (node, maybeparent) {
366 var self = MochiKit.DOM;
367 if (typeof(node) == "string") {
368 node = self.getElement(node);
369 }
370 if (typeof(maybeparent) == "string") {
371 maybeparent = self.getElement(maybeparent);
372 }
373 if (node === maybeparent) {
374 return true;
375 }
376 while (node && node.tagName.toUpperCase() != "BODY") {
377 node = node.parentNode;
378 if (node === maybeparent) {
379 return true;
380 }
381 }
382 return false;
383 },
384
385 /** @id MochiKit.DOM.setNodeAttribute */
386 setNodeAttribute: function (node, attr, value) {
387 var o = {};
388 o[attr] = value;
389 try {
390 return MochiKit.DOM.updateNodeAttributes(node, o);
391 } catch (e) {
392 // pass
393 }
394 return null;
395 },
396
397 /** @id MochiKit.DOM.getNodeAttribute */
398 getNodeAttribute: function (node, attr) {
399 var self = MochiKit.DOM;
400 var rename = self.attributeArray.renames[attr];
401 node = self.getElement(node);
402 try {
403 if (rename) {
404 return node[rename];
405 }
406 return node.getAttribute(attr);
407 } catch (e) {
408 // pass
409 }
410 return null;
411 },
412
413 /** @id MochiKit.DOM.removeNodeAttribute */
414 removeNodeAttribute: function (node, attr) {
415 var self = MochiKit.DOM;
416 var rename = self.attributeArray.renames[attr];
417 node = self.getElement(node);
418 try {
419 if (rename) {
420 return node[rename];
421 }
422 return node.removeAttribute(attr);
423 } catch (e) {
424 // pass
425 }
426 return null;
427 },
428
429 /** @id MochiKit.DOM.updateNodeAttributes */
430 updateNodeAttributes: function (node, attrs) {
431 var elem = node;
432 var self = MochiKit.DOM;
433 if (typeof(node) == 'string') {
434 elem = self.getElement(node);
435 }
436 if (attrs) {
437 var updatetree = MochiKit.Base.updatetree;
438 if (self.attributeArray.compliant) {
439 // not IE, good.
440 for (var k in attrs) {
441 var v = attrs[k];
442 if (typeof(v) == 'object' && typeof(elem[k]) == 'object') {
443 if (k == "style" && MochiKit.Style) {
444 MochiKit.Style.setStyle(elem, v);
445 } else {
446 updatetree(elem[k], v);
447 }
448 } else if (k.substring(0, 2) == "on") {
449 if (typeof(v) == "string") {
450 v = new Function(v);
451 }
452 elem[k] = v;
453 } else {
454 elem.setAttribute(k, v);
455 }
456 }
457 } else {
458 // IE is insane in the membrane
459 var renames = self.attributeArray.renames;
460 for (var k in attrs) {
461 v = attrs[k];
462 var renamed = renames[k];
463 if (k == "style" && typeof(v) == "string") {
464 elem.style.cssText = v;
465 } else if (typeof(renamed) == "string") {
466 elem[renamed] = v;
467 } else if (typeof(elem[k]) == 'object'
468 && typeof(v) == 'object') {
469 if (k == "style" && MochiKit.Style) {
470 MochiKit.Style.setStyle(elem, v);
471 } else {
472 updatetree(elem[k], v);
473 }
474 } else if (k.substring(0, 2) == "on") {
475 if (typeof(v) == "string") {
476 v = new Function(v);
477 }
478 elem[k] = v;
479 } else {
480 elem.setAttribute(k, v);
481 }
482 }
483 }
484 }
485 return elem;
486 },
487
488 /** @id MochiKit.DOM.appendChildNodes */
489 appendChildNodes: function (node/*, nodes...*/) {
490 var elem = node;
491 var self = MochiKit.DOM;
492 if (typeof(node) == 'string') {
493 elem = self.getElement(node);
494 }
495 var nodeStack = [
496 self.coerceToDOM(
497 MochiKit.Base.extend(null, arguments, 1),
498 elem
499 )
500 ];
501 var concat = MochiKit.Base.concat;
502 while (nodeStack.length) {
503 var n = nodeStack.shift();
504 if (typeof(n) == 'undefined' || n === null) {
505 // pass
506 } else if (typeof(n.nodeType) == 'number') {
507 elem.appendChild(n);
508 } else {
509 nodeStack = concat(n, nodeStack);
510 }
511 }
512 return elem;
513 },
514
515
516 /** @id MochiKit.DOM.insertSiblingNodesBefore */
517 insertSiblingNodesBefore: function (node/*, nodes...*/) {
518 var elem = node;
519 var self = MochiKit.DOM;
520 if (typeof(node) == 'string') {
521 elem = self.getElement(node);
522 }
523 var nodeStack = [
524 self.coerceToDOM(
525 MochiKit.Base.extend(null, arguments, 1),
526 elem
527 )
528 ];
529 var parentnode = elem.parentNode;
530 var concat = MochiKit.Base.concat;
531 while (nodeStack.length) {
532 var n = nodeStack.shift();
533 if (typeof(n) == 'undefined' || n === null) {
534 // pass
535 } else if (typeof(n.nodeType) == 'number') {
536 parentnode.insertBefore(n, elem);
537 } else {
538 nodeStack = concat(n, nodeStack);
539 }
540 }
541 return parentnode;
542 },
543
544 /** @id MochiKit.DOM.insertSiblingNodesAfter */
545 insertSiblingNodesAfter: function (node/*, nodes...*/) {
546 var elem = node;
547 var self = MochiKit.DOM;
548
549 if (typeof(node) == 'string') {
550 elem = self.getElement(node);
551 }
552 var nodeStack = [
553 self.coerceToDOM(
554 MochiKit.Base.extend(null, arguments, 1),
555 elem
556 )
557 ];
558
559 if (elem.nextSibling) {
560 return self.insertSiblingNodesBefore(elem.nextSibling, nodeStack);
561 }
562 else {
563 return self.appendChildNodes(elem.parentNode, nodeStack);
564 }
565 },
566
567 /** @id MochiKit.DOM.replaceChildNodes */
568 replaceChildNodes: function (node/*, nodes...*/) {
569 var elem = node;
570 var self = MochiKit.DOM;
571 if (typeof(node) == 'string') {
572 elem = self.getElement(node);
573 arguments[0] = elem;
574 }
575 var child;
576 while ((child = elem.firstChild)) {
577 elem.removeChild(child);
578 }
579 if (arguments.length < 2) {
580 return elem;
581 } else {
582 return self.appendChildNodes.apply(this, arguments);
583 }
584 },
585
586 /** @id MochiKit.DOM.createDOM */
587 createDOM: function (name, attrs/*, nodes... */) {
588 var elem;
589 var self = MochiKit.DOM;
590 var m = MochiKit.Base;
591 if (typeof(attrs) == "string" || typeof(attrs) == "number") {
592 var args = m.extend([name, null], arguments, 1);
593 return arguments.callee.apply(this, args);
594 }
595 if (typeof(name) == 'string') {
596 // Internet Explorer is dumb
597 var xhtml = self._xhtml;
598 if (attrs && !self.attributeArray.compliant) {
599 // http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/name_2.asp
600 var contents = "";
601 if ('name' in attrs) {
602 contents += ' name="' + self.escapeHTML(attrs.name) + '"';
603 }
604 if (name == 'input' && 'type' in attrs) {
605 contents += ' type="' + self.escapeHTML(attrs.type) + '"';
606 }
607 if (contents) {
608 name = "<" + name + contents + ">";
609 xhtml = false;
610 }
611 }
612 var d = self._document;
613 if (xhtml && d === document) {
614 elem = d.createElementNS("http://www.w3.org/1999/xhtml", name);
615 } else {
616 elem = d.createElement(name);
617 }
618 } else {
619 elem = name;
620 }
621 if (attrs) {
622 self.updateNodeAttributes(elem, attrs);
623 }
624 if (arguments.length <= 2) {
625 return elem;
626 } else {
627 var args = m.extend([elem], arguments, 2);
628 return self.appendChildNodes.apply(this, args);
629 }
630 },
631
632 /** @id MochiKit.DOM.createDOMFunc */
633 createDOMFunc: function (/* tag, attrs, *nodes */) {
634 var m = MochiKit.Base;
635 return m.partial.apply(
636 this,
637 m.extend([MochiKit.DOM.createDOM], arguments)
638 );
639 },
640
641 /** @id MochiKit.DOM.removeElement */
642 removeElement: function (elem) {
643 var e = MochiKit.DOM.getElement(elem);
644 e.parentNode.removeChild(e);
645 return e;
646 },
647
648 /** @id MochiKit.DOM.swapDOM */
649 swapDOM: function (dest, src) {
650 var self = MochiKit.DOM;
651 dest = self.getElement(dest);
652 var parent = dest.parentNode;
653 if (src) {
654 src = self.getElement(src);
655 parent.replaceChild(src, dest);
656 } else {
657 parent.removeChild(dest);
658 }
659 return src;
660 },
661
662 /** @id MochiKit.DOM.getElement */
663 getElement: function (id) {
664 var self = MochiKit.DOM;
665 if (arguments.length == 1) {
666 return ((typeof(id) == "string") ?
667 self._document.getElementById(id) : id);
668 } else {
669 return MochiKit.Base.map(self.getElement, arguments);
670 }
671 },
672
673 /** @id MochiKit.DOM.getElementsByTagAndClassName */
674 getElementsByTagAndClassName: function (tagName, className,
675 /* optional */parent) {
676 var self = MochiKit.DOM;
677 if (typeof(tagName) == 'undefined' || tagName === null) {
678 tagName = '*';
679 }
680 if (typeof(parent) == 'undefined' || parent === null) {
681 parent = self._document;
682 }
683 parent = self.getElement(parent);
684 var children = (parent.getElementsByTagName(tagName)
685 || self._document.all);
686 if (typeof(className) == 'undefined' || className === null) {
687 return MochiKit.Base.extend(null, children);
688 }
689
690 var elements = [];
691 for (var i = 0; i < children.length; i++) {
692 var child = children[i];
693 var cls = child.className;
694 if (!cls) {
695 continue;
696 }
697 var classNames = cls.split(' ');
698 for (var j = 0; j < classNames.length; j++) {
699 if (classNames[j] == className) {
700 elements.push(child);
701 break;
702 }
703 }
704 }
705
706 return elements;
707 },
708
709 _newCallStack: function (path, once) {
710 var rval = function () {
711 var callStack = arguments.callee.callStack;
712 for (var i = 0; i < callStack.length; i++) {
713 if (callStack[i].apply(this, arguments) === false) {
714 break;
715 }
716 }
717 if (once) {
718 try {
719 this[path] = null;
720 } catch (e) {
721 // pass
722 }
723 }
724 };
725 rval.callStack = [];
726 return rval;
727 },
728
729 /** @id MochiKit.DOM.addToCallStack */
730 addToCallStack: function (target, path, func, once) {
731 var self = MochiKit.DOM;
732 var existing = target[path];
733 var regfunc = existing;
734 if (!(typeof(existing) == 'function'
735 && typeof(existing.callStack) == "object"
736 && existing.callStack !== null)) {
737 regfunc = self._newCallStack(path, once);
738 if (typeof(existing) == 'function') {
739 regfunc.callStack.push(existing);
740 }
741 target[path] = regfunc;
742 }
743 regfunc.callStack.push(func);
744 },
745
746 /** @id MochiKit.DOM.addLoadEvent */
747 addLoadEvent: function (func) {
748 var self = MochiKit.DOM;
749 self.addToCallStack(self._window, "onload", func, true);
750
751 },
752
753 /** @id MochiKit.DOM.focusOnLoad */
754 focusOnLoad: function (element) {
755 var self = MochiKit.DOM;
756 self.addLoadEvent(function () {
757 element = self.getElement(element);
758 if (element) {
759 element.focus();
760 }
761 });
762 },
763
764 /** @id MochiKit.DOM.setElementClass */
765 setElementClass: function (element, className) {
766 var self = MochiKit.DOM;
767 var obj = self.getElement(element);
768 if (self.attributeArray.compliant) {
769 obj.setAttribute("class", className);
770 } else {
771 obj.setAttribute("className", className);
772 }
773 },
774
775 /** @id MochiKit.DOM.toggleElementClass */
776 toggleElementClass: function (className/*, element... */) {
777 var self = MochiKit.DOM;
778 for (var i = 1; i < arguments.length; i++) {
779 var obj = self.getElement(arguments[i]);
780 if (!self.addElementClass(obj, className)) {
781 self.removeElementClass(obj, className);
782 }
783 }
784 },
785
786 /** @id MochiKit.DOM.addElementClass */
787 addElementClass: function (element, className) {
788 var self = MochiKit.DOM;
789 var obj = self.getElement(element);
790 var cls = obj.className;
791 // trivial case, no className yet
792 if (cls == undefined || cls.length === 0) {
793 self.setElementClass(obj, className);
794 return true;
795 }
796 // the other trivial case, already set as the only class
797 if (cls == className) {
798 return false;
799 }
800 var classes = cls.split(" ");
801 for (var i = 0; i < classes.length; i++) {
802 // already present
803 if (classes[i] == className) {
804 return false;
805 }
806 }
807 // append class
808 self.setElementClass(obj, cls + " " + className);
809 return true;
810 },
811
812 /** @id MochiKit.DOM.removeElementClass */
813 removeElementClass: function (element, className) {
814 var self = MochiKit.DOM;
815 var obj = self.getElement(element);
816 var cls = obj.className;
817 // trivial case, no className yet
818 if (cls == undefined || cls.length === 0) {
819 return false;
820 }
821 // other trivial case, set only to className
822 if (cls == className) {
823 self.setElementClass(obj, "");
824 return true;
825 }
826 var classes = cls.split(" ");
827 for (var i = 0; i < classes.length; i++) {
828 // already present
829 if (classes[i] == className) {
830 // only check sane case where the class is used once
831 classes.splice(i, 1);
832 self.setElementClass(obj, classes.join(" "));
833 return true;
834 }
835 }
836 // not found
837 return false;
838 },
839
840 /** @id MochiKit.DOM.swapElementClass */
841 swapElementClass: function (element, fromClass, toClass) {
842 var obj = MochiKit.DOM.getElement(element);
843 var res = MochiKit.DOM.removeElementClass(obj, fromClass);
844 if (res) {
845 MochiKit.DOM.addElementClass(obj, toClass);
846 }
847 return res;
848 },
849
850 /** @id MochiKit.DOM.hasElementClass */
851 hasElementClass: function (element, className/*...*/) {
852 var obj = MochiKit.DOM.getElement(element);
853 var cls = obj.className;
854 if (!cls) {
855 return false;
856 }
857 var classes = cls.split(" ");
858 for (var i = 1; i < arguments.length; i++) {
859 var good = false;
860 for (var j = 0; j < classes.length; j++) {
861 if (classes[j] == arguments[i]) {
862 good = true;
863 break;
864 }
865 }
866 if (!good) {
867 return false;
868 }
869 }
870 return true;
871 },
872
873 /** @id MochiKit.DOM.escapeHTML */
874 escapeHTML: function (s) {
875 return s.replace(/&/g, "&amp;"
876 ).replace(/"/g, "&quot;"
877 ).replace(/</g, "&lt;"
878 ).replace(/>/g, "&gt;");
879 },
880
881 /** @id MochiKit.DOM.toHTML */
882 toHTML: function (dom) {
883 return MochiKit.DOM.emitHTML(dom).join("");
884 },
885
886 /** @id MochiKit.DOM.emitHTML */
887 emitHTML: function (dom, /* optional */lst) {
888 if (typeof(lst) == 'undefined' || lst === null) {
889 lst = [];
890 }
891 // queue is the call stack, we're doing this non-recursively
892 var queue = [dom];
893 var self = MochiKit.DOM;
894 var escapeHTML = self.escapeHTML;
895 var attributeArray = self.attributeArray;
896 while (queue.length) {
897 dom = queue.pop();
898 if (typeof(dom) == 'string') {
899 lst.push(dom);
900 } else if (dom.nodeType == 1) {
901 // we're not using higher order stuff here
902 // because safari has heisenbugs.. argh.
903 //
904 // I think it might have something to do with
905 // garbage collection and function calls.
906 lst.push('<' + dom.tagName.toLowerCase());
907 var attributes = [];
908 var domAttr = attributeArray(dom);
909 for (var i = 0; i < domAttr.length; i++) {
910 var a = domAttr[i];
911 attributes.push([
912 " ",
913 a.name,
914 '="',
915 escapeHTML(a.value),
916 '"'
917 ]);
918 }
919 attributes.sort();
920 for (i = 0; i < attributes.length; i++) {
921 var attrs = attributes[i];
922 for (var j = 0; j < attrs.length; j++) {
923 lst.push(attrs[j]);
924 }
925 }
926 if (dom.hasChildNodes()) {
927 lst.push(">");
928 // queue is the FILO call stack, so we put the close tag
929 // on first
930 queue.push("</" + dom.tagName.toLowerCase() + ">");
931 var cnodes = dom.childNodes;
932 for (i = cnodes.length - 1; i >= 0; i--) {
933 queue.push(cnodes[i]);
934 }
935 } else {
936 lst.push('/>');
937 }
938 } else if (dom.nodeType == 3) {
939 lst.push(escapeHTML(dom.nodeValue));
940 }
941 }
942 return lst;
943 },
944
945 /** @id MochiKit.DOM.scrapeText */
946 scrapeText: function (node, /* optional */asArray) {
947 var rval = [];
948 (function (node) {
949 var cn = node.childNodes;
950 if (cn) {
951 for (var i = 0; i < cn.length; i++) {
952 arguments.callee.call(this, cn[i]);
953 }
954 }
955 var nodeValue = node.nodeValue;
956 if (typeof(nodeValue) == 'string') {
957 rval.push(nodeValue);
958 }
959 })(MochiKit.DOM.getElement(node));
960 if (asArray) {
961 return rval;
962 } else {
963 return rval.join("");
964 }
965 },
966
967 /** @id MochiKit.DOM.removeEmptyTextNodes */
968 removeEmptyTextNodes: function (element) {
969 element = MochiKit.DOM.getElement(element);
970 for (var i = 0; i < element.childNodes.length; i++) {
971 var node = element.childNodes[i];
972 if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) {
973 node.parentNode.removeChild(node);
974 }
975 }
976 },
977
978 /** @id MochiKit.DOM.makeClipping */
979 makeClipping: function (element) {
980 element = MochiKit.DOM.getElement(element);
981 var oldOverflow = element.style.overflow;
982 if ((MochiKit.Style.getStyle(element, 'overflow') || 'visible') != 'hidden') {
983 element.style.overflow = 'hidden';
984 }
985 return oldOverflow;
986 },
987
988 /** @id MochiKit.DOM.undoClipping */
989 undoClipping: function (element, overflow) {
990 element = MochiKit.DOM.getElement(element);
991 if (!overflow) {
992 return;
993 }
994 element.style.overflow = overflow;
995 },
996
997 /** @id MochiKit.DOM.makePositioned */
998 makePositioned: function (element) {
999 element = MochiKit.DOM.getElement(element);
1000 var pos = MochiKit.Style.getStyle(element, 'position');
1001 if (pos == 'static' || !pos) {
1002 element.style.position = 'relative';
1003 // Opera returns the offset relative to the positioning context,
1004 // when an element is position relative but top and left have
1005 // not been defined
1006 if (/Opera/.test(navigator.userAgent)) {
1007 element.style.top = 0;
1008 element.style.left = 0;
1009 }
1010 }
1011 },
1012
1013 /** @id MochiKit.DOM.undoPositioned */
1014 undoPositioned: function (element) {
1015 element = MochiKit.DOM.getElement(element);
1016 if (element.style.position == 'relative') {
1017 element.style.position = element.style.top = element.style.left = element.style.bottom = element.style.right = '';
1018 }
1019 },
1020
1021 /** @id MochiKit.DOM.getFirstElementByTagAndClassName */
1022 getFirstElementByTagAndClassName: function (tagName, className,
1023 /* optional */parent) {
1024 var self = MochiKit.DOM;
1025 if (typeof(tagName) == 'undefined' || tagName === null) {
1026 tagName = '*';
1027 }
1028 if (typeof(parent) == 'undefined' || parent === null) {
1029 parent = self._document;
1030 }
1031 parent = self.getElement(parent);
1032 var children = (parent.getElementsByTagName(tagName)
1033 || self._document.all);
1034 if (typeof(className) == 'undefined' || className === null) {
1035 return children[0];
1036 }
1037
1038 for (var i = 0; i < children.length; i++) {
1039 var child = children[i];
1040 var classNames = child.className.split(' ');
1041 for (var j = 0; j < classNames.length; j++) {
1042 if (classNames[j] == className) {
1043 return child;
1044 }
1045 }
1046 }
1047 },
1048
1049 /** @id MochiKit.DOM.getFirstParentByTagAndClassName */
1050 getFirstParentByTagAndClassName: function (elem, tagName, className) {
1051 var self = MochiKit.DOM;
1052 elem = self.getElement(elem);
1053 if (typeof(tagName) == 'undefined' || tagName === null) {
1054 tagName = '*';
1055 } else {
1056 tagName = tagName.toUpperCase();
1057 }
1058 if (typeof(className) == 'undefined' || className === null) {
1059 className = null;
1060 }
1061
1062 var classList = '';
1063 var curTagName = '';
1064 while (elem && elem.tagName) {
1065 elem = elem.parentNode;
1066 if (tagName == '*' && className === null) {
1067 return elem;
1068 }
1069 classList = elem.className.split(' ');
1070 curTagName = elem.tagName.toUpperCase();
1071 if (className === null && tagName == curTagName) {
1072 return elem;
1073 } else if (className !== null) {
1074 for (var i = 0; i < classList.length; i++) {
1075 if (tagName == '*' && classList[i] == className) {
1076 return elem;
1077 } else if (tagName == curTagName && classList[i] == className) {
1078 return elem;
1079 }
1080 }
1081 }
1082 }
1083 return elem;
1084 },
1085
1086 /** @id MochiKit.DOM.isParent */
1087 isParent: function (child, element) {
1088 if (!child.parentNode || child == element) {
1089 return false;
1090 }
1091
1092 if (child.parentNode == element) {
1093 return true;
1094 }
1095
1096 return MochiKit.DOM.isParent(child.parentNode, element);
1097 },
1098
1099 __new__: function (win) {
1100
1101 var m = MochiKit.Base;
1102 if (typeof(document) != "undefined") {
1103 this._document = document;
1104 var kXULNSURI = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
1105 this._xhtml = (document.documentElement &&
1106 document.createElementNS &&
1107 document.documentElement.namespaceURI === kXULNSURI);
1108 } else if (MochiKit.MockDOM) {
1109 this._document = MochiKit.MockDOM.document;
1110 }
1111 this._window = win;
1112
1113 this.domConverters = new m.AdapterRegistry();
1114
1115 var __tmpElement = this._document.createElement("span");
1116 var attributeArray;
1117 if (__tmpElement && __tmpElement.attributes &&
1118 __tmpElement.attributes.length > 0) {
1119 // for braindead browsers (IE) that insert extra junk
1120 var filter = m.filter;
1121 attributeArray = function (node) {
1122 return filter(attributeArray.ignoreAttrFilter, node.attributes);
1123 };
1124 attributeArray.ignoreAttr = {};
1125 var attrs = __tmpElement.attributes;
1126 var ignoreAttr = attributeArray.ignoreAttr;
1127 for (var i = 0; i < attrs.length; i++) {
1128 var a = attrs[i];
1129 ignoreAttr[a.name] = a.value;
1130 }
1131 attributeArray.ignoreAttrFilter = function (a) {
1132 return (attributeArray.ignoreAttr[a.name] != a.value);
1133 };
1134 attributeArray.compliant = false;
1135 attributeArray.renames = {
1136 "class": "className",
1137 "checked": "defaultChecked",
1138 "usemap": "useMap",
1139 "for": "htmlFor",
1140 "readonly": "readOnly",
1141 "colspan": "colSpan",
1142 "bgcolor": "bgColor",
1143 "cellspacing": "cellSpacing",
1144 "cellpadding": "cellPadding"
1145 };
1146 } else {
1147 attributeArray = function (node) {
1148 /***
1149
1150 Return an array of attributes for a given node,
1151 filtering out attributes that don't belong for
1152 that are inserted by "Certain Browsers".
1153
1154 ***/
1155 return node.attributes;
1156 };
1157 attributeArray.compliant = true;
1158 attributeArray.renames = {};
1159 }
1160 this.attributeArray = attributeArray;
1161
1162 // FIXME: this really belongs in Base, and could probably be cleaner
1163 var _deprecated = function(fromModule, arr) {
1164 var modules = arr[1].split('.');
1165 var str = '';
1166 var obj = {};
1167
1168 str += 'if (!MochiKit.' + modules[1] + ') { throw new Error("';
1169 str += 'This function has been deprecated and depends on MochiKit.';
1170 str += modules[1] + '.");}';
1171 str += 'return MochiKit.' + modules[1] + '.' + arr[0];
1172 str += '.apply(this, arguments);';
1173
1174 obj[modules[2]] = new Function(str);
1175 MochiKit.Base.update(MochiKit[fromModule], obj);
1176 }
1177 for (var i; i < MochiKit.DOM.DEPRECATED.length; i++) {
1178 _deprecated('DOM', MochiKit.DOM.DEPRECATED[i]);
1179 }
1180
1181 // shorthand for createDOM syntax
1182 var createDOMFunc = this.createDOMFunc;
1183 /** @id MochiKit.DOM.UL */
1184 this.UL = createDOMFunc("ul");
1185 /** @id MochiKit.DOM.OL */
1186 this.OL = createDOMFunc("ol");
1187 /** @id MochiKit.DOM.LI */
1188 this.LI = createDOMFunc("li");
1189 /** @id MochiKit.DOM.TD */
1190 this.TD = createDOMFunc("td");
1191 /** @id MochiKit.DOM.TR */
1192 this.TR = createDOMFunc("tr");
1193 /** @id MochiKit.DOM.TBODY */
1194 this.TBODY = createDOMFunc("tbody");
1195 /** @id MochiKit.DOM.THEAD */
1196 this.THEAD = createDOMFunc("thead");
1197 /** @id MochiKit.DOM.TFOOT */
1198 this.TFOOT = createDOMFunc("tfoot");
1199 /** @id MochiKit.DOM.TABLE */
1200 this.TABLE = createDOMFunc("table");
1201 /** @id MochiKit.DOM.TH */
1202 this.TH = createDOMFunc("th");
1203 /** @id MochiKit.DOM.INPUT */
1204 this.INPUT = createDOMFunc("input");
1205 /** @id MochiKit.DOM.SPAN */
1206 this.SPAN = createDOMFunc("span");
1207 /** @id MochiKit.DOM.A */
1208 this.A = createDOMFunc("a");
1209 /** @id MochiKit.DOM.DIV */
1210 this.DIV = createDOMFunc("div");
1211 /** @id MochiKit.DOM.IMG */
1212 this.IMG = createDOMFunc("img");
1213 /** @id MochiKit.DOM.BUTTON */
1214 this.BUTTON = createDOMFunc("button");
1215 /** @id MochiKit.DOM.TT */
1216 this.TT = createDOMFunc("tt");
1217 /** @id MochiKit.DOM.PRE */
1218 this.PRE = createDOMFunc("pre");
1219 /** @id MochiKit.DOM.H1 */
1220 this.H1 = createDOMFunc("h1");
1221 /** @id MochiKit.DOM.H2 */
1222 this.H2 = createDOMFunc("h2");
1223 /** @id MochiKit.DOM.H3 */
1224 this.H3 = createDOMFunc("h3");
1225 /** @id MochiKit.DOM.BR */
1226 this.BR = createDOMFunc("br");
1227 /** @id MochiKit.DOM.HR */
1228 this.HR = createDOMFunc("hr");
1229 /** @id MochiKit.DOM.LABEL */
1230 this.LABEL = createDOMFunc("label");
1231 /** @id MochiKit.DOM.TEXTAREA */
1232 this.TEXTAREA = createDOMFunc("textarea");
1233 /** @id MochiKit.DOM.FORM */
1234 this.FORM = createDOMFunc("form");
1235 /** @id MochiKit.DOM.P */
1236 this.P = createDOMFunc("p");
1237 /** @id MochiKit.DOM.SELECT */
1238 this.SELECT = createDOMFunc("select");
1239 /** @id MochiKit.DOM.OPTION */
1240 this.OPTION = createDOMFunc("option");
1241 /** @id MochiKit.DOM.OPTGROUP */
1242 this.OPTGROUP = createDOMFunc("optgroup");
1243 /** @id MochiKit.DOM.LEGEND */
1244 this.LEGEND = createDOMFunc("legend");
1245 /** @id MochiKit.DOM.FIELDSET */
1246 this.FIELDSET = createDOMFunc("fieldset");
1247 /** @id MochiKit.DOM.STRONG */
1248 this.STRONG = createDOMFunc("strong");
1249 /** @id MochiKit.DOM.CANVAS */
1250 this.CANVAS = createDOMFunc("canvas");
1251
1252 /** @id MochiKit.DOM.$ */
1253 this.$ = this.getElement;
1254
1255 this.EXPORT_TAGS = {
1256 ":common": this.EXPORT,
1257 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
1258 };
1259
1260 m.nameFunctions(this);
1261
1262 }
1263 });
1264
1265
1266 MochiKit.DOM.__new__(((typeof(window) == "undefined") ? this : window));
1267
1268 //
1269 // XXX: Internet Explorer blows
1270 //
1271 if (MochiKit.__export__) {
1272 withWindow = MochiKit.DOM.withWindow;
1273 withDocument = MochiKit.DOM.withDocument;
1274 }
1275
1276 MochiKit.Base._exportSymbols(this, MochiKit.DOM);