<!DOCTYPE html>
<html lang="auto">
    <head>
        <script>
            var INDEXLANGUAGE = 0;
            //CHANGE YOUR LANGUAGE PREFERRED
            var SEPARATION = 700;
            var FIX = 500;
        </script>
        <meta charset="utf-8">
        <title class="skiptranslate">Babelin Speech 1.1 / Speech Recording with Web Speech API</title>
        <link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,=">
        <style>
            html, body {
                height: 100%;
                font-family: Arial, Helvetica, sans-serif;
            }

            h2 {
                text-transform: uppercase;
            }

            .divHidden, .divTop, .divBottom, .divControlSpeech {
                width: 100%;
            }

            .divHelp {
                position: fixed;
                right: 0;
                font-size: 250%;
                border: 1px solid gray;
                background-color: slategrey;
                z-index: 1;
                cursor: pointer;
            }

            .divTop {
                position: fixed;
                border: none;
                background-color: white;
            }

            .divHidden, .divTop {
                height: 50%;
            }

            .divBottom {
                height: auto;
                border: 1px solid red;
            }

            .divControlSpeech {
                border: none;
                height: 65px;
            }

            .divControlsSubs {
                height: 65px;
                border-bottom: 1px solid black;
            }

            .divVideo, .divTranscript {
                height: calc(100% - 145px)
            }

            .divVideo {
                background: black;
                display: inline-block;
                width: 55%;
                opacity: 100%;
                position: relative;
            }

            .divTranscript {
                overflow: auto;
                vertical-align: top;
                display: inline-block;
                border: 1px solid green;
                width: 43%;
            }

            video::cue {
                background: rgba(1, 1, 1, 0);
                text-shadow: -1px -1px 0 #000000,1px -1px 0 #000000,-1px 1px 0 #000000,1px 1px 0 #000000;
            }

            ::cue(.recordRed) {
                color: red;
                font-size: 120%;
            }

            ::cue(.recordWhite) {
                color: white;
                font-size: 120%;
            }

            ::cue(.textGreen) {
                color: lawngreen;
            }

            ::cue(.textWhite) {
                color: white;
            }

            ::cue(.countS) {
                font-size: 70%;
            }

            ::cue(i) {
                font-size: 60%;
            }

            select, label, button, .divControlsEdit div {
                border: none;
                color: white;
                padding: 0px 0px;
                text-align: center;
                text-decoration: none;
                display: inline-block;
                font-size: 100%;
                margin: 0px 0px;
                background-color: #717171;
                vertical-align: bottom;
                height: 25px;
                padding-right: 10px;
                padding-left: 10px;
            }

            #editAll {
                color: blue;
                cursor: pointer;
                text-decoration: underline;
                display: none;
            }

            .divControlsEdit {
                position: fixed;
                top: 58px;
                right: 20px;
            }

            .divControlsEdit div {
                cursor: pointer;
            }

            #idCountCharacters {
                background: black;
                color: white;
                position: sticky;
                top: 0px;
                right: 0px;
            }

            #idCountCharacters[data-diff="true"] {
                background: red;
            }

            #idTranscript:hover~#idCountCharacters {
                display: none;
            }

            #idLabelSubtitle,#idButtonRecognition,#idLabelVideo, #idSelectLanguage,#idSelectDialect,#idCaptions {
                height: 25px;
                width: 30%;
                font-size: 100%;
                cursor: pointer;
            }

            button {
                cursor: pointer;
            }

            #idButtonDown,#idButtonUp,#idDownloadTable,#idDownloadTrack {
                font-size: 100%;
            }

            #idGroup1,#idGroup2,#idGroup3,#idGroup4 {
                margin: 5px;
            }

            input {
                border: none;
                text-align: left;
                padding: 0px 0px;
                display: inline-block;
                font-size: 100%;
                transition-duration: 0.4s;
                /*height:100%;*/
            }

            .inputGroup {
                border: 1px solid black;
                display: inline-block;
                vertical-align: text-bottom;
            }

            .DowloadSubtitle {
                width: auto;
                visibility: hidden;
            }

            .visually-hidden {
                position: absolute !important;
                height: 1px;
                width: 1px;
                overflow: hidden;
                clip: rect(1px, 1px, 1px, 1px);
            }

            input.visually-hidden:focus + label {
                outline: thin dotted;
            }

            input.visually-hidden:focus-within + label {
                outline: thin dotted;
            }

            .idModal {
                display: none;/* Hidden by default */
                position: fixed;/* Stay in place */
                z-index: 1;   /* Sit on top */
                padding-top: 100px; /* Location of the box */
                left: 0;
                top: 0;
                width: 100%; /* Full width */
                height: 100%; /* Full height */
                overflow: auto; /* Enable scroll if needed */
                background-color: rgb(0,0,0); /* Fallback color */
                background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
            }

            .idModal-content {
                position: relative;
                background-color: #fefefe;
                margin: auto;
                padding: 0;
                border: 1px solid #888;
                width: 80%;
                box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);
                -webkit-animation-name: animatetop;
                -webkit-animation-duration: 0.4s;
                animation-name: animatetop;
                animation-duration: 0.4s
            }

            @-webkit-keyframes animatetop {
                from {
                    top: -300px;
                    opacity: 0
                }

                to {
                    top: 0;
                    opacity: 1
                }
            }

            @keyframes animatetop {
                from {
                    top: -300px;
                    opacity: 0
                }

                to {
                    top: 0;
                    opacity: 1
                }
            }

            .close {
                color: white;
                float: right;
                font-size: 28px;
                font-weight: bold;
            }

            .close:hover,.close:focus {
                color: #000;
                text-decoration: none;
                cursor: pointer;
            }

            .idModal-header {
                padding: 2px 16px;
                background-color: slategrey;
                color: white;
            }

            .idModal-body {
                padding: 2px 16px;
            }

            .idModal-footer {
                padding: 2px 16px;
                background-color: slategrey;
                color: white;
            }

            ol {
                padding: 0px;
            }

            ol li {
                margin-left: 40px;
            }
        </style>
    </head>
    <body onkeydown="keyEvent(event)" onpaste="pasteURLVideo(event)">
        <div class="skiptranslate">
            <div id="idHelp" class="divHelp" title="" onclick="idFrame.src=vyt;idModal.style.display = 'block'">?</div>
        </div>
        <div class="divTop">
            <div class="divControlSpeech skiptranslate" id="idControlsUP">
                <div id="idGroup1">
                    <input type="file" id="idFileVideo" name="File Video" accept="video/*,.mkv,audio/*" class="visually-hidden">
                    <label for="idFileVideo" id="idLabelVideo" title="You can drag and drop the video on this page or select the video file.">Load Video</label>
                    <select id="idSelectLanguage" onchange="updateCountry()" title="Select the language of the video. You can set the default language that is loaded by modifying line 5 of this file"></select>
                    &nbsp;&nbsp;<select id="idSelectDialect"></select>
                </div>
                <div id="idGroup2">
                    <button id="idButtonRecognition" type="button" onclick="startStopRecognition()" title="Start/stop video recognition speech. Ctrl + Q"/>Start Recognition</button>
                    <div class="inputGroup" title="CPS (characters per second). Used to make corrections to start times as well as table colors. For English 10-14 CPS, Spanish 10-15 CPS, Japanese/Chinese/Korean 4-6 CPS , 10 CPS Belgium, 16 Finland">
                        <label>CPS: </label>
                        <input type="number" id="idCPS" value=10 min="1" max="20">
                    </div>
                    <div class="inputGroup">
                        <select id="idFilter" name="filter">
                            <option value="0">All</option>
                            <option value="1">Only separations</option>
                            <option value="2" selected>Only Groups</option>
                        </select>
                    </div>
                    <div id="idGSeparation"  style="display:none" class="inputGroup" title="If the silence between sentences exceeds this time, a new line is created for the subtitle">
                        <label>Separation (ms): </label>
                        <input type="number" id="idDistanceSentences" value=0 min="0" max="3000">
                    </div>
                    <div class="inputGroup" title="Fix delay Recognition in milliseconds (0-2000). There is a time delay between when you start speaking and the time the program recognizes the word. Here you can make the correction of the times of the generated subtitles.">
                        <label>Fix(ms): </label>
                        <input type="number" id="idDelayRecognition" value=0 min="0" max="2000">
                    </div>
                </div>
            </div>
            <div class="divVideo skiptranslate">
                <video id="idVideo" width="100%" height="100%" controls>
                    <source src="" onerror="" type="video/mp4">
                    <track kind="subtitles" id="idFileVTT" label="File">
                </video>
            </div>
            <div class="divTranscript">
                <div id="idCountCharacters" data-diff="false"></div>
                <div id="idTranscript"></div>
                <div class="divControlsEdit" id="idControlEdit" style="display:none">
                    <a href="http://www.deepl.com" target="_blank">DeepL</a>
                    <a href="http://translate.yandex.com" target="_blank">Yandex</a>
                    <a href="http://www.bing.com/translator" target="_blank">Bing</a>
                    <a href="http://translate.google.com/" target="_blank">Google</a>
                    <div onclick='cBusy(); setTimeout("updateTable()", 500) ' title="Updates the table with the text line by line, if the lines are different, it does not update the table.">Update</div>
                    <div onclick="closeEdit()" title="Returns to the previous mode. The data in the table is not modified.">Close</div>
                </div>
            </div>
            <div id="idControlsSubs" class="divControlsSubs skiptranslate">
                <div id="idGroup3">
                    <select id="idCaptions" title="Select subtitles/captions for the video. File: Subtitle loaded from a file. Raw: It is the transcript of the video. Translations: This is the one that should be used to upload the translations. Real-Time: it is only used when the recognition is performed in real time.">
                        <option value="File">File</option>
                        <option value="Raw">Raw</option>
                        <option value="Translation">Translation</option>
                        <option value="MySubtitle">My Subtitle</option>
                    </select>
                    <input type="file" id="idFileSubtitle" name="File Subtitle" accept=".ass,.vtt,.srt" class="visually-hidden">
                    <label title=" Load subtitles (ass,vtt, srt) for video" for="idFileSubtitle" id="idLabelSubtitle">Load Subtitle</label>
                </div>
                <div id="idGroup4">
                    <button id="idButtonDown" type="button" title="Creates a table based on the video's subtitles (File, Raw, Translation), that is, subtitles loaded by voice recognition or by opening a subtitle file (alt+Click Mix with table)">⇩⇩⇩⇩⇩⇩</button>
                    <button id="idButtonUp" type="button" onclick="tableToTrack(currentTrack)" title="The table is uploaded to the track the video (File, Raw, Translation).">⇧⇧⇧⇧⇧⇧</button>
                    <label title="Automatically updates the transcript of the selected track when any changes are made to the table.">
                        <input type="checkbox" id="idUpdateTrack">⇧⇧⇧ Auto-Update:
                    </label>
                    <label style="color:yellow; vertical-align:bottom;" id="idLabelTrackUpdate"></label>
                    <button id="idButtonFixTime" type="button" onclick="fixTimeStartTable()" title="Correct start times based on subtitle CPS. It is very important to establish the CPS based on the language. Chinse/Japanese/Korean 4-6 CPS, 9-15 (english, belgium, spanish, italian, german)">Fix Time Start (10 CPS)</button>
                    <button id="idDownloadTable" type="button" onclick="downloadTable()" title="Download subtitles from table">Download Table</button>
                    <button id="idDownloadTrack" type="button" onclick="downloadTrack(currentTrack)" title="Download subtitles from the video track selected">Download Track</button>
                    <div id="idDownloadSubtiles" class="inputGroup DowloadSubtitle">
                        <b id="idLabelDownload">Download Subtitles (Translation):</b>
                        <a id="idVTT" href="">.vtt</a>
                        <a id="idSRT" href="">.srt</a>
                        <a id="idASS" href="">.ass</a>
                    </div>
                </div>
            </div>
        </div>
        <div class="divHidden" id="idWatchTranslation">- </div>
        <ol>
            <div class="divBottom">
                <table border="1" cellpadding="5" id="idTable" border="0">
                    <thead>
                        <tr class="skiptranslate">
                            <th>#</th>
                            <th>
                                <strong>Start</strong>
                            </th>
                            <th>End</th>
                            <th>Settings cue</th>
                            <th onclick="cleanTable()" >
                                <img id="idRecycleBin">
                            </th>
                            <th>
                                Text <span id="editAll" onclick="editTable()" title="Allows you to edit only the text, it is useful when you want to use other translation options, such as: www.deepl.com, www.bing.com/translator, translate.yandex.com/">(Edit All)</span>
                            </th>
                        </tr>
                    </thead>
                </table>
            </div>
        </ol>
        <div id="idModal" class="idModal skiptranslate">
            <div class="idModal-content">
                <div class="idModal-header">
                    <span class="close">&times;</span>
                    <h2>Babelin Speech For voice recognition and real-time translation</h2>
                </div>
                <div class="idModal-body">
                    <p>
                        It uses the recognition and translation services offered by web browsers, which in this case is Chrome.
					For this, it is only necessary to change the microphone <a href="https://www.youtube.com/watch?v=Bd3moKLV5sE" target="_blank">input to Stereo Mix.</a>
                    </p>
                    <iframe id="idFrame" width="420" height="315" src=""></iframe>
                    <h3>Tips:</h3>
                    <li>
                        In local mode, permission will always be asked to access the microphone for safety. If a web server is running, it will only ask for permission once.
						<a href="https://www.python.org/downloads/" target="_blank">Python </a>
                        (python.exe -m http.server 8000 --bind 127.0.0.1), or <a href="https://www.voidtools.com/support/everything/http/" target="_blank">Everthing </a>
                        can be used as web servers.

                    </li>
                    <li>Sometimes it is better to stop the recognition, for each person speaking.</li>
                    <li>Use shortcuts in full screen</li>
                    <h4>Known Issues.</h4>
                    <li>After a few seconds of silence, the automatic recognition stops. </li>
                    <li>Sometimes it stops recognizing the voice after a period of time, stop and start</li>
                </div>
                <div class="idModal-footer">
                    <h4>Is open source - FREE</h4>
                </div>
            </div>
        </div>
