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 =
|
123 | !observerOrOnValue || typeof observerOrOnValue === 'function'
|
124 | ? {value: observerOrOnValue, error: onError, end: onEnd}
|
125 | : observerOrOnValue
|
126 |
|
127 | const handler = function(event) {
|
128 | if (event.type === END) {
|
129 | closed = true
|
130 | }
|
131 | if (event.type === VALUE && observer.value) {
|
132 | observer.value(event.value)
|
133 | } else if (event.type === ERROR && observer.error) {
|
134 | observer.error(event.value)
|
135 | } else if (event.type === END && observer.end) {
|
136 | observer.end(event.value)
|
137 | }
|
138 | }
|
139 |
|
140 | this.onAny(handler)
|
141 |
|
142 | return {
|
143 | unsubscribe() {
|
144 | if (!closed) {
|
145 | _this.offAny(handler)
|
146 | closed = true
|
147 | }
|
148 | },
|
149 | get closed() {
|
150 | return closed
|
151 | },
|
152 | }
|
153 | },
|
154 |
|
155 |
|
156 | _ofSameType(A, B) {
|
157 | return A.prototype.getType() === this.getType() ? A : B
|
158 | },
|
159 |
|
160 | setName(sourceObs , selfName) {
|
161 | this._name = selfName ? `${sourceObs._name}.${selfName}` : sourceObs
|
162 | return this
|
163 | },
|
164 |
|
165 | log(name = this.toString()) {
|
166 | let isCurrent
|
167 | let handler = function(event) {
|
168 | let type = `<${event.type}${isCurrent ? ':current' : ''}>`
|
169 | if (event.type === END) {
|
170 | console.log(name, type)
|
171 | } else {
|
172 | console.log(name, type, event.value)
|
173 | }
|
174 | }
|
175 |
|
176 | if (this._alive) {
|
177 | if (!this._logHandlers) {
|
178 | this._logHandlers = []
|
179 | }
|
180 | this._logHandlers.push({name: name, handler: handler})
|
181 | }
|
182 |
|
183 | isCurrent = true
|
184 | this.onAny(handler)
|
185 | isCurrent = false
|
186 |
|
187 | return this
|
188 | },
|
189 |
|
190 | offLog(name = this.toString()) {
|
191 | if (this._logHandlers) {
|
192 | let handlerIndex = findByPred(this._logHandlers, obj => obj.name === name)
|
193 | if (handlerIndex !== -1) {
|
194 | this.offAny(this._logHandlers[handlerIndex].handler)
|
195 | this._logHandlers.splice(handlerIndex, 1)
|
196 | }
|
197 | }
|
198 |
|
199 | return this
|
200 | },
|
201 |
|
202 | spy(name = this.toString()) {
|
203 | let handler = function(event) {
|
204 | let type = `<${event.type}>`
|
205 | if (event.type === END) {
|
206 | console.log(name, type)
|
207 | } else {
|
208 | console.log(name, type, event.value)
|
209 | }
|
210 | }
|
211 | if (this._alive) {
|
212 | if (!this._spyHandlers) {
|
213 | this._spyHandlers = []
|
214 | }
|
215 | this._spyHandlers.push({name: name, handler: handler})
|
216 | this._dispatcher.addSpy(handler)
|
217 | }
|
218 | return this
|
219 | },
|
220 |
|
221 | offSpy(name = this.toString()) {
|
222 | if (this._spyHandlers) {
|
223 | let handlerIndex = findByPred(this._spyHandlers, obj => obj.name === name)
|
224 | if (handlerIndex !== -1) {
|
225 | this._dispatcher.removeSpy(this._spyHandlers[handlerIndex].handler)
|
226 | this._spyHandlers.splice(handlerIndex, 1)
|
227 | }
|
228 | }
|
229 | return this
|
230 | },
|
231 | })
|
232 |
|
233 |
|
234 | Observable.prototype.toString = function() {
|
235 | return `[${this._name}]`
|
236 | }
|
237 |
|
238 | export default Observable
|