scorm-again 1.7.0 → 2.0.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/.babelrc +18 -7
- package/.github/dependabot.yml +5 -0
- package/.github/workflows/main.yml +79 -0
- package/.jsdoc.json +4 -5
- package/.mocharc.json +8 -0
- package/.run/Mocha Unit Tests.run.xml +5 -2
- package/CONTRIBUTING.md +1 -1
- package/README.md +14 -1
- package/dist/aicc.js +3661 -7170
- package/dist/aicc.js.map +1 -1
- package/dist/aicc.min.js +2 -40
- package/dist/aicc.min.js.map +1 -0
- package/dist/scorm-again.js +5671 -10695
- package/dist/scorm-again.js.map +1 -1
- package/dist/scorm-again.min.js +2 -52
- package/dist/scorm-again.min.js.map +1 -0
- package/dist/scorm12.js +2871 -5433
- package/dist/scorm12.js.map +1 -1
- package/dist/scorm12.min.js +2 -34
- package/dist/scorm12.min.js.map +1 -0
- package/dist/scorm2004.js +3868 -6797
- package/dist/scorm2004.js.map +1 -1
- package/dist/scorm2004.min.js +2 -40
- package/dist/scorm2004.min.js.map +1 -0
- package/eslint.config.js +21 -0
- package/package.json +73 -35
- package/results.json +34254 -0
- package/src/{AICC.js → AICC.ts} +27 -21
- package/src/BaseAPI.ts +1449 -0
- package/src/Scorm12API.ts +360 -0
- package/src/{Scorm2004API.js → Scorm2004API.ts} +245 -163
- package/src/cmi/aicc_cmi.ts +1248 -0
- package/src/cmi/common.ts +411 -0
- package/src/cmi/scorm12_cmi.ts +1426 -0
- package/src/cmi/scorm2004_cmi.ts +1874 -0
- package/src/constants/api_constants.ts +318 -0
- package/src/constants/error_codes.ts +88 -0
- package/src/constants/language_constants.ts +394 -0
- package/src/constants/regex.ts +97 -0
- package/src/constants/{response_constants.js → response_constants.ts} +67 -62
- package/src/exceptions.ts +133 -0
- package/src/exports/aicc.js +1 -1
- package/src/exports/scorm-again.js +3 -3
- package/src/exports/scorm12.js +1 -1
- package/src/exports/scorm2004.js +1 -1
- package/src/{utilities.js → utilities.ts} +114 -74
- package/tea.yaml +6 -0
- package/test/{AICC.spec.js → AICC.spec.ts} +70 -72
- package/test/Scorm12API.spec.ts +580 -0
- package/test/Scorm2004API.spec.ts +812 -0
- package/test/api_helpers.ts +176 -0
- package/test/cmi/{aicc_cmi.spec.js → aicc_cmi.spec.ts} +193 -209
- package/test/cmi/{scorm12_cmi.spec.js → scorm12_cmi.spec.ts} +251 -269
- package/test/cmi/scorm2004_cmi.spec.ts +1031 -0
- package/test/cmi_helpers.ts +207 -0
- package/test/exceptions.spec.ts +79 -0
- package/test/field_values.ts +202 -0
- package/test/utilities.spec.ts +322 -0
- package/tsconfig.json +18 -0
- package/webpack.config.js +65 -0
- package/.circleci/config.yml +0 -99
- package/.codeclimate.yml +0 -7
- package/.eslintrc.js +0 -36
- package/src/.flowconfig +0 -11
- package/src/BaseAPI.js +0 -1275
- package/src/Scorm12API.js +0 -308
- package/src/cmi/aicc_cmi.js +0 -1141
- package/src/cmi/common.js +0 -328
- package/src/cmi/scorm12_cmi.js +0 -1312
- package/src/cmi/scorm2004_cmi.js +0 -1692
- package/src/constants/api_constants.js +0 -218
- package/src/constants/error_codes.js +0 -87
- package/src/constants/language_constants.js +0 -76
- package/src/constants/regex.js +0 -84
- package/src/exceptions.js +0 -104
- package/test/Scorm12API.spec.js +0 -528
- package/test/Scorm2004API.spec.js +0 -775
- package/test/abstract_classes.spec.js +0 -17
- package/test/api_helpers.js +0 -128
- package/test/cmi/scorm2004_cmi.spec.js +0 -1066
- package/test/cmi_helpers.js +0 -161
- package/test/exceptions.spec.js +0 -71
- package/test/field_values.js +0 -353
- package/test/utilities.spec.js +0 -339
- package/webpack.js +0 -78
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import APIConstants from "./constants/api_constants";
|
|
2
|
+
import { BaseScormValidationError } from "./cmi/common";
|
|
3
|
+
|
|
4
|
+
const scorm12_errors = APIConstants.scorm12.error_descriptions;
|
|
5
|
+
const aicc_errors = APIConstants.aicc.error_descriptions;
|
|
6
|
+
const scorm2004_errors = APIConstants.scorm2004.error_descriptions;
|
|
7
|
+
|
|
8
|
+
type APIError = {
|
|
9
|
+
errorCode: number;
|
|
10
|
+
errorMessage: string;
|
|
11
|
+
detailedMessage: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Base Validation Exception
|
|
16
|
+
*/
|
|
17
|
+
export class ValidationError
|
|
18
|
+
extends BaseScormValidationError
|
|
19
|
+
implements APIError
|
|
20
|
+
{
|
|
21
|
+
/**
|
|
22
|
+
* Constructor to take in an error message and code
|
|
23
|
+
* @param {number} errorCode
|
|
24
|
+
* @param {string} errorMessage
|
|
25
|
+
* @param {string} detailedMessage
|
|
26
|
+
*/
|
|
27
|
+
constructor(
|
|
28
|
+
errorCode: number,
|
|
29
|
+
errorMessage: string,
|
|
30
|
+
detailedMessage?: string,
|
|
31
|
+
) {
|
|
32
|
+
super(errorCode);
|
|
33
|
+
this.setMessage(errorMessage);
|
|
34
|
+
this._errorMessage = errorMessage;
|
|
35
|
+
if (detailedMessage) {
|
|
36
|
+
this._detailedMessage = detailedMessage;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private readonly _errorMessage: string;
|
|
41
|
+
private readonly _detailedMessage: string = "";
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Getter for _errorMessage
|
|
45
|
+
* @return {string}
|
|
46
|
+
*/
|
|
47
|
+
get errorMessage(): string {
|
|
48
|
+
return this._errorMessage;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Getter for _detailedMessage
|
|
53
|
+
* @return {string}
|
|
54
|
+
*/
|
|
55
|
+
get detailedMessage(): string {
|
|
56
|
+
return this._detailedMessage;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* SCORM 1.2 Validation Error
|
|
62
|
+
*/
|
|
63
|
+
export class Scorm12ValidationError extends ValidationError {
|
|
64
|
+
/**
|
|
65
|
+
* Constructor to take in an error code
|
|
66
|
+
* @param {number} errorCode
|
|
67
|
+
*/
|
|
68
|
+
constructor(errorCode: number) {
|
|
69
|
+
if ({}.hasOwnProperty.call(scorm12_errors, String(errorCode))) {
|
|
70
|
+
super(
|
|
71
|
+
errorCode,
|
|
72
|
+
scorm12_errors[String(errorCode)].basicMessage,
|
|
73
|
+
scorm12_errors[String(errorCode)].detailMessage,
|
|
74
|
+
);
|
|
75
|
+
} else {
|
|
76
|
+
super(
|
|
77
|
+
101,
|
|
78
|
+
scorm12_errors["101"].basicMessage,
|
|
79
|
+
scorm12_errors["101"].detailMessage,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* AICC Validation Error
|
|
87
|
+
*/
|
|
88
|
+
export class AICCValidationError extends ValidationError {
|
|
89
|
+
/**
|
|
90
|
+
* Constructor to take in an error code
|
|
91
|
+
* @param {number} errorCode
|
|
92
|
+
*/
|
|
93
|
+
constructor(errorCode: number) {
|
|
94
|
+
if ({}.hasOwnProperty.call(aicc_errors, String(errorCode))) {
|
|
95
|
+
super(
|
|
96
|
+
errorCode,
|
|
97
|
+
aicc_errors[String(errorCode)].basicMessage,
|
|
98
|
+
aicc_errors[String(errorCode)].detailMessage,
|
|
99
|
+
);
|
|
100
|
+
} else {
|
|
101
|
+
super(
|
|
102
|
+
101,
|
|
103
|
+
aicc_errors["101"].basicMessage,
|
|
104
|
+
aicc_errors["101"].detailMessage,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* SCORM 2004 Validation Error
|
|
112
|
+
*/
|
|
113
|
+
export class Scorm2004ValidationError extends ValidationError {
|
|
114
|
+
/**
|
|
115
|
+
* Constructor to take in an error code
|
|
116
|
+
* @param {number} errorCode
|
|
117
|
+
*/
|
|
118
|
+
constructor(errorCode: number) {
|
|
119
|
+
if ({}.hasOwnProperty.call(scorm2004_errors, String(errorCode))) {
|
|
120
|
+
super(
|
|
121
|
+
errorCode,
|
|
122
|
+
scorm2004_errors[String(errorCode)].basicMessage,
|
|
123
|
+
scorm2004_errors[String(errorCode)].detailMessage,
|
|
124
|
+
);
|
|
125
|
+
} else {
|
|
126
|
+
super(
|
|
127
|
+
101,
|
|
128
|
+
scorm2004_errors["101"].basicMessage,
|
|
129
|
+
scorm2004_errors["101"].detailMessage,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
package/src/exports/aicc.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import Scorm2004API from
|
|
2
|
-
import Scorm12API from
|
|
3
|
-
import AICC from
|
|
1
|
+
import Scorm2004API from "../Scorm2004API.ts";
|
|
2
|
+
import Scorm12API from "../Scorm12API.ts";
|
|
3
|
+
import AICC from "../AICC.ts";
|
|
4
4
|
|
|
5
5
|
window.Scorm12API = Scorm12API;
|
|
6
6
|
window.Scorm2004API = Scorm2004API;
|
package/src/exports/scorm12.js
CHANGED
package/src/exports/scorm2004.js
CHANGED
|
@@ -1,87 +1,101 @@
|
|
|
1
|
-
// @flow
|
|
2
1
|
export const SECONDS_PER_SECOND = 1.0;
|
|
3
2
|
export const SECONDS_PER_MINUTE = 60;
|
|
4
3
|
export const SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTE;
|
|
5
4
|
export const SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR;
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
[
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
];
|
|
6
|
+
type Designation = {
|
|
7
|
+
[key: string]: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type StringKeyMap = {
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const designations: Designation = {
|
|
15
|
+
D: SECONDS_PER_DAY,
|
|
16
|
+
H: SECONDS_PER_HOUR,
|
|
17
|
+
M: SECONDS_PER_MINUTE,
|
|
18
|
+
S: SECONDS_PER_SECOND,
|
|
19
|
+
};
|
|
13
20
|
|
|
14
21
|
/**
|
|
15
22
|
* Converts a Number to a String of HH:MM:SS
|
|
16
23
|
*
|
|
17
|
-
* @param {
|
|
24
|
+
* @param {number} totalSeconds
|
|
18
25
|
* @return {string}
|
|
19
26
|
*/
|
|
20
|
-
export function getSecondsAsHHMMSS(totalSeconds:
|
|
27
|
+
export function getSecondsAsHHMMSS(totalSeconds: number | null): string {
|
|
21
28
|
// SCORM spec does not deal with negative durations, give zero back
|
|
22
29
|
if (!totalSeconds || totalSeconds <= 0) {
|
|
23
|
-
return
|
|
30
|
+
return "00:00:00";
|
|
24
31
|
}
|
|
25
32
|
|
|
26
33
|
const hours = Math.floor(totalSeconds / SECONDS_PER_HOUR);
|
|
27
|
-
|
|
28
34
|
const dateObj = new Date(totalSeconds * 1000);
|
|
29
35
|
const minutes = dateObj.getUTCMinutes();
|
|
30
36
|
// make sure we add any possible decimal value
|
|
31
37
|
const seconds = dateObj.getSeconds();
|
|
32
38
|
const ms = totalSeconds % 1.0;
|
|
33
|
-
let msStr =
|
|
39
|
+
let msStr = "";
|
|
40
|
+
|
|
34
41
|
if (countDecimals(ms) > 0) {
|
|
35
42
|
if (countDecimals(ms) > 2) {
|
|
36
43
|
msStr = ms.toFixed(2);
|
|
37
44
|
} else {
|
|
38
45
|
msStr = String(ms);
|
|
39
46
|
}
|
|
40
|
-
|
|
47
|
+
|
|
48
|
+
msStr = "." + msStr.split(".")[1];
|
|
41
49
|
}
|
|
42
50
|
|
|
43
|
-
return (
|
|
44
|
-
|
|
51
|
+
return (
|
|
52
|
+
(hours + ":" + minutes + ":" + seconds).replace(/\b\d\b/g, "0$&") + msStr
|
|
53
|
+
);
|
|
45
54
|
}
|
|
46
55
|
|
|
47
56
|
/**
|
|
48
57
|
* Calculate the number of seconds from ISO 8601 Duration
|
|
49
58
|
*
|
|
50
|
-
* @param {
|
|
51
|
-
* @return {
|
|
59
|
+
* @param {number} seconds
|
|
60
|
+
* @return {string}
|
|
52
61
|
*/
|
|
53
|
-
export function getSecondsAsISODuration(seconds:
|
|
62
|
+
export function getSecondsAsISODuration(seconds: number | null): string {
|
|
54
63
|
// SCORM spec does not deal with negative durations, give zero back
|
|
55
64
|
if (!seconds || seconds <= 0) {
|
|
56
|
-
return
|
|
65
|
+
return "PT0S";
|
|
57
66
|
}
|
|
58
67
|
|
|
59
|
-
let duration =
|
|
68
|
+
let duration = "P";
|
|
60
69
|
let remainder = seconds;
|
|
61
|
-
|
|
62
|
-
|
|
70
|
+
for (const designationsKey in designations) {
|
|
71
|
+
const current_seconds = designations[designationsKey];
|
|
63
72
|
let value = Math.floor(remainder / current_seconds);
|
|
64
|
-
|
|
65
73
|
remainder = remainder % current_seconds;
|
|
74
|
+
|
|
66
75
|
if (countDecimals(remainder) > 2) {
|
|
67
76
|
remainder = Number(Number(remainder).toFixed(2));
|
|
68
77
|
}
|
|
78
|
+
|
|
69
79
|
// If we have anything left in the remainder, and we're currently adding
|
|
70
80
|
// seconds to the duration, go ahead and add the decimal to the seconds
|
|
71
|
-
if (
|
|
81
|
+
if (designationsKey === "S" && remainder > 0) {
|
|
72
82
|
value += remainder;
|
|
73
83
|
}
|
|
74
84
|
|
|
75
85
|
if (value) {
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
86
|
+
if (
|
|
87
|
+
(duration.indexOf("D") > 0 ||
|
|
88
|
+
designationsKey === "H" ||
|
|
89
|
+
designationsKey === "M" ||
|
|
90
|
+
designationsKey === "S") &&
|
|
91
|
+
duration.indexOf("T") === -1
|
|
92
|
+
) {
|
|
93
|
+
duration += "T";
|
|
80
94
|
}
|
|
81
|
-
duration += `${value}${sign}`;
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
95
|
|
|
96
|
+
duration += `${value}${designationsKey}`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
85
99
|
return duration;
|
|
86
100
|
}
|
|
87
101
|
|
|
@@ -92,16 +106,25 @@ export function getSecondsAsISODuration(seconds: Number) {
|
|
|
92
106
|
* @param {RegExp} timeRegex
|
|
93
107
|
* @return {number}
|
|
94
108
|
*/
|
|
95
|
-
export function getTimeAsSeconds(
|
|
96
|
-
|
|
97
|
-
|
|
109
|
+
export function getTimeAsSeconds(
|
|
110
|
+
timeString: string | number | boolean | null,
|
|
111
|
+
timeRegex: RegExp | string,
|
|
112
|
+
): number {
|
|
113
|
+
if (typeof timeString === "number" || typeof timeString === "boolean") {
|
|
114
|
+
timeString = String(timeString);
|
|
115
|
+
}
|
|
116
|
+
if (typeof timeRegex === "string") {
|
|
117
|
+
timeRegex = new RegExp(timeRegex);
|
|
118
|
+
}
|
|
119
|
+
if (!timeString || !timeString.match(timeRegex)) {
|
|
98
120
|
return 0;
|
|
99
121
|
}
|
|
100
|
-
|
|
122
|
+
|
|
123
|
+
const parts = timeString.split(":");
|
|
101
124
|
const hours = Number(parts[0]);
|
|
102
125
|
const minutes = Number(parts[1]);
|
|
103
126
|
const seconds = Number(parts[2]);
|
|
104
|
-
return
|
|
127
|
+
return hours * 3600 + minutes * 60 + seconds;
|
|
105
128
|
}
|
|
106
129
|
|
|
107
130
|
/**
|
|
@@ -111,22 +134,26 @@ export function getTimeAsSeconds(timeString: String, timeRegex: RegExp) {
|
|
|
111
134
|
* @param {RegExp} durationRegex
|
|
112
135
|
* @return {number}
|
|
113
136
|
*/
|
|
114
|
-
export function getDurationAsSeconds(
|
|
137
|
+
export function getDurationAsSeconds(
|
|
138
|
+
duration: string | null,
|
|
139
|
+
durationRegex: RegExp | string,
|
|
140
|
+
): number {
|
|
141
|
+
if (typeof durationRegex === "string") {
|
|
142
|
+
durationRegex = new RegExp(durationRegex);
|
|
143
|
+
}
|
|
144
|
+
|
|
115
145
|
if (!duration || !duration.match(durationRegex)) {
|
|
116
146
|
return 0;
|
|
117
147
|
}
|
|
118
148
|
|
|
119
|
-
const [, years,
|
|
120
|
-
|
|
121
|
-
|
|
149
|
+
const [, years, _, , days, hours, minutes, seconds] =
|
|
150
|
+
new RegExp(durationRegex).exec(duration) || [];
|
|
122
151
|
let result = 0.0;
|
|
123
|
-
|
|
124
|
-
result +=
|
|
125
|
-
result +=
|
|
126
|
-
result +=
|
|
127
|
-
result +=
|
|
128
|
-
result += (Number(years) * (60 * 60 * 24 * 365.0) || 0.0);
|
|
129
|
-
|
|
152
|
+
result += Number(seconds) || 0.0;
|
|
153
|
+
result += Number(minutes) * 60.0 || 0.0;
|
|
154
|
+
result += Number(hours) * 3600.0 || 0.0;
|
|
155
|
+
result += Number(days) * (60 * 60 * 24.0) || 0.0;
|
|
156
|
+
result += Number(years) * (60 * 60 * 24 * 365.0) || 0.0;
|
|
130
157
|
return result;
|
|
131
158
|
}
|
|
132
159
|
|
|
@@ -135,16 +162,20 @@ export function getDurationAsSeconds(duration: String, durationRegex: RegExp) {
|
|
|
135
162
|
*
|
|
136
163
|
* @param {string} first
|
|
137
164
|
* @param {string} second
|
|
138
|
-
* @param {RegExp} durationRegex
|
|
165
|
+
* @param {RegExp|string} durationRegex
|
|
139
166
|
* @return {string}
|
|
140
167
|
*/
|
|
141
168
|
export function addTwoDurations(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
169
|
+
first: string,
|
|
170
|
+
second: string,
|
|
171
|
+
durationRegex: RegExp | string,
|
|
172
|
+
): string {
|
|
173
|
+
const regex: RegExp =
|
|
174
|
+
typeof durationRegex === "string"
|
|
175
|
+
? new RegExp(durationRegex)
|
|
176
|
+
: durationRegex;
|
|
145
177
|
return getSecondsAsISODuration(
|
|
146
|
-
|
|
147
|
-
getDurationAsSeconds(second, durationRegex),
|
|
178
|
+
getDurationAsSeconds(first, regex) + getDurationAsSeconds(second, regex),
|
|
148
179
|
);
|
|
149
180
|
}
|
|
150
181
|
|
|
@@ -157,13 +188,15 @@ export function addTwoDurations(
|
|
|
157
188
|
* @return {string}
|
|
158
189
|
*/
|
|
159
190
|
export function addHHMMSSTimeStrings(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
191
|
+
first: string,
|
|
192
|
+
second: string,
|
|
193
|
+
timeRegex: RegExp | string,
|
|
194
|
+
): string {
|
|
195
|
+
if (typeof timeRegex === "string") {
|
|
196
|
+
timeRegex = new RegExp(timeRegex);
|
|
197
|
+
}
|
|
163
198
|
return getSecondsAsHHMMSS(
|
|
164
|
-
|
|
165
|
-
getTimeAsSeconds(
|
|
166
|
-
second, timeRegex),
|
|
199
|
+
getTimeAsSeconds(first, timeRegex) + getTimeAsSeconds(second, timeRegex),
|
|
167
200
|
);
|
|
168
201
|
}
|
|
169
202
|
|
|
@@ -172,35 +205,37 @@ export function addHHMMSSTimeStrings(
|
|
|
172
205
|
* @param {object} data
|
|
173
206
|
* @return {object}
|
|
174
207
|
*/
|
|
175
|
-
export function flatten(data) {
|
|
176
|
-
const result = {};
|
|
208
|
+
export function flatten(data: StringKeyMap): object {
|
|
209
|
+
const result: StringKeyMap = {};
|
|
177
210
|
|
|
178
211
|
/**
|
|
179
212
|
* Recurse through the object
|
|
180
213
|
* @param {*} cur
|
|
181
214
|
* @param {*} prop
|
|
182
215
|
*/
|
|
183
|
-
function recurse(cur, prop) {
|
|
216
|
+
function recurse(cur: any, prop: any) {
|
|
184
217
|
if (Object(cur) !== cur) {
|
|
185
218
|
result[prop] = cur;
|
|
186
219
|
} else if (Array.isArray(cur)) {
|
|
187
220
|
for (let i = 0, l = cur.length; i < l; i++) {
|
|
188
|
-
recurse(cur[i], prop +
|
|
221
|
+
recurse(cur[i], prop + "[" + i + "]");
|
|
189
222
|
if (l === 0) result[prop] = [];
|
|
190
223
|
}
|
|
191
224
|
} else {
|
|
192
225
|
let isEmpty = true;
|
|
226
|
+
|
|
193
227
|
for (const p in cur) {
|
|
194
228
|
if ({}.hasOwnProperty.call(cur, p)) {
|
|
195
229
|
isEmpty = false;
|
|
196
|
-
recurse(cur[p], prop ? prop +
|
|
230
|
+
recurse(cur[p], prop ? prop + "." + p : p);
|
|
197
231
|
}
|
|
198
232
|
}
|
|
233
|
+
|
|
199
234
|
if (isEmpty && prop) result[prop] = {};
|
|
200
235
|
}
|
|
201
236
|
}
|
|
202
237
|
|
|
203
|
-
recurse(data,
|
|
238
|
+
recurse(data, "");
|
|
204
239
|
return result;
|
|
205
240
|
}
|
|
206
241
|
|
|
@@ -209,25 +244,30 @@ export function flatten(data) {
|
|
|
209
244
|
* @param {object} data
|
|
210
245
|
* @return {object}
|
|
211
246
|
*/
|
|
212
|
-
export function unflatten(data) {
|
|
213
|
-
|
|
247
|
+
export function unflatten(data: StringKeyMap): object {
|
|
248
|
+
"use strict";
|
|
249
|
+
|
|
214
250
|
if (Object(data) !== data || Array.isArray(data)) return data;
|
|
215
251
|
const regex = /\.?([^.[\]]+)|\[(\d+)]/g;
|
|
216
|
-
const result = {};
|
|
252
|
+
const result: StringKeyMap = {};
|
|
253
|
+
|
|
217
254
|
for (const p in data) {
|
|
218
255
|
if ({}.hasOwnProperty.call(data, p)) {
|
|
219
256
|
let cur = result;
|
|
220
|
-
let prop =
|
|
257
|
+
let prop = "";
|
|
221
258
|
let m = regex.exec(p);
|
|
259
|
+
|
|
222
260
|
while (m) {
|
|
223
|
-
cur = cur[prop] || (cur[prop] =
|
|
261
|
+
cur = cur[prop] || (cur[prop] = m[2] ? [] : {});
|
|
224
262
|
prop = m[2] || m[1];
|
|
225
263
|
m = regex.exec(p);
|
|
226
264
|
}
|
|
265
|
+
|
|
227
266
|
cur[prop] = data[p];
|
|
228
267
|
}
|
|
229
268
|
}
|
|
230
|
-
|
|
269
|
+
|
|
270
|
+
return result[""] || result;
|
|
231
271
|
}
|
|
232
272
|
|
|
233
273
|
/**
|
|
@@ -235,8 +275,8 @@ export function unflatten(data) {
|
|
|
235
275
|
* @param {number} num
|
|
236
276
|
* @return {number}
|
|
237
277
|
*/
|
|
238
|
-
export function countDecimals(num: number) {
|
|
239
|
-
if (Math.floor(num) === num || String(num).indexOf(
|
|
240
|
-
const parts = num.toString().split(
|
|
278
|
+
export function countDecimals(num: number): number {
|
|
279
|
+
if (Math.floor(num) === num || String(num).indexOf(".") < 0) return 0;
|
|
280
|
+
const parts = num.toString().split(".")[1];
|
|
241
281
|
return parts.length || 0;
|
|
242
282
|
}
|