<script>
const vyt = "https://www.youtube.com/embed/Q-7P6Xgqwb0"
const imgRecycleBin = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAACpF6WWAAABy0lEQVQ4T+2VPWhTURiGny9cMTglheKNVCGt+DNpwMFmSfFODg4GRETsolPdHAStxSvYLXbQCg4VBzFLaJ10MyAdMtYuLVKqxR8acLiZbKWF16HmJr3e2AjFyQc+OJyX9+Uc+M53oAMS2YenmMbhzW917uUUUirq2ZH5m7wCVxw8tNAeeOQAH8AVtz9PRj1/RCJXOok4OyWk7DZthTOjbrzWxJFI3jtG3l9ubU4UKZbeAf1BjT2WxSE022GYvAI8ewwXv13H4XVoPP8WKoUqEoP3QexGXXghJDdhRm0T0is3GAdgZHYGSHdbsyPMQBEmFq5RWUpjVncAfGjczbAGQE8GoEGXZHp+LfqPr4HfAEi06dvQECmGttpGIgl+2EI+pPA7t1RsqIRb7iWgtxwguUtjPIUnAY80KjHoQECiFkR9TWJDoyQgCXVYjyrxdBX6t/wP3X3+XagZ9T7woM/DrD6wlzs4Ax77KQNzm0fx+LThRX1NnOhGk0KFKhQAsDEWYXmRYcOGAajyfkuLo3XSfaznADa+d3x+cXz5QR4y4MbMC4lc6XTMOOumLk2vtn8v4fXNmFOZ/PxXrj5fbQ3lHTlx6yOXiw8wC0/6E1A7svTAq4CZAAAAAElFTkSuQmCC";
idRecycleBin.src = imgRecycleBin;
idDelayRecognition.value = FIX;
idDistanceSentences.value = SEPARATION;
var allTranscript = [], arrTranscript = [],arrSeparations=[], resultFinal=[],languageName = "", languageCode = "";
var allResult, lastTranscript;
var trackRaw = idVideo.addTextTrack("subtitles", "Raw");
var trackTranslation = idVideo.addTextTrack("subtitles", "Translation");
var trackMySubtitle = idVideo.addTextTrack("subtitles", "MySubtitle");
var trackRealTime = idVideo.addTextTrack("subtitles", "RealTime");
var currentTrack, lastCueActive, aLastCueActive = [], cueRec, cueRealTime, indexCueNext = 0;
var sRecRed = "<c.recordRed> ⊙ </c>",sRecWhite = "<c.recordWhite> ⊙ </c>";
var timer,count, countP, cueCount, bStatusRecognition = false,bGroup=false;
var nameVideo = "", ID_G = 0, timeWaitFinish = -1,waitTerminate = false;
var span = document.getElementsByClassName("close")[0];
var observerTranscript = new MutationObserver(function(event) {vttRealTime(idTranscript.textContent)});
var observerTranslation = new MutationObserver(()=>changeTranslation());
var observerTable = new MutationObserver(changeTable);
const observerOptionsTable = {attributes: false,childList: true,characterData: true,subtree: true};
observerTranscript.observe(idTranscript, {attributes: false, childList: true,characterData: true,subtree: true });
observerTranslation.observe(idWatchTranslation, {childList: true});
observerTable.observe(idTable, observerOptionsTable);
idFileVideo.onchange = (evt)=>loadSrcVideo(evt.target.files);
idFileSubtitle.onchange = (evt)=>{loadSrcSubtitle(evt.target.files);evt.target.value = null};
idVideo.onloadedmetadata = loadedVideo;
idVideo.textTracks.onchange = changeTracks;
idVideo.onended = ()=>bStatusRecognition && startStopRecognition();
idVideo.onkeydown = newSubtitle;
idVideo.onkeyup = newSubtitle;
idCPS.onchange = textButtonFixTime;
idFilter.onchange = ()=>idGSeparation.style.display = idFilter.value==2?"none":"inline-block";
idCaptions.onchange = ()=>showingTrack(idVideo.textTracks[idCaptions.selectedIndex]);
idButtonDown.onclick = (e)=>{cBusy();setTimeout(()=>{VTTtoTable(currentTrack,e)},400)};
span.onclick = ()=>idModal.style.display = "none";
window.onclick = (e)=>{if (e.target == idModal)idModal.style.display = "none"}
document.body.addEventListener("dragenter", dragStop, false);
document.body.addEventListener("dragover", dragStop, false);
document.body.addEventListener("drop", drop, false);
Number.prototype.toTime = function() {return new Date(this * 1000).toISOString().substr(11, 12)};
String.prototype.toSeconds = function() {return  +this.split(':').reduce((acc,time)=>(60 * acc) + +time).toFixed(3)};
String.prototype.countCharacters = function() {return  Array.from(this).length};
String.prototype.toListItems = function() {return  "<li>" + this.split("\n").join("</li><li>") + "</li>"};
String.prototype.removeExtension = function() {return this.split('.').slice(0, -1).join('.') || this};

