@willwade/aac-processors 0.0.3

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.
Files changed (89) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +787 -0
  3. package/dist/cli/index.d.ts +2 -0
  4. package/dist/cli/index.js +189 -0
  5. package/dist/cli/prettyPrint.d.ts +2 -0
  6. package/dist/cli/prettyPrint.js +28 -0
  7. package/dist/core/analyze.d.ts +6 -0
  8. package/dist/core/analyze.js +49 -0
  9. package/dist/core/baseProcessor.d.ts +94 -0
  10. package/dist/core/baseProcessor.js +208 -0
  11. package/dist/core/fileProcessor.d.ts +7 -0
  12. package/dist/core/fileProcessor.js +51 -0
  13. package/dist/core/stringCasing.d.ts +37 -0
  14. package/dist/core/stringCasing.js +174 -0
  15. package/dist/core/treeStructure.d.ts +190 -0
  16. package/dist/core/treeStructure.js +223 -0
  17. package/dist/index.d.ts +23 -0
  18. package/dist/index.js +96 -0
  19. package/dist/optional/symbolTools.d.ts +28 -0
  20. package/dist/optional/symbolTools.js +126 -0
  21. package/dist/processors/applePanelsProcessor.d.ts +23 -0
  22. package/dist/processors/applePanelsProcessor.js +521 -0
  23. package/dist/processors/astericsGridProcessor.d.ts +49 -0
  24. package/dist/processors/astericsGridProcessor.js +1427 -0
  25. package/dist/processors/dotProcessor.d.ts +21 -0
  26. package/dist/processors/dotProcessor.js +191 -0
  27. package/dist/processors/excelProcessor.d.ts +145 -0
  28. package/dist/processors/excelProcessor.js +556 -0
  29. package/dist/processors/gridset/helpers.d.ts +4 -0
  30. package/dist/processors/gridset/helpers.js +48 -0
  31. package/dist/processors/gridset/resolver.d.ts +8 -0
  32. package/dist/processors/gridset/resolver.js +100 -0
  33. package/dist/processors/gridsetProcessor.d.ts +28 -0
  34. package/dist/processors/gridsetProcessor.js +1339 -0
  35. package/dist/processors/index.d.ts +14 -0
  36. package/dist/processors/index.js +42 -0
  37. package/dist/processors/obfProcessor.d.ts +21 -0
  38. package/dist/processors/obfProcessor.js +278 -0
  39. package/dist/processors/opmlProcessor.d.ts +21 -0
  40. package/dist/processors/opmlProcessor.js +235 -0
  41. package/dist/processors/snap/helpers.d.ts +4 -0
  42. package/dist/processors/snap/helpers.js +27 -0
  43. package/dist/processors/snapProcessor.d.ts +44 -0
  44. package/dist/processors/snapProcessor.js +586 -0
  45. package/dist/processors/touchchat/helpers.d.ts +4 -0
  46. package/dist/processors/touchchat/helpers.js +27 -0
  47. package/dist/processors/touchchatProcessor.d.ts +27 -0
  48. package/dist/processors/touchchatProcessor.js +768 -0
  49. package/dist/types/aac.d.ts +47 -0
  50. package/dist/types/aac.js +2 -0
  51. package/docs/.keep +1 -0
  52. package/docs/ApplePanels.md +309 -0
  53. package/docs/Grid3-XML-Format.md +1788 -0
  54. package/docs/TobiiDynavox-Snap-Details.md +394 -0
  55. package/docs/asterics-Grid-fileformat-details.md +443 -0
  56. package/docs/obf_.obz Open Board File Formats.md +432 -0
  57. package/docs/touchchat.md +520 -0
  58. package/examples/.coverage +0 -0
  59. package/examples/.keep +1 -0
  60. package/examples/README.md +31 -0
  61. package/examples/communikate.dot +2637 -0
  62. package/examples/demo.js +143 -0
  63. package/examples/example-images.gridset +0 -0
  64. package/examples/example.ce +0 -0
  65. package/examples/example.dot +14 -0
  66. package/examples/example.grd +1 -0
  67. package/examples/example.gridset +0 -0
  68. package/examples/example.obf +27 -0
  69. package/examples/example.obz +0 -0
  70. package/examples/example.opml +18 -0
  71. package/examples/example.spb +0 -0
  72. package/examples/example.sps +0 -0
  73. package/examples/example2.grd +1 -0
  74. package/examples/gemini_response.txt +845 -0
  75. package/examples/image-map.js +45 -0
  76. package/examples/package-lock.json +1326 -0
  77. package/examples/package.json +10 -0
  78. package/examples/styled-output/converted-snap-to-touchchat.ce +0 -0
  79. package/examples/styled-output/styled-example.ce +0 -0
  80. package/examples/styled-output/styled-example.gridset +0 -0
  81. package/examples/styled-output/styled-example.obf +37 -0
  82. package/examples/styled-output/styled-example.spb +0 -0
  83. package/examples/styling-example.ts +316 -0
  84. package/examples/translate.js +39 -0
  85. package/examples/translate_demo.js +254 -0
  86. package/examples/translation_cache.json +44894 -0
  87. package/examples/typescript-demo.ts +251 -0
  88. package/examples/unified-interface-demo.ts +183 -0
  89. package/package.json +106 -0
