<!DOCTYPE HTML>
<html>

<!--
  pgn4web javascript chessboard
  copyright (C) 2009-2013 Paolo Casaschi
  see README file and http://pgn4web.casaschi.net
  for credits, license and more details
-->

<head>

<title>pgn4web integration with HTML5 video</title>

<link rel="shortcut icon" href="pawn.ico" />

<style type="text/css">

html,
body {
  margin: 0px;
  padding: 0px;
}

body {
  color: black;
  background: white;
  font-family: sans-serif;
}

a {
  color: black;
  text-decoration: none;
}

.boardTable {
  height: 272px;
  width: 272px;
  box-shadow: 0px 0px 15px #663300;
}

.boardBox {
  width: 272px;
  text-align:left;
  font-weight:bold;
  text-shadow: 1px 1px 3px #C4C4C4
}

.videoBox {
  box-shadow: 0px 0px 16px #663300;
}

.pieceImage {
  height: 26px;
  width: 26px;
}

.whiteSquare,
.blackSquare,
.highlightWhiteSquare,
.highlightBlackSquare {
  width: 32px;
  height: 32px;
  border-style: solid;
  border-width: 1px;
}

.whiteSquare,
.highlightWhiteSquare {
  border-color: #FFCC99;
  background: #FFCC99;
}

.blackSquare,
.highlightBlackSquare {
  border-color: #CC9966;
  background: #CC9966;
}

.highlightWhiteSquare,
.highlightBlackSquare {
  border-style: inset;
  border-color: #CC9966;
}

</style>

<script type="text/javascript" src="libs/swfobject/swfobject.js"></script>

<script src="pgn4web.js" type="text/javascript"></script>

</head>

<body>

<!-- paste your PGN below and make sure you dont specify an external source with SetPgnUrl() -->
<form style="display: none;"><textarea style="display: none;" id="pgnText">

</textarea></form>

<script type="text/javascript">
"use strict";

function gup(name) {

  name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
  var regexS = "[\\?&]"+name+"=([^&#]*)";
  // commented below to match first occurrence (to avoid users overruling setting)
  // regexS = regexS+"(?!.*"+regexS+")"; // matches the LAST occurrence
  var regex = new RegExp( regexS, "i" );
  var results = regex.exec( window.location.href );
  if (results !== null) { return decodeURIComponent(results[1]); }

  // allows for short version of the URL parameters, for instance sC matches squareColor
  var compact_name = name.charAt(0);
  for (var i=1; i<name.length; i++) {
    if (name.charAt(i).match(/[A-Z]/)) { compact_name = compact_name + name.charAt(i).toLowerCase(); }
  }
  name = compact_name;

  name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
  regexS = "[\\?&]"+name+"=([^&#]*)";
  // commented below to match first occurrence (to avoid users overruling setting)
  // regexS = regexS+"(?!.*"+regexS+")"; // matches the LAST occurrence
  regex = new RegExp( regexS, "i" );

  results = regex.exec( window.location.href );
  if (results !== null) { return decodeURIComponent(results[1]); }

  return "";
}

SetCommentsIntoMoveText(false);

var pgnFile_default = "";
var videoUrl_default = "";
var youtubeVideoid_default = "";
var videoWidth_default = "480";
var videoHeight_default = "270";
function print_default(value) { return value ? value : "null"; }

var videoHelp = ((gup("help") == "true") || (gup("help") == "t"));

