OSDN Git Service

add user session
[cloudmanganw/git_repo.git] / war / WEB-INF / classes / jp / sourceforge / manganetwork / page / javascripts / spinelz_lib / prototype.js
1 /*  Prototype JavaScript framework, version 1.5.0_rc1\r
2  *  (c) 2005 Sam Stephenson <sam@conio.net>\r
3  *\r
4  *  Prototype is freely distributable under the terms of an MIT-style license.\r
5  *  For details, see the Prototype web site: http://prototype.conio.net/\r
6  *\r
7 /*--------------------------------------------------------------------------*/\r
8 \r
9 var Prototype = {\r
10   Version: '1.5.0_rc1',\r
11   ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',\r
12 \r
13   emptyFunction: function() {},\r
14   K: function(x) {return x}\r
15 }\r
16 \r
17 var Class = {\r
18   create: function() {\r
19     return function() {\r
20       this.initialize.apply(this, arguments);\r
21     }\r
22   }\r
23 }\r
24 \r
25 var Abstract = new Object();\r
26 \r
27 Object.extend = function(destination, source) {\r
28   for (var property in source) {\r
29     destination[property] = source[property];\r
30   }\r
31   return destination;\r
32 }\r
33 \r
34 Object.extend(Object, {\r
35   inspect: function(object) {\r
36     try {\r
37       if (object == undefined) return 'undefined';\r
38       if (object == null) return 'null';\r
39       return object.inspect ? object.inspect() : object.toString();\r
40     } catch (e) {\r
41       if (e instanceof RangeError) return '...';\r
42       throw e;\r
43     }\r
44   },\r
45 \r
46   keys: function(object) {\r
47     var keys = [];\r
48     for (var property in object)\r
49       keys.push(property);\r
50     return keys;\r
51   },\r
52 \r
53   values: function(object) {\r
54     var values = [];\r
55     for (var property in object)\r
56       values.push(object[property]);\r
57     return values;\r
58   },\r
59 \r
60   clone: function(object) {\r
61     return Object.extend({}, object);\r
62   }\r
63 });\r
64 \r
65 Function.prototype.bind = function() {\r
66   var __method = this, args = $A(arguments), object = args.shift();\r
67   return function() {\r
68     return __method.apply(object, args.concat($A(arguments)));\r
69   }\r
70 }\r
71 \r
72 Function.prototype.bindAsEventListener = function(object) {\r
73   var __method = this, args = $A(arguments), object = args.shift();\r
74   return function(event) {\r
75     return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));\r
76   }\r
77 }\r
78 \r
79 Object.extend(Number.prototype, {\r
80   toColorPart: function() {\r
81     var digits = this.toString(16);\r
82     if (this < 16) return '0' + digits;\r
83     return digits;\r
84   },\r
85 \r
86   succ: function() {\r
87     return this + 1;\r
88   },\r
89 \r
90   times: function(iterator) {\r
91     $R(0, this, true).each(iterator);\r
92     return this;\r
93   }\r
94 });\r
95 \r
96 var Try = {\r
97   these: function() {\r
98     var returnValue;\r
99 \r
100     for (var i = 0; i < arguments.length; i++) {\r
101       var lambda = arguments[i];\r
102       try {\r
103         returnValue = lambda();\r
104         break;\r
105       } catch (e) {}\r
106     }\r
107 \r
108     return returnValue;\r
109   }\r
110 }\r
111 \r
112 /*--------------------------------------------------------------------------*/\r
113 \r
114 var PeriodicalExecuter = Class.create();\r
115 PeriodicalExecuter.prototype = {\r
116   initialize: function(callback, frequency) {\r
117     this.callback = callback;\r
118     this.frequency = frequency;\r
119     this.currentlyExecuting = false;\r
120 \r
121     this.registerCallback();\r
122   },\r
123 \r
124   registerCallback: function() {\r
125     this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);\r
126   },\r
127 \r
128   stop: function() {\r
129     if (!this.timer) return;\r
130     clearInterval(this.timer);\r
131     this.timer = null;\r
132   },\r
133 \r
134   onTimerEvent: function() {\r
135     if (!this.currentlyExecuting) {\r
136       try {\r
137         this.currentlyExecuting = true;\r
138         this.callback(this);\r
139       } finally {\r
140         this.currentlyExecuting = false;\r
141       }\r
142     }\r
143   }\r
144 }\r
145 Object.extend(String.prototype, {\r
146   gsub: function(pattern, replacement) {\r
147     var result = '', source = this, match;\r
148     replacement = arguments.callee.prepareReplacement(replacement);\r
149 \r
150     while (source.length > 0) {\r
151       if (match = source.match(pattern)) {\r
152         result += source.slice(0, match.index);\r
153         result += (replacement(match) || '').toString();\r
154         source  = source.slice(match.index + match[0].length);\r
155       } else {\r
156         result += source, source = '';\r
157       }\r
158     }\r
159     return result;\r
160   },\r
161 \r
162   sub: function(pattern, replacement, count) {\r
163     replacement = this.gsub.prepareReplacement(replacement);\r
164     count = count === undefined ? 1 : count;\r
165 \r
166     return this.gsub(pattern, function(match) {\r
167       if (--count < 0) return match[0];\r
168       return replacement(match);\r
169     });\r
170   },\r
171 \r
172   scan: function(pattern, iterator) {\r
173     this.gsub(pattern, iterator);\r
174     return this;\r
175   },\r
176 \r
177   truncate: function(length, truncation) {\r
178     length = length || 30;\r
179     truncation = truncation === undefined ? '...' : truncation;\r
180     return this.length > length ?\r
181       this.slice(0, length - truncation.length) + truncation : this;\r
182   },\r
183 \r
184   strip: function() {\r
185     return this.replace(/^\s+/, '').replace(/\s+$/, '');\r
186   },\r
187 \r
188   stripTags: function() {\r
189     return this.replace(/<\/?[^>]+>/gi, '');\r
190   },\r
191 \r
192   stripScripts: function() {\r
193     return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');\r
194   },\r
195 \r
196   extractScripts: function() {\r
197     var matchAll = new RegExp(Prototype.ScriptFragment, 'img');\r
198     var matchOne = new RegExp(Prototype.ScriptFragment, 'im');\r
199     return (this.match(matchAll) || []).map(function(scriptTag) {\r
200       return (scriptTag.match(matchOne) || ['', ''])[1];\r
201     });\r
202   },\r
203 \r
204   evalScripts: function() {\r
205     return this.extractScripts().map(function(script) { return eval(script) });\r
206   },\r
207 \r
208   escapeHTML: function() {\r
209     var div = document.createElement('div');\r
210     var text = document.createTextNode(this);\r
211     div.appendChild(text);\r
212     return div.innerHTML;\r
213   },\r
214 \r
215   unescapeHTML: function() {\r
216     var div = document.createElement('div');\r
217     div.innerHTML = this.stripTags();\r
218     return div.childNodes[0] ? div.childNodes[0].nodeValue : '';\r
219   },\r
220 \r
221   toQueryParams: function() {\r
222     var pairs = this.match(/^\??(.*)$/)[1].split('&');\r
223     return pairs.inject({}, function(params, pairString) {\r
224       var pair  = pairString.split('=');\r
225       var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;\r
226       params[decodeURIComponent(pair[0])] = value;\r
227       return params;\r
228     });\r
229   },\r
230 \r
231   toArray: function() {\r
232     return this.split('');\r
233   },\r
234 \r
235   camelize: function() {\r
236     var oStringList = this.split('-');\r
237     if (oStringList.length == 1) return oStringList[0];\r
238 \r
239     var camelizedString = this.indexOf('-') == 0\r
240       ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)\r
241       : oStringList[0];\r
242 \r
243     for (var i = 1, len = oStringList.length; i < len; i++) {\r
244       var s = oStringList[i];\r
245       camelizedString += s.charAt(0).toUpperCase() + s.substring(1);\r
246     }\r
247 \r
248     return camelizedString;\r
249   },\r
250 \r
251   inspect: function(useDoubleQuotes) {\r
252     var escapedString = this.replace(/\\/g, '\\\\');\r
253     if (useDoubleQuotes)\r
254       return '"' + escapedString.replace(/"/g, '\\"') + '"';\r
255     else\r
256       return "'" + escapedString.replace(/'/g, '\\\'') + "'";\r
257   }\r
258 });\r
259 \r
260 String.prototype.gsub.prepareReplacement = function(replacement) {\r
261   if (typeof replacement == 'function') return replacement;\r
262   var template = new Template(replacement);\r
263   return function(match) { return template.evaluate(match) };\r
264 }\r
265 \r
266 String.prototype.parseQuery = String.prototype.toQueryParams;\r
267 \r
268 var Template = Class.create();\r
269 Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;\r
270 Template.prototype = {\r
271   initialize: function(template, pattern) {\r
272     this.template = template.toString();\r
273     this.pattern  = pattern || Template.Pattern;\r
274   },\r
275 \r
276   evaluate: function(object) {\r
277     return this.template.gsub(this.pattern, function(match) {\r
278       var before = match[1];\r
279       if (before == '\\') return match[2];\r
280       return before + (object[match[3]] || '').toString();\r
281     });\r
282   }\r
283 }\r
284 \r
285 var $break    = new Object();\r
286 var $continue = new Object();\r
287 \r
288 var Enumerable = {\r
289   each: function(iterator) {\r
290     var index = 0;\r
291     try {\r
292       this._each(function(value) {\r
293         try {\r
294           iterator(value, index++);\r
295         } catch (e) {\r
296           if (e != $continue) throw e;\r
297         }\r
298       });\r
299     } catch (e) {\r
300       if (e != $break) throw e;\r
301     }\r
302   },\r
303 \r
304   all: function(iterator) {\r
305     var result = true;\r
306     this.each(function(value, index) {\r
307       result = result && !!(iterator || Prototype.K)(value, index);\r
308       if (!result) throw $break;\r
309     });\r
310     return result;\r
311   },\r
312 \r
313   any: function(iterator) {\r
314     var result = false;\r
315     this.each(function(value, index) {\r
316       if (result = !!(iterator || Prototype.K)(value, index))\r
317         throw $break;\r
318     });\r
319     return result;\r
320   },\r
321 \r
322   collect: function(iterator) {\r
323     var results = [];\r
324     this.each(function(value, index) {\r
325       results.push(iterator(value, index));\r
326     });\r
327     return results;\r
328   },\r
329 \r
330   detect: function (iterator) {\r
331     var result;\r
332     this.each(function(value, index) {\r
333       if (iterator(value, index)) {\r
334         result = value;\r
335         throw $break;\r
336       }\r
337     });\r
338     return result;\r
339   },\r
340 \r
341   findAll: function(iterator) {\r
342     var results = [];\r
343     this.each(function(value, index) {\r
344       if (iterator(value, index))\r
345         results.push(value);\r
346     });\r
347     return results;\r
348   },\r
349 \r
350   grep: function(pattern, iterator) {\r
351     var results = [];\r
352     this.each(function(value, index) {\r
353       var stringValue = value.toString();\r
354       if (stringValue.match(pattern))\r
355         results.push((iterator || Prototype.K)(value, index));\r
356     })\r
357     return results;\r
358   },\r
359 \r
360   include: function(object) {\r
361     var found = false;\r
362     this.each(function(value) {\r
363       if (value == object) {\r
364         found = true;\r
365         throw $break;\r
366       }\r
367     });\r
368     return found;\r
369   },\r
370 \r
371   inject: function(memo, iterator) {\r
372     this.each(function(value, index) {\r
373       memo = iterator(memo, value, index);\r
374     });\r
375     return memo;\r
376   },\r
377 \r
378   invoke: function(method) {\r
379     var args = $A(arguments).slice(1);\r
380     return this.collect(function(value) {\r
381       return value[method].apply(value, args);\r
382     });\r
383   },\r
384 \r
385   max: function(iterator) {\r
386     var result;\r
387     this.each(function(value, index) {\r
388       value = (iterator || Prototype.K)(value, index);\r
389       if (result == undefined || value >= result)\r
390         result = value;\r
391     });\r
392     return result;\r
393   },\r
394 \r
395   min: function(iterator) {\r
396     var result;\r
397     this.each(function(value, index) {\r
398       value = (iterator || Prototype.K)(value, index);\r
399       if (result == undefined || value < result)\r
400         result = value;\r
401     });\r
402     return result;\r
403   },\r
404 \r
405   partition: function(iterator) {\r
406     var trues = [], falses = [];\r
407     this.each(function(value, index) {\r
408       ((iterator || Prototype.K)(value, index) ?\r
409         trues : falses).push(value);\r
410     });\r
411     return [trues, falses];\r
412   },\r
413 \r
414   pluck: function(property) {\r
415     var results = [];\r
416     this.each(function(value, index) {\r
417       results.push(value[property]);\r
418     });\r
419     return results;\r
420   },\r
421 \r
422   reject: function(iterator) {\r
423     var results = [];\r
424     this.each(function(value, index) {\r
425       if (!iterator(value, index))\r
426         results.push(value);\r
427     });\r
428     return results;\r
429   },\r
430 \r
431   sortBy: function(iterator) {\r
432     return this.collect(function(value, index) {\r
433       return {value: value, criteria: iterator(value, index)};\r
434     }).sort(function(left, right) {\r
435       var a = left.criteria, b = right.criteria;\r
436       return a < b ? -1 : a > b ? 1 : 0;\r
437     }).pluck('value');\r
438   },\r
439 \r
440   toArray: function() {\r
441     return this.collect(Prototype.K);\r
442   },\r
443 \r
444   zip: function() {\r
445     var iterator = Prototype.K, args = $A(arguments);\r
446     if (typeof args.last() == 'function')\r
447       iterator = args.pop();\r
448 \r
449     var collections = [this].concat(args).map($A);\r
450     return this.map(function(value, index) {\r
451       return iterator(collections.pluck(index));\r
452     });\r
453   },\r
454 \r
455   inspect: function() {\r
456     return '#<Enumerable:' + this.toArray().inspect() + '>';\r
457   }\r
458 }\r
459 \r
460 Object.extend(Enumerable, {\r
461   map:     Enumerable.collect,\r
462   find:    Enumerable.detect,\r
463   select:  Enumerable.findAll,\r
464   member:  Enumerable.include,\r
465   entries: Enumerable.toArray\r
466 });\r
467 var $A = Array.from = function(iterable) {\r
468   if (!iterable) return [];\r
469   if (iterable.toArray) {\r
470     return iterable.toArray();\r
471   } else {\r
472     var results = [];\r
473     for (var i = 0; i < iterable.length; i++)\r
474       results.push(iterable[i]);\r
475     return results;\r
476   }\r
477 }\r
478 \r
479 Object.extend(Array.prototype, Enumerable);\r
480 \r
481 if (!Array.prototype._reverse)\r
482   Array.prototype._reverse = Array.prototype.reverse;\r
483 \r
484 Object.extend(Array.prototype, {\r
485   _each: function(iterator) {\r
486     for (var i = 0; i < this.length; i++)\r
487       iterator(this[i]);\r
488   },\r
489 \r
490   clear: function() {\r
491     this.length = 0;\r
492     return this;\r
493   },\r
494 \r
495   first: function() {\r
496     return this[0];\r
497   },\r
498 \r
499   last: function() {\r
500     return this[this.length - 1];\r
501   },\r
502 \r
503   compact: function() {\r
504     return this.select(function(value) {\r
505       return value != undefined || value != null;\r
506     });\r
507   },\r
508 \r
509   flatten: function() {\r
510     return this.inject([], function(array, value) {\r
511       return array.concat(value && value.constructor == Array ?\r
512         value.flatten() : [value]);\r
513     });\r
514   },\r
515 \r
516   without: function() {\r
517     var values = $A(arguments);\r
518     return this.select(function(value) {\r
519       return !values.include(value);\r
520     });\r
521   },\r
522 \r
523   indexOf: function(object) {\r
524     for (var i = 0; i < this.length; i++)\r
525       if (this[i] == object) return i;\r
526     return -1;\r
527   },\r
528 \r
529   reverse: function(inline) {\r
530     return (inline !== false ? this : this.toArray())._reverse();\r
531   },\r
532 \r
533   reduce: function() {\r
534     return this.length > 1 ? this : this[0];\r
535   },\r
536 \r
537   uniq: function() {\r
538     return this.inject([], function(array, value) {\r
539       return array.include(value) ? array : array.concat([value]);\r
540     });\r
541   },\r
542 \r
543   inspect: function() {\r
544     return '[' + this.map(Object.inspect).join(', ') + ']';\r
545   }\r
546 });\r
547 var Hash = {\r
548   _each: function(iterator) {\r
549     for (var key in this) {\r
550       var value = this[key];\r
551       if (typeof value == 'function') continue;\r
552 \r
553       var pair = [key, value];\r
554       pair.key = key;\r
555       pair.value = value;\r
556       iterator(pair);\r
557     }\r
558   },\r
559 \r
560   keys: function() {\r
561     return this.pluck('key');\r
562   },\r
563 \r
564   values: function() {\r
565     return this.pluck('value');\r
566   },\r
567 \r
568   merge: function(hash) {\r
569     return $H(hash).inject($H(this), function(mergedHash, pair) {\r
570       mergedHash[pair.key] = pair.value;\r
571       return mergedHash;\r
572     });\r
573   },\r
574 \r
575   toQueryString: function() {\r
576     return this.map(function(pair) {\r
577       return pair.map(encodeURIComponent).join('=');\r
578     }).join('&');\r
579   },\r
580 \r
581   inspect: function() {\r
582     return '#<Hash:{' + this.map(function(pair) {\r
583       return pair.map(Object.inspect).join(': ');\r
584     }).join(', ') + '}>';\r
585   }\r
586 }\r
587 \r
588 function $H(object) {\r
589   var hash = Object.extend({}, object || {});\r
590   Object.extend(hash, Enumerable);\r
591   Object.extend(hash, Hash);\r
592   return hash;\r
593 }\r
594 ObjectRange = Class.create();\r
595 Object.extend(ObjectRange.prototype, Enumerable);\r
596 Object.extend(ObjectRange.prototype, {\r
597   initialize: function(start, end, exclusive) {\r
598     this.start = start;\r
599     this.end = end;\r
600     this.exclusive = exclusive;\r
601   },\r
602 \r
603   _each: function(iterator) {\r
604     var value = this.start;\r
605     while (this.include(value)) {\r
606       iterator(value);\r
607       value = value.succ();\r
608     }\r
609   },\r
610 \r
611   include: function(value) {\r
612     if (value < this.start)\r
613       return false;\r
614     if (this.exclusive)\r
615       return value < this.end;\r
616     return value <= this.end;\r
617   }\r
618 });\r
619 \r
620 var $R = function(start, end, exclusive) {\r
621   return new ObjectRange(start, end, exclusive);\r
622 }\r
623 \r
624 var Ajax = {\r
625   getTransport: function() {\r
626     return Try.these(\r
627       function() {return new XMLHttpRequest()},\r
628       function() {return new ActiveXObject('Msxml2.XMLHTTP')},\r
629       function() {return new ActiveXObject('Microsoft.XMLHTTP')}\r
630     ) || false;\r
631   },\r
632 \r
633   activeRequestCount: 0\r
634 }\r
635 \r
636 Ajax.Responders = {\r
637   responders: [],\r
638 \r
639   _each: function(iterator) {\r
640     this.responders._each(iterator);\r
641   },\r
642 \r
643   register: function(responderToAdd) {\r
644     if (!this.include(responderToAdd))\r
645       this.responders.push(responderToAdd);\r
646   },\r
647 \r
648   unregister: function(responderToRemove) {\r
649     this.responders = this.responders.without(responderToRemove);\r
650   },\r
651 \r
652   dispatch: function(callback, request, transport, json) {\r
653     this.each(function(responder) {\r
654       if (responder[callback] && typeof responder[callback] == 'function') {\r
655         try {\r
656           responder[callback].apply(responder, [request, transport, json]);\r
657         } catch (e) {}\r
658       }\r
659     });\r
660   }\r
661 };\r
662 \r
663 Object.extend(Ajax.Responders, Enumerable);\r
664 \r
665 Ajax.Responders.register({\r
666   onCreate: function() {\r
667     Ajax.activeRequestCount++;\r
668   },\r
669 \r
670   onComplete: function() {\r
671     Ajax.activeRequestCount--;\r
672   }\r
673 });\r
674 \r
675 Ajax.Base = function() {};\r
676 Ajax.Base.prototype = {\r
677   setOptions: function(options) {\r
678     this.options = {\r
679       method:       'post',\r
680       asynchronous: true,\r
681       contentType:  'application/x-www-form-urlencoded',\r
682       parameters:   ''\r
683     }\r
684     Object.extend(this.options, options || {});\r
685   },\r
686 \r
687   responseIsSuccess: function() {\r
688     return this.transport.status == undefined\r
689         || this.transport.status == 0\r
690         || (this.transport.status >= 200 && this.transport.status < 300);\r
691   },\r
692 \r
693   responseIsFailure: function() {\r
694     return !this.responseIsSuccess();\r
695   }\r
696 }\r
697 \r
698 Ajax.Request = Class.create();\r
699 Ajax.Request.Events =\r
700   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];\r
701 \r
702 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {\r
703   initialize: function(url, options) {\r
704     this.transport = Ajax.getTransport();\r
705     this.setOptions(options);\r
706     this.request(url);\r
707   },\r
708 \r
709   request: function(url) {\r
710     var parameters = this.options.parameters || '';\r
711     if (parameters.length > 0) parameters += '&_=';\r
712 \r
713     /* Simulate other verbs over post */\r
714     if (this.options.method != 'get' && this.options.method != 'post') {\r
715       parameters += (parameters.length > 0 ? '&' : '') + '_method=' + this.options.method;\r
716       this.options.method = 'post';\r
717     }\r
718 \r
719     try {\r
720       this.url = url;\r
721       if (this.options.method == 'get' && parameters.length > 0)\r
722         this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;\r
723 \r
724       Ajax.Responders.dispatch('onCreate', this, this.transport);\r
725 \r
726       this.transport.open(this.options.method, this.url,\r
727         this.options.asynchronous);\r
728 \r
729       if (this.options.asynchronous)\r
730         setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);\r
731 \r
732       this.transport.onreadystatechange = this.onStateChange.bind(this);\r
733       this.setRequestHeaders();\r
734 \r
735       var body = this.options.postBody ? this.options.postBody : parameters;\r
736       this.transport.send(this.options.method == 'post' ? body : null);\r
737 \r
738       /* Force Firefox to handle ready state 4 for synchronous requests */\r
739       if (!this.options.asynchronous && this.transport.overrideMimeType)\r
740         this.onStateChange();\r
741 \r
742     } catch (e) {\r
743       this.dispatchException(e);\r
744     }\r
745   },\r
746 \r
747   setRequestHeaders: function() {\r
748     var requestHeaders =\r
749       ['X-Requested-With', 'XMLHttpRequest',\r
750        'X-Prototype-Version', Prototype.Version,\r
751        'Accept', 'text/javascript, text/html, application/xml, text/xml, */*'];\r
752 \r
753     if (this.options.method == 'post') {\r
754       requestHeaders.push('Content-type', this.options.contentType);\r
755 \r
756       /* Force "Connection: close" for Mozilla browsers to work around\r
757        * a bug where XMLHttpReqeuest sends an incorrect Content-length\r
758        * header. See Mozilla Bugzilla #246651.\r
759        */\r
760       if (this.transport.overrideMimeType)\r
761         requestHeaders.push('Connection', 'close');\r
762     }\r
763 \r
764     if (this.options.requestHeaders)\r
765       requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);\r
766 \r
767     for (var i = 0; i < requestHeaders.length; i += 2)\r
768       this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);\r
769   },\r
770 \r
771   onStateChange: function() {\r
772     var readyState = this.transport.readyState;\r
773     if (readyState != 1)\r
774       this.respondToReadyState(this.transport.readyState);\r
775   },\r
776 \r
777   header: function(name) {\r
778     try {\r
779       return this.transport.getResponseHeader(name);\r
780     } catch (e) {}\r
781   },\r
782 \r
783   evalJSON: function() {\r
784     try {\r
785       return eval('(' + this.header('X-JSON') + ')');\r
786     } catch (e) {}\r
787   },\r
788 \r
789   evalResponse: function() {\r
790     try {\r
791       return eval(this.transport.responseText);\r
792     } catch (e) {\r
793       this.dispatchException(e);\r
794     }\r
795   },\r
796 \r
797   respondToReadyState: function(readyState) {\r
798     var event = Ajax.Request.Events[readyState];\r
799     var transport = this.transport, json = this.evalJSON();\r
800 \r
801     if (event == 'Complete') {\r
802       try {\r
803         (this.options['on' + this.transport.status]\r
804          || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]\r
805          || Prototype.emptyFunction)(transport, json);\r
806       } catch (e) {\r
807         this.dispatchException(e);\r
808       }\r
809 \r
810       if ((this.header('Content-type') || '').match(/^text\/javascript/i))\r
811         this.evalResponse();\r
812     }\r
813 \r
814     try {\r
815       (this.options['on' + event] || Prototype.emptyFunction)(transport, json);\r
816       Ajax.Responders.dispatch('on' + event, this, transport, json);\r
817     } catch (e) {\r
818       this.dispatchException(e);\r
819     }\r
820 \r
821     /* Avoid memory leak in MSIE: clean up the oncomplete event handler */\r
822     if (event == 'Complete')\r
823       this.transport.onreadystatechange = Prototype.emptyFunction;\r
824   },\r
825 \r
826   dispatchException: function(exception) {\r
827     (this.options.onException || Prototype.emptyFunction)(this, exception);\r
828     Ajax.Responders.dispatch('onException', this, exception);\r
829   }\r
830 });\r
831 \r
832 Ajax.Updater = Class.create();\r
833 \r
834 Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {\r
835   initialize: function(container, url, options) {\r
836     this.containers = {\r
837       success: container.success ? $(container.success) : $(container),\r
838       failure: container.failure ? $(container.failure) :\r
839         (container.success ? null : $(container))\r
840     }\r
841 \r
842     this.transport = Ajax.getTransport();\r
843     this.setOptions(options);\r
844 \r
845     var onComplete = this.options.onComplete || Prototype.emptyFunction;\r
846     this.options.onComplete = (function(transport, object) {\r
847       this.updateContent();\r
848       onComplete(transport, object);\r
849     }).bind(this);\r
850 \r
851     this.request(url);\r
852   },\r
853 \r
854   updateContent: function() {\r
855     var receiver = this.responseIsSuccess() ?\r
856       this.containers.success : this.containers.failure;\r
857     var response = this.transport.responseText;\r
858 \r
859     if (!this.options.evalScripts)\r
860       response = response.stripScripts();\r
861 \r
862     if (receiver) {\r
863       if (this.options.insertion) {\r
864         new this.options.insertion(receiver, response);\r
865       } else {\r
866         Element.update(receiver, response);\r
867       }\r
868     }\r
869 \r
870     if (this.responseIsSuccess()) {\r
871       if (this.onComplete)\r
872         setTimeout(this.onComplete.bind(this), 10);\r
873     }\r
874   }\r
875 });\r
876 \r
877 Ajax.PeriodicalUpdater = Class.create();\r
878 Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {\r
879   initialize: function(container, url, options) {\r
880     this.setOptions(options);\r
881     this.onComplete = this.options.onComplete;\r
882 \r
883     this.frequency = (this.options.frequency || 2);\r
884     this.decay = (this.options.decay || 1);\r
885 \r
886     this.updater = {};\r
887     this.container = container;\r
888     this.url = url;\r
889 \r
890     this.start();\r
891   },\r
892 \r
893   start: function() {\r
894     this.options.onComplete = this.updateComplete.bind(this);\r
895     this.onTimerEvent();\r
896   },\r
897 \r
898   stop: function() {\r
899     this.updater.options.onComplete = undefined;\r
900     clearTimeout(this.timer);\r
901     (this.onComplete || Prototype.emptyFunction).apply(this, arguments);\r
902   },\r
903 \r
904   updateComplete: function(request) {\r
905     if (this.options.decay) {\r
906       this.decay = (request.responseText == this.lastText ?\r
907         this.decay * this.options.decay : 1);\r
908 \r
909       this.lastText = request.responseText;\r
910     }\r
911     this.timer = setTimeout(this.onTimerEvent.bind(this),\r
912       this.decay * this.frequency * 1000);\r
913   },\r
914 \r
915   onTimerEvent: function() {\r
916     this.updater = new Ajax.Updater(this.container, this.url, this.options);\r
917   }\r
918 });\r
919 function $() {\r
920   var results = [], element;\r
921   for (var i = 0; i < arguments.length; i++) {\r
922     element = arguments[i];\r
923     if (typeof element == 'string')\r
924       element = document.getElementById(element);\r
925     results.push(Element.extend(element));\r
926   }\r
927   return results.reduce();\r
928 }\r
929 \r
930 document.getElementsByClassName = function(className, parentElement) {\r
931   var children = ($(parentElement) || document.body).getElementsByTagName('*');\r
932   return $A(children).inject([], function(elements, child) {\r
933     if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))\r
934       elements.push(Element.extend(child));\r
935     return elements;\r
936   });\r
937 }\r
938 \r
939 /*--------------------------------------------------------------------------*/\r
940 \r
941 if (!window.Element)\r
942   var Element = new Object();\r
943 \r
944 Element.extend = function(element) {\r
945   if (!element) return;\r
946   if (_nativeExtensions || element.nodeType == 3) return element;\r
947 \r
948   if (!element._extended && element.tagName && element != window) {\r
949     var methods = Object.clone(Element.Methods), cache = Element.extend.cache;\r
950 \r
951     if (element.tagName == 'FORM')\r
952       Object.extend(methods, Form.Methods);\r
953     if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))\r
954       Object.extend(methods, Form.Element.Methods);\r
955 \r
956     for (var property in methods) {\r
957       var value = methods[property];\r
958       if (typeof value == 'function')\r
959         element[property] = cache.findOrStore(value);\r
960     }\r
961   }\r
962 \r
963   element._extended = true;\r
964   return element;\r
965 }\r
966 \r
967 Element.extend.cache = {\r
968   findOrStore: function(value) {\r
969     return this[value] = this[value] || function() {\r
970       return value.apply(null, [this].concat($A(arguments)));\r
971     }\r
972   }\r
973 }\r
974 \r
975 Element.Methods = {\r
976   visible: function(element) {\r
977     return $(element).style.display != 'none';\r
978   },\r
979 \r
980   toggle: function(element) {\r
981     element = $(element);\r
982     Element[Element.visible(element) ? 'hide' : 'show'](element);\r
983     return element;\r
984   },\r
985 \r
986   hide: function(element) {\r
987     $(element).style.display = 'none';\r
988     return element;\r
989   },\r
990 \r
991   show: function(element) {\r
992     $(element).style.display = '';\r
993     return element;\r
994   },\r
995 \r
996   remove: function(element) {\r
997     element = $(element);\r
998     element.parentNode.removeChild(element);\r
999     return element;\r
1000   },\r
1001 \r
1002   update: function(element, html) {\r
1003     $(element).innerHTML = html.stripScripts();\r
1004     setTimeout(function() {html.evalScripts()}, 10);\r
1005     return element;\r
1006   },\r
1007 \r
1008   replace: function(element, html) {\r
1009     element = $(element);\r
1010     if (element.outerHTML) {\r
1011       element.outerHTML = html.stripScripts();\r
1012     } else {\r
1013       var range = element.ownerDocument.createRange();\r
1014       range.selectNodeContents(element);\r
1015       element.parentNode.replaceChild(\r
1016         range.createContextualFragment(html.stripScripts()), element);\r
1017     }\r
1018     setTimeout(function() {html.evalScripts()}, 10);\r
1019     return element;\r
1020   },\r
1021 \r
1022   inspect: function(element) {\r
1023     element = $(element);\r
1024     var result = '<' + element.tagName.toLowerCase();\r
1025     $H({'id': 'id', 'className': 'class'}).each(function(pair) {\r
1026       var property = pair.first(), attribute = pair.last();\r
1027       var value = (element[property] || '').toString();\r
1028       if (value) result += ' ' + attribute + '=' + value.inspect(true);\r
1029     });\r
1030     return result + '>';\r
1031   },\r
1032 \r
1033   recursivelyCollect: function(element, property) {\r
1034     element = $(element);\r
1035     var elements = [];\r
1036     while (element = element[property])\r
1037       if (element.nodeType == 1)\r
1038         elements.push(Element.extend(element));\r
1039     return elements;\r
1040   },\r
1041 \r
1042   ancestors: function(element) {\r
1043     return $(element).recursivelyCollect('parentNode');\r
1044   },\r
1045 \r
1046   descendants: function(element) {\r
1047     element = $(element);\r
1048     return $A(element.getElementsByTagName('*'));\r
1049   },\r
1050 \r
1051   previousSiblings: function(element) {\r
1052     return $(element).recursivelyCollect('previousSibling');\r
1053   },\r
1054 \r
1055   nextSiblings: function(element) {\r
1056     return $(element).recursivelyCollect('nextSibling');\r
1057   },\r
1058 \r
1059   siblings: function(element) {\r
1060     element = $(element);\r
1061     return element.previousSiblings().reverse().concat(element.nextSiblings());\r
1062   },\r
1063 \r
1064   match: function(element, selector) {\r
1065     element = $(element);\r
1066     if (typeof selector == 'string')\r
1067       selector = new Selector(selector);\r
1068     return selector.match(element);\r
1069   },\r
1070 \r
1071   up: function(element, expression, index) {\r
1072     return Selector.findElement($(element).ancestors(), expression, index);\r
1073   },\r
1074 \r
1075   down: function(element, expression, index) {\r
1076     return Selector.findElement($(element).descendants(), expression, index);\r
1077   },\r
1078 \r
1079   previous: function(element, expression, index) {\r
1080     return Selector.findElement($(element).previousSiblings(), expression, index);\r
1081   },\r
1082 \r
1083   next: function(element, expression, index) {\r
1084     return Selector.findElement($(element).nextSiblings(), expression, index);\r
1085   },\r
1086 \r
1087   getElementsBySelector: function() {\r
1088     var args = $A(arguments), element = $(args.shift());\r
1089     return Selector.findChildElements(element, args);\r
1090   },\r
1091 \r
1092   getElementsByClassName: function(element, className) {\r
1093     element = $(element);\r
1094     return document.getElementsByClassName(className, element);\r
1095   },\r
1096 \r
1097   getHeight: function(element) {\r
1098     element = $(element);\r
1099     return element.offsetHeight;\r
1100   },\r
1101 \r
1102   classNames: function(element) {\r
1103     return new Element.ClassNames(element);\r
1104   },\r
1105 \r
1106   hasClassName: function(element, className) {\r
1107     if (!(element = $(element))) return;\r
1108     return Element.classNames(element).include(className);\r
1109   },\r
1110 \r
1111   addClassName: function(element, className) {\r
1112     if (!(element = $(element))) return;\r
1113     Element.classNames(element).add(className);\r
1114     return element;\r
1115   },\r
1116 \r
1117   removeClassName: function(element, className) {\r
1118     if (!(element = $(element))) return;\r
1119     Element.classNames(element).remove(className);\r
1120     return element;\r
1121   },\r
1122 \r
1123   observe: function() {\r
1124     Event.observe.apply(Event, arguments);\r
1125     return $A(arguments).first();\r
1126   },\r
1127 \r
1128   stopObserving: function() {\r
1129     Event.stopObserving.apply(Event, arguments);\r
1130     return $A(arguments).first();\r
1131   },\r
1132 \r
1133   // removes whitespace-only text node children\r
1134   cleanWhitespace: function(element) {\r
1135     element = $(element);\r
1136     var node = element.firstChild;\r
1137     while (node) {\r
1138       var nextNode = node.nextSibling;\r
1139       if (node.nodeType == 3 && !/\S/.test(node.nodeValue))\r
1140         element.removeChild(node);\r
1141       node = nextNode;\r
1142     }\r
1143     return element;\r
1144   },\r
1145 \r
1146   empty: function(element) {\r
1147     return $(element).innerHTML.match(/^\s*$/);\r
1148   },\r
1149 \r
1150   childOf: function(element, ancestor) {\r
1151     element = $(element), ancestor = $(ancestor);\r
1152     while (element = element.parentNode)\r
1153       if (element == ancestor) return true;\r
1154     return false;\r
1155   },\r
1156 \r
1157   scrollTo: function(element) {\r
1158     element = $(element);\r
1159     var x = element.x ? element.x : element.offsetLeft,\r
1160         y = element.y ? element.y : element.offsetTop;\r
1161     window.scrollTo(x, y);\r
1162     return element;\r
1163   },\r
1164 \r
1165   getStyle: function(element, style) {\r
1166     element = $(element);\r
1167     var value = element.style[style.camelize()];\r
1168     if (!value) {\r
1169       if (document.defaultView && document.defaultView.getComputedStyle) {\r
1170         var css = document.defaultView.getComputedStyle(element, null);\r
1171         value = css ? css.getPropertyValue(style) : null;\r
1172       } else if (element.currentStyle) {\r
1173         value = element.currentStyle[style.camelize()];\r
1174       }\r
1175     }\r
1176 \r
1177     if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))\r
1178       if (Element.getStyle(element, 'position') == 'static') value = 'auto';\r
1179 \r
1180     return value == 'auto' ? null : value;\r
1181   },\r
1182 \r
1183   setStyle: function(element, style) {\r
1184     element = $(element);\r
1185     for (var name in style)\r
1186       element.style[name.camelize()] = style[name];\r
1187     return element;\r
1188   },\r
1189 \r
1190   getDimensions: function(element) {\r
1191     element = $(element);\r
1192     if (Element.getStyle(element, 'display') != 'none')\r
1193       return {width: element.offsetWidth, height: element.offsetHeight};\r
1194 \r
1195     // All *Width and *Height properties give 0 on elements with display none,\r
1196     // so enable the element temporarily\r
1197     var els = element.style;\r
1198     var originalVisibility = els.visibility;\r
1199     var originalPosition = els.position;\r
1200     els.visibility = 'hidden';\r
1201     els.position = 'absolute';\r
1202     els.display = '';\r
1203     var originalWidth = element.clientWidth;\r
1204     var originalHeight = element.clientHeight;\r
1205     els.display = 'none';\r
1206     els.position = originalPosition;\r
1207     els.visibility = originalVisibility;\r
1208     return {width: originalWidth, height: originalHeight};\r
1209   },\r
1210 \r
1211   makePositioned: function(element) {\r
1212     element = $(element);\r
1213     var pos = Element.getStyle(element, 'position');\r
1214     if (pos == 'static' || !pos) {\r
1215       element._madePositioned = true;\r
1216       element.style.position = 'relative';\r
1217       // Opera returns the offset relative to the positioning context, when an\r
1218       // element is position relative but top and left have not been defined\r
1219       if (window.opera) {\r
1220         element.style.top = 0;\r
1221         element.style.left = 0;\r
1222       }\r
1223     }\r
1224     return element;\r
1225   },\r
1226 \r
1227   undoPositioned: function(element) {\r
1228     element = $(element);\r
1229     if (element._madePositioned) {\r
1230       element._madePositioned = undefined;\r
1231       element.style.position =\r
1232         element.style.top =\r
1233         element.style.left =\r
1234         element.style.bottom =\r
1235         element.style.right = '';\r
1236     }\r
1237     return element;\r
1238   },\r
1239 \r
1240   makeClipping: function(element) {\r
1241     element = $(element);\r
1242     if (element._overflow) return;\r
1243     element._overflow = element.style.overflow || 'auto';\r
1244     if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')\r
1245       element.style.overflow = 'hidden';\r
1246     return element;\r
1247   },\r
1248 \r
1249   undoClipping: function(element) {\r
1250     element = $(element);\r
1251     if (!element._overflow) return;\r
1252     element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;\r
1253     element._overflow = null;\r
1254     return element;\r
1255   }\r
1256 }\r
1257 \r
1258 // IE is missing .innerHTML support for TABLE-related elements\r
1259 if(document.all){\r
1260   Element.Methods.update = function(element, html) {\r
1261     element = $(element);\r
1262     var tagName = element.tagName.toUpperCase();\r
1263     if (['THEAD','TBODY','TR','TD'].indexOf(tagName) > -1) {\r
1264       var div = document.createElement('div');\r
1265       switch (tagName) {\r
1266         case 'THEAD':\r
1267         case 'TBODY':\r
1268           div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';\r
1269           depth = 2;\r
1270           break;\r
1271         case 'TR':\r
1272           div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';\r
1273           depth = 3;\r
1274           break;\r
1275         case 'TD':\r
1276           div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';\r
1277           depth = 4;\r
1278       }\r
1279       $A(element.childNodes).each(function(node){\r
1280         element.removeChild(node)\r
1281       });\r
1282       depth.times(function(){ div = div.firstChild });\r
1283 \r
1284       $A(div.childNodes).each(\r
1285         function(node){ element.appendChild(node) });\r
1286     } else {\r
1287       element.innerHTML = html.stripScripts();\r
1288     }\r
1289     setTimeout(function() {html.evalScripts()}, 10);\r
1290     return element;\r
1291   }\r
1292 }\r
1293 \r
1294 Object.extend(Element, Element.Methods);\r
1295 \r
1296 var _nativeExtensions = false;\r
1297 \r
1298 if (!window.HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) {\r
1299   /* Emulate HTMLElement, HTMLFormElement, HTMLInputElement, HTMLTextAreaElement,\r
1300      and HTMLSelectElement in Safari */\r
1301   ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {\r
1302     var klass = window['HTML' + tag + 'Element'] = {};\r
1303     klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;\r
1304   });\r
1305 }\r
1306 \r
1307 Element.addMethods = function(methods) {\r
1308   Object.extend(Element.Methods, methods || {});\r
1309 \r
1310   function copy(methods, destination) {\r
1311     var cache = Element.extend.cache;\r
1312     for (var property in methods) {\r
1313       var value = methods[property];\r
1314       destination[property] = cache.findOrStore(value);\r
1315     }\r
1316   }\r
1317 \r
1318   if (typeof HTMLElement != 'undefined') {\r
1319     copy(Element.Methods, HTMLElement.prototype);\r
1320     copy(Form.Methods, HTMLFormElement.prototype);\r
1321     [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {\r
1322       copy(Form.Element.Methods, klass.prototype);\r
1323     });\r
1324     _nativeExtensions = true;\r
1325   }\r
1326 }\r
1327 \r
1328 var Toggle = new Object();\r
1329 Toggle.display = Element.toggle;\r
1330 \r
1331 /*--------------------------------------------------------------------------*/\r
1332 \r
1333 Abstract.Insertion = function(adjacency) {\r
1334   this.adjacency = adjacency;\r
1335 }\r
1336 \r
1337 Abstract.Insertion.prototype = {\r
1338   initialize: function(element, content) {\r
1339     this.element = $(element);\r
1340     this.content = content.stripScripts();\r
1341 \r
1342     if (this.adjacency && this.element.insertAdjacentHTML) {\r
1343       try {\r
1344         this.element.insertAdjacentHTML(this.adjacency, this.content);\r
1345       } catch (e) {\r
1346         var tagName = this.element.tagName.toLowerCase();\r
1347         if (tagName == 'tbody' || tagName == 'tr') {\r
1348           this.insertContent(this.contentFromAnonymousTable());\r
1349         } else {\r
1350           throw e;\r
1351         }\r
1352       }\r
1353     } else {\r
1354       this.range = this.element.ownerDocument.createRange();\r
1355       if (this.initializeRange) this.initializeRange();\r
1356       this.insertContent([this.range.createContextualFragment(this.content)]);\r
1357     }\r
1358 \r
1359     setTimeout(function() {content.evalScripts()}, 10);\r
1360   },\r
1361 \r
1362   contentFromAnonymousTable: function() {\r
1363     var div = document.createElement('div');\r
1364     div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';\r
1365     return $A(div.childNodes[0].childNodes[0].childNodes);\r
1366   }\r
1367 }\r
1368 \r
1369 var Insertion = new Object();\r
1370 \r
1371 Insertion.Before = Class.create();\r
1372 Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {\r
1373   initializeRange: function() {\r
1374     this.range.setStartBefore(this.element);\r
1375   },\r
1376 \r
1377   insertContent: function(fragments) {\r
1378     fragments.each((function(fragment) {\r
1379       this.element.parentNode.insertBefore(fragment, this.element);\r
1380     }).bind(this));\r
1381   }\r
1382 });\r
1383 \r
1384 Insertion.Top = Class.create();\r
1385 Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {\r
1386   initializeRange: function() {\r
1387     this.range.selectNodeContents(this.element);\r
1388     this.range.collapse(true);\r
1389   },\r
1390 \r
1391   insertContent: function(fragments) {\r
1392     fragments.reverse(false).each((function(fragment) {\r
1393       this.element.insertBefore(fragment, this.element.firstChild);\r
1394     }).bind(this));\r
1395   }\r
1396 });\r
1397 \r
1398 Insertion.Bottom = Class.create();\r
1399 Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {\r
1400   initializeRange: function() {\r
1401     this.range.selectNodeContents(this.element);\r
1402     this.range.collapse(this.element);\r
1403   },\r
1404 \r
1405   insertContent: function(fragments) {\r
1406     fragments.each((function(fragment) {\r
1407       this.element.appendChild(fragment);\r
1408     }).bind(this));\r
1409   }\r
1410 });\r
1411 \r
1412 Insertion.After = Class.create();\r
1413 Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {\r
1414   initializeRange: function() {\r
1415     this.range.setStartAfter(this.element);\r
1416   },\r
1417 \r
1418   insertContent: function(fragments) {\r
1419     fragments.each((function(fragment) {\r
1420       this.element.parentNode.insertBefore(fragment,\r
1421         this.element.nextSibling);\r
1422     }).bind(this));\r
1423   }\r
1424 });\r
1425 \r
1426 /*--------------------------------------------------------------------------*/\r
1427 \r
1428 Element.ClassNames = Class.create();\r
1429 Element.ClassNames.prototype = {\r
1430   initialize: function(element) {\r
1431     this.element = $(element);\r
1432   },\r
1433 \r
1434   _each: function(iterator) {\r
1435     this.element.className.split(/\s+/).select(function(name) {\r
1436       return name.length > 0;\r
1437     })._each(iterator);\r
1438   },\r
1439 \r
1440   set: function(className) {\r
1441     this.element.className = className;\r
1442   },\r
1443 \r
1444   add: function(classNameToAdd) {\r
1445     if (this.include(classNameToAdd)) return;\r
1446     this.set(this.toArray().concat(classNameToAdd).join(' '));\r
1447   },\r
1448 \r
1449   remove: function(classNameToRemove) {\r
1450     if (!this.include(classNameToRemove)) return;\r
1451     this.set(this.select(function(className) {\r
1452       return className != classNameToRemove;\r
1453     }).join(' '));\r
1454   },\r
1455 \r
1456   toString: function() {\r
1457     return this.toArray().join(' ');\r
1458   }\r
1459 }\r
1460 \r
1461 Object.extend(Element.ClassNames.prototype, Enumerable);\r
1462 var Selector = Class.create();\r
1463 Selector.prototype = {\r
1464   initialize: function(expression) {\r
1465     this.params = {classNames: []};\r
1466     this.expression = expression.toString().strip();\r
1467     this.parseExpression();\r
1468     this.compileMatcher();\r
1469   },\r
1470 \r
1471   parseExpression: function() {\r
1472     function abort(message) { throw 'Parse error in selector: ' + message; }\r
1473 \r
1474     if (this.expression == '')  abort('empty expression');\r
1475 \r
1476     var params = this.params, expr = this.expression, match, modifier, clause, rest;\r
1477     while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {\r
1478       params.attributes = params.attributes || [];\r
1479       params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});\r
1480       expr = match[1];\r
1481     }\r
1482 \r
1483     if (expr == '*') return this.params.wildcard = true;\r
1484 \r
1485     while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {\r
1486       modifier = match[1], clause = match[2], rest = match[3];\r
1487       switch (modifier) {\r
1488         case '#':       params.id = clause; break;\r
1489         case '.':       params.classNames.push(clause); break;\r
1490         case '':\r
1491         case undefined: params.tagName = clause.toUpperCase(); break;\r
1492         default:        abort(expr.inspect());\r
1493       }\r
1494       expr = rest;\r
1495     }\r
1496 \r
1497     if (expr.length > 0) abort(expr.inspect());\r
1498   },\r
1499 \r
1500   buildMatchExpression: function() {\r
1501     var params = this.params, conditions = [], clause;\r
1502 \r
1503     if (params.wildcard)\r
1504       conditions.push('true');\r
1505     if (clause = params.id)\r
1506       conditions.push('element.id == ' + clause.inspect());\r
1507     if (clause = params.tagName)\r
1508       conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());\r
1509     if ((clause = params.classNames).length > 0)\r
1510       for (var i = 0; i < clause.length; i++)\r
1511         conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');\r
1512     if (clause = params.attributes) {\r
1513       clause.each(function(attribute) {\r
1514         var value = 'element.getAttribute(' + attribute.name.inspect() + ')';\r
1515         var splitValueBy = function(delimiter) {\r
1516           return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';\r
1517         }\r
1518 \r
1519         switch (attribute.operator) {\r
1520           case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break;\r
1521           case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;\r
1522           case '|=':      conditions.push(\r
1523                             splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()\r
1524                           ); break;\r
1525           case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;\r
1526           case '':\r
1527           case undefined: conditions.push(value + ' != null'); break;\r
1528           default:        throw 'Unknown operator ' + attribute.operator + ' in selector';\r
1529         }\r
1530       });\r
1531     }\r
1532 \r
1533     return conditions.join(' && ');\r
1534   },\r
1535 \r
1536   compileMatcher: function() {\r
1537     this.match = new Function('element', 'if (!element.tagName) return false; \\r
1538       return ' + this.buildMatchExpression());\r
1539   },\r
1540 \r
1541   findElements: function(scope) {\r
1542     var element;\r
1543 \r
1544     if (element = $(this.params.id))\r
1545       if (this.match(element))\r
1546         if (!scope || Element.childOf(element, scope))\r
1547           return [element];\r
1548 \r
1549     scope = (scope || document).getElementsByTagName(this.params.tagName || '*');\r
1550 \r
1551     var results = [];\r
1552     for (var i = 0; i < scope.length; i++)\r
1553       if (this.match(element = scope[i]))\r
1554         results.push(Element.extend(element));\r
1555 \r
1556     return results;\r
1557   },\r
1558 \r
1559   toString: function() {\r
1560     return this.expression;\r
1561   }\r
1562 }\r
1563 \r
1564 Object.extend(Selector, {\r
1565   matchElements: function(elements, expression) {\r
1566     var selector = new Selector(expression);\r
1567     return elements.select(selector.match.bind(selector));\r
1568   },\r
1569 \r
1570   findElement: function(elements, expression, index) {\r
1571     if (typeof expression == 'number') index = expression, expression = false;\r
1572     return Selector.matchElements(elements, expression || '*')[index || 0];\r
1573   },\r
1574 \r
1575   findChildElements: function(element, expressions) {\r
1576     return expressions.map(function(expression) {\r
1577       return expression.strip().split(/\s+/).inject([null], function(results, expr) {\r
1578         var selector = new Selector(expr);\r
1579         return results.inject([], function(elements, result) {\r
1580           return elements.concat(selector.findElements(result || element));\r
1581         });\r
1582       });\r
1583     }).flatten();\r
1584   }\r
1585 });\r
1586 \r
1587 function $$() {\r
1588   return Selector.findChildElements(document, $A(arguments));\r
1589 }\r
1590 var Form = {\r
1591   reset: function(form) {\r
1592     $(form).reset();\r
1593     return form;\r
1594   }\r
1595 };\r
1596 \r
1597 Form.Methods = {\r
1598   serialize: function(form) {\r
1599     var elements = Form.getElements($(form));\r
1600     var queryComponents = new Array();\r
1601 \r
1602     for (var i = 0; i < elements.length; i++) {\r
1603       var queryComponent = Form.Element.serialize(elements[i]);\r
1604       if (queryComponent)\r
1605         queryComponents.push(queryComponent);\r
1606     }\r
1607 \r
1608     return queryComponents.join('&');\r
1609   },\r
1610 \r
1611   getElements: function(form) {\r
1612     form = $(form);\r
1613     var elements = new Array();\r
1614 \r
1615     for (var tagName in Form.Element.Serializers) {\r
1616       var tagElements = form.getElementsByTagName(tagName);\r
1617       for (var j = 0; j < tagElements.length; j++)\r
1618         elements.push(tagElements[j]);\r
1619     }\r
1620     return elements;\r
1621   },\r
1622 \r
1623   getInputs: function(form, typeName, name) {\r
1624     form = $(form);\r
1625     var inputs = form.getElementsByTagName('input');\r
1626 \r
1627     if (!typeName && !name)\r
1628       return inputs;\r
1629 \r
1630     var matchingInputs = new Array();\r
1631     for (var i = 0; i < inputs.length; i++) {\r
1632       var input = inputs[i];\r
1633       if ((typeName && input.type != typeName) ||\r
1634           (name && input.name != name))\r
1635         continue;\r
1636       matchingInputs.push(input);\r
1637     }\r
1638 \r
1639     return matchingInputs;\r
1640   },\r
1641 \r
1642   disable: function(form) {\r
1643     form = $(form);\r
1644     var elements = Form.getElements(form);\r
1645     for (var i = 0; i < elements.length; i++) {\r
1646       var element = elements[i];\r
1647       element.blur();\r
1648       element.disabled = 'true';\r
1649     }\r
1650     return form;\r
1651   },\r
1652 \r
1653   enable: function(form) {\r
1654     form = $(form);\r
1655     var elements = Form.getElements(form);\r
1656     for (var i = 0; i < elements.length; i++) {\r
1657       var element = elements[i];\r
1658       element.disabled = '';\r
1659     }\r
1660     return form;\r
1661   },\r
1662 \r
1663   findFirstElement: function(form) {\r
1664     return Form.getElements(form).find(function(element) {\r
1665       return element.type != 'hidden' && !element.disabled &&\r
1666         ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());\r
1667     });\r
1668   },\r
1669 \r
1670   focusFirstElement: function(form) {\r
1671     form = $(form);\r
1672     Field.activate(Form.findFirstElement(form));\r
1673     return form;\r
1674   }\r
1675 }\r
1676 \r
1677 Object.extend(Form, Form.Methods);\r
1678 \r
1679 /*--------------------------------------------------------------------------*/\r
1680 \r
1681 Form.Element = {\r
1682   focus: function(element) {\r
1683     $(element).focus();\r
1684     return element;\r
1685   },\r
1686 \r
1687   select: function(element) {\r
1688     $(element).select();\r
1689     return element;\r
1690   }\r
1691 }\r
1692 \r
1693 Form.Element.Methods = {\r
1694   serialize: function(element) {\r
1695     element = $(element);\r
1696     var method = element.tagName.toLowerCase();\r
1697     var parameter = Form.Element.Serializers[method](element);\r
1698 \r
1699     if (parameter) {\r
1700       var key = encodeURIComponent(parameter[0]);\r
1701       if (key.length == 0) return;\r
1702 \r
1703       if (parameter[1].constructor != Array)\r
1704         parameter[1] = [parameter[1]];\r
1705 \r
1706       return parameter[1].map(function(value) {\r
1707         return key + '=' + encodeURIComponent(value);\r
1708       }).join('&');\r
1709     }\r
1710   },\r
1711 \r
1712   getValue: function(element) {\r
1713     element = $(element);\r
1714     var method = element.tagName.toLowerCase();\r
1715     var parameter = Form.Element.Serializers[method](element);\r
1716 \r
1717     if (parameter)\r
1718       return parameter[1];\r
1719   },\r
1720 \r
1721   clear: function(element) {\r
1722     $(element).value = '';\r
1723     return element;\r
1724   },\r
1725 \r
1726   present: function(element) {\r
1727     return $(element).value != '';\r
1728   },\r
1729 \r
1730   activate: function(element) {\r
1731     element = $(element);\r
1732     element.focus();\r
1733     if (element.select)\r
1734       element.select();\r
1735     return element;\r
1736   },\r
1737 \r
1738   disable: function(element) {\r
1739     element = $(element);\r
1740     element.disabled = '';\r
1741     return element;\r
1742   },\r
1743 \r
1744   enable: function(element) {\r
1745     element = $(element);\r
1746     element.blur();\r
1747     element.disabled = 'true';\r
1748     return element;\r
1749   }\r
1750 }\r
1751 \r
1752 Object.extend(Form.Element, Form.Element.Methods);\r
1753 var Field = Form.Element;\r
1754 \r
1755 /*--------------------------------------------------------------------------*/\r
1756 \r
1757 Form.Element.Serializers = {\r
1758   input: function(element) {\r
1759     switch (element.type.toLowerCase()) {\r
1760       case 'checkbox':\r
1761       case 'radio':\r
1762         return Form.Element.Serializers.inputSelector(element);\r
1763       default:\r
1764         return Form.Element.Serializers.textarea(element);\r
1765     }\r
1766     return false;\r
1767   },\r
1768 \r
1769   inputSelector: function(element) {\r
1770     if (element.checked)\r
1771       return [element.name, element.value];\r
1772   },\r
1773 \r
1774   textarea: function(element) {\r
1775     return [element.name, element.value];\r
1776   },\r
1777 \r
1778   select: function(element) {\r
1779     return Form.Element.Serializers[element.type == 'select-one' ?\r
1780       'selectOne' : 'selectMany'](element);\r
1781   },\r
1782 \r
1783   selectOne: function(element) {\r
1784     var value = '', opt, index = element.selectedIndex;\r
1785     if (index >= 0) {\r
1786       opt = element.options[index];\r
1787       value = opt.value || opt.text;\r
1788     }\r
1789     return [element.name, value];\r
1790   },\r
1791 \r
1792   selectMany: function(element) {\r
1793     var value = [];\r
1794     for (var i = 0; i < element.length; i++) {\r
1795       var opt = element.options[i];\r
1796       if (opt.selected)\r
1797         value.push(opt.value || opt.text);\r
1798     }\r
1799     return [element.name, value];\r
1800   }\r
1801 }\r
1802 \r
1803 /*--------------------------------------------------------------------------*/\r
1804 \r
1805 var $F = Form.Element.getValue;\r
1806 \r
1807 /*--------------------------------------------------------------------------*/\r
1808 \r
1809 Abstract.TimedObserver = function() {}\r
1810 Abstract.TimedObserver.prototype = {\r
1811   initialize: function(element, frequency, callback) {\r
1812     this.frequency = frequency;\r
1813     this.element   = $(element);\r
1814     this.callback  = callback;\r
1815 \r
1816     this.lastValue = this.getValue();\r
1817     this.registerCallback();\r
1818   },\r
1819 \r
1820   registerCallback: function() {\r
1821     setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);\r
1822   },\r
1823 \r
1824   onTimerEvent: function() {\r
1825     var value = this.getValue();\r
1826     if (this.lastValue != value) {\r
1827       this.callback(this.element, value);\r
1828       this.lastValue = value;\r
1829     }\r
1830   }\r
1831 }\r
1832 \r
1833 Form.Element.Observer = Class.create();\r
1834 Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {\r
1835   getValue: function() {\r
1836     return Form.Element.getValue(this.element);\r
1837   }\r
1838 });\r
1839 \r
1840 Form.Observer = Class.create();\r
1841 Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {\r
1842   getValue: function() {\r
1843     return Form.serialize(this.element);\r
1844   }\r
1845 });\r
1846 \r
1847 /*--------------------------------------------------------------------------*/\r
1848 \r
1849 Abstract.EventObserver = function() {}\r
1850 Abstract.EventObserver.prototype = {\r
1851   initialize: function(element, callback) {\r
1852     this.element  = $(element);\r
1853     this.callback = callback;\r
1854 \r
1855     this.lastValue = this.getValue();\r
1856     if (this.element.tagName.toLowerCase() == 'form')\r
1857       this.registerFormCallbacks();\r
1858     else\r
1859       this.registerCallback(this.element);\r
1860   },\r
1861 \r
1862   onElementEvent: function() {\r
1863     var value = this.getValue();\r
1864     if (this.lastValue != value) {\r
1865       this.callback(this.element, value);\r
1866       this.lastValue = value;\r
1867     }\r
1868   },\r
1869 \r
1870   registerFormCallbacks: function() {\r
1871     var elements = Form.getElements(this.element);\r
1872     for (var i = 0; i < elements.length; i++)\r
1873       this.registerCallback(elements[i]);\r
1874   },\r
1875 \r
1876   registerCallback: function(element) {\r
1877     if (element.type) {\r
1878       switch (element.type.toLowerCase()) {\r
1879         case 'checkbox':\r
1880         case 'radio':\r
1881           Event.observe(element, 'click', this.onElementEvent.bind(this));\r
1882           break;\r
1883         default:\r
1884           Event.observe(element, 'change', this.onElementEvent.bind(this));\r
1885           break;\r
1886       }\r
1887     }\r
1888   }\r
1889 }\r
1890 \r
1891 Form.Element.EventObserver = Class.create();\r
1892 Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {\r
1893   getValue: function() {\r
1894     return Form.Element.getValue(this.element);\r
1895   }\r
1896 });\r
1897 \r
1898 Form.EventObserver = Class.create();\r
1899 Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {\r
1900   getValue: function() {\r
1901     return Form.serialize(this.element);\r
1902   }\r
1903 });\r
1904 if (!window.Event) {\r
1905   var Event = new Object();\r
1906 }\r
1907 \r
1908 Object.extend(Event, {\r
1909   KEY_BACKSPACE: 8,\r
1910   KEY_TAB:       9,\r
1911   KEY_RETURN:   13,\r
1912   KEY_ESC:      27,\r
1913   KEY_LEFT:     37,\r
1914   KEY_UP:       38,\r
1915   KEY_RIGHT:    39,\r
1916   KEY_DOWN:     40,\r
1917   KEY_DELETE:   46,\r
1918   KEY_HOME:     36,\r
1919   KEY_END:      35,\r
1920   KEY_PAGEUP:   33,\r
1921   KEY_PAGEDOWN: 34,\r
1922 \r
1923   element: function(event) {\r
1924     return event.target || event.srcElement;\r
1925   },\r
1926 \r
1927   isLeftClick: function(event) {\r
1928     return (((event.which) && (event.which == 1)) ||\r
1929             ((event.button) && (event.button == 1)));\r
1930   },\r
1931 \r
1932   pointerX: function(event) {\r
1933     return event.pageX || (event.clientX +\r
1934       (document.documentElement.scrollLeft || document.body.scrollLeft));\r
1935   },\r
1936 \r
1937   pointerY: function(event) {\r
1938     return event.pageY || (event.clientY +\r
1939       (document.documentElement.scrollTop || document.body.scrollTop));\r
1940   },\r
1941 \r
1942   stop: function(event) {\r
1943     if (event.preventDefault) {\r
1944       event.preventDefault();\r
1945       event.stopPropagation();\r
1946     } else {\r
1947       event.returnValue = false;\r
1948       event.cancelBubble = true;\r
1949     }\r
1950   },\r
1951 \r
1952   // find the first node with the given tagName, starting from the\r
1953   // node the event was triggered on; traverses the DOM upwards\r
1954   findElement: function(event, tagName) {\r
1955     var element = Event.element(event);\r
1956     while (element.parentNode && (!element.tagName ||\r
1957         (element.tagName.toUpperCase() != tagName.toUpperCase())))\r
1958       element = element.parentNode;\r
1959     return element;\r
1960   },\r
1961 \r
1962   observers: false,\r
1963 \r
1964   _observeAndCache: function(element, name, observer, useCapture) {\r
1965     if (!this.observers) this.observers = [];\r
1966     if (element.addEventListener) {\r
1967       this.observers.push([element, name, observer, useCapture]);\r
1968       element.addEventListener(name, observer, useCapture);\r
1969     } else if (element.attachEvent) {\r
1970       this.observers.push([element, name, observer, useCapture]);\r
1971       element.attachEvent('on' + name, observer);\r
1972     }\r
1973   },\r
1974 \r
1975   unloadCache: function() {\r
1976     if (!Event.observers) return;\r
1977     for (var i = 0; i < Event.observers.length; i++) {\r
1978       Event.stopObserving.apply(this, Event.observers[i]);\r
1979       Event.observers[i][0] = null;\r
1980     }\r
1981     Event.observers = false;\r
1982   },\r
1983 \r
1984   observe: function(element, name, observer, useCapture) {\r
1985     element = $(element);\r
1986     useCapture = useCapture || false;\r
1987 \r
1988     if (name == 'keypress' &&\r
1989         (navigator.appVersion.match(/Konqueror|Safari|KHTML/)\r
1990         || element.attachEvent))\r
1991       name = 'keydown';\r
1992 \r
1993     Event._observeAndCache(element, name, observer, useCapture);\r
1994   },\r
1995 \r
1996   stopObserving: function(element, name, observer, useCapture) {\r
1997     element = $(element);\r
1998     useCapture = useCapture || false;\r
1999 \r
2000     if (name == 'keypress' &&\r
2001         (navigator.appVersion.match(/Konqueror|Safari|KHTML/)\r
2002         || element.detachEvent))\r
2003       name = 'keydown';\r
2004 \r
2005     if (element.removeEventListener) {\r
2006       element.removeEventListener(name, observer, useCapture);\r
2007     } else if (element.detachEvent) {\r
2008       try {\r
2009         element.detachEvent('on' + name, observer);\r
2010       } catch (e) {}\r
2011     }\r
2012   }\r
2013 });\r
2014 \r
2015 /* prevent memory leaks in IE */\r
2016 if (navigator.appVersion.match(/\bMSIE\b/))\r
2017   Event.observe(window, 'unload', Event.unloadCache, false);\r
2018 var Position = {\r
2019   // set to true if needed, warning: firefox performance problems\r
2020   // NOT neeeded for page scrolling, only if draggable contained in\r
2021   // scrollable elements\r
2022   includeScrollOffsets: false,\r
2023 \r
2024   // must be called before calling withinIncludingScrolloffset, every time the\r
2025   // page is scrolled\r
2026   prepare: function() {\r
2027     this.deltaX =  window.pageXOffset\r
2028                 || document.documentElement.scrollLeft\r
2029                 || document.body.scrollLeft\r
2030                 || 0;\r
2031     this.deltaY =  window.pageYOffset\r
2032                 || document.documentElement.scrollTop\r
2033                 || document.body.scrollTop\r
2034                 || 0;\r
2035   },\r
2036 \r
2037   realOffset: function(element) {\r
2038     var valueT = 0, valueL = 0;\r
2039     do {\r
2040       valueT += element.scrollTop  || 0;\r
2041       valueL += element.scrollLeft || 0;\r
2042       element = element.parentNode;\r
2043     } while (element);\r
2044     return [valueL, valueT];\r
2045   },\r
2046 \r
2047   cumulativeOffset: function(element) {\r
2048     var valueT = 0, valueL = 0;\r
2049     do {\r
2050       valueT += element.offsetTop  || 0;\r
2051       valueL += element.offsetLeft || 0;\r
2052       element = element.offsetParent;\r
2053     } while (element);\r
2054     return [valueL, valueT];\r
2055   },\r
2056 \r
2057   positionedOffset: function(element) {\r
2058     var valueT = 0, valueL = 0;\r
2059     do {\r
2060       valueT += element.offsetTop  || 0;\r
2061       valueL += element.offsetLeft || 0;\r
2062       element = element.offsetParent;\r
2063       if (element) {\r
2064         p = Element.getStyle(element, 'position');\r
2065         if (p == 'relative' || p == 'absolute') break;\r
2066       }\r
2067     } while (element);\r
2068     return [valueL, valueT];\r
2069   },\r
2070 \r
2071   offsetParent: function(element) {\r
2072     if (element.offsetParent) return element.offsetParent;\r
2073     if (element == document.body) return element;\r
2074 \r
2075     while ((element = element.parentNode) && element != document.body)\r
2076       if (Element.getStyle(element, 'position') != 'static')\r
2077         return element;\r
2078 \r
2079     return document.body;\r
2080   },\r
2081 \r
2082   // caches x/y coordinate pair to use with overlap\r
2083   within: function(element, x, y) {\r
2084     if (this.includeScrollOffsets)\r
2085       return this.withinIncludingScrolloffsets(element, x, y);\r
2086     this.xcomp = x;\r
2087     this.ycomp = y;\r
2088     this.offset = this.cumulativeOffset(element);\r
2089 \r
2090     return (y >= this.offset[1] &&\r
2091             y <  this.offset[1] + element.offsetHeight &&\r
2092             x >= this.offset[0] &&\r
2093             x <  this.offset[0] + element.offsetWidth);\r
2094   },\r
2095 \r
2096   withinIncludingScrolloffsets: function(element, x, y) {\r
2097     var offsetcache = this.realOffset(element);\r
2098 \r
2099     this.xcomp = x + offsetcache[0] - this.deltaX;\r
2100     this.ycomp = y + offsetcache[1] - this.deltaY;\r
2101     this.offset = this.cumulativeOffset(element);\r
2102 \r
2103     return (this.ycomp >= this.offset[1] &&\r
2104             this.ycomp <  this.offset[1] + element.offsetHeight &&\r
2105             this.xcomp >= this.offset[0] &&\r
2106             this.xcomp <  this.offset[0] + element.offsetWidth);\r
2107   },\r
2108 \r
2109   // within must be called directly before\r
2110   overlap: function(mode, element) {\r
2111     if (!mode) return 0;\r
2112     if (mode == 'vertical')\r
2113       return ((this.offset[1] + element.offsetHeight) - this.ycomp) /\r
2114         element.offsetHeight;\r
2115     if (mode == 'horizontal')\r
2116       return ((this.offset[0] + element.offsetWidth) - this.xcomp) /\r
2117         element.offsetWidth;\r
2118   },\r
2119 \r
2120   page: function(forElement) {\r
2121     var valueT = 0, valueL = 0;\r
2122 \r
2123     var element = forElement;\r
2124     do {\r
2125       valueT += element.offsetTop  || 0;\r
2126       valueL += element.offsetLeft || 0;\r
2127 \r
2128       // Safari fix\r
2129       if (element.offsetParent==document.body)\r
2130         if (Element.getStyle(element,'position')=='absolute') break;\r
2131 \r
2132     } while (element = element.offsetParent);\r
2133 \r
2134     element = forElement;\r
2135     do {\r
2136       if (!window.opera || element.tagName=='BODY') {\r
2137         valueT -= element.scrollTop  || 0;\r
2138         valueL -= element.scrollLeft || 0;\r
2139       }\r
2140     } while (element = element.parentNode);\r
2141 \r
2142     return [valueL, valueT];\r
2143   },\r
2144 \r
2145   clone: function(source, target) {\r
2146     var options = Object.extend({\r
2147       setLeft:    true,\r
2148       setTop:     true,\r
2149       setWidth:   true,\r
2150       setHeight:  true,\r
2151       offsetTop:  0,\r
2152       offsetLeft: 0\r
2153     }, arguments[2] || {})\r
2154 \r
2155     // find page position of source\r
2156     source = $(source);\r
2157     var p = Position.page(source);\r
2158 \r
2159     // find coordinate system to use\r
2160     target = $(target);\r
2161     var delta = [0, 0];\r
2162     var parent = null;\r
2163     // delta [0,0] will do fine with position: fixed elements,\r
2164     // position:absolute needs offsetParent deltas\r
2165     if (Element.getStyle(target,'position') == 'absolute') {\r
2166       parent = Position.offsetParent(target);\r
2167       delta = Position.page(parent);\r
2168     }\r
2169 \r
2170     // correct by body offsets (fixes Safari)\r
2171     if (parent == document.body) {\r
2172       delta[0] -= document.body.offsetLeft;\r
2173       delta[1] -= document.body.offsetTop;\r
2174     }\r
2175 \r
2176     // set position\r
2177     if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';\r
2178     if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';\r
2179     if(options.setWidth)  target.style.width = source.offsetWidth + 'px';\r
2180     if(options.setHeight) target.style.height = source.offsetHeight + 'px';\r
2181   },\r
2182 \r
2183   absolutize: function(element) {\r
2184     element = $(element);\r
2185     if (element.style.position == 'absolute') return;\r
2186     Position.prepare();\r
2187 \r
2188     var offsets = Position.positionedOffset(element);\r
2189     var top     = offsets[1];\r
2190     var left    = offsets[0];\r
2191     var width   = element.clientWidth;\r
2192     var height  = element.clientHeight;\r
2193 \r
2194     element._originalLeft   = left - parseFloat(element.style.left  || 0);\r
2195     element._originalTop    = top  - parseFloat(element.style.top || 0);\r
2196     element._originalWidth  = element.style.width;\r
2197     element._originalHeight = element.style.height;\r
2198 \r
2199     element.style.position = 'absolute';\r
2200     element.style.top    = top + 'px';;\r
2201     element.style.left   = left + 'px';;\r
2202     element.style.width  = width + 'px';;\r
2203     element.style.height = height + 'px';;\r
2204   },\r
2205 \r
2206   relativize: function(element) {\r
2207     element = $(element);\r
2208     if (element.style.position == 'relative') return;\r
2209     Position.prepare();\r
2210 \r
2211     element.style.position = 'relative';\r
2212     var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);\r
2213     var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);\r
2214 \r
2215     element.style.top    = top + 'px';\r
2216     element.style.left   = left + 'px';\r
2217     element.style.height = element._originalHeight;\r
2218     element.style.width  = element._originalWidth;\r
2219   }\r
2220 }\r
2221 \r
2222 // Safari returns margins on body which is incorrect if the child is absolutely\r
2223 // positioned.  For performance reasons, redefine Position.cumulativeOffset for\r
2224 // KHTML/WebKit only.\r
2225 if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {\r
2226   Position.cumulativeOffset = function(element) {\r
2227     var valueT = 0, valueL = 0;\r
2228     do {\r
2229       valueT += element.offsetTop  || 0;\r
2230       valueL += element.offsetLeft || 0;\r
2231       if (element.offsetParent == document.body)\r
2232         if (Element.getStyle(element, 'position') == 'absolute') break;\r
2233 \r
2234       element = element.offsetParent;\r
2235     } while (element);\r
2236 \r
2237     return [valueL, valueT];\r
2238   }\r
2239 }\r
2240 \r
2241 Element.addMethods();