function textButtonFixTime(){idButtonFixTime.textContent = `Fix Time Start:(${idCPS.value} CPS)`;if (idTable.children[1]) for (row of idTable.children[1].rows) checkCPS(row)}
function cleanTable() { (idTable.lastChild.nodeName == "TBODY") && idTable.removeChild(idTable.lastChild)}
function cBusy() { document.body.style.cursor = "wait"}
function normalize(val, max, min) { return (val - min) / (max - min)}
//function stadistics(sentence, paragraph) {cueCount.text = "     ";  cueCount.text = ` <c.countS>Lines ${[0,1].includes(+idFilter.value)?sentence:""}  ${[0,2].includes(+idFilter.value)?" Groups: " + paragraph:""} < /c> `;}
function uCue(cue, text) { cue.text = text;cue.startTime=idVideo.currentTime-.1 }
function removeAllCues(trackM) { while (trackM.cues.length > 0) trackM.removeCue(trackM.cues[0]) };
function dragStop(e) {e.stopPropagation(); e.preventDefault()};
function drop(e) {
    dragStop(e);
    const dt = e.dataTransfer;
    const files = dt.files;
    [".srt", ".ass", ".vtt"].find((ext)=>files[0].name.toLowerCase().endsWith(ext)) ? loadSrcSubtitle(files) : loadSrcVideo(files);
    e.target.value = null;
}
function listCuesCurrentTrackFilter() { return Object.values(currentTrack.cues).filter((c)=>c.line == "auto");}
function pasteURLVideo(e) {
    if (e.target.tagName != "BODY") return;
    paste = (event.clipboardData || window.clipboardData).getData('text');
    if (paste.includes("http")) idVideo.src = paste;
}
function getParentByTagName(node, tagname) {
	var parent;
	if (node === null || tagname === '') return;
	parent  = node.parentNode;
	tagname = tagname.toUpperCase();

	while (parent.tagName !== "HTML") {
		if (parent.tagName === tagname) {
			return parent;
		}
		parent = parent.parentNode;
	}

	return parent;
}
function changeTable(me) {
    idDownloadSubtiles.style.visibility = "hidden";
    idUpdateTrack.checked && tableToTrack(currentTrack);
	for (m of me )
	{
		if (m.target.nodeName == "#text" && m.target.textContent != "")   
			cell=m.target.parentNode;
		else if (m.target.nodeName == "TD")
			cell=m.target;
		else
			continue;
		if (m.target.parentNode.nodeName == "FONT")
			cell=getParentByTagName(cell,"TD")

		colorCells(cell);
		checkCPS(cell.parentNode)
	}
    
}
function colorCells(target) {
    let tStart = target.parentNode.children[1].textContent.toSeconds();
    let tEnd = target.parentNode.children[2].textContent.toSeconds();
    let text = target.parentNode.children[5].textContent;
    let indexCell = Array.prototype.findIndex.call(target.parentNode.children, (c)=>c === target)
    if (tStart >= tEnd)
        target.parentNode.style.backgroundColor = "rgba(255,0,0)";
    else
        target.parentNode.style.backgroundColor = "rgba(255,255,255)";

    if ([1, 2, 5].includes(indexCell)) {
        let cpsText = text.length / (tEnd - tStart)
        target.parentNode.children[0].style.backgroundColor = `rgba(255,0 , 0,${normalize(cpsText, +idCPS.value * 3, +idCPS.value)} )`
        if (indexCell == 1)
            checkTimePrevious(target, tStart);

        else if (indexCell == 2)
            if (target.parentNode.nextSibling)
                checkTimePrevious(target.parentNode.nextSibling.children[1], target.parentNode.nextSibling.children[1].textContent.toSeconds());
    }
}
function checkTimePrevious(target, tStart) {
    if (target.parentNode.previousSibling) {
        previousTimeEnd = target.parentNode.previousSibling.children[2].textContent.toSeconds();
        target.parentNode.children[1].style.backgroundColor = `rgba(255,255,0,${+(previousTimeEnd > tStart)} )`;
        target.parentNode.previousSibling.children[2].style.backgroundColor = `rgba(255,255,0,${+(previousTimeEnd > tStart)} )`;
    }
}

function cueNear(listCues, m) {
    let t = idVideo.currentTime, i;
    for (i = 0; i < listCues.length; i++)
        if (t <= listCues[i].startTime)
            return m == "NEXT" ? i - 1 : i;

    return i;
}
function nextCue(m) {
    if (!currentTrack || !currentTrack.cues.length) return;
    ;let listCues = listCuesCurrentTrackFilter();
    if (listCues) {
        indexCueNext = currentTrack.activeCues.length ? (listCues.indexOf(lastCueActive)) : cueNear(listCues, m);
        indexCueNext += (m == "NEXT") ? (indexCueNext < listCues.length - 1 ? 1 : 0) : (indexCueNext > 0 ? -1 : 0);
        let nCue = listCues[indexCueNext];
        nCue && (idVideo.currentTime = nCue.startTime + .01);
        //add .01 seconds, sometimes it doesn't show the subtitle
    }
}
function changeTracks() {
    currentTrack = undefined;
    for (let t of idVideo.textTracks) {
        if (t.mode == "showing")
            currentTrack = t;
    }
    idDownloadSubtiles.style.visibility = "hidden";
    if (currentTrack) {
        idGroup4.hidden = false;
        idTable.hidden = false;
        idCaptions.value = currentTrack.label;
        currentTrack.oncuechange = cueActives;
        idDownloadTrack.innerText = "Download Track: " + currentTrack.label;
        idLabelTrackUpdate.textContent = currentTrack.label;
    } else {
        idGroup4.hidden = true;
        idTable.hidden = true;
    }
}
function changeTranslation() {
    if (bStatusRecognition) {
        showingTrack(trackRealTime);
        return;
    }
    if (document.documentElement.className.match('translated')) {
        showingTrack(trackTranslation);
    }
    idDownloadSubtiles.style.visibility = "hidden";
}

