Refactorization

This commit is contained in:
Luis Leiva 2017-11-18 18:14:27 +01:00
parent 4653df6c23
commit ff1e7c82b4
11 changed files with 403 additions and 285 deletions

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
!function(a){function b(b){function c(){if(h>0){h--;var a=new Image;a.src=g[h].image,a.onload=function(){e(this)}}}function d(){if(h<g.length-1){h++;var a=new Image;a.src=g[h].image,a.onload=function(){e(this)}}}function e(a){b.sketchable("handler",function(b,c){c.sketch.clear(),c.sketch.graphics.drawImage(a,0,0),c.strokes=g[h].strokes.slice()})}function f(a){if(a.ctrlKey)switch(a.which){case 26:a.shiftKey?i.redo():i.undo();break;case 25:i.redo()}}var g=[],h=-1,i=this;this.undo=function(){return c(),this},this.redo=function(){return d(),this},this.reset=function(){return g=[],h=-1,this},this.save=function(a){return b.sketchable("handler",function(b,c){a&&a.identifier>0?g[h].strokes=c.strokes.slice():(g.push({image:b[0].toDataURL(),strokes:c.strokes.slice()}),h++)}),this},this.init=function(){return a(document).off("keypress",f),a(document).on("keypress",f),this},this.destroy=function(){return a(document).off("keypress",f),this.reset()}}var c="sketchable";a.fn.sketchable.plugins.memento=function(d){function e(a){if(!f.options[a+"$bound"])if(f.options[a+"$bound"]=!0,f.options.events&&"function"==typeof f.options.events[a]){var b=f.options.events[a];f.options.events[a]=function(){b.apply(d,arguments),g[a].apply(d,arguments)}}else f.options.events[a]=g[a]}for(var f=d.sketchable("config"),g={clear:function(a,b){b.memento.reset().save()},mouseup:function(a,b,c){b.memento.save(c)},destroy:function(a,b){b.memento.destroy()}},h="mouseup clear destroy".split(" "),i=0;i<h.length;i++)e(h[i]);a.extend(a.fn.sketchable.api,{undo:function(){var b=a(this),d=b.data(c);d.memento.undo()},redo:function(){var b=a(this),d=b.data(c);d.memento.redo()},save:function(){var b=a(this),d=b.data(c);d.memento.save()}}),f.memento=new b(d),f.memento.init().save()}}(jQuery);
!function(a){function b(b){function c(a,c){b.sketchable("handler",function(b,d){d.sketch.clear(),d.sketch.graphics.drawImage(a,0,0),d.strokes=c.slice()})}function d(a){if(a.ctrlKey)switch(a.which){case 26:a.shiftKey?g.redo():g.undo();break;case 25:g.redo()}}var e=[],f=-1,g=this;this.undo=function(){return f>0&&(f--,this.restore()),this},this.redo=function(){return f<e.length-1&&(f++,this.restore()),this},this.reset=function(){return e=[],f=-1,this.save()},this.save=function(a){return b.sketchable("handler",function(b,c){a&&a.identifier>0?e[f].strokes=c.strokes.slice():(e.push({image:b[0].toDataURL(),strokes:c.strokes.slice()}),f++)}),this},this.state=function(){return JSON.parse(JSON.stringify(e[f]))},this.restore=function(a){a||(a=e[f]);var b=new Image;b.src=a.image,b.onload=function(){c(this,a.strokes)}},this.init=function(){return a(document).off("keypress",d),a(document).on("keypress",d),this.save()},this.destroy=function(){return a(document).off("keypress",d),this.reset()}}var c="sketchable";a.fn.sketchable.plugins.memento=function(d){function e(a){if(!f.options["_bound$"+a])if(f.options["_bound$"+a]=!0,f.options.events&&"function"==typeof f.options.events[a]){var b=f.options.events[a];f.options.events[a]=function(){b.apply(d,arguments),g[a].apply(d,arguments)}}else f.options.events[a]=g[a]}for(var f=d.sketchable("config"),g={clear:function(a,b){b.memento.reset()},mouseup:function(a,b,c){b.memento.save(c)},destroy:function(a,b){b.memento.destroy()}},h="mouseup clear destroy".split(" "),i=0;i<h.length;i++)e(h[i]);a.extend(a.fn.sketchable.api,{memento:{undo:function(){var b=a(this).data(c);return b.memento.undo()},redo:function(){var b=a(this).data(c);return b.memento.redo()},save:function(){var b=a(this).data(c);return b.memento.save()},state:function(){var b=a(this).data(c);return b.memento.state()},restore:function(b){var d=a(this).data(c);return d.memento.restore(b)}}}),f.memento=new b(d),f.memento.init()}}(jQuery);

View File

