1489 Switch to the elastic plugin.

Signed-off-by: Kyle Adams kyle@geek.net

Kyle Adams Kyle Adams 2011-02-11

changed Allura/allura/lib/widgets/form_fields.py
copied Allura/allura/lib/widgets/resources/js/jquery.exptextarea.js -> Allura/allura/lib/widgets/resources/js/jquery.elastic.js
Allura/allura/lib/widgets/form_fields.py Diff Switch to side-by-side view
Loading...
Allura/allura/lib/widgets/resources/js/jquery.exptextarea.js to Allura/allura/lib/widgets/resources/js/jquery.elastic.js
--- a/Allura/allura/lib/widgets/resources/js/jquery.exptextarea.js
+++ b/Allura/allura/lib/widgets/resources/js/jquery.elastic.js
@@ -1,218 +1,117 @@
-/*
- * jQuery expanding textarea
- * 
- * https://github.com/ferama/jquery-expanding-textarea
- * 
- * Adapted for jQuery from dojo TextArea widget
- *
- * @author Marco Ferragina
- * @version 1.00
- */
+/**
+*	@name							Elastic
+*	@descripton						Elastic is Jquery plugin that grow and shrink your textareas automaticliy
+*	@version						1.6.4
+*	@requires						Jquery 1.2.6+
+*
+*	@author							Jan Jarfalk
+*	@author-email					jan.jarfalk@unwrongest.com
+*	@author-website					http://www.unwrongest.com
+*
+*	@licens							MIT License - http://www.opensource.org/licenses/mit-license.php
+*/
 
