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