ResponsiveVoice.JS Test - JSFiddle

by MegaScience

HTML

<script src="https://code.responsivevoice.org/responsivevoice.js"></script>
<form name="TTS" onsubmit="TTSObj.processSubmit(); return false">
  <fieldset>
    <legend class="clickToDrag"><legend class="label"><label for="advanced">Advanced</label></legend>Responsive Voice
      <br>Sound Player</legend>
    <table>
      <tbody>
        <tr>
          <td>
            <select name="voice">
              <option selected>UK English Female</option>
            </select>
          </td>
        </tr>
        <tr>
          <td>
            <textarea required name="text" type="text" placeholder="Type here.">Example text.</textarea>
          </td>
        </tr>
        <tr>
          <td class="sliders">
            <div>
              <output data-name="Volume" name="volumeOutName" id="volumeOutID">1</output>
              <input type="range" name="volumeName" id="volumeID" value="1" min="0" max="1" step="0.001" oninput="volumeOutID.value = volumeID.value">
            </div><br/>
            <input name="advanced" id="advanced" type="checkbox" />
            <div class="advanced">
              <div>
                <output data-name="Pitch" name="pitchOutName" id="pitchOutID">1</output>
                <input type="range" name="pitchName" id="pitchID" value="1" min="0" max="2" step="0.01" oninput="pitchOutID.value = pitchID.value">
              </div><br/>
              <div>
                <output data-name="Rate" name="rateOutName" id="rateOutID">1</output>
                <input type="range" name="rateName" id="rateID" value="1" min="0" max="1.5" step="0.1" oninput="rateOutID.value = rateID.value">
              </div>
            </div>
          </td>
        </tr>
        <tr>
          <td>
            <input name="submit" type="submit" value="Play Voice" />
          </td>
        </tr>
      </tbody>
    </table>
  </fieldset>
</form>
<!--
<input onclick='responsiveVoice.speak("Hello World");' type='button' value='������ Play' />-->

CSS

html,
body {
  height: 100%;
  margin: 0;
}

body {
  overflow: hidden;
}

form {
  display: table;
  position: relative;
  margin: 0 auto;
}

fieldset {
  box-sizing: border-box;
  border: 1px solid black;
  border-radius: 4px;
  background-color: ivory;
  padding: 5px;
}

legend.clickToDrag {
  background-color: floralwhite;
  text-align: center;
  border: 1px dashed black;
  border-radius: 7px;
}

select,
textarea {
  margin-bottom: 5px;
}

output,
input[type="range"] {
  clear: both;
}

input,
textarea {
  width: 100%;
  box-sizing: border-box;
}

td.sliders,
div.advanced {
  /*display: flex;
  flex-wrap: wrap;
  flex-direction: row;
  justify-content: space-between;
  align-items: stretch;*/
  text-align: center;
}

td.sliders {
  line-height: 0;
}

output {
  line-height: 1em;
}

/*td.sliders > div,
td.sliders > div.advanced > div {
  flex: 1 1 auto;
}*/

output[data-name]::before {
  content: attr(data-name) ": ";
  font-weight: bold;
}

form[name="TTS"] {
  position: absolute;
  z-index: 9;
}

form[name="TTS"] legend.clickToDrag {
  -webkit-user-select: none;
  -moz-user-select: none;
  user-select: none;
  cursor: move;
  z-index: 10;
}

input#advanced {
  position: fixed;
  left: -9999px;
}

legend.clickToDrag legend.label/*, label[for="advanced"]*/ {
  position: absolute;
  right: 0.5em;
  top: 0.5em;
  /*width: 1.3em;
  height: 1.3em;
  box-sizing: border-box;
  background-color: #2980B9;
  color: white;*/
  border: 1px solid black;
  background-color: white;
  border-radius: 4px;
  text-align: center;
}

input#advanced:not(:checked) + div.advanced {
  display: none;
}

JavaScript

const TTSObj = {
    form: document.forms.TTS,
    processSubmit: function () {
        if (responsiveVoice.voiceSupport()) {
            if (!responsiveVoice.isPlaying()) {
                if ((txt = this.form.text.value.trim()).length > 0) {
                    this.form.submit.disabled = true
                    this.form.submit.value = '...'
                    responsiveVoice.speak(txt, this.form.voice.value, {
                        pitch: this.form.pitchName.value,
                        rate: this.form.rateName.value,
                        volume: this.form.volumeName.value,
                        onstart: () => {
                            this.form.submit.value = 'Stop Voice'
                            this.form.submit.disabled = false
                        },
                        onend: () => this.form.submit.value = 'Play Voice'
                    })
                } else alert('Please type a text to say.')
            } else {
                this.form.submit.value = 'Play Voice'
                responsiveVoice.cancel()
            }
        } else alert('Browser does not support TTS.')
    },
    init: function () {
        dragElement(this.form)
        if (responsiveVoice.voiceSupport()) {
            this.form.voice.remove(0)
            for (let v of responsiveVoice.getVoices()) {
                let o = document.createElement('option')
                o.textContent = v.name
                this.form.voice.add(o)
            }
        } else this.form.submit.disabled = true
    }
}
// pitch, volume, rate, onstart, onend
// https://www.w3schools.com/howto/howto_js_draggable.asp
const dragElement = (elem, par) => {
    let mouseDown = false
    let positions = new Array(4).fill(0)
    let bounds = {}

    const within = (i, min, max) => Math.min(Math.max(i, min), max)
    const dragMouseDown = e => {
        mouseDown = true
        e = eventType(e || window.event)
        positions[3] = e.clientX
        positions[4] = e.clientY
   ...