baja/obj/Float.js

/**
 * @copyright 2015 Tridium, Inc. All Rights Reserved.
 * @author Gareth Johnson
 */

/**
 * Defines {@link baja.Float}.
 * @module baja/obj/Float
 */
define([ 'bajaScript/sys',
        'bajaScript/baja/obj/numberUtil',
        'bajaScript/baja/obj/objUtil',
        'bajaScript/baja/obj/Simple' ], function (
        baja,
        numberUtil,
        objUtil,
        Simple) {
  
  'use strict';
  
  var subclass = baja.subclass,
      callSuper = baja.callSuper,
      strictArg = baja.strictArg,

      floatingPointToString = numberUtil.floatingPointToString,

      cacheDecode = objUtil.cacheDecode,
      cacheEncode = objUtil.cacheEncode,
      uncacheConstantEncodeDecode = objUtil.uncacheConstantEncodeDecode;

  /**
   * Represents a `baja:Float` in BajaScript.
   * 
   * Boxes JavaScript `Number` to represent a `baja:Float`.
   * 
   * When creating a `Simple`, always use the `make()` method instead of 
   * creating a new Object.
   *
   * @class
   * @alias baja.Float
   * @extends baja.Simple
   */
  var Float = function Float(val) {
    callSuper(Float, this, arguments); 
    this.$val = strictArg(val, Number);
  };
  
  subclass(Float, Simple);
      
  /**
   * Make a `Float`.
   *
   * @param {Number} val - the number value.
   *
   * @returns {baja.Float}
   */
  Float.make = function (val) {        
    if (val === 0) {
      return Float.DEFAULT;
    }
    
    if (isNaN(val)) {
      return Float.NaN;
    }
    
    if (val === Float.POSITIVE_INFINITY.valueOf()) {
      return Float.POSITIVE_INFINITY;
    }
    
    if (val === Float.NEGATIVE_INFINITY.valueOf()) {
      return Float.NEGATIVE_INFINITY;
    }
    
    if (val >= Float.MAX_VALUE.valueOf()) {
      return Float.MAX_VALUE;
    }
    
    if (val > 0 && val <= Float.MIN_VALUE.valueOf()) {
      return Float.MIN_VALUE;
    }
      
    return new Float(val);
  };
  
  /**
   * Make a `Float`.
   *
   * @param {Number} val - the number value.
   *
   * @returns {baja.Float}
   */
  Float.prototype.make = function (val) {
    return Float.make(val);
  };
    
  /**
   * Decode a `Float` from a `String`.
   *
   * @param {String} str - an encoded `Float`.
   *
   * @returns {baja.Float}
   */
  Float.prototype.decodeFromString = function (str) {
    switch (str) {
    case "0": return Float.DEFAULT;
    
    // Infinity and NaN
    case "+inf": return Float.POSITIVE_INFINITY;
    case "-inf": return Float.NEGATIVE_INFINITY;
    case "nan": return Float.NaN;
    
    default: return Float.make(parseFloat(str));
    }
  };
      
  /**
   * Encode the `Float` (itself) to a `String`.
   *
   * @returns {String}
   */
  Float.prototype.encodeToString = function () {
  
    // Infinity and NaN
    if (this.$val === Number.POSITIVE_INFINITY) {
      return "+inf";
    }
    
    if (this.$val === Number.NEGATIVE_INFINITY) {
      return "-inf";
    }
    
    if (isNaN(this.$val)) {
      return "nan";
    }
    
    // Check range limits
    if (this.$val >= Float.MAX_VALUE.valueOf()) {
      return Float.MAX_VALUE.valueOf().toString();
    }
    
    if (this.$val > 0 && this.$val <= Float.MIN_VALUE.valueOf()) {
      return Float.MIN_VALUE.valueOf().toString();
    }
    
    return this.$val.toString();
  };
  
  /**
   * Default `Float` instance.
   * @type {baja.Float}
   */
  Float.DEFAULT = uncacheConstantEncodeDecode(new Float(0));
  
  /**
   * `Float` Max Value.
   * @type {baja.Float}
   */
  Float.MAX_VALUE = uncacheConstantEncodeDecode(new Float(3.4028235E38));
  
  /**
   * `Float` Min Value.
   * @type {baja.Float}
   */
  Float.MIN_VALUE = uncacheConstantEncodeDecode(new Float(1.4E-45));
  
  /**
   * `Float` Positive Infinity.
   * @type {baja.Float}
   */
  Float.POSITIVE_INFINITY = uncacheConstantEncodeDecode(new Float(Number.POSITIVE_INFINITY));
  
  /**
   * `Float` Negative Infinity.
   * @type {baja.Float}
   */
  Float.NEGATIVE_INFINITY = uncacheConstantEncodeDecode(new Float(Number.NEGATIVE_INFINITY));
  
  /**
   * `Float` Not A Number.
   * @type {baja.Float}
   */
  Float.NaN = uncacheConstantEncodeDecode(new Float(Number.NaN));
  Float.NAN = Float.NaN;  // Here for customers that may be using 'NAN'

  Float.prototype.decodeFromString = cacheDecode(Float.prototype.decodeFromString);
  Float.prototype.encodeToString = cacheEncode(Float.prototype.encodeToString);
  
  /**
   * Return the data type symbol.
   *
   * Used for encoding this data type (primarily for facets).
   *
   * @returns {String}
   */
  Float.prototype.getDataTypeSymbol = function () {
    return "f";
  };

  /**
   * Equality test.
   *
   * @param obj
   *
   * @returns {Boolean}
   */
  Float.prototype.equals = function (obj) {
    return objUtil.valueOfEquals(this, obj);
  };
  
  /**
   * Return the `Number` encapsulated in the `Float` (itself).
   *
   * @returns {Number}
   */
  Float.prototype.valueOf = function () {
    return this.$val;
  };
  
  /**
   * Return the `String` representation of the `Float` (itself).
   *
   * @param {baja.Facets|Object} [cx] - used to specify formatting facets. The
   * argument can also be an Object Literal.
   *
   * @param {Boolean} [cx.forceSign] - specifying 'true' will concatenate a '+'
   * to the beginning of the number if positive.
   *
   * @param {Number} [cx.precision] - the number of decimal places to show in
   * the return string. Specifying '0' will also remove the decimal. If a context
   * is provided without precision, this value will default to 2. If no context
   * is provided, there will be no precision applied.
   *
   * @param {Boolean} [cx.showSeparators] - include separators.
   *
   * @param {baja.Unit} [cx.units] - the baja Unit to apply to the return
   * string.
   *
   * @param {baja.Enum|Number|String} [cx.unitConversion] - the
   * `baja:UnitConversion` enum, an ordinal, or tag.
   *
   * @param {Number} [cx.zeroPad] - the minimum number of the whole-number
   * digits to be displayed, filling in zeroes when necessary.
   *
   * @param {boolean} [cx.trimTrailingZeros] - set to true if trailing zeros
   * should not be included in the string. If the only digits after the decimal
   * point are zeros, the decimal point will be removed as well.
   *
   * @returns {String|Promise.<String>} returns a Promise if a cx is passed in.
   */
  Float.prototype.toString = function (cx) {
    if (cx) {
      if (cx instanceof baja.Facets) {
        cx = cx.toObject();
      }
      return floatingPointToString(this, cx);
    }
    return this.encodeToString();
  };
  
  return Float;
});