209 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			209 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /*!
 | |
|  * fill-range <https://github.com/jonschlinkert/fill-range>
 | |
|  *
 | |
|  * Copyright (c) 2014-2015, 2017, Jon Schlinkert.
 | |
|  * Released under the MIT License.
 | |
|  */
 | |
| 
 | |
| 'use strict';
 | |
| 
 | |
| var util = require('util');
 | |
| var isNumber = require('is-number');
 | |
| var extend = require('extend-shallow');
 | |
| var repeat = require('repeat-string');
 | |
| var toRegex = require('to-regex-range');
 | |
| 
 | |
| /**
 | |
|  * Return a range of numbers or letters.
 | |
|  *
 | |
|  * @param  {String} `start` Start of the range
 | |
|  * @param  {String} `stop` End of the range
 | |
|  * @param  {String} `step` Increment or decrement to use.
 | |
|  * @param  {Function} `fn` Custom function to modify each element in the range.
 | |
|  * @return {Array}
 | |
|  */
 | |
| 
 | |
| function fillRange(start, stop, step, options) {
 | |
|   if (typeof start === 'undefined') {
 | |
|     return [];
 | |
|   }
 | |
| 
 | |
|   if (typeof stop === 'undefined' || start === stop) {
 | |
|     // special case, for handling negative zero
 | |
|     var isString = typeof start === 'string';
 | |
|     if (isNumber(start) && !toNumber(start)) {
 | |
|       return [isString ? '0' : 0];
 | |
|     }
 | |
|     return [start];
 | |
|   }
 | |
| 
 | |
|   if (typeof step !== 'number' && typeof step !== 'string') {
 | |
|     options = step;
 | |
|     step = undefined;
 | |
|   }
 | |
| 
 | |
|   if (typeof options === 'function') {
 | |
|     options = { transform: options };
 | |
|   }
 | |
| 
 | |
|   var opts = extend({step: step}, options);
 | |
|   if (opts.step && !isValidNumber(opts.step)) {
 | |
|     if (opts.strictRanges === true) {
 | |
|       throw new TypeError('expected options.step to be a number');
 | |
|     }
 | |
|     return [];
 | |
|   }
 | |
| 
 | |
|   opts.isNumber = isValidNumber(start) && isValidNumber(stop);
 | |
|   if (!opts.isNumber && !isValid(start, stop)) {
 | |
|     if (opts.strictRanges === true) {
 | |
|       throw new RangeError('invalid range arguments: ' + util.inspect([start, stop]));
 | |
|     }
 | |
|     return [];
 | |
|   }
 | |
| 
 | |
|   opts.isPadded = isPadded(start) || isPadded(stop);
 | |
|   opts.toString = opts.stringify
 | |
|     || typeof opts.step === 'string'
 | |
|     || typeof start === 'string'
 | |
|     || typeof stop === 'string'
 | |
|     || !opts.isNumber;
 | |
| 
 | |
|   if (opts.isPadded) {
 | |
|     opts.maxLength = Math.max(String(start).length, String(stop).length);
 | |
|   }
 | |
| 
 | |
|   // support legacy minimatch/fill-range options
 | |
|   if (typeof opts.optimize === 'boolean') opts.toRegex = opts.optimize;
 | |
|   if (typeof opts.makeRe === 'boolean') opts.toRegex = opts.makeRe;
 | |
|   return expand(start, stop, opts);
 | |
| }
 | |
| 
 | |