if (videoHelp) {
  document.write("<PRE>");
  document.write("pgn4web video.html URL parameters:\n");
  document.write("\n");
  document.write(" - pgnData = PGN URL to load as PGN data source, overrides youtubeVideoid; default " + print_default(pgnFile_default) + "\n");
  document.write(" - videoUrl = video URL to load as video source, overrides youtubeVideoid; default " + print_default(videoUrl_default) + "\n");
  document.write(" - youtubeVideoid = youtube video id to use as video source and/or as PGN data source (PGN data expected in the youtube video description); default " + print_default(youtubeVideoid_default) + "\n");
  document.write("\n");
  document.write(" - videoWidth = video width; default " + print_default(videoWidth_default) + "\n");
  document.write(" - videoHeigth = video height; default " + print_default(videoHeight_default) + "\n");
  document.write(" - videoAutoplay = if set true video autoplays at page load; default true\n");
  document.write(" - videoLoop = if set true video loops; default false\n");
  document.write(" - horizontalLayout = if set true the chessboard is next to the video, otherwise underneath; default true\n");
  document.write("\n");
  document.write(" - setupVideotimes = if set true, setup special mode to add video times info to the PGN data; default false\n");
  document.write(" - testVideotimes = if set true, detects missing/wrong video times info in the PGN data; default false\n");
  document.write(" - ignoreVideotimes = if set true, video times info is ignored; default false\n");
  document.write("\n");
  document.write(" - help = true\n");
  document.write("\n");
  document.write("Either the pgnData or the youtubeVideoid URL parameter is required as PGN data source\n");
  document.write("Either the videoUrl or the youtubeVideoid URL parameter is required as video source\n");
  document.write("\n");
  document.write("URL parameters can be shortened: for example pgnData => pd, videoUrl => vu, youtubeVideoid => yv\n");
  document.write("Also supplied textual values can be shortened: true => t, false => f\n");
  document.write("\n");
  document.write("</PRE>\n<HR>\n");
}

var horizontalLayout = ((gup("horizontalLayout") != "false") && (gup("hl") != "f"));

</script>

<center>

<table>

<script type="text/javascript">
"use strict";

if (!horizontalLayout) { document.write('<tr><td height="20"></td></tr>'); }

</script>

<tr><td>

<div id="videoBox" class="videoBox">
<div id="videodiv" style="text-align:center; font-style:italic;">
<video id="videoPlayer" controls="controls"><br/><br/>warning: HTML5 video support required</video>
</div>
</div>

</td>

<script type="text/javascript">
"use strict";

if (horizontalLayout) { document.write('<td width="20"></td>'); }
else { document.write('</tr><tr><td height="20"></td></tr><tr>'); }

</script>

<td align="center">

<div class="boardBox">
<div style="margin-bottom:1em;">
<div id="GameBlackClock" style="float:right;"></div>
<span id="GameBlack"></span>&nbsp;
</div>
<div id="GameBoard"><div style="width:272px; height:272px;"></div></div>
<div style="margin-top:1em;">
<div id="GameWhiteClock" style="float:right;"></div>
<span id="GameWhite"></span>&nbsp;
</div>
</div>

</td></tr></table>

<form id="setupBox" style="display:none;">
<table style="width:100%;"><tr valign="bottom"><td>

<div id="videoTimeInfo" style="font-weight:bold; font-size:small; margin-top:2em;"></div>

</td><td>

</td><td>

<div style="text-align:right; margin-top:2em;">
<span style="font-weight:bold; font-size:small; margin-right:0.5em; margin-left:0.5em;">time correction:</span>
<input id="timeCorrection" type="text" value="-0.3" style="width:6em;" title="time correction in seconds to be added to the captured video time, usually negative to take into account the lag between seeing a move and clicking the capture button, default -0.3" />
</div>

</td></tr><tr valign="bottom"><td style="width:30%">

<div style="font-weight:bold; font-size:small; margin-right:0.5em;">next move: <span id="GameNextMove"></span></div>

</td><td style="width:40%">

<input id="captureButton" type="button" value="capture videotime into the PGN data" disabled="1" onclick="getVideotime();" style="width:100%;" />

</td><td style="width:30%">

<input id="togglevideoButton" type="button" value="play/pause video" disabled="1" onclick="toggleVideoPlayPause();" style="width:100%;" />

</td></tr><tr><td colspan="3">

<textarea id="setupOutput" style="height:200px; width:100%; padding: 10px; border-color: lightgray;">
% you can add video times information to the PGN file by pressing the capture button
% each time a new game starts, including the first game, and each time a move is made;

</textarea>

</td></tr></table>
</form>

</center>

