activeldap 0.9.0 → 0.10.0

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