data-structure-typed 1.41.6 → 1.41.7
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 +1 -1
- package/README.md +11 -11
- package/benchmark/report.html +11 -11
- package/benchmark/report.json +111 -111
- package/dist/cjs/src/data-structures/graph/abstract-graph.js +5 -5
- package/dist/cjs/src/data-structures/graph/abstract-graph.js.map +1 -1
- package/dist/mjs/src/data-structures/graph/abstract-graph.js +5 -5
- package/dist/umd/data-structure-typed.min.js +1 -1
- package/dist/umd/data-structure-typed.min.js.map +1 -1
- package/package.json +5 -5
- package/src/data-structures/graph/abstract-graph.ts +6 -6
- package/test/config.ts +1 -0
- package/test/integration/avl-tree.test.ts +110 -0
- package/test/integration/bst.test.ts +385 -0
- package/test/integration/heap.test.js +16 -0
- package/test/integration/index.html +51 -0
- package/test/performance/data-structures/binary-tree/avl-tree.test.ts +36 -0
- package/test/performance/data-structures/binary-tree/binary-index-tree.test.ts +0 -0
- package/test/performance/data-structures/binary-tree/binary-tree.test.ts +45 -0
- package/test/performance/data-structures/binary-tree/bst.test.ts +36 -0
- package/test/performance/data-structures/binary-tree/overall.test.ts +0 -0
- package/test/performance/data-structures/binary-tree/rb-tree.test.ts +0 -0
- package/test/performance/data-structures/binary-tree/segment-tree.test.ts +0 -0
- package/test/performance/data-structures/binary-tree/tree-multiset.test.ts +0 -0
- package/test/performance/data-structures/graph/abstract-graph.test.ts +0 -0
- package/test/performance/data-structures/graph/directed-graph.test.ts +49 -0
- package/test/performance/data-structures/graph/map-graph.test.ts +0 -0
- package/test/performance/data-structures/graph/overall.test.ts +0 -0
- package/test/performance/data-structures/graph/undirected-graph.test.ts +0 -0
- package/test/performance/data-structures/hash/coordinate-map.test.ts +0 -0
- package/test/performance/data-structures/hash/coordinate-set.test.ts +0 -0
- package/test/performance/data-structures/hash/hash-map.test.ts +0 -0
- package/test/performance/data-structures/hash/hash-table.test.ts +0 -0
- package/test/performance/data-structures/heap/heap.test.ts +30 -0
- package/test/performance/data-structures/heap/max-heap.test.ts +0 -0
- package/test/performance/data-structures/heap/min-heap.test.ts +0 -0
- package/test/performance/data-structures/linked-list/doubly-linked-list.test.ts +40 -0
- package/test/performance/data-structures/linked-list/linked-list.test.ts +0 -0
- package/test/performance/data-structures/linked-list/singly-linked-list.test.ts +34 -0
- package/test/performance/data-structures/linked-list/skip-linked-list.test.ts +0 -0
- package/test/performance/data-structures/linked-list/skip-list.test.ts +0 -0
- package/test/performance/data-structures/matrix/matrix.test.ts +0 -0
- package/test/performance/data-structures/matrix/matrix2d.test.ts +0 -0
- package/test/performance/data-structures/matrix/navigator.test.ts +0 -0
- package/test/performance/data-structures/matrix/vector2d.test.ts +0 -0
- package/test/performance/data-structures/priority-queue/max-priority-queue.test.ts +19 -0
- package/test/performance/data-structures/priority-queue/min-priority-queue.test.ts +0 -0
- package/test/performance/data-structures/priority-queue/priority-queue.test.ts +0 -0
- package/test/performance/data-structures/queue/deque.test.ts +21 -0
- package/test/performance/data-structures/queue/queue.test.ts +25 -0
- package/test/performance/data-structures/stack/stack.test.ts +0 -0
- package/test/performance/data-structures/tree/tree.test.ts +0 -0
- package/test/performance/data-structures/trie/trie.test.ts +22 -0
- package/test/performance/reportor.ts +186 -0
- package/test/performance/types/index.ts +1 -0
- package/test/performance/types/reportor.ts +3 -0
- package/test/types/index.ts +1 -0
- package/test/types/utils/big-o.ts +1 -0
- package/test/types/utils/index.ts +2 -0
- package/test/types/utils/json2html.ts +1 -0
- package/test/unit/data-structures/binary-tree/avl-tree.test.ts +269 -0
- package/test/unit/data-structures/binary-tree/binary-index-tree.test.ts +320 -0
- package/test/unit/data-structures/binary-tree/binary-tree.test.ts +486 -0
- package/test/unit/data-structures/binary-tree/bst.test.ts +840 -0
- package/test/unit/data-structures/binary-tree/overall.test.ts +66 -0
- package/test/unit/data-structures/binary-tree/rb-tree.test.ts +435 -0
- package/test/unit/data-structures/binary-tree/segment-tree.test.ts +50 -0
- package/test/unit/data-structures/binary-tree/tree-multiset.test.ts +542 -0
- package/test/unit/data-structures/graph/abstract-graph.test.ts +100 -0
- package/test/unit/data-structures/graph/directed-graph.test.ts +564 -0
- package/test/unit/data-structures/graph/map-graph.test.ts +126 -0
- package/test/unit/data-structures/graph/overall.test.ts +49 -0
- package/test/unit/data-structures/graph/salty-edges.json +1 -0
- package/test/unit/data-structures/graph/salty-vertexes.json +1 -0
- package/test/unit/data-structures/graph/undirected-graph.test.ts +168 -0
- package/test/unit/data-structures/hash/coordinate-map.test.ts +74 -0
- package/test/unit/data-structures/hash/coordinate-set.test.ts +66 -0
- package/test/unit/data-structures/hash/hash-map.test.ts +103 -0
- package/test/unit/data-structures/hash/hash-table.test.ts +186 -0
- package/test/unit/data-structures/heap/heap.test.ts +254 -0
- package/test/unit/data-structures/heap/max-heap.test.ts +52 -0
- package/test/unit/data-structures/heap/min-heap.test.ts +52 -0
- package/test/unit/data-structures/linked-list/doubly-linked-list.test.ts +400 -0
- package/test/unit/data-structures/linked-list/linked-list.test.ts +8 -0
- package/test/unit/data-structures/linked-list/singly-linked-list.test.ts +474 -0
- package/test/unit/data-structures/linked-list/skip-linked-list.test.ts +13 -0
- package/test/unit/data-structures/linked-list/skip-list.test.ts +86 -0
- package/test/unit/data-structures/matrix/matrix.test.ts +54 -0
- package/test/unit/data-structures/matrix/matrix2d.test.ts +345 -0
- package/test/unit/data-structures/matrix/navigator.test.ts +244 -0
- package/test/unit/data-structures/matrix/vector2d.test.ts +171 -0
- package/test/unit/data-structures/priority-queue/max-priority-queue.test.ts +73 -0
- package/test/unit/data-structures/priority-queue/min-priority-queue.test.ts +63 -0
- package/test/unit/data-structures/priority-queue/priority-queue.test.ts +53 -0
- package/test/unit/data-structures/queue/deque.test.ts +410 -0
- package/test/unit/data-structures/queue/queue.test.ts +207 -0
- package/test/unit/data-structures/stack/stack.test.ts +67 -0
- package/test/unit/data-structures/tree/tree.test.ts +39 -0
- package/test/unit/data-structures/trie/trie.test.ts +825 -0
- package/test/utils/array.ts +5514 -0
- package/test/utils/big-o.ts +207 -0
- package/test/utils/console.ts +31 -0
- package/test/utils/index.ts +7 -0
- package/test/utils/is.ts +56 -0
- package/test/utils/json2html.ts +322 -0
- package/test/utils/number.ts +13 -0
- package/test/utils/string.ts +1 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import {AnyFunction} from '../types';
|
|
2
|
+
import {isDebugTest} from '../config';
|
|
3
|
+
|
|
4
|
+
const isDebug = isDebugTest;
|
|
5
|
+
const orderReducedBy = 1; // reduction of bigO's order compared to the baseline bigO
|
|
6
|
+
|
|
7
|
+
export const magnitude = {
|
|
8
|
+
CONSTANT: Math.pow(10, 9),
|
|
9
|
+
LOG_N: Math.pow(10, 8 - orderReducedBy),
|
|
10
|
+
LINEAR: Math.pow(10, 7 - orderReducedBy),
|
|
11
|
+
N_LOG_N: Math.pow(10, 4 - orderReducedBy),
|
|
12
|
+
SQUARED: Math.pow(10, 3 - orderReducedBy),
|
|
13
|
+
CUBED: Math.pow(10, 2 - orderReducedBy),
|
|
14
|
+
FACTORIAL: 20 - orderReducedBy,
|
|
15
|
+
THOUSAND: 1000,
|
|
16
|
+
TEN_THOUSAND: 10000,
|
|
17
|
+
HUNDRED_THOUSAND: 100000,
|
|
18
|
+
MILLION: 1000000,
|
|
19
|
+
TEN_MILLION: 10000000,
|
|
20
|
+
BILLION: 100000000
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const bigO = {
|
|
24
|
+
CONSTANT: magnitude.CONSTANT / 100000,
|
|
25
|
+
LOG_N: Math.log2(magnitude.LOG_N) / 1000,
|
|
26
|
+
LINEAR: magnitude.LINEAR / 1000,
|
|
27
|
+
N_LOG_N: (magnitude.N_LOG_N * Math.log2(magnitude.LOG_N)) / 1000,
|
|
28
|
+
SQUARED: Math.pow(magnitude.SQUARED, 2) / 1000,
|
|
29
|
+
CUBED: Math.pow(magnitude.SQUARED, 3) / 1000,
|
|
30
|
+
FACTORIAL: 10000
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function findPotentialN(input: any): number {
|
|
34
|
+
let longestArray: any[] = [];
|
|
35
|
+
let mostProperties: {[key: string]: any} = {};
|
|
36
|
+
|
|
37
|
+
function recurse(obj: any) {
|
|
38
|
+
if (Array.isArray(obj)) {
|
|
39
|
+
if (obj.length > longestArray.length) {
|
|
40
|
+
longestArray = obj;
|
|
41
|
+
}
|
|
42
|
+
} else if (typeof obj === 'object' && obj !== null) {
|
|
43
|
+
const keys = Object.keys(obj);
|
|
44
|
+
if (keys.length > Object.keys(mostProperties).length) {
|
|
45
|
+
mostProperties = obj;
|
|
46
|
+
}
|
|
47
|
+
keys.forEach(key => {
|
|
48
|
+
recurse(obj[key]);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (Array.isArray(input)) {
|
|
54
|
+
input.forEach(item => {
|
|
55
|
+
recurse(item);
|
|
56
|
+
});
|
|
57
|
+
} else {
|
|
58
|
+
recurse(input);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// return [longestArray, mostProperties] : [any[], { [key: string]: any }];
|
|
62
|
+
return Math.max(longestArray.length, Object.keys(mostProperties).length);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function linearRegression(x: number[], y: number[]) {
|
|
66
|
+
const n = x.length;
|
|
67
|
+
|
|
68
|
+
const sumX = x.reduce((acc, value) => acc + value, 0);
|
|
69
|
+
const sumY = y.reduce((acc, value) => acc + value, 0);
|
|
70
|
+
|
|
71
|
+
const sumXSquared = x.reduce((acc, value) => acc + value ** 2, 0);
|
|
72
|
+
const sumXY = x.reduce((acc, value, i) => acc + value * y[i], 0);
|
|
73
|
+
|
|
74
|
+
const slope = (n * sumXY - sumX * sumY) / (n * sumXSquared - sumX ** 2);
|
|
75
|
+
const intercept = (sumY - slope * sumX) / n;
|
|
76
|
+
|
|
77
|
+
const yHat = x.map(value => slope * value + intercept);
|
|
78
|
+
|
|
79
|
+
const totalVariation = y.map((value, i) => (value - yHat[i]) ** 2).reduce((acc, value) => acc + value, 0);
|
|
80
|
+
const explainedVariation = y.map(value => (value - sumY / n) ** 2).reduce((acc, value) => acc + value, 0);
|
|
81
|
+
|
|
82
|
+
const rSquared = 1 - totalVariation / explainedVariation;
|
|
83
|
+
|
|
84
|
+
return {slope, intercept, rSquared};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function estimateBigO(runtimes: number[], dataSizes: number[]): string {
|
|
88
|
+
// Make sure the input runtimes and data sizes have the same length
|
|
89
|
+
if (runtimes.length !== dataSizes.length) {
|
|
90
|
+
return 'Lengths of input arrays do not match';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Create an array to store the computational complexity of each data point
|
|
94
|
+
const complexities: string[] = [];
|
|
95
|
+
|
|
96
|
+
// Traverse different possible complexities
|
|
97
|
+
const complexitiesToCheck: string[] = [
|
|
98
|
+
'O(1)', // constant time complexity
|
|
99
|
+
'O(log n)', // Logarithmic time complexity
|
|
100
|
+
'O(n)', // linear time complexity
|
|
101
|
+
'O(n log n)', // linear logarithmic time complexity
|
|
102
|
+
'O(n^2)' // squared time complexity
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
for (const complexity of complexitiesToCheck) {
|
|
106
|
+
// Calculate data points for fitting
|
|
107
|
+
const fittedData: number[] = dataSizes.map(size => {
|
|
108
|
+
if (complexity === 'O(1)') {
|
|
109
|
+
return 1; // constant time complexity
|
|
110
|
+
} else if (complexity === 'O(log n)') {
|
|
111
|
+
return Math.log(size);
|
|
112
|
+
} else if (complexity === 'O(n)') {
|
|
113
|
+
return size;
|
|
114
|
+
} else if (complexity === 'O(n log n)') {
|
|
115
|
+
return size * Math.log(size);
|
|
116
|
+
} else if (complexity === 'O(n^2)') {
|
|
117
|
+
return size ** 2;
|
|
118
|
+
} else {
|
|
119
|
+
return size ** 10;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Fit the data points using linear regression analysis
|
|
124
|
+
const regressionResult = linearRegression(fittedData, runtimes);
|
|
125
|
+
|
|
126
|
+
// Check the R-squared value of the fit. It is usually considered a valid fit if it is greater than 0.9.
|
|
127
|
+
if (regressionResult.rSquared >= 0.9) {
|
|
128
|
+
complexities.push(complexity);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// If there is no valid fitting result, return "cannot estimate", otherwise return the estimated time complexity
|
|
133
|
+
if (complexities.length === 0) {
|
|
134
|
+
return 'Unable to estimate';
|
|
135
|
+
} else {
|
|
136
|
+
return complexities.join(' or ');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const methodLogs: Map<string, [number, number][]> = new Map();
|
|
141
|
+
|
|
142
|
+
export function logBigOMetricsWrap<F extends AnyFunction>(fn: F, args: Parameters<F>, fnName: string) {
|
|
143
|
+
const startTime = performance.now();
|
|
144
|
+
const result = fn(args);
|
|
145
|
+
const endTime = performance.now();
|
|
146
|
+
const runTime = endTime - startTime;
|
|
147
|
+
const methodName = `${fnName}`;
|
|
148
|
+
if (!methodLogs.has(methodName)) {
|
|
149
|
+
methodLogs.set(methodName, []);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const methodLog = methodLogs.get(methodName);
|
|
153
|
+
|
|
154
|
+
const maxDataSize = args.length === 1 && typeof args[0] === 'number' ? args[0] : findPotentialN(args);
|
|
155
|
+
if (methodLog) {
|
|
156
|
+
methodLog.push([runTime, maxDataSize]);
|
|
157
|
+
|
|
158
|
+
if (methodLog.length >= 20) {
|
|
159
|
+
isDebug && console.log('triggered', methodName, methodLog);
|
|
160
|
+
const bigO = estimateBigO(
|
|
161
|
+
methodLog.map(([runTime]) => runTime),
|
|
162
|
+
methodLog.map(([runTime]) => runTime)
|
|
163
|
+
);
|
|
164
|
+
isDebug && console.log(`Estimated Big O: ${bigO}`);
|
|
165
|
+
methodLogs.delete(methodName);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function logBigOMetrics(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
|
173
|
+
const originalMethod = descriptor.value;
|
|
174
|
+
|
|
175
|
+
descriptor.value = function (...args: any[]) {
|
|
176
|
+
const startTime = performance.now();
|
|
177
|
+
const result = originalMethod.apply(this, args);
|
|
178
|
+
const endTime = performance.now();
|
|
179
|
+
const runTime = endTime - startTime;
|
|
180
|
+
|
|
181
|
+
const methodName = `${target.constructor.name}.${propertyKey}`;
|
|
182
|
+
if (!methodLogs.has(methodName)) {
|
|
183
|
+
methodLogs.set(methodName, []);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const methodLog = methodLogs.get(methodName);
|
|
187
|
+
|
|
188
|
+
const maxDataSize = args.length === 1 && typeof args[0] === 'number' ? args[0] : findPotentialN(args);
|
|
189
|
+
if (methodLog) {
|
|
190
|
+
methodLog.push([runTime, maxDataSize]);
|
|
191
|
+
|
|
192
|
+
if (methodLog.length >= 20) {
|
|
193
|
+
isDebug && console.log('triggered', methodName, methodLog);
|
|
194
|
+
const bigO = estimateBigO(
|
|
195
|
+
methodLog.map(([runTime]) => runTime),
|
|
196
|
+
methodLog.map(([runTime]) => runTime)
|
|
197
|
+
);
|
|
198
|
+
isDebug && console.log(`Estimated Big O: ${bigO}`);
|
|
199
|
+
methodLogs.delete(methodName);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return result;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
return descriptor;
|
|
207
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const Color = {
|
|
2
|
+
END: '\x1b[0m',
|
|
3
|
+
BOLD: '\x1b[1m',
|
|
4
|
+
DIM: '\x1b[2m',
|
|
5
|
+
ITALIC: '\x1b[3m',
|
|
6
|
+
UNDERLINE: '\x1b[4m',
|
|
7
|
+
INVERSE: '\x1b[7m',
|
|
8
|
+
STRIKETHROUGH: '\x1b[9m',
|
|
9
|
+
NO_BOLD: '\x1b[22m',
|
|
10
|
+
NO_ITALIC: '\x1b[23m',
|
|
11
|
+
NO_UNDERLINE: '\x1b[24m',
|
|
12
|
+
NO_INVERSE: '\x1b[27m',
|
|
13
|
+
NO_STRIKETHROUGH: '\x1b[29m',
|
|
14
|
+
BLACK: '\x1b[30m',
|
|
15
|
+
RED: '\x1b[31m',
|
|
16
|
+
GREEN: '\x1b[32m',
|
|
17
|
+
YELLOW: '\x1b[33m',
|
|
18
|
+
BLUE: '\x1b[34m',
|
|
19
|
+
MAGENTA: '\x1b[35m',
|
|
20
|
+
GRAY: '\x1b[90m',
|
|
21
|
+
CYAN: '\x1b[36m',
|
|
22
|
+
WHITE: '\x1b[37m',
|
|
23
|
+
BG_BLACK: '\x1b[40m',
|
|
24
|
+
BG_RED: '\x1b[41m',
|
|
25
|
+
BG_GREEN: '\x1b[42m',
|
|
26
|
+
BG_YELLOW: '\x1b[43m',
|
|
27
|
+
BG_BLUE: '\x1b[44m',
|
|
28
|
+
BG_MAGENTA: '\x1b[45m',
|
|
29
|
+
BG_CYAN: '\x1b[46m',
|
|
30
|
+
BG_WHITE: '\x1b[47m'
|
|
31
|
+
};
|
package/test/utils/is.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export const isNumber = (value: any) => {
|
|
2
|
+
return typeof value === 'number';
|
|
3
|
+
};
|
|
4
|
+
export const isString = (value: any) => {
|
|
5
|
+
return typeof value === 'string';
|
|
6
|
+
};
|
|
7
|
+
export const isBoolean = (value: any) => {
|
|
8
|
+
return typeof value === 'boolean';
|
|
9
|
+
};
|
|
10
|
+
export const isDate = (value: any) => {
|
|
11
|
+
return value instanceof Date;
|
|
12
|
+
};
|
|
13
|
+
export const isNull = (value: any) => {
|
|
14
|
+
return value === null;
|
|
15
|
+
};
|
|
16
|
+
export const isUndefined = (value: any) => {
|
|
17
|
+
return typeof value === 'undefined';
|
|
18
|
+
};
|
|
19
|
+
export const isFunction = (value: any) => {
|
|
20
|
+
return typeof value === 'function';
|
|
21
|
+
};
|
|
22
|
+
export const isObject = (value: any) => {
|
|
23
|
+
return typeof value === 'object';
|
|
24
|
+
};
|
|
25
|
+
export const isArray = (value: any) => {
|
|
26
|
+
return Array.isArray(value);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const isEqual = (objA: any, objB: any): boolean => {
|
|
30
|
+
if (objA === objB) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (typeof objA !== 'object' || typeof objB !== 'object' || objA === null || objB === null) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const keysA = Object.keys(objA);
|
|
39
|
+
const keysB = Object.keys(objB);
|
|
40
|
+
|
|
41
|
+
if (keysA.length !== keysB.length) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for (const key of keysA) {
|
|
46
|
+
if (!keysB.includes(key)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!isEqual(objA[key], objB[key])) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return true;
|
|
56
|
+
};
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import * as _ from './is';
|
|
2
|
+
import {Json2htmlOptions} from '../types';
|
|
3
|
+
|
|
4
|
+
function toggleJS(options?: Json2htmlOptions): string {
|
|
5
|
+
if (options?.plainHtml) {
|
|
6
|
+
return '';
|
|
7
|
+
} else {
|
|
8
|
+
return 'onclick="json-to-html.toggleVisibility(this);return false"';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function makeLabelDiv(options: any, level: number, keyName: string | number, datatype?: string): string {
|
|
13
|
+
if (typeof keyName === 'number') {
|
|
14
|
+
return `<div class='index'><span class='json-to-html-label'>${keyName} </span></div>`;
|
|
15
|
+
} else if (typeof keyName === 'string') {
|
|
16
|
+
if (datatype === 'array') {
|
|
17
|
+
return `<div class='collapsible level${level}' ${toggleJS(
|
|
18
|
+
options
|
|
19
|
+
)}><span class='json-to-html-label'>${keyName}</span></div>`;
|
|
20
|
+
} else if (datatype === 'object') {
|
|
21
|
+
return `<div class='attribute collapsible level${level}' ${toggleJS(
|
|
22
|
+
options
|
|
23
|
+
)}><span class='json-to-html-label'>${keyName}:</span></div>`;
|
|
24
|
+
} else {
|
|
25
|
+
return `<div class='leaf level${level}'><span class='json-to-html-label'>${keyName}:</span></div>`;
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getContentClass(keyName: string | number): string {
|
|
33
|
+
if (typeof keyName === 'string') {
|
|
34
|
+
return 'content';
|
|
35
|
+
} else {
|
|
36
|
+
return '';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isPlainObject(val: any): boolean {
|
|
41
|
+
let lastKey: string | undefined;
|
|
42
|
+
let lastOwnKey: string | undefined;
|
|
43
|
+
for (const key in val) {
|
|
44
|
+
if (val.hasOwnProperty(key)) {
|
|
45
|
+
lastOwnKey = key;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
for (const key in val) {
|
|
49
|
+
lastKey = key;
|
|
50
|
+
}
|
|
51
|
+
return lastOwnKey === lastKey;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function isLeafValue(val: any): boolean {
|
|
55
|
+
return (
|
|
56
|
+
_.isNumber(val) ||
|
|
57
|
+
_.isString(val) ||
|
|
58
|
+
_.isBoolean(val) ||
|
|
59
|
+
_.isDate(val) ||
|
|
60
|
+
_.isNull(val) ||
|
|
61
|
+
_.isUndefined(val) ||
|
|
62
|
+
isNaN(val) ||
|
|
63
|
+
_.isFunction(val) ||
|
|
64
|
+
!isPlainObject(val)
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function isLeafObject(obj: any): boolean {
|
|
69
|
+
if (!_.isObject(obj)) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
for (const key in obj) {
|
|
73
|
+
const val = obj[key];
|
|
74
|
+
if (!isLeafValue(val)) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function isTable(arr: any[]): boolean {
|
|
82
|
+
if (!_.isArray(arr)) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
if (arr.length === 0 || !_.isObject(arr[0])) {
|
|
86
|
+
return false;
|
|
87
|
+
} else {
|
|
88
|
+
let nonCompliant = arr.find(row => !isLeafObject(row));
|
|
89
|
+
if (nonCompliant) {
|
|
90
|
+
return false;
|
|
91
|
+
} else {
|
|
92
|
+
const cols = Object.keys(arr[0]);
|
|
93
|
+
nonCompliant = arr.find((row: object) => !_.isEqual(cols, Object.keys(row)));
|
|
94
|
+
if (nonCompliant) {
|
|
95
|
+
return false;
|
|
96
|
+
} else {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function drawTable(arr: any[]): string {
|
|
104
|
+
function drawRow(headers: string[], rowObj: any): string {
|
|
105
|
+
return '<td>' + headers.map(header => rowObj[header]).join('</td><td>') + '</td>';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const cols = Object.keys(arr[0]);
|
|
109
|
+
const content = arr.map(rowObj => drawRow(cols, rowObj));
|
|
110
|
+
const headingHtml = '<tr><th>' + cols.join('</th><th>') + '</th></tr>';
|
|
111
|
+
const contentHtml = '<tr>' + content.join('</tr><tr>') + '</tr>';
|
|
112
|
+
return '<table>' + headingHtml + contentHtml + '</table>';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function _render(name: string, data: any, options: Json2htmlOptions, level: number, altRow: number): string {
|
|
116
|
+
const contentClass = getContentClass(name);
|
|
117
|
+
if (_.isArray(data)) {
|
|
118
|
+
const title = makeLabelDiv(options, level, `${name}`, 'array');
|
|
119
|
+
let subs: string;
|
|
120
|
+
if (isTable(data)) {
|
|
121
|
+
subs = drawTable(data);
|
|
122
|
+
} else {
|
|
123
|
+
subs =
|
|
124
|
+
"<div class='altRows'>" +
|
|
125
|
+
data
|
|
126
|
+
.map((val: any, idx: number) => _render(idx.toString(), val, options, level + 1, idx % 2))
|
|
127
|
+
.join("</div><div class='altRows'>") +
|
|
128
|
+
'</div>';
|
|
129
|
+
}
|
|
130
|
+
return `<div class="json-to-html-collapse clearfix ${altRow}">
|
|
131
|
+
${title}
|
|
132
|
+
<div class="${contentClass}">${subs}</div>
|
|
133
|
+
</div>`;
|
|
134
|
+
} else if (isLeafValue(data)) {
|
|
135
|
+
const title = makeLabelDiv(options, level, name);
|
|
136
|
+
if (_.isFunction(data)) {
|
|
137
|
+
return `${title}<span class='json-to-html-value'> -function() can't _render-</span>`;
|
|
138
|
+
} else if (!isPlainObject(data)) {
|
|
139
|
+
if (_.isFunction(data.toString)) {
|
|
140
|
+
return `${title}<span class='json-to-html-value'> ${data.toString()}</span>`;
|
|
141
|
+
} else {
|
|
142
|
+
return `${title}<span class='json-to-html-value'> -instance object, can't render-</span>`;
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
return `${title}<span class='json-to-html-value'> ${data}</span>`;
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
const title = makeLabelDiv(options, level, name, 'object');
|
|
149
|
+
let count = 0;
|
|
150
|
+
const subs =
|
|
151
|
+
'<div>' +
|
|
152
|
+
Object.entries(data)
|
|
153
|
+
.map(([key, val]) => _render(key, val, options, level + 1, count++ % 2))
|
|
154
|
+
.join('</div><div>') +
|
|
155
|
+
'</div>';
|
|
156
|
+
const inner = `<div class="json-to-html-expand clearfix ${altRow}">
|
|
157
|
+
${title}
|
|
158
|
+
<div class="${contentClass}">${subs}</div>
|
|
159
|
+
</div>`;
|
|
160
|
+
return `${level === 0 ? "<div id='json-to-html'>" : ''}
|
|
161
|
+
${inner}
|
|
162
|
+
${level === 0 ? '</div>' : ''}`;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function render(name: string, json: any, options: Json2htmlOptions): string {
|
|
167
|
+
// return `${head}${_render('', json, options, 0, 0)}`;
|
|
168
|
+
return `${_render(name, json, options, 0, 0)}`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// const head = `<style>
|
|
172
|
+
// #json-to-html table {
|
|
173
|
+
// border-collapse: collapse;
|
|
174
|
+
// }
|
|
175
|
+
// #json-to-html th {
|
|
176
|
+
// color: #888;
|
|
177
|
+
// }
|
|
178
|
+
// #json-to-html table,th, td {
|
|
179
|
+
// border: 1px solid #DDD;
|
|
180
|
+
// padding: 10px 5px;
|
|
181
|
+
// }
|
|
182
|
+
// #json-to-html th, td {
|
|
183
|
+
// text-align: center;
|
|
184
|
+
// }
|
|
185
|
+
// #json-to-html .content {
|
|
186
|
+
// padding-left: 30px;
|
|
187
|
+
// font-family: Arial;
|
|
188
|
+
// }
|
|
189
|
+
//
|
|
190
|
+
// #json-to-html .index {
|
|
191
|
+
// font-size: 100%;
|
|
192
|
+
// color: #999;
|
|
193
|
+
// float: left;
|
|
194
|
+
// }
|
|
195
|
+
// #json-to-html .clearfix:after {
|
|
196
|
+
// content: ".";
|
|
197
|
+
// display: block;
|
|
198
|
+
// height: 0;
|
|
199
|
+
// clear: both;
|
|
200
|
+
// visibility: hidden;
|
|
201
|
+
// }
|
|
202
|
+
// #json-to-html .json-to-html-label {
|
|
203
|
+
// font-family: Helvetica Neue;
|
|
204
|
+
// color: #333;
|
|
205
|
+
// }
|
|
206
|
+
// #json-to-html .json-to-html-value {
|
|
207
|
+
// font-family: Arial;
|
|
208
|
+
// color: #777;
|
|
209
|
+
// }
|
|
210
|
+
// #json-to-html .collapsible > .json-to-html-label:hover {
|
|
211
|
+
// text-decoration: underline;
|
|
212
|
+
// }
|
|
213
|
+
// #json-to-html .collapsible > .json-to-html-label {
|
|
214
|
+
// color: #15C;
|
|
215
|
+
// }
|
|
216
|
+
// #json-to-html .json-to-html-collapse > div.content {
|
|
217
|
+
// display: none;
|
|
218
|
+
// }
|
|
219
|
+
// #json-to-html .json-to-html-collapse > .json-to-html-label {
|
|
220
|
+
// font-weight: bold;
|
|
221
|
+
// }
|
|
222
|
+
//
|
|
223
|
+
// #json-to-html .json-to-html-expand > div > .json-to-html-label, #json-to-html .json-to-html-collapse > div > .json-to-html-label {
|
|
224
|
+
// background-repeat: no-repeat;
|
|
225
|
+
// background-position: left;
|
|
226
|
+
// padding-left: 25px;
|
|
227
|
+
// margin: 5px 0px 5px 15px;
|
|
228
|
+
// display: inline-block;
|
|
229
|
+
// }
|
|
230
|
+
//
|
|
231
|
+
// #json-to-html .json-to-html-expand > div > .json-to-html-label {
|
|
232
|
+
// width: 30px;
|
|
233
|
+
// height: 30px;
|
|
234
|
+
// background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><polygon points="50,10 10,90 90,90" fill="blue" /></svg>');
|
|
235
|
+
// background-size: cover;
|
|
236
|
+
// background-position: center;
|
|
237
|
+
// }
|
|
238
|
+
//
|
|
239
|
+
// #json-to-html .json-to-html-collapse > div > .json-to-html-label {
|
|
240
|
+
// width: 30px;
|
|
241
|
+
// height: 30px;
|
|
242
|
+
// background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><polygon points="50,10 10,90 90,90" fill="blue" /></svg>');
|
|
243
|
+
// background-size: cover;
|
|
244
|
+
// background-position: center;
|
|
245
|
+
// }
|
|
246
|
+
//
|
|
247
|
+
// #json-to-html .json-to-html-collapse > span.collapsible:before {
|
|
248
|
+
// border-radius: 2px;
|
|
249
|
+
// border-color: #A44;
|
|
250
|
+
// border-style: solid;
|
|
251
|
+
// border-width: 1px;
|
|
252
|
+
// color: #A44;
|
|
253
|
+
// content: '+';
|
|
254
|
+
// display: inline-block;
|
|
255
|
+
// line-height: 7px;
|
|
256
|
+
// margin: 0 2px;
|
|
257
|
+
// overflow: hidden;
|
|
258
|
+
// padding: 1px;
|
|
259
|
+
// font-size: 11px;
|
|
260
|
+
// }
|
|
261
|
+
//
|
|
262
|
+
// #json-to-html .json-to-html-expand > span.collapsible:before {
|
|
263
|
+
// border: none;
|
|
264
|
+
// color: #A44;
|
|
265
|
+
// content: '-';
|
|
266
|
+
// display: inline-block;
|
|
267
|
+
// line-height: 7px;
|
|
268
|
+
// margin: 4px;
|
|
269
|
+
// overflow: hidden;
|
|
270
|
+
// padding: 1px;
|
|
271
|
+
// font-size: 11px;
|
|
272
|
+
// }
|
|
273
|
+
//
|
|
274
|
+
// #json-to-html.level0 {
|
|
275
|
+
// font-size: 25px;
|
|
276
|
+
// }
|
|
277
|
+
// #json-to-html .level1 {
|
|
278
|
+
// font-size: 22px;
|
|
279
|
+
// }
|
|
280
|
+
//
|
|
281
|
+
// #json-to-html .leaf {
|
|
282
|
+
// color: #666;
|
|
283
|
+
// display: inline;
|
|
284
|
+
// }
|
|
285
|
+
//
|
|
286
|
+
// #json-to-html .altRows:nth-child(odd) { background-color:#ddd; }
|
|
287
|
+
// #json-to-html .altRows:nth-child(even) { background-color:#fff; }
|
|
288
|
+
//
|
|
289
|
+
// #json-to-html tr:nth-child(odd) { background-color:#eee; }
|
|
290
|
+
// #json-to-html tr:nth-child(even) { background-color:#fff; }
|
|
291
|
+
// </style>
|
|
292
|
+
// <script type="text/javascript">
|
|
293
|
+
// json-to-html = {
|
|
294
|
+
// toggleVisibility: function(el, name) {
|
|
295
|
+
// json-to-html.toggleClass(el.parentElement,'json-to-html-collapse json-to-html-expand');
|
|
296
|
+
// },
|
|
297
|
+
// classRe: function(name) {
|
|
298
|
+
// return new RegExp('(?:^|\\s)'+name+'(?!\\S)');
|
|
299
|
+
// },
|
|
300
|
+
// addClass: function(el, name) {
|
|
301
|
+
// el.className += " "+name;
|
|
302
|
+
// },
|
|
303
|
+
// removeClass: function(el, name) {
|
|
304
|
+
// var re = json-to-html.classRe(name);
|
|
305
|
+
// el.className = el.className.replace(json-to-html.classRe(name) , '' )
|
|
306
|
+
// },
|
|
307
|
+
// hasClass: function(el, name) {
|
|
308
|
+
// var re = json-to-html.classRe(name);
|
|
309
|
+
// return json-to-html.classRe(name).exec(el.className);
|
|
310
|
+
// },
|
|
311
|
+
// toggleClass: function(el, name) {
|
|
312
|
+
// var names = name.split(/\s+/);
|
|
313
|
+
// for (n in names) {
|
|
314
|
+
// if (json-to-html.hasClass(el, names[n])) {
|
|
315
|
+
// json-to-html.removeClass(el, names[n]);
|
|
316
|
+
// } else {
|
|
317
|
+
// json-to-html.addClass(el, names[n]);
|
|
318
|
+
// }
|
|
319
|
+
// }
|
|
320
|
+
// }
|
|
321
|
+
// };
|
|
322
|
+
// </script>`;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function getRandomInt(min: number, max: number) {
|
|
2
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function numberFix(num: number, decimalPlaces: number): string {
|
|
6
|
+
if (num > 10000 || num < 0.001) {
|
|
7
|
+
const [mantissa, exponent] = num.toExponential().split('e');
|
|
8
|
+
const formattedMantissa = Number(mantissa).toFixed(decimalPlaces);
|
|
9
|
+
return `${formattedMantissa}e${exponent}`;
|
|
10
|
+
} else {
|
|
11
|
+
return num.toFixed(decimalPlaces);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|