You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000
  1. /**
  2. * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
  3. *
  4. * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
  5. *
  6. * This program is free software; you can redistribute it and/or modify it under the
  7. * terms of the GNU Lesser General Public License as published by the Free Software
  8. * Foundation; either version 3.0 of the License, or (at your option) any later
  9. * version.
  10. *
  11. * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
  12. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  13. * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Lesser General Public License along
  16. * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. // - - - START OF GLOBAL VARIABLES - - - //
  20. function getUrlParameters() {
  21. console.log("** Getting url params");
  22. var map = {};
  23. window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) { map[key] = value; });
  24. return map;
  25. }
  26. // - - - END OF GLOBAL VARIABLES - - - //
  27. // - - - START OF JAVASCRIPT FUNCTIONS - - - //
  28. // Draw the cursor at a specific point
  29. function drawCursor(scaledX, scaledY) {
  30. var containerObj = $("#slide > object");
  31. // the offsets of the container that has the image inside it
  32. var imageOffsetX = containerObj.offset().left;
  33. var imageOffsetY = containerObj.offset().top;
  34. // position of the cursor relative to the container
  35. var cursorXInImage = scaledX * containerObj.width();
  36. var cursorYInImage = scaledY * containerObj.height();
  37. // absolute position of the cursor in the page
  38. var cursorLeft = parseInt(imageOffsetX + cursorXInImage, 10);
  39. var cursorTop = parseInt(imageOffsetY + cursorYInImage, 10);
  40. if (cursorLeft < 0) {
  41. cursorLeft = 0;
  42. }
  43. if (cursorTop < 0) {
  44. cursorTop = 0;
  45. }
  46. var cursorStyle = document.getElementById("cursor").style;
  47. cursorStyle.left = cursorLeft + "px";
  48. cursorStyle.top = cursorTop + "px";
  49. }
  50. function showCursor(show) {
  51. if (show) {
  52. document.getElementById("cursor").style.visibility = 'visible';
  53. } else {
  54. document.getElementById("cursor").style.visibility = 'hidden';
  55. }
  56. };
  57. function setViewBox(time) {
  58. var vboxVal = getViewboxAtTime(time);
  59. if(vboxVal !== undefined) {
  60. setTransform(time);
  61. if(svgobj.contentDocument) svgfile = svgobj.contentDocument.getElementById("svgfile");
  62. else svgfile = svgobj.getSVGDocument('svgfile').getElementById("svgfile");
  63. svgfile.setAttribute('viewBox', vboxVal);
  64. }
  65. }
  66. function getImageAtTime(time) {
  67. var curr_t = parseFloat(time);
  68. var key;
  69. for (key in imageAtTime) {
  70. if(imageAtTime.hasOwnProperty(key)) {
  71. var arry = key.split(",");
  72. if ((parseFloat(arry[0]) <= curr_t) && (parseFloat(arry[1]) >= curr_t)) {
  73. return imageAtTime[key];
  74. }
  75. }
  76. }
  77. }
  78. function getViewboxAtTime(time) {
  79. var curr_t = parseFloat(time);
  80. var key;
  81. var isDeskshare = mustShowDesktopVideo(time);
  82. for (key in vboxValues) {
  83. if(vboxValues.hasOwnProperty(key)) {
  84. var arry = key.split(",");
  85. if(arry[1] == "end") {
  86. return isDeskshare ? adaptViewBoxToDeskshare(time) : vboxValues[key];
  87. }
  88. else if ((parseFloat(arry[0]) <= curr_t) && (parseFloat(arry[1]) >= curr_t)) {
  89. return isDeskshare ? adaptViewBoxToDeskshare(time) : vboxValues[key];
  90. }
  91. }
  92. }
  93. }
  94. function setSlideAspect(time, imageWidth, imageHeight) {
  95. var isDeskshare = mustShowDesktopVideo(time);
  96. var aspectAtTime = getAspectAtTime(time);
  97. if (aspectAtTime != undefined && aspectAtTime != 0 && !isDeskshare) {
  98. currentSlideAspect = aspectAtTime;
  99. } else {
  100. currentSlideAspect = parseFloat((imageWidth/imageHeight));
  101. }
  102. }
  103. function getAspectAtTime(time) {
  104. var curr_t = parseFloat(time);
  105. var key;
  106. for (key in slideAspectValues) {
  107. if(slideAspectValues.hasOwnProperty(key)) {
  108. var arry = key.split(",");
  109. if(arry[1] == "end") {
  110. return slideAspectValues[key];
  111. }
  112. else if ((parseFloat(arry[0]) <= curr_t) && (parseFloat(arry[1]) >= curr_t)) {
  113. return slideAspectValues[key];
  114. }
  115. }
  116. }
  117. }
  118. function getCursorAtTime(time) {
  119. var coords = cursorValues[time];
  120. if(coords) return coords.split(' ');
  121. }
  122. function removeSlideChangeAttribute() {
  123. $('#video').removeAttr('slide-change');
  124. Popcorn('#video').unlisten(Popcorn.play, 'removeSlideChangeAttribute');
  125. }
  126. function mustShowDesktopVideo(time) {
  127. var canShow = false;
  128. if (isThereDeskshareVideo()) {
  129. for (var m = 0; m < deskshareTimes.length; m++) {
  130. var start_timestamp = deskshareTimes[m][0];
  131. var stop_timestamp = deskshareTimes[m][1];
  132. if(time >= start_timestamp && time <= stop_timestamp)
  133. canShow = true;
  134. }
  135. }
  136. return canShow;
  137. }
  138. function getDeskshareDimension(time) {
  139. var start_timestamp = 0.0;
  140. var stop_timestamp = 0.0;
  141. var width = deskshareWidth;
  142. var height = deskshareHeight;
  143. if (isThereDeskshareVideo()) {
  144. for (var m = 0; m < deskshareTimes.length; m++) {
  145. start_timestamp = deskshareTimes[m][0];
  146. stop_timestamp = deskshareTimes[m][1];
  147. if(time >= start_timestamp && time <= stop_timestamp) {
  148. width = deskshareTimes[m][2];
  149. height = deskshareTimes[m][3];
  150. break;
  151. }
  152. }
  153. }
  154. return {
  155. width: width,
  156. height: height
  157. };
  158. }
  159. function isThereDeskshareVideo() {
  160. var deskshareVideo = document.getElementById("deskshare-video");
  161. if (deskshareVideo != null) {
  162. return true;
  163. } else {
  164. return false;
  165. }
  166. }
  167. function handlePresentationAreaContent(time) {
  168. if(time >= meetingDuration)
  169. return;
  170. var mustShow = mustShowDesktopVideo(time);
  171. if(!sharingDesktop && mustShow) {
  172. console.log("Showing deskshare video...");
  173. document.getElementById("deskshare-video").style.visibility = "visible";
  174. $('#slide').addClass('no-background');
  175. sharingDesktop = true;
  176. } else if(sharingDesktop && !mustShow) {
  177. console.log("Hiding deskshare video...");
  178. document.getElementById("deskshare-video").style.visibility = "hidden";
  179. $('#slide').removeClass('no-background');
  180. sharingDesktop = false;
  181. }
  182. resizeDeshareVideo();
  183. }
  184. // - - - END OF JAVASCRIPT FUNCTIONS - - - //
  185. function startLoadingBar() {
  186. console.log("==Hide playback content");
  187. $("#playback-content").css('visibility', 'hidden');
  188. Pace.once('done', function() {
  189. $("#loading-error").css('height','0');
  190. console.log("==Show playback content");
  191. $("#playback-content").css('visibility', 'visible');
  192. });
  193. Pace.start();
  194. }
  195. function runPopcorn() {
  196. console.log("** Running popcorn");
  197. getMetadata();
  198. if(svgobj.contentDocument) svgfile = svgobj.contentDocument.getElementById("svgfile");
  199. else svgfile = svgobj.getSVGDocument('svgfile');
  200. //making the object for requesting the read of the XML files.
  201. if (window.XMLHttpRequest) {
  202. // code for IE7+, Firefox, Chrome, Opera, Safari
  203. var xmlhttp = new XMLHttpRequest();
  204. } else {
  205. // code for IE6, IE5
  206. var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
  207. }
  208. // PROCESS SHAPES.SVG (in XML format).
  209. console.log("** Getting shapes_svg");
  210. xmlhttp.open("GET", shapes_svg, false);
  211. xmlhttp.send();
  212. var xmlDoc = xmlhttp.responseXML;
  213. console.log("** Processing shapes_svg");
  214. //getting all the event tags
  215. var shapeelements = xmlDoc.getElementsByTagName("svg");
  216. //get the array of values for the first shape (getDataPoints(0) is the first shape).
  217. var shapesArray = $(shapeelements[0]).find("g").filter(function(){ //get all the lines from the svg file
  218. return $(this).attr('class') == 'shape';
  219. });
  220. //create a map from timestamp to id list
  221. var timestampToId = {};
  222. for (var j = 0; j < shapesArray.length; j++) {
  223. shapeTime = shapesArray[j].getAttribute("timestamp");
  224. shapeId = shapesArray[j].getAttribute("id");
  225. if (timestampToId[shapeTime] == undefined) {
  226. timestampToId[shapeTime] = new Array(0);
  227. }
  228. timestampToId[shapeTime].push(shapeId);
  229. }
  230. //fill the times array with the times of the svg images.
  231. for (var j = 0; j < shapesArray.length; j++) {
  232. times[j] = shapesArray[j].getAttribute("timestamp");
  233. }
  234. var times_length = times.length; //get the length of the times array.
  235. // PROCESS PANZOOMS.XML
  236. console.log("** Getting panzooms.xml");
  237. xmlhttp.open("GET", events_xml, false);
  238. xmlhttp.send();
  239. xmlDoc = xmlhttp.responseXML;
  240. //getting all the event tags
  241. console.log("** Processing panzooms.xml");
  242. var panelements = xmlDoc.getElementsByTagName("recording");
  243. var panZoomArray = panelements[0].getElementsByTagName("event");
  244. viewBoxes = xmlDoc.getElementsByTagName("viewBox");
  245. var pzlen = panZoomArray.length;
  246. var second_val;
  247. //fill the times array with the times of the svg images.
  248. for (var k = 0;k < pzlen; k++) {
  249. if(panZoomArray[k+1] == undefined) {
  250. second_val = "end";
  251. }
  252. else second_val = panZoomArray[k+1].getAttribute("timestamp");
  253. vboxValues[[panZoomArray[k].getAttribute("timestamp"), second_val]] = viewBoxes[k].childNodes[0].data;
  254. }
  255. getPresentationText();
  256. // PROCESS CURSOR.XML
  257. console.log("** Getting cursor.xml");
  258. xmlhttp.open("GET", cursor_xml, false);
  259. xmlhttp.send();
  260. xmlDoc = xmlhttp.responseXML;
  261. //getting all the event tags
  262. console.log("** Processing cursor.xml");
  263. var curelements = xmlDoc.getElementsByTagName("recording");
  264. var cursorArray = curelements[0].getElementsByTagName("event");
  265. coords = xmlDoc.getElementsByTagName("cursor");
  266. var clen = cursorArray.length;
  267. //fill the times array with the times of the svg images.
  268. if(cursorArray.length != 0) cursorValues["0"] = "0 0";
  269. for (var m = 0; m < clen; m++) {
  270. cursorValues[cursorArray[m].getAttribute("timestamp")] = coords[m].childNodes[0].data;
  271. }
  272. // PROCESS DESKSHARE.XML
  273. console.log("** Getting deskshare.xml");
  274. xmlhttp.open("GET", deskshare_xml, false);
  275. xmlhttp.send();
  276. xmlDoc = xmlhttp.responseXML;
  277. if (xmlDoc) {
  278. //getting all the event tags
  279. console.log("** Processing deskshare.xml");
  280. var deskelements = xmlDoc.getElementsByTagName("recording");
  281. var deskshareArray = deskelements[0].getElementsByTagName("event");
  282. if(deskshareArray != null && deskshareArray.length != 0) {
  283. for (var m = 0; m < deskshareArray.length; m++) {
  284. var deskTimes = [parseFloat(deskshareArray[m].getAttribute("start_timestamp")),
  285. parseFloat(deskshareArray[m].getAttribute("stop_timestamp")),
  286. parseFloat(deskshareArray[m].getAttribute("video_width")),
  287. parseFloat(deskshareArray[m].getAttribute("video_height"))];
  288. deskshareTimes[m] = deskTimes;
  289. }
  290. }
  291. }
  292. svgobj.style.left = document.getElementById("slide").offsetLeft + "px";
  293. svgobj.style.top = "0px";
  294. var next_shape;
  295. var shape;
  296. for (var j = 0; j < shapesArray.length - 1; j++) { //iterate through all the shapes and pick out the main ones
  297. var time = shapesArray[j].getAttribute("timestamp");
  298. shape = shapesArray[j].getAttribute("shape");
  299. next_shape = shapesArray[j+1].getAttribute("shape");
  300. if(shape !== next_shape) {
  301. main_shapes_ids.push(shapesArray[j].getAttribute("id"));
  302. }
  303. }
  304. if (shapesArray.length !== 0) {
  305. main_shapes_ids.push(shapesArray[shapesArray.length-1].getAttribute("id")); //put last value into this array always!
  306. }
  307. var get_shapes_in_time = function(t) {
  308. // console.log("** Getting shapes in time");
  309. var shapes_in_time = timestampToId[t];
  310. var shapes = [];
  311. if (shapes_in_time != undefined) {
  312. var shape = null;
  313. for (var i = 0; i < shapes_in_time.length; i++) {
  314. var id = shapes_in_time[i];
  315. if(svgobj.contentDocument) shape = svgobj.contentDocument.getElementById(id);
  316. else shape = svgobj.getSVGDocument('svgfile').getElementById(id);
  317. if (shape !== null) { //if there is actually a new shape to be displayed
  318. shape = shape.getAttribute("shape"); //get actual shape tag for this specific time of playback
  319. shapes.push(shape);
  320. }
  321. }
  322. }
  323. return shapes;
  324. };
  325. var p = new Popcorn("#video");
  326. //update 60x / second the position of the next value.
  327. p.code({
  328. start: 1, // start time
  329. end: p.duration(),
  330. onFrame: function(options) {
  331. var currentTime = p.currentTime();
  332. if ( (!p.paused() || p.seeking()) && (Math.abs(currentTime - lastFrameTime) >= 0.1) ) {
  333. lastFrameTime = currentTime;
  334. var t = currentTime.toFixed(1); //get the time and round to 1 decimal place
  335. current_shapes = get_shapes_in_time(t);
  336. //redraw everything (only way to make everything elegant)
  337. for (var i = 0; i < shapesArray.length; i++) {
  338. var time_s = shapesArray[i].getAttribute("timestamp");
  339. var time_f = parseFloat(time_s);
  340. if(svgobj.contentDocument) shape = svgobj.contentDocument.getElementById(shapesArray[i].getAttribute("id"));
  341. else shape = svgobj.getSVGDocument('svgfile').getElementById(shapesArray[i].getAttribute("id"));
  342. if(shape != null) {
  343. var shape_i = shape.getAttribute("shape");
  344. if (time_f < t) {
  345. if(current_shapes.indexOf(shape_i) > -1) { //currently drawing the same shape so don't draw the older steps
  346. shape.style.visibility = "hidden"; //hide older steps to shape
  347. } 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.
  348. if(parseFloat(shape.getAttribute("undo")) === -1) { //As long as the undo event hasn't happened yet...
  349. shape.style.visibility = "visible";
  350. } else if (parseFloat(shape.getAttribute("undo")) > t) {
  351. shape.style.visibility = "visible";
  352. } else {
  353. shape.style.visibility = "hidden";
  354. }
  355. } else {
  356. shape.style.visibility = "hidden";
  357. }
  358. } else if(time_s === t) { //for the shapes with the time specific to the current time
  359. // only makes visible the last drawing of a given shape
  360. var idx = current_shapes.indexOf(shape_i);
  361. if (idx > -1) {
  362. current_shapes.splice(idx, 1);
  363. idx = current_shapes.indexOf(shape_i);
  364. if (idx > -1) {
  365. shape.style.visibility = "hidden";
  366. } else {
  367. shape.style.visibility = "visible";
  368. }
  369. } else {
  370. // this is an inconsistent state, since current_shapes should have at least one drawing of this shape
  371. shape.style.visibility = "hidden";
  372. }
  373. } else { //for shapes that shouldn't be drawn yet (larger time than current time), don't draw them.
  374. shape.style.visibility = "hidden";
  375. }
  376. }
  377. }
  378. var next_image = getImageAtTime(t); //fetch the name of the image at this time.
  379. if(current_image && (current_image !== next_image) && (next_image !== undefined)){ //changing slide image
  380. if(svgobj.contentDocument) {
  381. var img = svgobj.contentDocument.getElementById(current_image);
  382. if (img) {
  383. img.style.visibility = "hidden";
  384. }
  385. var ni = svgobj.contentDocument.getElementById(next_image);
  386. }
  387. else {
  388. var img = svgobj.getSVGDocument('svgfile').getElementById(current_image);
  389. if (img) {
  390. img.style.visibility = "hidden";
  391. }
  392. var ni = svgobj.getSVGDocument('svgfile').getElementById(next_image);
  393. }
  394. document.getElementById("slideText").innerHTML = ""; //destroy old plain text
  395. ni.style.visibility = "visible";
  396. document.getElementById("slideText").innerHTML = slidePlainText[next_image] + next_image; //set new plain text
  397. if ($("#accEnabled").is(':checked')) {
  398. //pause the playback on slide change
  399. p.pause();
  400. $('#video').attr('slide-change', 'slide-change');
  401. p.listen(Popcorn.play, removeSlideChangeAttribute);
  402. }
  403. current_canvas = getCanvasFromImage(current_image);
  404. if(current_canvas !== null) {
  405. current_canvas.setAttribute("display", "none");
  406. }
  407. next_canvas = getCanvasFromImage(next_image);
  408. if((next_canvas !== undefined) && (next_canvas != null)) {
  409. next_canvas.setAttribute("display", "");
  410. }
  411. previous_image = current_image;
  412. current_image = next_image;
  413. }
  414. if(svgobj.contentDocument) var thisimg = svgobj.contentDocument.getElementById(current_image);
  415. else var thisimg = svgobj.getSVGDocument('svgfile').getElementById(current_image);
  416. if (thisimg) {
  417. var imageWidth = parseFloat(thisimg.getAttribute("width"));
  418. var imageHeight = parseFloat(thisimg.getAttribute("height"));
  419. setViewBox(t);
  420. setSlideAspect(t,imageWidth,imageHeight);
  421. if (getCursorAtTime(t) != null && getCursorAtTime(t) != undefined && !$('#slide').hasClass('no-background')) {
  422. currentCursorVal = getCursorAtTime(t);
  423. cursorShownAt = new Date().getTime();
  424. showCursor(true);
  425. // width and height are divided by 2 because that's the value used as a reference
  426. // when positions in cursor.xml is calculated
  427. var cursorX = parseFloat(currentCursorVal[0]) / (imageWidth/2);
  428. var cursorY = parseFloat(currentCursorVal[1]) / (imageHeight/2);
  429. drawCursor(cursorX, cursorY);
  430. // hide the cursor after 3s of inactivity
  431. } else if (cursorShownAt < new Date().getTime() - 3000) {
  432. showCursor(false);
  433. }
  434. // store the current slide and adjust the size of the slides
  435. currentImage = thisimg;
  436. resizeSlides();
  437. }
  438. handlePresentationAreaContent(t);
  439. }
  440. }
  441. });
  442. };
  443. // Deskshare's whiteboard variables
  444. var deskshareWidth = 1280.0;
  445. var deskshareHeight = 720.0;
  446. var widthScale = 1;
  447. var heightScale = 1;
  448. var widthTranslate = 0;
  449. var heightTranslate = 0;
  450. function clearTransform() {
  451. widthScale = 1;
  452. heightScale = 1;
  453. widthTranslate = 0;
  454. heightTranslate = 0;
  455. }
  456. function setDeskshareScale(originalVideoWidth, originalVideoHeight) {
  457. widthScale = originalVideoWidth / deskshareWidth;
  458. heightScale = originalVideoHeight / deskshareHeight;
  459. }
  460. function setDeskshareTranslate(originalVideoWidth, originalVideoHeight) {
  461. widthTranslate = (deskshareWidth - originalVideoWidth) / 2;
  462. heightTranslate = (deskshareHeight - originalVideoHeight) / 2;
  463. }
  464. // Deskshare viewBox has the information to transform the canvas to place it above the video
  465. function adaptViewBoxToDeskshare(time) {
  466. var dimension = getDeskshareDimension(time);
  467. setDeskshareScale(dimension.width, dimension.height);
  468. setDeskshareTranslate(dimension.width, dimension.height);
  469. var viewBox = "0.0 0.0 " + deskshareWidth + " " + deskshareHeight;
  470. return viewBox;
  471. }
  472. function getCanvasFromImage(image) {
  473. var canvasId = "canvas" + image.substr(5);
  474. var canvas = svgobj.contentDocument ? svgobj.contentDocument.getElementById(canvasId) : svgobj.getSVGDocument('svgfile').getElementById(canvasId);
  475. return canvas;
  476. }
  477. function getDeskshareImage() {
  478. var images = svgobj.contentDocument ? svgobj.contentDocument.getElementsByTagName("image") : svgobj.getSVGDocument('svgfile').getElementsByTagName("image");
  479. for(var i = 0; i < images.length; i++) {
  480. var element = images[i];
  481. var id = element.getAttribute("id");
  482. var href = element.getAttribute("xlink:href");
  483. if (href != null && href.indexOf("deskshare") !=-1) {
  484. return id;
  485. }
  486. }
  487. return "image0";
  488. }
  489. // Transform canvas to fit the different deskshare video sizes
  490. function setTransform(time) {
  491. if (deskshare_image == null) {
  492. deskshare_image = getDeskshareImage();
  493. }
  494. if (mustShowDesktopVideo(time)) {
  495. var canvas = getCanvasFromImage(deskshare_image);
  496. if (canvas !== null) {
  497. var scale = "scale(" + widthScale.toString() + ", " + heightScale.toString() + ")";
  498. var translate = "translate(" + widthTranslate.toString() + ", " + heightTranslate.toString() + ")";
  499. var transform = translate + " " + scale;
  500. canvas.setAttribute('transform', transform);
  501. }
  502. } else {
  503. clearTransform();
  504. }
  505. }
  506. function defineStartTime() {
  507. console.log("** Defining start time");
  508. if (params.t === undefined)
  509. return 1;
  510. var extractNumber = /\d+/g;
  511. var extractUnit = /\D+/g;
  512. var temp_start_time = 0;
  513. while (true) {
  514. var param1 = extractUnit.exec(params.t);
  515. var param2 = extractNumber.exec(params.t);
  516. if (param1 == null || param2 == null)
  517. break;
  518. var unit = String(param1).toLowerCase();
  519. var value = parseInt(String(param2));
  520. if (unit == "h")
  521. value *= 3600;
  522. else if (unit == "m")
  523. value *= 60;
  524. temp_start_time += value;
  525. }
  526. console.log("Start time: " + temp_start_time);
  527. return temp_start_time;
  528. }
  529. var lastFrameTime = 0.0;
  530. var shape;
  531. var current_shapes = [];
  532. var deskshare_image = null;
  533. var current_image = "image0";
  534. var previous_image = null;
  535. var current_canvas;
  536. var next_canvas;
  537. var next_image;
  538. var next_pgid;
  539. var curr_pgid;
  540. var svgfile;
  541. //current time
  542. var t;
  543. var len;
  544. var meetingDuration;
  545. //coordinates for x and y for each second
  546. var panAndZoomTimes = [];
  547. var viewBoxes = [];
  548. var coords = [];
  549. var times = [];
  550. // timestamp and id for drawings
  551. var shapeTime;
  552. var shapeId;
  553. var clearTimes = [];
  554. var main_shapes_ids = [];
  555. var vboxValues = {};
  556. var slideAspectValues = {};
  557. var currentSlideAspect = 0;
  558. var cursorValues = {};
  559. var currentCursorVal;
  560. var imageAtTime = {};
  561. var slidePlainText = {}; //holds slide plain text for retrieval
  562. var cursorStyle;
  563. var cursorShownAt = 0;
  564. var deskshareTimes = [];
  565. var sharingDesktop = false;
  566. var params = getUrlParameters();
  567. var MEETINGID = params.meetingId;
  568. // var HOST = window.location.host;
  569. // var url = "http://" + HOST + "/presentation/" + MEETINGID;
  570. var url = "/presentation/" + MEETINGID;
  571. var shapes_svg = url + '/shapes.svg';
  572. var events_xml = url + '/panzooms.xml';
  573. var cursor_xml = url + '/cursor.xml';
  574. var metadata_xml = url + '/metadata.xml';
  575. var deskshare_xml = url + '/deskshare.xml';
  576. var presentation_text_json = url + '/presentation_text.json';
  577. var firstLoad = true;
  578. var svgReady = false;
  579. var videoReady = false;
  580. var audioReady = false;
  581. var deskshareReady = false;
  582. var svgobj = document.createElement('object');
  583. svgobj.setAttribute('data', shapes_svg);
  584. svgobj.setAttribute('height', '100%');
  585. svgobj.setAttribute('width', '100%');
  586. // It's important to verify if all medias are ready before running Popcorn
  587. document.addEventListener('media-ready', function(event) {
  588. switch(event.detail) {
  589. case 'video':
  590. videoReady = true;
  591. break;
  592. case 'audio':
  593. audioReady = true;
  594. break;
  595. case 'deskshare':
  596. deskshareReady = true;
  597. break;
  598. case 'svg':
  599. svgReady = true;
  600. break;
  601. default:
  602. console.log('unhandled media-ready event: ' + event.detail);
  603. }
  604. if ((audioReady || videoReady) && deskshareReady && svgReady) {
  605. runPopcorn();
  606. if (firstLoad) initPopcorn();
  607. }
  608. }, false);
  609. function initPopcorn() {
  610. firstLoad = false;
  611. generateThumbnails();
  612. var startTime = defineStartTime();
  613. console.log("** startTime = " + startTime);
  614. Popcorn("#video").currentTime(startTime);
  615. if(isThereDeskshareVideo())
  616. Popcorn("#deskshare-video").currentTime(startTime);
  617. //Popcorn documentation suggests this way to get the duration, since this information does not come with 'loadedmetadata' event.
  618. Popcorn("#video").cue(2, function() {
  619. meetingDuration = parseFloat(Popcorn("#video").duration().toFixed(1));
  620. console.log("** Meeting duration (seconds): " + meetingDuration);
  621. });
  622. }
  623. svgobj.addEventListener('load', function() {
  624. console.log("got svgobj 'load' event");
  625. document.dispatchEvent(new CustomEvent('media-ready', {'detail': 'svg'}));
  626. }, false);
  627. svgobj.addEventListener('error', function() {
  628. console.log("got svgobj 'error' event");
  629. onSVGLoadingError();
  630. }, false);
  631. function onSVGLoadingError() {
  632. Pace.off('done');
  633. Pace.stop();
  634. $("#loading-error").css('visibility', 'visible');
  635. }
  636. // Fetches the metadata associated with the recording and uses it to configure
  637. // the playback page
  638. var getMetadata = function() {
  639. var xmlhttp;
  640. if (window.XMLHttpRequest) {// code for IE7, Firefox, Chrome, Opera, Safari
  641. xmlhttp = new XMLHttpRequest();
  642. } else {// code for IE6, IE5
  643. xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
  644. }
  645. xmlhttp.open("GET", metadata_xml, false);
  646. xmlhttp.send(null);
  647. if (xmlhttp.responseXML)
  648. var xmlDoc = xmlhttp.responseXML;
  649. else {
  650. var parser = new DOMParser();
  651. var xmlDoc = parser.parseFromString(xmlhttp.responseText, "application/xml");
  652. }
  653. var metadata = xmlDoc.getElementsByTagName("meta");
  654. if (metadata.length > 0) {
  655. metadata = metadata[0];
  656. var meetingName = metadata.getElementsByTagName("meetingName");
  657. if (meetingName.length > 0) {
  658. $("#recording-title").text(meetingName[0].textContent);
  659. $("#recording-title").attr("title", meetingName[0].textContent);
  660. }
  661. }
  662. };
  663. function setPresentationTextFromJSON(images, presentationText) {
  664. for (var m = 0; m < images.length; m++) {
  665. len = images[m].getAttribute("in").split(" ").length;
  666. for (var n = 0; n < len; n++) {
  667. imageAtTime[[images[m].getAttribute("in").split(" ")[n], images[m].getAttribute("out").split(" ")[n]]] = images[m].getAttribute("id");
  668. }
  669. // The logo at the start has no text attribute
  670. if (images[m].getAttribute("text")) {
  671. var imgId = images[m].getAttribute("id"); // Have to save the value because images array might go out of scope
  672. var imgTxt = images[m].getAttribute("text").split("/"); // Text format: presentation/PRESENTATION_ID/textfiles/SLIDE_ID.txt
  673. var presentationId = imgTxt[1];
  674. var slideId = imgTxt[3].split(".")[0];
  675. if (presentationText[presentationId] && presentationText[presentationId][slideId]) {
  676. slidePlainText[imgId] = $('<div/>').text(presentationText[presentationId][slideId]).html();
  677. } else {
  678. slidePlainText[imgId] = $('<div/>')
  679. }
  680. }
  681. }
  682. }
  683. function setPresentationTextFromTxt(images) {
  684. for (var m = 0; m < images.length; m++) {
  685. len = images[m].getAttribute("in").split(" ").length;
  686. for (var n = 0; n < len; n++) {
  687. imageAtTime[[images[m].getAttribute("in").split(" ")[n], images[m].getAttribute("out").split(" ")[n]]] = images[m].getAttribute("id");
  688. }
  689. // The logo at the start has no text attribute
  690. if (images[m].getAttribute("text")) {
  691. var txtFile = false;
  692. if (window.XMLHttpRequest) {
  693. // Code for IE7+, Firefox, Chrome, Opera, Safari
  694. txtFile = new XMLHttpRequest();
  695. } else {
  696. // Code for IE6, IE5
  697. txtFile = new ActiveXObject("Microsoft.XMLHTTP");
  698. }
  699. var imgId = images[m].getAttribute("id"); // Have to save the value because images array might go out of scope
  700. txtFile.open("GET", url + "/" + images[m].getAttribute("text"), false);
  701. txtFile.onreadystatechange = function() {
  702. if (txtFile.readyState === 4) {
  703. if (txtFile.status === 200) {
  704. slidePlainText[imgId] = $('<div/>').text(txtFile.responseText).html();
  705. }
  706. }
  707. };
  708. txtFile.send(null);
  709. }
  710. }
  711. }
  712. function processPresentationText(response) {
  713. // Making the object for requesting the read of the XML files.
  714. if (window.XMLHttpRequest) {
  715. // Code for IE7+, Firefox, Chrome, Opera, Safari
  716. var xmlhttp = new XMLHttpRequest();
  717. } else {
  718. // Code for IE6, IE5
  719. var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
  720. }
  721. xmlhttp.open("GET", shapes_svg, false);
  722. xmlhttp.send();
  723. var xmlDoc = xmlhttp.responseXML;
  724. // Getting all the event tags
  725. var shapeelements = xmlDoc.getElementsByTagName("svg");
  726. // Newer recordings have slide images identified by class="slide"
  727. // because they might include images in shapes
  728. var images = shapeelements[0].getElementsByClassName("slide");
  729. // To handle old recordings, fetch a list of all images instead
  730. if (images.length == 0) {
  731. images = shapeelements[0].getElementsByTagName("image");
  732. }
  733. if (response !== undefined) {
  734. setPresentationTextFromJSON(images, response);
  735. } else {
  736. setPresentationTextFromTxt(images);
  737. }
  738. //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.
  739. processSlideAspectTimes();
  740. }
  741. function processSlideAspectTimes() {
  742. var key;
  743. var lastAspectValue = 0;
  744. for (key in vboxValues) {
  745. if (vboxValues.hasOwnProperty(key)) {
  746. var start_timestamp = key.split(",")[0];
  747. var stop_timestamp = key.split(",")[1];
  748. var vboxWidth = parseFloat(vboxValues[key].split(" ")[2]);
  749. var vboxHeight = parseFloat(vboxValues[key].split(" ")[3]);
  750. var aspectValue = processAspectValue(vboxWidth,vboxHeight,start_timestamp,lastAspectValue);
  751. slideAspectValues[[start_timestamp, stop_timestamp]] = aspectValue;
  752. lastAspectValue = aspectValue;
  753. }
  754. }
  755. }
  756. function processAspectValue(vboxWidth, vboxHeight, time, lastAspectValue) {
  757. var imageId;
  758. if (time == "0.0") {
  759. //a little hack 'cause function getImageAtTime with time = 0.0 returns the background image...
  760. //we need the first slide instead
  761. imageId = "image1";
  762. }
  763. else {
  764. imageId = getImageAtTime(time);
  765. }
  766. if (imageId !== undefined) {
  767. var image;
  768. if (svgobj.contentDocument) {
  769. image = svgobj.contentDocument.getElementById(imageId);
  770. }
  771. else {
  772. image = svgobj.getSVGDocument('svgfile').getElementById(imageId);
  773. }
  774. if (image) {
  775. if(mustShowDesktopVideo(parseFloat(time))) {
  776. return lastAspectValue;
  777. }
  778. var imageWidth = parseFloat(image.getAttribute("width"));
  779. var imageHeight = parseFloat(image.getAttribute("height"));
  780. //fit-to-width: returning vbox aspect
  781. if(vboxWidth == imageWidth && vboxHeight < imageHeight) {
  782. return parseFloat(vboxWidth/vboxHeight);
  783. }
  784. //fit-to-page: returning image aspect
  785. else if(vboxWidth == imageWidth && vboxHeight == imageHeight) {
  786. return parseFloat(imageWidth/imageHeight);
  787. }
  788. //if it's not fit-to-width neither fit-to-page we return the previous aspect
  789. else {
  790. return lastAspectValue;
  791. }
  792. } else {
  793. console.log("processAspectValue: there is no image for the id = " + imageId);
  794. return lastAspectValue;
  795. }
  796. } else {
  797. console.log("processAspectValue: imageId undefined");
  798. return lastAspectValue;
  799. }
  800. }
  801. function getPresentationText() {
  802. console.log("** Getting text files");
  803. loadJSON(processPresentationText, presentation_text_json);
  804. }
  805. function loadJSON(callback, url) {
  806. var xobj;
  807. if (window.XMLHttpRequest) {
  808. // Code for IE7+, Firefox, Chrome, Opera, Safari
  809. xobj = new XMLHttpRequest();
  810. } else {
  811. // Code for IE6, IE5
  812. xobj = new ActiveXObject("Microsoft.XMLHTTP");
  813. }
  814. xobj.overrideMimeType("application/json");
  815. xobj.open('GET', url, true);
  816. xobj.onreadystatechange = function () {
  817. if (xobj.readyState == 4) {
  818. // Required use of an anonymous callback as .open will NOT return a value but simply returns undefined in asynchronous mode
  819. if (xobj.status == "200") {
  820. callback(JSON.parse(xobj.responseText));
  821. } else {
  822. console.log("Could not get JSON file");
  823. callback(undefined);
  824. }
  825. }
  826. };
  827. xobj.send(null);
  828. }
  829. document.getElementById('slide').appendChild(svgobj);
  830. var currentImage;
  831. // A small hack to hide the cursor when resizing the window, so it's not
  832. // misplaced while the window is being resized
  833. window.onresize = function(event) {
  834. showCursor(false);
  835. resizeSlides();
  836. resizeDeshareVideo();
  837. };
  838. // Resize the container that has the slides (and whiteboard) to be the maximum
  839. // size possible but still maintaining the aspect ratio of the slides.
  840. //
  841. // This is done here only because of pan and zoom. Pan/zoom is done by modifiyng
  842. // the svg's viewBox, and that requires the container that has the svg to be the
  843. // exact size we want to display the slides so that parts of the svg that are outside
  844. // of its parent's area are hidden. If we let the svg occupy all presentation area
  845. // (letting the svg do the image resizing), the slides will move and zoom around the
  846. // entire area when pan/zoom is activated, usually displaying more of the slide
  847. // than we want to (i.e. more than was displayed in the conference).
  848. var resizeSlides = function() {
  849. if (currentImage) {
  850. var $slide = $("#slide");
  851. var maxWidth = currentSlideAspect * $slide.parent().outerHeight();
  852. $slide.css("max-width", maxWidth);
  853. var maxHeight = $slide.parent().width() / currentSlideAspect;
  854. $slide.css("max-height", maxHeight);
  855. }
  856. };
  857. var resizeDeshareVideo = function() {
  858. if (!isThereDeskshareVideo()) return;
  859. var deskshareVideo = document.getElementById("deskshare-video");
  860. var $deskhareVideo = $("#deskshare-video");
  861. var videoWidth = parseInt(deskshareVideo.videoWidth, 10);
  862. var videoHeight = parseInt(deskshareVideo.videoHeight, 10);
  863. var aspectRatio = videoWidth/videoHeight;
  864. var max = aspectRatio * $deskhareVideo.parent().outerHeight();
  865. $deskhareVideo.css("max-width", max);
  866. var height = $deskhareVideo.parent().width() / aspectRatio;
  867. $deskhareVideo.css("max-height", height);
  868. };