<script type="text/javascript">
"use strict";

// accepts pgnData as alias for pgnFile for consistency with board.html
var pgnFile;
if ((pgnFile = gup("pgnData")) === "") {
  if ((pgnFile = gup("pgnFile")) === "") {
    pgnFile = pgnFile_default;
  }
}

var videoUrl = gup("videoUrl");
if (videoUrl === "") { videoUrl = videoUrl_default; }


var youtubeVideoid = gup("youtubeVideoid");
if (youtubeVideoid === "") { youtubeVideoid = youtubeVideoid_default; }

var videoWidth = gup("videoWidth");
if (videoWidth === "") { videoWidth = videoWidth_default; }

var videoHeight = gup("videoHeight");
if (videoHeight === "") { videoHeight = videoHeight_default; }

var videoAutoplay = ((gup("videoAutoplay") != "false") && (gup("videoAutoplay") != "f"));

var videoLoop = ((gup("videoLoop") == "true") || (gup("videoLoop") == "t"));

var ignoreVideotimes = ((gup("ignoreVideotimes") == "true") || (gup("ignoreVideotimes") == "t"));

var testVideotimes = ((gup("testVideotimes") == "true") || (gup("testVideotimes") == "t"));

var setupVideotimes = ((gup("setupVideotimes") == "true") || (gup("setupVideotimes") == "t"));
document.getElementById("setupBox").style.display = setupVideotimes ? "inline" : "none";


var paramError = "";

var theObj;

if (theObj = document.getElementById("videoBox")) {
  if (videoWidth) { theObj.style.width = videoWidth + "px"; }
  if (videoHeight) { theObj.style.height = videoHeight + "px"; }
}

if (videoUrl) {
  if (theObj = document.getElementById("videoPlayer")) {
    theObj.src = videoUrl;
    if (videoWidth) { theObj.width = videoWidth; }
    if (videoHeight) { theObj.height = videoHeight; }
    if (videoAutoplay) { theObj.autoplay = "autoplay"; }
    if (videoLoop) { theObj.loop = "loop"; }
  }
} else if (youtubeVideoid) {
  document.title = "pgn4web integration with youtube";
  document.getElementById("videodiv").innerHTML = "<br/><br/>warning: flash video support required";
  var expressInstall = null;
  var flashvars = { };
  var params = { allowScriptAccess: "always", allowFullScreen: "true" };
  var atts = { id: "videoPlayer" };
  swfobject.embedSWF("http://www.youtube.com/e/" + youtubeVideoid + "?enablejsapi=1&autohide=1" + (videoAutoplay ? "&autoplay=1" : "&autoplay=0") + "&disablekb=1&fs=1" + (videoLoop ? "&loop=1&playlist=" + youtubeVideoid : "&loop=0") + "&rel=0&showinfo=0", "videodiv", videoWidth, videoHeight, "8", expressInstall, flashvars, params, atts);
} else {
  paramError += "error: videoUrl or youtubeVideoid URL parameter required as video source\n\n";
}

function youtubeFeedCallback(res) {
    if (res && res["data"] && res["data"]["description"]) {
      document.getElementById("pgnText").value = res["data"]["description"];
    }
    gotYuotubeDetails = true;
    new_start_pgn4web();
}

function getYoutubeDetails(videoid) {
  var headID = document.getElementsByTagName("head")[0];
  var newScript = document.createElement("script");
  newScript.type = "text/javascript";
  newScript.src = "http://gdata.youtube.com/feeds/api/videos/" + videoid + "?v=2&alt=jsonc&callback=youtubeFeedCallback";
  headID.appendChild(newScript);
}

if (pgnFile) {
  SetPgnUrl(pgnFile);
} else if (youtubeVideoid) {
  getYoutubeDetails(youtubeVideoid);
} else {
  paramError += "error: pgnData or youtubeVideoid URL parameter required as PGN data source\n\n";
}

if (paramError && (!videoHelp)) {
  if (confirm("pgn4web video.html\n\n" + paramError + "click OK for more help")) {
    window.location.search += (window.location.search ? "&" : "?") + "help=true";
  }
}