function recognitionStart() {
    window.onbeforeunload = (e)=>{e.returnValue = true};
	document.body.style.cursor = ""
    idButtonRecognition.style.borderStyle = 'inset'
    arrTranscript = [];
	arrSeparations=[];
	resultFinal=[];
	allTimend=[];
	lastTranscript="";
    ID_G++;
	timer=-1;
    (!cueRec.track) && initTrackRealTime();
    count = 0;
    countP = 0;
    cueRec.text = sRecRed;
	cueRealTime.text="";
    showingTrack(trackRealTime);
    bStatusRecognition = true;
    idVideo.play();
    idButtonRecognition.innerHTML = "Stop Recognition";
}
function recognitionTerminate() {
    waitTerminate = true;
    idVideo.pause();
    timeWaitFinish = setTimeout(cleanTerminate, 1000);//wait transcript final.
}
function cleanTerminate() {
    cueRec.text = "";
	cueRealTime.text="";
    idButtonRecognition.style.borderStyle = "outset";
    loadTranscriptToRawVTT();
    VTTtoTable(trackRaw);
    idButtonRecognition.innerHTML = "Start Recognition";
    bStatusRecognition = false;
    waitTerminate = false;
    timeWaitFinish = -1;
}
function showingTrack(trackM) {
    for (let t of idVideo.textTracks) {
        if (t == trackM)
            t.mode = "showing";
        else
            t.mode = "hidden";
    }
}
function VTTtoTable(trackM,e) {
    if (trackM.cues.length == 0) { document.body.style.cursor = ""; return }
	observerTable.disconnect();
    idDownloadSubtiles.style.visibility = "hidden";
    editAll.style.display = "block";
	bmixTable=(e && (e.ctrlKey || e.altKey))?true:false;

	if(!bmixTable)
	{
		cleanTable();
		tbody = idTable.createTBody();
		tbody.id = "idTBody";
	}
	
	
    for (let cue of trackM.cues) {
        rowTable(cue.startTime, cue.endTime, settingCue(cue), cue.text, -1);
    }
    document.body.style.cursor = "";
    observerTable.observe(idTable, observerOptionsTable);
}
function rowTable(tStart, tEnd, settingscue, transcription, indexRow) {

    if (typeof idTBody=="undefined") return;
	
	let row = idTBody.insertRow(indexRow);
    let td0 = document.createElement("td");
    let td1 = document.createElement("td");
    let td2 = document.createElement("td");
    let td3 = document.createElement("td");
    let td4 = document.createElement("td");
    let td5 = document.createElement("td");
    let newImage = document.createElement('img');
    td1.name = "TimeStart";
    td2.name = "TimeEnd";
    td5.name = "transcription";
    newImage.src = imgRecycleBin;
    newImage.onclick = ()=>idTable.deleteRow(row.rowIndex);
    td0.className = "skiptranslate";
    td1.className = "skiptranslate";
    td2.className = "skiptranslate";
    td3.className = "skiptranslate";
    td4.appendChild(newImage);
    td0.innerHTML = "<li></li>";
    td1.textContent = tStart.toTime();
    td2.textContent = tEnd.toTime();
    td1.onkeypress = addRemMiliseconds;
    td2.onkeypress = addRemMiliseconds;
    td1.contentEditable = true;
    td2.contentEditable = true;
    td3.contentEditable = true;
    td4.contentEditable = false;
    td5.contentEditable = true;
    td3.textContent = settingscue;
    td5.textContent = transcription;
    row.appendChild(td0);
    row.appendChild(td1);
    row.appendChild(td2);
    row.appendChild(td3);
    row.appendChild(td4);
    row.appendChild(td5);
	checkCPS(row);
    checkTimePrevious(td1, tStart);
    if (tStart >= tEnd)
        row.style.backgroundColor = "rgb(255,0,0)";

    row.onclick = ()=>{idVideo.currentTime = row.children[1].textContent.toSeconds();colorCells(row.children[1]); }
    ;
}
function checkCPS(row)
{
    let cells = row.children;
	let cpsText = cells[5].textContent.length / (cells[2].textContent.toSeconds() - cells[1].textContent.toSeconds() );
    cells[0].style.backgroundColor = `rgba(255,0 , 0, ${normalize(cpsText, +idCPS.value * 3, +idCPS.value)})`;
}
function settingCue(cue) {
    p1 = (cue.align == "center") ? "" : `align:${cue.align};` ;
    p2 = (cue.line == "auto") ? "" : `line:${cue.line};` ;
    p3 = (cue.position == "auto") ? "" : `position:${cue.position};` ;
    p4 = (cue.size == 100) ? "" : `size:${cue.size};` ;
    p5 = (cue.vertical == "") ? "" : `vertical:${cue.vertical};` ;
    return p1 + p2 + p3 + p4 + p5;
}
function downloadTrack(trackM) {
    let arrayS = [];
    idDownloadSubtiles.style.visibility = "hidden";
    if (trackM.cues.length == 0) {
        document.body.style.cursor = "";
        return;
    }
    for (let cue of trackM.cues) { 
		arrayS.push([cue.startTime.toTime(), cue.endTime.toTime(), settingCue(cue), cue.text]) 
	}
    arrayToSubtitle(arrayS, trackM.label);
}
function downloadTable() {
    let arrayS = [];
    idDownloadSubtiles.style.visibility = "hidden";
    if (!idTable.children[1])
        return;
    for (let row of idTable.children[1].rows) {
        arrayS.push([row.children[1].textContent, row.children[2].textContent, row.children[3].textContent, row.children[5].textContent])
    }
    arrayToSubtitle(arrayS, "Table");
}
function arrayToSubtitle(arrayS, labelText) {
    let aASS = ["\uFEFF" + headASS], aSRT = ["\uFEFF"], aVTT = ["\uFEFFWEBVTT\r\n\r\n"];
    let i = 1, j = 1;
    for (let row of arrayS) {
        let tStart = row[0], tEnd = row[1],setting = row[2], text = row[3];
        let textSRT = `${i}\r\n${tStart.replace(".", ",")} --> ${tEnd.replace(".", ",")}\r\n${text}\r\n\r\n` ;
        let textVTT = `${j}\r\n${tStart} --> ${tEnd} ${setting.replace(";", " ")}\r\n${text}\r\n\r\n` ;
        let textASS = `Dialogue: 0,${tStart.slice(1, -1)},${tEnd.slice(1, -1)},Default,,0,0,0,,${text}\r\n`;
        if (setting.includes("line:0")) // line is different default/auto, 
        {
            textASS = textASS.replace(",Default,,", ",Top,,");
        } else //srt does not allow times overlaps
        {
            aSRT.push(textSRT);
			i++;
        }
        aVTT.push(textVTT);aASS.push(textASS);j++;
    }
    let cc = "";
    idCaptions.value == "Raw" && (cc = "." + languageCode);
    idCaptions.value == "Translation" && (cc = "." + navigator.language.split("-")[0]);
    if (document.documentElement.className.match('translated')) {
        idLabelDownload.innerHTML = "Download Subtitles (Translation):";
        cc = "." + navigator.language.split("-")[0];
    } else
        idLabelDownload.innerHTML = `Download Subtitles (${labelText}):`;
    idVTT.href = URL.createObjectURL(new Blob(aVTT,{type: "text/plain"}));
    idVTT.download = `${nameVideo}${cc}.vtt`;
    idASS.href = URL.createObjectURL(new Blob(aASS,{type: "text/plain"}));
    idASS.download = `${nameVideo}${cc}.ass`;
    idSRT.href = URL.createObjectURL(new Blob(aSRT,{type: "text/plain"}));
    idSRT.download = `${nameVideo}${cc}.srt`;
    idDownloadSubtiles.style.visibility = "visible";
}
function tableToTrack(trackM) {
    showingTrack(trackM)
    if (!idTable.children[1] || trackM.constructor.name != "TextTrack" || !trackM.cues)  return;
    let cueVtt, p, p0, p1;
    removeAllCues(trackM);
    for (let row of idTable.children[1].rows) {
        let tStart = row.children[1].textContent, tEnd = row.children[2].textContent, setting = row.children[3].textContent;
        let text = row.children[5].textContent;
        cueVtt = new VTTCue(tStart.toSeconds(),tEnd.toSeconds(),text);
        for (let a of setting.split(";")) {
            if (a == "")
                continue;
            p = a.split(":");
            //clean quotation marks
            p0 = (p[0] != undefined) ? p[0].replace('"', '').replace('"', '').trim() : "";
            p1 = (p[1] != undefined) ? p[1].replace('"', '').replace('"', '').trim() : "";
            (p0 == "align") && (cueVtt.align = p1);
            (p0 == "line") && (cueVtt.line = isNaN(p1) ? "auto" : +p1);
            (p0 == "position") && (cueVtt.position = isNaN(p1) ? "auto" : +p1);
            (p0 == "size") && (cueVtt.size = isNaN(p1) ? 100 : +p1);
            (p0 == "vertical") && (cueVtt.vertical = p1);
        }
        trackM.addCue(cueVtt);
    }
    let cc = "";
    idCaptions.value == "Raw" && (cc = "." + languageCode);
    idCaptions.value == "Translation" && (cc = "." + navigator.language.split("-")[0]);
}
function closeEdit() {
    document.body.style.cursor = "";
    idControlsUP.style.visibility = "visible";
    idControlEdit.style.display = "none";
    idGroup3.style.visibility = "visible";
    idGroup4.style.visibility = "visible";
    document.body.onkeydown = keyEvent;
    document.onselectionchange = "";
    idTranscript.onpaste = "";
    idTranscript.title = "";
    idTranscript.innerHTML = "";
    idSelectDialect.style.visibility = idSelectDialect.visibilityTemp;
}
function editTable() {
    if (!idTable.children[1]) return;
    idControlEdit.style.display = "block";
    idControlsUP.style.visibility = "hidden";
    idGroup3.style.visibility = "hidden";
    idGroup4.style.visibility = "hidden";
    idSelectDialect.visibilityTemp = idSelectDialect.style.visibility;
    idSelectDialect.style.visibility = "hidden";
    idTranscript.title = "Here you can copy the text (ctrl-a + ctrl-c), translate it in other translators such as yandex or deepl, and paste the translated text, and update the table."
    document.body.onkeydown = "";
    document.onselectionchange = function(e) {
        idCountCharacters.innerText = (document.activeElement.nodeName == "OL") ? `T:${idTranscript.innerText.split("\n").length}/${idTable.tBodies[0].childNodes.length} C:${document.getSelection().toString().countCharacters()}/${idTranscript.innerText.toString().countCharacters()}` : ""
        idCountCharacters.dataset.diff = (idTable.tBodies[0].childNodes.length != idTranscript.innerText.split("\n").length) ? true : false;
    }
    ;
    let listText = document.createElement("ol");
    listText.contentEditable = true;
    idTranscript.onpaste = ()=>{
        //fix node duplicates(li) on paste in ol
        (idTranscript.firstChild.nodeName = "OL") && setTimeout(()=>idTranscript.firstChild.innerHTML = idTranscript.firstChild.innerText.trim().toListItems(), 100);
    }

    for (let row of idTable.children[1].rows) {
        let olText = document.createElement("li");
        olText.innerText = row.children[5].innerText.split("\n").join(". ").replace(/[{<].*?[>}]/g, " ").replace(/\s+/g, " ");
        listText.appendChild(olText);
    }
    idTranscript.innerHTML = listText.outerHTML;
}
function updateTable() {
    if (idTable.tBodies[0].childNodes.length != idTranscript.innerText.split("\n").length) {
        alert(`Table length:${idTable.tBodies[0].childNodes.length} and translation ${idTranscript.innerText.split("\n").length} are different`);
        document.body.style.cursor = "";
        return;
    }
    let aText = idTranscript.innerText.split("\n");
    idTable.tBodies[0].childNodes.forEach((row,index)=>{
        let text = aText[index];
        let tags = [];
        row.children[5].textContent.replace(/[{<].*?[>}]/g, (t)=>{
            tags.push(t);
            tags.length == 1 && tags.push(text);
        }
        )
        row.children[5].textContent = (tags.length) ? tags.join("") : text;
    }
    )
    document.body.style.cursor = "";
}
function fixTimeStartTable() {
    idTable.tBodies[0]&&idTable.tBodies[0].childNodes&&idTable.tBodies[0].childNodes.forEach((row,index)=>{

        let tStart = row.children[1].textContent.toSeconds();
        let tEnd = row.children[2].textContent.toSeconds();
        let text = row.children[5].textContent.length;

        let tEstimated = text / +idCPS.value;
        if (tEnd - tStart < tEstimated) {
            tCorrected = tEnd - tEstimated
            timePrevious = row.previousSibling&&row.previousSibling.children[2]&&row.previousSibling.children[2].textContent&&row.previousSibling.children[2].textContent.toSeconds();
            if (timePrevious)
                tCorrected = timePrevious > tCorrected ? timePrevious + .1 : tCorrected;

            row.children[1].textContent = tCorrected.toTime();
            colorCells(row.children[1]);
            colorCells(row.children[2]);
        }
    }
    )
}
function loadedVideo() {
    initTrackRealTime();
    ID_G = 0;
    allTranscript = [];
    removeAllCues(trackRaw);
    removeAllCues(trackTranslation);
    removeAllCues(trackMySubtitle);
    cleanTable();
}
function initTrackRealTime() {
    removeAllCues(trackRealTime);
    cueRealTime = new VTTCue(0,idVideo.duration,"");
	cueRealTime.snapToLines=true;//For bug Version 106, lines jump down to top (setTimeout)
    cueRec = new VTTCue(0,idVideo.duration,"");
    cueRec.align = "left";
    cueRec.line = 0;
	cueRec.snapToLines=false;
    cueCount = new VTTCue(0,idVideo.duration,"");
    cueCount.align = "right";
    cueCount.line = 2;
	cueCount.snapToLines=false;
    trackRealTime.addCue(cueRealTime);
    trackRealTime.addCue(cueRec);
    trackRealTime.addCue(cueCount);
}


