1+ import attachTemplate from '../util/attachTemplate.js' ;
2+ // import { MyElement } from '../MyElement.js';
3+
4+ const { speechSynthesis, SpeechSynthesisUtterance } = window ;
5+
16/**
27 * Speech synthesis using the Web Speech API.
38 *
1621 * @status experimental
1722 * @since 1.X.0
1823 */
19-
20- import { MyElement } from '../MyElement.js' ;
21-
22- const { speechSynthesis, SpeechSynthesisUtterance } = window ;
23-
24- export class MyTextToSpeechElement extends MyElement {
24+ export class MyTextToSpeechElement extends HTMLElement {
2525 static getTag ( ) {
2626 return 'my-text-to-speech' ; // Was: 'my-speech-synthesis';
2727 }
2828
29- /* constructor () { // "Useless constructor"!
30- super() ;
31- } */
29+ get pitch ( ) { return parseFloat ( this . getAttribute ( 'pitch' ) ) || 1 ; } // Default: 1; Range: 0-2;
30+ get rate ( ) { return parseFloat ( this . getAttribute ( 'rate' ) ) || 1 ; } // Default: 1; Range: 0.1-10 ;
31+ get volume ( ) { return parseFloat ( this . getAttribute ( 'volume' ) ) || 1 ; } // Default: 1; Range: 0-1;
3232
3333 async connectedCallback ( ) {
3434 const langRegex = this . getAttribute ( 'lang-regex' ) || 'en.*' ;
@@ -37,11 +37,12 @@ export class MyTextToSpeechElement extends MyElement {
3737 const rate = parseFloat ( this . getAttribute ( 'rate' ) ) || 1 ; // Default: 1; Range: 0.1-10;
3838 const volume = parseFloat ( this . getAttribute ( 'volume' ) ) || 1 ; // Range: 0-1;
3939
40- await this . _initialize ( { langRegex, voxRegex, pitch, rate, volume } ) ;
40+ await this . #initialize ( { langRegex, voxRegex, pitch, rate, volume } ) ;
4141 }
4242
43- async _initialize ( ATTR = { } ) {
44- await this . getTemplate ( 'my-text-to-speech' ) ;
43+ async #initialize ( ATTR = { } ) {
44+ attachTemplate ( this . #htmlTemplate) . to . shadowDOM ( this ) ;
45+ // Was: await this.getTemplate('my-text-to-speech');
4546
4647 const utterElem = this . shadowRoot . querySelector ( '#utterance' ) ;
4748 const voxSelect = this . shadowRoot . querySelector ( '#vox select' ) ;
@@ -54,15 +55,15 @@ export class MyTextToSpeechElement extends MyElement {
5455
5556 speechSynthesis . onvoiceschanged = this . _addVoicesToSelect ;
5657
57- setTimeout ( ( ) => this . _addVoicesToSelect ( ) , 1500 ) ;
58+ setTimeout ( ( ) => this . #addVoicesToSelect ( ) , 1500 ) ;
5859
5960 console . debug ( 'my-text-to-speech:' , this ) ;
6061 console . dir ( this ) ;
6162 }
6263
6364 _speak ( say = null , ev = null ) {
6465 const ATTR = this . $$ ;
65- const SAY = this . _getText ( say ) ;
66+ const SAY = this . #getText ( say ) ;
6667 const VOX = this . _findVoiceByName ( ATTR . voxRegex ) ; // /(Fiona|Goo*UK Eng*Female)/);
6768
6869 const utterThis = new SpeechSynthesisUtterance ( SAY ) ;
@@ -78,7 +79,7 @@ export class MyTextToSpeechElement extends MyElement {
7879 console . debug ( 'Speak:' , SAY , utterThis , VOX , ev ) ;
7980 }
8081
81- _getText ( say = null ) {
82+ #getText ( say = null ) {
8283 const TEXT = this . textContent . replace ( / [ \n ] { 2 , } / g, '\n' ) ;
8384 const EL = this . $$ . utterElem ; // this.shadowRoot.querySelector('#utterance');
8485 // console.debug('Utter:', ELEM, ELEM.innerHTML, ELEM.content);
@@ -100,7 +101,7 @@ export class MyTextToSpeechElement extends MyElement {
100101 return VOX . find ( vox => nameRegex . test ( vox . name ) ) ;
101102 }
102103
103- _addVoicesToSelect ( ) {
104+ #addVoicesToSelect ( ) {
104105 const ALL = / \? v o x = a l l / . test ( document . location . href ) ;
105106 const SELECT = this . $$ . voxSelect ; // document.querySelector('#vox');
106107 const LOG = this . $$ . log ; // document.querySelector('#log');
@@ -126,4 +127,57 @@ export class MyTextToSpeechElement extends MyElement {
126127
127128 console . debug ( 'Voices:' , SELECT , VOICES ) ;
128129 }
130+
131+ // get #utterElem () { return this.shadowRoot.querySelector('#utterance'); }
132+ // get #button () { return this.shadowRoot.querySelector('button'); }
133+
134+ get #stylesheet ( ) {
135+ return `
136+ button,
137+ select {
138+ font: inherit;
139+ padding: .5rem;
140+ }
141+ button {
142+ padding: .2rem 2rem;
143+ }
144+ select {
145+ margin: 0 .5rem;
146+ min-width: 9rem;
147+ max-width: 100%;
148+ }
149+ pre {
150+ font-size: small;
151+ line-height: 1.2;
152+ }
153+ label,
154+ [ contenteditable = true ] {
155+ X-padding: 0 .2rem;
156+ outline-offset: .3rem;
157+ }
158+ ` ;
159+ }
160+
161+ get #htmlTemplate ( ) {
162+ return `
163+ <template>
164+ <style>${ this . #stylesheet} </style>
165+
166+ <div id="utterance" contenteditable="true" aria-label="Text to speak" part="utterance">
167+ <slot> Hello! You can edit me. </slot>
168+ </div>
169+ <!-- <textarea><slot></slot></textarea> -->
170+
171+ <p>
172+ <label id="vox">Voice:<select></select></label>
173+ <button part="button"> Speak </button>
174+ </p>
175+
176+ <details part="details">
177+ <summary> Voices </summary>
178+ <pre id="log"><pre>
179+ </details>
180+ </template>
181+ ` ;
182+ }
129183}
0 commit comments