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