@@ -0,0 +1,432 @@
1
+ # b.obf and .obz Open Board File Formats
2
+
3
+ #
4
+
5
+ [Introduction](#introduction)
6
+ [Basics](#basics)
7
+ [Linking to Other Boards](#linking-to-other-boards)
8
+ [Additional Paramet](#additional-parameters)
9
+ [ers](#additional-parameters)
10
+ [Button Ordering](#button-ordering)
11
+ [Proprietary Symbol Sets](#proprietary-symbol-sets)
12
+ [Different Sound than Labelled](#different-sound-than-labelled)
13
+ [Spelling and Specialty Actions](#spelling-and-specialty-actions)
14
+ [Color Definitions](#color-definitions)
15
+ [Absolute Positioning](#absolute-positioning)
16
+ [Licensing](#licensing)
17
+ [Extensions](#extensions)
18
+ [url vs. data\_url](#url-vs.-data_url)
19
+ [Parsing Guidelines and Gotchas](#parsing-guidelines-and-gotchas)
20
+ [ID Uniqueness](#id-uniqueness)
21
+ [Images and Sounds with Multiple References](#images-and-sounds-with-multiple-references)
22
+ [Future Work](#future-work)
23
+
24
+ #
25
+
26
+ # Introduction {#introduction}
27
+
28
+ AAC apps are relatively consistent in their interface and layout, so it seems reasonable to expect a way to transfer boards and board sets from one app to another. However, there isn’t a standardized file format for doing so. The .obf (stands for Open Board Format) and .obz file extensions are meant to address this problem by providing a simple, flexible format for packaging potentially-self-contained board configurations.
29
+
30
+ # Basics {#basics}
31
+
32
+ .obf files are JSON files that contain the structure and layout of an individual board. There are a number of more advanced options, but at a basic level you define a list of buttons, a list of images, and a grid-based layout for the buttons (non-grid layouts are supported, I’ll cover that below). The format attribute should be "open-board-0.1" to support versioning. All IDs should be strings (not numbers). Buttons, images and sounds must have a unique ID, but it doesn’t need to be globally unique, just unique to the current .obf or .obz file. Note that image (and sound) files are referenced on button entries using image\_id (or sound\_id).
33
+
34
+ {
35
+ "format": "open-board-0.1",
36
+ "id": "1",
37
+ "locale": "en",
38
+ "url": "http://www.myaacapp.com/boards/123",
39
+ "name": "Example Board",
40
+ "description\_html": "This is just a \<b\>simple\</b\> example board I put together.",
41
+ "buttons": \[
42
+ {
43
+ "id": "1",
44
+ "image\_id": "2",
45
+ "label": "happy",
46
+ "border\_color": "rgb(0, 0, 55)",
47
+ "background\_color": "rgba(200, 255, 255, 0.2)"
48
+ },
49
+ {
50
+ "id": "2",
51
+ "label": "drinks",
52
+ "image\_id": "2"
53
+ },
54
+ \],
55
+ "grid": {
56
+ "rows": 1,
57
+ "columns": 2,
58
+ "order": \[
59
+ \["1","2"\]
60
+ \]
61
+ },
62
+ "images": \[
63
+ {
64
+ "id": "1",
65
+ "url": "http://cdn.myaacapp.com/happy.png",
66
+ "width": 1024,
67
+ "height": 768,
68
+ "content\_type": "image/png"
69
+ }
70
+ \]
71
+ }
72
+
73
+ .obz files at their most basic are one or more .obf files compressed as a zip file. If the .obz file contains more than one .obf file then a ./manifest.json file is required in the root of the zip file to define the contents. The manifest.json provides id-based mapping of all resources in the package, and a root attribute, which specifies the location within the zipped package of the primary board (note that id-to-path mappings are technically redundant but still required):
74
+
75
+ {
76
+ "format": "open-board-0.1",
77
+ "root": "boards/1.obf",
78
+ "paths": {
79
+ "boards": {
80
+ "1": "boards/1.obf",
81
+ "2": "boards/2.obf"
82
+ },
83
+ "images": { },
84
+ "sounds": { }
85
+ }
86
+ }
87
+
88
+ These examples reference external URLs for images. You can also include images and sounds inline using the data attribute with the value set as a data-URI:
89
+
90
+ "images": \[
91
+ {
92
+ "id": "1",
93
+ "data": "data:image/png;base64,iVBORw0KGgoAA...FTkSuQmCC",
94
+ "width": 1024,
95
+ "height": 768,
96
+ "content\_type": "image/png",
97
+ }
98
+ \]
99
+
100
+ Magic, right? If you’re wondering how to handle proprietary symbol sets, there’s more details farther down. When packaging as an .obz file instead of an .obf then you can also just include the files in the zipped package and reference them using the path attribute:
101
+
102
+ "images": \[
103
+ {
104
+ "id": "1",
105
+ "path": "images/pic.png",
106
+ "width": 1024,
107
+ "height": 768,
108
+ "content\_type": "image/png",
109
+ }
110
+ \]
111
+
112
+ Path-based referencing is useful because then you can use the same file across multiple boards, potentially resulting in a smaller overall file size.
113
+
114
+ # Linking to Other Boards {#linking-to-other-boards}
115
+
116
+ You can link to other boards within an .obz file or using URLs. To configure a button as a linked button, you define its load\_board attribute:
117
+
118
+ "buttons": \[
119
+ {
120
+ "id": "2",
121
+ "label": "drinks",
122
+ "image\_id": "2",
123
+ "load\_board": {
124
+ "name": "Drinks",
125
+ "data\_url": "http://myboards.net/api/v1/boards/123",
126
+ "url": "http://myboards.net/boards/123"
127
+ }
128
+ }
129
+ \]
130
+
131
+ The url attribute allows you to redirect the user to a web site, in case the linked board is contained in another system. The data\_url attribute is an endpoint that should return programmatic data about the board, but may require authentication. There’s not really a clean way I can think of other than this to include settings that link to another board when importing a standalone .obf file, but at least within the same system this should prove helpful. The importing app may ignore links like this if they don’t know how to handle them.
132
+
133
+ Linking within an .obz file is a lot more straightforward. The load\_board attribute should have a path attribute defined that points to the linked .obf file:
134
+
135
+ "buttons": \[
136
+ {
137
+ "id": "2",
138
+ "label": "drinks",
139
+ "image\_id": "2",
140
+ "load\_board": {
141
+ "id": "1",
142
+ "name": "Drinks",
143
+ "data\_url": "http://myboards.net/api/v1/boards/123",
144
+ "url": "http://myboards.net/boards/123",
145
+ "path": "boards/3.obf"
146
+ }
147
+ }
148
+ \]
149
+
150
+ Which can then be used to parse the next .obf file. Note that it is recommended that an importing app import all .obf files in the .obz package, regardless of whether they are eventually referenced through the root board, but this behavior is not required for compliance.
151
+
152
+ # Additional Parameters {#additional-parameters}
153
+
154
+ In addition to linking and referencing resources, there are a bunch of other things you can configure. The idea is to be flexible without going crazy, but if there are things you think should be here that aren’t, please make a comment.
155
+
156
+ ### **Button Ordering** {#button-ordering}
157
+
158
+ In a grid-based layout (absolute-positioned is [defined below](#absolute-positioning)) you define your button order as a list of ids. Each id should match the unique id provided for the corresponding button in the buttons list. The grid is defined using the board’s grid attribute. Any buttons that are empty or unused should have a null placeholder in the grid’s order attribute, which is an array of arrays.
159
+
160
+ {
161
+ "grid": {
162
+ "rows": 3,
163
+ "columns": 6,
164
+ "order": \[
165
+ \["1","2","3","4","5","6"\],
166
+ \["1",null,null,null,null,"6"\],
167
+ \["9",null,"23",null,null,"4"\]
168
+ \]
169
+ }
170
+ }
171
+
172
+ ### **Proprietary Symbol Sets** {#proprietary-symbol-sets}
173
+
174
+ If you’re using a proprietary or licensed symbol set then it’s likely a violation of terms of use to have a public image URL or embed the images in shareable data files like an .obf. In that case you can define the symbol attribute on the image, which includes a few sub-attributes. Standardization of symbol sets across apps is desired (i.e. if two apps use SymbolStix, it should be possible to pass .obf files back and forth and the boards should remain intact). *NOTE: I haven’t used any proprietary symbol sets, so I’m definitely open to suggestions on the best way to organize this attribute.* If an app does not have access to the specified symbol set, it may fail or notify the user of alternative images.
175
+
176
+ "images": \[
177
+ {
178
+ "id": "1",
179
+ "symbol": {
180
+ "set": "symbolstix",
181
+ "filename": "happy.png"
182
+ }
183
+ "width": 1024,
184
+ "height": 768,
185
+ "content\_type": "image/png",
186
+ }
187
+ \]
188
+
189
+ ### **Different Sound than Labelled** {#different-sound-than-labelled}
190
+
191
+ Sometimes you want to make a different sound than is provided by the label (i.e. the button says "happy" but you want it to say "I am happy"). In these cases you can define the vocalization attribute to whatever you would like. If it is not defined, then the label attribute will be used to generate the sound.
192
+
193
+ "buttons": \[
194
+ {
195
+ "id": "1",
196
+ "image\_id": "2",
197
+ "label": "happy",
198
+ "vocalization": "Are you as happy as me?"
199
+ }
200
+ \]
201
+
202
+ ### **Spelling and Specialty Actions** {#spelling-and-specialty-actions}
203
+
204
+ Some buttons will be used to perform actions other than vocalizing a word or phrase. For example, a keyboard-style board would have individual letters which should be combined to make single words, not pronounced independently, and would also have a “spacebar” button for ending words. Other common actions include returning to the home screen, clearing the current sentence, etc. as well as more advanced functions like copy and paste. All of these options are defined using the action attribute on a button.
205
+
206
+ To specify buttons as spelling-style buttons, you define action as “+something”, i.e. “+a” to add an “a” to a word, “+oo” to add “oo” to a word, etc.
207
+
208
+ "buttons": \[
209
+ {
210
+ "id": "1",
211
+ "image\_id": "2",
212
+ "label": "F",
213
+ "action": "+f"
214
+ }
215
+ \]
216
+
217
+ To specify other actions, you define action as “:something”, i.e. “:space” to finish the previous word and start a new word, “:clear” to clear the current sentence, etc.
218
+
219
+ "buttons": \[
220
+ {
221
+ "id": "1",
222
+ "image\_id": "2",
223
+ "label": "clear",
224
+ "action": ":clear"
225
+ }
226
+ \]
227
+
228
+ Not all communication apps support these types of specialty actions, and if your app doesn’t support them but you find them in an imported file, you should notify the user. The list of suggested supported actions are:
229
+
230
+ * \+something \- append letter(s) to a currently-spelled word
231
+ * :space \- finish spelling and start a new word
232
+ * :home \- return to the user’s home board
233
+ * :speak \- speak the current sentence box contents
234
+ * :clear \- clear the sentence box
235
+ * :backspace \- remove the last entry in the sentence box
236
+
237
+ This list will probably grow over time, and will be split into suggested and optional actions. In the mean time, if you have actions that are custom to your app, you can use the ext\_ notation [covered in more detail below](#extensions) (in this case still prefaced by a colon).
238
+
239
+ "buttons": \[
240
+ {
241
+ "id": "1",
242
+ "image\_id": "2",
243
+ "label": "clear",
244
+ "action": ":ext\_speakify\_flip\_buttons"
245
+ }
246
+ \]
247
+
248
+ If a button needs to support multiple actions, you can define it in the optional actions attribute. Some AAC systems will only support a single action per button, so you should include a single action attribute as a fallback in such a case.
249
+
250
+ "buttons": \[
251
+ {
252
+ "id": "1",
253
+ "image\_id": "2",
254
+ "label": "clear and home",
255
+ "action": ":clear",
256
+ "actions": \[":clear", ":home"\]
257
+ }
258
+ \]
259
+
260
+ ### **Color Definitions** {#color-definitions}
261
+
262
+ Buttons can define both background and border colors. Text color is currently non-configurable and should be handled by the communication app based on its implementation. Color attributes are string values and can be represented as rgb or rgba values. If alpha is not supported (i.e. rgba) then it is suggested that the communication app convert the color into a similar non-alpha color based on its implementation (note this probably should be something more intelligent than “just strip off the alpha” or the color won’t really match as expected).
263
+
264
+ "buttons": \[
265
+ {
266
+ "id": "1",
267
+ "image\_id": "2",
268
+ "label": "happy",
269
+ "background\_color": "rgb(255, 255, 255)",
270
+ "border\_color": "rgba(255, 0, 0, 0.5)"
271
+ }
272
+ \]
273
+
274
+ ### **Absolute Positioning** {#absolute-positioning}
275
+
276
+ The default layout is defined using the grid attribute, and grid should always be defined in case an importing app doesn’t support absolute positioning of buttons. However, if an app supports absolute positioning, you can define top, left, width, and height attributes for all buttons, in which case they will be rendered using absolute positioning. All four attributes are required for all buttons, and have values defined as a percentage (from 0.0 and 1.0).
277
+
278
+ "buttons": \[
279
+ {
280
+ "id": "1",
281
+ "image\_id": "2",
282
+ "label": "happy",
283
+ "left": 0.102,
284
+ "top": 0.2345,
285
+ "width": 0.08,
286
+ "height": 0.11
287
+ }
288
+ \]
289
+
290
+ The above example would position the button (screen\_width \* .102) from the left, (screen\_height \* .2345) from the top, with a width of (screen\_width \* .08) and a height of (screen\_height \* 0.11). With absolute positioning, buttons are rendered in the order they are specified so later buttons could potentially be drawn on top of earlier buttons.
291
+
292
+ ### **String Lists**
293
+
294
+ As an optional enhancement to support boards that can be rendered in multiple languages, obf allows for the inclusion of multiple string lists. String lists can be defined for multiple locales, and are essentially a dictionary where the keys match values set for label and vocalization attributes on buttons. String lists are defined via the strings attribute on the root level and are mapped by locale. If no matches are found in the strings dictionary for a given button, the parser can use the attribute’s value as-is. It’s up to the app to decide whether to fall back in cases with a more general locale (i.e. using ”en“ if nothing is defined for “en-us”)
295
+
296
+ {
297
+ "locale": "en",
298
+ "strings": {
299
+ "en": {
300
+ "happy": "happy",
301
+ ":time": "time"
302
+ },
303
+ "es": {
304
+ "happy": "contento",
305
+ ":time": "hora"
306
+ },
307
+ "fr": {
308
+ ":time": "temps"
309
+ }
310
+ }
311
+ "buttons": \[
312
+ {
313
+ "id": "1",
314
+ "image\_id": "2",
315
+ "label": "happy",
316
+ "border\_color": "rgb(0, 0, 55)",
317
+ "background\_color": "rgba(200, 255, 255, 0.2)"
318
+ },
319
+ {
320
+ "id": "2",
321
+ "label": ":time",
322
+ "image\_id": "2"
323
+ },
324
+ {
325
+ "id": "3",
326
+ "label": "Brian",
327
+ "image\_id": "2"
328
+ },
329
+ \]
330
+ }
331
+
332
+ In the above example the English strings matches some of the attributes defined on the buttons. The label “Brian” is not translated, so the app will fall back to using the attribute as-is. The Spanish strings are also, but the French strings are missing a translation for “Brian” and also “happy”, so the app can fall back to the strings as-is for both of these labels, or search alternate string lists for a different fallback.
333
+
334
+ ### **Licensing** {#licensing}
335
+
336
+ You can define licenses for boards, images and sounds, including proper attribution. If no license is defined for a resource then the importing app should assume it is private, all rights reserved.
337
+
338
+ {
339
+ "format": "open-board-0.1",
340
+ "id": "1",
341
+ "url": "http://coughdropaac.com/boards/123",
342
+ "name": "Example Board",
343
+ "license": {
344
+ "type": "CC-By-SA",
345
+ "Copyright\_notice\_url": "https://creativecommons.org/licenses/by-sa/4.0",
346
+ "source\_url": "http://coughdropaac.com/boards/123?optional",
347
+ "author\_name": "Bob Jones",
348
+ "author\_url": "http://about.me/bobjones",
349
+ "author\_email": "email\_or\_link@dontneedboth.com"
350
+ }
351
+ "images": \[
352
+ {
353
+ "id": "1",
354
+ "url": "http://cdn.coughdropaac.com/happy.png",
355
+ "width": 1024,
356
+ "height": 768,
357
+ "content\_type": "image/png",
358
+ "license": {
359
+ "type": "CC-By",
360
+ "copyright\_notice\_url": "https://creativecommons.org/licenses/by/4.0",
361
+ "source\_url": "http://coughdropaac.com/optional/url",
362
+ "author\_name": "Sarah Miller",
363
+ "author\_url": "http://facebook.com/bobjones",
364
+ "author\_email": "email\_or\_link@dontneedboth.com"
365
+ }
366
+ }
367
+ \]
368
+ }
369
+
370
+ ### **Extensions** {#extensions}
371
+
372
+ If there are additional parameters you need that aren’t available, you are free to add them to the file, just make sure to prefix them with ext\_ to prevent any collisions ion the future. It is recommended that you include the name of your app in the attribute name (i.e. for an app called “speakify” you could define ext\_speakify\_name, ext\_speakify\_animation, etc.)
373
+
374
+ "buttons": \[
375
+ {
376
+ "id": "1",
377
+ "image\_id": "2",
378
+ "label": "happy",
379
+ "ext\_speakify\_app\_store\_id": "com.facebook.Facebook"
380
+ }
381
+ \]
382
+
383
+ ### **url vs. data\_url** {#url-vs.-data_url}
384
+
385
+ In addition to defining url attributes on images and sounds, you can also define the data\_url attribute, which is the URL to a programmatic endpoint (API call) for retrieving information about the resource. These endpoints may or may not require authentication, depending on the app.
386
+
387
+ # Parsing Guidelines and Gotchas {#parsing-guidelines-and-gotchas}
388
+
389
+ There are cases where an .obf file may include some ambiguity. The goal of this section is to help address that.
390
+
391
+ ### **Numerical IDs**
392
+
393
+ It has been observed that some implementers have been using numerical IDs instead of string IDs. This is contrary to the spec, but you may see it in the real world. The easiest fix is to cast all IDs as strings (button IDs, image IDs, grid order IDs, etc.) before trying to use them.
394
+
395
+ ### **ID Uniqueness** {#id-uniqueness}
396
+
397
+ It’s worth repeating that board, image and sound ids do not need to be globally unique, but should be unique to the current .obf or .obz file. When multiple .obf files are zipped up together in a single .obz file, the image and sound ids should be unique to that .obz file, which is a stronger constraint than is required for the .obf files when bundled independently (i.e. if two .obf files both have images with id of “2”, when they are independent that is fine, but when they are combined into a single .obz file the package is invalid). As such, it may actually be in your best interest to use globally-unique or system-wide-unique ids to make your life easier.
398
+
399
+ ### **Images and Sounds with Multiple References** {#images-and-sounds-with-multiple-references}
400
+
401
+ There is nothing preventing an image or sound record in an .obf file from including multiple file references (i.e. a data-URI as well as a remote URL or zipped file attribute). If multiple references are included, the app should use the first reference found in the following order:
402
+
403
+ * data
404
+ * path
405
+ * url
406
+ * symbol
407
+
408
+ # Future Work {#future-work}
409
+
410
+ Currently I’m [working on a ruby gem](https://github.com/CoughDrop/obf) (library) that will provide better support for importing and exporting from other systems. I also plan to host a website that will provide public documentation as well as a validator and .obf-to-.pdf and .obz-to-.pdf converters.
411
+
412
+ Some board systems have a hard-coded header that manages common actions like “speak” and “back”, and others don’t. What to do, what to do...
413
+
414
+ Define spelling buttons, so you can create a board that is a keyboard.
415
+
416
+ Option to "say it now without adding to the sentence" for quick fires. Also additional button-specific settings and overrides.
417
+
418
+ Consider adding optional word-type attribute (noun, verb, etc.).
419
+
420
+ # Changes
421
+
422
+ 3/4/2019 \- Added Changes section, clarified location of manifest.json for .obz files
423
+
424
+ # Additional References
425
+
426
+ - [http://www.oatsoft.org/Software/concept-coding-framework-ccf/](http://www.oatsoft.org/Software/concept-coding-framework-ccf/)
427
+ - [http://sensorysoftware.com/answers/xml-schema-for-the-grid-2/](http://sensorysoftware.com/answers/xml-schema-for-the-grid-2/)
428
+ - [https://github.com/willwade/AAC-Tools/tree/master/Extractors/SymbolDB](https://github.com/willwade/AAC-Tools/tree/master/Extractors/SymbolDB)
429
+
430
+ [![88x31.png][image1]](http://creativecommons.org/licenses/by/4.0/)OpenAAC. 2020
431
+
432
+ [image1]: <data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAYAAABjyArgAAAFlElEQVR4Xu2azU5bRxTHva4UxJ4Ny7CjrdJlipQ+AH2AVLxBUXeRqIIiUilSJYSoonbRUKm7JsF8fxpswHzaxjaBbHkEHuF0fnM51+O5NmBjX5yEI/2l6/k4M/rfM/85M9cJEelKJBJyj9bDWAKzPxbXFmQ5tSSrmyuS2lqXzZ1N2drNyM7Bjuwd7cpBfl8OC4eSOz6SfDEn+VI+gHnOFY/kyNTRhrZZ04e+aeMDX6ubq9Y3Y8yvzMns8ozMLCZleuG9vJ9/Z/Fu7q28nf3vs4FDdIXctfSqbGynJJNNy87+tiXrsHBgCTwuF6T0oSjl05KcnJ3Ih48BeC6flm0dbXKGcPrQFx+Z3bT1iW+f5OTidEgyBH+mJCeqyTWRRwTu5/ZttEIapI69HJMnPzyRB10PIkuBMurGfhuzbY9Pju1LIaKzh1kbzRvbG1Ukz63MysxSMiB5vkKyP8lG8WzkmfT19UXmSBl1fvt2IiQYWXDJhZh8KWejcmJyQnp6eiITrgfaTvwxYfsiIUTzbkhyKpSLhdV5mVsOSG5FFE/+OSkP+x6G8xgYGJDh4WELnrWcNrT1+7cDl2MmrE4iCxVy8zYSB38crCKvu7tbhoaGZHR0VNLptAXPlFHntqVvdn9HCuWAZCIZuQg0eUWW1herpeIWUTz17xvp6uoKxh0clPPzczaYKqOMOtrQNg6SQ4LZ0NBLZIHI9cmFPIi8uLjw511lU1NTVUQTLZDMC+PFMQYb33pmTVY2lgOpqBHF/kSvg0YuL9o1nYdrzFHn5vtpNUKCWb5sSmguS9slt7+/v2ZE1DNeAn20P77wiSYzRkUqnCheMlG8MN2UTKCrdhwTnR/PzqrmUotgTCO53ZocEkwqdpA/sBsamuuSe13U1jKfZDSZjc9KhRkrnQ2i2GrxWqDFKhONEqwbWiNBQFv60Nf310qEBNvoNRGGNOiGxlIvFov+3G5skKxygU98k8JpFKe2gih2M4pmZAL/bGK+IRf6gn3pwHTj8/21EiHB6GPBRC+pmE4Kzb2tqd4BUjhWiGrx5mXa5spEswSTKfim4yp8o09sBHNCKxqdJJflN5HnSgPZArrFWwcu+X7d+Ph4WIdpFOMbLebEF5EJk7LZbMLRYX+y9fBJEMzmxhLWQ4S7pCDQnyyAzGQyGSn3++tSxTcnPqSIvJiUbT2zbrMJdFjTtWYI7niJ4D7h5KwcTsiNQt2sent77eYA4Tyz/LVOMw0I1zo1VyY4VjNWRYcDgjVda4bgqzY5Hde32Dc5cl/uFXRCkKimZbU0+ao6NXcFMAY58d7RXssIdtM03+oRHHua9ikTDBo5aKj2xnrQuKlEsPGRuvEMqa5E+HVq7ZYI0PFH5as2uas2sqvq1DRigk2uZMdq5SangGT3soeXzjwY3z303Mllj6ZpekQmtXLNT8XcCIVkLffrMCIan5qmMZamaWvp26dpPjryulIPGu4x2c9nm7F6B43ty4MGV5d60Gj2JNfJCAnePax9VK6lZTe1uI7KnYyQYPey580/f4dR17LLHrMy8M1lD2PplaV78d7sZU8nIyQYjDwfCa8rn/70tIrkRi59iPqa15XG94uXL8LyLwiVH8O//GzTqNJp4xfu1NHGvXB/9N0jKw3k2bzAGoN/Cagu+NUQwWEAkt1IBo1+MoLcgvH1+q/X/qBtgWtumd/G79dmRAoCkolks7TR5IY/ek7qR89cbOSCesTq8x2QCyIFFsiF+9ke0q77bM+XC/vZnv9HmL5xy4Jrter8spgQKQjxzbdfS3IuaVM48mQOI8EfT8rOH0/Ktsz948nMfFIef/844q/dcEn0CfV/x4hIQQSQ9er3V5LKpJy/Tl2Cv06ZMupocxfEKlyrVeeXxYRIwT1aCfNiv4oU3qMlYNH8D8YjNDMPmXCyAAAAAElFTkSuQmCC>