WaBis

walter.bislins.ch

Datei: CP: NumFormatter.js

Inhalt der Datei: ./javascript/ControlPanel/src/NumFormatter.js
// NumFormatter.js (C) Walter Bislin; walter.bislins.ch; Juli 2013
//
// description and download:
//  http://walter.bislins.ch/doku/NumFormatter
//
// dependecies:
//  none
//
// History:
//  2013-08-08: first Version

function CNumFormatter() {
  this.Lang        = 'iso';     // iso, en, de, ch, '' = iso
  this.DecimalChar = ',';
  this.MantGrpChar = ' ';
  this.MantGrpSize = 3;
  this.MantGrpMinSize = 4;
  this.FracGrpChar = ' ';
  this.FracGrpSize = 3;
  this.FracGrpMinSize = 4;
  this.TableLike   = true;     // true -> always use MantGrpSize and fracGrpsSize to group digits
  this.ExpChar     = ' E';     // 'E' or 'e' only! A space in front of e/E is allowed.
  this.ExpLeadZero = 2;        // 1-3
  this.ShowExpPlus = true;
  this.HideZeroExp = false;
  this.UnitSepChar = ' ';
  this.Mode        = 'std';    // std, prec, fix, fix0, weak, weak0, sci, eng, unit
  this.AltMode     = 'eng';    // sci, eng
  this.AltPrec     = 13;       // precision if fix and weak mode alters to sci or eng (MaxDigits-(5 or 4)) dep. on ExpLeadZero
  this.Precision   = 8;
  this.Prefix      = [ 'a', 'f', 'p', 'n', 'μ', 'm', '', 'k', 'M', 'G', 'T', 'P', 'E' ];
  this.Prefix0     = 6;
  this.MaxPrec     = 13;       // < 17! rounding errors occour with MaxPrec > 13
  this.MaxDigits   = 17;
}

CNumFormatter.prototype.SetLang = function( aLang ) {
  // aLang = 'ch', 'de', 'fr', 'en', '' = 'en'
  if (aLang == 'en') {
    this.DecimalChar = '.';
    this.MantGrpChar = ',';
    this.MantGrpSize = 3;
    this.MantGrpMinSize = 4;
    this.FracGrpChar = '';
    this.FracGrpSize = 3;
    this.FracGrpMinSize = 4;
  } else if (aLang == 'de') {
    this.DecimalChar = ',';
    this.MantGrpChar = '.';
    this.MantGrpSize = 3;
    this.MantGrpMinSize = 4;
    this.FracGrpChar = '';
    this.FracGrpSize = 3;
    this.FracGrpMinSize = 4;
  } else if (aLang == 'ch') {
    this.DecimalChar = ',';
    this.MantGrpChar = '\'';
    this.MantGrpSize = 3;
    this.MantGrpMinSize = 4;
    this.FracGrpChar = '';
    this.FracGrpSize = 3;
    this.FracGrpMinSize = 4;
  } else {
    // aLang = 'iso' and others
    this.DecimalChar = ',';
    this.MantGrpChar = ' ';
    this.MantGrpSize = 3;
    this.MantGrpMinSize = 4;
    this.FracGrpChar = ' ';
    this.FracGrpSize = 3;
    this.FracGrpMinSize = 4;
  }
  this.Lang = aLang;
}

CNumFormatter.prototype.SetPrefix = function( aPrefixList, aPrefix0 ) {
  this.Prefix = aPrefixList;
  this.Prefix0 = aPrefix0;
}

CNumFormatter.prototype.GetPrefixFromNumParts = function( aNumParts ) {
  var ix = aNumParts.PrefixIx;
  if (ix >= 0 && ix < this.Prefix.length) {
    return this.Prefix[ix];
  } else {
    return '';
  }
}

CNumFormatter.prototype.IsArray = function( aObj ) {
  return (Object.prototype.toString.call(aObj) === '[object Array]');
}