@ -1 +1 @@
!function(a){function b(a,b){b||(b=a.data(o).options),b.cssCursors&&(a[0].style.cursor=b.interactive?"pointer":"not-allowed"),this.onselectstart=function(){return!1}}function c(b){var c=a(b.target),d=c.offset();return{x:Math.round(b.pageX-d.left),y:Math.round(b.pageY-d.top)}}function d(a,b,c){b.coords[a]||(b.coords[a]=[]);var d=b.coords[a],e=(new Date).getTime();if(b.options.relTimestamps&&(0===b.strokes.length&&0===d.length&&(b.timestamp=e),e-=b.timestamp),d.push([c.x,c.y,e,+b.sketch.isDrawing]),b.options.filterCoords&&d.length>1){var f=d.length-1,g=d[f],h=d[f-1];g[0]==h[0]&&g[1]==h[1]&&d.splice(f,1)}}function e(a){return a.originalEvent.touches?!1:void l(a)}function f(a){return a.originalEvent.touches?!1:void m(a)}function g(a){return a.originalEvent.touches?!1:void n(a)}function h(b,c){var d=a(b.target),e=d.data(o),f=e.options;if(f.multitouch)for(var g=b.originalEvent.changedTouches,h=0;h<g.length;h++){var i=g[h];i.type=b.type,i.identifier=h,c(i)}else{var i=b.originalEvent.touches[0];i.type=b.type,i.identifier=0,c(i)}}function i(a){h(a,l),a.preventDefault()}function j(a){h(a,m),a.preventDefault()}function k(a){h(a,n),a.preventDefault()}function l(b){if(3===b.which)return!1;var e=b.identifier||0,f=a(b.target),g=f.data(o),h=g.options;if(h.interactive){g.sketch.isDrawing=!0;var i=c(b);h.graphics.firstPointSize>0&&g.sketch.beginFill(h.graphics.fillStyle).fillCircle(i.x,i.y,h.graphics.firstPointSize).endFill(),g.coords[e]||(g.coords[e]=[]),g.coords[e].length>0&&(g.strokes.push(g.coords[e]),g.coords[e]=[]),d(e,g,i),"function"==typeof h.events.mousedown&&h.events.mousedown(f,g,b)}}function m(b){var e=b.identifier||0,f=a(b.target),g=f.data(o),h=g.options;if(h.interactive&&(h.mouseupMovements&&0!==g.strokes.length||g.sketch.isDrawing)){var i=c(b);if(g.sketch.isDrawing){var j=g.coords[e][g.coords[e].length-1];g.sketch.beginPath().line(j[0],j[1],i.x,i.y).stroke().closePath()}d(e,g,i),"function"==typeof h.events.mousemove&&h.events.mousemove(f,g,b)}}function n(b){var c=b.identifier||0,d=a(b.target),e=d.data(o),f=e.options;f.interactive&&(e.sketch.isDrawing=!1,e.strokes.push(e.coords[c]),e.coords[c]=[],"function"==typeof f.events.mouseup&&f.events.mouseup(d,e,b))}var o="sketchable",p={init:function(c){var d=a.extend(!0,{},a.fn.sketchable.defaults,c||{});return this.each(function(){var c=a(this),h=c.data(o);h||(c.bind("mousedown",e),c.bind("mousemove",f),c.bind("mouseup",g),c.bind("touchstart",i),c.bind("touchmove",j),c.bind("touchend",k),b(c,d));var l=new jSketch(this,d.graphics);c.data(o,{strokes:[],coords:{},timestamp:(new Date).getTime(),sketch:l,options:d}),d.events&&"function"==typeof d.events.init&&d.events.init(c,c.data(o));for(var m in a.fn.sketchable.plugins)a.fn.sketchable.plugins[m](c)})},config:function(c){return c?this.each(function(){var d=a(this),e=d.data(o);e.options=a.extend(!0,{},a.fn.sketchable.defaults,e.options,c),b(d)}):a(this).data(o)},strokes:function(b){if(b)return this.each(function(){var c=a(this),d=c.data(o);d.strokes=b});var c=a(this).data(o);return c.strokes},handler:function(b){return this.each(function(){var c=a(this),d=c.data(o);b(c,d)})},clear:function(){return this.each(function(){var b=a(this),c=b.data(o)||{},d=c.options;c.sketch&&(c.sketch.clear(),c.strokes=[],c.coords={}),d&&"function"==typeof d.events.clear&&d.events.clear(b,c)})},reset:function(b){return this.each(function(){var c=a(this),d=c.data(o)||{},e=d.options;c.sketchable("destroy").sketchable(b),e&&"function"==typeof e.events.reset&&e.events.reset(c,d)})},destroy:function(){return this.each(function(){var b=a(this),c=b.data(o)||{},d=c.options;b.unbind("mouseup",g),b.unbind("mousemove",f),b.unbind("mousedown",e),b.unbind("touchstart",i),b.unbind("touchmove",j),b.unbind("touchend",k),b.removeData(o),d&&"function"==typeof d.events.destroy&&d.events.destroy(b,c)})}};a.fn.sketchable=function(b){return"object"!=typeof b&&b?p[b]?p[b].apply(this,Array.prototype.slice.call(arguments,1)):(a.error("Unknown method: "+b),this):p.init.apply(this,arguments)},a.fn.sketchable.api=p,a.fn.sketchable.plugins={},a.fn.sketchable.defaults={interactive:!0,mouseupMovements:!1,relTimestamps:!1,multitouch:!0,cssCursors:!0,filterCoords:!1,events:{},graphics:{firstPointSize:3,lineWidth:3,strokeStyle:"#F0F",fillStyle:"#F0F",lineCap:"round",lineJoin:"round",miterLimit:10}}}(jQuery);
!function(a){function b(a,b){b||(b=a.data(p).options),b.cssCursors&&(a[0].style.cursor=b.interactive?"pointer":"not-allowed"),this.onselectstart=function(){return!1}}function c(a,b){b=b.split(".");for(var c=0;c<b.length;c++){var d=b[c];a=a[d]}return a}function d(b){var c=a(b.target),d=c.offset();return{x:Math.round(b.pageX-d.left),y:Math.round(b.pageY-d.top)}}function e(a,b,c){var d=b.coords[a],e=(new Date).getTime();if(b.options.relTimestamps&&(0===b.strokes.length&&0===d.length&&(b.timestamp=e),e-=b.timestamp),d.push([c.x,c.y,e,+b.sketch.isDrawing]),b.options.filterCoords&&d.length>1){var f=d.length-1,g=d[f],h=d[f-1];g[0]==h[0]&&g[1]==h[1]&&d.splice(f,1)}}function f(a){return a.originalEvent.touches?!1:void l(a)}function g(a){return a.originalEvent.touches?!1:void m(a)}function h(a){return a.originalEvent.touches?!1:void n(a)}function i(a){o(a,l),a.preventDefault()}function j(a){o(a,m),a.preventDefault()}function k(a){o(a,n),a.preventDefault()}function l(b){if(3===b.which)return!1;var c=b.identifier||0,f=a(b.target),g=f.data(p),h=g.options;if(h.interactive){g.sketch.isDrawing=!0;var i=d(b);h.graphics.firstPointSize>0&&g.sketch.beginFill(h.graphics.fillStyle).fillCircle(i.x,i.y,h.graphics.firstPointSize).endFill();var j=g.coords[c];j||(j=[]),j.length>0&&g.strokes.push(j),g.coords[c]=[],e(c,g,i),"function"==typeof h.events.mousedown&&h.events.mousedown(f,g,b)}}function m(b){var c=b.identifier||0,f=a(b.target),g=f.data(p),h=g.options;if(h.interactive&&(h.mouseupMovements&&0!==g.strokes.length||g.sketch.isDrawing)){var i=d(b),j=g.coords[c],k=j[j.length-1];if(k){var l=g.sketch.beginPath();g.sketch.isDrawing?l.lineStyle(h.graphics.strokeStyle,h.graphics.lineWidth):h.mouseupMovements.visible!==!1&&l.lineStyle(h.mouseupMovements.strokeStyle||"#DDD",h.mouseupMovements.lineWidth||1),l.line(k[0],k[1],i.x,i.y).stroke().closePath()}e(c,g,i),"function"==typeof h.events.mousemove&&h.events.mousemove(f,g,b)}}function n(b){var c=b.identifier||0,d=a(b.target),e=d.data(p),f=e.options;f.interactive&&(e.sketch.isDrawing=!1,e.strokes.push(e.coords[c]),e.coords[c]=[],"function"==typeof f.events.mouseup&&f.events.mouseup(d,e,b))}function o(b,c){var d=a(b.target),e=d.data(p),f=e.options;if(f.multitouch)for(var g=b.originalEvent.changedTouches,h=0;h<g.length;h++){var i=g[h];i.type=b.type,i.identifier=h,c(i)}else{var i=b.originalEvent.touches[0];i.type=b.type,i.identifier=0,c(i)}}var p="sketchable",q={init:function(c){var d=a.extend(!0,{},a.fn.sketchable.defaults,c||{});return this.each(function(){var c=a(this),e=c.data(p);e||(c.bind("mousedown",f),c.bind("mousemove",g),c.bind("mouseup",h),c.bind("touchstart",i),c.bind("touchmove",j),c.bind("touchend",k),b(c,d));var l=new jSketch(this,d.graphics);c.data(p,{strokes:[],coords:{},timestamp:(new Date).getTime(),sketch:l,options:d}),d.events&&"function"==typeof d.events.init&&d.events.init(c,c.data(p));for(var m in a.fn.sketchable.plugins)a.fn.sketchable.plugins[m](c)})},config:function(c){return c?this.each(function(){var d=a(this),e=d.data(p);e.options=a.extend(!0,{},a.fn.sketchable.defaults,e.options,c),b(d)}):a(this).data(p)},strokes:function(b){if(b)return this.each(function(){var c=a(this),d=c.data(p);d.strokes=b});var c=a(this).data(p);return c.strokes},handler:function(b){return this.each(function(){var c=a(this),d=c.data(p);b(c,d)})},clear:function(){return this.each(function(){var b=a(this),c=b.data(p)||{},d=c.options;c.sketch&&(c.sketch.clear(),c.strokes=[],c.coords={}),d&&"function"==typeof d.events.clear&&d.events.clear(b,c)})},reset:function(b){return this.each(function(){var c=a(this),d=c.data(p)||{},e=d.options;c.sketchable("destroy").sketchable(b),e&&"function"==typeof e.events.reset&&e.events.reset(c,d)})},destroy:function(){return this.each(function(){var b=a(this),c=b.data(p)||{},d=c.options;b.unbind("mouseup",h),b.unbind("mousemove",g),b.unbind("mousedown",f),b.unbind("touchstart",i),b.unbind("touchmove",j),b.unbind("touchend",k),b.removeData(p),d&&"function"==typeof d.events.destroy&&d.events.destroy(b,c)})}};a.fn.sketchable=function(b){var d=Array.prototype.slice.call(arguments,1);if("object"==typeof b||!b)return q.init.apply(this,arguments);if(b.indexOf(".")>-1){var e=c(q,b);return e.apply(this,d)}return q[b]?q[b].apply(this,d):(a.error("Unknown method: "+b),this)},a.fn.sketchable.api=q,a.fn.sketchable.plugins={},a.fn.sketchable.defaults={interactive:!0,mouseupMovements:!1,relTimestamps:!1,multitouch:!0,cssCursors:!0,filterCoords:!1,events:{},graphics:{firstPointSize:3,lineWidth:3,strokeStyle:"#F0F",fillStyle:"#F0F",lineCap:"round",lineJoin:"round",miterLimit:10}}}(jQuery);

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
!function(a){function b(a){function b(){if(g>0){g--;var a=new Image;a.src=f[g].image,a.onload=function(){d(this)}}}function c(){if(g<f.length-1){g++;var a=new Image;a.src=f[g].image,a.onload=function(){d(this)}}}function d(b){a.handler(function(a,c){c.sketch.clear(),c.sketch.graphics.drawImage(b,0,0),c.strokes=f[g].strokes.slice()})}function e(a){if(a.ctrlKey)switch(a.which){case 26:a.shiftKey?h.redo():h.undo();break;case 25:h.redo()}}var f=[],g=-1,h=this;this.undo=function(){return b(),this},this.redo=function(){return c(),this},this.reset=function(){return f=[],g=-1,this},this.save=function(b){return a.handler(function(a,c){b&&b.identifier>0?f[g].strokes=c.strokes.slice():(f.push({image:a.toDataURL(),strokes:c.strokes.slice()}),g++)}),this},this.init=function(){return Event.remove(document,"keypress",e),Event.add(document,"keypress",e),this},this.destroy=function(){return Event.remove(document,"keypress",e),this.reset()}}var c="sketchable";Sketchable.prototype.plugins.memento=function(a){function d(b){if(!e.options[b+"$bound"])if(e.options[b+"$bound"]=!0,e.options.events&&"function"==typeof e.options.events[b]){var c=e.options.events[b];e.options.events[b]=function(){c.apply(a,arguments),f[b].apply(a,arguments)}}else e.options.events[b]=f[b]}for(var e=a.config(),f={clear:function(a,b){b.memento.reset().save()},mouseup:function(a,b,c){b.memento.save(c)},destroy:function(a,b){b.memento.destroy()}},g="mouseup clear destroy".split(" "),h=0;h<g.length;h++)d(g[h]);deepExtend(Sketchable.prototype,{undo:function(){var a=this.elem,b=dataBind(a)[c];b.memento.undo()},redo:function(){var a=this.elem,b=dataBind(a)[c];b.memento.redo()},save:function(){var a=this.elem,b=dataBind(a)[c];b.memento.save()}}),e.memento=new b(a),e.memento.init().save()}}(this);
!function(a){function b(a){function b(b,c){a.handler(function(a,d){d.sketch.clear(),d.sketch.graphics.drawImage(b,0,0),d.strokes=c.slice()})}function c(a){if(a.ctrlKey)switch(a.which){case 26:a.shiftKey?f.redo():f.undo();break;case 25:f.redo()}}var d=[],e=-1,f=this;this.undo=function(){return e>0&&(e--,this.restore()),this},this.redo=function(){return e<d.length-1&&(e++,this.restore()),this},this.reset=function(){return d=[],e=-1,this.save()},this.save=function(b){return a.handler(function(a,c){b&&b.identifier>0?d[e].strokes=c.strokes.slice():(d.push({image:a.toDataURL(),strokes:c.strokes.slice()}),e++)}),this},this.state=function(){return JSON.parse(JSON.stringify(d[e]))},this.restore=function(a){a||(a=d[e]);var c=new Image;c.src=a.image,c.onload=function(){b(this,a.strokes)}},this.init=function(){return Event.remove(document,"keypress",c),Event.add(document,"keypress",c),this.save()},this.destroy=function(){return Event.remove(document,"keypress",c),this.reset()}}var c="sketchable";Sketchable.prototype.plugins.memento=function(a){function d(b){if(!e.options["_bound$"+b])if(e.options["_bound$"+b]=!0,e.options.events&&"function"==typeof e.options.events[b]){var c=e.options.events[b];e.options.events[b]=function(){c.apply(a,arguments),f[b].apply(a,arguments)}}else e.options.events[b]=f[b]}for(var e=a.config(),f={clear:function(a,b){b.memento.reset()},mouseup:function(a,b,c){b.memento.save(c)},destroy:function(a,b){b.memento.destroy()}},g="mouseup clear destroy".split(" "),h=0;h<g.length;h++)d(g[h]);deepExtend(a,{memento:{undo:function(){var b=dataBind(a.elem)[c];return b.memento.undo()},redo:function(){var b=dataBind(a.elem)[c];return b.memento.redo()},save:function(){var b=dataBind(a.elem)[c];return b.memento.save()},state:function(){var b=dataBind(a.elem)[c];return b.memento.state()},restore:function(b){var d=dataBind(a.elem)[c];return d.memento.restore(b)}}}),e.memento=new b(a),e.memento.init()}}(this);

