JJ Allaire | 375805c | 2016-11-15 08:56:43 -0500 | [diff] [blame^] | 1 | /***************************************************************** |
| 2 | ** Author: Asvin Goel, goel@telematique.eu |
| 3 | ** |
| 4 | ** A plugin for reveal.js adding a chalkboard. |
| 5 | ** |
| 6 | ** Version: 0.5 |
| 7 | ** |
| 8 | ** License: MIT license (see LICENSE.md) |
| 9 | ** |
| 10 | ** Credits: |
| 11 | ** Chalkboard effect by Mohamed Moustafa https://github.com/mmoustafa/Chalkboard |
| 12 | ******************************************************************/ |
| 13 | |
| 14 | var RevealChalkboard = window.RevealChalkboard || (function(){ |
| 15 | var path = scriptPath(); |
| 16 | function scriptPath() { |
| 17 | // obtain plugin path from the script element |
| 18 | var src; |
| 19 | if (document.currentScript) { |
| 20 | src = document.currentScript.src; |
| 21 | } else { |
| 22 | var sel = document.querySelector('script[src$="/chalkboard.js"]') |
| 23 | if (sel) { |
| 24 | src = sel.src; |
| 25 | } |
| 26 | } |
| 27 | |
| 28 | var path = typeof src === undefined ? src |
| 29 | : src.slice(0, src.lastIndexOf("/") + 1); |
| 30 | //console.log("Path: " + path); |
| 31 | return path; |
| 32 | } |
| 33 | |
| 34 | /***************************************************************** |
| 35 | ** Configuration |
| 36 | ******************************************************************/ |
| 37 | var config = Reveal.getConfig().chalkboard || {}; |
| 38 | |
| 39 | var background, pen, draw, color; |
| 40 | var theme = config.theme || "chalkboard"; |
| 41 | switch ( theme ) { |
| 42 | case "whiteboard": |
| 43 | background = [ 'rgba(127,127,127,.1)' , path + 'img/whiteboard.png' ]; |
| 44 | pen = [ 'url(' + path + 'img/boardmarker.png), auto', |
| 45 | 'url(' + path + 'img/boardmarker.png), auto' ]; |
| 46 | draw = [ drawWithPen , drawWithPen ]; |
| 47 | color = [ 'rgba(0,0,255,1)', 'rgba(0,0,255,1)' ]; |
| 48 | break; |
| 49 | default: |
| 50 | background = [ 'rgba(127,127,127,.1)' , path + 'img/blackboard.png' ]; |
| 51 | pen = [ 'url(' + path + 'img/boardmarker.png), auto', |
| 52 | 'url(' + path + 'img/chalk.png), auto' ]; |
| 53 | draw = [ drawWithPen , drawWithChalk ]; |
| 54 | color = [ 'rgba(0,0,255,1)', 'rgba(255,255,255,0.5)' ]; |
| 55 | } |
| 56 | |
| 57 | if ( config.background ) background = config.background; |
| 58 | if ( config.pen ) pen = config.pen; |
| 59 | if ( config.draw ) draw = config.draw; |
| 60 | if ( config.color ) color = config.color; |
| 61 | |
| 62 | var toggleChalkboardButton = config.toggleChalkboardButton == undefined ? true : config.toggleChalkboardButton; |
| 63 | var toggleNotesButton = config.toggleNotesButton == undefined ? true : config.toggleNotesButton; |
| 64 | var transition = config.transition || 800; |
| 65 | |
| 66 | var readOnly = config.readOnly; |
| 67 | |
| 68 | var legacyFileSupport = config.legacyFileSupport; |
| 69 | if ( legacyFileSupport ) { console.warn("Legacy file support is deprecated and may be removed in future versions!") } |
| 70 | |
| 71 | /***************************************************************** |
| 72 | ** Setup |
| 73 | ******************************************************************/ |
| 74 | |
| 75 | var eraserDiameter = 20; |
| 76 | |
| 77 | if ( toggleChalkboardButton ) { |
| 78 | //console.log("toggleChalkboardButton") |
| 79 | var button = document.createElement( 'div' ); |
| 80 | button.className = "chalkboard-button"; |
| 81 | button.id = "toggle-chalkboard"; |
| 82 | button.style.vivibility = "visible"; |
| 83 | button.style.position = "absolute"; |
| 84 | button.style.zIndex = 30; |
| 85 | button.style.fontSize = "24px"; |
| 86 | |
| 87 | button.style.left = toggleChalkboardButton.left || "30px"; |
| 88 | button.style.bottom = toggleChalkboardButton.bottom || "30px"; |
| 89 | button.style.top = toggleChalkboardButton.top || "auto"; |
| 90 | button.style.right = toggleChalkboardButton.right || "auto"; |
| 91 | |
| 92 | button.innerHTML = '<a href="#" onclick="RevealChalkboard.toggleChalkboard(); return false;"><i class="fa fa-pencil-square-o"></i></a>' |
| 93 | document.querySelector(".reveal").appendChild( button ); |
| 94 | } |
| 95 | if ( toggleNotesButton ) { |
| 96 | //console.log("toggleNotesButton") |
| 97 | var button = document.createElement( 'div' ); |
| 98 | button.className = "chalkboard-button"; |
| 99 | button.id = "toggle-notes"; |
| 100 | button.style.position = "absolute"; |
| 101 | button.style.zIndex = 30; |
| 102 | button.style.fontSize = "24px"; |
| 103 | |
| 104 | button.style.left = toggleNotesButton.left || "70px"; |
| 105 | button.style.bottom = toggleNotesButton.bottom || "30px"; |
| 106 | button.style.top = toggleNotesButton.top || "auto"; |
| 107 | button.style.right = toggleNotesButton.right || "auto"; |
| 108 | |
| 109 | button.innerHTML = '<a href="#" onclick="RevealChalkboard.toggleNotesCanvas(); return false;"><i class="fa fa-pencil"></i></a>' |
| 110 | document.querySelector(".reveal").appendChild( button ); |
| 111 | } |
| 112 | //alert("Buttons"); |
| 113 | |
| 114 | var drawingCanvas = [ {id: "notescanvas" }, {id: "chalkboard" } ]; |
| 115 | setupDrawingCanvas(0); |
| 116 | setupDrawingCanvas(1); |
| 117 | |
| 118 | var mode = 0; // 0: notes canvas, 1: chalkboard |
| 119 | |
| 120 | var mouseX = 0; |
| 121 | var mouseY = 0; |
| 122 | var xLast = null; |
| 123 | var yLast = null; |
| 124 | |
| 125 | var slideStart = Date.now(); |
| 126 | var slideIndices = { h:0, v:0 }; |
| 127 | var event = null; |
| 128 | var timeouts = [ [], [] ]; |
| 129 | var touchTimeout = null; |
| 130 | var slidechangeTimeout = null; |
| 131 | var playback = false; |
| 132 | |
| 133 | function setupDrawingCanvas( id ) { |
| 134 | var container = document.createElement( 'div' ); |
| 135 | container.id = drawingCanvas[id].id; |
| 136 | container.classList.add( 'overlay' ); |
| 137 | container.setAttribute( 'data-prevent-swipe', '' ); |
| 138 | container.oncontextmenu = function() { return false; } |
| 139 | container.style.cursor = pen[ id ]; |
| 140 | |
| 141 | drawingCanvas[id].width = window.innerWidth; |
| 142 | drawingCanvas[id].height = window.innerHeight; |
| 143 | drawingCanvas[id].scale = 1; |
| 144 | drawingCanvas[id].xOffset = 0; |
| 145 | drawingCanvas[id].yOffset = 0; |
| 146 | |
| 147 | |
| 148 | if ( id == "0" ) { |
| 149 | container.style.background = 'rgba(0,0,0,0)'; |
| 150 | container.style.zIndex = "24"; |
| 151 | container.classList.add( 'visible' ) |
| 152 | container.style.pointerEvents = "none"; |
| 153 | |
| 154 | var slides = document.querySelector(".slides"); |
| 155 | var aspectRatio = Reveal.getConfig().width / Reveal.getConfig().height; |
| 156 | if ( drawingCanvas[id].width > drawingCanvas[id].height*aspectRatio ) { |
| 157 | drawingCanvas[id].xOffset = (drawingCanvas[id].width - drawingCanvas[id].height*aspectRatio) / 2; |
| 158 | } |
| 159 | else if ( drawingCanvas[id].height > drawingCanvas[id].width/aspectRatio ) { |
| 160 | drawingCanvas[id].yOffset = ( drawingCanvas[id].height - drawingCanvas[id].width/aspectRatio ) / 2; |
| 161 | } |
| 162 | } |
| 163 | else { |
| 164 | container.style.background = 'url("' + background[id] + '") repeat'; |
| 165 | container.style.zIndex = "26"; |
| 166 | } |
| 167 | |
| 168 | var sponge = document.createElement( 'img' ); |
| 169 | sponge.src = path + 'img/sponge.png'; |
| 170 | sponge.id = "sponge"; |
| 171 | sponge.style.visibility = "hidden"; |
| 172 | sponge.style.position = "absolute"; |
| 173 | container.appendChild( sponge ); |
| 174 | drawingCanvas[id].sponge = sponge; |
| 175 | |
| 176 | var canvas = document.createElement( 'canvas' ); |
| 177 | canvas.width = drawingCanvas[id].width; |
| 178 | canvas.height = drawingCanvas[id].height; |
| 179 | canvas.setAttribute( 'data-chalkboard', id ); |
| 180 | canvas.style.cursor = pen[ id ]; |
| 181 | container.appendChild( canvas ); |
| 182 | drawingCanvas[id].canvas = canvas; |
| 183 | |
| 184 | drawingCanvas[id].context = canvas.getContext("2d"); |
| 185 | |
| 186 | |
| 187 | document.querySelector( '.reveal' ).appendChild( container ); |
| 188 | drawingCanvas[id].container = container; |
| 189 | } |
| 190 | |
| 191 | |
| 192 | /***************************************************************** |
| 193 | ** Storage |
| 194 | ******************************************************************/ |
| 195 | var storage = [ |
| 196 | { width: drawingCanvas[0].width - 2 * drawingCanvas[0].xOffset, height: drawingCanvas[0].height - 2 * drawingCanvas[0].yOffset, data: []}, |
| 197 | { width: drawingCanvas[1].width, height: drawingCanvas[1].height, data: []} |
| 198 | ]; |
| 199 | //console.log( JSON.stringify(storage)); |
| 200 | |
| 201 | if ( config.src != null ) { |
| 202 | loadData( config.src ); |
| 203 | } |
| 204 | |
| 205 | |
| 206 | /** |
| 207 | * Load data. |
| 208 | */ |
| 209 | function loadData( filename ) { |
| 210 | var xhr = new XMLHttpRequest(); |
| 211 | xhr.onload = function() { |
| 212 | if (xhr.readyState === 4) { |
| 213 | storage = JSON.parse(xhr.responseText); |
| 214 | for (var id = 0; id < storage.length; id++) { |
| 215 | if ( drawingCanvas[id].width != storage[id].width || drawingCanvas[id].height != storage[id].height ) { |
| 216 | drawingCanvas[id].scale = Math.min( drawingCanvas[id].width/storage[id].width, drawingCanvas[id].height/storage[id].height); |
| 217 | drawingCanvas[id].xOffset = (drawingCanvas[id].width - storage[id].width * drawingCanvas[id].scale)/2; |
| 218 | drawingCanvas[id].yOffset = (drawingCanvas[id].height - storage[id].height * drawingCanvas[id].scale)/2; |
| 219 | } |
| 220 | if ( config.readOnly ) { |
| 221 | drawingCanvas[id].container.style.cursor = 'default'; |
| 222 | drawingCanvas[id].canvas.style.cursor = 'default'; |
| 223 | } |
| 224 | } |
| 225 | //console.log("Drawings loaded"); |
| 226 | } |
| 227 | else { |
| 228 | config.readOnly = undefined; |
| 229 | readOnly = undefined; |
| 230 | console.warn( 'Failed to get file ' + filename +". ReadyState: " + xhr.readyState + ", Status: " + xhr.status); |
| 231 | } |
| 232 | }; |
| 233 | |
| 234 | xhr.open( 'GET', filename, true ); |
| 235 | try { |
| 236 | xhr.send(); |
| 237 | } |
| 238 | catch ( error ) { |
| 239 | config.readOnly = undefined; |
| 240 | readOnly = undefined; |
| 241 | console.warn( 'Failed to get file ' + filename + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + error ); |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | /** |
| 246 | * Download data. |
| 247 | */ |
| 248 | function downloadData() { |
| 249 | var a = document.createElement('a'); |
| 250 | document.body.appendChild(a); |
| 251 | try { |
| 252 | // cleanup slide data without events |
| 253 | for (var id = 0; id < 2; id++) { |
| 254 | for (var i = storage[id].data.length-1; i >= 0; i--) { |
| 255 | if (storage[id].data[i].events.length == 0) { |
| 256 | storage[id].data.splice(i, 1); |
| 257 | } |
| 258 | } |
| 259 | } |
| 260 | a.download = "chalkboard.json"; |
| 261 | var blob = new Blob( [ JSON.stringify( storage ) ], { type: "application/json"} ); |
| 262 | a.href = window.URL.createObjectURL( blob ); |
| 263 | } catch( error ) { |
| 264 | a.innerHTML += " (" + error + ")"; |
| 265 | } |
| 266 | a.click(); |
| 267 | document.body.removeChild(a); |
| 268 | } |
| 269 | |
| 270 | /** |
| 271 | * Returns data object for the slide with the given indices. |
| 272 | */ |
| 273 | function getSlideData( indices, id ) { |
| 274 | if ( id == undefined ) id = mode; |
| 275 | if (!indices) indices = slideIndices; |
| 276 | var data; |
| 277 | for (var i = 0; i < storage[id].data.length; i++) { |
| 278 | if (storage[id].data[i].slide.h === indices.h && storage[id].data[i].slide.v === indices.v && storage[id].data[i].slide.f === indices.f ) { |
| 279 | data = storage[id].data[i]; |
| 280 | return data; |
| 281 | } |
| 282 | if ( !legacyFileSupport && |
| 283 | ( storage[id].data[i].slide.h > indices.h || |
| 284 | ( storage[id].data[i].slide.h === indices.h && storage[id].data[i].slide.v > indices.v ) || |
| 285 | ( storage[id].data[i].slide.h === indices.h && storage[id].data[i].slide.v === indices.v && storage[id].data[i].slide.f > indices.f ) |
| 286 | ) |
| 287 | ) { |
| 288 | storage[id].data.splice( i, 0, { slide: indices, events: [], duration: 0 } ); |
| 289 | data = storage[id].data[i]; |
| 290 | return data; |
| 291 | } |
| 292 | } |
| 293 | storage[id].data.push( { slide: indices, events: [], duration: 0 } ); |
| 294 | data = storage[id].data[storage[id].data.length-1]; |
| 295 | return data; |
| 296 | } |
| 297 | |
| 298 | /** |
| 299 | * Returns maximum duration of slide playback for both modes |
| 300 | */ |
| 301 | function getSlideDuration( indices ) { |
| 302 | if (!indices) indices = slideIndices; |
| 303 | var duration = 0; |
| 304 | for (var id = 0; id < 2; id++) { |
| 305 | for (var i = 0; i < storage[id].data.length; i++) { |
| 306 | if (storage[id].data[i].slide.h === indices.h && storage[id].data[i].slide.v === indices.v && storage[id].data[i].slide.f === indices.f ) { |
| 307 | duration = Math.max( duration, storage[id].data[i].duration ); |
| 308 | break; |
| 309 | } |
| 310 | } |
| 311 | } |
| 312 | //console.log( duration ); |
| 313 | return duration; |
| 314 | } |
| 315 | |
| 316 | /***************************************************************** |
| 317 | ** Print |
| 318 | ******************************************************************/ |
| 319 | var printMode = ( /print-pdf/gi ).test( window.location.search ); |
| 320 | //console.log("createPrintout" + printMode) |
| 321 | |
| 322 | function createPrintout( ) { |
| 323 | //console.log( 'Create printout for ' + storage[1].data.length + " slides"); |
| 324 | drawingCanvas[0].container.classList.remove( 'visible' ); // do not print notes canvas |
| 325 | |
| 326 | var patImg = new Image(); |
| 327 | patImg.onload = function () { |
| 328 | var nextSlide = []; |
| 329 | var width = Reveal.getConfig().width; |
| 330 | var height = Reveal.getConfig().height; |
| 331 | var scale = 1; |
| 332 | var xOffset = 0; |
| 333 | var yOffset = 0; |
| 334 | if ( width != storage[1].width || height != storage[1].height ) { |
| 335 | scale = Math.min( width/storage[1].width, height/storage[1].height); |
| 336 | xOffset = (width - storage[1].width * scale)/2; |
| 337 | yOffset = (height - storage[1].height * scale)/2; |
| 338 | } |
| 339 | |
| 340 | for (var i = 0; i < storage[1].data.length; i++) { |
| 341 | var slide = Reveal.getSlide( storage[1].data[i].slide.h, storage[1].data[i].slide.v ); |
| 342 | nextSlide.push( slide.nextSibling ); |
| 343 | } |
| 344 | for (var i = 0; i < storage[1].data.length; i++) { |
| 345 | var slideData = getSlideData( storage[1].data[i].slide, 1 ); |
| 346 | |
| 347 | var imgCanvas = document.createElement('canvas'); |
| 348 | imgCanvas.width = width; |
| 349 | imgCanvas.height = height; |
| 350 | |
| 351 | var imgCtx = imgCanvas.getContext("2d"); |
| 352 | imgCtx.fillStyle = imgCtx.createPattern( patImg ,'repeat'); |
| 353 | imgCtx.rect(0,0,imgCanvas.width,imgCanvas.height); |
| 354 | imgCtx.fill(); |
| 355 | |
| 356 | for (var j = 0; j < slideData.events.length; j++) { |
| 357 | switch ( slideData.events[j].type ) { |
| 358 | case "draw": |
| 359 | for (var k = 1; k < slideData.events[j].curve.length; k++) { |
| 360 | draw[1]( imgCtx, |
| 361 | xOffset + slideData.events[j].curve[k-1].x*scale, |
| 362 | yOffset + slideData.events[j].curve[k-1].y*scale, |
| 363 | xOffset + slideData.events[j].curve[k].x*scale, |
| 364 | yOffset + slideData.events[j].curve[k].y*scale |
| 365 | ); |
| 366 | } |
| 367 | break; |
| 368 | case "erase": |
| 369 | for (var k = 0; k < slideData.events[j].curve.length; k++) { |
| 370 | erase( imgCtx, |
| 371 | xOffset + slideData.events[j].curve[k].x*scale, |
| 372 | yOffset + slideData.events[j].curve[k].y*scale |
| 373 | ); |
| 374 | } |
| 375 | break; |
| 376 | case "clear": |
| 377 | addPrintout( nextSlide[i], imgCanvas, patImg ); |
| 378 | imgCtx.clearRect(0,0,imgCanvas.width,imgCanvas.height); |
| 379 | imgCtx.fill(); |
| 380 | break; |
| 381 | default: |
| 382 | break; |
| 383 | } |
| 384 | } |
| 385 | if ( slideData.events.length ) { |
| 386 | addPrintout( nextSlide[i], imgCanvas, patImg ); |
| 387 | } |
| 388 | } |
| 389 | Reveal.sync(); |
| 390 | }; |
| 391 | patImg.src = background[1]; |
| 392 | } |
| 393 | |
| 394 | function addPrintout( nextSlide, imgCanvas, patImg ) { |
| 395 | var slideCanvas = document.createElement('canvas'); |
| 396 | slideCanvas.width = Reveal.getConfig().width; |
| 397 | slideCanvas.height = Reveal.getConfig().height; |
| 398 | var ctx = slideCanvas.getContext("2d"); |
| 399 | ctx.fillStyle = ctx.createPattern( patImg ,'repeat'); |
| 400 | ctx.rect(0,0,slideCanvas.width,slideCanvas.height); |
| 401 | ctx.fill(); |
| 402 | ctx.drawImage(imgCanvas, 0, 0); |
| 403 | |
| 404 | var newSlide = document.createElement( 'section' ); |
| 405 | newSlide.classList.add( 'present' ); |
| 406 | newSlide.innerHTML = '<h1 style="visibility:hidden">Drawing</h1>'; |
| 407 | newSlide.setAttribute("data-background-size", '100% 100%' ); |
| 408 | newSlide.setAttribute("data-background-repeat", 'norepeat' ); |
| 409 | newSlide.setAttribute("data-background", 'url("' + slideCanvas.toDataURL("image/png") +'")' ); |
| 410 | nextSlide.parentElement.insertBefore( newSlide, nextSlide ); |
| 411 | } |
| 412 | |
| 413 | /***************************************************************** |
| 414 | ** Drawings |
| 415 | ******************************************************************/ |
| 416 | |
| 417 | function drawWithPen(context,fromX,fromY,toX,toY){ |
| 418 | context.lineWidth = 3; |
| 419 | context.lineCap = 'round'; |
| 420 | context.strokeStyle = color[0]; |
| 421 | context.beginPath(); |
| 422 | context.moveTo(fromX, fromY); |
| 423 | context.lineTo(toX, toY); |
| 424 | context.stroke(); |
| 425 | } |
| 426 | |
| 427 | function drawWithChalk(context,fromX,fromY,toX,toY){ |
| 428 | var brushDiameter = 7; |
| 429 | context.lineWidth = brushDiameter; |
| 430 | context.lineCap = 'round'; |
| 431 | context.fillStyle = color[1]; // 'rgba(255,255,255,0.5)'; |
| 432 | context.strokeStyle = color[1]; |
| 433 | var opacity = Math.min(0.8, Math.max(0,color[1].replace(/^.*,(.+)\)/,'$1') - 0.1)) + Math.random()*0.2; |
| 434 | context.strokeStyle = context.strokeStyle.replace(/[\d\.]+\)$/g, opacity + ')'); |
| 435 | context.beginPath(); |
| 436 | context.moveTo(fromX, fromY); |
| 437 | context.lineTo(toX, toY); |
| 438 | context.stroke(); |
| 439 | // Chalk Effect |
| 440 | var length = Math.round(Math.sqrt(Math.pow(toX-fromX,2)+Math.pow(toY-fromY,2))/(5/brushDiameter)); |
| 441 | var xUnit = (toX-fromX)/length; |
| 442 | var yUnit = (toY-fromY)/length; |
| 443 | for(var i=0; i<length; i++ ){ |
| 444 | var xCurrent = fromX+(i*xUnit); |
| 445 | var yCurrent = fromY+(i*yUnit); |
| 446 | var xRandom = xCurrent+(Math.random()-0.5)*brushDiameter*1.2; |
| 447 | var yRandom = yCurrent+(Math.random()-0.5)*brushDiameter*1.2; |
| 448 | context.clearRect( xRandom, yRandom, Math.random()*2+2, Math.random()+1); |
| 449 | } |
| 450 | } |
| 451 | function erase(context,x,y){ |
| 452 | context.save(); |
| 453 | context.beginPath(); |
| 454 | context.arc(x, y, eraserDiameter, 0, 2 * Math.PI, false); |
| 455 | context.clip(); |
| 456 | context.clearRect(x - eraserDiameter - 1, y - eraserDiameter - 1, eraserDiameter * 2 + 2, eraserDiameter * 2 + 2); |
| 457 | context.restore(); |
| 458 | |
| 459 | } |
| 460 | |
| 461 | |
| 462 | /** |
| 463 | * Opens an overlay for the chalkboard. |
| 464 | */ |
| 465 | function showChalkboard() { |
| 466 | //console.log("showChalkboard"); |
| 467 | clearTimeout(touchTimeout); |
| 468 | touchTimeout = null; |
| 469 | drawingCanvas[0].sponge.style.visibility = "hidden"; // make sure that the sponge from touch events is hidden |
| 470 | drawingCanvas[1].sponge.style.visibility = "hidden"; // make sure that the sponge from touch events is hidden |
| 471 | drawingCanvas[1].container.classList.add( 'visible' ); |
| 472 | mode = 1; |
| 473 | } |
| 474 | |
| 475 | |
| 476 | /** |
| 477 | * Closes open chalkboard. |
| 478 | */ |
| 479 | function closeChalkboard() { |
| 480 | clearTimeout(touchTimeout); |
| 481 | touchTimeout = null; |
| 482 | drawingCanvas[0].sponge.style.visibility = "hidden"; // make sure that the sponge from touch events is hidden |
| 483 | drawingCanvas[1].sponge.style.visibility = "hidden"; // make sure that the sponge from touch events is hidden |
| 484 | drawingCanvas[1].container.classList.remove( 'visible' ); |
| 485 | xLast = null; |
| 486 | yLast = null; |
| 487 | event = null; |
| 488 | mode = 0; |
| 489 | } |
| 490 | |
| 491 | /** |
| 492 | * Clear current canvas. |
| 493 | */ |
| 494 | function clearCanvas( id ) { |
| 495 | if ( id == 0 ) clearTimeout( slidechangeTimeout ); |
| 496 | drawingCanvas[id].context.clearRect(0,0,drawingCanvas[id].width,drawingCanvas[id].height); |
| 497 | } |
| 498 | |
| 499 | /***************************************************************** |
| 500 | ** Playback |
| 501 | ******************************************************************/ |
| 502 | |
| 503 | document.addEventListener('seekplayback', function( event ) { |
| 504 | //console.log('event seekplayback ' + event.timestamp); |
| 505 | stopPlayback(); |
| 506 | if ( !playback || event.timestamp == 0) { |
| 507 | // in other cases startplayback fires after seeked |
| 508 | startPlayback( event.timestamp ); |
| 509 | } |
| 510 | //console.log('seeked'); |
| 511 | }); |
| 512 | |
| 513 | |
| 514 | document.addEventListener('startplayback', function( event ) { |
| 515 | //console.log('event startplayback ' + event.timestamp); |
| 516 | stopPlayback(); |
| 517 | playback = true; |
| 518 | startPlayback( event.timestamp ); |
| 519 | }); |
| 520 | |
| 521 | document.addEventListener('stopplayback', function( event ) { |
| 522 | //console.log('event stopplayback ' + (Date.now() - slideStart) ); |
| 523 | playback = false; |
| 524 | stopPlayback(); |
| 525 | }); |
| 526 | |
| 527 | document.addEventListener('startrecording', function( event ) { |
| 528 | //console.log('event startrecording ' + event.timestamp); |
| 529 | startRecording(); |
| 530 | }); |
| 531 | |
| 532 | function recordEvent( event ) { |
| 533 | var slideData = getSlideData(); |
| 534 | var i = slideData.events.length; |
| 535 | while ( i > 0 && event.begin < slideData.events[i-1].begin ) { |
| 536 | i--; |
| 537 | } |
| 538 | slideData.events.splice( i, 0, event); |
| 539 | slideData.duration = Math.max( slideData.duration, Date.now() - slideStart ) + 1; |
| 540 | } |
| 541 | |
| 542 | function startRecording() { |
| 543 | resetSlide( true ); |
| 544 | updateReadOnlyMode(); |
| 545 | slideStart = Date.now(); |
| 546 | } |
| 547 | |
| 548 | function startPlayback( timestamp, finalMode, resized ) { |
| 549 | //console.log("playback " + timestamp ); |
| 550 | if ( resized == undefined ) { |
| 551 | updateReadOnlyMode(); |
| 552 | } |
| 553 | slideStart = Date.now() - timestamp; |
| 554 | closeChalkboard(); |
| 555 | mode = 0; |
| 556 | for ( var id = 0; id < 2; id++ ) { |
| 557 | clearCanvas( id ); |
| 558 | var slideData = getSlideData( slideIndices, id ); |
| 559 | //console.log( timestamp +" / " + JSON.stringify(slideData)); |
| 560 | var index = 0; |
| 561 | while ( index < slideData.events.length && slideData.events[index].begin < (Date.now() - slideStart) ) { |
| 562 | playEvent( id, slideData.events[index], timestamp ); |
| 563 | index++; |
| 564 | } |
| 565 | |
| 566 | while ( playback && index < slideData.events.length ) { |
| 567 | timeouts[id].push( setTimeout( playEvent, slideData.events[index].begin - (Date.now() - slideStart), id, slideData.events[index], timestamp ) ); |
| 568 | index++; |
| 569 | } |
| 570 | } |
| 571 | //console.log("Mode: " + finalMode + "/" + mode ); |
| 572 | if ( finalMode != undefined ) { |
| 573 | mode = finalMode; |
| 574 | } |
| 575 | if( mode == 1 ) showChalkboard(); |
| 576 | //console.log("playback (ok)"); |
| 577 | |
| 578 | }; |
| 579 | |
| 580 | function stopPlayback() { |
| 581 | //console.log("stopPlayback"); |
| 582 | //console.log("Timeouts: " + timeouts[0].length + "/"+ timeouts[1].length); |
| 583 | for ( var id = 0; id < 2; id++ ) { |
| 584 | for (var i = 0; i < timeouts[id].length; i++) { |
| 585 | clearTimeout(timeouts[id][i]); |
| 586 | } |
| 587 | timeouts[id] = []; |
| 588 | } |
| 589 | }; |
| 590 | |
| 591 | function playEvent( id, event, timestamp ) { |
| 592 | //console.log( timestamp +" / " + JSON.stringify(event)); |
| 593 | //console.log( id + ": " + timestamp +" / " + event.begin +" / " + event.type +" / " + mode ); |
| 594 | switch ( event.type ) { |
| 595 | case "open": |
| 596 | if ( timestamp <= event.begin ) { |
| 597 | showChalkboard(); |
| 598 | } |
| 599 | else { |
| 600 | mode = 1; |
| 601 | } |
| 602 | |
| 603 | break; |
| 604 | case "close": |
| 605 | if ( timestamp < event.begin ) { |
| 606 | closeChalkboard(); |
| 607 | } |
| 608 | else { |
| 609 | mode = 0; |
| 610 | } |
| 611 | break; |
| 612 | case "clear": |
| 613 | clearCanvas( id ); |
| 614 | break; |
| 615 | case "draw": |
| 616 | drawCurve( id, event, timestamp ); |
| 617 | break; |
| 618 | case "erase": |
| 619 | eraseCurve( id, event, timestamp ); |
| 620 | break; |
| 621 | |
| 622 | } |
| 623 | }; |
| 624 | |
| 625 | function drawCurve( id, event, timestamp ) { |
| 626 | if ( event.curve.length > 1 ) { |
| 627 | var ctx = drawingCanvas[id].context; |
| 628 | var scale = drawingCanvas[id].scale; |
| 629 | var xOffset = drawingCanvas[id].xOffset; |
| 630 | var yOffset = drawingCanvas[id].yOffset; |
| 631 | |
| 632 | var stepDuration = ( event.end - event.begin )/ ( event.curve.length - 1 ); |
| 633 | //console.log("---"); |
| 634 | for (var i = 1; i < event.curve.length; i++) { |
| 635 | if (event.begin + i * stepDuration <= (Date.now() - slideStart)) { |
| 636 | //console.log( "Draw " + timestamp +" / " + event.begin + " + " + i + " * " + stepDuration ); |
| 637 | draw[id](ctx, xOffset + event.curve[i-1].x*scale, yOffset + event.curve[i-1].y*scale, xOffset + event.curve[i].x*scale, yOffset + event.curve[i].y*scale); |
| 638 | } |
| 639 | else if ( playback ) { |
| 640 | //console.log( "Cue " + timestamp +" / " + (Date.now() - slideStart) +" / " + event.begin + " + " + i + " * " + stepDuration + " = " + Math.max(0,event.begin + i * stepDuration - timestamp) ); |
| 641 | timeouts.push( setTimeout( |
| 642 | draw[id], Math.max(0,event.begin + i * stepDuration - (Date.now() - slideStart)), ctx, |
| 643 | xOffset + event.curve[i-1].x*scale, |
| 644 | yOffset + event.curve[i-1].y*scale, |
| 645 | xOffset + event.curve[i].x*scale, |
| 646 | yOffset + event.curve[i].y*scale |
| 647 | ) |
| 648 | ); |
| 649 | } |
| 650 | } |
| 651 | } |
| 652 | |
| 653 | }; |
| 654 | |
| 655 | function eraseCurve( id, event, timestamp ) { |
| 656 | if ( event.curve.length > 1 ) { |
| 657 | var ctx = drawingCanvas[id].context; |
| 658 | var scale = drawingCanvas[id].scale; |
| 659 | var xOffset = drawingCanvas[id].xOffset; |
| 660 | var yOffset = drawingCanvas[id].yOffset; |
| 661 | |
| 662 | var stepDuration = ( event.end - event.begin )/ event.curve.length; |
| 663 | for (var i = 0; i < event.curve.length; i++) { |
| 664 | if (event.begin + i * stepDuration <= (Date.now() - slideStart)) { |
| 665 | erase(ctx, xOffset + event.curve[i].x*scale, yOffset + event.curve[i].y*scale); |
| 666 | } |
| 667 | else if ( playback ) { |
| 668 | timeouts.push( setTimeout( |
| 669 | erase, Math.max(0,event.begin + i * stepDuration - (Date.now() - slideStart)), ctx, |
| 670 | xOffset + event.curve[i].x * scale, |
| 671 | yOffset + event.curve[i].y * scale |
| 672 | ) |
| 673 | ); |
| 674 | } |
| 675 | } |
| 676 | } |
| 677 | |
| 678 | }; |
| 679 | |
| 680 | /***************************************************************** |
| 681 | ** User interface |
| 682 | ******************************************************************/ |
| 683 | |
| 684 | |
| 685 | // TODO: check all touchevents |
| 686 | document.addEventListener('touchstart', function(evt) { |
| 687 | if ( !readOnly && evt.target.getAttribute('data-chalkboard') == mode ) { |
| 688 | var ctx = drawingCanvas[mode].context; |
| 689 | var scale = drawingCanvas[mode].scale; |
| 690 | var xOffset = drawingCanvas[mode].xOffset; |
| 691 | var yOffset = drawingCanvas[mode].yOffset; |
| 692 | |
| 693 | evt.preventDefault(); |
| 694 | var touch = evt.touches[0]; |
| 695 | mouseX = touch.pageX; |
| 696 | mouseY = touch.pageY; |
| 697 | xLast = mouseX; |
| 698 | yLast = mouseY; |
| 699 | event = { type: "draw", begin: Date.now() - slideStart, end: null, curve: [{x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale}] }; |
| 700 | touchTimeout = setTimeout( showSponge, 500, mouseX, mouseY ); |
| 701 | } |
| 702 | }, false); |
| 703 | |
| 704 | document.addEventListener('touchmove', function(evt) { |
| 705 | clearTimeout( touchTimeout ); |
| 706 | touchTimeout = null; |
| 707 | if ( event ) { |
| 708 | var ctx = drawingCanvas[mode].context; |
| 709 | var scale = drawingCanvas[mode].scale; |
| 710 | var xOffset = drawingCanvas[mode].xOffset; |
| 711 | var yOffset = drawingCanvas[mode].yOffset; |
| 712 | |
| 713 | var touch = evt.touches[0]; |
| 714 | mouseX = touch.pageX; |
| 715 | mouseY = touch.pageY; |
| 716 | if (mouseY < drawingCanvas[mode].height && mouseX < drawingCanvas[mode].width) { |
| 717 | evt.preventDefault(); |
| 718 | event.curve.push({x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale}); |
| 719 | if ( event.type == "erase" ) { |
| 720 | drawingCanvas[mode].sponge.style.left = (mouseX - eraserDiameter) +"px" ; |
| 721 | drawingCanvas[mode].sponge.style.top = (mouseY - eraserDiameter) +"px" ; |
| 722 | erase(ctx, mouseX, mouseY); |
| 723 | } |
| 724 | else { |
| 725 | draw[mode](ctx, xLast, yLast, mouseX, mouseY); |
| 726 | } |
| 727 | xLast = mouseX; |
| 728 | yLast = mouseY; |
| 729 | } |
| 730 | } |
| 731 | }, false); |
| 732 | |
| 733 | document.addEventListener('touchend', function(evt) { |
| 734 | clearTimeout( touchTimeout ); |
| 735 | touchTimeout = null; |
| 736 | // hide sponge image |
| 737 | drawingCanvas[mode].sponge.style.visibility = "hidden"; |
| 738 | if ( event ) { |
| 739 | event.end = Date.now() - slideStart; |
| 740 | if ( event.type == "erase" || event.curve.length > 1 ) { |
| 741 | // do not save a line with a single point only |
| 742 | recordEvent( event ); |
| 743 | } |
| 744 | event = null; |
| 745 | } |
| 746 | }, false); |
| 747 | |
| 748 | function showSponge(x,y) { |
| 749 | if ( event ) { |
| 750 | event.type = "erase"; |
| 751 | event.begin = Date.now() - slideStart; |
| 752 | // show sponge image |
| 753 | drawingCanvas[mode].sponge.style.left = (x - eraserDiameter) +"px" ; |
| 754 | drawingCanvas[mode].sponge.style.top = (y - eraserDiameter) +"px" ; |
| 755 | drawingCanvas[mode].sponge.style.visibility = "visible"; |
| 756 | erase(drawingCanvas[mode].context,x,y); |
| 757 | } |
| 758 | } |
| 759 | |
| 760 | document.addEventListener( 'mousedown', function( evt ) { |
| 761 | //console.log( "Read only: " + readOnly ); |
| 762 | if ( !readOnly && evt.target.getAttribute('data-chalkboard') == mode ) { |
| 763 | //console.log( "mousedown: " + evt.target.getAttribute('data-chalkboard') ); |
| 764 | var ctx = drawingCanvas[mode].context; |
| 765 | var scale = drawingCanvas[mode].scale; |
| 766 | var xOffset = drawingCanvas[mode].xOffset; |
| 767 | var yOffset = drawingCanvas[mode].yOffset; |
| 768 | |
| 769 | mouseX = evt.pageX; |
| 770 | mouseY = evt.pageY; |
| 771 | xLast = mouseX; |
| 772 | yLast = mouseY; |
| 773 | if ( evt.button == 2) { |
| 774 | event = { type: "erase", begin: Date.now() - slideStart, end: null, curve: [{x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale}]}; |
| 775 | drawingCanvas[mode].canvas.style.cursor = 'url("' + path + 'img/sponge.png") ' + eraserDiameter + ' ' + eraserDiameter + ', auto'; |
| 776 | erase(ctx,mouseX,mouseY); |
| 777 | } |
| 778 | else { |
| 779 | event = { type: "draw", begin: Date.now() - slideStart, end: null, curve: [{x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale}] }; |
| 780 | } |
| 781 | } |
| 782 | } ); |
| 783 | |
| 784 | document.addEventListener( 'mousemove', function( evt ) { |
| 785 | if ( event ) { |
| 786 | var ctx = drawingCanvas[mode].context; |
| 787 | var scale = drawingCanvas[mode].scale; |
| 788 | var xOffset = drawingCanvas[mode].xOffset; |
| 789 | var yOffset = drawingCanvas[mode].yOffset; |
| 790 | |
| 791 | mouseX = evt.pageX; |
| 792 | mouseY = evt.pageY; |
| 793 | event.curve.push({x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale}); |
| 794 | if(mouseY < drawingCanvas[mode].height && mouseX < drawingCanvas[mode].width) { |
| 795 | if ( event.type == "erase" ) { |
| 796 | erase(ctx,mouseX,mouseY); |
| 797 | } |
| 798 | else { |
| 799 | draw[mode](ctx, xLast, yLast, mouseX,mouseY); |
| 800 | } |
| 801 | xLast = mouseX; |
| 802 | yLast = mouseY; |
| 803 | } |
| 804 | } |
| 805 | } ); |
| 806 | |
| 807 | document.addEventListener( 'mouseup', function( evt ) { |
| 808 | drawingCanvas[mode].canvas.style.cursor = pen[mode]; |
| 809 | if ( event ) { |
| 810 | if(evt.button == 2){ |
| 811 | } |
| 812 | event.end = Date.now() - slideStart; |
| 813 | if ( event.type == "erase" || event.curve.length > 1 ) { |
| 814 | // do not save a line with a single point only |
| 815 | recordEvent( event ); |
| 816 | } |
| 817 | event = null; |
| 818 | } |
| 819 | } ); |
| 820 | |
| 821 | window.addEventListener( "resize", function() { |
| 822 | //console.log("resize"); |
| 823 | // Resize the canvas and draw everything again |
| 824 | var timestamp = Date.now() - slideStart; |
| 825 | if ( !playback ) { |
| 826 | timestamp = getSlideDuration(); |
| 827 | } |
| 828 | |
| 829 | //console.log( drawingCanvas[0].scale + "/" + drawingCanvas[0].xOffset + "/" +drawingCanvas[0].yOffset ); |
| 830 | for (var id = 0; id < 2; id++ ) { |
| 831 | drawingCanvas[id].width = window.innerWidth; |
| 832 | drawingCanvas[id].height = window.innerHeight; |
| 833 | drawingCanvas[id].canvas.width = drawingCanvas[id].width; |
| 834 | drawingCanvas[id].canvas.height = drawingCanvas[id].height; |
| 835 | drawingCanvas[id].context.canvas.width = drawingCanvas[id].width; |
| 836 | drawingCanvas[id].context.canvas.height = drawingCanvas[id].height; |
| 837 | |
| 838 | drawingCanvas[id].scale = Math.min( drawingCanvas[id].width/storage[id].width, drawingCanvas[id].height/storage[id].height ); |
| 839 | drawingCanvas[id].xOffset = (drawingCanvas[id].width - storage[id].width * drawingCanvas[id].scale)/2; |
| 840 | drawingCanvas[id].yOffset = (drawingCanvas[id].height - storage[id].height * drawingCanvas[id].scale)/2; |
| 841 | //console.log( drawingCanvas[id].scale + "/" + drawingCanvas[id].xOffset + "/" +drawingCanvas[id].yOffset ); |
| 842 | } |
| 843 | //console.log( window.innerWidth + "/" + window.innerHeight); |
| 844 | startPlayback( timestamp, mode, true ); |
| 845 | |
| 846 | } ); |
| 847 | |
| 848 | function updateReadOnlyMode() { |
| 849 | //console.log("updateReadOnlyMode"); |
| 850 | if ( config.readOnly == undefined ) { |
| 851 | readOnly = ( getSlideDuration() > 0 ); |
| 852 | if ( readOnly ) { |
| 853 | drawingCanvas[0].container.style.cursor = 'default'; |
| 854 | drawingCanvas[1].container.style.cursor = 'default'; |
| 855 | drawingCanvas[0].canvas.style.cursor = 'default'; |
| 856 | drawingCanvas[1].canvas.style.cursor = 'default'; |
| 857 | if ( notescanvas.style.pointerEvents != "none" ) { |
| 858 | event = null; |
| 859 | notescanvas.style.background = 'rgba(0,0,0,0)'; |
| 860 | notescanvas.style.pointerEvents = "none"; |
| 861 | } |
| 862 | |
| 863 | } |
| 864 | else { |
| 865 | drawingCanvas[0].container.style.cursor = pen[0]; |
| 866 | drawingCanvas[1].container.style.cursor = pen[1]; |
| 867 | drawingCanvas[0].canvas.style.cursor = pen[0]; |
| 868 | drawingCanvas[1].canvas.style.cursor = pen[1]; |
| 869 | } |
| 870 | } |
| 871 | } |
| 872 | |
| 873 | Reveal.addEventListener( 'ready', function( evt ) { |
| 874 | //console.log('ready'); |
| 875 | if ( !printMode ) { |
| 876 | slideStart = Date.now(); |
| 877 | slideIndices = Reveal.getIndices(); |
| 878 | if ( !playback ) { |
| 879 | startPlayback( getSlideDuration(), 0 ); |
| 880 | } |
| 881 | if ( Reveal.isAutoSliding() ) { |
| 882 | var event = new CustomEvent('startplayback'); |
| 883 | event.timestamp = 0; |
| 884 | document.dispatchEvent( event ); |
| 885 | } |
| 886 | updateReadOnlyMode(); |
| 887 | } |
| 888 | else { |
| 889 | createPrintout(); |
| 890 | } |
| 891 | }); |
| 892 | Reveal.addEventListener( 'slidechanged', function( evt ) { |
| 893 | // clearTimeout( slidechangeTimeout ); |
| 894 | //console.log('slidechanged'); |
| 895 | if ( !printMode ) { |
| 896 | slideStart = Date.now(); |
| 897 | slideIndices = Reveal.getIndices(); |
| 898 | closeChalkboard(); |
| 899 | clearCanvas( 0 ); |
| 900 | clearCanvas( 1 ); |
| 901 | if ( !playback ) { |
| 902 | slidechangeTimeout = setTimeout( startPlayback, transition, getSlideDuration(), 0 ); |
| 903 | } |
| 904 | if ( Reveal.isAutoSliding() ) { |
| 905 | var event = new CustomEvent('startplayback'); |
| 906 | event.timestamp = 0; |
| 907 | document.dispatchEvent( event ); |
| 908 | } |
| 909 | |
| 910 | updateReadOnlyMode(); |
| 911 | } |
| 912 | }); |
| 913 | Reveal.addEventListener( 'fragmentshown', function( evt ) { |
| 914 | // clearTimeout( slidechangeTimeout ); |
| 915 | //console.log('fragmentshown'); |
| 916 | if ( !printMode ) { |
| 917 | slideStart = Date.now(); |
| 918 | slideIndices = Reveal.getIndices(); |
| 919 | closeChalkboard(); |
| 920 | clearCanvas( 0 ); |
| 921 | clearCanvas( 1 ); |
| 922 | if ( Reveal.isAutoSliding() ) { |
| 923 | var event = new CustomEvent('startplayback'); |
| 924 | event.timestamp = 0; |
| 925 | document.dispatchEvent( event ); |
| 926 | } |
| 927 | else if ( !playback ) { |
| 928 | // |
| 929 | startPlayback( getSlideDuration(), 0 ); |
| 930 | // closeChalkboard(); |
| 931 | } |
| 932 | updateReadOnlyMode(); |
| 933 | } |
| 934 | }); |
| 935 | Reveal.addEventListener( 'fragmenthidden', function( evt ) { |
| 936 | // clearTimeout( slidechangeTimeout ); |
| 937 | //console.log('fragmenthidden'); |
| 938 | if ( !printMode ) { |
| 939 | slideStart = Date.now(); |
| 940 | slideIndices = Reveal.getIndices(); |
| 941 | closeChalkboard(); |
| 942 | clearCanvas( 0 ); |
| 943 | clearCanvas( 1 ); |
| 944 | if ( Reveal.isAutoSliding() ) { |
| 945 | document.dispatchEvent( new CustomEvent('stopplayback') ); |
| 946 | } |
| 947 | else if ( !playback ) { |
| 948 | startPlayback( getSlideDuration() ); |
| 949 | closeChalkboard(); |
| 950 | } |
| 951 | updateReadOnlyMode(); |
| 952 | } |
| 953 | }); |
| 954 | |
| 955 | Reveal.addEventListener( 'autoslideresumed', function( evt ) { |
| 956 | //console.log('autoslideresumed'); |
| 957 | var event = new CustomEvent('startplayback'); |
| 958 | event.timestamp = 0; |
| 959 | document.dispatchEvent( event ); |
| 960 | }); |
| 961 | Reveal.addEventListener( 'autoslidepaused', function( evt ) { |
| 962 | //console.log('autoslidepaused'); |
| 963 | document.dispatchEvent( new CustomEvent('stopplayback') ); |
| 964 | |
| 965 | // advance to end of slide |
| 966 | // closeChalkboard(); |
| 967 | startPlayback( getSlideDuration(), 0 ); |
| 968 | }); |
| 969 | |
| 970 | function toggleNotesCanvas() { |
| 971 | if ( !readOnly ) { |
| 972 | if ( mode == 1 ) { |
| 973 | toggleChalkboard(); |
| 974 | notescanvas.style.background = background[0]; //'rgba(255,0,0,0.5)'; |
| 975 | notescanvas.style.pointerEvents = "auto"; |
| 976 | } |
| 977 | else { |
| 978 | if ( notescanvas.style.pointerEvents != "none" ) { |
| 979 | event = null; |
| 980 | notescanvas.style.background = 'rgba(0,0,0,0)'; |
| 981 | notescanvas.style.pointerEvents = "none"; |
| 982 | } |
| 983 | else { |
| 984 | notescanvas.style.background = background[0]; //'rgba(255,0,0,0.5)'; |
| 985 | notescanvas.style.pointerEvents = "auto"; |
| 986 | } |
| 987 | } |
| 988 | } |
| 989 | }; |
| 990 | |
| 991 | function toggleChalkboard() { |
| 992 | //console.log("toggleChalkboard " + mode); |
| 993 | if ( mode == 1 ) { |
| 994 | event = null; |
| 995 | if ( !readOnly ) recordEvent( { type:"close", begin: Date.now() - slideStart } ); |
| 996 | closeChalkboard(); |
| 997 | } |
| 998 | else { |
| 999 | showChalkboard(); |
| 1000 | if ( !readOnly ) recordEvent( { type:"open", begin: Date.now() - slideStart } ); |
| 1001 | } |
| 1002 | }; |
| 1003 | |
| 1004 | function clear() { |
| 1005 | if ( !readOnly ) { |
| 1006 | recordEvent( { type:"clear", begin: Date.now() - slideStart } ); |
| 1007 | clearCanvas( mode ); |
| 1008 | } |
| 1009 | }; |
| 1010 | |
| 1011 | function resetSlide( force ) { |
| 1012 | var ok = force || confirm("Please confirm to delete chalkboard drawings on this slide!"); |
| 1013 | if ( ok ) { |
| 1014 | //console.log("resetSlide "); |
| 1015 | stopPlayback(); |
| 1016 | slideStart = Date.now(); |
| 1017 | event = null; |
| 1018 | closeChalkboard(); |
| 1019 | |
| 1020 | clearCanvas( 0 ); |
| 1021 | clearCanvas( 1 ); |
| 1022 | |
| 1023 | mode = 1; |
| 1024 | var slideData = getSlideData(); |
| 1025 | slideData.duration = 0; |
| 1026 | slideData.events = []; |
| 1027 | mode = 0; |
| 1028 | var slideData = getSlideData(); |
| 1029 | slideData.duration = 0; |
| 1030 | slideData.events = []; |
| 1031 | |
| 1032 | updateReadOnlyMode(); |
| 1033 | } |
| 1034 | }; |
| 1035 | |
| 1036 | function resetStorage( force ) { |
| 1037 | var ok = force || confirm("Please confirm to delete all chalkboard drawings!"); |
| 1038 | if ( ok ) { |
| 1039 | stopPlayback(); |
| 1040 | slideStart = Date.now(); |
| 1041 | clearCanvas( 0 ); |
| 1042 | clearCanvas( 1 ); |
| 1043 | if ( mode == 1 ) { |
| 1044 | event = null; |
| 1045 | closeChalkboard(); |
| 1046 | } |
| 1047 | storage = [ |
| 1048 | { width: drawingCanvas[0].width - 2 * drawingCanvas[0].xOffset, height: drawingCanvas[0].height - 2 * drawingCanvas[0].yOffset, data: []}, |
| 1049 | { width: drawingCanvas[1].width, height: drawingCanvas[1].height, data: []} |
| 1050 | ]; |
| 1051 | |
| 1052 | updateReadOnlyMode(); |
| 1053 | } |
| 1054 | }; |
| 1055 | |
| 1056 | this.drawWithPen = drawWithPen; |
| 1057 | this.drawWithChalk = drawWithChalk; |
| 1058 | this.toggleNotesCanvas = toggleNotesCanvas; |
| 1059 | this.toggleChalkboard = toggleChalkboard; |
| 1060 | this.startRecording = startRecording; |
| 1061 | this.clear = clear; |
| 1062 | this.reset = resetSlide; |
| 1063 | this.resetAll = resetStorage; |
| 1064 | this.download = downloadData; |
| 1065 | |
| 1066 | return this; |
| 1067 | })(); |