history.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import Parchment from 'parchment';
  2. import Quill from '../core/quill';
  3. import Module from '../core/module';
  4. class History extends Module {
  5. constructor(quill, options) {
  6. super(quill, options);
  7. this.lastRecorded = 0;
  8. this.ignoreChange = false;
  9. this.clear();
  10. this.quill.on(Quill.events.EDITOR_CHANGE, (eventName, delta, oldDelta, source) => {
  11. if (eventName !== Quill.events.TEXT_CHANGE || this.ignoreChange) return;
  12. if (!this.options.userOnly || source === Quill.sources.USER) {
  13. this.record(delta, oldDelta);
  14. } else {
  15. this.transform(delta);
  16. }
  17. });
  18. this.quill.keyboard.addBinding({ key: 'Z', shortKey: true }, this.undo.bind(this));
  19. this.quill.keyboard.addBinding({ key: 'Z', shortKey: true, shiftKey: true }, this.redo.bind(this));
  20. if (/Win/i.test(navigator.platform)) {
  21. this.quill.keyboard.addBinding({ key: 'Y', shortKey: true }, this.redo.bind(this));
  22. }
  23. }
  24. change(source, dest) {
  25. if (this.stack[source].length === 0) return;
  26. let delta = this.stack[source].pop();
  27. this.stack[dest].push(delta);
  28. this.lastRecorded = 0;
  29. this.ignoreChange = true;
  30. this.quill.updateContents(delta[source], Quill.sources.USER);
  31. this.ignoreChange = false;
  32. let index = getLastChangeIndex(delta[source]);
  33. this.quill.setSelection(index);
  34. }
  35. clear() {
  36. this.stack = { undo: [], redo: [] };
  37. }
  38. cutoff() {
  39. this.lastRecorded = 0;
  40. }
  41. record(changeDelta, oldDelta) {
  42. if (changeDelta.ops.length === 0) return;
  43. this.stack.redo = [];
  44. let undoDelta = this.quill.getContents().diff(oldDelta);
  45. let timestamp = Date.now();
  46. if (this.lastRecorded + this.options.delay > timestamp && this.stack.undo.length > 0) {
  47. let delta = this.stack.undo.pop();
  48. undoDelta = undoDelta.compose(delta.undo);
  49. changeDelta = delta.redo.compose(changeDelta);
  50. } else {
  51. this.lastRecorded = timestamp;
  52. }
  53. this.stack.undo.push({
  54. redo: changeDelta,
  55. undo: undoDelta
  56. });
  57. if (this.stack.undo.length > this.options.maxStack) {
  58. this.stack.undo.shift();
  59. }
  60. }
  61. redo() {
  62. this.change('redo', 'undo');
  63. }
  64. transform(delta) {
  65. this.stack.undo.forEach(function(change) {
  66. change.undo = delta.transform(change.undo, true);
  67. change.redo = delta.transform(change.redo, true);
  68. });
  69. this.stack.redo.forEach(function(change) {
  70. change.undo = delta.transform(change.undo, true);
  71. change.redo = delta.transform(change.redo, true);
  72. });
  73. }
  74. undo() {
  75. this.change('undo', 'redo');
  76. }
  77. }
  78. History.DEFAULTS = {
  79. delay: 1000,
  80. maxStack: 100,
  81. userOnly: false
  82. };
  83. function endsWithNewlineChange(delta) {
  84. let lastOp = delta.ops[delta.ops.length - 1];
  85. if (lastOp == null) return false;
  86. if (lastOp.insert != null) {
  87. return typeof lastOp.insert === 'string' && lastOp.insert.endsWith('\n');
  88. }
  89. if (lastOp.attributes != null) {
  90. return Object.keys(lastOp.attributes).some(function(attr) {
  91. return Parchment.query(attr, Parchment.Scope.BLOCK) != null;
  92. });
  93. }
  94. return false;
  95. }
  96. function getLastChangeIndex(delta) {
  97. let deleteLength = delta.reduce(function(length, op) {
  98. length += (op.delete || 0);
  99. return length;
  100. }, 0);
  101. let changeIndex = delta.length() - deleteLength;
  102. if (endsWithNewlineChange(delta)) {
  103. changeIndex -= 1;
  104. }
  105. return changeIndex;
  106. }
  107. export { History as default, getLastChangeIndex };