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 // This library is free software; you can redistribute it and/or
26 // modify it under the terms of the GNU Lesser General Public
27 // License as published by the Free Software Foundation; either
28 // version 2.1 of the License, or (at your option) any later version.
29 // See http://www.gnu.org/copyleft/lesser.html
31 Array
.flatten = function(array
, excludeUndefined
) {
32 if (excludeUndefined
=== undefined) {
33 excludeUndefined
= false;
36 var len
= array
.length
;
37 for (var i
= 0; i
< len
; i
++) {
39 if (el
instanceof Array
) {
40 var flat
= el
.flatten(excludeUndefined
);
41 result
= result
.concat(flat
);
42 } else if (!excludeUndefined
|| el
!= undefined) {
49 if (!Array
.prototype.flatten
) {
50 Array
.prototype.flatten = function(excludeUndefined
) {
51 return Array
.flatten(this, excludeUndefined
);
55 /*--------------------------------------------------------------------------*/
58 node: function(elementName
) {
59 var element
= document
.createElement('div');
61 "<" + elementName
+ "></" + elementName
+ ">";
63 // attributes (or text)
65 if(this._isStringOrNumber(arguments
[1]) ||
66 (arguments
[1] instanceof Array
)) {
67 this._children(element
.firstChild
, arguments
[1]);
69 var attrs
= this._attributes(arguments
[1]);
71 element
.innerHTML
= "<" +elementName
+ " " +
72 attrs
+ "></" + elementName
+ ">";
75 // text, or array of children
77 this._children(element
.firstChild
, arguments
[2]);
79 return element
.firstChild
;
81 _text: function(text
) {
82 return document
.createTextNode(text
);
84 _attributes: function(attributes
) {
86 for(attribute
in attributes
)
87 attrs
.push((attribute
=='className' ? 'class' : attribute
) +
88 '="' + attributes
[attribute
].toString().escapeHTML() + '"');
89 return attrs
.join(" ");
91 _children: function(element
, children
) {
92 if(typeof children
=='object') { // array can hold nodes and text
93 children
= children
.flatten();
94 for(var i
= 0; i
<children
.length
; i
++)
95 if(typeof children
[i
]=='object')
96 element
.appendChild(children
[i
]);
98 if(this._isStringOrNumber(children
[i
]))
99 element
.appendChild(this._text(children
[i
]));
101 if(this._isStringOrNumber(children
))
102 element
.appendChild(this._text(children
));
104 _isStringOrNumber: function(param
) {
105 return(typeof param
=='string' || typeof param
=='number');
109 /* ------------- element ext -------------- */
111 // adapted from http://dhtmlkitchen.com/learn/js/setstyle/index4.jsp
112 // note: Safari return null on elements with display:none; see http://bugzilla.opendarwin.org/show_bug.cgi?id=4125
113 // instead of "auto" values returns null so it's easier to use with || constructs
115 String
.prototype.camelize = function() {
116 var oStringList
= this.split('-');
117 if(oStringList
.length
== 1)
118 return oStringList
[0];
119 var ret
= this.indexOf("-") == 0 ?
120 oStringList
[0].charAt(0).toUpperCase() + oStringList
[0].substring(1) : oStringList
[0];
121 for(var i
= 1, len
= oStringList
.length
; i
< len
; i
++){
122 var s
= oStringList
[i
];
123 ret
+= s
.charAt(0).toUpperCase() + s
.substring(1)
128 Element
.getStyle = function(element
, style
) {
129 element
= $(element
);
130 var value
= element
.style
[style
.camelize()];
132 if(document
.defaultView
&& document
.defaultView
.getComputedStyle
) {
133 var css
= document
.defaultView
.getComputedStyle(element
, null);
134 value
= (css
!=null) ? css
.getPropertyValue(style
) : null;
135 } else if(element
.currentStyle
) {
136 value
= element
.currentStyle
[style
.camelize()];
138 if(value
=='auto') value
= null;
142 Element
.makePositioned = function(element
) {
143 element
= $(element
);
144 if(Element
.getStyle(element
, 'position')=='static')
145 element
.style
.position
= "relative";
148 Element
.makeClipping = function(element
) {
149 element
= $(element
);
150 element
._overflow
= Element
.getStyle(element
, 'overflow') || 'visible';
151 if(element
._overflow
!='hidden') element
.style
.overflow
= 'hidden';
154 Element
.undoClipping = function(element
) {
155 element
= $(element
);
156 if(element
._overflow
!='hidden') element
.style
.overflow
= element
._overflow
;
159 Element
.collectTextNodesIgnoreClass = function(element
, ignoreclass
) {
160 var children
= $(element
).childNodes
;
162 var classtest
= new RegExp("^([^ ]+ )*" + ignoreclass
+ "( [^ ]+)*$","i");
164 for (var i
= 0; i
< children
.length
; i
++) {
165 if(children
[i
].nodeType
==3) {
166 text
+=children
[i
].nodeValue
;
168 if((!children
[i
].className
.match(classtest
)) && children
[i
].hasChildNodes())
169 text
+= Element
.collectTextNodesIgnoreClass(children
[i
], ignoreclass
);
176 /*--------------------------------------------------------------------------*/
178 Position
.positionedOffset = function(element
) {
179 var valueT
= 0, valueL
= 0;
181 valueT
+= element
.offsetTop
|| 0;
182 valueL
+= element
.offsetLeft
|| 0;
183 element
= element
.offsetParent
;
185 p
= Element
.getStyle(element
,'position');
186 if(p
== 'relative' || p
== 'absolute') break;
189 return [valueL
, valueT
];
192 // Safari returns margins on body which is incorrect if the child is absolutely positioned.
193 // for performance reasons, we create a specialized version of Position.positionedOffset for
196 if(/Konqueror|Safari|KHTML/.test(navigator
.userAgent
)) {
197 Position
.cumulativeOffset = function(element
) {
198 var valueT
= 0, valueL
= 0;
200 valueT
+= element
.offsetTop
|| 0;
201 valueL
+= element
.offsetLeft
|| 0;
203 if (element
.offsetParent
==document
.body
)
204 if (Element
.getStyle(element
,'position')=='absolute') break;
206 element
= element
.offsetParent
;
208 return [valueL
, valueT
];
212 Position
.page = function(forElement
) {
213 if(element
== document
.body
) return [0, 0];
214 var valueT
= 0, valueL
= 0;
216 var element
= forElement
;
218 valueT
+= element
.offsetTop
|| 0;
219 valueL
+= element
.offsetLeft
|| 0;
222 if (element
.offsetParent
==document
.body
)
223 if (Element
.getStyle(element
,'position')=='absolute') break;
225 } while (element
= element
.offsetParent
);
227 element
= forElement
;
229 valueT
-= element
.scrollTop
|| 0;
230 valueL
-= element
.scrollLeft
|| 0;
231 } while (element
= element
.parentNode
);
233 return [valueL
, valueT
];
236 // elements with display:none don't return an offsetParent,
237 // fall back to manual calculation
238 Position
.offsetParent = function(element
) {
239 if(element
.offsetParent
) return element
.offsetParent
;
240 if(element
== document
.body
) return element
;
242 while ((element
= element
.parentNode
) && element
!= document
.body
)
243 if (Element
.getStyle(element
,'position')!='static')
246 return document
.body
;
249 Position
.clone = function(source
, target
) {
250 var options
= Object
.extend({
257 }, arguments
[2] || {})
259 // find page position of source
261 var p
= Position
.page(source
);
263 // find coordinate system to use
267 // delta [0,0] will do fine with position: fixed elements,
268 // position:absolute needs offsetParent deltas
269 if (Element
.getStyle(target
,'position') == 'absolute') {
270 parent
= Position
.offsetParent(target
);
271 delta
= Position
.page(parent
);
274 // correct by body offsets (fixes Safari)
275 if (parent
==document
.body
) {
276 delta
[0] -= document
.body
.offsetLeft
;
277 delta
[1] -= document
.body
.offsetTop
;
281 if(options
.setLeft
) target
.style
.left
= (p
[0] - delta
[0] + options
.offsetLeft
) + "px";
282 if(options
.setTop
) target
.style
.top
= (p
[1] - delta
[1] + options
.offsetTop
) + "px";
283 if(options
.setWidth
) target
.style
.width
= source
.offsetWidth
+ "px";
284 if(options
.setHeight
) target
.style
.height
= source
.offsetHeight
+ "px";
287 Position
.absolutize = function(element
) {
288 element
= $(element
);
289 if(element
.style
.position
=='absolute') return;
292 var offsets
= Position
.positionedOffset(element
);
293 var top
= offsets
[1];
294 var left
= offsets
[0];
295 var width
= element
.clientWidth
;
296 var height
= element
.clientHeight
;
298 element
._originalLeft
= left
- parseFloat(element
.style
.left
|| 0);
299 element
._originalTop
= top
- parseFloat(element
.style
.top
|| 0);
300 element
._originalWidth
= element
.style
.width
;
301 element
._originalHeight
= element
.style
.height
;
303 element
.style
.position
= 'absolute';
304 element
.style
.top
= top
+ 'px';;
305 element
.style
.left
= left
+ 'px';;
306 element
.style
.width
= width
+ 'px';;
307 element
.style
.height
= height
+ 'px';;
310 Position
.relativize = function(element
) {
311 element
= $(element
);
312 if(element
.style
.position
=='relative') return;
315 element
.style
.position
= 'relative';
316 var top
= parseFloat(element
.style
.top
|| 0) - (element
._originalTop
|| 0);
317 var left
= parseFloat(element
.style
.left
|| 0) - (element
._originalLeft
|| 0);
319 element
.style
.top
= top
+ 'px';
320 element
.style
.left
= left
+ 'px';
321 element
.style
.height
= element
._originalHeight
;
322 element
.style
.width
= element
._originalWidth
;
325 /*--------------------------------------------------------------------------*/
328 // Element.toggleClass(element, className) toggles the class being on/off
329 // Element.toggleClass(element, className1, className2) toggles between both classes,
330 // defaulting to className1 if neither exist
331 toggle: function(element
, className
) {
332 if(Element
.Class
.has(element
, className
)) {
333 Element
.Class
.remove(element
, className
);
334 if(arguments
.length
== 3) Element
.Class
.add(element
, arguments
[2]);
336 Element
.Class
.add(element
, className
);
337 if(arguments
.length
== 3) Element
.Class
.remove(element
, arguments
[2]);
341 // gets space-delimited classnames of an element as an array
342 get: function(element
) {
343 element
= $(element
);
344 return element
.className
.split(' ');
347 // functions adapted from original functions by Gavin Kistner
348 remove: function(element
) {
349 element
= $(element
);
351 for(var i
= 1; i
< arguments
.length
; i
++) {
352 regEx
= new RegExp("(^|\\s)" + arguments
[i
] + "(\\s|$)", 'g');
353 element
.className
= element
.className
.replace(regEx
, '')
357 add: function(element
) {
358 element
= $(element
);
359 for(var i
= 1; i
< arguments
.length
; i
++) {
360 Element
.Class
.remove(element
, arguments
[i
]);
361 element
.className
+= (element
.className
.length
> 0 ? ' ' : '') + arguments
[i
];
365 // returns true if all given classes exist in said element
366 has: function(element
) {
367 element
= $(element
);
368 if(!element
|| !element
.className
) return false;
370 for(var i
= 1; i
< arguments
.length
; i
++) {
371 if((typeof arguments
[i
] == 'object') &&
372 (arguments
[i
].constructor == Array
)) {
373 for(var j
= 0; j
< arguments
[i
].length
; j
++) {
374 regEx
= new RegExp("(^|\\s)" + arguments
[i
][j
] + "(\\s|$)");
375 if(!regEx
.test(element
.className
)) return false;
378 regEx
= new RegExp("(^|\\s)" + arguments
[i
] + "(\\s|$)");
379 if(!regEx
.test(element
.className
)) return false;
385 // expects arrays of strings and/or strings as optional paramters
386 // Element.Class.has_any(element, ['classA','classB','classC'], 'classD')
387 has_any: function(element
) {
388 element
= $(element
);
389 if(!element
|| !element
.className
) return false;
391 for(var i
= 1; i
< arguments
.length
; i
++) {
392 if((typeof arguments
[i
] == 'object') &&
393 (arguments
[i
].constructor == Array
)) {
394 for(var j
= 0; j
< arguments
[i
].length
; j
++) {
395 regEx
= new RegExp("(^|\\s)" + arguments
[i
][j
] + "(\\s|$)");
396 if(regEx
.test(element
.className
)) return true;
399 regEx
= new RegExp("(^|\\s)" + arguments
[i
] + "(\\s|$)");
400 if(regEx
.test(element
.className
)) return true;
406 childrenWith: function(element
, className
) {
407 var children
= $(element
).getElementsByTagName('*');
408 var elements
= new Array();
410 for (var i
= 0; i
< children
.length
; i
++) {
411 if (Element
.Class
.has(children
[i
], className
)) {
412 elements
.push(children
[i
]);
421 /*--------------------------------------------------------------------------*/
423 String
.prototype.parseQuery = function() {
425 if(str
.substring(0,1) == '?') {
426 str
= this.substring(1);
429 var pairs
= str
.split('&');
430 for(var i
= 0; i
< pairs
.length
; i
++) {
431 var pair
= pairs
[i
].split('=');
432 result
[pair
[0]] = pair
[1];