CNumFormatter.prototype.IsNumeric = function( x ) {
  // returns true if x is type number or numeric string. '100%' -> false!
  // Source: jQuery
  return (!this.IsArray(x) && (x - parseFloat(x) >= 0));
}

CNumFormatter.prototype.FormatNumStr = function( aNumStr ) {
  // inserts SepChar and FracChar into aNumStr
  // aNumStr = 12345.678912 -> 12'234.678 912 or 12.234,678912 or ...
  var x = aNumStr.split( '.' );
  if (!this.IsNumeric(x[0]) || (x.length > 1 && !this.IsNumeric(x[1]))) return aNumStr;
  var x1 = x[0];
  var x2 = x.length > 1 ? x[1] : '';
  if (this.MantGrpChar != '' && this.MantGrpSize > 0 && x1 != '' && (this.TableLike || x1.length > this.MantGrpMinSize)) {
    var rgx = new RegExp( '(\\d+)(\\d{' + this.MantGrpSize + '})', '' );
    while (rgx.test(x1)) {
      x1 = x1.replace( rgx, '$1' + this.MantGrpChar + '$2' );
    }
  }
  if (this.FracGrpChar != '' && this.FracGrpSize > 0 && x2 != '' && (this.TableLike || x2.length > this.FracGrpMinSize)) {
    var rgx = new RegExp( '(\\d{' + this.FracGrpSize + '})(\\d+)', '' );
    while (rgx.test(x2)) {
      x2 = x2.replace( rgx, '$1' + this.FracGrpChar + '$2' );
    }
  }
  if (x2 != '') x1 += this.DecimalChar + x2;
  return x1;
}

CNumFormatter.prototype.CutTrailingZeros = function( aNumStr ) {
  // cuts unneeded zeros behind decimal point
  var p = aNumStr.indexOf( '.' );
  if (p < 0) return aNumStr;
  var s = aNumStr.replace( /0+$/, '' );
  // remove point if it is last left char at end
  if (p == (s.length - 1)) {
    s = s.substr( 0, p );
  }
  return s;
}

CNumFormatter.prototype.SplitNumStr = function( aNumStr ) {
  // returns { MantStr: str, ExpStr: str, Exp: +/-int }
  var exp = 0;
  var expStr = '';
  var mantStr = aNumStr;
  var p = mantStr.indexOf( 'e' );
  if (p < 0) p = mantStr.indexOf( 'E' );
  if (p > 0) {
    // handle exponent
    expStr = mantStr.substr( p+1 );
    mantStr = mantStr.substr( 0, p );
    exp = parseInt( expStr, 10 );
  }
  return { MantStr: mantStr, ExpStr: expStr, Exp: exp };
}

CNumFormatter.prototype.GetExponent = function( aNum, aPrec ) {
  if (aPrec < 1) aPrec = 1;
  var numSciStr = aNum.toExponential( aPrec-1 );
  var numStrParts = this.SplitNumStr( numSciStr );
  return numStrParts.Exp;
}

CNumFormatter.prototype.NDigits = function( aNumStr ) {
  // count digits after removing exponent and all not digit chars
  var s = aNumStr.replace( /[eE][+-]?\d+/, '' );
  s = s.replace( /\D/g, '' );
  return s.length;
}

CNumFormatter.prototype.ExpToStr = function( aExp, aLeadingZeros ) {
  // converts an exponent integer into a string and prepends it with leading zeros
  var expStr = aExp.toFixed(0);
  while (expStr.length < aLeadingZeros) expStr = '0' + expStr;
  return expStr;
}

