Commit ec2272898489f98e8e7bafa76d2ea74547486300

Authored by Rodrigo Souto
1 parent 12208eb9

wall: correct textarea growing indefinetely

ActionItem3136
app/views/layouts/_javascript.html.erb
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'colorbox', 3 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'colorbox',
4 'jquery-ui-1.10.4/js/jquery-ui-1.10.4.min', 'jquery.scrollTo', 'jquery.form.js', 'jquery-validation/jquery.validate', 4 'jquery-ui-1.10.4/js/jquery-ui-1.10.4.min', 'jquery.scrollTo', 'jquery.form.js', 'jquery-validation/jquery.validate',
5 'jquery.cookie', 'jquery.ba-bbq.min.js', 'reflection', 'jquery.tokeninput', 5 'jquery.cookie', 'jquery.ba-bbq.min.js', 'reflection', 'jquery.tokeninput',
6 -'add-and-join', 'report-abuse', 'catalog', 'manage-products', 6 +'add-and-join', 'report-abuse', 'catalog', 'manage-products', 'autogrow',
7 'jquery-timepicker-addon/dist/jquery-ui-timepicker-addon', 'application.js', 'rails.js', :cache => 'cache/application' %> 7 'jquery-timepicker-addon/dist/jquery-ui-timepicker-addon', 'application.js', 'rails.js', :cache => 'cache/application' %>
8 8
9 <% language = FastGettext.locale %> 9 <% language = FastGettext.locale %>
app/views/profile/_profile_wall.html.erb
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 <div id='leave_scrap'> 2 <div id='leave_scrap'>
3 <%= flash[:error] %> 3 <%= flash[:error] %>
4 <%= form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap', :tab_action => 'wall' }, :update => 'profile_activities', :success => "$('leave_scrap_content').value=''", :complete => "jQuery('#leave_scrap_form').removeClass('loading').find('*').attr('disabled', false)", :loading => "jQuery('#leave_scrap_form').addClass('loading').find('*').attr('disabled', true)", :html => {:id => 'leave_scrap_form' } do %> 4 <%= form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap', :tab_action => 'wall' }, :update => 'profile_activities', :success => "$('leave_scrap_content').value=''", :complete => "jQuery('#leave_scrap_form').removeClass('loading').find('*').attr('disabled', false)", :loading => "jQuery('#leave_scrap_form').addClass('loading').find('*').attr('disabled', true)", :html => {:id => 'leave_scrap_form' } do %>
5 - <%= limited_text_area :scrap, :content, 420, 'leave_scrap_content', :cols => 50, :rows => 2 %> 5 + <%= limited_text_area :scrap, :content, 420, 'leave_scrap_content', :cols => 50, :rows => 2, :class => 'autogrow' %>
6 <%= submit_button :new, _('Share') %> 6 <%= submit_button :new, _('Share') %>
7 <% end %> 7 <% end %>
8 </div> 8 </div>
public/javascripts/application.js
@@ -681,7 +681,6 @@ function hide_and_show(hide_elements, show_elements) { @@ -681,7 +681,6 @@ function hide_and_show(hide_elements, show_elements) {
681 681
682 function limited_text_area(textid, limit) { 682 function limited_text_area(textid, limit) {
683 var text = jQuery('#' + textid).val(); 683 var text = jQuery('#' + textid).val();
684 - grow_text_area(textid);  
685 var textlength = text.length; 684 var textlength = text.length;
686 jQuery('#' + textid + '_left span').html(limit - textlength); 685 jQuery('#' + textid + '_left span').html(limit - textlength);
687 if (textlength > limit) { 686 if (textlength > limit) {
@@ -696,14 +695,9 @@ function limited_text_area(textid, limit) { @@ -696,14 +695,9 @@ function limited_text_area(textid, limit) {
696 } 695 }
697 } 696 }
698 697
699 -function grow_text_area(textid) {  
700 - var height = jQuery('#' + textid).attr('scrollHeight');  
701 - if (jQuery.browser.webkit) {  
702 - height -= parseInt(jQuery('#' + textid).css('padding-top')) +  
703 - parseInt(jQuery('#' + textid).css('padding-bottom'));  
704 - }  
705 - jQuery('#' + textid).css('height', height + 'px');  
706 -} 698 +jQuery(function($) {
  699 + $('.autogrow').autogrow();
  700 +});
707 701
708 jQuery(function($) { 702 jQuery(function($) {
709 $('a').each(function() { 703 $('a').each(function() {
public/javascripts/autogrow.js 0 → 100644
@@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
  1 +;(function($){
  2 + //pass in just the context as a $(obj) or a settings JS object
  3 + $.fn.autogrow = function(opts) {
  4 + var that = $(this).css({overflow: 'hidden', resize: 'none'}) //prevent scrollies
  5 + , selector = that.selector
  6 + , defaults = {
  7 + context: $(document) //what to wire events to
  8 + , animate: true //if you want the size change to animate
  9 + , speed: 200 //speed of animation
  10 + , fixMinHeight: true //if you don't want the box to shrink below its initial size
  11 + , cloneClass: 'autogrowclone' //helper CSS class for clone if you need to add special rules
  12 + , onInitialize: false //resizes the textareas when the plugin is initialized
  13 + }
  14 + ;
  15 + opts = $.isPlainObject(opts) ? opts : {context: opts ? opts : $(document)};
  16 + opts = $.extend({}, defaults, opts);
  17 + that.each(function(i, elem){
  18 + var min, clone;
  19 + elem = $(elem);
  20 + //if the element is "invisible", we get an incorrect height value
  21 + //to get correct value, clone and append to the body.
  22 + if (elem.is(':visible') || parseInt(elem.css('height'), 10) > 0) {
  23 + min = parseInt(elem.css('height'), 10) || elem.innerHeight();
  24 + } else {
  25 + clone = elem.clone()
  26 + .addClass(opts.cloneClass)
  27 + .val(elem.val())
  28 + .css({
  29 + position: 'absolute'
  30 + , visibility: 'hidden'
  31 + , display: 'block'
  32 + })
  33 + ;
  34 + $('body').append(clone);
  35 + min = clone.innerHeight();
  36 + clone.remove();
  37 + }
  38 + if (opts.fixMinHeight) {
  39 + elem.data('autogrow-start-height', min); //set min height
  40 + }
  41 + elem.css('height', min);
  42 +
  43 + if (opts.onInitialize) {
  44 + resize.call(elem);
  45 + }
  46 + });
  47 + opts.context
  48 + .on('keyup paste', selector, resize)
  49 + ;
  50 +
  51 + function resize (e){
  52 + var box = $(this)
  53 + , oldHeight = box.innerHeight()
  54 + , newHeight = this.scrollHeight
  55 + , minHeight = box.data('autogrow-start-height') || 0
  56 + , clone
  57 + ;
  58 + if (oldHeight < newHeight) { //user is typing
  59 + this.scrollTop = 0; //try to reduce the top of the content hiding for a second
  60 + opts.animate ? box.stop().animate({height: newHeight}, opts.speed) : box.innerHeight(newHeight);
  61 + } else if (!e || e.which == 8 || e.which == 46 || (e.ctrlKey && e.which == 88)) { //user is deleting, backspacing, or cutting
  62 + if (oldHeight > minHeight) { //shrink!
  63 + //this cloning part is not particularly necessary. however, it helps with animation
  64 + //since the only way to cleanly calculate where to shrink the box to is to incrementally
  65 + //reduce the height of the box until the $.innerHeight() and the scrollHeight differ.
  66 + //doing this on an exact clone to figure out the height first and then applying it to the
  67 + //actual box makes it look cleaner to the user
  68 + clone = box.clone()
  69 + .addClass(opts.cloneClass) //add clone class for extra css rules
  70 + .css({position: 'absolute', zIndex:-10}) //make "invisible"
  71 + .val(box.val()) //populate with content for consistent measuring
  72 + ;
  73 + box.after(clone); //append as close to the box as possible for best CSS matching for clone
  74 + do { //reduce height until they don't match
  75 + newHeight = clone[0].scrollHeight - 1;
  76 + clone.innerHeight(newHeight);
  77 + } while (newHeight === clone[0].scrollHeight);
  78 + newHeight++; //adding one back eliminates a wiggle on deletion
  79 + clone.remove();
  80 + box.focus(); // Fix issue with Chrome losing focus from the textarea.
  81 +
  82 + //if user selects all and deletes or holds down delete til beginning
  83 + //user could get here and shrink whole box
  84 + newHeight < minHeight && (newHeight = minHeight);
  85 + oldHeight > newHeight && opts.animate ? box.stop().animate({height: newHeight}, opts.speed) : box.innerHeight(newHeight);
  86 + } else { //just set to the minHeight
  87 + box.innerHeight(minHeight);
  88 + }
  89 + }
  90 + }
  91 + return that;
  92 + }
  93 +})(jQuery);