1 | import {extend} from './utils/objects'
|
2 | import {VALUE, ERROR, ANY, END} from './constants'
|
3 | import {Dispatcher, callSubscriber} from './dispatcher'
|
4 | import {findByPred} from './utils/collections'
|
5 |
|
6 | function Observable() {
|
7 | this._dispatcher = new Dispatcher()
|
8 | this._active = false
|
9 | this._alive = true
|
10 | this._activating = false
|
11 | this._logHandlers = null
|
12 | this._spyHandlers = null
|
13 | }
|
14 |
|
15 | extend(Observable.prototype, {
|
16 | _name: 'observable',
|
17 |
|
18 | _onActivation() {},
|
19 | _onDeactivation() {},
|
20 |
|
21 | _setActive(active) {
|
22 | if (this._active !== active) {
|
23 | this._active = active
|
24 | if (active) {
|
25 | this._activating = true
|
26 | this._onActivation()
|
27 | this._activating = false
|
28 | } else {
|
29 | this._onDeactivation()
|
30 | }
|
31 | }
|
32 | },
|
33 |
|
34 | _clear() {
|
35 | this._setActive(false)
|
36 | this._dispatcher.cleanup()
|
37 | this._dispatcher = null
|
38 | this._logHandlers = null
|
39 | },
|
40 |
|
41 | _emit(type, x) {
|
42 | switch (type) {
|
43 | case VALUE:
|
44 | return this._emitValue(x)
|
45 | case ERROR:
|
46 | return this._emitError(x)
|
47 | case END:
|
48 | return this._emitEnd()
|
49 | }
|
50 | },
|
51 |
|
52 | _emitValue(value) {
|
53 | if (this._alive) {
|
54 | this._dispatcher.dispatch({type: VALUE, value})
|
55 | }
|
56 | },
|
57 |
|
58 | _emitError(value) {
|
59 | if (this._alive) {
|
60 | this._dispatcher.dispatch({type: ERROR, value})
|
61 | }
|
62 | },
|
63 |
|
64 | _emitEnd() {
|
65 | if (this._alive) {
|
66 | this._alive = false
|
67 | this._dispatcher.dispatch({type: END})
|
68 | this._clear()
|
69 | }
|
70 | },
|
71 |
|
72 | _on(type, fn) {
|
73 | if (this._alive) {
|
74 | this._dispatcher.add(type, fn)
|
75 | this._setActive(true)
|
76 | } else {
|
77 | callSubscriber(type, fn, {type: END})
|
78 | }
|
79 | return this
|
80 | },
|
81 |
|
82 | _off(type, fn) {
|
83 | if (this._alive) {
|
84 | let count = this._dispatcher.remove(type, fn)
|
85 | if (count === 0) {
|
86 | this._setActive(false)
|
87 | }
|
88 | }
|
89 | return this
|
90 | },
|
91 |
|
92 | onValue(fn) {
|
93 | return this._on(VALUE, fn)
|
94 | },
|
95 | onError(fn) {
|
96 | return this._on(ERROR, fn)
|
97 | },
|
98 | onEnd(fn) {
|
99 | return this._on(END, fn)
|
100 | },
|
101 | onAny(fn) {
|
102 | return this._on(ANY, fn)
|
103 | },
|
104 |
|
105 | offValue(fn) {
|
106 | return this._off(VALUE, fn)
|
107 | },
|
108 | offError(fn) {
|
109 | return this._off(ERROR, fn)
|
110 | },
|
111 | offEnd(fn) {
|
112 | return this._off(END, fn)
|
113 | },
|
114 | offAny(fn) {
|
115 | return this._off(ANY, fn)
|
116 | },
|
117 |
|
118 | observe(observerOrOnValue, onError, onEnd) {
|
119 | const _this = this
|
120 | let closed = false
|
121 |
|
122 | const observer = !observerOrOnValue || typeof observerOrOnValue === 'function'
|
123 | ? {value: observerOrOnValue, error: onError, end: onEnd}
|
124 | : observerOrOnValue
|
125 |
|
126 | const handler = function(event) {
|
127 | if (event.type === END) {
|
128 | closed = true
|
129 | }
|
130 | if (event.type === VALUE && observer.value) {
|
131 | observer.value(event.value)
|
132 | } else if (event.type === ERROR && observer.error) {
|
133 | observer.error(event.value)
|
134 | } else if (event.type === END && observer.end) {
|
135 | observer.end(event.value)
|
136 | }
|
137 | }
|
138 |
|
139 | this.onAny(handler)
|
140 |
|
141 | return {
|
142 | unsubscribe() {
|
143 | if (!closed) {
|
144 | _this.offAny(handler)
|
145 | closed = true
|
146 | }
|
147 | },
|
148 | get closed() {
|
149 | return closed
|
150 | },
|
151 | }
|
152 | },
|
153 |
|
154 |
|
155 | _ofSameType(A, B) {
|
156 | return A.prototype.getType() === this.getType() ? A : B
|
157 | },
|
158 |
|
159 | setName(sourceObs , selfName) {
|
160 | this._name = selfName ? `${sourceObs._name}.${selfName}` : sourceObs
|
161 | return this
|
162 | },
|
163 |
|
164 | log(name = this.toString()) {
|
165 | let isCurrent
|
166 | let handler = function(event) {
|
167 | let type = `<${event.type}${isCurrent ? ':current' : ''}>`
|
168 | if (event.type === END) {
|
169 | console.log(name, type)
|
170 | } else {
|
171 | console.log(name, type, event.value)
|
172 | }
|
173 | }
|
174 |
|
175 | if (this._alive) {
|
176 | if (!this._logHandlers) {
|
177 | this._logHandlers = []
|
178 | }
|
179 | this._logHandlers.push({name: name, handler: handler})
|
180 | }
|
181 |
|
182 | isCurrent = true
|
183 | this.onAny(handler)
|
184 | isCurrent = false
|
185 |
|
186 | return this
|
187 | },
|
188 |
|
189 | offLog(name = this.toString()) {
|
190 | if (this._logHandlers) {
|
191 | let handlerIndex = findByPred(this._logHandlers, obj => obj.name === name)
|
192 | if (handlerIndex !== -1) {
|
193 | this.offAny(this._logHandlers[handlerIndex].handler)
|
194 | this._logHandlers.splice(handlerIndex, 1)
|
195 | }
|
196 | }
|
197 |
|
198 | return this
|
199 | },
|
200 |
|
201 | spy(name = this.toString()) {
|
202 | let handler = function(event) {
|
203 | let type = `<${event.type}>`
|
204 | if (event.type === END) {
|
205 | console.log(name, type)
|
206 | } else {
|
207 | console.log(name, type, event.value)
|
208 | }
|
209 | }
|
210 | if (this._alive) {
|
211 | if (!this._spyHandlers) {
|
212 | this._spyHandlers = []
|
213 | }
|
214 | this._spyHandlers.push({name: name, handler: handler})
|
215 | this._dispatcher.addSpy(handler)
|
216 | }
|
217 | return this
|
218 | },
|
219 |
|
220 | offSpy(name = this.toString()) {
|
221 | if (this._spyHandlers) {
|
222 | let handlerIndex = findByPred(this._spyHandlers, obj => obj.name === name)
|
223 | if (handlerIndex !== -1) {
|
224 | this._dispatcher.removeSpy(this._spyHandlers[handlerIndex].handler)
|
225 | this._spyHandlers.splice(handlerIndex, 1)
|
226 | }
|
227 | }
|
228 | return this
|
229 | },
|
230 | })
|
231 |
|
232 |
|
233 | Observable.prototype.toString = function() {
|
234 | return `[${this._name}]`
|
235 | }
|
236 |
|
237 | export default Observable
|