bivouac 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/README +6 -7
  2. data/bin/bivouac +1 -1
  3. data/doc/rdoc/classes/BivouacHelpers/BaseView.html +178 -0
  4. data/doc/rdoc/classes/BivouacHelpers/FormView.html +398 -0
  5. data/doc/rdoc/classes/BivouacHelpers/HtmlView.html +305 -0
  6. data/doc/rdoc/classes/BivouacHelpers/JavaScriptView.html +573 -0
  7. data/doc/rdoc/classes/BivouacHelpers/ScriptAculoUsView.html +258 -0
  8. data/doc/rdoc/classes/BivouacHelpers/TooltipView.html +158 -0
  9. data/doc/rdoc/classes/BivouacHelpers.html +117 -0
  10. data/doc/rdoc/classes/JavaScriptGenerator.html +564 -0
  11. data/doc/rdoc/created.rid +1 -0
  12. data/doc/rdoc/files/AUTHORS.html +109 -0
  13. data/doc/rdoc/files/COPYING.html +533 -0
  14. data/doc/rdoc/files/README.html +427 -0
  15. data/doc/rdoc/files/lib/bivouac/helpers/view/goh/base_rb.html +109 -0
  16. data/doc/rdoc/files/lib/bivouac/helpers/view/goh/form_rb.html +109 -0
  17. data/doc/rdoc/files/lib/bivouac/helpers/view/goh/html_rb.html +109 -0
  18. data/doc/rdoc/files/lib/bivouac/helpers/view/goh/javascript_rb.html +113 -0
  19. data/doc/rdoc/files/lib/bivouac/helpers/view/goh/scriptaculous_rb.html +113 -0
  20. data/doc/rdoc/files/lib/bivouac/helpers/view/goh/tooltip_rb.html +109 -0
  21. data/doc/rdoc/index.html +10 -0
  22. data/doc/rdoc/permalink.gif +0 -0
  23. data/doc/rdoc/rdoc-style.css +106 -0
  24. data/doc/rdoc/rubyfr.png +0 -0
  25. data/examples/bivouac_sample/Rakefile +48 -0
  26. data/examples/bivouac_sample/app/bivouac_sample.rb +15 -7
  27. data/examples/bivouac_sample/app/controllers/index.rb +2 -2
  28. data/examples/bivouac_sample/app/controllers/sound.rb +10 -0
  29. data/examples/bivouac_sample/app/helpers/_helpers.rb +6 -3
  30. data/examples/bivouac_sample/app/views/sound.rb +16 -0
  31. data/examples/bivouac_sample/config/environment.rb +5 -2
  32. data/examples/bivouac_sample/config/postamble.rb +89 -18
  33. data/examples/bivouac_sample/public/javascripts/builder.js +12 -7
  34. data/examples/bivouac_sample/public/javascripts/controls.js +485 -355
  35. data/examples/bivouac_sample/public/javascripts/dragdrop.js +82 -52
  36. data/examples/bivouac_sample/public/javascripts/effects.js +361 -329
  37. data/examples/bivouac_sample/public/javascripts/prototype.js +2826 -1120
  38. data/examples/bivouac_sample/public/javascripts/scriptaculous.js +15 -8
  39. data/examples/bivouac_sample/public/javascripts/slider.js +40 -43
  40. data/examples/bivouac_sample/public/javascripts/sound.js +55 -0
  41. data/examples/bivouac_sample/public/javascripts/unittest.js +16 -12
  42. data/examples/bivouac_sample/public/sound/sword.mp3 +0 -0
  43. data/examples/bivouac_sample/script/console +6 -0
  44. data/examples/bivouac_sample/script/plugin +3 -0
  45. data/examples/bivouac_sample/script/server +2 -1
  46. data/examples/bivouac_sample/test/test_sound.rb +15 -0
  47. data/lib/bivouac/helpers/view/goh/sound.rb +38 -0
  48. data/lib/bivouac/template/application/helpers_goh.rb +2 -0
  49. data/lib/bivouac/template/static/builder.js +12 -7
  50. data/lib/bivouac/template/static/controls.js +485 -355
  51. data/lib/bivouac/template/static/dragdrop.js +82 -52
  52. data/lib/bivouac/template/static/effects.js +361 -329
  53. data/lib/bivouac/template/static/prototype.js +2826 -1120
  54. data/lib/bivouac/template/static/scriptaculous.js +15 -8
  55. data/lib/bivouac/template/static/slider.js +40 -43
  56. data/lib/bivouac/template/static/sound.js +55 -0
  57. data/lib/bivouac/template/static/unittest.js +16 -12
  58. metadata +45 -2
@@ -1,51 +1,153 @@
1
- /* Prototype JavaScript framework, version 1.5.0
2
- * (c) 2005-2007 Sam Stephenson
1
+ /* Prototype JavaScript framework, version 1.6.0.2
2
+ * (c) 2005-2008 Sam Stephenson
3
3
  *
4
4
  * Prototype is freely distributable under the terms of an MIT-style license.
5
- * For details, see the Prototype web site: http://prototype.conio.net/
5
+ * For details, see the Prototype web site: http://www.prototypejs.org/
6
6
  *
7
- /*--------------------------------------------------------------------------*/
7
+ *--------------------------------------------------------------------------*/
8
8
 
9
9
  var Prototype = {
10
- Version: '1.5.0',
10
+ Version: '1.6.0.2',
11
+
12
+ Browser: {
13
+ IE: !!(window.attachEvent && !window.opera),
14
+ Opera: !!window.opera,
15
+ WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
16
+ Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
17
+ MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
18
+ },
19
+
11
20
  BrowserFeatures: {
12
- XPath: !!document.evaluate
21
+ XPath: !!document.evaluate,
22
+ ElementExtensions: !!window.HTMLElement,
23
+ SpecificElementExtensions:
24
+ document.createElement('div').__proto__ &&
25
+ document.createElement('div').__proto__ !==
26
+ document.createElement('form').__proto__
13
27
  },
14
28
 
15
- ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
16
- emptyFunction: function() {},
29
+ ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
30
+ JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
31
+
32
+ emptyFunction: function() { },
17
33
  K: function(x) { return x }
18
- }
34
+ };
35
+
36
+ if (Prototype.Browser.MobileSafari)
37
+ Prototype.BrowserFeatures.SpecificElementExtensions = false;
19
38
 
39
+
40
+ /* Based on Alex Arnell's inheritance implementation. */
20
41
  var Class = {
21
42
  create: function() {
22
- return function() {
43
+ var parent = null, properties = $A(arguments);
44
+ if (Object.isFunction(properties[0]))
45
+ parent = properties.shift();
46
+
47
+ function klass() {
23
48
  this.initialize.apply(this, arguments);
24
49
  }
50
+
51
+ Object.extend(klass, Class.Methods);
52
+ klass.superclass = parent;
53
+ klass.subclasses = [];
54
+
55
+ if (parent) {
56
+ var subclass = function() { };
57
+ subclass.prototype = parent.prototype;
58
+ klass.prototype = new subclass;
59
+ parent.subclasses.push(klass);
60
+ }
61
+
62
+ for (var i = 0; i < properties.length; i++)
63
+ klass.addMethods(properties[i]);
64
+
65
+ if (!klass.prototype.initialize)
66
+ klass.prototype.initialize = Prototype.emptyFunction;
67
+
68
+ klass.prototype.constructor = klass;
69
+
70
+ return klass;
71
+ }
72
+ };
73
+
74
+ Class.Methods = {
75
+ addMethods: function(source) {
76
+ var ancestor = this.superclass && this.superclass.prototype;
77
+ var properties = Object.keys(source);
78
+
79
+ if (!Object.keys({ toString: true }).length)
80
+ properties.push("toString", "valueOf");
81
+
82
+ for (var i = 0, length = properties.length; i < length; i++) {
83
+ var property = properties[i], value = source[property];
84
+ if (ancestor && Object.isFunction(value) &&
85
+ value.argumentNames().first() == "$super") {
86
+ var method = value, value = Object.extend((function(m) {
87
+ return function() { return ancestor[m].apply(this, arguments) };
88
+ })(property).wrap(method), {
89
+ valueOf: function() { return method },
90
+ toString: function() { return method.toString() }
91
+ });
92
+ }
93
+ this.prototype[property] = value;
94
+ }
95
+
96
+ return this;
25
97
  }
26
- }
98
+ };
27
99
 
28
- var Abstract = new Object();
100
+ var Abstract = { };
29
101
 
30
102
  Object.extend = function(destination, source) {
31
- for (var property in source) {
103
+ for (var property in source)
32
104
  destination[property] = source[property];
33
- }
34
105
  return destination;
35
- }
106
+ };
36
107
 
