best_in_place 0.1.8 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,3 +1,5 @@
1
1
  .DS_Store
2
2
  testapp/log
3
3
  testapp/config/database.yml
4
+ .bundle/*
5
+ pkg/*
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- best_in_place (0.1.4)
4
+ best_in_place (0.1.8)
5
5
  rails (~> 3.0.0)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -20,10 +20,11 @@ The editor works by PUTting the updated value to the server and GETting the upda
20
20
  - Compatible with **textarea**
21
21
  - Compatible with **select** dropdown with custom collections
22
22
  - Compatible with custom boolean values (same usage of **checkboxes**)
23
- - Sanitize HTML and trim spaces of user's input
23
+ - Sanitize HTML and trim spaces of user's input on user's choice
24
24
  - Displays server-side **validation** errors
25
25
  - Allows external activator
26
- - ESC key destroys changes
26
+ - ESC key destroys changes (requires user confirmation)
27
+ - Autogrowing textarea
27
28
 
28
29
  ---
29
30
 
@@ -122,7 +123,7 @@ When the user tries to introduce invalid data, the error messages defined in the
122
123
  It works by simply copying and loading the files from the folder **/public/javascripts** to your application and loading them in your layouts
123
124
  in the following order:
124
125
 
125
- - jquery-1.4.2.js
126
+ - jquery-1.4.4.js
126
127
  - jquery.purr.js
127
128
  - **best_in_place.js**
128
129
 
@@ -169,10 +170,10 @@ If the script is used with the Rails Gem no html tags will be allowed unless the
169
170
  fixing bug with objects inside namespaces, adding feature for passing an external activator handler as param. Adding feature
170
171
  of key ESCAPE for destroying changes before they are made permanent (in inputs and textarea).
171
172
  - v.0.1.6-0.1.7 Avoiding request when the input is not modified and allowing the user to not sanitize input data.
172
- - v.0.1.8 jslint compliant, sanitizing tags in the gem, getting right csrf params.
173
+ - v.0.1.8 jslint compliant, sanitizing tags in the gem, getting right csrf params, controlling size of textarea (elastic script, for autogrowing textarea)
173
174
 
174
175
  ##Authors, License and Stuff
175
176
 
