diff --git a/amd/build/canvas.min.js b/amd/build/canvas.min.js
index c386bb5..0b9b343 100644
--- a/amd/build/canvas.min.js
+++ b/amd/build/canvas.min.js
@@ -1,2 +1,13 @@
-function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("mod_gcanvas/canvas",["jquery","core/notification"],function(a,b){'use strict';var c={id:0,debugjs:!1,hasHorizontalRuler:!0,background:""},d=function(a){var b,d;for(b in c){if(c.hasOwnProperty(b)&&a.hasOwnProperty(b)){d=_typeof(c[b]);if("boolean"===d){c[b]=!!a[b]}else if("number"===d){c[b]=+a[b]}else if("string"===d){c[b]=a[b]+""}}}},f={},g=0,h=!0,i=function(a){if(a){for(var b in console){if("function"==typeof console[b]){f[b]=console[b].bind(window.console)}}}else{for(var c in console){if("function"==typeof console[c]){f[c]=function(){}}}}},j=null,k={canvasWidth:800,canvasHeight:500,defaultShaperect:{width:70,height:70,left:200,top:50,angle:0,fill:"#ffb628"},defaultShapecircle:{radius:40,left:200,top:50,fill:"#b3cc2b"},defaultShapetriangle:{top:50,left:200,width:70,height:70,fill:"#0081b4"},defaultShapetextbox:{top:50,left:200,fill:"#0081b4"},loadHistory:function loadHistory(){a.ajax({type:"POST",url:M.cfg.wwwroot+"/mod/gcanvas/ajax.php",data:{sesskey:M.cfg.sesskey,action:"load_history",data:{id:c.id}},dataType:"json",success:function success(b){f.log(b);if(b.success){a("#history").html(b.html)}},error:function error(a){f.error(a.responseText);b.addNotification({message:a.responseText,type:"error"})}})},saveCanvasAjax:function saveCanvasAjax(){if(!fabric.Canvas.supports("toDataURL")){b.addNotification({message:"This browser doesn't provide means to serialize canvas to an image",type:"error"})}else{a.ajax({type:"POST",url:M.cfg.wwwroot+"/mod/gcanvas/ajax.php",data:{sesskey:M.cfg.sesskey,action:"save_canvas",data:{id:c.id,status:"final",canvas_data:j.toDataURL({multiplier:1,format:"png"}),json_data:JSON.stringify(j)}},dataType:"json",success:function success(a){f.log(a);if(a.success){b.addNotification({message:M.util.get_string("javascript:updated","mod_gcanvas"),type:"success"});k.loadHistory()}else{b.addNotification({message:a.error,type:"error"})}},error:function error(a){f.error(a.responseText);b.addNotification({message:a.responseText,type:"error"})}})}},deleteSelectedCanvasItems:function deleteSelectedCanvasItems(){try{var a=j.getActiveObjects();if(0>=a.length){f.log("Selection empty");return}for(var b in a){if(a.hasOwnProperty(b)){var c=a[b];if(c.id!==void 0&&"ruler"===c.id){f.log("Ruler: Not removable!");continue}j.remove(c)}}j.discardActiveObject().renderAll()}catch(a){f.error("Nothing selected",a)}},loadDynamicToolbarMappingShapes:function loadDynamicToolbarMappingShapes(){a("#toolbar .icon[data-element-type]").on("click",function(){var b,c=a(this).data("element-type");try{j.discardActiveObject()}catch(a){}var d="defaultShape"+c.toLowerCase();f.log("Search for shape: "+d);if(k.hasOwnProperty(d)){f.log("Shape found");if("Textbox"===c){b=new fabric[c]("DEMO",k[d])}else{b=new fabric[c](k[d])}j.add(b);j.setActiveObject(b)}else{f.error("Shape not found!")}j.renderAll()})},loadColorPicker:function loadColorPicker(){a("#colorpicker").spectrum({showPalette:!0,palette:[],showSelectionPalette:!0,selectionPalette:["red","green","blue","orange"],flat:!1,change:function change(a){f.log("change color");k.setColor(a)}}).on("dragstart.spectrum , dragstop.spectrum",function(a,b){f.log("change color - dragstop - dragstart");k.setColor(b)})},loadEmojiCsv:function loadEmojiCsv(a){f.log("loadEmojiCsv : ",a);fabric.Image.fromURL(a.replace(".png",".svg"),function(a){a.set({height:500,width:500,left:150,top:100,angle:0,centerTransform:!0}).scale(.4).setCoords();j.add(a);j.setActiveObject(a)})},deleteAttempt:function deleteAttempt(d){f.log("Delete",d);b.confirm(M.util.get_string("javascript:confirm_title","mod_gcanvas"),M.util.get_string("javascript:confirm_desc","mod_gcanvas"),M.util.get_string("javascript:yes","mod_gcanvas"),M.util.get_string("javascript:no","mod_gcanvas"),function(){a.ajax({type:"POST",url:M.cfg.wwwroot+"/mod/gcanvas/ajax.php",data:{sesskey:M.cfg.sesskey,action:"delete_attempt",data:{id:c.id,attempt_id:d.data("id")}},dataType:"json",success:function success(a){f.log(a);if(a.success){k.loadHistory()}else{b.addNotification({message:a.error,type:"error"})}},error:function error(a){f.error(a.responseText);b.addNotification({message:a.responseText,type:"error"})}})})},restoreAttempt:function restoreAttempt(b){f.log("Restore",b);a.ajax({type:"POST",url:M.cfg.wwwroot+"/mod/gcanvas/ajax.php",data:{sesskey:M.cfg.sesskey,action:"get_attempt",data:{id:c.id,attempt_id:b.data("id")}},dataType:"json",success:function success(a){f.log(a);if(a.success){h=!1;j.loadFromJSON(a.record.json_data,j.renderAll.bind(j));setTimeout(function(){h=!0},1e3)}}})},showFileuploader:function showFileuploader(b){a("#canvas-filepicker-form-"+b).toggle()},setBackgroundImage:function setBackgroundImage(){if(""!==c.background){fabric.Image.fromURL(c.background,function(a){j.setBackgroundImage(a,j.renderAll.bind(j),{scaleX:j.width/a.width,scaleY:j.height/a.height})})}},addUserImage:function addUserImage(){var b={id:c.id},d=a("#canvas-filepicker-form-student_image form").serializeArray();a.each(d,function(a,c){b[c.name]=c.value});a.ajax({type:"POST",url:M.cfg.wwwroot+"/mod/gcanvas/ajax.php",data:{sesskey:M.cfg.sesskey,action:"upload_images",data:b},dataType:"json",success:function success(b){f.log(b);if(b.success){k.addImageFromUrl(b.image)}a("#canvas-filepicker-form-student_image").hide()}})},addImageFromUrl:function addImageFromUrl(a){fabric.Image.fromURL(a,function(a){a.set({left:150,top:100,angle:0,centerTransform:!0}).setCoords();var b=j.getWidth()/3;if(a.width>b){a.scaleToWidth(b)}j.add(a);j.setActiveObject(a)})},selectToolbarImage:function selectToolbarImage(){var b=a("#image-picker");if(b.is(":visible")){b.hide();return}b.show();a.ajax({type:"POST",url:M.cfg.wwwroot+"/mod/gcanvas/ajax.php",data:{sesskey:M.cfg.sesskey,action:"get_toolbar_images",data:{id:c.id}},dataType:"json",success:function success(c){f.log(c);if(c.success){var d="
";a.each(c.images,function(a,b){d+=""});d+="
";b.html(d)}}})},undo:function undo(){if(0===g){return}try{var a=localStorage.getItem("buffer_"+g);j.loadFromJSON(a,j.renderAll.bind(j));localStorage.removeItem("buffer_"+g);g--}catch(a){f.log(a)}},loadToolbar:function loadToolbar(){this.setBackgroundImage();this.loadDynamicToolbarMappingShapes();this.loadColorPicker();a("#clear").on("click",function(){j.clear();if(c.hasHorizontalRuler){k.addHorizontalRuler()}k.setBackgroundImage()});a("#arrow i").on("click",function(){k.loadArrowToCanvas()});a("#trash i").on("click",function(){k.deleteSelectedCanvasItems()});a("#smiley i").on("click",function(){k.loadEmojiPicker()});a("#undo").on("click",function(){h=!1;k.undo();setTimeout(function(){h=!0},500)});a("#add-image i").on("click",function(){k.showFileuploader("student_image")});a("#select-a-image i").on("click",function(){k.selectToolbarImage()});a("#image-picker ").on("click","img",function(){k.addImageFromUrl(a(this).attr("src"));a("#image-picker").hide()});a("#save-canvas").on("click",function(){k.saveCanvasAjax()});a("#show-help").on("click",function(){a("#dialog-help").show()});a("#dialog-help").on("click",function(){a("#dialog-help").hide()});a("#history").on("click",".delete",function(b){b.preventDefault();k.deleteAttempt(a(this))}).on("click",".restore",function(b){b.preventDefault();k.restoreAttempt(a(this))});a("#emoji-picker").on("click","img",function(){k.loadEmojiCsv(a(this).attr("src"));a("#emoji-picker").hide()});a("#change_background").on("click",function(){k.showFileuploader("background")});a("#add_toolbar_images").on("click",function(){k.showFileuploader("toolbar_shape")});a("#canvas-filepicker-form-student_image").on("click","#id_submitbutton",function(a){a.preventDefault();k.addUserImage()});a(".dialog").on("click",".btn-secondary",function(b){b.preventDefault();f.log("Cancel");a(".dialog").hide()})},loadEmojiPicker:function loadEmojiPicker(){var b=a("#emoji-picker");if(""!==b.html()){f.log("Toggle emoji");b.toggle();return}a.ajax({type:"POST",url:M.cfg.wwwroot+"/mod/gcanvas/ajax.php",data:{sesskey:M.cfg.sesskey,action:"emoji",data:{id:c.id}},dataType:"json",success:function success(a){f.log(a);if(a.success){b.html(a.html).show()}}})},loadArrowToCanvas:function loadArrowToCanvas(){fabric.loadSVGFromURL("pix/arrow.svg",function(a,b){var c=fabric.util.groupSVGElements(a,b);j.add(c.scale(.1)).centerObject(c).renderAll();c.setCoords();j.setActiveObject(c)})},setColor:function setColor(a){var b=a.toHexString(),c=j.getActiveObjects();if(c){for(var d in c){if(c.hasOwnProperty(d)){var e=c[d];if(e.hasOwnProperty("id")&&"ruler"===e.id){continue}e.set("fill",b)}}j.renderAll()}else{f.log("No active items")}},preventMovingOutOfCanvas:function preventMovingOutOfCanvas(){j.on("object:moving",function(a){var b=a.target;if(b.currentHeight>b.canvas.height||b.currentWidth>b.canvas.width){return}b.setCoords();if(0>b.getBoundingRect().top||0>b.getBoundingRect().left){b.top=Math.max(b.top,b.top-b.getBoundingRect().top);b.left=Math.max(b.left,b.left-b.getBoundingRect().left)}if(b.getBoundingRect().top+b.getBoundingRect().height>b.canvas.height||b.getBoundingRect().left+b.getBoundingRect().width>b.canvas.width){b.top=Math.min(b.top,b.canvas.height-b.getBoundingRect().height+b.top-b.getBoundingRect().top);b.left=Math.min(b.left,b.canvas.width-b.getBoundingRect().width+b.left-b.getBoundingRect().left)}})},keyboardActions:function keyboardActions(){a(document).keydown(function(a){f.log("keypress",a.which);switch(a.which){case 46:k.deleteSelectedCanvasItems();break;}})},init:function init(){this.__canvas=j=new fabric.Canvas("sketch");a("body").on("contextmenu","canvas , img",function(){return!1});j.setHeight(this.canvasHeight);j.setWidth(this.canvasWidth-70);j.on({"selection:created":this.onchange,"selection:updated":this.onchange,"object:added":this.addToCache,"object:removed":this.addToCache,"object:modified":this.addToCache});localStorage.clear();this.preventMovingOutOfCanvas();this.loadToolbar();if(c.hasHorizontalRuler){this.addHorizontalRuler()}this.loadHistory();this.keyboardActions()},addToCache:function addToCache(){f.log("history");k.addCanvasToCacheBuffer()},addCanvasToCacheBuffer:function addCanvasToCacheBuffer(){if(!h){return}clearTimeout(0);setTimeout(function(){try{g++;localStorage.setItem("buffer_"+g,JSON.stringify(j))}catch(a){f.log(a)}},500)},addHorizontalRuler:function addHorizontalRuler(){var b=new fabric.Rect({width:this.canvasWidth,height:2,id:"ruler",left:0,top:410,angle:0,fill:"#8b58a1"});b.flipY=!1;b.lockMovementX=!0;b.lockScalingX=!0;b.lockScalingY=!0;b.lockUniScaling=!0;b.lockRotation=!0;j.add(b);j.renderAll();a(document).keydown(function(a){switch(a.which){case 38:b.top=b.top-10;j.renderAll();break;case 40:b.top=b.top+10;j.renderAll();break;default:return;}a.preventDefault()})},onchange:function onchange(b){if(b.target.hasOwnProperty("id")&&"ruler"===b.target.id){return}a("#colorpicker").spectrum("set",b.target.fill)}};return{initialise:function initialise(b){a.getScript(M.cfg.wwwroot+"/mod/gcanvas/javascript/spectrum.js").done(function(){d(b);i(c.debugjs);a.noConflict();a(document).ready(function(){f.log("Canvas Module v1.2");k.init()})}).fail(function(a,b,c){f.log(a);f.log(b);f.log(c)})}}});
-//# sourceMappingURL=canvas.min.js.map
+/**
+ * JS Canvas
+ *
+ * Tested in Moodle 3.9
+ *
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ *
+ * @copyright 2018 MFreak.nl
+ * @author Luuk Verhoeven
+ **/
+define("mod_gcanvas/canvas",["jquery","core/notification"],(function($,notification){var opts={id:0,debugjs:!1,hasHorizontalRuler:!0,background:""},debug={},bufferStep=0,bufferActive=!0,canvas=null,canvasModule={canvasWidth:800,canvasHeight:500,defaultShaperect:{width:70,height:70,left:200,top:50,angle:0,fill:"#ffb628"},defaultShapecircle:{radius:40,left:200,top:50,fill:"#b3cc2b"},defaultShapetriangle:{top:50,left:200,width:70,height:70,fill:"#0081b4"},defaultShapetextbox:{top:50,left:200,fill:"#0081b4"},loadHistory:function(){$.ajax({type:"POST",url:M.cfg.wwwroot+"/mod/gcanvas/ajax.php",data:{sesskey:M.cfg.sesskey,action:"load_history",data:{id:opts.id}},dataType:"json",success:function(response){debug.log(response),response.success&&$("#history").html(response.html)},error:function(response){debug.error(response.responseText),notification.addNotification({message:response.responseText,type:"error"})}})},saveCanvasAjax:function(){fabric.Canvas.supports("toDataURL")?$.ajax({type:"POST",url:M.cfg.wwwroot+"/mod/gcanvas/ajax.php",data:{sesskey:M.cfg.sesskey,action:"save_canvas",data:{id:opts.id,status:"final",canvas_data:canvas.toDataURL({multiplier:1,format:"png"}),json_data:JSON.stringify(canvas)}},dataType:"json",success:function(response){debug.log(response),response.success?(notification.addNotification({message:M.util.get_string("javascript:updated","mod_gcanvas"),type:"success"}),canvasModule.loadHistory()):notification.addNotification({message:response.error,type:"error"})},error:function(response){debug.error(response.responseText),notification.addNotification({message:response.responseText,type:"error"})}}):notification.addNotification({message:"This browser doesn't provide means to serialize canvas to an image",type:"error"})},deleteSelectedCanvasItems:function(){try{var activeobjects=canvas.getActiveObjects();if(activeobjects.length<=0)return void debug.log("Selection empty");for(var i in activeobjects)if(activeobjects.hasOwnProperty(i)){var element=activeobjects[i];if(void 0!==element.id&&"ruler"===element.id){debug.log("Ruler: Not removable!");continue}canvas.remove(element)}canvas.discardActiveObject().renderAll()}catch(e){debug.error("Nothing selected",e)}},loadDynamicToolbarMappingShapes:function(){$("#toolbar .icon[data-element-type]").on("click",(function(){var el,elementtype=$(this).data("element-type");try{canvas.discardActiveObject()}catch(e){}var shape="defaultShape"+elementtype.toLowerCase();debug.log("Search for shape: "+shape),canvasModule.hasOwnProperty(shape)?(debug.log("Shape found"),el="Textbox"===elementtype?new fabric[elementtype]("DEMO",canvasModule[shape]):new fabric[elementtype](canvasModule[shape]),canvas.add(el),canvas.setActiveObject(el)):debug.error("Shape not found!"),canvas.renderAll()}))},loadColorPicker:function(){$("#colorpicker").spectrum({showPalette:!0,palette:[],showSelectionPalette:!0,selectionPalette:["red","green","blue","orange"],flat:!1,change:function(color){debug.log("change color"),canvasModule.setColor(color)}}).on("dragstart.spectrum , dragstop.spectrum",(function(e,color){debug.log("change color - dragstop - dragstart"),canvasModule.setColor(color)}))},loadEmojiCsv:function(src){debug.log("loadEmojiCsv : ",src),fabric.Image.fromURL(src.replace(".png",".svg"),(function(object){object.set({height:500,width:500,left:150,top:100,angle:0,centerTransform:!0}).scale(.4).setCoords(),canvas.add(object),canvas.setActiveObject(object)}))},deleteAttempt:function($el){debug.log("Delete",$el),notification.confirm(M.util.get_string("javascript:confirm_title","mod_gcanvas"),M.util.get_string("javascript:confirm_desc","mod_gcanvas"),M.util.get_string("javascript:yes","mod_gcanvas"),M.util.get_string("javascript:no","mod_gcanvas"),(function(){$.ajax({type:"POST",url:M.cfg.wwwroot+"/mod/gcanvas/ajax.php",data:{sesskey:M.cfg.sesskey,action:"delete_attempt",data:{id:opts.id,attempt_id:$el.data("id")}},dataType:"json",success:function(response){debug.log(response),response.success?canvasModule.loadHistory():notification.addNotification({message:response.error,type:"error"})},error:function(response){debug.error(response.responseText),notification.addNotification({message:response.responseText,type:"error"})}})}))},restoreAttempt:function($el){debug.log("Restore",$el),$.ajax({type:"POST",url:M.cfg.wwwroot+"/mod/gcanvas/ajax.php",data:{sesskey:M.cfg.sesskey,action:"get_attempt",data:{id:opts.id,attempt_id:$el.data("id")}},dataType:"json",success:function(response){debug.log(response),response.success&&(bufferActive=!1,canvas.loadFromJSON(response.record.json_data,canvas.renderAll.bind(canvas)),setTimeout((function(){bufferActive=!0}),1e3))}})},showFileuploader:function(filearea){$("#canvas-filepicker-form-"+filearea).toggle()},setBackgroundImage:function(){""!==opts.background&&"false"!==opts.background&&fabric.Image.fromURL(opts.background,(function(img){canvas.setBackgroundImage(img,canvas.renderAll.bind(canvas),{scaleX:canvas.width/img.width,scaleY:canvas.height/img.height})}))},addUserImage:function(){var formdata={id:opts.id},inputs=$("#canvas-filepicker-form-student_image form").serializeArray();$.each(inputs,(function(i,input){formdata[input.name]=input.value})),$.ajax({type:"POST",url:M.cfg.wwwroot+"/mod/gcanvas/ajax.php",data:{sesskey:M.cfg.sesskey,action:"upload_images",data:formdata},dataType:"json",success:function(response){debug.log(response),response.success&&canvasModule.addImageFromUrl(response.image),$("#canvas-filepicker-form-student_image").hide()}})},addImageFromUrl:function(path){fabric.Image.fromURL(path,(function(object){object.set({left:150,top:100,angle:0,centerTransform:!0}).setCoords();var maxwidth=canvas.getWidth()/3;object.width>maxwidth&&object.scaleToWidth(maxwidth),canvas.add(object),canvas.setActiveObject(object)}))},selectToolbarImage:function(){var dialog=$("#image-picker");dialog.is(":visible")?dialog.hide():(dialog.show(),$.ajax({type:"POST",url:M.cfg.wwwroot+"/mod/gcanvas/ajax.php",data:{sesskey:M.cfg.sesskey,action:"get_toolbar_images",data:{id:opts.id}},dataType:"json",success:function(response){if(debug.log(response),response.success){var html='';$.each(response.images,(function(i,src){html+=''})),html+="
",dialog.html(html)}}}))},undo:function(){if(0!==bufferStep)try{var data=localStorage.getItem("buffer_"+bufferStep);canvas.loadFromJSON(data,canvas.renderAll.bind(canvas)),localStorage.removeItem("buffer_"+bufferStep),bufferStep--}catch(e){debug.log(e)}},loadToolbar:function(){this.setBackgroundImage(),this.loadDynamicToolbarMappingShapes(),this.loadColorPicker(),$("#clear").on("click",(function(){canvas.clear(),opts.hasHorizontalRuler&&canvasModule.addHorizontalRuler(),canvasModule.setBackgroundImage()})),$("#arrow i").on("click",(function(){canvasModule.loadArrowToCanvas()})),$("#trash i").on("click",(function(){canvasModule.deleteSelectedCanvasItems()})),$("#smiley i").on("click",(function(){canvasModule.loadEmojiPicker()})),$("#undo").on("click",(function(){bufferActive=!1,canvasModule.undo(),setTimeout((function(){bufferActive=!0}),500)})),$("#add-image i").on("click",(function(){canvasModule.showFileuploader("student_image")})),$("#select-a-image i").on("click",(function(){canvasModule.selectToolbarImage()})),$("#image-picker ").on("click","img",(function(){canvasModule.addImageFromUrl($(this).attr("src")),$("#image-picker").hide()})),$("#save-canvas").on("click",(function(){canvasModule.saveCanvasAjax()})),$("#show-help").on("click",(function(){$("#dialog-help").show()})),$("#dialog-help").on("click",(function(){$("#dialog-help").hide()})),$("#history").on("click",".delete",(function(e){e.preventDefault(),canvasModule.deleteAttempt($(this))})).on("click",".restore",(function(e){e.preventDefault(),canvasModule.restoreAttempt($(this))})),$("#emoji-picker").on("click","img",(function(){canvasModule.loadEmojiCsv($(this).attr("src")),$("#emoji-picker").hide()})),$("#change_background").on("click",(function(){canvasModule.showFileuploader("background")})),$("#add_toolbar_images").on("click",(function(){canvasModule.showFileuploader("toolbar_shape")})),$("#canvas-filepicker-form-student_image").on("click","#id_submitbutton",(function(e){e.preventDefault(),canvasModule.addUserImage()})),$(".dialog").on("click",".btn-secondary",(function(e){e.preventDefault(),debug.log("Cancel"),$(".dialog").hide()}))},loadEmojiPicker:function(){var $picker=$("#emoji-picker");if(""!==$picker.html())return debug.log("Toggle emoji"),void $picker.toggle();$.ajax({type:"POST",url:M.cfg.wwwroot+"/mod/gcanvas/ajax.php",data:{sesskey:M.cfg.sesskey,action:"emoji",data:{id:opts.id}},dataType:"json",success:function(response){debug.log(response),response.success&&$picker.html(response.html).show()}})},loadArrowToCanvas:function(){fabric.loadSVGFromURL("pix/arrow.svg",(function(objects,options){var obj=fabric.util.groupSVGElements(objects,options);canvas.add(obj.scale(.1)).centerObject(obj).renderAll(),obj.setCoords(),canvas.setActiveObject(obj)}))},setColor:function(color){var colorhex=color.toHexString(),activeobjects=canvas.getActiveObjects();if(activeobjects){for(var i in activeobjects)if(activeobjects.hasOwnProperty(i)){var element=activeobjects[i];if(element.hasOwnProperty("id")&&"ruler"===element.id)continue;element.set("fill",colorhex)}canvas.renderAll()}else debug.log("No active items")},preventMovingOutOfCanvas:function(){canvas.on("object:moving",(function(e){var obj=e.target;obj.currentHeight>obj.canvas.height||obj.currentWidth>obj.canvas.width||(obj.setCoords(),(obj.getBoundingRect().top<0||obj.getBoundingRect().left<0)&&(obj.top=Math.max(obj.top,obj.top-obj.getBoundingRect().top),obj.left=Math.max(obj.left,obj.left-obj.getBoundingRect().left)),(obj.getBoundingRect().top+obj.getBoundingRect().height>obj.canvas.height||obj.getBoundingRect().left+obj.getBoundingRect().width>obj.canvas.width)&&(obj.top=Math.min(obj.top,obj.canvas.height-obj.getBoundingRect().height+obj.top-obj.getBoundingRect().top),obj.left=Math.min(obj.left,obj.canvas.width-obj.getBoundingRect().width+obj.left-obj.getBoundingRect().left)))}))},keyboardActions:function(){$(document).keydown((function(e){if(debug.log("keypress",e.which),46===e.which)canvasModule.deleteSelectedCanvasItems()}))},init:function(){this.__canvas=canvas=new fabric.Canvas("sketch"),$("body").on("contextmenu","canvas , img",(function(){return!1})),canvas.setHeight(this.canvasHeight),canvas.setWidth(this.canvasWidth-70),canvas.on({"selection:created":this.onchange,"selection:updated":this.onchange,"object:added":this.addToCache,"object:removed":this.addToCache,"object:modified":this.addToCache}),localStorage.clear(),this.preventMovingOutOfCanvas(),this.loadToolbar(),opts.hasHorizontalRuler&&this.addHorizontalRuler(),this.loadHistory(),this.keyboardActions()},addToCache:function(){debug.log("history"),canvasModule.addCanvasToCacheBuffer()},addCanvasToCacheBuffer:function(){bufferActive&&(clearTimeout(0),setTimeout((function(){try{bufferStep++,localStorage.setItem("buffer_"+bufferStep,JSON.stringify(canvas))}catch(e){debug.log(e)}}),500))},addHorizontalRuler:function(){var ruler=new fabric.Rect({width:this.canvasWidth,height:2,id:"ruler",left:0,top:410,angle:0,fill:"#8b58a1"});ruler.flipY=!1,ruler.lockMovementX=!0,ruler.lockScalingX=!0,ruler.lockScalingY=!0,ruler.lockUniScaling=!0,ruler.lockRotation=!0,canvas.add(ruler),canvas.renderAll(),$(document).keydown((function(e){switch(e.which){case 38:ruler.top=ruler.top-10,canvas.renderAll();break;case 40:ruler.top=ruler.top+10,canvas.renderAll();break;default:return}e.preventDefault()}))},onchange:function(options){options.target.hasOwnProperty("id")&&"ruler"===options.target.id||$("#colorpicker").spectrum("set",options.target.fill)}};return{initialise:function(args){$.getScript(M.cfg.wwwroot+"/mod/gcanvas/javascript/spectrum.js").done((function(){!function(options){var key,vartype;for(key in opts)opts.hasOwnProperty(key)&&options.hasOwnProperty(key)&&("boolean"==(vartype=typeof opts[key])?opts[key]=Boolean(options[key]):"number"===vartype?opts[key]=Number(options[key]):"string"===vartype&&(opts[key]=String(options[key])))}(args),function(isenabled){if(isenabled)for(var m in console)"function"==typeof console[m]&&(debug[m]=console[m].bind(window.console));else for(var i in console)"function"==typeof console[i]&&(debug[i]=function(){})}(opts.debugjs),$.noConflict(),$(document).ready((function(){debug.log("Canvas Module v1.2"),canvasModule.init()}))})).fail((function(jqxhr,settings,exception){debug.log(jqxhr),debug.log(settings),debug.log(exception)}))}}}));
+
+//# sourceMappingURL=canvas.min.js.map
\ No newline at end of file
diff --git a/amd/build/canvas.min.js.map b/amd/build/canvas.min.js.map
index c6567f2..803ebcd 100644
--- a/amd/build/canvas.min.js.map
+++ b/amd/build/canvas.min.js.map
@@ -1 +1 @@
-{"version":3,"sources":["../src/canvas.js"],"names":["define","$","notification","opts","id","debugjs","hasHorizontalRuler","background","setOptions","options","key","vartype","hasOwnProperty","debug","bufferStep","bufferActive","setDebug","isenabled","m","console","bind","window","i","canvas","canvasModule","canvasWidth","canvasHeight","defaultShaperect","width","height","left","top","angle","fill","defaultShapecircle","radius","defaultShapetriangle","defaultShapetextbox","loadHistory","ajax","type","url","M","cfg","wwwroot","data","sesskey","action","dataType","success","response","log","html","error","responseText","addNotification","message","saveCanvasAjax","fabric","Canvas","supports","toDataURL","multiplier","format","JSON","stringify","util","get_string","deleteSelectedCanvasItems","activeobjects","getActiveObjects","length","element","remove","discardActiveObject","renderAll","e","loadDynamicToolbarMappingShapes","on","el","elementtype","shape","toLowerCase","add","setActiveObject","loadColorPicker","spectrum","showPalette","palette","showSelectionPalette","selectionPalette","flat","change","color","setColor","loadEmojiCsv","src","Image","fromURL","replace","object","set","centerTransform","scale","setCoords","deleteAttempt","$el","confirm","restoreAttempt","loadFromJSON","record","json_data","setTimeout","showFileuploader","filearea","toggle","setBackgroundImage","img","scaleX","scaleY","addUserImage","formdata","inputs","serializeArray","each","input","name","value","addImageFromUrl","image","hide","path","maxwidth","getWidth","scaleToWidth","selectToolbarImage","dialog","is","show","images","undo","localStorage","getItem","removeItem","loadToolbar","clear","addHorizontalRuler","loadArrowToCanvas","loadEmojiPicker","attr","preventDefault","$picker","loadSVGFromURL","objects","obj","groupSVGElements","centerObject","colorhex","toHexString","preventMovingOutOfCanvas","target","currentHeight","currentWidth","getBoundingRect","Math","max","min","keyboardActions","document","keydown","which","init","__canvas","setHeight","setWidth","onchange","addToCache","addCanvasToCacheBuffer","clearTimeout","setItem","ruler","Rect","flipY","lockMovementX","lockScalingX","lockScalingY","lockUniScaling","lockRotation","initialise","args","getScript","done","noConflict","ready","fail","jqxhr","settings","exception"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * JS Canvas\n *\n * Tested in Moodle 3.9\n *\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n *\n * @package local_commander\n * @copyright 2018 MFreak.nl\n * @author Luuk Verhoeven\n **/\n/* global fabric */\n/* eslint no-unused-expressions: \"off\", no-console:off, no-invalid-this:\"off\",no-script-url:\"off\", block-scoped-var: \"off\" */\ndefine(['jquery', 'core/notification'], function($, notification) {\n 'use strict';\n\n /**\n * Possible options\n * @type {{id: number, debugjs: boolean, hasHorizontalRuler: boolean, background: string}}\n */\n var opts = {\n id: 0,\n debugjs: false,\n hasHorizontalRuler: true,\n background: '',\n };\n\n /**\n * Set options base on listed options\n * @param {object} options\n */\n var setOptions = function(options) {\n \"use strict\";\n var key, vartype;\n for (key in opts) {\n if (opts.hasOwnProperty(key) && options.hasOwnProperty(key)) {\n\n // Casting to prevent errors.\n vartype = typeof opts[key];\n if (vartype === \"boolean\") {\n opts[key] = Boolean(options[key]);\n } else if (vartype === 'number') {\n opts[key] = Number(options[key]);\n } else if (vartype === 'string') {\n opts[key] = String(options[key]);\n }\n // Skip all other types.\n }\n }\n };\n\n /**\n * Console log debug wrapper.\n */\n var debug = {};\n\n /**\n * Local history/cache buffer\n * @type {number}\n */\n var bufferStep = 0;\n\n /**\n *\n * @type {number}\n */\n var bufferTimer = 0;\n\n /**\n * Should we store changes to localstorage\n * @type {boolean}\n */\n var bufferActive = true;\n\n /**\n * Set debug mode\n * Should only be enabled if site is in debug mode.\n * @param {boolean} isenabled\n */\n var setDebug = function(isenabled) {\n\n if (isenabled) {\n for (var m in console) {\n if (typeof console[m] == 'function') {\n debug[m] = console[m].bind(window.console);\n }\n }\n } else {\n // Fake wrapper.\n for (var i in console) {\n if (typeof console[i] == 'function') {\n debug[i] = function() {\n // Don't do anything.\n };\n }\n }\n }\n };\n\n /**\n * Canvas holder.\n * @type fabric.Canvas\n */\n var canvas = null;\n\n /**\n * Module canvas wrapper.\n */\n var canvasModule = {\n\n /**\n * @type int\n */\n canvasWidth: 800,\n\n /**\n * @type int\n */\n canvasHeight: 500,\n\n /**\n * Default rectangle.\n */\n defaultShaperect: {\n width: 70,\n height: 70,\n left: 200,\n top: 50,\n angle: 0,\n fill: '#ffb628'\n },\n\n /**\n * Default circle.\n */\n defaultShapecircle: {\n radius: 40,\n left: 200,\n top: 50,\n fill: '#b3cc2b'\n },\n\n /**\n * Default triangle.\n */\n defaultShapetriangle: {\n top: 50,\n left: 200,\n width: 70,\n height: 70,\n fill: '#0081b4'\n },\n\n /**\n * Default text.\n */\n defaultShapetextbox: {\n top: 50,\n left: 200,\n fill: '#0081b4'\n },\n\n /**\n * Load user there history.\n */\n loadHistory: function() {\n $.ajax({\n type: 'POST',\n url: M.cfg.wwwroot + '/mod/gcanvas/ajax.php',\n data: {\n sesskey: M.cfg.sesskey,\n action: 'load_history',\n data: {\n 'id': opts.id,\n }\n },\n dataType: \"json\",\n success: function(response) {\n debug.log(response);\n if (response.success) {\n $('#history').html(response.html);\n }\n },\n error: function(response) {\n debug.error(response.responseText);\n // Show a error messages.\n notification.addNotification({\n message: response.responseText,\n type: \"error\"\n });\n }\n });\n },\n\n /**\n * Save the current canvas.\n */\n saveCanvasAjax: function() {\n // Canvas to image.\n // https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL\n if (!fabric.Canvas.supports('toDataURL')) {\n\n notification.addNotification({\n message: 'This browser doesn\\'t provide means to serialize canvas to an image',\n type: \"error\"\n });\n\n } else {\n\n // Send data to AJAX.\n $.ajax({\n type: 'POST',\n url: M.cfg.wwwroot + '/mod/gcanvas/ajax.php',\n data: {\n sesskey: M.cfg.sesskey,\n action: 'save_canvas',\n data: {\n 'id': opts.id,\n 'status': 'final',\n 'canvas_data': canvas.toDataURL({\n multiplier: 1,\n format: 'png'\n }),\n 'json_data': JSON.stringify(canvas)\n }\n },\n dataType: \"json\",\n success: function(response) {\n debug.log(response);\n\n if (response.success) {\n notification.addNotification({\n message: M.util.get_string('javascript:updated', 'mod_gcanvas'),\n type: \"success\",\n });\n\n // Load attempts.\n canvasModule.loadHistory();\n\n } else {\n notification.addNotification({\n message: response.error,\n type: \"error\"\n });\n }\n },\n error: function(response) {\n debug.error(response.responseText);\n // Show a error messages.\n notification.addNotification({\n message: response.responseText,\n type: \"error\"\n });\n }\n });\n }\n },\n\n /**\n * Trash selected canvas items.\n */\n deleteSelectedCanvasItems: function() {\n try {\n var activeobjects = canvas.getActiveObjects();\n if (activeobjects.length <= 0) {\n debug.log('Selection empty');\n return;\n }\n\n for (var i in activeobjects) {\n if (activeobjects.hasOwnProperty(i)) {\n var element = activeobjects[i];\n\n if (element.id !== undefined && element.id === 'ruler') {\n debug.log('Ruler: Not removable!');\n continue;\n }\n\n canvas.remove(element);\n }\n }\n\n canvas.discardActiveObject().renderAll();\n\n } catch (e) {\n debug.error('Nothing selected', e);\n }\n },\n\n /**\n * Dynamic method for selecting a shape.\n */\n loadDynamicToolbarMappingShapes: function() {\n\n $('#toolbar .icon[data-element-type]').on('click', function() {\n var el;\n var elementtype = $(this).data('element-type');\n\n try {\n canvas.discardActiveObject();\n } catch (e) {\n // If nothing is added to the canvas this gives a error.\n }\n\n var shape = \"defaultShape\" + elementtype.toLowerCase();\n debug.log(\"Search for shape: \" + shape);\n\n if (canvasModule.hasOwnProperty(shape)) {\n debug.log(\"Shape found\");\n\n if (elementtype === 'Textbox') {\n el = new fabric[elementtype]('DEMO', canvasModule[shape]);\n } else {\n el = new fabric[elementtype](canvasModule[shape]);\n }\n\n canvas.add(el);\n canvas.setActiveObject(el);\n } else {\n debug.error('Shape not found!');\n }\n\n canvas.renderAll();\n });\n },\n\n /**\n * Colorpicker used for changing the color of a shape.\n */\n loadColorPicker: function() {\n $(\"#colorpicker\").spectrum({\n showPalette: true,\n palette: [],\n showSelectionPalette: true,\n selectionPalette: [\"red\", \"green\", \"blue\", \"orange\"],\n flat: false,\n change: function(color) {\n debug.log('change color');\n canvasModule.setColor(color);\n }\n }).on(\"dragstart.spectrum , dragstop.spectrum\", function(e, color) {\n debug.log('change color - dragstop - dragstart');\n canvasModule.setColor(color);\n }\n );\n },\n\n /**\n * Using svg making sure it looks nice.\n *\n * @param {string} src\n */\n loadEmojiCsv: function(src) {\n debug.log('loadEmojiCsv : ', src);\n // Issue when using loadsvgfromurl.\n fabric.Image.fromURL(src.replace('.png', '.svg'), function(object) {\n // Logic below needed to work with the svg files.\n object.set({\n height: 500,\n width: 500,\n left: 150,\n top: 100,\n angle: 0,\n centerTransform: true\n }).scale(0.4).setCoords();\n canvas.add(object);\n canvas.setActiveObject(object);\n });\n },\n\n /**\n * Delete a attempt/sketch\n * TODO we could better use amd Ajax helper Moodle and external webservice, for now this is okay.\n *\n * @param {jQuery} $el\n */\n deleteAttempt: function($el) {\n debug.log('Delete', $el);\n notification.confirm(\n M.util.get_string('javascript:confirm_title', 'mod_gcanvas'),\n M.util.get_string('javascript:confirm_desc', 'mod_gcanvas'),\n M.util.get_string('javascript:yes', 'mod_gcanvas'),\n M.util.get_string('javascript:no', 'mod_gcanvas'), function() {\n\n $.ajax({\n type: 'POST',\n url: M.cfg.wwwroot + '/mod/gcanvas/ajax.php',\n data: {\n sesskey: M.cfg.sesskey,\n action: 'delete_attempt',\n data: {\n 'id': opts.id,\n 'attempt_id': $el.data('id'),\n }\n },\n dataType: \"json\",\n success: function(response) {\n debug.log(response);\n\n if (response.success) {\n // Load attempts.\n canvasModule.loadHistory();\n\n } else {\n notification.addNotification({\n message: response.error,\n type: \"error\"\n });\n }\n },\n error: function(response) {\n debug.error(response.responseText);\n // Show a error messages.\n notification.addNotification({\n message: response.responseText,\n type: \"error\"\n });\n }\n });\n });\n },\n\n /**\n * Restore a attempt/sketch\n *\n * @param {jQuery} $el\n */\n restoreAttempt: function($el) {\n debug.log('Restore', $el);\n $.ajax({\n type: 'POST',\n url: M.cfg.wwwroot + '/mod/gcanvas/ajax.php',\n data: {\n sesskey: M.cfg.sesskey,\n action: 'get_attempt',\n data: {\n 'id': opts.id,\n 'attempt_id': $el.data('id'),\n }\n },\n dataType: \"json\",\n success: function(response) {\n debug.log(response);\n\n if (response.success) {\n bufferActive = false;\n\n canvas.loadFromJSON(response.record.json_data, canvas.renderAll.bind(canvas));\n\n setTimeout(function() {\n bufferActive = true;\n }, 1000);\n }\n }\n });\n },\n\n /**\n * Show the fileuploader.\n *\n * @param {string} filearea\n */\n showFileuploader: function(filearea) {\n $('#canvas-filepicker-form-' + filearea).toggle();\n },\n\n /**\n * Set background of the canvas.\n */\n setBackgroundImage: function() {\n\n if (opts.background !== '') {\n fabric.Image.fromURL(opts.background, function(img) {\n // Add background image.\n canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas), {\n scaleX: canvas.width / img.width,\n scaleY: canvas.height / img.height\n });\n });\n }\n },\n\n /**\n * Add user image.\n */\n addUserImage: function() {\n var formdata = {'id': opts.id},\n inputs = $('#canvas-filepicker-form-student_image form').serializeArray();\n\n $.each(inputs, function(i, input) {\n formdata[input.name] = input.value;\n });\n\n $.ajax({\n type: 'POST',\n url: M.cfg.wwwroot + '/mod/gcanvas/ajax.php',\n data: {\n sesskey: M.cfg.sesskey,\n action: 'upload_images',\n data: formdata\n },\n dataType: \"json\",\n success: function(response) {\n debug.log(response);\n\n if (response.success) {\n canvasModule.addImageFromUrl(response.image);\n }\n $('#canvas-filepicker-form-student_image').hide();\n }\n });\n },\n\n /**\n * Add image from url\n * TODO SVG support.\n * @param {string} path\n */\n addImageFromUrl: function(path) {\n\n fabric.Image.fromURL(path, function(object) {\n object.set({\n left: 150,\n top: 100,\n angle: 0,\n centerTransform: true\n }).setCoords();\n\n // Prevent to large objects.\n var maxwidth = canvas.getWidth() / 3;\n\n if (object.width > maxwidth) {\n object.scaleToWidth(maxwidth);\n }\n\n canvas.add(object);\n canvas.setActiveObject(object);\n });\n },\n\n /**\n * Select toolbar images.\n */\n selectToolbarImage: function() {\n var dialog = $('#image-picker');\n if (dialog.is(':visible')) {\n dialog.hide();\n return;\n }\n dialog.show();\n\n $.ajax({\n type: 'POST',\n url: M.cfg.wwwroot + '/mod/gcanvas/ajax.php',\n data: {\n sesskey: M.cfg.sesskey,\n action: 'get_toolbar_images',\n data: {\n 'id': opts.id,\n }\n },\n dataType: \"json\",\n success: function(response) {\n debug.log(response);\n\n if (response.success) {\n var html = '';\n $.each(response.images, function(i, src) {\n html += '';\n });\n html += '
';\n\n dialog.html(html);\n }\n }\n });\n },\n\n /**\n * Undo 1 canvas step.\n */\n undo: function() {\n\n if (bufferStep === 0) {\n return;\n }\n try {\n var data = localStorage.getItem('buffer_' + bufferStep);\n canvas.loadFromJSON(data, canvas.renderAll.bind(canvas));\n\n localStorage.removeItem('buffer_' + bufferStep);\n\n bufferStep--;\n } catch (e) {\n debug.log(e);\n }\n },\n\n /**\n * Toolbar actions.\n */\n loadToolbar: function() {\n\n this.setBackgroundImage();\n\n // Most shapes will be placed on canvas by this function.\n this.loadDynamicToolbarMappingShapes();\n\n // Color picker.\n this.loadColorPicker();\n\n // Clear canvas items.\n $('#clear').on('click', function() {\n canvas.clear();\n\n if (opts.hasHorizontalRuler) {\n canvasModule.addHorizontalRuler();\n }\n\n canvasModule.setBackgroundImage();\n });\n\n // Arrow.\n $('#arrow i').on('click', function() {\n canvasModule.loadArrowToCanvas();\n });\n\n // Remove selected items.\n $('#trash i').on('click', function() {\n canvasModule.deleteSelectedCanvasItems();\n });\n\n // Load emoji picker.\n $('#smiley i').on('click', function() {\n canvasModule.loadEmojiPicker();\n });\n\n $('#undo').on('click', function() {\n bufferActive = false;\n canvasModule.undo();\n\n setTimeout(function() {\n bufferActive = true;\n }, 500);\n });\n\n // Add own image to the canvas.\n $('#add-image i').on('click', function() {\n canvasModule.showFileuploader('student_image');\n });\n\n // Student selecting a image, added to the toolbar by teachers.\n $('#select-a-image i').on('click', function() {\n canvasModule.selectToolbarImage();\n });\n\n $('#image-picker ').on('click', 'img', function() {\n canvasModule.addImageFromUrl($(this).attr('src'));\n $('#image-picker').hide();\n });\n\n $('#save-canvas').on('click', function() {\n canvasModule.saveCanvasAjax();\n });\n\n $('#show-help').on('click', function() {\n $('#dialog-help').show();\n });\n\n $('#dialog-help').on('click', function() {\n $('#dialog-help').hide();\n });\n\n $('#history').on('click', '.delete', function(e) {\n e.preventDefault();\n canvasModule.deleteAttempt($(this));\n\n }).on('click', '.restore', function(e) {\n\n e.preventDefault();\n canvasModule.restoreAttempt($(this));\n\n });\n\n // Dialog to selected a emoji.\n $('#emoji-picker').on('click', 'img', function() {\n canvasModule.loadEmojiCsv($(this).attr('src'));\n $('#emoji-picker').hide();\n });\n\n // Teacher background.\n $('#change_background').on('click', function() {\n canvasModule.showFileuploader('background');\n });\n\n // Teacher image.\n $('#add_toolbar_images').on('click', function() {\n canvasModule.showFileuploader('toolbar_shape');\n });\n\n $('#canvas-filepicker-form-student_image').on('click', '#id_submitbutton', function(e) {\n e.preventDefault();\n\n // Form should be posted to AJAX call so we can get the image.\n canvasModule.addUserImage();\n });\n\n // Hide pressing on cancel.\n $('.dialog').on('click', '.btn-secondary', function(e) {\n e.preventDefault();\n debug.log('Cancel');\n $('.dialog').hide();\n });\n },\n\n /**\n * Emoji dialog\n */\n loadEmojiPicker: function() {\n var $picker = $('#emoji-picker');\n if ($picker.html() !== '') {\n debug.log('Toggle emoji');\n $picker.toggle();\n return;\n }\n\n $.ajax({\n type: 'POST',\n url: M.cfg.wwwroot + '/mod/gcanvas/ajax.php',\n data: {\n sesskey: M.cfg.sesskey,\n action: 'emoji',\n data: {\n 'id': opts.id,\n }\n },\n dataType: \"json\",\n success: function(response) {\n debug.log(response);\n\n if (response.success) {\n $picker.html(response.html).show();\n }\n }\n });\n },\n\n /**\n * Load the arrow icon to the canvas.\n */\n loadArrowToCanvas: function() {\n fabric.loadSVGFromURL('pix/arrow.svg', function(objects, options) {\n var obj = fabric.util.groupSVGElements(objects, options);\n canvas.add(obj.scale(0.1)).centerObject(obj).renderAll();\n obj.setCoords();\n canvas.setActiveObject(obj);\n });\n },\n\n /**\n * Set active element colors.\n * @param {object} color\n */\n setColor: function(color) {\n\n var colorhex = color.toHexString(); // #ff0000\n var activeobjects = canvas.getActiveObjects();\n if (activeobjects) {\n\n for (var i in activeobjects) {\n if (activeobjects.hasOwnProperty(i)) {\n var element = activeobjects[i];\n\n if (element.hasOwnProperty('id') && element.id === 'ruler') {\n continue;\n }\n\n element.set(\"fill\", colorhex);\n }\n }\n\n canvas.renderAll();\n } else {\n debug.log('No active items');\n }\n },\n\n /**\n * Don't allow going out of canvas.\n */\n preventMovingOutOfCanvas: function() {\n canvas.on('object:moving', function(e) {\n var obj = e.target;\n // If object is too big ignore.\n if (obj.currentHeight > obj.canvas.height || obj.currentWidth > obj.canvas.width) {\n return;\n }\n obj.setCoords();\n\n if (obj.getBoundingRect().top < 0 || obj.getBoundingRect().left < 0) {\n obj.top = Math.max(obj.top, obj.top - obj.getBoundingRect().top);\n obj.left = Math.max(obj.left, obj.left - obj.getBoundingRect().left);\n }\n\n if (obj.getBoundingRect().top + obj.getBoundingRect().height > obj.canvas.height ||\n obj.getBoundingRect().left + obj.getBoundingRect().width > obj.canvas.width) {\n obj.top = Math.min(obj.top, obj.canvas.height - obj.getBoundingRect().height + obj.top -\n obj.getBoundingRect().top);\n obj.left = Math.min(obj.left, obj.canvas.width - obj.getBoundingRect().width + obj.left -\n obj.getBoundingRect().left);\n }\n });\n },\n\n /**\n * Trigger for extra keyboard commands.\n */\n keyboardActions: function() {\n $(document).keydown(function(e) {\n debug.log('keypress', e.which);\n switch (e.which) {\n case 46:\n canvasModule.deleteSelectedCanvasItems();\n break;\n }\n });\n },\n\n /**\n * Start this module.\n */\n init: function() {\n\n // Load canvas.\n this.__canvas = canvas = new fabric.Canvas('sketch');\n\n // Prevent right click.\n $('body').on('contextmenu', 'canvas , img', function() {\n return false;\n });\n\n // Dimensions.\n canvas.setHeight(this.canvasHeight);\n canvas.setWidth(this.canvasWidth - 70);\n\n // Catch some actions.\n canvas.on({\n 'selection:created': this.onchange,\n 'selection:updated': this.onchange,\n\n // 'object:moving' : this.add_to_history,\n 'object:added': this.addToCache,\n 'object:removed': this.addToCache,\n 'object:modified': this.addToCache,\n });\n\n // Make sure we start with a empty storage.\n localStorage.clear();\n\n this.preventMovingOutOfCanvas();\n\n this.loadToolbar();\n\n if (opts.hasHorizontalRuler) {\n this.addHorizontalRuler();\n }\n\n this.loadHistory();\n\n this.keyboardActions();\n },\n\n /**\n * Keep a history/cache buffer.\n */\n addToCache: function() {\n debug.log('history');\n canvasModule.addCanvasToCacheBuffer();\n },\n\n /**\n * Add canvas to local storage.\n */\n addCanvasToCacheBuffer: function() {\n // When undo there lot of modified events we doesnt want to trigger if thats the case.\n if (!bufferActive) {\n return;\n }\n\n clearTimeout(bufferTimer);\n setTimeout(function() {\n try {\n bufferStep++;\n localStorage.setItem('buffer_' + bufferStep, JSON.stringify(canvas));\n } catch (e) {\n debug.log(e);\n }\n }, 500);\n },\n\n /**\n * Horizontal ruler.\n */\n addHorizontalRuler: function() {\n var ruler = new fabric.Rect({\n width: this.canvasWidth,\n height: 2,\n id: 'ruler',\n left: 0,\n top: 410,\n angle: 0,\n fill: '#8b58a1'\n });\n\n ruler.flipY = false;\n ruler.lockMovementX = true;\n ruler.lockScalingX = true;\n ruler.lockScalingY = true;\n ruler.lockUniScaling = true;\n ruler.lockRotation = true;\n\n canvas.add(ruler);\n canvas.renderAll();\n\n // Keyboard arrows move ruler.\n $(document).keydown(function(e) {\n switch (e.which) {\n\n case 38: // Up arrow.\n ruler.top = ruler.top - 10;\n canvas.renderAll();\n break;\n\n case 40: // Down arrow.\n ruler.top = ruler.top + 10;\n canvas.renderAll();\n\n break;\n\n default:\n return; // Exit this handler for other keys.\n }\n e.preventDefault(); // Prevent the default action (scroll / move caret).\n });\n },\n\n /**\n * Trigger on canvas element actions.\n * @param {object} options\n */\n onchange: function(options) {\n if (options.target.hasOwnProperty('id') && options.target.id === 'ruler') {\n return;\n }\n\n $(\"#colorpicker\").spectrum(\"set\", options.target.fill);\n }\n };\n\n return {\n\n /**\n * Init\n * @param {object} args\n */\n initialise: function(args) {\n\n // Load spectrum javascript form here.\n $.getScript(M.cfg.wwwroot + \"/mod/gcanvas/javascript/spectrum.js\").done(function() {\n\n // Load the args passed from PHP.\n setOptions(args);\n\n // Set internal debug console.\n setDebug(opts.debugjs);\n\n $.noConflict();\n $(document).ready(function() {\n debug.log('Canvas Module v1.2');\n canvasModule.init();\n });\n }).fail(function(jqxhr, settings, exception) {\n // Display loading issue in console.\n debug.log(jqxhr);\n debug.log(settings);\n debug.log(exception);\n });\n }\n };\n});"],"file":"canvas.min.js"}
\ No newline at end of file
+{"version":3,"file":"canvas.min.js","sources":["../src/canvas.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * JS Canvas\n *\n * Tested in Moodle 3.9\n *\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n *\n * @copyright 2018 MFreak.nl\n * @author Luuk Verhoeven\n **/\n/* global fabric */\n/* eslint no-unused-expressions: \"off\", no-console:off, no-invalid-this:\"off\",no-script-url:\"off\", block-scoped-var: \"off\" */\ndefine(['jquery', 'core/notification'], function($, notification) {\n 'use strict';\n\n /**\n * Possible options\n * @type {{id: number, debugjs: boolean, hasHorizontalRuler: boolean, background: string}}\n */\n var opts = {\n id: 0,\n debugjs: false,\n hasHorizontalRuler: true,\n background: '',\n };\n\n /**\n * Set options base on listed options\n * @param {object} options\n */\n var setOptions = function(options) {\n \"use strict\";\n var key, vartype;\n for (key in opts) {\n if (opts.hasOwnProperty(key) && options.hasOwnProperty(key)) {\n\n // Casting to prevent errors.\n vartype = typeof opts[key];\n if (vartype === \"boolean\") {\n opts[key] = Boolean(options[key]);\n } else if (vartype === 'number') {\n opts[key] = Number(options[key]);\n } else if (vartype === 'string') {\n opts[key] = String(options[key]);\n }\n // Skip all other types.\n }\n }\n };\n\n /**\n * Console log debug wrapper.\n */\n var debug = {};\n\n /**\n * Local history/cache buffer\n * @type {number}\n */\n var bufferStep = 0;\n\n /**\n *\n * @type {number}\n */\n var bufferTimer = 0;\n\n /**\n * Should we store changes to localstorage\n * @type {boolean}\n */\n var bufferActive = true;\n\n /**\n * Set debug mode\n * Should only be enabled if site is in debug mode.\n * @param {boolean} isenabled\n */\n var setDebug = function(isenabled) {\n\n if (isenabled) {\n for (var m in console) {\n if (typeof console[m] == 'function') {\n debug[m] = console[m].bind(window.console);\n }\n }\n } else {\n // Fake wrapper.\n for (var i in console) {\n if (typeof console[i] == 'function') {\n debug[i] = function() {\n // Don't do anything.\n };\n }\n }\n }\n };\n\n /**\n * Canvas holder.\n * @type fabric.Canvas\n */\n var canvas = null;\n\n /**\n * Module canvas wrapper.\n */\n var canvasModule = {\n\n /**\n * @type int\n */\n canvasWidth: 800,\n\n /**\n * @type int\n */\n canvasHeight: 500,\n\n /**\n * Default rectangle.\n */\n defaultShaperect: {\n width: 70,\n height: 70,\n left: 200,\n top: 50,\n angle: 0,\n fill: '#ffb628'\n },\n\n /**\n * Default circle.\n */\n defaultShapecircle: {\n radius: 40,\n left: 200,\n top: 50,\n fill: '#b3cc2b'\n },\n\n /**\n * Default triangle.\n */\n defaultShapetriangle: {\n top: 50,\n left: 200,\n width: 70,\n height: 70,\n fill: '#0081b4'\n },\n\n /**\n * Default text.\n */\n defaultShapetextbox: {\n top: 50,\n left: 200,\n fill: '#0081b4'\n },\n\n /**\n * Load user there history.\n */\n loadHistory: function() {\n $.ajax({\n type: 'POST',\n url: M.cfg.wwwroot + '/mod/gcanvas/ajax.php',\n data: {\n sesskey: M.cfg.sesskey,\n action: 'load_history',\n data: {\n 'id': opts.id,\n }\n },\n dataType: \"json\",\n success: function(response) {\n debug.log(response);\n if (response.success) {\n $('#history').html(response.html);\n }\n },\n error: function(response) {\n debug.error(response.responseText);\n // Show a error messages.\n notification.addNotification({\n message: response.responseText,\n type: \"error\"\n });\n }\n });\n },\n\n /**\n * Save the current canvas.\n */\n saveCanvasAjax: function() {\n // Canvas to image.\n // https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL\n if (!fabric.Canvas.supports('toDataURL')) {\n\n notification.addNotification({\n message: 'This browser doesn\\'t provide means to serialize canvas to an image',\n type: \"error\"\n });\n\n } else {\n\n // Send data to AJAX.\n $.ajax({\n type: 'POST',\n url: M.cfg.wwwroot + '/mod/gcanvas/ajax.php',\n data: {\n sesskey: M.cfg.sesskey,\n action: 'save_canvas',\n data: {\n 'id': opts.id,\n 'status': 'final',\n 'canvas_data': canvas.toDataURL({\n multiplier: 1,\n format: 'png'\n }),\n 'json_data': JSON.stringify(canvas)\n }\n },\n dataType: \"json\",\n success: function(response) {\n debug.log(response);\n\n if (response.success) {\n notification.addNotification({\n message: M.util.get_string('javascript:updated', 'mod_gcanvas'),\n type: \"success\",\n });\n\n // Load attempts.\n canvasModule.loadHistory();\n\n } else {\n notification.addNotification({\n message: response.error,\n type: \"error\"\n });\n }\n },\n error: function(response) {\n debug.error(response.responseText);\n // Show a error messages.\n notification.addNotification({\n message: response.responseText,\n type: \"error\"\n });\n }\n });\n }\n },\n\n /**\n * Trash selected canvas items.\n */\n deleteSelectedCanvasItems: function() {\n try {\n var activeobjects = canvas.getActiveObjects();\n if (activeobjects.length <= 0) {\n debug.log('Selection empty');\n return;\n }\n\n for (var i in activeobjects) {\n if (activeobjects.hasOwnProperty(i)) {\n var element = activeobjects[i];\n\n if (element.id !== undefined && element.id === 'ruler') {\n debug.log('Ruler: Not removable!');\n continue;\n }\n\n canvas.remove(element);\n }\n }\n\n canvas.discardActiveObject().renderAll();\n\n } catch (e) {\n debug.error('Nothing selected', e);\n }\n },\n\n /**\n * Dynamic method for selecting a shape.\n */\n loadDynamicToolbarMappingShapes: function() {\n\n $('#toolbar .icon[data-element-type]').on('click', function() {\n var el;\n var elementtype = $(this).data('element-type');\n\n try {\n canvas.discardActiveObject();\n } catch (e) {\n // If nothing is added to the canvas this gives a error.\n }\n\n var shape = \"defaultShape\" + elementtype.toLowerCase();\n debug.log(\"Search for shape: \" + shape);\n\n if (canvasModule.hasOwnProperty(shape)) {\n debug.log(\"Shape found\");\n\n if (elementtype === 'Textbox') {\n el = new fabric[elementtype]('DEMO', canvasModule[shape]);\n } else {\n el = new fabric[elementtype](canvasModule[shape]);\n }\n\n canvas.add(el);\n canvas.setActiveObject(el);\n } else {\n debug.error('Shape not found!');\n }\n\n canvas.renderAll();\n });\n },\n\n /**\n * Colorpicker used for changing the color of a shape.\n */\n loadColorPicker: function() {\n $(\"#colorpicker\").spectrum({\n showPalette: true,\n palette: [],\n showSelectionPalette: true,\n selectionPalette: [\"red\", \"green\", \"blue\", \"orange\"],\n flat: false,\n change: function(color) {\n debug.log('change color');\n canvasModule.setColor(color);\n }\n }).on(\"dragstart.spectrum , dragstop.spectrum\", function(e, color) {\n debug.log('change color - dragstop - dragstart');\n canvasModule.setColor(color);\n }\n );\n },\n\n /**\n * Using svg making sure it looks nice.\n *\n * @param {string} src\n */\n loadEmojiCsv: function(src) {\n debug.log('loadEmojiCsv : ', src);\n // Issue when using loadsvgfromurl.\n fabric.Image.fromURL(src.replace('.png', '.svg'), function(object) {\n // Logic below needed to work with the svg files.\n object.set({\n height: 500,\n width: 500,\n left: 150,\n top: 100,\n angle: 0,\n centerTransform: true\n }).scale(0.4).setCoords();\n canvas.add(object);\n canvas.setActiveObject(object);\n });\n },\n\n /**\n * Delete a attempt/sketch\n * TODO we could better use amd Ajax helper Moodle and external webservice, for now this is okay.\n *\n * @param {jQuery} $el\n */\n deleteAttempt: function($el) {\n debug.log('Delete', $el);\n notification.confirm(\n M.util.get_string('javascript:confirm_title', 'mod_gcanvas'),\n M.util.get_string('javascript:confirm_desc', 'mod_gcanvas'),\n M.util.get_string('javascript:yes', 'mod_gcanvas'),\n M.util.get_string('javascript:no', 'mod_gcanvas'), function() {\n\n $.ajax({\n type: 'POST',\n url: M.cfg.wwwroot + '/mod/gcanvas/ajax.php',\n data: {\n sesskey: M.cfg.sesskey,\n action: 'delete_attempt',\n data: {\n 'id': opts.id,\n 'attempt_id': $el.data('id'),\n }\n },\n dataType: \"json\",\n success: function(response) {\n debug.log(response);\n\n if (response.success) {\n // Load attempts.\n canvasModule.loadHistory();\n\n } else {\n notification.addNotification({\n message: response.error,\n type: \"error\"\n });\n }\n },\n error: function(response) {\n debug.error(response.responseText);\n // Show a error messages.\n notification.addNotification({\n message: response.responseText,\n type: \"error\"\n });\n }\n });\n });\n },\n\n /**\n * Restore a attempt/sketch\n *\n * @param {jQuery} $el\n */\n restoreAttempt: function($el) {\n debug.log('Restore', $el);\n $.ajax({\n type: 'POST',\n url: M.cfg.wwwroot + '/mod/gcanvas/ajax.php',\n data: {\n sesskey: M.cfg.sesskey,\n action: 'get_attempt',\n data: {\n 'id': opts.id,\n 'attempt_id': $el.data('id'),\n }\n },\n dataType: \"json\",\n success: function(response) {\n debug.log(response);\n\n if (response.success) {\n bufferActive = false;\n\n canvas.loadFromJSON(response.record.json_data, canvas.renderAll.bind(canvas));\n\n setTimeout(function() {\n bufferActive = true;\n }, 1000);\n }\n }\n });\n },\n\n /**\n * Show the fileuploader.\n *\n * @param {string} filearea\n */\n showFileuploader: function(filearea) {\n $('#canvas-filepicker-form-' + filearea).toggle();\n },\n\n /**\n * Set background of the canvas.\n */\n setBackgroundImage: function() {\n\n if (opts.background !== '' && opts.background !== 'false') {\n fabric.Image.fromURL(opts.background, function(img) {\n // Add background image.\n canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas), {\n scaleX: canvas.width / img.width,\n scaleY: canvas.height / img.height\n });\n });\n }\n },\n\n /**\n * Add user image.\n */\n addUserImage: function() {\n var formdata = {'id': opts.id},\n inputs = $('#canvas-filepicker-form-student_image form').serializeArray();\n\n $.each(inputs, function(i, input) {\n formdata[input.name] = input.value;\n });\n\n $.ajax({\n type: 'POST',\n url: M.cfg.wwwroot + '/mod/gcanvas/ajax.php',\n data: {\n sesskey: M.cfg.sesskey,\n action: 'upload_images',\n data: formdata\n },\n dataType: \"json\",\n success: function(response) {\n debug.log(response);\n\n if (response.success) {\n canvasModule.addImageFromUrl(response.image);\n }\n $('#canvas-filepicker-form-student_image').hide();\n }\n });\n },\n\n /**\n * Add image from url\n * TODO SVG support.\n * @param {string} path\n */\n addImageFromUrl: function(path) {\n\n fabric.Image.fromURL(path, function(object) {\n object.set({\n left: 150,\n top: 100,\n angle: 0,\n centerTransform: true\n }).setCoords();\n\n // Prevent to large objects.\n var maxwidth = canvas.getWidth() / 3;\n\n if (object.width > maxwidth) {\n object.scaleToWidth(maxwidth);\n }\n\n canvas.add(object);\n canvas.setActiveObject(object);\n });\n },\n\n /**\n * Select toolbar images.\n */\n selectToolbarImage: function() {\n var dialog = $('#image-picker');\n if (dialog.is(':visible')) {\n dialog.hide();\n return;\n }\n dialog.show();\n\n $.ajax({\n type: 'POST',\n url: M.cfg.wwwroot + '/mod/gcanvas/ajax.php',\n data: {\n sesskey: M.cfg.sesskey,\n action: 'get_toolbar_images',\n data: {\n 'id': opts.id,\n }\n },\n dataType: \"json\",\n success: function(response) {\n debug.log(response);\n\n if (response.success) {\n var html = '';\n $.each(response.images, function(i, src) {\n html += '';\n });\n html += '
';\n\n dialog.html(html);\n }\n }\n });\n },\n\n /**\n * Undo 1 canvas step.\n */\n undo: function() {\n\n if (bufferStep === 0) {\n return;\n }\n try {\n var data = localStorage.getItem('buffer_' + bufferStep);\n canvas.loadFromJSON(data, canvas.renderAll.bind(canvas));\n\n localStorage.removeItem('buffer_' + bufferStep);\n\n bufferStep--;\n } catch (e) {\n debug.log(e);\n }\n },\n\n /**\n * Toolbar actions.\n */\n loadToolbar: function() {\n\n this.setBackgroundImage();\n\n // Most shapes will be placed on canvas by this function.\n this.loadDynamicToolbarMappingShapes();\n\n // Color picker.\n this.loadColorPicker();\n\n // Clear canvas items.\n $('#clear').on('click', function() {\n canvas.clear();\n\n if (opts.hasHorizontalRuler) {\n canvasModule.addHorizontalRuler();\n }\n\n canvasModule.setBackgroundImage();\n });\n\n // Arrow.\n $('#arrow i').on('click', function() {\n canvasModule.loadArrowToCanvas();\n });\n\n // Remove selected items.\n $('#trash i').on('click', function() {\n canvasModule.deleteSelectedCanvasItems();\n });\n\n // Load emoji picker.\n $('#smiley i').on('click', function() {\n canvasModule.loadEmojiPicker();\n });\n\n $('#undo').on('click', function() {\n bufferActive = false;\n canvasModule.undo();\n\n setTimeout(function() {\n bufferActive = true;\n }, 500);\n });\n\n // Add own image to the canvas.\n $('#add-image i').on('click', function() {\n canvasModule.showFileuploader('student_image');\n });\n\n // Student selecting a image, added to the toolbar by teachers.\n $('#select-a-image i').on('click', function() {\n canvasModule.selectToolbarImage();\n });\n\n $('#image-picker ').on('click', 'img', function() {\n canvasModule.addImageFromUrl($(this).attr('src'));\n $('#image-picker').hide();\n });\n\n $('#save-canvas').on('click', function() {\n canvasModule.saveCanvasAjax();\n });\n\n $('#show-help').on('click', function() {\n $('#dialog-help').show();\n });\n\n $('#dialog-help').on('click', function() {\n $('#dialog-help').hide();\n });\n\n $('#history').on('click', '.delete', function(e) {\n e.preventDefault();\n canvasModule.deleteAttempt($(this));\n\n }).on('click', '.restore', function(e) {\n\n e.preventDefault();\n canvasModule.restoreAttempt($(this));\n\n });\n\n // Dialog to selected a emoji.\n $('#emoji-picker').on('click', 'img', function() {\n canvasModule.loadEmojiCsv($(this).attr('src'));\n $('#emoji-picker').hide();\n });\n\n // Teacher background.\n $('#change_background').on('click', function() {\n canvasModule.showFileuploader('background');\n });\n\n // Teacher image.\n $('#add_toolbar_images').on('click', function() {\n canvasModule.showFileuploader('toolbar_shape');\n });\n\n $('#canvas-filepicker-form-student_image').on('click', '#id_submitbutton', function(e) {\n e.preventDefault();\n\n // Form should be posted to AJAX call so we can get the image.\n canvasModule.addUserImage();\n });\n\n // Hide pressing on cancel.\n $('.dialog').on('click', '.btn-secondary', function(e) {\n e.preventDefault();\n debug.log('Cancel');\n $('.dialog').hide();\n });\n },\n\n /**\n * Emoji dialog\n */\n loadEmojiPicker: function() {\n var $picker = $('#emoji-picker');\n if ($picker.html() !== '') {\n debug.log('Toggle emoji');\n $picker.toggle();\n return;\n }\n\n $.ajax({\n type: 'POST',\n url: M.cfg.wwwroot + '/mod/gcanvas/ajax.php',\n data: {\n sesskey: M.cfg.sesskey,\n action: 'emoji',\n data: {\n 'id': opts.id,\n }\n },\n dataType: \"json\",\n success: function(response) {\n debug.log(response);\n\n if (response.success) {\n $picker.html(response.html).show();\n }\n }\n });\n },\n\n /**\n * Load the arrow icon to the canvas.\n */\n loadArrowToCanvas: function() {\n fabric.loadSVGFromURL('pix/arrow.svg', function(objects, options) {\n var obj = fabric.util.groupSVGElements(objects, options);\n canvas.add(obj.scale(0.1)).centerObject(obj).renderAll();\n obj.setCoords();\n canvas.setActiveObject(obj);\n });\n },\n\n /**\n * Set active element colors.\n * @param {object} color\n */\n setColor: function(color) {\n\n var colorhex = color.toHexString(); // #ff0000\n var activeobjects = canvas.getActiveObjects();\n if (activeobjects) {\n\n for (var i in activeobjects) {\n if (activeobjects.hasOwnProperty(i)) {\n var element = activeobjects[i];\n\n if (element.hasOwnProperty('id') && element.id === 'ruler') {\n continue;\n }\n\n element.set(\"fill\", colorhex);\n }\n }\n\n canvas.renderAll();\n } else {\n debug.log('No active items');\n }\n },\n\n /**\n * Don't allow going out of canvas.\n */\n preventMovingOutOfCanvas: function() {\n canvas.on('object:moving', function(e) {\n var obj = e.target;\n // If object is too big ignore.\n if (obj.currentHeight > obj.canvas.height || obj.currentWidth > obj.canvas.width) {\n return;\n }\n obj.setCoords();\n\n if (obj.getBoundingRect().top < 0 || obj.getBoundingRect().left < 0) {\n obj.top = Math.max(obj.top, obj.top - obj.getBoundingRect().top);\n obj.left = Math.max(obj.left, obj.left - obj.getBoundingRect().left);\n }\n\n if (obj.getBoundingRect().top + obj.getBoundingRect().height > obj.canvas.height ||\n obj.getBoundingRect().left + obj.getBoundingRect().width > obj.canvas.width) {\n obj.top = Math.min(obj.top, obj.canvas.height - obj.getBoundingRect().height + obj.top -\n obj.getBoundingRect().top);\n obj.left = Math.min(obj.left, obj.canvas.width - obj.getBoundingRect().width + obj.left -\n obj.getBoundingRect().left);\n }\n });\n },\n\n /**\n * Trigger for extra keyboard commands.\n */\n keyboardActions: function() {\n $(document).keydown(function(e) {\n debug.log('keypress', e.which);\n switch (e.which) {\n case 46:\n canvasModule.deleteSelectedCanvasItems();\n break;\n }\n });\n },\n\n /**\n * Start this module.\n */\n init: function() {\n\n // Load canvas.\n this.__canvas = canvas = new fabric.Canvas('sketch');\n\n // Prevent right click.\n $('body').on('contextmenu', 'canvas , img', function() {\n return false;\n });\n\n // Dimensions.\n canvas.setHeight(this.canvasHeight);\n canvas.setWidth(this.canvasWidth - 70);\n\n // Catch some actions.\n canvas.on({\n 'selection:created': this.onchange,\n 'selection:updated': this.onchange,\n\n // 'object:moving' : this.add_to_history,\n 'object:added': this.addToCache,\n 'object:removed': this.addToCache,\n 'object:modified': this.addToCache,\n });\n\n // Make sure we start with a empty storage.\n localStorage.clear();\n\n this.preventMovingOutOfCanvas();\n\n this.loadToolbar();\n\n if (opts.hasHorizontalRuler) {\n this.addHorizontalRuler();\n }\n\n this.loadHistory();\n\n this.keyboardActions();\n },\n\n /**\n * Keep a history/cache buffer.\n */\n addToCache: function() {\n debug.log('history');\n canvasModule.addCanvasToCacheBuffer();\n },\n\n /**\n * Add canvas to local storage.\n */\n addCanvasToCacheBuffer: function() {\n // When undo there lot of modified events we doesnt want to trigger if thats the case.\n if (!bufferActive) {\n return;\n }\n\n clearTimeout(bufferTimer);\n setTimeout(function() {\n try {\n bufferStep++;\n localStorage.setItem('buffer_' + bufferStep, JSON.stringify(canvas));\n } catch (e) {\n debug.log(e);\n }\n }, 500);\n },\n\n /**\n * Horizontal ruler.\n */\n addHorizontalRuler: function() {\n var ruler = new fabric.Rect({\n width: this.canvasWidth,\n height: 2,\n id: 'ruler',\n left: 0,\n top: 410,\n angle: 0,\n fill: '#8b58a1'\n });\n\n ruler.flipY = false;\n ruler.lockMovementX = true;\n ruler.lockScalingX = true;\n ruler.lockScalingY = true;\n ruler.lockUniScaling = true;\n ruler.lockRotation = true;\n\n canvas.add(ruler);\n canvas.renderAll();\n\n // Keyboard arrows move ruler.\n $(document).keydown(function(e) {\n switch (e.which) {\n\n case 38: // Up arrow.\n ruler.top = ruler.top - 10;\n canvas.renderAll();\n break;\n\n case 40: // Down arrow.\n ruler.top = ruler.top + 10;\n canvas.renderAll();\n\n break;\n\n default:\n return; // Exit this handler for other keys.\n }\n e.preventDefault(); // Prevent the default action (scroll / move caret).\n });\n },\n\n /**\n * Trigger on canvas element actions.\n * @param {object} options\n */\n onchange: function(options) {\n if (options.target.hasOwnProperty('id') && options.target.id === 'ruler') {\n return;\n }\n\n $(\"#colorpicker\").spectrum(\"set\", options.target.fill);\n }\n };\n\n return {\n\n /**\n * Init\n * @param {object} args\n */\n initialise: function(args) {\n\n // Load spectrum javascript form here.\n $.getScript(M.cfg.wwwroot + \"/mod/gcanvas/javascript/spectrum.js\").done(function() {\n\n // Load the args passed from PHP.\n setOptions(args);\n\n // Set internal debug console.\n setDebug(opts.debugjs);\n\n $.noConflict();\n $(document).ready(function() {\n debug.log('Canvas Module v1.2');\n canvasModule.init();\n });\n }).fail(function(jqxhr, settings, exception) {\n // Display loading issue in console.\n debug.log(jqxhr);\n debug.log(settings);\n debug.log(exception);\n });\n }\n };\n});\n"],"names":["define","$","notification","opts","id","debugjs","hasHorizontalRuler","background","debug","bufferStep","bufferActive","canvas","canvasModule","canvasWidth","canvasHeight","defaultShaperect","width","height","left","top","angle","fill","defaultShapecircle","radius","defaultShapetriangle","defaultShapetextbox","loadHistory","ajax","type","url","M","cfg","wwwroot","data","sesskey","action","dataType","success","response","log","html","error","responseText","addNotification","message","saveCanvasAjax","fabric","Canvas","supports","toDataURL","multiplier","format","JSON","stringify","util","get_string","deleteSelectedCanvasItems","activeobjects","getActiveObjects","length","i","hasOwnProperty","element","undefined","remove","discardActiveObject","renderAll","e","loadDynamicToolbarMappingShapes","on","el","elementtype","this","shape","toLowerCase","add","setActiveObject","loadColorPicker","spectrum","showPalette","palette","showSelectionPalette","selectionPalette","flat","change","color","setColor","loadEmojiCsv","src","Image","fromURL","replace","object","set","centerTransform","scale","setCoords","deleteAttempt","$el","confirm","restoreAttempt","loadFromJSON","record","json_data","bind","setTimeout","showFileuploader","filearea","toggle","setBackgroundImage","img","scaleX","scaleY","addUserImage","formdata","inputs","serializeArray","each","input","name","value","addImageFromUrl","image","hide","path","maxwidth","getWidth","scaleToWidth","selectToolbarImage","dialog","is","show","images","undo","localStorage","getItem","removeItem","loadToolbar","clear","addHorizontalRuler","loadArrowToCanvas","loadEmojiPicker","attr","preventDefault","$picker","loadSVGFromURL","objects","options","obj","groupSVGElements","centerObject","colorhex","toHexString","preventMovingOutOfCanvas","target","currentHeight","currentWidth","getBoundingRect","Math","max","min","keyboardActions","document","keydown","which","init","__canvas","setHeight","setWidth","onchange","addToCache","addCanvasToCacheBuffer","clearTimeout","setItem","ruler","Rect","flipY","lockMovementX","lockScalingX","lockScalingY","lockUniScaling","lockRotation","initialise","args","getScript","done","key","vartype","Boolean","Number","String","setOptions","isenabled","m","console","window","setDebug","noConflict","ready","fail","jqxhr","settings","exception"],"mappings":"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"}
\ No newline at end of file