@lightningjs/blits 1.30.2 → 1.31.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/CHANGELOG.md +1035 -1019
- package/LICENSE +202 -202
- package/NOTICE +12 -12
- package/README.md +33 -33
- package/bin/index.js +8 -8
- package/blits.d.ts +6 -6
- package/index.d.ts +1099 -1099
- package/index.js +28 -28
- package/package.json +57 -56
- package/public/assets/fps_sprite.base64.js +1 -1
- package/scripts/postpublish.js +59 -59
- package/scripts/prepublishOnly.js +84 -84
- package/scripts/prepush.js +78 -78
- package/src/announcer/announcer.js +162 -162
- package/src/announcer/speechSynthesis.js +107 -107
- package/src/application.js +100 -100
- package/src/component/base/announcer.js +27 -27
- package/src/component/base/events.js +69 -69
- package/src/component/base/index.js +33 -33
- package/src/component/base/methods.js +244 -244
- package/src/component/base/router.js +41 -41
- package/src/component/base/timeouts_intervals.js +99 -99
- package/src/component/base/utils.js +72 -72
- package/src/component/setup/computed.js +48 -48
- package/src/component/setup/index.js +62 -62
- package/src/component/setup/input.js +30 -30
- package/src/component/setup/methods.js +37 -37
- package/src/component/setup/props.js +62 -62
- package/src/component/setup/routes.js +42 -42
- package/src/component/setup/state.js +59 -59
- package/src/component/setup/watch.js +35 -35
- package/src/component.js +437 -437
- package/src/components/Circle.js +73 -73
- package/src/components/FPScounter.js +1510 -1510
- package/src/components/RouterView.js +81 -81
- package/src/components/Sprite.js +94 -94
- package/src/components/index.js +28 -28
- package/src/constants.js +21 -21
- package/src/engine.js +1 -1
- package/src/engines/L3/element.js +744 -744
- package/src/engines/L3/fontLoader.js +38 -38
- package/src/engines/L3/index.js +7 -7
- package/src/engines/L3/launch.js +123 -123
- package/src/engines/L3/shaderLoader.js +12 -12
- package/src/engines/index.js +1 -1
- package/src/focus.js +90 -90
- package/src/helpers/deepEqualArray.js +50 -50
- package/src/helpers/fetchJson.js +34 -34
- package/src/helpers/sequence.js +26 -26
- package/src/launch.js +123 -123
- package/src/lib/blitsfileconverter/blitsfileconverter.js +52 -52
- package/src/lib/codegenerator/generator.js +788 -788
- package/src/lib/colors/colors.js +92 -92
- package/src/lib/colors/htmlColors.js +168 -168
- package/src/lib/componentId.js +49 -49
- package/src/lib/eventListeners.js +123 -123
- package/src/lib/hooks.js +59 -59
- package/src/lib/lifecycle.js +85 -85
- package/src/lib/log.js +117 -117
- package/src/lib/precompiler/precompiler.js +73 -73
- package/src/lib/reactivity/effect.js +93 -93
- package/src/lib/reactivity/reactive.js +208 -208
- package/src/lib/reactivityguard/computedprops.js +218 -218
- package/src/lib/symbols.js +108 -108
- package/src/lib/templateparser/parser.js +386 -386
- package/src/lib/utils.js +27 -27
- package/src/plugin.js +27 -27
- package/src/plugins/appstate.js +26 -26
- package/src/plugins/index.js +20 -20
- package/src/plugins/language.js +152 -152
- package/src/plugins/log.js +25 -25
- package/src/plugins/theme.js +94 -94
- package/src/router/router.js +516 -514
- package/src/router/transitions/fadeIn.js +27 -27
- package/src/router/transitions/fadeInOut.js +33 -33
- package/src/router/transitions/index.js +21 -21
- package/src/router/transitions/slideIn.js +28 -28
- package/src/router/transitions/slideInOut.js +33 -33
- package/src/settings.js +40 -40
- package/vite/blitsFileConverter.js +67 -67
- package/vite/index.js +28 -28
- package/vite/msdfGenerator.js +269 -269
- package/vite/preCompiler.js +47 -42
- package/vite/reactivityGuard.js +45 -45
- package/vscode/data/template-attributes.json +589 -589
- package/.editorconfig +0 -7
- package/.eslintignore +0 -1
- package/.eslintrc.cjs +0 -65
- package/.github/workflows/comment-lint-results.yml +0 -125
- package/.github/workflows/comment-test-results.yml +0 -109
- package/.github/workflows/deploy-docs.yml +0 -27
- package/.github/workflows/run-blits-tests.yml +0 -142
- package/.github/workflows/run-lint.yml +0 -128
- package/.husky/pre-commit +0 -1
- package/.husky/pre-push +0 -1
- package/.vscode/extensions.json +0 -7
- package/.vscode/settings.json +0 -9
- package/COPYING +0 -1
- package/banner.png +0 -0
- package/docs/.nojekyll +0 -0
- package/docs/README.md +0 -19
- package/docs/_sidebar.md +0 -37
- package/docs/built-in/directives.md +0 -50
- package/docs/built-in/for-loop.md +0 -204
- package/docs/built-in/layout.md +0 -168
- package/docs/components/component_state.md +0 -90
- package/docs/components/computed_properties.md +0 -45
- package/docs/components/lifecycle_events.md +0 -42
- package/docs/components/methods.md +0 -45
- package/docs/components/props.md +0 -56
- package/docs/components/user_input.md +0 -186
- package/docs/components/utility_methods.md +0 -320
- package/docs/components/watchers.md +0 -34
- package/docs/essentials/application_root.md +0 -19
- package/docs/essentials/components.md +0 -68
- package/docs/essentials/displaying_images.md +0 -67
- package/docs/essentials/displaying_text.md +0 -135
- package/docs/essentials/element_attributes.md +0 -195
- package/docs/essentials/settings.md +0 -117
- package/docs/essentials/template_syntax.md +0 -83
- package/docs/getting_started/file_structure.md +0 -58
- package/docs/getting_started/getting_started.md +0 -45
- package/docs/getting_started/intro.md +0 -20
- package/docs/index.html +0 -47
- package/docs/performance/lazy-loading.md +0 -161
- package/docs/plugins/global_app_state.md +0 -98
- package/docs/plugins/language.md +0 -172
- package/docs/plugins/text-to-speech-announcer.md +0 -120
- package/docs/plugins/theme.md +0 -176
- package/docs/router/basics.md +0 -175
- package/docs/router/hooks.md +0 -70
- package/docs/router/transitions.md +0 -146
- package/docs/sidebar.json +0 -171
- package/docs/transitions_animations/transitions.md +0 -150
- package/jsconfig.json +0 -12
- package/src/component/setup/props.test.js +0 -238
- package/src/component.test.js +0 -388
- package/src/components/Circle.orig.js +0 -31
- package/src/components/FPScounter.orig.js +0 -121
- package/src/components/RouterView.orig.js +0 -54
- package/src/components/Sprite.orig.js +0 -58
- package/src/components/index.orig.js +0 -28
- package/src/engines/L3/element.test.js +0 -579
- package/src/helpers/deepEqualArray.test.js +0 -127
- package/src/lib/codegenerator/generator.test.js +0 -3801
- package/src/lib/colors/colors.test.js +0 -290
- package/src/lib/componentId.test.js +0 -84
- package/src/lib/eventListeners.test.js +0 -236
- package/src/lib/reactivity/effect.test.js +0 -35
- package/src/lib/reactivity/reactive.test.js +0 -27
- package/src/lib/templateparser/parser.test.js +0 -1411
- package/src/router/router.test.js +0 -473
package/scripts/prepush.js
CHANGED
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* If not stated otherwise in this file or this component's LICENSE file the
|
|
3
|
-
* following copyright and licenses apply:
|
|
4
|
-
*
|
|
5
|
-
* Copyright 2023 Comcast
|
|
6
|
-
*
|
|
7
|
-
* Licensed under the Apache License, Version 2.0 (the License);
|
|
8
|
-
* you may not use this file except in compliance with the License.
|
|
9
|
-
* You may obtain a copy of the License at
|
|
10
|
-
*
|
|
11
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
-
*
|
|
13
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
-
* See the License for the specific language governing permissions and
|
|
17
|
-
* limitations under the License.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { execSync } from 'child_process'
|
|
21
|
-
import { readFileSync } from 'fs'
|
|
22
|
-
|
|
23
|
-
// Read the input provided by the pre-push hook
|
|
24
|
-
const input = readFileSync(0, 'utf-8').trim().split(' ')
|
|
25
|
-
if (input.length < 4) {
|
|
26
|
-
console.log('Insufficient input from pre-push hook. Skipping lint.')
|
|
27
|
-
process.exit(0)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const localSha = input[1] // The SHA being pushed
|
|
31
|
-
const remoteSha = input[3] // The remote SHA, can be zeros for new branch
|
|
32
|
-
|
|
33
|
-
// If remote is all zeros, it's a new branch
|
|
34
|
-
const isNewBranch = remoteSha === '0000000000000000000000000000000000000000'
|
|
35
|
-
|
|
36
|
-
let filesToLint
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
if (isNewBranch) {
|
|
40
|
-
// For a new branch, compare with dev
|
|
41
|
-
filesToLint = execSync(`git diff --name-only dev...${localSha} | grep '\\.js$'`, {
|
|
42
|
-
encoding: 'utf8',
|
|
43
|
-
})
|
|
44
|
-
} else {
|
|
45
|
-
// Only check files in the commits that are being pushed right now
|
|
46
|
-
filesToLint = execSync(`git diff --name-only ${remoteSha}..${localSha} | grep '\\.js$'`, {
|
|
47
|
-
encoding: 'utf8',
|
|
48
|
-
})
|
|
49
|
-
}
|
|
50
|
-
} catch (error) {
|
|
51
|
-
// grep will exit with code 1 if it doesn't find anything
|
|
52
|
-
console.log('No JavaScript files changed. Nothing to lint.')
|
|
53
|
-
process.exit(0)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (!filesToLint.trim()) {
|
|
57
|
-
console.log('No JavaScript files changed. Nothing to lint.')
|
|
58
|
-
process.exit(0)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const fileList = filesToLint.trim().split('\n')
|
|
62
|
-
console.log(`Running ESLint on ${fileList.length} changed JavaScript files:\n`)
|
|
63
|
-
console.log(filesToLint)
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
// Run ESLint on the changed files
|
|
67
|
-
execSync(`npx eslint ${fileList.join(' ')}`, { stdio: 'inherit' })
|
|
68
|
-
console.log('Linting passed successfully!\n')
|
|
69
|
-
process.exit(0)
|
|
70
|
-
} catch (error) {
|
|
71
|
-
console.log('\n==========================================================================')
|
|
72
|
-
console.log('ESLint found issues in your code. Please fix them before pushing.')
|
|
73
|
-
console.log('You can run:\n')
|
|
74
|
-
console.log('npx eslint --fix "**/*.js"\n')
|
|
75
|
-
console.log('on the specific files to automatically fix some issues.')
|
|
76
|
-
console.log('==========================================================================\n')
|
|
77
|
-
process.exit(1)
|
|
78
|
-
}
|
|
1
|
+
/*
|
|
2
|
+
* If not stated otherwise in this file or this component's LICENSE file the
|
|
3
|
+
* following copyright and licenses apply:
|
|
4
|
+
*
|
|
5
|
+
* Copyright 2023 Comcast
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the Apache License, Version 2.0 (the License);
|
|
8
|
+
* you may not use this file except in compliance with the License.
|
|
9
|
+
* You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { execSync } from 'child_process'
|
|
21
|
+
import { readFileSync } from 'fs'
|
|
22
|
+
|
|
23
|
+
// Read the input provided by the pre-push hook
|
|
24
|
+
const input = readFileSync(0, 'utf-8').trim().split(' ')
|
|
25
|
+
if (input.length < 4) {
|
|
26
|
+
console.log('Insufficient input from pre-push hook. Skipping lint.')
|
|
27
|
+
process.exit(0)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const localSha = input[1] // The SHA being pushed
|
|
31
|
+
const remoteSha = input[3] // The remote SHA, can be zeros for new branch
|
|
32
|
+
|
|
33
|
+
// If remote is all zeros, it's a new branch
|
|
34
|
+
const isNewBranch = remoteSha === '0000000000000000000000000000000000000000'
|
|
35
|
+
|
|
36
|
+
let filesToLint
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
if (isNewBranch) {
|
|
40
|
+
// For a new branch, compare with dev
|
|
41
|
+
filesToLint = execSync(`git diff --name-only dev...${localSha} | grep '\\.js$'`, {
|
|
42
|
+
encoding: 'utf8',
|
|
43
|
+
})
|
|
44
|
+
} else {
|
|
45
|
+
// Only check files in the commits that are being pushed right now
|
|
46
|
+
filesToLint = execSync(`git diff --name-only ${remoteSha}..${localSha} | grep '\\.js$'`, {
|
|
47
|
+
encoding: 'utf8',
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
} catch (error) {
|
|
51
|
+
// grep will exit with code 1 if it doesn't find anything
|
|
52
|
+
console.log('No JavaScript files changed. Nothing to lint.')
|
|
53
|
+
process.exit(0)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!filesToLint.trim()) {
|
|
57
|
+
console.log('No JavaScript files changed. Nothing to lint.')
|
|
58
|
+
process.exit(0)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const fileList = filesToLint.trim().split('\n')
|
|
62
|
+
console.log(`Running ESLint on ${fileList.length} changed JavaScript files:\n`)
|
|
63
|
+
console.log(filesToLint)
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
// Run ESLint on the changed files
|
|
67
|
+
execSync(`npx eslint ${fileList.join(' ')}`, { stdio: 'inherit' })
|
|
68
|
+
console.log('Linting passed successfully!\n')
|
|
69
|
+
process.exit(0)
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.log('\n==========================================================================')
|
|
72
|
+
console.log('ESLint found issues in your code. Please fix them before pushing.')
|
|
73
|
+
console.log('You can run:\n')
|
|
74
|
+
console.log('npx eslint --fix "**/*.js"\n')
|
|
75
|
+
console.log('on the specific files to automatically fix some issues.')
|
|
76
|
+
console.log('==========================================================================\n')
|
|
77
|
+
process.exit(1)
|
|
78
|
+
}
|
|
@@ -1,162 +1,162 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2024 Comcast Cable Communications Management, LLC
|
|
3
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
* you may not use this file except in compliance with the License.
|
|
5
|
-
* You may obtain a copy of the License at
|
|
6
|
-
*
|
|
7
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
*
|
|
9
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
* See the License for the specific language governing permissions and
|
|
13
|
-
* limitations under the License.
|
|
14
|
-
*
|
|
15
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import speechSynthesis from './speechSynthesis.js'
|
|
19
|
-
|
|
20
|
-
let active = false
|
|
21
|
-
let count = 0
|
|
22
|
-
const queue = []
|
|
23
|
-
let isProcessing = false
|
|
24
|
-
let currentId = null
|
|
25
|
-
let debounce = null
|
|
26
|
-
|
|
27
|
-
const noopAnnouncement = {
|
|
28
|
-
then() {},
|
|
29
|
-
done() {},
|
|
30
|
-
cancel() {},
|
|
31
|
-
stop() {},
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const enable = () => {
|
|
35
|
-
active = true
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const disable = () => {
|
|
39
|
-
active = false
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const toggle = (v) => {
|
|
43
|
-
active = v ? true : false
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const speak = (message, politeness = 'off') => {
|
|
47
|
-
if (active === false) return noopAnnouncement
|
|
48
|
-
|
|
49
|
-
return addToQueue(message, politeness)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const pause = (delay) => {
|
|
53
|
-
if (active === false) return noopAnnouncement
|
|
54
|
-
|
|
55
|
-
return addToQueue(undefined, undefined, delay)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const addToQueue = (message, politeness, delay = false) => {
|
|
59
|
-
// keep track of the id so message can be canceled
|
|
60
|
-
const id = count++
|
|
61
|
-
|
|
62
|
-
// setup a promise to allow developer to chain functionality
|
|
63
|
-
// when specific utterances are done
|
|
64
|
-
let resolveFn
|
|
65
|
-
const done = new Promise((resolve) => {
|
|
66
|
-
resolveFn = resolve
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
// augment the promise with a cancel function
|
|
70
|
-
done.cancel = () => {
|
|
71
|
-
const index = queue.findIndex((item) => item.id === id)
|
|
72
|
-
if (index !== -1) queue.splice(index, 1)
|
|
73
|
-
isProcessing = false
|
|
74
|
-
resolveFn('canceled')
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// augment the promise with a stop function
|
|
78
|
-
done.stop = () => {
|
|
79
|
-
if (id === currentId) {
|
|
80
|
-
speechSynthesis.cancel()
|
|
81
|
-
isProcessing = false
|
|
82
|
-
resolveFn('interupted')
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// add message of pause
|
|
87
|
-
if (delay === false) {
|
|
88
|
-
politeness === 'assertive'
|
|
89
|
-
? queue.unshift({ message, resolveFn, id })
|
|
90
|
-
: queue.push({ message, resolveFn, id })
|
|
91
|
-
} else {
|
|
92
|
-
queue.push({ delay, resolveFn, id })
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
setTimeout(() => {
|
|
96
|
-
processQueue()
|
|
97
|
-
}, 100)
|
|
98
|
-
|
|
99
|
-
return done
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const processQueue = async () => {
|
|
103
|
-
if (isProcessing === true || queue.length === 0) return
|
|
104
|
-
isProcessing = true
|
|
105
|
-
|
|
106
|
-
const { message, resolveFn, delay, id } = queue.shift()
|
|
107
|
-
|
|
108
|
-
currentId = id
|
|
109
|
-
|
|
110
|
-
if (delay) {
|
|
111
|
-
setTimeout(() => {
|
|
112
|
-
isProcessing = false
|
|
113
|
-
currentId = null
|
|
114
|
-
resolveFn('finished')
|
|
115
|
-
processQueue()
|
|
116
|
-
}, delay)
|
|
117
|
-
} else {
|
|
118
|
-
if (debounce !== null) clearTimeout(debounce)
|
|
119
|
-
// add some easing when speaking the messages to reduce stuttering
|
|
120
|
-
debounce = setTimeout(() => {
|
|
121
|
-
speechSynthesis
|
|
122
|
-
.speak({ message })
|
|
123
|
-
.then(() => {
|
|
124
|
-
isProcessing = false
|
|
125
|
-
currentId = null
|
|
126
|
-
resolveFn('finished')
|
|
127
|
-
processQueue()
|
|
128
|
-
})
|
|
129
|
-
.catch((e) => {
|
|
130
|
-
isProcessing = false
|
|
131
|
-
currentId = null
|
|
132
|
-
resolveFn(e.error)
|
|
133
|
-
processQueue()
|
|
134
|
-
})
|
|
135
|
-
debounce = null
|
|
136
|
-
}, 200)
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const polite = (message) => speak(message, 'polite')
|
|
141
|
-
|
|
142
|
-
const assertive = (message) => speak(message, 'assertive')
|
|
143
|
-
|
|
144
|
-
const stop = () => {
|
|
145
|
-
speechSynthesis.cancel()
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const clear = () => {
|
|
149
|
-
queue.length = 0
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export default {
|
|
153
|
-
speak,
|
|
154
|
-
polite,
|
|
155
|
-
assertive,
|
|
156
|
-
stop,
|
|
157
|
-
enable,
|
|
158
|
-
disable,
|
|
159
|
-
toggle,
|
|
160
|
-
clear,
|
|
161
|
-
pause,
|
|
162
|
-
}
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 Comcast Cable Communications Management, LLC
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License.
|
|
5
|
+
* You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
* See the License for the specific language governing permissions and
|
|
13
|
+
* limitations under the License.
|
|
14
|
+
*
|
|
15
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import speechSynthesis from './speechSynthesis.js'
|
|
19
|
+
|
|
20
|
+
let active = false
|
|
21
|
+
let count = 0
|
|
22
|
+
const queue = []
|
|
23
|
+
let isProcessing = false
|
|
24
|
+
let currentId = null
|
|
25
|
+
let debounce = null
|
|
26
|
+
|
|
27
|
+
const noopAnnouncement = {
|
|
28
|
+
then() {},
|
|
29
|
+
done() {},
|
|
30
|
+
cancel() {},
|
|
31
|
+
stop() {},
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const enable = () => {
|
|
35
|
+
active = true
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const disable = () => {
|
|
39
|
+
active = false
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const toggle = (v) => {
|
|
43
|
+
active = v ? true : false
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const speak = (message, politeness = 'off') => {
|
|
47
|
+
if (active === false) return noopAnnouncement
|
|
48
|
+
|
|
49
|
+
return addToQueue(message, politeness)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const pause = (delay) => {
|
|
53
|
+
if (active === false) return noopAnnouncement
|
|
54
|
+
|
|
55
|
+
return addToQueue(undefined, undefined, delay)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const addToQueue = (message, politeness, delay = false) => {
|
|
59
|
+
// keep track of the id so message can be canceled
|
|
60
|
+
const id = count++
|
|
61
|
+
|
|
62
|
+
// setup a promise to allow developer to chain functionality
|
|
63
|
+
// when specific utterances are done
|
|
64
|
+
let resolveFn
|
|
65
|
+
const done = new Promise((resolve) => {
|
|
66
|
+
resolveFn = resolve
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
// augment the promise with a cancel function
|
|
70
|
+
done.cancel = () => {
|
|
71
|
+
const index = queue.findIndex((item) => item.id === id)
|
|
72
|
+
if (index !== -1) queue.splice(index, 1)
|
|
73
|
+
isProcessing = false
|
|
74
|
+
resolveFn('canceled')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// augment the promise with a stop function
|
|
78
|
+
done.stop = () => {
|
|
79
|
+
if (id === currentId) {
|
|
80
|
+
speechSynthesis.cancel()
|
|
81
|
+
isProcessing = false
|
|
82
|
+
resolveFn('interupted')
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// add message of pause
|
|
87
|
+
if (delay === false) {
|
|
88
|
+
politeness === 'assertive'
|
|
89
|
+
? queue.unshift({ message, resolveFn, id })
|
|
90
|
+
: queue.push({ message, resolveFn, id })
|
|
91
|
+
} else {
|
|
92
|
+
queue.push({ delay, resolveFn, id })
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
setTimeout(() => {
|
|
96
|
+
processQueue()
|
|
97
|
+
}, 100)
|
|
98
|
+
|
|
99
|
+
return done
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const processQueue = async () => {
|
|
103
|
+
if (isProcessing === true || queue.length === 0) return
|
|
104
|
+
isProcessing = true
|
|
105
|
+
|
|
106
|
+
const { message, resolveFn, delay, id } = queue.shift()
|
|
107
|
+
|
|
108
|
+
currentId = id
|
|
109
|
+
|
|
110
|
+
if (delay) {
|
|
111
|
+
setTimeout(() => {
|
|
112
|
+
isProcessing = false
|
|
113
|
+
currentId = null
|
|
114
|
+
resolveFn('finished')
|
|
115
|
+
processQueue()
|
|
116
|
+
}, delay)
|
|
117
|
+
} else {
|
|
118
|
+
if (debounce !== null) clearTimeout(debounce)
|
|
119
|
+
// add some easing when speaking the messages to reduce stuttering
|
|
120
|
+
debounce = setTimeout(() => {
|
|
121
|
+
speechSynthesis
|
|
122
|
+
.speak({ message })
|
|
123
|
+
.then(() => {
|
|
124
|
+
isProcessing = false
|
|
125
|
+
currentId = null
|
|
126
|
+
resolveFn('finished')
|
|
127
|
+
processQueue()
|
|
128
|
+
})
|
|
129
|
+
.catch((e) => {
|
|
130
|
+
isProcessing = false
|
|
131
|
+
currentId = null
|
|
132
|
+
resolveFn(e.error)
|
|
133
|
+
processQueue()
|
|
134
|
+
})
|
|
135
|
+
debounce = null
|
|
136
|
+
}, 200)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const polite = (message) => speak(message, 'polite')
|
|
141
|
+
|
|
142
|
+
const assertive = (message) => speak(message, 'assertive')
|
|
143
|
+
|
|
144
|
+
const stop = () => {
|
|
145
|
+
speechSynthesis.cancel()
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const clear = () => {
|
|
149
|
+
queue.length = 0
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export default {
|
|
153
|
+
speak,
|
|
154
|
+
polite,
|
|
155
|
+
assertive,
|
|
156
|
+
stop,
|
|
157
|
+
enable,
|
|
158
|
+
disable,
|
|
159
|
+
toggle,
|
|
160
|
+
clear,
|
|
161
|
+
pause,
|
|
162
|
+
}
|