-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathranger.js
73 lines (62 loc) · 2.63 KB
/
ranger.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
const
RANGER_SYMBOL = Symbol('ranger'),
paramsStore = {}
let isInitialised = false
// attach the range syntax to the target object, using the given function to generate the range
export function attach(target, rangeFunc) {
!isInitialised && init()
// add the generator that will return the 'ranger array' when the spread operator is used on the target
target[Symbol.iterator] = function* range() {
yield [ RANGER_SYMBOL, target, getTempRangeMethodSymbol(target, this, rangeFunc) ]
}
}
// do one time initialisation (set up the toPrimitive trick on Array)
function init() {
// set up our 'toPrimitive' method on the Array prototype
Array.prototype[Symbol.toPrimitive] = function (hint) {
// try to leave as much default behaviour alone as possible
if (hint === 'default') return this.toString()
if (hint === 'number') return Number(this.toString())
if (!isValidRangerArray(this)) return this.toString()
// store any additional params we will be passing to the range function
paramsStore[this[0][2]] = this.slice(1)
// return the symbol that 'names' the method on the target object
return this[0][2]
}
}
// create the one-time temporary method for the target, attach it, and return symbol 'name' for the method
function getTempRangeMethodSymbol(target, rangeEndValue, func) {
const sym = Symbol('ranger temp method')
const get = function() {
let res = func.call(this, this, rangeEndValue, ...paramsStore[sym])
delete target[sym]
delete paramsStore[sym]
return res
}
Object.defineProperty(target, sym, { configurable: true, get })
return sym
}
// check if given array is a 'Ranger' array
const isValidRangerArray = ([[rangerSym, target, methodSym]]) =>
rangerSym === RANGER_SYMBOL && target.hasOwnProperty(methodSym)
// sample usage - range function for numbers, with optional step size
// e.g. 27[[...42]] 1[[...3, 0.5]]
export function initNumberRangeSyntax() {
attach(Number.prototype, (start, end, stepSize = 1) => {
const absStep = stepSize<0 ? Math.abs(stepSize) : stepSize
const step = start<=end ? absStep : -absStep
let arr = [], i, d = end > start
for (i=+start; d ? i<=end : i>=end; i+=step) arr.push(i)
return arr
})
}
// sample usage - range generator function for numbers, with optional step size
// e.g. 27[[...42]] 1[[...3, 0.5]]
export function initNumberRangeSyntaxGenerator() {
attach(Number.prototype, function* (start, end, stepSize = 1) {
const absStep = stepSize<0 ? Math.abs(stepSize) : stepSize
const step = start<=end ? absStep : -absStep
let arr = [], i, d = end > start
for (i=+start; d ? i<=end : i>=end; i+=step) yield i
})
}