rspec 1.1.4 → 1.1.5

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