commandz 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f2704f6b71bfb108f4089d4539879479a1f5e6c9
4
- data.tar.gz: a7511dae7fbb4fa5b5d499947f1712718a3bc2c0
3
+ metadata.gz: 706f3e28800a1bd1c1e659a2fcf7a355c8b03fab
4
+ data.tar.gz: 4b3451066e237bbf6e4c75e8245f9ac1e528a490
5
5
  SHA512:
6
- metadata.gz: 0c650267d1bac2d6e1525a1f77a044cfd5929a9baed475f8b53a6bd51430b000731031a4182f196f3611d180f7bc2fe162a8c11fb4e07fc0d0f63dc9cec4b1b2
7
- data.tar.gz: 076c76e6f4e9ba0dbfe2efaea07317a63eb902382ccf9ec0dfce6f7cf8dca245e039cf5e232cfd7fb4dee0ca92322ae05284de2e617ff03c907f498f865986c3
6
+ metadata.gz: e2caf799f9bab92f94978984affae1337d23acb9dba7f4476d3a55637a5ed810e60d08ce08ba4c708b7251f0d5df24e65c2b6ea61f4bf43d69891bf9850ecb35
7
+ data.tar.gz: aa1ddb7d6a90db28f177d10b24eac586e94dfc16eea054ed29aa372d30d3d9dbdf609e8a9a5c42f03ee44079991efa6737f6840372f185814d858d5b011951aa
@@ -1,3 +1,9 @@
1
+ ## [v0.1.0](https://github.com/EtienneLem/commandz/tree/v0.1.0)
2
+ - Rename `CommandZ.commands` -> `CommandZ.history`
3
+ - Rename `CommandZ.onChange` -> `CommandZ.onStatusChange`
4
+ - Add storage with threshold support
5
+ - Add `CommandZ.reset()`
6
+
1
7
  ## [v0.0.3](https://github.com/EtienneLem/commandz/tree/v0.0.3)
2
8
  - Fix a bug where `handleChange` wouldn’t be called after `CommandZ.execute()`
3
9
 
data/README.md CHANGED
@@ -16,14 +16,19 @@
16
16
 
17
17
  ## Table of contents
