backlog 0.12.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +15 -0
- data/app/controllers/application.rb +17 -0
- data/app/controllers/customers_controller.rb +51 -0
- data/app/controllers/estimates_controller.rb +3 -4
- data/app/controllers/tasks_controller.rb +20 -32
- data/app/helpers/application_helper.rb +11 -2
- data/app/helpers/customers_helper.rb +2 -0
- data/app/helpers/tasks_helper.rb +69 -0
- data/app/views/customers/_form.rhtml +7 -0
- data/app/views/customers/edit.rhtml +9 -0
- data/app/views/customers/list.rhtml +27 -0
- data/app/views/customers/new.rhtml +8 -0
- data/app/views/customers/show.rhtml +8 -0
- data/app/views/periods/_show_active.rhtml +13 -10
- data/app/views/redirect.rjs +1 -0
- data/app/views/redirect.rjs~ +0 -0
- data/app/views/tasks/_fields_header.rhtml +1 -1
- data/app/views/tasks/_form.rhtml +6 -1
- data/app/views/tasks/_task.rhtml +3 -3
- data/app/views/tasks/finish.rjs +4 -0
- data/app/views/tasks/move_to_period.rjs +4 -0
- data/app/views/tasks/reopen.rjs +4 -24
- data/config/boot.rb +1 -10
- data/public/javascripts/builder.js +136 -0
- data/public/javascripts/controls.js +486 -354
- data/public/javascripts/dragdrop.js +82 -52
- data/public/javascripts/effects.js +398 -364
- data/public/javascripts/prototype.js +2764 -1095
- data/public/javascripts/scriptaculous.js +58 -0
- data/public/javascripts/slider.js +275 -0
- data/public/javascripts/sound.js +55 -0
- data/public/javascripts/unittest.js +568 -0
- data/test/functional/backlogs_controller_test.rb +1 -1
- data/test/functional/customers_controller_test.rb +93 -0
- data/test/functional/tasks_controller_test.rb +4 -8
- data/test/functional/work_accounts_controller_test.rb +1 -1
- data/test/performance/test_threaded.rb +40 -31
- data/test/test_helper.rb +4 -2
- metadata +20 -3
- data/app/views/tasks/finish_ajax.rjs +0 -25
@@ -2,7 +2,7 @@
|
|
2
2
|
<th/>
|
3
3
|
<th align="center"><%=active ? '#' : l(:resolution_abr)%></th>
|
4
4
|
<th><%=l :task %></th>
|
5
|
-
<th><%=l :start if backlog.work_account && backlog.work_account.track_times?
|
5
|
+
<th><%=l :start if backlog.work_account && backlog.work_account.track_times? %></th>
|
6
6
|
<th/>
|
7
7
|
<th><%=l :done if backlog.track_done? %></th>
|
8
8
|
<th width="*"><%=l :todo if backlog.track_todo? %></th>
|
data/app/views/tasks/_form.rhtml
CHANGED
@@ -33,7 +33,12 @@
|
|
33
33
|
|
34
34
|
<% if @task.enable_customer? %>
|
35
35
|
<p><label for="task_customer"><%=l :customer%></label><br/>
|
36
|
-
<%=
|
36
|
+
<%=select 'task', 'customer_id', [['', '']] + @customers.map{|c| [c.name, c.id]} %>
|
37
|
+
<% if @task.customer %>
|
38
|
+
<%=image_detour_to('customer.png', "#{l(:customer)} #{@task.customer.name}", {:controller => 'customers', :action => :edit, :id => @task.customer}, {:class => 'image-submit', :style => 'vertical-align: bottom'}) %>
|
39
|
+
<% end %>
|
40
|
+
<%=detour_to l(:new_customer), :controller => 'customers', :action => :new %>
|
41
|
+
</p>
|
37
42
|
<% end %>
|
38
43
|
|
39
44
|
<% if @task.track_todo? %>
|
data/app/views/tasks/_task.rhtml
CHANGED
@@ -69,7 +69,7 @@
|
|
69
69
|
<% end -%>
|
70
70
|
<% end -%>
|
71
71
|
<% if (not @task.track_times?) && !@task.work_started? && (@task.period.nil? || @task.period.active?) %>
|
72
|
-
<%= image_link_to_remote('checkmark.png', l(:complete), {:controller => 'tasks', :action => :
|
72
|
+
<%= image_link_to_remote('checkmark.png', l(:complete), {:controller => 'tasks', :action => :finish, :id => @task}, nil, true)%>
|
73
73
|
<% end -%>
|
74
74
|
<% end -%>
|
75
75
|
<% end -%>
|
@@ -79,8 +79,8 @@
|
|
79
79
|
<% if @task.active? %>
|
80
80
|
<% if @task.loggable? %>
|
81
81
|
<% unless @task.work_started? %>
|
82
|
-
<%=
|
83
|
-
<%=
|
82
|
+
<%=image_link_to_remote('arrow_right.png', l(:move_to_next_period), {:controller => 'tasks', :action => :move_to_next_period, :id => @task}, nil, true)%>
|
83
|
+
<%=image_link_to_remote('ernes_stop.png', l(:abort), {:controller => 'tasks', :action => :abort, :id => @task}, nil, true)%>
|
84
84
|
<% end %>
|
85
85
|
<% end %>
|
86
86
|
<% elsif (@task.period.nil? || (not @task.period.passed?)) && @task.leaf? %>
|
data/app/views/tasks/reopen.rjs
CHANGED
@@ -1,26 +1,6 @@
|
|
1
|
-
page
|
2
|
-
page.visual_effect(:appear, :notice)
|
1
|
+
display_notice(page)
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
page.select('#active_tasks tr').first.remove
|
7
|
-
end
|
3
|
+
remove_finished_task(page)
|
4
|
+
add_active_task(page)
|
8
5
|
|
9
|
-
page
|
10
|
-
|
11
|
-
page.remove "task_#{@task.id}"
|
12
|
-
|
13
|
-
if @last_finished
|
14
|
-
page.select('#completed_tasks tr').first.remove
|
15
|
-
end
|
16
|
-
|
17
|
-
page.insert_html :top, :active_tasks, :partial => '/tasks/task', :locals => { :task => @task, :i => 1, :active => true, :highlight_task => false, :update => :spotlight, :hidden => true }
|
18
|
-
page[:no_tasks_message].hide
|
19
|
-
|
20
|
-
page.insert_html :top, :active_tasks, :partial => '/tasks/fields_header', :locals => {:backlog => @task.backlog}
|
21
|
-
page.insert_html :top, :active_tasks, :partial => '/tasks/backlog_header', :locals => {:backlog => @task.backlog}
|
22
|
-
page.visual_effect :blind_down, "active_tasks"
|
23
|
-
page.visual_effect :appear, "task_#{@task.id}"
|
24
|
-
|
25
|
-
page.visual_effect :appear, "task_#{@task.id}"
|
26
|
-
page['burn_down_chart'].src = url_for(:controller => 'periods', :action => :burn_down_chart_thumbnail, :id => @task.period_id, :rnd => rand)
|
6
|
+
update_burn_down_chart(page)
|
data/config/boot.rb
CHANGED
@@ -1,15 +1,6 @@
|
|
1
1
|
# Don't change this file. Configuration is done in config/environment.rb and config/environments/*.rb
|
2
2
|
|
3
|
-
unless defined?(RAILS_ROOT)
|
4
|
-
root_path = File.join(File.dirname(__FILE__), '..')
|
5
|
-
|
6
|
-
unless RUBY_PLATFORM =~ /(:?mswin|mingw)/
|
7
|
-
require 'pathname'
|
8
|
-
root_path = Pathname.new(root_path).cleanpath(true).to_s
|
9
|
-
end
|
10
|
-
|
11
|
-
RAILS_ROOT = root_path
|
12
|
-
end
|
3
|
+
RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
|
13
4
|
|
14
5
|
unless defined?(Rails::Initializer)
|
15
6
|
if File.directory?("#{RAILS_ROOT}/vendor/rails")
|
@@ -0,0 +1,136 @@
|
|
1
|
+
// script.aculo.us builder.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
|
2
|
+
|
3
|
+
// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
4
|
+
//
|
5
|
+
// script.aculo.us is freely distributable under the terms of an MIT-style license.
|
6
|
+
// For details, see the script.aculo.us web site: http://script.aculo.us/
|
7
|
+
|
8
|
+
var Builder = {
|
9
|
+
NODEMAP: {
|
10
|
+
AREA: 'map',
|
11
|
+
CAPTION: 'table',
|
12
|
+
COL: 'table',
|
13
|
+
COLGROUP: 'table',
|
14
|
+
LEGEND: 'fieldset',
|
15
|
+
OPTGROUP: 'select',
|
16
|
+
OPTION: 'select',
|
17
|
+
PARAM: 'object',
|
18
|
+
TBODY: 'table',
|
19
|
+
TD: 'table',
|
20
|
+
TFOOT: 'table',
|
21
|
+
TH: 'table',
|
22
|
+
THEAD: 'table',
|
23
|
+
TR: 'table'
|
24
|
+
},
|
25
|
+
// note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
|
26
|
+
// due to a Firefox bug
|
27
|
+
node: function(elementName) {
|
28
|
+
elementName = elementName.toUpperCase();
|
29
|
+
|
30
|
+
// try innerHTML approach
|
31
|
+
var parentTag = this.NODEMAP[elementName] || 'div';
|
32
|
+
var parentElement = document.createElement(parentTag);
|
33
|
+
try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
|
34
|
+
parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
|
35
|
+
} catch(e) {}
|
36
|
+
var element = parentElement.firstChild || null;
|
37
|
+
|
38
|
+
// see if browser added wrapping tags
|
39
|
+
if(element && (element.tagName.toUpperCase() != elementName))
|
40
|
+
element = element.getElementsByTagName(elementName)[0];
|
41
|
+
|
42
|
+
// fallback to createElement approach
|
43
|
+
if(!element) element = document.createElement(elementName);
|
44
|
+
|
45
|
+
// abort if nothing could be created
|
46
|
+
if(!element) return;
|
47
|
+
|
48
|
+
// attributes (or text)
|
49
|
+
if(arguments[1])
|
50
|
+
if(this._isStringOrNumber(arguments[1]) ||
|
51
|
+
(arguments[1] instanceof Array) ||
|
52
|
+
arguments[1].tagName) {
|
53
|
+
this._children(element, arguments[1]);
|
54
|
+
} else {
|
55
|
+
var attrs = this._attributes(arguments[1]);
|
56
|
+
if(attrs.length) {
|
57
|
+
try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
|
58
|
+
parentElement.innerHTML = "<" +elementName + " " +
|
59
|
+
attrs + "></" + elementName + ">";
|
60
|
+
} catch(e) {}
|
61
|
+
element = parentElement.firstChild || null;
|
62
|
+
// workaround firefox 1.0.X bug
|
63
|
+
if(!element) {
|
64
|
+
element = document.createElement(elementName);
|
65
|
+
for(attr in arguments[1])
|
66
|
+
element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
|
67
|
+
}
|
68
|
+
if(element.tagName.toUpperCase() != elementName)
|
69
|
+
element = parentElement.getElementsByTagName(elementName)[0];
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
// text, or array of children
|
74
|
+
if(arguments[2])
|
75
|
+
this._children(element, arguments[2]);
|
76
|
+
|
77
|
+
return element;
|
78
|
+
},
|
79
|
+
_text: function(text) {
|
80
|
+
return document.createTextNode(text);
|
81
|
+
},
|
82
|
+
|
83
|
+
ATTR_MAP: {
|
84
|
+
'className': 'class',
|
85
|
+
'htmlFor': 'for'
|
86
|
+
},
|
87
|
+
|
88
|
+
_attributes: function(attributes) {
|
89
|
+
var attrs = [];
|
90
|
+
for(attribute in attributes)
|
91
|
+
attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
|
92
|
+
'="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'"') + '"');
|
93
|
+
return attrs.join(" ");
|
94
|
+
},
|
95
|
+
_children: function(element, children) {
|
96
|
+
if(children.tagName) {
|
97
|
+
element.appendChild(children);
|
98
|
+
return;
|
99
|
+
}
|
100
|
+
if(typeof children=='object') { // array can hold nodes and text
|
101
|
+
children.flatten().each( function(e) {
|
102
|
+
if(typeof e=='object')
|
103
|
+
element.appendChild(e)
|
104
|
+
else
|
105
|
+
if(Builder._isStringOrNumber(e))
|
106
|
+
element.appendChild(Builder._text(e));
|
107
|
+
});
|
108
|
+
} else
|
109
|
+
if(Builder._isStringOrNumber(children))
|
110
|
+
element.appendChild(Builder._text(children));
|
111
|
+
},
|
112
|
+
_isStringOrNumber: function(param) {
|
113
|
+
return(typeof param=='string' || typeof param=='number');
|
114
|
+
},
|
115
|
+
build: function(html) {
|
116
|
+
var element = this.node('div');
|
117
|
+
$(element).update(html.strip());
|
118
|
+
return element.down();
|
119
|
+
},
|
120
|
+
dump: function(scope) {
|
121
|
+
if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope
|
122
|
+
|
123
|
+
var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
|
124
|
+
"BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
|
125
|
+
"FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
|
126
|
+
"KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
|
127
|
+
"PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
|
128
|
+
"TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
|
129
|
+
|
130
|
+
tags.each( function(tag){
|
131
|
+
scope[tag] = function() {
|
132
|
+
return Builder.node.apply(Builder, [tag].concat($A(arguments)));
|
133
|
+
}
|
134
|
+
});
|
135
|
+
}
|
136
|
+
}
|
@@ -1,6 +1,8 @@
|
|
1
|
-
//
|
2
|
-
|
3
|
-
//
|
1
|
+
// script.aculo.us controls.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
|
2
|
+
|
3
|
+
// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
4
|
+
// (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
|
5
|
+
// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
|
4
6
|
// Contributors:
|
5
7
|
// Richard Livsey
|
6
8
|
// Rahul Bhargava
|
@@ -37,22 +39,23 @@
|
|
37
39
|
if(typeof Effect == 'undefined')
|
38
40
|
throw("controls.js requires including script.aculo.us' effects.js library");
|
39
41
|
|
40
|
-
var Autocompleter = {}
|
41
|
-
Autocompleter.Base =
|
42
|
-
Autocompleter.Base.prototype = {
|
42
|
+
var Autocompleter = { }
|
43
|
+
Autocompleter.Base = Class.create({
|
43
44
|
baseInitialize: function(element, update, options) {
|
44
|
-
|
45
|
+
element = $(element)
|
46
|
+
this.element = element;
|
45
47
|
this.update = $(update);
|
46
48
|
this.hasFocus = false;
|
47
49
|
this.changed = false;
|
48
50
|
this.active = false;
|
49
51
|
this.index = 0;
|
50
52
|
this.entryCount = 0;
|
53
|
+
this.oldElementValue = this.element.value;
|
51
54
|
|
52
55
|
if(this.setOptions)
|
53
56
|
this.setOptions(options);
|
54
57
|
else
|
55
|
-
this.options = options || {};
|
58
|
+
this.options = options || { };
|
56
59
|
|
57
60
|
this.options.paramName = this.options.paramName || this.element.name;
|
58
61
|
this.options.tokens = this.options.tokens || [];
|
@@ -74,6 +77,9 @@ Autocompleter.Base.prototype = {
|
|
74
77
|
|
75
78
|
if(typeof(this.options.tokens) == 'string')
|
76
79
|
this.options.tokens = new Array(this.options.tokens);
|
80
|
+
// Force carriage returns as token delimiters anyway
|
81
|
+
if (!this.options.tokens.include('\n'))
|
82
|
+
this.options.tokens.push('\n');
|
77
83
|
|
78
84
|
this.observer = null;
|
79
85
|
|
@@ -81,15 +87,14 @@ Autocompleter.Base.prototype = {
|
|
81
87
|
|
82
88
|
Element.hide(this.update);
|
83
89
|
|
84
|
-
Event.observe(this.element,
|
85
|
-
Event.observe(this.element,
|
90
|
+
Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
|
91
|
+
Event.observe(this.element, 'keypress', this.onKeyPress.bindAsEventListener(this));
|
86
92
|
},
|
87
93
|
|
88
94
|
show: function() {
|
89
95
|
if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
|
90
96
|
if(!this.iefix &&
|
91
|
-
(
|
92
|
-
(navigator.userAgent.indexOf('Opera')<0) &&
|
97
|
+
(Prototype.Browser.IE) &&
|
93
98
|
(Element.getStyle(this.update, 'position')=='absolute')) {
|
94
99
|
new Insertion.After(this.update,
|
95
100
|
'<iframe id="' + this.update.id + '_iefix" '+
|
@@ -139,17 +144,17 @@ Autocompleter.Base.prototype = {
|
|
139
144
|
case Event.KEY_UP:
|
140
145
|
this.markPrevious();
|
141
146
|
this.render();
|
142
|
-
if(
|
147
|
+
if(Prototype.Browser.WebKit) Event.stop(event);
|
143
148
|
return;
|
144
149
|
case Event.KEY_DOWN:
|
145
150
|
this.markNext();
|
146
151
|
this.render();
|
147
|
-
if(
|
152
|
+
if(Prototype.Browser.WebKit) Event.stop(event);
|
148
153
|
return;
|
149
154
|
}
|
150
155
|
else
|
151
156
|
if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
|
152
|
-
(
|
157
|
+
(Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
|
153
158
|
|
154
159
|
this.changed = true;
|
155
160
|
this.hasFocus = true;
|
@@ -195,7 +200,6 @@ Autocompleter.Base.prototype = {
|
|
195
200
|
this.index==i ?
|
196
201
|
Element.addClassName(this.getEntry(i),"selected") :
|
197
202
|
Element.removeClassName(this.getEntry(i),"selected");
|
198
|
-
|
199
203
|
if(this.hasFocus) {
|
200
204
|
this.show();
|
201
205
|
this.active = true;
|
@@ -238,21 +242,22 @@ Autocompleter.Base.prototype = {
|
|
238
242
|
}
|
239
243
|
var value = '';
|
240
244
|
if (this.options.select) {
|
241
|
-
var nodes =
|
245
|
+
var nodes = $(selectedElement).select('.' + this.options.select) || [];
|
242
246
|
if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
|
243
247
|
} else
|
244
248
|
value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
|
245
249
|
|
246
|
-
var
|
247
|
-
if (
|
248
|
-
var newValue = this.element.value.substr(0,
|
249
|
-
var whitespace = this.element.value.substr(
|
250
|
+
var bounds = this.getTokenBounds();
|
251
|
+
if (bounds[0] != -1) {
|
252
|
+
var newValue = this.element.value.substr(0, bounds[0]);
|
253
|
+
var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
|
250
254
|
if (whitespace)
|
251
255
|
newValue += whitespace[0];
|
252
|
-
this.element.value = newValue + value;
|
256
|
+
this.element.value = newValue + value + this.element.value.substr(bounds[1]);
|
253
257
|
} else {
|
254
258
|
this.element.value = value;
|
255
259
|
}
|
260
|
+
this.oldElementValue = this.element.value;
|
256
261
|
this.element.focus();
|
257
262
|
|
258
263
|
if (this.options.afterUpdateElement)
|
@@ -296,39 +301,48 @@ Autocompleter.Base.prototype = {
|
|
296
301
|
|
297
302
|
onObserverEvent: function() {
|
298
303
|
this.changed = false;
|
304
|
+
this.tokenBounds = null;
|
299
305
|
if(this.getToken().length>=this.options.minChars) {
|
300
|
-
this.startIndicator();
|
301
306
|
this.getUpdatedChoices();
|
302
307
|
} else {
|
303
308
|
this.active = false;
|
304
309
|
this.hide();
|
305
310
|
}
|
311
|
+
this.oldElementValue = this.element.value;
|
306
312
|
},
|
307
313
|
|
308
314
|
getToken: function() {
|
309
|
-
var
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
var
|
320
|
-
|
321
|
-
for (var
|
322
|
-
|
323
|
-
if (
|
324
|
-
|
315
|
+
var bounds = this.getTokenBounds();
|
316
|
+
return this.element.value.substring(bounds[0], bounds[1]).strip();
|
317
|
+
},
|
318
|
+
|
319
|
+
getTokenBounds: function() {
|
320
|
+
if (null != this.tokenBounds) return this.tokenBounds;
|
321
|
+
var value = this.element.value;
|
322
|
+
if (value.strip().empty()) return [-1, 0];
|
323
|
+
var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
|
324
|
+
var offset = (diff == this.oldElementValue.length ? 1 : 0);
|
325
|
+
var prevTokenPos = -1, nextTokenPos = value.length;
|
326
|
+
var tp;
|
327
|
+
for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
|
328
|
+
tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
|
329
|
+
if (tp > prevTokenPos) prevTokenPos = tp;
|
330
|
+
tp = value.indexOf(this.options.tokens[index], diff + offset);
|
331
|
+
if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
|
325
332
|
}
|
326
|
-
return
|
333
|
+
return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
|
327
334
|
}
|
328
|
-
}
|
335
|
+
});
|
336
|
+
|
337
|
+
Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
|
338
|
+
var boundary = Math.min(newS.length, oldS.length);
|
339
|
+
for (var index = 0; index < boundary; ++index)
|
340
|
+
if (newS[index] != oldS[index])
|
341
|
+
return index;
|
342
|
+
return boundary;
|
343
|
+
};
|
329
344
|
|
330
|
-
Ajax.Autocompleter = Class.create(
|
331
|
-
Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
|
345
|
+
Ajax.Autocompleter = Class.create(Autocompleter.Base, {
|
332
346
|
initialize: function(element, update, url, options) {
|
333
347
|
this.baseInitialize(element, update, options);
|
334
348
|
this.options.asynchronous = true;
|
@@ -338,7 +352,9 @@ Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.pro
|
|
338
352
|
},
|
339
353
|
|
340
354
|
getUpdatedChoices: function() {
|
341
|
-
|
355
|
+
this.startIndicator();
|
356
|
+
|
357
|
+
var entry = encodeURIComponent(this.options.paramName) + '=' +
|
342
358
|
encodeURIComponent(this.getToken());
|
343
359
|
|
344
360
|
this.options.parameters = this.options.callback ?
|
@@ -346,14 +362,13 @@ Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.pro
|
|
346
362
|
|
347
363
|
if(this.options.defaultParams)
|
348
364
|
this.options.parameters += '&' + this.options.defaultParams;
|
349
|
-
|
365
|
+
|
350
366
|
new Ajax.Request(this.url, this.options);
|
351
367
|
},
|
352
368
|
|
353
369
|
onComplete: function(request) {
|
354
370
|
this.updateChoices(request.responseText);
|
355
371
|
}
|
356
|
-
|
357
372
|
});
|
358
373
|
|
359
374
|
// The local array autocompleter. Used when you'd prefer to
|
@@ -391,8 +406,7 @@ Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.pro
|
|
391
406
|
// In that case, the other options above will not apply unless
|
392
407
|
// you support them.
|
393
408
|
|
394
|
-
Autocompleter.Local = Class.create(
|
395
|
-
Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
|
409
|
+
Autocompleter.Local = Class.create(Autocompleter.Base, {
|
396
410
|
initialize: function(element, update, array, options) {
|
397
411
|
this.baseInitialize(element, update, options);
|
398
412
|
this.options.array = array;
|
@@ -448,13 +462,12 @@ Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
|
|
448
462
|
ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
|
449
463
|
return "<ul>" + ret.join('') + "</ul>";
|
450
464
|
}
|
451
|
-
}, options || {});
|
465
|
+
}, options || { });
|
452
466
|
}
|
453
467
|
});
|
454
468
|
|
455
|
-
// AJAX in-place editor
|
456
|
-
//
|
457
|
-
// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
|
469
|
+
// AJAX in-place editor and collection editor
|
470
|
+
// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
|
458
471
|
|
459
472
|
// Use this if you notice weird scrolling problems on some browsers,
|
460
473
|
// the DOM might be a bit confused when this gets called so do this
|
@@ -465,353 +478,472 @@ Field.scrollFreeActivate = function(field) {
|
|
465
478
|
}, 1);
|
466
479
|
}
|
467
480
|
|
468
|
-
Ajax.InPlaceEditor = Class.create(
|
469
|
-
Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
|
470
|
-
Ajax.InPlaceEditor.prototype = {
|
481
|
+
Ajax.InPlaceEditor = Class.create({
|
471
482
|
initialize: function(element, url, options) {
|
472
483
|
this.url = url;
|
473
|
-
this.element = $(element);
|
474
|
-
|
475
|
-
this.
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
clickToEditText: "Click to edit",
|
483
|
-
okText: "ok",
|
484
|
-
rows: 1,
|
485
|
-
onComplete: function(transport, element) {
|
486
|
-
new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
|
487
|
-
},
|
488
|
-
onFailure: function(transport) {
|
489
|
-
alert("Error communicating with the server: " + transport.responseText.stripTags());
|
490
|
-
},
|
491
|
-
callback: function(form) {
|
492
|
-
return Form.serialize(form);
|
493
|
-
},
|
494
|
-
handleLineBreaks: true,
|
495
|
-
loadingText: 'Loading...',
|
496
|
-
savingClassName: 'inplaceeditor-saving',
|
497
|
-
loadingClassName: 'inplaceeditor-loading',
|
498
|
-
formClassName: 'inplaceeditor-form',
|
499
|
-
highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
|
500
|
-
highlightendcolor: "#FFFFFF",
|
501
|
-
externalControl: null,
|
502
|
-
submitOnBlur: false,
|
503
|
-
ajaxOptions: {},
|
504
|
-
evalScripts: false
|
505
|
-
}, options || {});
|
506
|
-
|
507
|
-
if(!this.options.formId && this.element.id) {
|
508
|
-
this.options.formId = this.element.id + "-inplaceeditor";
|
509
|
-
if ($(this.options.formId)) {
|
510
|
-
// there's already a form with that name, don't specify an id
|
511
|
-
this.options.formId = null;
|
512
|
-
}
|
484
|
+
this.element = element = $(element);
|
485
|
+
this.prepareOptions();
|
486
|
+
this._controls = { };
|
487
|
+
arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
|
488
|
+
Object.extend(this.options, options || { });
|
489
|
+
if (!this.options.formId && this.element.id) {
|
490
|
+
this.options.formId = this.element.id + '-inplaceeditor';
|
491
|
+
if ($(this.options.formId))
|
492
|
+
this.options.formId = '';
|
513
493
|
}
|
514
|
-
|
515
|
-
if (this.options.externalControl) {
|
494
|
+
if (this.options.externalControl)
|
516
495
|
this.options.externalControl = $(this.options.externalControl);
|
517
|
-
|
518
|
-
|
519
|
-
this.
|
520
|
-
if (!this.originalBackground) {
|
521
|
-
this.originalBackground = "transparent";
|
522
|
-
}
|
523
|
-
|
496
|
+
if (!this.options.externalControl)
|
497
|
+
this.options.externalControlOnly = false;
|
498
|
+
this._originalBackground = this.element.getStyle('background-color') || 'transparent';
|
524
499
|
this.element.title = this.options.clickToEditText;
|
525
|
-
|
526
|
-
this.
|
527
|
-
this.
|
528
|
-
this.
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
this.
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
if (
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
this.createEditField();
|
563
|
-
|
564
|
-
if (this.options.textarea) {
|
565
|
-
var br = document.createElement("br");
|
566
|
-
this.form.appendChild(br);
|
567
|
-
}
|
568
|
-
|
569
|
-
if (this.options.okButton) {
|
570
|
-
okButton = document.createElement("input");
|
571
|
-
okButton.type = "submit";
|
572
|
-
okButton.value = this.options.okText;
|
573
|
-
okButton.className = 'editor_ok_button';
|
574
|
-
this.form.appendChild(okButton);
|
575
|
-
}
|
576
|
-
|
577
|
-
if (this.options.cancelLink) {
|
578
|
-
cancelLink = document.createElement("a");
|
579
|
-
cancelLink.href = "#";
|
580
|
-
cancelLink.appendChild(document.createTextNode(this.options.cancelText));
|
581
|
-
cancelLink.onclick = this.onclickCancel.bind(this);
|
582
|
-
cancelLink.className = 'editor_cancel';
|
583
|
-
this.form.appendChild(cancelLink);
|
500
|
+
this._boundCancelHandler = this.handleFormCancellation.bind(this);
|
501
|
+
this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
|
502
|
+
this._boundFailureHandler = this.handleAJAXFailure.bind(this);
|
503
|
+
this._boundSubmitHandler = this.handleFormSubmission.bind(this);
|
504
|
+
this._boundWrapperHandler = this.wrapUp.bind(this);
|
505
|
+
this.registerListeners();
|
506
|
+
},
|
507
|
+
checkForEscapeOrReturn: function(e) {
|
508
|
+
if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
|
509
|
+
if (Event.KEY_ESC == e.keyCode)
|
510
|
+
this.handleFormCancellation(e);
|
511
|
+
else if (Event.KEY_RETURN == e.keyCode)
|
512
|
+
this.handleFormSubmission(e);
|
513
|
+
},
|
514
|
+
createControl: function(mode, handler, extraClasses) {
|
515
|
+
var control = this.options[mode + 'Control'];
|
516
|
+
var text = this.options[mode + 'Text'];
|
517
|
+
if ('button' == control) {
|
518
|
+
var btn = document.createElement('input');
|
519
|
+
btn.type = 'submit';
|
520
|
+
btn.value = text;
|
521
|
+
btn.className = 'editor_' + mode + '_button';
|
522
|
+
if ('cancel' == mode)
|
523
|
+
btn.onclick = this._boundCancelHandler;
|
524
|
+
this._form.appendChild(btn);
|
525
|
+
this._controls[mode] = btn;
|
526
|
+
} else if ('link' == control) {
|
527
|
+
var link = document.createElement('a');
|
528
|
+
link.href = '#';
|
529
|
+
link.appendChild(document.createTextNode(text));
|
530
|
+
link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
|
531
|
+
link.className = 'editor_' + mode + '_link';
|
532
|
+
if (extraClasses)
|
533
|
+
link.className += ' ' + extraClasses;
|
534
|
+
this._form.appendChild(link);
|
535
|
+
this._controls[mode] = link;
|
584
536
|
}
|
585
537
|
},
|
586
|
-
hasHTMLLineBreaks: function(string) {
|
587
|
-
if (!this.options.handleLineBreaks) return false;
|
588
|
-
return string.match(/<br/i) || string.match(/<p>/i);
|
589
|
-
},
|
590
|
-
convertHTMLLineBreaks: function(string) {
|
591
|
-
return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
|
592
|
-
},
|
593
538
|
createEditField: function() {
|
594
|
-
var text;
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
}
|
600
|
-
|
601
|
-
var obj = this;
|
602
|
-
|
603
|
-
if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
|
604
|
-
this.options.textarea = false;
|
605
|
-
var textField = document.createElement("input");
|
606
|
-
textField.obj = this;
|
607
|
-
textField.type = "text";
|
608
|
-
textField.name = this.options.paramName;
|
609
|
-
textField.value = text;
|
610
|
-
textField.style.backgroundColor = this.options.highlightcolor;
|
611
|
-
textField.className = 'editor_field';
|
539
|
+
var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
|
540
|
+
var fld;
|
541
|
+
if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
|
542
|
+
fld = document.createElement('input');
|
543
|
+
fld.type = 'text';
|
612
544
|
var size = this.options.size || this.options.cols || 0;
|
613
|
-
if (
|
614
|
-
if (this.options.submitOnBlur)
|
615
|
-
textField.onblur = this.onSubmit.bind(this);
|
616
|
-
this.editField = textField;
|
545
|
+
if (0 < size) fld.size = size;
|
617
546
|
} else {
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
textArea.name = this.options.paramName;
|
622
|
-
textArea.value = this.convertHTMLLineBreaks(text);
|
623
|
-
textArea.rows = this.options.rows;
|
624
|
-
textArea.cols = this.options.cols || 40;
|
625
|
-
textArea.className = 'editor_field';
|
626
|
-
if (this.options.submitOnBlur)
|
627
|
-
textArea.onblur = this.onSubmit.bind(this);
|
628
|
-
this.editField = textArea;
|
547
|
+
fld = document.createElement('textarea');
|
548
|
+
fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
|
549
|
+
fld.cols = this.options.cols || 40;
|
629
550
|
}
|
630
|
-
|
631
|
-
|
551
|
+
fld.name = this.options.paramName;
|
552
|
+
fld.value = text; // No HTML breaks conversion anymore
|
553
|
+
fld.className = 'editor_field';
|
554
|
+
if (this.options.submitOnBlur)
|
555
|
+
fld.onblur = this._boundSubmitHandler;
|
556
|
+
this._controls.editor = fld;
|
557
|
+
if (this.options.loadTextURL)
|
632
558
|
this.loadExternalText();
|
633
|
-
|
634
|
-
|
559
|
+
this._form.appendChild(this._controls.editor);
|
560
|
+
},
|
561
|
+
createForm: function() {
|
562
|
+
var ipe = this;
|
563
|
+
function addText(mode, condition) {
|
564
|
+
var text = ipe.options['text' + mode + 'Controls'];
|
565
|
+
if (!text || condition === false) return;
|
566
|
+
ipe._form.appendChild(document.createTextNode(text));
|
567
|
+
};
|
568
|
+
this._form = $(document.createElement('form'));
|
569
|
+
this._form.id = this.options.formId;
|
570
|
+
this._form.addClassName(this.options.formClassName);
|
571
|
+
this._form.onsubmit = this._boundSubmitHandler;
|
572
|
+
this.createEditField();
|
573
|
+
if ('textarea' == this._controls.editor.tagName.toLowerCase())
|
574
|
+
this._form.appendChild(document.createElement('br'));
|
575
|
+
if (this.options.onFormCustomization)
|
576
|
+
this.options.onFormCustomization(this, this._form);
|
577
|
+
addText('Before', this.options.okControl || this.options.cancelControl);
|
578
|
+
this.createControl('ok', this._boundSubmitHandler);
|
579
|
+
addText('Between', this.options.okControl && this.options.cancelControl);
|
580
|
+
this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
|
581
|
+
addText('After', this.options.okControl || this.options.cancelControl);
|
582
|
+
},
|
583
|
+
destroy: function() {
|
584
|
+
if (this._oldInnerHTML)
|
585
|
+
this.element.innerHTML = this._oldInnerHTML;
|
586
|
+
this.leaveEditMode();
|
587
|
+
this.unregisterListeners();
|
588
|
+
},
|
589
|
+
enterEditMode: function(e) {
|
590
|
+
if (this._saving || this._editing) return;
|
591
|
+
this._editing = true;
|
592
|
+
this.triggerCallback('onEnterEditMode');
|
593
|
+
if (this.options.externalControl)
|
594
|
+
this.options.externalControl.hide();
|
595
|
+
this.element.hide();
|
596
|
+
this.createForm();
|
597
|
+
this.element.parentNode.insertBefore(this._form, this.element);
|
598
|
+
if (!this.options.loadTextURL)
|
599
|
+
this.postProcessEditField();
|
600
|
+
if (e) Event.stop(e);
|
601
|
+
},
|
602
|
+
enterHover: function(e) {
|
603
|
+
if (this.options.hoverClassName)
|
604
|
+
this.element.addClassName(this.options.hoverClassName);
|
605
|
+
if (this._saving) return;
|
606
|
+
this.triggerCallback('onEnterHover');
|
635
607
|
},
|
636
608
|
getText: function() {
|
637
609
|
return this.element.innerHTML;
|
638
610
|
},
|
639
|
-
|
640
|
-
|
641
|
-
this.
|
642
|
-
|
643
|
-
this.
|
644
|
-
Object.extend({
|
645
|
-
asynchronous: true,
|
646
|
-
onComplete: this.onLoadedExternalText.bind(this)
|
647
|
-
}, this.options.ajaxOptions)
|
648
|
-
);
|
649
|
-
},
|
650
|
-
onLoadedExternalText: function(transport) {
|
651
|
-
Element.removeClassName(this.form, this.options.loadingClassName);
|
652
|
-
this.editField.disabled = false;
|
653
|
-
this.editField.value = transport.responseText.stripTags();
|
654
|
-
Field.scrollFreeActivate(this.editField);
|
655
|
-
},
|
656
|
-
onclickCancel: function() {
|
657
|
-
this.onComplete();
|
658
|
-
this.leaveEditMode();
|
659
|
-
return false;
|
660
|
-
},
|
661
|
-
onFailure: function(transport) {
|
662
|
-
this.options.onFailure(transport);
|
663
|
-
if (this.oldInnerHTML) {
|
664
|
-
this.element.innerHTML = this.oldInnerHTML;
|
665
|
-
this.oldInnerHTML = null;
|
611
|
+
handleAJAXFailure: function(transport) {
|
612
|
+
this.triggerCallback('onFailure', transport);
|
613
|
+
if (this._oldInnerHTML) {
|
614
|
+
this.element.innerHTML = this._oldInnerHTML;
|
615
|
+
this._oldInnerHTML = null;
|
666
616
|
}
|
667
|
-
return false;
|
668
617
|
},
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
this.
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
this.
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
}, this.options.ajaxOptions));
|
698
|
-
}
|
699
|
-
// stop the event to avoid a page refresh in Safari
|
700
|
-
if (arguments.length > 1) {
|
701
|
-
Event.stop(arguments[0]);
|
618
|
+
handleFormCancellation: function(e) {
|
619
|
+
this.wrapUp();
|
620
|
+
if (e) Event.stop(e);
|
621
|
+
},
|
622
|
+
handleFormSubmission: function(e) {
|
623
|
+
var form = this._form;
|
624
|
+
var value = $F(this._controls.editor);
|
625
|
+
this.prepareSubmission();
|
626
|
+
var params = this.options.callback(form, value) || '';
|
627
|
+
if (Object.isString(params))
|
628
|
+
params = params.toQueryParams();
|
629
|
+
params.editorId = this.element.id;
|
630
|
+
if (this.options.htmlResponse) {
|
631
|
+
var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
|
632
|
+
Object.extend(options, {
|
633
|
+
parameters: params,
|
634
|
+
onComplete: this._boundWrapperHandler,
|
635
|
+
onFailure: this._boundFailureHandler
|
636
|
+
});
|
637
|
+
new Ajax.Updater({ success: this.element }, this.url, options);
|
638
|
+
} else {
|
639
|
+
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
|
640
|
+
Object.extend(options, {
|
641
|
+
parameters: params,
|
642
|
+
onComplete: this._boundWrapperHandler,
|
643
|
+
onFailure: this._boundFailureHandler
|
644
|
+
});
|
645
|
+
new Ajax.Request(this.url, options);
|
702
646
|
}
|
703
|
-
|
647
|
+
if (e) Event.stop(e);
|
648
|
+
},
|
649
|
+
leaveEditMode: function() {
|
650
|
+
this.element.removeClassName(this.options.savingClassName);
|
651
|
+
this.removeForm();
|
652
|
+
this.leaveHover();
|
653
|
+
this.element.style.backgroundColor = this._originalBackground;
|
654
|
+
this.element.show();
|
655
|
+
if (this.options.externalControl)
|
656
|
+
this.options.externalControl.show();
|
657
|
+
this._saving = false;
|
658
|
+
this._editing = false;
|
659
|
+
this._oldInnerHTML = null;
|
660
|
+
this.triggerCallback('onLeaveEditMode');
|
661
|
+
},
|
662
|
+
leaveHover: function(e) {
|
663
|
+
if (this.options.hoverClassName)
|
664
|
+
this.element.removeClassName(this.options.hoverClassName);
|
665
|
+
if (this._saving) return;
|
666
|
+
this.triggerCallback('onLeaveHover');
|
704
667
|
},
|
705
|
-
|
706
|
-
this.
|
668
|
+
loadExternalText: function() {
|
669
|
+
this._form.addClassName(this.options.loadingClassName);
|
670
|
+
this._controls.editor.disabled = true;
|
671
|
+
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
|
672
|
+
Object.extend(options, {
|
673
|
+
parameters: 'editorId=' + encodeURIComponent(this.element.id),
|
674
|
+
onComplete: Prototype.emptyFunction,
|
675
|
+
onSuccess: function(transport) {
|
676
|
+
this._form.removeClassName(this.options.loadingClassName);
|
677
|
+
var text = transport.responseText;
|
678
|
+
if (this.options.stripLoadedTextTags)
|
679
|
+
text = text.stripTags();
|
680
|
+
this._controls.editor.value = text;
|
681
|
+
this._controls.editor.disabled = false;
|
682
|
+
this.postProcessEditField();
|
683
|
+
}.bind(this),
|
684
|
+
onFailure: this._boundFailureHandler
|
685
|
+
});
|
686
|
+
new Ajax.Request(this.options.loadTextURL, options);
|
687
|
+
},
|
688
|
+
postProcessEditField: function() {
|
689
|
+
var fpc = this.options.fieldPostCreation;
|
690
|
+
if (fpc)
|
691
|
+
$(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
|
692
|
+
},
|
693
|
+
prepareOptions: function() {
|
694
|
+
this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
|
695
|
+
Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
|
696
|
+
[this._extraDefaultOptions].flatten().compact().each(function(defs) {
|
697
|
+
Object.extend(this.options, defs);
|
698
|
+
}.bind(this));
|
699
|
+
},
|
700
|
+
prepareSubmission: function() {
|
701
|
+
this._saving = true;
|
707
702
|
this.removeForm();
|
708
703
|
this.leaveHover();
|
709
704
|
this.showSaving();
|
710
705
|
},
|
706
|
+
registerListeners: function() {
|
707
|
+
this._listeners = { };
|
708
|
+
var listener;
|
709
|
+
$H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
|
710
|
+
listener = this[pair.value].bind(this);
|
711
|
+
this._listeners[pair.key] = listener;
|
712
|
+
if (!this.options.externalControlOnly)
|
713
|
+
this.element.observe(pair.key, listener);
|
714
|
+
if (this.options.externalControl)
|
715
|
+
this.options.externalControl.observe(pair.key, listener);
|
716
|
+
}.bind(this));
|
717
|
+
},
|
718
|
+
removeForm: function() {
|
719
|
+
if (!this._form) return;
|
720
|
+
this._form.remove();
|
721
|
+
this._form = null;
|
722
|
+
this._controls = { };
|
723
|
+
},
|
711
724
|
showSaving: function() {
|
712
|
-
this.
|
725
|
+
this._oldInnerHTML = this.element.innerHTML;
|
713
726
|
this.element.innerHTML = this.options.savingText;
|
714
|
-
|
715
|
-
this.element.style.backgroundColor = this.
|
716
|
-
|
727
|
+
this.element.addClassName(this.options.savingClassName);
|
728
|
+
this.element.style.backgroundColor = this._originalBackground;
|
729
|
+
this.element.show();
|
717
730
|
},
|
718
|
-
|
719
|
-
if(this.
|
720
|
-
|
721
|
-
this.form = null;
|
731
|
+
triggerCallback: function(cbName, arg) {
|
732
|
+
if ('function' == typeof this.options[cbName]) {
|
733
|
+
this.options[cbName](this, arg);
|
722
734
|
}
|
723
735
|
},
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
this.
|
729
|
-
|
730
|
-
|
736
|
+
unregisterListeners: function() {
|
737
|
+
$H(this._listeners).each(function(pair) {
|
738
|
+
if (!this.options.externalControlOnly)
|
739
|
+
this.element.stopObserving(pair.key, pair.value);
|
740
|
+
if (this.options.externalControl)
|
741
|
+
this.options.externalControl.stopObserving(pair.key, pair.value);
|
742
|
+
}.bind(this));
|
731
743
|
},
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
744
|
+
wrapUp: function(transport) {
|
745
|
+
this.leaveEditMode();
|
746
|
+
// Can't use triggerCallback due to backward compatibility: requires
|
747
|
+
// binding + direct element
|
748
|
+
this._boundComplete(transport, this.element);
|
749
|
+
}
|
750
|
+
});
|
751
|
+
|
752
|
+
Object.extend(Ajax.InPlaceEditor.prototype, {
|
753
|
+
dispose: Ajax.InPlaceEditor.prototype.destroy
|
754
|
+
});
|
755
|
+
|
756
|
+
Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
|
757
|
+
initialize: function($super, element, url, options) {
|
758
|
+
this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
|
759
|
+
$super(element, url, options);
|
760
|
+
},
|
761
|
+
|
762
|
+
createEditField: function() {
|
763
|
+
var list = document.createElement('select');
|
764
|
+
list.name = this.options.paramName;
|
765
|
+
list.size = 1;
|
766
|
+
this._controls.editor = list;
|
767
|
+
this._collection = this.options.collection || [];
|
768
|
+
if (this.options.loadCollectionURL)
|
769
|
+
this.loadCollection();
|
770
|
+
else
|
771
|
+
this.checkForExternalText();
|
772
|
+
this._form.appendChild(this._controls.editor);
|
773
|
+
},
|
774
|
+
|
775
|
+
loadCollection: function() {
|
776
|
+
this._form.addClassName(this.options.loadingClassName);
|
777
|
+
this.showLoadingText(this.options.loadingCollectionText);
|
778
|
+
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
|
779
|
+
Object.extend(options, {
|
780
|
+
parameters: 'editorId=' + encodeURIComponent(this.element.id),
|
781
|
+
onComplete: Prototype.emptyFunction,
|
782
|
+
onSuccess: function(transport) {
|
783
|
+
var js = transport.responseText.strip();
|
784
|
+
if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
|
785
|
+
throw 'Server returned an invalid collection representation.';
|
786
|
+
this._collection = eval(js);
|
787
|
+
this.checkForExternalText();
|
788
|
+
}.bind(this),
|
789
|
+
onFailure: this.onFailure
|
742
790
|
});
|
791
|
+
new Ajax.Request(this.options.loadCollectionURL, options);
|
743
792
|
},
|
744
|
-
|
745
|
-
|
746
|
-
this.
|
747
|
-
this.
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
793
|
+
|
794
|
+
showLoadingText: function(text) {
|
795
|
+
this._controls.editor.disabled = true;
|
796
|
+
var tempOption = this._controls.editor.firstChild;
|
797
|
+
if (!tempOption) {
|
798
|
+
tempOption = document.createElement('option');
|
799
|
+
tempOption.value = '';
|
800
|
+
this._controls.editor.appendChild(tempOption);
|
801
|
+
tempOption.selected = true;
|
752
802
|
}
|
753
|
-
|
754
|
-
this.saving = false;
|
755
|
-
this.oldInnerHTML = null;
|
756
|
-
this.onLeaveEditMode();
|
803
|
+
tempOption.update((text || '').stripScripts().stripTags());
|
757
804
|
},
|
758
|
-
|
759
|
-
|
760
|
-
this.
|
805
|
+
|
806
|
+
checkForExternalText: function() {
|
807
|
+
this._text = this.getText();
|
808
|
+
if (this.options.loadTextURL)
|
809
|
+
this.loadExternalText();
|
810
|
+
else
|
811
|
+
this.buildOptionList();
|
761
812
|
},
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
813
|
+
|
814
|
+
loadExternalText: function() {
|
815
|
+
this.showLoadingText(this.options.loadingText);
|
816
|
+
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
|
817
|
+
Object.extend(options, {
|
818
|
+
parameters: 'editorId=' + encodeURIComponent(this.element.id),
|
819
|
+
onComplete: Prototype.emptyFunction,
|
820
|
+
onSuccess: function(transport) {
|
821
|
+
this._text = transport.responseText.strip();
|
822
|
+
this.buildOptionList();
|
823
|
+
}.bind(this),
|
824
|
+
onFailure: this.onFailure
|
825
|
+
});
|
826
|
+
new Ajax.Request(this.options.loadTextURL, options);
|
827
|
+
},
|
828
|
+
|
829
|
+
buildOptionList: function() {
|
830
|
+
this._form.removeClassName(this.options.loadingClassName);
|
831
|
+
this._collection = this._collection.map(function(entry) {
|
832
|
+
return 2 === entry.length ? entry : [entry, entry].flatten();
|
833
|
+
});
|
834
|
+
var marker = ('value' in this.options) ? this.options.value : this._text;
|
835
|
+
var textFound = this._collection.any(function(entry) {
|
836
|
+
return entry[0] == marker;
|
837
|
+
}.bind(this));
|
838
|
+
this._controls.editor.update('');
|
839
|
+
var option;
|
840
|
+
this._collection.each(function(entry, index) {
|
841
|
+
option = document.createElement('option');
|
842
|
+
option.value = entry[0];
|
843
|
+
option.selected = textFound ? entry[0] == marker : 0 == index;
|
844
|
+
option.appendChild(document.createTextNode(entry[1]));
|
845
|
+
this._controls.editor.appendChild(option);
|
846
|
+
}.bind(this));
|
847
|
+
this._controls.editor.disabled = false;
|
848
|
+
Field.scrollFreeActivate(this._controls.editor);
|
777
849
|
}
|
778
|
-
};
|
850
|
+
});
|
779
851
|
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
}
|
852
|
+
//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
|
853
|
+
//**** This only exists for a while, in order to let ****
|
854
|
+
//**** users adapt to the new API. Read up on the new ****
|
855
|
+
//**** API and convert your code to it ASAP! ****
|
856
|
+
|
857
|
+
Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
|
858
|
+
if (!options) return;
|
859
|
+
function fallback(name, expr) {
|
860
|
+
if (name in options || expr === undefined) return;
|
861
|
+
options[name] = expr;
|
862
|
+
};
|
863
|
+
fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
|
864
|
+
options.cancelLink == options.cancelButton == false ? false : undefined)));
|
865
|
+
fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
|
866
|
+
options.okLink == options.okButton == false ? false : undefined)));
|
867
|
+
fallback('highlightColor', options.highlightcolor);
|
868
|
+
fallback('highlightEndColor', options.highlightendcolor);
|
869
|
+
};
|
799
870
|
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
871
|
+
Object.extend(Ajax.InPlaceEditor, {
|
872
|
+
DefaultOptions: {
|
873
|
+
ajaxOptions: { },
|
874
|
+
autoRows: 3, // Use when multi-line w/ rows == 1
|
875
|
+
cancelControl: 'link', // 'link'|'button'|false
|
876
|
+
cancelText: 'cancel',
|
877
|
+
clickToEditText: 'Click to edit',
|
878
|
+
externalControl: null, // id|elt
|
879
|
+
externalControlOnly: false,
|
880
|
+
fieldPostCreation: 'activate', // 'activate'|'focus'|false
|
881
|
+
formClassName: 'inplaceeditor-form',
|
882
|
+
formId: null, // id|elt
|
883
|
+
highlightColor: '#ffff99',
|
884
|
+
highlightEndColor: '#ffffff',
|
885
|
+
hoverClassName: '',
|
886
|
+
htmlResponse: true,
|
887
|
+
loadingClassName: 'inplaceeditor-loading',
|
888
|
+
loadingText: 'Loading...',
|
889
|
+
okControl: 'button', // 'link'|'button'|false
|
890
|
+
okText: 'ok',
|
891
|
+
paramName: 'value',
|
892
|
+
rows: 1, // If 1 and multi-line, uses autoRows
|
893
|
+
savingClassName: 'inplaceeditor-saving',
|
894
|
+
savingText: 'Saving...',
|
895
|
+
size: 0,
|
896
|
+
stripLoadedTextTags: false,
|
897
|
+
submitOnBlur: false,
|
898
|
+
textAfterControls: '',
|
899
|
+
textBeforeControls: '',
|
900
|
+
textBetweenControls: ''
|
901
|
+
},
|
902
|
+
DefaultCallbacks: {
|
903
|
+
callback: function(form) {
|
904
|
+
return Form.serialize(form);
|
905
|
+
},
|
906
|
+
onComplete: function(transport, element) {
|
907
|
+
// For backward compatibility, this one is bound to the IPE, and passes
|
908
|
+
// the element directly. It was too often customized, so we don't break it.
|
909
|
+
new Effect.Highlight(element, {
|
910
|
+
startcolor: this.options.highlightColor, keepBackgroundImage: true });
|
911
|
+
},
|
912
|
+
onEnterEditMode: null,
|
913
|
+
onEnterHover: function(ipe) {
|
914
|
+
ipe.element.style.backgroundColor = ipe.options.highlightColor;
|
915
|
+
if (ipe._effect)
|
916
|
+
ipe._effect.cancel();
|
917
|
+
},
|
918
|
+
onFailure: function(transport, ipe) {
|
919
|
+
alert('Error communication with the server: ' + transport.responseText.stripTags());
|
920
|
+
},
|
921
|
+
onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
|
922
|
+
onLeaveEditMode: null,
|
923
|
+
onLeaveHover: function(ipe) {
|
924
|
+
ipe._effect = new Effect.Highlight(ipe.element, {
|
925
|
+
startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
|
926
|
+
restorecolor: ipe._originalBackground, keepBackgroundImage: true
|
927
|
+
});
|
805
928
|
}
|
929
|
+
},
|
930
|
+
Listeners: {
|
931
|
+
click: 'enterEditMode',
|
932
|
+
keydown: 'checkForEscapeOrReturn',
|
933
|
+
mouseover: 'enterHover',
|
934
|
+
mouseout: 'leaveHover'
|
806
935
|
}
|
807
936
|
});
|
808
937
|
|
938
|
+
Ajax.InPlaceCollectionEditor.DefaultOptions = {
|
939
|
+
loadingCollectionText: 'Loading options...'
|
940
|
+
};
|
941
|
+
|
809
942
|
// Delayed observer, like Form.Element.Observer,
|
810
943
|
// but waits for delay after last key input
|
811
944
|
// Ideal for live-search fields
|
812
945
|
|
813
|
-
Form.Element.DelayedObserver = Class.create(
|
814
|
-
Form.Element.DelayedObserver.prototype = {
|
946
|
+
Form.Element.DelayedObserver = Class.create({
|
815
947
|
initialize: function(element, delay, callback) {
|
816
948
|
this.delay = delay || 0.5;
|
817
949
|
this.element = $(element);
|
@@ -830,4 +962,4 @@ Form.Element.DelayedObserver.prototype = {
|
|
830
962
|
this.timer = null;
|
831
963
|
this.callback(this.element, $F(this.element));
|
832
964
|
}
|
833
|
-
};
|
965
|
+
});
|