View File

@ -1 +1 @@
!function(a){function b(a,b){if(!a)throw new Error("Sketchable requires a DOM element.");return"string"==typeof a&&(a=r.querySelector(a)),this.elem=a,this.init(b)}function c(b){var c=b.getBoundingClientRect(),d=r.body,e=r.documentElement,f=a.pageYOffset||e.scrollTop||d.scrollTop,g=a.pageXOffset||e.scrollLeft||d.scrollLeft,h=e.clientTop||d.clientTop||0,i=e.clientLeft||d.clientLeft||0,j=c.top+f-h,k=c.left+g-i;return{top:Math.round(j),left:Math.round(k)}}function d(a,b){b||(b=dataBind(a)[q].options),b.cssCursors&&(a.style.cursor=b.interactive?"pointer":"not-allowed"),a.onselectstart=function(){return!1}}function e(a){var b=a.target,d=c(b);return{x:Math.round(a.pageX-d.left),y:Math.round(a.pageY-d.top)}}function f(a,b,c){b.coords[a]||(b.coords[a]=[]);var d=b.coords[a],e=(new Date).getTime();if(b.options.relTimestamps&&(0===b.strokes.length&&0===d.length&&(b.timestamp=e),e-=b.timestamp),d.push([c.x,c.y,e,+b.sketch.isDrawing]),b.options.filterCoords&&d.length>1){var f=d.length-1,g=d[f],h=d[f-1];g[0]==h[0]&&g[1]==h[1]&&d.splice(f,1)}}function g(a){return a.touches?!1:void n(a)}function h(a){return a.touches?!1:void o(a)}function i(a){return a.touches?!1:void p(a)}function j(a,b){var c=a.target,d=dataBind(c)[q],e=d.options;if(e.multitouch)for(var f=a.changedTouches,g=0;g<f.length;g++){var h=f[g];h.type=a.type,h.identifier=g,b(h)}else{var h=a.touches[0];h.type=a.type,h.identifier=0,b(h)}a.preventDefault()}function k(a){j(a,n),a.preventDefault()}function l(a){j(a,o),a.preventDefault()}function m(a){j(a,p),a.preventDefault()}function n(a){if(Event.isRightClick(a))return!1;var b=a.identifier||0,c=a.target,d=dataBind(c)[q],g=d.options;if(g.interactive){d.sketch.isDrawing=!0;var h=e(a);g.graphics.firstPointSize>0&&d.sketch.beginFill(g.graphics.fillStyle).fillCircle(h.x,h.y,g.graphics.firstPointSize).endFill(),d.coords[b]&&d.coords[b].length>0&&(d.strokes.push(d.coords[b].slice()),d.coords[b]=[]),f(b,d,h),"function"==typeof g.events.mousedown&&g.events.mousedown(c,d,a)}}function o(a){var b=a.identifier||0,c=a.target,d=dataBind(c)[q],g=d.options;if(g.interactive&&(g.mouseupMovements&&0!==d.strokes.length||d.sketch.isDrawing)){var h=e(a);if(d.sketch.isDrawing){var i=d.coords[b][d.coords[b].length-1];d.sketch.beginPath().line(i[0],i[1],h.x,h.y).stroke().closePath()}f(b,d,h),"function"==typeof g.events.mousemove&&g.events.mousemove(c,d,a)}}function p(a){var b=a.identifier||0,c=a.target,d=dataBind(c)[q],e=d.options;e.interactive&&(d.sketch.isDrawing=!1,d.strokes.push(d.coords[b].slice()),d.coords[b]=[],"function"==typeof e.events.mouseup&&e.events.mouseup(c,d,a))}var q="sketchable",r=a.document;b.prototype={init:function(a){var a=deepExtend({},b.prototype.defaults,a||{}),c=this.elem,e=dataBind(c)[q];e||(Event.add(c,"mousedown",g),Event.add(c,"mousemove",h),Event.add(c,"mouseup",i),Event.add(c,"touchstart",k),Event.add(c,"touchmove",l),Event.add(c,"touchend",m),d(c,a));var f=new jSketch(c,a.graphics);dataBind(c)[q]=e={strokes:[],coords:{},timestamp:(new Date).getTime(),sketch:f,options:a},"function"==typeof a.events.init&&a.events.init(c,e);for(var j in this.plugins)this.plugins[j](this);return this},config:function(a){var c=this.elem,e=dataBind(c)[q];return a?(e.options=deepExtend({},b.prototype.defaults,a||{}),d(c),this):e},strokes:function(a){var b=this.elem;if(a){var c=dataBind(b)[q];return c.strokes=a,this}var c=dataBind(b)[q];return c.strokes},handler:function(a){var b=this.elem,c=dataBind(b)[q];return a(b,c),this},clear:function(){var a=this.elem,b=dataBind(a)[q],c=b.options;return b.sketch.clear(),b.strokes=[],b.coords={},"function"==typeof c.events.clear&&c.events.clear(a,b),this},reset:function(a){var b=this.elem,c=dataBind(b)[q],a=c.options;return this.destroy().init(a),"function"==typeof a.events.reset&&a.events.reset(b,c),this},destroy:function(){var a=this.elem,b=dataBind(a)[q],c=b.options;return Event.remove(a,"mouseup",i),Event.remove(a,"mousemove",h),Event.remove(a,"mousedown",g),Event.remove(a,"touchstart",k),Event.remove(a,"touchmove",l),Event.remove(a,"touchend",m),dataBind(a)[q]=null,"function"==typeof c.events.destroy&&c.events.destroy(a,b),this}},b.prototype.plugins={},b.prototype.defaults={interactive:!0,mouseupMovements:!1,relTimestamps:!1,multitouch:!0,cssCursors:!0,filterCoords:!1,events:{},graphics:{firstPointSize:3,lineWidth:3,strokeStyle:"#F0F",fillStyle:"#F0F",lineCap:"round",lineJoin:"round",miterLimit:10}},a.Sketchable=b}(this);
!function(a){function b(a,b){if(!a)throw new Error("Sketchable requires a DOM element.");return"string"==typeof a&&(a=r.querySelector(a)),this.elem=a,this.init(b)}function c(b){var c=b.getBoundingClientRect(),d=r.body,e=r.documentElement,f=a.pageYOffset||e.scrollTop||d.scrollTop,g=a.pageXOffset||e.scrollLeft||d.scrollLeft,h=e.clientTop||d.clientTop||0,i=e.clientLeft||d.clientLeft||0,j=c.top+f-h,k=c.left+g-i;return{top:Math.round(j),left:Math.round(k)}}function d(a,b){b||(b=dataBind(a)[q].options),b.cssCursors&&(a.style.cursor=b.interactive?"pointer":"not-allowed"),a.onselectstart=function(){return!1}}function e(a){var b=a.target,d=c(b);return{x:Math.round(a.pageX-d.left),y:Math.round(a.pageY-d.top)}}function f(a,b,c){var d=b.coords[a],e=(new Date).getTime();if(b.options.relTimestamps&&(0===b.strokes.length&&0===d.length&&(b.timestamp=e),e-=b.timestamp),d.push([c.x,c.y,e,+b.sketch.isDrawing]),b.options.filterCoords&&d.length>1){var f=d.length-1,g=d[f],h=d[f-1];g[0]==h[0]&&g[1]==h[1]&&d.splice(f,1)}}function g(a){return a.touches?!1:void m(a)}function h(a){return a.touches?!1:void n(a)}function i(a){return a.touches?!1:void o(a)}function j(a){p(a,m),a.preventDefault()}function k(a){p(a,n),a.preventDefault()}function l(a){p(a,o),a.preventDefault()}function m(a){if(Event.isRightClick(a))return!1;var b=a.identifier||0,c=a.target,d=dataBind(c)[q],g=d.options;if(g.interactive){d.sketch.isDrawing=!0;var h=e(a);g.graphics.firstPointSize>0&&d.sketch.beginFill(g.graphics.fillStyle).fillCircle(h.x,h.y,g.graphics.firstPointSize).endFill();var i=d.coords[b];i||(i=[]),i.length>0&&d.strokes.push(i),d.coords[b]=[],f(b,d,h),"function"==typeof g.events.mousedown&&g.events.mousedown(c,d,a)}}function n(a){var b=a.identifier||0;if(elem=a.target,data=dataBind(elem)[q],options=data.options,options.interactive&&(options.mouseupMovements&&0!==data.strokes.length||data.sketch.isDrawing)){var c=e(a),d=data.coords[b],g=d[d.length-1];if(g){var h=data.sketch.beginPath();data.sketch.isDrawing?h.lineStyle(options.graphics.strokeStyle,options.graphics.lineWidth):options.mouseupMovements.visible!==!1&&h.lineStyle(options.mouseupMovements.strokeStyle||"#DDD",options.mouseupMovements.lineWidth||1),h.line(g[0],g[1],c.x,c.y).stroke().closePath()}f(b,data,c),"function"==typeof options.events.mousemove&&options.events.mousemove(elem,data,a)}}function o(a){var b=a.identifier||0;elem=a.target,data=dataBind(elem)[q],options=data.options,options.interactive&&(data.sketch.isDrawing=!1,data.strokes.push(data.coords[b]),data.coords[b]=[],"function"==typeof options.events.mouseup&&options.events.mouseup(elem,data,a))}function p(a,b){var c=a.target,d=dataBind(c)[q],e=d.options;if(e.multitouch)for(var f=a.changedTouches,g=0;g<f.length;g++){var h=f[g];h.type=a.type,h.identifier=g,b(h)}else{var h=a.touches[0];h.type=a.type,h.identifier=0,b(h)}a.preventDefault()}var q="sketchable",r=a.document;b.prototype={init:function(a){var a=deepExtend({},b.prototype.defaults,a||{}),c=this.elem,e=dataBind(c)[q];e||(Event.add(c,"mousedown",g),Event.add(c,"mousemove",h),Event.add(c,"mouseup",i),Event.add(c,"touchstart",j),Event.add(c,"touchmove",k),Event.add(c,"touchend",l),d(c,a));var f=new jSketch(c,a.graphics);dataBind(c)[q]=e={strokes:[],coords:{},timestamp:(new Date).getTime(),sketch:f,instance:this,options:a},"function"==typeof a.events.init&&a.events.init(c,e);for(var m in this.plugins)this.plugins[m](this);return this},config:function(a){var c=this.elem,e=dataBind(c)[q];return a?(e.options=deepExtend({},b.prototype.defaults,e.options,a),d(c),this):e},strokes:function(a){var b=this.elem;if(a){var c=dataBind(b)[q];return c.strokes=a,this}var c=dataBind(b)[q];return c.strokes},handler:function(a){var b=this.elem,c=dataBind(b)[q];return a(b,c),this},clear:function(){var a=this.elem,b=dataBind(a)[q],c=b.options;return b.sketch.clear(),b.strokes=[],b.coords={},"function"==typeof c.events.clear&&c.events.clear(a,b),this},reset:function(a){var b=this.elem,c=dataBind(b)[q],a=c.options;return this.destroy().init(a),"function"==typeof a.events.reset&&a.events.reset(b,c),this},destroy:function(){var a=this.elem,b=dataBind(a)[q],c=b.options;return Event.remove(a,"mouseup",i),Event.remove(a,"mousemove",h),Event.remove(a,"mousedown",g),Event.remove(a,"touchstart",j),Event.remove(a,"touchmove",k),Event.remove(a,"touchend",l),dataBind(a)[q]=null,"function"==typeof c.events.destroy&&c.events.destroy(a,b),this}},b.prototype.plugins={},b.prototype.defaults={interactive:!0,mouseupMovements:!1,relTimestamps:!1,multitouch:!0,cssCursors:!0,filterCoords:!1,events:{},graphics:{firstPointSize:3,lineWidth:3,strokeStyle:"#F0F",fillStyle:"#F0F",lineCap:"round",lineJoin:"round",miterLimit:10}},a.Sketchable=b}(this);

