taskr 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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();