-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwraptor.js
112 lines (91 loc) · 3.14 KB
/
wraptor.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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
(function(root, factory) {
if(typeof define === 'function' && define.amd) {
define([], factory)
} else if (typeof module === 'object' && module.exports) {
module.exports = factory()
} else {
root['wraptor'] = factory()
}
}(this, function() {
const slice = Array.prototype.slice
function isFunction(fn) {
return (fn && typeof fn === 'function')
}
function notifyNext(x, o) { o.next(x) }
function notifyError(err, o) {
if(isFunction(o.error)) {
o.error(err)
} else {
throw err
}
}
function partial(fn, args) {
return Function.bind.apply(fn, [null].concat(args))
}
function applyCurry(fn, arg) {
if(!isFunction(fn)) { return fn }
return fn.length > 1 ? fn.bind(null, arg) : fn.call(null, arg)
}
// Main entry point for the lib.
return function wraptor(fn) {
// We need a function for this to work.
if(!(isFunction(fn))) {
throw new TypeError('Must provide a function to wrap')
}
// remove any binding and only access function once
const safeFn = fn.bind(null)
// cache number of arguments for partial detection
const argsLength = safeFn.length
// partially apply ObserverFunction for returning partials
const partialFunc = partial.bind(null, ObservableFunction)
// cached list of subscribers
const subs = []
function subscribe(o) {
// Bare min is a next function on the provided Observer
if(!(o && isFunction(o.next))) {
throw new TypeError('Invalid Observer passed to subscribe')
}
// Add the Observer to the list of subscribers
if(subs.indexOf(o) === -1) { subs.push(o) }
// Return a Subcription object
return {
closed() { return false },
unsubscribe() {
const idx = subs.indexOf(o)
if(idx !== -1) { subs.splice(idx, 1) }
}
}
}
function ObservableFunction() {
// arraylike args to Array
const args = slice.call(arguments)
// check for partial application
// return a partially applied Observer Function
if(args.length < argsLength) {
return partialFunc(args)
}
try {
const _val = (args.length === argsLength)
? safeFn.apply(null, args) // Fully applied, run function
: args.reduce(applyCurry, safeFn) // Possible curry, fold arguments
// If we need more arguments, return partial ObserverFunction
if(isFunction(_val)) { return partialFunc(args) }
// Notify all subscribers with result, only if we are fully applied
subs.forEach(notifyNext.bind(null, _val))
// return the final value for fully applied function
return _val
} catch(e) {
// Notify subscribers on error channel,
// or rethrow if we do not have anyone listening
if(subs.length) {
subs.forEach(notifyError.bind(null, e))
} else { throw err }
}
}
// Provide subscribe interface on Symbol.observable
ObservableFunction[Symbol.observable] = () => ({ subscribe })
// Provide subscribe method on function
ObservableFunction.subscribe = subscribe
return ObservableFunction
}
}))