Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!
Guest

UndoManager

By: a guest on Jan 2nd, 2013  |  syntax: JavaScript  |  size: 5.53 KB  |  views: 41  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. (function(tinymce) {
  2.         var Dispatcher = tinymce.util.Dispatcher;
  3.  
  4.         tinymce.UndoManager = function(editor) {
  5.                 var self, index = 0, data = [], beforeBookmark, onAdd, onUndo, onRedo;
  6.  
  7.                 function getContent() {
  8.                         // Remove whitespace before/after and remove pure bogus nodes
  9.                         return tinymce.trim(editor.getContent({format : 'raw', no_events : 1}).replace(/<span[^>]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\/span>/g, ''));
  10.                 };
  11.  
  12.                 function addNonTypingUndoLevel() {
  13.                         self.typing = false;
  14.                         self.add();
  15.                 };
  16.  
  17.                 // Create event instances
  18.                 onBeforeAdd = new Dispatcher(self);
  19.                 onAdd       = new Dispatcher(self);
  20.                 onUndo      = new Dispatcher(self);
  21.                 onRedo      = new Dispatcher(self);
  22.  
  23.                 // Pass though onAdd event from UndoManager to Editor as onChange
  24.                 onAdd.add(function(undoman, level) {
  25.                         if (undoman.hasUndo())
  26.                                 return editor.onChange.dispatch(editor, level, undoman);
  27.                 });
  28.  
  29.                 // Pass though onUndo event from UndoManager to Editor
  30.                 onUndo.add(function(undoman, level) {
  31.                         return editor.onUndo.dispatch(editor, level, undoman);
  32.                 });
  33.  
  34.                 // Pass though onRedo event from UndoManager to Editor
  35.                 onRedo.add(function(undoman, level) {
  36.                         return editor.onRedo.dispatch(editor, level, undoman);
  37.                 });
  38.  
  39.                 // Add initial undo level when the editor is initialized
  40.                 editor.onInit.add(function() {
  41.                         self.add();
  42.                 });
  43.  
  44.                 // Get position before an execCommand is processed
  45.                 editor.onBeforeExecCommand.add(function(ed, cmd, ui, val, args) {
  46.                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!args || !args.skip_undo)) {
  47.                                 self.beforeChange();
  48.                         }
  49.                 });
  50.  
  51.                 // Add undo level after an execCommand call was made
  52.                 editor.onExecCommand.add(function(ed, cmd, ui, val, args) {
  53.                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!args || !args.skip_undo)) {
  54.                                 self.add();
  55.                         }
  56.                 });
  57.  
  58.                 // Add undo level on save contents, drag end and blur/focusout
  59.                 editor.onSaveContent.add(addNonTypingUndoLevel);
  60.                 editor.dom.bind(editor.dom.getRoot(), 'dragend', addNonTypingUndoLevel);
  61.                 editor.dom.bind(editor.getBody(), 'focusout', function(e) {
  62.                         if (!editor.removed && self.typing) {
  63.                                 addNonTypingUndoLevel();
  64.                         }
  65.                 });
  66.  
  67.                 editor.onKeyUp.add(function(editor, e) {
  68.                         var keyCode = e.keyCode;
  69.  
  70.                         if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45 || keyCode == 13 || e.ctrlKey) {
  71.                                 addNonTypingUndoLevel();
  72.                         }
  73.                 });
  74.  
  75.                 editor.onKeyDown.add(function(editor, e) {
  76.                         var keyCode = e.keyCode;
  77.  
  78.                         // Is caracter positon keys left,right,up,down,home,end,pgdown,pgup,enter
  79.                         if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45) {
  80.                                 if (self.typing) {
  81.                                         addNonTypingUndoLevel();
  82.                                 }
  83.  
  84.                                 return;
  85.                         }
  86.  
  87.                         // If key isn't shift,ctrl,alt,capslock,metakey
  88.                         if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !self.typing) {
  89.                                 self.beforeChange();
  90.                                 self.typing = true;
  91.                                 self.add();
  92.                         }
  93.                 });
  94.  
  95.                 editor.onMouseDown.add(function(editor, e) {
  96.                         if (self.typing) {
  97.                                 addNonTypingUndoLevel();
  98.                         }
  99.                 });
  100.  
  101.                 // Add keyboard shortcuts for undo/redo keys
  102.                 editor.addShortcut('ctrl+z', 'undo_desc', 'Undo');
  103.                 editor.addShortcut('ctrl+y', 'redo_desc', 'Redo');
  104.  
  105.                 self = {
  106.                         // Explose for debugging reasons
  107.                         data : data,
  108.  
  109.                         typing : false,
  110.                        
  111.                         onBeforeAdd: onBeforeAdd,
  112.  
  113.                         onAdd : onAdd,
  114.  
  115.                         onUndo : onUndo,
  116.  
  117.                         onRedo : onRedo,
  118.  
  119.                         beforeChange : function() {
  120.                                 beforeBookmark = editor.selection.getBookmark(2, true);
  121.                         },
  122.  
  123.                         add : function(level) {
  124.                                 var i, settings = editor.settings, lastLevel;
  125.  
  126.                                 level = level || {};
  127.                                 level.content = getContent();
  128.                                
  129.                                 self.onBeforeAdd.dispatch(self, level);
  130.  
  131.                                 // Add undo level if needed
  132.                                 lastLevel = data[index];
  133.                                 if (lastLevel && lastLevel.content == level.content)
  134.                                         return null;
  135.  
  136.                                 // Set before bookmark on previous level
  137.                                 if (data[index])
  138.                                         data[index].beforeBookmark = beforeBookmark;
  139.  
  140.                                 // Time to compress
  141.                                 if (settings.custom_undo_redo_levels) {
  142.                                         if (data.length > settings.custom_undo_redo_levels) {
  143.                                                 for (i = 0; i < data.length - 1; i++)
  144.                                                         data[i] = data[i + 1];
  145.  
  146.                                                 data.length--;
  147.                                                 index = data.length;
  148.                                         }
  149.                                 }
  150.  
  151.                                 // Get a non intrusive normalized bookmark
  152.                                 level.bookmark = editor.selection.getBookmark(2, true);
  153.  
  154.                                 // Crop array if needed
  155.                                 if (index < data.length - 1)
  156.                                         data.length = index + 1;
  157.  
  158.                                 data.push(level);
  159.                                 index = data.length - 1;
  160.  
  161.                                 self.onAdd.dispatch(self, level);
  162.                                 editor.isNotDirty = 0;
  163.  
  164.                                 return level;
  165.                         },
  166.  
  167.                         undo : function() {
  168.                                 var level, i;
  169.  
  170.                                 if (self.typing) {
  171.                                         self.add();
  172.                                         self.typing = false;
  173.                                 }
  174.  
  175.                                 if (index > 0) {
  176.                                         level = data[--index];
  177.  
  178.                                         editor.setContent(level.content, {format : 'raw'});
  179.                                         editor.selection.moveToBookmark(level.beforeBookmark);
  180.  
  181.                                         self.onUndo.dispatch(self, level);
  182.                                 }
  183.  
  184.                                 return level;
  185.                         },
  186.  
  187.                         redo : function() {
  188.                                 var level;
  189.  
  190.                                 if (index < data.length - 1) {
  191.                                         level = data[++index];
  192.  
  193.                                         editor.setContent(level.content, {format : 'raw'});
  194.                                         editor.selection.moveToBookmark(level.bookmark);
  195.  
  196.                                         self.onRedo.dispatch(self, level);
  197.                                 }
  198.  
  199.                                 return level;
  200.                         },
  201.  
  202.                         clear : function() {
  203.                                 data = [];
  204.                                 index = 0;
  205.                                 self.typing = false;
  206.                         },
  207.  
  208.                         hasUndo : function() {
  209.                                 return index > 0 || this.typing;
  210.                         },
  211.  
  212.                         hasRedo : function() {
  213.                                 return index < data.length - 1 && !this.typing;
  214.                         }
  215.                 };
  216.  
  217.                 return self;
  218.         };
  219. })(tinymce);