@lemonadejs/calendar 5.2.1 → 5.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.js +633 -3430
- package/dist/style.css +3 -4
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -6,6 +6,13 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
6
6
|
var Modal = require('@lemonadejs/modal');
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
if (! utils && typeof (require) === 'function') {
|
|
10
|
+
var utils = require('@jsuites/utils');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const Helpers = utils.Helpers;
|
|
14
|
+
const Mask = utils.Mask;
|
|
15
|
+
|
|
9
16
|
; (function (global, factory) {
|
|
10
17
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
11
18
|
typeof define === 'function' && define.amd ? define(factory) :
|
|
@@ -51,2960 +58,200 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
51
58
|
}
|
|
52
59
|
}
|
|
53
60
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
component.two = function(value) {
|
|
64
|
-
value = '' + value;
|
|
65
|
-
if (value.length === 1) {
|
|
66
|
-
value = '0' + value;
|
|
67
|
-
}
|
|
68
|
-
return value;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
component.isValidDate = function(d) {
|
|
72
|
-
return d instanceof Date && !isNaN(d.getTime());
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
component.isValidDateFormat = function(date, format) {
|
|
76
|
-
if (typeof date === 'string') {
|
|
77
|
-
// Check format: YYYY-MM-DD using regex
|
|
78
|
-
const match = date.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
79
|
-
if (match) {
|
|
80
|
-
const year = Number(match[1]);
|
|
81
|
-
const month = Number(match[2]) - 1;
|
|
82
|
-
const day = Number(match[3]);
|
|
83
|
-
const parsed = new Date(Date.UTC(year, month, day));
|
|
84
|
-
// Return
|
|
85
|
-
return parsed.getUTCFullYear() === year && parsed.getUTCMonth() === month && parsed.getUTCDate() === day;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
component.toString = function (date, dateOnly) {
|
|
93
|
-
let y = null;
|
|
94
|
-
let m = null;
|
|
95
|
-
let d = null;
|
|
96
|
-
let h = null;
|
|
97
|
-
let i = null;
|
|
98
|
-
let s = null;
|
|
99
|
-
|
|
100
|
-
if (Array.isArray(date)) {
|
|
101
|
-
y = date[0];
|
|
102
|
-
m = date[1];
|
|
103
|
-
d = date[2];
|
|
104
|
-
h = date[3];
|
|
105
|
-
i = date[4];
|
|
106
|
-
s = date[5];
|
|
107
|
-
} else {
|
|
108
|
-
if (! date) {
|
|
109
|
-
date = new Date();
|
|
110
|
-
}
|
|
111
|
-
y = date.getUTCFullYear();
|
|
112
|
-
m = date.getUTCMonth() + 1;
|
|
113
|
-
d = date.getUTCDate();
|
|
114
|
-
h = date.getUTCHours();
|
|
115
|
-
i = date.getUTCMinutes();
|
|
116
|
-
s = date.getUTCSeconds();
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (dateOnly === true) {
|
|
120
|
-
return component.two(y) + '-' + component.two(m) + '-' + component.two(d);
|
|
121
|
-
} else {
|
|
122
|
-
return component.two(y) + '-' + component.two(m) + '-' + component.two(d) + ' ' + component.two(h) + ':' + component.two(i) + ':' + component.two(s);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
component.toArray = function (value) {
|
|
127
|
-
let date = value.split(((value.indexOf('T') !== -1) ? 'T' : ' '));
|
|
128
|
-
let time = date[1];
|
|
129
|
-
|
|
130
|
-
date = date[0].split('-');
|
|
131
|
-
let y = parseInt(date[0]);
|
|
132
|
-
let m = parseInt(date[1]);
|
|
133
|
-
let d = parseInt(date[2]);
|
|
134
|
-
let h = 0;
|
|
135
|
-
let i = 0;
|
|
136
|
-
|
|
137
|
-
if (time) {
|
|
138
|
-
time = time.split(':');
|
|
139
|
-
h = parseInt(time[0]);
|
|
140
|
-
i = parseInt(time[1]);
|
|
141
|
-
}
|
|
142
|
-
return [y, m, d, h, i, 0];
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
component.arrayToStringDate = function(arr) {
|
|
146
|
-
return component.toString(arr, false);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
component.dateToNum = function(jsDate) {
|
|
150
|
-
if (typeof(jsDate) === 'string') {
|
|
151
|
-
jsDate = new Date(jsDate + ' GMT+0');
|
|
152
|
-
}
|
|
153
|
-
let jsDateInMilliseconds = jsDate.getTime();
|
|
154
|
-
if (jsDateInMilliseconds >= excelLeapYearBug) {
|
|
155
|
-
jsDateInMilliseconds += millisecondsPerDay;
|
|
156
|
-
}
|
|
157
|
-
jsDateInMilliseconds -= excelInitialTime;
|
|
158
|
-
|
|
159
|
-
return jsDateInMilliseconds / millisecondsPerDay;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
component.numToDate = function(excelSerialNumber, asArray) {
|
|
163
|
-
// allow 0; only bail on null/undefined/empty
|
|
164
|
-
if (excelSerialNumber === null || excelSerialNumber === undefined || excelSerialNumber === '') {
|
|
165
|
-
return '';
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const MS_PER_DAY = 86_400_000;
|
|
169
|
-
const SEC_PER_DAY = 86_400;
|
|
170
|
-
|
|
171
|
-
// Excel day 0 is 1899-12-31 (with the fake 1900-02-29 at serial 60)
|
|
172
|
-
const EXCEL_DAY0_UTC_MS = Date.UTC(1899, 11, 31);
|
|
173
|
-
|
|
174
|
-
let wholeDays = Math.floor(excelSerialNumber);
|
|
175
|
-
let fractionalDay = excelSerialNumber - wholeDays;
|
|
176
|
-
|
|
177
|
-
// Fix the 1900 leap-year bug: shift serials >= 60 back one day
|
|
178
|
-
if (wholeDays >= 60) wholeDays -= 1;
|
|
179
|
-
|
|
180
|
-
// Build midnight UTC of the day
|
|
181
|
-
let ms = EXCEL_DAY0_UTC_MS + wholeDays * MS_PER_DAY;
|
|
182
|
-
|
|
183
|
-
// Add time part using integer seconds to avoid FP jitter
|
|
184
|
-
const seconds = Math.round(fractionalDay * SEC_PER_DAY);
|
|
185
|
-
ms += seconds * 1000;
|
|
186
|
-
|
|
187
|
-
const d = new Date(ms);
|
|
188
|
-
|
|
189
|
-
const arr = [
|
|
190
|
-
d.getUTCFullYear(),
|
|
191
|
-
component.two(d.getUTCMonth() + 1),
|
|
192
|
-
component.two(d.getUTCDate()),
|
|
193
|
-
component.two(d.getUTCHours()),
|
|
194
|
-
component.two(d.getUTCMinutes()),
|
|
195
|
-
component.two(d.getUTCSeconds()),
|
|
196
|
-
];
|
|
197
|
-
|
|
198
|
-
return asArray ? arr : component.toString(arr, false);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
component.prettify = function (d, texts) {
|
|
202
|
-
if (! texts) {
|
|
203
|
-
texts = {
|
|
204
|
-
justNow: 'Just now',
|
|
205
|
-
xMinutesAgo: '{0}m ago',
|
|
206
|
-
xHoursAgo: '{0}h ago',
|
|
207
|
-
xDaysAgo: '{0}d ago',
|
|
208
|
-
xWeeksAgo: '{0}w ago',
|
|
209
|
-
xMonthsAgo: '{0} mon ago',
|
|
210
|
-
xYearsAgo: '{0}y ago',
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (d.indexOf('GMT') === -1 && d.indexOf('Z') === -1) {
|
|
215
|
-
d += ' GMT';
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
let d1 = new Date();
|
|
219
|
-
let d2 = new Date(d);
|
|
220
|
-
let total = parseInt((d1 - d2) / 1000 / 60);
|
|
221
|
-
|
|
222
|
-
const format = (t, o) => {
|
|
223
|
-
return t.replace('{0}', o);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (! total) {
|
|
227
|
-
return texts.justNow;
|
|
228
|
-
} else if (total < 90) {
|
|
229
|
-
return format(texts.xMinutesAgo, total);
|
|
230
|
-
} else if (total < 1440) { // One day
|
|
231
|
-
return format(texts.xHoursAgo, Math.round(total / 60));
|
|
232
|
-
} else if (total < 20160) { // 14 days
|
|
233
|
-
return format(texts.xDaysAgo, Math.round(total / 1440));
|
|
234
|
-
} else if (total < 43200) { // 30 days
|
|
235
|
-
return format(texts.xWeeksAgo, Math.round(total / 10080));
|
|
236
|
-
} else if (total < 1036800) { // 24 months
|
|
237
|
-
return format(texts.xMonthsAgo, Math.round(total / 43200));
|
|
238
|
-
} else { // 24 months+
|
|
239
|
-
return format(texts.xYearsAgo, Math.round(total / 525600));
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
component.prettifyAll = function () {
|
|
244
|
-
let elements = document.querySelectorAll('.prettydate');
|
|
245
|
-
for (let i = 0; i < elements.length; i++) {
|
|
246
|
-
if (elements[i].getAttribute('data-date')) {
|
|
247
|
-
elements[i].innerHTML = component.prettify(elements[i].getAttribute('data-date'));
|
|
248
|
-
} else {
|
|
249
|
-
if (elements[i].innerHTML) {
|
|
250
|
-
elements[i].setAttribute('title', elements[i].innerHTML);
|
|
251
|
-
elements[i].setAttribute('data-date', elements[i].innerHTML);
|
|
252
|
-
elements[i].innerHTML = component.prettify(elements[i].innerHTML);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// Compatibility with jSuites
|
|
259
|
-
component.now = component.toString;
|
|
260
|
-
|
|
261
|
-
const weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
|
262
|
-
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
|
|
263
|
-
|
|
264
|
-
const translate = function(t) {
|
|
265
|
-
if (typeof(document) !== "undefined" && document.dictionary) {
|
|
266
|
-
return document.dictionary[t] || t;
|
|
267
|
-
} else {
|
|
268
|
-
return t;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
Object.defineProperty(component, 'weekdays', {
|
|
273
|
-
get: function () {
|
|
274
|
-
return weekdays.map(function(v) {
|
|
275
|
-
return translate(v);
|
|
276
|
-
});
|
|
277
|
-
},
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
Object.defineProperty(component, 'weekdaysShort', {
|
|
281
|
-
get: function () {
|
|
282
|
-
return weekdays.map(function(v) {
|
|
283
|
-
return translate(v).substring(0,3);
|
|
284
|
-
});
|
|
285
|
-
},
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
Object.defineProperty(component, 'months', {
|
|
289
|
-
get: function () {
|
|
290
|
-
return months.map(function(v) {
|
|
291
|
-
return translate(v);
|
|
292
|
-
});
|
|
293
|
-
},
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
Object.defineProperty(component, 'monthsShort', {
|
|
297
|
-
get: function () {
|
|
298
|
-
return months.map(function(v) {
|
|
299
|
-
return translate(v).substring(0,3);
|
|
300
|
-
});
|
|
301
|
-
},
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
return component;
|
|
305
|
-
})();
|
|
306
|
-
|
|
307
|
-
const Mask = (function Mask() {
|
|
308
|
-
// Currency
|
|
309
|
-
const tokens = {
|
|
310
|
-
// Text
|
|
311
|
-
text: [ '@', '&' ],
|
|
312
|
-
// Number
|
|
313
|
-
fraction: [ '#{0,1}.*?\\?+\\/[0-9?]+' ],
|
|
314
|
-
// Currency tokens
|
|
315
|
-
currency: [ '#(.{1})##0?(.{1}0+)?( ?;(.*)?)?' ],
|
|
316
|
-
// Scientific
|
|
317
|
-
scientific: [ '[0#]+([.,]{1}0*#*)?E{1}\\+0+' ],
|
|
318
|
-
// Percentage
|
|
319
|
-
percentage: [ '[0#]+([.,]{1}0*#*)?%' ],
|
|
320
|
-
// Number
|
|
321
|
-
numeric: [ '[0#]+([.,]{1}0*#*)?', '#+' ],
|
|
322
|
-
// Data tokens
|
|
323
|
-
datetime: [ 'YYYY', 'YYY', 'YY', 'MMMMM', 'MMMM', 'MMM', 'MM', 'DDDDD', 'DDDD', 'DDD', 'DD', 'DY', 'DAY', 'WD', 'D', 'Q', 'MONTH', 'MON', 'HH24', 'HH12', 'HH', '\\[H\\]', 'H', 'AM/PM', 'MI', 'SS', 'MS', 'S', 'Y', 'M', 'I' ],
|
|
324
|
-
// Other
|
|
325
|
-
general: [ 'A', '0', '\\?', '\\*', ',,M', ',,,B', '[0-9a-zA-Z\\$]+', '_\\(', '_\\)', '\\(', '\\)', '_-', '.']
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// All expressions
|
|
329
|
-
const allExpressions = [].concat(tokens.fraction, tokens.currency, tokens.datetime, tokens.percentage, tokens.scientific, tokens.numeric, tokens.text, tokens.general).join('|');
|
|
330
|
-
|
|
331
|
-
// Pre-compile all regexes once at initialization for better performance
|
|
332
|
-
const compiledTokens = {};
|
|
333
|
-
const tokenPriority = ['fraction', 'currency', 'scientific', 'percentage', 'numeric', 'datetime', 'text', 'general'];
|
|
334
|
-
|
|
335
|
-
// Initialize compiled regexes
|
|
336
|
-
for (const type of tokenPriority) {
|
|
337
|
-
compiledTokens[type] = tokens[type].map(pattern => ({
|
|
338
|
-
regex: new RegExp('^' + pattern + '$', 'gi'),
|
|
339
|
-
method: pattern
|
|
340
|
-
}));
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// Pre-compile regex for getTokens function
|
|
344
|
-
const allExpressionsRegex = new RegExp(allExpressions, 'gi');
|
|
345
|
-
|
|
346
|
-
// Pre-compile currency symbol regexes for autoCastingCurrency
|
|
347
|
-
const knownSymbols = ['$', '€', '£', '¥', '₹', '₽', '₩', '₫', 'R$', 'CHF', 'AED'];
|
|
348
|
-
const currencyRegexes = knownSymbols.map(s => ({
|
|
349
|
-
symbol: s,
|
|
350
|
-
regex: new RegExp(`^${s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')}(\\s?)`)
|
|
351
|
-
}));
|
|
352
|
-
|
|
353
|
-
const hiddenCaret = "\u200B";
|
|
354
|
-
|
|
355
|
-
// Labels
|
|
356
|
-
const weekDaysFull = Helpers.weekdays;
|
|
357
|
-
const weekDays = Helpers.weekdaysShort;
|
|
358
|
-
const monthsFull = Helpers.months;
|
|
359
|
-
const months = Helpers.monthsShort;
|
|
360
|
-
|
|
361
|
-
// Helpers
|
|
362
|
-
|
|
363
|
-
const focus = function(el) {
|
|
364
|
-
if (el.textContent.length) {
|
|
365
|
-
// Handle contenteditable elements
|
|
366
|
-
const range = document.createRange();
|
|
367
|
-
const sel = window.getSelection();
|
|
368
|
-
|
|
369
|
-
let node = el;
|
|
370
|
-
// Go as deep as possible to the last text node
|
|
371
|
-
while (node.lastChild) node = node.lastChild;
|
|
372
|
-
// Ensure it's a text node
|
|
373
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
374
|
-
range.setStart(node, node.length);
|
|
375
|
-
} else {
|
|
376
|
-
range.setStart(node, node.childNodes.length);
|
|
377
|
-
}
|
|
378
|
-
range.collapse(true);
|
|
379
|
-
sel.removeAllRanges();
|
|
380
|
-
sel.addRange(range);
|
|
381
|
-
|
|
382
|
-
el.scrollLeft = el.scrollWidth;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Returns if the given value is considered blank
|
|
388
|
-
*/
|
|
389
|
-
const isBlank = function(v) {
|
|
390
|
-
return v === null || v === '' || typeof(v) === 'undefined';
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Receives a string from a method type and returns if it's a numeric method
|
|
395
|
-
*/
|
|
396
|
-
const isNumeric = function(t) {
|
|
397
|
-
return t === 'currency' || t === 'percentage' || t === '' || t === 'numeric';
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
const adjustPrecision = function(num) {
|
|
401
|
-
if (typeof num === 'number' && ! Number.isInteger(num)) {
|
|
402
|
-
const v = num.toString().split('.');
|
|
403
|
-
|
|
404
|
-
if (v[1] && v[1].length > 10) {
|
|
405
|
-
let t0 = 0;
|
|
406
|
-
const t1 = v[1][v[1].length - 2];
|
|
407
|
-
|
|
408
|
-
if (t1 == 0 || t1 == 9) {
|
|
409
|
-
for (let i = v[1].length - 2; i > 0; i--) {
|
|
410
|
-
if (t0 >= 0 && v[1][i] == t1) {
|
|
411
|
-
t0++;
|
|
412
|
-
if (t0 > 6) {
|
|
413
|
-
break;
|
|
414
|
-
}
|
|
415
|
-
} else {
|
|
416
|
-
t0 = 0;
|
|
417
|
-
break;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
if (t0) {
|
|
422
|
-
return parseFloat(parseFloat(num).toFixed(v[1].length - 1));
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
return num;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
/**
|
|
432
|
-
* Get the decimal defined in the mask configuration
|
|
433
|
-
*/
|
|
434
|
-
const getDecimal = function(v) {
|
|
435
|
-
let decimal;
|
|
436
|
-
if (this.decimal) {
|
|
437
|
-
decimal = this.decimal;
|
|
438
|
-
} else {
|
|
439
|
-
if (this.locale) {
|
|
440
|
-
let t = Intl.NumberFormat(this.locale).format(1.1);
|
|
441
|
-
decimal = t[1];
|
|
442
|
-
} else {
|
|
443
|
-
if (! v) {
|
|
444
|
-
v = this.mask;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// Fixed regex: 0* means zero or more 0s before decimal separator
|
|
448
|
-
let e = new RegExp('0*([,.])0+', 'ig');
|
|
449
|
-
let t = e.exec(v);
|
|
450
|
-
if (t && t[1] && t[1].length === 1) {
|
|
451
|
-
decimal = t[1];
|
|
452
|
-
} else {
|
|
453
|
-
// Try the second pattern for # formats
|
|
454
|
-
e = new RegExp('#{1}(.{1})#+', 'ig');
|
|
455
|
-
t = e.exec(v);
|
|
456
|
-
if (t && t[1] && t[1].length === 1) {
|
|
457
|
-
if (t[1] === ',') {
|
|
458
|
-
decimal = '.';
|
|
459
|
-
} else if (t[1] === "'" || t[1] === '.') {
|
|
460
|
-
decimal = ',';
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
if (! decimal) {
|
|
466
|
-
decimal = '1.1'.toLocaleString().substring(1, 2);
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
if (decimal) {
|
|
472
|
-
return decimal;
|
|
473
|
-
} else {
|
|
474
|
-
return null;
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* Caret position getter
|
|
480
|
-
* `this` in this function should be the element with a caret
|
|
481
|
-
*/
|
|
482
|
-
const getCaretPosition = function(editableDiv) {
|
|
483
|
-
let caretPos = 0;
|
|
484
|
-
let sel = window.getSelection();
|
|
485
|
-
if (sel && sel.rangeCount > 0) {
|
|
486
|
-
let range = sel.getRangeAt(0);
|
|
487
|
-
let preRange = range.cloneRange();
|
|
488
|
-
preRange.selectNodeContents(editableDiv);
|
|
489
|
-
preRange.setEnd(range.endContainer, range.endOffset);
|
|
490
|
-
caretPos = preRange.toString().length;
|
|
491
|
-
}
|
|
492
|
-
return caretPos;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
/**
|
|
496
|
-
* Caret position getter
|
|
497
|
-
* `this` in this function should be the element with a caret
|
|
498
|
-
*/
|
|
499
|
-
const getCaret = function(el) {
|
|
500
|
-
if (el.tagName === 'DIV') {
|
|
501
|
-
return getCaretPosition(el);
|
|
502
|
-
} else {
|
|
503
|
-
return el.selectionStart;
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
/**
|
|
508
|
-
* Caret position setter
|
|
509
|
-
* `this` should be the element (input/textarea or contenteditable div)
|
|
510
|
-
*/
|
|
511
|
-
const setCaret = function(index) {
|
|
512
|
-
if (typeof index !== 'number') index = Number(index) || 0;
|
|
513
|
-
|
|
514
|
-
if (this.tagName !== 'DIV' || this.isContentEditable !== true) {
|
|
515
|
-
const n = this.value ?? '';
|
|
516
|
-
if (index < 0) index = 0;
|
|
517
|
-
if (index > n.length) index = n.length;
|
|
518
|
-
this.focus();
|
|
519
|
-
this.selectionStart = index;
|
|
520
|
-
this.selectionEnd = index;
|
|
521
|
-
return;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// Contenteditable DIV
|
|
525
|
-
const el = /** @type {HTMLElement} */ (this);
|
|
526
|
-
const totalLen = (el.textContent || '').length;
|
|
527
|
-
|
|
528
|
-
if (index < 0) index = 0;
|
|
529
|
-
if (index > totalLen) index = totalLen;
|
|
530
|
-
|
|
531
|
-
const sel = window.getSelection();
|
|
532
|
-
if (!sel) return;
|
|
533
|
-
|
|
534
|
-
const range = document.createRange();
|
|
535
|
-
el.focus();
|
|
536
|
-
|
|
537
|
-
// Empty element → ensure a text node to place the caret into
|
|
538
|
-
if (totalLen === 0) {
|
|
539
|
-
if (!el.firstChild) el.appendChild(document.createTextNode(''));
|
|
540
|
-
// place at start
|
|
541
|
-
range.setStart(el.firstChild, 0);
|
|
542
|
-
range.collapse(true);
|
|
543
|
-
sel.removeAllRanges();
|
|
544
|
-
sel.addRange(range);
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// If caret is at the very end, this is fastest/cleanest
|
|
549
|
-
if (index === totalLen) {
|
|
550
|
-
range.selectNodeContents(el);
|
|
551
|
-
range.collapse(false);
|
|
552
|
-
sel.removeAllRanges();
|
|
553
|
-
sel.addRange(range);
|
|
554
|
-
return;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// Walk text nodes to find the node that contains the index-th character
|
|
558
|
-
const walker = document.createTreeWalker(
|
|
559
|
-
el,
|
|
560
|
-
NodeFilter.SHOW_TEXT,
|
|
561
|
-
{
|
|
562
|
-
acceptNode(node) {
|
|
563
|
-
// skip empty/whitespace-only nodes if you want; or just accept all text
|
|
564
|
-
return node.nodeValue ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
);
|
|
568
|
-
|
|
569
|
-
let pos = 0;
|
|
570
|
-
let node = walker.nextNode();
|
|
571
|
-
while (node) {
|
|
572
|
-
const nextPos = pos + node.nodeValue.length;
|
|
573
|
-
if (index <= nextPos) {
|
|
574
|
-
const offset = index - pos; // char offset within this text node
|
|
575
|
-
range.setStart(node, offset);
|
|
576
|
-
range.collapse(true);
|
|
577
|
-
sel.removeAllRanges();
|
|
578
|
-
sel.addRange(range);
|
|
579
|
-
return;
|
|
580
|
-
}
|
|
581
|
-
pos = nextPos;
|
|
582
|
-
node = walker.nextNode();
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// Fallback: collapse at end if something unexpected happened
|
|
586
|
-
range.selectNodeContents(el);
|
|
587
|
-
range.collapse(false);
|
|
588
|
-
sel.removeAllRanges();
|
|
589
|
-
sel.addRange(range);
|
|
590
|
-
};
|
|
591
|
-
|
|
592
|
-
/**
|
|
593
|
-
* Methods to deal with different types of data
|
|
594
|
-
*/
|
|
595
|
-
const parseMethods = {
|
|
596
|
-
'FIND': function(v, a) {
|
|
597
|
-
if (isBlank(this.values[this.index])) {
|
|
598
|
-
this.values[this.index] = '';
|
|
599
|
-
}
|
|
600
|
-
// TODO: tratar eventos
|
|
601
|
-
if (this.event && this.event.inputType && this.event.inputType.indexOf('delete') > -1) {
|
|
602
|
-
this.values[this.index] += v;
|
|
603
|
-
return;
|
|
604
|
-
}
|
|
605
|
-
let pos = 0;
|
|
606
|
-
let count = 0;
|
|
607
|
-
let value = (this.values[this.index] + v).toLowerCase();
|
|
608
|
-
for (let i = 0; i < a.length; i++) {
|
|
609
|
-
if (a[i].toLowerCase().indexOf(value) === 0) {
|
|
610
|
-
pos = i;
|
|
611
|
-
count++;
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
if (count > 1) {
|
|
615
|
-
this.values[this.index] += v;
|
|
616
|
-
} else if (count === 1) {
|
|
617
|
-
// Jump a number of chars
|
|
618
|
-
let t = (a[pos].length - this.values[this.index].length) - 1;
|
|
619
|
-
this.position += t;
|
|
620
|
-
this.values[this.index] = a[pos];
|
|
621
|
-
this.index++;
|
|
622
|
-
return pos;
|
|
623
|
-
}
|
|
624
|
-
},
|
|
625
|
-
'YEAR': function(v, s) {
|
|
626
|
-
if (isBlank(this.values[this.index])) {
|
|
627
|
-
this.values[this.index] = '';
|
|
628
|
-
}
|
|
629
|
-
if (parseInt(v) >= 0 && parseInt(v) <= 10) {
|
|
630
|
-
if (this.values[this.index].length < s) {
|
|
631
|
-
this.values[this.index] += v;
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
if (this.values[this.index].length === s) {
|
|
635
|
-
let y = new Date().getFullYear().toString();
|
|
636
|
-
if (s === 2) {
|
|
637
|
-
y = y.substring(0,2) + this.values[this.index];
|
|
638
|
-
} else if (s === 3) {
|
|
639
|
-
y = y.substring(0,1) + this.values[this.index];
|
|
640
|
-
} else if (s === 4) {
|
|
641
|
-
y = this.values[this.index];
|
|
642
|
-
}
|
|
643
|
-
this.date[0] = y;
|
|
644
|
-
this.index++;
|
|
645
|
-
}
|
|
646
|
-
},
|
|
647
|
-
'YYYY': function(v) {
|
|
648
|
-
parseMethods.YEAR.call(this, v, 4);
|
|
649
|
-
},
|
|
650
|
-
'YYY': function(v) {
|
|
651
|
-
parseMethods.YEAR.call(this, v, 3);
|
|
652
|
-
},
|
|
653
|
-
'YY': function(v) {
|
|
654
|
-
parseMethods.YEAR.call(this, v, 2);
|
|
655
|
-
},
|
|
656
|
-
'MMMMM': function(v) {
|
|
657
|
-
if (isBlank(this.values[this.index])) {
|
|
658
|
-
this.values[this.index] = '';
|
|
659
|
-
}
|
|
660
|
-
let value = (this.values[this.index] + v).toLowerCase();
|
|
661
|
-
for (var i = 0; i < monthsFull.length; i++) {
|
|
662
|
-
if (monthsFull[i][0].toLowerCase().indexOf(value) === 0) {
|
|
663
|
-
this.values[this.index] = monthsFull[i][0];
|
|
664
|
-
this.date[1] = i + 1;
|
|
665
|
-
this.index++;
|
|
666
|
-
break;
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
},
|
|
670
|
-
'MMMM': function(v) {
|
|
671
|
-
let ret = parseMethods.FIND.call(this, v, monthsFull);
|
|
672
|
-
if (typeof(ret) !== 'undefined') {
|
|
673
|
-
this.date[1] = ret + 1;
|
|
674
|
-
}
|
|
675
|
-
},
|
|
676
|
-
'MMM': function(v) {
|
|
677
|
-
let ret = parseMethods.FIND.call(this, v, months);
|
|
678
|
-
if (typeof(ret) !== 'undefined') {
|
|
679
|
-
this.date[1] = ret + 1;
|
|
680
|
-
}
|
|
681
|
-
},
|
|
682
|
-
'MM': function(v, single) {
|
|
683
|
-
const commit = () => {
|
|
684
|
-
this.date[1] = this.values[this.index];
|
|
685
|
-
this.index++;
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
if (isBlank(this.values[this.index])) {
|
|
689
|
-
if (parseInt(v) > 1 && parseInt(v) < 10) {
|
|
690
|
-
if (! single) {
|
|
691
|
-
v = '0' + v;
|
|
692
|
-
}
|
|
693
|
-
this.values[this.index] = v;
|
|
694
|
-
commit();
|
|
695
|
-
} else if (parseInt(v) < 2) {
|
|
696
|
-
this.values[this.index] = v;
|
|
697
|
-
}
|
|
698
|
-
} else {
|
|
699
|
-
if (this.values[this.index] == 1 && parseInt(v) < 3) {
|
|
700
|
-
this.values[this.index] += v;
|
|
701
|
-
commit();
|
|
702
|
-
} else if (this.values[this.index] == 0 && parseInt(v) > 0 && parseInt(v) < 10) {
|
|
703
|
-
this.values[this.index] += v;
|
|
704
|
-
commit();
|
|
705
|
-
} else {
|
|
706
|
-
let test = parseInt(this.values[this.index]);
|
|
707
|
-
if (test > 0 && test <= 12) {
|
|
708
|
-
if (! single) {
|
|
709
|
-
test = '0' + test;
|
|
710
|
-
}
|
|
711
|
-
this.values[this.index] = test;
|
|
712
|
-
commit();
|
|
713
|
-
return false;
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
},
|
|
718
|
-
'M': function(v) {
|
|
719
|
-
return parseMethods['MM'].call(this, v, true);
|
|
720
|
-
},
|
|
721
|
-
'MONTH': function(v) {
|
|
722
|
-
return parseMethods['MMMM'].call(this, v);
|
|
723
|
-
},
|
|
724
|
-
'MON': function(v) {
|
|
725
|
-
return parseMethods['MMM'].call(this, v);
|
|
726
|
-
},
|
|
727
|
-
'DDDD': function(v) {
|
|
728
|
-
return parseMethods.FIND.call(this, v, weekDaysFull);
|
|
729
|
-
},
|
|
730
|
-
'DDD': function(v) {
|
|
731
|
-
return parseMethods.FIND.call(this, v, weekDays);
|
|
732
|
-
},
|
|
733
|
-
'DD': function(v, single) {
|
|
734
|
-
const commit = () => {
|
|
735
|
-
this.date[2] = this.values[this.index];
|
|
736
|
-
this.index++;
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
if (isBlank(this.values[this.index])) {
|
|
740
|
-
if (parseInt(v) > 3 && parseInt(v) < 10) {
|
|
741
|
-
if (! single) {
|
|
742
|
-
v = '0' + v;
|
|
743
|
-
}
|
|
744
|
-
this.values[this.index] = v;
|
|
745
|
-
commit();
|
|
746
|
-
} else if (parseInt(v) < 10) {
|
|
747
|
-
this.values[this.index] = v;
|
|
748
|
-
}
|
|
749
|
-
} else {
|
|
750
|
-
if (this.values[this.index] == 3 && parseInt(v) < 2) {
|
|
751
|
-
this.values[this.index] += v;
|
|
752
|
-
commit();
|
|
753
|
-
} else if ((this.values[this.index] == 1 || this.values[this.index] == 2) && parseInt(v) < 10) {
|
|
754
|
-
this.values[this.index] += v;
|
|
755
|
-
commit();
|
|
756
|
-
} else if (this.values[this.index] == 0 && parseInt(v) > 0 && parseInt(v) < 10) {
|
|
757
|
-
this.values[this.index] += v;
|
|
758
|
-
commit();
|
|
759
|
-
} else {
|
|
760
|
-
let test = parseInt(this.values[this.index]);
|
|
761
|
-
if (test > 0 && test <= 31) {
|
|
762
|
-
if (! single) {
|
|
763
|
-
test = '0' + test;
|
|
764
|
-
}
|
|
765
|
-
this.values[this.index] = test;
|
|
766
|
-
commit();
|
|
767
|
-
return false;
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
},
|
|
772
|
-
'D': function(v) {
|
|
773
|
-
return parseMethods['DD'].call(this, v, true);
|
|
774
|
-
},
|
|
775
|
-
'DY': function(v) {
|
|
776
|
-
return parseMethods['DDD'].call(this, v);
|
|
777
|
-
},
|
|
778
|
-
'DAY': function(v) {
|
|
779
|
-
return parseMethods['DDDD'].call(this, v);
|
|
780
|
-
},
|
|
781
|
-
'HH12': function(v, two) {
|
|
782
|
-
let test = false;
|
|
783
|
-
if (isBlank(this.values[this.index])) {
|
|
784
|
-
if (parseInt(v) > 1 && parseInt(v) < 10) {
|
|
785
|
-
if (two) {
|
|
786
|
-
v = 0 + v;
|
|
787
|
-
}
|
|
788
|
-
this.date[3] = this.values[this.index] = v;
|
|
789
|
-
this.index++;
|
|
790
|
-
} else if (parseInt(v) < 10) {
|
|
791
|
-
this.values[this.index] = v;
|
|
792
|
-
}
|
|
793
|
-
} else {
|
|
794
|
-
if (this.values[this.index] == 1 && parseInt(v) < 3) {
|
|
795
|
-
this.date[3] = this.values[this.index] += v;
|
|
796
|
-
this.index++;
|
|
797
|
-
} else if (this.values[this.index] < 1 && parseInt(v) < 10) {
|
|
798
|
-
this.date[3] = this.values[this.index] += v;
|
|
799
|
-
this.index++;
|
|
800
|
-
} else {
|
|
801
|
-
test = true;
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
// Re-test
|
|
806
|
-
if (test === true) {
|
|
807
|
-
var t = parseInt(this.values[this.index]);
|
|
808
|
-
if (t >= 0 && t <= 12) {
|
|
809
|
-
this.date[3] = this.values[this.index];
|
|
810
|
-
this.index++;
|
|
811
|
-
return false;
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
},
|
|
815
|
-
'HH24': function(v, two) {
|
|
816
|
-
let test = false;
|
|
817
|
-
if (parseInt(v) >= 0 && parseInt(v) < 10) {
|
|
818
|
-
if (isBlank(this.values[this.index])) {
|
|
819
|
-
if (parseInt(v) > 2 && parseInt(v) < 10) {
|
|
820
|
-
if (two) {
|
|
821
|
-
v = 0 + v;
|
|
822
|
-
}
|
|
823
|
-
this.date[3] = this.values[this.index] = v;
|
|
824
|
-
this.index++;
|
|
825
|
-
} else if (parseInt(v) < 10) {
|
|
826
|
-
this.values[this.index] = v;
|
|
827
|
-
}
|
|
828
|
-
} else {
|
|
829
|
-
if (this.values[this.index] == 2 && parseInt(v) < 4) {
|
|
830
|
-
if (! two && this.values[this.index] === '0') {
|
|
831
|
-
this.values[this.index] = '';
|
|
832
|
-
}
|
|
833
|
-
this.date[3] = this.values[this.index] += v;
|
|
834
|
-
this.index++;
|
|
835
|
-
} else if (this.values[this.index] < 2 && parseInt(v) < 10) {
|
|
836
|
-
if (! two && this.values[this.index] === '0') {
|
|
837
|
-
this.values[this.index] = '';
|
|
838
|
-
}
|
|
839
|
-
this.date[3] = this.values[this.index] += v;
|
|
840
|
-
this.index++;
|
|
841
|
-
} else {
|
|
842
|
-
test = true;
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
} else {
|
|
846
|
-
test = true;
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
// Re-test
|
|
850
|
-
if (test === true) {
|
|
851
|
-
var t = parseInt(this.values[this.index]);
|
|
852
|
-
if (t >= 0 && t < 24) {
|
|
853
|
-
this.date[3] = this.values[this.index];
|
|
854
|
-
this.index++;
|
|
855
|
-
return false;
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
},
|
|
859
|
-
'HH': function(v) {
|
|
860
|
-
parseMethods['HH24'].call(this, v, 1);
|
|
861
|
-
},
|
|
862
|
-
'H': function(v) {
|
|
863
|
-
parseMethods['HH24'].call(this, v, 0);
|
|
864
|
-
},
|
|
865
|
-
'\\[H\\]': function(v) {
|
|
866
|
-
if (this.values[this.index] == undefined) {
|
|
867
|
-
this.values[this.index] = '';
|
|
868
|
-
}
|
|
869
|
-
if (v.match(/[0-9]/g)) {
|
|
870
|
-
this.date[3] = this.values[this.index] += v;
|
|
871
|
-
} else {
|
|
872
|
-
if (this.values[this.index].match(/[0-9]/g)) {
|
|
873
|
-
this.date[3] = this.values[this.index];
|
|
874
|
-
this.index++;
|
|
875
|
-
return false;
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
},
|
|
879
|
-
'N60': function(v, i, two) {
|
|
880
|
-
let test = false;
|
|
881
|
-
if (parseInt(v) >= 0 && parseInt(v) < 10) {
|
|
882
|
-
if (isBlank(this.values[this.index])) {
|
|
883
|
-
if (parseInt(v) > 5 && parseInt(v) < 10) {
|
|
884
|
-
if (two) {
|
|
885
|
-
v = '0' + v;
|
|
886
|
-
}
|
|
887
|
-
this.date[i] = this.values[this.index] = v;
|
|
888
|
-
this.index++;
|
|
889
|
-
} else if (parseInt(v) < 10) {
|
|
890
|
-
this.values[this.index] = v;
|
|
891
|
-
}
|
|
892
|
-
} else {
|
|
893
|
-
if (this.values[this.index] < 6 && parseInt(v) < 10) {
|
|
894
|
-
if (! two && this.values[this.index] === '0') {
|
|
895
|
-
this.values[this.index] = '';
|
|
896
|
-
}
|
|
897
|
-
this.date[i] = this.values[this.index] += v;
|
|
898
|
-
this.index++;
|
|
899
|
-
} else {
|
|
900
|
-
test = true;
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
} else {
|
|
904
|
-
test = true;
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
// Re-test
|
|
908
|
-
if (test === true) {
|
|
909
|
-
var t = parseInt(this.values[this.index]);
|
|
910
|
-
if (t >= 0 && t < 60) {
|
|
911
|
-
this.date[i] = this.values[this.index];
|
|
912
|
-
this.index++;
|
|
913
|
-
return false;
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
},
|
|
917
|
-
'MI': function(v) {
|
|
918
|
-
parseMethods.N60.call(this, v, 4, true);
|
|
919
|
-
},
|
|
920
|
-
'SS': function(v) {
|
|
921
|
-
parseMethods.N60.call(this, v, 5, true);
|
|
922
|
-
},
|
|
923
|
-
'I': function(v) {
|
|
924
|
-
parseMethods.N60.call(this, v, 4, false);
|
|
925
|
-
},
|
|
926
|
-
'S': function(v) {
|
|
927
|
-
parseMethods.N60.call(this, v, 5, false);
|
|
928
|
-
},
|
|
929
|
-
'AM/PM': function(v) {
|
|
930
|
-
if (typeof(this.values[this.index]) === 'undefined') {
|
|
931
|
-
this.values[this.index] = '';
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
if (this.values[this.index] === '') {
|
|
935
|
-
if (v.match(/a/i) && this.date[3] < 13) {
|
|
936
|
-
this.values[this.index] += 'A';
|
|
937
|
-
} else if (v.match(/p/i)) {
|
|
938
|
-
this.values[this.index] += 'P';
|
|
939
|
-
}
|
|
940
|
-
} else if (this.values[this.index] === 'A' || this.values[this.index] === 'P') {
|
|
941
|
-
this.values[this.index] += 'M';
|
|
942
|
-
this.index++;
|
|
943
|
-
}
|
|
944
|
-
},
|
|
945
|
-
'WD': function(v) {
|
|
946
|
-
if (typeof(this.values[this.index]) === 'undefined') {
|
|
947
|
-
this.values[this.index] = '';
|
|
948
|
-
}
|
|
949
|
-
if (parseInt(v) >= 0 && parseInt(v) < 7) {
|
|
950
|
-
this.values[this.index] = v;
|
|
951
|
-
}
|
|
952
|
-
if (this.values[this.index].length == 1) {
|
|
953
|
-
this.index++;
|
|
954
|
-
}
|
|
955
|
-
},
|
|
956
|
-
// Numeric Methods
|
|
957
|
-
'[0#]+([.,]{1}0*#*)?': function(v) {
|
|
958
|
-
if (v === '.' && inputIsANumber(this.raw)) {
|
|
959
|
-
v = this.decimal;
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
if (isBlank(this.values[this.index])) {
|
|
963
|
-
this.values[this.index] = '';
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
if (v === '-') {
|
|
967
|
-
// Transform the number into negative if it is not already
|
|
968
|
-
if (this.values[this.index][0] != '-') {
|
|
969
|
-
this.values[this.index] = '-' + this.values[this.index];
|
|
970
|
-
}
|
|
971
|
-
} else if (v === '+') {
|
|
972
|
-
// Transform the number into positive if it is negative
|
|
973
|
-
if (this.values[this.index][0] == '-') {
|
|
974
|
-
this.values[this.index] = this.values[this.index].replace('-', '');
|
|
975
|
-
}
|
|
976
|
-
} else if (v == '0') {
|
|
977
|
-
// Only adds zero if there's a non-zero number before
|
|
978
|
-
if (this.values[this.index] != '0' && this.values[this.index] != '-0') {
|
|
979
|
-
this.values[this.index] += v;
|
|
980
|
-
}
|
|
981
|
-
} else if (v > 0 && v < 10) {
|
|
982
|
-
// Verify if there's a zero to remove it, avoiding left zeros
|
|
983
|
-
if (this.values[this.index] == '0' || this.values[this.index] == '-0') {
|
|
984
|
-
this.values[this.index] = this.values[this.index].replace('0', '');
|
|
985
|
-
}
|
|
986
|
-
this.values[this.index] += v;
|
|
987
|
-
} else if (v === this.decimal) {
|
|
988
|
-
// Only adds decimal when there's a number value on its left
|
|
989
|
-
if (! this.values[this.index].includes(this.decimal)) {
|
|
990
|
-
if (! this.values[this.index].replace('-', '').length) {
|
|
991
|
-
this.values[this.index] += '0';
|
|
992
|
-
}
|
|
993
|
-
this.values[this.index] += this.decimal;
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
},
|
|
997
|
-
'[0#]+([.,]{1}0*#*)?%': function(v) {
|
|
998
|
-
parseMethods['[0#]+([.,]{1}0*#*)?'].call(this, v);
|
|
999
|
-
|
|
1000
|
-
// Adds the % only if it has a number value
|
|
1001
|
-
if (this.values[this.index].match(/[\-0-9]/g)) {
|
|
1002
|
-
if (this.values[this.index].indexOf('%') !== -1) {
|
|
1003
|
-
this.values[this.index] = this.values[this.index].replaceAll('%', '');
|
|
1004
|
-
}
|
|
1005
|
-
this.values[this.index] += '%';
|
|
1006
|
-
} else {
|
|
1007
|
-
this.values[this.index] = '';
|
|
1008
|
-
}
|
|
1009
|
-
},
|
|
1010
|
-
'#(.{1})##0?(.{1}0+)?( ?;(.*)?)?': function(v) {
|
|
1011
|
-
// Process first the number
|
|
1012
|
-
parseMethods['[0#]+([.,]{1}0*#*)?'].call(this, v, true);
|
|
1013
|
-
// Create the separators
|
|
1014
|
-
let separator = this.tokens[this.index].substring(1,2);
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
let currentValue = this.values[this.index];
|
|
1018
|
-
// Remove existing separators and negative sign
|
|
1019
|
-
currentValue = currentValue.replaceAll(separator, '');
|
|
1020
|
-
// Process separators
|
|
1021
|
-
let val = currentValue.split(this.decimal);
|
|
1022
|
-
if (val[0].length > 3) {
|
|
1023
|
-
let number = [];
|
|
1024
|
-
let count = 0;
|
|
1025
|
-
for (var j = val[0].length - 1; j >= 0 ; j--) {
|
|
1026
|
-
let c = val[0][j];
|
|
1027
|
-
if (c >= 0 && c <= 9) {
|
|
1028
|
-
if (count && ! (count % 3)) {
|
|
1029
|
-
number.unshift(separator);
|
|
1030
|
-
}
|
|
1031
|
-
count++;
|
|
1032
|
-
}
|
|
1033
|
-
number.unshift(c);
|
|
1034
|
-
}
|
|
1035
|
-
val[0] = number.join('');
|
|
1036
|
-
}
|
|
1037
|
-
// Reconstruct the value
|
|
1038
|
-
this.values[this.index] = val.join(this.decimal);
|
|
1039
|
-
},
|
|
1040
|
-
'[0#]+([.,]{1}0*#*)?E{1}\\+0+': function(v) {
|
|
1041
|
-
parseMethods['[0#]+([.,]{1}0*#*)?'].call(this, v);
|
|
1042
|
-
},
|
|
1043
|
-
'#{0,1}.*?\\?+\\/[0-9?]+': function (v) {
|
|
1044
|
-
if (isBlank(this.values[this.index])) {
|
|
1045
|
-
this.values[this.index] = '';
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
const token = this.tokens[this.index]; // e.g. "# ?/?", "?/2", "# ??/16"
|
|
1049
|
-
let cur = this.values[this.index];
|
|
1050
|
-
|
|
1051
|
-
// Parse RHS of mask to decide denominator rule
|
|
1052
|
-
const rhsRaw = (token.split('/')[1] || '').replace(/\s+/g, '');
|
|
1053
|
-
const allowDen = /^\d+$/.test(rhsRaw) ? rhsRaw : /^\?+$/.test(rhsRaw) ? '?' : '?';
|
|
1054
|
-
|
|
1055
|
-
// ----- NEW: allow '-' as first char -----
|
|
1056
|
-
if (v === '-') {
|
|
1057
|
-
if (cur.length === 0) {
|
|
1058
|
-
this.values[this.index] = '-';
|
|
1059
|
-
}
|
|
1060
|
-
return; // never return false
|
|
1061
|
-
}
|
|
1062
|
-
// ----------------------------------------
|
|
1063
|
-
|
|
1064
|
-
// Only accept digits / space / slash; ignore everything else
|
|
1065
|
-
if (!(/[0-9\/ ]/.test(v))) {
|
|
1066
|
-
return;
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
// If we already have a slash and denominator is fixed but not yet appended,
|
|
1070
|
-
// auto-complete immediately regardless of what the user typed now.
|
|
1071
|
-
const hasSlashNow = cur.includes('/');
|
|
1072
|
-
if (hasSlashNow && allowDen !== '?') {
|
|
1073
|
-
const afterSlash = cur.slice(cur.indexOf('/') + 1);
|
|
1074
|
-
if (afterSlash.length === 0) {
|
|
1075
|
-
this.values[this.index] = cur + allowDen;
|
|
1076
|
-
this.index++; // move to next token
|
|
1077
|
-
return;
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
// Empty -> only digits (or a leading '-' handled above)
|
|
1082
|
-
if (cur.length === 0) {
|
|
1083
|
-
if (/\d/.test(v)) this.values[this.index] = v;
|
|
1084
|
-
return;
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
const hasSpace = cur.includes(' ');
|
|
1088
|
-
const hasSlash = cur.includes('/');
|
|
1089
|
-
const last = cur[cur.length - 1];
|
|
1090
|
-
|
|
1091
|
-
// Space rules: only one, must be before slash, must follow a digit
|
|
1092
|
-
if (v === ' ') {
|
|
1093
|
-
if (!hasSpace && !hasSlash && /\d/.test(last)) {
|
|
1094
|
-
this.values[this.index] = cur + ' ';
|
|
1095
|
-
}
|
|
1096
|
-
return;
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
// Slash rules: only one slash, not right after a space, must follow a digit
|
|
1100
|
-
if (v === '/') {
|
|
1101
|
-
if (!hasSlash && last !== ' ' && /\d/.test(last)) {
|
|
1102
|
-
if (allowDen === '?') {
|
|
1103
|
-
this.values[this.index] = cur + '/';
|
|
1104
|
-
} else {
|
|
1105
|
-
this.values[this.index] = cur + '/' + allowDen;
|
|
1106
|
-
this.index++; // conclude this token
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
return;
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
// Digit rules
|
|
1113
|
-
if (/\d/.test(v)) {
|
|
1114
|
-
if (!hasSlash) {
|
|
1115
|
-
// Before slash: digits always fine
|
|
1116
|
-
this.values[this.index] = cur + v;
|
|
1117
|
-
return;
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
// After slash
|
|
1121
|
-
if (allowDen === '?') {
|
|
1122
|
-
this.values[this.index] = cur + v;
|
|
1123
|
-
return;
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
// Fixed denominator: enforce prefix and advance when complete
|
|
1127
|
-
const afterSlash = cur.slice(cur.indexOf('/') + 1);
|
|
1128
|
-
const nextDen = afterSlash + v;
|
|
1129
|
-
if (allowDen.startsWith(nextDen)) {
|
|
1130
|
-
this.values[this.index] = cur + v;
|
|
1131
|
-
if (nextDen.length === allowDen.length) {
|
|
1132
|
-
this.index++;
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
},
|
|
1137
|
-
'[0-9a-zA-Z\\$]+': function(v) {
|
|
1138
|
-
// Token to be added to the value
|
|
1139
|
-
let word = this.tokens[this.index];
|
|
1140
|
-
// Value
|
|
1141
|
-
if (typeof(this.values[this.index]) === 'undefined') {
|
|
1142
|
-
this.values[this.index] = '';
|
|
1143
|
-
}
|
|
1144
|
-
if (v === null) {
|
|
1145
|
-
let size = this.values[this.index].length;
|
|
1146
|
-
v = word.substring(size, size+1);
|
|
1147
|
-
}
|
|
1148
|
-
// Add the value
|
|
1149
|
-
this.values[this.index] += v;
|
|
1150
|
-
// Only if caret is before the change
|
|
1151
|
-
let current = this.values[this.index];
|
|
1152
|
-
// Add token to the values
|
|
1153
|
-
if (current !== word.substring(0,current.length)) {
|
|
1154
|
-
this.values[this.index] = word;
|
|
1155
|
-
// Next token to process
|
|
1156
|
-
this.index++;
|
|
1157
|
-
return false;
|
|
1158
|
-
} else if (current === word) {
|
|
1159
|
-
// Next token to process
|
|
1160
|
-
this.index++;
|
|
1161
|
-
}
|
|
1162
|
-
},
|
|
1163
|
-
'A': function(v) {
|
|
1164
|
-
return parseMethods['[0-9a-zA-Z\\$]+'].call(this, v);
|
|
1165
|
-
},
|
|
1166
|
-
'a': function(v) {
|
|
1167
|
-
return parseMethods['[0-9a-zA-Z\\$]+'].call(this, v);
|
|
1168
|
-
},
|
|
1169
|
-
'.': function(v) {
|
|
1170
|
-
return parseMethods['[0-9a-zA-Z\\$]+'].call(this, v);
|
|
1171
|
-
},
|
|
1172
|
-
'&': function(v) {
|
|
1173
|
-
if (v.match(/^[a-zA-Z ]+$/)) {
|
|
1174
|
-
this.values[this.index] = v;
|
|
1175
|
-
this.index++;
|
|
1176
|
-
}
|
|
1177
|
-
},
|
|
1178
|
-
'\\*': function() {
|
|
1179
|
-
this.values[this.index] = '';
|
|
1180
|
-
this.index++;
|
|
1181
|
-
return false;
|
|
1182
|
-
},
|
|
1183
|
-
'C': function(v) {
|
|
1184
|
-
parseMethods['&'].call(this, v);
|
|
1185
|
-
},
|
|
1186
|
-
// General Methods
|
|
1187
|
-
'0': function(v) {
|
|
1188
|
-
if (v.match(/[0-9]/g)) {
|
|
1189
|
-
this.values[this.index] = v;
|
|
1190
|
-
this.index++;
|
|
1191
|
-
}
|
|
1192
|
-
},
|
|
1193
|
-
'9': function(v) {
|
|
1194
|
-
parseMethods['0'].call(this, v);
|
|
1195
|
-
},
|
|
1196
|
-
'#': function(v) {
|
|
1197
|
-
parseMethods['0'].call(this, v);
|
|
1198
|
-
},
|
|
1199
|
-
'L': function(v) {
|
|
1200
|
-
if (v.match(/[a-zA-Z]/gi)) {
|
|
1201
|
-
this.values[this.index] = v;
|
|
1202
|
-
this.index++;
|
|
1203
|
-
}
|
|
1204
|
-
},
|
|
1205
|
-
'\\?': function(v) {
|
|
1206
|
-
if (v.match(/[1-9]/g)) {
|
|
1207
|
-
this.values[this.index] = v;
|
|
1208
|
-
this.index++;
|
|
1209
|
-
}
|
|
1210
|
-
},
|
|
1211
|
-
'@': function(v) {
|
|
1212
|
-
if (isBlank(this.values[this.index])) {
|
|
1213
|
-
this.values[this.index] = '';
|
|
1214
|
-
}
|
|
1215
|
-
this.values[this.index] += v;
|
|
1216
|
-
},
|
|
1217
|
-
'_\\(': function() {
|
|
1218
|
-
this.values[this.index] = ' ';
|
|
1219
|
-
this.index++;
|
|
1220
|
-
return false;
|
|
1221
|
-
},
|
|
1222
|
-
'_\\)': function() {
|
|
1223
|
-
this.values[this.index] = ' ';
|
|
1224
|
-
this.index++;
|
|
1225
|
-
return false;
|
|
1226
|
-
},
|
|
1227
|
-
'\\(': function() {
|
|
1228
|
-
if (this.type === 'currency' && this.parenthesisForNegativeNumbers) {
|
|
1229
|
-
this.values[this.index] = '';
|
|
1230
|
-
} else {
|
|
1231
|
-
this.values[this.index] = '(';
|
|
1232
|
-
}
|
|
1233
|
-
this.index++;
|
|
1234
|
-
return false;
|
|
1235
|
-
},
|
|
1236
|
-
'\\)': function() {
|
|
1237
|
-
if (this.type === 'currency' && this.parenthesisForNegativeNumbers) {
|
|
1238
|
-
this.values[this.index] = '';
|
|
1239
|
-
} else {
|
|
1240
|
-
this.values[this.index] = ')';
|
|
1241
|
-
}
|
|
1242
|
-
this.index++;
|
|
1243
|
-
return false;
|
|
1244
|
-
},
|
|
1245
|
-
'_-': function() {
|
|
1246
|
-
this.values[this.index] = ' ';
|
|
1247
|
-
this.index++;
|
|
1248
|
-
return false;
|
|
1249
|
-
},
|
|
1250
|
-
',,M': function() {
|
|
1251
|
-
this.values[this.index] = 'M';
|
|
1252
|
-
this.index++;
|
|
1253
|
-
return false;
|
|
1254
|
-
},
|
|
1255
|
-
',,,B': function() {
|
|
1256
|
-
this.values[this.index] = 'B';
|
|
1257
|
-
this.index++;
|
|
1258
|
-
return false;
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
|
|
1262
|
-
const extractDate = function() {
|
|
1263
|
-
let v = '';
|
|
1264
|
-
if (! (this.date[0] && this.date[1] && this.date[2]) && (this.date[3] || this.date[4])) {
|
|
1265
|
-
if (this.mask.toLowerCase().indexOf('[h]') !== -1) {
|
|
1266
|
-
v = parseInt(this.date[3]);
|
|
1267
|
-
} else {
|
|
1268
|
-
let h = parseInt(this.date[3]);
|
|
1269
|
-
if (h < 13 && this.values.indexOf('PM') !== -1) {
|
|
1270
|
-
v = (h+12) % 24;
|
|
1271
|
-
} else {
|
|
1272
|
-
v = h % 24;
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
|
-
if (this.date[4]) {
|
|
1276
|
-
v += parseFloat(this.date[4] / 60);
|
|
1277
|
-
}
|
|
1278
|
-
if (this.date[5]) {
|
|
1279
|
-
v += parseFloat(this.date[5] / 3600);
|
|
1280
|
-
}
|
|
1281
|
-
v /= 24;
|
|
1282
|
-
} else if (this.date[0] || this.date[1] || this.date[2] || this.date[3] || this.date[4] || this.date[5]) {
|
|
1283
|
-
if (this.date[0] && this.date[1] && ! this.date[2]) {
|
|
1284
|
-
this.date[2] = 1;
|
|
1285
|
-
}
|
|
1286
|
-
var t = Helpers.now(this.date);
|
|
1287
|
-
v = Helpers.dateToNum(t);
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
|
-
if (isNaN(v)) {
|
|
1291
|
-
v = '';
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
return v;
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
// Types TODO: Generate types so we can garantee that text,scientific, numeric,percentage, current are not duplicates. If they are, it will be general or broken.
|
|
1298
|
-
|
|
1299
|
-
const getTokens = function(str) {
|
|
1300
|
-
allExpressionsRegex.lastIndex = 0; // Reset for global regex
|
|
1301
|
-
return str.match(allExpressionsRegex);
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
|
-
/**
|
|
1305
|
-
* Get the method of one given token
|
|
1306
|
-
*/
|
|
1307
|
-
const getMethod = function(str, temporary) {
|
|
1308
|
-
str = str.toString().toUpperCase();
|
|
1309
|
-
|
|
1310
|
-
// Check for datetime mask
|
|
1311
|
-
const datetime = temporary.every(t => t.type === 'datetime' || t.type === 'general');
|
|
1312
|
-
|
|
1313
|
-
// Use priority order for faster matching with pre-compiled regexes
|
|
1314
|
-
for (const type of tokenPriority) {
|
|
1315
|
-
if (!datetime && type === 'datetime') continue;
|
|
1316
|
-
|
|
1317
|
-
for (const compiled of compiledTokens[type]) {
|
|
1318
|
-
let regex = compiled.regex;
|
|
1319
|
-
regex.lastIndex = 0; // Reset regex state
|
|
1320
|
-
if (regex.test(str)) {
|
|
1321
|
-
return { type: type, method: compiled.method };
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
}
|
|
1325
|
-
return null;
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
const fixMinuteToken = function(t) {
|
|
1329
|
-
for (let i = 0; i < t.length; i++) {
|
|
1330
|
-
if (t[i] === 'M' || t[i] === 'MM') {
|
|
1331
|
-
// Not a month, correct to minutes
|
|
1332
|
-
if ((t[i - 1] && t[i - 1].indexOf('H') >= 0) ||
|
|
1333
|
-
(t[i - 2] && t[i - 2].indexOf('H') >= 0) ||
|
|
1334
|
-
(t[i + 1] && t[i + 1].indexOf('S') >= 0) ||
|
|
1335
|
-
(t[i + 2] && t[i + 2].indexOf('S') >= 0)) {
|
|
1336
|
-
// Apply minute token
|
|
1337
|
-
t[i] = t[i] === 'M' ? 'I': 'MI';
|
|
1338
|
-
}
|
|
1339
|
-
}
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
|
-
/**
|
|
1344
|
-
* Identify each method for each token
|
|
1345
|
-
*/
|
|
1346
|
-
const getMethodsFromTokens = function(t) {
|
|
1347
|
-
// Uppercase
|
|
1348
|
-
t = t.map(v => {
|
|
1349
|
-
return v.toString().toUpperCase();
|
|
1350
|
-
});
|
|
1351
|
-
|
|
1352
|
-
// Compatibility with Excel
|
|
1353
|
-
fixMinuteToken(t);
|
|
1354
|
-
|
|
1355
|
-
let result = [];
|
|
1356
|
-
for (let i = 0; i < t.length; i++) {
|
|
1357
|
-
var m = getMethod(t[i], result);
|
|
1358
|
-
if (m) {
|
|
1359
|
-
result.push(m);
|
|
1360
|
-
} else {
|
|
1361
|
-
result.push(null);
|
|
1362
|
-
}
|
|
1363
|
-
}
|
|
1364
|
-
return result;
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
const getMethodByPosition = function(control) {
|
|
1368
|
-
let methodName;
|
|
1369
|
-
if (control.methods[control.index] && typeof(control.value[control.position]) !== 'undefined') {
|
|
1370
|
-
methodName = control.methods[control.index].method;
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
|
-
if (methodName && typeof(parseMethods[methodName]) === 'function') {
|
|
1374
|
-
return parseMethods[methodName];
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
return false;
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
const processPaddingZeros = function(token, value, decimal) {
|
|
1381
|
-
if (! value) {
|
|
1382
|
-
return value;
|
|
1383
|
-
}
|
|
1384
|
-
let m = token.split(decimal);
|
|
1385
|
-
let desiredNumOfPaddingZeros = m[0].match(/[0]+/g);
|
|
1386
|
-
if (desiredNumOfPaddingZeros[0]) {
|
|
1387
|
-
desiredNumOfPaddingZeros = desiredNumOfPaddingZeros[0].length
|
|
1388
|
-
let v = value.toString().split(decimal);
|
|
1389
|
-
let len = v[0].length;
|
|
1390
|
-
if (desiredNumOfPaddingZeros > len) {
|
|
1391
|
-
v[0] = v[0].padStart(desiredNumOfPaddingZeros, '0');
|
|
1392
|
-
return v.join(decimal);
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
}
|
|
1396
|
-
|
|
1397
|
-
const processNumOfPaddingZeros = function(control) {
|
|
1398
|
-
let negativeSignal = false;
|
|
1399
|
-
control.methods.forEach((method, k) => {
|
|
1400
|
-
if (method.type === 'numeric' || method.type === 'percentage' || method.type === 'scientific') {
|
|
1401
|
-
let ret = processPaddingZeros(control.tokens[k], control.values[k], control.decimal);
|
|
1402
|
-
if (ret) {
|
|
1403
|
-
control.values[k] = ret;
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
if (control.type === 'currency' && control.parenthesisForNegativeNumbers === true) {
|
|
1408
|
-
if (method.type === 'currency') {
|
|
1409
|
-
if (control.values[k].toString().includes('-')) {
|
|
1410
|
-
control.values[k] = control.values[k].replace('-', '');
|
|
1411
|
-
|
|
1412
|
-
negativeSignal = true;
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
});
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
if (control.type === 'currency' && control.parenthesisForNegativeNumbers === true && negativeSignal) {
|
|
1420
|
-
control.methods.forEach((method, k) => {
|
|
1421
|
-
if (! control.values[k] && control.tokens[k] === '(') {
|
|
1422
|
-
control.values[k] = '(';
|
|
1423
|
-
} else if (! control.values[k] && control.tokens[k] === ')') {
|
|
1424
|
-
control.values[k] = ')';
|
|
1425
|
-
}
|
|
1426
|
-
});
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
const getValue = function(control) {
|
|
1431
|
-
return control.values.join('');
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
|
-
const inputIsANumber = function(num) {
|
|
1435
|
-
if (typeof(num) === 'string') {
|
|
1436
|
-
num = num.trim();
|
|
1437
|
-
}
|
|
1438
|
-
return !isNaN(num) && num !== null && num !== '';
|
|
1439
|
-
}
|
|
1440
|
-
|
|
1441
|
-
const getType = function(control) {
|
|
1442
|
-
// Mask type
|
|
1443
|
-
let type = 'general';
|
|
1444
|
-
// Process other types
|
|
1445
|
-
for (var i = 0; i < control.methods.length; i++) {
|
|
1446
|
-
let m = control.methods[i];
|
|
1447
|
-
if (m && m.type !== 'general' && m.type !== type) {
|
|
1448
|
-
if (type === 'general') {
|
|
1449
|
-
type = m.type;
|
|
1450
|
-
} else {
|
|
1451
|
-
type = 'general';
|
|
1452
|
-
break;
|
|
1453
|
-
}
|
|
1454
|
-
}
|
|
1455
|
-
}
|
|
1456
|
-
return type;
|
|
1457
|
-
}
|
|
1458
|
-
|
|
1459
|
-
const isNumber = function(num) {
|
|
1460
|
-
if (typeof(num) === 'string') {
|
|
1461
|
-
num = num.trim();
|
|
1462
|
-
}
|
|
1463
|
-
return !isNaN(num) && num !== null && num !== '';
|
|
1464
|
-
}
|
|
1465
|
-
|
|
1466
|
-
// TODO, get negative mask automatically based on the input sign?
|
|
1467
|
-
|
|
1468
|
-
const getConfig = function(config, value) {
|
|
1469
|
-
// Internal default control of the mask system
|
|
1470
|
-
const control = {
|
|
1471
|
-
// Mask options
|
|
1472
|
-
options: {},
|
|
1473
|
-
// New values for each token found
|
|
1474
|
-
values: [],
|
|
1475
|
-
// Token position
|
|
1476
|
-
index: 0,
|
|
1477
|
-
// Character position
|
|
1478
|
-
position: 0,
|
|
1479
|
-
// Date raw values
|
|
1480
|
-
date: [0,0,0,0,0,0],
|
|
1481
|
-
// Raw number for the numeric values
|
|
1482
|
-
number: 0,
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
if (typeof(value) === 'undefined' || value === null) {
|
|
1486
|
-
value = '';
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
// Value to be masked
|
|
1490
|
-
control.value = value.toString();
|
|
1491
|
-
control.raw = value;
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
// Options defined by the user
|
|
1495
|
-
if (typeof(config) == 'string') {
|
|
1496
|
-
// Mask
|
|
1497
|
-
control.mask = config;
|
|
1498
|
-
} else if (config) {
|
|
1499
|
-
// Mask
|
|
1500
|
-
let k = Object.keys(config);
|
|
1501
|
-
for (var i = 0; i < k.length; i++) {
|
|
1502
|
-
control[k[i]] = config[k[i]];
|
|
1503
|
-
}
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
// Controls of Excel that should be ignored
|
|
1507
|
-
if (control.mask) {
|
|
1508
|
-
let d = control.mask.split(';');
|
|
1509
|
-
// Mask
|
|
1510
|
-
let mask = d[0];
|
|
1511
|
-
|
|
1512
|
-
if (typeof(value) === 'number' || isNumber(value)) {
|
|
1513
|
-
if (Number(value) < 0 && d[1]) {
|
|
1514
|
-
mask = d[1];
|
|
1515
|
-
} else if (Number(value) === 0 && d[2]) {
|
|
1516
|
-
mask = d[2];
|
|
1517
|
-
}
|
|
1518
|
-
} else {
|
|
1519
|
-
if (d[3]) {
|
|
1520
|
-
mask = d[3];
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
// Cleaning the mask
|
|
1524
|
-
mask = mask.replace(new RegExp('"', 'mgi'), "");
|
|
1525
|
-
// Parenthesis
|
|
1526
|
-
let reg = /(?<!_)\((?![^()]*_)([^'"]*?)\)/g;
|
|
1527
|
-
if (mask.match(reg)) {
|
|
1528
|
-
control.parenthesisForNegativeNumbers = true;
|
|
1529
|
-
}
|
|
1530
|
-
// Match brackets that should be removed (NOT the time format codes)
|
|
1531
|
-
reg = /\[(?!(?:s|ss|h|hh|m|mm)\])([^\]]*)\]/g;
|
|
1532
|
-
if (mask.match(reg)) {
|
|
1533
|
-
mask = mask.replace(reg, ''); // Removes brackets and content
|
|
1534
|
-
}
|
|
1535
|
-
// Get only the first mask for now and remove
|
|
1536
|
-
control.mask = mask;
|
|
1537
|
-
// Get tokens which are the methods for parsing
|
|
1538
|
-
control.tokens = getTokens(control.mask);
|
|
1539
|
-
// Get methods from the tokens
|
|
1540
|
-
control.methods = getMethodsFromTokens(control.tokens);
|
|
1541
|
-
// Type
|
|
1542
|
-
control.type = getType(control);
|
|
1543
|
-
}
|
|
1544
|
-
|
|
1545
|
-
// Decimal
|
|
1546
|
-
control.decimal = getDecimal.call(control);
|
|
1547
|
-
|
|
1548
|
-
return control;
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
|
-
const toPlainString = function(num) {
|
|
1552
|
-
// Convert number to string if it isn't already
|
|
1553
|
-
num = String(num);
|
|
1554
|
-
|
|
1555
|
-
// If it's not in exponential form, return as-is
|
|
1556
|
-
if (!/e/i.test(num)) return num;
|
|
1557
|
-
|
|
1558
|
-
// Decompose scientific notation
|
|
1559
|
-
const [coefficient, exponent] = num.toLowerCase().split('e');
|
|
1560
|
-
const exp = parseInt(exponent, 10);
|
|
1561
|
-
|
|
1562
|
-
// Handle sign
|
|
1563
|
-
const sign = coefficient[0] === '-' ? '-' : '';
|
|
1564
|
-
const [intPart, fracPart = ''] = coefficient.replace('-', '').split('.');
|
|
1565
|
-
|
|
1566
|
-
const digits = intPart + fracPart;
|
|
1567
|
-
const decimalPos = intPart.length;
|
|
1568
|
-
|
|
1569
|
-
let newPos = decimalPos + exp;
|
|
1570
|
-
|
|
1571
|
-
if (newPos <= 0) {
|
|
1572
|
-
// Decimal point moves left
|
|
1573
|
-
return sign + '0.' + '0'.repeat(-newPos) + digits;
|
|
1574
|
-
} else if (newPos >= digits.length) {
|
|
1575
|
-
// Decimal point moves right, add trailing zeros
|
|
1576
|
-
return sign + digits + '0'.repeat(newPos - digits.length);
|
|
1577
|
-
} else {
|
|
1578
|
-
// Decimal point moves into the number
|
|
1579
|
-
return sign + digits.slice(0, newPos) + '.' + digits.slice(newPos);
|
|
1580
|
-
}
|
|
1581
|
-
};
|
|
1582
|
-
|
|
1583
|
-
const adjustNumberOfDecimalPlaces = function(config, value) {
|
|
1584
|
-
let temp = value;
|
|
1585
|
-
let mask = config.mask;
|
|
1586
|
-
let expo;
|
|
1587
|
-
|
|
1588
|
-
if (config.type === 'scientific') {
|
|
1589
|
-
mask = config.mask.toUpperCase().split('E')[0];
|
|
1590
|
-
|
|
1591
|
-
let numOfDecimalPlaces = mask.split(config.decimal);
|
|
1592
|
-
numOfDecimalPlaces = numOfDecimalPlaces[1].match(/[0#]+/g);
|
|
1593
|
-
numOfDecimalPlaces = numOfDecimalPlaces[0]?.length ?? 0;
|
|
1594
|
-
temp = temp.toExponential(numOfDecimalPlaces);
|
|
1595
|
-
expo = temp.toString().split('e+');
|
|
1596
|
-
temp = Number(expo[0]);
|
|
1597
|
-
}
|
|
1598
|
-
|
|
1599
|
-
if (mask.indexOf(config.decimal) === -1) {
|
|
1600
|
-
// No decimal places
|
|
1601
|
-
if (! Number.isInteger(temp)) {
|
|
1602
|
-
temp = temp.toFixed(0);
|
|
1603
|
-
}
|
|
1604
|
-
} else {
|
|
1605
|
-
// Length of the decimal
|
|
1606
|
-
let mandatoryDecimalPlaces = mask.split(config.decimal);
|
|
1607
|
-
mandatoryDecimalPlaces = mandatoryDecimalPlaces[1].match(/0+/g);
|
|
1608
|
-
if (mandatoryDecimalPlaces) {
|
|
1609
|
-
mandatoryDecimalPlaces = mandatoryDecimalPlaces[0].length;
|
|
1610
|
-
} else {
|
|
1611
|
-
mandatoryDecimalPlaces = 0;
|
|
1612
|
-
}
|
|
1613
|
-
// Amount of decimal
|
|
1614
|
-
let numOfDecimalPlaces = temp.toString().split(config.decimal)
|
|
1615
|
-
numOfDecimalPlaces = numOfDecimalPlaces[1]?.length ?? 0;
|
|
1616
|
-
// Necessary adjustment
|
|
1617
|
-
let necessaryAdjustment = 0;
|
|
1618
|
-
if (numOfDecimalPlaces < mandatoryDecimalPlaces) {
|
|
1619
|
-
necessaryAdjustment = mandatoryDecimalPlaces;
|
|
1620
|
-
} else {
|
|
1621
|
-
// Optional
|
|
1622
|
-
let optionalDecimalPlaces = mask.split(config.decimal);
|
|
1623
|
-
optionalDecimalPlaces = optionalDecimalPlaces[1].match(/[0#]+/g);
|
|
1624
|
-
if (optionalDecimalPlaces) {
|
|
1625
|
-
optionalDecimalPlaces = optionalDecimalPlaces[0].length;
|
|
1626
|
-
if (numOfDecimalPlaces > optionalDecimalPlaces) {
|
|
1627
|
-
necessaryAdjustment = optionalDecimalPlaces;
|
|
1628
|
-
}
|
|
1629
|
-
}
|
|
1630
|
-
}
|
|
1631
|
-
// Adjust decimal numbers if applicable
|
|
1632
|
-
if (necessaryAdjustment) {
|
|
1633
|
-
let t = temp.toFixed(necessaryAdjustment);
|
|
1634
|
-
let n = temp.toString().split('.');
|
|
1635
|
-
let fraction = n[1];
|
|
1636
|
-
if (fraction && fraction.length > necessaryAdjustment && fraction[fraction.length - 1] === '5') {
|
|
1637
|
-
t = parseFloat(n[0] + '.' + fraction + '1').toFixed(necessaryAdjustment);
|
|
1638
|
-
}
|
|
1639
|
-
temp = t;
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
|
|
1643
|
-
if (config.type === 'scientific') {
|
|
1644
|
-
let ret = processPaddingZeros(mask, temp, config.decimal);
|
|
1645
|
-
if (ret) {
|
|
1646
|
-
temp = ret;
|
|
1647
|
-
}
|
|
1648
|
-
expo[0] = temp;
|
|
1649
|
-
|
|
1650
|
-
mask = config.mask.toUpperCase().split('E+')[1];
|
|
1651
|
-
ret = processPaddingZeros(mask, expo[1], config.decimal);
|
|
1652
|
-
if (ret) {
|
|
1653
|
-
expo[1] = ret;
|
|
1654
|
-
}
|
|
1655
|
-
|
|
1656
|
-
temp = expo.join('e+');
|
|
1657
|
-
}
|
|
1658
|
-
|
|
1659
|
-
return temp;
|
|
1660
|
-
}
|
|
1661
|
-
|
|
1662
|
-
const formatFraction = function(value, mask) {
|
|
1663
|
-
let maxDenominator;
|
|
1664
|
-
let fixedDenominator = null;
|
|
1665
|
-
let allowWholeNumber = true;
|
|
1666
|
-
|
|
1667
|
-
// Check for fixed denominator like # ?/8 or ?/8
|
|
1668
|
-
const fixed = mask.match(/\/(\d+)/);
|
|
1669
|
-
if (fixed) {
|
|
1670
|
-
fixedDenominator = parseInt(fixed[1], 10);
|
|
1671
|
-
maxDenominator = fixedDenominator;
|
|
1672
|
-
} else {
|
|
1673
|
-
// Determine based on question marks in mask
|
|
1674
|
-
const match = mask.match(/\?\/(\?+)/);
|
|
1675
|
-
if (match) {
|
|
1676
|
-
maxDenominator = Math.pow(10, match[1].length) - 1;
|
|
1677
|
-
} else {
|
|
1678
|
-
maxDenominator = 9; // Default for # ?/? or ?/?
|
|
1679
|
-
}
|
|
1680
|
-
}
|
|
1681
|
-
// Check if mask allows whole number (e.g., ?/? or ?/8 implies no whole number)
|
|
1682
|
-
allowWholeNumber = mask.includes('#');
|
|
1683
|
-
|
|
1684
|
-
// If we have a fixed denominator, use it exactly (don't simplify)
|
|
1685
|
-
if (fixedDenominator) {
|
|
1686
|
-
const isNegative = value < 0;
|
|
1687
|
-
const absValue = Math.abs(value);
|
|
1688
|
-
const numerator = Math.round(absValue * fixedDenominator);
|
|
1689
|
-
|
|
1690
|
-
// For masks like ?/8, always output as pure fraction (no whole number)
|
|
1691
|
-
if (!allowWholeNumber) {
|
|
1692
|
-
return isNegative ? `-${numerator}/${fixedDenominator}` : `${numerator}/${fixedDenominator}`;
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1695
|
-
// For masks like # ?/8, allow whole number
|
|
1696
|
-
const whole = Math.floor(numerator / fixedDenominator);
|
|
1697
|
-
const remainder = numerator % fixedDenominator;
|
|
1698
|
-
if (remainder === 0) {
|
|
1699
|
-
return isNegative ? `-${whole}` : `${whole}`;
|
|
1700
|
-
}
|
|
1701
|
-
if (whole === 0) {
|
|
1702
|
-
return isNegative ? `-${numerator}/${fixedDenominator}` : `${numerator}/${fixedDenominator}`;
|
|
1703
|
-
}
|
|
1704
|
-
return isNegative ? `-${whole} ${remainder}/${fixedDenominator}` : `${whole} ${remainder}/${fixedDenominator}`;
|
|
1705
|
-
}
|
|
1706
|
-
|
|
1707
|
-
// Use continued fractions algorithm for better approximation
|
|
1708
|
-
function continuedFraction(value, maxDenom) {
|
|
1709
|
-
if (value === 0) return [0, 1];
|
|
1710
|
-
let sign = value < 0 ? -1 : 1;
|
|
1711
|
-
value = Math.abs(value);
|
|
1712
|
-
let whole = Math.floor(value);
|
|
1713
|
-
let frac = value - whole;
|
|
1714
|
-
if (frac === 0) return [sign * whole, 1];
|
|
1715
|
-
|
|
1716
|
-
let h1 = 1, h2 = 0;
|
|
1717
|
-
let k1 = 0, k2 = 1;
|
|
1718
|
-
let x = frac;
|
|
1719
|
-
while (k1 <= maxDenom) {
|
|
1720
|
-
let a = Math.floor(x);
|
|
1721
|
-
let h0 = a * h1 + h2;
|
|
1722
|
-
let k0 = a * k1 + k2;
|
|
1723
|
-
if (k0 > maxDenom) break;
|
|
1724
|
-
h2 = h1; h1 = h0;
|
|
1725
|
-
k2 = k1; k1 = k0;
|
|
1726
|
-
if (Math.abs(x - a) < 1e-10) break;
|
|
1727
|
-
x = 1 / (x - a);
|
|
1728
|
-
}
|
|
1729
|
-
|
|
1730
|
-
// Add the whole part back only if allowed
|
|
1731
|
-
let finalNum = sign * (allowWholeNumber ? whole * k1 + h1 : Math.round(value * k1));
|
|
1732
|
-
let finalDen = k1;
|
|
1733
|
-
return [finalNum, finalDen];
|
|
1734
|
-
}
|
|
1735
|
-
|
|
1736
|
-
const [numerator, denominator] = continuedFraction(value, maxDenominator);
|
|
1737
|
-
|
|
1738
|
-
// Handle the result
|
|
1739
|
-
const isNegative = numerator < 0;
|
|
1740
|
-
const absNumerator = Math.abs(numerator);
|
|
1741
|
-
const whole = allowWholeNumber ? Math.floor(absNumerator / denominator) : 0;
|
|
1742
|
-
const remainder = absNumerator % denominator;
|
|
1743
|
-
const sign = isNegative ? '-' : '';
|
|
1744
|
-
|
|
1745
|
-
if (remainder === 0) {
|
|
1746
|
-
return `${sign}${whole || 0}`;
|
|
1747
|
-
}
|
|
1748
|
-
if (whole === 0 || !allowWholeNumber) {
|
|
1749
|
-
return `${sign}${absNumerator}/${denominator}`;
|
|
1750
|
-
}
|
|
1751
|
-
return `${sign}${whole} ${remainder}/${denominator}`;
|
|
1752
|
-
}
|
|
1753
|
-
|
|
1754
|
-
const extractDateAndTime = function(value) {
|
|
1755
|
-
value = value.toString().substring(0,19);
|
|
1756
|
-
let splitStr = (value.indexOf('T') !== -1) ? 'T' : ' ';
|
|
1757
|
-
value = value.split(splitStr);
|
|
1758
|
-
|
|
1759
|
-
let y = null;
|
|
1760
|
-
let m = null;
|
|
1761
|
-
let d = null;
|
|
1762
|
-
let h = '0';
|
|
1763
|
-
let i = '0';
|
|
1764
|
-
let s = '0';
|
|
1765
|
-
|
|
1766
|
-
if (! value[1]) {
|
|
1767
|
-
if (value[0].indexOf(':') !== -1) {
|
|
1768
|
-
value[0] = value[0].split(':');
|
|
1769
|
-
h = value[0][0];
|
|
1770
|
-
i = value[0][1];
|
|
1771
|
-
s = value[0][2];
|
|
1772
|
-
} else {
|
|
1773
|
-
value[0] = value[0].split('-');
|
|
1774
|
-
y = value[0][0];
|
|
1775
|
-
m = value[0][1];
|
|
1776
|
-
d = value[0][2];
|
|
1777
|
-
}
|
|
1778
|
-
} else {
|
|
1779
|
-
value[0] = value[0].split('-');
|
|
1780
|
-
y = value[0][0];
|
|
1781
|
-
m = value[0][1];
|
|
1782
|
-
d = value[0][2];
|
|
1783
|
-
|
|
1784
|
-
value[1] = value[1].split(':');
|
|
1785
|
-
h = value[1][0];
|
|
1786
|
-
i = value[1][1];
|
|
1787
|
-
s = value[1][2];
|
|
1788
|
-
}
|
|
1789
|
-
|
|
1790
|
-
return [y,m,d,h,i,s];
|
|
1791
|
-
}
|
|
1792
|
-
|
|
1793
|
-
const Component = function(str, config, returnObject) {
|
|
1794
|
-
// Get configuration
|
|
1795
|
-
const control = getConfig(config, str);
|
|
1796
|
-
|
|
1797
|
-
if (control.locale) {
|
|
1798
|
-
// Process the locale
|
|
1799
|
-
} else if (control.mask) {
|
|
1800
|
-
// Walk every character on the value
|
|
1801
|
-
let method;
|
|
1802
|
-
while (method = getMethodByPosition(control)) {
|
|
1803
|
-
let char = control.value[control.position];
|
|
1804
|
-
if (char === hiddenCaret) {
|
|
1805
|
-
control.caret = {
|
|
1806
|
-
index: control.index,
|
|
1807
|
-
position: control.values[control.index]?.length ?? 0,
|
|
1808
|
-
}
|
|
1809
|
-
control.position++;
|
|
1810
|
-
} else {
|
|
1811
|
-
// Get the method name to handle the current token
|
|
1812
|
-
let ret = method.call(control, char);
|
|
1813
|
-
// Next position
|
|
1814
|
-
if (ret !== false) {
|
|
1815
|
-
control.position++;
|
|
1816
|
-
}
|
|
1817
|
-
}
|
|
1818
|
-
}
|
|
1819
|
-
|
|
1820
|
-
// Move index
|
|
1821
|
-
if (control.methods[control.index]) {
|
|
1822
|
-
let type = control.methods[control.index].type;
|
|
1823
|
-
if (isNumeric(type) && control.methods[++control.index]) {
|
|
1824
|
-
let next;
|
|
1825
|
-
while (next = control.methods[control.index]) {
|
|
1826
|
-
if (control.methods[control.index].type === 'general') {
|
|
1827
|
-
let method = control.methods[control.index].method;
|
|
1828
|
-
if (method && typeof(parseMethods[method]) === 'function') {
|
|
1829
|
-
parseMethods[method].call(control, null);
|
|
1830
|
-
}
|
|
1831
|
-
} else {
|
|
1832
|
-
break;
|
|
1833
|
-
}
|
|
1834
|
-
}
|
|
1835
|
-
}
|
|
1836
|
-
}
|
|
1837
|
-
}
|
|
1838
|
-
if (control.caret) {
|
|
1839
|
-
let index = control.caret.index;
|
|
1840
|
-
let position = control.caret.position;
|
|
1841
|
-
let value = control.values[index] ?? '';
|
|
1842
|
-
// Re-apply the caret to the original position
|
|
1843
|
-
control.values[index] = value.substring(0, position) + hiddenCaret + value.substring(position);
|
|
1844
|
-
}
|
|
1845
|
-
|
|
1846
|
-
control.value = getValue(control);
|
|
1847
|
-
|
|
1848
|
-
if (returnObject) {
|
|
1849
|
-
return control;
|
|
1850
|
-
} else {
|
|
1851
|
-
return control.value;
|
|
1852
|
-
}
|
|
1853
|
-
}
|
|
1854
|
-
|
|
1855
|
-
// Helper: Compare rendered value to original input
|
|
1856
|
-
const testMask = function(mask, value, original) {
|
|
1857
|
-
const rendered = Component.render(value, { mask }, true);
|
|
1858
|
-
return rendered.replace(/\s/g, '') === original.replace(/\s/g, '');
|
|
1859
|
-
}
|
|
1860
|
-
|
|
1861
|
-
const autoCastingFractions = function(value) {
|
|
1862
|
-
const fractionPattern = /^\s*(-?\d+\s+)?(-?\d+)\/(\d+)\s*$/;
|
|
1863
|
-
const fractionMatch = value.match(fractionPattern);
|
|
1864
|
-
if (fractionMatch) {
|
|
1865
|
-
const sign = value.trim().startsWith('-') ? -1 : 1;
|
|
1866
|
-
const whole = fractionMatch[1] ? Math.abs(parseInt(fractionMatch[1])) : 0;
|
|
1867
|
-
const numerator = Math.abs(parseInt(fractionMatch[2]));
|
|
1868
|
-
const denominator = parseInt(fractionMatch[3]);
|
|
1869
|
-
|
|
1870
|
-
if (denominator === 0) return null;
|
|
1871
|
-
|
|
1872
|
-
const decimalValue = sign * (whole + (numerator / denominator));
|
|
1873
|
-
|
|
1874
|
-
// Determine the mask
|
|
1875
|
-
let mask;
|
|
1876
|
-
if ([2, 4, 8, 16, 32].includes(denominator)) {
|
|
1877
|
-
mask = whole !== 0 ? `# ?/${denominator}` : `?/${denominator}`;
|
|
1878
|
-
} else if (denominator <= 9) {
|
|
1879
|
-
mask = whole !== 0 ? '# ?/?' : '?/?';
|
|
1880
|
-
} else {
|
|
1881
|
-
mask = whole !== 0 ? '# ??/??' : '??/??';
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
|
-
if (testMask(mask, decimalValue, value.trim())) {
|
|
1885
|
-
return { mask, value: decimalValue };
|
|
1886
|
-
}
|
|
1887
|
-
}
|
|
1888
|
-
return null;
|
|
1889
|
-
}
|
|
1890
|
-
|
|
1891
|
-
const autoCastingPercent = function(value) {
|
|
1892
|
-
const percentPattern = /^\s*([+-]?\d+(?:[.,]\d+)?)%\s*$/;
|
|
1893
|
-
const percentMatch = value.match(percentPattern);
|
|
1894
|
-
if (percentMatch) {
|
|
1895
|
-
const rawNumber = percentMatch[1].replace(',', '.');
|
|
1896
|
-
const decimalValue = parseFloat(rawNumber) / 100;
|
|
1897
|
-
|
|
1898
|
-
const decimalPart = rawNumber.split('.')[1];
|
|
1899
|
-
const decimalPlaces = decimalPart ? decimalPart.length : 0;
|
|
1900
|
-
const mask = decimalPlaces > 0 ? `0.${'0'.repeat(decimalPlaces)}%` : '0%';
|
|
1901
|
-
|
|
1902
|
-
if (testMask(mask, decimalValue, value.trim())) {
|
|
1903
|
-
return { mask: mask, value: decimalValue };
|
|
1904
|
-
}
|
|
1905
|
-
}
|
|
1906
|
-
return null;
|
|
1907
|
-
}
|
|
1908
|
-
|
|
1909
|
-
const autoCastingDates = function(value) {
|
|
1910
|
-
if (!value || typeof value !== 'string') {
|
|
1911
|
-
return null;
|
|
1912
|
-
}
|
|
1913
|
-
|
|
1914
|
-
// Smart pattern detection based on the structure of the string
|
|
1915
|
-
|
|
1916
|
-
// 1. Analyze the structure to determine possible formats
|
|
1917
|
-
const analyzeStructure = function(str) {
|
|
1918
|
-
const patterns = [];
|
|
1919
|
-
|
|
1920
|
-
// Check for date with forward slashes: XX/XX/XXXX or XX/XX/XX
|
|
1921
|
-
if (str.match(/^\d{1,2}\/\d{1,2}\/\d{2,4}$/)) {
|
|
1922
|
-
const parts = str.split('/');
|
|
1923
|
-
const p1 = parseInt(parts[0]);
|
|
1924
|
-
const p2 = parseInt(parts[1]);
|
|
1925
|
-
const p3 = parseInt(parts[2]);
|
|
1926
|
-
|
|
1927
|
-
// Determine likely format based on values
|
|
1928
|
-
if (p1 <= 12 && p2 <= 31 && p2 > 12) {
|
|
1929
|
-
// Likely mm/dd/yyyy
|
|
1930
|
-
patterns.push('mm/dd/yyyy', 'mm/dd/yy', 'm/d/yyyy', 'm/d/yy');
|
|
1931
|
-
} else if (p1 <= 31 && p2 <= 12 && p1 > 12) {
|
|
1932
|
-
// Likely dd/mm/yyyy
|
|
1933
|
-
patterns.push('dd/mm/yyyy', 'dd/mm/yy', 'd/m/yyyy', 'd/m/yy');
|
|
1934
|
-
} else if (p1 <= 12 && p2 <= 12) {
|
|
1935
|
-
// Ambiguous - could be either, use locale preference
|
|
1936
|
-
const locale = navigator.language || 'en-US';
|
|
1937
|
-
if (locale.startsWith('en-US')) {
|
|
1938
|
-
patterns.push('mm/dd/yyyy', 'dd/mm/yyyy', 'mm/dd/yy', 'dd/mm/yy');
|
|
1939
|
-
} else {
|
|
1940
|
-
patterns.push('dd/mm/yyyy', 'mm/dd/yyyy', 'dd/mm/yy', 'mm/dd/yy');
|
|
1941
|
-
}
|
|
1942
|
-
}
|
|
1943
|
-
|
|
1944
|
-
// Add variations
|
|
1945
|
-
if (p3 < 100) {
|
|
1946
|
-
patterns.push('dd/mm/yy', 'mm/dd/yy', 'd/m/yy', 'm/d/yy');
|
|
1947
|
-
} else {
|
|
1948
|
-
patterns.push('dd/mm/yyyy', 'mm/dd/yyyy', 'd/m/yyyy', 'm/d/yyyy');
|
|
1949
|
-
}
|
|
1950
|
-
}
|
|
1951
|
-
|
|
1952
|
-
// Check for date with dashes: XX-XX-XXXX
|
|
1953
|
-
else if (str.match(/^\d{1,2}-\d{1,2}-\d{2,4}$/)) {
|
|
1954
|
-
const parts = str.split('-');
|
|
1955
|
-
const p1 = parseInt(parts[0]);
|
|
1956
|
-
const p2 = parseInt(parts[1]);
|
|
1957
|
-
|
|
1958
|
-
if (p1 <= 12 && p2 <= 31 && p2 > 12) {
|
|
1959
|
-
patterns.push('mm-dd-yyyy', 'mm-dd-yy', 'm-d-yyyy', 'm-d-yy');
|
|
1960
|
-
} else if (p1 <= 31 && p2 <= 12 && p1 > 12) {
|
|
1961
|
-
patterns.push('dd-mm-yyyy', 'dd-mm-yy', 'd-m-yyyy', 'd-m-yy');
|
|
1962
|
-
} else {
|
|
1963
|
-
patterns.push('dd-mm-yyyy', 'mm-dd-yyyy', 'dd-mm-yy', 'mm-dd-yy');
|
|
1964
|
-
}
|
|
1965
|
-
}
|
|
1966
|
-
|
|
1967
|
-
// Check for ISO format: YYYY-MM-DD
|
|
1968
|
-
else if (str.match(/^\d{4}-\d{1,2}-\d{1,2}$/)) {
|
|
1969
|
-
patterns.push('yyyy-mm-dd', 'yyyy-m-d');
|
|
1970
|
-
}
|
|
1971
|
-
|
|
1972
|
-
// Check for format: YYYY/MM/DD
|
|
1973
|
-
else if (str.match(/^\d{4}\/\d{1,2}\/\d{1,2}$/)) {
|
|
1974
|
-
patterns.push('yyyy/mm/dd', 'yyyy/m/d');
|
|
1975
|
-
}
|
|
1976
|
-
|
|
1977
|
-
// Check for dates with month names
|
|
1978
|
-
else if (str.match(/\b(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i)) {
|
|
1979
|
-
// DD Mon YYYY or DD Month YYYY
|
|
1980
|
-
if (str.match(/^\d{1,2}\s+\w+\s+\d{2,4}$/i)) {
|
|
1981
|
-
patterns.push('dd mmm yyyy', 'dd mmmm yyyy', 'd mmm yyyy', 'd mmmm yyyy',
|
|
1982
|
-
'dd mmm yy', 'dd mmmm yy', 'd mmm yy', 'd mmmm yy');
|
|
1983
|
-
}
|
|
1984
|
-
// Mon DD, YYYY or Month DD, YYYY
|
|
1985
|
-
else if (str.match(/^\w+\s+\d{1,2},?\s+\d{2,4}$/i)) {
|
|
1986
|
-
patterns.push('mmm dd, yyyy', 'mmmm dd, yyyy', 'mmm d, yyyy', 'mmmm d, yyyy',
|
|
1987
|
-
'mmm dd yyyy', 'mmmm dd yyyy', 'mmm d yyyy', 'mmmm d yyyy');
|
|
1988
|
-
}
|
|
1989
|
-
// DD-Mon-YYYY
|
|
1990
|
-
else if (str.match(/^\d{1,2}-\w+-\d{2,4}$/i)) {
|
|
1991
|
-
patterns.push('dd-mmm-yyyy', 'dd-mmmm-yyyy', 'd-mmm-yyyy', 'd-mmmm-yyyy',
|
|
1992
|
-
'dd-mmm-yy', 'dd-mmmm-yy', 'd-mmm-yy', 'd-mmmm-yy');
|
|
1993
|
-
}
|
|
1994
|
-
}
|
|
1995
|
-
|
|
1996
|
-
// Check for weekday formats
|
|
1997
|
-
else if (str.match(/^(mon|tue|wed|thu|fri|sat|sun)/i)) {
|
|
1998
|
-
if (str.match(/^\w+,\s+\d{1,2}\s+\w+\s+\d{4}$/i)) {
|
|
1999
|
-
patterns.push('ddd, dd mmm yyyy', 'ddd, d mmm yyyy',
|
|
2000
|
-
'dddd, dd mmmm yyyy', 'dddd, d mmmm yyyy');
|
|
2001
|
-
}
|
|
2002
|
-
}
|
|
2003
|
-
|
|
2004
|
-
// Check for datetime formats
|
|
2005
|
-
else if (str.includes(' ') && str.match(/\d{1,2}:\d{2}/)) {
|
|
2006
|
-
const parts = str.split(' ');
|
|
2007
|
-
if (parts.length >= 2) {
|
|
2008
|
-
const datePart = parts[0];
|
|
2009
|
-
const timePart = parts.slice(1).join(' ');
|
|
2010
|
-
|
|
2011
|
-
// Determine date format
|
|
2012
|
-
let dateMasks = [];
|
|
2013
|
-
if (datePart.includes('/')) {
|
|
2014
|
-
dateMasks = ['dd/mm/yyyy', 'mm/dd/yyyy', 'd/m/yyyy', 'm/d/yyyy'];
|
|
2015
|
-
} else if (datePart.includes('-')) {
|
|
2016
|
-
if (datePart.match(/^\d{4}-/)) {
|
|
2017
|
-
dateMasks = ['yyyy-mm-dd', 'yyyy-m-d'];
|
|
2018
|
-
} else {
|
|
2019
|
-
dateMasks = ['dd-mm-yyyy', 'mm-dd-yyyy', 'd-m-yyyy', 'm-d-yyyy'];
|
|
2020
|
-
}
|
|
2021
|
-
}
|
|
2022
|
-
|
|
2023
|
-
// Determine time format
|
|
2024
|
-
let timeMasks = [];
|
|
2025
|
-
if (timePart.match(/\d{1,2}:\d{2}:\d{2}/)) {
|
|
2026
|
-
timeMasks = ['hh:mm:ss', 'h:mm:ss'];
|
|
2027
|
-
} else {
|
|
2028
|
-
timeMasks = ['hh:mm', 'h:mm'];
|
|
2029
|
-
}
|
|
2030
|
-
|
|
2031
|
-
// Add AM/PM variants if present
|
|
2032
|
-
if (timePart.match(/[ap]m/i)) {
|
|
2033
|
-
timeMasks = timeMasks.map(t => t + ' am/pm');
|
|
2034
|
-
}
|
|
2035
|
-
|
|
2036
|
-
// Combine date and time masks
|
|
2037
|
-
for (const dateMask of dateMasks) {
|
|
2038
|
-
for (const timeMask of timeMasks) {
|
|
2039
|
-
patterns.push(`${dateMask} ${timeMask}`);
|
|
2040
|
-
}
|
|
2041
|
-
}
|
|
2042
|
-
}
|
|
2043
|
-
}
|
|
2044
|
-
|
|
2045
|
-
// Check for time-only formats
|
|
2046
|
-
else if (str.match(/^\d{1,2}:\d{2}(:\d{2})?(\s*(am|pm))?$/i)) {
|
|
2047
|
-
if (str.match(/:\d{2}:\d{2}/)) {
|
|
2048
|
-
patterns.push('hh:mm:ss', 'h:mm:ss');
|
|
2049
|
-
if (str.match(/[ap]m/i)) {
|
|
2050
|
-
patterns.push('hh:mm:ss am/pm', 'h:mm:ss am/pm');
|
|
2051
|
-
}
|
|
2052
|
-
} else {
|
|
2053
|
-
patterns.push('hh:mm', 'h:mm');
|
|
2054
|
-
if (str.match(/[ap]m/i)) {
|
|
2055
|
-
patterns.push('hh:mm am/pm', 'h:mm am/pm');
|
|
2056
|
-
}
|
|
2057
|
-
}
|
|
2058
|
-
}
|
|
2059
|
-
|
|
2060
|
-
// Check for extended hour format [h]:mm:ss
|
|
2061
|
-
else if (str.match(/^\[?\d+\]?:\d{2}:\d{2}$/)) {
|
|
2062
|
-
patterns.push('[h]:mm:ss');
|
|
2063
|
-
}
|
|
2064
|
-
|
|
2065
|
-
return [...new Set(patterns)]; // Remove duplicates
|
|
2066
|
-
};
|
|
2067
|
-
|
|
2068
|
-
// Get candidate masks based on the string structure
|
|
2069
|
-
const candidateMasks = analyzeStructure(value);
|
|
2070
|
-
|
|
2071
|
-
// If no patterns detected, try some common formats as fallback
|
|
2072
|
-
if (candidateMasks.length === 0) {
|
|
2073
|
-
const locale = navigator.language || 'en-US';
|
|
2074
|
-
if (locale.startsWith('en-US')) {
|
|
2075
|
-
candidateMasks.push(
|
|
2076
|
-
'mm/dd/yyyy', 'mm-dd-yyyy', 'yyyy-mm-dd',
|
|
2077
|
-
'mm/dd/yy', 'mm-dd-yy',
|
|
2078
|
-
'hh:mm:ss', 'hh:mm', 'h:mm am/pm'
|
|
2079
|
-
);
|
|
2080
|
-
} else {
|
|
2081
|
-
candidateMasks.push(
|
|
2082
|
-
'dd/mm/yyyy', 'dd-mm-yyyy', 'yyyy-mm-dd',
|
|
2083
|
-
'dd/mm/yy', 'dd-mm-yy',
|
|
2084
|
-
'hh:mm:ss', 'hh:mm', 'h:mm'
|
|
2085
|
-
);
|
|
2086
|
-
}
|
|
2087
|
-
}
|
|
2088
|
-
|
|
2089
|
-
// Try each candidate mask
|
|
2090
|
-
for (const mask of candidateMasks) {
|
|
2091
|
-
try {
|
|
2092
|
-
// Use Component.extractDateFromString to parse the date
|
|
2093
|
-
const isoDate = Component.extractDateFromString(value, mask);
|
|
2094
|
-
|
|
2095
|
-
if (isoDate && isoDate !== '') {
|
|
2096
|
-
// Parse the ISO date string to components
|
|
2097
|
-
const parts = isoDate.split(' ');
|
|
2098
|
-
const dateParts = parts[0].split('-');
|
|
2099
|
-
const timeParts = parts[1] ? parts[1].split(':') : ['0', '0', '0'];
|
|
2100
|
-
|
|
2101
|
-
const year = parseInt(dateParts[0]);
|
|
2102
|
-
const month = parseInt(dateParts[1]);
|
|
2103
|
-
const day = parseInt(dateParts[2]);
|
|
2104
|
-
const hour = parseInt(timeParts[0]);
|
|
2105
|
-
const minute = parseInt(timeParts[1]);
|
|
2106
|
-
const second = parseInt(timeParts[2]);
|
|
2107
|
-
|
|
2108
|
-
// Validate the date components
|
|
2109
|
-
if (year > 0 && month >= 1 && month <= 12 && day >= 1 && day <= 31 &&
|
|
2110
|
-
hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60) {
|
|
2111
|
-
|
|
2112
|
-
// Convert to Excel serial number
|
|
2113
|
-
const excelNumber = Helpers.dateToNum(isoDate);
|
|
2114
|
-
|
|
2115
|
-
// Verify by rendering back
|
|
2116
|
-
const rendered = Component.render(excelNumber, { mask: mask }, true);
|
|
2117
|
-
|
|
2118
|
-
// Case-insensitive comparison for month names
|
|
2119
|
-
if (rendered.toLowerCase() === value.toLowerCase()) {
|
|
2120
|
-
return {
|
|
2121
|
-
mask: mask,
|
|
2122
|
-
value: excelNumber,
|
|
2123
|
-
};
|
|
2124
|
-
}
|
|
2125
|
-
}
|
|
2126
|
-
}
|
|
2127
|
-
} catch (e) {
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
2130
|
-
|
|
2131
|
-
// No matching format found
|
|
2132
|
-
return null;
|
|
2133
|
-
};
|
|
2134
|
-
|
|
2135
|
-
const autoCastingCurrency = function (input) {
|
|
2136
|
-
if (typeof input !== 'string') return null;
|
|
2137
|
-
|
|
2138
|
-
const original = input.trim();
|
|
2139
|
-
|
|
2140
|
-
const isNegative = /^\s*[-(]/.test(original);
|
|
2141
|
-
const hasParens = /^\s*\(.+\)\s*$/.test(original);
|
|
2142
|
-
let value = original.replace(/[()\-]/g, '').trim();
|
|
2143
|
-
|
|
2144
|
-
// Use pre-compiled currency regexes
|
|
2145
|
-
let symbol = '';
|
|
2146
|
-
|
|
2147
|
-
for (let {symbol: s, regex} of currencyRegexes) {
|
|
2148
|
-
const match = value.match(regex);
|
|
2149
|
-
if (match) {
|
|
2150
|
-
symbol = s + (match[1] || '');
|
|
2151
|
-
value = value.replace(regex, '');
|
|
2152
|
-
break;
|
|
2153
|
-
}
|
|
2154
|
-
}
|
|
2155
|
-
|
|
2156
|
-
// Generic symbol/prefix (e.g., "U$", "US$")
|
|
2157
|
-
if (!symbol) {
|
|
2158
|
-
const prefixMatch = value.match(/^([^\d\s.,-]{1,4})(\s?)/);
|
|
2159
|
-
if (prefixMatch) {
|
|
2160
|
-
symbol = prefixMatch[1] + (prefixMatch[2] || '');
|
|
2161
|
-
value = value.replace(prefixMatch[0], '');
|
|
2162
|
-
}
|
|
2163
|
-
}
|
|
2164
|
-
|
|
2165
|
-
// Code suffix (e.g., USD, BRL)
|
|
2166
|
-
const codeMatch = value.match(/([A-Z]{3})$/);
|
|
2167
|
-
if (codeMatch) {
|
|
2168
|
-
value = value.replace(codeMatch[1], '').trim();
|
|
2169
|
-
if (!symbol) symbol = codeMatch[1] + ' ';
|
|
2170
|
-
}
|
|
2171
|
-
|
|
2172
|
-
value = value.replace(/\s+/g, '');
|
|
2173
|
-
|
|
2174
|
-
// Infer separators
|
|
2175
|
-
let group = ',', decimal = '.';
|
|
2176
|
-
|
|
2177
|
-
if (value.includes(',') && value.includes('.')) {
|
|
2178
|
-
const lastComma = value.lastIndexOf(',');
|
|
2179
|
-
const lastDot = value.lastIndexOf('.');
|
|
2180
|
-
if (lastComma > lastDot) {
|
|
2181
|
-
group = '.';
|
|
2182
|
-
decimal = ',';
|
|
2183
|
-
} else {
|
|
2184
|
-
group = ',';
|
|
2185
|
-
decimal = '.';
|
|
2186
|
-
}
|
|
2187
|
-
} else if (value.includes('.')) {
|
|
2188
|
-
const parts = value.split('.');
|
|
2189
|
-
const lastPart = parts[parts.length - 1];
|
|
2190
|
-
if (/^\d{3}$/.test(lastPart)) {
|
|
2191
|
-
group = '.';
|
|
2192
|
-
decimal = ',';
|
|
2193
|
-
} else {
|
|
2194
|
-
group = ',';
|
|
2195
|
-
decimal = '.';
|
|
2196
|
-
}
|
|
2197
|
-
} else if (value.includes(',')) {
|
|
2198
|
-
const parts = value.split(',');
|
|
2199
|
-
const lastPart = parts[parts.length - 1];
|
|
2200
|
-
if (/^\d{3}$/.test(lastPart)) {
|
|
2201
|
-
group = ',';
|
|
2202
|
-
decimal = '.';
|
|
2203
|
-
} else {
|
|
2204
|
-
group = '.';
|
|
2205
|
-
decimal = ',';
|
|
2206
|
-
}
|
|
2207
|
-
}
|
|
2208
|
-
|
|
2209
|
-
// Normalize and parse
|
|
2210
|
-
const normalized = value
|
|
2211
|
-
.replace(new RegExp(`\\${group}`, 'g'), '')
|
|
2212
|
-
.replace(decimal, '.');
|
|
2213
|
-
|
|
2214
|
-
const parsed = parseFloat(normalized);
|
|
2215
|
-
if (isNaN(parsed)) return null;
|
|
2216
|
-
|
|
2217
|
-
const finalValue = isNegative ? -parsed : parsed;
|
|
2218
|
-
|
|
2219
|
-
// Build dynamic group + decimal mask
|
|
2220
|
-
const decimalPlaces = normalized.includes('.') ? normalized.split('.')[1].length : 0;
|
|
2221
|
-
const maskDecimal = decimalPlaces ? decimal + '0'.repeat(decimalPlaces) : '';
|
|
2222
|
-
const groupMask = '#' + group + '##0';
|
|
2223
|
-
let mask = `${symbol}${groupMask}${maskDecimal}`;
|
|
2224
|
-
|
|
2225
|
-
if (isNegative) {
|
|
2226
|
-
mask = hasParens ? `(${mask})` : `-${mask}`;
|
|
2227
|
-
}
|
|
2228
|
-
|
|
2229
|
-
return {
|
|
2230
|
-
mask,
|
|
2231
|
-
value: finalValue
|
|
2232
|
-
};
|
|
2233
|
-
}
|
|
2234
|
-
|
|
2235
|
-
const autoCastingNumber = function (input) {
|
|
2236
|
-
// If you currently support numeric inputs directly, keep this:
|
|
2237
|
-
if (typeof input === 'number' && Number.isFinite(input)) {
|
|
2238
|
-
return { mask: '0', value: input };
|
|
2239
|
-
}
|
|
2240
|
-
|
|
2241
|
-
if (typeof input !== 'string') {
|
|
2242
|
-
return null;
|
|
2243
|
-
}
|
|
2244
|
-
|
|
2245
|
-
const sRaw = input.trim();
|
|
2246
|
-
if (!/^[+-]?\d+$/.test(sRaw)) {
|
|
2247
|
-
return null;
|
|
2248
|
-
}
|
|
2249
|
-
|
|
2250
|
-
const sign = /^[+-]/.test(sRaw) ? sRaw[0] : '';
|
|
2251
|
-
const digitsClean = (sign ? sRaw.slice(1) : sRaw); // keep as you already do
|
|
2252
|
-
|
|
2253
|
-
// ***** NEW: mask derived from RAW leading zeros only *****
|
|
2254
|
-
const rawDigits = sign ? sRaw.slice(1) : sRaw; // no extra cleaning here
|
|
2255
|
-
const m = rawDigits.match(/^0+/);
|
|
2256
|
-
const leadingZeros = m ? m[0].length : 0;
|
|
2257
|
-
|
|
2258
|
-
const mask = leadingZeros > 0 ? '0'.repeat(rawDigits.length) : '0';
|
|
2259
|
-
|
|
2260
|
-
// Your existing numeric value (from the cleaned digits)
|
|
2261
|
-
const value = Number(sign + digitsClean);
|
|
2262
|
-
|
|
2263
|
-
return { mask, value };
|
|
2264
|
-
};
|
|
2265
|
-
|
|
2266
|
-
const autoCastingScientific = function(input) {
|
|
2267
|
-
if (typeof input !== 'string') return null;
|
|
2268
|
-
|
|
2269
|
-
const original = input.trim();
|
|
2270
|
-
|
|
2271
|
-
// Match scientific notation: 1e3, -2.5E-4, etc.
|
|
2272
|
-
const sciPattern = /^[-+]?\d*\.?\d+[eE][-+]?\d+$/;
|
|
2273
|
-
if (!sciPattern.test(original)) return null;
|
|
2274
|
-
|
|
2275
|
-
const parsed = parseFloat(original);
|
|
2276
|
-
if (isNaN(parsed)) return null;
|
|
2277
|
-
|
|
2278
|
-
// Extract parts to determine mask
|
|
2279
|
-
const [coefficient, exponent] = original.toLowerCase().split('e');
|
|
2280
|
-
const decimalPlaces = coefficient.includes('.') ? coefficient.split('.')[1].length : 0;
|
|
2281
|
-
const mask = `0${decimalPlaces ? '.' + '0'.repeat(decimalPlaces) : ''}E+00`;
|
|
2282
|
-
|
|
2283
|
-
return {
|
|
2284
|
-
mask,
|
|
2285
|
-
value: parsed
|
|
2286
|
-
};
|
|
2287
|
-
}
|
|
2288
|
-
|
|
2289
|
-
const autoCastingTime = function (input) {
|
|
2290
|
-
if (typeof input !== 'string') return null;
|
|
2291
|
-
const original = input.trim();
|
|
2292
|
-
|
|
2293
|
-
// hh:mm[:ss][ am/pm]
|
|
2294
|
-
const m = original.match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?(?:\s*(am|pm))?$/i);
|
|
2295
|
-
if (!m) return null;
|
|
2296
|
-
|
|
2297
|
-
let h = parseInt(m[1], 10);
|
|
2298
|
-
const i = parseInt(m[2], 10);
|
|
2299
|
-
const s = m[3] ? parseInt(m[3], 10) : 0;
|
|
2300
|
-
const mer = m[4] && m[4].toLowerCase();
|
|
2301
|
-
|
|
2302
|
-
// basic range checks
|
|
2303
|
-
if (i > 59 || s > 59) return null;
|
|
2304
|
-
if (mer) {
|
|
2305
|
-
if (h < 1 || h > 12) return null;
|
|
2306
|
-
if (mer === 'pm' && h < 12) h += 12;
|
|
2307
|
-
if (mer === 'am' && h === 12) h = 0;
|
|
2308
|
-
} else {
|
|
2309
|
-
if (h > 23) return null;
|
|
2310
|
-
}
|
|
2311
|
-
|
|
2312
|
-
// Excel serial for time-of-day = hours/24 + minutes/1440 + seconds/86400
|
|
2313
|
-
const excel = (h + i / 60 + s / 3600) / 24;
|
|
2314
|
-
|
|
2315
|
-
// Build mask according to how user typed it
|
|
2316
|
-
const hourToken = m[1].length === 1 ? 'h' : 'hh';
|
|
2317
|
-
const base = s !== 0 || m[3] ? `${hourToken}:mm:ss` : `${hourToken}:mm`;
|
|
2318
|
-
const mask = mer ? `${base} am/pm` : base;
|
|
2319
|
-
|
|
2320
|
-
// Verify we can render back exactly what the user typed
|
|
2321
|
-
if (testMask(mask, excel, original)) { // uses Component.render under the hood
|
|
2322
|
-
return { mask: mask, value: excel};
|
|
2323
|
-
}
|
|
2324
|
-
|
|
2325
|
-
// Try alternate hour width if needed
|
|
2326
|
-
const altHour = hourToken === 'hh' ? 'h' : 'hh';
|
|
2327
|
-
const alt = mer
|
|
2328
|
-
? `${altHour}${base.slice(hourToken.length)} am/pm`
|
|
2329
|
-
: `${altHour}${base.slice(hourToken.length)}`;
|
|
2330
|
-
|
|
2331
|
-
if (testMask(alt, excel, original)) {
|
|
2332
|
-
return { mask: alt, value: excel };
|
|
2333
|
-
}
|
|
2334
|
-
|
|
2335
|
-
return null;
|
|
2336
|
-
};
|
|
2337
|
-
|
|
2338
|
-
const ParseValue = function(v, config) {
|
|
2339
|
-
if (v === '') return '';
|
|
2340
|
-
|
|
2341
|
-
const decimal = config.decimal || '.';
|
|
2342
|
-
|
|
2343
|
-
v = ('' + v).split(decimal);
|
|
2344
|
-
|
|
2345
|
-
// Detect negative sign
|
|
2346
|
-
let signal = v[0].includes('-');
|
|
2347
|
-
|
|
2348
|
-
v[0] = v[0].match(/[0-9]+/g);
|
|
2349
|
-
if (v[0]) {
|
|
2350
|
-
if (signal) v[0].unshift('-');
|
|
2351
|
-
v[0] = v[0].join('');
|
|
2352
|
-
} else {
|
|
2353
|
-
v[0] = signal ? '-' : '';
|
|
2354
|
-
}
|
|
2355
|
-
|
|
2356
|
-
if (v[1] !== undefined) {
|
|
2357
|
-
v[1] = v[1].match(/[0-9]+/g);
|
|
2358
|
-
v[1] = v[1] ? v[1].join('') : '';
|
|
2359
|
-
}
|
|
2360
|
-
|
|
2361
|
-
return v[0] || v[1] ? v : '';
|
|
2362
|
-
}
|
|
2363
|
-
|
|
2364
|
-
const Extract = function(v, config) {
|
|
2365
|
-
const parsed = ParseValue(v, config);
|
|
2366
|
-
if (parsed) {
|
|
2367
|
-
if (parsed[0] === '-') {
|
|
2368
|
-
parsed[0] = '-0';
|
|
2369
|
-
}
|
|
2370
|
-
return parseFloat(parsed.join('.'));
|
|
2371
|
-
}
|
|
2372
|
-
return null;
|
|
2373
|
-
}
|
|
2374
|
-
|
|
2375
|
-
/**
|
|
2376
|
-
* Try to get which mask that can transform the number in that format
|
|
2377
|
-
*/
|
|
2378
|
-
Component.autoCasting = function(value, returnObject) {
|
|
2379
|
-
const methods = [
|
|
2380
|
-
autoCastingDates, // Most structured, the least ambiguous
|
|
2381
|
-
autoCastingTime,
|
|
2382
|
-
autoCastingFractions, // Specific pattern with slashes
|
|
2383
|
-
autoCastingPercent, // Recognizable with "%"
|
|
2384
|
-
autoCastingScientific,
|
|
2385
|
-
autoCastingNumber, // Only picks up basic digits, decimals, leading 0s
|
|
2386
|
-
autoCastingCurrency, // Complex formats, but recognizable
|
|
2387
|
-
];
|
|
2388
|
-
|
|
2389
|
-
for (let method of methods) {
|
|
2390
|
-
const test = method(value);
|
|
2391
|
-
if (test) {
|
|
2392
|
-
return test;
|
|
2393
|
-
}
|
|
2394
|
-
}
|
|
2395
|
-
|
|
2396
|
-
return null;
|
|
2397
|
-
}
|
|
2398
|
-
|
|
2399
|
-
Component.extract = function(value, options, returnObject) {
|
|
2400
|
-
if (!value || typeof options !== 'object') return value;
|
|
2401
|
-
|
|
2402
|
-
// Get decimal, group, type, etc.
|
|
2403
|
-
const config = getConfig(options, value);
|
|
2404
|
-
const type = config.type;
|
|
2405
|
-
|
|
2406
|
-
let result;
|
|
2407
|
-
let o = options;
|
|
2408
|
-
|
|
2409
|
-
if (type === 'text') {
|
|
2410
|
-
result = value;
|
|
2411
|
-
} else if (type === 'general') {
|
|
2412
|
-
result = Component(value, options);
|
|
2413
|
-
} else if (type === 'datetime') {
|
|
2414
|
-
if (value instanceof Date) {
|
|
2415
|
-
value = Component.getDateString(value, config.mask);
|
|
2416
|
-
}
|
|
2417
|
-
|
|
2418
|
-
o = Component(value, options, true);
|
|
2419
|
-
|
|
2420
|
-
result = typeof o.value === 'number' ? o.value : extractDate.call(o);
|
|
2421
|
-
} else if (type === 'scientific') {
|
|
2422
|
-
result = typeof value === 'string' ? Number(value) : value;
|
|
2423
|
-
} else if (type === 'fraction') {
|
|
2424
|
-
// Parse a fraction string according to the mask (supports mixed "# ?/d" or simple "?/d")
|
|
2425
|
-
const mask = config.mask;
|
|
2426
|
-
|
|
2427
|
-
// Detect fixed denominator (e.g. "# ?/16" or "?/8")
|
|
2428
|
-
const fixedDenMatch = mask.match(/\/\s*(\d+)\s*$/);
|
|
2429
|
-
const fixedDen = fixedDenMatch ? parseInt(fixedDenMatch[1], 10) : null;
|
|
2430
|
-
|
|
2431
|
-
// Whether a mask allows a whole part (e.g. "# ?/?")
|
|
2432
|
-
const allowWhole = mask.includes('#');
|
|
2433
|
-
|
|
2434
|
-
let s = ('' + value).trim();
|
|
2435
|
-
if (! s) {
|
|
2436
|
-
result = null;
|
|
2437
|
-
} else {
|
|
2438
|
-
// Allow leading parentheses or '-' for negatives
|
|
2439
|
-
let sign = 1;
|
|
2440
|
-
if (/^\(.*\)$/.test(s)) {
|
|
2441
|
-
sign = -1;
|
|
2442
|
-
s = s.slice(1, -1).trim();
|
|
2443
|
-
}
|
|
2444
|
-
if (/^\s*-/.test(s)) {
|
|
2445
|
-
sign = -1;
|
|
2446
|
-
s = s.replace(/^\s*-/, '').trim();
|
|
2447
|
-
}
|
|
2448
|
-
|
|
2449
|
-
let out = null;
|
|
2450
|
-
|
|
2451
|
-
if (s.includes('/')) {
|
|
2452
|
-
// sign? (whole )? numerator / denominator
|
|
2453
|
-
// Examples:
|
|
2454
|
-
// "1 1/2" => whole=1, num=1, den=2
|
|
2455
|
-
// "1/2" => whole=undefined, num=1, den=2
|
|
2456
|
-
const m = s.match(/^\s*(?:(\d+)\s+)?(\d+)\s*\/\s*(\d+)\s*$/);
|
|
2457
|
-
if (m) {
|
|
2458
|
-
const whole = allowWhole && m[1] ? parseInt(m[1], 10) : 0;
|
|
2459
|
-
const num = parseInt(m[2], 10);
|
|
2460
|
-
let den = parseInt(m[3], 10);
|
|
2461
|
-
|
|
2462
|
-
// If mask fixes the denominator, enforce it
|
|
2463
|
-
if (fixedDen) den = fixedDen;
|
|
2464
|
-
|
|
2465
|
-
if (den !== 0) {
|
|
2466
|
-
out = sign * (whole + num / den);
|
|
2467
|
-
}
|
|
2468
|
-
}
|
|
2469
|
-
} else {
|
|
2470
|
-
// No slash → treats as a plain number (e.g., whole only)
|
|
2471
|
-
const plain = Number(s.replace(',', '.'));
|
|
2472
|
-
if (!Number.isNaN(plain)) {
|
|
2473
|
-
out = sign * Math.abs(plain);
|
|
2474
|
-
}
|
|
2475
|
-
}
|
|
2476
|
-
|
|
2477
|
-
result = out;
|
|
2478
|
-
}
|
|
2479
|
-
} else {
|
|
2480
|
-
// Default fallback — numeric/currency/percent/etc.
|
|
2481
|
-
result = Extract(value, config);
|
|
2482
|
-
// Adjust percent
|
|
2483
|
-
if (type === 'percentage' && ('' + value).indexOf('%') !== -1) {
|
|
2484
|
-
result = result / 100;
|
|
2485
|
-
}
|
|
2486
|
-
}
|
|
2487
|
-
|
|
2488
|
-
o.value = result;
|
|
2489
|
-
|
|
2490
|
-
if (! o.type && type) {
|
|
2491
|
-
o.type = type;
|
|
2492
|
-
}
|
|
2493
|
-
|
|
2494
|
-
return returnObject ? o : result;
|
|
2495
|
-
};
|
|
2496
|
-
|
|
2497
|
-
Component.render = function(value, options, fullMask) {
|
|
2498
|
-
// Nothing to render
|
|
2499
|
-
if (value === '' || value === undefined || value === null) {
|
|
2500
|
-
return '';
|
|
2501
|
-
}
|
|
2502
|
-
|
|
2503
|
-
// Config
|
|
2504
|
-
const config = getConfig(options, value);
|
|
2505
|
-
|
|
2506
|
-
// Percentage
|
|
2507
|
-
if (config.type === 'datetime') {
|
|
2508
|
-
var t = Component.getDateString(value, config.mask);
|
|
2509
|
-
if (t) {
|
|
2510
|
-
value = t;
|
|
2511
|
-
} else {
|
|
2512
|
-
return '';
|
|
2513
|
-
}
|
|
2514
|
-
} else if (config.type === 'text') {
|
|
2515
|
-
// Parse number
|
|
2516
|
-
if (typeof(value) === 'number') {
|
|
2517
|
-
value = value.toString();
|
|
2518
|
-
}
|
|
2519
|
-
} else {
|
|
2520
|
-
if (config.type === 'percentage') {
|
|
2521
|
-
if (typeof(value) === 'string' && value.indexOf('%') !== -1) {
|
|
2522
|
-
value = value.replace('%', '');
|
|
2523
|
-
} else {
|
|
2524
|
-
value = adjustPrecision(Number(value) * 100);
|
|
2525
|
-
}
|
|
2526
|
-
} else {
|
|
2527
|
-
if (config.mask.includes(',,M')) {
|
|
2528
|
-
if (typeof(value) === 'string' && value.indexOf('M') !== -1) {
|
|
2529
|
-
value = value.replace('M', '');
|
|
2530
|
-
} else {
|
|
2531
|
-
value = Number(value) / 1000000;
|
|
2532
|
-
}
|
|
2533
|
-
} else if (config.mask.includes(',,,B')) {
|
|
2534
|
-
if (typeof(value) === 'string' && value.indexOf('B') !== -1) {
|
|
2535
|
-
value = value.replace('B', '');
|
|
2536
|
-
} else {
|
|
2537
|
-
value = Number(value) / 1000000000;
|
|
2538
|
-
}
|
|
2539
|
-
}
|
|
2540
|
-
}
|
|
2541
|
-
|
|
2542
|
-
if (typeof(value) === 'string' && isNumber(value)) {
|
|
2543
|
-
value = Number(value);
|
|
2544
|
-
}
|
|
2545
|
-
|
|
2546
|
-
if (typeof value === 'number') {
|
|
2547
|
-
// Temporary value
|
|
2548
|
-
let temp = value;
|
|
2549
|
-
|
|
2550
|
-
if (config.type === 'fraction') {
|
|
2551
|
-
temp = formatFraction(value, config.mask);
|
|
2552
|
-
} else {
|
|
2553
|
-
if (fullMask) {
|
|
2554
|
-
temp = adjustNumberOfDecimalPlaces(config, value);
|
|
2555
|
-
|
|
2556
|
-
if (config.type === 'scientific') {
|
|
2557
|
-
return temp;
|
|
2558
|
-
}
|
|
2559
|
-
}
|
|
2560
|
-
}
|
|
2561
|
-
|
|
2562
|
-
value = toPlainString(temp);
|
|
2563
|
-
|
|
2564
|
-
if (config.decimal === ',') {
|
|
2565
|
-
value = value.replace('.', config.decimal);
|
|
2566
|
-
}
|
|
2567
|
-
}
|
|
2568
|
-
}
|
|
2569
|
-
|
|
2570
|
-
// Process mask
|
|
2571
|
-
let control = Component(value, options, true);
|
|
2572
|
-
// Complement render
|
|
2573
|
-
if (fullMask) {
|
|
2574
|
-
processNumOfPaddingZeros(control);
|
|
2575
|
-
}
|
|
2576
|
-
|
|
2577
|
-
value = getValue(control);
|
|
2578
|
-
|
|
2579
|
-
if (options.input && options.input.tagName) {
|
|
2580
|
-
if (options.input.contentEditable) {
|
|
2581
|
-
options.input.textContent = value;
|
|
2582
|
-
} else {
|
|
2583
|
-
options.input.value = value;
|
|
2584
|
-
}
|
|
2585
|
-
focus(options.input);
|
|
2586
|
-
}
|
|
2587
|
-
|
|
2588
|
-
return value;
|
|
2589
|
-
}
|
|
2590
|
-
|
|
2591
|
-
// Helper to extract date from a string
|
|
2592
|
-
Component.extractDateFromString = function (date, format) {
|
|
2593
|
-
let o = Component(date, { mask: format }, true);
|
|
2594
|
-
|
|
2595
|
-
// Check if in format Excel (Need difference with format date or type detected is numeric)
|
|
2596
|
-
if (date > 0 && Number(date) == date && (o.values.join("") !== o.value || o.type == "numeric")) {
|
|
2597
|
-
var d = new Date(Math.round((date - 25569) * 86400 * 1000));
|
|
2598
|
-
return d.getFullYear() + "-" + Helpers.two(d.getMonth()) + "-" + Helpers.two(d.getDate()) + ' 00:00:00';
|
|
2599
|
-
}
|
|
2600
|
-
|
|
2601
|
-
let complete = false;
|
|
2602
|
-
|
|
2603
|
-
if (o.values && o.values.length === o.tokens.length && o.values[o.values.length - 1].length >= o.tokens[o.tokens.length - 1].length) {
|
|
2604
|
-
complete = true;
|
|
2605
|
-
}
|
|
2606
|
-
|
|
2607
|
-
if (o.date[0] && o.date[1] && (o.date[2] || complete)) {
|
|
2608
|
-
if (!o.date[2]) {
|
|
2609
|
-
o.date[2] = 1;
|
|
2610
|
-
}
|
|
2611
|
-
|
|
2612
|
-
return o.date[0] + '-' + Helpers.two(o.date[1]) + '-' + Helpers.two(o.date[2]) + ' ' + Helpers.two(o.date[3]) + ':' + Helpers.two(o.date[4]) + ':' + Helpers.two(o.date[5]);
|
|
2613
|
-
}
|
|
2614
|
-
|
|
2615
|
-
return '';
|
|
2616
|
-
}
|
|
2617
|
-
|
|
2618
|
-
// Tokens
|
|
2619
|
-
const dateTokens = ['DAY', 'WD', 'DDDD', 'DDD', 'DD', 'D', 'Q', 'HH24', 'HH12', 'HH', '\\[H\\]', 'H', 'AM/PM', 'MI', 'SS', 'MS', 'YYYY', 'YYY', 'YY', 'Y', 'MONTH', 'MON', 'MMMMM', 'MMMM', 'MMM', 'MM', 'M', '.'];
|
|
2620
|
-
// All date tokens
|
|
2621
|
-
const allDateTokens = dateTokens.join('|')
|
|
2622
|
-
|
|
2623
|
-
Component.getDateString = function(value, options) {
|
|
2624
|
-
if (! options) {
|
|
2625
|
-
options = {};
|
|
2626
|
-
}
|
|
2627
|
-
|
|
2628
|
-
// Labels
|
|
2629
|
-
let format;
|
|
2630
|
-
|
|
2631
|
-
if (options && typeof(options) == 'object') {
|
|
2632
|
-
if (options.format) {
|
|
2633
|
-
format = options.format;
|
|
2634
|
-
} else if (options.mask) {
|
|
2635
|
-
format = options.mask;
|
|
2636
|
-
}
|
|
2637
|
-
} else {
|
|
2638
|
-
format = options;
|
|
2639
|
-
}
|
|
2640
|
-
|
|
2641
|
-
if (! format) {
|
|
2642
|
-
format = 'YYYY-MM-DD';
|
|
2643
|
-
}
|
|
2644
|
-
|
|
2645
|
-
format = format.toUpperCase();
|
|
2646
|
-
|
|
2647
|
-
// Date instance
|
|
2648
|
-
if (value instanceof Date) {
|
|
2649
|
-
value = Helpers.now(value);
|
|
2650
|
-
} else if (isNumber(value)) {
|
|
2651
|
-
value = Helpers.numToDate(value);
|
|
2652
|
-
}
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
// Expression to extract all tokens from the string
|
|
2656
|
-
let e = new RegExp(allDateTokens, 'gi');
|
|
2657
|
-
// Extract
|
|
2658
|
-
let t = format.match(e);
|
|
2659
|
-
|
|
2660
|
-
// Compatibility with Excel
|
|
2661
|
-
fixMinuteToken(t);
|
|
2662
|
-
|
|
2663
|
-
// Object
|
|
2664
|
-
const o = {
|
|
2665
|
-
tokens: t
|
|
2666
|
-
}
|
|
2667
|
-
|
|
2668
|
-
// Value
|
|
2669
|
-
if (value) {
|
|
2670
|
-
try {
|
|
2671
|
-
// Data
|
|
2672
|
-
o.data = extractDateAndTime(value);
|
|
2673
|
-
|
|
2674
|
-
if (o.data[1] && o.data[1] > 12) {
|
|
2675
|
-
throw new Error('Invalid date');
|
|
2676
|
-
} else if (o.data[4] && o.data[4] > 59) {
|
|
2677
|
-
throw new Error('Invalid date');
|
|
2678
|
-
} else if (o.data[5] && o.data[5] > 59) {
|
|
2679
|
-
throw new Error('Invalid date');
|
|
2680
|
-
} else if (o.data[0] != null && o.data[1] != null) {
|
|
2681
|
-
let day = new Date(o.data[0], o.data[1], 0).getDate();
|
|
2682
|
-
if (o.data[2] > day) {
|
|
2683
|
-
throw new Error('Invalid date');
|
|
2684
|
-
}
|
|
2685
|
-
}
|
|
2686
|
-
|
|
2687
|
-
// Value
|
|
2688
|
-
o.value = [];
|
|
2689
|
-
|
|
2690
|
-
// Calendar instance
|
|
2691
|
-
let calendar = new Date(o.data[0], o.data[1] - 1, o.data[2], o.data[3], o.data[4], o.data[5]);
|
|
2692
|
-
|
|
2693
|
-
// Get method
|
|
2694
|
-
const get = function (i) {
|
|
2695
|
-
// Token
|
|
2696
|
-
let t = this.tokens[i];
|
|
2697
|
-
|
|
2698
|
-
// Case token
|
|
2699
|
-
let s = t.toUpperCase();
|
|
2700
|
-
let v = null;
|
|
2701
|
-
|
|
2702
|
-
if (s === 'YYYY') {
|
|
2703
|
-
v = this.data[0];
|
|
2704
|
-
} else if (s === 'YYY') {
|
|
2705
|
-
v = this.data[0].substring(1, 4);
|
|
2706
|
-
} else if (s === 'YY') {
|
|
2707
|
-
v = this.data[0].substring(2, 4);
|
|
2708
|
-
} else if (s === 'Y') {
|
|
2709
|
-
v = this.data[0].substring(3, 4);
|
|
2710
|
-
} else if (t === 'MON') {
|
|
2711
|
-
v = Helpers.months[calendar.getMonth()].substr(0, 3).toUpperCase();
|
|
2712
|
-
} else if (t === 'mon') {
|
|
2713
|
-
v = Helpers.months[calendar.getMonth()].substr(0, 3).toLowerCase();
|
|
2714
|
-
} else if (t === 'MONTH') {
|
|
2715
|
-
v = Helpers.months[calendar.getMonth()].toUpperCase();
|
|
2716
|
-
} else if (t === 'month') {
|
|
2717
|
-
v = Helpers.months[calendar.getMonth()].toLowerCase();
|
|
2718
|
-
} else if (s === 'MMMMM') {
|
|
2719
|
-
v = Helpers.months[calendar.getMonth()].substr(0, 1);
|
|
2720
|
-
} else if (s === 'MMMM' || t === 'Month') {
|
|
2721
|
-
v = Helpers.months[calendar.getMonth()];
|
|
2722
|
-
} else if (s === 'MMM' || t == 'Mon') {
|
|
2723
|
-
v = Helpers.months[calendar.getMonth()].substr(0, 3);
|
|
2724
|
-
} else if (s === 'MM') {
|
|
2725
|
-
v = Helpers.two(this.data[1]);
|
|
2726
|
-
} else if (s === 'M') {
|
|
2727
|
-
v = calendar.getMonth() + 1;
|
|
2728
|
-
} else if (t === 'DAY') {
|
|
2729
|
-
v = Helpers.weekdays[calendar.getDay()].toUpperCase();
|
|
2730
|
-
} else if (t === 'day') {
|
|
2731
|
-
v = Helpers.weekdays[calendar.getDay()].toLowerCase();
|
|
2732
|
-
} else if (s === 'DDDD' || t == 'Day') {
|
|
2733
|
-
v = Helpers.weekdays[calendar.getDay()];
|
|
2734
|
-
} else if (s === 'DDD') {
|
|
2735
|
-
v = Helpers.weekdays[calendar.getDay()].substr(0, 3);
|
|
2736
|
-
} else if (s === 'DD') {
|
|
2737
|
-
v = Helpers.two(this.data[2]);
|
|
2738
|
-
} else if (s === 'D') {
|
|
2739
|
-
v = parseInt(this.data[2]);
|
|
2740
|
-
} else if (s === 'Q') {
|
|
2741
|
-
v = Math.floor((calendar.getMonth() + 3) / 3);
|
|
2742
|
-
} else if (s === 'HH24' || s === 'HH') {
|
|
2743
|
-
v = this.data[3]%24;
|
|
2744
|
-
if (this.tokens.indexOf('AM/PM') !== -1) {
|
|
2745
|
-
if (v > 12) {
|
|
2746
|
-
v -= 12;
|
|
2747
|
-
} else if (v == '0' || v == '00') {
|
|
2748
|
-
v = 12;
|
|
2749
|
-
}
|
|
2750
|
-
}
|
|
2751
|
-
v = Helpers.two(v);
|
|
2752
|
-
} else if (s === 'HH12') {
|
|
2753
|
-
v = this.data[3]%24;
|
|
2754
|
-
if (v > 12) {
|
|
2755
|
-
v = Helpers.two(v - 12);
|
|
2756
|
-
} else {
|
|
2757
|
-
v = Helpers.two(v);
|
|
2758
|
-
}
|
|
2759
|
-
} else if (s === 'H') {
|
|
2760
|
-
v = this.data[3]%24;
|
|
2761
|
-
if (this.tokens.indexOf('AM/PM') !== -1) {
|
|
2762
|
-
if (v > 12) {
|
|
2763
|
-
v -= 12;
|
|
2764
|
-
} else if (v == '0' || v == '00') {
|
|
2765
|
-
v = 12;
|
|
2766
|
-
}
|
|
2767
|
-
}
|
|
2768
|
-
} else if (s === '[H]') {
|
|
2769
|
-
v = this.data[3];
|
|
2770
|
-
} else if (s === 'MI') {
|
|
2771
|
-
v = Helpers.two(this.data[4]);
|
|
2772
|
-
} else if (s === 'I') {
|
|
2773
|
-
v = parseInt(this.data[4]);
|
|
2774
|
-
} else if (s === 'SS') {
|
|
2775
|
-
v = Helpers.two(this.data[5]);
|
|
2776
|
-
} else if (s === 'S') {
|
|
2777
|
-
v = parseInt(this.data[5]);
|
|
2778
|
-
} else if (s === 'MS') {
|
|
2779
|
-
v = calendar.getMilliseconds();
|
|
2780
|
-
} else if (s === 'AM/PM') {
|
|
2781
|
-
if (this.data[3] >= 12) {
|
|
2782
|
-
v = 'PM';
|
|
2783
|
-
} else {
|
|
2784
|
-
v = 'AM';
|
|
2785
|
-
}
|
|
2786
|
-
} else if (s === 'WD') {
|
|
2787
|
-
v = Helpers.weekdays[calendar.getDay()];
|
|
2788
|
-
}
|
|
2789
|
-
|
|
2790
|
-
if (v === null) {
|
|
2791
|
-
this.value[i] = this.tokens[i];
|
|
2792
|
-
} else {
|
|
2793
|
-
this.value[i] = v;
|
|
2794
|
-
}
|
|
2795
|
-
}
|
|
2796
|
-
|
|
2797
|
-
for (let i = 0; i < o.tokens.length; i++) {
|
|
2798
|
-
get.call(o, i);
|
|
61
|
+
const filterData = function(year, month) {
|
|
62
|
+
// Data for the month
|
|
63
|
+
let data = {};
|
|
64
|
+
if (Array.isArray(this.data)) {
|
|
65
|
+
this.data.map(function (v) {
|
|
66
|
+
let d = year + '-' + Helpers.two(month + 1);
|
|
67
|
+
if (v.date.substring(0, 7) === d) {
|
|
68
|
+
if (!data[v.date]) {
|
|
69
|
+
data[v.date] = [];
|
|
2799
70
|
}
|
|
2800
|
-
|
|
2801
|
-
value = o.value.join('');
|
|
2802
|
-
} catch (e) {
|
|
2803
|
-
console.log(e)
|
|
2804
|
-
value = '';
|
|
71
|
+
data[v.date].push(v);
|
|
2805
72
|
}
|
|
2806
|
-
}
|
|
2807
|
-
|
|
2808
|
-
return value;
|
|
73
|
+
});
|
|
2809
74
|
}
|
|
75
|
+
return data;
|
|
76
|
+
}
|
|
2810
77
|
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
78
|
+
// Get the short weekdays name
|
|
79
|
+
const getWeekdays = function(firstDayOfWeek) {
|
|
80
|
+
const reorderedWeekdays = [];
|
|
81
|
+
for (let i = 0; i < 7; i++) {
|
|
82
|
+
const dayIndex = (firstDayOfWeek + i) % 7;
|
|
83
|
+
reorderedWeekdays.push(Helpers.weekdays[dayIndex]);
|
|
84
|
+
}
|
|
2815
85
|
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
86
|
+
return reorderedWeekdays.map(w => {
|
|
87
|
+
return { title: w.substring(0, 1) };
|
|
88
|
+
});
|
|
89
|
+
}
|
|
2820
90
|
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
91
|
+
const Views = function(self) {
|
|
92
|
+
const view = {};
|
|
93
|
+
|
|
94
|
+
// Create years container
|
|
95
|
+
view.years = [];
|
|
96
|
+
view.months = [];
|
|
97
|
+
view.days = [];
|
|
98
|
+
view.hours = [];
|
|
99
|
+
view.minutes = [];
|
|
100
|
+
|
|
101
|
+
for (let i = 0; i < 16; i++) {
|
|
102
|
+
view.years.push({
|
|
103
|
+
title: null,
|
|
104
|
+
value: null,
|
|
105
|
+
selected: false,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
2828
108
|
|
|
2829
|
-
|
|
109
|
+
for (let i = 0; i < 12; i++) {
|
|
110
|
+
view.months.push({
|
|
111
|
+
title: null,
|
|
112
|
+
value: null,
|
|
113
|
+
selected: false,
|
|
114
|
+
});
|
|
2830
115
|
}
|
|
2831
116
|
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
property = 'textContent';
|
|
2840
|
-
}
|
|
2841
|
-
// Value
|
|
2842
|
-
let value = element[property];
|
|
2843
|
-
// Get the mask
|
|
2844
|
-
if (! mask) {
|
|
2845
|
-
mask = element.getAttribute('data-mask');
|
|
2846
|
-
}
|
|
2847
|
-
// Keep the current caret position
|
|
2848
|
-
let caret = getCaret(element);
|
|
2849
|
-
if (caret) {
|
|
2850
|
-
value = value.substring(0, caret) + hiddenCaret + value.substring(caret);
|
|
2851
|
-
}
|
|
117
|
+
for (let i = 0; i < 42; i++) {
|
|
118
|
+
view.days.push({
|
|
119
|
+
title: null,
|
|
120
|
+
value: null,
|
|
121
|
+
selected: false,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
2852
124
|
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
// Apply the result back to the element
|
|
2859
|
-
if (newValue !== value && ! e.inputType.includes('delete')) {
|
|
2860
|
-
// Set the caret to the position before transformation
|
|
2861
|
-
let caret = newValue.indexOf(hiddenCaret);
|
|
2862
|
-
if (caret !== -1) {
|
|
2863
|
-
// Apply value
|
|
2864
|
-
element[property] = newValue.replace(hiddenCaret, "");
|
|
2865
|
-
// Set caret
|
|
2866
|
-
setCaret.call(element, caret);
|
|
2867
|
-
} else {
|
|
2868
|
-
// Apply value
|
|
2869
|
-
element[property] = newValue;
|
|
2870
|
-
// Make sure the caret is positioned in the end
|
|
2871
|
-
focus(element);
|
|
2872
|
-
}
|
|
2873
|
-
}
|
|
125
|
+
for (let i = 0; i < 24; i++) {
|
|
126
|
+
view.hours.push({
|
|
127
|
+
title: Helpers.two(i),
|
|
128
|
+
value: i
|
|
129
|
+
});
|
|
2874
130
|
}
|
|
2875
131
|
|
|
2876
|
-
|
|
132
|
+
for (let i = 0; i < 60; i++) {
|
|
133
|
+
view.minutes.push({
|
|
134
|
+
title: Helpers.two(i),
|
|
135
|
+
value: i
|
|
136
|
+
});
|
|
137
|
+
}
|
|
2877
138
|
|
|
2878
|
-
|
|
139
|
+
view.years.update = function(date) {
|
|
140
|
+
let year = date.getUTCFullYear();
|
|
141
|
+
let start = year - (year % 16);
|
|
2879
142
|
|
|
2880
|
-
|
|
2881
|
-
|
|
143
|
+
for (let i = 0; i < 16; i++) {
|
|
144
|
+
let item = view.years[i];
|
|
145
|
+
let value = start + i;
|
|
2882
146
|
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
num = num.trim();
|
|
2886
|
-
}
|
|
2887
|
-
return !isNaN(num) && num !== null && num !== '';
|
|
2888
|
-
}
|
|
147
|
+
item.title = value
|
|
148
|
+
item.value = value;
|
|
2889
149
|
|
|
2890
|
-
|
|
2891
|
-
* Create a data calendar object based on the view
|
|
2892
|
-
*/
|
|
2893
|
-
const views = {
|
|
2894
|
-
years: function(date) {
|
|
2895
|
-
let year = date.getUTCFullYear();
|
|
2896
|
-
let result = [];
|
|
2897
|
-
let start = year % 16;
|
|
2898
|
-
let complement = 16 - start;
|
|
2899
|
-
|
|
2900
|
-
for (let i = year-start; i < year+complement; i++) {
|
|
2901
|
-
let item = {
|
|
2902
|
-
title: i,
|
|
2903
|
-
value: i
|
|
2904
|
-
};
|
|
2905
|
-
result.push(item);
|
|
2906
|
-
// Select cursor
|
|
2907
|
-
if (this.cursor.y === i) {
|
|
2908
|
-
// Select item
|
|
150
|
+
if (self.cursor.y === value) {
|
|
2909
151
|
item.selected = true;
|
|
2910
|
-
//
|
|
2911
|
-
|
|
152
|
+
// Current item
|
|
153
|
+
self.cursor.current = item;
|
|
154
|
+
} else {
|
|
155
|
+
item.selected = false;
|
|
2912
156
|
}
|
|
2913
157
|
}
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
months
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
view.months.update = function(date) {
|
|
2917
161
|
let year = date.getUTCFullYear();
|
|
2918
|
-
|
|
162
|
+
|
|
2919
163
|
for (let i = 0; i < 12; i++) {
|
|
2920
|
-
let item =
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
// Select cursor
|
|
2927
|
-
if (this.cursor.y === year && this.cursor.m === i) {
|
|
2928
|
-
// Select item
|
|
164
|
+
let item = view.months[i];
|
|
165
|
+
|
|
166
|
+
item.title = Helpers.months[i].substring(0,3);
|
|
167
|
+
item.value = i;
|
|
168
|
+
|
|
169
|
+
if (self.cursor.y === year && self.cursor.m === i) {
|
|
2929
170
|
item.selected = true;
|
|
2930
|
-
//
|
|
2931
|
-
|
|
171
|
+
// Current item
|
|
172
|
+
self.cursor.current = item;
|
|
173
|
+
} else {
|
|
174
|
+
item.selected = false;
|
|
2932
175
|
}
|
|
2933
176
|
}
|
|
177
|
+
}
|
|
2934
178
|
|
|
2935
|
-
|
|
2936
|
-
},
|
|
2937
|
-
days: function(date) {
|
|
179
|
+
view.days.update = function(date) {
|
|
2938
180
|
let year = date.getUTCFullYear();
|
|
2939
181
|
let month = date.getUTCMonth();
|
|
2940
|
-
let data = filterData.call(
|
|
182
|
+
let data = filterData.call(self, year, month);
|
|
2941
183
|
|
|
2942
184
|
// First day
|
|
2943
185
|
let tmp = new Date(Date.UTC(year, month, 1, 0, 0, 0));
|
|
2944
186
|
let firstDayOfMonth = tmp.getUTCDay();
|
|
2945
|
-
let firstDayOfWeek =
|
|
187
|
+
let firstDayOfWeek = self.startingDay ?? 0;
|
|
2946
188
|
|
|
2947
|
-
// Calculate offset based on desired first day of week
|
|
2948
|
-
// firstDayOfWeek: 0 = Sunday, 1 = Monday, 2 = Tuesday, etc.
|
|
189
|
+
// Calculate offset based on desired first day of week. firstDayOfWeek: 0 = Sunday, 1 = Monday, 2 = Tuesday, etc.
|
|
2949
190
|
let offset = (firstDayOfMonth - firstDayOfWeek + 7) % 7;
|
|
2950
191
|
|
|
2951
|
-
let
|
|
2952
|
-
|
|
192
|
+
let index = -1 * offset;
|
|
193
|
+
|
|
194
|
+
for (let i = 0; i < 42; i++) {
|
|
195
|
+
index++;
|
|
196
|
+
// Item
|
|
197
|
+
let item = view.days[i];
|
|
2953
198
|
// Get the day
|
|
2954
|
-
tmp = new Date(Date.UTC(year, month,
|
|
199
|
+
tmp = new Date(Date.UTC(year, month, index, 0, 0, 0));
|
|
2955
200
|
// Day
|
|
2956
201
|
let day = tmp.getUTCDate();
|
|
202
|
+
|
|
2957
203
|
// Create the item
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
204
|
+
item.title = day;
|
|
205
|
+
item.value = index;
|
|
206
|
+
item.number = Helpers.dateToNum(tmp.toISOString().substring(0, 10));
|
|
207
|
+
|
|
208
|
+
// Reset range properties for each item
|
|
209
|
+
item.start = false;
|
|
210
|
+
item.end = false;
|
|
211
|
+
item.range = false;
|
|
212
|
+
item.last = false;
|
|
213
|
+
item.disabled = false;
|
|
214
|
+
item.data = null;
|
|
215
|
+
|
|
2965
216
|
// Check selections
|
|
2966
217
|
if (tmp.getUTCMonth() !== month) {
|
|
2967
218
|
// Days are not in the current month
|
|
2968
219
|
item.grey = true;
|
|
2969
220
|
} else {
|
|
2970
221
|
// Check for data
|
|
2971
|
-
let d = [ year, Helpers.two(month+1), Helpers.two(day)].join('-');
|
|
222
|
+
let d = [ year, Helpers.two(month+1), Helpers.two(day) ].join('-');
|
|
223
|
+
|
|
2972
224
|
if (data && data[d]) {
|
|
2973
225
|
item.data = data[d];
|
|
2974
226
|
}
|
|
227
|
+
|
|
228
|
+
item.grey = false;
|
|
2975
229
|
}
|
|
2976
230
|
// Month
|
|
2977
231
|
let m = tmp.getUTCMonth();
|
|
232
|
+
|
|
2978
233
|
// Select cursor
|
|
2979
|
-
if (
|
|
2980
|
-
// Select item
|
|
234
|
+
if (self.cursor.y === year && self.cursor.m === m && self.cursor.d === day) {
|
|
2981
235
|
item.selected = true;
|
|
2982
|
-
//
|
|
2983
|
-
|
|
236
|
+
// Current item
|
|
237
|
+
self.cursor.current = item;
|
|
238
|
+
} else {
|
|
239
|
+
item.selected = false;
|
|
2984
240
|
}
|
|
2985
|
-
// Valid ranges
|
|
2986
|
-
if (this.validRange) {
|
|
2987
|
-
let current = year + '-' + Helpers.two(m+1) + '-' + Helpers.two(day);
|
|
2988
|
-
let test1;
|
|
2989
|
-
let test2;
|
|
2990
241
|
|
|
2991
|
-
|
|
2992
|
-
|
|
242
|
+
|
|
243
|
+
// Valid ranges
|
|
244
|
+
if (self.validRange) {
|
|
245
|
+
if (typeof self.validRange === 'function') {
|
|
246
|
+
let ret = self.validRange(day,m,year,item);
|
|
2993
247
|
if (typeof ret !== 'undefined') {
|
|
2994
248
|
item.disabled = ret;
|
|
2995
249
|
}
|
|
2996
250
|
} else {
|
|
2997
|
-
|
|
2998
|
-
test1 = true;
|
|
2999
|
-
} else {
|
|
3000
|
-
test1 = false;
|
|
3001
|
-
}
|
|
251
|
+
let current = year + '-' + Helpers.two(m+1) + '-' + Helpers.two(day);
|
|
3002
252
|
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
} else {
|
|
3006
|
-
test2 = false;
|
|
3007
|
-
}
|
|
253
|
+
let test1 = !self.validRange[0] || current >= self.validRange[0].substr(0, 10);
|
|
254
|
+
let test2 = !self.validRange[1] || current <= self.validRange[1].substr(0, 10);
|
|
3008
255
|
|
|
3009
256
|
if (! (test1 && test2)) {
|
|
3010
257
|
item.disabled = true;
|
|
@@ -3013,111 +260,37 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3013
260
|
}
|
|
3014
261
|
|
|
3015
262
|
// Select range
|
|
3016
|
-
if (
|
|
3017
|
-
//
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
if (this.rangeValues[1] === item.number) {
|
|
3023
|
-
item.range = true;
|
|
3024
|
-
item.end = true;
|
|
3025
|
-
}
|
|
3026
|
-
// Re-recreate teh range
|
|
3027
|
-
if (this.rangeValues[0] && this.rangeValues[1]) {
|
|
3028
|
-
if (this.rangeValues[0] <= item.number && this.rangeValues[1] >= item.number) {
|
|
3029
|
-
item.range = true;
|
|
3030
|
-
}
|
|
3031
|
-
}
|
|
263
|
+
if (self.range && self.rangeValues) {
|
|
264
|
+
// Only mark start/end if the number matches
|
|
265
|
+
item.start = self.rangeValues[0] === item.number;
|
|
266
|
+
item.end = self.rangeValues[1] === item.number;
|
|
267
|
+
// Mark as part of range if between start and end
|
|
268
|
+
item.range = self.rangeValues[0] && self.rangeValues[1] && self.rangeValues[0] <= item.number && self.rangeValues[1] >= item.number;
|
|
3032
269
|
}
|
|
3033
270
|
}
|
|
3034
|
-
|
|
3035
|
-
return result;
|
|
3036
|
-
},
|
|
3037
|
-
hours: function() {
|
|
3038
|
-
let result = [];
|
|
3039
|
-
for (let i = 0; i < 24; i++) {
|
|
3040
|
-
let item = {
|
|
3041
|
-
title: Helpers.two(i),
|
|
3042
|
-
value: i
|
|
3043
|
-
};
|
|
3044
|
-
result.push(item);
|
|
3045
|
-
}
|
|
3046
|
-
return result;
|
|
3047
|
-
},
|
|
3048
|
-
minutes: function() {
|
|
3049
|
-
let result = [];
|
|
3050
|
-
for (let i = 0; i < 60; i++) {
|
|
3051
|
-
let item = {
|
|
3052
|
-
title: Helpers.two(i),
|
|
3053
|
-
value: i
|
|
3054
|
-
};
|
|
3055
|
-
result.push(item);
|
|
3056
|
-
}
|
|
3057
|
-
return result;
|
|
3058
|
-
}
|
|
3059
|
-
}
|
|
3060
|
-
|
|
3061
|
-
const filterData = function(year, month) {
|
|
3062
|
-
// Data for the month
|
|
3063
|
-
let data = {};
|
|
3064
|
-
if (Array.isArray(this.data)) {
|
|
3065
|
-
this.data.map(function (v) {
|
|
3066
|
-
let d = year + '-' + Helpers.two(month + 1);
|
|
3067
|
-
if (v.date.substring(0, 7) === d) {
|
|
3068
|
-
if (!data[v.date]) {
|
|
3069
|
-
data[v.date] = [];
|
|
3070
|
-
}
|
|
3071
|
-
data[v.date].push(v);
|
|
3072
|
-
}
|
|
3073
|
-
});
|
|
3074
|
-
}
|
|
3075
|
-
return data;
|
|
3076
|
-
}
|
|
3077
|
-
|
|
3078
|
-
// Get the short weekdays name
|
|
3079
|
-
const getWeekdays = function(firstDayOfWeek) {
|
|
3080
|
-
const reorderedWeekdays = [];
|
|
3081
|
-
for (let i = 0; i < 7; i++) {
|
|
3082
|
-
const dayIndex = (firstDayOfWeek + i) % 7;
|
|
3083
|
-
reorderedWeekdays.push(Helpers.weekdays[dayIndex]);
|
|
3084
271
|
}
|
|
3085
272
|
|
|
3086
|
-
return
|
|
3087
|
-
return { title: w.substring(0, 1) };
|
|
3088
|
-
});
|
|
273
|
+
return view;
|
|
3089
274
|
}
|
|
3090
275
|
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
|
|
3094
|
-
return this.view === 'days' ? 7 : 4;
|
|
3095
|
-
}
|
|
3096
|
-
|
|
3097
|
-
return 1;
|
|
276
|
+
const isTrue = function(v) {
|
|
277
|
+
return v === true || v === 'true';
|
|
3098
278
|
}
|
|
3099
279
|
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
if (this.view === 'years') {
|
|
3104
|
-
position = 0;
|
|
3105
|
-
} else if (this.view === 'months') {
|
|
3106
|
-
position = 1;
|
|
280
|
+
const isNumber = function (num) {
|
|
281
|
+
if (typeof(num) === 'string') {
|
|
282
|
+
num = num.trim();
|
|
3107
283
|
}
|
|
3108
|
-
return
|
|
284
|
+
return !isNaN(num) && num !== null && num !== '';
|
|
3109
285
|
}
|
|
3110
286
|
|
|
3111
|
-
const Calendar = function(children, { onchange, onload }) {
|
|
287
|
+
const Calendar = function(children, { onchange, onload, track }) {
|
|
3112
288
|
let self = this;
|
|
3113
289
|
|
|
3114
290
|
// Event
|
|
3115
291
|
let change = self.onchange;
|
|
3116
292
|
self.onchange = null;
|
|
3117
293
|
|
|
3118
|
-
// Decide the type based on the size of the screen
|
|
3119
|
-
let autoType = self.type === 'auto';
|
|
3120
|
-
|
|
3121
294
|
// Weekdays
|
|
3122
295
|
self.weekdays = getWeekdays(self.startingDay ?? 0);
|
|
3123
296
|
|
|
@@ -3127,49 +300,121 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3127
300
|
// Time
|
|
3128
301
|
self.time = !! self.time;
|
|
3129
302
|
|
|
303
|
+
// Range values
|
|
304
|
+
self.rangeValues = null;
|
|
305
|
+
|
|
3130
306
|
// Calendar date
|
|
3131
307
|
let date = new Date();
|
|
3132
308
|
|
|
3133
|
-
//
|
|
3134
|
-
|
|
3135
|
-
|
|
309
|
+
// Views
|
|
310
|
+
const views = Views(self);
|
|
311
|
+
const hours = views.hours;
|
|
312
|
+
const minutes = views.minutes;
|
|
313
|
+
|
|
314
|
+
// Initial view
|
|
315
|
+
self.view = 'days';
|
|
316
|
+
|
|
317
|
+
// Auto Input
|
|
318
|
+
if (self.input === 'auto') {
|
|
319
|
+
self.input = document.createElement('input');
|
|
320
|
+
self.input.type = 'text';
|
|
3136
321
|
}
|
|
3137
322
|
|
|
3138
|
-
// Range
|
|
3139
|
-
self.rangeValues = null;
|
|
3140
323
|
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
d = new Date(Date.UTC(...d));
|
|
3149
|
-
} else if (typeof(d) === 'string') {
|
|
3150
|
-
d = new Date(d);
|
|
324
|
+
// Get the position of the data based on the view
|
|
325
|
+
const getPosition = function() {
|
|
326
|
+
let position = 2;
|
|
327
|
+
if (self.view === 'years') {
|
|
328
|
+
position = 0;
|
|
329
|
+
} else if (self.view === 'months') {
|
|
330
|
+
position = 1;
|
|
3151
331
|
}
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
332
|
+
return position;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const setView = function(e) {
|
|
336
|
+
if (typeof e === 'object') {
|
|
337
|
+
e = this.getAttribute('data-view');
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Valid views
|
|
341
|
+
const validViews = ['days', 'months', 'years'];
|
|
342
|
+
|
|
343
|
+
// Define new view
|
|
344
|
+
if (validViews.includes(e) && self.view !== e) {
|
|
345
|
+
self.view = e;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const reloadView = function(reset) {
|
|
350
|
+
if (reset) {
|
|
351
|
+
// Update options to the view
|
|
352
|
+
self.options = views[self.view];
|
|
353
|
+
}
|
|
354
|
+
// Update the values of hte options of hte view
|
|
355
|
+
views[self.view]?.update.call(self, date);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const getValue = function() {
|
|
359
|
+
let value = null;
|
|
360
|
+
if (isTrue(self.range)) {
|
|
361
|
+
if (Array.isArray(self.rangeValues)) {
|
|
362
|
+
if (isTrue(self.numeric)) {
|
|
363
|
+
value = self.rangeValues;
|
|
364
|
+
} else {
|
|
365
|
+
value = [
|
|
366
|
+
Helpers.numToDate(self.rangeValues[0]).substring(0, 10),
|
|
367
|
+
Helpers.numToDate(self.rangeValues[1]).substring(0, 10)
|
|
368
|
+
];
|
|
369
|
+
}
|
|
370
|
+
}
|
|
3169
371
|
} else {
|
|
3170
|
-
|
|
3171
|
-
|
|
372
|
+
value = getDate();
|
|
373
|
+
if (isTrue(self.numeric)) {
|
|
374
|
+
value = Helpers.dateToNum(value);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return value;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const setValue = function(v) {
|
|
381
|
+
let d = new Date();
|
|
382
|
+
if (v) {
|
|
383
|
+
if (isTrue(self.range)) {
|
|
384
|
+
if (v) {
|
|
385
|
+
if (! Array.isArray(v)) {
|
|
386
|
+
v = v.toString().split(',');
|
|
387
|
+
}
|
|
388
|
+
self.rangeValues = [...v];
|
|
389
|
+
|
|
390
|
+
if (v[0] && typeof (v[0]) === 'string' && v[0].indexOf('-')) {
|
|
391
|
+
self.rangeValues[0] = Helpers.dateToNum(v[0]);
|
|
392
|
+
}
|
|
393
|
+
if (v[1] && typeof (v[1]) === 'string' && v[1].indexOf('-')) {
|
|
394
|
+
self.rangeValues[1] = Helpers.dateToNum(v[1]);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
v = v[0];
|
|
398
|
+
}
|
|
399
|
+
} else if (typeof v === 'string' && v.includes(',')) {
|
|
400
|
+
v = v.split(',')[0];
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (v) {
|
|
404
|
+
v = isNumber(v) ? Helpers.numToDate(v) : v;
|
|
405
|
+
d = new Date(v + ' GMT+0');
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// if no date is defined
|
|
409
|
+
if (! Helpers.isValidDate(d)) {
|
|
410
|
+
d = new Date();
|
|
411
|
+
}
|
|
3172
412
|
}
|
|
413
|
+
|
|
414
|
+
// Update the internal calendar date
|
|
415
|
+
setDate(d, true);
|
|
416
|
+
// Update the view
|
|
417
|
+
reloadView();
|
|
3173
418
|
}
|
|
3174
419
|
|
|
3175
420
|
const getDate = function() {
|
|
@@ -3183,47 +428,129 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3183
428
|
}
|
|
3184
429
|
}
|
|
3185
430
|
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
431
|
+
const setDate = function(d, update) {
|
|
432
|
+
if (Array.isArray(d)) {
|
|
433
|
+
d = new Date(Date.UTC(...d));
|
|
434
|
+
} else if (typeof(d) === 'string') {
|
|
435
|
+
d = new Date(d);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Update the date
|
|
439
|
+
let value = d.toISOString().substring(0,10).split('-');
|
|
440
|
+
let month = Helpers.months[parseInt(value[1])-1];
|
|
441
|
+
let year = parseInt(value[0]);
|
|
442
|
+
|
|
443
|
+
if (self.month !== month) {
|
|
444
|
+
self.month = month;
|
|
445
|
+
}
|
|
446
|
+
if (self.year !== year) {
|
|
447
|
+
self.year = year;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Update the time
|
|
451
|
+
let time = d.toISOString().substring(11,19).split(':');
|
|
452
|
+
let hour = parseInt(time[0]);
|
|
453
|
+
let minute = parseInt(time[1]);
|
|
454
|
+
|
|
455
|
+
if (self.hour !== hour) {
|
|
456
|
+
self.hour = hour;
|
|
457
|
+
}
|
|
458
|
+
if (self.minute !== minute) {
|
|
459
|
+
self.minute = minute;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Update internal date
|
|
463
|
+
date = d;
|
|
464
|
+
|
|
465
|
+
// Update cursor information
|
|
466
|
+
if (update) {
|
|
467
|
+
updateCursor();
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const updateCursor = function() {
|
|
472
|
+
self.cursor.y = date.getUTCFullYear();
|
|
473
|
+
self.cursor.m = date.getUTCMonth();
|
|
474
|
+
self.cursor.d = date.getUTCDate();
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const resetCursor = function() {
|
|
3191
478
|
// Remove selection from the current object
|
|
3192
|
-
let
|
|
3193
|
-
|
|
3194
|
-
|
|
479
|
+
let current = self.cursor.current;
|
|
480
|
+
// Current item
|
|
481
|
+
if (typeof current !== 'undefined') {
|
|
482
|
+
current.selected = false;
|
|
3195
483
|
}
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
//
|
|
3200
|
-
|
|
3201
|
-
y: d.getUTCFullYear(),
|
|
3202
|
-
m: d.getUTCMonth(),
|
|
3203
|
-
d: d.getUTCDate(),
|
|
3204
|
-
};
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const setCursor = function(s) {
|
|
487
|
+
// Reset current visual cursor
|
|
488
|
+
resetCursor();
|
|
3205
489
|
// Update cursor based on the object position
|
|
3206
490
|
if (s) {
|
|
491
|
+
// Update current
|
|
492
|
+
self.cursor.current = s;
|
|
3207
493
|
// Update selected property
|
|
3208
494
|
s.selected = true;
|
|
3209
|
-
|
|
3210
|
-
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
updateCursor();
|
|
498
|
+
|
|
499
|
+
// Update range
|
|
500
|
+
if (isTrue(self.range)) {
|
|
501
|
+
updateRange(s)
|
|
3211
502
|
}
|
|
3212
503
|
|
|
3213
504
|
Dispatch.call(self, self.onupdate, 'update', {
|
|
3214
505
|
instance: self,
|
|
3215
|
-
value:
|
|
506
|
+
value: date.toISOString(),
|
|
3216
507
|
});
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const select = function(e, s) {
|
|
511
|
+
// Get new date content
|
|
512
|
+
let d = updateDate(s.value, getPosition());
|
|
513
|
+
// New date
|
|
514
|
+
setDate(new Date(Date.UTC(...d)))
|
|
515
|
+
// Based where was the click
|
|
516
|
+
if (self.view !== 'days') {
|
|
517
|
+
// Back to the days
|
|
518
|
+
self.view = 'days';
|
|
519
|
+
} else if (! s.disabled) {
|
|
520
|
+
setCursor(s);
|
|
521
|
+
|
|
522
|
+
if (isTrue(self.range)) {
|
|
523
|
+
// Start a new range
|
|
524
|
+
if (self.rangeValues && (self.rangeValues[0] >= s.number || self.rangeValues[1])) {
|
|
525
|
+
destroyRange();
|
|
526
|
+
}
|
|
527
|
+
// Range
|
|
528
|
+
s.range = true;
|
|
529
|
+
// Update range
|
|
530
|
+
if (! self.rangeValues) {
|
|
531
|
+
s.start = true;
|
|
532
|
+
self.rangeValues = [s.number, null];
|
|
533
|
+
} else {
|
|
534
|
+
s.end = true;
|
|
535
|
+
self.rangeValues[1] = s.number;
|
|
536
|
+
}
|
|
537
|
+
} else {
|
|
538
|
+
update();
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
3217
542
|
|
|
3218
|
-
|
|
543
|
+
// Update Calendar
|
|
544
|
+
const update = function(e) {
|
|
545
|
+
self.setValue(getValue());
|
|
546
|
+
self.close({ origin: 'button' });
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const reset = function() {
|
|
550
|
+
self.setValue('');
|
|
551
|
+
self.close({ origin: 'button' });
|
|
3219
552
|
}
|
|
3220
553
|
|
|
3221
|
-
/**
|
|
3222
|
-
* Update the current date
|
|
3223
|
-
* @param {number} v new value for year, month or day
|
|
3224
|
-
* @param {number} position (0,1,2 - year,month,day)
|
|
3225
|
-
* @returns {number[]}
|
|
3226
|
-
*/
|
|
3227
554
|
const updateDate = function(v, position) {
|
|
3228
555
|
// Current internal date
|
|
3229
556
|
let value = [date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), self.hour, self.minute, 0];
|
|
@@ -3233,11 +560,11 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3233
560
|
return value;
|
|
3234
561
|
}
|
|
3235
562
|
|
|
3236
|
-
/**
|
|
3237
|
-
* This method move the data from the view up or down
|
|
3238
|
-
* @param direction
|
|
3239
|
-
*/
|
|
3240
563
|
const move = function(direction) {
|
|
564
|
+
// Reset visual cursor
|
|
565
|
+
resetCursor();
|
|
566
|
+
|
|
567
|
+
// Value
|
|
3241
568
|
let value;
|
|
3242
569
|
|
|
3243
570
|
// Update the new internal date
|
|
@@ -3249,71 +576,73 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3249
576
|
value = updateDate(date.getUTCFullYear()+direction, 0);
|
|
3250
577
|
} else if (self.view === 'years') {
|
|
3251
578
|
// Select the new internal date
|
|
3252
|
-
value = updateDate(date.
|
|
579
|
+
value = updateDate(date.getUTCFullYear()+(direction*16), 0);
|
|
3253
580
|
}
|
|
3254
581
|
|
|
3255
582
|
// Update view
|
|
3256
|
-
|
|
3257
|
-
|
|
583
|
+
setDate(value);
|
|
584
|
+
|
|
585
|
+
// Reload content of the view
|
|
586
|
+
reloadView();
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const getJump = function(e) {
|
|
590
|
+
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
|
|
591
|
+
return self.view === 'days' ? 7 : 4;
|
|
3258
592
|
}
|
|
593
|
+
|
|
594
|
+
return 1;
|
|
3259
595
|
}
|
|
3260
596
|
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
//
|
|
3273
|
-
|
|
597
|
+
const prev = function(e) {
|
|
598
|
+
if (e && e.type === 'keydown') {
|
|
599
|
+
// Current index
|
|
600
|
+
let total = self.options.length;
|
|
601
|
+
let position = self.options.indexOf(self.cursor.current) - getJump(e);
|
|
602
|
+
if (position < 0) {
|
|
603
|
+
// Next month
|
|
604
|
+
move(-1);
|
|
605
|
+
// New position
|
|
606
|
+
position = total + position;
|
|
607
|
+
}
|
|
608
|
+
// Update cursor
|
|
609
|
+
setCursor(self.options[position])
|
|
610
|
+
} else {
|
|
611
|
+
move(-1);
|
|
3274
612
|
}
|
|
613
|
+
}
|
|
3275
614
|
|
|
3276
|
-
|
|
3277
|
-
|
|
615
|
+
const next = function(e) {
|
|
616
|
+
if (e && e.type === 'keydown') {
|
|
617
|
+
// Current index
|
|
618
|
+
let total = self.options.length;
|
|
619
|
+
let position = self.options.indexOf(self.cursor.current) + getJump(e);
|
|
620
|
+
if (position >= total) {
|
|
621
|
+
// Next month
|
|
622
|
+
move(1);
|
|
623
|
+
// New position
|
|
624
|
+
position = position - total;
|
|
625
|
+
}
|
|
626
|
+
// Update cursor
|
|
627
|
+
setCursor(self.options[position])
|
|
628
|
+
} else {
|
|
629
|
+
move(1);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
3278
632
|
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
index = index - 42;
|
|
3287
|
-
}
|
|
3288
|
-
} else if (self.view === 'years') {
|
|
3289
|
-
if (index < 0) {
|
|
3290
|
-
index = 4 + index;
|
|
3291
|
-
} else {
|
|
3292
|
-
index = index - 4;
|
|
3293
|
-
}
|
|
3294
|
-
} else if (self.view === 'months') {
|
|
3295
|
-
if (index < 0) {
|
|
3296
|
-
index = 12 + index;
|
|
3297
|
-
} else {
|
|
3298
|
-
index = index - 12;
|
|
3299
|
-
}
|
|
633
|
+
const getInput = function() {
|
|
634
|
+
let input = self.input;
|
|
635
|
+
if (input && input.current) {
|
|
636
|
+
input = input.current;
|
|
637
|
+
} else {
|
|
638
|
+
if (self.input) {
|
|
639
|
+
input = self.input;
|
|
3300
640
|
}
|
|
3301
|
-
|
|
3302
|
-
// Move the data up or down
|
|
3303
|
-
move(direction > 0 ? 1 : -1);
|
|
3304
641
|
}
|
|
3305
642
|
|
|
3306
|
-
|
|
3307
|
-
setCursor(self.options[index]);
|
|
3308
|
-
|
|
3309
|
-
// Update ranges
|
|
3310
|
-
updateRange(self.options[index])
|
|
643
|
+
return input;
|
|
3311
644
|
}
|
|
3312
645
|
|
|
3313
|
-
/**
|
|
3314
|
-
* Update the visible range
|
|
3315
|
-
* @param s
|
|
3316
|
-
*/
|
|
3317
646
|
const updateRange = function(s) {
|
|
3318
647
|
if (self.range && self.view === 'days' && self.rangeValues) {
|
|
3319
648
|
// Creating a range
|
|
@@ -3322,137 +651,101 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3322
651
|
if (number) {
|
|
3323
652
|
// Update range properties
|
|
3324
653
|
for (let i = 0; i < self.options.length; i++) {
|
|
3325
|
-
// Item number
|
|
3326
654
|
let v = self.options[i].number;
|
|
3327
655
|
// Update property condition
|
|
3328
656
|
self.options[i].range = v >= self.rangeValues[0] && v <= number;
|
|
3329
|
-
|
|
3330
|
-
if (v === number) {
|
|
3331
|
-
self.options[i].last = true;
|
|
3332
|
-
} else {
|
|
3333
|
-
self.options[i].last = false;
|
|
3334
|
-
}
|
|
657
|
+
self.options[i].last = (v === number);
|
|
3335
658
|
}
|
|
3336
659
|
}
|
|
3337
660
|
}
|
|
3338
661
|
}
|
|
3339
662
|
}
|
|
3340
663
|
|
|
3341
|
-
/**
|
|
3342
|
-
* Destroy the range
|
|
3343
|
-
*/
|
|
3344
664
|
const destroyRange = function() {
|
|
3345
665
|
if (self.range) {
|
|
3346
666
|
for (let i = 0; i < self.options.length; i++) {
|
|
3347
667
|
if (self.options[i].range !== false) {
|
|
3348
|
-
self.options[i].range =
|
|
668
|
+
self.options[i].range = false;
|
|
3349
669
|
}
|
|
3350
670
|
if (self.options[i].start !== false) {
|
|
3351
|
-
self.options[i].start =
|
|
671
|
+
self.options[i].start = false;
|
|
3352
672
|
}
|
|
3353
673
|
if (self.options[i].end !== false) {
|
|
3354
|
-
self.options[i].end =
|
|
674
|
+
self.options[i].end = false;
|
|
675
|
+
}
|
|
676
|
+
if (self.options[i].last !== false) {
|
|
677
|
+
self.options[i].last = false;
|
|
3355
678
|
}
|
|
3356
679
|
}
|
|
3357
680
|
self.rangeValues = null;
|
|
3358
681
|
}
|
|
3359
682
|
}
|
|
3360
683
|
|
|
3361
|
-
const
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
if (self.numeric) {
|
|
3366
|
-
value = self.rangeValues;
|
|
3367
|
-
} else {
|
|
3368
|
-
value = [
|
|
3369
|
-
Helpers.numToDate(self.rangeValues[0]).substring(0, 10),
|
|
3370
|
-
Helpers.numToDate(self.rangeValues[1]).substring(0, 10)
|
|
3371
|
-
];
|
|
3372
|
-
}
|
|
3373
|
-
}
|
|
3374
|
-
} else {
|
|
3375
|
-
value = getDate();
|
|
3376
|
-
if (self.numeric) {
|
|
3377
|
-
value = Helpers.dateToNum(value);
|
|
684
|
+
const render = function(v) {
|
|
685
|
+
if (v) {
|
|
686
|
+
if (! Array.isArray(v)) {
|
|
687
|
+
v = v.toString().split(',');
|
|
3378
688
|
}
|
|
689
|
+
|
|
690
|
+
v = v.map(entry => {
|
|
691
|
+
return Mask.render(entry, self.format || 'YYYY-MM-DD');
|
|
692
|
+
}).join(',');
|
|
3379
693
|
}
|
|
3380
|
-
return
|
|
694
|
+
return v;
|
|
3381
695
|
}
|
|
3382
696
|
|
|
3383
|
-
const
|
|
3384
|
-
if (
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
v = v.toString().split(',');
|
|
3388
|
-
}
|
|
3389
|
-
self.rangeValues = [...v];
|
|
697
|
+
const normalize = function(v) {
|
|
698
|
+
if (! Array.isArray(v)) {
|
|
699
|
+
v = v.toString().split(',');
|
|
700
|
+
}
|
|
3390
701
|
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
702
|
+
return v.map(item => {
|
|
703
|
+
if (Number(item) == item) {
|
|
704
|
+
return Helpers.numToDate(item);
|
|
705
|
+
} else {
|
|
706
|
+
if (Helpers.isValidDateFormat(item)) {
|
|
707
|
+
return item;
|
|
708
|
+
} else if (self.format) {
|
|
709
|
+
let tmp = Mask.extractDateFromString(item, self.format);
|
|
710
|
+
if (tmp) {
|
|
711
|
+
return tmp;
|
|
712
|
+
}
|
|
3396
713
|
}
|
|
3397
|
-
|
|
3398
|
-
v = v[0];
|
|
3399
714
|
}
|
|
3400
|
-
}
|
|
3401
|
-
|
|
3402
|
-
let d;
|
|
3403
|
-
if (v) {
|
|
3404
|
-
v = isNumber(v) ? Helpers.numToDate(v) : v;
|
|
3405
|
-
d = new Date(v + ' GMT+0');
|
|
3406
|
-
}
|
|
3407
|
-
// if no date is defined
|
|
3408
|
-
if (! Helpers.isValidDate(d)) {
|
|
3409
|
-
d = new Date();
|
|
3410
|
-
}
|
|
3411
|
-
// Update my index
|
|
3412
|
-
self.cursor = {
|
|
3413
|
-
y: d.getUTCFullYear(),
|
|
3414
|
-
m: d.getUTCMonth(),
|
|
3415
|
-
d: d.getUTCDate(),
|
|
3416
|
-
};
|
|
3417
|
-
// Update the internal calendar date
|
|
3418
|
-
setDate(d);
|
|
715
|
+
})
|
|
3419
716
|
}
|
|
3420
717
|
|
|
3421
|
-
const
|
|
3422
|
-
let input =
|
|
3423
|
-
if (input
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
718
|
+
const extractValueFromInput = function() {
|
|
719
|
+
let input = getInput();
|
|
720
|
+
if (input) {
|
|
721
|
+
let v;
|
|
722
|
+
if (input.tagName === 'INPUT' || input.tagName === 'TEXTAREA') {
|
|
723
|
+
v = input.value;
|
|
724
|
+
} else if (input.isContentEditable) {
|
|
725
|
+
v = input.textContent;
|
|
726
|
+
}
|
|
727
|
+
if (v) {
|
|
728
|
+
return normalize(v).join(',');
|
|
3428
729
|
}
|
|
730
|
+
return v;
|
|
3429
731
|
}
|
|
3430
|
-
|
|
3431
|
-
return input;
|
|
3432
|
-
}
|
|
3433
|
-
|
|
3434
|
-
const update = function() {
|
|
3435
|
-
self.setValue(renderValue());
|
|
3436
|
-
self.close({ origin: 'button' });
|
|
3437
732
|
}
|
|
3438
733
|
|
|
3439
|
-
const onopen = function(
|
|
734
|
+
const onopen = function() {
|
|
3440
735
|
let isEditable = false;
|
|
3441
|
-
let input = getInput();
|
|
3442
736
|
let value = self.value;
|
|
737
|
+
|
|
738
|
+
let input = getInput();
|
|
3443
739
|
if (input) {
|
|
3444
|
-
let v;
|
|
3445
740
|
if (input.tagName === 'INPUT' || input.tagName === 'TEXTAREA') {
|
|
3446
741
|
isEditable = !input.hasAttribute('readonly') && !input.hasAttribute('disabled');
|
|
3447
|
-
v = input.value;
|
|
3448
742
|
} else if (input.isContentEditable) {
|
|
3449
743
|
isEditable = true;
|
|
3450
|
-
v = input.textContent;
|
|
3451
744
|
}
|
|
3452
745
|
|
|
3453
|
-
let
|
|
3454
|
-
if (
|
|
3455
|
-
value =
|
|
746
|
+
let ret = extractValueFromInput();
|
|
747
|
+
if (ret && ret !== value) {
|
|
748
|
+
value = ret;
|
|
3456
749
|
}
|
|
3457
750
|
}
|
|
3458
751
|
|
|
@@ -3461,7 +754,8 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3461
754
|
}
|
|
3462
755
|
|
|
3463
756
|
// Update the internal date values
|
|
3464
|
-
|
|
757
|
+
setValue(value);
|
|
758
|
+
|
|
3465
759
|
// Open event
|
|
3466
760
|
Dispatch.call(self, self.onopen, 'open', {
|
|
3467
761
|
instance: self
|
|
@@ -3478,51 +772,29 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3478
772
|
});
|
|
3479
773
|
}
|
|
3480
774
|
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
// Start a new range
|
|
3502
|
-
if (self.rangeValues && (self.rangeValues[0] >= number || self.rangeValues[1])) {
|
|
3503
|
-
destroyRange();
|
|
3504
|
-
}
|
|
3505
|
-
// Range
|
|
3506
|
-
item.range = true;
|
|
3507
|
-
// Update range
|
|
3508
|
-
if (! self.rangeValues) {
|
|
3509
|
-
item.start = true;
|
|
3510
|
-
self.rangeValues = [number, null];
|
|
3511
|
-
} else {
|
|
3512
|
-
item.end = true;
|
|
3513
|
-
self.rangeValues[1] = number;
|
|
3514
|
-
}
|
|
3515
|
-
} else {
|
|
3516
|
-
update();
|
|
3517
|
-
}
|
|
3518
|
-
}
|
|
775
|
+
const dispatchOnChangeEvent = function() {
|
|
776
|
+
// Destroy range
|
|
777
|
+
destroyRange();
|
|
778
|
+
// Update the internal controllers
|
|
779
|
+
setValue(self.value);
|
|
780
|
+
// Events
|
|
781
|
+
Dispatch.call(self, change, 'change', {
|
|
782
|
+
instance: self,
|
|
783
|
+
value: self.value,
|
|
784
|
+
});
|
|
785
|
+
// Update input
|
|
786
|
+
let input = getInput();
|
|
787
|
+
if (input) {
|
|
788
|
+
// Update input value
|
|
789
|
+
input.value = render(self.value);
|
|
790
|
+
// Dispatch event
|
|
791
|
+
Dispatch.call(input, null, 'change', {
|
|
792
|
+
instance: self,
|
|
793
|
+
value: self.value,
|
|
794
|
+
});
|
|
3519
795
|
}
|
|
3520
796
|
}
|
|
3521
797
|
|
|
3522
|
-
const isTrue = function(v) {
|
|
3523
|
-
return v === true || v === 'true';
|
|
3524
|
-
}
|
|
3525
|
-
|
|
3526
798
|
const events = {
|
|
3527
799
|
focusin: (e) => {
|
|
3528
800
|
if (self.modal && self.isClosed()) {
|
|
@@ -3565,189 +837,35 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3565
837
|
input: (e) => {
|
|
3566
838
|
let input = e.target;
|
|
3567
839
|
if (input.classList.contains('lm-calendar-input')) {
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
Mask.oninput(e, self.format);
|
|
3574
|
-
}
|
|
3575
|
-
// Check if that is a valid date
|
|
3576
|
-
if (Helpers.isValidDateFormat(content)) {
|
|
3577
|
-
value = content;
|
|
3578
|
-
} else {
|
|
3579
|
-
let tmp = Mask.extractDateFromString(content, self.format);
|
|
3580
|
-
if (tmp) {
|
|
3581
|
-
value = tmp;
|
|
840
|
+
if (! isTrue(self.range)) {
|
|
841
|
+
// TODO: process with range
|
|
842
|
+
// Apply mask
|
|
843
|
+
if (self.format) {
|
|
844
|
+
Mask.oninput(e, self.format);
|
|
3582
845
|
}
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
* Next handler
|
|
3594
|
-
* @param {object?} e mouse event
|
|
3595
|
-
*/
|
|
3596
|
-
self.next = function(e) {
|
|
3597
|
-
if (! e || e.type === 'click') {
|
|
3598
|
-
// Icon click
|
|
3599
|
-
move(1);
|
|
3600
|
-
} else {
|
|
3601
|
-
// Keyboard handler
|
|
3602
|
-
moveCursor(1, e);
|
|
3603
|
-
}
|
|
3604
|
-
}
|
|
3605
|
-
|
|
3606
|
-
/**
|
|
3607
|
-
* Next handler
|
|
3608
|
-
* @param {object?} e mouse event
|
|
3609
|
-
*/
|
|
3610
|
-
self.prev = function(e) {
|
|
3611
|
-
if (! e || e.type === 'click') {
|
|
3612
|
-
// Icon click
|
|
3613
|
-
move(-1);
|
|
3614
|
-
} else {
|
|
3615
|
-
// Keyboard handler
|
|
3616
|
-
moveCursor(-1, e);
|
|
3617
|
-
}
|
|
3618
|
-
}
|
|
3619
|
-
|
|
3620
|
-
/**
|
|
3621
|
-
* Open the modal
|
|
3622
|
-
*/
|
|
3623
|
-
self.open = function(e) {
|
|
3624
|
-
if (self.modal) {
|
|
3625
|
-
if (autoType) {
|
|
3626
|
-
self.type = window.innerWidth > 640 ? self.type = 'default' : 'picker';
|
|
3627
|
-
}
|
|
3628
|
-
self.modal.open();
|
|
3629
|
-
}
|
|
3630
|
-
}
|
|
3631
|
-
|
|
3632
|
-
/**
|
|
3633
|
-
* Close the modal
|
|
3634
|
-
*/
|
|
3635
|
-
self.close = function(options) {
|
|
3636
|
-
if (self.modal) {
|
|
3637
|
-
if (options && options.origin) {
|
|
3638
|
-
self.modal.close(options)
|
|
3639
|
-
} else {
|
|
3640
|
-
self.modal.close({ origin: 'button' })
|
|
3641
|
-
}
|
|
3642
|
-
}
|
|
3643
|
-
}
|
|
3644
|
-
|
|
3645
|
-
self.isClosed = function() {
|
|
3646
|
-
if (self.modal) {
|
|
3647
|
-
return self.modal.isClosed();
|
|
3648
|
-
}
|
|
3649
|
-
}
|
|
3650
|
-
|
|
3651
|
-
self.reset = function() {
|
|
3652
|
-
self.setValue('');
|
|
3653
|
-
self.close({ origin: 'button' });
|
|
3654
|
-
}
|
|
3655
|
-
|
|
3656
|
-
/**
|
|
3657
|
-
* Change the view
|
|
3658
|
-
*/
|
|
3659
|
-
self.setView = function(e) {
|
|
3660
|
-
if (typeof e === 'object') {
|
|
3661
|
-
e = this.getAttribute('data-view');
|
|
3662
|
-
}
|
|
3663
|
-
|
|
3664
|
-
const validViews = ['days', 'months', 'years'];
|
|
3665
|
-
if (validViews.includes(e)) {
|
|
3666
|
-
self.view = e;
|
|
3667
|
-
}
|
|
3668
|
-
}
|
|
3669
|
-
|
|
3670
|
-
/**
|
|
3671
|
-
* Get value from cursor
|
|
3672
|
-
* @returns {string}
|
|
3673
|
-
*/
|
|
3674
|
-
self.getValue = function() {
|
|
3675
|
-
let value = self.value;
|
|
3676
|
-
|
|
3677
|
-
if (isNumber(value)) {
|
|
3678
|
-
if (! isTrue(self.numeric)) {
|
|
3679
|
-
value = Helpers.numToDate(value);
|
|
3680
|
-
}
|
|
3681
|
-
} else {
|
|
3682
|
-
if (isTrue(self.numeric)) {
|
|
3683
|
-
value = Helpers.dateToNum(value);
|
|
3684
|
-
}
|
|
3685
|
-
}
|
|
3686
|
-
return value;
|
|
3687
|
-
}
|
|
3688
|
-
|
|
3689
|
-
self.setValue = function(v) {
|
|
3690
|
-
// Destroy range
|
|
3691
|
-
destroyRange();
|
|
3692
|
-
// Update the internal controllers
|
|
3693
|
-
updateValue(v);
|
|
3694
|
-
// Events
|
|
3695
|
-
if (v !== self.value) {
|
|
3696
|
-
// Update value
|
|
3697
|
-
self.value = v;
|
|
3698
|
-
// Events
|
|
3699
|
-
Dispatch.call(self, change, 'change', {
|
|
3700
|
-
instance: self,
|
|
3701
|
-
value: self.value,
|
|
3702
|
-
});
|
|
3703
|
-
// Update input
|
|
3704
|
-
let input = getInput();
|
|
3705
|
-
if (input) {
|
|
3706
|
-
if (self.format && v) {
|
|
3707
|
-
if (self.range) {
|
|
3708
|
-
if (v[0]) {
|
|
3709
|
-
v[0] = Mask.render(v[0], self.format);
|
|
3710
|
-
}
|
|
3711
|
-
if (v[1]) {
|
|
3712
|
-
v[1] = Mask.render(v[1], self.format);
|
|
846
|
+
let value = null;
|
|
847
|
+
// Content
|
|
848
|
+
let content = (input.tagName === 'INPUT' || input.tagName === 'TEXTAREA') ? input.value : input.textContent;
|
|
849
|
+
// Check if that is a valid date
|
|
850
|
+
if (Helpers.isValidDateFormat(content)) {
|
|
851
|
+
value = content;
|
|
852
|
+
} else if (self.format) {
|
|
853
|
+
let tmp = Mask.extractDateFromString(content, self.format);
|
|
854
|
+
if (tmp) {
|
|
855
|
+
value = tmp;
|
|
3713
856
|
}
|
|
3714
|
-
}
|
|
3715
|
-
|
|
857
|
+
}
|
|
858
|
+
// Change the calendar view
|
|
859
|
+
if (value) {
|
|
860
|
+
setValue(value);
|
|
3716
861
|
}
|
|
3717
862
|
}
|
|
3718
|
-
// Update input value
|
|
3719
|
-
input.value = v;
|
|
3720
|
-
// Dispatch event
|
|
3721
|
-
Dispatch.call(input, null, 'change', {
|
|
3722
|
-
instance: self,
|
|
3723
|
-
value: self.value,
|
|
3724
|
-
});
|
|
3725
863
|
}
|
|
3726
864
|
}
|
|
3727
865
|
}
|
|
3728
866
|
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
self.onevent = function(e) {
|
|
3732
|
-
if (events[e.type]) {
|
|
3733
|
-
events[e.type](e);
|
|
3734
|
-
}
|
|
3735
|
-
}
|
|
3736
|
-
|
|
3737
|
-
onchange(function(prop) {
|
|
3738
|
-
if (prop === 'view') {
|
|
3739
|
-
if (typeof(views[self.view]) === 'function') {
|
|
3740
|
-
// When change the view update the data
|
|
3741
|
-
self.options = views[self.view].call(self, date);
|
|
3742
|
-
}
|
|
3743
|
-
} else if (prop === 'value') {
|
|
3744
|
-
self.setValue(self.value);
|
|
3745
|
-
} else if (prop === 'startingDay') {
|
|
3746
|
-
self.weekdays = getWeekdays(self.startingDay);
|
|
3747
|
-
}
|
|
3748
|
-
});
|
|
3749
|
-
|
|
3750
|
-
onload(function() {
|
|
867
|
+
// Onload
|
|
868
|
+
onload(() => {
|
|
3751
869
|
if (self.type !== "inline") {
|
|
3752
870
|
// Create modal instance
|
|
3753
871
|
self.modal = {
|
|
@@ -3764,16 +882,11 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3764
882
|
Modal(self.el, self.modal);
|
|
3765
883
|
}
|
|
3766
884
|
|
|
3767
|
-
|
|
3768
|
-
if (self.range === 'true') {
|
|
3769
|
-
self.range = true;
|
|
3770
|
-
}
|
|
885
|
+
let ret;
|
|
3771
886
|
|
|
3772
887
|
// Create input controls
|
|
3773
888
|
if (self.input && self.initInput !== false) {
|
|
3774
|
-
if (self.input
|
|
3775
|
-
self.input = document.createElement('input');
|
|
3776
|
-
self.input.type = 'text';
|
|
889
|
+
if (! self.input.parentNode) {
|
|
3777
890
|
self.el.parentNode.insertBefore(self.input, self.el);
|
|
3778
891
|
}
|
|
3779
892
|
|
|
@@ -3795,15 +908,25 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3795
908
|
|
|
3796
909
|
// Retrieve the value
|
|
3797
910
|
if (self.value) {
|
|
3798
|
-
input.value = self.value;
|
|
3799
|
-
} else
|
|
3800
|
-
|
|
911
|
+
input.value = render(self.value);
|
|
912
|
+
} else {
|
|
913
|
+
let value = extractValueFromInput();
|
|
914
|
+
if (value && value !== self.value) {
|
|
915
|
+
ret = value;
|
|
916
|
+
}
|
|
3801
917
|
}
|
|
3802
918
|
}
|
|
3803
919
|
}
|
|
3804
920
|
|
|
3805
921
|
// Update the internal date values
|
|
3806
|
-
|
|
922
|
+
if (ret) {
|
|
923
|
+
self.setValue(ret);
|
|
924
|
+
} else {
|
|
925
|
+
setValue(self.value);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// Reload view
|
|
929
|
+
reloadView(true);
|
|
3807
930
|
|
|
3808
931
|
/**
|
|
3809
932
|
* Handler keyboard
|
|
@@ -3815,21 +938,20 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3815
938
|
if (e.target !== self.content) {
|
|
3816
939
|
self.content.focus();
|
|
3817
940
|
}
|
|
3818
|
-
|
|
941
|
+
prev(e);
|
|
3819
942
|
prevent = true;
|
|
3820
943
|
} else if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
|
|
3821
944
|
if (e.target !== self.content) {
|
|
3822
945
|
self.content.focus();
|
|
3823
946
|
}
|
|
3824
|
-
|
|
947
|
+
next(e);
|
|
3825
948
|
prevent = true;
|
|
3826
949
|
} else if (e.key === 'Enter') {
|
|
3827
950
|
if (e.target === self.content) {
|
|
3828
951
|
// Item
|
|
3829
|
-
|
|
3830
|
-
if (item) {
|
|
952
|
+
if (self.cursor.current) {
|
|
3831
953
|
// Select
|
|
3832
|
-
select(e,
|
|
954
|
+
select(e, self.cursor.current);
|
|
3833
955
|
prevent = true;
|
|
3834
956
|
}
|
|
3835
957
|
}
|
|
@@ -3853,9 +975,9 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3853
975
|
self.content.addEventListener('wheel', function(e){
|
|
3854
976
|
if (self.wheel !== false) {
|
|
3855
977
|
if (e.deltaY < 0) {
|
|
3856
|
-
|
|
978
|
+
prev(e);
|
|
3857
979
|
} else {
|
|
3858
|
-
|
|
980
|
+
next(e);
|
|
3859
981
|
}
|
|
3860
982
|
e.preventDefault();
|
|
3861
983
|
}
|
|
@@ -3882,22 +1004,103 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3882
1004
|
});
|
|
3883
1005
|
});
|
|
3884
1006
|
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
1007
|
+
onchange((prop) => {
|
|
1008
|
+
if (prop === 'view') {
|
|
1009
|
+
reloadView(true);
|
|
1010
|
+
} else if (prop === 'startingDay') {
|
|
1011
|
+
self.weekdays = getWeekdays(self.startingDay ?? 0);
|
|
1012
|
+
} else if (prop === 'value') {
|
|
1013
|
+
dispatchOnChangeEvent();
|
|
1014
|
+
}
|
|
1015
|
+
})
|
|
1016
|
+
|
|
1017
|
+
// Tracking variables
|
|
1018
|
+
track('value');
|
|
1019
|
+
|
|
1020
|
+
// Public methods
|
|
1021
|
+
|
|
1022
|
+
self.open = function(e) {
|
|
1023
|
+
if (self.modal) {
|
|
1024
|
+
if (self.type === 'auto') {
|
|
1025
|
+
self.type = window.innerWidth > 640 ? self.type = 'default' : 'picker';
|
|
1026
|
+
}
|
|
1027
|
+
self.modal.open();
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
self.close = function(options) {
|
|
1032
|
+
if (self.modal) {
|
|
1033
|
+
if (options && options.origin) {
|
|
1034
|
+
self.modal.close(options)
|
|
1035
|
+
} else {
|
|
1036
|
+
self.modal.close({ origin: 'button' })
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
self.isClosed = function() {
|
|
1042
|
+
if (self.modal) {
|
|
1043
|
+
return self.modal.isClosed();
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
self.getValue = function() {
|
|
1048
|
+
return self.value;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
self.setValue = function(v) {
|
|
1052
|
+
// Update value
|
|
1053
|
+
if (v) {
|
|
1054
|
+
let ret = normalize(v);
|
|
1055
|
+
if (isTrue(self.numeric)) {
|
|
1056
|
+
ret = ret.map(entry => {
|
|
1057
|
+
return Helpers.dateToNum(entry);
|
|
1058
|
+
})
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
if (! Array.isArray(v)) {
|
|
1062
|
+
ret = ret.join(',');
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
if (ret == Number(ret)) {
|
|
1066
|
+
ret = Number(ret);
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
v = ret;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
// Events
|
|
1073
|
+
if (v !== self.value) {
|
|
1074
|
+
self.value = v;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
self.onevent = function(e) {
|
|
1079
|
+
if (events[e.type]) {
|
|
1080
|
+
events[e.type](e);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
3888
1083
|
|
|
3889
|
-
|
|
1084
|
+
self.update = update;
|
|
1085
|
+
self.next = next;
|
|
1086
|
+
self.prev = prev;
|
|
1087
|
+
self.reset = reset;
|
|
1088
|
+
self.setView = setView;
|
|
1089
|
+
self.helpers = Helpers;
|
|
1090
|
+
self.helpers.getDate = Mask.getDate;
|
|
1091
|
+
|
|
1092
|
+
return render => render`<div class="lm-calendar" data-grid="{{self.grid}}" data-type="{{self.type}}" data-disabled="{{self.disabled}}" data-starting-day="{{self.startingDay}}">
|
|
3890
1093
|
<div class="lm-calendar-options">
|
|
3891
|
-
<button type="button" onclick="
|
|
1094
|
+
<button type="button" onclick="${reset}">${T('Reset')}</button>
|
|
3892
1095
|
<button type="button" onclick="${update}">${T('Done')}</button>
|
|
3893
1096
|
</div>
|
|
3894
1097
|
<div class="lm-calendar-container" data-view="{{self.view}}">
|
|
3895
1098
|
<div class="lm-calendar-header">
|
|
3896
1099
|
<div>
|
|
3897
|
-
<div class="lm-calendar-labels"><button type="button" onclick="
|
|
1100
|
+
<div class="lm-calendar-labels"><button type="button" onclick="${setView}" data-view="months">{{self.month}}</button> <button type="button" onclick="${setView}" data-view="years">{{self.year}}</button></div>
|
|
3898
1101
|
<div class="lm-calendar-navigation">
|
|
3899
|
-
<button type="button" class="lm-calendar-icon lm-ripple" onclick="
|
|
3900
|
-
<button type="button" class="lm-calendar-icon lm-ripple" onclick="
|
|
1102
|
+
<button type="button" class="lm-calendar-icon lm-ripple" onclick="${prev}" tabindex="0">expand_less</button>
|
|
1103
|
+
<button type="button" class="lm-calendar-icon lm-ripple" onclick="${next}" tabindex="0">expand_more</button>
|
|
3901
1104
|
</div>
|
|
3902
1105
|
</div>
|
|
3903
1106
|
<div class="lm-calendar-weekdays" :loop="self.weekdays"><div>{{self.title}}</div></div>
|
|
@@ -3907,7 +1110,7 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3907
1110
|
</div>
|
|
3908
1111
|
<div class="lm-calendar-footer" data-visible="{{self.footer}}">
|
|
3909
1112
|
<div class="lm-calendar-time" data-visible="{{self.time}}"><select :loop="${hours}" :bind="self.hour" class="lm-calendar-control"><option value="{{self.value}}">{{self.title}}</option></select>:<select :loop="${minutes}" :bind="self.minute" class="lm-calendar-control"><option value="{{self.value}}">{{self.title}}</option></select></div>
|
|
3910
|
-
<div class="lm-calendar-update"><input type="button" value="${T('Update')}" onclick="${update}" class="lm-ripple"></div>
|
|
1113
|
+
<div class="lm-calendar-update"><input type="button" value="${T('Update')}" onclick="${update}" class="lm-ripple lm-input"></div>
|
|
3911
1114
|
</div>
|
|
3912
1115
|
</div>
|
|
3913
1116
|
</div>`
|