PluginProbe
Booking Calendar / 11.9
Booking Calendar v11.9
11.9 11.8.4 11.8.3 11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 All 205 releases
booking / vendors / imask / esm / masked / base.js

base.js in Booking Calendar 11.9, at vendors/imask/esm/masked/base.js

423 lines 11.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import ChangeDetails from '../core/change-details.js';
2 import ContinuousTailDetails from '../core/continuous-tail-details.js';
3 import { isString, DIRECTION, objectIncludes, forceDirection } from '../core/utils.js';
4 import IMask from '../core/holder.js';
5
6 /** Append flags */
7
8 /** Extract flags */
9
10 // see https://github.com/microsoft/TypeScript/issues/6223
11
12 /** Provides common masking stuff */
13 class Masked {
14 /** */
15
16 /** */
17
18 /** Transforms value before mask processing */
19
20 /** Transforms each char before mask processing */
21
22 /** Validates if value is acceptable */
23
24 /** Does additional processing at the end of editing */
25
26 /** Format typed value to string */
27
28 /** Parse string to get typed value */
29
30 /** Enable characters overwriting */
31
32 /** */
33
34 /** */
35
36 /** */
37
38 /** */
39
40 constructor(opts) {
41 this._value = '';
42 this._update({
43 ...Masked.DEFAULTS,
44 ...opts
45 });
46 this._initialized = true;
47 }
48
49 /** Sets and applies new options */
50 updateOptions(opts) {
51 if (!this.optionsIsChanged(opts)) return;
52 this.withValueRefresh(this._update.bind(this, opts));
53 }
54
55 /** Sets new options */
56 _update(opts) {
57 Object.assign(this, opts);
58 }
59
60 /** Mask state */
61 get state() {
62 return {
63 _value: this.value,
64 _rawInputValue: this.rawInputValue
65 };
66 }
67 set state(state) {
68 this._value = state._value;
69 }
70
71 /** Resets value */
72 reset() {
73 this._value = '';
74 }
75 get value() {
76 return this._value;
77 }
78 set value(value) {
79 this.resolve(value, {
80 input: true
81 });
82 }
83
84 /** Resolve new value */
85 resolve(value, flags) {
86 if (flags === void 0) {
87 flags = {
88 input: true
89 };
90 }
91 this.reset();
92 this.append(value, flags, '');
93 this.doCommit();
94 }
95 get unmaskedValue() {
96 return this.value;
97 }
98 set unmaskedValue(value) {
99 this.resolve(value, {});
100 }
101 get typedValue() {
102 return this.parse ? this.parse(this.value, this) : this.unmaskedValue;
103 }
104 set typedValue(value) {
105 if (this.format) {
106 this.value = this.format(value, this);
107 } else {
108 this.unmaskedValue = String(value);
109 }
110 }
111
112 /** Value that includes raw user input */
113 get rawInputValue() {
114 return this.extractInput(0, this.displayValue.length, {
115 raw: true
116 });
117 }
118 set rawInputValue(value) {
119 this.resolve(value, {
120 raw: true
121 });
122 }
123 get displayValue() {
124 return this.value;
125 }
126 get isComplete() {
127 return true;
128 }
129 get isFilled() {
130 return this.isComplete;
131 }
132
133 /** Finds nearest input position in direction */
134 nearestInputPos(cursorPos, direction) {
135 return cursorPos;
136 }
137 totalInputPositions(fromPos, toPos) {
138 if (fromPos === void 0) {
139 fromPos = 0;
140 }
141 if (toPos === void 0) {
142 toPos = this.displayValue.length;
143 }
144 return Math.min(this.displayValue.length, toPos - fromPos);
145 }
146
147 /** Extracts value in range considering flags */
148 extractInput(fromPos, toPos, flags) {
149 if (fromPos === void 0) {
150 fromPos = 0;
151 }
152 if (toPos === void 0) {
153 toPos = this.displayValue.length;
154 }
155 return this.displayValue.slice(fromPos, toPos);
156 }
157
158 /** Extracts tail in range */
159 extractTail(fromPos, toPos) {
160 if (fromPos === void 0) {
161 fromPos = 0;
162 }
163 if (toPos === void 0) {
164 toPos = this.displayValue.length;
165 }
166 return new ContinuousTailDetails(this.extractInput(fromPos, toPos), fromPos);
167 }
168
169 /** Appends tail */
170 appendTail(tail) {
171 if (isString(tail)) tail = new ContinuousTailDetails(String(tail));
172 return tail.appendTo(this);
173 }
174
175 /** Appends char */
176 _appendCharRaw(ch, flags) {
177 if (!ch) return new ChangeDetails();
178 this._value += ch;
179 return new ChangeDetails({
180 inserted: ch,
181 rawInserted: ch
182 });
183 }
184
185 /** Appends char */
186 _appendChar(ch, flags, checkTail) {
187 if (flags === void 0) {
188 flags = {};
189 }
190 const consistentState = this.state;
191 let details;
192 [ch, details] = this.doPrepareChar(ch, flags);
193 if (ch) {
194 details = details.aggregate(this._appendCharRaw(ch, flags));
195
196 // TODO handle `skip`?
197
198 // try `autofix` lookahead
199 if (!details.rawInserted && this.autofix === 'pad') {
200 const noFixState = this.state;
201 this.state = consistentState;
202 let fixDetails = this.pad(flags);
203 const chDetails = this._appendCharRaw(ch, flags);
204 fixDetails = fixDetails.aggregate(chDetails);
205
206 // if fix was applied or
207 // if details are equal use skip restoring state optimization
208 if (chDetails.rawInserted || fixDetails.equals(details)) {
209 details = fixDetails;
210 } else {
211 this.state = noFixState;
212 }
213 }
214 }
215 if (details.inserted) {
216 let consistentTail;
217 let appended = this.doValidate(flags) !== false;
218 if (appended && checkTail != null) {
219 // validation ok, check tail
220 const beforeTailState = this.state;
221 if (this.overwrite === true) {
222 consistentTail = checkTail.state;
223 for (let i = 0; i < details.rawInserted.length; ++i) {
224 checkTail.unshift(this.displayValue.length - details.tailShift);
225 }
226 }
227 let tailDetails = this.appendTail(checkTail);
228 appended = tailDetails.rawInserted.length === checkTail.toString().length;
229
230 // not ok, try shift
231 if (!(appended && tailDetails.inserted) && this.overwrite === 'shift') {
232 this.state = beforeTailState;
233 consistentTail = checkTail.state;
234 for (let i = 0; i < details.rawInserted.length; ++i) {
235 checkTail.shift();
236 }
237 tailDetails = this.appendTail(checkTail);
238 appended = tailDetails.rawInserted.length === checkTail.toString().length;
239 }
240
241 // if ok, rollback state after tail
242 if (appended && tailDetails.inserted) this.state = beforeTailState;
243 }
244
245 // revert all if something went wrong
246 if (!appended) {
247 details = new ChangeDetails();
248 this.state = consistentState;
249 if (checkTail && consistentTail) checkTail.state = consistentTail;
250 }
251 }
252 return details;
253 }
254
255 /** Appends optional placeholder at the end */
256 _appendPlaceholder() {
257 return new ChangeDetails();
258 }
259
260 /** Appends optional eager placeholder at the end */
261 _appendEager() {
262 return new ChangeDetails();
263 }
264
265 /** Appends symbols considering flags */
266 append(str, flags, tail) {
267 if (!isString(str)) throw new Error('value should be string');
268 const checkTail = isString(tail) ? new ContinuousTailDetails(String(tail)) : tail;
269 if (flags != null && flags.tail) flags._beforeTailState = this.state;
270 let details;
271 [str, details] = this.doPrepare(str, flags);
272 for (let ci = 0; ci < str.length; ++ci) {
273 const d = this._appendChar(str[ci], flags, checkTail);
274 if (!d.rawInserted && !this.doSkipInvalid(str[ci], flags, checkTail)) break;
275 details.aggregate(d);
276 }
277 if ((this.eager === true || this.eager === 'append') && flags != null && flags.input && str) {
278 details.aggregate(this._appendEager());
279 }
280
281 // append tail but aggregate only tailShift
282 if (checkTail != null) {
283 details.tailShift += this.appendTail(checkTail).tailShift;
284 // TODO it's a good idea to clear state after appending ends
285 // but it causes bugs when one append calls another (when dynamic dispatch set rawInputValue)
286 // this._resetBeforeTailState();
287 }
288 return details;
289 }
290 remove(fromPos, toPos) {
291 if (fromPos === void 0) {
292 fromPos = 0;
293 }
294 if (toPos === void 0) {
295 toPos = this.displayValue.length;
296 }
297 this._value = this.displayValue.slice(0, fromPos) + this.displayValue.slice(toPos);
298 return new ChangeDetails();
299 }
300
301 /** Calls function and reapplies current value */
302 withValueRefresh(fn) {
303 if (this._refreshing || !this._initialized) return fn();
304 this._refreshing = true;
305 const rawInput = this.rawInputValue;
306 const value = this.value;
307 const ret = fn();
308 this.rawInputValue = rawInput;
309 // append lost trailing chars at the end
310 if (this.value && this.value !== value && value.indexOf(this.value) === 0) {
311 this.append(value.slice(this.displayValue.length), {}, '');
312 this.doCommit();
313 }
314 delete this._refreshing;
315 return ret;
316 }
317 runIsolated(fn) {
318 if (this._isolated || !this._initialized) return fn(this);
319 this._isolated = true;
320 const state = this.state;
321 const ret = fn(this);
322 this.state = state;
323 delete this._isolated;
324 return ret;
325 }
326 doSkipInvalid(ch, flags, checkTail) {
327 return Boolean(this.skipInvalid);
328 }
329
330 /** Prepares string before mask processing */
331 doPrepare(str, flags) {
332 if (flags === void 0) {
333 flags = {};
334 }
335 return ChangeDetails.normalize(this.prepare ? this.prepare(str, this, flags) : str);
336 }
337
338 /** Prepares each char before mask processing */
339 doPrepareChar(str, flags) {
340 if (flags === void 0) {
341 flags = {};
342 }
343 return ChangeDetails.normalize(this.prepareChar ? this.prepareChar(str, this, flags) : str);
344 }
345
346 /** Validates if value is acceptable */
347 doValidate(flags) {
348 return (!this.validate || this.validate(this.value, this, flags)) && (!this.parent || this.parent.doValidate(flags));
349 }
350
351 /** Does additional processing at the end of editing */
352 doCommit() {
353 if (this.commit) this.commit(this.value, this);
354 }
355 splice(start, deleteCount, inserted, removeDirection, flags) {
356 if (inserted === void 0) {
357 inserted = '';
358 }
359 if (removeDirection === void 0) {
360 removeDirection = DIRECTION.NONE;
361 }
362 if (flags === void 0) {
363 flags = {
364 input: true
365 };
366 }
367 const tailPos = start + deleteCount;
368 const tail = this.extractTail(tailPos);
369 const eagerRemove = this.eager === true || this.eager === 'remove';
370 let oldRawValue;
371 if (eagerRemove) {
372 removeDirection = forceDirection(removeDirection);
373 oldRawValue = this.extractInput(0, tailPos, {
374 raw: true
375 });
376 }
377 let startChangePos = start;
378 const details = new ChangeDetails();
379
380 // if it is just deletion without insertion
381 if (removeDirection !== DIRECTION.NONE) {
382 startChangePos = this.nearestInputPos(start, deleteCount > 1 && start !== 0 && !eagerRemove ? DIRECTION.NONE : removeDirection);
383
384 // adjust tailShift if start was aligned
385 details.tailShift = startChangePos - start;
386 }
387 details.aggregate(this.remove(startChangePos));
388 if (eagerRemove && removeDirection !== DIRECTION.NONE && oldRawValue === this.rawInputValue) {
389 if (removeDirection === DIRECTION.FORCE_LEFT) {
390 let valLength;
391 while (oldRawValue === this.rawInputValue && (valLength = this.displayValue.length)) {
392 details.aggregate(new ChangeDetails({
393 tailShift: -1
394 })).aggregate(this.remove(valLength - 1));
395 }
396 } else if (removeDirection === DIRECTION.FORCE_RIGHT) {
397 tail.unshift();
398 }
399 }
400 return details.aggregate(this.append(inserted, flags, tail));
401 }
402 maskEquals(mask) {
403 return this.mask === mask;
404 }
405 optionsIsChanged(opts) {
406 return !objectIncludes(this, opts);
407 }
408 typedValueEquals(value) {
409 const tval = this.typedValue;
410 return value === tval || Masked.EMPTY_VALUES.includes(value) && Masked.EMPTY_VALUES.includes(tval) || (this.format ? this.format(value, this) === this.format(this.typedValue, this) : false);
411 }
412 pad(flags) {
413 return new ChangeDetails();
414 }
415 }
416 Masked.DEFAULTS = {
417 skipInvalid: true
418 };
419 Masked.EMPTY_VALUES = [undefined, null, ''];
420 IMask.Masked = Masked;
421
422 export { Masked as default };
423