actionpack 1.9.1 → 1.10.1

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 (123) hide show
  1. data/CHANGELOG +237 -0
  2. data/README +12 -12
  3. data/lib/action_controller.rb +17 -12
  4. data/lib/action_controller/assertions.rb +119 -67
  5. data/lib/action_controller/base.rb +184 -102
  6. data/lib/action_controller/benchmarking.rb +35 -6
  7. data/lib/action_controller/caching.rb +115 -58
  8. data/lib/action_controller/cgi_ext/cgi_methods.rb +54 -21
  9. data/lib/action_controller/cgi_ext/cookie_performance_fix.rb +39 -35
  10. data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +34 -21
  11. data/lib/action_controller/cgi_process.rb +23 -20
  12. data/lib/action_controller/components.rb +11 -2
  13. data/lib/action_controller/dependencies.rb +0 -5
  14. data/lib/action_controller/deprecated_redirects.rb +17 -0
  15. data/lib/action_controller/filters.rb +13 -9
  16. data/lib/action_controller/flash.rb +7 -7
  17. data/lib/action_controller/helpers.rb +1 -14
  18. data/lib/action_controller/layout.rb +40 -29
  19. data/lib/action_controller/macros/auto_complete.rb +52 -0
  20. data/lib/action_controller/macros/in_place_editing.rb +32 -0
  21. data/lib/action_controller/pagination.rb +44 -28
  22. data/lib/action_controller/request.rb +54 -40
  23. data/lib/action_controller/rescue.rb +8 -6
  24. data/lib/action_controller/routing.rb +77 -28
  25. data/lib/action_controller/scaffolding.rb +10 -14
  26. data/lib/action_controller/session/active_record_store.rb +36 -7
  27. data/lib/action_controller/session_management.rb +126 -0
  28. data/lib/action_controller/streaming.rb +14 -5
  29. data/lib/action_controller/templates/rescues/_request_and_response.rhtml +1 -1
  30. data/lib/action_controller/templates/rescues/_trace.rhtml +24 -0
  31. data/lib/action_controller/templates/rescues/diagnostics.rhtml +2 -13
  32. data/lib/action_controller/templates/rescues/template_error.rhtml +4 -2
  33. data/lib/action_controller/templates/scaffolds/list.rhtml +1 -1
  34. data/lib/action_controller/test_process.rb +35 -17
  35. data/lib/action_controller/upload_progress.rb +52 -0
  36. data/lib/action_controller/url_rewriter.rb +21 -16
  37. data/lib/action_controller/vendor/html-scanner/html/document.rb +2 -2
  38. data/lib/action_controller/vendor/html-scanner/html/node.rb +30 -3
  39. data/lib/action_pack/version.rb +9 -0
  40. data/lib/action_view.rb +1 -1
  41. data/lib/action_view/base.rb +204 -60
  42. data/lib/action_view/compiled_templates.rb +70 -0
  43. data/lib/action_view/helpers/active_record_helper.rb +7 -3
  44. data/lib/action_view/helpers/asset_tag_helper.rb +22 -12
  45. data/lib/action_view/helpers/capture_helper.rb +2 -10
  46. data/lib/action_view/helpers/date_helper.rb +21 -13
  47. data/lib/action_view/helpers/form_helper.rb +14 -10
  48. data/lib/action_view/helpers/form_options_helper.rb +4 -4
  49. data/lib/action_view/helpers/form_tag_helper.rb +59 -25
  50. data/lib/action_view/helpers/java_script_macros_helper.rb +188 -0
  51. data/lib/action_view/helpers/javascript_helper.rb +68 -133
  52. data/lib/action_view/helpers/javascripts/controls.js +427 -165
  53. data/lib/action_view/helpers/javascripts/dragdrop.js +256 -277
  54. data/lib/action_view/helpers/javascripts/effects.js +766 -277
  55. data/lib/action_view/helpers/javascripts/prototype.js +906 -218
  56. data/lib/action_view/helpers/javascripts/slider.js +258 -0
  57. data/lib/action_view/helpers/number_helper.rb +4 -3
  58. data/lib/action_view/helpers/pagination_helper.rb +42 -27
  59. data/lib/action_view/helpers/tag_helper.rb +25 -11
  60. data/lib/action_view/helpers/text_helper.rb +119 -13
  61. data/lib/action_view/helpers/upload_progress_helper.rb +2 -2
  62. data/lib/action_view/helpers/url_helper.rb +68 -21
  63. data/lib/action_view/partials.rb +17 -6
  64. data/lib/action_view/template_error.rb +19 -24
  65. data/rakefile +4 -3
  66. data/test/abstract_unit.rb +2 -1
  67. data/test/controller/action_pack_assertions_test.rb +62 -2
  68. data/test/controller/active_record_assertions_test.rb +5 -6
  69. data/test/controller/active_record_store_test.rb +23 -1
  70. data/test/controller/addresses_render_test.rb +4 -0
  71. data/test/controller/{base_tests.rb → base_test.rb} +4 -3
  72. data/test/controller/benchmark_test.rb +36 -0
  73. data/test/controller/caching_filestore.rb +22 -40
  74. data/test/controller/capture_test.rb +10 -1
  75. data/test/controller/cgi_test.rb +145 -23
  76. data/test/controller/components_test.rb +50 -0
  77. data/test/controller/custom_handler_test.rb +3 -3
  78. data/test/controller/fake_controllers.rb +24 -0
  79. data/test/controller/filters_test.rb +6 -6
  80. data/test/controller/flash_test.rb +6 -6
  81. data/test/controller/fragment_store_setting_test.rb +45 -0
  82. data/test/controller/helper_test.rb +1 -3
  83. data/test/controller/new_render_test.rb +119 -7
  84. data/test/controller/redirect_test.rb +11 -1
  85. data/test/controller/render_test.rb +34 -1
  86. data/test/controller/request_test.rb +14 -5
  87. data/test/controller/routing_test.rb +238 -42
  88. data/test/controller/send_file_test.rb +11 -10
  89. data/test/controller/session_management_test.rb +94 -0
  90. data/test/controller/test_test.rb +194 -5
  91. data/test/controller/url_rewriter_test.rb +46 -0
  92. data/test/fixtures/layouts/talk_from_action.rhtml +2 -0
  93. data/test/fixtures/layouts/yield.rhtml +2 -0
  94. data/test/fixtures/multipart/binary_file +0 -0
  95. data/test/fixtures/multipart/large_text_file +10 -0
  96. data/test/fixtures/multipart/mixed_files +0 -0
  97. data/test/fixtures/multipart/single_parameter +5 -0
  98. data/test/fixtures/multipart/text_file +10 -0
  99. data/test/fixtures/test/_customer_greeting.rhtml +1 -0
  100. data/test/fixtures/test/_hash_object.rhtml +1 -0
  101. data/test/fixtures/test/_person.rhtml +2 -0
  102. data/test/fixtures/test/action_talk_to_layout.rhtml +2 -0
  103. data/test/fixtures/test/content_for.rhtml +2 -0
  104. data/test/fixtures/test/potential_conflicts.rhtml +4 -0
  105. data/test/template/active_record_helper_test.rb +15 -8
  106. data/test/template/asset_tag_helper_test.rb +40 -16
  107. data/test/template/compiled_templates_tests.rb +63 -0
  108. data/test/template/date_helper_test.rb +80 -4
  109. data/test/template/form_helper_test.rb +48 -42
  110. data/test/template/form_options_helper_test.rb +40 -40
  111. data/test/template/form_tag_helper_test.rb +21 -15
  112. data/test/template/java_script_macros_helper_test.rb +56 -0
  113. data/test/template/javascript_helper_test.rb +70 -47
  114. data/test/template/number_helper_test.rb +2 -0
  115. data/test/template/tag_helper_test.rb +9 -0
  116. data/test/template/text_helper_test.rb +146 -1
  117. data/test/template/upload_progress_helper_testx.rb +11 -147
  118. data/test/template/url_helper_test.rb +90 -22
  119. data/test/testing_sandbox.rb +26 -0
  120. metadata +37 -7
  121. data/lib/action_controller/auto_complete.rb +0 -47
  122. data/lib/action_controller/deprecated_renders_and_redirects.rb +0 -76
  123. data/lib/action_controller/session.rb +0 -14
