Fix caret positioning outside editable.
[redakcja.git] / src / redakcja / static / js / lib / jquery / jquery.elastic.js
1 /**
2 *       @name                                                   Elastic
3 *       @descripton                                             Elastic is jQuery plugin that grow and shrink your textareas automatically
4 *       @version                                                1.6.11
5 *       @requires                                               jQuery 1.2.6+
6 *
7 *       @author                                                 Jan Jarfalk
8 *       @author-email                                   jan.jarfalk@unwrongest.com
9 *       @author-website                                 http://www.unwrongest.com
10 *
11 *       @licence                                                MIT License - http://www.opensource.org/licenses/mit-license.php
12 */
13
14 (function($){ 
15         jQuery.fn.extend({  
16                 elastic: function() {
17                 
18                         //      We will create a div clone of the textarea
19                         //      by copying these attributes from the textarea to the div.
20                         var mimics = [
21                                 'paddingTop',
22                                 'paddingRight',
23                                 'paddingBottom',
24                                 'paddingLeft',
25                                 'fontSize',
26                                 'lineHeight',
27                                 'fontFamily',
28                                 'width',
29                                 'fontWeight',
30                                 'border-top-width',
31                                 'border-right-width',
32                                 'border-bottom-width',
33                                 'border-left-width',
34                                 'borderTopStyle',
35                                 'borderTopColor',
36                                 'borderRightStyle',
37                                 'borderRightColor',
38                                 'borderBottomStyle',
39                                 'borderBottomColor',
40                                 'borderLeftStyle',
41                                 'borderLeftColor'
42                                 ];
43                         
44                         return this.each( function() {
45
46                                 // Elastic only works on textareas
47                                 if ( this.type !== 'textarea' ) {
48                                         return false;
49                                 }
50                                         
51                         var $textarea   = jQuery(this),
52                                 $twin           = jQuery('<div />').css({
53                                         'position'              : 'absolute',
54                                         'display'               : 'none',
55                                         'word-wrap'             : 'break-word',
56                                         'white-space'   :'pre-wrap'
57                                 }),
58                                 lineHeight      = parseInt($textarea.css('line-height'),10) || parseInt($textarea.css('font-size'),'10'),
59                                 minheight       = parseInt($textarea.css('height'),10) || lineHeight*3,
60                                 maxheight       = parseInt($textarea.css('max-height'),10) || Number.MAX_VALUE,
61                                 goalheight      = 0;
62                                 
63                                 // Opera returns max-height of -1 if not set
64                                 if (maxheight < 0) { maxheight = Number.MAX_VALUE; }
65                                         
66                                 // Append the twin to the DOM
67                                 // We are going to meassure the height of this, not the textarea.
68                                 $twin.appendTo($textarea.parent());
69                                 
70                                 // Copy the essential styles (mimics) from the textarea to the twin
71                                 var i = mimics.length;
72                                 while(i--){
73                                         $twin.css(mimics[i].toString(),$textarea.css(mimics[i].toString()));
74                                 }
75                                 
76                                 // Updates the width of the twin. (solution for textareas with widths in percent)
77                                 function setTwinWidth(){
78                                         var curatedWidth = Math.floor(parseInt($textarea.width(),10));
79                                         if($twin.width() !== curatedWidth){
80                                                 $twin.css({'width': curatedWidth + 'px'});
81                                                 
82                                                 // Update height of textarea
83                                                 update(true);
84                                         }
85                                 }
86                                 
87                                 // Sets a given height and overflow state on the textarea
88                                 function setHeightAndOverflow(height, overflow){
89                                 
90                                         var curratedHeight = Math.floor(parseInt(height,10));
91                                         if($textarea.height() !== curratedHeight){
92                                                 $textarea.css({'height': curratedHeight + 'px','overflow':overflow});
93                                         }
94                                 }
95                                 
96                                 // This function will update the height of the textarea if necessary 
97                                 function update(forced) {
98                                         
99                                         // Get curated content from the textarea.
100                                         var textareaContent = $textarea.val().replace(/&/g,'&amp;').replace(/ {2}/g, '&nbsp;').replace(/<|>/g, '&gt;').replace(/\n/g, '<br />');
101                                         
102                                         // Compare curated content with curated twin.
103                                         var twinContent = $twin.html().replace(/<br>/ig,'<br />');
104                                         
105                                         if(forced || textareaContent+'&nbsp;' !== twinContent){
106                                         
107                                                 // Add an extra white space so new rows are added when you are at the end of a row.
108                                                 $twin.html(textareaContent+'&nbsp;');
109                                                 
110                                                 // Change textarea height if twin plus the height of one line differs more than 3 pixel from textarea height
111                                                 if(Math.abs($twin.height() + lineHeight - $textarea.height()) > 3){
112                                                         
113                                                         var goalheight = $twin.height()+lineHeight;
114                                                         if(goalheight >= maxheight) {
115                                                                 setHeightAndOverflow(maxheight,'auto');
116                                                         } else if(goalheight <= minheight) {
117                                                                 setHeightAndOverflow(minheight,'hidden');
118                                                         } else {
119                                                                 setHeightAndOverflow(goalheight,'hidden');
120                                                         }
121                                                         
122                                                 }
123                                                 
124                                         }
125                                         
126                                 }
127                                 
128                                 // Hide scrollbars
129                                 $textarea.css({'overflow':'hidden'});
130                                 
131                                 // Update textarea size on keyup, change, cut and paste
132                                 $textarea.bind('keyup change cut paste', function(){
133                                         update(); 
134                                 });
135                                 
136                                 // Update width of twin if browser or textarea is resized (solution for textareas with widths in percent)
137                                 jQuery(window).bind('resize', setTwinWidth);
138                                 $textarea.bind('resize', setTwinWidth);
139                                 $textarea.bind('update', update);
140                                 
141                                 // Compact textarea on blur
142                                 $textarea.bind('blur',function(){
143                                         if($twin.height() < maxheight){
144                                                 if($twin.height() > minheight) {
145                                                         $textarea.height($twin.height());
146                                                 } else {
147                                                         $textarea.height(minheight);
148                                                 }
149                                         }
150                                 });
151                                 
152                                 // And this line is to catch the browser paste event
153                                 $textarea.bind('input paste',function(e){ setTimeout( update, 250); });                         
154                                 
155                                 // Run update once when elastic is initialized
156                                 update();
157                                 
158                         });
159                         
160         } 
161     }); 
162 })(jQuery);