undo.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. ///import core
  2. ///commands 撤销和重做
  3. ///commandsName Undo,Redo
  4. ///commandsTitle 撤销,重做
  5. /**
  6. * @description 回退
  7. * @author zhanyi
  8. */
  9. UE.plugins['undo'] = function() {
  10. var me = this,
  11. maxUndoCount = me.options.maxUndoCount || 20,
  12. maxInputCount = me.options.maxInputCount || 20,
  13. fillchar = new RegExp(domUtils.fillChar + '|<\/hr>','gi'),// ie会产生多余的</hr>
  14. //在比较时,需要过滤掉这些属性
  15. specialAttr = /\b(?:href|src|name)="[^"]*?"/gi;
  16. function UndoManager() {
  17. this.list = [];
  18. this.index = 0;
  19. this.hasUndo = false;
  20. this.hasRedo = false;
  21. this.undo = function() {
  22. if ( this.hasUndo ) {
  23. var currentScene = this.getScene(),
  24. lastScene = this.list[this.index];
  25. if ( lastScene.content.replace(specialAttr,'') != currentScene.content.replace(specialAttr,'') ) {
  26. this.save();
  27. }
  28. if(!this.list[this.index - 1] && this.list.length == 1){
  29. this.reset();
  30. return;
  31. }
  32. while ( this.list[this.index].content == this.list[this.index - 1].content ) {
  33. this.index--;
  34. if ( this.index == 0 ) {
  35. return this.restore( 0 )
  36. }
  37. }
  38. this.restore( --this.index );
  39. }
  40. };
  41. this.redo = function() {
  42. if ( this.hasRedo ) {
  43. while ( this.list[this.index].content == this.list[this.index + 1].content ) {
  44. this.index++;
  45. if ( this.index == this.list.length - 1 ) {
  46. return this.restore( this.index )
  47. }
  48. }
  49. this.restore( ++this.index );
  50. }
  51. };
  52. this.restore = function() {
  53. var scene = this.list[this.index];
  54. //trace:873
  55. //去掉展位符
  56. me.document.body.innerHTML = scene.bookcontent.replace(fillchar,'');
  57. //处理undo后空格不展位的问题
  58. if(browser.ie){
  59. for(var i=0,pi,ps = me.document.getElementsByTagName('p');pi = ps[i++];){
  60. if(pi.innerHTML == ''){
  61. domUtils.fillNode(me.document,pi);
  62. }
  63. }
  64. }
  65. var range = new dom.Range( me.document );
  66. range.moveToBookmark( {
  67. start : '_baidu_bookmark_start_',
  68. end : '_baidu_bookmark_end_',
  69. id : true
  70. //去掉true 是为了<b>|</b>,回退后还能在b里
  71. //todo safari里输入中文时,会因为改变了dom而导致丢字
  72. } );
  73. //trace:1278 ie9block元素为空,将出现光标定位的问题,必须填充内容
  74. if(browser.ie && browser.version == 9 && range.collapsed && domUtils.isBlockElm(range.startContainer) && domUtils.isEmptyNode(range.startContainer)){
  75. domUtils.fillNode(range.document,range.startContainer);
  76. }
  77. range.select(!browser.gecko);
  78. setTimeout(function(){
  79. range.scrollToView(me.autoHeightEnabled,me.autoHeightEnabled ? domUtils.getXY(me.iframe).y:0);
  80. },200);
  81. this.update();
  82. //table的单独处理
  83. if(me.currentSelectedArr){
  84. me.currentSelectedArr = [];
  85. var tds = me.document.getElementsByTagName('td');
  86. for(var i=0,td;td=tds[i++];){
  87. if(td.className == me.options.selectedTdClass){
  88. me.currentSelectedArr.push(td);
  89. }
  90. }
  91. }
  92. this.clearKey();
  93. //不能把自己reset了
  94. me.fireEvent('reset',true);
  95. me.fireEvent('contentchange')
  96. };
  97. this.getScene = function() {
  98. var range = me.selection.getRange(),
  99. cont = me.body.innerHTML.replace(fillchar,'');
  100. //有可能边界落到了<table>|<tbody>这样的位置,所以缩一下位置
  101. range.shrinkBoundary();
  102. browser.ie && (cont = cont.replace(/>&nbsp;</g,'><').replace(/\s*</g,'').replace(/>\s*/g,'>'));
  103. var bookmark = range.createBookmark( true, true ),
  104. bookCont = me.body.innerHTML.replace(fillchar,'');
  105. range.moveToBookmark( bookmark ).select( true );
  106. return {
  107. bookcontent : bookCont,
  108. content : cont
  109. }
  110. };
  111. this.save = function() {
  112. var currentScene = this.getScene(),
  113. lastScene = this.list[this.index];
  114. //内容相同位置相同不存
  115. if ( lastScene && lastScene.content == currentScene.content &&
  116. lastScene.bookcontent == currentScene.bookcontent
  117. ) {
  118. return;
  119. }
  120. this.list = this.list.slice( 0, this.index + 1 );
  121. this.list.push( currentScene );
  122. //如果大于最大数量了,就把最前的剔除
  123. if ( this.list.length > maxUndoCount ) {
  124. this.list.shift();
  125. }
  126. this.index = this.list.length - 1;
  127. this.clearKey();
  128. //跟新undo/redo状态
  129. this.update();
  130. me.fireEvent('contentchange')
  131. };
  132. this.update = function() {
  133. this.hasRedo = this.list[this.index + 1] ? true : false;
  134. this.hasUndo = this.list[this.index - 1] || this.list.length == 1 ? true : false;
  135. };
  136. this.reset = function() {
  137. this.list = [];
  138. this.index = 0;
  139. this.hasUndo = false;
  140. this.hasRedo = false;
  141. this.clearKey();
  142. };
  143. this.clearKey = function(){
  144. keycont = 0;
  145. lastKeyCode = null;
  146. }
  147. }
  148. me.undoManger = new UndoManager();
  149. function saveScene() {
  150. this.undoManger.save()
  151. }
  152. me.addListener( 'beforeexeccommand', saveScene );
  153. me.addListener( 'afterexeccommand', saveScene );
  154. me.addListener('reset',function(type,exclude){
  155. if(!exclude)
  156. me.undoManger.reset();
  157. });
  158. me.commands['redo'] = me.commands['undo'] = {
  159. execCommand : function( cmdName ) {
  160. me.undoManger[cmdName]();
  161. },
  162. queryCommandState : function( cmdName ) {
  163. return me.undoManger['has' + (cmdName.toLowerCase() == 'undo' ? 'Undo' : 'Redo')] ? 0 : -1;
  164. },
  165. notNeedUndo : 1
  166. };
  167. var keys = {
  168. // /*Backspace*/ 8:1, /*Delete*/ 46:1,
  169. /*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1,
  170. 37:1, 38:1, 39:1, 40:1,
  171. 13:1 /*enter*/
  172. },
  173. keycont = 0,
  174. lastKeyCode;
  175. me.addListener( 'keydown', function( type, evt ) {
  176. var keyCode = evt.keyCode || evt.which;
  177. if ( !keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey ) {
  178. if ( me.undoManger.list.length == 0 || ((keyCode == 8 ||keyCode == 46) && lastKeyCode != keyCode) ) {
  179. me.undoManger.save();
  180. lastKeyCode = keyCode;
  181. return
  182. }
  183. //trace:856
  184. //修正第一次输入后,回退,再输入要到keycont>maxInputCount才能在回退的问题
  185. if(me.undoManger.list.length == 2 && me.undoManger.index == 0 && keycont == 0){
  186. me.undoManger.list.splice(1,1);
  187. me.undoManger.update();
  188. }
  189. lastKeyCode = keyCode;
  190. keycont++;
  191. if ( keycont > maxInputCount ) {
  192. setTimeout( function() {
  193. me.undoManger.save();
  194. }, 0 );
  195. }
  196. }
  197. } )
  198. };