選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

writing.js 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  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. "use strict";
  21. function getUrlParameters() {
  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 draw(x, y) {
  30. cursorStyle = document.getElementById("cursor").style;
  31. var slide = document.getElementById("slide");
  32. var obj = $("#slide > object");
  33. var scaledX = parseInt(x, 10) * (parseInt(obj.attr("width"), 10) / 800);
  34. var scaledY = parseInt(y, 10) * (parseInt(obj.attr("height"), 10) / 600);
  35. //move to the next place
  36. var leftValue = parseInt(slide.offsetLeft, 10) + parseInt(scaledX, 10)
  37. var topValue = parseInt(slide.offsetTop, 10) + parseInt(scaledY, 10)
  38. if (leftValue < 0){
  39. leftValue = 0
  40. }
  41. if (topValue < 0){
  42. topValue = 0
  43. }
  44. cursorStyle.left = leftValue + "px";
  45. cursorStyle.top = topValue + "px";
  46. }
  47. // Shows or hides the cursor object depending on true/false parameter passed.
  48. function showCursor(boolVal) {
  49. cursorStyle = document.getElementById("cursor").style;
  50. if(boolVal === false) {
  51. cursorStyle.height = "0px";
  52. cursorStyle.width = "0px";
  53. }
  54. else {
  55. cursorStyle.height = "10px";
  56. cursorStyle.width = "10px";
  57. }
  58. }
  59. function setViewBox(val) {
  60. if(svgobj.contentDocument) svgfile = svgobj.contentDocument.getElementById("svgfile");
  61. else svgfile = svgobj.getSVGDocument('svgfile').getElementById("svgfile");
  62. svgfile.setAttribute('viewBox', val);
  63. }
  64. function setCursor(val) {
  65. draw(val[0], val[1]);
  66. }
  67. function getImageAtTime(time) {
  68. var curr_t = parseFloat(time);
  69. var key;
  70. for (key in imageAtTime) {
  71. if(imageAtTime.hasOwnProperty(key)) {
  72. var arry = key.split(",");
  73. if ((parseFloat(arry[0]) <= curr_t) && (parseFloat(arry[1]) >= curr_t)) {
  74. return imageAtTime[key];
  75. }
  76. }
  77. }
  78. }
  79. function getViewboxAtTime(time) {
  80. var curr_t = parseFloat(time);
  81. var key;
  82. for (key in vboxValues) {
  83. if(vboxValues.hasOwnProperty(key)) {
  84. var arry = key.split(",");
  85. if(arry[1] == "end") {
  86. return vboxValues[key];
  87. }
  88. else if ((parseFloat(arry[0]) <= curr_t) && (parseFloat(arry[1]) >= curr_t)) {
  89. return vboxValues[key];
  90. }
  91. }
  92. }
  93. }
  94. function getCursorAtTime(time) {
  95. var coords = cursorValues[time];
  96. if(coords) return coords.split(' ');
  97. }
  98. function removeSlideChangeAttribute() {
  99. $('#video').removeAttr('slide-change');
  100. Popcorn('#video').unlisten(Popcorn.play, 'removeSlideChangeAttribute');
  101. }
  102. // - - - END OF JAVASCRIPT FUNCTIONS - - - //
  103. function runPopcorn() {
  104. if(svgobj.contentDocument) svgfile = svgobj.contentDocument.getElementById("svgfile");
  105. else svgfile = svgobj.getSVGDocument('svgfile');
  106. //making the object for requesting the read of the XML files.
  107. if (window.XMLHttpRequest) {
  108. // code for IE7+, Firefox, Chrome, Opera, Safari
  109. var xmlhttp = new XMLHttpRequest();
  110. } else {
  111. // code for IE6, IE5
  112. var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
  113. }
  114. // PROCESS SHAPES.SVG (in XML format).
  115. xmlhttp.open("GET", shapes_svg, false);
  116. xmlhttp.send();
  117. var xmlDoc = xmlhttp.responseXML;
  118. //getting all the event tags
  119. var shapeelements = xmlDoc.getElementsByTagName("svg");
  120. //get the array of values for the first shape (getDataPoints(0) is the first shape).
  121. var array = $(shapeelements[0]).find("g").filter(function(){ //get all the lines from the svg file
  122. return $(this).attr('class') == 'shape';
  123. });
  124. var images = shapeelements[0].getElementsByTagName("image");
  125. //fill the times array with the times of the svg images.
  126. for (var j = 0; j < array.length; j++) {
  127. times[j] = array[j].getAttribute("id").substr(4);
  128. }
  129. var times_length = times.length; //get the length of the times array.
  130. for(var m = 0; m < images.length; m++) {
  131. len = images[m].getAttribute("in").split(" ").length;
  132. for(var n = 0; n < len; n++) {
  133. imageAtTime[[images[m].getAttribute("in").split(" ")[n], images[m].getAttribute("out").split(" ")[n]]] = images[m].getAttribute("id");
  134. }
  135. // the logo at the start has no text attribute
  136. if (images[m].getAttribute("text")) {
  137. var txtFile = false;
  138. if (window.XMLHttpRequest) {
  139. // code for IE7+, Firefox, Chrome, Opera, Safari
  140. txtFile = new XMLHttpRequest();
  141. } else {
  142. // code for IE6, IE5
  143. txtFile = new ActiveXObject("Microsoft.XMLHTTP");
  144. }
  145. var imgid = images[m].getAttribute("id"); //have to save the value because images array might go out of scope
  146. txtFile.open("GET", url + "/" + images[m].getAttribute("text"), false);
  147. txtFile.onreadystatechange = function() {
  148. if (txtFile.readyState === 4) {
  149. if (txtFile.status === 200) {
  150. slidePlainText[imgid] = $('<div/>').text(txtFile.responseText).html();
  151. console.log("Text file read " + imgid);
  152. }
  153. }
  154. };
  155. txtFile.send(null);
  156. }
  157. }
  158. // PROCESS PANZOOMS.XML
  159. xmlhttp.open("GET", events_xml, false);
  160. xmlhttp.send();
  161. xmlDoc = xmlhttp.responseXML;
  162. //getting all the event tags
  163. var panelements = xmlDoc.getElementsByTagName("recording");
  164. var panZoomArray = panelements[0].getElementsByTagName("event");
  165. viewBoxes = xmlDoc.getElementsByTagName("viewBox");
  166. var pzlen = panZoomArray.length;
  167. var second_val;
  168. //fill the times array with the times of the svg images.
  169. for (var k = 0;k < pzlen; k++) {
  170. if(panZoomArray[k+1] == undefined) {
  171. second_val = "end";
  172. }
  173. else second_val = panZoomArray[k+1].getAttribute("timestamp");
  174. vboxValues[[panZoomArray[k].getAttribute("timestamp"), second_val]] = viewBoxes[k].childNodes[0].data;
  175. }
  176. // PROCESS CURSOR.XML
  177. xmlhttp.open("GET", cursor_xml, false);
  178. xmlhttp.send();
  179. xmlDoc = xmlhttp.responseXML;
  180. //getting all the event tags
  181. var curelements = xmlDoc.getElementsByTagName("recording");
  182. var cursorArray = curelements[0].getElementsByTagName("event");
  183. coords = xmlDoc.getElementsByTagName("cursor");
  184. var clen = cursorArray.length;
  185. //fill the times array with the times of the svg images.
  186. if(cursorArray.length != 0) cursorValues["0"] = "0 0";
  187. for (var m = 0; m < clen; m++) {
  188. cursorValues[cursorArray[m].getAttribute("timestamp")] = coords[m].childNodes[0].data;
  189. }
  190. svgobj.style.left = document.getElementById("slide").offsetLeft + "px";
  191. svgobj.style.top = "8px";
  192. var next_shape;
  193. var shape;
  194. for (var i = 0, len = times_length; i < len-1; i++) { //iterate through all the shapes and pick out the main ones
  195. var time = times[i];
  196. shape = svgobj.contentDocument.getElementById("draw" + time).getAttribute("shape");
  197. next_shape = svgobj.contentDocument.getElementById("draw" + times[i+1]).getAttribute("shape");
  198. if(shape !== next_shape) {
  199. main_shapes_times[main_shapes_times.length] = time;
  200. }
  201. }
  202. if(times.length !== 0) {
  203. main_shapes_times[main_shapes_times.length] = times[times.length-1]; //put last value into this array always!
  204. }
  205. var p = new Popcorn("#video");
  206. //update 60x / second the position of the next value.
  207. p.code({
  208. start: 1, // start time
  209. end: p.duration(),
  210. onFrame: function(options) {
  211. if(!((p.paused() === true) && (p.seeking() === false))) {
  212. var t = p.currentTime().toFixed(1); //get the time and round to 1 decimal place
  213. if(svgobj.contentDocument) current_shape = svgobj.contentDocument.getElementById("draw" + t);
  214. else current_shape = svgobj.getSVGDocument('svgfile').getElementById("draw" + t);
  215. if(current_shape !== null) { //if there is actually a new shape to be displayed
  216. current_shape = current_shape.getAttribute("shape"); //get actual shape tag for this specific time of playback
  217. }
  218. //redraw everything (only way to make everything elegant)
  219. for (var i = 0, len = times_length; i < len; i++) {
  220. var time_s = times[i];
  221. var time_f = parseFloat(time_s);
  222. if(svgobj.contentDocument) shape = svgobj.contentDocument.getElementById("draw" + time_s);
  223. else shape = svgobj.getSVGDocument('svgfile').getElementById("draw" + time_s);
  224. var shape_i = shape.getAttribute("shape");
  225. if (time_f < t) {
  226. if(shape_i === current_shape) { //currently drawing the same shape so don't draw the older steps
  227. shape.style.visibility = "hidden"; //hide older steps to shape
  228. }
  229. else if(main_shapes_times.indexOf(time_s) !== -1) { //as long as it is a main shape, it can be drawn... no intermediate steps.
  230. if(parseFloat(shape.getAttribute("undo")) === -1) { //As long as the undo event hasn't happened yet...
  231. shape.style.visibility = "visible";
  232. }
  233. else if (parseFloat(shape.getAttribute("undo")) > t) {
  234. shape.style.visibility = "visible";
  235. }
  236. else {
  237. shape.style.visibility = "hidden";
  238. }
  239. }
  240. }
  241. else if(time_s === t) { //for the shape with the time specific to the current time
  242. shape.style.visibility = "visible";
  243. }
  244. else { //for shapes that shouldn't be drawn yet (larger time than current time), don't draw them.
  245. shape.style.visibility = "hidden";
  246. }
  247. }
  248. var next_image = getImageAtTime(t); //fetch the name of the image at this time.
  249. var imageXOffset = 0;
  250. var imageYOffset = 0;
  251. if((current_image !== next_image) && (next_image !== undefined)){ //changing slide image
  252. if(svgobj.contentDocument) {
  253. svgobj.contentDocument.getElementById(current_image).style.visibility = "hidden";
  254. var ni = svgobj.contentDocument.getElementById(next_image);
  255. }
  256. else {
  257. svgobj.getSVGDocument('svgfile').getElementById(current_image).style.visibility = "hidden";
  258. var ni = svgobj.getSVGDocument('svgfile').getElementById(next_image);
  259. }
  260. document.getElementById("slideText").innerHTML = ""; //destroy old plain text
  261. ni.style.visibility = "visible";
  262. document.getElementById("slideText").innerHTML = slidePlainText[next_image] + next_image; //set new plain text
  263. if ($("#accEnabled").is(':checked')) {
  264. //pause the playback on slide change
  265. p.pause();
  266. $('#video').attr('slide-change', 'slide-change');
  267. p.listen(Popcorn.play, removeSlideChangeAttribute);
  268. }
  269. var num_current = current_image.substr(5);
  270. var num_next = next_image.substr(5);
  271. if(svgobj.contentDocument) currentcanvas = svgobj.contentDocument.getElementById("canvas" + num_current);
  272. else currentcanvas = svgobj.getSVGDocument('svgfile').getElementById("canvas" + num_current);
  273. if(currentcanvas !== null) {
  274. currentcanvas.setAttribute("display", "none");
  275. }
  276. if(svgobj.contentDocument) nextcanvas = svgobj.contentDocument.getElementById("canvas" + num_next);
  277. else nextcanvas = svgobj.getSVGDocument('svgfile').getElementById("canvas" + num_next);
  278. if((nextcanvas !== undefined) && (nextcanvas != null)) {
  279. nextcanvas.setAttribute("display", "");
  280. }
  281. current_image = next_image;
  282. }
  283. if(svgobj.contentDocument) var thisimg = svgobj.contentDocument.getElementById(current_image);
  284. else var thisimg = svgobj.getSVGDocument('svgfile').getElementById(current_image);
  285. var offsets = thisimg.getBoundingClientRect();
  286. // Offsets divided by 4. By 2 because of the padding and by 2 again because 800x600 is half 1600x1200
  287. imageXOffset = (1600 - parseInt(thisimg.getAttribute("width"), 10))/4;
  288. imageYOffset = (1200 - parseInt(thisimg.getAttribute("height"), 10))/4;
  289. var vboxVal = getViewboxAtTime(t);
  290. if(vboxVal !== undefined) {
  291. setViewBox(vboxVal);
  292. }
  293. var cursorVal = getCursorAtTime(t);
  294. var cursor_on = false;
  295. if(cursorVal != null) {
  296. if(!cursor_on) {
  297. document.getElementById("cursor").style.visibility = 'visible'; //make visible
  298. cursor_on = true;
  299. }
  300. setCursor([parseFloat(cursorVal[0]) + imageXOffset - 6, parseFloat(cursorVal[1]) + imageYOffset - 6]); //-6 is for radius of cursor offset
  301. }
  302. }
  303. }
  304. });
  305. };
  306. function defineStartTime() {
  307. if (params.t === undefined)
  308. return 1;
  309. var extractNumber = /\d+/g;
  310. var extractUnit = /\D+/g;
  311. var temp_start_time = 0;
  312. while (true) {
  313. var param1 = extractUnit.exec(params.t);
  314. var param2 = extractNumber.exec(params.t);
  315. if (param1 == null || param2 == null)
  316. break;
  317. var unit = String(param1).toLowerCase();
  318. var value = parseInt(String(param2));
  319. if (unit == "h")
  320. value *= 3600;
  321. else if (unit == "m")
  322. value *= 60;
  323. temp_start_time += value;
  324. }
  325. console.log("Start time: " + temp_start_time);
  326. return temp_start_time;
  327. }
  328. var current_canvas = "canvas0";
  329. var current_image = "image0";
  330. var currentcanvas;
  331. var shape;
  332. var nextcanvas;
  333. var next_image;
  334. var next_pgid;
  335. var curr_pgid;
  336. var svgfile;
  337. //current time
  338. var t;
  339. var len;
  340. var current_shape;
  341. //coordinates for x and y for each second
  342. var panAndZoomTimes = [];
  343. var viewBoxes = [];
  344. var coords = [];
  345. var times = [];
  346. var clearTimes = [];
  347. var main_shapes_times = [];
  348. var vboxValues = {};
  349. var cursorValues = {};
  350. var imageAtTime = {};
  351. var slidePlainText = {}; //holds slide plain text for retrieval
  352. var cursorStyle;
  353. var params = getUrlParameters();
  354. var MEETINGID = params.meetingId;
  355. var url = "/presentation/" + MEETINGID;
  356. var shapes_svg = url + '/shapes.svg';
  357. var events_xml = url + '/panzooms.xml';
  358. var cursor_xml = url + '/cursor.xml';
  359. var svgobj = document.createElement('object');
  360. svgobj.setAttribute('data', shapes_svg);
  361. svgobj.setAttribute('height', '600px');
  362. svgobj.setAttribute('width', '800px');
  363. svgobj.addEventListener('load', runPopcorn, false);
  364. /**
  365. * we need an urgently refactor here
  366. * first the writing.js must be loaded, and then runPopcorn loads, but it loads
  367. * only after the svg file gets loaded, and the generation of thumbnails must
  368. * came after that because it needs the popcorn element to be created properly
  369. */
  370. svgobj.addEventListener('load', function() {
  371. generateThumbnails();
  372. var p = Popcorn("#video");
  373. p.on('loadeddata', function() {
  374. p.currentTime(defineStartTime());
  375. });
  376. // Sometimes media has already loaded before our loadeddata listener is
  377. // attached. If the media is already past the loadeddata stage then we
  378. // trigger the event manually ourselves
  379. if ($('#video')[0].readyState > 0) {
  380. p.emit('loadeddata');
  381. }
  382. }, false);
  383. document.getElementById('slide').appendChild(svgobj);
  384. window.onresize = function(event) {
  385. svgobj.style.left = document.getElementById("slide").offsetLeft + "px";
  386. svgobj.style.top = "8px";
  387. };