CNumFormatter.prototype.SplitNum = function( aNum, aFormat ) {
  // aFormat = { Mode, Precision, Units }
  // Mode = 'std', 'prec', 'fix', 'fix0', 'weak', 'weak0', 'sci', 'eng', 'unit'
  // Precision = positive Int
  // if Mode = 'unit' then exp is corrected for used unit-prefix and return.PrefixIx is index into used this.Prefix
  // returns { MantSign: +/-1, Mant: +num, ExpSign: +/-1, Exp: +int, Exp3: int, PrefixIx: int, Mode: str, InitMode: str, Precision: +int, Units: str };

  var mode = this.Mode;
  var prec = this.Precision;
  var units = '';
  if (typeof(aFormat) == 'object') {
    if (typeof(aFormat.Mode) == 'string') { mode = aFormat.Mode; }
    if (typeof(aFormat.Units) == 'string') { units = aFormat.Units; }
    if (typeof(aFormat.Precision) == 'number') { prec = aFormat.Precision; }
  }
  if (prec < 0) { prec = 0; }
  if (prec > this.MaxPrec) { prec = this.MaxPrec; }
  var numParts = { MantSign: 1, Mant: aNum, ExpSign: 1, Exp: 0, Exp3: 0, PrefixIx: -1, InitMode: mode, Mode: mode, Precision: prec, Units: units };

  var mantSign = 1;
  var mant = aNum;
  if (mant < 0) {
    mant = -mant;
    mantSign = -1;
  }
  numParts.MantSign = mantSign;
  numParts.Mant = mant;

  // if mode = 'prec' and exponent is negativ then use standard mode with trailing zeros
  if (mode == 'prec') {
    var exp = this.GetExponent( mant, prec );
    if (exp < 0) {
      mode = 'std';
      numParts.Mode = mode;
    }
  }

  // handle mode std, fix, fix0, prec, weak, weak0
  if (mode == 'std') {
    if (prec < 1) { prec = 1; }
    numParts.Precision = prec;
    var nd = prec;
    var exp = this.GetExponent( mant, prec );
    var prec1 = prec - 1;
    if (exp >= prec1) {
      nd += exp - prec1;
    } else {
      nd += -exp;
    }
    if (nd > this.MaxDigits ) {
      mode = this.AltMode;
    } else {
      return numParts;
    }

  } else if (mode == 'fix' || mode == 'fix0') {
    var precStr = mant.toFixed( prec );
    var forceExp = (precStr.indexOf('e') > 0 || precStr.indexOf('E') > 0);
    if (mode == 'fix') precStr = this.RemoveTrailingZeros(precStr);
    var nd = this.NDigits( precStr );
    if (nd > this.MaxDigits || forceExp) {
      mode = this.AltMode;
      prec = this.AltPrec;
      if (prec > this.MaxPrec) { prec = this.MaxPrec; }
    } else {
      return numParts;
    }

  } else if (mode == 'weak' || mode == 'weak0') {
    var exp = this.GetExponent( mant, prec );
    var precStr = mant.toFixed( prec );
    var forceExp = (precStr.indexOf('e') > 0 || precStr.indexOf('E') > 0);
    if (mode == 'weak') precStr = this.RemoveTrailingZeros(precStr);
    var nd = this.NDigits( precStr );
    if (nd > this.MaxDigits || exp < -prec || forceExp) {
      mode = this.AltMode;
      prec = this.AltPrec;
      if (prec > this.MaxPrec) { prec = this.MaxPrec; }
    } else {
      return numParts;
    }

  } else if (mode == 'prec') {
    if (prec < 1) { prec = 1; }
    numParts.Precision = prec;
    var exp = this.GetExponent( mant, prec );
    var precStr = mant.toPrecision( prec );
    var forceExp = (precStr.indexOf('e') > 0 || precStr.indexOf('E') > 0);
    var nd = this.NDigits( precStr );
    if (nd > this.MaxDigits || exp <= -prec || exp >= prec || forceExp) {
      mode = this.AltMode;
    } else {
      return numParts;
    }

  } else if (mode !== 'sci' && mode !== 'eng' && mode !== 'unit') {
    // invalid mode -> use 'sci'
    mode = 'sci';
    numParts.Mode = mode;
  }

  // handle mode sci and eng:
  // split mantisse and exponent into mantSign, mant, expSign, exp
  if (prec < 1) { prec = 1; }
  var exp = 0;
  var expSign = 1;
  var exp3 = 0;
  if ((mode == 'eng' || mode == 'unit') && prec < 3) prec = 3;
  exp = this.GetExponent( mant, prec );
  if (exp < 0) {
    expSign = -1;
    exp = -exp;
  }
  mant /= Math.pow( 10, expSign * exp );
  // handle mode eng by correcting mant and exp
  if (mode === 'eng' || mode === 'unit') {
    var corr = exp % 3;
    if (expSign < 0) {
      corr = (3 - corr) % 3;
    }
    if (corr > 0) {
      exp -= expSign * corr;
      mant *= Math.pow( 10, corr );
    }
    prec -= corr;
    exp3 = expSign * Math.floor( exp / 3 );
    if (mode === 'unit') {
      var prefixIx = exp3 + this.Prefix0;
      //if (prefixIx >= 0 && prefixIx < this.Prefix.length && exp3 != 0) {
      if (prefixIx >= 0 && prefixIx < this.Prefix.length) {
        exp -= expSign * exp3 * 3;
        numParts.PrefixIx = prefixIx;
      }
    }
  }
  // make numParts
  numParts.Mode = mode;
  numParts.MantSign = mantSign;
  numParts.Mant = mant;
  numParts.ExpSign = expSign;
  numParts.Exp = exp;
  numParts.Exp3 = exp3;
  numParts.Precision = prec;
  return numParts;
}

