docs: function parse_number_pattern(pattern) {
          // Parse number format patterns
          var PREFIX_END = '[^0-9@#.,]';
          var NUMBER_TOKEN = '[0-9@#.,E+\-]';
  
          var PREFIX_PATTERN = "((?:'[^']*'|"+PREFIX_END+")*)";
          var NUMBER_PATTERN = "("+NUMBER_TOKEN+"+)";
          var SUFFIX_PATTERN = "(.*)";
  
          var number_re = new RegExp(PREFIX_PATTERN + NUMBER_PATTERN + SUFFIX_PATTERN);
          if (pattern instanceof NumberPattern) {
              return pattern;
          }
  
          var neg_pattern, pos_suffix, pos_prefix, neg_prefix, neg_suffix, num, exp, dum, sp;
          // Do we have a negative subpattern?
          if (pattern.indexOf(';')!==-1) {
              sp = pattern.split(';', 2);
              pattern=sp[0]; neg_pattern=sp[1];
  
              sp = pattern.match(number_re).slice(1);
              pos_prefix=sp[0]; num=sp[1]; pos_suffix=sp[2];
  
              sp = neg_pattern.match(number_re).slice(1);
              neg_prefix=sp[0]; neg_suffix=[2];
          } else {
              sp = pattern.match(number_re).slice(1);
              pos_prefix=sp[0]; num=sp[1]; pos_suffix=sp[2];
              neg_prefix = '-' + pos_prefix;
              neg_suffix = pos_suffix;
          }
          if (num.indexOf('E')!==-1) {
              sp = num.split('E', 2);
              num = sp[0]; exp=sp[1];
          } else {
              exp = null;
          }
          if (num.indexOf('@')!==-1) {
              if (num.indexOf('.')!==-1 && num.indexOf('0')!==-1)
                  return alert('Significant digit patterns can not contain "@" or "0"')
          }
          var integer, fraction;
          if (num.indexOf('.')!==-1)  {
              sp = num.rsplit('.', 2);
              integer=sp[0]; fraction=sp[1];
          } else {
              integer = num;
              fraction = '';
          }
          var min_frac = 0, max_frac = 0 ;
  
          function parse_precision(p) {
              // Calculate the min and max allowed digits
              var min = 0; var max = 0;
              for(var i=0; i<p.length; i++) {
                  var c = p.substr(i, 1);
                  if ('@0'.indexOf(c)!==-1) {
                      min += 1
                      max += 1
                  } else if (c == '#') {
                      max += 1
                  } else if (c == ',') {
                      continue;
                  } else {
                      break;
                  }
              }
              return [min, max];
          }
  
          function parse_grouping(p) {
              /*
              Parse primary and secondary digit grouping
  
              >>> parse_grouping('##')
              0, 0
              >>> parse_grouping('#,###')
              3, 3
              >>> parse_grouping('#,####,###')
              3, 4
              */
              var width = p.length;
              var g1 = p.lastIndexOf(',');
              if (g1 == -1)
                  return [1000, 1000];
              g1 = width - g1 - 1;
              // var g2 = p[:-g1 - 1].lastIndexOf(',')
              var g2 = p.substr(0, p.length-g1-1).lastIndexOf(',');
              if (g2 == -1)
                  return [g1, g1];
              g2 = width - g1 - g2 - 2 ;
              return [g1, g2];
          }
  
          var int_prec = parse_precision(integer);
          var frac_prec = parse_precision(fraction);
          var exp_plus;
          var exp_prec;
          if (exp) {
              frac_prec = parse_precision(integer+fraction);
              exp_plus = exp.substr(0, 1) == '+';
              exp = exp.replace(/^\++/, '');
              exp_prec = parse_precision(exp);
          } else {
              exp_plus = null;
              exp_prec = null;
          }
          var grouping = parse_grouping(integer);
          return new NumberPattern(pattern, [pos_prefix, neg_prefix],
                               [pos_suffix, neg_suffix], grouping,
                               int_prec, frac_prec,
                               exp_prec, exp_plus);
      }