| function expand(start, stop, options) {
 | |
|   var a = options.isNumber ? toNumber(start) : start.charCodeAt(0);
 | |
|   var b = options.isNumber ? toNumber(stop) : stop.charCodeAt(0);
 | |
| 
 | |
|   var step = Math.abs(toNumber(options.step)) || 1;
 | |
|   if (options.toRegex && step === 1) {
 | |
|     return toRange(a, b, start, stop, options);
 | |
|   }
 | |
| 
 | |
|   var zero = {greater: [], lesser: []};
 | |
|   var asc = a < b;
 | |
|   var arr = new Array(Math.round((asc ? b - a : a - b) / step));
 | |
|   var idx = 0;
 | |
| 
 | |
|   while (asc ? a <= b : a >= b) {
 | |
|     var val = options.isNumber ? a : String.fromCharCode(a);
 | |
|     if (options.toRegex && (val >= 0 || !options.isNumber)) {
 | |
|       zero.greater.push(val);
 | |
|     } else {
 | |
|       zero.lesser.push(Math.abs(val));
 | |
|     }
 | |
| 
 | |
|     if (options.isPadded) {
 | |
|       val = zeros(val, options);
 | |
|     }
 | |
| 
 | |
|     if (options.toString) {
 | |
|       val = String(val);
 | |
|     }
 | |
| 
 | |
|     if (typeof options.transform === 'function') {
 | |
|       arr[idx++] = options.transform(val, a, b, step, idx, arr, options);
 | |
|     } else {
 | |
|       arr[idx++] = val;
 | |
|     }
 | |
| 
 | |
|     if (asc) {
 | |
|       a += step;
 | |
|     } else {
 | |
|       a -= step;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (options.toRegex === true) {
 | |
|     return toSequence(arr, zero, options);
 | |
|   }
 | |
|   return arr;
 | |
| }
 | |
| 
 | |
| function toRange(a, b, start, stop, options) {
 | |
|   if (options.isPadded) {
 | |
|     return toRegex(start, stop, options);
 | |
|   }
 | |
| 
 | |
|   if (options.isNumber) {
 | |
|     return toRegex(Math.min(a, b), Math.max(a, b), options);
 | |
|   }
 | |
| 
 | |
|   var start = String.fromCharCode(Math.min(a, b));
 | |
|   var stop = String.fromCharCode(Math.max(a, b));
 | |
|   return '[' + start + '-' + stop + ']';
 | |
| }
 | |
| 
 | |
| function toSequence(arr, zeros, options) {
 | |
|   var greater = '', lesser = '';
 | |
|   if (zeros.greater.length) {
 | |
|     greater = zeros.greater.join('|');
 | |
|   }
 | |
|   if (zeros.lesser.length) {
 | |
|     lesser = '-(' + zeros.lesser.join('|') + ')';
 | |
|   }
 | |
|   var res = greater && lesser
 | |
|     ? greater + '|' + lesser
 | |
|     : greater || lesser;
 | |
| 
 | |
|   if (options.capture) {
 | |
|     return '(' + res + ')';
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| function zeros(val, options) {
 | |
|   if (options.isPadded) {
 | |
|     var str = String(val);
 | |
|     var len = str.length;
 | |
|     var dash = '';
 | |
|     if (str.charAt(0) === '-') {
 | |
|       dash = '-';
 | |
|       str = str.slice(1);
 | |
|     }
 | |
|     var diff = options.maxLength - len;
 | |
|     var pad = repeat('0', diff);
 | |
|     val = (dash + pad + str);
 | |
|   }
 | |
|   if (options.stringify) {
 | |
|     return String(val);
 | |
|   }
 | |
|   return val;
 | |
| }
 | |
| 
 | |
| function toNumber(val) {
 | |
|   return Number(val) || 0;
 | |
| }
 | |
| 
 | |
| function isPadded(str) {
 | |
|   return /^-?0\d/.test(str);
 | |
| }
 | |
| 
 | |
| function isValid(min, max) {
 | |
|   return (isValidNumber(min) || isValidLetter(min))
 | |
|       && (isValidNumber(max) || isValidLetter(max));
 | |
| }
 | |
| 
 | |
| function isValidLetter(ch) {
 | |
|   return typeof ch === 'string' && ch.length === 1 && /^\w+$/.test(ch);
 | |
| }
 | |
| 
 | |
| function isValidNumber(n) {
 | |
|   return isNumber(n) && !/\./.test(n);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Expose `fillRange`
 | |
|  * @type {Function}
 | |
|  */
 | |
| 
 | |
| module.exports = fillRange;
 |