| 'use strict' |
| |
| class Hoopy extends Array { |
| constructor (size) { |
| let index, isIndexOverflowed |
| |
| if (! isPositiveInteger(size)) { |
| throw new TypeError('Argument `size` must be a positive integer.') |
| } |
| |
| super(size) |
| |
| this.grow = by => { |
| if (! isPositiveInteger(by)) { |
| throw new TypeError('Argument `by` must be a positive integer.') |
| } |
| |
| let i |
| const newSize = size + by |
| |
| for (i = size; i < newSize; ++i) { |
| this[i] = undefined |
| } |
| |
| if (isIndexOverflowed) { |
| for (i = 0; i <= index; ++i) { |
| let j = size + i |
| if (j >= newSize) { |
| j %= newSize |
| } |
| this[j] = this[i] |
| this[i] = undefined |
| } |
| } |
| |
| size = newSize |
| } |
| |
| return new Proxy(this, { |
| get (target, key) { |
| if (isInteger(key)) { |
| return target[getIndex(key, size)] |
| } |
| |
| return target[key] |
| }, |
| |
| set (target, key, value) { |
| if (isInteger(key)) { |
| index = getIndex(key, size) |
| target[index] = value |
| |
| if (Math.abs(key) >= size) { |
| isIndexOverflowed = true |
| } else { |
| isIndexOverflowed = false |
| } |
| } else { |
| target[key] = value |
| } |
| return true |
| } |
| }) |
| } |
| } |
| |
| function isPositiveInteger (thing) { |
| return isInteger(thing) && thing > 0 |
| } |
| |
| function isInteger (thing) { |
| try { |
| return +thing % 1 === 0 |
| } catch (error) { |
| // Coercing symbols to numbers throws an error |
| } |
| |
| return false |
| } |
| |
| function getIndex (key, size) { |
| if (key === 0) { |
| return 0 |
| } |
| |
| if (key < 0) { |
| return (size - Math.abs(key)) % size |
| } |
| |
| return key % size |
| } |
| |
| function nop () { |
| throw new Error('Not implemented') |
| } |
| |
| Hoopy.prototype.push = nop |
| Hoopy.prototype.pop = nop |
| Hoopy.prototype.shift = nop |
| Hoopy.prototype.unshift = nop |
| |
| module.exports = Hoopy |
| |