@willwade/aac-processors 0.2.20 → 0.3.1
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/README.md +1 -0
- package/dist/browser/index.browser.js +17 -1
- package/dist/browser/processors/gotalkNow/nsKeyed.js +108 -0
- package/dist/browser/processors/gotalkNowProcessor.js +538 -0
- package/dist/browser/processors/gridsetProcessor.js +2 -2
- package/dist/browser/processors/obfProcessor.js +17 -6
- package/dist/browser/validation/gridsetValidator.js +54 -198
- package/dist/cli/index.js +3 -0
- package/dist/core/analyze.js +5 -0
- package/dist/core/treeStructure.d.ts +12 -0
- package/dist/gotalkNow.d.ts +6 -0
- package/dist/gotalkNow.js +11 -0
- package/dist/index.browser.d.ts +1 -0
- package/dist/index.browser.js +19 -2
- package/dist/index.node.d.ts +1 -0
- package/dist/index.node.js +6 -1
- package/dist/processors/gotalkNow/nsKeyed.d.ts +27 -0
- package/dist/processors/gotalkNow/nsKeyed.js +112 -0
- package/dist/processors/gotalkNowProcessor.d.ts +51 -0
- package/dist/processors/gotalkNowProcessor.js +544 -0
- package/dist/processors/gridsetProcessor.js +2 -2
- package/dist/processors/index.d.ts +1 -0
- package/dist/processors/index.js +3 -1
- package/dist/processors/obfProcessor.js +17 -6
- package/dist/validation/gridsetValidator.d.ts +8 -15
- package/dist/validation/gridsetValidator.js +54 -198
- package/package.json +5 -1
|
@@ -29,12 +29,12 @@ export class GridsetValidator extends BaseValidator {
|
|
|
29
29
|
if (name.endsWith('.gridset') || name.endsWith('.gridsetx')) {
|
|
30
30
|
return true;
|
|
31
31
|
}
|
|
32
|
-
// Try to parse as XML and check for
|
|
32
|
+
// Try to parse as XML and check for a Grid 3 <Grid> root element
|
|
33
33
|
try {
|
|
34
34
|
const contentStr = typeof content === 'string' ? content : decodeText(toUint8Array(content));
|
|
35
35
|
const parser = new xml2js.Parser();
|
|
36
36
|
const result = await parser.parseStringPromise(contentStr);
|
|
37
|
-
return result && (result.
|
|
37
|
+
return !!(result && (result.Grid || result.grid));
|
|
38
38
|
}
|
|
39
39
|
catch {
|
|
40
40
|
return false;
|
|
@@ -78,7 +78,7 @@ export class GridsetValidator extends BaseValidator {
|
|
|
78
78
|
return content[0] === 0x50 && content[1] === 0x4b && content[2] === 0x03 && content[3] === 0x04;
|
|
79
79
|
}
|
|
80
80
|
/**
|
|
81
|
-
* Validate a single
|
|
81
|
+
* Validate a single grid.xml file (exploded/non-zip format)
|
|
82
82
|
*/
|
|
83
83
|
async validateSingleXml(content, filename, _filesize) {
|
|
84
84
|
let xmlObj = null;
|
|
@@ -94,49 +94,57 @@ export class GridsetValidator extends BaseValidator {
|
|
|
94
94
|
});
|
|
95
95
|
if (!xmlObj)
|
|
96
96
|
return;
|
|
97
|
-
await this.add_check('xml_structure', '
|
|
98
|
-
if (!xmlObj.
|
|
99
|
-
this.err('missing root
|
|
97
|
+
await this.add_check('xml_structure', 'Grid root element', async () => {
|
|
98
|
+
if (!xmlObj.Grid && !xmlObj.grid) {
|
|
99
|
+
this.err('missing root Grid element', true);
|
|
100
100
|
}
|
|
101
101
|
});
|
|
102
|
-
const
|
|
103
|
-
if (
|
|
104
|
-
await this.
|
|
102
|
+
const grid = xmlObj.Grid || xmlObj.grid;
|
|
103
|
+
if (grid) {
|
|
104
|
+
await this.validateGridStructure(grid, filename);
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
/**
|
|
108
108
|
* Validate a ZIP archive (.gridset)
|
|
109
|
+
*
|
|
110
|
+
* Real Grid 3 archives do NOT contain a top-level gridset.xml. The required
|
|
111
|
+
* structure is: Settings0/settings.xml + FileMap.xml + Grids/<name>/grid.xml
|
|
112
|
+
* (+ optional Styles, images, thumbnails).
|
|
109
113
|
*/
|
|
110
|
-
async validateZipArchive(content,
|
|
114
|
+
async validateZipArchive(content, _filename, _filesize) {
|
|
111
115
|
const zip = await this._options.zipAdapter(content);
|
|
112
116
|
const entries = zip.listFiles();
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (
|
|
117
|
-
this.err('
|
|
117
|
+
// --- Required: at least one Grids/<name>/grid.xml ---
|
|
118
|
+
const gridEntries = entries.filter((e) => /^Grids\/.+\/grid\.xml$/i.test(e));
|
|
119
|
+
await this.add_check('grid_xml_presence', 'Grids/*/grid.xml presence', async () => {
|
|
120
|
+
if (gridEntries.length === 0) {
|
|
121
|
+
this.err('No Grids/<name>/grid.xml found in archive', true);
|
|
118
122
|
}
|
|
119
|
-
|
|
123
|
+
});
|
|
124
|
+
// Validate the <Grid> structure of each grid.xml (sample up to 5)
|
|
125
|
+
for (let i = 0; i < Math.min(gridEntries.length, 5); i++) {
|
|
126
|
+
const entry = gridEntries[i];
|
|
127
|
+
await this.add_check(`grid_xml[${entry}]`, `grid structure: ${entry}`, async () => {
|
|
120
128
|
try {
|
|
121
|
-
const
|
|
129
|
+
const gridXml = await zip.readFile(entry);
|
|
122
130
|
const parser = new xml2js.Parser();
|
|
123
|
-
const xmlObj = await parser.parseStringPromise(
|
|
124
|
-
const
|
|
125
|
-
if (!
|
|
126
|
-
this.err(
|
|
131
|
+
const xmlObj = await parser.parseStringPromise(gridXml);
|
|
132
|
+
const grid = xmlObj.Grid || xmlObj.grid;
|
|
133
|
+
if (!grid) {
|
|
134
|
+
this.err(`${entry}: missing root <Grid> element`);
|
|
127
135
|
}
|
|
128
136
|
else {
|
|
129
|
-
await this.
|
|
137
|
+
await this.validateGridStructure(grid, entry);
|
|
130
138
|
}
|
|
131
139
|
}
|
|
132
140
|
catch (e) {
|
|
133
|
-
this.err(
|
|
141
|
+
this.err(`${entry}: failed to parse XML: ${e.message}`);
|
|
134
142
|
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
//
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
// --- Recommended: Settings0/settings.xml ---
|
|
138
146
|
await this.add_check('settings_xml_presence', 'settings.xml presence', async () => {
|
|
139
|
-
const settingsEntry = entries.find((e) => e.toLowerCase() === 'settings.xml');
|
|
147
|
+
const settingsEntry = entries.find((e) => e.toLowerCase() === 'settings.xml' || e.toLowerCase().endsWith('/settings.xml'));
|
|
140
148
|
if (!settingsEntry) {
|
|
141
149
|
this.warn('Missing settings.xml in archive (required for full metadata)');
|
|
142
150
|
}
|
|
@@ -161,187 +169,35 @@ export class GridsetValidator extends BaseValidator {
|
|
|
161
169
|
}
|
|
162
170
|
}
|
|
163
171
|
});
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// Check for required elements
|
|
170
|
-
await this.add_check('gridset_id', 'gridset id', async () => {
|
|
171
|
-
const id = gridset.$.id || gridset.$.Id;
|
|
172
|
-
if (!id) {
|
|
173
|
-
this.warn('gridset should have an id attribute');
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
await this.add_check('gridset_name', 'gridset name', async () => {
|
|
177
|
-
const name = gridset.$.name || gridset.$.Name || gridset.name?.[0];
|
|
178
|
-
if (!name) {
|
|
179
|
-
this.warn('gridset should have a name attribute or element');
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
// Check for pages
|
|
183
|
-
await this.add_check('pages', 'pages element', async () => {
|
|
184
|
-
if (!gridset.pages && !gridset.Pages) {
|
|
185
|
-
this.err('gridset must have a pages element');
|
|
186
|
-
}
|
|
187
|
-
else {
|
|
188
|
-
const pages = gridset.pages || gridset.Pages;
|
|
189
|
-
if (!pages[0] || !Array.isArray(pages[0].page)) {
|
|
190
|
-
this.warn('pages should contain at least one page element');
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
// Validate individual pages
|
|
195
|
-
const pages = gridset.pages?.[0] || gridset.Pages?.[0];
|
|
196
|
-
if (pages && Array.isArray(pages.page)) {
|
|
197
|
-
await this.add_check('page_count', 'page count', async () => {
|
|
198
|
-
if (pages.page.length === 0) {
|
|
199
|
-
this.err('gridset must contain at least one page');
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
for (let i = 0; i < Math.min(pages.page.length, 10); i++) {
|
|
203
|
-
// Limit to first 10 pages to avoid excessive validation
|
|
204
|
-
const page = pages.page[i];
|
|
205
|
-
await this.validatePage(page, i);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
// Check for fixedCellSize
|
|
209
|
-
await this.add_check('fixed_cell_size', 'fixedCellSize element', async () => {
|
|
210
|
-
const fixedSize = gridset.fixedCellSize || gridset.FixedCellSize;
|
|
211
|
-
if (!fixedSize) {
|
|
212
|
-
this.warn('gridset should have a fixedCellSize element for consistency');
|
|
213
|
-
}
|
|
214
|
-
else {
|
|
215
|
-
// Validate fixedCellSize structure
|
|
216
|
-
const size = fixedSize[0];
|
|
217
|
-
if (size) {
|
|
218
|
-
const width = size.$.width || size.$.Width;
|
|
219
|
-
const height = size.$.height || size.$.Height;
|
|
220
|
-
if (!width || !height) {
|
|
221
|
-
this.warn('fixedCellSize should have both width and height attributes');
|
|
222
|
-
}
|
|
223
|
-
else if (isNaN(parseInt(width)) || isNaN(parseInt(height))) {
|
|
224
|
-
this.err('fixedCellSize width and height must be valid numbers');
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
// Check for styles
|
|
230
|
-
await this.add_check('styles', 'styles element', async () => {
|
|
231
|
-
const styles = gridset.styles || gridset.Styles;
|
|
232
|
-
if (!styles) {
|
|
233
|
-
this.warn('gridset should have a styles element for consistent formatting');
|
|
172
|
+
// --- Recommended: FileMap.xml (used for image resolution) ---
|
|
173
|
+
await this.add_check('filemap_presence', 'FileMap.xml presence', async () => {
|
|
174
|
+
const filemapEntry = entries.find((e) => e.toLowerCase() === 'filemap.xml');
|
|
175
|
+
if (!filemapEntry) {
|
|
176
|
+
this.warn('Missing FileMap.xml in archive (used for image resolution)');
|
|
234
177
|
}
|
|
235
178
|
});
|
|
236
179
|
}
|
|
237
180
|
/**
|
|
238
|
-
* Validate a
|
|
181
|
+
* Validate a real Grid 3 <Grid> element (from grid.xml).
|
|
182
|
+
* Checks for GridGuid, Cells, and basic cell structure.
|
|
239
183
|
*/
|
|
240
|
-
async
|
|
241
|
-
await this.add_check(
|
|
242
|
-
const
|
|
243
|
-
if (!
|
|
244
|
-
this.
|
|
184
|
+
async validateGridStructure(grid, label) {
|
|
185
|
+
await this.add_check(`${label}_gridguid`, 'GridGuid', async () => {
|
|
186
|
+
const guid = grid.GridGuid || grid.gridGuid;
|
|
187
|
+
if (!guid) {
|
|
188
|
+
this.warn(`${label}: missing <GridGuid> element`);
|
|
245
189
|
}
|
|
246
190
|
});
|
|
247
|
-
await this.add_check(
|
|
248
|
-
const
|
|
249
|
-
if (!name) {
|
|
250
|
-
this.warn(`page ${index} should have a name`);
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
// Check for cells
|
|
254
|
-
await this.add_check(`page[${index}]_cells`, `page ${index} cells`, async () => {
|
|
255
|
-
const cells = page.cells || page.Cells;
|
|
191
|
+
await this.add_check(`${label}_cells`, 'Cells element', async () => {
|
|
192
|
+
const cells = grid.Cells || grid.cells;
|
|
256
193
|
if (!cells) {
|
|
257
|
-
this.warn(
|
|
194
|
+
this.warn(`${label}: missing <Cells> element`);
|
|
195
|
+
return;
|
|
258
196
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
this.warn(`page ${index} should contain at least one cell`);
|
|
263
|
-
}
|
|
197
|
+
const cellArray = cells[0]?.Cell || cells[0]?.cell;
|
|
198
|
+
if (!cellArray || !Array.isArray(cellArray) || cellArray.length === 0) {
|
|
199
|
+
this.warn(`${label}: <Cells> should contain at least one <Cell>`);
|
|
264
200
|
}
|
|
265
201
|
});
|
|
266
|
-
// Validate cells if present
|
|
267
|
-
const cells = page.cells?.[0] || page.Cells?.[0];
|
|
268
|
-
if (cells) {
|
|
269
|
-
const cellArray = cells.cell || cells.Cell;
|
|
270
|
-
if (Array.isArray(cellArray) && cellArray.length > 0) {
|
|
271
|
-
// Sample a few cells to validate
|
|
272
|
-
const sampleSize = Math.min(cellArray.length, 5);
|
|
273
|
-
for (let i = 0; i < sampleSize; i++) {
|
|
274
|
-
await this.validateCell(cellArray[i], index, i);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Validate a single cell
|
|
281
|
-
*/
|
|
282
|
-
async validateCell(cell, pageIdx, cellIdx) {
|
|
283
|
-
await this.add_check(`page[${pageIdx}]_cell[${cellIdx}]_id`, `cell id`, async () => {
|
|
284
|
-
const id = cell.$.id || cell.$.Id;
|
|
285
|
-
if (!id) {
|
|
286
|
-
this.warn(`cell ${cellIdx} on page ${pageIdx} is missing id attribute`);
|
|
287
|
-
}
|
|
288
|
-
});
|
|
289
|
-
await this.add_check(`page[${pageIdx}]_cell[${cellIdx}]_content`, `cell content`, async () => {
|
|
290
|
-
const label = cell.$.label || cell.$.Label;
|
|
291
|
-
const image = cell.$.image || cell.$.Image;
|
|
292
|
-
if (!label && !image) {
|
|
293
|
-
this.warn(`cell ${cellIdx} on page ${pageIdx} should have a label or image`);
|
|
294
|
-
}
|
|
295
|
-
});
|
|
296
|
-
// Validate scan block number (Grid 3 attribute)
|
|
297
|
-
await this.add_check(`page[${pageIdx}]_cell[${cellIdx}]_scanblock`, `cell scan block`, async () => {
|
|
298
|
-
const scanBlock = cell.$.scanBlock || cell.$.ScanBlock;
|
|
299
|
-
if (scanBlock !== undefined) {
|
|
300
|
-
const blockNum = parseInt(scanBlock, 10);
|
|
301
|
-
if (isNaN(blockNum) || blockNum < 1 || blockNum > 8) {
|
|
302
|
-
this.err(`cell ${cellIdx} on page ${pageIdx} has invalid scanBlock value: ${scanBlock} (must be 1-8)`, false);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
});
|
|
306
|
-
// Check for color attributes
|
|
307
|
-
const backgroundColor = cell.$.backgroundColor || cell.$.BackgroundColor;
|
|
308
|
-
const _color = cell.$.color || cell.$.Color;
|
|
309
|
-
if (backgroundColor) {
|
|
310
|
-
await this.add_check(`page[${pageIdx}]_cell[${cellIdx}]_bg_color`, `cell background color`, async () => {
|
|
311
|
-
// Grid3 colors can be in various formats: named colors, hex, ARGB
|
|
312
|
-
// We just check it's not empty
|
|
313
|
-
if (backgroundColor.length === 0) {
|
|
314
|
-
this.warn(`cell ${cellIdx} has empty background color`);
|
|
315
|
-
}
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
// Check for valid jump references
|
|
319
|
-
const jump = cell.$.jump || cell.$.Jump;
|
|
320
|
-
if (jump) {
|
|
321
|
-
await this.add_check(`page[${pageIdx}]_cell[${cellIdx}]_jump`, `cell jump reference`, async () => {
|
|
322
|
-
if (typeof jump !== 'string' || jump.length === 0) {
|
|
323
|
-
this.warn(`cell ${cellIdx} has invalid jump reference`);
|
|
324
|
-
}
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* Validate color format (Grid3 uses ARGB format)
|
|
330
|
-
*/
|
|
331
|
-
isValidGrid3Color(color) {
|
|
332
|
-
if (!color || color.length === 0)
|
|
333
|
-
return false;
|
|
334
|
-
// Named colors are valid
|
|
335
|
-
if (/^[a-zA-Z]+$/.test(color))
|
|
336
|
-
return true;
|
|
337
|
-
// ARGB format: #AARRGGBB or #RRGGBB
|
|
338
|
-
if (color.startsWith('#')) {
|
|
339
|
-
return color.length === 7 || color.length === 9;
|
|
340
|
-
}
|
|
341
|
-
// RGB format: rgb(r,g,b) or rgba(r,g,b,a)
|
|
342
|
-
if (color.startsWith('rgb')) {
|
|
343
|
-
return true; // Simplified check
|
|
344
|
-
}
|
|
345
|
-
return false;
|
|
346
202
|
}
|
|
347
203
|
}
|
package/dist/cli/index.js
CHANGED
|
@@ -25,6 +25,9 @@ async function detectFormat(filePath) {
|
|
|
25
25
|
if (filePath.endsWith('.gridset')) {
|
|
26
26
|
return 'gridset';
|
|
27
27
|
}
|
|
28
|
+
if (filePath.endsWith('.gtbz')) {
|
|
29
|
+
return 'gotalknow';
|
|
30
|
+
}
|
|
28
31
|
// Otherwise use file extension
|
|
29
32
|
return (0, io_1.extname)(filePath).slice(1);
|
|
30
33
|
}
|
package/dist/core/analyze.js
CHANGED
|
@@ -11,6 +11,7 @@ const snapProcessor_1 = require("../processors/snapProcessor");
|
|
|
11
11
|
const dotProcessor_1 = require("../processors/dotProcessor");
|
|
12
12
|
const excelProcessor_1 = require("../processors/excelProcessor");
|
|
13
13
|
const applePanelsProcessor_1 = require("../processors/applePanelsProcessor");
|
|
14
|
+
const gotalkNowProcessor_1 = require("../processors/gotalkNowProcessor");
|
|
14
15
|
/**
|
|
15
16
|
* Resolve a processor instance by friendly format name or common extension.
|
|
16
17
|
* @param format Format key or extension (e.g., 'snap', 'obf', 'xlsx')
|
|
@@ -45,6 +46,10 @@ function getProcessor(format, options) {
|
|
|
45
46
|
case 'panels': // Apple Panels file extension
|
|
46
47
|
case 'ascconfig': // Apple Panels folder format
|
|
47
48
|
return new applePanelsProcessor_1.ApplePanelsProcessor(options);
|
|
49
|
+
case 'gotalknow':
|
|
50
|
+
case 'gotalk':
|
|
51
|
+
case 'gtbz': // GoTalk NOW file extension
|
|
52
|
+
return new gotalkNowProcessor_1.GotalkNowProcessor(options);
|
|
48
53
|
default:
|
|
49
54
|
throw new Error('Unknown format: ' + format);
|
|
50
55
|
}
|
|
@@ -114,6 +114,18 @@ export interface AACSemanticAction {
|
|
|
114
114
|
[key: string]: any;
|
|
115
115
|
};
|
|
116
116
|
};
|
|
117
|
+
gotalkNow?: {
|
|
118
|
+
buttonType: string;
|
|
119
|
+
jumpTo?: number;
|
|
120
|
+
jumpBook?: string;
|
|
121
|
+
audioLocation?: string;
|
|
122
|
+
trimStart?: number;
|
|
123
|
+
trimEnd?: number;
|
|
124
|
+
url?: string;
|
|
125
|
+
openInternally?: boolean;
|
|
126
|
+
goVisual?: string;
|
|
127
|
+
goVisualLink?: unknown;
|
|
128
|
+
};
|
|
117
129
|
};
|
|
118
130
|
fallback?: {
|
|
119
131
|
type: 'SPEAK' | 'NAVIGATE' | 'ACTION';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GoTalk NOW Namespace
|
|
4
|
+
*
|
|
5
|
+
* GoTalk NOW (Attainment Company) format processing utilities.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.GotalkNowProcessor = void 0;
|
|
9
|
+
// Processor class
|
|
10
|
+
var gotalkNowProcessor_1 = require("./processors/gotalkNowProcessor");
|
|
11
|
+
Object.defineProperty(exports, "GotalkNowProcessor", { enumerable: true, get: function () { return gotalkNowProcessor_1.GotalkNowProcessor; } });
|
package/dist/index.browser.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export { SnapProcessor } from './processors/snapProcessor';
|
|
|
22
22
|
export { TouchChatProcessor } from './processors/touchchatProcessor';
|
|
23
23
|
export { ApplePanelsProcessor } from './processors/applePanelsProcessor';
|
|
24
24
|
export { AstericsGridProcessor } from './processors/astericsGridProcessor';
|
|
25
|
+
export { GotalkNowProcessor } from './processors/gotalkNowProcessor';
|
|
25
26
|
export * as Metrics from './metrics';
|
|
26
27
|
import { BaseProcessor, ProcessorOptions } from './core/baseProcessor';
|
|
27
28
|
export { configureSqlJs } from './utils/sqlite';
|
package/dist/index.browser.js
CHANGED
|
@@ -39,7 +39,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
39
39
|
return result;
|
|
40
40
|
};
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
-
exports.configureSqlJs = exports.Metrics = exports.AstericsGridProcessor = exports.ApplePanelsProcessor = exports.TouchChatProcessor = exports.SnapProcessor = exports.GridsetProcessor = exports.ObfProcessor = exports.OpmlProcessor = exports.DotProcessor = void 0;
|
|
42
|
+
exports.configureSqlJs = exports.Metrics = exports.GotalkNowProcessor = exports.AstericsGridProcessor = exports.ApplePanelsProcessor = exports.TouchChatProcessor = exports.SnapProcessor = exports.GridsetProcessor = exports.ObfProcessor = exports.OpmlProcessor = exports.DotProcessor = void 0;
|
|
43
43
|
exports.getProcessor = getProcessor;
|
|
44
44
|
exports.getSupportedExtensions = getSupportedExtensions;
|
|
45
45
|
exports.isExtensionSupported = isExtensionSupported;
|
|
@@ -68,6 +68,8 @@ var applePanelsProcessor_1 = require("./processors/applePanelsProcessor");
|
|
|
68
68
|
Object.defineProperty(exports, "ApplePanelsProcessor", { enumerable: true, get: function () { return applePanelsProcessor_1.ApplePanelsProcessor; } });
|
|
69
69
|
var astericsGridProcessor_1 = require("./processors/astericsGridProcessor");
|
|
70
70
|
Object.defineProperty(exports, "AstericsGridProcessor", { enumerable: true, get: function () { return astericsGridProcessor_1.AstericsGridProcessor; } });
|
|
71
|
+
var gotalkNowProcessor_1 = require("./processors/gotalkNowProcessor");
|
|
72
|
+
Object.defineProperty(exports, "GotalkNowProcessor", { enumerable: true, get: function () { return gotalkNowProcessor_1.GotalkNowProcessor; } });
|
|
71
73
|
// ===================================================================
|
|
72
74
|
// UTILITY FUNCTIONS
|
|
73
75
|
// ===================================================================
|
|
@@ -81,6 +83,7 @@ const snapProcessor_2 = require("./processors/snapProcessor");
|
|
|
81
83
|
const touchchatProcessor_2 = require("./processors/touchchatProcessor");
|
|
82
84
|
const applePanelsProcessor_2 = require("./processors/applePanelsProcessor");
|
|
83
85
|
const astericsGridProcessor_2 = require("./processors/astericsGridProcessor");
|
|
86
|
+
const gotalkNowProcessor_2 = require("./processors/gotalkNowProcessor");
|
|
84
87
|
var sqlite_1 = require("./utils/sqlite");
|
|
85
88
|
Object.defineProperty(exports, "configureSqlJs", { enumerable: true, get: function () { return sqlite_1.configureSqlJs; } });
|
|
86
89
|
/**
|
|
@@ -112,6 +115,8 @@ function getProcessor(filePathOrExtension, options) {
|
|
|
112
115
|
return new applePanelsProcessor_2.ApplePanelsProcessor(options);
|
|
113
116
|
case '.grd':
|
|
114
117
|
return new astericsGridProcessor_2.AstericsGridProcessor(options);
|
|
118
|
+
case '.gtbz':
|
|
119
|
+
return new gotalkNowProcessor_2.GotalkNowProcessor(options);
|
|
115
120
|
default:
|
|
116
121
|
throw new Error(`Unsupported file extension: ${extension}`);
|
|
117
122
|
}
|
|
@@ -121,7 +126,19 @@ function getProcessor(filePathOrExtension, options) {
|
|
|
121
126
|
* @returns Array of supported file extensions
|
|
122
127
|
*/
|
|
123
128
|
function getSupportedExtensions() {
|
|
124
|
-
return [
|
|
129
|
+
return [
|
|
130
|
+
'.dot',
|
|
131
|
+
'.opml',
|
|
132
|
+
'.obf',
|
|
133
|
+
'.obz',
|
|
134
|
+
'.gridset',
|
|
135
|
+
'.spb',
|
|
136
|
+
'.sps',
|
|
137
|
+
'.ce',
|
|
138
|
+
'.plist',
|
|
139
|
+
'.grd',
|
|
140
|
+
'.gtbz',
|
|
141
|
+
];
|
|
125
142
|
}
|
|
126
143
|
/**
|
|
127
144
|
* Check if a file extension is supported
|
package/dist/index.node.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export * as Excel from './excel';
|
|
|
25
25
|
export * as Opml from './opml';
|
|
26
26
|
export * as ApplePanels from './applePanels';
|
|
27
27
|
export * as AstericsGrid from './astericsGrid';
|
|
28
|
+
export * as GotalkNow from './gotalkNow';
|
|
28
29
|
export * as Translation from './translation';
|
|
29
30
|
import { BaseProcessor, ProcessorOptions } from './core/baseProcessor';
|
|
30
31
|
/**
|
package/dist/index.node.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
33
33
|
return result;
|
|
34
34
|
};
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.Translation = exports.AstericsGrid = exports.ApplePanels = exports.Opml = exports.Excel = exports.Dot = exports.TouchChat = exports.Obfset = exports.OBF = exports.Snap = exports.Gridset = exports.WordFormGenerator = exports.TDSnapLexiconParser = exports.Grid3VerbsParser = exports.Metrics = exports.Validation = exports.Analytics = void 0;
|
|
36
|
+
exports.Translation = exports.GotalkNow = exports.AstericsGrid = exports.ApplePanels = exports.Opml = exports.Excel = exports.Dot = exports.TouchChat = exports.Obfset = exports.OBF = exports.Snap = exports.Gridset = exports.WordFormGenerator = exports.TDSnapLexiconParser = exports.Grid3VerbsParser = exports.Metrics = exports.Validation = exports.Analytics = void 0;
|
|
37
37
|
exports.getProcessor = getProcessor;
|
|
38
38
|
exports.getSupportedExtensions = getSupportedExtensions;
|
|
39
39
|
exports.isExtensionSupported = isExtensionSupported;
|
|
@@ -74,6 +74,7 @@ exports.Excel = __importStar(require("./excel"));
|
|
|
74
74
|
exports.Opml = __importStar(require("./opml"));
|
|
75
75
|
exports.ApplePanels = __importStar(require("./applePanels"));
|
|
76
76
|
exports.AstericsGrid = __importStar(require("./astericsGrid"));
|
|
77
|
+
exports.GotalkNow = __importStar(require("./gotalkNow"));
|
|
77
78
|
exports.Translation = __importStar(require("./translation"));
|
|
78
79
|
const dotProcessor_1 = require("./processors/dotProcessor");
|
|
79
80
|
const excelProcessor_1 = require("./processors/excelProcessor");
|
|
@@ -85,6 +86,7 @@ const touchchatProcessor_1 = require("./processors/touchchatProcessor");
|
|
|
85
86
|
const applePanelsProcessor_1 = require("./processors/applePanelsProcessor");
|
|
86
87
|
const astericsGridProcessor_1 = require("./processors/astericsGridProcessor");
|
|
87
88
|
const obfsetProcessor_1 = require("./processors/obfsetProcessor");
|
|
89
|
+
const gotalkNowProcessor_1 = require("./processors/gotalkNowProcessor");
|
|
88
90
|
/**
|
|
89
91
|
* Factory function to get the appropriate processor for a file extension
|
|
90
92
|
* @param filePathOrExtension - File path or extension (e.g., '.dot', '/path/to/file.obf')
|
|
@@ -124,6 +126,8 @@ function getProcessor(filePathOrExtension, options) {
|
|
|
124
126
|
return new applePanelsProcessor_1.ApplePanelsProcessor(options);
|
|
125
127
|
case '.grd':
|
|
126
128
|
return new astericsGridProcessor_1.AstericsGridProcessor(options);
|
|
129
|
+
case '.gtbz':
|
|
130
|
+
return new gotalkNowProcessor_1.GotalkNowProcessor(options);
|
|
127
131
|
default:
|
|
128
132
|
throw new Error(`Unsupported file extension: ${extension}`);
|
|
129
133
|
}
|
|
@@ -147,6 +151,7 @@ function getSupportedExtensions() {
|
|
|
147
151
|
'.ce',
|
|
148
152
|
'.plist',
|
|
149
153
|
'.grd',
|
|
154
|
+
'.gtbz',
|
|
150
155
|
];
|
|
151
156
|
}
|
|
152
157
|
/**
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for decoding GoTalk NOW's NSKeyedArchiver-encoded values.
|
|
3
|
+
*
|
|
4
|
+
* GoTalk NOW stores UIColor and UIFont objects inside `<data>` tags as
|
|
5
|
+
* binary property lists (NSKeyedArchiver). Rather than pulling in a full
|
|
6
|
+
* bplist parser, we exploit the fact that the archived objects embed
|
|
7
|
+
* human-readable ASCII representations of the component values, e.g. an
|
|
8
|
+
* NSRGB colour appears as "0.908 0.355 0.522" and a font name appears as
|
|
9
|
+
* "Futura-Medium".
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Decode an NSKeyedArchiver-encoded UIColor into a #rrggbb hex string.
|
|
13
|
+
*
|
|
14
|
+
* NSRGB / NSCalibratedRGB colours store a human-readable "R G B [A]" string
|
|
15
|
+
* (e.g. "0.908 0.355 0.522") inside the archive — this is the only encoding we
|
|
16
|
+
* can reliably read without a full binary-plist parser. NSWhite and
|
|
17
|
+
* UISystemColor values store their components as raw IEEE-754 floats, so we
|
|
18
|
+
* return undefined for those rather than risk an incorrect colour.
|
|
19
|
+
*/
|
|
20
|
+
export declare function decodeNsColor(buffer: Uint8Array | Buffer | undefined): string | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Decode an NSKeyedArchiver-encoded UIFont into its family name.
|
|
23
|
+
* Font size is encoded as a raw IEEE-754 float and is not extracted.
|
|
24
|
+
*/
|
|
25
|
+
export declare function decodeNsFont(buffer: Uint8Array | Buffer | undefined): {
|
|
26
|
+
fontFamily?: string;
|
|
27
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Helpers for decoding GoTalk NOW's NSKeyedArchiver-encoded values.
|
|
4
|
+
*
|
|
5
|
+
* GoTalk NOW stores UIColor and UIFont objects inside `<data>` tags as
|
|
6
|
+
* binary property lists (NSKeyedArchiver). Rather than pulling in a full
|
|
7
|
+
* bplist parser, we exploit the fact that the archived objects embed
|
|
8
|
+
* human-readable ASCII representations of the component values, e.g. an
|
|
9
|
+
* NSRGB colour appears as "0.908 0.355 0.522" and a font name appears as
|
|
10
|
+
* "Futura-Medium".
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.decodeNsColor = decodeNsColor;
|
|
14
|
+
exports.decodeNsFont = decodeNsFont;
|
|
15
|
+
function bufferToLatinString(buffer) {
|
|
16
|
+
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(buffer)) {
|
|
17
|
+
return buffer.toString('latin1');
|
|
18
|
+
}
|
|
19
|
+
let out = '';
|
|
20
|
+
for (let i = 0; i < buffer.length; i++)
|
|
21
|
+
out += String.fromCharCode(buffer[i]);
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
function clampUnit(v) {
|
|
25
|
+
if (Number.isNaN(v))
|
|
26
|
+
return 0;
|
|
27
|
+
return Math.max(0, Math.min(1, v));
|
|
28
|
+
}
|
|
29
|
+
function toHexChannel(v) {
|
|
30
|
+
return Math.round(clampUnit(v) * 255)
|
|
31
|
+
.toString(16)
|
|
32
|
+
.padStart(2, '0');
|
|
33
|
+
}
|
|
34
|
+
/** Convert a 0-1 float triple to an #rrggbb hex string. */
|
|
35
|
+
function rgbToHex(r, g, b) {
|
|
36
|
+
return `#${toHexChannel(r)}${toHexChannel(g)}${toHexChannel(b)}`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Decode an NSKeyedArchiver-encoded UIColor into a #rrggbb hex string.
|
|
40
|
+
*
|
|
41
|
+
* NSRGB / NSCalibratedRGB colours store a human-readable "R G B [A]" string
|
|
42
|
+
* (e.g. "0.908 0.355 0.522") inside the archive — this is the only encoding we
|
|
43
|
+
* can reliably read without a full binary-plist parser. NSWhite and
|
|
44
|
+
* UISystemColor values store their components as raw IEEE-754 floats, so we
|
|
45
|
+
* return undefined for those rather than risk an incorrect colour.
|
|
46
|
+
*/
|
|
47
|
+
function decodeNsColor(buffer) {
|
|
48
|
+
if (!buffer)
|
|
49
|
+
return undefined;
|
|
50
|
+
const s = bufferToLatinString(buffer);
|
|
51
|
+
// Match RGB[A] component strings like "1 0.949 0.561" or "0.5 0.5 0.5 1".
|
|
52
|
+
// Allow integer components (e.g. "1") but require at least one decimal to
|
|
53
|
+
// avoid matching bplist header bytes (e.g. "00 00 00").
|
|
54
|
+
const candidates = s.match(/\d+(?:\.\d+)?\s+\d+(?:\.\d+)?\s+\d+(?:\.\d+)?(?:\s+\d+(?:\.\d+)?)?/g);
|
|
55
|
+
if (candidates) {
|
|
56
|
+
// Find the first candidate that looks like RGB (has at least one decimal point).
|
|
57
|
+
const rgbStr = candidates.find((c) => c.includes('.'));
|
|
58
|
+
if (rgbStr) {
|
|
59
|
+
const parts = rgbStr.split(/\s+/).map(Number);
|
|
60
|
+
return rgbToHex(parts[0], parts[1], parts[2]);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
const FONT_KEYWORDS = new Set([
|
|
66
|
+
'bplist',
|
|
67
|
+
'bplist00',
|
|
68
|
+
'UIFont',
|
|
69
|
+
'UIFontName',
|
|
70
|
+
'UIFontPointSize',
|
|
71
|
+
'UIFontDescriptor',
|
|
72
|
+
'UIFontTraits',
|
|
73
|
+
'UIFontSystemFont',
|
|
74
|
+
'UIFontDescriptorAttributes',
|
|
75
|
+
'UIFontSystemFontName',
|
|
76
|
+
'NSFont',
|
|
77
|
+
'NSFontNameAttribute',
|
|
78
|
+
'NSFontSizeAttribute',
|
|
79
|
+
'NSName',
|
|
80
|
+
'NSSize',
|
|
81
|
+
'NSMutableDictionary',
|
|
82
|
+
'NSDictionary',
|
|
83
|
+
'NSObject',
|
|
84
|
+
'NSKeyedArchiver',
|
|
85
|
+
'NSColor',
|
|
86
|
+
'NSColorSpace',
|
|
87
|
+
'VUIFont',
|
|
88
|
+
'XNSObject',
|
|
89
|
+
'classname',
|
|
90
|
+
'classhints',
|
|
91
|
+
'classes',
|
|
92
|
+
'objects',
|
|
93
|
+
'version',
|
|
94
|
+
'archiver',
|
|
95
|
+
'root',
|
|
96
|
+
]);
|
|
97
|
+
/**
|
|
98
|
+
* Decode an NSKeyedArchiver-encoded UIFont into its family name.
|
|
99
|
+
* Font size is encoded as a raw IEEE-754 float and is not extracted.
|
|
100
|
+
*/
|
|
101
|
+
function decodeNsFont(buffer) {
|
|
102
|
+
if (!buffer)
|
|
103
|
+
return {};
|
|
104
|
+
const s = bufferToLatinString(buffer);
|
|
105
|
+
const tokens = s.match(/[A-Za-z][A-Za-z0-9-]{2,}/g) || [];
|
|
106
|
+
const fontFamily = tokens.find((t) => !FONT_KEYWORDS.has(t) &&
|
|
107
|
+
!t.startsWith('UI') &&
|
|
108
|
+
!t.startsWith('NS') &&
|
|
109
|
+
!t.startsWith('CF') &&
|
|
110
|
+
!t.startsWith('GT'));
|
|
111
|
+
return fontFamily ? { fontFamily } : {};
|
|
112
|
+
}
|