summaryrefslogtreecommitdiff
path: root/lib/dijit/form/_ExpandingTextAreaMixin.js.uncompressed.js
blob: c066568638cc0da55a5535bd08ac7fb20c5a6a52 (plain)
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
define("dijit/form/_ExpandingTextAreaMixin", [
	"dojo/_base/declare", // declare
	"dojo/dom-construct", // domConstruct.create
	"dojo/_base/lang", // lang.hitch
	"dojo/_base/window" // win.body
], function(declare, domConstruct, lang, win){

	// module:
	//		dijit/form/_ExpandingTextAreaMixin
	// summary:
	//		Mixin for textarea widgets to add auto-expanding capability

	// feature detection
	var needsHelpShrinking;

	return declare("dijit.form._ExpandingTextAreaMixin", null, {
		// summary:
		//		Mixin for textarea widgets to add auto-expanding capability

		_setValueAttr: function(){
			this.inherited(arguments);
			this.resize();
		},

		postCreate: function(){
			this.inherited(arguments);
			var textarea = this.textbox;

			if(needsHelpShrinking == undefined){
				var te = domConstruct.create('textarea', {rows:"5", cols:"20", value: ' ', style: {zoom:1, overflow:'hidden', visibility:'hidden', position:'absolute', border:"0px solid black", padding:"0px"}}, win.body(), "last");
				needsHelpShrinking = te.scrollHeight >= te.clientHeight;
				win.body().removeChild(te);
			}
			this.connect(textarea, "onscroll", "_resizeLater");
			this.connect(textarea, "onresize", "_resizeLater");
			this.connect(textarea, "onfocus", "_resizeLater");
			textarea.style.overflowY = "hidden";
			this._estimateHeight();
			this._resizeLater();
		},

		_onInput: function(e){
			this.inherited(arguments);
			this.resize();
		},

		_estimateHeight: function(){
			// summary:
			//		Approximate the height when the textarea is invisible with the number of lines in the text.
			//		Fails when someone calls setValue with a long wrapping line, but the layout fixes itself when the user clicks inside so . . .
			//		In IE, the resize event is supposed to fire when the textarea becomes visible again and that will correct the size automatically.
			//
			var textarea = this.textbox;
			textarea.style.height = "auto";
			// #rows = #newlines+1
			// Note: on Moz, the following #rows appears to be 1 too many.
			// Actually, Moz is reserving room for the scrollbar.
			// If you increase the font size, this behavior becomes readily apparent as the last line gets cut off without the +1.
			textarea.rows = (textarea.value.match(/\n/g) || []).length + 2;
		},

		_resizeLater: function(){
			this.defer("resize");
		},

		resize: function(){
			// summary:
			//		Resizes the textarea vertically (should be called after a style/value change)
			function textareaScrollHeight(){
				var empty = false;
				if(textarea.value === ''){
					textarea.value = ' ';
					empty = true;
				}
				var sh = textarea.scrollHeight;
				if(empty){ textarea.value = ''; }
				return sh;
			}

			var textarea = this.textbox;
			if(textarea.style.overflowY == "hidden"){ textarea.scrollTop = 0; }
			if(this.busyResizing){ return; }
			this.busyResizing = true;
			if(textareaScrollHeight() || textarea.offsetHeight){
				var currentHeight = textarea.style.height;
				if(!(/px/.test(currentHeight))){
					currentHeight = textareaScrollHeight();
					textarea.rows = 1;
					textarea.style.height = currentHeight + "px";
				}
				var newH = Math.max(Math.max(textarea.offsetHeight, parseInt(currentHeight)) - textarea.clientHeight, 0) + textareaScrollHeight();
				var newHpx = newH + "px";
				if(newHpx != textarea.style.height){
					textarea.rows = 1;
					textarea.style.height = newHpx;
				}
				if(needsHelpShrinking){
					var	origScrollHeight = textareaScrollHeight(),
						newScrollHeight = origScrollHeight,
						origMinHeight = textarea.style.minHeight,
						decrement = 4, // not too fast, not too slow
						thisScrollHeight;
					textarea.style.minHeight = newHpx; // maintain current height
					textarea.style.height = "auto"; // allow scrollHeight to change
					while(newH > 0){
						textarea.style.minHeight = Math.max(newH - decrement, 4) + "px";
						thisScrollHeight = textareaScrollHeight();
						var change = newScrollHeight - thisScrollHeight;
						newH -= change;
						if(change < decrement){
							break; // scrollHeight didn't shrink
						}
						newScrollHeight = thisScrollHeight;
						decrement <<= 1;
					}
					textarea.style.height = newH + "px";
					textarea.style.minHeight = origMinHeight;
				}
				textarea.style.overflowY = textareaScrollHeight() > textarea.clientHeight ? "auto" : "hidden";
			}else{
				// hidden content of unknown size
				this._estimateHeight();
			}
			this.busyResizing = false;
		}
	});
});