Projekt

Obecné

Profil

Stáhnout (4.94 KB) Statistiky
| Větev: | Revize:
1 3a515b92 cagy
/*!
2
 * fill-range <https://github.com/jonschlinkert/fill-range>
3
 *
4
 * Copyright (c) 2014-2015, 2017, Jon Schlinkert.
5
 * Released under the MIT License.
6
 */
7
8
'use strict';
9
10
var util = require('util');
11
var isNumber = require('is-number');
12
var extend = require('extend-shallow');
13
var repeat = require('repeat-string');
14
var toRegex = require('to-regex-range');
15
16
/**
17
 * Return a range of numbers or letters.
18
 *
19
 * @param  {String} `start` Start of the range
20
 * @param  {String} `stop` End of the range
21
 * @param  {String} `step` Increment or decrement to use.
22
 * @param  {Function} `fn` Custom function to modify each element in the range.
23
 * @return {Array}
24
 */
25
26
function fillRange(start, stop, step, options) {
27
  if (typeof start === 'undefined') {
28
    return [];
29
  }
30
31
  if (typeof stop === 'undefined' || start === stop) {
32
    // special case, for handling negative zero
33
    var isString = typeof start === 'string';
34
    if (isNumber(start) && !toNumber(start)) {
35
      return [isString ? '0' : 0];
36
    }
37
    return [start];
38
  }
39
40
  if (typeof step !== 'number' && typeof step !== 'string') {
41
    options = step;
42
    step = undefined;
43
  }
44
45
  if (typeof options === 'function') {
46
    options = { transform: options };
47
  }
48
49
  var opts = extend({step: step}, options);
50
  if (opts.step && !isValidNumber(opts.step)) {
51
    if (opts.strictRanges === true) {
52
      throw new TypeError('expected options.step to be a number');
53
    }
54
    return [];
55
  }
56
57
  opts.isNumber = isValidNumber(start) && isValidNumber(stop);
58
  if (!opts.isNumber && !isValid(start, stop)) {
59
    if (opts.strictRanges === true) {
60
      throw new RangeError('invalid range arguments: ' + util.inspect([start, stop]));
61
    }
62
    return [];
63
  }
64
65
  opts.isPadded = isPadded(start) || isPadded(stop);
66
  opts.toString = opts.stringify
67
    || typeof opts.step === 'string'
68
    || typeof start === 'string'
69
    || typeof stop === 'string'
70
    || !opts.isNumber;
71
72
  if (opts.isPadded) {
73
    opts.maxLength = Math.max(String(start).length, String(stop).length);
74
  }
75
76
  // support legacy minimatch/fill-range options
77
  if (typeof opts.optimize === 'boolean') opts.toRegex = opts.optimize;
78
  if (typeof opts.makeRe === 'boolean') opts.toRegex = opts.makeRe;
79
  return expand(start, stop, opts);
80
}
81
82
function expand(start, stop, options) {
83
  var a = options.isNumber ? toNumber(start) : start.charCodeAt(0);
84
  var b = options.isNumber ? toNumber(stop) : stop.charCodeAt(0);
85
86
  var step = Math.abs(toNumber(options.step)) || 1;
87
  if (options.toRegex && step === 1) {
88
    return toRange(a, b, start, stop, options);
89
  }
90
91
  var zero = {greater: [], lesser: []};
92
  var asc = a < b;
93
  var arr = new Array(Math.round((asc ? b - a : a - b) / step));
94
  var idx = 0;
95
96
  while (asc ? a <= b : a >= b) {
97
    var val = options.isNumber ? a : String.fromCharCode(a);
98
    if (options.toRegex && (val >= 0 || !options.isNumber)) {
99
      zero.greater.push(val);
100
    } else {
101
      zero.lesser.push(Math.abs(val));
102
    }
103
104
    if (options.isPadded) {
105
      val = zeros(val, options);
106
    }
107
108
    if (options.toString) {
109
      val = String(val);
110
    }
111
112
    if (typeof options.transform === 'function') {
113
      arr[idx++] = options.transform(val, a, b, step, idx, arr, options);
114
    } else {
115
      arr[idx++] = val;
116
    }
117
118
    if (asc) {
119
      a += step;
120
    } else {
121
      a -= step;
122
    }
123
  }
124
125
  if (options.toRegex === true) {
126
    return toSequence(arr, zero, options);
127
  }
128
  return arr;
129
}
130
131
function toRange(a, b, start, stop, options) {
132
  if (options.isPadded) {
133
    return toRegex(start, stop, options);
134
  }
135
136
  if (options.isNumber) {
137
    return toRegex(Math.min(a, b), Math.max(a, b), options);
138
  }
139
140
  var start = String.fromCharCode(Math.min(a, b));
141
  var stop = String.fromCharCode(Math.max(a, b));
142
  return '[' + start + '-' + stop + ']';
143
}
144
145
function toSequence(arr, zeros, options) {
146
  var greater = '', lesser = '';
147
  if (zeros.greater.length) {
148
    greater = zeros.greater.join('|');
149
  }
150
  if (zeros.lesser.length) {
151
    lesser = '-(' + zeros.lesser.join('|') + ')';
152
  }
153
  var res = greater && lesser
154
    ? greater + '|' + lesser
155
    : greater || lesser;
156
157
  if (options.capture) {
158
    return '(' + res + ')';
159
  }
160
  return res;
161
}
162
163
function zeros(val, options) {
164
  if (options.isPadded) {
165
    var str = String(val);
166
    var len = str.length;
167
    var dash = '';
168
    if (str.charAt(0) === '-') {
169
      dash = '-';
170
      str = str.slice(1);
171
    }
172
    var diff = options.maxLength - len;
173
    var pad = repeat('0', diff);
174
    val = (dash + pad + str);
175
  }
176
  if (options.stringify) {
177
    return String(val);
178
  }
179
  return val;
180
}
181
182
function toNumber(val) {
183
  return Number(val) || 0;
184
}
185
186
function isPadded(str) {
187
  return /^-?0\d/.test(str);
188
}
189
190
function isValid(min, max) {
191
  return (isValidNumber(min) || isValidLetter(min))
192
      && (isValidNumber(max) || isValidLetter(max));
193
}
194
195
function isValidLetter(ch) {
196
  return typeof ch === 'string' && ch.length === 1 && /^\w+$/.test(ch);
197
}
198
199
function isValidNumber(n) {
200
  return isNumber(n) && !/\./.test(n);
201
}
202
203
/**
204
 * Expose `fillRange`
205
 * @type {Function}
206
 */
207
208
module.exports = fillRange;