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