/** * 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 - - - // "use strict"; function getUrlParameters() { 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 draw(x, y) { cursorStyle = document.getElementById("cursor").style; var slide = document.getElementById("slide"); var obj = $("#slide > object"); var scaledX = parseInt(x, 10) * (parseInt(obj.attr("width"), 10) / 800); var scaledY = parseInt(y, 10) * (parseInt(obj.attr("height"), 10) / 600); //move to the next place var leftValue = parseInt(slide.offsetLeft, 10) + parseInt(scaledX, 10) var topValue = parseInt(slide.offsetTop, 10) + parseInt(scaledY, 10) if (leftValue < 0){ leftValue = 0 } if (topValue < 0){ topValue = 0 } cursorStyle.left = leftValue + "px"; cursorStyle.top = topValue + "px"; } // Shows or hides the cursor object depending on true/false parameter passed. function showCursor(boolVal) { cursorStyle = document.getElementById("cursor").style; if(boolVal === false) { cursorStyle.height = "0px"; cursorStyle.width = "0px"; } else { cursorStyle.height = "10px"; cursorStyle.width = "10px"; } } function setViewBox(val) { if(svgobj.contentDocument) svgfile = svgobj.contentDocument.getElementById("svgfile"); else svgfile = svgobj.getSVGDocument('svgfile').getElementById("svgfile"); svgfile.setAttribute('viewBox', val); } function setCursor(val) { draw(val[0], val[1]); } 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; for (key in vboxValues) { if(vboxValues.hasOwnProperty(key)) { var arry = key.split(","); if(arry[1] == "end") { return vboxValues[key]; } else if ((parseFloat(arry[0]) <= curr_t) && (parseFloat(arry[1]) >= curr_t)) { return vboxValues[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'); } // - - - END OF JAVASCRIPT FUNCTIONS - - - // function runPopcorn() { 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). xmlhttp.open("GET", shapes_svg, false); xmlhttp.send(); var xmlDoc = xmlhttp.responseXML; //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 array = $(shapeelements[0]).find("g").filter(function(){ //get all the lines from the svg file return $(this).attr('class') == 'shape'; }); var images = shapeelements[0].getElementsByTagName("image"); //fill the times array with the times of the svg images. for (var j = 0; j < array.length; j++) { times[j] = array[j].getAttribute("id").substr(4); } var times_length = times.length; //get the length of the times array. 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(); console.log("Text file read " + imgid); } } }; txtFile.send(null); } } // PROCESS PANZOOMS.XML xmlhttp.open("GET", events_xml, false); xmlhttp.send(); xmlDoc = xmlhttp.responseXML; //getting all the event tags 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; } // PROCESS CURSOR.XML xmlhttp.open("GET", cursor_xml, false); xmlhttp.send(); xmlDoc = xmlhttp.responseXML; //getting all the event tags 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; } svgobj.style.left = document.getElementById("slide").offsetLeft + "px"; svgobj.style.top = "8px"; var next_shape; var shape; for (var i = 0, len = times_length; i < len-1; i++) { //iterate through all the shapes and pick out the main ones var time = times[i]; shape = svgobj.contentDocument.getElementById("draw" + time).getAttribute("shape"); next_shape = svgobj.contentDocument.getElementById("draw" + times[i+1]).getAttribute("shape"); if(shape !== next_shape) { main_shapes_times[main_shapes_times.length] = time; } } if(times.length !== 0) { main_shapes_times[main_shapes_times.length] = times[times.length-1]; //put last value into this array always! } 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) { if(!((p.paused() === true) && (p.seeking() === false))) { var t = p.currentTime().toFixed(1); //get the time and round to 1 decimal place if(svgobj.contentDocument) current_shape = svgobj.contentDocument.getElementById("draw" + t); else current_shape = svgobj.getSVGDocument('svgfile').getElementById("draw" + t); if(current_shape !== null) { //if there is actually a new shape to be displayed current_shape = current_shape.getAttribute("shape"); //get actual shape tag for this specific time of playback } //redraw everything (only way to make everything elegant) for (var i = 0, len = times_length; i < len; i++) { var time_s = times[i]; var time_f = parseFloat(time_s); if(svgobj.contentDocument) shape = svgobj.contentDocument.getElementById("draw" + time_s); else shape = svgobj.getSVGDocument('svgfile').getElementById("draw" + time_s); var shape_i = shape.getAttribute("shape"); if (time_f < t) { if(shape_i === current_shape) { //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_times.indexOf(time_s) !== -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 if(time_s === t) { //for the shape with the time specific to the current time shape.style.visibility = "visible"; } 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. var imageXOffset = 0; var imageYOffset = 0; if((current_image !== next_image) && (next_image !== undefined)){ //changing slide image if(svgobj.contentDocument) { svgobj.contentDocument.getElementById(current_image).style.visibility = "hidden"; var ni = svgobj.contentDocument.getElementById(next_image); } else { svgobj.getSVGDocument('svgfile').getElementById(current_image).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); } var num_current = current_image.substr(5); var num_next = next_image.substr(5); if(svgobj.contentDocument) currentcanvas = svgobj.contentDocument.getElementById("canvas" + num_current); else currentcanvas = svgobj.getSVGDocument('svgfile').getElementById("canvas" + num_current); if(currentcanvas !== null) { currentcanvas.setAttribute("display", "none"); } if(svgobj.contentDocument) nextcanvas = svgobj.contentDocument.getElementById("canvas" + num_next); else nextcanvas = svgobj.getSVGDocument('svgfile').getElementById("canvas" + num_next); if((nextcanvas !== undefined) && (nextcanvas != null)) { nextcanvas.setAttribute("display", ""); } current_image = next_image; } if(svgobj.contentDocument) var thisimg = svgobj.contentDocument.getElementById(current_image); else var thisimg = svgobj.getSVGDocument('svgfile').getElementById(current_image); var offsets = thisimg.getBoundingClientRect(); // Offsets divided by 4. By 2 because of the padding and by 2 again because 800x600 is half 1600x1200 imageXOffset = (1600 - parseInt(thisimg.getAttribute("width"), 10))/4; imageYOffset = (1200 - parseInt(thisimg.getAttribute("height"), 10))/4; var vboxVal = getViewboxAtTime(t); if(vboxVal !== undefined) { setViewBox(vboxVal); } var cursorVal = getCursorAtTime(t); var cursor_on = false; if(cursorVal != null) { if(!cursor_on) { document.getElementById("cursor").style.visibility = 'visible'; //make visible cursor_on = true; } setCursor([parseFloat(cursorVal[0]) + imageXOffset - 6, parseFloat(cursorVal[1]) + imageYOffset - 6]); //-6 is for radius of cursor offset } } } }); }; function defineStartTime() { 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 current_canvas = "canvas0"; var current_image = "image0"; var currentcanvas; var shape; var nextcanvas; var next_image; var next_pgid; var curr_pgid; var svgfile; //current time var t; var len; var current_shape; //coordinates for x and y for each second var panAndZoomTimes = []; var viewBoxes = []; var coords = []; var times = []; var clearTimes = []; var main_shapes_times = []; var vboxValues = {}; var cursorValues = {}; var imageAtTime = {}; var slidePlainText = {}; //holds slide plain text for retrieval var cursorStyle; var params = getUrlParameters(); var MEETINGID = params.meetingId; var url = "/presentation/" + MEETINGID; var shapes_svg = url + '/shapes.svg'; var events_xml = url + '/panzooms.xml'; var cursor_xml = url + '/cursor.xml'; var svgobj = document.createElement('object'); svgobj.setAttribute('data', shapes_svg); svgobj.setAttribute('height', '600px'); svgobj.setAttribute('width', '800px'); svgobj.addEventListener('load', runPopcorn, false); /** * we need an urgently refactor here * first the writing.js must be loaded, and then runPopcorn loads, but it loads * only after the svg file gets loaded, and the generation of thumbnails must * came after that because it needs the popcorn element to be created properly */ svgobj.addEventListener('load', function() { generateThumbnails(); var p = Popcorn("#video"); p.on('loadeddata', function() { p.currentTime(defineStartTime()); }); // Sometimes media has already loaded before our loadeddata listener is // attached. If the media is already past the loadeddata stage then we // trigger the event manually ourselves if ($('#video')[0].readyState > 0) { p.emit('loadeddata'); } }, false); document.getElementById('slide').appendChild(svgobj); window.onresize = function(event) { svgobj.style.left = document.getElementById("slide").offsetLeft + "px"; svgobj.style.top = "8px"; };