// Based off https://code.google.com/p/gaequery/source/browse/trunk/src/static/scripts/jquery.autogrow-textarea.js?r=2 // Modified by David Beck // Mofified by kasimi (c) 2016 ( function( factory ) { // UMD wrapper if ( typeof define === 'function' && define.amd ) { // AMD define( [ 'jquery' ], factory ); } else if ( typeof exports !== 'undefined' ) { // Node/CommonJS module.exports = factory( require( 'jquery' ) ); } else { // Browser globals factory( jQuery ); } }( function( $ ) { /* * Auto-growing textareas; technique ripped from Facebook */ $.fn.autogrow = function(options) { options = $.extend( { vertical: true, horizontal: false, characterSlop: 0 }, options); this.filter('textarea,input').each(function() { var $this = $(this), borderBox = $this.css( 'box-sizing' ) === 'border-box', // minHeight = borderBox ? $this.outerHeight() : $this.height(), maxHeight = $this.attr( "maxHeight" ), minWidth = typeof( $this.attr( "minWidth" ) ) == "undefined" ? 0 : $this.attr( "minWidth" ); if( typeof( maxHeight ) == "undefined" ) maxHeight = 1000000; var shadow = $('
').css( { position: 'absolute', top: -10000, left: -10000, fontSize: $this.css('fontSize'), fontFamily: $this.css('fontFamily'), fontWeight: $this.css('fontWeight'), lineHeight: $this.css('lineHeight'), paddingLeft: $this.css('paddingLeft'), paddingRight: $this.css('paddingRight'), paddingTop: $this.css('paddingTop'), paddingBottom: $this.css('paddingBottom'), borderTop: $this.css('borderTop'), borderBottom: $this.css('borderBottom'), borderLeft: $this.css('borderLeft'), borderRight: $this.css('borderRight'), whiteSpace: 'pre-wrap', resize: 'none' } ).appendTo(document.body); shadow.html( 'a' ); var characterWidth = shadow.width(); shadow.html( '' ); var isTextarea = $this.is('textarea'); var update = function( val ) { var times = function(string, number) { for (var i = 0, r = ''; i < number; i ++) r += string; return r; }; if( typeof val === 'undefined' ) val = this.value; if( val === '' && $(this).attr("placeholder") ) val = $(this).attr("placeholder"); if( options.vertical ) val = val.replace(/&/g, '&') .replace(//g, '>') .replace(/\n$/, '
 ') .replace(/\n/g, '
') .replace(/ {2,}/g, function(space) { return times(' ', space.length -1) + ' '; }); else val = escapeHtml( val ); //if( options.horizontal ) // val = $.trim( val ); // if( $(this).prop( 'tagName' ).toUpperCase() === 'INPUT' ) // shadow.text(val).css( "width", "auto" ); // else shadow.html( val ).css( "width", "auto" ); // need to use html here otherwise no way to count spaces (with html we can use  ) if( options.horizontal ) { var slopWidth = options.characterSlop * characterWidth + 2; var newWidth = Math.max( shadow.width() + slopWidth, minWidth ); var maxWidth = options.maxWidth; //if( typeof( maxWidth ) === "undefined" ) maxWidth = $this.parent().width() - 12; // not sure why we were doing this but seems like a bad idea. doesn't work with inline-block parents for one thing, since it is the text area that should be "pushing" them to be wider if( maxWidth ) newWidth = Math.min( newWidth, maxWidth ); // Take scrollbar into account if (isTextarea && shadow.get(0).scrollHeight > shadow.height()) { newWidth += 20; } $(this).css( "width", newWidth ); } if( options.vertical ) { var shadowWidth = $(this).width(); if( ! borderBox ) shadowWidth = shadowWidth - parseInt($this.css('paddingLeft'),10) - parseInt($this.css('paddingRight'),10); shadow.css( "width", shadowWidth ); var shadowHeight = borderBox ? shadow.outerHeight() : shadow.height(); $(this).css( "height", "auto" ); minHeight = borderBox ? $this.outerHeight() : $this.height(); var newHeight = Math.min( Math.max( shadowHeight, minHeight ), maxHeight ); $(this).css( "height", newHeight ); $(this).css( "overflow", newHeight == maxHeight ? "auto" : "hidden" ); } }; $(this) .change(function(){update.call( this );return true;}) .keyup(function(){update.call( this );return true;}) .keypress(function( event ) { if( event.ctrlKey || event.metaKey ) return; var val = this.value; var caretInfo = _getCaretInfo( this ); var typedChar = event.which === 13 ? "\n" : String.fromCharCode( event.which ); var valAfterKeypress = val.slice( 0, caretInfo.start ) + typedChar + val.slice( caretInfo.end ); update.call( this, valAfterKeypress ); return true; }) .bind( "update.autogrow", function(){ update.apply(this); } ) .bind( "remove.autogrow", function() { shadow.remove(); } ); update.apply(this); }); return this; }; // comes from https://github.com/madapaja/jquery.selection/blob/master/src/jquery.selection.js var _getCaretInfo = function(element){ var res = { text: '', start: 0, end: 0 }; if (!element.value) { /* no value or empty string */ return res; } try { if (window.getSelection) { /* except IE */ res.start = element.selectionStart; res.end = element.selectionEnd; res.text = element.value.slice(res.start, res.end); } else if (doc.selection) { /* for IE */ element.focus(); var range = doc.selection.createRange(), range2 = doc.body.createTextRange(); res.text = range.text; try { range2.moveToElementText(element); range2.setEndPoint('StartToStart', range); } catch (e) { range2 = element.createTextRange(); range2.setEndPoint('StartToStart', range); } res.start = element.value.length - range2.text.length; res.end = res.start + range.text.length; } } catch (e) { /* give up */ } return res; }; var entityMap = { "&": "&", "<": "<", ">": ">", '"': '"', "'": ''', "/": '/', " ": ' ' }; function escapeHtml(string) { return String(string).replace(/[&<>"'\/\ ]/g, function (s) { return entityMap[s]; } ); } } ) );