jQuery 3.6.1.
[readifood.git] / www / jquery.autosize.js
1 const map = (typeof Map === "function") ? new Map() : (function () {
2         const keys = [];
3         const values = [];
4
5         return {
6                 has(key) {
7                         return keys.indexOf(key) > -1;
8                 },
9                 get(key) {
10                         return values[keys.indexOf(key)];
11                 },
12                 set(key, value) {
13                         if (keys.indexOf(key) === -1) {
14                                 keys.push(key);
15                                 values.push(value);
16                         }
17                 },
18                 delete(key) {
19                         const index = keys.indexOf(key);
20                         if (index > -1) {
21                                 keys.splice(index, 1);
22                                 values.splice(index, 1);
23                         }
24                 },
25         }
26 })();
27
28 let createEvent = (name)=> new Event(name, {bubbles: true});
29 try {
30         new Event('test');
31 } catch(e) {
32         // IE does not support `new Event()`
33         createEvent = (name)=> {
34                 const evt = document.createEvent('Event');
35                 evt.initEvent(name, true, false);
36                 return evt;
37         };
38 }
39
40 function assign(ta) {
41         if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || map.has(ta)) return;
42
43         let heightOffset = null;
44         let clientWidth = null;
45         let cachedHeight = null;
46
47         function init() {
48                 const style = window.getComputedStyle(ta, null);
49
50                 if (style.resize === 'vertical') {
51                         ta.style.resize = 'none';
52                 } else if (style.resize === 'both') {
53                         ta.style.resize = 'horizontal';
54                 }
55
56                 if (style.boxSizing === 'content-box') {
57                         heightOffset = -(parseFloat(style.paddingTop)+parseFloat(style.paddingBottom));
58                 } else {
59                         heightOffset = parseFloat(style.borderTopWidth)+parseFloat(style.borderBottomWidth);
60                 }
61                 // Fix when a textarea is not on document body and heightOffset is Not a Number
62                 if (isNaN(heightOffset)) {
63                         heightOffset = 0;
64                 }
65
66                 update();
67         }
68
69         function changeOverflow(value) {
70                 {
71                         // Chrome/Safari-specific fix:
72                         // When the textarea y-overflow is hidden, Chrome/Safari do not reflow the text to account for the space
73                         // made available by removing the scrollbar. The following forces the necessary text reflow.
74                         const width = ta.style.width;
75                         ta.style.width = '0px';
76                         // Force reflow:
77                         /* jshint ignore:start */
78                         ta.offsetWidth;
79                         /* jshint ignore:end */
80                         ta.style.width = width;
81                 }
82
83                 ta.style.overflowY = value;
84         }
85
86         function getParentOverflows(el) {
87                 const arr = [];
88
89                 while (el && el.parentNode && el.parentNode instanceof Element) {
90                         if (el.parentNode.scrollTop) {
91                                 arr.push({
92                                         node: el.parentNode,
93                                         scrollTop: el.parentNode.scrollTop,
94                                 })
95                         }
96                         el = el.parentNode;
97                 }
98
99                 return arr;
100         }
101
102         function resize() {
103                 if (ta.scrollHeight === 0) {
104                         // If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM.
105                         return;
106                 }
107
108                 const overflows = getParentOverflows(ta);
109                 const docTop = document.documentElement && document.documentElement.scrollTop; // Needed for Mobile IE (ticket #240)
110
111                 ta.style.height = '';
112                 ta.style.height = (ta.scrollHeight+heightOffset)+'px';
113
114                 // used to check if an update is actually necessary on window.resize
115                 clientWidth = ta.clientWidth;
116
117                 // prevents scroll-position jumping
118                 overflows.forEach(el => {
119                         el.node.scrollTop = el.scrollTop
120                 });
121
122                 if (docTop) {
123                         document.documentElement.scrollTop = docTop;
124                 }
125         }
126
127         function update() {
128                 resize();
129
130                 const styleHeight = Math.round(parseFloat(ta.style.height));
131                 const computed = window.getComputedStyle(ta, null);
132
133                 // Using offsetHeight as a replacement for computed.height in IE, because IE does not account use of border-box
134                 var actualHeight = computed.boxSizing === 'content-box' ? Math.round(parseFloat(computed.height)) : ta.offsetHeight;
135
136                 // The actual height not matching the style height (set via the resize method) indicates that 
137                 // the max-height has been exceeded, in which case the overflow should be allowed.
138                 if (actualHeight < styleHeight) {
139                         if (computed.overflowY === 'hidden') {
140                                 changeOverflow('scroll');
141                                 resize();
142                                 actualHeight = computed.boxSizing === 'content-box' ? Math.round(parseFloat(window.getComputedStyle(ta, null).height)) : ta.offsetHeight;
143                         }
144                 } else {
145                         // Normally keep overflow set to hidden, to avoid flash of scrollbar as the textarea expands.
146                         if (computed.overflowY !== 'hidden') {
147                                 changeOverflow('hidden');
148                                 resize();
149                                 actualHeight = computed.boxSizing === 'content-box' ? Math.round(parseFloat(window.getComputedStyle(ta, null).height)) : ta.offsetHeight;
150                         }
151                 }
152
153                 if (cachedHeight !== actualHeight) {
154                         cachedHeight = actualHeight;
155                         const evt = createEvent('autosize:resized');
156                         try {
157                                 ta.dispatchEvent(evt);
158                         } catch (err) {
159                                 // Firefox will throw an error on dispatchEvent for a detached element
160                                 // https://bugzilla.mozilla.org/show_bug.cgi?id=889376
161                         }
162                 }
163         }
164
165         const pageResize = () => {
166                 if (ta.clientWidth !== clientWidth) {
167                         update();
168                 }
169         };
170
171         const destroy = (style => {
172                 window.removeEventListener('resize', pageResize, false);
173                 ta.removeEventListener('input', update, false);
174                 ta.removeEventListener('keyup', update, false);
175                 ta.removeEventListener('autosize:destroy', destroy, false);
176                 ta.removeEventListener('autosize:update', update, false);
177
178                 Object.keys(style).forEach(key => {
179                         ta.style[key] = style[key];
180                 });
181
182                 map.delete(ta);
183         }).bind(ta, {
184                 height: ta.style.height,
185                 resize: ta.style.resize,
186                 overflowY: ta.style.overflowY,
187                 overflowX: ta.style.overflowX,
188                 wordWrap: ta.style.wordWrap,
189         });
190
191         ta.addEventListener('autosize:destroy', destroy, false);
192
193         // IE9 does not fire onpropertychange or oninput for deletions,
194         // so binding to onkeyup to catch most of those events.
195         // There is no way that I know of to detect something like 'cut' in IE9.
196         if ('onpropertychange' in ta && 'oninput' in ta) {
197                 ta.addEventListener('keyup', update, false);
198         }
199
200         window.addEventListener('resize', pageResize, false);
201         ta.addEventListener('input', update, false);
202         ta.addEventListener('autosize:update', update, false);
203         ta.style.overflowX = 'hidden';
204         ta.style.wordWrap = 'break-word';
205
206         map.set(ta, {
207                 destroy,
208                 update,
209         });
210
211         init();
212 }
213
214 function destroy(ta) {
215         const methods = map.get(ta);
216         if (methods) {
217                 methods.destroy();
218         }
219 }
220
221 function update(ta) {
222         const methods = map.get(ta);
223         if (methods) {
224                 methods.update();
225         }
226 }
227
228 let autosize = null;
229
230 // Do nothing in Node.js environment and IE8 (or lower)
231 if (typeof window === 'undefined' || typeof window.getComputedStyle !== 'function') {
232         autosize = el => el;
233         autosize.destroy = el => el;
234         autosize.update = el => el;
235 } else {
236         autosize = (el, options) => {
237                 if (el) {
238                         Array.prototype.forEach.call(el.length ? el : [el], x => assign(x, options));
239                 }
240                 return el;
241         };
242         autosize.destroy = el => {
243                 if (el) {
244                         Array.prototype.forEach.call(el.length ? el : [el], destroy);
245                 }
246                 return el;
247         };
248         autosize.update = el => {
249                 if (el) {
250                         Array.prototype.forEach.call(el.length ? el : [el], update);
251                 }
252                 return el;
253         };
254 }
255
256 export default autosize;