176
- Code by [Bernat Farrero](http://bernatfarrero.com) (based on the [original project](http://github.com/janv/rest_in_place/) of Jan Varwig) and released under [MIT](http://www.opensource.org/licenses/mit-license.php).
177
+ Code by [Bernat Farrero](http://bernatfarrero.com) from [Itnig Web Services](http://itnig.net) (it was based on the [original project](http://github.com/janv/rest_in_place/) of Jan Varwig) and released under [MIT license](http://www.opensource.org/licenses/mit-license.php).
177
178
 
178
- Many thanks to the contributors: Specially [Roger Campos](http://github.com/rogercampos)
179
+ Many thanks to the contributors: [Roger Campos](http://github.com/rogercampos) and [Jack Senechal](https://github.com/jacksenechal)
data/lib/best_in_place.rb CHANGED
@@ -3,9 +3,8 @@ module BestInPlace
3
3
  def best_in_place(object, field, opts = {})
4
4
  opts[:type] ||= :input
5
5
  opts[:collection] ||= []
6
- opts[:nil] ||= "-"
7
6
  field = field.to_s
8
- value = object.send(field).blank? ? opts[:nil].to_s : object.send(field)
7
+ value = object.send(field).blank? ? "" : object.send(field)
9
8
  collection = nil
10
9
  if opts[:type] == :select && !opts[:collection].blank?
11
10
  v = object.send(field)
@@ -27,12 +26,13 @@ module BestInPlace
27
26
  out << " data-collection='#{collection}'" unless collection.blank?
28
27
  out << " data-attribute='#{field}'"
29
28
  out << " data-activator='#{opts[:activator]}'" unless opts[:activator].blank?
29
+ out << " data-nil='#{opts[:nil].to_s}'" unless opts[:nil].blank?
30
30
  out << " data-type='#{opts[:type].to_s}'"
31
31
  if !opts[:sanitize].nil? && !opts[:sanitize]
32
32
  out << " data-sanitize='false'>"
33
33
  out << sanitize(value.to_s, :tags => %w(b i u s a strong em p h1 h2 h3 h4 h5 ul li ol hr pre span img), :attributes => %w(id class))
34
34
  else
35
- out << ">#{sanitize(value, :tags => nil, :attributes => nil)}"
35
+ out << ">#{sanitize(value.to_s, :tags => nil, :attributes => nil)}"
36
36
  end
37
37
  out << "</span>"
38
38
  raw out
@@ -1,3 +1,3 @@
1
1
  module BestInPlace
2
- VERSION = "0.1.8"
2
+ VERSION = "0.1.9"
3
3
  end
@@ -23,6 +23,7 @@ function BestInPlaceEditor(e) {
23
23
  this.element = jQuery(e);
24
24
  this.initOptions();
25
25
  this.bindForm();
26
+ this.initNil();
26
27
  $(this.activator).bind('click', {editor: this}, this.clickHandler);
27
28
  }
28
29
 
@@ -30,14 +31,15 @@ BestInPlaceEditor.prototype = {
30
31
  // Public Interface Functions //////////////////////////////////////////////
31
32
 
32
33
  activate : function() {
33
- var elem = this.element.html();
34
+ var elem = this.isNil ? "" : this.element.html();
34
35
  this.oldValue = elem;
35
36
  $(this.activator).unbind("click", this.clickHandler);
36
37
  this.activateForm();
37
38
  },
38
39
 
39
40
  abort : function() {
40
- this.element.html(this.oldValue);
41
+ if (this.isNil) this.element.html(this.nil);
42
+ else this.element.html(this.oldValue);
41
43
  $(this.activator).bind('click', {editor: this}, this.clickHandler);
42
44
  },
43
45
 
@@ -45,10 +47,10 @@ BestInPlaceEditor.prototype = {
45
47
  var editor = this;
46
48
  if (this.formType in {"input":1, "textarea":1} && this.getValue() == this.oldValue)
47
49
  { // Avoid request if no change is made
48
- editor.element.html(this.getValue());
49
- $(this.activator).bind('click', {editor: this}, this.clickHandler);
50
+ this.abort();
50
51
  return true;
51
52
  }
53
+ this.isNil = false;
52
54
  editor.ajax({
53
55
  "type" : "post",
54
56
  "dataType" : "text",
@@ -86,6 +88,7 @@ BestInPlaceEditor.prototype = {
86
88
  self.formType = self.formType || jQuery(this).attr("data-type");
87
89
  self.objectName = self.objectName || jQuery(this).attr("data-object");
88
90
  self.attributeName = self.attributeName || jQuery(this).attr("data-attribute");
91
+ self.nil = self.nil || jQuery(this).attr("data-nil");
89
92
  });
90
93
 
91
94
  // Try Rails-id based if parents did not explicitly supply something
@@ -103,6 +106,7 @@ BestInPlaceEditor.prototype = {
103
106
  self.objectName = self.element.attr("data-object") || self.objectName;
104
107
  self.attributeName = self.element.attr("data-attribute") || self.attributeName;
105
108
  self.activator = self.element.attr("data-activator") || self.element;
109
+ self.nil = self.element.attr("data-nil") || self.nil || "-";
106
110
 
107
111
  if (!self.element.attr("data-sanitize")) {
108
112
  self.sanitize = true;
@@ -122,6 +126,14 @@ BestInPlaceEditor.prototype = {
122
126
  this.getValue = BestInPlaceEditor.forms[this.formType].getValue;
123
127
  },
124
128
 
129
+ initNil: function() {
130
+ if (this.element.html() == "")
131
+ {
132
+ this.isNil = true
133
+ this.element.html(this.nil)
134
+ }
135
+ },
136
+
125
137
  getValue : function() {
126
138
  alert("The form was not properly initialized. getValue is unbound");
127
139
  },
@@ -228,6 +240,9 @@ BestInPlaceEditor.forms = {
228
240
  output += "</select></form>";
229
241
  this.element.html(output);
230
242
  this.element.find("select").bind('change', {editor: this}, BestInPlaceEditor.forms.select.blurHandler);
243
+ this.element.find("select").bind('blur', {editor: this}, BestInPlaceEditor.forms.select.blurHandler);
244
+ this.element.find("select").bind('keyup', {editor: this}, BestInPlaceEditor.forms.select.keyupHandler);
245
+ this.element.find("select")[0].focus();
231
246
  },
232
247
 
233
248
  getValue : function() {
@@ -236,6 +251,10 @@ BestInPlaceEditor.forms = {
236
251
 
237
252
  blurHandler : function(event) {
238
253
  event.data.editor.update();
254
+ },
255
+
256
+ keyupHandler : function(event) {
257
+ if (event.keyCode == 27) event.data.editor.abort();
239
258
  }
240
259
  },
241
260
 
@@ -254,11 +273,21 @@ BestInPlaceEditor.forms = {
254
273
 
255
274
  "textarea" : {
256
275
  activateForm : function() {
276
+ // grab width and height of text
277
+ width = this.element.css('width');
278
+ height = this.element.css('height');
279
+
280
+ // construct the form
257
281
  var output = '<form action="javascript:void(0)" style="display:inline;"><textarea>';
258
282
  output += this.sanitizeValue(this.oldValue);
259
283
  output += '</textarea></form>';
260
284
  this.element.html(output);
261
- this.element.find('textarea')[0].select();
285
+
286
+ // set width and height of textarea
287
+ jQuery(this.element.find("textarea")[0]).css({ 'min-width': width, 'min-height': height });
288
+ jQuery(this.element.find("textarea")[0]).elastic();
289
+
290
+ this.element.find("textarea")[0].focus();
262
291
  this.element.find("textarea").bind('blur', {editor: this}, BestInPlaceEditor.forms.textarea.blurHandler);
263
292
  this.element.find("textarea").bind('keyup', {editor: this}, BestInPlaceEditor.forms.textarea.keyupHandler);
264
293
  },
@@ -273,7 +302,13 @@ BestInPlaceEditor.forms = {
273
302
 
274
303
  keyupHandler : function(event) {
275
304
  if (event.keyCode == 27) {
276
- event.data.editor.abort();
305
+ BestInPlaceEditor.forms.textarea.abort(event.data.editor);
306
+ }
307
+ },
308
+
309
+ abort : function(editor) {
310
+ if (confirm("Are you sure you want to discard your changes?")) {
311
+ editor.abort();
277
312
  }
278
313
  }
279
314
  }
@@ -284,4 +319,138 @@ jQuery.fn.best_in_place = function() {
284
319
  jQuery(this).data('bestInPlaceEditor', new BestInPlaceEditor(this));
285
320
  });
286
321
  return this;
287
- };
322
+ };
323
+
324
+
325
+
326
+ /**
327
+ * @name Elastic
328
+ * @descripton Elastic is Jquery plugin that grow and shrink your textareas automaticliy
329
+ * @version 1.6.5
330
+ * @requires Jquery 1.2.6+
331
+ *
332
+ * @author Jan Jarfalk
333
+ * @author-email jan.jarfalk@unwrongest.com
334
+ * @author-website http://www.unwrongest.com
335
+ *
336
+ * @licens MIT License - http://www.opensource.org/licenses/mit-license.php
337
+ */
338
+
339
+ (function(jQuery){
340
+ jQuery.fn.extend({
341
+ elastic: function() {
342
+ // We will create a div clone of the textarea
343
+ // by copying these attributes from the textarea to the div.
344
+ var mimics = [
345
+ 'paddingTop',
346
+ 'paddingRight',
347
+ 'paddingBottom',
348
+ 'paddingLeft',
349
+ 'fontSize',
350
+ 'lineHeight',
351
+ 'fontFamily',
352
+ 'width',
353
+ 'fontWeight'];
354
+
355
+ return this.each( function() {
356
+
357
+ // Elastic only works on textareas
358
+ if ( this.type != 'textarea' ) {
359
+ return false;
360
+ }
361
+
362
+ var $textarea = jQuery(this),
363
+ $twin = jQuery('<div />').css({'position': 'absolute','display':'none','word-wrap':'break-word'}),
364
+ lineHeight = parseInt($textarea.css('line-height'),10) || parseInt($textarea.css('font-size'),'10'),
365
+ minheight = parseInt($textarea.css('height'),10) || lineHeight*3,
366
+ maxheight = parseInt($textarea.css('max-height'),10) || Number.MAX_VALUE,
367
+ goalheight = 0,
368
+ i = 0;
369
+
370
+ // Opera returns max-height of -1 if not set
371
+ if (maxheight < 0) { maxheight = Number.MAX_VALUE; }
372
+
373
+ // Append the twin to the DOM
374
+ // We are going to meassure the height of this, not the textarea.
375
+ $twin.appendTo($textarea.parent());
376
+
377
+ // Copy the essential styles (mimics) from the textarea to the twin
378
+ var i = mimics.length;
379
+ while(i--){
380
+ $twin.css(mimics[i].toString(),$textarea.css(mimics[i].toString()));
381
+ }
382
+
383
+
384
+ // Sets a given height and overflow state on the textarea
385
+ function setHeightAndOverflow(height, overflow){
386
+ curratedHeight = Math.floor(parseInt(height,10));
387
+ if($textarea.height() != curratedHeight){
388
+ $textarea.css({'height': curratedHeight + 'px','overflow':overflow});
389
+
390
+ }
391
+ }
392
+
393
+
394
+ // This function will update the height of the textarea if necessary
395
+ function update() {
396
+
397
+ // Get curated content from the textarea.
398
+ var textareaContent = $textarea.val().replace(/&/g,'&amp;').replace(/ /g, '&nbsp;').replace(/<|>/g, '&gt;').replace(/\n/g, '<br />');
399
+
400
+ // Compare curated content with curated twin.
401
+ var twinContent = $twin.html().replace(/<br>/ig,'<br />');
402
+
403
+ if(textareaContent+'&nbsp;' != twinContent){
404
+
405
+ // Add an extra white space so new rows are added when you are at the end of a row.
406
+ $twin.html(textareaContent+'&nbsp;');
407
+
408
+ // Change textarea height if twin plus the height of one line differs more than 3 pixel from textarea height
409
+ if(Math.abs($twin.height() + lineHeight - $textarea.height()) > 3){
410
+
411
+ var goalheight = $twin.height()+lineHeight;
412
+ if(goalheight >= maxheight) {
413
+ setHeightAndOverflow(maxheight,'auto');
414
+ } else if(goalheight <= minheight) {
415
+ setHeightAndOverflow(minheight,'hidden');
416
+ } else {
417
+ setHeightAndOverflow(goalheight,'hidden');
418
+ }
419
+
420
+ }
421
+
422
+ }
423
+
424
+ }
425
+
426
+ // Hide scrollbars
427
+ $textarea.css({'overflow':'hidden'});
428
+
429
+ // Update textarea size on keyup, change, cut and paste
430
+ $textarea.bind('keyup change cut paste', function(){
431
+ update();
432
+ });
433
+
434
+ // Compact textarea on blur
435
+ // Lets animate this....
436
+ $textarea.bind('blur',function(){
437
+ if($twin.height() < maxheight){
438
+ if($twin.height() > minheight) {
439
+ $textarea.height($twin.height());
440
+ } else {
441
+ $textarea.height(minheight);
442
+ }
443
+ }
444
+ });
445
+
446
+ // And this line is to catch the browser paste event
447
+ $textarea.live('input paste',function(e){ setTimeout( update, 250); });
448
+
449
+ // Run update once when elastic is initialized
450
+ update();
451
+
452
+ });
453
+
454
+ }
455
+ });
456
+ })(jQuery);
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- best_in_place (0.1.7)
4
+ best_in_place (0.1.8)
5
5
  rails (~> 3.0.0)
6
6
 
7
7
  GEM
@@ -81,9 +81,4 @@ class UsersController < ApplicationController
81
81
  format.xml { head :ok }
82
82
  end
83
83
  end
84
-
85
- def get_countries
86
- @countries = { "spain", "Spain", "italy", "Italy", "germany", "Germany", "france", "France" }.to_json
87
- render :json => @countries
88
- end
89
84
  end
@@ -33,10 +33,14 @@
33
33
  </div>
34
34
  <div class="field">
35
35
  <%= f.label :country %><br />
36
- <select id="user_country" name="user[country]">
37
- <option value="spain">Spain</option>
38
- <option value="italy">Italy</option>
39
- </select>
36
+ <%=
37
+ f.select :country, COUNTRIES.map {|c|
38
+ v0 = c[0]
39
+ c[0] = c[1]
40
+ c[1] = v0
41
+ c
42
+ }
43
+ %>
40
44
  <div class="actions">
41
45
  <%= f.submit %>
42
46
  </div>
@@ -1,9 +1,5 @@
1
1
  BipApp::Application.routes.draw do
2
- resources :users do
3
- collection do
4
- get :get_countries, :as => "countries"
5
- end
6
- end
2
+ resources :users
7
3
 
8
4
  # The priority is based upon order of creation:
9
5
  # first created -> highest priority.
@@ -3,13 +3,13 @@
3
3
 
4
4
  $(document).ready(function() {
5
5
  /* Activating Best In Place */
6
- jQuery(".best_in_place").best_in_place()
6
+ jQuery(".best_in_place").best_in_place();
7
7
  });
8
8
 
9
9
  function inspect(obj, maxLevels, level)
10
10
  {
11
11
  var str = '', type, msg;
12
-
12
+ 1
13
13
  // Start Input Validations
14
14
  // Don't touch, we start iterating at level zero
15
15
  if(level == null) level = 0;
@@ -23,6 +23,7 @@ function BestInPlaceEditor(e) {
23
23
  this.element = jQuery(e);
24
24
  this.initOptions();
25
25
  this.bindForm();
26
+ this.initNil();
26
27
  $(this.activator).bind('click', {editor: this}, this.clickHandler);
27
28
  }
28
29
 
@@ -30,14 +31,15 @@ BestInPlaceEditor.prototype = {
30
31
  // Public Interface Functions //////////////////////////////////////////////
31
32
 
32
33
  activate : function() {
33
- var elem = this.element.html();
34
+ var elem = this.isNil ? "" : this.element.html();
34
35
  this.oldValue = elem;
35
36
  $(this.activator).unbind("click", this.clickHandler);
36
37
  this.activateForm();
37
38
  },
38
39
 
39
40
  abort : function() {
40
- this.element.html(this.oldValue);
41
+ if (this.isNil) this.element.html(this.nil);
42
+ else this.element.html(this.oldValue);
41
43
  $(this.activator).bind('click', {editor: this}, this.clickHandler);
42
44
  },
43
45
 
@@ -45,10 +47,10 @@ BestInPlaceEditor.prototype = {
45
47
  var editor = this;
46
48
  if (this.formType in {"input":1, "textarea":1} && this.getValue() == this.oldValue)
47
49
  { // Avoid request if no change is made
48
- editor.element.html(this.getValue());
49
- $(this.activator).bind('click', {editor: this}, this.clickHandler);
50
+ this.abort();
50
51
  return true;
51
52
  }
53
+ this.isNil = false;
52
54
  editor.ajax({
53
55
  "type" : "post",
54
56
  "dataType" : "text",
@@ -86,6 +88,7 @@ BestInPlaceEditor.prototype = {
86
88
  self.formType = self.formType || jQuery(this).attr("data-type");
87
89
  self.objectName = self.objectName || jQuery(this).attr("data-object");
88
90
  self.attributeName = self.attributeName || jQuery(this).attr("data-attribute");
91
+ self.nil = self.nil || jQuery(this).attr("data-nil");
89
92
  });
90
93
 
91
94
  // Try Rails-id based if parents did not explicitly supply something
@@ -103,6 +106,7 @@ BestInPlaceEditor.prototype = {
103
106
  self.objectName = self.element.attr("data-object") || self.objectName;
104
107
  self.attributeName = self.element.attr("data-attribute") || self.attributeName;
105
108
  self.activator = self.element.attr("data-activator") || self.element;
109
+ self.nil = self.element.attr("data-nil") || self.nil || "-";
106
110
 
107
111
  if (!self.element.attr("data-sanitize")) {
108
112
  self.sanitize = true;
@@ -122,6 +126,14 @@ BestInPlaceEditor.prototype = {
122
126
  this.getValue = BestInPlaceEditor.forms[this.formType].getValue;
123
127
  },
124
128
 
129
+ initNil: function() {
130
+ if (this.element.html() == "")
131
+ {
132
+ this.isNil = true
133
+ this.element.html(this.nil)
134
+ }
135
+ },
136
+
125
137
  getValue : function() {
126
138
  alert("The form was not properly initialized. getValue is unbound");
127
139
  },
@@ -228,6 +240,9 @@ BestInPlaceEditor.forms = {
228
240
  output += "</select></form>";
229
241
  this.element.html(output);
230
242
  this.element.find("select").bind('change', {editor: this}, BestInPlaceEditor.forms.select.blurHandler);
243
+ this.element.find("select").bind('blur', {editor: this}, BestInPlaceEditor.forms.select.blurHandler);
244
+ this.element.find("select").bind('keyup', {editor: this}, BestInPlaceEditor.forms.select.keyupHandler);
245
+ this.element.find("select")[0].focus();
231
246
  },
232
247
 
233
248
  getValue : function() {
@@ -236,6 +251,10 @@ BestInPlaceEditor.forms = {
236
251
 
237
252
  blurHandler : function(event) {
238
253
  event.data.editor.update();
254
+ },
255
+
256
+ keyupHandler : function(event) {
257
+ if (event.keyCode == 27) event.data.editor.abort();
239
258
  }
240
259
  },
241
260
 
@@ -254,11 +273,21 @@ BestInPlaceEditor.forms = {
254
273
 
255
274
  "textarea" : {
256
275
  activateForm : function() {
276
+ // grab width and height of text
277
+ width = this.element.css('width');
278
+ height = this.element.css('height');
279
+
280
+ // construct the form
257
281
  var output = '<form action="javascript:void(0)" style="display:inline;"><textarea>';
258
282
  output += this.sanitizeValue(this.oldValue);
259
283
  output += '</textarea></form>';
260
284
  this.element.html(output);
261
- this.element.find('textarea')[0].select();
285
+
286
+ // set width and height of textarea
287
+ jQuery(this.element.find("textarea")[0]).css({ 'min-width': width, 'min-height': height });
288
+ jQuery(this.element.find("textarea")[0]).elastic();
289
+
290
+ this.element.find("textarea")[0].focus();
262
291
  this.element.find("textarea").bind('blur', {editor: this}, BestInPlaceEditor.forms.textarea.blurHandler);
263
292
  this.element.find("textarea").bind('keyup', {editor: this}, BestInPlaceEditor.forms.textarea.keyupHandler);
264
293
  },
@@ -273,7 +302,13 @@ BestInPlaceEditor.forms = {
273
302
 
274
303
  keyupHandler : function(event) {
275
304
  if (event.keyCode == 27) {
276
- event.data.editor.abort();
305
+ BestInPlaceEditor.forms.textarea.abort(event.data.editor);
306
+ }
307
+ },
308
+
309
+ abort : function(editor) {
310
+ if (confirm("Are you sure you want to discard your changes?")) {
311
+ editor.abort();
277
312
  }
278
313
  }
279
314
  }
@@ -284,4 +319,138 @@ jQuery.fn.best_in_place = function() {
284
319
  jQuery(this).data('bestInPlaceEditor', new BestInPlaceEditor(this));
285
320
  });
286
321
  return this;
287
- };
322
+ };
323
+
324
+
325
+
326
+ /**
327
+ * @name Elastic
328
+ * @descripton Elastic is Jquery plugin that grow and shrink your textareas automaticliy
329
+ * @version 1.6.5
330
+ * @requires Jquery 1.2.6+
331
+ *
332
+ * @author Jan Jarfalk
333
+ * @author-email jan.jarfalk@unwrongest.com
334
+ * @author-website http://www.unwrongest.com
335
+ *
336
+ * @licens MIT License - http://www.opensource.org/licenses/mit-license.php
337
+ */
338
+
339
+ (function(jQuery){
340
+ jQuery.fn.extend({
341
+ elastic: function() {
342
+ // We will create a div clone of the textarea
343
+ // by copying these attributes from the textarea to the div.
344
+ var mimics = [
345
+ 'paddingTop',
346
+ 'paddingRight',
347
+ 'paddingBottom',
348
+ 'paddingLeft',
349
+ 'fontSize',
350
+ 'lineHeight',
351
+ 'fontFamily',
352
+ 'width',
353
+ 'fontWeight'];
354
+
355
+ return this.each( function() {
356
+
357
+ // Elastic only works on textareas
358
+ if ( this.type != 'textarea' ) {
359
+ return false;
360
+ }
361
+
362
+ var $textarea = jQuery(this),
363
+ $twin = jQuery('<div />').css({'position': 'absolute','display':'none','word-wrap':'break-word'}),
364
+ lineHeight = parseInt($textarea.css('line-height'),10) || parseInt($textarea.css('font-size'),'10'),
365
+ minheight = parseInt($textarea.css('height'),10) || lineHeight*3,
366
+ maxheight = parseInt($textarea.css('max-height'),10) || Number.MAX_VALUE,
367
+ goalheight = 0,
368
+ i = 0;
369
+
370
+ // Opera returns max-height of -1 if not set
371
+ if (maxheight < 0) { maxheight = Number.MAX_VALUE; }
372
+
373
+ // Append the twin to the DOM
374
+ // We are going to meassure the height of this, not the textarea.
375
+ $twin.appendTo($textarea.parent());
376
+
377
+ // Copy the essential styles (mimics) from the textarea to the twin
378
+ var i = mimics.length;
379
+ while(i--){
380
+ $twin.css(mimics[i].toString(),$textarea.css(mimics[i].toString()));
381
+ }
382
+
383
+
384
+ // Sets a given height and overflow state on the textarea
385
+ function setHeightAndOverflow(height, overflow){
386
+ curratedHeight = Math.floor(parseInt(height,10));
387
+ if($textarea.height() != curratedHeight){
388
+ $textarea.css({'height': curratedHeight + 'px','overflow':overflow});
389
+
390
+ }
391
+ }
392
+
393
+
394
+ // This function will update the height of the textarea if necessary
395
+ function update() {
396
+
397
+ // Get curated content from the textarea.
398
+ var textareaContent = $textarea.val().replace(/&/g,'&amp;').replace(/ /g, '&nbsp;').replace(/<|>/g, '&gt;').replace(/\n/g, '<br />');
399
+
400
+ // Compare curated content with curated twin.
401
+ var twinContent = $twin.html().replace(/<br>/ig,'<br />');
402
+
403
+ if(textareaContent+'&nbsp;' != twinContent){
404
+
405
+ // Add an extra white space so new rows are added when you are at the end of a row.
406
+ $twin.html(textareaContent+'&nbsp;');
407
+
408
+ // Change textarea height if twin plus the height of one line differs more than 3 pixel from textarea height
409
+ if(Math.abs($twin.height() + lineHeight - $textarea.height()) > 3){
410
+
411
+ var goalheight = $twin.height()+lineHeight;
412
+ if(goalheight >= maxheight) {
413
+ setHeightAndOverflow(maxheight,'auto');
414
+ } else if(goalheight <= minheight) {
415
+ setHeightAndOverflow(minheight,'hidden');
416
+ } else {
417
+ setHeightAndOverflow(goalheight,'hidden');
418
+ }
419
+
420
+ }
421
+
422
+ }
423
+
424
+ }
425
+
426
+ // Hide scrollbars
427
+ $textarea.css({'overflow':'hidden'});
428
+
429
+ // Update textarea size on keyup, change, cut and paste
430
+ $textarea.bind('keyup change cut paste', function(){
431
+ update();
432
+ });
433
+
434
+ // Compact textarea on blur
435
+ // Lets animate this....
436
+ $textarea.bind('blur',function(){
437
+ if($twin.height() < maxheight){
438
+ if($twin.height() > minheight) {
439
+ $textarea.height($twin.height());
440
+ } else {
441
+ $textarea.height(minheight);
442
+ }
443
+ }
444
+ });
445
+
446
+ // And this line is to catch the browser paste event
447
+ $textarea.live('input paste',function(e){ setTimeout( update, 250); });
448
+
449
+ // Run update once when elastic is initialized
450
+ update();
451
+
452
+ });
453
+
454
+ }
455
+ });
456
+ })(jQuery);
@@ -24,16 +24,13 @@ input[type=checkbox] {
24
24
  width: 1em;
25
25
  }
26
26
  textarea {
27
- height:15em;
28
- width: 95%;
27
+ max-height:15em;
28
+ min-width: 40em;
29
29
  }
30
30
  .best_in_place {
31
31
  padding: .1m;
32
32
  cursor: hand;
33
33
  cursor: pointer;
34
- -moz-transition: background 0.5s linear;
35
- -o-transition: background 0.5s linear;
36
- -webkit-transition: background 0.5s linear;
37
34
  -moz-border-radius: 5px;
38
35
  -webkit-border-radius: 5px;
39
36
  -o-border-radius: 5px;
@@ -62,7 +59,13 @@ textarea {
62
59
  padding: 20px;
63
60
  background-color: #000000;
64
61
  color: #FFFFFF;
65
- border: 2px solid #FFFFFF;
62
+ border: 2px solid #AAA;
63
+ -moz-border-radius: 10px;
64
+ -webkit-border-radius: 10px;
65
+ -o-border-radius: 10px;
66
+ -ms-border-radius: 10px;
67
+ -khtml-border-radius: 10px;
68
+ border-radius: 10px;
66
69
  }
67
70
  .purr:hover .close {
68
71
  position: absolute;
@@ -1,17 +1,17 @@
1
1
  # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2
2
 
3
3
  one:
4
- name: MyString
5
- last_name: MyString
6
- address: MyString
7
- email: MyString
8
- zip: 1
9
- country: MyString
4
+ name: testy
5
+ last_name: tester
6
+ address: 123 main st
7
+ email: testytester@example.com
8
+ zip: 12345
9
+ country: 1
10
10
 
11
11
  two:
12
- name: MyString
13
- last_name: MyString
14
- address: MyString
15
- email: MyString
16
- zip: 1
17
- country: MyString
12
+ name: mystery
13
+ last_name: man
14
+ address: PO Box 0, Nowhere
15
+ email: misterioso@example.com
16
+ zip: 99999
17
+ country:
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: best_in_place
3
3
  version: !ruby/object:Gem::Version
4
- hash: 11
4
+ hash: 9
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 8
10
- version: 0.1.8
9
+ - 9
10
+ version: 0.1.9
11
11
  platform: ruby
12
12
  authors:
13
13
  - Bernat Farrero
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-02-05 00:00:00 +01:00
18
+ date: 2011-02-12 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency