fit_js 0.94.5 → 0.94.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/fit_js.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "fitjs/rails/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "fit_js"
7
+ s.version = Fitjs::Rails::VERSION
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
+ # Specify which files should be added to the gem when it is released.
17
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
+ s.files = Dir.chdir(File.expand_path('..', __FILE__)) do
19
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ end
21
+ s.bindir = "exe"
22
+ s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ s.require_paths = ["lib"]
24
+
25
+ s.add_development_dependency "bundler", "~> 1.16"
26
+ s.add_development_dependency "rake", "~> 10.0"
27
+ end
@@ -0,0 +1,5 @@
1
+ module Fitjs
2
+ module Rails
3
+ VERSION = "0.94.7"
4
+ end
5
+ end
data/sig/fitjs.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Fitjs
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata CHANGED
@@ -1,42 +1,77 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fit_js
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.94.5
4
+ version: 0.94.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Dunn
8
8
  autorequire:
9
- bindir: bin
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: fit_js
14
+ name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.94.5
20
- - - ">="
21
- - !ruby/object:Gem::Version
22
- version: 0.94.5
23
- type: :runtime
19
+ version: '1.16'
20
+ type: :development
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
24
  - - "~>"
28
25
  - !ruby/object:Gem::Version
29
- version: 0.94.5
30
- - - ">="
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
+ - - "~>"
31
39
  - !ruby/object:Gem::Version
32
- version: 0.94.5
40
+ version: '10.0'
33
41
  description: The javascript library for the Garmin FIT file format
34
42
  email: axeouse@gmail.com
35
43
  executables: []
36
44
  extensions: []
37
45
  extra_rdoc_files: []
38
46
  files:
47
+ - ".DS_Store"
48
+ - CHANGELOG.md
49
+ - CODE_OF_CONDUCT.md
50
+ - Gemfile
51
+ - Gemfile.lock
52
+ - LICENSE.txt
53
+ - README.md
54
+ - Rakefile
55
+ - app/.DS_Store
56
+ - app/assets/.DS_Store
57
+ - app/assets/javascripts/accumulator.js
58
+ - app/assets/javascripts/bit-stream.js
59
+ - app/assets/javascripts/crc-calculator.js
60
+ - app/assets/javascripts/decoder.js
61
+ - app/assets/javascripts/fit.js
62
+ - app/assets/javascripts/fitjs.js
63
+ - app/assets/javascripts/index.js
64
+ - app/assets/javascripts/profile.js
65
+ - app/assets/javascripts/stream.js
66
+ - app/assets/javascripts/utils-hr-mesg.js
67
+ - app/assets/javascripts/utils-internal.js
68
+ - app/assets/javascripts/utils.js
69
+ - bin/console
70
+ - bin/setup
71
+ - fit_js.gemspec
39
72
  - lib/fitjs.rb
73
+ - lib/fitjs/rails/version.rb
74
+ - sig/fitjs.rbs
40
75
  homepage: https://rubygems.org/gems/fit_js
41
76
  licenses:
42
77
  - MIT