Description:
initial commit
git-svn-id: http://theory.cpe.ku.ac.th/grader/web/trunk@1 6386c4cd-e34a-4fa8-8920-d93eb39b512e
Commit status:
[Not Reviewed]
References:
Diff options:
Comments:
0 Commit comments
0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
r0:adb458d2f524 - - 114 files changed: 7811 inserted, 0 deleted
@@ -0,0 +1,182 | |||
|
1 | + == Welcome to Rails | |
|
2 | + | |
|
3 | + Rails is a web-application and persistence framework that includes everything | |
|
4 | + needed to create database-backed web-applications according to the | |
|
5 | + Model-View-Control pattern of separation. This pattern splits the view (also | |
|
6 | + called the presentation) into "dumb" templates that are primarily responsible | |
|
7 | + for inserting pre-built data in between HTML tags. The model contains the | |
|
8 | + "smart" domain objects (such as Account, Product, Person, Post) that holds all | |
|
9 | + the business logic and knows how to persist themselves to a database. The | |
|
10 | + controller handles the incoming requests (such as Save New Account, Update | |
|
11 | + Product, Show Post) by manipulating the model and directing data to the view. | |
|
12 | + | |
|
13 | + In Rails, the model is handled by what's called an object-relational mapping | |
|
14 | + layer entitled Active Record. This layer allows you to present the data from | |
|
15 | + database rows as objects and embellish these data objects with business logic | |
|
16 | + methods. You can read more about Active Record in | |
|
17 | + link:files/vendor/rails/activerecord/README.html. | |
|
18 | + | |
|
19 | + The controller and view are handled by the Action Pack, which handles both | |
|
20 | + layers by its two parts: Action View and Action Controller. These two layers | |
|
21 | + are bundled in a single package due to their heavy interdependence. This is | |
|
22 | + unlike the relationship between the Active Record and Action Pack that is much | |
|
23 | + more separate. Each of these packages can be used independently outside of | |
|
24 | + Rails. You can read more about Action Pack in | |
|
25 | + link:files/vendor/rails/actionpack/README.html. | |
|
26 | + | |
|
27 | + | |
|
28 | + == Getting started | |
|
29 | + | |
|
30 | + 1. At the command prompt, start a new rails application using the rails command | |
|
31 | + and your application name. Ex: rails myapp | |
|
32 | + (If you've downloaded rails in a complete tgz or zip, this step is already done) | |
|
33 | + 2. Change directory into myapp and start the web server: <tt>script/server</tt> (run with --help for options) | |
|
34 | + 3. Go to http://localhost:3000/ and get "Welcome aboard: You’re riding the Rails!" | |
|
35 | + 4. Follow the guidelines to start developing your application | |
|
36 | + | |
|
37 | + | |
|
38 | + == Web Servers | |
|
39 | + | |
|
40 | + By default, Rails will try to use Mongrel and lighttpd if they are installed, otherwise | |
|
41 | + Rails will use the WEBrick, the webserver that ships with Ruby. When you run script/server, | |
|
42 | + Rails will check if Mongrel exists, then lighttpd and finally fall back to WEBrick. This ensures | |
|
43 | + that you can always get up and running quickly. | |
|
44 | + | |
|
45 | + Mongrel is a Ruby-based webserver with a C-component (which requires compilation) that is | |
|
46 | + suitable for development and deployment of Rails applications. If you have Ruby Gems installed, | |
|
47 | + getting up and running with mongrel is as easy as: <tt>gem install mongrel</tt>. | |
|
48 | + More info at: http://mongrel.rubyforge.org | |
|
49 | + | |
|
50 | + If Mongrel is not installed, Rails will look for lighttpd. It's considerably faster than | |
|
51 | + Mongrel and WEBrick and also suited for production use, but requires additional | |
|
52 | + installation and currently only works well on OS X/Unix (Windows users are encouraged | |
|
53 | + to start with Mongrel). We recommend version 1.4.11 and higher. You can download it from | |
|
54 | + http://www.lighttpd.net. | |
|
55 | + | |
|
56 | + And finally, if neither Mongrel or lighttpd are installed, Rails will use the built-in Ruby | |
|
57 | + web server, WEBrick. WEBrick is a small Ruby web server suitable for development, but not | |
|
58 | + for production. | |
|
59 | + | |
|
60 | + But of course its also possible to run Rails on any platform that supports FCGI. | |
|
61 | + Apache, LiteSpeed, IIS are just a few. For more information on FCGI, | |
|
62 | + please visit: http://wiki.rubyonrails.com/rails/pages/FastCGI | |
|
63 | + | |
|
64 | + | |
|
65 | + == Debugging Rails | |
|
66 | + | |
|
67 | + Have "tail -f" commands running on the server.log and development.log. Rails will | |
|
68 | + automatically display debugging and runtime information to these files. Debugging | |
|
69 | + info will also be shown in the browser on requests from 127.0.0.1. | |
|
70 | + | |
|
71 | + | |
|
72 | + == Breakpoints | |
|
73 | + | |
|
74 | + Breakpoint support is available through the script/breakpointer client. This | |
|
75 | + means that you can break out of execution at any point in the code, investigate | |
|
76 | + and change the model, AND then resume execution! Example: | |
|
77 | + | |
|
78 | + class WeblogController < ActionController::Base | |
|
79 | + def index | |
|
80 | + @posts = Post.find(:all) | |
|
81 | + breakpoint "Breaking out from the list" | |
|
82 | + end | |
|
83 | + end | |
|
84 | + | |
|
85 | + So the controller will accept the action, run the first line, then present you | |
|
86 | + with a IRB prompt in the breakpointer window. Here you can do things like: | |
|
87 | + | |
|
88 | + Executing breakpoint "Breaking out from the list" at .../webrick_server.rb:16 in 'breakpoint' | |
|
89 | + | |
|
90 | + >> @posts.inspect | |
|
91 | + => "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>, | |
|
92 | + #<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]" | |
|
93 | + >> @posts.first.title = "hello from a breakpoint" | |
|
94 | + => "hello from a breakpoint" | |
|
95 | + | |
|
96 | + ...and even better is that you can examine how your runtime objects actually work: | |
|
97 | + | |
|
98 | + >> f = @posts.first | |
|
99 | + => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}> | |
|
100 | + >> f. | |
|
101 | + Display all 152 possibilities? (y or n) | |
|
102 | + | |
|
103 | + Finally, when you're ready to resume execution, you press CTRL-D | |
|
104 | + | |
|
105 | + | |
|
106 | + == Console | |
|
107 | + | |
|
108 | + You can interact with the domain model by starting the console through <tt>script/console</tt>. | |
|
109 | + Here you'll have all parts of the application configured, just like it is when the | |
|
110 | + application is running. You can inspect domain models, change values, and save to the | |
|
111 | + database. Starting the script without arguments will launch it in the development environment. | |
|
112 | + Passing an argument will specify a different environment, like <tt>script/console production</tt>. | |
|
113 | + | |
|
114 | + To reload your controllers and models after launching the console run <tt>reload!</tt> | |
|
115 | + | |
|
116 | + To reload your controllers and models after launching the console run <tt>reload!</tt> | |
|
117 | + | |
|
118 | + | |
|
119 | + | |
|
120 | + == Description of contents | |
|
121 | + | |
|
122 | + app | |
|
123 | + Holds all the code that's specific to this particular application. | |
|
124 | + | |
|
125 | + app/controllers | |
|
126 | + Holds controllers that should be named like weblogs_controller.rb for | |
|
127 | + automated URL mapping. All controllers should descend from ApplicationController | |
|
128 | + which itself descends from ActionController::Base. | |
|
129 | + | |
|
130 | + app/models | |
|
131 | + Holds models that should be named like post.rb. | |
|
132 | + Most models will descend from ActiveRecord::Base. | |
|
133 | + | |
|
134 | + app/views | |
|
135 | + Holds the template files for the view that should be named like | |
|
136 | + weblogs/index.rhtml for the WeblogsController#index action. All views use eRuby | |
|
137 | + syntax. | |
|
138 | + | |
|
139 | + app/views/layouts | |
|
140 | + Holds the template files for layouts to be used with views. This models the common | |
|
141 | + header/footer method of wrapping views. In your views, define a layout using the | |
|
142 | + <tt>layout :default</tt> and create a file named default.rhtml. Inside default.rhtml, | |
|
143 | + call <% yield %> to render the view using this layout. | |
|
144 | + | |
|
145 | + app/helpers | |
|
146 | + Holds view helpers that should be named like weblogs_helper.rb. These are generated | |
|
147 | + for you automatically when using script/generate for controllers. Helpers can be used to | |
|
148 | + wrap functionality for your views into methods. | |
|
149 | + | |
|
150 | + config | |
|
151 | + Configuration files for the Rails environment, the routing map, the database, and other dependencies. | |
|
152 | + | |
|
153 | + components | |
|
154 | + Self-contained mini-applications that can bundle together controllers, models, and views. | |
|
155 | + | |
|
156 | + db | |
|
157 | + Contains the database schema in schema.rb. db/migrate contains all | |
|
158 | + the sequence of Migrations for your schema. | |
|
159 | + | |
|
160 | + doc | |
|
161 | + This directory is where your application documentation will be stored when generated | |
|
162 | + using <tt>rake doc:app</tt> | |
|
163 | + | |
|
164 | + lib | |
|
165 | + Application specific libraries. Basically, any kind of custom code that doesn't | |
|
166 | + belong under controllers, models, or helpers. This directory is in the load path. | |
|
167 | + | |
|
168 | + public | |
|
169 | + The directory available for the web server. Contains subdirectories for images, stylesheets, | |
|
170 | + and javascripts. Also contains the dispatchers and the default HTML files. This should be | |
|
171 | + set as the DOCUMENT_ROOT of your web server. | |
|
172 | + | |
|
173 | + script | |
|
174 | + Helper scripts for automation and generation. | |
|
175 | + | |
|
176 | + test | |
|
177 | + Unit and functional tests along with fixtures. When using the script/generate scripts, template | |
|
178 | + test files will be generated for you and placed in this directory. | |
|
179 | + | |
|
180 | + vendor | |
|
181 | + External libraries that the application depends on. Also includes the plugins subdirectory. | |
|
182 | + This directory is in the load path. |
@@ -0,0 +1,10 | |||
|
1 | + # Add your own tasks in files placed in lib/tasks ending in .rake, | |
|
2 | + # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. | |
|
3 | + | |
|
4 | + require(File.join(File.dirname(__FILE__), 'config', 'boot')) | |
|
5 | + | |
|
6 | + require 'rake' | |
|
7 | + require 'rake/testtask' | |
|
8 | + require 'rake/rdoctask' | |
|
9 | + | |
|
10 | + require 'tasks/rails' |
@@ -0,0 +1,34 | |||
|
1 | + # Filters added to this controller apply to all controllers in the application. | |
|
2 | + # Likewise, all the methods added will be available for all controllers. | |
|
3 | + | |
|
4 | + class ApplicationController < ActionController::Base | |
|
5 | + # Pick a unique cookie name to distinguish our session data from others' | |
|
6 | + session :session_key => '_grader_session_id' | |
|
7 | + | |
|
8 | + protected | |
|
9 | + def authenticate | |
|
10 | + unless session[:user_id] | |
|
11 | + redirect_to :controller => 'main', :action => 'login' | |
|
12 | + return false | |
|
13 | + end | |
|
14 | + return true | |
|
15 | + end | |
|
16 | + | |
|
17 | + def authorization | |
|
18 | + return false unless authenticate | |
|
19 | + user = User.find(session[:user_id]) | |
|
20 | + unless user.roles.detect { |role| | |
|
21 | + role.rights.detect{ |right| | |
|
22 | + right.controller == self.class.controller_name and | |
|
23 | + (right.action == 'all' or right.action == action_name) | |
|
24 | + } | |
|
25 | + } | |
|
26 | + flash[:notice] = 'You are not authorized to view the page you requested' | |
|
27 | + #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login') | |
|
28 | + redirect_to :controller => 'login' | |
|
29 | + return false | |
|
30 | + end | |
|
31 | + end | |
|
32 | + | |
|
33 | + end | |
|
34 | + |
@@ -0,0 +1,19 | |||
|
1 | + class LoginController < ApplicationController | |
|
2 | + | |
|
3 | + def index | |
|
4 | + # show login screen | |
|
5 | + reset_session | |
|
6 | + redirect_to :controller => 'main', :action => 'login' | |
|
7 | + end | |
|
8 | + | |
|
9 | + def login | |
|
10 | + if user = User.authenticate(params[:login], params[:password]) | |
|
11 | + session[:user_id] = user.id | |
|
12 | + redirect_to :controller => 'main', :action => 'list' | |
|
13 | + else | |
|
14 | + flash[:notice] = 'Wrong password' | |
|
15 | + redirect_to :controller => 'main', :action => 'login' | |
|
16 | + end | |
|
17 | + end | |
|
18 | + | |
|
19 | + end |
@@ -0,0 +1,65 | |||
|
1 | + class MainController < ApplicationController | |
|
2 | + | |
|
3 | + before_filter :authenticate, :except => [:index, :login] | |
|
4 | + | |
|
5 | + verify :method => :post, :only => [:submit], | |
|
6 | + :redirect_to => { :action => :index } | |
|
7 | + | |
|
8 | + def index | |
|
9 | + end | |
|
10 | + | |
|
11 | + def login | |
|
12 | + reset_session | |
|
13 | + end | |
|
14 | + | |
|
15 | + def list | |
|
16 | + @problems = Problem.find_available_problems | |
|
17 | + @prob_submissions = Array.new | |
|
18 | + @user = User.find(session[:user_id]) | |
|
19 | + @problems.each do |p| | |
|
20 | + c, sub = Submission.find_by_user_and_problem(@user.id,p.id) | |
|
21 | + @prob_submissions << [c,sub] | |
|
22 | + end | |
|
23 | + end | |
|
24 | + | |
|
25 | + def submit | |
|
26 | + submission = Submission.new(params[:submission]) | |
|
27 | + submission.user_id = session[:user_id] | |
|
28 | + submission.language_id = 0 | |
|
29 | + source = params['file'].read | |
|
30 | + if source.length > 100_000 | |
|
31 | + flash[:notice] = 'Error: file too long' | |
|
32 | + elsif (lang = Submission.find_language_in_source(source))==nil | |
|
33 | + flash[:notice] = 'Error: cannot determine language used' | |
|
34 | + elsif ((submission.problem_id==-1) and | |
|
35 | + !(problem=Submission.find_problem_in_source(source))) | |
|
36 | + flash[:notice] = 'Error: cannot determine problem submitted' | |
|
37 | + elsif ((submission.problem_id==-1) and | |
|
38 | + (problem.available == false)) | |
|
39 | + flash[:notice] = 'Error: problem is not available' | |
|
40 | + else | |
|
41 | + submission.problem_id = problem.id if submission.problem_id == -1 | |
|
42 | + submission.source = source | |
|
43 | + submission.language_id = lang.id | |
|
44 | + submission.submitted_at = Time.new | |
|
45 | + if submission.save == false | |
|
46 | + flash[:notice] = 'Error saving your submission' | |
|
47 | + elsif Task.create(:submission_id => submission.id) == false | |
|
48 | + flash[:notice] = 'Error adding your submission to task queue' | |
|
49 | + end | |
|
50 | + end | |
|
51 | + redirect_to :action => 'list' | |
|
52 | + end | |
|
53 | + | |
|
54 | + def get_source | |
|
55 | + submission = Submission.find(params[:id]) | |
|
56 | + if submission.user_id == session[:user_id] | |
|
57 | + fname = submission.problem.name + '.' + submission.language.ext | |
|
58 | + send_data(submission.source, | |
|
59 | + {:filename => fname, | |
|
60 | + :type => 'text/plain'}) | |
|
61 | + else | |
|
62 | + flash[:notice] = 'Error viewing source' | |
|
63 | + end | |
|
64 | + end | |
|
65 | + end |
@@ -0,0 +1,68 | |||
|
1 | + class ProblemsController < ApplicationController | |
|
2 | + | |
|
3 | + before_filter :authenticate, :authorization | |
|
4 | + | |
|
5 | + def index | |
|
6 | + list | |
|
7 | + render :action => 'list' | |
|
8 | + end | |
|
9 | + | |
|
10 | + # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html) | |
|
11 | + verify :method => :post, :only => [ :destroy, :create, :update ], | |
|
12 | + :redirect_to => { :action => :list } | |
|
13 | + | |
|
14 | + def list | |
|
15 | + @problem_pages, @problems = paginate(:problems, | |
|
16 | + :per_page => 10, | |
|
17 | + :order => 'date_added DESC') | |
|
18 | + end | |
|
19 | + | |
|
20 | + def show | |
|
21 | + @problem = Problem.find(params[:id]) | |
|
22 | + end | |
|
23 | + | |
|
24 | + def new | |
|
25 | + @problem = Problem.new | |
|
26 | + end | |
|
27 | + | |
|
28 | + def create | |
|
29 | + @problem = Problem.new(params[:problem]) | |
|
30 | + if @problem.save | |
|
31 | + flash[:notice] = 'Problem was successfully created.' | |
|
32 | + redirect_to :action => 'list' | |
|
33 | + else | |
|
34 | + render :action => 'new' | |
|
35 | + end | |
|
36 | + end | |
|
37 | + | |
|
38 | + def edit | |
|
39 | + @problem = Problem.find(params[:id]) | |
|
40 | + end | |
|
41 | + | |
|
42 | + def update | |
|
43 | + @problem = Problem.find(params[:id]) | |
|
44 | + if @problem.update_attributes(params[:problem]) | |
|
45 | + flash[:notice] = 'Problem was successfully updated.' | |
|
46 | + redirect_to :action => 'show', :id => @problem | |
|
47 | + else | |
|
48 | + render :action => 'edit' | |
|
49 | + end | |
|
50 | + end | |
|
51 | + | |
|
52 | + def destroy | |
|
53 | + Problem.find(params[:id]).destroy | |
|
54 | + redirect_to :action => 'list' | |
|
55 | + end | |
|
56 | + | |
|
57 | + def toggle_avail | |
|
58 | + problem = Problem.find(params[:id]) | |
|
59 | + problem.available = !(problem.available) | |
|
60 | + problem.save | |
|
61 | + redirect_to :action => 'list' | |
|
62 | + end | |
|
63 | + | |
|
64 | + def stat | |
|
65 | + @problem = Problem.find(params[:id]) | |
|
66 | + @submissions = Submission.find_last_by_problem(params[:id]) | |
|
67 | + end | |
|
68 | + end |
@@ -0,0 +1,73 | |||
|
1 | + class UserAdminController < ApplicationController | |
|
2 | + | |
|
3 | + before_filter :authenticate, :authorization | |
|
4 | + | |
|
5 | + def index | |
|
6 | + list | |
|
7 | + render :action => 'list' | |
|
8 | + end | |
|
9 | + | |
|
10 | + # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html) | |
|
11 | + verify :method => :post, :only => [ :destroy, :create, :update ], | |
|
12 | + :redirect_to => { :action => :list } | |
|
13 | + | |
|
14 | + def list | |
|
15 | + @user_pages, @users = paginate :users, :per_page => 50 | |
|
16 | + end | |
|
17 | + | |
|
18 | + def show | |
|
19 | + @user = User.find(params[:id]) | |
|
20 | + end | |
|
21 | + | |
|
22 | + def new | |
|
23 | + @user = User.new | |
|
24 | + end | |
|
25 | + | |
|
26 | + def create | |
|
27 | + @user = User.new(params[:user]) | |
|
28 | + if @user.save | |
|
29 | + flash[:notice] = 'User was successfully created.' | |
|
30 | + redirect_to :action => 'list' | |
|
31 | + else | |
|
32 | + render :action => 'new' | |
|
33 | + end | |
|
34 | + end | |
|
35 | + | |
|
36 | + def edit | |
|
37 | + @user = User.find(params[:id]) | |
|
38 | + end | |
|
39 | + | |
|
40 | + def update | |
|
41 | + @user = User.find(params[:id]) | |
|
42 | + if @user.update_attributes(params[:user]) | |
|
43 | + flash[:notice] = 'User was successfully updated.' | |
|
44 | + redirect_to :action => 'show', :id => @user | |
|
45 | + else | |
|
46 | + render :action => 'edit' | |
|
47 | + end | |
|
48 | + end | |
|
49 | + | |
|
50 | + def destroy | |
|
51 | + User.find(params[:id]).destroy | |
|
52 | + redirect_to :action => 'list' | |
|
53 | + end | |
|
54 | + | |
|
55 | + def user_stat | |
|
56 | + @problems = Problem.find_available_problems | |
|
57 | + @users = User.find(:all) | |
|
58 | + @scorearray = Array.new | |
|
59 | + @users.each do |u| | |
|
60 | + ustat = Array.new | |
|
61 | + ustat[0] = u.login | |
|
62 | + @problems.each do |p| | |
|
63 | + c, sub = Submission.find_by_user_and_problem(u.id,p.id) | |
|
64 | + if c!=0 | |
|
65 | + ustat << sub.points | |
|
66 | + else | |
|
67 | + ustat << 0 | |
|
68 | + end | |
|
69 | + end | |
|
70 | + @scorearray << ustat | |
|
71 | + end | |
|
72 | + end | |
|
73 | + end |
@@ -0,0 +1,3 | |||
|
1 | + # Methods added to this helper will be available to all templates in the application. | |
|
2 | + module ApplicationHelper | |
|
3 | + end |
@@ -0,0 +1,61 | |||
|
1 | + module MainHelper | |
|
2 | + | |
|
3 | + def user_options | |
|
4 | + options = '' | |
|
5 | + user = User.find(session[:user_id]) | |
|
6 | + if user.admin? | |
|
7 | + options = options + ' ' + | |
|
8 | + link_to('[Problem admin]', | |
|
9 | + {:controller => 'problems', :action => 'index'}) | |
|
10 | + options = options + ' ' + | |
|
11 | + link_to('[User admin]', | |
|
12 | + {:controller => 'user_admin', :action => 'index'}) | |
|
13 | + end | |
|
14 | + options = options + ' ' + | |
|
15 | + link_to('[Log out]', {:controller => 'main', :action => 'login'}) | |
|
16 | + options | |
|
17 | + end | |
|
18 | + | |
|
19 | + def format_short_time(time) | |
|
20 | + now = Time.now | |
|
21 | + st = '' | |
|
22 | + if (time.yday != now.yday) or | |
|
23 | + (time.year != now.year) | |
|
24 | + st = time.strftime("%x") | |
|
25 | + end | |
|
26 | + st + time.strftime("%X") | |
|
27 | + end | |
|
28 | + | |
|
29 | + def format_compiler_msg(sub) | |
|
30 | + <<cmpmsg | |
|
31 | + <div> | |
|
32 | + <div><a href="#" onClick="n = this.parentNode.parentNode.lastChild; | |
|
33 | + if(n.style.display == 'none') { n.style.display = 'block'; } | |
|
34 | + else {n.style.display ='none'; } return false;"> | |
|
35 | + Compiler message</a> (click to see)</div> | |
|
36 | + <div style="display: none"> | |
|
37 | + <div class="compilermsgbody" style="border: thin solid grey; margin: 2px"> | |
|
38 | + #{h(sub.compiler_message).gsub(/\n/,'<br/>')} | |
|
39 | + </div> | |
|
40 | + </div></div> | |
|
41 | + cmpmsg | |
|
42 | + end | |
|
43 | + | |
|
44 | + def format_submission(sub, count) | |
|
45 | + msg = "#{count} submission(s)<br />" | |
|
46 | + if count>0 | |
|
47 | + msg = msg + "Last on " + | |
|
48 | + format_short_time(sub.submitted_at) + ' ' + | |
|
49 | + link_to('[source]',{:action => 'get_source', :id => sub.id}) + | |
|
50 | + "<br />" | |
|
51 | + end | |
|
52 | + if sub!=nil and sub.graded_at!=nil | |
|
53 | + msg = msg + 'Graded at ' + format_short_time(sub.graded_at) + ', score: '+ | |
|
54 | + sub.points.to_s + | |
|
55 | + ' [' + sub.grader_comment + "]<br />" + | |
|
56 | + format_compiler_msg(sub) | |
|
57 | + end | |
|
58 | + msg | |
|
59 | + end | |
|
60 | + | |
|
61 | + end |
@@ -0,0 +1,7 | |||
|
1 | + class Problem < ActiveRecord::Base | |
|
2 | + | |
|
3 | + def self.find_available_problems | |
|
4 | + find(:all, :conditions => {:available => true}) | |
|
5 | + end | |
|
6 | + | |
|
7 | + end |
@@ -0,0 +1,4 | |||
|
1 | + class Role < ActiveRecord::Base | |
|
2 | + has_and_belongs_to_many :users | |
|
3 | + has_and_belongs_to_many :rights | |
|
4 | + end |
@@ -0,0 +1,64 | |||
|
1 | + class Submission < ActiveRecord::Base | |
|
2 | + | |
|
3 | + belongs_to :language | |
|
4 | + belongs_to :problem | |
|
5 | + | |
|
6 | + def self.find_by_user_and_problem(user_id, problem_id) | |
|
7 | + subcount = count(:conditions => "user_id = #{user_id} AND problem_id = #{problem_id}") | |
|
8 | + if subcount != 0 | |
|
9 | + last_sub = find(:first, | |
|
10 | + :conditions => {:user_id => user_id, | |
|
11 | + :problem_id => problem_id}, | |
|
12 | + :order => 'submitted_at DESC') | |
|
13 | + else | |
|
14 | + last_sub = nil | |
|
15 | + end | |
|
16 | + return subcount, last_sub | |
|
17 | + end | |
|
18 | + | |
|
19 | + def self.find_last_by_problem(problem_id) | |
|
20 | + # need to put in SQL command, maybe there's a better way | |
|
21 | + Submission.find_by_sql("SELECT * FROM submissions " + | |
|
22 | + "WHERE id = " + | |
|
23 | + "(SELECT MAX(id) FROM submissions AS subs " + | |
|
24 | + "WHERE subs.user_id = submissions.user_id AND " + | |
|
25 | + "problem_id = " + problem_id.to_s + " " + | |
|
26 | + "GROUP BY user_id)") | |
|
27 | + end | |
|
28 | + | |
|
29 | + def self.find_option_in_source(option, source) | |
|
30 | + i = 0 | |
|
31 | + source.each_line do |s| | |
|
32 | + if s =~ option | |
|
33 | + words = s.split | |
|
34 | + return words[1] | |
|
35 | + end | |
|
36 | + i = i + 1 | |
|
37 | + if i==10 | |
|
38 | + return nil | |
|
39 | + end | |
|
40 | + end | |
|
41 | + return nil | |
|
42 | + end | |
|
43 | + | |
|
44 | + def self.find_language_in_source(source) | |
|
45 | + langopt = find_option_in_source(/^LANG:/,source) | |
|
46 | + if language = Language.find_by_name(langopt) | |
|
47 | + return language | |
|
48 | + elsif language = Language.find_by_pretty_name(langopt) | |
|
49 | + return language | |
|
50 | + else | |
|
51 | + return nil | |
|
52 | + end | |
|
53 | + end | |
|
54 | + | |
|
55 | + def self.find_problem_in_source(source) | |
|
56 | + prob_opt = find_option_in_source(/^TASK:/,source) | |
|
57 | + if problem = Problem.find_by_name(prob_opt) | |
|
58 | + return problem | |
|
59 | + else | |
|
60 | + return nil | |
|
61 | + end | |
|
62 | + end | |
|
63 | + | |
|
64 | + end |
@@ -0,0 +1,45 | |||
|
1 | + require 'digest/sha1' | |
|
2 | + | |
|
3 | + class User < ActiveRecord::Base | |
|
4 | + | |
|
5 | + has_and_belongs_to_many :roles | |
|
6 | + | |
|
7 | + validates_presence_of :login | |
|
8 | + validates_presence_of :full_name | |
|
9 | + | |
|
10 | + validates_presence_of :password, :if => :password_required? | |
|
11 | + validates_length_of :password, :within => 4..20, :if => :password_required? | |
|
12 | + validates_confirmation_of :password, :if => :password_required? | |
|
13 | + | |
|
14 | + attr_accessor :password | |
|
15 | + | |
|
16 | + before_save :encrypt_new_password | |
|
17 | + | |
|
18 | + def self.authenticate(login, password) | |
|
19 | + user = find_by_login(login) | |
|
20 | + return user if user && user.authenticated?(password) | |
|
21 | + end | |
|
22 | + | |
|
23 | + def authenticated?(password) | |
|
24 | + hashed_password == encrypt(password,salt) | |
|
25 | + end | |
|
26 | + | |
|
27 | + def admin? | |
|
28 | + self.roles.detect {|r| r.name == 'admin' } | |
|
29 | + end | |
|
30 | + | |
|
31 | + # protected | |
|
32 | + def encrypt_new_password | |
|
33 | + return if password.blank? | |
|
34 | + self.salt = (10+rand(90)).to_s | |
|
35 | + self.hashed_password = encrypt(password,salt) | |
|
36 | + end | |
|
37 | + | |
|
38 | + def password_required? | |
|
39 | + hashed_password.blank? || !password.blank? | |
|
40 | + end | |
|
41 | + | |
|
42 | + def encrypt(string,salt) | |
|
43 | + Digest::SHA1.hexdigest(salt + string) | |
|
44 | + end | |
|
45 | + end |
@@ -0,0 +1,15 | |||
|
1 | + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | |
|
2 | + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
|
3 | + | |
|
4 | + <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |
|
5 | + <head> | |
|
6 | + <meta http-equiv="content-type" content="text/html;charset=UTF-8" /> | |
|
7 | + <title>Grader!</title> | |
|
8 | + <%= stylesheet_link_tag 'application' %> | |
|
9 | + </head> | |
|
10 | + <body> | |
|
11 | + | |
|
12 | + <%= yield %> | |
|
13 | + | |
|
14 | + </body> | |
|
15 | + </html> |
@@ -0,0 +1,17 | |||
|
1 | + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | |
|
2 | + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
|
3 | + | |
|
4 | + <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |
|
5 | + <head> | |
|
6 | + <meta http-equiv="content-type" content="text/html;charset=UTF-8" /> | |
|
7 | + <title>Problems: <%= controller.action_name %></title> | |
|
8 | + <%= stylesheet_link_tag 'scaffold' %> | |
|
9 | + </head> | |
|
10 | + <body> | |
|
11 | + | |
|
12 | + <p style="color: green"><%= flash[:notice] %></p> | |
|
13 | + | |
|
14 | + <%= yield %> | |
|
15 | + | |
|
16 | + </body> | |
|
17 | + </html> |
@@ -0,0 +1,17 | |||
|
1 | + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | |
|
2 | + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
|
3 | + | |
|
4 | + <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |
|
5 | + <head> | |
|
6 | + <meta http-equiv="content-type" content="text/html;charset=UTF-8" /> | |
|
7 | + <title>UserAdmin: <%= controller.action_name %></title> | |
|
8 | + <%= stylesheet_link_tag 'scaffold' %> | |
|
9 | + </head> | |
|
10 | + <body> | |
|
11 | + | |
|
12 | + <p style="color: green"><%= flash[:notice] %></p> | |
|
13 | + | |
|
14 | + <%= yield %> | |
|
15 | + | |
|
16 | + </body> | |
|
17 | + </html> |
@@ -0,0 +1,17 | |||
|
1 | + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | |
|
2 | + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
|
3 | + | |
|
4 | + <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |
|
5 | + <head> | |
|
6 | + <meta http-equiv="content-type" content="text/html;charset=UTF-8" /> | |
|
7 | + <title>Users: <%= controller.action_name %></title> | |
|
8 | + <%= stylesheet_link_tag 'scaffold' %> | |
|
9 | + </head> | |
|
10 | + <body> | |
|
11 | + | |
|
12 | + <p style="color: green"><%= flash[:notice] %></p> | |
|
13 | + | |
|
14 | + <%= yield %> | |
|
15 | + | |
|
16 | + </body> | |
|
17 | + </html> |
@@ -0,0 +1,5 | |||
|
1 | + <h1>Grader</h1> | |
|
2 | + | |
|
3 | + Welcome... first you need to | |
|
4 | + <%= link_to 'login', :controller => 'main', :action => 'login' %> | |
|
5 | + to see problem list. |
@@ -0,0 +1,38 | |||
|
1 | + <h1>Hello <%=h @user.full_name %></h1> | |
|
2 | + | |
|
3 | + <div class="usermenu"> | |
|
4 | + <%= user_options %> | |
|
5 | + </div> | |
|
6 | + | |
|
7 | + <hr> | |
|
8 | + | |
|
9 | + <p style="color: red"><%= flash[:notice] %></p> | |
|
10 | + | |
|
11 | + <div class="problist"> | |
|
12 | + <% i = 0 %> | |
|
13 | + <% @problems.each do |p| %> | |
|
14 | + <div class="problist-each"> | |
|
15 | + <div class="probname"> | |
|
16 | + <%= "#{i+1}: #{p.full_name} (#{p.name})" %> | |
|
17 | + </div> | |
|
18 | + <div class="subinfo"> | |
|
19 | + <%= format_submission(@prob_submissions[i][1], | |
|
20 | + @prob_submissions[i][0]) %> | |
|
21 | + </div> | |
|
22 | + </div> | |
|
23 | + <% i = i+1 %> | |
|
24 | + <% end %> | |
|
25 | + </div> | |
|
26 | + | |
|
27 | + <br /> | |
|
28 | + <hr /> | |
|
29 | + <br /> | |
|
30 | + | |
|
31 | + <% form_tag({:action => 'submit'}, :multipart => true) do %> | |
|
32 | + Problem: <%= select 'submission', 'problem_id', | |
|
33 | + [['Specified in header','-1']] + | |
|
34 | + @problems.collect {|p| [p.full_name, p.id]}, | |
|
35 | + :selected => '-1' %> | |
|
36 | + File: <%= file_field_tag 'file' %> | |
|
37 | + <%= submit_tag 'Submit' %> | |
|
38 | + <% end %> |
@@ -0,0 +1,19 | |||
|
1 | + <h1>Login</h1> | |
|
2 | + | |
|
3 | + <% if flash[:notice] %> | |
|
4 | + <hr> | |
|
5 | + <b><%= flash[:notice] %></b> | |
|
6 | + <hr> | |
|
7 | + <% end %> | |
|
8 | + | |
|
9 | + <% form_tag :controller => 'login', :action => 'login' do %> | |
|
10 | + <table> | |
|
11 | + <tr> | |
|
12 | + <td align="right">User name:</td><td><%= text_field_tag 'login' %></td> | |
|
13 | + </tr> | |
|
14 | + <tr> | |
|
15 | + <td align="right">Password:</td><td><%= password_field_tag %></td> | |
|
16 | + </tr> | |
|
17 | + </table> | |
|
18 | + <%= submit_tag 'Login' %> | |
|
19 | + <% end %> |
@@ -0,0 +1,19 | |||
|
1 | + <%= error_messages_for 'problem' %> | |
|
2 | + | |
|
3 | + <!--[form:problem]--> | |
|
4 | + <p><label for="problem_name">Name</label><br/> | |
|
5 | + <%= text_field 'problem', 'name' %></p> | |
|
6 | + | |
|
7 | + <p><label for="problem_full_name">Full name</label><br/> | |
|
8 | + <%= text_field 'problem', 'full_name' %></p> | |
|
9 | + | |
|
10 | + <p><label for="problem_full_score">Full score</label><br/> | |
|
11 | + <%= text_field 'problem', 'full_score' %></p> | |
|
12 | + | |
|
13 | + <p><label for="problem_date_added">Date added</label><br/> | |
|
14 | + <%= date_select 'problem', 'date_added' %></p> | |
|
15 | + | |
|
16 | + <p><label for="problem_available">Available</label><br/> | |
|
17 | + <select id="problem_available" name="problem[available]"><option value="false">False</option><option value="true">True</option></select></p> | |
|
18 | + <!--[eoform:problem]--> | |
|
19 | + |
@@ -0,0 +1,9 | |||
|
1 | + <h1>Editing problem</h1> | |
|
2 | + | |
|
3 | + <% form_tag :action => 'update', :id => @problem do %> | |
|
4 | + <%= render :partial => 'form' %> | |
|
5 | + <%= submit_tag 'Edit' %> | |
|
6 | + <% end %> | |
|
7 | + | |
|
8 | + <%= link_to 'Show', :action => 'show', :id => @problem %> | | |
|
9 | + <%= link_to 'Back', :action => 'list' %> |
@@ -0,0 +1,33 | |||
|
1 | + <h1>Listing problems</h1> | |
|
2 | + | |
|
3 | + <div class="usermenu"> | |
|
4 | + <%= link_to 'Main', :controller => 'main', :action => 'list' %> | |
|
5 | + </div> | |
|
6 | + | |
|
7 | + <table> | |
|
8 | + <tr> | |
|
9 | + <% for column in Problem.content_columns %> | |
|
10 | + <th><%= column.human_name %></th> | |
|
11 | + <% end %> | |
|
12 | + </tr> | |
|
13 | + | |
|
14 | + <% for problem in @problems %> | |
|
15 | + <tr> | |
|
16 | + <% for column in Problem.content_columns %> | |
|
17 | + <td><%=h problem.send(column.name) %></td> | |
|
18 | + <% end %> | |
|
19 | + <td><%= link_to '[Toggle]', :action => 'toggle_avail', :id => problem.id %></td> | |
|
20 | + <td><%= link_to '[Stat]', :action => 'stat', :id => problem.id %></td> | |
|
21 | + <td><%= link_to '[Show]', :action => 'show', :id => problem %></td> | |
|
22 | + <td><%= link_to '[Edit]', :action => 'edit', :id => problem %></td> | |
|
23 | + <td><%= link_to '[Destroy]', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :post %></td> | |
|
24 | + </tr> | |
|
25 | + <% end %> | |
|
26 | + </table> | |
|
27 | + | |
|
28 | + <%= link_to 'Previous page', { :page => @problem_pages.current.previous } if @problem_pages.current.previous %> | |
|
29 | + <%= link_to 'Next page', { :page => @problem_pages.current.next } if @problem_pages.current.next %> | |
|
30 | + | |
|
31 | + <br /> | |
|
32 | + | |
|
33 | + <%= link_to 'New problem', :action => 'new' %> |
@@ -0,0 +1,8 | |||
|
1 | + <h1>New problem</h1> | |
|
2 | + | |
|
3 | + <% form_tag :action => 'create' do %> | |
|
4 | + <%= render :partial => 'form' %> | |
|
5 | + <%= submit_tag "Create" %> | |
|
6 | + <% end %> | |
|
7 | + | |
|
8 | + <%= link_to 'Back', :action => 'list' %> |
@@ -0,0 +1,8 | |||
|
1 | + <% for column in Problem.content_columns %> | |
|
2 | + <p> | |
|
3 | + <b><%= column.human_name %>:</b> <%=h @problem.send(column.name) %> | |
|
4 | + </p> | |
|
5 | + <% end %> | |
|
6 | + | |
|
7 | + <%= link_to 'Edit', :action => 'edit', :id => @problem %> | | |
|
8 | + <%= link_to 'Back', :action => 'list' %> |
@@ -0,0 +1,23 | |||
|
1 | + <h1>Problem stat: <%= @problem.name %></h1> | |
|
2 | + | |
|
3 | + <div class="usermenu"> | |
|
4 | + <%= link_to 'Main', :controller => 'main', :action => 'list' %> | |
|
5 | + </div> | |
|
6 | + | |
|
7 | + <i>This is just a hack. Really not efficient.</i><br/><br/> | |
|
8 | + | |
|
9 | + <% if @submissions!=nil %> | |
|
10 | + <table border="1"> | |
|
11 | + <tr><td>user_id</td><td>submitted_at</td><td>points</td><td>comment</td></tr> | |
|
12 | + <% @submissions.each do |sub| %> | |
|
13 | + <tr> | |
|
14 | + <td><%= sub.user_id %></td> | |
|
15 | + <td><%= sub.submitted_at.to_s %></td> | |
|
16 | + <td><%= sub.points %></td> | |
|
17 | + <td><div style="font-family: monospace"><%= sub.grader_comment %></div></td> | |
|
18 | + </tr> | |
|
19 | + <% end %> | |
|
20 | + </table> | |
|
21 | + <% else %> | |
|
22 | + No submission | |
|
23 | + <% end %> |
@@ -0,0 +1,19 | |||
|
1 | + <%= error_messages_for 'user' %> | |
|
2 | + | |
|
3 | + <!--[form:user]--> | |
|
4 | + <p><label for="user_name">Login</label><br/> | |
|
5 | + <%= text_field 'user', 'login' %></p> | |
|
6 | + | |
|
7 | + <p><label for="user_name">Full name</label><br/> | |
|
8 | + <%= text_field 'user', 'full_name' %></p> | |
|
9 | + | |
|
10 | + <p><label for="password">Password</label><br/> | |
|
11 | + <%= password_field 'user', 'password' %></p> | |
|
12 | + | |
|
13 | + <p><label for="password_confirmation">Password (confirm)</label><br/> | |
|
14 | + <%= password_field 'user', 'password_confirmation' %></p> | |
|
15 | + | |
|
16 | + <p><label for="user_alias">Alias</label><br/> | |
|
17 | + <%= text_field 'user', 'alias' %></p> | |
|
18 | + <!--[eoform:user]--> | |
|
19 | + |
@@ -0,0 +1,9 | |||
|
1 | + <h1>Editing user</h1> | |
|
2 | + | |
|
3 | + <% form_tag :action => 'update', :id => @user do %> | |
|
4 | + <%= render :partial => 'form' %> | |
|
5 | + <%= submit_tag 'Edit' %> | |
|
6 | + <% end %> | |
|
7 | + | |
|
8 | + <%= link_to 'Show', :action => 'show', :id => @user %> | | |
|
9 | + <%= link_to 'Back', :action => 'list' %> |
@@ -0,0 +1,55 | |||
|
1 | + <h1>Listing users</h1> | |
|
2 | + | |
|
3 | + <div class="usermenu"> | |
|
4 | + <%= link_to 'Stat', :action => 'user_stat' %> | |
|
5 | + <%= link_to 'Main', :controller => 'main', :action => 'list' %> | |
|
6 | + </div> | |
|
7 | + | |
|
8 | + <div style="border: solid 1px; margin: 2px"> | |
|
9 | + <b>Quick add</b> | |
|
10 | + <% form_tag :action => 'create' do %> | |
|
11 | + <table border="0"> | |
|
12 | + <tr> | |
|
13 | + <td><label for="user_name">Login</label></td> | |
|
14 | + <td><label for="user_name">Full name</label></td> | |
|
15 | + <td><label for="user_alias">Alias</label></td> | |
|
16 | + <td><label for="password">Password</label></td> | |
|
17 | + <td><label for="password_confirmation">confirm</label></td> | |
|
18 | + </tr> | |
|
19 | + <tr> | |
|
20 | + <td><%= text_field 'user', 'login', :size => 10 %></td> | |
|
21 | + <td><%= text_field 'user', 'full_name', :size => 30 %></td> | |
|
22 | + <td><%= text_field 'user', 'alias', :size => 10 %></td> | |
|
23 | + <td><%= password_field 'user', 'password', :size => 10 %></td> | |
|
24 | + <td><%= password_field 'user', 'password_confirmation', :size => 10 %></td> | |
|
25 | + <td><%= submit_tag "Create" %></td> | |
|
26 | + </tr></table> | |
|
27 | + <% end %> | |
|
28 | + | |
|
29 | + </div> | |
|
30 | + | |
|
31 | + <table> | |
|
32 | + <tr> | |
|
33 | + <% for column in User.content_columns %> | |
|
34 | + <th><%= column.human_name %></th> | |
|
35 | + <% end %> | |
|
36 | + </tr> | |
|
37 | + | |
|
38 | + <% for user in @users %> | |
|
39 | + <tr> | |
|
40 | + <% for column in User.content_columns %> | |
|
41 | + <td><%=h user.send(column.name) %></td> | |
|
42 | + <% end %> | |
|
43 | + <td><%= link_to 'Show', :action => 'show', :id => user %></td> | |
|
44 | + <td><%= link_to 'Edit', :action => 'edit', :id => user %></td> | |
|
45 | + <td><%= link_to 'Destroy', { :action => 'destroy', :id => user }, :confirm => 'Are you sure?', :method => :post %></td> | |
|
46 | + </tr> | |
|
47 | + <% end %> | |
|
48 | + </table> | |
|
49 | + | |
|
50 | + <%= link_to 'Previous page', { :page => @user_pages.current.previous } if @user_pages.current.previous %> | |
|
51 | + <%= link_to 'Next page', { :page => @user_pages.current.next } if @user_pages.current.next %> | |
|
52 | + | |
|
53 | + <br /> | |
|
54 | + | |
|
55 | + <%= link_to 'New user', :action => 'new' %> |
@@ -0,0 +1,8 | |||
|
1 | + <h1>New user</h1> | |
|
2 | + | |
|
3 | + <% form_tag :action => 'create' do %> | |
|
4 | + <%= render :partial => 'form' %> | |
|
5 | + <%= submit_tag "Create" %> | |
|
6 | + <% end %> | |
|
7 | + | |
|
8 | + <%= link_to 'Back', :action => 'list' %> |
@@ -0,0 +1,8 | |||
|
1 | + <% for column in User.content_columns %> | |
|
2 | + <p> | |
|
3 | + <b><%= column.human_name %>:</b> <%=h @user.send(column.name) %> | |
|
4 | + </p> | |
|
5 | + <% end %> | |
|
6 | + | |
|
7 | + <%= link_to 'Edit', :action => 'edit', :id => @user %> | | |
|
8 | + <%= link_to 'Back', :action => 'list' %> |
@@ -0,0 +1,26 | |||
|
1 | + <h1>User stat</h1> | |
|
2 | + | |
|
3 | + <div class="usermenu"> | |
|
4 | + <%= link_to 'List', :action => 'list' %> | |
|
5 | + <%= link_to 'Main', :controller => 'main', :action => 'list' %> | |
|
6 | + </div> | |
|
7 | + | |
|
8 | + | |
|
9 | + <table border="1"> | |
|
10 | + <tr><td>User</td> | |
|
11 | + <% @problems.each do |p| %> | |
|
12 | + <td><%= p.name %></td> | |
|
13 | + <% end %> | |
|
14 | + <td>Total</td> | |
|
15 | + </tr> | |
|
16 | + <% @scorearray.each do |sc| %> | |
|
17 | + <tr> | |
|
18 | + <% total = 0 %> | |
|
19 | + <% sc.each do |i| %> | |
|
20 | + <td><%= i %></td> | |
|
21 | + <% total += i.to_i %> | |
|
22 | + <% end %> | |
|
23 | + <td><%= total %></td> | |
|
24 | + </tr> | |
|
25 | + <% end %> | |
|
26 | + </table> |
@@ -0,0 +1,19 | |||
|
1 | + <%= error_messages_for 'user' %> | |
|
2 | + | |
|
3 | + <!--[form:user]--> | |
|
4 | + <p><label for="user_name">Login</label><br/> | |
|
5 | + <%= text_field 'user', 'login' %></p> | |
|
6 | + | |
|
7 | + <p><label for="user_name">Full name</label><br/> | |
|
8 | + <%= text_field 'user', 'full_name' %></p> | |
|
9 | + | |
|
10 | + <p><label for="password">Password</label><br/> | |
|
11 | + <%= password_field 'user', 'password' %></p> | |
|
12 | + | |
|
13 | + <p><label for="password_confirmation">Password (confirm)</label><br/> | |
|
14 | + <%= password_field 'user', 'password_confirmation' %></p> | |
|
15 | + | |
|
16 | + <p><label for="user_alias">Alias</label><br/> | |
|
17 | + <%= text_field 'user', 'alias' %></p> | |
|
18 | + <!--[eoform:user]--> | |
|
19 | + |
@@ -0,0 +1,9 | |||
|
1 | + <h1>Editing user</h1> | |
|
2 | + | |
|
3 | + <% form_tag :action => 'update', :id => @user do %> | |
|
4 | + <%= render :partial => 'form' %> | |
|
5 | + <%= submit_tag 'Edit' %> | |
|
6 | + <% end %> | |
|
7 | + | |
|
8 | + <%= link_to 'Show', :action => 'show', :id => @user %> | | |
|
9 | + <%= link_to 'Back', :action => 'list' %> |
@@ -0,0 +1,27 | |||
|
1 | + <h1>Listing users</h1> | |
|
2 | + | |
|
3 | + <table> | |
|
4 | + <tr> | |
|
5 | + <% for column in User.content_columns %> | |
|
6 | + <th><%= column.human_name %></th> | |
|
7 | + <% end %> | |
|
8 | + </tr> | |
|
9 | + | |
|
10 | + <% for user in @users %> | |
|
11 | + <tr> | |
|
12 | + <% for column in User.content_columns %> | |
|
13 | + <td><%=h user.send(column.name) %></td> | |
|
14 | + <% end %> | |
|
15 | + <td><%= link_to 'Show', :action => 'show', :id => user %></td> | |
|
16 | + <td><%= link_to 'Edit', :action => 'edit', :id => user %></td> | |
|
17 | + <td><%= link_to 'Destroy', { :action => 'destroy', :id => user }, :confirm => 'Are you sure?', :method => :post %></td> | |
|
18 | + </tr> | |
|
19 | + <% end %> | |
|
20 | + </table> | |
|
21 | + | |
|
22 | + <%= link_to 'Previous page', { :page => @user_pages.current.previous } if @user_pages.current.previous %> | |
|
23 | + <%= link_to 'Next page', { :page => @user_pages.current.next } if @user_pages.current.next %> | |
|
24 | + | |
|
25 | + <br /> | |
|
26 | + | |
|
27 | + <%= link_to 'New user', :action => 'new' %> |
@@ -0,0 +1,8 | |||
|
1 | + <h1>New user</h1> | |
|
2 | + | |
|
3 | + <% form_tag :action => 'create' do %> | |
|
4 | + <%= render :partial => 'form' %> | |
|
5 | + <%= submit_tag "Create" %> | |
|
6 | + <% end %> | |
|
7 | + | |
|
8 | + <%= link_to 'Back', :action => 'list' %> |
@@ -0,0 +1,8 | |||
|
1 | + <% for column in User.content_columns %> | |
|
2 | + <p> | |
|
3 | + <b><%= column.human_name %>:</b> <%=h @user.send(column.name) %> | |
|
4 | + </p> | |
|
5 | + <% end %> | |
|
6 | + | |
|
7 | + <%= link_to 'Edit', :action => 'edit', :id => @user %> | | |
|
8 | + <%= link_to 'Back', :action => 'list' %> |
@@ -0,0 +1,45 | |||
|
1 | + # Don't change this file. Configuration is done in config/environment.rb and config/environments/*.rb | |
|
2 | + | |
|
3 | + unless defined?(RAILS_ROOT) | |
|
4 | + root_path = File.join(File.dirname(__FILE__), '..') | |
|
5 | + | |
|
6 | + unless RUBY_PLATFORM =~ /(:?mswin|mingw)/ | |
|
7 | + require 'pathname' | |
|
8 | + root_path = Pathname.new(root_path).cleanpath(true).to_s | |
|
9 | + end | |
|
10 | + | |
|
11 | + RAILS_ROOT = root_path | |
|
12 | + end | |
|
13 | + | |
|
14 | + unless defined?(Rails::Initializer) | |
|
15 | + if File.directory?("#{RAILS_ROOT}/vendor/rails") | |
|
16 | + require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer" | |
|
17 | + else | |
|
18 | + require 'rubygems' | |
|
19 | + | |
|
20 | + environment_without_comments = IO.readlines(File.dirname(__FILE__) + '/environment.rb').reject { |l| l =~ /^#/ }.join | |
|
21 | + environment_without_comments =~ /[^#]RAILS_GEM_VERSION = '([\d.]+)'/ | |
|
22 | + rails_gem_version = $1 | |
|
23 | + | |
|
24 | + if version = defined?(RAILS_GEM_VERSION) ? RAILS_GEM_VERSION : rails_gem_version | |
|
25 | + # Asking for 1.1.6 will give you 1.1.6.5206, if available -- makes it easier to use beta gems | |
|
26 | + rails_gem = Gem.cache.search('rails', "~>#{version}.0").sort_by { |g| g.version.version }.last | |
|
27 | + | |
|
28 | + if rails_gem | |
|
29 | + gem "rails", "=#{rails_gem.version.version}" | |
|
30 | + require rails_gem.full_gem_path + '/lib/initializer' | |
|
31 | + else | |
|
32 | + STDERR.puts %(Cannot find gem for Rails ~>#{version}.0: | |
|
33 | + Install the missing gem with 'gem install -v=#{version} rails', or | |
|
34 | + change environment.rb to define RAILS_GEM_VERSION with your desired version. | |
|
35 | + ) | |
|
36 | + exit 1 | |
|
37 | + end | |
|
38 | + else | |
|
39 | + gem "rails" | |
|
40 | + require 'initializer' | |
|
41 | + end | |
|
42 | + end | |
|
43 | + | |
|
44 | + Rails::Initializer.run(:set_load_path) | |
|
45 | + end |
@@ -0,0 +1,36 | |||
|
1 | + # MySQL (default setup). Versions 4.1 and 5.0 are recommended. | |
|
2 | + # | |
|
3 | + # Install the MySQL driver: | |
|
4 | + # gem install mysql | |
|
5 | + # On MacOS X: | |
|
6 | + # gem install mysql -- --include=/usr/local/lib | |
|
7 | + # On Windows: | |
|
8 | + # gem install mysql | |
|
9 | + # Choose the win32 build. | |
|
10 | + # Install MySQL and put its /bin directory on your path. | |
|
11 | + # | |
|
12 | + # And be sure to use new-style password hashing: | |
|
13 | + # http://dev.mysql.com/doc/refman/5.0/en/old-client.html | |
|
14 | + development: | |
|
15 | + adapter: mysql | |
|
16 | + database: ioi | |
|
17 | + username: ioi | |
|
18 | + password: ioi | |
|
19 | + host: localhost | |
|
20 | + | |
|
21 | + # Warning: The database defined as 'test' will be erased and | |
|
22 | + # re-generated from your development database when you run 'rake'. | |
|
23 | + # Do not set this db to the same as development or production. | |
|
24 | + test: | |
|
25 | + adapter: mysql | |
|
26 | + database: grader_test | |
|
27 | + username: root | |
|
28 | + password: | |
|
29 | + host: localhost | |
|
30 | + | |
|
31 | + production: | |
|
32 | + adapter: mysql | |
|
33 | + database: ioi | |
|
34 | + username: ioi | |
|
35 | + password: mioisql | |
|
36 | + host: localhost |
@@ -0,0 +1,60 | |||
|
1 | + # Be sure to restart your web server when you modify this file. | |
|
2 | + | |
|
3 | + # Uncomment below to force Rails into production mode when | |
|
4 | + # you don't control web/app server and can't set it the proper way | |
|
5 | + # ENV['RAILS_ENV'] ||= 'production' | |
|
6 | + | |
|
7 | + # Specifies gem version of Rails to use when vendor/rails is not present | |
|
8 | + RAILS_GEM_VERSION = '1.2.4' unless defined? RAILS_GEM_VERSION | |
|
9 | + | |
|
10 | + # Bootstrap the Rails environment, frameworks, and default configuration | |
|
11 | + require File.join(File.dirname(__FILE__), 'boot') | |
|
12 | + | |
|
13 | + Rails::Initializer.run do |config| | |
|
14 | + # Settings in config/environments/* take precedence over those specified here | |
|
15 | + | |
|
16 | + # Skip frameworks you're not going to use (only works if using vendor/rails) | |
|
17 | + # config.frameworks -= [ :action_web_service, :action_mailer ] | |
|
18 | + | |
|
19 | + # Only load the plugins named here, by default all plugins in vendor/plugins are loaded | |
|
20 | + # config.plugins = %W( exception_notification ssl_requirement ) | |
|
21 | + | |
|
22 | + # Add additional load paths for your own custom dirs | |
|
23 | + # config.load_paths += %W( #{RAILS_ROOT}/extras ) | |
|
24 | + | |
|
25 | + # Force all environments to use the same logger level | |
|
26 | + # (by default production uses :info, the others :debug) | |
|
27 | + # config.log_level = :debug | |
|
28 | + | |
|
29 | + # Use the database for sessions instead of the file system | |
|
30 | + # (create the session table with 'rake db:sessions:create') | |
|
31 | + config.action_controller.session_store = :active_record_store | |
|
32 | + | |
|
33 | + # Use SQL instead of Active Record's schema dumper when creating the test database. | |
|
34 | + # This is necessary if your schema can't be completely dumped by the schema dumper, | |
|
35 | + # like if you have constraints or database-specific column types | |
|
36 | + # config.active_record.schema_format = :sql | |
|
37 | + | |
|
38 | + # Activate observers that should always be running | |
|
39 | + # config.active_record.observers = :cacher, :garbage_collector | |
|
40 | + | |
|
41 | + # Make Active Record use UTC-base instead of local time | |
|
42 | + config.active_record.default_timezone = :utc | |
|
43 | + | |
|
44 | + # See Rails::Configuration for more options | |
|
45 | + end | |
|
46 | + | |
|
47 | + # Add new inflection rules using the following format | |
|
48 | + # (all these examples are active by default): | |
|
49 | + # Inflector.inflections do |inflect| | |
|
50 | + # inflect.plural /^(ox)$/i, '\1en' | |
|
51 | + # inflect.singular /^(ox)en/i, '\1' | |
|
52 | + # inflect.irregular 'person', 'people' | |
|
53 | + # inflect.uncountable %w( fish sheep ) | |
|
54 | + # end | |
|
55 | + | |
|
56 | + # Add new mime types for use in respond_to blocks: | |
|
57 | + # Mime::Type.register "text/richtext", :rtf | |
|
58 | + # Mime::Type.register "application/x-mobile", :mobile | |
|
59 | + | |
|
60 | + # Include your application configuration below |
@@ -0,0 +1,21 | |||
|
1 | + # Settings specified here will take precedence over those in config/environment.rb | |
|
2 | + | |
|
3 | + # In the development environment your application's code is reloaded on | |
|
4 | + # every request. This slows down response time but is perfect for development | |
|
5 | + # since you don't have to restart the webserver when you make code changes. | |
|
6 | + config.cache_classes = false | |
|
7 | + | |
|
8 | + # Log error messages when you accidentally call methods on nil. | |
|
9 | + config.whiny_nils = true | |
|
10 | + | |
|
11 | + # Enable the breakpoint server that script/breakpointer connects to | |
|
12 | + config.breakpoint_server = true | |
|
13 | + | |
|
14 | + # Show full error reports and disable caching | |
|
15 | + config.action_controller.consider_all_requests_local = true | |
|
16 | + config.action_controller.perform_caching = false | |
|
17 | + config.action_view.cache_template_extensions = false | |
|
18 | + config.action_view.debug_rjs = true | |
|
19 | + | |
|
20 | + # Don't care if the mailer can't send | |
|
21 | + config.action_mailer.raise_delivery_errors = false |
@@ -0,0 +1,18 | |||
|
1 | + # Settings specified here will take precedence over those in config/environment.rb | |
|
2 | + | |
|
3 | + # The production environment is meant for finished, "live" apps. | |
|
4 | + # Code is not reloaded between requests | |
|
5 | + config.cache_classes = true | |
|
6 | + | |
|
7 | + # Use a different logger for distributed setups | |
|
8 | + # config.logger = SyslogLogger.new | |
|
9 | + | |
|
10 | + # Full error reports are disabled and caching is turned on | |
|
11 | + config.action_controller.consider_all_requests_local = false | |
|
12 | + config.action_controller.perform_caching = true | |
|
13 | + | |
|
14 | + # Enable serving of images, stylesheets, and javascripts from an asset server | |
|
15 | + # config.action_controller.asset_host = "http://assets.example.com" | |
|
16 | + | |
|
17 | + # Disable delivery errors, bad email addresses will be ignored | |
|
18 | + # config.action_mailer.raise_delivery_errors = false |
@@ -0,0 +1,19 | |||
|
1 | + # Settings specified here will take precedence over those in config/environment.rb | |
|
2 | + | |
|
3 | + # The test environment is used exclusively to run your application's | |
|
4 | + # test suite. You never need to work with it otherwise. Remember that | |
|
5 | + # your test database is "scratch space" for the test suite and is wiped | |
|
6 | + # and recreated between test runs. Don't rely on the data there! | |
|
7 | + config.cache_classes = true | |
|
8 | + | |
|
9 | + # Log error messages when you accidentally call methods on nil. | |
|
10 | + config.whiny_nils = true | |
|
11 | + | |
|
12 | + # Show full error reports and disable caching | |
|
13 | + config.action_controller.consider_all_requests_local = true | |
|
14 | + config.action_controller.perform_caching = false | |
|
15 | + | |
|
16 | + # Tell ActionMailer not to deliver emails to the real world. | |
|
17 | + # The :test delivery method accumulates sent emails in the | |
|
18 | + # ActionMailer::Base.deliveries array. | |
|
19 | + config.action_mailer.delivery_method = :test No newline at end of file |
@@ -0,0 +1,23 | |||
|
1 | + ActionController::Routing::Routes.draw do |map| | |
|
2 | + # The priority is based upon order of creation: first created -> highest priority. | |
|
3 | + | |
|
4 | + # Sample of regular route: | |
|
5 | + # map.connect 'products/:id', :controller => 'catalog', :action => 'view' | |
|
6 | + # Keep in mind you can assign values other than :controller and :action | |
|
7 | + | |
|
8 | + # Sample of named route: | |
|
9 | + # map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase' | |
|
10 | + # This route can be invoked with purchase_url(:id => product.id) | |
|
11 | + | |
|
12 | + # You can have the root of your site routed by hooking up '' | |
|
13 | + # -- just remember to delete public/index.html. | |
|
14 | + # map.connect '', :controller => "welcome" | |
|
15 | + | |
|
16 | + # Allow downloading Web Service WSDL as a file with an extension | |
|
17 | + # instead of a file named 'wsdl' | |
|
18 | + map.connect ':controller/service.wsdl', :action => 'wsdl' | |
|
19 | + | |
|
20 | + # Install the default route as the lowest priority. | |
|
21 | + map.connect ':controller/:action/:id.:format' | |
|
22 | + map.connect ':controller/:action/:id' | |
|
23 | + end |
@@ -0,0 +1,17 | |||
|
1 | + class CreateUsers < ActiveRecord::Migration | |
|
2 | + def self.up | |
|
3 | + create_table :users do |t| | |
|
4 | + t.column :login, :string, :limit => 10 | |
|
5 | + t.column :full_name, :string | |
|
6 | + t.column :hashed_password, :string | |
|
7 | + t.column :salt, :string, :limit => 5 | |
|
8 | + t.column :alias, :string | |
|
9 | + end | |
|
10 | + # force unique name | |
|
11 | + add_index :users, :login, :unique => true | |
|
12 | + end | |
|
13 | + | |
|
14 | + def self.down | |
|
15 | + drop_table :users | |
|
16 | + end | |
|
17 | + end |
@@ -0,0 +1,15 | |||
|
1 | + class CreateProblems < ActiveRecord::Migration | |
|
2 | + def self.up | |
|
3 | + create_table :problems do |t| | |
|
4 | + t.column :name, :string, :limit => 30 | |
|
5 | + t.column :full_name, :string | |
|
6 | + t.column :full_score, :integer | |
|
7 | + t.column :date_added, :date | |
|
8 | + t.column :available, :boolean | |
|
9 | + end | |
|
10 | + end | |
|
11 | + | |
|
12 | + def self.down | |
|
13 | + drop_table :problems | |
|
14 | + end | |
|
15 | + end |
@@ -0,0 +1,21 | |||
|
1 | + class CreateSubmissions < ActiveRecord::Migration | |
|
2 | + def self.up | |
|
3 | + create_table :submissions do |t| | |
|
4 | + t.column :user_id, :integer | |
|
5 | + t.column :problem_id, :integer | |
|
6 | + t.column :language_id, :integer | |
|
7 | + t.column :source, :text | |
|
8 | + t.column :binary, :binary | |
|
9 | + t.column :submitted_at, :datetime | |
|
10 | + t.column :compiled_at, :datetime | |
|
11 | + t.column :compiler_message, :text | |
|
12 | + t.column :graded_at, :datetime | |
|
13 | + t.column :points, :integer | |
|
14 | + t.column :grader_comment, :text | |
|
15 | + end | |
|
16 | + end | |
|
17 | + | |
|
18 | + def self.down | |
|
19 | + drop_table :submissions | |
|
20 | + end | |
|
21 | + end |
@@ -0,0 +1,16 | |||
|
1 | + class CreateLanguages < ActiveRecord::Migration | |
|
2 | + def self.up | |
|
3 | + create_table :languages do |t| | |
|
4 | + t.column :name, :string, :limit => 10 | |
|
5 | + t.column :pretty_name, :string | |
|
6 | + end | |
|
7 | + | |
|
8 | + Language.create(:name => "c", :pretty_name => "C") | |
|
9 | + Language.create(:name => "cpp", :pretty_name => "C++") | |
|
10 | + Language.create(:name => "pas", :pretty_name => "Pascal") | |
|
11 | + end | |
|
12 | + | |
|
13 | + def self.down | |
|
14 | + drop_table :languages | |
|
15 | + end | |
|
16 | + end |
@@ -0,0 +1,9 | |||
|
1 | + class AddIndexToSubmissions < ActiveRecord::Migration | |
|
2 | + def self.up | |
|
3 | + add_index :submissions, [:user_id, :problem_id] | |
|
4 | + end | |
|
5 | + | |
|
6 | + def self.down | |
|
7 | + remove_index :submissions, :column => [:user_id, :problem_id] | |
|
8 | + end | |
|
9 | + end |
@@ -0,0 +1,19 | |||
|
1 | + class CreateRoles < ActiveRecord::Migration | |
|
2 | + def self.up | |
|
3 | + create_table :roles do |t| | |
|
4 | + t.column 'name', :string | |
|
5 | + end | |
|
6 | + | |
|
7 | + create_table :roles_users, :id => false do |t| | |
|
8 | + t.column 'role_id', :integer | |
|
9 | + t.column 'user_id', :integer | |
|
10 | + end | |
|
11 | + | |
|
12 | + add_index :roles_users, :user_id | |
|
13 | + end | |
|
14 | + | |
|
15 | + def self.down | |
|
16 | + drop_table :roles_users | |
|
17 | + drop_table :roles | |
|
18 | + end | |
|
19 | + end |
@@ -0,0 +1,21 | |||
|
1 | + class CreateRights < ActiveRecord::Migration | |
|
2 | + def self.up | |
|
3 | + create_table :rights do |t| | |
|
4 | + t.column 'name', :string | |
|
5 | + t.column 'controller', :string | |
|
6 | + t.column 'action', :string | |
|
7 | + end | |
|
8 | + | |
|
9 | + create_table :rights_roles, :id => false do |t| | |
|
10 | + t.column 'right_id', :integer | |
|
11 | + t.column 'role_id', :integer | |
|
12 | + end | |
|
13 | + | |
|
14 | + add_index :rights_roles, :role_id | |
|
15 | + end | |
|
16 | + | |
|
17 | + def self.down | |
|
18 | + drop_table :rights_roles | |
|
19 | + drop_table :rights | |
|
20 | + end | |
|
21 | + end |
@@ -0,0 +1,12 | |||
|
1 | + class CreateTasks < ActiveRecord::Migration | |
|
2 | + def self.up | |
|
3 | + create_table :tasks do |t| | |
|
4 | + t.column 'submission_id', :integer | |
|
5 | + t.column 'created_at', :datetime | |
|
6 | + end | |
|
7 | + end | |
|
8 | + | |
|
9 | + def self.down | |
|
10 | + drop_table :tasks | |
|
11 | + end | |
|
12 | + end |
@@ -0,0 +1,16 | |||
|
1 | + class AddSessions < ActiveRecord::Migration | |
|
2 | + def self.up | |
|
3 | + create_table :sessions do |t| | |
|
4 | + t.column :session_id, :string | |
|
5 | + t.column :data, :text | |
|
6 | + t.column :updated_at, :datetime | |
|
7 | + end | |
|
8 | + | |
|
9 | + add_index :sessions, :session_id | |
|
10 | + add_index :sessions, :updated_at | |
|
11 | + end | |
|
12 | + | |
|
13 | + def self.down | |
|
14 | + drop_table :sessions | |
|
15 | + end | |
|
16 | + end |
@@ -0,0 +1,38 | |||
|
1 | + class AddAdminAndRoles < ActiveRecord::Migration | |
|
2 | + def self.up | |
|
3 | + root = User.new(:login => 'root', | |
|
4 | + :full_name => 'Administrator', | |
|
5 | + :alias => 'root') | |
|
6 | + root.password = 'ioionrails'; | |
|
7 | + root.encrypt_new_password | |
|
8 | + | |
|
9 | + role = Role.create(:name => 'admin') | |
|
10 | + root.roles << role; | |
|
11 | + root.save | |
|
12 | + | |
|
13 | + user_admin_right = Right.create(:name => 'user_admin', | |
|
14 | + :controller => 'user_admin', | |
|
15 | + :action => 'all') | |
|
16 | + problem_admin_right = Right.create(:name=> 'problem_admin', | |
|
17 | + :controller => 'problems', | |
|
18 | + :action => 'all') | |
|
19 | + | |
|
20 | + role.rights << user_admin_right; | |
|
21 | + role.rights << problem_admin_right; | |
|
22 | + role.save | |
|
23 | + end | |
|
24 | + | |
|
25 | + def self.down | |
|
26 | + admin_role = Role.find_by_name('admin') | |
|
27 | + admin_role.destroy unless admin_role==nil | |
|
28 | + | |
|
29 | + admin_right = Right.find_by_name('user_admin') | |
|
30 | + admin_right.destroy unless admin_right==nil | |
|
31 | + | |
|
32 | + admin_right = Right.find_by_name('problem_admin') | |
|
33 | + admin_right.destroy unless admin_right==nil | |
|
34 | + | |
|
35 | + root = User.find_by_login('root') | |
|
36 | + root.destroy unless root==nil | |
|
37 | + end | |
|
38 | + end |
@@ -0,0 +1,15 | |||
|
1 | + class AddLanguageExt < ActiveRecord::Migration | |
|
2 | + def self.up | |
|
3 | + add_column :languages, :ext, :string, :limit => 10 | |
|
4 | + | |
|
5 | + langs = Language.find(:all) | |
|
6 | + langs.each do |l| | |
|
7 | + l.ext = l.name | |
|
8 | + l.save | |
|
9 | + end | |
|
10 | + end | |
|
11 | + | |
|
12 | + def self.down | |
|
13 | + remove_column :languages, :ext | |
|
14 | + end | |
|
15 | + end |
@@ -0,0 +1,2 | |||
|
1 | + Use this README file to introduce your application and point to useful places in the API for learning more. | |
|
2 | + Run "rake appdoc" to generate API documentation for your models and controllers. No newline at end of file |
@@ -0,0 +1,40 | |||
|
1 | + # General Apache options | |
|
2 | + AddHandler fastcgi-script .fcgi | |
|
3 | + AddHandler cgi-script .cgi | |
|
4 | + Options +FollowSymLinks +ExecCGI | |
|
5 | + | |
|
6 | + # If you don't want Rails to look in certain directories, | |
|
7 | + # use the following rewrite rules so that Apache won't rewrite certain requests | |
|
8 | + # | |
|
9 | + # Example: | |
|
10 | + # RewriteCond %{REQUEST_URI} ^/notrails.* | |
|
11 | + # RewriteRule .* - [L] | |
|
12 | + | |
|
13 | + # Redirect all requests not available on the filesystem to Rails | |
|
14 | + # By default the cgi dispatcher is used which is very slow | |
|
15 | + # | |
|
16 | + # For better performance replace the dispatcher with the fastcgi one | |
|
17 | + # | |
|
18 | + # Example: | |
|
19 | + # RewriteRule ^(.*)$ dispatch.fcgi [QSA,L] | |
|
20 | + RewriteEngine On | |
|
21 | + | |
|
22 | + # If your Rails application is accessed via an Alias directive, | |
|
23 | + # then you MUST also set the RewriteBase in this htaccess file. | |
|
24 | + # | |
|
25 | + # Example: | |
|
26 | + # Alias /myrailsapp /path/to/myrailsapp/public | |
|
27 | + # RewriteBase /myrailsapp | |
|
28 | + | |
|
29 | + RewriteRule ^$ index.html [QSA] | |
|
30 | + RewriteRule ^([^.]+)$ $1.html [QSA] | |
|
31 | + RewriteCond %{REQUEST_FILENAME} !-f | |
|
32 | + RewriteRule ^(.*)$ dispatch.cgi [QSA,L] | |
|
33 | + | |
|
34 | + # In case Rails experiences terminal errors | |
|
35 | + # Instead of displaying this message you can supply a file here which will be rendered instead | |
|
36 | + # | |
|
37 | + # Example: | |
|
38 | + # ErrorDocument 500 /500.html | |
|
39 | + | |
|
40 | + ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly" No newline at end of file |
@@ -0,0 +1,30 | |||
|
1 | + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | |
|
2 | + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
|
3 | + | |
|
4 | + <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |
|
5 | + | |
|
6 | + <head> | |
|
7 | + <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> | |
|
8 | + <title>The page you were looking for doesn't exist (404)</title> | |
|
9 | + <style type="text/css"> | |
|
10 | + body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; } | |
|
11 | + div.dialog { | |
|
12 | + width: 25em; | |
|
13 | + padding: 0 4em; | |
|
14 | + margin: 4em auto 0 auto; | |
|
15 | + border: 1px solid #ccc; | |
|
16 | + border-right-color: #999; | |
|
17 | + border-bottom-color: #999; | |
|
18 | + } | |
|
19 | + h1 { font-size: 100%; color: #f00; line-height: 1.5em; } | |
|
20 | + </style> | |
|
21 | + </head> | |
|
22 | + | |
|
23 | + <body> | |
|
24 | + <!-- This file lives in public/404.html --> | |
|
25 | + <div class="dialog"> | |
|
26 | + <h1>The page you were looking for doesn't exist.</h1> | |
|
27 | + <p>You may have mistyped the address or the page may have moved.</p> | |
|
28 | + </div> | |
|
29 | + </body> | |
|
30 | + </html> No newline at end of file |
@@ -0,0 +1,30 | |||
|
1 | + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | |
|
2 | + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
|
3 | + | |
|
4 | + <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |
|
5 | + | |
|
6 | + <head> | |
|
7 | + <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> | |
|
8 | + <title>We're sorry, but something went wrong</title> | |
|
9 | + <style type="text/css"> | |
|
10 | + body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; } | |
|
11 | + div.dialog { | |
|
12 | + width: 25em; | |
|
13 | + padding: 0 4em; | |
|
14 | + margin: 4em auto 0 auto; | |
|
15 | + border: 1px solid #ccc; | |
|
16 | + border-right-color: #999; | |
|
17 | + border-bottom-color: #999; | |
|
18 | + } | |
|
19 | + h1 { font-size: 100%; color: #f00; line-height: 1.5em; } | |
|
20 | + </style> | |
|
21 | + </head> | |
|
22 | + | |
|
23 | + <body> | |
|
24 | + <!-- This file lives in public/500.html --> | |
|
25 | + <div class="dialog"> | |
|
26 | + <h1>We're sorry, but something went wrong.</h1> | |
|
27 | + <p>We've been notified about this issue and we'll take a look at it shortly.</p> | |
|
28 | + </div> | |
|
29 | + </body> | |
|
30 | + </html> No newline at end of file |
@@ -0,0 +1,10 | |||
|
1 | + #!c:/ruby/bin/ruby | |
|
2 | + | |
|
3 | + require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT) | |
|
4 | + | |
|
5 | + # If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like: | |
|
6 | + # "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired | |
|
7 | + require "dispatcher" | |
|
8 | + | |
|
9 | + ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun) | |
|
10 | + Dispatcher.dispatch No newline at end of file |
@@ -0,0 +1,24 | |||
|
1 | + #!c:/ruby/bin/ruby | |
|
2 | + # | |
|
3 | + # You may specify the path to the FastCGI crash log (a log of unhandled | |
|
4 | + # exceptions which forced the FastCGI instance to exit, great for debugging) | |
|
5 | + # and the number of requests to process before running garbage collection. | |
|
6 | + # | |
|
7 | + # By default, the FastCGI crash log is RAILS_ROOT/log/fastcgi.crash.log | |
|
8 | + # and the GC period is nil (turned off). A reasonable number of requests | |
|
9 | + # could range from 10-100 depending on the memory footprint of your app. | |
|
10 | + # | |
|
11 | + # Example: | |
|
12 | + # # Default log path, normal GC behavior. | |
|
13 | + # RailsFCGIHandler.process! | |
|
14 | + # | |
|
15 | + # # Default log path, 50 requests between GC. | |
|
16 | + # RailsFCGIHandler.process! nil, 50 | |
|
17 | + # | |
|
18 | + # # Custom log path, normal GC behavior. | |
|
19 | + # RailsFCGIHandler.process! '/var/log/myapp_fcgi_crash.log' | |
|
20 | + # | |
|
21 | + require File.dirname(__FILE__) + "/../config/environment" | |
|
22 | + require 'fcgi_handler' | |
|
23 | + | |
|
24 | + RailsFCGIHandler.process! |
@@ -0,0 +1,10 | |||
|
1 | + #!c:/ruby/bin/ruby | |
|
2 | + | |
|
3 | + require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT) | |
|
4 | + | |
|
5 | + # If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like: | |
|
6 | + # "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired | |
|
7 | + require "dispatcher" | |
|
8 | + | |
|
9 | + ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun) | |
|
10 | + Dispatcher.dispatch No newline at end of file |
new file 100644 |
new file 100644 | |||
binary diff hidden |
@@ -0,0 +1,277 | |||
|
1 | + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | |
|
2 | + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
|
3 | + <html> | |
|
4 | + <head> | |
|
5 | + <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> | |
|
6 | + <title>Ruby on Rails: Welcome aboard</title> | |
|
7 | + <style type="text/css" media="screen"> | |
|
8 | + body { | |
|
9 | + margin: 0; | |
|
10 | + margin-bottom: 25px; | |
|
11 | + padding: 0; | |
|
12 | + background-color: #f0f0f0; | |
|
13 | + font-family: "Lucida Grande", "Bitstream Vera Sans", "Verdana"; | |
|
14 | + font-size: 13px; | |
|
15 | + color: #333; | |
|
16 | + } | |
|
17 | + | |
|
18 | + h1 { | |
|
19 | + font-size: 28px; | |
|
20 | + color: #000; | |
|
21 | + } | |
|
22 | + | |
|
23 | + a {color: #03c} | |
|
24 | + a:hover { | |
|
25 | + background-color: #03c; | |
|
26 | + color: white; | |
|
27 | + text-decoration: none; | |
|
28 | + } | |
|
29 | + | |
|
30 | + | |
|
31 | + #page { | |
|
32 | + background-color: #f0f0f0; | |
|
33 | + width: 750px; | |
|
34 | + margin: 0; | |
|
35 | + margin-left: auto; | |
|
36 | + margin-right: auto; | |
|
37 | + } | |
|
38 | + | |
|
39 | + #content { | |
|
40 | + float: left; | |
|
41 | + background-color: white; | |
|
42 | + border: 3px solid #aaa; | |
|
43 | + border-top: none; | |
|
44 | + padding: 25px; | |
|
45 | + width: 500px; | |
|
46 | + } | |
|
47 | + | |
|
48 | + #sidebar { | |
|
49 | + float: right; | |
|
50 | + width: 175px; | |
|
51 | + } | |
|
52 | + | |
|
53 | + #footer { | |
|
54 | + clear: both; | |
|
55 | + } | |
|
56 | + | |
|
57 | + | |
|
58 | + #header, #about, #getting-started { | |
|
59 | + padding-left: 75px; | |
|
60 | + padding-right: 30px; | |
|
61 | + } | |
|
62 | + | |
|
63 | + | |
|
64 | + #header { | |
|
65 | + background-image: url("images/rails.png"); | |
|
66 | + background-repeat: no-repeat; | |
|
67 | + background-position: top left; | |
|
68 | + height: 64px; | |
|
69 | + } | |
|
70 | + #header h1, #header h2 {margin: 0} | |
|
71 | + #header h2 { | |
|
72 | + color: #888; | |
|
73 | + font-weight: normal; | |
|
74 | + font-size: 16px; | |
|
75 | + } | |
|
76 | + | |
|
77 | + | |
|
78 | + #about h3 { | |
|
79 | + margin: 0; | |
|
80 | + margin-bottom: 10px; | |
|
81 | + font-size: 14px; | |
|
82 | + } | |
|
83 | + | |
|
84 | + #about-content { | |
|
85 | + background-color: #ffd; | |
|
86 | + border: 1px solid #fc0; | |
|
87 | + margin-left: -11px; | |
|
88 | + } | |
|
89 | + #about-content table { | |
|
90 | + margin-top: 10px; | |
|
91 | + margin-bottom: 10px; | |
|
92 | + font-size: 11px; | |
|
93 | + border-collapse: collapse; | |
|
94 | + } | |
|
95 | + #about-content td { | |
|
96 | + padding: 10px; | |
|
97 | + padding-top: 3px; | |
|
98 | + padding-bottom: 3px; | |
|
99 | + } | |
|
100 | + #about-content td.name {color: #555} | |
|
101 | + #about-content td.value {color: #000} | |
|
102 | + | |
|
103 | + #about-content.failure { | |
|
104 | + background-color: #fcc; | |
|
105 | + border: 1px solid #f00; | |
|
106 | + } | |
|
107 | + #about-content.failure p { | |
|
108 | + margin: 0; | |
|
109 | + padding: 10px; | |
|
110 | + } | |
|
111 | + | |
|
112 | + | |
|
113 | + #getting-started { | |
|
114 | + border-top: 1px solid #ccc; | |
|
115 | + margin-top: 25px; | |
|
116 | + padding-top: 15px; | |
|
117 | + } | |
|
118 | + #getting-started h1 { | |
|
119 | + margin: 0; | |
|
120 | + font-size: 20px; | |
|
121 | + } | |
|
122 | + #getting-started h2 { | |
|
123 | + margin: 0; | |
|
124 | + font-size: 14px; | |
|
125 | + font-weight: normal; | |
|
126 | + color: #333; | |
|
127 | + margin-bottom: 25px; | |
|
128 | + } | |
|
129 | + #getting-started ol { | |
|
130 | + margin-left: 0; | |
|
131 | + padding-left: 0; | |
|
132 | + } | |
|
133 | + #getting-started li { | |
|
134 | + font-size: 18px; | |
|
135 | + color: #888; | |
|
136 | + margin-bottom: 25px; | |
|
137 | + } | |
|
138 | + #getting-started li h2 { | |
|
139 | + margin: 0; | |
|
140 | + font-weight: normal; | |
|
141 | + font-size: 18px; | |
|
142 | + color: #333; | |
|
143 | + } | |
|
144 | + #getting-started li p { | |
|
145 | + color: #555; | |
|
146 | + font-size: 13px; | |
|
147 | + } | |
|
148 | + | |
|
149 | + | |
|
150 | + #search { | |
|
151 | + margin: 0; | |
|
152 | + padding-top: 10px; | |
|
153 | + padding-bottom: 10px; | |
|
154 | + font-size: 11px; | |
|
155 | + } | |
|
156 | + #search input { | |
|
157 | + font-size: 11px; | |
|
158 | + margin: 2px; | |
|
159 | + } | |
|
160 | + #search-text {width: 170px} | |
|
161 | + | |
|
162 | + | |
|
163 | + #sidebar ul { | |
|
164 | + margin-left: 0; | |
|
165 | + padding-left: 0; | |
|
166 | + } | |
|
167 | + #sidebar ul h3 { | |
|
168 | + margin-top: 25px; | |
|
169 | + font-size: 16px; | |
|
170 | + padding-bottom: 10px; | |
|
171 | + border-bottom: 1px solid #ccc; | |
|
172 | + } | |
|
173 | + #sidebar li { | |
|
174 | + list-style-type: none; | |
|
175 | + } | |
|
176 | + #sidebar ul.links li { | |
|
177 | + margin-bottom: 5px; | |
|
178 | + } | |
|
179 | + | |
|
180 | + </style> | |
|
181 | + <script type="text/javascript" src="javascripts/prototype.js"></script> | |
|
182 | + <script type="text/javascript" src="javascripts/effects.js"></script> | |
|
183 | + <script type="text/javascript"> | |
|
184 | + function about() { | |
|
185 | + if (Element.empty('about-content')) { | |
|
186 | + new Ajax.Updater('about-content', 'rails/info/properties', { | |
|
187 | + method: 'get', | |
|
188 | + onFailure: function() {Element.classNames('about-content').add('failure')}, | |
|
189 | + onComplete: function() {new Effect.BlindDown('about-content', {duration: 0.25})} | |
|
190 | + }); | |
|
191 | + } else { | |
|
192 | + new Effect[Element.visible('about-content') ? | |
|
193 | + 'BlindUp' : 'BlindDown']('about-content', {duration: 0.25}); | |
|
194 | + } | |
|
195 | + } | |
|
196 | + | |
|
197 | + window.onload = function() { | |
|
198 | + $('search-text').value = ''; | |
|
199 | + $('search').onsubmit = function() { | |
|
200 | + $('search-text').value = 'site:rubyonrails.org ' + $F('search-text'); | |
|
201 | + } | |
|
202 | + } | |
|
203 | + </script> | |
|
204 | + </head> | |
|
205 | + <body> | |
|
206 | + <div id="page"> | |
|
207 | + <div id="sidebar"> | |
|
208 | + <ul id="sidebar-items"> | |
|
209 | + <li> | |
|
210 | + <form id="search" action="http://www.google.com/search" method="get"> | |
|
211 | + <input type="hidden" name="hl" value="en" /> | |
|
212 | + <input type="text" id="search-text" name="q" value="site:rubyonrails.org " /> | |
|
213 | + <input type="submit" value="Search" /> the Rails site | |
|
214 | + </form> | |
|
215 | + </li> | |
|
216 | + | |
|
217 | + <li> | |
|
218 | + <h3>Join the community</h3> | |
|
219 | + <ul class="links"> | |
|
220 | + <li><a href="http://www.rubyonrails.org/">Ruby on Rails</a></li> | |
|
221 | + <li><a href="http://weblog.rubyonrails.org/">Official weblog</a></li> | |
|
222 | + <li><a href="http://lists.rubyonrails.org/">Mailing lists</a></li> | |
|
223 | + <li><a href="http://wiki.rubyonrails.org/rails/pages/IRC">IRC channel</a></li> | |
|
224 | + <li><a href="http://wiki.rubyonrails.org/">Wiki</a></li> | |
|
225 | + <li><a href="http://dev.rubyonrails.org/">Bug tracker</a></li> | |
|
226 | + </ul> | |
|
227 | + </li> | |
|
228 | + | |
|
229 | + <li> | |
|
230 | + <h3>Browse the documentation</h3> | |
|
231 | + <ul class="links"> | |
|
232 | + <li><a href="http://api.rubyonrails.org/">Rails API</a></li> | |
|
233 | + <li><a href="http://stdlib.rubyonrails.org/">Ruby standard library</a></li> | |
|
234 | + <li><a href="http://corelib.rubyonrails.org/">Ruby core</a></li> | |
|
235 | + </ul> | |
|
236 | + </li> | |
|
237 | + </ul> | |
|
238 | + </div> | |
|
239 | + | |
|
240 | + <div id="content"> | |
|
241 | + <div id="header"> | |
|
242 | + <h1>Welcome aboard</h1> | |
|
243 | + <h2>You’re riding the Rails!</h2> | |
|
244 | + </div> | |
|
245 | + | |
|
246 | + <div id="about"> | |
|
247 | + <h3><a href="rails/info/properties" onclick="about(); return false">About your application’s environment</a></h3> | |
|
248 | + <div id="about-content" style="display: none"></div> | |
|
249 | + </div> | |
|
250 | + | |
|
251 | + <div id="getting-started"> | |
|
252 | + <h1>Getting started</h1> | |
|
253 | + <h2>Here’s how to get rolling:</h2> | |
|
254 | + | |
|
255 | + <ol> | |
|
256 | + <li> | |
|
257 | + <h2>Create your databases and edit <tt>config/database.yml</tt></h2> | |
|
258 | + <p>Rails needs to know your login and password.</p> | |
|
259 | + </li> | |
|
260 | + | |
|
261 | + <li> | |
|
262 | + <h2>Use <tt>script/generate</tt> to create your models and controllers</h2> | |
|
263 | + <p>To see all available options, run it without parameters.</p> | |
|
264 | + </li> | |
|
265 | + | |
|
266 | + <li> | |
|
267 | + <h2>Set up a default route and remove or rename this file</h2> | |
|
268 | + <p>Routes are setup in config/routes.rb.</p> | |
|
269 | + </li> | |
|
270 | + </ol> | |
|
271 | + </div> | |
|
272 | + </div> | |
|
273 | + | |
|
274 | + <div id="footer"> </div> | |
|
275 | + </div> | |
|
276 | + </body> | |
|
277 | + </html> No newline at end of file |
@@ -0,0 +1,2 | |||
|
1 | + // Place your application-specific JavaScript functions and classes here | |
|
2 | + // This file is automatically included by javascript_include_tag :defaults |
This diff has been collapsed as it changes many lines, (833 lines changed) Show them Hide them | |||
@@ -0,0 +1,833 | |||
|
1 | + // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) | |
|
2 | + // (c) 2005, 2006 Ivan Krstic (http://blogs.law.harvard.edu/ivan) | |
|
3 | + // (c) 2005, 2006 Jon Tirsen (http://www.tirsen.com) | |
|
4 | + // Contributors: | |
|
5 | + // Richard Livsey | |
|
6 | + // Rahul Bhargava | |
|
7 | + // Rob Wills | |
|
8 | + // | |
|
9 | + // script.aculo.us is freely distributable under the terms of an MIT-style license. | |
|
10 | + // For details, see the script.aculo.us web site: http://script.aculo.us/ | |
|
11 | + | |
|
12 | + // Autocompleter.Base handles all the autocompletion functionality | |
|
13 | + // that's independent of the data source for autocompletion. This | |
|
14 | + // includes drawing the autocompletion menu, observing keyboard | |
|
15 | + // and mouse events, and similar. | |
|
16 | + // | |
|
17 | + // Specific autocompleters need to provide, at the very least, | |
|
18 | + // a getUpdatedChoices function that will be invoked every time | |
|
19 | + // the text inside the monitored textbox changes. This method | |
|
20 | + // should get the text for which to provide autocompletion by | |
|
21 | + // invoking this.getToken(), NOT by directly accessing | |
|
22 | + // this.element.value. This is to allow incremental tokenized | |
|
23 | + // autocompletion. Specific auto-completion logic (AJAX, etc) | |
|
24 | + // belongs in getUpdatedChoices. | |
|
25 | + // | |
|
26 | + // Tokenized incremental autocompletion is enabled automatically | |
|
27 | + // when an autocompleter is instantiated with the 'tokens' option | |
|
28 | + // in the options parameter, e.g.: | |
|
29 | + // new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' }); | |
|
30 | + // will incrementally autocomplete with a comma as the token. | |
|
31 | + // Additionally, ',' in the above example can be replaced with | |
|
32 | + // a token array, e.g. { tokens: [',', '\n'] } which | |
|
33 | + // enables autocompletion on multiple tokens. This is most | |
|
34 | + // useful when one of the tokens is \n (a newline), as it | |
|
35 | + // allows smart autocompletion after linebreaks. | |
|
36 | + | |
|
37 | + if(typeof Effect == 'undefined') | |
|
38 | + throw("controls.js requires including script.aculo.us' effects.js library"); | |
|
39 | + | |
|
40 | + var Autocompleter = {} | |
|
41 | + Autocompleter.Base = function() {}; | |
|
42 | + Autocompleter.Base.prototype = { | |
|
43 | + baseInitialize: function(element, update, options) { | |
|
44 | + this.element = $(element); | |
|
45 | + this.update = $(update); | |
|
46 | + this.hasFocus = false; | |
|
47 | + this.changed = false; | |
|
48 | + this.active = false; | |
|
49 | + this.index = 0; | |
|
50 | + this.entryCount = 0; | |
|
51 | + | |
|
52 | + if(this.setOptions) | |
|
53 | + this.setOptions(options); | |
|
54 | + else | |
|
55 | + this.options = options || {}; | |
|
56 | + | |
|
57 | + this.options.paramName = this.options.paramName || this.element.name; | |
|
58 | + this.options.tokens = this.options.tokens || []; | |
|
59 | + this.options.frequency = this.options.frequency || 0.4; | |
|
60 | + this.options.minChars = this.options.minChars || 1; | |
|
61 | + this.options.onShow = this.options.onShow || | |
|
62 | + function(element, update){ | |
|
63 | + if(!update.style.position || update.style.position=='absolute') { | |
|
64 | + update.style.position = 'absolute'; | |
|
65 | + Position.clone(element, update, { | |
|
66 | + setHeight: false, | |
|
67 | + offsetTop: element.offsetHeight | |
|
68 | + }); | |
|
69 | + } | |
|
70 | + Effect.Appear(update,{duration:0.15}); | |
|
71 | + }; | |
|
72 | + this.options.onHide = this.options.onHide || | |
|
73 | + function(element, update){ new Effect.Fade(update,{duration:0.15}) }; | |
|
74 | + | |
|
75 | + if(typeof(this.options.tokens) == 'string') | |
|
76 | + this.options.tokens = new Array(this.options.tokens); | |
|
77 | + | |
|
78 | + this.observer = null; | |
|
79 | + | |
|
80 | + this.element.setAttribute('autocomplete','off'); | |
|
81 | + | |
|
82 | + Element.hide(this.update); | |
|
83 | + | |
|
84 | + Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this)); | |
|
85 | + Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this)); | |
|
86 | + }, | |
|
87 | + | |
|
88 | + show: function() { | |
|
89 | + if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update); | |
|
90 | + if(!this.iefix && | |
|
91 | + (navigator.appVersion.indexOf('MSIE')>0) && | |
|
92 | + (navigator.userAgent.indexOf('Opera')<0) && | |
|
93 | + (Element.getStyle(this.update, 'position')=='absolute')) { | |
|
94 | + new Insertion.After(this.update, | |
|
95 | + '<iframe id="' + this.update.id + '_iefix" '+ | |
|
96 | + 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' + | |
|
97 | + 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>'); | |
|
98 | + this.iefix = $(this.update.id+'_iefix'); | |
|
99 | + } | |
|
100 | + if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50); | |
|
101 | + }, | |
|
102 | + | |
|
103 | + fixIEOverlapping: function() { | |
|
104 | + Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)}); | |
|
105 | + this.iefix.style.zIndex = 1; | |
|
106 | + this.update.style.zIndex = 2; | |
|
107 | + Element.show(this.iefix); | |
|
108 | + }, | |
|
109 | + | |
|
110 | + hide: function() { | |
|
111 | + this.stopIndicator(); | |
|
112 | + if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update); | |
|
113 | + if(this.iefix) Element.hide(this.iefix); | |
|
114 | + }, | |
|
115 | + | |
|
116 | + startIndicator: function() { | |
|
117 | + if(this.options.indicator) Element.show(this.options.indicator); | |
|
118 | + }, | |
|
119 | + | |
|
120 | + stopIndicator: function() { | |
|
121 | + if(this.options.indicator) Element.hide(this.options.indicator); | |
|
122 | + }, | |
|
123 | + | |
|
124 | + onKeyPress: function(event) { | |
|
125 | + if(this.active) | |
|
126 | + switch(event.keyCode) { | |
|
127 | + case Event.KEY_TAB: | |
|
128 | + case Event.KEY_RETURN: | |
|
129 | + this.selectEntry(); | |
|
130 | + Event.stop(event); | |
|
131 | + case Event.KEY_ESC: | |
|
132 | + this.hide(); | |
|
133 | + this.active = false; | |
|
134 | + Event.stop(event); | |
|
135 | + return; | |
|
136 | + case Event.KEY_LEFT: | |
|
137 | + case Event.KEY_RIGHT: | |
|
138 | + return; | |
|
139 | + case Event.KEY_UP: | |
|
140 | + this.markPrevious(); | |
|
141 | + this.render(); | |
|
142 | + if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); | |
|
143 | + return; | |
|
144 | + case Event.KEY_DOWN: | |
|
145 | + this.markNext(); | |
|
146 | + this.render(); | |
|
147 | + if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); | |
|
148 | + return; | |
|
149 | + } | |
|
150 | + else | |
|
151 | + if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || | |
|
152 | + (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return; | |
|
153 | + | |
|
154 | + this.changed = true; | |
|
155 | + this.hasFocus = true; | |
|
156 | + | |
|
157 | + if(this.observer) clearTimeout(this.observer); | |
|
158 | + this.observer = | |
|
159 | + setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); | |
|
160 | + }, | |
|
161 | + | |
|
162 | + activate: function() { | |
|
163 | + this.changed = false; | |
|
164 | + this.hasFocus = true; | |
|
165 | + this.getUpdatedChoices(); | |
|
166 | + }, | |
|
167 | + | |
|
168 | + onHover: function(event) { | |
|
169 | + var element = Event.findElement(event, 'LI'); | |
|
170 | + if(this.index != element.autocompleteIndex) | |
|
171 | + { | |
|
172 | + this.index = element.autocompleteIndex; | |
|
173 | + this.render(); | |
|
174 | + } | |
|
175 | + Event.stop(event); | |
|
176 | + }, | |
|
177 | + | |
|
178 | + onClick: function(event) { | |
|
179 | + var element = Event.findElement(event, 'LI'); | |
|
180 | + this.index = element.autocompleteIndex; | |
|
181 | + this.selectEntry(); | |
|
182 | + this.hide(); | |
|
183 | + }, | |
|
184 | + | |
|
185 | + onBlur: function(event) { | |
|
186 | + // needed to make click events working | |
|
187 | + setTimeout(this.hide.bind(this), 250); | |
|
188 | + this.hasFocus = false; | |
|
189 | + this.active = false; | |
|
190 | + }, | |
|
191 | + | |
|
192 | + render: function() { | |
|
193 | + if(this.entryCount > 0) { | |
|
194 | + for (var i = 0; i < this.entryCount; i++) | |
|
195 | + this.index==i ? | |
|
196 | + Element.addClassName(this.getEntry(i),"selected") : | |
|
197 | + Element.removeClassName(this.getEntry(i),"selected"); | |
|
198 | + | |
|
199 | + if(this.hasFocus) { | |
|
200 | + this.show(); | |
|
201 | + this.active = true; | |
|
202 | + } | |
|
203 | + } else { | |
|
204 | + this.active = false; | |
|
205 | + this.hide(); | |
|
206 | + } | |
|
207 | + }, | |
|
208 | + | |
|
209 | + markPrevious: function() { | |
|
210 | + if(this.index > 0) this.index-- | |
|
211 | + else this.index = this.entryCount-1; | |
|
212 | + this.getEntry(this.index).scrollIntoView(true); | |
|
213 | + }, | |
|
214 | + | |
|
215 | + markNext: function() { | |
|
216 | + if(this.index < this.entryCount-1) this.index++ | |
|
217 | + else this.index = 0; | |
|
218 | + this.getEntry(this.index).scrollIntoView(false); | |
|
219 | + }, | |
|
220 | + | |
|
221 | + getEntry: function(index) { | |
|
222 | + return this.update.firstChild.childNodes[index]; | |
|
223 | + }, | |
|
224 | + | |
|
225 | + getCurrentEntry: function() { | |
|
226 | + return this.getEntry(this.index); | |
|
227 | + }, | |
|
228 | + | |
|
229 | + selectEntry: function() { | |
|
230 | + this.active = false; | |
|
231 | + this.updateElement(this.getCurrentEntry()); | |
|
232 | + }, | |
|
233 | + | |
|
234 | + updateElement: function(selectedElement) { | |
|
235 | + if (this.options.updateElement) { | |
|
236 | + this.options.updateElement(selectedElement); | |
|
237 | + return; | |
|
238 | + } | |
|
239 | + var value = ''; | |
|
240 | + if (this.options.select) { | |
|
241 | + var nodes = document.getElementsByClassName(this.options.select, selectedElement) || []; | |
|
242 | + if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select); | |
|
243 | + } else | |
|
244 | + value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); | |
|
245 | + | |
|
246 | + var lastTokenPos = this.findLastToken(); | |
|
247 | + if (lastTokenPos != -1) { | |
|
248 | + var newValue = this.element.value.substr(0, lastTokenPos + 1); | |
|
249 | + var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/); | |
|
250 | + if (whitespace) | |
|
251 | + newValue += whitespace[0]; | |
|
252 | + this.element.value = newValue + value; | |
|
253 | + } else { | |
|
254 | + this.element.value = value; | |
|
255 | + } | |
|
256 | + this.element.focus(); | |
|
257 | + | |
|
258 | + if (this.options.afterUpdateElement) | |
|
259 | + this.options.afterUpdateElement(this.element, selectedElement); | |
|
260 | + }, | |
|
261 | + | |
|
262 | + updateChoices: function(choices) { | |
|
263 | + if(!this.changed && this.hasFocus) { | |
|
264 | + this.update.innerHTML = choices; | |
|
265 | + Element.cleanWhitespace(this.update); | |
|
266 | + Element.cleanWhitespace(this.update.down()); | |
|
267 | + | |
|
268 | + if(this.update.firstChild && this.update.down().childNodes) { | |
|
269 | + this.entryCount = | |
|
270 | + this.update.down().childNodes.length; | |
|
271 | + for (var i = 0; i < this.entryCount; i++) { | |
|
272 | + var entry = this.getEntry(i); | |
|
273 | + entry.autocompleteIndex = i; | |
|
274 | + this.addObservers(entry); | |
|
275 | + } | |
|
276 | + } else { | |
|
277 | + this.entryCount = 0; | |
|
278 | + } | |
|
279 | + | |
|
280 | + this.stopIndicator(); | |
|
281 | + this.index = 0; | |
|
282 | + | |
|
283 | + if(this.entryCount==1 && this.options.autoSelect) { | |
|
284 | + this.selectEntry(); | |
|
285 | + this.hide(); | |
|
286 | + } else { | |
|
287 | + this.render(); | |
|
288 | + } | |
|
289 | + } | |
|
290 | + }, | |
|
291 | + | |
|
292 | + addObservers: function(element) { | |
|
293 | + Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this)); | |
|
294 | + Event.observe(element, "click", this.onClick.bindAsEventListener(this)); | |
|
295 | + }, | |
|
296 | + | |
|
297 | + onObserverEvent: function() { | |
|
298 | + this.changed = false; | |
|
299 | + if(this.getToken().length>=this.options.minChars) { | |
|
300 | + this.startIndicator(); | |
|
301 | + this.getUpdatedChoices(); | |
|
302 | + } else { | |
|
303 | + this.active = false; | |
|
304 | + this.hide(); | |
|
305 | + } | |
|
306 | + }, | |
|
307 | + | |
|
308 | + getToken: function() { | |
|
309 | + var tokenPos = this.findLastToken(); | |
|
310 | + if (tokenPos != -1) | |
|
311 | + var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,''); | |
|
312 | + else | |
|
313 | + var ret = this.element.value; | |
|
314 | + | |
|
315 | + return /\n/.test(ret) ? '' : ret; | |
|
316 | + }, | |
|
317 | + | |
|
318 | + findLastToken: function() { | |
|
319 | + var lastTokenPos = -1; | |
|
320 | + | |
|
321 | + for (var i=0; i<this.options.tokens.length; i++) { | |
|
322 | + var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]); | |
|
323 | + if (thisTokenPos > lastTokenPos) | |
|
324 | + lastTokenPos = thisTokenPos; | |
|
325 | + } | |
|
326 | + return lastTokenPos; | |
|
327 | + } | |
|
328 | + } | |
|
329 | + | |
|
330 | + Ajax.Autocompleter = Class.create(); | |
|
331 | + Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), { | |
|
332 | + initialize: function(element, update, url, options) { | |
|
333 | + this.baseInitialize(element, update, options); | |
|
334 | + this.options.asynchronous = true; | |
|
335 | + this.options.onComplete = this.onComplete.bind(this); | |
|
336 | + this.options.defaultParams = this.options.parameters || null; | |
|
337 | + this.url = url; | |
|
338 | + }, | |
|
339 | + | |
|
340 | + getUpdatedChoices: function() { | |
|
341 | + entry = encodeURIComponent(this.options.paramName) + '=' + | |
|
342 | + encodeURIComponent(this.getToken()); | |
|
343 | + | |
|
344 | + this.options.parameters = this.options.callback ? | |
|
345 | + this.options.callback(this.element, entry) : entry; | |
|
346 | + | |
|
347 | + if(this.options.defaultParams) | |
|
348 | + this.options.parameters += '&' + this.options.defaultParams; | |
|
349 | + | |
|
350 | + new Ajax.Request(this.url, this.options); | |
|
351 | + }, | |
|
352 | + | |
|
353 | + onComplete: function(request) { | |
|
354 | + this.updateChoices(request.responseText); | |
|
355 | + } | |
|
356 | + | |
|
357 | + }); | |
|
358 | + | |
|
359 | + // The local array autocompleter. Used when you'd prefer to | |
|
360 | + // inject an array of autocompletion options into the page, rather | |
|
361 | + // than sending out Ajax queries, which can be quite slow sometimes. | |
|
362 | + // | |
|
363 | + // The constructor takes four parameters. The first two are, as usual, | |
|
364 | + // the id of the monitored textbox, and id of the autocompletion menu. | |
|
365 | + // The third is the array you want to autocomplete from, and the fourth | |
|
366 | + // is the options block. | |
|
367 | + // | |
|
368 | + // Extra local autocompletion options: | |
|
369 | + // - choices - How many autocompletion choices to offer | |
|
370 | + // | |
|
371 | + // - partialSearch - If false, the autocompleter will match entered | |
|
372 | + // text only at the beginning of strings in the | |
|
373 | + // autocomplete array. Defaults to true, which will | |
|
374 | + // match text at the beginning of any *word* in the | |
|
375 | + // strings in the autocomplete array. If you want to | |
|
376 | + // search anywhere in the string, additionally set | |
|
377 | + // the option fullSearch to true (default: off). | |
|
378 | + // | |
|
379 | + // - fullSsearch - Search anywhere in autocomplete array strings. | |
|
380 | + // | |
|
381 | + // - partialChars - How many characters to enter before triggering | |
|
382 | + // a partial match (unlike minChars, which defines | |
|
383 | + // how many characters are required to do any match | |
|
384 | + // at all). Defaults to 2. | |
|
385 | + // | |
|
386 | + // - ignoreCase - Whether to ignore case when autocompleting. | |
|
387 | + // Defaults to true. | |
|
388 | + // | |
|
389 | + // It's possible to pass in a custom function as the 'selector' | |
|
390 | + // option, if you prefer to write your own autocompletion logic. | |
|
391 | + // In that case, the other options above will not apply unless | |
|
392 | + // you support them. | |
|
393 | + | |
|
394 | + Autocompleter.Local = Class.create(); | |
|
395 | + Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), { | |
|
396 | + initialize: function(element, update, array, options) { | |
|
397 | + this.baseInitialize(element, update, options); | |
|
398 | + this.options.array = array; | |
|
399 | + }, | |
|
400 | + | |
|
401 | + getUpdatedChoices: function() { | |
|
402 | + this.updateChoices(this.options.selector(this)); | |
|
403 | + }, | |
|
404 | + | |
|
405 | + setOptions: function(options) { | |
|
406 | + this.options = Object.extend({ | |
|
407 | + choices: 10, | |
|
408 | + partialSearch: true, | |
|
409 | + partialChars: 2, | |
|
410 | + ignoreCase: true, | |
|
411 | + fullSearch: false, | |
|
412 | + selector: function(instance) { | |
|
413 | + var ret = []; // Beginning matches | |
|
414 | + var partial = []; // Inside matches | |
|
415 | + var entry = instance.getToken(); | |
|
416 | + var count = 0; | |
|
417 | + | |
|
418 | + for (var i = 0; i < instance.options.array.length && | |
|
419 | + ret.length < instance.options.choices ; i++) { | |
|
420 | + | |
|
421 | + var elem = instance.options.array[i]; | |
|
422 | + var foundPos = instance.options.ignoreCase ? | |
|
423 | + elem.toLowerCase().indexOf(entry.toLowerCase()) : | |
|
424 | + elem.indexOf(entry); | |
|
425 | + | |
|
426 | + while (foundPos != -1) { | |
|
427 | + if (foundPos == 0 && elem.length != entry.length) { | |
|
428 | + ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + | |
|
429 | + elem.substr(entry.length) + "</li>"); | |
|
430 | + break; | |
|
431 | + } else if (entry.length >= instance.options.partialChars && | |
|
432 | + instance.options.partialSearch && foundPos != -1) { | |
|
433 | + if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) { | |
|
434 | + partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" + | |
|
435 | + elem.substr(foundPos, entry.length) + "</strong>" + elem.substr( | |
|
436 | + foundPos + entry.length) + "</li>"); | |
|
437 | + break; | |
|
438 | + } | |
|
439 | + } | |
|
440 | + | |
|
441 | + foundPos = instance.options.ignoreCase ? | |
|
442 | + elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : | |
|
443 | + elem.indexOf(entry, foundPos + 1); | |
|
444 | + | |
|
445 | + } | |
|
446 | + } | |
|
447 | + if (partial.length) | |
|
448 | + ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)) | |
|
449 | + return "<ul>" + ret.join('') + "</ul>"; | |
|
450 | + } | |
|
451 | + }, options || {}); | |
|
452 | + } | |
|
453 | + }); | |
|
454 | + | |
|
455 | + // AJAX in-place editor | |
|
456 | + // | |
|
457 | + // see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor | |
|
458 | + | |
|
459 | + // Use this if you notice weird scrolling problems on some browsers, | |
|
460 | + // the DOM might be a bit confused when this gets called so do this | |
|
461 | + // waits 1 ms (with setTimeout) until it does the activation | |
|
462 | + Field.scrollFreeActivate = function(field) { | |
|
463 | + setTimeout(function() { | |
|
464 | + Field.activate(field); | |
|
465 | + }, 1); | |
|
466 | + } | |
|
467 | + | |
|
468 | + Ajax.InPlaceEditor = Class.create(); | |
|
469 | + Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99"; | |
|
470 | + Ajax.InPlaceEditor.prototype = { | |
|
471 | + initialize: function(element, url, options) { | |
|
472 | + this.url = url; | |
|
473 | + this.element = $(element); | |
|
474 | + | |
|
475 | + this.options = Object.extend({ | |
|
476 | + paramName: "value", | |
|
477 | + okButton: true, | |
|
478 | + okText: "ok", | |
|
479 | + cancelLink: true, | |
|
480 | + cancelText: "cancel", | |
|
481 | + savingText: "Saving...", | |
|
482 | + clickToEditText: "Click to edit", | |
|
483 | + okText: "ok", | |
|
484 | + rows: 1, | |
|
485 | + onComplete: function(transport, element) { | |
|
486 | + new Effect.Highlight(element, {startcolor: this.options.highlightcolor}); | |
|
487 | + }, | |
|
488 | + onFailure: function(transport) { | |
|
489 | + alert("Error communicating with the server: " + transport.responseText.stripTags()); | |
|
490 | + }, | |
|
491 | + callback: function(form) { | |
|
492 | + return Form.serialize(form); | |
|
493 | + }, | |
|
494 | + handleLineBreaks: true, | |
|
495 | + loadingText: 'Loading...', | |
|
496 | + savingClassName: 'inplaceeditor-saving', | |
|
497 | + loadingClassName: 'inplaceeditor-loading', | |
|
498 | + formClassName: 'inplaceeditor-form', | |
|
499 | + highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor, | |
|
500 | + highlightendcolor: "#FFFFFF", | |
|
501 | + externalControl: null, | |
|
502 | + submitOnBlur: false, | |
|
503 | + ajaxOptions: {}, | |
|
504 | + evalScripts: false | |
|
505 | + }, options || {}); | |
|
506 | + | |
|
507 | + if(!this.options.formId && this.element.id) { | |
|
508 | + this.options.formId = this.element.id + "-inplaceeditor"; | |
|
509 | + if ($(this.options.formId)) { | |
|
510 | + // there's already a form with that name, don't specify an id | |
|
511 | + this.options.formId = null; | |
|
512 | + } | |
|
513 | + } | |
|
514 | + | |
|
515 | + if (this.options.externalControl) { | |
|
516 | + this.options.externalControl = $(this.options.externalControl); | |
|
517 | + } | |
|
518 | + | |
|
519 | + this.originalBackground = Element.getStyle(this.element, 'background-color'); | |
|
520 | + if (!this.originalBackground) { | |
|
521 | + this.originalBackground = "transparent"; | |
|
522 | + } | |
|
523 | + | |
|
524 | + this.element.title = this.options.clickToEditText; | |
|
525 | + | |
|
526 | + this.onclickListener = this.enterEditMode.bindAsEventListener(this); | |
|
527 | + this.mouseoverListener = this.enterHover.bindAsEventListener(this); | |
|
528 | + this.mouseoutListener = this.leaveHover.bindAsEventListener(this); | |
|
529 | + Event.observe(this.element, 'click', this.onclickListener); | |
|
530 | + Event.observe(this.element, 'mouseover', this.mouseoverListener); | |
|
531 | + Event.observe(this.element, 'mouseout', this.mouseoutListener); | |
|
532 | + if (this.options.externalControl) { | |
|
533 | + Event.observe(this.options.externalControl, 'click', this.onclickListener); | |
|
534 | + Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener); | |
|
535 | + Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener); | |
|
536 | + } | |
|
537 | + }, | |
|
538 | + enterEditMode: function(evt) { | |
|
539 | + if (this.saving) return; | |
|
540 | + if (this.editing) return; | |
|
541 | + this.editing = true; | |
|
542 | + this.onEnterEditMode(); | |
|
543 | + if (this.options.externalControl) { | |
|
544 | + Element.hide(this.options.externalControl); | |
|
545 | + } | |
|
546 | + Element.hide(this.element); | |
|
547 | + this.createForm(); | |
|
548 | + this.element.parentNode.insertBefore(this.form, this.element); | |
|
549 | + if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField); | |
|
550 | + // stop the event to avoid a page refresh in Safari | |
|
551 | + if (evt) { | |
|
552 | + Event.stop(evt); | |
|
553 | + } | |
|
554 | + return false; | |
|
555 | + }, | |
|
556 | + createForm: function() { | |
|
557 | + this.form = document.createElement("form"); | |
|
558 | + this.form.id = this.options.formId; | |
|
559 | + Element.addClassName(this.form, this.options.formClassName) | |
|
560 | + this.form.onsubmit = this.onSubmit.bind(this); | |
|
561 | + | |
|
562 | + this.createEditField(); | |
|
563 | + | |
|
564 | + if (this.options.textarea) { | |
|
565 | + var br = document.createElement("br"); | |
|
566 | + this.form.appendChild(br); | |
|
567 | + } | |
|
568 | + | |
|
569 | + if (this.options.okButton) { | |
|
570 | + okButton = document.createElement("input"); | |
|
571 | + okButton.type = "submit"; | |
|
572 | + okButton.value = this.options.okText; | |
|
573 | + okButton.className = 'editor_ok_button'; | |
|
574 | + this.form.appendChild(okButton); | |
|
575 | + } | |
|
576 | + | |
|
577 | + if (this.options.cancelLink) { | |
|
578 | + cancelLink = document.createElement("a"); | |
|
579 | + cancelLink.href = "#"; | |
|
580 | + cancelLink.appendChild(document.createTextNode(this.options.cancelText)); | |
|
581 | + cancelLink.onclick = this.onclickCancel.bind(this); | |
|
582 | + cancelLink.className = 'editor_cancel'; | |
|
583 | + this.form.appendChild(cancelLink); | |
|
584 | + } | |
|
585 | + }, | |
|
586 | + hasHTMLLineBreaks: function(string) { | |
|
587 | + if (!this.options.handleLineBreaks) return false; | |
|
588 | + return string.match(/<br/i) || string.match(/<p>/i); | |
|
589 | + }, | |
|
590 | + convertHTMLLineBreaks: function(string) { | |
|
591 | + return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, ""); | |
|
592 | + }, | |
|
593 | + createEditField: function() { | |
|
594 | + var text; | |
|
595 | + if(this.options.loadTextURL) { | |
|
596 | + text = this.options.loadingText; | |
|
597 | + } else { | |
|
598 | + text = this.getText(); | |
|
599 | + } | |
|
600 | + | |
|
601 | + var obj = this; | |
|
602 | + | |
|
603 | + if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) { | |
|
604 | + this.options.textarea = false; | |
|
605 | + var textField = document.createElement("input"); | |
|
606 | + textField.obj = this; | |
|
607 | + textField.type = "text"; | |
|
608 | + textField.name = this.options.paramName; | |
|
609 | + textField.value = text; | |
|
610 | + textField.style.backgroundColor = this.options.highlightcolor; | |
|
611 | + textField.className = 'editor_field'; | |
|
612 | + var size = this.options.size || this.options.cols || 0; | |
|
613 | + if (size != 0) textField.size = size; | |
|
614 | + if (this.options.submitOnBlur) | |
|
615 | + textField.onblur = this.onSubmit.bind(this); | |
|
616 | + this.editField = textField; | |
|
617 | + } else { | |
|
618 | + this.options.textarea = true; | |
|
619 | + var textArea = document.createElement("textarea"); | |
|
620 | + textArea.obj = this; | |
|
621 | + textArea.name = this.options.paramName; | |
|
622 | + textArea.value = this.convertHTMLLineBreaks(text); | |
|
623 | + textArea.rows = this.options.rows; | |
|
624 | + textArea.cols = this.options.cols || 40; | |
|
625 | + textArea.className = 'editor_field'; | |
|
626 | + if (this.options.submitOnBlur) | |
|
627 | + textArea.onblur = this.onSubmit.bind(this); | |
|
628 | + this.editField = textArea; | |
|
629 | + } | |
|
630 | + | |
|
631 | + if(this.options.loadTextURL) { | |
|
632 | + this.loadExternalText(); | |
|
633 | + } | |
|
634 | + this.form.appendChild(this.editField); | |
|
635 | + }, | |
|
636 | + getText: function() { | |
|
637 | + return this.element.innerHTML; | |
|
638 | + }, | |
|
639 | + loadExternalText: function() { | |
|
640 | + Element.addClassName(this.form, this.options.loadingClassName); | |
|
641 | + this.editField.disabled = true; | |
|
642 | + new Ajax.Request( | |
|
643 | + this.options.loadTextURL, | |
|
644 | + Object.extend({ | |
|
645 | + asynchronous: true, | |
|
646 | + onComplete: this.onLoadedExternalText.bind(this) | |
|
647 | + }, this.options.ajaxOptions) | |
|
648 | + ); | |
|
649 | + }, | |
|
650 | + onLoadedExternalText: function(transport) { | |
|
651 | + Element.removeClassName(this.form, this.options.loadingClassName); | |
|
652 | + this.editField.disabled = false; | |
|
653 | + this.editField.value = transport.responseText.stripTags(); | |
|
654 | + Field.scrollFreeActivate(this.editField); | |
|
655 | + }, | |
|
656 | + onclickCancel: function() { | |
|
657 | + this.onComplete(); | |
|
658 | + this.leaveEditMode(); | |
|
659 | + return false; | |
|
660 | + }, | |
|
661 | + onFailure: function(transport) { | |
|
662 | + this.options.onFailure(transport); | |
|
663 | + if (this.oldInnerHTML) { | |
|
664 | + this.element.innerHTML = this.oldInnerHTML; | |
|
665 | + this.oldInnerHTML = null; | |
|
666 | + } | |
|
667 | + return false; | |
|
668 | + }, | |
|
669 | + onSubmit: function() { | |
|
670 | + // onLoading resets these so we need to save them away for the Ajax call | |
|
671 | + var form = this.form; | |
|
672 | + var value = this.editField.value; | |
|
673 | + | |
|
674 | + // do this first, sometimes the ajax call returns before we get a chance to switch on Saving... | |
|
675 | + // which means this will actually switch on Saving... *after* we've left edit mode causing Saving... | |
|
676 | + // to be displayed indefinitely | |
|
677 | + this.onLoading(); | |
|
678 | + | |
|
679 | + if (this.options.evalScripts) { | |
|
680 | + new Ajax.Request( | |
|
681 | + this.url, Object.extend({ | |
|
682 | + parameters: this.options.callback(form, value), | |
|
683 | + onComplete: this.onComplete.bind(this), | |
|
684 | + onFailure: this.onFailure.bind(this), | |
|
685 | + asynchronous:true, | |
|
686 | + evalScripts:true | |
|
687 | + }, this.options.ajaxOptions)); | |
|
688 | + } else { | |
|
689 | + new Ajax.Updater( | |
|
690 | + { success: this.element, | |
|
691 | + // don't update on failure (this could be an option) | |
|
692 | + failure: null }, | |
|
693 | + this.url, Object.extend({ | |
|
694 | + parameters: this.options.callback(form, value), | |
|
695 | + onComplete: this.onComplete.bind(this), | |
|
696 | + onFailure: this.onFailure.bind(this) | |
|
697 | + }, this.options.ajaxOptions)); | |
|
698 | + } | |
|
699 | + // stop the event to avoid a page refresh in Safari | |
|
700 | + if (arguments.length > 1) { | |
|
701 | + Event.stop(arguments[0]); | |
|
702 | + } | |
|
703 | + return false; | |
|
704 | + }, | |
|
705 | + onLoading: function() { | |
|
706 | + this.saving = true; | |
|
707 | + this.removeForm(); | |
|
708 | + this.leaveHover(); | |
|
709 | + this.showSaving(); | |
|
710 | + }, | |
|
711 | + showSaving: function() { | |
|
712 | + this.oldInnerHTML = this.element.innerHTML; | |
|
713 | + this.element.innerHTML = this.options.savingText; | |
|
714 | + Element.addClassName(this.element, this.options.savingClassName); | |
|
715 | + this.element.style.backgroundColor = this.originalBackground; | |
|
716 | + Element.show(this.element); | |
|
717 | + }, | |
|
718 | + removeForm: function() { | |
|
719 | + if(this.form) { | |
|
720 | + if (this.form.parentNode) Element.remove(this.form); | |
|
721 | + this.form = null; | |
|
722 | + } | |
|
723 | + }, | |
|
724 | + enterHover: function() { | |
|
725 | + if (this.saving) return; | |
|
726 | + this.element.style.backgroundColor = this.options.highlightcolor; | |
|
727 | + if (this.effect) { | |
|
728 | + this.effect.cancel(); | |
|
729 | + } | |
|
730 | + Element.addClassName(this.element, this.options.hoverClassName) | |
|
731 | + }, | |
|
732 | + leaveHover: function() { | |
|
733 | + if (this.options.backgroundColor) { | |
|
734 | + this.element.style.backgroundColor = this.oldBackground; | |
|
735 | + } | |
|
736 | + Element.removeClassName(this.element, this.options.hoverClassName) | |
|
737 | + if (this.saving) return; | |
|
738 | + this.effect = new Effect.Highlight(this.element, { | |
|
739 | + startcolor: this.options.highlightcolor, | |
|
740 | + endcolor: this.options.highlightendcolor, | |
|
741 | + restorecolor: this.originalBackground | |
|
742 | + }); | |
|
743 | + }, | |
|
744 | + leaveEditMode: function() { | |
|
745 | + Element.removeClassName(this.element, this.options.savingClassName); | |
|
746 | + this.removeForm(); | |
|
747 | + this.leaveHover(); | |
|
748 | + this.element.style.backgroundColor = this.originalBackground; | |
|
749 | + Element.show(this.element); | |
|
750 | + if (this.options.externalControl) { | |
|
751 | + Element.show(this.options.externalControl); | |
|
752 | + } | |
|
753 | + this.editing = false; | |
|
754 | + this.saving = false; | |
|
755 | + this.oldInnerHTML = null; | |
|
756 | + this.onLeaveEditMode(); | |
|
757 | + }, | |
|
758 | + onComplete: function(transport) { | |
|
759 | + this.leaveEditMode(); | |
|
760 | + this.options.onComplete.bind(this)(transport, this.element); | |
|
761 | + }, | |
|
762 | + onEnterEditMode: function() {}, | |
|
763 | + onLeaveEditMode: function() {}, | |
|
764 | + dispose: function() { | |
|
765 | + if (this.oldInnerHTML) { | |
|
766 | + this.element.innerHTML = this.oldInnerHTML; | |
|
767 | + } | |
|
768 | + this.leaveEditMode(); | |
|
769 | + Event.stopObserving(this.element, 'click', this.onclickListener); | |
|
770 | + Event.stopObserving(this.element, 'mouseover', this.mouseoverListener); | |
|
771 | + Event.stopObserving(this.element, 'mouseout', this.mouseoutListener); | |
|
772 | + if (this.options.externalControl) { | |
|
773 | + Event.stopObserving(this.options.externalControl, 'click', this.onclickListener); | |
|
774 | + Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener); | |
|
775 | + Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener); | |
|
776 | + } | |
|
777 | + } | |
|
778 | + }; | |
|
779 | + | |
|
780 | + Ajax.InPlaceCollectionEditor = Class.create(); | |
|
781 | + Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype); | |
|
782 | + Object.extend(Ajax.InPlaceCollectionEditor.prototype, { | |
|
783 | + createEditField: function() { | |
|
784 | + if (!this.cached_selectTag) { | |
|
785 | + var selectTag = document.createElement("select"); | |
|
786 | + var collection = this.options.collection || []; | |
|
787 | + var optionTag; | |
|
788 | + collection.each(function(e,i) { | |
|
789 | + optionTag = document.createElement("option"); | |
|
790 | + optionTag.value = (e instanceof Array) ? e[0] : e; | |
|
791 | + if((typeof this.options.value == 'undefined') && | |
|
792 | + ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true; | |
|
793 | + if(this.options.value==optionTag.value) optionTag.selected = true; | |
|
794 | + optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e)); | |
|
795 | + selectTag.appendChild(optionTag); | |
|
796 | + }.bind(this)); | |
|
797 | + this.cached_selectTag = selectTag; | |
|
798 | + } | |
|
799 | + | |
|
800 | + this.editField = this.cached_selectTag; | |
|
801 | + if(this.options.loadTextURL) this.loadExternalText(); | |
|
802 | + this.form.appendChild(this.editField); | |
|
803 | + this.options.callback = function(form, value) { | |
|
804 | + return "value=" + encodeURIComponent(value); | |
|
805 | + } | |
|
806 | + } | |
|
807 | + }); | |
|
808 | + | |
|
809 | + // Delayed observer, like Form.Element.Observer, | |
|
810 | + // but waits for delay after last key input | |
|
811 | + // Ideal for live-search fields | |
|
812 | + | |
|
813 | + Form.Element.DelayedObserver = Class.create(); | |
|
814 | + Form.Element.DelayedObserver.prototype = { | |
|
815 | + initialize: function(element, delay, callback) { | |
|
816 | + this.delay = delay || 0.5; | |
|
817 | + this.element = $(element); | |
|
818 | + this.callback = callback; | |
|
819 | + this.timer = null; | |
|
820 | + this.lastValue = $F(this.element); | |
|
821 | + Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this)); | |
|
822 | + }, | |
|
823 | + delayedListener: function(event) { | |
|
824 | + if(this.lastValue == $F(this.element)) return; | |
|
825 | + if(this.timer) clearTimeout(this.timer); | |
|
826 | + this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000); | |
|
827 | + this.lastValue = $F(this.element); | |
|
828 | + }, | |
|
829 | + onTimerEvent: function() { | |
|
830 | + this.timer = null; | |
|
831 | + this.callback(this.element, $F(this.element)); | |
|
832 | + } | |
|
833 | + }; |
This diff has been collapsed as it changes many lines, (942 lines changed) Show them Hide them | |||
@@ -0,0 +1,942 | |||
|
1 | + // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) | |
|
2 | + // (c) 2005, 2006 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz) | |
|
3 | + // | |
|
4 | + // script.aculo.us is freely distributable under the terms of an MIT-style license. | |
|
5 | + // For details, see the script.aculo.us web site: http://script.aculo.us/ | |
|
6 | + | |
|
7 | + if(typeof Effect == 'undefined') | |
|
8 | + throw("dragdrop.js requires including script.aculo.us' effects.js library"); | |
|
9 | + | |
|
10 | + var Droppables = { | |
|
11 | + drops: [], | |
|
12 | + | |
|
13 | + remove: function(element) { | |
|
14 | + this.drops = this.drops.reject(function(d) { return d.element==$(element) }); | |
|
15 | + }, | |
|
16 | + | |
|
17 | + add: function(element) { | |
|
18 | + element = $(element); | |
|
19 | + var options = Object.extend({ | |
|
20 | + greedy: true, | |
|
21 | + hoverclass: null, | |
|
22 | + tree: false | |
|
23 | + }, arguments[1] || {}); | |
|
24 | + | |
|
25 | + // cache containers | |
|
26 | + if(options.containment) { | |
|
27 | + options._containers = []; | |
|
28 | + var containment = options.containment; | |
|
29 | + if((typeof containment == 'object') && | |
|
30 | + (containment.constructor == Array)) { | |
|
31 | + containment.each( function(c) { options._containers.push($(c)) }); | |
|
32 | + } else { | |
|
33 | + options._containers.push($(containment)); | |
|
34 | + } | |
|
35 | + } | |
|
36 | + | |
|
37 | + if(options.accept) options.accept = [options.accept].flatten(); | |
|
38 | + | |
|
39 | + Element.makePositioned(element); // fix IE | |
|
40 | + options.element = element; | |
|
41 | + | |
|
42 | + this.drops.push(options); | |
|
43 | + }, | |
|
44 | + | |
|
45 | + findDeepestChild: function(drops) { | |
|
46 | + deepest = drops[0]; | |
|
47 | + | |
|
48 | + for (i = 1; i < drops.length; ++i) | |
|
49 | + if (Element.isParent(drops[i].element, deepest.element)) | |
|
50 | + deepest = drops[i]; | |
|
51 | + | |
|
52 | + return deepest; | |
|
53 | + }, | |
|
54 | + | |
|
55 | + isContained: function(element, drop) { | |
|
56 | + var containmentNode; | |
|
57 | + if(drop.tree) { | |
|
58 | + containmentNode = element.treeNode; | |
|
59 | + } else { | |
|
60 | + containmentNode = element.parentNode; | |
|
61 | + } | |
|
62 | + return drop._containers.detect(function(c) { return containmentNode == c }); | |
|
63 | + }, | |
|
64 | + | |
|
65 | + isAffected: function(point, element, drop) { | |
|
66 | + return ( | |
|
67 | + (drop.element!=element) && | |
|
68 | + ((!drop._containers) || | |
|
69 | + this.isContained(element, drop)) && | |
|
70 | + ((!drop.accept) || | |
|
71 | + (Element.classNames(element).detect( | |
|
72 | + function(v) { return drop.accept.include(v) } ) )) && | |
|
73 | + Position.within(drop.element, point[0], point[1]) ); | |
|
74 | + }, | |
|
75 | + | |
|
76 | + deactivate: function(drop) { | |
|
77 | + if(drop.hoverclass) | |
|
78 | + Element.removeClassName(drop.element, drop.hoverclass); | |
|
79 | + this.last_active = null; | |
|
80 | + }, | |
|
81 | + | |
|
82 | + activate: function(drop) { | |
|
83 | + if(drop.hoverclass) | |
|
84 | + Element.addClassName(drop.element, drop.hoverclass); | |
|
85 | + this.last_active = drop; | |
|
86 | + }, | |
|
87 | + | |
|
88 | + show: function(point, element) { | |
|
89 | + if(!this.drops.length) return; | |
|
90 | + var affected = []; | |
|
91 | + | |
|
92 | + if(this.last_active) this.deactivate(this.last_active); | |
|
93 | + this.drops.each( function(drop) { | |
|
94 | + if(Droppables.isAffected(point, element, drop)) | |
|
95 | + affected.push(drop); | |
|
96 | + }); | |
|
97 | + | |
|
98 | + if(affected.length>0) { | |
|
99 | + drop = Droppables.findDeepestChild(affected); | |
|
100 | + Position.within(drop.element, point[0], point[1]); | |
|
101 | + if(drop.onHover) | |
|
102 | + drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); | |
|
103 | + | |
|
104 | + Droppables.activate(drop); | |
|
105 | + } | |
|
106 | + }, | |
|
107 | + | |
|
108 | + fire: function(event, element) { | |
|
109 | + if(!this.last_active) return; | |
|
110 | + Position.prepare(); | |
|
111 | + | |
|
112 | + if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active)) | |
|
113 | + if (this.last_active.onDrop) | |
|
114 | + this.last_active.onDrop(element, this.last_active.element, event); | |
|
115 | + }, | |
|
116 | + | |
|
117 | + reset: function() { | |
|
118 | + if(this.last_active) | |
|
119 | + this.deactivate(this.last_active); | |
|
120 | + } | |
|
121 | + } | |
|
122 | + | |
|
123 | + var Draggables = { | |
|
124 | + drags: [], | |
|
125 | + observers: [], | |
|
126 | + | |
|
127 | + register: function(draggable) { | |
|
128 | + if(this.drags.length == 0) { | |
|
129 | + this.eventMouseUp = this.endDrag.bindAsEventListener(this); | |
|
130 | + this.eventMouseMove = this.updateDrag.bindAsEventListener(this); | |
|
131 | + this.eventKeypress = this.keyPress.bindAsEventListener(this); | |
|
132 | + | |
|
133 | + Event.observe(document, "mouseup", this.eventMouseUp); | |
|
134 | + Event.observe(document, "mousemove", this.eventMouseMove); | |
|
135 | + Event.observe(document, "keypress", this.eventKeypress); | |
|
136 | + } | |
|
137 | + this.drags.push(draggable); | |
|
138 | + }, | |
|
139 | + | |
|
140 | + unregister: function(draggable) { | |
|
141 | + this.drags = this.drags.reject(function(d) { return d==draggable }); | |
|
142 | + if(this.drags.length == 0) { | |
|
143 | + Event.stopObserving(document, "mouseup", this.eventMouseUp); | |
|
144 | + Event.stopObserving(document, "mousemove", this.eventMouseMove); | |
|
145 | + Event.stopObserving(document, "keypress", this.eventKeypress); | |
|
146 | + } | |
|
147 | + }, | |
|
148 | + | |
|
149 | + activate: function(draggable) { | |
|
150 | + if(draggable.options.delay) { | |
|
151 | + this._timeout = setTimeout(function() { | |
|
152 | + Draggables._timeout = null; | |
|
153 | + window.focus(); | |
|
154 | + Draggables.activeDraggable = draggable; | |
|
155 | + }.bind(this), draggable.options.delay); | |
|
156 | + } else { | |
|
157 | + window.focus(); // allows keypress events if window isn't currently focused, fails for Safari | |
|
158 | + this.activeDraggable = draggable; | |
|
159 | + } | |
|
160 | + }, | |
|
161 | + | |
|
162 | + deactivate: function() { | |
|
163 | + this.activeDraggable = null; | |
|
164 | + }, | |
|
165 | + | |
|
166 | + updateDrag: function(event) { | |
|
167 | + if(!this.activeDraggable) return; | |
|
168 | + var pointer = [Event.pointerX(event), Event.pointerY(event)]; | |
|
169 | + // Mozilla-based browsers fire successive mousemove events with | |
|
170 | + // the same coordinates, prevent needless redrawing (moz bug?) | |
|
171 | + if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return; | |
|
172 | + this._lastPointer = pointer; | |
|
173 | + | |
|
174 | + this.activeDraggable.updateDrag(event, pointer); | |
|
175 | + }, | |
|
176 | + | |
|
177 | + endDrag: function(event) { | |
|
178 | + if(this._timeout) { | |
|
179 | + clearTimeout(this._timeout); | |
|
180 | + this._timeout = null; | |
|
181 | + } | |
|
182 | + if(!this.activeDraggable) return; | |
|
183 | + this._lastPointer = null; | |
|
184 | + this.activeDraggable.endDrag(event); | |
|
185 | + this.activeDraggable = null; | |
|
186 | + }, | |
|
187 | + | |
|
188 | + keyPress: function(event) { | |
|
189 | + if(this.activeDraggable) | |
|
190 | + this.activeDraggable.keyPress(event); | |
|
191 | + }, | |
|
192 | + | |
|
193 | + addObserver: function(observer) { | |
|
194 | + this.observers.push(observer); | |
|
195 | + this._cacheObserverCallbacks(); | |
|
196 | + }, | |
|
197 | + | |
|
198 | + removeObserver: function(element) { // element instead of observer fixes mem leaks | |
|
199 | + this.observers = this.observers.reject( function(o) { return o.element==element }); | |
|
200 | + this._cacheObserverCallbacks(); | |
|
201 | + }, | |
|
202 | + | |
|
203 | + notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag' | |
|
204 | + if(this[eventName+'Count'] > 0) | |
|
205 | + this.observers.each( function(o) { | |
|
206 | + if(o[eventName]) o[eventName](eventName, draggable, event); | |
|
207 | + }); | |
|
208 | + if(draggable.options[eventName]) draggable.options[eventName](draggable, event); | |
|
209 | + }, | |
|
210 | + | |
|
211 | + _cacheObserverCallbacks: function() { | |
|
212 | + ['onStart','onEnd','onDrag'].each( function(eventName) { | |
|
213 | + Draggables[eventName+'Count'] = Draggables.observers.select( | |
|
214 | + function(o) { return o[eventName]; } | |
|
215 | + ).length; | |
|
216 | + }); | |
|
217 | + } | |
|
218 | + } | |
|
219 | + | |
|
220 | + /*--------------------------------------------------------------------------*/ | |
|
221 | + | |
|
222 | + var Draggable = Class.create(); | |
|
223 | + Draggable._dragging = {}; | |
|
224 | + | |
|
225 | + Draggable.prototype = { | |
|
226 | + initialize: function(element) { | |
|
227 | + var defaults = { | |
|
228 | + handle: false, | |
|
229 | + reverteffect: function(element, top_offset, left_offset) { | |
|
230 | + var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02; | |
|
231 | + new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur, | |
|
232 | + queue: {scope:'_draggable', position:'end'} | |
|
233 | + }); | |
|
234 | + }, | |
|
235 | + endeffect: function(element) { | |
|
236 | + var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0; | |
|
237 | + new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, | |
|
238 | + queue: {scope:'_draggable', position:'end'}, | |
|
239 | + afterFinish: function(){ | |
|
240 | + Draggable._dragging[element] = false | |
|
241 | + } | |
|
242 | + }); | |
|
243 | + }, | |
|
244 | + zindex: 1000, | |
|
245 | + revert: false, | |
|
246 | + scroll: false, | |
|
247 | + scrollSensitivity: 20, | |
|
248 | + scrollSpeed: 15, | |
|
249 | + snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] } | |
|
250 | + delay: 0 | |
|
251 | + }; | |
|
252 | + | |
|
253 | + if(!arguments[1] || typeof arguments[1].endeffect == 'undefined') | |
|
254 | + Object.extend(defaults, { | |
|
255 | + starteffect: function(element) { | |
|
256 | + element._opacity = Element.getOpacity(element); | |
|
257 | + Draggable._dragging[element] = true; | |
|
258 | + new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); | |
|
259 | + } | |
|
260 | + }); | |
|
261 | + | |
|
262 | + var options = Object.extend(defaults, arguments[1] || {}); | |
|
263 | + | |
|
264 | + this.element = $(element); | |
|
265 | + | |
|
266 | + if(options.handle && (typeof options.handle == 'string')) | |
|
267 | + this.handle = this.element.down('.'+options.handle, 0); | |
|
268 | + | |
|
269 | + if(!this.handle) this.handle = $(options.handle); | |
|
270 | + if(!this.handle) this.handle = this.element; | |
|
271 | + | |
|
272 | + if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) { | |
|
273 | + options.scroll = $(options.scroll); | |
|
274 | + this._isScrollChild = Element.childOf(this.element, options.scroll); | |
|
275 | + } | |
|
276 | + | |
|
277 | + Element.makePositioned(this.element); // fix IE | |
|
278 | + | |
|
279 | + this.delta = this.currentDelta(); | |
|
280 | + this.options = options; | |
|
281 | + this.dragging = false; | |
|
282 | + | |
|
283 | + this.eventMouseDown = this.initDrag.bindAsEventListener(this); | |
|
284 | + Event.observe(this.handle, "mousedown", this.eventMouseDown); | |
|
285 | + | |
|
286 | + Draggables.register(this); | |
|
287 | + }, | |
|
288 | + | |
|
289 | + destroy: function() { | |
|
290 | + Event.stopObserving(this.handle, "mousedown", this.eventMouseDown); | |
|
291 | + Draggables.unregister(this); | |
|
292 | + }, | |
|
293 | + | |
|
294 | + currentDelta: function() { | |
|
295 | + return([ | |
|
296 | + parseInt(Element.getStyle(this.element,'left') || '0'), | |
|
297 | + parseInt(Element.getStyle(this.element,'top') || '0')]); | |
|
298 | + }, | |
|
299 | + | |
|
300 | + initDrag: function(event) { | |
|
301 | + if(typeof Draggable._dragging[this.element] != 'undefined' && | |
|
302 | + Draggable._dragging[this.element]) return; | |
|
303 | + if(Event.isLeftClick(event)) { | |
|
304 | + // abort on form elements, fixes a Firefox issue | |
|
305 | + var src = Event.element(event); | |
|
306 | + if(src.tagName && ( | |
|
307 | + src.tagName=='INPUT' || | |
|
308 | + src.tagName=='SELECT' || | |
|
309 | + src.tagName=='OPTION' || | |
|
310 | + src.tagName=='BUTTON' || | |
|
311 | + src.tagName=='TEXTAREA')) return; | |
|
312 | + | |
|
313 | + var pointer = [Event.pointerX(event), Event.pointerY(event)]; | |
|
314 | + var pos = Position.cumulativeOffset(this.element); | |
|
315 | + this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) }); | |
|
316 | + | |
|
317 | + Draggables.activate(this); | |
|
318 | + Event.stop(event); | |
|
319 | + } | |
|
320 | + }, | |
|
321 | + | |
|
322 | + startDrag: function(event) { | |
|
323 | + this.dragging = true; | |
|
324 | + | |
|
325 | + if(this.options.zindex) { | |
|
326 | + this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); | |
|
327 | + this.element.style.zIndex = this.options.zindex; | |
|
328 | + } | |
|
329 | + | |
|
330 | + if(this.options.ghosting) { | |
|
331 | + this._clone = this.element.cloneNode(true); | |
|
332 | + Position.absolutize(this.element); | |
|
333 | + this.element.parentNode.insertBefore(this._clone, this.element); | |
|
334 | + } | |
|
335 | + | |
|
336 | + if(this.options.scroll) { | |
|
337 | + if (this.options.scroll == window) { | |
|
338 | + var where = this._getWindowScroll(this.options.scroll); | |
|
339 | + this.originalScrollLeft = where.left; | |
|
340 | + this.originalScrollTop = where.top; | |
|
341 | + } else { | |
|
342 | + this.originalScrollLeft = this.options.scroll.scrollLeft; | |
|
343 | + this.originalScrollTop = this.options.scroll.scrollTop; | |
|
344 | + } | |
|
345 | + } | |
|
346 | + | |
|
347 | + Draggables.notify('onStart', this, event); | |
|
348 | + | |
|
349 | + if(this.options.starteffect) this.options.starteffect(this.element); | |
|
350 | + }, | |
|
351 | + | |
|
352 | + updateDrag: function(event, pointer) { | |
|
353 | + if(!this.dragging) this.startDrag(event); | |
|
354 | + Position.prepare(); | |
|
355 | + Droppables.show(pointer, this.element); | |
|
356 | + Draggables.notify('onDrag', this, event); | |
|
357 | + | |
|
358 | + this.draw(pointer); | |
|
359 | + if(this.options.change) this.options.change(this); | |
|
360 | + | |
|
361 | + if(this.options.scroll) { | |
|
362 | + this.stopScrolling(); | |
|
363 | + | |
|
364 | + var p; | |
|
365 | + if (this.options.scroll == window) { | |
|
366 | + with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; } | |
|
367 | + } else { | |
|
368 | + p = Position.page(this.options.scroll); | |
|
369 | + p[0] += this.options.scroll.scrollLeft + Position.deltaX; | |
|
370 | + p[1] += this.options.scroll.scrollTop + Position.deltaY; | |
|
371 | + p.push(p[0]+this.options.scroll.offsetWidth); | |
|
372 | + p.push(p[1]+this.options.scroll.offsetHeight); | |
|
373 | + } | |
|
374 | + var speed = [0,0]; | |
|
375 | + if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity); | |
|
376 | + if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity); | |
|
377 | + if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity); | |
|
378 | + if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity); | |
|
379 | + this.startScrolling(speed); | |
|
380 | + } | |
|
381 | + | |
|
382 | + // fix AppleWebKit rendering | |
|
383 | + if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); | |
|
384 | + | |
|
385 | + Event.stop(event); | |
|
386 | + }, | |
|
387 | + | |
|
388 | + finishDrag: function(event, success) { | |
|
389 | + this.dragging = false; | |
|
390 | + | |
|
391 | + if(this.options.ghosting) { | |
|
392 | + Position.relativize(this.element); | |
|
393 | + Element.remove(this._clone); | |
|
394 | + this._clone = null; | |
|
395 | + } | |
|
396 | + | |
|
397 | + if(success) Droppables.fire(event, this.element); | |
|
398 | + Draggables.notify('onEnd', this, event); | |
|
399 | + | |
|
400 | + var revert = this.options.revert; | |
|
401 | + if(revert && typeof revert == 'function') revert = revert(this.element); | |
|
402 | + | |
|
403 | + var d = this.currentDelta(); | |
|
404 | + if(revert && this.options.reverteffect) { | |
|
405 | + this.options.reverteffect(this.element, | |
|
406 | + d[1]-this.delta[1], d[0]-this.delta[0]); | |
|
407 | + } else { | |
|
408 | + this.delta = d; | |
|
409 | + } | |
|
410 | + | |
|
411 | + if(this.options.zindex) | |
|
412 | + this.element.style.zIndex = this.originalZ; | |
|
413 | + | |
|
414 | + if(this.options.endeffect) | |
|
415 | + this.options.endeffect(this.element); | |
|
416 | + | |
|
417 | + Draggables.deactivate(this); | |
|
418 | + Droppables.reset(); | |
|
419 | + }, | |
|
420 | + | |
|
421 | + keyPress: function(event) { | |
|
422 | + if(event.keyCode!=Event.KEY_ESC) return; | |
|
423 | + this.finishDrag(event, false); | |
|
424 | + Event.stop(event); | |
|
425 | + }, | |
|
426 | + | |
|
427 | + endDrag: function(event) { | |
|
428 | + if(!this.dragging) return; | |
|
429 | + this.stopScrolling(); | |
|
430 | + this.finishDrag(event, true); | |
|
431 | + Event.stop(event); | |
|
432 | + }, | |
|
433 | + | |
|
434 | + draw: function(point) { | |
|
435 | + var pos = Position.cumulativeOffset(this.element); | |
|
436 | + if(this.options.ghosting) { | |
|
437 | + var r = Position.realOffset(this.element); | |
|
438 | + pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY; | |
|
439 | + } | |
|
440 | + | |
|
441 | + var d = this.currentDelta(); | |
|
442 | + pos[0] -= d[0]; pos[1] -= d[1]; | |
|
443 | + | |
|
444 | + if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) { | |
|
445 | + pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft; | |
|
446 | + pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop; | |
|
447 | + } | |
|
448 | + | |
|
449 | + var p = [0,1].map(function(i){ | |
|
450 | + return (point[i]-pos[i]-this.offset[i]) | |
|
451 | + }.bind(this)); | |
|
452 | + | |
|
453 | + if(this.options.snap) { | |
|
454 | + if(typeof this.options.snap == 'function') { | |
|
455 | + p = this.options.snap(p[0],p[1],this); | |
|
456 | + } else { | |
|
457 | + if(this.options.snap instanceof Array) { | |
|
458 | + p = p.map( function(v, i) { | |
|
459 | + return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this)) | |
|
460 | + } else { | |
|
461 | + p = p.map( function(v) { | |
|
462 | + return Math.round(v/this.options.snap)*this.options.snap }.bind(this)) | |
|
463 | + } | |
|
464 | + }} | |
|
465 | + | |
|
466 | + var style = this.element.style; | |
|
467 | + if((!this.options.constraint) || (this.options.constraint=='horizontal')) | |
|
468 | + style.left = p[0] + "px"; | |
|
469 | + if((!this.options.constraint) || (this.options.constraint=='vertical')) | |
|
470 | + style.top = p[1] + "px"; | |
|
471 | + | |
|
472 | + if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering | |
|
473 | + }, | |
|
474 | + | |
|
475 | + stopScrolling: function() { | |
|
476 | + if(this.scrollInterval) { | |
|
477 | + clearInterval(this.scrollInterval); | |
|
478 | + this.scrollInterval = null; | |
|
479 | + Draggables._lastScrollPointer = null; | |
|
480 | + } | |
|
481 | + }, | |
|
482 | + | |
|
483 | + startScrolling: function(speed) { | |
|
484 | + if(!(speed[0] || speed[1])) return; | |
|
485 | + this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed]; | |
|
486 | + this.lastScrolled = new Date(); | |
|
487 | + this.scrollInterval = setInterval(this.scroll.bind(this), 10); | |
|
488 | + }, | |
|
489 | + | |
|
490 | + scroll: function() { | |
|
491 | + var current = new Date(); | |
|
492 | + var delta = current - this.lastScrolled; | |
|
493 | + this.lastScrolled = current; | |
|
494 | + if(this.options.scroll == window) { | |
|
495 | + with (this._getWindowScroll(this.options.scroll)) { | |
|
496 | + if (this.scrollSpeed[0] || this.scrollSpeed[1]) { | |
|
497 | + var d = delta / 1000; | |
|
498 | + this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] ); | |
|
499 | + } | |
|
500 | + } | |
|
501 | + } else { | |
|
502 | + this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000; | |
|
503 | + this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000; | |
|
504 | + } | |
|
505 | + | |
|
506 | + Position.prepare(); | |
|
507 | + Droppables.show(Draggables._lastPointer, this.element); | |
|
508 | + Draggables.notify('onDrag', this); | |
|
509 | + if (this._isScrollChild) { | |
|
510 | + Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer); | |
|
511 | + Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000; | |
|
512 | + Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000; | |
|
513 | + if (Draggables._lastScrollPointer[0] < 0) | |
|
514 | + Draggables._lastScrollPointer[0] = 0; | |
|
515 | + if (Draggables._lastScrollPointer[1] < 0) | |
|
516 | + Draggables._lastScrollPointer[1] = 0; | |
|
517 | + this.draw(Draggables._lastScrollPointer); | |
|
518 | + } | |
|
519 | + | |
|
520 | + if(this.options.change) this.options.change(this); | |
|
521 | + }, | |
|
522 | + | |
|
523 | + _getWindowScroll: function(w) { | |
|
524 | + var T, L, W, H; | |
|
525 | + with (w.document) { | |
|
526 | + if (w.document.documentElement && documentElement.scrollTop) { | |
|
527 | + T = documentElement.scrollTop; | |
|
528 | + L = documentElement.scrollLeft; | |
|
529 | + } else if (w.document.body) { | |
|
530 | + T = body.scrollTop; | |
|
531 | + L = body.scrollLeft; | |
|
532 | + } | |
|
533 | + if (w.innerWidth) { | |
|
534 | + W = w.innerWidth; | |
|
535 | + H = w.innerHeight; | |
|
536 | + } else if (w.document.documentElement && documentElement.clientWidth) { | |
|
537 | + W = documentElement.clientWidth; | |
|
538 | + H = documentElement.clientHeight; | |
|
539 | + } else { | |
|
540 | + W = body.offsetWidth; | |
|
541 | + H = body.offsetHeight | |
|
542 | + } | |
|
543 | + } | |
|
544 | + return { top: T, left: L, width: W, height: H }; | |
|
545 | + } | |
|
546 | + } | |
|
547 | + | |
|
548 | + /*--------------------------------------------------------------------------*/ | |
|
549 | + | |
|
550 | + var SortableObserver = Class.create(); | |
|
551 | + SortableObserver.prototype = { | |
|
552 | + initialize: function(element, observer) { | |
|
553 | + this.element = $(element); | |
|
554 | + this.observer = observer; | |
|
555 | + this.lastValue = Sortable.serialize(this.element); | |
|
556 | + }, | |
|
557 | + | |
|
558 | + onStart: function() { | |
|
559 | + this.lastValue = Sortable.serialize(this.element); | |
|
560 | + }, | |
|
561 | + | |
|
562 | + onEnd: function() { | |
|
563 | + Sortable.unmark(); | |
|
564 | + if(this.lastValue != Sortable.serialize(this.element)) | |
|
565 | + this.observer(this.element) | |
|
566 | + } | |
|
567 | + } | |
|
568 | + | |
|
569 | + var Sortable = { | |
|
570 | + SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/, | |
|
571 | + | |
|
572 | + sortables: {}, | |
|
573 | + | |
|
574 | + _findRootElement: function(element) { | |
|
575 | + while (element.tagName != "BODY") { | |
|
576 | + if(element.id && Sortable.sortables[element.id]) return element; | |
|
577 | + element = element.parentNode; | |
|
578 | + } | |
|
579 | + }, | |
|
580 | + | |
|
581 | + options: function(element) { | |
|
582 | + element = Sortable._findRootElement($(element)); | |
|
583 | + if(!element) return; | |
|
584 | + return Sortable.sortables[element.id]; | |
|
585 | + }, | |
|
586 | + | |
|
587 | + destroy: function(element){ | |
|
588 | + var s = Sortable.options(element); | |
|
589 | + | |
|
590 | + if(s) { | |
|
591 | + Draggables.removeObserver(s.element); | |
|
592 | + s.droppables.each(function(d){ Droppables.remove(d) }); | |
|
593 | + s.draggables.invoke('destroy'); | |
|
594 | + | |
|
595 | + delete Sortable.sortables[s.element.id]; | |
|
596 | + } | |
|
597 | + }, | |
|
598 | + | |
|
599 | + create: function(element) { | |
|
600 | + element = $(element); | |
|
601 | + var options = Object.extend({ | |
|
602 | + element: element, | |
|
603 | + tag: 'li', // assumes li children, override with tag: 'tagname' | |
|
604 | + dropOnEmpty: false, | |
|
605 | + tree: false, | |
|
606 | + treeTag: 'ul', | |
|
607 | + overlap: 'vertical', // one of 'vertical', 'horizontal' | |
|
608 | + constraint: 'vertical', // one of 'vertical', 'horizontal', false | |
|
609 | + containment: element, // also takes array of elements (or id's); or false | |
|
610 | + handle: false, // or a CSS class | |
|
611 | + only: false, | |
|
612 | + delay: 0, | |
|
613 | + hoverclass: null, | |
|
614 | + ghosting: false, | |
|
615 | + scroll: false, | |
|
616 | + scrollSensitivity: 20, | |
|
617 | + scrollSpeed: 15, | |
|
618 | + format: this.SERIALIZE_RULE, | |
|
619 | + onChange: Prototype.emptyFunction, | |
|
620 | + onUpdate: Prototype.emptyFunction | |
|
621 | + }, arguments[1] || {}); | |
|
622 | + | |
|
623 | + // clear any old sortable with same element | |
|
624 | + this.destroy(element); | |
|
625 | + | |
|
626 | + // build options for the draggables | |
|
627 | + var options_for_draggable = { | |
|
628 | + revert: true, | |
|
629 | + scroll: options.scroll, | |
|
630 | + scrollSpeed: options.scrollSpeed, | |
|
631 | + scrollSensitivity: options.scrollSensitivity, | |
|
632 | + delay: options.delay, | |
|
633 | + ghosting: options.ghosting, | |
|
634 | + constraint: options.constraint, | |
|
635 | + handle: options.handle }; | |
|
636 | + | |
|
637 | + if(options.starteffect) | |
|
638 | + options_for_draggable.starteffect = options.starteffect; | |
|
639 | + | |
|
640 | + if(options.reverteffect) | |
|
641 | + options_for_draggable.reverteffect = options.reverteffect; | |
|
642 | + else | |
|
643 | + if(options.ghosting) options_for_draggable.reverteffect = function(element) { | |
|
644 | + element.style.top = 0; | |
|
645 | + element.style.left = 0; | |
|
646 | + }; | |
|
647 | + | |
|
648 | + if(options.endeffect) | |
|
649 | + options_for_draggable.endeffect = options.endeffect; | |
|
650 | + | |
|
651 | + if(options.zindex) | |
|
652 | + options_for_draggable.zindex = options.zindex; | |
|
653 | + | |
|
654 | + // build options for the droppables | |
|
655 | + var options_for_droppable = { | |
|
656 | + overlap: options.overlap, | |
|
657 | + containment: options.containment, | |
|
658 | + tree: options.tree, | |
|
659 | + hoverclass: options.hoverclass, | |
|
660 | + onHover: Sortable.onHover | |
|
661 | + } | |
|
662 | + | |
|
663 | + var options_for_tree = { | |
|
664 | + onHover: Sortable.onEmptyHover, | |
|
665 | + overlap: options.overlap, | |
|
666 | + containment: options.containment, | |
|
667 | + hoverclass: options.hoverclass | |
|
668 | + } | |
|
669 | + | |
|
670 | + // fix for gecko engine | |
|
671 | + Element.cleanWhitespace(element); | |
|
672 | + | |
|
673 | + options.draggables = []; | |
|
674 | + options.droppables = []; | |
|
675 | + | |
|
676 | + // drop on empty handling | |
|
677 | + if(options.dropOnEmpty || options.tree) { | |
|
678 | + Droppables.add(element, options_for_tree); | |
|
679 | + options.droppables.push(element); | |
|
680 | + } | |
|
681 | + | |
|
682 | + (this.findElements(element, options) || []).each( function(e) { | |
|
683 | + // handles are per-draggable | |
|
684 | + var handle = options.handle ? | |
|
685 | + $(e).down('.'+options.handle,0) : e; | |
|
686 | + options.draggables.push( | |
|
687 | + new Draggable(e, Object.extend(options_for_draggable, { handle: handle }))); | |
|
688 | + Droppables.add(e, options_for_droppable); | |
|
689 | + if(options.tree) e.treeNode = element; | |
|
690 | + options.droppables.push(e); | |
|
691 | + }); | |
|
692 | + | |
|
693 | + if(options.tree) { | |
|
694 | + (Sortable.findTreeElements(element, options) || []).each( function(e) { | |
|
695 | + Droppables.add(e, options_for_tree); | |
|
696 | + e.treeNode = element; | |
|
697 | + options.droppables.push(e); | |
|
698 | + }); | |
|
699 | + } | |
|
700 | + | |
|
701 | + // keep reference | |
|
702 | + this.sortables[element.id] = options; | |
|
703 | + | |
|
704 | + // for onupdate | |
|
705 | + Draggables.addObserver(new SortableObserver(element, options.onUpdate)); | |
|
706 | + | |
|
707 | + }, | |
|
708 | + | |
|
709 | + // return all suitable-for-sortable elements in a guaranteed order | |
|
710 | + findElements: function(element, options) { | |
|
711 | + return Element.findChildren( | |
|
712 | + element, options.only, options.tree ? true : false, options.tag); | |
|
713 | + }, | |
|
714 | + | |
|
715 | + findTreeElements: function(element, options) { | |
|
716 | + return Element.findChildren( | |
|
717 | + element, options.only, options.tree ? true : false, options.treeTag); | |
|
718 | + }, | |
|
719 | + | |
|
720 | + onHover: function(element, dropon, overlap) { | |
|
721 | + if(Element.isParent(dropon, element)) return; | |
|
722 | + | |
|
723 | + if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) { | |
|
724 | + return; | |
|
725 | + } else if(overlap>0.5) { | |
|
726 | + Sortable.mark(dropon, 'before'); | |
|
727 | + if(dropon.previousSibling != element) { | |
|
728 | + var oldParentNode = element.parentNode; | |
|
729 | + element.style.visibility = "hidden"; // fix gecko rendering | |
|
730 | + dropon.parentNode.insertBefore(element, dropon); | |
|
731 | + if(dropon.parentNode!=oldParentNode) | |
|
732 | + Sortable.options(oldParentNode).onChange(element); | |
|
733 | + Sortable.options(dropon.parentNode).onChange(element); | |
|
734 | + } | |
|
735 | + } else { | |
|
736 | + Sortable.mark(dropon, 'after'); | |
|
737 | + var nextElement = dropon.nextSibling || null; | |
|
738 | + if(nextElement != element) { | |
|
739 | + var oldParentNode = element.parentNode; | |
|
740 | + element.style.visibility = "hidden"; // fix gecko rendering | |
|
741 | + dropon.parentNode.insertBefore(element, nextElement); | |
|
742 | + if(dropon.parentNode!=oldParentNode) | |
|
743 | + Sortable.options(oldParentNode).onChange(element); | |
|
744 | + Sortable.options(dropon.parentNode).onChange(element); | |
|
745 | + } | |
|
746 | + } | |
|
747 | + }, | |
|
748 | + | |
|
749 | + onEmptyHover: function(element, dropon, overlap) { | |
|
750 | + var oldParentNode = element.parentNode; | |
|
751 | + var droponOptions = Sortable.options(dropon); | |
|
752 | + | |
|
753 | + if(!Element.isParent(dropon, element)) { | |
|
754 | + var index; | |
|
755 | + | |
|
756 | + var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only}); | |
|
757 | + var child = null; | |
|
758 | + | |
|
759 | + if(children) { | |
|
760 | + var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap); | |
|
761 | + | |
|
762 | + for (index = 0; index < children.length; index += 1) { | |
|
763 | + if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) { | |
|
764 | + offset -= Element.offsetSize (children[index], droponOptions.overlap); | |
|
765 | + } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) { | |
|
766 | + child = index + 1 < children.length ? children[index + 1] : null; | |
|
767 | + break; | |
|
768 | + } else { | |
|
769 | + child = children[index]; | |
|
770 | + break; | |
|
771 | + } | |
|
772 | + } | |
|
773 | + } | |
|
774 | + | |
|
775 | + dropon.insertBefore(element, child); | |
|
776 | + | |
|
777 | + Sortable.options(oldParentNode).onChange(element); | |
|
778 | + droponOptions.onChange(element); | |
|
779 | + } | |
|
780 | + }, | |
|
781 | + | |
|
782 | + unmark: function() { | |
|
783 | + if(Sortable._marker) Sortable._marker.hide(); | |
|
784 | + }, | |
|
785 | + | |
|
786 | + mark: function(dropon, position) { | |
|
787 | + // mark on ghosting only | |
|
788 | + var sortable = Sortable.options(dropon.parentNode); | |
|
789 | + if(sortable && !sortable.ghosting) return; | |
|
790 | + | |
|
791 | + if(!Sortable._marker) { | |
|
792 | + Sortable._marker = | |
|
793 | + ($('dropmarker') || Element.extend(document.createElement('DIV'))). | |
|
794 | + hide().addClassName('dropmarker').setStyle({position:'absolute'}); | |
|
795 | + document.getElementsByTagName("body").item(0).appendChild(Sortable._marker); | |
|
796 | + } | |
|
797 | + var offsets = Position.cumulativeOffset(dropon); | |
|
798 | + Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'}); | |
|
799 | + | |
|
800 | + if(position=='after') | |
|
801 | + if(sortable.overlap == 'horizontal') | |
|
802 | + Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'}); | |
|
803 | + else | |
|
804 | + Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'}); | |
|
805 | + | |
|
806 | + Sortable._marker.show(); | |
|
807 | + }, | |
|
808 | + | |
|
809 | + _tree: function(element, options, parent) { | |
|
810 | + var children = Sortable.findElements(element, options) || []; | |
|
811 | + | |
|
812 | + for (var i = 0; i < children.length; ++i) { | |
|
813 | + var match = children[i].id.match(options.format); | |
|
814 | + | |
|
815 | + if (!match) continue; | |
|
816 | + | |
|
817 | + var child = { | |
|
818 | + id: encodeURIComponent(match ? match[1] : null), | |
|
819 | + element: element, | |
|
820 | + parent: parent, | |
|
821 | + children: [], | |
|
822 | + position: parent.children.length, | |
|
823 | + container: $(children[i]).down(options.treeTag) | |
|
824 | + } | |
|
825 | + | |
|
826 | + /* Get the element containing the children and recurse over it */ | |
|
827 | + if (child.container) | |
|
828 | + this._tree(child.container, options, child) | |
|
829 | + | |
|
830 | + parent.children.push (child); | |
|
831 | + } | |
|
832 | + | |
|
833 | + return parent; | |
|
834 | + }, | |
|
835 | + | |
|
836 | + tree: function(element) { | |
|
837 | + element = $(element); | |
|
838 | + var sortableOptions = this.options(element); | |
|
839 | + var options = Object.extend({ | |
|
840 | + tag: sortableOptions.tag, | |
|
841 | + treeTag: sortableOptions.treeTag, | |
|
842 | + only: sortableOptions.only, | |
|
843 | + name: element.id, | |
|
844 | + format: sortableOptions.format | |
|
845 | + }, arguments[1] || {}); | |
|
846 | + | |
|
847 | + var root = { | |
|
848 | + id: null, | |
|
849 | + parent: null, | |
|
850 | + children: [], | |
|
851 | + container: element, | |
|
852 | + position: 0 | |
|
853 | + } | |
|
854 | + | |
|
855 | + return Sortable._tree(element, options, root); | |
|
856 | + }, | |
|
857 | + | |
|
858 | + /* Construct a [i] index for a particular node */ | |
|
859 | + _constructIndex: function(node) { | |
|
860 | + var index = ''; | |
|
861 | + do { | |
|
862 | + if (node.id) index = '[' + node.position + ']' + index; | |
|
863 | + } while ((node = node.parent) != null); | |
|
864 | + return index; | |
|
865 | + }, | |
|
866 | + | |
|
867 | + sequence: function(element) { | |
|
868 | + element = $(element); | |
|
869 | + var options = Object.extend(this.options(element), arguments[1] || {}); | |
|
870 | + | |
|
871 | + return $(this.findElements(element, options) || []).map( function(item) { | |
|
872 | + return item.id.match(options.format) ? item.id.match(options.format)[1] : ''; | |
|
873 | + }); | |
|
874 | + }, | |
|
875 | + | |
|
876 | + setSequence: function(element, new_sequence) { | |
|
877 | + element = $(element); | |
|
878 | + var options = Object.extend(this.options(element), arguments[2] || {}); | |
|
879 | + | |
|
880 | + var nodeMap = {}; | |
|
881 | + this.findElements(element, options).each( function(n) { | |
|
882 | + if (n.id.match(options.format)) | |
|
883 | + nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode]; | |
|
884 | + n.parentNode.removeChild(n); | |
|
885 | + }); | |
|
886 | + | |
|
887 | + new_sequence.each(function(ident) { | |
|
888 | + var n = nodeMap[ident]; | |
|
889 | + if (n) { | |
|
890 | + n[1].appendChild(n[0]); | |
|
891 | + delete nodeMap[ident]; | |
|
892 | + } | |
|
893 | + }); | |
|
894 | + }, | |
|
895 | + | |
|
896 | + serialize: function(element) { | |
|
897 | + element = $(element); | |
|
898 | + var options = Object.extend(Sortable.options(element), arguments[1] || {}); | |
|
899 | + var name = encodeURIComponent( | |
|
900 | + (arguments[1] && arguments[1].name) ? arguments[1].name : element.id); | |
|
901 | + | |
|
902 | + if (options.tree) { | |
|
903 | + return Sortable.tree(element, arguments[1]).children.map( function (item) { | |
|
904 | + return [name + Sortable._constructIndex(item) + "[id]=" + | |
|
905 | + encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); | |
|
906 | + }).flatten().join('&'); | |
|
907 | + } else { | |
|
908 | + return Sortable.sequence(element, arguments[1]).map( function(item) { | |
|
909 | + return name + "[]=" + encodeURIComponent(item); | |
|
910 | + }).join('&'); | |
|
911 | + } | |
|
912 | + } | |
|
913 | + } | |
|
914 | + | |
|
915 | + // Returns true if child is contained within element | |
|
916 | + Element.isParent = function(child, element) { | |
|
917 | + if (!child.parentNode || child == element) return false; | |
|
918 | + if (child.parentNode == element) return true; | |
|
919 | + return Element.isParent(child.parentNode, element); | |
|
920 | + } | |
|
921 | + | |
|
922 | + Element.findChildren = function(element, only, recursive, tagName) { | |
|
923 | + if(!element.hasChildNodes()) return null; | |
|
924 | + tagName = tagName.toUpperCase(); | |
|
925 | + if(only) only = [only].flatten(); | |
|
926 | + var elements = []; | |
|
927 | + $A(element.childNodes).each( function(e) { | |
|
928 | + if(e.tagName && e.tagName.toUpperCase()==tagName && | |
|
929 | + (!only || (Element.classNames(e).detect(function(v) { return only.include(v) })))) | |
|
930 | + elements.push(e); | |
|
931 | + if(recursive) { | |
|
932 | + var grandchildren = Element.findChildren(e, only, recursive, tagName); | |
|
933 | + if(grandchildren) elements.push(grandchildren); | |
|
934 | + } | |
|
935 | + }); | |
|
936 | + | |
|
937 | + return (elements.length>0 ? elements.flatten() : []); | |
|
938 | + } | |
|
939 | + | |
|
940 | + Element.offsetSize = function (element, type) { | |
|
941 | + return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')]; | |
|
942 | + } |
This diff has been collapsed as it changes many lines, (1088 lines changed) Show them Hide them | |||
@@ -0,0 +1,1088 | |||
|
1 | + // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) | |
|
2 | + // Contributors: | |
|
3 | + // Justin Palmer (http://encytemedia.com/) | |
|
4 | + // Mark Pilgrim (http://diveintomark.org/) | |
|
5 | + // Martin Bialasinki | |
|
6 | + // | |
|
7 | + // script.aculo.us is freely distributable under the terms of an MIT-style license. | |
|
8 | + // For details, see the script.aculo.us web site: http://script.aculo.us/ | |
|
9 | + | |
|
10 | + // converts rgb() and #xxx to #xxxxxx format, | |
|
11 | + // returns self (or first argument) if not convertable | |
|
12 | + String.prototype.parseColor = function() { | |
|
13 | + var color = '#'; | |
|
14 | + if(this.slice(0,4) == 'rgb(') { | |
|
15 | + var cols = this.slice(4,this.length-1).split(','); | |
|
16 | + var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); | |
|
17 | + } else { | |
|
18 | + if(this.slice(0,1) == '#') { | |
|
19 | + if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); | |
|
20 | + if(this.length==7) color = this.toLowerCase(); | |
|
21 | + } | |
|
22 | + } | |
|
23 | + return(color.length==7 ? color : (arguments[0] || this)); | |
|
24 | + } | |
|
25 | + | |
|
26 | + /*--------------------------------------------------------------------------*/ | |
|
27 | + | |
|
28 | + Element.collectTextNodes = function(element) { | |
|
29 | + return $A($(element).childNodes).collect( function(node) { | |
|
30 | + return (node.nodeType==3 ? node.nodeValue : | |
|
31 | + (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); | |
|
32 | + }).flatten().join(''); | |
|
33 | + } | |
|
34 | + | |
|
35 | + Element.collectTextNodesIgnoreClass = function(element, className) { | |
|
36 | + return $A($(element).childNodes).collect( function(node) { | |
|
37 | + return (node.nodeType==3 ? node.nodeValue : | |
|
38 | + ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? | |
|
39 | + Element.collectTextNodesIgnoreClass(node, className) : '')); | |
|
40 | + }).flatten().join(''); | |
|
41 | + } | |
|
42 | + | |
|
43 | + Element.setContentZoom = function(element, percent) { | |
|
44 | + element = $(element); | |
|
45 | + element.setStyle({fontSize: (percent/100) + 'em'}); | |
|
46 | + if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); | |
|
47 | + return element; | |
|
48 | + } | |
|
49 | + | |
|
50 | + Element.getOpacity = function(element){ | |
|
51 | + element = $(element); | |
|
52 | + var opacity; | |
|
53 | + if (opacity = element.getStyle('opacity')) | |
|
54 | + return parseFloat(opacity); | |
|
55 | + if (opacity = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) | |
|
56 | + if(opacity[1]) return parseFloat(opacity[1]) / 100; | |
|
57 | + return 1.0; | |
|
58 | + } | |
|
59 | + | |
|
60 | + Element.setOpacity = function(element, value){ | |
|
61 | + element= $(element); | |
|
62 | + if (value == 1){ | |
|
63 | + element.setStyle({ opacity: | |
|
64 | + (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? | |
|
65 | + 0.999999 : 1.0 }); | |
|
66 | + if(/MSIE/.test(navigator.userAgent) && !window.opera) | |
|
67 | + element.setStyle({filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')}); | |
|
68 | + } else { | |
|
69 | + if(value < 0.00001) value = 0; | |
|
70 | + element.setStyle({opacity: value}); | |
|
71 | + if(/MSIE/.test(navigator.userAgent) && !window.opera) | |
|
72 | + element.setStyle( | |
|
73 | + { filter: element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') + | |
|
74 | + 'alpha(opacity='+value*100+')' }); | |
|
75 | + } | |
|
76 | + return element; | |
|
77 | + } | |
|
78 | + | |
|
79 | + Element.getInlineOpacity = function(element){ | |
|
80 | + return $(element).style.opacity || ''; | |
|
81 | + } | |
|
82 | + | |
|
83 | + Element.forceRerendering = function(element) { | |
|
84 | + try { | |
|
85 | + element = $(element); | |
|
86 | + var n = document.createTextNode(' '); | |
|
87 | + element.appendChild(n); | |
|
88 | + element.removeChild(n); | |
|
89 | + } catch(e) { } | |
|
90 | + }; | |
|
91 | + | |
|
92 | + /*--------------------------------------------------------------------------*/ | |
|
93 | + | |
|
94 | + Array.prototype.call = function() { | |
|
95 | + var args = arguments; | |
|
96 | + this.each(function(f){ f.apply(this, args) }); | |
|
97 | + } | |
|
98 | + | |
|
99 | + /*--------------------------------------------------------------------------*/ | |
|
100 | + | |
|
101 | + var Effect = { | |
|
102 | + _elementDoesNotExistError: { | |
|
103 | + name: 'ElementDoesNotExistError', | |
|
104 | + message: 'The specified DOM element does not exist, but is required for this effect to operate' | |
|
105 | + }, | |
|
106 | + tagifyText: function(element) { | |
|
107 | + if(typeof Builder == 'undefined') | |
|
108 | + throw("Effect.tagifyText requires including script.aculo.us' builder.js library"); | |
|
109 | + | |
|
110 | + var tagifyStyle = 'position:relative'; | |
|
111 | + if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1'; | |
|
112 | + | |
|
113 | + element = $(element); | |
|
114 | + $A(element.childNodes).each( function(child) { | |
|
115 | + if(child.nodeType==3) { | |
|
116 | + child.nodeValue.toArray().each( function(character) { | |
|
117 | + element.insertBefore( | |
|
118 | + Builder.node('span',{style: tagifyStyle}, | |
|
119 | + character == ' ' ? String.fromCharCode(160) : character), | |
|
120 | + child); | |
|
121 | + }); | |
|
122 | + Element.remove(child); | |
|
123 | + } | |
|
124 | + }); | |
|
125 | + }, | |
|
126 | + multiple: function(element, effect) { | |
|
127 | + var elements; | |
|
128 | + if(((typeof element == 'object') || | |
|
129 | + (typeof element == 'function')) && | |
|
130 | + (element.length)) | |
|
131 | + elements = element; | |
|
132 | + else | |
|
133 | + elements = $(element).childNodes; | |
|
134 | + | |
|
135 | + var options = Object.extend({ | |
|
136 | + speed: 0.1, | |
|
137 | + delay: 0.0 | |
|
138 | + }, arguments[2] || {}); | |
|
139 | + var masterDelay = options.delay; | |
|
140 | + | |
|
141 | + $A(elements).each( function(element, index) { | |
|
142 | + new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay })); | |
|
143 | + }); | |
|
144 | + }, | |
|
145 | + PAIRS: { | |
|
146 | + 'slide': ['SlideDown','SlideUp'], | |
|
147 | + 'blind': ['BlindDown','BlindUp'], | |
|
148 | + 'appear': ['Appear','Fade'] | |
|
149 | + }, | |
|
150 | + toggle: function(element, effect) { | |
|
151 | + element = $(element); | |
|
152 | + effect = (effect || 'appear').toLowerCase(); | |
|
153 | + var options = Object.extend({ | |
|
154 | + queue: { position:'end', scope:(element.id || 'global'), limit: 1 } | |
|
155 | + }, arguments[2] || {}); | |
|
156 | + Effect[element.visible() ? | |
|
157 | + Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options); | |
|
158 | + } | |
|
159 | + }; | |
|
160 | + | |
|
161 | + var Effect2 = Effect; // deprecated | |
|
162 | + | |
|
163 | + /* ------------- transitions ------------- */ | |
|
164 | + | |
|
165 | + Effect.Transitions = { | |
|
166 | + linear: Prototype.K, | |
|
167 | + sinoidal: function(pos) { | |
|
168 | + return (-Math.cos(pos*Math.PI)/2) + 0.5; | |
|
169 | + }, | |
|
170 | + reverse: function(pos) { | |
|
171 | + return 1-pos; | |
|
172 | + }, | |
|
173 | + flicker: function(pos) { | |
|
174 | + return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; | |
|
175 | + }, | |
|
176 | + wobble: function(pos) { | |
|
177 | + return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; | |
|
178 | + }, | |
|
179 | + pulse: function(pos, pulses) { | |
|
180 | + pulses = pulses || 5; | |
|
181 | + return ( | |
|
182 | + Math.round((pos % (1/pulses)) * pulses) == 0 ? | |
|
183 | + ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) : | |
|
184 | + 1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) | |
|
185 | + ); | |
|
186 | + }, | |
|
187 | + none: function(pos) { | |
|
188 | + return 0; | |
|
189 | + }, | |
|
190 | + full: function(pos) { | |
|
191 | + return 1; | |
|
192 | + } | |
|
193 | + }; | |
|
194 | + | |
|
195 | + /* ------------- core effects ------------- */ | |
|
196 | + | |
|
197 | + Effect.ScopedQueue = Class.create(); | |
|
198 | + Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), { | |
|
199 | + initialize: function() { | |
|
200 | + this.effects = []; | |
|
201 | + this.interval = null; | |
|
202 | + }, | |
|
203 | + _each: function(iterator) { | |
|
204 | + this.effects._each(iterator); | |
|
205 | + }, | |
|
206 | + add: function(effect) { | |
|
207 | + var timestamp = new Date().getTime(); | |
|
208 | + | |
|
209 | + var position = (typeof effect.options.queue == 'string') ? | |
|
210 | + effect.options.queue : effect.options.queue.position; | |
|
211 | + | |
|
212 | + switch(position) { | |
|
213 | + case 'front': | |
|
214 | + // move unstarted effects after this effect | |
|
215 | + this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { | |
|
216 | + e.startOn += effect.finishOn; | |
|
217 | + e.finishOn += effect.finishOn; | |
|
218 | + }); | |
|
219 | + break; | |
|
220 | + case 'with-last': | |
|
221 | + timestamp = this.effects.pluck('startOn').max() || timestamp; | |
|
222 | + break; | |
|
223 | + case 'end': | |
|
224 | + // start effect after last queued effect has finished | |
|
225 | + timestamp = this.effects.pluck('finishOn').max() || timestamp; | |
|
226 | + break; | |
|
227 | + } | |
|
228 | + | |
|
229 | + effect.startOn += timestamp; | |
|
230 | + effect.finishOn += timestamp; | |
|
231 | + | |
|
232 | + if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) | |
|
233 | + this.effects.push(effect); | |
|
234 | + | |
|
235 | + if(!this.interval) | |
|
236 | + this.interval = setInterval(this.loop.bind(this), 40); | |
|
237 | + }, | |
|
238 | + remove: function(effect) { | |
|
239 | + this.effects = this.effects.reject(function(e) { return e==effect }); | |
|
240 | + if(this.effects.length == 0) { | |
|
241 | + clearInterval(this.interval); | |
|
242 | + this.interval = null; | |
|
243 | + } | |
|
244 | + }, | |
|
245 | + loop: function() { | |
|
246 | + var timePos = new Date().getTime(); | |
|
247 | + this.effects.invoke('loop', timePos); | |
|
248 | + } | |
|
249 | + }); | |
|
250 | + | |
|
251 | + Effect.Queues = { | |
|
252 | + instances: $H(), | |
|
253 | + get: function(queueName) { | |
|
254 | + if(typeof queueName != 'string') return queueName; | |
|
255 | + | |
|
256 | + if(!this.instances[queueName]) | |
|
257 | + this.instances[queueName] = new Effect.ScopedQueue(); | |
|
258 | + | |
|
259 | + return this.instances[queueName]; | |
|
260 | + } | |
|
261 | + } | |
|
262 | + Effect.Queue = Effect.Queues.get('global'); | |
|
263 | + | |
|
264 | + Effect.DefaultOptions = { | |
|
265 | + transition: Effect.Transitions.sinoidal, | |
|
266 | + duration: 1.0, // seconds | |
|
267 | + fps: 25.0, // max. 25fps due to Effect.Queue implementation | |
|
268 | + sync: false, // true for combining | |
|
269 | + from: 0.0, | |
|
270 | + to: 1.0, | |
|
271 | + delay: 0.0, | |
|
272 | + queue: 'parallel' | |
|
273 | + } | |
|
274 | + | |
|
275 | + Effect.Base = function() {}; | |
|
276 | + Effect.Base.prototype = { | |
|
277 | + position: null, | |
|
278 | + start: function(options) { | |
|
279 | + this.options = Object.extend(Object.extend({},Effect.DefaultOptions), options || {}); | |
|
280 | + this.currentFrame = 0; | |
|
281 | + this.state = 'idle'; | |
|
282 | + this.startOn = this.options.delay*1000; | |
|
283 | + this.finishOn = this.startOn + (this.options.duration*1000); | |
|
284 | + this.event('beforeStart'); | |
|
285 | + if(!this.options.sync) | |
|
286 | + Effect.Queues.get(typeof this.options.queue == 'string' ? | |
|
287 | + 'global' : this.options.queue.scope).add(this); | |
|
288 | + }, | |
|
289 | + loop: function(timePos) { | |
|
290 | + if(timePos >= this.startOn) { | |
|
291 | + if(timePos >= this.finishOn) { | |
|
292 | + this.render(1.0); | |
|
293 | + this.cancel(); | |
|
294 | + this.event('beforeFinish'); | |
|
295 | + if(this.finish) this.finish(); | |
|
296 | + this.event('afterFinish'); | |
|
297 | + return; | |
|
298 | + } | |
|
299 | + var pos = (timePos - this.startOn) / (this.finishOn - this.startOn); | |
|
300 | + var frame = Math.round(pos * this.options.fps * this.options.duration); | |
|
301 | + if(frame > this.currentFrame) { | |
|
302 | + this.render(pos); | |
|
303 | + this.currentFrame = frame; | |
|
304 | + } | |
|
305 | + } | |
|
306 | + }, | |
|
307 | + render: function(pos) { | |
|
308 | + if(this.state == 'idle') { | |
|
309 | + this.state = 'running'; | |
|
310 | + this.event('beforeSetup'); | |
|
311 | + if(this.setup) this.setup(); | |
|
312 | + this.event('afterSetup'); | |
|
313 | + } | |
|
314 | + if(this.state == 'running') { | |
|
315 | + if(this.options.transition) pos = this.options.transition(pos); | |
|
316 | + pos *= (this.options.to-this.options.from); | |
|
317 | + pos += this.options.from; | |
|
318 | + this.position = pos; | |
|
319 | + this.event('beforeUpdate'); | |
|
320 | + if(this.update) this.update(pos); | |
|
321 | + this.event('afterUpdate'); | |
|
322 | + } | |
|
323 | + }, | |
|
324 | + cancel: function() { | |
|
325 | + if(!this.options.sync) | |
|
326 | + Effect.Queues.get(typeof this.options.queue == 'string' ? | |
|
327 | + 'global' : this.options.queue.scope).remove(this); | |
|
328 | + this.state = 'finished'; | |
|
329 | + }, | |
|
330 | + event: function(eventName) { | |
|
331 | + if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); | |
|
332 | + if(this.options[eventName]) this.options[eventName](this); | |
|
333 | + }, | |
|
334 | + inspect: function() { | |
|
335 | + return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>'; | |
|
336 | + } | |
|
337 | + } | |
|
338 | + | |
|
339 | + Effect.Parallel = Class.create(); | |
|
340 | + Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), { | |
|
341 | + initialize: function(effects) { | |
|
342 | + this.effects = effects || []; | |
|
343 | + this.start(arguments[1]); | |
|
344 | + }, | |
|
345 | + update: function(position) { | |
|
346 | + this.effects.invoke('render', position); | |
|
347 | + }, | |
|
348 | + finish: function(position) { | |
|
349 | + this.effects.each( function(effect) { | |
|
350 | + effect.render(1.0); | |
|
351 | + effect.cancel(); | |
|
352 | + effect.event('beforeFinish'); | |
|
353 | + if(effect.finish) effect.finish(position); | |
|
354 | + effect.event('afterFinish'); | |
|
355 | + }); | |
|
356 | + } | |
|
357 | + }); | |
|
358 | + | |
|
359 | + Effect.Event = Class.create(); | |
|
360 | + Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), { | |
|
361 | + initialize: function() { | |
|
362 | + var options = Object.extend({ | |
|
363 | + duration: 0 | |
|
364 | + }, arguments[0] || {}); | |
|
365 | + this.start(options); | |
|
366 | + }, | |
|
367 | + update: Prototype.emptyFunction | |
|
368 | + }); | |
|
369 | + | |
|
370 | + Effect.Opacity = Class.create(); | |
|
371 | + Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), { | |
|
372 | + initialize: function(element) { | |
|
373 | + this.element = $(element); | |
|
374 | + if(!this.element) throw(Effect._elementDoesNotExistError); | |
|
375 | + // make this work on IE on elements without 'layout' | |
|
376 | + if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout)) | |
|
377 | + this.element.setStyle({zoom: 1}); | |
|
378 | + var options = Object.extend({ | |
|
379 | + from: this.element.getOpacity() || 0.0, | |
|
380 | + to: 1.0 | |
|
381 | + }, arguments[1] || {}); | |
|
382 | + this.start(options); | |
|
383 | + }, | |
|
384 | + update: function(position) { | |
|
385 | + this.element.setOpacity(position); | |
|
386 | + } | |
|
387 | + }); | |
|
388 | + | |
|
389 | + Effect.Move = Class.create(); | |
|
390 | + Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), { | |
|
391 | + initialize: function(element) { | |
|
392 | + this.element = $(element); | |
|
393 | + if(!this.element) throw(Effect._elementDoesNotExistError); | |
|
394 | + var options = Object.extend({ | |
|
395 | + x: 0, | |
|
396 | + y: 0, | |
|
397 | + mode: 'relative' | |
|
398 | + }, arguments[1] || {}); | |
|
399 | + this.start(options); | |
|
400 | + }, | |
|
401 | + setup: function() { | |
|
402 | + // Bug in Opera: Opera returns the "real" position of a static element or | |
|
403 | + // relative element that does not have top/left explicitly set. | |
|
404 | + // ==> Always set top and left for position relative elements in your stylesheets | |
|
405 | + // (to 0 if you do not need them) | |
|
406 | + this.element.makePositioned(); | |
|
407 | + this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); | |
|
408 | + this.originalTop = parseFloat(this.element.getStyle('top') || '0'); | |
|
409 | + if(this.options.mode == 'absolute') { | |
|
410 | + // absolute movement, so we need to calc deltaX and deltaY | |
|
411 | + this.options.x = this.options.x - this.originalLeft; | |
|
412 | + this.options.y = this.options.y - this.originalTop; | |
|
413 | + } | |
|
414 | + }, | |
|
415 | + update: function(position) { | |
|
416 | + this.element.setStyle({ | |
|
417 | + left: Math.round(this.options.x * position + this.originalLeft) + 'px', | |
|
418 | + top: Math.round(this.options.y * position + this.originalTop) + 'px' | |
|
419 | + }); | |
|
420 | + } | |
|
421 | + }); | |
|
422 | + | |
|
423 | + // for backwards compatibility | |
|
424 | + Effect.MoveBy = function(element, toTop, toLeft) { | |
|
425 | + return new Effect.Move(element, | |
|
426 | + Object.extend({ x: toLeft, y: toTop }, arguments[3] || {})); | |
|
427 | + }; | |
|
428 | + | |
|
429 | + Effect.Scale = Class.create(); | |
|
430 | + Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { | |
|
431 | + initialize: function(element, percent) { | |
|
432 | + this.element = $(element); | |
|
433 | + if(!this.element) throw(Effect._elementDoesNotExistError); | |
|
434 | + var options = Object.extend({ | |
|
435 | + scaleX: true, | |
|
436 | + scaleY: true, | |
|
437 | + scaleContent: true, | |
|
438 | + scaleFromCenter: false, | |
|
439 | + scaleMode: 'box', // 'box' or 'contents' or {} with provided values | |
|
440 | + scaleFrom: 100.0, | |
|
441 | + scaleTo: percent | |
|
442 | + }, arguments[2] || {}); | |
|
443 | + this.start(options); | |
|
444 | + }, | |
|
445 | + setup: function() { | |
|
446 | + this.restoreAfterFinish = this.options.restoreAfterFinish || false; | |
|
447 | + this.elementPositioning = this.element.getStyle('position'); | |
|
448 | + | |
|
449 | + this.originalStyle = {}; | |
|
450 | + ['top','left','width','height','fontSize'].each( function(k) { | |
|
451 | + this.originalStyle[k] = this.element.style[k]; | |
|
452 | + }.bind(this)); | |
|
453 | + | |
|
454 | + this.originalTop = this.element.offsetTop; | |
|
455 | + this.originalLeft = this.element.offsetLeft; | |
|
456 | + | |
|
457 | + var fontSize = this.element.getStyle('font-size') || '100%'; | |
|
458 | + ['em','px','%','pt'].each( function(fontSizeType) { | |
|
459 | + if(fontSize.indexOf(fontSizeType)>0) { | |
|
460 | + this.fontSize = parseFloat(fontSize); | |
|
461 | + this.fontSizeType = fontSizeType; | |
|
462 | + } | |
|
463 | + }.bind(this)); | |
|
464 | + | |
|
465 | + this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; | |
|
466 | + | |
|
467 | + this.dims = null; | |
|
468 | + if(this.options.scaleMode=='box') | |
|
469 | + this.dims = [this.element.offsetHeight, this.element.offsetWidth]; | |
|
470 | + if(/^content/.test(this.options.scaleMode)) | |
|
471 | + this.dims = [this.element.scrollHeight, this.element.scrollWidth]; | |
|
472 | + if(!this.dims) | |
|
473 | + this.dims = [this.options.scaleMode.originalHeight, | |
|
474 | + this.options.scaleMode.originalWidth]; | |
|
475 | + }, | |
|
476 | + update: function(position) { | |
|
477 | + var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); | |
|
478 | + if(this.options.scaleContent && this.fontSize) | |
|
479 | + this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType }); | |
|
480 | + this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); | |
|
481 | + }, | |
|
482 | + finish: function(position) { | |
|
483 | + if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle); | |
|
484 | + }, | |
|
485 | + setDimensions: function(height, width) { | |
|
486 | + var d = {}; | |
|
487 | + if(this.options.scaleX) d.width = Math.round(width) + 'px'; | |
|
488 | + if(this.options.scaleY) d.height = Math.round(height) + 'px'; | |
|
489 | + if(this.options.scaleFromCenter) { | |
|
490 | + var topd = (height - this.dims[0])/2; | |
|
491 | + var leftd = (width - this.dims[1])/2; | |
|
492 | + if(this.elementPositioning == 'absolute') { | |
|
493 | + if(this.options.scaleY) d.top = this.originalTop-topd + 'px'; | |
|
494 | + if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; | |
|
495 | + } else { | |
|
496 | + if(this.options.scaleY) d.top = -topd + 'px'; | |
|
497 | + if(this.options.scaleX) d.left = -leftd + 'px'; | |
|
498 | + } | |
|
499 | + } | |
|
500 | + this.element.setStyle(d); | |
|
501 | + } | |
|
502 | + }); | |
|
503 | + | |
|
504 | + Effect.Highlight = Class.create(); | |
|
505 | + Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), { | |
|
506 | + initialize: function(element) { | |
|
507 | + this.element = $(element); | |
|
508 | + if(!this.element) throw(Effect._elementDoesNotExistError); | |
|
509 | + var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {}); | |
|
510 | + this.start(options); | |
|
511 | + }, | |
|
512 | + setup: function() { | |
|
513 | + // Prevent executing on elements not in the layout flow | |
|
514 | + if(this.element.getStyle('display')=='none') { this.cancel(); return; } | |
|
515 | + // Disable background image during the effect | |
|
516 | + this.oldStyle = { | |
|
517 | + backgroundImage: this.element.getStyle('background-image') }; | |
|
518 | + this.element.setStyle({backgroundImage: 'none'}); | |
|
519 | + if(!this.options.endcolor) | |
|
520 | + this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff'); | |
|
521 | + if(!this.options.restorecolor) | |
|
522 | + this.options.restorecolor = this.element.getStyle('background-color'); | |
|
523 | + // init color calculations | |
|
524 | + this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this)); | |
|
525 | + this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this)); | |
|
526 | + }, | |
|
527 | + update: function(position) { | |
|
528 | + this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){ | |
|
529 | + return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) }); | |
|
530 | + }, | |
|
531 | + finish: function() { | |
|
532 | + this.element.setStyle(Object.extend(this.oldStyle, { | |
|
533 | + backgroundColor: this.options.restorecolor | |
|
534 | + })); | |
|
535 | + } | |
|
536 | + }); | |
|
537 | + | |
|
538 | + Effect.ScrollTo = Class.create(); | |
|
539 | + Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), { | |
|
540 | + initialize: function(element) { | |
|
541 | + this.element = $(element); | |
|
542 | + this.start(arguments[1] || {}); | |
|
543 | + }, | |
|
544 | + setup: function() { | |
|
545 | + Position.prepare(); | |
|
546 | + var offsets = Position.cumulativeOffset(this.element); | |
|
547 | + if(this.options.offset) offsets[1] += this.options.offset; | |
|
548 | + var max = window.innerHeight ? | |
|
549 | + window.height - window.innerHeight : | |
|
550 | + document.body.scrollHeight - | |
|
551 | + (document.documentElement.clientHeight ? | |
|
552 | + document.documentElement.clientHeight : document.body.clientHeight); | |
|
553 | + this.scrollStart = Position.deltaY; | |
|
554 | + this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart; | |
|
555 | + }, | |
|
556 | + update: function(position) { | |
|
557 | + Position.prepare(); | |
|
558 | + window.scrollTo(Position.deltaX, | |
|
559 | + this.scrollStart + (position*this.delta)); | |
|
560 | + } | |
|
561 | + }); | |
|
562 | + | |
|
563 | + /* ------------- combination effects ------------- */ | |
|
564 | + | |
|
565 | + Effect.Fade = function(element) { | |
|
566 | + element = $(element); | |
|
567 | + var oldOpacity = element.getInlineOpacity(); | |
|
568 | + var options = Object.extend({ | |
|
569 | + from: element.getOpacity() || 1.0, | |
|
570 | + to: 0.0, | |
|
571 | + afterFinishInternal: function(effect) { | |
|
572 | + if(effect.options.to!=0) return; | |
|
573 | + effect.element.hide().setStyle({opacity: oldOpacity}); | |
|
574 | + }}, arguments[1] || {}); | |
|
575 | + return new Effect.Opacity(element,options); | |
|
576 | + } | |
|
577 | + | |
|
578 | + Effect.Appear = function(element) { | |
|
579 | + element = $(element); | |
|
580 | + var options = Object.extend({ | |
|
581 | + from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0), | |
|
582 | + to: 1.0, | |
|
583 | + // force Safari to render floated elements properly | |
|
584 | + afterFinishInternal: function(effect) { | |
|
585 | + effect.element.forceRerendering(); | |
|
586 | + }, | |
|
587 | + beforeSetup: function(effect) { | |
|
588 | + effect.element.setOpacity(effect.options.from).show(); | |
|
589 | + }}, arguments[1] || {}); | |
|
590 | + return new Effect.Opacity(element,options); | |
|
591 | + } | |
|
592 | + | |
|
593 | + Effect.Puff = function(element) { | |
|
594 | + element = $(element); | |
|
595 | + var oldStyle = { | |
|
596 | + opacity: element.getInlineOpacity(), | |
|
597 | + position: element.getStyle('position'), | |
|
598 | + top: element.style.top, | |
|
599 | + left: element.style.left, | |
|
600 | + width: element.style.width, | |
|
601 | + height: element.style.height | |
|
602 | + }; | |
|
603 | + return new Effect.Parallel( | |
|
604 | + [ new Effect.Scale(element, 200, | |
|
605 | + { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), | |
|
606 | + new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], | |
|
607 | + Object.extend({ duration: 1.0, | |
|
608 | + beforeSetupInternal: function(effect) { | |
|
609 | + Position.absolutize(effect.effects[0].element) | |
|
610 | + }, | |
|
611 | + afterFinishInternal: function(effect) { | |
|
612 | + effect.effects[0].element.hide().setStyle(oldStyle); } | |
|
613 | + }, arguments[1] || {}) | |
|
614 | + ); | |
|
615 | + } | |
|
616 | + | |
|
617 | + Effect.BlindUp = function(element) { | |
|
618 | + element = $(element); | |
|
619 | + element.makeClipping(); | |
|
620 | + return new Effect.Scale(element, 0, | |
|
621 | + Object.extend({ scaleContent: false, | |
|
622 | + scaleX: false, | |
|
623 | + restoreAfterFinish: true, | |
|
624 | + afterFinishInternal: function(effect) { | |
|
625 | + effect.element.hide().undoClipping(); | |
|
626 | + } | |
|
627 | + }, arguments[1] || {}) | |
|
628 | + ); | |
|
629 | + } | |
|
630 | + | |
|
631 | + Effect.BlindDown = function(element) { | |
|
632 | + element = $(element); | |
|
633 | + var elementDimensions = element.getDimensions(); | |
|
634 | + return new Effect.Scale(element, 100, Object.extend({ | |
|
635 | + scaleContent: false, | |
|
636 | + scaleX: false, | |
|
637 | + scaleFrom: 0, | |
|
638 | + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, | |
|
639 | + restoreAfterFinish: true, | |
|
640 | + afterSetup: function(effect) { | |
|
641 | + effect.element.makeClipping().setStyle({height: '0px'}).show(); | |
|
642 | + }, | |
|
643 | + afterFinishInternal: function(effect) { | |
|
644 | + effect.element.undoClipping(); | |
|
645 | + } | |
|
646 | + }, arguments[1] || {})); | |
|
647 | + } | |
|
648 | + | |
|
649 | + Effect.SwitchOff = function(element) { | |
|
650 | + element = $(element); | |
|
651 | + var oldOpacity = element.getInlineOpacity(); | |
|
652 | + return new Effect.Appear(element, Object.extend({ | |
|
653 | + duration: 0.4, | |
|
654 | + from: 0, | |
|
655 | + transition: Effect.Transitions.flicker, | |
|
656 | + afterFinishInternal: function(effect) { | |
|
657 | + new Effect.Scale(effect.element, 1, { | |
|
658 | + duration: 0.3, scaleFromCenter: true, | |
|
659 | + scaleX: false, scaleContent: false, restoreAfterFinish: true, | |
|
660 | + beforeSetup: function(effect) { | |
|
661 | + effect.element.makePositioned().makeClipping(); | |
|
662 | + }, | |
|
663 | + afterFinishInternal: function(effect) { | |
|
664 | + effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity}); | |
|
665 | + } | |
|
666 | + }) | |
|
667 | + } | |
|
668 | + }, arguments[1] || {})); | |
|
669 | + } | |
|
670 | + | |
|
671 | + Effect.DropOut = function(element) { | |
|
672 | + element = $(element); | |
|
673 | + var oldStyle = { | |
|
674 | + top: element.getStyle('top'), | |
|
675 | + left: element.getStyle('left'), | |
|
676 | + opacity: element.getInlineOpacity() }; | |
|
677 | + return new Effect.Parallel( | |
|
678 | + [ new Effect.Move(element, {x: 0, y: 100, sync: true }), | |
|
679 | + new Effect.Opacity(element, { sync: true, to: 0.0 }) ], | |
|
680 | + Object.extend( | |
|
681 | + { duration: 0.5, | |
|
682 | + beforeSetup: function(effect) { | |
|
683 | + effect.effects[0].element.makePositioned(); | |
|
684 | + }, | |
|
685 | + afterFinishInternal: function(effect) { | |
|
686 | + effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle); | |
|
687 | + } | |
|
688 | + }, arguments[1] || {})); | |
|
689 | + } | |
|
690 | + | |
|
691 | + Effect.Shake = function(element) { | |
|
692 | + element = $(element); | |
|
693 | + var oldStyle = { | |
|
694 | + top: element.getStyle('top'), | |
|
695 | + left: element.getStyle('left') }; | |
|
696 | + return new Effect.Move(element, | |
|
697 | + { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { | |
|
698 | + new Effect.Move(effect.element, | |
|
699 | + { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { | |
|
700 | + new Effect.Move(effect.element, | |
|
701 | + { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { | |
|
702 | + new Effect.Move(effect.element, | |
|
703 | + { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { | |
|
704 | + new Effect.Move(effect.element, | |
|
705 | + { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { | |
|
706 | + new Effect.Move(effect.element, | |
|
707 | + { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { | |
|
708 | + effect.element.undoPositioned().setStyle(oldStyle); | |
|
709 | + }}) }}) }}) }}) }}) }}); | |
|
710 | + } | |
|
711 | + | |
|
712 | + Effect.SlideDown = function(element) { | |
|
713 | + element = $(element).cleanWhitespace(); | |
|
714 | + // SlideDown need to have the content of the element wrapped in a container element with fixed height! | |
|
715 | + var oldInnerBottom = element.down().getStyle('bottom'); | |
|
716 | + var elementDimensions = element.getDimensions(); | |
|
717 | + return new Effect.Scale(element, 100, Object.extend({ | |
|
718 | + scaleContent: false, | |
|
719 | + scaleX: false, | |
|
720 | + scaleFrom: window.opera ? 0 : 1, | |
|
721 | + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, | |
|
722 | + restoreAfterFinish: true, | |
|
723 | + afterSetup: function(effect) { | |
|
724 | + effect.element.makePositioned(); | |
|
725 | + effect.element.down().makePositioned(); | |
|
726 | + if(window.opera) effect.element.setStyle({top: ''}); | |
|
727 | + effect.element.makeClipping().setStyle({height: '0px'}).show(); | |
|
728 | + }, | |
|
729 | + afterUpdateInternal: function(effect) { | |
|
730 | + effect.element.down().setStyle({bottom: | |
|
731 | + (effect.dims[0] - effect.element.clientHeight) + 'px' }); | |
|
732 | + }, | |
|
733 | + afterFinishInternal: function(effect) { | |
|
734 | + effect.element.undoClipping().undoPositioned(); | |
|
735 | + effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); } | |
|
736 | + }, arguments[1] || {}) | |
|
737 | + ); | |
|
738 | + } | |
|
739 | + | |
|
740 | + Effect.SlideUp = function(element) { | |
|
741 | + element = $(element).cleanWhitespace(); | |
|
742 | + var oldInnerBottom = element.down().getStyle('bottom'); | |
|
743 | + return new Effect.Scale(element, window.opera ? 0 : 1, | |
|
744 | + Object.extend({ scaleContent: false, | |
|
745 | + scaleX: false, | |
|
746 | + scaleMode: 'box', | |
|
747 | + scaleFrom: 100, | |
|
748 | + restoreAfterFinish: true, | |
|
749 | + beforeStartInternal: function(effect) { | |
|
750 | + effect.element.makePositioned(); | |
|
751 | + effect.element.down().makePositioned(); | |
|
752 | + if(window.opera) effect.element.setStyle({top: ''}); | |
|
753 | + effect.element.makeClipping().show(); | |
|
754 | + }, | |
|
755 | + afterUpdateInternal: function(effect) { | |
|
756 | + effect.element.down().setStyle({bottom: | |
|
757 | + (effect.dims[0] - effect.element.clientHeight) + 'px' }); | |
|
758 | + }, | |
|
759 | + afterFinishInternal: function(effect) { | |
|
760 | + effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom}); | |
|
761 | + effect.element.down().undoPositioned(); | |
|
762 | + } | |
|
763 | + }, arguments[1] || {}) | |
|
764 | + ); | |
|
765 | + } | |
|
766 | + | |
|
767 | + // Bug in opera makes the TD containing this element expand for a instance after finish | |
|
768 | + Effect.Squish = function(element) { | |
|
769 | + return new Effect.Scale(element, window.opera ? 1 : 0, { | |
|
770 | + restoreAfterFinish: true, | |
|
771 | + beforeSetup: function(effect) { | |
|
772 | + effect.element.makeClipping(); | |
|
773 | + }, | |
|
774 | + afterFinishInternal: function(effect) { | |
|
775 | + effect.element.hide().undoClipping(); | |
|
776 | + } | |
|
777 | + }); | |
|
778 | + } | |
|
779 | + | |
|
780 | + Effect.Grow = function(element) { | |
|
781 | + element = $(element); | |
|
782 | + var options = Object.extend({ | |
|
783 | + direction: 'center', | |
|
784 | + moveTransition: Effect.Transitions.sinoidal, | |
|
785 | + scaleTransition: Effect.Transitions.sinoidal, | |
|
786 | + opacityTransition: Effect.Transitions.full | |
|
787 | + }, arguments[1] || {}); | |
|
788 | + var oldStyle = { | |
|
789 | + top: element.style.top, | |
|
790 | + left: element.style.left, | |
|
791 | + height: element.style.height, | |
|
792 | + width: element.style.width, | |
|
793 | + opacity: element.getInlineOpacity() }; | |
|
794 | + | |
|
795 | + var dims = element.getDimensions(); | |
|
796 | + var initialMoveX, initialMoveY; | |
|
797 | + var moveX, moveY; | |
|
798 | + | |
|
799 | + switch (options.direction) { | |
|
800 | + case 'top-left': | |
|
801 | + initialMoveX = initialMoveY = moveX = moveY = 0; | |
|
802 | + break; | |
|
803 | + case 'top-right': | |
|
804 | + initialMoveX = dims.width; | |
|
805 | + initialMoveY = moveY = 0; | |
|
806 | + moveX = -dims.width; | |
|
807 | + break; | |
|
808 | + case 'bottom-left': | |
|
809 | + initialMoveX = moveX = 0; | |
|
810 | + initialMoveY = dims.height; | |
|
811 | + moveY = -dims.height; | |
|
812 | + break; | |
|
813 | + case 'bottom-right': | |
|
814 | + initialMoveX = dims.width; | |
|
815 | + initialMoveY = dims.height; | |
|
816 | + moveX = -dims.width; | |
|
817 | + moveY = -dims.height; | |
|
818 | + break; | |
|
819 | + case 'center': | |
|
820 | + initialMoveX = dims.width / 2; | |
|
821 | + initialMoveY = dims.height / 2; | |
|
822 | + moveX = -dims.width / 2; | |
|
823 | + moveY = -dims.height / 2; | |
|
824 | + break; | |
|
825 | + } | |
|
826 | + | |
|
827 | + return new Effect.Move(element, { | |
|
828 | + x: initialMoveX, | |
|
829 | + y: initialMoveY, | |
|
830 | + duration: 0.01, | |
|
831 | + beforeSetup: function(effect) { | |
|
832 | + effect.element.hide().makeClipping().makePositioned(); | |
|
833 | + }, | |
|
834 | + afterFinishInternal: function(effect) { | |
|
835 | + new Effect.Parallel( | |
|
836 | + [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }), | |
|
837 | + new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }), | |
|
838 | + new Effect.Scale(effect.element, 100, { | |
|
839 | + scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, | |
|
840 | + sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true}) | |
|
841 | + ], Object.extend({ | |
|
842 | + beforeSetup: function(effect) { | |
|
843 | + effect.effects[0].element.setStyle({height: '0px'}).show(); | |
|
844 | + }, | |
|
845 | + afterFinishInternal: function(effect) { | |
|
846 | + effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); | |
|
847 | + } | |
|
848 | + }, options) | |
|
849 | + ) | |
|
850 | + } | |
|
851 | + }); | |
|
852 | + } | |
|
853 | + | |
|
854 | + Effect.Shrink = function(element) { | |
|
855 | + element = $(element); | |
|
856 | + var options = Object.extend({ | |
|
857 | + direction: 'center', | |
|
858 | + moveTransition: Effect.Transitions.sinoidal, | |
|
859 | + scaleTransition: Effect.Transitions.sinoidal, | |
|
860 | + opacityTransition: Effect.Transitions.none | |
|
861 | + }, arguments[1] || {}); | |
|
862 | + var oldStyle = { | |
|
863 | + top: element.style.top, | |
|
864 | + left: element.style.left, | |
|
865 | + height: element.style.height, | |
|
866 | + width: element.style.width, | |
|
867 | + opacity: element.getInlineOpacity() }; | |
|
868 | + | |
|
869 | + var dims = element.getDimensions(); | |
|
870 | + var moveX, moveY; | |
|
871 | + | |
|
872 | + switch (options.direction) { | |
|
873 | + case 'top-left': | |
|
874 | + moveX = moveY = 0; | |
|
875 | + break; | |
|
876 | + case 'top-right': | |
|
877 | + moveX = dims.width; | |
|
878 | + moveY = 0; | |
|
879 | + break; | |
|
880 | + case 'bottom-left': | |
|
881 | + moveX = 0; | |
|
882 | + moveY = dims.height; | |
|
883 | + break; | |
|
884 | + case 'bottom-right': | |
|
885 | + moveX = dims.width; | |
|
886 | + moveY = dims.height; | |
|
887 | + break; | |
|
888 | + case 'center': | |
|
889 | + moveX = dims.width / 2; | |
|
890 | + moveY = dims.height / 2; | |
|
891 | + break; | |
|
892 | + } | |
|
893 | + | |
|
894 | + return new Effect.Parallel( | |
|
895 | + [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }), | |
|
896 | + new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}), | |
|
897 | + new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }) | |
|
898 | + ], Object.extend({ | |
|
899 | + beforeStartInternal: function(effect) { | |
|
900 | + effect.effects[0].element.makePositioned().makeClipping(); | |
|
901 | + }, | |
|
902 | + afterFinishInternal: function(effect) { | |
|
903 | + effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); } | |
|
904 | + }, options) | |
|
905 | + ); | |
|
906 | + } | |
|
907 | + | |
|
908 | + Effect.Pulsate = function(element) { | |
|
909 | + element = $(element); | |
|
910 | + var options = arguments[1] || {}; | |
|
911 | + var oldOpacity = element.getInlineOpacity(); | |
|
912 | + var transition = options.transition || Effect.Transitions.sinoidal; | |
|
913 | + var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) }; | |
|
914 | + reverser.bind(transition); | |
|
915 | + return new Effect.Opacity(element, | |
|
916 | + Object.extend(Object.extend({ duration: 2.0, from: 0, | |
|
917 | + afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } | |
|
918 | + }, options), {transition: reverser})); | |
|
919 | + } | |
|
920 | + | |
|
921 | + Effect.Fold = function(element) { | |
|
922 | + element = $(element); | |
|
923 | + var oldStyle = { | |
|
924 | + top: element.style.top, | |
|
925 | + left: element.style.left, | |
|
926 | + width: element.style.width, | |
|
927 | + height: element.style.height }; | |
|
928 | + element.makeClipping(); | |
|
929 | + return new Effect.Scale(element, 5, Object.extend({ | |
|
930 | + scaleContent: false, | |
|
931 | + scaleX: false, | |
|
932 | + afterFinishInternal: function(effect) { | |
|
933 | + new Effect.Scale(element, 1, { | |
|
934 | + scaleContent: false, | |
|
935 | + scaleY: false, | |
|
936 | + afterFinishInternal: function(effect) { | |
|
937 | + effect.element.hide().undoClipping().setStyle(oldStyle); | |
|
938 | + } }); | |
|
939 | + }}, arguments[1] || {})); | |
|
940 | + }; | |
|
941 | + | |
|
942 | + Effect.Morph = Class.create(); | |
|
943 | + Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), { | |
|
944 | + initialize: function(element) { | |
|
945 | + this.element = $(element); | |
|
946 | + if(!this.element) throw(Effect._elementDoesNotExistError); | |
|
947 | + var options = Object.extend({ | |
|
948 | + style: '' | |
|
949 | + }, arguments[1] || {}); | |
|
950 | + this.start(options); | |
|
951 | + }, | |
|
952 | + setup: function(){ | |
|
953 | + function parseColor(color){ | |
|
954 | + if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; | |
|
955 | + color = color.parseColor(); | |
|
956 | + return $R(0,2).map(function(i){ | |
|
957 | + return parseInt( color.slice(i*2+1,i*2+3), 16 ) | |
|
958 | + }); | |
|
959 | + } | |
|
960 | + this.transforms = this.options.style.parseStyle().map(function(property){ | |
|
961 | + var originalValue = this.element.getStyle(property[0]); | |
|
962 | + return $H({ | |
|
963 | + style: property[0], | |
|
964 | + originalValue: property[1].unit=='color' ? | |
|
965 | + parseColor(originalValue) : parseFloat(originalValue || 0), | |
|
966 | + targetValue: property[1].unit=='color' ? | |
|
967 | + parseColor(property[1].value) : property[1].value, | |
|
968 | + unit: property[1].unit | |
|
969 | + }); | |
|
970 | + }.bind(this)).reject(function(transform){ | |
|
971 | + return ( | |
|
972 | + (transform.originalValue == transform.targetValue) || | |
|
973 | + ( | |
|
974 | + transform.unit != 'color' && | |
|
975 | + (isNaN(transform.originalValue) || isNaN(transform.targetValue)) | |
|
976 | + ) | |
|
977 | + ) | |
|
978 | + }); | |
|
979 | + }, | |
|
980 | + update: function(position) { | |
|
981 | + var style = $H(), value = null; | |
|
982 | + this.transforms.each(function(transform){ | |
|
983 | + value = transform.unit=='color' ? | |
|
984 | + $R(0,2).inject('#',function(m,v,i){ | |
|
985 | + return m+(Math.round(transform.originalValue[i]+ | |
|
986 | + (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) : | |
|
987 | + transform.originalValue + Math.round( | |
|
988 | + ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit; | |
|
989 | + style[transform.style] = value; | |
|
990 | + }); | |
|
991 | + this.element.setStyle(style); | |
|
992 | + } | |
|
993 | + }); | |
|
994 | + | |
|
995 | + Effect.Transform = Class.create(); | |
|
996 | + Object.extend(Effect.Transform.prototype, { | |
|
997 | + initialize: function(tracks){ | |
|
998 | + this.tracks = []; | |
|
999 | + this.options = arguments[1] || {}; | |
|
1000 | + this.addTracks(tracks); | |
|
1001 | + }, | |
|
1002 | + addTracks: function(tracks){ | |
|
1003 | + tracks.each(function(track){ | |
|
1004 | + var data = $H(track).values().first(); | |
|
1005 | + this.tracks.push($H({ | |
|
1006 | + ids: $H(track).keys().first(), | |
|
1007 | + effect: Effect.Morph, | |
|
1008 | + options: { style: data } | |
|
1009 | + })); | |
|
1010 | + }.bind(this)); | |
|
1011 | + return this; | |
|
1012 | + }, | |
|
1013 | + play: function(){ | |
|
1014 | + return new Effect.Parallel( | |
|
1015 | + this.tracks.map(function(track){ | |
|
1016 | + var elements = [$(track.ids) || $$(track.ids)].flatten(); | |
|
1017 | + return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) }); | |
|
1018 | + }).flatten(), | |
|
1019 | + this.options | |
|
1020 | + ); | |
|
1021 | + } | |
|
1022 | + }); | |
|
1023 | + | |
|
1024 | + Element.CSS_PROPERTIES = ['azimuth', 'backgroundAttachment', 'backgroundColor', 'backgroundImage', | |
|
1025 | + 'backgroundPosition', 'backgroundRepeat', 'borderBottomColor', 'borderBottomStyle', | |
|
1026 | + 'borderBottomWidth', 'borderCollapse', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth', | |
|
1027 | + 'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderSpacing', 'borderTopColor', | |
|
1028 | + 'borderTopStyle', 'borderTopWidth', 'bottom', 'captionSide', 'clear', 'clip', 'color', 'content', | |
|
1029 | + 'counterIncrement', 'counterReset', 'cssFloat', 'cueAfter', 'cueBefore', 'cursor', 'direction', | |
|
1030 | + 'display', 'elevation', 'emptyCells', 'fontFamily', 'fontSize', 'fontSizeAdjust', 'fontStretch', | |
|
1031 | + 'fontStyle', 'fontVariant', 'fontWeight', 'height', 'left', 'letterSpacing', 'lineHeight', | |
|
1032 | + 'listStyleImage', 'listStylePosition', 'listStyleType', 'marginBottom', 'marginLeft', 'marginRight', | |
|
1033 | + 'marginTop', 'markerOffset', 'marks', 'maxHeight', 'maxWidth', 'minHeight', 'minWidth', 'opacity', | |
|
1034 | + 'orphans', 'outlineColor', 'outlineOffset', 'outlineStyle', 'outlineWidth', 'overflowX', 'overflowY', | |
|
1035 | + 'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'page', 'pageBreakAfter', 'pageBreakBefore', | |
|
1036 | + 'pageBreakInside', 'pauseAfter', 'pauseBefore', 'pitch', 'pitchRange', 'position', 'quotes', | |
|
1037 | + 'richness', 'right', 'size', 'speakHeader', 'speakNumeral', 'speakPunctuation', 'speechRate', 'stress', | |
|
1038 | + 'tableLayout', 'textAlign', 'textDecoration', 'textIndent', 'textShadow', 'textTransform', 'top', | |
|
1039 | + 'unicodeBidi', 'verticalAlign', 'visibility', 'voiceFamily', 'volume', 'whiteSpace', 'widows', | |
|
1040 | + 'width', 'wordSpacing', 'zIndex']; | |
|
1041 | + | |
|
1042 | + Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; | |
|
1043 | + | |
|
1044 | + String.prototype.parseStyle = function(){ | |
|
1045 | + var element = Element.extend(document.createElement('div')); | |
|
1046 | + element.innerHTML = '<div style="' + this + '"></div>'; | |
|
1047 | + var style = element.down().style, styleRules = $H(); | |
|
1048 | + | |
|
1049 | + Element.CSS_PROPERTIES.each(function(property){ | |
|
1050 | + if(style[property]) styleRules[property] = style[property]; | |
|
1051 | + }); | |
|
1052 | + | |
|
1053 | + var result = $H(); | |
|
1054 | + | |
|
1055 | + styleRules.each(function(pair){ | |
|
1056 | + var property = pair[0], value = pair[1], unit = null; | |
|
1057 | + | |
|
1058 | + if(value.parseColor('#zzzzzz') != '#zzzzzz') { | |
|
1059 | + value = value.parseColor(); | |
|
1060 | + unit = 'color'; | |
|
1061 | + } else if(Element.CSS_LENGTH.test(value)) | |
|
1062 | + var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/), | |
|
1063 | + value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null; | |
|
1064 | + | |
|
1065 | + result[property.underscore().dasherize()] = $H({ value:value, unit:unit }); | |
|
1066 | + }.bind(this)); | |
|
1067 | + | |
|
1068 | + return result; | |
|
1069 | + }; | |
|
1070 | + | |
|
1071 | + Element.morph = function(element, style) { | |
|
1072 | + new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {})); | |
|
1073 | + return element; | |
|
1074 | + }; | |
|
1075 | + | |
|
1076 | + ['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom', | |
|
1077 | + 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each( | |
|
1078 | + function(f) { Element.Methods[f] = Element[f]; } | |
|
1079 | + ); | |
|
1080 | + | |
|
1081 | + Element.Methods.visualEffect = function(element, effect, options) { | |
|
1082 | + s = effect.gsub(/_/, '-').camelize(); | |
|
1083 | + effect_class = s.charAt(0).toUpperCase() + s.substring(1); | |
|
1084 | + new Effect[effect_class](element, options); | |
|
1085 | + return $(element); | |
|
1086 | + }; | |
|
1087 | + | |
|
1088 | + Element.addMethods(); No newline at end of file |
This diff has been collapsed as it changes many lines, (2515 lines changed) Show them Hide them | |||
@@ -0,0 +1,2515 | |||
|
1 | + /* Prototype JavaScript framework, version 1.5.0 | |
|
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://prototype.conio.net/ | |
|
6 | + * | |
|
7 | + /*--------------------------------------------------------------------------*/ | |
|
8 | + | |
|
9 | + var Prototype = { | |
|
10 | + Version: '1.5.0', | |
|
11 | + BrowserFeatures: { | |
|
12 | + XPath: !!document.evaluate | |
|
13 | + }, | |
|
14 | + | |
|
15 | + ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)', | |
|
16 | + emptyFunction: function() {}, | |
|
17 | + K: function(x) { return x } | |
|
18 | + } | |
|
19 | + | |
|
20 | + var Class = { | |
|
21 | + create: function() { | |
|
22 | + return function() { | |
|
23 | + this.initialize.apply(this, arguments); | |
|
24 | + } | |
|
25 | + } | |
|
26 | + } | |
|
27 | + | |
|
28 | + var Abstract = new Object(); | |
|
29 | + | |
|
30 | + Object.extend = function(destination, source) { | |
|
31 | + for (var property in source) { | |
|
32 | + destination[property] = source[property]; | |
|
33 | + } | |
|
34 | + return destination; | |
|
35 | + } | |
|
36 | + | |
|
37 | + Object.extend(Object, { | |
|
38 | + inspect: function(object) { | |
|
39 | + try { | |
|
40 | + if (object === undefined) return 'undefined'; | |
|
41 | + if (object === null) return 'null'; | |
|
42 | + return object.inspect ? object.inspect() : object.toString(); | |
|
43 | + } catch (e) { | |
|
44 | + if (e instanceof RangeError) return '...'; | |
|
45 | + throw e; | |
|
46 | + } | |
|
47 | + }, | |
|
48 | + | |
|
49 | + keys: function(object) { | |
|
50 | + var keys = []; | |
|
51 | + for (var property in object) | |
|
52 | + keys.push(property); | |
|
53 | + return keys; | |
|
54 | + }, | |
|
55 | + | |
|
56 | + values: function(object) { | |
|
57 | + var values = []; | |
|
58 | + for (var property in object) | |
|
59 | + values.push(object[property]); | |
|
60 | + return values; | |
|
61 | + }, | |
|
62 | + | |
|
63 | + clone: function(object) { | |
|
64 | + return Object.extend({}, object); | |
|
65 | + } | |
|
66 | + }); | |
|
67 | + | |
|
68 | + Function.prototype.bind = function() { | |
|
69 | + var __method = this, args = $A(arguments), object = args.shift(); | |
|
70 | + return function() { | |
|
71 | + return __method.apply(object, args.concat($A(arguments))); | |
|
72 | + } | |
|
73 | + } | |
|
74 | + | |
|
75 | + Function.prototype.bindAsEventListener = function(object) { | |
|
76 | + var __method = this, args = $A(arguments), object = args.shift(); | |
|
77 | + return function(event) { | |
|
78 | + return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments))); | |
|
79 | + } | |
|
80 | + } | |
|
81 | + | |
|
82 | + Object.extend(Number.prototype, { | |
|
83 | + toColorPart: function() { | |
|
84 | + var digits = this.toString(16); | |
|
85 | + if (this < 16) return '0' + digits; | |
|
86 | + return digits; | |
|
87 | + }, | |
|
88 | + | |
|
89 | + succ: function() { | |
|
90 | + return this + 1; | |
|
91 | + }, | |
|
92 | + | |
|
93 | + times: function(iterator) { | |
|
94 | + $R(0, this, true).each(iterator); | |
|
95 | + return this; | |
|
96 | + } | |
|
97 | + }); | |
|
98 | + | |
|
99 | + var Try = { | |
|
100 | + these: function() { | |
|
101 | + var returnValue; | |
|
102 | + | |
|
103 | + for (var i = 0, length = arguments.length; i < length; i++) { | |
|
104 | + var lambda = arguments[i]; | |
|
105 | + try { | |
|
106 | + returnValue = lambda(); | |
|
107 | + break; | |
|
108 | + } catch (e) {} | |
|
109 | + } | |
|
110 | + | |
|
111 | + return returnValue; | |
|
112 | + } | |
|
113 | + } | |
|
114 | + | |
|
115 | + /*--------------------------------------------------------------------------*/ | |
|
116 | + | |
|
117 | + var PeriodicalExecuter = Class.create(); | |
|
118 | + PeriodicalExecuter.prototype = { | |
|
119 | + initialize: function(callback, frequency) { | |
|
120 | + this.callback = callback; | |
|
121 | + this.frequency = frequency; | |
|
122 | + this.currentlyExecuting = false; | |
|
123 | + | |
|
124 | + this.registerCallback(); | |
|
125 | + }, | |
|
126 | + | |
|
127 | + registerCallback: function() { | |
|
128 | + this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); | |
|
129 | + }, | |
|
130 | + | |
|
131 | + stop: function() { | |
|
132 | + if (!this.timer) return; | |
|
133 | + clearInterval(this.timer); | |
|
134 | + this.timer = null; | |
|
135 | + }, | |
|
136 | + | |
|
137 | + onTimerEvent: function() { | |
|
138 | + if (!this.currentlyExecuting) { | |
|
139 | + try { | |
|
140 | + this.currentlyExecuting = true; | |
|
141 | + this.callback(this); | |
|
142 | + } finally { | |
|
143 | + this.currentlyExecuting = false; | |
|
144 | + } | |
|
145 | + } | |
|
146 | + } | |
|
147 | + } | |
|
148 | + String.interpret = function(value){ | |
|
149 | + return value == null ? '' : String(value); | |
|
150 | + } | |
|
151 | + | |
|
152 | + Object.extend(String.prototype, { | |
|
153 | + gsub: function(pattern, replacement) { | |
|
154 | + var result = '', source = this, match; | |
|
155 | + replacement = arguments.callee.prepareReplacement(replacement); | |
|
156 | + | |
|
157 | + while (source.length > 0) { | |
|
158 | + if (match = source.match(pattern)) { | |
|
159 | + result += source.slice(0, match.index); | |
|
160 | + result += String.interpret(replacement(match)); | |
|
161 | + source = source.slice(match.index + match[0].length); | |
|
162 | + } else { | |
|
163 | + result += source, source = ''; | |
|
164 | + } | |
|
165 | + } | |
|
166 | + return result; | |
|
167 | + }, | |
|
168 | + | |
|
169 | + sub: function(pattern, replacement, count) { | |
|
170 | + replacement = this.gsub.prepareReplacement(replacement); | |
|
171 | + count = count === undefined ? 1 : count; | |
|
172 | + | |
|
173 | + return this.gsub(pattern, function(match) { | |
|
174 | + if (--count < 0) return match[0]; | |
|
175 | + return replacement(match); | |
|
176 | + }); | |
|
177 | + }, | |
|
178 | + | |
|
179 | + scan: function(pattern, iterator) { | |
|
180 | + this.gsub(pattern, iterator); | |
|
181 | + return this; | |
|
182 | + }, | |
|
183 | + | |
|
184 | + truncate: function(length, truncation) { | |
|
185 | + length = length || 30; | |
|
186 | + truncation = truncation === undefined ? '...' : truncation; | |
|
187 | + return this.length > length ? | |
|
188 | + this.slice(0, length - truncation.length) + truncation : this; | |
|
189 | + }, | |
|
190 | + | |
|
191 | + strip: function() { | |
|
192 | + return this.replace(/^\s+/, '').replace(/\s+$/, ''); | |
|
193 | + }, | |
|
194 | + | |
|
195 | + stripTags: function() { | |
|
196 | + return this.replace(/<\/?[^>]+>/gi, ''); | |
|
197 | + }, | |
|
198 | + | |
|
199 | + stripScripts: function() { | |
|
200 | + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); | |
|
201 | + }, | |
|
202 | + | |
|
203 | + extractScripts: function() { | |
|
204 | + var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); | |
|
205 | + var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); | |
|
206 | + return (this.match(matchAll) || []).map(function(scriptTag) { | |
|
207 | + return (scriptTag.match(matchOne) || ['', ''])[1]; | |
|
208 | + }); | |
|
209 | + }, | |
|
210 | + | |
|
211 | + evalScripts: function() { | |
|
212 | + return this.extractScripts().map(function(script) { return eval(script) }); | |
|
213 | + }, | |
|
214 | + | |
|
215 | + escapeHTML: function() { | |
|
216 | + var div = document.createElement('div'); | |
|
217 | + var text = document.createTextNode(this); | |
|
218 | + div.appendChild(text); | |
|
219 | + return div.innerHTML; | |
|
220 | + }, | |
|
221 | + | |
|
222 | + unescapeHTML: function() { | |
|
223 | + var div = document.createElement('div'); | |
|
224 | + div.innerHTML = this.stripTags(); | |
|
225 | + return div.childNodes[0] ? (div.childNodes.length > 1 ? | |
|
226 | + $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) : | |
|
227 | + div.childNodes[0].nodeValue) : ''; | |
|
228 | + }, | |
|
229 | + | |
|
230 | + toQueryParams: function(separator) { | |
|
231 | + var match = this.strip().match(/([^?#]*)(#.*)?$/); | |
|
232 | + if (!match) return {}; | |
|
233 | + | |
|
234 | + return match[1].split(separator || '&').inject({}, function(hash, pair) { | |
|
235 | + if ((pair = pair.split('='))[0]) { | |
|
236 | + var name = decodeURIComponent(pair[0]); | |
|
237 | + var value = pair[1] ? decodeURIComponent(pair[1]) : undefined; | |
|
238 | + | |
|
239 | + if (hash[name] !== undefined) { | |
|
240 | + if (hash[name].constructor != Array) | |
|
241 | + hash[name] = [hash[name]]; | |
|
242 | + if (value) hash[name].push(value); | |
|
243 | + } | |
|
244 | + else hash[name] = value; | |
|
245 | + } | |
|
246 | + return hash; | |
|
247 | + }); | |
|
248 | + }, | |
|
249 | + | |
|
250 | + toArray: function() { | |
|
251 | + return this.split(''); | |
|
252 | + }, | |
|
253 | + | |
|
254 | + succ: function() { | |
|
255 | + return this.slice(0, this.length - 1) + | |
|
256 | + String.fromCharCode(this.charCodeAt(this.length - 1) + 1); | |
|
257 | + }, | |
|
258 | + | |
|
259 | + camelize: function() { | |
|
260 | + var parts = this.split('-'), len = parts.length; | |
|
261 | + if (len == 1) return parts[0]; | |
|
262 | + | |
|
263 | + var camelized = this.charAt(0) == '-' | |
|
264 | + ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) | |
|
265 | + : parts[0]; | |
|
266 | + | |
|
267 | + for (var i = 1; i < len; i++) | |
|
268 | + camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); | |
|
269 | + | |
|
270 | + return camelized; | |
|
271 | + }, | |
|
272 | + | |
|
273 | + capitalize: function(){ | |
|
274 | + return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); | |
|
275 | + }, | |
|
276 | + | |
|
277 | + underscore: function() { | |
|
278 | + return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase(); | |
|
279 | + }, | |
|
280 | + | |
|
281 | + dasherize: function() { | |
|
282 | + return this.gsub(/_/,'-'); | |
|
283 | + }, | |
|
284 | + | |
|
285 | + inspect: function(useDoubleQuotes) { | |
|
286 | + var escapedString = this.replace(/\\/g, '\\\\'); | |
|
287 | + if (useDoubleQuotes) | |
|
288 | + return '"' + escapedString.replace(/"/g, '\\"') + '"'; | |
|
289 | + else | |
|
290 | + return "'" + escapedString.replace(/'/g, '\\\'') + "'"; | |
|
291 | + } | |
|
292 | + }); | |
|
293 | + | |
|
294 | + String.prototype.gsub.prepareReplacement = function(replacement) { | |
|
295 | + if (typeof replacement == 'function') return replacement; | |
|
296 | + var template = new Template(replacement); | |
|
297 | + return function(match) { return template.evaluate(match) }; | |
|
298 | + } | |
|
299 | + | |
|
300 | + String.prototype.parseQuery = String.prototype.toQueryParams; | |
|
301 | + | |
|
302 | + var Template = Class.create(); | |
|
303 | + Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; | |
|
304 | + Template.prototype = { | |
|
305 | + initialize: function(template, pattern) { | |
|
306 | + this.template = template.toString(); | |
|
307 | + this.pattern = pattern || Template.Pattern; | |
|
308 | + }, | |
|
309 | + | |
|
310 | + evaluate: function(object) { | |
|
311 | + return this.template.gsub(this.pattern, function(match) { | |
|
312 | + var before = match[1]; | |
|
313 | + if (before == '\\') return match[2]; | |
|
314 | + return before + String.interpret(object[match[3]]); | |
|
315 | + }); | |
|
316 | + } | |
|
317 | + } | |
|
318 | + | |
|
319 | + var $break = new Object(); | |
|
320 | + var $continue = new Object(); | |
|
321 | + | |
|
322 | + var Enumerable = { | |
|
323 | + each: function(iterator) { | |
|
324 | + var index = 0; | |
|
325 | + try { | |
|
326 | + this._each(function(value) { | |
|
327 | + try { | |
|
328 | + iterator(value, index++); | |
|
329 | + } catch (e) { | |
|
330 | + if (e != $continue) throw e; | |
|
331 | + } | |
|
332 | + }); | |
|
333 | + } catch (e) { | |
|
334 | + if (e != $break) throw e; | |
|
335 | + } | |
|
336 | + return this; | |
|
337 | + }, | |
|
338 | + | |
|
339 | + eachSlice: function(number, iterator) { | |
|
340 | + var index = -number, slices = [], array = this.toArray(); | |
|
341 | + while ((index += number) < array.length) | |
|
342 | + slices.push(array.slice(index, index+number)); | |
|
343 | + return slices.map(iterator); | |
|
344 | + }, | |
|
345 | + | |
|
346 | + all: function(iterator) { | |
|
347 | + var result = true; | |
|
348 | + this.each(function(value, index) { | |
|
349 | + result = result && !!(iterator || Prototype.K)(value, index); | |
|
350 | + if (!result) throw $break; | |
|
351 | + }); | |
|
352 | + return result; | |
|
353 | + }, | |
|
354 | + | |
|
355 | + any: function(iterator) { | |
|
356 | + var result = false; | |
|
357 | + this.each(function(value, index) { | |
|
358 | + if (result = !!(iterator || Prototype.K)(value, index)) | |
|
359 | + throw $break; | |
|
360 | + }); | |
|
361 | + return result; | |
|
362 | + }, | |
|
363 | + | |
|
364 | + collect: function(iterator) { | |
|
365 | + var results = []; | |
|
366 | + this.each(function(value, index) { | |
|
367 | + results.push((iterator || Prototype.K)(value, index)); | |
|
368 | + }); | |
|
369 | + return results; | |
|
370 | + }, | |
|
371 | + | |
|
372 | + detect: function(iterator) { | |
|
373 | + var result; | |
|
374 | + this.each(function(value, index) { | |
|
375 | + if (iterator(value, index)) { | |
|
376 | + result = value; | |
|
377 | + throw $break; | |
|
378 | + } | |
|
379 | + }); | |
|
380 | + return result; | |
|
381 | + }, | |
|
382 | + | |
|
383 | + findAll: function(iterator) { | |
|
384 | + var results = []; | |
|
385 | + this.each(function(value, index) { | |
|
386 | + if (iterator(value, index)) | |
|
387 | + results.push(value); | |
|
388 | + }); | |
|
389 | + return results; | |
|
390 | + }, | |
|
391 | + | |
|
392 | + grep: function(pattern, iterator) { | |
|
393 | + var results = []; | |
|
394 | + this.each(function(value, index) { | |
|
395 | + var stringValue = value.toString(); | |
|
396 | + if (stringValue.match(pattern)) | |
|
397 | + results.push((iterator || Prototype.K)(value, index)); | |
|
398 | + }) | |
|
399 | + return results; | |
|
400 | + }, | |
|
401 | + | |
|
402 | + include: function(object) { | |
|
403 | + var found = false; | |
|
404 | + this.each(function(value) { | |
|
405 | + if (value == object) { | |
|
406 | + found = true; | |
|
407 | + throw $break; | |
|
408 | + } | |
|
409 | + }); | |
|
410 | + return found; | |
|
411 | + }, | |
|
412 | + | |
|
413 | + inGroupsOf: function(number, fillWith) { | |
|
414 | + fillWith = fillWith === undefined ? null : fillWith; | |
|
415 | + return this.eachSlice(number, function(slice) { | |
|
416 | + while(slice.length < number) slice.push(fillWith); | |
|
417 | + return slice; | |
|
418 | + }); | |
|
419 | + }, | |
|
420 | + | |
|
421 | + inject: function(memo, iterator) { | |
|
422 | + this.each(function(value, index) { | |
|
423 | + memo = iterator(memo, value, index); | |
|
424 | + }); | |
|
425 | + return memo; | |
|
426 | + }, | |
|
427 | + | |
|
428 | + invoke: function(method) { | |
|
429 | + var args = $A(arguments).slice(1); | |
|
430 | + return this.map(function(value) { | |
|
431 | + return value[method].apply(value, args); | |
|
432 | + }); | |
|
433 | + }, | |
|
434 | + | |
|
435 | + max: function(iterator) { | |
|
436 | + var result; | |
|
437 | + this.each(function(value, index) { | |
|
438 | + value = (iterator || Prototype.K)(value, index); | |
|
439 | + if (result == undefined || value >= result) | |
|
440 | + result = value; | |
|
441 | + }); | |
|
442 | + return result; | |
|
443 | + }, | |
|
444 | + | |
|
445 | + min: function(iterator) { | |
|
446 | + var result; | |
|
447 | + this.each(function(value, index) { | |
|
448 | + value = (iterator || Prototype.K)(value, index); | |
|
449 | + if (result == undefined || value < result) | |
|
450 | + result = value; | |
|
451 | + }); | |
|
452 | + return result; | |
|
453 | + }, | |
|
454 | + | |
|
455 | + partition: function(iterator) { | |
|
456 | + var trues = [], falses = []; | |
|
457 | + this.each(function(value, index) { | |
|
458 | + ((iterator || Prototype.K)(value, index) ? | |
|
459 | + trues : falses).push(value); | |
|
460 | + }); | |
|
461 | + return [trues, falses]; | |
|
462 | + }, | |
|
463 | + | |
|
464 | + pluck: function(property) { | |
|
465 | + var results = []; | |
|
466 | + this.each(function(value, index) { | |
|
467 | + results.push(value[property]); | |
|
468 | + }); | |
|
469 | + return results; | |
|
470 | + }, | |
|
471 | + | |
|
472 | + reject: function(iterator) { | |
|
473 | + var results = []; | |
|
474 | + this.each(function(value, index) { | |
|
475 | + if (!iterator(value, index)) | |
|
476 | + results.push(value); | |
|
477 | + }); | |
|
478 | + return results; | |
|
479 | + }, | |
|
480 | + | |
|
481 | + sortBy: function(iterator) { | |
|
482 | + return this.map(function(value, index) { | |
|
483 | + return {value: value, criteria: iterator(value, index)}; | |
|
484 | + }).sort(function(left, right) { | |
|
485 | + var a = left.criteria, b = right.criteria; | |
|
486 | + return a < b ? -1 : a > b ? 1 : 0; | |
|
487 | + }).pluck('value'); | |
|
488 | + }, | |
|
489 | + | |
|
490 | + toArray: function() { | |
|
491 | + return this.map(); | |
|
492 | + }, | |
|
493 | + | |
|
494 | + zip: function() { | |
|
495 | + var iterator = Prototype.K, args = $A(arguments); | |
|
496 | + if (typeof args.last() == 'function') | |
|
497 | + iterator = args.pop(); | |
|
498 | + | |
|
499 | + var collections = [this].concat(args).map($A); | |
|
500 | + return this.map(function(value, index) { | |
|
501 | + return iterator(collections.pluck(index)); | |
|
502 | + }); | |
|
503 | + }, | |
|
504 | + | |
|
505 | + size: function() { | |
|
506 | + return this.toArray().length; | |
|
507 | + }, | |
|
508 | + | |
|
509 | + inspect: function() { | |
|
510 | + return '#<Enumerable:' + this.toArray().inspect() + '>'; | |
|
511 | + } | |
|
512 | + } | |
|
513 | + | |
|
514 | + Object.extend(Enumerable, { | |
|
515 | + map: Enumerable.collect, | |
|
516 | + find: Enumerable.detect, | |
|
517 | + select: Enumerable.findAll, | |
|
518 | + member: Enumerable.include, | |
|
519 | + entries: Enumerable.toArray | |
|
520 | + }); | |
|
521 | + var $A = Array.from = function(iterable) { | |
|
522 | + if (!iterable) return []; | |
|
523 | + if (iterable.toArray) { | |
|
524 | + return iterable.toArray(); | |
|
525 | + } else { | |
|
526 | + var results = []; | |
|
527 | + for (var i = 0, length = iterable.length; i < length; i++) | |
|
528 | + results.push(iterable[i]); | |
|
529 | + return results; | |
|
530 | + } | |
|
531 | + } | |
|
532 | + | |
|
533 | + Object.extend(Array.prototype, Enumerable); | |
|
534 | + | |
|
535 | + if (!Array.prototype._reverse) | |
|
536 | + Array.prototype._reverse = Array.prototype.reverse; | |
|
537 | + | |
|
538 | + Object.extend(Array.prototype, { | |
|
539 | + _each: function(iterator) { | |
|
540 | + for (var i = 0, length = this.length; i < length; i++) | |
|
541 | + iterator(this[i]); | |
|
542 | + }, | |
|
543 | + | |
|
544 | + clear: function() { | |
|
545 | + this.length = 0; | |
|
546 | + return this; | |
|
547 | + }, | |
|
548 | + | |
|
549 | + first: function() { | |
|
550 | + return this[0]; | |
|
551 | + }, | |
|
552 | + | |
|
553 | + last: function() { | |
|
554 | + return this[this.length - 1]; | |
|
555 | + }, | |
|
556 | + | |
|
557 | + compact: function() { | |
|
558 | + return this.select(function(value) { | |
|
559 | + return value != null; | |
|
560 | + }); | |
|
561 | + }, | |
|
562 | + | |
|
563 | + flatten: function() { | |
|
564 | + return this.inject([], function(array, value) { | |
|
565 | + return array.concat(value && value.constructor == Array ? | |
|
566 | + value.flatten() : [value]); | |
|
567 | + }); | |
|
568 | + }, | |
|
569 | + | |
|
570 | + without: function() { | |
|
571 | + var values = $A(arguments); | |
|
572 | + return this.select(function(value) { | |
|
573 | + return !values.include(value); | |
|
574 | + }); | |
|
575 | + }, | |
|
576 | + | |
|
577 | + indexOf: function(object) { | |
|
578 | + for (var i = 0, length = this.length; i < length; i++) | |
|
579 | + if (this[i] == object) return i; | |
|
580 | + return -1; | |
|
581 | + }, | |
|
582 | + | |
|
583 | + reverse: function(inline) { | |
|
584 | + return (inline !== false ? this : this.toArray())._reverse(); | |
|
585 | + }, | |
|
586 | + | |
|
587 | + reduce: function() { | |
|
588 | + return this.length > 1 ? this : this[0]; | |
|
589 | + }, | |
|
590 | + | |
|
591 | + uniq: function() { | |
|
592 | + return this.inject([], function(array, value) { | |
|
593 | + return array.include(value) ? array : array.concat([value]); | |
|
594 | + }); | |
|
595 | + }, | |
|
596 | + | |
|
597 | + clone: function() { | |
|
598 | + return [].concat(this); | |
|
599 | + }, | |
|
600 | + | |
|
601 | + size: function() { | |
|
602 | + return this.length; | |
|
603 | + }, | |
|
604 | + | |
|
605 | + inspect: function() { | |
|
606 | + return '[' + this.map(Object.inspect).join(', ') + ']'; | |
|
607 | + } | |
|
608 | + }); | |
|
609 | + | |
|
610 | + Array.prototype.toArray = Array.prototype.clone; | |
|
611 | + | |
|
612 | + function $w(string){ | |
|
613 | + string = string.strip(); | |
|
614 | + return string ? string.split(/\s+/) : []; | |
|
615 | + } | |
|
616 | + | |
|
617 | + if(window.opera){ | |
|
618 | + Array.prototype.concat = function(){ | |
|
619 | + var array = []; | |
|
620 | + for(var i = 0, length = this.length; i < length; i++) array.push(this[i]); | |
|
621 | + for(var i = 0, length = arguments.length; i < length; i++) { | |
|
622 | + if(arguments[i].constructor == Array) { | |
|
623 | + for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) | |
|
624 | + array.push(arguments[i][j]); | |
|
625 | + } else { | |
|
626 | + array.push(arguments[i]); | |
|
627 | + } | |
|
628 | + } | |
|
629 | + return array; | |
|
630 | + } | |
|
631 | + } | |
|
632 | + var Hash = function(obj) { | |
|
633 | + Object.extend(this, obj || {}); | |
|
634 | + }; | |
|
635 | + | |
|
636 | + Object.extend(Hash, { | |
|
637 | + toQueryString: function(obj) { | |
|
638 | + var parts = []; | |
|
639 | + | |
|
640 | + this.prototype._each.call(obj, function(pair) { | |
|
641 | + if (!pair.key) return; | |
|
642 | + | |
|
643 | + if (pair.value && pair.value.constructor == Array) { | |
|
644 | + var values = pair.value.compact(); | |
|
645 | + if (values.length < 2) pair.value = values.reduce(); | |
|
646 | + else { | |
|
647 | + key = encodeURIComponent(pair.key); | |
|
648 | + values.each(function(value) { | |
|
649 | + value = value != undefined ? encodeURIComponent(value) : ''; | |
|
650 | + parts.push(key + '=' + encodeURIComponent(value)); | |
|
651 | + }); | |
|
652 | + return; | |
|
653 | + } | |
|
654 | + } | |
|
655 | + if (pair.value == undefined) pair[1] = ''; | |
|
656 | + parts.push(pair.map(encodeURIComponent).join('=')); | |
|
657 | + }); | |
|
658 | + | |
|
659 | + return parts.join('&'); | |
|
660 | + } | |
|
661 | + }); | |
|
662 | + | |
|
663 | + Object.extend(Hash.prototype, Enumerable); | |
|
664 | + Object.extend(Hash.prototype, { | |
|
665 | + _each: function(iterator) { | |
|
666 | + for (var key in this) { | |
|
667 | + var value = this[key]; | |
|
668 | + if (value && value == Hash.prototype[key]) continue; | |
|
669 | + | |
|
670 | + var pair = [key, value]; | |
|
671 | + pair.key = key; | |
|
672 | + pair.value = value; | |
|
673 | + iterator(pair); | |
|
674 | + } | |
|
675 | + }, | |
|
676 | + | |
|
677 | + keys: function() { | |
|
678 | + return this.pluck('key'); | |
|
679 | + }, | |
|
680 | + | |
|
681 | + values: function() { | |
|
682 | + return this.pluck('value'); | |
|
683 | + }, | |
|
684 | + | |
|
685 | + merge: function(hash) { | |
|
686 | + return $H(hash).inject(this, function(mergedHash, pair) { | |
|
687 | + mergedHash[pair.key] = pair.value; | |
|
688 | + return mergedHash; | |
|
689 | + }); | |
|
690 | + }, | |
|
691 | + | |
|
692 | + remove: function() { | |
|
693 | + var result; | |
|
694 | + for(var i = 0, length = arguments.length; i < length; i++) { | |
|
695 | + var value = this[arguments[i]]; | |
|
696 | + if (value !== undefined){ | |
|
697 | + if (result === undefined) result = value; | |
|
698 | + else { | |
|
699 | + if (result.constructor != Array) result = [result]; | |
|
700 | + result.push(value) | |
|
701 | + } | |
|
702 | + } | |
|
703 | + delete this[arguments[i]]; | |
|
704 | + } | |
|
705 | + return result; | |
|
706 | + }, | |
|
707 | + | |
|
708 | + toQueryString: function() { | |
|
709 | + return Hash.toQueryString(this); | |
|
710 | + }, | |
|
711 | + | |
|
712 | + inspect: function() { | |
|
713 | + return '#<Hash:{' + this.map(function(pair) { | |
|
714 | + return pair.map(Object.inspect).join(': '); | |
|
715 | + }).join(', ') + '}>'; | |
|
716 | + } | |
|
717 | + }); | |
|
718 | + | |
|
719 | + function $H(object) { | |
|
720 | + if (object && object.constructor == Hash) return object; | |
|
721 | + return new Hash(object); | |
|
722 | + }; | |
|
723 | + ObjectRange = Class.create(); | |
|
724 | + Object.extend(ObjectRange.prototype, Enumerable); | |
|
725 | + Object.extend(ObjectRange.prototype, { | |
|
726 | + initialize: function(start, end, exclusive) { | |
|
727 | + this.start = start; | |
|
728 | + this.end = end; | |
|
729 | + this.exclusive = exclusive; | |
|
730 | + }, | |
|
731 | + | |
|
732 | + _each: function(iterator) { | |
|
733 | + var value = this.start; | |
|
734 | + while (this.include(value)) { | |
|
735 | + iterator(value); | |
|
736 | + value = value.succ(); | |
|
737 | + } | |
|
738 | + }, | |
|
739 | + | |
|
740 | + include: function(value) { | |
|
741 | + if (value < this.start) | |
|
742 | + return false; | |
|
743 | + if (this.exclusive) | |
|
744 | + return value < this.end; | |
|
745 | + return value <= this.end; | |
|
746 | + } | |
|
747 | + }); | |
|
748 | + | |
|
749 | + var $R = function(start, end, exclusive) { | |
|
750 | + return new ObjectRange(start, end, exclusive); | |
|
751 | + } | |
|
752 | + | |
|
753 | + var Ajax = { | |
|
754 | + getTransport: function() { | |
|
755 | + return Try.these( | |
|
756 | + function() {return new XMLHttpRequest()}, | |
|
757 | + function() {return new ActiveXObject('Msxml2.XMLHTTP')}, | |
|
758 | + function() {return new ActiveXObject('Microsoft.XMLHTTP')} | |
|
759 | + ) || false; | |
|
760 | + }, | |
|
761 | + | |
|
762 | + activeRequestCount: 0 | |
|
763 | + } | |
|
764 | + | |
|
765 | + Ajax.Responders = { | |
|
766 | + responders: [], | |
|
767 | + | |
|
768 | + _each: function(iterator) { | |
|
769 | + this.responders._each(iterator); | |
|
770 | + }, | |
|
771 | + | |
|
772 | + register: function(responder) { | |
|
773 | + if (!this.include(responder)) | |
|
774 | + this.responders.push(responder); | |
|
775 | + }, | |
|
776 | + | |
|
777 | + unregister: function(responder) { | |
|
778 | + this.responders = this.responders.without(responder); | |
|
779 | + }, | |
|
780 | + | |
|
781 | + dispatch: function(callback, request, transport, json) { | |
|
782 | + this.each(function(responder) { | |
|
783 | + if (typeof responder[callback] == 'function') { | |
|
784 | + try { | |
|
785 | + responder[callback].apply(responder, [request, transport, json]); | |
|
786 | + } catch (e) {} | |
|
787 | + } | |
|
788 | + }); | |
|
789 | + } | |
|
790 | + }; | |
|
791 | + | |
|
792 | + Object.extend(Ajax.Responders, Enumerable); | |
|
793 | + | |
|
794 | + Ajax.Responders.register({ | |
|
795 | + onCreate: function() { | |
|
796 | + Ajax.activeRequestCount++; | |
|
797 | + }, | |
|
798 | + onComplete: function() { | |
|
799 | + Ajax.activeRequestCount--; | |
|
800 | + } | |
|
801 | + }); | |
|
802 | + | |
|
803 | + Ajax.Base = function() {}; | |
|
804 | + Ajax.Base.prototype = { | |
|
805 | + setOptions: function(options) { | |
|
806 | + this.options = { | |
|
807 | + method: 'post', | |
|
808 | + asynchronous: true, | |
|
809 | + contentType: 'application/x-www-form-urlencoded', | |
|
810 | + encoding: 'UTF-8', | |
|
811 | + parameters: '' | |
|
812 | + } | |
|
813 | + Object.extend(this.options, options || {}); | |
|
814 | + | |
|
815 | + this.options.method = this.options.method.toLowerCase(); | |
|
816 | + if (typeof this.options.parameters == 'string') | |
|
817 | + this.options.parameters = this.options.parameters.toQueryParams(); | |
|
818 | + } | |
|
819 | + } | |
|
820 | + | |
|
821 | + Ajax.Request = Class.create(); | |
|
822 | + Ajax.Request.Events = | |
|
823 | + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; | |
|
824 | + | |
|
825 | + Ajax.Request.prototype = Object.extend(new Ajax.Base(), { | |
|
826 | + _complete: false, | |
|
827 | + | |
|
828 | + initialize: function(url, options) { | |
|
829 | + this.transport = Ajax.getTransport(); | |
|
830 | + this.setOptions(options); | |
|
831 | + this.request(url); | |
|
832 | + }, | |
|
833 | + | |
|
834 | + request: function(url) { | |
|
835 | + this.url = url; | |
|
836 | + this.method = this.options.method; | |
|
837 | + var params = this.options.parameters; | |
|
838 | + | |
|
839 | + if (!['get', 'post'].include(this.method)) { | |
|
840 | + // simulate other verbs over post | |
|
841 | + params['_method'] = this.method; | |
|
842 | + this.method = 'post'; | |
|
843 | + } | |
|
844 | + | |
|
845 | + params = Hash.toQueryString(params); | |
|
846 | + if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_=' | |
|
847 | + | |
|
848 | + // when GET, append parameters to URL | |
|
849 | + if (this.method == 'get' && params) | |
|
850 | + this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params; | |
|
851 | + | |
|
852 | + try { | |
|
853 | + Ajax.Responders.dispatch('onCreate', this, this.transport); | |
|
854 | + | |
|
855 | + this.transport.open(this.method.toUpperCase(), this.url, | |
|
856 | + this.options.asynchronous); | |
|
857 | + | |
|
858 | + if (this.options.asynchronous) | |
|
859 | + setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10); | |
|
860 | + | |
|
861 | + this.transport.onreadystatechange = this.onStateChange.bind(this); | |
|
862 | + this.setRequestHeaders(); | |
|
863 | + | |
|
864 | + var body = this.method == 'post' ? (this.options.postBody || params) : null; | |
|
865 | + | |
|
866 | + this.transport.send(body); | |
|
867 | + | |
|
868 | + /* Force Firefox to handle ready state 4 for synchronous requests */ | |
|
869 | + if (!this.options.asynchronous && this.transport.overrideMimeType) | |
|
870 | + this.onStateChange(); | |
|
871 | + | |
|
872 | + } | |
|
873 | + catch (e) { | |
|
874 | + this.dispatchException(e); | |
|
875 | + } | |
|
876 | + }, | |
|
877 | + | |
|
878 | + onStateChange: function() { | |
|
879 | + var readyState = this.transport.readyState; | |
|
880 | + if (readyState > 1 && !((readyState == 4) && this._complete)) | |
|
881 | + this.respondToReadyState(this.transport.readyState); | |
|
882 | + }, | |
|
883 | + | |
|
884 | + setRequestHeaders: function() { | |
|
885 | + var headers = { | |
|
886 | + 'X-Requested-With': 'XMLHttpRequest', | |
|
887 | + 'X-Prototype-Version': Prototype.Version, | |
|
888 | + 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' | |
|
889 | + }; | |
|
890 | + | |
|
891 | + if (this.method == 'post') { | |
|
892 | + headers['Content-type'] = this.options.contentType + | |
|
893 | + (this.options.encoding ? '; charset=' + this.options.encoding : ''); | |
|
894 | + | |
|
895 | + /* Force "Connection: close" for older Mozilla browsers to work | |
|
896 | + * around a bug where XMLHttpRequest sends an incorrect | |
|
897 | + * Content-length header. See Mozilla Bugzilla #246651. | |
|
898 | + */ | |
|
899 | + if (this.transport.overrideMimeType && | |
|
900 | + (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) | |
|
901 | + headers['Connection'] = 'close'; | |
|
902 | + } | |
|
903 | + | |
|
904 | + // user-defined headers | |
|
905 | + if (typeof this.options.requestHeaders == 'object') { | |
|
906 | + var extras = this.options.requestHeaders; | |
|
907 | + | |
|
908 | + if (typeof extras.push == 'function') | |
|
909 | + for (var i = 0, length = extras.length; i < length; i += 2) | |
|
910 | + headers[extras[i]] = extras[i+1]; | |
|
911 | + else | |
|
912 | + $H(extras).each(function(pair) { headers[pair.key] = pair.value }); | |
|
913 | + } | |
|
914 | + | |
|
915 | + for (var name in headers) | |
|
916 | + this.transport.setRequestHeader(name, headers[name]); | |
|
917 | + }, | |
|
918 | + | |
|
919 | + success: function() { | |
|
920 | + return !this.transport.status | |
|
921 | + || (this.transport.status >= 200 && this.transport.status < 300); | |
|
922 | + }, | |
|
923 | + | |
|
924 | + respondToReadyState: function(readyState) { | |
|
925 | + var state = Ajax.Request.Events[readyState]; | |
|
926 | + var transport = this.transport, json = this.evalJSON(); | |
|
927 | + | |
|
928 | + if (state == 'Complete') { | |
|
929 | + try { | |
|
930 | + this._complete = true; | |
|
931 | + (this.options['on' + this.transport.status] | |
|
932 | + || this.options['on' + (this.success() ? 'Success' : 'Failure')] | |
|
933 | + || Prototype.emptyFunction)(transport, json); | |
|
934 | + } catch (e) { | |
|
935 | + this.dispatchException(e); | |
|
936 | + } | |
|
937 | + | |
|
938 | + if ((this.getHeader('Content-type') || 'text/javascript').strip(). | |
|
939 | + match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i)) | |
|
940 | + this.evalResponse(); | |
|
941 | + } | |
|
942 | + | |
|
943 | + try { | |
|
944 | + (this.options['on' + state] || Prototype.emptyFunction)(transport, json); | |
|
945 | + Ajax.Responders.dispatch('on' + state, this, transport, json); | |
|
946 | + } catch (e) { | |
|
947 | + this.dispatchException(e); | |
|
948 | + } | |
|
949 | + | |
|
950 | + if (state == 'Complete') { | |
|
951 | + // avoid memory leak in MSIE: clean up | |
|
952 | + this.transport.onreadystatechange = Prototype.emptyFunction; | |
|
953 | + } | |
|
954 | + }, | |
|
955 | + | |
|
956 | + getHeader: function(name) { | |
|
957 | + try { | |
|
958 | + return this.transport.getResponseHeader(name); | |
|
959 | + } catch (e) { return null } | |
|
960 | + }, | |
|
961 | + | |
|
962 | + evalJSON: function() { | |
|
963 | + try { | |
|
964 | + var json = this.getHeader('X-JSON'); | |
|
965 | + return json ? eval('(' + json + ')') : null; | |
|
966 | + } catch (e) { return null } | |
|
967 | + }, | |
|
968 | + | |
|
969 | + evalResponse: function() { | |
|
970 | + try { | |
|
971 | + return eval(this.transport.responseText); | |
|
972 | + } catch (e) { | |
|
973 | + this.dispatchException(e); | |
|
974 | + } | |
|
975 | + }, | |
|
976 | + | |
|
977 | + dispatchException: function(exception) { | |
|
978 | + (this.options.onException || Prototype.emptyFunction)(this, exception); | |
|
979 | + Ajax.Responders.dispatch('onException', this, exception); | |
|
980 | + } | |
|
981 | + }); | |
|
982 | + | |
|
983 | + Ajax.Updater = Class.create(); | |
|
984 | + | |
|
985 | + Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { | |
|
986 | + initialize: function(container, url, options) { | |
|
987 | + this.container = { | |
|
988 | + success: (container.success || container), | |
|
989 | + failure: (container.failure || (container.success ? null : container)) | |
|
990 | + } | |
|
991 | + | |
|
992 | + this.transport = Ajax.getTransport(); | |
|
993 | + this.setOptions(options); | |
|
994 | + | |
|
995 | + var onComplete = this.options.onComplete || Prototype.emptyFunction; | |
|
996 | + this.options.onComplete = (function(transport, param) { | |
|
997 | + this.updateContent(); | |
|
998 | + onComplete(transport, param); | |
|
999 | + }).bind(this); | |
|
1000 | + | |
|
1001 | + this.request(url); | |
|
1002 | + }, | |
|
1003 | + | |
|
1004 | + updateContent: function() { | |
|
1005 | + var receiver = this.container[this.success() ? 'success' : 'failure']; | |
|
1006 | + var response = this.transport.responseText; | |
|
1007 | + | |
|
1008 | + if (!this.options.evalScripts) response = response.stripScripts(); | |
|
1009 | + | |
|
1010 | + if (receiver = $(receiver)) { | |
|
1011 | + if (this.options.insertion) | |
|
1012 | + new this.options.insertion(receiver, response); | |
|
1013 | + else | |
|
1014 | + receiver.update(response); | |
|
1015 | + } | |
|
1016 | + | |
|
1017 | + if (this.success()) { | |
|
1018 | + if (this.onComplete) | |
|
1019 | + setTimeout(this.onComplete.bind(this), 10); | |
|
1020 | + } | |
|
1021 | + } | |
|
1022 | + }); | |
|
1023 | + | |
|
1024 | + Ajax.PeriodicalUpdater = Class.create(); | |
|
1025 | + Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { | |
|
1026 | + initialize: function(container, url, options) { | |
|
1027 | + this.setOptions(options); | |
|
1028 | + this.onComplete = this.options.onComplete; | |
|
1029 | + | |
|
1030 | + this.frequency = (this.options.frequency || 2); | |
|
1031 | + this.decay = (this.options.decay || 1); | |
|
1032 | + | |
|
1033 | + this.updater = {}; | |
|
1034 | + this.container = container; | |
|
1035 | + this.url = url; | |
|
1036 | + | |
|
1037 | + this.start(); | |
|
1038 | + }, | |
|
1039 | + | |
|
1040 | + start: function() { | |
|
1041 | + this.options.onComplete = this.updateComplete.bind(this); | |
|
1042 | + this.onTimerEvent(); | |
|
1043 | + }, | |
|
1044 | + | |
|
1045 | + stop: function() { | |
|
1046 | + this.updater.options.onComplete = undefined; | |
|
1047 | + clearTimeout(this.timer); | |
|
1048 | + (this.onComplete || Prototype.emptyFunction).apply(this, arguments); | |
|
1049 | + }, | |
|
1050 | + | |
|
1051 | + updateComplete: function(request) { | |
|
1052 | + if (this.options.decay) { | |
|
1053 | + this.decay = (request.responseText == this.lastText ? | |
|
1054 | + this.decay * this.options.decay : 1); | |
|
1055 | + | |
|
1056 | + this.lastText = request.responseText; | |
|
1057 | + } | |
|
1058 | + this.timer = setTimeout(this.onTimerEvent.bind(this), | |
|
1059 | + this.decay * this.frequency * 1000); | |
|
1060 | + }, | |
|
1061 | + | |
|
1062 | + onTimerEvent: function() { | |
|
1063 | + this.updater = new Ajax.Updater(this.container, this.url, this.options); | |
|
1064 | + } | |
|
1065 | + }); | |
|
1066 | + function $(element) { | |
|
1067 | + if (arguments.length > 1) { | |
|
1068 | + for (var i = 0, elements = [], length = arguments.length; i < length; i++) | |
|
1069 | + elements.push($(arguments[i])); | |
|
1070 | + return elements; | |
|
1071 | + } | |
|
1072 | + if (typeof element == 'string') | |
|
1073 | + element = document.getElementById(element); | |
|
1074 | + return Element.extend(element); | |
|
1075 | + } | |
|
1076 | + | |
|
1077 | + if (Prototype.BrowserFeatures.XPath) { | |
|
1078 | + document._getElementsByXPath = function(expression, parentElement) { | |
|
1079 | + var results = []; | |
|
1080 | + var query = document.evaluate(expression, $(parentElement) || document, | |
|
1081 | + null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); | |
|
1082 | + for (var i = 0, length = query.snapshotLength; i < length; i++) | |
|
1083 | + results.push(query.snapshotItem(i)); | |
|
1084 | + return results; | |
|
1085 | + }; | |
|
1086 | + } | |
|
1087 | + | |
|
1088 | + document.getElementsByClassName = function(className, parentElement) { | |
|
1089 | + if (Prototype.BrowserFeatures.XPath) { | |
|
1090 | + var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]"; | |
|
1091 | + return document._getElementsByXPath(q, parentElement); | |
|
1092 | + } else { | |
|
1093 | + var children = ($(parentElement) || document.body).getElementsByTagName('*'); | |
|
1094 | + var elements = [], child; | |
|
1095 | + for (var i = 0, length = children.length; i < length; i++) { | |
|
1096 | + child = children[i]; | |
|
1097 | + if (Element.hasClassName(child, className)) | |
|
1098 | + elements.push(Element.extend(child)); | |
|
1099 | + } | |
|
1100 | + return elements; | |
|
1101 | + } | |
|
1102 | + }; | |
|
1103 | + | |
|
1104 | + /*--------------------------------------------------------------------------*/ | |
|
1105 | + | |
|
1106 | + if (!window.Element) | |
|
1107 | + var Element = new Object(); | |
|
1108 | + | |
|
1109 | + Element.extend = function(element) { | |
|
1110 | + if (!element || _nativeExtensions || element.nodeType == 3) return element; | |
|
1111 | + | |
|
1112 | + if (!element._extended && element.tagName && element != window) { | |
|
1113 | + var methods = Object.clone(Element.Methods), cache = Element.extend.cache; | |
|
1114 | + | |
|
1115 | + if (element.tagName == 'FORM') | |
|
1116 | + Object.extend(methods, Form.Methods); | |
|
1117 | + if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName)) | |
|
1118 | + Object.extend(methods, Form.Element.Methods); | |
|
1119 | + | |
|
1120 | + Object.extend(methods, Element.Methods.Simulated); | |
|
1121 | + | |
|
1122 | + for (var property in methods) { | |
|
1123 | + var value = methods[property]; | |
|
1124 | + if (typeof value == 'function' && !(property in element)) | |
|
1125 | + element[property] = cache.findOrStore(value); | |
|
1126 | + } | |
|
1127 | + } | |
|
1128 | + | |
|
1129 | + element._extended = true; | |
|
1130 | + return element; | |
|
1131 | + }; | |
|
1132 | + | |
|
1133 | + Element.extend.cache = { | |
|
1134 | + findOrStore: function(value) { | |
|
1135 | + return this[value] = this[value] || function() { | |
|
1136 | + return value.apply(null, [this].concat($A(arguments))); | |
|
1137 | + } | |
|
1138 | + } | |
|
1139 | + }; | |
|
1140 | + | |
|
1141 | + Element.Methods = { | |
|
1142 | + visible: function(element) { | |
|
1143 | + return $(element).style.display != 'none'; | |
|
1144 | + }, | |
|
1145 | + | |
|
1146 | + toggle: function(element) { | |
|
1147 | + element = $(element); | |
|
1148 | + Element[Element.visible(element) ? 'hide' : 'show'](element); | |
|
1149 | + return element; | |
|
1150 | + }, | |
|
1151 | + | |
|
1152 | + hide: function(element) { | |
|
1153 | + $(element).style.display = 'none'; | |
|
1154 | + return element; | |
|
1155 | + }, | |
|
1156 | + | |
|
1157 | + show: function(element) { | |
|
1158 | + $(element).style.display = ''; | |
|
1159 | + return element; | |
|
1160 | + }, | |
|
1161 | + | |
|
1162 | + remove: function(element) { | |
|
1163 | + element = $(element); | |
|
1164 | + element.parentNode.removeChild(element); | |
|
1165 | + return element; | |
|
1166 | + }, | |
|
1167 | + | |
|
1168 | + update: function(element, html) { | |
|
1169 | + html = typeof html == 'undefined' ? '' : html.toString(); | |
|
1170 | + $(element).innerHTML = html.stripScripts(); | |
|
1171 | + setTimeout(function() {html.evalScripts()}, 10); | |
|
1172 | + return element; | |
|
1173 | + }, | |
|
1174 | + | |
|
1175 | + replace: function(element, html) { | |
|
1176 | + element = $(element); | |
|
1177 | + html = typeof html == 'undefined' ? '' : html.toString(); | |
|
1178 | + if (element.outerHTML) { | |
|
1179 | + element.outerHTML = html.stripScripts(); | |
|
1180 | + } else { | |
|
1181 | + var range = element.ownerDocument.createRange(); | |
|
1182 | + range.selectNodeContents(element); | |
|
1183 | + element.parentNode.replaceChild( | |
|
1184 | + range.createContextualFragment(html.stripScripts()), element); | |
|
1185 | + } | |
|
1186 | + setTimeout(function() {html.evalScripts()}, 10); | |
|
1187 | + return element; | |
|
1188 | + }, | |
|
1189 | + | |
|
1190 | + inspect: function(element) { | |
|
1191 | + element = $(element); | |
|
1192 | + var result = '<' + element.tagName.toLowerCase(); | |
|
1193 | + $H({'id': 'id', 'className': 'class'}).each(function(pair) { | |
|
1194 | + var property = pair.first(), attribute = pair.last(); | |
|
1195 | + var value = (element[property] || '').toString(); | |
|
1196 | + if (value) result += ' ' + attribute + '=' + value.inspect(true); | |
|
1197 | + }); | |
|
1198 | + return result + '>'; | |
|
1199 | + }, | |
|
1200 | + | |
|
1201 | + recursivelyCollect: function(element, property) { | |
|
1202 | + element = $(element); | |
|
1203 | + var elements = []; | |
|
1204 | + while (element = element[property]) | |
|
1205 | + if (element.nodeType == 1) | |
|
1206 | + elements.push(Element.extend(element)); | |
|
1207 | + return elements; | |
|
1208 | + }, | |
|
1209 | + | |
|
1210 | + ancestors: function(element) { | |
|
1211 | + return $(element).recursivelyCollect('parentNode'); | |
|
1212 | + }, | |
|
1213 | + | |
|
1214 | + descendants: function(element) { | |
|
1215 | + return $A($(element).getElementsByTagName('*')); | |
|
1216 | + }, | |
|
1217 | + | |
|
1218 | + immediateDescendants: function(element) { | |
|
1219 | + if (!(element = $(element).firstChild)) return []; | |
|
1220 | + while (element && element.nodeType != 1) element = element.nextSibling; | |
|
1221 | + if (element) return [element].concat($(element).nextSiblings()); | |
|
1222 | + return []; | |
|
1223 | + }, | |
|
1224 | + | |
|
1225 | + previousSiblings: function(element) { | |
|
1226 | + return $(element).recursivelyCollect('previousSibling'); | |
|
1227 | + }, | |
|
1228 | + | |
|
1229 | + nextSiblings: function(element) { | |
|
1230 | + return $(element).recursivelyCollect('nextSibling'); | |
|
1231 | + }, | |
|
1232 | + | |
|
1233 | + siblings: function(element) { | |
|
1234 | + element = $(element); | |
|
1235 | + return element.previousSiblings().reverse().concat(element.nextSiblings()); | |
|
1236 | + }, | |
|
1237 | + | |
|
1238 | + match: function(element, selector) { | |
|
1239 | + if (typeof selector == 'string') | |
|
1240 | + selector = new Selector(selector); | |
|
1241 | + return selector.match($(element)); | |
|
1242 | + }, | |
|
1243 | + | |
|
1244 | + up: function(element, expression, index) { | |
|
1245 | + return Selector.findElement($(element).ancestors(), expression, index); | |
|
1246 | + }, | |
|
1247 | + | |
|
1248 | + down: function(element, expression, index) { | |
|
1249 | + return Selector.findElement($(element).descendants(), expression, index); | |
|
1250 | + }, | |
|
1251 | + | |
|
1252 | + previous: function(element, expression, index) { | |
|
1253 | + return Selector.findElement($(element).previousSiblings(), expression, index); | |
|
1254 | + }, | |
|
1255 | + | |
|
1256 | + next: function(element, expression, index) { | |
|
1257 | + return Selector.findElement($(element).nextSiblings(), expression, index); | |
|
1258 | + }, | |
|
1259 | + | |
|
1260 | + getElementsBySelector: function() { | |
|
1261 | + var args = $A(arguments), element = $(args.shift()); | |
|
1262 | + return Selector.findChildElements(element, args); | |
|
1263 | + }, | |
|
1264 | + | |
|
1265 | + getElementsByClassName: function(element, className) { | |
|
1266 | + return document.getElementsByClassName(className, element); | |
|
1267 | + }, | |
|
1268 | + | |
|
1269 | + readAttribute: function(element, name) { | |
|
1270 | + element = $(element); | |
|
1271 | + if (document.all && !window.opera) { | |
|
1272 | + var t = Element._attributeTranslations; | |
|
1273 | + if (t.values[name]) return t.values[name](element, name); | |
|
1274 | + if (t.names[name]) name = t.names[name]; | |
|
1275 | + var attribute = element.attributes[name]; | |
|
1276 | + if(attribute) return attribute.nodeValue; | |
|
1277 | + } | |
|
1278 | + return element.getAttribute(name); | |
|
1279 | + }, | |
|
1280 | + | |
|
1281 | + getHeight: function(element) { | |
|
1282 | + return $(element).getDimensions().height; | |
|
1283 | + }, | |
|
1284 | + | |
|
1285 | + getWidth: function(element) { | |
|
1286 | + return $(element).getDimensions().width; | |
|
1287 | + }, | |
|
1288 | + | |
|
1289 | + classNames: function(element) { | |
|
1290 | + return new Element.ClassNames(element); | |
|
1291 | + }, | |
|
1292 | + | |
|
1293 | + hasClassName: function(element, className) { | |
|
1294 | + if (!(element = $(element))) return; | |
|
1295 | + var elementClassName = element.className; | |
|
1296 | + if (elementClassName.length == 0) return false; | |
|
1297 | + if (elementClassName == className || | |
|
1298 | + elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) | |
|
1299 | + return true; | |
|
1300 | + return false; | |
|
1301 | + }, | |
|
1302 | + | |
|
1303 | + addClassName: function(element, className) { | |
|
1304 | + if (!(element = $(element))) return; | |
|
1305 | + Element.classNames(element).add(className); | |
|
1306 | + return element; | |
|
1307 | + }, | |
|
1308 | + | |
|
1309 | + removeClassName: function(element, className) { | |
|
1310 | + if (!(element = $(element))) return; | |
|
1311 | + Element.classNames(element).remove(className); | |
|
1312 | + return element; | |
|
1313 | + }, | |
|
1314 | + | |
|
1315 | + toggleClassName: function(element, className) { | |
|
1316 | + if (!(element = $(element))) return; | |
|
1317 | + Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className); | |
|
1318 | + return element; | |
|
1319 | + }, | |
|
1320 | + | |
|
1321 | + observe: function() { | |
|
1322 | + Event.observe.apply(Event, arguments); | |
|
1323 | + return $A(arguments).first(); | |
|
1324 | + }, | |
|
1325 | + | |
|
1326 | + stopObserving: function() { | |
|
1327 | + Event.stopObserving.apply(Event, arguments); | |
|
1328 | + return $A(arguments).first(); | |
|
1329 | + }, | |
|
1330 | + | |
|
1331 | + // removes whitespace-only text node children | |
|
1332 | + cleanWhitespace: function(element) { | |
|
1333 | + element = $(element); | |
|
1334 | + var node = element.firstChild; | |
|
1335 | + while (node) { | |
|
1336 | + var nextNode = node.nextSibling; | |
|
1337 | + if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) | |
|
1338 | + element.removeChild(node); | |
|
1339 | + node = nextNode; | |
|
1340 | + } | |
|
1341 | + return element; | |
|
1342 | + }, | |
|
1343 | + | |
|
1344 | + empty: function(element) { | |
|
1345 | + return $(element).innerHTML.match(/^\s*$/); | |
|
1346 | + }, | |
|
1347 | + | |
|
1348 | + descendantOf: function(element, ancestor) { | |
|
1349 | + element = $(element), ancestor = $(ancestor); | |
|
1350 | + while (element = element.parentNode) | |
|
1351 | + if (element == ancestor) return true; | |
|
1352 | + return false; | |
|
1353 | + }, | |
|
1354 | + | |
|
1355 | + scrollTo: function(element) { | |
|
1356 | + element = $(element); | |
|
1357 | + var pos = Position.cumulativeOffset(element); | |
|
1358 | + window.scrollTo(pos[0], pos[1]); | |
|
1359 | + return element; | |
|
1360 | + }, | |
|
1361 | + | |
|
1362 | + getStyle: function(element, style) { | |
|
1363 | + element = $(element); | |
|
1364 | + if (['float','cssFloat'].include(style)) | |
|
1365 | + style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat'); | |
|
1366 | + style = style.camelize(); | |
|
1367 | + var value = element.style[style]; | |
|
1368 | + if (!value) { | |
|
1369 | + if (document.defaultView && document.defaultView.getComputedStyle) { | |
|
1370 | + var css = document.defaultView.getComputedStyle(element, null); | |
|
1371 | + value = css ? css[style] : null; | |
|
1372 | + } else if (element.currentStyle) { | |
|
1373 | + value = element.currentStyle[style]; | |
|
1374 | + } | |
|
1375 | + } | |
|
1376 | + | |
|
1377 | + if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none')) | |
|
1378 | + value = element['offset'+style.capitalize()] + 'px'; | |
|
1379 | + | |
|
1380 | + if (window.opera && ['left', 'top', 'right', 'bottom'].include(style)) | |
|
1381 | + if (Element.getStyle(element, 'position') == 'static') value = 'auto'; | |
|
1382 | + if(style == 'opacity') { | |
|
1383 | + if(value) return parseFloat(value); | |
|
1384 | + if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) | |
|
1385 | + if(value[1]) return parseFloat(value[1]) / 100; | |
|
1386 | + return 1.0; | |
|
1387 | + } | |
|
1388 | + return value == 'auto' ? null : value; | |
|
1389 | + }, | |
|
1390 | + | |
|
1391 | + setStyle: function(element, style) { | |
|
1392 | + element = $(element); | |
|
1393 | + for (var name in style) { | |
|
1394 | + var value = style[name]; | |
|
1395 | + if(name == 'opacity') { | |
|
1396 | + if (value == 1) { | |
|
1397 | + value = (/Gecko/.test(navigator.userAgent) && | |
|
1398 | + !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0; | |
|
1399 | + if(/MSIE/.test(navigator.userAgent) && !window.opera) | |
|
1400 | + element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,''); | |
|
1401 | + } else if(value == '') { | |
|
1402 | + if(/MSIE/.test(navigator.userAgent) && !window.opera) | |
|
1403 | + element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,''); | |
|
1404 | + } else { | |
|
1405 | + if(value < 0.00001) value = 0; | |
|
1406 | + if(/MSIE/.test(navigator.userAgent) && !window.opera) | |
|
1407 | + element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') + | |
|
1408 | + 'alpha(opacity='+value*100+')'; | |
|
1409 | + } | |
|
1410 | + } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat'; | |
|
1411 | + element.style[name.camelize()] = value; | |
|
1412 | + } | |
|
1413 | + return element; | |
|
1414 | + }, | |
|
1415 | + | |
|
1416 | + getDimensions: function(element) { | |
|
1417 | + element = $(element); | |
|
1418 | + var display = $(element).getStyle('display'); | |
|
1419 | + if (display != 'none' && display != null) // Safari bug | |
|
1420 | + return {width: element.offsetWidth, height: element.offsetHeight}; | |
|
1421 | + | |
|
1422 | + // All *Width and *Height properties give 0 on elements with display none, | |
|
1423 | + // so enable the element temporarily | |
|
1424 | + var els = element.style; | |
|
1425 | + var originalVisibility = els.visibility; | |
|
1426 | + var originalPosition = els.position; | |
|
1427 | + var originalDisplay = els.display; | |
|
1428 | + els.visibility = 'hidden'; | |
|
1429 | + els.position = 'absolute'; | |
|
1430 | + els.display = 'block'; | |
|
1431 | + var originalWidth = element.clientWidth; | |
|
1432 | + var originalHeight = element.clientHeight; | |
|
1433 | + els.display = originalDisplay; | |
|
1434 | + els.position = originalPosition; | |
|
1435 | + els.visibility = originalVisibility; | |
|
1436 | + return {width: originalWidth, height: originalHeight}; | |
|
1437 | + }, | |
|
1438 | + | |
|
1439 | + makePositioned: function(element) { | |
|
1440 | + element = $(element); | |
|
1441 | + var pos = Element.getStyle(element, 'position'); | |
|
1442 | + if (pos == 'static' || !pos) { | |
|
1443 | + element._madePositioned = true; | |
|
1444 | + element.style.position = 'relative'; | |
|
1445 | + // Opera returns the offset relative to the positioning context, when an | |
|
1446 | + // element is position relative but top and left have not been defined | |
|
1447 | + if (window.opera) { | |
|
1448 | + element.style.top = 0; | |
|
1449 | + element.style.left = 0; | |
|
1450 | + } | |
|
1451 | + } | |
|
1452 | + return element; | |
|
1453 | + }, | |
|
1454 | + | |
|
1455 | + undoPositioned: function(element) { | |
|
1456 | + element = $(element); | |
|
1457 | + if (element._madePositioned) { | |
|
1458 | + element._madePositioned = undefined; | |
|
1459 | + element.style.position = | |
|
1460 | + element.style.top = | |
|
1461 | + element.style.left = | |
|
1462 | + element.style.bottom = | |
|
1463 | + element.style.right = ''; | |
|
1464 | + } | |
|
1465 | + return element; | |
|
1466 | + }, | |
|
1467 | + | |
|
1468 | + makeClipping: function(element) { | |
|
1469 | + element = $(element); | |
|
1470 | + if (element._overflow) return element; | |
|
1471 | + element._overflow = element.style.overflow || 'auto'; | |
|
1472 | + if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') | |
|
1473 | + element.style.overflow = 'hidden'; | |
|
1474 | + return element; | |
|
1475 | + }, | |
|
1476 | + | |
|
1477 | + undoClipping: function(element) { | |
|
1478 | + element = $(element); | |
|
1479 | + if (!element._overflow) return element; | |
|
1480 | + element.style.overflow = element._overflow == 'auto' ? '' : element._overflow; | |
|
1481 | + element._overflow = null; | |
|
1482 | + return element; | |
|
1483 | + } | |
|
1484 | + }; | |
|
1485 | + | |
|
1486 | + Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf}); | |
|
1487 | + | |
|
1488 | + Element._attributeTranslations = {}; | |
|
1489 | + | |
|
1490 | + Element._attributeTranslations.names = { | |
|
1491 | + colspan: "colSpan", | |
|
1492 | + rowspan: "rowSpan", | |
|
1493 | + valign: "vAlign", | |
|
1494 | + datetime: "dateTime", | |
|
1495 | + accesskey: "accessKey", | |
|
1496 | + tabindex: "tabIndex", | |
|
1497 | + enctype: "encType", | |
|
1498 | + maxlength: "maxLength", | |
|
1499 | + readonly: "readOnly", | |
|
1500 | + longdesc: "longDesc" | |
|
1501 | + }; | |
|
1502 | + | |
|
1503 | + Element._attributeTranslations.values = { | |
|
1504 | + _getAttr: function(element, attribute) { | |
|
1505 | + return element.getAttribute(attribute, 2); | |
|
1506 | + }, | |
|
1507 | + | |
|
1508 | + _flag: function(element, attribute) { | |
|
1509 | + return $(element).hasAttribute(attribute) ? attribute : null; | |
|
1510 | + }, | |
|
1511 | + | |
|
1512 | + style: function(element) { | |
|
1513 | + return element.style.cssText.toLowerCase(); | |
|
1514 | + }, | |
|
1515 | + | |
|
1516 | + title: function(element) { | |
|
1517 | + var node = element.getAttributeNode('title'); | |
|
1518 | + return node.specified ? node.nodeValue : null; | |
|
1519 | + } | |
|
1520 | + }; | |
|
1521 | + | |
|
1522 | + Object.extend(Element._attributeTranslations.values, { | |
|
1523 | + href: Element._attributeTranslations.values._getAttr, | |
|
1524 | + src: Element._attributeTranslations.values._getAttr, | |
|
1525 | + disabled: Element._attributeTranslations.values._flag, | |
|
1526 | + checked: Element._attributeTranslations.values._flag, | |
|
1527 | + readonly: Element._attributeTranslations.values._flag, | |
|
1528 | + multiple: Element._attributeTranslations.values._flag | |
|
1529 | + }); | |
|
1530 | + | |
|
1531 | + Element.Methods.Simulated = { | |
|
1532 | + hasAttribute: function(element, attribute) { | |
|
1533 | + var t = Element._attributeTranslations; | |
|
1534 | + attribute = t.names[attribute] || attribute; | |
|
1535 | + return $(element).getAttributeNode(attribute).specified; | |
|
1536 | + } | |
|
1537 | + }; | |
|
1538 | + | |
|
1539 | + // IE is missing .innerHTML support for TABLE-related elements | |
|
1540 | + if (document.all && !window.opera){ | |
|
1541 | + Element.Methods.update = function(element, html) { | |
|
1542 | + element = $(element); | |
|
1543 | + html = typeof html == 'undefined' ? '' : html.toString(); | |
|
1544 | + var tagName = element.tagName.toUpperCase(); | |
|
1545 | + if (['THEAD','TBODY','TR','TD'].include(tagName)) { | |
|
1546 | + var div = document.createElement('div'); | |
|
1547 | + switch (tagName) { | |
|
1548 | + case 'THEAD': | |
|
1549 | + case 'TBODY': | |
|
1550 | + div.innerHTML = '<table><tbody>' + html.stripScripts() + '</tbody></table>'; | |
|
1551 | + depth = 2; | |
|
1552 | + break; | |
|
1553 | + case 'TR': | |
|
1554 | + div.innerHTML = '<table><tbody><tr>' + html.stripScripts() + '</tr></tbody></table>'; | |
|
1555 | + depth = 3; | |
|
1556 | + break; | |
|
1557 | + case 'TD': | |
|
1558 | + div.innerHTML = '<table><tbody><tr><td>' + html.stripScripts() + '</td></tr></tbody></table>'; | |
|
1559 | + depth = 4; | |
|
1560 | + } | |
|
1561 | + $A(element.childNodes).each(function(node){ | |
|
1562 | + element.removeChild(node) | |
|
1563 | + }); | |
|
1564 | + depth.times(function(){ div = div.firstChild }); | |
|
1565 | + | |
|
1566 | + $A(div.childNodes).each( | |
|
1567 | + function(node){ element.appendChild(node) }); | |
|
1568 | + } else { | |
|
1569 | + element.innerHTML = html.stripScripts(); | |
|
1570 | + } | |
|
1571 | + setTimeout(function() {html.evalScripts()}, 10); | |
|
1572 | + return element; | |
|
1573 | + } | |
|
1574 | + }; | |
|
1575 | + | |
|
1576 | + Object.extend(Element, Element.Methods); | |
|
1577 | + | |
|
1578 | + var _nativeExtensions = false; | |
|
1579 | + | |
|
1580 | + if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)) | |
|
1581 | + ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) { | |
|
1582 | + var className = 'HTML' + tag + 'Element'; | |
|
1583 | + if(window[className]) return; | |
|
1584 | + var klass = window[className] = {}; | |
|
1585 | + klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__; | |
|
1586 | + }); | |
|
1587 | + | |
|
1588 | + Element.addMethods = function(methods) { | |
|
1589 | + Object.extend(Element.Methods, methods || {}); | |
|
1590 | + | |
|
1591 | + function copy(methods, destination, onlyIfAbsent) { | |
|
1592 | + onlyIfAbsent = onlyIfAbsent || false; | |
|
1593 | + var cache = Element.extend.cache; | |
|
1594 | + for (var property in methods) { | |
|
1595 | + var value = methods[property]; | |
|
1596 | + if (!onlyIfAbsent || !(property in destination)) | |
|
1597 | + destination[property] = cache.findOrStore(value); | |
|
1598 | + } | |
|
1599 | + } | |
|
1600 | + | |
|
1601 | + if (typeof HTMLElement != 'undefined') { | |
|
1602 | + copy(Element.Methods, HTMLElement.prototype); | |
|
1603 | + copy(Element.Methods.Simulated, HTMLElement.prototype, true); | |
|
1604 | + copy(Form.Methods, HTMLFormElement.prototype); | |
|
1605 | + [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) { | |
|
1606 | + copy(Form.Element.Methods, klass.prototype); | |
|
1607 | + }); | |
|
1608 | + _nativeExtensions = true; | |
|
1609 | + } | |
|
1610 | + } | |
|
1611 | + | |
|
1612 | + var Toggle = new Object(); | |
|
1613 | + Toggle.display = Element.toggle; | |
|
1614 | + | |
|
1615 | + /*--------------------------------------------------------------------------*/ | |
|
1616 | + | |
|
1617 | + Abstract.Insertion = function(adjacency) { | |
|
1618 | + this.adjacency = adjacency; | |
|
1619 | + } | |
|
1620 | + | |
|
1621 | + Abstract.Insertion.prototype = { | |
|
1622 | + initialize: function(element, content) { | |
|
1623 | + this.element = $(element); | |
|
1624 | + this.content = content.stripScripts(); | |
|
1625 | + | |
|
1626 | + if (this.adjacency && this.element.insertAdjacentHTML) { | |
|
1627 | + try { | |
|
1628 | + this.element.insertAdjacentHTML(this.adjacency, this.content); | |
|
1629 | + } catch (e) { | |
|
1630 | + var tagName = this.element.tagName.toUpperCase(); | |
|
1631 | + if (['TBODY', 'TR'].include(tagName)) { | |
|
1632 | + this.insertContent(this.contentFromAnonymousTable()); | |
|
1633 | + } else { | |
|
1634 | + throw e; | |
|
1635 | + } | |
|
1636 | + } | |
|
1637 | + } else { | |
|
1638 | + this.range = this.element.ownerDocument.createRange(); | |
|
1639 | + if (this.initializeRange) this.initializeRange(); | |
|
1640 | + this.insertContent([this.range.createContextualFragment(this.content)]); | |
|
1641 | + } | |
|
1642 | + | |
|
1643 | + setTimeout(function() {content.evalScripts()}, 10); | |
|
1644 | + }, | |
|
1645 | + | |
|
1646 | + contentFromAnonymousTable: function() { | |
|
1647 | + var div = document.createElement('div'); | |
|
1648 | + div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>'; | |
|
1649 | + return $A(div.childNodes[0].childNodes[0].childNodes); | |
|
1650 | + } | |
|
1651 | + } | |
|
1652 | + | |
|
1653 | + var Insertion = new Object(); | |
|
1654 | + | |
|
1655 | + Insertion.Before = Class.create(); | |
|
1656 | + Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { | |
|
1657 | + initializeRange: function() { | |
|
1658 | + this.range.setStartBefore(this.element); | |
|
1659 | + }, | |
|
1660 | + | |
|
1661 | + insertContent: function(fragments) { | |
|
1662 | + fragments.each((function(fragment) { | |
|
1663 | + this.element.parentNode.insertBefore(fragment, this.element); | |
|
1664 | + }).bind(this)); | |
|
1665 | + } | |
|
1666 | + }); | |
|
1667 | + | |
|
1668 | + Insertion.Top = Class.create(); | |
|
1669 | + Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { | |
|
1670 | + initializeRange: function() { | |
|
1671 | + this.range.selectNodeContents(this.element); | |
|
1672 | + this.range.collapse(true); | |
|
1673 | + }, | |
|
1674 | + | |
|
1675 | + insertContent: function(fragments) { | |
|
1676 | + fragments.reverse(false).each((function(fragment) { | |
|
1677 | + this.element.insertBefore(fragment, this.element.firstChild); | |
|
1678 | + }).bind(this)); | |
|
1679 | + } | |
|
1680 | + }); | |
|
1681 | + | |
|
1682 | + Insertion.Bottom = Class.create(); | |
|
1683 | + Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { | |
|
1684 | + initializeRange: function() { | |
|
1685 | + this.range.selectNodeContents(this.element); | |
|
1686 | + this.range.collapse(this.element); | |
|
1687 | + }, | |
|
1688 | + | |
|
1689 | + insertContent: function(fragments) { | |
|
1690 | + fragments.each((function(fragment) { | |
|
1691 | + this.element.appendChild(fragment); | |
|
1692 | + }).bind(this)); | |
|
1693 | + } | |
|
1694 | + }); | |
|
1695 | + | |
|
1696 | + Insertion.After = Class.create(); | |
|
1697 | + Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { | |
|
1698 | + initializeRange: function() { | |
|
1699 | + this.range.setStartAfter(this.element); | |
|
1700 | + }, | |
|
1701 | + | |
|
1702 | + insertContent: function(fragments) { | |
|
1703 | + fragments.each((function(fragment) { | |
|
1704 | + this.element.parentNode.insertBefore(fragment, | |
|
1705 | + this.element.nextSibling); | |
|
1706 | + }).bind(this)); | |
|
1707 | + } | |
|
1708 | + }); | |
|
1709 | + | |
|
1710 | + /*--------------------------------------------------------------------------*/ | |
|
1711 | + | |
|
1712 | + Element.ClassNames = Class.create(); | |
|
1713 | + Element.ClassNames.prototype = { | |
|
1714 | + initialize: function(element) { | |
|
1715 | + this.element = $(element); | |
|
1716 | + }, | |
|
1717 | + | |
|
1718 | + _each: function(iterator) { | |
|
1719 | + this.element.className.split(/\s+/).select(function(name) { | |
|
1720 | + return name.length > 0; | |
|
1721 | + })._each(iterator); | |
|
1722 | + }, | |
|
1723 | + | |
|
1724 | + set: function(className) { | |
|
1725 | + this.element.className = className; | |
|
1726 | + }, | |
|
1727 | + | |
|
1728 | + add: function(classNameToAdd) { | |
|
1729 | + if (this.include(classNameToAdd)) return; | |
|
1730 | + this.set($A(this).concat(classNameToAdd).join(' ')); | |
|
1731 | + }, | |
|
1732 | + | |
|
1733 | + remove: function(classNameToRemove) { | |
|
1734 | + if (!this.include(classNameToRemove)) return; | |
|
1735 | + this.set($A(this).without(classNameToRemove).join(' ')); | |
|
1736 | + }, | |
|
1737 | + | |
|
1738 | + toString: function() { | |
|
1739 | + return $A(this).join(' '); | |
|
1740 | + } | |
|
1741 | + }; | |
|
1742 | + | |
|
1743 | + Object.extend(Element.ClassNames.prototype, Enumerable); | |
|
1744 | + var Selector = Class.create(); | |
|
1745 | + Selector.prototype = { | |
|
1746 | + initialize: function(expression) { | |
|
1747 | + this.params = {classNames: []}; | |
|
1748 | + this.expression = expression.toString().strip(); | |
|
1749 | + this.parseExpression(); | |
|
1750 | + this.compileMatcher(); | |
|
1751 | + }, | |
|
1752 | + | |
|
1753 | + parseExpression: function() { | |
|
1754 | + function abort(message) { throw 'Parse error in selector: ' + message; } | |
|
1755 | + | |
|
1756 | + if (this.expression == '') abort('empty expression'); | |
|
1757 | + | |
|
1758 | + var params = this.params, expr = this.expression, match, modifier, clause, rest; | |
|
1759 | + while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) { | |
|
1760 | + params.attributes = params.attributes || []; | |
|
1761 | + params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''}); | |
|
1762 | + expr = match[1]; | |
|
1763 | + } | |
|
1764 | + | |
|
1765 | + if (expr == '*') return this.params.wildcard = true; | |
|
1766 | + | |
|
1767 | + while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) { | |
|
1768 | + modifier = match[1], clause = match[2], rest = match[3]; | |
|
1769 | + switch (modifier) { | |
|
1770 | + case '#': params.id = clause; break; | |
|
1771 | + case '.': params.classNames.push(clause); break; | |
|
1772 | + case '': | |
|
1773 | + case undefined: params.tagName = clause.toUpperCase(); break; | |
|
1774 | + default: abort(expr.inspect()); | |
|
1775 | + } | |
|
1776 | + expr = rest; | |
|
1777 | + } | |
|
1778 | + | |
|
1779 | + if (expr.length > 0) abort(expr.inspect()); | |
|
1780 | + }, | |
|
1781 | + | |
|
1782 | + buildMatchExpression: function() { | |
|
1783 | + var params = this.params, conditions = [], clause; | |
|
1784 | + | |
|
1785 | + if (params.wildcard) | |
|
1786 | + conditions.push('true'); | |
|
1787 | + if (clause = params.id) | |
|
1788 | + conditions.push('element.readAttribute("id") == ' + clause.inspect()); | |
|
1789 | + if (clause = params.tagName) | |
|
1790 | + conditions.push('element.tagName.toUpperCase() == ' + clause.inspect()); | |
|
1791 | + if ((clause = params.classNames).length > 0) | |
|
1792 | + for (var i = 0, length = clause.length; i < length; i++) | |
|
1793 | + conditions.push('element.hasClassName(' + clause[i].inspect() + ')'); | |
|
1794 | + if (clause = params.attributes) { | |
|
1795 | + clause.each(function(attribute) { | |
|
1796 | + var value = 'element.readAttribute(' + attribute.name.inspect() + ')'; | |
|
1797 | + var splitValueBy = function(delimiter) { | |
|
1798 | + return value + ' && ' + value + '.split(' + delimiter.inspect() + ')'; | |
|
1799 | + } | |
|
1800 | + | |
|
1801 | + switch (attribute.operator) { | |
|
1802 | + case '=': conditions.push(value + ' == ' + attribute.value.inspect()); break; | |
|
1803 | + case '~=': conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break; | |
|
1804 | + case '|=': conditions.push( | |
|
1805 | + splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect() | |
|
1806 | + ); break; | |
|
1807 | + case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break; | |
|
1808 | + case '': | |
|
1809 | + case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break; | |
|
1810 | + default: throw 'Unknown operator ' + attribute.operator + ' in selector'; | |
|
1811 | + } | |
|
1812 | + }); | |
|
1813 | + } | |
|
1814 | + | |
|
1815 | + return conditions.join(' && '); | |
|
1816 | + }, | |
|
1817 | + | |
|
1818 | + compileMatcher: function() { | |
|
1819 | + this.match = new Function('element', 'if (!element.tagName) return false; \ | |
|
1820 | + element = $(element); \ | |
|
1821 | + return ' + this.buildMatchExpression()); | |
|
1822 | + }, | |
|
1823 | + | |
|
1824 | + findElements: function(scope) { | |
|
1825 | + var element; | |
|
1826 | + | |
|
1827 | + if (element = $(this.params.id)) | |
|
1828 | + if (this.match(element)) | |
|
1829 | + if (!scope || Element.childOf(element, scope)) | |
|
1830 | + return [element]; | |
|
1831 | + | |
|
1832 | + scope = (scope || document).getElementsByTagName(this.params.tagName || '*'); | |
|
1833 | + | |
|
1834 | + var results = []; | |
|
1835 | + for (var i = 0, length = scope.length; i < length; i++) | |
|
1836 | + if (this.match(element = scope[i])) | |
|
1837 | + results.push(Element.extend(element)); | |
|
1838 | + | |
|
1839 | + return results; | |
|
1840 | + }, | |
|
1841 | + | |
|
1842 | + toString: function() { | |
|
1843 | + return this.expression; | |
|
1844 | + } | |
|
1845 | + } | |
|
1846 | + | |
|
1847 | + Object.extend(Selector, { | |
|
1848 | + matchElements: function(elements, expression) { | |
|
1849 | + var selector = new Selector(expression); | |
|
1850 | + return elements.select(selector.match.bind(selector)).map(Element.extend); | |
|
1851 | + }, | |
|
1852 | + | |
|
1853 | + findElement: function(elements, expression, index) { | |
|
1854 | + if (typeof expression == 'number') index = expression, expression = false; | |
|
1855 | + return Selector.matchElements(elements, expression || '*')[index || 0]; | |
|
1856 | + }, | |
|
1857 | + | |
|
1858 | + findChildElements: function(element, expressions) { | |
|
1859 | + return expressions.map(function(expression) { | |
|
1860 | + return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) { | |
|
1861 | + var selector = new Selector(expr); | |
|
1862 | + return results.inject([], function(elements, result) { | |
|
1863 | + return elements.concat(selector.findElements(result || element)); | |
|
1864 | + }); | |
|
1865 | + }); | |
|
1866 | + }).flatten(); | |
|
1867 | + } | |
|
1868 | + }); | |
|
1869 | + | |
|
1870 | + function $$() { | |
|
1871 | + return Selector.findChildElements(document, $A(arguments)); | |
|
1872 | + } | |
|
1873 | + var Form = { | |
|
1874 | + reset: function(form) { | |
|
1875 | + $(form).reset(); | |
|
1876 | + return form; | |
|
1877 | + }, | |
|
1878 | + | |
|
1879 | + serializeElements: function(elements, getHash) { | |
|
1880 | + var data = elements.inject({}, function(result, element) { | |
|
1881 | + if (!element.disabled && element.name) { | |
|
1882 | + var key = element.name, value = $(element).getValue(); | |
|
1883 | + if (value != undefined) { | |
|
1884 | + if (result[key]) { | |
|
1885 | + if (result[key].constructor != Array) result[key] = [result[key]]; | |
|
1886 | + result[key].push(value); | |
|
1887 | + } | |
|
1888 | + else result[key] = value; | |
|
1889 | + } | |
|
1890 | + } | |
|
1891 | + return result; | |
|
1892 | + }); | |
|
1893 | + | |
|
1894 | + return getHash ? data : Hash.toQueryString(data); | |
|
1895 | + } | |
|
1896 | + }; | |
|
1897 | + | |
|
1898 | + Form.Methods = { | |
|
1899 | + serialize: function(form, getHash) { | |
|
1900 | + return Form.serializeElements(Form.getElements(form), getHash); | |
|
1901 | + }, | |
|
1902 | + | |
|
1903 | + getElements: function(form) { | |
|
1904 | + return $A($(form).getElementsByTagName('*')).inject([], | |
|
1905 | + function(elements, child) { | |
|
1906 | + if (Form.Element.Serializers[child.tagName.toLowerCase()]) | |
|
1907 | + elements.push(Element.extend(child)); | |
|
1908 | + return elements; | |
|
1909 | + } | |
|
1910 | + ); | |
|
1911 | + }, | |
|
1912 | + | |
|
1913 | + getInputs: function(form, typeName, name) { | |
|
1914 | + form = $(form); | |
|
1915 | + var inputs = form.getElementsByTagName('input'); | |
|
1916 | + | |
|
1917 | + if (!typeName && !name) return $A(inputs).map(Element.extend); | |
|
1918 | + | |
|
1919 | + for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { | |
|
1920 | + var input = inputs[i]; | |
|
1921 | + if ((typeName && input.type != typeName) || (name && input.name != name)) | |
|
1922 | + continue; | |
|
1923 | + matchingInputs.push(Element.extend(input)); | |
|
1924 | + } | |
|
1925 | + | |
|
1926 | + return matchingInputs; | |
|
1927 | + }, | |
|
1928 | + | |
|
1929 | + disable: function(form) { | |
|
1930 | + form = $(form); | |
|
1931 | + form.getElements().each(function(element) { | |
|
1932 | + element.blur(); | |
|
1933 | + element.disabled = 'true'; | |
|
1934 | + }); | |
|
1935 | + return form; | |
|
1936 | + }, | |
|
1937 | + | |
|
1938 | + enable: function(form) { | |
|
1939 | + form = $(form); | |
|
1940 | + form.getElements().each(function(element) { | |
|
1941 | + element.disabled = ''; | |
|
1942 | + }); | |
|
1943 | + return form; | |
|
1944 | + }, | |
|
1945 | + | |
|
1946 | + findFirstElement: function(form) { | |
|
1947 | + return $(form).getElements().find(function(element) { | |
|
1948 | + return element.type != 'hidden' && !element.disabled && | |
|
1949 | + ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); | |
|
1950 | + }); | |
|
1951 | + }, | |
|
1952 | + | |
|
1953 | + focusFirstElement: function(form) { | |
|
1954 | + form = $(form); | |
|
1955 | + form.findFirstElement().activate(); | |
|
1956 | + return form; | |
|
1957 | + } | |
|
1958 | + } | |
|
1959 | + | |
|
1960 | + Object.extend(Form, Form.Methods); | |
|
1961 | + | |
|
1962 | + /*--------------------------------------------------------------------------*/ | |
|
1963 | + | |
|
1964 | + Form.Element = { | |
|
1965 | + focus: function(element) { | |
|
1966 | + $(element).focus(); | |
|
1967 | + return element; | |
|
1968 | + }, | |
|
1969 | + | |
|
1970 | + select: function(element) { | |
|
1971 | + $(element).select(); | |
|
1972 | + return element; | |
|
1973 | + } | |
|
1974 | + } | |
|
1975 | + | |
|
1976 | + Form.Element.Methods = { | |
|
1977 | + serialize: function(element) { | |
|
1978 | + element = $(element); | |
|
1979 | + if (!element.disabled && element.name) { | |
|
1980 | + var value = element.getValue(); | |
|
1981 | + if (value != undefined) { | |
|
1982 | + var pair = {}; | |
|
1983 | + pair[element.name] = value; | |
|
1984 | + return Hash.toQueryString(pair); | |
|
1985 | + } | |
|
1986 | + } | |
|
1987 | + return ''; | |
|
1988 | + }, | |
|
1989 | + | |
|
1990 | + getValue: function(element) { | |
|
1991 | + element = $(element); | |
|
1992 | + var method = element.tagName.toLowerCase(); | |
|
1993 | + return Form.Element.Serializers[method](element); | |
|
1994 | + }, | |
|
1995 | + | |
|
1996 | + clear: function(element) { | |
|
1997 | + $(element).value = ''; | |
|
1998 | + return element; | |
|
1999 | + }, | |
|
2000 | + | |
|
2001 | + present: function(element) { | |
|
2002 | + return $(element).value != ''; | |
|
2003 | + }, | |
|
2004 | + | |
|
2005 | + activate: function(element) { | |
|
2006 | + element = $(element); | |
|
2007 | + element.focus(); | |
|
2008 | + if (element.select && ( element.tagName.toLowerCase() != 'input' || | |
|
2009 | + !['button', 'reset', 'submit'].include(element.type) ) ) | |
|
2010 | + element.select(); | |
|
2011 | + return element; | |
|
2012 | + }, | |
|
2013 | + | |
|
2014 | + disable: function(element) { | |
|
2015 | + element = $(element); | |
|
2016 | + element.disabled = true; | |
|
2017 | + return element; | |
|
2018 | + }, | |
|
2019 | + | |
|
2020 | + enable: function(element) { | |
|
2021 | + element = $(element); | |
|
2022 | + element.blur(); | |
|
2023 | + element.disabled = false; | |
|
2024 | + return element; | |
|
2025 | + } | |
|
2026 | + } | |
|
2027 | + | |
|
2028 | + Object.extend(Form.Element, Form.Element.Methods); | |
|
2029 | + var Field = Form.Element; | |
|
2030 | + var $F = Form.Element.getValue; | |
|
2031 | + | |
|
2032 | + /*--------------------------------------------------------------------------*/ | |
|
2033 | + | |
|
2034 | + Form.Element.Serializers = { | |
|
2035 | + input: function(element) { | |
|
2036 | + switch (element.type.toLowerCase()) { | |
|
2037 | + case 'checkbox': | |
|
2038 | + case 'radio': | |
|
2039 | + return Form.Element.Serializers.inputSelector(element); | |
|
2040 | + default: | |
|
2041 | + return Form.Element.Serializers.textarea(element); | |
|
2042 | + } | |
|
2043 | + }, | |
|
2044 | + | |
|
2045 | + inputSelector: function(element) { | |
|
2046 | + return element.checked ? element.value : null; | |
|
2047 | + }, | |
|
2048 | + | |
|
2049 | + textarea: function(element) { | |
|
2050 | + return element.value; | |
|
2051 | + }, | |
|
2052 | + | |
|
2053 | + select: function(element) { | |
|
2054 | + return this[element.type == 'select-one' ? | |
|
2055 | + 'selectOne' : 'selectMany'](element); | |
|
2056 | + }, | |
|
2057 | + | |
|
2058 | + selectOne: function(element) { | |
|
2059 | + var index = element.selectedIndex; | |
|
2060 | + return index >= 0 ? this.optionValue(element.options[index]) : null; | |
|
2061 | + }, | |
|
2062 | + | |
|
2063 | + selectMany: function(element) { | |
|
2064 | + var values, length = element.length; | |
|
2065 | + if (!length) return null; | |
|
2066 | + | |
|
2067 | + for (var i = 0, values = []; i < length; i++) { | |
|
2068 | + var opt = element.options[i]; | |
|
2069 | + if (opt.selected) values.push(this.optionValue(opt)); | |
|
2070 | + } | |
|
2071 | + return values; | |
|
2072 | + }, | |
|
2073 | + | |
|
2074 | + optionValue: function(opt) { | |
|
2075 | + // extend element because hasAttribute may not be native | |
|
2076 | + return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; | |
|
2077 | + } | |
|
2078 | + } | |
|
2079 | + | |
|
2080 | + /*--------------------------------------------------------------------------*/ | |
|
2081 | + | |
|
2082 | + Abstract.TimedObserver = function() {} | |
|
2083 | + Abstract.TimedObserver.prototype = { | |
|
2084 | + initialize: function(element, frequency, callback) { | |
|
2085 | + this.frequency = frequency; | |
|
2086 | + this.element = $(element); | |
|
2087 | + this.callback = callback; | |
|
2088 | + | |
|
2089 | + this.lastValue = this.getValue(); | |
|
2090 | + this.registerCallback(); | |
|
2091 | + }, | |
|
2092 | + | |
|
2093 | + registerCallback: function() { | |
|
2094 | + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); | |
|
2095 | + }, | |
|
2096 | + | |
|
2097 | + onTimerEvent: function() { | |
|
2098 | + var value = this.getValue(); | |
|
2099 | + var changed = ('string' == typeof this.lastValue && 'string' == typeof value | |
|
2100 | + ? this.lastValue != value : String(this.lastValue) != String(value)); | |
|
2101 | + if (changed) { | |
|
2102 | + this.callback(this.element, value); | |
|
2103 | + this.lastValue = value; | |
|
2104 | + } | |
|
2105 | + } | |
|
2106 | + } | |
|
2107 | + | |
|
2108 | + Form.Element.Observer = Class.create(); | |
|
2109 | + Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { | |
|
2110 | + getValue: function() { | |
|
2111 | + return Form.Element.getValue(this.element); | |
|
2112 | + } | |
|
2113 | + }); | |
|
2114 | + | |
|
2115 | + Form.Observer = Class.create(); | |
|
2116 | + Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { | |
|
2117 | + getValue: function() { | |
|
2118 | + return Form.serialize(this.element); | |
|
2119 | + } | |
|
2120 | + }); | |
|
2121 | + | |
|
2122 | + /*--------------------------------------------------------------------------*/ | |
|
2123 | + | |
|
2124 | + Abstract.EventObserver = function() {} | |
|
2125 | + Abstract.EventObserver.prototype = { | |
|
2126 | + initialize: function(element, callback) { | |
|
2127 | + this.element = $(element); | |
|
2128 | + this.callback = callback; | |
|
2129 | + | |
|
2130 | + this.lastValue = this.getValue(); | |
|
2131 | + if (this.element.tagName.toLowerCase() == 'form') | |
|
2132 | + this.registerFormCallbacks(); | |
|
2133 | + else | |
|
2134 | + this.registerCallback(this.element); | |
|
2135 | + }, | |
|
2136 | + | |
|
2137 | + onElementEvent: function() { | |
|
2138 | + var value = this.getValue(); | |
|
2139 | + if (this.lastValue != value) { | |
|
2140 | + this.callback(this.element, value); | |
|
2141 | + this.lastValue = value; | |
|
2142 | + } | |
|
2143 | + }, | |
|
2144 | + | |
|
2145 | + registerFormCallbacks: function() { | |
|
2146 | + Form.getElements(this.element).each(this.registerCallback.bind(this)); | |
|
2147 | + }, | |
|
2148 | + | |
|
2149 | + registerCallback: function(element) { | |
|
2150 | + if (element.type) { | |
|
2151 | + switch (element.type.toLowerCase()) { | |
|
2152 | + case 'checkbox': | |
|
2153 | + case 'radio': | |
|
2154 | + Event.observe(element, 'click', this.onElementEvent.bind(this)); | |
|
2155 | + break; | |
|
2156 | + default: | |
|
2157 | + Event.observe(element, 'change', this.onElementEvent.bind(this)); | |
|
2158 | + break; | |
|
2159 | + } | |
|
2160 | + } | |
|
2161 | + } | |
|
2162 | + } | |
|
2163 | + | |
|
2164 | + Form.Element.EventObserver = Class.create(); | |
|
2165 | + Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { | |
|
2166 | + getValue: function() { | |
|
2167 | + return Form.Element.getValue(this.element); | |
|
2168 | + } | |
|
2169 | + }); | |
|
2170 | + | |
|
2171 | + Form.EventObserver = Class.create(); | |
|
2172 | + Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { | |
|
2173 | + getValue: function() { | |
|
2174 | + return Form.serialize(this.element); | |
|
2175 | + } | |
|
2176 | + }); | |
|
2177 | + if (!window.Event) { | |
|
2178 | + var Event = new Object(); | |
|
2179 | + } | |
|
2180 | + | |
|
2181 | + Object.extend(Event, { | |
|
2182 | + KEY_BACKSPACE: 8, | |
|
2183 | + KEY_TAB: 9, | |
|
2184 | + KEY_RETURN: 13, | |
|
2185 | + KEY_ESC: 27, | |
|
2186 | + KEY_LEFT: 37, | |
|
2187 | + KEY_UP: 38, | |
|
2188 | + KEY_RIGHT: 39, | |
|
2189 | + KEY_DOWN: 40, | |
|
2190 | + KEY_DELETE: 46, | |
|
2191 | + KEY_HOME: 36, | |
|
2192 | + KEY_END: 35, | |
|
2193 | + KEY_PAGEUP: 33, | |
|
2194 | + KEY_PAGEDOWN: 34, | |
|
2195 | + | |
|
2196 | + element: function(event) { | |
|
2197 | + return event.target || event.srcElement; | |
|
2198 | + }, | |
|
2199 | + | |
|
2200 | + isLeftClick: function(event) { | |
|
2201 | + return (((event.which) && (event.which == 1)) || | |
|
2202 | + ((event.button) && (event.button == 1))); | |
|
2203 | + }, | |
|
2204 | + | |
|
2205 | + pointerX: function(event) { | |
|
2206 | + return event.pageX || (event.clientX + | |
|
2207 | + (document.documentElement.scrollLeft || document.body.scrollLeft)); | |
|
2208 | + }, | |
|
2209 | + | |
|
2210 | + pointerY: function(event) { | |
|
2211 | + return event.pageY || (event.clientY + | |
|
2212 | + (document.documentElement.scrollTop || document.body.scrollTop)); | |
|
2213 | + }, | |
|
2214 | + | |
|
2215 | + stop: function(event) { | |
|
2216 | + if (event.preventDefault) { | |
|
2217 | + event.preventDefault(); | |
|
2218 | + event.stopPropagation(); | |
|
2219 | + } else { | |
|
2220 | + event.returnValue = false; | |
|
2221 | + event.cancelBubble = true; | |
|
2222 | + } | |
|
2223 | + }, | |
|
2224 | + | |
|
2225 | + // find the first node with the given tagName, starting from the | |
|
2226 | + // node the event was triggered on; traverses the DOM upwards | |
|
2227 | + findElement: function(event, tagName) { | |
|
2228 | + var element = Event.element(event); | |
|
2229 | + while (element.parentNode && (!element.tagName || | |
|
2230 | + (element.tagName.toUpperCase() != tagName.toUpperCase()))) | |
|
2231 | + element = element.parentNode; | |
|
2232 | + return element; | |
|
2233 | + }, | |
|
2234 | + | |
|
2235 | + observers: false, | |
|
2236 | + | |
|
2237 | + _observeAndCache: function(element, name, observer, useCapture) { | |
|
2238 | + if (!this.observers) this.observers = []; | |
|
2239 | + if (element.addEventListener) { | |
|
2240 | + this.observers.push([element, name, observer, useCapture]); | |
|
2241 | + element.addEventListener(name, observer, useCapture); | |
|
2242 | + } else if (element.attachEvent) { | |
|
2243 | + this.observers.push([element, name, observer, useCapture]); | |
|
2244 | + element.attachEvent('on' + name, observer); | |
|
2245 | + } | |
|
2246 | + }, | |
|
2247 | + | |
|
2248 | + unloadCache: function() { | |
|
2249 | + if (!Event.observers) return; | |
|
2250 | + for (var i = 0, length = Event.observers.length; i < length; i++) { | |
|
2251 | + Event.stopObserving.apply(this, Event.observers[i]); | |
|
2252 | + Event.observers[i][0] = null; | |
|
2253 | + } | |
|
2254 | + Event.observers = false; | |
|
2255 | + }, | |
|
2256 | + | |
|
2257 | + observe: function(element, name, observer, useCapture) { | |
|
2258 | + element = $(element); | |
|
2259 | + useCapture = useCapture || false; | |
|
2260 | + | |
|
2261 | + if (name == 'keypress' && | |
|
2262 | + (navigator.appVersion.match(/Konqueror|Safari|KHTML/) | |
|
2263 | + || element.attachEvent)) | |
|
2264 | + name = 'keydown'; | |
|
2265 | + | |
|
2266 | + Event._observeAndCache(element, name, observer, useCapture); | |
|
2267 | + }, | |
|
2268 | + | |
|
2269 | + stopObserving: function(element, name, observer, useCapture) { | |
|
2270 | + element = $(element); | |
|
2271 | + useCapture = useCapture || false; | |
|
2272 | + | |
|
2273 | + if (name == 'keypress' && | |
|
2274 | + (navigator.appVersion.match(/Konqueror|Safari|KHTML/) | |
|
2275 | + || element.detachEvent)) | |
|
2276 | + name = 'keydown'; | |
|
2277 | + | |
|
2278 | + if (element.removeEventListener) { | |
|
2279 | + element.removeEventListener(name, observer, useCapture); | |
|
2280 | + } else if (element.detachEvent) { | |
|
2281 | + try { | |
|
2282 | + element.detachEvent('on' + name, observer); | |
|
2283 | + } catch (e) {} | |
|
2284 | + } | |
|
2285 | + } | |
|
2286 | + }); | |
|
2287 | + | |
|
2288 | + /* prevent memory leaks in IE */ | |
|
2289 | + if (navigator.appVersion.match(/\bMSIE\b/)) | |
|
2290 | + Event.observe(window, 'unload', Event.unloadCache, false); | |
|
2291 | + var Position = { | |
|
2292 | + // set to true if needed, warning: firefox performance problems | |
|
2293 | + // NOT neeeded for page scrolling, only if draggable contained in | |
|
2294 | + // scrollable elements | |
|
2295 | + includeScrollOffsets: false, | |
|
2296 | + | |
|
2297 | + // must be called before calling withinIncludingScrolloffset, every time the | |
|
2298 | + // page is scrolled | |
|
2299 | + prepare: function() { | |
|
2300 | + this.deltaX = window.pageXOffset | |
|
2301 | + || document.documentElement.scrollLeft | |
|
2302 | + || document.body.scrollLeft | |
|
2303 | + || 0; | |
|
2304 | + this.deltaY = window.pageYOffset | |
|
2305 | + || document.documentElement.scrollTop | |
|
2306 | + || document.body.scrollTop | |
|
2307 | + || 0; | |
|
2308 | + }, | |
|
2309 | + | |
|
2310 | + realOffset: function(element) { | |
|
2311 | + var valueT = 0, valueL = 0; | |
|
2312 | + do { | |
|
2313 | + valueT += element.scrollTop || 0; | |
|
2314 | + valueL += element.scrollLeft || 0; | |
|
2315 | + element = element.parentNode; | |
|
2316 | + } while (element); | |
|
2317 | + return [valueL, valueT]; | |
|
2318 | + }, | |
|
2319 | + | |
|
2320 | + cumulativeOffset: function(element) { | |
|
2321 | + var valueT = 0, valueL = 0; | |
|
2322 | + do { | |
|
2323 | + valueT += element.offsetTop || 0; | |
|
2324 | + valueL += element.offsetLeft || 0; | |
|
2325 | + element = element.offsetParent; | |
|
2326 | + } while (element); | |
|
2327 | + return [valueL, valueT]; | |
|
2328 | + }, | |
|
2329 | + | |
|
2330 | + positionedOffset: function(element) { | |
|
2331 | + var valueT = 0, valueL = 0; | |
|
2332 | + do { | |
|
2333 | + valueT += element.offsetTop || 0; | |
|
2334 | + valueL += element.offsetLeft || 0; | |
|
2335 | + element = element.offsetParent; | |
|
2336 | + if (element) { | |
|
2337 | + if(element.tagName=='BODY') break; | |
|
2338 | + var p = Element.getStyle(element, 'position'); | |
|
2339 | + if (p == 'relative' || p == 'absolute') break; | |
|
2340 | + } | |
|
2341 | + } while (element); | |
|
2342 | + return [valueL, valueT]; | |
|
2343 | + }, | |
|
2344 | + | |
|
2345 | + offsetParent: function(element) { | |
|
2346 | + if (element.offsetParent) return element.offsetParent; | |
|
2347 | + if (element == document.body) return element; | |
|
2348 | + | |
|
2349 | + while ((element = element.parentNode) && element != document.body) | |
|
2350 | + if (Element.getStyle(element, 'position') != 'static') | |
|
2351 | + return element; | |
|
2352 | + | |
|
2353 | + return document.body; | |
|
2354 | + }, | |
|
2355 | + | |
|
2356 | + // caches x/y coordinate pair to use with overlap | |
|
2357 | + within: function(element, x, y) { | |
|
2358 | + if (this.includeScrollOffsets) | |
|
2359 | + return this.withinIncludingScrolloffsets(element, x, y); | |
|
2360 | + this.xcomp = x; | |
|
2361 | + this.ycomp = y; | |
|
2362 | + this.offset = this.cumulativeOffset(element); | |
|
2363 | + | |
|
2364 | + return (y >= this.offset[1] && | |
|
2365 | + y < this.offset[1] + element.offsetHeight && | |
|
2366 | + x >= this.offset[0] && | |
|
2367 | + x < this.offset[0] + element.offsetWidth); | |
|
2368 | + }, | |
|
2369 | + | |
|
2370 | + withinIncludingScrolloffsets: function(element, x, y) { | |
|
2371 | + var offsetcache = this.realOffset(element); | |
|
2372 | + | |
|
2373 | + this.xcomp = x + offsetcache[0] - this.deltaX; | |
|
2374 | + this.ycomp = y + offsetcache[1] - this.deltaY; | |
|
2375 | + this.offset = this.cumulativeOffset(element); | |
|
2376 | + | |
|
2377 | + return (this.ycomp >= this.offset[1] && | |
|
2378 | + this.ycomp < this.offset[1] + element.offsetHeight && | |
|
2379 | + this.xcomp >= this.offset[0] && | |
|
2380 | + this.xcomp < this.offset[0] + element.offsetWidth); | |
|
2381 | + }, | |
|
2382 | + | |
|
2383 | + // within must be called directly before | |
|
2384 | + overlap: function(mode, element) { | |
|
2385 | + if (!mode) return 0; | |
|
2386 | + if (mode == 'vertical') | |
|
2387 | + return ((this.offset[1] + element.offsetHeight) - this.ycomp) / | |
|
2388 | + element.offsetHeight; | |
|
2389 | + if (mode == 'horizontal') | |
|
2390 | + return ((this.offset[0] + element.offsetWidth) - this.xcomp) / | |
|
2391 | + element.offsetWidth; | |
|
2392 | + }, | |
|
2393 | + | |
|
2394 | + page: function(forElement) { | |
|
2395 | + var valueT = 0, valueL = 0; | |
|
2396 | + | |
|
2397 | + var element = forElement; | |
|
2398 | + do { | |
|
2399 | + valueT += element.offsetTop || 0; | |
|
2400 | + valueL += element.offsetLeft || 0; | |
|
2401 | + | |
|
2402 | + // Safari fix | |
|
2403 | + if (element.offsetParent==document.body) | |
|
2404 | + if (Element.getStyle(element,'position')=='absolute') break; | |
|
2405 | + | |
|
2406 | + } while (element = element.offsetParent); | |
|
2407 | + | |
|
2408 | + element = forElement; | |
|
2409 | + do { | |
|
2410 | + if (!window.opera || element.tagName=='BODY') { | |
|
2411 | + valueT -= element.scrollTop || 0; | |
|
2412 | + valueL -= element.scrollLeft || 0; | |
|
2413 | + } | |
|
2414 | + } while (element = element.parentNode); | |
|
2415 | + | |
|
2416 | + return [valueL, valueT]; | |
|
2417 | + }, | |
|
2418 | + | |
|
2419 | + clone: function(source, target) { | |
|
2420 | + var options = Object.extend({ | |
|
2421 | + setLeft: true, | |
|
2422 | + setTop: true, | |
|
2423 | + setWidth: true, | |
|
2424 | + setHeight: true, | |
|
2425 | + offsetTop: 0, | |
|
2426 | + offsetLeft: 0 | |
|
2427 | + }, arguments[2] || {}) | |
|
2428 | + | |
|
2429 | + // find page position of source | |
|
2430 | + source = $(source); | |
|
2431 | + var p = Position.page(source); | |
|
2432 | + | |
|
2433 | + // find coordinate system to use | |
|
2434 | + target = $(target); | |
|
2435 | + var delta = [0, 0]; | |
|
2436 | + var parent = null; | |
|
2437 | + // delta [0,0] will do fine with position: fixed elements, | |
|
2438 | + // position:absolute needs offsetParent deltas | |
|
2439 | + if (Element.getStyle(target,'position') == 'absolute') { | |
|
2440 | + parent = Position.offsetParent(target); | |
|
2441 | + delta = Position.page(parent); | |
|
2442 | + } | |
|
2443 | + | |
|
2444 | + // correct by body offsets (fixes Safari) | |
|
2445 | + if (parent == document.body) { | |
|
2446 | + delta[0] -= document.body.offsetLeft; | |
|
2447 | + delta[1] -= document.body.offsetTop; | |
|
2448 | + } | |
|
2449 | + | |
|
2450 | + // set position | |
|
2451 | + if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; | |
|
2452 | + if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; | |
|
2453 | + if(options.setWidth) target.style.width = source.offsetWidth + 'px'; | |
|
2454 | + if(options.setHeight) target.style.height = source.offsetHeight + 'px'; | |
|
2455 | + }, | |
|
2456 | + | |
|
2457 | + absolutize: function(element) { | |
|
2458 | + element = $(element); | |
|
2459 | + if (element.style.position == 'absolute') return; | |
|
2460 | + Position.prepare(); | |
|
2461 | + | |
|
2462 | + var offsets = Position.positionedOffset(element); | |
|
2463 | + var top = offsets[1]; | |
|
2464 | + var left = offsets[0]; | |
|
2465 | + var width = element.clientWidth; | |
|
2466 | + var height = element.clientHeight; | |
|
2467 | + | |
|
2468 | + element._originalLeft = left - parseFloat(element.style.left || 0); | |
|
2469 | + element._originalTop = top - parseFloat(element.style.top || 0); | |
|
2470 | + element._originalWidth = element.style.width; | |
|
2471 | + element._originalHeight = element.style.height; | |
|
2472 | + | |
|
2473 | + element.style.position = 'absolute'; | |
|
2474 | + element.style.top = top + 'px'; | |
|
2475 | + element.style.left = left + 'px'; | |
|
2476 | + element.style.width = width + 'px'; | |
|
2477 | + element.style.height = height + 'px'; | |
|
2478 | + }, | |
|
2479 | + | |
|
2480 | + relativize: function(element) { | |
|
2481 | + element = $(element); | |
|
2482 | + if (element.style.position == 'relative') return; | |
|
2483 | + Position.prepare(); | |
|
2484 | + | |
|
2485 | + element.style.position = 'relative'; | |
|
2486 | + var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); | |
|
2487 | + var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); | |
|
2488 | + | |
|
2489 | + element.style.top = top + 'px'; | |
|
2490 | + element.style.left = left + 'px'; | |
|
2491 | + element.style.height = element._originalHeight; | |
|
2492 | + element.style.width = element._originalWidth; | |
|
2493 | + } | |
|
2494 | + } | |
|
2495 | + | |
|
2496 | + // Safari returns margins on body which is incorrect if the child is absolutely | |
|
2497 | + // positioned. For performance reasons, redefine Position.cumulativeOffset for | |
|
2498 | + // KHTML/WebKit only. | |
|
2499 | + if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) { | |
|
2500 | + Position.cumulativeOffset = function(element) { | |
|
2501 | + var valueT = 0, valueL = 0; | |
|
2502 | + do { | |
|
2503 | + valueT += element.offsetTop || 0; | |
|
2504 | + valueL += element.offsetLeft || 0; | |
|
2505 | + if (element.offsetParent == document.body) | |
|
2506 | + if (Element.getStyle(element, 'position') == 'absolute') break; | |
|
2507 | + | |
|
2508 | + element = element.offsetParent; | |
|
2509 | + } while (element); | |
|
2510 | + | |
|
2511 | + return [valueL, valueT]; | |
|
2512 | + } | |
|
2513 | + } | |
|
2514 | + | |
|
2515 | + Element.addMethods(); No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file No newline at end of file |
@@ -0,0 +1,20 | |||
|
1 | + | |
|
2 | + div.problist-each { margin-top: 2px; margin-bottom: 2px} | |
|
3 | + | |
|
4 | + div.usermenu { | |
|
5 | + border-top: thin solid grey; | |
|
6 | + border-bottom: thin solid grey; | |
|
7 | + text-align: right | |
|
8 | + } | |
|
9 | + | |
|
10 | + div.probname { | |
|
11 | + background-color: #66cccc; | |
|
12 | + font-weight: bold | |
|
13 | + } | |
|
14 | + | |
|
15 | + div.subinfo { | |
|
16 | + margin-left: 20px; | |
|
17 | + margin-top: 2px; | |
|
18 | + border-bottom: thin solid grey; | |
|
19 | + border-left: thin solid grey | |
|
20 | + } No newline at end of file |
@@ -0,0 +1,80 | |||
|
1 | + body { background-color: #fff; color: #333; } | |
|
2 | + | |
|
3 | + div.usermenu { | |
|
4 | + border-top: thin solid grey; | |
|
5 | + border-bottom: thin solid grey; | |
|
6 | + text-align: right | |
|
7 | + } | |
|
8 | + | |
|
9 | + body, p, ol, ul, td { | |
|
10 | + font-family: verdana, arial, helvetica, sans-serif; | |
|
11 | + font-size: 13px; | |
|
12 | + line-height: 18px; | |
|
13 | + } | |
|
14 | + | |
|
15 | + pre { | |
|
16 | + background-color: #eee; | |
|
17 | + padding: 10px; | |
|
18 | + font-size: 11px; | |
|
19 | + } | |
|
20 | + | |
|
21 | + a { color: #000; } | |
|
22 | + a:visited { color: #666; } | |
|
23 | + a:hover { color: #fff; background-color:#000; } | |
|
24 | + | |
|
25 | + .fieldWithErrors { | |
|
26 | + padding: 2px; | |
|
27 | + background-color: red; | |
|
28 | + display: table; | |
|
29 | + } | |
|
30 | + | |
|
31 | + #errorExplanation { | |
|
32 | + width: 400px; | |
|
33 | + border: 2px solid red; | |
|
34 | + padding: 7px; | |
|
35 | + padding-bottom: 12px; | |
|
36 | + margin-bottom: 20px; | |
|
37 | + background-color: #f0f0f0; | |
|
38 | + } | |
|
39 | + | |
|
40 | + #errorExplanation h2 { | |
|
41 | + text-align: left; | |
|
42 | + font-weight: bold; | |
|
43 | + padding: 5px 5px 5px 15px; | |
|
44 | + font-size: 12px; | |
|
45 | + margin: -7px; | |
|
46 | + background-color: #c00; | |
|
47 | + color: #fff; | |
|
48 | + } | |
|
49 | + | |
|
50 | + #errorExplanation p { | |
|
51 | + color: #333; | |
|
52 | + margin-bottom: 0; | |
|
53 | + padding: 5px; | |
|
54 | + } | |
|
55 | + | |
|
56 | + #errorExplanation ul li { | |
|
57 | + font-size: 12px; | |
|
58 | + list-style: square; | |
|
59 | + } | |
|
60 | + | |
|
61 | + div.uploadStatus { | |
|
62 | + margin: 5px; | |
|
63 | + } | |
|
64 | + | |
|
65 | + div.progressBar { | |
|
66 | + margin: 5px; | |
|
67 | + } | |
|
68 | + | |
|
69 | + div.progressBar div.border { | |
|
70 | + background-color: #fff; | |
|
71 | + border: 1px solid grey; | |
|
72 | + width: 100%; | |
|
73 | + } | |
|
74 | + | |
|
75 | + div.progressBar div.background { | |
|
76 | + background-color: #333; | |
|
77 | + height: 18px; | |
|
78 | + width: 0%; | |
|
79 | + } | |
|
80 | + |
@@ -0,0 +1,3 | |||
|
1 | + #!/usr/bin/env ruby | |
|
2 | + require File.dirname(__FILE__) + '/../config/boot' | |
|
3 | + require 'commands/about' No newline at end of file |
@@ -0,0 +1,3 | |||
|
1 | + #!/usr/bin/env ruby | |
|
2 | + require File.dirname(__FILE__) + '/../config/boot' | |
|
3 | + require 'commands/breakpointer' No newline at end of file |
@@ -0,0 +1,3 | |||
|
1 | + #!/usr/bin/env ruby | |
|
2 | + require File.dirname(__FILE__) + '/../config/boot' | |
|
3 | + require 'commands/console' No newline at end of file |
@@ -0,0 +1,3 | |||
|
1 | + #!/usr/bin/env ruby | |
|
2 | + require File.dirname(__FILE__) + '/../config/boot' | |
|
3 | + require 'commands/destroy' No newline at end of file |
@@ -0,0 +1,3 | |||
|
1 | + #!/usr/bin/env ruby | |
|
2 | + require File.dirname(__FILE__) + '/../config/boot' | |
|
3 | + require 'commands/generate' No newline at end of file |
@@ -0,0 +1,3 | |||
|
1 | + #!/usr/bin/env ruby | |
|
2 | + require File.dirname(__FILE__) + '/../../config/boot' | |
|
3 | + require 'commands/performance/benchmarker' |
@@ -0,0 +1,3 | |||
|
1 | + #!/usr/bin/env ruby | |
|
2 | + require File.dirname(__FILE__) + '/../../config/boot' | |
|
3 | + require 'commands/performance/profiler' |
@@ -0,0 +1,3 | |||
|
1 | + #!/usr/bin/env ruby | |
|
2 | + require File.dirname(__FILE__) + '/../config/boot' | |
|
3 | + require 'commands/plugin' No newline at end of file |
@@ -0,0 +1,3 | |||
|
1 | + #!/usr/bin/env ruby | |
|
2 | + require File.dirname(__FILE__) + '/../../config/boot' | |
|
3 | + require 'commands/process/inspector' |
@@ -0,0 +1,3 | |||
|
1 | + #!/usr/bin/env ruby | |
|
2 | + require File.dirname(__FILE__) + '/../../config/boot' | |
|
3 | + require 'commands/process/reaper' |
@@ -0,0 +1,3 | |||
|
1 | + #!/usr/bin/env ruby | |
|
2 | + require File.dirname(__FILE__) + '/../../config/boot' | |
|
3 | + require 'commands/process/spawner' |
@@ -0,0 +1,3 | |||
|
1 | + #!/usr/bin/env ruby | |
|
2 | + require File.dirname(__FILE__) + '/../config/boot' | |
|
3 | + require 'commands/runner' No newline at end of file |
@@ -0,0 +1,3 | |||
|
1 | + #!/usr/bin/env ruby | |
|
2 | + require File.dirname(__FILE__) + '/../config/boot' | |
|
3 | + require 'commands/server' No newline at end of file |
@@ -0,0 +1,5 | |||
|
1 | + # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html | |
|
2 | + one: | |
|
3 | + id: 1 | |
|
4 | + two: | |
|
5 | + id: 2 |
@@ -0,0 +1,5 | |||
|
1 | + # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html | |
|
2 | + one: | |
|
3 | + id: 1 | |
|
4 | + two: | |
|
5 | + id: 2 |
@@ -0,0 +1,5 | |||
|
1 | + # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html | |
|
2 | + one: | |
|
3 | + id: 1 | |
|
4 | + two: | |
|
5 | + id: 2 |
@@ -0,0 +1,5 | |||
|
1 | + # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html | |
|
2 | + one: | |
|
3 | + id: 1 | |
|
4 | + two: | |
|
5 | + id: 2 |
@@ -0,0 +1,5 | |||
|
1 | + # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html | |
|
2 | + one: | |
|
3 | + id: 1 | |
|
4 | + two: | |
|
5 | + id: 2 |
@@ -0,0 +1,5 | |||
|
1 | + # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html | |
|
2 | + one: | |
|
3 | + id: 1 | |
|
4 | + two: | |
|
5 | + id: 2 |
@@ -0,0 +1,5 | |||
|
1 | + # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html | |
|
2 | + one: | |
|
3 | + id: 1 | |
|
4 | + two: | |
|
5 | + id: 2 |
@@ -0,0 +1,18 | |||
|
1 | + require File.dirname(__FILE__) + '/../test_helper' | |
|
2 | + require 'login_controller' | |
|
3 | + | |
|
4 | + # Re-raise errors caught by the controller. | |
|
5 | + class LoginController; def rescue_action(e) raise e end; end | |
|
6 | + | |
|
7 | + class LoginControllerTest < Test::Unit::TestCase | |
|
8 | + def setup | |
|
9 | + @controller = LoginController.new | |
|
10 | + @request = ActionController::TestRequest.new | |
|
11 | + @response = ActionController::TestResponse.new | |
|
12 | + end | |
|
13 | + | |
|
14 | + # Replace this with your real tests. | |
|
15 | + def test_truth | |
|
16 | + assert true | |
|
17 | + end | |
|
18 | + end |
@@ -0,0 +1,18 | |||
|
1 | + require File.dirname(__FILE__) + '/../test_helper' | |
|
2 | + require 'main_controller' | |
|
3 | + | |
|
4 | + # Re-raise errors caught by the controller. | |
|
5 | + class MainController; def rescue_action(e) raise e end; end | |
|
6 | + | |
|
7 | + class MainControllerTest < Test::Unit::TestCase | |
|
8 | + def setup | |
|
9 | + @controller = MainController.new | |
|
10 | + @request = ActionController::TestRequest.new | |
|
11 | + @response = ActionController::TestResponse.new | |
|
12 | + end | |
|
13 | + | |
|
14 | + # Replace this with your real tests. | |
|
15 | + def test_truth | |
|
16 | + assert true | |
|
17 | + end | |
|
18 | + end |
@@ -0,0 +1,92 | |||
|
1 | + require File.dirname(__FILE__) + '/../test_helper' | |
|
2 | + require 'problems_controller' | |
|
3 | + | |
|
4 | + # Re-raise errors caught by the controller. | |
|
5 | + class ProblemsController; def rescue_action(e) raise e end; end | |
|
6 | + | |
|
7 | + class ProblemsControllerTest < Test::Unit::TestCase | |
|
8 | + fixtures :problems | |
|
9 | + | |
|
10 | + def setup | |
|
11 | + @controller = ProblemsController.new | |
|
12 | + @request = ActionController::TestRequest.new | |
|
13 | + @response = ActionController::TestResponse.new | |
|
14 | + | |
|
15 | + @first_id = problems(:first).id | |
|
16 | + end | |
|
17 | + | |
|
18 | + def test_index | |
|
19 | + get :index | |
|
20 | + assert_response :success | |
|
21 | + assert_template 'list' | |
|
22 | + end | |
|
23 | + | |
|
24 | + def test_list | |
|
25 | + get :list | |
|
26 | + | |
|
27 | + assert_response :success | |
|
28 | + assert_template 'list' | |
|
29 | + | |
|
30 | + assert_not_nil assigns(:problems) | |
|
31 | + end | |
|
32 | + | |
|
33 | + def test_show | |
|
34 | + get :show, :id => @first_id | |
|
35 | + | |
|
36 | + assert_response :success | |
|
37 | + assert_template 'show' | |
|
38 | + | |
|
39 | + assert_not_nil assigns(:problem) | |
|
40 | + assert assigns(:problem).valid? | |
|
41 | + end | |
|
42 | + | |
|
43 | + def test_new | |
|
44 | + get :new | |
|
45 | + | |
|
46 | + assert_response :success | |
|
47 | + assert_template 'new' | |
|
48 | + | |
|
49 | + assert_not_nil assigns(:problem) | |
|
50 | + end | |
|
51 | + | |
|
52 | + def test_create | |
|
53 | + num_problems = Problem.count | |
|
54 | + | |
|
55 | + post :create, :problem => {} | |
|
56 | + | |
|
57 | + assert_response :redirect | |
|
58 | + assert_redirected_to :action => 'list' | |
|
59 | + | |
|
60 | + assert_equal num_problems + 1, Problem.count | |
|
61 | + end | |
|
62 | + | |
|
63 | + def test_edit | |
|
64 | + get :edit, :id => @first_id | |
|
65 | + | |
|
66 | + assert_response :success | |
|
67 | + assert_template 'edit' | |
|
68 | + | |
|
69 | + assert_not_nil assigns(:problem) | |
|
70 | + assert assigns(:problem).valid? | |
|
71 | + end | |
|
72 | + | |
|
73 | + def test_update | |
|
74 | + post :update, :id => @first_id | |
|
75 | + assert_response :redirect | |
|
76 | + assert_redirected_to :action => 'show', :id => @first_id | |
|
77 | + end | |
|
78 | + | |
|
79 | + def test_destroy | |
|
80 | + assert_nothing_raised { | |
|
81 | + Problem.find(@first_id) | |
|
82 | + } | |
|
83 | + | |
|
84 | + post :destroy, :id => @first_id | |
|
85 | + assert_response :redirect | |
|
86 | + assert_redirected_to :action => 'list' | |
|
87 | + | |
|
88 | + assert_raise(ActiveRecord::RecordNotFound) { | |
|
89 | + Problem.find(@first_id) | |
|
90 | + } | |
|
91 | + end | |
|
92 | + end |
@@ -0,0 +1,92 | |||
|
1 | + require File.dirname(__FILE__) + '/../test_helper' | |
|
2 | + require 'user_admin_controller' | |
|
3 | + | |
|
4 | + # Re-raise errors caught by the controller. | |
|
5 | + class UserAdminController; def rescue_action(e) raise e end; end | |
|
6 | + | |
|
7 | + class UserAdminControllerTest < Test::Unit::TestCase | |
|
8 | + fixtures :users | |
|
9 | + | |
|
10 | + def setup | |
|
11 | + @controller = UserAdminController.new | |
|
12 | + @request = ActionController::TestRequest.new | |
|
13 | + @response = ActionController::TestResponse.new | |
|
14 | + | |
|
15 | + @first_id = users(:first).id | |
|
16 | + end | |
|
17 | + | |
|
18 | + def test_index | |
|
19 | + get :index | |
|
20 | + assert_response :success | |
|
21 | + assert_template 'list' | |
|
22 | + end | |
|
23 | + | |
|
24 | + def test_list | |
|
25 | + get :list | |
|
26 | + | |
|
27 | + assert_response :success | |
|
28 | + assert_template 'list' | |
|
29 | + | |
|
30 | + assert_not_nil assigns(:users) | |
|
31 | + end | |
|
32 | + | |
|
33 | + def test_show | |
|
34 | + get :show, :id => @first_id | |
|
35 | + | |
|
36 | + assert_response :success | |
|
37 | + assert_template 'show' | |
|
38 | + | |
|
39 | + assert_not_nil assigns(:user) | |
|
40 | + assert assigns(:user).valid? | |
|
41 | + end | |
|
42 | + | |
|
43 | + def test_new | |
|
44 | + get :new | |
|
45 | + | |
|
46 | + assert_response :success | |
|
47 | + assert_template 'new' | |
|
48 | + | |
|
49 | + assert_not_nil assigns(:user) | |
|
50 | + end | |
|
51 | + | |
|
52 | + def test_create | |
|
53 | + num_users = User.count | |
|
54 | + | |
|
55 | + post :create, :user => {} | |
|
56 | + | |
|
57 | + assert_response :redirect | |
|
58 | + assert_redirected_to :action => 'list' | |
|
59 | + | |
|
60 | + assert_equal num_users + 1, User.count | |
|
61 | + end | |
|
62 | + | |
|
63 | + def test_edit | |
|
64 | + get :edit, :id => @first_id | |
|
65 | + | |
|
66 | + assert_response :success | |
|
67 | + assert_template 'edit' | |
|
68 | + | |
|
69 | + assert_not_nil assigns(:user) | |
|
70 | + assert assigns(:user).valid? | |
|
71 | + end | |
|
72 | + | |
|
73 | + def test_update | |
|
74 | + post :update, :id => @first_id | |
|
75 | + assert_response :redirect | |
|
76 | + assert_redirected_to :action => 'show', :id => @first_id | |
|
77 | + end | |
|
78 | + | |
|
79 | + def test_destroy | |
|
80 | + assert_nothing_raised { | |
|
81 | + User.find(@first_id) | |
|
82 | + } | |
|
83 | + | |
|
84 | + post :destroy, :id => @first_id | |
|
85 | + assert_response :redirect | |
|
86 | + assert_redirected_to :action => 'list' | |
|
87 | + | |
|
88 | + assert_raise(ActiveRecord::RecordNotFound) { | |
|
89 | + User.find(@first_id) | |
|
90 | + } | |
|
91 | + end | |
|
92 | + end |
@@ -0,0 +1,18 | |||
|
1 | + require File.dirname(__FILE__) + '/../test_helper' | |
|
2 | + require 'users_controller' | |
|
3 | + | |
|
4 | + # Re-raise errors caught by the controller. | |
|
5 | + class UsersController; def rescue_action(e) raise e end; end | |
|
6 | + | |
|
7 | + class UsersControllerTest < Test::Unit::TestCase | |
|
8 | + def setup | |
|
9 | + @controller = UsersController.new | |
|
10 | + @request = ActionController::TestRequest.new | |
|
11 | + @response = ActionController::TestResponse.new | |
|
12 | + end | |
|
13 | + | |
|
14 | + # Replace this with your real tests. | |
|
15 | + def test_truth | |
|
16 | + assert true | |
|
17 | + end | |
|
18 | + end |
@@ -0,0 +1,28 | |||
|
1 | + ENV["RAILS_ENV"] = "test" | |
|
2 | + require File.expand_path(File.dirname(__FILE__) + "/../config/environment") | |
|
3 | + require 'test_help' | |
|
4 | + | |
|
5 | + class Test::Unit::TestCase | |
|
6 | + # Transactional fixtures accelerate your tests by wrapping each test method | |
|
7 | + # in a transaction that's rolled back on completion. This ensures that the | |
|
8 | + # test database remains unchanged so your fixtures don't have to be reloaded | |
|
9 | + # between every test method. Fewer database queries means faster tests. | |
|
10 | + # | |
|
11 | + # Read Mike Clark's excellent walkthrough at | |
|
12 | + # http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting | |
|
13 | + # | |
|
14 | + # Every Active Record database supports transactions except MyISAM tables | |
|
15 | + # in MySQL. Turn off transactional fixtures in this case; however, if you | |
|
16 | + # don't care one way or the other, switching from MyISAM to InnoDB tables | |
|
17 | + # is recommended. | |
|
18 | + self.use_transactional_fixtures = true | |
|
19 | + | |
|
20 | + # Instantiated fixtures are slow, but give you @david where otherwise you | |
|
21 | + # would need people(:david). If you don't want to migrate your existing | |
|
22 | + # test cases which use the @david style and don't mind the speed hit (each | |
|
23 | + # instantiated fixtures translates to a database query per test method), | |
|
24 | + # then set this back to true. | |
|
25 | + self.use_instantiated_fixtures = false | |
|
26 | + | |
|
27 | + # Add more helper methods to be used by all tests here... | |
|
28 | + end |
@@ -0,0 +1,10 | |||
|
1 | + require File.dirname(__FILE__) + '/../test_helper' | |
|
2 | + | |
|
3 | + class LanguageTest < Test::Unit::TestCase | |
|
4 | + fixtures :languages | |
|
5 | + | |
|
6 | + # Replace this with your real tests. | |
|
7 | + def test_truth | |
|
8 | + assert true | |
|
9 | + end | |
|
10 | + end |
@@ -0,0 +1,10 | |||
|
1 | + require File.dirname(__FILE__) + '/../test_helper' | |
|
2 | + | |
|
3 | + class ProblemTest < Test::Unit::TestCase | |
|
4 | + fixtures :problems | |
|
5 | + | |
|
6 | + # Replace this with your real tests. | |
|
7 | + def test_truth | |
|
8 | + assert true | |
|
9 | + end | |
|
10 | + end |
@@ -0,0 +1,10 | |||
|
1 | + require File.dirname(__FILE__) + '/../test_helper' | |
|
2 | + | |
|
3 | + class RightTest < Test::Unit::TestCase | |
|
4 | + fixtures :rights | |
|
5 | + | |
|
6 | + # Replace this with your real tests. | |
|
7 | + def test_truth | |
|
8 | + assert true | |
|
9 | + end | |
|
10 | + end |
@@ -0,0 +1,10 | |||
|
1 | + require File.dirname(__FILE__) + '/../test_helper' | |
|
2 | + | |
|
3 | + class RoleTest < Test::Unit::TestCase | |
|
4 | + fixtures :roles | |
|
5 | + | |
|
6 | + # Replace this with your real tests. | |
|
7 | + def test_truth | |
|
8 | + assert true | |
|
9 | + end | |
|
10 | + end |
@@ -0,0 +1,10 | |||
|
1 | + require File.dirname(__FILE__) + '/../test_helper' | |
|
2 | + | |
|
3 | + class SubmissionTest < Test::Unit::TestCase | |
|
4 | + fixtures :submissions | |
|
5 | + | |
|
6 | + # Replace this with your real tests. | |
|
7 | + def test_truth | |
|
8 | + assert true | |
|
9 | + end | |
|
10 | + end |
You need to be logged in to leave comments.
Login now