├── .editorconfig ├── .gitignore ├── Gruntfile.js ├── README.md ├── bower.json ├── demo ├── demo.css ├── fb.jpg ├── highlight.github.css ├── highlight.pack.js └── support.js ├── dist ├── voice-player.html └── voice-recognition.html ├── index.html ├── package.json └── src ├── voice-player.html └── voice-recognition.html /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # http://editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | # Change these settings to your own preference 9 | indent_style = space 10 | indent_size = 4 11 | 12 | # We recommend you to keep these unchanged 13 | end_of_line = lf 14 | charset = utf-8 15 | trim_trailing_whitespace = true 16 | insert_final_newline = true 17 | 18 | [*.md] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components/ 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | grunt.initConfig({ 4 | 'connect': { 5 | demo: { 6 | options: { 7 | open: true, 8 | keepalive: true 9 | } 10 | } 11 | }, 12 | 'gh-pages': { 13 | options: { 14 | clone: 'bower_components/voice-elements' 15 | }, 16 | src: [ 17 | 'bower_components/**/*', 18 | '!bower_components/voice-elements/**/*', 19 | 'demo/*', 'src/*', 'index.html' 20 | ] 21 | }, 22 | 'replace': { 23 | example: { 24 | src: ['src/*'], 25 | dest: 'dist/', 26 | replacements: [{ 27 | from: 'bower_components', 28 | to: '..' 29 | }] 30 | } 31 | } 32 | }); 33 | 34 | grunt.loadNpmTasks('grunt-contrib-connect'); 35 | grunt.loadNpmTasks('grunt-gh-pages'); 36 | grunt.loadNpmTasks('grunt-text-replace'); 37 | 38 | grunt.registerTask('build', ['replace']); 39 | grunt.registerTask('deploy', ['gh-pages']); 40 | grunt.registerTask('server', ['connect']); 41 | 42 | }; 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Published on Vaadin Directory](https://img.shields.io/badge/Vaadin%20Directory-published-00b4f0.svg)](https://vaadin.com/directory/component/zenorochavoice-elements) 2 | [![Stars on vaadin.com/directory](https://img.shields.io/vaadin-directory/star/binhbbui411shields-badge.svg)](https://vaadin.com/directory/component/zenorochavoice-elements) 3 | 4 | 5 | # <voice-elements> 6 | 7 | > Web Component wrapper to the [Web Speech API](https://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html), that allows you to do voice recognition (speech to text) and speech synthesis (text to speech) using [Polymer](http://www.polymer-project.org/). 8 | 9 | ## Demo 10 | 11 | [Check it live!](http://zenorocha.github.io/voice-elements) 12 | 13 | ## Install 14 | 15 | Install the component using [Bower](http://bower.io/): 16 | 17 | ```sh 18 | $ bower install voice-elements --save 19 | ``` 20 | 21 | Or [download as ZIP](https://github.com/zenorocha/voice-elements/archive/gh-pages.zip). 22 | 23 | ## Usage 24 | 25 | 1. Import Web Components' polyfill: 26 | 27 | ```html 28 | 29 | ``` 30 | 31 | 2. Import Custom Element: 32 | 33 | ```html 34 | 35 | 36 | ``` 37 | 38 | 3. Start using it! 39 | 40 | ```html 41 | 42 | 43 | ``` 44 | 45 | ## <voice-player> 46 | 47 | Provides you a simple DOM API to do speech synthesis (text to speech). 48 | 49 | ### Options 50 | 51 | Attribute | Options | Default | Description 52 | --- | --- | --- | --- 53 | `autoplay` | *boolean* | `false` | Specifies if the audio should play when page loads. 54 | `accent` | `en-US`, `en-GB`, `es-ES`, `fr-FR`, `it-IT`, `de-DE`, `ja-JP`, `ko-KR`, `zh-CN` | `en-US` | Specifies the language to be synthesized and spoken. 55 | `text` | *string* | `You are awesome` | Specifies the text to be synthesized and spoken. 56 | 57 | ### Methods 58 | 59 | Method | Parameters | Returns | Description 60 | --- | --- | --- | --- 61 | `speak()` | None. | Nothing. | Triggers the voice audio to be played. 62 | `cancel()` | None. | Nothing. | Triggers the voice audio to be canceled. 63 | `pause()` | None. | Nothing. | Triggers the voice audio to be paused. 64 | `resume()` | None. | Nothing. | Triggers the voice audio to be resumed. 65 | 66 | ### Events 67 | 68 | Event | Description 69 | --- | --- 70 | `onstart` | Triggers when the voice begun to be spoken. 71 | `onend` | Triggers when the voice completed to be spoken. 72 | `onerror` | Triggers when the voice player detects an error. 73 | `onpause` | Triggers when the voice player is resumed. 74 | `onresume` | Triggers when the voice player is resumed. 75 | 76 | ## <voice-recognition> 77 | 78 | Provides you a simple DOM API to do voice recognition (speech to text). 79 | 80 | ### Options 81 | 82 | Attribute | Options | Default | Description 83 | --- | --- | --- | --- 84 | `continuous` | *boolean* | `true` | Specifies if the recognition should continue when the user pauses while speaking. 85 | `text` | *string* | | Returns the recognized text. 86 | 87 | ### Methods 88 | 89 | Method | Parameters | Returns | Description 90 | --- | --- | --- | --- 91 | `start()` | None. | Nothing. | Starts the voice recognition. 92 | `stop()` | None. | Nothing. | Requests to recognition service to stop listening to more audio. 93 | `abort()` | None. | Nothing. | Requests to immediately stop listening and stop recognizing. 94 | 95 | ### Events 96 | 97 | Event | Description 98 | --- | --- 99 | `onstart` | Triggers when the recognition begins. 100 | `onerror` | Triggers when there's a recognition error. 101 | `onend` | Triggers when the recognition ends. 102 | `onresult` | Triggers when there's a recognition result. 103 | 104 | ## Browser Support 105 | 106 | Unfortunately, the [Web Speech API](https://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html) still have a poor support. Check [Can I Use](http://caniuse.com/#feat=web-speech) for more information. 107 | 108 | ![IE](https://cloud.githubusercontent.com/assets/398893/3528325/20373e76-078e-11e4-8e3a-1cb86cf506f0.png) | ![Chrome](https://cloud.githubusercontent.com/assets/398893/3528328/23bc7bc4-078e-11e4-8752-ba2809bf5cce.png) | ![Firefox](https://cloud.githubusercontent.com/assets/398893/3528329/26283ab0-078e-11e4-84d4-db2cf1009953.png) | ![Opera](https://cloud.githubusercontent.com/assets/398893/3528330/27ec9fa8-078e-11e4-95cb-709fd11dac16.png) | ![Safari](https://cloud.githubusercontent.com/assets/398893/3528331/29df8618-078e-11e4-8e3e-ed8ac738693f.png) 109 | --- | --- | --- | --- | --- | 110 | None ✘ | Latest ✔ | None ✘ | None ✘ | Latest (<voice-player> only) ✔ | 111 | 112 | ## Development 113 | 114 | In order to run it locally you'll need to fetch some dependencies and a basic server setup. 115 | 116 | 1. Install [Bower](http://bower.io/) & [Grunt](http://gruntjs.com/): 117 | 118 | ```sh 119 | $ [sudo] npm install -g bower grunt-cli 120 | ``` 121 | 122 | 2. Install local dependencies: 123 | 124 | ```sh 125 | $ bower install && npm install 126 | ``` 127 | 128 | 3. To test your project, start the development server and open `http://localhost:8000`. 129 | 130 | ```sh 131 | $ grunt server 132 | ``` 133 | 134 | 4. To build the distribution files before releasing a new version. 135 | 136 | ```sh 137 | $ grunt build 138 | ``` 139 | 140 | 5. To provide a live demo, send everything to `gh-pages` branch. 141 | 142 | ```sh 143 | $ grunt deploy 144 | ``` 145 | 146 | ## Contributing 147 | 148 | 1. Fork it! 149 | 2. Create your feature branch: `git checkout -b my-new-feature` 150 | 3. Commit your changes: `git commit -m 'Add some feature'` 151 | 4. Push to the branch: `git push origin my-new-feature` 152 | 5. Submit a pull request :D 153 | 154 | ## History 155 | 156 | For detailed changelog, check [Releases](https://github.com/zenorocha/voice-elements/releases). 157 | 158 | ## License 159 | 160 | [MIT License](http://zenorocha.mit-license.org/) © Zeno Rocha 161 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "voice-elements", 3 | "version": "1.1.0", 4 | "description": "Web Speech API wrapper that allows you to do voice recognition (speech to text) and speech synthesis (text to speech) using Polymer.", 5 | "authors": [ 6 | "Zeno Rocha " 7 | ], 8 | "license": "MIT", 9 | "main": "dist/voice-player.html", 10 | "keywords": [ 11 | "polymer", 12 | "speech", 13 | "speech-synthesis", 14 | "voice", 15 | "voice-player", 16 | "voice-recognition", 17 | "web-speech", 18 | "web-components" 19 | ], 20 | "ignore": [ 21 | "**/.*", 22 | "node_modules", 23 | "bower_components" 24 | ], 25 | "dependencies": { 26 | "polymer": "Polymer/polymer#^1.0.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /demo/demo.css: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | Global selectors 3 | ========================================================================== */ 4 | 5 | * { 6 | -webkit-box-sizing: border-box; 7 | -moz-box-sizing: border-box; 8 | box-sizing: border-box; 9 | } 10 | 11 | body { 12 | line-height: 1.7em; 13 | color: #7f8c8d; 14 | font-size: 14px; 15 | font-family: 'Roboto Slab', serif; 16 | } 17 | 18 | h1, h2, h3, h4, h5, h6 { 19 | color: #34495e; 20 | } 21 | 22 | pre { 23 | display: block; 24 | padding: 9.5px; 25 | margin: 0 0 10px; 26 | font-size: 14px; 27 | line-height: 20px; 28 | word-break: break-all; 29 | word-wrap: break-word; 30 | white-space: pre; 31 | white-space: pre-wrap; 32 | background-color: #f8f8f8; 33 | border-radius: 4px; 34 | font-family: Monaco, Consolas, "Lucida Console", monospace; 35 | } 36 | 37 | /* ========================================================================== 38 | Layout styles 39 | ========================================================================== */ 40 | 41 | .l-box { 42 | padding: 1em; 43 | } 44 | 45 | .l-box-lrg { 46 | padding: 2em; 47 | } 48 | 49 | .section { 50 | margin-bottom: 2em !important; 51 | } 52 | 53 | .is-center { 54 | text-align: center; 55 | } 56 | 57 | 58 | /* ========================================================================== 59 | Pure button 60 | ========================================================================== */ 61 | 62 | .pure-button { 63 | background-color: #1f8dd6; 64 | color: white; 65 | padding: 0.5em 2em; 66 | border-radius: 5px; 67 | } 68 | 69 | a.pure-button-primary { 70 | background: white; 71 | color: #1f8dd6; 72 | border-radius: 5px; 73 | font-size: 120%; 74 | } 75 | 76 | /* ========================================================================== 77 | Browser Support 78 | ========================================================================== */ 79 | 80 | .brower-support { 81 | width: 100%; 82 | height: 60px; 83 | background: white; 84 | text-align: center; 85 | position: fixed; 86 | z-index: 2; 87 | display: none; 88 | } 89 | .brower-support a { 90 | color: #EA3556; 91 | text-decoration: none; 92 | border-bottom: 1px dotted #EA3556; 93 | } 94 | 95 | /* ========================================================================== 96 | Splash Screen 97 | ========================================================================== */ 98 | 99 | .splash-container { 100 | background: #EA3556; 101 | z-index: 1; 102 | overflow: hidden; 103 | /* The following styles are required for the "scroll-over" effect */ 104 | width: 100%; 105 | height: 88%; 106 | top: 0; 107 | left: 0; 108 | position: fixed; 109 | } 110 | 111 | .splash { 112 | /* absolute center .splash within .splash-container */ 113 | width: 350px; 114 | height: 50%; 115 | margin: auto; 116 | position: absolute; 117 | top: 70px; left: 0; bottom: 0; right: 0; 118 | text-align: center; 119 | } 120 | 121 | /* This is the main heading that appears on the blue section */ 122 | .splash-head { 123 | font-size: 45px; 124 | font-weight: bold; 125 | color: white; 126 | font-weight: 100; 127 | border-radius: 5px; 128 | line-height: 1em; 129 | opacity: 1; 130 | color: rgba(255, 255, 255, .4); 131 | margin: 0; 132 | } 133 | 134 | .splash-first-line { 135 | display: inline; 136 | } 137 | 138 | .splash-second-line { 139 | display: inline-block; 140 | } 141 | 142 | .highlight { 143 | color: rgba(255, 255, 255, 1); 144 | } 145 | 146 | /* This is the subheading that appears on the blue section */ 147 | .splash-subhead { 148 | color: white; 149 | font-size: 13px; 150 | letter-spacing: 0.05em; 151 | opacity: 0.8; 152 | text-transform: uppercase; 153 | padding: 1em 0; 154 | } 155 | 156 | .gh-btns { 157 | margin-bottom: 0; 158 | } 159 | 160 | /* ========================================================================== 161 | Content Area 162 | ========================================================================== */ 163 | 164 | .content-wrapper { 165 | /* These styles are required for the "scroll-over" effect */ 166 | position: absolute; 167 | top: 87%; 168 | width: 100%; 169 | min-height: 12%; 170 | z-index: 2; 171 | background: white; 172 | } 173 | 174 | .content { 175 | padding: 1em; 176 | } 177 | 178 | /* This is the class used for the main content headers (

) */ 179 | .content-head { 180 | font-weight: 400; 181 | text-transform: uppercase; 182 | letter-spacing: 0.1em; 183 | margin: 2em 0 1em; 184 | /*width: 960px;*/ 185 | } 186 | 187 | /* This is a modifier class used when the content-head is inside a ribbon */ 188 | .content-head-ribbon { 189 | color: #06a0a5; 190 | font-family: 'Roboto Slab', serif; 191 | } 192 | 193 | /* This is the class used for the content sub-headers (

) */ 194 | .content-subhead { 195 | color: #1f8dd6; 196 | } 197 | 198 | /* This is the class used for the dark-background areas. */ 199 | .ribbon { 200 | background: #61D2D6; /*#61D2D6;*/ 201 | } 202 | 203 | .ribbon p { 204 | color: #fff; 205 | } 206 | 207 | .features a { 208 | color: #1f8dd6; 209 | text-decoration: none; 210 | border-bottom: 1px dotted #1f8dd6; 211 | } 212 | 213 | /* Code example 214 | ========================================================================== */ 215 | 216 | #demo-input { 217 | width: 50%; 218 | } 219 | 220 | .center { 221 | max-width: 768px; 222 | margin: 0 auto; 223 | display: block; 224 | } 225 | 226 | .example { 227 | position: relative; 228 | margin: 15px 0; 229 | padding: 39px 19px 14px; 230 | background-color: #fff; 231 | border-radius: 4px 4px 0 0; 232 | } 233 | 234 | .example p { 235 | color: #666; 236 | } 237 | 238 | .example:after { 239 | content: "Example"; 240 | position: absolute; 241 | top: 0; 242 | left: 0; 243 | padding: 2px 8px; 244 | font-size: 12px; 245 | font-weight: bold; 246 | background-color: #f5f5f5; 247 | color: #9da0a4; 248 | border-radius: 4px 0 4px 0; 249 | } 250 | 251 | .example + pre { 252 | margin-top: -20px; 253 | padding-top: 15px; 254 | } 255 | 256 | /* Pure Table 257 | ========================================================================== */ 258 | 259 | .pure-table { 260 | font-size: 12px; 261 | background-color: #fff; 262 | border-radius: 4px; 263 | border-collapse: separate; 264 | overflow: hidden; 265 | border: 0; 266 | } 267 | 268 | .pure-table thead { 269 | background: #06a0a5; 270 | color: #fff; 271 | } 272 | 273 | .pure-table td, .pure-table th { 274 | border: 0; 275 | } 276 | 277 | /* ========================================================================== 278 | Footer 279 | ========================================================================== */ 280 | 281 | .footer { 282 | color: #fff; 283 | background: #EA3556; 284 | } 285 | 286 | .footer a { 287 | color: #fdacbb; 288 | text-decoration: none; 289 | border-bottom: 1px dotted #fdacbb; 290 | } 291 | 292 | /* Love animation 293 | ========================================================================== */ 294 | 295 | .love { 296 | color: #fdacbb; 297 | display: inline-block; 298 | position: relative; 299 | top: .2em; 300 | font-size: 1.4em; 301 | -webkit-transform: scale(.9); 302 | -moz-transform: scale(.9); 303 | transform: scale(.9); 304 | -webkit-animation: love .5s infinite linear alternate-reverse; 305 | -moz-animation: love .5s infinite linear alternate-reverse; 306 | animation: love .5s infinite linear alternate-reverse; 307 | } 308 | 309 | @-webkit-keyframes love { 310 | to {-webkit-transform: scale(1.2);} 311 | } 312 | 313 | @-moz-keyframes love { 314 | to {-moz-transform: scale(1.2);} 315 | } 316 | @keyframes love { 317 | to {transform: scale(1.2);} 318 | } 319 | 320 | /* ========================================================================== 321 | Media Queries 322 | ========================================================================== */ 323 | 324 | /* Tablet (and up) 325 | ========================================================================== */ 326 | 327 | @media (min-width: 48em) { 328 | body { 329 | font-size: 16px; 330 | } 331 | 332 | .splash { 333 | width: 400px; 334 | } 335 | 336 | .splash-head { 337 | font-size: 70px; 338 | } 339 | 340 | .splash-first-line { 341 | padding-right: 183px; 342 | } 343 | 344 | .splash-subhead { 345 | font-size: 14px; 346 | } 347 | 348 | .pure-table { 349 | font-size: 16px; 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /demo/fb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenorocha/voice-elements/351a0bc94e8534c8b8ff554f8be1e141b8b03b11/demo/fb.jpg -------------------------------------------------------------------------------- /demo/highlight.github.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | github.com style (c) Vasily Polovnyov 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; padding: 0.5em; 9 | color: #333; 10 | background: #f8f8f8 11 | } 12 | 13 | .hljs-comment, 14 | .hljs-template_comment, 15 | .diff .hljs-header, 16 | .hljs-javadoc { 17 | color: #998; 18 | font-style: italic 19 | } 20 | 21 | .hljs-keyword, 22 | .css .rule .hljs-keyword, 23 | .hljs-winutils, 24 | .javascript .hljs-title, 25 | .nginx .hljs-title, 26 | .hljs-subst, 27 | .hljs-request, 28 | .hljs-status { 29 | color: #333; 30 | font-weight: bold 31 | } 32 | 33 | .hljs-number, 34 | .hljs-hexcolor, 35 | .ruby .hljs-constant { 36 | color: #099; 37 | } 38 | 39 | .hljs-string, 40 | .hljs-tag .hljs-value, 41 | .hljs-phpdoc, 42 | .tex .hljs-formula { 43 | color: #d14 44 | } 45 | 46 | .hljs-title, 47 | .hljs-id, 48 | .coffeescript .hljs-params, 49 | .scss .hljs-preprocessor { 50 | color: #900; 51 | font-weight: bold 52 | } 53 | 54 | .javascript .hljs-title, 55 | .lisp .hljs-title, 56 | .clojure .hljs-title, 57 | .hljs-subst { 58 | font-weight: normal 59 | } 60 | 61 | .hljs-class .hljs-title, 62 | .haskell .hljs-type, 63 | .vhdl .hljs-literal, 64 | .tex .hljs-command { 65 | color: #458; 66 | font-weight: bold 67 | } 68 | 69 | .hljs-tag, 70 | .hljs-tag .hljs-title, 71 | .hljs-rules .hljs-property, 72 | .django .hljs-tag .hljs-keyword { 73 | color: #000080; 74 | font-weight: normal 75 | } 76 | 77 | .hljs-attribute, 78 | .hljs-variable, 79 | .lisp .hljs-body { 80 | color: #008080 81 | } 82 | 83 | .hljs-regexp { 84 | color: #009926 85 | } 86 | 87 | .hljs-symbol, 88 | .ruby .hljs-symbol .hljs-string, 89 | .lisp .hljs-keyword, 90 | .tex .hljs-special, 91 | .hljs-prompt { 92 | color: #990073 93 | } 94 | 95 | .hljs-built_in, 96 | .lisp .hljs-title, 97 | .clojure .hljs-built_in { 98 | color: #0086b3 99 | } 100 | 101 | .hljs-preprocessor, 102 | .hljs-pragma, 103 | .hljs-pi, 104 | .hljs-doctype, 105 | .hljs-shebang, 106 | .hljs-cdata { 107 | color: #999; 108 | font-weight: bold 109 | } 110 | 111 | .hljs-deletion { 112 | background: #fdd 113 | } 114 | 115 | .hljs-addition { 116 | background: #dfd 117 | } 118 | 119 | .diff .hljs-change { 120 | background: #0086b3 121 | } 122 | 123 | .hljs-chunk { 124 | color: #aaa 125 | } 126 | -------------------------------------------------------------------------------- /demo/highlight.pack.js: -------------------------------------------------------------------------------- 1 | var hljs=new function(){function k(v){return v.replace(/&/gm,"&").replace(//gm,">")}function t(v){return v.nodeName.toLowerCase()}function i(w,x){var v=w&&w.exec(x);return v&&v.index==0}function d(v){return Array.prototype.map.call(v.childNodes,function(w){if(w.nodeType==3){return b.useBR?w.nodeValue.replace(/\n/g,""):w.nodeValue}if(t(w)=="br"){return"\n"}return d(w)}).join("")}function r(w){var v=(w.className+" "+(w.parentNode?w.parentNode.className:"")).split(/\s+/);v=v.map(function(x){return x.replace(/^language-/,"")});return v.filter(function(x){return j(x)||x=="no-highlight"})[0]}function o(x,y){var v={};for(var w in x){v[w]=x[w]}if(y){for(var w in y){v[w]=y[w]}}return v}function u(x){var v=[];(function w(y,z){for(var A=y.firstChild;A;A=A.nextSibling){if(A.nodeType==3){z+=A.nodeValue.length}else{if(t(A)=="br"){z+=1}else{if(A.nodeType==1){v.push({event:"start",offset:z,node:A});z=w(A,z);v.push({event:"stop",offset:z,node:A})}}}}return z})(x,0);return v}function q(w,y,C){var x=0;var F="";var z=[];function B(){if(!w.length||!y.length){return w.length?w:y}if(w[0].offset!=y[0].offset){return(w[0].offset"}function E(G){F+=""}function v(G){(G.event=="start"?A:E)(G.node)}while(w.length||y.length){var D=B();F+=k(C.substr(x,D[0].offset-x));x=D[0].offset;if(D==w){z.reverse().forEach(E);do{v(D.splice(0,1)[0]);D=B()}while(D==w&&D.length&&D[0].offset==x);z.reverse().forEach(A)}else{if(D[0].event=="start"){z.push(D[0].node)}else{z.pop()}v(D.splice(0,1)[0])}}return F+k(C.substr(x))}function m(y){function v(z){return(z&&z.source)||z}function w(A,z){return RegExp(v(A),"m"+(y.cI?"i":"")+(z?"g":""))}function x(D,C){if(D.compiled){return}D.compiled=true;D.k=D.k||D.bK;if(D.k){var z={};function E(G,F){if(y.cI){F=F.toLowerCase()}F.split(" ").forEach(function(H){var I=H.split("|");z[I[0]]=[G,I[1]?Number(I[1]):1]})}if(typeof D.k=="string"){E("keyword",D.k)}else{Object.keys(D.k).forEach(function(F){E(F,D.k[F])})}D.k=z}D.lR=w(D.l||/\b[A-Za-z0-9_]+\b/,true);if(C){if(D.bK){D.b=D.bK.split(" ").join("|")}if(!D.b){D.b=/\B|\b/}D.bR=w(D.b);if(!D.e&&!D.eW){D.e=/\B|\b/}if(D.e){D.eR=w(D.e)}D.tE=v(D.e)||"";if(D.eW&&C.tE){D.tE+=(D.e?"|":"")+C.tE}}if(D.i){D.iR=w(D.i)}if(D.r===undefined){D.r=1}if(!D.c){D.c=[]}var B=[];D.c.forEach(function(F){if(F.v){F.v.forEach(function(G){B.push(o(F,G))})}else{B.push(F=="self"?D:F)}});D.c=B;D.c.forEach(function(F){x(F,D)});if(D.starts){x(D.starts,C)}var A=D.c.map(function(F){return F.bK?"\\.?\\b("+F.b+")\\b\\.?":F.b}).concat([D.tE]).concat([D.i]).map(v).filter(Boolean);D.t=A.length?w(A.join("|"),true):{exec:function(F){return null}};D.continuation={}}x(y)}function c(S,L,J,R){function v(U,V){for(var T=0;T";U+=Z+'">';return U+X+Y}function N(){var U=k(C);if(!I.k){return U}var T="";var X=0;I.lR.lastIndex=0;var V=I.lR.exec(U);while(V){T+=U.substr(X,V.index-X);var W=E(I,V);if(W){H+=W[1];T+=w(W[0],V[0])}else{T+=V[0]}X=I.lR.lastIndex;V=I.lR.exec(U)}return T+U.substr(X)}function F(){if(I.sL&&!f[I.sL]){return k(C)}var T=I.sL?c(I.sL,C,true,I.continuation.top):g(C);if(I.r>0){H+=T.r}if(I.subLanguageMode=="continuous"){I.continuation.top=T.top}return w(T.language,T.value,false,true)}function Q(){return I.sL!==undefined?F():N()}function P(V,U){var T=V.cN?w(V.cN,"",true):"";if(V.rB){D+=T;C=""}else{if(V.eB){D+=k(U)+T;C=""}else{D+=T;C=U}}I=Object.create(V,{parent:{value:I}})}function G(T,X){C+=T;if(X===undefined){D+=Q();return 0}var V=v(X,I);if(V){D+=Q();P(V,X);return V.rB?0:X.length}var W=z(I,X);if(W){var U=I;if(!(U.rE||U.eE)){C+=X}D+=Q();do{if(I.cN){D+=""}H+=I.r;I=I.parent}while(I!=W.parent);if(U.eE){D+=k(X)}C="";if(W.starts){P(W.starts,"")}return U.rE?0:X.length}if(A(X,I)){throw new Error('Illegal lexeme "'+X+'" for mode "'+(I.cN||"")+'"')}C+=X;return X.length||1}var M=j(S);if(!M){throw new Error('Unknown language: "'+S+'"')}m(M);var I=R||M;var D="";for(var K=I;K!=M;K=K.parent){if(K.cN){D=w(K.cN,D,true)}}var C="";var H=0;try{var B,y,x=0;while(true){I.t.lastIndex=x;B=I.t.exec(L);if(!B){break}y=G(L.substr(x,B.index-x),B[0]);x=B.index+y}G(L.substr(x));for(var K=I;K.parent;K=K.parent){if(K.cN){D+=""}}return{r:H,value:D,language:S,top:I}}catch(O){if(O.message.indexOf("Illegal")!=-1){return{r:0,value:k(L)}}else{throw O}}}function g(y,x){x=x||b.languages||Object.keys(f);var v={r:0,value:k(y)};var w=v;x.forEach(function(z){if(!j(z)){return}var A=c(z,y,false);A.language=z;if(A.r>w.r){w=A}if(A.r>v.r){w=v;v=A}});if(w.language){v.second_best=w}return v}function h(v){if(b.tabReplace){v=v.replace(/^((<[^>]+>|\t)+)/gm,function(w,z,y,x){return z.replace(/\t/g,b.tabReplace)})}if(b.useBR){v=v.replace(/\n/g,"
")}return v}function p(z){var y=d(z);var A=r(z);if(A=="no-highlight"){return}var v=A?c(A,y,true):g(y);var w=u(z);if(w.length){var x=document.createElementNS("http://www.w3.org/1999/xhtml","pre");x.innerHTML=v.value;v.value=q(w,u(x),y)}v.value=h(v.value);z.innerHTML=v.value;z.className+=" hljs "+(!A&&v.language||"");z.result={language:v.language,re:v.r};if(v.second_best){z.second_best={language:v.second_best.language,re:v.second_best.r}}}var b={classPrefix:"hljs-",tabReplace:null,useBR:false,languages:undefined};function s(v){b=o(b,v)}function l(){if(l.called){return}l.called=true;var v=document.querySelectorAll("pre code");Array.prototype.forEach.call(v,p)}function a(){addEventListener("DOMContentLoaded",l,false);addEventListener("load",l,false)}var f={};var n={};function e(v,x){var w=f[v]=x(this);if(w.aliases){w.aliases.forEach(function(y){n[y]=v})}}function j(v){return f[v]||f[n[v]]}this.highlight=c;this.highlightAuto=g;this.fixMarkup=h;this.highlightBlock=p;this.configure=s;this.initHighlighting=l;this.initHighlightingOnLoad=a;this.registerLanguage=e;this.getLanguage=j;this.inherit=o;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE]};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE]};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.REGEXP_MODE={cN:"regexp",b:/\//,e:/\/[gim]*/,i:/\n/,c:[this.BE,{b:/\[/,e:/\]/,r:0,c:[this.BE]}]};this.TM={cN:"title",b:this.IR,r:0};this.UTM={cN:"title",b:this.UIR,r:0}}();hljs.registerLanguage("javascript",function(a){return{aliases:["js"],k:{keyword:"in if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const class",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require"},c:[{cN:"pi",b:/^\s*('|")use strict('|")/,r:10},a.ASM,a.QSM,a.CLCM,a.CBLCLM,a.CNM,{b:"("+a.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[a.CLCM,a.CBLCLM,a.REGEXP_MODE,{b:/;/,r:0,sL:"xml"}],r:0},{cN:"function",bK:"function",e:/\{/,c:[a.inherit(a.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/}),{cN:"params",b:/\(/,e:/\)/,c:[a.CLCM,a.CBLCLM],i:/["'\(]/}],i:/\[|%/},{b:/\$[(.]/},{b:"\\."+a.IR,r:0}]}});hljs.registerLanguage("xml",function(a){var c="[A-Za-z0-9\\._:-]+";var d={b:/<\?(php)?(?!\w)/,e:/\?>/,sL:"php",subLanguageMode:"continuous"};var b={eW:true,i:/]+/}]}]}]};return{aliases:["html"],cI:true,c:[{cN:"doctype",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"|$)",e:">",k:{title:"style"},c:[b],starts:{e:"",rE:true,sL:"css"}},{cN:"tag",b:"|$)",e:">",k:{title:"script"},c:[b],starts:{e:"<\/script>",rE:true,sL:"javascript"}},{b:"<%",e:"%>",sL:"vbscript"},d,{cN:"pi",b:/<\?\w+/,e:/\?>/,r:10},{cN:"tag",b:"",c:[{cN:"title",b:"[^ /><]+",r:0},b]}]}}); -------------------------------------------------------------------------------- /demo/support.js: -------------------------------------------------------------------------------- 1 | (function (window, undefined) { 2 | var SpeechRecognition = window.webkitSpeechRecognition; 3 | var SpeechSynthesis = window.speechSynthesis; 4 | 5 | if (!SpeechRecognition || !SpeechSynthesis) { 6 | var supportWarning = document.getElementsByClassName('brower-support'); 7 | supportWarning[0].style.display = 'block'; 8 | } 9 | })(window); 10 | -------------------------------------------------------------------------------- /dist/voice-player.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 84 | -------------------------------------------------------------------------------- /dist/voice-recognition.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 94 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | <voice-elements> 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |

Sorry, your browser does not support the Web Speech API :(

35 |
36 | 37 | 38 |
39 |
40 |

41 |
<voice
42 |
elements>
43 |

44 |

45 | A couple of Web Components that can do
amazing stuff using the Web Speech API 46 |

47 |

48 | 50 | 52 |

53 |
54 |
55 | 56 | 57 |
58 | 59 | 60 |
61 |

Install

62 | 63 |
64 |
65 |
66 |

Install the component using Bower

67 |
$ bower install voice-elements
68 | Fork on GitHub 69 | Download as ZIP 70 |
71 |
72 |
73 |
74 | 75 | 76 |
77 |
78 |

<voice-player>

79 |

Provides you a simple DOM API to do speech synthesis (text to speech).

80 | 81 | 82 |

In the following demo, we set some content into the text attribute. Then, by using the autoplay attribute, the voice is played when the element loads.

83 | 84 | 85 | 86 |
87 |

Reload the page to hear it again.

88 |
89 | 90 |
<voice-player autoplay text="Welcome to the jungle! hahaha just kidding!"></voice-player>
91 | 92 | 93 |

In the following demo, we define a different language by using the accent attribute. When the button is clicked, we call the speak method to trigger the audio.

94 | 95 | 96 | 107 | 108 |
109 |
110 |
111 | 112 |
113 |
114 |
115 | 116 |
<voice-player id="mi-elemento" accent="es-ES" text="Me gusta la gasolina"></voice-player>
117 | 
118 | <script>
119 | var form = document.querySelector('#mi-form'),
120 |     element = document.querySelector('#mi-elemento');
121 | 
122 | form.addEventListener('submit', function(e) {
123 |     e.preventDefault();
124 |     element.speak();
125 | });
126 | </script>
127 | 128 | 129 |

In the following demo, the text attribute is set according to the input value. When the form is submitted, we call the speak method to trigger the audio.

130 | 131 | 132 | 150 | 151 |
152 |
153 |
154 | 155 | 156 |
157 |
158 |
159 | 160 |
<voice-player id="player-element" text=""></voice-player>
161 | 
162 | <script>
163 | var form = document.querySelector('#player-form'),
164 |     input = document.querySelector('#player-input'),
165 |     element = document.querySelector('#player-element');
166 | 
167 | input.addEventListener('input', function(e) {
168 |     element.setAttribute('text', input.value);
169 | });
170 | 
171 | form.addEventListener('submit', function(e) {
172 |     e.preventDefault();
173 |     element.speak();
174 | });
175 | </script>
176 | 
177 | 178 | 179 |

Options

180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 |
AttributeOptionsDefaultDescription
autoplaybooleanfalseSpecifies if the audio should play when page loads.
accenten-US, en-GB, es-ES, fr-FR, it-IT, de-DE, ja-JP, ko-KR, zh-CNen-USSpecifies the language to be synthesized and spoken.
textstringYou are awesomeSpecifies the text to be synthesized and spoken.
211 | 212 | 213 |

Methods

214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 |
MethodParametersReturnsDescription
speak()NoneNothingTriggers the voice audio to be played.
cancel()NoneNothingTriggers the voice audio to be canceled.
pause()NoneNothingTriggers the voice audio to be paused.
resume()NoneNothingTriggers the voice audio to be resumed.
251 | 252 | 253 |

Events

254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 |
EventDescription
onstartTriggers when the voice begun to be spoken.
onendTriggers when the voice completed to be spoken.
onerrorTriggers when the voice player detects an error.
onpauseTriggers when the voice player is resumed.
onresumeTriggers when the voice player is resumed.
285 |
286 |
287 | 288 | 289 |
290 |

Features

291 | 292 |
293 |
294 |

295 | Voice recognition (speech to text) 296 |

297 |

298 | Voice recognition is the translation of spoken words into text. This is achieved in the browser by using the SpeechRecognition interface from the Web Speech API. 299 |

300 |
301 |
302 |

303 | Speech synthesis (text to speech) 304 |

305 |

306 | Speech synthesis is the conversion of language text into speech. This is achieved in the browser by using then SpeechSynthesis interface from the Web Speech API. 307 |

308 |
309 |
310 |
311 | 312 | 313 |
314 |
315 |

<voice-recognition>

316 |

Provides you a simple DOM API to do voice recognition (speech to text).

317 | 318 | 319 |

In the following demo, we trigger the voice recognition by using the start method when the user submits the form. Then, we add a listener to the result event to fetch the recognized content and put it into the textarea.

320 | 321 | 322 | 338 | 339 |
340 |

Click "start", authorize, and say something...

341 | 342 |
343 |
344 | 345 |
346 |
347 | 348 |
349 |
350 |
351 | 352 |
<voice-recognition id="recognition-element"></voice-recognition>
353 | 
354 | <script>
355 | var form = document.querySelector('#recognition-form'),
356 |     input = document.querySelector('#recognition-input'),
357 |     element = document.querySelector('#recognition-element');
358 | 
359 | form.addEventListener('submit', function(e) {
360 |     e.preventDefault();
361 |     element.start();
362 | });
363 | 
364 | element.addEventListener('result', function(e) {
365 |     input.textContent = e.detail.result;
366 | });
367 | </script>
368 | 
369 | 370 | 371 |

Options

372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 |
AttributeOptionsDefaultDescription
continuousbooleantrueSpecifies if the recognition should continue when the user pauses while speaking.
accenten-US, en-GB, es-ES, fr-FR, it-IT, de-DE, ja-JP, ko-KR, zh-CNen-USSpecifies the language to be synthesized.
textstringReturns the recognized text.
403 | 404 | 405 |

Methods

406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 |
MethodParametersReturnsDescription
start()NoneNothingStarts the voice recognition.
stop()NoneNothingRequests to recognition service to stop listening to more audio.
abort()NoneNothingRequests to immediately stop listening and stop recognizing.
437 | 438 | 439 |

Events

440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 |
EventDescription
onstartTriggers when the recognition begins.
onerrorTriggers when there's a recognition error.
onendTriggers when the recognition ends.
onresultTriggers when there's a recognition result.
467 |
468 |
469 | 470 | 471 |
472 |

Usage

473 | 474 |
475 |
476 |

1. Import Web Components' polyfill

477 |
<script src="bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
478 | 479 |

2. Import Custom Element

480 |
<link rel="import" href="bower_components/voice-elements/dist/voice-player.html">
481 | <link rel="import" href="bower_components/voice-elements/dist/voice-recognition.html">
482 | 483 |

3. Start using it!

484 |
<voice-player></voice-player>
485 | <voice-recognition></voice-recognition>
486 |
487 |
488 |
489 | 490 | 491 | 496 |
497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 516 | 517 | 518 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "devDependencies": { 4 | "grunt": "~0.4.1", 5 | "grunt-cli": "~0.1.9", 6 | "grunt-contrib-connect": "~0.9.0", 7 | "grunt-gh-pages": "~0.9.1", 8 | "grunt-text-replace": "~0.4.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/voice-player.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 84 | -------------------------------------------------------------------------------- /src/voice-recognition.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 94 | --------------------------------------------------------------------------------