commandz 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 702744b575b0c129c8031ffb874531ccabc57686
4
+ data.tar.gz: fcd2c78539f793ce8ca8d2724bcd00a03e234d95
5
+ SHA512:
6
+ metadata.gz: 49f382728b5c447021de95d10e8b7ed1a6a6386592dfd1945fda902933962fd7c99149008a4332331235ff9ab03bb499c726952cd0a9dcc06c0b872b8ba0a596
7
+ data.tar.gz: c349a981e728cb17c0b8fb885e4bec53d545367c9e1def72e37f833d30a112d4a949d40995df85542731a9f55d6887cbf44d8d992d932e35b3087c9a23adfbb8
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ .jhw-cache
@@ -0,0 +1,2 @@
1
+ --colors
2
+ --jasmine-config config/jasmine.yml
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,7 @@
1
+ spec_location = "spec/%s_spec"
2
+
3
+ guard 'jasmine-headless-webkit' do
4
+ watch(%r{^lib/assets/javascripts/(.*)\.coffee$}) { |m| newest_js_file(spec_location % m[1]) }
5
+ watch(%r{^spec/(.*)_spec\..*}) { |m| newest_js_file(spec_location % m[1]) }
6
+ watch(%r{spec/fixtures/.+$})
7
+ end
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 Etienne Lemay
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,200 @@
1
+ <p align="center">
2
+ <a href="https://github.com/EtienneLem/commandz">
3
+ <img src="https://f.cloud.github.com/assets/436043/1047139/032acfc8-1059-11e3-8baa-d22caee7e517.png">
4
+ </a>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <strong>CommandZ</strong> undo and redo commands.<br>
9
+ Add commands history support to your web app.
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="http://badge.fury.io/rb/commandz"><img src="https://badge.fury.io/rb/commandz@2x.png" alt="Gem Version" height="18"></a>
14
+ </p>
15
+
16
+ ## API
17
+ #### Glossary
18
+ ```js
19
+ COMMANDS: An Array of COMMAND
20
+ COMMAND: {
21
+ up: function() {},
22
+ down: function() {}
23
+ }
24
+ ```
25
+
26
+ ### execute
27
+ Receive `COMMAND` or `COMMANDS` and execute `COMMAND.up()`.
28
+
29
+ **Single command per history item**<br>
30
+ Store one history item per `COMMAND`.
31
+
32
+ ```js
33
+ CommandZ.execute({
34
+ up: function() { console.log('up 1') },
35
+ down: function() { console.log('down 1') }
36
+ }) // => up 1
37
+
38
+ CommandZ.execute({
39
+ up: function() { console.log('up 2') },
40
+ down: function() { console.log('down 2') }
41
+ }) // => up 2
42
+
43
+ console.log(CommandZ.commands.length) // => 2
44
+ console.log(CommandZ.index) // => 1
45
+ ```
46
+
47
+ **Multiple commands per history item**<br>
48
+ Store one history item per `COMMAND` group (`COMMANDS`).<br>
49
+ A *single* undo would go back through *all* of the `COMMAND` inside `COMMANDS`.
50
+
51
+ ```js
52
+ commands = []
53
+ for (var i=1; i <= 5; i++) {
54
+ commands.push({
55
+ up: function() { 'up 1.' + i },
56
+ down: function() { 'down 1.' + i }
57
+ })
58
+ }
59
+
60
+ CommandZ.execute(commands) // => up 1.1, up 1.2, up 1.3, up 1.4, up 1.5
61
+
62
+ console.log(CommandZ.commands.length) // => 1
63
+ console.log(CommandZ.index) // => 0
64
+ ```
65
+
66
+ ### undo
67
+ Call `COMMAND.down()` and set the index to the previous command.
68
+
69
+ ```js
70
+ CommandZ.execute({
71
+ up: function() { console.log('up 1') },
72
+ down: function() { console.log('down 1') }
73
+ }) // => up 1
74
+
75
+ CommandZ.execute({
76
+ up: function() { console.log('up 2') },
77
+ down: function() { console.log('down 2') }
78
+ }) // => up 2
79
+
80
+ CommandZ.undo() // => down 2
81
+
82
+ console.log(CommandZ.commands.length) // => 2
83
+ console.log(CommandZ.index) // => 0
84
+
85
+ CommandZ.undo() // => down 1
86
+
87
+ console.log(CommandZ.commands.length) // => 2
88
+ console.log(CommandZ.index) // => -1
89
+ ```
90
+
91
+ ### redo
92
+ Set the index to the next command and call `COMMAND.up()`.
93
+
94
+ ```js
95
+ CommandZ.execute({
96
+ up: function() { console.log('up 1') },
97
+ down: function() { console.log('down 1') }
98
+ }) // => up 1
99
+
100
+ CommandZ.execute({
101
+ up: function() { console.log('up 2') },
102
+ down: function() { console.log('down 2') }
103
+ }) // => up 2
104
+
105
+ CommandZ.undo(2) // => down 2, down 1
106
+ CommandZ.redo() // => up 1
107
+
108
+ console.log(CommandZ.commands.length) // => 2
109
+ console.log(CommandZ.index) // => 0
110
+ ```
111
+
112
+ ### status
113
+ Return the current `COMMAND`.
114
+
115
+ ```js
116
+ CommandZ.execute({
117
+ up: function() { console.log('up 1') },
118
+ down: function() { console.log('down 1') }
119
+ }) // => up 1
120
+
121
+ CommandZ.execute({
122
+ up: function() { console.log('up 2') },
123
+ down: function() { console.log('down 2') }
124
+ }) // => up 2
125
+
126
+ console.log(CommandZ.status()) // => { up: function() { console.log('up 2') }, down: function() { console.log('down 2') } }
127
+ console.log(CommandZ.commands.length) // => 2
128
+ console.log(CommandZ.index) // => 1
129
+ ```
130
+
131
+ ### clear
132
+ Clear history.
133
+
134
+ ```js
135
+ CommandZ.execute({
136
+ up: function() { console.log('up 1') },
137
+ down: function() { console.log('down 1') }
138
+ }) // => up 1
139
+
140
+ CommandZ.execute({
141
+ up: function() { console.log('up 2') },
142
+ down: function() { console.log('down 2') }
143
+ }) // => up 2
144
+
145
+ CommandZ.clear()
146
+
147
+ console.log(CommandZ.status()) // => undefined
148
+ console.log(CommandZ.commands.length) // => 0
149
+ console.log(CommandZ.index) // => -1
150
+ ```
151
+
152
+ ## DOM Example
153
+ ```js
154
+ // This example requires jQuery or Zepto
155
+ $container = $('<div></div>')
156
+
157
+ // Lets do 5 commands
158
+ [1, 2, 3, 4, 5].forEach(function(i) {
159
+ var $i = $('<i>' + i + '</i>')
160
+ CommandZ.execute({
161
+ up: function() { $container.append($i) },
162
+ down: function() { $i.detach() } // When removing DOM elements, I highly recommend .detach()
163
+ })
164
+ })
165
+
166
+ console.log($container.html()) // => <i>1</i><i>2</i><i>3</i><i>4</i><i>5</i>
167
+
168
+ // Undo
169
+ CommandZ.undo()
170
+ console.log($container.html()) // => <i>1</i><i>2</i><i>3</i><i>4</i>
171
+
172
+ // Redo
173
+ CommandZ.redo()
174
+ console.log($container.html()) // => <i>1</i><i>2</i><i>3</i><i>4</i><i>5</i>
175
+
176
+ // When undoing, a new command will overwrite all upcoming commands
177
+ CommandZ.undo(3)
178
+
179
+ $i = $('<i>1337</i>')
180
+ CommandZ.execute({
181
+ up: function() { $container.append($i) },
182
+ down: function() { $i.detach() }
183
+ })
184
+
185
+ console.log($container.html()) // => <i>1</i><i>2</i><i>1337</i>
186
+ console.log(CommandZ.commands.length) // => 3
187
+ console.log(CommandZ.index) // => 2
188
+ ```
189
+
190
+ ## Setup
191
+ ### Rails
192
+ 1. Add `gem 'commandz'` to your Gemfile.
193
+ 2. Add `//= require commandz` to your JavaScript manifest file.
194
+ 3. Restart your server and `CMD+Z` - `CMD+SHIFT+Z` away!
195
+
196
+ ### Other
197
+ Download and include [commandz.min.js](https://raw.github.com/EtienneLem/commandz/master/commandz.min.js) in your HTML pages.
198
+
199
+ ## Tests
200
+ Run the `rake spec` task or `bundle exec guard` for continuous testing.
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), *%w[lib])
2
+ require 'tasks/task_helpers'
3
+
4
+ # Rubygems
5
+ require 'bundler'
6
+ Bundler::GemHelper.install_tasks
7
+
8
+ # Dependencies
9
+ require 'uglifier'
10
+ require 'commandz'
11
+ require 'sprockets'
12
+
13
+ # Tasks
14
+ desc 'Merge, compiles and minify CoffeeScript files'
15
+ task :compile do
16
+ @environment = Sprockets::Environment.new
17
+ @environment.append_path 'lib/assets/javascripts'
18
+ @environment.js_compressor = Uglifier.new(mangle: true)
19
+
20
+ compile('commandz.js')
21
+ end
22
+
23
+ desc 'run Jasmine specs'
24
+ task :spec do
25
+ system('bundle exec jasmine-headless-webkit')
26
+ end
27
+
28
+ # Helpers
29
+ def compile(file)
30
+ minjs = @environment[file].to_s
31
+ out = "#{file.sub('.js', '.min.js')}"
32
+
33
+ File.open(out, 'w') { |f| f.write(copyright + minjs + "\n") }
34
+ success "Compiled #{out}"
35
+ end
36
+
37
+ def copyright
38
+ @copyright ||= <<-EOS
39
+ /*
40
+ * CommandZ v#{CommandZ::VERSION}
41
+ * https://github.com/EtienneLem/commandz
42
+ *
43
+ * Copyright 2013, Etienne Lemay http://heliom.ca
44
+ * Released under the MIT license
45
+ *
46
+ * Date: #{Time.now}
47
+ */
48
+ EOS
49
+ end
data/commandz.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ require './lib/commandz/version'
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'commandz'
5
+ s.version = CommandZ::VERSION
6
+ s.authors = ['Etienne Lemay']
7
+ s.email = ['etienne@heliom.ca']
8
+ s.homepage = 'https://github.com/EtienneLem/commandz'
9
+ s.summary = 'CommandZ undo and redo commands'
10
+ s.license = 'MIT'
11
+
12
+ s.files = `git ls-files`.split($/)
13
+ s.test_files = s.files.grep(%r{^(spec)/})
14
+
15
+ s.add_development_dependency 'rake'
16
+ s.add_development_dependency 'jasmine'
17
+ s.add_development_dependency 'uglifier'
18
+ s.add_development_dependency 'sprockets'
19
+ s.add_development_dependency 'jasmine-headless-webkit'
20
+ s.add_development_dependency 'guard-jasmine-headless-webkit'
21
+ end
data/commandz.min.js ADDED
@@ -0,0 +1,10 @@
1
+ /*
2
+ * CommandZ v0.0.1
3
+ * https://github.com/EtienneLem/commandz
4
+ *
5
+ * Copyright 2013, Etienne Lemay http://heliom.ca
6
+ * Released under the MIT license
7
+ *
8
+ * Date: 2013-08-28 23:28:40 -0400
9
+ */
10
+ (function(){var t;t=function(){function t(){this.VERSION="0.0.1",this.clear()}return t.prototype.clear=function(){return this.commands=[],this.index=-1},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},t.prototype.undo=function(t){var n,i;if(null==t&&(t=1),!(this.index<0))for(n=i=1;t>=1?t>=i:i>=t;n=t>=1?++i:--i){if(!this.commands[this.index])return;this.down(this.commands[this.index]),this.index--}},t.prototype.redo=function(t){var n,i;if(null==t&&(t=1),!(this.index>=this.commands.length-1))for(n=i=1;t>=1?t>=i:i>=t;n=t>=1?++i:--i){if(!this.commands[this.index+1])return;this.index++,this.up(this.commands[this.index])}},t.prototype.exec=function(t,n){var i,s,e,o;if(!(n instanceof Array))return n[t]();for(o=[],s=0,e=n.length;e>s;s++)i=n[s],o.push(i[t]());return o},t.prototype.up=function(t){return this.exec("up",t)},t.prototype.down=function(t){return this.exec("down",t)},t.prototype.status=function(){return this.commands[this.index]},t}(),this.CommandZ=new t}).call(this);
@@ -0,0 +1,10 @@
1
+ src_files:
2
+ - "**/*"
3
+ helpers:
4
+ - helpers/**/*
5
+ spec_files:
6
+ - "**/*_spec.*"
7
+ src_dir:
8
+ - lib
9
+ - vendor
10
+ spec_dir: spec
@@ -0,0 +1,50 @@
1
+ class CommandZ
2
+
3
+ constructor: ->
4
+ @VERSION = '0.0.1'
5
+ this.clear()
6
+
7
+ clear: ->
8
+ @commands = []
9
+ @index = -1
10
+
11
+ execute: (command) ->
12
+ this.up(command)
13
+
14
+ # Overwrites following commands (if @index < @commands.length)
15
+ if (@index < @commands.length - 1)
16
+ difference = (@commands.length - @index) - 1
17
+ @commands.splice(-difference)
18
+
19
+ # Push new command
20
+ @commands.push(command)
21
+ @index = @commands.length - 1
22
+
23
+ undo: (times=1) ->
24
+ return if @index < 0
25
+ for i in [1..times]
26
+ return unless @commands[@index]
27
+ this.down(@commands[@index])
28
+ @index--
29
+
30
+ redo: (times=1) ->
31
+ return if @index >= @commands.length - 1
32
+ for i in [1..times]
33
+ return unless @commands[@index + 1]
34
+ @index++
35
+ this.up(@commands[@index])
36
+
37
+ # Execute up/down on a command
38
+ # command can be a group of commands or a single command
39
+ exec: (action, command) ->
40
+ return command[action]() unless command instanceof Array
41
+ c[action]() for c in command
42
+
43
+ up: (command) -> this.exec('up', command)
44
+ down: (command) -> this.exec('down', command)
45
+
46
+ # Return current command
47
+ status: -> @commands[@index]
48
+
49
+ # Singleton
50
+ @CommandZ = new CommandZ
data/lib/commandz.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'commandz/version'
2
+ require 'commandz/engine' if defined?(Rails) && Rails::VERSION::MAJOR >= 3
@@ -0,0 +1,3 @@
1
+ module CommandZ
2
+ class Engine < ::Rails::Engine; end
3
+ end
@@ -0,0 +1,3 @@
1
+ module CommandZ
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,18 @@
1
+ def colorize(text, color_code)
2
+ "\e[#{color_code}m#{text}\e[0m"
3
+ end
4
+
5
+ { # See: http://kpumuk.info/ruby-on-rails/colorizing-console-ruby-script-output/
6
+ 31 => 'red',
7
+ 32 => 'green',
8
+ 33 => 'yellow',
9
+ 34 => 'blue',
10
+ 35 => 'magenta',
11
+ 36 => 'cyan',
12
+ }.each do |code, color|
13
+ Kernel.send(:define_method, color) { |text| colorize(text, code) }
14
+ end
15
+
16
+ def error(text); puts red('ERROR: ') + text; end
17
+ def success(text); puts green('SUCCESS: ') + text; end
18
+ def warn(text); puts yellow('WARNING: ') + text; end
@@ -0,0 +1,118 @@
1
+ describe 'CommandZ', ->
2
+ describe 'unit', ->
3
+ it 'stores commands', ->
4
+ [0..9].forEach (i) -> CommandZ.execute({up: (-> i), down: (-> i)})
5
+
6
+ expect(CommandZ.commands.length).toBe(10)
7
+ expect(CommandZ.index).toBe(9)
8
+
9
+ it 'undo', ->
10
+ [0..3].forEach -> CommandZ.undo()
11
+
12
+ expect(CommandZ.commands.length).toBe(10)
13
+ expect(CommandZ.index).toBe(5)
14
+
15
+ it 'redo', ->
16
+ CommandZ.redo()
17
+
18
+ expect(CommandZ.commands.length).toBe(10)
19
+ expect(CommandZ.index).toBe(6)
20
+
21
+ it 'undo many times', ->
22
+ CommandZ.undo(3)
23
+ expect(CommandZ.commands.length).toBe(10)
24
+ expect(CommandZ.index).toBe(3)
25
+
26
+ CommandZ.undo(100)
27
+ expect(CommandZ.commands.length).toBe(10)
28
+ expect(CommandZ.index).toBe(-1)
29
+
30
+ it 'redo many times', ->
31
+ CommandZ.redo(3)
32
+ expect(CommandZ.commands.length).toBe(10)
33
+ expect(CommandZ.index).toBe(2)
34
+
35
+ CommandZ.redo(100)
36
+ expect(CommandZ.commands.length).toBe(10)
37
+ expect(CommandZ.index).toBe(9)
38
+
39
+ it 'returns current command', ->
40
+ currentCommand = CommandZ.status()
41
+ expect(currentCommand.up()).toBe(9)
42
+
43
+ it 'overwrites upcoming commands', ->
44
+ CommandZ.undo(3)
45
+ CommandZ.execute({up: (->), down: (->)})
46
+
47
+ expect(CommandZ.commands.length).toBe(8)
48
+ expect(CommandZ.index).toBe(7)
49
+
50
+ it 'clears commands', ->
51
+ CommandZ.clear()
52
+
53
+ expect(CommandZ.commands.length).toBe(0)
54
+ expect(CommandZ.index).toBe(-1)
55
+
56
+ it 'stores grouped commands', ->
57
+ CommandZ.execute([{up: (->), down: (->)}, {up: (->), down: (->)}])
58
+ CommandZ.undo()
59
+ CommandZ.redo()
60
+
61
+ expect(CommandZ.commands.length).toBe(1)
62
+ expect(CommandZ.commands[0].length).toBe(2)
63
+
64
+ expect(CommandZ.index).toBe(0)
65
+
66
+ describe 'integration', ->
67
+ $container = null
68
+
69
+ beforeEach ->
70
+ CommandZ.clear()
71
+ loadFixtures('spec_container.html')
72
+
73
+ $container = $('#spec-container')
74
+ [0..4].forEach ->
75
+ $test = $('<div class="foo"></div>')
76
+ CommandZ.execute
77
+ up: -> $container.append($test)
78
+ down: -> $test.remove()
79
+
80
+ it 'executes commands', ->
81
+ expect($container.children().length).toBe(5)
82
+
83
+ it 'undo', ->
84
+ CommandZ.undo(3)
85
+ expect($container.children().length).toBe(2)
86
+
87
+ it 'redo', ->
88
+ CommandZ.undo(3)
89
+ CommandZ.redo()
90
+
91
+ expect($container.children().length).toBe(3)
92
+
93
+ it 'overwrites upcoming commands', ->
94
+ CommandZ.undo(10)
95
+ CommandZ.redo()
96
+
97
+ $bar = $('<div class="bar"></div>')
98
+ CommandZ.execute
99
+ up: -> $container.append($bar)
100
+ down: -> $bar.remove()
101
+
102
+ expect($container.html()).toBe('<div class="foo"></div><div class="bar"></div>')
103
+
104
+ it 'executes grouped commands', ->
105
+ $container.html('')
106
+ commands = []
107
+
108
+ [1..3].forEach (i) ->
109
+ $i = $("<i>#{i}</i>")
110
+ commands.push
111
+ up: -> $container.append($i)
112
+ down: -> $i.remove()
113
+
114
+ CommandZ.execute(commands)
115
+ expect($container.html()).toBe('<i>1</i><i>2</i><i>3</i>')
116
+
117
+ CommandZ.undo()
118
+ expect($container.html()).toBe('')