function fixTimeStart(tStart, tEnd, text,arrTimesEnd)
{
	if (tStart>=tEnd) {
		tStart = tEnd - (text.length / +idCPS.value);
		let aTimesEnd=arrTimesEnd.filter((a)=>a<tEnd);
		let last = aTimesEnd&&aTimesEnd.slice(-1)[0];
		let lastTimeEnd=last?last:0;
		if (lastTimeEnd > tStart)  
			tStart = lastTimeEnd + .1;
	}
	if (tEnd-tStart<0.5) tStart=tEnd-0.5;
	return [tStart, tEnd, text]
}

function timesPhrase(arr,arrTimesEnd)  // [ [2.34,"my","ID_0_1"],[3.52,"my Trans","ID_0_1"],[4.49,"my Transcription","ID_0_1"] ]
{
		let aSep=[],tLastStart,tLastEnd,textLast,fixR=0;
		let tBreak = idDistanceSentences.value/1000;
		arr.forEach(function(a, index, array) {
		
			if (index == 0 || tLastStart < 0) { tLastStart = a[0];tLastEnd = a[0];textLast = a[1];idGroupLast=a[2];  }
			if (  ( a[0] - tLastEnd + fixR> tBreak && textLast!=a[1] ) || idGroupLast!=a[2] || index == array.length - 1 ) 
			{
				aSep.push(   [tLastStart, tLastEnd, textLast,idGroupLast] );
				if (index == array.length - 1 && textLast!=a[1] ) {

					if (a[0] - tLastEnd + fixR> tBreak) { aSep.push( [ a[0], a[0], a[1], a[2] ] ) }
					else { aSep[aSep.length - 1][1] = a[0]; aSep[aSep.length - 1][2] = a[1]	}
				}
				tLastStart = a[0];
			}
			
			fixR = (index != 0 && textLast == a[1] && idGroupLast==a[2])?a[0]-tLastEnd:0; //Fix time for same text repeat
			tLastEnd = a[0];textLast = a[1]; idGroupLast=a[2] });
			
		backText=aSep.map((c)=>c[2]);
		//remove text duplicate previous line and Fix Start Times
		aSep.forEach( function(a,index)
			{ 
				a[2] =  (index==0 || a[3]!=aSep[index-1][3] )?a[2]:findDifference(backText[index-1],a[2]);
				[a[0]] = fixTimeStart(a[0], a[1], a[2], arrTimesEnd )
			});
 return aSep; //[ 2.34, 4.49, "Transcription","ID_0_1" ]
}
function recognitionResult(e) {
    allResult = e;//log
    //uCue(cueRec, sRecWhite);
    let IDGroupSentence = "ID_" + ID_G + "_" + e.resultIndex;
    transcript = e.results[e.resultIndex][0].transcript;
	isFinal= e.results[e.resultIndex].isFinal;
    allTranscript.push([idVideo.currentTime, transcript, IDGroupSentence, isFinal]);
    if(!isFinal) {arrSeparations.push([idVideo.currentTime,transcript,IDGroupSentence]), allTimend.push(idVideo.currentTime)}
	//add transcription final (group transcription/good)
    if (isFinal) {
        countP++;
		let arrTemp=timesPhrase( arrSeparations, allTimend );
		arrTranscript=arrTranscript.concat(arrTemp);
		tStart = arrTemp.length?arrTemp[0][0]:idVideo.currentTime;
		tEnd = arrTemp.length?arrTemp.slice(-1)[0][1]:idVideo.currentTime;
		[tStart] = fixTimeStart(tStart, tEnd, transcript, resultFinal.map((a)=>a[1]) );
		resultFinal.push([tStart,tEnd,transcript,IDGroupSentence]);
		arrSeparations=[];
		bGroup=true;
		if (timeWaitFinish != -1) {clearTimeout(timeWaitFinish);cleanTerminate();};
    }
	else {
		bGroup=false;
	}
	if (transcript==lastTranscript) return;
	
	clearTimeout(timer);
	timer = setTimeout(	 function(){lastTranscript=transcript;}, idDistanceSentences.value   );
	
	idTranscript.removeAttribute("_msthash");
	idTranscript.innerHTML = idFilter.value==2?transcript:findDifference(lastTranscript,transcript);
}

