style_train 0.1.0
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.
- 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
|