trackswap 0.0.1 → 0.0.2
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/dist/FIT/base.d.ts +91 -0
- package/dist/FIT/base.js +101 -0
- package/dist/FIT/crc.d.ts +1 -0
- package/dist/FIT/crc.js +16 -0
- package/dist/FIT/decoder.d.ts +83 -0
- package/dist/FIT/decoder.js +323 -0
- package/dist/FIT/encoder.d.ts +142 -0
- package/dist/FIT/encoder.js +445 -0
- package/dist/FIT/index.d.ts +12 -0
- package/dist/FIT/index.js +13 -0
- package/dist/FIT/mesg.d.ts +40 -0
- package/dist/FIT/mesg.js +208 -0
- package/dist/FIT/middleware.d.ts +48 -0
- package/dist/FIT/middleware.js +206 -0
- package/dist/FIT/plugins.d.ts +26 -0
- package/dist/FIT/plugins.js +104 -0
- package/dist/FIT/processor.d.ts +58 -0
- package/dist/FIT/processor.js +170 -0
- package/dist/FIT/types/course_point.d.ts +56 -0
- package/dist/FIT/types/course_point.js +58 -0
- package/dist/FIT/types/event.d.ts +49 -0
- package/dist/FIT/types/event.js +50 -0
- package/dist/FIT/types/event_type.d.ts +18 -0
- package/dist/FIT/types/event_type.js +19 -0
- package/dist/FIT/types/file.d.ts +23 -0
- package/dist/FIT/types/file.js +24 -0
- package/dist/FIT/types/index.d.ts +119 -0
- package/dist/FIT/types/index.js +297 -0
- package/dist/FIT/types/mesg_num.d.ts +105 -0
- package/dist/FIT/types/mesg_num.js +106 -0
- package/dist/FIT/types/sport.d.ts +47 -0
- package/dist/FIT/types/sport.js +48 -0
- package/dist/FIT/types.d.ts +231 -0
- package/dist/FIT/types.js +1 -0
- package/dist/FIT/util.d.ts +1 -0
- package/dist/FIT/util.js +7 -0
- package/dist/GPX/base.d.ts +43 -67
- package/dist/GPX/base.js +40 -67
- package/dist/GPX/converters.d.ts +12 -36
- package/dist/GPX/converters.js +11 -103
- package/dist/GPX/decoder.d.ts +5 -5
- package/dist/GPX/decoder.js +6 -0
- package/dist/GPX/encoder.d.ts +4 -0
- package/dist/GPX/encoder.js +3 -0
- package/dist/GPX/index.d.ts +1 -1
- package/dist/GPX/index.js +1 -1
- package/dist/GPX/middleware.d.ts +27 -0
- package/dist/GPX/middleware.js +93 -0
- package/dist/GPX/processor.d.ts +1 -1
- package/dist/GPX/processor.js +12 -11
- package/dist/GPX/types.d.ts +2 -3
- package/dist/TCX/base.d.ts +52 -0
- package/dist/TCX/base.js +62 -0
- package/dist/TCX/converters.d.ts +75 -0
- package/dist/TCX/converters.js +333 -0
- package/dist/TCX/decoder.d.ts +87 -0
- package/dist/TCX/decoder.js +350 -0
- package/dist/TCX/encoder.d.ts +150 -0
- package/dist/TCX/encoder.js +513 -0
- package/dist/TCX/index.d.ts +10 -0
- package/dist/TCX/index.js +9 -0
- package/dist/TCX/middleware.d.ts +27 -0
- package/dist/TCX/middleware.js +93 -0
- package/dist/TCX/processor.d.ts +48 -0
- package/dist/TCX/processor.js +197 -0
- package/dist/TCX/types.d.ts +203 -0
- package/dist/TCX/types.js +1 -0
- package/dist/core/base.d.ts +119 -0
- package/dist/core/base.js +138 -0
- package/dist/core/decoder.d.ts +66 -0
- package/dist/core/decoder.js +90 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +4 -0
- package/dist/core/processor.d.ts +69 -0
- package/dist/core/processor.js +265 -0
- package/dist/core/xml-utils.d.ts +41 -0
- package/dist/core/xml-utils.js +140 -0
- package/package.json +8 -5
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { PositionConverter, TrackpointConverter, TrackConverter, ActivityLapConverter, ActivityConverter, ActivityListConverter, MultiSportSessionConverter, AbstractSourceConverter, } from "./converters.js";
|
|
2
|
+
import { AstGenerateProcessor, CompleteProcessor, ConvertProcessor, TokenizeProcessor, PipelineStage, } from "./processor.js";
|
|
3
|
+
/**
|
|
4
|
+
* https://www8.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd
|
|
5
|
+
*/
|
|
6
|
+
export class TCXDecoder {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.converterPlugins = new Map();
|
|
9
|
+
this.registeredPlugins = new Set(); // 跟踪已注册的插件名
|
|
10
|
+
this.middlewarePlugins = [];
|
|
11
|
+
this.processors = []; // 固定的核心处理器
|
|
12
|
+
this.initialized = false;
|
|
13
|
+
this.defaultConvertersRegistered = false;
|
|
14
|
+
this.initializePipeline();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 初始化流水线 - 注册固定的核心处理器
|
|
18
|
+
*/
|
|
19
|
+
initializePipeline() {
|
|
20
|
+
// 固定的核心处理器,不允许用户修改
|
|
21
|
+
this.processors = [
|
|
22
|
+
new TokenizeProcessor(),
|
|
23
|
+
new AstGenerateProcessor(),
|
|
24
|
+
new ConvertProcessor((tag) => this.getConverter(tag)),
|
|
25
|
+
new CompleteProcessor(),
|
|
26
|
+
];
|
|
27
|
+
console.log("🔧 TCX核心流水线处理器已初始化 (固定不可修改)");
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 自动注册默认转换器 - 处理标准TCX标签
|
|
31
|
+
*/
|
|
32
|
+
async registerDefaultConverters() {
|
|
33
|
+
if (this.defaultConvertersRegistered)
|
|
34
|
+
return;
|
|
35
|
+
const defaultConverters = [
|
|
36
|
+
new PositionConverter(),
|
|
37
|
+
new TrackpointConverter(),
|
|
38
|
+
new TrackConverter(),
|
|
39
|
+
new ActivityLapConverter(),
|
|
40
|
+
new ActivityConverter(),
|
|
41
|
+
new ActivityListConverter(),
|
|
42
|
+
new MultiSportSessionConverter(),
|
|
43
|
+
new AbstractSourceConverter(),
|
|
44
|
+
];
|
|
45
|
+
for (const converter of defaultConverters) {
|
|
46
|
+
// 内部注册,不显示日志避免干扰
|
|
47
|
+
converter.supportedTags?.forEach((tag) => {
|
|
48
|
+
this.addConverterForTag(tag, converter);
|
|
49
|
+
});
|
|
50
|
+
this.registeredPlugins.add(converter.name);
|
|
51
|
+
}
|
|
52
|
+
this.defaultConvertersRegistered = true;
|
|
53
|
+
console.log("✅ 默认TCX转换器已自动注册 (Activities, Position, Track等)");
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 为指定标签添加转换器,按优先级排序
|
|
57
|
+
*/
|
|
58
|
+
addConverterForTag(tag, converter) {
|
|
59
|
+
if (!this.converterPlugins.has(tag)) {
|
|
60
|
+
this.converterPlugins.set(tag, []);
|
|
61
|
+
}
|
|
62
|
+
const converters = this.converterPlugins.get(tag);
|
|
63
|
+
converters.push(converter);
|
|
64
|
+
// 按优先级排序(数字越小优先级越高)
|
|
65
|
+
converters.sort((a, b) => (a.priority || 100) - (b.priority || 100));
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 注册转换器插件
|
|
69
|
+
*/
|
|
70
|
+
async registerConverter(plugin) {
|
|
71
|
+
// 检查插件是否已注册
|
|
72
|
+
if (this.registeredPlugins.has(plugin.name)) {
|
|
73
|
+
throw new Error(`转换器插件 ${plugin.name} 已存在`);
|
|
74
|
+
}
|
|
75
|
+
// 注册支持的标签(允许多个转换器处理同一标签)
|
|
76
|
+
plugin.supportedTags?.forEach((tag) => {
|
|
77
|
+
this.addConverterForTag(tag, plugin);
|
|
78
|
+
});
|
|
79
|
+
this.registeredPlugins.add(plugin.name);
|
|
80
|
+
// 显示优先级信息
|
|
81
|
+
const priorityInfo = plugin.priority !== undefined ? ` (优先级: ${plugin.priority})` : "";
|
|
82
|
+
console.log(`📦 已注册自定义TCX转换器: ${plugin.name}${priorityInfo}, 支持标签: ${plugin.supportedTags?.join(", ")}`);
|
|
83
|
+
// 显示标签的转换器优先级顺序
|
|
84
|
+
plugin.supportedTags?.forEach((tag) => {
|
|
85
|
+
const converters = this.converterPlugins.get(tag) || [];
|
|
86
|
+
const converterNames = converters.map((c) => `${c.name}(${c.priority || 100})`);
|
|
87
|
+
console.log(` 📋 标签 "${tag}" 转换器优先级: ${converterNames.join(" > ")}`);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* 注册中间件插件 - 真正的扩展点
|
|
92
|
+
*/
|
|
93
|
+
async registerMiddleware(plugin) {
|
|
94
|
+
const existingIndex = this.middlewarePlugins.findIndex((p) => p.name === plugin.name);
|
|
95
|
+
if (existingIndex !== -1) {
|
|
96
|
+
this.middlewarePlugins[existingIndex] = plugin;
|
|
97
|
+
console.log(`🔄 已更新TCX中间件: ${plugin.name}`);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
this.middlewarePlugins.push(plugin);
|
|
101
|
+
console.log(`🔌 已注册TCX中间件: ${plugin.name}`);
|
|
102
|
+
}
|
|
103
|
+
// 显示中间件支持的钩子
|
|
104
|
+
const hooks = [];
|
|
105
|
+
if (plugin.onTokenize)
|
|
106
|
+
hooks.push("onTokenize");
|
|
107
|
+
if (plugin.onAstGenerate)
|
|
108
|
+
hooks.push("onAstGenerate");
|
|
109
|
+
if (plugin.onConvert)
|
|
110
|
+
hooks.push("onConvert");
|
|
111
|
+
if (plugin.onComplete)
|
|
112
|
+
hooks.push("onComplete");
|
|
113
|
+
if (plugin.onError)
|
|
114
|
+
hooks.push("onError");
|
|
115
|
+
if (hooks.length > 0) {
|
|
116
|
+
console.log(` 🎣 支持的钩子: ${hooks.join(", ")}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* 获取转换器(返回优先级最高的)
|
|
121
|
+
*/
|
|
122
|
+
getConverter(tag) {
|
|
123
|
+
const converters = this.converterPlugins.get(tag);
|
|
124
|
+
return converters && converters.length > 0 ? converters[0] : undefined;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* 获取指定标签的所有转换器(按优先级排序)
|
|
128
|
+
*/
|
|
129
|
+
getAllConverters(tag) {
|
|
130
|
+
return this.converterPlugins.get(tag) || [];
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* 移除转换器
|
|
134
|
+
*/
|
|
135
|
+
unregisterConverter(pluginName) {
|
|
136
|
+
// 找到插件实例
|
|
137
|
+
let pluginToRemove;
|
|
138
|
+
for (const [tag, converters] of this.converterPlugins) {
|
|
139
|
+
const index = converters.findIndex((plugin) => plugin.name === pluginName);
|
|
140
|
+
if (index !== -1) {
|
|
141
|
+
pluginToRemove = converters[index];
|
|
142
|
+
converters.splice(index, 1);
|
|
143
|
+
// 如果该标签没有转换器了,删除整个条目
|
|
144
|
+
if (converters.length === 0) {
|
|
145
|
+
this.converterPlugins.delete(tag);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (!pluginToRemove) {
|
|
150
|
+
console.warn(`TCX转换器插件 ${pluginName} 不存在`);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
this.registeredPlugins.delete(pluginName);
|
|
154
|
+
console.log(`🗑️ 已移除TCX转换器: ${pluginName}`);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* 移除中间件
|
|
158
|
+
*/
|
|
159
|
+
unregisterMiddleware(name) {
|
|
160
|
+
const index = this.middlewarePlugins.findIndex((p) => p.name === name);
|
|
161
|
+
if (index !== -1) {
|
|
162
|
+
this.middlewarePlugins.splice(index, 1);
|
|
163
|
+
console.log(`🗑️ 已移除TCX中间件: ${name}`);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
console.warn(`TCX中间件 ${name} 不存在`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 初始化解码器
|
|
171
|
+
*/
|
|
172
|
+
async initialize() {
|
|
173
|
+
if (this.initialized)
|
|
174
|
+
return;
|
|
175
|
+
// 首先注册默认转换器
|
|
176
|
+
await this.registerDefaultConverters();
|
|
177
|
+
const context = this.createContext();
|
|
178
|
+
// 初始化所有插件
|
|
179
|
+
const allPlugins = [
|
|
180
|
+
...Array.from(this.converterPlugins.values()).flat(),
|
|
181
|
+
...this.middlewarePlugins,
|
|
182
|
+
];
|
|
183
|
+
for (const plugin of allPlugins) {
|
|
184
|
+
try {
|
|
185
|
+
await plugin.initialize?.(context);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.error(`TCX插件 ${plugin.name} 初始化失败:`, error);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
this.initialized = true;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* 销毁解码器
|
|
195
|
+
*/
|
|
196
|
+
async destroy() {
|
|
197
|
+
if (!this.initialized)
|
|
198
|
+
return;
|
|
199
|
+
const context = this.createContext();
|
|
200
|
+
// 销毁所有插件
|
|
201
|
+
const allPlugins = [
|
|
202
|
+
...Array.from(this.converterPlugins.values()).flat(),
|
|
203
|
+
...this.middlewarePlugins,
|
|
204
|
+
];
|
|
205
|
+
for (const plugin of allPlugins) {
|
|
206
|
+
try {
|
|
207
|
+
await plugin.destroy?.(context);
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
console.error(`TCX插件 ${plugin.name} 销毁失败:`, error);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
this.initialized = false;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* 创建解码上下文
|
|
217
|
+
*/
|
|
218
|
+
createContext() {
|
|
219
|
+
return {
|
|
220
|
+
metadata: new Map(),
|
|
221
|
+
errors: [],
|
|
222
|
+
warnings: [],
|
|
223
|
+
stats: {
|
|
224
|
+
startTime: Date.now(),
|
|
225
|
+
processedTokens: 0,
|
|
226
|
+
convertedElements: 0,
|
|
227
|
+
},
|
|
228
|
+
performance: {
|
|
229
|
+
startTime: Date.now(),
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* 执行中间件钩子
|
|
235
|
+
*/
|
|
236
|
+
async executeMiddlewareHook(hookName, data, context) {
|
|
237
|
+
let result = data;
|
|
238
|
+
for (const middleware of this.middlewarePlugins) {
|
|
239
|
+
const hook = middleware[hookName];
|
|
240
|
+
if (hook) {
|
|
241
|
+
try {
|
|
242
|
+
result = (await hook.call(middleware, result, context)) || result;
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
context.errors.push(error);
|
|
246
|
+
await middleware.onError?.(error, context);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return result;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* 主解析方法 - 固定的流水线 + 灵活的中间件
|
|
254
|
+
*/
|
|
255
|
+
async parseByBuffer(buffer) {
|
|
256
|
+
await this.initialize();
|
|
257
|
+
let context = this.createContext();
|
|
258
|
+
context.rawData = buffer;
|
|
259
|
+
context.xmlContent = buffer.toString("utf-8");
|
|
260
|
+
// 在上下文中传递解码器实例引用
|
|
261
|
+
context.metadata.set("decoder", this);
|
|
262
|
+
try {
|
|
263
|
+
// 固定的流水线阶段顺序执行
|
|
264
|
+
for (const processor of this.processors) {
|
|
265
|
+
try {
|
|
266
|
+
// 1. 执行核心处理器
|
|
267
|
+
context = await processor.process(context);
|
|
268
|
+
// 2. 根据阶段执行对应的中间件钩子
|
|
269
|
+
switch (processor.stage) {
|
|
270
|
+
case PipelineStage.TOKENIZE:
|
|
271
|
+
if (context.tokens) {
|
|
272
|
+
context.tokens = await this.executeMiddlewareHook("onTokenize", context.tokens, context);
|
|
273
|
+
}
|
|
274
|
+
break;
|
|
275
|
+
case PipelineStage.AST_GENERATE:
|
|
276
|
+
if (context.ast) {
|
|
277
|
+
context.ast = await this.executeMiddlewareHook("onAstGenerate", context.ast, context);
|
|
278
|
+
}
|
|
279
|
+
break;
|
|
280
|
+
case PipelineStage.CONVERT:
|
|
281
|
+
if (context.result) {
|
|
282
|
+
context.result = await this.executeMiddlewareHook("onConvert", context.result, context);
|
|
283
|
+
}
|
|
284
|
+
break;
|
|
285
|
+
case PipelineStage.COMPLETE:
|
|
286
|
+
if (context.result) {
|
|
287
|
+
context.result = await this.executeMiddlewareHook("onComplete", context.result, context);
|
|
288
|
+
}
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
context.errors.push(error);
|
|
294
|
+
// 执行错误处理中间件
|
|
295
|
+
for (const middleware of this.middlewarePlugins) {
|
|
296
|
+
await middleware.onError?.(error, context);
|
|
297
|
+
}
|
|
298
|
+
console.error(`TCX流水线阶段 ${processor.stage} 处理失败:`, error);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return context.result;
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
console.error("TCX解析失败:", error);
|
|
305
|
+
throw error;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* 转换Extensions(向后兼容)
|
|
310
|
+
*/
|
|
311
|
+
convertExtensions(extensionsAST) {
|
|
312
|
+
const extensions = {};
|
|
313
|
+
const { attributes, tag, value } = extensionsAST;
|
|
314
|
+
// 处理属性
|
|
315
|
+
if (attributes) {
|
|
316
|
+
Object.entries(attributes).forEach(([key, value]) => {
|
|
317
|
+
extensions[key] = value;
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
// 如果有子节点,直接处理子节点,不再包装外层标签
|
|
321
|
+
if (extensionsAST?.children?.length) {
|
|
322
|
+
extensionsAST.children?.forEach((child) => {
|
|
323
|
+
extensions[child.tag] =
|
|
324
|
+
this.convertExtensions(child);
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
else if (value !== undefined) {
|
|
328
|
+
// 如果没有子节点但有值,直接返回值
|
|
329
|
+
return value;
|
|
330
|
+
}
|
|
331
|
+
return extensions;
|
|
332
|
+
}
|
|
333
|
+
// ==================== 向后兼容方法 ====================
|
|
334
|
+
/**
|
|
335
|
+
* @deprecated 使用 parseByBuffer 替代
|
|
336
|
+
*/
|
|
337
|
+
async parserByBuffer(buffer) {
|
|
338
|
+
return this.parseByBuffer(buffer);
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* @deprecated 使用新的插件系统替代
|
|
342
|
+
*/
|
|
343
|
+
async parseByString(xmlContent) {
|
|
344
|
+
const buffer = Buffer.from(xmlContent, "utf-8");
|
|
345
|
+
return this.parseByBuffer(buffer);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
export default {
|
|
349
|
+
TCXDecoder,
|
|
350
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { TCXFileType } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* TCX 编码器 - 将 TCXFileType 数据编码为 TCX XML 格式
|
|
4
|
+
*/
|
|
5
|
+
export declare class TCXEncoder {
|
|
6
|
+
private compact;
|
|
7
|
+
/**
|
|
8
|
+
* 构造函数
|
|
9
|
+
* @param options 编码器选项
|
|
10
|
+
*/
|
|
11
|
+
constructor(options?: {
|
|
12
|
+
compact?: boolean;
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* 编码 TCX 数据为 Buffer
|
|
16
|
+
*/
|
|
17
|
+
encode(tcxData: TCXFileType): Promise<Buffer>;
|
|
18
|
+
/**
|
|
19
|
+
* 编码 TCX 数据为 XML 字符串
|
|
20
|
+
*/
|
|
21
|
+
encodeToString(tcxData: TCXFileType): string;
|
|
22
|
+
/**
|
|
23
|
+
* 构建完整的 TCX XML
|
|
24
|
+
*/
|
|
25
|
+
private buildTCXXML;
|
|
26
|
+
/**
|
|
27
|
+
* 压缩XML字符串,去掉多余的换行和空格
|
|
28
|
+
*/
|
|
29
|
+
private compressXML;
|
|
30
|
+
/**
|
|
31
|
+
* 构建 XML 头部
|
|
32
|
+
*/
|
|
33
|
+
private buildXMLHeader;
|
|
34
|
+
/**
|
|
35
|
+
* 构建 TCX 内容
|
|
36
|
+
*/
|
|
37
|
+
private buildTCXContent;
|
|
38
|
+
/**
|
|
39
|
+
* 构建 Folders 元素
|
|
40
|
+
*/
|
|
41
|
+
private buildFolders;
|
|
42
|
+
/**
|
|
43
|
+
* 构建 History 元素
|
|
44
|
+
*/
|
|
45
|
+
private buildHistory;
|
|
46
|
+
/**
|
|
47
|
+
* 构建 HistoryFolder 元素
|
|
48
|
+
*/
|
|
49
|
+
private buildHistoryFolder;
|
|
50
|
+
/**
|
|
51
|
+
* 构建 Activities 元素
|
|
52
|
+
*/
|
|
53
|
+
private buildActivities;
|
|
54
|
+
/**
|
|
55
|
+
* 构建单个 Activity 元素
|
|
56
|
+
*/
|
|
57
|
+
private buildActivity;
|
|
58
|
+
/**
|
|
59
|
+
* 构建 ActivityLap 元素
|
|
60
|
+
*/
|
|
61
|
+
private buildActivityLap;
|
|
62
|
+
/**
|
|
63
|
+
* 构建 Track 元素
|
|
64
|
+
*/
|
|
65
|
+
private buildTrack;
|
|
66
|
+
/**
|
|
67
|
+
* 构建 Trackpoint 元素
|
|
68
|
+
*/
|
|
69
|
+
private buildTrackpoint;
|
|
70
|
+
/**
|
|
71
|
+
* 构建 Position 元素
|
|
72
|
+
*/
|
|
73
|
+
private buildPosition;
|
|
74
|
+
/**
|
|
75
|
+
* 构建 Cadence 元素
|
|
76
|
+
*/
|
|
77
|
+
private buildCadence;
|
|
78
|
+
/**
|
|
79
|
+
* 构建 MultiSportSession 元素
|
|
80
|
+
*/
|
|
81
|
+
private buildMultiSportSession;
|
|
82
|
+
/**
|
|
83
|
+
* 构建 FirstSport 元素
|
|
84
|
+
*/
|
|
85
|
+
private buildFirstSport;
|
|
86
|
+
/**
|
|
87
|
+
* 构建 NextSport 元素
|
|
88
|
+
*/
|
|
89
|
+
private buildNextSport;
|
|
90
|
+
/**
|
|
91
|
+
* 构建 Workouts 元素
|
|
92
|
+
*/
|
|
93
|
+
private buildWorkouts;
|
|
94
|
+
/**
|
|
95
|
+
* 构建 Courses 元素
|
|
96
|
+
*/
|
|
97
|
+
private buildCourses;
|
|
98
|
+
/**
|
|
99
|
+
* 构建 Author 元素
|
|
100
|
+
*/
|
|
101
|
+
private buildAuthor;
|
|
102
|
+
/**
|
|
103
|
+
* 构建扩展元素
|
|
104
|
+
*/
|
|
105
|
+
private buildExtensions;
|
|
106
|
+
/**
|
|
107
|
+
* 递归构建扩展内容
|
|
108
|
+
*/
|
|
109
|
+
private buildExtensionContent;
|
|
110
|
+
/**
|
|
111
|
+
* 格式化时间
|
|
112
|
+
*/
|
|
113
|
+
private formatTime;
|
|
114
|
+
/**
|
|
115
|
+
* 添加可选元素
|
|
116
|
+
*/
|
|
117
|
+
private addOptionalElement;
|
|
118
|
+
/**
|
|
119
|
+
* 添加可选数值元素
|
|
120
|
+
*/
|
|
121
|
+
private addOptionalNumericElement;
|
|
122
|
+
/**
|
|
123
|
+
* 验证坐标有效性
|
|
124
|
+
*/
|
|
125
|
+
private isValidCoordinate;
|
|
126
|
+
/**
|
|
127
|
+
* 格式化坐标
|
|
128
|
+
*/
|
|
129
|
+
private formatCoordinate;
|
|
130
|
+
/**
|
|
131
|
+
* 数字四舍五入
|
|
132
|
+
*/
|
|
133
|
+
private roundTo;
|
|
134
|
+
/**
|
|
135
|
+
* XML 转义
|
|
136
|
+
*/
|
|
137
|
+
private escapeXML;
|
|
138
|
+
/**
|
|
139
|
+
* XML 属性值转义(专门用于属性值的安全转义)
|
|
140
|
+
*/
|
|
141
|
+
private escapeXMLAttribute;
|
|
142
|
+
/**
|
|
143
|
+
* 深度拷贝
|
|
144
|
+
*/
|
|
145
|
+
private deepClone;
|
|
146
|
+
/**
|
|
147
|
+
* @deprecated 使用 encode() 替代
|
|
148
|
+
*/
|
|
149
|
+
encoder(node: TCXFileType): Promise<Buffer>;
|
|
150
|
+
}
|