-
Notifications
You must be signed in to change notification settings - Fork 333
/
Copy pathutil.js
233 lines (211 loc) · 6.72 KB
/
util.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
// utility functions
export * from "vis-util/esnext";
import * as util from "vis-util/esnext";
import { getType, isNumber, isString } from "vis-util/esnext";
import { DataSet, createNewDataPipeFrom } from "vis-data/esnext";
import moment from "moment";
// parse ASP.Net Date pattern,
// for example '/Date(1198908717056)/' or '/Date(1198908717056-0700)/'
// code from http://momentjs.com/
const ASPDateRegex = /^\/?Date\((-?\d+)/i;
/**
* Convert an object into another type
*
* @param object - Value of unknown type.
* @param type - Name of the desired type.
*
* @returns Object in the desired type.
* @throws Error
*/
export function convert(object, type) {
let match;
if (object === undefined) {
return undefined;
}
if (object === null) {
return null;
}
if (!type) {
return object;
}
if (!(typeof type === "string") && !(type instanceof String)) {
throw new Error("Type must be a string");
}
//noinspection FallthroughInSwitchStatementJS
switch (type) {
case "boolean":
case "Boolean":
return Boolean(object);
case "number":
case "Number":
if (isString(object) && !isNaN(Date.parse(object))) {
return moment(object).valueOf();
} else {
// @TODO: I don't think that Number and String constructors are a good idea.
// This could also fail if the object doesn't have valueOf method or if it's redefined.
// For example: Object.create(null) or { valueOf: 7 }.
return Number(object.valueOf());
}
case "string":
case "String":
return String(object);
case "Date":
if (isNumber(object)) {
return new Date(object);
}
if (object instanceof Date) {
return new Date(object.valueOf());
} else if (moment.isMoment(object)) {
return new Date(object.valueOf());
}
if (isString(object)) {
match = ASPDateRegex.exec(object);
if (match) {
// object is an ASP date
return new Date(Number(match[1])); // parse number
} else {
return moment(new Date(object)).toDate(); // parse string
}
} else {
throw new Error(
"Cannot convert object of type " + getType(object) + " to type Date"
);
}
case "Moment":
if (isNumber(object)) {
return moment(object);
}
if (object instanceof Date) {
return moment(object.valueOf());
} else if (moment.isMoment(object)) {
return moment(object);
}
if (isString(object)) {
match = ASPDateRegex.exec(object);
if (match) {
// object is an ASP date
return moment(Number(match[1])); // parse number
} else {
return moment(object); // parse string
}
} else {
throw new Error(
"Cannot convert object of type " + getType(object) + " to type Date"
);
}
case "ISODate":
if (isNumber(object)) {
return new Date(object);
} else if (object instanceof Date) {
return object.toISOString();
} else if (moment.isMoment(object)) {
return object.toDate().toISOString();
} else if (isString(object)) {
match = ASPDateRegex.exec(object);
if (match) {
// object is an ASP date
return new Date(Number(match[1])).toISOString(); // parse number
} else {
return moment(object).format(); // ISO 8601
}
} else {
throw new Error(
"Cannot convert object of type " +
getType(object) +
" to type ISODate"
);
}
case "ASPDate":
if (isNumber(object)) {
return "/Date(" + object + ")/";
} else if (object instanceof Date || moment.isMoment(object)) {
return "/Date(" + object.valueOf() + ")/";
} else if (isString(object)) {
match = ASPDateRegex.exec(object);
let value;
if (match) {
// object is an ASP date
value = new Date(Number(match[1])).valueOf(); // parse number
} else {
value = new Date(object).valueOf(); // parse string
}
return "/Date(" + value + ")/";
} else {
throw new Error(
"Cannot convert object of type " +
getType(object) +
" to type ASPDate"
);
}
default:
throw new Error(`Unknown type ${type}`);
}
}
/**
* Create a Data Set like wrapper to seamlessly coerce data types.
*
* @param rawDS - The Data Set with raw uncoerced data.
* @param type - A record assigning a data type to property name.
*
* @remarks
* The write operations (`add`, `remove`, `update` and `updateOnly`) write into
* the raw (uncoerced) data set. These values are then picked up by a pipe
* which coerces the values using the [[convert]] function and feeds them into
* the coerced data set. When querying (`forEach`, `get`, `getIds`, `off` and
* `on`) the values are then fetched from the coerced data set and already have
* the required data types. The values are coerced only once when inserted and
* then the same value is returned each time until it is updated or deleted.
*
* For example: `typeCoercedDataSet.add({ id: 7, start: "2020-01-21" })` would
* result in `typeCoercedDataSet.get(7)` returning `{ id: 7, start: moment(new
* Date("2020-01-21")).toDate() }`.
*
* Use the dispose method prior to throwing a reference to this away. Otherwise
* the pipe connecting the two Data Sets will keep the unaccessible coerced
* Data Set alive and updated as long as the raw Data Set exists.
*
* @returns A Data Set like object that saves data into the raw Data Set and
* retrieves them from the coerced Data Set.
*/
export function typeCoerceDataSet(
rawDS,
type = { start: "Date", end: "Date" }
) {
const idProp = rawDS._idProp;
const coercedDS = new DataSet({ fieldId: idProp });
const pipe = createNewDataPipeFrom(rawDS)
.map(item =>
Object.keys(item).reduce((acc, key) => {
acc[key] = convert(item[key], type[key]);
return acc;
}, {})
)
.to(coercedDS);
pipe.all().start();
return {
// Write only.
add: (...args) => rawDS.getDataSet().add(...args),
remove: (...args) => rawDS.getDataSet().remove(...args),
update: (...args) => rawDS.getDataSet().update(...args),
updateOnly: (...args) => rawDS.getDataSet().updateOnly(...args),
// Read only.
forEach: coercedDS.forEach.bind(coercedDS),
get: coercedDS.get.bind(coercedDS),
getIds: coercedDS.getIds.bind(coercedDS),
off: coercedDS.off.bind(coercedDS),
on: coercedDS.on.bind(coercedDS),
get length() {
return coercedDS.length;
},
// Non standard.
idProp,
type,
rawDS,
coercedDS,
dispose: () => pipe.stop()
};
}
export default {
...util,
convert
};