@@ -1,8 +1,8 @@
1
- /* Prototype JavaScript framework, version 1.3.1
1
+ /* Prototype JavaScript framework, version 1.4.0_rc1
2
2
  * (c) 2005 Sam Stephenson <sam@conio.net>
3
3
  *
4
4
  * THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff
5
- * against the source tree, available from the Prototype darcs repository.
5
+ * against the source tree, available from the Prototype darcs repository.
6
6
  *
7
7
  * Prototype is freely distributable under the terms of an MIT-style license.
8
8
  *
@@ -11,13 +11,15 @@
11
11
  /*--------------------------------------------------------------------------*/
12
12
 
13
13
  var Prototype = {
14
- Version: '1.3.1',
15
- emptyFunction: function() {}
14
+ Version: '1.4.0_rc1',
15
+
16
+ emptyFunction: function() {},
17
+ K: function(x) {return x}
16
18
  }
17
19
 
18
20
  var Class = {
19
21
  create: function() {
20
- return function() {
22
+ return function() {
21
23
  this.initialize.apply(this, arguments);
22
24
  }
23
25
  }
@@ -32,29 +34,47 @@ Object.extend = function(destination, source) {
32
34
  return destination;
33
35
  }
34
36
 
35
- Object.prototype.extend = function(object) {
36
- return Object.extend.apply(this, [this, object]);
37
+ Object.inspect = function(object) {
38
+ try {
39
+ if (object == undefined) return 'undefined';
40
+ if (object == null) return 'null';
41
+ return object.inspect ? object.inspect() : object.toString();
42
+ } catch (e) {
43
+ if (e instanceof RangeError) return '...';
44
+ throw e;
45
+ }
37
46
  }
38
47
 
39
48
  Function.prototype.bind = function(object) {
40
49
  var __method = this;
41
50
  return function() {
42
- __method.apply(object, arguments);
51
+ return __method.apply(object, arguments);
43
52
  }
44
53
  }
45
54
 
46
55
  Function.prototype.bindAsEventListener = function(object) {
47
56
  var __method = this;
48
57
  return function(event) {
49
- __method.call(object, event || window.event);
58
+ return __method.call(object, event || window.event);
50
59
  }
51
60
  }
52
61
 
53
- Number.prototype.toColorPart = function() {
54
- var digits = this.toString(16);
55
- if (this < 16) return '0' + digits;
56
- return digits;
57
- }
62
+ Object.extend(Number.prototype, {
63
+ toColorPart: function() {
64
+ var digits = this.toString(16);
65
+ if (this < 16) return '0' + digits;
66
+ return digits;
67
+ },
68
+
69
+ succ: function() {
70
+ return this + 1;
71
+ },
72
+
73
+ times: function(iterator) {
74
+ $R(0, this, true).each(iterator);
75
+ return this;
76
+ }
77
+ });
58
78
 
59
79
  var Try = {
60
80
  these: function() {
@@ -90,10 +110,10 @@ PeriodicalExecuter.prototype = {
90
110
 
91
111
  onTimerEvent: function() {
92
112
  if (!this.currentlyExecuting) {
93
- try {
113
+ try {
94
114
  this.currentlyExecuting = true;
95
- this.callback();
96
- } finally {
115
+ this.callback();
116
+ } finally {
97
117
  this.currentlyExecuting = false;
98
118
  }
99
119
  }
@@ -110,7 +130,7 @@ function $() {
110
130
  if (typeof element == 'string')
111
131
  element = document.getElementById(element);
112
132
 
113
- if (arguments.length == 1)
133
+ if (arguments.length == 1)
114
134
  return element;
115
135
 
116
136
  elements.push(element);
@@ -118,36 +138,7 @@ function $() {
118
138
 
119
139
  return elements;
120
140
  }
121
-
122
- if (!Array.prototype.push) {
123
- Array.prototype.push = function() {
124
- var startLength = this.length;
125
- for (var i = 0; i < arguments.length; i++)
126
- this[startLength + i] = arguments[i];
127
- return this.length;
128
- }
129
- }
130
-
131
- if (!Function.prototype.apply) {
132
- // Based on code from http://www.youngpup.net/
133
- Function.prototype.apply = function(object, parameters) {
134
- var parameterStrings = new Array();
135
- if (!object) object = window;
136
- if (!parameters) parameters = new Array();
137
-
138
- for (var i = 0; i < parameters.length; i++)
139
- parameterStrings[i] = 'parameters[' + i + ']';
140
-
141
- object.__apply__ = this;
142
- var result = eval('object.__apply__(' +
143
- parameterStrings[i].join(', ') + ')');
144
- object.__apply__ = null;
145
-
146
- return result;
147
- }
148
- }
149
-
150
- String.prototype.extend({
141
+ Object.extend(String.prototype, {
151
142
  stripTags: function() {
152
143
  return this.replace(/<\/?[^>]+>/gi, '');
153
144
  },
@@ -162,9 +153,368 @@ String.prototype.extend({
162
153
  unescapeHTML: function() {
163
154
  var div = document.createElement('div');
164
155
  div.innerHTML = this.stripTags();
165
- return div.childNodes[0].nodeValue;
156
+ return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
157
+ },
158
+
159
+ toQueryParams: function() {
160
+ var pairs = this.match(/^\??(.*)$/)[1].split('&');
161
+ return pairs.inject({}, function(params, pairString) {
162
+ var pair = pairString.split('=');
163
+ params[pair[0]] = pair[1];
164
+ return params;
165
+ });
166
+ },
167
+
168
+ toArray: function() {
169
+ return this.split('');
170
+ },
171
+
172
+ camelize: function() {
173
+ var oStringList = this.split('-');
174
+ if (oStringList.length == 1) return oStringList[0];
175
+
176
+ var camelizedString = this.indexOf('-') == 0
177
+ ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
178
+ : oStringList[0];
179
+
180
+ for (var i = 1, len = oStringList.length; i < len; i++) {
181
+ var s = oStringList[i];
182
+ camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
183
+ }
184
+
185
+ return camelizedString;
186
+ },
187
+
188
+ inspect: function() {
189
+ return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
190
+ }
191
+ });
192
+
193
+ String.prototype.parseQuery = String.prototype.toQueryParams;
194
+
195
+ var $break = new Object();
196
+ var $continue = new Object();
197
+
198
+ var Enumerable = {
199
+ each: function(iterator) {
200
+ var index = 0;
201
+ try {
202
+ this._each(function(value) {
203
+ try {
204
+ iterator(value, index++);
205
+ } catch (e) {
206
+ if (e != $continue) throw e;
207
+ }
208
+ });
209
+ } catch (e) {
210
+ if (e != $break) throw e;
211
+ }
212
+ },
213
+
214
+ all: function(iterator) {
215
+ var result = true;
216
+ this.each(function(value, index) {
217
+ if (!(result &= (iterator || Prototype.K)(value, index)))
218
+ throw $break;
219
+ });
220
+ return result;
221
+ },
222
+
223
+ any: function(iterator) {
224
+ var result = true;
225
+ this.each(function(value, index) {
226
+ if (result &= (iterator || Prototype.K)(value, index))
227
+ throw $break;
228
+ });
229
+ return result;
230
+ },
231
+
232
+ collect: function(iterator) {
233
+ var results = [];
234
+ this.each(function(value, index) {
235
+ results.push(iterator(value, index));
236
+ });
237
+ return results;
238
+ },
239
+
240
+ detect: function (iterator) {
241
+ var result;
242
+ this.each(function(value, index) {
243
+ if (iterator(value, index)) {
244
+ result = value;
245
+ throw $break;
246
+ }
247
+ });
248
+ return result;
249
+ },
250
+
251
+ findAll: function(iterator) {
252
+ var results = [];
253
+ this.each(function(value, index) {
254
+ if (iterator(value, index))
255
+ results.push(value);
256
+ });
257
+ return results;
258
+ },
259
+
260
+ grep: function(pattern, iterator) {
261
+ var results = [];
262
+ this.each(function(value, index) {
263
+ var stringValue = value.toString();
264
+ if (stringValue.match(pattern))
265
+ results.push((iterator || Prototype.K)(value, index));
266
+ })
267
+ return results;
268
+ },
269
+
270
+ include: function(object) {
271
+ var found = false;
272
+ this.each(function(value) {
273
+ if (value == object) {
274
+ found = true;
275
+ throw $break;
276
+ }
277
+ });
278
+ return found;
279
+ },
280
+
281
+ inject: function(memo, iterator) {
282
+ this.each(function(value, index) {
283
+ memo = iterator(memo, value, index);
284
+ });
285
+ return memo;
286
+ },
287
+
288
+ invoke: function(method) {
289
+ var args = $A(arguments).slice(1);
290
+ return this.collect(function(value) {
291
+ return value[method].apply(value, args);
292
+ });
293
+ },
294
+
295
+ max: function(iterator) {
296
+ var result;
297
+ this.each(function(value, index) {
298
+ value = (iterator || Prototype.K)(value, index);
299
+ if (value >= (result || value))
300
+ result = value;
301
+ });
302
+ return result;
303
+ },
304
+
305
+ min: function(iterator) {
306
+ var result;
307
+ this.each(function(value, index) {
308
+ value = (iterator || Prototype.K)(value, index);
309
+ if (value <= (result || value))
310
+ result = value;
311
+ });
312
+ return result;
313
+ },
314
+
315
+ partition: function(iterator) {
316
+ var trues = [], falses = [];
317
+ this.each(function(value, index) {
318
+ ((iterator || Prototype.K)(value, index) ?
319
+ trues : falses).push(value);
320
+ });
321
+ return [trues, falses];
322
+ },
323
+
324
+ pluck: function(property) {
325
+ var results = [];
326
+ this.each(function(value, index) {
327
+ results.push(value[property]);
328
+ });
329
+ return results;
330
+ },
331
+
332
+ reject: function(iterator) {
333
+ var results = [];
334
+ this.each(function(value, index) {
335
+ if (!iterator(value, index))
336
+ results.push(value);
337
+ });
338
+ return results;
339
+ },
340
+
341
+ sortBy: function(iterator) {
342
+ return this.collect(function(value, index) {
343
+ return {value: value, criteria: iterator(value, index)};
344
+ }).sort(function(left, right) {
345
+ var a = left.criteria, b = right.criteria;
346
+ return a < b ? -1 : a > b ? 1 : 0;
347
+ }).pluck('value');
348
+ },
349
+
350
+ toArray: function() {
351
+ return this.collect(Prototype.K);
352
+ },
353
+
354
+ zip: function() {
355
+ var iterator = Prototype.K, args = $A(arguments);
356
+ if (typeof args.last() == 'function')
357
+ iterator = args.pop();
358
+
359
+ var collections = [this].concat(args).map($A);
360
+ return this.map(function(value, index) {
361
+ iterator(value = collections.pluck(index));
362
+ return value;
363
+ });
364
+ },
365
+
366
+ inspect: function() {
367
+ return '#<Enumerable:' + this.toArray().inspect() + '>';
368
+ }
369
+ }
370
+
371
+ Object.extend(Enumerable, {
372
+ map: Enumerable.collect,
373
+ find: Enumerable.detect,
374
+ select: Enumerable.findAll,
375
+ member: Enumerable.include,
376
+ entries: Enumerable.toArray
377
+ });
378
+ var $A = Array.from = function(iterable) {
379
+ if (iterable.toArray) {
380
+ return iterable.toArray();
381
+ } else {
382
+ var results = [];
383
+ for (var i = 0; i < iterable.length; i++)
384
+ results.push(iterable[i]);
385
+ return results;
386
+ }
387
+ }
388
+
389
+ Object.extend(Array.prototype, Enumerable);
390
+
391
+ Object.extend(Array.prototype, {
392
+ _each: function(iterator) {
393
+ for (var i = 0; i < this.length; i++)
394
+ iterator(this[i]);
395
+ },
396
+
397
+ first: function() {
398
+ return this[0];
399
+ },
400
+
401
+ last: function() {
402
+ return this[this.length - 1];
403
+ },
404
+
405
+ compact: function() {
406
+ return this.select(function(value) {
407
+ return value != undefined || value != null;
408
+ });
409
+ },
410
+
411
+ flatten: function() {
412
+ return this.inject([], function(array, value) {
413
+ return array.concat(value.constructor == Array ?
414
+ value.flatten() : [value]);
415
+ });
416
+ },
417
+
418
+ without: function() {
419
+ var values = $A(arguments);
420
+ return this.select(function(value) {
421
+ return !values.include(value);
422
+ });
423
+ },
424
+
425
+ indexOf: function(object) {
426
+ for (var i = 0; i < this.length; i++)
427
+ if (this[i] == object) return i;
428
+ return false;
429
+ },
430
+
431
+ reverse: function() {
432
+ var result = [];
433
+ for (var i = this.length; i > 0; i--)
434
+ result.push(this[i-1]);
435
+ return result;
436
+ },
437
+
438
+ inspect: function() {
439
+ return '[' + this.map(Object.inspect).join(', ') + ']';
166
440
  }
167
441
  });
442
+ var Hash = {
443
+ _each: function(iterator) {
444
+ for (key in this) {
445
+ var value = this[key];
446
+ if (typeof value == 'function') continue;
447
+
448
+ var pair = [key, value];
449
+ pair.key = key;
450
+ pair.value = value;
451
+ iterator(pair);
452
+ }
453
+ },
454
+
455
+ keys: function() {
456
+ return this.pluck('key');
457
+ },
458
+
459
+ values: function() {
460
+ return this.pluck('value');
461
+ },
462
+
463
+ merge: function(hash) {
464
+ return $H(hash).inject($H(this), function(mergedHash, pair) {
465
+ mergedHash[pair.key] = pair.value;
466
+ return mergedHash;
467
+ });
468
+ },
469
+
470
+ toQueryString: function() {
471
+ return this.map(function(pair) {
472
+ return pair.map(encodeURIComponent).join('=');
473
+ }).join('&');
474
+ },
475
+
476
+ inspect: function() {
477
+ return '#<Hash:{' + this.map(function(pair) {
478
+ return pair.map(Object.inspect).join(': ');
479
+ }).join(', ') + '}>';
480
+ }
481
+ }
482
+
483
+ function $H(object) {
484
+ var hash = Object.extend({}, object || {});
485
+ Object.extend(hash, Enumerable);
486
+ Object.extend(hash, Hash);
487
+ return hash;
488
+ }
489
+ var Range = Class.create();
490
+ Object.extend(Range.prototype, Enumerable);
491
+ Object.extend(Range.prototype, {
492
+ initialize: function(start, end, exclusive) {
493
+ this.start = start;
494
+ this.end = end;
495
+ this.exclusive = exclusive;
496
+ },
497
+
498
+ _each: function(iterator) {
499
+ var value = this.start;
500
+ do {
501
+ iterator(value);
502
+ value = value.succ();
503
+ } while (this.include(value));
504
+ },
505
+
506
+ include: function(value) {
507
+ if (value < this.start)
508
+ return false;
509
+ if (this.exclusive)
510
+ return value < this.end;
511
+ return value <= this.end;
512
+ }
513
+ });
514
+
515
+ var $R = function(start, end, exclusive) {
516
+ return new Range(start, end, exclusive);
517
+ }
168
518
 
169
519
  var Ajax = {
170
520
  getTransport: function() {
@@ -173,9 +523,51 @@ var Ajax = {
173
523
  function() {return new ActiveXObject('Microsoft.XMLHTTP')},
174
524
  function() {return new XMLHttpRequest()}
175
525
  ) || false;
176
- }
526
+ },
527
+
528
+ activeRequestCount: 0
177
529
  }
178
530
 
531
+ Ajax.Responders = {
532
+ responders: [],
533
+
534
+ _each: function(iterator) {
535
+ this.responders._each(iterator);
536
+ },
537
+
538
+ register: function(responderToAdd) {
539
+ if (!this.include(responderToAdd))
540
+ this.responders.push(responderToAdd);
541
+ },
542
+
543
+ unregister: function(responderToRemove) {
544
+ this.responders = this.responders.without(responderToRemove);
545
+ },
546
+
547
+ dispatch: function(callback, request, transport, json) {
548
+ this.each(function(responder) {
549
+ if (responder[callback] && typeof responder[callback] == 'function') {
550
+ try {
551
+ responder[callback].apply(responder, [request, transport, json]);
552
+ } catch (e) {
553
+ }
554
+ }
555
+ });
556
+ }
557
+ };
558
+
559
+ Object.extend(Ajax.Responders, Enumerable);
560
+
561
+ Ajax.Responders.register({
562
+ onCreate: function() {
563
+ Ajax.activeRequestCount++;
564
+ },
565
+
566
+ onComplete: function() {
567
+ Ajax.activeRequestCount--;
568
+ }
569
+ });
570
+
179
571
  Ajax.Base = function() {};
180
572
  Ajax.Base.prototype = {
181
573
  setOptions: function(options) {
@@ -183,12 +575,13 @@ Ajax.Base.prototype = {
183
575
  method: 'post',
184
576
  asynchronous: true,
185
577
  parameters: ''
186
- }.extend(options || {});
578
+ }
579
+ Object.extend(this.options, options || {});
187
580
  },
188
581
 
189
582
  responseIsSuccess: function() {
190
583
  return this.transport.status == undefined
191
- || this.transport.status == 0
584
+ || this.transport.status == 0
192
585
  || (this.transport.status >= 200 && this.transport.status < 300);
193
586
  },
194
587
 
@@ -198,10 +591,10 @@ Ajax.Base.prototype = {
198
591
  }
199
592
 
200
593
  Ajax.Request = Class.create();
201
- Ajax.Request.Events =
594
+ Ajax.Request.Events =
202
595
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
203
596
 
204
- Ajax.Request.prototype = (new Ajax.Base()).extend({
597
+ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
205
598
  initialize: function(url, options) {
206
599
  this.transport = Ajax.getTransport();
207
600
  this.setOptions(options);
@@ -213,10 +606,13 @@ Ajax.Request.prototype = (new Ajax.Base()).extend({
213
606
  if (parameters.length > 0) parameters += '&_=';
214
607
 
215
608
  try {
216
- if (this.options.method == 'get')
217
- url += '?' + parameters;
609
+ this.url = url;
610
+ if (this.options.method == 'get' && parameters.length > 0)
611
+ this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
612
+
613
+ Ajax.Responders.dispatch('onCreate', this, this.transport);
218
614
 
219
- this.transport.open(this.options.method, url,
615
+ this.transport.open(this.options.method, this.url,
220
616
  this.options.asynchronous);
221
617
 
222
618
  if (this.options.asynchronous) {
@@ -230,21 +626,23 @@ Ajax.Request.prototype = (new Ajax.Base()).extend({
230
626
  this.transport.send(this.options.method == 'post' ? body : null);
231
627
 
232
628
  } catch (e) {
629
+ (this.options.onException || Prototype.emptyFunction)(this, e);
630
+ Ajax.Responders.dispatch('onException', this, e);
233
631
  }
234
632
  },
235
633
 
236
634
  setRequestHeaders: function() {
237
- var requestHeaders =
635
+ var requestHeaders =
238
636
  ['X-Requested-With', 'XMLHttpRequest',
239
637
  'X-Prototype-Version', Prototype.Version];
240
638
 
241
639
  if (this.options.method == 'post') {
242
- requestHeaders.push('Content-type',
640
+ requestHeaders.push('Content-type',
243
641
  'application/x-www-form-urlencoded');
244
642
 
245
643
  /* Force "Connection: close" for Mozilla browsers to work around
246
644
  * a bug where XMLHttpReqeuest sends an incorrect Content-length
247
- * header. See Mozilla Bugzilla #246651.
645
+ * header. See Mozilla Bugzilla #246651.
248
646
  */
249
647
  if (this.transport.overrideMimeType)
250
648
  requestHeaders.push('Connection', 'close');
@@ -263,15 +661,26 @@ Ajax.Request.prototype = (new Ajax.Base()).extend({
263
661
  this.respondToReadyState(this.transport.readyState);
264
662
  },
265
663
 
664
+ evalJSON: function() {
665
+ try {
666
+ var json = this.transport.getResponseHeader('X-JSON'), object;
667
+ object = eval(json);
668
+ return object;
669
+ } catch (e) {
670
+ }
671
+ },
672
+
266
673
  respondToReadyState: function(readyState) {
267
674
  var event = Ajax.Request.Events[readyState];
675
+ var transport = this.transport, json = this.evalJSON();
268
676
 
269
677
  if (event == 'Complete')
270
678
  (this.options['on' + this.transport.status]
271
- || this.options['on' + this.responseIsSuccess() ? 'Success' : 'Failure']
272
- || Prototype.emptyFunction)(this.transport);
679
+ || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
680
+ || Prototype.emptyFunction)(transport, json);
273
681
 
274
- (this.options['on' + event] || Prototype.emptyFunction)(this.transport);
682
+ (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
683
+ Ajax.Responders.dispatch('on' + event, this, transport, json);
275
684
 
276
685
  /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
277
686
  if (event == 'Complete')
@@ -282,7 +691,7 @@ Ajax.Request.prototype = (new Ajax.Base()).extend({
282
691
  Ajax.Updater = Class.create();
283
692
  Ajax.Updater.ScriptFragment = '(?:<script.*?>)((\n|.)*?)(?:<\/script>)';
284
693
 
285
- Ajax.Updater.prototype.extend(Ajax.Request.prototype).extend({
694
+ Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
286
695
  initialize: function(container, url, options) {
287
696
  this.containers = {
288
697
  success: container.success ? $(container.success) : $(container),
@@ -294,9 +703,9 @@ Ajax.Updater.prototype.extend(Ajax.Request.prototype).extend({
294
703
  this.setOptions(options);
295
704
 
296
705
  var onComplete = this.options.onComplete || Prototype.emptyFunction;
297
- this.options.onComplete = (function() {
706
+ this.options.onComplete = (function(transport, object) {
298
707
  this.updateContent();
299
- onComplete(this.transport);
708
+ onComplete(transport, object);
300
709
  }).bind(this);
301
710
 
302
711
  this.request(url);
@@ -320,8 +729,7 @@ Ajax.Updater.prototype.extend(Ajax.Request.prototype).extend({
320
729
 
321
730
  if (this.responseIsSuccess()) {
322
731
  if (this.onComplete)
323
- setTimeout((function() {this.onComplete(
324
- this.transport)}).bind(this), 10);
732
+ setTimeout(this.onComplete.bind(this), 10);
325
733
  }
326
734
 
327
735
  if (this.options.evalScripts && scripts) {
@@ -335,13 +743,13 @@ Ajax.Updater.prototype.extend(Ajax.Request.prototype).extend({
335
743
  });
336
744
 
337
745
  Ajax.PeriodicalUpdater = Class.create();
338
- Ajax.PeriodicalUpdater.prototype = (new Ajax.Base()).extend({
746
+ Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
339
747
  initialize: function(container, url, options) {
340
748
  this.setOptions(options);
341
749
  this.onComplete = this.options.onComplete;
342
750
 
343
751
  this.frequency = (this.options.frequency || 2);
344
- this.decay = 1;
752
+ this.decay = (this.options.decay || 1);
345
753
 
346
754
  this.updater = {};
347
755
  this.container = container;
@@ -358,17 +766,17 @@ Ajax.PeriodicalUpdater.prototype = (new Ajax.Base()).extend({
358
766
  stop: function() {
359
767
  this.updater.onComplete = undefined;
360
768
  clearTimeout(this.timer);
361
- (this.onComplete || Ajax.emptyFunction).apply(this, arguments);
769
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
362
770
  },
363
771
 
364
772
  updateComplete: function(request) {
365
773
  if (this.options.decay) {
366
- this.decay = (request.responseText == this.lastText ?
774
+ this.decay = (request.responseText == this.lastText ?
367
775
  this.decay * this.options.decay : 1);
368
776
 
369
777
  this.lastText = request.responseText;
370
778
  }
371
- this.timer = setTimeout(this.onTimerEvent.bind(this),
779
+ this.timer = setTimeout(this.onTimerEvent.bind(this),
372
780
  this.decay * this.frequency * 1000);
373
781
  },
374
782
 
@@ -376,23 +784,13 @@ Ajax.PeriodicalUpdater.prototype = (new Ajax.Base()).extend({
376
784
  this.updater = new Ajax.Updater(this.container, this.url, this.options);
377
785
  }
378
786
  });
379
-
380
- document.getElementsByClassName = function(className) {
381
- var children = document.getElementsByTagName('*') || document.all;
382
- var elements = new Array();
383
-
384
- for (var i = 0; i < children.length; i++) {
385
- var child = children[i];
386
- var classNames = child.className.split(' ');
387
- for (var j = 0; j < classNames.length; j++) {
388
- if (classNames[j] == className) {
389
- elements.push(child);
390
- break;
391
- }
392
- }
393
- }
394
-
395
- return elements;
787
+ document.getElementsByClassName = function(className, parentElement) {
788
+ var children = ($(parentElement) || document.body).getElementsByTagName('*');
789
+ return $A(children).inject([], function(elements, child) {
790
+ if (Element.hasClassName(child, className))
791
+ elements.push(child);
792
+ return elements;
793
+ });
396
794
  }
397
795
 
398
796
  /*--------------------------------------------------------------------------*/
@@ -402,11 +800,14 @@ if (!window.Element) {
402
800
  }
403
801
 
404
802
  Object.extend(Element, {
803
+ visible: function(element) {
804
+ return $(element).style.display != 'none';
805
+ },
806
+
405
807
  toggle: function() {
406
808
  for (var i = 0; i < arguments.length; i++) {
407
809
  var element = $(arguments[i]);
408
- element.style.display =
409
- (element.style.display == 'none' ? '' : 'none');
810
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
410
811
  }
411
812
  },
412
813
 
@@ -428,54 +829,131 @@ Object.extend(Element, {
428
829
  element = $(element);
429
830
  element.parentNode.removeChild(element);
430
831
  },
431
-
832
+
432
833
  getHeight: function(element) {
433
834
  element = $(element);
434
- return element.offsetHeight;
835
+ return element.offsetHeight;
836
+ },
837
+
838
+ classNames: function(element) {
839
+ return new Element.ClassNames(element);
435
840
  },
436
841
 
437
842
  hasClassName: function(element, className) {
438
- element = $(element);
439
- if (!element)
440
- return;
441
- var a = element.className.split(' ');
442
- for (var i = 0; i < a.length; i++) {
443
- if (a[i] == className)
444
- return true;
445
- }
446
- return false;
843
+ if (!(element = $(element))) return;
844
+ return Element.classNames(element).include(className);
447
845
  },
448
846
 
449
847
  addClassName: function(element, className) {
450
- element = $(element);
451
- Element.removeClassName(element, className);
452
- element.className += ' ' + className;
848
+ if (!(element = $(element))) return;
849
+ return Element.classNames(element).add(className);
453
850
  },
454
851
 
455
852
  removeClassName: function(element, className) {
456
- element = $(element);
457
- if (!element)
458
- return;
459
- var newClassName = '';
460
- var a = element.className.split(' ');
461
- for (var i = 0; i < a.length; i++) {
462
- if (a[i] != className) {
463
- if (i > 0)
464
- newClassName += ' ';
465
- newClassName += a[i];
466
- }
467
- }
468
- element.className = newClassName;
853
+ if (!(element = $(element))) return;
854
+ return Element.classNames(element).remove(className);
469
855
  },
470
-
856
+
471
857
  // removes whitespace-only text node children
472
858
  cleanWhitespace: function(element) {
473
- var element = $(element);
859
+ element = $(element);
474
860
  for (var i = 0; i < element.childNodes.length; i++) {
475
861
  var node = element.childNodes[i];
476
- if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
862
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
477
863
  Element.remove(node);
478
864
  }
865
+ },
866
+
867
+ empty: function(element) {
868
+ return $(element).innerHTML.match(/^\s*$/);
869
+ },
870
+
871
+ scrollTo: function(element) {
872
+ element = $(element);
873
+ var x = element.x ? element.x : element.offsetLeft,
874
+ y = element.y ? element.y : element.offsetTop;
875
+ window.scrollTo(x, y);
876
+ },
877
+
878
+ getStyle: function(element, style) {
879
+ element = $(element);
880
+ var value = element.style[style.camelize()];
881
+ if (!value) {
882
+ if (document.defaultView && document.defaultView.getComputedStyle) {
883
+ var css = document.defaultView.getComputedStyle(element, null);
884
+ value = css ? css.getPropertyValue(style) : null;
885
+ } else if (element.currentStyle) {
886
+ value = element.currentStyle[style.camelize()];
887
+ }
888
+ }
889
+
890
+ if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
891
+ if (Element.getStyle(element, 'position') == 'static') value = 'auto';
892
+
893
+ return value == 'auto' ? null : value;
894
+ },
895
+
896
+ getDimensions: function(element) {
897
+ element = $(element);
898
+ if (Element.getStyle(element, 'display') != 'none')
899
+ return {width: element.offsetWidth, height: element.offsetHeight};
900
+
901
+ // All *Width and *Height properties give 0 on elements with display none,
902
+ // so enable the element temporarily
903
+ var els = element.style;
904
+ var originalVisibility = els.visibility;
905
+ var originalPosition = els.position;
906
+ els.visibility = 'hidden';
907
+ els.position = 'absolute';
908
+ els.display = '';
909
+ var originalWidth = element.clientWidth;
910
+ var originalHeight = element.clientHeight;
911
+ els.display = 'none';
912
+ els.position = originalPosition;
913
+ els.visibility = originalVisibility;
914
+ return {width: originalWidth, height: originalHeight};
915
+ },
916
+
917
+ makePositioned: function(element) {
918
+ element = $(element);
919
+ var pos = Element.getStyle(element, 'position');
920
+ if (pos == 'static' || !pos) {
921
+ element._madePositioned = true;
922
+ element.style.position = 'relative';
923
+ // Opera returns the offset relative to the positioning context, when an
924
+ // element is position relative but top and left have not been defined
925
+ if (window.opera) {
926
+ element.style.top = 0;
927
+ element.style.left = 0;
928
+ }
929
+ }
930
+ },
931
+
932
+ undoPositioned: function(element) {
933
+ element = $(element);
934
+ if (element._madePositioned) {
935
+ element._madePositioned = undefined;
936
+ element.style.position =
937
+ element.style.top =
938
+ element.style.left =
939
+ element.style.bottom =
940
+ element.style.right = '';
941
+ }
942
+ },
943
+
944
+ makeClipping: function(element) {
945
+ element = $(element);
946
+ if (element._overflow) return;
947
+ element._overflow = element.style.overflow;
948
+ if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
949
+ element.style.overflow = 'hidden';
950
+ },
951
+
952
+ undoClipping: function(element) {
953
+ element = $(element);
954
+ if (element._overflow) return;
955
+ element.style.overflow = element._overflow;
956
+ element._overflow = undefined;
479
957
  }
480
958
  });
481
959
 
@@ -492,67 +970,124 @@ Abstract.Insertion.prototype = {
492
970
  initialize: function(element, content) {
493
971
  this.element = $(element);
494
972
  this.content = content;
495
-
973
+
496
974
  if (this.adjacency && this.element.insertAdjacentHTML) {
497
- this.element.insertAdjacentHTML(this.adjacency, this.content);
975
+ try {
976
+ this.element.insertAdjacentHTML(this.adjacency, this.content);
977
+ } catch (e) {
978
+ if (this.element.tagName.toLowerCase() == 'tbody') {
979
+ this.insertContent(this.contentFromAnonymousTable());
980
+ } else {
981
+ throw e;
982
+ }
983
+ }
498
984
  } else {
499
985
  this.range = this.element.ownerDocument.createRange();
500
986
  if (this.initializeRange) this.initializeRange();
501
- this.fragment = this.range.createContextualFragment(this.content);
502
- this.insertContent();
987
+ this.insertContent([this.range.createContextualFragment(this.content)]);
503
988
  }
989
+ },
990
+
991
+ contentFromAnonymousTable: function() {
992
+ var div = document.createElement('div');
993
+ div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
994
+ return $A(div.childNodes[0].childNodes[0].childNodes);
504
995
  }
505
996
  }
506
997
 
507
998
  var Insertion = new Object();
508
999
 
509
1000
  Insertion.Before = Class.create();
510
- Insertion.Before.prototype = (new Abstract.Insertion('beforeBegin')).extend({
1001
+ Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
511
1002
  initializeRange: function() {
512
1003
  this.range.setStartBefore(this.element);
513
1004
  },
514
-
515
- insertContent: function() {
516
- this.element.parentNode.insertBefore(this.fragment, this.element);
1005
+
1006
+ insertContent: function(fragments) {
1007
+ fragments.each((function(fragment) {
1008
+ this.element.parentNode.insertBefore(fragment, this.element);
1009
+ }).bind(this));
517
1010
  }
518
1011
  });
519
1012
 
520
1013
  Insertion.Top = Class.create();
521
- Insertion.Top.prototype = (new Abstract.Insertion('afterBegin')).extend({
1014
+ Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
522
1015
  initializeRange: function() {
523
1016
  this.range.selectNodeContents(this.element);
524
1017
  this.range.collapse(true);
525
1018
  },
526
-
527
- insertContent: function() {
528
- this.element.insertBefore(this.fragment, this.element.firstChild);
1019
+
1020
+ insertContent: function(fragments) {
1021
+ fragments.reverse().each((function(fragment) {
1022
+ this.element.insertBefore(fragment, this.element.firstChild);
1023
+ }).bind(this));
529
1024
  }
530
1025
  });
531
1026
 
532
1027
  Insertion.Bottom = Class.create();
533
- Insertion.Bottom.prototype = (new Abstract.Insertion('beforeEnd')).extend({
1028
+ Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
534
1029
  initializeRange: function() {
535
1030
  this.range.selectNodeContents(this.element);
536
1031
  this.range.collapse(this.element);
537
1032
  },
538
-
539
- insertContent: function() {
540
- this.element.appendChild(this.fragment);
1033
+
1034
+ insertContent: function(fragments) {
1035
+ fragments.each((function(fragment) {
1036
+ this.element.appendChild(fragment);
1037
+ }).bind(this));
541
1038
  }
542
1039
  });
543
1040
 
544
1041
  Insertion.After = Class.create();
545
- Insertion.After.prototype = (new Abstract.Insertion('afterEnd')).extend({
1042
+ Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
546
1043
  initializeRange: function() {
547
1044
  this.range.setStartAfter(this.element);
548
1045
  },
549
-
550
- insertContent: function() {
551
- this.element.parentNode.insertBefore(this.fragment,
552
- this.element.nextSibling);
1046
+
1047
+ insertContent: function(fragments) {
1048
+ fragments.each((function(fragment) {
1049
+ this.element.parentNode.insertBefore(fragment,
1050
+ this.element.nextSibling);
1051
+ }).bind(this));
553
1052
  }
554
1053
  });
555
1054
 
1055
+ /*--------------------------------------------------------------------------*/
1056
+
1057
+ Element.ClassNames = Class.create();
1058
+ Element.ClassNames.prototype = {
1059
+ initialize: function(element) {
1060
+ this.element = $(element);
1061
+ },
1062
+
1063
+ _each: function(iterator) {
1064
+ this.element.className.split(/\s+/).select(function(name) {
1065
+ return name.length > 0;
1066
+ })._each(iterator);
1067
+ },
1068
+
1069
+ set: function(className) {
1070
+ this.element.className = className;
1071
+ },
1072
+
1073
+ add: function(classNameToAdd) {
1074
+ if (this.include(classNameToAdd)) return;
1075
+ this.set(this.toArray().concat(classNameToAdd).join(' '));
1076
+ },
1077
+
1078
+ remove: function(classNameToRemove) {
1079
+ if (!this.include(classNameToRemove)) return;
1080
+ this.set(this.select(function(className) {
1081
+ return className != classNameToRemove;
1082
+ }));
1083
+ },
1084
+
1085
+ toString: function() {
1086
+ return this.toArray().join(' ');
1087
+ }
1088
+ }
1089
+
1090
+ Object.extend(Element.ClassNames.prototype, Enumerable);
556
1091
  var Field = {
557
1092
  clear: function() {
558
1093
  for (var i = 0; i < arguments.length; i++)
@@ -562,17 +1097,17 @@ var Field = {
562
1097
  focus: function(element) {
563
1098
  $(element).focus();
564
1099
  },
565
-
1100
+
566
1101
  present: function() {
567
1102
  for (var i = 0; i < arguments.length; i++)
568
1103
  if ($(arguments[i]).value == '') return false;
569
1104
  return true;
570
1105
  },
571
-
1106
+
572
1107
  select: function(element) {
573
1108
  $(element).select();
574
1109
  },
575
-
1110
+
576
1111
  activate: function(element) {
577
1112
  $(element).focus();
578
1113
  $(element).select();
@@ -585,16 +1120,16 @@ var Form = {
585
1120
  serialize: function(form) {
586
1121
  var elements = Form.getElements($(form));
587
1122
  var queryComponents = new Array();
588
-
1123
+
589
1124
  for (var i = 0; i < elements.length; i++) {
590
1125
  var queryComponent = Form.Element.serialize(elements[i]);
591
1126
  if (queryComponent)
592
1127
  queryComponents.push(queryComponent);
593
1128
  }
594
-
1129
+
595
1130
  return queryComponents.join('&');
596
1131
  },
597
-
1132
+
598
1133
  getElements: function(form) {
599
1134
  var form = $(form);
600
1135
  var elements = new Array();
@@ -606,19 +1141,19 @@ var Form = {
606
1141
  }
607
1142
  return elements;
608
1143
  },
609
-
1144
+
610
1145
  getInputs: function(form, typeName, name) {
611
1146
  var form = $(form);
612
1147
  var inputs = form.getElementsByTagName('input');
613
-
1148
+
614
1149
  if (!typeName && !name)
615
1150
  return inputs;
616
-
1151
+
617
1152
  var matchingInputs = new Array();
618
1153
  for (var i = 0; i < inputs.length; i++) {
619
1154
  var input = inputs[i];
620
1155
  if ((typeName && input.type != typeName) ||
621
- (name && input.name != name))
1156
+ (name && input.name != name))
622
1157
  continue;
623
1158
  matchingInputs.push(input);
624
1159
  }
@@ -665,18 +1200,18 @@ Form.Element = {
665
1200
  var element = $(element);
666
1201
  var method = element.tagName.toLowerCase();
667
1202
  var parameter = Form.Element.Serializers[method](element);
668
-
1203
+
669
1204
  if (parameter)
670
- return encodeURIComponent(parameter[0]) + '=' +
671
- encodeURIComponent(parameter[1]);
1205
+ return encodeURIComponent(parameter[0]) + '=' +
1206
+ encodeURIComponent(parameter[1]);
672
1207
  },
673
-
1208
+
674
1209
  getValue: function(element) {
675
1210
  var element = $(element);
676
1211
  var method = element.tagName.toLowerCase();
677
1212
  var parameter = Form.Element.Serializers[method](element);
678
-
679
- if (parameter)
1213
+
1214
+ if (parameter)
680
1215
  return parameter[1];
681
1216
  }
682
1217
  }
@@ -689,7 +1224,7 @@ Form.Element.Serializers = {
689
1224
  case 'password':
690
1225
  case 'text':
691
1226
  return Form.Element.Serializers.textarea(element);
692
- case 'checkbox':
1227
+ case 'checkbox':
693
1228
  case 'radio':
694
1229
  return Form.Element.Serializers.inputSelector(element);
695
1230
  }
@@ -706,17 +1241,30 @@ Form.Element.Serializers = {
706
1241
  },
707
1242
 
708
1243
  select: function(element) {
709
- var value = '';
710
- if (element.type == 'select-one') {
711
- var index = element.selectedIndex;
712
- if (index >= 0)
713
- value = element.options[index].value || element.options[index].text;
714
- } else {
715
- value = new Array();
716
- for (var i = 0; i < element.length; i++) {
717
- var opt = element.options[i];
718
- if (opt.selected)
719
- value.push(opt.value || opt.text);
1244
+ return Form.Element.Serializers[element.type == 'select-one' ?
1245
+ 'selectOne' : 'selectMany'](element);
1246
+ },
1247
+
1248
+ selectOne: function(element) {
1249
+ var value = '', opt, index = element.selectedIndex;
1250
+ if (index >= 0) {
1251
+ opt = element.options[index];
1252
+ value = opt.value;
1253
+ if (!value && !('value' in opt))
1254
+ value = opt.text;
1255
+ }
1256
+ return [element.name, value];
1257
+ },
1258
+
1259
+ selectMany: function(element) {
1260
+ var value = new Array();
1261
+ for (var i = 0; i < element.length; i++) {
1262
+ var opt = element.options[i];
1263
+ if (opt.selected) {
1264
+ var optValue = opt.value;
1265
+ if (!optValue && !('value' in opt))
1266
+ optValue = opt.text;
1267
+ value.push(optValue);
720
1268
  }
721
1269
  }
722
1270
  return [element.name, value];
@@ -735,15 +1283,15 @@ Abstract.TimedObserver.prototype = {
735
1283
  this.frequency = frequency;
736
1284
  this.element = $(element);
737
1285
  this.callback = callback;
738
-
1286
+
739
1287
  this.lastValue = this.getValue();
740
1288
  this.registerCallback();
741
1289
  },
742
-
1290
+
743
1291
  registerCallback: function() {
744
1292
  setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
745
1293
  },
746
-
1294
+
747
1295
  onTimerEvent: function() {
748
1296
  var value = this.getValue();
749
1297
  if (this.lastValue != value) {
@@ -754,14 +1302,14 @@ Abstract.TimedObserver.prototype = {
754
1302
  }
755
1303
 
756
1304
  Form.Element.Observer = Class.create();
757
- Form.Element.Observer.prototype = (new Abstract.TimedObserver()).extend({
1305
+ Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
758
1306
  getValue: function() {
759
1307
  return Form.Element.getValue(this.element);
760
1308
  }
761
1309
  });
762
1310
 
763
1311
  Form.Observer = Class.create();
764
- Form.Observer.prototype = (new Abstract.TimedObserver()).extend({
1312
+ Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
765
1313
  getValue: function() {
766
1314
  return Form.serialize(this.element);
767
1315
  }
@@ -774,14 +1322,14 @@ Abstract.EventObserver.prototype = {
774
1322
  initialize: function(element, callback) {
775
1323
  this.element = $(element);
776
1324
  this.callback = callback;
777
-
1325
+
778
1326
  this.lastValue = this.getValue();
779
1327
  if (this.element.tagName.toLowerCase() == 'form')
780
1328
  this.registerFormCallbacks();
781
1329
  else
782
1330
  this.registerCallback(this.element);
783
1331
  },
784
-
1332
+
785
1333
  onElementEvent: function() {
786
1334
  var value = this.getValue();
787
1335
  if (this.lastValue != value) {
@@ -789,22 +1337,22 @@ Abstract.EventObserver.prototype = {
789
1337
  this.lastValue = value;
790
1338
  }
791
1339
  },
792
-
1340
+
793
1341
  registerFormCallbacks: function() {
794
1342
  var elements = Form.getElements(this.element);
795
1343
  for (var i = 0; i < elements.length; i++)
796
1344
  this.registerCallback(elements[i]);
797
1345
  },
798
-
1346
+
799
1347
  registerCallback: function(element) {
800
1348
  if (element.type) {
801
1349
  switch (element.type.toLowerCase()) {
802
- case 'checkbox':
1350
+ case 'checkbox':
803
1351
  case 'radio':
804
1352
  element.target = this;
805
1353
  element.prev_onclick = element.onclick || Prototype.emptyFunction;
806
1354
  element.onclick = function() {
807
- this.prev_onclick();
1355
+ this.prev_onclick();
808
1356
  this.target.onElementEvent();
809
1357
  }
810
1358
  break;
@@ -816,30 +1364,28 @@ Abstract.EventObserver.prototype = {
816
1364
  element.target = this;
817
1365
  element.prev_onchange = element.onchange || Prototype.emptyFunction;
818
1366
  element.onchange = function() {
819
- this.prev_onchange();
1367
+ this.prev_onchange();
820
1368
  this.target.onElementEvent();
821
1369
  }
822
1370
  break;
823
1371
  }
824
- }
1372
+ }
825
1373
  }
826
1374
  }
827
1375
 
828
1376
  Form.Element.EventObserver = Class.create();
829
- Form.Element.EventObserver.prototype = (new Abstract.EventObserver()).extend({
1377
+ Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
830
1378
  getValue: function() {
831
1379
  return Form.Element.getValue(this.element);
832
1380
  }
833
1381
  });
834
1382
 
835
1383
  Form.EventObserver = Class.create();
836
- Form.EventObserver.prototype = (new Abstract.EventObserver()).extend({
1384
+ Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
837
1385
  getValue: function() {
838
1386
  return Form.serialize(this.element);
839
1387
  }
840
1388
  });
841
-
842
-
843
1389
  if (!window.Event) {
844
1390
  var Event = new Object();
845
1391
  }
@@ -865,21 +1411,22 @@ Object.extend(Event, {
865
1411
  },
866
1412
 
867
1413
  pointerX: function(event) {
868
- return event.pageX || (event.clientX +
1414
+ return event.pageX || (event.clientX +
869
1415
  (document.documentElement.scrollLeft || document.body.scrollLeft));
870
1416
  },
871
1417
 
872
1418
  pointerY: function(event) {
873
- return event.pageY || (event.clientY +
1419
+ return event.pageY || (event.clientY +
874
1420
  (document.documentElement.scrollTop || document.body.scrollTop));
875
1421
  },
876
1422
 
877
1423
  stop: function(event) {
878
- if (event.preventDefault) {
879
- event.preventDefault();
880
- event.stopPropagation();
1424
+ if (event.preventDefault) {
1425
+ event.preventDefault();
1426
+ event.stopPropagation();
881
1427
  } else {
882
1428
  event.returnValue = false;
1429
+ event.cancelBubble = true;
883
1430
  }
884
1431
  },
885
1432
 
@@ -894,7 +1441,7 @@ Object.extend(Event, {
894
1441
  },
895
1442
 
896
1443
  observers: false,
897
-
1444
+
898
1445
  _observeAndCache: function(element, name, observer, useCapture) {
899
1446
  if (!this.observers) this.observers = [];
900
1447
  if (element.addEventListener) {
@@ -905,7 +1452,7 @@ Object.extend(Event, {
905
1452
  element.attachEvent('on' + name, observer);
906
1453
  }
907
1454
  },
908
-
1455
+
909
1456
  unloadCache: function() {
910
1457
  if (!Event.observers) return;
911
1458
  for (var i = 0; i < Event.observers.length; i++) {
@@ -918,24 +1465,24 @@ Object.extend(Event, {
918
1465
  observe: function(element, name, observer, useCapture) {
919
1466
  var element = $(element);
920
1467
  useCapture = useCapture || false;
921
-
1468
+
922
1469
  if (name == 'keypress' &&
923
- ((navigator.appVersion.indexOf('AppleWebKit') > 0)
1470
+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
924
1471
  || element.attachEvent))
925
1472
  name = 'keydown';
926
-
1473
+
927
1474
  this._observeAndCache(element, name, observer, useCapture);
928
1475
  },
929
1476
 
930
1477
  stopObserving: function(element, name, observer, useCapture) {
931
1478
  var element = $(element);
932
1479
  useCapture = useCapture || false;
933
-
1480
+
934
1481
  if (name == 'keypress' &&
935
- ((navigator.appVersion.indexOf('AppleWebKit') > 0)
1482
+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
936
1483
  || element.detachEvent))
937
1484
  name = 'keydown';
938
-
1485
+
939
1486
  if (element.removeEventListener) {
940
1487
  element.removeEventListener(name, observer, useCapture);
941
1488
  } else if (element.detachEvent) {
@@ -946,24 +1493,22 @@ Object.extend(Event, {
946
1493
 
947
1494
  /* prevent memory leaks in IE */
948
1495
  Event.observe(window, 'unload', Event.unloadCache, false);
949
-
950
1496
  var Position = {
951
-
952
1497
  // set to true if needed, warning: firefox performance problems
953
1498
  // NOT neeeded for page scrolling, only if draggable contained in
954
1499
  // scrollable elements
955
- includeScrollOffsets: false,
1500
+ includeScrollOffsets: false,
956
1501
 
957
1502
  // must be called before calling withinIncludingScrolloffset, every time the
958
1503
  // page is scrolled
959
1504
  prepare: function() {
960
- this.deltaX = window.pageXOffset
961
- || document.documentElement.scrollLeft
962
- || document.body.scrollLeft
1505
+ this.deltaX = window.pageXOffset
1506
+ || document.documentElement.scrollLeft
1507
+ || document.body.scrollLeft
963
1508
  || 0;
964
- this.deltaY = window.pageYOffset
965
- || document.documentElement.scrollTop
966
- || document.body.scrollTop
1509
+ this.deltaY = window.pageYOffset
1510
+ || document.documentElement.scrollTop
1511
+ || document.body.scrollTop
967
1512
  || 0;
968
1513
  },
969
1514
 
@@ -971,7 +1516,7 @@ var Position = {
971
1516
  var valueT = 0, valueL = 0;
972
1517
  do {
973
1518
  valueT += element.scrollTop || 0;
974
- valueL += element.scrollLeft || 0;
1519
+ valueL += element.scrollLeft || 0;
975
1520
  element = element.parentNode;
976
1521
  } while (element);
977
1522
  return [valueL, valueT];
@@ -987,6 +1532,31 @@ var Position = {
987
1532
  return [valueL, valueT];
988
1533
  },
989
1534
 
1535
+ positionedOffset: function(element) {
1536
+ var valueT = 0, valueL = 0;
1537
+ do {
1538
+ valueT += element.offsetTop || 0;
1539
+ valueL += element.offsetLeft || 0;
1540
+ element = element.offsetParent;
1541
+ if (element) {
1542
+ p = Element.getStyle(element, 'position');
1543
+ if (p == 'relative' || p == 'absolute') break;
1544
+ }
1545
+ } while (element);
1546
+ return [valueL, valueT];
1547
+ },
1548
+
1549
+ offsetParent: function(element) {
1550
+ if (element.offsetParent) return element.offsetParent;
1551
+ if (element == document.body) return element;
1552
+
1553
+ while ((element = element.parentNode) && element != document.body)
1554
+ if (Element.getStyle(element, 'position') != 'static')
1555
+ return element;
1556
+
1557
+ return document.body;
1558
+ },
1559
+
990
1560
  // caches x/y coordinate pair to use with overlap
991
1561
  within: function(element, x, y) {
992
1562
  if (this.includeScrollOffsets)
@@ -997,7 +1567,7 @@ var Position = {
997
1567
 
998
1568
  return (y >= this.offset[1] &&
999
1569
  y < this.offset[1] + element.offsetHeight &&
1000
- x >= this.offset[0] &&
1570
+ x >= this.offset[0] &&
1001
1571
  x < this.offset[0] + element.offsetWidth);
1002
1572
  },
1003
1573
 
@@ -1010,18 +1580,18 @@ var Position = {
1010
1580
 
1011
1581
  return (this.ycomp >= this.offset[1] &&
1012
1582
  this.ycomp < this.offset[1] + element.offsetHeight &&
1013
- this.xcomp >= this.offset[0] &&
1583
+ this.xcomp >= this.offset[0] &&
1014
1584
  this.xcomp < this.offset[0] + element.offsetWidth);
1015
1585
  },
1016
1586
 
1017
1587
  // within must be called directly before
1018
- overlap: function(mode, element) {
1019
- if (!mode) return 0;
1020
- if (mode == 'vertical')
1021
- return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
1588
+ overlap: function(mode, element) {
1589
+ if (!mode) return 0;
1590
+ if (mode == 'vertical')
1591
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
1022
1592
  element.offsetHeight;
1023
1593
  if (mode == 'horizontal')
1024
- return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
1594
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
1025
1595
  element.offsetWidth;
1026
1596
  },
1027
1597
 
@@ -1034,5 +1604,123 @@ var Position = {
1034
1604
  target.style.left = offsets[0] + 'px';
1035
1605
  target.style.width = source.offsetWidth + 'px';
1036
1606
  target.style.height = source.offsetHeight + 'px';
1607
+ },
1608
+
1609
+ page: function(forElement) {
1610
+ var valueT = 0, valueL = 0;
1611
+
1612
+ var element = forElement;
1613
+ do {
1614
+ valueT += element.offsetTop || 0;
1615
+ valueL += element.offsetLeft || 0;
1616
+
1617
+ // Safari fix
1618
+ if (element.offsetParent==document.body)
1619
+ if (Element.getStyle(element,'position')=='absolute') break;
1620
+
1621
+ } while (element = element.offsetParent);
1622
+
1623
+ element = forElement;
1624
+ do {
1625
+ valueT -= element.scrollTop || 0;
1626
+ valueL -= element.scrollLeft || 0;
1627
+ } while (element = element.parentNode);
1628
+
1629
+ return [valueL, valueT];
1630
+ },
1631
+
1632
+ clone: function(source, target) {
1633
+ var options = Object.extend({
1634
+ setLeft: true,
1635
+ setTop: true,
1636
+ setWidth: true,
1637
+ setHeight: true,
1638
+ offsetTop: 0,
1639
+ offsetLeft: 0
1640
+ }, arguments[2] || {})
1641
+
1642
+ // find page position of source
1643
+ source = $(source);
1644
+ var p = Position.page(source);
1645
+
1646
+ // find coordinate system to use
1647
+ target = $(target);
1648
+ var delta = [0, 0];
1649
+ var parent = null;
1650
+ // delta [0,0] will do fine with position: fixed elements,
1651
+ // position:absolute needs offsetParent deltas
1652
+ if (Element.getStyle(target,'position') == 'absolute') {
1653
+ parent = Position.offsetParent(target);
1654
+ delta = Position.page(parent);
1655
+ }
1656
+
1657
+ // correct by body offsets (fixes Safari)
1658
+ if (parent == document.body) {
1659
+ delta[0] -= document.body.offsetLeft;
1660
+ delta[1] -= document.body.offsetTop;
1661
+ }
1662
+
1663
+ // set position
1664
+ if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
1665
+ if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
1666
+ if(options.setWidth) target.style.width = source.offsetWidth + 'px';
1667
+ if(options.setHeight) target.style.height = source.offsetHeight + 'px';
1668
+ },
1669
+
1670
+ absolutize: function(element) {
1671
+ element = $(element);
1672
+ if (element.style.position == 'absolute') return;
1673
+ Position.prepare();
1674
+
1675
+ var offsets = Position.positionedOffset(element);
1676
+ var top = offsets[1];
1677
+ var left = offsets[0];
1678
+ var width = element.clientWidth;
1679
+ var height = element.clientHeight;
1680
+
1681
+ element._originalLeft = left - parseFloat(element.style.left || 0);
1682
+ element._originalTop = top - parseFloat(element.style.top || 0);
1683
+ element._originalWidth = element.style.width;
1684
+ element._originalHeight = element.style.height;
1685
+
1686
+ element.style.position = 'absolute';
1687
+ element.style.top = top + 'px';;
1688
+ element.style.left = left + 'px';;
1689
+ element.style.width = width + 'px';;
1690
+ element.style.height = height + 'px';;
1691
+ },
1692
+
1693
+ relativize: function(element) {
1694
+ element = $(element);
1695
+ if (element.style.position == 'relative') return;
1696
+ Position.prepare();
1697
+
1698
+ element.style.position = 'relative';
1699
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
1700
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
1701
+
1702
+ element.style.top = top + 'px';
1703
+ element.style.left = left + 'px';
1704
+ element.style.height = element._originalHeight;
1705
+ element.style.width = element._originalWidth;
1037
1706
  }
1038
1707
  }
1708
+
1709
+ // Safari returns margins on body which is incorrect if the child is absolutely
1710
+ // positioned. For performance reasons, redefine Position.cumulativeOffset for
1711
+ // KHTML/WebKit only.
1712
+ if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
1713
+ Position.cumulativeOffset = function(element) {
1714
+ var valueT = 0, valueL = 0;
1715
+ do {
1716
+ valueT += element.offsetTop || 0;
1717
+ valueL += element.offsetLeft || 0;
1718
+ if (element.offsetParent == document.body)
1719
+ if (Element.getStyle(element, 'position') == 'absolute') break;
1720
+
1721
+ element = element.offsetParent;
1722
+ } while (element);
1723
+
1724
+ return [valueL, valueT];
1725
+ }
1726
+ }