@willwade/aac-processors 0.0.14 → 0.0.15
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 +41 -2
- package/dist/applePanels.d.ts +6 -0
- package/dist/applePanels.js +13 -0
- package/dist/astericsGrid.d.ts +6 -0
- package/dist/astericsGrid.js +13 -0
- package/dist/dot.d.ts +6 -0
- package/dist/dot.js +13 -0
- package/dist/excel.d.ts +6 -0
- package/dist/excel.js +13 -0
- package/dist/gridset.d.ts +17 -0
- package/dist/gridset.js +130 -0
- package/dist/index.d.ts +23 -2
- package/dist/index.js +36 -7
- package/dist/obf.d.ts +7 -0
- package/dist/obf.js +15 -0
- package/dist/obfset.d.ts +6 -0
- package/dist/obfset.js +13 -0
- package/dist/opml.d.ts +6 -0
- package/dist/opml.js +13 -0
- package/dist/processors/index.d.ts +8 -18
- package/dist/processors/index.js +9 -175
- package/dist/snap.d.ts +7 -0
- package/dist/snap.js +24 -0
- package/dist/touchchat.d.ts +7 -0
- package/dist/touchchat.js +16 -0
- package/dist/translation.d.ts +13 -0
- package/dist/translation.js +21 -0
- package/dist/validation.d.ts +13 -0
- package/dist/validation.js +28 -0
- package/package.json +58 -4
- package/dist/utilities/screenshotConverter.d.ts +0 -69
- package/dist/utilities/screenshotConverter.js +0 -453
|
@@ -1,453 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.ScreenshotConverter = void 0;
|
|
7
|
-
const treeStructure_1 = require("../core/treeStructure");
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
class ScreenshotConverter {
|
|
10
|
-
/**
|
|
11
|
-
* Parse filename to extract page hierarchy and names
|
|
12
|
-
* Examples:
|
|
13
|
-
* - "Home.png" → pageName: "Home", parentPath: ""
|
|
14
|
-
* - "Home->Fragen.png" → pageName: "Fragen", parentPath: "Home"
|
|
15
|
-
* - "Home->Settings->Profile.jpg" → pageName: "Profile", parentPath: "Home->Settings"
|
|
16
|
-
*/
|
|
17
|
-
static parseFilename(filename, delimiter = '->') {
|
|
18
|
-
const baseName = path_1.default.parse(filename).name;
|
|
19
|
-
const parts = baseName.split(delimiter).map((part) => part.trim());
|
|
20
|
-
return {
|
|
21
|
-
pageName: parts[parts.length - 1] || baseName,
|
|
22
|
-
parentPath: parts.slice(0, -1).join(delimiter),
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Build page hierarchy from an array of screenshots
|
|
27
|
-
*/
|
|
28
|
-
static buildPageHierarchy(screenshots) {
|
|
29
|
-
const hierarchy = {};
|
|
30
|
-
const delimiter = this.defaultOptions.filenameDelimiter || '->';
|
|
31
|
-
// First pass: parse all filenames
|
|
32
|
-
screenshots.forEach((screenshot, index) => {
|
|
33
|
-
const { pageName, parentPath } = this.parseFilename(screenshot.filename, delimiter);
|
|
34
|
-
screenshot.pageName = pageName;
|
|
35
|
-
screenshot.parentPath = parentPath;
|
|
36
|
-
const pageId = `page_${index}`;
|
|
37
|
-
hierarchy[pageId] = {
|
|
38
|
-
page: screenshot,
|
|
39
|
-
children: [],
|
|
40
|
-
parent: undefined,
|
|
41
|
-
};
|
|
42
|
-
});
|
|
43
|
-
// Second pass: establish parent-child relationships
|
|
44
|
-
Object.entries(hierarchy).forEach(([pageId, entry]) => {
|
|
45
|
-
const parentPath = entry.page.parentPath;
|
|
46
|
-
if (parentPath) {
|
|
47
|
-
// Find parent by matching the full path
|
|
48
|
-
const parent = Object.values(hierarchy).find((h) => h.page.pageName === parentPath.split(delimiter).pop());
|
|
49
|
-
if (parent) {
|
|
50
|
-
entry.parent = Object.keys(hierarchy).find((key) => hierarchy[key] === parent);
|
|
51
|
-
parent.children.push(pageId);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
return hierarchy;
|
|
56
|
-
}
|
|
57
|
-
static parseOCRText(ocrResult) {
|
|
58
|
-
const lines = ocrResult.split('\n').filter((line) => line.trim());
|
|
59
|
-
const cells = [];
|
|
60
|
-
const categories = new Set();
|
|
61
|
-
// Skip header metadata
|
|
62
|
-
const contentStart = lines.findIndex((line) => line.includes('ich möchte') && !line.includes('ich möchte ich'));
|
|
63
|
-
if (contentStart === -1) {
|
|
64
|
-
// Try another approach if the first pattern doesn't match
|
|
65
|
-
const gridStart = lines.findIndex((line) => line.includes('ich möchte') && line.split(/\s+/).length > 2);
|
|
66
|
-
if (gridStart === -1)
|
|
67
|
-
return { rows: 6, cols: 11, cells: [], categories: [] };
|
|
68
|
-
}
|
|
69
|
-
// Find the line with the grid content (usually has tab-separated values)
|
|
70
|
-
const gridLineIndex = lines.findIndex((line) => line.includes('ich möchte') && line.includes('\t') && line.split(/\s+/).length > 5);
|
|
71
|
-
let rows = 6;
|
|
72
|
-
let cols = 11;
|
|
73
|
-
// If we found a properly formatted grid line
|
|
74
|
-
if (gridLineIndex !== -1) {
|
|
75
|
-
const gridLine = lines[gridLineIndex];
|
|
76
|
-
// Split by tabs to get individual cell values
|
|
77
|
-
const tokens = gridLine
|
|
78
|
-
.split('\t')
|
|
79
|
-
.map((t) => t.trim())
|
|
80
|
-
.filter((t) => t);
|
|
81
|
-
cols = Math.max(tokens.length, cols);
|
|
82
|
-
// Create first row from the main grid line
|
|
83
|
-
tokens.forEach((token, col) => {
|
|
84
|
-
const isCategory = this.isCategoryToken(token);
|
|
85
|
-
const isNavigation = this.isNavigationToken(token);
|
|
86
|
-
const isEmpty = !token || token === '...' || token === '';
|
|
87
|
-
if (isCategory)
|
|
88
|
-
categories.add(token);
|
|
89
|
-
if (!isEmpty) {
|
|
90
|
-
cells.push({
|
|
91
|
-
text: token,
|
|
92
|
-
row: 0,
|
|
93
|
-
col,
|
|
94
|
-
isCategory,
|
|
95
|
-
isNavigation,
|
|
96
|
-
isEmpty,
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
// Process subsequent lines
|
|
101
|
-
let currentRow = 1;
|
|
102
|
-
for (let i = gridLineIndex + 1; i < lines.length; i++) {
|
|
103
|
-
const line = lines[i].trim();
|
|
104
|
-
if (!line)
|
|
105
|
-
continue;
|
|
106
|
-
// Skip lines that look like headers or metadata
|
|
107
|
-
if (line.match(/^\d+:\d+/) || line.match(/[A-Z][a-z]{2},\s+\d+/) || line.includes('%'))
|
|
108
|
-
continue;
|
|
109
|
-
// Skip duplicate "ich möchte" at start
|
|
110
|
-
if (line === 'ich möchte' && currentRow === 1) {
|
|
111
|
-
currentRow = 0;
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
const tokens = line
|
|
115
|
-
.split('\t')
|
|
116
|
-
.map((t) => t.trim())
|
|
117
|
-
.filter((t) => t);
|
|
118
|
-
tokens.forEach((token, col) => {
|
|
119
|
-
const isCategory = this.isCategoryToken(token);
|
|
120
|
-
const isNavigation = this.isNavigationToken(token);
|
|
121
|
-
const isEmpty = !token || token === '...' || token === '';
|
|
122
|
-
if (isCategory)
|
|
123
|
-
categories.add(token);
|
|
124
|
-
if (!isEmpty) {
|
|
125
|
-
cells.push({
|
|
126
|
-
text: token,
|
|
127
|
-
row: currentRow,
|
|
128
|
-
col,
|
|
129
|
-
isCategory,
|
|
130
|
-
isNavigation,
|
|
131
|
-
isEmpty,
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
currentRow++;
|
|
136
|
-
if (currentRow >= rows)
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
// Fallback: simple whitespace parsing for unstructured OCR
|
|
142
|
-
let currentRow = 0;
|
|
143
|
-
lines.forEach((line, _lineIndex) => {
|
|
144
|
-
if (!line.trim())
|
|
145
|
-
return;
|
|
146
|
-
// Skip metadata
|
|
147
|
-
if (line.includes('%') || line.match(/\d+:\d+/) || line.match(/[A-Z][a-z]{2},\s+\d+/))
|
|
148
|
-
return;
|
|
149
|
-
const tokens = line.trim().split(/\s+/);
|
|
150
|
-
tokens.forEach((token, tokenIndex) => {
|
|
151
|
-
if (tokenIndex >= cols)
|
|
152
|
-
return; // Skip if beyond expected columns
|
|
153
|
-
const isCategory = this.isCategoryToken(token);
|
|
154
|
-
const isNavigation = this.isNavigationToken(token);
|
|
155
|
-
const isEmpty = !token || token.trim() === '' || token === '...';
|
|
156
|
-
if (isCategory)
|
|
157
|
-
categories.add(token);
|
|
158
|
-
if (!isEmpty) {
|
|
159
|
-
cells.push({
|
|
160
|
-
text: token,
|
|
161
|
-
row: currentRow,
|
|
162
|
-
col: tokenIndex,
|
|
163
|
-
isCategory,
|
|
164
|
-
isNavigation,
|
|
165
|
-
isEmpty,
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
currentRow++;
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
// Auto-detect actual grid dimensions
|
|
173
|
-
if (cells.length > 0) {
|
|
174
|
-
rows = Math.max(...cells.map((c) => c.row)) + 1;
|
|
175
|
-
cols = Math.max(...cells.map((c) => c.col)) + 1;
|
|
176
|
-
}
|
|
177
|
-
return {
|
|
178
|
-
rows,
|
|
179
|
-
cols,
|
|
180
|
-
cells,
|
|
181
|
-
categories: Array.from(categories),
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
static isCategoryToken(token) {
|
|
185
|
-
const knownCategories = [
|
|
186
|
-
// English categories
|
|
187
|
-
'Questions',
|
|
188
|
-
'Meetings',
|
|
189
|
-
'Praise',
|
|
190
|
-
'Complaints',
|
|
191
|
-
'Phrases',
|
|
192
|
-
'Conversations',
|
|
193
|
-
'Verbs',
|
|
194
|
-
'People',
|
|
195
|
-
'Messages',
|
|
196
|
-
'Properties',
|
|
197
|
-
'Feelings',
|
|
198
|
-
'Actions',
|
|
199
|
-
'Activities',
|
|
200
|
-
'Food',
|
|
201
|
-
'Drink',
|
|
202
|
-
'Colors',
|
|
203
|
-
'Shapes',
|
|
204
|
-
'Settings',
|
|
205
|
-
'Home',
|
|
206
|
-
'Back',
|
|
207
|
-
'Next',
|
|
208
|
-
'Menu',
|
|
209
|
-
// German categories
|
|
210
|
-
'Fragen',
|
|
211
|
-
'Treffen',
|
|
212
|
-
'Lob',
|
|
213
|
-
'Beschwerde',
|
|
214
|
-
'Sprüche',
|
|
215
|
-
'Gespräche',
|
|
216
|
-
'Verben',
|
|
217
|
-
'Leute',
|
|
218
|
-
'Mitteilungen',
|
|
219
|
-
'Eigenschaften',
|
|
220
|
-
'Gefühle',
|
|
221
|
-
'Spielen',
|
|
222
|
-
'Multimedia',
|
|
223
|
-
'Essen',
|
|
224
|
-
'Trinken',
|
|
225
|
-
'Farben/Formen',
|
|
226
|
-
];
|
|
227
|
-
// Check for known categories
|
|
228
|
-
if (knownCategories.includes(token)) {
|
|
229
|
-
return true;
|
|
230
|
-
}
|
|
231
|
-
// Check for common category patterns
|
|
232
|
-
const categoryPatterns = [
|
|
233
|
-
/.*Questions?$/,
|
|
234
|
-
/.*Category.*/,
|
|
235
|
-
/.*Menu.*/,
|
|
236
|
-
/.*Settings?$/,
|
|
237
|
-
/.*Options?$/,
|
|
238
|
-
/谈话/i, // Chinese
|
|
239
|
-
/質問/i, // Japanese
|
|
240
|
-
/preguntas/i, // Spanish
|
|
241
|
-
];
|
|
242
|
-
return categoryPatterns.some((pattern) => pattern.test(token));
|
|
243
|
-
}
|
|
244
|
-
static isNavigationToken(token) {
|
|
245
|
-
const navTokens = [
|
|
246
|
-
// English
|
|
247
|
-
'Home',
|
|
248
|
-
'Back',
|
|
249
|
-
'Next',
|
|
250
|
-
'Previous',
|
|
251
|
-
'Menu',
|
|
252
|
-
'Settings',
|
|
253
|
-
'Exit',
|
|
254
|
-
'Close',
|
|
255
|
-
'OK',
|
|
256
|
-
'Cancel',
|
|
257
|
-
'Yes',
|
|
258
|
-
'No',
|
|
259
|
-
'Help',
|
|
260
|
-
'Search',
|
|
261
|
-
// German
|
|
262
|
-
'Home',
|
|
263
|
-
'Zurück',
|
|
264
|
-
'Weiter',
|
|
265
|
-
'Menü',
|
|
266
|
-
'Einstellungen',
|
|
267
|
-
'Beenden',
|
|
268
|
-
'Schließen',
|
|
269
|
-
'Hilfe',
|
|
270
|
-
'Suche',
|
|
271
|
-
// Navigation indicators
|
|
272
|
-
'←',
|
|
273
|
-
'→',
|
|
274
|
-
'↑',
|
|
275
|
-
'↓',
|
|
276
|
-
'◀',
|
|
277
|
-
'▶',
|
|
278
|
-
'▲',
|
|
279
|
-
'▼',
|
|
280
|
-
];
|
|
281
|
-
return (navTokens.includes(token) || token === '←' || token === '→' || token === '↑' || token === '↓');
|
|
282
|
-
}
|
|
283
|
-
static convertToAACPage(screenshotPage, pageHierarchy, options) {
|
|
284
|
-
const opts = {
|
|
285
|
-
...this.defaultOptions,
|
|
286
|
-
...options,
|
|
287
|
-
};
|
|
288
|
-
const buttons = [];
|
|
289
|
-
// Convert cells to AAC buttons
|
|
290
|
-
screenshotPage.grid.cells.forEach((cell) => {
|
|
291
|
-
if (cell.isEmpty && !opts.includeEmptyCells)
|
|
292
|
-
return;
|
|
293
|
-
const button = new treeStructure_1.AACButton({
|
|
294
|
-
id: `cell_${cell.row}_${cell.col}`,
|
|
295
|
-
label: cell.text,
|
|
296
|
-
message: cell.text,
|
|
297
|
-
style: {
|
|
298
|
-
backgroundColor: cell.isCategory ? '#4CAF50' : cell.isNavigation ? '#2196F3' : '#FFFFFF',
|
|
299
|
-
fontColor: cell.isCategory || cell.isNavigation ? '#FFFFFF' : '#000000',
|
|
300
|
-
borderColor: '#CCCCCC',
|
|
301
|
-
borderWidth: 1,
|
|
302
|
-
},
|
|
303
|
-
semanticAction: this.createSemanticAction(cell, screenshotPage, pageHierarchy, opts),
|
|
304
|
-
x: cell.col,
|
|
305
|
-
y: cell.row,
|
|
306
|
-
});
|
|
307
|
-
buttons.push(button);
|
|
308
|
-
});
|
|
309
|
-
return new treeStructure_1.AACPage({
|
|
310
|
-
id: screenshotPage.pageName || 'screenshot_page',
|
|
311
|
-
name: screenshotPage.pageTitle || screenshotPage.pageName || 'Screenshot Page',
|
|
312
|
-
buttons,
|
|
313
|
-
grid: {
|
|
314
|
-
columns: screenshotPage.grid.cols,
|
|
315
|
-
rows: screenshotPage.grid.rows,
|
|
316
|
-
},
|
|
317
|
-
style: {
|
|
318
|
-
backgroundColor: '#F5F5F5',
|
|
319
|
-
},
|
|
320
|
-
parentId: null,
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
static createSemanticAction(cell, screenshotPage, pageHierarchy, options) {
|
|
324
|
-
if (cell.isEmpty)
|
|
325
|
-
return undefined;
|
|
326
|
-
const _opts = {
|
|
327
|
-
...this.defaultOptions,
|
|
328
|
-
...options,
|
|
329
|
-
};
|
|
330
|
-
if (cell.isCategory) {
|
|
331
|
-
// Try to find target page in hierarchy based on category name
|
|
332
|
-
let targetId = `category_${cell.text.toLowerCase().replace(/\s+/g, '_')}`;
|
|
333
|
-
if (pageHierarchy) {
|
|
334
|
-
// Look for a page that matches this category
|
|
335
|
-
const matchingPage = Object.values(pageHierarchy).find((h) => h.page.pageName?.toLowerCase() === cell.text.toLowerCase());
|
|
336
|
-
if (matchingPage) {
|
|
337
|
-
targetId = matchingPage.page.pageName || targetId;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
return {
|
|
341
|
-
category: treeStructure_1.AACSemanticCategory.NAVIGATION,
|
|
342
|
-
intent: treeStructure_1.AACSemanticIntent.NAVIGATE_TO,
|
|
343
|
-
targetId,
|
|
344
|
-
parameters: { category: cell.text },
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
if (cell.isNavigation) {
|
|
348
|
-
const text = cell.text.toLowerCase();
|
|
349
|
-
// Home navigation
|
|
350
|
-
if (text === 'home' || text === '⌂') {
|
|
351
|
-
return {
|
|
352
|
-
category: treeStructure_1.AACSemanticCategory.NAVIGATION,
|
|
353
|
-
intent: treeStructure_1.AACSemanticIntent.GO_HOME,
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
// Back navigation
|
|
357
|
-
if (text === 'back' || text === 'zurück' || text === '←' || text === '◀') {
|
|
358
|
-
return {
|
|
359
|
-
category: treeStructure_1.AACSemanticCategory.NAVIGATION,
|
|
360
|
-
intent: treeStructure_1.AACSemanticIntent.GO_BACK,
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
// Next/forward navigation
|
|
364
|
-
if (text === 'next' || text === 'weiter' || text === '→' || text === '▶') {
|
|
365
|
-
// If we have hierarchy, navigate to parent
|
|
366
|
-
if (pageHierarchy && screenshotPage.parentPath) {
|
|
367
|
-
const parentId = Object.keys(pageHierarchy).find((key) => pageHierarchy[key].page.pageName === screenshotPage.parentPath?.split('->').pop());
|
|
368
|
-
if (parentId) {
|
|
369
|
-
return {
|
|
370
|
-
category: treeStructure_1.AACSemanticCategory.NAVIGATION,
|
|
371
|
-
intent: treeStructure_1.AACSemanticIntent.NAVIGATE_TO,
|
|
372
|
-
targetId: parentId,
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
return {
|
|
377
|
-
category: treeStructure_1.AACSemanticCategory.NAVIGATION,
|
|
378
|
-
intent: treeStructure_1.AACSemanticIntent.NAVIGATE_TO,
|
|
379
|
-
targetId: 'next_page',
|
|
380
|
-
parameters: { direction: 'next' },
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
// Menu navigation
|
|
384
|
-
if (text === 'menu' || text === 'menü') {
|
|
385
|
-
return {
|
|
386
|
-
category: treeStructure_1.AACSemanticCategory.NAVIGATION,
|
|
387
|
-
intent: treeStructure_1.AACSemanticIntent.NAVIGATE_TO,
|
|
388
|
-
targetId: 'main_menu',
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
// Default to speaking the text
|
|
393
|
-
return {
|
|
394
|
-
category: treeStructure_1.AACSemanticCategory.COMMUNICATION,
|
|
395
|
-
intent: treeStructure_1.AACSemanticIntent.SPEAK_IMMEDIATE,
|
|
396
|
-
text: cell.text,
|
|
397
|
-
richText: {
|
|
398
|
-
text: cell.text,
|
|
399
|
-
},
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
static convertToAACTree(screenshotPages, options) {
|
|
403
|
-
const opts = { ...this.defaultOptions, ...options };
|
|
404
|
-
const tree = new treeStructure_1.AACTree();
|
|
405
|
-
// Build page hierarchy
|
|
406
|
-
const pageHierarchy = this.buildPageHierarchy(screenshotPages);
|
|
407
|
-
// Set metadata on tree
|
|
408
|
-
tree.version = '1.0';
|
|
409
|
-
tree.metadata = {
|
|
410
|
-
name: 'Screenshot Conversion',
|
|
411
|
-
author: 'AAC Processors',
|
|
412
|
-
description: 'Converted from screenshot images',
|
|
413
|
-
language: opts.language,
|
|
414
|
-
};
|
|
415
|
-
// Convert each screenshot page to AAC page
|
|
416
|
-
screenshotPages.forEach((screenshotPage, index) => {
|
|
417
|
-
const page = this.convertToAACPage(screenshotPage, pageHierarchy, opts);
|
|
418
|
-
// Ensure unique ID by using page name if available
|
|
419
|
-
if (screenshotPage.pageName) {
|
|
420
|
-
page.id = this.sanitizePageId(screenshotPage.pageName);
|
|
421
|
-
}
|
|
422
|
-
else {
|
|
423
|
-
page.id = `screenshot_page_${index}`;
|
|
424
|
-
}
|
|
425
|
-
tree.addPage(page);
|
|
426
|
-
});
|
|
427
|
-
// Set root page to the one with no parent
|
|
428
|
-
const rootPage = Object.entries(pageHierarchy).find(([_, entry]) => !entry.parent);
|
|
429
|
-
if (rootPage) {
|
|
430
|
-
const rootPageId = this.sanitizePageId(rootPage[1].page.pageName || 'home');
|
|
431
|
-
if (tree.pages[rootPageId]) {
|
|
432
|
-
tree.rootId = rootPageId;
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
return tree;
|
|
436
|
-
}
|
|
437
|
-
static sanitizePageId(pageName) {
|
|
438
|
-
return pageName
|
|
439
|
-
.toLowerCase()
|
|
440
|
-
.replace(/[^a-z0-9]/g, '_')
|
|
441
|
-
.replace(/_+/g, '_')
|
|
442
|
-
.replace(/^_|_$/g, '');
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
exports.ScreenshotConverter = ScreenshotConverter;
|
|
446
|
-
ScreenshotConverter.defaultOptions = {
|
|
447
|
-
includeEmptyCells: false,
|
|
448
|
-
generateIds: true,
|
|
449
|
-
targetPlatform: 'grid3',
|
|
450
|
-
language: 'en',
|
|
451
|
-
fallbackCategory: 'General',
|
|
452
|
-
filenameDelimiter: '->',
|
|
453
|
-
};
|