style_train 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,5 @@
1
+ # todo: when needed
2
+ module StyleTrain
3
+ class HSLcolor < ColorType
4
+ end
5
+ end