actionpack 1.12.5 → 1.13.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (179) hide show
  1. data/CHANGELOG +517 -15
  2. data/MIT-LICENSE +1 -1
  3. data/README +18 -20
  4. data/Rakefile +7 -4
  5. data/examples/address_book_controller.rb +3 -3
  6. data/examples/blog_controller.cgi +3 -3
  7. data/examples/debate_controller.cgi +5 -5
  8. data/lib/action_controller.rb +2 -2
  9. data/lib/action_controller/assertions.rb +73 -311
  10. data/lib/action_controller/{deprecated_assertions.rb → assertions/deprecated_assertions.rb} +32 -8
  11. data/lib/action_controller/assertions/dom_assertions.rb +25 -0
  12. data/lib/action_controller/assertions/model_assertions.rb +12 -0
  13. data/lib/action_controller/assertions/response_assertions.rb +140 -0
  14. data/lib/action_controller/assertions/routing_assertions.rb +82 -0
  15. data/lib/action_controller/assertions/selector_assertions.rb +571 -0
  16. data/lib/action_controller/assertions/tag_assertions.rb +117 -0
  17. data/lib/action_controller/base.rb +334 -163
  18. data/lib/action_controller/benchmarking.rb +3 -6
  19. data/lib/action_controller/caching.rb +83 -22
  20. data/lib/action_controller/cgi_ext/cgi_ext.rb +0 -7
  21. data/lib/action_controller/cgi_ext/cgi_methods.rb +167 -173
  22. data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +43 -22
  23. data/lib/action_controller/cgi_process.rb +50 -27
  24. data/lib/action_controller/components.rb +21 -25
  25. data/lib/action_controller/cookies.rb +10 -9
  26. data/lib/action_controller/{dependencies.rb → deprecated_dependencies.rb} +9 -27
  27. data/lib/action_controller/filters.rb +448 -225
  28. data/lib/action_controller/flash.rb +24 -20
  29. data/lib/action_controller/helpers.rb +2 -5
  30. data/lib/action_controller/integration.rb +40 -16
  31. data/lib/action_controller/layout.rb +11 -8
  32. data/lib/action_controller/macros/auto_complete.rb +3 -2
  33. data/lib/action_controller/macros/in_place_editing.rb +3 -2
  34. data/lib/action_controller/mime_responds.rb +41 -29
  35. data/lib/action_controller/mime_type.rb +68 -10
  36. data/lib/action_controller/pagination.rb +4 -3
  37. data/lib/action_controller/request.rb +22 -14
  38. data/lib/action_controller/rescue.rb +25 -22
  39. data/lib/action_controller/resources.rb +302 -0
  40. data/lib/action_controller/response.rb +20 -2
  41. data/lib/action_controller/response.rb.rej +17 -0
  42. data/lib/action_controller/routing.rb +1165 -567
  43. data/lib/action_controller/scaffolding.rb +30 -31
  44. data/lib/action_controller/session/active_record_store.rb +2 -0
  45. data/lib/action_controller/session/drb_store.rb +4 -0
  46. data/lib/action_controller/session/mem_cache_store.rb +4 -0
  47. data/lib/action_controller/session_management.rb +6 -9
  48. data/lib/action_controller/status_codes.rb +89 -0
  49. data/lib/action_controller/streaming.rb +6 -15
  50. data/lib/action_controller/templates/rescues/_request_and_response.rhtml +5 -5
  51. data/lib/action_controller/templates/rescues/diagnostics.rhtml +2 -2
  52. data/lib/action_controller/templates/rescues/routing_error.rhtml +4 -4
  53. data/lib/action_controller/templates/rescues/template_error.rhtml +1 -1
  54. data/lib/action_controller/templates/scaffolds/list.rhtml +1 -1
  55. data/lib/action_controller/test_process.rb +52 -30
  56. data/lib/action_controller/url_rewriter.rb +63 -29
  57. data/lib/action_controller/vendor/html-scanner/html/document.rb +1 -0
  58. data/lib/action_controller/vendor/html-scanner/html/node.rb +3 -4
  59. data/lib/action_controller/vendor/html-scanner/html/selector.rb +822 -0
  60. data/lib/action_controller/verification.rb +22 -11
  61. data/lib/action_pack.rb +1 -1
  62. data/lib/action_pack/version.rb +2 -2
  63. data/lib/action_view.rb +1 -1
  64. data/lib/action_view/base.rb +46 -43
  65. data/lib/action_view/compiled_templates.rb +1 -1
  66. data/lib/action_view/helpers/active_record_helper.rb +54 -17
  67. data/lib/action_view/helpers/asset_tag_helper.rb +97 -46
  68. data/lib/action_view/helpers/capture_helper.rb +1 -1
  69. data/lib/action_view/helpers/date_helper.rb +258 -136
  70. data/lib/action_view/helpers/debug_helper.rb +1 -1
  71. data/lib/action_view/helpers/deprecated_helper.rb +34 -0
  72. data/lib/action_view/helpers/form_helper.rb +75 -35
  73. data/lib/action_view/helpers/form_options_helper.rb +7 -5
  74. data/lib/action_view/helpers/form_tag_helper.rb +44 -6
  75. data/lib/action_view/helpers/java_script_macros_helper.rb +59 -46
  76. data/lib/action_view/helpers/javascript_helper.rb +71 -10
  77. data/lib/action_view/helpers/javascripts/controls.js +41 -23
  78. data/lib/action_view/helpers/javascripts/dragdrop.js +105 -76
  79. data/lib/action_view/helpers/javascripts/effects.js +293 -163
  80. data/lib/action_view/helpers/javascripts/prototype.js +897 -389
  81. data/lib/action_view/helpers/javascripts/prototype.js.rej +561 -0
  82. data/lib/action_view/helpers/number_helper.rb +111 -65
  83. data/lib/action_view/helpers/prototype_helper.rb +84 -109
  84. data/lib/action_view/helpers/scriptaculous_helper.rb +5 -0
  85. data/lib/action_view/helpers/tag_helper.rb +69 -16
  86. data/lib/action_view/helpers/text_helper.rb +149 -112
  87. data/lib/action_view/helpers/url_helper.rb +200 -107
  88. data/lib/action_view/template_error.rb +66 -42
  89. data/test/abstract_unit.rb +4 -2
  90. data/test/active_record_unit.rb +84 -56
  91. data/test/activerecord/active_record_assertions_test.rb +26 -18
  92. data/test/activerecord/active_record_store_test.rb +4 -36
  93. data/test/activerecord/pagination_test.rb +1 -6
  94. data/test/controller/action_pack_assertions_test.rb +230 -113
  95. data/test/controller/addresses_render_test.rb +2 -6
  96. data/test/controller/assert_select_test.rb +576 -0
  97. data/test/controller/base_test.rb +73 -3
  98. data/test/controller/caching_test.rb +228 -0
  99. data/test/controller/capture_test.rb +12 -10
  100. data/test/controller/cgi_test.rb +89 -12
  101. data/test/controller/components_test.rb +24 -2
  102. data/test/controller/content_type_test.rb +139 -0
  103. data/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb +0 -0
  104. data/test/controller/controller_fixtures/app/controllers/user_controller.rb +0 -0
  105. data/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb +0 -0
  106. data/test/controller/cookie_test.rb +33 -25
  107. data/test/controller/deprecated_instance_variables_test.rb +48 -0
  108. data/test/controller/deprecation/deprecated_base_methods_test.rb +60 -0
  109. data/test/controller/fake_controllers.rb +0 -1
  110. data/test/controller/filters_test.rb +301 -16
  111. data/test/controller/flash_test.rb +19 -2
  112. data/test/controller/helper_test.rb +2 -2
  113. data/test/controller/integration_test.rb +154 -0
  114. data/test/controller/layout_test.rb +115 -1
  115. data/test/controller/mime_responds_test.rb +94 -0
  116. data/test/controller/mime_type_test.rb +9 -0
  117. data/test/controller/new_render_test.rb +161 -11
  118. data/test/controller/raw_post_test.rb +52 -15
  119. data/test/controller/redirect_test.rb +27 -14
  120. data/test/controller/render_test.rb +76 -29
  121. data/test/controller/request_test.rb +55 -4
  122. data/test/controller/resources_test.rb +274 -0
  123. data/test/controller/routing_test.rb +1533 -824
  124. data/test/controller/selector_test.rb +628 -0
  125. data/test/controller/send_file_test.rb +9 -1
  126. data/test/controller/session_management_test.rb +51 -0
  127. data/test/controller/test_test.rb +113 -29
  128. data/test/controller/url_rewriter_test.rb +86 -17
  129. data/test/controller/verification_test.rb +19 -17
  130. data/test/controller/webservice_test.rb +0 -7
  131. data/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml +1 -0
  132. data/test/fixtures/content_type/render_default_for_rhtml.rhtml +1 -0
  133. data/test/fixtures/content_type/render_default_for_rjs.rjs +1 -0
  134. data/test/fixtures/content_type/render_default_for_rxml.rxml +1 -0
  135. data/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml +1 -0
  136. data/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml +1 -0
  137. data/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml +1 -0
  138. data/test/fixtures/deprecated_instance_variables/_flash_method.rhtml +1 -0
  139. data/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml +1 -0
  140. data/test/fixtures/deprecated_instance_variables/_headers_method.rhtml +1 -0
  141. data/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml +1 -0
  142. data/test/fixtures/deprecated_instance_variables/_params_method.rhtml +1 -0
  143. data/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml +1 -0
  144. data/test/fixtures/deprecated_instance_variables/_request_method.rhtml +1 -0
  145. data/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml +1 -0
  146. data/test/fixtures/deprecated_instance_variables/_response_method.rhtml +1 -0
  147. data/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml +1 -0
  148. data/test/fixtures/deprecated_instance_variables/_session_method.rhtml +1 -0
  149. data/test/fixtures/multipart/binary_file +0 -0
  150. data/test/fixtures/public/javascripts/application.js +1 -0
  151. data/test/fixtures/test/_hello.rxml +1 -0
  152. data/test/fixtures/test/hello_world_container.rxml +3 -0
  153. data/test/fixtures/topic.rb +2 -2
  154. data/test/template/active_record_helper_test.rb +83 -12
  155. data/test/template/asset_tag_helper_test.rb +75 -95
  156. data/test/template/compiled_templates_test.rb +1 -0
  157. data/test/template/date_helper_test.rb +873 -181
  158. data/test/template/deprecated_helper_test.rb +36 -0
  159. data/test/template/deprecated_instance_variables_test.rb +43 -0
  160. data/test/template/form_helper_test.rb +77 -1
  161. data/test/template/form_options_helper_test.rb +4 -0
  162. data/test/template/form_tag_helper_test.rb +66 -2
  163. data/test/template/java_script_macros_helper_test.rb +4 -1
  164. data/test/template/javascript_helper_test.rb +29 -0
  165. data/test/template/number_helper_test.rb +63 -27
  166. data/test/template/prototype_helper_test.rb +77 -34
  167. data/test/template/tag_helper_test.rb +34 -6
  168. data/test/template/text_helper_test.rb +69 -34
  169. data/test/template/url_helper_test.rb +168 -16
  170. data/test/testing_sandbox.rb +7 -22
  171. metadata +66 -20
  172. data/filler.txt +0 -50
  173. data/lib/action_controller/code_generation.rb +0 -235
  174. data/lib/action_controller/vendor/xml_simple.rb +0 -1019
  175. data/test/controller/caching_filestore.rb +0 -74
  176. data/test/fixtures/application_root/app/controllers/a_class_that_contains_a_controller/poorly_placed_controller.rb +0 -7
  177. data/test/fixtures/application_root/app/controllers/module_that_holds_controllers/nested_controller.rb +0 -3
  178. data/test/fixtures/application_root/app/models/a_class_that_contains_a_controller.rb +0 -7
  179. data/test/fixtures/dont_load.rb +0 -3
