commandz 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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('')