ajax-scaffold-generator 2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006 Richard White
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,28 @@
1
+ Description:
2
+ The ajax scaffold generator creates a controller to interact with a model.
3
+ If the model does not exist, it creates the model as well. Unlike the
4
+ standard scaffold, the ajax scaffold generator uses AJAX.
5
+
6
+ The generator takes a model name, an optional controller name, and a
7
+ list of views as arguments. Scaffolded actions and views are created
8
+ automatically. Any views left over generate empty stubs.
9
+
10
+ If a controller name is not given, the plural form of the model name
11
+ will be used. The model and controller names may be given in CamelCase
12
+ or under_score and should not be suffixed with 'Model' or 'Controller'.
13
+ Both model and controller names may be prefixed with a module like a
14
+ file path; see the Modules Example for usage.
15
+
16
+ Example:
17
+ ./script/generate ajax_scaffold Account Bank debit credit
18
+
19
+ This will generate an Account model and BankController with a full test
20
+ suite and a basic user interface. Now create the accounts table in your
21
+ database and browse to http://localhost/bank/ -- voila, you're on Rails!
22
+
23
+ Modules Example:
24
+ ./script/generate ajax_scaffold CreditCard 'admin/credit_card' suspend late_fee
25
+
26
+ This will generate a CreditCard model and CreditCardController controller
27
+ in the admin module.
28
+
@@ -0,0 +1,123 @@
1
+ require "rails_generator/generators/components/scaffold/scaffold_generator"
2
+
3
+ class AjaxScaffoldingSandbox < ScaffoldingSandbox
4
+ def default_input_block
5
+ Proc.new { |record, column| "<div class=\"form-element\">\n <label for=\"#{record}_#{column.name}\">#{column.human_name}</label>\n #{input(record, column.name)}\n</div>\n" }
6
+ end
7
+ end
8
+
9
+ class AjaxScaffoldGenerator < ScaffoldGenerator
10
+
11
+ def manifest
12
+ record do |m|
13
+ # Check for class naming collisions.
14
+ m.class_collisions controller_class_path, "#{controller_class_name}Controller", "#{controller_class_name}ControllerTest", "#{controller_class_name}Helper"
15
+
16
+ # Controller, helper, views, and test directories.
17
+ m.directory File.join('app/controllers', controller_class_path)
18
+ m.directory File.join('app/helpers', controller_class_path)
19
+ m.directory File.join('app/views', controller_class_path, controller_file_name)
20
+ m.directory File.join('public/images')
21
+ m.directory File.join('test/functional', controller_class_path)
22
+
23
+ # Depend on model generator but skip if the model exists.
24
+ m.dependency 'model', [singular_name], :collision => :skip
25
+
26
+ # Scaffolded forms.
27
+ m.complex_template 'form.rhtml',
28
+ File.join('app/views',
29
+ controller_class_path,
30
+ controller_file_name,
31
+ '_form.rhtml'),
32
+ :insert => 'form_scaffolding.rhtml',
33
+ :sandbox => lambda { create_sandbox },
34
+ :begin_mark => 'form',
35
+ :end_mark => 'eoform',
36
+ :mark_id => singular_name
37
+
38
+ # Scaffolded partials.
39
+ m.template "partial_item.rhtml",
40
+ File.join('app/views',
41
+ controller_class_path,
42
+ controller_file_name,
43
+ "_#{controller_file_name}.rhtml")
44
+
45
+ m.template "partial_form_errors.rhtml",
46
+ File.join('app/views',
47
+ controller_class_path,
48
+ controller_file_name,
49
+ "_form_errors.rhtml")
50
+
51
+ # Scaffolded views.
52
+ scaffold_views.each do |action|
53
+ m.template "view_#{action}.rhtml",
54
+ File.join('app/views',
55
+ controller_class_path,
56
+ controller_file_name,
57
+ "#{action}.rhtml"),
58
+ :assigns => { :action => action }
59
+ end
60
+
61
+ # Controller class, functional test, helper, and views.
62
+ m.template 'controller.rb',
63
+ File.join('app/controllers',
64
+ controller_class_path,
65
+ "#{controller_file_name}_controller.rb")
66
+
67
+ m.template 'functional_test.rb',
68
+ File.join('test/functional',
69
+ controller_class_path,
70
+ "#{controller_file_name}_controller_test.rb")
71
+
72
+ m.template 'helper.rb',
73
+ File.join('app/helpers',
74
+ controller_class_path,
75
+ "#{controller_file_name}_helper.rb")
76
+
77
+ # Layout and stylesheet.
78
+ m.template 'layout.rhtml', "app/views/layouts/#{controller_file_name}.rhtml"
79
+ m.template 'style.css', 'public/stylesheets/ajax_scaffold.css'
80
+ m.template 'script.js', 'public/javascripts/ajax_scaffold.js'
81
+ m.template 'rico_corner.js', 'public/javascripts/rico_corner.js'
82
+ m.template 'indicator.gif', 'public/images/indicator.gif'
83
+ m.template 'add.gif', 'public/images/add.gif'
84
+ m.template 'error.gif', 'public/images/error.gif'
85
+
86
+ # Unscaffolded views.
87
+ unscaffolded_actions.each do |action|
88
+ path = File.join('app/views',
89
+ controller_class_path,
90
+ controller_file_name,
91
+ "#{action}.rhtml")
92
+
93
+ m.template 'controller:view.rhtml', path,
94
+ :assigns => { :action => action, :path => path }
95
+ end
96
+ end
97
+ end
98
+
99
+ protected
100
+ # Override with your own usage banner.
101
+ def banner
102
+ "Usage: #{$0} ajax_scaffold ModelName [ControllerName] [action, ...]"
103
+ end
104
+
105
+ def scaffold_views
106
+ %w( list edit new index )
107
+ end
108
+
109
+ def create_sandbox
110
+ sandbox = AjaxScaffoldingSandbox.new
111
+ sandbox.singular_name = singular_name
112
+ begin
113
+ sandbox.model_instance = model_instance
114
+ sandbox.instance_variable_set("@#{singular_name}", sandbox.model_instance)
115
+ rescue ActiveRecord::StatementInvalid => e
116
+ logger.error "Before updating scaffolding from new DB schema, try creating a table for your model (#{class_name})"
117
+ raise SystemExit
118
+ end
119
+ sandbox.suffix = suffix
120
+ sandbox
121
+ end
122
+
123
+ end
Binary file
@@ -0,0 +1,61 @@
1
+ class <%= controller_class_name %>Controller < ApplicationController
2
+
3
+ <% unless suffix -%>
4
+ def index
5
+
6
+ end
7
+ <% end -%>
8
+
9
+ <% for action in unscaffolded_actions -%>
10
+ def <%= action %><%= suffix %>
11
+ end
12
+ <% end -%>
13
+
14
+ def list<%= suffix %>
15
+ @<%= plural_name %> = <%= model_name %>.find :all
16
+ render :layout => false
17
+ end
18
+
19
+ def new<%= suffix %>
20
+ @<%= singular_name %> = <%= model_name %>.new
21
+
22
+ @temp_id = Time.new.to_i
23
+ @headers['<%= singular_name %>-id'] = @temp_id
24
+ @headers['Content-Type'] = 'text/html; charset=utf-8'
25
+
26
+ render :layout => false
27
+
28
+ # If you want to send an error message:
29
+ # render :inline => "Error text goes here", :layout => false, :status => 500
30
+ end
31
+
32
+ def create<%= suffix %>
33
+ @<%= singular_name %> = <%= model_name %>.new(params[:<%= singular_name %>])
34
+ if @<%= singular_name %>.save
35
+ @headers['<%= singular_name %>-id'] = @<%= singular_name %>.id
36
+ @headers['Content-Type'] = 'text/html; charset=utf-8'
37
+ render :partial => '<%= singular_name %><%= suffix %>', :layout => false
38
+ else
39
+ render :partial => 'form_errors', :layout => false, :status => 500
40
+ end
41
+ end
42
+
43
+ def edit<%= suffix %>
44
+ @<%= singular_name %> = <%= model_name %>.find(params[:id])
45
+ render :layout => false
46
+ end
47
+
48
+ def update
49
+ @<%= singular_name %> = <%= model_name %>.find(params[:id])
50
+ if @<%= singular_name %>.update_attributes(params[:<%= singular_name %>])
51
+ render :partial => '<%= singular_name %><%= suffix %>', :layout => false
52
+ else
53
+ render :partial => 'form_errors', :layout => false, :status => 500
54
+ end
55
+ end
56
+
57
+ def destroy<%= suffix %>
58
+ <%= model_name %>.find(params[:id]).destroy
59
+ render :nothing => true
60
+ end
61
+ end
Binary file
@@ -0,0 +1,5 @@
1
+ <fieldset>
2
+ <div class="row">
3
+ <%= template_for_inclusion %>
4
+ </div>
5
+ </fieldset>
@@ -0,0 +1 @@
1
+ <%= all_input_tags(@model_instance, @singular_name, {}) %>
@@ -0,0 +1,88 @@
1
+ require File.dirname(__FILE__) + '<%= "/.." * controller_class_nesting_depth %>/../test_helper'
2
+ require '<%= controller_file_path %>_controller'
3
+
4
+ # Re-raise errors caught by the controller.
5
+ class <%= controller_class_name %>Controller; def rescue_action(e) raise e end; end
6
+
7
+ class <%= controller_class_name %>ControllerTest < Test::Unit::TestCase
8
+ fixtures :<%= table_name %>
9
+
10
+ def setup
11
+ @controller = <%= controller_class_name %>Controller.new
12
+ @request = ActionController::TestRequest.new
13
+ @response = ActionController::TestResponse.new
14
+ end
15
+
16
+ <% for action in unscaffolded_actions -%>
17
+ def test_<%= action %>
18
+ get :<%= action %>
19
+ assert_response :success
20
+ assert_template '<%= action %>'
21
+ end
22
+
23
+ <% end -%>
24
+ <% unless suffix -%>
25
+ def test_index
26
+ get :index
27
+ assert_response :success
28
+ assert_template 'list'
29
+ end
30
+
31
+ <% end -%>
32
+ def test_list<%= suffix %>
33
+ get :list<%= suffix %>
34
+
35
+ assert_response :success
36
+ assert_template 'list<%= suffix %>'
37
+
38
+ assert_not_nil assigns(:<%= plural_name %>)
39
+ end
40
+
41
+ def test_new<%= suffix %>
42
+ get :new<%= suffix %>
43
+
44
+ assert_response :success
45
+ assert_template 'new<%= suffix %>'
46
+
47
+ assert_not_nil assigns(:<%= singular_name %>)
48
+ end
49
+
50
+ def test_create
51
+ num_<%= plural_name %> = <%= model_name %>.count
52
+
53
+ post :create<%= suffix %>, :<%= singular_name %> => {}
54
+
55
+ assert_response :redirect
56
+ assert_redirected_to :action => 'list<%= suffix %>'
57
+
58
+ assert_equal num_<%= plural_name %> + 1, <%= model_name %>.count
59
+ end
60
+
61
+ def test_edit<%= suffix %>
62
+ get :edit<%= suffix %>, :id => 1
63
+
64
+ assert_response :success
65
+ assert_template 'edit<%= suffix %>'
66
+
67
+ assert_not_nil assigns(:<%= singular_name %>)
68
+ assert assigns(:<%= singular_name %>).valid?
69
+ end
70
+
71
+ def test_update<%= suffix %>
72
+ post :update<%= suffix %>, :id => 1
73
+ assert_response :redirect
74
+ assert_redirected_to :action => 'show<%= suffix %>', :id => 1
75
+ end
76
+
77
+ def test_destroy<%= suffix %>
78
+ assert_not_nil <%= model_name %>.find(1)
79
+
80
+ post :destroy, :id => 1
81
+ assert_response :redirect
82
+ assert_redirected_to :action => 'list<%= suffix %>'
83
+
84
+ assert_raise(ActiveRecord::RecordNotFound) {
85
+ <%= model_name %>.find(1)
86
+ }
87
+ end
88
+ end
@@ -0,0 +1,12 @@
1
+ module <%= controller_class_name %>Helper
2
+
3
+ def num_columns
4
+ <%= model_name %>.content_columns.length + 1
5
+ end
6
+
7
+ # This can be moved into application_helper.rb
8
+ def loading_indicator_tag(scope,id)
9
+ "<img src=\"/images/indicator.gif\" style=\"display: none;\" id=\"#{scope}-#{id}-loading-indicator\" alt=\"loading indicator\" class=\"loading-indicator\" />"
10
+ end
11
+
12
+ end
Binary file
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html
2
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3
+ "DTD/xhtml1-strict.dtd">
4
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="en">
5
+ <head>
6
+ <title><%= Inflector.titleize(plural_name) %></title>
7
+ <%%= stylesheet_link_tag 'ajax_scaffold', :media => 'all' %>
8
+ <%%= javascript_include_tag 'prototype', 'effects', 'rico_corner', 'ajax_scaffold' %>
9
+ </head>
10
+ <body>
11
+
12
+ <%%= @content_for_layout %>
13
+
14
+ </body>
15
+ </html>
@@ -0,0 +1,9 @@
1
+ <%% unless @<%= singular_name %>.nil? %>
2
+ <%%= error_messages_for '<%= singular_name %>' %>
3
+ <%% end -%>
4
+
5
+ <%% for name in [:notice, :warning, :error, :message] %>
6
+ <%% if @flash[name] %>
7
+ <%%= "<div class=\"#{name}\">#{@flash[name]}</div>" %>
8
+ <%% end %>
9
+ <%% end %>
@@ -0,0 +1,25 @@
1
+ <tr id="view-<%= singular_name %>-<%%= <%= singular_name %>.id %>">
2
+ <%% for column in <%= model_name %>.content_columns %>
3
+ <td><%%=h <%= singular_name %>.send(column.name) %>&nbsp;</td>
4
+ <%% end %>
5
+ <td class="actions">
6
+ <div>
7
+ <%%= loading_indicator_tag '<%= singular_name %>', "edit-#{<%= singular_name %>.id}" %>
8
+ <%%= link_to_remote "Edit",
9
+ :url => { :controller => '<%= controller_name %>', :action => 'edit', :id => <%= singular_name %> },
10
+ :loading => "AjaxScaffold.editOnLoading(request,'<%= singular_name %>', #{<%= singular_name %>.id});",
11
+ :success => "AjaxScaffold.editOnSuccess(request,'<%= singular_name %>', #{<%= singular_name %>.id});",
12
+ :failure => "AjaxScaffold.editOnFailure(request,'<%= singular_name %>', #{<%= singular_name %>.id});",
13
+ :complete => "AjaxScaffold.editOnComplete(request,'<%= singular_name %>', #{<%= singular_name %>.id});",
14
+ :post => true; %>
15
+ <%%= link_to_remote "Delete",
16
+ :url => { :controller => '<%= controller_name %>',:action => 'destroy', :id => <%= singular_name %> },
17
+ :confirm => 'Are you sure?',
18
+ :loading => "AjaxScaffold.deleteOnLoading('<%= singular_name %>', #{<%= singular_name %>.id});",
19
+ :success => "AjaxScaffold.deleteOnSuccess('<%= singular_name %>', #{<%= singular_name %>.id});",
20
+ :failure => "AjaxScaffold.deleteOnFailure('<%= singular_name %>', #{<%= singular_name %>.id});",
21
+ :complete => "AjaxScaffold.deleteOnComplete('<%= singular_name %>', #{<%= singular_name %>.id});",
22
+ :post => true; %>
23
+ </div>
24
+ </td>
25
+ </tr>
@@ -0,0 +1,781 @@
1
+ /**
2
+ *
3
+ * Copyright 2005 Sabre Airline Solutions
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
6
+ * file except in compliance with the License. You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software distributed under the
11
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
12
+ * either express or implied. See the License for the specific language governing permissions
13
+ * and limitations under the License.
14
+ **/
15
+
16
+
17
+ //-------------------- rico.js
18
+ var Rico = {
19
+ Version: '1.1.0',
20
+ prototypeVersion: parseFloat(Prototype.Version.split(".")[0] + "." + Prototype.Version.split(".")[1])
21
+ }
22
+
23
+
24
+
25
+
26
+ //-------------------- ricoColor.js
27
+ Rico.Color = Class.create();
28
+
29
+ Rico.Color.prototype = {
30
+
31
+ initialize: function(red, green, blue) {
32
+ this.rgb = { r: red, g : green, b : blue };
33
+ },
34
+
35
+ setRed: function(r) {
36
+ this.rgb.r = r;
37
+ },
38
+
39
+ setGreen: function(g) {
40
+ this.rgb.g = g;
41
+ },
42
+
43
+ setBlue: function(b) {
44
+ this.rgb.b = b;
45
+ },
46
+
47
+ setHue: function(h) {
48
+
49
+ // get an HSB model, and set the new hue...
50
+ var hsb = this.asHSB();
51
+ hsb.h = h;
52
+
53
+ // convert back to RGB...
54
+ this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
55
+ },
56
+
57
+ setSaturation: function(s) {
58
+ // get an HSB model, and set the new hue...
59
+ var hsb = this.asHSB();
60
+ hsb.s = s;
61
+
62
+ // convert back to RGB and set values...
63
+ this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
64
+ },
65
+
66
+ setBrightness: function(b) {
67
+ // get an HSB model, and set the new hue...
68
+ var hsb = this.asHSB();
69
+ hsb.b = b;
70
+
71
+ // convert back to RGB and set values...
72
+ this.rgb = Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b );
73
+ },
74
+
75
+ darken: function(percent) {
76
+ var hsb = this.asHSB();
77
+ this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0));
78
+ },
79
+
80
+ brighten: function(percent) {
81
+ var hsb = this.asHSB();
82
+ this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1));
83
+ },
84
+
85
+ blend: function(other) {
86
+ this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2);
87
+ this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2);
88
+ this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2);
89
+ },
90
+
91
+ isBright: function() {
92
+ var hsb = this.asHSB();
93
+ return this.asHSB().b > 0.5;
94
+ },
95
+
96
+ isDark: function() {
97
+ return ! this.isBright();
98
+ },
99
+
100
+ asRGB: function() {
101
+ return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
102
+ },
103
+
104
+ asHex: function() {
105
+ return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
106
+ },
107
+
108
+ asHSB: function() {
109
+ return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
110
+ },
111
+
112
+ toString: function() {
113
+ return this.asHex();
114
+ }
115
+
116
+ };
117
+
118
+ Rico.Color.createFromHex = function(hexCode) {
119
+ if(hexCode.length==4) {
120
+ var shortHexCode = hexCode;
121
+ var hexCode = '#';
122
+ for(var i=1;i<4;i++) hexCode += (shortHexCode.charAt(i) +
123
+ shortHexCode.charAt(i));
124
+ }
125
+ if ( hexCode.indexOf('#') == 0 )
126
+ hexCode = hexCode.substring(1);
127
+ var red = hexCode.substring(0,2);
128
+ var green = hexCode.substring(2,4);
129
+ var blue = hexCode.substring(4,6);
130
+ return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) );
131
+ }
132
+
133
+ /**
134
+ * Factory method for creating a color from the background of
135
+ * an HTML element.
136
+ */
137
+ Rico.Color.createColorFromBackground = function(elem) {
138
+
139
+ var actualColor = RicoUtil.getElementsComputedStyle($(elem), "backgroundColor", "background-color");
140
+
141
+ if ( actualColor == "transparent" && elem.parentNode )
142
+ return Rico.Color.createColorFromBackground(elem.parentNode);
143
+
144
+ if ( actualColor == null )
145
+ return new Rico.Color(255,255,255);
146
+
147
+ if ( actualColor.indexOf("rgb(") == 0 ) {
148
+ var colors = actualColor.substring(4, actualColor.length - 1 );
149
+ var colorArray = colors.split(",");
150
+ return new Rico.Color( parseInt( colorArray[0] ),
151
+ parseInt( colorArray[1] ),
152
+ parseInt( colorArray[2] ) );
153
+
154
+ }
155
+ else if ( actualColor.indexOf("#") == 0 ) {
156
+ return Rico.Color.createFromHex(actualColor);
157
+ }
158
+ else
159
+ return new Rico.Color(255,255,255);
160
+ }
161
+
162
+ Rico.Color.HSBtoRGB = function(hue, saturation, brightness) {
163
+
164
+ var red = 0;
165
+ var green = 0;
166
+ var blue = 0;
167
+
168
+ if (saturation == 0) {
169
+ red = parseInt(brightness * 255.0 + 0.5);
170
+ green = red;
171
+ blue = red;
172
+ }
173
+ else {
174
+ var h = (hue - Math.floor(hue)) * 6.0;
175
+ var f = h - Math.floor(h);
176
+ var p = brightness * (1.0 - saturation);
177
+ var q = brightness * (1.0 - saturation * f);
178
+ var t = brightness * (1.0 - (saturation * (1.0 - f)));
179
+
180
+ switch (parseInt(h)) {
181
+ case 0:
182
+ red = (brightness * 255.0 + 0.5);
183
+ green = (t * 255.0 + 0.5);
184
+ blue = (p * 255.0 + 0.5);
185
+ break;
186
+ case 1:
187
+ red = (q * 255.0 + 0.5);
188
+ green = (brightness * 255.0 + 0.5);
189
+ blue = (p * 255.0 + 0.5);
190
+ break;
191
+ case 2:
192
+ red = (p * 255.0 + 0.5);
193
+ green = (brightness * 255.0 + 0.5);
194
+ blue = (t * 255.0 + 0.5);
195
+ break;
196
+ case 3:
197
+ red = (p * 255.0 + 0.5);
198
+ green = (q * 255.0 + 0.5);
199
+ blue = (brightness * 255.0 + 0.5);
200
+ break;
201
+ case 4:
202
+ red = (t * 255.0 + 0.5);
203
+ green = (p * 255.0 + 0.5);
204
+ blue = (brightness * 255.0 + 0.5);
205
+ break;
206
+ case 5:
207
+ red = (brightness * 255.0 + 0.5);
208
+ green = (p * 255.0 + 0.5);
209
+ blue = (q * 255.0 + 0.5);
210
+ break;
211
+ }
212
+ }
213
+
214
+ return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) };
215
+ }
216
+
217
+ Rico.Color.RGBtoHSB = function(r, g, b) {
218
+
219
+ var hue;
220
+ var saturation;
221
+ var brightness;
222
+
223
+ var cmax = (r > g) ? r : g;
224
+ if (b > cmax)
225
+ cmax = b;
226
+
227
+ var cmin = (r < g) ? r : g;
228
+ if (b < cmin)
229
+ cmin = b;
230
+
231
+ brightness = cmax / 255.0;
232
+ if (cmax != 0)
233
+ saturation = (cmax - cmin)/cmax;
234
+ else
235
+ saturation = 0;
236
+
237
+ if (saturation == 0)
238
+ hue = 0;
239
+ else {
240
+ var redc = (cmax - r)/(cmax - cmin);
241
+ var greenc = (cmax - g)/(cmax - cmin);
242
+ var bluec = (cmax - b)/(cmax - cmin);
243
+
244
+ if (r == cmax)
245
+ hue = bluec - greenc;
246
+ else if (g == cmax)
247
+ hue = 2.0 + redc - bluec;
248
+ else
249
+ hue = 4.0 + greenc - redc;
250
+
251
+ hue = hue / 6.0;
252
+ if (hue < 0)
253
+ hue = hue + 1.0;
254
+ }
255
+
256
+ return { h : hue, s : saturation, b : brightness };
257
+ }
258
+
259
+
260
+ //-------------------- ricoCorner.js
261
+ Rico.Corner = {
262
+
263
+ round: function(e, options) {
264
+ var e = $(e);
265
+ this._setOptions(options);
266
+
267
+ var color = this.options.color;
268
+ if ( this.options.color == "fromElement" )
269
+ color = this._background(e);
270
+
271
+ var bgColor = this.options.bgColor;
272
+ if ( this.options.bgColor == "fromParent" )
273
+ bgColor = this._background(e.offsetParent);
274
+
275
+ this._roundCornersImpl(e, color, bgColor);
276
+ },
277
+
278
+ _roundCornersImpl: function(e, color, bgColor) {
279
+ if(this.options.border)
280
+ this._renderBorder(e,bgColor);
281
+ if(this._isTopRounded())
282
+ this._roundTopCorners(e,color,bgColor);
283
+ if(this._isBottomRounded())
284
+ this._roundBottomCorners(e,color,bgColor);
285
+ },
286
+
287
+ _renderBorder: function(el,bgColor) {
288
+ var borderValue = "1px solid " + this._borderColor(bgColor);
289
+ var borderL = "border-left: " + borderValue;
290
+ var borderR = "border-right: " + borderValue;
291
+ var style = "style='" + borderL + ";" + borderR + "'";
292
+ el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>"
293
+ },
294
+
295
+ _roundTopCorners: function(el, color, bgColor) {
296
+ var corner = this._createCorner(bgColor);
297
+ for(var i=0 ; i < this.options.numSlices ; i++ )
298
+ corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));
299
+ el.style.paddingTop = 0;
300
+ el.insertBefore(corner,el.firstChild);
301
+ },
302
+
303
+ _roundBottomCorners: function(el, color, bgColor) {
304
+ var corner = this._createCorner(bgColor);
305
+ for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- )
306
+ corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));
307
+ el.style.paddingBottom = 0;
308
+ el.appendChild(corner);
309
+ },
310
+
311
+ _createCorner: function(bgColor) {
312
+ var corner = document.createElement("div");
313
+ corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor);
314
+ return corner;
315
+ },
316
+
317
+ _createCornerSlice: function(color,bgColor, n, position) {
318
+ var slice = document.createElement("span");
319
+
320
+ var inStyle = slice.style;
321
+ inStyle.backgroundColor = color;
322
+ inStyle.display = "block";
323
+ inStyle.height = "1px";
324
+ inStyle.overflow = "hidden";
325
+ inStyle.fontSize = "1px";
326
+
327
+ var borderColor = this._borderColor(color,bgColor);
328
+ if ( this.options.border && n == 0 ) {
329
+ inStyle.borderTopStyle = "solid";
330
+ inStyle.borderTopWidth = "1px";
331
+ inStyle.borderLeftWidth = "0px";
332
+ inStyle.borderRightWidth = "0px";
333
+ inStyle.borderBottomWidth = "0px";
334
+ inStyle.height = "0px"; // assumes css compliant box model
335
+ inStyle.borderColor = borderColor;
336
+ }
337
+ else if(borderColor) {
338
+ inStyle.borderColor = borderColor;
339
+ inStyle.borderStyle = "solid";
340
+ inStyle.borderWidth = "0px 1px";
341
+ }
342
+
343
+ if ( !this.options.compact && (n == (this.options.numSlices-1)) )
344
+ inStyle.height = "2px";
345
+
346
+ this._setMargin(slice, n, position);
347
+ this._setBorder(slice, n, position);
348
+ return slice;
349
+ },
350
+
351
+ _setOptions: function(options) {
352
+ this.options = {
353
+ corners : "all",
354
+ color : "fromElement",
355
+ bgColor : "fromParent",
356
+ blend : true,
357
+ border : false,
358
+ compact : false
359
+ }
360
+ Object.extend(this.options, options || {});
361
+
362
+ this.options.numSlices = this.options.compact ? 2 : 4;
363
+ if ( this._isTransparent() )
364
+ this.options.blend = false;
365
+ },
366
+
367
+ _whichSideTop: function() {
368
+ if ( this._hasString(this.options.corners, "all", "top") )
369
+ return "";
370
+
371
+ if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 )
372
+ return "";
373
+
374
+ if (this.options.corners.indexOf("tl") >= 0)
375
+ return "left";
376
+ else if (this.options.corners.indexOf("tr") >= 0)
377
+ return "right";
378
+ return "";
379
+ },
380
+
381
+ _whichSideBottom: function() {
382
+ if ( this._hasString(this.options.corners, "all", "bottom") )
383
+ return "";
384
+
385
+ if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 )
386
+ return "";
387
+
388
+ if(this.options.corners.indexOf("bl") >=0)
389
+ return "left";
390
+ else if(this.options.corners.indexOf("br")>=0)
391
+ return "right";
392
+ return "";
393
+ },
394
+
395
+ _borderColor : function(color,bgColor) {
396
+ if ( color == "transparent" )
397
+ return bgColor;
398
+ else if ( this.options.border )
399
+ return this.options.border;
400
+ else if ( this.options.blend )
401
+ return this._blend( bgColor, color );
402
+ else
403
+ return "";
404
+ },
405
+
406
+
407
+ _setMargin: function(el, n, corners) {
408
+ var marginSize = this._marginSize(n);
409
+ var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
410
+
411
+ if ( whichSide == "left" ) {
412
+ el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px";
413
+ }
414
+ else if ( whichSide == "right" ) {
415
+ el.style.marginRight = marginSize + "px"; el.style.marginLeft = "0px";
416
+ }
417
+ else {
418
+ el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px";
419
+ }
420
+ },
421
+
422
+ _setBorder: function(el,n,corners) {
423
+ var borderSize = this._borderSize(n);
424
+ var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
425
+ if ( whichSide == "left" ) {
426
+ el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px";
427
+ }
428
+ else if ( whichSide == "right" ) {
429
+ el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth = "0px";
430
+ }
431
+ else {
432
+ el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
433
+ }
434
+ if (this.options.border != false)
435
+ el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
436
+ },
437
+
438
+ _marginSize: function(n) {
439
+ if ( this._isTransparent() )
440
+ return 0;
441
+
442
+ var marginSizes = [ 5, 3, 2, 1 ];
443
+ var blendedMarginSizes = [ 3, 2, 1, 0 ];
444
+ var compactMarginSizes = [ 2, 1 ];
445
+ var smBlendedMarginSizes = [ 1, 0 ];
446
+
447
+ if ( this.options.compact && this.options.blend )
448
+ return smBlendedMarginSizes[n];
449
+ else if ( this.options.compact )
450
+ return compactMarginSizes[n];
451
+ else if ( this.options.blend )
452
+ return blendedMarginSizes[n];
453
+ else
454
+ return marginSizes[n];
455
+ },
456
+
457
+ _borderSize: function(n) {
458
+ var transparentBorderSizes = [ 5, 3, 2, 1 ];
459
+ var blendedBorderSizes = [ 2, 1, 1, 1 ];
460
+ var compactBorderSizes = [ 1, 0 ];
461
+ var actualBorderSizes = [ 0, 2, 0, 0 ];
462
+
463
+ if ( this.options.compact && (this.options.blend || this._isTransparent()) )
464
+ return 1;
465
+ else if ( this.options.compact )
466
+ return compactBorderSizes[n];
467
+ else if ( this.options.blend )
468
+ return blendedBorderSizes[n];
469
+ else if ( this.options.border )
470
+ return actualBorderSizes[n];
471
+ else if ( this._isTransparent() )
472
+ return transparentBorderSizes[n];
473
+ return 0;
474
+ },
475
+
476
+ _hasString: function(str) { for(var i=1 ; i<arguments.length ; i++) if (str.indexOf(arguments[i]) >= 0) return true; return false; },
477
+ _blend: function(c1, c2) { var cc1 = Rico.Color.createFromHex(c1); cc1.blend(Rico.Color.createFromHex(c2)); return cc1; },
478
+ _background: function(el) { try { return Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } },
479
+ _isTransparent: function() { return this.options.color == "transparent"; },
480
+ _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); },
481
+ _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); },
482
+ _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; }
483
+ }
484
+
485
+ //-------------------- ricoUtil.js
486
+ Rico.ArrayExtensions = new Array();
487
+
488
+ if (Object.prototype.extend) {
489
+ // in prototype.js...
490
+ Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend;
491
+ }else{
492
+ Object.prototype.extend = function(object) {
493
+ return Object.extend.apply(this, [this, object]);
494
+ }
495
+ Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend;
496
+ }
497
+
498
+ if (Array.prototype.push) {
499
+ // in prototype.js...
500
+ Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.push;
501
+ }
502
+
503
+ if (!Array.prototype.remove) {
504
+ Array.prototype.remove = function(dx) {
505
+ if( isNaN(dx) || dx > this.length )
506
+ return false;
507
+ for( var i=0,n=0; i<this.length; i++ )
508
+ if( i != dx )
509
+ this[n++]=this[i];
510
+ this.length-=1;
511
+ };
512
+ Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.remove;
513
+ }
514
+
515
+ if (!Array.prototype.removeItem) {
516
+ Array.prototype.removeItem = function(item) {
517
+ for ( var i = 0 ; i < this.length ; i++ )
518
+ if ( this[i] == item ) {
519
+ this.remove(i);
520
+ break;
521
+ }
522
+ };
523
+ Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.removeItem;
524
+ }
525
+
526
+ if (!Array.prototype.indices) {
527
+ Array.prototype.indices = function() {
528
+ var indexArray = new Array();
529
+ for ( index in this ) {
530
+ var ignoreThis = false;
531
+ for ( var i = 0 ; i < Rico.ArrayExtensions.length ; i++ ) {
532
+ if ( this[index] == Rico.ArrayExtensions[i] ) {
533
+ ignoreThis = true;
534
+ break;
535
+ }
536
+ }
537
+ if ( !ignoreThis )
538
+ indexArray[ indexArray.length ] = index;
539
+ }
540
+ return indexArray;
541
+ }
542
+ Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.indices;
543
+ }
544
+
545
+ Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.unique;
546
+ Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.inArray;
547
+
548
+
549
+ // Create the loadXML method and xml getter for Mozilla
550
+ if ( window.DOMParser &&
551
+ window.XMLSerializer &&
552
+ window.Node && Node.prototype && Node.prototype.__defineGetter__ ) {
553
+
554
+ if (!Document.prototype.loadXML) {
555
+ Document.prototype.loadXML = function (s) {
556
+ var doc2 = (new DOMParser()).parseFromString(s, "text/xml");
557
+ while (this.hasChildNodes())
558
+ this.removeChild(this.lastChild);
559
+
560
+ for (var i = 0; i < doc2.childNodes.length; i++) {
561
+ this.appendChild(this.importNode(doc2.childNodes[i], true));
562
+ }
563
+ };
564
+ }
565
+
566
+ Document.prototype.__defineGetter__( "xml",
567
+ function () {
568
+ return (new XMLSerializer()).serializeToString(this);
569
+ }
570
+ );
571
+ }
572
+
573
+ document.getElementsByTagAndClassName = function(tagName, className) {
574
+ if ( tagName == null )
575
+ tagName = '*';
576
+
577
+ var children = document.getElementsByTagName(tagName) || document.all;
578
+ var elements = new Array();
579
+
580
+ if ( className == null )
581
+ return children;
582
+
583
+ for (var i = 0; i < children.length; i++) {
584
+ var child = children[i];
585
+ var classNames = child.className.split(' ');
586
+ for (var j = 0; j < classNames.length; j++) {
587
+ if (classNames[j] == className) {
588
+ elements.push(child);
589
+ break;
590
+ }
591
+ }
592
+ }
593
+
594
+ return elements;
595
+ }
596
+
597
+ var RicoUtil = {
598
+
599
+ getElementsComputedStyle: function ( htmlElement, cssProperty, mozillaEquivalentCSS) {
600
+ if ( arguments.length == 2 )
601
+ mozillaEquivalentCSS = cssProperty;
602
+
603
+ var el = $(htmlElement);
604
+ if ( el.currentStyle )
605
+ return el.currentStyle[cssProperty];
606
+ else
607
+ return document.defaultView.getComputedStyle(el, null).getPropertyValue(mozillaEquivalentCSS);
608
+ },
609
+
610
+ createXmlDocument : function() {
611
+ if (document.implementation && document.implementation.createDocument) {
612
+ var doc = document.implementation.createDocument("", "", null);
613
+
614
+ if (doc.readyState == null) {
615
+ doc.readyState = 1;
616
+ doc.addEventListener("load", function () {
617
+ doc.readyState = 4;
618
+ if (typeof doc.onreadystatechange == "function")
619
+ doc.onreadystatechange();
620
+ }, false);
621
+ }
622
+
623
+ return doc;
624
+ }
625
+
626
+ if (window.ActiveXObject)
627
+ return Try.these(
628
+ function() { return new ActiveXObject('MSXML2.DomDocument') },
629
+ function() { return new ActiveXObject('Microsoft.DomDocument')},
630
+ function() { return new ActiveXObject('MSXML.DomDocument') },
631
+ function() { return new ActiveXObject('MSXML3.DomDocument') }
632
+ ) || false;
633
+
634
+ return null;
635
+ },
636
+
637
+ getContentAsString: function( parentNode ) {
638
+ return parentNode.xml != undefined ?
639
+ this._getContentAsStringIE(parentNode) :
640
+ this._getContentAsStringMozilla(parentNode);
641
+ },
642
+
643
+ _getContentAsStringIE: function(parentNode) {
644
+ var contentStr = "";
645
+ for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
646
+ var n = parentNode.childNodes[i];
647
+ if (n.nodeType == 4) {
648
+ contentStr += n.nodeValue;
649
+ }
650
+ else {
651
+ contentStr += n.xml;
652
+ }
653
+ }
654
+ return contentStr;
655
+ },
656
+
657
+ _getContentAsStringMozilla: function(parentNode) {
658
+ var xmlSerializer = new XMLSerializer();
659
+ var contentStr = "";
660
+ for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
661
+ var n = parentNode.childNodes[i];
662
+ if (n.nodeType == 4) { // CDATA node
663
+ contentStr += n.nodeValue;
664
+ }
665
+ else {
666
+ contentStr += xmlSerializer.serializeToString(n);
667
+ }
668
+ }
669
+ return contentStr;
670
+ },
671
+
672
+ toViewportPosition: function(element) {
673
+ return this._toAbsolute(element,true);
674
+ },
675
+
676
+ toDocumentPosition: function(element) {
677
+ return this._toAbsolute(element,false);
678
+ },
679
+
680
+ /**
681
+ * Compute the elements position in terms of the window viewport
682
+ * so that it can be compared to the position of the mouse (dnd)
683
+ * This is additions of all the offsetTop,offsetLeft values up the
684
+ * offsetParent hierarchy, ...taking into account any scrollTop,
685
+ * scrollLeft values along the way...
686
+ *
687
+ * IE has a bug reporting a correct offsetLeft of elements within a
688
+ * a relatively positioned parent!!!
689
+ **/
690
+ _toAbsolute: function(element,accountForDocScroll) {
691
+
692
+ if ( navigator.userAgent.toLowerCase().indexOf("msie") == -1 )
693
+ return this._toAbsoluteMozilla(element,accountForDocScroll);
694
+
695
+ var x = 0;
696
+ var y = 0;
697
+ var parent = element;
698
+ while ( parent ) {
699
+
700
+ var borderXOffset = 0;
701
+ var borderYOffset = 0;
702
+ if ( parent != element ) {
703
+ var borderXOffset = parseInt(this.getElementsComputedStyle(parent, "borderLeftWidth" ));
704
+ var borderYOffset = parseInt(this.getElementsComputedStyle(parent, "borderTopWidth" ));
705
+ borderXOffset = isNaN(borderXOffset) ? 0 : borderXOffset;
706
+ borderYOffset = isNaN(borderYOffset) ? 0 : borderYOffset;
707
+ }
708
+
709
+ x += parent.offsetLeft - parent.scrollLeft + borderXOffset;
710
+ y += parent.offsetTop - parent.scrollTop + borderYOffset;
711
+ parent = parent.offsetParent;
712
+ }
713
+
714
+ if ( accountForDocScroll ) {
715
+ x -= this.docScrollLeft();
716
+ y -= this.docScrollTop();
717
+ }
718
+
719
+ return { x:x, y:y };
720
+ },
721
+
722
+ /**
723
+ * Mozilla did not report all of the parents up the hierarchy via the
724
+ * offsetParent property that IE did. So for the calculation of the
725
+ * offsets we use the offsetParent property, but for the calculation of
726
+ * the scrollTop/scrollLeft adjustments we navigate up via the parentNode
727
+ * property instead so as to get the scroll offsets...
728
+ *
729
+ **/
730
+ _toAbsoluteMozilla: function(element,accountForDocScroll) {
731
+ var x = 0;
732
+ var y = 0;
733
+ var parent = element;
734
+ while ( parent ) {
735
+ x += parent.offsetLeft;
736
+ y += parent.offsetTop;
737
+ parent = parent.offsetParent;
738
+ }
739
+
740
+ parent = element;
741
+ while ( parent &&
742
+ parent != document.body &&
743
+ parent != document.documentElement ) {
744
+ if ( parent.scrollLeft )
745
+ x -= parent.scrollLeft;
746
+ if ( parent.scrollTop )
747
+ y -= parent.scrollTop;
748
+ parent = parent.parentNode;
749
+ }
750
+
751
+ if ( accountForDocScroll ) {
752
+ x -= this.docScrollLeft();
753
+ y -= this.docScrollTop();
754
+ }
755
+
756
+ return { x:x, y:y };
757
+ },
758
+
759
+ docScrollLeft: function() {
760
+ if ( window.pageXOffset )
761
+ return window.pageXOffset;
762
+ else if ( document.documentElement && document.documentElement.scrollLeft )
763
+ return document.documentElement.scrollLeft;
764
+ else if ( document.body )
765
+ return document.body.scrollLeft;
766
+ else
767
+ return 0;
768
+ },
769
+
770
+ docScrollTop: function() {
771
+ if ( window.pageYOffset )
772
+ return window.pageYOffset;
773
+ else if ( document.documentElement && document.documentElement.scrollTop )
774
+ return document.documentElement.scrollTop;
775
+ else if ( document.body )
776
+ return document.body.scrollTop;
777
+ else
778
+ return 0;
779
+ }
780
+
781
+ };