SetImagePath ("images/alpha/26");
SetImageType("png");
SetHighlightOption(true);
SetShortcutKeysEnabled(ignoreVideotimes);

if (!ignoreVideotimes) {
  clearShortcutSquares("A", "123456");
  clearShortcutSquares("BCDEFGH", "1234567");
}

var videotimeForGame = new Array();
function getVideotimeForGame() {
  videotimeForGame = new Array();
  for (var thisGame=0; thisGame<numberOfGames; thisGame++) {
    videotimeForGame[thisGame] = parseFloat(customPgnHeaderTag("VideoTime", null, thisGame));
    if (!videotimeForGame[thisGame]) { videotimeForGame[thisGame] = 0; }
  }
}

var videotimeAtPly = new Array();
function getVideotimeAtPly() {
  videotimeAtPly = new Array();
  for (var thisPly=StartPly; thisPly<=StartPly+PlyNumber; thisPly++) {
    videotimeAtPly[thisPly] = parseFloat(customPgnCommentTag("vt", null, thisPly));
    if (!videotimeAtPly[thisPly]) { videotimeAtPly[thisPly] = 0; }
  }
}

function customFunctionOnPgnGameLoad() {
  if (setupVideotimes) {
    document.getElementById("togglevideoButton").disabled = 0;
    document.getElementById("captureButton").disabled = 0;
    document.getElementById("captureButton").focus();
  }
  getVideotimeAtPly();
}

function runTestVidoetimes() {
  for (var thisGame=0; thisGame<numberOfGames; thisGame++) {
    if ((thisGame>0) && (videotimeForGame[thisGame] <= videotimeForGame[thisGame-1])) {
      alert("warning: " + pgnFile + " missing/wrong VideoTime header tag for game " + (thisGame+1));
      return false;
    }
    Init(thisGame);
    for (var thisPly=StartPly; thisPly<=StartPly+PlyNumber; thisPly++) {
      if ((thisPly>StartPly) && (videotimeAtPly[thisPly] <= videotimeAtPly[thisPly-1])) {
        alert("warning: " + pgnFile + " missing/wrong %vt comment at ply " + thisPly + " of game " + (thisGame+1));
        return false;
      }
    }
  }
  alert("info: " + pgnFile + " video time values looks ok");
  return true;
}

var syncInterval = 456; // milliseconds
var syncTimer = null;
function customFunctionOnPgnTextLoad() {
  getVideotimeForGame();
  if (testVideotimes) { runTestVidoetimes(); }
  if (syncTimer) { clearTimeout(syncTimer); }
  if (!ignoreVideotimes) { syncTimer = setTimeout("syncBoard();", syncInterval); }
}

// assumes arrayValues ordered
function findIndexInOrderedArray(target, arrayValues, arrayFirst, arrayLast, previous) {
  if (previous < arrayFirst) { previous = arrayFirst; }
  if (previous > arrayLast ) { previous = arrayLast ; }
  var found = previous;
  if ((previous > arrayFirst) && (target < arrayValues[previous])) {
    for (var index=previous; (index>arrayFirst) && (target < arrayValues[index]); index--) {}
    found = index;
  } else if ((previous < arrayLast) && (target > arrayValues[previous+1])) {
    for (index=previous; (index<arrayLast) && (target > arrayValues[index+1]); index++) {}
    found = index;
  }
  return found;
}

function videoPlayerCurrentTime() {
  var videoPlayer = document.getElementById("videoPlayer");
  var videoCurrentTime = 0;
  if (videoPlayer && videoPlayer.currentTime) { videoCurrentTime = videoPlayer.currentTime; }
  else if (videoPlayer && videoPlayer.getCurrentTime) { videoCurrentTime = videoPlayer.getCurrentTime(); }
  document.getElementById("videoTimeInfo").innerHTML = "video time: " + (Math.round(videoCurrentTime * 1000) / 1000);
  return videoCurrentTime;
}

