best_in_place 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -25,6 +25,7 @@ The editor works by PUTting the updated value to the server and GETting the upda
25
25
  - Sanitize HTML and trim spaces of user's input on user's choice
26
26
  - Displays server-side **validation** errors
27
27
  - Allows external activator
28
+ - Allows optional, configurable OK and Cancel buttons for inputs and textareas
28
29
  - ESC key destroys changes (requires user confirmation)
29
30
  - Autogrowing textarea
30
31
  - Helper for generating the best_in_place field only if a condition is satisfied
@@ -51,11 +52,14 @@ Options:
51
52
  - **:nil**: The nil param defines the content displayed in case no value is defined for that field. It can be something like "click me to edit".
52
53
  If not defined it will show *"-"*.
53
54
  - **:activator**: Is the DOM object that can activate the field. If not defined the user will making editable by clicking on it.
55
+ - **:ok_button**: (Inputs and textareas only) If set to a string, then an OK button will be shown with the string as its label, replacing save on blur.
56
+ - **:cancel_button**: (Inputs and textareas only) If set to a string, then a Cancel button will be shown with the string as its label.
54
57
  - **:sanitize**: True by default. If set to false the input/textarea will accept html tags.
55
58
  - **:html_attrs**: Hash of html arguments, such as maxlength, default-value etc.
56
59
  - **:inner_class**: Class that is set to the rendered form.
57
60
  - **:display_as**: A model method which will be called in order to display
58
61
  this field.
62
+ - **:object_name**: Used for overriding the default params key used for the object (the data-object attribute). Useful for e.g. STI scenarios where best_in_place should post to a common controller for different models.
59
63
 
60
64
  ###best_in_place_if
61
65
  **best_in_place_if condition, object, field, OPTIONS**
@@ -97,6 +101,8 @@ Examples (code in the views):
97
101
 
98
102
  <%= best_in_place @user, :description, :type => :textarea %>
99
103
 
104
+ <%= best_in_place @user, :favorite_books, :type => :textarea, :ok_button => 'Save', :cancel_button => 'Cancel' %>
105
+
100
106
  ### Select
101
107
 
102
108
  <%= best_in_place @user, :country, :type => :select, :collection => [[1, "Spain"], [2, "Italy"], [3, "Germany"], [4, "France"]] %>
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
21
 
22
- s.add_dependency "rails", "~> 3.1.0"
22
+ s.add_dependency "rails", "~> 3.1"
23
23
  s.add_dependency "jquery-rails"
24
24
 
25
25
  s.add_development_dependency "rspec-rails", "~> 2.7.0"
@@ -55,6 +55,12 @@ BestInPlaceEditor.prototype = {
55
55
  $(this.activator).bind('click', {editor: this}, this.clickHandler);
56
56
  },
57
57
 