//update text/transcript in video(real-time)
function vttRealTime(text) {
    if (!trackRealTime || !cueRealTime) return;

    if (document.documentElement.className.match('translated')) //prevents mixing translations and text raw(transcript) for real-time when translated/google active.
        if (idTranscript.lastChild.nodeName == "#text")
            return;

	if (idTranscript.hasAttribute("_msttexthash")) 
		if (!idTranscript.hasAttribute("_msthash")) 
			return;	
	cueRealTime.text = `<c.${bGroup?"textGreen":"textWhite"}> ${text}`+"</c>" ;
	cueRealTime.endTime=idVideo.currentTime+2
}
function loadTranscriptToRawVTT() {
    showingTrack(trackRaw);
	let delay=idDelayRecognition.value / 1000 ;
	idFilter.value != 2 && arrTranscript.forEach(  function (t,i){
		let text=t[2];
		let cueVtt = new VTTCue(t[0] - delay ,t[1] - delay,text);
		trackRaw.addCue(cueVtt);
	});
	
	idFilter.value !=1 && resultFinal.forEach(  function (t,i){
		let cueVtt = new VTTCue(t[0] - delay,t[1] - delay,t[2]);
		if (+idFilter.value == 0) cueVtt.line = 0;
		trackRaw.addCue(cueVtt);
	});

}
function findDifference(str1, str2) {
    let pos = 0;
    str2.split('').some((val,i)=>{
        if (val != str1.charAt(i)) {
            pos = i;
            return true;
        }
    }
    )
    return str2.substr(pos);
}
function startStopRecognition() {
    if (waitTerminate || idVideo.src == "")
        return;
    try {
        recognition.lang = idSelectDialect.value;
        recognition.start();
        cBusy();
    } catch (e) {
        idVideo.pause();
        recognition.stop();
    }
}
function loadSrcVideo(files) {
    if (files.length != 1)	return;
    idVideo.getAttribute('src') && URL.revokeObjectURL(idVideo.getAttribute('src'));
    idVideo.setAttribute('src', URL.createObjectURL(files[0]));
    idVideo.load();
    nameVideo = files[0].name.removeExtension()
}
function loadSrcSubtitle(files) {
    if (files.length != 1)
        return;
    file = files[0];
    nameVideo = file.name.removeExtension();
    contentS = ["WEBVTT\r\n\r\n"];
    fReader = new FileReader();
    fReader.readAsText(file)
    switch (file.name.toLowerCase().split('.').slice(-1)[0]) {
    case "srt":
        fReader.onload = (e)=>setSrcSub(new Blob(contentS.concat(e.target.result.replace(/(\d\d:\d\d),(\d\d\d)/g, '$1.$2'))));
        break;
    case "ass":
        const fAss = /(\d+:\d\d:\d\d.\d\d),(\d+:\d\d:\d\d.\d\d),(.*?),.*?,.*?,.*?,.*?,.*?,(.*)/g;
        fReader.onload = (e)=>setSrcSub(new Blob(e.target.result.replace(fAss, (a,tStart,tEnd,style,text)=>contentS.push(`${tStart}0 --> ${tEnd}0${style == "Top" && " line:0"}\r\n${text}\r\n\r\n`)) && contentS));
        break;
    default:
        setSrcSub(file)
    }
    function setSrcSub(b) {
        idFileVTT.getAttribute('src') && URL.revokeObjectURL(idFileVTT.getAttribute('src'));
        idFileVTT.setAttribute('src', URL.createObjectURL(b, {type: "text/plain"}));
        showingTrack(idVideo.textTracks.getTrackById("idFileVTT"));
    }
}

