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; |