Added Canvas 1.1.0, originally not under SCM so no historical development records...
[canvas.git] / res / js / prototype.lb.js
blobd046a86cde5d75e4b110e1ecd822a6302b713835
1 /*  Prototype JavaScript framework, version 1.4.0\r
2  *  (c) 2005 Sam Stephenson <sam@conio.net>\r
3  *\r
4  *  THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff\r
5  *  against the source tree, available from the Prototype darcs repository.\r
6  *\r
7  *  Prototype is freely distributable under the terms of an MIT-style license.\r
8  *\r
9  *  For details, see the Prototype web site: http://prototype.conio.net/\r
10  *\r
11 /*--------------------------------------------------------------------------*/\r
13 var Prototype = {\r
14   Version: '1.4.0',\r
15   ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',\r
17   emptyFunction: function() {},\r
18   K: function(x) {return x}\r
19 }\r
21 var Class = {\r
22   create: function() {\r
23     return function() {\r
24       this.initialize.apply(this, arguments);\r
25     }\r
26   }\r
27 }\r
29 var Abstract = new Object();\r
31 Object.extend = function(destination, source) {\r
32   for (property in source) {\r
33     destination[property] = source[property];\r
34   }\r
35   return destination;\r
36 }\r
38 Object.inspect = function(object) {\r
39   try {\r
40     if (object == undefined) return 'undefined';\r
41     if (object == null) return 'null';\r
42     return object.inspect ? object.inspect() : object.toString();\r
43   } catch (e) {\r
44     if (e instanceof RangeError) return '...';\r
45     throw e;\r
46   }\r
47 }\r
49 Function.prototype.bind = function() {\r
50   var __method = this, args = $A(arguments), object = args.shift();\r
51   return function() {\r
52     return __method.apply(object, args.concat($A(arguments)));\r
53   }\r
54 }\r
56 Function.prototype.bindAsEventListener = function(object) {\r
57   var __method = this;\r
58   return function(event) {\r
59     return __method.call(object, event || window.event);\r
60   }\r
61 }\r
63 Object.extend(Number.prototype, {\r
64   toColorPart: function() {\r
65     var digits = this.toString(16);\r
66     if (this < 16) return '0' + digits;\r
67     return digits;\r
68   },\r
70   succ: function() {\r
71     return this + 1;\r
72   },\r
74   times: function(iterator) {\r
75     $R(0, this, true).each(iterator);\r
76     return this;\r
77   }\r
78 });\r
80 var Try = {\r
81   these: function() {\r
82     var returnValue;\r
84     for (var i = 0; i < arguments.length; i++) {\r
85       var lambda = arguments[i];\r
86       try {\r
87         returnValue = lambda();\r
88         break;\r
89       } catch (e) {}\r
90     }\r
92     return returnValue;\r
93   }\r
94 }\r
96 /*--------------------------------------------------------------------------*/\r
98 var PeriodicalExecuter = Class.create();\r
99 PeriodicalExecuter.prototype = {\r
100   initialize: function(callback, frequency) {\r
101     this.callback = callback;\r
102     this.frequency = frequency;\r
103     this.currentlyExecuting = false;\r
105     this.registerCallback();\r
106   },\r
108   registerCallback: function() {\r
109     setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);\r
110   },\r
112   onTimerEvent: function() {\r
113     if (!this.currentlyExecuting) {\r
114       try {\r
115         this.currentlyExecuting = true;\r
116         this.callback();\r
117       } finally {\r
118         this.currentlyExecuting = false;\r
119       }\r
120     }\r
121   }\r
124 /*--------------------------------------------------------------------------*/\r
126 function $() {\r
127   var elements = new Array();\r
129   for (var i = 0; i < arguments.length; i++) {\r
130     var element = arguments[i];\r
131     if (typeof element == 'string')\r
132       element = document.getElementById(element);\r
134     if (arguments.length == 1)\r
135       return element;\r
137     elements.push(element);\r
138   }\r
140   return elements;\r
142 Object.extend(String.prototype, {\r
143   stripTags: function() {\r
144     return this.replace(/<\/?[^>]+>/gi, '');\r
145   },\r
147   stripScripts: function() {\r
148     return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');\r
149   },\r
151   extractScripts: function() {\r
152     var matchAll = new RegExp(Prototype.ScriptFragment, 'img');\r
153     var matchOne = new RegExp(Prototype.ScriptFragment, 'im');\r
154     return (this.match(matchAll) || []).map(function(scriptTag) {\r
155       return (scriptTag.match(matchOne) || ['', ''])[1];\r
156     });\r
157   },\r
159   evalScripts: function() {\r
160     return this.extractScripts().map(eval);\r
161   },\r
163   escapeHTML: function() {\r
164     var div = document.createElement('div');\r
165     var text = document.createTextNode(this);\r
166     div.appendChild(text);\r
167     return div.innerHTML;\r
168   },\r
170   unescapeHTML: function() {\r
171     var div = document.createElement('div');\r
172     div.innerHTML = this.stripTags();\r
173     return div.childNodes[0] ? div.childNodes[0].nodeValue : '';\r
174   },\r
176   toQueryParams: function() {\r
177     var pairs = this.match(/^\??(.*)$/)[1].split('&');\r
178     return pairs.inject({}, function(params, pairString) {\r
179       var pair = pairString.split('=');\r
180       params[pair[0]] = pair[1];\r
181       return params;\r
182     });\r
183   },\r
185   toArray: function() {\r
186     return this.split('');\r
187   },\r
189   camelize: function() {\r
190     var oStringList = this.split('-');\r
191     if (oStringList.length == 1) return oStringList[0];\r
193     var camelizedString = this.indexOf('-') == 0\r
194       ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)\r
195       : oStringList[0];\r
197     for (var i = 1, len = oStringList.length; i < len; i++) {\r
198       var s = oStringList[i];\r
199       camelizedString += s.charAt(0).toUpperCase() + s.substring(1);\r
200     }\r
202     return camelizedString;\r
203   },\r
205   inspect: function() {\r
206     return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";\r
207   }\r
208 });\r
210 String.prototype.parseQuery = String.prototype.toQueryParams;\r
212 var $break    = new Object();\r
213 var $continue = new Object();\r
215 var Enumerable = {\r
216   each: function(iterator) {\r
217     var index = 0;\r
218     try {\r
219       this._each(function(value) {\r
220         try {\r
221           iterator(value, index++);\r
222         } catch (e) {\r
223           if (e != $continue) throw e;\r
224         }\r
225       });\r
226     } catch (e) {\r
227       if (e != $break) throw e;\r
228     }\r
229   },\r
231   all: function(iterator) {\r
232     var result = true;\r
233     this.each(function(value, index) {\r
234       result = result && !!(iterator || Prototype.K)(value, index);\r
235       if (!result) throw $break;\r
236     });\r
237     return result;\r
238   },\r
240   any: function(iterator) {\r
241     var result = true;\r
242     this.each(function(value, index) {\r
243       if (result = !!(iterator || Prototype.K)(value, index))\r
244         throw $break;\r
245     });\r
246     return result;\r
247   },\r
249   collect: function(iterator) {\r
250     var results = [];\r
251     this.each(function(value, index) {\r
252       results.push(iterator(value, index));\r
253     });\r
254     return results;\r
255   },\r
257   detect: function (iterator) {\r
258     var result;\r
259     this.each(function(value, index) {\r
260       if (iterator(value, index)) {\r
261         result = value;\r
262         throw $break;\r
263       }\r
264     });\r
265     return result;\r
266   },\r
268   findAll: function(iterator) {\r
269     var results = [];\r
270     this.each(function(value, index) {\r
271       if (iterator(value, index))\r
272         results.push(value);\r
273     });\r
274     return results;\r
275   },\r
277   grep: function(pattern, iterator) {\r
278     var results = [];\r
279     this.each(function(value, index) {\r
280       var stringValue = value.toString();\r
281       if (stringValue.match(pattern))\r
282         results.push((iterator || Prototype.K)(value, index));\r
283     })\r
284     return results;\r
285   },\r
287   include: function(object) {\r
288     var found = false;\r
289     this.each(function(value) {\r
290       if (value == object) {\r
291         found = true;\r
292         throw $break;\r
293       }\r
294     });\r
295     return found;\r
296   },\r
298   inject: function(memo, iterator) {\r
299     this.each(function(value, index) {\r
300       memo = iterator(memo, value, index);\r
301     });\r
302     return memo;\r
303   },\r
305   invoke: function(method) {\r
306     var args = $A(arguments).slice(1);\r
307     return this.collect(function(value) {\r
308       return value[method].apply(value, args);\r
309     });\r
310   },\r
312   max: function(iterator) {\r
313     var result;\r
314     this.each(function(value, index) {\r
315       value = (iterator || Prototype.K)(value, index);\r
316       if (value >= (result || value))\r
317         result = value;\r
318     });\r
319     return result;\r
320   },\r
322   min: function(iterator) {\r
323     var result;\r
324     this.each(function(value, index) {\r
325       value = (iterator || Prototype.K)(value, index);\r
326       if (value <= (result || value))\r
327         result = value;\r
328     });\r
329     return result;\r
330   },\r
332   partition: function(iterator) {\r
333     var trues = [], falses = [];\r
334     this.each(function(value, index) {\r
335       ((iterator || Prototype.K)(value, index) ?\r
336         trues : falses).push(value);\r
337     });\r
338     return [trues, falses];\r
339   },\r
341   pluck: function(property) {\r
342     var results = [];\r
343     this.each(function(value, index) {\r
344       results.push(value[property]);\r
345     });\r
346     return results;\r
347   },\r
349   reject: function(iterator) {\r
350     var results = [];\r
351     this.each(function(value, index) {\r
352       if (!iterator(value, index))\r
353         results.push(value);\r
354     });\r
355     return results;\r
356   },\r
358   sortBy: function(iterator) {\r
359     return this.collect(function(value, index) {\r
360       return {value: value, criteria: iterator(value, index)};\r
361     }).sort(function(left, right) {\r
362       var a = left.criteria, b = right.criteria;\r
363       return a < b ? -1 : a > b ? 1 : 0;\r
364     }).pluck('value');\r
365   },\r
367   toArray: function() {\r
368     return this.collect(Prototype.K);\r
369   },\r
371   zip: function() {\r
372     var iterator = Prototype.K, args = $A(arguments);\r
373     if (typeof args.last() == 'function')\r
374       iterator = args.pop();\r
376     var collections = [this].concat(args).map($A);\r
377     return this.map(function(value, index) {\r
378       iterator(value = collections.pluck(index));\r
379       return value;\r
380     });\r
381   },\r
383   inspect: function() {\r
384     return '#<Enumerable:' + this.toArray().inspect() + '>';\r
385   }\r
388 Object.extend(Enumerable, {\r
389   map:     Enumerable.collect,\r
390   find:    Enumerable.detect,\r
391   select:  Enumerable.findAll,\r
392   member:  Enumerable.include,\r
393   entries: Enumerable.toArray\r
394 });\r
395 var $A = Array.from = function(iterable) {\r
396   if (!iterable) return [];\r
397   if (iterable.toArray) {\r
398     return iterable.toArray();\r
399   } else {\r
400     var results = [];\r
401     for (var i = 0; i < iterable.length; i++)\r
402       results.push(iterable[i]);\r
403     return results;\r
404   }\r
407 Object.extend(Array.prototype, Enumerable);\r
409 Array.prototype._reverse = Array.prototype.reverse;\r
411 Object.extend(Array.prototype, {\r
412   _each: function(iterator) {\r
413     for (var i = 0; i < this.length; i++)\r
414       iterator(this[i]);\r
415   },\r
417   clear: function() {\r
418     this.length = 0;\r
419     return this;\r
420   },\r
422   first: function() {\r
423     return this[0];\r
424   },\r
426   last: function() {\r
427     return this[this.length - 1];\r
428   },\r
430   compact: function() {\r
431     return this.select(function(value) {\r
432       return value != undefined || value != null;\r
433     });\r
434   },\r
436   flatten: function() {\r
437     return this.inject([], function(array, value) {\r
438       return array.concat(value.constructor == Array ?\r
439         value.flatten() : [value]);\r
440     });\r
441   },\r
443   without: function() {\r
444     var values = $A(arguments);\r
445     return this.select(function(value) {\r
446       return !values.include(value);\r
447     });\r
448   },\r
450   indexOf: function(object) {\r
451     for (var i = 0; i < this.length; i++)\r
452       if (this[i] == object) return i;\r
453     return -1;\r
454   },\r
456   reverse: function(inline) {\r
457     return (inline !== false ? this : this.toArray())._reverse();\r
458   },\r
460   shift: function() {\r
461     var result = this[0];\r
462     for (var i = 0; i < this.length - 1; i++)\r
463       this[i] = this[i + 1];\r
464     this.length--;\r
465     return result;\r
466   },\r
468   inspect: function() {\r
469     return '[' + this.map(Object.inspect).join(', ') + ']';\r
470   }\r
471 });\r
472 var Hash = {\r
473   _each: function(iterator) {\r
474     for (key in this) {\r
475       var value = this[key];\r
476       if (typeof value == 'function') continue;\r
478       var pair = [key, value];\r
479       pair.key = key;\r
480       pair.value = value;\r
481       iterator(pair);\r
482     }\r
483   },\r
485   keys: function() {\r
486     return this.pluck('key');\r
487   },\r
489   values: function() {\r
490     return this.pluck('value');\r
491   },\r
493   merge: function(hash) {\r
494     return $H(hash).inject($H(this), function(mergedHash, pair) {\r
495       mergedHash[pair.key] = pair.value;\r
496       return mergedHash;\r
497     });\r
498   },\r
500   toQueryString: function() {\r
501     return this.map(function(pair) {\r
502       return pair.map(encodeURIComponent).join('=');\r
503     }).join('&');\r
504   },\r
506   inspect: function() {\r
507     return '#<Hash:{' + this.map(function(pair) {\r
508       return pair.map(Object.inspect).join(': ');\r
509     }).join(', ') + '}>';\r
510   }\r
513 function $H(object) {\r
514   var hash = Object.extend({}, object || {});\r
515   Object.extend(hash, Enumerable);\r
516   Object.extend(hash, Hash);\r
517   return hash;\r
519 ObjectRange = Class.create();\r
520 Object.extend(ObjectRange.prototype, Enumerable);\r
521 Object.extend(ObjectRange.prototype, {\r
522   initialize: function(start, end, exclusive) {\r
523     this.start = start;\r
524     this.end = end;\r
525     this.exclusive = exclusive;\r
526   },\r
528   _each: function(iterator) {\r
529     var value = this.start;\r
530     do {\r
531       iterator(value);\r
532       value = value.succ();\r
533     } while (this.include(value));\r
534   },\r
536   include: function(value) {\r
537     if (value < this.start)\r
538       return false;\r
539     if (this.exclusive)\r
540       return value < this.end;\r
541     return value <= this.end;\r
542   }\r
543 });\r
545 var $R = function(start, end, exclusive) {\r
546   return new ObjectRange(start, end, exclusive);\r
549 var Ajax = {\r
550   getTransport: function() {\r
551     return Try.these(\r
552       function() {return new ActiveXObject('Msxml2.XMLHTTP')},\r
553       function() {return new ActiveXObject('Microsoft.XMLHTTP')},\r
554       function() {return new XMLHttpRequest()}\r
555     ) || false;\r
556   },\r
558   activeRequestCount: 0\r
561 Ajax.Responders = {\r
562   responders: [],\r
564   _each: function(iterator) {\r
565     this.responders._each(iterator);\r
566   },\r
568   register: function(responderToAdd) {\r
569     if (!this.include(responderToAdd))\r
570       this.responders.push(responderToAdd);\r
571   },\r
573   unregister: function(responderToRemove) {\r
574     this.responders = this.responders.without(responderToRemove);\r
575   },\r
577   dispatch: function(callback, request, transport, json) {\r
578     this.each(function(responder) {\r
579       if (responder[callback] && typeof responder[callback] == 'function') {\r
580         try {\r
581           responder[callback].apply(responder, [request, transport, json]);\r
582         } catch (e) {}\r
583       }\r
584     });\r
585   }\r
586 };\r
588 Object.extend(Ajax.Responders, Enumerable);\r
590 Ajax.Responders.register({\r
591   onCreate: function() {\r
592     Ajax.activeRequestCount++;\r
593   },\r
595   onComplete: function() {\r
596     Ajax.activeRequestCount--;\r
597   }\r
598 });\r
600 Ajax.Base = function() {};\r
601 Ajax.Base.prototype = {\r
602   setOptions: function(options) {\r
603     this.options = {\r
604       method:       'post',\r
605       asynchronous: true,\r
606       parameters:   ''\r
607     }\r
608     Object.extend(this.options, options || {});\r
609   },\r
611   responseIsSuccess: function() {\r
612     return this.transport.status == undefined\r
613         || this.transport.status == 0\r
614         || (this.transport.status >= 200 && this.transport.status < 300);\r
615   },\r
617   responseIsFailure: function() {\r
618     return !this.responseIsSuccess();\r
619   }\r
622 Ajax.Request = Class.create();\r
623 Ajax.Request.Events =\r
624   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];\r
626 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {\r
627   initialize: function(url, options) {\r
628     this.transport = Ajax.getTransport();\r
629     this.setOptions(options);\r
630     this.request(url);\r
631   },\r
633   request: function(url) {\r
634     var parameters = this.options.parameters || '';\r
635     if (parameters.length > 0) parameters += '&_=';\r
637     try {\r
638       this.url = url;\r
639       if (this.options.method == 'get' && parameters.length > 0)\r
640         this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;\r
642       Ajax.Responders.dispatch('onCreate', this, this.transport);\r
644       this.transport.open(this.options.method, this.url,\r
645         this.options.asynchronous);\r
647       if (this.options.asynchronous) {\r
648         this.transport.onreadystatechange = this.onStateChange.bind(this);\r
649         setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);\r
650       }\r
652       this.setRequestHeaders();\r
654       var body = this.options.postBody ? this.options.postBody : parameters;\r
655       this.transport.send(this.options.method == 'post' ? body : null);\r
657     } catch (e) {\r
658       this.dispatchException(e);\r
659     }\r
660   },\r
662   setRequestHeaders: function() {\r
663     var requestHeaders =\r
664       ['X-Requested-With', 'XMLHttpRequest',\r
665        'X-Prototype-Version', Prototype.Version];\r
667     if (this.options.method == 'post') {\r
668       requestHeaders.push('Content-type',\r
669         'application/x-www-form-urlencoded');\r
671       /* Force "Connection: close" for Mozilla browsers to work around\r
672        * a bug where XMLHttpReqeuest sends an incorrect Content-length\r
673        * header. See Mozilla Bugzilla #246651.\r
674        */\r
675       if (this.transport.overrideMimeType)\r
676         requestHeaders.push('Connection', 'close');\r
677     }\r
679     if (this.options.requestHeaders)\r
680       requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);\r
682     for (var i = 0; i < requestHeaders.length; i += 2)\r
683       this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);\r
684   },\r
686   onStateChange: function() {\r
687     var readyState = this.transport.readyState;\r
688     if (readyState != 1)\r
689       this.respondToReadyState(this.transport.readyState);\r
690   },\r
692   header: function(name) {\r
693     try {\r
694       return this.transport.getResponseHeader(name);\r
695     } catch (e) {}\r
696   },\r
698   evalJSON: function() {\r
699     try {\r
700       return eval(this.header('X-JSON'));\r
701     } catch (e) {}\r
702   },\r
704   evalResponse: function() {\r
705     try {\r
706       return eval(this.transport.responseText);\r
707     } catch (e) {\r
708       this.dispatchException(e);\r
709     }\r
710   },\r
712   respondToReadyState: function(readyState) {\r
713     var event = Ajax.Request.Events[readyState];\r
714     var transport = this.transport, json = this.evalJSON();\r
716     if (event == 'Complete') {\r
717       try {\r
718         (this.options['on' + this.transport.status]\r
719          || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]\r
720          || Prototype.emptyFunction)(transport, json);\r
721       } catch (e) {\r
722         this.dispatchException(e);\r
723       }\r
725       if ((this.header('Content-type') || '').match(/^text\/javascript/i))\r
726         this.evalResponse();\r
727     }\r
729     try {\r
730       (this.options['on' + event] || Prototype.emptyFunction)(transport, json);\r
731       Ajax.Responders.dispatch('on' + event, this, transport, json);\r
732     } catch (e) {\r
733       this.dispatchException(e);\r
734     }\r
736     /* Avoid memory leak in MSIE: clean up the oncomplete event handler */\r
737     if (event == 'Complete')\r
738       this.transport.onreadystatechange = Prototype.emptyFunction;\r
739   },\r
741   dispatchException: function(exception) {\r
742     (this.options.onException || Prototype.emptyFunction)(this, exception);\r
743     Ajax.Responders.dispatch('onException', this, exception);\r
744   }\r
745 });\r
747 Ajax.Updater = Class.create();\r
749 Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {\r
750   initialize: function(container, url, options) {\r
751     this.containers = {\r
752       success: container.success ? $(container.success) : $(container),\r
753       failure: container.failure ? $(container.failure) :\r
754         (container.success ? null : $(container))\r
755     }\r
757     this.transport = Ajax.getTransport();\r
758     this.setOptions(options);\r
760     var onComplete = this.options.onComplete || Prototype.emptyFunction;\r
761     this.options.onComplete = (function(transport, object) {\r
762       this.updateContent();\r
763       onComplete(transport, object);\r
764     }).bind(this);\r
766     this.request(url);\r
767   },\r
769   updateContent: function() {\r
770     var receiver = this.responseIsSuccess() ?\r
771       this.containers.success : this.containers.failure;\r
772     var response = this.transport.responseText;\r
774     if (!this.options.evalScripts)\r
775       response = response.stripScripts();\r
777     if (receiver) {\r
778       if (this.options.insertion) {\r
779         new this.options.insertion(receiver, response);\r
780       } else {\r
781         Element.update(receiver, response);\r
782       }\r
783     }\r
785     if (this.responseIsSuccess()) {\r
786       if (this.onComplete)\r
787         setTimeout(this.onComplete.bind(this), 10);\r
788     }\r
789   }\r
790 });\r
792 Ajax.PeriodicalUpdater = Class.create();\r
793 Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {\r
794   initialize: function(container, url, options) {\r
795     this.setOptions(options);\r
796     this.onComplete = this.options.onComplete;\r
798     this.frequency = (this.options.frequency || 2);\r
799     this.decay = (this.options.decay || 1);\r
801     this.updater = {};\r
802     this.container = container;\r
803     this.url = url;\r
805     this.start();\r
806   },\r
808   start: function() {\r
809     this.options.onComplete = this.updateComplete.bind(this);\r
810     this.onTimerEvent();\r
811   },\r
813   stop: function() {\r
814     this.updater.onComplete = undefined;\r
815     clearTimeout(this.timer);\r
816     (this.onComplete || Prototype.emptyFunction).apply(this, arguments);\r
817   },\r
819   updateComplete: function(request) {\r
820     if (this.options.decay) {\r
821       this.decay = (request.responseText == this.lastText ?\r
822         this.decay * this.options.decay : 1);\r
824       this.lastText = request.responseText;\r
825     }\r
826     this.timer = setTimeout(this.onTimerEvent.bind(this),\r
827       this.decay * this.frequency * 1000);\r
828   },\r
830   onTimerEvent: function() {\r
831     this.updater = new Ajax.Updater(this.container, this.url, this.options);\r
832   }\r
833 });\r
834 document.getElementsByClassName = function(className, parentElement) {\r
835   var children = ($(parentElement) || document.body).getElementsByTagName('*');\r
836   return $A(children).inject([], function(elements, child) {\r
837     if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))\r
838       elements.push(child);\r
839     return elements;\r
840   });\r
843 /*--------------------------------------------------------------------------*/\r
845 if (!window.Element) {\r
846   var Element = new Object();\r
849 Object.extend(Element, {\r
850   visible: function(element) {\r
851     return $(element).style.display != 'none';\r
852   },\r
854   toggle: function() {\r
855     for (var i = 0; i < arguments.length; i++) {\r
856       var element = $(arguments[i]);\r
857       Element[Element.visible(element) ? 'hide' : 'show'](element);\r
858     }\r
859   },\r
861   hide: function() {\r
862     for (var i = 0; i < arguments.length; i++) {\r
863       var element = $(arguments[i]);\r
864       element.style.display = 'none';\r
865     }\r
866   },\r
868   show: function() {\r
869     for (var i = 0; i < arguments.length; i++) {\r
870       var element = $(arguments[i]);\r
871       element.style.display = '';\r
872     }\r
873   },\r
875   remove: function(element) {\r
876     element = $(element);\r
877     element.parentNode.removeChild(element);\r
878   },\r
880   update: function(element, html) {\r
881     $(element).innerHTML = html.stripScripts();\r
882     setTimeout(function() {html.evalScripts()}, 10);\r
883   },\r
885   getHeight: function(element) {\r
886     element = $(element);\r
887     return element.offsetHeight;\r
888   },\r
890   classNames: function(element) {\r
891     return new Element.ClassNames(element);\r
892   },\r
894   hasClassName: function(element, className) {\r
895     if (!(element = $(element))) return;\r
896     return Element.classNames(element).include(className);\r
897   },\r
899   addClassName: function(element, className) {\r
900     if (!(element = $(element))) return;\r
901     return Element.classNames(element).add(className);\r
902   },\r
904   removeClassName: function(element, className) {\r
905     if (!(element = $(element))) return;\r
906     return Element.classNames(element).remove(className);\r
907   },\r
909   // removes whitespace-only text node children\r
910   cleanWhitespace: function(element) {\r
911     element = $(element);\r
912     for (var i = 0; i < element.childNodes.length; i++) {\r
913       var node = element.childNodes[i];\r
914       if (node.nodeType == 3 && !/\S/.test(node.nodeValue))\r
915         Element.remove(node);\r
916     }\r
917   },\r
919   empty: function(element) {\r
920     return $(element).innerHTML.match(/^\s*$/);\r
921   },\r
923   scrollTo: function(element) {\r
924     element = $(element);\r
925     var x = element.x ? element.x : element.offsetLeft,\r
926         y = element.y ? element.y : element.offsetTop;\r
927     window.scrollTo(x, y);\r
928   },\r
930   getStyle: function(element, style) {\r
931     element = $(element);\r
932     var value = element.style[style.camelize()];\r
933     if (!value) {\r
934       if (document.defaultView && document.defaultView.getComputedStyle) {\r
935         var css = document.defaultView.getComputedStyle(element, null);\r
936         value = css ? css.getPropertyValue(style) : null;\r
937       } else if (element.currentStyle) {\r
938         value = element.currentStyle[style.camelize()];\r
939       }\r
940     }\r
942     if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))\r
943       if (Element.getStyle(element, 'position') == 'static') value = 'auto';\r
945     return value == 'auto' ? null : value;\r
946   },\r
948   setStyle: function(element, style) {\r
949     element = $(element);\r
950     for (name in style)\r
951       element.style[name.camelize()] = style[name];\r
952   },\r
954   getDimensions: function(element) {\r
955     element = $(element);\r
956     if (Element.getStyle(element, 'display') != 'none')\r
957       return {width: element.offsetWidth, height: element.offsetHeight};\r
959     // All *Width and *Height properties give 0 on elements with display none,\r
960     // so enable the element temporarily\r
961     var els = element.style;\r
962     var originalVisibility = els.visibility;\r
963     var originalPosition = els.position;\r
964     els.visibility = 'hidden';\r
965     els.position = 'absolute';\r
966     els.display = '';\r
967     var originalWidth = element.clientWidth;\r
968     var originalHeight = element.clientHeight;\r
969     els.display = 'none';\r
970     els.position = originalPosition;\r
971     els.visibility = originalVisibility;\r
972     return {width: originalWidth, height: originalHeight};\r
973   },\r
975   makePositioned: function(element) {\r
976     element = $(element);\r
977     var pos = Element.getStyle(element, 'position');\r
978     if (pos == 'static' || !pos) {\r
979       element._madePositioned = true;\r
980       element.style.position = 'relative';\r
981       // Opera returns the offset relative to the positioning context, when an\r
982       // element is position relative but top and left have not been defined\r
983       if (window.opera) {\r
984         element.style.top = 0;\r
985         element.style.left = 0;\r
986       }\r
987     }\r
988   },\r
990   undoPositioned: function(element) {\r
991     element = $(element);\r
992     if (element._madePositioned) {\r
993       element._madePositioned = undefined;\r
994       element.style.position =\r
995         element.style.top =\r
996         element.style.left =\r
997         element.style.bottom =\r
998         element.style.right = '';\r
999     }\r
1000   },\r
1002   makeClipping: function(element) {\r
1003     element = $(element);\r
1004     if (element._overflow) return;\r
1005     element._overflow = element.style.overflow;\r
1006     if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')\r
1007       element.style.overflow = 'hidden';\r
1008   },\r
1010   undoClipping: function(element) {\r
1011     element = $(element);\r
1012     if (element._overflow) return;\r
1013     element.style.overflow = element._overflow;\r
1014     element._overflow = undefined;\r
1015   }\r
1016 });\r
1018 var Toggle = new Object();\r
1019 Toggle.display = Element.toggle;\r
1021 /*--------------------------------------------------------------------------*/\r
1023 Abstract.Insertion = function(adjacency) {\r
1024   this.adjacency = adjacency;\r
1027 Abstract.Insertion.prototype = {\r
1028   initialize: function(element, content) {\r
1029     this.element = $(element);\r
1030     this.content = content.stripScripts();\r
1032     if (this.adjacency && this.element.insertAdjacentHTML) {\r
1033       try {\r
1034         this.element.insertAdjacentHTML(this.adjacency, this.content);\r
1035       } catch (e) {\r
1036         if (this.element.tagName.toLowerCase() == 'tbody' || this.element.tagName.toLowerCase() == 'tr') {\r
1037           this.insertContent(this.contentFromAnonymousTable());\r
1038         } else {\r
1039           throw e;\r
1040         }\r
1041       }\r
1042     } else {\r
1043       this.range = this.element.ownerDocument.createRange();\r
1044       if (this.initializeRange) this.initializeRange();\r
1045       this.insertContent([this.range.createContextualFragment(this.content)]);\r
1046     }\r
1048     setTimeout(function() {content.evalScripts()}, 10);\r
1049   },\r
1051   contentFromAnonymousTable: function() {\r
1052     var div = document.createElement('div');\r
1053     div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';\r
1054     return $A(div.childNodes[0].childNodes[0].childNodes);\r
1055   }\r
1058 var Insertion = new Object();\r
1060 Insertion.Before = Class.create();\r
1061 Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {\r
1062   initializeRange: function() {\r
1063     this.range.setStartBefore(this.element);\r
1064   },\r
1066   insertContent: function(fragments) {\r
1067     fragments.each((function(fragment) {\r
1068       this.element.parentNode.insertBefore(fragment, this.element);\r
1069     }).bind(this));\r
1070   }\r
1071 });\r
1073 Insertion.Top = Class.create();\r
1074 Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {\r
1075   initializeRange: function() {\r
1076     this.range.selectNodeContents(this.element);\r
1077     this.range.collapse(true);\r
1078   },\r
1080   insertContent: function(fragments) {\r
1081     fragments.reverse(false).each((function(fragment) {\r
1082       this.element.insertBefore(fragment, this.element.firstChild);\r
1083     }).bind(this));\r
1084   }\r
1085 });\r
1087 Insertion.Bottom = Class.create();\r
1088 Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {\r
1089   initializeRange: function() {\r
1090     this.range.selectNodeContents(this.element);\r
1091     this.range.collapse(this.element);\r
1092   },\r
1094   insertContent: function(fragments) {\r
1095     fragments.each((function(fragment) {\r
1096       this.element.appendChild(fragment);\r
1097     }).bind(this));\r
1098   }\r
1099 });\r
1101 Insertion.After = Class.create();\r
1102 Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {\r
1103   initializeRange: function() {\r
1104     this.range.setStartAfter(this.element);\r
1105   },\r
1107   insertContent: function(fragments) {\r
1108     fragments.each((function(fragment) {\r
1109       this.element.parentNode.insertBefore(fragment,\r
1110         this.element.nextSibling);\r
1111     }).bind(this));\r
1112   }\r
1113 });\r
1115 /*--------------------------------------------------------------------------*/\r
1117 Element.ClassNames = Class.create();\r
1118 Element.ClassNames.prototype = {\r
1119   initialize: function(element) {\r
1120     this.element = $(element);\r
1121   },\r
1123   _each: function(iterator) {\r
1124     this.element.className.split(/\s+/).select(function(name) {\r
1125       return name.length > 0;\r
1126     })._each(iterator);\r
1127   },\r
1129   set: function(className) {\r
1130     this.element.className = className;\r
1131   },\r
1133   add: function(classNameToAdd) {\r
1134     if (this.include(classNameToAdd)) return;\r
1135     this.set(this.toArray().concat(classNameToAdd).join(' '));\r
1136   },\r
1138   remove: function(classNameToRemove) {\r
1139     if (!this.include(classNameToRemove)) return;\r
1140     this.set(this.select(function(className) {\r
1141       return className != classNameToRemove;\r
1142     }).join(' '));\r
1143   },\r
1145   toString: function() {\r
1146     return this.toArray().join(' ');\r
1147   }\r
1150 Object.extend(Element.ClassNames.prototype, Enumerable);\r
1151 var Field = {\r
1152   clear: function() {\r
1153     for (var i = 0; i < arguments.length; i++)\r
1154       $(arguments[i]).value = '';\r
1155   },\r
1157   focus: function(element) {\r
1158     $(element).focus();\r
1159   },\r
1161   present: function() {\r
1162     for (var i = 0; i < arguments.length; i++)\r
1163       if ($(arguments[i]).value == '') return false;\r
1164     return true;\r
1165   },\r
1167   select: function(element) {\r
1168     $(element).select();\r
1169   },\r
1171   activate: function(element) {\r
1172     element = $(element);\r
1173     element.focus();\r
1174     if (element.select)\r
1175       element.select();\r
1176   }\r
1179 /*--------------------------------------------------------------------------*/\r
1181 var Form = {\r
1182   serialize: function(form) {\r
1183     var elements = Form.getElements($(form));\r
1184     var queryComponents = new Array();\r
1186     for (var i = 0; i < elements.length; i++) {\r
1187       var queryComponent = Form.Element.serialize(elements[i]);\r
1188       if (queryComponent)\r
1189         queryComponents.push(queryComponent);\r
1190     }\r
1192     return queryComponents.join('&');\r
1193   },\r
1195   getElements: function(form) {\r
1196     form = $(form);\r
1197     var elements = new Array();\r
1199     for (tagName in Form.Element.Serializers) {\r
1200       var tagElements = form.getElementsByTagName(tagName);\r
1201       for (var j = 0; j < tagElements.length; j++)\r
1202         elements.push(tagElements[j]);\r
1203     }\r
1204     return elements;\r
1205   },\r
1207   getInputs: function(form, typeName, name) {\r
1208     form = $(form);\r
1209     var inputs = form.getElementsByTagName('input');\r
1211     if (!typeName && !name)\r
1212       return inputs;\r
1214     var matchingInputs = new Array();\r
1215     for (var i = 0; i < inputs.length; i++) {\r
1216       var input = inputs[i];\r
1217       if ((typeName && input.type != typeName) ||\r
1218           (name && input.name != name))\r
1219         continue;\r
1220       matchingInputs.push(input);\r
1221     }\r
1223     return matchingInputs;\r
1224   },\r
1226   disable: function(form) {\r
1227     var elements = Form.getElements(form);\r
1228     for (var i = 0; i < elements.length; i++) {\r
1229       var element = elements[i];\r
1230       element.blur();\r
1231       element.disabled = 'true';\r
1232     }\r
1233   },\r
1235   enable: function(form) {\r
1236     var elements = Form.getElements(form);\r
1237     for (var i = 0; i < elements.length; i++) {\r
1238       var element = elements[i];\r
1239       element.disabled = '';\r
1240     }\r
1241   },\r
1243   findFirstElement: function(form) {\r
1244     return Form.getElements(form).find(function(element) {\r
1245       return element.type != 'hidden' && !element.disabled &&\r
1246         ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());\r
1247     });\r
1248   },\r
1250   focusFirstElement: function(form) {\r
1251     Field.activate(Form.findFirstElement(form));\r
1252   },\r
1254   reset: function(form) {\r
1255     $(form).reset();\r
1256   }\r
1259 Form.Element = {\r
1260   serialize: function(element) {\r
1261     element = $(element);\r
1262     var method = element.tagName.toLowerCase();\r
1263     var parameter = Form.Element.Serializers[method](element);\r
1265     if (parameter) {\r
1266       var key = encodeURIComponent(parameter[0]);\r
1267       if (key.length == 0) return;\r
1269       if (parameter[1].constructor != Array)\r
1270         parameter[1] = [parameter[1]];\r
1272       return parameter[1].map(function(value) {\r
1273         return key + '=' + encodeURIComponent(value);\r
1274       }).join('&');\r
1275     }\r
1276   },\r
1278   getValue: function(element) {\r
1279     element = $(element);\r
1280     var method = element.tagName.toLowerCase();\r
1281     var parameter = Form.Element.Serializers[method](element);\r
1283     if (parameter)\r
1284       return parameter[1];\r
1285   }\r
1288 Form.Element.Serializers = {\r
1289   input: function(element) {\r
1290     switch (element.type.toLowerCase()) {\r
1291       case 'submit':\r
1292       case 'hidden':\r
1293       case 'password':\r
1294       case 'text':\r
1295         return Form.Element.Serializers.textarea(element);\r
1296       case 'checkbox':\r
1297       case 'radio':\r
1298         return Form.Element.Serializers.inputSelector(element);\r
1299     }\r
1300     return false;\r
1301   },\r
1303   inputSelector: function(element) {\r
1304     if (element.checked)\r
1305       return [element.name, element.value];\r
1306   },\r
1308   textarea: function(element) {\r
1309     return [element.name, element.value];\r
1310   },\r
1312   select: function(element) {\r
1313     return Form.Element.Serializers[element.type == 'select-one' ?\r
1314       'selectOne' : 'selectMany'](element);\r
1315   },\r
1317   selectOne: function(element) {\r
1318     var value = '', opt, index = element.selectedIndex;\r
1319     if (index >= 0) {\r
1320       opt = element.options[index];\r
1321       value = opt.value;\r
1322       if (!value && !('value' in opt))\r
1323         value = opt.text;\r
1324     }\r
1325     return [element.name, value];\r
1326   },\r
1328   selectMany: function(element) {\r
1329     var value = new Array();\r
1330     for (var i = 0; i < element.length; i++) {\r
1331       var opt = element.options[i];\r
1332       if (opt.selected) {\r
1333         var optValue = opt.value;\r
1334         if (!optValue && !('value' in opt))\r
1335           optValue = opt.text;\r
1336         value.push(optValue);\r
1337       }\r
1338     }\r
1339     return [element.name, value];\r
1340   }\r
1343 /*--------------------------------------------------------------------------*/\r
1345 var $F = Form.Element.getValue;\r
1347 /*--------------------------------------------------------------------------*/\r
1349 Abstract.TimedObserver = function() {}\r
1350 Abstract.TimedObserver.prototype = {\r
1351   initialize: function(element, frequency, callback) {\r
1352     this.frequency = frequency;\r
1353     this.element   = $(element);\r
1354     this.callback  = callback;\r
1356     this.lastValue = this.getValue();\r
1357     this.registerCallback();\r
1358   },\r
1360   registerCallback: function() {\r
1361     setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);\r
1362   },\r
1364   onTimerEvent: function() {\r
1365     var value = this.getValue();\r
1366     if (this.lastValue != value) {\r
1367       this.callback(this.element, value);\r
1368       this.lastValue = value;\r
1369     }\r
1370   }\r
1373 Form.Element.Observer = Class.create();\r
1374 Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {\r
1375   getValue: function() {\r
1376     return Form.Element.getValue(this.element);\r
1377   }\r
1378 });\r
1380 Form.Observer = Class.create();\r
1381 Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {\r
1382   getValue: function() {\r
1383     return Form.serialize(this.element);\r
1384   }\r
1385 });\r
1387 /*--------------------------------------------------------------------------*/\r
1389 Abstract.EventObserver = function() {}\r
1390 Abstract.EventObserver.prototype = {\r
1391   initialize: function(element, callback) {\r
1392     this.element  = $(element);\r
1393     this.callback = callback;\r
1395     this.lastValue = this.getValue();\r
1396     if (this.element.tagName.toLowerCase() == 'form')\r
1397       this.registerFormCallbacks();\r
1398     else\r
1399       this.registerCallback(this.element);\r
1400   },\r
1402   onElementEvent: function() {\r
1403     var value = this.getValue();\r
1404     if (this.lastValue != value) {\r
1405       this.callback(this.element, value);\r
1406       this.lastValue = value;\r
1407     }\r
1408   },\r
1410   registerFormCallbacks: function() {\r
1411     var elements = Form.getElements(this.element);\r
1412     for (var i = 0; i < elements.length; i++)\r
1413       this.registerCallback(elements[i]);\r
1414   },\r
1416   registerCallback: function(element) {\r
1417     if (element.type) {\r
1418       switch (element.type.toLowerCase()) {\r
1419         case 'checkbox':\r
1420         case 'radio':\r
1421           Event.observe(element, 'click', this.onElementEvent.bind(this));\r
1422           break;\r
1423         case 'password':\r
1424         case 'text':\r
1425         case 'textarea':\r
1426         case 'select-one':\r
1427         case 'select-multiple':\r
1428           Event.observe(element, 'change', this.onElementEvent.bind(this));\r
1429           break;\r
1430       }\r
1431     }\r
1432   }\r
1435 Form.Element.EventObserver = Class.create();\r
1436 Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {\r
1437   getValue: function() {\r
1438     return Form.Element.getValue(this.element);\r
1439   }\r
1440 });\r
1442 Form.EventObserver = Class.create();\r
1443 Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {\r
1444   getValue: function() {\r
1445     return Form.serialize(this.element);\r
1446   }\r
1447 });\r
1448 if (!window.Event) {\r
1449   var Event = new Object();\r
1452 Object.extend(Event, {\r
1453   KEY_BACKSPACE: 8,\r
1454   KEY_TAB:       9,\r
1455   KEY_RETURN:   13,\r
1456   KEY_ESC:      27,\r
1457   KEY_LEFT:     37,\r
1458   KEY_UP:       38,\r
1459   KEY_RIGHT:    39,\r
1460   KEY_DOWN:     40,\r
1461   KEY_DELETE:   46,\r
1463   element: function(event) {\r
1464     return event.target || event.srcElement;\r
1465   },\r
1467   isLeftClick: function(event) {\r
1468     return (((event.which) && (event.which == 1)) ||\r
1469             ((event.button) && (event.button == 1)));\r
1470   },\r
1472   pointerX: function(event) {\r
1473     return event.pageX || (event.clientX +\r
1474       (document.documentElement.scrollLeft || document.body.scrollLeft));\r
1475   },\r
1477   pointerY: function(event) {\r
1478     return event.pageY || (event.clientY +\r
1479       (document.documentElement.scrollTop || document.body.scrollTop));\r
1480   },\r
1482   stop: function(event) {\r
1483     if (event.preventDefault) {\r
1484       event.preventDefault();\r
1485       event.stopPropagation();\r
1486     } else {\r
1487       event.returnValue = false;\r
1488       event.cancelBubble = true;\r
1489     }\r
1490   },\r
1492   // find the first node with the given tagName, starting from the\r
1493   // node the event was triggered on; traverses the DOM upwards\r
1494   findElement: function(event, tagName) {\r
1495     var element = Event.element(event);\r
1496     while (element.parentNode && (!element.tagName ||\r
1497         (element.tagName.toUpperCase() != tagName.toUpperCase())))\r
1498       element = element.parentNode;\r
1499     return element;\r
1500   },\r
1502   observers: false,\r
1504   _observeAndCache: function(element, name, observer, useCapture) {\r
1505     if (!this.observers) this.observers = [];\r
1506     if (element.addEventListener) {\r
1507       this.observers.push([element, name, observer, useCapture]);\r
1508       element.addEventListener(name, observer, useCapture);\r
1509     } else if (element.attachEvent) {\r
1510       this.observers.push([element, name, observer, useCapture]);\r
1511       element.attachEvent('on' + name, observer);\r
1512     }\r
1513   },\r
1515   unloadCache: function() {\r
1516     if (!Event.observers) return;\r
1517     for (var i = 0; i < Event.observers.length; i++) {\r
1518       Event.stopObserving.apply(this, Event.observers[i]);\r
1519       Event.observers[i][0] = null;\r
1520     }\r
1521     Event.observers = false;\r
1522   },\r
1524   observe: function(element, name, observer, useCapture) {\r
1525     var element = $(element);\r
1526     useCapture = useCapture || false;\r
1528     if (name == 'keypress' &&\r
1529         (navigator.appVersion.match(/Konqueror|Safari|KHTML/)\r
1530         || element.attachEvent))\r
1531       name = 'keydown';\r
1533     this._observeAndCache(element, name, observer, useCapture);\r
1534   },\r
1536   stopObserving: function(element, name, observer, useCapture) {\r
1537     var element = $(element);\r
1538     useCapture = useCapture || false;\r
1540     if (name == 'keypress' &&\r
1541         (navigator.appVersion.match(/Konqueror|Safari|KHTML/)\r
1542         || element.detachEvent))\r
1543       name = 'keydown';\r
1545     if (element.removeEventListener) {\r
1546       element.removeEventListener(name, observer, useCapture);\r
1547     } else if (element.detachEvent) {\r
1548       element.detachEvent('on' + name, observer);\r
1549     }\r
1550   }\r
1551 });\r
1553 /* prevent memory leaks in IE */\r
1554 Event.observe(window, 'unload', Event.unloadCache, false);\r
1555 var Position = {\r
1556   // set to true if needed, warning: firefox performance problems\r
1557   // NOT neeeded for page scrolling, only if draggable contained in\r
1558   // scrollable elements\r
1559   includeScrollOffsets: false,\r
1561   // must be called before calling withinIncludingScrolloffset, every time the\r
1562   // page is scrolled\r
1563   prepare: function() {\r
1564     this.deltaX =  window.pageXOffset\r
1565                 || document.documentElement.scrollLeft\r
1566                 || document.body.scrollLeft\r
1567                 || 0;\r
1568     this.deltaY =  window.pageYOffset\r
1569                 || document.documentElement.scrollTop\r
1570                 || document.body.scrollTop\r
1571                 || 0;\r
1572   },\r
1574   realOffset: function(element) {\r
1575     var valueT = 0, valueL = 0;\r
1576     do {\r
1577       valueT += element.scrollTop  || 0;\r
1578       valueL += element.scrollLeft || 0;\r
1579       element = element.parentNode;\r
1580     } while (element);\r
1581     return [valueL, valueT];\r
1582   },\r
1584   cumulativeOffset: function(element) {\r
1585     var valueT = 0, valueL = 0;\r
1586     do {\r
1587       valueT += element.offsetTop  || 0;\r
1588       valueL += element.offsetLeft || 0;\r
1589       element = element.offsetParent;\r
1590     } while (element);\r
1591     return [valueL, valueT];\r
1592   },\r
1594   positionedOffset: function(element) {\r
1595     var valueT = 0, valueL = 0;\r
1596     do {\r
1597       valueT += element.offsetTop  || 0;\r
1598       valueL += element.offsetLeft || 0;\r
1599       element = element.offsetParent;\r
1600       if (element) {\r
1601         p = Element.getStyle(element, 'position');\r
1602         if (p == 'relative' || p == 'absolute') break;\r
1603       }\r
1604     } while (element);\r
1605     return [valueL, valueT];\r
1606   },\r
1608   offsetParent: function(element) {\r
1609     if (element.offsetParent) return element.offsetParent;\r
1610     if (element == document.body) return element;\r
1612     while ((element = element.parentNode) && element != document.body)\r
1613       if (Element.getStyle(element, 'position') != 'static')\r
1614         return element;\r
1616     return document.body;\r
1617   },\r
1619   // caches x/y coordinate pair to use with overlap\r
1620   within: function(element, x, y) {\r
1621     if (this.includeScrollOffsets)\r
1622       return this.withinIncludingScrolloffsets(element, x, y);\r
1623     this.xcomp = x;\r
1624     this.ycomp = y;\r
1625     this.offset = this.cumulativeOffset(element);\r
1627     return (y >= this.offset[1] &&\r
1628             y <  this.offset[1] + element.offsetHeight &&\r
1629             x >= this.offset[0] &&\r
1630             x <  this.offset[0] + element.offsetWidth);\r
1631   },\r
1633   withinIncludingScrolloffsets: function(element, x, y) {\r
1634     var offsetcache = this.realOffset(element);\r
1636     this.xcomp = x + offsetcache[0] - this.deltaX;\r
1637     this.ycomp = y + offsetcache[1] - this.deltaY;\r
1638     this.offset = this.cumulativeOffset(element);\r
1640     return (this.ycomp >= this.offset[1] &&\r
1641             this.ycomp <  this.offset[1] + element.offsetHeight &&\r
1642             this.xcomp >= this.offset[0] &&\r
1643             this.xcomp <  this.offset[0] + element.offsetWidth);\r
1644   },\r
1646   // within must be called directly before\r
1647   overlap: function(mode, element) {\r
1648     if (!mode) return 0;\r
1649     if (mode == 'vertical')\r
1650       return ((this.offset[1] + element.offsetHeight) - this.ycomp) /\r
1651         element.offsetHeight;\r
1652     if (mode == 'horizontal')\r
1653       return ((this.offset[0] + element.offsetWidth) - this.xcomp) /\r
1654         element.offsetWidth;\r
1655   },\r
1657   clone: function(source, target) {\r
1658     source = $(source);\r
1659     target = $(target);\r
1660     target.style.position = 'absolute';\r
1661     var offsets = this.cumulativeOffset(source);\r
1662     target.style.top    = offsets[1] + 'px';\r
1663     target.style.left   = offsets[0] + 'px';\r
1664     target.style.width  = source.offsetWidth + 'px';\r
1665     target.style.height = source.offsetHeight + 'px';\r
1666   },\r
1668   page: function(forElement) {\r
1669     var valueT = 0, valueL = 0;\r
1671     var element = forElement;\r
1672     do {\r
1673       valueT += element.offsetTop  || 0;\r
1674       valueL += element.offsetLeft || 0;\r
1676       // Safari fix\r
1677       if (element.offsetParent==document.body)\r
1678         if (Element.getStyle(element,'position')=='absolute') break;\r
1680     } while (element = element.offsetParent);\r
1682     element = forElement;\r
1683     do {\r
1684       valueT -= element.scrollTop  || 0;\r
1685       valueL -= element.scrollLeft || 0;\r
1686     } while (element = element.parentNode);\r
1688     return [valueL, valueT];\r
1689   },\r
1691   clone: function(source, target) {\r
1692     var options = Object.extend({\r
1693       setLeft:    true,\r
1694       setTop:     true,\r
1695       setWidth:   true,\r
1696       setHeight:  true,\r
1697       offsetTop:  0,\r
1698       offsetLeft: 0\r
1699     }, arguments[2] || {})\r
1701     // find page position of source\r
1702     source = $(source);\r
1703     var p = Position.page(source);\r
1705     // find coordinate system to use\r
1706     target = $(target);\r
1707     var delta = [0, 0];\r
1708     var parent = null;\r
1709     // delta [0,0] will do fine with position: fixed elements,\r
1710     // position:absolute needs offsetParent deltas\r
1711     if (Element.getStyle(target,'position') == 'absolute') {\r
1712       parent = Position.offsetParent(target);\r
1713       delta = Position.page(parent);\r
1714     }\r
1716     // correct by body offsets (fixes Safari)\r
1717     if (parent == document.body) {\r
1718       delta[0] -= document.body.offsetLeft;\r
1719       delta[1] -= document.body.offsetTop;\r
1720     }\r
1722     // set position\r
1723     if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';\r
1724     if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';\r
1725     if(options.setWidth)  target.style.width = source.offsetWidth + 'px';\r
1726     if(options.setHeight) target.style.height = source.offsetHeight + 'px';\r
1727   },\r
1729   absolutize: function(element) {\r
1730     element = $(element);\r
1731     if (element.style.position == 'absolute') return;\r
1732     Position.prepare();\r
1734     var offsets = Position.positionedOffset(element);\r
1735     var top     = offsets[1];\r
1736     var left    = offsets[0];\r
1737     var width   = element.clientWidth;\r
1738     var height  = element.clientHeight;\r
1740     element._originalLeft   = left - parseFloat(element.style.left  || 0);\r
1741     element._originalTop    = top  - parseFloat(element.style.top || 0);\r
1742     element._originalWidth  = element.style.width;\r
1743     element._originalHeight = element.style.height;\r
1745     element.style.position = 'absolute';\r
1746     element.style.top    = top + 'px';;\r
1747     element.style.left   = left + 'px';;\r
1748     element.style.width  = width + 'px';;\r
1749     element.style.height = height + 'px';;\r
1750   },\r
1752   relativize: function(element) {\r
1753     element = $(element);\r
1754     if (element.style.position == 'relative') return;\r
1755     Position.prepare();\r
1757     element.style.position = 'relative';\r
1758     var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);\r
1759     var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);\r
1761     element.style.top    = top + 'px';\r
1762     element.style.left   = left + 'px';\r
1763     element.style.height = element._originalHeight;\r
1764     element.style.width  = element._originalWidth;\r
1765   }\r
1768 // Safari returns margins on body which is incorrect if the child is absolutely\r
1769 // positioned.  For performance reasons, redefine Position.cumulativeOffset for\r
1770 // KHTML/WebKit only.\r
1771 if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {\r
1772   Position.cumulativeOffset = function(element) {\r
1773     var valueT = 0, valueL = 0;\r
1774     do {\r
1775       valueT += element.offsetTop  || 0;\r
1776       valueL += element.offsetLeft || 0;\r
1777       if (element.offsetParent == document.body)\r
1778         if (Element.getStyle(element, 'position') == 'absolute') break;\r
1780       element = element.offsetParent;\r
1781     } while (element);\r
1783     return [valueL, valueT];\r
1784   }\r