View File

@ -1,5 +1,5 @@
/*!
* jQuery sketchable | v2.0 | Luis A. Leiva | MIT license
* jQuery sketchable | v2.1 | Luis A. Leiva | MIT license
* A jQuery plugin for the jSketch drawing library.
*/
@ -15,13 +15,14 @@
*/
/* eslint-env browser */
/* global jQuery */
;(function($) {
// Custom namespace ID, for private data bindind.
var namespace = 'sketchable';
// Begin jQuery Sketchable plugin API.
var methods = {
var api = {
/**
* Initialize the selected jQuery objects.
* @param {Object} [options] - Configuration (default: {@link $.fn.sketchable.defaults}).
@ -217,7 +218,7 @@
* @namespace $.fn.sketchable
* @param {String|Object} method - Method to invoke, or a configuration object.
* @return jQuery
* @version 1.9
* @version 2.1
* @author Luis A. Leiva
* @license MIT license
* @example
@ -225,10 +226,17 @@
* $('canvas').sketchable({ interactive:false });
*/
$.fn.sketchable = function(method) {
var args = Array.prototype.slice.call(arguments, 1);
if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
// Constructor.
return api.init.apply(this, arguments);
} else if (method.indexOf('.') > -1) {
// Plugin method.
var actualMethod = locate(api, method);
return actualMethod.apply(this, args);
} else if (api[method]) {
// Instance method.
return api[method].apply(this, args);
} else {
$.error('Unknown method: ' + method);
}
@ -242,7 +250,7 @@
* @type {Object}
* @see Sketchable.prototype
*/
$.fn.sketchable.api = methods;
$.fn.sketchable.api = api;
/**
* Plugins store.
@ -263,11 +271,11 @@
* @type {Object}
* @example
* // The following is the default configuration:
* new Sketchable('canvas', {
* $('canvas').sketchable({
* interactive: true,
* mouseupMovements: false,
* relTimestamps: false,
* multitouch: false,
* multitouch: true,
* cssCursors: true,
* filterCoords: false,
* // Event hooks.
@ -353,6 +361,18 @@
this.onselectstart = function() { return false };
};
/**
* @private
*/
function locate(obj, path) {
path = path.split('.');
for (var i = 0; i < path.length; i++) {
var key = path[i];
obj = obj[key];
}
return obj;
}
/**
* @private
*/
@ -368,11 +388,7 @@
* @private
*/
function saveMousePos(idx, data, pt) {
// Ensure that coords is properly initialized.
if (!data.coords[idx]) {
data.coords[idx] = [];
}
// Use pointer for easy handling.
// Current coords are already initialized.
var coords = data.coords[idx];
var time = (new Date).getTime();
@ -420,30 +436,6 @@
upHandler(e);
};
/**
* @private
*/
function execTouchEvent(e, callback) {
var elem = $(e.target), data = elem.data(namespace), options = data.options;
if (options.multitouch) {
// Track all fingers.
var touches = e.originalEvent.changedTouches;
for (var i = 0; i < touches.length; i++) {
var touch = touches[i];
// Add event type and finger ID.
touch.type = e.type;
touch.identifier = i;
callback(touch);
}
} else {
// Track only the current finger.
var touch = e.originalEvent.touches[0];
touch.type = e.type;
touch.identifier = 0;
callback(touch);
}
};
/**
* @private
*/
@ -483,20 +475,21 @@
if (!options.interactive) return;
data.sketch.isDrawing = true;
var p = getMousePos(e);
// Mark visually 1st point of stroke.
if (options.graphics.firstPointSize > 0) {
data.sketch.beginFill(options.graphics.fillStyle).fillCircle(p.x, p.y, options.graphics.firstPointSize).endFill();
}
// Ensure that coords is properly initialized.
if (!data.coords[idx]) {
data.coords[idx] = [];
}
var coords = data.coords[idx];
if (!coords) coords = [];
// Don't mix mouseup and mousedown in the same stroke.
if (data.coords[idx].length > 0) {
data.strokes.push(data.coords[idx]);
data.coords[idx] = [];
}
if (coords.length > 0) data.strokes.push(coords);
// In any case, ensure that coords is properly reset/initialized.
data.coords[idx] = [];
saveMousePos(idx, data, p);
if (typeof options.events.mousedown === 'function') {
@ -508,20 +501,31 @@
* @private
*/
function moveHandler(e) {
var idx = e.identifier || 0;
var elem = $(e.target), data = elem.data(namespace), options = data.options;
var idx = e.identifier || 0,
elem = $(e.target),
data = elem.data(namespace),
options = data.options;
// Exit early if interactivity is disabled.
if (!options.interactive) return;
//if (!options.mouseupMovements && !data.sketch.isDrawing) return;
// This would grab all penup strokes AFTER drawing something on the canvas for the first time.
// Grab penup strokes AFTER drawing something on the canvas for the first time.
if ( (!options.mouseupMovements || data.strokes.length === 0) && !data.sketch.isDrawing ) return;
var p = getMousePos(e);
if (data.sketch.isDrawing) {
var last = data.coords[idx][ data.coords[idx].length - 1 ];
data.sketch.beginPath().line(last[0], last[1], p.x, p.y).stroke().closePath();
var coords = data.coords[idx];
var last = coords[coords.length - 1];
if (last) {
var brush = data.sketch.beginPath();
if (data.sketch.isDrawing) {
// Style for regular, pendown strokes.
brush.lineStyle(options.graphics.strokeStyle, options.graphics.lineWidth);
} else if (options.mouseupMovements.visible !== false) {
// Style for penup strokes.
brush.lineStyle(options.mouseupMovements.strokeStyle || '#DDD', options.mouseupMovements.lineWidth || 1);
}
brush.line(last[0], last[1], p.x, p.y).stroke().closePath();
}
saveMousePos(idx, data, p);
if (typeof options.events.mousemove === 'function') {
@ -533,12 +537,15 @@
* @private
*/
function upHandler(e) {
var idx = e.identifier || 0;
var elem = $(e.target), data = elem.data(namespace), options = data.options;
var idx = e.identifier || 0,
elem = $(e.target),
data = elem.data(namespace),
options = data.options;
// Exit early if interactivity is disabled.
if (!options.interactive) return;
data.sketch.isDrawing = false;
data.strokes.push(data.coords[idx]);
data.coords[idx] = [];
@ -547,4 +554,28 @@
}
};
/**
* @private
*/
function execTouchEvent(e, callback) {
var elem = $(e.target), data = elem.data(namespace), options = data.options;
if (options.multitouch) {
// Track all fingers.
var touches = e.originalEvent.changedTouches;
for (var i = 0; i < touches.length; i++) {
var touch = touches[i];
// Add event type and finger ID.
touch.type = e.type;
touch.identifier = i;
callback(touch);
}
} else {
// Track only the current finger.
var touch = e.originalEvent.touches[0];
touch.type = e.type;
touch.identifier = 0;
callback(touch);
}
};
})(jQuery);

View File

@ -1,8 +1,9 @@
/*!
* Memento plugin for jQuery Sketchable | v2.0 | Luis A. Leiva | MIT license
* Memento plugin for jQuery Sketchable | v2.1 | Luis A. Leiva | MIT license
*/
/* eslint-env browser */
/* global jQuery */
;(function($) {
// Custom namespace ID, for private data bindind.
@ -12,7 +13,7 @@
* This class implements the <a href="https://en.wikipedia.org/wiki/Memento_pattern">Memento pattern</a>
* and is part of the {@link $.fn.sketchable.plugins.memento} plugin.
* @class
* @version 2.0
* @version 2.1
* @example
* var sketcher = $('canvas').sketchable();
* // This is internally done by the plugin, plus some checks:
@ -24,37 +25,12 @@
var stpos = -1;
var self = this;
/**
* Update state.
* @param {Image} snapshot Image object.
* @param {Array} strokes Strokes associated with snapshot.
* @private
*/
function prev() {
if (stpos > 0) {
stpos--;
var snapshot = new Image();
snapshot.src = stack[stpos].image;
snapshot.onload = function() {
restore(this);
};
}
};
/**
* @private
*/
function next() {
if (stpos < stack.length - 1) {
stpos++;
var snapshot = new Image();
snapshot.src = stack[stpos].image;
snapshot.onload = function() {
restore(this);
};
}
};
/**
* Snashot restorer.
* @param {String} snapshot Base64 image.
* @private
*/
function restore(snapshot) {
function draw(snapshot, strokes) {
// Manipulate canvas via jQuery sketchable API.
// This way, we don't lose default drawing settings et al.
$instance.sketchable('handler', function(elem, data) {
@ -64,9 +40,9 @@
data.sketch.clear();
data.sketch.graphics.drawImage(snapshot, 0,0);
// Update strokes.
data.strokes = stack[stpos].strokes.slice();
data.strokes = strokes.slice();
});
};
}
/**
* Key event manager.
* - Undo: "Ctrl + Z"
@ -89,14 +65,16 @@
break;
}
}
};
}
/**
* Goes back to the last saved state, if available.
* @return {MementoCanvas} Class instance.
*/
this.undo = function() {
prev();
if (stpos > 0) {
stpos--;
this.restore();
}
return this;
};
/**
@ -104,7 +82,10 @@
* @return {MementoCanvas} Class instance.
*/
this.redo = function() {
next();
if (stpos < stack.length - 1) {
stpos++;
this.restore();
}
return this;
};
/**
@ -114,7 +95,8 @@
this.reset = function() {
stack = [];
stpos = -1;
return this;
// Save blank state afterward.
return this.save();
};
/**
* Save current state.
@ -134,6 +116,28 @@
});
return this;
};
/**
* Read current state: `{ image:String, strokes:Array }`.
* @return {Object}
*/
this.state = function() {
// Create a fresh copy of the current state.
return JSON.parse(JSON.stringify(stack[stpos]));
};
/**
* Restore state.
* @param {Object} state Canvas state: `{ image:String, strokes:Array }`. Default: current state.
* @private
*/
this.restore = function(state) {
if (!state) state = stack[stpos];
var snapshot = new Image();
snapshot.src = state.image;
snapshot.onload = function() {
draw(this, state.strokes);
};
};
/**
* Init instance. Currently just (re)attach key event listeners.
* @return {MementoCanvas} Class instance.
@ -141,7 +145,8 @@
this.init = function() {
$(document).off('keypress', keyManager);
$(document).on('keypress', keyManager);
return this;
// Save blank state to begin with.
return this.save();
};
/**
* Destroy instance: reset state and remove key event listeners.
@ -152,11 +157,11 @@
return this.reset();
};
};
}
/**
* Memento plugin constructor for jQuery Sketchable instances.
* @param {Object} $instance - A jQuery Sketchable instance.
* @param {Object} $instance jQuery Sketchable instance.
* @memberof $.fn.sketchable.plugins
*/
$.fn.sketchable.plugins.memento = function($instance) {
@ -165,7 +170,7 @@
var callbacks = {
clear: function(elem, data) {
data.memento.reset().save();
data.memento.reset();
},
mouseup: function(elem, data, evt) {
data.memento.save(evt);
@ -176,24 +181,24 @@
};
// A helper function to override user-defined event listeners.
function override(ev) {
function override(evName) {
// Flag event override so that it doesn't get fired more than once.
if (config.options[ev + '$bound']) return;
config.options[ev + '$bound'] = true;
if (config.options['_bound$' + evName]) return;
config.options['_bound$' + evName] = true;
if (config.options.events && typeof config.options.events[ev] === 'function') {
if (config.options.events && typeof config.options.events[evName] === 'function') {
// User has defined this event, so wrap it.
var fn = config.options.events[ev];
config.options.events[ev] = function() {
var fn = config.options.events[evName];
config.options.events[evName] = function() {
// Exec original function first, then exec our callback.
fn.apply($instance, arguments);
callbacks[ev].apply($instance, arguments);
callbacks[evName].apply($instance, arguments);
}
} else {
// User has not defined this event, so attach our callback.
config.options.events[ev] = callbacks[ev];
config.options.events[evName] = callbacks[evName];
}
};
}
// Note: the init event is used to create sketchable instances,
// therefore it should NOT be overriden.
@ -204,38 +209,67 @@
// Expose public API: all jQuery sketchable instances will have these methods.
$.extend($.fn.sketchable.api, {
/**
* Goes back to the previous CANVAS state, if available.
* @memberof $.fn.sketchable
* @example jqueryElem.sketchable('undo');
*/
undo: function() {
var elem = $(this), data = elem.data(namespace);
data.memento.undo();
},
/**
* Goes forward to the previous CANVAS state, if available.
* @memberof $.fn.sketchable
* @example jqueryElem.sketchable('redo');
*/
redo: function() {
var elem = $(this), data = elem.data(namespace);
data.memento.redo();
},
/**
* Save a snapshot of the current CANVAS status.
* @memberof $.fn.sketchable
* @example jqueryElem.sketchable('save');
*/
save: function() {
var elem = $(this), data = elem.data(namespace);
data.memento.save();
// Namespace methods to avoid collisions with other plugins.
memento: {
/**
* Goes back to the previous CANVAS state, if available.
* @return {MementoCanvas}
* @memberof $.fn.sketchable
* @example jqueryElem.sketchable('memento.undo');
*/
undo: function() {
var data = $(this).data(namespace);
return data.memento.undo();
},
/**
* Goes forward to the previous CANVAS state, if available.
* @return {MementoCanvas}
* @memberof $.fn.sketchable
* @example jqueryElem.sketchable('memento.redo');
*/
redo: function() {
var data = $(this).data(namespace);
return data.memento.redo();
},
/**
* Save a snapshot of the current CANVAS status.
* @return {MementoCanvas}
* @memberof $.fn.sketchable
* @example jqueryElem.sketchable('memento.save');
*/
save: function() {
var data = $(this).data(namespace);
return data.memento.save();
},
/**
* Read current snapshot of the CANVAS state: `{ image:String, strokes:Array }`.
* @return {Object}
* @memberof Sketchable
* @example var state = jqueryElem.sketchable('memento.state');
*/
state: function() {
var data = $(this).data(namespace);
return data.memento.state();
},
/**
* Restore a snapshot of the CANVAS.
* @param {Object} state
* @param {String} state.image Base64 image.
* @param {Array} state.strokes Associated strokes.
* @return {MementoCanvas}
* @memberof Sketchable
* @example jqueryElem.sketchable('memento.restore', state);
*/
restore: function(state) {
var data = $(this).data(namespace);
return data.memento.restore(state);
}
}
});
// Initialize plugin here.
config.memento = new MementoCanvas($instance);
config.memento.init().save();
config.memento.init();
};
})(jQuery);

View File

@ -1,5 +1,5 @@
/*!
* Sketchable | v2.0 | Luis A. Leiva | MIT license
* Sketchable | v2.1 | Luis A. Leiva | MIT license
* A plugin for the jSketch drawing library.
*/
@ -21,7 +21,7 @@
* @param {Object} [options] - Configuration (default: {@link Sketchable#defaults}).
* @class
* @global
* @version 1.9
* @version 2.1
* @author Luis A. Leiva
* @license MIT
* @example
@ -84,6 +84,10 @@
timestamp: (new Date).getTime(),
// Save a pointer to the drawing canvas (jSketch instance).
sketch: sketch,
// Save also a pointer to the Sketchable instance.
// In the jQuery version this is not needed,
// since we access the instance via `$('selector').sketchable('method')`.
instance: this,
// Save also a pointer to the given options.
options: options
};
@ -112,7 +116,7 @@
config: function(options) {
var elem = this.elem, data = dataBind(elem)[namespace];
if (options) { // setter
data.options = deepExtend({}, Sketchable.prototype.defaults, options || {});
data.options = deepExtend({}, Sketchable.prototype.defaults, data.options, options);
postProcess(elem);
return this;
} else { // getter
@ -253,11 +257,11 @@
* @static
* @example
* // The following is the default configuration:
* new Sketchable('canvas', {
* new Sketchable('#canvasId', {
* interactive: true,
* mouseupMovements: false,
* relTimestamps: false,
* multitouch: false,
* multitouch: true,
* cssCursors: true,
* filterCoords: false,
* // Event hooks.
@ -292,7 +296,7 @@
* lineJoin: 'round',
* miterLimit: 10
* }
* });
* };
*/
Sketchable.prototype.defaults = {
// In interactive mode, it's possible to draw via mouse/pen/touch input.
@ -300,7 +304,7 @@
// Indicate whether non-drawing strokes should be registered as well.
// Notice that the last mouseUp stroke is never recorded, as the user has already finished drawing.
mouseupMovements: false,
// Inidicate whether timestamps should be relative (start at time 0) or absolute (start at Unix epoch).
// Indicate whether timestamps should be relative (start at time 0) or absolute (start at Unix epoch).
relTimestamps: false,
// Enable multitouch drawing.
multitouch: true,
@ -377,11 +381,7 @@
* @private
*/
function saveMousePos(idx, data, pt) {
// Ensure that coords is properly initialized.
if (!data.coords[idx]) {
data.coords[idx] = [];
}
// Use pointer for easy handling.
// Current coords are already initialized.
var coords = data.coords[idx];
var time = (new Date).getTime();
@ -429,31 +429,6 @@
upHandler(e);
};
/**
* @private
*/
function execTouchEvent(e, callback) {
var elem = e.target, data = dataBind(elem)[namespace], options = data.options;
if (options.multitouch) {
// Track all fingers.
var touches = e.changedTouches;
for (var i = 0; i < touches.length; i++) {
var touch = touches[i];
// Add event type and finger ID.
touch.type = e.type;
touch.identifier = i;
callback(touch);
}
} else {
// Track only the current finger.
var touch = e.touches[0];
touch.type = e.type;
touch.identifier = 0;
callback(touch);
}
e.preventDefault();
};
/**
* @private
*/
@ -485,26 +460,29 @@
// Don't handle right clicks.
if (Event.isRightClick(e)) return false;
var idx = e.identifier || 0;
var elem = e.target, data = dataBind(elem)[namespace], options = data.options;
var idx = e.identifier || 0,
elem = e.target,
data = dataBind(elem)[namespace],
options = data.options;
// Exit early if interactivity is disabled.
if (!options.interactive) return;
data.sketch.isDrawing = true;
var p = getMousePos(e);
// Mark visually 1st point of stroke.
if (options.graphics.firstPointSize > 0) {
data.sketch.beginFill(options.graphics.fillStyle).fillCircle(p.x, p.y, options.graphics.firstPointSize).endFill();
}
// Ensure that coords is properly initialized.
if (!data.coords[idx]) {
data.coords[idx] = [];
}
var coords = data.coords[idx];
if (!coords) coords = [];
// Don't mix mouseup and mousedown in the same stroke.
if (data.coords[idx].length > 0) {
data.strokes.push(data.coords[idx]);
data.coords[idx] = [];
}
if (coords.length > 0) data.strokes.push(coords);
// In any case, ensure that coords is properly reset/initialized.
data.coords[idx] = [];
saveMousePos(idx, data, p);
if (typeof options.events.mousedown === 'function') {
@ -516,19 +494,31 @@
* @private
*/
function moveHandler(e) {
var idx = e.identifier || 0;
var elem = e.target, data = dataBind(elem)[namespace], options = data.options;
var idx = e.identifier || 0
elem = e.target,
data = dataBind(elem)[namespace],
options = data.options;
// Exit early if interactivity is disabled.
if (!options.interactive) return;
//if (!options.mouseupMovements && !data.sketch.isDrawing) return;
// This would grab all penup strokes AFTER drawing something on the canvas for the first time.
// Grab penup strokes AFTER drawing something on the canvas for the first time.
if ( (!options.mouseupMovements || data.strokes.length === 0) && !data.sketch.isDrawing ) return;
var p = getMousePos(e);
if (data.sketch.isDrawing) {
var last = data.coords[idx][ data.coords[idx].length - 1 ];
data.sketch.beginPath().line(last[0], last[1], p.x, p.y).stroke().closePath();
var coords = data.coords[idx];
var last = coords[coords.length - 1];
if (last) {
var brush = data.sketch.beginPath();
if (data.sketch.isDrawing) {
// Style for regular, pendown strokes.
brush.lineStyle(options.graphics.strokeStyle, options.graphics.lineWidth);
} else if (options.mouseupMovements.visible !== false) {
// Style for penup strokes.
brush.lineStyle(options.mouseupMovements.strokeStyle || '#DDD', options.mouseupMovements.lineWidth || 1);
}
brush.line(last[0], last[1], p.x, p.y).stroke().closePath();
}
saveMousePos(idx, data, p);
if (typeof options.events.mousemove === 'function') {
@ -540,12 +530,15 @@
* @private
*/
function upHandler(e) {
var idx = e.identifier || 0;
var elem = e.target, data = dataBind(elem)[namespace], options = data.options;
var idx = e.identifier || 0
elem = e.target,
data = dataBind(elem)[namespace],
options = data.options;
// Exit early if interactivity is disabled.
if (!options.interactive) return;
data.sketch.isDrawing = false;
data.strokes.push(data.coords[idx]);
data.coords[idx] = [];
@ -554,6 +547,31 @@
}
};
/**
* @private
*/
function execTouchEvent(e, callback) {
var elem = e.target, data = dataBind(elem)[namespace], options = data.options;
if (options.multitouch) {
// Track all fingers.
var touches = e.changedTouches;
for (var i = 0; i < touches.length; i++) {
var touch = touches[i];
// Add event type and finger ID.
touch.type = e.type;
touch.identifier = i;
callback(touch);
}
} else {
// Track only the current finger.
var touch = e.touches[0];
touch.type = e.type;
touch.identifier = 0;
callback(touch);
}
e.preventDefault();
};
// Expose.
window.Sketchable = Sketchable;

View File

@ -1,5 +1,5 @@
/*!
* Memento plugin for Sketchable | v2.0 | Luis A. Leiva | MIT license
* Memento plugin for Sketchable | v2.1 | Luis A. Leiva | MIT license
*/
// XXX: Requires `sketchable.utils.js` to be loaded first.
@ -15,7 +15,7 @@
* This class implements the <a href="https://en.wikipedia.org/wiki/Memento_pattern">Memento pattern</a>
* and is part of the {@link Sketchable.plugins.memento} plugin.
* @class
* @version 2.0
* @version 2.1
* @example
* var sketcher = new Sketchable('canvas');
* // This is internally done by the plugin, plus some checks:
@ -27,38 +27,13 @@
var stpos = -1;
var self = this;
/**
* Update state.
* @param {Image} snapshot Image object.
* @param {Array} strokes Strokes associated with snapshot.
* @private
*/
function prev() {
if (stpos > 0) {
stpos--;
var snapshot = new Image();
snapshot.src = stack[stpos].image;
snapshot.onload = function() {
restore(this);
};
}
};
/**
* @private
*/
function next() {
if (stpos < stack.length - 1) {
stpos++;
var snapshot = new Image();
snapshot.src = stack[stpos].image;
snapshot.onload = function() {
restore(this);
};
}
};
/**
* Snashot restorer.
* @param {String} snapshot Base64 image.
* @private
*/
function restore(snapshot) {
// Manipulate canvas via jQuery sketchable API.
function draw(snapshot, strokes) {
// Manipulate canvas via Sketchable API.
// This way, we don't lose default drawing settings et al.
instance.handler(function(elem, data) {
//data.sketch.clear().drawImage(snapshot.src);
@ -67,9 +42,9 @@
data.sketch.clear();
data.sketch.graphics.drawImage(snapshot, 0,0);
// Update strokes.
data.strokes = stack[stpos].strokes.slice();
data.strokes = strokes.slice();
});
};
}
/**
* Key event manager.
* - Undo: "Ctrl + Z"
@ -92,14 +67,16 @@
break;
}
}
};
}
/**
* Goes back to the last saved state, if available.
* @return {MementoCanvas} Class instance.
*/
this.undo = function() {
prev();
if (stpos > 0) {
stpos--;
this.restore();
}
return this;
};
/**
@ -107,7 +84,10 @@
* @return {MementoCanvas} Class instance.
*/
this.redo = function() {
next();
if (stpos < stack.length - 1) {
stpos++;
this.restore();
}
return this;
};
/**
@ -117,7 +97,8 @@
this.reset = function() {
stack = [];
stpos = -1;
return this;
// Save blank state afterward.
return this.save();
};
/**
* Save current state.
@ -137,6 +118,28 @@
});
return this;
};
/**
* Read current state: `{ image:String, strokes:Array }`.
* @return {Object}
*/
this.state = function() {
// Create a fresh copy of the current state.
return JSON.parse(JSON.stringify(stack[stpos]));
};
/**
* Restore state.
* @param {Object} state Canvas state: `{ image:String, strokes:Array }`. Default: current state.
* @private
*/
this.restore = function(state) {
if (!state) state = stack[stpos];
var snapshot = new Image();
snapshot.src = state.image;
snapshot.onload = function() {
draw(this, state.strokes);
};
};
/**
* Init instance. Currently just (re)attach key event listeners.
* @return {MementoCanvas} Class instance.
@ -144,7 +147,8 @@
this.init = function() {
Event.remove(document, 'keypress', keyManager);
Event.add(document, 'keypress', keyManager);
return this;
// Save blank state to begin with.
return this.save();
};
/**
* Destroy instance: reset state and remove key event listeners.
@ -155,11 +159,11 @@
return this.reset();
};
};
}
/**
* Memento plugin constructor for jQuery Sketchable instances.
* @param {Object} sketchable - An Sketchable instance.
* Memento plugin constructor for Sketchable instances.
* @param {Object} sketchable Sketchable instance.
* @memberof Sketchable#plugins
*/
Sketchable.prototype.plugins.memento = function(instance) {
@ -168,7 +172,7 @@
var callbacks = {
clear: function(elem, data) {
data.memento.reset().save();
data.memento.reset();
},
mouseup: function(elem, data, evt) {
data.memento.save(evt);
@ -179,24 +183,24 @@
};
// A helper function to override user-defined event listeners.
function override(ev) {
function override(evName) {
// Flag event override so that it doesn't get fired more than once.
if (config.options[ev + '$bound']) return;
config.options[ev + '$bound'] = true;
if (config.options['_bound$' + evName]) return;
config.options['_bound$' + evName] = true;
if (config.options.events && typeof config.options.events[ev] === 'function') {
if (config.options.events && typeof config.options.events[evName] === 'function') {
// User has defined this event, so wrap it.
var fn = config.options.events[ev];
config.options.events[ev] = function() {
var fn = config.options.events[evName];
config.options.events[evName] = function() {
// Exec original function first, then exec our callback.
fn.apply(instance, arguments);
callbacks[ev].apply(instance, arguments);
callbacks[evName].apply(instance, arguments);
}
} else {
// User has not defined this event, so attach our callback.
config.options.events[ev] = callbacks[ev];
config.options.events[evName] = callbacks[evName];
}
};
}
// Note: the init event is used to create Sketchable instances,
// therefore it should NOT be overriden.
@ -206,39 +210,68 @@
}
// Expose public API: all Sketchable instances will have these methods.
deepExtend(Sketchable.prototype, {
/**
* Goes back to the previous CANVAS state, if available.
* @memberof Sketchable
* @example sketchableInstance.undo();
*/
undo: function() {
var elem = this.elem, data = dataBind(elem)[namespace];
data.memento.undo();
},
/**
* Goes forward to the previous CANVAS state, if available.
* @memberof Sketchable
* @example sketchableInstance.redo();
*/
redo: function() {
var elem = this.elem, data = dataBind(elem)[namespace];
data.memento.redo();
},
/**
* Save a snapshot of the current CANVAS status.
* @memberof Sketchable
* @example sketchableInstance.save();
*/
save: function() {
var elem = this.elem, data = dataBind(elem)[namespace];
data.memento.save();
deepExtend(instance, {
// Namespace methods to avoid collisions with other plugins.
memento: {
/**
* Goes back to the previous CANVAS state, if available.
* @return {MementoCanvas}
* @memberof Sketchable
* @example sketchableInstance.memento.undo();
*/
undo: function() {
var data = dataBind(instance.elem)[namespace];
return data.memento.undo();
},
/**
* Goes forward to the previous CANVAS state, if available.
* @return {MementoCanvas}
* @memberof Sketchable
* @example sketchableInstance.memento.redo();
*/
redo: function() {
var data = dataBind(instance.elem)[namespace];
return data.memento.redo();
},
/**
* Save a snapshot of the current CANVAS state.
* @return {MementoCanvas}
* @memberof Sketchable
* @example sketchableInstance.memento.save();
*/
save: function() {
var data = dataBind(instance.elem)[namespace];
return data.memento.save();
},
/**
* Read current snapshot of the CANVAS state: `{ image:String, strokes:Array }`.
* @return {Object}
* @memberof Sketchable
* @example var state = sketchableInstance.memento.state();
*/
state: function() {
var data = dataBind(instance.elem)[namespace];
return data.memento.state();
},
/**
* Restore a snapshot of the CANVAS.
* @param {Object} state
* @param {String} state.image Base64 image.
* @param {Array} state.strokes Associated strokes.
* @return {MementoCanvas}
* @memberof Sketchable
* @example sketchableInstance.memento.restore();
*/
restore: function(state) {
var data = dataBind(instance.elem)[namespace];
return data.memento.restore(state);
}
}
});
// Initialize plugin here.
config.memento = new MementoCanvas(instance);
config.memento.init().save();
config.memento.init();
};
})(this);

View File

@ -1,5 +1,5 @@
(function(){
var cache = [0], expando = 'data' + +(new Date);
(function() {
var cache = [0], expando = 'data' + Date.now();
function data(elem) {
var cacheIndex = elem[expando],
nextCacheIndex = cache.length;
@ -17,9 +17,11 @@
* @example
* var elem = document.getElementById('foo');
* // Attach private data to element:
* dataBind(elem)['some-name'] = { value: 42 };
* dataBind(elem).someName = { value: 42 };
* dataBind(elem)['other-name'] = { value: 43 };
* // Read private data from element:
* var dat = dataBind(elem)['some-name'];
* var some = dataBind(elem).someName;
* var other = dataBind(elem)['other-name'];
*/
window.dataBind = data;
})();