var langs = [['English', ['en-AU', 'Australia'], ['en-CA', 'Canada'], ['en-IN', 'India'], ['en-KE', 'Kenya'], ['en-TZ', 'Tanzania'], ['en-GH', 'Ghana'], ['en-NZ', 'New Zealand'], ['en-NG', 'Nigeria'], ['en-ZA', 'South Africa'], ['en-PH', 'Philippines'], ['en-GB', 'United Kingdom'], ['en-US', 'United States']], ['Español/Spanish', ['es-AR', 'Argentina'], ['es-BO', 'Bolivia'], ['es-CL', 'Chile'], ['es-CO', 'Colombia'], ['es-CR', 'Costa Rica'], ['es-EC', 'Ecuador'], ['es-SV', 'El Salvador'], ['es-ES', 'España'], ['es-US', 'Estados Unidos'], ['es-GT', 'Guatemala'], ['es-HN', 'Honduras'], ['es-MX', 'México'], ['es-NI', 'Nicaragua'], ['es-PA', 'Panamá'], ['es-PY', 'Paraguay'], ['es-PE', 'Perú'], ['es-PR', 'Puerto Rico'], ['es-DO', 'República Dominicana'], ['es-UY', 'Uruguay'], ['es-VE', 'Venezuela']], ['中文/Chinese', ['cmn-Hans-CN', '普通话 (中国大陆)/Mandarin (Mainland China)'], ['cmn-Hans-HK', '普通话 (香港)/Mandarin (Hong Kong)'], ['cmn-Hant-TW', '中文 (台灣)/Chinese (Taiwan)'], ['yue-Hant-HK', '粵語 (香港)/Cantonese (Hong Kong)']], ['日本語/Japanese', ['ja-JP']], ['한국어/Korean', ['ko-KR']], ['Deutsch/German', ['de-DE']], ['Français/French', ['fr-FR']], ['Italiano/Italian', ['it-IT', 'Italia'], ['it-CH', 'Svizzera']], ['Português/Portuguese', ['pt-BR', 'Brasil'], ['pt-PT', 'Portugal']], ['Pусский/Esloven/Russian', ['ru-RU']], ['Nederlands/Dutch', ['nl-NL']], ['Català/Catalan', ['ca-ES']], ['Čeština/Czech', ['cs-CZ']], ['Polski/Polish', ['pl-PL']], ['Filipino', ['fil-PH']], ['Slovenčina', ['sk-SK']], ['Suomi/Finland', ['fi-FI']], ['Svenska/Swedish', ['sv-SE']], ['Tiếng Việt/Vietnamese', ['vi-VN']], ['Türkçe/Turkish', ['tr-TR']], ['Română/Romanian', ['ro-RO']], ['Українська/Ukrainian', ['uk-UA']], ['हिन्दी/Hindi', ['hi-IN']], ['Galego', ['gl-ES']], ['Dansk', ['da-DK']], ['Afrikaans', ['af-ZA']], ['አማርኛ/Amárico', ['am-ET']], ['Azərbaycanca', ['az-AZ']], ['বাংলা/Bengali', ['bn-BD', 'বাংলাদেশ'], ['bn-IN', 'ভারত']], ['Bahasa Indonesia', ['id-ID']], ['Bahasa Melayu', ['ms-MY']], ['Euskara/Basque', ['eu-ES']], ['Basa Jawa/Javanese base', ['jv-ID']], ['ગુજરાતી/Gujarati', ['gu-IN']], ['Hrvatski/Croatian', ['hr-HR']], ['IsiZulu', ['zu-ZA']], ['Íslenska/Icelandic', ['is-IS']], ['ಕನ್ನಡ/Kannada', ['kn-IN']], ['ភាសាខ្មែរ/Camboyan', ['km-KH']], ['Latviešu/Latvian', ['lv-LV']], ['Lietuvių/Lithuanian', ['lt-LT']], ['മലയാളം/Malayalam', ['ml-IN']], ['मराठी/Marathi', ['mr-IN']], ['Magyar/Hungarian', ['hu-HU']], ['ລາວ/Lao', ['lo-LA']], ['नेपाली भाषा/Nepali', ['ne-NP']], ['Norsk bokmål/Norwegian Bokmål', ['nb-NO']], ['සිංහල/the Sinhala', ['si-LK']], ['Slovenščina/Slovene', ['sl-SI']], ['Basa Sunda/Sundanese', ['su-ID']], ['Kiswahili', ['sw-TZ', 'Tanzania'], ['sw-KE', 'Kenya']], ['ქართული/Georgian', ['ka-GE']], ['Հայերեն/Armenian', ['hy-AM']], ['தமிழ்/Tamil', ['ta-IN', 'இந்தியா'], ['ta-SG', 'சிங்கப்பூர்'], ['ta-LK', 'இலங்கை'], ['ta-MY', 'மலேசியா']], ['తెలుగు/Telugu', ['te-IN']], ['اُردُو/Sindhi', ['ur-PK', 'پاکستان'], ['ur-IN', 'بھارت']], ['Ελληνικά/Greek', ['el-GR']], ['български/Bulgarian', ['bg-BG']], ['Српски/Serbian', ['sr-RS']], ['ภาษาไทย/Thailand', ['th-TH']]];
for (var i = 0; i < langs.length; i++) {
    idSelectLanguage.options[i] = new Option(langs[i][0],i);
}
idSelectLanguage.selectedIndex = INDEXLANGUAGE;
idSelectDialect.selectedIndex = 0;
function updateCountry() {
    for (var i = idSelectDialect.options.length - 1; i >= 0; i--) {
        idSelectDialect.remove(i);
    }
    var list = langs[idSelectLanguage.selectedIndex];
    for (var i = 1; i < list.length; i++) {
        idSelectDialect.options.add(new Option(list[i][1],list[i][0]));
    }
    idSelectDialect.style.visibility = list[1].length == 1 ? 'hidden' : 'visible';
    languageName = langs[idSelectLanguage.value][0].replace(/\/.*/, "");
    languageCode = idSelectDialect.value.replace(/-.*/, "");
    idTranscript.innerText = languageName;
	idCPS.value=["日本語/Japanese","한국어/Korean","中文/Chinese"].includes(idSelectLanguage.options[idSelectLanguage.selectedIndex].text)?6:10;
	textButtonFixTime();
}
function keyEvent(e) {
    if (document.activeElement == idVideo || document.fullscreenElement ) 
	{
        (e.ctrlKey == false && e.altKey == false && e.shiftKey == false && e.code == "KeyP" && !e.repeat) && (idVideo.paused ? idVideo.play() : idVideo.pause());
        (e.ctrlKey == false && e.altKey == false && e.shiftKey == false && e.code == "KeyQ" && !e.repeat) && startStopRecognition();
        (e.ctrlKey == false && e.altKey == false && e.shiftKey == false && e.code == "KeyM") && idVideo.currentTime++;
        (e.ctrlKey == false && e.altKey == false && e.shiftKey == false && e.code == "KeyN") && idVideo.currentTime--;
        (e.ctrlKey == false && e.altKey == false && e.shiftKey == true && e.code == "KeyM") && (idVideo.currentTime += 0.5);
        (e.ctrlKey == false && e.altKey == false && e.shiftKey == true && e.code == "KeyN") && (idVideo.currentTime -= 0.5);

        (e.ctrlKey == false && e.altKey == true && e.shiftKey == false && e.key == "ArrowDown") && (idVideo.currentTime -= 5);
        (e.ctrlKey == false && e.altKey == true && e.shiftKey == false && e.key == "ArrowUp") && (idVideo.currentTime += 5);
        (e.ctrlKey == true && e.altKey == false && e.shiftKey == false && e.key == "ArrowDown") && (idVideo.currentTime -= 10);
        (e.ctrlKey == true && e.altKey == false && e.shiftKey == false && e.key == "ArrowUp") && (idVideo.currentTime += 10);
        (e.ctrlKey == false && e.altKey == false && e.shiftKey == false && e.key == "PageDown") && nextCue("NEXT");
        (e.ctrlKey == false && e.altKey == false && e.shiftKey == false && e.key == "PageUp") && nextCue("PREV");

        (e.ctrlKey == false && e.altKey == false && e.shiftKey == false && e.code == "KeyT") && cueNearFixTimeStartEnd(0); //Fix startTime for Next Cue
        (e.ctrlKey == false && e.altKey == false && e.shiftKey == false && e.code == "KeyU") && cueNearFixTimeStartEnd(1); //Fix endTime for Previous Cue
        if (e.ctrlKey == false && e.altKey == true && e.shiftKey == false && e.key == "+")  for (let cue of aLastCueActive) {cue.startTime += 0.1};
		if (e.ctrlKey == false && e.altKey == true && e.shiftKey == false && e.key == "-")  for (let cue of aLastCueActive) {cue.startTime -= 0.1};
		(e.ctrlKey == false && e.altKey == false && e.shiftKey == false && e.key == "+") && (lastCueActive && (lastCueActive.startTime += 0.1));
        (e.ctrlKey == false && e.altKey == false && e.shiftKey == false && e.key == "-") && (lastCueActive && (lastCueActive.startTime -= 0.1));
        (e.ctrlKey == false && e.altKey == false && e.shiftKey == true && e.key == "+") && (lastCueActive && (lastCueActive.endTime += 0.1));
        (e.ctrlKey == false && e.altKey == false && e.shiftKey == true && e.key == "-") && (lastCueActive && (lastCueActive.endTime -= 0.1));
        if (e.ctrlKey == true && e.altKey == false && e.shiftKey == false && e.code == "Delete")for (cues of currentTrack.activeCues) {currentTrack.removeCue(cues)}
        (e.ctrlKey == false && e.altKey == false && e.shiftKey == false && e.code == "Delete") && (cueActives() && removeCueCurrentTrack(lastCueActive));
    }

    (e.ctrlKey == true && e.altKey == false && e.shiftKey == false && e.code == "KeyQ" && !e.repeat) && startStopRecognition();
    (e.ctrlKey == true && e.altKey == true && e.shiftKey == false && e.code == "KeyJ") && splitLine(e);
    (e.ctrlKey == true && e.altKey == true && e.shiftKey == false && e.code == "ArrowUp") && joinLines("PREV");
    (e.ctrlKey == true && e.altKey == true && e.shiftKey == false && e.code == "ArrowDown") && joinLines("NEXT");
    (e.ctrlKey == false && e.altKey == true && e.shiftKey == false && e.key == "PageDown") && nextCue("NEXT");
    (e.ctrlKey == false && e.altKey == true && e.shiftKey == false && e.key == "PageUp") && nextCue("PREV");
	
    function joinLines(sibling) {
        if (document.activeElement && document.activeElement.parentNode.nodeName != "TR") return;
        let nodeA = (sibling == "NEXT") ? document.activeElement.parentNode : document.activeElement.parentNode.previousSibling;
        let nodeB = (sibling == "NEXT") ? document.activeElement.parentNode.nextSibling : document.activeElement.parentNode;
        if (!nodeB || !nodeA) return;
        if (nodeB.children[3].innerText || nodeA.children[3].innerText) return;

        nodeA.children[2].innerText = nodeB.children[2].innerText;
        nodeA.children[5].innerText = nodeA.children[5].innerText + " " + nodeB.children[5].innerText;
        idTable.deleteRow(nodeB.rowIndex);
        nodeA.children[5].focus();
    }
}

