opal/browser/css/unit.rb
module Browser; module CSS
class Unit
COMPATIBLE = %i[in pt mm cm px pc]
attr_reader :type
def initialize(number, type)
@number = number
@type = type
end
def coerce(other)
return self, other
end
def ==(other)
@number == convert(other, @type)
end
def ===(other)
@type == other.type && @number == other.to_f
end
alias eql? ==
def hash
[@number, @type].hash
end
%i[em ex ch rem vh vw vmin vmax px mm cm in pt pc].each {|name|
define_method name do
Unit.new(convert(self, name), name)
end
}
def +(other)
return Unit.new(@number + other, @type) unless Unit === other
if @type == other.type
Unit.new(@number + other.to_f, @type)
elsif compatible?(self) and compatible?(other)
Unit.new(@number + convert(other, @type), @type)
else
raise ArgumentError, "#{other.type} isn't compatible with #{@type}"
end
end
def -(other)
return Unit.new(@number - other, @type) unless Unit === other
if @type == other.type
Unit.new(@number - other.to_f, @type)
elsif compatible?(self) and compatible?(other)
Unit.new(@number - convert(other, @type), @type)
else
raise ArgumentError, "#{other.type} isn't compatible with #{@type}"
end
end
def *(other)
return Unit.new(@number * other, @type) unless Unit === other
if @type == other.type
Unit.new(@number * other.to_f, @type)
elsif compatible?(self) and compatible?(other)
Unit.new(@number * convert(other, @type), @type)
else
raise ArgumentError, "#{other.type} isn't compatible with #{@type}"
end
end
def /(other)
return Unit.new(@number / other, @type) unless Unit === other
if @type == other.type
Unit.new(@number / other.to_f, @type)
elsif compatible?(self) and compatible?(other)
Unit.new(@number / convert(other, @type), @type)
else
raise ArgumentError, "#{other.type} isn't compatible with #{@type}"
end
end
def -@
Unit.new(@number * -1, @type)
end
def +@
Unit.new(@number, @type)
end
def to_i
@number.to_i
end
def to_f
@number.to_f
end
def to_u
self
end
def to_s
"#{@number}#{@type}"
end
alias to_str to_s
alias inspect to_s
private
def compatible?(unit)
COMPATIBLE.include?(unit.type)
end
def convert(unit, type)
value = unit.to_f
return value if unit.type == type
px = case unit.type
when :in then value * 96
when :pt then value * 4.0 / 3.0
when :pc then value / 12 * 4.0 / 3.0
when :mm then value * 3.77953
when :cm then value * 10 * 3.77953
when :px then value
end
case type
when :in then px / 96.0
when :pt then px / 4.0 / 3.0
when :pc then px * 12 / 4.0 / 3.0
when :mm then px / 3.77953
when :cm then px / 10 / 3.77953
when :px then px
end
end
end
end; end
class Numeric
Unit = Browser::CSS::Unit
%i[em ex ch rem vh vw vmin vmax px mm cm in pt pc].each {|name|
define_method name do
Unit.new(self, name)
end
}
alias old_percent %
def %(other = nil)
if other
old_percent(other)
else
Unit.new(self, :%)
end
end
def to_u
self
end
end
class String
def to_u
if matches = self.match(/^([\d+.]+)(.+)?$/)
value = matches[1].to_f
if unit = matches[2]
value.__send__(unit.downcase)
else
value
end
else
0
end
end
end
class NilClass
def to_u
0
end
end