1 | define([ |
---|
2 | "dojo/_base/declare", // declare |
---|
3 | "dojo/dom-construct", // domConstruct.create |
---|
4 | "dojo/has", |
---|
5 | "dojo/_base/lang", // lang.hitch |
---|
6 | "dojo/on", |
---|
7 | "dojo/_base/window", // win.body |
---|
8 | "../Viewport" |
---|
9 | ], function(declare, domConstruct, has, lang, on, win, Viewport){ |
---|
10 | |
---|
11 | // module: |
---|
12 | // dijit/form/_ExpandingTextAreaMixin |
---|
13 | |
---|
14 | // feature detection, true for mozilla and webkit |
---|
15 | has.add("textarea-needs-help-shrinking", function(){ |
---|
16 | var body = win.body(), // note: if multiple documents exist, doesn't matter which one we use |
---|
17 | te = domConstruct.create('textarea', { |
---|
18 | rows:"5", |
---|
19 | cols:"20", |
---|
20 | value: ' ', |
---|
21 | style: {zoom:1, fontSize:"12px", height:"96px", overflow:'hidden', visibility:'hidden', position:'absolute', border:"5px solid white", margin:"0", padding:"0", boxSizing: 'border-box', MsBoxSizing: 'border-box', WebkitBoxSizing: 'border-box', MozBoxSizing: 'border-box' } |
---|
22 | }, body, "last"); |
---|
23 | var needsHelpShrinking = te.scrollHeight >= te.clientHeight; |
---|
24 | body.removeChild(te); |
---|
25 | return needsHelpShrinking; |
---|
26 | }); |
---|
27 | |
---|
28 | return declare("dijit.form._ExpandingTextAreaMixin", null, { |
---|
29 | // summary: |
---|
30 | // Mixin for textarea widgets to add auto-expanding capability |
---|
31 | |
---|
32 | _setValueAttr: function(){ |
---|
33 | this.inherited(arguments); |
---|
34 | this.resize(); |
---|
35 | }, |
---|
36 | |
---|
37 | postCreate: function(){ |
---|
38 | this.inherited(arguments); |
---|
39 | var textarea = this.textbox; |
---|
40 | textarea.style.overflowY = "hidden"; |
---|
41 | this.own(on(textarea, "focus, resize", lang.hitch(this, "_resizeLater"))); |
---|
42 | }, |
---|
43 | |
---|
44 | startup: function(){ |
---|
45 | this.inherited(arguments); |
---|
46 | this.own(Viewport.on("resize", lang.hitch(this, "_resizeLater"))); |
---|
47 | this._resizeLater(); |
---|
48 | }, |
---|
49 | |
---|
50 | _onInput: function(e){ |
---|
51 | this.inherited(arguments); |
---|
52 | this.resize(); |
---|
53 | }, |
---|
54 | |
---|
55 | _estimateHeight: function(){ |
---|
56 | // summary: |
---|
57 | // Approximate the height when the textarea is invisible with the number of lines in the text. |
---|
58 | // Fails when someone calls setValue with a long wrapping line, but the layout fixes itself when the user clicks inside so . . . |
---|
59 | // In IE, the resize event is supposed to fire when the textarea becomes visible again and that will correct the size automatically. |
---|
60 | // |
---|
61 | var textarea = this.textbox; |
---|
62 | // #rows = #newlines+1 |
---|
63 | textarea.rows = (textarea.value.match(/\n/g) || []).length + 1; |
---|
64 | }, |
---|
65 | |
---|
66 | _resizeLater: function(){ |
---|
67 | this.defer("resize"); |
---|
68 | }, |
---|
69 | |
---|
70 | resize: function(){ |
---|
71 | // summary: |
---|
72 | // Resizes the textarea vertically (should be called after a style/value change) |
---|
73 | |
---|
74 | var textarea = this.textbox; |
---|
75 | |
---|
76 | function textareaScrollHeight(){ |
---|
77 | var empty = false; |
---|
78 | if(textarea.value === ''){ |
---|
79 | textarea.value = ' '; |
---|
80 | empty = true; |
---|
81 | } |
---|
82 | var sh = textarea.scrollHeight; |
---|
83 | if(empty){ textarea.value = ''; } |
---|
84 | return sh; |
---|
85 | } |
---|
86 | |
---|
87 | if(textarea.style.overflowY == "hidden"){ textarea.scrollTop = 0; } |
---|
88 | if(this.busyResizing){ return; } |
---|
89 | this.busyResizing = true; |
---|
90 | if(textareaScrollHeight() || textarea.offsetHeight){ |
---|
91 | var newH = textareaScrollHeight() + Math.max(textarea.offsetHeight - textarea.clientHeight, 0); |
---|
92 | var newHpx = newH + "px"; |
---|
93 | if(newHpx != textarea.style.height){ |
---|
94 | textarea.style.height = newHpx; |
---|
95 | textarea.rows = 1; // rows can act like a minHeight if not cleared |
---|
96 | } |
---|
97 | if(has("textarea-needs-help-shrinking")){ |
---|
98 | var origScrollHeight = textareaScrollHeight(), |
---|
99 | newScrollHeight = origScrollHeight, |
---|
100 | origMinHeight = textarea.style.minHeight, |
---|
101 | decrement = 4, // not too fast, not too slow |
---|
102 | thisScrollHeight, |
---|
103 | origScrollTop = textarea.scrollTop; |
---|
104 | textarea.style.minHeight = newHpx; // maintain current height |
---|
105 | textarea.style.height = "auto"; // allow scrollHeight to change |
---|
106 | while(newH > 0){ |
---|
107 | textarea.style.minHeight = Math.max(newH - decrement, 4) + "px"; |
---|
108 | thisScrollHeight = textareaScrollHeight(); |
---|
109 | var change = newScrollHeight - thisScrollHeight; |
---|
110 | newH -= change; |
---|
111 | if(change < decrement){ |
---|
112 | break; // scrollHeight didn't shrink |
---|
113 | } |
---|
114 | newScrollHeight = thisScrollHeight; |
---|
115 | decrement <<= 1; |
---|
116 | } |
---|
117 | textarea.style.height = newH + "px"; |
---|
118 | textarea.style.minHeight = origMinHeight; |
---|
119 | textarea.scrollTop = origScrollTop; |
---|
120 | } |
---|
121 | textarea.style.overflowY = textareaScrollHeight() > textarea.clientHeight ? "auto" : "hidden"; |
---|
122 | if(textarea.style.overflowY == "hidden"){ textarea.scrollTop = 0; } |
---|
123 | }else{ |
---|
124 | // hidden content of unknown size |
---|
125 | this._estimateHeight(); |
---|
126 | } |
---|
127 | this.busyResizing = false; |
---|
128 | } |
---|
129 | }); |
---|
130 | }); |
---|