##// END OF EJS Templates
Added a bit of AJAX on the SCM browser (tree view)....
Jean-Philippe Lang -
r849:8ca4d35dcc46
parent child
Show More
@@ -0,0 +1,32
1 <% @entries.each do |entry| %>
2 <% tr_id = Digest::MD5.hexdigest(entry.path)
3 depth = params[:depth].to_i %>
4 <tr id="<%= tr_id %>">
5 <td>
6 <%= if entry.is_dir?
7 link_to_remote h(entry.name),
8 {:url => {:action => 'browse', :id => @project, :path => entry.path, :rev => @rev, :depth => (depth + 1)},
9 :update => tr_id,
10 :position => :after,
11 :success => "Element.addClassName('#{tr_id}', 'open');",
12 :condition => "!Element.hasClassName('#{tr_id}', 'open')"
13 },
14 {:href => url_for({:action => 'browse', :id => @project, :path => entry.path, :rev => @rev}),
15 :class => ('icon icon-folder'),
16 :style => "margin-left: #{18 * depth}px;"
17 }
18 else
19 link_to h(entry.name),
20 {:action => (entry.is_dir? ? 'browse' : 'changes'), :id => @project, :path => entry.path, :rev => @rev},
21 :class => 'icon icon-file',
22 :style => "margin-left: #{18 * depth}px;"
23 end %>
24 </td>
25 <td align="right"><%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %></td>
26 <td align="right"><%= link_to(entry.lastrev.name, :action => 'revision', :id => @project, :rev => entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %></td>
27 <td align="center"><%= format_time(entry.lastrev.time) if entry.lastrev %></td>
28 <td align="center"><em><%=h(entry.lastrev.author) if entry.lastrev %></em></td>
29 <% changeset = @project.repository.changesets.find_by_revision(entry.lastrev.identifier) if entry.lastrev %>
30 <td><%=h truncate(changeset.comments, 50) unless changeset.nil? %></td>
31 </tr>
32 <% end %>
1 NO CONTENT: new file 100644, binary diff hidden
@@ -1,253 +1,257
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'SVG/Graph/Bar'
19 19 require 'SVG/Graph/BarHorizontal'
20 20 require 'digest/sha1'
21 21
22 22 class RepositoriesController < ApplicationController
23 23 layout 'base'
24 24 before_filter :find_repository, :except => :edit
25 25 before_filter :find_project, :only => :edit
26 26 before_filter :authorize
27 27 accept_key_auth :revisions
28 28
29 29 def edit
30 30 @repository = @project.repository
31 31 if !@repository
32 32 @repository = Repository.factory(params[:repository_scm])
33 33 @repository.project = @project
34 34 end
35 35 if request.post?
36 36 @repository.attributes = params[:repository]
37 37 @repository.save
38 38 end
39 39 render(:update) {|page| page.replace_html "tab-content-repository", :partial => 'projects/settings/repository'}
40 40 end
41 41
42 42 def destroy
43 43 @repository.destroy
44 44 redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
45 45 end
46 46
47 47 def show
48 48 # check if new revisions have been committed in the repository
49 49 @repository.fetch_changesets if Setting.autofetch_changesets?
50 50 # get entries for the browse frame
51 51 @entries = @repository.entries('')
52 52 # latest changesets
53 53 @changesets = @repository.changesets.find(:all, :limit => 10, :order => "committed_on DESC")
54 54 show_error and return unless @entries || @changesets.any?
55 55 end
56 56
57 57 def browse
58 58 @entries = @repository.entries(@path, @rev)
59 show_error and return unless @entries
59 if request.xhr?
60 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
61 else
62 show_error unless @entries
63 end
60 64 end
61 65
62 66 def changes
63 67 @entry = @repository.scm.entry(@path, @rev)
64 68 show_error and return unless @entry
65 69 @changesets = @repository.changesets_for_path(@path)
66 70 end
67 71
68 72 def revisions
69 73 @changeset_count = @repository.changesets.count
70 74 @changeset_pages = Paginator.new self, @changeset_count,
71 75 25,
72 76 params['page']
73 77 @changesets = @repository.changesets.find(:all,
74 78 :limit => @changeset_pages.items_per_page,
75 79 :offset => @changeset_pages.current.offset)
76 80
77 81 respond_to do |format|
78 82 format.html { render :layout => false if request.xhr? }
79 83 format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
80 84 end
81 85 end
82 86
83 87 def entry
84 88 @content = @repository.scm.cat(@path, @rev)
85 89 show_error and return unless @content
86 90 if 'raw' == params[:format]
87 91 send_data @content, :filename => @path.split('/').last
88 92 end
89 93 end
90 94
91 95 def revision
92 96 @changeset = @repository.changesets.find_by_revision(@rev)
93 97 show_error and return unless @changeset
94 98 @changes_count = @changeset.changes.size
95 99 @changes_pages = Paginator.new self, @changes_count, 150, params['page']
96 100 @changes = @changeset.changes.find(:all,
97 101 :limit => @changes_pages.items_per_page,
98 102 :offset => @changes_pages.current.offset)
99 103
100 104 render :action => "revision", :layout => false if request.xhr?
101 105 end
102 106
103 107 def diff
104 108 @rev_to = params[:rev_to] ? params[:rev_to].to_i : (@rev - 1)
105 109 @diff_type = ('sbs' == params[:type]) ? 'sbs' : 'inline'
106 110
107 111 @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}")
108 112 unless read_fragment(@cache_key)
109 113 @diff = @repository.diff(@path, @rev, @rev_to, type)
110 114 show_error and return unless @diff
111 115 end
112 116 end
113 117
114 118 def stats
115 119 end
116 120
117 121 def graph
118 122 data = nil
119 123 case params[:graph]
120 124 when "commits_per_month"
121 125 data = graph_commits_per_month(@repository)
122 126 when "commits_per_author"
123 127 data = graph_commits_per_author(@repository)
124 128 end
125 129 if data
126 130 headers["Content-Type"] = "image/svg+xml"
127 131 send_data(data, :type => "image/svg+xml", :disposition => "inline")
128 132 else
129 133 render_404
130 134 end
131 135 end
132 136
133 137 private
134 138 def find_project
135 139 @project = Project.find(params[:id])
136 140 rescue ActiveRecord::RecordNotFound
137 141 render_404
138 142 end
139 143
140 144 def find_repository
141 145 @project = Project.find(params[:id])
142 146 @repository = @project.repository
143 147 render_404 and return false unless @repository
144 148 @path = params[:path].squeeze('/') if params[:path]
145 149 @path ||= ''
146 150 @rev = params[:rev].to_i if params[:rev]
147 151 rescue ActiveRecord::RecordNotFound
148 152 render_404
149 153 end
150 154
151 155 def show_error
152 156 flash.now[:error] = l(:notice_scm_error)
153 157 render :nothing => true, :layout => true
154 158 end
155 159
156 160 def graph_commits_per_month(repository)
157 161 @date_to = Date.today
158 162 @date_from = @date_to << 11
159 163 @date_from = Date.civil(@date_from.year, @date_from.month, 1)
160 164 commits_by_day = repository.changesets.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
161 165 commits_by_month = [0] * 12
162 166 commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
163 167
164 168 changes_by_day = repository.changes.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
165 169 changes_by_month = [0] * 12
166 170 changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
167 171
168 172 fields = []
169 173 month_names = l(:actionview_datehelper_select_month_names_abbr).split(',')
170 174 12.times {|m| fields << month_names[((Date.today.month - 1 - m) % 12)]}
171 175
172 176 graph = SVG::Graph::Bar.new(
173 177 :height => 300,
174 178 :width => 500,
175 179 :fields => fields.reverse,
176 180 :stack => :side,
177 181 :scale_integers => true,
178 182 :step_x_labels => 2,
179 183 :show_data_values => false,
180 184 :graph_title => l(:label_commits_per_month),
181 185 :show_graph_title => true
182 186 )
183 187
184 188 graph.add_data(
185 189 :data => commits_by_month[0..11].reverse,
186 190 :title => l(:label_revision_plural)
187 191 )
188 192
189 193 graph.add_data(
190 194 :data => changes_by_month[0..11].reverse,
191 195 :title => l(:label_change_plural)
192 196 )
193 197
194 198 graph.burn
195 199 end
196 200
197 201 def graph_commits_per_author(repository)
198 202 commits_by_author = repository.changesets.count(:all, :group => :committer)
199 203 commits_by_author.sort! {|x, y| x.last <=> y.last}
200 204
201 205 changes_by_author = repository.changes.count(:all, :group => :committer)
202 206 h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
203 207
204 208 fields = commits_by_author.collect {|r| r.first}
205 209 commits_data = commits_by_author.collect {|r| r.last}
206 210 changes_data = commits_by_author.collect {|r| h[r.first] || 0}
207 211
208 212 fields = fields + [""]*(10 - fields.length) if fields.length<10
209 213 commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
210 214 changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
211 215
212 216 graph = SVG::Graph::BarHorizontal.new(
213 217 :height => 300,
214 218 :width => 500,
215 219 :fields => fields,
216 220 :stack => :side,
217 221 :scale_integers => true,
218 222 :show_data_values => false,
219 223 :rotate_y_labels => false,
220 224 :graph_title => l(:label_commits_per_author),
221 225 :show_graph_title => true
222 226 )
223 227
224 228 graph.add_data(
225 229 :data => commits_data,
226 230 :title => l(:label_revision_plural)
227 231 )
228 232
229 233 graph.add_data(
230 234 :data => changes_data,
231 235 :title => l(:label_change_plural)
232 236 )
233 237
234 238 graph.burn
235 239 end
236 240
237 241 end
238 242
239 243 class Date
240 244 def months_ago(date = Date.today)
241 245 (date.year - self.year)*12 + (date.month - self.month)
242 246 end
243 247
244 248 def weeks_ago(date = Date.today)
245 249 (date.year - self.year)*52 + (date.cweek - self.cweek)
246 250 end
247 251 end
248 252
249 253 class String
250 254 def with_leading_slash
251 255 starts_with?('/') ? self : "/#{self}"
252 256 end
253 257 end
@@ -1,26 +1,15
1 1 <table class="list">
2 <thead><tr>
2 <thead>
3 <tr id="root">
3 4 <th><%= l(:field_name) %></th>
4 5 <th><%= l(:field_filesize) %></th>
5 6 <th><%= l(:label_revision) %></th>
6 7 <th><%= l(:label_date) %></th>
7 8 <th><%= l(:field_author) %></th>
8 9 <th><%= l(:field_comments) %></th>
9 </tr></thead>
10 <tbody>
11 <% total_size = 0
12 @entries.each do |entry| %>
13 <tr class="<%= cycle 'odd', 'even' %>">
14 <td><%= link_to h(entry.name), { :action => (entry.is_dir? ? 'browse' : 'changes'), :id => @project, :path => entry.path, :rev => @rev }, :class => ("icon " + (entry.is_dir? ? 'icon-folder' : 'icon-file')) %></td>
15 <td align="right"><%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %></td>
16 <td align="right"><%= link_to(entry.lastrev.name, :action => 'revision', :id => @project, :rev => entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %></td>
17 <td align="center"><%= format_time(entry.lastrev.time) if entry.lastrev %></td>
18 <td align="center"><em><%=h(entry.lastrev.author) if entry.lastrev %></em></td>
19 <% changeset = @project.repository.changesets.find_by_revision(entry.lastrev.identifier) if entry.lastrev %>
20 <td><%=h truncate(changeset.comments, 100) unless changeset.nil? %></td>
21 10 </tr>
22 <% total_size += entry.size if entry.size
23 end %>
11 </thead>
12 <tbody>
13 <%= render :partial => 'dir_list_content' %>
24 14 </tbody>
25 15 </table>
26 <p class="textright"><em><%= l(:label_total) %>: <%= number_to_human_size(total_size) %></em></p> No newline at end of file
1 NO CONTENT: modified file, binary diff hidden
@@ -1,2515 +1,2515
1 1 /* Prototype JavaScript framework, version 1.5.0
2 2 * (c) 2005-2007 Sam Stephenson
3 3 *
4 4 * Prototype is freely distributable under the terms of an MIT-style license.
5 5 * For details, see the Prototype web site: http://prototype.conio.net/
6 6 *
7 7 /*--------------------------------------------------------------------------*/
8 8
9 9 var Prototype = {
10 10 Version: '1.5.0',
11 11 BrowserFeatures: {
12 12 XPath: !!document.evaluate
13 13 },
14 14
15 15 ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
16 16 emptyFunction: function() {},
17 17 K: function(x) { return x }
18 18 }
19 19
20 20 var Class = {
21 21 create: function() {
22 22 return function() {
23 23 this.initialize.apply(this, arguments);
24 24 }
25 25 }
26 26 }
27 27
28 28 var Abstract = new Object();
29 29
30 30 Object.extend = function(destination, source) {
31 31 for (var property in source) {
32 32 destination[property] = source[property];
33 33 }
34 34 return destination;
35 35 }
36 36
37 37 Object.extend(Object, {
38 38 inspect: function(object) {
39 39 try {
40 40 if (object === undefined) return 'undefined';
41 41 if (object === null) return 'null';
42 42 return object.inspect ? object.inspect() : object.toString();
43 43 } catch (e) {
44 44 if (e instanceof RangeError) return '...';
45 45 throw e;
46 46 }
47 47 },
48 48
49 49 keys: function(object) {
50 50 var keys = [];
51 51 for (var property in object)
52 52 keys.push(property);
53 53 return keys;
54 54 },
55 55
56 56 values: function(object) {
57 57 var values = [];
58 58 for (var property in object)
59 59 values.push(object[property]);
60 60 return values;
61 61 },
62 62
63 63 clone: function(object) {
64 64 return Object.extend({}, object);
65 65 }
66 66 });
67 67
68 68 Function.prototype.bind = function() {
69 69 var __method = this, args = $A(arguments), object = args.shift();
70 70 return function() {
71 71 return __method.apply(object, args.concat($A(arguments)));
72 72 }
73 73 }
74 74
75 75 Function.prototype.bindAsEventListener = function(object) {
76 76 var __method = this, args = $A(arguments), object = args.shift();
77 77 return function(event) {
78 78 return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
79 79 }
80 80 }
81 81
82 82 Object.extend(Number.prototype, {
83 83 toColorPart: function() {
84 84 var digits = this.toString(16);
85 85 if (this < 16) return '0' + digits;
86 86 return digits;
87 87 },
88 88
89 89 succ: function() {
90 90 return this + 1;
91 91 },
92 92
93 93 times: function(iterator) {
94 94 $R(0, this, true).each(iterator);
95 95 return this;
96 96 }
97 97 });
98 98
99 99 var Try = {
100 100 these: function() {
101 101 var returnValue;
102 102
103 103 for (var i = 0, length = arguments.length; i < length; i++) {
104 104 var lambda = arguments[i];
105 105 try {
106 106 returnValue = lambda();
107 107 break;
108 108 } catch (e) {}
109 109 }
110 110
111 111 return returnValue;
112 112 }
113 113 }
114 114
115 115 /*--------------------------------------------------------------------------*/
116 116
117 117 var PeriodicalExecuter = Class.create();
118 118 PeriodicalExecuter.prototype = {
119 119 initialize: function(callback, frequency) {
120 120 this.callback = callback;
121 121 this.frequency = frequency;
122 122 this.currentlyExecuting = false;
123 123
124 124 this.registerCallback();
125 125 },
126 126
127 127 registerCallback: function() {
128 128 this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
129 129 },
130 130
131 131 stop: function() {
132 132 if (!this.timer) return;
133 133 clearInterval(this.timer);
134 134 this.timer = null;
135 135 },
136 136
137 137 onTimerEvent: function() {
138 138 if (!this.currentlyExecuting) {
139 139 try {
140 140 this.currentlyExecuting = true;
141 141 this.callback(this);
142 142 } finally {
143 143 this.currentlyExecuting = false;
144 144 }
145 145 }
146 146 }
147 147 }
148 148 String.interpret = function(value){
149 149 return value == null ? '' : String(value);
150 150 }
151 151
152 152 Object.extend(String.prototype, {
153 153 gsub: function(pattern, replacement) {
154 154 var result = '', source = this, match;
155 155 replacement = arguments.callee.prepareReplacement(replacement);
156 156
157 157 while (source.length > 0) {
158 158 if (match = source.match(pattern)) {
159 159 result += source.slice(0, match.index);
160 160 result += String.interpret(replacement(match));
161 161 source = source.slice(match.index + match[0].length);
162 162 } else {
163 163 result += source, source = '';
164 164 }
165 165 }
166 166 return result;
167 167 },
168 168
169 169 sub: function(pattern, replacement, count) {
170 170 replacement = this.gsub.prepareReplacement(replacement);
171 171 count = count === undefined ? 1 : count;
172 172
173 173 return this.gsub(pattern, function(match) {
174 174 if (--count < 0) return match[0];
175 175 return replacement(match);
176 176 });
177 177 },
178 178
179 179 scan: function(pattern, iterator) {
180 180 this.gsub(pattern, iterator);
181 181 return this;
182 182 },
183 183
184 184 truncate: function(length, truncation) {
185 185 length = length || 30;
186 186 truncation = truncation === undefined ? '...' : truncation;
187 187 return this.length > length ?
188 188 this.slice(0, length - truncation.length) + truncation : this;
189 189 },
190 190
191 191 strip: function() {
192 192 return this.replace(/^\s+/, '').replace(/\s+$/, '');
193 193 },
194 194
195 195 stripTags: function() {
196 196 return this.replace(/<\/?[^>]+>/gi, '');
197 197 },
198 198
199 199 stripScripts: function() {
200 200 return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
201 201 },
202 202
203 203 extractScripts: function() {
204 204 var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
205 205 var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
206 206 return (this.match(matchAll) || []).map(function(scriptTag) {
207 207 return (scriptTag.match(matchOne) || ['', ''])[1];
208 208 });
209 209 },
210 210
211 211 evalScripts: function() {
212 212 return this.extractScripts().map(function(script) { return eval(script) });
213 213 },
214 214
215 215 escapeHTML: function() {
216 216 var div = document.createElement('div');
217 217 var text = document.createTextNode(this);
218 218 div.appendChild(text);
219 219 return div.innerHTML;
220 220 },
221 221
222 222 unescapeHTML: function() {
223 223 var div = document.createElement('div');
224 224 div.innerHTML = this.stripTags();
225 225 return div.childNodes[0] ? (div.childNodes.length > 1 ?
226 226 $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) :
227 227 div.childNodes[0].nodeValue) : '';
228 228 },
229 229
230 230 toQueryParams: function(separator) {
231 231 var match = this.strip().match(/([^?#]*)(#.*)?$/);
232 232 if (!match) return {};
233 233
234 234 return match[1].split(separator || '&').inject({}, function(hash, pair) {
235 235 if ((pair = pair.split('='))[0]) {
236 236 var name = decodeURIComponent(pair[0]);
237 237 var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
238 238
239 239 if (hash[name] !== undefined) {
240 240 if (hash[name].constructor != Array)
241 241 hash[name] = [hash[name]];
242 242 if (value) hash[name].push(value);
243 243 }
244 244 else hash[name] = value;
245 245 }
246 246 return hash;
247 247 });
248 248 },
249 249
250 250 toArray: function() {
251 251 return this.split('');
252 252 },
253 253
254 254 succ: function() {
255 255 return this.slice(0, this.length - 1) +
256 256 String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
257 257 },
258 258
259 259 camelize: function() {
260 260 var parts = this.split('-'), len = parts.length;
261 261 if (len == 1) return parts[0];
262 262
263 263 var camelized = this.charAt(0) == '-'
264 264 ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
265 265 : parts[0];
266 266
267 267 for (var i = 1; i < len; i++)
268 268 camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
269 269
270 270 return camelized;
271 271 },
272 272
273 273 capitalize: function(){
274 274 return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
275 275 },
276 276
277 277 underscore: function() {
278 278 return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
279 279 },
280 280
281 281 dasherize: function() {
282 282 return this.gsub(/_/,'-');
283 283 },
284 284
285 285 inspect: function(useDoubleQuotes) {
286 286 var escapedString = this.replace(/\\/g, '\\\\');
287 287 if (useDoubleQuotes)
288 288 return '"' + escapedString.replace(/"/g, '\\"') + '"';
289 289 else
290 290 return "'" + escapedString.replace(/'/g, '\\\'') + "'";
291 291 }
292 292 });
293 293
294 294 String.prototype.gsub.prepareReplacement = function(replacement) {
295 295 if (typeof replacement == 'function') return replacement;
296 296 var template = new Template(replacement);
297 297 return function(match) { return template.evaluate(match) };
298 298 }
299 299
300 300 String.prototype.parseQuery = String.prototype.toQueryParams;
301 301
302 302 var Template = Class.create();
303 303 Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
304 304 Template.prototype = {
305 305 initialize: function(template, pattern) {
306 306 this.template = template.toString();
307 307 this.pattern = pattern || Template.Pattern;
308 308 },
309 309
310 310 evaluate: function(object) {
311 311 return this.template.gsub(this.pattern, function(match) {
312 312 var before = match[1];
313 313 if (before == '\\') return match[2];
314 314 return before + String.interpret(object[match[3]]);
315 315 });
316 316 }
317 317 }
318 318
319 319 var $break = new Object();
320 320 var $continue = new Object();
321 321
322 322 var Enumerable = {
323 323 each: function(iterator) {
324 324 var index = 0;
325 325 try {
326 326 this._each(function(value) {
327 327 try {
328 328 iterator(value, index++);
329 329 } catch (e) {
330 330 if (e != $continue) throw e;
331 331 }
332 332 });
333 333 } catch (e) {
334 334 if (e != $break) throw e;
335 335 }
336 336 return this;
337 337 },
338 338
339 339 eachSlice: function(number, iterator) {
340 340 var index = -number, slices = [], array = this.toArray();
341 341 while ((index += number) < array.length)
342 342 slices.push(array.slice(index, index+number));
343 343 return slices.map(iterator);
344 344 },
345 345
346 346 all: function(iterator) {
347 347 var result = true;
348 348 this.each(function(value, index) {
349 349 result = result && !!(iterator || Prototype.K)(value, index);
350 350 if (!result) throw $break;
351 351 });
352 352 return result;
353 353 },
354 354
355 355 any: function(iterator) {
356 356 var result = false;
357 357 this.each(function(value, index) {
358 358 if (result = !!(iterator || Prototype.K)(value, index))
359 359 throw $break;
360 360 });
361 361 return result;
362 362 },
363 363
364 364 collect: function(iterator) {
365 365 var results = [];
366 366 this.each(function(value, index) {
367 367 results.push((iterator || Prototype.K)(value, index));
368 368 });
369 369 return results;
370 370 },
371 371
372 372 detect: function(iterator) {
373 373 var result;
374 374 this.each(function(value, index) {
375 375 if (iterator(value, index)) {
376 376 result = value;
377 377 throw $break;
378 378 }
379 379 });
380 380 return result;
381 381 },
382 382
383 383 findAll: function(iterator) {
384 384 var results = [];
385 385 this.each(function(value, index) {
386 386 if (iterator(value, index))
387 387 results.push(value);
388 388 });
389 389 return results;
390 390 },
391 391
392 392 grep: function(pattern, iterator) {
393 393 var results = [];
394 394 this.each(function(value, index) {
395 395 var stringValue = value.toString();
396 396 if (stringValue.match(pattern))
397 397 results.push((iterator || Prototype.K)(value, index));
398 398 })
399 399 return results;
400 400 },
401 401
402 402 include: function(object) {
403 403 var found = false;
404 404 this.each(function(value) {
405 405 if (value == object) {
406 406 found = true;
407 407 throw $break;
408 408 }
409 409 });
410 410 return found;
411 411 },
412 412
413 413 inGroupsOf: function(number, fillWith) {
414 414 fillWith = fillWith === undefined ? null : fillWith;
415 415 return this.eachSlice(number, function(slice) {
416 416 while(slice.length < number) slice.push(fillWith);
417 417 return slice;
418 418 });
419 419 },
420 420
421 421 inject: function(memo, iterator) {
422 422 this.each(function(value, index) {
423 423 memo = iterator(memo, value, index);
424 424 });
425 425 return memo;
426 426 },
427 427
428 428 invoke: function(method) {
429 429 var args = $A(arguments).slice(1);
430 430 return this.map(function(value) {
431 431 return value[method].apply(value, args);
432 432 });
433 433 },
434 434
435 435 max: function(iterator) {
436 436 var result;
437 437 this.each(function(value, index) {
438 438 value = (iterator || Prototype.K)(value, index);
439 439 if (result == undefined || value >= result)
440 440 result = value;
441 441 });
442 442 return result;
443 443 },
444 444
445 445 min: function(iterator) {
446 446 var result;
447 447 this.each(function(value, index) {
448 448 value = (iterator || Prototype.K)(value, index);
449 449 if (result == undefined || value < result)
450 450 result = value;
451 451 });
452 452 return result;
453 453 },
454 454
455 455 partition: function(iterator) {
456 456 var trues = [], falses = [];
457 457 this.each(function(value, index) {
458 458 ((iterator || Prototype.K)(value, index) ?
459 459 trues : falses).push(value);
460 460 });
461 461 return [trues, falses];
462 462 },
463 463
464 464 pluck: function(property) {
465 465 var results = [];
466 466 this.each(function(value, index) {
467 467 results.push(value[property]);
468 468 });
469 469 return results;
470 470 },
471 471
472 472 reject: function(iterator) {
473 473 var results = [];
474 474 this.each(function(value, index) {
475 475 if (!iterator(value, index))
476 476 results.push(value);
477 477 });
478 478 return results;
479 479 },
480 480
481 481 sortBy: function(iterator) {
482 482 return this.map(function(value, index) {
483 483 return {value: value, criteria: iterator(value, index)};
484 484 }).sort(function(left, right) {
485 485 var a = left.criteria, b = right.criteria;
486 486 return a < b ? -1 : a > b ? 1 : 0;
487 487 }).pluck('value');
488 488 },
489 489
490 490 toArray: function() {
491 491 return this.map();
492 492 },
493 493
494 494 zip: function() {
495 495 var iterator = Prototype.K, args = $A(arguments);
496 496 if (typeof args.last() == 'function')
497 497 iterator = args.pop();
498 498
499 499 var collections = [this].concat(args).map($A);
500 500 return this.map(function(value, index) {
501 501 return iterator(collections.pluck(index));
502 502 });
503 503 },
504 504
505 505 size: function() {
506 506 return this.toArray().length;
507 507 },
508 508
509 509 inspect: function() {
510 510 return '#<Enumerable:' + this.toArray().inspect() + '>';
511 511 }
512 512 }
513 513
514 514 Object.extend(Enumerable, {
515 515 map: Enumerable.collect,
516 516 find: Enumerable.detect,
517 517 select: Enumerable.findAll,
518 518 member: Enumerable.include,
519 519 entries: Enumerable.toArray
520 520 });
521 521 var $A = Array.from = function(iterable) {
522 522 if (!iterable) return [];
523 523 if (iterable.toArray) {
524 524 return iterable.toArray();
525 525 } else {
526 526 var results = [];
527 527 for (var i = 0, length = iterable.length; i < length; i++)
528 528 results.push(iterable[i]);
529 529 return results;
530 530 }
531 531 }
532 532
533 533 Object.extend(Array.prototype, Enumerable);
534 534
535 535 if (!Array.prototype._reverse)
536 536 Array.prototype._reverse = Array.prototype.reverse;
537 537
538 538 Object.extend(Array.prototype, {
539 539 _each: function(iterator) {
540 540 for (var i = 0, length = this.length; i < length; i++)
541 541 iterator(this[i]);
542 542 },
543 543
544 544 clear: function() {
545 545 this.length = 0;
546 546 return this;
547 547 },
548 548
549 549 first: function() {
550 550 return this[0];
551 551 },
552 552
553 553 last: function() {
554 554 return this[this.length - 1];
555 555 },
556 556
557 557 compact: function() {
558 558 return this.select(function(value) {
559 559 return value != null;
560 560 });
561 561 },
562 562
563 563 flatten: function() {
564 564 return this.inject([], function(array, value) {
565 565 return array.concat(value && value.constructor == Array ?
566 566 value.flatten() : [value]);
567 567 });
568 568 },
569 569
570 570 without: function() {
571 571 var values = $A(arguments);
572 572 return this.select(function(value) {
573 573 return !values.include(value);
574 574 });
575 575 },
576 576
577 577 indexOf: function(object) {
578 578 for (var i = 0, length = this.length; i < length; i++)
579 579 if (this[i] == object) return i;
580 580 return -1;
581 581 },
582 582
583 583 reverse: function(inline) {
584 584 return (inline !== false ? this : this.toArray())._reverse();
585 585 },
586 586
587 587 reduce: function() {
588 588 return this.length > 1 ? this : this[0];
589 589 },
590 590
591 591 uniq: function() {
592 592 return this.inject([], function(array, value) {
593 593 return array.include(value) ? array : array.concat([value]);
594 594 });
595 595 },
596 596
597 597 clone: function() {
598 598 return [].concat(this);
599 599 },
600 600
601 601 size: function() {
602 602 return this.length;
603 603 },
604 604
605 605 inspect: function() {
606 606 return '[' + this.map(Object.inspect).join(', ') + ']';
607 607 }
608 608 });
609 609
610 610 Array.prototype.toArray = Array.prototype.clone;
611 611
612 612 function $w(string){
613 613 string = string.strip();
614 614 return string ? string.split(/\s+/) : [];
615 615 }
616 616
617 617 if(window.opera){
618 618 Array.prototype.concat = function(){
619 619 var array = [];
620 620 for(var i = 0, length = this.length; i < length; i++) array.push(this[i]);
621 621 for(var i = 0, length = arguments.length; i < length; i++) {
622 622 if(arguments[i].constructor == Array) {
623 623 for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
624 624 array.push(arguments[i][j]);
625 625 } else {
626 626 array.push(arguments[i]);
627 627 }
628 628 }
629 629 return array;
630 630 }
631 631 }
632 632 var Hash = function(obj) {
633 633 Object.extend(this, obj || {});
634 634 };
635 635
636 636 Object.extend(Hash, {
637 637 toQueryString: function(obj) {
638 638 var parts = [];
639 639
640 640 this.prototype._each.call(obj, function(pair) {
641 641 if (!pair.key) return;
642 642
643 643 if (pair.value && pair.value.constructor == Array) {
644 644 var values = pair.value.compact();
645 645 if (values.length < 2) pair.value = values.reduce();
646 646 else {
647 647 key = encodeURIComponent(pair.key);
648 648 values.each(function(value) {
649 649 value = value != undefined ? encodeURIComponent(value) : '';
650 650 parts.push(key + '=' + encodeURIComponent(value));
651 651 });
652 652 return;
653 653 }
654 654 }
655 655 if (pair.value == undefined) pair[1] = '';
656 656 parts.push(pair.map(encodeURIComponent).join('='));
657 657 });
658 658
659 659 return parts.join('&');
660 660 }
661 661 });
662 662
663 663 Object.extend(Hash.prototype, Enumerable);
664 664 Object.extend(Hash.prototype, {
665 665 _each: function(iterator) {
666 666 for (var key in this) {
667 667 var value = this[key];
668 668 if (value && value == Hash.prototype[key]) continue;
669 669
670 670 var pair = [key, value];
671 671 pair.key = key;
672 672 pair.value = value;
673 673 iterator(pair);
674 674 }
675 675 },
676 676
677 677 keys: function() {
678 678 return this.pluck('key');
679 679 },
680 680
681 681 values: function() {
682 682 return this.pluck('value');
683 683 },
684 684
685 685 merge: function(hash) {
686 686 return $H(hash).inject(this, function(mergedHash, pair) {
687 687 mergedHash[pair.key] = pair.value;
688 688 return mergedHash;
689 689 });
690 690 },
691 691
692 692 remove: function() {
693 693 var result;
694 694 for(var i = 0, length = arguments.length; i < length; i++) {
695 695 var value = this[arguments[i]];
696 696 if (value !== undefined){
697 697 if (result === undefined) result = value;
698 698 else {
699 699 if (result.constructor != Array) result = [result];
700 700 result.push(value)
701 701 }
702 702 }
703 703 delete this[arguments[i]];
704 704 }
705 705 return result;
706 706 },
707 707
708 708 toQueryString: function() {
709 709 return Hash.toQueryString(this);
710 710 },
711 711
712 712 inspect: function() {
713 713 return '#<Hash:{' + this.map(function(pair) {
714 714 return pair.map(Object.inspect).join(': ');
715 715 }).join(', ') + '}>';
716 716 }
717 717 });
718 718
719 719 function $H(object) {
720 720 if (object && object.constructor == Hash) return object;
721 721 return new Hash(object);
722 722 };
723 723 ObjectRange = Class.create();
724 724 Object.extend(ObjectRange.prototype, Enumerable);
725 725 Object.extend(ObjectRange.prototype, {
726 726 initialize: function(start, end, exclusive) {
727 727 this.start = start;
728 728 this.end = end;
729 729 this.exclusive = exclusive;
730 730 },
731 731
732 732 _each: function(iterator) {
733 733 var value = this.start;
734 734 while (this.include(value)) {
735 735 iterator(value);
736 736 value = value.succ();
737 737 }
738 738 },
739 739
740 740 include: function(value) {
741 741 if (value < this.start)
742 742 return false;
743 743 if (this.exclusive)
744 744 return value < this.end;
745 745 return value <= this.end;
746 746 }
747 747 });
748 748
749 749 var $R = function(start, end, exclusive) {
750 750 return new ObjectRange(start, end, exclusive);
751 751 }
752 752
753 753 var Ajax = {
754 754 getTransport: function() {
755 755 return Try.these(
756 756 function() {return new XMLHttpRequest()},
757 757 function() {return new ActiveXObject('Msxml2.XMLHTTP')},
758 758 function() {return new ActiveXObject('Microsoft.XMLHTTP')}
759 759 ) || false;
760 760 },
761 761
762 762 activeRequestCount: 0
763 763 }
764 764
765 765 Ajax.Responders = {
766 766 responders: [],
767 767
768 768 _each: function(iterator) {
769 769 this.responders._each(iterator);
770 770 },
771 771
772 772 register: function(responder) {
773 773 if (!this.include(responder))
774 774 this.responders.push(responder);
775 775 },
776 776
777 777 unregister: function(responder) {
778 778 this.responders = this.responders.without(responder);
779 779 },
780 780
781 781 dispatch: function(callback, request, transport, json) {
782 782 this.each(function(responder) {
783 783 if (typeof responder[callback] == 'function') {
784 784 try {
785 785 responder[callback].apply(responder, [request, transport, json]);
786 786 } catch (e) {}
787 787 }
788 788 });
789 789 }
790 790 };
791 791
792 792 Object.extend(Ajax.Responders, Enumerable);
793 793
794 794 Ajax.Responders.register({
795 795 onCreate: function() {
796 796 Ajax.activeRequestCount++;
797 797 },
798 798 onComplete: function() {
799 799 Ajax.activeRequestCount--;
800 800 }
801 801 });
802 802
803 803 Ajax.Base = function() {};
804 804 Ajax.Base.prototype = {
805 805 setOptions: function(options) {
806 806 this.options = {
807 807 method: 'post',
808 808 asynchronous: true,
809 809 contentType: 'application/x-www-form-urlencoded',
810 810 encoding: 'UTF-8',
811 811 parameters: ''
812 812 }
813 813 Object.extend(this.options, options || {});
814 814
815 815 this.options.method = this.options.method.toLowerCase();
816 816 if (typeof this.options.parameters == 'string')
817 817 this.options.parameters = this.options.parameters.toQueryParams();
818 818 }
819 819 }
820 820
821 821 Ajax.Request = Class.create();
822 822 Ajax.Request.Events =
823 823 ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
824 824
825 825 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
826 826 _complete: false,
827 827
828 828 initialize: function(url, options) {
829 829 this.transport = Ajax.getTransport();
830 830 this.setOptions(options);
831 831 this.request(url);
832 832 },
833 833
834 834 request: function(url) {
835 835 this.url = url;
836 836 this.method = this.options.method;
837 837 var params = this.options.parameters;
838 838
839 839 if (!['get', 'post'].include(this.method)) {
840 840 // simulate other verbs over post
841 841 params['_method'] = this.method;
842 842 this.method = 'post';
843 843 }
844 844
845 845 params = Hash.toQueryString(params);
846 846 if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='
847 847
848 848 // when GET, append parameters to URL
849 849 if (this.method == 'get' && params)
850 850 this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;
851 851
852 852 try {
853 853 Ajax.Responders.dispatch('onCreate', this, this.transport);
854 854
855 855 this.transport.open(this.method.toUpperCase(), this.url,
856 856 this.options.asynchronous);
857 857
858 858 if (this.options.asynchronous)
859 859 setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
860 860
861 861 this.transport.onreadystatechange = this.onStateChange.bind(this);
862 862 this.setRequestHeaders();
863 863
864 864 var body = this.method == 'post' ? (this.options.postBody || params) : null;
865 865
866 866 this.transport.send(body);
867 867
868 868 /* Force Firefox to handle ready state 4 for synchronous requests */
869 869 if (!this.options.asynchronous && this.transport.overrideMimeType)
870 870 this.onStateChange();
871 871
872 872 }
873 873 catch (e) {
874 874 this.dispatchException(e);
875 875 }
876 876 },
877 877
878 878 onStateChange: function() {
879 879 var readyState = this.transport.readyState;
880 880 if (readyState > 1 && !((readyState == 4) && this._complete))
881 881 this.respondToReadyState(this.transport.readyState);
882 882 },
883 883
884 884 setRequestHeaders: function() {
885 885 var headers = {
886 886 'X-Requested-With': 'XMLHttpRequest',
887 887 'X-Prototype-Version': Prototype.Version,
888 888 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
889 889 };
890 890
891 891 if (this.method == 'post') {
892 892 headers['Content-type'] = this.options.contentType +
893 893 (this.options.encoding ? '; charset=' + this.options.encoding : '');
894 894
895 895 /* Force "Connection: close" for older Mozilla browsers to work
896 896 * around a bug where XMLHttpRequest sends an incorrect
897 897 * Content-length header. See Mozilla Bugzilla #246651.
898 898 */
899 899 if (this.transport.overrideMimeType &&
900 900 (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
901 901 headers['Connection'] = 'close';
902 902 }
903 903
904 904 // user-defined headers
905 905 if (typeof this.options.requestHeaders == 'object') {
906 906 var extras = this.options.requestHeaders;
907 907
908 908 if (typeof extras.push == 'function')
909 909 for (var i = 0, length = extras.length; i < length; i += 2)
910 910 headers[extras[i]] = extras[i+1];
911 911 else
912 912 $H(extras).each(function(pair) { headers[pair.key] = pair.value });
913 913 }
914 914
915 915 for (var name in headers)
916 916 this.transport.setRequestHeader(name, headers[name]);
917 917 },
918 918
919 919 success: function() {
920 920 return !this.transport.status
921 921 || (this.transport.status >= 200 && this.transport.status < 300);
922 922 },
923 923
924 924 respondToReadyState: function(readyState) {
925 925 var state = Ajax.Request.Events[readyState];
926 926 var transport = this.transport, json = this.evalJSON();
927 927
928 928 if (state == 'Complete') {
929 929 try {
930 930 this._complete = true;
931 931 (this.options['on' + this.transport.status]
932 932 || this.options['on' + (this.success() ? 'Success' : 'Failure')]
933 933 || Prototype.emptyFunction)(transport, json);
934 934 } catch (e) {
935 935 this.dispatchException(e);
936 936 }
937 937
938 938 if ((this.getHeader('Content-type') || 'text/javascript').strip().
939 939 match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
940 940 this.evalResponse();
941 941 }
942 942
943 943 try {
944 944 (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
945 945 Ajax.Responders.dispatch('on' + state, this, transport, json);
946 946 } catch (e) {
947 947 this.dispatchException(e);
948 948 }
949 949
950 950 if (state == 'Complete') {
951 951 // avoid memory leak in MSIE: clean up
952 952 this.transport.onreadystatechange = Prototype.emptyFunction;
953 953 }
954 954 },
955 955
956 956 getHeader: function(name) {
957 957 try {
958 958 return this.transport.getResponseHeader(name);
959 959 } catch (e) { return null }
960 960 },
961 961
962 962 evalJSON: function() {
963 963 try {
964 964 var json = this.getHeader('X-JSON');
965 965 return json ? eval('(' + json + ')') : null;
966 966 } catch (e) { return null }
967 967 },
968 968
969 969 evalResponse: function() {
970 970 try {
971 971 return eval(this.transport.responseText);
972 972 } catch (e) {
973 973 this.dispatchException(e);
974 974 }
975 975 },
976 976
977 977 dispatchException: function(exception) {
978 978 (this.options.onException || Prototype.emptyFunction)(this, exception);
979 979 Ajax.Responders.dispatch('onException', this, exception);
980 980 }
981 981 });
982 982
983 983 Ajax.Updater = Class.create();
984 984
985 985 Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
986 986 initialize: function(container, url, options) {
987 987 this.container = {
988 988 success: (container.success || container),
989 989 failure: (container.failure || (container.success ? null : container))
990 990 }
991 991
992 992 this.transport = Ajax.getTransport();
993 993 this.setOptions(options);
994 994
995 995 var onComplete = this.options.onComplete || Prototype.emptyFunction;
996 996 this.options.onComplete = (function(transport, param) {
997 997 this.updateContent();
998 998 onComplete(transport, param);
999 999 }).bind(this);
1000 1000
1001 1001 this.request(url);
1002 1002 },
1003 1003
1004 1004 updateContent: function() {
1005 1005 var receiver = this.container[this.success() ? 'success' : 'failure'];
1006 1006 var response = this.transport.responseText;
1007 1007
1008 1008 if (!this.options.evalScripts) response = response.stripScripts();
1009 1009
1010 1010 if (receiver = $(receiver)) {
1011 1011 if (this.options.insertion)
1012 1012 new this.options.insertion(receiver, response);
1013 1013 else
1014 1014 receiver.update(response);
1015 1015 }
1016 1016
1017 1017 if (this.success()) {
1018 1018 if (this.onComplete)
1019 1019 setTimeout(this.onComplete.bind(this), 10);
1020 1020 }
1021 1021 }
1022 1022 });
1023 1023
1024 1024 Ajax.PeriodicalUpdater = Class.create();
1025 1025 Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
1026 1026 initialize: function(container, url, options) {
1027 1027 this.setOptions(options);
1028 1028 this.onComplete = this.options.onComplete;
1029 1029
1030 1030 this.frequency = (this.options.frequency || 2);
1031 1031 this.decay = (this.options.decay || 1);
1032 1032
1033 1033 this.updater = {};
1034 1034 this.container = container;
1035 1035 this.url = url;
1036 1036
1037 1037 this.start();
1038 1038 },
1039 1039
1040 1040 start: function() {
1041 1041 this.options.onComplete = this.updateComplete.bind(this);
1042 1042 this.onTimerEvent();
1043 1043 },
1044 1044
1045 1045 stop: function() {
1046 1046 this.updater.options.onComplete = undefined;
1047 1047 clearTimeout(this.timer);
1048 1048 (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1049 1049 },
1050 1050
1051 1051 updateComplete: function(request) {
1052 1052 if (this.options.decay) {
1053 1053 this.decay = (request.responseText == this.lastText ?
1054 1054 this.decay * this.options.decay : 1);
1055 1055
1056 1056 this.lastText = request.responseText;
1057 1057 }
1058 1058 this.timer = setTimeout(this.onTimerEvent.bind(this),
1059 1059 this.decay * this.frequency * 1000);
1060 1060 },
1061 1061
1062 1062 onTimerEvent: function() {
1063 1063 this.updater = new Ajax.Updater(this.container, this.url, this.options);
1064 1064 }
1065 1065 });
1066 1066 function $(element) {
1067 1067 if (arguments.length > 1) {
1068 1068 for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1069 1069 elements.push($(arguments[i]));
1070 1070 return elements;
1071 1071 }
1072 1072 if (typeof element == 'string')
1073 1073 element = document.getElementById(element);
1074 1074 return Element.extend(element);
1075 1075 }
1076 1076
1077 1077 if (Prototype.BrowserFeatures.XPath) {
1078 1078 document._getElementsByXPath = function(expression, parentElement) {
1079 1079 var results = [];
1080 1080 var query = document.evaluate(expression, $(parentElement) || document,
1081 1081 null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1082 1082 for (var i = 0, length = query.snapshotLength; i < length; i++)
1083 1083 results.push(query.snapshotItem(i));
1084 1084 return results;
1085 1085 };
1086 1086 }
1087 1087
1088 1088 document.getElementsByClassName = function(className, parentElement) {
1089 1089 if (Prototype.BrowserFeatures.XPath) {
1090 1090 var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
1091 1091 return document._getElementsByXPath(q, parentElement);
1092 1092 } else {
1093 1093 var children = ($(parentElement) || document.body).getElementsByTagName('*');
1094 1094 var elements = [], child;
1095 1095 for (var i = 0, length = children.length; i < length; i++) {
1096 1096 child = children[i];
1097 1097 if (Element.hasClassName(child, className))
1098 1098 elements.push(Element.extend(child));
1099 1099 }
1100 1100 return elements;
1101 1101 }
1102 1102 };
1103 1103
1104 1104 /*--------------------------------------------------------------------------*/
1105 1105
1106 1106 if (!window.Element)
1107 1107 var Element = new Object();
1108 1108
1109 1109 Element.extend = function(element) {
1110 1110 if (!element || _nativeExtensions || element.nodeType == 3) return element;
1111 1111
1112 1112 if (!element._extended && element.tagName && element != window) {
1113 1113 var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
1114 1114
1115 1115 if (element.tagName == 'FORM')
1116 1116 Object.extend(methods, Form.Methods);
1117 1117 if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
1118 1118 Object.extend(methods, Form.Element.Methods);
1119 1119
1120 1120 Object.extend(methods, Element.Methods.Simulated);
1121 1121
1122 1122 for (var property in methods) {
1123 1123 var value = methods[property];
1124 1124 if (typeof value == 'function' && !(property in element))
1125 1125 element[property] = cache.findOrStore(value);
1126 1126 }
1127 1127 }
1128 1128
1129 1129 element._extended = true;
1130 1130 return element;
1131 1131 };
1132 1132
1133 1133 Element.extend.cache = {
1134 1134 findOrStore: function(value) {
1135 1135 return this[value] = this[value] || function() {
1136 1136 return value.apply(null, [this].concat($A(arguments)));
1137 1137 }
1138 1138 }
1139 1139 };
1140 1140
1141 1141 Element.Methods = {
1142 1142 visible: function(element) {
1143 1143 return $(element).style.display != 'none';
1144 1144 },
1145 1145
1146 1146 toggle: function(element) {
1147 1147 element = $(element);
1148 1148 Element[Element.visible(element) ? 'hide' : 'show'](element);
1149 1149 return element;
1150 1150 },
1151 1151
1152 1152 hide: function(element) {
1153 1153 $(element).style.display = 'none';
1154 1154 return element;
1155 1155 },
1156 1156
1157 1157 show: function(element) {
1158 1158 $(element).style.display = '';
1159 1159 return element;
1160 1160 },
1161 1161
1162 1162 remove: function(element) {
1163 1163 element = $(element);
1164 1164 element.parentNode.removeChild(element);
1165 1165 return element;
1166 1166 },
1167 1167
1168 1168 update: function(element, html) {
1169 1169 html = typeof html == 'undefined' ? '' : html.toString();
1170 1170 $(element).innerHTML = html.stripScripts();
1171 1171 setTimeout(function() {html.evalScripts()}, 10);
1172 1172 return element;
1173 1173 },
1174 1174
1175 1175 replace: function(element, html) {
1176 1176 element = $(element);
1177 1177 html = typeof html == 'undefined' ? '' : html.toString();
1178 1178 if (element.outerHTML) {
1179 1179 element.outerHTML = html.stripScripts();
1180 1180 } else {
1181 1181 var range = element.ownerDocument.createRange();
1182 1182 range.selectNodeContents(element);
1183 1183 element.parentNode.replaceChild(
1184 1184 range.createContextualFragment(html.stripScripts()), element);
1185 1185 }
1186 1186 setTimeout(function() {html.evalScripts()}, 10);
1187 1187 return element;
1188 1188 },
1189 1189
1190 1190 inspect: function(element) {
1191 1191 element = $(element);
1192 1192 var result = '<' + element.tagName.toLowerCase();
1193 1193 $H({'id': 'id', 'className': 'class'}).each(function(pair) {
1194 1194 var property = pair.first(), attribute = pair.last();
1195 1195 var value = (element[property] || '').toString();
1196 1196 if (value) result += ' ' + attribute + '=' + value.inspect(true);
1197 1197 });
1198 1198 return result + '>';
1199 1199 },
1200 1200
1201 1201 recursivelyCollect: function(element, property) {
1202 1202 element = $(element);
1203 1203 var elements = [];
1204 1204 while (element = element[property])
1205 1205 if (element.nodeType == 1)
1206 1206 elements.push(Element.extend(element));
1207 1207 return elements;
1208 1208 },
1209 1209
1210 1210 ancestors: function(element) {
1211 1211 return $(element).recursivelyCollect('parentNode');
1212 1212 },
1213 1213
1214 1214 descendants: function(element) {
1215 1215 return $A($(element).getElementsByTagName('*'));
1216 1216 },
1217 1217
1218 1218 immediateDescendants: function(element) {
1219 1219 if (!(element = $(element).firstChild)) return [];
1220 1220 while (element && element.nodeType != 1) element = element.nextSibling;
1221 1221 if (element) return [element].concat($(element).nextSiblings());
1222 1222 return [];
1223 1223 },
1224 1224
1225 1225 previousSiblings: function(element) {
1226 1226 return $(element).recursivelyCollect('previousSibling');
1227 1227 },
1228 1228
1229 1229 nextSiblings: function(element) {
1230 1230 return $(element).recursivelyCollect('nextSibling');
1231 1231 },
1232 1232
1233 1233 siblings: function(element) {
1234 1234 element = $(element);
1235 1235 return element.previousSiblings().reverse().concat(element.nextSiblings());
1236 1236 },
1237 1237
1238 1238 match: function(element, selector) {
1239 1239 if (typeof selector == 'string')
1240 1240 selector = new Selector(selector);
1241 1241 return selector.match($(element));
1242 1242 },
1243 1243
1244 1244 up: function(element, expression, index) {
1245 1245 return Selector.findElement($(element).ancestors(), expression, index);
1246 1246 },
1247 1247
1248 1248 down: function(element, expression, index) {
1249 1249 return Selector.findElement($(element).descendants(), expression, index);
1250 1250 },
1251 1251
1252 1252 previous: function(element, expression, index) {
1253 1253 return Selector.findElement($(element).previousSiblings(), expression, index);
1254 1254 },
1255 1255
1256 1256 next: function(element, expression, index) {
1257 1257 return Selector.findElement($(element).nextSiblings(), expression, index);
1258 1258 },
1259 1259
1260 1260 getElementsBySelector: function() {
1261 1261 var args = $A(arguments), element = $(args.shift());
1262 1262 return Selector.findChildElements(element, args);
1263 1263 },
1264 1264
1265 1265 getElementsByClassName: function(element, className) {
1266 1266 return document.getElementsByClassName(className, element);
1267 1267 },
1268 1268
1269 1269 readAttribute: function(element, name) {
1270 1270 element = $(element);
1271 1271 if (document.all && !window.opera) {
1272 1272 var t = Element._attributeTranslations;
1273 1273 if (t.values[name]) return t.values[name](element, name);
1274 1274 if (t.names[name]) name = t.names[name];
1275 1275 var attribute = element.attributes[name];
1276 1276 if(attribute) return attribute.nodeValue;
1277 1277 }
1278 1278 return element.getAttribute(name);
1279 1279 },
1280 1280
1281 1281 getHeight: function(element) {
1282 1282 return $(element).getDimensions().height;
1283 1283 },
1284 1284
1285 1285 getWidth: function(element) {
1286 1286 return $(element).getDimensions().width;
1287 1287 },
1288 1288
1289 1289 classNames: function(element) {
1290 1290 return new Element.ClassNames(element);
1291 1291 },
1292 1292
1293 1293 hasClassName: function(element, className) {
1294 1294 if (!(element = $(element))) return;
1295 1295 var elementClassName = element.className;
1296 1296 if (elementClassName.length == 0) return false;
1297 1297 if (elementClassName == className ||
1298 1298 elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
1299 1299 return true;
1300 1300 return false;
1301 1301 },
1302 1302
1303 1303 addClassName: function(element, className) {
1304 1304 if (!(element = $(element))) return;
1305 1305 Element.classNames(element).add(className);
1306 1306 return element;
1307 1307 },
1308 1308
1309 1309 removeClassName: function(element, className) {
1310 1310 if (!(element = $(element))) return;
1311 1311 Element.classNames(element).remove(className);
1312 1312 return element;
1313 1313 },
1314 1314
1315 1315 toggleClassName: function(element, className) {
1316 1316 if (!(element = $(element))) return;
1317 1317 Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
1318 1318 return element;
1319 1319 },
1320 1320
1321 1321 observe: function() {
1322 1322 Event.observe.apply(Event, arguments);
1323 1323 return $A(arguments).first();
1324 1324 },
1325 1325
1326 1326 stopObserving: function() {
1327 1327 Event.stopObserving.apply(Event, arguments);
1328 1328 return $A(arguments).first();
1329 1329 },
1330 1330
1331 1331 // removes whitespace-only text node children
1332 1332 cleanWhitespace: function(element) {
1333 1333 element = $(element);
1334 1334 var node = element.firstChild;
1335 1335 while (node) {
1336 1336 var nextNode = node.nextSibling;
1337 1337 if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
1338 1338 element.removeChild(node);
1339 1339 node = nextNode;
1340 1340 }
1341 1341 return element;
1342 1342 },
1343 1343
1344 1344 empty: function(element) {
1345 1345 return $(element).innerHTML.match(/^\s*$/);
1346 1346 },
1347 1347
1348 1348 descendantOf: function(element, ancestor) {
1349 1349 element = $(element), ancestor = $(ancestor);
1350 1350 while (element = element.parentNode)
1351 1351 if (element == ancestor) return true;
1352 1352 return false;
1353 1353 },
1354 1354
1355 1355 scrollTo: function(element) {
1356 1356 element = $(element);
1357 1357 var pos = Position.cumulativeOffset(element);
1358 1358 window.scrollTo(pos[0], pos[1]);
1359 1359 return element;
1360 1360 },
1361 1361
1362 1362 getStyle: function(element, style) {
1363 1363 element = $(element);
1364 1364 if (['float','cssFloat'].include(style))
1365 1365 style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
1366 1366 style = style.camelize();
1367 1367 var value = element.style[style];
1368 1368 if (!value) {
1369 1369 if (document.defaultView && document.defaultView.getComputedStyle) {
1370 1370 var css = document.defaultView.getComputedStyle(element, null);
1371 1371 value = css ? css[style] : null;
1372 1372 } else if (element.currentStyle) {
1373 1373 value = element.currentStyle[style];
1374 1374 }
1375 1375 }
1376 1376
1377 1377 if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
1378 1378 value = element['offset'+style.capitalize()] + 'px';
1379 1379
1380 1380 if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
1381 1381 if (Element.getStyle(element, 'position') == 'static') value = 'auto';
1382 1382 if(style == 'opacity') {
1383 1383 if(value) return parseFloat(value);
1384 1384 if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
1385 1385 if(value[1]) return parseFloat(value[1]) / 100;
1386 1386 return 1.0;
1387 1387 }
1388 1388 return value == 'auto' ? null : value;
1389 1389 },
1390 1390
1391 1391 setStyle: function(element, style) {
1392 1392 element = $(element);
1393 1393 for (var name in style) {
1394 1394 var value = style[name];
1395 1395 if(name == 'opacity') {
1396 1396 if (value == 1) {
1397 1397 value = (/Gecko/.test(navigator.userAgent) &&
1398 1398 !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
1399 1399 if(/MSIE/.test(navigator.userAgent) && !window.opera)
1400 1400 element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
1401 1401 } else if(value == '') {
1402 1402 if(/MSIE/.test(navigator.userAgent) && !window.opera)
1403 1403 element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
1404 1404 } else {
1405 1405 if(value < 0.00001) value = 0;
1406 1406 if(/MSIE/.test(navigator.userAgent) && !window.opera)
1407 1407 element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
1408 1408 'alpha(opacity='+value*100+')';
1409 1409 }
1410 1410 } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
1411 1411 element.style[name.camelize()] = value;
1412 1412 }
1413 1413 return element;
1414 1414 },
1415 1415
1416 1416 getDimensions: function(element) {
1417 1417 element = $(element);
1418 1418 var display = $(element).getStyle('display');
1419 1419 if (display != 'none' && display != null) // Safari bug
1420 1420 return {width: element.offsetWidth, height: element.offsetHeight};
1421 1421
1422 1422 // All *Width and *Height properties give 0 on elements with display none,
1423 1423 // so enable the element temporarily
1424 1424 var els = element.style;
1425 1425 var originalVisibility = els.visibility;
1426 1426 var originalPosition = els.position;
1427 1427 var originalDisplay = els.display;
1428 1428 els.visibility = 'hidden';
1429 1429 els.position = 'absolute';
1430 1430 els.display = 'block';
1431 1431 var originalWidth = element.clientWidth;
1432 1432 var originalHeight = element.clientHeight;
1433 1433 els.display = originalDisplay;
1434 1434 els.position = originalPosition;
1435 1435 els.visibility = originalVisibility;
1436 1436 return {width: originalWidth, height: originalHeight};
1437 1437 },
1438 1438
1439 1439 makePositioned: function(element) {
1440 1440 element = $(element);
1441 1441 var pos = Element.getStyle(element, 'position');
1442 1442 if (pos == 'static' || !pos) {
1443 1443 element._madePositioned = true;
1444 1444 element.style.position = 'relative';
1445 1445 // Opera returns the offset relative to the positioning context, when an
1446 1446 // element is position relative but top and left have not been defined
1447 1447 if (window.opera) {
1448 1448 element.style.top = 0;
1449 1449 element.style.left = 0;
1450 1450 }
1451 1451 }
1452 1452 return element;
1453 1453 },
1454 1454
1455 1455 undoPositioned: function(element) {
1456 1456 element = $(element);
1457 1457 if (element._madePositioned) {
1458 1458 element._madePositioned = undefined;
1459 1459 element.style.position =
1460 1460 element.style.top =
1461 1461 element.style.left =
1462 1462 element.style.bottom =
1463 1463 element.style.right = '';
1464 1464 }
1465 1465 return element;
1466 1466 },
1467 1467
1468 1468 makeClipping: function(element) {
1469 1469 element = $(element);
1470 1470 if (element._overflow) return element;
1471 1471 element._overflow = element.style.overflow || 'auto';
1472 1472 if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
1473 1473 element.style.overflow = 'hidden';
1474 1474 return element;
1475 1475 },
1476 1476
1477 1477 undoClipping: function(element) {
1478 1478 element = $(element);
1479 1479 if (!element._overflow) return element;
1480 1480 element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
1481 1481 element._overflow = null;
1482 1482 return element;
1483 1483 }
1484 1484 };
1485 1485
1486 1486 Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
1487 1487
1488 1488 Element._attributeTranslations = {};
1489 1489
1490 1490 Element._attributeTranslations.names = {
1491 1491 colspan: "colSpan",
1492 1492 rowspan: "rowSpan",
1493 1493 valign: "vAlign",
1494 1494 datetime: "dateTime",
1495 1495 accesskey: "accessKey",
1496 1496 tabindex: "tabIndex",
1497 1497 enctype: "encType",
1498 1498 maxlength: "maxLength",
1499 1499 readonly: "readOnly",
1500 1500 longdesc: "longDesc"
1501 1501 };
1502 1502
1503 1503 Element._attributeTranslations.values = {
1504 1504 _getAttr: function(element, attribute) {
1505 1505 return element.getAttribute(attribute, 2);
1506 1506 },
1507 1507
1508 1508 _flag: function(element, attribute) {
1509 1509 return $(element).hasAttribute(attribute) ? attribute : null;
1510 1510 },
1511 1511
1512 1512 style: function(element) {
1513 1513 return element.style.cssText.toLowerCase();
1514 1514 },
1515 1515
1516 1516 title: function(element) {
1517 1517 var node = element.getAttributeNode('title');
1518 1518 return node.specified ? node.nodeValue : null;
1519 1519 }
1520 1520 };
1521 1521
1522 1522 Object.extend(Element._attributeTranslations.values, {
1523 1523 href: Element._attributeTranslations.values._getAttr,
1524 1524 src: Element._attributeTranslations.values._getAttr,
1525 1525 disabled: Element._attributeTranslations.values._flag,
1526 1526 checked: Element._attributeTranslations.values._flag,
1527 1527 readonly: Element._attributeTranslations.values._flag,
1528 1528 multiple: Element._attributeTranslations.values._flag
1529 1529 });
1530 1530
1531 1531 Element.Methods.Simulated = {
1532 1532 hasAttribute: function(element, attribute) {
1533 1533 var t = Element._attributeTranslations;
1534 1534 attribute = t.names[attribute] || attribute;
1535 1535 return $(element).getAttributeNode(attribute).specified;
1536 1536 }
1537 1537 };
1538 1538
1539 1539 // IE is missing .innerHTML support for TABLE-related elements
1540 1540 if (document.all && !window.opera){
1541 1541 Element.Methods.update = function(element, html) {
1542 1542 element = $(element);
1543 1543 html = typeof html == 'undefined' ? '' : html.toString();
1544 1544 var tagName = element.tagName.toUpperCase();
1545 1545 if (['THEAD','TBODY','TR','TD'].include(tagName)) {
1546 1546 var div = document.createElement('div');
1547 1547 switch (tagName) {
1548 1548 case 'THEAD':
1549 1549 case 'TBODY':
1550 1550 div.innerHTML = '<table><tbody>' + html.stripScripts() + '</tbody></table>';
1551 1551 depth = 2;
1552 1552 break;
1553 1553 case 'TR':
1554 1554 div.innerHTML = '<table><tbody><tr>' + html.stripScripts() + '</tr></tbody></table>';
1555 1555 depth = 3;
1556 1556 break;
1557 1557 case 'TD':
1558 1558 div.innerHTML = '<table><tbody><tr><td>' + html.stripScripts() + '</td></tr></tbody></table>';
1559 1559 depth = 4;
1560 1560 }
1561 1561 $A(element.childNodes).each(function(node){
1562 1562 element.removeChild(node)
1563 1563 });
1564 1564 depth.times(function(){ div = div.firstChild });
1565 1565
1566 1566 $A(div.childNodes).each(
1567 1567 function(node){ element.appendChild(node) });
1568 1568 } else {
1569 1569 element.innerHTML = html.stripScripts();
1570 1570 }
1571 1571 setTimeout(function() {html.evalScripts()}, 10);
1572 1572 return element;
1573 1573 }
1574 1574 };
1575 1575
1576 1576 Object.extend(Element, Element.Methods);
1577 1577
1578 1578 var _nativeExtensions = false;
1579 1579
1580 1580 if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1581 1581 ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
1582 1582 var className = 'HTML' + tag + 'Element';
1583 1583 if(window[className]) return;
1584 1584 var klass = window[className] = {};
1585 1585 klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
1586 1586 });
1587 1587
1588 1588 Element.addMethods = function(methods) {
1589 1589 Object.extend(Element.Methods, methods || {});
1590 1590
1591 1591 function copy(methods, destination, onlyIfAbsent) {
1592 1592 onlyIfAbsent = onlyIfAbsent || false;
1593 1593 var cache = Element.extend.cache;
1594 1594 for (var property in methods) {
1595 1595 var value = methods[property];
1596 1596 if (!onlyIfAbsent || !(property in destination))
1597 1597 destination[property] = cache.findOrStore(value);
1598 1598 }
1599 1599 }
1600 1600
1601 1601 if (typeof HTMLElement != 'undefined') {
1602 1602 copy(Element.Methods, HTMLElement.prototype);
1603 1603 copy(Element.Methods.Simulated, HTMLElement.prototype, true);
1604 1604 copy(Form.Methods, HTMLFormElement.prototype);
1605 1605 [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
1606 1606 copy(Form.Element.Methods, klass.prototype);
1607 1607 });
1608 1608 _nativeExtensions = true;
1609 1609 }
1610 1610 }
1611 1611
1612 1612 var Toggle = new Object();
1613 1613 Toggle.display = Element.toggle;
1614 1614
1615 1615 /*--------------------------------------------------------------------------*/
1616 1616
1617 1617 Abstract.Insertion = function(adjacency) {
1618 1618 this.adjacency = adjacency;
1619 1619 }
1620 1620
1621 1621 Abstract.Insertion.prototype = {
1622 1622 initialize: function(element, content) {
1623 1623 this.element = $(element);
1624 1624 this.content = content.stripScripts();
1625 1625
1626 1626 if (this.adjacency && this.element.insertAdjacentHTML) {
1627 1627 try {
1628 1628 this.element.insertAdjacentHTML(this.adjacency, this.content);
1629 1629 } catch (e) {
1630 1630 var tagName = this.element.tagName.toUpperCase();
1631 1631 if (['TBODY', 'TR'].include(tagName)) {
1632 this.insertContent(this.contentFromAnonymousTable());
1632 this.insertContent(this.contentFromAnonymousTable()._reverse());
1633 1633 } else {
1634 1634 throw e;
1635 1635 }
1636 1636 }
1637 1637 } else {
1638 1638 this.range = this.element.ownerDocument.createRange();
1639 1639 if (this.initializeRange) this.initializeRange();
1640 1640 this.insertContent([this.range.createContextualFragment(this.content)]);
1641 1641 }
1642 1642
1643 1643 setTimeout(function() {content.evalScripts()}, 10);
1644 1644 },
1645 1645
1646 1646 contentFromAnonymousTable: function() {
1647 1647 var div = document.createElement('div');
1648 1648 div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
1649 1649 return $A(div.childNodes[0].childNodes[0].childNodes);
1650 1650 }
1651 1651 }
1652 1652
1653 1653 var Insertion = new Object();
1654 1654
1655 1655 Insertion.Before = Class.create();
1656 1656 Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
1657 1657 initializeRange: function() {
1658 1658 this.range.setStartBefore(this.element);
1659 1659 },
1660 1660
1661 1661 insertContent: function(fragments) {
1662 1662 fragments.each((function(fragment) {
1663 1663 this.element.parentNode.insertBefore(fragment, this.element);
1664 1664 }).bind(this));
1665 1665 }
1666 1666 });
1667 1667
1668 1668 Insertion.Top = Class.create();
1669 1669 Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
1670 1670 initializeRange: function() {
1671 1671 this.range.selectNodeContents(this.element);
1672 1672 this.range.collapse(true);
1673 1673 },
1674 1674
1675 1675 insertContent: function(fragments) {
1676 1676 fragments.reverse(false).each((function(fragment) {
1677 1677 this.element.insertBefore(fragment, this.element.firstChild);
1678 1678 }).bind(this));
1679 1679 }
1680 1680 });
1681 1681
1682 1682 Insertion.Bottom = Class.create();
1683 1683 Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
1684 1684 initializeRange: function() {
1685 1685 this.range.selectNodeContents(this.element);
1686 1686 this.range.collapse(this.element);
1687 1687 },
1688 1688
1689 1689 insertContent: function(fragments) {
1690 1690 fragments.each((function(fragment) {
1691 1691 this.element.appendChild(fragment);
1692 1692 }).bind(this));
1693 1693 }
1694 1694 });
1695 1695
1696 1696 Insertion.After = Class.create();
1697 1697 Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
1698 1698 initializeRange: function() {
1699 1699 this.range.setStartAfter(this.element);
1700 1700 },
1701 1701
1702 1702 insertContent: function(fragments) {
1703 1703 fragments.each((function(fragment) {
1704 1704 this.element.parentNode.insertBefore(fragment,
1705 1705 this.element.nextSibling);
1706 1706 }).bind(this));
1707 1707 }
1708 1708 });
1709 1709
1710 1710 /*--------------------------------------------------------------------------*/
1711 1711
1712 1712 Element.ClassNames = Class.create();
1713 1713 Element.ClassNames.prototype = {
1714 1714 initialize: function(element) {
1715 1715 this.element = $(element);
1716 1716 },
1717 1717
1718 1718 _each: function(iterator) {
1719 1719 this.element.className.split(/\s+/).select(function(name) {
1720 1720 return name.length > 0;
1721 1721 })._each(iterator);
1722 1722 },
1723 1723
1724 1724 set: function(className) {
1725 1725 this.element.className = className;
1726 1726 },
1727 1727
1728 1728 add: function(classNameToAdd) {
1729 1729 if (this.include(classNameToAdd)) return;
1730 1730 this.set($A(this).concat(classNameToAdd).join(' '));
1731 1731 },
1732 1732
1733 1733 remove: function(classNameToRemove) {
1734 1734 if (!this.include(classNameToRemove)) return;
1735 1735 this.set($A(this).without(classNameToRemove).join(' '));
1736 1736 },
1737 1737
1738 1738 toString: function() {
1739 1739 return $A(this).join(' ');
1740 1740 }
1741 1741 };
1742 1742
1743 1743 Object.extend(Element.ClassNames.prototype, Enumerable);
1744 1744 var Selector = Class.create();
1745 1745 Selector.prototype = {
1746 1746 initialize: function(expression) {
1747 1747 this.params = {classNames: []};
1748 1748 this.expression = expression.toString().strip();
1749 1749 this.parseExpression();
1750 1750 this.compileMatcher();
1751 1751 },
1752 1752
1753 1753 parseExpression: function() {
1754 1754 function abort(message) { throw 'Parse error in selector: ' + message; }
1755 1755
1756 1756 if (this.expression == '') abort('empty expression');
1757 1757
1758 1758 var params = this.params, expr = this.expression, match, modifier, clause, rest;
1759 1759 while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
1760 1760 params.attributes = params.attributes || [];
1761 1761 params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
1762 1762 expr = match[1];
1763 1763 }
1764 1764
1765 1765 if (expr == '*') return this.params.wildcard = true;
1766 1766
1767 1767 while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
1768 1768 modifier = match[1], clause = match[2], rest = match[3];
1769 1769 switch (modifier) {
1770 1770 case '#': params.id = clause; break;
1771 1771 case '.': params.classNames.push(clause); break;
1772 1772 case '':
1773 1773 case undefined: params.tagName = clause.toUpperCase(); break;
1774 1774 default: abort(expr.inspect());
1775 1775 }
1776 1776 expr = rest;
1777 1777 }
1778 1778
1779 1779 if (expr.length > 0) abort(expr.inspect());
1780 1780 },
1781 1781
1782 1782 buildMatchExpression: function() {
1783 1783 var params = this.params, conditions = [], clause;
1784 1784
1785 1785 if (params.wildcard)
1786 1786 conditions.push('true');
1787 1787 if (clause = params.id)
1788 1788 conditions.push('element.readAttribute("id") == ' + clause.inspect());
1789 1789 if (clause = params.tagName)
1790 1790 conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
1791 1791 if ((clause = params.classNames).length > 0)
1792 1792 for (var i = 0, length = clause.length; i < length; i++)
1793 1793 conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
1794 1794 if (clause = params.attributes) {
1795 1795 clause.each(function(attribute) {
1796 1796 var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
1797 1797 var splitValueBy = function(delimiter) {
1798 1798 return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
1799 1799 }
1800 1800
1801 1801 switch (attribute.operator) {
1802 1802 case '=': conditions.push(value + ' == ' + attribute.value.inspect()); break;
1803 1803 case '~=': conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
1804 1804 case '|=': conditions.push(
1805 1805 splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
1806 1806 ); break;
1807 1807 case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break;
1808 1808 case '':
1809 1809 case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
1810 1810 default: throw 'Unknown operator ' + attribute.operator + ' in selector';
1811 1811 }
1812 1812 });
1813 1813 }
1814 1814
1815 1815 return conditions.join(' && ');
1816 1816 },
1817 1817
1818 1818 compileMatcher: function() {
1819 1819 this.match = new Function('element', 'if (!element.tagName) return false; \
1820 1820 element = $(element); \
1821 1821 return ' + this.buildMatchExpression());
1822 1822 },
1823 1823
1824 1824 findElements: function(scope) {
1825 1825 var element;
1826 1826
1827 1827 if (element = $(this.params.id))
1828 1828 if (this.match(element))
1829 1829 if (!scope || Element.childOf(element, scope))
1830 1830 return [element];
1831 1831
1832 1832 scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
1833 1833
1834 1834 var results = [];
1835 1835 for (var i = 0, length = scope.length; i < length; i++)
1836 1836 if (this.match(element = scope[i]))
1837 1837 results.push(Element.extend(element));
1838 1838
1839 1839 return results;
1840 1840 },
1841 1841
1842 1842 toString: function() {
1843 1843 return this.expression;
1844 1844 }
1845 1845 }
1846 1846
1847 1847 Object.extend(Selector, {
1848 1848 matchElements: function(elements, expression) {
1849 1849 var selector = new Selector(expression);
1850 1850 return elements.select(selector.match.bind(selector)).map(Element.extend);
1851 1851 },
1852 1852
1853 1853 findElement: function(elements, expression, index) {
1854 1854 if (typeof expression == 'number') index = expression, expression = false;
1855 1855 return Selector.matchElements(elements, expression || '*')[index || 0];
1856 1856 },
1857 1857
1858 1858 findChildElements: function(element, expressions) {
1859 1859 return expressions.map(function(expression) {
1860 1860 return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
1861 1861 var selector = new Selector(expr);
1862 1862 return results.inject([], function(elements, result) {
1863 1863 return elements.concat(selector.findElements(result || element));
1864 1864 });
1865 1865 });
1866 1866 }).flatten();
1867 1867 }
1868 1868 });
1869 1869
1870 1870 function $$() {
1871 1871 return Selector.findChildElements(document, $A(arguments));
1872 1872 }
1873 1873 var Form = {
1874 1874 reset: function(form) {
1875 1875 $(form).reset();
1876 1876 return form;
1877 1877 },
1878 1878
1879 1879 serializeElements: function(elements, getHash) {
1880 1880 var data = elements.inject({}, function(result, element) {
1881 1881 if (!element.disabled && element.name) {
1882 1882 var key = element.name, value = $(element).getValue();
1883 1883 if (value != undefined) {
1884 1884 if (result[key]) {
1885 1885 if (result[key].constructor != Array) result[key] = [result[key]];
1886 1886 result[key].push(value);
1887 1887 }
1888 1888 else result[key] = value;
1889 1889 }
1890 1890 }
1891 1891 return result;
1892 1892 });
1893 1893
1894 1894 return getHash ? data : Hash.toQueryString(data);
1895 1895 }
1896 1896 };
1897 1897
1898 1898 Form.Methods = {
1899 1899 serialize: function(form, getHash) {
1900 1900 return Form.serializeElements(Form.getElements(form), getHash);
1901 1901 },
1902 1902
1903 1903 getElements: function(form) {
1904 1904 return $A($(form).getElementsByTagName('*')).inject([],
1905 1905 function(elements, child) {
1906 1906 if (Form.Element.Serializers[child.tagName.toLowerCase()])
1907 1907 elements.push(Element.extend(child));
1908 1908 return elements;
1909 1909 }
1910 1910 );
1911 1911 },
1912 1912
1913 1913 getInputs: function(form, typeName, name) {
1914 1914 form = $(form);
1915 1915 var inputs = form.getElementsByTagName('input');
1916 1916
1917 1917 if (!typeName && !name) return $A(inputs).map(Element.extend);
1918 1918
1919 1919 for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
1920 1920 var input = inputs[i];
1921 1921 if ((typeName && input.type != typeName) || (name && input.name != name))
1922 1922 continue;
1923 1923 matchingInputs.push(Element.extend(input));
1924 1924 }
1925 1925
1926 1926 return matchingInputs;
1927 1927 },
1928 1928
1929 1929 disable: function(form) {
1930 1930 form = $(form);
1931 1931 form.getElements().each(function(element) {
1932 1932 element.blur();
1933 1933 element.disabled = 'true';
1934 1934 });
1935 1935 return form;
1936 1936 },
1937 1937
1938 1938 enable: function(form) {
1939 1939 form = $(form);
1940 1940 form.getElements().each(function(element) {
1941 1941 element.disabled = '';
1942 1942 });
1943 1943 return form;
1944 1944 },
1945 1945
1946 1946 findFirstElement: function(form) {
1947 1947 return $(form).getElements().find(function(element) {
1948 1948 return element.type != 'hidden' && !element.disabled &&
1949 1949 ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
1950 1950 });
1951 1951 },
1952 1952
1953 1953 focusFirstElement: function(form) {
1954 1954 form = $(form);
1955 1955 form.findFirstElement().activate();
1956 1956 return form;
1957 1957 }
1958 1958 }
1959 1959
1960 1960 Object.extend(Form, Form.Methods);
1961 1961
1962 1962 /*--------------------------------------------------------------------------*/
1963 1963
1964 1964 Form.Element = {
1965 1965 focus: function(element) {
1966 1966 $(element).focus();
1967 1967 return element;
1968 1968 },
1969 1969
1970 1970 select: function(element) {
1971 1971 $(element).select();
1972 1972 return element;
1973 1973 }
1974 1974 }
1975 1975
1976 1976 Form.Element.Methods = {
1977 1977 serialize: function(element) {
1978 1978 element = $(element);
1979 1979 if (!element.disabled && element.name) {
1980 1980 var value = element.getValue();
1981 1981 if (value != undefined) {
1982 1982 var pair = {};
1983 1983 pair[element.name] = value;
1984 1984 return Hash.toQueryString(pair);
1985 1985 }
1986 1986 }
1987 1987 return '';
1988 1988 },
1989 1989
1990 1990 getValue: function(element) {
1991 1991 element = $(element);
1992 1992 var method = element.tagName.toLowerCase();
1993 1993 return Form.Element.Serializers[method](element);
1994 1994 },
1995 1995
1996 1996 clear: function(element) {
1997 1997 $(element).value = '';
1998 1998 return element;
1999 1999 },
2000 2000
2001 2001 present: function(element) {
2002 2002 return $(element).value != '';
2003 2003 },
2004 2004
2005 2005 activate: function(element) {
2006 2006 element = $(element);
2007 2007 element.focus();
2008 2008 if (element.select && ( element.tagName.toLowerCase() != 'input' ||
2009 2009 !['button', 'reset', 'submit'].include(element.type) ) )
2010 2010 element.select();
2011 2011 return element;
2012 2012 },
2013 2013
2014 2014 disable: function(element) {
2015 2015 element = $(element);
2016 2016 element.disabled = true;
2017 2017 return element;
2018 2018 },
2019 2019
2020 2020 enable: function(element) {
2021 2021 element = $(element);
2022 2022 element.blur();
2023 2023 element.disabled = false;
2024 2024 return element;
2025 2025 }
2026 2026 }
2027 2027
2028 2028 Object.extend(Form.Element, Form.Element.Methods);
2029 2029 var Field = Form.Element;
2030 2030 var $F = Form.Element.getValue;
2031 2031
2032 2032 /*--------------------------------------------------------------------------*/
2033 2033
2034 2034 Form.Element.Serializers = {
2035 2035 input: function(element) {
2036 2036 switch (element.type.toLowerCase()) {
2037 2037 case 'checkbox':
2038 2038 case 'radio':
2039 2039 return Form.Element.Serializers.inputSelector(element);
2040 2040 default:
2041 2041 return Form.Element.Serializers.textarea(element);
2042 2042 }
2043 2043 },
2044 2044
2045 2045 inputSelector: function(element) {
2046 2046 return element.checked ? element.value : null;
2047 2047 },
2048 2048
2049 2049 textarea: function(element) {
2050 2050 return element.value;
2051 2051 },
2052 2052
2053 2053 select: function(element) {
2054 2054 return this[element.type == 'select-one' ?
2055 2055 'selectOne' : 'selectMany'](element);
2056 2056 },
2057 2057
2058 2058 selectOne: function(element) {
2059 2059 var index = element.selectedIndex;
2060 2060 return index >= 0 ? this.optionValue(element.options[index]) : null;
2061 2061 },
2062 2062
2063 2063 selectMany: function(element) {
2064 2064 var values, length = element.length;
2065 2065 if (!length) return null;
2066 2066
2067 2067 for (var i = 0, values = []; i < length; i++) {
2068 2068 var opt = element.options[i];
2069 2069 if (opt.selected) values.push(this.optionValue(opt));
2070 2070 }
2071 2071 return values;
2072 2072 },
2073 2073
2074 2074 optionValue: function(opt) {
2075 2075 // extend element because hasAttribute may not be native
2076 2076 return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
2077 2077 }
2078 2078 }
2079 2079
2080 2080 /*--------------------------------------------------------------------------*/
2081 2081
2082 2082 Abstract.TimedObserver = function() {}
2083 2083 Abstract.TimedObserver.prototype = {
2084 2084 initialize: function(element, frequency, callback) {
2085 2085 this.frequency = frequency;
2086 2086 this.element = $(element);
2087 2087 this.callback = callback;
2088 2088
2089 2089 this.lastValue = this.getValue();
2090 2090 this.registerCallback();
2091 2091 },
2092 2092
2093 2093 registerCallback: function() {
2094 2094 setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
2095 2095 },
2096 2096
2097 2097 onTimerEvent: function() {
2098 2098 var value = this.getValue();
2099 2099 var changed = ('string' == typeof this.lastValue && 'string' == typeof value
2100 2100 ? this.lastValue != value : String(this.lastValue) != String(value));
2101 2101 if (changed) {
2102 2102 this.callback(this.element, value);
2103 2103 this.lastValue = value;
2104 2104 }
2105 2105 }
2106 2106 }
2107 2107
2108 2108 Form.Element.Observer = Class.create();
2109 2109 Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2110 2110 getValue: function() {
2111 2111 return Form.Element.getValue(this.element);
2112 2112 }
2113 2113 });
2114 2114
2115 2115 Form.Observer = Class.create();
2116 2116 Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2117 2117 getValue: function() {
2118 2118 return Form.serialize(this.element);
2119 2119 }
2120 2120 });
2121 2121
2122 2122 /*--------------------------------------------------------------------------*/
2123 2123
2124 2124 Abstract.EventObserver = function() {}
2125 2125 Abstract.EventObserver.prototype = {
2126 2126 initialize: function(element, callback) {
2127 2127 this.element = $(element);
2128 2128 this.callback = callback;
2129 2129
2130 2130 this.lastValue = this.getValue();
2131 2131 if (this.element.tagName.toLowerCase() == 'form')
2132 2132 this.registerFormCallbacks();
2133 2133 else
2134 2134 this.registerCallback(this.element);
2135 2135 },
2136 2136
2137 2137 onElementEvent: function() {
2138 2138 var value = this.getValue();
2139 2139 if (this.lastValue != value) {
2140 2140 this.callback(this.element, value);
2141 2141 this.lastValue = value;
2142 2142 }
2143 2143 },
2144 2144
2145 2145 registerFormCallbacks: function() {
2146 2146 Form.getElements(this.element).each(this.registerCallback.bind(this));
2147 2147 },
2148 2148
2149 2149 registerCallback: function(element) {
2150 2150 if (element.type) {
2151 2151 switch (element.type.toLowerCase()) {
2152 2152 case 'checkbox':
2153 2153 case 'radio':
2154 2154 Event.observe(element, 'click', this.onElementEvent.bind(this));
2155 2155 break;
2156 2156 default:
2157 2157 Event.observe(element, 'change', this.onElementEvent.bind(this));
2158 2158 break;
2159 2159 }
2160 2160 }
2161 2161 }
2162 2162 }
2163 2163
2164 2164 Form.Element.EventObserver = Class.create();
2165 2165 Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2166 2166 getValue: function() {
2167 2167 return Form.Element.getValue(this.element);
2168 2168 }
2169 2169 });
2170 2170
2171 2171 Form.EventObserver = Class.create();
2172 2172 Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2173 2173 getValue: function() {
2174 2174 return Form.serialize(this.element);
2175 2175 }
2176 2176 });
2177 2177 if (!window.Event) {
2178 2178 var Event = new Object();
2179 2179 }
2180 2180
2181 2181 Object.extend(Event, {
2182 2182 KEY_BACKSPACE: 8,
2183 2183 KEY_TAB: 9,
2184 2184 KEY_RETURN: 13,
2185 2185 KEY_ESC: 27,
2186 2186 KEY_LEFT: 37,
2187 2187 KEY_UP: 38,
2188 2188 KEY_RIGHT: 39,
2189 2189 KEY_DOWN: 40,
2190 2190 KEY_DELETE: 46,
2191 2191 KEY_HOME: 36,
2192 2192 KEY_END: 35,
2193 2193 KEY_PAGEUP: 33,
2194 2194 KEY_PAGEDOWN: 34,
2195 2195
2196 2196 element: function(event) {
2197 2197 return event.target || event.srcElement;
2198 2198 },
2199 2199
2200 2200 isLeftClick: function(event) {
2201 2201 return (((event.which) && (event.which == 1)) ||
2202 2202 ((event.button) && (event.button == 1)));
2203 2203 },
2204 2204
2205 2205 pointerX: function(event) {
2206 2206 return event.pageX || (event.clientX +
2207 2207 (document.documentElement.scrollLeft || document.body.scrollLeft));
2208 2208 },
2209 2209
2210 2210 pointerY: function(event) {
2211 2211 return event.pageY || (event.clientY +
2212 2212 (document.documentElement.scrollTop || document.body.scrollTop));
2213 2213 },
2214 2214
2215 2215 stop: function(event) {
2216 2216 if (event.preventDefault) {
2217 2217 event.preventDefault();
2218 2218 event.stopPropagation();
2219 2219 } else {
2220 2220 event.returnValue = false;
2221 2221 event.cancelBubble = true;
2222 2222 }
2223 2223 },
2224 2224
2225 2225 // find the first node with the given tagName, starting from the
2226 2226 // node the event was triggered on; traverses the DOM upwards
2227 2227 findElement: function(event, tagName) {
2228 2228 var element = Event.element(event);
2229 2229 while (element.parentNode && (!element.tagName ||
2230 2230 (element.tagName.toUpperCase() != tagName.toUpperCase())))
2231 2231 element = element.parentNode;
2232 2232 return element;
2233 2233 },
2234 2234
2235 2235 observers: false,
2236 2236
2237 2237 _observeAndCache: function(element, name, observer, useCapture) {
2238 2238 if (!this.observers) this.observers = [];
2239 2239 if (element.addEventListener) {
2240 2240 this.observers.push([element, name, observer, useCapture]);
2241 2241 element.addEventListener(name, observer, useCapture);
2242 2242 } else if (element.attachEvent) {
2243 2243 this.observers.push([element, name, observer, useCapture]);
2244 2244 element.attachEvent('on' + name, observer);
2245 2245 }
2246 2246 },
2247 2247
2248 2248 unloadCache: function() {
2249 2249 if (!Event.observers) return;
2250 2250 for (var i = 0, length = Event.observers.length; i < length; i++) {
2251 2251 Event.stopObserving.apply(this, Event.observers[i]);
2252 2252 Event.observers[i][0] = null;
2253 2253 }
2254 2254 Event.observers = false;
2255 2255 },
2256 2256
2257 2257 observe: function(element, name, observer, useCapture) {
2258 2258 element = $(element);
2259 2259 useCapture = useCapture || false;
2260 2260
2261 2261 if (name == 'keypress' &&
2262 2262 (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
2263 2263 || element.attachEvent))
2264 2264 name = 'keydown';
2265 2265
2266 2266 Event._observeAndCache(element, name, observer, useCapture);
2267 2267 },
2268 2268
2269 2269 stopObserving: function(element, name, observer, useCapture) {
2270 2270 element = $(element);
2271 2271 useCapture = useCapture || false;
2272 2272
2273 2273 if (name == 'keypress' &&
2274 2274 (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
2275 2275 || element.detachEvent))
2276 2276 name = 'keydown';
2277 2277
2278 2278 if (element.removeEventListener) {
2279 2279 element.removeEventListener(name, observer, useCapture);
2280 2280 } else if (element.detachEvent) {
2281 2281 try {
2282 2282 element.detachEvent('on' + name, observer);
2283 2283 } catch (e) {}
2284 2284 }
2285 2285 }
2286 2286 });
2287 2287
2288 2288 /* prevent memory leaks in IE */
2289 2289 if (navigator.appVersion.match(/\bMSIE\b/))
2290 2290 Event.observe(window, 'unload', Event.unloadCache, false);
2291 2291 var Position = {
2292 2292 // set to true if needed, warning: firefox performance problems
2293 2293 // NOT neeeded for page scrolling, only if draggable contained in
2294 2294 // scrollable elements
2295 2295 includeScrollOffsets: false,
2296 2296
2297 2297 // must be called before calling withinIncludingScrolloffset, every time the
2298 2298 // page is scrolled
2299 2299 prepare: function() {
2300 2300 this.deltaX = window.pageXOffset
2301 2301 || document.documentElement.scrollLeft
2302 2302 || document.body.scrollLeft
2303 2303 || 0;
2304 2304 this.deltaY = window.pageYOffset
2305 2305 || document.documentElement.scrollTop
2306 2306 || document.body.scrollTop
2307 2307 || 0;
2308 2308 },
2309 2309
2310 2310 realOffset: function(element) {
2311 2311 var valueT = 0, valueL = 0;
2312 2312 do {
2313 2313 valueT += element.scrollTop || 0;
2314 2314 valueL += element.scrollLeft || 0;
2315 2315 element = element.parentNode;
2316 2316 } while (element);
2317 2317 return [valueL, valueT];
2318 2318 },
2319 2319
2320 2320 cumulativeOffset: function(element) {
2321 2321 var valueT = 0, valueL = 0;
2322 2322 do {
2323 2323 valueT += element.offsetTop || 0;
2324 2324 valueL += element.offsetLeft || 0;
2325 2325 element = element.offsetParent;
2326 2326 } while (element);
2327 2327 return [valueL, valueT];
2328 2328 },
2329 2329
2330 2330 positionedOffset: function(element) {
2331 2331 var valueT = 0, valueL = 0;
2332 2332 do {
2333 2333 valueT += element.offsetTop || 0;
2334 2334 valueL += element.offsetLeft || 0;
2335 2335 element = element.offsetParent;
2336 2336 if (element) {
2337 2337 if(element.tagName=='BODY') break;
2338 2338 var p = Element.getStyle(element, 'position');
2339 2339 if (p == 'relative' || p == 'absolute') break;
2340 2340 }
2341 2341 } while (element);
2342 2342 return [valueL, valueT];
2343 2343 },
2344 2344
2345 2345 offsetParent: function(element) {
2346 2346 if (element.offsetParent) return element.offsetParent;
2347 2347 if (element == document.body) return element;
2348 2348
2349 2349 while ((element = element.parentNode) && element != document.body)
2350 2350 if (Element.getStyle(element, 'position') != 'static')
2351 2351 return element;
2352 2352
2353 2353 return document.body;
2354 2354 },
2355 2355
2356 2356 // caches x/y coordinate pair to use with overlap
2357 2357 within: function(element, x, y) {
2358 2358 if (this.includeScrollOffsets)
2359 2359 return this.withinIncludingScrolloffsets(element, x, y);
2360 2360 this.xcomp = x;
2361 2361 this.ycomp = y;
2362 2362 this.offset = this.cumulativeOffset(element);
2363 2363
2364 2364 return (y >= this.offset[1] &&
2365 2365 y < this.offset[1] + element.offsetHeight &&
2366 2366 x >= this.offset[0] &&
2367 2367 x < this.offset[0] + element.offsetWidth);
2368 2368 },
2369 2369
2370 2370 withinIncludingScrolloffsets: function(element, x, y) {
2371 2371 var offsetcache = this.realOffset(element);
2372 2372
2373 2373 this.xcomp = x + offsetcache[0] - this.deltaX;
2374 2374 this.ycomp = y + offsetcache[1] - this.deltaY;
2375 2375 this.offset = this.cumulativeOffset(element);
2376 2376
2377 2377 return (this.ycomp >= this.offset[1] &&
2378 2378 this.ycomp < this.offset[1] + element.offsetHeight &&
2379 2379 this.xcomp >= this.offset[0] &&
2380 2380 this.xcomp < this.offset[0] + element.offsetWidth);
2381 2381 },
2382 2382
2383 2383 // within must be called directly before
2384 2384 overlap: function(mode, element) {
2385 2385 if (!mode) return 0;
2386 2386 if (mode == 'vertical')
2387 2387 return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
2388 2388 element.offsetHeight;
2389 2389 if (mode == 'horizontal')
2390 2390 return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
2391 2391 element.offsetWidth;
2392 2392 },
2393 2393
2394 2394 page: function(forElement) {
2395 2395 var valueT = 0, valueL = 0;
2396 2396
2397 2397 var element = forElement;
2398 2398 do {
2399 2399 valueT += element.offsetTop || 0;
2400 2400 valueL += element.offsetLeft || 0;
2401 2401
2402 2402 // Safari fix
2403 2403 if (element.offsetParent==document.body)
2404 2404 if (Element.getStyle(element,'position')=='absolute') break;
2405 2405
2406 2406 } while (element = element.offsetParent);
2407 2407
2408 2408 element = forElement;
2409 2409 do {
2410 2410 if (!window.opera || element.tagName=='BODY') {
2411 2411 valueT -= element.scrollTop || 0;
2412 2412 valueL -= element.scrollLeft || 0;
2413 2413 }
2414 2414 } while (element = element.parentNode);
2415 2415
2416 2416 return [valueL, valueT];
2417 2417 },
2418 2418
2419 2419 clone: function(source, target) {
2420 2420 var options = Object.extend({
2421 2421 setLeft: true,
2422 2422 setTop: true,
2423 2423 setWidth: true,
2424 2424 setHeight: true,
2425 2425 offsetTop: 0,
2426 2426 offsetLeft: 0
2427 2427 }, arguments[2] || {})
2428 2428
2429 2429 // find page position of source
2430 2430 source = $(source);
2431 2431 var p = Position.page(source);
2432 2432
2433 2433 // find coordinate system to use
2434 2434 target = $(target);
2435 2435 var delta = [0, 0];
2436 2436 var parent = null;
2437 2437 // delta [0,0] will do fine with position: fixed elements,
2438 2438 // position:absolute needs offsetParent deltas
2439 2439 if (Element.getStyle(target,'position') == 'absolute') {
2440 2440 parent = Position.offsetParent(target);
2441 2441 delta = Position.page(parent);
2442 2442 }
2443 2443
2444 2444 // correct by body offsets (fixes Safari)
2445 2445 if (parent == document.body) {
2446 2446 delta[0] -= document.body.offsetLeft;
2447 2447 delta[1] -= document.body.offsetTop;
2448 2448 }
2449 2449
2450 2450 // set position
2451 2451 if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
2452 2452 if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
2453 2453 if(options.setWidth) target.style.width = source.offsetWidth + 'px';
2454 2454 if(options.setHeight) target.style.height = source.offsetHeight + 'px';
2455 2455 },
2456 2456
2457 2457 absolutize: function(element) {
2458 2458 element = $(element);
2459 2459 if (element.style.position == 'absolute') return;
2460 2460 Position.prepare();
2461 2461
2462 2462 var offsets = Position.positionedOffset(element);
2463 2463 var top = offsets[1];
2464 2464 var left = offsets[0];
2465 2465 var width = element.clientWidth;
2466 2466 var height = element.clientHeight;
2467 2467
2468 2468 element._originalLeft = left - parseFloat(element.style.left || 0);
2469 2469 element._originalTop = top - parseFloat(element.style.top || 0);
2470 2470 element._originalWidth = element.style.width;
2471 2471 element._originalHeight = element.style.height;
2472 2472
2473 2473 element.style.position = 'absolute';
2474 2474 element.style.top = top + 'px';
2475 2475 element.style.left = left + 'px';
2476 2476 element.style.width = width + 'px';
2477 2477 element.style.height = height + 'px';
2478 2478 },
2479 2479
2480 2480 relativize: function(element) {
2481 2481 element = $(element);
2482 2482 if (element.style.position == 'relative') return;
2483 2483 Position.prepare();
2484 2484
2485 2485 element.style.position = 'relative';
2486 2486 var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
2487 2487 var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
2488 2488
2489 2489 element.style.top = top + 'px';
2490 2490 element.style.left = left + 'px';
2491 2491 element.style.height = element._originalHeight;
2492 2492 element.style.width = element._originalWidth;
2493 2493 }
2494 2494 }
2495 2495
2496 2496 // Safari returns margins on body which is incorrect if the child is absolutely
2497 2497 // positioned. For performance reasons, redefine Position.cumulativeOffset for
2498 2498 // KHTML/WebKit only.
2499 2499 if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
2500 2500 Position.cumulativeOffset = function(element) {
2501 2501 var valueT = 0, valueL = 0;
2502 2502 do {
2503 2503 valueT += element.offsetTop || 0;
2504 2504 valueL += element.offsetLeft || 0;
2505 2505 if (element.offsetParent == document.body)
2506 2506 if (Element.getStyle(element, 'position') == 'absolute') break;
2507 2507
2508 2508 element = element.offsetParent;
2509 2509 } while (element);
2510 2510
2511 2511 return [valueL, valueT];
2512 2512 }
2513 2513 }
2514 2514
2515 2515 Element.addMethods(); No newline at end of file
@@ -1,476 +1,477
1 1 body { font-family: Verdana, sans-serif; font-size: 12px; color:#484848; margin: 0; padding: 0; min-width: 900px; }
2 2
3 3 h1, h2, h3, h4 { font-family: "Trebuchet MS", Verdana, sans-serif;}
4 4 h1 {margin:0; padding:0; font-size: 24px;}
5 5 h2, .wiki h1 {font-size: 20px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
6 6 h3, .wiki h2 {font-size: 16px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
7 7 h4, .wiki h3 {font-size: 12px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb; color: #444;}
8 8
9 9 /***** Layout *****/
10 10 #top-menu {background: #2C4056;color: #fff;height:1.5em; padding: 2px 6px 0px 6px;}
11 11 #top-menu a {color: #fff; padding-right: 4px;}
12 12 #account {float:right;}
13 13
14 14 #header {height:5.3em;margin:0;background-color:#507AAA;color:#f8f8f8; padding: 4px 8px 0px 6px; position:relative;}
15 15 #header a {color:#f8f8f8;}
16 16 #quick-search {float:right;}
17 17
18 18 #main-menu {position: absolute; bottom: 0px; left:6px;}
19 19 #main-menu ul {margin: 0; padding: 0;}
20 20 #main-menu li {
21 21 float:left;
22 22 list-style-type:none;
23 23 margin: 0px 10px 0px 0px;
24 24 padding: 0px 0px 0px 0px;
25 25 white-space:nowrap;
26 26 }
27 27 #main-menu li a {
28 28 display: block;
29 29 color: #fff;
30 30 text-decoration: none;
31 31 margin: 0;
32 32 padding: 4px 4px 4px 4px;
33 33 background: #2C4056;
34 34 }
35 35 #main-menu li a:hover {background:#759FCF;}
36 36
37 37 #main {background: url(../images/mainbg.png) repeat-x; background-color:#EEEEEE;}
38 38
39 39 #sidebar{ float: right; width: 17%; position: relative; z-index: 9; min-height: 600px; padding: 0; margin: 0;}
40 40 * html #sidebar{ width: 17%; }
41 41 #sidebar h3{ font-size: 14px; margin-top:14px; color: #666; }
42 42 #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
43 43 * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; }
44 44
45 45 #content { width: 80%; background: url(../images/contentbg.png) repeat-x; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; position: relative; z-index: 10; height:600px; min-height: 600px;}
46 46 * html #content{ width: 80%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;}
47 47 html>body #content {
48 48 height: auto;
49 49 min-height: 600px;
50 50 }
51 51
52 52 #main.nosidebar #sidebar{ display: none; }
53 53 #main.nosidebar #content{ width: auto; border-right: 0; }
54 54
55 55 #footer {clear: both; border-top: 1px solid #bbb; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
56 56
57 57 #login-form table {margin-top:5em; padding:1em; margin-left: auto; margin-right: auto; border: 2px solid #FDBF3B; background-color:#FFEBC1; }
58 58 #login-form table td {padding: 6px;}
59 59 #login-form label {font-weight: bold;}
60 60
61 61 .clear:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; }
62 62
63 63 /***** Links *****/
64 64 a, a:link, a:visited{ color: #2A5685; text-decoration: none; }
65 65 a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
66 66 a img{ border: 0; }
67 67
68 68 /***** Tables *****/
69 69 table.list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
70 70 table.list th { background-color:#EEEEEE; padding: 4px; white-space:nowrap; }
71 71 table.list td { overflow: hidden; text-overflow: ellipsis; vertical-align: top;}
72 72 table.list td.id { width: 2%; text-align: center;}
73 73 table.list td.checkbox { width: 15px; padding: 0px;}
74 74
75 75 tr.issue { text-align: center; white-space: nowrap; }
76 76 tr.issue td.subject, tr.issue td.category { white-space: normal; }
77 77 tr.issue td.subject { text-align: left; }
78 78
79 79 table.list tbody tr:hover { background-color:#ffffdd; }
80 80 table td {padding:2px;}
81 81 table p {margin:0;}
82 82 .odd {background-color:#f6f7f8;}
83 83 .even {background-color: #fff;}
84 84
85 85 .highlight { background-color: #FCFD8D;}
86 86 .highlight.token-1 { background-color: #faa;}
87 87 .highlight.token-2 { background-color: #afa;}
88 88 .highlight.token-3 { background-color: #aaf;}
89 89
90 90 .box{
91 91 padding:6px;
92 92 margin-bottom: 10px;
93 93 background-color:#f6f6f6;
94 94 color:#505050;
95 95 line-height:1.5em;
96 96 border: 1px solid #e4e4e4;
97 97 }
98 98
99 99 div.square {
100 100 border: 1px solid #999;
101 101 float: left;
102 102 margin: .3em .4em 0 .4em;
103 103 overflow: hidden;
104 104 width: .6em; height: .6em;
105 105 }
106 106
107 107 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin-top:5px;font-size:0.9em;}
108 108 .splitcontentleft{float:left; width:49%;}
109 109 .splitcontentright{float:right; width:49%;}
110 110 form {display: inline;}
111 111 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
112 112 fieldset {border: 1px solid #e4e4e4; margin:0;}
113 113 legend {color: #484848;}
114 114 hr { width: 100%; height: 1px; background: #ccc; border: 0;}
115 115 textarea.wiki-edit { width: 99%; }
116 116 li p {margin-top: 0;}
117 117 div.issue {background:#ffffdd; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7;}
118 118 .autoscroll {overflow-x: auto; padding:1px; width:100%;}
119 119 #user_firstname, #user_lastname, #user_mail, #notification_option { width: 90%; }
120 120
121 121 /***** Tabular forms ******/
122 122 .tabular p{
123 123 margin: 0;
124 124 padding: 5px 0 8px 0;
125 125 padding-left: 180px; /*width of left column containing the label elements*/
126 126 height: 1%;
127 127 clear:left;
128 128 }
129 129
130 130 .tabular label{
131 131 font-weight: bold;
132 132 float: left;
133 133 text-align: right;
134 134 margin-left: -180px; /*width of left column*/
135 135 width: 175px; /*width of labels. Should be smaller than left column to create some right
136 136 margin*/
137 137 }
138 138
139 139 .tabular label.floating{
140 140 font-weight: normal;
141 141 margin-left: 0px;
142 142 text-align: left;
143 143 width: 200px;
144 144 }
145 145
146 146 #preview fieldset {margin-top: 1em; background: url(../images/draft.png)}
147 147
148 148 #settings .tabular p{ padding-left: 300px; }
149 149 #settings .tabular label{ margin-left: -300px; width: 295px; }
150 150
151 151 .required {color: #bb0000;}
152 152 .summary {font-style: italic;}
153 153
154 154 div.attachments p { margin:4px 0 2px 0; }
155 155
156 156 /***** Flash & error messages ****/
157 157 #errorExplanation, div.flash, div.nodata {
158 158 padding: 4px 4px 4px 30px;
159 159 margin-bottom: 12px;
160 160 font-size: 1.1em;
161 161 border: 2px solid;
162 162 }
163 163
164 164 div.flash {margin-top: 8px;}
165 165
166 166 div.flash.error, #errorExplanation {
167 167 background: url(../images/false.png) 8px 5px no-repeat;
168 168 background-color: #ffe3e3;
169 169 border-color: #dd0000;
170 170 color: #550000;
171 171 }
172 172
173 173 div.flash.notice {
174 174 background: url(../images/true.png) 8px 5px no-repeat;
175 175 background-color: #dfffdf;
176 176 border-color: #9fcf9f;
177 177 color: #005f00;
178 178 }
179 179
180 180 .nodata {
181 181 text-align: center;
182 182 background-color: #FFEBC1;
183 183 border-color: #FDBF3B;
184 184 color: #A6750C;
185 185 }
186 186
187 187 #errorExplanation ul { font-size: 0.9em;}
188 188
189 189 /***** Ajax indicator ******/
190 190 #ajax-indicator {
191 191 position: absolute; /* fixed not supported by IE */
192 192 background-color:#eee;
193 193 border: 1px solid #bbb;
194 194 top:35%;
195 195 left:40%;
196 196 width:20%;
197 197 font-weight:bold;
198 198 text-align:center;
199 199 padding:0.6em;
200 200 z-index:100;
201 201 filter:alpha(opacity=50);
202 202 -moz-opacity:0.5;
203 203 opacity: 0.5;
204 204 -khtml-opacity: 0.5;
205 205 }
206 206
207 207 html>body #ajax-indicator { position: fixed; }
208 208
209 209 #ajax-indicator span {
210 210 background-position: 0% 40%;
211 211 background-repeat: no-repeat;
212 212 background-image: url(../images/loading.gif);
213 213 padding-left: 26px;
214 214 vertical-align: bottom;
215 215 }
216 216
217 217 /***** Calendar *****/
218 218 table.cal {border-collapse: collapse; width: 100%; margin: 8px 0 6px 0;border: 1px solid #d7d7d7;}
219 219 table.cal thead th {width: 14%;}
220 220 table.cal tbody tr {height: 100px;}
221 221 table.cal th { background-color:#EEEEEE; padding: 4px; }
222 222 table.cal td {border: 1px solid #d7d7d7; vertical-align: top; font-size: 0.9em;}
223 223 table.cal td p.day-num {font-size: 1.1em; text-align:right;}
224 224 table.cal td.odd p.day-num {color: #bbb;}
225 225 table.cal td.today {background:#ffffdd;}
226 226 table.cal td.today p.day-num {font-weight: bold;}
227 227
228 228 /***** Tooltips ******/
229 229 .tooltip{position:relative;z-index:24;}
230 230 .tooltip:hover{z-index:25;color:#000;}
231 231 .tooltip span.tip{display: none; text-align:left;}
232 232
233 233 div.tooltip:hover span.tip{
234 234 display:block;
235 235 position:absolute;
236 236 top:12px; left:24px; width:270px;
237 237 border:1px solid #555;
238 238 background-color:#fff;
239 239 padding: 4px;
240 240 font-size: 0.8em;
241 241 color:#505050;
242 242 }
243 243
244 244 /***** Progress bar *****/
245 245 .progress {
246 246 border: 1px solid #D7D7D7;
247 247 border-collapse: collapse;
248 248 border-spacing: 0pt;
249 249 empty-cells: show;
250 250 padding: 3px;
251 251 width: 40em;
252 252 text-align: center;
253 253 }
254 254
255 255 .progress td { height: 1em; }
256 256 .progress .closed { background: #BAE0BA none repeat scroll 0%; }
257 257 .progress .open { background: #FFF none repeat scroll 0%; }
258 258
259 259 /***** Tabs *****/
260 260 #content .tabs{height: 2.6em;}
261 261 #content .tabs ul{margin:0;}
262 262 #content .tabs ul li{
263 263 float:left;
264 264 list-style-type:none;
265 265 white-space:nowrap;
266 266 margin-right:8px;
267 267 background:#fff;
268 268 }
269 269 #content .tabs ul li a{
270 270 display:block;
271 271 font-size: 0.9em;
272 272 text-decoration:none;
273 273 line-height:1em;
274 274 padding:4px;
275 275 border: 1px solid #c0c0c0;
276 276 }
277 277
278 278 #content .tabs ul li a.selected, #content .tabs ul li a:hover{
279 279 background-color: #507AAA;
280 280 border: 1px solid #507AAA;
281 281 color: #fff;
282 282 text-decoration:none;
283 283 }
284 284
285 285 /***** Diff *****/
286 286 .diff_out { background: #fcc; }
287 287 .diff_in { background: #cfc; }
288 288
289 289 /***** Wiki *****/
290 290 div.wiki table {
291 291 border: 1px solid #505050;
292 292 border-collapse: collapse;
293 293 }
294 294
295 295 div.wiki table, div.wiki td, div.wiki th {
296 296 border: 1px solid #bbb;
297 297 padding: 4px;
298 298 }
299 299
300 300 div.wiki .external {
301 301 background-position: 0% 60%;
302 302 background-repeat: no-repeat;
303 303 padding-left: 12px;
304 304 background-image: url(../images/external.png);
305 305 }
306 306
307 307 div.wiki a.new {
308 308 color: #b73535;
309 309 }
310 310
311 311 div.wiki pre {
312 312 margin: 1em 1em 1em 1.6em;
313 313 padding: 2px;
314 314 background-color: #fafafa;
315 315 border: 1px solid #dadada;
316 316 width:95%;
317 317 overflow-x: auto;
318 318 }
319 319
320 320 div.wiki div.toc {
321 321 background-color: #ffffdd;
322 322 border: 1px solid #e4e4e4;
323 323 padding: 4px;
324 324 line-height: 1.2em;
325 325 margin-bottom: 12px;
326 326 margin-right: 12px;
327 327 display: table
328 328 }
329 329 * html div.wiki div.toc { width: 50%; } /* IE6 doesn't autosize div */
330 330
331 331 div.wiki div.toc.right { float: right; margin-left: 12px; margin-right: 0; width: auto; }
332 332 div.wiki div.toc.left { float: left; margin-right: 12px; margin-left: 0; width: auto; }
333 333
334 334 div.wiki div.toc a {
335 335 display: block;
336 336 font-size: 0.9em;
337 337 font-weight: normal;
338 338 text-decoration: none;
339 339 color: #606060;
340 340 }
341 341 div.wiki div.toc a:hover { color: #c61a1a; text-decoration: underline;}
342 342
343 343 div.wiki div.toc a.heading2 { margin-left: 6px; }
344 344 div.wiki div.toc a.heading3 { margin-left: 12px; font-size: 0.8em; }
345 345
346 346 /***** My page layout *****/
347 347 .block-receiver {
348 348 border:1px dashed #c0c0c0;
349 349 margin-bottom: 20px;
350 350 padding: 15px 0 15px 0;
351 351 }
352 352
353 353 .mypage-box {
354 354 margin:0 0 20px 0;
355 355 color:#505050;
356 356 line-height:1.5em;
357 357 }
358 358
359 359 .handle {
360 360 cursor: move;
361 361 }
362 362
363 363 a.close-icon {
364 364 display:block;
365 365 margin-top:3px;
366 366 overflow:hidden;
367 367 width:12px;
368 368 height:12px;
369 369 background-repeat: no-repeat;
370 370 cursor:pointer;
371 371 background-image:url('../images/close.png');
372 372 }
373 373
374 374 a.close-icon:hover {
375 375 background-image:url('../images/close_hl.png');
376 376 }
377 377
378 378 /***** Gantt chart *****/
379 379 .gantt_hdr {
380 380 position:absolute;
381 381 top:0;
382 382 height:16px;
383 383 border-top: 1px solid #c0c0c0;
384 384 border-bottom: 1px solid #c0c0c0;
385 385 border-right: 1px solid #c0c0c0;
386 386 text-align: center;
387 387 overflow: hidden;
388 388 }
389 389
390 390 .task {
391 391 position: absolute;
392 392 height:8px;
393 393 font-size:0.8em;
394 394 color:#888;
395 395 padding:0;
396 396 margin:0;
397 397 line-height:0.8em;
398 398 }
399 399
400 400 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
401 401 .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; }
402 402 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
403 403 .milestone { background-image:url(../images/milestone.png); background-repeat: no-repeat; border: 0; }
404 404
405 405 /***** Icons *****/
406 406 .icon {
407 407 background-position: 0% 40%;
408 408 background-repeat: no-repeat;
409 409 padding-left: 20px;
410 410 padding-top: 2px;
411 411 padding-bottom: 3px;
412 412 }
413 413
414 414 .icon22 {
415 415 background-position: 0% 40%;
416 416 background-repeat: no-repeat;
417 417 padding-left: 26px;
418 418 line-height: 22px;
419 419 vertical-align: middle;
420 420 }
421 421
422 422 .icon-add { background-image: url(../images/add.png); }
423 423 .icon-edit { background-image: url(../images/edit.png); }
424 424 .icon-del { background-image: url(../images/delete.png); }
425 425 .icon-move { background-image: url(../images/move.png); }
426 426 .icon-save { background-image: url(../images/save.png); }
427 427 .icon-cancel { background-image: url(../images/cancel.png); }
428 428 .icon-pdf { background-image: url(../images/pdf.png); }
429 429 .icon-csv { background-image: url(../images/csv.png); }
430 430 .icon-html { background-image: url(../images/html.png); }
431 431 .icon-image { background-image: url(../images/image.png); }
432 432 .icon-txt { background-image: url(../images/txt.png); }
433 433 .icon-file { background-image: url(../images/file.png); }
434 434 .icon-folder { background-image: url(../images/folder.png); }
435 .open .icon-folder { background-image: url(../images/folder_open.png); }
435 436 .icon-package { background-image: url(../images/package.png); }
436 437 .icon-home { background-image: url(../images/home.png); }
437 438 .icon-user { background-image: url(../images/user.png); }
438 439 .icon-mypage { background-image: url(../images/user_page.png); }
439 440 .icon-admin { background-image: url(../images/admin.png); }
440 441 .icon-projects { background-image: url(../images/projects.png); }
441 442 .icon-logout { background-image: url(../images/logout.png); }
442 443 .icon-help { background-image: url(../images/help.png); }
443 444 .icon-attachment { background-image: url(../images/attachment.png); }
444 445 .icon-index { background-image: url(../images/index.png); }
445 446 .icon-history { background-image: url(../images/history.png); }
446 447 .icon-feed { background-image: url(../images/feed.png); }
447 448 .icon-time { background-image: url(../images/time.png); }
448 449 .icon-stats { background-image: url(../images/stats.png); }
449 450 .icon-warning { background-image: url(../images/warning.png); }
450 451 .icon-fav { background-image: url(../images/fav.png); }
451 452 .icon-fav-off { background-image: url(../images/fav_off.png); }
452 453 .icon-reload { background-image: url(../images/reload.png); }
453 454 .icon-lock { background-image: url(../images/locked.png); }
454 455 .icon-unlock { background-image: url(../images/unlock.png); }
455 456 .icon-note { background-image: url(../images/note.png); }
456 457
457 458 .icon22-projects { background-image: url(../images/22x22/projects.png); }
458 459 .icon22-users { background-image: url(../images/22x22/users.png); }
459 460 .icon22-tracker { background-image: url(../images/22x22/tracker.png); }
460 461 .icon22-role { background-image: url(../images/22x22/role.png); }
461 462 .icon22-workflow { background-image: url(../images/22x22/workflow.png); }
462 463 .icon22-options { background-image: url(../images/22x22/options.png); }
463 464 .icon22-notifications { background-image: url(../images/22x22/notifications.png); }
464 465 .icon22-authent { background-image: url(../images/22x22/authent.png); }
465 466 .icon22-info { background-image: url(../images/22x22/info.png); }
466 467 .icon22-comment { background-image: url(../images/22x22/comment.png); }
467 468 .icon22-package { background-image: url(../images/22x22/package.png); }
468 469 .icon22-settings { background-image: url(../images/22x22/settings.png); }
469 470 .icon22-plugin { background-image: url(../images/22x22/plugin.png); }
470 471
471 472 /***** Media print specific styles *****/
472 473 @media print {
473 474 #top-menu, #header, #main-menu, #sidebar, #footer, .contextual { display:none; }
474 475 #main { background: #fff; }
475 476 #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; }
476 477 }
General Comments 0
You need to be logged in to leave comments. Login now