function newSubtitle(e) {
    if (!currentTrack || e.repeat || !(e.ctrlKey == false && e.altKey == false && e.shiftKey == false && e.code == "KeyC"))    return;
    
	if (e.type == "keydown") {
        idVideo.play();
        cueVttNS = new VTTCue(0,idVideo.duration,"My New Line Subtitle");
        currentTrack.addCue(cueVttNS);
        tstartCue = idVideo.currentTime;
    } else {
        cueVttNS.startTime = tstartCue;
        cueVttNS.endTime = idVideo.currentTime - .001;
        //currentTime different time actual, or cue forever showing
        cueVttNS.text = "";
        VTTtoTable(currentTrack);

    }

}

function splitLine(e) {
    if (e.target.name != "transcription")  return;
    let cells = e.target.parentNode.children;
    let tStart = cells[1].textContent.toSeconds();
    let tEnd = cells[2].textContent.toSeconds();
    let r = window.getSelection().getRangeAt(0);
    let text = r.startContainer.textContent.split("");
    let cpt = (tEnd - tStart) / text.length;
    endOffset = r.startOffset == r.endOffset ? text.length : r.endOffset;
    textExtracted = text.splice(r.startOffset, endOffset - r.startOffset).join("");
    tBoth = (tStart + (text.length * cpt));
    cells[2].textContent = tBoth.toTime();
    cells[5].textContent = text.join("");
    rowTable(tBoth + .001, tEnd, cells[3].textContent, textExtracted, cells[1].parentNode.rowIndex);
}
function addRemMiliseconds(e) {
    (e.ctrlKey == false && e.altKey == false && e.shiftKey == false && e.key == "+") && (e.target.textContent = (e.target.textContent.toSeconds() + 0.1).toTime());
    (e.ctrlKey == false && e.altKey == false && e.shiftKey == false && e.key == "-") && (e.target.textContent = (e.target.textContent.toSeconds() - 0.1).toTime());
    if (isNaN(String.fromCharCode(e.which)))  e.preventDefault();

    colorCells(e.target);
    idVideo.currentTime = e.target.textContent.toSeconds();

}
function removeCueCurrentTrack(cue) {
    let listCues = listCuesCurrentTrackFilter();
    if (listCues) {
        index = listCues.indexOf(cue);
        lastCueActive = index >= 1 ? listCues[index - 1] : undefined;
    }
    currentTrack.removeCue(cue);
}

function cueActives() {
    if (!currentTrack || currentTrack.label == "RealTime" || !currentTrack.activeCues.length)	return false;
    aLastCueActive = [];
    for (let cue of currentTrack.activeCues) {
        aLastCueActive.push(cue);
        if (cue.line == "auto")
            lastCueActive = cue;
        else if (currentTrack.activeCues.length == 1)
            lastCueActive = cue;
    }
    return true;
}

function cueNearFixTimeStartEnd(iPrev) // 1 Previous Fix endTime ,   0 Next Fix startTime
{
    let i = 0, cueSelected = undefined;
    if (currentTrack.activeCues && currentTrack.activeCues.length == 0) {
        for (cue of currentTrack.cues) {
            if (cue.startTime >= idVideo.currentTime)
                break;
            i++;
        }
        cueSelected = currentTrack.cues[i - iPrev];
    } else cueSelected = currentTrack.activeCues[0];
    
    if (!cueSelected)  return;

    if (iPrev)
        cueSelected.endTime = idVideo.currentTime;
    else
        cueSelected.startTime = idVideo.currentTime;

}

idHelp.title = `
‎Shortcuts General
Ctrl+Q: Start/Stop	 Recognition Speech
alt + PageDown: Next cue
alt + PageUp:	Previous cue

‎SHORTCUTS IN TABLE
Ctrl+alt+ArrowUp: Join row previous
Ctrl+alt+ArrowDown: Join row next
Ctrl+alt+J: Split row in cursor or selection
SHORTCUTS IN CELLS TIME 
- add 100 ms to start time
+ rest 100 ms to start time

‎SHORTCUTS IN VIDEO
Q: Start/Stop	 Recognition Speech
P: Play/Stop video
C: Hold down the key C, create a new subtitle line empty
Alt + ArrowUp: Forward 5 seconds
Alt + ArrowDown:	Rewind 5 seconds
Ctrl + ArrowUp: Forward 10 seconds
Ctrl + ArrowDown:	Rewind 10 seconds
PageDown: Next cue
PageUp:	Previous cue
M: Forward 1 second
Shift + M:  Forward 500 ms
N: Rewind 1 second
Shift + N: Rewind 500 ms
Del: Delete cue(s) active
Ctrl + Del: Delete all cue(s) active

+: add 100 ms to start time of last cue active.
-: rest 100 ms to star time of last cue active.
Alt + +: add 100 ms to start time of last cue active(All).
Alt + -: rest 100 ms to star time of last cue active(All).
ctrl + +: add 100 ms to end time of last cue active.
ctrl + -: rest 100 ms to end time of last cue active.
`
var headASS = `[Script Info]
Title:
WrapStyle: 0
ScaledBorderAndShadow: yes
PlayResX: 1280
PlayResY: 720
YCbCr Matrix: None

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,40,&H00FFFFFF,&H0300FFFF,&H00000000,&H02000000,0,0,0,0,100,100,0,0,1,2,1,2,10,10,10,1
Style: Top,Arial,35,&H00FFFFFF,&H00F6C8A5,&H00000000,&H00000000,0,-1,0,0,100,100,0,0,1,2,0,8,10,10,10,1

[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
`;

const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition
if (SpeechRecognition) {
    recognition = new SpeechRecognition();
    recognition.maxAlternatives = 1;
    recognition.continuous = true;
    recognition.interimResults = true;
    recognition.onstart = recognitionStart;
    recognition.onerror = (e)=>{ idTranscript.innerHTML = "<b>Error: </b>" + e.error;idVideo.pause();};
    recognition.onaudioend = recognitionTerminate;
    recognition.onresult = recognitionResult;
    updateCountry();
} else {
    idGroup2.style.visibility = "hidden";
    idTranscript.innerHTML = "Your browser not support Web Speech API, use last version Chrome/Edge";
}

</script>  
</body>
</html>