fit_js 0.94.6 → 0.94.9
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.
- checksums.yaml +4 -4
- data/.DS_Store +0 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +16 -0
- data/LICENSE.txt +21 -0
- data/README.md +37 -0
- data/Rakefile +4 -0
- data/app/.DS_Store +0 -0
- data/app/assets/.DS_Store +0 -0
- data/app/assets/javascripts/accumulator.js +48 -0
- data/app/assets/javascripts/bit-stream.js +85 -0
- data/app/assets/javascripts/crc-calculator.js +56 -0
- data/app/assets/javascripts/decoder.js +737 -0
- data/app/assets/javascripts/fit.js +95 -0
- data/app/assets/javascripts/fitjs.js +11 -0
- data/app/assets/javascripts/index.js +18 -0
- data/app/assets/javascripts/profile.js +21386 -0
- data/app/assets/javascripts/stream.js +250 -0
- data/app/assets/javascripts/utils-hr-mesg.js +175 -0
- data/app/assets/javascripts/utils-internal.js +35 -0
- data/app/assets/javascripts/utils.js +31 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/fit_js.gemspec +26 -0
- data/lib/fitjs/rails/version.rb +5 -0
- data/sig/fitjs.rbs +4 -0
- metadata +62 -7
@@ -0,0 +1,250 @@
|
|
1
|
+
/////////////////////////////////////////////////////////////////////////////////////////////
|
2
|
+
// Copyright 2022 Garmin International, Inc.
|
3
|
+
// Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you
|
4
|
+
// may not use this file except in compliance with the Flexible and Interoperable Data
|
5
|
+
// Transfer (FIT) Protocol License.
|
6
|
+
/////////////////////////////////////////////////////////////////////////////////////////////
|
7
|
+
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
|
8
|
+
// Profile Version = 21.94Release
|
9
|
+
// Tag = production/akw/21.94.00-0-g0f668193
|
10
|
+
/////////////////////////////////////////////////////////////////////////////////////////////
|
11
|
+
|
12
|
+
|
13
|
+
import FIT from "./fit.js";
|
14
|
+
import UtilsInternal from "./utils-internal.js";
|
15
|
+
|
16
|
+
class Stream {
|
17
|
+
static LITTLE_ENDIAN = true;
|
18
|
+
static BIG_ENDIAN = false;
|
19
|
+
|
20
|
+
#position = 0;
|
21
|
+
#arrayBuffer = null;
|
22
|
+
#textDecoder = new TextDecoder("utf-8", { fatal: false, ignoreBOM: true });
|
23
|
+
#crcCalculator = null;
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Convenience method for creating a Stream from a byte array
|
27
|
+
* @param {Array<number>} data An array of bytes
|
28
|
+
* @returns {Stream} A new Stream object
|
29
|
+
* @static
|
30
|
+
*/
|
31
|
+
static fromByteArray(data) {
|
32
|
+
const buf = new Uint8Array(data);
|
33
|
+
return this.fromArrayBuffer(buf.buffer);
|
34
|
+
}
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Convenience method for creating a Stream from a Node Buffer
|
38
|
+
* @param {Buffer} buffer - Node Buffer of bytes
|
39
|
+
* @returns {Stream} A new Stream object
|
40
|
+
* @static
|
41
|
+
*/
|
42
|
+
static fromBuffer(buffer) {
|
43
|
+
const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
44
|
+
return this.fromArrayBuffer(arrayBuffer);
|
45
|
+
}
|
46
|
+
|
47
|
+
/**
|
48
|
+
* Convenience method for creating a Stream from an ArrayBuffer
|
49
|
+
* @param {ArrayBuffer} arrayBuffer - An ArrayBuffer of bytes
|
50
|
+
* @returns {Stream} A new Stream object
|
51
|
+
* @static
|
52
|
+
*/
|
53
|
+
static fromArrayBuffer(arrayBuffer) {
|
54
|
+
const stream = new Stream(arrayBuffer);
|
55
|
+
return stream;
|
56
|
+
}
|
57
|
+
|
58
|
+
/**
|
59
|
+
* Creates a Stream containing a FIT file
|
60
|
+
* @constructor
|
61
|
+
* @param {ArrayBuffer} stream - ArrayBuffer containing a FIT file
|
62
|
+
*/
|
63
|
+
constructor(arrayBuffer) {
|
64
|
+
this.#position = 0;
|
65
|
+
this.#arrayBuffer = arrayBuffer;
|
66
|
+
}
|
67
|
+
|
68
|
+
get length() {
|
69
|
+
return this.#arrayBuffer.byteLength;
|
70
|
+
}
|
71
|
+
|
72
|
+
get bytesRead() {
|
73
|
+
return this.#position;
|
74
|
+
}
|
75
|
+
|
76
|
+
get position() {
|
77
|
+
return this.#position;
|
78
|
+
}
|
79
|
+
|
80
|
+
get crcCalculator() {
|
81
|
+
return this.#crcCalculator;
|
82
|
+
}
|
83
|
+
|
84
|
+
set crcCalculator(crcCalculator) {
|
85
|
+
this.#crcCalculator = crcCalculator;
|
86
|
+
}
|
87
|
+
|
88
|
+
reset() {
|
89
|
+
this.seek(0);
|
90
|
+
}
|
91
|
+
|
92
|
+
seek(position) {
|
93
|
+
this.#position = position;
|
94
|
+
}
|
95
|
+
|
96
|
+
slice(begin, end) {
|
97
|
+
return this.#arrayBuffer.slice(begin, end);
|
98
|
+
}
|
99
|
+
|
100
|
+
peekByte() {
|
101
|
+
const arrayBuffer = this.#arrayBuffer.slice(this.#position, this.#position + 1);
|
102
|
+
const dataView = new DataView(arrayBuffer);
|
103
|
+
return dataView.getUint8(0);
|
104
|
+
}
|
105
|
+
|
106
|
+
readByte() {
|
107
|
+
return this.readUInt8();
|
108
|
+
}
|
109
|
+
|
110
|
+
readBytes(size) {
|
111
|
+
if (this.#position + size > this.#arrayBuffer.byteLength) {
|
112
|
+
throw Error(`FIT Runtime Error end of stream at byte ${this.#position}`);
|
113
|
+
}
|
114
|
+
|
115
|
+
const bytes = this.#arrayBuffer.slice(this.#position, this.#position + size);
|
116
|
+
this.#position += size;
|
117
|
+
|
118
|
+
this.#crcCalculator?.addBytes(new Uint8Array(bytes), 0, size);
|
119
|
+
|
120
|
+
return bytes;
|
121
|
+
}
|
122
|
+
|
123
|
+
readUInt8() {
|
124
|
+
return this.readValue(FIT.BaseType.UINT8, 1);
|
125
|
+
}
|
126
|
+
|
127
|
+
readInt8() {
|
128
|
+
return this.readValue(FIT.BaseType.SINT8, 1);
|
129
|
+
}
|
130
|
+
|
131
|
+
readUInt16(opts) {
|
132
|
+
return this.readValue(FIT.BaseType.UINT16, 2, { convertInvalidToNull: false, ...opts });
|
133
|
+
}
|
134
|
+
|
135
|
+
readInt16(opts) {
|
136
|
+
return this.readValue(FIT.BaseType.SINT16, 2, { convertInvalidToNull: false, ...opts });
|
137
|
+
}
|
138
|
+
|
139
|
+
readUInt32(opts) {
|
140
|
+
return this.readValue(FIT.BaseType.UINT32, 4, { convertInvalidToNull: false, ...opts });
|
141
|
+
}
|
142
|
+
|
143
|
+
readInt32(opts) {
|
144
|
+
return this.readValue(FIT.BaseType.SINT32, 4, { convertInvalidToNull: false, ...opts });
|
145
|
+
}
|
146
|
+
|
147
|
+
readUInt64(opts) {
|
148
|
+
return this.readValue(FIT.BaseType.UINT64, 8, { convertInvalidToNull: false, ...opts });
|
149
|
+
}
|
150
|
+
|
151
|
+
readInt64(opts) {
|
152
|
+
return this.readValue(FIT.BaseType.SINT64, 8, { convertInvalidToNull: false, ...opts });
|
153
|
+
}
|
154
|
+
|
155
|
+
readFloat32(opts) {
|
156
|
+
return this.readValue(FIT.BaseType.FLOAT32, 4, { convertInvalidToNull: false, ...opts });
|
157
|
+
}
|
158
|
+
|
159
|
+
readFloat64(opts) {
|
160
|
+
return this.readValue(FIT.BaseType.FLOAT64, 8, { convertInvalidToNull: false, ...opts });
|
161
|
+
}
|
162
|
+
|
163
|
+
readString(strlen) {
|
164
|
+
return this.readValue(FIT.BaseType.STRING, strlen);
|
165
|
+
}
|
166
|
+
|
167
|
+
readValue(baseType, size, { endianness = Stream.LITTLE_ENDIAN, convertInvalidToNull = true } = {}) {
|
168
|
+
const baseTypeSize = FIT.BaseTypeDefinitions[baseType].size;
|
169
|
+
const baseTypeInvalid = FIT.BaseTypeDefinitions[baseType].invalid;
|
170
|
+
|
171
|
+
const arrayBuffer = this.readBytes(size);
|
172
|
+
const count = size / baseTypeSize;
|
173
|
+
|
174
|
+
if (baseType === FIT.BaseType.STRING) {
|
175
|
+
const string = this.#textDecoder.decode(arrayBuffer).replace(/\uFFFD/g, "");
|
176
|
+
const strings = string.split('\0');
|
177
|
+
|
178
|
+
while (strings[strings.length - 1] === "") {
|
179
|
+
strings.pop();
|
180
|
+
}
|
181
|
+
|
182
|
+
if (strings.length === 0) {
|
183
|
+
return null;
|
184
|
+
}
|
185
|
+
|
186
|
+
return strings.length === 1 ? strings[0] : strings;
|
187
|
+
}
|
188
|
+
|
189
|
+
const dataView = new DataView(arrayBuffer);
|
190
|
+
let values = [];
|
191
|
+
|
192
|
+
for (let i = 0; i < count; i++) {
|
193
|
+
|
194
|
+
switch (baseType) {
|
195
|
+
case FIT.BaseType.BYTE:
|
196
|
+
case FIT.BaseType.ENUM:
|
197
|
+
case FIT.BaseType.UINT8:
|
198
|
+
case FIT.BaseType.UINT8Z:
|
199
|
+
values.push(dataView.getUint8(i * baseTypeSize));
|
200
|
+
break;
|
201
|
+
|
202
|
+
case FIT.BaseType.SINT8:
|
203
|
+
values.push(dataView.getInt8(i * baseTypeSize));
|
204
|
+
break;
|
205
|
+
|
206
|
+
case FIT.BaseType.UINT16:
|
207
|
+
case FIT.BaseType.UINT16Z:
|
208
|
+
values.push(dataView.getUint16(i * baseTypeSize, endianness));
|
209
|
+
break;
|
210
|
+
|
211
|
+
case FIT.BaseType.SINT16:
|
212
|
+
values.push(dataView.getInt16(i * baseTypeSize, endianness));
|
213
|
+
break;
|
214
|
+
|
215
|
+
case FIT.BaseType.UINT32:
|
216
|
+
case FIT.BaseType.UINT32Z:
|
217
|
+
values.push(dataView.getUint32(i * baseTypeSize, endianness));
|
218
|
+
break;
|
219
|
+
|
220
|
+
case FIT.BaseType.SINT32:
|
221
|
+
values.push(dataView.getInt32(i * baseTypeSize, endianness));
|
222
|
+
break;
|
223
|
+
|
224
|
+
case FIT.BaseType.UINT64:
|
225
|
+
case FIT.BaseType.UINT64Z:
|
226
|
+
values.push(dataView.getBigUint64(i * baseTypeSize, endianness));
|
227
|
+
break;
|
228
|
+
case FIT.BaseType.SINT64:
|
229
|
+
values.push(dataView.getBigInt64(i * baseTypeSize, endianness));
|
230
|
+
break;
|
231
|
+
|
232
|
+
case FIT.BaseType.FLOAT32:
|
233
|
+
values.push(dataView.getFloat32(i * baseTypeSize, endianness));
|
234
|
+
break;
|
235
|
+
|
236
|
+
case FIT.BaseType.FLOAT64:
|
237
|
+
values.push(dataView.getFloat64(i * baseTypeSize, endianness));
|
238
|
+
break;
|
239
|
+
}
|
240
|
+
}
|
241
|
+
|
242
|
+
if (convertInvalidToNull) {
|
243
|
+
values = values.map(value => value === baseTypeInvalid ? null : value);
|
244
|
+
}
|
245
|
+
|
246
|
+
return UtilsInternal.sanitizeValues(values);
|
247
|
+
}
|
248
|
+
}
|
249
|
+
|
250
|
+
export default Stream;
|
@@ -0,0 +1,175 @@
|
|
1
|
+
/////////////////////////////////////////////////////////////////////////////////////////////
|
2
|
+
// Copyright 2022 Garmin International, Inc.
|
3
|
+
// Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you
|
4
|
+
// may not use this file except in compliance with the Flexible and Interoperable Data
|
5
|
+
// Transfer (FIT) Protocol License.
|
6
|
+
/////////////////////////////////////////////////////////////////////////////////////////////
|
7
|
+
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
|
8
|
+
// Profile Version = 21.94Release
|
9
|
+
// Tag = production/akw/21.94.00-0-g0f668193
|
10
|
+
/////////////////////////////////////////////////////////////////////////////////////////////
|
11
|
+
|
12
|
+
|
13
|
+
import Utils from "./utils.js";
|
14
|
+
|
15
|
+
const mergeHeartRates = (hrMesgs, recordMesgs) => {
|
16
|
+
|
17
|
+
if (hrMesgs == null || recordMesgs == null ||
|
18
|
+
hrMesgs.length == 0 || recordMesgs.length == 0) {
|
19
|
+
return;
|
20
|
+
}
|
21
|
+
|
22
|
+
const heartrates = expandHeartRates(hrMesgs);
|
23
|
+
|
24
|
+
let heartrateIndex = 0;
|
25
|
+
let recordRangeStartTime = null;
|
26
|
+
|
27
|
+
for (let i = 0; i < recordMesgs.length; ++i) {
|
28
|
+
const recordMesg = recordMesgs[i];
|
29
|
+
|
30
|
+
let hrSum = 0;
|
31
|
+
let hrSumCount = 0;
|
32
|
+
|
33
|
+
const recordRangeEndTime = secondsSinceFitEpoch(recordMesg.timestamp);
|
34
|
+
|
35
|
+
if (recordRangeStartTime == null) {
|
36
|
+
recordRangeStartTime = recordRangeEndTime;
|
37
|
+
}
|
38
|
+
|
39
|
+
if (recordRangeStartTime === recordRangeEndTime) {
|
40
|
+
recordRangeStartTime--;
|
41
|
+
heartrateIndex = (heartrateIndex >= 1) ? heartrateIndex - 1 : 0;
|
42
|
+
}
|
43
|
+
|
44
|
+
let findingInRangeHrMesgs = true;
|
45
|
+
while (findingInRangeHrMesgs && (heartrateIndex < heartrates.length)) {
|
46
|
+
|
47
|
+
const heartrate = heartrates[heartrateIndex];
|
48
|
+
|
49
|
+
// Check if the heartrate timestamp is gt record start time
|
50
|
+
// and if the heartrate timestamp is lte to record end time
|
51
|
+
if (heartrate.timestamp > recordRangeStartTime
|
52
|
+
&& heartrate.timestamp <= recordRangeEndTime) {
|
53
|
+
hrSum += heartrate.heartRate;
|
54
|
+
hrSumCount++;
|
55
|
+
}
|
56
|
+
// Check if the heartrate timestamp exceeds the record time
|
57
|
+
else if (heartrate.timestamp > recordRangeEndTime) {
|
58
|
+
findingInRangeHrMesgs = false;
|
59
|
+
|
60
|
+
if (hrSumCount > 0) {
|
61
|
+
// Update record's heart rate value
|
62
|
+
const avgHR = Math.round(hrSum / hrSumCount);
|
63
|
+
recordMesg.heartRate = avgHR;
|
64
|
+
|
65
|
+
}
|
66
|
+
// Reset HR average accumulators
|
67
|
+
hrSum = 0;
|
68
|
+
hrSumCount = 0;
|
69
|
+
|
70
|
+
recordRangeStartTime = recordRangeEndTime;
|
71
|
+
|
72
|
+
// Breaks out of findingInRangeHrMesgs while loop w/o incrementing heartrateIndex
|
73
|
+
break;
|
74
|
+
}
|
75
|
+
|
76
|
+
heartrateIndex++;
|
77
|
+
}
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
const expandHeartRates = (hrMesgs) => {
|
82
|
+
const GAP_INCREMENT_MILLISECONDS = 250;
|
83
|
+
const GAP_INCREMENT_SECONDS = GAP_INCREMENT_MILLISECONDS / 1000.0;
|
84
|
+
const GAP_MAX_MILLISECONDS = 5000;
|
85
|
+
const GAP_MAX_STEPS = GAP_MAX_MILLISECONDS / GAP_INCREMENT_MILLISECONDS;
|
86
|
+
|
87
|
+
if (hrMesgs == null || hrMesgs.length == 0) {
|
88
|
+
return [];
|
89
|
+
}
|
90
|
+
|
91
|
+
let anchorEventTimestamp = 0.0;
|
92
|
+
let anchorTimestamp = null;
|
93
|
+
|
94
|
+
const heartrates = [];
|
95
|
+
hrMesgs.forEach(hrMesg => {
|
96
|
+
if (hrMesg == null) {
|
97
|
+
throwError("HR mesg must not be null");
|
98
|
+
}
|
99
|
+
|
100
|
+
const eventTimestamps = Array.isArray(hrMesg.eventTimestamp) ? hrMesg.eventTimestamp : [hrMesg.eventTimestamp];
|
101
|
+
const filteredBpms = Array.isArray(hrMesg.filteredBpm) ? hrMesg.filteredBpm : [hrMesg.filteredBpm];
|
102
|
+
|
103
|
+
// Update HR timestamp anchor, if present
|
104
|
+
if (hrMesg.timestamp != null) {
|
105
|
+
anchorTimestamp = secondsSinceFitEpoch(hrMesg.timestamp);
|
106
|
+
|
107
|
+
if (hrMesg.fractionalTimestamp != null) {
|
108
|
+
anchorTimestamp += hrMesg.fractionalTimestamp;
|
109
|
+
}
|
110
|
+
|
111
|
+
if (eventTimestamps.length == 1) {
|
112
|
+
anchorEventTimestamp = eventTimestamps[0];
|
113
|
+
} else {
|
114
|
+
throwError("anchor HR mesg must have 1 event_timestamp");
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
if (anchorTimestamp == null || anchorEventTimestamp == null) {
|
119
|
+
// We cannot process any HR messages if we have not received a timestamp anchor
|
120
|
+
throwError("no anchor timestamp received in a HR mesg before delta HR mesgs");
|
121
|
+
} else if (eventTimestamps.length != filteredBpms.length) {
|
122
|
+
throwError("HR mesg with mismatching event timestamp and filtered bpm");
|
123
|
+
}
|
124
|
+
|
125
|
+
for (let i = 0; i < eventTimestamps.length; i++) {
|
126
|
+
let eventTimestamp = eventTimestamps[i];
|
127
|
+
|
128
|
+
// Check to see if the event timestamp rolled over
|
129
|
+
if (eventTimestamp < anchorEventTimestamp) {
|
130
|
+
if ((anchorEventTimestamp - eventTimestamp) > (0x400000)) {
|
131
|
+
eventTimestamp += (0x400000);
|
132
|
+
} else {
|
133
|
+
throwError("anchor event_timestamp is greater than subsequent event_timestamp. This does not allow for correct delta calculation.");
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
const currentHr = { timestamp: anchorTimestamp, heartRate: filteredBpms[i] };
|
138
|
+
currentHr.timestamp += (eventTimestamp - anchorEventTimestamp);
|
139
|
+
|
140
|
+
// Carry the previous HR value forward across the gap to the current
|
141
|
+
// HR value for up to 5 Seconds (5000ms) in 250ms increments
|
142
|
+
if (heartrates.length > 0) {
|
143
|
+
const previousHR = heartrates[heartrates.length - 1];
|
144
|
+
let gapInMilliseconds = Math.abs(currentHr.timestamp - previousHR.timestamp) * 1000;
|
145
|
+
let step = 1;
|
146
|
+
while (gapInMilliseconds > GAP_INCREMENT_MILLISECONDS && step <= GAP_MAX_STEPS) {
|
147
|
+
const gapHR = { timestamp: previousHR.timestamp, heartRate: previousHR.heartRate };
|
148
|
+
gapHR.timestamp += (GAP_INCREMENT_SECONDS * step);
|
149
|
+
heartrates.push(gapHR);
|
150
|
+
|
151
|
+
gapInMilliseconds -= GAP_INCREMENT_MILLISECONDS;
|
152
|
+
step++;
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
heartrates.push(currentHr);
|
157
|
+
}
|
158
|
+
});
|
159
|
+
|
160
|
+
return heartrates;
|
161
|
+
}
|
162
|
+
|
163
|
+
const secondsSinceFitEpoch = (timestamp) => {
|
164
|
+
if (timestamp instanceof Date) {
|
165
|
+
return (timestamp.getTime() - Utils.FIT_EPOCH_MS) / 1000;
|
166
|
+
}
|
167
|
+
|
168
|
+
return timestamp;
|
169
|
+
}
|
170
|
+
|
171
|
+
const throwError = (error = "") => {
|
172
|
+
throw Error(`FIT Runtime Error ${error}`.trimEnd());
|
173
|
+
}
|
174
|
+
|
175
|
+
export default { mergeHeartRates, expandHeartRates };
|
@@ -0,0 +1,35 @@
|
|
1
|
+
/////////////////////////////////////////////////////////////////////////////////////////////
|
2
|
+
// Copyright 2022 Garmin International, Inc.
|
3
|
+
// Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you
|
4
|
+
// may not use this file except in compliance with the Flexible and Interoperable Data
|
5
|
+
// Transfer (FIT) Protocol License.
|
6
|
+
/////////////////////////////////////////////////////////////////////////////////////////////
|
7
|
+
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
|
8
|
+
// Profile Version = 21.94Release
|
9
|
+
// Tag = production/akw/21.94.00-0-g0f668193
|
10
|
+
/////////////////////////////////////////////////////////////////////////////////////////////
|
11
|
+
|
12
|
+
|
13
|
+
const sanitizeValues = (values) => {
|
14
|
+
if (onlyNullValues(values)) {
|
15
|
+
return null;
|
16
|
+
}
|
17
|
+
|
18
|
+
return values.length === 1 ? values[0] : values;
|
19
|
+
}
|
20
|
+
|
21
|
+
const onlyNullValues = (values) => values.reduce((state, value) => value != null ? false : state, true);
|
22
|
+
|
23
|
+
const onlyInvalidValues = (rawFieldValue, invalidValue) => {
|
24
|
+
if (Array.isArray(rawFieldValue)) {
|
25
|
+
return rawFieldValue.reduce((state, value) => value != invalidValue ? false : state, true);
|
26
|
+
}
|
27
|
+
|
28
|
+
return rawFieldValue === invalidValue;
|
29
|
+
}
|
30
|
+
|
31
|
+
export default {
|
32
|
+
sanitizeValues,
|
33
|
+
onlyNullValues,
|
34
|
+
onlyInvalidValues
|
35
|
+
};
|
@@ -0,0 +1,31 @@
|
|
1
|
+
/////////////////////////////////////////////////////////////////////////////////////////////
|
2
|
+
// Copyright 2022 Garmin International, Inc.
|
3
|
+
// Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you
|
4
|
+
// may not use this file except in compliance with the Flexible and Interoperable Data
|
5
|
+
// Transfer (FIT) Protocol License.
|
6
|
+
/////////////////////////////////////////////////////////////////////////////////////////////
|
7
|
+
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
|
8
|
+
// Profile Version = 21.94Release
|
9
|
+
// Tag = production/akw/21.94.00-0-g0f668193
|
10
|
+
/////////////////////////////////////////////////////////////////////////////////////////////
|
11
|
+
|
12
|
+
|
13
|
+
/**
|
14
|
+
* The millisecond offset between UNIX and FIT Epochs (631065600000).
|
15
|
+
* @const {number}
|
16
|
+
*/
|
17
|
+
const FIT_EPOCH_MS = 631065600000;
|
18
|
+
|
19
|
+
/**
|
20
|
+
* Convert a FIT DateTime to a JavaScript Date
|
21
|
+
* @param {number} datetime - Seconds since FIT EPOCH
|
22
|
+
* @returns {Date} A JavaScript Date object
|
23
|
+
*/
|
24
|
+
const convertDateTimeToDate = (datetime) => {
|
25
|
+
return new Date((datetime ?? 0) * 1000 + FIT_EPOCH_MS);
|
26
|
+
};
|
27
|
+
|
28
|
+
export default {
|
29
|
+
FIT_EPOCH_MS,
|
30
|
+
convertDateTimeToDate,
|
31
|
+
};
|
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "fitjs"
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require "irb"
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/fit_js.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "fit_js"
|
7
|
+
s.version = "0.94.9"
|
8
|
+
s.summary = "Javascript Wrapper for FIT Files"
|
9
|
+
s.description = "The javascript library for the Garmin FIT file format"
|
10
|
+
s.authors = ["Justin Dunn"]
|
11
|
+
s.email = "axeouse@gmail.com"
|
12
|
+
s.files = ["lib", "app/assets/javascript"]
|
13
|
+
s.homepage =
|
14
|
+
"https://rubygems.org/gems/fit_js"
|
15
|
+
s.license = "MIT"
|
16
|
+
s.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
17
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
end
|
19
|
+
s.bindir = "exe"
|
20
|
+
s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
|
23
|
+
s.add_development_dependency "bundler", "~> 1.16"
|
24
|
+
s.add_development_dependency "rake", "~> 10.0"
|
25
|
+
s.add_runtime_dependency 'autoprefixer-rails', '~> 9.1', '>= 9.1.0'
|
26
|
+
end
|
data/sig/fitjs.rbs
ADDED
metadata
CHANGED
@@ -1,42 +1,97 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fit_js
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.94.
|
4
|
+
version: 0.94.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Dunn
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
11
|
date: 2023-01-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: '1.16'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.16'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: autoprefixer-rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '9.1'
|
20
48
|
- - ">="
|
21
49
|
- !ruby/object:Gem::Version
|
22
|
-
version:
|
50
|
+
version: 9.1.0
|
23
51
|
type: :runtime
|
24
52
|
prerelease: false
|
25
53
|
version_requirements: !ruby/object:Gem::Requirement
|
26
54
|
requirements:
|
27
55
|
- - "~>"
|
28
56
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
57
|
+
version: '9.1'
|
30
58
|
- - ">="
|
31
59
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
60
|
+
version: 9.1.0
|
33
61
|
description: The javascript library for the Garmin FIT file format
|
34
62
|
email: axeouse@gmail.com
|
35
63
|
executables: []
|
36
64
|
extensions: []
|
37
65
|
extra_rdoc_files: []
|
38
66
|
files:
|
67
|
+
- ".DS_Store"
|
68
|
+
- CHANGELOG.md
|
69
|
+
- CODE_OF_CONDUCT.md
|
70
|
+
- Gemfile
|
71
|
+
- Gemfile.lock
|
72
|
+
- LICENSE.txt
|
73
|
+
- README.md
|
74
|
+
- Rakefile
|
75
|
+
- app/.DS_Store
|
76
|
+
- app/assets/.DS_Store
|
77
|
+
- app/assets/javascripts/accumulator.js
|
78
|
+
- app/assets/javascripts/bit-stream.js
|
79
|
+
- app/assets/javascripts/crc-calculator.js
|
80
|
+
- app/assets/javascripts/decoder.js
|
81
|
+
- app/assets/javascripts/fit.js
|
82
|
+
- app/assets/javascripts/fitjs.js
|
83
|
+
- app/assets/javascripts/index.js
|
84
|
+
- app/assets/javascripts/profile.js
|
85
|
+
- app/assets/javascripts/stream.js
|
86
|
+
- app/assets/javascripts/utils-hr-mesg.js
|
87
|
+
- app/assets/javascripts/utils-internal.js
|
88
|
+
- app/assets/javascripts/utils.js
|
89
|
+
- bin/console
|
90
|
+
- bin/setup
|
91
|
+
- fit_js.gemspec
|
39
92
|
- lib/fitjs.rb
|
93
|
+
- lib/fitjs/rails/version.rb
|
94
|
+
- sig/fitjs.rbs
|
40
95
|
homepage: https://rubygems.org/gems/fit_js
|
41
96
|
licenses:
|
42
97
|
- MIT
|