<commit_before><!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Transcript Splitter</title>
<link href="libs.css" type="text/css" rel="stylesheet" />
<link href="base.css" type="text/css" rel="stylesheet" />
<link href="transcriptsplitter.css" type="text/css" rel="stylesheet" />
<style>
/***** Layout *****/
	/* All flexbox autoprefixes made with: http://pleeease.io/play/ */
	
	html, body { height: 100%; margin: 0; }
	body { padding: 4px; box-sizing: border-box; }
	
	.menu-wrapper { position: absolute; top: 0; left: 0; z-index: 1; }
	
	menu { position: absolute; margin: 0; white-space: nowrap; background: #fff; }
	menu .menu-opener-wrapper { position: absolute; }
	menu .menu-opener { position: absolute; overflow: hidden; }
	
	body, main, #player-div, #transcript-div { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; }
	body { -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; }
	main, #transcript-div, .transcript, .transsplitterwrap { -webkit-box-flex: 1; -webkit-flex-grow: 1; -ms-flex-positive: 1; flex-grow: 1; }
	
	#player-div, #transcript-div { padding: 4px; box-sizing: border-box; }
	
	#video { min-width: 150px; min-height: 50px; width: auto; height: auto; }
	.transcript { min-width: 250px; min-height: 150px; box-sizing: border-box; }
	
	
/***** Horizontal Layout *****/
	
	body.horiz menu { bottom: 0; left: 0; }
	body.horiz menu .menu-opener-wrapper { left: 0; bottom: 0; right: 0; }
	body.horiz menu .menu-opener { left: 0; top: 0; right: 0; }
	body.horiz menu:hover { top: 0; bottom: auto; }
	body.horiz menu:hover .menu-opener { height: 0; }
	
	body.horiz main { margin-top: 20px; }
	body.horiz #player-div, body.horiz #transcript-div { -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; }
	
	
/***** Vertical Layout *****/
	
	body.vert menu { top: 0; right: 0; }
	body.vert menu .menu-opener-wrapper { top: 0; right: 0; bottom: 0; }
	body.vert menu .menu-opener { top: 0; left: 0; bottom: 0; }
	body.vert menu:hover { left: 0; right: auto; }
	body.vert menu:hover .menu-opener { width: 0; }
	
	body.vert main { margin-left: 40px; }
	body.vert main { -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; }
	
	
/***** Functional *****/
	
	body:not(.splitterMode) .onlySplitterMode { display: none; }
	
	
/***** Design *****/
	
	body { font: 14px Helvetica, arial, sans-serif; }
	a, .link {text-decoration: none; }
	
	#player-controls-div #playbackRates-div { margin: 4px 0; }
	input[type="radio"].noradio+label { margin: 0; padding: 4px 6px; font-weight: bold; }
	input[type="radio"].noradio:checked+label { background: #91FF54; }
	
	.transcript { font-size: 1.2em; }
	
	#player-url-input { width: 800px; min-width: 50px; }
	.canPlayProbably { color: green; font-weight: bold; }
	.canPlayMaybe { color: green; font-weight: bold; }
	.canPlayNo { color: #c33; text-decoration: line-through; }
	
	.show-state-saved { color: #5DA634; }
	.show-state-waiting { color: grey; }
	
	
	
	#video { width: 450px; }
</style>
</head>
<body class="horiz">
<div class="menu-wrapper">
	<menu>
		<div class="menu-opener-wrapper"><div class="menu-opener">Menu</div></div>
		<p class="title"><a href="https://github.com/jlgrall/TranscriptSplitter">Transcript Splitter v0.2</a></p>
		<p><a href="#modal-load" rel="modal:open">Load files</a></p>
		<p><a class="download-transcript" download="transcript.txt" href="data:text/plain;charset=utf-8,...">Save transcript</a></p>
		<p><a class="button-splitter-toggle" href="javascript:void(0);">Splitter mode</a></p>
		<p><a class="button-player-toggle" href="javascript:void(0);">Player on/off</a></p>
		<p><a href="#modal-help" rel="modal:open">Help</a></p>
	</menu>
</div>

<main>
	
	<div id="player-div">
		<video id="video" class="video-js" controls preload="auto">
		</video>
		<div id="player-controls-div" class="controls-div">
			<div id="playbackRates-div" class="control-div">
				Speed:
				<input type="radio" class="noradio" id="0.5" name="playbackRate" value="0.5" /><label for="0.5">0.5</label>
				<input type="radio" class="noradio" id="0.75" name="playbackRate" value="0.75" /><label for="0.75">0.75</label>
				<input type="radio" class="noradio" id="1" name="playbackRate" value="1" checked /><label for="1">1</label>
				<input type="radio" class="noradio" id="1.25" name="playbackRate" value="1.25" /><label for="1.25">1.25</label>
				<input type="radio" class="noradio" id="1.5" name="playbackRate" value="1.5" /><label for="1.5">1.5</label>
				<input type="radio" class="noradio" id="2" name="playbackRate" value="2" /><label for="2">2</label>
			</div>
			<div id="player-sizes-div" class="control-div">
				Size:
				<select id="player-size">
					<optgroup label="Zoom">
						<option value="0.25x">0.25x</option>
						<option value="0.5x">0.5x</option>
						<option value="1x">1x</option>
						<option value="2x">2x</option>
					</optgroup>
					<optgroup label="Width">
						<option value="150px">150px</option>
						<option value="300px">300px</option>
						<option value="450px" selected>450px</option>
						<option value="600px">600px</option>
						<option value="750px">750px</option>
					</optgroup>
					<optgroup label="Height">
						<option value="144p">&nbsp;&nbsp;144p</option>
						<option value="240p">&nbsp;&nbsp;240p</option>
						<option value="360p">&nbsp;&nbsp;360p</option>
						<option value="480p">&nbsp;&nbsp;480p</option>
						<option value="720p">&nbsp;&nbsp;720p</option>
						<option value="1080p">1080p</option>
					</optgroup>
				</select>
			</div>
		</div>
	</div>

	<div id="transcript-div">
		<textarea class="transcript"></textarea>
		<div id="transcript-controls-div" class="controls-div">
			<div class="control-div">
				Insert:
				<button class="button-insert-bold">&lt;b&gt;</button>
				<button class="button-insert-italic">&lt;i&gt;</button>
				<button class="button-insert-underline">&lt;u&gt;</button>
				<button class="button-insert-timestamp">[Timestamp]</button>
			</div>
			<div class="onlySplitterMode control-div">
				Font: <button class="button-font-kind">monospaced &lt;-&gt; proportional</button>,
				action: <button class="button-transcript-splitSentences">Split sentences</button>
			</div>
		</div>
	</div>
	
</main>

<p>Progress stored in <a href="https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage" target="_blank">LocalStorage</a> automatically every 30 sec:
	<span class="show-state-saved show-state">Saved</span><span class="show-state-waiting show-state">...</span>
</p>

<div id="modal-load" class="modal-hider">
	<div id="load-div">
		<p class="title" id="player-url-input-div">
			<label for="player-url-input">Load video by URL:</label>
			<input type="text" id="player-url-input" class="modal-focus" name="source" value="" />
		</p>	

		<p class="title">Or just drop video or transcript files on this page to load them</p>

		<p class="title">Or <a class="button-open-files" href="javascript:void(0);">choose them from your computer</a>
			<input type="file" id="open-files" multiple accept="video/*,text/*,.txt,.vtt,.srt,.sub,.sbv,.cap,.smi,.sami,.rt" style="display:none">
		</p>

		<p class="title"><span class="canPlay-hide">Accepted files, result depends on</span><span class="canPlay-show" style="display: none;">Files accepted by</span> your browser:</p>
		<!-- MIME and canPlayType() infos:
			http://www.leanbackplayer.com/test/h5mt.html
			http://demos.flowplayer.org/videotest/canplay.html
			https://wiki.whatwg.org/wiki/Video_type_parameters
		 -->
		 <p class="header">Video files:</p>
		<ul>
			<li><span class="canPlay" data-type='video/mp4'>.mp4</span>, <span class="canPlay" data-type='video/x-m4v'>.m4v</span></li>
			<li><span class="canPlay" data-type='video/webm'>.webm</span> (codecs: <span class="canPlay" data-type='video/webm; codecs="vp8"'>VP8</span>, <span class="canPlay" data-type='video/webm; codecs="vp9"'>VP9</span>, <span class="canPlay" data-type='video/webm; codecs="opus"'>Opus</span>, <span class="canPlay" data-type='video/webm; codecs="vorbis"'>Vorbis</span>)</li>
			<li><span class="canPlay" data-type='video/ogg'>.ogv</span> (codecs: <span class="canPlay" data-type='video/ogg; codecs="theora"'>Theora</span>, <span class="canPlay" data-type='video/ogg; codecs="dirac"'>Dirac</span>, <span class="canPlay" data-type='video/ogg; codecs="vorbis"'>Vorbis</span>, <span class="canPlay" data-type='video/ogg; codecs="speex"'>Speex</span>, <span class="canPlay" data-type='video/ogg; codecs="flac"'>FLAC</span>)</li>
			<li><span class="canPlay" data-type='video/mpeg'>.mpeg</span>, <span class="canPlay" data-type='video/mpeg'>.mpg</span></li>
			<li><span class="canPlay" data-type='video/avi'>.avi</span>, <span class="canPlay" data-type='video/divx'>.divx</span></li>
			<li><span class="canPlay" data-type='video/quicktime'>.mov</span>, <span class="canPlay" data-type='video/quicktime'>.qt</span></li>
			<li><span class="canPlay" data-type='video/3gpp'>.3gp</span></li>
		</ul>
		<p class="header">Transcript files:</p>
		<ul>
			<li>.txt</li>
			<li>.vtt, .srt, .sub, .sbv, .cap, .smi, .sami, .rt</li>
		</ul>
	</div>
</div>

<div id="modal-help" class="modal-hider">
	<div id="help-div">
		<div id="shortcuts-div">
			<p class="title">Shortcuts</p>
			<ul>
				<li class="shortcut"><span class="action">Play/pause</span>: <span class="keys">Tab</span></li>
				<li class="shortcut"><span class="action">-3 secs (of listening speed)</span>: <span class="keys">Shift + Tab</span></li>
				<li class="shortcut"><span class="action">Add/remove bold tags</span>: <span class="keys">Ctrl + B (Mac: Command + B)</span></li>
				<li class="shortcut"><span class="action">Add/remove italic tags</span>: <span class="keys">Ctrl + I (Mac: Command + I)</span></li>
				<li class="shortcut"><span class="action">Add/remove underline tags</span>: <span class="keys">Ctrl + U (Mac: Command + U)</span></li>
			</ul>
			<p class="tip">Double-click timestamps in the transcript to seek the video</p>
		</div>
	</div>
</div>

<div class="modal">
	<div class="modal-inner">
		<a rel="modal:close">X</a>
		<div class="modal-content"></div>
	</div>
</div>

<script src="jquery-2.1.4.min.js"></script>
<script src="libs.js"></script>
<script src="gadget.js"></script>
<script src="transcriptvideo.js"></script>
<script src="transcriptplayer.js"></script>
<script src="transcriptwriter.js"></script>
<script src="transcriptsaver.js"></script>
<script src="transcriptsplitter.js"></script>
<script>
$(function($) {
	
/***** Init *****/
	
	
	var $window = $(window),
		$document = $(document),
		$html = $("html"),
		$body = $("body"),
		$video = $("#video"),
		$playerDiv = $("#player-div"),
		player,
		$playbackRates = $("input[name='playbackRate']"),
		$textarea = $(".transcript"),
		$playerURLInput = $("#player-url-input"),
		$openFilesInput = $("#open-files"),
		$downloadTranscriptButtons = $(".download-transcript");
	
	var modal = new VanillaModal({
			onOpen: function() {
				$(modal.$.modalContent).find(".modal-focus").focus();
			},
		});
	
	$.fn.transcriptSplitter.prototype.options.keepWidth = false;
	
	
/***** Player *****/
	
	$video.transcriptVideo();
	player = $playerDiv.transcriptPlayer({
		$transcriptVideo: $video,
	}).transcriptPlayer("getPlayer");
	
	$playerDiv.on("playbackRateChanged", function(e, data) {
		var playbackRate = data.playbackRate,
			$playbackRate = $playbackRates.filter("[value='" + playbackRate + "']");
		if($playbackRate.length > 0) $playbackRate.click();
		else $playbackRates.filter(":checked").prop("checked", false);
	});
	
	$playbackRates.change(function() {
		var playbackRate = parseFloat(this.value);
		if(!isNaN(playbackRate) && playbackRate != player.playbackRate) {
			$playerDiv.transcriptPlayer("playbackRate", playbackRate);
		}
	});
	
	$document.on("click", ".button-player-toggle", function() {
		var display = $playerDiv.css("display") === "none" ? "" : "none";
		$playerDiv.css("display", display);
	});
	
/***** Transcript *****/
	
	$textarea.transcriptWriter({
		player: player,
	}).on("dblclick", function() {
		$textarea.transcriptWriter("seekToTimestampAtCaret");
	});
	
	$document.on("click", ".button-insert-bold", function() {
		$textarea.transcriptWriter("toggleWrapSelectionWithTag", "b");
	});
	$document.on("click", ".button-insert-italic", function() {
		$textarea.transcriptWriter("toggleWrapSelectionWithTag", "i");
	});
	$document.on("click", ".button-insert-underline", function() {
		$textarea.transcriptWriter("toggleWrapSelectionWithTag", "u");
	});
	$document.on("click", ".button-insert-timestamp", function() {
		$textarea.transcriptWriter("insertTimestampAtSelection");
	});
	
	
/***** Files (load, drop, download) *****/
	
	var isVideoFile = function(file, name, ext, type) {
			return type.indexOf("video") !== -1;
		},
		isTranscriptFile = function(file, name, ext, type) {
			if(type.indexOf("text") === 0) return true;
			if(/txt|vtt|srt|sub|sbv|cap|smi|sami|rt/.test(ext)) return true;
			if(type === "") return true;
			return false;
		},
		fileOpened = function(file) {
			var name = file.name,
				ext = name.slice((name.lastIndexOf(".") - 1 >>> 0) + 2),	// http://stackoverflow.com/questions/190852/how-can-i-get-file-extensions-with-javascript/12900504#12900504
				type = file.type;
			console.log(file.name, type);
			if(isVideoFile(file, name, ext, type)) {
				$playerDiv.transcriptPlayer("setSrc", URL.createObjectURL(file));
			}
			else if(isTranscriptFile(file, name, ext, type)) {
				var reader = new FileReader();
				reader.onload = function(e) {
					$textarea.val(e.target.result);
					$textarea.transcriptWriter("transcriptChanged");
				};
				reader.readAsText(file);
			}
		};
	
	$playerURLInput.on("keydown", function(e) {
		if(e.which === 13) {
			var src = $playerURLInput.val().trim();
			$playerDiv.transcriptPlayer("setSrc", src);
			$body.transcriptSaver("savePlayerSource");
		}
	});
	
	$document.on("click", ".button-open-files", function() {
		$openFilesInput.click();
	});
	$openFilesInput.on("change", function() {
		var files = this.files;
		for (var i = 0; i < files.length; i++) fileOpened(files[i]);
	});
	
	$downloadTranscriptButtons.on("mousedown click mouseenter", function() {	// mousedown for right-click, click for keyboard activation
		this.href = "data:text/plain;charset=utf-8," + encodeURIComponent($textarea.val());
	});
	
	// Inspired by: http://www.html5rocks.com/en/tutorials/file/dndfiles/
	$body.on("dragover", function(e) {
		if($.inArray('Files', e.originalEvent.dataTransfer.types) !== -1) {
			e.originalEvent.dataTransfer.dropEffect = "copy"; // Explicitly show this is a copy.
		}
		return false;
	}).on("drop", function(e) {
		var files = e.originalEvent.dataTransfer.files;
		for (var i = 0; i < files.length; i++) fileOpened(files[i]);
		return false;
	});
	
	$(".canPlay-hide").css("display", "none");
	$(".canPlay-show").css("display", "");
	$playerDiv.transcriptPlayer("processCanPlayElements", $(".canPlay"));
	
	
/***** LocalStorage *****/
	
	$body.transcriptSaver({
		$playerURLInput: $playerURLInput,
		$transcriptPlayer: $playerDiv,
		$transcriptWriter: $textarea,
	}).transcriptSaver("loadAll");
	
	$textarea.on("transcriptChanged", function() {
		$html.removeClass("transcript-state-saved").addClass("transcript-state-waiting");
	});
	$body.on("progressSaved", function() {
		$html.removeClass("transcript-state-waiting").addClass("transcript-state-saved");
	}).on("progressLoaded", function() {
		$html.removeClass("transcript-state-waiting").addClass("transcript-state-saved");
	});
	
	
/***** Splitter *****/
	
	// http://webdesignerwall.com/tutorials/css-elastic-videos
	// https://css-tricks.com/NetMag/FluidWidthVideo/Article-FluidWidthVideo.php
	
		
	var scrolledlinesDiv = undefined,
		styledFontElems = undefined,
		isSplitterMode = function() {
			return !!$textarea.data("gadget-transcriptSplitter");
		},
		toggleSplitterMode = function() {
			if(isSplitterMode()) {
				$textarea.transcriptSplitter("destroy");
				scrolledlinesDiv = undefined;
				styledFontElems = undefined;
				$body.removeClass("splitterMode");
			}
			else {
				$textarea.transcriptSplitter();
				scrolledlinesDiv = $(".transsplitterwrap .scrolledlines");
				styledFontElems = $([$textarea[0], scrolledlinesDiv[0]]);
				$body.addClass("splitterMode");
			}
		};
	
	$document.on("click", ".button-splitter-toggle", toggleSplitterMode);
	
	$(".button-font-kind").click(function(e) {
		if(!isSplitterMode()) return;
		var ta = $textarea[0],
			oldScrollPercent = (ta.scrollTop + ta.clientHeight/2) / ta.scrollHeight;
		if($textarea[0].style.fontFamily) {
			styledFontElems.css("fontSize", "");
			styledFontElems.css("fontFamily", "");
			$textarea.css("backgroundImage", "");
		}
		else {
			var oldHeight = $textarea.innerHeight();	
			styledFontElems.css("fontFamily", "Roboto, arial, sans-serif");
			var height = $textarea.innerHeight(),
				fontSize = parseInt($textarea.css("fontSize"), 10);
			styledFontElems.css("fontSize", fontSize * oldHeight / height);
			$textarea.css("backgroundImage", "none");
		}
		ta.scrollTop = oldScrollPercent * ta.scrollHeight - ta.clientHeight/2;
	});
	
	$(".button-transcript-splitSentences").click(function(e) {
		if(!isSplitterMode()) return;
		var text = $textarea.val(),
			// Original: text.replace(/(\.|\!|\?)( |\n(?!\n))/g, "$1\n\n");
			newText = text.replace(/(\.|\!|\?)(?: \n?|\n)\n?/g, function(match, p1, offset) {
				return text.length > offset + match.length ? p1 + "\n\n" : match;
			});
		if(newText !== text) {
			$textarea.val(newText).focus().change();
		}
	});
	
	
/***** Global *****/
	
	$document.on("focusout", function(e) {
		var rel = e.relatedTarget;
		if(rel == null || !$(rel).is(":text, :password, textarea")) {
			$textarea.focus();
		}
	});
	$textarea.focus();
	
	
	
	
	
	
	
	$("menu").on("click", function(e) {
		if(e.target != this) return;
		if($body.hasClass("horiz")) $body.removeClass("horiz").addClass("vert");
		else $body.removeClass("vert").addClass("horiz");
	});
	
	//toggleSplitterMode();
	
});
</script>
</body>
</html><commit_msg>Add placeholder and autofocus to textarea<commit_after><!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Transcript Splitter</title>
<link href="libs.css" type="text/css" rel="stylesheet" />
<link href="base.css" type="text/css" rel="stylesheet" />
<link href="transcriptsplitter.css" type="text/css" rel="stylesheet" />
<style>
/***** Layout *****/
	/* All flexbox autoprefixes made with: http://pleeease.io/play/ */
	
	html, body { height: 100%; margin: 0; }
	body { padding: 4px; box-sizing: border-box; }
	
	.menu-wrapper { position: absolute; top: 0; left: 0; z-index: 1; }
	
	menu { position: absolute; margin: 0; white-space: nowrap; background: #fff; }
	menu .menu-opener-wrapper { position: absolute; }
	menu .menu-opener { position: absolute; overflow: hidden; }
	
	body, main, #player-div, #transcript-div { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; }
	body { -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; }
	main, #transcript-div, .transcript, .transsplitterwrap { -webkit-box-flex: 1; -webkit-flex-grow: 1; -ms-flex-positive: 1; flex-grow: 1; }
	
	#player-div, #transcript-div { padding: 4px; box-sizing: border-box; }
	
	#video { min-width: 150px; min-height: 50px; width: auto; height: auto; }
	.transcript { min-width: 250px; min-height: 150px; box-sizing: border-box; }
	
	
/***** Horizontal Layout *****/
	
	body.horiz menu { bottom: 0; left: 0; }
	body.horiz menu .menu-opener-wrapper { left: 0; bottom: 0; right: 0; }
	body.horiz menu .menu-opener { left: 0; top: 0; right: 0; }
	body.horiz menu:hover { top: 0; bottom: auto; }
	body.horiz menu:hover .menu-opener { height: 0; }
	
	body.horiz main { margin-top: 20px; }
	body.horiz #player-div, body.horiz #transcript-div { -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; }
	
	
/***** Vertical Layout *****/
	
	body.vert menu { top: 0; right: 0; }
	body.vert menu .menu-opener-wrapper { top: 0; right: 0; bottom: 0; }
	body.vert menu .menu-opener { top: 0; left: 0; bottom: 0; }
	body.vert menu:hover { left: 0; right: auto; }
	body.vert menu:hover .menu-opener { width: 0; }
	
	body.vert main { margin-left: 40px; }
	body.vert main { -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; }
	
	
/***** Functional *****/
	
	body:not(.splitterMode) .onlySplitterMode { display: none; }
	
	
/***** Design *****/
	
	body { font: 14px Helvetica, arial, sans-serif; }
	a, .link {text-decoration: none; }
	
	#player-controls-div #playbackRates-div { margin: 4px 0; }
	input[type="radio"].noradio+label { margin: 0; padding: 4px 6px; font-weight: bold; }
	input[type="radio"].noradio:checked+label { background: #91FF54; }
	
	.transcript { font-size: 1.2em; }
	
	#player-url-input { width: 800px; min-width: 50px; }
	.canPlayProbably { color: green; font-weight: bold; }
	.canPlayMaybe { color: green; font-weight: bold; }
	.canPlayNo { color: #c33; text-decoration: line-through; }
	
	.show-state-saved { color: #5DA634; }
	.show-state-waiting { color: grey; }
	
	
	
	#video { width: 450px; }
</style>
</head>
<body class="horiz">
<div class="menu-wrapper">
	<menu>
		<div class="menu-opener-wrapper"><div class="menu-opener">Menu</div></div>
		<p class="title"><a href="https://github.com/jlgrall/TranscriptSplitter">Transcript Splitter v0.2</a></p>
		<p><a href="#modal-load" rel="modal:open">Load files</a></p>
		<p><a class="download-transcript" download="transcript.txt" href="data:text/plain;charset=utf-8,...">Save transcript</a></p>
		<p><a class="button-splitter-toggle" href="javascript:void(0);">Splitter mode</a></p>
		<p><a class="button-player-toggle" href="javascript:void(0);">Player on/off</a></p>
		<p><a href="#modal-help" rel="modal:open">Help</a></p>
	</menu>
</div>

<main>
	
	<div id="player-div">
		<video id="video" class="video-js" controls preload="auto">
		</video>
		<div id="player-controls-div" class="controls-div">
			<div id="playbackRates-div" class="control-div">
				Speed:
				<input type="radio" class="noradio" id="0.5" name="playbackRate" value="0.5" /><label for="0.5">0.5</label>
				<input type="radio" class="noradio" id="0.75" name="playbackRate" value="0.75" /><label for="0.75">0.75</label>
				<input type="radio" class="noradio" id="1" name="playbackRate" value="1" checked /><label for="1">1</label>
				<input type="radio" class="noradio" id="1.25" name="playbackRate" value="1.25" /><label for="1.25">1.25</label>
				<input type="radio" class="noradio" id="1.5" name="playbackRate" value="1.5" /><label for="1.5">1.5</label>
				<input type="radio" class="noradio" id="2" name="playbackRate" value="2" /><label for="2">2</label>
			</div>
			<div id="player-sizes-div" class="control-div">
				Size:
				<select id="player-size">
					<optgroup label="Zoom">
						<option value="0.25x">0.25x</option>
						<option value="0.5x">0.5x</option>
						<option value="1x">1x</option>
						<option value="2x">2x</option>
					</optgroup>
					<optgroup label="Width">
						<option value="150px">150px</option>
						<option value="300px">300px</option>
						<option value="450px" selected>450px</option>
						<option value="600px">600px</option>
						<option value="750px">750px</option>
					</optgroup>
					<optgroup label="Height">
						<option value="144p">&nbsp;&nbsp;144p</option>
						<option value="240p">&nbsp;&nbsp;240p</option>
						<option value="360p">&nbsp;&nbsp;360p</option>
						<option value="480p">&nbsp;&nbsp;480p</option>
						<option value="720p">&nbsp;&nbsp;720p</option>
						<option value="1080p">1080p</option>
					</optgroup>
				</select>
			</div>
		</div>
	</div>

	<div id="transcript-div">
		<textarea class="transcript" placeholder=" Write and edit your transcript here..." autofocus></textarea>
		<div id="transcript-controls-div" class="controls-div">
			<div class="control-div">
				Insert:
				<button class="button-insert-bold">&lt;b&gt;</button>
				<button class="button-insert-italic">&lt;i&gt;</button>
				<button class="button-insert-underline">&lt;u&gt;</button>
				<button class="button-insert-timestamp">[Timestamp]</button>
			</div>
			<div class="onlySplitterMode control-div">
				Font: <button class="button-font-kind">monospaced &lt;-&gt; proportional</button>,
				action: <button class="button-transcript-splitSentences">Split sentences</button>
			</div>
		</div>
	</div>
	
</main>

<p>Progress stored in <a href="https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage" target="_blank">LocalStorage</a> automatically every 30 sec:
	<span class="show-state-saved show-state">Saved</span><span class="show-state-waiting show-state">...</span>
</p>

<div id="modal-load" class="modal-hider">
	<div id="load-div">
		<p class="title" id="player-url-input-div">
			<label for="player-url-input">Load video by URL:</label>
			<input type="text" id="player-url-input" class="modal-focus" name="source" value="" />
		</p>	

		<p class="title">Or just drop video or transcript files on this page to load them</p>

		<p class="title">Or <a class="button-open-files" href="javascript:void(0);">choose them from your computer</a>
			<input type="file" id="open-files" multiple accept="video/*,text/*,.txt,.vtt,.srt,.sub,.sbv,.cap,.smi,.sami,.rt" style="display:none">
		</p>

		<p class="title"><span class="canPlay-hide">Accepted files, result depends on</span><span class="canPlay-show" style="display: none;">Files accepted by</span> your browser:</p>
		<!-- MIME and canPlayType() infos:
			http://www.leanbackplayer.com/test/h5mt.html
			http://demos.flowplayer.org/videotest/canplay.html
			https://wiki.whatwg.org/wiki/Video_type_parameters
		 -->
		 <p class="header">Video files:</p>
		<ul>
			<li><span class="canPlay" data-type='video/mp4'>.mp4</span>, <span class="canPlay" data-type='video/x-m4v'>.m4v</span></li>
			<li><span class="canPlay" data-type='video/webm'>.webm</span> (codecs: <span class="canPlay" data-type='video/webm; codecs="vp8"'>VP8</span>, <span class="canPlay" data-type='video/webm; codecs="vp9"'>VP9</span>, <span class="canPlay" data-type='video/webm; codecs="opus"'>Opus</span>, <span class="canPlay" data-type='video/webm; codecs="vorbis"'>Vorbis</span>)</li>
			<li><span class="canPlay" data-type='video/ogg'>.ogv</span> (codecs: <span class="canPlay" data-type='video/ogg; codecs="theora"'>Theora</span>, <span class="canPlay" data-type='video/ogg; codecs="dirac"'>Dirac</span>, <span class="canPlay" data-type='video/ogg; codecs="vorbis"'>Vorbis</span>, <span class="canPlay" data-type='video/ogg; codecs="speex"'>Speex</span>, <span class="canPlay" data-type='video/ogg; codecs="flac"'>FLAC</span>)</li>
			<li><span class="canPlay" data-type='video/mpeg'>.mpeg</span>, <span class="canPlay" data-type='video/mpeg'>.mpg</span></li>
			<li><span class="canPlay" data-type='video/avi'>.avi</span>, <span class="canPlay" data-type='video/divx'>.divx</span></li>
			<li><span class="canPlay" data-type='video/quicktime'>.mov</span>, <span class="canPlay" data-type='video/quicktime'>.qt</span></li>
			<li><span class="canPlay" data-type='video/3gpp'>.3gp</span></li>
		</ul>
		<p class="header">Transcript files:</p>
		<ul>
			<li>.txt</li>
			<li>.vtt, .srt, .sub, .sbv, .cap, .smi, .sami, .rt</li>
		</ul>
	</div>
</div>

<div id="modal-help" class="modal-hider">
	<div id="help-div">
		<div id="shortcuts-div">
			<p class="title">Shortcuts</p>
			<ul>
				<li class="shortcut"><span class="action">Play/pause</span>: <span class="keys">Tab</span></li>
				<li class="shortcut"><span class="action">-3 secs (of listening speed)</span>: <span class="keys">Shift + Tab</span></li>
				<li class="shortcut"><span class="action">Add/remove bold tags</span>: <span class="keys">Ctrl + B (Mac: Command + B)</span></li>
				<li class="shortcut"><span class="action">Add/remove italic tags</span>: <span class="keys">Ctrl + I (Mac: Command + I)</span></li>
				<li class="shortcut"><span class="action">Add/remove underline tags</span>: <span class="keys">Ctrl + U (Mac: Command + U)</span></li>
			</ul>
			<p class="tip">Double-click timestamps in the transcript to seek the video</p>
		</div>
	</div>
</div>

<div class="modal">
	<div class="modal-inner">
		<a rel="modal:close">X</a>
		<div class="modal-content"></div>
	</div>
</div>

<script src="jquery-2.1.4.min.js"></script>
<script src="libs.js"></script>
<script src="gadget.js"></script>
<script src="transcriptvideo.js"></script>
<script src="transcriptplayer.js"></script>
<script src="transcriptwriter.js"></script>
<script src="transcriptsaver.js"></script>
<script src="transcriptsplitter.js"></script>
<script>
$(function($) {
	
/***** Init *****/
	
	
	var $window = $(window),
		$document = $(document),
		$html = $("html"),
		$body = $("body"),
		$video = $("#video"),
		$playerDiv = $("#player-div"),
		player,
		$playbackRates = $("input[name='playbackRate']"),
		$textarea = $(".transcript"),
		$playerURLInput = $("#player-url-input"),
		$openFilesInput = $("#open-files"),
		$downloadTranscriptButtons = $(".download-transcript");
	
	var modal = new VanillaModal({
			onOpen: function() {
				$(modal.$.modalContent).find(".modal-focus").focus();
			},
		});
	
	$.fn.transcriptSplitter.prototype.options.keepWidth = false;
	
	
/***** Player *****/
	
	$video.transcriptVideo();
	player = $playerDiv.transcriptPlayer({
		$transcriptVideo: $video,
	}).transcriptPlayer("getPlayer");
	
	$playerDiv.on("playbackRateChanged", function(e, data) {
		var playbackRate = data.playbackRate,
			$playbackRate = $playbackRates.filter("[value='" + playbackRate + "']");
		if($playbackRate.length > 0) $playbackRate.click();
		else $playbackRates.filter(":checked").prop("checked", false);
	});
	
	$playbackRates.change(function() {
		var playbackRate = parseFloat(this.value);
		if(!isNaN(playbackRate) && playbackRate != player.playbackRate) {
			$playerDiv.transcriptPlayer("playbackRate", playbackRate);
		}
	});
	
	$document.on("click", ".button-player-toggle", function() {
		var display = $playerDiv.css("display") === "none" ? "" : "none";
		$playerDiv.css("display", display);
	});
	
/***** Transcript *****/
	
	$textarea.transcriptWriter({
		player: player,
	}).on("dblclick", function() {
		$textarea.transcriptWriter("seekToTimestampAtCaret");
	});
	
	$document.on("click", ".button-insert-bold", function() {
		$textarea.transcriptWriter("toggleWrapSelectionWithTag", "b");
	});
	$document.on("click", ".button-insert-italic", function() {
		$textarea.transcriptWriter("toggleWrapSelectionWithTag", "i");
	});
	$document.on("click", ".button-insert-underline", function() {
		$textarea.transcriptWriter("toggleWrapSelectionWithTag", "u");
	});
	$document.on("click", ".button-insert-timestamp", function() {
		$textarea.transcriptWriter("insertTimestampAtSelection");
	});
	
	
/***** Files (load, drop, download) *****/
	
	var isVideoFile = function(file, name, ext, type) {
			return type.indexOf("video") !== -1;
		},
		isTranscriptFile = function(file, name, ext, type) {
			if(type.indexOf("text") === 0) return true;
			if(/txt|vtt|srt|sub|sbv|cap|smi|sami|rt/.test(ext)) return true;
			if(type === "") return true;
			return false;
		},
		fileOpened = function(file) {
			var name = file.name,
				ext = name.slice((name.lastIndexOf(".") - 1 >>> 0) + 2),	// http://stackoverflow.com/questions/190852/how-can-i-get-file-extensions-with-javascript/12900504#12900504
				type = file.type;
			console.log(file.name, type);
			if(isVideoFile(file, name, ext, type)) {
				$playerDiv.transcriptPlayer("setSrc", URL.createObjectURL(file));
			}
			else if(isTranscriptFile(file, name, ext, type)) {
				var reader = new FileReader();
				reader.onload = function(e) {
					$textarea.val(e.target.result);
					$textarea.transcriptWriter("transcriptChanged");
				};
				reader.readAsText(file);
			}
		};
	
	$playerURLInput.on("keydown", function(e) {
		if(e.which === 13) {
			var src = $playerURLInput.val().trim();
			$playerDiv.transcriptPlayer("setSrc", src);
			$body.transcriptSaver("savePlayerSource");
		}
	});
	
	$document.on("click", ".button-open-files", function() {
		$openFilesInput.click();
	});
	$openFilesInput.on("change", function() {
		var files = this.files;
		for (var i = 0; i < files.length; i++) fileOpened(files[i]);
	});
	
	$downloadTranscriptButtons.on("mousedown click mouseenter", function() {	// mousedown for right-click, click for keyboard activation
		this.href = "data:text/plain;charset=utf-8," + encodeURIComponent($textarea.val());
	});
	
	// Inspired by: http://www.html5rocks.com/en/tutorials/file/dndfiles/
	$body.on("dragover", function(e) {
		if($.inArray('Files', e.originalEvent.dataTransfer.types) !== -1) {
			e.originalEvent.dataTransfer.dropEffect = "copy"; // Explicitly show this is a copy.
		}
		return false;
	}).on("drop", function(e) {
		var files = e.originalEvent.dataTransfer.files;
		for (var i = 0; i < files.length; i++) fileOpened(files[i]);
		return false;
	});
	
	$(".canPlay-hide").css("display", "none");
	$(".canPlay-show").css("display", "");
	$playerDiv.transcriptPlayer("processCanPlayElements", $(".canPlay"));
	
	
/***** LocalStorage *****/
	
	$body.transcriptSaver({
		$playerURLInput: $playerURLInput,
		$transcriptPlayer: $playerDiv,
		$transcriptWriter: $textarea,
	}).transcriptSaver("loadAll");
	
	$textarea.on("transcriptChanged", function() {
		$html.removeClass("transcript-state-saved").addClass("transcript-state-waiting");
	});
	$body.on("progressSaved", function() {
		$html.removeClass("transcript-state-waiting").addClass("transcript-state-saved");
	}).on("progressLoaded", function() {
		$html.removeClass("transcript-state-waiting").addClass("transcript-state-saved");
	});
	
	
/***** Splitter *****/
	
	// http://webdesignerwall.com/tutorials/css-elastic-videos
	// https://css-tricks.com/NetMag/FluidWidthVideo/Article-FluidWidthVideo.php
	
		
	var scrolledlinesDiv = undefined,
		styledFontElems = undefined,
		isSplitterMode = function() {
			return !!$textarea.data("gadget-transcriptSplitter");
		},
		toggleSplitterMode = function() {
			if(isSplitterMode()) {
				$textarea.transcriptSplitter("destroy");
				scrolledlinesDiv = undefined;
				styledFontElems = undefined;
				$body.removeClass("splitterMode");
			}
			else {
				$textarea.transcriptSplitter();
				scrolledlinesDiv = $(".transsplitterwrap .scrolledlines");
				styledFontElems = $([$textarea[0], scrolledlinesDiv[0]]);
				$body.addClass("splitterMode");
			}
		};
	
	$document.on("click", ".button-splitter-toggle", toggleSplitterMode);
	
	$(".button-font-kind").click(function(e) {
		if(!isSplitterMode()) return;
		var ta = $textarea[0],
			oldScrollPercent = (ta.scrollTop + ta.clientHeight/2) / ta.scrollHeight;
		if($textarea[0].style.fontFamily) {
			styledFontElems.css("fontSize", "");
			styledFontElems.css("fontFamily", "");
			$textarea.css("backgroundImage", "");
		}
		else {
			var oldHeight = $textarea.innerHeight();	
			styledFontElems.css("fontFamily", "Roboto, arial, sans-serif");
			var height = $textarea.innerHeight(),
				fontSize = parseInt($textarea.css("fontSize"), 10);
			styledFontElems.css("fontSize", fontSize * oldHeight / height);
			$textarea.css("backgroundImage", "none");
		}
		ta.scrollTop = oldScrollPercent * ta.scrollHeight - ta.clientHeight/2;
	});
	
	$(".button-transcript-splitSentences").click(function(e) {
		if(!isSplitterMode()) return;
		var text = $textarea.val(),
			// Original: text.replace(/(\.|\!|\?)( |\n(?!\n))/g, "$1\n\n");
			newText = text.replace(/(\.|\!|\?)(?: \n?|\n)\n?/g, function(match, p1, offset) {
				return text.length > offset + match.length ? p1 + "\n\n" : match;
			});
		if(newText !== text) {
			$textarea.val(newText).focus().change();
		}
	});
	
	
/***** Global *****/
	
	$document.on("focusout", function(e) {
		var rel = e.relatedTarget;
		if(rel == null || !$(rel).is(":text, :password, textarea")) {
			$textarea.focus();
		}
	});
	$textarea.focus();
	
	
	
	
	
	
	
	$("menu").on("click", function(e) {
		if(e.target != this) return;
		if($body.hasClass("horiz")) $body.removeClass("horiz").addClass("vert");
		else $body.removeClass("vert").addClass("horiz");
	});
	
	//toggleSplitterMode();
	
});
</script>
</body>
</html>
{'fraction_non_alphanumeric': 0.20636942675159237, 'fraction_numerical': 0.01464968152866242, 'mean_word_length': 4.007441860465116, 'pattern_counts': {'":': 4, '<': 611, '<?xml version=': 0, '>': 617, 'https://': 8, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5360874', 'n_tokens_mistral': 14760, 'n_tokens_neox': 12910, 'n_words': 2310}