-
Notifications
You must be signed in to change notification settings - Fork 55
/
jquery.autosize.js
188 lines (157 loc) · 5.65 KB
/
jquery.autosize.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/*!
jQuery Autosize v1.16.13
(c) 2013 Jack Moore - jacklmoore.com
updated: 2013-06-04
license: http://www.opensource.org/licenses/mit-license.php
*/
(function ($) {
var
defaults = {
className: 'autosizejs',
append: '',
callback: false
},
hidden = 'hidden',
borderBox = 'border-box',
lineHeight = 'lineHeight',
// border:0 is unnecessary, but avoids a bug in FireFox on OSX
copy = '<textarea tabindex="-1" style="position:absolute; top:-999px; left:0; right:auto; bottom:auto; border:0; -moz-box-sizing:content-box; -webkit-box-sizing:content-box; box-sizing:content-box; word-wrap:break-word; height:0 !important; min-height:0 !important; overflow:hidden;"/>',
// line-height is conditionally included because IE7/IE8/old Opera do not return the correct value.
copyStyle = [
'fontFamily',
'fontSize',
'fontWeight',
'fontStyle',
'letterSpacing',
'textTransform',
'wordSpacing',
'textIndent'
],
oninput = 'oninput',
onpropertychange = 'onpropertychange',
// to keep track which textarea is being mirrored when adjust() is called.
mirrored,
// the mirror element, which is used to calculate what size the mirrored element should be.
mirror = $(copy).data('autosize', true)[0];
// test that line-height can be accurately copied.
mirror.style.lineHeight = '99px';
if ($(mirror).css(lineHeight) === '99px') {
copyStyle.push(lineHeight);
}
mirror.style.lineHeight = '';
$.fn.autosize = function (options) {
options = $.extend({}, defaults, options || {});
if (mirror.parentNode !== document.body) {
$(document.body).append(mirror);
}
return this.each(function () {
var
ta = this,
$ta = $(ta),
minHeight,
maxHeight,
resize,
boxOffset = 0,
callback = $.isFunction(options.callback);
if ($ta.data('autosize')) {
// exit if autosize has already been applied, or if the textarea is the mirror element.
return;
}
if ($ta.css('box-sizing') === borderBox || $ta.css('-moz-box-sizing') === borderBox || $ta.css('-webkit-box-sizing') === borderBox){
boxOffset = $ta.outerHeight() - $ta.height();
}
// IE8 and lower return 'auto', which parses to NaN, if no min-height is set.
minHeight = Math.max(parseInt($ta.css('minHeight'), 10) - boxOffset || 0, $ta.height());
resize = ($ta.css('resize') === 'none' || $ta.css('resize') === 'vertical') ? 'none' : 'horizontal';
$ta.css({
overflow: hidden,
overflowY: hidden,
wordWrap: 'break-word',
resize: resize
}).data('autosize', true);
function initMirror() {
mirrored = ta;
mirror.className = options.className;
maxHeight = parseInt($ta.css('maxHeight'), 10);
// mirror is a duplicate textarea located off-screen that
// is automatically updated to contain the same text as the
// original textarea. mirror always has a height of 0.
// This gives a cross-browser supported way getting the actual
// height of the text, through the scrollTop property.
$.each(copyStyle, function(i, val){
mirror.style[val] = $ta.css(val);
});
// The textarea overflow is probably now hidden, but Chrome doesn't reflow the text to account for the
// new space made available by removing the scrollbars. This workaround causes Chrome to reflow the text.
if (oninput in ta) {
var width = ta.style.width;
ta.style.width = '0px';
var discard = ta.offsetWidth; // trigger a reflow
ta.style.width = width;
}
}
// Using mainly bare JS in this function because it is going
// to fire very often while typing, and needs to very efficient.
function adjust() {
var height, overflow, original;
if (mirrored !== ta) {
initMirror();
}
mirror.value = ta.value + options.append;
mirror.style.overflowY = ta.style.overflowY;
original = parseInt(ta.style.height,10);
// Update the width in case the original textarea width has changed
// A floor of 0 is needed because IE8 returns a negative value for hidden textareas, raising an error.
mirror.style.width = Math.max($ta.width(), 0) + 'px';
// Needed for IE8 and lower to reliably return the correct scrollTop
mirror.scrollTop = 0;
mirror.scrollTop = 9e4;
// Using scrollTop rather than scrollHeight because scrollHeight is non-standard and includes padding.
height = mirror.scrollTop;
if (maxHeight && height > maxHeight) {
height = maxHeight;
overflow = 'scroll';
} else if (height < minHeight) {
height = minHeight;
}
height += boxOffset;
ta.style.overflowY = overflow || hidden;
if (original !== height) {
ta.style.height = height + 'px';
if (callback) {
options.callback.call(ta,ta);
}
}
}
if (onpropertychange in ta) {
if (oninput in ta) {
// Detects IE9. IE9 does not fire onpropertychange or oninput for deletions,
// so binding to onkeyup to catch most of those occassions. There is no way that I
// know of to detect something like 'cut' in IE9.
ta[oninput] = ta.onkeyup = adjust;
} else {
// IE7 / IE8
ta[onpropertychange] = function(){
if(event.propertyName === 'value'){
adjust();
}
};
}
} else {
// Modern Browsers
ta[oninput] = adjust;
}
$(window).on('resize', function(){
active = false;
adjust();
});
// Allow for manual triggering if needed.
$ta.on('autosize', function(){
active = false;
adjust();
});
// Call adjust in case the textarea already contains text.
adjust();
});
};
}(window.jQuery || window.Zepto));