1 // small but works-for-me stuff for testing javascripts
2 // not ready for "production" use
4 Object.inspect = function(obj) {
7 if(typeof obj in ["string","number"]) {
11 if(typeof obj[property]!="function")
12 info.push(property + ' => ' +
13 (typeof obj[property] == "string" ?
14 '"' + obj[property] + '"' :
18 return ("'" + obj + "' #" + typeof obj +
19 ": {" + info.join(", ") + "}");
22 // borrowed from http://www.schuerig.de/michael/javascript/stdext.js
23 // Copyright (c) 2005, Michael Schuerig, michael@schuerig.de
25 Array.flatten = function(array, excludeUndefined) {
26 if (excludeUndefined === undefined) {
27 excludeUndefined = false;
30 var len = array.length;
31 for (var i = 0; i < len; i++) {
33 if (el instanceof Array) {
34 var flat = el.flatten(excludeUndefined);
35 result = result.concat(flat);
36 } else if (!excludeUndefined || el != undefined) {
43 if (!Array.prototype.flatten) {
44 Array.prototype.flatten = function(excludeUndefined) {
45 return Array.flatten(this, excludeUndefined);
49 /*--------------------------------------------------------------------------*/
52 node: function(elementName) {
53 var element = document.createElement('div');
55 "<" + elementName + "></" + elementName + ">";
57 // attributes (or text)
59 if(this._isStringOrNumber(arguments[1]) ||
60 (arguments[1] instanceof Array)) {
61 this._children(element.firstChild, arguments[1]);
63 var attrs = this._attributes(arguments[1]);
65 element.innerHTML = "<" +elementName + " " +
66 attrs + "></" + elementName + ">";
69 // text, or array of children
71 this._children(element.firstChild, arguments[2]);
73 return element.firstChild;
75 _text: function(text) {
76 return document.createTextNode(text);
78 _attributes: function(attributes) {
80 for(attribute in attributes)
81 attrs.push((attribute=='className' ? 'class' : attribute) +
82 '="' + attributes[attribute].toString().escapeHTML() + '"');
83 return attrs.join(" ");
85 _children: function(element, children) {
86 if(typeof children=='object') { // array can hold nodes and text
87 children = children.flatten();
88 for(var i = 0; i<children.length; i++)
89 if(typeof children[i]=='object')
90 element.appendChild(children[i]);
92 if(this._isStringOrNumber(children[i]))
93 element.appendChild(this._text(children[i]));
95 if(this._isStringOrNumber(children))
96 element.appendChild(this._text(children));
98 _isStringOrNumber: function(param) {
99 return(typeof param=='string' || typeof param=='number');
103 /* ------------- element ext -------------- */
105 // adapted from http://dhtmlkitchen.com/learn/js/setstyle/index4.jsp
106 // note: Safari return null on elements with display:none; see http://bugzilla.opendarwin.org/show_bug.cgi?id=4125
107 // instead of "auto" values returns null so it's easier to use with || constructs
109 String.prototype.camelize = function() {
110 var oStringList = this.split('-');
111 if(oStringList.length == 1)
112 return oStringList[0];
113 var ret = this.indexOf("-") == 0 ?
114 oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) : oStringList[0];
115 for(var i = 1, len = oStringList.length; i < len; i++){
116 var s = oStringList[i];
117 ret += s.charAt(0).toUpperCase() + s.substring(1)
122 Element.getStyle = function(element, style) {
123 element = $(element);
124 var value = element.style[style.camelize()];
126 if(document.defaultView && document.defaultView.getComputedStyle) {
127 var css = document.defaultView.getComputedStyle(element, null);
128 value = (css!=null) ? css.getPropertyValue(style) : null;
129 } else if(element.currentStyle) {
130 value = element.currentStyle[style.camelize()];
132 if(value=='auto') value = null;
136 Element.makePositioned = function(element) {
137 element = $(element);
138 if(Element.getStyle(element, 'position')=='static')
139 element.style.position = "relative";
142 Element.makeClipping = function(element) {
143 element = $(element);
144 element._overflow = Element.getStyle(element, 'overflow') || 'visible';
145 if(element._overflow!='hidden') element.style.overflow = 'hidden';
148 Element.undoClipping = function(element) {
149 element = $(element);
150 if(element._overflow!='hidden') element.style.overflow = element._overflow;
153 Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
154 var children = $(element).childNodes;
156 var classtest = new RegExp("^([^ ]+ )*" + ignoreclass+ "( [^ ]+)*$","i");
158 for (var i = 0; i < children.length; i++) {
159 if(children[i].nodeType==3) {
160 text+=children[i].nodeValue;
162 if((!children[i].className.match(classtest)) && children[i].hasChildNodes())
163 text += Element.collectTextNodesIgnoreClass(children[i], ignoreclass);
170 /*--------------------------------------------------------------------------*/
172 Position.positionedOffset = function(element) {
173 var valueT = 0, valueL = 0;
175 valueT += element.offsetTop || 0;
176 valueL += element.offsetLeft || 0;
177 element = element.offsetParent;
179 p = Element.getStyle(element,'position');
180 if(p == 'relative' || p == 'absolute') break;
183 return [valueL, valueT];
186 // Safari returns margins on body which is incorrect if the child is absolutely positioned.
187 // for performance reasons, we create a specialized version of Position.positionedOffset for
190 if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
191 Position.cumulativeOffset = function(element) {
192 var valueT = 0, valueL = 0;
194 valueT += element.offsetTop || 0;
195 valueL += element.offsetLeft || 0;
197 if (element.offsetParent==document.body)
198 if (Element.getStyle(element,'position')=='absolute') break;
200 element = element.offsetParent;
202 return [valueL, valueT];
206 Position.page = function(forElement) {
207 if(element == document.body) return [0, 0];
208 var valueT = 0, valueL = 0;
210 var element = forElement;
212 valueT += element.offsetTop || 0;
213 valueL += element.offsetLeft || 0;
216 if (element.offsetParent==document.body)
217 if (Element.getStyle(element,'position')=='absolute') break;
219 } while (element = element.offsetParent);
221 element = forElement;
223 valueT -= element.scrollTop || 0;
224 valueL -= element.scrollLeft || 0;
225 } while (element = element.parentNode);
227 return [valueL, valueT];
230 // elements with display:none don't return an offsetParent,
231 // fall back to manual calculation
232 Position.offsetParent = function(element) {
233 if(element.offsetParent) return element.offsetParent;
234 if(element == document.body) return element;
236 while ((element = element.parentNode) && element != document.body)
237 if (Element.getStyle(element,'position')!='static')
240 return document.body;
243 Position.clone = function(source, target) {
244 var options = Object.extend({
251 }, arguments[2] || {})
253 // find page position of source
255 var p = Position.page(source);
257 // find coordinate system to use
261 // delta [0,0] will do fine with position: fixed elements,
262 // position:absolute needs offsetParent deltas
263 if (Element.getStyle(target,'position') == 'absolute') {
264 parent = Position.offsetParent(target);
265 delta = Position.page(parent);
268 // correct by body offsets (fixes Safari)
269 if (parent==document.body) {
270 delta[0] -= document.body.offsetLeft;
271 delta[1] -= document.body.offsetTop;
275 if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + "px";
276 if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + "px";
277 if(options.setWidth) target.style.width = source.offsetWidth + "px";
278 if(options.setHeight) target.style.height = source.offsetHeight + "px";
281 Position.absolutize = function(element) {
282 element = $(element);
283 if(element.style.position=='absolute') return;
286 var offsets = Position.positionedOffset(element);
287 var top = offsets[1];
288 var left = offsets[0];
289 var width = element.clientWidth;
290 var height = element.clientHeight;
292 element._originalLeft = left - parseFloat(element.style.left || 0);
293 element._originalTop = top - parseFloat(element.style.top || 0);
294 element._originalWidth = element.style.width;
295 element._originalHeight = element.style.height;
297 element.style.position = 'absolute';
298 element.style.top = top + 'px';;
299 element.style.left = left + 'px';;
300 element.style.width = width + 'px';;
301 element.style.height = height + 'px';;
304 Position.relativize = function(element) {
305 element = $(element);
306 if(element.style.position=='relative') return;
309 element.style.position = 'relative';
310 var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
311 var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
313 element.style.top = top + 'px';
314 element.style.left = left + 'px';
315 element.style.height = element._originalHeight;
316 element.style.width = element._originalWidth;
319 /*--------------------------------------------------------------------------*/
322 // Element.toggleClass(element, className) toggles the class being on/off
323 // Element.toggleClass(element, className1, className2) toggles between both classes,
324 // defaulting to className1 if neither exist
325 toggle: function(element, className) {
326 if(Element.Class.has(element, className)) {
327 Element.Class.remove(element, className);
328 if(arguments.length == 3) Element.Class.add(element, arguments[2]);
330 Element.Class.add(element, className);
331 if(arguments.length == 3) Element.Class.remove(element, arguments[2]);
335 // gets space-delimited classnames of an element as an array
336 get: function(element) {
337 element = $(element);
338 return element.className.split(' ');
341 // functions adapted from original functions by Gavin Kistner
342 remove: function(element) {
343 element = $(element);
345 for(var i = 1; i < arguments.length; i++) {
346 regEx = new RegExp("(^|\\s)" + arguments[i] + "(\\s|$)", 'g');
347 element.className = element.className.replace(regEx, '')
351 add: function(element) {
352 element = $(element);
353 for(var i = 1; i < arguments.length; i++) {
354 Element.Class.remove(element, arguments[i]);
355 element.className += (element.className.length > 0 ? ' ' : '') + arguments[i];
359 // returns true if all given classes exist in said element
360 has: function(element) {
361 element = $(element);
362 if(!element || !element.className) return false;
364 for(var i = 1; i < arguments.length; i++) {
365 if((typeof arguments[i] == 'object') &&
366 (arguments[i].constructor == Array)) {
367 for(var j = 0; j < arguments[i].length; j++) {
368 regEx = new RegExp("(^|\\s)" + arguments[i][j] + "(\\s|$)");
369 if(!regEx.test(element.className)) return false;
372 regEx = new RegExp("(^|\\s)" + arguments[i] + "(\\s|$)");
373 if(!regEx.test(element.className)) return false;
379 // expects arrays of strings and/or strings as optional paramters
380 // Element.Class.has_any(element, ['classA','classB','classC'], 'classD')
381 has_any: function(element) {
382 element = $(element);
383 if(!element || !element.className) return false;
385 for(var i = 1; i < arguments.length; i++) {
386 if((typeof arguments[i] == 'object') &&
387 (arguments[i].constructor == Array)) {
388 for(var j = 0; j < arguments[i].length; j++) {
389 regEx = new RegExp("(^|\\s)" + arguments[i][j] + "(\\s|$)");
390 if(regEx.test(element.className)) return true;
393 regEx = new RegExp("(^|\\s)" + arguments[i] + "(\\s|$)");
394 if(regEx.test(element.className)) return true;
400 childrenWith: function(element, className) {
401 var children = $(element).getElementsByTagName('*');
402 var elements = new Array();
404 for (var i = 0; i < children.length; i++) {
405 if (Element.Class.has(children[i], className)) {
406 elements.push(children[i]);
415 /*--------------------------------------------------------------------------*/
417 String.prototype.parseQuery = function() {
419 if(str.substring(0,1) == '?') {
420 str = this.substring(1);
423 var pairs = str.split('&');
424 for(var i = 0; i < pairs.length; i++) {
425 var pair = pairs[i].split('=');
426 result[pair[0]] = pair[1];