/** * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ * * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). * * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation; either version 3.0 of the License, or (at your option) any later * version. * * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along * with BigBlueButton; if not, see . * */ // - - - START OF GLOBAL VARIABLES - - - // function getUrlParameters() { console.log("** Getting url params"); var map = {}; window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) { map[key] = value; }); return map; } // - - - END OF GLOBAL VARIABLES - - - // // - - - START OF JAVASCRIPT FUNCTIONS - - - // // Draw the cursor at a specific point function drawCursor(scaledX, scaledY) { var containerObj = $("#slide > object"); // the offsets of the container that has the image inside it var imageOffsetX = containerObj.offset().left; var imageOffsetY = containerObj.offset().top; // position of the cursor relative to the container var cursorXInImage = scaledX * containerObj.width(); var cursorYInImage = scaledY * containerObj.height(); // absolute position of the cursor in the page var cursorLeft = parseInt(imageOffsetX + cursorXInImage, 10); var cursorTop = parseInt(imageOffsetY + cursorYInImage, 10); if (cursorLeft < 0) { cursorLeft = 0; } if (cursorTop < 0) { cursorTop = 0; } var cursorStyle = document.getElementById("cursor").style; cursorStyle.left = cursorLeft + "px"; cursorStyle.top = cursorTop + "px"; } function showCursor(show) { if (show) { document.getElementById("cursor").style.visibility = 'visible'; } else { document.getElementById("cursor").style.visibility = 'hidden'; } }; function setViewBox(time) { var vboxVal = getViewboxAtTime(time); if(vboxVal !== undefined) { setTransform(time); if(svgobj.contentDocument) svgfile = svgobj.contentDocument.getElementById("svgfile"); else svgfile = svgobj.getSVGDocument('svgfile').getElementById("svgfile"); svgfile.setAttribute('viewBox', vboxVal); } } function getImageAtTime(time) { var curr_t = parseFloat(time); var key; for (key in imageAtTime) { if(imageAtTime.hasOwnProperty(key)) { var arry = key.split(","); if ((parseFloat(arry[0]) <= curr_t) && (parseFloat(arry[1]) >= curr_t)) { return imageAtTime[key]; } } } } function getViewboxAtTime(time) { var curr_t = parseFloat(time); var key; var isDeskshare = mustShowDesktopVideo(time); for (key in vboxValues) { if(vboxValues.hasOwnProperty(key)) { var arry = key.split(","); if(arry[1] == "end") { return isDeskshare ? adaptViewBoxToDeskshare(time) : vboxValues[key]; } else if ((parseFloat(arry[0]) <= curr_t) && (parseFloat(arry[1]) >= curr_t)) { return isDeskshare ? adaptViewBoxToDeskshare(time) : vboxValues[key]; } } } } function setSlideAspect(time, imageWidth, imageHeight) { var isDeskshare = mustShowDesktopVideo(time); var aspectAtTime = getAspectAtTime(time); if (aspectAtTime != undefined && aspectAtTime != 0 && !isDeskshare) { currentSlideAspect = aspectAtTime; } else { currentSlideAspect = parseFloat((imageWidth/imageHeight)); } } function getAspectAtTime(time) { var curr_t = parseFloat(time); var key; for (key in slideAspectValues) { if(slideAspectValues.hasOwnProperty(key)) { var arry = key.split(","); if(arry[1] == "end") { return slideAspectValues[key]; } else if ((parseFloat(arry[0]) <= curr_t) && (parseFloat(arry[1]) >= curr_t)) { return slideAspectValues[key]; } } } } function getCursorAtTime(time) { var coords = cursorValues[time]; if(coords) return coords.split(' '); } function removeSlideChangeAttribute() { $('#video').removeAttr('slide-change'); Popcorn('#video').unlisten(Popcorn.play, 'removeSlideChangeAttribute'); } function mustShowDesktopVideo(time) { var canShow = false; if (isThereDeskshareVideo()) { for (var m = 0; m < deskshareTimes.length; m++) { var start_timestamp = deskshareTimes[m][0]; var stop_timestamp = deskshareTimes[m][1]; if(time >= start_timestamp && time <= stop_timestamp) canShow = true; } } return canShow; } function getDeskshareDimension(time) { var start_timestamp = 0.0; var stop_timestamp = 0.0; var width = deskshareWidth; var height = deskshareHeight; if (isThereDeskshareVideo()) { for (var m = 0; m < deskshareTimes.length; m++) { start_timestamp = deskshareTimes[m][0]; stop_timestamp = deskshareTimes[m][1]; if(time >= start_timestamp && time <= stop_timestamp) { width = deskshareTimes[m][2]; height = deskshareTimes[m][3]; break; } } } return { width: width, height: height }; } function isThereDeskshareVideo() { var deskshareVideo = document.getElementById("deskshare-video"); if (deskshareVideo != null) { return true; } else { return false; } } function handlePresentationAreaContent(time) { if(time >= meetingDuration) return; var mustShow = mustShowDesktopVideo(time); if(!sharingDesktop && mustShow) { console.log("Showing deskshare video..."); document.getElementById("deskshare-video").style.visibility = "visible"; $('#slide').addClass('no-background'); sharingDesktop = true; } else if(sharingDesktop && !mustShow) { console.log("Hiding deskshare video..."); document.getElementById("deskshare-video").style.visibility = "hidden"; $('#slide').removeClass('no-background'); sharingDesktop = false; } resizeDeshareVideo(); } // - - - END OF JAVASCRIPT FUNCTIONS - - - // function startLoadingBar() { console.log("==Hide playback content"); $("#playback-content").css('visibility', 'hidden'); Pace.once('done', function() { $("#loading-error").css('height','0'); console.log("==Show playback content"); $("#playback-content").css('visibility', 'visible'); }); Pace.start(); } function runPopcorn() { console.log("** Running popcorn"); getMetadata(); if(svgobj.contentDocument) svgfile = svgobj.contentDocument.getElementById("svgfile"); else svgfile = svgobj.getSVGDocument('svgfile'); //making the object for requesting the read of the XML files. if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari var xmlhttp = new XMLHttpRequest(); } else { // code for IE6, IE5 var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } // PROCESS SHAPES.SVG (in XML format). console.log("** Getting shapes_svg"); xmlhttp.open("GET", shapes_svg, false); xmlhttp.send(); var xmlDoc = xmlhttp.responseXML; console.log("** Processing shapes_svg"); //getting all the event tags var shapeelements = xmlDoc.getElementsByTagName("svg"); //get the array of values for the first shape (getDataPoints(0) is the first shape). var shapesArray = $(shapeelements[0]).find("g").filter(function(){ //get all the lines from the svg file return $(this).attr('class') == 'shape'; }); //create a map from timestamp to id list var timestampToId = {}; for (var j = 0; j < shapesArray.length; j++) { shapeTime = shapesArray[j].getAttribute("timestamp"); shapeId = shapesArray[j].getAttribute("id"); if (timestampToId[shapeTime] == undefined) { timestampToId[shapeTime] = new Array(0); } timestampToId[shapeTime].push(shapeId); } //fill the times array with the times of the svg images. for (var j = 0; j < shapesArray.length; j++) { times[j] = shapesArray[j].getAttribute("timestamp"); } var times_length = times.length; //get the length of the times array. // PROCESS PANZOOMS.XML console.log("** Getting panzooms.xml"); xmlhttp.open("GET", events_xml, false); xmlhttp.send(); xmlDoc = xmlhttp.responseXML; //getting all the event tags console.log("** Processing panzooms.xml"); var panelements = xmlDoc.getElementsByTagName("recording"); var panZoomArray = panelements[0].getElementsByTagName("event"); viewBoxes = xmlDoc.getElementsByTagName("viewBox"); var pzlen = panZoomArray.length; var second_val; //fill the times array with the times of the svg images. for (var k = 0;k < pzlen; k++) { if(panZoomArray[k+1] == undefined) { second_val = "end"; } else second_val = panZoomArray[k+1].getAttribute("timestamp"); vboxValues[[panZoomArray[k].getAttribute("timestamp"), second_val]] = viewBoxes[k].childNodes[0].data; } getPresentationText(); // PROCESS CURSOR.XML console.log("** Getting cursor.xml"); xmlhttp.open("GET", cursor_xml, false); xmlhttp.send(); xmlDoc = xmlhttp.responseXML; //getting all the event tags console.log("** Processing cursor.xml"); var curelements = xmlDoc.getElementsByTagName("recording"); var cursorArray = curelements[0].getElementsByTagName("event"); coords = xmlDoc.getElementsByTagName("cursor"); var clen = cursorArray.length; //fill the times array with the times of the svg images. if(cursorArray.length != 0) cursorValues["0"] = "0 0"; for (var m = 0; m < clen; m++) { cursorValues[cursorArray[m].getAttribute("timestamp")] = coords[m].childNodes[0].data; } // PROCESS DESKSHARE.XML console.log("** Getting deskshare.xml"); xmlhttp.open("GET", deskshare_xml, false); xmlhttp.send(); xmlDoc = xmlhttp.responseXML; if (xmlDoc) { //getting all the event tags console.log("** Processing deskshare.xml"); var deskelements = xmlDoc.getElementsByTagName("recording"); var deskshareArray = deskelements[0].getElementsByTagName("event"); if(deskshareArray != null && deskshareArray.length != 0) { for (var m = 0; m < deskshareArray.length; m++) { var deskTimes = [parseFloat(deskshareArray[m].getAttribute("start_timestamp")), parseFloat(deskshareArray[m].getAttribute("stop_timestamp")), parseFloat(deskshareArray[m].getAttribute("video_width")), parseFloat(deskshareArray[m].getAttribute("video_height"))]; deskshareTimes[m] = deskTimes; } } } svgobj.style.left = document.getElementById("slide").offsetLeft + "px"; svgobj.style.top = "0px"; var next_shape; var shape; for (var j = 0; j < shapesArray.length - 1; j++) { //iterate through all the shapes and pick out the main ones var time = shapesArray[j].getAttribute("timestamp"); shape = shapesArray[j].getAttribute("shape"); next_shape = shapesArray[j+1].getAttribute("shape"); if(shape !== next_shape) { main_shapes_ids.push(shapesArray[j].getAttribute("id")); } } if (shapesArray.length !== 0) { main_shapes_ids.push(shapesArray[shapesArray.length-1].getAttribute("id")); //put last value into this array always! } var get_shapes_in_time = function(t) { // console.log("** Getting shapes in time"); var shapes_in_time = timestampToId[t]; var shapes = []; if (shapes_in_time != undefined) { var shape = null; for (var i = 0; i < shapes_in_time.length; i++) { var id = shapes_in_time[i]; if(svgobj.contentDocument) shape = svgobj.contentDocument.getElementById(id); else shape = svgobj.getSVGDocument('svgfile').getElementById(id); if (shape !== null) { //if there is actually a new shape to be displayed shape = shape.getAttribute("shape"); //get actual shape tag for this specific time of playback shapes.push(shape); } } } return shapes; }; var p = new Popcorn("#video"); //update 60x / second the position of the next value. p.code({ start: 1, // start time end: p.duration(), onFrame: function(options) { var currentTime = p.currentTime(); if ( (!p.paused() || p.seeking()) && (Math.abs(currentTime - lastFrameTime) >= 0.1) ) { lastFrameTime = currentTime; var t = currentTime.toFixed(1); //get the time and round to 1 decimal place current_shapes = get_shapes_in_time(t); //redraw everything (only way to make everything elegant) for (var i = 0; i < shapesArray.length; i++) { var time_s = shapesArray[i].getAttribute("timestamp"); var time_f = parseFloat(time_s); if(svgobj.contentDocument) shape = svgobj.contentDocument.getElementById(shapesArray[i].getAttribute("id")); else shape = svgobj.getSVGDocument('svgfile').getElementById(shapesArray[i].getAttribute("id")); if(shape != null) { var shape_i = shape.getAttribute("shape"); if (time_f < t) { if(current_shapes.indexOf(shape_i) > -1) { //currently drawing the same shape so don't draw the older steps shape.style.visibility = "hidden"; //hide older steps to shape } else if(main_shapes_ids.indexOf(shape.getAttribute("id")) > -1) { //as long as it is a main shape, it can be drawn... no intermediate steps. if(parseFloat(shape.getAttribute("undo")) === -1) { //As long as the undo event hasn't happened yet... shape.style.visibility = "visible"; } else if (parseFloat(shape.getAttribute("undo")) > t) { shape.style.visibility = "visible"; } else { shape.style.visibility = "hidden"; } } else { shape.style.visibility = "hidden"; } } else if(time_s === t) { //for the shapes with the time specific to the current time // only makes visible the last drawing of a given shape var idx = current_shapes.indexOf(shape_i); if (idx > -1) { current_shapes.splice(idx, 1); idx = current_shapes.indexOf(shape_i); if (idx > -1) { shape.style.visibility = "hidden"; } else { shape.style.visibility = "visible"; } } else { // this is an inconsistent state, since current_shapes should have at least one drawing of this shape shape.style.visibility = "hidden"; } } else { //for shapes that shouldn't be drawn yet (larger time than current time), don't draw them. shape.style.visibility = "hidden"; } } } var next_image = getImageAtTime(t); //fetch the name of the image at this time. if(current_image && (current_image !== next_image) && (next_image !== undefined)){ //changing slide image if(svgobj.contentDocument) { var img = svgobj.contentDocument.getElementById(current_image); if (img) { img.style.visibility = "hidden"; } var ni = svgobj.contentDocument.getElementById(next_image); } else { var img = svgobj.getSVGDocument('svgfile').getElementById(current_image); if (img) { img.style.visibility = "hidden"; } var ni = svgobj.getSVGDocument('svgfile').getElementById(next_image); } document.getElementById("slideText").innerHTML = ""; //destroy old plain text ni.style.visibility = "visible"; document.getElementById("slideText").innerHTML = slidePlainText[next_image] + next_image; //set new plain text if ($("#accEnabled").is(':checked')) { //pause the playback on slide change p.pause(); $('#video').attr('slide-change', 'slide-change'); p.listen(Popcorn.play, removeSlideChangeAttribute); } current_canvas = getCanvasFromImage(current_image); if(current_canvas !== null) { current_canvas.setAttribute("display", "none"); } next_canvas = getCanvasFromImage(next_image); if((next_canvas !== undefined) && (next_canvas != null)) { next_canvas.setAttribute("display", ""); } previous_image = current_image; current_image = next_image; } if(svgobj.contentDocument) var thisimg = svgobj.contentDocument.getElementById(current_image); else var thisimg = svgobj.getSVGDocument('svgfile').getElementById(current_image); if (thisimg) { var imageWidth = parseFloat(thisimg.getAttribute("width")); var imageHeight = parseFloat(thisimg.getAttribute("height")); setViewBox(t); setSlideAspect(t,imageWidth,imageHeight); if (getCursorAtTime(t) != null && getCursorAtTime(t) != undefined && !$('#slide').hasClass('no-background')) { currentCursorVal = getCursorAtTime(t); cursorShownAt = new Date().getTime(); showCursor(true); // width and height are divided by 2 because that's the value used as a reference // when positions in cursor.xml is calculated var cursorX = parseFloat(currentCursorVal[0]) / (imageWidth/2); var cursorY = parseFloat(currentCursorVal[1]) / (imageHeight/2); drawCursor(cursorX, cursorY); // hide the cursor after 3s of inactivity } else if (cursorShownAt < new Date().getTime() - 3000) { showCursor(false); } // store the current slide and adjust the size of the slides currentImage = thisimg; resizeSlides(); } handlePresentationAreaContent(t); } } }); }; // Deskshare's whiteboard variables var deskshareWidth = 1280.0; var deskshareHeight = 720.0; var widthScale = 1; var heightScale = 1; var widthTranslate = 0; var heightTranslate = 0; function clearTransform() { widthScale = 1; heightScale = 1; widthTranslate = 0; heightTranslate = 0; } function setDeskshareScale(originalVideoWidth, originalVideoHeight) { widthScale = originalVideoWidth / deskshareWidth; heightScale = originalVideoHeight / deskshareHeight; } function setDeskshareTranslate(originalVideoWidth, originalVideoHeight) { widthTranslate = (deskshareWidth - originalVideoWidth) / 2; heightTranslate = (deskshareHeight - originalVideoHeight) / 2; } // Deskshare viewBox has the information to transform the canvas to place it above the video function adaptViewBoxToDeskshare(time) { var dimension = getDeskshareDimension(time); setDeskshareScale(dimension.width, dimension.height); setDeskshareTranslate(dimension.width, dimension.height); var viewBox = "0.0 0.0 " + deskshareWidth + " " + deskshareHeight; return viewBox; } function getCanvasFromImage(image) { var canvasId = "canvas" + image.substr(5); var canvas = svgobj.contentDocument ? svgobj.contentDocument.getElementById(canvasId) : svgobj.getSVGDocument('svgfile').getElementById(canvasId); return canvas; } function getDeskshareImage() { var images = svgobj.contentDocument ? svgobj.contentDocument.getElementsByTagName("image") : svgobj.getSVGDocument('svgfile').getElementsByTagName("image"); for(var i = 0; i < images.length; i++) { var element = images[i]; var id = element.getAttribute("id"); var href = element.getAttribute("xlink:href"); if (href != null && href.indexOf("deskshare") !=-1) { return id; } } return "image0"; } // Transform canvas to fit the different deskshare video sizes function setTransform(time) { if (deskshare_image == null) { deskshare_image = getDeskshareImage(); } if (mustShowDesktopVideo(time)) { var canvas = getCanvasFromImage(deskshare_image); if (canvas !== null) { var scale = "scale(" + widthScale.toString() + ", " + heightScale.toString() + ")"; var translate = "translate(" + widthTranslate.toString() + ", " + heightTranslate.toString() + ")"; var transform = translate + " " + scale; canvas.setAttribute('transform', transform); } } else { clearTransform(); } } function defineStartTime() { console.log("** Defining start time"); if (params.t === undefined) return 1; var extractNumber = /\d+/g; var extractUnit = /\D+/g; var temp_start_time = 0; while (true) { var param1 = extractUnit.exec(params.t); var param2 = extractNumber.exec(params.t); if (param1 == null || param2 == null) break; var unit = String(param1).toLowerCase(); var value = parseInt(String(param2)); if (unit == "h") value *= 3600; else if (unit == "m") value *= 60; temp_start_time += value; } console.log("Start time: " + temp_start_time); return temp_start_time; } var lastFrameTime = 0.0; var shape; var current_shapes = []; var deskshare_image = null; var current_image = "image0"; var previous_image = null; var current_canvas; var next_canvas; var next_image; var next_pgid; var curr_pgid; var svgfile; //current time var t; var len; var meetingDuration; //coordinates for x and y for each second var panAndZoomTimes = []; var viewBoxes = []; var coords = []; var times = []; // timestamp and id for drawings var shapeTime; var shapeId; var clearTimes = []; var main_shapes_ids = []; var vboxValues = {}; var slideAspectValues = {}; var currentSlideAspect = 0; var cursorValues = {}; var currentCursorVal; var imageAtTime = {}; var slidePlainText = {}; //holds slide plain text for retrieval var cursorStyle; var cursorShownAt = 0; var deskshareTimes = []; var sharingDesktop = false; var params = getUrlParameters(); var MEETINGID = params.meetingId; // var HOST = window.location.host; // var url = "http://" + HOST + "/presentation/" + MEETINGID; var url = "/presentation/" + MEETINGID; var shapes_svg = url + '/shapes.svg'; var events_xml = url + '/panzooms.xml'; var cursor_xml = url + '/cursor.xml'; var metadata_xml = url + '/metadata.xml'; var deskshare_xml = url + '/deskshare.xml'; var presentation_text_json = url + '/presentation_text.json'; var firstLoad = true; var svgReady = false; var videoReady = false; var audioReady = false; var deskshareReady = false; var svgobj = document.createElement('object'); svgobj.setAttribute('data', shapes_svg); svgobj.setAttribute('height', '100%'); svgobj.setAttribute('width', '100%'); // It's important to verify if all medias are ready before running Popcorn document.addEventListener('media-ready', function(event) { switch(event.detail) { case 'video': videoReady = true; break; case 'audio': audioReady = true; break; case 'deskshare': deskshareReady = true; break; case 'svg': svgReady = true; break; default: console.log('unhandled media-ready event: ' + event.detail); } if ((audioReady || videoReady) && deskshareReady && svgReady) { runPopcorn(); if (firstLoad) initPopcorn(); } }, false); function initPopcorn() { firstLoad = false; generateThumbnails(); var startTime = defineStartTime(); console.log("** startTime = " + startTime); Popcorn("#video").currentTime(startTime); if(isThereDeskshareVideo()) Popcorn("#deskshare-video").currentTime(startTime); //Popcorn documentation suggests this way to get the duration, since this information does not come with 'loadedmetadata' event. Popcorn("#video").cue(2, function() { meetingDuration = parseFloat(Popcorn("#video").duration().toFixed(1)); console.log("** Meeting duration (seconds): " + meetingDuration); }); } svgobj.addEventListener('load', function() { console.log("got svgobj 'load' event"); document.dispatchEvent(new CustomEvent('media-ready', {'detail': 'svg'})); }, false); svgobj.addEventListener('error', function() { console.log("got svgobj 'error' event"); onSVGLoadingError(); }, false); function onSVGLoadingError() { Pace.off('done'); Pace.stop(); $("#loading-error").css('visibility', 'visible'); } // Fetches the metadata associated with the recording and uses it to configure // the playback page var getMetadata = function() { var xmlhttp; if (window.XMLHttpRequest) {// code for IE7, Firefox, Chrome, Opera, Safari xmlhttp = new XMLHttpRequest(); } else {// code for IE6, IE5 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } xmlhttp.open("GET", metadata_xml, false); xmlhttp.send(null); if (xmlhttp.responseXML) var xmlDoc = xmlhttp.responseXML; else { var parser = new DOMParser(); var xmlDoc = parser.parseFromString(xmlhttp.responseText, "application/xml"); } var metadata = xmlDoc.getElementsByTagName("meta"); if (metadata.length > 0) { metadata = metadata[0]; var meetingName = metadata.getElementsByTagName("meetingName"); if (meetingName.length > 0) { $("#recording-title").text(meetingName[0].textContent); $("#recording-title").attr("title", meetingName[0].textContent); } } }; function setPresentationTextFromJSON(images, presentationText) { for (var m = 0; m < images.length; m++) { len = images[m].getAttribute("in").split(" ").length; for (var n = 0; n < len; n++) { imageAtTime[[images[m].getAttribute("in").split(" ")[n], images[m].getAttribute("out").split(" ")[n]]] = images[m].getAttribute("id"); } // The logo at the start has no text attribute if (images[m].getAttribute("text")) { var imgId = images[m].getAttribute("id"); // Have to save the value because images array might go out of scope var imgTxt = images[m].getAttribute("text").split("/"); // Text format: presentation/PRESENTATION_ID/textfiles/SLIDE_ID.txt var presentationId = imgTxt[1]; var slideId = imgTxt[3].split(".")[0]; if (presentationText[presentationId] && presentationText[presentationId][slideId]) { slidePlainText[imgId] = $('
').text(presentationText[presentationId][slideId]).html(); } else { slidePlainText[imgId] = $('
') } } } } function setPresentationTextFromTxt(images) { for (var m = 0; m < images.length; m++) { len = images[m].getAttribute("in").split(" ").length; for (var n = 0; n < len; n++) { imageAtTime[[images[m].getAttribute("in").split(" ")[n], images[m].getAttribute("out").split(" ")[n]]] = images[m].getAttribute("id"); } // The logo at the start has no text attribute if (images[m].getAttribute("text")) { var txtFile = false; if (window.XMLHttpRequest) { // Code for IE7+, Firefox, Chrome, Opera, Safari txtFile = new XMLHttpRequest(); } else { // Code for IE6, IE5 txtFile = new ActiveXObject("Microsoft.XMLHTTP"); } var imgId = images[m].getAttribute("id"); // Have to save the value because images array might go out of scope txtFile.open("GET", url + "/" + images[m].getAttribute("text"), false); txtFile.onreadystatechange = function() { if (txtFile.readyState === 4) { if (txtFile.status === 200) { slidePlainText[imgId] = $('
').text(txtFile.responseText).html(); } } }; txtFile.send(null); } } } function processPresentationText(response) { // Making the object for requesting the read of the XML files. if (window.XMLHttpRequest) { // Code for IE7+, Firefox, Chrome, Opera, Safari var xmlhttp = new XMLHttpRequest(); } else { // Code for IE6, IE5 var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } xmlhttp.open("GET", shapes_svg, false); xmlhttp.send(); var xmlDoc = xmlhttp.responseXML; // Getting all the event tags var shapeelements = xmlDoc.getElementsByTagName("svg"); // Newer recordings have slide images identified by class="slide" // because they might include images in shapes var images = shapeelements[0].getElementsByClassName("slide"); // To handle old recordings, fetch a list of all images instead if (images.length == 0) { images = shapeelements[0].getElementsByTagName("image"); } if (response !== undefined) { setPresentationTextFromJSON(images, response); } else { setPresentationTextFromTxt(images); } //at this point, we're sure that the array 'imageAtTime' is ready. Now, we need to set the aspects times to resize the slide div during the playback. processSlideAspectTimes(); } function processSlideAspectTimes() { var key; var lastAspectValue = 0; for (key in vboxValues) { if (vboxValues.hasOwnProperty(key)) { var start_timestamp = key.split(",")[0]; var stop_timestamp = key.split(",")[1]; var vboxWidth = parseFloat(vboxValues[key].split(" ")[2]); var vboxHeight = parseFloat(vboxValues[key].split(" ")[3]); var aspectValue = processAspectValue(vboxWidth,vboxHeight,start_timestamp,lastAspectValue); slideAspectValues[[start_timestamp, stop_timestamp]] = aspectValue; lastAspectValue = aspectValue; } } } function processAspectValue(vboxWidth, vboxHeight, time, lastAspectValue) { var imageId; if (time == "0.0") { //a little hack 'cause function getImageAtTime with time = 0.0 returns the background image... //we need the first slide instead imageId = "image1"; } else { imageId = getImageAtTime(time); } if (imageId !== undefined) { var image; if (svgobj.contentDocument) { image = svgobj.contentDocument.getElementById(imageId); } else { image = svgobj.getSVGDocument('svgfile').getElementById(imageId); } if (image) { if(mustShowDesktopVideo(parseFloat(time))) { return lastAspectValue; } var imageWidth = parseFloat(image.getAttribute("width")); var imageHeight = parseFloat(image.getAttribute("height")); //fit-to-width: returning vbox aspect if(vboxWidth == imageWidth && vboxHeight < imageHeight) { return parseFloat(vboxWidth/vboxHeight); } //fit-to-page: returning image aspect else if(vboxWidth == imageWidth && vboxHeight == imageHeight) { return parseFloat(imageWidth/imageHeight); } //if it's not fit-to-width neither fit-to-page we return the previous aspect else { return lastAspectValue; } } else { console.log("processAspectValue: there is no image for the id = " + imageId); return lastAspectValue; } } else { console.log("processAspectValue: imageId undefined"); return lastAspectValue; } } function getPresentationText() { console.log("** Getting text files"); loadJSON(processPresentationText, presentation_text_json); } function loadJSON(callback, url) { var xobj; if (window.XMLHttpRequest) { // Code for IE7+, Firefox, Chrome, Opera, Safari xobj = new XMLHttpRequest(); } else { // Code for IE6, IE5 xobj = new ActiveXObject("Microsoft.XMLHTTP"); } xobj.overrideMimeType("application/json"); xobj.open('GET', url, true); xobj.onreadystatechange = function () { if (xobj.readyState == 4) { // Required use of an anonymous callback as .open will NOT return a value but simply returns undefined in asynchronous mode if (xobj.status == "200") { callback(JSON.parse(xobj.responseText)); } else { console.log("Could not get JSON file"); callback(undefined); } } }; xobj.send(null); } document.getElementById('slide').appendChild(svgobj); var currentImage; // A small hack to hide the cursor when resizing the window, so it's not // misplaced while the window is being resized window.onresize = function(event) { showCursor(false); resizeSlides(); resizeDeshareVideo(); }; // Resize the container that has the slides (and whiteboard) to be the maximum // size possible but still maintaining the aspect ratio of the slides. // // This is done here only because of pan and zoom. Pan/zoom is done by modifiyng // the svg's viewBox, and that requires the container that has the svg to be the // exact size we want to display the slides so that parts of the svg that are outside // of its parent's area are hidden. If we let the svg occupy all presentation area // (letting the svg do the image resizing), the slides will move and zoom around the // entire area when pan/zoom is activated, usually displaying more of the slide // than we want to (i.e. more than was displayed in the conference). var resizeSlides = function() { if (currentImage) { var $slide = $("#slide"); var maxWidth = currentSlideAspect * $slide.parent().outerHeight(); $slide.css("max-width", maxWidth); var maxHeight = $slide.parent().width() / currentSlideAspect; $slide.css("max-height", maxHeight); } }; var resizeDeshareVideo = function() { if (!isThereDeskshareVideo()) return; var deskshareVideo = document.getElementById("deskshare-video"); var $deskhareVideo = $("#deskshare-video"); var videoWidth = parseInt(deskshareVideo.videoWidth, 10); var videoHeight = parseInt(deskshareVideo.videoHeight, 10); var aspectRatio = videoWidth/videoHeight; var max = aspectRatio * $deskhareVideo.parent().outerHeight(); $deskhareVideo.css("max-width", max); var height = $deskhareVideo.parent().width() / aspectRatio; $deskhareVideo.css("max-height", height); };