diff --git a/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button-debug.js b/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button-debug.js
index 50a76a4da80..81ceac4af17 100644
--- a/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button-debug.js
+++ b/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button-debug.js
@@ -25,6 +25,8 @@ YUI.add('moodle-atto_undo-button', function (Y, NAME) {
  * @module moodle-atto_undo-button
  */
 
+var LOGNAME = 'moodle-atto_undo-button';
+
 /**
  * Atto text editor undo plugin.
  *
@@ -86,34 +88,168 @@ Y.namespace('M.atto_undo').Button = Y.Base.create('button', Y.M.editor_atto.Edit
             keys: 89
         });
 
-        this.get('host').on('atto:selectionchanged', this._changeListener, this);
+        // Enable the undo once everything has loaded.
+        this.get('host').on('pluginsloaded', function() {
+            // Adds the current value to the stack.
+            this._addToUndo(this._getHTML());
+            this.get('host').on('atto:selectionchanged', this._changeListener, this);
+        }, this);
+
+        this._updateButtonsStates();
+    },
+
+    /**
+     * Adds an element to the redo stack.
+     *
+     * @method _addToRedo
+     * @private
+     * @param {String} html The HTML content to save.
+     */
+    _addToRedo: function(html) {
+        this._redoStack.push(html);
+    },
+
+    /**
+     * Adds an element to the undo stack.
+     *
+     * @method _addToUndo
+     * @private
+     * @param {String} html The HTML content to save.
+     * @param {Boolean} [clearRedo=false] Whether or not we should clear the redo stack.
+     */
+    _addToUndo: function(html, clearRedo) {
+        var last = this._undoStack[this._undoStack.length - 1];
+
+        if (typeof clearRedo === 'undefined') {
+            clearRedo = false;
+        }
+
+        if (typeof last === 'undefined') {
+            Y.log('Oops, nothing was in the undo stack! There should always be something in there.', 'warn', LOGNAME);
+        }
+
+        if (last !== html) {
+            this._undoStack.push(html);
+            if (clearRedo) {
+                this._redoStack = [];
+            }
+        }
+
+        while (this._undoStack.length > this._maxUndos) {
+            this._undoStack.shift();
+        }
+    },
+
+    /**
+     * Get the editor HTML.
+     *
+     * @method _getHTML
+     * @private
+     * @return {String} The HTML.
+     */
+    _getHTML: function() {
+        return this.get('host').getCleanHTML();
+    },
+
+    /**
+     * Get an element on the redo stack.
+     *
+     * @method _getRedo
+     * @private
+     * @return {String} The HTML to restore, or undefined.
+     */
+    _getRedo: function() {
+        return this._redoStack.pop();
+    },
+
+    /**
+     * Get an element on the undo stack.
+     *
+     * @method _getUndo
+     * @private
+     * @param {String} current The current HTML.
+     * @return {String} The HTML to restore.
+     */
+    _getUndo: function(current) {
+        if (this._undoStack.length === 1) {
+            return this._undoStack[0];
+        }
+
+        last = this._undoStack.pop();
+        if (last === current) {
+            // Oops, the latest undo step is the current content, we should unstack once more.
+            // There is no need to do that in a loop as the same stack should never contain duplicates.
+            last = this._undoStack.pop();
+        }
+
+        // We always need to keep the first element of the stack.
+        if (this._undoStack.length === 0) {
+            this._addToUndo(last);
+        }
+
+        return last;
+    },
+
+    /**
+     * Restore a value from a stack.
+     *
+     * @method _restoreValue
+     * @private
+     * @param {String} html The HTML to restore in the editor.
+     */
+    _restoreValue: function(html) {
+        this.editor.setHTML(html);
+        // We always add the restored value to the stack, otherwise an event could think that
+        // the content has changed and clear the redo stack.
+        this._addToUndo(html);
+    },
+
+    /**
+     * Update the states of the buttons.
+     *
+     * @method _updateButtonsStates
+     * @private
+     */
+    _updateButtonsStates: function() {
+        if (this._undoStack.length > 1) {
+            this.enableButtons('undo');
+        } else {
+            this.disableButtons('undo');
+        }
+
+        if (this._redoStack.length > 0) {
+            this.enableButtons('redo');
+        } else {
+            this.disableButtons('redo');
+        }
     },
 
     /**
      * Handle a click on undo
      *
      * @method _undoHandler
+     * @param {Event} The click event
      * @private
      */
-    _undoHandler: function() {
-        var html = this.editor.getHTML();
+    _undoHandler: function(e) {
+        e.preventDefault();
+        var html = this._getHTML(),
+            undo = this._getUndo(html);
 
-        this._redoStack.push(html);
-        var last = this._undoStack.pop();
-        if (last === html) {
-            last = this._undoStack.pop();
-        }
-        if (last) {
-            this.editor.setHTML(last);
-            // Put it back in the undo stack so a new event wont clear the redo stack.
-            this._undoStack.push(last);
-            this.highlightButtons('redo');
+        // Edge case, but that could happen. We do nothing when the content equals the undo step.
+        if (html === undo) {
+            this._updateButtonsStates();
+            return;
         }
 
-        if (this._undoStack.length === 0) {
-            // If there are no undos left, unhighlight the undo button.
-            this.unHighlightButtons('undo');
-        }
+        // Restore the value.
+        this._restoreValue(undo);
+
+        // Add to the redo stack.
+        this._addToRedo(html);
+
+        // Update the button states.
+        this._updateButtonsStates();
     },
 
     /**
@@ -125,12 +261,19 @@ Y.namespace('M.atto_undo').Button = Y.Base.create('button', Y.M.editor_atto.Edit
      */
     _redoHandler: function(e) {
         e.preventDefault();
-        var html = this.editor.getHTML();
+        var html = this._getHTML(),
+            redo = this._getRedo();
 
-        this._undoStack.push(html);
-        var last = this._redoStack.pop();
-        this.editor.setHTML(last);
-        this._undoStack.push(last);
+        // Edge case, but that could happen. We do nothing when the content equals the redo step.
+        if (html === redo) {
+            this._updateButtonsStates();
+            return;
+        }
+        // Restore the value.
+        this._restoreValue(redo);
+
+        // Update the button states.
+        this._updateButtonsStates();
     },
 
     /**
@@ -144,36 +287,16 @@ Y.namespace('M.atto_undo').Button = Y.Base.create('button', Y.M.editor_atto.Edit
         if (e.event.type.indexOf('key') !== -1) {
             // These are the 4 arrow keys.
             if ((e.event.keyCode !== 39) &&
-                (e.event.keyCode !== 37) &&
-                (e.event.keyCode !== 40) &&
-                (e.event.keyCode !== 38)) {
+                    (e.event.keyCode !== 37) &&
+                    (e.event.keyCode !== 40) &&
+                    (e.event.keyCode !== 38)) {
                 // Skip this event type. We only want focus/mouse/arrow events.
                 return;
             }
         }
 
-        if (typeof this._undoStack === 'undefined') {
-            this._undoStack = [];
-        }
-
-        var last = this._undoStack[this._undoStack.length-1];
-        var html = this.editor.getHTML();
-        if (last !== html) {
-            this._undoStack.push(this.editor.getHTML());
-            this._redoStack = [];
-            this.unHighlightButtons('redo');
-        }
-
-        while (this._undoStack.length > this._maxUndos) {
-            this._undoStack.shift();
-        }
-
-        // Show in the buttons if undo/redo is possible.
-        if (this._undoStack.length) {
-           this.highlightButtons('undo');
-        } else {
-           this.unHighlightButtons('undo');
-        }
+        this._addToUndo(this._getHTML(), true);
+        this._updateButtonsStates();
     }
 });
 
diff --git a/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button-min.js b/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button-min.js
index 0c1a4ef5789..c93d2166e34 100644
--- a/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button-min.js
+++ b/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_undo-button",function(e,t){e.namespace("M.atto_undo").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_maxUndos:40,_undoStack:null,_redoStack:null,initializer:function(){this._undoStack=[],this._redoStack=[],this.addButton({icon:"e/undo",callback:this._undoHandler,buttonName:"undo",keys:90}),this.addButton({icon:"e/redo",callback:this._redoHandler,buttonName:"redo",keys:89}),this.get("host").on("atto:selectionchanged",this._changeListener,this)},_undoHandler:function(){var e=this.editor.getHTML();this._redoStack.push(e);var t=this._undoStack.pop();t===e&&(t=this._undoStack.pop()),t&&(this.editor.setHTML(t),this._undoStack.push(t),this.highlightButtons("redo")),this._undoStack.length===0&&this.unHighlightButtons("undo")},_redoHandler:function(e){e.preventDefault();var t=this.editor.getHTML();this._undoStack.push(t);var n=this._redoStack.pop();this.editor.setHTML(n),this._undoStack.push(n)},_changeListener:function(e){if(e.event.type.indexOf("key")!==-1&&e.event.keyCode!==39&&e.event.keyCode!==37&&e.event.keyCode!==40&&e.event.keyCode!==38)return;typeof this._undoStack=="undefined"&&(this._undoStack=[]);var t=this._undoStack[this._undoStack.length-1],n=this.editor.getHTML();t!==n&&(this._undoStack.push(this.editor.getHTML()),this._redoStack=[],this.unHighlightButtons("redo"));while(this._undoStack.length>this._maxUndos)this._undoStack.shift();this._undoStack.length?this.highlightButtons("undo"):this.unHighlightButtons("undo")}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
+YUI.add("moodle-atto_undo-button",function(e,t){var n="moodle-atto_undo-button";e.namespace("M.atto_undo").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_maxUndos:40,_undoStack:null,_redoStack:null,initializer:function(){this._undoStack=[],this._redoStack=[],this.addButton({icon:"e/undo",callback:this._undoHandler,buttonName:"undo",keys:90}),this.addButton({icon:"e/redo",callback:this._redoHandler,buttonName:"redo",keys:89}),this.get("host").on("pluginsloaded",function(){this._addToUndo(this._getHTML()),this.get("host").on("atto:selectionchanged",this._changeListener,this)},this),this._updateButtonsStates()},_addToRedo:function(e){this._redoStack.push(e)},_addToUndo:function(e,t){var n=this._undoStack[this._undoStack.length-1];typeof t=="undefined"&&(t=!1),typeof n=="undefined",n!==e&&(this._undoStack.push(e),t&&(this._redoStack=[]));while(this._undoStack.length>this._maxUndos)this._undoStack.shift()},_getHTML:function(){return this.get("host").getCleanHTML()},_getRedo:function(){return this._redoStack.pop()},_getUndo:function(e){return this._undoStack.length===1?this._undoStack[0]:(last=this._undoStack.pop(),last===e&&(last=this._undoStack.pop()),this._undoStack.length===0&&this._addToUndo(last),last)},_restoreValue:function(e){this.editor.setHTML(e),this._addToUndo(e)},_updateButtonsStates:function(){this._undoStack.length>1?this.enableButtons("undo"):this.disableButtons("undo"),this._redoStack.length>0?this.enableButtons("redo"):this.disableButtons("redo")},_undoHandler:function(e){e.preventDefault();var t=this._getHTML(),n=this._getUndo(t);if(t===n){this._updateButtonsStates();return}this._restoreValue(n),this._addToRedo(t),this._updateButtonsStates()},_redoHandler:function(e){e.preventDefault();var t=this._getHTML(),n=this._getRedo();if(t===n){this._updateButtonsStates();return}this._restoreValue(n),this._updateButtonsStates()},_changeListener:function(e){if(e.event.type.indexOf("key")!==-1&&e.event.keyCode!==39&&e.event.keyCode!==37&&e.event.keyCode!==40&&e.event.keyCode!==38)return;this._addToUndo(this._getHTML(),!0),this._updateButtonsStates()}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
diff --git a/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button.js b/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button.js
index 50a76a4da80..f13bcedee64 100644
--- a/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button.js
+++ b/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button.js
@@ -25,6 +25,8 @@ YUI.add('moodle-atto_undo-button', function (Y, NAME) {
  * @module moodle-atto_undo-button
  */
 
+var LOGNAME = 'moodle-atto_undo-button';
+
 /**
  * Atto text editor undo plugin.
  *
@@ -86,34 +88,167 @@ Y.namespace('M.atto_undo').Button = Y.Base.create('button', Y.M.editor_atto.Edit
             keys: 89
         });
 
-        this.get('host').on('atto:selectionchanged', this._changeListener, this);
+        // Enable the undo once everything has loaded.
+        this.get('host').on('pluginsloaded', function() {
+            // Adds the current value to the stack.
+            this._addToUndo(this._getHTML());
+            this.get('host').on('atto:selectionchanged', this._changeListener, this);
+        }, this);
+
+        this._updateButtonsStates();
+    },
+
+    /**
+     * Adds an element to the redo stack.
+     *
+     * @method _addToRedo
+     * @private
+     * @param {String} html The HTML content to save.
+     */
+    _addToRedo: function(html) {
+        this._redoStack.push(html);
+    },
+
+    /**
+     * Adds an element to the undo stack.
+     *
+     * @method _addToUndo
+     * @private
+     * @param {String} html The HTML content to save.
+     * @param {Boolean} [clearRedo=false] Whether or not we should clear the redo stack.
+     */
+    _addToUndo: function(html, clearRedo) {
+        var last = this._undoStack[this._undoStack.length - 1];
+
+        if (typeof clearRedo === 'undefined') {
+            clearRedo = false;
+        }
+
+        if (typeof last === 'undefined') {
+        }
+
+        if (last !== html) {
+            this._undoStack.push(html);
+            if (clearRedo) {
+                this._redoStack = [];
+            }
+        }
+
+        while (this._undoStack.length > this._maxUndos) {
+            this._undoStack.shift();
+        }
+    },
+
+    /**
+     * Get the editor HTML.
+     *
+     * @method _getHTML
+     * @private
+     * @return {String} The HTML.
+     */
+    _getHTML: function() {
+        return this.get('host').getCleanHTML();
+    },
+
+    /**
+     * Get an element on the redo stack.
+     *
+     * @method _getRedo
+     * @private
+     * @return {String} The HTML to restore, or undefined.
+     */
+    _getRedo: function() {
+        return this._redoStack.pop();
+    },
+
+    /**
+     * Get an element on the undo stack.
+     *
+     * @method _getUndo
+     * @private
+     * @param {String} current The current HTML.
+     * @return {String} The HTML to restore.
+     */
+    _getUndo: function(current) {
+        if (this._undoStack.length === 1) {
+            return this._undoStack[0];
+        }
+
+        last = this._undoStack.pop();
+        if (last === current) {
+            // Oops, the latest undo step is the current content, we should unstack once more.
+            // There is no need to do that in a loop as the same stack should never contain duplicates.
+            last = this._undoStack.pop();
+        }
+
+        // We always need to keep the first element of the stack.
+        if (this._undoStack.length === 0) {
+            this._addToUndo(last);
+        }
+
+        return last;
+    },
+
+    /**
+     * Restore a value from a stack.
+     *
+     * @method _restoreValue
+     * @private
+     * @param {String} html The HTML to restore in the editor.
+     */
+    _restoreValue: function(html) {
+        this.editor.setHTML(html);
+        // We always add the restored value to the stack, otherwise an event could think that
+        // the content has changed and clear the redo stack.
+        this._addToUndo(html);
+    },
+
+    /**
+     * Update the states of the buttons.
+     *
+     * @method _updateButtonsStates
+     * @private
+     */
+    _updateButtonsStates: function() {
+        if (this._undoStack.length > 1) {
+            this.enableButtons('undo');
+        } else {
+            this.disableButtons('undo');
+        }
+
+        if (this._redoStack.length > 0) {
+            this.enableButtons('redo');
+        } else {
+            this.disableButtons('redo');
+        }
     },
 
     /**
      * Handle a click on undo
      *
      * @method _undoHandler
+     * @param {Event} The click event
      * @private
      */
-    _undoHandler: function() {
-        var html = this.editor.getHTML();
+    _undoHandler: function(e) {
+        e.preventDefault();
+        var html = this._getHTML(),
+            undo = this._getUndo(html);
 
-        this._redoStack.push(html);
-        var last = this._undoStack.pop();
-        if (last === html) {
-            last = this._undoStack.pop();
-        }
-        if (last) {
-            this.editor.setHTML(last);
-            // Put it back in the undo stack so a new event wont clear the redo stack.
-            this._undoStack.push(last);
-            this.highlightButtons('redo');
+        // Edge case, but that could happen. We do nothing when the content equals the undo step.
+        if (html === undo) {
+            this._updateButtonsStates();
+            return;
         }
 
-        if (this._undoStack.length === 0) {
-            // If there are no undos left, unhighlight the undo button.
-            this.unHighlightButtons('undo');
-        }
+        // Restore the value.
+        this._restoreValue(undo);
+
+        // Add to the redo stack.
+        this._addToRedo(html);
+
+        // Update the button states.
+        this._updateButtonsStates();
     },
 
     /**
@@ -125,12 +260,19 @@ Y.namespace('M.atto_undo').Button = Y.Base.create('button', Y.M.editor_atto.Edit
      */
     _redoHandler: function(e) {
         e.preventDefault();
-        var html = this.editor.getHTML();
+        var html = this._getHTML(),
+            redo = this._getRedo();
 
-        this._undoStack.push(html);
-        var last = this._redoStack.pop();
-        this.editor.setHTML(last);
-        this._undoStack.push(last);
+        // Edge case, but that could happen. We do nothing when the content equals the redo step.
+        if (html === redo) {
+            this._updateButtonsStates();
+            return;
+        }
+        // Restore the value.
+        this._restoreValue(redo);
+
+        // Update the button states.
+        this._updateButtonsStates();
     },
 
     /**
@@ -144,36 +286,16 @@ Y.namespace('M.atto_undo').Button = Y.Base.create('button', Y.M.editor_atto.Edit
         if (e.event.type.indexOf('key') !== -1) {
             // These are the 4 arrow keys.
             if ((e.event.keyCode !== 39) &&
-                (e.event.keyCode !== 37) &&
-                (e.event.keyCode !== 40) &&
-                (e.event.keyCode !== 38)) {
+                    (e.event.keyCode !== 37) &&
+                    (e.event.keyCode !== 40) &&
+                    (e.event.keyCode !== 38)) {
                 // Skip this event type. We only want focus/mouse/arrow events.
                 return;
             }
         }
 
-        if (typeof this._undoStack === 'undefined') {
-            this._undoStack = [];
-        }
-
-        var last = this._undoStack[this._undoStack.length-1];
-        var html = this.editor.getHTML();
-        if (last !== html) {
-            this._undoStack.push(this.editor.getHTML());
-            this._redoStack = [];
-            this.unHighlightButtons('redo');
-        }
-
-        while (this._undoStack.length > this._maxUndos) {
-            this._undoStack.shift();
-        }
-
-        // Show in the buttons if undo/redo is possible.
-        if (this._undoStack.length) {
-           this.highlightButtons('undo');
-        } else {
-           this.unHighlightButtons('undo');
-        }
+        this._addToUndo(this._getHTML(), true);
+        this._updateButtonsStates();
     }
 });
 
diff --git a/lib/editor/atto/plugins/undo/yui/src/button/js/button.js b/lib/editor/atto/plugins/undo/yui/src/button/js/button.js
index d95b725234b..026e4a24409 100644
--- a/lib/editor/atto/plugins/undo/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/undo/yui/src/button/js/button.js
@@ -23,6 +23,8 @@
  * @module moodle-atto_undo-button
  */
 
+var LOGNAME = 'moodle-atto_undo-button';
+
 /**
  * Atto text editor undo plugin.
  *
@@ -84,34 +86,168 @@ Y.namespace('M.atto_undo').Button = Y.Base.create('button', Y.M.editor_atto.Edit
             keys: 89
         });
 
-        this.get('host').on('atto:selectionchanged', this._changeListener, this);
+        // Enable the undo once everything has loaded.
+        this.get('host').on('pluginsloaded', function() {
+            // Adds the current value to the stack.
+            this._addToUndo(this._getHTML());
+            this.get('host').on('atto:selectionchanged', this._changeListener, this);
+        }, this);
+
+        this._updateButtonsStates();
+    },
+
+    /**
+     * Adds an element to the redo stack.
+     *
+     * @method _addToRedo
+     * @private
+     * @param {String} html The HTML content to save.
+     */
+    _addToRedo: function(html) {
+        this._redoStack.push(html);
+    },
+
+    /**
+     * Adds an element to the undo stack.
+     *
+     * @method _addToUndo
+     * @private
+     * @param {String} html The HTML content to save.
+     * @param {Boolean} [clearRedo=false] Whether or not we should clear the redo stack.
+     */
+    _addToUndo: function(html, clearRedo) {
+        var last = this._undoStack[this._undoStack.length - 1];
+
+        if (typeof clearRedo === 'undefined') {
+            clearRedo = false;
+        }
+
+        if (typeof last === 'undefined') {
+            Y.log('Oops, nothing was in the undo stack! There should always be something in there.', 'warn', LOGNAME);
+        }
+
+        if (last !== html) {
+            this._undoStack.push(html);
+            if (clearRedo) {
+                this._redoStack = [];
+            }
+        }
+
+        while (this._undoStack.length > this._maxUndos) {
+            this._undoStack.shift();
+        }
+    },
+
+    /**
+     * Get the editor HTML.
+     *
+     * @method _getHTML
+     * @private
+     * @return {String} The HTML.
+     */
+    _getHTML: function() {
+        return this.get('host').getCleanHTML();
+    },
+
+    /**
+     * Get an element on the redo stack.
+     *
+     * @method _getRedo
+     * @private
+     * @return {String} The HTML to restore, or undefined.
+     */
+    _getRedo: function() {
+        return this._redoStack.pop();
+    },
+
+    /**
+     * Get an element on the undo stack.
+     *
+     * @method _getUndo
+     * @private
+     * @param {String} current The current HTML.
+     * @return {String} The HTML to restore.
+     */
+    _getUndo: function(current) {
+        if (this._undoStack.length === 1) {
+            return this._undoStack[0];
+        }
+
+        last = this._undoStack.pop();
+        if (last === current) {
+            // Oops, the latest undo step is the current content, we should unstack once more.
+            // There is no need to do that in a loop as the same stack should never contain duplicates.
+            last = this._undoStack.pop();
+        }
+
+        // We always need to keep the first element of the stack.
+        if (this._undoStack.length === 0) {
+            this._addToUndo(last);
+        }
+
+        return last;
+    },
+
+    /**
+     * Restore a value from a stack.
+     *
+     * @method _restoreValue
+     * @private
+     * @param {String} html The HTML to restore in the editor.
+     */
+    _restoreValue: function(html) {
+        this.editor.setHTML(html);
+        // We always add the restored value to the stack, otherwise an event could think that
+        // the content has changed and clear the redo stack.
+        this._addToUndo(html);
+    },
+
+    /**
+     * Update the states of the buttons.
+     *
+     * @method _updateButtonsStates
+     * @private
+     */
+    _updateButtonsStates: function() {
+        if (this._undoStack.length > 1) {
+            this.enableButtons('undo');
+        } else {
+            this.disableButtons('undo');
+        }
+
+        if (this._redoStack.length > 0) {
+            this.enableButtons('redo');
+        } else {
+            this.disableButtons('redo');
+        }
     },
 
     /**
      * Handle a click on undo
      *
      * @method _undoHandler
+     * @param {Event} The click event
      * @private
      */
-    _undoHandler: function() {
-        var html = this.editor.getHTML();
+    _undoHandler: function(e) {
+        e.preventDefault();
+        var html = this._getHTML(),
+            undo = this._getUndo(html);
 
-        this._redoStack.push(html);
-        var last = this._undoStack.pop();
-        if (last === html) {
-            last = this._undoStack.pop();
-        }
-        if (last) {
-            this.editor.setHTML(last);
-            // Put it back in the undo stack so a new event wont clear the redo stack.
-            this._undoStack.push(last);
-            this.highlightButtons('redo');
+        // Edge case, but that could happen. We do nothing when the content equals the undo step.
+        if (html === undo) {
+            this._updateButtonsStates();
+            return;
         }
 
-        if (this._undoStack.length === 0) {
-            // If there are no undos left, unhighlight the undo button.
-            this.unHighlightButtons('undo');
-        }
+        // Restore the value.
+        this._restoreValue(undo);
+
+        // Add to the redo stack.
+        this._addToRedo(html);
+
+        // Update the button states.
+        this._updateButtonsStates();
     },
 
     /**
@@ -123,12 +259,19 @@ Y.namespace('M.atto_undo').Button = Y.Base.create('button', Y.M.editor_atto.Edit
      */
     _redoHandler: function(e) {
         e.preventDefault();
-        var html = this.editor.getHTML();
+        var html = this._getHTML(),
+            redo = this._getRedo();
 
-        this._undoStack.push(html);
-        var last = this._redoStack.pop();
-        this.editor.setHTML(last);
-        this._undoStack.push(last);
+        // Edge case, but that could happen. We do nothing when the content equals the redo step.
+        if (html === redo) {
+            this._updateButtonsStates();
+            return;
+        }
+        // Restore the value.
+        this._restoreValue(redo);
+
+        // Update the button states.
+        this._updateButtonsStates();
     },
 
     /**
@@ -142,35 +285,15 @@ Y.namespace('M.atto_undo').Button = Y.Base.create('button', Y.M.editor_atto.Edit
         if (e.event.type.indexOf('key') !== -1) {
             // These are the 4 arrow keys.
             if ((e.event.keyCode !== 39) &&
-                (e.event.keyCode !== 37) &&
-                (e.event.keyCode !== 40) &&
-                (e.event.keyCode !== 38)) {
+                    (e.event.keyCode !== 37) &&
+                    (e.event.keyCode !== 40) &&
+                    (e.event.keyCode !== 38)) {
                 // Skip this event type. We only want focus/mouse/arrow events.
                 return;
             }
         }
 
-        if (typeof this._undoStack === 'undefined') {
-            this._undoStack = [];
-        }
-
-        var last = this._undoStack[this._undoStack.length-1];
-        var html = this.editor.getHTML();
-        if (last !== html) {
-            this._undoStack.push(this.editor.getHTML());
-            this._redoStack = [];
-            this.unHighlightButtons('redo');
-        }
-
-        while (this._undoStack.length > this._maxUndos) {
-            this._undoStack.shift();
-        }
-
-        // Show in the buttons if undo/redo is possible.
-        if (this._undoStack.length) {
-           this.highlightButtons('undo');
-        } else {
-           this.unHighlightButtons('undo');
-        }
+        this._addToUndo(this._getHTML(), true);
+        this._updateButtonsStates();
     }
 });