@lemonadejs/calendar 5.3.0 → 5.8.1
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 +615 -3466
- package/dist/style.css +0 -1
- 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
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
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
|
-
for (let i = 0; i < 12; i++) {
|
|
2930
|
-
let item = {
|
|
2931
|
-
title: Helpers.months[i].substring(0,3),
|
|
2932
|
-
value: i
|
|
2933
|
-
}
|
|
2934
|
-
// Add the item to the data
|
|
2935
|
-
result.push(item);
|
|
2936
|
-
// Select cursor
|
|
2937
|
-
if (this.cursor.y === year && this.cursor.m === i) {
|
|
2938
|
-
// Select item
|
|
163
|
+
for (let i = 0; i < 12; i++) {
|
|
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
|
+
}
|
|
419
|
+
|
|
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
|
+
}
|
|
3150
430
|
|
|
3151
|
-
|
|
3152
|
-
* Update the internal date
|
|
3153
|
-
* @param {Date|string|number[]} d instance of Date
|
|
3154
|
-
*
|
|
3155
|
-
*/
|
|
3156
|
-
const setDate = function(d) {
|
|
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,99 @@ 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
|
+
if (isTrue(self.range)) {
|
|
521
|
+
// Start a new range
|
|
522
|
+
if (self.rangeValues && (self.rangeValues[0] >= s.number || self.rangeValues[1])) {
|
|
523
|
+
destroyRange();
|
|
524
|
+
}
|
|
525
|
+
// Range
|
|
526
|
+
s.range = true;
|
|
527
|
+
// Update range
|
|
528
|
+
if (! self.rangeValues) {
|
|
529
|
+
s.start = true;
|
|
530
|
+
self.rangeValues = [s.number, null];
|
|
531
|
+
} else {
|
|
532
|
+
s.end = true;
|
|
533
|
+
self.rangeValues[1] = s.number;
|
|
534
|
+
}
|
|
535
|
+
setCursor(s);
|
|
536
|
+
} else {
|
|
537
|
+
setCursor(s);
|
|
538
|
+
|
|
539
|
+
update();
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Update Calendar
|
|
545
|
+
const update = function(e) {
|
|
546
|
+
self.setValue(getValue());
|
|
547
|
+
self.close({ origin: 'button' });
|
|
548
|
+
}
|
|
3240
549
|
|
|
3241
|
-
|
|
550
|
+
const reset = function() {
|
|
551
|
+
self.setValue('');
|
|
552
|
+
self.close({ origin: 'button' });
|
|
3242
553
|
}
|
|
3243
554
|
|
|
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
555
|
const updateDate = function(v, position) {
|
|
3251
556
|
// Current internal date
|
|
3252
557
|
let value = [date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), self.hour, self.minute, 0];
|
|
@@ -3256,11 +561,11 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3256
561
|
return value;
|
|
3257
562
|
}
|
|
3258
563
|
|
|
3259
|
-
/**
|
|
3260
|
-
* This method move the data from the view up or down
|
|
3261
|
-
* @param direction
|
|
3262
|
-
*/
|
|
3263
564
|
const move = function(direction) {
|
|
565
|
+
// Reset visual cursor
|
|
566
|
+
resetCursor();
|
|
567
|
+
|
|
568
|
+
// Value
|
|
3264
569
|
let value;
|
|
3265
570
|
|
|
3266
571
|
// Update the new internal date
|
|
@@ -3272,71 +577,73 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3272
577
|
value = updateDate(date.getUTCFullYear()+direction, 0);
|
|
3273
578
|
} else if (self.view === 'years') {
|
|
3274
579
|
// Select the new internal date
|
|
3275
|
-
value = updateDate(date.
|
|
580
|
+
value = updateDate(date.getUTCFullYear()+(direction*16), 0);
|
|
3276
581
|
}
|
|
3277
582
|
|
|
3278
583
|
// Update view
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
584
|
+
setDate(value);
|
|
585
|
+
|
|
586
|
+
// Reload content of the view
|
|
587
|
+
reloadView();
|
|
3282
588
|
}
|
|
3283
589
|
|
|
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 ]);
|
|
590
|
+
const getJump = function(e) {
|
|
591
|
+
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
|
|
592
|
+
return self.view === 'days' ? 7 : 4;
|
|
3297
593
|
}
|
|
3298
594
|
|
|
3299
|
-
|
|
3300
|
-
|
|
595
|
+
return 1;
|
|
596
|
+
}
|
|
3301
597
|
|
|
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
|
-
}
|
|
598
|
+
const prev = function(e) {
|
|
599
|
+
if (e && e.type === 'keydown') {
|
|
600
|
+
// Current index
|
|
601
|
+
let total = self.options.length;
|
|
602
|
+
let position = self.options.indexOf(self.cursor.current) - getJump(e);
|
|
603
|
+
if (position < 0) {
|
|
604
|
+
// Next month
|
|
605
|
+
move(-1);
|
|
606
|
+
// New position
|
|
607
|
+
position = total + position;
|
|
3323
608
|
}
|
|
609
|
+
// Update cursor
|
|
610
|
+
setCursor(self.options[position])
|
|
611
|
+
} else {
|
|
612
|
+
move(-1);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
3324
615
|
|
|
3325
|
-
|
|
3326
|
-
|
|
616
|
+
const next = function(e) {
|
|
617
|
+
if (e && e.type === 'keydown') {
|
|
618
|
+
// Current index
|
|
619
|
+
let total = self.options.length;
|
|
620
|
+
let position = self.options.indexOf(self.cursor.current) + getJump(e);
|
|
621
|
+
if (position >= total) {
|
|
622
|
+
// Next month
|
|
623
|
+
move(1);
|
|
624
|
+
// New position
|
|
625
|
+
position = position - total;
|
|
626
|
+
}
|
|
627
|
+
// Update cursor
|
|
628
|
+
setCursor(self.options[position])
|
|
629
|
+
} else {
|
|
630
|
+
move(1);
|
|
3327
631
|
}
|
|
632
|
+
}
|
|
3328
633
|
|
|
3329
|
-
|
|
3330
|
-
|
|
634
|
+
const getInput = function() {
|
|
635
|
+
let input = self.input;
|
|
636
|
+
if (input && input.current) {
|
|
637
|
+
input = input.current;
|
|
638
|
+
} else {
|
|
639
|
+
if (self.input) {
|
|
640
|
+
input = self.input;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
3331
643
|
|
|
3332
|
-
|
|
3333
|
-
updateRange(self.options[index])
|
|
644
|
+
return input;
|
|
3334
645
|
}
|
|
3335
646
|
|
|
3336
|
-
/**
|
|
3337
|
-
* Update the visible range
|
|
3338
|
-
* @param s
|
|
3339
|
-
*/
|
|
3340
647
|
const updateRange = function(s) {
|
|
3341
648
|
if (self.range && self.view === 'days' && self.rangeValues) {
|
|
3342
649
|
// Creating a range
|
|
@@ -3345,148 +652,100 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3345
652
|
if (number) {
|
|
3346
653
|
// Update range properties
|
|
3347
654
|
for (let i = 0; i < self.options.length; i++) {
|
|
3348
|
-
// Item number
|
|
3349
655
|
let v = self.options[i].number;
|
|
3350
656
|
// Update property condition
|
|
3351
657
|
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
|
-
}
|
|
658
|
+
self.options[i].last = (v === number);
|
|
3358
659
|
}
|
|
3359
660
|
}
|
|
3360
661
|
}
|
|
3361
662
|
}
|
|
3362
663
|
}
|
|
3363
664
|
|
|
3364
|
-
/**
|
|
3365
|
-
* Destroy the range
|
|
3366
|
-
*/
|
|
3367
665
|
const destroyRange = function() {
|
|
3368
666
|
if (self.range) {
|
|
3369
667
|
for (let i = 0; i < self.options.length; i++) {
|
|
3370
668
|
if (self.options[i].range !== false) {
|
|
3371
|
-
self.options[i].range =
|
|
669
|
+
self.options[i].range = false;
|
|
3372
670
|
}
|
|
3373
671
|
if (self.options[i].start !== false) {
|
|
3374
|
-
self.options[i].start =
|
|
672
|
+
self.options[i].start = false;
|
|
3375
673
|
}
|
|
3376
674
|
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]);
|
|
675
|
+
self.options[i].end = false;
|
|
3416
676
|
}
|
|
3417
|
-
if (
|
|
3418
|
-
self.
|
|
677
|
+
if (self.options[i].last !== false) {
|
|
678
|
+
self.options[i].last = false;
|
|
3419
679
|
}
|
|
3420
|
-
|
|
3421
|
-
v = v[0];
|
|
3422
680
|
}
|
|
681
|
+
self.rangeValues = null;
|
|
3423
682
|
}
|
|
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
683
|
}
|
|
3443
684
|
|
|
3444
|
-
const
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
} else {
|
|
3449
|
-
if (self.input) {
|
|
3450
|
-
input = self.input;
|
|
685
|
+
const render = function(v) {
|
|
686
|
+
if (v) {
|
|
687
|
+
if (! Array.isArray(v)) {
|
|
688
|
+
v = v.toString().split(',');
|
|
3451
689
|
}
|
|
3452
|
-
}
|
|
3453
690
|
|
|
3454
|
-
|
|
691
|
+
v = v.map(entry => {
|
|
692
|
+
return Mask.render(entry, self.format || 'YYYY-MM-DD');
|
|
693
|
+
}).join(',');
|
|
694
|
+
}
|
|
695
|
+
return v;
|
|
3455
696
|
}
|
|
3456
697
|
|
|
3457
|
-
const
|
|
3458
|
-
|
|
3459
|
-
|
|
698
|
+
const normalize = function(v) {
|
|
699
|
+
if (! Array.isArray(v)) {
|
|
700
|
+
v = v.toString().split(',');
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
return v.map(item => {
|
|
704
|
+
if (Number(item) == item) {
|
|
705
|
+
return Helpers.numToDate(item);
|
|
706
|
+
} else {
|
|
707
|
+
if (Helpers.isValidDateFormat(item)) {
|
|
708
|
+
return item;
|
|
709
|
+
} else if (self.format) {
|
|
710
|
+
let tmp = Mask.extractDateFromString(item, self.format);
|
|
711
|
+
if (tmp) {
|
|
712
|
+
return tmp;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
})
|
|
3460
717
|
}
|
|
3461
718
|
|
|
3462
|
-
const
|
|
3463
|
-
let isEditable = false;
|
|
719
|
+
const extractValueFromInput = function() {
|
|
3464
720
|
let input = getInput();
|
|
3465
|
-
let value = self.value;
|
|
3466
721
|
if (input) {
|
|
3467
|
-
let v
|
|
722
|
+
let v;
|
|
3468
723
|
if (input.tagName === 'INPUT' || input.tagName === 'TEXTAREA') {
|
|
3469
|
-
isEditable = !input.hasAttribute('readonly') && !input.hasAttribute('disabled');
|
|
3470
724
|
v = input.value;
|
|
3471
725
|
} else if (input.isContentEditable) {
|
|
3472
|
-
isEditable = true;
|
|
3473
726
|
v = input.textContent;
|
|
3474
727
|
}
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
if (ret && Number(ret) == ret) {
|
|
3478
|
-
ret = Helpers.numToDate(ret);
|
|
728
|
+
if (v) {
|
|
729
|
+
return normalize(v).join(',');
|
|
3479
730
|
}
|
|
731
|
+
return v;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
3480
734
|
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
735
|
+
const onopen = function() {
|
|
736
|
+
let isEditable = false;
|
|
737
|
+
let value = self.value;
|
|
738
|
+
|
|
739
|
+
let input = getInput();
|
|
740
|
+
if (input) {
|
|
741
|
+
if (input.tagName === 'INPUT' || input.tagName === 'TEXTAREA') {
|
|
742
|
+
isEditable = !input.hasAttribute('readonly') && !input.hasAttribute('disabled');
|
|
743
|
+
} else if (input.isContentEditable) {
|
|
744
|
+
isEditable = true;
|
|
3487
745
|
}
|
|
3488
746
|
|
|
3489
|
-
|
|
747
|
+
let ret = extractValueFromInput();
|
|
748
|
+
if (ret && ret !== value) {
|
|
3490
749
|
value = ret;
|
|
3491
750
|
}
|
|
3492
751
|
}
|
|
@@ -3496,7 +755,8 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3496
755
|
}
|
|
3497
756
|
|
|
3498
757
|
// Update the internal date values
|
|
3499
|
-
|
|
758
|
+
setValue(value);
|
|
759
|
+
|
|
3500
760
|
// Open event
|
|
3501
761
|
Dispatch.call(self, self.onopen, 'open', {
|
|
3502
762
|
instance: self
|
|
@@ -3513,51 +773,29 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3513
773
|
});
|
|
3514
774
|
}
|
|
3515
775
|
|
|
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
|
-
}
|
|
776
|
+
const dispatchOnChangeEvent = function() {
|
|
777
|
+
// Destroy range
|
|
778
|
+
destroyRange();
|
|
779
|
+
// Update the internal controllers
|
|
780
|
+
setValue(self.value);
|
|
781
|
+
// Events
|
|
782
|
+
Dispatch.call(self, change, 'change', {
|
|
783
|
+
instance: self,
|
|
784
|
+
value: self.value,
|
|
785
|
+
});
|
|
786
|
+
// Update input
|
|
787
|
+
let input = getInput();
|
|
788
|
+
if (input) {
|
|
789
|
+
// Update input value
|
|
790
|
+
input.value = render(self.value);
|
|
791
|
+
// Dispatch event
|
|
792
|
+
Dispatch.call(input, null, 'change', {
|
|
793
|
+
instance: self,
|
|
794
|
+
value: self.value,
|
|
795
|
+
});
|
|
3554
796
|
}
|
|
3555
797
|
}
|
|
3556
798
|
|
|
3557
|
-
const isTrue = function(v) {
|
|
3558
|
-
return v === true || v === 'true';
|
|
3559
|
-
}
|
|
3560
|
-
|
|
3561
799
|
const events = {
|
|
3562
800
|
focusin: (e) => {
|
|
3563
801
|
if (self.modal && self.isClosed()) {
|
|
@@ -3600,201 +838,35 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3600
838
|
input: (e) => {
|
|
3601
839
|
let input = e.target;
|
|
3602
840
|
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;
|
|
841
|
+
if (! isTrue(self.range)) {
|
|
842
|
+
// TODO: process with range
|
|
843
|
+
// Apply mask
|
|
844
|
+
if (self.format) {
|
|
845
|
+
Mask.oninput(e, self.format);
|
|
3617
846
|
}
|
|
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);
|
|
847
|
+
let value = null;
|
|
848
|
+
// Content
|
|
849
|
+
let content = (input.tagName === 'INPUT' || input.tagName === 'TEXTAREA') ? input.value : input.textContent;
|
|
850
|
+
// Check if that is a valid date
|
|
851
|
+
if (Helpers.isValidDateFormat(content)) {
|
|
852
|
+
value = content;
|
|
853
|
+
} else if (self.format) {
|
|
854
|
+
let tmp = Mask.extractDateFromString(content, self.format);
|
|
855
|
+
if (tmp) {
|
|
856
|
+
value = tmp;
|
|
857
|
+
}
|
|
3750
858
|
}
|
|
3751
|
-
|
|
3752
|
-
|
|
859
|
+
// Change the calendar view
|
|
860
|
+
if (value) {
|
|
861
|
+
setValue(value);
|
|
3753
862
|
}
|
|
3754
|
-
} else {
|
|
3755
|
-
v = Mask.render(v, self.format);
|
|
3756
863
|
}
|
|
3757
864
|
}
|
|
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
865
|
}
|
|
3789
|
-
});
|
|
3790
|
-
|
|
3791
|
-
// Input
|
|
3792
|
-
if (self.input === 'auto') {
|
|
3793
|
-
self.input = document.createElement('input');
|
|
3794
|
-
self.input.type = 'text';
|
|
3795
866
|
}
|
|
3796
867
|
|
|
3797
|
-
|
|
868
|
+
// Onload
|
|
869
|
+
onload(() => {
|
|
3798
870
|
if (self.type !== "inline") {
|
|
3799
871
|
// Create modal instance
|
|
3800
872
|
self.modal = {
|
|
@@ -3811,11 +883,6 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3811
883
|
Modal(self.el, self.modal);
|
|
3812
884
|
}
|
|
3813
885
|
|
|
3814
|
-
// Correct casting
|
|
3815
|
-
if (self.range === 'true') {
|
|
3816
|
-
self.range = true;
|
|
3817
|
-
}
|
|
3818
|
-
|
|
3819
886
|
let ret;
|
|
3820
887
|
|
|
3821
888
|
// Create input controls
|
|
@@ -3842,24 +909,26 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3842
909
|
|
|
3843
910
|
// Retrieve the value
|
|
3844
911
|
if (self.value) {
|
|
3845
|
-
input.value = self.value;
|
|
3846
|
-
} else
|
|
3847
|
-
|
|
3848
|
-
if (self.
|
|
3849
|
-
|
|
912
|
+
input.value = render(self.value);
|
|
913
|
+
} else {
|
|
914
|
+
let value = extractValueFromInput();
|
|
915
|
+
if (value && value !== self.value) {
|
|
916
|
+
ret = value;
|
|
3850
917
|
}
|
|
3851
|
-
ret = input.value;
|
|
3852
918
|
}
|
|
3853
919
|
}
|
|
3854
920
|
}
|
|
3855
921
|
|
|
3856
922
|
// Update the internal date values
|
|
3857
923
|
if (ret) {
|
|
3858
|
-
self.
|
|
924
|
+
self.setValue(ret);
|
|
3859
925
|
} else {
|
|
3860
|
-
|
|
926
|
+
setValue(self.value);
|
|
3861
927
|
}
|
|
3862
928
|
|
|
929
|
+
// Reload view
|
|
930
|
+
reloadView(true);
|
|
931
|
+
|
|
3863
932
|
/**
|
|
3864
933
|
* Handler keyboard
|
|
3865
934
|
* @param {object} e - event
|
|
@@ -3870,21 +939,20 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3870
939
|
if (e.target !== self.content) {
|
|
3871
940
|
self.content.focus();
|
|
3872
941
|
}
|
|
3873
|
-
|
|
942
|
+
prev(e);
|
|
3874
943
|
prevent = true;
|
|
3875
944
|
} else if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
|
|
3876
945
|
if (e.target !== self.content) {
|
|
3877
946
|
self.content.focus();
|
|
3878
947
|
}
|
|
3879
|
-
|
|
948
|
+
next(e);
|
|
3880
949
|
prevent = true;
|
|
3881
950
|
} else if (e.key === 'Enter') {
|
|
3882
951
|
if (e.target === self.content) {
|
|
3883
952
|
// Item
|
|
3884
|
-
|
|
3885
|
-
if (item) {
|
|
953
|
+
if (self.cursor.current) {
|
|
3886
954
|
// Select
|
|
3887
|
-
select(e,
|
|
955
|
+
select(e, self.cursor.current);
|
|
3888
956
|
prevent = true;
|
|
3889
957
|
}
|
|
3890
958
|
}
|
|
@@ -3908,9 +976,9 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3908
976
|
self.content.addEventListener('wheel', function(e){
|
|
3909
977
|
if (self.wheel !== false) {
|
|
3910
978
|
if (e.deltaY < 0) {
|
|
3911
|
-
|
|
979
|
+
prev(e);
|
|
3912
980
|
} else {
|
|
3913
|
-
|
|
981
|
+
next(e);
|
|
3914
982
|
}
|
|
3915
983
|
e.preventDefault();
|
|
3916
984
|
}
|
|
@@ -3937,22 +1005,103 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
3937
1005
|
});
|
|
3938
1006
|
});
|
|
3939
1007
|
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
1008
|
+
onchange((prop) => {
|
|
1009
|
+
if (prop === 'view') {
|
|
1010
|
+
reloadView(true);
|
|
1011
|
+
} else if (prop === 'startingDay') {
|
|
1012
|
+
self.weekdays = getWeekdays(self.startingDay ?? 0);
|
|
1013
|
+
} else if (prop === 'value') {
|
|
1014
|
+
dispatchOnChangeEvent();
|
|
1015
|
+
}
|
|
1016
|
+
})
|
|
1017
|
+
|
|
1018
|
+
// Tracking variables
|
|
1019
|
+
track('value');
|
|
1020
|
+
|
|
1021
|
+
// Public methods
|
|
1022
|
+
|
|
1023
|
+
self.open = function(e) {
|
|
1024
|
+
if (self.modal) {
|
|
1025
|
+
if (self.type === 'auto') {
|
|
1026
|
+
self.type = window.innerWidth > 640 ? self.type = 'default' : 'picker';
|
|
1027
|
+
}
|
|
1028
|
+
self.modal.open();
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
self.close = function(options) {
|
|
1033
|
+
if (self.modal) {
|
|
1034
|
+
if (options && options.origin) {
|
|
1035
|
+
self.modal.close(options)
|
|
1036
|
+
} else {
|
|
1037
|
+
self.modal.close({ origin: 'button' })
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
self.isClosed = function() {
|
|
1043
|
+
if (self.modal) {
|
|
1044
|
+
return self.modal.isClosed();
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
self.getValue = function() {
|
|
1049
|
+
return self.value;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
self.setValue = function(v) {
|
|
1053
|
+
// Update value
|
|
1054
|
+
if (v) {
|
|
1055
|
+
let ret = normalize(v);
|
|
1056
|
+
if (isTrue(self.numeric)) {
|
|
1057
|
+
ret = ret.map(entry => {
|
|
1058
|
+
return Helpers.dateToNum(entry);
|
|
1059
|
+
})
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
if (! Array.isArray(v)) {
|
|
1063
|
+
ret = ret.join(',');
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
if (ret == Number(ret)) {
|
|
1067
|
+
ret = Number(ret);
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
v = ret;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// Events
|
|
1074
|
+
if (v !== self.value) {
|
|
1075
|
+
self.value = v;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
self.onevent = function(e) {
|
|
1080
|
+
if (events[e.type]) {
|
|
1081
|
+
events[e.type](e);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
self.update = update;
|
|
1086
|
+
self.next = next;
|
|
1087
|
+
self.prev = prev;
|
|
1088
|
+
self.reset = reset;
|
|
1089
|
+
self.setView = setView;
|
|
1090
|
+
self.helpers = Helpers;
|
|
1091
|
+
self.helpers.getDate = Mask.getDate;
|
|
3943
1092
|
|
|
3944
|
-
return render => render`<div class="lm-calendar"
|
|
1093
|
+
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
1094
|
<div class="lm-calendar-options">
|
|
3946
|
-
<button type="button" onclick="
|
|
1095
|
+
<button type="button" onclick="${reset}">${T('Reset')}</button>
|
|
3947
1096
|
<button type="button" onclick="${update}">${T('Done')}</button>
|
|
3948
1097
|
</div>
|
|
3949
1098
|
<div class="lm-calendar-container" data-view="{{self.view}}">
|
|
3950
1099
|
<div class="lm-calendar-header">
|
|
3951
1100
|
<div>
|
|
3952
|
-
<div class="lm-calendar-labels"><button type="button" onclick="
|
|
1101
|
+
<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
1102
|
<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="
|
|
1103
|
+
<button type="button" class="lm-calendar-icon lm-ripple" onclick="${prev}" tabindex="0">expand_less</button>
|
|
1104
|
+
<button type="button" class="lm-calendar-icon lm-ripple" onclick="${next}" tabindex="0">expand_more</button>
|
|
3956
1105
|
</div>
|
|
3957
1106
|
</div>
|
|
3958
1107
|
<div class="lm-calendar-weekdays" :loop="self.weekdays"><div>{{self.title}}</div></div>
|