CNumFormatter.prototype.RemoveTrailingZeros = function( numStr ) {
  numStr = numStr.replace( /(\.\d*?)0+$/, '$1' );
  numStr = numStr.replace( /\.$/, '' );
  return numStr;
}

CNumFormatter.prototype.NumPartsToString = function( aNumParts ) {
  // numParts = { MantSign: +/-1, Mant: +int, ExpSign: +/-1, Exp: +int, Exp3: int, PrefixIx: int, InitMode: str, Mode: str, Precision: +int, Units: str };

  function NZeros( aNumZeros ) {
    var zz = '00000000000000000000';
    return zz.substr( 0, aNumZeros );
  }

  if (isNaN(aNumParts.Mant) || !isFinite(aNumParts.Mant)) return aNumParts.Mant.toString();
  var numStr = '';
  var mode = aNumParts.Mode;
  var zerosOnly = false;

  if (mode == 'std') {
    var prec1 = aNumParts.Precision - 1;
    numStr = aNumParts.Mant.toExponential( prec1 );
    var numStrParts = this.SplitNumStr( numStr );
    // returns { MantStr: str, ExpStr: str, Exp: +/-int }
    var mantStr = numStrParts.MantStr.replace( '.', '' );
    var exp = numStrParts.Exp;
    if (exp >= 0) {
      if (exp < prec1) {
        // insert decimal point
        var p = exp + 1;
        numStr = mantStr.substr( 0, p ) + '.' + mantStr.substr( p );
      } else {
        // append zeros
        numStr = mantStr + NZeros( exp - prec1 );
      }
    } else {
      // insert zeros after 0.
      numStr = '0.' + NZeros( -exp - 1 ) + mantStr;
    }
    if (aNumParts.InitMode !== 'prec') {
      numStr = this.CutTrailingZeros( numStr );
    }
    numStr = this.FormatNumStr( numStr );

  } else if (mode == 'prec') {
    numStr = aNumParts.Mant.toPrecision( aNumParts.Precision );
    numStr = this.FormatNumStr( numStr );

  } else if (mode == 'fix' || mode == 'fix0' || mode == 'weak' || mode == 'weak0') {
    numStr = aNumParts.Mant.toFixed( aNumParts.Precision );
    // limit significant digits to this.MaxPrec
    var nDigi = this.NDigits( numStr );
    if (nDigi > this.MaxPrec) {
      var prec1 = this.MaxPrec - 1;
      var mantStr = aNumParts.Mant.toExponential( prec1 );
      var numStrParts = this.SplitNumStr( mantStr );
      // returns { MantStr: str, ExpStr: str, Exp: +/-int }
      mantStr = numStrParts.MantStr.replace( '.', '' );
      var exp = numStrParts.Exp;
      if (exp >= 0) {
        if (exp < prec1) {
          // insert decimal point
          var p = exp + 1;
          numStr = mantStr.substr( 0, p ) + '.' + mantStr.substr( p );
          // fill zeros to aNumParts.Precision
          var nZeros = aNumParts.Precision - (this.MaxPrec - p);
          numStr += NZeros( nZeros );
        } else {
          // append zeros until dezimal point
          numStr = mantStr + NZeros( exp - prec1 );
          if (mode == 'fix0' || mode == 'weak0') {
            // append zeros after dezimal point
            if (aNumParts.Precision > 0) numStr += '.' + NZeros( aNumParts.Precision );
          }
        }
      // else use nativ toFixed format
      }
    }
    if (mode == 'fix' || mode == 'weak') numStr = this.RemoveTrailingZeros( numStr );
    zerosOnly = (numStr.replace( /[\.0]/g, '' ) == '');
    numStr = this.FormatNumStr( numStr );

  } else {
    // mode = 'sci' or 'eng' or 'unit'
    numStr = aNumParts.Mant.toFixed( aNumParts.Precision-1 );
    if (aNumParts.InitMode == 'std') {
      numStr = this.CutTrailingZeros( numStr );
    }
    numStr = this.FormatNumStr( numStr );
    if (aNumParts.Exp > 0 || (!this.HideZeroExp && aNumParts.PrefixIx == -1)) {
      numStr += this.ExpChar;
      if (aNumParts.ExpSign < 0) {
        numStr += '-';
      } else if (this.ShowExpPlus) {
        numStr += '+';
      }
      numStr += this.ExpToStr( aNumParts.Exp, this.ExpLeadZero );
    }
  }

  if (aNumParts.MantSign < 0 && !zerosOnly) {
    numStr = '-' + numStr;
  }

  if (numStr != 'NaN' && aNumParts.Units != '') {
    numStr += this.UnitSepChar;
    numStr += this.GetPrefixFromNumParts( aNumParts );
    numStr += aNumParts.Units;
  }
  return numStr;
}