@@ -1,5 +1,5 @@
1
- /* Prototype JavaScript framework, version 1.5.0_rc0
2
- * (c) 2005 Sam Stephenson <sam@conio.net>
1
+ /* Prototype JavaScript framework, version 1.5.0
2
+ * (c) 2005-2007 Sam Stephenson
3
3
  *
4
4
  * Prototype is freely distributable under the terms of an MIT-style license.
5
5
  * For details, see the Prototype web site: http://prototype.conio.net/
@@ -7,11 +7,14 @@
7
7
  /*--------------------------------------------------------------------------*/
8
8
 
9
9
  var Prototype = {
10
- Version: '1.5.0_rc0',
11
- ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
10
+ Version: '1.5.0',
11
+ BrowserFeatures: {
12
+ XPath: !!document.evaluate
13
+ },
12
14
 
15
+ ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
13
16
  emptyFunction: function() {},
14
- K: function(x) {return x}
17
+ K: function(x) { return x }
15
18
  }
16
19
 
17
20
  var Class = {
@@ -31,16 +34,36 @@ Object.extend = function(destination, source) {
31
34
  return destination;
32
35
  }
33
36
 
34
- Object.inspect = function(object) {
35
- try {
36
- if (object == undefined) return 'undefined';
37
- if (object == null) return 'null';
38
- return object.inspect ? object.inspect() : object.toString();
39
- } catch (e) {
40
- if (e instanceof RangeError) return '...';
41
- throw e;
37
+ Object.extend(Object, {
38
+ inspect: function(object) {
39
+ try {
40
+ if (object === undefined) return 'undefined';
41
+ if (object === null) return 'null';
42
+ return object.inspect ? object.inspect() : object.toString();
43
+ } catch (e) {
44
+ if (e instanceof RangeError) return '...';
45
+ throw e;
46
+ }
47
+ },
48
+
49
+ keys: function(object) {
50
+ var keys = [];
51
+ for (var property in object)
52
+ keys.push(property);
53
+ return keys;
54
+ },
55
+
56
+ values: function(object) {
57
+ var values = [];
58
+ for (var property in object)
59
+ values.push(object[property]);
60
+ return values;
61
+ },
62
+
63
+ clone: function(object) {
64
+ return Object.extend({}, object);
42
65
  }
43
- }
66
+ });
44
67
 
45
68
  Function.prototype.bind = function() {
46
69
  var __method = this, args = $A(arguments), object = args.shift();
@@ -50,9 +73,9 @@ Function.prototype.bind = function() {
50
73
  }
51
74
 
52
75
  Function.prototype.bindAsEventListener = function(object) {
53
- var __method = this;
76
+ var __method = this, args = $A(arguments), object = args.shift();
54
77
  return function(event) {
55
- return __method.call(object, event || window.event);
78
+ return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
56
79
  }
57
80
  }
58
81
 
@@ -77,7 +100,7 @@ var Try = {
77
100
  these: function() {
78
101
  var returnValue;
79
102
 
80
- for (var i = 0; i < arguments.length; i++) {
103
+ for (var i = 0, length = arguments.length; i < length; i++) {
81
104
  var lambda = arguments[i];
82
105
  try {
83
106
  returnValue = lambda();
@@ -102,20 +125,30 @@ PeriodicalExecuter.prototype = {
102
125
  },
103
126
 
104
127
  registerCallback: function() {
105
- setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
128
+ this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
129
+ },
130
+
131
+ stop: function() {
132
+ if (!this.timer) return;
133
+ clearInterval(this.timer);
134
+ this.timer = null;
106
135
  },
107
136
 
108
137
  onTimerEvent: function() {
109
138
  if (!this.currentlyExecuting) {
110
139
  try {
111
140
  this.currentlyExecuting = true;
112
- this.callback();
141
+ this.callback(this);
113
142
  } finally {
114
143
  this.currentlyExecuting = false;
115
144
  }
116
145
  }
117
146
  }
118
147
  }
148
+ String.interpret = function(value){
149
+ return value == null ? '' : String(value);
150
+ }
151
+
119
152
  Object.extend(String.prototype, {
120
153
  gsub: function(pattern, replacement) {
121
154
  var result = '', source = this, match;
@@ -124,7 +157,7 @@ Object.extend(String.prototype, {
124
157
  while (source.length > 0) {
125
158
  if (match = source.match(pattern)) {
126
159
  result += source.slice(0, match.index);
127
- result += (replacement(match) || '').toString();
160
+ result += String.interpret(replacement(match));
128
161
  source = source.slice(match.index + match[0].length);
129
162
  } else {
130
163
  result += source, source = '';
@@ -189,15 +222,28 @@ Object.extend(String.prototype, {
189
222
  unescapeHTML: function() {
190
223
  var div = document.createElement('div');
191
224
  div.innerHTML = this.stripTags();
192
- return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
225
+ return div.childNodes[0] ? (div.childNodes.length > 1 ?
226
+ $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) :
227
+ div.childNodes[0].nodeValue) : '';
193
228
  },
194
229
 
195
- toQueryParams: function() {
196
- var pairs = this.match(/^\??(.*)$/)[1].split('&');
197
- return pairs.inject({}, function(params, pairString) {
198
- var pair = pairString.split('=');
199
- params[pair[0]] = pair[1];
200
- return params;
230
+ toQueryParams: function(separator) {
231
+ var match = this.strip().match(/([^?#]*)(#.*)?$/);
232
+ if (!match) return {};
233
+
234
+ return match[1].split(separator || '&').inject({}, function(hash, pair) {
235
+ if ((pair = pair.split('='))[0]) {
236
+ var name = decodeURIComponent(pair[0]);
237
+ var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
238
+
239
+ if (hash[name] !== undefined) {
240
+ if (hash[name].constructor != Array)
241
+ hash[name] = [hash[name]];
242
+ if (value) hash[name].push(value);
243
+ }
244
+ else hash[name] = value;
245
+ }
246
+ return hash;
201
247
  });
202
248
  },
203
249
 
@@ -205,24 +251,43 @@ Object.extend(String.prototype, {
205
251
  return this.split('');
206
252
  },
207
253
 
254
+ succ: function() {
255
+ return this.slice(0, this.length - 1) +
256
+ String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
257
+ },
258
+
208
259
  camelize: function() {
209
- var oStringList = this.split('-');
210
- if (oStringList.length == 1) return oStringList[0];
260
+ var parts = this.split('-'), len = parts.length;
261
+ if (len == 1) return parts[0];
211
262
 
212
- var camelizedString = this.indexOf('-') == 0
213
- ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
214
- : oStringList[0];
263
+ var camelized = this.charAt(0) == '-'
264
+ ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
265
+ : parts[0];
215
266
 
216
- for (var i = 1, len = oStringList.length; i < len; i++) {
217
- var s = oStringList[i];
218
- camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
219
- }
267
+ for (var i = 1; i < len; i++)
268
+ camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
220
269
 
221
- return camelizedString;
270
+ return camelized;
222
271
  },
223
272
 
224
- inspect: function() {
225
- return "'" + this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + "'";
273
+ capitalize: function(){
274
+ return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
275
+ },
276
+
277
+ underscore: function() {
278
+ return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
279
+ },
280
+
281
+ dasherize: function() {
282
+ return this.gsub(/_/,'-');
283
+ },
284
+
285
+ inspect: function(useDoubleQuotes) {
286
+ var escapedString = this.replace(/\\/g, '\\\\');
287
+ if (useDoubleQuotes)
288
+ return '"' + escapedString.replace(/"/g, '\\"') + '"';
289
+ else
290
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
226
291
  }
227
292
  });
228
293
 
@@ -246,7 +311,7 @@ Template.prototype = {
246
311
  return this.template.gsub(this.pattern, function(match) {
247
312
  var before = match[1];
248
313
  if (before == '\\') return match[2];
249
- return before + (object[match[3]] || '').toString();
314
+ return before + String.interpret(object[match[3]]);
250
315
  });
251
316
  }
252
317
  }
@@ -268,6 +333,14 @@ var Enumerable = {
268
333
  } catch (e) {
269
334
  if (e != $break) throw e;
270
335
  }
336
+ return this;
337
+ },
338
+
339
+ eachSlice: function(number, iterator) {
340
+ var index = -number, slices = [], array = this.toArray();
341
+ while ((index += number) < array.length)
342
+ slices.push(array.slice(index, index+number));
343
+ return slices.map(iterator);
271
344
  },
272
345
 
273
346
  all: function(iterator) {
@@ -280,7 +353,7 @@ var Enumerable = {
280
353
  },
281
354
 
282
355
  any: function(iterator) {
283
- var result = true;
356
+ var result = false;
284
357
  this.each(function(value, index) {
285
358
  if (result = !!(iterator || Prototype.K)(value, index))
286
359
  throw $break;
@@ -291,12 +364,12 @@ var Enumerable = {
291
364
  collect: function(iterator) {
292
365
  var results = [];
293
366
  this.each(function(value, index) {
294
- results.push(iterator(value, index));
367
+ results.push((iterator || Prototype.K)(value, index));
295
368
  });
296
369
  return results;
297
370
  },
298
371
 
299
- detect: function (iterator) {
372
+ detect: function(iterator) {
300
373
  var result;
301
374
  this.each(function(value, index) {
302
375
  if (iterator(value, index)) {
@@ -337,6 +410,14 @@ var Enumerable = {
337
410
  return found;
338
411
  },
339
412
 
413
+ inGroupsOf: function(number, fillWith) {
414
+ fillWith = fillWith === undefined ? null : fillWith;
415
+ return this.eachSlice(number, function(slice) {
416
+ while(slice.length < number) slice.push(fillWith);
417
+ return slice;
418
+ });
419
+ },
420
+
340
421
  inject: function(memo, iterator) {
341
422
  this.each(function(value, index) {
342
423
  memo = iterator(memo, value, index);
@@ -346,7 +427,7 @@ var Enumerable = {
346
427
 
347
428
  invoke: function(method) {
348
429
  var args = $A(arguments).slice(1);
349
- return this.collect(function(value) {
430
+ return this.map(function(value) {
350
431
  return value[method].apply(value, args);
351
432
  });
352
433
  },
@@ -398,7 +479,7 @@ var Enumerable = {
398
479
  },
399
480
 
400
481
  sortBy: function(iterator) {
401
- return this.collect(function(value, index) {
482
+ return this.map(function(value, index) {
402
483
  return {value: value, criteria: iterator(value, index)};
403
484
  }).sort(function(left, right) {
404
485
  var a = left.criteria, b = right.criteria;
@@ -407,7 +488,7 @@ var Enumerable = {
407
488
  },
408
489
 
409
490
  toArray: function() {
410
- return this.collect(Prototype.K);
491
+ return this.map();
411
492
  },
412
493
 
413
494
  zip: function() {
@@ -421,6 +502,10 @@ var Enumerable = {
421
502
  });
422
503
  },
423
504
 
505
+ size: function() {
506
+ return this.toArray().length;
507
+ },
508
+
424
509
  inspect: function() {
425
510
  return '#<Enumerable:' + this.toArray().inspect() + '>';
426
511
  }
@@ -439,7 +524,7 @@ var $A = Array.from = function(iterable) {
439
524
  return iterable.toArray();
440
525
  } else {
441
526
  var results = [];
442
- for (var i = 0; i < iterable.length; i++)
527
+ for (var i = 0, length = iterable.length; i < length; i++)
443
528
  results.push(iterable[i]);
444
529
  return results;
445
530
  }
@@ -452,7 +537,7 @@ if (!Array.prototype._reverse)
452
537
 
453
538
  Object.extend(Array.prototype, {
454
539
  _each: function(iterator) {
455
- for (var i = 0; i < this.length; i++)
540
+ for (var i = 0, length = this.length; i < length; i++)
456
541
  iterator(this[i]);
457
542
  },
458
543
 
@@ -471,7 +556,7 @@ Object.extend(Array.prototype, {
471
556
 
472
557
  compact: function() {
473
558
  return this.select(function(value) {
474
- return value != undefined || value != null;
559
+ return value != null;
475
560
  });
476
561
  },
477
562
 
@@ -490,7 +575,7 @@ Object.extend(Array.prototype, {
490
575
  },
491
576
 
492
577
  indexOf: function(object) {
493
- for (var i = 0; i < this.length; i++)
578
+ for (var i = 0, length = this.length; i < length; i++)
494
579
  if (this[i] == object) return i;
495
580
  return -1;
496
581
  },
@@ -499,15 +584,88 @@ Object.extend(Array.prototype, {
499
584
  return (inline !== false ? this : this.toArray())._reverse();
500
585
  },
501
586
 
587
+ reduce: function() {
588
+ return this.length > 1 ? this : this[0];
589
+ },
590
+
591
+ uniq: function() {
592
+ return this.inject([], function(array, value) {
593
+ return array.include(value) ? array : array.concat([value]);
594
+ });
595
+ },
596
+
597
+ clone: function() {
598
+ return [].concat(this);
599
+ },
600
+
601
+ size: function() {
602
+ return this.length;
603
+ },
604
+
502
605
  inspect: function() {
503
606
  return '[' + this.map(Object.inspect).join(', ') + ']';
504
607
  }
505
608
  });
506
- var Hash = {
609
+
610
+ Array.prototype.toArray = Array.prototype.clone;
611
+
612
+ function $w(string){
613
+ string = string.strip();
614
+ return string ? string.split(/\s+/) : [];
615
+ }
616
+
617
+ if(window.opera){
618
+ Array.prototype.concat = function(){
619
+ 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++)
624
+ array.push(arguments[i][j]);
625
+ } else {
626
+ array.push(arguments[i]);
627
+ }
628
+ }
629
+ return array;
630
+ }
631
+ }
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, {
507
665
  _each: function(iterator) {
508
666
  for (var key in this) {
509
667
  var value = this[key];
510
- if (typeof value == 'function') continue;
668
+ if (value && value == Hash.prototype[key]) continue;
511
669
 
512
670
  var pair = [key, value];
513
671
  pair.key = key;
@@ -525,16 +683,30 @@ var Hash = {
525
683
  },
526
684
 
527
685
  merge: function(hash) {
528
- return $H(hash).inject($H(this), function(mergedHash, pair) {
686
+ return $H(hash).inject(this, function(mergedHash, pair) {
529
687
  mergedHash[pair.key] = pair.value;
530
688
  return mergedHash;
531
689
  });
532
690
  },
533
691
 
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;
706
+ },
707
+
534
708
  toQueryString: function() {
535
- return this.map(function(pair) {
536
- return pair.map(encodeURIComponent).join('=');
537
- }).join('&');
709
+ return Hash.toQueryString(this);
538
710
  },
539
711
 
540
712
  inspect: function() {
@@ -542,14 +714,12 @@ var Hash = {
542
714
  return pair.map(Object.inspect).join(': ');
543
715
  }).join(', ') + '}>';
544
716
  }
545
- }
717
+ });
546
718
 
547
719
  function $H(object) {
548
- var hash = Object.extend({}, object || {});
549
- Object.extend(hash, Enumerable);
550
- Object.extend(hash, Hash);
551
- return hash;
552
- }
720
+ if (object && object.constructor == Hash) return object;
721
+ return new Hash(object);
722
+ };
553
723
  ObjectRange = Class.create();
554
724
  Object.extend(ObjectRange.prototype, Enumerable);
555
725
  Object.extend(ObjectRange.prototype, {
@@ -561,10 +731,10 @@ Object.extend(ObjectRange.prototype, {
561
731
 
562
732
  _each: function(iterator) {
563
733
  var value = this.start;
564
- do {
734
+ while (this.include(value)) {
565
735
  iterator(value);
566
736
  value = value.succ();
567
- } while (this.include(value));
737
+ }
568
738
  },
569
739
 
570
740
  include: function(value) {
@@ -599,18 +769,18 @@ Ajax.Responders = {
599
769
  this.responders._each(iterator);
600
770
  },
601
771
 
602
- register: function(responderToAdd) {
603
- if (!this.include(responderToAdd))
604
- this.responders.push(responderToAdd);
772
+ register: function(responder) {
773
+ if (!this.include(responder))
774
+ this.responders.push(responder);
605
775
  },
606
776
 
607
- unregister: function(responderToRemove) {
608
- this.responders = this.responders.without(responderToRemove);
777
+ unregister: function(responder) {
778
+ this.responders = this.responders.without(responder);
609
779
  },
610
780
 
611
781
  dispatch: function(callback, request, transport, json) {
612
782
  this.each(function(responder) {
613
- if (responder[callback] && typeof responder[callback] == 'function') {
783
+ if (typeof responder[callback] == 'function') {
614
784
  try {
615
785
  responder[callback].apply(responder, [request, transport, json]);
616
786
  } catch (e) {}
@@ -625,7 +795,6 @@ Ajax.Responders.register({
625
795
  onCreate: function() {
626
796
  Ajax.activeRequestCount++;
627
797
  },
628
-
629
798
  onComplete: function() {
630
799
  Ajax.activeRequestCount--;
631
800
  }
@@ -638,19 +807,14 @@ Ajax.Base.prototype = {
638
807
  method: 'post',
639
808
  asynchronous: true,
640
809
  contentType: 'application/x-www-form-urlencoded',
810
+ encoding: 'UTF-8',
641
811
  parameters: ''
642
812
  }
643
813
  Object.extend(this.options, options || {});
644
- },
645
-
646
- responseIsSuccess: function() {
647
- return this.transport.status == undefined
648
- || this.transport.status == 0
649
- || (this.transport.status >= 200 && this.transport.status < 300);
650
- },
651
814
 
652
- responseIsFailure: function() {
653
- return !this.responseIsSuccess();
815
+ this.options.method = this.options.method.toLowerCase();
816
+ if (typeof this.options.parameters == 'string')
817
+ this.options.parameters = this.options.parameters.toQueryParams();
654
818
  }
655
819
  }
656
820
 
@@ -659,6 +823,8 @@ Ajax.Request.Events =
659
823
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
660
824
 
661
825
  Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
826
+ _complete: false,
827
+
662
828
  initialize: function(url, options) {
663
829
  this.transport = Ajax.getTransport();
664
830
  this.setOptions(options);
@@ -666,111 +832,145 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
666
832
  },
667
833
 
668
834
  request: function(url) {
669
- var parameters = this.options.parameters || '';
670
- if (parameters.length > 0) parameters += '&_=';
835
+ this.url = url;
836
+ var params = this.options.parameters, method = this.options.method;
671
837
 
672
- try {
673
- this.url = url;
674
- if (this.options.method == 'get' && parameters.length > 0)
675
- this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
838
+ if (!['get', 'post'].include(method)) {
839
+ // simulate other verbs over post
840
+ params['_method'] = method;
841
+ method = 'post';
842
+ }
676
843
 
844
+ params = Hash.toQueryString(params);
845
+ if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='
846
+
847
+ // when GET, append parameters to URL
848
+ if (method == 'get' && params)
849
+ this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;
850
+
851
+ try {
677
852
  Ajax.Responders.dispatch('onCreate', this, this.transport);
678
853
 
679
- this.transport.open(this.options.method, this.url,
854
+ this.transport.open(method.toUpperCase(), this.url,
680
855
  this.options.asynchronous);
681
856
 
682
- if (this.options.asynchronous) {
683
- this.transport.onreadystatechange = this.onStateChange.bind(this);
684
- setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
685
- }
857
+ if (this.options.asynchronous)
858
+ setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
686
859
 
860
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
687
861
  this.setRequestHeaders();
688
862
 
689
- var body = this.options.postBody ? this.options.postBody : parameters;
690
- this.transport.send(this.options.method == 'post' ? body : null);
863
+ var body = method == 'post' ? (this.options.postBody || params) : null;
691
864
 
692
- } catch (e) {
865
+ this.transport.send(body);
866
+
867
+ /* Force Firefox to handle ready state 4 for synchronous requests */
868
+ if (!this.options.asynchronous && this.transport.overrideMimeType)
869
+ this.onStateChange();
870
+
871
+ }
872
+ catch (e) {
693
873
  this.dispatchException(e);
694
874
  }
695
875
  },
696
876
 
877
+ onStateChange: function() {
878
+ var readyState = this.transport.readyState;
879
+ if (readyState > 1 && !((readyState == 4) && this._complete))
880
+ this.respondToReadyState(this.transport.readyState);
881
+ },
882
+
697
883
  setRequestHeaders: function() {
698
- var requestHeaders =
699
- ['X-Requested-With', 'XMLHttpRequest',
700
- 'X-Prototype-Version', Prototype.Version,
701
- 'Accept', 'text/javascript, text/html, application/xml, text/xml, */*'];
884
+ var headers = {
885
+ 'X-Requested-With': 'XMLHttpRequest',
886
+ 'X-Prototype-Version': Prototype.Version,
887
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
888
+ };
702
889
 
703
890
  if (this.options.method == 'post') {
704
- requestHeaders.push('Content-type', this.options.contentType);
891
+ headers['Content-type'] = this.options.contentType +
892
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
705
893
 
706
- /* Force "Connection: close" for Mozilla browsers to work around
707
- * a bug where XMLHttpReqeuest sends an incorrect Content-length
708
- * header. See Mozilla Bugzilla #246651.
894
+ /* Force "Connection: close" for older Mozilla browsers to work
895
+ * around a bug where XMLHttpRequest sends an incorrect
896
+ * Content-length header. See Mozilla Bugzilla #246651.
709
897
  */
710
- if (this.transport.overrideMimeType)
711
- requestHeaders.push('Connection', 'close');
898
+ if (this.transport.overrideMimeType &&
899
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
900
+ headers['Connection'] = 'close';
712
901
  }
713
902
 
714
- if (this.options.requestHeaders)
715
- requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
903
+ // user-defined headers
904
+ if (typeof this.options.requestHeaders == 'object') {
905
+ var extras = this.options.requestHeaders;
716
906
 
717
- for (var i = 0; i < requestHeaders.length; i += 2)
718
- this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
719
- },
720
-
721
- onStateChange: function() {
722
- var readyState = this.transport.readyState;
723
- if (readyState != 1)
724
- this.respondToReadyState(this.transport.readyState);
725
- },
726
-
727
- header: function(name) {
728
- try {
729
- return this.transport.getResponseHeader(name);
730
- } catch (e) {}
731
- },
907
+ if (typeof extras.push == 'function')
908
+ for (var i = 0, length = extras.length; i < length; i += 2)
909
+ headers[extras[i]] = extras[i+1];
910
+ else
911
+ $H(extras).each(function(pair) { headers[pair.key] = pair.value });
912
+ }
732
913
 
733
- evalJSON: function() {
734
- try {
735
- return eval('(' + this.header('X-JSON') + ')');
736
- } catch (e) {}
914
+ for (var name in headers)
915
+ this.transport.setRequestHeader(name, headers[name]);
737
916
  },
738
917
 
739
- evalResponse: function() {
740
- try {
741
- return eval(this.transport.responseText);
742
- } catch (e) {
743
- this.dispatchException(e);
744
- }
918
+ success: function() {
919
+ return !this.transport.status
920
+ || (this.transport.status >= 200 && this.transport.status < 300);
745
921
  },
746
922
 
747
923
  respondToReadyState: function(readyState) {
748
- var event = Ajax.Request.Events[readyState];
924
+ var state = Ajax.Request.Events[readyState];
749
925
  var transport = this.transport, json = this.evalJSON();
750
926
 
751
- if (event == 'Complete') {
927
+ if (state == 'Complete') {
752
928
  try {
929
+ this._complete = true;
753
930
  (this.options['on' + this.transport.status]
754
- || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
931
+ || this.options['on' + (this.success() ? 'Success' : 'Failure')]
755
932
  || Prototype.emptyFunction)(transport, json);
756
933
  } catch (e) {
757
934
  this.dispatchException(e);
758
935
  }
759
936
 
760
- if ((this.header('Content-type') || '').match(/^text\/javascript/i))
761
- this.evalResponse();
937
+ if ((this.getHeader('Content-type') || 'text/javascript').strip().
938
+ match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
939
+ this.evalResponse();
762
940
  }
763
941
 
764
942
  try {
765
- (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
766
- Ajax.Responders.dispatch('on' + event, this, transport, json);
943
+ (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
944
+ Ajax.Responders.dispatch('on' + state, this, transport, json);
767
945
  } catch (e) {
768
946
  this.dispatchException(e);
769
947
  }
770
948
 
771
- /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
772
- if (event == 'Complete')
949
+ if (state == 'Complete') {
950
+ // avoid memory leak in MSIE: clean up
773
951
  this.transport.onreadystatechange = Prototype.emptyFunction;
952
+ }
953
+ },
954
+
955
+ getHeader: function(name) {
956
+ try {
957
+ return this.transport.getResponseHeader(name);
958
+ } catch (e) { return null }
959
+ },
960
+
961
+ evalJSON: function() {
962
+ try {
963
+ var json = this.getHeader('X-JSON');
964
+ return json ? eval('(' + json + ')') : null;
965
+ } catch (e) { return null }
966
+ },
967
+
968
+ evalResponse: function() {
969
+ try {
970
+ return eval(this.transport.responseText);
971
+ } catch (e) {
972
+ this.dispatchException(e);
973
+ }
774
974
  },
775
975
 
776
976
  dispatchException: function(exception) {
@@ -783,41 +983,37 @@ Ajax.Updater = Class.create();
783
983
 
784
984
  Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
785
985
  initialize: function(container, url, options) {
786
- this.containers = {
787
- success: container.success ? $(container.success) : $(container),
788
- failure: container.failure ? $(container.failure) :
789
- (container.success ? null : $(container))
986
+ this.container = {
987
+ success: (container.success || container),
988
+ failure: (container.failure || (container.success ? null : container))
790
989
  }
791
990
 
792
991
  this.transport = Ajax.getTransport();
793
992
  this.setOptions(options);
794
993
 
795
994
  var onComplete = this.options.onComplete || Prototype.emptyFunction;
796
- this.options.onComplete = (function(transport, object) {
995
+ this.options.onComplete = (function(transport, param) {
797
996
  this.updateContent();
798
- onComplete(transport, object);
997
+ onComplete(transport, param);
799
998
  }).bind(this);
800
999
 
801
1000
  this.request(url);
802
1001
  },
803
1002
 
804
1003
  updateContent: function() {
805
- var receiver = this.responseIsSuccess() ?
806
- this.containers.success : this.containers.failure;
1004
+ var receiver = this.container[this.success() ? 'success' : 'failure'];
807
1005
  var response = this.transport.responseText;
808
1006
 
809
- if (!this.options.evalScripts)
810
- response = response.stripScripts();
1007
+ if (!this.options.evalScripts) response = response.stripScripts();
811
1008
 
812
- if (receiver) {
813
- if (this.options.insertion) {
1009
+ if (receiver = $(receiver)) {
1010
+ if (this.options.insertion)
814
1011
  new this.options.insertion(receiver, response);
815
- } else {
816
- Element.update(receiver, response);
817
- }
1012
+ else
1013
+ receiver.update(response);
818
1014
  }
819
1015
 
820
- if (this.responseIsSuccess()) {
1016
+ if (this.success()) {
821
1017
  if (this.onComplete)
822
1018
  setTimeout(this.onComplete.bind(this), 10);
823
1019
  }
@@ -846,7 +1042,7 @@ Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
846
1042
  },
847
1043
 
848
1044
  stop: function() {
849
- this.updater.onComplete = undefined;
1045
+ this.updater.options.onComplete = undefined;
850
1046
  clearTimeout(this.timer);
851
1047
  (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
852
1048
  },
@@ -866,25 +1062,43 @@ Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
866
1062
  this.updater = new Ajax.Updater(this.container, this.url, this.options);
867
1063
  }
868
1064
  });
869
- function $() {
870
- var results = [], element;
871
- for (var i = 0; i < arguments.length; i++) {
872
- element = arguments[i];
873
- if (typeof element == 'string')
874
- element = document.getElementById(element);
875
- results.push(Element.extend(element));
1065
+ function $(element) {
1066
+ if (arguments.length > 1) {
1067
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1068
+ elements.push($(arguments[i]));
1069
+ return elements;
876
1070
  }
877
- return results.length < 2 ? results[0] : results;
1071
+ if (typeof element == 'string')
1072
+ element = document.getElementById(element);
1073
+ return Element.extend(element);
1074
+ }
1075
+
1076
+ if (Prototype.BrowserFeatures.XPath) {
1077
+ document._getElementsByXPath = function(expression, parentElement) {
1078
+ var results = [];
1079
+ var query = document.evaluate(expression, $(parentElement) || document,
1080
+ null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1081
+ for (var i = 0, length = query.snapshotLength; i < length; i++)
1082
+ results.push(query.snapshotItem(i));
1083
+ return results;
1084
+ };
878
1085
  }
879
1086
 
880
1087
  document.getElementsByClassName = function(className, parentElement) {
881
- var children = ($(parentElement) || document.body).getElementsByTagName('*');
882
- return $A(children).inject([], function(elements, child) {
883
- if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
884
- elements.push(Element.extend(child));
1088
+ if (Prototype.BrowserFeatures.XPath) {
1089
+ var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
1090
+ return document._getElementsByXPath(q, parentElement);
1091
+ } else {
1092
+ var children = ($(parentElement) || document.body).getElementsByTagName('*');
1093
+ var elements = [], child;
1094
+ for (var i = 0, length = children.length; i < length; i++) {
1095
+ child = children[i];
1096
+ if (Element.hasClassName(child, className))
1097
+ elements.push(Element.extend(child));
1098
+ }
885
1099
  return elements;
886
- });
887
- }
1100
+ }
1101
+ };
888
1102
 
889
1103
  /*--------------------------------------------------------------------------*/
890
1104
 
@@ -892,21 +1106,28 @@ if (!window.Element)
892
1106
  var Element = new Object();
893
1107
 
894
1108
  Element.extend = function(element) {
895
- if (!element) return;
896
- if (_nativeExtensions) return element;
1109
+ if (!element || _nativeExtensions || element.nodeType == 3) return element;
897
1110
 
898
1111
  if (!element._extended && element.tagName && element != window) {
899
- var methods = Element.Methods, cache = Element.extend.cache;
900
- for (property in methods) {
1112
+ var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
1113
+
1114
+ if (element.tagName == 'FORM')
1115
+ Object.extend(methods, Form.Methods);
1116
+ if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
1117
+ Object.extend(methods, Form.Element.Methods);
1118
+
1119
+ Object.extend(methods, Element.Methods.Simulated);
1120
+
1121
+ for (var property in methods) {
901
1122
  var value = methods[property];
902
- if (typeof value == 'function')
1123
+ if (typeof value == 'function' && !(property in element))
903
1124
  element[property] = cache.findOrStore(value);
904
1125
  }
905
1126
  }
906
1127
 
907
1128
  element._extended = true;
908
1129
  return element;
909
- }
1130
+ };
910
1131
 
911
1132
  Element.extend.cache = {
912
1133
  findOrStore: function(value) {
@@ -914,46 +1135,45 @@ Element.extend.cache = {
914
1135
  return value.apply(null, [this].concat($A(arguments)));
915
1136
  }
916
1137
  }
917
- }
1138
+ };
918
1139
 
919
1140
  Element.Methods = {
920
1141
  visible: function(element) {
921
1142
  return $(element).style.display != 'none';
922
1143
  },
923
1144
 
924
- toggle: function() {
925
- for (var i = 0; i < arguments.length; i++) {
926
- var element = $(arguments[i]);
927
- Element[Element.visible(element) ? 'hide' : 'show'](element);
928
- }
1145
+ toggle: function(element) {
1146
+ element = $(element);
1147
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
1148
+ return element;
929
1149
  },
930
1150
 
931
- hide: function() {
932
- for (var i = 0; i < arguments.length; i++) {
933
- var element = $(arguments[i]);
934
- element.style.display = 'none';
935
- }
1151
+ hide: function(element) {
1152
+ $(element).style.display = 'none';
1153
+ return element;
936
1154
  },
937
1155
 
938
- show: function() {
939
- for (var i = 0; i < arguments.length; i++) {
940
- var element = $(arguments[i]);
941
- element.style.display = '';
942
- }
1156
+ show: function(element) {
1157
+ $(element).style.display = '';
1158
+ return element;
943
1159
  },
944
1160
 
945
1161
  remove: function(element) {
946
1162
  element = $(element);
947
1163
  element.parentNode.removeChild(element);
1164
+ return element;
948
1165
  },
949
1166
 
950
1167
  update: function(element, html) {
1168
+ html = typeof html == 'undefined' ? '' : html.toString();
951
1169
  $(element).innerHTML = html.stripScripts();
952
1170
  setTimeout(function() {html.evalScripts()}, 10);
1171
+ return element;
953
1172
  },
954
1173
 
955
1174
  replace: function(element, html) {
956
1175
  element = $(element);
1176
+ html = typeof html == 'undefined' ? '' : html.toString();
957
1177
  if (element.outerHTML) {
958
1178
  element.outerHTML = html.stripScripts();
959
1179
  } else {
@@ -963,11 +1183,106 @@ Element.Methods = {
963
1183
  range.createContextualFragment(html.stripScripts()), element);
964
1184
  }
965
1185
  setTimeout(function() {html.evalScripts()}, 10);
1186
+ return element;
966
1187
  },
967
1188
 
968
- getHeight: function(element) {
1189
+ inspect: function(element) {
1190
+ element = $(element);
1191
+ var result = '<' + element.tagName.toLowerCase();
1192
+ $H({'id': 'id', 'className': 'class'}).each(function(pair) {
1193
+ var property = pair.first(), attribute = pair.last();
1194
+ var value = (element[property] || '').toString();
1195
+ if (value) result += ' ' + attribute + '=' + value.inspect(true);
1196
+ });
1197
+ return result + '>';
1198
+ },
1199
+
1200
+ recursivelyCollect: function(element, property) {
1201
+ element = $(element);
1202
+ var elements = [];
1203
+ while (element = element[property])
1204
+ if (element.nodeType == 1)
1205
+ elements.push(Element.extend(element));
1206
+ return elements;
1207
+ },
1208
+
1209
+ ancestors: function(element) {
1210
+ return $(element).recursivelyCollect('parentNode');
1211
+ },
1212
+
1213
+ descendants: function(element) {
1214
+ return $A($(element).getElementsByTagName('*'));
1215
+ },
1216
+
1217
+ immediateDescendants: function(element) {
1218
+ if (!(element = $(element).firstChild)) return [];
1219
+ while (element && element.nodeType != 1) element = element.nextSibling;
1220
+ if (element) return [element].concat($(element).nextSiblings());
1221
+ return [];
1222
+ },
1223
+
1224
+ previousSiblings: function(element) {
1225
+ return $(element).recursivelyCollect('previousSibling');
1226
+ },
1227
+
1228
+ nextSiblings: function(element) {
1229
+ return $(element).recursivelyCollect('nextSibling');
1230
+ },
1231
+
1232
+ siblings: function(element) {
969
1233
  element = $(element);
970
- return element.offsetHeight;
1234
+ return element.previousSiblings().reverse().concat(element.nextSiblings());
1235
+ },
1236
+
1237
+ match: function(element, selector) {
1238
+ if (typeof selector == 'string')
1239
+ selector = new Selector(selector);
1240
+ return selector.match($(element));
1241
+ },
1242
+
1243
+ up: function(element, expression, index) {
1244
+ return Selector.findElement($(element).ancestors(), expression, index);
1245
+ },
1246
+
1247
+ down: function(element, expression, index) {
1248
+ return Selector.findElement($(element).descendants(), expression, index);
1249
+ },
1250
+
1251
+ previous: function(element, expression, index) {
1252
+ return Selector.findElement($(element).previousSiblings(), expression, index);
1253
+ },
1254
+
1255
+ next: function(element, expression, index) {
1256
+ return Selector.findElement($(element).nextSiblings(), expression, index);
1257
+ },
1258
+
1259
+ getElementsBySelector: function() {
1260
+ var args = $A(arguments), element = $(args.shift());
1261
+ return Selector.findChildElements(element, args);
1262
+ },
1263
+
1264
+ getElementsByClassName: function(element, className) {
1265
+ return document.getElementsByClassName(className, element);
1266
+ },
1267
+
1268
+ readAttribute: function(element, name) {
1269
+ element = $(element);
1270
+ if (document.all && !window.opera) {
1271
+ var t = Element._attributeTranslations;
1272
+ if (t.values[name]) return t.values[name](element, name);
1273
+ if (t.names[name]) name = t.names[name];
1274
+ var attribute = element.attributes[name];
1275
+ if(attribute) return attribute.nodeValue;
1276
+ }
1277
+ return element.getAttribute(name);
1278
+ },
1279
+
1280
+ getHeight: function(element) {
1281
+ return $(element).getDimensions().height;
1282
+ },
1283
+
1284
+ getWidth: function(element) {
1285
+ return $(element).getDimensions().width;
971
1286
  },
972
1287
 
973
1288
  classNames: function(element) {
@@ -976,34 +1291,60 @@ Element.Methods = {
976
1291
 
977
1292
  hasClassName: function(element, className) {
978
1293
  if (!(element = $(element))) return;
979
- return Element.classNames(element).include(className);
1294
+ var elementClassName = element.className;
1295
+ if (elementClassName.length == 0) return false;
1296
+ if (elementClassName == className ||
1297
+ elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
1298
+ return true;
1299
+ return false;
980
1300
  },
981
1301
 
982
1302
  addClassName: function(element, className) {
983
1303
  if (!(element = $(element))) return;
984
- return Element.classNames(element).add(className);
1304
+ Element.classNames(element).add(className);
1305
+ return element;
985
1306
  },
986
1307
 
987
1308
  removeClassName: function(element, className) {
988
1309
  if (!(element = $(element))) return;
989
- return Element.classNames(element).remove(className);
1310
+ Element.classNames(element).remove(className);
1311
+ return element;
1312
+ },
1313
+
1314
+ toggleClassName: function(element, className) {
1315
+ if (!(element = $(element))) return;
1316
+ Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
1317
+ return element;
1318
+ },
1319
+
1320
+ observe: function() {
1321
+ Event.observe.apply(Event, arguments);
1322
+ return $A(arguments).first();
1323
+ },
1324
+
1325
+ stopObserving: function() {
1326
+ Event.stopObserving.apply(Event, arguments);
1327
+ return $A(arguments).first();
990
1328
  },
991
1329
 
992
1330
  // removes whitespace-only text node children
993
1331
  cleanWhitespace: function(element) {
994
1332
  element = $(element);
995
- for (var i = 0; i < element.childNodes.length; i++) {
996
- var node = element.childNodes[i];
1333
+ var node = element.firstChild;
1334
+ while (node) {
1335
+ var nextNode = node.nextSibling;
997
1336
  if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
998
- Element.remove(node);
1337
+ element.removeChild(node);
1338
+ node = nextNode;
999
1339
  }
1340
+ return element;
1000
1341
  },
1001
1342
 
1002
1343
  empty: function(element) {
1003
1344
  return $(element).innerHTML.match(/^\s*$/);
1004
1345
  },
1005
1346
 
1006
- childOf: function(element, ancestor) {
1347
+ descendantOf: function(element, ancestor) {
1007
1348
  element = $(element), ancestor = $(ancestor);
1008
1349
  while (element = element.parentNode)
1009
1350
  if (element == ancestor) return true;
@@ -1012,38 +1353,69 @@ Element.Methods = {
1012
1353
 
1013
1354
  scrollTo: function(element) {
1014
1355
  element = $(element);
1015
- var x = element.x ? element.x : element.offsetLeft,
1016
- y = element.y ? element.y : element.offsetTop;
1017
- window.scrollTo(x, y);
1356
+ var pos = Position.cumulativeOffset(element);
1357
+ window.scrollTo(pos[0], pos[1]);
1358
+ return element;
1018
1359
  },
1019
1360
 
1020
1361
  getStyle: function(element, style) {
1021
1362
  element = $(element);
1022
- var value = element.style[style.camelize()];
1363
+ if (['float','cssFloat'].include(style))
1364
+ style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
1365
+ style = style.camelize();
1366
+ var value = element.style[style];
1023
1367
  if (!value) {
1024
1368
  if (document.defaultView && document.defaultView.getComputedStyle) {
1025
1369
  var css = document.defaultView.getComputedStyle(element, null);
1026
- value = css ? css.getPropertyValue(style) : null;
1370
+ value = css ? css[style] : null;
1027
1371
  } else if (element.currentStyle) {
1028
- value = element.currentStyle[style.camelize()];
1372
+ value = element.currentStyle[style];
1029
1373
  }
1030
1374
  }
1031
1375
 
1376
+ if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
1377
+ value = element['offset'+style.capitalize()] + 'px';
1378
+
1032
1379
  if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
1033
1380
  if (Element.getStyle(element, 'position') == 'static') value = 'auto';
1034
-
1381
+ if(style == 'opacity') {
1382
+ if(value) return parseFloat(value);
1383
+ if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
1384
+ if(value[1]) return parseFloat(value[1]) / 100;
1385
+ return 1.0;
1386
+ }
1035
1387
  return value == 'auto' ? null : value;
1036
1388
  },
1037
1389
 
1038
1390
  setStyle: function(element, style) {
1039
1391
  element = $(element);
1040
- for (var name in style)
1041
- element.style[name.camelize()] = style[name];
1392
+ for (var name in style) {
1393
+ var value = style[name];
1394
+ if(name == 'opacity') {
1395
+ if (value == 1) {
1396
+ value = (/Gecko/.test(navigator.userAgent) &&
1397
+ !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
1398
+ if(/MSIE/.test(navigator.userAgent) && !window.opera)
1399
+ element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
1400
+ } else if(value == '') {
1401
+ if(/MSIE/.test(navigator.userAgent) && !window.opera)
1402
+ element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
1403
+ } else {
1404
+ if(value < 0.00001) value = 0;
1405
+ if(/MSIE/.test(navigator.userAgent) && !window.opera)
1406
+ element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
1407
+ 'alpha(opacity='+value*100+')';
1408
+ }
1409
+ } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
1410
+ element.style[name.camelize()] = value;
1411
+ }
1412
+ return element;
1042
1413
  },
1043
1414
 
1044
1415
  getDimensions: function(element) {
1045
1416
  element = $(element);
1046
- if (Element.getStyle(element, 'display') != 'none')
1417
+ var display = $(element).getStyle('display');
1418
+ if (display != 'none' && display != null) // Safari bug
1047
1419
  return {width: element.offsetWidth, height: element.offsetHeight};
1048
1420
 
1049
1421
  // All *Width and *Height properties give 0 on elements with display none,
@@ -1051,12 +1423,13 @@ Element.Methods = {
1051
1423
  var els = element.style;
1052
1424
  var originalVisibility = els.visibility;
1053
1425
  var originalPosition = els.position;
1426
+ var originalDisplay = els.display;
1054
1427
  els.visibility = 'hidden';
1055
1428
  els.position = 'absolute';
1056
- els.display = '';
1429
+ els.display = 'block';
1057
1430
  var originalWidth = element.clientWidth;
1058
1431
  var originalHeight = element.clientHeight;
1059
- els.display = 'none';
1432
+ els.display = originalDisplay;
1060
1433
  els.position = originalPosition;
1061
1434
  els.visibility = originalVisibility;
1062
1435
  return {width: originalWidth, height: originalHeight};
@@ -1075,6 +1448,7 @@ Element.Methods = {
1075
1448
  element.style.left = 0;
1076
1449
  }
1077
1450
  }
1451
+ return element;
1078
1452
  },
1079
1453
 
1080
1454
  undoPositioned: function(element) {
@@ -1087,49 +1461,153 @@ Element.Methods = {
1087
1461
  element.style.bottom =
1088
1462
  element.style.right = '';
1089
1463
  }
1464
+ return element;
1090
1465
  },
1091
1466
 
1092
1467
  makeClipping: function(element) {
1093
1468
  element = $(element);
1094
- if (element._overflow) return;
1095
- element._overflow = element.style.overflow;
1469
+ if (element._overflow) return element;
1470
+ element._overflow = element.style.overflow || 'auto';
1096
1471
  if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
1097
1472
  element.style.overflow = 'hidden';
1473
+ return element;
1098
1474
  },
1099
1475
 
1100
1476
  undoClipping: function(element) {
1101
1477
  element = $(element);
1102
- if (element._overflow) return;
1103
- element.style.overflow = element._overflow;
1104
- element._overflow = undefined;
1478
+ if (!element._overflow) return element;
1479
+ element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
1480
+ element._overflow = null;
1481
+ return element;
1105
1482
  }
1106
- }
1483
+ };
1484
+
1485
+ Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
1486
+
1487
+ Element._attributeTranslations = {};
1488
+
1489
+ Element._attributeTranslations.names = {
1490
+ colspan: "colSpan",
1491
+ rowspan: "rowSpan",
1492
+ valign: "vAlign",
1493
+ datetime: "dateTime",
1494
+ accesskey: "accessKey",
1495
+ tabindex: "tabIndex",
1496
+ enctype: "encType",
1497
+ maxlength: "maxLength",
1498
+ readonly: "readOnly",
1499
+ longdesc: "longDesc"
1500
+ };
1501
+
1502
+ Element._attributeTranslations.values = {
1503
+ _getAttr: function(element, attribute) {
1504
+ return element.getAttribute(attribute, 2);
1505
+ },
1506
+
1507
+ _flag: function(element, attribute) {
1508
+ return $(element).hasAttribute(attribute) ? attribute : null;
1509
+ },
1510
+
1511
+ style: function(element) {
1512
+ return element.style.cssText.toLowerCase();
1513
+ },
1514
+
1515
+ title: function(element) {
1516
+ var node = element.getAttributeNode('title');
1517
+ return node.specified ? node.nodeValue : null;
1518
+ }
1519
+ };
1520
+
1521
+ Object.extend(Element._attributeTranslations.values, {
1522
+ href: Element._attributeTranslations.values._getAttr,
1523
+ src: Element._attributeTranslations.values._getAttr,
1524
+ disabled: Element._attributeTranslations.values._flag,
1525
+ checked: Element._attributeTranslations.values._flag,
1526
+ readonly: Element._attributeTranslations.values._flag,
1527
+ multiple: Element._attributeTranslations.values._flag
1528
+ });
1529
+
1530
+ Element.Methods.Simulated = {
1531
+ hasAttribute: function(element, attribute) {
1532
+ var t = Element._attributeTranslations;
1533
+ attribute = t.names[attribute] || attribute;
1534
+ return $(element).getAttributeNode(attribute).specified;
1535
+ }
1536
+ };
1537
+
1538
+ // IE is missing .innerHTML support for TABLE-related elements
1539
+ if (document.all && !window.opera){
1540
+ Element.Methods.update = function(element, html) {
1541
+ element = $(element);
1542
+ html = typeof html == 'undefined' ? '' : html.toString();
1543
+ var tagName = element.tagName.toUpperCase();
1544
+ if (['THEAD','TBODY','TR','TD'].include(tagName)) {
1545
+ var div = document.createElement('div');
1546
+ switch (tagName) {
1547
+ case 'THEAD':
1548
+ case 'TBODY':
1549
+ div.innerHTML = '<table><tbody>' + html.stripScripts() + '</tbody></table>';
1550
+ depth = 2;
1551
+ break;
1552
+ case 'TR':
1553
+ div.innerHTML = '<table><tbody><tr>' + html.stripScripts() + '</tr></tbody></table>';
1554
+ depth = 3;
1555
+ break;
1556
+ case 'TD':
1557
+ div.innerHTML = '<table><tbody><tr><td>' + html.stripScripts() + '</td></tr></tbody></table>';
1558
+ depth = 4;
1559
+ }
1560
+ $A(element.childNodes).each(function(node){
1561
+ element.removeChild(node)
1562
+ });
1563
+ depth.times(function(){ div = div.firstChild });
1564
+
1565
+ $A(div.childNodes).each(
1566
+ function(node){ element.appendChild(node) });
1567
+ } else {
1568
+ element.innerHTML = html.stripScripts();
1569
+ }
1570
+ setTimeout(function() {html.evalScripts()}, 10);
1571
+ return element;
1572
+ }
1573
+ };
1107
1574
 
1108
1575
  Object.extend(Element, Element.Methods);
1109
1576
 
1110
1577
  var _nativeExtensions = false;
1111
1578
 
1112
- if(!HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
1113
- var HTMLElement = {}
1114
- HTMLElement.prototype = document.createElement('div').__proto__;
1115
- }
1579
+ if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1580
+ ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
1581
+ var className = 'HTML' + tag + 'Element';
1582
+ if(window[className]) return;
1583
+ var klass = window[className] = {};
1584
+ klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
1585
+ });
1116
1586
 
1117
1587
  Element.addMethods = function(methods) {
1118
1588
  Object.extend(Element.Methods, methods || {});
1119
1589
 
1120
- if(typeof HTMLElement != 'undefined') {
1121
- var methods = Element.Methods, cache = Element.extend.cache;
1122
- for (property in methods) {
1590
+ function copy(methods, destination, onlyIfAbsent) {
1591
+ onlyIfAbsent = onlyIfAbsent || false;
1592
+ var cache = Element.extend.cache;
1593
+ for (var property in methods) {
1123
1594
  var value = methods[property];
1124
- if (typeof value == 'function')
1125
- HTMLElement.prototype[property] = cache.findOrStore(value);
1595
+ if (!onlyIfAbsent || !(property in destination))
1596
+ destination[property] = cache.findOrStore(value);
1126
1597
  }
1598
+ }
1599
+
1600
+ if (typeof HTMLElement != 'undefined') {
1601
+ copy(Element.Methods, HTMLElement.prototype);
1602
+ copy(Element.Methods.Simulated, HTMLElement.prototype, true);
1603
+ copy(Form.Methods, HTMLFormElement.prototype);
1604
+ [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
1605
+ copy(Form.Element.Methods, klass.prototype);
1606
+ });
1127
1607
  _nativeExtensions = true;
1128
1608
  }
1129
1609
  }
1130
1610
 
1131
- Element.addMethods();
1132
-
1133
1611
  var Toggle = new Object();
1134
1612
  Toggle.display = Element.toggle;
1135
1613
 
@@ -1148,8 +1626,8 @@ Abstract.Insertion.prototype = {
1148
1626
  try {
1149
1627
  this.element.insertAdjacentHTML(this.adjacency, this.content);
1150
1628
  } catch (e) {
1151
- var tagName = this.element.tagName.toLowerCase();
1152
- if (tagName == 'tbody' || tagName == 'tr') {
1629
+ var tagName = this.element.tagName.toUpperCase();
1630
+ if (['TBODY', 'TR'].include(tagName)) {
1153
1631
  this.insertContent(this.contentFromAnonymousTable());
1154
1632
  } else {
1155
1633
  throw e;
@@ -1248,20 +1726,18 @@ Element.ClassNames.prototype = {
1248
1726
 
1249
1727
  add: function(classNameToAdd) {
1250
1728
  if (this.include(classNameToAdd)) return;
1251
- this.set(this.toArray().concat(classNameToAdd).join(' '));
1729
+ this.set($A(this).concat(classNameToAdd).join(' '));
1252
1730
  },
1253
1731
 
1254
1732
  remove: function(classNameToRemove) {
1255
1733
  if (!this.include(classNameToRemove)) return;
1256
- this.set(this.select(function(className) {
1257
- return className != classNameToRemove;
1258
- }).join(' '));
1734
+ this.set($A(this).without(classNameToRemove).join(' '));
1259
1735
  },
1260
1736
 
1261
1737
  toString: function() {
1262
- return this.toArray().join(' ');
1738
+ return $A(this).join(' ');
1263
1739
  }
1264
- }
1740
+ };
1265
1741
 
1266
1742
  Object.extend(Element.ClassNames.prototype, Enumerable);
1267
1743
  var Selector = Class.create();
@@ -1308,15 +1784,15 @@ Selector.prototype = {
1308
1784
  if (params.wildcard)
1309
1785
  conditions.push('true');
1310
1786
  if (clause = params.id)
1311
- conditions.push('element.id == ' + clause.inspect());
1787
+ conditions.push('element.readAttribute("id") == ' + clause.inspect());
1312
1788
  if (clause = params.tagName)
1313
1789
  conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
1314
1790
  if ((clause = params.classNames).length > 0)
1315
- for (var i = 0; i < clause.length; i++)
1316
- conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
1791
+ for (var i = 0, length = clause.length; i < length; i++)
1792
+ conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
1317
1793
  if (clause = params.attributes) {
1318
1794
  clause.each(function(attribute) {
1319
- var value = 'element.getAttribute(' + attribute.name.inspect() + ')';
1795
+ var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
1320
1796
  var splitValueBy = function(delimiter) {
1321
1797
  return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
1322
1798
  }
@@ -1329,7 +1805,7 @@ Selector.prototype = {
1329
1805
  ); break;
1330
1806
  case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break;
1331
1807
  case '':
1332
- case undefined: conditions.push(value + ' != null'); break;
1808
+ case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
1333
1809
  default: throw 'Unknown operator ' + attribute.operator + ' in selector';
1334
1810
  }
1335
1811
  });
@@ -1340,6 +1816,7 @@ Selector.prototype = {
1340
1816
 
1341
1817
  compileMatcher: function() {
1342
1818
  this.match = new Function('element', 'if (!element.tagName) return false; \
1819
+ element = $(element); \
1343
1820
  return ' + this.buildMatchExpression());
1344
1821
  },
1345
1822
 
@@ -1354,7 +1831,7 @@ Selector.prototype = {
1354
1831
  scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
1355
1832
 
1356
1833
  var results = [];
1357
- for (var i = 0; i < scope.length; i++)
1834
+ for (var i = 0, length = scope.length; i < length; i++)
1358
1835
  if (this.match(element = scope[i]))
1359
1836
  results.push(Element.extend(element));
1360
1837
 
@@ -1366,206 +1843,241 @@ Selector.prototype = {
1366
1843
  }
1367
1844
  }
1368
1845
 
1369
- function $$() {
1370
- return $A(arguments).map(function(expression) {
1371
- return expression.strip().split(/\s+/).inject([null], function(results, expr) {
1372
- var selector = new Selector(expr);
1373
- return results.map(selector.findElements.bind(selector)).flatten();
1374
- });
1375
- }).flatten();
1376
- }
1377
- var Field = {
1378
- clear: function() {
1379
- for (var i = 0; i < arguments.length; i++)
1380
- $(arguments[i]).value = '';
1381
- },
1382
-
1383
- focus: function(element) {
1384
- $(element).focus();
1385
- },
1386
-
1387
- present: function() {
1388
- for (var i = 0; i < arguments.length; i++)
1389
- if ($(arguments[i]).value == '') return false;
1390
- return true;
1846
+ Object.extend(Selector, {
1847
+ matchElements: function(elements, expression) {
1848
+ var selector = new Selector(expression);
1849
+ return elements.select(selector.match.bind(selector)).map(Element.extend);
1391
1850
  },
1392
1851
 
1393
- select: function(element) {
1394
- $(element).select();
1852
+ findElement: function(elements, expression, index) {
1853
+ if (typeof expression == 'number') index = expression, expression = false;
1854
+ return Selector.matchElements(elements, expression || '*')[index || 0];
1395
1855
  },
1396
1856
 
1397
- activate: function(element) {
1398
- element = $(element);
1399
- element.focus();
1400
- if (element.select)
1401
- element.select();
1857
+ findChildElements: function(element, expressions) {
1858
+ return expressions.map(function(expression) {
1859
+ return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
1860
+ var selector = new Selector(expr);
1861
+ return results.inject([], function(elements, result) {
1862
+ return elements.concat(selector.findElements(result || element));
1863
+ });
1864
+ });
1865
+ }).flatten();
1402
1866
  }
1403
- }
1404
-
1405
- /*--------------------------------------------------------------------------*/
1867
+ });
1406
1868
 
1869
+ function $$() {
1870
+ return Selector.findChildElements(document, $A(arguments));
1871
+ }
1407
1872
  var Form = {
1408
- serialize: function(form) {
1409
- var elements = Form.getElements($(form));
1410
- var queryComponents = new Array();
1411
-
1412
- for (var i = 0; i < elements.length; i++) {
1413
- var queryComponent = Form.Element.serialize(elements[i]);
1414
- if (queryComponent)
1415
- queryComponents.push(queryComponent);
1416
- }
1873
+ reset: function(form) {
1874
+ $(form).reset();
1875
+ return form;
1876
+ },
1877
+
1878
+ serializeElements: function(elements, getHash) {
1879
+ var data = elements.inject({}, function(result, element) {
1880
+ if (!element.disabled && element.name) {
1881
+ var key = element.name, value = $(element).getValue();
1882
+ if (value != undefined) {
1883
+ if (result[key]) {
1884
+ if (result[key].constructor != Array) result[key] = [result[key]];
1885
+ result[key].push(value);
1886
+ }
1887
+ else result[key] = value;
1888
+ }
1889
+ }
1890
+ return result;
1891
+ });
1892
+
1893
+ return getHash ? data : Hash.toQueryString(data);
1894
+ }
1895
+ };
1417
1896
 
1418
- return queryComponents.join('&');
1897
+ Form.Methods = {
1898
+ serialize: function(form, getHash) {
1899
+ return Form.serializeElements(Form.getElements(form), getHash);
1419
1900
  },
1420
1901
 
1421
1902
  getElements: function(form) {
1422
- form = $(form);
1423
- var elements = new Array();
1424
-
1425
- for (var tagName in Form.Element.Serializers) {
1426
- var tagElements = form.getElementsByTagName(tagName);
1427
- for (var j = 0; j < tagElements.length; j++)
1428
- elements.push(tagElements[j]);
1429
- }
1430
- return elements;
1903
+ return $A($(form).getElementsByTagName('*')).inject([],
1904
+ function(elements, child) {
1905
+ if (Form.Element.Serializers[child.tagName.toLowerCase()])
1906
+ elements.push(Element.extend(child));
1907
+ return elements;
1908
+ }
1909
+ );
1431
1910
  },
1432
1911
 
1433
1912
  getInputs: function(form, typeName, name) {
1434
1913
  form = $(form);
1435
1914
  var inputs = form.getElementsByTagName('input');
1436
1915
 
1437
- if (!typeName && !name)
1438
- return inputs;
1916
+ if (!typeName && !name) return $A(inputs).map(Element.extend);
1439
1917
 
1440
- var matchingInputs = new Array();
1441
- for (var i = 0; i < inputs.length; i++) {
1918
+ for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
1442
1919
  var input = inputs[i];
1443
- if ((typeName && input.type != typeName) ||
1444
- (name && input.name != name))
1920
+ if ((typeName && input.type != typeName) || (name && input.name != name))
1445
1921
  continue;
1446
- matchingInputs.push(input);
1922
+ matchingInputs.push(Element.extend(input));
1447
1923
  }
1448
1924
 
1449
1925
  return matchingInputs;
1450
1926
  },
1451
1927
 
1452
1928
  disable: function(form) {
1453
- var elements = Form.getElements(form);
1454
- for (var i = 0; i < elements.length; i++) {
1455
- var element = elements[i];
1929
+ form = $(form);
1930
+ form.getElements().each(function(element) {
1456
1931
  element.blur();
1457
1932
  element.disabled = 'true';
1458
- }
1933
+ });
1934
+ return form;
1459
1935
  },
1460
1936
 
1461
1937
  enable: function(form) {
1462
- var elements = Form.getElements(form);
1463
- for (var i = 0; i < elements.length; i++) {
1464
- var element = elements[i];
1938
+ form = $(form);
1939
+ form.getElements().each(function(element) {
1465
1940
  element.disabled = '';
1466
- }
1941
+ });
1942
+ return form;
1467
1943
  },
1468
1944
 
1469
1945
  findFirstElement: function(form) {
1470
- return Form.getElements(form).find(function(element) {
1946
+ return $(form).getElements().find(function(element) {
1471
1947
  return element.type != 'hidden' && !element.disabled &&
1472
1948
  ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
1473
1949
  });
1474
1950
  },
1475
1951
 
1476
1952
  focusFirstElement: function(form) {
1477
- Field.activate(Form.findFirstElement(form));
1953
+ form = $(form);
1954
+ form.findFirstElement().activate();
1955
+ return form;
1956
+ }
1957
+ }
1958
+
1959
+ Object.extend(Form, Form.Methods);
1960
+
1961
+ /*--------------------------------------------------------------------------*/
1962
+
1963
+ Form.Element = {
1964
+ focus: function(element) {
1965
+ $(element).focus();
1966
+ return element;
1478
1967
  },
1479
1968
 
1480
- reset: function(form) {
1481
- $(form).reset();
1969
+ select: function(element) {
1970
+ $(element).select();
1971
+ return element;
1482
1972
  }
1483
1973
  }
1484
1974
 
1485
- Form.Element = {
1975
+ Form.Element.Methods = {
1486
1976
  serialize: function(element) {
1977
+ element = $(element);
1978
+ if (!element.disabled && element.name) {
1979
+ var value = element.getValue();
1980
+ if (value != undefined) {
1981
+ var pair = {};
1982
+ pair[element.name] = value;
1983
+ return Hash.toQueryString(pair);
1984
+ }
1985
+ }
1986
+ return '';
1987
+ },
1988
+
1989
+ getValue: function(element) {
1487
1990
  element = $(element);
1488
1991
  var method = element.tagName.toLowerCase();
1489
- var parameter = Form.Element.Serializers[method](element);
1992
+ return Form.Element.Serializers[method](element);
1993
+ },
1490
1994
 
1491
- if (parameter) {
1492
- var key = encodeURIComponent(parameter[0]);
1493
- if (key.length == 0) return;
1995
+ clear: function(element) {
1996
+ $(element).value = '';
1997
+ return element;
1998
+ },
1494
1999
 
1495
- if (parameter[1].constructor != Array)
1496
- parameter[1] = [parameter[1]];
2000
+ present: function(element) {
2001
+ return $(element).value != '';
2002
+ },
1497
2003
 
1498
- return parameter[1].map(function(value) {
1499
- return key + '=' + encodeURIComponent(value);
1500
- }).join('&');
1501
- }
2004
+ activate: function(element) {
2005
+ element = $(element);
2006
+ element.focus();
2007
+ if (element.select && ( element.tagName.toLowerCase() != 'input' ||
2008
+ !['button', 'reset', 'submit'].include(element.type) ) )
2009
+ element.select();
2010
+ return element;
1502
2011
  },
1503
2012
 
1504
- getValue: function(element) {
2013
+ disable: function(element) {
1505
2014
  element = $(element);
1506
- var method = element.tagName.toLowerCase();
1507
- var parameter = Form.Element.Serializers[method](element);
2015
+ element.disabled = true;
2016
+ return element;
2017
+ },
1508
2018
 
1509
- if (parameter)
1510
- return parameter[1];
2019
+ enable: function(element) {
2020
+ element = $(element);
2021
+ element.blur();
2022
+ element.disabled = false;
2023
+ return element;
1511
2024
  }
1512
2025
  }
1513
2026
 
2027
+ Object.extend(Form.Element, Form.Element.Methods);
2028
+ var Field = Form.Element;
2029
+ var $F = Form.Element.getValue;
2030
+
2031
+ /*--------------------------------------------------------------------------*/
2032
+
1514
2033
  Form.Element.Serializers = {
1515
2034
  input: function(element) {
1516
2035
  switch (element.type.toLowerCase()) {
1517
- case 'submit':
1518
- case 'hidden':
1519
- case 'password':
1520
- case 'text':
1521
- return Form.Element.Serializers.textarea(element);
1522
2036
  case 'checkbox':
1523
2037
  case 'radio':
1524
2038
  return Form.Element.Serializers.inputSelector(element);
2039
+ default:
2040
+ return Form.Element.Serializers.textarea(element);
1525
2041
  }
1526
- return false;
1527
2042
  },
1528
2043
 
1529
2044
  inputSelector: function(element) {
1530
- if (element.checked)
1531
- return [element.name, element.value];
2045
+ return element.checked ? element.value : null;
1532
2046
  },
1533
2047
 
1534
2048
  textarea: function(element) {
1535
- return [element.name, element.value];
2049
+ return element.value;
1536
2050
  },
1537
2051
 
1538
2052
  select: function(element) {
1539
- return Form.Element.Serializers[element.type == 'select-one' ?
2053
+ return this[element.type == 'select-one' ?
1540
2054
  'selectOne' : 'selectMany'](element);
1541
2055
  },
1542
2056
 
1543
2057
  selectOne: function(element) {
1544
- var value = '', opt, index = element.selectedIndex;
1545
- if (index >= 0) {
1546
- opt = element.options[index];
1547
- value = opt.value || opt.text;
1548
- }
1549
- return [element.name, value];
2058
+ var index = element.selectedIndex;
2059
+ return index >= 0 ? this.optionValue(element.options[index]) : null;
1550
2060
  },
1551
2061
 
1552
2062
  selectMany: function(element) {
1553
- var value = [];
1554
- for (var i = 0; i < element.length; i++) {
2063
+ var values, length = element.length;
2064
+ if (!length) return null;
2065
+
2066
+ for (var i = 0, values = []; i < length; i++) {
1555
2067
  var opt = element.options[i];
1556
- if (opt.selected)
1557
- value.push(opt.value || opt.text);
2068
+ if (opt.selected) values.push(this.optionValue(opt));
1558
2069
  }
1559
- return [element.name, value];
2070
+ return values;
2071
+ },
2072
+
2073
+ optionValue: function(opt) {
2074
+ // extend element because hasAttribute may not be native
2075
+ return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
1560
2076
  }
1561
2077
  }
1562
2078
 
1563
2079
  /*--------------------------------------------------------------------------*/
1564
2080
 
1565
- var $F = Form.Element.getValue;
1566
-
1567
- /*--------------------------------------------------------------------------*/
1568
-
1569
2081
  Abstract.TimedObserver = function() {}
1570
2082
  Abstract.TimedObserver.prototype = {
1571
2083
  initialize: function(element, frequency, callback) {
@@ -1583,7 +2095,9 @@ Abstract.TimedObserver.prototype = {
1583
2095
 
1584
2096
  onTimerEvent: function() {
1585
2097
  var value = this.getValue();
1586
- if (this.lastValue != value) {
2098
+ var changed = ('string' == typeof this.lastValue && 'string' == typeof value
2099
+ ? this.lastValue != value : String(this.lastValue) != String(value));
2100
+ if (changed) {
1587
2101
  this.callback(this.element, value);
1588
2102
  this.lastValue = value;
1589
2103
  }
@@ -1628,9 +2142,7 @@ Abstract.EventObserver.prototype = {
1628
2142
  },
1629
2143
 
1630
2144
  registerFormCallbacks: function() {
1631
- var elements = Form.getElements(this.element);
1632
- for (var i = 0; i < elements.length; i++)
1633
- this.registerCallback(elements[i]);
2145
+ Form.getElements(this.element).each(this.registerCallback.bind(this));
1634
2146
  },
1635
2147
 
1636
2148
  registerCallback: function(element) {
@@ -1640,11 +2152,7 @@ Abstract.EventObserver.prototype = {
1640
2152
  case 'radio':
1641
2153
  Event.observe(element, 'click', this.onElementEvent.bind(this));
1642
2154
  break;
1643
- case 'password':
1644
- case 'text':
1645
- case 'textarea':
1646
- case 'select-one':
1647
- case 'select-multiple':
2155
+ default:
1648
2156
  Event.observe(element, 'change', this.onElementEvent.bind(this));
1649
2157
  break;
1650
2158
  }
@@ -1679,6 +2187,10 @@ Object.extend(Event, {
1679
2187
  KEY_RIGHT: 39,
1680
2188
  KEY_DOWN: 40,
1681
2189
  KEY_DELETE: 46,
2190
+ KEY_HOME: 36,
2191
+ KEY_END: 35,
2192
+ KEY_PAGEUP: 33,
2193
+ KEY_PAGEDOWN: 34,
1682
2194
 
1683
2195
  element: function(event) {
1684
2196
  return event.target || event.srcElement;
@@ -1734,7 +2246,7 @@ Object.extend(Event, {
1734
2246
 
1735
2247
  unloadCache: function() {
1736
2248
  if (!Event.observers) return;
1737
- for (var i = 0; i < Event.observers.length; i++) {
2249
+ for (var i = 0, length = Event.observers.length; i < length; i++) {
1738
2250
  Event.stopObserving.apply(this, Event.observers[i]);
1739
2251
  Event.observers[i][0] = null;
1740
2252
  }
@@ -1742,7 +2254,7 @@ Object.extend(Event, {
1742
2254
  },
1743
2255
 
1744
2256
  observe: function(element, name, observer, useCapture) {
1745
- var element = $(element);
2257
+ element = $(element);
1746
2258
  useCapture = useCapture || false;
1747
2259
 
1748
2260
  if (name == 'keypress' &&
@@ -1750,11 +2262,11 @@ Object.extend(Event, {
1750
2262
  || element.attachEvent))
1751
2263
  name = 'keydown';
1752
2264
 
1753
- this._observeAndCache(element, name, observer, useCapture);
2265
+ Event._observeAndCache(element, name, observer, useCapture);
1754
2266
  },
1755
2267
 
1756
2268
  stopObserving: function(element, name, observer, useCapture) {
1757
- var element = $(element);
2269
+ element = $(element);
1758
2270
  useCapture = useCapture || false;
1759
2271
 
1760
2272
  if (name == 'keypress' &&
@@ -1765,7 +2277,9 @@ Object.extend(Event, {
1765
2277
  if (element.removeEventListener) {
1766
2278
  element.removeEventListener(name, observer, useCapture);
1767
2279
  } else if (element.detachEvent) {
1768
- element.detachEvent('on' + name, observer);
2280
+ try {
2281
+ element.detachEvent('on' + name, observer);
2282
+ } catch (e) {}
1769
2283
  }
1770
2284
  }
1771
2285
  });
@@ -1819,7 +2333,8 @@ var Position = {
1819
2333
  valueL += element.offsetLeft || 0;
1820
2334
  element = element.offsetParent;
1821
2335
  if (element) {
1822
- p = Element.getStyle(element, 'position');
2336
+ if(element.tagName=='BODY') break;
2337
+ var p = Element.getStyle(element, 'position');
1823
2338
  if (p == 'relative' || p == 'absolute') break;
1824
2339
  }
1825
2340
  } while (element);
@@ -1875,17 +2390,6 @@ var Position = {
1875
2390
  element.offsetWidth;
1876
2391
  },
1877
2392
 
1878
- clone: function(source, target) {
1879
- source = $(source);
1880
- target = $(target);
1881
- target.style.position = 'absolute';
1882
- var offsets = this.cumulativeOffset(source);
1883
- target.style.top = offsets[1] + 'px';
1884
- target.style.left = offsets[0] + 'px';
1885
- target.style.width = source.offsetWidth + 'px';
1886
- target.style.height = source.offsetHeight + 'px';
1887
- },
1888
-
1889
2393
  page: function(forElement) {
1890
2394
  var valueT = 0, valueL = 0;
1891
2395
 
@@ -1902,8 +2406,10 @@ var Position = {
1902
2406
 
1903
2407
  element = forElement;
1904
2408
  do {
1905
- valueT -= element.scrollTop || 0;
1906
- valueL -= element.scrollLeft || 0;
2409
+ if (!window.opera || element.tagName=='BODY') {
2410
+ valueT -= element.scrollTop || 0;
2411
+ valueL -= element.scrollLeft || 0;
2412
+ }
1907
2413
  } while (element = element.parentNode);
1908
2414
 
1909
2415
  return [valueL, valueT];
@@ -1964,10 +2470,10 @@ var Position = {
1964
2470
  element._originalHeight = element.style.height;
1965
2471
 
1966
2472
  element.style.position = 'absolute';
1967
- element.style.top = top + 'px';;
1968
- element.style.left = left + 'px';;
1969
- element.style.width = width + 'px';;
1970
- element.style.height = height + 'px';;
2473
+ element.style.top = top + 'px';
2474
+ element.style.left = left + 'px';
2475
+ element.style.width = width + 'px';
2476
+ element.style.height = height + 'px';
1971
2477
  },
1972
2478
 
1973
2479
  relativize: function(element) {
@@ -2003,4 +2509,6 @@ if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
2003
2509
 
2004
2510
  return [valueL, valueT];
2005
2511
  }
2006
- }
2512
+ }
2513
+
2514
+ Element.addMethods();