scratch – Blame information for rev 84
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
84 | office | 1 | (function() { |
2 | |||
3 | 'use strict'; |
||
4 | |||
5 | /** |
||
6 | * SimpleUndo is a very basic javascript undo/redo stack for managing histories of basically anything. |
||
7 | * |
||
8 | * options are: { |
||
9 | * * `provider` : required. a function to call on `save`, which should provide the current state of the historized object through the given "done" callback |
||
10 | * * `maxLength` : the maximum number of items in history |
||
11 | * * `opUpdate` : a function to call to notify of changes in history. Will be called on `save`, `undo`, `redo` and `clear` |
||
12 | * } |
||
13 | * |
||
14 | */ |
||
15 | var SimpleUndo = function(options) { |
||
16 | |||
17 | var settings = options ? options : {}; |
||
18 | var defaultOptions = { |
||
19 | provider: function() { |
||
20 | throw new Error("No provider!"); |
||
21 | }, |
||
22 | maxLength: 30, |
||
23 | onUpdate: function() {} |
||
24 | }; |
||
25 | |||
26 | this.provider = (typeof settings.provider != 'undefined') ? settings.provider : defaultOptions.provider; |
||
27 | this.maxLength = (typeof settings.maxLength != 'undefined') ? settings.maxLength : defaultOptions.maxLength; |
||
28 | this.onUpdate = (typeof settings.onUpdate != 'undefined') ? settings.onUpdate : defaultOptions.onUpdate; |
||
29 | |||
30 | this.initialItem = null; |
||
31 | this.clear(); |
||
32 | }; |
||
33 | |||
34 | function truncate (stack, limit) { |
||
35 | while (stack.length > limit) { |
||
36 | stack.shift(); |
||
37 | } |
||
38 | } |
||
39 | |||
40 | SimpleUndo.prototype.initialize = function(initialItem) { |
||
41 | this.stack[0] = initialItem; |
||
42 | this.initialItem = initialItem; |
||
43 | }; |
||
44 | |||
45 | |||
46 | SimpleUndo.prototype.clear = function() { |
||
47 | this.stack = [this.initialItem]; |
||
48 | this.position = 0; |
||
49 | this.onUpdate(); |
||
50 | }; |
||
51 | |||
52 | SimpleUndo.prototype.save = function() { |
||
53 | this.provider(function(current) { |
||
54 | truncate(this.stack, this.maxLength); |
||
55 | this.position = Math.min(this.position,this.stack.length - 1); |
||
56 | |||
57 | this.stack = this.stack.slice(0, this.position + 1); |
||
58 | this.stack.push(current); |
||
59 | this.position++; |
||
60 | this.onUpdate(); |
||
61 | }.bind(this)); |
||
62 | }; |
||
63 | |||
64 | SimpleUndo.prototype.undo = function(callback) { |
||
65 | if (this.canUndo()) { |
||
66 | var item = this.stack[--this.position]; |
||
67 | this.onUpdate(); |
||
68 | |||
69 | if (callback) { |
||
70 | callback(item); |
||
71 | } |
||
72 | } |
||
73 | }; |
||
74 | |||
75 | SimpleUndo.prototype.redo = function(callback) { |
||
76 | if (this.canRedo()) { |
||
77 | var item = this.stack[++this.position]; |
||
78 | this.onUpdate(); |
||
79 | |||
80 | if (callback) { |
||
81 | callback(item); |
||
82 | } |
||
83 | } |
||
84 | }; |
||
85 | |||
86 | SimpleUndo.prototype.canUndo = function() { |
||
87 | return this.position > 0; |
||
88 | }; |
||
89 | |||
90 | SimpleUndo.prototype.canRedo = function() { |
||
91 | return this.position < this.count(); |
||
92 | }; |
||
93 | |||
94 | SimpleUndo.prototype.count = function() { |
||
95 | return this.stack.length - 1; // -1 because of initial item |
||
96 | }; |
||
97 | |||
98 | |||
99 | |||
100 | |||
101 | |||
102 | //exports |
||
103 | // node module |
||
104 | if (typeof module != 'undefined') { |
||
105 | module.exports = SimpleUndo; |
||
106 | } |
||
107 | |||
108 | // browser global |
||
109 | if (typeof window != 'undefined') { |
||
110 | window.SimpleUndo = SimpleUndo; |
||
111 | } |
||
112 | |||
113 | })(); |