18
18
  - [API](#api)
19
- - [CommandZ.execute](#execute)
20
- - [CommandZ.undo](#undo)
21
- - [CommandZ.redo](#redo)
22
- - [CommandZ.status](#status)
23
- - [CommandZ.onChange](#onchange)
24
- - [CommandZ.clear](#clear)
25
- - [CommandZ.keyboardShortcuts](#keyboardshortcuts)
19
+ - [execute](#execute) | [store](#store)
20
+ - [undo (commands)](#undo-commands) | [undo (storage)](#undo-storage)
21
+ - [redo (commands)](#redo-commands) | [redo (storage)](#redo-storage)
22
+ - [setThreshold (storage only)](#setthreshold-storage-only)
23
+ - [status](#status)
24
+ - [onStatusChange](#onstatuschange)
25
+ - [onStorageChange](#onstoragechange)
26
+ - [clear](#clear)
27
+ - [reset](#reset)
28
+ - [keyboardShortcuts](#keyboardshortcuts)
26
29
  - [DOM Example](#dom-example)
30
+ - [Commands](#commands)
31
+ - [Storage](#storage)
27
32
  - [Setup](#setup)
28
33
  - [Rails](#rails)
29
34
  - [Other](#other)
@@ -39,8 +44,9 @@ COMMAND: {
39
44
  }
40
45
  ```
41
46
 
42
- ### execute
43
- Receive `COMMAND` or `COMMANDS` and execute `COMMAND.up()`.
47
+ ### #execute
48
+ Receive `COMMAND` or `COMMANDS` and execute `COMMAND.up()`.<br>
49
+ Store commands as a `{ command: COMMAND }` object in the history array.
44
50
 
45
51
  **Single command per history item**<br>
46
52
  Store one history item per `COMMAND`.
@@ -79,8 +85,8 @@ console.log(CommandZ.commands.length) // => 1
79
85
  console.log(CommandZ.index) // => 0
80
86
  ```
81
87
 
82
- ### undo
83
- Call `COMMAND.down()` and set the index to the previous command.
88
+ ### #undo (commands)
89
+ Call `COMMAND.down()` and set the index to the previous history item.
84
90
 
85
91
  ```js
86
92
  CommandZ.execute({
@@ -104,8 +110,8 @@ console.log(CommandZ.commands.length) // => 2
104
110
  console.log(CommandZ.index) // => -1
105
111
  ```
106
112
 
107
- ### redo
108
- Set the index to the next command and call `COMMAND.up()`.
113
+ ### #redo (commands)
114
+ Set the index to the next history item and call `COMMAND.up()`.
109
115
 
110
116
  ```js
111
117
  CommandZ.execute({
@@ -125,7 +131,73 @@ console.log(CommandZ.commands.length) // => 2
125
131
  console.log(CommandZ.index) // => 0
126
132
  ```
127
133
 
128
- ### status
134
+ ### #store
135
+ Store data as a `{ data: … }` object in the history array.
136
+
137
+ ```js
138
+ CommandZ.store({ width: 100, height: 100 })
139
+ ```
140
+
141
+ ### #undo (storage)
142
+ Set the index to the previous history item and send data via [`CommandZ.onStorageChange`](#onstoragechange).
143
+
144
+ ```js
145
+ CommandZ.onStorageChange(function(data) {
146
+ console.log(data)
147
+ })
148
+
149
+ CommandZ.store({ width: 100, height: 100 })
150
+ CommandZ.undo()
151
+
152
+ console.log(CommandZ.commands.length) // => 1
153
+ console.log(CommandZ.index) // => -1
154
+
155
+ CommandZ.store({ width: 100, height: 100 })
156
+ CommandZ.store({ width: 200, height: 200 })
157
+ CommandZ.undo() # => { width: 100, height: 100 }
158
+
159
+ console.log(CommandZ.commands.length) // => 2
160
+ console.log(CommandZ.index) // => 0
161
+ ```
162
+
163
+ ### #redo (storage)
164
+ Set the index to the next history item and send data via [`CommandZ.onStorageChange`](#onstoragechange).
165
+
166
+ ```js
167
+ CommandZ.onStorageChange(function(data) {
168
+ console.log(data)
169
+ })
170
+
171
+ CommandZ.store({ width: 100, height: 100 })
172
+ CommandZ.store({ width: 200, height: 200 })
173
+ CommandZ.undo() # => { width: 100, height: 100 }
174
+ CommandZ.redo() # => { width: 200, height: 200 }
175
+
176
+ console.log(CommandZ.commands.length) // => 2
177
+ console.log(CommandZ.index) // => 1
178
+ ```
179
+
180
+ ### #setThreshold (storage only)
181
+ Unlike commands, you can allow your users to spam the `CMD+Z` button without restoring every states at every steps.<br>
182
+ Threshold is set in `milliseconds`.
183
+
184
+ ```js
185
+ CommandZ.setThreshold(500)
186
+ CommandZ.onStorageChange(function(data) {
187
+ console.log(data)
188
+ })
189
+
190
+ CommandZ.store({ width: 100, height: 100 })
191
+ CommandZ.store({ width: 200, height: 200 })
192
+ CommandZ.store({ width: 300, height: 300 })
193
+
194
+ CommandZ.undo(100)
195
+
196
+ // Wait 500ms
197
+ // => { width: 100, height: 100 }
198
+ ```
199
+
200
+ ### #status
129
201
  Return the current status.
130
202
 
131
203
  ```js
@@ -144,11 +216,11 @@ console.log(CommandZ.commands.length) // => 2
144
216
  console.log(CommandZ.index) // => 1
145
217
  ```
146
218
 
147
- ### onChange
219
+ ### #onStatusChange
148
220
  Register a callback that will be called with the `status` every time there’s a change to the history.
149
221
 
150
222
  ```js
151
- CommandZ.onChange(function(status) {
223
+ CommandZ.onStatusChange(function(status) {
152
224
  console.log(status)
153
225
  })
154
226
 
@@ -166,7 +238,21 @@ CommandZ.undo() // => { canUndo: true, canRedo: true }
166
238
  CommandZ.undo() // => { canUndo: false, canRedo: true }
167
239
  ```
168
240
 
169
- ### clear
241
+ ### #onStorageChange
242
+ Register a callback that will be called with the `data` on undo/redo.
243
+
244
+ ```js
245
+ CommandZ.onStorageChange(function(data) {
246
+ console.log(data)
247
+ })
248
+
249
+ CommandZ.store({ width: 100, height: 100 })
250
+ CommandZ.store({ width: 200, height: 200 })
251
+
252
+ CommandZ.undo() // => { width: 100, height: 100 }
253
+ ```
254
+
255
+ ### #clear
170
256
  Clear history.
171
257
 
172
258
  ```js
@@ -187,7 +273,10 @@ console.log(CommandZ.commands.length) // => 0
187
273
  console.log(CommandZ.index) // => -1
188
274
  ```
189
275
 
190
- ### keyboardShortcuts
276
+ ### #reset
277
+ Clear history, remove callbacks and set threshold to 0.
278
+
279
+ ### #keyboardShortcuts
191
280
  Enable or disable `CMD+Z` & `CMD+SHIFT+Z` keyboard shortcuts. These shortcuts are enabled by default.<br>
192
281
  Will only `undo()` & `redo()` if the current selected element is not an input so that it doesn’t prevent your OS default behavior.
193
282
 
@@ -197,6 +286,7 @@ CommandZ.keyboardShortcuts(false)
197
286
  ```
198
287
 
199
288
  ## DOM Example
289
+ ### Commands
200
290
  ```js
201
291
  // This example requires jQuery or Zepto
202
292
  $container = $('<div></div>')
@@ -234,6 +324,35 @@ console.log(CommandZ.commands.length) // => 3
234
324
  console.log(CommandZ.index) // => 2
235
325
  ```
236
326
 
327
+ ### Storage
328
+ ```js
329
+ // This example requires jQuery or Zepto
330
+ $container = $('<div></div>')
331
+
332
+ img = new Image
333
+ $container.html(img)
334
+
335
+ // Register undo/redo callback
336
+ CommandZ.onStorageChange = function(data) {
337
+ img.width = data.width
338
+ img.height = data.height
339
+ }
340
+
341
+ img.width = 100
342
+ img.height = 100
343
+
344
+ // Lets store some states
345
+ [1, 2, 3, 4].forEach(function(i) {
346
+ CommandZ.store({ width: i * 100, height: i * 100 })
347
+ })
348
+
349
+ CommandZ.undo(2)
350
+ console.log(img.width) // => 200
351
+
352
+ CommandZ.redo()
353
+ console.log(img.width) // => 300
354
+ ```
355
+
237
356
  ## Setup
238
357
  ### Rails
239
358
  1. Add `gem 'commandz'` to your Gemfile.
@@ -1,10 +1,10 @@
1
1
  /*
2
- * CommandZ v0.0.3
2
+ * CommandZ v0.1.0
3
3
  * https://github.com/EtienneLem/commandz
4
4
  *
5
5
  * Copyright 2013, Etienne Lemay http://heliom.ca
6
6
  * Released under the MIT license
7
7
  *
8
- * Date: 2013-08-29 16:12:59 -0400
8
+ * Date: 2013-09-02 11:47:04 -0400
9
9
  */
10
- (function(){var t,n=function(t,n){return function(){return t.apply(n,arguments)}};t=function(){function t(){this.handleKeyboard=n(this.handleKeyboard,this),this.VERSION="0.0.3",this.changeCallback=null,this.clear(),this.keyboardShortcuts(!0)}return t.prototype.clear=function(){return this.commands=[],this.index=-1},t.prototype.keyboardShortcuts=function(t){var n;return null==t&&(t=!0),n=t?"addEventListener":"removeEventListener",document[n]("keypress",this.handleKeyboard)},t.prototype.handleKeyboard=function(t){return"INPUT"!==document.activeElement.nodeName&&122===t.keyCode&&t.metaKey===!0?(t.preventDefault(),t.shiftKey?this.redo():this.undo()):void 0},t.prototype.execute=function(t){var n;return this.up(t),this.index<this.commands.length-1&&(n=this.commands.length-this.index-1,this.commands.splice(-n)),this.commands.push(t),this.index=this.commands.length-1,this.handleChange()},t.prototype.undo=function(t){var n,e;if(null==t&&(t=1),this.status().canUndo)for(n=e=1;t>=1?t>=e:e>=t;n=t>=1?++e:--e){if(!this.commands[this.index])return;this.down(this.commands[this.index]),this.index--,this.handleChange()}},t.prototype.redo=function(t){var n,e;if(null==t&&(t=1),this.status().canRedo)for(n=e=1;t>=1?t>=e:e>=t;n=t>=1?++e:--e){if(!this.commands[this.index+1])return;this.index++,this.up(this.commands[this.index]),this.handleChange()}},t.prototype.exec=function(t,n){var e,i,o,s;if(!(n instanceof Array))return n[t]();for(s=[],i=0,o=n.length;o>i;i++)e=n[i],s.push(e[t]());return s},t.prototype.up=function(t){return this.exec("up",t)},t.prototype.down=function(t){return this.exec("down",t)},t.prototype.onChange=function(t){return this.changeCallback=t,this.handleChange()},t.prototype.handleChange=function(){return this.changeCallback?this.changeCallback(this.status()):void 0},t.prototype.status=function(){return{canUndo:this.index>-1,canRedo:this.index<this.commands.length-1}},t}(),this.CommandZ=new t}).call(this);
10
+ (function(){var t,e=function(t,e){return function(){return t.apply(e,arguments)}};t=function(){function t(){this.handleKeypress=e(this.handleKeypress,this),this.VERSION="0.1.0",this.reset(),this.keyboardShortcuts(!0)}return t.prototype.reset=function(){return this.clear(),this.statusChangeCallback=null,this.storageChangeCallback=null,this.thresholdTimer=null,this.threshold=0},t.prototype.clear=function(){return this.history=[],this.index=-1},t.prototype.keyboardShortcuts=function(t){var e;return null==t&&(t=!0),e=t?"addEventListener":"removeEventListener",document[e]("keypress",this.handleKeypress)},t.prototype.handleKeypress=function(t){return"INPUT"!==document.activeElement.nodeName&&122===t.keyCode&&t.metaKey===!0?(t.preventDefault(),t.shiftKey?this.redo():this.undo()):void 0},t.prototype.execute=function(t){var e;return e={},e.command=t,this.up(t),this.addToHistory(e)},t.prototype.store=function(t){var e;return e={},e.data=t,this.addToHistory(e)},t.prototype.addToHistory=function(t){var e;return this.index<this.history.length-1&&(e=this.history.length-this.index-1,this.history.splice(-e)),this.history.push(t),this.index=this.history.length-1,this.handleStatusChange()},t.prototype.undo=function(t){var e,n,s,i,h;if(null==t&&(t=1),this.status().canUndo)for(i=h=1;t>=1?t>=h:h>=t;i=t>=1?++h:--h){if(!this.history[this.index])return;s=this.history[this.index],(e=s.command)&&this.down(e),this.index--,(s=this.history[this.index])&&(n=s.data)&&this.handleData(n),this.handleStatusChange()}},t.prototype.redo=function(t){var e,n,s,i,h;if(null==t&&(t=1),this.status().canRedo)for(i=h=1;t>=1?t>=h:h>=t;i=t>=1?++h:--h){if(!this.history[this.index+1])return;this.index++,s=this.history[this.index],(e=s.command)&&this.up(e),(n=s.data)&&this.handleData(n),this.handleStatusChange()}},t.prototype.exec=function(t,e){var n,s,i,h;if(!(e instanceof Array))return e[t]();for(h=[],s=0,i=e.length;i>s;s++)n=e[s],h.push(n[t]());return h},t.prototype.up=function(t){return this.exec("up",t)},t.prototype.down=function(t){return this.exec("down",t)},t.prototype.handleData=function(t){var e=this;return this.threshold>0?(clearTimeout(this.thresholdTimer),this.thresholdTimer=setTimeout(function(){return e.sendData(t)},this.threshold)):this.sendData(t)},t.prototype.sendData=function(t){return this.storageChangeCallback?this.storageChangeCallback(t):void 0},t.prototype.onStorageChange=function(t){return this.storageChangeCallback=t},t.prototype.setThreshold=function(t){return this.threshold=t},t.prototype.onStatusChange=function(t){return this.statusChangeCallback=t,this.handleStatusChange()},t.prototype.handleStatusChange=function(){return this.statusChangeCallback?this.statusChangeCallback(this.status()):void 0},t.prototype.status=function(){return{canUndo:this.index>-1,canRedo:this.index<this.history.length-1}},t}(),this.CommandZ=new t}).call(this);
@@ -1,63 +1,94 @@
1
1
  class CommandZ
2
2
 
3
3
  constructor: ->
4
- @VERSION = '0.0.3'
5
- @changeCallback = null
4
+ @VERSION = '0.1.0'
6
5
 
7
- this.clear()
6
+ this.reset()
8
7
  this.keyboardShortcuts(true)
9
8
 
9
+ reset: ->
10
+ this.clear()
11
+
12
+ @statusChangeCallback = null
13
+ @storageChangeCallback = null
14
+ @thresholdTimer = null
15
+ @threshold = 0
16
+
10
17
  clear: ->
11
- @commands = []
18
+ @history = []
12
19
  @index = -1
13
20
 
14
21
  keyboardShortcuts: (enable=true) ->
15
22
  addOrRemove = if enable then 'addEventListener' else 'removeEventListener'
16
- document[addOrRemove]('keypress', this.handleKeyboard)
23
+ document[addOrRemove]('keypress', this.handleKeypress)
17
24
 
18
- handleKeyboard: (e) =>
25
+ handleKeypress: (e) =>
19
26
  return if document.activeElement.nodeName is 'INPUT'
20
27
  return unless e.keyCode is 122 and e.metaKey is true
21
28
 
22
29
  e.preventDefault()
23
30
  if e.shiftKey then this.redo() else this.undo()
24
31
 
32
+ # Execute and store commands as { command: {up: ->, down: ->} }
25
33
  execute: (command) ->
34
+ historyItem = {}
35
+ historyItem.command = command
36
+
26
37
  this.up(command)
38
+ this.addToHistory(historyItem)
39
+
40
+ # Store data as { data: … }
41
+ store: (data) ->
42
+ historyItem = {}
43
+ historyItem.data = data
44
+
45
+ this.addToHistory(historyItem)
46
+
47
+ # History management
48
+ addToHistory: (historyItem) ->
49
+ # Overwrite upcoming history items
50
+ if @index < @history.length - 1
51
+ difference = (@history.length - @index) - 1
52
+ @history.splice(-difference)
27
53
 
28
- # Overwrites following commands (if @index < @commands.length)
29
- if (@index < @commands.length - 1)
30
- difference = (@commands.length - @index) - 1
31
- @commands.splice(-difference)
54
+ @history.push(historyItem)
55
+ @index = @history.length - 1
32
56
 
33
- # Push new command
34
- @commands.push(command)
35
- @index = @commands.length - 1
36
- this.handleChange()
57
+ this.handleStatusChange()
37
58
 
38
59
  undo: (times=1) ->
39
60
  return unless this.status().canUndo
40
61
 
41
62
  for i in [1..times]
42
- return unless @commands[@index]
63
+ return unless @history[@index]
43
64
 
44
- this.down(@commands[@index])
65
+ historyItem = @history[@index]
66
+ this.down(command) if command = historyItem.command
67
+
68
+ # Has to be after a command item, but before a data item
45
69
  @index--
46
70
 
47
- this.handleChange()
71
+ if historyItem = @history[@index]
72
+ this.handleData(data) if data = historyItem.data
73
+
74
+ this.handleStatusChange()
48
75
 
49
76
  redo: (times=1) ->
50
77
  return unless this.status().canRedo
51
78
 
52
79
  for i in [1..times]
53
- return unless @commands[@index + 1]
80
+ return unless @history[@index + 1]
54
81
 
82
+ # Has to be before both a command and a data item
55
83
  @index++
56
- this.up(@commands[@index])
57
84
 
58
- this.handleChange()
85
+ historyItem = @history[@index]
86
+ this.up(command) if command = historyItem.command
87
+ this.handleData(data) if data = historyItem.data
59
88
 
60
- # Execute up/down on a command
89
+ this.handleStatusChange()
90
+
91
+ # Execute up/down action on a command
61
92
  # command can be a group of commands or a single command
62
93
  exec: (action, command) ->
63
94
  return command[action]() unless command instanceof Array
@@ -66,19 +97,38 @@ class CommandZ
66
97
  up: (command) -> this.exec('up', command)
67
98
  down: (command) -> this.exec('down', command)
68
99
 
69
- # Register onChange callback
70
- onChange: (callback) ->
71
- @changeCallback = callback
72
- this.handleChange()
100
+ # Send current history item data
101
+ handleData: (data) ->
102
+ return this.sendData(data) unless @threshold > 0
103
+
104
+ clearTimeout(@thresholdTimer)
105
+ @thresholdTimer = setTimeout =>
106
+ this.sendData(data)
107
+ , @threshold
108
+
109
+ sendData: (data) ->
110
+ return unless @storageChangeCallback
111
+ @storageChangeCallback(data)
112
+
113
+ # Storage management
114
+ onStorageChange: (callback) ->
115
+ @storageChangeCallback = callback
116
+
117
+ setThreshold: (threshold) ->
118
+ @threshold = threshold
119
+
120
+ # Status management
121
+ onStatusChange: (callback) ->
122
+ @statusChangeCallback = callback
123
+ this.handleStatusChange()
73
124
 
74
- handleChange: ->
75
- return unless @changeCallback
76
- @changeCallback(this.status())
125
+ handleStatusChange: ->
126
+ return unless @statusChangeCallback
127
+ @statusChangeCallback(this.status())
77
128
 
78
- # Return current status
79
129
  status: ->
80
130
  canUndo: @index > -1
81
- canRedo: @index < @commands.length - 1
131
+ canRedo: @index < @history.length - 1
82
132
 
83
133
  # Singleton
84
134
  @CommandZ = new CommandZ
@@ -1,3 +1,3 @@
1
1
  module CommandZ
2
- VERSION = '0.0.3'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -1,39 +1,39 @@
1
1
  describe 'CommandZ', ->
2
- describe 'unit', ->
2
+ describe 'commands', ->
3
3
  it 'stores commands', ->
4
4
  [0..9].forEach (i) -> CommandZ.execute({up: (-> i), down: (-> i)})
5
5
 
6
- expect(CommandZ.commands.length).toBe(10)
6
+ expect(CommandZ.history.length).toBe(10)
7
7
  expect(CommandZ.index).toBe(9)
8
8
 
9
9
  it 'undo', ->
10
10
  [0..3].forEach -> CommandZ.undo()
11
11
 
12
- expect(CommandZ.commands.length).toBe(10)
12
+ expect(CommandZ.history.length).toBe(10)
13
13
  expect(CommandZ.index).toBe(5)
14
14
 
15
15
  it 'redo', ->
16
16
  CommandZ.redo()
17
17
 
18
- expect(CommandZ.commands.length).toBe(10)
18
+ expect(CommandZ.history.length).toBe(10)
19
19
  expect(CommandZ.index).toBe(6)
20
20
 
21
21
  it 'undo many times', ->
22
22
  CommandZ.undo(3)
23
- expect(CommandZ.commands.length).toBe(10)
23
+ expect(CommandZ.history.length).toBe(10)
24
24
  expect(CommandZ.index).toBe(3)
25
25
 
26
26
  CommandZ.undo(100)
27
- expect(CommandZ.commands.length).toBe(10)
27
+ expect(CommandZ.history.length).toBe(10)
28
28
  expect(CommandZ.index).toBe(-1)
29
29
 
30
30
  it 'redo many times', ->
31
31
  CommandZ.redo(3)
32
- expect(CommandZ.commands.length).toBe(10)
32
+ expect(CommandZ.history.length).toBe(10)
33
33
  expect(CommandZ.index).toBe(2)
34
34
 
35
35
  CommandZ.redo(100)
36
- expect(CommandZ.commands.length).toBe(10)
36
+ expect(CommandZ.history.length).toBe(10)
37
37
  expect(CommandZ.index).toBe(9)
38
38
 
39
39
  it 'returns current status', ->
@@ -46,47 +46,92 @@ describe 'CommandZ', ->
46
46
  CommandZ.undo(3)
47
47
  CommandZ.execute({up: (->), down: (->)})
48
48
 
49
- expect(CommandZ.commands.length).toBe(8)
49
+ expect(CommandZ.history.length).toBe(8)
50
50
  expect(CommandZ.index).toBe(7)
51
51
 
52
52
  it 'clears commands', ->
53
53
  CommandZ.clear()
54
54
 
55
- expect(CommandZ.commands.length).toBe(0)
55
+ expect(CommandZ.history.length).toBe(0)
56
56
  expect(CommandZ.index).toBe(-1)
57
57
 
58
58
  it 'stores grouped commands', ->
59
59
  CommandZ.execute([{up: (->), down: (->)}, {up: (->), down: (->)}])
60
- CommandZ.undo()
61
- CommandZ.redo()
62
60
 
63
- expect(CommandZ.commands.length).toBe(1)
64
- expect(CommandZ.commands[0].length).toBe(2)
61
+ expect(CommandZ.history.length).toBe(1)
62
+ expect(CommandZ.history[0].command.length).toBe(2)
65
63
 
66
64
  expect(CommandZ.index).toBe(0)
67
65
 
68
- it 'registers onChange callback', ->
66
+ it 'registers onStatusChange callback', ->
69
67
  CommandZ.clear()
70
68
  [0..2].forEach (i) -> CommandZ.execute({up: (-> i), down: (-> i)})
71
69
 
72
- onChangeCallback = jasmine.createSpy('onChangeCallback')
73
- CommandZ.onChange (status) -> onChangeCallback('test')
70
+ onStatusChangeCallback = jasmine.createSpy('onStatusChangeCallback')
71
+ CommandZ.onStatusChange (status) -> onStatusChangeCallback('test')
74
72
 
75
73
  CommandZ.undo(3)
76
74
  CommandZ.redo(2)
77
75
  CommandZ.execute({up: (->), down: (->)})
78
76
 
79
- expect(onChangeCallback.calls.length).toBe(7)
80
- CommandZ.onChange(null)
77
+ expect(onStatusChangeCallback.calls.length).toBe(7)
78
+ CommandZ.onStatusChange(null)
79
+
80
+ describe 'storage', ->
81
+ data = null
82
+
83
+ beforeEach ->
84
+ CommandZ.reset()
85
+ [1..3].forEach (i) -> CommandZ.store({ width: i * 100, height: i * 100 })
86
+
87
+ it 'stores data', ->
88
+ expect(CommandZ.history.length).toBe(3)
89
+ expect(CommandZ.index).toBe(2)
90
+
91
+ it 'undo', ->
92
+ CommandZ.onStorageChange (storageData) -> data = storageData
93
+ CommandZ.undo()
94
+
95
+ expect(data).toEqual({ width: 200, height: 200 })
96
+
97
+ it 'redo', ->
98
+ CommandZ.undo()
99
+ CommandZ.onStorageChange (storageData) -> data = storageData
100
+ CommandZ.redo()
101
+
102
+ expect(data).toEqual({ width: 300, height: 300 })
103
+
104
+ it 'undo and redo many times', ->
105
+ spyOn(CommandZ, 'sendData')
106
+
107
+ CommandZ.undo(2)
108
+ CommandZ.redo(2)
109
+ expect(CommandZ.sendData.calls.length).toBe(4)
110
+
111
+ CommandZ.redo(100)
112
+ expect(CommandZ.sendData.calls.length).toBe(4)
113
+
114
+ it 'has a threshold', ->
115
+ spyOn(CommandZ, 'sendData')
116
+
117
+ CommandZ.setThreshold(500)
118
+ CommandZ.undo(100)
119
+
120
+ waits(450)
121
+ runs -> expect(CommandZ.sendData.calls.length).toBe(0)
122
+
123
+ waits(100)
124
+ runs -> expect(CommandZ.sendData.calls.length).toBe(1)
81
125
 
82
126
  describe 'integration', ->
83
127
  $container = null
84
128
 
85
129
  beforeEach ->
86
- CommandZ.clear()
87
- loadFixtures('spec_container.html')
130
+ CommandZ.reset()
88
131
 
132
+ loadFixtures('spec_container.html')
89
133
  $container = $('#spec-container')
134
+
90
135
  [0..4].forEach ->
91
136
  $test = $('<div class="foo"></div>')
92
137
  CommandZ.execute
@@ -132,3 +177,28 @@ describe 'CommandZ', ->
132
177
 
133
178
  CommandZ.undo()
134
179
  expect($container.html()).toBe('')
180
+
181
+ it 'restores states with stored data', ->
182
+ img = new Image
183
+ $container.html(img)
184
+
185
+ CommandZ.onStorageChange (data) ->
186
+ img.width = data.width
187
+ img.height = data.height
188
+
189
+ img.width = 100
190
+ img.height = 100
191
+
192
+ [1..4].forEach (i) -> CommandZ.store({ width: i * 100, height: i * 100 })
193
+
194
+ CommandZ.undo(2)
195
+ expect(img.width).toBe(200)
196
+
197
+ CommandZ.redo()
198
+ expect(img.width).toBe(300)
199
+
200
+ CommandZ.redo(100)
201
+ expect(img.width).toBe(400)
202
+
203
+ CommandZ.undo(100)
204
+ expect(img.width).toBe(100)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: commandz
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Etienne Lemay
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-29 00:00:00.000000000 Z
11
+ date: 2013-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake