@willwade/aac-processors 0.1.11 → 0.1.13
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/browser/processors/gridset/resolver.js +10 -0
- package/dist/browser/processors/gridsetProcessor.js +155 -9
- package/dist/processors/gridset/resolver.js +10 -0
- package/dist/processors/gridsetProcessor.js +155 -9
- package/package.json +1 -3
- package/examples/.coverage +0 -0
- package/examples/.keep +0 -1
- package/examples/README.md +0 -55
- package/examples/browser-test.html +0 -331
- package/examples/communikate.dot +0 -2637
- package/examples/demo.js +0 -143
- package/examples/example-images/FileMap.xml +0 -51
- package/examples/example-images/Grids/Start/0-1-0-text-0.png +0 -0
- package/examples/example-images/Grids/Start/0-2-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/0-3-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/0-4-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/0-5-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/1-1-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/1-2-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/1-3-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/1-4-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/1-5-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/10-3-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/10-4-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/11-3-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/11-4-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/2-1-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/2-2-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/2-3-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/2-4-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/2-5-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/3-1-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/3-3-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/3-4-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/3-5-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/4-1-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/4-3-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/4-4-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/4-5-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/5-4-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/5-5-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/6-3-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/6-4-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/7-1-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/7-2-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/7-3-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/7-4-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/8-1-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/8-2-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/8-3-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/8-4-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/9-1.png +0 -0
- package/examples/example-images/Grids/Start/9-2.png +0 -0
- package/examples/example-images/Grids/Start/9-3-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/9-4-0-text-0.jpeg +0 -0
- package/examples/example-images/Grids/Start/grid.xml +0 -1325
- package/examples/example-images/Settings0/Styles/styles.xml +0 -39
- package/examples/example-images/Settings0/WebBrowser/webbrowserextensions.xml +0 -3
- package/examples/example-images/Settings0/settings.xml +0 -16
- package/examples/example-images.gridset +0 -0
- package/examples/example-images.zip +0 -0
- package/examples/example.ce +0 -0
- package/examples/example.dot +0 -14
- package/examples/example.grd +0 -1
- package/examples/example.gridset +0 -0
- package/examples/example.obf +0 -27
- package/examples/example.obz +0 -0
- package/examples/example.opml +0 -18
- package/examples/example.spb +0 -0
- package/examples/example.sps +0 -0
- package/examples/example2.grd +0 -1
- package/examples/obf/aboutme.json +0 -376
- package/examples/obf/array.json +0 -6
- package/examples/obf/hash.json +0 -4
- package/examples/obf/links.obz +0 -0
- package/examples/obf/simple.obf +0 -53
- package/examples/package-lock.json +0 -1326
- package/examples/package.json +0 -10
- package/examples/styled-output/converted-snap-to-touchchat.ce +0 -0
- package/examples/styled-output/styled-example.ce +0 -0
- package/examples/styled-output/styled-example.gridset +0 -0
- package/examples/styled-output/styled-example.obf +0 -37
- package/examples/styled-output/styled-example.spb +0 -0
- package/examples/styling-example.ts +0 -316
- package/examples/translate.js +0 -39
- package/examples/translate_demo.js +0 -254
- package/examples/typescript-demo.ts +0 -251
- package/examples/vitedemo/README.md +0 -164
- package/examples/vitedemo/index.html +0 -580
- package/examples/vitedemo/package-lock.json +0 -1751
- package/examples/vitedemo/package.json +0 -24
- package/examples/vitedemo/src/main.ts +0 -1001
- package/examples/vitedemo/src/vite-env.d.ts +0 -1
- package/examples/vitedemo/test-files/example.dot +0 -14
- package/examples/vitedemo/test-files/example.grd +0 -1
- package/examples/vitedemo/test-files/example.gridset +0 -0
- package/examples/vitedemo/test-files/example.obz +0 -0
- package/examples/vitedemo/test-files/example.opml +0 -18
- package/examples/vitedemo/test-files/simple.obf +0 -53
- package/examples/vitedemo/tsconfig.json +0 -24
- package/examples/vitedemo/vite.config.ts +0 -57
|
@@ -42,6 +42,13 @@ export function resolveGrid3CellImage(zip, args, zipEntries) {
|
|
|
42
42
|
const y = args.y;
|
|
43
43
|
const entries = new Set(listZipEntries(zip, zipEntries));
|
|
44
44
|
const has = (p) => entries.has(normalizeZipPathLocal(p));
|
|
45
|
+
// Debug logging for cells that fail to resolve
|
|
46
|
+
const shouldDebug = imageName?.startsWith('-') && x !== undefined && y !== undefined;
|
|
47
|
+
const debugLog = (msg) => {
|
|
48
|
+
if (shouldDebug) {
|
|
49
|
+
console.log(`[Resolver] ${baseDir} (${x},${y}) "${imageName}": ${msg}`);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
45
52
|
// Built-in resource like [grid3x]... (old format, not symbol library)
|
|
46
53
|
// Check this BEFORE general symbol references to avoid misclassification
|
|
47
54
|
if (imageName && imageName.startsWith('[')) {
|
|
@@ -72,6 +79,7 @@ export function resolveGrid3CellImage(zip, args, zipEntries) {
|
|
|
72
79
|
// to be prefixed with the cell coordinates
|
|
73
80
|
if (imageName.startsWith('-') && x != null && y != null) {
|
|
74
81
|
const coordPrefixed = joinBaseDir(baseDir, `${x}-${y}${imageName}`);
|
|
82
|
+
debugLog(`trying coord-prefixed: ${coordPrefixed}, found: ${has(coordPrefixed)}`);
|
|
75
83
|
if (has(coordPrefixed))
|
|
76
84
|
return coordPrefixed;
|
|
77
85
|
}
|
|
@@ -114,12 +122,14 @@ export function resolveGrid3CellImage(zip, args, zipEntries) {
|
|
|
114
122
|
`${x}-${y}.jpg`,
|
|
115
123
|
`${x}-${y}.png`,
|
|
116
124
|
].map((n) => joinBaseDir(baseDir, n));
|
|
125
|
+
debugLog(`trying candidates: ${candidates.filter(has).join(', ') || 'none found'}`);
|
|
117
126
|
for (const c of candidates) {
|
|
118
127
|
if (has(c))
|
|
119
128
|
return c;
|
|
120
129
|
}
|
|
121
130
|
}
|
|
122
131
|
}
|
|
132
|
+
debugLog(`NOT FOUND - returning null`);
|
|
123
133
|
return null;
|
|
124
134
|
}
|
|
125
135
|
/**
|
|
@@ -325,8 +325,8 @@ class GridsetProcessor extends BaseProcessor {
|
|
|
325
325
|
if (typeof val === 'number')
|
|
326
326
|
return String(val);
|
|
327
327
|
if (typeof val === 'object') {
|
|
328
|
-
|
|
329
|
-
|
|
328
|
+
// Don't immediately return #text - it might be whitespace alongside structured content
|
|
329
|
+
// Process structured format first: <p><s><r>text</r></s></p>
|
|
330
330
|
// Handle Grid3 structured format <p><s><r>text</r></s></p>
|
|
331
331
|
// Can start at p, s, or r level
|
|
332
332
|
const parts = [];
|
|
@@ -342,8 +342,17 @@ class GridsetProcessor extends BaseProcessor {
|
|
|
342
342
|
}
|
|
343
343
|
continue;
|
|
344
344
|
}
|
|
345
|
-
if (typeof r === 'object' && r !== null
|
|
346
|
-
|
|
345
|
+
if (typeof r === 'object' && r !== null) {
|
|
346
|
+
// Check for #text (regular text) or #cdata (CDATA sections)
|
|
347
|
+
if ('#text' in r) {
|
|
348
|
+
parts.push(String(r['#text']));
|
|
349
|
+
}
|
|
350
|
+
else if ('#cdata' in r) {
|
|
351
|
+
parts.push(String(r['#cdata']));
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
parts.push(String(r));
|
|
355
|
+
}
|
|
347
356
|
}
|
|
348
357
|
else {
|
|
349
358
|
parts.push(String(r));
|
|
@@ -396,7 +405,15 @@ class GridsetProcessor extends BaseProcessor {
|
|
|
396
405
|
}
|
|
397
406
|
const password = this.getGridsetPassword(filePathOrBuffer);
|
|
398
407
|
const entries = getZipEntriesFromAdapter(zipResult.zip, password);
|
|
399
|
-
const
|
|
408
|
+
const options = {
|
|
409
|
+
ignoreAttributes: false,
|
|
410
|
+
ignoreDeclaration: true,
|
|
411
|
+
parseTagValue: false,
|
|
412
|
+
trimValues: false,
|
|
413
|
+
textNodeName: '#text',
|
|
414
|
+
cdataProp: '#cdata',
|
|
415
|
+
};
|
|
416
|
+
const parser = new XMLParser(options);
|
|
400
417
|
const isEncryptedArchive = typeof filePathOrBuffer === 'string' && filePathOrBuffer.toLowerCase().endsWith('.gridsetx');
|
|
401
418
|
const encryptedContentPassword = this.getGridsetPassword(filePathOrBuffer);
|
|
402
419
|
// Initialize metadata
|
|
@@ -634,6 +651,13 @@ class GridsetProcessor extends BaseProcessor {
|
|
|
634
651
|
if (text) {
|
|
635
652
|
const val = this.textOf(text);
|
|
636
653
|
if (val) {
|
|
654
|
+
// Debug: log WordList items with spaces to check extraction
|
|
655
|
+
if (pageWordListItems.length < 3) {
|
|
656
|
+
console.log(`[WordList] Extracted text: "${val}" (length: ${val.length}, has spaces: ${val.includes(' ')})`);
|
|
657
|
+
console.log(`[WordList] Chars:`, Array.from(val)
|
|
658
|
+
.map((c) => `"${c}" (${c.charCodeAt(0)})`)
|
|
659
|
+
.join(', '));
|
|
660
|
+
}
|
|
637
661
|
pageWordListItems.push({
|
|
638
662
|
text: val,
|
|
639
663
|
image: item.Image || item.image || undefined,
|
|
@@ -646,15 +670,130 @@ class GridsetProcessor extends BaseProcessor {
|
|
|
646
670
|
// Track WordList AutoContent cells and their positions for "more" button placement
|
|
647
671
|
const wordListAutoContentCells = [];
|
|
648
672
|
let wordListCellIndex = 0;
|
|
673
|
+
// Helper function to find next available position in grid (auto-flow)
|
|
674
|
+
// Returns {x, y} for next available slot that can accommodate the given span
|
|
675
|
+
const findNextAvailablePosition = (width, height, gridLayout) => {
|
|
676
|
+
for (let y = 0; y < maxRows; y++) {
|
|
677
|
+
for (let x = 0; x <= maxCols - width; x++) {
|
|
678
|
+
// Check if this position and the required span area are all free
|
|
679
|
+
let fits = true;
|
|
680
|
+
for (let dy = 0; dy < height && y + dy < maxRows; dy++) {
|
|
681
|
+
for (let dx = 0; dx < width && x + dx < maxCols; dx++) {
|
|
682
|
+
if (gridLayout[y + dy][x + dx] !== null) {
|
|
683
|
+
fits = false;
|
|
684
|
+
break;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
if (!fits)
|
|
688
|
+
break;
|
|
689
|
+
}
|
|
690
|
+
if (fits) {
|
|
691
|
+
return { x, y };
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
// If no position found, return 0,0 (will be placed at first available)
|
|
696
|
+
return { x: 0, y: 0 };
|
|
697
|
+
};
|
|
698
|
+
// Helper function to find next available X position in a specific row
|
|
699
|
+
const findNextAvailableXInRow = (rowY, width, gridLayout) => {
|
|
700
|
+
for (let x = 0; x <= maxCols - width; x++) {
|
|
701
|
+
let fits = true;
|
|
702
|
+
for (let dx = 0; dx < width; dx++) {
|
|
703
|
+
if (gridLayout[rowY][x + dx] !== null) {
|
|
704
|
+
fits = false;
|
|
705
|
+
break;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
if (fits)
|
|
709
|
+
return x;
|
|
710
|
+
}
|
|
711
|
+
return 0;
|
|
712
|
+
};
|
|
713
|
+
const cellsWithExplicitPosition = [];
|
|
714
|
+
const cellsWithYOnly = [];
|
|
715
|
+
const cellsWithXOnly = [];
|
|
716
|
+
const cellsWithAutoFlow = [];
|
|
649
717
|
cellArr.forEach((cell, idx) => {
|
|
650
718
|
if (!cell || !cell.Content)
|
|
651
719
|
return;
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
720
|
+
const hasX = cell['@_X'] !== undefined;
|
|
721
|
+
const hasY = cell['@_Y'] !== undefined;
|
|
722
|
+
if (hasX && hasY) {
|
|
723
|
+
cellsWithExplicitPosition.push({ cell, idx });
|
|
724
|
+
}
|
|
725
|
+
else if (hasY && !hasX) {
|
|
726
|
+
cellsWithYOnly.push({ cell, idx });
|
|
727
|
+
}
|
|
728
|
+
else if (!hasY && hasX) {
|
|
729
|
+
cellsWithXOnly.push({ cell, idx });
|
|
730
|
+
}
|
|
731
|
+
else {
|
|
732
|
+
cellsWithAutoFlow.push({ cell, idx });
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
// Process cells in order: explicit -> Y-only -> X-only -> auto-flow
|
|
736
|
+
const allCellsToProcess = [
|
|
737
|
+
...cellsWithExplicitPosition,
|
|
738
|
+
...cellsWithYOnly,
|
|
739
|
+
...cellsWithXOnly,
|
|
740
|
+
...cellsWithAutoFlow,
|
|
741
|
+
];
|
|
742
|
+
allCellsToProcess.forEach(({ cell, idx }) => {
|
|
743
|
+
// Extract span information first
|
|
656
744
|
const colSpan = parseInt(String(cell['@_ColumnSpan'] || '1'), 10);
|
|
657
745
|
const rowSpan = parseInt(String(cell['@_RowSpan'] || '1'), 10);
|
|
746
|
+
// Determine position based on what attributes are present
|
|
747
|
+
const hasX = cell['@_X'] !== undefined;
|
|
748
|
+
const hasY = cell['@_Y'] !== undefined;
|
|
749
|
+
let cellX;
|
|
750
|
+
let cellY;
|
|
751
|
+
if (hasX && hasY) {
|
|
752
|
+
// Explicit position: both X and Y provided
|
|
753
|
+
// Grid 3 XML coordinates are already 0-based, use them directly
|
|
754
|
+
cellX = Math.max(0, parseInt(String(cell['@_X']), 10));
|
|
755
|
+
cellY = Math.max(0, parseInt(String(cell['@_Y']), 10));
|
|
756
|
+
}
|
|
757
|
+
else if (hasY && !hasX) {
|
|
758
|
+
// Y-only: auto-flow X in the specified row
|
|
759
|
+
// Grid 3 XML coordinates are already 0-based, use them directly
|
|
760
|
+
cellY = Math.max(0, parseInt(String(cell['@_Y']), 10));
|
|
761
|
+
cellX = findNextAvailableXInRow(cellY, colSpan, gridLayout);
|
|
762
|
+
}
|
|
763
|
+
else if (!hasY && hasX) {
|
|
764
|
+
// X-only: place at specified X in next available row
|
|
765
|
+
// Grid 3 XML coordinates are already 0-based, use them directly
|
|
766
|
+
cellX = Math.max(0, parseInt(String(cell['@_X']), 10));
|
|
767
|
+
// Find first row where this X position is available
|
|
768
|
+
cellY = 0;
|
|
769
|
+
let found = false;
|
|
770
|
+
for (let y = 0; y < maxRows; y++) {
|
|
771
|
+
let fits = true;
|
|
772
|
+
for (let dx = 0; dx < colSpan && cellX + dx < maxCols; dx++) {
|
|
773
|
+
if (gridLayout[y][cellX + dx] !== null) {
|
|
774
|
+
fits = false;
|
|
775
|
+
break;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
if (fits) {
|
|
779
|
+
cellY = y;
|
|
780
|
+
found = true;
|
|
781
|
+
break;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
if (!found) {
|
|
785
|
+
// No available row found, use auto-flow
|
|
786
|
+
const pos = findNextAvailablePosition(colSpan, rowSpan, gridLayout);
|
|
787
|
+
cellX = pos.x;
|
|
788
|
+
cellY = pos.y;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
else {
|
|
792
|
+
// No position: auto-flow both X and Y
|
|
793
|
+
const pos = findNextAvailablePosition(colSpan, rowSpan, gridLayout);
|
|
794
|
+
cellX = pos.x;
|
|
795
|
+
cellY = pos.y;
|
|
796
|
+
}
|
|
658
797
|
// Extract scan block number (1-8) for block scanning support
|
|
659
798
|
const scanBlock = parseInt(String(cell['@_ScanBlock'] || '1'), 10);
|
|
660
799
|
// Extract visibility from Grid 3's <Visibility> child element
|
|
@@ -797,6 +936,13 @@ class GridsetProcessor extends BaseProcessor {
|
|
|
797
936
|
y: cellY,
|
|
798
937
|
dynamicFiles,
|
|
799
938
|
}, entries) || undefined;
|
|
939
|
+
// Debug: log resolution for cells with images
|
|
940
|
+
if (declaredImageName && resolvedImageEntry) {
|
|
941
|
+
console.log(`[GridsetProcessor] Cell (${cellX + 1},${cellY + 1}) [XML coords]: ${declaredImageName} -> ${resolvedImageEntry}`);
|
|
942
|
+
}
|
|
943
|
+
else if (declaredImageName && !resolvedImageEntry) {
|
|
944
|
+
console.log(`[GridsetProcessor] Cell (${cellX + 1},${cellY + 1}) [XML coords]: ${declaredImageName} -> NOT FOUND`);
|
|
945
|
+
}
|
|
800
946
|
// Check if image is a symbol library reference
|
|
801
947
|
let symbolLibraryRef = null;
|
|
802
948
|
if (declaredImageName && isSymbolLibraryReference(declaredImageName)) {
|
|
@@ -47,6 +47,13 @@ function resolveGrid3CellImage(zip, args, zipEntries) {
|
|
|
47
47
|
const y = args.y;
|
|
48
48
|
const entries = new Set(listZipEntries(zip, zipEntries));
|
|
49
49
|
const has = (p) => entries.has(normalizeZipPathLocal(p));
|
|
50
|
+
// Debug logging for cells that fail to resolve
|
|
51
|
+
const shouldDebug = imageName?.startsWith('-') && x !== undefined && y !== undefined;
|
|
52
|
+
const debugLog = (msg) => {
|
|
53
|
+
if (shouldDebug) {
|
|
54
|
+
console.log(`[Resolver] ${baseDir} (${x},${y}) "${imageName}": ${msg}`);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
50
57
|
// Built-in resource like [grid3x]... (old format, not symbol library)
|
|
51
58
|
// Check this BEFORE general symbol references to avoid misclassification
|
|
52
59
|
if (imageName && imageName.startsWith('[')) {
|
|
@@ -77,6 +84,7 @@ function resolveGrid3CellImage(zip, args, zipEntries) {
|
|
|
77
84
|
// to be prefixed with the cell coordinates
|
|
78
85
|
if (imageName.startsWith('-') && x != null && y != null) {
|
|
79
86
|
const coordPrefixed = joinBaseDir(baseDir, `${x}-${y}${imageName}`);
|
|
87
|
+
debugLog(`trying coord-prefixed: ${coordPrefixed}, found: ${has(coordPrefixed)}`);
|
|
80
88
|
if (has(coordPrefixed))
|
|
81
89
|
return coordPrefixed;
|
|
82
90
|
}
|
|
@@ -119,12 +127,14 @@ function resolveGrid3CellImage(zip, args, zipEntries) {
|
|
|
119
127
|
`${x}-${y}.jpg`,
|
|
120
128
|
`${x}-${y}.png`,
|
|
121
129
|
].map((n) => joinBaseDir(baseDir, n));
|
|
130
|
+
debugLog(`trying candidates: ${candidates.filter(has).join(', ') || 'none found'}`);
|
|
122
131
|
for (const c of candidates) {
|
|
123
132
|
if (has(c))
|
|
124
133
|
return c;
|
|
125
134
|
}
|
|
126
135
|
}
|
|
127
136
|
}
|
|
137
|
+
debugLog(`NOT FOUND - returning null`);
|
|
128
138
|
return null;
|
|
129
139
|
}
|
|
130
140
|
/**
|
|
@@ -351,8 +351,8 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
351
351
|
if (typeof val === 'number')
|
|
352
352
|
return String(val);
|
|
353
353
|
if (typeof val === 'object') {
|
|
354
|
-
|
|
355
|
-
|
|
354
|
+
// Don't immediately return #text - it might be whitespace alongside structured content
|
|
355
|
+
// Process structured format first: <p><s><r>text</r></s></p>
|
|
356
356
|
// Handle Grid3 structured format <p><s><r>text</r></s></p>
|
|
357
357
|
// Can start at p, s, or r level
|
|
358
358
|
const parts = [];
|
|
@@ -368,8 +368,17 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
368
368
|
}
|
|
369
369
|
continue;
|
|
370
370
|
}
|
|
371
|
-
if (typeof r === 'object' && r !== null
|
|
372
|
-
|
|
371
|
+
if (typeof r === 'object' && r !== null) {
|
|
372
|
+
// Check for #text (regular text) or #cdata (CDATA sections)
|
|
373
|
+
if ('#text' in r) {
|
|
374
|
+
parts.push(String(r['#text']));
|
|
375
|
+
}
|
|
376
|
+
else if ('#cdata' in r) {
|
|
377
|
+
parts.push(String(r['#cdata']));
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
parts.push(String(r));
|
|
381
|
+
}
|
|
373
382
|
}
|
|
374
383
|
else {
|
|
375
384
|
parts.push(String(r));
|
|
@@ -422,7 +431,15 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
422
431
|
}
|
|
423
432
|
const password = this.getGridsetPassword(filePathOrBuffer);
|
|
424
433
|
const entries = (0, password_1.getZipEntriesFromAdapter)(zipResult.zip, password);
|
|
425
|
-
const
|
|
434
|
+
const options = {
|
|
435
|
+
ignoreAttributes: false,
|
|
436
|
+
ignoreDeclaration: true,
|
|
437
|
+
parseTagValue: false,
|
|
438
|
+
trimValues: false,
|
|
439
|
+
textNodeName: '#text',
|
|
440
|
+
cdataProp: '#cdata',
|
|
441
|
+
};
|
|
442
|
+
const parser = new fast_xml_parser_1.XMLParser(options);
|
|
426
443
|
const isEncryptedArchive = typeof filePathOrBuffer === 'string' && filePathOrBuffer.toLowerCase().endsWith('.gridsetx');
|
|
427
444
|
const encryptedContentPassword = this.getGridsetPassword(filePathOrBuffer);
|
|
428
445
|
// Initialize metadata
|
|
@@ -660,6 +677,13 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
660
677
|
if (text) {
|
|
661
678
|
const val = this.textOf(text);
|
|
662
679
|
if (val) {
|
|
680
|
+
// Debug: log WordList items with spaces to check extraction
|
|
681
|
+
if (pageWordListItems.length < 3) {
|
|
682
|
+
console.log(`[WordList] Extracted text: "${val}" (length: ${val.length}, has spaces: ${val.includes(' ')})`);
|
|
683
|
+
console.log(`[WordList] Chars:`, Array.from(val)
|
|
684
|
+
.map((c) => `"${c}" (${c.charCodeAt(0)})`)
|
|
685
|
+
.join(', '));
|
|
686
|
+
}
|
|
663
687
|
pageWordListItems.push({
|
|
664
688
|
text: val,
|
|
665
689
|
image: item.Image || item.image || undefined,
|
|
@@ -672,15 +696,130 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
672
696
|
// Track WordList AutoContent cells and their positions for "more" button placement
|
|
673
697
|
const wordListAutoContentCells = [];
|
|
674
698
|
let wordListCellIndex = 0;
|
|
699
|
+
// Helper function to find next available position in grid (auto-flow)
|
|
700
|
+
// Returns {x, y} for next available slot that can accommodate the given span
|
|
701
|
+
const findNextAvailablePosition = (width, height, gridLayout) => {
|
|
702
|
+
for (let y = 0; y < maxRows; y++) {
|
|
703
|
+
for (let x = 0; x <= maxCols - width; x++) {
|
|
704
|
+
// Check if this position and the required span area are all free
|
|
705
|
+
let fits = true;
|
|
706
|
+
for (let dy = 0; dy < height && y + dy < maxRows; dy++) {
|
|
707
|
+
for (let dx = 0; dx < width && x + dx < maxCols; dx++) {
|
|
708
|
+
if (gridLayout[y + dy][x + dx] !== null) {
|
|
709
|
+
fits = false;
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
if (!fits)
|
|
714
|
+
break;
|
|
715
|
+
}
|
|
716
|
+
if (fits) {
|
|
717
|
+
return { x, y };
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
// If no position found, return 0,0 (will be placed at first available)
|
|
722
|
+
return { x: 0, y: 0 };
|
|
723
|
+
};
|
|
724
|
+
// Helper function to find next available X position in a specific row
|
|
725
|
+
const findNextAvailableXInRow = (rowY, width, gridLayout) => {
|
|
726
|
+
for (let x = 0; x <= maxCols - width; x++) {
|
|
727
|
+
let fits = true;
|
|
728
|
+
for (let dx = 0; dx < width; dx++) {
|
|
729
|
+
if (gridLayout[rowY][x + dx] !== null) {
|
|
730
|
+
fits = false;
|
|
731
|
+
break;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
if (fits)
|
|
735
|
+
return x;
|
|
736
|
+
}
|
|
737
|
+
return 0;
|
|
738
|
+
};
|
|
739
|
+
const cellsWithExplicitPosition = [];
|
|
740
|
+
const cellsWithYOnly = [];
|
|
741
|
+
const cellsWithXOnly = [];
|
|
742
|
+
const cellsWithAutoFlow = [];
|
|
675
743
|
cellArr.forEach((cell, idx) => {
|
|
676
744
|
if (!cell || !cell.Content)
|
|
677
745
|
return;
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
746
|
+
const hasX = cell['@_X'] !== undefined;
|
|
747
|
+
const hasY = cell['@_Y'] !== undefined;
|
|
748
|
+
if (hasX && hasY) {
|
|
749
|
+
cellsWithExplicitPosition.push({ cell, idx });
|
|
750
|
+
}
|
|
751
|
+
else if (hasY && !hasX) {
|
|
752
|
+
cellsWithYOnly.push({ cell, idx });
|
|
753
|
+
}
|
|
754
|
+
else if (!hasY && hasX) {
|
|
755
|
+
cellsWithXOnly.push({ cell, idx });
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
cellsWithAutoFlow.push({ cell, idx });
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
// Process cells in order: explicit -> Y-only -> X-only -> auto-flow
|
|
762
|
+
const allCellsToProcess = [
|
|
763
|
+
...cellsWithExplicitPosition,
|
|
764
|
+
...cellsWithYOnly,
|
|
765
|
+
...cellsWithXOnly,
|
|
766
|
+
...cellsWithAutoFlow,
|
|
767
|
+
];
|
|
768
|
+
allCellsToProcess.forEach(({ cell, idx }) => {
|
|
769
|
+
// Extract span information first
|
|
682
770
|
const colSpan = parseInt(String(cell['@_ColumnSpan'] || '1'), 10);
|
|
683
771
|
const rowSpan = parseInt(String(cell['@_RowSpan'] || '1'), 10);
|
|
772
|
+
// Determine position based on what attributes are present
|
|
773
|
+
const hasX = cell['@_X'] !== undefined;
|
|
774
|
+
const hasY = cell['@_Y'] !== undefined;
|
|
775
|
+
let cellX;
|
|
776
|
+
let cellY;
|
|
777
|
+
if (hasX && hasY) {
|
|
778
|
+
// Explicit position: both X and Y provided
|
|
779
|
+
// Grid 3 XML coordinates are already 0-based, use them directly
|
|
780
|
+
cellX = Math.max(0, parseInt(String(cell['@_X']), 10));
|
|
781
|
+
cellY = Math.max(0, parseInt(String(cell['@_Y']), 10));
|
|
782
|
+
}
|
|
783
|
+
else if (hasY && !hasX) {
|
|
784
|
+
// Y-only: auto-flow X in the specified row
|
|
785
|
+
// Grid 3 XML coordinates are already 0-based, use them directly
|
|
786
|
+
cellY = Math.max(0, parseInt(String(cell['@_Y']), 10));
|
|
787
|
+
cellX = findNextAvailableXInRow(cellY, colSpan, gridLayout);
|
|
788
|
+
}
|
|
789
|
+
else if (!hasY && hasX) {
|
|
790
|
+
// X-only: place at specified X in next available row
|
|
791
|
+
// Grid 3 XML coordinates are already 0-based, use them directly
|
|
792
|
+
cellX = Math.max(0, parseInt(String(cell['@_X']), 10));
|
|
793
|
+
// Find first row where this X position is available
|
|
794
|
+
cellY = 0;
|
|
795
|
+
let found = false;
|
|
796
|
+
for (let y = 0; y < maxRows; y++) {
|
|
797
|
+
let fits = true;
|
|
798
|
+
for (let dx = 0; dx < colSpan && cellX + dx < maxCols; dx++) {
|
|
799
|
+
if (gridLayout[y][cellX + dx] !== null) {
|
|
800
|
+
fits = false;
|
|
801
|
+
break;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
if (fits) {
|
|
805
|
+
cellY = y;
|
|
806
|
+
found = true;
|
|
807
|
+
break;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
if (!found) {
|
|
811
|
+
// No available row found, use auto-flow
|
|
812
|
+
const pos = findNextAvailablePosition(colSpan, rowSpan, gridLayout);
|
|
813
|
+
cellX = pos.x;
|
|
814
|
+
cellY = pos.y;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
// No position: auto-flow both X and Y
|
|
819
|
+
const pos = findNextAvailablePosition(colSpan, rowSpan, gridLayout);
|
|
820
|
+
cellX = pos.x;
|
|
821
|
+
cellY = pos.y;
|
|
822
|
+
}
|
|
684
823
|
// Extract scan block number (1-8) for block scanning support
|
|
685
824
|
const scanBlock = parseInt(String(cell['@_ScanBlock'] || '1'), 10);
|
|
686
825
|
// Extract visibility from Grid 3's <Visibility> child element
|
|
@@ -823,6 +962,13 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
823
962
|
y: cellY,
|
|
824
963
|
dynamicFiles,
|
|
825
964
|
}, entries) || undefined;
|
|
965
|
+
// Debug: log resolution for cells with images
|
|
966
|
+
if (declaredImageName && resolvedImageEntry) {
|
|
967
|
+
console.log(`[GridsetProcessor] Cell (${cellX + 1},${cellY + 1}) [XML coords]: ${declaredImageName} -> ${resolvedImageEntry}`);
|
|
968
|
+
}
|
|
969
|
+
else if (declaredImageName && !resolvedImageEntry) {
|
|
970
|
+
console.log(`[GridsetProcessor] Cell (${cellX + 1},${cellY + 1}) [XML coords]: ${declaredImageName} -> NOT FOUND`);
|
|
971
|
+
}
|
|
826
972
|
// Check if image is a symbol library reference
|
|
827
973
|
let symbolLibraryRef = null;
|
|
828
974
|
if (declaredImageName && (0, resolver_2.isSymbolLibraryReference)(declaredImageName)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@willwade/aac-processors",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "A comprehensive TypeScript library for processing AAC (Augmentative and Alternative Communication) file formats with translation support",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"browser": "dist/browser/index.browser.js",
|
|
@@ -80,13 +80,11 @@
|
|
|
80
80
|
"files": [
|
|
81
81
|
"dist/**/*",
|
|
82
82
|
"docs/**/*",
|
|
83
|
-
"examples/**/*",
|
|
84
83
|
"README.md",
|
|
85
84
|
"LICENSE"
|
|
86
85
|
],
|
|
87
86
|
"directories": {
|
|
88
87
|
"doc": "docs",
|
|
89
|
-
"example": "examples",
|
|
90
88
|
"test": "test"
|
|
91
89
|
},
|
|
92
90
|
"scripts": {
|
package/examples/.coverage
DELETED
|
Binary file
|
package/examples/.keep
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
package/examples/README.md
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# AAC Processors Example Pagesets
|
|
2
|
-
|
|
3
|
-
This directory contains example AAC pagesets in various formats used for testing and demonstration purposes.
|
|
4
|
-
|
|
5
|
-
## Available Pagesets
|
|
6
|
-
|
|
7
|
-
### Grid3 Format (.gridset)
|
|
8
|
-
- **example.gridset** - Main example pageset with multiple grids and wordlists
|
|
9
|
-
- **example-images.gridset** - Example pageset with embedded images
|
|
10
|
-
|
|
11
|
-
### Snap Format (.spb, .sps)
|
|
12
|
-
- **example.spb** - Snap pageset (binary format)
|
|
13
|
-
- **example.sps** - Snap pageset (alternative format)
|
|
14
|
-
|
|
15
|
-
### TouchChat Format (.ce)
|
|
16
|
-
- **example.ce** - TouchChat pageset
|
|
17
|
-
|
|
18
|
-
### OBF/OBZ Format (.obf, .obz)
|
|
19
|
-
- **example.obf** - OBF pageset (JSON-based)
|
|
20
|
-
- **example.obz** - OBZ pageset (compressed)
|
|
21
|
-
|
|
22
|
-
**obf/** - Directory containing validation test samples from the obf-node project:
|
|
23
|
-
- **simple.obf** - Simple, valid OBF file for basic validation tests
|
|
24
|
-
- **aboutme.json** - Invalid OBF (missing locale field) for error testing
|
|
25
|
-
- **hash.json** - Non-OBF JSON structure for format detection tests
|
|
26
|
-
- **array.json** - JSON array (not object) for structure validation tests
|
|
27
|
-
- **links.obz** - OBZ package with links for zip archive validation
|
|
28
|
-
|
|
29
|
-
### Asterics Grid Format (.grd)
|
|
30
|
-
- **example.grd** - Asterics Grid pageset
|
|
31
|
-
- **example2.grd** - Alternative Asterics Grid pageset
|
|
32
|
-
|
|
33
|
-
### DOT Format (.dot)
|
|
34
|
-
- **example.dot** - Simple DOT format pageset
|
|
35
|
-
- **communikate.dot** - Communikate DOT format pageset
|
|
36
|
-
|
|
37
|
-
### OPML Format (.opml)
|
|
38
|
-
- **example.opml** - OPML pageset
|
|
39
|
-
|
|
40
|
-
### Styled Output
|
|
41
|
-
- **styled-output/** - Directory containing example pagesets with styling applied
|
|
42
|
-
|
|
43
|
-
## Usage
|
|
44
|
-
|
|
45
|
-
These pagesets are used by:
|
|
46
|
-
- Unit tests in the main test suite
|
|
47
|
-
- Demo scripts in the `scripts/` directory
|
|
48
|
-
- Integration examples
|
|
49
|
-
|
|
50
|
-
To run demo scripts that use these pagesets, see the [scripts/README.md](../scripts/README.md).
|
|
51
|
-
|
|
52
|
-
## Browser Demo (Vite)
|
|
53
|
-
|
|
54
|
-
For browser testing, use the Vite demo in `examples/vitedemo`. It bundles the
|
|
55
|
-
library and exercises real processors in a browser environment.
|