best_in_place 0.1.9 → 0.2.0
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/.gitignore +9 -3
- data/.rspec +1 -0
- data/.travis.yml +11 -0
- data/Gemfile +2 -0
- data/README.md +3 -1
- data/Rakefile +6 -0
- data/best_in_place.gemspec +5 -0
- data/lib/best_in_place.rb +3 -40
- data/lib/best_in_place/helper.rb +52 -0
- data/lib/best_in_place/test_helpers.rb +26 -0
- data/lib/best_in_place/version.rb +2 -2
- data/public/javascripts/best_in_place.js +148 -125
- data/spec/helpers/best_in_place_spec.rb +229 -0
- data/spec/integration/double_init_spec.rb +32 -0
- data/spec/integration/js_spec.rb +165 -0
- data/spec/spec_helper.rb +23 -0
- data/test_app/Gemfile +1 -25
- data/test_app/Gemfile.lock +57 -50
- data/test_app/app/controllers/users_controller.rb +15 -1
- data/test_app/app/views/users/double_init.html.erb +65 -0
- data/test_app/app/views/users/show.html.erb +15 -15
- data/test_app/config/environments/development.rb +0 -1
- data/test_app/config/initializers/countries.rb +1 -1
- data/test_app/config/routes.rb +6 -56
- data/test_app/db/schema.rb +1 -0
- data/test_app/db/seeds.rb +1 -0
- data/test_app/public/javascripts/application.js +2 -2
- data/test_app/public/javascripts/best_in_place.js +146 -125
- data/test_app/public/stylesheets/style.css +17 -17
- metadata +79 -56
- data/Gemfile.lock +0 -78
- data/test_app/.gitignore +0 -4
- data/test_app/public/javascripts/jquery.rest_in_place.js +0 -254
data/Gemfile.lock
DELETED
@@ -1,78 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
best_in_place (0.1.8)
|
5
|
-
rails (~> 3.0.0)
|
6
|
-
|
7
|
-
GEM
|
8
|
-
remote: http://rubygems.org/
|
9
|
-
specs:
|
10
|
-
abstract (1.0.0)
|
11
|
-
actionmailer (3.0.3)
|
12
|
-
actionpack (= 3.0.3)
|
13
|
-
mail (~> 2.2.9)
|
14
|
-
actionpack (3.0.3)
|
15
|
-
activemodel (= 3.0.3)
|
16
|
-
activesupport (= 3.0.3)
|
17
|
-
builder (~> 2.1.2)
|
18
|
-
erubis (~> 2.6.6)
|
19
|
-
i18n (~> 0.4)
|
20
|
-
rack (~> 1.2.1)
|
21
|
-
rack-mount (~> 0.6.13)
|
22
|
-
rack-test (~> 0.5.6)
|
23
|
-
tzinfo (~> 0.3.23)
|
24
|
-
activemodel (3.0.3)
|
25
|
-
activesupport (= 3.0.3)
|
26
|
-
builder (~> 2.1.2)
|
27
|
-
i18n (~> 0.4)
|
28
|
-
activerecord (3.0.3)
|
29
|
-
activemodel (= 3.0.3)
|
30
|
-
activesupport (= 3.0.3)
|
31
|
-
arel (~> 2.0.2)
|
32
|
-
tzinfo (~> 0.3.23)
|
33
|
-
activeresource (3.0.3)
|
34
|
-
activemodel (= 3.0.3)
|
35
|
-
activesupport (= 3.0.3)
|
36
|
-
activesupport (3.0.3)
|
37
|
-
arel (2.0.7)
|
38
|
-
builder (2.1.2)
|
39
|
-
erubis (2.6.6)
|
40
|
-
abstract (>= 1.0.0)
|
41
|
-
i18n (0.5.0)
|
42
|
-
mail (2.2.14)
|
43
|
-
activesupport (>= 2.3.6)
|
44
|
-
i18n (>= 0.4.0)
|
45
|
-
mime-types (~> 1.16)
|
46
|
-
treetop (~> 1.4.8)
|
47
|
-
mime-types (1.16)
|
48
|
-
polyglot (0.3.1)
|
49
|
-
rack (1.2.1)
|
50
|
-
rack-mount (0.6.13)
|
51
|
-
rack (>= 1.0.0)
|
52
|
-
rack-test (0.5.7)
|
53
|
-
rack (>= 1.0)
|
54
|
-
rails (3.0.3)
|
55
|
-
actionmailer (= 3.0.3)
|
56
|
-
actionpack (= 3.0.3)
|
57
|
-
activerecord (= 3.0.3)
|
58
|
-
activeresource (= 3.0.3)
|
59
|
-
activesupport (= 3.0.3)
|
60
|
-
bundler (~> 1.0)
|
61
|
-
railties (= 3.0.3)
|
62
|
-
railties (3.0.3)
|
63
|
-
actionpack (= 3.0.3)
|
64
|
-
activesupport (= 3.0.3)
|
65
|
-
rake (>= 0.8.7)
|
66
|
-
thor (~> 0.14.4)
|
67
|
-
rake (0.8.7)
|
68
|
-
thor (0.14.6)
|
69
|
-
treetop (1.4.9)
|
70
|
-
polyglot (>= 0.3.1)
|
71
|
-
tzinfo (0.3.24)
|
72
|
-
|
73
|
-
PLATFORMS
|
74
|
-
ruby
|
75
|
-
|
76
|
-
DEPENDENCIES
|
77
|
-
best_in_place!
|
78
|
-
rails (~> 3.0.0)
|
data/test_app/.gitignore
DELETED
@@ -1,254 +0,0 @@
|
|
1
|
-
function RestInPlaceEditor(e) {
|
2
|
-
this.element = jQuery(e);
|
3
|
-
this.initOptions();
|
4
|
-
this.bindForm();
|
5
|
-
this.activator.bind('click', {editor: this}, this.clickHandler);
|
6
|
-
}
|
7
|
-
|
8
|
-
RestInPlaceEditor.prototype = {
|
9
|
-
// Public Interface Functions //////////////////////////////////////////////
|
10
|
-
|
11
|
-
activate : function() {
|
12
|
-
var elem = this.element.html();
|
13
|
-
this.oldValue = elem;
|
14
|
-
this.activator.unbind("click", this.clickHandler);
|
15
|
-
this.activateForm();
|
16
|
-
},
|
17
|
-
|
18
|
-
abort : function() {
|
19
|
-
this.element.html(this.oldValue).bind('click', {editor: this}, this.clickHandler);
|
20
|
-
},
|
21
|
-
|
22
|
-
update : function() {
|
23
|
-
var editor = this;
|
24
|
-
editor.ajax({
|
25
|
-
"type" : "post",
|
26
|
-
"dataType" : "text",
|
27
|
-
"data" : editor.requestData(),
|
28
|
-
"success" : function(data){ editor.loadSuccessCallback(data) },
|
29
|
-
"error" : function(request, error){ editor.loadErrorCallback(request, error) }
|
30
|
-
});
|
31
|
-
editor.element.html(this.getValue());
|
32
|
-
},
|
33
|
-
|
34
|
-
activateForm : function() {
|
35
|
-
alert("The form was not properly initialized. activateForm is unbound");
|
36
|
-
},
|
37
|
-
|
38
|
-
// Helper Functions ////////////////////////////////////////////////////////
|
39
|
-
|
40
|
-
initOptions : function() {
|
41
|
-
// Try parent supplied info
|
42
|
-
var self = this;
|
43
|
-
self.element.parents().each(function(){
|
44
|
-
self.url = self.url || jQuery(this).attr("data-url");
|
45
|
-
self.selectValues = self.selectValues || jQuery(this).attr("data-selectValues");
|
46
|
-
self.formType = self.formType || jQuery(this).attr("data-formType");
|
47
|
-
self.objectName = self.objectName || jQuery(this).attr("data-object");
|
48
|
-
self.attributeName = self.attributeName || jQuery(this).attr("data-attribute");
|
49
|
-
});
|
50
|
-
// Try Rails-id based if parents did not explicitly supply something
|
51
|
-
self.element.parents().each(function(){
|
52
|
-
var res;
|
53
|
-
if (res = this.id.match(/^(\w+)_(\d+)$/i)) {
|
54
|
-
self.objectName = self.objectName || res[1];
|
55
|
-
}
|
56
|
-
});
|
57
|
-
|
58
|
-
var ft = (self.selectValues != null ? "select" : "input");
|
59
|
-
|
60
|
-
// Load own attributes (overrides all others)
|
61
|
-
self.url = self.element.attr("data-url") || self.url || document.location.pathname;
|
62
|
-
self.selectValues = self.element.attr("data-selectValues") || self.selectValues;
|
63
|
-
self.formType = self.element.attr("data-formType") || self.formtype || ft
|
64
|
-
self.objectName = self.element.attr("data-object") || self.objectName;
|
65
|
-
self.attributeName = self.element.attr("data-attribute") || self.attributeName;
|
66
|
-
self.activator = self.element.attr("data-activator") || self.element;
|
67
|
-
|
68
|
-
if (self.formType == "select" && self.selectValues != null)
|
69
|
-
{
|
70
|
-
var type = typeof self.selectValues;
|
71
|
-
if (type == "string" && self.selectValues != "")
|
72
|
-
{
|
73
|
-
$.get(self.selectValues, function(data) {
|
74
|
-
self.values = data;
|
75
|
-
});
|
76
|
-
}
|
77
|
-
else if (type == "object")// Values are given via javascript data()
|
78
|
-
{
|
79
|
-
self.values = self.selectValues;
|
80
|
-
}
|
81
|
-
}
|
82
|
-
},
|
83
|
-
|
84
|
-
bindForm : function() {
|
85
|
-
this.activateForm = RestInPlaceEditor.forms[this.formType].activateForm;
|
86
|
-
this.getValue = RestInPlaceEditor.forms[this.formType].getValue;
|
87
|
-
},
|
88
|
-
|
89
|
-
getValue : function() {
|
90
|
-
alert("The form was not properly initialized. getValue is unbound");
|
91
|
-
},
|
92
|
-
|
93
|
-
// Trim and Strips HTML from text
|
94
|
-
sanitize : function(s) {
|
95
|
-
var tmp = document.createElement("DIV");
|
96
|
-
tmp.innerHTML = s;
|
97
|
-
return jQuery.trim(tmp.textContent||tmp.innerText);
|
98
|
-
},
|
99
|
-
|
100
|
-
/* Generate the data sent in the POST request */
|
101
|
-
requestData : function() {
|
102
|
-
//jq14: data as JS object, not string.
|
103
|
-
var data = "_method=put";
|
104
|
-
data += "&"+this.objectName+'['+this.attributeName+']='+encodeURIComponent(this.getValue());
|
105
|
-
if (window.rails_authenticity_token) {
|
106
|
-
data += "&authenticity_token="+encodeURIComponent(window.rails_authenticity_token);
|
107
|
-
}
|
108
|
-
return data;
|
109
|
-
},
|
110
|
-
|
111
|
-
ajax : function(options) {
|
112
|
-
options.url = this.url;
|
113
|
-
options.beforeSend = function(xhr){ xhr.setRequestHeader("Accept", "application/json"); };
|
114
|
-
try { var ajaxRequest = jQuery.ajax(options) }
|
115
|
-
catch(e) { alert("error"); }
|
116
|
-
return ajaxRequest;
|
117
|
-
},
|
118
|
-
|
119
|
-
// Handlers ////////////////////////////////////////////////////////////////
|
120
|
-
|
121
|
-
loadSuccessCallback : function(data) {
|
122
|
-
//jq14: data as JS object, not string.
|
123
|
-
if (jQuery.fn.jquery < "1.4") data = eval('(' + data + ')' );
|
124
|
-
this.element.html(data[this.objectName]);
|
125
|
-
|
126
|
-
// Binding back after being clicked
|
127
|
-
this.element.bind('click', {editor: this}, this.clickHandler);
|
128
|
-
},
|
129
|
-
|
130
|
-
loadErrorCallback : function(request, error) {
|
131
|
-
this.element.html(this.oldValue);
|
132
|
-
|
133
|
-
// Display all error messages from server side validation
|
134
|
-
$.each(jQuery.parseJSON(request.responseText), function(index, value) {
|
135
|
-
var container = $("<span class='flash-error'></span>").html(index + ': ' + value);
|
136
|
-
container.purr();
|
137
|
-
});
|
138
|
-
|
139
|
-
// Binding back after being clicked
|
140
|
-
this.element.bind('click', {editor: this}, this.clickHandler);
|
141
|
-
},
|
142
|
-
|
143
|
-
clickHandler : function(event) {
|
144
|
-
event.data.editor.activate();
|
145
|
-
}
|
146
|
-
}
|
147
|
-
|
148
|
-
|
149
|
-
RestInPlaceEditor.forms = {
|
150
|
-
"input" : {
|
151
|
-
/* is bound to the editor and called to replace the element's content with a form for editing data */
|
152
|
-
activateForm : function() {
|
153
|
-
var form = '<form class="form_in_place" action="javascript:void(0)" style="display:inline;"><input type="text" value="' + this.sanitize(this.oldValue) + '"></form>'
|
154
|
-
this.element.html(form);
|
155
|
-
this.element.find('input')[0].select();
|
156
|
-
this.element.find("form")
|
157
|
-
.bind('submit', {editor: this}, RestInPlaceEditor.forms.input.submitHandler);
|
158
|
-
this.element.find("input")
|
159
|
-
.bind('blur', {editor: this}, RestInPlaceEditor.forms.input.inputBlurHandler);
|
160
|
-
},
|
161
|
-
|
162
|
-
getValue : function() {
|
163
|
-
return this.sanitize(this.element.find("input").val());
|
164
|
-
},
|
165
|
-
|
166
|
-
inputBlurHandler : function(event) {
|
167
|
-
event.data.editor.abort();
|
168
|
-
},
|
169
|
-
|
170
|
-
submitHandler : function(event) {
|
171
|
-
event.data.editor.update();
|
172
|
-
return false;
|
173
|
-
}
|
174
|
-
},
|
175
|
-
|
176
|
-
"select" : {
|
177
|
-
activateForm : function() {
|
178
|
-
var output = "<form action='javascript:void(0)' style='display:inline;'><select>";
|
179
|
-
var selected = "";
|
180
|
-
var oldValue = this.oldValue;
|
181
|
-
$.each(this.values, function(index, value) {
|
182
|
-
selected = (index == oldValue ? "selected='selected'" : "")
|
183
|
-
output += "<option value='" + index + "' " + selected + ">" + value + "</option>"
|
184
|
-
});
|
185
|
-
output += "</select></form>"
|
186
|
-
this.element.html(output);
|
187
|
-
this.element.find("select").bind('change', {editor: this}, RestInPlaceEditor.forms.select.blurHandler);
|
188
|
-
},
|
189
|
-
|
190
|
-
getValue : function() {
|
191
|
-
return this.sanitize(this.element.find("select").val());
|
192
|
-
},
|
193
|
-
|
194
|
-
blurHandler : function(event) {
|
195
|
-
event.data.editor.update();
|
196
|
-
}
|
197
|
-
},
|
198
|
-
|
199
|
-
"checkbox" : {
|
200
|
-
activateForm : function() {
|
201
|
-
var output = "<form action='javascript:void(0)' style='display:inline;'>";
|
202
|
-
checked = (this.oldValue ? "checked='checked'" : "");
|
203
|
-
output += "<input type='checkbox' " + checked + "/></form>"
|
204
|
-
this.element.html(output);
|
205
|
-
this.element.find("input").bind('change', {editor: this}, RestInPlaceEditor.forms.select.blurHandler);
|
206
|
-
},
|
207
|
-
|
208
|
-
getValue : function() {
|
209
|
-
return this.sanitize(this.element.find("input").val());
|
210
|
-
},
|
211
|
-
|
212
|
-
blurHandler : function(event) {
|
213
|
-
event.data.editor.update();
|
214
|
-
}
|
215
|
-
},
|
216
|
-
|
217
|
-
"textarea" : {
|
218
|
-
/* is bound to the editor and called to replace the element's content with a form for editing data */
|
219
|
-
activateForm : function() {
|
220
|
-
this.element.html('<form action="javascript:void(0)" style="display:inline;"><textarea>' + this.sanitize(this.oldValue) + '</textarea></form>');
|
221
|
-
this.element.find('textarea')[0].select();
|
222
|
-
this.element.find("textarea").bind('blur', {editor: this}, RestInPlaceEditor.forms.textarea.blurHandler);
|
223
|
-
},
|
224
|
-
|
225
|
-
getValue : function() {
|
226
|
-
return this.sanitize(this.element.find("textarea").val());
|
227
|
-
},
|
228
|
-
|
229
|
-
blurHandler : function(event) {
|
230
|
-
this.update();
|
231
|
-
}
|
232
|
-
|
233
|
-
}
|
234
|
-
}
|
235
|
-
|
236
|
-
jQuery.fn.rest_in_place = function(options) {
|
237
|
-
this.each(function(){
|
238
|
-
jQuery(this).data('restInPlaceEditor', new RestInPlaceEditor(this));
|
239
|
-
})
|
240
|
-
return this;
|
241
|
-
}
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
// TODO
|
247
|
-
// ====
|
248
|
-
// - Sanitize HTML + Trim spaces √
|
249
|
-
// - Server Side Validation and exception catching √
|
250
|
-
// - Populate select fields with collections √
|
251
|
-
// - Checkbox
|
252
|
-
// - Client Side Validation
|
253
|
-
// - Accepts given click handlers
|
254
|
-
// - Accepts handler to activate all rest_in_place fields at once
|