| /*! | 
|  * range-parser | 
|  * Copyright(c) 2012-2014 TJ Holowaychuk | 
|  * Copyright(c) 2015-2016 Douglas Christopher Wilson | 
|  * MIT Licensed | 
|  */ | 
|   | 
| 'use strict' | 
|   | 
| /** | 
|  * Module exports. | 
|  * @public | 
|  */ | 
|   | 
| module.exports = rangeParser | 
|   | 
| /** | 
|  * Parse "Range" header `str` relative to the given file `size`. | 
|  * | 
|  * @param {Number} size | 
|  * @param {String} str | 
|  * @param {Object} [options] | 
|  * @return {Array} | 
|  * @public | 
|  */ | 
|   | 
| function rangeParser (size, str, options) { | 
|   if (typeof str !== 'string') { | 
|     throw new TypeError('argument str must be a string') | 
|   } | 
|   | 
|   var index = str.indexOf('=') | 
|   | 
|   if (index === -1) { | 
|     return -2 | 
|   } | 
|   | 
|   // split the range string | 
|   var arr = str.slice(index + 1).split(',') | 
|   var ranges = [] | 
|   | 
|   // add ranges type | 
|   ranges.type = str.slice(0, index) | 
|   | 
|   // parse all ranges | 
|   for (var i = 0; i < arr.length; i++) { | 
|     var range = arr[i].split('-') | 
|     var start = parseInt(range[0], 10) | 
|     var end = parseInt(range[1], 10) | 
|   | 
|     // -nnn | 
|     if (isNaN(start)) { | 
|       start = size - end | 
|       end = size - 1 | 
|     // nnn- | 
|     } else if (isNaN(end)) { | 
|       end = size - 1 | 
|     } | 
|   | 
|     // limit last-byte-pos to current length | 
|     if (end > size - 1) { | 
|       end = size - 1 | 
|     } | 
|   | 
|     // invalid or unsatisifiable | 
|     if (isNaN(start) || isNaN(end) || start > end || start < 0) { | 
|       continue | 
|     } | 
|   | 
|     // add range | 
|     ranges.push({ | 
|       start: start, | 
|       end: end | 
|     }) | 
|   } | 
|   | 
|   if (ranges.length < 1) { | 
|     // unsatisifiable | 
|     return -1 | 
|   } | 
|   | 
|   return options && options.combine | 
|     ? combineRanges(ranges) | 
|     : ranges | 
| } | 
|   | 
| /** | 
|  * Combine overlapping & adjacent ranges. | 
|  * @private | 
|  */ | 
|   | 
| function combineRanges (ranges) { | 
|   var ordered = ranges.map(mapWithIndex).sort(sortByRangeStart) | 
|   | 
|   for (var j = 0, i = 1; i < ordered.length; i++) { | 
|     var range = ordered[i] | 
|     var current = ordered[j] | 
|   | 
|     if (range.start > current.end + 1) { | 
|       // next range | 
|       ordered[++j] = range | 
|     } else if (range.end > current.end) { | 
|       // extend range | 
|       current.end = range.end | 
|       current.index = Math.min(current.index, range.index) | 
|     } | 
|   } | 
|   | 
|   // trim ordered array | 
|   ordered.length = j + 1 | 
|   | 
|   // generate combined range | 
|   var combined = ordered.sort(sortByRangeIndex).map(mapWithoutIndex) | 
|   | 
|   // copy ranges type | 
|   combined.type = ranges.type | 
|   | 
|   return combined | 
| } | 
|   | 
| /** | 
|  * Map function to add index value to ranges. | 
|  * @private | 
|  */ | 
|   | 
| function mapWithIndex (range, index) { | 
|   return { | 
|     start: range.start, | 
|     end: range.end, | 
|     index: index | 
|   } | 
| } | 
|   | 
| /** | 
|  * Map function to remove index value from ranges. | 
|  * @private | 
|  */ | 
|   | 
| function mapWithoutIndex (range) { | 
|   return { | 
|     start: range.start, | 
|     end: range.end | 
|   } | 
| } | 
|   | 
| /** | 
|  * Sort function to sort ranges by index. | 
|  * @private | 
|  */ | 
|   | 
| function sortByRangeIndex (a, b) { | 
|   return a.index - b.index | 
| } | 
|   | 
| /** | 
|  * Sort function to sort ranges by start position. | 
|  * @private | 
|  */ | 
|   | 
| function sortByRangeStart (a, b) { | 
|   return a.start - b.start | 
| } |