aws-auth 0.9.0

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