style_train 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LISENCE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +27 -0
- data/VERSION +1 -0
- data/lib/style_train/color.rb +103 -0
- data/lib/style_train/color_types/color_type.rb +218 -0
- data/lib/style_train/color_types/hex_color.rb +44 -0
- data/lib/style_train/color_types/hsl_color.rb +5 -0
- data/lib/style_train/color_types/keyword_color.rb +192 -0
- data/lib/style_train/color_types/rgb_color.rb +57 -0
- data/lib/style_train/sheet.rb +234 -0
- data/lib/style_train/support/gnash.rb +143 -0
- data/lib/style_train/support/numbers.rb +23 -0
- data/lib/style_train/support/string.rb +24 -0
- data/lib/style_train.rb +22 -0
- data/spec/color/color_spec.rb +224 -0
- data/spec/color/color_type_spec.rb +370 -0
- data/spec/color/hex_color_spec.rb +160 -0
- data/spec/color/keyword_color_spec.rb +56 -0
- data/spec/color/rgb_color_spec.rb +75 -0
- data/spec/numbers_spec.rb +25 -0
- data/spec/sheet_spec.rb +549 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +11 -0
- data/style_train.gemspec +74 -0
- data/utils/alexch_color_gist/color.rb +160 -0
- data/utils/alexch_color_gist/color_test.rb +176 -0
- data/utils/overview.txt +151 -0
- data/utils/stylesheet.txt +161 -0
- metadata +102 -0
data/LISENCE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Kane Baccigalupi
|
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.rdoc
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
= StyleTrain
|
2
|
+
|
3
|
+
StyleTrain is a css color helper that aims to make it really easy to mix and use colors in CSS.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
13
|
+
* Send me a pull request. Bonus points for topic branches.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2010 Kane Baccigalupi. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
require 'rspec'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
|
7
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
8
|
+
t.pattern = 'spec/**/*_spec.rb'
|
9
|
+
end
|
10
|
+
|
11
|
+
task :default => :spec
|
12
|
+
|
13
|
+
begin
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
gem.name = "style_train"
|
17
|
+
gem.summary = %Q{style_train builds CSS with Ruby}
|
18
|
+
gem.description = %Q{style_train builds CSS using pure Ruby, not a DSL interpreted via Ruby. This allows inheritance, modules, instance level calculations and all the goodness Ruby can offer.}
|
19
|
+
gem.email = "baccigalupi@gmail.com"
|
20
|
+
gem.homepage = "http://github.com/baccigalupi/style_train"
|
21
|
+
gem.authors = ["Kane Baccigalupi"]
|
22
|
+
end
|
23
|
+
Jeweler::GemcutterTasks.new
|
24
|
+
rescue LoadError
|
25
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
26
|
+
end
|
27
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# This is a delegatory class that passes most everything to the particular ColorType descendant
|
2
|
+
module StyleTrain
|
3
|
+
class Color
|
4
|
+
attr_accessor :delegate, :background_set
|
5
|
+
|
6
|
+
# Constructor
|
7
|
+
def initialize( color, opts={} )
|
8
|
+
opts = Gnash.new(opts).merge(:color => color)
|
9
|
+
background_opts = opts.delete(:background)
|
10
|
+
self.background = background_opts if background_opts
|
11
|
+
if color.is_a?(Color)
|
12
|
+
self.delegate = color.delegate.dup
|
13
|
+
self.alpha = opts[:alpha] if opts[:alpha]
|
14
|
+
self.background = color.background if color.background_set
|
15
|
+
else
|
16
|
+
color_types.each do |klass|
|
17
|
+
if instance = klass.make(opts)
|
18
|
+
self.delegate = instance
|
19
|
+
break
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.color_types
|
26
|
+
@color_type ||= ColorType.color_types
|
27
|
+
end
|
28
|
+
|
29
|
+
def color_types
|
30
|
+
self.class.color_types
|
31
|
+
end
|
32
|
+
|
33
|
+
# delegated methods
|
34
|
+
[:alpha, :r, :g, :b].each do |meth|
|
35
|
+
class_eval "
|
36
|
+
def #{meth}
|
37
|
+
self.delegate.#{meth}
|
38
|
+
end
|
39
|
+
"
|
40
|
+
end
|
41
|
+
|
42
|
+
def alpha=( value )
|
43
|
+
self.delegate.alpha = value
|
44
|
+
end
|
45
|
+
|
46
|
+
def to(key)
|
47
|
+
self.delegate.to(key)
|
48
|
+
end
|
49
|
+
|
50
|
+
def =~( color )
|
51
|
+
if color.is_a?(Color)
|
52
|
+
self.delegate =~ color.delegate
|
53
|
+
elsif color.is_a?( ColorType )
|
54
|
+
self.delegate =~ color
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def ==( color )
|
59
|
+
color.is_a?( Color ) && self =~ color && self.background == color.background
|
60
|
+
end
|
61
|
+
|
62
|
+
# rendering
|
63
|
+
def background
|
64
|
+
@background ||= Color.new(:white).to(:hex)
|
65
|
+
end
|
66
|
+
|
67
|
+
def background=( opts )
|
68
|
+
color = opts.is_a?(Hash) && (opts[:color] || opts['color'])
|
69
|
+
|
70
|
+
@background = if color
|
71
|
+
opts.delete(:alpha)
|
72
|
+
Color.new(color, opts)
|
73
|
+
elsif opts.is_a? Color
|
74
|
+
color = opts.dup # so that the original color's alpha isn't affected
|
75
|
+
color.alpha = 1.0
|
76
|
+
color
|
77
|
+
else
|
78
|
+
Color.new(opts)
|
79
|
+
end
|
80
|
+
raise ArgumentError, "Background with color: #{color} and options: #{opts} not found" unless @background
|
81
|
+
raise ArgumentError, "Background color #{@background.inspect} has no delegate color" unless @background.delegate
|
82
|
+
|
83
|
+
self.background_set = true
|
84
|
+
@background
|
85
|
+
end
|
86
|
+
|
87
|
+
def render(render_style=nil)
|
88
|
+
if render_style && render_style.to_sym == :ie
|
89
|
+
self.background.to(:hex).layer(self.delegate).render
|
90
|
+
else
|
91
|
+
self.delegate.render
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def inspect
|
96
|
+
"<Color @delegate=#{self.delegate.to_s} @background=#{self.background}>"
|
97
|
+
end
|
98
|
+
|
99
|
+
def to_s(render_style=nil)
|
100
|
+
render(render_style)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
module StyleTrain
|
2
|
+
class ColorType
|
3
|
+
# ERROR CLASSES
|
4
|
+
class PercentageError < ArgumentError
|
5
|
+
def message
|
6
|
+
@message ||= 'Percentages must be between 0 and 100'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class ByteNumberError < ArgumentError
|
11
|
+
def message
|
12
|
+
@message ||= 'Byte numbers must be between 0 and 255'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class AlphaError < ArgumentError
|
17
|
+
def message
|
18
|
+
@message ||= 'Alpha must be between 0.0 and 1.0'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# ATTRIBUTES
|
23
|
+
attr_accessor :r, :g, :b
|
24
|
+
attr_reader :alpha
|
25
|
+
|
26
|
+
# INITIALIZATION
|
27
|
+
def initialize( opts )
|
28
|
+
if self.class.is_color?(opts)
|
29
|
+
build_from_color(opts)
|
30
|
+
else
|
31
|
+
opts = Gnash.new(opts)
|
32
|
+
color = opts[:color]
|
33
|
+
self.alpha = opts[:alpha]
|
34
|
+
type_initialize( color, opts )
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def type_initialize( color, opts )
|
39
|
+
raise NotYetImplemented, "type_initialize must be implemented for #{self}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def alpha=( value )
|
43
|
+
value = (value || 1.0).to_f
|
44
|
+
raise AlphaError if value > 1.0 || value < 0.0
|
45
|
+
@alpha = value
|
46
|
+
end
|
47
|
+
|
48
|
+
def build_from_color( color )
|
49
|
+
self.r = color.r
|
50
|
+
self.g = color.g
|
51
|
+
self.b = color.b
|
52
|
+
self.alpha = color.alpha
|
53
|
+
build
|
54
|
+
end
|
55
|
+
|
56
|
+
def build
|
57
|
+
raise NotImplementedError, "#build must be implemented for #{self.class}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.is_color?( color )
|
61
|
+
color.is_a? ColorType
|
62
|
+
end
|
63
|
+
|
64
|
+
# INSTANCE METHODS
|
65
|
+
# conversion
|
66
|
+
def to( color_type )
|
67
|
+
klass = self.class.color_type_class(color_type)
|
68
|
+
raise ArgumentError, 'color type not supported' unless klass
|
69
|
+
self.class == klass ? self : klass.new( self )
|
70
|
+
end
|
71
|
+
|
72
|
+
# comparison
|
73
|
+
def =~( color )
|
74
|
+
if color.is_a?( ColorType )
|
75
|
+
self.r == color.r && self.g == color.g && self.b == color.b
|
76
|
+
elsif color.is_a?( Color )
|
77
|
+
self =~ color.delegate
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def ==( color )
|
82
|
+
(self =~ color) && self.alpha == color.alpha
|
83
|
+
end
|
84
|
+
|
85
|
+
def ===( color )
|
86
|
+
(self == color) && self.class == color.class
|
87
|
+
end
|
88
|
+
|
89
|
+
# blending
|
90
|
+
def mix( color )
|
91
|
+
mixed = self.dup
|
92
|
+
mixed.r = self.class.average(self.r, color.r)
|
93
|
+
mixed.g = self.class.average(self.g, color.g)
|
94
|
+
mixed.b = self.class.average(self.b, color.b)
|
95
|
+
mixed.alpha = self.class.average(self.alpha, color.alpha, false)
|
96
|
+
mixed.build
|
97
|
+
mixed
|
98
|
+
end
|
99
|
+
|
100
|
+
def layer( color )
|
101
|
+
layered = self.dup
|
102
|
+
ratio = 1 - color.alpha
|
103
|
+
layered.r = self.class.blend( self.r, color.r, ratio )
|
104
|
+
layered.g = self.class.blend( self.g, color.g, ratio )
|
105
|
+
layered.b = self.class.blend( self.b, color.b, ratio )
|
106
|
+
layered.alpha = self.class.blend_alphas( self.alpha, color.alpha )
|
107
|
+
layered.build
|
108
|
+
layered
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.blend(first, second, ratio)
|
112
|
+
blended = (first*ratio + second*(1-ratio)).round
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.average(first, second, round=true)
|
116
|
+
averaged = ((first + second)/2.0)
|
117
|
+
round ? averaged.round : averaged
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.blend_alphas(first, second)
|
121
|
+
base, blender = first > second ? [first, second] : [second, first]
|
122
|
+
difference = 1.0 - base
|
123
|
+
base + difference * blender
|
124
|
+
end
|
125
|
+
|
126
|
+
# rendering
|
127
|
+
def render(opts=nil)
|
128
|
+
alpha? ? render_as_rgba : render_as_given
|
129
|
+
end
|
130
|
+
|
131
|
+
def to_s(opts=nil)
|
132
|
+
render(opts)
|
133
|
+
end
|
134
|
+
|
135
|
+
def render_as_given
|
136
|
+
raise NotImplementedError, "#render_as_given must be implemented for #{self.class}"
|
137
|
+
end
|
138
|
+
|
139
|
+
def render_as_rgba
|
140
|
+
"rgba( #{self.r}, #{self.g}, #{self.b}, #{self.alpha} )"
|
141
|
+
end
|
142
|
+
|
143
|
+
def alpha?
|
144
|
+
self.alpha != 1.0
|
145
|
+
end
|
146
|
+
|
147
|
+
# CLASS METHODS
|
148
|
+
|
149
|
+
def self.make(opts)
|
150
|
+
begin
|
151
|
+
new(opts)
|
152
|
+
rescue
|
153
|
+
nil
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# These can't contain real classes because it will create a dependency loop.
|
158
|
+
def self.color_opts
|
159
|
+
@color_opts ||= Gnash.new(
|
160
|
+
:rgb => 'StyleTrain::RGBcolor',
|
161
|
+
:keyword => 'StyleTrain::KeywordColor',
|
162
|
+
:hex => 'StyleTrain::HexColor'
|
163
|
+
# :hsl => 'HSLcolor' # when this class exists
|
164
|
+
)
|
165
|
+
end
|
166
|
+
|
167
|
+
# So they are replaced on usage with the actual constants, as needed
|
168
|
+
def self.color_type_class( color_type )
|
169
|
+
klass = color_opts[ color_type ]
|
170
|
+
if klass.class == String
|
171
|
+
color_opts[color_type] = klass.constantize
|
172
|
+
else
|
173
|
+
klass
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def self.color_types
|
178
|
+
unless @types_built
|
179
|
+
color_opts.each do |key, klass|
|
180
|
+
self.color_type_class( key )
|
181
|
+
end
|
182
|
+
@types_built = true
|
183
|
+
end
|
184
|
+
color_opts.values
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.percentage( str )
|
188
|
+
if str.class == String && str.match(/^([\d.]*)%$/)
|
189
|
+
number = $1.to_f
|
190
|
+
raise PercentageError if number > 100.0 || number < 0.0
|
191
|
+
number
|
192
|
+
else
|
193
|
+
false
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.byte( str )
|
198
|
+
str.to_i if valid_byte?( str, true )
|
199
|
+
end
|
200
|
+
|
201
|
+
def self.valid_byte?( number, raise_exe=false )
|
202
|
+
num = number.to_i
|
203
|
+
v = num <= 255 && num >= 0
|
204
|
+
raise ByteNumberError if v != true && raise_exe
|
205
|
+
v
|
206
|
+
end
|
207
|
+
|
208
|
+
def self.percent_to_byte( number )
|
209
|
+
raise PercentageError if number > 100.0 || number < 0.0
|
210
|
+
( number * 2.55 ).round
|
211
|
+
end
|
212
|
+
|
213
|
+
def self.byte_to_percentage( number )
|
214
|
+
raise ByteNumberError if number > 255.0 || number < 0.0
|
215
|
+
( number/2.55 ).round
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module StyleTrain
|
2
|
+
class HexColor < ColorType
|
3
|
+
class HexError < ArgumentError
|
4
|
+
def message
|
5
|
+
@message ||= 'Hexidecimal colors should be 3 or 6 hexidecimal digits and can be preceded by a # sign'
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :hex, :hex_6
|
10
|
+
|
11
|
+
def type_initialize( color, opts )
|
12
|
+
hex = color.to_s
|
13
|
+
raise HexError unless hex.match(/^#?([a-f0-9]{3})$/i) || hex.match(/^#?([a-f0-9]{6})$/i)
|
14
|
+
self.hex = "#{$1}"
|
15
|
+
self.hex_6 = self.class.expand( self.hex )
|
16
|
+
self.r = (self.hex_6 / 0x10000) & 0xff
|
17
|
+
self.g = (self.hex_6 / 0x100) & 0xff
|
18
|
+
self.b = (self.hex_6) & 0xff
|
19
|
+
end
|
20
|
+
|
21
|
+
def build
|
22
|
+
self.hex = "%.2x" % self.r + "%.2x" % self.g + "%.2x" % self.b
|
23
|
+
self.hex_6 = ('0x' + self.hex).to_i(16)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.expand( hex )
|
27
|
+
expanded = if (hex.size == 3)
|
28
|
+
str = ''
|
29
|
+
(0..2).each do |index|
|
30
|
+
char = hex[index..index]
|
31
|
+
str << char*2
|
32
|
+
end
|
33
|
+
str
|
34
|
+
else
|
35
|
+
hex
|
36
|
+
end
|
37
|
+
"0x#{expanded}".to_i(16)
|
38
|
+
end
|
39
|
+
|
40
|
+
def render_as_given
|
41
|
+
"##{self.hex}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|