taskr 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
@@ -0,0 +1,25 @@
1
+ CHANGELOG.txt
2
+ GPLv3-LICENSE.txt
3
+ History.txt
4
+ Manifest.txt
5
+ README.txt
6
+ Rakefile
7
+ bin/taskr
8
+ bin/taskr-ctl
9
+ config.example.yml
10
+ examples/active_resource_client_example.rb
11
+ examples/php_client_example.php
12
+ lib/public/prototype.js
13
+ lib/public/taskr.css
14
+ lib/taskr.rb
15
+ lib/taskr/actions.rb
16
+ lib/taskr/controllers.rb
17
+ lib/taskr/environment.rb
18
+ lib/taskr/helpers.rb
19
+ lib/taskr/models.rb
20
+ lib/taskr/version.rb
21
+ lib/taskr/views.rb
22
+ setup.rb
23
+ test.rb
24
+ test/taskr_test.rb
25
+ test/test_helper.rb
@@ -0,0 +1,14 @@
1
+ === LICENSE ===
2
+
3
+ This program is free software: you can redistribute it and/or modify
4
+ it under the terms of the GNU General Public License as published by
5
+ the Free Software Foundation, either version 3 of the License, or
6
+ (at your option) any later version.
7
+
8
+ This program is distributed in the hope that it will be useful,
9
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ GNU General Public License for more details.
12
+
13
+ You should have received a copy of the GNU General Public License
14
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/clean'
4
+ require 'rake/testtask'
5
+ require 'rake/packagetask'
6
+ require 'rake/gempackagetask'
7
+ require 'rake/rdoctask'
8
+ require 'rake/contrib/rubyforgepublisher'
9
+ require 'fileutils'
10
+ require 'hoe'
11
+ include FileUtils
12
+ require File.join(File.dirname(__FILE__), 'lib', 'taskr', 'version')
13
+
14
+ AUTHOR = "URBACON\mzukowski" # can also be an array of Authors
15
+ EMAIL = "your contact email for bug fixes and info"
16
+ DESCRIPTION = "description of gem"
17
+ GEM_NAME = "taskr" # what ppl will type to install your gem
18
+ RUBYFORGE_PROJECT = "taskr" # The unix name for your project
19
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
20
+
21
+
22
+ NAME = "taskr"
23
+ REV = nil
24
+ #REV = `svn info`[/Revision: (\d+)/, 1] rescue nil
25
+ VERS = ENV['VERSION'] || (Taskr::VERSION::STRING + (REV ? ".#{REV}" : ""))
26
+ CLEAN.include ['**/.*.sw?', '*.gem', '.config']
27
+ RDOC_OPTS = ['--quiet', '--title', "taskr documentation",
28
+ "--opname", "index.html",
29
+ "--line-numbers",
30
+ "--main", "README",
31
+ "--inline-source"]
32
+
33
+ class Hoe
34
+ def extra_deps
35
+ @extra_deps.reject { |x| Array(x).first == 'hoe' }
36
+ end
37
+ end
38
+
39
+ # Generate all the Rake tasks
40
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
41
+ hoe = Hoe.new(GEM_NAME, VERS) do |p|
42
+ p.author = AUTHOR
43
+ p.description = DESCRIPTION
44
+ p.email = EMAIL
45
+ p.summary = DESCRIPTION
46
+ p.url = HOMEPATH
47
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
48
+ p.test_globs = ["test/**/*_test.rb"]
49
+ p.clean_globs = CLEAN #An array of file patterns to delete on clean.
50
+
51
+ # == Optional
52
+ #p.changes - A description of the release's latest changes.
53
+ #p.extra_deps - An array of rubygem dependencies.
54
+ #p.spec_extras - A hash of extra values to set in the gemspec.
55
+
56
+ p.extra_deps = ['picnic', ['reststop', '~>0.2.0'], 'openwferu-scheduler']
57
+ p.spec_extras = {:executables => ['taskr', 'taskr-ctl']}
58
+ end
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # ---------------------------------------------------------------------
4
+ # This file is part of Taskr (http://ruby-taskr.googlecode.com/).
5
+ #
6
+ # Taskr is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Taskr is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Taskr. If not, see <http://www.gnu.org/licenses/>.
18
+ # ---------------------------------------------------------------------
19
+
20
+ if File.exists?(picnic = File.expand_path(File.dirname(File.expand_path(__FILE__))+'/../vendor/picnic/lib'))
21
+ $: << picnic
22
+ elsif File.exists?(picnic = File.expand_path(File.dirname(File.expand_path(__FILE__))+'/../../picnic/lib'))
23
+ $: << picnic
24
+ else
25
+ require 'rubygems'
26
+ end
27
+
28
+ require 'picnic/cli'
29
+
30
+ cli = Picnic::Cli.new(
31
+ 'taskr',
32
+ :app_path => File.expand_path(File.dirname(File.expand_path(__FILE__)))
33
+ )
34
+
35
+ cli.handle_cli_input
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # ---------------------------------------------------------------------
4
+ # This file is part of Taskr (http://ruby-taskr.googlecode.com/).
5
+ #
6
+ # Taskr is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later verTaskr
10
+ #
11
+ # Taskr is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Taskr. If not, see <http://www.gnu.org/licenses/>.
18
+ # ---------------------------------------------------------------------
19
+
20
+ if File.exists?(picnic = File.expand_path(File.dirname(File.expand_path(__FILE__))+'/../vendor/picnic/lib'))
21
+ $: << picnic
22
+ elsif File.exists?(picnic = File.expand_path(File.dirname(File.expand_path(__FILE__))+'/../../picnic/lib'))
23
+ $: << picnic
24
+ else
25
+ require 'rubygems'
26
+ end
27
+
28
+ require 'picnic/service_control'
29
+
30
+ ctl = Picnic::ServiceControl.new('taskr')
31
+
32
+ ctl.handle_cli_input
@@ -0,0 +1,100 @@
1
+ # This is a configuration file for the Taskr RESTful scheduler daemon.
2
+
3
+ # AN IMPORTANT NOTE ABOUT YAML CONFIGURATION FILES:
4
+ # !!! Be sure to use spaces instead of tabs for indentation, as YAML is very
5
+ # !!! sensitive to white-space inconsistencies!
6
+
7
+ ##### HTTP SERVER #####################################################################
8
+
9
+ # Under what HTTP environment are you running the Fluxr server? The following methods
10
+ # are currently supported:
11
+ #
12
+ # webrick -- simple stand-alone HTTP server; this is the default method
13
+ # mongrel -- fast stand-alone HTTP server; much faster than webrick, but
14
+ # you'll have to first install the mongrel gem
15
+ #
16
+
17
+ ### webrick example
18
+
19
+ server: webrick
20
+ port: 7007
21
+
22
+ ### webrick SSL example
23
+
24
+ #server: webrick
25
+ #port: 443
26
+ #ssl_cert: /path/to/your/ssl.pem
27
+
28
+ # if the private key is separate from cert:
29
+ #ssl_key: /path/to/your/private_key.pem
30
+
31
+
32
+ ### mongrel example
33
+
34
+ #server: mongrel
35
+ #port: 7007
36
+
37
+ # It is possible to run mongrel over SSL, but you will need to use a reverse proxy
38
+ # (try Pound or Apache).
39
+
40
+
41
+ ##### DATABASE ########################################################################
42
+
43
+ # Taskr needs a database to persist its state between restarts.
44
+ #
45
+ # By default, we use MySQL, since it is widely used and does not require any additional
46
+ # ruby libraries besides ActiveRecord.
47
+ #
48
+ # With MySQL, your config would be something like the following:
49
+ # (be sure to create the taskr database in MySQL beforehand,
50
+ # i.e. `mysqladmin -u root create taskr`)
51
+
52
+ database:
53
+ adapter: mysql
54
+ database: taskr
55
+ username: root
56
+ password:
57
+ host: localhost
58
+
59
+
60
+ # Instead of MySQL you can use SQLite3, PostgreSQL, MSSQL, or anything else supported
61
+ # by ActiveRecord.
62
+ #
63
+ # If you do not have a database server available, you can try using the SQLite3
64
+ # back-end. SQLite3 does not require it's own server. Instead all data is stored
65
+ # in local files. For SQLite3, your configuration would look something like the
66
+ # following (don't forget to install the 'sqlite3-ruby' gem first!):
67
+ #
68
+ #database:
69
+ # adapter: sqlite3
70
+ # dbfile: /var/lib/taskr.db
71
+
72
+
73
+ ##### AUTHENTICATION ##################################################################
74
+
75
+ # Taskr supports basic HTTP authentication. Uncomment this if you want Taskr to demand
76
+ # a username and password from clients. Note that this isn't very secure unless
77
+ # you run Taskr over HTTPS (see the webrick SSL example above).
78
+
79
+ #authentication:
80
+ # username: taskr
81
+ # password: task!
82
+
83
+
84
+ ##### LOGGING #########################################################################
85
+
86
+ # This log is where you'll want to look in case of problems.
87
+ #
88
+ # By default, we will try to create a log file named 'taskr.log' in the current
89
+ # directory (the directory where you're running taskr from). A better place to put
90
+ # the log is in /var/log, but you will have to run taskr as root or otherwise give
91
+ # it permissions.
92
+ #
93
+ # Set the level to DEBUG if you want more detailed logging. Other options are
94
+ # INFO, WARN, and ERROR (DEBUG is most verbose, ERROR is least).
95
+
96
+ log:
97
+ file: taskr.log
98
+ level: DEBUG
99
+ # file: /var/log/taskr.log
100
+ # level: INFO
@@ -0,0 +1,36 @@
1
+ require 'active_resource'
2
+
3
+ # Define the proxy class
4
+ class Task < ActiveResource::Base
5
+ self.site = 'http://taskr.example.com'
6
+ end
7
+
8
+ # Retrieve a list of all the Tasks
9
+ tasks = Task.find(:all)
10
+
11
+ # Print each Task's details for debugging
12
+ tasks.each {|t| puts t.inspect}
13
+
14
+ # Retrieve a specific Task
15
+ task = Task.find(123)
16
+
17
+ # Print the Task's name
18
+ puts task.name
19
+
20
+ # Create a new Task to be executed every 10 seconds
21
+ task = Task.new
22
+ task.name = "My Example Task"
23
+ task.schedule_method = 'every'
24
+ task.schedule_when = '10s'
25
+ task.actions = [
26
+ {:action_class_name => 'Ruby', :code => "puts 'Hello World!'"},
27
+ {:action_class_name => 'Ruby', :code => "puts 'Goodbye!'"}
28
+ ]
29
+
30
+ # Save the new Task -- the Task is not scheduled for execution until
31
+ # it is saved.
32
+ task.save
33
+
34
+ # Deleting a task
35
+ id = 123
36
+ Task.delete(id)
@@ -0,0 +1,85 @@
1
+ <?php
2
+
3
+ /**
4
+ To use this exmple, you will need the Zend Framework from:
5
+
6
+ http://framework.zend.com/download
7
+
8
+ For more information on using the Zendr_Rest_Client, see:
9
+
10
+ - http://framework.zend.com/manual/en/zend.rest.client.html
11
+ - http://www.pixelated-dreams.com/archives/243-Next-Generation-REST-Web-Services-Client.html
12
+ **/
13
+
14
+ require 'Zend/Rest/Client.php';
15
+
16
+ $taskr_site_url = "http://localhost:7007";
17
+
18
+
19
+
20
+ /**
21
+ Retreiving the list of all scheduled Tasks
22
+ **/
23
+
24
+ $rest = new Zend_Rest_Client($taskr_site_url);
25
+ $tasks = $rest->get("/tasks.xml");
26
+
27
+ // $tasks is a SimpleXml object, so calling print_r($result) will let
28
+ // you see all of its data.
29
+ //
30
+ // Here's an example of how to print out all of the tasks as an HTML list:
31
+
32
+ echo "<ul>";
33
+ foreach ($tasks->task as $task) {
34
+ echo "<li>".$task->name."</li>";
35
+ echo "<ul>";
36
+ echo "<li>execute ".$task->{'schedule-method'}." ".$task->{'schedule-when'}."</li>";
37
+ echo "<li>created by ".$task->{'created-by'}." on ".$task->{'created-on'}."</li>";
38
+ }
39
+ echo "</ul>";
40
+
41
+
42
+
43
+ /**
44
+ Retreiving a specific task
45
+ **/
46
+
47
+ $id = 6;
48
+ $rest = new Zend_Rest_Client($taskr_site_url);
49
+ $task = $rest->get("/tasks/$id.xml");
50
+
51
+ // print the Task's name
52
+ echo $task->name;
53
+ // print the type of the first action in this task
54
+ echo $task->{'task-actions'}->{'task-action'}[0]->{'action-class-name'};
55
+
56
+
57
+ /**
58
+ Creating a new task, to be executed every 10 seconds
59
+ **/
60
+
61
+ $data = array(
62
+ 'name' => "My Example Task",
63
+ 'schedule_method' => "every",
64
+ 'schedule_when' => "10s",
65
+ 'actions' => array(
66
+ array('action_class_name' => "Ruby",
67
+ 'code' => 'puts "Hello World!"'),
68
+ array('action_class_name' => "Ruby",
69
+ 'code' => 'puts "Goodbye!"')
70
+ )
71
+ );
72
+
73
+ $rest = new Zend_Rest_Client($taskr_site_url);
74
+ $rest->post('/tasks.xml', $data);
75
+
76
+
77
+ /**
78
+ Deleting a task
79
+ **/
80
+
81
+ $id = 6;
82
+ $rest = new Zend_Rest_Client($taskr_site_url);
83
+ $task = $rest->delete("/tasks/$id.xml");
84
+
85
+ ?>
@@ -0,0 +1,3271 @@
1
+ /* Prototype JavaScript framework, version 1.5.1
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.5.1',
11
+
12
+ Browser: {
13
+ IE: !!(window.attachEvent && !window.opera),
14
+ Opera: !!window.opera,
15
+ WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
16
+ Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1
17
+ },
18
+
19
+ BrowserFeatures: {
20
+ XPath: !!document.evaluate,
21
+ ElementExtensions: !!window.HTMLElement,
22
+ SpecificElementExtensions:
23
+ (document.createElement('div').__proto__ !==
24
+ document.createElement('form').__proto__)
25
+ },
26
+
27
+ ScriptFragment: '<script[^>]*>([\u0001-\uFFFF]*?)</script>',
28
+ JSONFilter: /^\/\*-secure-\s*(.*)\s*\*\/\s*$/,
29
+
30
+ emptyFunction: function() { },
31
+ K: function(x) { return x }
32
+ }
33
+
34
+ var Class = {
35
+ create: function() {
36
+ return function() {
37
+ this.initialize.apply(this, arguments);
38
+ }
39
+ }
40
+ }
41
+
42
+ var Abstract = new Object();
43
+
44
+ Object.extend = function(destination, source) {
45
+ for (var property in source) {
46
+ destination[property] = source[property];
47
+ }
48
+ return destination;
49
+ }
50
+
51
+ Object.extend(Object, {
52
+ inspect: function(object) {
53
+ try {
54
+ if (object === undefined) return 'undefined';
55
+ if (object === null) return 'null';
56
+ return object.inspect ? object.inspect() : object.toString();
57
+ } catch (e) {
58
+ if (e instanceof RangeError) return '...';
59
+ throw e;
60
+ }
61
+ },
62
+
63
+ toJSON: function(object) {
64
+ var type = typeof object;
65
+ switch(type) {
66
+ case 'undefined':
67
+ case 'function':
68
+ case 'unknown': return;
69
+ case 'boolean': return object.toString();
70
+ }
71
+ if (object === null) return 'null';
72
+ if (object.toJSON) return object.toJSON();
73
+ if (object.ownerDocument === document) return;
74
+ var results = [];
75
+ for (var property in object) {
76
+ var value = Object.toJSON(object[property]);
77
+ if (value !== undefined)
78
+ results.push(property.toJSON() + ': ' + value);
79
+ }
80
+ return '{' + results.join(', ') + '}';
81
+ },
82
+
83
+ keys: function(object) {
84
+ var keys = [];
85
+ for (var property in object)
86
+ keys.push(property);
87
+ return keys;
88
+ },
89
+
90
+ values: function(object) {
91
+ var values = [];
92
+ for (var property in object)
93
+ values.push(object[property]);
94
+ return values;
95
+ },
96
+
97
+ clone: function(object) {
98
+ return Object.extend({}, object);
99
+ }
100
+ });
101
+
102
+ Function.prototype.bind = function() {
103
+ var __method = this, args = $A(arguments), object = args.shift();
104
+ return function() {
105
+ return __method.apply(object, args.concat($A(arguments)));
106
+ }
107
+ }
108
+
109
+ Function.prototype.bindAsEventListener = function(object) {
110
+ var __method = this, args = $A(arguments), object = args.shift();
111
+ return function(event) {
112
+ return __method.apply(object, [event || window.event].concat(args));
113
+ }
114
+ }
115
+
116
+ Object.extend(Number.prototype, {
117
+ toColorPart: function() {
118
+ return this.toPaddedString(2, 16);
119
+ },
120
+
121
+ succ: function() {
122
+ return this + 1;
123
+ },
124
+
125
+ times: function(iterator) {
126
+ $R(0, this, true).each(iterator);
127
+ return this;
128
+ },
129
+
130
+ toPaddedString: function(length, radix) {
131
+ var string = this.toString(radix || 10);
132
+ return '0'.times(length - string.length) + string;
133
+ },
134
+
135
+ toJSON: function() {
136
+ return isFinite(this) ? this.toString() : 'null';
137
+ }
138
+ });
139
+
140
+ Date.prototype.toJSON = function() {
141
+ return '"' + this.getFullYear() + '-' +
142
+ (this.getMonth() + 1).toPaddedString(2) + '-' +
143
+ this.getDate().toPaddedString(2) + 'T' +
144
+ this.getHours().toPaddedString(2) + ':' +
145
+ this.getMinutes().toPaddedString(2) + ':' +
146
+ this.getSeconds().toPaddedString(2) + '"';
147
+ };
148
+
149
+ var Try = {
150
+ these: function() {
151
+ var returnValue;
152
+
153
+ for (var i = 0, length = arguments.length; i < length; i++) {
154
+ var lambda = arguments[i];
155
+ try {
156
+ returnValue = lambda();
157
+ break;
158
+ } catch (e) {}
159
+ }
160
+
161
+ return returnValue;
162
+ }
163
+ }
164
+
165
+ /*--------------------------------------------------------------------------*/
166
+
167
+ var PeriodicalExecuter = Class.create();
168
+ PeriodicalExecuter.prototype = {
169
+ initialize: function(callback, frequency) {
170
+ this.callback = callback;
171
+ this.frequency = frequency;
172
+ this.currentlyExecuting = false;
173
+
174
+ this.registerCallback();
175
+ },
176
+
177
+ registerCallback: function() {
178
+ this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
179
+ },
180
+
181
+ stop: function() {
182
+ if (!this.timer) return;
183
+ clearInterval(this.timer);
184
+ this.timer = null;
185
+ },
186
+
187
+ onTimerEvent: function() {
188
+ if (!this.currentlyExecuting) {
189
+ try {
190
+ this.currentlyExecuting = true;
191
+ this.callback(this);
192
+ } finally {
193
+ this.currentlyExecuting = false;
194
+ }
195
+ }
196
+ }
197
+ }
198
+ Object.extend(String, {
199
+ interpret: function(value) {
200
+ return value == null ? '' : String(value);
201
+ },
202
+ specialChar: {
203
+ '\b': '\\b',
204
+ '\t': '\\t',
205
+ '\n': '\\n',
206
+ '\f': '\\f',
207
+ '\r': '\\r',
208
+ '\\': '\\\\'
209
+ }
210
+ });
211
+
212
+ Object.extend(String.prototype, {
213
+ gsub: function(pattern, replacement) {
214
+ var result = '', source = this, match;
215
+ replacement = arguments.callee.prepareReplacement(replacement);
216
+
217
+ while (source.length > 0) {
218
+ if (match = source.match(pattern)) {
219
+ result += source.slice(0, match.index);
220
+ result += String.interpret(replacement(match));
221
+ source = source.slice(match.index + match[0].length);
222
+ } else {
223
+ result += source, source = '';
224
+ }
225
+ }
226
+ return result;
227
+ },
228
+
229
+ sub: function(pattern, replacement, count) {
230
+ replacement = this.gsub.prepareReplacement(replacement);
231
+ count = count === undefined ? 1 : count;
232
+
233
+ return this.gsub(pattern, function(match) {
234
+ if (--count < 0) return match[0];
235
+ return replacement(match);
236
+ });
237
+ },
238
+
239
+ scan: function(pattern, iterator) {
240
+ this.gsub(pattern, iterator);
241
+ return this;
242
+ },
243
+
244
+ truncate: function(length, truncation) {
245
+ length = length || 30;
246
+ truncation = truncation === undefined ? '...' : truncation;
247
+ return this.length > length ?
248
+ this.slice(0, length - truncation.length) + truncation : this;
249
+ },
250
+
251
+ strip: function() {
252
+ return this.replace(/^\s+/, '').replace(/\s+$/, '');
253
+ },
254
+
255
+ stripTags: function() {
256
+ return this.replace(/<\/?[^>]+>/gi, '');
257
+ },
258
+
259
+ stripScripts: function() {
260
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
261
+ },
262
+
263
+ extractScripts: function() {
264
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
265
+ var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
266
+ return (this.match(matchAll) || []).map(function(scriptTag) {
267
+ return (scriptTag.match(matchOne) || ['', ''])[1];
268
+ });
269
+ },
270
+
271
+ evalScripts: function() {
272
+ return this.extractScripts().map(function(script) { return eval(script) });
273
+ },
274
+
275
+ escapeHTML: function() {
276
+ var self = arguments.callee;
277
+ self.text.data = this;
278
+ return self.div.innerHTML;
279
+ },
280
+
281
+ unescapeHTML: function() {
282
+ var div = document.createElement('div');
283
+ div.innerHTML = this.stripTags();
284
+ return div.childNodes[0] ? (div.childNodes.length > 1 ?
285
+ $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
286
+ div.childNodes[0].nodeValue) : '';
287
+ },
288
+
289
+ toQueryParams: function(separator) {
290
+ var match = this.strip().match(/([^?#]*)(#.*)?$/);
291
+ if (!match) return {};
292
+
293
+ return match[1].split(separator || '&').inject({}, function(hash, pair) {
294
+ if ((pair = pair.split('='))[0]) {
295
+ var key = decodeURIComponent(pair.shift());
296
+ var value = pair.length > 1 ? pair.join('=') : pair[0];
297
+ if (value != undefined) value = decodeURIComponent(value);
298
+
299
+ if (key in hash) {
300
+ if (hash[key].constructor != Array) hash[key] = [hash[key]];
301
+ hash[key].push(value);
302
+ }
303
+ else hash[key] = value;
304
+ }
305
+ return hash;
306
+ });
307
+ },
308
+
309
+ toArray: function() {
310
+ return this.split('');
311
+ },
312
+
313
+ succ: function() {
314
+ return this.slice(0, this.length - 1) +
315
+ String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
316
+ },
317
+
318
+ times: function(count) {
319
+ var result = '';
320
+ for (var i = 0; i < count; i++) result += this;
321
+ return result;
322
+ },
323
+
324
+ camelize: function() {
325
+ var parts = this.split('-'), len = parts.length;
326
+ if (len == 1) return parts[0];
327
+
328
+ var camelized = this.charAt(0) == '-'
329
+ ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
330
+ : parts[0];
331
+
332
+ for (var i = 1; i < len; i++)
333
+ camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
334
+
335
+ return camelized;
336
+ },
337
+
338
+ capitalize: function() {
339
+ return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
340
+ },
341
+
342
+ underscore: function() {
343
+ return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
344
+ },
345
+
346
+ dasherize: function() {
347
+ return this.gsub(/_/,'-');
348
+ },
349
+
350
+ inspect: function(useDoubleQuotes) {
351
+ var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
352
+ var character = String.specialChar[match[0]];
353
+ return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
354
+ });
355
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
356
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
357
+ },
358
+
359
+ toJSON: function() {
360
+ return this.inspect(true);
361
+ },
362
+
363
+ unfilterJSON: function(filter) {
364
+ return this.sub(filter || Prototype.JSONFilter, '#{1}');
365
+ },
366
+
367
+ evalJSON: function(sanitize) {
368
+ var json = this.unfilterJSON();
369
+ try {
370
+ if (!sanitize || (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(json)))
371
+ return eval('(' + json + ')');
372
+ } catch (e) { }
373
+ throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
374
+ },
375
+
376
+ include: function(pattern) {
377
+ return this.indexOf(pattern) > -1;
378
+ },
379
+
380
+ startsWith: function(pattern) {
381
+ return this.indexOf(pattern) === 0;
382
+ },
383
+
384
+ endsWith: function(pattern) {
385
+ var d = this.length - pattern.length;
386
+ return d >= 0 && this.lastIndexOf(pattern) === d;
387
+ },
388
+
389
+ empty: function() {
390
+ return this == '';
391
+ },
392
+
393
+ blank: function() {
394
+ return /^\s*$/.test(this);
395
+ }
396
+ });
397
+
398
+ if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
399
+ escapeHTML: function() {
400
+ return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
401
+ },
402
+ unescapeHTML: function() {
403
+ return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
404
+ }
405
+ });
406
+
407
+ String.prototype.gsub.prepareReplacement = function(replacement) {
408
+ if (typeof replacement == 'function') return replacement;
409
+ var template = new Template(replacement);
410
+ return function(match) { return template.evaluate(match) };
411
+ }
412
+
413
+ String.prototype.parseQuery = String.prototype.toQueryParams;
414
+
415
+ Object.extend(String.prototype.escapeHTML, {
416
+ div: document.createElement('div'),
417
+ text: document.createTextNode('')
418
+ });
419
+
420
+ with (String.prototype.escapeHTML) div.appendChild(text);
421
+
422
+ var Template = Class.create();
423
+ Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
424
+ Template.prototype = {
425
+ initialize: function(template, pattern) {
426
+ this.template = template.toString();
427
+ this.pattern = pattern || Template.Pattern;
428
+ },
429
+
430
+ evaluate: function(object) {
431
+ return this.template.gsub(this.pattern, function(match) {
432
+ var before = match[1];
433
+ if (before == '\\') return match[2];
434
+ return before + String.interpret(object[match[3]]);
435
+ });
436
+ }
437
+ }
438
+
439
+ var $break = {}, $continue = new Error('"throw $continue" is deprecated, use "return" instead');
440
+
441
+ var Enumerable = {
442
+ each: function(iterator) {
443
+ var index = 0;
444
+ try {
445
+ this._each(function(value) {
446
+ iterator(value, index++);
447
+ });
448
+ } catch (e) {
449
+ if (e != $break) throw e;
450
+ }
451
+ return this;
452
+ },
453
+
454
+ eachSlice: function(number, iterator) {
455
+ var index = -number, slices = [], array = this.toArray();
456
+ while ((index += number) < array.length)
457
+ slices.push(array.slice(index, index+number));
458
+ return slices.map(iterator);
459
+ },
460
+
461
+ all: function(iterator) {
462
+ var result = true;
463
+ this.each(function(value, index) {
464
+ result = result && !!(iterator || Prototype.K)(value, index);
465
+ if (!result) throw $break;
466
+ });
467
+ return result;
468
+ },
469
+
470
+ any: function(iterator) {
471
+ var result = false;
472
+ this.each(function(value, index) {
473
+ if (result = !!(iterator || Prototype.K)(value, index))
474
+ throw $break;
475
+ });
476
+ return result;
477
+ },
478
+
479
+ collect: function(iterator) {
480
+ var results = [];
481
+ this.each(function(value, index) {
482
+ results.push((iterator || Prototype.K)(value, index));
483
+ });
484
+ return results;
485
+ },
486
+
487
+ detect: function(iterator) {
488
+ var result;
489
+ this.each(function(value, index) {
490
+ if (iterator(value, index)) {
491
+ result = value;
492
+ throw $break;
493
+ }
494
+ });
495
+ return result;
496
+ },
497
+
498
+ findAll: function(iterator) {
499
+ var results = [];
500
+ this.each(function(value, index) {
501
+ if (iterator(value, index))
502
+ results.push(value);
503
+ });
504
+ return results;
505
+ },
506
+
507
+ grep: function(pattern, iterator) {
508
+ var results = [];
509
+ this.each(function(value, index) {
510
+ var stringValue = value.toString();
511
+ if (stringValue.match(pattern))
512
+ results.push((iterator || Prototype.K)(value, index));
513
+ })
514
+ return results;
515
+ },
516
+
517
+ include: function(object) {
518
+ var found = false;
519
+ this.each(function(value) {
520
+ if (value == object) {
521
+ found = true;
522
+ throw $break;
523
+ }
524
+ });
525
+ return found;
526
+ },
527
+
528
+ inGroupsOf: function(number, fillWith) {
529
+ fillWith = fillWith === undefined ? null : fillWith;
530
+ return this.eachSlice(number, function(slice) {
531
+ while(slice.length < number) slice.push(fillWith);
532
+ return slice;
533
+ });
534
+ },
535
+
536
+ inject: function(memo, iterator) {
537
+ this.each(function(value, index) {
538
+ memo = iterator(memo, value, index);
539
+ });
540
+ return memo;
541
+ },
542
+
543
+ invoke: function(method) {
544
+ var args = $A(arguments).slice(1);
545
+ return this.map(function(value) {
546
+ return value[method].apply(value, args);
547
+ });
548
+ },
549
+
550
+ max: function(iterator) {
551
+ var result;
552
+ this.each(function(value, index) {
553
+ value = (iterator || Prototype.K)(value, index);
554
+ if (result == undefined || value >= result)
555
+ result = value;
556
+ });
557
+ return result;
558
+ },
559
+
560
+ min: function(iterator) {
561
+ var result;
562
+ this.each(function(value, index) {
563
+ value = (iterator || Prototype.K)(value, index);
564
+ if (result == undefined || value < result)
565
+ result = value;
566
+ });
567
+ return result;
568
+ },
569
+
570
+ partition: function(iterator) {
571
+ var trues = [], falses = [];
572
+ this.each(function(value, index) {
573
+ ((iterator || Prototype.K)(value, index) ?
574
+ trues : falses).push(value);
575
+ });
576
+ return [trues, falses];
577
+ },
578
+
579
+ pluck: function(property) {
580
+ var results = [];
581
+ this.each(function(value, index) {
582
+ results.push(value[property]);
583
+ });
584
+ return results;
585
+ },
586
+
587
+ reject: function(iterator) {
588
+ var results = [];
589
+ this.each(function(value, index) {
590
+ if (!iterator(value, index))
591
+ results.push(value);
592
+ });
593
+ return results;
594
+ },
595
+
596
+ sortBy: function(iterator) {
597
+ return this.map(function(value, index) {
598
+ return {value: value, criteria: iterator(value, index)};
599
+ }).sort(function(left, right) {
600
+ var a = left.criteria, b = right.criteria;
601
+ return a < b ? -1 : a > b ? 1 : 0;
602
+ }).pluck('value');
603
+ },
604
+
605
+ toArray: function() {
606
+ return this.map();
607
+ },
608
+
609
+ zip: function() {
610
+ var iterator = Prototype.K, args = $A(arguments);
611
+ if (typeof args.last() == 'function')
612
+ iterator = args.pop();
613
+
614
+ var collections = [this].concat(args).map($A);
615
+ return this.map(function(value, index) {
616
+ return iterator(collections.pluck(index));
617
+ });
618
+ },
619
+
620
+ size: function() {
621
+ return this.toArray().length;
622
+ },
623
+
624
+ inspect: function() {
625
+ return '#<Enumerable:' + this.toArray().inspect() + '>';
626
+ }
627
+ }
628
+
629
+ Object.extend(Enumerable, {
630
+ map: Enumerable.collect,
631
+ find: Enumerable.detect,
632
+ select: Enumerable.findAll,
633
+ member: Enumerable.include,
634
+ entries: Enumerable.toArray
635
+ });
636
+ var $A = Array.from = function(iterable) {
637
+ if (!iterable) return [];
638
+ if (iterable.toArray) {
639
+ return iterable.toArray();
640
+ } else {
641
+ var results = [];
642
+ for (var i = 0, length = iterable.length; i < length; i++)
643
+ results.push(iterable[i]);
644
+ return results;
645
+ }
646
+ }
647
+
648
+ if (Prototype.Browser.WebKit) {
649
+ $A = Array.from = function(iterable) {
650
+ if (!iterable) return [];
651
+ if (!(typeof iterable == 'function' && iterable == '[object NodeList]') &&
652
+ iterable.toArray) {
653
+ return iterable.toArray();
654
+ } else {
655
+ var results = [];
656
+ for (var i = 0, length = iterable.length; i < length; i++)
657
+ results.push(iterable[i]);
658
+ return results;
659
+ }
660
+ }
661
+ }
662
+
663
+ Object.extend(Array.prototype, Enumerable);
664
+
665
+ if (!Array.prototype._reverse)
666
+ Array.prototype._reverse = Array.prototype.reverse;
667
+
668
+ Object.extend(Array.prototype, {
669
+ _each: function(iterator) {
670
+ for (var i = 0, length = this.length; i < length; i++)
671
+ iterator(this[i]);
672
+ },
673
+
674
+ clear: function() {
675
+ this.length = 0;
676
+ return this;
677
+ },
678
+
679
+ first: function() {
680
+ return this[0];
681
+ },
682
+
683
+ last: function() {
684
+ return this[this.length - 1];
685
+ },
686
+
687
+ compact: function() {
688
+ return this.select(function(value) {
689
+ return value != null;
690
+ });
691
+ },
692
+
693
+ flatten: function() {
694
+ return this.inject([], function(array, value) {
695
+ return array.concat(value && value.constructor == Array ?
696
+ value.flatten() : [value]);
697
+ });
698
+ },
699
+
700
+ without: function() {
701
+ var values = $A(arguments);
702
+ return this.select(function(value) {
703
+ return !values.include(value);
704
+ });
705
+ },
706
+
707
+ indexOf: function(object) {
708
+ for (var i = 0, length = this.length; i < length; i++)
709
+ if (this[i] == object) return i;
710
+ return -1;
711
+ },
712
+
713
+ reverse: function(inline) {
714
+ return (inline !== false ? this : this.toArray())._reverse();
715
+ },
716
+
717
+ reduce: function() {
718
+ return this.length > 1 ? this : this[0];
719
+ },
720
+
721
+ uniq: function(sorted) {
722
+ return this.inject([], function(array, value, index) {
723
+ if (0 == index || (sorted ? array.last() != value : !array.include(value)))
724
+ array.push(value);
725
+ return array;
726
+ });
727
+ },
728
+
729
+ clone: function() {
730
+ return [].concat(this);
731
+ },
732
+
733
+ size: function() {
734
+ return this.length;
735
+ },
736
+
737
+ inspect: function() {
738
+ return '[' + this.map(Object.inspect).join(', ') + ']';
739
+ },
740
+
741
+ toJSON: function() {
742
+ var results = [];
743
+ this.each(function(object) {
744
+ var value = Object.toJSON(object);
745
+ if (value !== undefined) results.push(value);
746
+ });
747
+ return '[' + results.join(', ') + ']';
748
+ }
749
+ });
750
+
751
+ Array.prototype.toArray = Array.prototype.clone;
752
+
753
+ function $w(string) {
754
+ string = string.strip();
755
+ return string ? string.split(/\s+/) : [];
756
+ }
757
+
758
+ if (Prototype.Browser.Opera){
759
+ Array.prototype.concat = function() {
760
+ var array = [];
761
+ for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
762
+ for (var i = 0, length = arguments.length; i < length; i++) {
763
+ if (arguments[i].constructor == Array) {
764
+ for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
765
+ array.push(arguments[i][j]);
766
+ } else {
767
+ array.push(arguments[i]);
768
+ }
769
+ }
770
+ return array;
771
+ }
772
+ }
773
+ var Hash = function(object) {
774
+ if (object instanceof Hash) this.merge(object);
775
+ else Object.extend(this, object || {});
776
+ };
777
+
778
+ Object.extend(Hash, {
779
+ toQueryString: function(obj) {
780
+ var parts = [];
781
+ parts.add = arguments.callee.addPair;
782
+
783
+ this.prototype._each.call(obj, function(pair) {
784
+ if (!pair.key) return;
785
+ var value = pair.value;
786
+
787
+ if (value && typeof value == 'object') {
788
+ if (value.constructor == Array) value.each(function(value) {
789
+ parts.add(pair.key, value);
790
+ });
791
+ return;
792
+ }
793
+ parts.add(pair.key, value);
794
+ });
795
+
796
+ return parts.join('&');
797
+ },
798
+
799
+ toJSON: function(object) {
800
+ var results = [];
801
+ this.prototype._each.call(object, function(pair) {
802
+ var value = Object.toJSON(pair.value);
803
+ if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value);
804
+ });
805
+ return '{' + results.join(', ') + '}';
806
+ }
807
+ });
808
+
809
+ Hash.toQueryString.addPair = function(key, value, prefix) {
810
+ key = encodeURIComponent(key);
811
+ if (value === undefined) this.push(key);
812
+ else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value)));
813
+ }
814
+
815
+ Object.extend(Hash.prototype, Enumerable);
816
+ Object.extend(Hash.prototype, {
817
+ _each: function(iterator) {
818
+ for (var key in this) {
819
+ var value = this[key];
820
+ if (value && value == Hash.prototype[key]) continue;
821
+
822
+ var pair = [key, value];
823
+ pair.key = key;
824
+ pair.value = value;
825
+ iterator(pair);
826
+ }
827
+ },
828
+
829
+ keys: function() {
830
+ return this.pluck('key');
831
+ },
832
+
833
+ values: function() {
834
+ return this.pluck('value');
835
+ },
836
+
837
+ merge: function(hash) {
838
+ return $H(hash).inject(this, function(mergedHash, pair) {
839
+ mergedHash[pair.key] = pair.value;
840
+ return mergedHash;
841
+ });
842
+ },
843
+
844
+ remove: function() {
845
+ var result;
846
+ for(var i = 0, length = arguments.length; i < length; i++) {
847
+ var value = this[arguments[i]];
848
+ if (value !== undefined){
849
+ if (result === undefined) result = value;
850
+ else {
851
+ if (result.constructor != Array) result = [result];
852
+ result.push(value)
853
+ }
854
+ }
855
+ delete this[arguments[i]];
856
+ }
857
+ return result;
858
+ },
859
+
860
+ toQueryString: function() {
861
+ return Hash.toQueryString(this);
862
+ },
863
+
864
+ inspect: function() {
865
+ return '#<Hash:{' + this.map(function(pair) {
866
+ return pair.map(Object.inspect).join(': ');
867
+ }).join(', ') + '}>';
868
+ },
869
+
870
+ toJSON: function() {
871
+ return Hash.toJSON(this);
872
+ }
873
+ });
874
+
875
+ function $H(object) {
876
+ if (object instanceof Hash) return object;
877
+ return new Hash(object);
878
+ };
879
+
880
+ // Safari iterates over shadowed properties
881
+ if (function() {
882
+ var i = 0, Test = function(value) { this.key = value };
883
+ Test.prototype.key = 'foo';
884
+ for (var property in new Test('bar')) i++;
885
+ return i > 1;
886
+ }()) Hash.prototype._each = function(iterator) {
887
+ var cache = [];
888
+ for (var key in this) {
889
+ var value = this[key];
890
+ if ((value && value == Hash.prototype[key]) || cache.include(key)) continue;
891
+ cache.push(key);
892
+ var pair = [key, value];
893
+ pair.key = key;
894
+ pair.value = value;
895
+ iterator(pair);
896
+ }
897
+ };
898
+ ObjectRange = Class.create();
899
+ Object.extend(ObjectRange.prototype, Enumerable);
900
+ Object.extend(ObjectRange.prototype, {
901
+ initialize: function(start, end, exclusive) {
902
+ this.start = start;
903
+ this.end = end;
904
+ this.exclusive = exclusive;
905
+ },
906
+
907
+ _each: function(iterator) {
908
+ var value = this.start;
909
+ while (this.include(value)) {
910
+ iterator(value);
911
+ value = value.succ();
912
+ }
913
+ },
914
+
915
+ include: function(value) {
916
+ if (value < this.start)
917
+ return false;
918
+ if (this.exclusive)
919
+ return value < this.end;
920
+ return value <= this.end;
921
+ }
922
+ });
923
+
924
+ var $R = function(start, end, exclusive) {
925
+ return new ObjectRange(start, end, exclusive);
926
+ }
927
+
928
+ var Ajax = {
929
+ getTransport: function() {
930
+ return Try.these(
931
+ function() {return new XMLHttpRequest()},
932
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
933
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')}
934
+ ) || false;
935
+ },
936
+
937
+ activeRequestCount: 0
938
+ }
939
+
940
+ Ajax.Responders = {
941
+ responders: [],
942
+
943
+ _each: function(iterator) {
944
+ this.responders._each(iterator);
945
+ },
946
+
947
+ register: function(responder) {
948
+ if (!this.include(responder))
949
+ this.responders.push(responder);
950
+ },
951
+
952
+ unregister: function(responder) {
953
+ this.responders = this.responders.without(responder);
954
+ },
955
+
956
+ dispatch: function(callback, request, transport, json) {
957
+ this.each(function(responder) {
958
+ if (typeof responder[callback] == 'function') {
959
+ try {
960
+ responder[callback].apply(responder, [request, transport, json]);
961
+ } catch (e) {}
962
+ }
963
+ });
964
+ }
965
+ };
966
+
967
+ Object.extend(Ajax.Responders, Enumerable);
968
+
969
+ Ajax.Responders.register({
970
+ onCreate: function() {
971
+ Ajax.activeRequestCount++;
972
+ },
973
+ onComplete: function() {
974
+ Ajax.activeRequestCount--;
975
+ }
976
+ });
977
+
978
+ Ajax.Base = function() {};
979
+ Ajax.Base.prototype = {
980
+ setOptions: function(options) {
981
+ this.options = {
982
+ method: 'post',
983
+ asynchronous: true,
984
+ contentType: 'application/x-www-form-urlencoded',
985
+ encoding: 'UTF-8',
986
+ parameters: ''
987
+ }
988
+ Object.extend(this.options, options || {});
989
+
990
+ this.options.method = this.options.method.toLowerCase();
991
+ if (typeof this.options.parameters == 'string')
992
+ this.options.parameters = this.options.parameters.toQueryParams();
993
+ }
994
+ }
995
+
996
+ Ajax.Request = Class.create();
997
+ Ajax.Request.Events =
998
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
999
+
1000
+ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
1001
+ _complete: false,
1002
+
1003
+ initialize: function(url, options) {
1004
+ this.transport = Ajax.getTransport();
1005
+ this.setOptions(options);
1006
+ this.request(url);
1007
+ },
1008
+
1009
+ request: function(url) {
1010
+ this.url = url;
1011
+ this.method = this.options.method;
1012
+ var params = Object.clone(this.options.parameters);
1013
+
1014
+ if (!['get', 'post'].include(this.method)) {
1015
+ // simulate other verbs over post
1016
+ params['_method'] = this.method;
1017
+ this.method = 'post';
1018
+ }
1019
+
1020
+ this.parameters = params;
1021
+
1022
+ if (params = Hash.toQueryString(params)) {
1023
+ // when GET, append parameters to URL
1024
+ if (this.method == 'get')
1025
+ this.url += (this.url.include('?') ? '&' : '?') + params;
1026
+ else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1027
+ params += '&_=';
1028
+ }
1029
+
1030
+ try {
1031
+ if (this.options.onCreate) this.options.onCreate(this.transport);
1032
+ Ajax.Responders.dispatch('onCreate', this, this.transport);
1033
+
1034
+ this.transport.open(this.method.toUpperCase(), this.url,
1035
+ this.options.asynchronous);
1036
+
1037
+ if (this.options.asynchronous)
1038
+ setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
1039
+
1040
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
1041
+ this.setRequestHeaders();
1042
+
1043
+ this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1044
+ this.transport.send(this.body);
1045
+
1046
+ /* Force Firefox to handle ready state 4 for synchronous requests */
1047
+ if (!this.options.asynchronous && this.transport.overrideMimeType)
1048
+ this.onStateChange();
1049
+
1050
+ }
1051
+ catch (e) {
1052
+ this.dispatchException(e);
1053
+ }
1054
+ },
1055
+
1056
+ onStateChange: function() {
1057
+ var readyState = this.transport.readyState;
1058
+ if (readyState > 1 && !((readyState == 4) && this._complete))
1059
+ this.respondToReadyState(this.transport.readyState);
1060
+ },
1061
+
1062
+ setRequestHeaders: function() {
1063
+ var headers = {
1064
+ 'X-Requested-With': 'XMLHttpRequest',
1065
+ 'X-Prototype-Version': Prototype.Version,
1066
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1067
+ };
1068
+
1069
+ if (this.method == 'post') {
1070
+ headers['Content-type'] = this.options.contentType +
1071
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
1072
+
1073
+ /* Force "Connection: close" for older Mozilla browsers to work
1074
+ * around a bug where XMLHttpRequest sends an incorrect
1075
+ * Content-length header. See Mozilla Bugzilla #246651.
1076
+ */
1077
+ if (this.transport.overrideMimeType &&
1078
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1079
+ headers['Connection'] = 'close';
1080
+ }
1081
+
1082
+ // user-defined headers
1083
+ if (typeof this.options.requestHeaders == 'object') {
1084
+ var extras = this.options.requestHeaders;
1085
+
1086
+ if (typeof extras.push == 'function')
1087
+ for (var i = 0, length = extras.length; i < length; i += 2)
1088
+ headers[extras[i]] = extras[i+1];
1089
+ else
1090
+ $H(extras).each(function(pair) { headers[pair.key] = pair.value });
1091
+ }
1092
+
1093
+ for (var name in headers)
1094
+ this.transport.setRequestHeader(name, headers[name]);
1095
+ },
1096
+
1097
+ success: function() {
1098
+ return !this.transport.status
1099
+ || (this.transport.status >= 200 && this.transport.status < 300);
1100
+ },
1101
+
1102
+ respondToReadyState: function(readyState) {
1103
+ var state = Ajax.Request.Events[readyState];
1104
+ var transport = this.transport, json = this.evalJSON();
1105
+
1106
+ if (state == 'Complete') {
1107
+ try {
1108
+ this._complete = true;
1109
+ (this.options['on' + this.transport.status]
1110
+ || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1111
+ || Prototype.emptyFunction)(transport, json);
1112
+ } catch (e) {
1113
+ this.dispatchException(e);
1114
+ }
1115
+
1116
+ var contentType = this.getHeader('Content-type');
1117
+ if (contentType && contentType.strip().
1118
+ match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
1119
+ this.evalResponse();
1120
+ }
1121
+
1122
+ try {
1123
+ (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
1124
+ Ajax.Responders.dispatch('on' + state, this, transport, json);
1125
+ } catch (e) {
1126
+ this.dispatchException(e);
1127
+ }
1128
+
1129
+ if (state == 'Complete') {
1130
+ // avoid memory leak in MSIE: clean up
1131
+ this.transport.onreadystatechange = Prototype.emptyFunction;
1132
+ }
1133
+ },
1134
+
1135
+ getHeader: function(name) {
1136
+ try {
1137
+ return this.transport.getResponseHeader(name);
1138
+ } catch (e) { return null }
1139
+ },
1140
+
1141
+ evalJSON: function() {
1142
+ try {
1143
+ var json = this.getHeader('X-JSON');
1144
+ return json ? json.evalJSON() : null;
1145
+ } catch (e) { return null }
1146
+ },
1147
+
1148
+ evalResponse: function() {
1149
+ try {
1150
+ return eval((this.transport.responseText || '').unfilterJSON());
1151
+ } catch (e) {
1152
+ this.dispatchException(e);
1153
+ }
1154
+ },
1155
+
1156
+ dispatchException: function(exception) {
1157
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
1158
+ Ajax.Responders.dispatch('onException', this, exception);
1159
+ }
1160
+ });
1161
+
1162
+ Ajax.Updater = Class.create();
1163
+
1164
+ Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
1165
+ initialize: function(container, url, options) {
1166
+ this.container = {
1167
+ success: (container.success || container),
1168
+ failure: (container.failure || (container.success ? null : container))
1169
+ }
1170
+
1171
+ this.transport = Ajax.getTransport();
1172
+ this.setOptions(options);
1173
+
1174
+ var onComplete = this.options.onComplete || Prototype.emptyFunction;
1175
+ this.options.onComplete = (function(transport, param) {
1176
+ this.updateContent();
1177
+ onComplete(transport, param);
1178
+ }).bind(this);
1179
+
1180
+ this.request(url);
1181
+ },
1182
+
1183
+ updateContent: function() {
1184
+ var receiver = this.container[this.success() ? 'success' : 'failure'];
1185
+ var response = this.transport.responseText;
1186
+
1187
+ if (!this.options.evalScripts) response = response.stripScripts();
1188
+
1189
+ if (receiver = $(receiver)) {
1190
+ if (this.options.insertion)
1191
+ new this.options.insertion(receiver, response);
1192
+ else
1193
+ receiver.update(response);
1194
+ }
1195
+
1196
+ if (this.success()) {
1197
+ if (this.onComplete)
1198
+ setTimeout(this.onComplete.bind(this), 10);
1199
+ }
1200
+ }
1201
+ });
1202
+
1203
+ Ajax.PeriodicalUpdater = Class.create();
1204
+ Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
1205
+ initialize: function(container, url, options) {
1206
+ this.setOptions(options);
1207
+ this.onComplete = this.options.onComplete;
1208
+
1209
+ this.frequency = (this.options.frequency || 2);
1210
+ this.decay = (this.options.decay || 1);
1211
+
1212
+ this.updater = {};
1213
+ this.container = container;
1214
+ this.url = url;
1215
+
1216
+ this.start();
1217
+ },
1218
+
1219
+ start: function() {
1220
+ this.options.onComplete = this.updateComplete.bind(this);
1221
+ this.onTimerEvent();
1222
+ },
1223
+
1224
+ stop: function() {
1225
+ this.updater.options.onComplete = undefined;
1226
+ clearTimeout(this.timer);
1227
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1228
+ },
1229
+
1230
+ updateComplete: function(request) {
1231
+ if (this.options.decay) {
1232
+ this.decay = (request.responseText == this.lastText ?
1233
+ this.decay * this.options.decay : 1);
1234
+
1235
+ this.lastText = request.responseText;
1236
+ }
1237
+ this.timer = setTimeout(this.onTimerEvent.bind(this),
1238
+ this.decay * this.frequency * 1000);
1239
+ },
1240
+
1241
+ onTimerEvent: function() {
1242
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
1243
+ }
1244
+ });
1245
+ function $(element) {
1246
+ if (arguments.length > 1) {
1247
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1248
+ elements.push($(arguments[i]));
1249
+ return elements;
1250
+ }
1251
+ if (typeof element == 'string')
1252
+ element = document.getElementById(element);
1253
+ return Element.extend(element);
1254
+ }
1255
+
1256
+ if (Prototype.BrowserFeatures.XPath) {
1257
+ document._getElementsByXPath = function(expression, parentElement) {
1258
+ var results = [];
1259
+ var query = document.evaluate(expression, $(parentElement) || document,
1260
+ null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1261
+ for (var i = 0, length = query.snapshotLength; i < length; i++)
1262
+ results.push(query.snapshotItem(i));
1263
+ return results;
1264
+ };
1265
+
1266
+ document.getElementsByClassName = function(className, parentElement) {
1267
+ var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
1268
+ return document._getElementsByXPath(q, parentElement);
1269
+ }
1270
+
1271
+ } else document.getElementsByClassName = function(className, parentElement) {
1272
+ var children = ($(parentElement) || document.body).getElementsByTagName('*');
1273
+ var elements = [], child;
1274
+ for (var i = 0, length = children.length; i < length; i++) {
1275
+ child = children[i];
1276
+ if (Element.hasClassName(child, className))
1277
+ elements.push(Element.extend(child));
1278
+ }
1279
+ return elements;
1280
+ };
1281
+
1282
+ /*--------------------------------------------------------------------------*/
1283
+
1284
+ if (!window.Element) var Element = {};
1285
+
1286
+ Element.extend = function(element) {
1287
+ var F = Prototype.BrowserFeatures;
1288
+ if (!element || !element.tagName || element.nodeType == 3 ||
1289
+ element._extended || F.SpecificElementExtensions || element == window)
1290
+ return element;
1291
+
1292
+ var methods = {}, tagName = element.tagName, cache = Element.extend.cache,
1293
+ T = Element.Methods.ByTag;
1294
+
1295
+ // extend methods for all tags (Safari doesn't need this)
1296
+ if (!F.ElementExtensions) {
1297
+ Object.extend(methods, Element.Methods),
1298
+ Object.extend(methods, Element.Methods.Simulated);
1299
+ }
1300
+
1301
+ // extend methods for specific tags
1302
+ if (T[tagName]) Object.extend(methods, T[tagName]);
1303
+
1304
+ for (var property in methods) {
1305
+ var value = methods[property];
1306
+ if (typeof value == 'function' && !(property in element))
1307
+ element[property] = cache.findOrStore(value);
1308
+ }
1309
+
1310
+ element._extended = Prototype.emptyFunction;
1311
+ return element;
1312
+ };
1313
+
1314
+ Element.extend.cache = {
1315
+ findOrStore: function(value) {
1316
+ return this[value] = this[value] || function() {
1317
+ return value.apply(null, [this].concat($A(arguments)));
1318
+ }
1319
+ }
1320
+ };
1321
+
1322
+ Element.Methods = {
1323
+ visible: function(element) {
1324
+ return $(element).style.display != 'none';
1325
+ },
1326
+
1327
+ toggle: function(element) {
1328
+ element = $(element);
1329
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
1330
+ return element;
1331
+ },
1332
+
1333
+ hide: function(element) {
1334
+ $(element).style.display = 'none';
1335
+ return element;
1336
+ },
1337
+
1338
+ show: function(element) {
1339
+ $(element).style.display = '';
1340
+ return element;
1341
+ },
1342
+
1343
+ remove: function(element) {
1344
+ element = $(element);
1345
+ element.parentNode.removeChild(element);
1346
+ return element;
1347
+ },
1348
+
1349
+ update: function(element, html) {
1350
+ html = typeof html == 'undefined' ? '' : html.toString();
1351
+ $(element).innerHTML = html.stripScripts();
1352
+ setTimeout(function() {html.evalScripts()}, 10);
1353
+ return element;
1354
+ },
1355
+
1356
+ replace: function(element, html) {
1357
+ element = $(element);
1358
+ html = typeof html == 'undefined' ? '' : html.toString();
1359
+ if (element.outerHTML) {
1360
+ element.outerHTML = html.stripScripts();
1361
+ } else {
1362
+ var range = element.ownerDocument.createRange();
1363
+ range.selectNodeContents(element);
1364
+ element.parentNode.replaceChild(
1365
+ range.createContextualFragment(html.stripScripts()), element);
1366
+ }
1367
+ setTimeout(function() {html.evalScripts()}, 10);
1368
+ return element;
1369
+ },
1370
+
1371
+ inspect: function(element) {
1372
+ element = $(element);
1373
+ var result = '<' + element.tagName.toLowerCase();
1374
+ $H({'id': 'id', 'className': 'class'}).each(function(pair) {
1375
+ var property = pair.first(), attribute = pair.last();
1376
+ var value = (element[property] || '').toString();
1377
+ if (value) result += ' ' + attribute + '=' + value.inspect(true);
1378
+ });
1379
+ return result + '>';
1380
+ },
1381
+
1382
+ recursivelyCollect: function(element, property) {
1383
+ element = $(element);
1384
+ var elements = [];
1385
+ while (element = element[property])
1386
+ if (element.nodeType == 1)
1387
+ elements.push(Element.extend(element));
1388
+ return elements;
1389
+ },
1390
+
1391
+ ancestors: function(element) {
1392
+ return $(element).recursivelyCollect('parentNode');
1393
+ },
1394
+
1395
+ descendants: function(element) {
1396
+ return $A($(element).getElementsByTagName('*')).each(Element.extend);
1397
+ },
1398
+
1399
+ firstDescendant: function(element) {
1400
+ element = $(element).firstChild;
1401
+ while (element && element.nodeType != 1) element = element.nextSibling;
1402
+ return $(element);
1403
+ },
1404
+
1405
+ immediateDescendants: function(element) {
1406
+ if (!(element = $(element).firstChild)) return [];
1407
+ while (element && element.nodeType != 1) element = element.nextSibling;
1408
+ if (element) return [element].concat($(element).nextSiblings());
1409
+ return [];
1410
+ },
1411
+
1412
+ previousSiblings: function(element) {
1413
+ return $(element).recursivelyCollect('previousSibling');
1414
+ },
1415
+
1416
+ nextSiblings: function(element) {
1417
+ return $(element).recursivelyCollect('nextSibling');
1418
+ },
1419
+
1420
+ siblings: function(element) {
1421
+ element = $(element);
1422
+ return element.previousSiblings().reverse().concat(element.nextSiblings());
1423
+ },
1424
+
1425
+ match: function(element, selector) {
1426
+ if (typeof selector == 'string')
1427
+ selector = new Selector(selector);
1428
+ return selector.match($(element));
1429
+ },
1430
+
1431
+ up: function(element, expression, index) {
1432
+ element = $(element);
1433
+ if (arguments.length == 1) return $(element.parentNode);
1434
+ var ancestors = element.ancestors();
1435
+ return expression ? Selector.findElement(ancestors, expression, index) :
1436
+ ancestors[index || 0];
1437
+ },
1438
+
1439
+ down: function(element, expression, index) {
1440
+ element = $(element);
1441
+ if (arguments.length == 1) return element.firstDescendant();
1442
+ var descendants = element.descendants();
1443
+ return expression ? Selector.findElement(descendants, expression, index) :
1444
+ descendants[index || 0];
1445
+ },
1446
+
1447
+ previous: function(element, expression, index) {
1448
+ element = $(element);
1449
+ if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
1450
+ var previousSiblings = element.previousSiblings();
1451
+ return expression ? Selector.findElement(previousSiblings, expression, index) :
1452
+ previousSiblings[index || 0];
1453
+ },
1454
+
1455
+ next: function(element, expression, index) {
1456
+ element = $(element);
1457
+ if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
1458
+ var nextSiblings = element.nextSiblings();
1459
+ return expression ? Selector.findElement(nextSiblings, expression, index) :
1460
+ nextSiblings[index || 0];
1461
+ },
1462
+
1463
+ getElementsBySelector: function() {
1464
+ var args = $A(arguments), element = $(args.shift());
1465
+ return Selector.findChildElements(element, args);
1466
+ },
1467
+
1468
+ getElementsByClassName: function(element, className) {
1469
+ return document.getElementsByClassName(className, element);
1470
+ },
1471
+
1472
+ readAttribute: function(element, name) {
1473
+ element = $(element);
1474
+ if (Prototype.Browser.IE) {
1475
+ if (!element.attributes) return null;
1476
+ var t = Element._attributeTranslations;
1477
+ if (t.values[name]) return t.values[name](element, name);
1478
+ if (t.names[name]) name = t.names[name];
1479
+ var attribute = element.attributes[name];
1480
+ return attribute ? attribute.nodeValue : null;
1481
+ }
1482
+ return element.getAttribute(name);
1483
+ },
1484
+
1485
+ getHeight: function(element) {
1486
+ return $(element).getDimensions().height;
1487
+ },
1488
+
1489
+ getWidth: function(element) {
1490
+ return $(element).getDimensions().width;
1491
+ },
1492
+
1493
+ classNames: function(element) {
1494
+ return new Element.ClassNames(element);
1495
+ },
1496
+
1497
+ hasClassName: function(element, className) {
1498
+ if (!(element = $(element))) return;
1499
+ var elementClassName = element.className;
1500
+ if (elementClassName.length == 0) return false;
1501
+ if (elementClassName == className ||
1502
+ elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
1503
+ return true;
1504
+ return false;
1505
+ },
1506
+
1507
+ addClassName: function(element, className) {
1508
+ if (!(element = $(element))) return;
1509
+ Element.classNames(element).add(className);
1510
+ return element;
1511
+ },
1512
+
1513
+ removeClassName: function(element, className) {
1514
+ if (!(element = $(element))) return;
1515
+ Element.classNames(element).remove(className);
1516
+ return element;
1517
+ },
1518
+
1519
+ toggleClassName: function(element, className) {
1520
+ if (!(element = $(element))) return;
1521
+ Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
1522
+ return element;
1523
+ },
1524
+
1525
+ observe: function() {
1526
+ Event.observe.apply(Event, arguments);
1527
+ return $A(arguments).first();
1528
+ },
1529
+
1530
+ stopObserving: function() {
1531
+ Event.stopObserving.apply(Event, arguments);
1532
+ return $A(arguments).first();
1533
+ },
1534
+
1535
+ // removes whitespace-only text node children
1536
+ cleanWhitespace: function(element) {
1537
+ element = $(element);
1538
+ var node = element.firstChild;
1539
+ while (node) {
1540
+ var nextNode = node.nextSibling;
1541
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
1542
+ element.removeChild(node);
1543
+ node = nextNode;
1544
+ }
1545
+ return element;
1546
+ },
1547
+
1548
+ empty: function(element) {
1549
+ return $(element).innerHTML.blank();
1550
+ },
1551
+
1552
+ descendantOf: function(element, ancestor) {
1553
+ element = $(element), ancestor = $(ancestor);
1554
+ while (element = element.parentNode)
1555
+ if (element == ancestor) return true;
1556
+ return false;
1557
+ },
1558
+
1559
+ scrollTo: function(element) {
1560
+ element = $(element);
1561
+ var pos = Position.cumulativeOffset(element);
1562
+ window.scrollTo(pos[0], pos[1]);
1563
+ return element;
1564
+ },
1565
+
1566
+ getStyle: function(element, style) {
1567
+ element = $(element);
1568
+ style = style == 'float' ? 'cssFloat' : style.camelize();
1569
+ var value = element.style[style];
1570
+ if (!value) {
1571
+ var css = document.defaultView.getComputedStyle(element, null);
1572
+ value = css ? css[style] : null;
1573
+ }
1574
+ if (style == 'opacity') return value ? parseFloat(value) : 1.0;
1575
+ return value == 'auto' ? null : value;
1576
+ },
1577
+
1578
+ getOpacity: function(element) {
1579
+ return $(element).getStyle('opacity');
1580
+ },
1581
+
1582
+ setStyle: function(element, styles, camelized) {
1583
+ element = $(element);
1584
+ var elementStyle = element.style;
1585
+
1586
+ for (var property in styles)
1587
+ if (property == 'opacity') element.setOpacity(styles[property])
1588
+ else
1589
+ elementStyle[(property == 'float' || property == 'cssFloat') ?
1590
+ (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
1591
+ (camelized ? property : property.camelize())] = styles[property];
1592
+
1593
+ return element;
1594
+ },
1595
+
1596
+ setOpacity: function(element, value) {
1597
+ element = $(element);
1598
+ element.style.opacity = (value == 1 || value === '') ? '' :
1599
+ (value < 0.00001) ? 0 : value;
1600
+ return element;
1601
+ },
1602
+
1603
+ getDimensions: function(element) {
1604
+ element = $(element);
1605
+ var display = $(element).getStyle('display');
1606
+ if (display != 'none' && display != null) // Safari bug
1607
+ return {width: element.offsetWidth, height: element.offsetHeight};
1608
+
1609
+ // All *Width and *Height properties give 0 on elements with display none,
1610
+ // so enable the element temporarily
1611
+ var els = element.style;
1612
+ var originalVisibility = els.visibility;
1613
+ var originalPosition = els.position;
1614
+ var originalDisplay = els.display;
1615
+ els.visibility = 'hidden';
1616
+ els.position = 'absolute';
1617
+ els.display = 'block';
1618
+ var originalWidth = element.clientWidth;
1619
+ var originalHeight = element.clientHeight;
1620
+ els.display = originalDisplay;
1621
+ els.position = originalPosition;
1622
+ els.visibility = originalVisibility;
1623
+ return {width: originalWidth, height: originalHeight};
1624
+ },
1625
+
1626
+ makePositioned: function(element) {
1627
+ element = $(element);
1628
+ var pos = Element.getStyle(element, 'position');
1629
+ if (pos == 'static' || !pos) {
1630
+ element._madePositioned = true;
1631
+ element.style.position = 'relative';
1632
+ // Opera returns the offset relative to the positioning context, when an
1633
+ // element is position relative but top and left have not been defined
1634
+ if (window.opera) {
1635
+ element.style.top = 0;
1636
+ element.style.left = 0;
1637
+ }
1638
+ }
1639
+ return element;
1640
+ },
1641
+
1642
+ undoPositioned: function(element) {
1643
+ element = $(element);
1644
+ if (element._madePositioned) {
1645
+ element._madePositioned = undefined;
1646
+ element.style.position =
1647
+ element.style.top =
1648
+ element.style.left =
1649
+ element.style.bottom =
1650
+ element.style.right = '';
1651
+ }
1652
+ return element;
1653
+ },
1654
+
1655
+ makeClipping: function(element) {
1656
+ element = $(element);
1657
+ if (element._overflow) return element;
1658
+ element._overflow = element.style.overflow || 'auto';
1659
+ if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
1660
+ element.style.overflow = 'hidden';
1661
+ return element;
1662
+ },
1663
+
1664
+ undoClipping: function(element) {
1665
+ element = $(element);
1666
+ if (!element._overflow) return element;
1667
+ element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
1668
+ element._overflow = null;
1669
+ return element;
1670
+ }
1671
+ };
1672
+
1673
+ Object.extend(Element.Methods, {
1674
+ childOf: Element.Methods.descendantOf,
1675
+ childElements: Element.Methods.immediateDescendants
1676
+ });
1677
+
1678
+ if (Prototype.Browser.Opera) {
1679
+ Element.Methods._getStyle = Element.Methods.getStyle;
1680
+ Element.Methods.getStyle = function(element, style) {
1681
+ switch(style) {
1682
+ case 'left':
1683
+ case 'top':
1684
+ case 'right':
1685
+ case 'bottom':
1686
+ if (Element._getStyle(element, 'position') == 'static') return null;
1687
+ default: return Element._getStyle(element, style);
1688
+ }
1689
+ };
1690
+ }
1691
+ else if (Prototype.Browser.IE) {
1692
+ Element.Methods.getStyle = function(element, style) {
1693
+ element = $(element);
1694
+ style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
1695
+ var value = element.style[style];
1696
+ if (!value && element.currentStyle) value = element.currentStyle[style];
1697
+
1698
+ if (style == 'opacity') {
1699
+ if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
1700
+ if (value[1]) return parseFloat(value[1]) / 100;
1701
+ return 1.0;
1702
+ }
1703
+
1704
+ if (value == 'auto') {
1705
+ if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
1706
+ return element['offset'+style.capitalize()] + 'px';
1707
+ return null;
1708
+ }
1709
+ return value;
1710
+ };
1711
+
1712
+ Element.Methods.setOpacity = function(element, value) {
1713
+ element = $(element);
1714
+ var filter = element.getStyle('filter'), style = element.style;
1715
+ if (value == 1 || value === '') {
1716
+ style.filter = filter.replace(/alpha\([^\)]*\)/gi,'');
1717
+ return element;
1718
+ } else if (value < 0.00001) value = 0;
1719
+ style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') +
1720
+ 'alpha(opacity=' + (value * 100) + ')';
1721
+ return element;
1722
+ };
1723
+
1724
+ // IE is missing .innerHTML support for TABLE-related elements
1725
+ Element.Methods.update = function(element, html) {
1726
+ element = $(element);
1727
+ html = typeof html == 'undefined' ? '' : html.toString();
1728
+ var tagName = element.tagName.toUpperCase();
1729
+ if (['THEAD','TBODY','TR','TD'].include(tagName)) {
1730
+ var div = document.createElement('div');
1731
+ switch (tagName) {
1732
+ case 'THEAD':
1733
+ case 'TBODY':
1734
+ div.innerHTML = '<table><tbody>' + html.stripScripts() + '</tbody></table>';
1735
+ depth = 2;
1736
+ break;
1737
+ case 'TR':
1738
+ div.innerHTML = '<table><tbody><tr>' + html.stripScripts() + '</tr></tbody></table>';
1739
+ depth = 3;
1740
+ break;
1741
+ case 'TD':
1742
+ div.innerHTML = '<table><tbody><tr><td>' + html.stripScripts() + '</td></tr></tbody></table>';
1743
+ depth = 4;
1744
+ }
1745
+ $A(element.childNodes).each(function(node) { element.removeChild(node) });
1746
+ depth.times(function() { div = div.firstChild });
1747
+ $A(div.childNodes).each(function(node) { element.appendChild(node) });
1748
+ } else {
1749
+ element.innerHTML = html.stripScripts();
1750
+ }
1751
+ setTimeout(function() { html.evalScripts() }, 10);
1752
+ return element;
1753
+ }
1754
+ }
1755
+ else if (Prototype.Browser.Gecko) {
1756
+ Element.Methods.setOpacity = function(element, value) {
1757
+ element = $(element);
1758
+ element.style.opacity = (value == 1) ? 0.999999 :
1759
+ (value === '') ? '' : (value < 0.00001) ? 0 : value;
1760
+ return element;
1761
+ };
1762
+ }
1763
+
1764
+ Element._attributeTranslations = {
1765
+ names: {
1766
+ colspan: "colSpan",
1767
+ rowspan: "rowSpan",
1768
+ valign: "vAlign",
1769
+ datetime: "dateTime",
1770
+ accesskey: "accessKey",
1771
+ tabindex: "tabIndex",
1772
+ enctype: "encType",
1773
+ maxlength: "maxLength",
1774
+ readonly: "readOnly",
1775
+ longdesc: "longDesc"
1776
+ },
1777
+ values: {
1778
+ _getAttr: function(element, attribute) {
1779
+ return element.getAttribute(attribute, 2);
1780
+ },
1781
+ _flag: function(element, attribute) {
1782
+ return $(element).hasAttribute(attribute) ? attribute : null;
1783
+ },
1784
+ style: function(element) {
1785
+ return element.style.cssText.toLowerCase();
1786
+ },
1787
+ title: function(element) {
1788
+ var node = element.getAttributeNode('title');
1789
+ return node.specified ? node.nodeValue : null;
1790
+ }
1791
+ }
1792
+ };
1793
+
1794
+ (function() {
1795
+ Object.extend(this, {
1796
+ href: this._getAttr,
1797
+ src: this._getAttr,
1798
+ type: this._getAttr,
1799
+ disabled: this._flag,
1800
+ checked: this._flag,
1801
+ readonly: this._flag,
1802
+ multiple: this._flag
1803
+ });
1804
+ }).call(Element._attributeTranslations.values);
1805
+
1806
+ Element.Methods.Simulated = {
1807
+ hasAttribute: function(element, attribute) {
1808
+ var t = Element._attributeTranslations, node;
1809
+ attribute = t.names[attribute] || attribute;
1810
+ node = $(element).getAttributeNode(attribute);
1811
+ return node && node.specified;
1812
+ }
1813
+ };
1814
+
1815
+ Element.Methods.ByTag = {};
1816
+
1817
+ Object.extend(Element, Element.Methods);
1818
+
1819
+ if (!Prototype.BrowserFeatures.ElementExtensions &&
1820
+ document.createElement('div').__proto__) {
1821
+ window.HTMLElement = {};
1822
+ window.HTMLElement.prototype = document.createElement('div').__proto__;
1823
+ Prototype.BrowserFeatures.ElementExtensions = true;
1824
+ }
1825
+
1826
+ Element.hasAttribute = function(element, attribute) {
1827
+ if (element.hasAttribute) return element.hasAttribute(attribute);
1828
+ return Element.Methods.Simulated.hasAttribute(element, attribute);
1829
+ };
1830
+
1831
+ Element.addMethods = function(methods) {
1832
+ var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
1833
+
1834
+ if (!methods) {
1835
+ Object.extend(Form, Form.Methods);
1836
+ Object.extend(Form.Element, Form.Element.Methods);
1837
+ Object.extend(Element.Methods.ByTag, {
1838
+ "FORM": Object.clone(Form.Methods),
1839
+ "INPUT": Object.clone(Form.Element.Methods),
1840
+ "SELECT": Object.clone(Form.Element.Methods),
1841
+ "TEXTAREA": Object.clone(Form.Element.Methods)
1842
+ });
1843
+ }
1844
+
1845
+ if (arguments.length == 2) {
1846
+ var tagName = methods;
1847
+ methods = arguments[1];
1848
+ }
1849
+
1850
+ if (!tagName) Object.extend(Element.Methods, methods || {});
1851
+ else {
1852
+ if (tagName.constructor == Array) tagName.each(extend);
1853
+ else extend(tagName);
1854
+ }
1855
+
1856
+ function extend(tagName) {
1857
+ tagName = tagName.toUpperCase();
1858
+ if (!Element.Methods.ByTag[tagName])
1859
+ Element.Methods.ByTag[tagName] = {};
1860
+ Object.extend(Element.Methods.ByTag[tagName], methods);
1861
+ }
1862
+
1863
+ function copy(methods, destination, onlyIfAbsent) {
1864
+ onlyIfAbsent = onlyIfAbsent || false;
1865
+ var cache = Element.extend.cache;
1866
+ for (var property in methods) {
1867
+ var value = methods[property];
1868
+ if (!onlyIfAbsent || !(property in destination))
1869
+ destination[property] = cache.findOrStore(value);
1870
+ }
1871
+ }
1872
+
1873
+ function findDOMClass(tagName) {
1874
+ var klass;
1875
+ var trans = {
1876
+ "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
1877
+ "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
1878
+ "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
1879
+ "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
1880
+ "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
1881
+ "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
1882
+ "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
1883
+ "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
1884
+ "FrameSet", "IFRAME": "IFrame"
1885
+ };
1886
+ if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
1887
+ if (window[klass]) return window[klass];
1888
+ klass = 'HTML' + tagName + 'Element';
1889
+ if (window[klass]) return window[klass];
1890
+ klass = 'HTML' + tagName.capitalize() + 'Element';
1891
+ if (window[klass]) return window[klass];
1892
+
1893
+ window[klass] = {};
1894
+ window[klass].prototype = document.createElement(tagName).__proto__;
1895
+ return window[klass];
1896
+ }
1897
+
1898
+ if (F.ElementExtensions) {
1899
+ copy(Element.Methods, HTMLElement.prototype);
1900
+ copy(Element.Methods.Simulated, HTMLElement.prototype, true);
1901
+ }
1902
+
1903
+ if (F.SpecificElementExtensions) {
1904
+ for (var tag in Element.Methods.ByTag) {
1905
+ var klass = findDOMClass(tag);
1906
+ if (typeof klass == "undefined") continue;
1907
+ copy(T[tag], klass.prototype);
1908
+ }
1909
+ }
1910
+
1911
+ Object.extend(Element, Element.Methods);
1912
+ delete Element.ByTag;
1913
+ };
1914
+
1915
+ var Toggle = { display: Element.toggle };
1916
+
1917
+ /*--------------------------------------------------------------------------*/
1918
+
1919
+ Abstract.Insertion = function(adjacency) {
1920
+ this.adjacency = adjacency;
1921
+ }
1922
+
1923
+ Abstract.Insertion.prototype = {
1924
+ initialize: function(element, content) {
1925
+ this.element = $(element);
1926
+ this.content = content.stripScripts();
1927
+
1928
+ if (this.adjacency && this.element.insertAdjacentHTML) {
1929
+ try {
1930
+ this.element.insertAdjacentHTML(this.adjacency, this.content);
1931
+ } catch (e) {
1932
+ var tagName = this.element.tagName.toUpperCase();
1933
+ if (['TBODY', 'TR'].include(tagName)) {
1934
+ this.insertContent(this.contentFromAnonymousTable());
1935
+ } else {
1936
+ throw e;
1937
+ }
1938
+ }
1939
+ } else {
1940
+ this.range = this.element.ownerDocument.createRange();
1941
+ if (this.initializeRange) this.initializeRange();
1942
+ this.insertContent([this.range.createContextualFragment(this.content)]);
1943
+ }
1944
+
1945
+ setTimeout(function() {content.evalScripts()}, 10);
1946
+ },
1947
+
1948
+ contentFromAnonymousTable: function() {
1949
+ var div = document.createElement('div');
1950
+ div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
1951
+ return $A(div.childNodes[0].childNodes[0].childNodes);
1952
+ }
1953
+ }
1954
+
1955
+ var Insertion = new Object();
1956
+
1957
+ Insertion.Before = Class.create();
1958
+ Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
1959
+ initializeRange: function() {
1960
+ this.range.setStartBefore(this.element);
1961
+ },
1962
+
1963
+ insertContent: function(fragments) {
1964
+ fragments.each((function(fragment) {
1965
+ this.element.parentNode.insertBefore(fragment, this.element);
1966
+ }).bind(this));
1967
+ }
1968
+ });
1969
+
1970
+ Insertion.Top = Class.create();
1971
+ Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
1972
+ initializeRange: function() {
1973
+ this.range.selectNodeContents(this.element);
1974
+ this.range.collapse(true);
1975
+ },
1976
+
1977
+ insertContent: function(fragments) {
1978
+ fragments.reverse(false).each((function(fragment) {
1979
+ this.element.insertBefore(fragment, this.element.firstChild);
1980
+ }).bind(this));
1981
+ }
1982
+ });
1983
+
1984
+ Insertion.Bottom = Class.create();
1985
+ Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
1986
+ initializeRange: function() {
1987
+ this.range.selectNodeContents(this.element);
1988
+ this.range.collapse(this.element);
1989
+ },
1990
+
1991
+ insertContent: function(fragments) {
1992
+ fragments.each((function(fragment) {
1993
+ this.element.appendChild(fragment);
1994
+ }).bind(this));
1995
+ }
1996
+ });
1997
+
1998
+ Insertion.After = Class.create();
1999
+ Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
2000
+ initializeRange: function() {
2001
+ this.range.setStartAfter(this.element);
2002
+ },
2003
+
2004
+ insertContent: function(fragments) {
2005
+ fragments.each((function(fragment) {
2006
+ this.element.parentNode.insertBefore(fragment,
2007
+ this.element.nextSibling);
2008
+ }).bind(this));
2009
+ }
2010
+ });
2011
+
2012
+ /*--------------------------------------------------------------------------*/
2013
+
2014
+ Element.ClassNames = Class.create();
2015
+ Element.ClassNames.prototype = {
2016
+ initialize: function(element) {
2017
+ this.element = $(element);
2018
+ },
2019
+
2020
+ _each: function(iterator) {
2021
+ this.element.className.split(/\s+/).select(function(name) {
2022
+ return name.length > 0;
2023
+ })._each(iterator);
2024
+ },
2025
+
2026
+ set: function(className) {
2027
+ this.element.className = className;
2028
+ },
2029
+
2030
+ add: function(classNameToAdd) {
2031
+ if (this.include(classNameToAdd)) return;
2032
+ this.set($A(this).concat(classNameToAdd).join(' '));
2033
+ },
2034
+
2035
+ remove: function(classNameToRemove) {
2036
+ if (!this.include(classNameToRemove)) return;
2037
+ this.set($A(this).without(classNameToRemove).join(' '));
2038
+ },
2039
+
2040
+ toString: function() {
2041
+ return $A(this).join(' ');
2042
+ }
2043
+ };
2044
+
2045
+ Object.extend(Element.ClassNames.prototype, Enumerable);
2046
+ /* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
2047
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
2048
+ * license. Please see http://www.yui-ext.com/ for more information. */
2049
+
2050
+ var Selector = Class.create();
2051
+
2052
+ Selector.prototype = {
2053
+ initialize: function(expression) {
2054
+ this.expression = expression.strip();
2055
+ this.compileMatcher();
2056
+ },
2057
+
2058
+ compileMatcher: function() {
2059
+ // Selectors with namespaced attributes can't use the XPath version
2060
+ if (Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression))
2061
+ return this.compileXPathMatcher();
2062
+
2063
+ var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
2064
+ c = Selector.criteria, le, p, m;
2065
+
2066
+ if (Selector._cache[e]) {
2067
+ this.matcher = Selector._cache[e]; return;
2068
+ }
2069
+ this.matcher = ["this.matcher = function(root) {",
2070
+ "var r = root, h = Selector.handlers, c = false, n;"];
2071
+
2072
+ while (e && le != e && (/\S/).test(e)) {
2073
+ le = e;
2074
+ for (var i in ps) {
2075
+ p = ps[i];
2076
+ if (m = e.match(p)) {
2077
+ this.matcher.push(typeof c[i] == 'function' ? c[i](m) :
2078
+ new Template(c[i]).evaluate(m));
2079
+ e = e.replace(m[0], '');
2080
+ break;
2081
+ }
2082
+ }
2083
+ }
2084
+
2085
+ this.matcher.push("return h.unique(n);\n}");
2086
+ eval(this.matcher.join('\n'));
2087
+ Selector._cache[this.expression] = this.matcher;
2088
+ },
2089
+
2090
+ compileXPathMatcher: function() {
2091
+ var e = this.expression, ps = Selector.patterns,
2092
+ x = Selector.xpath, le, m;
2093
+
2094
+ if (Selector._cache[e]) {
2095
+ this.xpath = Selector._cache[e]; return;
2096
+ }
2097
+
2098
+ this.matcher = ['.//*'];
2099
+ while (e && le != e && (/\S/).test(e)) {
2100
+ le = e;
2101
+ for (var i in ps) {
2102
+ if (m = e.match(ps[i])) {
2103
+ this.matcher.push(typeof x[i] == 'function' ? x[i](m) :
2104
+ new Template(x[i]).evaluate(m));
2105
+ e = e.replace(m[0], '');
2106
+ break;
2107
+ }
2108
+ }
2109
+ }
2110
+
2111
+ this.xpath = this.matcher.join('');
2112
+ Selector._cache[this.expression] = this.xpath;
2113
+ },
2114
+
2115
+ findElements: function(root) {
2116
+ root = root || document;
2117
+ if (this.xpath) return document._getElementsByXPath(this.xpath, root);
2118
+ return this.matcher(root);
2119
+ },
2120
+
2121
+ match: function(element) {
2122
+ return this.findElements(document).include(element);
2123
+ },
2124
+
2125
+ toString: function() {
2126
+ return this.expression;
2127
+ },
2128
+
2129
+ inspect: function() {
2130
+ return "#<Selector:" + this.expression.inspect() + ">";
2131
+ }
2132
+ };
2133
+
2134
+ Object.extend(Selector, {
2135
+ _cache: {},
2136
+
2137
+ xpath: {
2138
+ descendant: "//*",
2139
+ child: "/*",
2140
+ adjacent: "/following-sibling::*[1]",
2141
+ laterSibling: '/following-sibling::*',
2142
+ tagName: function(m) {
2143
+ if (m[1] == '*') return '';
2144
+ return "[local-name()='" + m[1].toLowerCase() +
2145
+ "' or local-name()='" + m[1].toUpperCase() + "']";
2146
+ },
2147
+ className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
2148
+ id: "[@id='#{1}']",
2149
+ attrPresence: "[@#{1}]",
2150
+ attr: function(m) {
2151
+ m[3] = m[5] || m[6];
2152
+ return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
2153
+ },
2154
+ pseudo: function(m) {
2155
+ var h = Selector.xpath.pseudos[m[1]];
2156
+ if (!h) return '';
2157
+ if (typeof h === 'function') return h(m);
2158
+ return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
2159
+ },
2160
+ operators: {
2161
+ '=': "[@#{1}='#{3}']",
2162
+ '!=': "[@#{1}!='#{3}']",
2163
+ '^=': "[starts-with(@#{1}, '#{3}')]",
2164
+ '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
2165
+ '*=': "[contains(@#{1}, '#{3}')]",
2166
+ '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
2167
+ '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
2168
+ },
2169
+ pseudos: {
2170
+ 'first-child': '[not(preceding-sibling::*)]',
2171
+ 'last-child': '[not(following-sibling::*)]',
2172
+ 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
2173
+ 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
2174
+ 'checked': "[@checked]",
2175
+ 'disabled': "[@disabled]",
2176
+ 'enabled': "[not(@disabled)]",
2177
+ 'not': function(m) {
2178
+ var e = m[6], p = Selector.patterns,
2179
+ x = Selector.xpath, le, m, v;
2180
+
2181
+ var exclusion = [];
2182
+ while (e && le != e && (/\S/).test(e)) {
2183
+ le = e;
2184
+ for (var i in p) {
2185
+ if (m = e.match(p[i])) {
2186
+ v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m);
2187
+ exclusion.push("(" + v.substring(1, v.length - 1) + ")");
2188
+ e = e.replace(m[0], '');
2189
+ break;
2190
+ }
2191
+ }
2192
+ }
2193
+ return "[not(" + exclusion.join(" and ") + ")]";
2194
+ },
2195
+ 'nth-child': function(m) {
2196
+ return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
2197
+ },
2198
+ 'nth-last-child': function(m) {
2199
+ return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
2200
+ },
2201
+ 'nth-of-type': function(m) {
2202
+ return Selector.xpath.pseudos.nth("position() ", m);
2203
+ },
2204
+ 'nth-last-of-type': function(m) {
2205
+ return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
2206
+ },
2207
+ 'first-of-type': function(m) {
2208
+ m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
2209
+ },
2210
+ 'last-of-type': function(m) {
2211
+ m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
2212
+ },
2213
+ 'only-of-type': function(m) {
2214
+ var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
2215
+ },
2216
+ nth: function(fragment, m) {
2217
+ var mm, formula = m[6], predicate;
2218
+ if (formula == 'even') formula = '2n+0';
2219
+ if (formula == 'odd') formula = '2n+1';
2220
+ if (mm = formula.match(/^(\d+)$/)) // digit only
2221
+ return '[' + fragment + "= " + mm[1] + ']';
2222
+ if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
2223
+ if (mm[1] == "-") mm[1] = -1;
2224
+ var a = mm[1] ? Number(mm[1]) : 1;
2225
+ var b = mm[2] ? Number(mm[2]) : 0;
2226
+ predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
2227
+ "((#{fragment} - #{b}) div #{a} >= 0)]";
2228
+ return new Template(predicate).evaluate({
2229
+ fragment: fragment, a: a, b: b });
2230
+ }
2231
+ }
2232
+ }
2233
+ },
2234
+
2235
+ criteria: {
2236
+ tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
2237
+ className: 'n = h.className(n, r, "#{1}", c); c = false;',
2238
+ id: 'n = h.id(n, r, "#{1}", c); c = false;',
2239
+ attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
2240
+ attr: function(m) {
2241
+ m[3] = (m[5] || m[6]);
2242
+ return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
2243
+ },
2244
+ pseudo: function(m) {
2245
+ if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
2246
+ return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
2247
+ },
2248
+ descendant: 'c = "descendant";',
2249
+ child: 'c = "child";',
2250
+ adjacent: 'c = "adjacent";',
2251
+ laterSibling: 'c = "laterSibling";'
2252
+ },
2253
+
2254
+ patterns: {
2255
+ // combinators must be listed first
2256
+ // (and descendant needs to be last combinator)
2257
+ laterSibling: /^\s*~\s*/,
2258
+ child: /^\s*>\s*/,
2259
+ adjacent: /^\s*\+\s*/,
2260
+ descendant: /^\s/,
2261
+
2262
+ // selectors follow
2263
+ tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
2264
+ id: /^#([\w\-\*]+)(\b|$)/,
2265
+ className: /^\.([\w\-\*]+)(\b|$)/,
2266
+ pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|\s|(?=:))/,
2267
+ attrPresence: /^\[([\w]+)\]/,
2268
+ attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/
2269
+ },
2270
+
2271
+ handlers: {
2272
+ // UTILITY FUNCTIONS
2273
+ // joins two collections
2274
+ concat: function(a, b) {
2275
+ for (var i = 0, node; node = b[i]; i++)
2276
+ a.push(node);
2277
+ return a;
2278
+ },
2279
+
2280
+ // marks an array of nodes for counting
2281
+ mark: function(nodes) {
2282
+ for (var i = 0, node; node = nodes[i]; i++)
2283
+ node._counted = true;
2284
+ return nodes;
2285
+ },
2286
+
2287
+ unmark: function(nodes) {
2288
+ for (var i = 0, node; node = nodes[i]; i++)
2289
+ node._counted = undefined;
2290
+ return nodes;
2291
+ },
2292
+
2293
+ // mark each child node with its position (for nth calls)
2294
+ // "ofType" flag indicates whether we're indexing for nth-of-type
2295
+ // rather than nth-child
2296
+ index: function(parentNode, reverse, ofType) {
2297
+ parentNode._counted = true;
2298
+ if (reverse) {
2299
+ for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
2300
+ node = nodes[i];
2301
+ if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
2302
+ }
2303
+ } else {
2304
+ for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
2305
+ if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
2306
+ }
2307
+ },
2308
+
2309
+ // filters out duplicates and extends all nodes
2310
+ unique: function(nodes) {
2311
+ if (nodes.length == 0) return nodes;
2312
+ var results = [], n;
2313
+ for (var i = 0, l = nodes.length; i < l; i++)
2314
+ if (!(n = nodes[i])._counted) {
2315
+ n._counted = true;
2316
+ results.push(Element.extend(n));
2317
+ }
2318
+ return Selector.handlers.unmark(results);
2319
+ },
2320
+
2321
+ // COMBINATOR FUNCTIONS
2322
+ descendant: function(nodes) {
2323
+ var h = Selector.handlers;
2324
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
2325
+ h.concat(results, node.getElementsByTagName('*'));
2326
+ return results;
2327
+ },
2328
+
2329
+ child: function(nodes) {
2330
+ var h = Selector.handlers;
2331
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
2332
+ for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
2333
+ if (child.nodeType == 1 && child.tagName != '!') results.push(child);
2334
+ }
2335
+ return results;
2336
+ },
2337
+
2338
+ adjacent: function(nodes) {
2339
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
2340
+ var next = this.nextElementSibling(node);
2341
+ if (next) results.push(next);
2342
+ }
2343
+ return results;
2344
+ },
2345
+
2346
+ laterSibling: function(nodes) {
2347
+ var h = Selector.handlers;
2348
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
2349
+ h.concat(results, Element.nextSiblings(node));
2350
+ return results;
2351
+ },
2352
+
2353
+ nextElementSibling: function(node) {
2354
+ while (node = node.nextSibling)
2355
+ if (node.nodeType == 1) return node;
2356
+ return null;
2357
+ },
2358
+
2359
+ previousElementSibling: function(node) {
2360
+ while (node = node.previousSibling)
2361
+ if (node.nodeType == 1) return node;
2362
+ return null;
2363
+ },
2364
+
2365
+ // TOKEN FUNCTIONS
2366
+ tagName: function(nodes, root, tagName, combinator) {
2367
+ tagName = tagName.toUpperCase();
2368
+ var results = [], h = Selector.handlers;
2369
+ if (nodes) {
2370
+ if (combinator) {
2371
+ // fastlane for ordinary descendant combinators
2372
+ if (combinator == "descendant") {
2373
+ for (var i = 0, node; node = nodes[i]; i++)
2374
+ h.concat(results, node.getElementsByTagName(tagName));
2375
+ return results;
2376
+ } else nodes = this[combinator](nodes);
2377
+ if (tagName == "*") return nodes;
2378
+ }
2379
+ for (var i = 0, node; node = nodes[i]; i++)
2380
+ if (node.tagName.toUpperCase() == tagName) results.push(node);
2381
+ return results;
2382
+ } else return root.getElementsByTagName(tagName);
2383
+ },
2384
+
2385
+ id: function(nodes, root, id, combinator) {
2386
+ var targetNode = $(id), h = Selector.handlers;
2387
+ if (!nodes && root == document) return targetNode ? [targetNode] : [];
2388
+ if (nodes) {
2389
+ if (combinator) {
2390
+ if (combinator == 'child') {
2391
+ for (var i = 0, node; node = nodes[i]; i++)
2392
+ if (targetNode.parentNode == node) return [targetNode];
2393
+ } else if (combinator == 'descendant') {
2394
+ for (var i = 0, node; node = nodes[i]; i++)
2395
+ if (Element.descendantOf(targetNode, node)) return [targetNode];
2396
+ } else if (combinator == 'adjacent') {
2397
+ for (var i = 0, node; node = nodes[i]; i++)
2398
+ if (Selector.handlers.previousElementSibling(targetNode) == node)
2399
+ return [targetNode];
2400
+ } else nodes = h[combinator](nodes);
2401
+ }
2402
+ for (var i = 0, node; node = nodes[i]; i++)
2403
+ if (node == targetNode) return [targetNode];
2404
+ return [];
2405
+ }
2406
+ return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
2407
+ },
2408
+
2409
+ className: function(nodes, root, className, combinator) {
2410
+ if (nodes && combinator) nodes = this[combinator](nodes);
2411
+ return Selector.handlers.byClassName(nodes, root, className);
2412
+ },
2413
+
2414
+ byClassName: function(nodes, root, className) {
2415
+ if (!nodes) nodes = Selector.handlers.descendant([root]);
2416
+ var needle = ' ' + className + ' ';
2417
+ for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
2418
+ nodeClassName = node.className;
2419
+ if (nodeClassName.length == 0) continue;
2420
+ if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
2421
+ results.push(node);
2422
+ }
2423
+ return results;
2424
+ },
2425
+
2426
+ attrPresence: function(nodes, root, attr) {
2427
+ var results = [];
2428
+ for (var i = 0, node; node = nodes[i]; i++)
2429
+ if (Element.hasAttribute(node, attr)) results.push(node);
2430
+ return results;
2431
+ },
2432
+
2433
+ attr: function(nodes, root, attr, value, operator) {
2434
+ if (!nodes) nodes = root.getElementsByTagName("*");
2435
+ var handler = Selector.operators[operator], results = [];
2436
+ for (var i = 0, node; node = nodes[i]; i++) {
2437
+ var nodeValue = Element.readAttribute(node, attr);
2438
+ if (nodeValue === null) continue;
2439
+ if (handler(nodeValue, value)) results.push(node);
2440
+ }
2441
+ return results;
2442
+ },
2443
+
2444
+ pseudo: function(nodes, name, value, root, combinator) {
2445
+ if (nodes && combinator) nodes = this[combinator](nodes);
2446
+ if (!nodes) nodes = root.getElementsByTagName("*");
2447
+ return Selector.pseudos[name](nodes, value, root);
2448
+ }
2449
+ },
2450
+
2451
+ pseudos: {
2452
+ 'first-child': function(nodes, value, root) {
2453
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
2454
+ if (Selector.handlers.previousElementSibling(node)) continue;
2455
+ results.push(node);
2456
+ }
2457
+ return results;
2458
+ },
2459
+ 'last-child': function(nodes, value, root) {
2460
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
2461
+ if (Selector.handlers.nextElementSibling(node)) continue;
2462
+ results.push(node);
2463
+ }
2464
+ return results;
2465
+ },
2466
+ 'only-child': function(nodes, value, root) {
2467
+ var h = Selector.handlers;
2468
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
2469
+ if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
2470
+ results.push(node);
2471
+ return results;
2472
+ },
2473
+ 'nth-child': function(nodes, formula, root) {
2474
+ return Selector.pseudos.nth(nodes, formula, root);
2475
+ },
2476
+ 'nth-last-child': function(nodes, formula, root) {
2477
+ return Selector.pseudos.nth(nodes, formula, root, true);
2478
+ },
2479
+ 'nth-of-type': function(nodes, formula, root) {
2480
+ return Selector.pseudos.nth(nodes, formula, root, false, true);
2481
+ },
2482
+ 'nth-last-of-type': function(nodes, formula, root) {
2483
+ return Selector.pseudos.nth(nodes, formula, root, true, true);
2484
+ },
2485
+ 'first-of-type': function(nodes, formula, root) {
2486
+ return Selector.pseudos.nth(nodes, "1", root, false, true);
2487
+ },
2488
+ 'last-of-type': function(nodes, formula, root) {
2489
+ return Selector.pseudos.nth(nodes, "1", root, true, true);
2490
+ },
2491
+ 'only-of-type': function(nodes, formula, root) {
2492
+ var p = Selector.pseudos;
2493
+ return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
2494
+ },
2495
+
2496
+ // handles the an+b logic
2497
+ getIndices: function(a, b, total) {
2498
+ if (a == 0) return b > 0 ? [b] : [];
2499
+ return $R(1, total).inject([], function(memo, i) {
2500
+ if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
2501
+ return memo;
2502
+ });
2503
+ },
2504
+
2505
+ // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
2506
+ nth: function(nodes, formula, root, reverse, ofType) {
2507
+ if (nodes.length == 0) return [];
2508
+ if (formula == 'even') formula = '2n+0';
2509
+ if (formula == 'odd') formula = '2n+1';
2510
+ var h = Selector.handlers, results = [], indexed = [], m;
2511
+ h.mark(nodes);
2512
+ for (var i = 0, node; node = nodes[i]; i++) {
2513
+ if (!node.parentNode._counted) {
2514
+ h.index(node.parentNode, reverse, ofType);
2515
+ indexed.push(node.parentNode);
2516
+ }
2517
+ }
2518
+ if (formula.match(/^\d+$/)) { // just a number
2519
+ formula = Number(formula);
2520
+ for (var i = 0, node; node = nodes[i]; i++)
2521
+ if (node.nodeIndex == formula) results.push(node);
2522
+ } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
2523
+ if (m[1] == "-") m[1] = -1;
2524
+ var a = m[1] ? Number(m[1]) : 1;
2525
+ var b = m[2] ? Number(m[2]) : 0;
2526
+ var indices = Selector.pseudos.getIndices(a, b, nodes.length);
2527
+ for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
2528
+ for (var j = 0; j < l; j++)
2529
+ if (node.nodeIndex == indices[j]) results.push(node);
2530
+ }
2531
+ }
2532
+ h.unmark(nodes);
2533
+ h.unmark(indexed);
2534
+ return results;
2535
+ },
2536
+
2537
+ 'empty': function(nodes, value, root) {
2538
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
2539
+ // IE treats comments as element nodes
2540
+ if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
2541
+ results.push(node);
2542
+ }
2543
+ return results;
2544
+ },
2545
+
2546
+ 'not': function(nodes, selector, root) {
2547
+ var h = Selector.handlers, selectorType, m;
2548
+ var exclusions = new Selector(selector).findElements(root);
2549
+ h.mark(exclusions);
2550
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
2551
+ if (!node._counted) results.push(node);
2552
+ h.unmark(exclusions);
2553
+ return results;
2554
+ },
2555
+
2556
+ 'enabled': function(nodes, value, root) {
2557
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
2558
+ if (!node.disabled) results.push(node);
2559
+ return results;
2560
+ },
2561
+
2562
+ 'disabled': function(nodes, value, root) {
2563
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
2564
+ if (node.disabled) results.push(node);
2565
+ return results;
2566
+ },
2567
+
2568
+ 'checked': function(nodes, value, root) {
2569
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
2570
+ if (node.checked) results.push(node);
2571
+ return results;
2572
+ }
2573
+ },
2574
+
2575
+ operators: {
2576
+ '=': function(nv, v) { return nv == v; },
2577
+ '!=': function(nv, v) { return nv != v; },
2578
+ '^=': function(nv, v) { return nv.startsWith(v); },
2579
+ '$=': function(nv, v) { return nv.endsWith(v); },
2580
+ '*=': function(nv, v) { return nv.include(v); },
2581
+ '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
2582
+ '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
2583
+ },
2584
+
2585
+ matchElements: function(elements, expression) {
2586
+ var matches = new Selector(expression).findElements(), h = Selector.handlers;
2587
+ h.mark(matches);
2588
+ for (var i = 0, results = [], element; element = elements[i]; i++)
2589
+ if (element._counted) results.push(element);
2590
+ h.unmark(matches);
2591
+ return results;
2592
+ },
2593
+
2594
+ findElement: function(elements, expression, index) {
2595
+ if (typeof expression == 'number') {
2596
+ index = expression; expression = false;
2597
+ }
2598
+ return Selector.matchElements(elements, expression || '*')[index || 0];
2599
+ },
2600
+
2601
+ findChildElements: function(element, expressions) {
2602
+ var exprs = expressions.join(','), expressions = [];
2603
+ exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
2604
+ expressions.push(m[1].strip());
2605
+ });
2606
+ var results = [], h = Selector.handlers;
2607
+ for (var i = 0, l = expressions.length, selector; i < l; i++) {
2608
+ selector = new Selector(expressions[i].strip());
2609
+ h.concat(results, selector.findElements(element));
2610
+ }
2611
+ return (l > 1) ? h.unique(results) : results;
2612
+ }
2613
+ });
2614
+
2615
+ function $$() {
2616
+ return Selector.findChildElements(document, $A(arguments));
2617
+ }
2618
+ var Form = {
2619
+ reset: function(form) {
2620
+ $(form).reset();
2621
+ return form;
2622
+ },
2623
+
2624
+ serializeElements: function(elements, getHash) {
2625
+ var data = elements.inject({}, function(result, element) {
2626
+ if (!element.disabled && element.name) {
2627
+ var key = element.name, value = $(element).getValue();
2628
+ if (value != null) {
2629
+ if (key in result) {
2630
+ if (result[key].constructor != Array) result[key] = [result[key]];
2631
+ result[key].push(value);
2632
+ }
2633
+ else result[key] = value;
2634
+ }
2635
+ }
2636
+ return result;
2637
+ });
2638
+
2639
+ return getHash ? data : Hash.toQueryString(data);
2640
+ }
2641
+ };
2642
+
2643
+ Form.Methods = {
2644
+ serialize: function(form, getHash) {
2645
+ return Form.serializeElements(Form.getElements(form), getHash);
2646
+ },
2647
+
2648
+ getElements: function(form) {
2649
+ return $A($(form).getElementsByTagName('*')).inject([],
2650
+ function(elements, child) {
2651
+ if (Form.Element.Serializers[child.tagName.toLowerCase()])
2652
+ elements.push(Element.extend(child));
2653
+ return elements;
2654
+ }
2655
+ );
2656
+ },
2657
+
2658
+ getInputs: function(form, typeName, name) {
2659
+ form = $(form);
2660
+ var inputs = form.getElementsByTagName('input');
2661
+
2662
+ if (!typeName && !name) return $A(inputs).map(Element.extend);
2663
+
2664
+ for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
2665
+ var input = inputs[i];
2666
+ if ((typeName && input.type != typeName) || (name && input.name != name))
2667
+ continue;
2668
+ matchingInputs.push(Element.extend(input));
2669
+ }
2670
+
2671
+ return matchingInputs;
2672
+ },
2673
+
2674
+ disable: function(form) {
2675
+ form = $(form);
2676
+ Form.getElements(form).invoke('disable');
2677
+ return form;
2678
+ },
2679
+
2680
+ enable: function(form) {
2681
+ form = $(form);
2682
+ Form.getElements(form).invoke('enable');
2683
+ return form;
2684
+ },
2685
+
2686
+ findFirstElement: function(form) {
2687
+ return $(form).getElements().find(function(element) {
2688
+ return element.type != 'hidden' && !element.disabled &&
2689
+ ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
2690
+ });
2691
+ },
2692
+
2693
+ focusFirstElement: function(form) {
2694
+ form = $(form);
2695
+ form.findFirstElement().activate();
2696
+ return form;
2697
+ },
2698
+
2699
+ request: function(form, options) {
2700
+ form = $(form), options = Object.clone(options || {});
2701
+
2702
+ var params = options.parameters;
2703
+ options.parameters = form.serialize(true);
2704
+
2705
+ if (params) {
2706
+ if (typeof params == 'string') params = params.toQueryParams();
2707
+ Object.extend(options.parameters, params);
2708
+ }
2709
+
2710
+ if (form.hasAttribute('method') && !options.method)
2711
+ options.method = form.method;
2712
+
2713
+ return new Ajax.Request(form.readAttribute('action'), options);
2714
+ }
2715
+ }
2716
+
2717
+ /*--------------------------------------------------------------------------*/
2718
+
2719
+ Form.Element = {
2720
+ focus: function(element) {
2721
+ $(element).focus();
2722
+ return element;
2723
+ },
2724
+
2725
+ select: function(element) {
2726
+ $(element).select();
2727
+ return element;
2728
+ }
2729
+ }
2730
+
2731
+ Form.Element.Methods = {
2732
+ serialize: function(element) {
2733
+ element = $(element);
2734
+ if (!element.disabled && element.name) {
2735
+ var value = element.getValue();
2736
+ if (value != undefined) {
2737
+ var pair = {};
2738
+ pair[element.name] = value;
2739
+ return Hash.toQueryString(pair);
2740
+ }
2741
+ }
2742
+ return '';
2743
+ },
2744
+
2745
+ getValue: function(element) {
2746
+ element = $(element);
2747
+ var method = element.tagName.toLowerCase();
2748
+ return Form.Element.Serializers[method](element);
2749
+ },
2750
+
2751
+ clear: function(element) {
2752
+ $(element).value = '';
2753
+ return element;
2754
+ },
2755
+
2756
+ present: function(element) {
2757
+ return $(element).value != '';
2758
+ },
2759
+
2760
+ activate: function(element) {
2761
+ element = $(element);
2762
+ try {
2763
+ element.focus();
2764
+ if (element.select && (element.tagName.toLowerCase() != 'input' ||
2765
+ !['button', 'reset', 'submit'].include(element.type)))
2766
+ element.select();
2767
+ } catch (e) {}
2768
+ return element;
2769
+ },
2770
+
2771
+ disable: function(element) {
2772
+ element = $(element);
2773
+ element.blur();
2774
+ element.disabled = true;
2775
+ return element;
2776
+ },
2777
+
2778
+ enable: function(element) {
2779
+ element = $(element);
2780
+ element.disabled = false;
2781
+ return element;
2782
+ }
2783
+ }
2784
+
2785
+ /*--------------------------------------------------------------------------*/
2786
+
2787
+ var Field = Form.Element;
2788
+ var $F = Form.Element.Methods.getValue;
2789
+
2790
+ /*--------------------------------------------------------------------------*/
2791
+
2792
+ Form.Element.Serializers = {
2793
+ input: function(element) {
2794
+ switch (element.type.toLowerCase()) {
2795
+ case 'checkbox':
2796
+ case 'radio':
2797
+ return Form.Element.Serializers.inputSelector(element);
2798
+ default:
2799
+ return Form.Element.Serializers.textarea(element);
2800
+ }
2801
+ },
2802
+
2803
+ inputSelector: function(element) {
2804
+ return element.checked ? element.value : null;
2805
+ },
2806
+
2807
+ textarea: function(element) {
2808
+ return element.value;
2809
+ },
2810
+
2811
+ select: function(element) {
2812
+ return this[element.type == 'select-one' ?
2813
+ 'selectOne' : 'selectMany'](element);
2814
+ },
2815
+
2816
+ selectOne: function(element) {
2817
+ var index = element.selectedIndex;
2818
+ return index >= 0 ? this.optionValue(element.options[index]) : null;
2819
+ },
2820
+
2821
+ selectMany: function(element) {
2822
+ var values, length = element.length;
2823
+ if (!length) return null;
2824
+
2825
+ for (var i = 0, values = []; i < length; i++) {
2826
+ var opt = element.options[i];
2827
+ if (opt.selected) values.push(this.optionValue(opt));
2828
+ }
2829
+ return values;
2830
+ },
2831
+
2832
+ optionValue: function(opt) {
2833
+ // extend element because hasAttribute may not be native
2834
+ return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
2835
+ }
2836
+ }
2837
+
2838
+ /*--------------------------------------------------------------------------*/
2839
+
2840
+ Abstract.TimedObserver = function() {}
2841
+ Abstract.TimedObserver.prototype = {
2842
+ initialize: function(element, frequency, callback) {
2843
+ this.frequency = frequency;
2844
+ this.element = $(element);
2845
+ this.callback = callback;
2846
+
2847
+ this.lastValue = this.getValue();
2848
+ this.registerCallback();
2849
+ },
2850
+
2851
+ registerCallback: function() {
2852
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
2853
+ },
2854
+
2855
+ onTimerEvent: function() {
2856
+ var value = this.getValue();
2857
+ var changed = ('string' == typeof this.lastValue && 'string' == typeof value
2858
+ ? this.lastValue != value : String(this.lastValue) != String(value));
2859
+ if (changed) {
2860
+ this.callback(this.element, value);
2861
+ this.lastValue = value;
2862
+ }
2863
+ }
2864
+ }
2865
+
2866
+ Form.Element.Observer = Class.create();
2867
+ Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2868
+ getValue: function() {
2869
+ return Form.Element.getValue(this.element);
2870
+ }
2871
+ });
2872
+
2873
+ Form.Observer = Class.create();
2874
+ Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2875
+ getValue: function() {
2876
+ return Form.serialize(this.element);
2877
+ }
2878
+ });
2879
+
2880
+ /*--------------------------------------------------------------------------*/
2881
+
2882
+ Abstract.EventObserver = function() {}
2883
+ Abstract.EventObserver.prototype = {
2884
+ initialize: function(element, callback) {
2885
+ this.element = $(element);
2886
+ this.callback = callback;
2887
+
2888
+ this.lastValue = this.getValue();
2889
+ if (this.element.tagName.toLowerCase() == 'form')
2890
+ this.registerFormCallbacks();
2891
+ else
2892
+ this.registerCallback(this.element);
2893
+ },
2894
+
2895
+ onElementEvent: function() {
2896
+ var value = this.getValue();
2897
+ if (this.lastValue != value) {
2898
+ this.callback(this.element, value);
2899
+ this.lastValue = value;
2900
+ }
2901
+ },
2902
+
2903
+ registerFormCallbacks: function() {
2904
+ Form.getElements(this.element).each(this.registerCallback.bind(this));
2905
+ },
2906
+
2907
+ registerCallback: function(element) {
2908
+ if (element.type) {
2909
+ switch (element.type.toLowerCase()) {
2910
+ case 'checkbox':
2911
+ case 'radio':
2912
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
2913
+ break;
2914
+ default:
2915
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
2916
+ break;
2917
+ }
2918
+ }
2919
+ }
2920
+ }
2921
+
2922
+ Form.Element.EventObserver = Class.create();
2923
+ Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2924
+ getValue: function() {
2925
+ return Form.Element.getValue(this.element);
2926
+ }
2927
+ });
2928
+
2929
+ Form.EventObserver = Class.create();
2930
+ Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2931
+ getValue: function() {
2932
+ return Form.serialize(this.element);
2933
+ }
2934
+ });
2935
+ if (!window.Event) {
2936
+ var Event = new Object();
2937
+ }
2938
+
2939
+ Object.extend(Event, {
2940
+ KEY_BACKSPACE: 8,
2941
+ KEY_TAB: 9,
2942
+ KEY_RETURN: 13,
2943
+ KEY_ESC: 27,
2944
+ KEY_LEFT: 37,
2945
+ KEY_UP: 38,
2946
+ KEY_RIGHT: 39,
2947
+ KEY_DOWN: 40,
2948
+ KEY_DELETE: 46,
2949
+ KEY_HOME: 36,
2950
+ KEY_END: 35,
2951
+ KEY_PAGEUP: 33,
2952
+ KEY_PAGEDOWN: 34,
2953
+
2954
+ element: function(event) {
2955
+ return $(event.target || event.srcElement);
2956
+ },
2957
+
2958
+ isLeftClick: function(event) {
2959
+ return (((event.which) && (event.which == 1)) ||
2960
+ ((event.button) && (event.button == 1)));
2961
+ },
2962
+
2963
+ pointerX: function(event) {
2964
+ return event.pageX || (event.clientX +
2965
+ (document.documentElement.scrollLeft || document.body.scrollLeft));
2966
+ },
2967
+
2968
+ pointerY: function(event) {
2969
+ return event.pageY || (event.clientY +
2970
+ (document.documentElement.scrollTop || document.body.scrollTop));
2971
+ },
2972
+
2973
+ stop: function(event) {
2974
+ if (event.preventDefault) {
2975
+ event.preventDefault();
2976
+ event.stopPropagation();
2977
+ } else {
2978
+ event.returnValue = false;
2979
+ event.cancelBubble = true;
2980
+ }
2981
+ },
2982
+
2983
+ // find the first node with the given tagName, starting from the
2984
+ // node the event was triggered on; traverses the DOM upwards
2985
+ findElement: function(event, tagName) {
2986
+ var element = Event.element(event);
2987
+ while (element.parentNode && (!element.tagName ||
2988
+ (element.tagName.toUpperCase() != tagName.toUpperCase())))
2989
+ element = element.parentNode;
2990
+ return element;
2991
+ },
2992
+
2993
+ observers: false,
2994
+
2995
+ _observeAndCache: function(element, name, observer, useCapture) {
2996
+ if (!this.observers) this.observers = [];
2997
+ if (element.addEventListener) {
2998
+ this.observers.push([element, name, observer, useCapture]);
2999
+ element.addEventListener(name, observer, useCapture);
3000
+ } else if (element.attachEvent) {
3001
+ this.observers.push([element, name, observer, useCapture]);
3002
+ element.attachEvent('on' + name, observer);
3003
+ }
3004
+ },
3005
+
3006
+ unloadCache: function() {
3007
+ if (!Event.observers) return;
3008
+ for (var i = 0, length = Event.observers.length; i < length; i++) {
3009
+ Event.stopObserving.apply(this, Event.observers[i]);
3010
+ Event.observers[i][0] = null;
3011
+ }
3012
+ Event.observers = false;
3013
+ },
3014
+
3015
+ observe: function(element, name, observer, useCapture) {
3016
+ element = $(element);
3017
+ useCapture = useCapture || false;
3018
+
3019
+ if (name == 'keypress' &&
3020
+ (Prototype.Browser.WebKit || element.attachEvent))
3021
+ name = 'keydown';
3022
+
3023
+ Event._observeAndCache(element, name, observer, useCapture);
3024
+ },
3025
+
3026
+ stopObserving: function(element, name, observer, useCapture) {
3027
+ element = $(element);
3028
+ useCapture = useCapture || false;
3029
+
3030
+ if (name == 'keypress' &&
3031
+ (Prototype.Browser.WebKit || element.attachEvent))
3032
+ name = 'keydown';
3033
+
3034
+ if (element.removeEventListener) {
3035
+ element.removeEventListener(name, observer, useCapture);
3036
+ } else if (element.detachEvent) {
3037
+ try {
3038
+ element.detachEvent('on' + name, observer);
3039
+ } catch (e) {}
3040
+ }
3041
+ }
3042
+ });
3043
+
3044
+ /* prevent memory leaks in IE */
3045
+ if (Prototype.Browser.IE)
3046
+ Event.observe(window, 'unload', Event.unloadCache, false);
3047
+ var Position = {
3048
+ // set to true if needed, warning: firefox performance problems
3049
+ // NOT neeeded for page scrolling, only if draggable contained in
3050
+ // scrollable elements
3051
+ includeScrollOffsets: false,
3052
+
3053
+ // must be called before calling withinIncludingScrolloffset, every time the
3054
+ // page is scrolled
3055
+ prepare: function() {
3056
+ this.deltaX = window.pageXOffset
3057
+ || document.documentElement.scrollLeft
3058
+ || document.body.scrollLeft
3059
+ || 0;
3060
+ this.deltaY = window.pageYOffset
3061
+ || document.documentElement.scrollTop
3062
+ || document.body.scrollTop
3063
+ || 0;
3064
+ },
3065
+
3066
+ realOffset: function(element) {
3067
+ var valueT = 0, valueL = 0;
3068
+ do {
3069
+ valueT += element.scrollTop || 0;
3070
+ valueL += element.scrollLeft || 0;
3071
+ element = element.parentNode;
3072
+ } while (element);
3073
+ return [valueL, valueT];
3074
+ },
3075
+
3076
+ cumulativeOffset: function(element) {
3077
+ var valueT = 0, valueL = 0;
3078
+ do {
3079
+ valueT += element.offsetTop || 0;
3080
+ valueL += element.offsetLeft || 0;
3081
+ element = element.offsetParent;
3082
+ } while (element);
3083
+ return [valueL, valueT];
3084
+ },
3085
+
3086
+ positionedOffset: function(element) {
3087
+ var valueT = 0, valueL = 0;
3088
+ do {
3089
+ valueT += element.offsetTop || 0;
3090
+ valueL += element.offsetLeft || 0;
3091
+ element = element.offsetParent;
3092
+ if (element) {
3093
+ if(element.tagName=='BODY') break;
3094
+ var p = Element.getStyle(element, 'position');
3095
+ if (p == 'relative' || p == 'absolute') break;
3096
+ }
3097
+ } while (element);
3098
+ return [valueL, valueT];
3099
+ },
3100
+
3101
+ offsetParent: function(element) {
3102
+ if (element.offsetParent) return element.offsetParent;
3103
+ if (element == document.body) return element;
3104
+
3105
+ while ((element = element.parentNode) && element != document.body)
3106
+ if (Element.getStyle(element, 'position') != 'static')
3107
+ return element;
3108
+
3109
+ return document.body;
3110
+ },
3111
+
3112
+ // caches x/y coordinate pair to use with overlap
3113
+ within: function(element, x, y) {
3114
+ if (this.includeScrollOffsets)
3115
+ return this.withinIncludingScrolloffsets(element, x, y);
3116
+ this.xcomp = x;
3117
+ this.ycomp = y;
3118
+ this.offset = this.cumulativeOffset(element);
3119
+
3120
+ return (y >= this.offset[1] &&
3121
+ y < this.offset[1] + element.offsetHeight &&
3122
+ x >= this.offset[0] &&
3123
+ x < this.offset[0] + element.offsetWidth);
3124
+ },
3125
+
3126
+ withinIncludingScrolloffsets: function(element, x, y) {
3127
+ var offsetcache = this.realOffset(element);
3128
+
3129
+ this.xcomp = x + offsetcache[0] - this.deltaX;
3130
+ this.ycomp = y + offsetcache[1] - this.deltaY;
3131
+ this.offset = this.cumulativeOffset(element);
3132
+
3133
+ return (this.ycomp >= this.offset[1] &&
3134
+ this.ycomp < this.offset[1] + element.offsetHeight &&
3135
+ this.xcomp >= this.offset[0] &&
3136
+ this.xcomp < this.offset[0] + element.offsetWidth);
3137
+ },
3138
+
3139
+ // within must be called directly before
3140
+ overlap: function(mode, element) {
3141
+ if (!mode) return 0;
3142
+ if (mode == 'vertical')
3143
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
3144
+ element.offsetHeight;
3145
+ if (mode == 'horizontal')
3146
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
3147
+ element.offsetWidth;
3148
+ },
3149
+
3150
+ page: function(forElement) {
3151
+ var valueT = 0, valueL = 0;
3152
+
3153
+ var element = forElement;
3154
+ do {
3155
+ valueT += element.offsetTop || 0;
3156
+ valueL += element.offsetLeft || 0;
3157
+
3158
+ // Safari fix
3159
+ if (element.offsetParent == document.body)
3160
+ if (Element.getStyle(element,'position')=='absolute') break;
3161
+
3162
+ } while (element = element.offsetParent);
3163
+
3164
+ element = forElement;
3165
+ do {
3166
+ if (!window.opera || element.tagName=='BODY') {
3167
+ valueT -= element.scrollTop || 0;
3168
+ valueL -= element.scrollLeft || 0;
3169
+ }
3170
+ } while (element = element.parentNode);
3171
+
3172
+ return [valueL, valueT];
3173
+ },
3174
+
3175
+ clone: function(source, target) {
3176
+ var options = Object.extend({
3177
+ setLeft: true,
3178
+ setTop: true,
3179
+ setWidth: true,
3180
+ setHeight: true,
3181
+ offsetTop: 0,
3182
+ offsetLeft: 0
3183
+ }, arguments[2] || {})
3184
+
3185
+ // find page position of source
3186
+ source = $(source);
3187
+ var p = Position.page(source);
3188
+
3189
+ // find coordinate system to use
3190
+ target = $(target);
3191
+ var delta = [0, 0];
3192
+ var parent = null;
3193
+ // delta [0,0] will do fine with position: fixed elements,
3194
+ // position:absolute needs offsetParent deltas
3195
+ if (Element.getStyle(target,'position') == 'absolute') {
3196
+ parent = Position.offsetParent(target);
3197
+ delta = Position.page(parent);
3198
+ }
3199
+
3200
+ // correct by body offsets (fixes Safari)
3201
+ if (parent == document.body) {
3202
+ delta[0] -= document.body.offsetLeft;
3203
+ delta[1] -= document.body.offsetTop;
3204
+ }
3205
+
3206
+ // set position
3207
+ if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
3208
+ if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
3209
+ if(options.setWidth) target.style.width = source.offsetWidth + 'px';
3210
+ if(options.setHeight) target.style.height = source.offsetHeight + 'px';
3211
+ },
3212
+
3213
+ absolutize: function(element) {
3214
+ element = $(element);
3215
+ if (element.style.position == 'absolute') return;
3216
+ Position.prepare();
3217
+
3218
+ var offsets = Position.positionedOffset(element);
3219
+ var top = offsets[1];
3220
+ var left = offsets[0];
3221
+ var width = element.clientWidth;
3222
+ var height = element.clientHeight;
3223
+
3224
+ element._originalLeft = left - parseFloat(element.style.left || 0);
3225
+ element._originalTop = top - parseFloat(element.style.top || 0);
3226
+ element._originalWidth = element.style.width;
3227
+ element._originalHeight = element.style.height;
3228
+
3229
+ element.style.position = 'absolute';
3230
+ element.style.top = top + 'px';
3231
+ element.style.left = left + 'px';
3232
+ element.style.width = width + 'px';
3233
+ element.style.height = height + 'px';
3234
+ },
3235
+
3236
+ relativize: function(element) {
3237
+ element = $(element);
3238
+ if (element.style.position == 'relative') return;
3239
+ Position.prepare();
3240
+
3241
+ element.style.position = 'relative';
3242
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
3243
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
3244
+
3245
+ element.style.top = top + 'px';
3246
+ element.style.left = left + 'px';
3247
+ element.style.height = element._originalHeight;
3248
+ element.style.width = element._originalWidth;
3249
+ }
3250
+ }
3251
+
3252
+ // Safari returns margins on body which is incorrect if the child is absolutely
3253
+ // positioned. For performance reasons, redefine Position.cumulativeOffset for
3254
+ // KHTML/WebKit only.
3255
+ if (Prototype.Browser.WebKit) {
3256
+ Position.cumulativeOffset = function(element) {
3257
+ var valueT = 0, valueL = 0;
3258
+ do {
3259
+ valueT += element.offsetTop || 0;
3260
+ valueL += element.offsetLeft || 0;
3261
+ if (element.offsetParent == document.body)
3262
+ if (Element.getStyle(element, 'position') == 'absolute') break;
3263
+
3264
+ element = element.offsetParent;
3265
+ } while (element);
3266
+
3267
+ return [valueL, valueT];
3268
+ }
3269
+ }
3270
+
3271
+ Element.addMethods();