@teipublisher/pb-components 1.32.0 → 1.33.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/dist/demo/demos.json +4 -1
- package/dist/demo/pb-timeline.html +122 -0
- package/dist/demo/pb-timeline2.html +94 -0
- package/dist/demo/timeline-dev-data.json +3 -0
- package/dist/pb-components-bundle.js +508 -252
- package/dist/pb-elements.json +275 -3
- package/dist/pb-leaflet-map.js +2 -2
- package/i18n/common/de.json +4 -0
- package/i18n/common/en.json +4 -0
- package/package.json +1 -1
- package/pb-elements.json +275 -3
- package/src/parse-date-service.js +266 -0
- package/src/pb-browse-docs.js +1 -0
- package/src/pb-components.js +1 -0
- package/src/pb-geolocation.js +12 -0
- package/src/pb-leaflet-map.js +45 -9
- package/src/pb-split-list.js +3 -3
- package/src/pb-timeline.js +741 -0
- package/src/search-result-service.js +521 -0
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
import { get as i18n } from "./pb-i18n.js";
|
|
2
|
+
|
|
3
|
+
export class SearchResultService {
|
|
4
|
+
/*
|
|
5
|
+
* SEARCH RESULT OBJECT
|
|
6
|
+
* Service that loads initial data from a datasource,
|
|
7
|
+
* can be a database or an API, and converts it in
|
|
8
|
+
* a format that can be used by the pb-timeline component.
|
|
9
|
+
*
|
|
10
|
+
* public methods:
|
|
11
|
+
* getMinDateStr()
|
|
12
|
+
* getMaxDateStr()
|
|
13
|
+
* getMinDate()
|
|
14
|
+
* getMaxDate()
|
|
15
|
+
* export()
|
|
16
|
+
* getIntervalSizes()
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/*
|
|
20
|
+
* CONSRTUCTOR INPUTS EXPLAINED
|
|
21
|
+
* jsonData: data to load, object with
|
|
22
|
+
* keys => valid datestrings formatted YYYY-MM-DD
|
|
23
|
+
* values => number of results for this day
|
|
24
|
+
* maxInterval: max amount of bins allowed
|
|
25
|
+
* scopes: array of all 6 possible values for scope
|
|
26
|
+
*/
|
|
27
|
+
constructor(jsonData = {}, maxInterval = 60, scopes = ["D", "W", "M", "Y", "5Y", "10Y"]) {
|
|
28
|
+
this.data = { invalid: {}, valid: {} };
|
|
29
|
+
this.maxInterval = maxInterval;
|
|
30
|
+
this.scopes = scopes;
|
|
31
|
+
this._validateJsonData(jsonData);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/*
|
|
35
|
+
* based on the loaded jsonData, compute
|
|
36
|
+
* - min date as dateStr or utc-date-object
|
|
37
|
+
* - max date as dateStr or utc-date-object
|
|
38
|
+
*/
|
|
39
|
+
getMinDateStr() {
|
|
40
|
+
return Object.keys(this.data.valid).sort()[0];
|
|
41
|
+
}
|
|
42
|
+
getMaxDateStr() {
|
|
43
|
+
let days = Object.keys(this.data.valid);
|
|
44
|
+
return days.sort()[days.length - 1];
|
|
45
|
+
}
|
|
46
|
+
getMinDate() {
|
|
47
|
+
return this._dateStrToUTCDate(this.getMinDateStr());
|
|
48
|
+
}
|
|
49
|
+
getMaxDate() {
|
|
50
|
+
return this._dateStrToUTCDate(this.getMaxDateStr());
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getEndOfRangeDate(scope, date) {
|
|
54
|
+
return this._UTCDateToDateStr(this._increaseDateBy(scope, date));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/*
|
|
58
|
+
* exports data for each scope
|
|
59
|
+
* when no argument is provided, the optimal scope based
|
|
60
|
+
* on the maxInterval (default 60) will be assigned
|
|
61
|
+
*/
|
|
62
|
+
export(scope) {
|
|
63
|
+
// auto assign scope when no argument provided
|
|
64
|
+
scope = scope || this._autoAssignScope();
|
|
65
|
+
// validate scope
|
|
66
|
+
if (!this.scopes.includes(scope)) {
|
|
67
|
+
throw new Error(`invalid scope provided, expected: ["10Y", "5Y", "Y", "M", "W", "D"]. Got: "${scope}"`);
|
|
68
|
+
}
|
|
69
|
+
// initialize object to export
|
|
70
|
+
const exportData = {
|
|
71
|
+
data: [],
|
|
72
|
+
scope: scope,
|
|
73
|
+
binTitleRotated: this._binTitleRotatedLookup(scope)
|
|
74
|
+
}
|
|
75
|
+
if (Object.keys(this.data.valid).length === 0) {
|
|
76
|
+
return exportData;
|
|
77
|
+
}
|
|
78
|
+
// get start and end date
|
|
79
|
+
const startCategory = this._classify(this.getMinDateStr(), scope);
|
|
80
|
+
const startDateStr = this._getFirstDay(startCategory);
|
|
81
|
+
let currentDate = this._dateStrToUTCDate(startDateStr);
|
|
82
|
+
const endDate = this.getMaxDate();
|
|
83
|
+
// iterate until end of intervall reached, add binObject for each step
|
|
84
|
+
while (currentDate <= endDate) {
|
|
85
|
+
const currentDateStr = this._UTCDateToDateStr(currentDate);
|
|
86
|
+
const currentCategory = this._classify(currentDateStr, scope);
|
|
87
|
+
exportData.data.push(this._buildBinObject(currentDateStr, currentCategory, scope));
|
|
88
|
+
currentDate = this._increaseDateBy(scope, currentDate);
|
|
89
|
+
}
|
|
90
|
+
// count all values
|
|
91
|
+
Object.keys(this.data.valid).sort().forEach(dateStr => {
|
|
92
|
+
const currentCategory = this._classify(dateStr, scope);
|
|
93
|
+
const targetBinObject = exportData.data.find(it => it.category === currentCategory);
|
|
94
|
+
try {
|
|
95
|
+
const value = this.data.valid[dateStr];
|
|
96
|
+
if (typeof value === 'object') {
|
|
97
|
+
targetBinObject.value += value.count || 0;
|
|
98
|
+
if (value.info) {
|
|
99
|
+
targetBinObject.info = targetBinObject.info.concat(value.info);
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
targetBinObject.value += this.data.valid[dateStr] || 0;
|
|
103
|
+
}
|
|
104
|
+
} catch(e) {
|
|
105
|
+
console.log(e);
|
|
106
|
+
console.log("currentCategory");
|
|
107
|
+
console.log(currentCategory);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
if (this.data.invalid) {
|
|
111
|
+
let invalid = 0;
|
|
112
|
+
let info = [];
|
|
113
|
+
Object.values(this.data.invalid).forEach((value) => {
|
|
114
|
+
if (typeof value === 'object') {
|
|
115
|
+
invalid += value.count || 0;
|
|
116
|
+
info = info.concat(value.info);
|
|
117
|
+
} else {
|
|
118
|
+
invalid += value;
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
if (invalid > 0) {
|
|
122
|
+
exportData.data.push({
|
|
123
|
+
tooltip: i18n('timeline.unknown'),
|
|
124
|
+
title: i18n('timeline.unknown'),
|
|
125
|
+
// binTitle: i18n('timeline.unknown'),
|
|
126
|
+
category: '?',
|
|
127
|
+
separator: true,
|
|
128
|
+
value: invalid,
|
|
129
|
+
info
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return exportData;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/*
|
|
137
|
+
* returns optimal scope based on the maxInterval
|
|
138
|
+
* by computing the scope that meets the criteria
|
|
139
|
+
* nbr of bins <= maxInterval
|
|
140
|
+
*/
|
|
141
|
+
_autoAssignScope() {
|
|
142
|
+
for (let i = 0; i < this.scopes.length; i++) {
|
|
143
|
+
if (this._computeIntervalSize(this.scopes[i]) <= this.maxInterval) {
|
|
144
|
+
return this.scopes[i];
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
throw new Error(`Interval too big! Computed: ${this._computeIntervalSize(this.scopes[i])}. Allowed: ${this.maxInterval}. Try to increase maxInterval.`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/*
|
|
151
|
+
* splits input data in 2 sections
|
|
152
|
+
* => valid data
|
|
153
|
+
* => invalid (if not a vaid date, for example 2012-00-00 is invalid)
|
|
154
|
+
*/
|
|
155
|
+
_validateJsonData(jsonData) {
|
|
156
|
+
Object.keys(jsonData).sort().forEach(key => {
|
|
157
|
+
if (this._isValidDateStr(key)) {
|
|
158
|
+
this.data.valid[key] = jsonData[key];
|
|
159
|
+
} else {
|
|
160
|
+
this.data.invalid[key] = jsonData[key];
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/*
|
|
166
|
+
* lookup table which bin titles should be rotated
|
|
167
|
+
*/
|
|
168
|
+
_binTitleRotatedLookup(scope) {
|
|
169
|
+
const lookup = {
|
|
170
|
+
"10Y": true,
|
|
171
|
+
"5Y": true,
|
|
172
|
+
"Y": true,
|
|
173
|
+
"M": false, // only exception not to rotate in monthly scope
|
|
174
|
+
"W": true,
|
|
175
|
+
"D": true,
|
|
176
|
+
}
|
|
177
|
+
return lookup[scope];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/*
|
|
181
|
+
* Helper method that builds a binObject that
|
|
182
|
+
* can be read by the pb-timeline component
|
|
183
|
+
*/
|
|
184
|
+
_buildBinObject(dateStr, category, scope) {
|
|
185
|
+
const split = dateStr.split("-");
|
|
186
|
+
const yearStr = split[0];
|
|
187
|
+
const monthStr = split[1];
|
|
188
|
+
const dayStr = split[2];
|
|
189
|
+
// for all scopes this remains the same
|
|
190
|
+
const binObject = {
|
|
191
|
+
dateStr: dateStr,
|
|
192
|
+
category: category,
|
|
193
|
+
value: 0,
|
|
194
|
+
info: []
|
|
195
|
+
}
|
|
196
|
+
// scope specific bin data
|
|
197
|
+
if (scope === "10Y") {
|
|
198
|
+
binObject.tooltip = `${category} - ${Number(category) + 9}`; // 1900 - 1999
|
|
199
|
+
binObject.selectionStart = `${category}`;
|
|
200
|
+
binObject.selectionEnd = `${Number(category) + 9}`;
|
|
201
|
+
// seperator every 100 years (10 bins)
|
|
202
|
+
if (Number(category) % 100 === 0) {
|
|
203
|
+
binObject.title = `${category} - ${Number(category) + 99}`;
|
|
204
|
+
binObject.binTitle = category;
|
|
205
|
+
binObject.seperator = true;
|
|
206
|
+
};
|
|
207
|
+
} else if (scope === "5Y") {
|
|
208
|
+
binObject.tooltip = `${category} - ${Number(category) + 4}`; // 1995 - 1999
|
|
209
|
+
binObject.selectionStart = `${category}`;
|
|
210
|
+
binObject.selectionEnd = `${Number(category) + 4}`;
|
|
211
|
+
// seperator every 50 years (10 bins)
|
|
212
|
+
if (Number(category) % 50 === 0) {
|
|
213
|
+
binObject.title = `${category} - ${Number(category) + 49}`;
|
|
214
|
+
binObject.binTitle = category;
|
|
215
|
+
binObject.seperator = true;
|
|
216
|
+
}
|
|
217
|
+
} else if (scope === "Y") {
|
|
218
|
+
binObject.tooltip = category;
|
|
219
|
+
binObject.selectionStart = category;
|
|
220
|
+
binObject.selectionEnd = category;
|
|
221
|
+
// seperator every 10 years (10 bins)
|
|
222
|
+
if (Number(category) % 10 === 0) {
|
|
223
|
+
binObject.title = `${category} - ${Number(category) + 9}`;
|
|
224
|
+
binObject.binTitle = `${category}`;
|
|
225
|
+
binObject.seperator = true;
|
|
226
|
+
}
|
|
227
|
+
} else if (scope === "M") {
|
|
228
|
+
const monthNum = Number(monthStr);
|
|
229
|
+
const month = this._monthLookup(monthNum); // Jan,Feb,Mar,...,Nov,Dez
|
|
230
|
+
binObject.binTitle = month[0]; // J,F,M,A,M,J,J,..N,D
|
|
231
|
+
binObject.tooltip = `${month} ${yearStr}`; // May 1996
|
|
232
|
+
binObject.selectionStart = `${month} ${yearStr}`;
|
|
233
|
+
binObject.selectionEnd = `${month} ${yearStr}`;
|
|
234
|
+
// every first of the month
|
|
235
|
+
if (monthNum === 1) {
|
|
236
|
+
binObject.title = yearStr; // YYYY
|
|
237
|
+
binObject.seperator = true;
|
|
238
|
+
}
|
|
239
|
+
} else if (scope === "W") {
|
|
240
|
+
const week = category.split("-")[1];; // => W52
|
|
241
|
+
binObject.tooltip = `${yearStr} ${week}`; // 1996 W52
|
|
242
|
+
binObject.selectionStart = `${yearStr} ${week}`; // 1996 W52
|
|
243
|
+
binObject.selectionEnd = `${yearStr} ${week}`; // 1996 W52
|
|
244
|
+
let currentDate = this._dateStrToUTCDate(dateStr);
|
|
245
|
+
let lastWeek = this._addDays(currentDate, -7);
|
|
246
|
+
// title and binTitle every first monday of the month
|
|
247
|
+
if (currentDate.getUTCMonth() !== lastWeek.getUTCMonth()) {
|
|
248
|
+
binObject.binTitle = week;
|
|
249
|
+
binObject.title = this._monthLookup(currentDate.getUTCMonth() + 1);
|
|
250
|
+
}
|
|
251
|
+
// seperator every start of the year
|
|
252
|
+
binObject.seperator = week === "W1";
|
|
253
|
+
} else if (scope === "D") {
|
|
254
|
+
binObject.tooltip = dateStr;
|
|
255
|
+
binObject.selectionStart = dateStr;
|
|
256
|
+
binObject.selectionEnd = dateStr;
|
|
257
|
+
// every monday
|
|
258
|
+
if (this._dateStrToUTCDate(dateStr).getUTCDay() === 1) {
|
|
259
|
+
binObject.binTitle = `${Number(dayStr)}.${Number(monthStr)}`;
|
|
260
|
+
binObject.title = `${this._classify(dateStr, "W").replace("-", " ")}`;
|
|
261
|
+
binObject.seperator = true;
|
|
262
|
+
}
|
|
263
|
+
} else {
|
|
264
|
+
throw new Error(`invalid scope provided, expected: ["10Y", "5Y", "Y", "M", "W", "D"]. Got: "${scope}"`);
|
|
265
|
+
}
|
|
266
|
+
return binObject;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/*
|
|
270
|
+
* ...classifies dateStr into category (based on scope)
|
|
271
|
+
* EXAMPLES:
|
|
272
|
+
* _classify("2016-01-12", "10Y") // => "2010"
|
|
273
|
+
* _classify("2016-01-12", "5Y") // => "2015"
|
|
274
|
+
* _classify("2016-01-12", "Y") // => "2016"
|
|
275
|
+
* _classify("2016-01-12", "M") // => "2010-01"
|
|
276
|
+
* _classify("2016-01-12", "W") // => "2016-W2"
|
|
277
|
+
* _classify("2016-01-12", "D") // => "2016-01-12"
|
|
278
|
+
*/
|
|
279
|
+
_classify(dateStr, scope) { // returns category (as string)
|
|
280
|
+
if (!dateStr.match(/^\d{4}-\d{2}-\d{2}$/)) { // quick validate dateStr
|
|
281
|
+
throw new Error(`invalid dateStr format, expected "YYYY-MM-DD", got: "${dateStr}".`);
|
|
282
|
+
}
|
|
283
|
+
if (!dateStr || !scope) { // both inputs provided
|
|
284
|
+
throw new Error(`both inputs must be provided. Got dateStr=${dateStr}, scope=${scope}`);
|
|
285
|
+
}
|
|
286
|
+
switch (scope) {
|
|
287
|
+
case "10Y": case "5Y":
|
|
288
|
+
const intervalSize = Number(scope.replace("Y", ""));
|
|
289
|
+
const startYear = Math.floor(Number(dateStr.split("-")[0]) / intervalSize) * intervalSize;
|
|
290
|
+
return startYear.toString();
|
|
291
|
+
case "Y":
|
|
292
|
+
return dateStr.substr(0, 4);
|
|
293
|
+
case "M":
|
|
294
|
+
return dateStr.substr(0, 7);
|
|
295
|
+
case "W":
|
|
296
|
+
const UTCDate = this._dateStrToUTCDate(dateStr);
|
|
297
|
+
return this._UTCDateToWeekFormat(UTCDate);
|
|
298
|
+
case "D":
|
|
299
|
+
return dateStr;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/*
|
|
304
|
+
* ...gets first day as UTC Date, based on the category
|
|
305
|
+
* EXAMPLES:
|
|
306
|
+
* _getFirstDay("2010") // => 2010-01-01
|
|
307
|
+
* _getFirstDay("2010-12") // => 2010-12-01
|
|
308
|
+
* _getFirstDay("2010-W10") // => 2010-03-08
|
|
309
|
+
*/
|
|
310
|
+
_getFirstDay(categoryStr) {
|
|
311
|
+
if (categoryStr.match(/^\d{4}-\d{2}-\d{2}$/)) { // YYYY-MM-DD => return same value
|
|
312
|
+
return categoryStr;
|
|
313
|
+
}
|
|
314
|
+
if (categoryStr.match(/^\d{4}-\d{2}$/)) { // YYYY-MM
|
|
315
|
+
return `${categoryStr}-01`; // add -01
|
|
316
|
+
}
|
|
317
|
+
if (categoryStr.match(/^\d{4}$/)) { // YYYY
|
|
318
|
+
return `${categoryStr}-01-01`; // add -01-01
|
|
319
|
+
}
|
|
320
|
+
if (categoryStr.match(/^\d{4}-W([1-9]|[1-4][0-9]|5[0-3])$/)) { // YYYY-W? // ? => [1-53]
|
|
321
|
+
// |YYYY-W |1-9 | 10-49 | 50-53 |
|
|
322
|
+
const split = categoryStr.split("-");
|
|
323
|
+
const year = Number(split[0]);
|
|
324
|
+
const weekNumber = Number(split[1].replace("W", ""));
|
|
325
|
+
return this._getDateStrOfISOWeek(year, weekNumber);
|
|
326
|
+
}
|
|
327
|
+
throw new Error("invalid categoryStr");
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/*
|
|
331
|
+
* converts dateStr (YYYY-MM-DD) to a date object in UTC time
|
|
332
|
+
*/
|
|
333
|
+
_dateStrToUTCDate(dateStr) {
|
|
334
|
+
if (!this._isValidDateStr(dateStr)) {
|
|
335
|
+
throw new Error(`invalid dateStr, expected "YYYY-MM-DD" with month[1-12] and day[1-31], got: "${dateStr}".`);
|
|
336
|
+
}
|
|
337
|
+
const split = dateStr.split("-");
|
|
338
|
+
const year = Number(split[0]);
|
|
339
|
+
const month = Number(split[1]);
|
|
340
|
+
const day = Number(split[2]);
|
|
341
|
+
return new Date(Date.UTC(year, month - 1, day));
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/*
|
|
345
|
+
* converts a UTC date object to a dateStr (YYYY-MM-DD)
|
|
346
|
+
*/
|
|
347
|
+
_UTCDateToDateStr(UTCDate) {
|
|
348
|
+
return UTCDate.toISOString().split("T")[0];
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/*
|
|
352
|
+
* example:
|
|
353
|
+
* 1 Jan 2020 => 2020-W1
|
|
354
|
+
*/
|
|
355
|
+
_UTCDateToWeekFormat(UTCDate) {
|
|
356
|
+
const year = this._getISOWeekYear(UTCDate);
|
|
357
|
+
const weekNbr = this._getISOWeek(UTCDate);
|
|
358
|
+
return `${year}-W${weekNbr}`;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/*
|
|
362
|
+
* returns the ISO week (_getISOWeek) or year (_getISOWeekYear)
|
|
363
|
+
* as number based on a UTC date.
|
|
364
|
+
*/
|
|
365
|
+
_getISOWeek(UTCDate) { // https://weeknumber.net/how-to/javascript
|
|
366
|
+
let date = new Date(UTCDate.getTime());
|
|
367
|
+
date.setHours(0, 0, 0, 0);
|
|
368
|
+
// Thursday in current week decides the year.
|
|
369
|
+
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
|
|
370
|
+
// January 4 is always in week 1.
|
|
371
|
+
let week1 = new Date(date.getFullYear(), 0, 4);
|
|
372
|
+
// Adjust to Thursday in week 1 and count number of weeks from date to week1.
|
|
373
|
+
return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000
|
|
374
|
+
- 3 + (week1.getDay() + 6) % 7) / 7);
|
|
375
|
+
}
|
|
376
|
+
/*
|
|
377
|
+
* returns the ISO week year as number based on a UTC date
|
|
378
|
+
* this is only needed for rollovers, for example:
|
|
379
|
+
* => 1.jan 2011 is in W52 of year 2010.
|
|
380
|
+
*/
|
|
381
|
+
_getISOWeekYear(UTCDate) { // https://weeknumber.net/how-to/javascript
|
|
382
|
+
var date = new Date(UTCDate.getTime());
|
|
383
|
+
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
|
|
384
|
+
return date.getFullYear();
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/*
|
|
388
|
+
* given the year and weeknumber -> return dateStr (YYYY-MM-DD)
|
|
389
|
+
*/
|
|
390
|
+
_getDateStrOfISOWeek(year, weekNumber) { // https://stackoverflow.com/a/16591175/6272061
|
|
391
|
+
let simple = new Date(Date.UTC(year, 0, 1 + (weekNumber - 1) * 7));
|
|
392
|
+
let dow = simple.getUTCDay();
|
|
393
|
+
let ISOweekStart = simple;
|
|
394
|
+
if (dow <= 4)
|
|
395
|
+
ISOweekStart.setDate(simple.getDate() - simple.getUTCDay() + 1);
|
|
396
|
+
else
|
|
397
|
+
ISOweekStart.setDate(simple.getDate() + 8 - simple.getUTCDay());
|
|
398
|
+
return ISOweekStart.toISOString().split("T")[0];
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/*
|
|
402
|
+
* compute the interval sizes based on the scope
|
|
403
|
+
* prediction only, not the actuall export of the data
|
|
404
|
+
*/
|
|
405
|
+
getIntervalSizes() {
|
|
406
|
+
return {
|
|
407
|
+
"D": this._computeIntervalSize("D"),
|
|
408
|
+
"W": this._computeIntervalSize("W"),
|
|
409
|
+
"M": this._computeIntervalSize("M"),
|
|
410
|
+
"Y": this._computeIntervalSize("Y"),
|
|
411
|
+
"5Y": this._computeIntervalSize("5Y"),
|
|
412
|
+
"10Y": this._computeIntervalSize("10Y"),
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
_computeIntervalSize(scope) {
|
|
416
|
+
const maxDate = this.getMaxDateStr();
|
|
417
|
+
if (!maxDate) {
|
|
418
|
+
return 0;
|
|
419
|
+
}
|
|
420
|
+
const endDate = this._dateStrToUTCDate(maxDate);
|
|
421
|
+
const firstDayDateStr = this._getFirstDay(this._classify(this.getMinDateStr(), scope));
|
|
422
|
+
let currentDate = this._dateStrToUTCDate(firstDayDateStr);
|
|
423
|
+
let count = 0;
|
|
424
|
+
while (currentDate <= endDate) {
|
|
425
|
+
count++;
|
|
426
|
+
currentDate = this._increaseDateBy(scope, currentDate);
|
|
427
|
+
}
|
|
428
|
+
return count;
|
|
429
|
+
}
|
|
430
|
+
_increaseDateBy(scope, date) {
|
|
431
|
+
switch (scope) {
|
|
432
|
+
case "D":
|
|
433
|
+
return this._addDays(date, 1);
|
|
434
|
+
case "W":
|
|
435
|
+
return this._addDays(date, 7);
|
|
436
|
+
case "M":
|
|
437
|
+
return this._addMonths(date, 1);
|
|
438
|
+
case "Y":
|
|
439
|
+
return this._addYears(date, 1);
|
|
440
|
+
case "5Y":
|
|
441
|
+
return this._addYears(date, 5);
|
|
442
|
+
case "10Y":
|
|
443
|
+
return this._addYears(date, 10);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/*
|
|
448
|
+
* functions that add n days (_addDays), months (_addMonths)
|
|
449
|
+
* or years (_addYears) to a UTC date object
|
|
450
|
+
* returns the computed new UTC date
|
|
451
|
+
*/
|
|
452
|
+
_addDays(UTCDate, days) {
|
|
453
|
+
let newUTCDate = new Date(UTCDate.valueOf());
|
|
454
|
+
newUTCDate.setUTCDate(newUTCDate.getUTCDate() + days);
|
|
455
|
+
return newUTCDate;
|
|
456
|
+
}
|
|
457
|
+
_addMonths(UTCdate, months) {
|
|
458
|
+
let newUTCDate = new Date(UTCdate.valueOf());
|
|
459
|
+
let d = newUTCDate.getUTCDate();
|
|
460
|
+
newUTCDate.setUTCMonth(newUTCDate.getUTCMonth() + +months);
|
|
461
|
+
if (newUTCDate.getUTCDate() != d) {
|
|
462
|
+
newUTCDate.setUTCDate(0);
|
|
463
|
+
}
|
|
464
|
+
return newUTCDate;
|
|
465
|
+
}
|
|
466
|
+
_addYears(UTCdate, years) {
|
|
467
|
+
let newUTCDate = new Date(UTCdate.valueOf());
|
|
468
|
+
newUTCDate.setUTCFullYear(newUTCDate.getUTCFullYear() + years);
|
|
469
|
+
return newUTCDate;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/*
|
|
473
|
+
* Validates dateStr. rules:
|
|
474
|
+
* => year: 4 digit number
|
|
475
|
+
* => month: [1-12]
|
|
476
|
+
* => day: [1-31]
|
|
477
|
+
*/
|
|
478
|
+
_isValidDateStr(str) {
|
|
479
|
+
if (!str) {
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
let split = str.split("-");
|
|
484
|
+
if (split.length !== 3) return false;
|
|
485
|
+
let year = split[0];
|
|
486
|
+
let month = split[1];
|
|
487
|
+
let day = split[2];
|
|
488
|
+
if (year === "0000" || day === "00" || month === "00") return false;
|
|
489
|
+
if (Number(day) < 1 || Number(day) > 31) return false;
|
|
490
|
+
if (Number(month) < 1 || Number(month) > 12) return false;
|
|
491
|
+
// if all checks are passed => valid datestring!
|
|
492
|
+
return true;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/*
|
|
496
|
+
* Converts month number (str or number) to a 3 char
|
|
497
|
+
* abbreviation of the month (in english)
|
|
498
|
+
*/
|
|
499
|
+
_monthLookup(num) {
|
|
500
|
+
if (num > 12 || num < 1) {
|
|
501
|
+
throw new Error(`invalid 'num' provided, expected 1-12. Got: ${num}`);
|
|
502
|
+
}
|
|
503
|
+
const lookup = {
|
|
504
|
+
"1": "Jan",
|
|
505
|
+
"2": "Feb",
|
|
506
|
+
"3": "Mar",
|
|
507
|
+
"4": "Apr",
|
|
508
|
+
"5": "May",
|
|
509
|
+
"6": "Jun",
|
|
510
|
+
"7": "Jul",
|
|
511
|
+
"8": "Aug",
|
|
512
|
+
"9": "Sep",
|
|
513
|
+
"10": "Oct",
|
|
514
|
+
"11": "Nov",
|
|
515
|
+
"12": "Dec",
|
|
516
|
+
}
|
|
517
|
+
return lookup[num.toString()];
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
|