From b195ebeaf65d89c2132d80b699817b5eef22d969 Mon Sep 17 00:00:00 2001 From: Vincent Cornelis Date: Fri, 5 Apr 2024 18:21:58 +0200 Subject: [PATCH] Update #86bwwnckq - Update for Moodle 4.4 --- CHANGELOG.md | 3 +++ ajax.php | 4 +++- amd/build/canvas.min.js | 2 +- amd/build/canvas.min.js.map | 2 +- amd/src/canvas.js | 26 +++++++++++++------------- styles.css | 4 +++- version.php | 4 ++-- 7 files changed, 26 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 603b857..8f06b6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,9 @@ Types of changes * **Fixed** for any bug fixes. * **Security** in case of vulnerabilities. +## Version (4.4.0) - 2024-04-05 +- Upgraded and refactored for Moodle 4.4 + ## Version (4.2.1) - 2024-04-05 - Fixed issue with saving and loading canvas diff --git a/ajax.php b/ajax.php index d6a12bf..213bd26 100644 --- a/ajax.php +++ b/ajax.php @@ -40,7 +40,9 @@ defined('MOODLE_INTERNAL') || die; $action = optional_param('action', '', PARAM_TEXT); -$data = (object)optional_param('data', [], PARAM_RAW); +$data = optional_param('data', '', PARAM_RAW); + +$data = (object) json_decode($data, true); // Set course and context. $cm = get_coursemodule_from_id('gcanvas', $data->id, 0, false, diff --git a/amd/build/canvas.min.js b/amd/build/canvas.min.js index 0b9b343..2066718 100644 --- a/amd/build/canvas.min.js +++ b/amd/build/canvas.min.js @@ -8,6 +8,6 @@ * @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='",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)}))}}})); +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:JSON.stringify({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:JSON.stringify({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:JSON.stringify({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:JSON.stringify({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:JSON.stringify(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:JSON.stringify({id:opts.id})},dataType:"json",success:function(response){if(debug.log(response),response.success){var 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:JSON.stringify({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 803ebcd..285c894 100644 --- a/amd/build/canvas.min.js.map +++ b/amd/build/canvas.min.js.map @@ -1 +1 @@ -{"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\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 +{"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: JSON.stringify({\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: JSON.stringify({\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: JSON.stringify({\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: JSON.stringify({\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: JSON.stringify(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: JSON.stringify({\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: JSON.stringify({\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","JSON","stringify","dataType","success","response","log","html","error","responseText","addNotification","message","saveCanvasAjax","fabric","Canvas","supports","toDataURL","multiplier","format","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 diff --git a/amd/src/canvas.js b/amd/src/canvas.js index 7b3aeec..5700005 100644 --- a/amd/src/canvas.js +++ b/amd/src/canvas.js @@ -184,9 +184,9 @@ define(['jquery', 'core/notification'], function($, notification) { data: { sesskey: M.cfg.sesskey, action: 'load_history', - data: { + data: JSON.stringify({ 'id': opts.id, - } + }), }, dataType: "json", success: function(response) { @@ -228,7 +228,7 @@ define(['jquery', 'core/notification'], function($, notification) { data: { sesskey: M.cfg.sesskey, action: 'save_canvas', - data: { + data: JSON.stringify({ 'id': opts.id, 'status': 'final', 'canvas_data': canvas.toDataURL({ @@ -236,7 +236,7 @@ define(['jquery', 'core/notification'], function($, notification) { format: 'png' }), 'json_data': JSON.stringify(canvas) - } + }) }, dataType: "json", success: function(response) { @@ -402,10 +402,10 @@ define(['jquery', 'core/notification'], function($, notification) { data: { sesskey: M.cfg.sesskey, action: 'delete_attempt', - data: { + data: JSON.stringify({ 'id': opts.id, 'attempt_id': $el.data('id'), - } + }) }, dataType: "json", success: function(response) { @@ -447,10 +447,10 @@ define(['jquery', 'core/notification'], function($, notification) { data: { sesskey: M.cfg.sesskey, action: 'get_attempt', - data: { + data: JSON.stringify({ 'id': opts.id, 'attempt_id': $el.data('id'), - } + }) }, dataType: "json", success: function(response) { @@ -511,7 +511,7 @@ define(['jquery', 'core/notification'], function($, notification) { data: { sesskey: M.cfg.sesskey, action: 'upload_images', - data: formdata + data: JSON.stringify(formdata) }, dataType: "json", success: function(response) { @@ -569,9 +569,9 @@ define(['jquery', 'core/notification'], function($, notification) { data: { sesskey: M.cfg.sesskey, action: 'get_toolbar_images', - data: { + data: JSON.stringify({ 'id': opts.id, - } + }) }, dataType: "json", success: function(response) { @@ -745,9 +745,9 @@ define(['jquery', 'core/notification'], function($, notification) { data: { sesskey: M.cfg.sesskey, action: 'emoji', - data: { + data: JSON.stringify({ 'id': opts.id, - } + }) }, dataType: "json", success: function(response) { diff --git a/styles.css b/styles.css index b72b789..a7afeed 100644 --- a/styles.css +++ b/styles.css @@ -1,6 +1,8 @@ #page-mod-gcanvas-view #toolbar .item { width: 100%; + max-width: 100%; height: 40px; + max-height: 40px; line-height: 40px; vertical-align: middle; margin-bottom: 5px; @@ -208,4 +210,4 @@ #page-mod-gcanvas-view #select-a-image ul { list-style: none; -} \ No newline at end of file +} diff --git a/version.php b/version.php index 7471f3f..cc6deba 100644 --- a/version.php +++ b/version.php @@ -25,8 +25,8 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'mod_gcanvas'; -$plugin->release = '4.2.1'; +$plugin->release = '4.4.0'; $plugin->version = 2024040500; $plugin->requires = 2020061500; -$plugin->supported = [39, 402]; +$plugin->supported = [39, 404]; $plugin->maturity = MATURITY_STABLE;