function syncBoard() {
  if (syncTimer) { clearTimeout(syncTimer); }

  var videoCurrentTime = videoPlayerCurrentTime();

  if (!setupVideotimes) {
    var foundGame = findIndexInOrderedArray(videoCurrentTime, videotimeForGame, 0, numberOfGames-1, currentGame);
    if (foundGame !== currentGame) { Init(foundGame); }

    var foundPly = findIndexInOrderedArray(videoCurrentTime, videotimeAtPly, StartPly, StartPly+PlyNumber, CurrentPly);
    if (foundPly != CurrentPly) { GoToMove(foundPly); }
  }

  syncTimer = setTimeout("syncBoard();", syncInterval);
}

var setupOutputBox = document.getElementById("setupOutput");
function writeSetupOutput(text) {
  if (setupOutputBox) { setupOutputBox.value += text; }
  setupOutputBox.scrollTop = setupOutputBox.scrollHeight;
}

function newGameHeader(thisGame, videotime) {
  var newHeader = pgnHeader[thisGame] ? pgnHeader[thisGame] : "";
  newHeader = newHeader.replace(/\[\s*VideoTime\s*"([^"]*)"\s*\]\s*/g, "");
  newHeader = newHeader.replace(/(^\s*|\s*$)/g, "");
  newHeader += "\n[VideoTime \"" + videotime + "\"]";
  return newHeader;
}

var startedVideotimes = false;
var endedVideotimes = false;
function getVideotime() {
  if (!setupVideotimes || endedVideotimes) { return; }
  var storedVideotime = videoPlayerCurrentTime();
  var timeCorrection = parseFloat(document.getElementById("timeCorrection").value);
  if (timeCorrection) { storedVideotime += timeCorrection; }
  storedVideotime = Math.round(storedVideotime * 10) / 10;
  if (!startedVideotimes) {
    writeSetupOutput("\n" + newGameHeader(currentGame, storedVideotime) + "\n\n");
    startedVideotimes = true;
    return;
  }
  if (CurrentPly === StartPly+PlyNumber) {
    if (currentGame+1 < numberOfGames) {
      Init(currentGame + 1);
      writeSetupOutput("\n" + newGameHeader(currentGame, storedVideotime) + "\n\n");
    }
  } else {
    GoToMove(CurrentPly + 1);
    var moreText = "";
    moreText += Math.ceil(CurrentPly/2) + ".";
    if (!(CurrentPly % 2)) { moreText += ".."; }
    moreText += " " + Moves[CurrentPly-1] + " {[%vt " + storedVideotime + "]";
    var comment = MoveComments[CurrentPly].replace(/\s*\[%vt [\.0-9]*\]\s*/g, "");
    if (comment) { moreText += " " + comment.replace(/[{}]/g, ""); }
    moreText += "}\n";
    writeSetupOutput(moreText);
  }
  if (CurrentPly === StartPly+PlyNumber) {
    if (gameResult[currentGame]) { writeSetupOutput(gameResult[currentGame] + "\n"); }
    if (currentGame+1 === numberOfGames) {
      writeSetupOutput("\n\n% end of games and moves");
      endedVideotimes = true;
    }
  }
}

function customShortcutKey_Shift_0() {
  getVideotime();
}

function toggleVideoPlayPause() {
  var videoPlayer = document.getElementById("videoPlayer");
  if (videoPlayer && videoPlayer.playVideo) {
    if (videoPlayer.getPlayerState() == 2) {
      videoPlayer.playVideo();
    } else {
      videoPlayer.pauseVideo();
    }
  } else if (videoPlayer && videoPlayer.play) {
    if (videoPlayer.paused) { videoPlayer.play(); }
    else { videoPlayer.pause(); }
  }
}

var gotOnload = false;
var gotYuotubeDetails = false;

function pgn4web_onload(e) {
  gotOnload = true;
  new_start_pgn4web();
}

function new_start_pgn4web() {
  if (youtubeVideoid && !pgnFile && (!gotYuotubeDetails || !gotOnload)) { return; }
  start_pgn4web();
}

</script>

</body>

</html>