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 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