├── .gitignore ├── demo.gif ├── public ├── images │ └── mic.png ├── img │ ├── mic128.png │ ├── mic.svg │ └── save.svg ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── js │ ├── audiodisplay.js │ ├── FileSaver.min.js │ ├── recorderjs │ │ ├── recorder.js │ │ └── recorderWorker.js │ ├── p5 │ │ └── cellgrid.js │ ├── main.js │ ├── bootstrap.min.js │ └── p5libs │ │ └── p5.dom.js ├── css │ └── custom.css └── index.html ├── cris.json ├── package.json ├── LICENSE.txt ├── README.md ├── app.js └── cellmover.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ritazh/speech-to-text-demo/HEAD/demo.gif -------------------------------------------------------------------------------- /public/images/mic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ritazh/speech-to-text-demo/HEAD/public/images/mic.png -------------------------------------------------------------------------------- /public/img/mic128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ritazh/speech-to-text-demo/HEAD/public/img/mic128.png -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ritazh/speech-to-text-demo/HEAD/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ritazh/speech-to-text-demo/HEAD/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ritazh/speech-to-text-demo/HEAD/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ritazh/speech-to-text-demo/HEAD/public/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /cris.json: -------------------------------------------------------------------------------- 1 | move cell down 2 | move cell up 3 | move cell right 4 | move cell left 5 | add blue cell to one 6 | add red cell to one 7 | add yellow cell to two 8 | add blue cell to three 9 | get rid of one 10 | get rid of two 11 | remove one 12 | remove the blue box from one 13 | remove cell two 14 | i want you to put a blue box in fifteen 15 | i want you to put a blue box in five 16 | put a green box in seven 17 | make eighteen purple 18 | make one purple 19 | move cell to the right 20 | move cell to the left -------------------------------------------------------------------------------- /public/js/audiodisplay.js: -------------------------------------------------------------------------------- 1 | function drawBuffer( width, height, context, data ) { 2 | var step = Math.ceil( data.length / width ); 3 | var amp = height / 2; 4 | context.fillStyle = "silver"; 5 | context.clearRect(0,0,width,height); 6 | for(var i=0; i < width; i++){ 7 | var min = 1.0; 8 | var max = -1.0; 9 | for (j=0; j max) 14 | max = datum; 15 | } 16 | context.fillRect(i,(1+min)*amp,1,Math.max(1,(max-min)*amp)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "speech-to-text-demo", 3 | "version": "1.0.0", 4 | "description": "An application that does speech recognition and returns weather data for the city you asked about", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "standard" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/ritazh/speech-to-text-demo.git" 12 | }, 13 | "author": "ritazh", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/ritazh/speech-to-text-demo/issues" 17 | }, 18 | "homepage": "https://github.com/ritazh/speech-to-text-demo#readme", 19 | "dependencies": { 20 | "body-parser": "^1.15.2", 21 | "busboy": "^0.2.13", 22 | "connect": "^3.4.1", 23 | "express": "^4.14.0", 24 | "os": "^0.1.1", 25 | "path": "^0.12.7", 26 | "request": "^2.73.0", 27 | "util": "^0.10.3" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /public/img/mic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Microsoft Corporation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /public/css/custom.css: -------------------------------------------------------------------------------- 1 | @keyframes btnRecordColorCycle { 2 | 0% { 3 | background-color: #FB274A; 4 | } 5 | 50% { 6 | background-color:#C71414; 7 | } 8 | 100% { 9 | background-color: #FB274A; 10 | } 11 | } 12 | 13 | .btn_mic 14 | { 15 | padding: 0; 16 | width: 100px; 17 | height: 100px; 18 | overflow: hidden; 19 | border-radius: 50px; 20 | background: #FB274A url('../images/mic.png') no-repeat center center; 21 | background-size: 40%; 22 | border-style: none; 23 | cursor: pointer; 24 | text-transform: none; 25 | border: 5px solid #ffffff; 26 | } 27 | 28 | .btn_mic:focus, .btn_mic:hover { 29 | background: #FB274A url('../images/mic.png') no-repeat center center; 30 | background-size: 40%; 31 | border: 5px solid #ffffff; 32 | } 33 | 34 | .btnup { 35 | box-shadow: 1px 4px 0 0 #A69992; 36 | animation-name: none; 37 | } 38 | 39 | .btndown { 40 | box-shadow: none; 41 | margin-left: 1px; 42 | margin-right: -1px; 43 | margin-bottom: -4px; 44 | margin-top: 4px; 45 | animation-name: btnRecordColorCycle; 46 | animation-duration: .7s; 47 | animation-iteration-count: infinite; 48 | } 49 | 50 | .glyphicon-refresh-animate { 51 | animation: spin .7s infinite linear; 52 | } 53 | 54 | @keyframes spin { 55 | from { -webkit-transform: rotate(0deg);} 56 | to { -webkit-transform: rotate(360deg);} 57 | } 58 | -------------------------------------------------------------------------------- /public/js/FileSaver.min.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 2 | var saveAs=saveAs||function(e){"use strict";if(typeof e==="undefined"||typeof navigator!=="undefined"&&/MSIE [1-9]\./.test(navigator.userAgent)){return}var t=e.document,n=function(){return e.URL||e.webkitURL||e},r=t.createElementNS("http://www.w3.org/1999/xhtml","a"),o="download"in r,i=function(e){var t=new MouseEvent("click");e.dispatchEvent(t)},a=/constructor/i.test(e.HTMLElement),f=/CriOS\/[\d]+/.test(navigator.userAgent),u=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},d="application/octet-stream",s=1e3*40,c=function(e){var t=function(){if(typeof e==="string"){n().revokeObjectURL(e)}else{e.remove()}};setTimeout(t,s)},l=function(e,t,n){t=[].concat(t);var r=t.length;while(r--){var o=e["on"+t[r]];if(typeof o==="function"){try{o.call(e,n||e)}catch(i){u(i)}}}},p=function(e){if(/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)){return new Blob([String.fromCharCode(65279),e],{type:e.type})}return e},v=function(t,u,s){if(!s){t=p(t)}var v=this,w=t.type,m=w===d,y,h=function(){l(v,"writestart progress write writeend".split(" "))},S=function(){if((f||m&&a)&&e.FileReader){var r=new FileReader;r.onloadend=function(){var t=f?r.result:r.result.replace(/^data:[^;]*;/,"data:attachment/file;");var n=e.open(t,"_blank");if(!n)e.location.href=t;t=undefined;v.readyState=v.DONE;h()};r.readAsDataURL(t);v.readyState=v.INIT;return}if(!y){y=n().createObjectURL(t)}if(m){e.location.href=y}else{var o=e.open(y,"_blank");if(!o){e.location.href=y}}v.readyState=v.DONE;h();c(y)};v.readyState=v.INIT;if(o){y=n().createObjectURL(t);setTimeout(function(){r.href=y;r.download=u;i(r);h();c(y);v.readyState=v.DONE});return}S()},w=v.prototype,m=function(e,t,n){return new v(e,t||e.name||"download",n)};if(typeof navigator!=="undefined"&&navigator.msSaveOrOpenBlob){return function(e,t,n){t=t||e.name||"download";if(!n){e=p(e)}return navigator.msSaveOrOpenBlob(e,t)}}w.abort=function(){};w.readyState=w.INIT=0;w.WRITING=1;w.DONE=2;w.error=w.onwritestart=w.onprogress=w.onwrite=w.onabort=w.onerror=w.onwriteend=null;return m}(typeof self!=="undefined"&&self||typeof window!=="undefined"&&window||this.content);if(typeof module!=="undefined"&&module.exports){module.exports.saveAs=saveAs}else if(typeof define!=="undefined"&&define!==null&&define.amd!==null){define([],function(){return saveAs})} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Speech to Text Demo with Bing Speech and LUIS 2 | 3 | _An application that updates its own user interface based on user's voice commands using speech recognition and machine learning_. 4 | 5 | ![STT Demo](demo.gif) 6 | 7 | Ever wonder what it's like to have Jarvis from Iron Man? Well now with the advances in machine learning and speech recognition, what if we build web applications with something like Jarvis? This is a simple proof of concept that demonstrates how users can now build web UIs with simple voice commands. 8 | 9 | This application is built using RecorderJS to record audio, [Bing Speech API](https://www.microsoft.com/cognitive-services/en-us/speech-api/documentation/overview) to recognize user's voice commands while it also uses [LUIS](https://www.microsoft.com/cognitive-services/en-us/luis-api/documentation/home) (Language Understanding Intelligent Services) to understand the user's intentions, which are interpreted and used for updating cells in a web user interface. 10 | 11 | ## Installation 12 | 13 | Clone this repo and then install dependencies: 14 | 15 | git clone https://github.com/ritazh/sttdemo.git 16 | cd sttdemo 17 | npm i 18 | 19 | 20 | Run the application then hit your browser with `http://localhost:3000`: 21 | 22 | node app.js 23 | 24 | 25 | Setup your own keys for Bing Speech and LUIS: 26 | 27 | * Sign up for Microsoft Cognitive Service [here](https://www.microsoft.com/cognitive-services/en-us/sign-up) and get your keys for Speech API 28 | * Follow the steps [here](https://www.microsoft.com/cognitive-services/en-us/luis-api/documentation/getstartedwithluis-basics) to create your own LUIS app, then get your LUIS application id and your LUIS Subscription-key. 29 | * To get the same trained LUIS app for moving cells, you can also import an existing app by using [cellmover.json](cellmover.json). 30 | * To get the same context trained by CRIS, you can upload [cris.json](cris.json) to create your own language model. 31 | 32 | ## Acknowledgement 33 | Many thanks to [@rickbarraza](@rickbarraza) for designing and developing the user interface for this application. 34 | 35 | Many thanks to [@cwilso](@cwilso) for developing and maintaining [AudioRecorder](https://github.com/cwilso/AudioRecorder) for the awesome UI components in this app. 36 | 37 | ## License 38 | Licensed using the MIT License (MIT); Copyright (c) Microsoft Corporation. For more information, please see [LICENSE](LICENSE). 39 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Speech to Text Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 | 26 |
27 | 28 |
29 | 30 | 41 |
42 | Hold the button to record. Release to end.
43 | [e.g. Move cell down, Add blue button to one] 44 |
45 |
46 | 49 | 50 |
51 |
52 | 53 | 54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | 64 | -------------------------------------------------------------------------------- /public/js/recorderjs/recorder.js: -------------------------------------------------------------------------------- 1 | /*License (MIT) 2 | 3 | Copyright © 2013 Matt Diamond 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation 7 | the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 8 | to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of 11 | the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 14 | THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 16 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 17 | DEALINGS IN THE SOFTWARE. 18 | */ 19 | 20 | (function(window){ 21 | 22 | var WORKER_PATH = 'js/recorderjs/recorderWorker.js'; 23 | 24 | var Recorder = function(source, cfg){ 25 | var config = cfg || {}; 26 | var bufferLen = config.bufferLen || 4096; 27 | this.context = source.context; 28 | if(!this.context.createScriptProcessor){ 29 | this.node = this.context.createJavaScriptNode(bufferLen, 2, 2); 30 | } else { 31 | this.node = this.context.createScriptProcessor(bufferLen, 2, 2); 32 | } 33 | 34 | var worker = new Worker(config.workerPath || WORKER_PATH); 35 | worker.postMessage({ 36 | command: 'init', 37 | config: { 38 | sampleRate: this.context.sampleRate 39 | } 40 | }); 41 | var recording = false, 42 | currCallback; 43 | 44 | this.node.onaudioprocess = function(e){ 45 | if (!recording) return; 46 | worker.postMessage({ 47 | command: 'record', 48 | buffer: [ 49 | e.inputBuffer.getChannelData(0), 50 | e.inputBuffer.getChannelData(1) 51 | ] 52 | }); 53 | } 54 | 55 | this.configure = function(cfg){ 56 | for (var prop in cfg){ 57 | if (cfg.hasOwnProperty(prop)){ 58 | config[prop] = cfg[prop]; 59 | } 60 | } 61 | } 62 | 63 | this.record = function(){ 64 | recording = true; 65 | } 66 | 67 | this.stop = function(){ 68 | recording = false; 69 | } 70 | 71 | this.clear = function(){ 72 | worker.postMessage({ command: 'clear' }); 73 | } 74 | 75 | this.getBuffers = function(cb) { 76 | currCallback = cb || config.callback; 77 | worker.postMessage({ command: 'getBuffers' }) 78 | } 79 | 80 | this.exportWAV = function(cb, type){ 81 | currCallback = cb || config.callback; 82 | type = type || config.type || 'audio/wav'; 83 | if (!currCallback) throw new Error('Callback not set'); 84 | worker.postMessage({ 85 | command: 'exportWAV', 86 | type: type 87 | }); 88 | } 89 | 90 | this.exportMonoWAV = function(cb, type){ 91 | currCallback = cb || config.callback; 92 | type = type || config.type || 'audio/wav'; 93 | if (!currCallback) throw new Error('Callback not set'); 94 | worker.postMessage({ 95 | command: 'exportMonoWAV', 96 | type: type 97 | }); 98 | } 99 | 100 | worker.onmessage = function(e){ 101 | var blob = e.data; 102 | currCallback(blob); 103 | } 104 | 105 | source.connect(this.node); 106 | this.node.connect(this.context.destination); // if the script node is not connected to an output the "onaudioprocess" event is not triggered in chrome. 107 | }; 108 | 109 | Recorder.setupDownload = function(blob, filename){ 110 | var url = (window.URL || window.webkitURL).createObjectURL(blob); 111 | var link = document.getElementById("save"); 112 | link.href = url; 113 | link.download = filename || 'output.wav'; 114 | //saveAs(blob, filename); 115 | } 116 | 117 | window.Recorder = Recorder; 118 | 119 | })(window); 120 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'), 4 | util = require('util'), 5 | request = require('request'), 6 | inspect = require('util').inspect, 7 | os = require('os'), 8 | Busboy = require('busboy'), 9 | express = require('express'), 10 | app = express(), 11 | path = require('path'), 12 | connect = require('connect'), 13 | bodyParser = require('body-parser'); 14 | 15 | var clientId = 'test-app'; 16 | var clientSecret = 'f735c587f01b4c5a82048418caf5c97f'; 17 | //'ceb21dbbce474431ad3fc95b12a6cc90'; // API key from Bing Speech service 18 | var savedFile = null; 19 | 20 | function getAccessToken(clientId, clientSecret, callback) { 21 | request.post({ 22 | url: 'https://oxford-speech.cloudapp.net/token/issueToken', 23 | form: { 24 | 'grant_type': 'client_credentials', 25 | 'client_id': encodeURIComponent(clientId), 26 | 'client_secret': encodeURIComponent(clientSecret), 27 | 'scope': 'https://speech.platform.bing.com' 28 | } 29 | }, function(err, resp, body) { 30 | if(err) return callback(err); 31 | try { 32 | var accessToken = JSON.parse(body).access_token; 33 | if(accessToken) { 34 | callback(null, accessToken); 35 | } else { 36 | callback(body); 37 | } 38 | } catch(e) { 39 | callback(e); 40 | } 41 | }); 42 | } 43 | 44 | function speechToText(filename, accessToken, callback) { 45 | fs.readFile(filename, function(err, waveData) { 46 | if(err) return callback(err); 47 | request.post({ 48 | url: 'https://653b18d0e1f24f1397a20586e2654959.api.cris.ai/cris/speech/query?cid=a1787a15-7150-4ee4-b277-f51ddb98be87', //'https://speech.platform.bing.com/recognize', 49 | qs: { 50 | 'scenarios': 'ulm', 51 | 'appid': '31b3d95b-af74-4550-9619-de76fe33f0f0', 52 | //'D4D52672-91D7-4C74-8AD8-42B1D98141A5',// Using Bing Speech service 53 | 'locale': 'en-US', 54 | 'device.os': 'wp7', 55 | 'version': '3.0', 56 | 'format': 'json', 57 | 'requestid': '1d4b6030-9099-11e0-91e4-0800200c9a66', 58 | 'instanceid': '1d4b6030-9099-11e0-91e4-0800200c9a66' 59 | }, 60 | body: waveData, 61 | headers: { 62 | 'Authorization': 'Bearer ' + accessToken, 63 | 'Content-Type': 'audio/wav; samplerate=16000', 64 | 'Content-Length' : waveData.length 65 | } 66 | }, function(err, resp, body) { 67 | if(err) return callback(err); 68 | try { 69 | console.log(body); 70 | callback(null, JSON.parse(body)); 71 | } catch(e) { 72 | callback(e); 73 | } 74 | }); 75 | }); 76 | } 77 | 78 | function LUIS(query, callback) { 79 | request.get({ 80 | url: 'https://api.projectoxford.ai/luis/v1/application', 81 | qs: { 82 | 'id': '04eb9f25-174a-496f-b457-787ee0b5e6e7', // API key from Cognitive Service LUIS service 83 | 'subscription-key': 'c831821ce79e4f13844a75f6099de616', // LUIS Subscription ID 84 | 'q': query 85 | } 86 | }, function(err, resp, body) { 87 | if(err) return callback(err); 88 | try { 89 | callback(null, JSON.parse(body)); 90 | } catch(e) { 91 | callback(e); 92 | } 93 | }); 94 | } 95 | 96 | app.use(express.static(__dirname + '/public')); 97 | app.use(bodyParser.urlencoded({ extended: true })); 98 | 99 | app.get('/', function(req, res) { 100 | res.sendFile('index.html'); 101 | }); 102 | 103 | app.post('/recognize', function(req, res) { 104 | var busboy = new Busboy({ headers: req.headers }); 105 | busboy.on('file', function(fieldname, file, filename, encoding, mimetype) { 106 | savedFile = path.join(os.tmpDir(), 'test.wav'); 107 | file.pipe(fs.createWriteStream(savedFile)); 108 | console.log('File is saved to: ' + savedFile); 109 | }); 110 | 111 | busboy.on('finish', function() { 112 | var result = ''; 113 | 114 | getAccessToken(clientId, clientSecret, function(err, accessToken) { 115 | if(err) return console.log(err); 116 | console.log('Got access token: ' + accessToken) 117 | speechToText(savedFile, accessToken, function(err, speechres) { 118 | if(err) return console.log(err); 119 | result = 'Did you mean "' + speechres.results[0].lexical + '"? Confidence score: ' + speechres.results[0].confidence + '.'; 120 | console.log(result); 121 | res.status(200).send(speechres.results[0].lexical); 122 | 123 | }); 124 | }) 125 | 126 | }); 127 | 128 | req.pipe(busboy); 129 | }); 130 | 131 | app.get('/luis', function(req, res) { 132 | console.log(req.query.q); 133 | LUIS(req.query.q, function(err, luisres) { 134 | if(err) return console.log(err); 135 | console.log(luisres); 136 | res.status(200).send(luisres); 137 | }); 138 | }); 139 | 140 | app.listen(process.env.PORT || 3000); 141 | console.log("Running at Port 3000"); 142 | 143 | -------------------------------------------------------------------------------- /public/js/recorderjs/recorderWorker.js: -------------------------------------------------------------------------------- 1 | /*License (MIT) 2 | 3 | Copyright © 2013 Matt Diamond 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation 7 | the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 8 | to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of 11 | the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 14 | THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 16 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 17 | DEALINGS IN THE SOFTWARE. 18 | */ 19 | 20 | var recLength = 0, 21 | recBuffersL = [], 22 | recBuffersR = [], 23 | sampleRate; 24 | 25 | this.onmessage = function(e){ 26 | switch(e.data.command){ 27 | case 'init': 28 | init(e.data.config); 29 | break; 30 | case 'record': 31 | record(e.data.buffer); 32 | break; 33 | case 'exportWAV': 34 | exportWAV(e.data.type); 35 | break; 36 | case 'exportMonoWAV': 37 | exportMonoWAV(e.data.type); 38 | break; 39 | case 'getBuffers': 40 | getBuffers(); 41 | break; 42 | case 'clear': 43 | clear(); 44 | break; 45 | } 46 | }; 47 | 48 | function init(config){ 49 | sampleRate = config.sampleRate; 50 | } 51 | 52 | function record(inputBuffer){ 53 | recBuffersL.push(inputBuffer[0]); 54 | recBuffersR.push(inputBuffer[1]); 55 | recLength += inputBuffer[0].length; 56 | } 57 | 58 | function exportWAV(type){ 59 | var bufferL = mergeBuffers(recBuffersL, recLength); 60 | var bufferR = mergeBuffers(recBuffersR, recLength); 61 | var interleaved = interleave(bufferL, bufferR); 62 | var dataview = encodeWAV(interleaved); 63 | var audioBlob = new Blob([dataview], { type: type }); 64 | 65 | this.postMessage(audioBlob); 66 | } 67 | 68 | function exportMonoWAV(type){ 69 | var bufferL = mergeBuffers(recBuffersL, recLength); 70 | var dataview = encodeWAV(bufferL, true); 71 | var audioBlob = new Blob([dataview], { type: type }); 72 | 73 | this.postMessage(audioBlob); 74 | } 75 | 76 | function getBuffers() { 77 | var buffers = []; 78 | buffers.push( mergeBuffers(recBuffersL, recLength) ); 79 | buffers.push( mergeBuffers(recBuffersR, recLength) ); 80 | this.postMessage(buffers); 81 | } 82 | 83 | function clear(){ 84 | recLength = 0; 85 | recBuffersL = []; 86 | recBuffersR = []; 87 | } 88 | 89 | function mergeBuffers(recBuffers, recLength){ 90 | var result = new Float32Array(recLength); 91 | var offset = 0; 92 | for (var i = 0; i < recBuffers.length; i++){ 93 | result.set(recBuffers[i], offset); 94 | offset += recBuffers[i].length; 95 | } 96 | return result; 97 | } 98 | 99 | function interleave(inputL, inputR){ 100 | var length = inputL.length + inputR.length; 101 | var result = new Float32Array(length); 102 | 103 | var index = 0, 104 | inputIndex = 0; 105 | 106 | while (index < length){ 107 | result[index++] = inputL[inputIndex]; 108 | result[index++] = inputR[inputIndex]; 109 | inputIndex++; 110 | } 111 | return result; 112 | } 113 | 114 | function floatTo16BitPCM(output, offset, input){ 115 | for (var i = 0; i < input.length; i++, offset+=2){ 116 | var s = Math.max(-1, Math.min(1, input[i])); 117 | output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true); 118 | } 119 | } 120 | 121 | function writeString(view, offset, string){ 122 | for (var i = 0; i < string.length; i++){ 123 | view.setUint8(offset + i, string.charCodeAt(i)); 124 | } 125 | } 126 | 127 | function encodeWAV(samples, mono){ 128 | var buffer = new ArrayBuffer(44 + samples.length * 2); 129 | var view = new DataView(buffer); 130 | 131 | /* RIFF identifier */ 132 | writeString(view, 0, 'RIFF'); 133 | /* file length */ 134 | view.setUint32(4, 32 + samples.length * 2, true); 135 | /* RIFF type */ 136 | writeString(view, 8, 'WAVE'); 137 | /* format chunk identifier */ 138 | writeString(view, 12, 'fmt '); 139 | /* format chunk length */ 140 | view.setUint32(16, 16, true); 141 | /* sample format (raw) */ 142 | view.setUint16(20, 1, true); 143 | /* channel count */ 144 | view.setUint16(22, mono?1:2, true); 145 | /* sample rate */ 146 | view.setUint32(24, sampleRate, true); 147 | /* byte rate (sample rate * block align) */ 148 | view.setUint32(28, sampleRate * 4, true); 149 | /* block align (channel count * bytes per sample) */ 150 | view.setUint16(32, 4, true); 151 | /* bits per sample */ 152 | view.setUint16(34, 16, true); 153 | /* data chunk identifier */ 154 | writeString(view, 36, 'data'); 155 | /* data chunk length */ 156 | view.setUint32(40, samples.length * 2, true); 157 | 158 | floatTo16BitPCM(view, 44, samples); 159 | 160 | return view; 161 | } 162 | -------------------------------------------------------------------------------- /public/js/p5/cellgrid.js: -------------------------------------------------------------------------------- 1 | var canvas; 2 | 3 | var cellSize = 75; 4 | var cellGutter = 5; 5 | var lblIndex = 1; 6 | var cellData = [] 7 | var totalCells = 25; 8 | var lastIndex = 12; 9 | 10 | var defaultGrey = "#aeaeae"; 11 | 12 | var integerValues = {"one":1,"two":2,"three":3,"four":4,"five":5,"six":6,"seven":7,"eight":8,"nine":9,"ten":10, 13 | "eleven":11,"twelve":12,"thirteen":13,"fourteen":14,"fifteen":15,"sixteen":16,"seventeen":17,"eighteen":18,"nineteen":19,"twenty":20, 14 | "twenty one":21,"twenty two":22,"twenty three":23,"twenty four":24, "twenty five":25}; 15 | 16 | var colours = {"aliceblue":"#f0f8ff","antiquewhite":"#faebd7","aqua":"#00ffff","aquamarine":"#7fffd4","azure":"#f0ffff", 17 | "beige":"#f5f5dc","bisque":"#ffe4c4","black":"#000000","blanchedalmond":"#ffebcd","blue":"#0000ff","blueviolet":"#8a2be2","brown":"#a52a2a","burlywood":"#deb887", 18 | "cadetblue":"#5f9ea0","chartreuse":"#7fff00","chocolate":"#d2691e","coral":"#ff7f50","cornflowerblue":"#6495ed","cornsilk":"#fff8dc","crimson":"#dc143c","cyan":"#00ffff", 19 | "darkblue":"#00008b","darkcyan":"#008b8b","darkgoldenrod":"#b8860b","darkgray":"#a9a9a9","darkgreen":"#006400","darkkhaki":"#bdb76b","darkmagenta":"#8b008b","darkolivegreen":"#556b2f", 20 | "darkorange":"#ff8c00","darkorchid":"#9932cc","darkred":"#8b0000","darksalmon":"#e9967a","darkseagreen":"#8fbc8f","darkslateblue":"#483d8b","darkslategray":"#2f4f4f","darkturquoise":"#00ced1", 21 | "darkviolet":"#9400d3","deeppink":"#ff1493","deepskyblue":"#00bfff","dimgray":"#696969","dodgerblue":"#1e90ff", 22 | "firebrick":"#b22222","floralwhite":"#fffaf0","forestgreen":"#228b22","fuchsia":"#ff00ff", 23 | "gainsboro":"#dcdcdc","ghostwhite":"#f8f8ff","gold":"#ffd700","goldenrod":"#daa520","grey":"#80808","gray":"#808080","green":"#008000","greenyellow":"#adff2f", 24 | "honeydew":"#f0fff0","hotpink":"#ff69b4", 25 | "indianred ":"#cd5c5c","indigo":"#4b0082","ivory":"#fffff0","khaki":"#f0e68c", 26 | "lavender":"#e6e6fa","lavenderblush":"#fff0f5","lawngreen":"#7cfc00","lemonchiffon":"#fffacd","lightblue":"#add8e6","lightcoral":"#f08080","lightcyan":"#e0ffff","lightgoldenrodyellow":"#fafad2", 27 | "lightgrey":"#d3d3d3","lightgreen":"#90ee90","lightpink":"#ffb6c1","lightsalmon":"#ffa07a","lightseagreen":"#20b2aa","lightskyblue":"#87cefa","lightslategray":"#778899","lightsteelblue":"#b0c4de", 28 | "lightyellow":"#ffffe0","lime":"#00ff00","limegreen":"#32cd32","linen":"#faf0e6", 29 | "magenta":"#ff00ff","maroon":"#800000","mediumaquamarine":"#66cdaa","mediumblue":"#0000cd","mediumorchid":"#ba55d3","mediumpurple":"#9370d8","mediumseagreen":"#3cb371","mediumslateblue":"#7b68ee", 30 | "mediumspringgreen":"#00fa9a","mediumturquoise":"#48d1cc","mediumvioletred":"#c71585","midnightblue":"#191970","mintcream":"#f5fffa","mistyrose":"#ffe4e1","moccasin":"#ffe4b5", 31 | "navajowhite":"#ffdead","navy":"#000080", 32 | "oldlace":"#fdf5e6","olive":"#808000","olivedrab":"#6b8e23","orange":"#ffa500","orangered":"#ff4500","orchid":"#da70d6", 33 | "palegoldenrod":"#eee8aa","palegreen":"#98fb98","paleturquoise":"#afeeee","palevioletred":"#d87093","papayawhip":"#ffefd5","peachpuff":"#ffdab9","peru":"#cd853f","pink":"#ffc0cb","plum":"#dda0dd","powderblue":"#b0e0e6","purple":"#800080", 34 | "red":"#ff0000","rosybrown":"#bc8f8f","royalblue":"#4169e1", 35 | "saddlebrown":"#8b4513","salmon":"#fa8072","sandybrown":"#f4a460","seagreen":"#2e8b57","seashell":"#fff5ee","sienna":"#a0522d","silver":"#c0c0c0","skyblue":"#87ceeb","slateblue":"#6a5acd","slategray":"#708090","snow":"#fffafa","springgreen":"#00ff7f","steelblue":"#4682b4", 36 | "tan":"#d2b48c","teal":"#008080","thistle":"#d8bfd8","tomato":"#ff6347","turquoise":"#40e0d0", 37 | "violet":"#ee82ee", 38 | "wheat":"#f5deb3","white":"#ffffff","whitesmoke":"#f5f5f5", 39 | "yellow":"#ffff00","yellowgreen":"#9acd32"}; 40 | 41 | 42 | var postSetup = false; 43 | 44 | function setup() { 45 | 46 | canvas = createCanvas(400, 400); 47 | canvas.parent('primer'); 48 | canvas.position(40, 0); 49 | 50 | strokeWeight(1); 51 | stroke(255); 52 | 53 | textAlign(CENTER); 54 | 55 | postSetup = true; 56 | 57 | renderGrid(); 58 | } 59 | 60 | function renderGrid() { 61 | 62 | if (!postSetup) return; 63 | 64 | clear(); 65 | 66 | for ( var i = 0; i < totalCells; i++ ) { 67 | var _x = i%5; 68 | var _y = Math.floor(i/5); 69 | var xPos = _x*(cellSize+cellGutter); 70 | var yPos = _y*(cellSize+cellGutter); 71 | 72 | var c = color(cellData[i]); 73 | fill(c); 74 | rect(xPos, yPos, cellSize, cellSize); 75 | 76 | textSize(18); 77 | fill(255); 78 | text(i+1, xPos + .5*cellSize, yPos + .6*cellSize ); 79 | } 80 | 81 | } 82 | 83 | function getPositionAsIndex(position) { 84 | var _pos = NaN; 85 | 86 | if (typeof integerValues[position] != 'undefined') 87 | _pos = integerValues[position]; 88 | 89 | if ( isNaN(_pos) ) { 90 | console.log("couldn't recognize " + position + " as a number..."); 91 | return -1; 92 | } 93 | 94 | _pos = Math.floor(_pos); 95 | if ( _pos >= 210 && _pos < 225 ) { 96 | _pos -= 200; 97 | } 98 | 99 | _pos -= 1; 100 | if ( _pos < 0 || _pos > 24 ) { 101 | console.log("Please speak a position between 1 and 25."); 102 | return -2; 103 | } 104 | 105 | return _pos; 106 | }; 107 | 108 | function getColorHex(color) { 109 | var _colname = color.replace(/\s+/g, ''); 110 | 111 | if (typeof colours[_colname.toLowerCase()] != 'undefined') 112 | return colours[_colname.toLowerCase()]; 113 | 114 | return defaultGrey; 115 | }; 116 | 117 | function moveLastInDirection(direction) { 118 | var newPos = lastIndex; 119 | var error = false; 120 | 121 | if ( direction == "up") { 122 | newPos -= 5; 123 | if ( newPos < 0 ) error = true; 124 | } 125 | if ( direction == "down") { 126 | newPos += 5; 127 | if ( newPos > 24 ) return; 128 | } 129 | if ( direction == "left" ) { 130 | if ( (lastIndex % 5) == 0) error = true; 131 | newPos--; 132 | } 133 | if ( direction == "right" ) { 134 | if ( (lastIndex+1)%5 == 0 ) error = true; 135 | newPos++; 136 | } 137 | if ( newPos < 0 || newPos > 24 ) error = true; 138 | 139 | if ( error == true ) { 140 | console.log("couldn't move the cell in that direction"); 141 | return; 142 | } 143 | var newCol = cellData[lastIndex]; 144 | cellData[lastIndex] = defaultGrey; 145 | cellData[newPos] = newCol; 146 | lastIndex = newPos; 147 | updateGrid(); 148 | }; 149 | 150 | function moveLastToCell(newPosition) 151 | { 152 | var pos = getPositionAsIndex(newPosition); 153 | if ( pos < 0 ) return; 154 | var moveColor = cellData[lastIndex]; 155 | cellData[lastIndex] = defaultGrey; 156 | cellData[pos] = moveColor; 157 | lastIndex = pos; 158 | updateGrid(); 159 | }; 160 | 161 | function addCell(position, color) { 162 | var pos = getPositionAsIndex(position); 163 | if ( pos < 0 ) return; 164 | var hexcol = getColorHex(color); 165 | cellData[pos] = hexcol; 166 | lastIndex = pos; 167 | updateGrid(); 168 | }; 169 | 170 | function delCell(position) { 171 | var pos = getPositionAsIndex(position); 172 | if ( pos < 0 ) return; 173 | cellData[pos] = defaultGrey; 174 | lastIndex = pos; 175 | updateGrid(); 176 | }; 177 | 178 | function setupGridData() { 179 | cellData = []; 180 | for ( var i = 0; i < totalCells; i++ ) { 181 | cellData[i] = defaultGrey; 182 | } 183 | cellData[12] = "#7fff00"; 184 | lastIndex = 12; 185 | updateGrid(); 186 | }; 187 | 188 | function updateGrid() { 189 | renderGrid(); 190 | }; 191 | -------------------------------------------------------------------------------- /public/js/main.js: -------------------------------------------------------------------------------- 1 | (function (window) { 2 | 3 | var isRecording = false; 4 | var btnRecord; 5 | 6 | // AUDIO FUNCTIONS 7 | window.AudioContext = window.AudioContext || window.webkitAudioContext; 8 | 9 | var audioContext = new window.AudioContext(); 10 | 11 | var audioInput = null, 12 | realAudioInput = null, 13 | inputPoint = null, 14 | audioRecorder = null; 15 | var analyserContext = null; 16 | 17 | function submit(blob){ 18 | console.log(blob); 19 | 20 | var fd = new FormData(); 21 | fd.append('fname', 'test.wav'); 22 | fd.append('data', blob); 23 | 24 | $.ajax({ 25 | type: 'POST', 26 | url: '/recognize', 27 | data: fd, 28 | processData: false, 29 | contentType: false, 30 | success: function(result){ 31 | console.log('output updating to: ' + result); 32 | $('#spinPhrase').css('visibility', 'hidden'); 33 | showPhrase(result); 34 | } 35 | }); 36 | } 37 | 38 | function gotBuffers(buffers) { 39 | audioRecorder.exportWAV(doneEncoding); 40 | } 41 | 42 | function doneEncoding(blob) { 43 | submit(blob); 44 | } 45 | 46 | function intentReceived(jsonresponse) 47 | { 48 | var resp = JSON.parse(jsonresponse); 49 | 50 | var buildString = "You want me to : "; 51 | 52 | var entities = resp["entities"]; 53 | 54 | // PROCESS THE JSON FOR INTENT AND ENTITIES 55 | if ( resp["intents"][0]["intent"] == "AddCell") { 56 | // WE WANT TO ADD A CELL, SO LOOK FOR POSITION AND COLOR 57 | 58 | buildString += " [ADD A CELL ]"; 59 | 60 | var addColor = ""; 61 | var addPosition = ""; 62 | 63 | for ( var i = 0; i < entities.length; i++ ) { 64 | if ( entities[i]["type"] == "CellColor") { 65 | addColor = entities[i]["entity"]; 66 | buildString += " [ Color: " + entities[i]["entity"] + "] "; 67 | } 68 | if ( entities[i]["type"] == "builtin.number") { 69 | addPosition = entities[i]["entity"]; 70 | buildString += " [ Position: " + entities[i]["entity"] + "] "; 71 | } 72 | } 73 | 74 | if ( addColor != "" && addPosition != "") { 75 | addCell(addPosition, addColor); 76 | } 77 | 78 | } 79 | 80 | if (resp["intents"][0]["intent"] == "MoveCell" ) 81 | { 82 | buildString += " [MOVE A CELL ] "; 83 | 84 | var movePos = ""; 85 | var moveDirection = ""; 86 | 87 | for ( var i = 0; i < entities.length; i++ ) { 88 | if ( entities[i]["type"] == "builtin.number" || (entities[i]["type"] == "position") ) { 89 | buildString += " [ Position: " + entities[i]["entity"] + "] "; 90 | movePos = entities[i]["entity"]; 91 | } 92 | if ( entities[i]["type"] == "direction") { 93 | buildString += " [ Direction: " + entities[i]["entity"] + "] "; 94 | moveDirection = entities[i]["entity"]; 95 | } 96 | } 97 | 98 | if ( moveDirection != "" ) { 99 | console.log('before moveLastInDirection' + moveDirection); 100 | moveLastInDirection(moveDirection); 101 | } 102 | if ( movePos != "" ) { 103 | moveLastToCell(movePos); 104 | } 105 | } 106 | 107 | if (resp["intents"][0]["intent"] == "delete" ) 108 | { 109 | buildString += " [DELETE A CELL ] "; 110 | var deletePosition = ""; 111 | 112 | for ( var i = 0; i < entities.length; i++ ) { 113 | if ( entities[i]["type"] == "builtin.number") { 114 | deletePosition = entities[i]["entity"]; 115 | buildString += " [ Position: " + entities[i]["entity"] + "] "; 116 | } 117 | } 118 | 119 | if ( deletePosition != "") { 120 | delCell(deletePosition); 121 | } 122 | } 123 | 124 | 125 | $("#txtIntent").val(buildString); 126 | 127 | }; 128 | 129 | function sendPhraseToLUIS(phrase2intent) 130 | { 131 | $('#spinIntent').css('visibility', 'visible'); 132 | $.ajax({ 133 | url: '/luis?q=' + phrase2intent, 134 | type: "GET", 135 | data: null, 136 | }) 137 | .done(function(data) { 138 | $('#spinIntent').css('visibility', 'hidden'); 139 | $('#spinPhrase').css('visibility', 'hidden'); 140 | var stringy = JSON.stringify(data, null, 4); 141 | intentReceived(stringy); 142 | }) 143 | .fail(function() { 144 | $('#spinIntent').css('visibility', 'hidden'); 145 | $('#spinPhrase').css('visibility', 'hidden'); 146 | $("#txtIntent").val("I'm sorry, I encountered an error while deducing your intent :("); 147 | }); 148 | } 149 | 150 | function showPhrase(output) { 151 | if ( output == "" || output == undefined || output == "undefined" ) { 152 | $("#txtPhrase").val("I couldn't understand. Please try again..."); 153 | $('#spinIntent').css('visibility', 'hidden'); 154 | $('#spinPhrase').css('visibility', 'hidden'); 155 | } else { 156 | $("#txtPhrase").val(output); 157 | sendPhraseToLUIS(output); 158 | } 159 | } 160 | 161 | window.btnRecordDown = function (e) { 162 | 163 | $('#btnRecord').removeClass('btnup').addClass('btndown'); 164 | $('#spinIntent').css('visibility', 'hidden'); 165 | $('#spinPhrase').css('visibility', 'hidden'); 166 | $("#txtPhrase").val(""); 167 | $("#txtIntent").val(""); 168 | 169 | // START CLIENT SIDE AUDIO RECORDING PROCESS 170 | if (!audioRecorder) return; 171 | audioRecorder.clear(); 172 | audioRecorder.record(); 173 | 174 | isRecording = true; 175 | }; 176 | 177 | window.btnRecordOut = function (e) { 178 | if (isRecording) 179 | btnRecordUp(e); 180 | } 181 | 182 | window.btnRecordUp = function (e) { 183 | isRecording = false; 184 | $('#btnRecord').removeClass('btndown').addClass('btnup'); 185 | 186 | $('#spinPhrase').css('visibility', 'visible'); 187 | audioRecorder.stop(); 188 | audioRecorder.getBuffers(gotBuffers); 189 | 190 | // EVENTUALY, WE WILL DO THIS AT THE END: 191 | // updateGrid(); 192 | }; 193 | 194 | function callbackReceivedAudioStream(stream) { 195 | 196 | inputPoint = audioContext.createGain(); 197 | 198 | realAudioInput = audioContext.createMediaStreamSource(stream); 199 | audioInput = realAudioInput; 200 | audioInput.connect(inputPoint); 201 | 202 | analyserNode = audioContext.createAnalyser(); 203 | analyserNode.fftSize = 2048; 204 | inputPoint.connect( analyserNode ); 205 | 206 | audioRecorder = new Recorder( inputPoint ); 207 | 208 | zeroGain = audioContext.createGain(); 209 | zeroGain.gain.value = 0.0; 210 | inputPoint.connect( zeroGain ); 211 | zeroGain.connect( audioContext.destination ); 212 | }; 213 | 214 | function initAudio() { 215 | 216 | if (!navigator.getUserMedia) 217 | navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia; 218 | if (!navigator.cancelAnimationFrame) 219 | navigator.cancelAnimationFrame = navigator.webkitCancelAnimationFrame || navigator.mozCancelAnimationFrame; 220 | if (!navigator.requestAnimationFrame) 221 | navigator.requestAnimationFrame = navigator.webkitRequestAnimationFrame || navigator.mozRequestAnimationFrame; 222 | 223 | navigator.getUserMedia( 224 | { 225 | "audio": { 226 | "mandatory": { 227 | "googEchoCancellation": "false", 228 | "googAutoGainControl": "false", 229 | "googNoiseSuppression": "false", 230 | "googHighpassFilter": "false" 231 | }, 232 | "optional": [] 233 | }, 234 | }, callbackReceivedAudioStream, function (e) { 235 | alert('Error getting audio'); 236 | console.log(e); 237 | }); 238 | }; 239 | 240 | 241 | function main() { 242 | setupGridData(); 243 | initAudio(); 244 | }; 245 | 246 | window.addEventListener('load', main); 247 | 248 | })(this); 249 | -------------------------------------------------------------------------------- /cellmover.json: -------------------------------------------------------------------------------- 1 | { 2 | "luis_schema_version": "1.3.0", 3 | "name": "cellmover", 4 | "desc": "LUIS sample demo to explore moving colored cells in a grid", 5 | "culture": "en-us", 6 | "intents": [ 7 | { 8 | "name": "delete" 9 | }, 10 | { 11 | "name": "AddCell" 12 | }, 13 | { 14 | "name": "MoveCell" 15 | }, 16 | { 17 | "name": "None" 18 | } 19 | ], 20 | "entities": [ 21 | { 22 | "name": "CellColor" 23 | }, 24 | { 25 | "name": "position" 26 | }, 27 | { 28 | "name": "direction" 29 | } 30 | ], 31 | "composites": [ 32 | { 33 | "name": "CellType", 34 | "children": [ 35 | "direction" 36 | ] 37 | } 38 | ], 39 | "bing_entities": [ 40 | "number" 41 | ], 42 | "actions": [ 43 | { 44 | "actionName": "delete", 45 | "intentName": "delete", 46 | "actionParameters": [] 47 | }, 48 | { 49 | "actionName": "AddCell", 50 | "intentName": "AddCell", 51 | "actionParameters": [] 52 | } 53 | ], 54 | "model_features": [], 55 | "regex_features": [], 56 | "utterances": [ 57 | { 58 | "text": "add a red cell", 59 | "intent": "AddCell", 60 | "entities": [ 61 | { 62 | "entity": "CellType", 63 | "startPos": 3, 64 | "endPos": 3 65 | }, 66 | { 67 | "entity": "CellColor", 68 | "startPos": 2, 69 | "endPos": 2 70 | } 71 | ] 72 | }, 73 | { 74 | "text": "move the cell down", 75 | "intent": "MoveCell", 76 | "entities": [ 77 | { 78 | "entity": "CellType", 79 | "startPos": 2, 80 | "endPos": 2 81 | }, 82 | { 83 | "entity": "direction", 84 | "startPos": 3, 85 | "endPos": 3 86 | } 87 | ] 88 | }, 89 | { 90 | "text": "move the cell up", 91 | "intent": "MoveCell", 92 | "entities": [ 93 | { 94 | "entity": "CellType", 95 | "startPos": 2, 96 | "endPos": 2 97 | }, 98 | { 99 | "entity": "direction", 100 | "startPos": 3, 101 | "endPos": 3 102 | } 103 | ] 104 | }, 105 | { 106 | "text": "move the red cell to one", 107 | "intent": "MoveCell", 108 | "entities": [ 109 | { 110 | "entity": "CellType", 111 | "startPos": 3, 112 | "endPos": 3 113 | }, 114 | { 115 | "entity": "position", 116 | "startPos": 5, 117 | "endPos": 5 118 | }, 119 | { 120 | "entity": "CellColor", 121 | "startPos": 2, 122 | "endPos": 2 123 | } 124 | ] 125 | }, 126 | { 127 | "text": "add a blue cell to three", 128 | "intent": "AddCell", 129 | "entities": [ 130 | { 131 | "entity": "CellType", 132 | "startPos": 3, 133 | "endPos": 3 134 | }, 135 | { 136 | "entity": "position", 137 | "startPos": 5, 138 | "endPos": 5 139 | }, 140 | { 141 | "entity": "CellColor", 142 | "startPos": 2, 143 | "endPos": 2 144 | } 145 | ] 146 | }, 147 | { 148 | "text": "move the blue cell down", 149 | "intent": "MoveCell", 150 | "entities": [ 151 | { 152 | "entity": "CellType", 153 | "startPos": 3, 154 | "endPos": 3 155 | }, 156 | { 157 | "entity": "direction", 158 | "startPos": 4, 159 | "endPos": 4 160 | }, 161 | { 162 | "entity": "CellColor", 163 | "startPos": 2, 164 | "endPos": 2 165 | } 166 | ] 167 | }, 168 | { 169 | "text": "move the red cell to four", 170 | "intent": "MoveCell", 171 | "entities": [ 172 | { 173 | "entity": "CellType", 174 | "startPos": 3, 175 | "endPos": 3 176 | }, 177 | { 178 | "entity": "position", 179 | "startPos": 5, 180 | "endPos": 5 181 | }, 182 | { 183 | "entity": "CellColor", 184 | "startPos": 2, 185 | "endPos": 2 186 | } 187 | ] 188 | }, 189 | { 190 | "text": "move the red cell left", 191 | "intent": "MoveCell", 192 | "entities": [ 193 | { 194 | "entity": "CellType", 195 | "startPos": 3, 196 | "endPos": 3 197 | }, 198 | { 199 | "entity": "direction", 200 | "startPos": 4, 201 | "endPos": 4 202 | }, 203 | { 204 | "entity": "CellColor", 205 | "startPos": 2, 206 | "endPos": 2 207 | } 208 | ] 209 | }, 210 | { 211 | "text": "move the blue cell up", 212 | "intent": "MoveCell", 213 | "entities": [ 214 | { 215 | "entity": "CellType", 216 | "startPos": 3, 217 | "endPos": 3 218 | }, 219 | { 220 | "entity": "direction", 221 | "startPos": 4, 222 | "endPos": 4 223 | }, 224 | { 225 | "entity": "CellColor", 226 | "startPos": 2, 227 | "endPos": 2 228 | } 229 | ] 230 | }, 231 | { 232 | "text": "move the green cell down", 233 | "intent": "MoveCell", 234 | "entities": [ 235 | { 236 | "entity": "CellType", 237 | "startPos": 3, 238 | "endPos": 3 239 | }, 240 | { 241 | "entity": "direction", 242 | "startPos": 4, 243 | "endPos": 4 244 | }, 245 | { 246 | "entity": "CellColor", 247 | "startPos": 2, 248 | "endPos": 2 249 | } 250 | ] 251 | }, 252 | { 253 | "text": "put a green cell in seven", 254 | "intent": "AddCell", 255 | "entities": [ 256 | { 257 | "entity": "CellType", 258 | "startPos": 3, 259 | "endPos": 3 260 | }, 261 | { 262 | "entity": "CellColor", 263 | "startPos": 2, 264 | "endPos": 2 265 | } 266 | ] 267 | }, 268 | { 269 | "text": "put a blue square in box five", 270 | "intent": "AddCell", 271 | "entities": [ 272 | { 273 | "entity": "CellType", 274 | "startPos": 3, 275 | "endPos": 3 276 | }, 277 | { 278 | "entity": "position", 279 | "startPos": 6, 280 | "endPos": 6 281 | }, 282 | { 283 | "entity": "CellColor", 284 | "startPos": 2, 285 | "endPos": 2 286 | } 287 | ] 288 | }, 289 | { 290 | "text": "put a red square in two", 291 | "intent": "AddCell", 292 | "entities": [ 293 | { 294 | "entity": "CellType", 295 | "startPos": 3, 296 | "endPos": 3 297 | }, 298 | { 299 | "entity": "position", 300 | "startPos": 5, 301 | "endPos": 5 302 | }, 303 | { 304 | "entity": "CellColor", 305 | "startPos": 2, 306 | "endPos": 2 307 | } 308 | ] 309 | }, 310 | { 311 | "text": "put a red cell in six", 312 | "intent": "AddCell", 313 | "entities": [ 314 | { 315 | "entity": "CellType", 316 | "startPos": 3, 317 | "endPos": 3 318 | }, 319 | { 320 | "entity": "position", 321 | "startPos": 5, 322 | "endPos": 5 323 | }, 324 | { 325 | "entity": "CellColor", 326 | "startPos": 2, 327 | "endPos": 2 328 | } 329 | ] 330 | }, 331 | { 332 | "text": "add a green cell to one", 333 | "intent": "AddCell", 334 | "entities": [ 335 | { 336 | "entity": "CellType", 337 | "startPos": 3, 338 | "endPos": 3 339 | }, 340 | { 341 | "entity": "position", 342 | "startPos": 5, 343 | "endPos": 5 344 | }, 345 | { 346 | "entity": "CellColor", 347 | "startPos": 2, 348 | "endPos": 2 349 | } 350 | ] 351 | }, 352 | { 353 | "text": "put a blue cell in the ten", 354 | "intent": "AddCell", 355 | "entities": [ 356 | { 357 | "entity": "CellType", 358 | "startPos": 3, 359 | "endPos": 3 360 | }, 361 | { 362 | "entity": "position", 363 | "startPos": 6, 364 | "endPos": 6 365 | }, 366 | { 367 | "entity": "CellColor", 368 | "startPos": 2, 369 | "endPos": 2 370 | } 371 | ] 372 | }, 373 | { 374 | "text": "move it up", 375 | "intent": "MoveCell", 376 | "entities": [ 377 | { 378 | "entity": "direction", 379 | "startPos": 2, 380 | "endPos": 2 381 | } 382 | ] 383 | }, 384 | { 385 | "text": "put the black box in seven", 386 | "intent": "AddCell", 387 | "entities": [ 388 | { 389 | "entity": "CellType", 390 | "startPos": 3, 391 | "endPos": 3 392 | }, 393 | { 394 | "entity": "position", 395 | "startPos": 5, 396 | "endPos": 5 397 | }, 398 | { 399 | "entity": "CellColor", 400 | "startPos": 2, 401 | "endPos": 2 402 | } 403 | ] 404 | }, 405 | { 406 | "text": "put the red box in two", 407 | "intent": "AddCell", 408 | "entities": [ 409 | { 410 | "entity": "CellType", 411 | "startPos": 3, 412 | "endPos": 3 413 | }, 414 | { 415 | "entity": "position", 416 | "startPos": 5, 417 | "endPos": 5 418 | }, 419 | { 420 | "entity": "CellColor", 421 | "startPos": 2, 422 | "endPos": 2 423 | } 424 | ] 425 | }, 426 | { 427 | "text": "move the black cell to three", 428 | "intent": "MoveCell", 429 | "entities": [ 430 | { 431 | "entity": "CellType", 432 | "startPos": 3, 433 | "endPos": 3 434 | }, 435 | { 436 | "entity": "position", 437 | "startPos": 5, 438 | "endPos": 5 439 | }, 440 | { 441 | "entity": "CellColor", 442 | "startPos": 2, 443 | "endPos": 2 444 | } 445 | ] 446 | }, 447 | { 448 | "text": "move it right", 449 | "intent": "MoveCell", 450 | "entities": [ 451 | { 452 | "entity": "direction", 453 | "startPos": 2, 454 | "endPos": 2 455 | } 456 | ] 457 | }, 458 | { 459 | "text": "move it down", 460 | "intent": "MoveCell", 461 | "entities": [ 462 | { 463 | "entity": "direction", 464 | "startPos": 2, 465 | "endPos": 2 466 | } 467 | ] 468 | }, 469 | { 470 | "text": "move it to ten", 471 | "intent": "MoveCell", 472 | "entities": [ 473 | { 474 | "entity": "position", 475 | "startPos": 3, 476 | "endPos": 3 477 | } 478 | ] 479 | }, 480 | { 481 | "text": "move it to twenty", 482 | "intent": "MoveCell", 483 | "entities": [ 484 | { 485 | "entity": "position", 486 | "startPos": 3, 487 | "endPos": 3 488 | } 489 | ] 490 | }, 491 | { 492 | "text": "shift it up", 493 | "intent": "MoveCell", 494 | "entities": [ 495 | { 496 | "entity": "direction", 497 | "startPos": 2, 498 | "endPos": 2 499 | } 500 | ] 501 | }, 502 | { 503 | "text": "move the black cell up", 504 | "intent": "MoveCell", 505 | "entities": [ 506 | { 507 | "entity": "CellType", 508 | "startPos": 3, 509 | "endPos": 3 510 | }, 511 | { 512 | "entity": "direction", 513 | "startPos": 4, 514 | "endPos": 4 515 | }, 516 | { 517 | "entity": "CellColor", 518 | "startPos": 2, 519 | "endPos": 2 520 | } 521 | ] 522 | }, 523 | { 524 | "text": "move it left", 525 | "intent": "MoveCell", 526 | "entities": [ 527 | { 528 | "entity": "direction", 529 | "startPos": 2, 530 | "endPos": 2 531 | } 532 | ] 533 | }, 534 | { 535 | "text": "shift it down", 536 | "intent": "MoveCell", 537 | "entities": [ 538 | { 539 | "entity": "direction", 540 | "startPos": 2, 541 | "endPos": 2 542 | } 543 | ] 544 | }, 545 | { 546 | "text": "shift it left", 547 | "intent": "MoveCell", 548 | "entities": [ 549 | { 550 | "entity": "direction", 551 | "startPos": 2, 552 | "endPos": 2 553 | } 554 | ] 555 | }, 556 | { 557 | "text": "shift it right", 558 | "intent": "MoveCell", 559 | "entities": [ 560 | { 561 | "entity": "direction", 562 | "startPos": 2, 563 | "endPos": 2 564 | } 565 | ] 566 | }, 567 | { 568 | "text": "add a green box to 20", 569 | "intent": "AddCell", 570 | "entities": [ 571 | { 572 | "entity": "CellType", 573 | "startPos": 3, 574 | "endPos": 3 575 | }, 576 | { 577 | "entity": "position", 578 | "startPos": 5, 579 | "endPos": 5 580 | }, 581 | { 582 | "entity": "CellColor", 583 | "startPos": 2, 584 | "endPos": 2 585 | } 586 | ] 587 | }, 588 | { 589 | "text": "make ten orange", 590 | "intent": "AddCell", 591 | "entities": [ 592 | { 593 | "entity": "position", 594 | "startPos": 1, 595 | "endPos": 1 596 | }, 597 | { 598 | "entity": "CellColor", 599 | "startPos": 2, 600 | "endPos": 2 601 | } 602 | ] 603 | }, 604 | { 605 | "text": "make twenty yellow", 606 | "intent": "AddCell", 607 | "entities": [ 608 | { 609 | "entity": "position", 610 | "startPos": 1, 611 | "endPos": 1 612 | }, 613 | { 614 | "entity": "CellColor", 615 | "startPos": 2, 616 | "endPos": 2 617 | } 618 | ] 619 | }, 620 | { 621 | "text": "make three red", 622 | "intent": "AddCell", 623 | "entities": [ 624 | { 625 | "entity": "CellColor", 626 | "startPos": 2, 627 | "endPos": 2 628 | } 629 | ] 630 | }, 631 | { 632 | "text": "make twenty five yellow", 633 | "intent": "AddCell", 634 | "entities": [ 635 | { 636 | "entity": "CellColor", 637 | "startPos": 3, 638 | "endPos": 3 639 | } 640 | ] 641 | }, 642 | { 643 | "text": "make eleven salmon", 644 | "intent": "AddCell", 645 | "entities": [ 646 | { 647 | "entity": "position", 648 | "startPos": 1, 649 | "endPos": 1 650 | }, 651 | { 652 | "entity": "CellColor", 653 | "startPos": 2, 654 | "endPos": 2 655 | } 656 | ] 657 | }, 658 | { 659 | "text": "make five brown", 660 | "intent": "AddCell", 661 | "entities": [ 662 | { 663 | "entity": "position", 664 | "startPos": 1, 665 | "endPos": 1 666 | }, 667 | { 668 | "entity": "CellColor", 669 | "startPos": 2, 670 | "endPos": 2 671 | } 672 | ] 673 | }, 674 | { 675 | "text": "i don't think so insane", 676 | "intent": "None", 677 | "entities": [] 678 | }, 679 | { 680 | "text": "move it to four", 681 | "intent": "MoveCell", 682 | "entities": [ 683 | { 684 | "entity": "position", 685 | "startPos": 3, 686 | "endPos": 3 687 | } 688 | ] 689 | }, 690 | { 691 | "text": "add a blue box to 25", 692 | "intent": "AddCell", 693 | "entities": [ 694 | { 695 | "entity": "CellType", 696 | "startPos": 3, 697 | "endPos": 3 698 | }, 699 | { 700 | "entity": "position", 701 | "startPos": 5, 702 | "endPos": 5 703 | }, 704 | { 705 | "entity": "CellColor", 706 | "startPos": 2, 707 | "endPos": 2 708 | } 709 | ] 710 | }, 711 | { 712 | "text": "add a red cell to 3", 713 | "intent": "AddCell", 714 | "entities": [ 715 | { 716 | "entity": "CellType", 717 | "startPos": 3, 718 | "endPos": 3 719 | }, 720 | { 721 | "entity": "position", 722 | "startPos": 5, 723 | "endPos": 5 724 | }, 725 | { 726 | "entity": "CellColor", 727 | "startPos": 2, 728 | "endPos": 2 729 | } 730 | ] 731 | }, 732 | { 733 | "text": "add a black box 24", 734 | "intent": "AddCell", 735 | "entities": [ 736 | { 737 | "entity": "CellType", 738 | "startPos": 3, 739 | "endPos": 3 740 | }, 741 | { 742 | "entity": "CellColor", 743 | "startPos": 2, 744 | "endPos": 2 745 | } 746 | ] 747 | }, 748 | { 749 | "text": "move it to 16", 750 | "intent": "MoveCell", 751 | "entities": [ 752 | { 753 | "entity": "position", 754 | "startPos": 3, 755 | "endPos": 3 756 | } 757 | ] 758 | }, 759 | { 760 | "text": "move it to sell 14", 761 | "intent": "MoveCell", 762 | "entities": [] 763 | }, 764 | { 765 | "text": "move the green box down", 766 | "intent": "MoveCell", 767 | "entities": [ 768 | { 769 | "entity": "CellType", 770 | "startPos": 3, 771 | "endPos": 3 772 | }, 773 | { 774 | "entity": "direction", 775 | "startPos": 4, 776 | "endPos": 4 777 | }, 778 | { 779 | "entity": "CellColor", 780 | "startPos": 2, 781 | "endPos": 2 782 | } 783 | ] 784 | }, 785 | { 786 | "text": "make ten red", 787 | "intent": "AddCell", 788 | "entities": [ 789 | { 790 | "entity": "position", 791 | "startPos": 1, 792 | "endPos": 1 793 | }, 794 | { 795 | "entity": "CellColor", 796 | "startPos": 2, 797 | "endPos": 2 798 | } 799 | ] 800 | }, 801 | { 802 | "text": "put a green box in 6", 803 | "intent": "AddCell", 804 | "entities": [ 805 | { 806 | "entity": "CellType", 807 | "startPos": 3, 808 | "endPos": 3 809 | }, 810 | { 811 | "entity": "position", 812 | "startPos": 5, 813 | "endPos": 5 814 | }, 815 | { 816 | "entity": "CellColor", 817 | "startPos": 2, 818 | "endPos": 2 819 | } 820 | ] 821 | }, 822 | { 823 | "text": "add a blue cell position 15", 824 | "intent": "AddCell", 825 | "entities": [ 826 | { 827 | "entity": "CellType", 828 | "startPos": 3, 829 | "endPos": 3 830 | }, 831 | { 832 | "entity": "CellColor", 833 | "startPos": 2, 834 | "endPos": 2 835 | } 836 | ] 837 | }, 838 | { 839 | "text": "put the red box in two?t=0.7152162989061839", 840 | "intent": "AddCell", 841 | "entities": [ 842 | { 843 | "entity": "CellType", 844 | "startPos": 3, 845 | "endPos": 3 846 | }, 847 | { 848 | "entity": "position", 849 | "startPos": 5, 850 | "endPos": 5 851 | }, 852 | { 853 | "entity": "CellColor", 854 | "startPos": 2, 855 | "endPos": 2 856 | } 857 | ] 858 | }, 859 | { 860 | "text": "make 5 green", 861 | "intent": "AddCell", 862 | "entities": [ 863 | { 864 | "entity": "CellColor", 865 | "startPos": 2, 866 | "endPos": 2 867 | } 868 | ] 869 | }, 870 | { 871 | "text": "make seven blue", 872 | "intent": "AddCell", 873 | "entities": [ 874 | { 875 | "entity": "CellColor", 876 | "startPos": 2, 877 | "endPos": 2 878 | } 879 | ] 880 | }, 881 | { 882 | "text": "make twenty pink", 883 | "intent": "AddCell", 884 | "entities": [ 885 | { 886 | "entity": "position", 887 | "startPos": 1, 888 | "endPos": 1 889 | }, 890 | { 891 | "entity": "CellColor", 892 | "startPos": 2, 893 | "endPos": 2 894 | } 895 | ] 896 | }, 897 | { 898 | "text": "make five orange", 899 | "intent": "AddCell", 900 | "entities": [ 901 | { 902 | "entity": "position", 903 | "startPos": 1, 904 | "endPos": 1 905 | }, 906 | { 907 | "entity": "CellColor", 908 | "startPos": 2, 909 | "endPos": 2 910 | } 911 | ] 912 | }, 913 | { 914 | "text": "make 18 purple", 915 | "intent": "AddCell", 916 | "entities": [ 917 | { 918 | "entity": "CellColor", 919 | "startPos": 2, 920 | "endPos": 2 921 | } 922 | ] 923 | }, 924 | { 925 | "text": "i want you to put a blue box in 15", 926 | "intent": "AddCell", 927 | "entities": [ 928 | { 929 | "entity": "CellType", 930 | "startPos": 7, 931 | "endPos": 7 932 | }, 933 | { 934 | "entity": "position", 935 | "startPos": 9, 936 | "endPos": 9 937 | }, 938 | { 939 | "entity": "CellColor", 940 | "startPos": 6, 941 | "endPos": 6 942 | } 943 | ] 944 | }, 945 | { 946 | "text": "remove the blue box from 12", 947 | "intent": "delete", 948 | "entities": [ 949 | { 950 | "entity": "CellColor", 951 | "startPos": 2, 952 | "endPos": 2 953 | } 954 | ] 955 | }, 956 | { 957 | "text": "get rid of 15", 958 | "intent": "delete", 959 | "entities": [] 960 | }, 961 | { 962 | "text": "put a green box in seven", 963 | "intent": "AddCell", 964 | "entities": [ 965 | { 966 | "entity": "CellType", 967 | "startPos": 3, 968 | "endPos": 3 969 | }, 970 | { 971 | "entity": "position", 972 | "startPos": 5, 973 | "endPos": 5 974 | }, 975 | { 976 | "entity": "CellColor", 977 | "startPos": 2, 978 | "endPos": 2 979 | } 980 | ] 981 | }, 982 | { 983 | "text": "move square right", 984 | "intent": "MoveCell", 985 | "entities": [ 986 | { 987 | "entity": "CellType", 988 | "startPos": 1, 989 | "endPos": 1 990 | }, 991 | { 992 | "entity": "direction", 993 | "startPos": 2, 994 | "endPos": 2 995 | } 996 | ] 997 | }, 998 | { 999 | "text": "move square left", 1000 | "intent": "MoveCell", 1001 | "entities": [ 1002 | { 1003 | "entity": "direction", 1004 | "startPos": 2, 1005 | "endPos": 2 1006 | } 1007 | ] 1008 | }, 1009 | { 1010 | "text": "add purple cell to three", 1011 | "intent": "AddCell", 1012 | "entities": [ 1013 | { 1014 | "entity": "CellType", 1015 | "startPos": 2, 1016 | "endPos": 2 1017 | }, 1018 | { 1019 | "entity": "position", 1020 | "startPos": 4, 1021 | "endPos": 4 1022 | }, 1023 | { 1024 | "entity": "CellColor", 1025 | "startPos": 1, 1026 | "endPos": 1 1027 | } 1028 | ] 1029 | }, 1030 | { 1031 | "text": "add red cell to three", 1032 | "intent": "AddCell", 1033 | "entities": [ 1034 | { 1035 | "entity": "CellType", 1036 | "startPos": 2, 1037 | "endPos": 2 1038 | }, 1039 | { 1040 | "entity": "position", 1041 | "startPos": 4, 1042 | "endPos": 4 1043 | }, 1044 | { 1045 | "entity": "CellColor", 1046 | "startPos": 1, 1047 | "endPos": 1 1048 | } 1049 | ] 1050 | }, 1051 | { 1052 | "text": "move cell to the right", 1053 | "intent": "MoveCell", 1054 | "entities": [ 1055 | { 1056 | "entity": "CellType", 1057 | "startPos": 1, 1058 | "endPos": 1 1059 | }, 1060 | { 1061 | "entity": "direction", 1062 | "startPos": 4, 1063 | "endPos": 4 1064 | } 1065 | ] 1066 | }, 1067 | { 1068 | "text": "move cell to the left", 1069 | "intent": "MoveCell", 1070 | "entities": [ 1071 | { 1072 | "entity": "CellType", 1073 | "startPos": 1, 1074 | "endPos": 1 1075 | }, 1076 | { 1077 | "entity": "direction", 1078 | "startPos": 4, 1079 | "endPos": 4 1080 | } 1081 | ] 1082 | } 1083 | ] 1084 | } -------------------------------------------------------------------------------- /public/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.1.1 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.isLoading=!1};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",f.resetText||d.data("resetText",d[e]()),d[e](f[b]||this.options[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},b.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});return this.$element.trigger(j),j.isDefaultPrevented()?void 0:(this.sliding=!0,f&&this.pause(),this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(1e3*d.css("transition-duration").slice(0,-1))):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")),f&&this.cycle(),this)};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("collapse in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?void this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);!e&&f.toggle&&"show"==c&&(c=!c),e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(b){a(d).remove(),a(e).each(function(){var d=c(a(this)),e={relatedTarget:this};d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown",e)),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown",e))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;(e||"destroy"!=c)&&(e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]())})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(a(c).is("body")?window:c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);{var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})}},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);if(g&&b<=e[0])return g!=(a=f[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parentsUntil(this.options.target,".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(b.RESET).addClass("affix");var a=this.$window.scrollTop(),c=this.$element.offset();return this.pinnedOffset=c.top-a},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"top"==this.affixed&&(e.top+=d),"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top(this.$element)),"function"==typeof h&&(h=f.bottom(this.$element));var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;if(this.affixed!==i){this.unpin&&this.$element.css("top","");var j="affix"+(i?"-"+i:""),k=a.Event(j+".bs.affix");this.$element.trigger(k),k.isDefaultPrevented()||(this.affixed=i,this.unpin="bottom"==i?this.getPinnedOffset():null,this.$element.removeClass(b.RESET).addClass(j).trigger(a.Event(j.replace("affix","affixed"))),"bottom"==i&&this.$element.offset({top:c-h-this.$element.height()}))}}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(jQuery); 7 | -------------------------------------------------------------------------------- /public/img/save.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 21 | 24 | 28 | 32 | 33 | 36 | 40 | 44 | 45 | 48 | 52 | 56 | 57 | 68 | 71 | 75 | 79 | 80 | 90 | 93 | 97 | 101 | 102 | 113 | 115 | 119 | 123 | 127 | 131 | 135 | 136 | 138 | 142 | 146 | 147 | 150 | 154 | 158 | 159 | 162 | 166 | 170 | 171 | 173 | 177 | 181 | 182 | 185 | 189 | 193 | 194 | 196 | 200 | 204 | 205 | 207 | 211 | 215 | 216 | 226 | 236 | 246 | 257 | 267 | 278 | 288 | 297 | 306 | 315 | 316 | 337 | 339 | 340 | 342 | image/svg+xml 343 | 345 | Save 346 | 347 | 348 | Jakub Steiner 349 | 350 | 351 | 352 | 353 | hdd 354 | hard drive 355 | save 356 | io 357 | store 358 | 359 | 360 | 362 | 363 | http://jimmac.musichall.cz 364 | 365 | 367 | 369 | 371 | 373 | 375 | 377 | 379 | 380 | 381 | 382 | 386 | 396 | 401 | 406 | 411 | 418 | 423 | 428 | 432 | 442 | 452 | 457 | 461 | 465 | 469 | 473 | 477 | 481 | 485 | 489 | 493 | 497 | 501 | 505 | 515 | 516 | 520 | 530 | 535 | 540 | 545 | 546 | 547 | -------------------------------------------------------------------------------- /public/js/p5libs/p5.dom.js: -------------------------------------------------------------------------------- 1 | /*! p5.dom.js v0.2.11 June 17, 2016 */ 2 | /** 3 | *

The web is much more than just canvas and p5.dom makes it easy to interact 4 | * with other HTML5 objects, including text, hyperlink, image, input, video, 5 | * audio, and webcam.

6 | *

There is a set of creation methods, DOM manipulation methods, and 7 | * an extended p5.Element that supports a range of HTML elements. See the 8 | * 9 | * beyond the canvas tutorial for a full overview of how this addon works. 10 | * 11 | *

Methods and properties shown in black are part of the p5.js core, items in 12 | * blue are part of the p5.dom library. You will need to include an extra file 13 | * in order to access the blue functions. See the 14 | * using a library 15 | * section for information on how to include this library. p5.dom comes with 16 | * p5 complete or you can download the single file 17 | * 18 | * here.

19 | *

See tutorial: beyond the canvas 20 | * for more info on how to use this libary. 21 | * 22 | * @module p5.dom 23 | * @submodule p5.dom 24 | * @for p5.dom 25 | * @main 26 | */ 27 | 28 | (function (root, factory) { 29 | if (typeof define === 'function' && define.amd) 30 | define('p5.dom', ['p5'], function (p5) { (factory(p5));}); 31 | else if (typeof exports === 'object') 32 | factory(require('../p5')); 33 | else 34 | factory(root['p5']); 35 | }(this, function (p5) { 36 | // ============================================================================= 37 | // p5 additions 38 | // ============================================================================= 39 | 40 | /** 41 | * Searches the page for an element with the given ID, class, or tag name (using the '#' or '.' 42 | * prefixes to specify an ID or class respectively, and none for a tag) and returns it as 43 | * a p5.Element. If a class or tag name is given with more than 1 element, 44 | * only the first element will be returned. 45 | * The DOM node itself can be accessed with .elt. 46 | * Returns null if none found. You can also specify a container to search within. 47 | * 48 | * @method select 49 | * @param {String} name id, class, or tag name of element to search for 50 | * @param {String} [container] id, p5.Element, or HTML element to search within 51 | * @return {Object/p5.Element|Null} p5.Element containing node found 52 | * @example 53 | *

54 | * function setup() { 55 | * createCanvas(100,100); 56 | * //translates canvas 50px down 57 | * select('canvas').position(100, 100); 58 | * } 59 | *
60 | *
61 | * // these are all valid calls to select() 62 | * var a = select('#moo'); 63 | * var b = select('#blah', '#myContainer'); 64 | * var c = select('#foo', b); 65 | * var d = document.getElementById('beep'); 66 | * var e = select('p', d); 67 | *
68 | * 69 | */ 70 | p5.prototype.select = function (e, p) { 71 | var res = null; 72 | var container = getContainer(p); 73 | if (e[0] === '.'){ 74 | e = e.slice(1); 75 | res = container.getElementsByClassName(e); 76 | if (res.length) { 77 | res = res[0]; 78 | } else { 79 | res = null; 80 | } 81 | }else if (e[0] === '#'){ 82 | e = e.slice(1); 83 | res = container.getElementById(e); 84 | }else { 85 | res = container.getElementsByTagName(e); 86 | if (res.length) { 87 | res = res[0]; 88 | } else { 89 | res = null; 90 | } 91 | } 92 | if (res) { 93 | return wrapElement(res); 94 | } else { 95 | return null; 96 | } 97 | }; 98 | 99 | /** 100 | * Searches the page for elements with the given class or tag name (using the '.' prefix 101 | * to specify a class and no prefix for a tag) and returns them as p5.Elements 102 | * in an array. 103 | * The DOM node itself can be accessed with .elt. 104 | * Returns an empty array if none found. 105 | * You can also specify a container to search within. 106 | * 107 | * @method selectAll 108 | * @param {String} name class or tag name of elements to search for 109 | * @param {String} [container] id, p5.Element, or HTML element to search within 110 | * @return {Array} Array of p5.Elements containing nodes found 111 | * @example 112 | *
113 | * function setup() { 114 | * createButton('btn'); 115 | * createButton('2nd btn'); 116 | * createButton('3rd btn'); 117 | * var buttons = selectAll('button'); 118 | * 119 | * for (var i = 0; i < buttons.length; i++){ 120 | * buttons[i].size(100,100); 121 | * } 122 | * } 123 | *
124 | *
125 | * // these are all valid calls to selectAll() 126 | * var a = selectAll('.moo'); 127 | * var b = selectAll('div'); 128 | * var c = selectAll('button', '#myContainer'); 129 | * var d = select('#container'); 130 | * var e = selectAll('p', d); 131 | * var f = document.getElementById('beep'); 132 | * var g = select('.blah', f); 133 | *
134 | * 135 | */ 136 | p5.prototype.selectAll = function (e, p) { 137 | var arr = []; 138 | var res; 139 | var container = getContainer(p); 140 | if (e[0] === '.'){ 141 | e = e.slice(1); 142 | res = container.getElementsByClassName(e); 143 | } else { 144 | res = container.getElementsByTagName(e); 145 | } 146 | if (res) { 147 | for (var j = 0; j < res.length; j++) { 148 | var obj = wrapElement(res[j]); 149 | arr.push(obj); 150 | } 151 | } 152 | return arr; 153 | }; 154 | 155 | /** 156 | * Helper function for select and selectAll 157 | */ 158 | function getContainer(p) { 159 | var container = document; 160 | if (typeof p === 'string' && p[0] === '#'){ 161 | p = p.slice(1); 162 | container = document.getElementById(p) || document; 163 | } else if (p instanceof p5.Element){ 164 | container = p.elt; 165 | } else if (p instanceof HTMLElement){ 166 | container = p; 167 | } 168 | return container; 169 | } 170 | 171 | /** 172 | * Helper function for getElement and getElements. 173 | */ 174 | function wrapElement(elt) { 175 | if(elt.tagName === "INPUT" && elt.type === "checkbox") { 176 | var converted = new p5.Element(elt); 177 | converted.checked = function(){ 178 | if (arguments.length === 0){ 179 | return this.elt.checked; 180 | } else if(arguments[0]) { 181 | this.elt.checked = true; 182 | } else { 183 | this.elt.checked = false; 184 | } 185 | return this; 186 | }; 187 | return converted; 188 | } else if (elt.tagName === "VIDEO" || elt.tagName === "AUDIO") { 189 | return new p5.MediaElement(elt); 190 | } else { 191 | return new p5.Element(elt); 192 | } 193 | } 194 | 195 | /** 196 | * Removes all elements created by p5, except any canvas / graphics 197 | * elements created by createCanvas or createGraphics. 198 | * Event handlers are removed, and element is removed from the DOM. 199 | * @method removeElements 200 | * @example 201 | *
202 | * function setup() { 203 | * createCanvas(100, 100); 204 | * createDiv('this is some text'); 205 | * createP('this is a paragraph'); 206 | * } 207 | * function mousePressed() { 208 | * removeElements(); // this will remove the div and p, not canvas 209 | * } 210 | *
211 | * 212 | */ 213 | p5.prototype.removeElements = function (e) { 214 | for (var i=0; i 242 | * var myDiv; 243 | * function setup() { 244 | * myDiv = createDiv('this is some text'); 245 | * } 246 | * 247 | */ 248 | 249 | /** 250 | * Creates a <p></p> element in the DOM with given inner HTML. Used 251 | * for paragraph length text. 252 | * Appends to the container node if one is specified, otherwise 253 | * appends to body. 254 | * 255 | * @method createP 256 | * @param {String} html inner HTML for element created 257 | * @return {Object/p5.Element} pointer to p5.Element holding created node 258 | * @example 259 | *
260 | * var myP; 261 | * function setup() { 262 | * myP = createP('this is some text'); 263 | * } 264 | *
265 | */ 266 | 267 | /** 268 | * Creates a <span></span> element in the DOM with given inner HTML. 269 | * Appends to the container node if one is specified, otherwise 270 | * appends to body. 271 | * 272 | * @method createSpan 273 | * @param {String} html inner HTML for element created 274 | * @return {Object/p5.Element} pointer to p5.Element holding created node 275 | * @example 276 | *
277 | * var mySpan; 278 | * function setup() { 279 | * mySpan = createSpan('this is some text'); 280 | * } 281 | *
282 | */ 283 | var tags = ['div', 'p', 'span']; 284 | tags.forEach(function(tag) { 285 | var method = 'create' + tag.charAt(0).toUpperCase() + tag.slice(1); 286 | p5.prototype[method] = function(html) { 287 | var elt = document.createElement(tag); 288 | elt.innerHTML = typeof html === undefined ? "" : html; 289 | return addElement(elt, this); 290 | } 291 | }); 292 | 293 | /** 294 | * Creates an <img /> element in the DOM with given src and 295 | * alternate text. 296 | * Appends to the container node if one is specified, otherwise 297 | * appends to body. 298 | * 299 | * @method createImg 300 | * @param {String} src src path or url for image 301 | * @param {String} [alt] alternate text to be used if image does not load 302 | * @param {Function} [successCallback] callback to be called once image data is loaded 303 | * @return {Object/p5.Element} pointer to p5.Element holding created node 304 | * @example 305 | *
306 | * var img; 307 | * function setup() { 308 | * img = createImg('http://p5js.org/img/asterisk-01.png'); 309 | * } 310 | *
311 | */ 312 | p5.prototype.createImg = function() { 313 | var elt = document.createElement('img'); 314 | var args = arguments; 315 | var self; 316 | var setAttrs = function(){ 317 | self.width = elt.offsetWidth; 318 | self.height = elt.offsetHeight; 319 | if (args.length > 1 && typeof args[1] === 'function'){ 320 | self.fn = args[1]; 321 | self.fn(); 322 | }else if (args.length > 1 && typeof args[2] === 'function'){ 323 | self.fn = args[2]; 324 | self.fn(); 325 | } 326 | }; 327 | elt.src = args[0]; 328 | if (args.length > 1 && typeof args[1] === 'string'){ 329 | elt.alt = args[1]; 330 | } 331 | elt.onload = function(){ 332 | setAttrs(); 333 | } 334 | self = addElement(elt, this); 335 | return self; 336 | }; 337 | 338 | /** 339 | * Creates an <a></a> element in the DOM for including a hyperlink. 340 | * Appends to the container node if one is specified, otherwise 341 | * appends to body. 342 | * 343 | * @method createA 344 | * @param {String} href url of page to link to 345 | * @param {String} html inner html of link element to display 346 | * @param {String} [target] target where new link should open, 347 | * could be _blank, _self, _parent, _top. 348 | * @return {Object/p5.Element} pointer to p5.Element holding created node 349 | * @example 350 | *
351 | * var myLink; 352 | * function setup() { 353 | * myLink = createA('http://p5js.org/', 'this is a link'); 354 | * } 355 | *
356 | */ 357 | p5.prototype.createA = function(href, html, target) { 358 | var elt = document.createElement('a'); 359 | elt.href = href; 360 | elt.innerHTML = html; 361 | if (target) elt.target = target; 362 | return addElement(elt, this); 363 | }; 364 | 365 | /** INPUT **/ 366 | 367 | 368 | /** 369 | * Creates a slider <input></input> element in the DOM. 370 | * Use .size() to set the display length of the slider. 371 | * Appends to the container node if one is specified, otherwise 372 | * appends to body. 373 | * 374 | * @method createSlider 375 | * @param {Number} min minimum value of the slider 376 | * @param {Number} max maximum value of the slider 377 | * @param {Number} [value] default value of the slider 378 | * @param {Number} [step] step size for each tick of the slider 379 | * @return {Object/p5.Element} pointer to p5.Element holding created node 380 | * @example 381 | *
382 | * var slider; 383 | * function setup() { 384 | * slider = createSlider(0, 255, 100); 385 | * slider.position(10, 10); 386 | * slider.style('width', '80px'); 387 | * } 388 | * 389 | * function draw() { 390 | * var val = slider.value(); 391 | * background(val); 392 | * } 393 | *
394 | * 395 | *
396 | * var slider; 397 | * function setup() { 398 | * colorMode(HSB); 399 | * slider = createSlider(0, 360, 60, 40); 400 | * slider.position(10, 10); 401 | * slider.style('width', '80px'); 402 | * } 403 | * 404 | * function draw() { 405 | * var val = slider.value(); 406 | * background(val, 100, 100, 1); 407 | * } 408 | *
409 | */ 410 | p5.prototype.createSlider = function(min, max, value, step) { 411 | var elt = document.createElement('input'); 412 | elt.type = 'range'; 413 | elt.min = min; 414 | elt.max = max; 415 | if (step) elt.step = step; 416 | if (typeof(value) === "number") elt.value = value; 417 | return addElement(elt, this); 418 | }; 419 | 420 | /** 421 | * Creates a <button></button> element in the DOM. 422 | * Use .size() to set the display size of the button. 423 | * Use .mousePressed() to specify behavior on press. 424 | * Appends to the container node if one is specified, otherwise 425 | * appends to body. 426 | * 427 | * @method createButton 428 | * @param {String} label label displayed on the button 429 | * @param {String} [value] value of the button 430 | * @return {Object/p5.Element} pointer to p5.Element holding created node 431 | * @example 432 | *
433 | * var button; 434 | * function setup() { 435 | * createCanvas(100, 100); 436 | * background(0); 437 | * button = createButton('click me'); 438 | * button.position(19, 19); 439 | * button.mousePressed(changeBG); 440 | * } 441 | * 442 | * function changeBG() { 443 | * var val = random(255); 444 | * background(val); 445 | * } 446 | *
447 | */ 448 | p5.prototype.createButton = function(label, value) { 449 | var elt = document.createElement('button'); 450 | elt.innerHTML = label; 451 | elt.value = value; 452 | if (value) elt.value = value; 453 | return addElement(elt, this); 454 | }; 455 | 456 | /** 457 | * Creates a checkbox <input></input> element in the DOM. 458 | * Calling .checked() on a checkbox returns if it is checked or not 459 | * 460 | * @method createCheckbox 461 | * @param {String} [label] label displayed after checkbox 462 | * @param {boolean} [value] value of the checkbox; checked is true, unchecked is false.Unchecked if no value given 463 | * @return {Object/p5.Element} pointer to p5.Element holding created node 464 | * @example 465 | *
466 | * var checkbox; 467 | * 468 | * function setup() { 469 | * checkbox = createCheckbox('label', false); 470 | * checkbox.changed(myCheckedEvent); 471 | * } 472 | * 473 | * function myCheckedEvent() { 474 | * if (this.checked()) { 475 | * console.log("Checking!"); 476 | * } else { 477 | * console.log("Unchecking!"); 478 | * } 479 | * } 480 | *
481 | */ 482 | p5.prototype.createCheckbox = function() { 483 | var elt = document.createElement('div'); 484 | var checkbox = document.createElement('input'); 485 | checkbox.type = 'checkbox'; 486 | elt.appendChild(checkbox); 487 | //checkbox must be wrapped in p5.Element before label so that label appears after 488 | var self = addElement(elt, this); 489 | self.checked = function(){ 490 | var cb = self.elt.getElementsByTagName('input')[0]; 491 | if (cb) { 492 | if (arguments.length === 0){ 493 | return cb.checked; 494 | }else if(arguments[0]){ 495 | cb.checked = true; 496 | }else{ 497 | cb.checked = false; 498 | } 499 | } 500 | return self; 501 | }; 502 | this.value = function(val){ 503 | self.value = val; 504 | return this; 505 | }; 506 | if (arguments[0]){ 507 | var ran = Math.random().toString(36).slice(2); 508 | var label = document.createElement('label'); 509 | checkbox.setAttribute('id', ran); 510 | label.htmlFor = ran; 511 | self.value(arguments[0]); 512 | label.appendChild(document.createTextNode(arguments[0])); 513 | elt.appendChild(label); 514 | } 515 | if (arguments[1]){ 516 | checkbox.checked = true; 517 | } 518 | return self; 519 | }; 520 | 521 | /** 522 | * Creates a dropdown menu <select></select> element in the DOM. 523 | * @method createSelect 524 | * @param {boolean} [multiple] [true if dropdown should support multiple selections] 525 | * @return {Object/p5.Element} pointer to p5.Element holding created node 526 | * @example 527 | *
528 | * var sel; 529 | * 530 | * function setup() { 531 | * textAlign(CENTER); 532 | * background(200); 533 | * sel = createSelect(); 534 | * sel.position(10, 10); 535 | * sel.option('pear'); 536 | * sel.option('kiwi'); 537 | * sel.option('grape'); 538 | * sel.changed(mySelectEvent); 539 | * } 540 | * 541 | * function mySelectEvent() { 542 | * var item = sel.value(); 543 | * background(200); 544 | * text("it's a "+item+"!", 50, 50); 545 | * } 546 | *
547 | */ 548 | p5.prototype.createSelect = function(mult) { 549 | var elt = document.createElement('select'); 550 | if (mult){ 551 | elt.setAttribute('multiple', 'true'); 552 | } 553 | var self = addElement(elt, this); 554 | self.option = function(name, value){ 555 | var opt = document.createElement('option'); 556 | opt.innerHTML = name; 557 | if (arguments.length > 1) 558 | opt.value = value; 559 | else 560 | opt.value = name; 561 | elt.appendChild(opt); 562 | }; 563 | self.selected = function(value){ 564 | var arr = []; 565 | if (arguments.length > 0){ 566 | for (var i = 0; i < this.elt.length; i++){ 567 | if (value.toString() === this.elt[i].value){ 568 | this.elt.selectedIndex = i; 569 | } 570 | } 571 | return this; 572 | }else{ 573 | if (mult){ 574 | for (var i = 0; i < this.elt.selectedOptions.length; i++){ 575 | arr.push(this.elt.selectedOptions[i].value); 576 | } 577 | return arr; 578 | }else{ 579 | return this.elt.value; 580 | } 581 | } 582 | }; 583 | return self; 584 | }; 585 | 586 | /** 587 | * Creates a radio button <input></input> element in the DOM. 588 | * The .option() method can be used to set options for the radio after it is 589 | * created. The .value() method will return the currently selected option. 590 | * 591 | * @method createRadio 592 | * @param {String} [divId] the id and name of the created div and input field respectively 593 | * @return {Object/p5.Element} pointer to p5.Element holding created node 594 | * @example 595 | *
596 | * var radio; 597 | * 598 | * function setup() { 599 | * radio = createRadio(); 600 | * radio.option("black"); 601 | * radio.option("white"); 602 | * radio.option("gray"); 603 | * radio.style('width', '60px'); 604 | * textAlign(CENTER); 605 | * fill(255, 0, 0); 606 | * } 607 | * 608 | * function draw() { 609 | * var val = radio.value(); 610 | * background(val); 611 | * text(val, width/2, height/2); 612 | * } 613 | *
614 | *
615 | * var radio; 616 | * 617 | * function setup() { 618 | * radio = createRadio(); 619 | * radio.option('apple', 1); 620 | * radio.option('bread', 2); 621 | * radio.option('juice', 3); 622 | * radio.style('width', '60px'); 623 | * textAlign(CENTER); 624 | * } 625 | * 626 | * function draw() { 627 | * background(200); 628 | * var val = radio.value(); 629 | * if (val) { 630 | * text('item cost is $'+val, width/2, height/2); 631 | * } 632 | * } 633 | *
634 | */ 635 | p5.prototype.createRadio = function() { 636 | var radios = document.querySelectorAll("input[type=radio]"); 637 | var count = 0; 638 | if(radios.length > 1){ 639 | console.log(radios,radios[0].name); 640 | var length = radios.length; 641 | var prev=radios[0].name; 642 | var current = radios[1].name; 643 | count=1; 644 | for(var i = 1; i < length; i++ ){ 645 | current = radios[i].name; 646 | if(prev != current){ 647 | count++; 648 | } 649 | prev = current; 650 | } 651 | } 652 | else if (radios.length == 1){ 653 | count = 1; 654 | } 655 | var elt = document.createElement('div'); 656 | var self = addElement(elt, this); 657 | var times = -1; 658 | self.option = function(name, value){ 659 | var opt = document.createElement('input'); 660 | opt.type = 'radio'; 661 | opt.innerHTML = name; 662 | if (arguments.length > 1) 663 | opt.value = value; 664 | else 665 | opt.value = name; 666 | opt.setAttribute('name',"defaultradio"+count); 667 | elt.appendChild(opt); 668 | if (name){ 669 | times++; 670 | var ran = Math.random().toString(36).slice(2); 671 | var label = document.createElement('label'); 672 | opt.setAttribute('id', "defaultradio"+count+"-"+times); 673 | label.htmlFor = "defaultradio"+count+"-"+times; 674 | label.appendChild(document.createTextNode(name)); 675 | elt.appendChild(label); 676 | } 677 | return opt; 678 | }; 679 | self.selected = function(){ 680 | var length = this.elt.childNodes.length; 681 | if(arguments[0]) { 682 | for (var i = 0; i < length; i+=2){ 683 | if(this.elt.childNodes[i].value == arguments[0]) 684 | this.elt.childNodes[i].checked = true; 685 | } 686 | return this; 687 | } else { 688 | for (var i = 0; i < length; i+=2){ 689 | if(this.elt.childNodes[i].checked == true) 690 | return this.elt.childNodes[i].value; 691 | } 692 | } 693 | }; 694 | self.value = function(){ 695 | var length = this.elt.childNodes.length; 696 | if(arguments[0]) { 697 | for (var i = 0; i < length; i+=2){ 698 | if(this.elt.childNodes[i].value == arguments[0]) 699 | this.elt.childNodes[i].checked = true; 700 | } 701 | return this; 702 | } else { 703 | for (var i = 0; i < length; i+=2){ 704 | if(this.elt.childNodes[i].checked == true) 705 | return this.elt.childNodes[i].value; 706 | } 707 | return ""; 708 | } 709 | }; 710 | return self 711 | }; 712 | 713 | /** 714 | * Creates an <input></input> element in the DOM for text input. 715 | * Use .size() to set the display length of the box. 716 | * Appends to the container node if one is specified, otherwise 717 | * appends to body. 718 | * 719 | * @method createInput 720 | * @param {Number} [value] default value of the input box 721 | * @return {Object/p5.Element} pointer to p5.Element holding created node 722 | * @example 723 | *
724 | * function setup(){ 725 | * var inp = createInput(''); 726 | * inp.input(myInputEvent); 727 | * } 728 | * 729 | * function myInputEvent(){ 730 | * console.log('you are typing: ', this.value()); 731 | * } 732 | * 733 | *
734 | */ 735 | p5.prototype.createInput = function(value) { 736 | var elt = document.createElement('input'); 737 | elt.type = 'text'; 738 | if (value) elt.value = value; 739 | return addElement(elt, this); 740 | }; 741 | 742 | /** 743 | * Creates an <input></input> element in the DOM of type 'file'. 744 | * This allows users to select local files for use in a sketch. 745 | * 746 | * @method createFileInput 747 | * @param {Function} [callback] callback function for when a file loaded 748 | * @param {String} [multiple] optional to allow multiple files selected 749 | * @return {Object/p5.Element} pointer to p5.Element holding created DOM element 750 | */ 751 | p5.prototype.createFileInput = function(callback, multiple) { 752 | 753 | // Is the file stuff supported? 754 | if (window.File && window.FileReader && window.FileList && window.Blob) { 755 | // Yup, we're ok and make an input file selector 756 | var elt = document.createElement('input'); 757 | elt.type = 'file'; 758 | 759 | // If we get a second argument that evaluates to true 760 | // then we are looking for multiple files 761 | if (multiple) { 762 | // Anything gets the job done 763 | elt.multiple = 'multiple'; 764 | } 765 | 766 | // Function to handle when a file is selected 767 | // We're simplifying life and assuming that we always 768 | // want to load every selected file 769 | function handleFileSelect(evt) { 770 | // These are the files 771 | var files = evt.target.files; 772 | // Load each one and trigger a callback 773 | for (var i = 0; i < files.length; i++) { 774 | var f = files[i]; 775 | var reader = new FileReader(); 776 | function makeLoader(theFile) { 777 | // Making a p5.File object 778 | var p5file = new p5.File(theFile); 779 | return function(e) { 780 | p5file.data = e.target.result; 781 | callback(p5file); 782 | }; 783 | }; 784 | reader.onload = makeLoader(f); 785 | 786 | // Text or data? 787 | // This should likely be improved 788 | if (f.type.indexOf('text') > -1) { 789 | reader.readAsText(f); 790 | } else { 791 | reader.readAsDataURL(f); 792 | } 793 | } 794 | } 795 | 796 | // Now let's handle when a file was selected 797 | elt.addEventListener('change', handleFileSelect, false); 798 | return addElement(elt, this); 799 | } else { 800 | console.log('The File APIs are not fully supported in this browser. Cannot create element.'); 801 | } 802 | }; 803 | 804 | 805 | /** VIDEO STUFF **/ 806 | 807 | function createMedia(pInst, type, src, callback) { 808 | var elt = document.createElement(type); 809 | 810 | // allow src to be empty 811 | var src = src || ''; 812 | if (typeof src === 'string') { 813 | src = [src]; 814 | } 815 | for (var i=0; i