1/** 2 * Customised version of talkify.js for GSK 3 */ 4 5(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ 6var talkify = require('./src/talkify.js'); 7var talkifyConfig = require('./src/talkify-config.js'); 8var talkifyUtils = require('./src/talkify-utils.js'); 9var talkifyMessageHub = require('./src/talkify-messagehub.js'); 10var talkifyHttp = require('./src/talkify-ajax.js'); 11var TalkifyTextextractor = require('./src/talkify-textextractor.js'); 12var TalkifyWordHighlighter = require('./src/talkify-word-highlighter.js'); 13var BasePlayer = require('./src/talkify-player-core.js'); 14var Html5Player = require('./src/talkify-html5-speechsynthesis-player.js'); 15var TtsPlayer = require('./src/talkify-player.js'); 16var talkifyPlaylist = require('./src/talkify-playlist.js'); 17var talkifyControlcenterCore = require('./src/control-centers/talkify-controlcenter-core.js'); 18var talkifyPlaybar = require('./src/control-centers/talkify-controlcenter-classic.js'); 19var talkifyModernControlcenter = require('./src/control-centers/talkify-controlcenter-modern.js'); 20var talkifyLocalControlcenter = require('./src/control-centers/talkify-controlcenter-local.js'); 21var talkifyKeyCommands = require('./src/talkify-keyboard-commands.js'); 22var talkifyVoiceCommands = require('./src/talkify-speech-recognition.js'); 23var talkifyFormReader = require('./src/talkify-formreader.js'); 24var talkifyTableReader = require('./src/table-reader/talkify-tablereader.js'); 25var talkifyTestSelectionActivator = require('./src/talkify-text-selection-activator.js'); 26var talkifyAutoScroll = require('./src/talkify-autoscroll.js'); 27},{"./src/control-centers/talkify-controlcenter-classic.js":2,"./src/control-centers/talkify-controlcenter-core.js":3,"./src/control-centers/talkify-controlcenter-local.js":4,"./src/control-centers/talkify-controlcenter-modern.js":5,"./src/table-reader/talkify-tablereader.js":6,"./src/talkify-ajax.js":7,"./src/talkify-autoscroll.js":8,"./src/talkify-config.js":9,"./src/talkify-formreader.js":10,"./src/talkify-html5-speechsynthesis-player.js":11,"./src/talkify-keyboard-commands.js":12,"./src/talkify-messagehub.js":13,"./src/talkify-player-core.js":14,"./src/talkify-player.js":15,"./src/talkify-playlist.js":16,"./src/talkify-speech-recognition.js":17,"./src/talkify-text-selection-activator.js":18,"./src/talkify-textextractor.js":19,"./src/talkify-utils.js":20,"./src/talkify-word-highlighter.js":21,"./src/talkify.js":22}],2:[function(require,module,exports){ 28talkify = talkify || {}; 29talkify.controlcenters = talkify.controlcenters || {}; 30 31talkify.controlcenters.classic = function () { 32 this.html = '<div class="talkify-control-center classic attached">\ 33 <ul> \ 34 <li class="talkify-drag-area talkify-detached"> \ 35 <i class="fa fa-grip-horizontal"></i> \ 36 </li> \ 37 <li> \ 38 <button class="talkify-play-button talkify-disabled" title="Play"> \ 39 <i class="fas fa-play-circle"></i> \ 40 </button> \ 41 <button class="talkify-pause-button talkify-disabled" title="Pause"> \ 42 <i class="fas fa-pause-circle"></i> \ 43 </button> \ 44 <i class="fa fa-circle-notch fa-spin talkify-audio-loading"></i> \ 45 <i class="fas fa-exclamation-triangle talkify-audio-error" title="An error occurred at playback"></i> \ 46 </li> \ 47 <li class="progress-wrapper"> \ 48 <span class="talkify-time-elapsed-element">00:00</span> \ 49 <progress value="0.0" max="1.0"></progress> \ 50 <span class="talkify-time-duration-element">00:00</span> \ 51 </li> \ 52 <li> \ 53 <button class="talkify-volume-button" title="Volume"> \ 54 <i class="fa fa-volume-up"></i> \ 55 <div class="volume-slider"> \ 56 <input type="range" value="10" min="0" max="10" title="Adjust playback volume"> \ 57 </div> \ 58 </button></li> \ 59 </ul></div>'; 60 61} 62},{}],3:[function(require,module,exports){ 63talkify = talkify || {}; 64talkify.playbar = function (parent, correlationId, controlcenter) { 65 var mainFlags = ["de-DE", "fr-FR", "en-US", "zh-CN", "es-ES", "it-IT", "ja-JP", "ko-KR", "sv-SE", "nb-NO", "da-DK", "ru-RU", "nl-NL", "pl-PL", "tr-TR", "is-IS", "uk-UA", "sk-SK", "pt-PT", "ro-RO", "cy-GB", "bg-BG", "cs-CZ", "el-GR", "fi-FI", "he-IL", "hi-IN", "hr-HR", "hu-HU", "id-ID", "ms-MY", "sl-SI", "th-TH", "vi-VN", "ar-EG", "ar-SA", "ta-IN", "te-IN", "en-GB-WLS", "ca-ES", "gu-IN", "ml-IN", "bn-IN", "kn-IN", "fil-PH"]; 66 67 var settings = {
68 parentElement: parent || talkify.config.ui.audioControls.container || document.body, 69 controlCenterName: controlcenter || talkify.config.ui.audioControls.controlcenter 70 } 71 72 var playElement, pauseElement, rateElement, volumeElement, progressElement, voiceElement, elapsedElement, durationElement, textHighlightingElement, wrapper, voicePicker; 73 var attachElement, detatchedElement, dragArea, loader, erroroccurredElement, textInteractionElement, pitchElement, wordBreakElement, wordBreakElementWrapper; 74 var pitchElementWrapper, nextItemElement, previousItemElement, voiceNameElement, enhancedVisibilityElement; 75 var flagElement, phonationNormalElement, phonationSoftElement, phonationWhisperElement, phonationDropDown, downloadElement, downloadLoadingElement, downloadErrorElement; 76 var voices = []; 77 78 var noopElement = document.createElement("div"); 79 playElement = pitchElementWrapper = nextItemElement = enhancedVisibilityElement = voiceNameElement = previousItemElement = phonationNormalElement = phonationSoftElement = phonationWhisperElement = phonationDropDown = flagElement = wordBreakElement = wordBreakElementWrapper = pitchElement = dragArea = textInteractionElement = voiceWrapperElement = attachElement = elapsedElement = durationElement = detatchedElement = pauseElement = loader = erroroccurredElement = progressElement = textHighlightingElement = noopElement; 80 rateElement = volumeElement = []; 81 82 function hide(element) { 83 if (!element) { 84 return; 85 } 86 87 var arr = Array.isArray(element) ? 88 element : 89 NodeList.prototype.isPrototypeOf(element) ? 90 Array.from(element) : 91 [element]; 92 93 arr.forEach(function (x) { 94 if (x.classList.contains("talkify-hidden")) { 95 return; 96 } 97 98 x.className += " talkify-hidden"; 99 }); 100 } 101 102 function show(element) { 103 if (!element) { 104 return; 105 } 106 107 var arr = Array.isArray(element) ? 108 element : 109 NodeList.prototype.isPrototypeOf(element) ? 110 Array.from(element) : 111 [element]; 112 113 arr.forEach(function (x) { 114 x.className = x.className.replace("talkify-hidden", ""); 115 }); 116 } 117 118 function play() { 119 hide(loader); 120 hide(playElement); 121 hide(erroroccurredElement); 122 show(pauseElement); 123 } 124 125 function pause() { 126 hide(loader); 127 hide(pauseElement); 128 hide(erroroccurredElement); 129 show(playElement); 130 } 131 132 function onError() { 133 hide(loader); 134 hide(pauseElement); 135 hide(playElement); 136 show(erroroccurredElement); 137 } 138 139 function addClass(element, c) { 140 var arr = Array.isArray(element) ? 141 element : 142 NodeList.prototype.isPrototypeOf(element) ? 143 Array.from(element) : 144 [element]; 145 146 arr.forEach(function (x) { 147 if (x.classList.contains(c)) { 148 return; 149 } 150 151 x.className += (" " + c); 152 }); 153 } 154 155 function removeClass(element, c) { 156 var arr = Array.isArray(element) ? 157 element : 158 NodeList.prototype.isPrototypeOf(element) ? 159 Array.from(element) : 160 [element]; 161 162 arr.forEach(function (x) { 163 x.className = x.className.replace(c, ""); 164 }); 165 } 166 167 function createElement(type, classes) { 168 var element = document.createElement(type); 169 170 element.className = classes; 171 172 return element; 173 } 174 175 var groupBy = function (xs, keyFn) { 176 return xs.reduce(function (rv, x) { 177 var key = keyFn(x); 178 179 (rv[key] = rv[key] || []).push(x); 180 return rv; 181 }, {}); 182 }; 183 184 function createVoicePicker(voices, remoteVoices) { 185 var mainUl = createElement("ul", "voice-selector"); 186 187 if (!voices.length) { 188 return mainUl; 189 } 190 191 var byLanguage = groupBy(voices, function (v) { 192 return remoteVoices ? v.language : v.lang; 193 }); 194 195 for (var prop in byLanguage) { 196 if (!byLanguage.hasOwnProperty(prop)) { 197 continue; 198 } 199 200 var sortedVoices = byLanguage[prop].sort(function (a, b) { 201 return remoteVoices ? (a.culture - b.culture) : (a.name - b.name); 202 }); 203 204 var defaultVoice = remoteVoices ? 205 sortedVoices.filter(function (x) { return x.isStandard; })[0] : 206 (sortedVoices.filter(function (x) { return x.localService; }
206)[0] || sortedVoices[0]); 207 208 var foo = sortedVoices.find(function (v) { 209 return mainFlags.indexOf(v.culture || v.lang) !== -1; 210 }); 211 212 if (!foo) { 213 continue; 214 } 215 216 var mainCulture = foo.culture || foo.lang; 217 218 var mainFlag = "https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.3.0/flags/4x3/" + mainCulture.split("-")[1].toLowerCase() + ".svg"; 219 220 var li = createElement("li"); 221 222 var flagImg = createElement("img", "talkify-flag"); 223 flagImg.src = mainFlag; 224 225 var label = createElement("label", "talkify-clickable"); 226 label.innerHTML = sortedVoices[0].language || sortedVoices[0].lang; 227 label.htmlFor = "chk_" + prop; 228 229 var checkbox = createElement("input", ""); 230 checkbox.id = "chk_" + prop; 231 checkbox.type = "checkbox"; 232 checkbox.style = "display: none"; 233 234 li.appendChild(flagImg); 235 li.appendChild(label); 236 li.appendChild(checkbox); 237 238 var innerUl = createElement("ul", "language"); 239 240 li.appendChild(innerUl); 241 242 for (var j = 0; j < sortedVoices.length; j++) { 243 var voice = sortedVoices[j]; 244 var isDefault = !!defaultVoice && voice === defaultVoice; 245 246 var innerLi = createElement("li", "talkify-clickable"); 247 innerLi.setAttribute("data-default", isDefault); 248 innerLi.setAttribute("data-voice", remoteVoices ? JSON.stringify(voice) : voice.name); 249 innerLi.setAttribute("data-voice-name", voice.name); 250 251 var d = createElement("div", ""); 252 253 if (voice.isNeural) { 254 var i = createElement("i", "fas fa-network-wired"); 255 i.setAttribute("title", "Neural voice"); 256 } 257 else if (voice.isExclusive) { 258 var i = createElement("i", "fas fa-star"); 259 i.setAttribute("title", "Exclusive voice"); 260 } 261 else if (voice.isPremium) { 262 var i = createElement("i", "far fa-star"); 263 i.setAttribute("title", "Premium voice"); 264 } else { 265 var i = createElement("i", "far fa-check-circle"); 266 i.setAttribute("title", "Standard voice"); 267 } 268 269 var span = createElement("span"); 270 span.innerHTML = voice.name; 271 272 d.appendChild(i); 273 d.appendChild(span); 274 275 var flagDiv = createElement("div"); 276 var svg = "https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.3.0/flags/4x3/" + (voice.culture || voice.lang).split("-")[1].toLowerCase() + ".svg"; 277 278 var svgImg = createElement("img", "talkify-flag"); 279 svgImg.src = svg; 280 281 flagDiv.appendChild(svgImg); 282 283 innerLi.appendChild(d); 284 innerLi.appendChild(flagDiv); 285 innerUl.appendChild(innerLi); 286 } 287 288 mainUl.appendChild(li); 289 } 290 291 return mainUl; 292 } 293 294 function render() { 295 var existingControl = document.getElementsByClassName("talkify-control-center")[0]; 296 297 if (existingControl) { 298 existingControl.parentNode.removeChild(existingControl); 299 } 300 301 var controlcenter = new talkify.controlcenters[settings.controlCenterName](); 302 303 var div = document.createElement('div'); 304 div.innerHTML = controlcenter.html.trim(); 305 306 wrapper = div.firstChild; 307 308 settings.parentElement.appendChild(wrapper); 309 310 playElement = wrapper.getElementsByClassName("talkify-play-button")[0] || noopElement; 311 pauseElement = wrapper.getElementsByClassName("talkify-pause-button")[0] || noopElement; 312 loader = wrapper.getElementsByClassName("talkify-audio-loading")[0] || noopElement; 313 erroroccurredElement = wrapper.getElementsByClassName("talkify-audio-error")[0] || noopElement; 314 rateElement = wrapper.querySelectorAll(".talkify-rate-button input[type=range]") || []; 315 volumeElement = wrapper.querySelectorAll(".talkify-volume-button input[type=range]") || []; 316 progressElement = wrapper.getElementsByTagName("progress")[0] || noopElement; 317 textHighlightingElement = wrapper.getElementsByClassName("talkify-cc-button")[0] || noopElement; 318 durationElement = wrapper.getElementsByClassName("talkify-time-duration-element")[0] || noopElement; 319 elapsedElement = wrapper.getElementsByClassName("talkify-time-elapsed-element")[0] || noopElement; 320 detatchedElement = wrapper.getElementsByClassName("talkify-detatch-handle")[0] || noopElement; 321 attachElement = wrapper.getElementsByClassName("talkify-attach-handle")[0] || noopElement; 322 voiceWrapperElement = wrapper.querySelector(".talkify-voice-selector") || noopElement; 323 dragArea = wrapper.getElementsByClassName("talkify-drag-area")[0] || noopElement; 324 textInteractionElement = wrapper.getElementsByClassName("talkify-text-interaction-button")[0] || noopElement; 325 pitchElementWrapper = wrapper.querySelector(".talkify-pitch-element") || noopElement; 326 pitchElement = pitchElementWrapper.getElementsByTagName("input")[0] || noopElement; 327 wordBreakElementWrapper = wrapper.querySelector(".talkify-wordbreak-element") || noopElement; 328 wordBreakElement = wordBreakElementWrapper.getElementsByTagName("input")[0] || noopElement; 329 flagElement = wrapper.getElementsByClassName("talkify-selected-voice-flag")[0] || noopElement; 330 phonationDropDown = wrapper.querySelector(".talkify-phonation-element") || noopElement; 331 phonationWhisperElement = wrapper.querySelector("#talkify-phonation-whisper") || noopElement; 332 phonationSoftElement = wrapper.querySelector("#talkify-phonation-soft") || noopElement; 333 phonationNormalElement = wrapper.querySelector("#talkify-phonation-normal") || noopElement; 334 nextItemElement = wrapper.getElementsByClassName("talkify-step-forward-button")[0] || noopElement; 335 previousItemElement = wrapper.getElementsByClassName("talkify-step-backward-button")[0] || noopElement; 336 voiceNameElement = document.querySelector(".talkify-voice-selector span") || noopElement; 337 enhancedVisibilityElement = document.querySelector(".talkify-enhanced-visibility-button") || noopElement; 338 downloadElement = document.querySelectorAll('.talkify-download-button') || []; 339 downloadLoadingElement = document.querySelectorAll('.talkify-download-loading') || []; 340 downloadErrorElement = document.querySelectorAll('.talkify-download-error') || []; 341 342 settings.parentElement.appendChild(wrapper); 343 344 talkify.messageHub.publish(correlationId + ".controlcenter.attached", wrapper.getBoundingClientRect()); 345 346 hide(loader); 347 hide(downloadLoadingElement); 348 hide(downloadErrorElement); 349 addClass(downloadElement, "talkify-disabled"); 350
351 if (!talkify.config.ui.audioControls.downloadEnabled) { 352 hide(downloadElement); 353 354 downloadElement = downloadErrorElement = downloadLoadingElement = noopElement; 355 } 356 357 pause(); 358 } 359 360 function setupBindings() { 361 var controlCenter = document.getElementsByClassName("talkify-control-center")[0]; 362 363 playElement.addEventListener("click", function () { 364 if (playElement.classList.contains("talkify-disabled")) { 365 return; 366 } 367 368 talkify.messageHub.publish(correlationId + ".controlcenter.request.play"); 369 }); 370 371 pauseElement.addEventListener("click", function () { 372 if (pauseElement.classList.contains("talkify-disabled")) { 373 return; 374 } 375 talkify.messageHub.publish(correlationId + ".controlcenter.request.pause"); 376 }); 377 378 rateElement.forEach(function (x) { 379 x.addEventListener("change", function () { 380 talkify.messageHub.publish(correlationId + ".controlcenter.request.rate", parseFloat(this.value)); 381 }) 382 }); 383 384 volumeElement.forEach(function (x) { 385 x.addEventListener("change", function (e) { 386 talkify.messageHub.publish(correlationId + ".controlcenter.request.volume", parseInt(this.value)); 387 }); 388 }); 389 390 pitchElement.addEventListener("change", function () { 391 talkify.messageHub.publish(correlationId + ".controlcenter.request.pitch", parseInt(this.value)); 392 }); 393 394 wordBreakElement.addEventListener("change", function () { 395 talkify.messageHub.publish(correlationId + ".controlcenter.request.wordbreak", parseInt(this.value)); 396 }); 397 398 phonationDropDown.addEventListener("change", function () { 399 if (this.value === "normal") { 400 talkify.messageHub.publish(correlationId + ".controlcenter.request.phonation.normal"); 401 } else if (this.value === "soft") { 402 talkify.messageHub.publish(correlationId + ".controlcenter.request.phonation.soft"); 403 } else if (this.value === "whisper") { 404 talkify.messageHub.publish(correlationId + ".controlcenter.request.phonation.whisper"); 405 } 406 }); 407 408 phonationWhisperElement.addEventListener("click", function () { 409 talkify.messageHub.publish(correlationId + ".controlcenter.request.phonation.whisper"); 410 }); 411 412 phonationSoftElement.addEventListener("click", function () { 413 talkify.messageHub.publish(correlationId + ".controlcenter.request.phonation.soft"); 414 }); 415 416 phonationNormalElement.addEventListener("click", function () { 417 talkify.messageHub.publish(correlationId + ".controlcenter.request.phonation.normal"); 418 }); 419 420 textHighlightingElement.addEventListener("click", function (e) { 421 if (textHighlightingElement.classList.contains("talkify-disabled")) { 422 removeClass(textHighlightingElement, "talkify-disabled"); 423 talkify.messageHub.publish(correlationId + ".controlcenter.texthighlightoggled", true); 424 } else { 425 addClass(textHighlightingElement, "talkify-disabled"); 426 talkify.messageHub.publish(correlationId + ".controlcenter.texthighlightoggled", false); 427 } 428 }); 429 430 textInteractionElement.addEventListener("click", function (e) { 431 if (textInteractionElement.classList.contains("talkify-disabled")) { 432 talkify.messageHub.publish(correlationId + ".controlcenter.request.textinteractiontoggled", true); 433 } else { 434 talkify.messageHub.publish(correlationId + ".controlcenter.request.textinteractiontoggled", false); 435 } 436 }); 437 438 enhancedVisibilityElement.addEventListener("click", function (e) { 439 if (enhancedVisibilityElement.classList.contains("talkify-disabled")) { 440 talkify.messageHub.publish(correlationId + ".controlcenter.request.enhancedvisibility", true); 441 } else { 442 talkify.messageHub.publish(correlationId + ".controlcenter.request.enhancedvisibility", false); 443 } 444 }); 445 446 nextItemElement.addEventListener("click", function () { 447 if (nextItemElement.classList.contains("talkify-disabled")) { 448 return; 449 } 450 451 talkify.messageHub.publish(correlationId + ".controlcenter.request.playnext"); 452 }); 453 454 previousItemElement.addEventListener("click", function () { 455 if (previousItemElement.classList.contains("talkify-disabled")) { 456 return; 457 } 458 459 talkify.messageHub.publish(correlationId + ".controlcenter.request.playprevious"); 460 }); 461 462 progressElement.addEventListener("click", function (e) { 463 var clickedValue = (e.offsetX * this.max) / this.offsetWidth; 464 465 if (clickedValue > 1.0) { 466 clickedValue = 1.0; 467 } 468 469 if (clickedValue < 0.0) { 470 clickedValue = 0.0; 471 } 472 473 talkify.messageHub.publish(correlationId + ".controlcenter.request.seek", clickedValue); 474 }); 475 476 attachElement.addEventListener("click", function () { 477 addClass(controlCenter, "attached"); 478 removeClass(controlCenter, "detached"); 479 480 talkify.messageHub.publish(correlationId + ".controlcenter.attached", controlCenter.getBoundingClientRect()); 481 }); 482 483 detatchedElement.addEventListener("click", function () { 484 removeClass(controlCenter, "attached"); 485 addClass(controlCenter, "detached"); 486 487 talkify.messageHub.publish(correlationId + ".controlcenter.detatched", controlCenter.getBoundingClientRect()); 488 }); 489 490 dragArea.addEventListener("mousedown", onMouseDown); 491 document.addEventListener("mouseup", onMouseUp); 492
493 if (talkify.config.ui.audioControls.downloadEnabled) { 494 downloadElement.forEach(function (x) { 495 x.addEventListener("click", function () { 496 talkify.messageHub.publish(correlationId + ".controlcenter.request.download"); 497 }); 498 }); 499 } 500 501 function onMouseUp(e) { 502 document.removeEventListener("mousemove", onMouseMove); 503 } 504 505 function onMouseDown(e) { 506 document.addEventListener("mousemove", onMouseMove); 507 } 508 509 function onMouseMove(e) { 510 e.preventDefault(); 511 512 var dArea = dragArea.getBoundingClientRect(); 513 var leftOffset = (dArea.width / 2) + dArea.x - controlCenter.getBoundingClientRect().x; 514 515 controlCenter.style.top = e.clientY - (controlCenter.offsetHeight / 2) + "px"; 516 controlCenter.style.left = e.clientX - leftOffset + "px"; 517 } 518 } 519 520 function initialize() { 521 if (settings.controlCenterName === "native") { 522 return; 523 } 524 525 render(); 526 setupBindings(); 527 528 talkify.messageHub.subscribe("controlcenter", [correlationId + ".player.*.pause", correlationId + ".player.*.disposed", correlationId + ".player.html5.ended"], pause); 529 talkify.messageHub.subscribe("controlcenter", [correlationId + ".player.*.play", correlationId + ".player.*.resume"], play); 530 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.*.disposed", dispose); 531 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.*.loading", function () { 532 hide(playElement); 533 hide(pauseElement); 534 hide(erroroccurredElement); 535 show(loader); 536 }); 537 538 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.*.loaded", function () { 539 removeClass(pauseElement, "talkify-disabled"); 540 removeClass(playElement, "talkify-disabled"); 541 show(playElement); 542 hide(loader); 543 }); 544 545 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.*.texthighlight.enabled", function () { 546 removeClass(textHighlightingElement, "talkify-disabled"); 547 }); 548 549 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.*.texthighlight.disabled", function () { 550 addClass(textHighlightingElement, "talkify-disabled"); 551 }); 552 553 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.*.ratechanged", function (rate) { 554 rateElement.forEach(function (x) { 555 x.value = rate; 556 }); 557 }); 558 559 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.*.voiceset", function (voice) { 560 featureToggle(voice); 561 setVoiceName(voice); 562 }); 563 564 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.tts.error", onError); 565 566 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.tts.timeupdated", updateClock); 567 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.html5.timeupdated", function (value) { 568 progressElement.setAttribute("value", value); 569 }); 570 571 talkify.messageHub.subscribe("controlcenter", correlationId + ".playlist.loaded", function () { 572 removeClass(playElement, "talkify-disabled"); 573 removeClass(downloadElement, "talkify-disabled"); 574 }); 575 576 talkify.messageHub.subscribe("controlcenter", correlationId + ".playlist.textinteraction.enabled", function () { 577 removeClass(textInteractionElement, "talkify-disabled"); 578 }); 579 580 talkify.messageHub.subscribe("controlcenter", correlationId + ".playlist.textinteraction.disabled", function () { 581 addClass(textInteractionElement, "talkify-disabled"); 582 }); 583 584 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.tts.download.started", function () { 585 hide(downloadElement); 586 show(downloadLoadingElement); 587 }); 588 589 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.tts.download.completed", function () { 590 show(downloadElement); 591 hide(downloadLoadingElement); 592 }); 593 594 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.tts.download.error", function (obj) { 595 hide(downloadLoadingElement); 596 show(downloadErrorElement); 597 598 downloadErrorElement.title = obj.statusText; 599 }); 600 601 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.tts.wordbreakchanged", function (ms) { 602 wordBreakElement.value = ms; 603 }); 604 605 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.*.pitchchanged", function (value) { 606 pitchElement.value = value; 607 }); 608 609 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.tts.phonationchanged", function (phonation) { 610 phonationDropDown.value = phonation === "soft" ? "soft" : "normal"; 611 }); 612 613 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.tts.whisperchanged", function (whisper) { 614 phonationDropDown.value = whisper ? "whisper" : "normal"; 615 }); 616 617 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.tts.created", function () { 618 getRemoteVoices(); 619 }); 620 621 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.html5.created", function () {
622 if (!talkify.config.ui.audioControls.voicepicker.enabled) { 623 return; 624 } 625 626 getLocalVoices(); 627 }); 628 629 talkify.messageHub.subscribe("controlcenter", correlationId + ".player.*.enhancedvisibilityset", function (value) { 630 if (value) { 631 removeClass(enhancedVisibilityElement, "talkify-disabled"); 632 } else { 633 addClass(enhancedVisibilityElement, "talkify-disabled"); 634 } 635 }); 636 637 638 talkify.messageHub.subscribe("controlcenter", correlationId + ".playlist.playing", function (msg) { 639 removeClass(nextItemElement, "talkify-disabled"); 640 removeClass(previousItemElement, "talkify-disabled"); 641 642 if (msg.isLast) { 643 addClass(nextItemElement, "talkify-disabled"); 644 } 645 646 if (msg.isFirst) { 647 addClass(previousItemElement, "talkify-disabled"); 648 } 649 }); 650 651 652 }; 653 654 function getLocalVoices() { 655 window.speechSynthesis.getVoices(); 656 setTimeout(function () { 657 voices = window.speechSynthesis.getVoices(); 658 voicePicker = createVoicePicker(filterVoicesByConfig(voices), false); 659 660 wrapper.getElementsByClassName("talkify-voice-selector")[0].appendChild(voicePicker); 661 662 voicePicker.querySelectorAll(".language > li").forEach(function (item) { 663 item.addEventListener('click', function (e) { 664 var voice = voices.find(function (v) { return v.name === e.currentTarget.dataset.voice }); 665 666 talkify.messageHub.publish(correlationId + ".controlcenter.request.setvoice", voice); 667 }); 668 }); 669 670 if (voiceNameElement.textContent) { 671 var backendVoice = voices.find(function (v) { 672 return v.name === voiceNameElement.textContent; 673 }); 674 675 if (backendVoice) { 676 featureToggle(backendVoice); 677 setVoiceName(backendVoice); 678 } 679 } 680 }, 100); 681 } 682 683 function getRemoteVoices() { 684 talkify.http.get(talkify.config.remoteService.speechBaseUrl + "/voices") 685 .then(function (data) { 686 voices = window.talkify.toLowerCaseKeys(data); 687 688 if (voiceNameElement.textContent) { 689 var backendVoice = voices.find(function (v) { 690 return v.name === voiceNameElement.textContent; 691 }); 692 693 if (backendVoice) { 694 featureToggle(backendVoice); 695 setVoiceName(backendVoice); 696 } 697 } 698 699 if (!talkify.config.ui.audioControls.voicepicker.enabled) { 700 return; 701 } 702 703 voicePicker = createVoicePicker(filterVoicesByConfig(voices), true); 704 705 wrapper.getElementsByClassName("talkify-voice-selector")[0].appendChild(voicePicker); 706 707 voicePicker.querySelectorAll(".language > li").forEach(function (item) { 708 item.addEventListener('click', function (e) { 709 var voice = JSON.parse(e.currentTarget.dataset.voice); 710 711 talkify.messageHub.publish(correlationId + ".controlcenter.request.setvoice", window.talkify.toLowerCaseKeys(voice)); 712 }); 713 }); 714 }); 715 } 716 717 function filterVoicesByConfig(voices) { 718 var filter = talkify.config.ui.audioControls.voicepicker.filter; 719 720 if (!filter) { 721 return voices; 722 } 723 724 return voices.filter(function (voice) { 725 var active = true; 726 727 if (filter.byCulture.length) { 728 active = filter.byCulture.indexOf(voice.culture || voice.lang) !== -1; 729 } 730 731 if (active && filter.byLanguage.length) { 732 active = filter.byLanguage.indexOf(voice.language) !== -1; 733 } 734 735 if (active && filter.byClass.length) { 736 if (filter.byClass.indexOf("Standard") !== -1 && voice.isStandard) { 737 return true; 738 } 739 740 if (filter.byClass.indexOf("Premium") !== -1 && voice.isPremium) { 741 return true; 742 } 743 744 if (filter.byClass.indexOf("Exclusive") !== -1 && voice.isExclusive) { 745 return true; 746 } 747 748 if (filter.byClass.indexOf("Neural") !== -1 && voice.isNeural) { 749 return true; 750 } 751 752 return false;
753 } 754 755 return active; 756 }); 757 } 758 759 function updateClock(timeInfo) { 760 var currentTime = timeInfo.currentTime; 761 var duration = timeInfo.duration; 762 //TODO: Over tunnels duration === NaN. Look @ http://stackoverflow.com/questions/10868249/html5-audio-player-duration-showing-nan 763 progressElement.setAttribute("value", currentTime / duration); 764 765 if (!elapsedElement || !durationElement) { 766 return; 767 } 768 769 var currentminutes = Math.floor(currentTime / 60); 770 var currentseconds = Math.round(currentTime) - (currentminutes * 60); 771 772 var totalminutes = !!duration ? Math.floor(duration / 60) : 0; 773 var totalseconds = !!duration ? Math.round(duration) - (totalminutes * 60) : 0; 774 775 elapsedElement.textContent = ((currentminutes < 10) ? "0" + currentminutes : currentminutes) + ":" + ((currentseconds < 10) ? "0" + currentseconds : currentseconds); 776 durationElement.textContent = ((totalminutes < 10) ? "0" + totalminutes : totalminutes) + ":" + ((totalseconds < 10) ? "0" + totalseconds : totalseconds); 777 } 778 779 function isTalkifyHostedVoice(voice) { 780 return voice && voice.constructor.name !== "SpeechSynthesisVoice"; 781 } 782 783 function featureToggle(voice) { 784 show(progressElement); 785 show(textHighlightingElement); 786 787 if (!voice) { 788 return; 789 } 790 791 var backendVoice = voices.find(function (v) { 792 return v.name === voice.name; 793 }); 794 795 if (backendVoice) { 796 voice = backendVoice; 797 } 798 799 voice.canUseWordBreak ? show(wordBreakElementWrapper) : hide(wordBreakElementWrapper); 800 801 voice.canWhisper ? show(phonationWhisperElement) : hide(phonationWhisperElement); 802 voice.canSpeakSoftly ? show(phonationSoftElement) : hide(phonationSoftElement); 803 804 if (voice.canSpeakSoftly) { 805 806 } 807 808 if (isTalkifyHostedVoice(voice)) { 809 voice.canUsePitch ? show(pitchElementWrapper) : hide(pitchElementWrapper); 810 return; 811 } 812 813 show(pitchElementWrapper); 814 hide(elapsedElement); 815 hide(durationElement); 816 817 if (!voice.localService) { 818 hide(progressElement); 819 hide(textHighlightingElement); 820 } 821 } 822 823 function setVoiceName(voice) { 824 if (!voice) { 825 voiceNameElement.textContent = "Automatic voice detection"; 826 return; 827 } 828 829 var backendVoice = voices.find(function (v) { 830 return v.name === voice.name; 831 }); 832 833 if (backendVoice) { 834 voice = backendVoice; 835 } 836 837 var split = (voice.culture || voice.lang || "").split("-"); 838 839 if (split.length > 1) { 840 flagElement.src = "https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.3.0/flags/4x3/" + split[1].toLowerCase() + ".svg"; 841 show(flagElement); 842 } else { 843 hide(flagElement); 844 } 845 846 voiceNameElement.textContent = voice.name; 847 } 848 849 function dispose() { 850 var existingControl = document.getElementsByClassName("talkify-control-center")[0]; 851 852 if (existingControl) { 853 existingControl.parentNode.removeChild(existingControl); 854 } 855 856 talkify.messageHub.unsubscribe("controlcenter", [correlationId + ".player.*.pause", correlationId + ".player.*.disposed"]); 857 talkify.messageHub.unsubscribe("controlcenter", [correlationId + ".player.*.play", correlationId + ".player.*.resume"]); 858 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.*.disposed"); 859 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.*.loaded"); 860 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.*.loading"); 861 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.*.texthighlight.enabled"); 862 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.*.texthighlight.disabled"); 863 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.*.ratechanged"); 864 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.*.voiceset"); 865 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.tts.timeupdated"); 866 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.html5.timeupdated"); 867 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".playlist.loaded"); 868 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.tts.error"); 869 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".playlist.textinteraction.enabled"); 870 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".playlist.textinteraction.disabled"); 871 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.tts.wordbreakchanged"); 872 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.*.pitchchanged"); 873 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.tts.phonationchanged"); 874 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.tts.whisperchanged"); 875 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".playlist.playing"); 876 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.html5.ended"); 877 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.tts.created"); 878 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.html5.created"); 879 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.*.enhancedvisibilityset"); 880 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.tts.download.error"); 881 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.tts.download.started"); 882 talkify.messageHub.unsubscribe("controlcenter", correlationId + ".player.tts.download.completed"); 883 } 884 885 initialize(); 886 887 return {
888 setMaxRate: function (value) { 889 rateElement.forEach(function (x) { 890 x.setAttribute("max", value); 891 }); 892 893 return this; 894 }, 895 setMinRate: function (value) { 896 rateElement.forEach(function (x) { 897 x.setAttribute("min", value); 898 }); 899 900 return this; 901 }, 902 setRateStep: function (value) { 903 rateElement.forEach(function (x) { 904 x.setAttribute("step", value); 905 }); 906 907 return this; 908 }, 909 setMaxPitch: function (value) { 910 pitchElement.setAttribute("max", value); 911 912 return this; 913 }, 914 setMinPitch: function (value) { 915 pitchElement.setAttribute("min", value); 916 917 return this; 918 }, 919 setPitchStep: function (value) { 920 pitchElement.setAttribute("step", value); 921 922 return this; 923 }, 924 dispose: dispose 925 } 926} 927},{}],4:[function(require,module,exports){ 928talkify = talkify || {}; 929talkify.controlcenters = talkify.controlcenters || {}; 930 931talkify.controlcenters.local = function () { 932 this.html = 933 '<div class="talkify-control-center local">\ 934 <div>\ 935 <div class="talkify-brand">T</i></div>\ 936 <button class="talkify-step-backward-button" title="Previous">\ 937 <i class="fa fa-step-backward"></i>\ 938 </button>\ 939 <button class="talkify-play-button" title="Listen to text">\ 940 <i class="fas fa-play"></i>\ 941 </button>\ 942 <div class="talkify-audio-loading">\ 943 <i class="fas fa-dharmachakra fa-spin"></i>\ 944 </div>\ 945 <button class="talkify-pause-button" title="Pause">\ 946 <i class="fas fa-pause"></i>\ 947 </button>\ 948 <button class="talkify-audio-error" title="An error occurred at playback"><i class="fas fa-exclamation-triangle"></i></button>\ 949 <button class="talkify-step-forward-button" title="Next">\ 950 <i class="fa fa-step-forward"></i>\ 951 </button>\ 952 <button class="talkify-cc-button" title="Toggle text highlighting">\ 953 <i class="fa fa-closed-captioning"></i>\ 954 </button>\ 955 <button class="talkify-text-interaction-button" title="Toggle text interaction">\ 956 <i class="fas fa-hand-point-up"></i>\ 957 </button>\ 958 <button class="talkify-enhanced-visibility-button" title="Toggle enhanced visibility">\ 959 <i class="fas fa-eye"></i>\ 960 </button>\ 961 <button class="talkify-download-button" title="Download">\ 962 <i class="fas fa-download"></i>\ 963 </button>\ 964 <div class="talkify-download-loading">\ 965 <i class="fas fa-dharmachakra fa-spin"></i>\ 966 </div>\ 967 <div class="talkify-download-error">\ 968 <i class="fas fa-exclamation-triangle"></i>\ 969 </div>\ 970 <button title="More settings">\ 971 <label for="talkify-local-settings"><i class="fas fa-cog"></i></label>\ 972 </button>\ 973 </div>\ 974 <input type="checkbox" style="display:none;" id="talkify-local-settings"/>\ 975 <div class="talkify-columns talkify-more-settings">\ 976 <div title="Adjust playback volume">\ 977 <i class="fas fa-volume-up"></i>\ 978 <div class="volume-slider talkify-volume-button">\ 979 <input type="range" value="10" min="0" max="10">\ 980 </div>\ 981 </div>\ 982 <div title="Adjust playback rate">\ 983 <i class="fa fa-tachometer-alt"></i>\ 984 <div class="rate-slider talkify-rate-button">\ 985 <input type="range" value="5" min="0" max="10">\ 986 </div>\ 987 </div>\ 988 </div>\ 989 </div>'; 990}; 991},{}],5:[function(require,module,exports){ 992talkify = talkify || {}; 993talkify.controlcenters = talkify.controlcenters || {}; 994 995talkify.controlcenters.modern = function (parent, correlationId) { 996 997 this.html = '<div class="talkify-control-center modern attached"> \ 998 <progress value="0" max="1.0"></progress>\
999 <ul class="player-controls">\ 1000 <li style="display: flex; justify-content: center;">\ 1001 <button data-type="drag" class="talkify-detached talkify-drag-area" title="Drag player">\ 1002 <i class="fa fa-grip-horizontal"></i>\ 1003 </button>\ 1004 <button class="talkify-attached talkify-detatch-handle" title="Detatch player">\ 1005 <i class="fa fa-window-maximize"></i>\ 1006 </button>\ 1007 <div class="player-settings">\ 1008 <button>\ 1009 <label for="player-settings-toggle" title="More settings">\ 1010 <i class="fas fa-ellipsis-h"></i>\ 1011 </label>\ 1012 </button>\ 1013 <input type="checkbox" id="player-settings-toggle" style="display: none;" />\ 1014 <ul>\ 1015 <li>\ 1016 <button class="modern-talkify-control-center-accent" title="Playback mode">\ 1017 <i class="fas fa-music"></i>\ 1018 </button>\ 1019 <select class="talkify-phonation-element">\ 1020 <option id="talkify-phonation-normal" value="normal">Normal tone</option>\ 1021 <option id="talkify-phonation-soft" value="soft">Softer tone</option>\ 1022 <option id="talkify-phonation-whisper" value="whisper">Whisper</option>\ 1023 </select>\ 1024 </li>\ 1025 <li class="talkify-wordbreak-element">\ 1026 <button class="modern-talkify-control-center-accent" title="Word break">\ 1027 <i class="fas fa-arrows-alt-h"></i>\ 1028 </button>\ 1029 <div>\ 1030 <input type="range" value="0" min="0" max="3000" title="Adjust pause between words">\ 1031 </div>\ 1032 </li>\ 1033 <li class="talkify-pitch-element">\ 1034 <button class="modern-talkify-control-center-accent" title="Adjust pitch">\ 1035 <i class="fas fa-arrows-alt-h pitch-icon"></i>\ 1036 </button>\ 1037 <div>\ 1038 <input type="range" value="0" min="-7" max="7" title="Adjust pitch">\ 1039 </div>\ 1040 </li>\ 1041 <li class="talkify-cc-button">\ 1042 <button class="modern-talkify-control-center-accent"\ 1043 title="Toggle text highlighting">\ 1044 <i class="fa fa-closed-captioning"></i>\ 1045 </button>\ 1046 <div>Text highlighting</div>\ 1047 </li>\ 1048 <li class="talkify-text-interaction-button">\ 1049 <button class="modern-talkify-control-center-accent" title="Toggle text interaction">\ 1050 <i class="fas fa-hand-point-up"></i>\ 1051 </button>\ 1052 <div>Text interaction</div>\ 1053 </li>\ 1054 <li class="talkify-enhanced-visibility-button">\ 1055 <button class="modern-talkify-control-center-accent" title="Toggle enhanced visibility">\ 1056 <i class="fas fa-eye"></i>\ 1057 </button>\ 1058 <div>Enhanced visibility</div>\ 1059 </li>\ 1060 <li class="talkify-detached talkify-rate-button">\ 1061 <button class="modern-talkify-control-center-accent" title="Adjust playback rate">\ 1062 <i class="fas fa-tachometer-alt"></i>\ 1063 </button>\ 1064 <div>\ 1065 <input type="range" value="5" min="0" max="10" title="Adjust playback rate">\ 1066 </div>\ 1067 </li>\ 1068 <li class="talkify-detached talkify-volume-button">\ 1069 <button class="modern-talkify-control-center-accent" title="Adjust playback volume">\ 1070 <i class="fas fa-volume-up"></i>\ 1071 </button>\ 1072 <div class="detached-volume-slider">\ 1073 <input type="range" value="10" min="0" max="10" title="Adjust playback volume">\ 1074 </div>\ 1075 </li>\ 1076 <li class="talkify-download-button mobile">\ 1077 <button title="Download" class="modern-talkify-control-center-accent">\ 1078 <i class="fas fa-download"></i>\ 1079 </button>\ 1080 <div>Download</div>\ 1081 </li>\ 1082 <li class="talkify-detached talkify-attach-handle">\ 1083 <button class="modern-talkify-control-center-accent" title="Attach player">\ 1084 <i class="fa fa-window-minimize"></i>\ 1085 </button>\ 1086 <div>Dock player</div>\ 1087 </li>\ 1088 </ul>\ 1089 </div>\ 1090 </li>\ 1091 <li class="talkify-playback-controls">\ 1092 <button class="talkify-rate-button talkify-attached" title="Rate of speech">\ 1093 <i class="fa fa-tachometer-alt"></i>\ 1094 <div class="rate-slider">\ 1095 <input type="range" value="5" min="0" max="10" title="Adjust playback rate">\ 1096 </div>\ 1097 </button>\ 1098 <button class="talkify-step-backward-button" title="Previous">\ 1099 <i class="fa fa-step-backward"></i>\ 1100 </button>\ 1101 <button class="talkify-play-button" title="Play">\ 1102 <i class="fa fa-play"></i>\ 1103 </button>\ 1104 <button class="talkify-audio-loading"></button>\ 1105 <button class="talkify-pause-button" title="Pause">\ 1106 <i class="fa fa-pause"></i>\ 1107 </button>\ 1108 <button class="talkify-audio-error" title="An error occurred at playback"><i class="fas fa-exclamation-triangle"></i></button>\ 1109 <button class="talkify-step-forward-button" title="Next">\ 1110 <i class="fa fa-step-forward"></i>\ 1111 </button>\ 1112 <button class="talkify-volume-button talkify-attached" title=" Volume">\ 1113 <i class="fa fa-volume-up"></i>\ 1114 <div class="volume-slider">\ 1115 <input type="range" value="10" min="0" max="10" title="Adjust playback volume">\ 1116 </div>\ 1117 </button>\ 1118 <button class="talkify-download-button desktop" title="Download">\ 1119 <i class="fas fa-download"></i>\ 1120 </button>\ 1121 <div class="talkify-download-loading desktop">\ 1122 <i class="fas fa-dharmachakra fa-spin"></i>\ 1123 </div>\ 1124 <div class="talkify-download-error desktop">\ 1125 <i class="fas fa-exclamation-triangle"></i>\ 1126 </div>\ 1127 <li class="talkify-attached">\ 1128 <div class="talkify-voice-selector ">\ 1129 <label for="voice-selector-toggle">\
1130 <img class="talkify-selected-voice-flag talkify-flag talkify-hidden" src="">\ 1131 <span></span>\ 1132 </label><input type="checkbox" id="voice-selector-toggle" style="display: none;" />\ 1133 </div>\ 1134 </li>\ 1135 </ul>\ 1136</div>'; 1137} 1138},{}],6:[function(require,module,exports){ 1139talkify = talkify || {}; 1140 1141talkify.tableReader = function () { 1142 function addTables(config) { 1143 var mapped = config.map(function (cfg) { 1144 var tablesToRead = []; 1145 1146 if ((typeof cfg.table) === "string") { 1147 tablesToRead = document.querySelectorAll(cfg.table); 1148 } else { 1149 tablesToRead = cfg.table; 1150 } 1151 1152 cfg.tables = Array.prototype.slice.call(tablesToRead); 1153 1154 return cfg; 1155 }); 1156 1157 markTables(mapped); 1158 } 1159 1160 function getElements(obj) { 1161 if (!obj) { 1162 return null; 1163 } 1164 1165 if ((typeof obj) === "string") { 1166 return Array.from(document.querySelectorAll(obj)); 1167 } else { 1168 return obj; 1169 } 1170 } 1171 1172 function markTables(configurations) { 1173 for (var j = 0; j < configurations.length; j++) { 1174 var config = configurations[j]; 1175 1176 for (var k = 0; k < config.tables.length; k++) { 1177 var table = config.tables[k]; 1178 1179 table.classList.add("talkify-tts-table"); 1180 1181 var headerCells = getElements(config.headerCells) || table.querySelectorAll('th'); 1182 var bodyCells = getElements(config.bodyCells) || table.querySelectorAll('td'); 1183 1184 var cols = headerCells.length; 1185 var currentHeader = 0; 1186 1187 for (var i = 0; i < bodyCells.length; i++) { 1188 if (i % cols === 0) { 1189 currentHeader = 0; 1190 } 1191 1192 if(!bodyCells[i].innerText.trim()){ 1193 currentHeader++; 1194 continue; 1195 } 1196 1197 var headerCell = headerCells[currentHeader]; 1198 1199 if (headerCell.innerText.trim()) { 1200 bodyCells[i].setAttribute("data-talkify-prefix", headerCell.innerText); 1201 } 1202 1203 bodyCells[i].classList.add("talkify-tts-tablecell"); 1204 1205 currentHeader++; 1206 } 1207 } 1208 } 1209 } 1210 1211 return { 1212 markTables: addTables 1213 } 1214}(); 1215},{}],7:[function(require,module,exports){ 1216talkify = talkify || {}; 1217talkify.http = (function ajax() { 1218 var get = function (url) { 1219 var keypart = (url.indexOf('?') !== -1 ? "&key=" : "?key=") + talkify.config.remoteService.apiKey; 1220 1221 return fetch(window.talkify.config.remoteService.host + url + keypart) 1222 .then(function (res) { 1223 return res.json(); 1224 }); 1225 }; 1226 1227 return { 1228 get: get 1229 }; 1230})(); 1231},{}],8:[function(require,module,exports){ 1232talkify = talkify || {}; 1233 1234talkify.autoScroll = function () { 1235 function activate() { 1236 talkify.messageHub.unsubscribe("autoscroll", "*.player.*.loaded"); 1237 1238 talkify.messageHub.subscribe("autoscroll", "*.player.*.loaded", function (item) { 1239 var y = item.element.getBoundingClientRect().top + window.pageYOffset + talkify.config.autoScroll.offsetpx; 1240 1241 window.scrollTo({ top: y, behavior: 'smooth' }); 1242 }); 1243 } 1244 1245 return { 1246 activate: activate 1247 } 1248}(); 1249 1250},{}],9:[function(require,module,exports){ 1251talkify = talkify || {}; 1252talkify.config = { 1253 debug: false, 1254 useSsml: false, 1255 maximumTextLength: 5000, 1256 ui: 1257 { 1258 audioControls: { 1259 enabled: true, 1260 controlcenter: "native", //["classic", "modern", "local", "native"] 1261 container: document.body, 1262 downloadEnabled: true, 1263 voicepicker: { 1264 enabled: true, //where applicable 1265 filter: { 1266 byClass: [], //example: ["Standard", "Premium", "Exclusive"] 1267 byCulture: [], //example: ["en-EN", "en-AU"] 1268 byLanguage: [] //example: ["English", "Spanish"] 1269 } 1270 } 1271 } 1272 }, 1273 formReader: { 1274 voice: null, 1275 rate: 0, 1276 remoteService: true, 1277 requiredText: "This field is required", 1278 valueText: "You have entered {value} as: {label}.", 1279 selectedText: "You have selected {label}.", 1280 notSelectedText: "{label} is not selected." 1281 }, 1282 remoteService: { 1283 active: true, 1284 host: 'https://talkify.net', 1285 apiKey: '', 1286 speechBaseUrl: '/api/speech/v2', 1287 languageBaseUrl: '/api/language/v1' 1288 }, 1289 keyboardCommands: { 1290 enabled: false, 1291 commands: { 1292 playPause: 32, 1293 next: 39, 1294 previous: 37 1295 } 1296 }, 1297 voiceCommands: { 1298 enabled: false, 1299 keyboardActivation: { 1300 enabled: true, 1301 key: 77 1302 }, 1303 commands: { 1304 playPause: ["play", "pause", "stop", "start"], 1305 next: ["play next", "next"], 1306 previous: ["play previous", "previous", "back", "go back"] 1307 } 1308 }, 1309 autoScroll: { 1310 offsetpx: 100 1311 } 1312} 1313},{}],10:[function(require,module,exports){ 1314talkify = talkify || {}; 1315 1316talkify.formReader = function () { 1317 var player; 1318 var timeout; 1319 1320 function setupForm(formElement) { 1321 var elements = formElement.elements; 1322 1323 for (var i = 0; i < elements.length; i++) { 1324 elements[i].addEventListener("focus", onFocus); 1325 } 1326 } 1327 1328 function removeForm(formElement) { 1329 var elements = formElement.elements; 1330 1331 for (var i = 0; i < elements.length; i++) { 1332 elements[i].removeEventListener("focus", onFocus); 1333 } 1334 } 1335 1336 function onFocus(e) { 1337 if (timeout) { 1338 clearTimeout(timeout); 1339 } 1340 1341 var me = this; 1342 1343 timeout = setTimeout(function () { 1344 if (!player) { 1345 player = talkify.config.formReader.remoteService ? new talkify.TtsPlayer() : new talkify.Html5Player(); 1346 } 1347 1348 var config = talkify.config.formReader; 1349 1350 if (config.voice) { 1351 player.forceVoice({ name: config.voice }); 1352 } 1353 1354 player.setRate(config.rate); 1355 1356 if (me.type === "button" || me.type === "submit") { 1357 player.playText(me.value || me.innerText); 1358 return; 1359 } 1360 1361 var requiredText = me.attributes.required ? config.requiredText : ""; 1362 1363 var label = findLabelFor(me); 1364 1365 var text = getTextForCheckboxes(me, label) || getTextForSelects(me, label) || getTextForInputs(me, label) || ""; 1366 1367 player.playText(text + ". " + requiredText); 1368 }, 100); 1369 } 1370 1371 function getTextForCheckboxes(element, label) { 1372 var config = talkify.config.formReader; 1373 1374 if (element.type === "checkbox") { 1375 var labelText = label ? label.innerText : "checkbox"; 1376 1377 if (element.checked) { 1378 return config.selectedText.replace("{label}", labelText); 1379 } else { 1380 return config.notSelectedText.replace("{label}", labelText); 1381 } 1382 } 1383 1384 return null; 1385 } 1386 1387 function getTextForSelects(element, label) { 1388 var config = talkify.config.formReader; 1389 1390 if (element.tagName.toLowerCase() === "select") { 1391 var labelText = label ? label.innerText : "option"; 1392 1393 var value = element.options[element.options.selectedIndex].text; 1394 1395 return config.valueText.replace("{value}", value).replace("{label}", labelText); 1396 } 1397 1398 return null; 1399 } 1400 1401 function getTextForInputs(element, label) { 1402 var config = talkify.config.formReader; 1403 1404 if (!label) { 1405 return element.value; 1406 } 1407 1408 if (element.value) { 1409 return config.valueText.replace("{value}", element.value).replace("{label}", label.innerText); 1410 } else { 1411 return label.innerText + "."; 1412 } 1413 } 1414 1415 function findLabelFor(input) { 1416 var labels = document.getElementsByTagName('label'); 1417 for (var i = 0; i < labels.length; i++) { 1418 if (labels[i].htmlFor === input.id) { 1419 return labels[i]; 1420 } 1421 } 1422 1423 return null; 1424 } 1425 1426 return { 1427 addForm: function (formElement) { 1428 setupForm(formElement); 1429 }, 1430 removeForm: function(formElement) { 1431 removeForm(formElement); 1432 } 1433 }; 1434}(); 1435},{}],11:[function(require,module,exports){ 1436//TODO: Verify all events. Especially for this player. Trigger play, pause, stop and add console outputs and see what happens 1437talkify = talkify || {}; 1438 1439talkify.Html5Player = function () { 1440 this.isStopped = false;
1441 this.volume = 1; 1442 this.pitch = 1; 1443 1444 this.currentContext = { 1445 item: null, 1446 utterances: [], 1447 currentUtterance: null 1448 }; 1449 1450 var timeupdater; 1451 1452 var me = this; 1453 1454 this.playbar = { 1455 instance: null 1456 }; 1457 1458 this.audioSource = { 1459 play: function () { 1460 if (me.currentContext.item) { 1461 playCurrentContext(); 1462 } else { 1463 talkify.messageHub.publish(me.correlationId + ".player.html5.unplayable"); 1464 } 1465 }, 1466 pause: function () { 1467 window.speechSynthesis.pause(); 1468 1469 talkify.messageHub.publish(me.correlationId + ".player.html5.pause"); 1470 }, 1471 ended: function () { return !window.speechSynthesis.speaking; }, 1472 isPlaying: function () { return window.speechSynthesis.speaking; }, 1473 paused: function () { return !window.speechSynthesis.speaking; }, 1474 currentTime: function () { return 0; }, 1475 cancel: function (asPause) { 1476 if (asPause) { 1477 stop(); 1478 } else { 1479 window.speechSynthesis.cancel(); 1480 } 1481 }, 1482 stop: function () { 1483 stop(); 1484 }, 1485 dispose: function () { 1486 talkify.messageHub.unsubscribe("html5player", me.correlationId + ".controlcenter.request.play"); 1487 talkify.messageHub.unsubscribe("html5player", me.correlationId + ".controlcenter.request.pause"); 1488 talkify.messageHub.unsubscribe("html5player", me.correlationId + ".controlcenter.request.volume"); 1489 talkify.messageHub.unsubscribe("html5player", me.correlationId + ".controlcenter.request.rate"); 1490 talkify.messageHub.unsubscribe("html5player", me.correlationId + ".controlcenter.request.pitch"); 1491 1492 if (timeupdater) { 1493 clearInterval(timeupdater); 1494 } 1495 } 1496 }; 1497 1498 talkify.BasePlayer.call(this, this.audioSource, this.playbar); 1499 1500 this.playAudio = function (item) { 1501 me.currentContext.item = item; 1502 me.currentContext.utterances = []; 1503 me.currentContext.currentUtterance = null; 1504 1505 playCurrentContext(); 1506 }; 1507 1508 this.setVolume = function (volume) { 1509 me.volume = volume; 1510 1511 return this; 1512 }; 1513 1514 this.usePitch = function (pitch) { 1515 me.pitch = pitch; 1516 1517 talkify.messageHub.publish(me.correlationId + ".player.html5.pitchchanged", pitch); 1518 1519 return this; 1520 }; 1521 1522 talkify.messageHub.subscribe("html5player", me.correlationId + ".controlcenter.request.play", function () { me.audioSource.play(); }); 1523 talkify.messageHub.subscribe("html5player", me.correlationId + ".controlcenter.request.pause", function () { me.pause(); }); 1524 talkify.messageHub.subscribe("html5player", me.correlationId + ".controlcenter.request.volume", function (volume) { me.volume = volume / 10; }); 1525 1526 talkify.messageHub.subscribe("html5player", me.correlationId + ".controlcenter.request.rate", function (rate) { 1527 me.settings.rate = rate; 1528 talkify.messageHub.publish(me.correlationId + ".player.html5.ratechanged", rate); 1529 }); 1530 1531 talkify.messageHub.subscribe("html5player", me.correlationId + ".controlcenter.request.pitch", function (pitch) { 1532 me.pitch = pitch; 1533 1534 talkify.messageHub.publish(me.correlationId + ".player.html5.pitchchanged", me.pitch); 1535 }); 1536 1537 function playCurrentContext() { 1538 if (timeupdater) { 1539 clearInterval(timeupdater); 1540 } 1541 1542 var item = me.currentContext.item; 1543 1544 var chuncks = chunckText(item.text); 1545 1546 me.currentContext.utterances = []; 1547 me.isStopped = false; 1548 1549 chuncks.forEach(function (chunck) { 1550 var utterance = new window.SpeechSynthesisUtterance(); 1551 1552 utterance.text = chunck; 1553 utterance.lang = me.settings.lockedLanguage || me.settings.referenceLanguage.Culture; 1554 utterance.rate = me.settings.rate; 1555 utterance.volume = me.volume; 1556 utterance.pitch = me.pitch; 1557 1558 me.currentContext.utterances.push(utterance); 1559 }); 1560 1561 var wordIndex = 0; 1562 var previousCharIndex = 0; 1563 var words = extractWords(item.text); 1564 1565 me.currentContext.utterances[me.currentContext.utterances.length - 1].onend = function (e) { 1566 talkify.messageHub.publish(me.correlationId + ".player.html5.utterancecomplete", item); 1567 1568 if (timeupdater) { 1569 clearInterval(timeupdater); 1570 } 1571 1572 if (!me.currentContext.currentUtterance) { 1573 return; 1574 } 1575 1576 if (me.currentContext.currentUtterance.text !== e.currentTarget.text) { 1577 return; 1578 } 1579 1580 if (!me.isStopped) { 1581 talkify.messageHub.publish(me.correlationId + ".player.html5.ended", item); 1582 } 1583 }; 1584
1585 me.currentContext.utterances.forEach(function (u, index) { 1586 if (index === 0) { 1587 u.onstart = function (e) { 1588 me.currentContext.currentUtterance = e.utterance; 1589 talkify.messageHub.publish(me.correlationId + ".player.html5.loaded", me.currentContext.item); 1590 talkify.messageHub.publish(me.correlationId + ".player.html5.play", { item: me.currentContext.item, positions: [], currentTime: 0 }); 1591 1592 if (timeupdater) { 1593 clearInterval(timeupdater); 1594 } 1595 1596 timeupdater = setInterval(function () { 1597 talkify.messageHub.publish(me.correlationId + ".player.html5.timeupdated", (wordIndex + 1) / words.length); 1598 }, 100); 1599 }; 1600 } else { 1601 u.onstart = function (e) { 1602 me.currentContext.currentUtterance = e.utterance; 1603 }; 1604 } 1605 1606 u.onpause = function () { 1607 if (timeupdater) { 1608 clearInterval(timeupdater); 1609 } 1610 1611 talkify.messageHub.publish(me.correlationId + ".player.html5.pause"); 1612 }; 1613 1614 u.onresume = function () { }; 1615 1616 u.onboundary = function (e) { 1617 if (e.name !== "word" || !words[wordIndex]) { 1618 return; 1619 } 1620 1621 if (!me.settings.useTextHighlight || !u.voice.localService) { 1622 return; 1623 } 1624 1625 var fromIndex = 0; 1626 1627 for (var k = 0; k < wordIndex; k++) { 1628 fromIndex += words[k].length + 1; 1629 } 1630 1631 var isCommaOrSimilair = previousCharIndex + 1 === e.charIndex; 1632 1633 if (isCommaOrSimilair) { 1634 previousCharIndex = e.charIndex; 1635 return; 1636 } 1637 1638 me.wordHighlighter.highlight(item, words[wordIndex], fromIndex); 1639 wordIndex++; 1640 previousCharIndex = e.charIndex; 1641 }; 1642 1643 getVoice().then(function (voice) { 1644 if (words.length && me.settings.useTextHighlight && voice.localService) { 1645 me.wordHighlighter.highlight(item, words[0], 0); 1646 } 1647 1648 u.voice = voice; 1649 1650 console.log(u); //Keep this, speech bugs out otherwise 1651 1652 window.speechSynthesis.cancel(); 1653 1654 talkify.messageHub.publish(me.correlationId + ".player.html5.voiceset", voice); 1655 1656 window.setTimeout(function () { 1657 window.speechSynthesis.speak(u); 1658 }, 100); 1659 }); 1660 }); 1661 }; 1662 1663 function chunckText(text) { 1664 var language = me.settings.lockedLanguage || me.settings.referenceLanguage.Culture; 1665 var chunckSize = language.indexOf('zh-') > -1 ? 70 : 1666 language.indexOf('ko-') > -1 ? 130 : 200; 1667 1668 var chuncks = []; 1669 var sentences = text.split(/(\?|\.|ã)+/g); //('.'); 1670 var currentChunck = ''; 1671 1672 sentences.forEach(function (sentence) { 1673 if (sentence === '' || sentence === '.' || sentence === 'ã' || sentence === '?') { 1674 if (currentChunck) { 1675 currentChunck += sentence; 1676 } 1677 1678 return; 1679 } 1680 1681 if (currentChunck && ((currentChunck.length + sentence.length) > chunckSize)) { 1682 chuncks.push(currentChunck); 1683 currentChunck = ''; 1684 } 1685 1686 if (sentence.length > chunckSize) { 1687 var words = extractWords(sentence, language); 1688 1689 words.forEach(function (word) { 1690 if (currentChunck.length + word.length > chunckSize) { 1691 chuncks.push(currentChunck); 1692 currentChunck = ''; 1693 } 1694 1695 currentChunck += word.trim() + ' '; 1696 }); 1697 1698 if (currentChunck.trim()) { 1699 chuncks.push(currentChunck.trim() + '.'); 1700 currentChunck = ''; 1701 } 1702 1703 return; 1704 } 1705 1706 currentChunck += sentence; 1707 }); 1708 1709 chuncks.push(currentChunck); 1710 1711 return chuncks; 1712 }; 1713 1714 function extractWords(text, language) { 1715 var wordRegex = new RegExp(/[&\$\-|]|([("\-&])*(\b[^\s]+[.:,"-)!&?]*)/g); 1716 1717 if (language) { 1718 if (language.indexOf('zh-') > -1) { 1719 return text.split('ï¼'); 1720 } 1721 1722 if (language.indexOf('ko-') > -1) { 1723 return text.split('.'); 1724 } 1725 } 1726 1727 var words = []; 1728 var m; 1729 1730 while ((m = wordRegex.exec(text)) !== null) { 1731 if (m.index === wordRegex.lastIndex) { 1732 wordRegex.lastIndex++; 1733 } 1734 1735 words.push(m[0]); 1736 } 1737 1738 return words; 1739 }; 1740 1741 function selectVoiceToPlay(voices) { 1742 var matchingVoices = []; 1743 var voice = null; 1744 1745 var language = me.settings.lockedLanguage || me.settings.referenceLanguage.Culture; 1746 1747 for (var i = 0; i < voices.length; i++) { 1748 if (voices[i].lang === language) { 1749 matchingVoices.push(voices[i]); 1750 voice = voices[i]; 1751 } 1752 } 1753 1754 for (var j = 0; j < matchingVoices.length; j++) { 1755 if (matchingVoices[j].localService) { 1756 voice = matchingVoices[j]; 1757 1758 break; 1759 } 1760 } 1761 1762 if (!voice && matchingVoices.length) { 1763 voice = matchingVoices[0]; 1764 } 1765 1766 if (!voice && voices.length) { 1767 voice = voices[0]; 1768 } 1769 1770 return voice; 1771 }; 1772 1773 function getVoice() { 1774 if (me.forcedVoice) { 1775 return Promise.resolve(me.forcedVoice); 1776 } 1777 1778 if (window.speechSynthesis.getVoices().length) { 1779 var voices = window.speechSynthesis.getVoices(); 1780 1781 return Promise.resolve(selectVoiceToPlay(voices)); 1782 } 1783 1784 return new Promise(function(resolve){ 1785 window.speechSynthesis.onvoiceschanged = function () { 1786 var voices = window.speechSynthesis.getVoices(); 1787 1788 resolve(selectVoiceToPlay(voices)); 1789 }; 1790 }); 1791 }; 1792 1793 function stop() { 1794 me.isStopped = true; 1795 talkify.messageHub.publish(me.correlationId + ".player.html5.pause"); 1796 window.speechSynthesis.cancel(); 1797 1798 if (timeupdater) { 1799 clearInterval(timeupdater); 1800 } 1801 1802 if (me.currentContext.utterances.indexOf(me.currentContext.currentUtterance) < me.currentContext.utterances.length - 1) { 1803 console.log('Not the last, finishing anyway...'); 1804 talkify.messageHub.publish(me.correlationId + ".player.html5.utterancecomplete", me.currentContext.
1804item); 1805 } 1806 }; 1807 1808 if (me.playbar.instance) { 1809 me.playbar.instance 1810 .setMinRate(0) 1811 .setMaxRate(2) 1812 .setRateStep(0.2) 1813 .setMinPitch(0) 1814 .setMaxPitch(2) 1815 .setPitchStep(0.2); 1816 } 1817 1818 talkify.messageHub.publish(this.correlationId + ".player.html5.created"); 1819}; 1820 1821talkify.Html5Player.prototype.constructor = talkify.Html5Player; 1822},{}],12:[function(require,module,exports){ 1823talkify = talkify || {}; 1824 1825talkify.KeyboardCommands = function (keyboadCommands) { 1826 if (!keyboadCommands.enabled) { 1827 return { 1828 onPrevious: function () { }, 1829 onNext: function () { }, 1830 onPlayPause: function () { }, 1831 dispose: function () { } 1832 } 1833 } 1834 1835 1836 var onNextCallback = function () { }; 1837 var onPreviousCallback = function () { }; 1838 var onPlayPauseCallback = function () { }; 1839 1840 document.addEventListener("keyup", keyupEventHandler); 1841 1842 function keyupEventHandler(e) { 1843 if (!e.ctrlKey) { 1844 return; 1845 } 1846 1847 var key = e.keyCode ? e.keyCode : e.which; 1848 1849 if (key === keyboadCommands.commands.previous) { 1850 onPreviousCallback(); 1851 } else if (key === keyboadCommands.commands.next) { 1852 onNextCallback(); 1853 } else if (key === keyboadCommands.commands.playPause) { 1854 onPlayPauseCallback(); 1855 } 1856 } 1857 1858 return { 1859 onPrevious: function (callback) { 1860 onPreviousCallback = callback; 1861 }, 1862 onNext: function (callback) { 1863 onNextCallback = callback; 1864 }, 1865 onPlayPause: function (callback) { 1866 onPlayPauseCallback = callback; 1867 }, 1868 dispose: function () { 1869 document.removeEventListener("keyup", keyupEventHandler); 1870 } 1871 } 1872}; 1873},{}],13:[function(require,module,exports){ 1874talkify = talkify || {}; 1875talkify.messageHub = function () { 1876 var subscribers = {}; 1877 1878 function publish(topic, message) { 1879 if (topic.indexOf("timeupdate") === -1) { 1880 talkify.log("Publishing", topic); 1881 } 1882 1883 var topics = topic.split('.'); 1884 var candidates = []; 1885 1886 Object.keys(subscribers).forEach(function (subscriberKey) { 1887 if(subscriberKey === '*'){ 1888 candidates.push(subscriberKey); 1889 return; 1890 } 1891 1892 var s = subscriberKey.split('.'); 1893 1894 if (s.length != topics.length) { 1895 return; 1896 } 1897 1898 var match = true; 1899 1900 for (var i = 0; i < s.length; i++) { 1901 if (topics[i] === '*') { 1902 match = true; 1903 } 1904 else if (s[i] === topics[i]) { 1905 match = true; 1906 } else if (s[i] === "*") { 1907 match === true; 1908 } else { 1909 match = false; 1910 break; 1911 } 1912 } 1913 1914 if (match) { 1915 candidates.push(subscriberKey); 1916 } 1917 }); 1918 1919 if (candidates.length === 0) { 1920 console.warn("No subscriber found", topic) 1921 } 1922 1923 candidates.forEach(function (c) { 1924 subscribers[c].forEach(function (subscriber) { 1925 if (c.indexOf("timeupdate") === -1) { 1926 talkify.log("Calling subscriber", subscriber, c, message); 1927 } 1928 1929 subscriber.fn(message, topic); 1930 }); 1931 }) 1932 1933 } 1934 1935 function subscribe(key, topic, fn) { 1936 topic = Array.isArray(topic) ? topic : [topic]; 1937 1938 for (var i = 0; i < topic.length; i++) { 1939 subscribers[topic[i]] = subscribers[topic[i]] || []; 1940 1941 subscribers[topic[i]].push({ key: key, fn: fn }); 1942 } 1943 } 1944 1945 function unsubscribe(key, topic) { 1946 topic = Array.isArray(topic) ? topic : [topic]; 1947 1948 talkify.log("Unsubscribing", key, topic); 1949 1950 Object.keys(subscribers).filter(function (subscriberKey) { 1951 return topic.indexOf(subscriberKey) > -1 ; 1952 }
1952).forEach(function (subscriberKey) { 1953 subscribers[subscriberKey] = subscribers[subscriberKey].filter(function (subscriber) { 1954 return subscriber.key !== key; 1955 }); 1956 }); 1957 } 1958 1959 return { 1960 publish: publish, 1961 subscribe: subscribe, 1962 unsubscribe: unsubscribe 1963 } 1964}(); 1965},{}],14:[function(require,module,exports){ 1966talkify = talkify || {}; 1967talkify.BasePlayer = function (_audiosource, _playbar, options) { 1968 this.correlationId = talkify.generateGuid(); 1969 talkify.messageHub.publish(this.correlationId + ".player.*.creating"); 1970 1971 options = options || {}; 1972 options.controlcenter = options.controlcenter || {}; 1973 1974 this.audioSource = _audiosource; 1975 this.wordHighlighter = new talkify.wordHighlighter(this.correlationId); 1976 1977 var me = this; 1978 1979 this.settings = { 1980 useTextHighlight: false, 1981 referenceLanguage: { Culture: "", Language: -1 }, 1982 lockedLanguage: null, 1983 rate: 1 1984 }; 1985 1986 this.playbar = _playbar; 1987 this.forcedVoice = null; 1988 1989 if (talkify.config.ui.audioControls.enabled) { 1990 this.playbar.instance = talkify.playbar(options.controlcenter.container, this.correlationId, options.controlcenter.name); 1991 } 1992 1993 //Does infact prevent the usage of 2 simultanous players (which is not supported anyway) 1994 talkify.messageHub.subscribe("core-player", "*.player.*.creating", function () { 1995 me.dispose(); 1996 }); 1997 1998 talkify.messageHub.subscribe("core-player", this.correlationId + ".player.*.loaded", function (item) { 1999 item.isLoading = false; 2000 }); 2001 2002 talkify.messageHub.subscribe("core-player", this.correlationId + ".player.*.ended", function (item) { 2003 item.isPlaying = false; 2004 }); 2005 2006 talkify.messageHub.subscribe("core-player", this.correlationId + ".controlcenter.texthighlightoggled", function (enabled) { 2007 me.settings.useTextHighlight = enabled; 2008 }); 2009 2010 talkify.messageHub.subscribe("core-player", this.correlationId + ".controlcenter.request.setvoice", function (voice) { 2011 me.forceVoice(voice); 2012 }); 2013 2014 talkify.messageHub.subscribe("core-player", this.correlationId + ".controlcenter.request.enhancedvisibility", function (value) { 2015 talkify.messageHub.publish(me.correlationId + ".player.*.enhancedvisibilityset", value); 2016 }); 2017 2018 talkify.messageHub.publish(this.correlationId + ".player.*.ratechanged", me.settings.rate); 2019 talkify.messageHub.publish(this.correlationId + ".player.*.enhancedvisibilityset", false); 2020 2021 this.withReferenceLanguage = function (refLang) { 2022 this.settings.referenceLanguage = refLang; 2023 2024 return this; 2025 }; 2026 2027 this.enableTextHighlighting = function () { 2028 this.settings.useTextHighlight = true; 2029 2030 talkify.messageHub.publish(this.correlationId + ".player.*.texthighlight.enabled"); 2031 2032 return this; 2033 }; 2034 2035 this.disableTextHighlighting = function () { 2036 this.settings.useTextHighlight = false; 2037 2038 talkify.messageHub.publish(this.correlationId + ".player.*.texthighlight.disabled"); 2039 2040 return this; 2041 }; 2042 2043 this.setRate = function (r) { 2044 this.settings.rate = r; 2045 2046 talkify.messageHub.publish(this.correlationId + ".player.*.ratechanged", r); 2047 2048 return this; 2049 }; 2050 2051 this.subscribeTo = function (subscriptions) { 2052 talkify.messageHub.subscribe("core-player", this.correlationId + ".player.*.pause", subscriptions.onPause || function () { }); 2053 talkify.messageHub.subscribe("core-player", this.correlationId + ".player.*.resume", subscriptions.onResume || function () { }); 2054 talkify.messageHub.subscribe("core-player", this.correlationId + ".player.*.play", subscriptions.onPlay || function () { }); 2055 talkify.messageHub.subscribe("core-player", this.correlationId + ".player.*.loaded", subscriptions.onItemLoaded || function () { }); 2056 talkify.messageHub.subscribe("core-player", [this.correlationId + ".wordhighlighter.complete", this.correlationId + ".player.html5.utterancecomplete"], subscriptions.onItemFinished || function () { }); 2057 talkify.messageHub.subscribe("core-player", this.correlationId + ".player.*.prepareplay", subscriptions.onBeforeItemPlaying || function () { }); 2058 talkify.messageHub.subscribe("core-player", this.correlationId + ".controlcenter.texthighlightoggled", subscriptions.onTextHighligtChanged || function () { }); 2059 2060 return this; 2061 }; 2062 2063 this.playItem = function (item) { 2064 if (item && item.isPlaying) { 2065 if (this.audioSource.paused()) { 2066 this.audioSource.play(); 2067 } else { 2068 this.audioSource.pause(); 2069 } 2070 } 2071 2072 talkify.messageHub.publish(this.correlationId + ".player.*.prepareplay", item); 2073 2074 item.isLoading = true; 2075 item.isPlaying = true; 2076 item.element.classList.add("playing"); 2077 2078 this.playAudio(item); 2079 }; 2080 2081 this.createItems = function (text) { 2082 var safeMaxQuerystringLength = window.talkify.config.maximumTextLength || 1000; 2083 2084 var items = []; 2085 2086 //TODO: Smart split, should really split at the first end of sentence (.) that is < safeLength 2087 if (text.length > safeMaxQuerystringLength) { 2088 var f = text.substr(0, safeMaxQuerystringLength); 2089 2090 items.push(template(f)); 2091 2092 items = items.concat(this.createItems(text.substr(safeMaxQuerystringLength, text.length - 1))); 2093 2094 return items; 2095 } 2096 2097 items.push(template(text)); 2098 2099 return items; 2100 2101 function template(t) { 2102 //Null-objects 2103 var element = document.createElement("span"); 2104 var clone = element.cloneNode(true); 2105 2106 return { 2107 text: t, 2108 preview: t.substr(0, 40), 2109 element: element, 2110 originalElement: clone, 2111 isPlaying: false, 2112 isLoading: false 2113 }; 2114 } 2115 }; 2116 2117 this.playText = function (text) { 2118 if (!text) { 2119 return; 2120 } 2121 2122 var items = this.createItems(text); 2123 2124 var currentItem = 0; 2125 2126 talkify.messageHub.subscribe("core-player.playText", this.correlationId + ".player.*.ended", function () { 2127 currentItem++; 2128 2129 if (currentItem >= items.length) { 2130 talkify.messageHub.unsubscribe("core.playText", this.correlationId + ".player.*.ended"); 2131 return; 2132 } 2133 2134 this.playItem(items[currentItem]); 2135 }); 2136 2137 this.playItem(items[currentItem]); 2138 }; 2139 2140 this.paused = function () { 2141 return this.audioSource.paused(); 2142 }; 2143 2144 this.isPlaying = function () { 2145 return this.audioSource.isPlaying(); 2146 }; 2147 2148 this.play = function () { 2149 this.audioSource.play(); 2150 }; 2151 2152 this.pause = function () { 2153 this.audioSource.pause(); 2154 var me = this; 2155 2156 if (!me.audioSource.paused() && me.audioSource.cancel) { 2157 me.audioSource.cancel(true); 2158 } 2159 }; 2160 2161 this.dispose = function () { 2162 talkify.messageHub.publish(this.correlationId + ".player.tts.disposed"); 2163 this.audioSource.stop(); 2164 2165 this.audioSource.dispose(); 2166 this.wordHighlighter.dispose(); 2167 2168 talkify.messageHub.unsubscribe("core-player", this.correlationId + ".player.*.loaded"); 2169 talkify.messageHub.unsubscribe("core-player", this.correlationId + ".player.*.ended"); 2170 talkify.messageHub.unsubscribe("core-player", this.correlationId + ".controlcenter.texthighlightoggled"); 2171 talkify.messageHub.unsubscribe("core-player", this.correlationId + ".player.*.pause"); 2172 talkify.messageHub.unsubscribe("core-player", this.correlationId + ".player.*.resume"); 2173 talkify.messageHub.unsubscribe("core-player", this.correlationId + ".player.*.play"); 2174 talkify.messageHub.unsubscribe("core-player", [this.correlationId + ".wordhighlighter.complete", this.correlationId + ".player.html5.utterancecomplete"]); 2175 talkify.messageHub.unsubscribe("core-player", this.correlationId + ".player.*.prepareplay"); 2176 talkify.messageHub.unsubscribe("core-player", this.correlationId + ".controlcenter.texthighlightoggled"); 2177 talkify.messageHub.unsubscribe("core-player", this.correlationId + ".controlcenter.request.setvoice"); 2178 talkify.messageHub.unsubscribe("core-player", this.correlationId + ".controlcenter.request.enhancedvisibility"); 2179 talkify.messageHub.unsubscribe("core-player", "*.player.*.creating"); 2180 }; 2181 2182 this.forceLanguage = function (culture) { 2183 this.settings.lockedLanguage = culture; 2184 2185 return this; 2186 }; 2187 2188 this.forceVoice = function (voice) { 2189 this.forcedVoice = voice !== undefined ? voice : null; 2190 2191 this.settings.lockedLanguage = (voice && (voice.lang || voice.culture)) || this.settings.lockedLanguage; 2192 2193 talkify.messageHub.publish(this.correlationId + ".player.*.voiceset", voice); 2194 2195 return this; 2196 }; 2197 2198 this.enableEnhancedTextVisibility = function () { 2199 talkify.messageHub.publish(this.correlationId + ".player.*.enhancedvisibilityset", true); 2200 2201 return this; 2202 } 2203 2204 this.disableEnhancedTextVisibility = function () { 2205 talkify.messageHub.publish(this.correlationId + ".player.*.enhancedvisibilityset", false); 2206 2207 return this; 2208 } 2209}; 2210},{}],15:[function(require,module,exports){ 2211talkify = talkify || {}; 2212 2213talkify.TtsPlayer = function (options) { 2214 if (!talkify.config.remoteService.active) { 2215 throw "This player needs to communicate to a remote service. To enable this player please set flag talkify.config.remoteService.active to true."; 2216 } 2217 2218 var me = this; 2219 var audioElement, timeupdater; 2220 var currentVoice, currentPitch, currentWordBreak, currentRate; 2221 2222 this.currentContext = { 2223 item: null, 2224 positions: [] 2225 }; 2226 2227 this.playbar = { 2228 instance: null 2229 }; 2230 2231 this.audioSource = { 2232 play: function () { 2233 audioElement.play(); 2234 }, 2235 pause: function () { 2236 audioElement.pause(); 2237 }, 2238 isPlaying: function () { 2239 return audioElement.duration > 0 && !audioElement.paused; 2240 }, 2241 paused: function () { return audioElement.paused; }, 2242 currentTime: function () { return audioElement.currentTime; }, 2243 stop: function () { 2244 audioElement.pause(); 2245 audioElement.currentTime = 0; 2246 }, 2247 dispose: function () { 2248 if (timeupdater) { 2249 clearInterval(timeupdater); 2250 } 2251 2252 var existingElement = document.getElementById("talkify-audio"); 2253 2254 if (existingElement) { 2255 existingElement.outerHTML = ""; 2256 } 2257 2258 talkify.messageHub.unsubscribe("tts-player", me.correlationId + ".controlcenter.request.play"); 2259 talkify.messageHub.unsubscribe("tts-player", me.correlationId + ".controlcenter.request.pause"); 2260 talkify.messageHub.unsubscribe("tts-player", me.correlationId + ".controlcenter.request.seek"); 2261 talkify.messageHub.unsubscribe("tts-player", me.correlationId + ".controlcenter.request.volume"); 2262 talkify.messageHub.unsubscribe("tts-player", me.correlationId + ".controlcenter.request.rate"); 2263 talkify.messageHub.unsubscribe("tts-player", me.correlationId + ".controlcenter.request.wordbreak"); 2264 talkify.messageHub.unsubscribe("tts-player", me.correlationId + ".controlcenter.request.pitch"); 2265 talkify.messageHub.unsubscribe("tts-player", me.correlationId + ".controlcenter.request.phonation.normal"); 2266 talkify.messageHub.unsubscribe("tts-player", me.correlationId + ".controlcenter.request.phonation.soft"); 2267 talkify.messageHub.unsubscribe("tts-player", me.correlationId + ".controlcenter.request.phonation.whisper"); 2268 } 2269 }; 2270 2271 talkify.BasePlayer.call(this, this.audioSource, this.playbar, options); 2272 2273 this.settings.whisper = false;
2274 this.settings.soft = false; 2275 this.settings.wordbreakms = 0; 2276 this.settings.volumeDb = 0.0; 2277 this.settings.pitch = 0; 2278 2279 function setupBindings() { 2280 audioElement.addEventListener("pause", onPause); 2281 audioElement.addEventListener("play", onPlay); 2282 audioElement.addEventListener("seeked", onSeek); 2283 } 2284 2285 function onSeek() { 2286 talkify.messageHub.publish(me.correlationId + ".player.tts.seeked", { time: this.currentTime, item: me.currentContext.item, positions: me.currentContext.positions }); 2287 2288 if (me.audioSource.paused() && me.audioSource.currentTime() > 0.1) { 2289 me.audioSource.play(); 2290 } 2291 } 2292 2293 function onPause() { 2294 if (timeupdater) { 2295 clearInterval(timeupdater); 2296 } 2297 2298 talkify.messageHub.publish(me.correlationId + ".player.tts.pause"); 2299 } 2300 2301 function onPlay() { 2302 if (timeupdater) { 2303 clearInterval(timeupdater); 2304 } 2305 2306 timeupdater = setInterval(function () { 2307 talkify.messageHub.publish(me.correlationId + ".player.tts.timeupdated", { currentTime: audioElement.currentTime, duration: audioElement.duration }); 2308 }, 50); 2309 2310 if (!me.currentContext.item) { 2311 talkify.messageHub.publish(me.correlationId + ".player.tts.unplayable"); 2312 2313 me.audioSource.pause(); 2314 } 2315 2316 if (!me.currentContext.positions.length) { 2317 talkify.messageHub.publish(me.correlationId + ".player.tts.play", { item: me.currentContext.item, positions: [], currentTime: me.audioSource.currentTime() }); 2318 return; 2319 } 2320 2321 if (me.audioSource.currentTime() > 0.1) { 2322 talkify.messageHub.publish(me.correlationId + ".player.tts.resume", { currentTime: me.audioSource.currentTime() }); 2323 } else { 2324 var interval = setInterval(function () { 2325 if (me.audioSource.currentTime() > 0) { 2326 clearInterval(interval); 2327 2328 talkify.messageHub.publish(me.correlationId + ".player.tts.play", { item: me.currentContext.item, positions: me.currentContext.positions, currentTime: me.audioSource.currentTime() }); 2329 } 2330 }, 20); 2331 } 2332 } 2333 2334 function initialize() { 2335 if (timeupdater) { 2336 clearInterval(timeupdater); 2337 } 2338 2339 audioElement = null; 2340 var existingElement = document.getElementById("talkify-audio"); 2341 2342 if (existingElement) { 2343 existingElement.outerHTML = ""; 2344 } 2345 2346 var mp3Source = document.createElement("source"); 2347 var wavSource = document.createElement("source"); 2348 audioElement = document.createElement("audio"); 2349 2350 audioElement.appendChild(mp3Source); 2351 audioElement.appendChild(wavSource); 2352 2353 mp3Source.type = "audio/mpeg"; 2354 wavSource.type = "audio/wav"; 2355 audioElement.id = "talkify-audio"; 2356 audioElement.autoplay = false; 2357 2358 (talkify.config.ui.audioControls.container || document.body).appendChild(audioElement); 2359 2360 var clonedAudio = audioElement.cloneNode(true); 2361 audioElement.parentNode.replaceChild(clonedAudio, audioElement); 2362 2363 audioElement = clonedAudio; 2364 2365 talkify.messageHub.subscribe("tts-player", me.correlationId + ".controlcenter.request.play", function () { me.play(); }); 2366 talkify.messageHub.subscribe("tts-player", me.correlationId + ".controlcenter.request.pause", function () { audioElement.pause(); }); 2367 talkify.messageHub.subscribe("tts-player", me.correlationId + ".controlcenter.request.seek", function (position) { 2368 var pos = audioElement.duration * position; 2369 2370 if (isNaN(audioElement.duration)) { 2371 return; 2372 } 2373 2374 audioElement.currentTime = pos; 2375 }); 2376 2377 talkify.messageHub.subscribe("tts-player", me.correlationId + ".controlcenter.request.volume", function (volume) { audioElement.volume = volume / 10; }); 2378 talkify.messageHub.subscribe("tts-player", me.correlationId + ".controlcenter.request.rate", function (rate) { 2379 me.settings.rate = rate; 2380 2381 talkify.messageHub.publish(me.correlationId + ".player.tts.ratechanged", me.settings.rate); 2382 }); 2383 2384 talkify.messageHub.subscribe("tts-player", me.correlationId + ".controlcenter.request.wordbreak", function (ms) { 2385 me.useWordBreak(ms); 2386 }); 2387 2388 talkify.messageHub.subscribe("tts-player", me.correlationId + ".controlcenter.request.pitch", function (value) { 2389 me.usePitch(value); 2390 }); 2391 2392 talkify.messageHub.subscribe("tts-player", me.correlationId + ".controlcenter.request.phonation.soft", function () { 2393 me.normalTone(); 2394 me.usePhonation("soft"); 2395 }); 2396 2397 talkify.messageHub.subscribe("tts-player", me.correlationId + ".controlcenter.request.phonation.normal", function () { 2398 me.normalTone(); 2399 me.usePhonation("normal"); 2400 }); 2401 2402 talkify.messageHub.subscribe("tts-player", me.correlationId + ".controlcenter.request.phonation.whisper", function () { 2403 me.usePhonation("normal"); 2404 me.whisper(); 2405 }); 2406 2407 if (me.playbar.instance) { 2408 me.playbar.instance 2409 .setMinRate(-5) 2410 .setMaxRate(5) 2411 .setRateStep(1) 2412 .setMinPitch(-10) 2413 .setMaxPitch(10)
2414 .setPitchStep(1); 2415 } 2416 2417 talkify.messageHub.publish(this.correlationId + ".player.tts.created"); 2418 } 2419 2420 function getPositions(requestId) { 2421 return talkify.http.get(talkify.config.remoteService.speechBaseUrl + "/marks?id=" + requestId); 2422 }; 2423 2424 initialize.apply(this); 2425 2426 this.downloadAudio = function (text) { 2427 var textType = "text"; 2428 var voice = (me.forcedVoice ? me.forcedVoice.name : ""); 2429 var pitch = me.settings.pitch; 2430 var wordbreak = me.settings.wordbreakms; 2431 var rate = me.settings.rate; 2432 var textToPlay = textType === "ssml" ? 2433 encodeURIComponent(text.replace(/\n/g, " ")) : 2434 encodeURIComponent(text.replace(/\n/g, " ")); 2435 2436 var obj = { 2437 text: text, 2438 rate: rate, 2439 volume: me.settings.volumeDb, 2440 voice: voice, 2441 whisper: me.settings.whisper, 2442 wordBreakMs: wordbreak, 2443 soft: me.settings.soft, 2444 pitch: pitch 2445 }; 2446 2447 var xhr = new XMLHttpRequest(); 2448 var url = talkify.config.remoteService.host + talkify.config.remoteService.speechBaseUrl + "?key=" + talkify.config.remoteService.apiKey; 2449 xhr.open("POST", url, true); 2450 xhr.setRequestHeader("Content-Type", "application/json"); 2451 xhr.onreadystatechange = function () { 2452 if (xhr.readyState === 4 && xhr.status >= 400 && xhr.status <= 599) { 2453 talkify.messageHub.publish(me.correlationId + ".player.tts.download.error", xhr); 2454 } 2455 2456 if (xhr.readyState === 4 && xhr.status === 200) { 2457 var link = document.createElement('a'); 2458 2459 link.href = URL.createObjectURL(xhr.response); 2460 link.download = "talkify"; 2461 2462 document.body.appendChild(link); 2463 2464 link.dispatchEvent( 2465 new MouseEvent('click', { 2466 bubbles: true, 2467 cancelable: true, 2468 view: window 2469 }) 2470 ); 2471 2472 setTimeout(function () { 2473 document.body.removeChild(link); 2474 }, 1000); 2475 2476 talkify.messageHub.publish(me.correlationId + ".player.tts.download.completed", null); 2477 } 2478 }; 2479 var data = JSON.stringify(obj); 2480 xhr.responseType = 'blob'; 2481 2482 talkify.messageHub.publish(me.correlationId + ".player.tts.download.started", null); 2483 2484 xhr.send(data); 2485 } 2486 2487 this.playAudio = function (item) { 2488 talkify.messageHub.publish(me.correlationId + ".player.tts.loading", item); 2489 2490 me.currentContext.item = item; 2491 me.currentContext.positions = []; 2492 2493 audioElement.controls = talkify.config.ui.audioControls.enabled && talkify.config.ui.audioControls.controlcenter === "native"; 2494 audioElement.onloadeddata = null; 2495 audioElement.onended = null; 2496 2497 var sources = audioElement.getElementsByTagName("source"); 2498 2499 var textType = talkify.config.useSsml && item.ssml ? "ssml" : "text"; 2500 2501 var textToPlay = textType === "ssml" ? 2502 encodeURIComponent(item.ssml.replace(/\n/g, " ")) : 2503 encodeURIComponent(item.text.replace(/\n/g, " ")); 2504 2505 var voice = item.voice || (this.forcedVoice ? this.forcedVoice.name : ""); 2506 2507 if (voice !== currentVoice) { 2508 talkify.messageHub.publish(this.correlationId + ".player.tts.voiceset", { name: voice }); 2509 currentVoice = voice; 2510 } 2511 2512 var pitch = (item.pitch || this.settings.pitch); 2513 2514 if (pitch !== currentPitch) { 2515 talkify.messageHub.publish(me.correlationId + ".player.tts.pitchchanged", pitch); 2516 currentPitch = pitch; 2517 } 2518 2519 var wordbreak = (item.wordbreakms || this.settings.wordbreakms); 2520 2521 if (wordbreak !== currentWordBreak) { 2522 talkify.messageHub.publish(me.correlationId + ".player.tts.wordbreakchanged", wordbreak); 2523 currentWordBreak = wordbreak; 2524 } 2525 2526 var rate = (item.rate || this.settings.rate); 2527 2528 if (rate !== currentRate) { 2529 talkify.messageHub.publish(me.correlationId + ".player.tts.ratechanged", rate); 2530 currentRate = rate; 2531 } 2532 2533 var requestId = talkify.generateGuid(); 2534 2535 var audioUrl = talkify.config.remoteService.host + 2536 talkify.config.remoteService.speechBaseUrl + 2537 "?texttype=" + textType + 2538 "&text=" + textToPlay + 2539 "&fallbackLanguage=" + this.settings.referenceLanguage.Language + 2540 "&voice=" + (voice) + 2541 "&rate=" + rate + 2542 "&key=" + talkify.config.remoteService.apiKey + 2543 "&whisper=" + (item.whisper || this.settings.whisper) + 2544 "&soft=" + (item.soft || this.settings.soft) + 2545 "&wordbreakms=" + wordbreak + 2546 "&volume=" + this.settings.volumeDb + 2547 "&pitch=" + pitch; 2548 2549 if (me.settings.useTextHighlight) { 2550 audioUrl += "&marksid=" + requestId; 2551 } 2552 2553 sources[0].src = audioUrl + "&format=mp3"; 2554 sources[1].src = audioUrl + "&format=wav"; 2555 2556 sources[1].onerror = function (e) { 2557 talkify.messageHub.publish(me.correlationId + ".player.tts.error", null); 2558 } 2559 2560 audioElement.load(); 2561 2562 audioElement.onloadeddata = function () { 2563 me.audioSource.pause(); 2564 2565 if (!me.settings.useTextHighlight) { 2566 talkify.messageHub.publish(me.correlationId + ".player.tts.loaded", me.currentContext.
2566item); 2567 me.audioSource.play(); 2568 return; 2569 } 2570 2571 getPositions(requestId).then(function (positions) { 2572 me.currentContext.positions = positions || []; 2573 2574 talkify.messageHub.publish(me.correlationId + ".player.tts.loaded", me.currentContext.item); 2575 me.audioSource.play(); 2576 }); 2577 }; 2578 2579 audioElement.onended = function () { 2580 talkify.messageHub.publish(me.correlationId + ".player.tts.ended", item); 2581 }; 2582 }; 2583 2584 this.usePhonation = function (phonation) { 2585 this.settings.soft = phonation === "soft"; 2586 2587 talkify.messageHub.publish(me.correlationId + ".player.tts.phonationchanged", phonation); 2588 2589 return this; 2590 }; 2591 2592 this.whisper = function () { 2593 this.settings.whisper = true; 2594 2595 talkify.messageHub.publish(me.correlationId + ".player.tts.whisperchanged", true); 2596 2597 return this; 2598 }; 2599 2600 this.normalTone = function () { 2601 this.settings.whisper = false; 2602 2603 talkify.messageHub.publish(me.correlationId + ".player.tts.whisperchanged", false); 2604 2605 return this; 2606 }; 2607 2608 this.useWordBreak = function (ms) { 2609 this.settings.wordbreakms = Math.max(0, ms); 2610 2611 talkify.messageHub.publish(me.correlationId + ".player.tts.wordbreakchanged", this.settings.wordbreakms); 2612 2613 return this; 2614 }; 2615 2616 this.useVolumeBaseline = function (volumeDb) { 2617 this.settings.volumeDb = volumeDb; 2618 2619 talkify.messageHub.publish(me.correlationId + ".player.tts.volumechanged", volumeDb); 2620 2621 return this; 2622 }; 2623 2624 this.usePitch = function (pitch) { 2625 this.settings.pitch = pitch; 2626 2627 talkify.messageHub.publish(me.correlationId + ".player.tts.pitchchanged", pitch); 2628 2629 return this; 2630 }; 2631 2632 setupBindings(); 2633}; 2634 2635talkify.TtsPlayer.prototype.constructor = talkify.TtsPlayer; 2636},{}],16:[function(require,module,exports){ 2637talkify = talkify || {}; 2638talkify.playlist = function () { 2639 var defaults = { 2640 useGui: false, 2641 useTextInteraction: false, 2642 domElements: null, 2643 exclusions: [], 2644 rootSelector: "body", 2645 events: { 2646 onEnded: null, 2647 onVoiceCommandListeningStarted: null, 2648 onVoiceCommandListeningEnded: null 2649 } 2650 }; 2651 2652 var s = JSON.parse(JSON.stringify(defaults)); 2653 2654 var p = null; 2655 2656 function isSupported() { 2657 var a = document.createElement("audio"); 2658 2659 return (typeof a.canPlayType === "function" && (a.canPlayType("audio/mpeg") !== "" || a.canPlayType("audio/wav") !== "")); 2660 } 2661 2662 function implementation(_settings, player) { 2663 var textextractor = new talkify.textextractor(); 2664 2665 var playlist = { 2666 queue: [], 2667 currentlyPlaying: null, 2668 refrenceText: "", 2669 referenceLanguage: { Culture: '', Language: -1 } 2670 }; 2671 2672 var settings = _settings; 2673 var playerHasBeenReplaced = false; 2674 2675 var commands = [ 2676 new talkify.KeyboardCommands(talkify.config.keyboardCommands), 2677 new talkify.SpeechCommands(talkify.config.voiceCommands) 2678 ]; 2679 2680 var voiceCommands = commands[1]; 2681 2682 for (var k = 0; k < commands.length; k++) { 2683 commands[k].onNext(playNext); 2684 commands[k].onPrevious(playPrevious); 2685 commands[k].onPlayPause(function () { 2686 if (player.paused()) { 2687 player.play(); 2688 } else { 2689 pause(); 2690 } 2691 }); 2692 } 2693 2694 voiceCommands.onListeningStarted(settings.events.onVoiceCommandListeningStarted); 2695 voiceCommands.onListeningEnded(settings.events.onVoiceCommandListeningEnded); 2696 2697 setupHubSubscribers(); 2698 2699 function setupHubSubscribers() { 2700 talkify.messageHub.subscribe("playlist", player.correlationId + ".player.*.ended", function (endedItem) { 2701 if (playlist.queue.indexOf(endedItem) === -1) { 2702 return; 2703 } 2704 2705 var item = getNextItem(); 2706 2707 if (!item) { 2708 settings.events.onEnded(); 2709 resetPlaybackStates(); 2710 return; 2711 } 2712 2713 playItem(item); 2714 }); 2715 2716 talkify.messageHub.subscribe("playlist", player.correlationId + ".player.*.unplayable", function () { 2717 if (playlist.currentlyPlaying) { 2718 playItem(playlist.currentlyPlaying); 2719 return; 2720 } 2721 2722 playFromBeginning(); 2723 }); 2724 2725 talkify.messageHub.subscribe("playlist", player.correlationId + ".controlcenter.request.textinteractiontoggled", function (enabled) { 2726 if (enabled) { 2727 enableTextInteraction(); 2728 } else { 2729 disableTextInteraction(); 2730 } 2731 }); 2732 2733 talkify.messageHub.subscribe("playlist", player.correlationId + ".controlcenter.request.playnext", playNext); 2734 talkify.messageHub.subscribe("playlist", player.correlationId + ".controlcenter.request.playprevious", playPrevious); 2735 talkify.messageHub.subscribe("playlist", player.correlationId + ".controlcenter.request.download", downloadAudio); 2736 } 2737 2738 function downloadAudio() { 2739 if (!player) { 2740 return; 2741 } 2742 2743 if (player.downloadAudio) { 2744 var separators = ['\.', '\?', '!', 'ã']; 2745 2746 var text = playlist.queue.map(function (x) { 2747 if (separators.indexOf(x.text.trim().substr(-1)) !== -1) { 2748 return x.text; 2749 } 2750 2751 return x.text + "."; 2752 }); 2753 2754 player.downloadAudio(text.join(" ")); 2755 } 2756 } 2757 2758 function playNext() { 2759 var item = getNextItem(); 2760 2761 if (!item) { 2762 return; 2763 } 2764 2765 playItem(item); 2766 } 2767 2768 function playPrevious() { 2769 var item = getPreviousItem(); 2770 2771 if (!item) { 2772 return; 2773 } 2774 2775 playItem(item); 2776 } 2777 2778 function reset() { 2779 playlist.queue = []; 2780 player.withReferenceLanguage({ Culture: '', Language: -1 }); 2781 playlist.currentlyPlaying = null; 2782 playlist.refrenceText = ""; 2783 } 2784 2785 function insertAt(index, items) { 2786 playlist.queue = playlist.queue.slice(0, index) 2787 .concat(items) 2788 .concat(playlist.queue.slice(index)); 2789 } 2790 2791 function push(items) { 2792 playlist.queue = playlist.queue.concat(items); 2793 } 2794 2795 function resetPlaybackStates() { 2796 for (var j = 0; j < playlist.queue.length; j++) { 2797 var item = playlist.queue[j]; 2798 2799 //TODO: Call player.resetItem? 2800 item.isPlaying = false;
2801 item.isLoading = false; 2802 item.element.classList.remove("playing"); 2803 } 2804 }; 2805 2806 function isPlaying() { 2807 for (var i = 0; i < playlist.queue.length; i++) { 2808 if (playlist.queue[i].isPlaying) { 2809 return true; 2810 } 2811 } 2812 2813 return false; 2814 } 2815 2816 function domElementExistsInQueue(element) { //TODO: might need to look at construct as <a><h3></h3></a> and whether "a" is "h3" since it is just a wrapper 2817 for (var j = 0; j < playlist.queue.length; j++) { 2818 var item = playlist.queue[j]; 2819 2820 if (!item) { 2821 continue; 2822 } 2823 2824 if (element === item.element) { 2825 return true; 2826 } 2827 } 2828 2829 return false; 2830 } 2831 2832 function playItem(item) { 2833 if (!playerHasBeenReplaced && item && item.isPlaying) { 2834 if (player.paused()) { 2835 player.play(); 2836 } else { 2837 player.pause(); 2838 } 2839 } 2840 2841 playerHasBeenReplaced = false; 2842 2843 resetPlaybackStates(); 2844 2845 if (playlist.currentlyPlaying) { 2846 playlist.currentlyPlaying.element.innerHTML = playlist.currentlyPlaying.originalElement.innerHTML; 2847 } 2848 2849 playlist.currentlyPlaying = item; 2850 2851 var previous = getPreviousItem(); 2852 var next = getNextItem(); 2853 2854 talkify.messageHub.publish(player.correlationId + ".playlist.playing", { isFirst: !previous, isLast: !next }); 2855 2856 p = player.playItem(item); 2857 }; 2858 2859 function createItems(text, ssml, element) { 2860 var safeMaxQuerystringLength = window.talkify.config.maximumTextLength || 1000; 2861 2862 var items = []; 2863 2864 if (text.length > safeMaxQuerystringLength) { 2865 var chuncks = getSafeTextChunks(text, safeMaxQuerystringLength); 2866 2867 element.innerHTML = ''; 2868 2869 for (var i = 0; i < chuncks.length; i++) { 2870 var p = document.createElement("p"); 2871 p.textContent = chuncks[i]; 2872 2873 element.appendChild(p); 2874 2875 items = items.concat(template(chuncks[i], null, p)); 2876 } 2877 2878 return items; 2879 } 2880 2881 items = items.concat(template(text, ssml, element)); 2882 2883 return items; 2884 2885 function template(t, s, el) { 2886 el = el || document.createElement("span"); 2887 var clone = el.cloneNode(true); 2888 2889 var wordbreakms = el.getAttribute("data-talkify-wordbreakms"); 2890 var whisper = el.getAttribute("data-talkify-whisper"); 2891 var phonation = el.getAttribute("data-talkify-phonation"); 2892 var voice = el.getAttribute("data-talkify-voice") || null; 2893 var pitch = el.getAttribute("data-talkify-pitch") || null; 2894 var rate = el.getAttribute("data-talkify-rate") || null; 2895 var prefix = el.getAttribute("data-talkify-prefix") || null; 2896 2897 var response = []; 2898 2899 var text = prefix ? prefix + ": " + t : t; 2900 var ssml = prefix ? prefix + ": " + s : s; 2901 2902 response.push({ 2903 text: text, 2904 ssml: ssml, 2905 prefix: prefix, 2906 preview: t.substr(0, 40), 2907 element: el, 2908 originalElement: clone, 2909 isPlaying: false, 2910 isLoading: false, 2911 wordbreakms: wordbreakms ? parseInt(wordbreakms) : null, 2912 whisper: whisper ? whisper === "true" : null, 2913 soft: phonation ? phonation === "soft" : null, 2914 voice: voice, 2915 pitch: pitch ? parseInt(pitch) : null, 2916 rate: rate ? parseInt(rate) : null 2917 }); 2918 2919 return response; 2920 } 2921 } 2922 2923 function getSafeTextChunks(text, safeMaxQuerystringLength) { 2924 var chuncks = []; 2925 var remaining = text; 2926 2927 var chunck = getNextChunck(text, safeMaxQuerystringLength); 2928 chuncks.push(chunck); 2929 2930 while (chunck) { 2931 remaining = remaining.replace(chunck, ""); 2932 2933 chunck = getNextChunck(remaining, safeMaxQuerystringLength); 2934 2935 if (chunck) { 2936 chuncks.push(chunck); 2937 } 2938 } 2939 2940 return chuncks; 2941 } 2942 2943 function getNextChunck(text, safeMaxQuerystringLength) { 2944 if (!text) { 2945 return null; 2946 } 2947 2948 if (text.length < safeMaxQuerystringLength) { 2949 return text; 2950 } 2951 2952 var breakAt = text.substr(0, safeMaxQuerystringLength).lastIndexOf('.'); 2953 breakAt = breakAt > -1 ? breakAt : text.substr(0, safeMaxQuerystringLength).lastIndexOf('ã'); 2954 breakAt = breakAt > -1 ? breakAt : safeMaxQuerystringLength; 2955 2956 return text.substr(0, breakAt + 1); 2957 } 2958 2959 function play(item) { 2960 if (!item) { 2961 if (playlist.queue.length === 0) { 2962 return; 2963 } 2964 2965 playFromBeginning(); 2966 2967 return; 2968 } 2969 2970 playItem(item); 2971 } 2972 2973 function pause() { 2974 player.pause(); 2975 } 2976 2977 function setupItemForUserInteraction(item) { 2978 item.element.style.cursor = "pointer"; 2979 item.element.classList.add("talkify-highlight"); 2980 2981 removeEventListeners("click", item.element); 2982 addEventListener("click", item.element, textInteractionEventListener); 2983 2984 function textInteractionEventListener() { 2985 play(item); 2986 } 2987 } 2988 2989 function removeUserInteractionForItem(item) {
2990 item.element.style.cursor = "inherit"; 2991 item.element.classList.remove("talkify-highlight"); 2992 2993 removeEventListeners("click", item.element); 2994 } 2995 2996 function markTables() { 2997 if (!settings.tableConfig) { 2998 return; 2999 } 3000 3001 talkify.tableReader.markTables(settings.tableConfig); 3002 } 3003 3004 function extractTables() { 3005 if (!settings.tableConfig) { 3006 return []; 3007 } 3008 3009 return Array.from(document.querySelectorAll('.talkify-tts-table')); 3010 } 3011 3012 function initialize() { 3013 reset(); 3014 3015 markTables(); 3016 3017 if (!settings.domElements) { 3018 settings.domElements = textextractor.extract(settings.rootSelector, settings.exclusions); 3019 } 3020 3021 for (var i = 0; i < settings.domElements.length; i++) { 3022 var text, ssml; 3023 var element = null; 3024 3025 if (typeof settings.domElements[i] === "string") { 3026 text = settings.domElements[i]; 3027 } else { 3028 element = settings.domElements[i]; 3029 3030 ssml = convertToSsml(element); 3031 3032 text = element.innerText.trim(); 3033 3034 // Customisation: prevent voice reading attributions ("[1], [2] etc") 3035 text = text.replace(/(\[\d*\])/g, ''); 3036 } 3037 3038 if (text === "") { 3039 continue; 3040 } 3041 3042 push(createItems(text, ssml, element)); 3043 3044 if (text.length > playlist.refrenceText.length) { 3045 playlist.refrenceText = text; 3046 } 3047 } 3048 3049 var tables = extractTables(); 3050 3051 for (var t = 0; t < tables.length; t++) { 3052 var cells = Array.from(tables[t].querySelectorAll(".talkify-tts-tablecell")); 3053 3054 insertChunckOfElements(cells); 3055 } 3056 3057 if (settings.useTextInteraction) { 3058 for (var j = 0; j < playlist.queue.length; j++) { 3059 var item = playlist.queue[j]; 3060 3061 if (j > 0) { 3062 var isSameAsPrevious = item.element === playlist.queue[j - 1].element; 3063 3064 if (isSameAsPrevious) { 3065 continue; 3066 } 3067 } 3068 3069 setupItemForUserInteraction(item); 3070 } 3071 } 3072 3073 talkify.messageHub.publish(player.correlationId + ".playlist.loaded"); 3074 3075 if (settings.useTextInteraction) { 3076 talkify.messageHub.publish(player.correlationId + ".playlist.textinteraction.enabled"); 3077 } else { 3078 talkify.messageHub.publish(player.correlationId + ".playlist.textinteraction.disabled"); 3079 } 3080 } 3081 3082 function convertToSsml(element) { 3083 if (!talkify.config.useSsml) { 3084 return null; 3085 } 3086 3087 var ssmlMappings = { 3088 h1: { 3089 start: '###emphasis level="strong">', 3090 end: '###/emphasis>', 3091 trim: false 3092 }, 3093 h2: { 3094 start: '###emphasis level="strong">', 3095 end: '###/emphasis>', 3096 trim: false 3097 }, 3098 h3: { 3099 start: '###emphasis level="strong">', 3100 end: '###/emphasis>', 3101 trim: false 3102 }, 3103 b: { 3104 start: '###emphasis level="strong">', 3105 end: '###/emphasis>', 3106 trim: false 3107 }, 3108 strong: { 3109 start: '###emphasis level="strong">', 3110 end: '###/emphasis>', 3111 trim: false 3112 }, 3113 em: { 3114 start: '###emphasis level="strong">', 3115 end: '###/emphasis>', 3116 trim: false 3117 }, 3118 i: { 3119 start: '###emphasis level="reduced">', 3120 end: '###/emphasis>', 3121 trim: false 3122 }, 3123 br: { 3124 start: '###break strength="x-strong">###/break>', 3125 end: '', 3126 trim: true 3127 } 3128 }; 3129 3130 var htmlEntities = {}; 3131 htmlEntities[" "] = " "; 3132 htmlEntities["<"] = "<"; 3133 htmlEntities[">"] = ">"; 3134 htmlEntities["&qout;"] = "\""; 3135 htmlEntities["'"] = "'"; 3136 htmlEntities["&"] = "&"; 3137 3138 var ssml = element.innerHTML.replace(/ +/g, " ").replace(/(\r\n|\n|\r)/gm, "").trim(); 3139 3140 for (var key in htmlEntities) { 3141 ssml = ssml.replace(new RegExp(key, 'g'), htmlEntities[key]); 3142 } 3143 3144 for (var key in ssmlMappings) { 3145 var mapping = ssmlMappings[key]; 3146 3147 var startTagMatches = ssml.match(new RegExp('<' + key + '+(>|.*?[^?]>)', 'gi')) || []; 3148 3149 for (var j = 0; j < startTagMatches.length; j++) { 3150 if (startTagMatches[j] !== '<' + key + '>' && startTagMatches[j].indexOf('<' + key + ' ') !== 0) { 3151 continue; 3152 } 3153 3154 ssml = ssml.replace(startTagMatches[j], mapping.start); 3155 3156 if (mapping.trim) { 3157 ssml = ssml.split(mapping.start).map(function (x) { return x.trim() }).join(mapping.start); 3158 } 3159 } 3160 3161 ssml = ssml.split('</' + key + '>').map(function (x, i) { return mapping.trim ? x.trim() : x; }).join(mapping.end); 3162 } 3163 3164 ssml = ssml.replace(/<[^>]*>?/gm, ''); //removes html-tags 3165 ssml = ssml.replace(/\s+/g, ' ');
3165 //removes multiple whitespaces 3166 ssml = ssml.split('###').join('<'); 3167 3168 return ssml; 3169 } 3170 3171 function getNextItem() { 3172 var currentQueuePosition = playlist.queue.indexOf(playlist.currentlyPlaying); 3173 3174 if (currentQueuePosition === playlist.queue.length - 1) { 3175 return null; 3176 } 3177 3178 return playlist.queue[currentQueuePosition + 1]; 3179 } 3180 3181 function getPreviousItem() { 3182 var currentQueuePosition = playlist.queue.indexOf(playlist.currentlyPlaying); 3183 3184 if (currentQueuePosition === 0) { 3185 return null; 3186 } 3187 3188 return playlist.queue[currentQueuePosition - 1]; 3189 } 3190 3191 function playFromBeginning() { 3192 if (!talkify.config.remoteService.active || !hasTalkifyPlayer()) { 3193 onComplete({ Cultures: [], Language: -1 }); 3194 3195 return; 3196 } 3197 3198 var text = playlist.refrenceText.length <= 1000 ? playlist.refrenceText : playlist.refrenceText.substr(0, 1000); 3199 3200 talkify.http.get(talkify.config.remoteService.languageBaseUrl + "/detect?text=" + text) 3201 .then(function (data) { 3202 onComplete(data); 3203 }) 3204 .catch(function(){ 3205 onComplete({ Cultures: [], Language: -1 }); 3206 }); 3207 3208 function onComplete(refLang) { 3209 playlist.referenceLanguage = { Culture: refLang.Cultures[0], Language: refLang.Language }; 3210 player.withReferenceLanguage(playlist.referenceLanguage); 3211 3212 playItem(playlist.queue[0]); 3213 } 3214 } 3215 3216 function hasTalkifyPlayer(voice) { 3217 return player instanceof talkify.TtsPlayer; 3218 } 3219 3220 3221 function insertChunckOfElements(elements) { 3222 if (!elements || elements.length === 0) { 3223 return; 3224 } 3225 3226 if (!playlist.queue.length) { 3227 playlist.queue = elements.map(function (x) { 3228 var text = x.innerText.trim(); 3229 var ssml = convertToSsml(x); 3230 3231 return createItems(text, ssml, x); 3232 }).flat(); 3233 3234 return; 3235 } 3236 3237 var baseline = elements[0]; 3238 var documentPositionFollowing = 4; 3239 3240 for (var j = 0; j < playlist.queue.length; j++) { 3241 var item = playlist.queue[j]; 3242 3243 var isSelectionAfterQueueItem = baseline.compareDocumentPosition(item.element) == documentPositionFollowing; 3244 var shouldAddToBottom = j === playlist.queue.length - 1; 3245 3246 if (isSelectionAfterQueueItem || shouldAddToBottom) { 3247 var queueItems = elements.map(function (x) { 3248 var text = x.innerText.trim(); 3249 var ssml = convertToSsml(x); 3250 3251 return createItems(text, ssml, x); 3252 }).flat(); 3253 3254 var insertAtIndex = isSelectionAfterQueueItem ? j : j + 1; 3255 3256 insertAt(insertAtIndex, queueItems); 3257 3258 return; 3259 } 3260 } 3261 } 3262 3263 function insertElement(element) { 3264 if (!element) { 3265 return []; 3266 } 3267 3268 var items = []; 3269 3270 var text = element.innerText; 3271 3272 if (text.trim() === "") { 3273 return items; 3274 } 3275 3276 if (domElementExistsInQueue(element)) { 3277 return items; 3278 } 3279 3280 var documentPositionFollowing = 4; 3281 3282 for (var j = 0; j < playlist.queue.length; j++) { 3283 var item = playlist.queue[j]; 3284 3285 var isSelectionAfterQueueItem = element.compareDocumentPosition(item.element) == documentPositionFollowing; 3286 3287 if (isSelectionAfterQueueItem) { 3288 var queueItems = createItems(text, null, element); 3289 3290 insertAt(j, queueItems); 3291 3292 items = items.concat(queueItems); 3293 3294 break; 3295 } 3296 3297 var shouldAddToBottom = j === playlist.queue.length - 1; 3298 3299 if (shouldAddToBottom) { 3300 var qItems = createItems(text, null, element); 3301 3302 push(qItems); 3303 3304 items = items.concat(qItems); 3305 3306 break; 3307 } 3308 } 3309 3310 return items; 3311 } 3312 3313 function replayCurrent() { 3314 if (!playlist.currentlyPlaying) { 3315 return; 3316 } 3317 3318 playlist.currentlyPlaying.isPlaying = false;
3319 play(playlist.currentlyPlaying); 3320 } 3321 3322 //TODO: Extract and reuse? 3323 function removeEventListeners(eventType, element) { 3324 if (!element.trackedEvents || !element.trackedEvents[eventType]) { 3325 return; 3326 } 3327 3328 for (var i = 0; i < element.trackedEvents[eventType].length; i++) { 3329 element.removeEventListener(eventType, element.trackedEvents[eventType][i]); 3330 } 3331 } 3332 3333 function addEventListener(eventType, element, listener) { 3334 if (!element.trackedEvents) { 3335 element.trackedEvents = []; 3336 } 3337 3338 if (!element.trackedEvents[eventType]) { 3339 element.trackedEvents[eventType] = []; 3340 } 3341 3342 element.trackedEvents[eventType].push(listener); 3343 element.addEventListener(eventType, listener); 3344 } 3345 3346 function enableTextInteraction() { 3347 settings.useTextInteraction = true; 3348 3349 for (var i = 0; i < playlist.queue.length; i++) { 3350 setupItemForUserInteraction(playlist.queue[i]); 3351 } 3352 3353 talkify.messageHub.publish(player.correlationId + ".playlist.textinteraction.enabled"); 3354 } 3355 3356 function disableTextInteraction() { 3357 settings.useTextInteraction = false; 3358 3359 for (var i = 0; i < playlist.queue.length; i++) { 3360 removeUserInteractionForItem(playlist.queue[i]); 3361 } 3362 3363 talkify.messageHub.publish(player.correlationId + ".playlist.textinteraction.disabled"); 3364 } 3365 3366 initialize(); 3367 3368 return { 3369 getQueue: function () { return playlist.queue; }, 3370 play: play, 3371 pause: pause, 3372 replayCurrent: replayCurrent, 3373 insert: insertElement, 3374 isPlaying: isPlaying, 3375 enableTextInteraction: enableTextInteraction, 3376 disableTextInteraction: disableTextInteraction, 3377 setPlayer: function (p) { 3378 talkify.messageHub.unsubscribe("playlist", player.correlationId + ".player.*.ended"); 3379 talkify.messageHub.unsubscribe("playlist", player.correlationId + ".player.tts.unplayable"); 3380 talkify.messageHub.unsubscribe("playlist", player.correlationId + ".controlcenter.request.textinteractiontoggled"); 3381 talkify.messageHub.unsubscribe("playlist", player.correlationId + ".controlcenter.request.playnext"); 3382 talkify.messageHub.unsubscribe("playlist", player.correlationId + ".controlcenter.request.playprevious"); 3383 talkify.messageHub.unsubscribe("playlist", player.correlationId + ".controlcenter.request.download"); 3384 3385 player = p; 3386 player.withReferenceLanguage(playlist.referenceLanguage); 3387 playerHasBeenReplaced = true; 3388 3389 setupHubSubscribers(); 3390 3391 replayCurrent(); 3392 }, 3393 dispose: function () { 3394 resetPlaybackStates(); 3395 3396 for (var i = 0; i < playlist.queue.length; i++) { 3397 var item = playlist.queue[i]; 3398 3399 removeUserInteractionForItem(item); 3400 } 3401 3402 for (var i = 0; i < commands.length; i++) { 3403 commands[i].dispose(); 3404 } 3405 3406 talkify.messageHub.unsubscribe("playlist", player.correlationId + ".player.*.ended"); 3407 talkify.messageHub.unsubscribe("playlist", player.correlationId + ".player.tts.unplayable"); 3408 talkify.messageHub.unsubscribe("playlist", player.correlationId + ".controlcenter.request.textinteractiontoggled"); 3409 talkify.messageHub.unsubscribe("playlist", player.correlationId + ".controlcenter.request.playnext"); 3410 talkify.messageHub.unsubscribe("playlist", player.correlationId + ".controlcenter.request.playprevious"); 3411 }, 3412 startListeningToVoiceCommands: function () { 3413 voiceCommands.start(); 3414 }, 3415 stopListeningToVoiceCommands: function () { 3416 voiceCommands.stop(); 3417 } 3418 } 3419 } 3420 3421 return { 3422 begin: function () { 3423 s = JSON.parse(JSON.stringify(defaults)); 3424 p = null; 3425 3426 return { 3427 withTextInteraction: function () { 3428 s.useTextInteraction = true; 3429 3430 return this; 3431 }, 3432 withTalkifyUi: function () { 3433 s.useGui = true; 3434 3435 return this; 3436 }, 3437 excludeElements: function (elementsSelectors) { 3438 s.exclusions = elementsSelectors; 3439 3440 return this; 3441 }, 3442 withRootSelector: function (rootSelector) { 3443 s.rootSelector = rootSelector; 3444 3445 return this; 3446 }, 3447 withElements: function (elements) { 3448 s.domElements = elements; 3449 3450 return this; 3451 }, 3452 withTables: function (tableConfig) { 3453 s.tableConfig = tableConfig; 3454 3455 return this; 3456 }, 3457 usingPlayer: function (player) { 3458 p = player; 3459 3460 return this; 3461 }, 3462 subscribeTo: function (events) { 3463 s.events.onEnded = events.onEnded || function () { }; 3464 s.events.onVoiceCommandListeningStarted = events.onVoiceCommandListeningStarted || function () { }; 3465 s.events.onVoiceCommandListeningEnded = events.onVoiceCommandListeningEnded || function () { }; 3466 3467 3468 return this; 3469 }, 3470 build: function () { 3471 if (!isSupported()) { 3472 throw new Error("Not supported. The browser needs to support mp3 or wav HTML5 Audio."); 3473 } 3474 3475 if (!p) { 3476 throw new Error("A player must be provided. Please use the 'usingPlayer' method to provide one."); 3477 } 3478 3479 s.events.onEnded = s.events.onEnded || function () { }; 3480 s.events.onVoiceCommandListeningStarted = s.events.onVoiceCommandListeningStarted || function () { }; 3481 s.events.onVoiceCommandListeningEnded = s.events.onVoiceCommandListeningEnded || function () { }; 3482 3483 return new implementation(s, p); 3484 } 3485 } 3486 } 3487 3488 }; 3489}; 3490},{}],17:[function(require,module,exports){ 3491talkify = talkify || {}; 3492 3493talkify.SpeechCommands = function (speechCommandConfig) { 3494 if (!speechCommandConfig.enabled || !window.webkitSpeechRecognition) { 3495 var noop = function () { }; 3496 3497 return { 3498 onPrevious: noop, 3499 onNext: noop, 3500 onPlayPause: noop, 3501 start: noop, 3502 onListeningStarted: noop, 3503 onListeningEnded: noop, 3504 dispose: noop 3505 } 3506 } 3507 3508 var SpeechRecognition = window.webkitSpeechRecognition; 3509 3510 var isListening = false;
3511 var onNextCallback = function () { }; 3512 var onPreviousCallback = function () { }; 3513 var onPlayPauseCallback = function () { }; 3514 var onListeningStartedCallback = function () { }; 3515 var onListeningEndedCallback = function () { }; 3516 3517 var recognition = new SpeechRecognition(); 3518 3519 recognition.lang = 'en-US'; 3520 recognition.interimResults = false; 3521 recognition.maxAlternatives = 1; 3522 3523 recognition.onstart = function () { 3524 isListening = true; 3525 onListeningStartedCallback(); 3526 } 3527 3528 recognition.onresult = function (event) { 3529 var transcript = event.results[event.results.length - 1][0].transcript; 3530 3531 var matchingCommandName = evaluate(transcript, speechCommandConfig.commands); 3532 3533 if (speechCommandConfig.commands[matchingCommandName] === speechCommandConfig.commands.playPause) { 3534 onPlayPauseCallback(); 3535 } else if (speechCommandConfig.commands[matchingCommandName] === speechCommandConfig.commands.next) { 3536 onNextCallback(); 3537 } else if (speechCommandConfig.commands[matchingCommandName] === speechCommandConfig.commands.previous) { 3538 onPreviousCallback(); 3539 } 3540 } 3541 3542 recognition.onspeechend = function () { 3543 recognition.stop(); 3544 isListening = false; 3545 onListeningEndedCallback(); 3546 } 3547 3548 function evaluate(transcript, commands) { 3549 var wordsInTranscript = transcript.split(' '); 3550 var possibleMatches = []; 3551 3552 for (var key in commands) { 3553 if (!commands.hasOwnProperty(key)) { 3554 continue; 3555 } 3556 3557 var phrases = speechCommandConfig.commands[key]; 3558 3559 for (var i = 0; i < phrases.length; i++) { 3560 if (phrases[i].toLowerCase() === transcript) { 3561 //exact match 3562 return key; 3563 } 3564 3565 var match = phrases[i].split(' ').filter(function (word) { 3566 return wordsInTranscript.indexOf(word.toLowerCase()) > -1; 3567 })[0]; 3568 3569 //any word in phrase mathes 3570 if (match) { 3571 possibleMatches.push(key); 3572 break; 3573 } 3574 } 3575 } 3576 3577 if (possibleMatches.length > 0) { 3578 var bestValue = 0; 3579 var bestCommand = null; 3580 3581 for (var j = 0; j < possibleMatches.length; j++) { 3582 var temp = Math.max.apply(Math, 3583 speechCommandConfig.commands[possibleMatches[j]].map(function (phrase) { 3584 return levenshtein(phrase, transcript); 3585 })); 3586 3587 if (temp > bestValue) { 3588 bestValue = temp; 3589 bestCommand = possibleMatches[j]; 3590 } 3591 } 3592 3593 return bestCommand; 3594 } 3595 3596 return null; 3597 3598 } 3599 3600 function levenshtein(s1, s2) { 3601 var longer = s1; 3602 var shorter = s2; 3603 if (s1.length < s2.length) { 3604 longer = s2; 3605 shorter = s1; 3606 } 3607 var longerLength = longer.length; 3608 if (longerLength === 0) { 3609 return 1.0; 3610 } 3611 return (longerLength - editDistance(longer, shorter)) / parseFloat(longerLength); 3612 } 3613 3614 function editDistance(s1, s2) { 3615 s1 = s1.toLowerCase(); 3616 s2 = s2.toLowerCase(); 3617 3618 var costs = new Array(); 3619 for (var i = 0; i <= s1.length; i++) { 3620 var lastValue = i; 3621 for (var j = 0; j <= s2.length; j++) { 3622 if (i === 0) 3623 costs[j] = j; 3624 else { 3625 if (j > 0) { 3626 var newValue = costs[j - 1]; 3627 if (s1.charAt(i - 1) != s2.charAt(j - 1)) 3628 newValue = Math.min(Math.min(newValue, lastValue), 3629 costs[j]) + 1; 3630 costs[j - 1] = lastValue; 3631 lastValue = newValue; 3632 } 3633 } 3634 } 3635 if (i > 0) 3636 costs[s2.length] = lastValue; 3637 } 3638 return costs[s2.length]; 3639 } 3640
3641 if (speechCommandConfig.keyboardActivation.enabled) { 3642 document.addEventListener("keyup", 3643 function (e) { 3644 if (!e.ctrlKey) { 3645 return; 3646 } 3647 3648 if (isListening) { 3649 return; 3650 } 3651 3652 var key = e.keyCode ? e.keyCode : e.which; 3653 3654 if (key === speechCommandConfig.keyboardActivation.key) { 3655 recognition.start(); 3656 } 3657 }); 3658 } 3659 3660 return { 3661 onPrevious: function (callback) { 3662 onPreviousCallback = callback; 3663 }, 3664 onNext: function (callback) { 3665 onNextCallback = callback; 3666 }, 3667 onPlayPause: function (callback) { 3668 onPlayPauseCallback = callback; 3669 }, 3670 start: function () { 3671 if (isListening) { 3672 return; 3673 } 3674 3675 recognition.start(); 3676 }, 3677 onListeningStarted: function (callback) { 3678 onListeningStartedCallback = callback; 3679 }, 3680 onListeningEnded: function (callback) { 3681 onListeningEndedCallback = callback; 3682 }, 3683 dispose: function () {} 3684 } 3685}; 3686},{}],18:[function(require,module,exports){ 3687talkify = talkify || {}; 3688 3689talkify.selectionActivator = function () { 3690 var inlineElements = ['a', 'span', 'b', 'big', 'i', 'small', 'tt', 'abbr', 'acronym', 'cite', 'code', 'dfn', 'em', 'kbd', 'strong', 'samp', 'var', 'a', 'bdo', 'q', 'sub', 'sup', 'label']; 3691 var forbiddenElementsString = ['img', 'map', 'object', 'script', 'button', 'input', 'select', 'textarea', 'style', 'code', 'rp', 'rt']; 3692 var timeoutId, playlist, player, x, y, activatorHtml, controlcenterHtml, currentSelection; 3693 var orchestrateTimeout; 3694 var activated = false; 3695 var currentContext = {}; 3696 var settings = { 3697 exclude: [], 3698 enhancedVisibility: false, 3699 voice: { name: 'Zira' }, 3700 highlightText: false, 3701 buttonText: "Listen", 3702 rate: 0 3703 }; 3704 3705 var validNodeTypes = [1, 3]; 3706 3707 function getElementsInSelection() { 3708 if (!currentSelection) { 3709 return []; 3710 } 3711 3712 var anchorNode = currentContext.anchorNode; 3713 3714 if (!anchorNode) { 3715 return []; 3716 } 3717 3718 var nodes = getNodesInSelection(currentContext.range.commonAncestorContainer, currentContext.leftToRight); 3719 3720 return nodes; 3721 } 3722 3723 function createItemsFromNodes(nodes) { 3724 var items = []; 3725 3726 if (nodes.length === 1) { 3727 return [surroundNode(nodes[0], currentContext.range.startOffset, currentContext.range.endOffset)]; 3728 } 3729 3730 for (var i = 0; i < nodes.length; i++) { 3731 if (i === 0) { 3732 if (currentContext.leftToRight) { 3733 items.push(surroundNode(nodes[i], currentContext.anchorOffset, currentContext.anchorNode.textContent.length)) 3734 } else { 3735 items.push(surroundNode(nodes[i], currentContext.focusOffset, currentContext.focusNode.textContent.length)) 3736 } 3737 3738 continue; 3739 } 3740 3741 if (i === nodes.length - 1) { 3742 if (currentContext.leftToRight) { 3743 items.push(surroundNode(nodes[i], 0, currentContext.focusOffset)) 3744 } 3745 else { 3746 items.push(surroundNode(nodes[i], 0, currentContext.anchorOffset)) 3747 } 3748 3749 continue; 3750 } 3751 3752 items.push(surroundNode(nodes[i], 0, 0)) 3753 } 3754 3755 return items; 3756 } 3757 3758 function getNodesInSelection(element, leftToRight) { 3759 if (element === document.body.parentElement) { 3760 return []; 3761 } 3762 3763 var nodes = getNodesInBetween(element, leftToRight); 3764 3765 if (!leftToRight) { 3766 nodes.reverse(); 3767 } 3768 3769 return nodes; 3770 } 3771 3772 function getNodesInBetween(element, leftToRight) { 3773 var nodes = []; 3774 3775 if (element.nodeType === 3) { 3776 return [element]; 3777 } 3778 3779 var inlineGroups = getInlineGroups(element.childNodes); 3780 3781 for (var i = 0; i < element.childNodes.length; i++) { 3782 var node = leftToRight ? 3783 element.childNodes[i] : 3784 element.childNodes[element.childNodes.length - 1 - i]; 3785 3786 var tagName = node.tagName != undefined ? node.tagName.toLowerCase() : undefined; 3787
3788 if (forbiddenElementsString.indexOf(tagName) !== -1 || settings.exclude.indexOf(node) !== -1) { 3789 continue; 3790 } 3791 3792 if (validNodeTypes.indexOf(node.nodeType) === -1) { 3793 continue; 3794 } 3795 3796 if (node.textContent.trim() === "") { 3797 continue; 3798 } 3799 3800 var group = inlineGroups.filter(function (x) { return x.indexOf(node) > -1; })[0]; 3801 3802 if (group) { 3803 nodes.push(group); 3804 3805 var indexAfterGroup = i + group.length; 3806 i = indexAfterGroup; 3807 3808 continue; 3809 } 3810 3811 if (node.nodeType === 1) { 3812 nodes = nodes.concat(getNodesInBetween(node, leftToRight)); 3813 } 3814 3815 if (node.nodeType === 1) { 3816 continue; 3817 } 3818 3819 if (currentSelection.containsNode(node, true)) { 3820 nodes.push(node); 3821 } 3822 } 3823 3824 return nodes; 3825 } 3826 3827 function getInlineGroups(nodes) { 3828 var groups = []; 3829 3830 for (var i = 0; i < nodes.length; i++) { 3831 var node = nodes[i]; 3832 3833 if (!currentSelection.containsNode(node, true)) { 3834 continue; 3835 } 3836 3837 var isBlockElement = node.nodeType === 1 && inlineElements.indexOf(node.tagName.toLowerCase()) === -1; 3838 3839 if (isBlockElement) { 3840 continue; 3841 } 3842 3843 if (validNodeTypes.indexOf(node.nodeType) === -1) { 3844 continue; 3845 } 3846 3847 if (node.textContent.trim() === "") { 3848 continue; 3849 } 3850 3851 var group = [node]; 3852 3853 while (i < nodes.length && 3854 node.nextSibling && 3855 node.nextSibling.textContent.trim() !== "" && 3856 currentSelection.containsNode(node.nextSibling, true) && 3857 (node.nextSibling.nodeType === 3 || inlineElements.indexOf((node.nextSibling.tagName || "").toLowerCase()) !== -1)) { 3858 group.push(node.nextSibling); 3859 3860 i++; 3861 node = nodes[i]; 3862 } 3863 3864 if (group.length > 1) { 3865 groups.push(group); 3866 } 3867 } 3868 3869 return groups; 3870 } 3871 3872 function surroundNode(node, startOffset, endOffset) { 3873 var range = document.createRange(); 3874 var newParent = document.createElement('span'); 3875 newParent.classList.add("talkify-selected-text"); 3876 3877 if (Array.isArray(node)) { 3878 var originalNodes = []; 3879 node[0].parentNode.insertBefore(newParent, node[0]); 3880 3881 for (var i = 0; i < node.length; i++) { 3882 newParent.appendChild(node[i].cloneNode(true)); 3883 3884 originalNodes.push(node[i].cloneNode(true)); 3885 3886 node[i].parentNode.removeChild(node[i]); 3887 } 3888 3889 return { 3890 newNode: newParent, 3891 originalNode: originalNodes 3892 }; 3893 } 3894 3895 var original = node.cloneNode(true); 3896 3897 if (node.nodeType === 1) { 3898 node.classList.add("talkify-selected-text"); 3899 3900 return { 3901 newNode: node, 3902 originalNode: original 3903 }; 3904 } 3905 3906 if (startOffset === 0 && endOffset === 0) { 3907 range.setStart(node, 0); 3908 range.setEnd(node, node.textContent.length); 3909 range.surroundContents(newParent); 3910 } else { 3911 range.setStart(node, 0); 3912 range.setEnd(node, node.textContent.length); 3913 range.surroundContents(newParent); 3914 } 3915 3916 return { 3917 originalNode: original, 3918 newNode: newParent 3919 }; 3920 } 3921 3922 function deactivate() { 3923 activated = false; 3924 3925 document.removeEventListener('mouseup', onMouseUp, false); 3926 document.removeEventListener('keyup', onMouseUp, false); 3927 } 3928 3929 function activate() { 3930 if (activated) { 3931 console.warn("Selection activator is already activated"); 3932 return; 3933 } 3934 3935 activated = true; 3936 3937 if (settings.exclude && NodeList.prototype.isPrototypeOf(settings.exclude)) { 3938 settings.exclude = Array.from(settings.exclude); 3939 } 3940 3941 document.addEventListener('mouseup', onMouseUp, false); 3942 document.addEventListener('keyup', onMouseUp, false); 3943 document.addEventListener('mousemove', onMouseUpdate, false); 3944 document.addEventListener('mouseenter', onMouseUpdate, false); 3945 3946 talkify.domExtensions.recordEvents.begin(); 3947 } 3948 3949 function onMouseUp(e) { 3950 currentSelection = window.getSelection(); 3951 3952 if (currentSelection.type === "Range" && currentSelection.toString().trim()) { 3953 if (currentContext.anchorNode === currentSelection.anchorNode && currentContext.focusNode === currentSelection.focusNode) { 3954 return; 3955 } 3956 3957 if (activatorHtml && (activatorHtml.contains(currentSelection.focusNode) || activatorHtml.contains(currentSelection.anchorNode))) { 3958 return; 3959 } 3960 3961 if (currentContext && currentContext.active) { 3962 return; 3963 } 3964 3965 if (orchestrateTimeout) { 3966 clearTimeout(orchestrateTimeout); 3967 } 3968 3969 orchestrateTimeout = setTimeout(orchestrateAfterSelection, 200); 3970 } 3971 } 3972 3973 function orchestrateAfterSelection() { 3974 if (currentSelection.rangeCount === 0) { 3975 return; 3976 } 3977 3978 removeActivator(); 3979 3980 currentContext = { 3981 anchorNode: currentSelection.anchorNode, 3982 focusNode: currentSelection.focusNode, 3983 anchorOffset: currentSelection.anchorOffset, 3984 focusOffset: currentSelection.focusOffset, 3985 range: currentSelection.getRangeAt(0), 3986 active: false, 3987 elements: [] 3988 }; 3989 3990 var position = currentContext.anchorNode.compareDocumentPosition(currentContext.focusNode); 3991 currentContext.leftToRight = !(position & Node.DOCUMENT_POSITION_PRECEDING); 3992 3993 currentContext.nodes = getElementsInSelection(); 3994 3995 console.log(currentContext.nodes); 3996 3997 if (!currentContext.nodes.length) { 3998 currentContext = {}; 3999 return; 4000 } 4001 4002 renderActivator(); 4003 4004 activatorHtml.addEventListener('click', activateControlcenter, false); 4005 4006 if (timeoutId) { 4007 clearTimeout(timeoutId); 4008 } 4009 4010 timeoutId = setTimeout(function () { 4011 removeActivator(); 4012 currentContext = {}; 4013 }, 2000); 4014 } 4015 4016 function activateControlcenter() { 4017 if (timeoutId) { 4018 clearTimeout(timeoutId); 4019 } 4020 4021 currentContext.active = true; 4022 4023 currentContext.elements = createItemsFromNodes(currentContext.nodes); 4024 4025 var elements = currentContext.elements.map(function (x) { return x.newNode; }); 4026 4027 if (window.getSelection().empty) { 4028 window.getSelection().empty(); 4029 } else if (window.getSelection().removeAllRanges) { 4030 window.getSelection().removeAllRanges(); 4031 } 4032 4033 renderControlcenter(); 4034 removeActivator(); 4035 4036 player = new talkify.TtsPlayer({ controlcenter: { name: "local", container: controlcenterHtml } }); 4037 4038 talkify.messageHub.subscribe("text-selection-activator", player.correlationId + ".player.*.disposed", disposeControlCenter); 4039 4040 if (settings.highlightText) {
4041 player.enableTextHighlighting(); 4042 } 4043 4044 player.forceVoice(settings.voice); 4045 player.setRate(settings.rate); 4046 4047 if (settings.enhancedVisibility) { 4048 player.enableEnhancedTextVisibility(); 4049 } 4050 4051 if (playlist) { 4052 playlist.dispose(); 4053 } 4054 4055 playlist = new talkify.playlist() 4056 .begin() 4057 .usingPlayer(player) 4058 .withElements(elements) 4059 .subscribeTo({ onEnded: disposeControlCenter }) 4060 .build(); 4061 } 4062 4063 function disposeControlCenter() { 4064 if (player) { 4065 talkify.messageHub.unsubscribe("text-selection-activator", player.correlationId + ".player.*.disposed"); 4066 player.dispose(); 4067 } 4068 4069 if (playlist) { 4070 playlist.dispose(); 4071 } 4072 4073 removeControlcenter(); 4074 4075 if (currentContext.range.commonAncestorContainer.nodeType !== 1) { 4076 var element = currentContext.elements[0]; 4077 4078 if (element) { 4079 element.newNode.parentElement.replaceChild(element.originalNode, element.newNode); 4080 } 4081 } 4082 else { 4083 restoreDom(); 4084 } 4085 4086 currentContext = {}; 4087 } 4088 4089 function restoreDom() { 4090 var elements = currentContext.elements; 4091 4092 for (var i = 0; i < elements.length; i++) { 4093 var originalNode = elements[i].originalNode; 4094 var currentNode = elements[i].newNode; 4095 4096 if (Array.isArray(originalNode)) { 4097 restoreDomForGroupedNodes(currentNode, originalNode); 4098 } 4099 else { 4100 currentNode.parentElement.replaceChild(originalNode, currentNode); 4101 } 4102 4103 var candidatesForEvents = Array.isArray(originalNode) ? 4104 originalNode.map(function (n) { 4105 if (n.getElementsByTagName) { 4106 return [n].concat(Array.from(n.getElementsByTagName('*'))) 4107 } 4108 4109 return [n]; 4110 }).flat() : 4111 [originalNode].concat(originalNode.getElementsByTagName ? Array.from(originalNode.getElementsByTagName('*')) : []) 4112 4113 var events = candidatesForEvents.map(function (x) { 4114 return talkify.domExtensions.recordEvents.getEvents(x); 4115 }).filter(function (x) { return x !== null }); 4116 4117 for (var j = 0; j < candidatesForEvents.length; j++) { 4118 var element = candidatesForEvents[j]; 4119 4120 var eventsToApply = events 4121 .filter(function (x) { return element.isEqualNode(x.node) }) 4122 .map(function (x) { return x.events }) 4123 .flat(); 4124 4125 for (var k = 0; k < eventsToApply.length; k++) { 4126 var event = eventsToApply[k]; 4127 4128 element.addEventListener(event.type, event.listener, event.options); 4129 } 4130 } 4131 } 4132 } 4133 4134 function restoreDomForGroupedNodes(currentNode, originalNodes) { 4135 for (var i = 0; i < originalNodes.length; i++) { 4136 var originalNode = originalNodes[i]; 4137 currentNode.parentElement.insertBefore(originalNode, currentNode); 4138 } 4139 4140 currentNode.parentElement.removeChild(currentNode); 4141 } 4142 4143 function removeControlcenter() { 4144 if (!controlcenterHtml) { 4145 return; 4146 } 4147 4148 controlcenterHtml.innerHTML = ""; 4149 document.body.removeChild(controlcenterHtml); 4150 controlcenterHtml = null; 4151 } 4152 4153 function removeActivator() { 4154 if (!activatorHtml) { 4155 return; 4156 } 4157 4158 activatorHtml.removeEventListener("click", activateControlcenter, false); 4159 4160 activatorHtml.innerHTML = ""; 4161 4162 if (activatorHtml.parentElement) { 4163 document.body.removeChild(activatorHtml); 4164 } 4165 4166 activatorHtml = null; 4167 } 4168 4169 function onMouseUpdate(e) { 4170 x = e.clientX; 4171 y = e.clientY; 4172 } 4173 4174 function renderActivator() { 4175 var div = document.createElement('div'); 4176 div.classList.add("talkify-activator-wrapper"); 4177 4178 div.innerHTML = '<div class="talkify-popup-activator">\ 4179 <button title="Talkify">\ 4180 <i class="fas fa-play"></i>' + 4181 settings.buttonText + 4182 '</button>\ 4183 </div>'; 4184 4185 activatorHtml = div; 4186 4187 document.body.appendChild(activatorHtml); 4188 4189 var preferDown = currentContext.leftToRight || y < 50; 4190 4191 if (x >= window.outerWidth - 300) { 4192 x = x - (300 - (window.outerWidth - x)); 4193 } 4194 4195 activatorHtml.style.left = x + 'px';
4196 activatorHtml.style.top = (y + (preferDown ? 15 : -45)) + 'px'; 4197 } 4198 4199 function renderControlcenter() { 4200 controlcenterHtml = document.createElement('div'); 4201 controlcenterHtml.classList.add("talkify-controlcenter-wrapper"); 4202 4203 var closeButton = document.createElement('div'); 4204 closeButton.classList.add('talkify-close'); 4205 4206 closeButton.innerHTML = "<i class='fa fa-times'/>"; 4207 4208 closeButton.addEventListener("click", disposeControlCenter); 4209 4210 controlcenterHtml.appendChild(closeButton); 4211 4212 controlcenterHtml.style.left = activatorHtml.style.left; 4213 controlcenterHtml.style.top = activatorHtml.style.top; 4214 4215 document.body.appendChild(controlcenterHtml); 4216 } 4217 4218 return { 4219 withEnhancedVisibility: function () { 4220 settings.enhancedVisibility = true; 4221 return this; 4222 }, 4223 withVoice: function (voice) { 4224 settings.voice = voice; 4225 return this; 4226 }, 4227 withTextHighlighting: function () { 4228 settings.highlightText = true; 4229 return this; 4230 }, 4231 withButtonText: function (text) { 4232 settings.buttonText = text; 4233 return this; 4234 }, 4235 withRate: function(rate){ 4236 settings.rate = rate; 4237 }, 4238 excludeElements: function (domElements) { 4239 settings.exclude = domElements; 4240 return this; 4241 }, 4242 activate: activate, 4243 deactivate: deactivate 4244 } 4245}(); 4246 4247talkify.domExtensions = { 4248 recordEvents: function () { 4249 var events = []; 4250 var initialized = false; 4251 4252 function addEventListenerDecorator(type, listener, options) { 4253 this._addEventListener(type, listener, options); 4254 4255 events.push({ node: this, type: type, listener: listener, options: options }); 4256 } 4257 4258 function initialize() { 4259 if (initialized) { 4260 return; 4261 } 4262 4263 Node.prototype._addEventListener = Node.prototype.addEventListener; 4264 Node.prototype.addEventListener = addEventListenerDecorator; 4265 initialized = true; 4266 } 4267 4268 function getEvents(node) { 4269 var result = events.filter(function (e) { 4270 return e.node.isEqualNode(node); 4271 }); 4272 4273 if (!result.length) { 4274 return { 4275 events: [] 4276 }; 4277 } 4278 4279 return { 4280 node: result[0].node, 4281 events: result 4282 }; 4283 } 4284 4285 return { 4286 getEvents: getEvents, 4287 begin: initialize 4288 } 4289 }() 4290} 4291 4292 4293},{}],19:[function(require,module,exports){ 4294talkify = talkify || {}; 4295talkify.textextractor = function () { 4296 var validElements = []; 4297 4298 var inlineElements = ['a', 'span', 'b', 'big', 'i', 'small', 'tt', 'abbr', 'acronym', 'cite', 'code', 'dfn', 'em', 'kbd', 'strong', 'samp', 'var', 'a', 'bdo', 'q', 'sub', 'sup', 'label']; 4299 var forbiddenElementsString = ['img', 'map', 'object', 'script', 'button', 'input', 'select', 'textarea', 'style', 'code', 'nav', '#nav', '#navigation', '.nav', '.navigation', 'footer', 'rp', 'rt']; //removed br...revert? 4300 var forbiddenClasses = ['talkify-tts-table'] 4301 var userExcludedElements = []; 4302 4303 function getVisible(elements) { 4304 var result = []; 4305 4306 for (var j = 0; j < elements.length; j++) { 4307 if (!isVisible(elements[j])) { 4308 continue; 4309 } 4310 4311 result.push(validElements[j]); 4312 } 4313 4314 return result; 4315 } 4316 4317 function isVisible(element) { 4318 return !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length); 4319 } 4320 4321 function getStrippedText(text) { 4322 return text.replace(/(\r\n|\n|\r)/gm, "").trim(); 4323 } 4324 4325 function isValidTextNode(node) { 4326 if (!node) { 4327 return false; 4328 } 4329 4330 if (node.nodeType === 3) { 4331 return getStrippedText(node.textContent).length >= 10; 4332 } 4333 4334 return false; 4335 } 4336 4337 function isValidAnchor(node) { 4338 var nrOfSiblings = getSiblings(node); 4339 4340 if (nrOfSiblings.length >= 1) { 4341 return true; 4342 } 4343 4344 var previous = node.previousSibling; 4345 4346 if (isValidTextNode(previous)) { 4347 return true; 4348 } 4349 4350 if (isValidTextNode(node.nextSibling)) { 4351 return true; 4352 } 4353 4354 return false;
4355 } 4356 4357 function isValidForGrouping(node) { 4358 if (node.nodeName === "BR") { 4359 return true; 4360 } 4361 4362 var isTextNode = node.nodeType === 3; 4363 var textLength = getStrippedText(node.textContent).length; 4364 4365 return (isTextNode && textLength >= 2) || (!isForbidden(node) && elementIsInlineElement(node)); 4366 } 4367 4368 function getConnectedElements(nodes, firstIndex) { 4369 var connectedElements = []; 4370 4371 for (var l = firstIndex; l < nodes.length; l++) { 4372 if (isValidForGrouping(nodes[l])) { 4373 connectedElements.push(nodes[l]); 4374 } else { 4375 break; 4376 } 4377 } 4378 4379 return connectedElements; 4380 } 4381 4382 function group(elements) { 4383 //TODO: wrap in selectable element 4384 wrapping = document.createElement('span'); 4385 wrapping.classList.add("superbar"); 4386 4387 for (var j = 0; j < elements.length; j++) { 4388 wrapping.appendChild(elements[j].cloneNode(true)); 4389 } 4390 4391 return wrapping; 4392 } 4393 4394 function wrapInSelectableElement(node) { 4395 wrapping = document.createElement('span'); 4396 wrapping.classList.add("foobar"); 4397 wrapping.innerText = node.textContent.trim(); 4398 return wrapping; 4399 } 4400 4401 function wrapAndReplace(node) { 4402 var spanElement = wrapInSelectableElement(node); 4403 4404 if (node.parentNode) { 4405 node.parentNode.replaceChild(spanElement, node); 4406 } 4407 4408 return spanElement; 4409 } 4410 4411 function evaluate(nodes) { 4412 if (!nodes || nodes.length === 0) { 4413 return; 4414 } 4415 4416 for (var i = 0; i < nodes.length; i++) { 4417 var node = nodes[i]; 4418 4419 if (isForbiddenByClass(node)) { 4420 continue; 4421 } 4422 4423 if (elementIsParagraphOrHeader(node)) { 4424 validElements.push(node); 4425 continue; 4426 } 4427 4428 if (getForbiddenElements().indexOf(getSafeTagName(node).toLowerCase()) !== -1) { 4429 var forcedElement = (node.nodeType === 1 ? node : node.parentNode).querySelectorAll('h1, h2, h3, h4'); 4430 4431 for (var k = 0; k < forcedElement.length; k++) { 4432 validElements.push(forcedElement[k]); 4433 } 4434 4435 continue; 4436 } 4437 4438 if (getSafeTagName(node).toLowerCase() === 'a' && !isValidAnchor(node)) { 4439 continue; 4440 } 4441 4442 var connectedElements = getConnectedElements(nodes, i); 4443 4444 if (connectedElements.length > 1) { 4445 var wrapping = group(connectedElements); 4446 var isAboveThreshold = getStrippedText(wrapping.innerText).length >= 20; 4447 4448 if (isAboveThreshold) { 4449 nodes[i].parentNode.replaceChild(wrapping, nodes[i]); 4450 4451 for (var j = 0; j < connectedElements.length; j++) { 4452 var parentNode = connectedElements[j].parentNode; 4453 4454 if (!parentNode) { 4455 continue; 4456 } 4457 4458 connectedElements[j].parentNode.removeChild(connectedElements[j]); 4459 } 4460 4461 validElements.push(wrapping); 4462 4463 continue; 4464 } 4465 } 4466 4467 if (isValidTextNode(node)) { 4468 validElements.push(wrapAndReplace(node)); 4469 } 4470 4471 evaluate(node.childNodes); 4472 } 4473 } 4474 4475 function extract(rootSelector, exclusions) { 4476 userExcludedElements = exclusions || []; 4477 validElements = []; 4478 4479 var topLevelElements = document.querySelectorAll(rootSelector + ' > ' + generateExcludesFromForbiddenElements()); 4480 4481 for (var i = 0; i < topLevelElements.length; i++) { 4482 var element = topLevelElements[i]; 4483 4484 if (elementIsParagraphOrHeader(element)) { 4485 validElements.push(element); 4486 4487 continue; 4488 } 4489 4490 evaluate(topLevelElements[i].childNodes); 4491 } 4492 4493 var result = getVisible(validElements); 4494 4495 return result; 4496 } 4497 4498 function generateExcludesFromForbiddenElements() { 4499 var result = '*'; 4500 4501 var forbiddenElements = getForbiddenElements(); 4502 4503 for (var i = 0; i < forbiddenElements.length; i++) { 4504 result += ':not(' + forbiddenElements[i] + ')'; 4505 } 4506 4507 return result; 4508 } 4509 4510 function elementIsParagraphOrHeader(element) { 4511 if (element.nodeType === 3) { 4512 return false;
4513 } 4514 4515 return ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'].indexOf(getSafeTagName(element).toLowerCase()) != -1; 4516 } 4517 4518 function elementIsInlineElement(element) { 4519 if (element.nodeType === 3) { 4520 return false; 4521 } 4522 4523 return inlineElements.indexOf(getSafeTagName(element).toLowerCase()) != -1; 4524 } 4525 4526 function getSafeTagName(node) { 4527 return node.tagName || ''; 4528 } 4529 4530 function getChildren(n, skipMe) { 4531 var r = []; 4532 for (; n; n = n.nextSibling) 4533 if (n.nodeType == 1 && n != skipMe && !isForbidden(n)) 4534 r.push(n); 4535 return r; 4536 }; 4537 4538 function getSiblings(n) { 4539 if (!n) { 4540 return []; 4541 } 4542 4543 return getChildren(n.parentNode.firstChild, n); 4544 } 4545 4546 function getForbiddenElements() { 4547 return forbiddenElementsString.concat(userExcludedElements); 4548 } 4549 4550 function isForbidden(node) { 4551 return getForbiddenElements().indexOf(getSafeTagName(node).toLowerCase()) !== -1; 4552 } 4553 4554 function isForbiddenByClass(node){ 4555 if(!node.classList){ 4556 return false; 4557 } 4558 4559 return !!forbiddenClasses.find(function(x){ return node.closest("." + x) }); 4560 } 4561 4562 return { 4563 extract: extract 4564 }; 4565}; 4566},{}],20:[function(require,module,exports){ 4567talkify = talkify || {}; 4568 4569talkify.generateGuid = function() { 4570 return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { 4571 var r = Math.random() * 16 | 0, v = c === "x" ? r : (r & 0x3 | 0x8); 4572 return v.toString(16); 4573 }); 4574}; 4575 4576talkify.log = function(){ 4577 if(talkify.config.debug){ 4578 console.log.apply(console, arguments); 4579 } 4580} 4581 4582talkify.toLowerCaseKeys = function(o) { 4583 var newO, origKey, newKey, value; 4584 if (o instanceof Array) { 4585 return o.map(function(value) { 4586 if (typeof value === "object") { 4587 value = talkify.toLowerCaseKeys(value); 4588 } 4589 return value; 4590 }) 4591 } else { 4592 newO = {}; 4593 for (origKey in o) { 4594 if (o.hasOwnProperty(origKey)) { 4595 newKey = (origKey.charAt(0).toLowerCase() + origKey.slice(1) || origKey).toString(); 4596 value = o[origKey]; 4597 if (value instanceof Array || (value !== null && value.constructor === Object)) { 4598 value = talkify.toLowerCaseKeys(value); 4599 } 4600 newO[newKey] = value; 4601 } 4602 } 4603 } 4604 return newO; 4605 } 4606 4607},{}],21:[function(require,module,exports){ 4608talkify = talkify || {}; 4609talkify.wordHighlighter = function (correlationId) { 4610 var currentItem = null; 4611 var currentPositions = []; 4612 var currentPosition = -1; 4613 var currentWordbreakMs = 0; 4614 var useEnhancedView = false; 4615 var enhancedView = null; 4616 var currentControlcenterPosition = null; 4617 4618 talkify.messageHub.subscribe("word-highlighter", correlationId + ".player.tts.seeked", setPosition); 4619 talkify.messageHub.subscribe("word-highlighter", [correlationId + ".player.tts.loading", correlationId + ".player.tts.disposed", correlationId + ".player.tts.ended"], 4620 function () { 4621 cancel(); 4622 4623 if (enhancedView) { 4624 document.body.removeChild(enhancedView); 4625 enhancedView = null; 4626 } 4627 }); 4628 4629 talkify.messageHub.subscribe("word-highlighter", correlationId + ".player.tts.play", function (message) { 4630 setupWordHightlighting(message.item, message.positions); 4631 }); 4632 4633 talkify.messageHub.subscribe("word-highlighter", correlationId + ".player.tts.wordbreakchanged", function (wordbreakms) { 4634 currentWordbreakMs = wordbreakms; 4635 }); 4636 4637 talkify.messageHub.subscribe("word-highlighter", correlationId + ".player.tts.timeupdated", function (timeInfo) { 4638 if (!currentPositions.length) { 4639 return; 4640 } 4641 4642 var time = timeInfo.currentTime * 1000; 4643 4644 var currentPos = 0; 4645 4646 if (time < currentPositions[0].Position) { 4647 if (currentPosition === -1) { 4648 return; 4649 } 4650 4651 currentPosition = 0; 4652 highlight(currentItem, currentPositions[0].Word, currentPositions[0].CharPosition); 4653 return; 4654 } 4655 4656 for (var i = 0; i < currentPositions.length; i++) { 4657 if (i === currentPositions.length - 1) { 4658 currentPos = i; 4659 break; 4660 } 4661 4662 var position = currentPositions[i].Position; 4663 4664 if (time >= position && time <= currentPositions[i + 1].Position) { 4665 currentPos = i; 4666 break; 4667 } 4668 } 4669 4670 if (currentPosition === currentPos) { 4671 return; 4672 } 4673 4674 currentPosition = currentPos; 4675 4676 highlight(currentItem, currentPositions[currentPos].Word, currentPositions[currentPos].CharPosition); 4677 }); 4678 4679 function adjustPositionsForPrefix(item) { 4680 if (!item.prefix) { 4681 return; 4682 } 4683 4684 var itemPrefix = item.prefix.replace(/\s/g,'').replace(/[.,?!ãï¼]/g, '');; 4685 var prefix = ""; 4686 4687 var lastPrefixIndex = 0; 4688 var prefixEndsAtPosition = 0; 4689 4690 for (var i = 0; i < currentPositions.length; i++) { 4691 prefix += currentPositions[i].Word; 4692 4693 if (prefix === itemPrefix) { 4694 lastPrefixIndex = i; 4695 prefixEndsAtPosition = currentPositions[i].CharPosition + currentPositions[i].Word.length + 2; //": " 4696 break; 4697 } 4698 } 4699 4700 currentPositions.splice(0, lastPrefixIndex + 1); 4701 4702 for (var i = 0; i < currentPositions.length; i++) { 4703 currentPositions[i].CharPosition -= prefixEndsAtPosition; 4704 } 4705 } 4706 4707 talkify.messageHub.subscribe("word-highlighter", correlationId + ".player.tts.enhancedvisibilityset", function (value) { 4708 useEnhancedView = value; 4709 4710 if (!value && enhancedView) { 4711 document.body.removeChild(enhancedView); 4712 enhancedView = null; 4713 } 4714 }); 4715 4716 talkify.messageHub.subscribe("word-highlighter", correlationId + ".controlcenter.attached", function (position) { 4717 currentControlcenterPosition = position; 4718 4719 adjustRenderPositionToControlCenter(); 4720 }); 4721 4722 talkify.messageHub.subscribe("word-highlighter", correlationId + ".controlcenter.detatched", function (position) { 4723 currentControlcenterPosition = position; 4724 4725 adjustRenderPositionToControlCenter(); 4726 }); 4727 4728 function adjustRenderPositionToControlCenter() { 4729 if (enhancedView && currentControlcenterPosition) { 4730 var enhancedViewPosition = enhancedView.getBoundingClientRect(); 4731 4732 var hasOverlay = enhancedViewPosition.y >= currentControlcenterPosition.y && 4733 enhancedViewPosition.y <= (currentControlcenterPosition.y + currentControlcenterPosition.height); 4734 4735 hasOverlay = hasOverlay || (currentControlcenterPosition.y >= enhancedViewPosition.y && 4736 currentControlcenterPosition.y <= (enhancedViewPosition.y + enhancedViewPosition.height)); 4737 4738 if (hasOverlay) { 4739 enhancedView.style.bottom = currentControlcenterPosition.height + "px"; 4740 } else { 4741 enhancedView.style.bottom = ""; 4742 } 4743 } 4744 } 4745 4746 function adjustPositionsToSsml(text, positions) { 4747 var internalPos = JSON.parse(JSON.stringify(positions)); 4748 4749 var lastFound = 0; 4750 4751 for (var i = 0; i < internalPos.length; i++) { 4752 lastFound = text.indexOf(internalPos[i].Word, lastFound); 4753 internalPos[i].CharPosition = lastFound 4754 } 4755 4756 return internalPos; 4757 } 4758 4759 function highlight(item, word, charPosition) { 4760 resetCurrentItem(); 4761 4762 currentItem = item; 4763 var text = item.element.innerText.trim(); 4764 4765 // Customisation: prevent voice reading attributions ("[1], [2] etc") 4766 text = text.replace(/(\[\d*\])/g, ''); 4767 4768 var sentence = findCurrentSentence(item, charPosition); 4769 4770 item.element.innerHTML = 4771 text.substring(0, sentence.start) + 4772 '<span class="talkify-sentence-highlight">' + 4773 text.substring(sentence.start, charPosition) + 4774 '<span class="talkify-word-highlight">' + 4775 text.substring(charPosition, charPosition + word.length) + 4776 '</span>' + 4777 text.substring(charPosition + word.length, sentence.end) + 4778 '</span>' + 4779 text.substring(sentence.end); 4780 4781 renderEnhancedView(text, sentence, charPosition, word); 4782 } 4783 4784 function cancel() { 4785 resetCurrentItem(); 4786 4787 currentPositions = []; 4788 currentPosition = -1; 4789 } 4790 4791 function setupWordHightlighting(item, positions, startFrom) { 4792 cancel(); 4793 4794 if (!positions.length) { 4795 return; 4796 } 4797 4798 if (item.ssml || item.wordbreakms || currentWordbreakMs) { 4799 currentPositions = adjustPositionsToSsml(item.text, positions); 4800 } else { 4801 currentPositions = positions; 4802 } 4803 4804 adjustPositionsForPrefix(item); 4805 4806 var i = startFrom || 0; 4807 4808 var internalCallback = function () { 4809 currentItem = item; 4810 4811 i++; 4812 4813 if (i >= positions.length) { 4814 window.setTimeout(function () { 4815 item.element.innerHTML = item.originalElement.innerHTML; 4816 4817 talkify.messageHub.publish(correlationId + ".wordhighlighter.complete", item); 4818 4819 if (enhancedView) { 4820 document.body.removeChild(enhancedView);; 4821 enhancedView = null;
4822 initialEnhancedViewPosition = null; 4823 } 4824 }, 1000); 4825 4826 return; 4827 } 4828 }; 4829 4830 internalCallback(); 4831 } 4832 4833 function resetCurrentItem() { 4834 if (currentItem) { 4835 currentItem.element.innerHTML = currentItem.originalElement.innerHTML; 4836 } 4837 } 4838 4839 function setPosition(message) { 4840 var diff = 0; 4841 var timeInMs = message.time * 1000; 4842 var nextPosition = 0; 4843 4844 for (var i = 0; i < message.positions.length; i++) { 4845 var pos = message.positions[i]; 4846 4847 if (pos.Position < timeInMs) { 4848 continue; 4849 } 4850 4851 diff = pos.Position - timeInMs; 4852 nextPosition = i; 4853 4854 break; 4855 } 4856 4857 var item = currentItem; 4858 var positions = message.positions; 4859 4860 cancel(); 4861 4862 setTimeout(function () { 4863 setupWordHightlighting(item, positions, nextPosition); 4864 }, diff); 4865 } 4866 4867 function findCurrentSentence(item, charPosition) { 4868 var text = item.element.innerText.trim(); 4869 4870 // Customisation: prevent voice reading attributions ("[1], [2] etc") 4871 text = text.replace(/(\[\d*\])/g, ''); 4872 4873 var separators = ['\.', '\?', '!', 'ã']; 4874 var baseline = text.split(new RegExp('[' + separators.join('') + '](?!\\S)', 'g')); 4875 var result = []; 4876 4877 var currentSentence = ""; 4878 4879 for (var i = 0; i < baseline.length; i++) { 4880 currentSentence += baseline[i] + "."; 4881 4882 var isLast = i + 1 === baseline.length; 4883 4884 if (isLast || baseline[i + 1].startsWith(" ") || baseline[i + 1].startsWith("\n")) { 4885 result.push(currentSentence); 4886 currentSentence = ""; 4887 } 4888 } 4889 4890 var charactersTraversed = 0; 4891 var sentenceStart = 0; 4892 var sentenceEnd = text.length; 4893 4894 for (var i = 0; i < result.length; i++) { 4895 if (charPosition >= charactersTraversed && charPosition <= charactersTraversed + result[i].length) { 4896 if (charactersTraversed > 0) { 4897 sentenceStart = charactersTraversed + 1; 4898 } 4899 4900 sentenceEnd = charactersTraversed + result[i].length; 4901 break; 4902 } 4903 4904 charactersTraversed += result[i].length; 4905 } 4906 4907 return { 4908 start: sentenceStart, 4909 end: sentenceEnd 4910 }; 4911 } 4912 4913 function dispose() { 4914 talkify.messageHub.unsubscribe("word-highlighter", correlationId + ".player.tts.seeked"); 4915 talkify.messageHub.unsubscribe("word-highlighter", [correlationId + ".player.tts.loading", correlationId + ".player.tts.disposed"]); 4916 talkify.messageHub.unsubscribe("word-highlighter", correlationId + ".player.tts.play"); 4917 talkify.messageHub.unsubscribe("word-highlighter", correlationId + ".player.tts.timeupdated"); 4918 talkify.messageHub.unsubscribe("word-highlighter", correlationId + ".player.tts.enhancedvisibilityset"); 4919 talkify.messageHub.unsubscribe("word-highlighter", correlationId + ".controlcenter.attached"); 4920 talkify.messageHub.unsubscribe("word-highlighter", correlationId + ".controlcenter.detatched"); 4921 } 4922 4923 function renderEnhancedView(text, sentence, charPosition, word) { 4924 if (!useEnhancedView) { 4925 return; 4926 } 4927 4928 var html = 4929 '<p><span class="talkify-sentence-highlight">' + 4930 text.substring(sentence.start, charPosition) + 4931 '<span class="talkify-word-highlight">' + 4932 text.substring(charPosition, charPosition + word.length) + 4933 '</span>' + 4934 text.substring(charPosition + word.length, sentence.end) + 4935 '</span></p>'; 4936 4937 if (enhancedView) { 4938 enhancedView.innerHTML = html; 4939 4940 return; 4941 } 4942 4943 enhancedView = document.createElement("div"); 4944 4945 enhancedView.classList.add("talkify-enhanced-word-highligher"); 4946 4947 enhancedView.innerHTML = html; 4948 4949 document.body.appendChild(enhancedView); 4950 4951 adjustRenderPositionToControlCenter(); 4952 } 4953 4954 return { 4955 start: setupWordHightlighting, 4956 highlight: highlight,
4957 dispose: dispose 4958 }; 4959}; 4960},{}],22:[function(require,module,exports){ 4961talkify = {}; 4962},{}]},{},[1]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.