+
+ |
+ /***************************************************************** |
+
+
+ |
+ ** Author: Asvin Goel, goel@telematique.eu |
+
+
+ |
+ ** |
+
+
+ |
+ ** A plugin for reveal.js adding a chalkboard. |
+
+
+ |
+ ** |
+
+
+ |
+ ** Version: 0.5 |
+
+
+ |
+ ** |
+
+
+ |
+ ** License: MIT license (see LICENSE.md) |
+
+
+ |
+ ** |
+
+
+ |
+ ** Credits: |
+
+
+ |
+ ** Chalkboard effect by Mohamed Moustafa https://github.com/mmoustafa/Chalkboard |
+
+
+ |
+ ******************************************************************/ |
+
+
+ |
+
+ |
+
+
+ |
+ var RevealChalkboard = window.RevealChalkboard || (function(){ |
+
+
+ |
+ var path = scriptPath(); |
+
+
+ |
+ function scriptPath() { |
+
+
+ |
+ // obtain plugin path from the script element |
+
+
+ |
+ var src; |
+
+
+ |
+ if (document.currentScript) { |
+
+
+ |
+ src = document.currentScript.src; |
+
+
+ |
+ } else { |
+
+
+ |
+ var sel = document.querySelector('script[src$="/chalkboard.js"]') |
+
+
+ |
+ if (sel) { |
+
+
+ |
+ src = sel.src; |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ var path = typeof src === undefined ? src |
+
+
+ |
+ : src.slice(0, src.lastIndexOf("/") + 1); |
+
+
+ |
+ //console.log("Path: " + path); |
+
+
+ |
+ return path; |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ /***************************************************************** |
+
+
+ |
+ ** Configuration |
+
+
+ |
+ ******************************************************************/ |
+
+
+ |
+ var config = Reveal.getConfig().chalkboard || {}; |
+
+
+ |
+
+ |
+
+
+ |
+ var background, pen, draw, color; |
+
+
+ |
+ var theme = config.theme || "chalkboard"; |
+
+
+ |
+ switch ( theme ) { |
+
+
+ |
+ case "whiteboard": |
+
+
+ |
+ background = [ 'rgba(127,127,127,.1)' , path + 'img/whiteboard.png' ]; |
+
+
+ |
+ pen = [ 'url(' + path + 'img/boardmarker.png), auto', |
+
+
+ |
+ 'url(' + path + 'img/boardmarker.png), auto' ]; |
+
+
+ |
+ draw = [ drawWithPen , drawWithPen ]; |
+
+
+ |
+ color = [ 'rgba(0,0,255,1)', 'rgba(0,0,255,1)' ]; |
+
+
+ |
+ break; |
+
+
+ |
+ default: |
+
+
+ |
+ background = [ 'rgba(127,127,127,.1)' , path + 'img/blackboard.png' ]; |
+
+
+ |
+ pen = [ 'url(' + path + 'img/boardmarker.png), auto', |
+
+
+ |
+ 'url(' + path + 'img/chalk.png), auto' ]; |
+
+
+ |
+ draw = [ drawWithPen , drawWithChalk ]; |
+
+
+ |
+ color = [ 'rgba(0,0,255,1)', 'rgba(255,255,255,0.5)' ]; |
+
+
+ |
+ } |
+
+
+ |
+ |
+
+
+ |
+ if ( config.background ) background = config.background; |
+
+
+ |
+ if ( config.pen ) pen = config.pen; |
+
+
+ |
+ if ( config.draw ) draw = config.draw; |
+
+
+ |
+ if ( config.color ) color = config.color; |
+
+
+ |
+
+ |
+
+
+ |
+ var toggleChalkboardButton = config.toggleChalkboardButton == undefined ? true : config.toggleChalkboardButton; |
+
+
+ |
+ var toggleNotesButton = config.toggleNotesButton == undefined ? true : config.toggleNotesButton; |
+
+
+ |
+ var transition = config.transition || 800; |
+
+
+ |
+
+ |
+
+
+ |
+ var readOnly = config.readOnly; |
+
+
+ |
+
+ |
+
+
+ |
+ var legacyFileSupport = config.legacyFileSupport; |
+
+
+ |
+ if ( legacyFileSupport ) { console.warn("Legacy file support is deprecated and may be removed in future versions!") } |
+
+
+ |
+
+ |
+
+
+ |
+ /***************************************************************** |
+
+
+ |
+ ** Setup |
+
+
+ |
+ ******************************************************************/ |
+
+
+ |
+
+ |
+
+
+ |
+ var eraserDiameter = 20; |
+
+
+ |
+
+ |
+
+
+ |
+ if ( toggleChalkboardButton ) { |
+
+
+ |
+ //console.log("toggleChalkboardButton") |
+
+
+ |
+ var button = document.createElement( 'div' ); |
+
+
+ |
+ button.className = "chalkboard-button"; |
+
+
+ |
+ button.id = "toggle-chalkboard"; |
+
+
+ |
+ button.style.vivibility = "visible"; |
+
+
+ |
+ button.style.position = "absolute"; |
+
+
+ |
+ button.style.zIndex = 30; |
+
+
+ |
+ button.style.fontSize = "24px"; |
+
+
+ |
+
+ |
+
+
+ |
+ button.style.left = toggleChalkboardButton.left || "30px"; |
+
+
+ |
+ button.style.bottom = toggleChalkboardButton.bottom || "30px"; |
+
+
+ |
+ button.style.top = toggleChalkboardButton.top || "auto"; |
+
+
+ |
+ button.style.right = toggleChalkboardButton.right || "auto"; |
+
+
+ |
+
+ |
+
+
+ |
+ button.innerHTML = '<a href="#" onclick="RevealChalkboard.toggleChalkboard(); return false;"><i class="fa fa-pencil-square-o"></i></a>' |
+
+
+ |
+ document.querySelector(".reveal").appendChild( button ); |
+
+
+ |
+ } |
+
+
+ |
+ if ( toggleNotesButton ) { |
+
+
+ |
+ //console.log("toggleNotesButton") |
+
+
+ |
+ var button = document.createElement( 'div' ); |
+
+
+ |
+ button.className = "chalkboard-button"; |
+
+
+ |
+ button.id = "toggle-notes"; |
+
+
+ |
+ button.style.position = "absolute"; |
+
+
+ |
+ button.style.zIndex = 30; |
+
+
+ |
+ button.style.fontSize = "24px"; |
+
+
+ |
+
+ |
+
+
+ |
+ button.style.left = toggleNotesButton.left || "70px"; |
+
+
+ |
+ button.style.bottom = toggleNotesButton.bottom || "30px"; |
+
+
+ |
+ button.style.top = toggleNotesButton.top || "auto"; |
+
+
+ |
+ button.style.right = toggleNotesButton.right || "auto"; |
+
+
+ |
+
+ |
+
+
+ |
+ button.innerHTML = '<a href="#" onclick="RevealChalkboard.toggleNotesCanvas(); return false;"><i class="fa fa-pencil"></i></a>' |
+
+
+ |
+ document.querySelector(".reveal").appendChild( button ); |
+
+
+ |
+ } |
+
+
+ |
+ //alert("Buttons"); |
+
+
+ |
+
+ |
+
+
+ |
+ var drawingCanvas = [ {id: "notescanvas" }, {id: "chalkboard" } ]; |
+
+
+ |
+ setupDrawingCanvas(0); |
+
+
+ |
+ setupDrawingCanvas(1); |
+
+
+ |
+
+ |
+
+
+ |
+ var mode = 0; // 0: notes canvas, 1: chalkboard |
+
+
+ |
+
+ |
+
+
+ |
+ var mouseX = 0; |
+
+
+ |
+ var mouseY = 0; |
+
+
+ |
+ var xLast = null; |
+
+
+ |
+ var yLast = null; |
+
+
+ |
+
+ |
+
+
+ |
+ var slideStart = Date.now(); |
+
+
+ |
+ var slideIndices = { h:0, v:0 }; |
+
+
+ |
+ var event = null; |
+
+
+ |
+ var timeouts = [ [], [] ]; |
+
+
+ |
+ var touchTimeout = null; |
+
+
+ |
+ var slidechangeTimeout = null; |
+
+
+ |
+ var playback = false; |
+
+
+ |
+
+ |
+
+
+ |
+ function setupDrawingCanvas( id ) { |
+
+
+ |
+ var container = document.createElement( 'div' ); |
+
+
+ |
+ container.id = drawingCanvas[id].id; |
+
+
+ |
+ container.classList.add( 'overlay' ); |
+
+
+ |
+ container.setAttribute( 'data-prevent-swipe', '' ); |
+
+
+ |
+ container.oncontextmenu = function() { return false; } |
+
+
+ |
+ container.style.cursor = pen[ id ]; |
+
+
+ |
+
+ |
+
+
+ |
+ drawingCanvas[id].width = window.innerWidth; |
+
+
+ |
+ drawingCanvas[id].height = window.innerHeight; |
+
+
+ |
+ drawingCanvas[id].scale = 1; |
+
+
+ |
+ drawingCanvas[id].xOffset = 0; |
+
+
+ |
+ drawingCanvas[id].yOffset = 0; |
+
+
+ |
+
+ |
+
+
+ |
+
+ |
+
+
+ |
+ if ( id == "0" ) { |
+
+
+ |
+ container.style.background = 'rgba(0,0,0,0)'; |
+
+
+ |
+ container.style.zIndex = "24"; |
+
+
+ |
+ container.classList.add( 'visible' ) |
+
+
+ |
+ container.style.pointerEvents = "none"; |
+
+
+ |
+
+ |
+
+
+ |
+ var slides = document.querySelector(".slides"); |
+
+
+ |
+ var aspectRatio = Reveal.getConfig().width / Reveal.getConfig().height; |
+
+
+ |
+ if ( drawingCanvas[id].width > drawingCanvas[id].height*aspectRatio ) { |
+
+
+ |
+ drawingCanvas[id].xOffset = (drawingCanvas[id].width - drawingCanvas[id].height*aspectRatio) / 2; |
+
+
+ |
+ } |
+
+
+ |
+ else if ( drawingCanvas[id].height > drawingCanvas[id].width/aspectRatio ) { |
+
+
+ |
+ drawingCanvas[id].yOffset = ( drawingCanvas[id].height - drawingCanvas[id].width/aspectRatio ) / 2; |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ else { |
+
+
+ |
+ container.style.background = 'url("' + background[id] + '") repeat'; |
+
+
+ |
+ container.style.zIndex = "26"; |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ var sponge = document.createElement( 'img' ); |
+
+
+ |
+ sponge.src = path + 'img/sponge.png'; |
+
+
+ |
+ sponge.id = "sponge"; |
+
+
+ |
+ sponge.style.visibility = "hidden"; |
+
+
+ |
+ sponge.style.position = "absolute"; |
+
+
+ |
+ container.appendChild( sponge ); |
+
+
+ |
+ drawingCanvas[id].sponge = sponge; |
+
+
+ |
+
+ |
+
+
+ |
+ var canvas = document.createElement( 'canvas' ); |
+
+
+ |
+ canvas.width = drawingCanvas[id].width; |
+
+
+ |
+ canvas.height = drawingCanvas[id].height; |
+
+
+ |
+ canvas.setAttribute( 'data-chalkboard', id ); |
+
+
+ |
+ canvas.style.cursor = pen[ id ]; |
+
+
+ |
+ container.appendChild( canvas ); |
+
+
+ |
+ drawingCanvas[id].canvas = canvas; |
+
+
+ |
+
+ |
+
+
+ |
+ drawingCanvas[id].context = canvas.getContext("2d"); |
+
+
+ |
+
+ |
+
+
+ |
+
+ |
+
+
+ |
+ document.querySelector( '.reveal' ).appendChild( container ); |
+
+
+ |
+ drawingCanvas[id].container = container; |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+
+ |
+
+
+ |
+ /***************************************************************** |
+
+
+ |
+ ** Storage |
+
+
+ |
+ ******************************************************************/ |
+
+
+ |
+ var storage = [ |
+
+
+ |
+ { width: drawingCanvas[0].width - 2 * drawingCanvas[0].xOffset, height: drawingCanvas[0].height - 2 * drawingCanvas[0].yOffset, data: []}, |
+
+
+ |
+ { width: drawingCanvas[1].width, height: drawingCanvas[1].height, data: []} |
+
+
+ |
+ ]; |
+
+
+ |
+ //console.log( JSON.stringify(storage)); |
+
+
+ |
+
+ |
+
+
+ |
+ if ( config.src != null ) { |
+
+
+ |
+ loadData( config.src ); |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+
+ |
+
+
+ |
+ /** |
+
+
+ |
+ * Load data. |
+
+
+ |
+ */ |
+
+
+ |
+ function loadData( filename ) { |
+
+
+ |
+ var xhr = new XMLHttpRequest(); |
+
+
+ |
+ xhr.onload = function() { |
+
+
+ |
+ if (xhr.readyState === 4) { |
+
+
+ |
+ storage = JSON.parse(xhr.responseText); |
+
+
+ |
+ for (var id = 0; id < storage.length; id++) { |
+
+
+ |
+ if ( drawingCanvas[id].width != storage[id].width || drawingCanvas[id].height != storage[id].height ) { |
+
+
+ |
+ drawingCanvas[id].scale = Math.min( drawingCanvas[id].width/storage[id].width, drawingCanvas[id].height/storage[id].height); |
+
+
+ |
+ drawingCanvas[id].xOffset = (drawingCanvas[id].width - storage[id].width * drawingCanvas[id].scale)/2; |
+
+
+ |
+ drawingCanvas[id].yOffset = (drawingCanvas[id].height - storage[id].height * drawingCanvas[id].scale)/2; |
+
+
+ |
+ } |
+
+
+ |
+ if ( config.readOnly ) { |
+
+
+ |
+ drawingCanvas[id].container.style.cursor = 'default'; |
+
+
+ |
+ drawingCanvas[id].canvas.style.cursor = 'default'; |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ //console.log("Drawings loaded"); |
+
+
+ |
+ } |
+
+
+ |
+ else { |
+
+
+ |
+ config.readOnly = undefined; |
+
+
+ |
+ readOnly = undefined; |
+
+
+ |
+ console.warn( 'Failed to get file ' + filename +". ReadyState: " + xhr.readyState + ", Status: " + xhr.status); |
+
+
+ |
+ } |
+
+
+ |
+ }; |
+
+
+ |
+
+ |
+
+
+ |
+ xhr.open( 'GET', filename, true ); |
+
+
+ |
+ try { |
+
+
+ |
+ xhr.send(); |
+
+
+ |
+ } |
+
+
+ |
+ catch ( error ) { |
+
+
+ |
+ config.readOnly = undefined; |
+
+
+ |
+ readOnly = undefined; |
+
+
+ |
+ 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 ); |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ /** |
+
+
+ |
+ * Download data. |
+
+
+ |
+ */ |
+
+
+ |
+ function downloadData() { |
+
+
+ |
+ var a = document.createElement('a'); |
+
+
+ |
+ document.body.appendChild(a); |
+
+
+ |
+ try { |
+
+
+ |
+ // cleanup slide data without events |
+
+
+ |
+ for (var id = 0; id < 2; id++) { |
+
+
+ |
+ for (var i = storage[id].data.length-1; i >= 0; i--) { |
+
+
+ |
+ if (storage[id].data[i].events.length == 0) { |
+
+
+ |
+ storage[id].data.splice(i, 1); |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ a.download = "chalkboard.json"; |
+
+
+ |
+ var blob = new Blob( [ JSON.stringify( storage ) ], { type: "application/json"} ); |
+
+
+ |
+ a.href = window.URL.createObjectURL( blob ); |
+
+
+ |
+ } catch( error ) { |
+
+
+ |
+ a.innerHTML += " (" + error + ")"; |
+
+
+ |
+ } |
+
+
+ |
+ a.click(); |
+
+
+ |
+ document.body.removeChild(a); |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ /** |
+
+
+ |
+ * Returns data object for the slide with the given indices. |
+
+
+ |
+ */ |
+
+
+ |
+ function getSlideData( indices, id ) { |
+
+
+ |
+ if ( id == undefined ) id = mode; |
+
+
+ |
+ if (!indices) indices = slideIndices; |
+
+
+ |
+ var data; |
+
+
+ |
+ for (var i = 0; i < storage[id].data.length; i++) { |
+
+
+ |
+ 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 ) { |
+
+
+ |
+ data = storage[id].data[i]; |
+
+
+ |
+ return data; |
+
+
+ |
+ } |
+
+
+ |
+ if ( !legacyFileSupport && |
+
+
+ |
+ ( storage[id].data[i].slide.h > indices.h || |
+
+
+ |
+ ( storage[id].data[i].slide.h === indices.h && storage[id].data[i].slide.v > indices.v ) || |
+
+
+ |
+ ( storage[id].data[i].slide.h === indices.h && storage[id].data[i].slide.v === indices.v && storage[id].data[i].slide.f > indices.f ) |
+
+
+ |
+ ) |
+
+
+ |
+ ) { |
+
+
+ |
+ storage[id].data.splice( i, 0, { slide: indices, events: [], duration: 0 } ); |
+
+
+ |
+ data = storage[id].data[i]; |
+
+
+ |
+ return data; |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ storage[id].data.push( { slide: indices, events: [], duration: 0 } ); |
+
+
+ |
+ data = storage[id].data[storage[id].data.length-1]; |
+
+
+ |
+ return data; |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ /** |
+
+
+ |
+ * Returns maximum duration of slide playback for both modes |
+
+
+ |
+ */ |
+
+
+ |
+ function getSlideDuration( indices ) { |
+
+
+ |
+ if (!indices) indices = slideIndices; |
+
+
+ |
+ var duration = 0; |
+
+
+ |
+ for (var id = 0; id < 2; id++) { |
+
+
+ |
+ for (var i = 0; i < storage[id].data.length; i++) { |
+
+
+ |
+ 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 ) { |
+
+
+ |
+ duration = Math.max( duration, storage[id].data[i].duration ); |
+
+
+ |
+ break; |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ //console.log( duration ); |
+
+
+ |
+ return duration; |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ /***************************************************************** |
+
+
+ |
+ ** Print |
+
+
+ |
+ ******************************************************************/ |
+
+
+ |
+ var printMode = ( /print-pdf/gi ).test( window.location.search ); |
+
+
+ |
+ //console.log("createPrintout" + printMode) |
+
+
+ |
+
+ |
+
+
+ |
+ function createPrintout( ) { |
+
+
+ |
+ //console.log( 'Create printout for ' + storage[1].data.length + " slides"); |
+
+
+ |
+ drawingCanvas[0].container.classList.remove( 'visible' ); // do not print notes canvas |
+
+
+ |
+
+ |
+
+
+ |
+ var patImg = new Image(); |
+
+
+ |
+ patImg.onload = function () { |
+
+
+ |
+ var nextSlide = []; |
+
+
+ |
+ var width = Reveal.getConfig().width; |
+
+
+ |
+ var height = Reveal.getConfig().height; |
+
+
+ |
+ var scale = 1; |
+
+
+ |
+ var xOffset = 0; |
+
+
+ |
+ var yOffset = 0; |
+
+
+ |
+ if ( width != storage[1].width || height != storage[1].height ) { |
+
+
+ |
+ scale = Math.min( width/storage[1].width, height/storage[1].height); |
+
+
+ |
+ xOffset = (width - storage[1].width * scale)/2; |
+
+
+ |
+ yOffset = (height - storage[1].height * scale)/2; |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ for (var i = 0; i < storage[1].data.length; i++) { |
+
+
+ |
+ var slide = Reveal.getSlide( storage[1].data[i].slide.h, storage[1].data[i].slide.v ); |
+
+
+ |
+ nextSlide.push( slide.nextSibling ); |
+
+
+ |
+ } |
+
+
+ |
+ for (var i = 0; i < storage[1].data.length; i++) { |
+
+
+ |
+ var slideData = getSlideData( storage[1].data[i].slide, 1 ); |
+
+
+ |
+
+ |
+
+
+ |
+ var imgCanvas = document.createElement('canvas'); |
+
+
+ |
+ imgCanvas.width = width; |
+
+
+ |
+ imgCanvas.height = height; |
+
+
+ |
+
+ |
+
+
+ |
+ var imgCtx = imgCanvas.getContext("2d"); |
+
+
+ |
+ imgCtx.fillStyle = imgCtx.createPattern( patImg ,'repeat'); |
+
+
+ |
+ imgCtx.rect(0,0,imgCanvas.width,imgCanvas.height); |
+
+
+ |
+ imgCtx.fill(); |
+
+
+ |
+
+ |
+
+
+ |
+ for (var j = 0; j < slideData.events.length; j++) { |
+
+
+ |
+ switch ( slideData.events[j].type ) { |
+
+
+ |
+ case "draw": |
+
+
+ |
+ for (var k = 1; k < slideData.events[j].curve.length; k++) { |
+
+
+ |
+ draw[1]( imgCtx, |
+
+
+ |
+ xOffset + slideData.events[j].curve[k-1].x*scale, |
+
+
+ |
+ yOffset + slideData.events[j].curve[k-1].y*scale, |
+
+
+ |
+ xOffset + slideData.events[j].curve[k].x*scale, |
+
+
+ |
+ yOffset + slideData.events[j].curve[k].y*scale |
+
+
+ |
+ ); |
+
+
+ |
+ } |
+
+
+ |
+ break; |
+
+
+ |
+ case "erase": |
+
+
+ |
+ for (var k = 0; k < slideData.events[j].curve.length; k++) { |
+
+
+ |
+ erase( imgCtx, |
+
+
+ |
+ xOffset + slideData.events[j].curve[k].x*scale, |
+
+
+ |
+ yOffset + slideData.events[j].curve[k].y*scale |
+
+
+ |
+ ); |
+
+
+ |
+ } |
+
+
+ |
+ break; |
+
+
+ |
+ case "clear": |
+
+
+ |
+ addPrintout( nextSlide[i], imgCanvas, patImg ); |
+
+
+ |
+ imgCtx.clearRect(0,0,imgCanvas.width,imgCanvas.height); |
+
+
+ |
+ imgCtx.fill(); |
+
+
+ |
+ break; |
+
+
+ |
+ default: |
+
+
+ |
+ break; |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ if ( slideData.events.length ) { |
+
+
+ |
+ addPrintout( nextSlide[i], imgCanvas, patImg ); |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ Reveal.sync(); |
+
+
+ |
+ }; |
+
+
+ |
+ patImg.src = background[1]; |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ function addPrintout( nextSlide, imgCanvas, patImg ) { |
+
+
+ |
+ var slideCanvas = document.createElement('canvas'); |
+
+
+ |
+ slideCanvas.width = Reveal.getConfig().width; |
+
+
+ |
+ slideCanvas.height = Reveal.getConfig().height; |
+
+
+ |
+ var ctx = slideCanvas.getContext("2d"); |
+
+
+ |
+ ctx.fillStyle = ctx.createPattern( patImg ,'repeat'); |
+
+
+ |
+ ctx.rect(0,0,slideCanvas.width,slideCanvas.height); |
+
+
+ |
+ ctx.fill(); |
+
+
+ |
+ ctx.drawImage(imgCanvas, 0, 0); |
+
+
+ |
+
+ |
+
+
+ |
+ var newSlide = document.createElement( 'section' ); |
+
+
+ |
+ newSlide.classList.add( 'present' ); |
+
+
+ |
+ newSlide.innerHTML = '<h1 style="visibility:hidden">Drawing</h1>'; |
+
+
+ |
+ newSlide.setAttribute("data-background-size", '100% 100%' ); |
+
+
+ |
+ newSlide.setAttribute("data-background-repeat", 'norepeat' ); |
+
+
+ |
+ newSlide.setAttribute("data-background", 'url("' + slideCanvas.toDataURL("image/png") +'")' ); |
+
+
+ |
+ nextSlide.parentElement.insertBefore( newSlide, nextSlide ); |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ /***************************************************************** |
+
+
+ |
+ ** Drawings |
+
+
+ |
+ ******************************************************************/ |
+
+
+ |
+
+ |
+
+
+ |
+ function drawWithPen(context,fromX,fromY,toX,toY){ |
+
+
+ |
+ context.lineWidth = 3; |
+
+
+ |
+ context.lineCap = 'round'; |
+
+
+ |
+ context.strokeStyle = color[0]; |
+
+
+ |
+ context.beginPath(); |
+
+
+ |
+ context.moveTo(fromX, fromY); |
+
+
+ |
+ context.lineTo(toX, toY); |
+
+
+ |
+ context.stroke(); |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ function drawWithChalk(context,fromX,fromY,toX,toY){ |
+
+
+ |
+ var brushDiameter = 7; |
+
+
+ |
+ context.lineWidth = brushDiameter; |
+
+
+ |
+ context.lineCap = 'round'; |
+
+
+ |
+ context.fillStyle = color[1]; // 'rgba(255,255,255,0.5)'; |
+
+
+ |
+ context.strokeStyle = color[1]; |
+
+
+ |
+ var opacity = Math.min(0.8, Math.max(0,color[1].replace(/^.*,(.+)\)/,'$1') - 0.1)) + Math.random()*0.2; |
+
+
+ |
+ context.strokeStyle = context.strokeStyle.replace(/[\d\.]+\)$/g, opacity + ')'); |
+
+
+ |
+ context.beginPath(); |
+
+
+ |
+ context.moveTo(fromX, fromY); |
+
+
+ |
+ context.lineTo(toX, toY); |
+
+
+ |
+ context.stroke(); |
+
+
+ |
+ // Chalk Effect |
+
+
+ |
+ var length = Math.round(Math.sqrt(Math.pow(toX-fromX,2)+Math.pow(toY-fromY,2))/(5/brushDiameter)); |
+
+
+ |
+ var xUnit = (toX-fromX)/length; |
+
+
+ |
+ var yUnit = (toY-fromY)/length; |
+
+
+ |
+ for(var i=0; i<length; i++ ){ |
+
+
+ |
+ var xCurrent = fromX+(i*xUnit); |
+
+
+ |
+ var yCurrent = fromY+(i*yUnit); |
+
+
+ |
+ var xRandom = xCurrent+(Math.random()-0.5)*brushDiameter*1.2; |
+
+
+ |
+ var yRandom = yCurrent+(Math.random()-0.5)*brushDiameter*1.2; |
+
+
+ |
+ context.clearRect( xRandom, yRandom, Math.random()*2+2, Math.random()+1); |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ function erase(context,x,y){ |
+
+
+ |
+ context.save(); |
+
+
+ |
+ context.beginPath(); |
+
+
+ |
+ context.arc(x, y, eraserDiameter, 0, 2 * Math.PI, false); |
+
+
+ |
+ context.clip(); |
+
+
+ |
+ context.clearRect(x - eraserDiameter - 1, y - eraserDiameter - 1, eraserDiameter * 2 + 2, eraserDiameter * 2 + 2); |
+
+
+ |
+ context.restore(); |
+
+
+ |
+
+ |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+
+ |
+
+
+ |
+ /** |
+
+
+ |
+ * Opens an overlay for the chalkboard. |
+
+
+ |
+ */ |
+
+
+ |
+ function showChalkboard() { |
+
+
+ |
+ //console.log("showChalkboard"); |
+
+
+ |
+ clearTimeout(touchTimeout); |
+
+
+ |
+ touchTimeout = null; |
+
+
+ |
+ drawingCanvas[0].sponge.style.visibility = "hidden"; // make sure that the sponge from touch events is hidden |
+
+
+ |
+ drawingCanvas[1].sponge.style.visibility = "hidden"; // make sure that the sponge from touch events is hidden |
+
+
+ |
+ drawingCanvas[1].container.classList.add( 'visible' ); |
+
+
+ |
+ mode = 1; |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+
+ |
+
+
+ |
+ /** |
+
+
+ |
+ * Closes open chalkboard. |
+
+
+ |
+ */ |
+
+
+ |
+ function closeChalkboard() { |
+
+
+ |
+ clearTimeout(touchTimeout); |
+
+
+ |
+ touchTimeout = null; |
+
+
+ |
+ drawingCanvas[0].sponge.style.visibility = "hidden"; // make sure that the sponge from touch events is hidden |
+
+
+ |
+ drawingCanvas[1].sponge.style.visibility = "hidden"; // make sure that the sponge from touch events is hidden |
+
+
+ |
+ drawingCanvas[1].container.classList.remove( 'visible' ); |
+
+
+ |
+ xLast = null; |
+
+
+ |
+ yLast = null; |
+
+
+ |
+ event = null; |
+
+
+ |
+ mode = 0; |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ /** |
+
+
+ |
+ * Clear current canvas. |
+
+
+ |
+ */ |
+
+
+ |
+ function clearCanvas( id ) { |
+
+
+ |
+ if ( id == 0 ) clearTimeout( slidechangeTimeout ); |
+
+
+ |
+ drawingCanvas[id].context.clearRect(0,0,drawingCanvas[id].width,drawingCanvas[id].height); |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ /***************************************************************** |
+
+
+ |
+ ** Playback |
+
+
+ |
+ ******************************************************************/ |
+
+
+ |
+
+ |
+
+
+ |
+ document.addEventListener('seekplayback', function( event ) { |
+
+
+ |
+ //console.log('event seekplayback ' + event.timestamp); |
+
+
+ |
+ stopPlayback(); |
+
+
+ |
+ if ( !playback || event.timestamp == 0) { |
+
+
+ |
+ // in other cases startplayback fires after seeked |
+
+
+ |
+ startPlayback( event.timestamp ); |
+
+
+ |
+ } |
+
+
+ |
+ //console.log('seeked'); |
+
+
+ |
+ }); |
+
+
+ |
+
+ |
+
+
+ |
+
+ |
+
+
+ |
+ document.addEventListener('startplayback', function( event ) { |
+
+
+ |
+ //console.log('event startplayback ' + event.timestamp); |
+
+
+ |
+ stopPlayback(); |
+
+
+ |
+ playback = true; |
+
+
+ |
+ startPlayback( event.timestamp ); |
+
+
+ |
+ }); |
+
+
+ |
+
+ |
+
+
+ |
+ document.addEventListener('stopplayback', function( event ) { |
+
+
+ |
+ //console.log('event stopplayback ' + (Date.now() - slideStart) ); |
+
+
+ |
+ playback = false; |
+
+
+ |
+ stopPlayback(); |
+
+
+ |
+ }); |
+
+
+ |
+
+ |
+
+
+ |
+ document.addEventListener('startrecording', function( event ) { |
+
+
+ |
+ //console.log('event startrecording ' + event.timestamp); |
+
+
+ |
+ startRecording(); |
+
+
+ |
+ }); |
+
+
+ |
+
+ |
+
+
+ |
+ function recordEvent( event ) { |
+
+
+ |
+ var slideData = getSlideData(); |
+
+
+ |
+ var i = slideData.events.length; |
+
+
+ |
+ while ( i > 0 && event.begin < slideData.events[i-1].begin ) { |
+
+
+ |
+ i--; |
+
+
+ |
+ } |
+
+
+ |
+ slideData.events.splice( i, 0, event); |
+
+
+ |
+ slideData.duration = Math.max( slideData.duration, Date.now() - slideStart ) + 1; |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ function startRecording() { |
+
+
+ |
+ resetSlide( true ); |
+
+
+ |
+ updateReadOnlyMode(); |
+
+
+ |
+ slideStart = Date.now(); |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ function startPlayback( timestamp, finalMode, resized ) { |
+
+
+ |
+ //console.log("playback " + timestamp ); |
+
+
+ |
+ if ( resized == undefined ) { |
+
+
+ |
+ updateReadOnlyMode(); |
+
+
+ |
+ } |
+
+
+ |
+ slideStart = Date.now() - timestamp; |
+
+
+ |
+ closeChalkboard(); |
+
+
+ |
+ mode = 0; |
+
+
+ |
+ for ( var id = 0; id < 2; id++ ) { |
+
+
+ |
+ clearCanvas( id ); |
+
+
+ |
+ var slideData = getSlideData( slideIndices, id ); |
+
+
+ |
+ //console.log( timestamp +" / " + JSON.stringify(slideData)); |
+
+
+ |
+ var index = 0; |
+
+
+ |
+ while ( index < slideData.events.length && slideData.events[index].begin < (Date.now() - slideStart) ) { |
+
+
+ |
+ playEvent( id, slideData.events[index], timestamp ); |
+
+
+ |
+ index++; |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ while ( playback && index < slideData.events.length ) { |
+
+
+ |
+ timeouts[id].push( setTimeout( playEvent, slideData.events[index].begin - (Date.now() - slideStart), id, slideData.events[index], timestamp ) ); |
+
+
+ |
+ index++; |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ //console.log("Mode: " + finalMode + "/" + mode ); |
+
+
+ |
+ if ( finalMode != undefined ) { |
+
+
+ |
+ mode = finalMode; |
+
+
+ |
+ } |
+
+
+ |
+ if( mode == 1 ) showChalkboard(); |
+
+
+ |
+ //console.log("playback (ok)"); |
+
+
+ |
+ |
+
+
+ |
+ }; |
+
+
+ |
+
+ |
+
+
+ |
+ function stopPlayback() { |
+
+
+ |
+ //console.log("stopPlayback"); |
+
+
+ |
+ //console.log("Timeouts: " + timeouts[0].length + "/"+ timeouts[1].length); |
+
+
+ |
+ for ( var id = 0; id < 2; id++ ) { |
+
+
+ |
+ for (var i = 0; i < timeouts[id].length; i++) { |
+
+
+ |
+ clearTimeout(timeouts[id][i]); |
+
+
+ |
+ } |
+
+
+ |
+ timeouts[id] = []; |
+
+
+ |
+ } |
+
+
+ |
+ }; |
+
+
+ |
+
+ |
+
+
+ |
+ function playEvent( id, event, timestamp ) { |
+
+
+ |
+ //console.log( timestamp +" / " + JSON.stringify(event)); |
+
+
+ |
+ //console.log( id + ": " + timestamp +" / " + event.begin +" / " + event.type +" / " + mode ); |
+
+
+ |
+ switch ( event.type ) { |
+
+
+ |
+ case "open": |
+
+
+ |
+ if ( timestamp <= event.begin ) { |
+
+
+ |
+ showChalkboard(); |
+
+
+ |
+ } |
+
+
+ |
+ else { |
+
+
+ |
+ mode = 1; |
+
+
+ |
+ } |
+
+
+ |
+ |
+
+
+ |
+ break; |
+
+
+ |
+ case "close": |
+
+
+ |
+ if ( timestamp < event.begin ) { |
+
+
+ |
+ closeChalkboard(); |
+
+
+ |
+ } |
+
+
+ |
+ else { |
+
+
+ |
+ mode = 0; |
+
+
+ |
+ } |
+
+
+ |
+ break; |
+
+
+ |
+ case "clear": |
+
+
+ |
+ clearCanvas( id ); |
+
+
+ |
+ break; |
+
+
+ |
+ case "draw": |
+
+
+ |
+ drawCurve( id, event, timestamp ); |
+
+
+ |
+ break; |
+
+
+ |
+ case "erase": |
+
+
+ |
+ eraseCurve( id, event, timestamp ); |
+
+
+ |
+ break; |
+
+
+ |
+
+ |
+
+
+ |
+ } |
+
+
+ |
+ }; |
+
+
+ |
+
+ |
+
+
+ |
+ function drawCurve( id, event, timestamp ) { |
+
+
+ |
+ if ( event.curve.length > 1 ) { |
+
+
+ |
+ var ctx = drawingCanvas[id].context; |
+
+
+ |
+ var scale = drawingCanvas[id].scale; |
+
+
+ |
+ var xOffset = drawingCanvas[id].xOffset; |
+
+
+ |
+ var yOffset = drawingCanvas[id].yOffset; |
+
+
+ |
+
+ |
+
+
+ |
+ var stepDuration = ( event.end - event.begin )/ ( event.curve.length - 1 ); |
+
+
+ |
+ //console.log("---"); |
+
+
+ |
+ for (var i = 1; i < event.curve.length; i++) { |
+
+
+ |
+ if (event.begin + i * stepDuration <= (Date.now() - slideStart)) { |
+
+
+ |
+ //console.log( "Draw " + timestamp +" / " + event.begin + " + " + i + " * " + stepDuration ); |
+
+
+ |
+ 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); |
+
+
+ |
+ } |
+
+
+ |
+ else if ( playback ) { |
+
+
+ |
+ //console.log( "Cue " + timestamp +" / " + (Date.now() - slideStart) +" / " + event.begin + " + " + i + " * " + stepDuration + " = " + Math.max(0,event.begin + i * stepDuration - timestamp) ); |
+
+
+ |
+ timeouts.push( setTimeout( |
+
+
+ |
+ draw[id], Math.max(0,event.begin + i * stepDuration - (Date.now() - slideStart)), 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 |
+
+
+ |
+ ) |
+
+
+ |
+ ); |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ }; |
+
+
+ |
+
+ |
+
+
+ |
+ function eraseCurve( id, event, timestamp ) { |
+
+
+ |
+ if ( event.curve.length > 1 ) { |
+
+
+ |
+ var ctx = drawingCanvas[id].context; |
+
+
+ |
+ var scale = drawingCanvas[id].scale; |
+
+
+ |
+ var xOffset = drawingCanvas[id].xOffset; |
+
+
+ |
+ var yOffset = drawingCanvas[id].yOffset; |
+
+
+ |
+
+ |
+
+
+ |
+ var stepDuration = ( event.end - event.begin )/ event.curve.length; |
+
+
+ |
+ for (var i = 0; i < event.curve.length; i++) { |
+
+
+ |
+ if (event.begin + i * stepDuration <= (Date.now() - slideStart)) { |
+
+
+ |
+ erase(ctx, xOffset + event.curve[i].x*scale, yOffset + event.curve[i].y*scale); |
+
+
+ |
+ } |
+
+
+ |
+ else if ( playback ) { |
+
+
+ |
+ timeouts.push( setTimeout( |
+
+
+ |
+ erase, Math.max(0,event.begin + i * stepDuration - (Date.now() - slideStart)), ctx, |
+
+
+ |
+ xOffset + event.curve[i].x * scale, |
+
+
+ |
+ yOffset + event.curve[i].y * scale |
+
+
+ |
+ ) |
+
+
+ |
+ ); |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ }; |
+
+
+ |
+
+ |
+
+
+ |
+ /***************************************************************** |
+
+
+ |
+ ** User interface |
+
+
+ |
+ ******************************************************************/ |
+
+
+ |
+
+ |
+
+
+ |
+
+ |
+
+
+ |
+ // TODO: check all touchevents |
+
+
+ |
+ document.addEventListener('touchstart', function(evt) { |
+
+
+ |
+ if ( !readOnly && evt.target.getAttribute('data-chalkboard') == mode ) { |
+
+
+ |
+ var ctx = drawingCanvas[mode].context; |
+
+
+ |
+ var scale = drawingCanvas[mode].scale; |
+
+
+ |
+ var xOffset = drawingCanvas[mode].xOffset; |
+
+
+ |
+ var yOffset = drawingCanvas[mode].yOffset; |
+
+
+ |
+
+ |
+
+
+ |
+ evt.preventDefault(); |
+
+
+ |
+ var touch = evt.touches[0]; |
+
+
+ |
+ mouseX = touch.pageX; |
+
+
+ |
+ mouseY = touch.pageY; |
+
+
+ |
+ xLast = mouseX; |
+
+
+ |
+ yLast = mouseY; |
+
+
+ |
+ event = { type: "draw", begin: Date.now() - slideStart, end: null, curve: [{x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale}] }; |
+
+
+ |
+ touchTimeout = setTimeout( showSponge, 500, mouseX, mouseY ); |
+
+
+ |
+ } |
+
+
+ |
+ }, false); |
+
+
+ |
+
+ |
+
+
+ |
+ document.addEventListener('touchmove', function(evt) { |
+
+
+ |
+ clearTimeout( touchTimeout ); |
+
+
+ |
+ touchTimeout = null; |
+
+
+ |
+ if ( event ) { |
+
+
+ |
+ var ctx = drawingCanvas[mode].context; |
+
+
+ |
+ var scale = drawingCanvas[mode].scale; |
+
+
+ |
+ var xOffset = drawingCanvas[mode].xOffset; |
+
+
+ |
+ var yOffset = drawingCanvas[mode].yOffset; |
+
+
+ |
+
+ |
+
+
+ |
+ var touch = evt.touches[0]; |
+
+
+ |
+ mouseX = touch.pageX; |
+
+
+ |
+ mouseY = touch.pageY; |
+
+
+ |
+ if (mouseY < drawingCanvas[mode].height && mouseX < drawingCanvas[mode].width) { |
+
+
+ |
+ evt.preventDefault(); |
+
+
+ |
+ event.curve.push({x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale}); |
+
+
+ |
+ if ( event.type == "erase" ) { |
+
+
+ |
+ drawingCanvas[mode].sponge.style.left = (mouseX - eraserDiameter) +"px" ; |
+
+
+ |
+ drawingCanvas[mode].sponge.style.top = (mouseY - eraserDiameter) +"px" ; |
+
+
+ |
+ erase(ctx, mouseX, mouseY); |
+
+
+ |
+ } |
+
+
+ |
+ else { |
+
+
+ |
+ draw[mode](ctx, xLast, yLast, mouseX, mouseY); |
+
+
+ |
+ } |
+
+
+ |
+ xLast = mouseX; |
+
+
+ |
+ yLast = mouseY; |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ }, false); |
+
+
+ |
+
+ |
+
+
+ |
+ document.addEventListener('touchend', function(evt) { |
+
+
+ |
+ clearTimeout( touchTimeout ); |
+
+
+ |
+ touchTimeout = null; |
+
+
+ |
+ // hide sponge image |
+
+
+ |
+ drawingCanvas[mode].sponge.style.visibility = "hidden"; |
+
+
+ |
+ if ( event ) { |
+
+
+ |
+ event.end = Date.now() - slideStart; |
+
+
+ |
+ if ( event.type == "erase" || event.curve.length > 1 ) { |
+
+
+ |
+ // do not save a line with a single point only |
+
+
+ |
+ recordEvent( event ); |
+
+
+ |
+ } |
+
+
+ |
+ event = null; |
+
+
+ |
+ } |
+
+
+ |
+ }, false); |
+
+
+ |
+
+ |
+
+
+ |
+ function showSponge(x,y) { |
+
+
+ |
+ if ( event ) { |
+
+
+ |
+ event.type = "erase"; |
+
+
+ |
+ event.begin = Date.now() - slideStart; |
+
+
+ |
+ // show sponge image |
+
+
+ |
+ drawingCanvas[mode].sponge.style.left = (x - eraserDiameter) +"px" ; |
+
+
+ |
+ drawingCanvas[mode].sponge.style.top = (y - eraserDiameter) +"px" ; |
+
+
+ |
+ drawingCanvas[mode].sponge.style.visibility = "visible"; |
+
+
+ |
+ erase(drawingCanvas[mode].context,x,y); |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ document.addEventListener( 'mousedown', function( evt ) { |
+
+
+ |
+ //console.log( "Read only: " + readOnly ); |
+
+
+ |
+ if ( !readOnly && evt.target.getAttribute('data-chalkboard') == mode ) { |
+
+
+ |
+ //console.log( "mousedown: " + evt.target.getAttribute('data-chalkboard') ); |
+
+
+ |
+ var ctx = drawingCanvas[mode].context; |
+
+
+ |
+ var scale = drawingCanvas[mode].scale; |
+
+
+ |
+ var xOffset = drawingCanvas[mode].xOffset; |
+
+
+ |
+ var yOffset = drawingCanvas[mode].yOffset; |
+
+
+ |
+
+ |
+
+
+ |
+ mouseX = evt.pageX; |
+
+
+ |
+ mouseY = evt.pageY; |
+
+
+ |
+ xLast = mouseX; |
+
+
+ |
+ yLast = mouseY; |
+
+
+ |
+ if ( evt.button == 2) { |
+
+
+ |
+ event = { type: "erase", begin: Date.now() - slideStart, end: null, curve: [{x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale}]}; |
+
+
+ |
+ drawingCanvas[mode].canvas.style.cursor = 'url("' + path + 'img/sponge.png") ' + eraserDiameter + ' ' + eraserDiameter + ', auto'; |
+
+
+ |
+ erase(ctx,mouseX,mouseY); |
+
+
+ |
+ } |
+
+
+ |
+ else { |
+
+
+ |
+ event = { type: "draw", begin: Date.now() - slideStart, end: null, curve: [{x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale}] }; |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ } ); |
+
+
+ |
+
+ |
+
+
+ |
+ document.addEventListener( 'mousemove', function( evt ) { |
+
+
+ |
+ if ( event ) { |
+
+
+ |
+ var ctx = drawingCanvas[mode].context; |
+
+
+ |
+ var scale = drawingCanvas[mode].scale; |
+
+
+ |
+ var xOffset = drawingCanvas[mode].xOffset; |
+
+
+ |
+ var yOffset = drawingCanvas[mode].yOffset; |
+
+
+ |
+
+ |
+
+
+ |
+ mouseX = evt.pageX; |
+
+
+ |
+ mouseY = evt.pageY; |
+
+
+ |
+ event.curve.push({x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale}); |
+
+
+ |
+ if(mouseY < drawingCanvas[mode].height && mouseX < drawingCanvas[mode].width) { |
+
+
+ |
+ if ( event.type == "erase" ) { |
+
+
+ |
+ erase(ctx,mouseX,mouseY); |
+
+
+ |
+ } |
+
+
+ |
+ else { |
+
+
+ |
+ draw[mode](ctx, xLast, yLast, mouseX,mouseY); |
+
+
+ |
+ } |
+
+
+ |
+ xLast = mouseX; |
+
+
+ |
+ yLast = mouseY; |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ } ); |
+
+
+ |
+
+ |
+
+
+ |
+ document.addEventListener( 'mouseup', function( evt ) { |
+
+
+ |
+ drawingCanvas[mode].canvas.style.cursor = pen[mode]; |
+
+
+ |
+ if ( event ) { |
+
+
+ |
+ if(evt.button == 2){ |
+
+
+ |
+ } |
+
+
+ |
+ event.end = Date.now() - slideStart; |
+
+
+ |
+ if ( event.type == "erase" || event.curve.length > 1 ) { |
+
+
+ |
+ // do not save a line with a single point only |
+
+
+ |
+ recordEvent( event ); |
+
+
+ |
+ } |
+
+
+ |
+ event = null; |
+
+
+ |
+ } |
+
+
+ |
+ } ); |
+
+
+ |
+
+ |
+
+
+ |
+ window.addEventListener( "resize", function() { |
+
+
+ |
+ //console.log("resize"); |
+
+
+ |
+ // Resize the canvas and draw everything again |
+
+
+ |
+ var timestamp = Date.now() - slideStart; |
+
+
+ |
+ if ( !playback ) { |
+
+
+ |
+ timestamp = getSlideDuration(); |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ //console.log( drawingCanvas[0].scale + "/" + drawingCanvas[0].xOffset + "/" +drawingCanvas[0].yOffset ); |
+
+
+ |
+ for (var id = 0; id < 2; id++ ) { |
+
+
+ |
+ drawingCanvas[id].width = window.innerWidth; |
+
+
+ |
+ drawingCanvas[id].height = window.innerHeight; |
+
+
+ |
+ drawingCanvas[id].canvas.width = drawingCanvas[id].width; |
+
+
+ |
+ drawingCanvas[id].canvas.height = drawingCanvas[id].height; |
+
+
+ |
+ drawingCanvas[id].context.canvas.width = drawingCanvas[id].width; |
+
+
+ |
+ drawingCanvas[id].context.canvas.height = drawingCanvas[id].height; |
+
+
+ |
+
+ |
+
+
+ |
+ drawingCanvas[id].scale = Math.min( drawingCanvas[id].width/storage[id].width, drawingCanvas[id].height/storage[id].height ); |
+
+
+ |
+ drawingCanvas[id].xOffset = (drawingCanvas[id].width - storage[id].width * drawingCanvas[id].scale)/2; |
+
+
+ |
+ drawingCanvas[id].yOffset = (drawingCanvas[id].height - storage[id].height * drawingCanvas[id].scale)/2; |
+
+
+ |
+ //console.log( drawingCanvas[id].scale + "/" + drawingCanvas[id].xOffset + "/" +drawingCanvas[id].yOffset ); |
+
+
+ |
+ } |
+
+
+ |
+ //console.log( window.innerWidth + "/" + window.innerHeight); |
+
+
+ |
+ startPlayback( timestamp, mode, true ); |
+
+
+ |
+
+ |
+
+
+ |
+ } ); |
+
+
+ |
+
+ |
+
+
+ |
+ function updateReadOnlyMode() { |
+
+
+ |
+ //console.log("updateReadOnlyMode"); |
+
+
+ |
+ if ( config.readOnly == undefined ) { |
+
+
+ |
+ readOnly = ( getSlideDuration() > 0 ); |
+
+
+ |
+ if ( readOnly ) { |
+
+
+ |
+ drawingCanvas[0].container.style.cursor = 'default'; |
+
+
+ |
+ drawingCanvas[1].container.style.cursor = 'default'; |
+
+
+ |
+ drawingCanvas[0].canvas.style.cursor = 'default'; |
+
+
+ |
+ drawingCanvas[1].canvas.style.cursor = 'default'; |
+
+
+ |
+ if ( notescanvas.style.pointerEvents != "none" ) { |
+
+
+ |
+ event = null; |
+
+
+ |
+ notescanvas.style.background = 'rgba(0,0,0,0)'; |
+
+
+ |
+ notescanvas.style.pointerEvents = "none"; |
+
+
+ |
+ } |
+
+
+ |
+ |
+
+
+ |
+ } |
+
+
+ |
+ else { |
+
+
+ |
+ drawingCanvas[0].container.style.cursor = pen[0]; |
+
+
+ |
+ drawingCanvas[1].container.style.cursor = pen[1]; |
+
+
+ |
+ drawingCanvas[0].canvas.style.cursor = pen[0]; |
+
+
+ |
+ drawingCanvas[1].canvas.style.cursor = pen[1]; |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+
+ |
+
+
+ |
+ Reveal.addEventListener( 'ready', function( evt ) { |
+
+
+ |
+ //console.log('ready'); |
+
+
+ |
+ if ( !printMode ) { |
+
+
+ |
+ slideStart = Date.now(); |
+
+
+ |
+ slideIndices = Reveal.getIndices(); |
+
+
+ |
+ if ( !playback ) { |
+
+
+ |
+ startPlayback( getSlideDuration(), 0 ); |
+
+
+ |
+ } |
+
+
+ |
+ if ( Reveal.isAutoSliding() ) { |
+
+
+ |
+ var event = new CustomEvent('startplayback'); |
+
+
+ |
+ event.timestamp = 0; |
+
+
+ |
+ document.dispatchEvent( event ); |
+
+
+ |
+ } |
+
+
+ |
+ updateReadOnlyMode(); |
+
+
+ |
+ } |
+
+
+ |
+ else { |
+
+
+ |
+ createPrintout(); |
+
+
+ |
+ } |
+
+
+ |
+ }); |
+
+
+ |
+ Reveal.addEventListener( 'slidechanged', function( evt ) { |
+
+
+ |
+ // clearTimeout( slidechangeTimeout ); |
+
+
+ |
+ //console.log('slidechanged'); |
+
+
+ |
+ if ( !printMode ) { |
+
+
+ |
+ slideStart = Date.now(); |
+
+
+ |
+ slideIndices = Reveal.getIndices(); |
+
+
+ |
+ closeChalkboard(); |
+
+
+ |
+ clearCanvas( 0 ); |
+
+
+ |
+ clearCanvas( 1 ); |
+
+
+ |
+ if ( !playback ) { |
+
+
+ |
+ slidechangeTimeout = setTimeout( startPlayback, transition, getSlideDuration(), 0 ); |
+
+
+ |
+ } |
+
+
+ |
+ if ( Reveal.isAutoSliding() ) { |
+
+
+ |
+ var event = new CustomEvent('startplayback'); |
+
+
+ |
+ event.timestamp = 0; |
+
+
+ |
+ document.dispatchEvent( event ); |
+
+
+ |
+ } |
+
+
+ |
+ |
+
+
+ |
+ updateReadOnlyMode(); |
+
+
+ |
+ } |
+
+
+ |
+ }); |
+
+
+ |
+ Reveal.addEventListener( 'fragmentshown', function( evt ) { |
+
+
+ |
+ // clearTimeout( slidechangeTimeout ); |
+
+
+ |
+ //console.log('fragmentshown'); |
+
+
+ |
+ if ( !printMode ) { |
+
+
+ |
+ slideStart = Date.now(); |
+
+
+ |
+ slideIndices = Reveal.getIndices(); |
+
+
+ |
+ closeChalkboard(); |
+
+
+ |
+ clearCanvas( 0 ); |
+
+
+ |
+ clearCanvas( 1 ); |
+
+
+ |
+ if ( Reveal.isAutoSliding() ) { |
+
+
+ |
+ var event = new CustomEvent('startplayback'); |
+
+
+ |
+ event.timestamp = 0; |
+
+
+ |
+ document.dispatchEvent( event ); |
+
+
+ |
+ } |
+
+
+ |
+ else if ( !playback ) { |
+
+
+ |
+ // |
+
+
+ |
+ startPlayback( getSlideDuration(), 0 ); |
+
+
+ |
+ // closeChalkboard(); |
+
+
+ |
+ } |
+
+
+ |
+ updateReadOnlyMode(); |
+
+
+ |
+ } |
+
+
+ |
+ }); |
+
+
+ |
+ Reveal.addEventListener( 'fragmenthidden', function( evt ) { |
+
+
+ |
+ // clearTimeout( slidechangeTimeout ); |
+
+
+ |
+ //console.log('fragmenthidden'); |
+
+
+ |
+ if ( !printMode ) { |
+
+
+ |
+ slideStart = Date.now(); |
+
+
+ |
+ slideIndices = Reveal.getIndices(); |
+
+
+ |
+ closeChalkboard(); |
+
+
+ |
+ clearCanvas( 0 ); |
+
+
+ |
+ clearCanvas( 1 ); |
+
+
+ |
+ if ( Reveal.isAutoSliding() ) { |
+
+
+ |
+ document.dispatchEvent( new CustomEvent('stopplayback') ); |
+
+
+ |
+ } |
+
+
+ |
+ else if ( !playback ) { |
+
+
+ |
+ startPlayback( getSlideDuration() ); |
+
+
+ |
+ closeChalkboard(); |
+
+
+ |
+ } |
+
+
+ |
+ updateReadOnlyMode(); |
+
+
+ |
+ } |
+
+
+ |
+ }); |
+
+
+ |
+
+ |
+
+
+ |
+ Reveal.addEventListener( 'autoslideresumed', function( evt ) { |
+
+
+ |
+ //console.log('autoslideresumed'); |
+
+
+ |
+ var event = new CustomEvent('startplayback'); |
+
+
+ |
+ event.timestamp = 0; |
+
+
+ |
+ document.dispatchEvent( event ); |
+
+
+ |
+ }); |
+
+
+ |
+ Reveal.addEventListener( 'autoslidepaused', function( evt ) { |
+
+
+ |
+ //console.log('autoslidepaused'); |
+
+
+ |
+ document.dispatchEvent( new CustomEvent('stopplayback') ); |
+
+
+ |
+
+ |
+
+
+ |
+ // advance to end of slide |
+
+
+ |
+ // closeChalkboard(); |
+
+
+ |
+ startPlayback( getSlideDuration(), 0 ); |
+
+
+ |
+ }); |
+
+
+ |
+
+ |
+
+
+ |
+ function toggleNotesCanvas() { |
+
+
+ |
+ if ( !readOnly ) { |
+
+
+ |
+ if ( mode == 1 ) { |
+
+
+ |
+ toggleChalkboard(); |
+
+
+ |
+ notescanvas.style.background = background[0]; //'rgba(255,0,0,0.5)'; |
+
+
+ |
+ notescanvas.style.pointerEvents = "auto"; |
+
+
+ |
+ } |
+
+
+ |
+ else { |
+
+
+ |
+ if ( notescanvas.style.pointerEvents != "none" ) { |
+
+
+ |
+ event = null; |
+
+
+ |
+ notescanvas.style.background = 'rgba(0,0,0,0)'; |
+
+
+ |
+ notescanvas.style.pointerEvents = "none"; |
+
+
+ |
+ } |
+
+
+ |
+ else { |
+
+
+ |
+ notescanvas.style.background = background[0]; //'rgba(255,0,0,0.5)'; |
+
+
+ |
+ notescanvas.style.pointerEvents = "auto"; |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ } |
+
+
+ |
+ }; |
+
+
+ |
+
+ |
+
+
+ |
+ function toggleChalkboard() { |
+
+
+ |
+ //console.log("toggleChalkboard " + mode); |
+
+
+ |
+ if ( mode == 1 ) { |
+
+
+ |
+ event = null; |
+
+
+ |
+ if ( !readOnly ) recordEvent( { type:"close", begin: Date.now() - slideStart } ); |
+
+
+ |
+ closeChalkboard(); |
+
+
+ |
+ } |
+
+
+ |
+ else { |
+
+
+ |
+ showChalkboard(); |
+
+
+ |
+ if ( !readOnly ) recordEvent( { type:"open", begin: Date.now() - slideStart } ); |
+
+
+ |
+ } |
+
+
+ |
+ }; |
+
+
+ |
+
+ |
+
+
+ |
+ function clear() { |
+
+
+ |
+ if ( !readOnly ) { |
+
+
+ |
+ recordEvent( { type:"clear", begin: Date.now() - slideStart } ); |
+
+
+ |
+ clearCanvas( mode ); |
+
+
+ |
+ } |
+
+
+ |
+ }; |
+
+
+ |
+
+ |
+
+
+ |
+ function resetSlide( force ) { |
+
+
+ |
+ var ok = force || confirm("Please confirm to delete chalkboard drawings on this slide!"); |
+
+
+ |
+ if ( ok ) { |
+
+
+ |
+ //console.log("resetSlide "); |
+
+
+ |
+ stopPlayback(); |
+
+
+ |
+ slideStart = Date.now(); |
+
+
+ |
+ event = null; |
+
+
+ |
+ closeChalkboard(); |
+
+
+ |
+
+ |
+
+
+ |
+ clearCanvas( 0 ); |
+
+
+ |
+ clearCanvas( 1 ); |
+
+
+ |
+
+ |
+
+
+ |
+ mode = 1; |
+
+
+ |
+ var slideData = getSlideData(); |
+
+
+ |
+ slideData.duration = 0; |
+
+
+ |
+ slideData.events = []; |
+
+
+ |
+ mode = 0; |
+
+
+ |
+ var slideData = getSlideData(); |
+
+
+ |
+ slideData.duration = 0; |
+
+
+ |
+ slideData.events = []; |
+
+
+ |
+
+ |
+
+
+ |
+ updateReadOnlyMode(); |
+
+
+ |
+ } |
+
+
+ |
+ }; |
+
+
+ |
+
+ |
+
+
+ |
+ function resetStorage( force ) { |
+
+
+ |
+ var ok = force || confirm("Please confirm to delete all chalkboard drawings!"); |
+
+
+ |
+ if ( ok ) { |
+
+
+ |
+ stopPlayback(); |
+
+
+ |
+ slideStart = Date.now(); |
+
+
+ |
+ clearCanvas( 0 ); |
+
+
+ |
+ clearCanvas( 1 ); |
+
+
+ |
+ if ( mode == 1 ) { |
+
+
+ |
+ event = null; |
+
+
+ |
+ closeChalkboard(); |
+
+
+ |
+ } |
+
+
+ |
+ storage = [ |
+
+
+ |
+ { width: drawingCanvas[0].width - 2 * drawingCanvas[0].xOffset, height: drawingCanvas[0].height - 2 * drawingCanvas[0].yOffset, data: []}, |
+
+
+ |
+ { width: drawingCanvas[1].width, height: drawingCanvas[1].height, data: []} |
+
+
+ |
+ ]; |
+
+
+ |
+
+ |
+
+
+ |
+ updateReadOnlyMode(); |
+
+
+ |
+ } |
+
+
+ |
+ }; |
+
+
+ |
+
+ |
+
+
+ |
+ this.drawWithPen = drawWithPen; |
+
+
+ |
+ this.drawWithChalk = drawWithChalk; |
+
+
+ |
+ this.toggleNotesCanvas = toggleNotesCanvas; |
+
+
+ |
+ this.toggleChalkboard = toggleChalkboard; |
+
+
+ |
+ this.startRecording = startRecording; |
+
+
+ |
+ this.clear = clear; |
+
+
+ |
+ this.reset = resetSlide; |
+
+
+ |
+ this.resetAll = resetStorage; |
+
+
+ |
+ this.download = downloadData; |
+
+
+ |
+
+ |
+
+
+ |
+ return this; |
+
+
+ |
+ })(); |
+
+