Class | Sass::Script::Number |
In: |
lib/sass/script/number.rb
|
Parent: | Literal |
A SassScript object representing a number. SassScript numbers can have decimal values, and can also have units. For example, `12`, `1px`, and `10.45em` are all valid values.
Numbers can also have more complex units, such as `1px*em/in`. These cannot be inputted directly in Sass code at the moment.
PRECISION | = | 1000.0 | The precision with which numbers will be printed to CSS files. For example, if this is `1000.0`, `3.1415926` will be printed as `3.142`. | |
CONVERTABLE_UNITS | = | {"in" => 0, "cm" => 1, "pc" => 2, "mm" => 3, "pt" => 4} | A hash of unit names to their index in the conversion table @private | |
CONVERSION_TABLE | = | [[ 1, 2.54, 6, 25.4, 72 ], # in [ nil, 1, 2.36220473, 10, 28.3464567], # cm [ nil, nil, 1, 4.23333333, 12 ], # pc [ nil, nil, nil, 1, 2.83464567], # mm [ nil, nil, nil, nil, 1 ]] | @private |
denominator_units | [R] | A list of units in the denominator of the number. For example, `1px*em/in*cm` would return `["in", "cm"]` @return [Array<String>] |
numerator_units | [R] | A list of units in the numerator of the number. For example, `1px*em/in*cm` would return `["px", "em"]` @return [Array<String>] |
value | [R] |
The Ruby value of the number.
@return [Numeric] |
@param value [Numeric] The value of the number @param numerator_units [Array<String>] See \{numerator_units} @param denominator_units [Array<String>] See \{denominator_units}
# File lib/sass/script/number.rb, line 36 36: def initialize(value, numerator_units = [], denominator_units = []) 37: super(value) 38: @numerator_units = numerator_units 39: @denominator_units = denominator_units 40: normalize! 41: end
Returns this number converted to other units. The conversion takes into account the relationship between e.g. mm and cm, as well as between e.g. in and cm.
If this number has no units, it will simply return itself with the given units.
An incompatible coercion, e.g. between px and cm, will raise an error.
@param num_units [Array<String>] The numerator units to coerce this number into.
See {\#numerator\_units}
@param den_units [Array<String>] The denominator units to coerce this number into.
See {\#denominator\_units}
@return [Number] The number with the new units @raise [Sass::UnitConversionError] if the given units are incompatible with the number‘s
current units
# File lib/sass/script/number.rb, line 278 278: def coerce(num_units, den_units) 279: Number.new(if unitless? 280: self.value 281: else 282: self.value * coercion_factor(self.numerator_units, num_units) / 283: coercion_factor(self.denominator_units, den_units) 284: end, num_units, den_units) 285: end
The SassScript `/` operation. Its functionality depends on the type of its argument:
{Number} : Divides this number by the other, converting units appropriately.
{Literal} : See {Literal#div}.
@param other [Literal] The right-hand side of the operator @return [Literal] The result of the operation
# File lib/sass/script/number.rb, line 128 128: def div(other) 129: if other.is_a? Number 130: operate(other, :/) 131: else 132: super 133: end 134: end
The SassScript `==` operation.
@param other [Literal] The right-hand side of the operator @return [Boolean] Whether this number is equal to the other object
# File lib/sass/script/number.rb, line 157 157: def eq(other) 158: return Sass::Script::Bool.new(false) unless other.is_a?(Sass::Script::Number) 159: this = self 160: begin 161: if unitless? 162: this = this.coerce(other.numerator_units, other.denominator_units) 163: else 164: other = other.coerce(numerator_units, denominator_units) 165: end 166: rescue Sass::UnitConversionError 167: return Sass::Script::Bool.new(false) 168: end 169: 170: Sass::Script::Bool.new(this.value == other.value) 171: end
The SassScript `>` operation.
@param other [Number] The right-hand side of the operator @return [Boolean] Whether this number is greater than the other @raise [NoMethodError] if `other` is an invalid type
# File lib/sass/script/number.rb, line 178 178: def gt(other) 179: raise NoMethodError.new(nil, :gt) unless other.is_a?(Number) 180: operate(other, :>) 181: end
The SassScript `>=` operation.
@param other [Number] The right-hand side of the operator @return [Boolean] Whether this number is greater than or equal to the other @raise [NoMethodError] if `other` is an invalid type
# File lib/sass/script/number.rb, line 188 188: def gte(other) 189: raise NoMethodError.new(nil, :gte) unless other.is_a?(Number) 190: operate(other, :>=) 191: end
Returns a readable representation of this number.
This representation is valid CSS (and valid SassScript) as long as there is only one unit.
@return [String] The representation
# File lib/sass/script/number.rb, line 227 227: def inspect 228: value = 229: if self.value.is_a?(Float) && (self.value.infinite? || self.value.nan?) 230: self.value 231: elsif int? 232: self.value.to_i 233: else 234: (self.value * PRECISION).round / PRECISION 235: end 236: "#{value}#{unit_str}" 237: end
@return [Boolean] Whether or not this number is an integer.
# File lib/sass/script/number.rb, line 247 247: def int? 248: value % 1 == 0.0 249: end
@return [Boolean] Whether or not this number has units that can be represented in CSS
(that is, zero or one \{#numerator\_units}).
# File lib/sass/script/number.rb, line 258 258: def legal_units? 259: (numerator_units.empty? || numerator_units.size == 1) && denominator_units.empty? 260: end
The SassScript `<` operation.
@param other [Number] The right-hand side of the operator @return [Boolean] Whether this number is less than the other @raise [NoMethodError] if `other` is an invalid type
# File lib/sass/script/number.rb, line 198 198: def lt(other) 199: raise NoMethodError.new(nil, :lt) unless other.is_a?(Number) 200: operate(other, :<) 201: end
The SassScript `<=` operation.
@param other [Number] The right-hand side of the operator @return [Boolean] Whether this number is less than or equal to the other @raise [NoMethodError] if `other` is an invalid type
# File lib/sass/script/number.rb, line 208 208: def lte(other) 209: raise NoMethodError.new(nil, :lte) unless other.is_a?(Number) 210: operate(other, :<=) 211: end
The SassScript binary `-` operation (e.g. `!a - !b`). Its functionality depends on the type of its argument:
{Number} : Subtracts this number from the other, converting units if possible.
{Literal} : See {Literal#minus}.
@param other [Literal] The right-hand side of the operator @return [Literal] The result of the operation @raise [Sass::UnitConversionError] if `other` is a number with incompatible units
# File lib/sass/script/number.rb, line 80 80: def minus(other) 81: if other.is_a? Number 82: operate(other, :-) 83: else 84: super 85: end 86: end
The SassScript `%` operation.
@param other [Number] The right-hand side of the operator @return [Number] This number modulo the other @raise [NoMethodError] if `other` is an invalid type @raise [Sass::UnitConversionError] if `other` has any units
# File lib/sass/script/number.rb, line 142 142: def mod(other) 143: if other.is_a?(Number) 144: unless other.unitless? 145: raise Sass::UnitConversionError.new("Cannot modulo by a number with units: #{other.inspect}.") 146: end 147: operate(other, :%) 148: else 149: raise NoMethodError.new(nil, :mod) 150: end 151: end
The SassScript `+` operation. Its functionality depends on the type of its argument:
{Number} : Adds the two numbers together, converting units if possible.
{Color} : Adds this number to each of the RGB color channels.
{Literal} : See {Literal#plus}.
@param other [Literal] The right-hand side of the operator @return [Literal] The result of the operation @raise [Sass::UnitConversionError] if `other` is a number with incompatible units
# File lib/sass/script/number.rb, line 58 58: def plus(other) 59: if other.is_a? Number 60: operate(other, :+) 61: elsif other.is_a?(Color) 62: other.plus(self) 63: else 64: super 65: end 66: end
The SassScript `*` operation. Its functionality depends on the type of its argument:
{Number} : Multiplies the two numbers together, converting units appropriately.
{Color} : Multiplies each of the RGB color channels by this number.
@param other [Number, Color] The right-hand side of the operator @return [Number, Color] The result of the operation @raise [NoMethodError] if `other` is an invalid type
# File lib/sass/script/number.rb, line 107 107: def times(other) 108: if other.is_a? Number 109: self.operate(other, :*) 110: elsif other.is_a? Color 111: other.times(self) 112: else 113: raise NoMethodError.new(nil, :times) 114: end 115: end
@return [Fixnum] The integer value of the number @raise [Sass::SyntaxError] if the number isn‘t an integer
# File lib/sass/script/number.rb, line 241 241: def to_i 242: super unless int? 243: return value 244: end
@return [String] The CSS representation of this number @raise [Sass::SyntaxError] if this number has units that can‘t be used in CSS
(e.g. `px*in`)
# File lib/sass/script/number.rb, line 216 216: def to_s 217: raise Sass::SyntaxError.new("#{inspect} isn't a valid CSS value.") unless legal_units? 218: inspect 219: end
@return [Boolean] Whether or not this number has no units.
# File lib/sass/script/number.rb, line 252 252: def unitless? 253: numerator_units.empty? && denominator_units.empty? 254: end
# File lib/sass/script/number.rb, line 309 309: def coercion_factor(from_units, to_units) 310: # get a list of unmatched units 311: from_units, to_units = sans_common_units(from_units, to_units) 312: 313: if from_units.size != to_units.size || !convertable?(from_units | to_units) 314: raise Sass::UnitConversionError.new("Incompatible units: '#{from_units.join('*')}' and '#{to_units.join('*')}'.") 315: end 316: 317: from_units.zip(to_units).inject(1) {|m,p| m * conversion_factor(p[0], p[1]) } 318: end
# File lib/sass/script/number.rb, line 320 320: def compute_units(this, other, operation) 321: case operation 322: when :* 323: [this.numerator_units + other.numerator_units, this.denominator_units + other.denominator_units] 324: when :/ 325: [this.numerator_units + other.denominator_units, this.denominator_units + other.numerator_units] 326: else 327: [this.numerator_units, this.denominator_units] 328: end 329: end
# File lib/sass/script/number.rb, line 363 363: def conversion_factor(from_unit, to_unit) 364: res = CONVERSION_TABLE[CONVERTABLE_UNITS[from_unit]][CONVERTABLE_UNITS[to_unit]] 365: return 1.0 / conversion_factor(to_unit, from_unit) if res.nil? 366: res 367: end
# File lib/sass/script/number.rb, line 369 369: def convertable?(units) 370: Array(units).all?(&CONVERTABLE_UNITS.method(:include?)) 371: end
# File lib/sass/script/number.rb, line 340 340: def normalize! 341: return if unitless? 342: @numerator_units, @denominator_units = sans_common_units(numerator_units, denominator_units) 343: 344: @denominator_units.each_with_index do |d, i| 345: if convertable?(d) && (u = @numerator_units.detect(&method(:convertable?))) 346: @value /= conversion_factor(d, u) 347: @denominator_units.delete_at(i) 348: @numerator_units.delete_at(@numerator_units.index(u)) 349: end 350: end 351: end
# File lib/sass/script/number.rb, line 289 289: def operate(other, operation) 290: this = self 291: if [:+, :-, :<=, :<, :>, :>=].include?(operation) 292: if unitless? 293: this = this.coerce(other.numerator_units, other.denominator_units) 294: else 295: other = other.coerce(numerator_units, denominator_units) 296: end 297: end 298: # avoid integer division 299: value = (:/ == operation) ? this.value.to_f : this.value 300: result = value.send(operation, other.value) 301: 302: if result.is_a?(Numeric) 303: Number.new(result, *compute_units(this, other, operation)) 304: else # Boolean op 305: Bool.new(result) 306: end 307: end
# File lib/sass/script/number.rb, line 373 373: def sans_common_units(units1, units2) 374: units2 = units2.dup 375: # Can't just use -, because we want px*px to coerce properly to px*mm 376: return units1.map do |u| 377: next u unless j = units2.index(u) 378: units2.delete_at(j) 379: nil 380: end.compact, units2 381: end