CNumFormatter.prototype.NumToString = function( aNum, aFormat ) {
  // aFormat = { Mode = str, Precision = +int, UsePrefix = bool, Units = str }
  // Mode = 'std', 'prec', 'fix', 'weak', 'sci', 'eng'
  // Precision = positive Int
  // if Mode = 'unit' then exp is corrected for used unit-prefix and return.PrefixIx is index into used this.Prefix
  // Units = '' or any units; if Units is not '' then computed prefix and Units are appendet to returned numStr

  var numParts = this.SplitNum( aNum, aFormat );
  return this.NumPartsToString( numParts );
}

CNumFormatter.prototype.StringToNum = function( aStr ) {
  function replaceRegExp( aSrcStr, aRegExpStr, aReplStr ) {
    return aSrcStr.replace( new RegExp(aRegExpStr,'g'), aReplStr );
  }
  var s = aStr;
  var c = this.MantGrpChar;
  if (c != '') {
    if (c == '\\') c = '\\\\';
    if (c == ']') c = '\\]';
    s = replaceRegExp( s, '[' + c + ']', '' );
  }
  c = this.FracGrpChar;
  if (c != '') {
    if (c == '\\') c = '\\\\';
    if (c == ']') c = '\\]';
    s = replaceRegExp( s, '[' + c + ']', '' );
  }
  s = replaceRegExp( s, ' ', '' );
  s = replaceRegExp( s, ',', '.' )
  return Number( s );
}

var NumFormatter = new CNumFormatter();


Weitere Infos zur Seite
Erzeugt Sonntag, 7. August 2016
von wabis
Zum Seitenanfang
Geändert Sonntag, 7. August 2016
von wabis