58
+ abortIfConfirm : function () {
59
+ if (confirm("Are you sure you want to discard your changes?")) {
60
+ this.abort();
61
+ }
62
+ },
63
+
58
64
  update : function() {
59
65
  var editor = this;
60
66
  if (this.formType in {"input":1, "textarea":1} && this.getValue() == this.oldValue)
@@ -101,6 +107,9 @@ BestInPlaceEditor.prototype = {
101
107
  self.formType = self.formType || jQuery(this).attr("data-type");
102
108
  self.objectName = self.objectName || jQuery(this).attr("data-object");
103
109
  self.attributeName = self.attributeName || jQuery(this).attr("data-attribute");
110
+ self.activator = self.activator || jQuery(this).attr("data-activator");
111
+ self.okButton = self.okButton || jQuery(this).attr("data-ok-button");
112
+ self.cancelButton = self.cancelButton || jQuery(this).attr("data-cancel-button");
104
113
  self.nil = self.nil || jQuery(this).attr("data-nil");
105
114
  self.inner_class = self.inner_class || jQuery(this).attr("data-inner-class");
106
115
  self.html_attrs = self.html_attrs || jQuery(this).attr("data-html-attrs");
@@ -116,15 +125,17 @@ BestInPlaceEditor.prototype = {
116
125
  });
117
126
 
118
127
  // Load own attributes (overrides all others)
119
- self.url = self.element.attr("data-url") || self.url || document.location.pathname;
120
- self.collection = self.element.attr("data-collection") || self.collection;
121
- self.formType = self.element.attr("data-type") || self.formtype || "input";
122
- self.objectName = self.element.attr("data-object") || self.objectName;
123
- self.attributeName = self.element.attr("data-attribute") || self.attributeName;
124
- self.activator = self.element.attr("data-activator") || self.element;
125
- self.nil = self.element.attr("data-nil") || self.nil || "-";
126
- self.inner_class = self.element.attr("data-inner-class") || self.inner_class || null;
127
- self.html_attrs = self.element.attr("data-html-attrs") || self.html_attrs;
128
+ self.url = self.element.attr("data-url") || self.url || document.location.pathname;
129
+ self.collection = self.element.attr("data-collection") || self.collection;
130
+ self.formType = self.element.attr("data-type") || self.formtype || "input";
131
+ self.objectName = self.element.attr("data-object") || self.objectName;
132
+ self.attributeName = self.element.attr("data-attribute") || self.attributeName;
133
+ self.activator = self.element.attr("data-activator") || self.element;
134
+ self.okButton = self.element.attr("data-ok-button") || self.okButton;
135
+ self.cancelButton = self.element.attr("data-cancel-button") || self.cancelButton;
136
+ self.nil = self.element.attr("data-nil") || self.nil || "-";
137
+ self.inner_class = self.element.attr("data-inner-class") || self.inner_class || null;
138
+ self.html_attrs = self.element.attr("data-html-attrs") || self.html_attrs;
128
139
  self.original_content = self.element.attr("data-original-content") || self.original_content;
129
140
 
130
141
  if (!self.element.attr("data-sanitize")) {
@@ -165,7 +176,7 @@ BestInPlaceEditor.prototype = {
165
176
  tmp.innerHTML = s;
166
177
  s = tmp.textContent || tmp.innerText;
167
178
  }
168
- return jQuery.trim(s);
179
+ return jQuery.trim(s).replace(/"/g, '&quot;');
169
180
  },
170
181
 
171
182
  /* Generate the data sent in the POST request */
@@ -232,6 +243,11 @@ BestInPlaceEditor.prototype = {
232
243
  };
233
244
 
234
245
 
246
+ // Button cases:
247
+ // If no buttons, then blur saves, ESC cancels
248
+ // If just Cancel button, then blur saves, ESC or clicking Cancel cancels (careful of blur event!)
249
+ // If just OK button, then clicking OK saves (careful of blur event!), ESC or blur cancels
250
+ // If both buttons, then clicking OK saves, ESC or clicking Cancel or blur cancels
235
251
  BestInPlaceEditor.forms = {
236
252
  "input" : {
237
253
  activateForm : function() {
@@ -240,27 +256,65 @@ BestInPlaceEditor.forms = {
240
256
  if (this.inner_class != null) {
241
257
  output += ' class="' + this.inner_class + '"';
242
258
  }
243
- output += '></form>'
259
+ output += '>';
260
+ if (this.okButton) {
261
+ output += '<input type="submit" value="' + this.okButton + '" />'
262
+ }
263
+ if (this.cancelButton) {
264
+ output += '<input type="button" value="' + this.cancelButton + '" />'
265
+ }
266
+ output += '</form>';
244
267
  this.element.html(output);
245
268
  this.setHtmlAttributes();
246
- this.element.find('input')[0].select();
269
+ this.element.find("input[type='text']")[0].select();
247
270
  this.element.find("form").bind('submit', {editor: this}, BestInPlaceEditor.forms.input.submitHandler);
248
- this.element.find("input").bind('blur', {editor: this}, BestInPlaceEditor.forms.input.inputBlurHandler);
249
- this.element.find("input").bind('keyup', {editor: this}, BestInPlaceEditor.forms.input.keyupHandler);
271
+ if (this.cancelButton) {
272
+ this.element.find("input[type='button']").bind('click', {editor: this}, BestInPlaceEditor.forms.input.cancelButtonHandler)
273
+ }
274
+ this.element.find("input[type='text']").bind('blur', {editor: this}, BestInPlaceEditor.forms.input.inputBlurHandler);
275
+ this.element.find("input[type='text']").bind('keyup', {editor: this}, BestInPlaceEditor.forms.input.keyupHandler);
276
+ this.blurTimer = null;
277
+ this.userClicked = false;
250
278
  },
251
279
 
252
- getValue : function() {
280
+ getValue : function() {
253
281
  return this.sanitizeValue(this.element.find("input").val());
254
282
  },
255
283
 
284
+ // When buttons are present, use a timer on the blur event to give precedence to clicks
256
285
  inputBlurHandler : function(event) {
257
- event.data.editor.update();
286
+ if (event.data.editor.okButton) {
287
+ event.data.editor.blurTimer = setTimeout(function () {
288
+ if (!event.data.editor.userClicked) {
289
+ event.data.editor.abort();
290
+ }
291
+ }, 500);
292
+ } else {
293
+ if (event.data.editor.cancelButton) {
294
+ event.data.editor.blurTimer = setTimeout(function () {
295
+ if (!event.data.editor.userClicked) {
296
+ event.data.editor.update();
297
+ }
298
+ }, 500);
299
+ } else {
300
+ event.data.editor.update();
301
+ }
302
+ }
258
303
  },
259
304
 
260
305
  submitHandler : function(event) {
306
+ event.data.editor.userClicked = true;
307
+ clearTimeout(event.data.editor.blurTimer);
261
308
  event.data.editor.update();
262
309
  },
263
310
 
311
+ cancelButtonHandler : function(event) {
312
+ event.data.editor.userClicked = true;
313
+ clearTimeout(event.data.editor.blurTimer);
314
+ event.data.editor.abort();
315
+ event.stopPropagation(); // Without this, click isn't handled
316
+ },
317
+
264
318
  keyupHandler : function(event) {
265
319
  if (event.keyCode == 27) {
266
320
  event.data.editor.abort();
@@ -361,7 +415,14 @@ BestInPlaceEditor.forms = {
361
415
  // construct the form
362
416
  var output = '<form action="javascript:void(0)" style="display:inline;"><textarea>';
363
417
  output += this.sanitizeValue(this.display_value);
364
- output += '</textarea></form>';
418
+ output += '</textarea>';
419
+ if (this.okButton) {
420
+ output += '<input type="submit" value="' + this.okButton + '" />'
421
+ }
422
+ if (this.cancelButton) {
423
+ output += '<input type="button" value="' + this.cancelButton + '" />'
424
+ }
425
+ output += '</form>';
365
426
  this.element.html(output);
366
427
  this.setHtmlAttributes();
367
428
 
@@ -370,27 +431,57 @@ BestInPlaceEditor.forms = {
370
431
  jQuery(this.element.find("textarea")[0]).elastic();
371
432
 
372
433
  this.element.find("textarea")[0].focus();
434
+ this.element.find("form").bind('submit', {editor: this}, BestInPlaceEditor.forms.textarea.submitHandler);
435
+ if (this.cancelButton) {
436
+ this.element.find("input[type='button']").bind('click', {editor: this}, BestInPlaceEditor.forms.textarea.cancelButtonHandler)
437
+ }
373
438
  this.element.find("textarea").bind('blur', {editor: this}, BestInPlaceEditor.forms.textarea.blurHandler);
374
439
  this.element.find("textarea").bind('keyup', {editor: this}, BestInPlaceEditor.forms.textarea.keyupHandler);
440
+ this.blurTimer = null;
441
+ this.userClicked = false;
375
442
  },
376
443
 
377
444
  getValue : function() {
378
445
  return this.sanitizeValue(this.element.find("textarea").val());
379
446
  },
380
447
 
448
+ // When buttons are present, use a timer on the blur event to give precedence to clicks
381
449
  blurHandler : function(event) {
450
+ if (event.data.editor.okButton) {
451
+ event.data.editor.blurTimer = setTimeout(function () {
452
+ if (!event.data.editor.userClicked) {
453
+ event.data.editor.abortIfConfirm();
454
+ }
455
+ }, 500);
456
+ } else {
457
+ if (event.data.editor.cancelButton) {
458
+ event.data.editor.blurTimer = setTimeout(function () {
459
+ if (!event.data.editor.userClicked) {
460
+ event.data.editor.update();
461
+ }
462
+ }, 500);
463
+ } else {
464
+ event.data.editor.update();
465
+ }
466
+ }
467
+ },
468
+
469
+ submitHandler : function(event) {
470
+ event.data.editor.userClicked = true;
471
+ clearTimeout(event.data.editor.blurTimer);
382
472
  event.data.editor.update();
383
473
  },
384
474
 
385
- keyupHandler : function(event) {
386
- if (event.keyCode == 27) {
387
- BestInPlaceEditor.forms.textarea.abort(event.data.editor);
388
- }
475
+ cancelButtonHandler : function(event) {
476
+ event.data.editor.userClicked = true;
477
+ clearTimeout(event.data.editor.blurTimer);
478
+ event.data.editor.abortIfConfirm();
479
+ event.stopPropagation(); // Without this, click isn't handled
389
480
  },
390
481
 
391
- abort : function(editor) {
392
- if (confirm("Are you sure you want to discard your changes?")) {
393
- editor.abort();
482
+ keyupHandler : function(event) {
483
+ if (event.keyCode == 27) {
484
+ event.data.editor.abortIfConfirm();
394
485
  }
395
486
  }
396
487
  }
data/lib/best_in_place.rb CHANGED
@@ -1,13 +1,12 @@
1
+ require "best_in_place/check_version"
1
2
  require "best_in_place/utils"
2
3
  require "best_in_place/helper"
3
4
  require "best_in_place/engine"
5
+ require "best_in_place/railtie"
4
6
  require "best_in_place/controller_extensions"
5
7
  require "best_in_place/display_methods"
8
+ require "action_view"
6
9
 
7
10
  module BestInPlace
8
11
  autoload :TestHelpers, "best_in_place/test_helpers"
9
-
10
- module ViewHelpers
11
- extend ActionView::Helpers
12
- end
13
12
  end
@@ -0,0 +1,8 @@
1
+ module BestInPlace
2
+ module CheckVersion
3
+ if Rails::VERSION::STRING < "3.1"
4
+ raise "This version of Best in Place is intended to be used for Rails >= 3.1. If you want to use it with Rails 3.0 or lower, please use the rails-3.0 branch."
5
+ return
6
+ end
7
+ end
8
+ end
@@ -33,10 +33,12 @@ module BestInPlace
33
33
  out = "<span class='best_in_place'"
34
34
  out << " id='#{BestInPlace::Utils.build_best_in_place_id(object, field)}'"
35
35
  out << " data-url='#{opts[:path].blank? ? url_for(object) : url_for(opts[:path])}'"
36
- out << " data-object='#{object.class.to_s.gsub("::", "_").underscore}'"
36
+ out << " data-object='#{opts[:object_name] || object.class.to_s.gsub("::", "_").underscore}'"
37
37
  out << " data-collection='#{collection.gsub(/'/, "&#39;")}'" unless collection.blank?
38
38
  out << " data-attribute='#{field}'"
39
39
  out << " data-activator='#{opts[:activator]}'" unless opts[:activator].blank?
40
+ out << " data-ok-button='#{opts[:ok_button]}'" unless opts[:ok_button].blank?
41
+ out << " data-cancel-button='#{opts[:cancel_button]}'" unless opts[:cancel_button].blank?
40
42
  out << " data-nil='#{opts[:nil]}'" unless opts[:nil].blank?
41
43
  out << " data-type='#{opts[:type]}'"
42
44
  out << " data-inner-class='#{opts[:inner_class]}'" if opts[:inner_class]
@@ -0,0 +1,7 @@
1
+ module BestInPlace
2
+ class Railtie < Rails::Railtie
3
+ initializer "set view helpers" do
4
+ BestInPlace::ViewHelpers = ActionView::Base.new
5
+ end
6
+ end
7
+ end
@@ -1,3 +1,3 @@
1
1
  module BestInPlace
2
- VERSION = "1.0.4"
2
+ VERSION = "1.0.5"
3
3
  end
@@ -62,6 +62,14 @@ describe BestInPlace::BestInPlaceHelpers do
62
62
  @span.attribute("data-activator").should be_nil
63
63
  end
64
64
 
65
+ it "should have no OK button text by default" do
66
+ @span.attribute("data-ok-button").should be_nil
67
+ end
68
+
69
+ it "should have no Cancel button text by default" do
70
+ @span.attribute("data-cancel-button").should be_nil
71
+ end
72
+
65
73
  it "should have no inner_class by default" do
66
74
  @span.attribute("data-inner-class").should be_nil
67
75
  end
@@ -130,6 +138,29 @@ describe BestInPlace::BestInPlaceHelpers do
130
138
  span.attribute("data-activator").value.should == "awesome"
131
139
  end
132
140
 
141
+ it "should have the given OK button text" do
142
+ out = helper.best_in_place @user, :name, :ok_button => "okay"
143
+ nk = Nokogiri::HTML.parse(out)
144
+ span = nk.css("span")
145
+ span.attribute("data-ok-button").value.should == "okay"
146
+ end
147
+
148
+ it "should have the given Cancel button text" do
149
+ out = helper.best_in_place @user, :name, :cancel_button => "nasty"
150
+ nk = Nokogiri::HTML.parse(out)
151
+ span = nk.css("span")
152
+ span.attribute("data-cancel-button").value.should == "nasty"
153
+ end
154
+
155
+ describe "object_name" do
156
+ it "should change the data-object value" do
157
+ out = helper.best_in_place @user, :name, :object_name => "my_user"
158
+ nk = Nokogiri::HTML.parse(out)
159
+ span = nk.css("span")
160
+ span.attribute("data-object").value.should == "my_user"
161
+ end
162
+ end
163
+
133
164
  describe "display_as" do
134
165
  it "should render the address with a custom renderer" do
135
166
  @user.should_receive(:address_format).and_return("the result")
@@ -161,7 +192,6 @@ describe BestInPlace::BestInPlaceHelpers do
161
192
  end
162
193
  end
163
194
 
164
-
165
195
  context "with a text field attribute" do
166
196
  before do
167
197
  nk = Nokogiri::HTML.parse(helper.best_in_place @user, :name)
@@ -12,7 +12,10 @@ describe "JS behaviour", :js => true do
12
12
  :receive_email => false,
13
13
  :birth_date => Time.now.utc,
14
14
  :description => "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus a lectus et lacus ultrices auctor. Morbi aliquet convallis tincidunt. Praesent enim libero, iaculis at commodo nec, fermentum a dolor. Quisque eget eros id felis lacinia faucibus feugiat et ante. Aenean justo nisi, aliquam vel egestas vel, porta in ligula. Etiam molestie, lacus eget tincidunt accumsan, elit justo rhoncus urna, nec pretium neque mi et lorem. Aliquam posuere, dolor quis pulvinar luctus, felis dolor tincidunt leo, eget pretium orci purus ac nibh. Ut enim sem, suscipit ac elementum vitae, sodales vel sem.",
15
- :money => 100
15
+ :money => 100,
16
+ :favorite_color => 'Red',
17
+ :favorite_books => "The City of Gold and Lead",
18
+ :description => "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus a lectus et lacus ultrices auctor. Morbi aliquet convallis tincidunt. Praesent enim libero, iaculis at commodo nec, fermentum a dolor. Quisque eget eros id felis lacinia faucibus feugiat et ante. Aenean justo nisi, aliquam vel egestas vel, porta in ligula. Etiam molestie, lacus eget tincidunt accumsan, elit justo rhoncus urna, nec pretium neque mi et lorem. Aliquam posuere, dolor quis pulvinar luctus, felis dolor tincidunt leo, eget pretium orci purus ac nibh. Ut enim sem, suscipit ac elementum vitae, sodales vel sem."
16
19
  end
17
20
 
18
21
  describe "nil option" do
@@ -178,6 +181,182 @@ describe "JS behaviour", :js => true do
178
181
  end
179
182
  end
180
183
 
184
+ it "should correctly use an OK submit button when so configured for an input" do
185
+ @user.save!
186
+ visit user_path(@user)
187
+
188
+ within("#favorite_color") do
189
+ page.should have_content('Red')
190
+ end
191
+
192
+ id = BestInPlace::Utils.build_best_in_place_id @user, :favorite_color
193
+ page.execute_script <<-JS
194
+ $("##{id}").click();
195
+ $("##{id} input[name='favorite_color']").val('Blue');
196
+ $("##{id} input[type='submit']").click();
197
+ JS
198
+
199
+ visit user_path(@user)
200
+ within("#favorite_color") do
201
+ page.should have_content('Blue')
202
+ end
203
+ end
204
+
205
+ it "should correctly use a Cancel button when so configured for an input" do
206
+ @user.save!
207
+ visit user_path(@user)
208
+
209
+ within("#favorite_color") do
210
+ page.should have_content('Red')
211
+ end
212
+
213
+ id = BestInPlace::Utils.build_best_in_place_id @user, :favorite_color
214
+ page.execute_script <<-JS
215
+ $("##{id}").click();
216
+ $("##{id} input[name='favorite_color']").val('Blue');
217
+ $("##{id} input[type='button']").click();
218
+ JS
219
+
220
+ visit user_path(@user)
221
+ within("#favorite_color") do
222
+ page.should have_content('Red')
223
+ end
224
+ end
225
+
226
+ it "should not submit input on blur if there's an OK button present" do
227
+ @user.save!
228
+ visit user_path(@user)
229
+
230
+ within("#favorite_color") do
231
+ page.should have_content('Red')
232
+ end
233
+
234
+ id = BestInPlace::Utils.build_best_in_place_id @user, :favorite_color
235
+ page.execute_script <<-JS
236
+ $("##{id}").click();
237
+ $("##{id} input[name='favorite_color']").val('Blue');
238
+ $("##{id} input[name='favorite_color']").blur();
239
+ JS
240
+ sleep 1 # Increase if browser is slow
241
+
242
+ visit user_path(@user)
243
+ within("#favorite_color") do
244
+ page.should have_content('Red')
245
+ end
246
+ end
247
+
248
+ it "should still submit input on blur if there's only a Cancel button present" do
249
+ @user.save!
250
+ visit user_path(@user, :suppress_ok_button => 1)
251
+
252
+ within("#favorite_color") do
253
+ page.should have_content('Red')
254
+ end
255
+
256
+ id = BestInPlace::Utils.build_best_in_place_id @user, :favorite_color
257
+ page.execute_script %{$("##{id}").click();}
258
+ page.should have_no_css("##{id} input[type='submit']")
259
+ page.execute_script <<-JS
260
+ $("##{id} input[name='favorite_color']").val('Blue');
261
+ $("##{id} input[name='favorite_color']").blur();
262
+ JS
263
+ sleep 1 # Increase if browser is slow
264
+
265
+ visit user_path(@user)
266
+ within("#favorite_color") do
267
+ page.should have_content('Blue')
268
+ end
269
+ end
270
+
271
+ it "should correctly use an OK submit button when so configured for a text area" do
272
+ @user.save!
273
+ visit user_path(@user)
274
+
275
+ within("#favorite_books") do
276
+ page.should have_content('The City of Gold and Lead')
277
+ end
278
+
279
+ id = BestInPlace::Utils.build_best_in_place_id @user, :favorite_books
280
+ page.execute_script <<-JS
281
+ $("##{id}").click();
282
+ $("##{id} textarea").val('1Q84');
283
+ $("##{id} input[type='submit']").click();
284
+ JS
285
+
286
+ visit user_path(@user)
287
+ within("#favorite_books") do
288
+ page.should have_content('1Q84')
289
+ end
290
+ end
291
+
292
+ it "should correctly use a Cancel button when so configured for a text area" do
293
+ @user.save!
294
+ visit user_path(@user)
295
+
296
+ within("#favorite_books") do
297
+ page.should have_content('The City of Gold and Lead')
298
+ end
299
+
300
+ id = BestInPlace::Utils.build_best_in_place_id @user, :favorite_books
301
+ page.execute_script <<-JS
302
+ $("##{id}").click();
303
+ $("##{id} textarea").val('1Q84');
304
+ $("##{id} input[type='button']").click();
305
+ JS
306
+ page.driver.browser.switch_to.alert.accept
307
+
308
+ visit user_path(@user)
309
+ within("#favorite_books") do
310
+ page.should have_content('The City of Gold and Lead')
311
+ end
312
+ end
313
+
314
+ it "should not submit text area on blur if there's an OK button present" do
315
+ @user.save!
316
+ visit user_path(@user)
317
+
318
+ within("#favorite_books") do
319
+ page.should have_content('The City of Gold and Lead')
320
+ end
321
+
322
+ id = BestInPlace::Utils.build_best_in_place_id @user, :favorite_books
323
+ page.execute_script <<-JS
324
+ $("##{id}").click();
325
+ $("##{id} textarea").val('1Q84');
326
+ $("##{id} textarea").blur();
327
+ JS
328
+ sleep 1 # Increase if browser is slow
329
+ page.driver.browser.switch_to.alert.accept
330
+
331
+ visit user_path(@user)
332
+ within("#favorite_books") do
333
+ page.should have_content('The City of Gold and Lead')
334
+ end
335
+ end
336
+
337
+ it "should still submit text area on blur if there's only a Cancel button present" do
338
+ @user.save!
339
+ visit user_path(@user, :suppress_ok_button => 1)
340
+
341
+ within("#favorite_books") do
342
+ page.should have_content('The City of Gold and Lead')
343
+ end
344
+
345
+ id = BestInPlace::Utils.build_best_in_place_id @user, :favorite_books
346
+ page.execute_script %{$("##{id}").click();}
347
+ page.should have_no_css("##{id} input[type='submit']")
348
+ page.execute_script <<-JS
349
+ $("##{id} textarea").val('1Q84');
350
+ $("##{id} textarea").blur();
351
+ JS
352
+ sleep 1 # Increase if browser is slow
353
+
354
+ visit user_path(@user)
355
+ within("#favorite_books") do
356
+ page.should have_content('1Q84')
357
+ end
358
+ end
359
+
181
360
  it "should show validation errors" do
182
361
  @user.save!
183
362
  visit user_path(@user)
@@ -377,5 +556,21 @@ describe "JS behaviour", :js => true do
377
556
  end
378
557
 
379
558
  end
380
- end
381
559
 
560
+ it "should display strings with quotes correctly in fields" do
561
+ @user.last_name = "A last name \"with double quotes\""
562
+ @user.save!
563
+
564
+ retry_on_timeout do
565
+ visit user_path(@user)
566
+
567
+ id = BestInPlace::Utils.build_best_in_place_id @user, :last_name
568
+ page.execute_script <<-JS
569
+ $("##{id}").click();
570
+ JS
571
+
572
+ text = page.find("##{id} input").value
573
+ text.should == "A last name \"with double quotes\""
574
+ end
575
+ end
576
+ end
@@ -27,4 +27,13 @@ describe "JS behaviour", :js => true do
27
27
  page.should have_content("A new description")
28
28
  end
29
29
  end
30
+
31
+ it "should be able to use a bip_text with :display_with option" do
32
+ @user.description = "I'm so awesome"
33
+ @user.save!
34
+ visit user_path(@user)
35
+ within("#dw_description") do
36
+ page.should have_content("I'm so awesome")
37
+ end
38
+ end
30
39
  end
@@ -17,7 +17,7 @@ table th {
17
17
  input {
18
18
  width: 80%;
19
19
  }
20
- input[type=submit] {
20
+ input[type=submit], input[type=button] {
21
21
  width: 5em;
22
22
  }
23
23
  input[type=checkbox] {
@@ -3,7 +3,7 @@ class User < ActiveRecord::Base
3
3
  :length => { :minimum => 2, :maximum => 24, :message => "has invalid length"},
4
4
  :presence => {:message => "can't be blank"}
5
5
  validates :last_name,
6
- :length => { :minimum => 2, :maximum => 24, :message => "has invalid length"},
6
+ :length => { :minimum => 2, :maximum => 50, :message => "has invalid length"},
7
7
  :presence => {:message => "can't be blank"}
8
8
  validates :address,
9
9
  :length => { :minimum => 5, :message => "too short length"},
@@ -52,12 +52,34 @@
52
52
  <%= best_in_place @user, :receive_email, :type => :checkbox, :collection => ["No thanks", "Yes of course"] %>
53
53
  </td>
54
54
  </tr>
55
+ <tr>
56
+ <td>Favorite color</td>
57
+ <td id="favorite_color">
58
+ <%- opts = { :ok_button => 'Do it!', :cancel_button => 'Nope' } %>
59
+ <%- opts.delete(:ok_button) if params[:suppress_ok_button] %>
60
+ <%= best_in_place @user, :favorite_color, opts %>
61
+ </td>
62
+ </tr>
63
+ <tr>
64
+ <td>Favorite books</td>
65
+ <td id="favorite_books">
66
+ <%- opts = { :type => :textarea, :ok_button => 'Save', :cancel_button => 'Cancel' } %>
67
+ <%- opts.delete(:ok_button) if params[:suppress_ok_button] %>
68
+ <%= best_in_place @user, :favorite_books, opts %>
69
+ </td>
70
+ </tr>
55
71
  <tr>
56
72
  <td>User description</td>
57
73
  <td id="description">
58
74
  <%= best_in_place @user, :description, :display_as => :markdown_desc, :type => :textarea, :sanitize => false %>
59
75
  </td>
60
76
  </tr>
77
+ <tr>
78
+ <td>Simple-formatted user description</td>
79
+ <td id="dw_description">
80
+ <%= best_in_place @user, :description, :display_with => :simple_format, :type => :textarea %>
81
+ </td>
82
+ </tr>
61
83
  <tr>
62
84
  <td>Money</td>
63
85
  <td id="money">
@@ -0,0 +1,5 @@
1
+ class AddFavoriteColorToUsers < ActiveRecord::Migration
2
+ def change
3
+ add_column :users, :favorite_color, :string
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddFavoriteBooksToUsers < ActiveRecord::Migration
2
+ def change
3
+ add_column :users, :favorite_books, :text
4
+ end
5
+ end
@@ -12,12 +12,11 @@
12
12
  # It's strongly recommended to check this file into your version control system.
13
13
 
14
14
  ActiveRecord::Schema.define(:version => 20111224181356) do
15
-
16
15
  create_table "users", :force => true do |t|
17
16
  t.string "name"
18
17
  t.string "last_name"
19
18
  t.string "address"
20
- t.string "email", :null => false
19
+ t.string "email", :null => false
21
20
  t.string "zip"
22
21
  t.string "country"
23
22
  t.datetime "created_at"
@@ -26,6 +25,7 @@ ActiveRecord::Schema.define(:version => 20111224181356) do
26
25
  t.text "description"
27
26
  t.datetime "birth_date"
28
27
  t.float "money"
28
+ t.string "favorite_color"
29
+ t.text "favorite_books"
29
30
  end
30
-
31
31
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: best_in_place
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,22 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-02 00:00:00.000000000 Z
12
+ date: 2012-01-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
16
- requirement: &69170780 !ruby/object:Gem::Requirement
16
+ requirement: &83572300 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 3.1.0
21
+ version: '3.1'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *69170780
24
+ version_requirements: *83572300
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: jquery-rails
27
- requirement: &69170510 !ruby/object:Gem::Requirement
27
+ requirement: &83572040 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *69170510
35
+ version_requirements: *83572040
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec-rails
38
- requirement: &69170120 !ruby/object:Gem::Requirement
38
+ requirement: &83571670 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 2.7.0
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *69170120
46
+ version_requirements: *83571670
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: nokogiri
49
- requirement: &69169800 !ruby/object:Gem::Requirement
49
+ requirement: &83571350 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 1.5.0
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *69169800
57
+ version_requirements: *83571350
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: capybara
60
- requirement: &69169540 !ruby/object:Gem::Requirement
60
+ requirement: &83571040 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,7 +65,7 @@ dependencies:
65
65
  version: 1.0.1
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *69169540
68
+ version_requirements: *83571040
69
69
  description: BestInPlace is a jQuery script and a Rails 3 helper that provide the
70
70
  method best_in_place to display any object field easily editable for the user by
71
71
  just clicking on it. It supports input data, text data, boolean data and custom
@@ -86,10 +86,12 @@ files:
86
86
  - lib/assets/javascripts/best_in_place.js
87
87
  - lib/assets/javascripts/jquery.purr.js
88
88
  - lib/best_in_place.rb
89
+ - lib/best_in_place/check_version.rb
89
90
  - lib/best_in_place/controller_extensions.rb
90
91
  - lib/best_in_place/display_methods.rb
91
92
  - lib/best_in_place/engine.rb
92
93
  - lib/best_in_place/helper.rb
94
+ - lib/best_in_place/railtie.rb
93
95
  - lib/best_in_place/test_helpers.rb
94
96
  - lib/best_in_place/utils.rb
95
97
  - lib/best_in_place/version.rb
@@ -154,6 +156,8 @@ files:
154
156
  - test_app/db/migrate/20101206205922_create_users.rb
155
157
  - test_app/db/migrate/20101212170114_add_receive_email_to_user.rb
156
158
  - test_app/db/migrate/20110115204441_add_description_to_user.rb
159
+ - test_app/db/migrate/20111210084202_add_favorite_color_to_users.rb
160
+ - test_app/db/migrate/20111210084251_add_favorite_books_to_users.rb
157
161
  - test_app/db/migrate/20111217215935_add_birth_date_to_users.rb
158
162
  - test_app/db/migrate/20111224181356_add_money_to_user.rb
159
163
  - test_app/db/schema.rb
@@ -188,7 +192,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
188
192
  version: '0'
189
193
  segments:
190
194
  - 0
191
- hash: -35281219
195
+ hash: -572262513
192
196
  required_rubygems_version: !ruby/object:Gem::Requirement
193
197
  none: false
194
198
  requirements:
@@ -197,7 +201,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
197
201
  version: '0'
198
202
  segments:
199
203
  - 0
200
- hash: -35281219
204
+ hash: -572262513
201
205
  requirements: []
202
206
  rubyforge_project: best_in_place
203
207
  rubygems_version: 1.8.10