-(function($) {
+(function(jQuery){ 
+	jQuery.fn.extend({  
+		elastic: function() {
+		
+			//	We will create a div clone of the textarea
+			//	by copying these attributes from the textarea to the div.
+			var mimics = [
+				'paddingTop',
+				'paddingRight',
+				'paddingBottom',
+				'paddingLeft',
+				'fontSize',
+				'lineHeight',
+				'fontFamily',
+				'width',
+				'fontWeight'];
+			
+			return this.each( function() {
+				
+				// Elastic only works on textareas
+				if ( this.type != 'textarea' ) {
+					return false;
+				}
+				
+				var $textarea	=	jQuery(this),
+					$twin		=	jQuery('<div />').css({'position': 'absolute','display':'none','word-wrap':'break-word'}),
+					lineHeight	=	parseInt($textarea.css('line-height'),10) || parseInt($textarea.css('font-size'),'10'),
+					minheight	=	parseInt($textarea.css('height'),10) || lineHeight*3,
+					maxheight	=	parseInt($textarea.css('max-height'),10) || Number.MAX_VALUE,
+					goalheight	=	0,
+					i 			=	0;
+				
+				// Opera returns max-height of -1 if not set
+				if (maxheight < 0) { maxheight = Number.MAX_VALUE; }
+					
+				// Append the twin to the DOM
+				// We are going to meassure the height of this, not the textarea.
+				$twin.appendTo($textarea.parent());
+				
+				// Copy the essential styles (mimics) from the textarea to the twin
+				var i = mimics.length;
+				while(i--){
+					$twin.css(mimics[i].toString(),$textarea.css(mimics[i].toString()));
+				}
+				
+				
+				// Sets a given height and overflow state on the textarea
+				function setHeightAndOverflow(height, overflow){
+					curratedHeight = Math.floor(parseInt(height,10));
+					if($textarea.height() != curratedHeight){
+						$textarea.css({'height': curratedHeight + 'px','overflow':overflow});
+						
+					}
+				}
+				
+				
+				// This function will update the height of the textarea if necessary 
+				function update() {
+					
+					// Get curated content from the textarea.
+					var textareaContent = $textarea.val().replace(/&/g,'&amp;').replace(/  /g, '&nbsp;').replace(/<|>/g, '&gt;').replace(/\n/g, '<br />');
 
-$.fn.expandingTextArea = function() {
-    return this.filter('textarea').each(function(){
-        var textarea = this;
-        var $this = $(this);
-        $this.css({
-            'overflow-x': 'auto',
-            'overflow-y': 'hidden',
-            'box-sizing': 'border-box',
-            '-moz-box-sizing': 'border-box',
-            '-webkit-box-sizing': 'border-box',
-            'resize' : 'none'
-        });
-        var _busyResizing = false;
-        var _needsHelpShrinking = $.browser.mozilla || $.browser.webkit;
-        var _setTimoutHandle;
-
-        var gcs;
-
-        if ($.browser.webkit) {
-            gcs = function(node) {
-                var s;
-                if(node.nodeType == 1){
-                    var dv = node.ownerDocument.defaultView;
-                    s = dv.getComputedStyle(node, null);
-                    if(!s && node.style){
-                        node.style.display = "";
-                        s = dv.getComputedStyle(node, null);
-                    }
-                }
-                return s || {};
-            };
-        } else if ($.browser.msie) {
-            gcs = function(node){
-                // IE (as of 7) doesn't expose Element like sane browsers
-                return node.nodeType == 1 /* ELEMENT_NODE*/ ? node.currentStyle : {};
-            };
-        } else {
-            gcs = function(node){
-                return node.nodeType == 1 ?
-                    node.ownerDocument.defaultView.getComputedStyle(node, null) : {};
-            };
-        }
-
-        var px;
-        if (!$.browser.msie) {
-            px = function(element, value) {
-                return parseFloat(value) || 0;
-            };
-        } else {
-            px = function(element, avalue) {
-                if(!avalue){ return 0; }
-                // on IE7, medium is usually 4 pixels
-                if(avalue == "medium"){ return 4; }
-                // style values can be floats, client code may
-                // want to round this value for integer pixels.
-                if(avalue.slice && avalue.slice(-2) == 'px'){ return parseFloat(avalue); }
-                with(element){
-                    var sLeft = style.left;
-                    var rsLeft = runtimeStyle.left;
-                    runtimeStyle.left = currentStyle.left;
-                    try{
-                        // 'avalue' may be incompatible with style.left, which can cause IE to throw
-                        // this has been observed for border widths using "thin", "medium", "thick" constants
-                        // those particular constants could be trapped by a lookup
-                        // but perhaps there are more
-                        style.left = avalue;
-                        avalue = style.pixelLeft;
-                    }catch(e){
-                        avalue = 0;
-                    }
-                    style.left = sLeft;
-                    runtimeStyle.left = rsLeft;
-                }
-                return avalue;
-            };
-        }
-
-        function _getHeight() {
-            var newH = textarea.scrollHeight;
-            if ($.browser.msie) {
-                newH += textarea.offsetHeight - textarea.clientHeight;
-            } else if ($.browser.webkit) {
-                newH += getBorderExtents(textarea).h;
-            } else if ($.browser.mozilla) {
-                newH += textarea.offsetHeight - textarea.clientHeight;
-            } else {
-                newH += getPadBorderExtents(textarea).h;
-            }
-            return newH;
-        }
-        function getPadExtents(n, computedStyle) {
-            var
-                s = computedStyle||gcs(n),
-                l = px(n, s.paddingLeft),
-                t = px(n, s.paddingTop);
-            return {
-                l: l,
-                t: t,
-                w: l+px(n, s.paddingRight),
-                h: t+px(n, s.paddingBottom)
-            };
- 
-        }
-        function getBorderExtents(n, computedStyle) {
-            var 
-                ne = "none",
-                s = computedStyle||gcs(n),
-                bl = (s.borderLeftStyle != ne ? px(n, s.borderLeftWidth) : 0),
-                bt = (s.borderTopStyle != ne ? px(n, s.borderTopWidth) : 0);
-            return {
-                l: bl,
-                t: bt,
-                w: bl + (s.borderRightStyle!=ne ? px(n, s.borderRightWidth) : 0),
-                h: bt + (s.borderBottomStyle!=ne ? px(n, s.borderBottomWidth) : 0)
-            };
-        }
-        function getPadBorderExtents(n, computedStyle) {
-            var 
-                s = computedStyle||gcs(n),
-                p = getPadExtents(n, s),
-                b = getBorderExtents(n, s);
-            return {
-                l: p.l + b.l,
-                t: p.t + b.t,
-                w: p.w + b.w,
-                h: p.h + b.h
-            };
-        }
-
-        function _shrink() {
-            _setTimoutHandle = null;
-            if (_needsHelpShrinking && ! _busyResizing) {
-                _busyResizing = true;
-                var empty = false;
-                if(textarea.value == ''){
-                    textarea.value = ' '; // prevent collapse all the way back to 0
-                    empty = true;
-                }
-                var scrollHeight = textarea.scrollHeight;
-                if (!scrollHeight) {
-                    _estimateHeight();
-                } else {
-                    var oldPadding = $this.css('padding-bottom');
-                    var newPadding = getPadExtents(textarea);
-                    newPadding = newPadding.h - newPadding.t;
-                    $this.css('padding-bottom', newPadding + 1 +"px");
-                    var newH = _getHeight() - 1 + "px";
-                    if ($this.css('max-height') != newH) {
-                        $this.css('padding-bottom', newPadding + scrollHeight + "px");
-                        textarea.scrollTop = 0;
-                        $this.css('max-height', _getHeight() - scrollHeight + "px");
-                    }
-                    $this.css('padding-bottom', oldPadding);
-                }
-                if(empty){
-                    textarea.value = '';
-                }
-                _busyResizing = false;
-            }
-        }
-
-        function _estimateHeight() {
-            $this.css({
-                'max-height': '',
-                'height': 'auto'
-            });
-            textarea.rows = (textarea.value.match(/\n/g) || []).length + 1;
-        }
-
-        function _onInput() {
-            if (_busyResizing) { return; }
-            _busyResizing = true;
-            if(textarea.scrollHeight && textarea.offsetHeight && textarea.clientHeight){
-                var newH = _getHeight() + "px";
-                if ($this.css('height') != newH) {
-                    $this.css('height', newH);
-                    $this.css('maxHeight', newH);
-                }
-                if (_needsHelpShrinking) {
-                    if (_setTimoutHandle) {
-                        clearTimeout(_setTimoutHandle);
-                    }
-                    _setTimoutHandle = setTimeout(_shrink, 0);
-                }
-            } else {
-                _estimateHeight();
-            }
-            _busyResizing = false;
-        }
-
-        _setTimoutHandle = setTimeout(_onInput, 0);
-        $this.unbind('.expandingTextarea')
-            .bind('keyup.expandingTextarea', _onInput)
-            .bind('keydown.expandingTextarea', _onInput)
-            .bind('change.expandingTextarea', _onInput)
-            .bind('scroll.expandingTextarea', _onInput)
-            .bind('resize.expandingTextarea', _onInput)
-            .bind('focus.expandingTextarea', _onInput)
-            .bind('blur.expandingTextarea', _onInput)
-            .bind('cut.expandingTextarea', _onInput)
-            .bind('paste.expandingTextarea', _onInput);
-    });
-};
-
-})(jQuery);
+					var twinContent = $twin.html();
+					
+					if(textareaContent+'&nbsp;' != twinContent){
+					
+						// Add an extra white space so new rows are added when you are at the end of a row.
+						$twin.html(textareaContent+'&nbsp;');
+						
+						// Change textarea height if twin plus the height of one line differs more than 3 pixel from textarea height
+						if(Math.abs($twin.height()+lineHeight - $textarea.height()) > 3){
+							
+							var goalheight = $twin.height()+lineHeight;
+							if(goalheight >= maxheight) {
+								setHeightAndOverflow(maxheight,'auto');
+							} else if(goalheight <= minheight) {
+								setHeightAndOverflow(minheight,'hidden');
+							} else {
+								setHeightAndOverflow(goalheight,'hidden');
+							}
+							
+						}
+						
+					}
+					
+				}
+				
+				// Hide scrollbars
+				$textarea.css({'overflow':'hidden'});
+				
+				// Update textarea size on keyup
+				$textarea.keyup(function(){ update(); });
+				
+				// And this line is to catch the browser paste event
+				$textarea.live('input paste',function(e){ setTimeout( update, 250); });				
+				
+				// Run update once when elastic is initialized
+				update();
+				
+			});
+			
+        } 
+    }); 
+})(jQuery);