37
108
  Object.extend(Object, {
38
109
  inspect: function(object) {
39
110
  try {
40
- if (object === undefined) return 'undefined';
111
+ if (Object.isUndefined(object)) return 'undefined';
41
112
  if (object === null) return 'null';
42
- return object.inspect ? object.inspect() : object.toString();
113
+ return object.inspect ? object.inspect() : String(object);
43
114
  } catch (e) {
44
115
  if (e instanceof RangeError) return '...';
45
116
  throw e;
46
117
  }
47
118
  },
48
119
 
120
+ toJSON: function(object) {
121
+ var type = typeof object;
122
+ switch (type) {
123
+ case 'undefined':
124
+ case 'function':
125
+ case 'unknown': return;
126
+ case 'boolean': return object.toString();
127
+ }
128
+
129
+ if (object === null) return 'null';
130
+ if (object.toJSON) return object.toJSON();
131
+ if (Object.isElement(object)) return;
132
+
133
+ var results = [];
134
+ for (var property in object) {
135
+ var value = Object.toJSON(object[property]);
136
+ if (!Object.isUndefined(value))
137
+ results.push(property.toJSON() + ': ' + value);
138
+ }
139
+
140
+ return '{' + results.join(', ') + '}';
141
+ },
142
+
143
+ toQueryString: function(object) {
144
+ return $H(object).toQueryString();
145
+ },
146
+
147
+ toHTML: function(object) {
148
+ return object && object.toHTML ? object.toHTML() : String.interpret(object);
149
+ },
150
+
49
151
  keys: function(object) {
50
152
  var keys = [];
51
153
  for (var property in object)
@@ -61,41 +163,102 @@ Object.extend(Object, {
61
163
  },
62
164
 
63
165
  clone: function(object) {
64
- return Object.extend({}, object);
166
+ return Object.extend({ }, object);
167
+ },
168
+
169
+ isElement: function(object) {
170
+ return object && object.nodeType == 1;
171
+ },
172
+
173
+ isArray: function(object) {
174
+ return object != null && typeof object == "object" &&
175
+ 'splice' in object && 'join' in object;
176
+ },
177
+
178
+ isHash: function(object) {
179
+ return object instanceof Hash;
180
+ },
181
+
182
+ isFunction: function(object) {
183
+ return typeof object == "function";
184
+ },
185
+
186
+ isString: function(object) {
187
+ return typeof object == "string";
188
+ },
189
+
190
+ isNumber: function(object) {
191
+ return typeof object == "number";
192
+ },
193
+
194
+ isUndefined: function(object) {
195
+ return typeof object == "undefined";
65
196
  }
66
197
  });
67
198
 
68
- Function.prototype.bind = function() {
69
- var __method = this, args = $A(arguments), object = args.shift();
70
- return function() {
71
- return __method.apply(object, args.concat($A(arguments)));
72
- }
73
- }
199
+ Object.extend(Function.prototype, {
200
+ argumentNames: function() {
201
+ var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
202
+ return names.length == 1 && !names[0] ? [] : names;
203
+ },
74
204
 
75
- Function.prototype.bindAsEventListener = function(object) {
76
- var __method = this, args = $A(arguments), object = args.shift();
77
- return function(event) {
78
- return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
79
- }
80
- }
205
+ bind: function() {
206
+ if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
207
+ var __method = this, args = $A(arguments), object = args.shift();
208
+ return function() {
209
+ return __method.apply(object, args.concat($A(arguments)));
210
+ }
211
+ },
81
212
 
82
- Object.extend(Number.prototype, {
83
- toColorPart: function() {
84
- var digits = this.toString(16);
85
- if (this < 16) return '0' + digits;
86
- return digits;
213
+ bindAsEventListener: function() {
214
+ var __method = this, args = $A(arguments), object = args.shift();
215
+ return function(event) {
216
+ return __method.apply(object, [event || window.event].concat(args));
217
+ }
87
218
  },
88
219
 
89
- succ: function() {
90
- return this + 1;
220
+ curry: function() {
221
+ if (!arguments.length) return this;
222
+ var __method = this, args = $A(arguments);
223
+ return function() {
224
+ return __method.apply(this, args.concat($A(arguments)));
225
+ }
91
226
  },
92
227
 
93
- times: function(iterator) {
94
- $R(0, this, true).each(iterator);
95
- return this;
228
+ delay: function() {
229
+ var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
230
+ return window.setTimeout(function() {
231
+ return __method.apply(__method, args);
232
+ }, timeout);
233
+ },
234
+
235
+ wrap: function(wrapper) {
236
+ var __method = this;
237
+ return function() {
238
+ return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
239
+ }
240
+ },
241
+
242
+ methodize: function() {
243
+ if (this._methodized) return this._methodized;
244
+ var __method = this;
245
+ return this._methodized = function() {
246
+ return __method.apply(null, [this].concat($A(arguments)));
247
+ };
96
248
  }
97
249
  });
98
250
 
251
+ Function.prototype.defer = Function.prototype.delay.curry(0.01);
252
+
253
+ Date.prototype.toJSON = function() {
254
+ return '"' + this.getUTCFullYear() + '-' +
255
+ (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
256
+ this.getUTCDate().toPaddedString(2) + 'T' +
257
+ this.getUTCHours().toPaddedString(2) + ':' +
258
+ this.getUTCMinutes().toPaddedString(2) + ':' +
259
+ this.getUTCSeconds().toPaddedString(2) + 'Z"';
260
+ };
261
+
99
262
  var Try = {
100
263
  these: function() {
101
264
  var returnValue;
@@ -105,17 +268,22 @@ var Try = {
105
268
  try {
106
269
  returnValue = lambda();
107
270
  break;
108
- } catch (e) {}
271
+ } catch (e) { }
109
272
  }
110
273
 
111
274
  return returnValue;
112
275
  }
113
- }
276
+ };
277
+
278
+ RegExp.prototype.match = RegExp.prototype.test;
279
+
280
+ RegExp.escape = function(str) {
281
+ return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
282
+ };
114
283
 
115
284
  /*--------------------------------------------------------------------------*/
116
285
 
117
- var PeriodicalExecuter = Class.create();
118
- PeriodicalExecuter.prototype = {
286
+ var PeriodicalExecuter = Class.create({
119
287
  initialize: function(callback, frequency) {
120
288
  this.callback = callback;
121
289
  this.frequency = frequency;
@@ -128,6 +296,10 @@ PeriodicalExecuter.prototype = {
128
296
  this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
129
297
  },
130
298
 
299
+ execute: function() {
300
+ this.callback(this);
301
+ },
302
+
131
303
  stop: function() {
132
304
  if (!this.timer) return;
133
305
  clearInterval(this.timer);
@@ -138,16 +310,26 @@ PeriodicalExecuter.prototype = {
138
310
  if (!this.currentlyExecuting) {
139
311
  try {
140
312
  this.currentlyExecuting = true;
141
- this.callback(this);
313
+ this.execute();
142
314
  } finally {
143
315
  this.currentlyExecuting = false;
144
316
  }
145
317
  }
146
318
  }
147
- }
148
- String.interpret = function(value){
149
- return value == null ? '' : String(value);
150
- }
319
+ });
320
+ Object.extend(String, {
321
+ interpret: function(value) {
322
+ return value == null ? '' : String(value);
323
+ },
324
+ specialChar: {
325
+ '\b': '\\b',
326
+ '\t': '\\t',
327
+ '\n': '\\n',
328
+ '\f': '\\f',
329
+ '\r': '\\r',
330
+ '\\': '\\\\'
331
+ }
332
+ });
151
333
 
152
334
  Object.extend(String.prototype, {
153
335
  gsub: function(pattern, replacement) {
@@ -168,7 +350,7 @@ Object.extend(String.prototype, {
168
350
 
169
351
  sub: function(pattern, replacement, count) {
170
352
  replacement = this.gsub.prepareReplacement(replacement);
171
- count = count === undefined ? 1 : count;
353
+ count = Object.isUndefined(count) ? 1 : count;
172
354
 
173
355
  return this.gsub(pattern, function(match) {
174
356
  if (--count < 0) return match[0];
@@ -178,14 +360,14 @@ Object.extend(String.prototype, {
178
360
 
179
361
  scan: function(pattern, iterator) {
180
362
  this.gsub(pattern, iterator);
181
- return this;
363
+ return String(this);
182
364
  },
183
365
 
184
366
  truncate: function(length, truncation) {
185
367
  length = length || 30;
186
- truncation = truncation === undefined ? '...' : truncation;
368
+ truncation = Object.isUndefined(truncation) ? '...' : truncation;
187
369
  return this.length > length ?
188
- this.slice(0, length - truncation.length) + truncation : this;
370
+ this.slice(0, length - truncation.length) + truncation : String(this);
189
371
  },
190
372
 
191
373
  strip: function() {
@@ -213,35 +395,34 @@ Object.extend(String.prototype, {
213
395
  },
214
396
 
215
397
  escapeHTML: function() {
216
- var div = document.createElement('div');
217
- var text = document.createTextNode(this);
218
- div.appendChild(text);
219
- return div.innerHTML;
398
+ var self = arguments.callee;
399
+ self.text.data = this;
400
+ return self.div.innerHTML;
220
401
  },
221
402
 
222
403
  unescapeHTML: function() {
223
- var div = document.createElement('div');
404
+ var div = new Element('div');
224
405
  div.innerHTML = this.stripTags();
225
406
  return div.childNodes[0] ? (div.childNodes.length > 1 ?
226
- $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) :
407
+ $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
227
408
  div.childNodes[0].nodeValue) : '';
228
409
  },
229
410
 
230
411
  toQueryParams: function(separator) {
231
412
  var match = this.strip().match(/([^?#]*)(#.*)?$/);
232
- if (!match) return {};
413
+ if (!match) return { };
233
414
 
234
- return match[1].split(separator || '&').inject({}, function(hash, pair) {
415
+ return match[1].split(separator || '&').inject({ }, function(hash, pair) {
235
416
  if ((pair = pair.split('='))[0]) {
236
- var name = decodeURIComponent(pair[0]);
237
- var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
417
+ var key = decodeURIComponent(pair.shift());
418
+ var value = pair.length > 1 ? pair.join('=') : pair[0];
419
+ if (value != undefined) value = decodeURIComponent(value);
238
420
 
239
- if (hash[name] !== undefined) {
240
- if (hash[name].constructor != Array)
241
- hash[name] = [hash[name]];
242
- if (value) hash[name].push(value);
421
+ if (key in hash) {
422
+ if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
423
+ hash[key].push(value);
243
424
  }
244
- else hash[name] = value;
425
+ else hash[key] = value;
245
426
  }
246
427
  return hash;
247
428
  });
@@ -256,6 +437,10 @@ Object.extend(String.prototype, {
256
437
  String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
257
438
  },
258
439
 
440
+ times: function(count) {
441
+ return count < 1 ? '' : new Array(count + 1).join(this);
442
+ },
443
+
259
444
  camelize: function() {
260
445
  var parts = this.split('-'), len = parts.length;
261
446
  if (len == 1) return parts[0];
@@ -270,7 +455,7 @@ Object.extend(String.prototype, {
270
455
  return camelized;
271
456
  },
272
457
 
273
- capitalize: function(){
458
+ capitalize: function() {
274
459
  return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
275
460
  },
276
461
 
@@ -283,52 +468,131 @@ Object.extend(String.prototype, {
283
468
  },
284
469
 
285
470
  inspect: function(useDoubleQuotes) {
286
- var escapedString = this.replace(/\\/g, '\\\\');
287
- if (useDoubleQuotes)
288
- return '"' + escapedString.replace(/"/g, '\\"') + '"';
289
- else
290
- return "'" + escapedString.replace(/'/g, '\\\'') + "'";
471
+ var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
472
+ var character = String.specialChar[match[0]];
473
+ return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
474
+ });
475
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
476
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
477
+ },
478
+
479
+ toJSON: function() {
480
+ return this.inspect(true);
481
+ },
482
+
483
+ unfilterJSON: function(filter) {
484
+ return this.sub(filter || Prototype.JSONFilter, '#{1}');
485
+ },
486
+
487
+ isJSON: function() {
488
+ var str = this;
489
+ if (str.blank()) return false;
490
+ str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
491
+ return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
492
+ },
493
+
494
+ evalJSON: function(sanitize) {
495
+ var json = this.unfilterJSON();
496
+ try {
497
+ if (!sanitize || json.isJSON()) return eval('(' + json + ')');
498
+ } catch (e) { }
499
+ throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
500
+ },
501
+
502
+ include: function(pattern) {
503
+ return this.indexOf(pattern) > -1;
504
+ },
505
+
506
+ startsWith: function(pattern) {
507
+ return this.indexOf(pattern) === 0;
508
+ },
509
+
510
+ endsWith: function(pattern) {
511
+ var d = this.length - pattern.length;
512
+ return d >= 0 && this.lastIndexOf(pattern) === d;
513
+ },
514
+
515
+ empty: function() {
516
+ return this == '';
517
+ },
518
+
519
+ blank: function() {
520
+ return /^\s*$/.test(this);
521
+ },
522
+
523
+ interpolate: function(object, pattern) {
524
+ return new Template(this, pattern).evaluate(object);
525
+ }
526
+ });
527
+
528
+ if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
529
+ escapeHTML: function() {
530
+ return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
531
+ },
532
+ unescapeHTML: function() {
533
+ return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
291
534
  }
292
535
  });
293
536
 
294
537
  String.prototype.gsub.prepareReplacement = function(replacement) {
295
- if (typeof replacement == 'function') return replacement;
538
+ if (Object.isFunction(replacement)) return replacement;
296
539
  var template = new Template(replacement);
297
540
  return function(match) { return template.evaluate(match) };
298
- }
541
+ };
299
542
 
300
543
  String.prototype.parseQuery = String.prototype.toQueryParams;
301
544
 
302
- var Template = Class.create();
303
- Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
304
- Template.prototype = {
545
+ Object.extend(String.prototype.escapeHTML, {
546
+ div: document.createElement('div'),
547
+ text: document.createTextNode('')
548
+ });
549
+
550
+ with (String.prototype.escapeHTML) div.appendChild(text);
551
+
552
+ var Template = Class.create({
305
553
  initialize: function(template, pattern) {
306
554
  this.template = template.toString();
307
- this.pattern = pattern || Template.Pattern;
555
+ this.pattern = pattern || Template.Pattern;
308
556
  },
309
557
 
310
558
  evaluate: function(object) {
559
+ if (Object.isFunction(object.toTemplateReplacements))
560
+ object = object.toTemplateReplacements();
561
+
311
562
  return this.template.gsub(this.pattern, function(match) {
312
- var before = match[1];
563
+ if (object == null) return '';
564
+
565
+ var before = match[1] || '';
313
566
  if (before == '\\') return match[2];
314
- return before + String.interpret(object[match[3]]);
567
+
568
+ var ctx = object, expr = match[3];
569
+ var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
570
+ match = pattern.exec(expr);
571
+ if (match == null) return before;
572
+
573
+ while (match != null) {
574
+ var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
575
+ ctx = ctx[comp];
576
+ if (null == ctx || '' == match[3]) break;
577
+ expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
578
+ match = pattern.exec(expr);
579
+ }
580
+
581
+ return before + String.interpret(ctx);
315
582
  });
316
583
  }
317
- }
584
+ });
585
+ Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
318
586
 
319
- var $break = new Object();
320
- var $continue = new Object();
587
+ var $break = { };
321
588
 
322
589
  var Enumerable = {
323
- each: function(iterator) {
590
+ each: function(iterator, context) {
324
591
  var index = 0;
592
+ iterator = iterator.bind(context);
325
593
  try {
326
594
  this._each(function(value) {
327
- try {
328
- iterator(value, index++);
329
- } catch (e) {
330
- if (e != $continue) throw e;
331
- }
595
+ iterator(value, index++);
332
596
  });
333
597
  } catch (e) {
334
598
  if (e != $break) throw e;
@@ -336,40 +600,45 @@ var Enumerable = {
336
600
  return this;
337
601
  },
338
602
 
339
- eachSlice: function(number, iterator) {
603
+ eachSlice: function(number, iterator, context) {
604
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
340
605
  var index = -number, slices = [], array = this.toArray();
341
606
  while ((index += number) < array.length)
342
607
  slices.push(array.slice(index, index+number));
343
- return slices.map(iterator);
608
+ return slices.collect(iterator, context);
344
609
  },
345
610
 
346
- all: function(iterator) {
611
+ all: function(iterator, context) {
612
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
347
613
  var result = true;
348
614
  this.each(function(value, index) {
349
- result = result && !!(iterator || Prototype.K)(value, index);
615
+ result = result && !!iterator(value, index);
350
616
  if (!result) throw $break;
351
617
  });
352
618
  return result;
353
619
  },
354
620
 
355
- any: function(iterator) {
621
+ any: function(iterator, context) {
622
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
356
623
  var result = false;
357
624
  this.each(function(value, index) {
358
- if (result = !!(iterator || Prototype.K)(value, index))
625
+ if (result = !!iterator(value, index))
359
626
  throw $break;
360
627
  });
361
628
  return result;
362
629
  },
363
630
 
364
- collect: function(iterator) {
631
+ collect: function(iterator, context) {
632
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
365
633
  var results = [];
366
634
  this.each(function(value, index) {
367
- results.push((iterator || Prototype.K)(value, index));
635
+ results.push(iterator(value, index));
368
636
  });
369
637
  return results;
370
638
  },
371
639
 
372
- detect: function(iterator) {
640
+ detect: function(iterator, context) {
641
+ iterator = iterator.bind(context);
373
642
  var result;
374
643
  this.each(function(value, index) {
375
644
  if (iterator(value, index)) {
@@ -380,7 +649,8 @@ var Enumerable = {
380
649
  return result;
381
650
  },
382
651
 
383
- findAll: function(iterator) {
652
+ findAll: function(iterator, context) {
653
+ iterator = iterator.bind(context);
384
654
  var results = [];
385
655
  this.each(function(value, index) {
386
656
  if (iterator(value, index))
@@ -389,17 +659,24 @@ var Enumerable = {
389
659
  return results;
390
660
  },
391
661
 
392
- grep: function(pattern, iterator) {
662
+ grep: function(filter, iterator, context) {
663
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
393
664
  var results = [];
665
+
666
+ if (Object.isString(filter))
667
+ filter = new RegExp(filter);
668
+
394
669
  this.each(function(value, index) {
395
- var stringValue = value.toString();
396
- if (stringValue.match(pattern))
397
- results.push((iterator || Prototype.K)(value, index));
398
- })
670
+ if (filter.match(value))
671
+ results.push(iterator(value, index));
672
+ });
399
673
  return results;
400
674
  },
401
675
 
402
676
  include: function(object) {
677
+ if (Object.isFunction(this.indexOf))
678
+ if (this.indexOf(object) != -1) return true;
679
+
403
680
  var found = false;
404
681
  this.each(function(value) {
405
682
  if (value == object) {
@@ -411,14 +688,15 @@ var Enumerable = {
411
688
  },
412
689
 
413
690
  inGroupsOf: function(number, fillWith) {
414
- fillWith = fillWith === undefined ? null : fillWith;
691
+ fillWith = Object.isUndefined(fillWith) ? null : fillWith;
415
692
  return this.eachSlice(number, function(slice) {
416
693
  while(slice.length < number) slice.push(fillWith);
417
694
  return slice;
418
695
  });
419
696
  },
420
697
 
421
- inject: function(memo, iterator) {
698
+ inject: function(memo, iterator, context) {
699
+ iterator = iterator.bind(context);
422
700
  this.each(function(value, index) {
423
701
  memo = iterator(memo, value, index);
424
702
  });
@@ -432,30 +710,33 @@ var Enumerable = {
432
710
  });
433
711
  },
434
712
 
435
- max: function(iterator) {
713
+ max: function(iterator, context) {
714
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
436
715
  var result;
437
716
  this.each(function(value, index) {
438
- value = (iterator || Prototype.K)(value, index);
439
- if (result == undefined || value >= result)
717
+ value = iterator(value, index);
718
+ if (result == null || value >= result)
440
719
  result = value;
441
720
  });
442
721
  return result;
443
722
  },
444
723
 
445
- min: function(iterator) {
724
+ min: function(iterator, context) {
725
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
446
726
  var result;
447
727
  this.each(function(value, index) {
448
- value = (iterator || Prototype.K)(value, index);
449
- if (result == undefined || value < result)
728
+ value = iterator(value, index);
729
+ if (result == null || value < result)
450
730
  result = value;
451
731
  });
452
732
  return result;
453
733
  },
454
734
 
455
- partition: function(iterator) {
735
+ partition: function(iterator, context) {
736
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
456
737
  var trues = [], falses = [];
457
738
  this.each(function(value, index) {
458
- ((iterator || Prototype.K)(value, index) ?
739
+ (iterator(value, index) ?
459
740
  trues : falses).push(value);
460
741
  });
461
742
  return [trues, falses];
@@ -463,13 +744,14 @@ var Enumerable = {
463
744
 
464
745
  pluck: function(property) {
465
746
  var results = [];
466
- this.each(function(value, index) {
747
+ this.each(function(value) {
467
748
  results.push(value[property]);
468
749
  });
469
750
  return results;
470
751
  },
471
752
 
472
- reject: function(iterator) {
753
+ reject: function(iterator, context) {
754
+ iterator = iterator.bind(context);
473
755
  var results = [];
474
756
  this.each(function(value, index) {
475
757
  if (!iterator(value, index))
@@ -478,7 +760,8 @@ var Enumerable = {
478
760
  return results;
479
761
  },
480
762
 
481
- sortBy: function(iterator) {
763
+ sortBy: function(iterator, context) {
764
+ iterator = iterator.bind(context);
482
765
  return this.map(function(value, index) {
483
766
  return {value: value, criteria: iterator(value, index)};
484
767
  }).sort(function(left, right) {
@@ -493,7 +776,7 @@ var Enumerable = {
493
776
 
494
777
  zip: function() {
495
778
  var iterator = Prototype.K, args = $A(arguments);
496
- if (typeof args.last() == 'function')
779
+ if (Object.isFunction(args.last()))
497
780
  iterator = args.pop();
498
781
 
499
782
  var collections = [this].concat(args).map($A);
@@ -509,31 +792,42 @@ var Enumerable = {
509
792
  inspect: function() {
510
793
  return '#<Enumerable:' + this.toArray().inspect() + '>';
511
794
  }
512
- }
795
+ };
513
796
 
514
797
  Object.extend(Enumerable, {
515
798
  map: Enumerable.collect,
516
799
  find: Enumerable.detect,
517
800
  select: Enumerable.findAll,
801
+ filter: Enumerable.findAll,
518
802
  member: Enumerable.include,
519
- entries: Enumerable.toArray
803
+ entries: Enumerable.toArray,
804
+ every: Enumerable.all,
805
+ some: Enumerable.any
520
806
  });
521
- var $A = Array.from = function(iterable) {
807
+ function $A(iterable) {
522
808
  if (!iterable) return [];
523
- if (iterable.toArray) {
524
- return iterable.toArray();
525
- } else {
526
- var results = [];
527
- for (var i = 0, length = iterable.length; i < length; i++)
528
- results.push(iterable[i]);
809
+ if (iterable.toArray) return iterable.toArray();
810
+ var length = iterable.length || 0, results = new Array(length);
811
+ while (length--) results[length] = iterable[length];
812
+ return results;
813
+ }
814
+
815
+ if (Prototype.Browser.WebKit) {
816
+ $A = function(iterable) {
817
+ if (!iterable) return [];
818
+ if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
819
+ iterable.toArray) return iterable.toArray();
820
+ var length = iterable.length || 0, results = new Array(length);
821
+ while (length--) results[length] = iterable[length];
529
822
  return results;
530
- }
823
+ };
531
824
  }
532
825
 
826
+ Array.from = $A;
827
+
533
828
  Object.extend(Array.prototype, Enumerable);
534
829
 
535
- if (!Array.prototype._reverse)
536
- Array.prototype._reverse = Array.prototype.reverse;
830
+ if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
537
831
 
538
832
  Object.extend(Array.prototype, {
539
833
  _each: function(iterator) {
@@ -562,7 +856,7 @@ Object.extend(Array.prototype, {
562
856
 
563
857
  flatten: function() {
564
858
  return this.inject([], function(array, value) {
565
- return array.concat(value && value.constructor == Array ?
859
+ return array.concat(Object.isArray(value) ?
566
860
  value.flatten() : [value]);
567
861
  });
568
862
  },
@@ -574,12 +868,6 @@ Object.extend(Array.prototype, {
574
868
  });
575
869
  },
576
870
 
577
- indexOf: function(object) {
578
- for (var i = 0, length = this.length; i < length; i++)
579
- if (this[i] == object) return i;
580
- return -1;
581
- },
582
-
583
871
  reverse: function(inline) {
584
872
  return (inline !== false ? this : this.toArray())._reverse();
585
873
  },
@@ -588,9 +876,17 @@ Object.extend(Array.prototype, {
588
876
  return this.length > 1 ? this : this[0];
589
877
  },
590
878
 
591
- uniq: function() {
592
- return this.inject([], function(array, value) {
593
- return array.include(value) ? array : array.concat([value]);
879
+ uniq: function(sorted) {
880
+ return this.inject([], function(array, value, index) {
881
+ if (0 == index || (sorted ? array.last() != value : !array.include(value)))
882
+ array.push(value);
883
+ return array;
884
+ });
885
+ },
886
+
887
+ intersect: function(array) {
888
+ return this.uniq().findAll(function(item) {
889
+ return array.detect(function(value) { return item === value });
594
890
  });
595
891
  },
596
892
 
@@ -604,125 +900,187 @@ Object.extend(Array.prototype, {
604
900
 
605
901
  inspect: function() {
606
902
  return '[' + this.map(Object.inspect).join(', ') + ']';
903
+ },
904
+
905
+ toJSON: function() {
906
+ var results = [];
907
+ this.each(function(object) {
908
+ var value = Object.toJSON(object);
909
+ if (!Object.isUndefined(value)) results.push(value);
910
+ });
911
+ return '[' + results.join(', ') + ']';
607
912
  }
608
913
  });
609
914
 
915
+ // use native browser JS 1.6 implementation if available
916
+ if (Object.isFunction(Array.prototype.forEach))
917
+ Array.prototype._each = Array.prototype.forEach;
918
+
919
+ if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
920
+ i || (i = 0);
921
+ var length = this.length;
922
+ if (i < 0) i = length + i;
923
+ for (; i < length; i++)
924
+ if (this[i] === item) return i;
925
+ return -1;
926
+ };
927
+
928
+ if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
929
+ i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
930
+ var n = this.slice(0, i).reverse().indexOf(item);
931
+ return (n < 0) ? n : i - n - 1;
932
+ };
933
+
610
934
  Array.prototype.toArray = Array.prototype.clone;
611
935
 
612
- function $w(string){
936
+ function $w(string) {
937
+ if (!Object.isString(string)) return [];
613
938
  string = string.strip();
614
939
  return string ? string.split(/\s+/) : [];
615
940
  }
616
941
 
617
- if(window.opera){
618
- Array.prototype.concat = function(){
942
+ if (Prototype.Browser.Opera){
943
+ Array.prototype.concat = function() {
619
944
  var array = [];
620
- for(var i = 0, length = this.length; i < length; i++) array.push(this[i]);
621
- for(var i = 0, length = arguments.length; i < length; i++) {
622
- if(arguments[i].constructor == Array) {
623
- for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
945
+ for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
946
+ for (var i = 0, length = arguments.length; i < length; i++) {
947
+ if (Object.isArray(arguments[i])) {
948
+ for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
624
949
  array.push(arguments[i][j]);
625
950
  } else {
626
951
  array.push(arguments[i]);
627
952
  }
628
953
  }
629
954
  return array;
630
- }
955
+ };
631
956
  }
632
- var Hash = function(obj) {
633
- Object.extend(this, obj || {});
634
- };
635
-
636
- Object.extend(Hash, {
637
- toQueryString: function(obj) {
638
- var parts = [];
639
-
640
- this.prototype._each.call(obj, function(pair) {
641
- if (!pair.key) return;
642
-
643
- if (pair.value && pair.value.constructor == Array) {
644
- var values = pair.value.compact();
645
- if (values.length < 2) pair.value = values.reduce();
646
- else {
647
- key = encodeURIComponent(pair.key);
648
- values.each(function(value) {
649
- value = value != undefined ? encodeURIComponent(value) : '';
650
- parts.push(key + '=' + encodeURIComponent(value));
651
- });
652
- return;
653
- }
654
- }
655
- if (pair.value == undefined) pair[1] = '';
656
- parts.push(pair.map(encodeURIComponent).join('='));
657
- });
658
-
659
- return parts.join('&');
660
- }
661
- });
662
-
663
- Object.extend(Hash.prototype, Enumerable);
664
- Object.extend(Hash.prototype, {
665
- _each: function(iterator) {
666
- for (var key in this) {
667
- var value = this[key];
668
- if (value && value == Hash.prototype[key]) continue;
669
-
670
- var pair = [key, value];
671
- pair.key = key;
672
- pair.value = value;
673
- iterator(pair);
674
- }
675
- },
676
-
677
- keys: function() {
678
- return this.pluck('key');
679
- },
680
-
681
- values: function() {
682
- return this.pluck('value');
957
+ Object.extend(Number.prototype, {
958
+ toColorPart: function() {
959
+ return this.toPaddedString(2, 16);
683
960
  },
684
961
 
685
- merge: function(hash) {
686
- return $H(hash).inject(this, function(mergedHash, pair) {
687
- mergedHash[pair.key] = pair.value;
688
- return mergedHash;
689
- });
962
+ succ: function() {
963
+ return this + 1;
690
964
  },
691
965
 
692
- remove: function() {
693
- var result;
694
- for(var i = 0, length = arguments.length; i < length; i++) {
695
- var value = this[arguments[i]];
696
- if (value !== undefined){
697
- if (result === undefined) result = value;
698
- else {
699
- if (result.constructor != Array) result = [result];
700
- result.push(value)
701
- }
702
- }
703
- delete this[arguments[i]];
704
- }
705
- return result;
966
+ times: function(iterator) {
967
+ $R(0, this, true).each(iterator);
968
+ return this;
706
969
  },
707
970
 
708
- toQueryString: function() {
709
- return Hash.toQueryString(this);
971
+ toPaddedString: function(length, radix) {
972
+ var string = this.toString(radix || 10);
973
+ return '0'.times(length - string.length) + string;
710
974
  },
711
975
 
712
- inspect: function() {
713
- return '#<Hash:{' + this.map(function(pair) {
714
- return pair.map(Object.inspect).join(': ');
715
- }).join(', ') + '}>';
976
+ toJSON: function() {
977
+ return isFinite(this) ? this.toString() : 'null';
716
978
  }
717
979
  });
718
980
 
981
+ $w('abs round ceil floor').each(function(method){
982
+ Number.prototype[method] = Math[method].methodize();
983
+ });
719
984
  function $H(object) {
720
- if (object && object.constructor == Hash) return object;
721
985
  return new Hash(object);
722
986
  };
723
- ObjectRange = Class.create();
724
- Object.extend(ObjectRange.prototype, Enumerable);
725
- Object.extend(ObjectRange.prototype, {
987
+
988
+ var Hash = Class.create(Enumerable, (function() {
989
+
990
+ function toQueryPair(key, value) {
991
+ if (Object.isUndefined(value)) return key;
992
+ return key + '=' + encodeURIComponent(String.interpret(value));
993
+ }
994
+
995
+ return {
996
+ initialize: function(object) {
997
+ this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
998
+ },
999
+
1000
+ _each: function(iterator) {
1001
+ for (var key in this._object) {
1002
+ var value = this._object[key], pair = [key, value];
1003
+ pair.key = key;
1004
+ pair.value = value;
1005
+ iterator(pair);
1006
+ }
1007
+ },
1008
+
1009
+ set: function(key, value) {
1010
+ return this._object[key] = value;
1011
+ },
1012
+
1013
+ get: function(key) {
1014
+ return this._object[key];
1015
+ },
1016
+
1017
+ unset: function(key) {
1018
+ var value = this._object[key];
1019
+ delete this._object[key];
1020
+ return value;
1021
+ },
1022
+
1023
+ toObject: function() {
1024
+ return Object.clone(this._object);
1025
+ },
1026
+
1027
+ keys: function() {
1028
+ return this.pluck('key');
1029
+ },
1030
+
1031
+ values: function() {
1032
+ return this.pluck('value');
1033
+ },
1034
+
1035
+ index: function(value) {
1036
+ var match = this.detect(function(pair) {
1037
+ return pair.value === value;
1038
+ });
1039
+ return match && match.key;
1040
+ },
1041
+
1042
+ merge: function(object) {
1043
+ return this.clone().update(object);
1044
+ },
1045
+
1046
+ update: function(object) {
1047
+ return new Hash(object).inject(this, function(result, pair) {
1048
+ result.set(pair.key, pair.value);
1049
+ return result;
1050
+ });
1051
+ },
1052
+
1053
+ toQueryString: function() {
1054
+ return this.map(function(pair) {
1055
+ var key = encodeURIComponent(pair.key), values = pair.value;
1056
+
1057
+ if (values && typeof values == 'object') {
1058
+ if (Object.isArray(values))
1059
+ return values.map(toQueryPair.curry(key)).join('&');
1060
+ }
1061
+ return toQueryPair(key, values);
1062
+ }).join('&');
1063
+ },
1064
+
1065
+ inspect: function() {
1066
+ return '#<Hash:{' + this.map(function(pair) {
1067
+ return pair.map(Object.inspect).join(': ');
1068
+ }).join(', ') + '}>';
1069
+ },
1070
+
1071
+ toJSON: function() {
1072
+ return Object.toJSON(this.toObject());
1073
+ },
1074
+
1075
+ clone: function() {
1076
+ return new Hash(this);
1077
+ }
1078
+ }
1079
+ })());
1080
+
1081
+ Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
1082
+ Hash.from = $H;
1083
+ var ObjectRange = Class.create(Enumerable, {
726
1084
  initialize: function(start, end, exclusive) {
727
1085
  this.start = start;
728
1086
  this.end = end;
@@ -748,7 +1106,7 @@ Object.extend(ObjectRange.prototype, {
748
1106
 
749
1107
  var $R = function(start, end, exclusive) {
750
1108
  return new ObjectRange(start, end, exclusive);
751
- }
1109
+ };
752
1110
 
753
1111
  var Ajax = {
754
1112
  getTransport: function() {
@@ -760,7 +1118,7 @@ var Ajax = {
760
1118
  },
761
1119
 
762
1120
  activeRequestCount: 0
763
- }
1121
+ };
764
1122
 
765
1123
  Ajax.Responders = {
766
1124
  responders: [],
@@ -780,10 +1138,10 @@ Ajax.Responders = {
780
1138
 
781
1139
  dispatch: function(callback, request, transport, json) {
782
1140
  this.each(function(responder) {
783
- if (typeof responder[callback] == 'function') {
1141
+ if (Object.isFunction(responder[callback])) {
784
1142
  try {
785
1143
  responder[callback].apply(responder, [request, transport, json]);
786
- } catch (e) {}
1144
+ } catch (e) { }
787
1145
  }
788
1146
  });
789
1147
  }
@@ -792,49 +1150,45 @@ Ajax.Responders = {
792
1150
  Object.extend(Ajax.Responders, Enumerable);
793
1151
 
794
1152
  Ajax.Responders.register({
795
- onCreate: function() {
796
- Ajax.activeRequestCount++;
797
- },
798
- onComplete: function() {
799
- Ajax.activeRequestCount--;
800
- }
1153
+ onCreate: function() { Ajax.activeRequestCount++ },
1154
+ onComplete: function() { Ajax.activeRequestCount-- }
801
1155
  });
802
1156
 
803
- Ajax.Base = function() {};
804
- Ajax.Base.prototype = {
805
- setOptions: function(options) {
1157
+ Ajax.Base = Class.create({
1158
+ initialize: function(options) {
806
1159
  this.options = {
807
1160
  method: 'post',
808
1161
  asynchronous: true,
809
1162
  contentType: 'application/x-www-form-urlencoded',
810
1163
  encoding: 'UTF-8',
811
- parameters: ''
812
- }
813
- Object.extend(this.options, options || {});
1164
+ parameters: '',
1165
+ evalJSON: true,
1166
+ evalJS: true
1167
+ };
1168
+ Object.extend(this.options, options || { });
814
1169
 
815
1170
  this.options.method = this.options.method.toLowerCase();
816
- if (typeof this.options.parameters == 'string')
1171
+
1172
+ if (Object.isString(this.options.parameters))
817
1173
  this.options.parameters = this.options.parameters.toQueryParams();
1174
+ else if (Object.isHash(this.options.parameters))
1175
+ this.options.parameters = this.options.parameters.toObject();
818
1176
  }
819
- }
820
-
821
- Ajax.Request = Class.create();
822
- Ajax.Request.Events =
823
- ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1177
+ });
824
1178
 
825
- Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
1179
+ Ajax.Request = Class.create(Ajax.Base, {
826
1180
  _complete: false,
827
1181
 
828
- initialize: function(url, options) {
1182
+ initialize: function($super, url, options) {
1183
+ $super(options);
829
1184
  this.transport = Ajax.getTransport();
830
- this.setOptions(options);
831
1185
  this.request(url);
832
1186
  },
833
1187
 
834
1188
  request: function(url) {
835
1189
  this.url = url;
836
1190
  this.method = this.options.method;
837
- var params = this.options.parameters;
1191
+ var params = Object.clone(this.options.parameters);
838
1192
 
839
1193
  if (!['get', 'post'].include(this.method)) {
840
1194
  // simulate other verbs over post
@@ -842,28 +1196,31 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
842
1196
  this.method = 'post';
843
1197
  }
844
1198
 
845
- params = Hash.toQueryString(params);
846
- if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='
1199
+ this.parameters = params;
847
1200
 
848
- // when GET, append parameters to URL
849
- if (this.method == 'get' && params)
850
- this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;
1201
+ if (params = Object.toQueryString(params)) {
1202
+ // when GET, append parameters to URL
1203
+ if (this.method == 'get')
1204
+ this.url += (this.url.include('?') ? '&' : '?') + params;
1205
+ else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1206
+ params += '&_=';
1207
+ }
851
1208
 
852
1209
  try {
853
- Ajax.Responders.dispatch('onCreate', this, this.transport);
1210
+ var response = new Ajax.Response(this);
1211
+ if (this.options.onCreate) this.options.onCreate(response);
1212
+ Ajax.Responders.dispatch('onCreate', this, response);
854
1213
 
855
1214
  this.transport.open(this.method.toUpperCase(), this.url,
856
1215
  this.options.asynchronous);
857
1216
 
858
- if (this.options.asynchronous)
859
- setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
1217
+ if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
860
1218
 
861
1219
  this.transport.onreadystatechange = this.onStateChange.bind(this);
862
1220
  this.setRequestHeaders();
863
1221
 
864
- var body = this.method == 'post' ? (this.options.postBody || params) : null;
865
-
866
- this.transport.send(body);
1222
+ this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1223
+ this.transport.send(this.body);
867
1224
 
868
1225
  /* Force Firefox to handle ready state 4 for synchronous requests */
869
1226
  if (!this.options.asynchronous && this.transport.overrideMimeType)
@@ -905,7 +1262,7 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
905
1262
  if (typeof this.options.requestHeaders == 'object') {
906
1263
  var extras = this.options.requestHeaders;
907
1264
 
908
- if (typeof extras.push == 'function')
1265
+ if (Object.isFunction(extras.push))
909
1266
  for (var i = 0, length = extras.length; i < length; i += 2)
910
1267
  headers[extras[i]] = extras[i+1];
911
1268
  else
@@ -917,32 +1274,39 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
917
1274
  },
918
1275
 
919
1276
  success: function() {
920
- return !this.transport.status
921
- || (this.transport.status >= 200 && this.transport.status < 300);
1277
+ var status = this.getStatus();
1278
+ return !status || (status >= 200 && status < 300);
1279
+ },
1280
+
1281
+ getStatus: function() {
1282
+ try {
1283
+ return this.transport.status || 0;
1284
+ } catch (e) { return 0 }
922
1285
  },
923
1286
 
924
1287
  respondToReadyState: function(readyState) {
925
- var state = Ajax.Request.Events[readyState];
926
- var transport = this.transport, json = this.evalJSON();
1288
+ var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
927
1289
 
928
1290
  if (state == 'Complete') {
929
1291
  try {
930
1292
  this._complete = true;
931
- (this.options['on' + this.transport.status]
1293
+ (this.options['on' + response.status]
932
1294
  || this.options['on' + (this.success() ? 'Success' : 'Failure')]
933
- || Prototype.emptyFunction)(transport, json);
1295
+ || Prototype.emptyFunction)(response, response.headerJSON);
934
1296
  } catch (e) {
935
1297
  this.dispatchException(e);
936
1298
  }
937
1299
 
938
- if ((this.getHeader('Content-type') || 'text/javascript').strip().
939
- match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
940
- this.evalResponse();
1300
+ var contentType = response.getHeader('Content-type');
1301
+ if (this.options.evalJS == 'force'
1302
+ || (this.options.evalJS && this.isSameOrigin() && contentType
1303
+ && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
1304
+ this.evalResponse();
941
1305
  }
942
1306
 
943
1307
  try {
944
- (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
945
- Ajax.Responders.dispatch('on' + state, this, transport, json);
1308
+ (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
1309
+ Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
946
1310
  } catch (e) {
947
1311
  this.dispatchException(e);
948
1312
  }
@@ -953,22 +1317,24 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
953
1317
  }
954
1318
  },
955
1319
 
956
- getHeader: function(name) {
957
- try {
958
- return this.transport.getResponseHeader(name);
959
- } catch (e) { return null }
1320
+ isSameOrigin: function() {
1321
+ var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
1322
+ return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
1323
+ protocol: location.protocol,
1324
+ domain: document.domain,
1325
+ port: location.port ? ':' + location.port : ''
1326
+ }));
960
1327
  },
961
1328
 
962
- evalJSON: function() {
1329
+ getHeader: function(name) {
963
1330
  try {
964
- var json = this.getHeader('X-JSON');
965
- return json ? eval('(' + json + ')') : null;
1331
+ return this.transport.getResponseHeader(name) || null;
966
1332
  } catch (e) { return null }
967
1333
  },
968
1334
 
969
1335
  evalResponse: function() {
970
1336
  try {
971
- return eval(this.transport.responseText);
1337
+ return eval((this.transport.responseText || '').unfilterJSON());
972
1338
  } catch (e) {
973
1339
  this.dispatchException(e);
974
1340
  }
@@ -980,57 +1346,128 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
980
1346
  }
981
1347
  });
982
1348
 
983
- Ajax.Updater = Class.create();
1349
+ Ajax.Request.Events =
1350
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
984
1351
 
985
- Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
986
- initialize: function(container, url, options) {
987
- this.container = {
988
- success: (container.success || container),
989
- failure: (container.failure || (container.success ? null : container))
1352
+ Ajax.Response = Class.create({
1353
+ initialize: function(request){
1354
+ this.request = request;
1355
+ var transport = this.transport = request.transport,
1356
+ readyState = this.readyState = transport.readyState;
1357
+
1358
+ if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
1359
+ this.status = this.getStatus();
1360
+ this.statusText = this.getStatusText();
1361
+ this.responseText = String.interpret(transport.responseText);
1362
+ this.headerJSON = this._getHeaderJSON();
990
1363
  }
991
1364
 
992
- this.transport = Ajax.getTransport();
993
- this.setOptions(options);
1365
+ if(readyState == 4) {
1366
+ var xml = transport.responseXML;
1367
+ this.responseXML = Object.isUndefined(xml) ? null : xml;
1368
+ this.responseJSON = this._getResponseJSON();
1369
+ }
1370
+ },
994
1371
 
995
- var onComplete = this.options.onComplete || Prototype.emptyFunction;
996
- this.options.onComplete = (function(transport, param) {
997
- this.updateContent();
998
- onComplete(transport, param);
999
- }).bind(this);
1372
+ status: 0,
1373
+ statusText: '',
1000
1374
 
1001
- this.request(url);
1375
+ getStatus: Ajax.Request.prototype.getStatus,
1376
+
1377
+ getStatusText: function() {
1378
+ try {
1379
+ return this.transport.statusText || '';
1380
+ } catch (e) { return '' }
1002
1381
  },
1003
1382
 
1004
- updateContent: function() {
1005
- var receiver = this.container[this.success() ? 'success' : 'failure'];
1006
- var response = this.transport.responseText;
1383
+ getHeader: Ajax.Request.prototype.getHeader,
1384
+
1385
+ getAllHeaders: function() {
1386
+ try {
1387
+ return this.getAllResponseHeaders();
1388
+ } catch (e) { return null }
1389
+ },
1007
1390
 
1008
- if (!this.options.evalScripts) response = response.stripScripts();
1391
+ getResponseHeader: function(name) {
1392
+ return this.transport.getResponseHeader(name);
1393
+ },
1009
1394
 
1010
- if (receiver = $(receiver)) {
1011
- if (this.options.insertion)
1012
- new this.options.insertion(receiver, response);
1013
- else
1014
- receiver.update(response);
1395
+ getAllResponseHeaders: function() {
1396
+ return this.transport.getAllResponseHeaders();
1397
+ },
1398
+
1399
+ _getHeaderJSON: function() {
1400
+ var json = this.getHeader('X-JSON');
1401
+ if (!json) return null;
1402
+ json = decodeURIComponent(escape(json));
1403
+ try {
1404
+ return json.evalJSON(this.request.options.sanitizeJSON ||
1405
+ !this.request.isSameOrigin());
1406
+ } catch (e) {
1407
+ this.request.dispatchException(e);
1408
+ }
1409
+ },
1410
+
1411
+ _getResponseJSON: function() {
1412
+ var options = this.request.options;
1413
+ if (!options.evalJSON || (options.evalJSON != 'force' &&
1414
+ !(this.getHeader('Content-type') || '').include('application/json')) ||
1415
+ this.responseText.blank())
1416
+ return null;
1417
+ try {
1418
+ return this.responseText.evalJSON(options.sanitizeJSON ||
1419
+ !this.request.isSameOrigin());
1420
+ } catch (e) {
1421
+ this.request.dispatchException(e);
1015
1422
  }
1423
+ }
1424
+ });
1425
+
1426
+ Ajax.Updater = Class.create(Ajax.Request, {
1427
+ initialize: function($super, container, url, options) {
1428
+ this.container = {
1429
+ success: (container.success || container),
1430
+ failure: (container.failure || (container.success ? null : container))
1431
+ };
1016
1432
 
1017
- if (this.success()) {
1018
- if (this.onComplete)
1019
- setTimeout(this.onComplete.bind(this), 10);
1433
+ options = Object.clone(options);
1434
+ var onComplete = options.onComplete;
1435
+ options.onComplete = (function(response, json) {
1436
+ this.updateContent(response.responseText);
1437
+ if (Object.isFunction(onComplete)) onComplete(response, json);
1438
+ }).bind(this);
1439
+
1440
+ $super(url, options);
1441
+ },
1442
+
1443
+ updateContent: function(responseText) {
1444
+ var receiver = this.container[this.success() ? 'success' : 'failure'],
1445
+ options = this.options;
1446
+
1447
+ if (!options.evalScripts) responseText = responseText.stripScripts();
1448
+
1449
+ if (receiver = $(receiver)) {
1450
+ if (options.insertion) {
1451
+ if (Object.isString(options.insertion)) {
1452
+ var insertion = { }; insertion[options.insertion] = responseText;
1453
+ receiver.insert(insertion);
1454
+ }
1455
+ else options.insertion(receiver, responseText);
1456
+ }
1457
+ else receiver.update(responseText);
1020
1458
  }
1021
1459
  }
1022
1460
  });
1023
1461
 
1024
- Ajax.PeriodicalUpdater = Class.create();
1025
- Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
1026
- initialize: function(container, url, options) {
1027
- this.setOptions(options);
1462
+ Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
1463
+ initialize: function($super, container, url, options) {
1464
+ $super(options);
1028
1465
  this.onComplete = this.options.onComplete;
1029
1466
 
1030
1467
  this.frequency = (this.options.frequency || 2);
1031
1468
  this.decay = (this.options.decay || 1);
1032
1469
 
1033
- this.updater = {};
1470
+ this.updater = { };
1034
1471
  this.container = container;
1035
1472
  this.url = url;
1036
1473
 
@@ -1048,15 +1485,14 @@ Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
1048
1485
  (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1049
1486
  },
1050
1487
 
1051
- updateComplete: function(request) {
1488
+ updateComplete: function(response) {
1052
1489
  if (this.options.decay) {
1053
- this.decay = (request.responseText == this.lastText ?
1490
+ this.decay = (response.responseText == this.lastText ?
1054
1491
  this.decay * this.options.decay : 1);
1055
1492
 
1056
- this.lastText = request.responseText;
1493
+ this.lastText = response.responseText;
1057
1494
  }
1058
- this.timer = setTimeout(this.onTimerEvent.bind(this),
1059
- this.decay * this.frequency * 1000);
1495
+ this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
1060
1496
  },
1061
1497
 
1062
1498
  onTimerEvent: function() {
@@ -1069,7 +1505,7 @@ function $(element) {
1069
1505
  elements.push($(arguments[i]));
1070
1506
  return elements;
1071
1507
  }
1072
- if (typeof element == 'string')
1508
+ if (Object.isString(element))
1073
1509
  element = document.getElementById(element);
1074
1510
  return Element.extend(element);
1075
1511
  }
@@ -1080,63 +1516,51 @@ if (Prototype.BrowserFeatures.XPath) {
1080
1516
  var query = document.evaluate(expression, $(parentElement) || document,
1081
1517
  null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1082
1518
  for (var i = 0, length = query.snapshotLength; i < length; i++)
1083
- results.push(query.snapshotItem(i));
1519
+ results.push(Element.extend(query.snapshotItem(i)));
1084
1520
  return results;
1085
1521
  };
1086
1522
  }
1087
1523
 
1088
- document.getElementsByClassName = function(className, parentElement) {
1089
- if (Prototype.BrowserFeatures.XPath) {
1090
- var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
1091
- return document._getElementsByXPath(q, parentElement);
1092
- } else {
1093
- var children = ($(parentElement) || document.body).getElementsByTagName('*');
1094
- var elements = [], child;
1095
- for (var i = 0, length = children.length; i < length; i++) {
1096
- child = children[i];
1097
- if (Element.hasClassName(child, className))
1098
- elements.push(Element.extend(child));
1099
- }
1100
- return elements;
1101
- }
1102
- };
1103
-
1104
1524
  /*--------------------------------------------------------------------------*/
1105
1525
 
1106
- if (!window.Element)
1107
- var Element = new Object();
1108
-
1109
- Element.extend = function(element) {
1110
- if (!element || _nativeExtensions || element.nodeType == 3) return element;
1111
-
1112
- if (!element._extended && element.tagName && element != window) {
1113
- var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
1114
-
1115
- if (element.tagName == 'FORM')
1116
- Object.extend(methods, Form.Methods);
1117
- if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
1118
- Object.extend(methods, Form.Element.Methods);
1119
-
1120
- Object.extend(methods, Element.Methods.Simulated);
1526
+ if (!window.Node) var Node = { };
1527
+
1528
+ if (!Node.ELEMENT_NODE) {
1529
+ // DOM level 2 ECMAScript Language Binding
1530
+ Object.extend(Node, {
1531
+ ELEMENT_NODE: 1,
1532
+ ATTRIBUTE_NODE: 2,
1533
+ TEXT_NODE: 3,
1534
+ CDATA_SECTION_NODE: 4,
1535
+ ENTITY_REFERENCE_NODE: 5,
1536
+ ENTITY_NODE: 6,
1537
+ PROCESSING_INSTRUCTION_NODE: 7,
1538
+ COMMENT_NODE: 8,
1539
+ DOCUMENT_NODE: 9,
1540
+ DOCUMENT_TYPE_NODE: 10,
1541
+ DOCUMENT_FRAGMENT_NODE: 11,
1542
+ NOTATION_NODE: 12
1543
+ });
1544
+ }
1121
1545
 
1122
- for (var property in methods) {
1123
- var value = methods[property];
1124
- if (typeof value == 'function' && !(property in element))
1125
- element[property] = cache.findOrStore(value);
1546
+ (function() {
1547
+ var element = this.Element;
1548
+ this.Element = function(tagName, attributes) {
1549
+ attributes = attributes || { };
1550
+ tagName = tagName.toLowerCase();
1551
+ var cache = Element.cache;
1552
+ if (Prototype.Browser.IE && attributes.name) {
1553
+ tagName = '<' + tagName + ' name="' + attributes.name + '">';
1554
+ delete attributes.name;
1555
+ return Element.writeAttribute(document.createElement(tagName), attributes);
1126
1556
  }
1127
- }
1128
-
1129
- element._extended = true;
1130
- return element;
1131
- };
1557
+ if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
1558
+ return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
1559
+ };
1560
+ Object.extend(this.Element, element || { });
1561
+ }).call(window);
1132
1562
 
1133
- Element.extend.cache = {
1134
- findOrStore: function(value) {
1135
- return this[value] = this[value] || function() {
1136
- return value.apply(null, [this].concat($A(arguments)));
1137
- }
1138
- }
1139
- };
1563
+ Element.cache = { };
1140
1564
 
1141
1565
  Element.Methods = {
1142
1566
  visible: function(element) {
@@ -1165,28 +1589,78 @@ Element.Methods = {
1165
1589
  return element;
1166
1590
  },
1167
1591
 
1168
- update: function(element, html) {
1169
- html = typeof html == 'undefined' ? '' : html.toString();
1170
- $(element).innerHTML = html.stripScripts();
1171
- setTimeout(function() {html.evalScripts()}, 10);
1592
+ update: function(element, content) {
1593
+ element = $(element);
1594
+ if (content && content.toElement) content = content.toElement();
1595
+ if (Object.isElement(content)) return element.update().insert(content);
1596
+ content = Object.toHTML(content);
1597
+ element.innerHTML = content.stripScripts();
1598
+ content.evalScripts.bind(content).defer();
1172
1599
  return element;
1173
1600
  },
1174
1601
 
1175
- replace: function(element, html) {
1602
+ replace: function(element, content) {
1176
1603
  element = $(element);
1177
- html = typeof html == 'undefined' ? '' : html.toString();
1178
- if (element.outerHTML) {
1179
- element.outerHTML = html.stripScripts();
1180
- } else {
1604
+ if (content && content.toElement) content = content.toElement();
1605
+ else if (!Object.isElement(content)) {
1606
+ content = Object.toHTML(content);
1181
1607
  var range = element.ownerDocument.createRange();
1182
- range.selectNodeContents(element);
1183
- element.parentNode.replaceChild(
1184
- range.createContextualFragment(html.stripScripts()), element);
1608
+ range.selectNode(element);
1609
+ content.evalScripts.bind(content).defer();
1610
+ content = range.createContextualFragment(content.stripScripts());
1611
+ }
1612
+ element.parentNode.replaceChild(content, element);
1613
+ return element;
1614
+ },
1615
+
1616
+ insert: function(element, insertions) {
1617
+ element = $(element);
1618
+
1619
+ if (Object.isString(insertions) || Object.isNumber(insertions) ||
1620
+ Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
1621
+ insertions = {bottom:insertions};
1622
+
1623
+ var content, insert, tagName, childNodes;
1624
+
1625
+ for (var position in insertions) {
1626
+ content = insertions[position];
1627
+ position = position.toLowerCase();
1628
+ insert = Element._insertionTranslations[position];
1629
+
1630
+ if (content && content.toElement) content = content.toElement();
1631
+ if (Object.isElement(content)) {
1632
+ insert(element, content);
1633
+ continue;
1634
+ }
1635
+
1636
+ content = Object.toHTML(content);
1637
+
1638
+ tagName = ((position == 'before' || position == 'after')
1639
+ ? element.parentNode : element).tagName.toUpperCase();
1640
+
1641
+ childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
1642
+
1643
+ if (position == 'top' || position == 'after') childNodes.reverse();
1644
+ childNodes.each(insert.curry(element));
1645
+
1646
+ content.evalScripts.bind(content).defer();
1185
1647
  }
1186
- setTimeout(function() {html.evalScripts()}, 10);
1648
+
1187
1649
  return element;
1188
1650
  },
1189
1651
 
1652
+ wrap: function(element, wrapper, attributes) {
1653
+ element = $(element);
1654
+ if (Object.isElement(wrapper))
1655
+ $(wrapper).writeAttribute(attributes || { });
1656
+ else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
1657
+ else wrapper = new Element('div', wrapper);
1658
+ if (element.parentNode)
1659
+ element.parentNode.replaceChild(wrapper, element);
1660
+ wrapper.appendChild(element);
1661
+ return wrapper;
1662
+ },
1663
+
1190
1664
  inspect: function(element) {
1191
1665
  element = $(element);
1192
1666
  var result = '<' + element.tagName.toLowerCase();
@@ -1212,7 +1686,13 @@ Element.Methods = {
1212
1686
  },
1213
1687
 
1214
1688
  descendants: function(element) {
1215
- return $A($(element).getElementsByTagName('*'));
1689
+ return $(element).select("*");
1690
+ },
1691
+
1692
+ firstDescendant: function(element) {
1693
+ element = $(element).firstChild;
1694
+ while (element && element.nodeType != 1) element = element.nextSibling;
1695
+ return $(element);
1216
1696
  },
1217
1697
 
1218
1698
  immediateDescendants: function(element) {
@@ -1236,48 +1716,95 @@ Element.Methods = {
1236
1716
  },
1237
1717
 
1238
1718
  match: function(element, selector) {
1239
- if (typeof selector == 'string')
1719
+ if (Object.isString(selector))
1240
1720
  selector = new Selector(selector);
1241
1721
  return selector.match($(element));
1242
1722
  },
1243
1723
 
1244
1724
  up: function(element, expression, index) {
1245
- return Selector.findElement($(element).ancestors(), expression, index);
1725
+ element = $(element);
1726
+ if (arguments.length == 1) return $(element.parentNode);
1727
+ var ancestors = element.ancestors();
1728
+ return Object.isNumber(expression) ? ancestors[expression] :
1729
+ Selector.findElement(ancestors, expression, index);
1246
1730
  },
1247
1731
 
1248
1732
  down: function(element, expression, index) {
1249
- return Selector.findElement($(element).descendants(), expression, index);
1733
+ element = $(element);
1734
+ if (arguments.length == 1) return element.firstDescendant();
1735
+ return Object.isNumber(expression) ? element.descendants()[expression] :
1736
+ element.select(expression)[index || 0];
1250
1737
  },
1251
1738
 
1252
1739
  previous: function(element, expression, index) {
1253
- return Selector.findElement($(element).previousSiblings(), expression, index);
1740
+ element = $(element);
1741
+ if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
1742
+ var previousSiblings = element.previousSiblings();
1743
+ return Object.isNumber(expression) ? previousSiblings[expression] :
1744
+ Selector.findElement(previousSiblings, expression, index);
1254
1745
  },
1255
1746
 
1256
1747
  next: function(element, expression, index) {
1257
- return Selector.findElement($(element).nextSiblings(), expression, index);
1748
+ element = $(element);
1749
+ if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
1750
+ var nextSiblings = element.nextSiblings();
1751
+ return Object.isNumber(expression) ? nextSiblings[expression] :
1752
+ Selector.findElement(nextSiblings, expression, index);
1258
1753
  },
1259
1754
 
1260
- getElementsBySelector: function() {
1755
+ select: function() {
1261
1756
  var args = $A(arguments), element = $(args.shift());
1262
1757
  return Selector.findChildElements(element, args);
1263
1758
  },
1264
1759
 
1265
- getElementsByClassName: function(element, className) {
1266
- return document.getElementsByClassName(className, element);
1760
+ adjacent: function() {
1761
+ var args = $A(arguments), element = $(args.shift());
1762
+ return Selector.findChildElements(element.parentNode, args).without(element);
1763
+ },
1764
+
1765
+ identify: function(element) {
1766
+ element = $(element);
1767
+ var id = element.readAttribute('id'), self = arguments.callee;
1768
+ if (id) return id;
1769
+ do { id = 'anonymous_element_' + self.counter++ } while ($(id));
1770
+ element.writeAttribute('id', id);
1771
+ return id;
1267
1772
  },
1268
1773
 
1269
1774
  readAttribute: function(element, name) {
1270
1775
  element = $(element);
1271
- if (document.all && !window.opera) {
1272
- var t = Element._attributeTranslations;
1776
+ if (Prototype.Browser.IE) {
1777
+ var t = Element._attributeTranslations.read;
1273
1778
  if (t.values[name]) return t.values[name](element, name);
1274
- if (t.names[name]) name = t.names[name];
1275
- var attribute = element.attributes[name];
1276
- if(attribute) return attribute.nodeValue;
1779
+ if (t.names[name]) name = t.names[name];
1780
+ if (name.include(':')) {
1781
+ return (!element.attributes || !element.attributes[name]) ? null :
1782
+ element.attributes[name].value;
1783
+ }
1277
1784
  }
1278
1785
  return element.getAttribute(name);
1279
1786
  },
1280
1787
 
1788
+ writeAttribute: function(element, name, value) {
1789
+ element = $(element);
1790
+ var attributes = { }, t = Element._attributeTranslations.write;
1791
+
1792
+ if (typeof name == 'object') attributes = name;
1793
+ else attributes[name] = Object.isUndefined(value) ? true : value;
1794
+
1795
+ for (var attr in attributes) {
1796
+ name = t.names[attr] || attr;
1797
+ value = attributes[attr];
1798
+ if (t.values[attr]) name = t.values[attr](element, value);
1799
+ if (value === false || value === null)
1800
+ element.removeAttribute(name);
1801
+ else if (value === true)
1802
+ element.setAttribute(name, name);
1803
+ else element.setAttribute(name, value);
1804
+ }
1805
+ return element;
1806
+ },
1807
+
1281
1808
  getHeight: function(element) {
1282
1809
  return $(element).getDimensions().height;
1283
1810
  },
@@ -1293,39 +1820,28 @@ Element.Methods = {
1293
1820
  hasClassName: function(element, className) {
1294
1821
  if (!(element = $(element))) return;
1295
1822
  var elementClassName = element.className;
1296
- if (elementClassName.length == 0) return false;
1297
- if (elementClassName == className ||
1298
- elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
1299
- return true;
1300
- return false;
1823
+ return (elementClassName.length > 0 && (elementClassName == className ||
1824
+ new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
1301
1825
  },
1302
1826
 
1303
1827
  addClassName: function(element, className) {
1304
1828
  if (!(element = $(element))) return;
1305
- Element.classNames(element).add(className);
1829
+ if (!element.hasClassName(className))
1830
+ element.className += (element.className ? ' ' : '') + className;
1306
1831
  return element;
1307
1832
  },
1308
1833
 
1309
1834
  removeClassName: function(element, className) {
1310
1835
  if (!(element = $(element))) return;
1311
- Element.classNames(element).remove(className);
1836
+ element.className = element.className.replace(
1837
+ new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
1312
1838
  return element;
1313
1839
  },
1314
1840
 
1315
1841
  toggleClassName: function(element, className) {
1316
1842
  if (!(element = $(element))) return;
1317
- Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
1318
- return element;
1319
- },
1320
-
1321
- observe: function() {
1322
- Event.observe.apply(Event, arguments);
1323
- return $A(arguments).first();
1324
- },
1325
-
1326
- stopObserving: function() {
1327
- Event.stopObserving.apply(Event, arguments);
1328
- return $A(arguments).first();
1843
+ return element[element.hasClassName(className) ?
1844
+ 'removeClassName' : 'addClassName'](className);
1329
1845
  },
1330
1846
 
1331
1847
  // removes whitespace-only text node children
@@ -1342,74 +1858,77 @@ Element.Methods = {
1342
1858
  },
1343
1859
 
1344
1860
  empty: function(element) {
1345
- return $(element).innerHTML.match(/^\s*$/);
1861
+ return $(element).innerHTML.blank();
1346
1862
  },
1347
1863
 
1348
1864
  descendantOf: function(element, ancestor) {
1349
1865
  element = $(element), ancestor = $(ancestor);
1866
+ var originalAncestor = ancestor;
1867
+
1868
+ if (element.compareDocumentPosition)
1869
+ return (element.compareDocumentPosition(ancestor) & 8) === 8;
1870
+
1871
+ if (element.sourceIndex && !Prototype.Browser.Opera) {
1872
+ var e = element.sourceIndex, a = ancestor.sourceIndex,
1873
+ nextAncestor = ancestor.nextSibling;
1874
+ if (!nextAncestor) {
1875
+ do { ancestor = ancestor.parentNode; }
1876
+ while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
1877
+ }
1878
+ if (nextAncestor && nextAncestor.sourceIndex)
1879
+ return (e > a && e < nextAncestor.sourceIndex);
1880
+ }
1881
+
1350
1882
  while (element = element.parentNode)
1351
- if (element == ancestor) return true;
1883
+ if (element == originalAncestor) return true;
1352
1884
  return false;
1353
1885
  },
1354
1886
 
1355
1887
  scrollTo: function(element) {
1356
1888
  element = $(element);
1357
- var pos = Position.cumulativeOffset(element);
1889
+ var pos = element.cumulativeOffset();
1358
1890
  window.scrollTo(pos[0], pos[1]);
1359
1891
  return element;
1360
1892
  },
1361
1893
 
1362
1894
  getStyle: function(element, style) {
1363
1895
  element = $(element);
1364
- if (['float','cssFloat'].include(style))
1365
- style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
1366
- style = style.camelize();
1896
+ style = style == 'float' ? 'cssFloat' : style.camelize();
1367
1897
  var value = element.style[style];
1368
1898
  if (!value) {
1369
- if (document.defaultView && document.defaultView.getComputedStyle) {
1370
- var css = document.defaultView.getComputedStyle(element, null);
1371
- value = css ? css[style] : null;
1372
- } else if (element.currentStyle) {
1373
- value = element.currentStyle[style];
1374
- }
1899
+ var css = document.defaultView.getComputedStyle(element, null);
1900
+ value = css ? css[style] : null;
1375
1901
  }
1902
+ if (style == 'opacity') return value ? parseFloat(value) : 1.0;
1903
+ return value == 'auto' ? null : value;
1904
+ },
1376
1905
 
1377
- if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
1378
- value = element['offset'+style.capitalize()] + 'px';
1906
+ getOpacity: function(element) {
1907
+ return $(element).getStyle('opacity');
1908
+ },
1379
1909
 
1380
- if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
1381
- if (Element.getStyle(element, 'position') == 'static') value = 'auto';
1382
- if(style == 'opacity') {
1383
- if(value) return parseFloat(value);
1384
- if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
1385
- if(value[1]) return parseFloat(value[1]) / 100;
1386
- return 1.0;
1910
+ setStyle: function(element, styles) {
1911
+ element = $(element);
1912
+ var elementStyle = element.style, match;
1913
+ if (Object.isString(styles)) {
1914
+ element.style.cssText += ';' + styles;
1915
+ return styles.include('opacity') ?
1916
+ element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
1387
1917
  }
1388
- return value == 'auto' ? null : value;
1918
+ for (var property in styles)
1919
+ if (property == 'opacity') element.setOpacity(styles[property]);
1920
+ else
1921
+ elementStyle[(property == 'float' || property == 'cssFloat') ?
1922
+ (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
1923
+ property] = styles[property];
1924
+
1925
+ return element;
1389
1926
  },
1390
1927
 
1391
- setStyle: function(element, style) {
1928
+ setOpacity: function(element, value) {
1392
1929
  element = $(element);
1393
- for (var name in style) {
1394
- var value = style[name];
1395
- if(name == 'opacity') {
1396
- if (value == 1) {
1397
- value = (/Gecko/.test(navigator.userAgent) &&
1398
- !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
1399
- if(/MSIE/.test(navigator.userAgent) && !window.opera)
1400
- element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
1401
- } else if(value == '') {
1402
- if(/MSIE/.test(navigator.userAgent) && !window.opera)
1403
- element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
1404
- } else {
1405
- if(value < 0.00001) value = 0;
1406
- if(/MSIE/.test(navigator.userAgent) && !window.opera)
1407
- element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
1408
- 'alpha(opacity='+value*100+')';
1409
- }
1410
- } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
1411
- element.style[name.camelize()] = value;
1412
- }
1930
+ element.style.opacity = (value == 1 || value === '') ? '' :
1931
+ (value < 0.00001) ? 0 : value;
1413
1932
  return element;
1414
1933
  },
1415
1934
 
@@ -1468,8 +1987,8 @@ Element.Methods = {
1468
1987
  makeClipping: function(element) {
1469
1988
  element = $(element);
1470
1989
  if (element._overflow) return element;
1471
- element._overflow = element.style.overflow || 'auto';
1472
- if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
1990
+ element._overflow = Element.getStyle(element, 'overflow') || 'auto';
1991
+ if (element._overflow !== 'hidden')
1473
1992
  element.style.overflow = 'hidden';
1474
1993
  return element;
1475
1994
  },
@@ -1480,393 +1999,1376 @@ Element.Methods = {
1480
1999
  element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
1481
2000
  element._overflow = null;
1482
2001
  return element;
1483
- }
1484
- };
1485
-
1486
- Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
1487
-
1488
- Element._attributeTranslations = {};
1489
-
1490
- Element._attributeTranslations.names = {
1491
- colspan: "colSpan",
1492
- rowspan: "rowSpan",
1493
- valign: "vAlign",
1494
- datetime: "dateTime",
1495
- accesskey: "accessKey",
1496
- tabindex: "tabIndex",
1497
- enctype: "encType",
1498
- maxlength: "maxLength",
1499
- readonly: "readOnly",
1500
- longdesc: "longDesc"
1501
- };
1502
-
1503
- Element._attributeTranslations.values = {
1504
- _getAttr: function(element, attribute) {
1505
- return element.getAttribute(attribute, 2);
1506
2002
  },
1507
2003
 
1508
- _flag: function(element, attribute) {
1509
- return $(element).hasAttribute(attribute) ? attribute : null;
2004
+ cumulativeOffset: function(element) {
2005
+ var valueT = 0, valueL = 0;
2006
+ do {
2007
+ valueT += element.offsetTop || 0;
2008
+ valueL += element.offsetLeft || 0;
2009
+ element = element.offsetParent;
2010
+ } while (element);
2011
+ return Element._returnOffset(valueL, valueT);
1510
2012
  },
1511
2013
 
1512
- style: function(element) {
1513
- return element.style.cssText.toLowerCase();
2014
+ positionedOffset: function(element) {
2015
+ var valueT = 0, valueL = 0;
2016
+ do {
2017
+ valueT += element.offsetTop || 0;
2018
+ valueL += element.offsetLeft || 0;
2019
+ element = element.offsetParent;
2020
+ if (element) {
2021
+ if (element.tagName == 'BODY') break;
2022
+ var p = Element.getStyle(element, 'position');
2023
+ if (p !== 'static') break;
2024
+ }
2025
+ } while (element);
2026
+ return Element._returnOffset(valueL, valueT);
1514
2027
  },
1515
2028
 
1516
- title: function(element) {
1517
- var node = element.getAttributeNode('title');
1518
- return node.specified ? node.nodeValue : null;
1519
- }
1520
- };
1521
-
1522
- Object.extend(Element._attributeTranslations.values, {
1523
- href: Element._attributeTranslations.values._getAttr,
1524
- src: Element._attributeTranslations.values._getAttr,
1525
- disabled: Element._attributeTranslations.values._flag,
1526
- checked: Element._attributeTranslations.values._flag,
1527
- readonly: Element._attributeTranslations.values._flag,
1528
- multiple: Element._attributeTranslations.values._flag
1529
- });
1530
-
1531
- Element.Methods.Simulated = {
1532
- hasAttribute: function(element, attribute) {
1533
- var t = Element._attributeTranslations;
1534
- attribute = t.names[attribute] || attribute;
1535
- return $(element).getAttributeNode(attribute).specified;
1536
- }
1537
- };
1538
-
1539
- // IE is missing .innerHTML support for TABLE-related elements
1540
- if (document.all && !window.opera){
1541
- Element.Methods.update = function(element, html) {
2029
+ absolutize: function(element) {
1542
2030
  element = $(element);
1543
- html = typeof html == 'undefined' ? '' : html.toString();
1544
- var tagName = element.tagName.toUpperCase();
1545
- if (['THEAD','TBODY','TR','TD'].include(tagName)) {
1546
- var div = document.createElement('div');
1547
- switch (tagName) {
1548
- case 'THEAD':
1549
- case 'TBODY':
1550
- div.innerHTML = '<table><tbody>' + html.stripScripts() + '</tbody></table>';
1551
- depth = 2;
1552
- break;
1553
- case 'TR':
1554
- div.innerHTML = '<table><tbody><tr>' + html.stripScripts() + '</tr></tbody></table>';
1555
- depth = 3;
1556
- break;
1557
- case 'TD':
1558
- div.innerHTML = '<table><tbody><tr><td>' + html.stripScripts() + '</td></tr></tbody></table>';
1559
- depth = 4;
1560
- }
1561
- $A(element.childNodes).each(function(node){
1562
- element.removeChild(node)
1563
- });
1564
- depth.times(function(){ div = div.firstChild });
1565
-
1566
- $A(div.childNodes).each(
1567
- function(node){ element.appendChild(node) });
1568
- } else {
1569
- element.innerHTML = html.stripScripts();
1570
- }
1571
- setTimeout(function() {html.evalScripts()}, 10);
1572
- return element;
1573
- }
1574
- };
1575
-
1576
- Object.extend(Element, Element.Methods);
1577
-
1578
- var _nativeExtensions = false;
1579
-
1580
- if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1581
- ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
1582
- var className = 'HTML' + tag + 'Element';
1583
- if(window[className]) return;
1584
- var klass = window[className] = {};
1585
- klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
1586
- });
1587
-
1588
- Element.addMethods = function(methods) {
1589
- Object.extend(Element.Methods, methods || {});
2031
+ if (element.getStyle('position') == 'absolute') return;
2032
+ // Position.prepare(); // To be done manually by Scripty when it needs it.
1590
2033
 
1591
- function copy(methods, destination, onlyIfAbsent) {
1592
- onlyIfAbsent = onlyIfAbsent || false;
1593
- var cache = Element.extend.cache;
1594
- for (var property in methods) {
1595
- var value = methods[property];
1596
- if (!onlyIfAbsent || !(property in destination))
1597
- destination[property] = cache.findOrStore(value);
1598
- }
1599
- }
1600
-
1601
- if (typeof HTMLElement != 'undefined') {
1602
- copy(Element.Methods, HTMLElement.prototype);
1603
- copy(Element.Methods.Simulated, HTMLElement.prototype, true);
1604
- copy(Form.Methods, HTMLFormElement.prototype);
1605
- [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
1606
- copy(Form.Element.Methods, klass.prototype);
1607
- });
1608
- _nativeExtensions = true;
1609
- }
1610
- }
2034
+ var offsets = element.positionedOffset();
2035
+ var top = offsets[1];
2036
+ var left = offsets[0];
2037
+ var width = element.clientWidth;
2038
+ var height = element.clientHeight;
1611
2039
 
1612
- var Toggle = new Object();
1613
- Toggle.display = Element.toggle;
2040
+ element._originalLeft = left - parseFloat(element.style.left || 0);
2041
+ element._originalTop = top - parseFloat(element.style.top || 0);
2042
+ element._originalWidth = element.style.width;
2043
+ element._originalHeight = element.style.height;
1614
2044
 
1615
- /*--------------------------------------------------------------------------*/
2045
+ element.style.position = 'absolute';
2046
+ element.style.top = top + 'px';
2047
+ element.style.left = left + 'px';
2048
+ element.style.width = width + 'px';
2049
+ element.style.height = height + 'px';
2050
+ return element;
2051
+ },
1616
2052
 
1617
- Abstract.Insertion = function(adjacency) {
1618
- this.adjacency = adjacency;
1619
- }
2053
+ relativize: function(element) {
2054
+ element = $(element);
2055
+ if (element.getStyle('position') == 'relative') return;
2056
+ // Position.prepare(); // To be done manually by Scripty when it needs it.
1620
2057
 
1621
- Abstract.Insertion.prototype = {
1622
- initialize: function(element, content) {
1623
- this.element = $(element);
1624
- this.content = content.stripScripts();
2058
+ element.style.position = 'relative';
2059
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
2060
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
1625
2061
 
1626
- if (this.adjacency && this.element.insertAdjacentHTML) {
1627
- try {
1628
- this.element.insertAdjacentHTML(this.adjacency, this.content);
1629
- } catch (e) {
1630
- var tagName = this.element.tagName.toUpperCase();
1631
- if (['TBODY', 'TR'].include(tagName)) {
1632
- this.insertContent(this.contentFromAnonymousTable());
1633
- } else {
1634
- throw e;
1635
- }
1636
- }
1637
- } else {
1638
- this.range = this.element.ownerDocument.createRange();
1639
- if (this.initializeRange) this.initializeRange();
1640
- this.insertContent([this.range.createContextualFragment(this.content)]);
1641
- }
2062
+ element.style.top = top + 'px';
2063
+ element.style.left = left + 'px';
2064
+ element.style.height = element._originalHeight;
2065
+ element.style.width = element._originalWidth;
2066
+ return element;
2067
+ },
1642
2068
 
1643
- setTimeout(function() {content.evalScripts()}, 10);
2069
+ cumulativeScrollOffset: function(element) {
2070
+ var valueT = 0, valueL = 0;
2071
+ do {
2072
+ valueT += element.scrollTop || 0;
2073
+ valueL += element.scrollLeft || 0;
2074
+ element = element.parentNode;
2075
+ } while (element);
2076
+ return Element._returnOffset(valueL, valueT);
1644
2077
  },
1645
2078
 
1646
- contentFromAnonymousTable: function() {
1647
- var div = document.createElement('div');
1648
- div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
1649
- return $A(div.childNodes[0].childNodes[0].childNodes);
1650
- }
1651
- }
2079
+ getOffsetParent: function(element) {
2080
+ if (element.offsetParent) return $(element.offsetParent);
2081
+ if (element == document.body) return $(element);
1652
2082
 
1653
- var Insertion = new Object();
2083
+ while ((element = element.parentNode) && element != document.body)
2084
+ if (Element.getStyle(element, 'position') != 'static')
2085
+ return $(element);
1654
2086
 
1655
- Insertion.Before = Class.create();
1656
- Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
1657
- initializeRange: function() {
1658
- this.range.setStartBefore(this.element);
2087
+ return $(document.body);
1659
2088
  },
1660
2089
 
1661
- insertContent: function(fragments) {
1662
- fragments.each((function(fragment) {
1663
- this.element.parentNode.insertBefore(fragment, this.element);
1664
- }).bind(this));
1665
- }
1666
- });
2090
+ viewportOffset: function(forElement) {
2091
+ var valueT = 0, valueL = 0;
1667
2092
 
1668
- Insertion.Top = Class.create();
1669
- Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
1670
- initializeRange: function() {
1671
- this.range.selectNodeContents(this.element);
1672
- this.range.collapse(true);
1673
- },
2093
+ var element = forElement;
2094
+ do {
2095
+ valueT += element.offsetTop || 0;
2096
+ valueL += element.offsetLeft || 0;
1674
2097
 
1675
- insertContent: function(fragments) {
1676
- fragments.reverse(false).each((function(fragment) {
1677
- this.element.insertBefore(fragment, this.element.firstChild);
1678
- }).bind(this));
1679
- }
1680
- });
2098
+ // Safari fix
2099
+ if (element.offsetParent == document.body &&
2100
+ Element.getStyle(element, 'position') == 'absolute') break;
1681
2101
 
1682
- Insertion.Bottom = Class.create();
1683
- Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
1684
- initializeRange: function() {
1685
- this.range.selectNodeContents(this.element);
1686
- this.range.collapse(this.element);
1687
- },
2102
+ } while (element = element.offsetParent);
1688
2103
 
1689
- insertContent: function(fragments) {
1690
- fragments.each((function(fragment) {
1691
- this.element.appendChild(fragment);
1692
- }).bind(this));
1693
- }
1694
- });
2104
+ element = forElement;
2105
+ do {
2106
+ if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
2107
+ valueT -= element.scrollTop || 0;
2108
+ valueL -= element.scrollLeft || 0;
2109
+ }
2110
+ } while (element = element.parentNode);
1695
2111
 
1696
- Insertion.After = Class.create();
1697
- Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
1698
- initializeRange: function() {
1699
- this.range.setStartAfter(this.element);
2112
+ return Element._returnOffset(valueL, valueT);
1700
2113
  },
1701
2114
 
1702
- insertContent: function(fragments) {
1703
- fragments.each((function(fragment) {
1704
- this.element.parentNode.insertBefore(fragment,
1705
- this.element.nextSibling);
1706
- }).bind(this));
1707
- }
2115
+ clonePosition: function(element, source) {
2116
+ var options = Object.extend({
2117
+ setLeft: true,
2118
+ setTop: true,
2119
+ setWidth: true,
2120
+ setHeight: true,
2121
+ offsetTop: 0,
2122
+ offsetLeft: 0
2123
+ }, arguments[2] || { });
2124
+
2125
+ // find page position of source
2126
+ source = $(source);
2127
+ var p = source.viewportOffset();
2128
+
2129
+ // find coordinate system to use
2130
+ element = $(element);
2131
+ var delta = [0, 0];
2132
+ var parent = null;
2133
+ // delta [0,0] will do fine with position: fixed elements,
2134
+ // position:absolute needs offsetParent deltas
2135
+ if (Element.getStyle(element, 'position') == 'absolute') {
2136
+ parent = element.getOffsetParent();
2137
+ delta = parent.viewportOffset();
2138
+ }
2139
+
2140
+ // correct by body offsets (fixes Safari)
2141
+ if (parent == document.body) {
2142
+ delta[0] -= document.body.offsetLeft;
2143
+ delta[1] -= document.body.offsetTop;
2144
+ }
2145
+
2146
+ // set position
2147
+ if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
2148
+ if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
2149
+ if (options.setWidth) element.style.width = source.offsetWidth + 'px';
2150
+ if (options.setHeight) element.style.height = source.offsetHeight + 'px';
2151
+ return element;
2152
+ }
2153
+ };
2154
+
2155
+ Element.Methods.identify.counter = 1;
2156
+
2157
+ Object.extend(Element.Methods, {
2158
+ getElementsBySelector: Element.Methods.select,
2159
+ childElements: Element.Methods.immediateDescendants
1708
2160
  });
1709
2161
 
1710
- /*--------------------------------------------------------------------------*/
2162
+ Element._attributeTranslations = {
2163
+ write: {
2164
+ names: {
2165
+ className: 'class',
2166
+ htmlFor: 'for'
2167
+ },
2168
+ values: { }
2169
+ }
2170
+ };
1711
2171
 
1712
- Element.ClassNames = Class.create();
1713
- Element.ClassNames.prototype = {
1714
- initialize: function(element) {
1715
- this.element = $(element);
1716
- },
2172
+ if (Prototype.Browser.Opera) {
2173
+ Element.Methods.getStyle = Element.Methods.getStyle.wrap(
2174
+ function(proceed, element, style) {
2175
+ switch (style) {
2176
+ case 'left': case 'top': case 'right': case 'bottom':
2177
+ if (proceed(element, 'position') === 'static') return null;
2178
+ case 'height': case 'width':
2179
+ // returns '0px' for hidden elements; we want it to return null
2180
+ if (!Element.visible(element)) return null;
2181
+
2182
+ // returns the border-box dimensions rather than the content-box
2183
+ // dimensions, so we subtract padding and borders from the value
2184
+ var dim = parseInt(proceed(element, style), 10);
2185
+
2186
+ if (dim !== element['offset' + style.capitalize()])
2187
+ return dim + 'px';
2188
+
2189
+ var properties;
2190
+ if (style === 'height') {
2191
+ properties = ['border-top-width', 'padding-top',
2192
+ 'padding-bottom', 'border-bottom-width'];
2193
+ }
2194
+ else {
2195
+ properties = ['border-left-width', 'padding-left',
2196
+ 'padding-right', 'border-right-width'];
2197
+ }
2198
+ return properties.inject(dim, function(memo, property) {
2199
+ var val = proceed(element, property);
2200
+ return val === null ? memo : memo - parseInt(val, 10);
2201
+ }) + 'px';
2202
+ default: return proceed(element, style);
2203
+ }
2204
+ }
2205
+ );
1717
2206
 
1718
- _each: function(iterator) {
1719
- this.element.className.split(/\s+/).select(function(name) {
1720
- return name.length > 0;
1721
- })._each(iterator);
2207
+ Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
2208
+ function(proceed, element, attribute) {
2209
+ if (attribute === 'title') return element.title;
2210
+ return proceed(element, attribute);
2211
+ }
2212
+ );
2213
+ }
2214
+
2215
+ else if (Prototype.Browser.IE) {
2216
+ // IE doesn't report offsets correctly for static elements, so we change them
2217
+ // to "relative" to get the values, then change them back.
2218
+ Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
2219
+ function(proceed, element) {
2220
+ element = $(element);
2221
+ var position = element.getStyle('position');
2222
+ if (position !== 'static') return proceed(element);
2223
+ element.setStyle({ position: 'relative' });
2224
+ var value = proceed(element);
2225
+ element.setStyle({ position: position });
2226
+ return value;
2227
+ }
2228
+ );
2229
+
2230
+ $w('positionedOffset viewportOffset').each(function(method) {
2231
+ Element.Methods[method] = Element.Methods[method].wrap(
2232
+ function(proceed, element) {
2233
+ element = $(element);
2234
+ var position = element.getStyle('position');
2235
+ if (position !== 'static') return proceed(element);
2236
+ // Trigger hasLayout on the offset parent so that IE6 reports
2237
+ // accurate offsetTop and offsetLeft values for position: fixed.
2238
+ var offsetParent = element.getOffsetParent();
2239
+ if (offsetParent && offsetParent.getStyle('position') === 'fixed')
2240
+ offsetParent.setStyle({ zoom: 1 });
2241
+ element.setStyle({ position: 'relative' });
2242
+ var value = proceed(element);
2243
+ element.setStyle({ position: position });
2244
+ return value;
2245
+ }
2246
+ );
2247
+ });
2248
+
2249
+ Element.Methods.getStyle = function(element, style) {
2250
+ element = $(element);
2251
+ style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
2252
+ var value = element.style[style];
2253
+ if (!value && element.currentStyle) value = element.currentStyle[style];
2254
+
2255
+ if (style == 'opacity') {
2256
+ if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
2257
+ if (value[1]) return parseFloat(value[1]) / 100;
2258
+ return 1.0;
2259
+ }
2260
+
2261
+ if (value == 'auto') {
2262
+ if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
2263
+ return element['offset' + style.capitalize()] + 'px';
2264
+ return null;
2265
+ }
2266
+ return value;
2267
+ };
2268
+
2269
+ Element.Methods.setOpacity = function(element, value) {
2270
+ function stripAlpha(filter){
2271
+ return filter.replace(/alpha\([^\)]*\)/gi,'');
2272
+ }
2273
+ element = $(element);
2274
+ var currentStyle = element.currentStyle;
2275
+ if ((currentStyle && !currentStyle.hasLayout) ||
2276
+ (!currentStyle && element.style.zoom == 'normal'))
2277
+ element.style.zoom = 1;
2278
+
2279
+ var filter = element.getStyle('filter'), style = element.style;
2280
+ if (value == 1 || value === '') {
2281
+ (filter = stripAlpha(filter)) ?
2282
+ style.filter = filter : style.removeAttribute('filter');
2283
+ return element;
2284
+ } else if (value < 0.00001) value = 0;
2285
+ style.filter = stripAlpha(filter) +
2286
+ 'alpha(opacity=' + (value * 100) + ')';
2287
+ return element;
2288
+ };
2289
+
2290
+ Element._attributeTranslations = {
2291
+ read: {
2292
+ names: {
2293
+ 'class': 'className',
2294
+ 'for': 'htmlFor'
2295
+ },
2296
+ values: {
2297
+ _getAttr: function(element, attribute) {
2298
+ return element.getAttribute(attribute, 2);
2299
+ },
2300
+ _getAttrNode: function(element, attribute) {
2301
+ var node = element.getAttributeNode(attribute);
2302
+ return node ? node.value : "";
2303
+ },
2304
+ _getEv: function(element, attribute) {
2305
+ attribute = element.getAttribute(attribute);
2306
+ return attribute ? attribute.toString().slice(23, -2) : null;
2307
+ },
2308
+ _flag: function(element, attribute) {
2309
+ return $(element).hasAttribute(attribute) ? attribute : null;
2310
+ },
2311
+ style: function(element) {
2312
+ return element.style.cssText.toLowerCase();
2313
+ },
2314
+ title: function(element) {
2315
+ return element.title;
2316
+ }
2317
+ }
2318
+ }
2319
+ };
2320
+
2321
+ Element._attributeTranslations.write = {
2322
+ names: Object.extend({
2323
+ cellpadding: 'cellPadding',
2324
+ cellspacing: 'cellSpacing'
2325
+ }, Element._attributeTranslations.read.names),
2326
+ values: {
2327
+ checked: function(element, value) {
2328
+ element.checked = !!value;
2329
+ },
2330
+
2331
+ style: function(element, value) {
2332
+ element.style.cssText = value ? value : '';
2333
+ }
2334
+ }
2335
+ };
2336
+
2337
+ Element._attributeTranslations.has = {};
2338
+
2339
+ $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
2340
+ 'encType maxLength readOnly longDesc').each(function(attr) {
2341
+ Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
2342
+ Element._attributeTranslations.has[attr.toLowerCase()] = attr;
2343
+ });
2344
+
2345
+ (function(v) {
2346
+ Object.extend(v, {
2347
+ href: v._getAttr,
2348
+ src: v._getAttr,
2349
+ type: v._getAttr,
2350
+ action: v._getAttrNode,
2351
+ disabled: v._flag,
2352
+ checked: v._flag,
2353
+ readonly: v._flag,
2354
+ multiple: v._flag,
2355
+ onload: v._getEv,
2356
+ onunload: v._getEv,
2357
+ onclick: v._getEv,
2358
+ ondblclick: v._getEv,
2359
+ onmousedown: v._getEv,
2360
+ onmouseup: v._getEv,
2361
+ onmouseover: v._getEv,
2362
+ onmousemove: v._getEv,
2363
+ onmouseout: v._getEv,
2364
+ onfocus: v._getEv,
2365
+ onblur: v._getEv,
2366
+ onkeypress: v._getEv,
2367
+ onkeydown: v._getEv,
2368
+ onkeyup: v._getEv,
2369
+ onsubmit: v._getEv,
2370
+ onreset: v._getEv,
2371
+ onselect: v._getEv,
2372
+ onchange: v._getEv
2373
+ });
2374
+ })(Element._attributeTranslations.read.values);
2375
+ }
2376
+
2377
+ else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
2378
+ Element.Methods.setOpacity = function(element, value) {
2379
+ element = $(element);
2380
+ element.style.opacity = (value == 1) ? 0.999999 :
2381
+ (value === '') ? '' : (value < 0.00001) ? 0 : value;
2382
+ return element;
2383
+ };
2384
+ }
2385
+
2386
+ else if (Prototype.Browser.WebKit) {
2387
+ Element.Methods.setOpacity = function(element, value) {
2388
+ element = $(element);
2389
+ element.style.opacity = (value == 1 || value === '') ? '' :
2390
+ (value < 0.00001) ? 0 : value;
2391
+
2392
+ if (value == 1)
2393
+ if(element.tagName == 'IMG' && element.width) {
2394
+ element.width++; element.width--;
2395
+ } else try {
2396
+ var n = document.createTextNode(' ');
2397
+ element.appendChild(n);
2398
+ element.removeChild(n);
2399
+ } catch (e) { }
2400
+
2401
+ return element;
2402
+ };
2403
+
2404
+ // Safari returns margins on body which is incorrect if the child is absolutely
2405
+ // positioned. For performance reasons, redefine Element#cumulativeOffset for
2406
+ // KHTML/WebKit only.
2407
+ Element.Methods.cumulativeOffset = function(element) {
2408
+ var valueT = 0, valueL = 0;
2409
+ do {
2410
+ valueT += element.offsetTop || 0;
2411
+ valueL += element.offsetLeft || 0;
2412
+ if (element.offsetParent == document.body)
2413
+ if (Element.getStyle(element, 'position') == 'absolute') break;
2414
+
2415
+ element = element.offsetParent;
2416
+ } while (element);
2417
+
2418
+ return Element._returnOffset(valueL, valueT);
2419
+ };
2420
+ }
2421
+
2422
+ if (Prototype.Browser.IE || Prototype.Browser.Opera) {
2423
+ // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
2424
+ Element.Methods.update = function(element, content) {
2425
+ element = $(element);
2426
+
2427
+ if (content && content.toElement) content = content.toElement();
2428
+ if (Object.isElement(content)) return element.update().insert(content);
2429
+
2430
+ content = Object.toHTML(content);
2431
+ var tagName = element.tagName.toUpperCase();
2432
+
2433
+ if (tagName in Element._insertionTranslations.tags) {
2434
+ $A(element.childNodes).each(function(node) { element.removeChild(node) });
2435
+ Element._getContentFromAnonymousElement(tagName, content.stripScripts())
2436
+ .each(function(node) { element.appendChild(node) });
2437
+ }
2438
+ else element.innerHTML = content.stripScripts();
2439
+
2440
+ content.evalScripts.bind(content).defer();
2441
+ return element;
2442
+ };
2443
+ }
2444
+
2445
+ if ('outerHTML' in document.createElement('div')) {
2446
+ Element.Methods.replace = function(element, content) {
2447
+ element = $(element);
2448
+
2449
+ if (content && content.toElement) content = content.toElement();
2450
+ if (Object.isElement(content)) {
2451
+ element.parentNode.replaceChild(content, element);
2452
+ return element;
2453
+ }
2454
+
2455
+ content = Object.toHTML(content);
2456
+ var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
2457
+
2458
+ if (Element._insertionTranslations.tags[tagName]) {
2459
+ var nextSibling = element.next();
2460
+ var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2461
+ parent.removeChild(element);
2462
+ if (nextSibling)
2463
+ fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
2464
+ else
2465
+ fragments.each(function(node) { parent.appendChild(node) });
2466
+ }
2467
+ else element.outerHTML = content.stripScripts();
2468
+
2469
+ content.evalScripts.bind(content).defer();
2470
+ return element;
2471
+ };
2472
+ }
2473
+
2474
+ Element._returnOffset = function(l, t) {
2475
+ var result = [l, t];
2476
+ result.left = l;
2477
+ result.top = t;
2478
+ return result;
2479
+ };
2480
+
2481
+ Element._getContentFromAnonymousElement = function(tagName, html) {
2482
+ var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
2483
+ if (t) {
2484
+ div.innerHTML = t[0] + html + t[1];
2485
+ t[2].times(function() { div = div.firstChild });
2486
+ } else div.innerHTML = html;
2487
+ return $A(div.childNodes);
2488
+ };
2489
+
2490
+ Element._insertionTranslations = {
2491
+ before: function(element, node) {
2492
+ element.parentNode.insertBefore(node, element);
2493
+ },
2494
+ top: function(element, node) {
2495
+ element.insertBefore(node, element.firstChild);
2496
+ },
2497
+ bottom: function(element, node) {
2498
+ element.appendChild(node);
2499
+ },
2500
+ after: function(element, node) {
2501
+ element.parentNode.insertBefore(node, element.nextSibling);
1722
2502
  },
2503
+ tags: {
2504
+ TABLE: ['<table>', '</table>', 1],
2505
+ TBODY: ['<table><tbody>', '</tbody></table>', 2],
2506
+ TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
2507
+ TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
2508
+ SELECT: ['<select>', '</select>', 1]
2509
+ }
2510
+ };
1723
2511
 
1724
- set: function(className) {
1725
- this.element.className = className;
2512
+ (function() {
2513
+ Object.extend(this.tags, {
2514
+ THEAD: this.tags.TBODY,
2515
+ TFOOT: this.tags.TBODY,
2516
+ TH: this.tags.TD
2517
+ });
2518
+ }).call(Element._insertionTranslations);
2519
+
2520
+ Element.Methods.Simulated = {
2521
+ hasAttribute: function(element, attribute) {
2522
+ attribute = Element._attributeTranslations.has[attribute] || attribute;
2523
+ var node = $(element).getAttributeNode(attribute);
2524
+ return node && node.specified;
2525
+ }
2526
+ };
2527
+
2528
+ Element.Methods.ByTag = { };
2529
+
2530
+ Object.extend(Element, Element.Methods);
2531
+
2532
+ if (!Prototype.BrowserFeatures.ElementExtensions &&
2533
+ document.createElement('div').__proto__) {
2534
+ window.HTMLElement = { };
2535
+ window.HTMLElement.prototype = document.createElement('div').__proto__;
2536
+ Prototype.BrowserFeatures.ElementExtensions = true;
2537
+ }
2538
+
2539
+ Element.extend = (function() {
2540
+ if (Prototype.BrowserFeatures.SpecificElementExtensions)
2541
+ return Prototype.K;
2542
+
2543
+ var Methods = { }, ByTag = Element.Methods.ByTag;
2544
+
2545
+ var extend = Object.extend(function(element) {
2546
+ if (!element || element._extendedByPrototype ||
2547
+ element.nodeType != 1 || element == window) return element;
2548
+
2549
+ var methods = Object.clone(Methods),
2550
+ tagName = element.tagName, property, value;
2551
+
2552
+ // extend methods for specific tags
2553
+ if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
2554
+
2555
+ for (property in methods) {
2556
+ value = methods[property];
2557
+ if (Object.isFunction(value) && !(property in element))
2558
+ element[property] = value.methodize();
2559
+ }
2560
+
2561
+ element._extendedByPrototype = Prototype.emptyFunction;
2562
+ return element;
2563
+
2564
+ }, {
2565
+ refresh: function() {
2566
+ // extend methods for all tags (Safari doesn't need this)
2567
+ if (!Prototype.BrowserFeatures.ElementExtensions) {
2568
+ Object.extend(Methods, Element.Methods);
2569
+ Object.extend(Methods, Element.Methods.Simulated);
2570
+ }
2571
+ }
2572
+ });
2573
+
2574
+ extend.refresh();
2575
+ return extend;
2576
+ })();
2577
+
2578
+ Element.hasAttribute = function(element, attribute) {
2579
+ if (element.hasAttribute) return element.hasAttribute(attribute);
2580
+ return Element.Methods.Simulated.hasAttribute(element, attribute);
2581
+ };
2582
+
2583
+ Element.addMethods = function(methods) {
2584
+ var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
2585
+
2586
+ if (!methods) {
2587
+ Object.extend(Form, Form.Methods);
2588
+ Object.extend(Form.Element, Form.Element.Methods);
2589
+ Object.extend(Element.Methods.ByTag, {
2590
+ "FORM": Object.clone(Form.Methods),
2591
+ "INPUT": Object.clone(Form.Element.Methods),
2592
+ "SELECT": Object.clone(Form.Element.Methods),
2593
+ "TEXTAREA": Object.clone(Form.Element.Methods)
2594
+ });
2595
+ }
2596
+
2597
+ if (arguments.length == 2) {
2598
+ var tagName = methods;
2599
+ methods = arguments[1];
2600
+ }
2601
+
2602
+ if (!tagName) Object.extend(Element.Methods, methods || { });
2603
+ else {
2604
+ if (Object.isArray(tagName)) tagName.each(extend);
2605
+ else extend(tagName);
2606
+ }
2607
+
2608
+ function extend(tagName) {
2609
+ tagName = tagName.toUpperCase();
2610
+ if (!Element.Methods.ByTag[tagName])
2611
+ Element.Methods.ByTag[tagName] = { };
2612
+ Object.extend(Element.Methods.ByTag[tagName], methods);
2613
+ }
2614
+
2615
+ function copy(methods, destination, onlyIfAbsent) {
2616
+ onlyIfAbsent = onlyIfAbsent || false;
2617
+ for (var property in methods) {
2618
+ var value = methods[property];
2619
+ if (!Object.isFunction(value)) continue;
2620
+ if (!onlyIfAbsent || !(property in destination))
2621
+ destination[property] = value.methodize();
2622
+ }
2623
+ }
2624
+
2625
+ function findDOMClass(tagName) {
2626
+ var klass;
2627
+ var trans = {
2628
+ "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
2629
+ "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
2630
+ "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
2631
+ "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
2632
+ "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
2633
+ "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
2634
+ "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
2635
+ "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
2636
+ "FrameSet", "IFRAME": "IFrame"
2637
+ };
2638
+ if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
2639
+ if (window[klass]) return window[klass];
2640
+ klass = 'HTML' + tagName + 'Element';
2641
+ if (window[klass]) return window[klass];
2642
+ klass = 'HTML' + tagName.capitalize() + 'Element';
2643
+ if (window[klass]) return window[klass];
2644
+
2645
+ window[klass] = { };
2646
+ window[klass].prototype = document.createElement(tagName).__proto__;
2647
+ return window[klass];
2648
+ }
2649
+
2650
+ if (F.ElementExtensions) {
2651
+ copy(Element.Methods, HTMLElement.prototype);
2652
+ copy(Element.Methods.Simulated, HTMLElement.prototype, true);
2653
+ }
2654
+
2655
+ if (F.SpecificElementExtensions) {
2656
+ for (var tag in Element.Methods.ByTag) {
2657
+ var klass = findDOMClass(tag);
2658
+ if (Object.isUndefined(klass)) continue;
2659
+ copy(T[tag], klass.prototype);
2660
+ }
2661
+ }
2662
+
2663
+ Object.extend(Element, Element.Methods);
2664
+ delete Element.ByTag;
2665
+
2666
+ if (Element.extend.refresh) Element.extend.refresh();
2667
+ Element.cache = { };
2668
+ };
2669
+
2670
+ document.viewport = {
2671
+ getDimensions: function() {
2672
+ var dimensions = { };
2673
+ var B = Prototype.Browser;
2674
+ $w('width height').each(function(d) {
2675
+ var D = d.capitalize();
2676
+ dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
2677
+ (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
2678
+ });
2679
+ return dimensions;
1726
2680
  },
1727
2681
 
1728
- add: function(classNameToAdd) {
1729
- if (this.include(classNameToAdd)) return;
1730
- this.set($A(this).concat(classNameToAdd).join(' '));
2682
+ getWidth: function() {
2683
+ return this.getDimensions().width;
1731
2684
  },
1732
2685
 
1733
- remove: function(classNameToRemove) {
1734
- if (!this.include(classNameToRemove)) return;
1735
- this.set($A(this).without(classNameToRemove).join(' '));
2686
+ getHeight: function() {
2687
+ return this.getDimensions().height;
1736
2688
  },
1737
2689
 
1738
- toString: function() {
1739
- return $A(this).join(' ');
2690
+ getScrollOffsets: function() {
2691
+ return Element._returnOffset(
2692
+ window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
2693
+ window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
1740
2694
  }
1741
2695
  };
2696
+ /* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
2697
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
2698
+ * license. Please see http://www.yui-ext.com/ for more information. */
1742
2699
 
1743
- Object.extend(Element.ClassNames.prototype, Enumerable);
1744
- var Selector = Class.create();
1745
- Selector.prototype = {
2700
+ var Selector = Class.create({
1746
2701
  initialize: function(expression) {
1747
- this.params = {classNames: []};
1748
- this.expression = expression.toString().strip();
1749
- this.parseExpression();
2702
+ this.expression = expression.strip();
1750
2703
  this.compileMatcher();
1751
2704
  },
1752
2705
 
1753
- parseExpression: function() {
1754
- function abort(message) { throw 'Parse error in selector: ' + message; }
2706
+ shouldUseXPath: function() {
2707
+ if (!Prototype.BrowserFeatures.XPath) return false;
1755
2708
 
1756
- if (this.expression == '') abort('empty expression');
2709
+ var e = this.expression;
1757
2710
 
1758
- var params = this.params, expr = this.expression, match, modifier, clause, rest;
1759
- while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
1760
- params.attributes = params.attributes || [];
1761
- params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
1762
- expr = match[1];
1763
- }
2711
+ // Safari 3 chokes on :*-of-type and :empty
2712
+ if (Prototype.Browser.WebKit &&
2713
+ (e.include("-of-type") || e.include(":empty")))
2714
+ return false;
1764
2715
 
1765
- if (expr == '*') return this.params.wildcard = true;
2716
+ // XPath can't do namespaced attributes, nor can it read
2717
+ // the "checked" property from DOM nodes
2718
+ if ((/(\[[\w-]*?:|:checked)/).test(this.expression))
2719
+ return false;
1766
2720
 
1767
- while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
1768
- modifier = match[1], clause = match[2], rest = match[3];
1769
- switch (modifier) {
1770
- case '#': params.id = clause; break;
1771
- case '.': params.classNames.push(clause); break;
1772
- case '':
1773
- case undefined: params.tagName = clause.toUpperCase(); break;
1774
- default: abort(expr.inspect());
2721
+ return true;
2722
+ },
2723
+
2724
+ compileMatcher: function() {
2725
+ if (this.shouldUseXPath())
2726
+ return this.compileXPathMatcher();
2727
+
2728
+ var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
2729
+ c = Selector.criteria, le, p, m;
2730
+
2731
+ if (Selector._cache[e]) {
2732
+ this.matcher = Selector._cache[e];
2733
+ return;
2734
+ }
2735
+
2736
+ this.matcher = ["this.matcher = function(root) {",
2737
+ "var r = root, h = Selector.handlers, c = false, n;"];
2738
+
2739
+ while (e && le != e && (/\S/).test(e)) {
2740
+ le = e;
2741
+ for (var i in ps) {
2742
+ p = ps[i];
2743
+ if (m = e.match(p)) {
2744
+ this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
2745
+ new Template(c[i]).evaluate(m));
2746
+ e = e.replace(m[0], '');
2747
+ break;
2748
+ }
1775
2749
  }
1776
- expr = rest;
1777
2750
  }
1778
2751
 
1779
- if (expr.length > 0) abort(expr.inspect());
2752
+ this.matcher.push("return h.unique(n);\n}");
2753
+ eval(this.matcher.join('\n'));
2754
+ Selector._cache[this.expression] = this.matcher;
1780
2755
  },
1781
2756
 
1782
- buildMatchExpression: function() {
1783
- var params = this.params, conditions = [], clause;
2757
+ compileXPathMatcher: function() {
2758
+ var e = this.expression, ps = Selector.patterns,
2759
+ x = Selector.xpath, le, m;
1784
2760
 
1785
- if (params.wildcard)
1786
- conditions.push('true');
1787
- if (clause = params.id)
1788
- conditions.push('element.readAttribute("id") == ' + clause.inspect());
1789
- if (clause = params.tagName)
1790
- conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
1791
- if ((clause = params.classNames).length > 0)
1792
- for (var i = 0, length = clause.length; i < length; i++)
1793
- conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
1794
- if (clause = params.attributes) {
1795
- clause.each(function(attribute) {
1796
- var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
1797
- var splitValueBy = function(delimiter) {
1798
- return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
1799
- }
2761
+ if (Selector._cache[e]) {
2762
+ this.xpath = Selector._cache[e]; return;
2763
+ }
1800
2764
 
1801
- switch (attribute.operator) {
1802
- case '=': conditions.push(value + ' == ' + attribute.value.inspect()); break;
1803
- case '~=': conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
1804
- case '|=': conditions.push(
1805
- splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
1806
- ); break;
1807
- case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break;
1808
- case '':
1809
- case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
1810
- default: throw 'Unknown operator ' + attribute.operator + ' in selector';
2765
+ this.matcher = ['.//*'];
2766
+ while (e && le != e && (/\S/).test(e)) {
2767
+ le = e;
2768
+ for (var i in ps) {
2769
+ if (m = e.match(ps[i])) {
2770
+ this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
2771
+ new Template(x[i]).evaluate(m));
2772
+ e = e.replace(m[0], '');
2773
+ break;
1811
2774
  }
1812
- });
2775
+ }
1813
2776
  }
1814
2777
 
1815
- return conditions.join(' && ');
2778
+ this.xpath = this.matcher.join('');
2779
+ Selector._cache[this.expression] = this.xpath;
1816
2780
  },
1817
2781
 
1818
- compileMatcher: function() {
1819
- this.match = new Function('element', 'if (!element.tagName) return false; \
1820
- element = $(element); \
1821
- return ' + this.buildMatchExpression());
2782
+ findElements: function(root) {
2783
+ root = root || document;
2784
+ if (this.xpath) return document._getElementsByXPath(this.xpath, root);
2785
+ return this.matcher(root);
1822
2786
  },
1823
2787
 
1824
- findElements: function(scope) {
1825
- var element;
2788
+ match: function(element) {
2789
+ this.tokens = [];
1826
2790
 
1827
- if (element = $(this.params.id))
1828
- if (this.match(element))
1829
- if (!scope || Element.childOf(element, scope))
1830
- return [element];
2791
+ var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
2792
+ var le, p, m;
1831
2793
 
1832
- scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
2794
+ while (e && le !== e && (/\S/).test(e)) {
2795
+ le = e;
2796
+ for (var i in ps) {
2797
+ p = ps[i];
2798
+ if (m = e.match(p)) {
2799
+ // use the Selector.assertions methods unless the selector
2800
+ // is too complex.
2801
+ if (as[i]) {
2802
+ this.tokens.push([i, Object.clone(m)]);
2803
+ e = e.replace(m[0], '');
2804
+ } else {
2805
+ // reluctantly do a document-wide search
2806
+ // and look for a match in the array
2807
+ return this.findElements(document).include(element);
2808
+ }
2809
+ }
2810
+ }
2811
+ }
1833
2812
 
1834
- var results = [];
1835
- for (var i = 0, length = scope.length; i < length; i++)
1836
- if (this.match(element = scope[i]))
1837
- results.push(Element.extend(element));
2813
+ var match = true, name, matches;
2814
+ for (var i = 0, token; token = this.tokens[i]; i++) {
2815
+ name = token[0], matches = token[1];
2816
+ if (!Selector.assertions[name](element, matches)) {
2817
+ match = false; break;
2818
+ }
2819
+ }
1838
2820
 
1839
- return results;
2821
+ return match;
1840
2822
  },
1841
2823
 
1842
2824
  toString: function() {
1843
2825
  return this.expression;
2826
+ },
2827
+
2828
+ inspect: function() {
2829
+ return "#<Selector:" + this.expression.inspect() + ">";
1844
2830
  }
1845
- }
2831
+ });
1846
2832
 
1847
2833
  Object.extend(Selector, {
2834
+ _cache: { },
2835
+
2836
+ xpath: {
2837
+ descendant: "//*",
2838
+ child: "/*",
2839
+ adjacent: "/following-sibling::*[1]",
2840
+ laterSibling: '/following-sibling::*',
2841
+ tagName: function(m) {
2842
+ if (m[1] == '*') return '';
2843
+ return "[local-name()='" + m[1].toLowerCase() +
2844
+ "' or local-name()='" + m[1].toUpperCase() + "']";
2845
+ },
2846
+ className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
2847
+ id: "[@id='#{1}']",
2848
+ attrPresence: function(m) {
2849
+ m[1] = m[1].toLowerCase();
2850
+ return new Template("[@#{1}]").evaluate(m);
2851
+ },
2852
+ attr: function(m) {
2853
+ m[1] = m[1].toLowerCase();
2854
+ m[3] = m[5] || m[6];
2855
+ return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
2856
+ },
2857
+ pseudo: function(m) {
2858
+ var h = Selector.xpath.pseudos[m[1]];
2859
+ if (!h) return '';
2860
+ if (Object.isFunction(h)) return h(m);
2861
+ return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
2862
+ },
2863
+ operators: {
2864
+ '=': "[@#{1}='#{3}']",
2865
+ '!=': "[@#{1}!='#{3}']",
2866
+ '^=': "[starts-with(@#{1}, '#{3}')]",
2867
+ '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
2868
+ '*=': "[contains(@#{1}, '#{3}')]",
2869
+ '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
2870
+ '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
2871
+ },
2872
+ pseudos: {
2873
+ 'first-child': '[not(preceding-sibling::*)]',
2874
+ 'last-child': '[not(following-sibling::*)]',
2875
+ 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
2876
+ 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
2877
+ 'checked': "[@checked]",
2878
+ 'disabled': "[@disabled]",
2879
+ 'enabled': "[not(@disabled)]",
2880
+ 'not': function(m) {
2881
+ var e = m[6], p = Selector.patterns,
2882
+ x = Selector.xpath, le, v;
2883
+
2884
+ var exclusion = [];
2885
+ while (e && le != e && (/\S/).test(e)) {
2886
+ le = e;
2887
+ for (var i in p) {
2888
+ if (m = e.match(p[i])) {
2889
+ v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
2890
+ exclusion.push("(" + v.substring(1, v.length - 1) + ")");
2891
+ e = e.replace(m[0], '');
2892
+ break;
2893
+ }
2894
+ }
2895
+ }
2896
+ return "[not(" + exclusion.join(" and ") + ")]";
2897
+ },
2898
+ 'nth-child': function(m) {
2899
+ return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
2900
+ },
2901
+ 'nth-last-child': function(m) {
2902
+ return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
2903
+ },
2904
+ 'nth-of-type': function(m) {
2905
+ return Selector.xpath.pseudos.nth("position() ", m);
2906
+ },
2907
+ 'nth-last-of-type': function(m) {
2908
+ return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
2909
+ },
2910
+ 'first-of-type': function(m) {
2911
+ m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
2912
+ },
2913
+ 'last-of-type': function(m) {
2914
+ m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
2915
+ },
2916
+ 'only-of-type': function(m) {
2917
+ var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
2918
+ },
2919
+ nth: function(fragment, m) {
2920
+ var mm, formula = m[6], predicate;
2921
+ if (formula == 'even') formula = '2n+0';
2922
+ if (formula == 'odd') formula = '2n+1';
2923
+ if (mm = formula.match(/^(\d+)$/)) // digit only
2924
+ return '[' + fragment + "= " + mm[1] + ']';
2925
+ if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
2926
+ if (mm[1] == "-") mm[1] = -1;
2927
+ var a = mm[1] ? Number(mm[1]) : 1;
2928
+ var b = mm[2] ? Number(mm[2]) : 0;
2929
+ predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
2930
+ "((#{fragment} - #{b}) div #{a} >= 0)]";
2931
+ return new Template(predicate).evaluate({
2932
+ fragment: fragment, a: a, b: b });
2933
+ }
2934
+ }
2935
+ }
2936
+ },
2937
+
2938
+ criteria: {
2939
+ tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
2940
+ className: 'n = h.className(n, r, "#{1}", c); c = false;',
2941
+ id: 'n = h.id(n, r, "#{1}", c); c = false;',
2942
+ attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
2943
+ attr: function(m) {
2944
+ m[3] = (m[5] || m[6]);
2945
+ return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
2946
+ },
2947
+ pseudo: function(m) {
2948
+ if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
2949
+ return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
2950
+ },
2951
+ descendant: 'c = "descendant";',
2952
+ child: 'c = "child";',
2953
+ adjacent: 'c = "adjacent";',
2954
+ laterSibling: 'c = "laterSibling";'
2955
+ },
2956
+
2957
+ patterns: {
2958
+ // combinators must be listed first
2959
+ // (and descendant needs to be last combinator)
2960
+ laterSibling: /^\s*~\s*/,
2961
+ child: /^\s*>\s*/,
2962
+ adjacent: /^\s*\+\s*/,
2963
+ descendant: /^\s/,
2964
+
2965
+ // selectors follow
2966
+ tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
2967
+ id: /^#([\w\-\*]+)(\b|$)/,
2968
+ className: /^\.([\w\-\*]+)(\b|$)/,
2969
+ pseudo:
2970
+ /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
2971
+ attrPresence: /^\[([\w]+)\]/,
2972
+ attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
2973
+ },
2974
+
2975
+ // for Selector.match and Element#match
2976
+ assertions: {
2977
+ tagName: function(element, matches) {
2978
+ return matches[1].toUpperCase() == element.tagName.toUpperCase();
2979
+ },
2980
+
2981
+ className: function(element, matches) {
2982
+ return Element.hasClassName(element, matches[1]);
2983
+ },
2984
+
2985
+ id: function(element, matches) {
2986
+ return element.id === matches[1];
2987
+ },
2988
+
2989
+ attrPresence: function(element, matches) {
2990
+ return Element.hasAttribute(element, matches[1]);
2991
+ },
2992
+
2993
+ attr: function(element, matches) {
2994
+ var nodeValue = Element.readAttribute(element, matches[1]);
2995
+ return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
2996
+ }
2997
+ },
2998
+
2999
+ handlers: {
3000
+ // UTILITY FUNCTIONS
3001
+ // joins two collections
3002
+ concat: function(a, b) {
3003
+ for (var i = 0, node; node = b[i]; i++)
3004
+ a.push(node);
3005
+ return a;
3006
+ },
3007
+
3008
+ // marks an array of nodes for counting
3009
+ mark: function(nodes) {
3010
+ var _true = Prototype.emptyFunction;
3011
+ for (var i = 0, node; node = nodes[i]; i++)
3012
+ node._countedByPrototype = _true;
3013
+ return nodes;
3014
+ },
3015
+
3016
+ unmark: function(nodes) {
3017
+ for (var i = 0, node; node = nodes[i]; i++)
3018
+ node._countedByPrototype = undefined;
3019
+ return nodes;
3020
+ },
3021
+
3022
+ // mark each child node with its position (for nth calls)
3023
+ // "ofType" flag indicates whether we're indexing for nth-of-type
3024
+ // rather than nth-child
3025
+ index: function(parentNode, reverse, ofType) {
3026
+ parentNode._countedByPrototype = Prototype.emptyFunction;
3027
+ if (reverse) {
3028
+ for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
3029
+ var node = nodes[i];
3030
+ if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
3031
+ }
3032
+ } else {
3033
+ for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
3034
+ if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
3035
+ }
3036
+ },
3037
+
3038
+ // filters out duplicates and extends all nodes
3039
+ unique: function(nodes) {
3040
+ if (nodes.length == 0) return nodes;
3041
+ var results = [], n;
3042
+ for (var i = 0, l = nodes.length; i < l; i++)
3043
+ if (!(n = nodes[i])._countedByPrototype) {
3044
+ n._countedByPrototype = Prototype.emptyFunction;
3045
+ results.push(Element.extend(n));
3046
+ }
3047
+ return Selector.handlers.unmark(results);
3048
+ },
3049
+
3050
+ // COMBINATOR FUNCTIONS
3051
+ descendant: function(nodes) {
3052
+ var h = Selector.handlers;
3053
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
3054
+ h.concat(results, node.getElementsByTagName('*'));
3055
+ return results;
3056
+ },
3057
+
3058
+ child: function(nodes) {
3059
+ var h = Selector.handlers;
3060
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
3061
+ for (var j = 0, child; child = node.childNodes[j]; j++)
3062
+ if (child.nodeType == 1 && child.tagName != '!') results.push(child);
3063
+ }
3064
+ return results;
3065
+ },
3066
+
3067
+ adjacent: function(nodes) {
3068
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
3069
+ var next = this.nextElementSibling(node);
3070
+ if (next) results.push(next);
3071
+ }
3072
+ return results;
3073
+ },
3074
+
3075
+ laterSibling: function(nodes) {
3076
+ var h = Selector.handlers;
3077
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
3078
+ h.concat(results, Element.nextSiblings(node));
3079
+ return results;
3080
+ },
3081
+
3082
+ nextElementSibling: function(node) {
3083
+ while (node = node.nextSibling)
3084
+ if (node.nodeType == 1) return node;
3085
+ return null;
3086
+ },
3087
+
3088
+ previousElementSibling: function(node) {
3089
+ while (node = node.previousSibling)
3090
+ if (node.nodeType == 1) return node;
3091
+ return null;
3092
+ },
3093
+
3094
+ // TOKEN FUNCTIONS
3095
+ tagName: function(nodes, root, tagName, combinator) {
3096
+ var uTagName = tagName.toUpperCase();
3097
+ var results = [], h = Selector.handlers;
3098
+ if (nodes) {
3099
+ if (combinator) {
3100
+ // fastlane for ordinary descendant combinators
3101
+ if (combinator == "descendant") {
3102
+ for (var i = 0, node; node = nodes[i]; i++)
3103
+ h.concat(results, node.getElementsByTagName(tagName));
3104
+ return results;
3105
+ } else nodes = this[combinator](nodes);
3106
+ if (tagName == "*") return nodes;
3107
+ }
3108
+ for (var i = 0, node; node = nodes[i]; i++)
3109
+ if (node.tagName.toUpperCase() === uTagName) results.push(node);
3110
+ return results;
3111
+ } else return root.getElementsByTagName(tagName);
3112
+ },
3113
+
3114
+ id: function(nodes, root, id, combinator) {
3115
+ var targetNode = $(id), h = Selector.handlers;
3116
+ if (!targetNode) return [];
3117
+ if (!nodes && root == document) return [targetNode];
3118
+ if (nodes) {
3119
+ if (combinator) {
3120
+ if (combinator == 'child') {
3121
+ for (var i = 0, node; node = nodes[i]; i++)
3122
+ if (targetNode.parentNode == node) return [targetNode];
3123
+ } else if (combinator == 'descendant') {
3124
+ for (var i = 0, node; node = nodes[i]; i++)
3125
+ if (Element.descendantOf(targetNode, node)) return [targetNode];
3126
+ } else if (combinator == 'adjacent') {
3127
+ for (var i = 0, node; node = nodes[i]; i++)
3128
+ if (Selector.handlers.previousElementSibling(targetNode) == node)
3129
+ return [targetNode];
3130
+ } else nodes = h[combinator](nodes);
3131
+ }
3132
+ for (var i = 0, node; node = nodes[i]; i++)
3133
+ if (node == targetNode) return [targetNode];
3134
+ return [];
3135
+ }
3136
+ return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
3137
+ },
3138
+
3139
+ className: function(nodes, root, className, combinator) {
3140
+ if (nodes && combinator) nodes = this[combinator](nodes);
3141
+ return Selector.handlers.byClassName(nodes, root, className);
3142
+ },
3143
+
3144
+ byClassName: function(nodes, root, className) {
3145
+ if (!nodes) nodes = Selector.handlers.descendant([root]);
3146
+ var needle = ' ' + className + ' ';
3147
+ for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
3148
+ nodeClassName = node.className;
3149
+ if (nodeClassName.length == 0) continue;
3150
+ if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
3151
+ results.push(node);
3152
+ }
3153
+ return results;
3154
+ },
3155
+
3156
+ attrPresence: function(nodes, root, attr, combinator) {
3157
+ if (!nodes) nodes = root.getElementsByTagName("*");
3158
+ if (nodes && combinator) nodes = this[combinator](nodes);
3159
+ var results = [];
3160
+ for (var i = 0, node; node = nodes[i]; i++)
3161
+ if (Element.hasAttribute(node, attr)) results.push(node);
3162
+ return results;
3163
+ },
3164
+
3165
+ attr: function(nodes, root, attr, value, operator, combinator) {
3166
+ if (!nodes) nodes = root.getElementsByTagName("*");
3167
+ if (nodes && combinator) nodes = this[combinator](nodes);
3168
+ var handler = Selector.operators[operator], results = [];
3169
+ for (var i = 0, node; node = nodes[i]; i++) {
3170
+ var nodeValue = Element.readAttribute(node, attr);
3171
+ if (nodeValue === null) continue;
3172
+ if (handler(nodeValue, value)) results.push(node);
3173
+ }
3174
+ return results;
3175
+ },
3176
+
3177
+ pseudo: function(nodes, name, value, root, combinator) {
3178
+ if (nodes && combinator) nodes = this[combinator](nodes);
3179
+ if (!nodes) nodes = root.getElementsByTagName("*");
3180
+ return Selector.pseudos[name](nodes, value, root);
3181
+ }
3182
+ },
3183
+
3184
+ pseudos: {
3185
+ 'first-child': function(nodes, value, root) {
3186
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
3187
+ if (Selector.handlers.previousElementSibling(node)) continue;
3188
+ results.push(node);
3189
+ }
3190
+ return results;
3191
+ },
3192
+ 'last-child': function(nodes, value, root) {
3193
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
3194
+ if (Selector.handlers.nextElementSibling(node)) continue;
3195
+ results.push(node);
3196
+ }
3197
+ return results;
3198
+ },
3199
+ 'only-child': function(nodes, value, root) {
3200
+ var h = Selector.handlers;
3201
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
3202
+ if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
3203
+ results.push(node);
3204
+ return results;
3205
+ },
3206
+ 'nth-child': function(nodes, formula, root) {
3207
+ return Selector.pseudos.nth(nodes, formula, root);
3208
+ },
3209
+ 'nth-last-child': function(nodes, formula, root) {
3210
+ return Selector.pseudos.nth(nodes, formula, root, true);
3211
+ },
3212
+ 'nth-of-type': function(nodes, formula, root) {
3213
+ return Selector.pseudos.nth(nodes, formula, root, false, true);
3214
+ },
3215
+ 'nth-last-of-type': function(nodes, formula, root) {
3216
+ return Selector.pseudos.nth(nodes, formula, root, true, true);
3217
+ },
3218
+ 'first-of-type': function(nodes, formula, root) {
3219
+ return Selector.pseudos.nth(nodes, "1", root, false, true);
3220
+ },
3221
+ 'last-of-type': function(nodes, formula, root) {
3222
+ return Selector.pseudos.nth(nodes, "1", root, true, true);
3223
+ },
3224
+ 'only-of-type': function(nodes, formula, root) {
3225
+ var p = Selector.pseudos;
3226
+ return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
3227
+ },
3228
+
3229
+ // handles the an+b logic
3230
+ getIndices: function(a, b, total) {
3231
+ if (a == 0) return b > 0 ? [b] : [];
3232
+ return $R(1, total).inject([], function(memo, i) {
3233
+ if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
3234
+ return memo;
3235
+ });
3236
+ },
3237
+
3238
+ // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
3239
+ nth: function(nodes, formula, root, reverse, ofType) {
3240
+ if (nodes.length == 0) return [];
3241
+ if (formula == 'even') formula = '2n+0';
3242
+ if (formula == 'odd') formula = '2n+1';
3243
+ var h = Selector.handlers, results = [], indexed = [], m;
3244
+ h.mark(nodes);
3245
+ for (var i = 0, node; node = nodes[i]; i++) {
3246
+ if (!node.parentNode._countedByPrototype) {
3247
+ h.index(node.parentNode, reverse, ofType);
3248
+ indexed.push(node.parentNode);
3249
+ }
3250
+ }
3251
+ if (formula.match(/^\d+$/)) { // just a number
3252
+ formula = Number(formula);
3253
+ for (var i = 0, node; node = nodes[i]; i++)
3254
+ if (node.nodeIndex == formula) results.push(node);
3255
+ } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
3256
+ if (m[1] == "-") m[1] = -1;
3257
+ var a = m[1] ? Number(m[1]) : 1;
3258
+ var b = m[2] ? Number(m[2]) : 0;
3259
+ var indices = Selector.pseudos.getIndices(a, b, nodes.length);
3260
+ for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
3261
+ for (var j = 0; j < l; j++)
3262
+ if (node.nodeIndex == indices[j]) results.push(node);
3263
+ }
3264
+ }
3265
+ h.unmark(nodes);
3266
+ h.unmark(indexed);
3267
+ return results;
3268
+ },
3269
+
3270
+ 'empty': function(nodes, value, root) {
3271
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
3272
+ // IE treats comments as element nodes
3273
+ if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
3274
+ results.push(node);
3275
+ }
3276
+ return results;
3277
+ },
3278
+
3279
+ 'not': function(nodes, selector, root) {
3280
+ var h = Selector.handlers, selectorType, m;
3281
+ var exclusions = new Selector(selector).findElements(root);
3282
+ h.mark(exclusions);
3283
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
3284
+ if (!node._countedByPrototype) results.push(node);
3285
+ h.unmark(exclusions);
3286
+ return results;
3287
+ },
3288
+
3289
+ 'enabled': function(nodes, value, root) {
3290
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
3291
+ if (!node.disabled) results.push(node);
3292
+ return results;
3293
+ },
3294
+
3295
+ 'disabled': function(nodes, value, root) {
3296
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
3297
+ if (node.disabled) results.push(node);
3298
+ return results;
3299
+ },
3300
+
3301
+ 'checked': function(nodes, value, root) {
3302
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
3303
+ if (node.checked) results.push(node);
3304
+ return results;
3305
+ }
3306
+ },
3307
+
3308
+ operators: {
3309
+ '=': function(nv, v) { return nv == v; },
3310
+ '!=': function(nv, v) { return nv != v; },
3311
+ '^=': function(nv, v) { return nv.startsWith(v); },
3312
+ '$=': function(nv, v) { return nv.endsWith(v); },
3313
+ '*=': function(nv, v) { return nv.include(v); },
3314
+ '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
3315
+ '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
3316
+ },
3317
+
3318
+ split: function(expression) {
3319
+ var expressions = [];
3320
+ expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
3321
+ expressions.push(m[1].strip());
3322
+ });
3323
+ return expressions;
3324
+ },
3325
+
1848
3326
  matchElements: function(elements, expression) {
1849
- var selector = new Selector(expression);
1850
- return elements.select(selector.match.bind(selector)).map(Element.extend);
3327
+ var matches = $$(expression), h = Selector.handlers;
3328
+ h.mark(matches);
3329
+ for (var i = 0, results = [], element; element = elements[i]; i++)
3330
+ if (element._countedByPrototype) results.push(element);
3331
+ h.unmark(matches);
3332
+ return results;
1851
3333
  },
1852
3334
 
1853
3335
  findElement: function(elements, expression, index) {
1854
- if (typeof expression == 'number') index = expression, expression = false;
3336
+ if (Object.isNumber(expression)) {
3337
+ index = expression; expression = false;
3338
+ }
1855
3339
  return Selector.matchElements(elements, expression || '*')[index || 0];
1856
3340
  },
1857
3341
 
1858
3342
  findChildElements: function(element, expressions) {
1859
- return expressions.map(function(expression) {
1860
- return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
1861
- var selector = new Selector(expr);
1862
- return results.inject([], function(elements, result) {
1863
- return elements.concat(selector.findElements(result || element));
1864
- });
1865
- });
1866
- }).flatten();
3343
+ expressions = Selector.split(expressions.join(','));
3344
+ var results = [], h = Selector.handlers;
3345
+ for (var i = 0, l = expressions.length, selector; i < l; i++) {
3346
+ selector = new Selector(expressions[i].strip());
3347
+ h.concat(results, selector.findElements(element));
3348
+ }
3349
+ return (l > 1) ? h.unique(results) : results;
1867
3350
  }
1868
3351
  });
1869
3352
 
3353
+ if (Prototype.Browser.IE) {
3354
+ Object.extend(Selector.handlers, {
3355
+ // IE returns comment nodes on getElementsByTagName("*").
3356
+ // Filter them out.
3357
+ concat: function(a, b) {
3358
+ for (var i = 0, node; node = b[i]; i++)
3359
+ if (node.tagName !== "!") a.push(node);
3360
+ return a;
3361
+ },
3362
+
3363
+ // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
3364
+ unmark: function(nodes) {
3365
+ for (var i = 0, node; node = nodes[i]; i++)
3366
+ node.removeAttribute('_countedByPrototype');
3367
+ return nodes;
3368
+ }
3369
+ });
3370
+ }
3371
+
1870
3372
  function $$() {
1871
3373
  return Selector.findChildElements(document, $A(arguments));
1872
3374
  }
@@ -1876,13 +3378,19 @@ var Form = {
1876
3378
  return form;
1877
3379
  },
1878
3380
 
1879
- serializeElements: function(elements, getHash) {
1880
- var data = elements.inject({}, function(result, element) {
3381
+ serializeElements: function(elements, options) {
3382
+ if (typeof options != 'object') options = { hash: !!options };
3383
+ else if (Object.isUndefined(options.hash)) options.hash = true;
3384
+ var key, value, submitted = false, submit = options.submit;
3385
+
3386
+ var data = elements.inject({ }, function(result, element) {
1881
3387
  if (!element.disabled && element.name) {
1882
- var key = element.name, value = $(element).getValue();
1883
- if (value != undefined) {
1884
- if (result[key]) {
1885
- if (result[key].constructor != Array) result[key] = [result[key]];
3388
+ key = element.name; value = $(element).getValue();
3389
+ if (value != null && (element.type != 'submit' || (!submitted &&
3390
+ submit !== false && (!submit || key == submit) && (submitted = true)))) {
3391
+ if (key in result) {
3392
+ // a key is already present; construct an array of values
3393
+ if (!Object.isArray(result[key])) result[key] = [result[key]];
1886
3394
  result[key].push(value);
1887
3395
  }
1888
3396
  else result[key] = value;
@@ -1891,13 +3399,13 @@ var Form = {
1891
3399
  return result;
1892
3400
  });
1893
3401
 
1894
- return getHash ? data : Hash.toQueryString(data);
3402
+ return options.hash ? data : Object.toQueryString(data);
1895
3403
  }
1896
3404
  };
1897
3405
 
1898
3406
  Form.Methods = {
1899
- serialize: function(form, getHash) {
1900
- return Form.serializeElements(Form.getElements(form), getHash);
3407
+ serialize: function(form, options) {
3408
+ return Form.serializeElements(Form.getElements(form), options);
1901
3409
  },
1902
3410
 
1903
3411
  getElements: function(form) {
@@ -1928,25 +3436,26 @@ Form.Methods = {
1928
3436
 
1929
3437
  disable: function(form) {
1930
3438
  form = $(form);
1931
- form.getElements().each(function(element) {
1932
- element.blur();
1933
- element.disabled = 'true';
1934
- });
3439
+ Form.getElements(form).invoke('disable');
1935
3440
  return form;
1936
3441
  },
1937
3442
 
1938
3443
  enable: function(form) {
1939
3444
  form = $(form);
1940
- form.getElements().each(function(element) {
1941
- element.disabled = '';
1942
- });
3445
+ Form.getElements(form).invoke('enable');
1943
3446
  return form;
1944
3447
  },
1945
3448
 
1946
3449
  findFirstElement: function(form) {
1947
- return $(form).getElements().find(function(element) {
1948
- return element.type != 'hidden' && !element.disabled &&
1949
- ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
3450
+ var elements = $(form).getElements().findAll(function(element) {
3451
+ return 'hidden' != element.type && !element.disabled;
3452
+ });
3453
+ var firstByIndex = elements.findAll(function(element) {
3454
+ return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
3455
+ }).sortBy(function(element) { return element.tabIndex }).first();
3456
+
3457
+ return firstByIndex ? firstByIndex : elements.find(function(element) {
3458
+ return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
1950
3459
  });
1951
3460
  },
1952
3461
 
@@ -1954,10 +3463,26 @@ Form.Methods = {
1954
3463
  form = $(form);
1955
3464
  form.findFirstElement().activate();
1956
3465
  return form;
1957
- }
1958
- }
3466
+ },
3467
+
3468
+ request: function(form, options) {
3469
+ form = $(form), options = Object.clone(options || { });
1959
3470
 
1960
- Object.extend(Form, Form.Methods);
3471
+ var params = options.parameters, action = form.readAttribute('action') || '';
3472
+ if (action.blank()) action = window.location.href;
3473
+ options.parameters = form.serialize(true);
3474
+
3475
+ if (params) {
3476
+ if (Object.isString(params)) params = params.toQueryParams();
3477
+ Object.extend(options.parameters, params);
3478
+ }
3479
+
3480
+ if (form.hasAttribute('method') && !options.method)
3481
+ options.method = form.method;
3482
+
3483
+ return new Ajax.Request(action, options);
3484
+ }
3485
+ };
1961
3486
 
1962
3487
  /*--------------------------------------------------------------------------*/
1963
3488
 
@@ -1971,7 +3496,7 @@ Form.Element = {
1971
3496
  $(element).select();
1972
3497
  return element;
1973
3498
  }
1974
- }
3499
+ };
1975
3500
 
1976
3501
  Form.Element.Methods = {
1977
3502
  serialize: function(element) {
@@ -1979,9 +3504,9 @@ Form.Element.Methods = {
1979
3504
  if (!element.disabled && element.name) {
1980
3505
  var value = element.getValue();
1981
3506
  if (value != undefined) {
1982
- var pair = {};
3507
+ var pair = { };
1983
3508
  pair[element.name] = value;
1984
- return Hash.toQueryString(pair);
3509
+ return Object.toQueryString(pair);
1985
3510
  }
1986
3511
  }
1987
3512
  return '';
@@ -1993,6 +3518,13 @@ Form.Element.Methods = {
1993
3518
  return Form.Element.Serializers[method](element);
1994
3519
  },
1995
3520
 
3521
+ setValue: function(element, value) {
3522
+ element = $(element);
3523
+ var method = element.tagName.toLowerCase();
3524
+ Form.Element.Serializers[method](element, value);
3525
+ return element;
3526
+ },
3527
+
1996
3528
  clear: function(element) {
1997
3529
  $(element).value = '';
1998
3530
  return element;
@@ -2004,55 +3536,75 @@ Form.Element.Methods = {
2004
3536
 
2005
3537
  activate: function(element) {
2006
3538
  element = $(element);
2007
- element.focus();
2008
- if (element.select && ( element.tagName.toLowerCase() != 'input' ||
2009
- !['button', 'reset', 'submit'].include(element.type) ) )
2010
- element.select();
3539
+ try {
3540
+ element.focus();
3541
+ if (element.select && (element.tagName.toLowerCase() != 'input' ||
3542
+ !['button', 'reset', 'submit'].include(element.type)))
3543
+ element.select();
3544
+ } catch (e) { }
2011
3545
  return element;
2012
3546
  },
2013
3547
 
2014
3548
  disable: function(element) {
2015
3549
  element = $(element);
3550
+ element.blur();
2016
3551
  element.disabled = true;
2017
3552
  return element;
2018
3553
  },
2019
3554
 
2020
3555
  enable: function(element) {
2021
3556
  element = $(element);
2022
- element.blur();
2023
3557
  element.disabled = false;
2024
3558
  return element;
2025
3559
  }
2026
- }
3560
+ };
3561
+
3562
+ /*--------------------------------------------------------------------------*/
2027
3563
 
2028
- Object.extend(Form.Element, Form.Element.Methods);
2029
3564
  var Field = Form.Element;
2030
- var $F = Form.Element.getValue;
3565
+ var $F = Form.Element.Methods.getValue;
2031
3566
 
2032
3567
  /*--------------------------------------------------------------------------*/
2033
3568
 
2034
3569
  Form.Element.Serializers = {
2035
- input: function(element) {
3570
+ input: function(element, value) {
2036
3571
  switch (element.type.toLowerCase()) {
2037
3572
  case 'checkbox':
2038
3573
  case 'radio':
2039
- return Form.Element.Serializers.inputSelector(element);
3574
+ return Form.Element.Serializers.inputSelector(element, value);
2040
3575
  default:
2041
- return Form.Element.Serializers.textarea(element);
3576
+ return Form.Element.Serializers.textarea(element, value);
2042
3577
  }
2043
3578
  },
2044
3579
 
2045
- inputSelector: function(element) {
2046
- return element.checked ? element.value : null;
3580
+ inputSelector: function(element, value) {
3581
+ if (Object.isUndefined(value)) return element.checked ? element.value : null;
3582
+ else element.checked = !!value;
2047
3583
  },
2048
3584
 
2049
- textarea: function(element) {
2050
- return element.value;
3585
+ textarea: function(element, value) {
3586
+ if (Object.isUndefined(value)) return element.value;
3587
+ else element.value = value;
2051
3588
  },
2052
3589
 
2053
- select: function(element) {
2054
- return this[element.type == 'select-one' ?
2055
- 'selectOne' : 'selectMany'](element);
3590
+ select: function(element, index) {
3591
+ if (Object.isUndefined(index))
3592
+ return this[element.type == 'select-one' ?
3593
+ 'selectOne' : 'selectMany'](element);
3594
+ else {
3595
+ var opt, value, single = !Object.isArray(index);
3596
+ for (var i = 0, length = element.length; i < length; i++) {
3597
+ opt = element.options[i];
3598
+ value = this.optionValue(opt);
3599
+ if (single) {
3600
+ if (value == index) {
3601
+ opt.selected = true;
3602
+ return;
3603
+ }
3604
+ }
3605
+ else opt.selected = index.include(value);
3606
+ }
3607
+ }
2056
3608
  },
2057
3609
 
2058
3610
  selectOne: function(element) {
@@ -2075,219 +3627,440 @@ Form.Element.Serializers = {
2075
3627
  // extend element because hasAttribute may not be native
2076
3628
  return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
2077
3629
  }
2078
- }
3630
+ };
2079
3631
 
2080
3632
  /*--------------------------------------------------------------------------*/
2081
3633
 
2082
- Abstract.TimedObserver = function() {}
2083
- Abstract.TimedObserver.prototype = {
2084
- initialize: function(element, frequency, callback) {
2085
- this.frequency = frequency;
3634
+ Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
3635
+ initialize: function($super, element, frequency, callback) {
3636
+ $super(callback, frequency);
2086
3637
  this.element = $(element);
2087
- this.callback = callback;
2088
-
2089
3638
  this.lastValue = this.getValue();
2090
- this.registerCallback();
2091
3639
  },
2092
3640
 
2093
- registerCallback: function() {
2094
- setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
3641
+ execute: function() {
3642
+ var value = this.getValue();
3643
+ if (Object.isString(this.lastValue) && Object.isString(value) ?
3644
+ this.lastValue != value : String(this.lastValue) != String(value)) {
3645
+ this.callback(this.element, value);
3646
+ this.lastValue = value;
3647
+ }
3648
+ }
3649
+ });
3650
+
3651
+ Form.Element.Observer = Class.create(Abstract.TimedObserver, {
3652
+ getValue: function() {
3653
+ return Form.Element.getValue(this.element);
3654
+ }
3655
+ });
3656
+
3657
+ Form.Observer = Class.create(Abstract.TimedObserver, {
3658
+ getValue: function() {
3659
+ return Form.serialize(this.element);
3660
+ }
3661
+ });
3662
+
3663
+ /*--------------------------------------------------------------------------*/
3664
+
3665
+ Abstract.EventObserver = Class.create({
3666
+ initialize: function(element, callback) {
3667
+ this.element = $(element);
3668
+ this.callback = callback;
3669
+
3670
+ this.lastValue = this.getValue();
3671
+ if (this.element.tagName.toLowerCase() == 'form')
3672
+ this.registerFormCallbacks();
3673
+ else
3674
+ this.registerCallback(this.element);
2095
3675
  },
2096
3676
 
2097
- onTimerEvent: function() {
3677
+ onElementEvent: function() {
2098
3678
  var value = this.getValue();
2099
- var changed = ('string' == typeof this.lastValue && 'string' == typeof value
2100
- ? this.lastValue != value : String(this.lastValue) != String(value));
2101
- if (changed) {
3679
+ if (this.lastValue != value) {
2102
3680
  this.callback(this.element, value);
2103
3681
  this.lastValue = value;
2104
3682
  }
3683
+ },
3684
+
3685
+ registerFormCallbacks: function() {
3686
+ Form.getElements(this.element).each(this.registerCallback, this);
3687
+ },
3688
+
3689
+ registerCallback: function(element) {
3690
+ if (element.type) {
3691
+ switch (element.type.toLowerCase()) {
3692
+ case 'checkbox':
3693
+ case 'radio':
3694
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
3695
+ break;
3696
+ default:
3697
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
3698
+ break;
3699
+ }
3700
+ }
2105
3701
  }
2106
- }
3702
+ });
3703
+
3704
+ Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
3705
+ getValue: function() {
3706
+ return Form.Element.getValue(this.element);
3707
+ }
3708
+ });
3709
+
3710
+ Form.EventObserver = Class.create(Abstract.EventObserver, {
3711
+ getValue: function() {
3712
+ return Form.serialize(this.element);
3713
+ }
3714
+ });
3715
+ if (!window.Event) var Event = { };
3716
+
3717
+ Object.extend(Event, {
3718
+ KEY_BACKSPACE: 8,
3719
+ KEY_TAB: 9,
3720
+ KEY_RETURN: 13,
3721
+ KEY_ESC: 27,
3722
+ KEY_LEFT: 37,
3723
+ KEY_UP: 38,
3724
+ KEY_RIGHT: 39,
3725
+ KEY_DOWN: 40,
3726
+ KEY_DELETE: 46,
3727
+ KEY_HOME: 36,
3728
+ KEY_END: 35,
3729
+ KEY_PAGEUP: 33,
3730
+ KEY_PAGEDOWN: 34,
3731
+ KEY_INSERT: 45,
3732
+
3733
+ cache: { },
3734
+
3735
+ relatedTarget: function(event) {
3736
+ var element;
3737
+ switch(event.type) {
3738
+ case 'mouseover': element = event.fromElement; break;
3739
+ case 'mouseout': element = event.toElement; break;
3740
+ default: return null;
3741
+ }
3742
+ return Element.extend(element);
3743
+ }
3744
+ });
3745
+
3746
+ Event.Methods = (function() {
3747
+ var isButton;
3748
+
3749
+ if (Prototype.Browser.IE) {
3750
+ var buttonMap = { 0: 1, 1: 4, 2: 2 };
3751
+ isButton = function(event, code) {
3752
+ return event.button == buttonMap[code];
3753
+ };
3754
+
3755
+ } else if (Prototype.Browser.WebKit) {
3756
+ isButton = function(event, code) {
3757
+ switch (code) {
3758
+ case 0: return event.which == 1 && !event.metaKey;
3759
+ case 1: return event.which == 1 && event.metaKey;
3760
+ default: return false;
3761
+ }
3762
+ };
3763
+
3764
+ } else {
3765
+ isButton = function(event, code) {
3766
+ return event.which ? (event.which === code + 1) : (event.button === code);
3767
+ };
3768
+ }
3769
+
3770
+ return {
3771
+ isLeftClick: function(event) { return isButton(event, 0) },
3772
+ isMiddleClick: function(event) { return isButton(event, 1) },
3773
+ isRightClick: function(event) { return isButton(event, 2) },
3774
+
3775
+ element: function(event) {
3776
+ var node = Event.extend(event).target;
3777
+ return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
3778
+ },
3779
+
3780
+ findElement: function(event, expression) {
3781
+ var element = Event.element(event);
3782
+ if (!expression) return element;
3783
+ var elements = [element].concat(element.ancestors());
3784
+ return Selector.findElement(elements, expression, 0);
3785
+ },
3786
+
3787
+ pointer: function(event) {
3788
+ return {
3789
+ x: event.pageX || (event.clientX +
3790
+ (document.documentElement.scrollLeft || document.body.scrollLeft)),
3791
+ y: event.pageY || (event.clientY +
3792
+ (document.documentElement.scrollTop || document.body.scrollTop))
3793
+ };
3794
+ },
3795
+
3796
+ pointerX: function(event) { return Event.pointer(event).x },
3797
+ pointerY: function(event) { return Event.pointer(event).y },
3798
+
3799
+ stop: function(event) {
3800
+ Event.extend(event);
3801
+ event.preventDefault();
3802
+ event.stopPropagation();
3803
+ event.stopped = true;
3804
+ }
3805
+ };
3806
+ })();
3807
+
3808
+ Event.extend = (function() {
3809
+ var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
3810
+ m[name] = Event.Methods[name].methodize();
3811
+ return m;
3812
+ });
3813
+
3814
+ if (Prototype.Browser.IE) {
3815
+ Object.extend(methods, {
3816
+ stopPropagation: function() { this.cancelBubble = true },
3817
+ preventDefault: function() { this.returnValue = false },
3818
+ inspect: function() { return "[object Event]" }
3819
+ });
3820
+
3821
+ return function(event) {
3822
+ if (!event) return false;
3823
+ if (event._extendedByPrototype) return event;
3824
+
3825
+ event._extendedByPrototype = Prototype.emptyFunction;
3826
+ var pointer = Event.pointer(event);
3827
+ Object.extend(event, {
3828
+ target: event.srcElement,
3829
+ relatedTarget: Event.relatedTarget(event),
3830
+ pageX: pointer.x,
3831
+ pageY: pointer.y
3832
+ });
3833
+ return Object.extend(event, methods);
3834
+ };
3835
+
3836
+ } else {
3837
+ Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
3838
+ Object.extend(Event.prototype, methods);
3839
+ return Prototype.K;
3840
+ }
3841
+ })();
3842
+
3843
+ Object.extend(Event, (function() {
3844
+ var cache = Event.cache;
3845
+
3846
+ function getEventID(element) {
3847
+ if (element._prototypeEventID) return element._prototypeEventID[0];
3848
+ arguments.callee.id = arguments.callee.id || 1;
3849
+ return element._prototypeEventID = [++arguments.callee.id];
3850
+ }
3851
+
3852
+ function getDOMEventName(eventName) {
3853
+ if (eventName && eventName.include(':')) return "dataavailable";
3854
+ return eventName;
3855
+ }
3856
+
3857
+ function getCacheForID(id) {
3858
+ return cache[id] = cache[id] || { };
3859
+ }
3860
+
3861
+ function getWrappersForEventName(id, eventName) {
3862
+ var c = getCacheForID(id);
3863
+ return c[eventName] = c[eventName] || [];
3864
+ }
3865
+
3866
+ function createWrapper(element, eventName, handler) {
3867
+ var id = getEventID(element);
3868
+ var c = getWrappersForEventName(id, eventName);
3869
+ if (c.pluck("handler").include(handler)) return false;
3870
+
3871
+ var wrapper = function(event) {
3872
+ if (!Event || !Event.extend ||
3873
+ (event.eventName && event.eventName != eventName))
3874
+ return false;
3875
+
3876
+ Event.extend(event);
3877
+ handler.call(element, event);
3878
+ };
3879
+
3880
+ wrapper.handler = handler;
3881
+ c.push(wrapper);
3882
+ return wrapper;
3883
+ }
3884
+
3885
+ function findWrapper(id, eventName, handler) {
3886
+ var c = getWrappersForEventName(id, eventName);
3887
+ return c.find(function(wrapper) { return wrapper.handler == handler });
3888
+ }
3889
+
3890
+ function destroyWrapper(id, eventName, handler) {
3891
+ var c = getCacheForID(id);
3892
+ if (!c[eventName]) return false;
3893
+ c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
3894
+ }
3895
+
3896
+ function destroyCache() {
3897
+ for (var id in cache)
3898
+ for (var eventName in cache[id])
3899
+ cache[id][eventName] = null;
3900
+ }
3901
+
3902
+ if (window.attachEvent) {
3903
+ window.attachEvent("onunload", destroyCache);
3904
+ }
3905
+
3906
+ return {
3907
+ observe: function(element, eventName, handler) {
3908
+ element = $(element);
3909
+ var name = getDOMEventName(eventName);
3910
+
3911
+ var wrapper = createWrapper(element, eventName, handler);
3912
+ if (!wrapper) return element;
3913
+
3914
+ if (element.addEventListener) {
3915
+ element.addEventListener(name, wrapper, false);
3916
+ } else {
3917
+ element.attachEvent("on" + name, wrapper);
3918
+ }
3919
+
3920
+ return element;
3921
+ },
3922
+
3923
+ stopObserving: function(element, eventName, handler) {
3924
+ element = $(element);
3925
+ var id = getEventID(element), name = getDOMEventName(eventName);
2107
3926
 
2108
- Form.Element.Observer = Class.create();
2109
- Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2110
- getValue: function() {
2111
- return Form.Element.getValue(this.element);
2112
- }
2113
- });
3927
+ if (!handler && eventName) {
3928
+ getWrappersForEventName(id, eventName).each(function(wrapper) {
3929
+ element.stopObserving(eventName, wrapper.handler);
3930
+ });
3931
+ return element;
2114
3932
 
2115
- Form.Observer = Class.create();
2116
- Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2117
- getValue: function() {
2118
- return Form.serialize(this.element);
2119
- }
2120
- });
3933
+ } else if (!eventName) {
3934
+ Object.keys(getCacheForID(id)).each(function(eventName) {
3935
+ element.stopObserving(eventName);
3936
+ });
3937
+ return element;
3938
+ }
2121
3939
 
2122
- /*--------------------------------------------------------------------------*/
3940
+ var wrapper = findWrapper(id, eventName, handler);
3941
+ if (!wrapper) return element;
2123
3942
 
2124
- Abstract.EventObserver = function() {}
2125
- Abstract.EventObserver.prototype = {
2126
- initialize: function(element, callback) {
2127
- this.element = $(element);
2128
- this.callback = callback;
3943
+ if (element.removeEventListener) {
3944
+ element.removeEventListener(name, wrapper, false);
3945
+ } else {
3946
+ element.detachEvent("on" + name, wrapper);
3947
+ }
2129
3948
 
2130
- this.lastValue = this.getValue();
2131
- if (this.element.tagName.toLowerCase() == 'form')
2132
- this.registerFormCallbacks();
2133
- else
2134
- this.registerCallback(this.element);
2135
- },
3949
+ destroyWrapper(id, eventName, handler);
2136
3950
 
2137
- onElementEvent: function() {
2138
- var value = this.getValue();
2139
- if (this.lastValue != value) {
2140
- this.callback(this.element, value);
2141
- this.lastValue = value;
2142
- }
2143
- },
3951
+ return element;
3952
+ },
2144
3953
 
2145
- registerFormCallbacks: function() {
2146
- Form.getElements(this.element).each(this.registerCallback.bind(this));
2147
- },
3954
+ fire: function(element, eventName, memo) {
3955
+ element = $(element);
3956
+ if (element == document && document.createEvent && !element.dispatchEvent)
3957
+ element = document.documentElement;
2148
3958
 
2149
- registerCallback: function(element) {
2150
- if (element.type) {
2151
- switch (element.type.toLowerCase()) {
2152
- case 'checkbox':
2153
- case 'radio':
2154
- Event.observe(element, 'click', this.onElementEvent.bind(this));
2155
- break;
2156
- default:
2157
- Event.observe(element, 'change', this.onElementEvent.bind(this));
2158
- break;
3959
+ var event;
3960
+ if (document.createEvent) {
3961
+ event = document.createEvent("HTMLEvents");
3962
+ event.initEvent("dataavailable", true, true);
3963
+ } else {
3964
+ event = document.createEventObject();
3965
+ event.eventType = "ondataavailable";
3966
+ }
3967
+
3968
+ event.eventName = eventName;
3969
+ event.memo = memo || { };
3970
+
3971
+ if (document.createEvent) {
3972
+ element.dispatchEvent(event);
3973
+ } else {
3974
+ element.fireEvent(event.eventType, event);
2159
3975
  }
3976
+
3977
+ return Event.extend(event);
2160
3978
  }
2161
- }
2162
- }
3979
+ };
3980
+ })());
2163
3981
 
2164
- Form.Element.EventObserver = Class.create();
2165
- Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2166
- getValue: function() {
2167
- return Form.Element.getValue(this.element);
2168
- }
3982
+ Object.extend(Event, Event.Methods);
3983
+
3984
+ Element.addMethods({
3985
+ fire: Event.fire,
3986
+ observe: Event.observe,
3987
+ stopObserving: Event.stopObserving
2169
3988
  });
2170
3989
 
2171
- Form.EventObserver = Class.create();
2172
- Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2173
- getValue: function() {
2174
- return Form.serialize(this.element);
2175
- }
3990
+ Object.extend(document, {
3991
+ fire: Element.Methods.fire.methodize(),
3992
+ observe: Element.Methods.observe.methodize(),
3993
+ stopObserving: Element.Methods.stopObserving.methodize(),
3994
+ loaded: false
2176
3995
  });
2177
- if (!window.Event) {
2178
- var Event = new Object();
2179
- }
2180
3996
 
2181
- Object.extend(Event, {
2182
- KEY_BACKSPACE: 8,
2183
- KEY_TAB: 9,
2184
- KEY_RETURN: 13,
2185
- KEY_ESC: 27,
2186
- KEY_LEFT: 37,
2187
- KEY_UP: 38,
2188
- KEY_RIGHT: 39,
2189
- KEY_DOWN: 40,
2190
- KEY_DELETE: 46,
2191
- KEY_HOME: 36,
2192
- KEY_END: 35,
2193
- KEY_PAGEUP: 33,
2194
- KEY_PAGEDOWN: 34,
3997
+ (function() {
3998
+ /* Support for the DOMContentLoaded event is based on work by Dan Webb,
3999
+ Matthias Miller, Dean Edwards and John Resig. */
2195
4000
 
2196
- element: function(event) {
2197
- return event.target || event.srcElement;
2198
- },
4001
+ var timer;
2199
4002
 
2200
- isLeftClick: function(event) {
2201
- return (((event.which) && (event.which == 1)) ||
2202
- ((event.button) && (event.button == 1)));
2203
- },
4003
+ function fireContentLoadedEvent() {
4004
+ if (document.loaded) return;
4005
+ if (timer) window.clearInterval(timer);
4006
+ document.fire("dom:loaded");
4007
+ document.loaded = true;
4008
+ }
2204
4009
 
2205
- pointerX: function(event) {
2206
- return event.pageX || (event.clientX +
2207
- (document.documentElement.scrollLeft || document.body.scrollLeft));
2208
- },
4010
+ if (document.addEventListener) {
4011
+ if (Prototype.Browser.WebKit) {
4012
+ timer = window.setInterval(function() {
4013
+ if (/loaded|complete/.test(document.readyState))
4014
+ fireContentLoadedEvent();
4015
+ }, 0);
2209
4016
 
2210
- pointerY: function(event) {
2211
- return event.pageY || (event.clientY +
2212
- (document.documentElement.scrollTop || document.body.scrollTop));
2213
- },
4017
+ Event.observe(window, "load", fireContentLoadedEvent);
2214
4018
 
2215
- stop: function(event) {
2216
- if (event.preventDefault) {
2217
- event.preventDefault();
2218
- event.stopPropagation();
2219
4019
  } else {
2220
- event.returnValue = false;
2221
- event.cancelBubble = true;
4020
+ document.addEventListener("DOMContentLoaded",
4021
+ fireContentLoadedEvent, false);
2222
4022
  }
2223
- },
2224
-
2225
- // find the first node with the given tagName, starting from the
2226
- // node the event was triggered on; traverses the DOM upwards
2227
- findElement: function(event, tagName) {
2228
- var element = Event.element(event);
2229
- while (element.parentNode && (!element.tagName ||
2230
- (element.tagName.toUpperCase() != tagName.toUpperCase())))
2231
- element = element.parentNode;
2232
- return element;
2233
- },
2234
-
2235
- observers: false,
2236
4023
 
2237
- _observeAndCache: function(element, name, observer, useCapture) {
2238
- if (!this.observers) this.observers = [];
2239
- if (element.addEventListener) {
2240
- this.observers.push([element, name, observer, useCapture]);
2241
- element.addEventListener(name, observer, useCapture);
2242
- } else if (element.attachEvent) {
2243
- this.observers.push([element, name, observer, useCapture]);
2244
- element.attachEvent('on' + name, observer);
2245
- }
2246
- },
4024
+ } else {
4025
+ document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
4026
+ $("__onDOMContentLoaded").onreadystatechange = function() {
4027
+ if (this.readyState == "complete") {
4028
+ this.onreadystatechange = null;
4029
+ fireContentLoadedEvent();
4030
+ }
4031
+ };
4032
+ }
4033
+ })();
4034
+ /*------------------------------- DEPRECATED -------------------------------*/
2247
4035
 
2248
- unloadCache: function() {
2249
- if (!Event.observers) return;
2250
- for (var i = 0, length = Event.observers.length; i < length; i++) {
2251
- Event.stopObserving.apply(this, Event.observers[i]);
2252
- Event.observers[i][0] = null;
2253
- }
2254
- Event.observers = false;
2255
- },
4036
+ Hash.toQueryString = Object.toQueryString;
2256
4037
 
2257
- observe: function(element, name, observer, useCapture) {
2258
- element = $(element);
2259
- useCapture = useCapture || false;
4038
+ var Toggle = { display: Element.toggle };
2260
4039
 
2261
- if (name == 'keypress' &&
2262
- (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
2263
- || element.attachEvent))
2264
- name = 'keydown';
4040
+ Element.Methods.childOf = Element.Methods.descendantOf;
2265
4041
 
2266
- Event._observeAndCache(element, name, observer, useCapture);
4042
+ var Insertion = {
4043
+ Before: function(element, content) {
4044
+ return Element.insert(element, {before:content});
2267
4045
  },
2268
4046
 
2269
- stopObserving: function(element, name, observer, useCapture) {
2270
- element = $(element);
2271
- useCapture = useCapture || false;
4047
+ Top: function(element, content) {
4048
+ return Element.insert(element, {top:content});
4049
+ },
2272
4050
 
2273
- if (name == 'keypress' &&
2274
- (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
2275
- || element.detachEvent))
2276
- name = 'keydown';
4051
+ Bottom: function(element, content) {
4052
+ return Element.insert(element, {bottom:content});
4053
+ },
2277
4054
 
2278
- if (element.removeEventListener) {
2279
- element.removeEventListener(name, observer, useCapture);
2280
- } else if (element.detachEvent) {
2281
- try {
2282
- element.detachEvent('on' + name, observer);
2283
- } catch (e) {}
2284
- }
4055
+ After: function(element, content) {
4056
+ return Element.insert(element, {after:content});
2285
4057
  }
2286
- });
4058
+ };
2287
4059
 
2288
- /* prevent memory leaks in IE */
2289
- if (navigator.appVersion.match(/\bMSIE\b/))
2290
- Event.observe(window, 'unload', Event.unloadCache, false);
4060
+ var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
4061
+
4062
+ // This should be moved to script.aculo.us; notice the deprecated methods
4063
+ // further below, that map to the newer Element methods.
2291
4064
  var Position = {
2292
4065
  // set to true if needed, warning: firefox performance problems
2293
4066
  // NOT neeeded for page scrolling, only if draggable contained in
@@ -2307,59 +4080,13 @@ var Position = {
2307
4080
  || 0;
2308
4081
  },
2309
4082
 
2310
- realOffset: function(element) {
2311
- var valueT = 0, valueL = 0;
2312
- do {
2313
- valueT += element.scrollTop || 0;
2314
- valueL += element.scrollLeft || 0;
2315
- element = element.parentNode;
2316
- } while (element);
2317
- return [valueL, valueT];
2318
- },
2319
-
2320
- cumulativeOffset: function(element) {
2321
- var valueT = 0, valueL = 0;
2322
- do {
2323
- valueT += element.offsetTop || 0;
2324
- valueL += element.offsetLeft || 0;
2325
- element = element.offsetParent;
2326
- } while (element);
2327
- return [valueL, valueT];
2328
- },
2329
-
2330
- positionedOffset: function(element) {
2331
- var valueT = 0, valueL = 0;
2332
- do {
2333
- valueT += element.offsetTop || 0;
2334
- valueL += element.offsetLeft || 0;
2335
- element = element.offsetParent;
2336
- if (element) {
2337
- if(element.tagName=='BODY') break;
2338
- var p = Element.getStyle(element, 'position');
2339
- if (p == 'relative' || p == 'absolute') break;
2340
- }
2341
- } while (element);
2342
- return [valueL, valueT];
2343
- },
2344
-
2345
- offsetParent: function(element) {
2346
- if (element.offsetParent) return element.offsetParent;
2347
- if (element == document.body) return element;
2348
-
2349
- while ((element = element.parentNode) && element != document.body)
2350
- if (Element.getStyle(element, 'position') != 'static')
2351
- return element;
2352
-
2353
- return document.body;
2354
- },
2355
-
2356
4083
  // caches x/y coordinate pair to use with overlap
2357
4084
  within: function(element, x, y) {
2358
4085
  if (this.includeScrollOffsets)
2359
4086
  return this.withinIncludingScrolloffsets(element, x, y);
2360
4087
  this.xcomp = x;
2361
4088
  this.ycomp = y;
2362
- this.offset = this.cumulativeOffset(element);
4089
+ this.offset = Element.cumulativeOffset(element);
2363
4090
 
2364
4091
  return (y >= this.offset[1] &&
2365
4092
  y < this.offset[1] + element.offsetHeight &&
@@ -2368,11 +4095,11 @@ var Position = {
2368
4095
  },
2369
4096
 
2370
4097
  withinIncludingScrolloffsets: function(element, x, y) {
2371
- var offsetcache = this.realOffset(element);
4098
+ var offsetcache = Element.cumulativeScrollOffset(element);
2372
4099
 
2373
4100
  this.xcomp = x + offsetcache[0] - this.deltaX;
2374
4101
  this.ycomp = y + offsetcache[1] - this.deltaY;
2375
- this.offset = this.cumulativeOffset(element);
4102
+ this.offset = Element.cumulativeOffset(element);
2376
4103
 
2377
4104
  return (this.ycomp >= this.offset[1] &&
2378
4105
  this.ycomp < this.offset[1] + element.offsetHeight &&
@@ -2391,125 +4118,104 @@ var Position = {
2391
4118
  element.offsetWidth;
2392
4119
  },
2393
4120
 
2394
- page: function(forElement) {
2395
- var valueT = 0, valueL = 0;
2396
-
2397
- var element = forElement;
2398
- do {
2399
- valueT += element.offsetTop || 0;
2400
- valueL += element.offsetLeft || 0;
4121
+ // Deprecation layer -- use newer Element methods now (1.5.2).
2401
4122
 
2402
- // Safari fix
2403
- if (element.offsetParent==document.body)
2404
- if (Element.getStyle(element,'position')=='absolute') break;
4123
+ cumulativeOffset: Element.Methods.cumulativeOffset,
2405
4124
 
2406
- } while (element = element.offsetParent);
4125
+ positionedOffset: Element.Methods.positionedOffset,
2407
4126
 
2408
- element = forElement;
2409
- do {
2410
- if (!window.opera || element.tagName=='BODY') {
2411
- valueT -= element.scrollTop || 0;
2412
- valueL -= element.scrollLeft || 0;
2413
- }
2414
- } while (element = element.parentNode);
4127
+ absolutize: function(element) {
4128
+ Position.prepare();
4129
+ return Element.absolutize(element);
4130
+ },
2415
4131
 
2416
- return [valueL, valueT];
4132
+ relativize: function(element) {
4133
+ Position.prepare();
4134
+ return Element.relativize(element);
2417
4135
  },
2418
4136
 
2419
- clone: function(source, target) {
2420
- var options = Object.extend({
2421
- setLeft: true,
2422
- setTop: true,
2423
- setWidth: true,
2424
- setHeight: true,
2425
- offsetTop: 0,
2426
- offsetLeft: 0
2427
- }, arguments[2] || {})
4137
+ realOffset: Element.Methods.cumulativeScrollOffset,
2428
4138
 
2429
- // find page position of source
2430
- source = $(source);
2431
- var p = Position.page(source);
4139
+ offsetParent: Element.Methods.getOffsetParent,
2432
4140
 
2433
- // find coordinate system to use
2434
- target = $(target);
2435
- var delta = [0, 0];
2436
- var parent = null;
2437
- // delta [0,0] will do fine with position: fixed elements,
2438
- // position:absolute needs offsetParent deltas
2439
- if (Element.getStyle(target,'position') == 'absolute') {
2440
- parent = Position.offsetParent(target);
2441
- delta = Position.page(parent);
2442
- }
4141
+ page: Element.Methods.viewportOffset,
2443
4142
 
2444
- // correct by body offsets (fixes Safari)
2445
- if (parent == document.body) {
2446
- delta[0] -= document.body.offsetLeft;
2447
- delta[1] -= document.body.offsetTop;
4143
+ clone: function(source, target, options) {
4144
+ options = options || { };
4145
+ return Element.clonePosition(target, source, options);
4146
+ }
4147
+ };
4148
+
4149
+ /*--------------------------------------------------------------------------*/
4150
+
4151
+ if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
4152
+ function iter(name) {
4153
+ return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
4154
+ }
4155
+
4156
+ instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
4157
+ function(element, className) {
4158
+ className = className.toString().strip();
4159
+ var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
4160
+ return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
4161
+ } : function(element, className) {
4162
+ className = className.toString().strip();
4163
+ var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
4164
+ if (!classNames && !className) return elements;
4165
+
4166
+ var nodes = $(element).getElementsByTagName('*');
4167
+ className = ' ' + className + ' ';
4168
+
4169
+ for (var i = 0, child, cn; child = nodes[i]; i++) {
4170
+ if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
4171
+ (classNames && classNames.all(function(name) {
4172
+ return !name.toString().blank() && cn.include(' ' + name + ' ');
4173
+ }))))
4174
+ elements.push(Element.extend(child));
2448
4175
  }
4176
+ return elements;
4177
+ };
2449
4178
 
2450
- // set position
2451
- if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
2452
- if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
2453
- if(options.setWidth) target.style.width = source.offsetWidth + 'px';
2454
- if(options.setHeight) target.style.height = source.offsetHeight + 'px';
2455
- },
4179
+ return function(className, parentElement) {
4180
+ return $(parentElement || document.body).getElementsByClassName(className);
4181
+ };
4182
+ }(Element.Methods);
2456
4183
 
2457
- absolutize: function(element) {
2458
- element = $(element);
2459
- if (element.style.position == 'absolute') return;
2460
- Position.prepare();
4184
+ /*--------------------------------------------------------------------------*/
2461
4185
 
2462
- var offsets = Position.positionedOffset(element);
2463
- var top = offsets[1];
2464
- var left = offsets[0];
2465
- var width = element.clientWidth;
2466
- var height = element.clientHeight;
4186
+ Element.ClassNames = Class.create();
4187
+ Element.ClassNames.prototype = {
4188
+ initialize: function(element) {
4189
+ this.element = $(element);
4190
+ },
2467
4191
 
2468
- element._originalLeft = left - parseFloat(element.style.left || 0);
2469
- element._originalTop = top - parseFloat(element.style.top || 0);
2470
- element._originalWidth = element.style.width;
2471
- element._originalHeight = element.style.height;
4192
+ _each: function(iterator) {
4193
+ this.element.className.split(/\s+/).select(function(name) {
4194
+ return name.length > 0;
4195
+ })._each(iterator);
4196
+ },
2472
4197
 
2473
- element.style.position = 'absolute';
2474
- element.style.top = top + 'px';
2475
- element.style.left = left + 'px';
2476
- element.style.width = width + 'px';
2477
- element.style.height = height + 'px';
4198
+ set: function(className) {
4199
+ this.element.className = className;
2478
4200
  },
2479
4201
 
2480
- relativize: function(element) {
2481
- element = $(element);
2482
- if (element.style.position == 'relative') return;
2483
- Position.prepare();
4202
+ add: function(classNameToAdd) {
4203
+ if (this.include(classNameToAdd)) return;
4204
+ this.set($A(this).concat(classNameToAdd).join(' '));
4205
+ },
2484
4206
 
2485
- element.style.position = 'relative';
2486
- var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
2487
- var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
4207
+ remove: function(classNameToRemove) {
4208
+ if (!this.include(classNameToRemove)) return;
4209
+ this.set($A(this).without(classNameToRemove).join(' '));
4210
+ },
2488
4211
 
2489
- element.style.top = top + 'px';
2490
- element.style.left = left + 'px';
2491
- element.style.height = element._originalHeight;
2492
- element.style.width = element._originalWidth;
4212
+ toString: function() {
4213
+ return $A(this).join(' ');
2493
4214
  }
2494
- }
2495
-
2496
- // Safari returns margins on body which is incorrect if the child is absolutely
2497
- // positioned. For performance reasons, redefine Position.cumulativeOffset for
2498
- // KHTML/WebKit only.
2499
- if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
2500
- Position.cumulativeOffset = function(element) {
2501
- var valueT = 0, valueL = 0;
2502
- do {
2503
- valueT += element.offsetTop || 0;
2504
- valueL += element.offsetLeft || 0;
2505
- if (element.offsetParent == document.body)
2506
- if (Element.getStyle(element, 'position') == 'absolute') break;
4215
+ };
2507
4216
 
2508
- element = element.offsetParent;
2509
- } while (element);
4217
+ Object.extend(Element.ClassNames.prototype, Enumerable);
2510
4218
 
2511
- return [valueL, valueT];
2512
- }
2513
- }
4219
+ /*--------------------------------------------------------------------------*/
2514
4220
 
2515
4221
  Element.addMethods();