diff --git a/jquery.sketchable.memento.js b/jquery.sketchable.memento.js
new file mode 100644
index 0000000..eeb49c5
--- /dev/null
+++ b/jquery.sketchable.memento.js
@@ -0,0 +1,225 @@
+/*!
+ * Memento plugin for jQuery sketchable | v1.1 | Luis A. Leiva | MIT license
+ */
+/**
+ * @name $
+ * @class
+ * See the jQuery library for full details.
+ * This just documents the method that is added to jQuery by this plugin.
+ */
+/**
+ * @name $.fn
+ * @class
+ * See the jQuery library for full details.
+ * This just documents the method that is added to jQuery by this plugin.
+ */
+;(function($) {
+
+ /**
+ * @name MementoCanvas
+ * @class
+ * @return Object
+ * @example
+ * var mc = new MementoCanvas( $('canvas-selector') );
+ */
+ var MementoCanvas = function($canvas) {
+
+ // Private stuff //////////////////////////////////////////////////////////
+ var stack = [];
+ var stpos = -1;
+ var self = this;
+
+ function prev() {
+ if (stpos > 0) {
+ stpos--;
+ var snapshot = new Image();
+ snapshot.src = stack[stpos].state;
+ snapshot.onload = function() {
+ restore(this);
+ };
+ }
+ };
+
+ function next() {
+ if (stpos < stack.length - 1) {
+ stpos++;
+ var snapshot = new Image();
+ snapshot.src = stack[stpos].state;
+ snapshot.onload = function() {
+ restore(this);
+ };
+ }
+ };
+
+ function restore(snapshot) {
+ // Manipulate canvas via jQuery sketchable API.
+ // This way, we don't lose default drawing settings et al.
+ $canvas.sketchable('handler', function(elem, data){
+ data.sketch.clear();
+ data.sketch.graphics.drawImage(snapshot, 0,0);
+ });
+ };
+
+ // Key event manager.
+ // Undo: "Ctrl + Z"
+ // Redo: "Ctrl + Y" or "Ctrl + Shift + Z"
+ // TODO: decouple shortcut definition, perhaps via jquery.hotkeys plugin.
+ function keyManager(e) {
+ if (e.ctrlKey) {
+ switch (e.which) {
+ case 26: // Z
+ if (e.shiftKey) self.redo();
+ else self.undo();
+ break;
+ case 25: // Y
+ self.redo();
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ // Public stuff ///////////////////////////////////////////////////////////
+
+ /**
+ * Goes back to the last saved state, if available.
+ * @name undo
+ * @memberOf MementoCanvas
+ */
+ this.undo = function() {
+ prev();
+ $canvas.sketchable('handler', function(elem, data) {
+ data.strokes = stack[stpos].strokes.slice();
+ });
+ };
+ /**
+ * Goes forward to the last saved state, if available.
+ * @name redo
+ * @memberOf MementoCanvas
+ */
+ this.redo = function() {
+ next();
+ $canvas.sketchable('handler', function(elem, data) {
+ data.strokes = stack[stpos].strokes.slice();
+ });
+ };
+ /**
+ * Resets stack.
+ * @name reset
+ * @memberOf MementoCanvas
+ */
+ this.reset = function() {
+ stack = [];
+ stpos = -1;
+ };
+ /**
+ * Save state.
+ * @name save
+ * @memberOf MementoCanvas
+ */
+ this.save = function() {
+ stpos++;
+ if (stpos < stack.length) stack.length = stpos;
+ $canvas.sketchable('handler', function(elem, data) {
+ stack.push({ state: elem[0].toDataURL(), strokes: data.strokes.slice() });
+ });
+ };
+ /**
+ * Init instance.
+ * @name init
+ * @memberOf MementoCanvas
+ */
+ this.init = function() {
+ $(document).on("keypress", keyManager);
+ };
+ /**
+ * Destroy instance.
+ * @name destroy
+ * @memberOf MementoCanvas
+ */
+ this.destroy = function() {
+ $(document).off("keypress", keyManager);
+ this.reset();
+ };
+
+ };
+
+ // Bind plugin extension ////////////////////////////////////////////////////
+
+ var plugin = $.fn.sketchable;
+ var availMethods = plugin('methods');
+
+ function configure(elem, options) {
+ $.extend(plugin.defaults, options);
+
+ var mc = new MementoCanvas(elem);
+
+ var callbacks = {
+ init: function(elem, data) {
+ data.memento = mc;
+ data.memento.save();
+ data.memento.init();
+ },
+ mouseup: function(elem, data, e) {
+ data.memento.save();
+ },
+ destroy: function(elem, data) {
+ data.memento.destroy();
+ }
+ };
+
+ // A helper function to override user-defined event listeners.
+ function override(ev) {
+ if (options.events && typeof options.events[ev] === 'function') {
+ var _init = options.events[ev];
+ options.events[ev] = function() {
+ var args = Array.prototype.slice.call(arguments, 0);
+ _init.call(this, args);
+ callbacks[ev].apply(this, args);
+ }
+ } else {
+ plugin.defaults.events[ev] = callbacks[ev];
+ }
+ };
+
+ var events = 'init mouseup destroy'.split(" ");
+ for (var i = 0; i < events.length; i++) {
+ override(events[i]);
+ }
+
+ // Expose public API for jquery.sketchable plugin.
+ $.extend(availMethods, {
+ undo: function() {
+ mc.undo();
+ },
+ redo: function() {
+ mc.redo();
+ },
+ });
+
+ return plugin.defaults;
+ };
+
+ /**
+ * Creates a new memento-capable jQuery.sketchable object.
+ * @param {String|Object} method name of the method to invoke,
+ * or a configuration object.
+ * @return jQuery
+ * @class
+ * @example
+ * $(selector).sketchable();
+ * $(selector).sketchable({interactive:false});
+ */
+ $.fn.sketchable = function(method) {
+ if (typeof method === 'object' || !method) {
+ // Object creation: configure memento extension.
+ var newOptions = configure(this, arguments[0]);
+ return availMethods.init.apply(this, [newOptions]);
+ } else if (availMethods[method]) {
+ // Method invocation: execute it as usual.
+ return availMethods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+ }
+ };
+
+})(jQuery);
diff --git a/jquery.sketchable.memento.min.js b/jquery.sketchable.memento.min.js
new file mode 100644
index 0000000..ceb9586
--- /dev/null
+++ b/jquery.sketchable.memento.min.js
@@ -0,0 +1,4 @@
+/*!
+ * Memento plugin for jQuery sketchable | v1.1 | Luis A. Leiva | MIT license
+ */
+(function(d){var a=function(l){var g=[];var f=-1;var h=this;function k(){if(f>0){f--;var n=new Image();n.src=g[f].state;n.onload=function(){i(this)}}}function j(){if(f