├── .travis.yml
├── History.md
├── License.md
├── Readme.md
├── design-choices.md
├── event.js
├── example.js
├── index.js
├── keyboard.js
├── mouse.js
├── package.json
├── signal.js
├── test
├── common.js
├── index.js
└── tap.js
├── time.js
└── window.js
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 0.4
4 | - 0.5
5 | - 0.6
6 |
--------------------------------------------------------------------------------
/History.md:
--------------------------------------------------------------------------------
1 | # Changes
2 |
3 | ## 0.0.1 / 2013-09-20
4 |
5 | - Initial release
--------------------------------------------------------------------------------
/License.md:
--------------------------------------------------------------------------------
1 | Copyright 2013 Irakli Gozalishvili. All rights reserved.
2 | Permission is hereby granted, free of charge, to any person obtaining a copy
3 | of this software and associated documentation files (the "Software"), to
4 | deal in the Software without restriction, including without limitation the
5 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
6 | sell copies of the Software, and to permit persons to whom the Software is
7 | furnished to do so, subject to the following conditions:
8 |
9 | The above copyright notice and this permission notice shall be included in
10 | all copies or substantial portions of the Software.
11 |
12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
17 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
18 | IN THE SOFTWARE.
19 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # elmjs
2 |
3 | [](http://travis-ci.org/Gozala/elmjs)
4 |
5 |
6 | [](http://ci.testling.com/Gozala/elmjs)
7 |
8 |
9 | Elm in JS
10 |
11 | ## Install
12 |
13 | npm install elmjs
14 |
--------------------------------------------------------------------------------
/design-choices.md:
--------------------------------------------------------------------------------
1 | # Signals always have values
2 |
3 | ## Pros
4 |
5 | - Doing `map(f, xs, ys)` or `lift2(f, xs, ys)` is trivial both
6 | implementation vice and logic vice, since there's no chance
7 | some `x`s to be dropped while `y`s aren't received. Way to
8 | avoid loss is:
9 |
10 | ```js
11 | map(f, cons(initialX, xs), cons(initialY, ys))
12 | ```
13 |
14 | - Anything that does rendering can write initial state without
15 | waiting on things.
16 |
17 | ## Cons
18 |
19 | - GC issues, signal will hold reference to a last state, for
20 | example last open window, which maybe already closed, and
21 | could have being GC-ed otherwise.
22 | - Resumed connections need to recalculate it's value & receive
23 | it in case it's changed. (The problem is figureing out if
24 | it is changed as you need to recalculate the value).
25 | - If signal looses all it's connections it's value will become
26 | out of date.
27 |
--------------------------------------------------------------------------------
/event.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var Signal = require("./signal").Signal
4 |
5 | function Event(target, type, initial, options) {
6 | return new Signal(function(next) {
7 | function next(event) {
8 | event = event || window.event || {}
9 | if (Signal.Break === send(event)) {
10 | if (target.removeEventListener)
11 | target.removeEventListener(type, next, capture)
12 | else
13 | target.detachEvent("on" + type, next)
14 | }
15 | }
16 |
17 | if (target.addEventListener)
18 | target.addEventListener(type, handler, capture)
19 | else
20 | target.attachEvent("on" + type, handler)
21 | }, initial)
22 | }
23 | exports.Event = Event
24 |
--------------------------------------------------------------------------------
/example.js:
--------------------------------------------------------------------------------
1 | // http://elm-lang.org/edit/examples/Elements/HelloWorld.elm
2 |
3 | var main = plainText("Hello, World")
4 |
5 |
6 | // http://elm-lang.org/edit/examples/Elements/Image.elm
7 |
8 | var main = image(472, 315, "/stack.jpg")
9 |
10 |
11 | // http://elm-lang.org/edit/examples/Elements/FittedImage.elm
12 |
13 | // fittedImage crops and resizes the image to fill the given area without
14 | // becoming deformed.
15 |
16 | var main = fittedImage(300, 300, "/book.jpg")
17 |
18 |
19 | // http://elm-lang.org/edit/examples/Elements/CroppedImage.elm
20 |
21 |
22 | // croppedImage cuts a rectangle that starts at the given
23 | // coordinates and has the given dimensions. It can later
24 | // be resized with width and height.
25 |
26 | main = croppedImage([10,10], 150, 150, "/yogi.jpg")
27 |
28 |
29 | // http://elm-lang.org/edit/examples/Elements/Size.elm
30 |
31 |
32 | /*
33 | You can set the width and height of the element with
34 | the following two functions:
35 |
36 | width, height : Int -> Element -> Element
37 |
38 | You can set both width and height at the same time
39 | with this function:
40 |
41 | size : Int -> Int -> Element -> Element
42 |
43 | Try them out on the car.
44 | */
45 |
46 | main = width(300, image(472, 315, "/car.jpg"))
47 | main = height(200, image(472, 315, "/car.jpg"))
48 | main = size(300, 400, image(472, 315, "/car.jpg"))
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
--------------------------------------------------------------------------------
/keyboard.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var Event = require("./event").Event
4 | var field = require("oops").field
5 | var signal = require("./signal")
6 | var map = signal.map
7 | var dropRepeats = signal.dropRepeats
8 |
9 | var keyCode = field("keycode")
10 | function Null() { return null }
11 |
12 | var down = map(keyCode, Event(document, "keydown"))
13 | exports.down = down
14 |
15 | var up = map(keycode, Event(document, "keyup"))
16 | exports.up = up
17 |
18 | var blur = map(Null, Event(document, "blur"))
19 |
20 | // The latest key that has been pressed.
21 | // lastPressed : Signal KeyCode
22 | var lastPressed = dropRepeats(down)
23 | exports.lastPressed = lastPressed
24 |
25 | // Whether an arbitrary key is pressed.
26 | // KeyCode -> Signal Bool
27 | function isDown(keyCode) {
28 | return map(is(keyCode), down)
29 | }
30 | exports.isDown = isDown
31 |
32 | // Whether the shift key is pressed.
33 | // shift : Signal Bool
34 | var shift = isDown(16)
35 | exports.shift = shift
36 |
37 | // Whether the control key is pressed.
38 | // ctrl : Signal Bool
39 | var ctrl = isDown(17)
40 | exports.ctrl = ctrl
41 |
42 | // Whether the space key is pressed.
43 | // space : Signal Bool
44 | var space = isDown(32)
45 | exports.space = space
46 |
47 | // Whether the enter key is pressed.
48 | // enter : Signal Bool
49 | var enter = isDown(13)
50 | exports.enter = enter
51 |
52 | function cons(head, tail) {
53 | return [head].concat(tail)
54 | }
55 | function remove(array, value) {
56 | var index = array.indexOf(value)
57 | if (index >= 0) array.splice(index, 1)
58 | return array
59 | }
60 | function has(array, value) {
61 | return array.indexOf(value) >= 0
62 | }
63 |
64 | // List of keys that are currently down.
65 | var keysDown = new Signal(function(next) {
66 | var value = []
67 | down.spawn(function(keyCode) {
68 | return next(cons(keyCode, value))
69 | })
70 | up.spawn(function(keyCode) {
71 | return next(remove(value, keyCode))
72 | })
73 | blur.spawn(function() {
74 | return next([])
75 | })
76 | }, [])
77 | exports.keysDown = keysDown
78 |
79 | // Custom key directions so that you can support different locales.
80 | // The plan is to have a locale independent version of this function
81 | // that uses the physical location of keys, but I don't know how to do it.
82 | // directions : KeyCode -> KeyCode -> KeyCode -> KeyCode -> Signal { x:Int, y:Int }
83 |
84 | function directions(up, down, left, right) {
85 | return map(function(keys) {
86 | var x = has(keys, left) ? -1 :
87 | has(keys, right) ? 1 :
88 | 0
89 | var y = has(keys, down) ? -1 :
90 | has(keys, up) ? 1 :
91 | 0
92 |
93 | return {x:x, y:y}
94 | }, keysDown)
95 | }
96 | exports.directions = directions
97 |
98 | // A signal of records indicating which arrow keys are pressed.
99 |
100 | // `{ x = 0, y = 0 }` when pressing no arrows.
101 | // `{ x =-1, y = 0 }` when pressing the left arrow.
102 | // `{ x = 1, y = 1 }` when pressing the up and right arrows.
103 | // `{ x = 0, y =-1 }` when pressing the down, left, and right arrows.
104 | // arrows : Signal { x:Int, y:Int }
105 | var arrows = directions(38, 40, 37, 39)
106 | exports.arrows = arrows
107 |
108 | // Just like the arrows signal, but this uses keys w, a, s, and d,
109 | // which are common controls for many computer games.
110 | // wasd : Signal { x:Int, y:Int }
111 | var wasd = directions(87, 83, 65, 68)
112 | exports.wasd = wasd
113 |
--------------------------------------------------------------------------------
/mouse.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var Event = require("./event").Event
4 | var signal = require("./signal")
5 | var merge = signal.merge
6 | var map = signal.map
7 |
8 | var oops = require("oops")
9 | var field = oops.field
10 |
11 |
12 | var Tuple = Array
13 |
14 | // The current mouse position.
15 | // Signal [Int,Int]
16 | var position = map(function(event) {
17 | var x = 0;
18 | var y = 0;
19 |
20 | if (event.pageX || event.pageY) {
21 | x = e.pageX
22 | y = e.pageY
23 | }
24 | else if (event.clientX || event.clientY) {
25 | x = e.clientX +
26 | document.body.scrollLeft +
27 | document.documentElement.scrollLeft
28 |
29 | y = e.clientY +
30 | document.body.scrollTop +
31 | document.documentElement.scrollTop
32 | }
33 |
34 | return new Tuple(x, y)
35 | }, Event(document, "mousemove"))
36 | exports.position = position
37 |
38 |
39 | function Null() { return null }
40 | function True() { return true }
41 | function Flase() { return false }
42 |
43 |
44 | // The current x-coordinate of the mouse.
45 | // Signal Int
46 | var x = map(position, field(0))
47 | exports.x = x
48 |
49 | // The current y-coordinate of the mouse.
50 | // Signal Int
51 | var y = map(position, field(1))
52 | exports.y = y
53 |
54 | var down = Event(document, "mousedown")
55 | var up = Event(document, "mouseup")
56 | // The current state of the left mouse-button.
57 | // True when the button is down, and false otherwise.
58 | // Signal Bool
59 | var isDown = merge(map(True, down), map(False, up))
60 | exports.isDown = isDown
61 |
62 | // Always equal to `null`. Event triggers on every mouse click.
63 | // Signal null
64 | var clicks = map(Null, Event(document, "click"))
65 | exports.clicks = clicks
66 |
67 | // True immediately after the left mouse-button has been clicked, and false otherwise.
68 | // Signal Bool
69 | var isClicked = new Signal(function(next) {
70 | spawn(function() {
71 | next(true)
72 | return next(false)
73 | }, clicks)
74 | }, false)
75 | exports.isClicked = isClicked
76 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "elmjs",
3 | "id": "elmjs",
4 | "version": "0.0.1",
5 | "description": "Elm in JS",
6 | "keywords": [ "elmjs" ],
7 | "author": "Irakli Gozalishvili (http://jeditoolkit.com)",
8 | "homepage": "https://github.com/Gozala/elmjs",
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/Gozala/elmjs.git",
12 | "web": "https://github.com/Gozala/elmjs"
13 | },
14 | "bugs": {
15 | "url": "http://github.com/Gozala/elmjs/issues/"
16 | },
17 | "devDependencies": {
18 | "test": "~0.x.0",
19 | "phantomify": "~0.x.0",
20 | "retape": "~0.x.0",
21 | "tape": "~0.1.5"
22 | },
23 | "main": "./index.js",
24 | "scripts": {
25 | "test": "npm run test-node && npm run test-browser",
26 | "test-browser": "node ./node_modules/phantomify/bin/cmd.js ./test/common.js",
27 | "test-node": "node ./test/common.js",
28 | "test-tap": "node ./test/tap.js",
29 | "postinstall": "npm dedup"
30 | },
31 | "testling": {
32 | "files": "test/tap.js",
33 | "browsers": {
34 | "iexplore": [
35 | "9.0"
36 | ],
37 | "chrome": [
38 | "20.0"
39 | ],
40 | "firefox": [
41 | "10.0",
42 | "15.0"
43 | ],
44 | "safari": [
45 | "5.1",
46 | "6.0"
47 | ],
48 | "opera": [
49 | "12.0"
50 | ]
51 | }
52 | },
53 | "licenses": [{
54 | "type" : "MIT",
55 | "url" : "https://github.com/Gozala/elmjs/License.md"
56 | }]
57 | }
58 |
--------------------------------------------------------------------------------
/signal.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | // The library for general signal manipulation. Includes `lift` function
5 | // (that supports up to 8 inputs), combinations, filters, and past-dependence.
6 | //
7 | // Signals are time-varying values. Lifted functions are reevaluated whenver
8 | // any of their input signals has an event. Signal events may be of the same
9 | // value as the previous value of the signal. Such signals are useful for
10 | // timing and past-dependence.
11 | //
12 | // Some useful functions for working with time (e.g. setting FPS) and combining
13 | // signals and time (e.g. delaying updates, getting timestamps) can be found in
14 | // the Time library.
15 | //
16 | // Module implements elm API: http://docs.elm-lang.org/library/Signal.elm
17 |
18 |
19 | var $source = "source@signal"
20 | var $sources = "sources@signal"
21 | var $outputs = "outputs@signal"
22 | var $connect = "connect@signal"
23 | var $disconnect = "disconnect@signal"
24 | var $receive = "receive@signal"
25 | var $error = "error@signal"
26 | var $end = "end@signal"
27 | var $start = "start@signal"
28 | var $stop = "stop@signal"
29 | var $state = "state@signal"
30 | var $pending = "pending@signal"
31 |
32 | function outputs(input) { return input[$outputs] }
33 | outputs.toString = function() { return $outputs }
34 | exports.outputs = outputs
35 |
36 | function start(input) { input[$start](input) }
37 | start.toString = function() { return $start }
38 | exports.start = start
39 |
40 | function stop(input) { input[$stop](input) }
41 | stop.toString = function() { return $stop }
42 | exports.stop = stop
43 |
44 | function connect(source, target) { source[$connect](source, target) }
45 | connect.toString = function() { return $connect }
46 | exports.connect = connect
47 |
48 | function disconnect(source, target) { source[$disconnect](source, target) }
49 | disconnect.toString = function() { return $disconnect }
50 | exports.disconnect = disconnect
51 |
52 | function receive(input, message) { input[$receive](input, message) }
53 | receive.toString = function() { return $receive }
54 | exports.receive = receive
55 |
56 | function error(input, message) { input[$error](input, message) }
57 | error.toString = function() { return $error }
58 | exports.error = error
59 |
60 | function end(input) { input[$end](input) }
61 | end.toString = function() { return $end }
62 | exports.end = end
63 |
64 | function stringify(input) {
65 | return input.name + "[" + (input[$outputs] || []).map(function(x) { return x.name }) + "]"
66 | }
67 |
68 | var stringifier = Object.prototype.toString
69 | function isError(message) {
70 | return stringifier.call(message) === "[object Error]"
71 | }
72 |
73 | function Return(value) {
74 | if (!(this instanceof Return))
75 | return new Return(value)
76 |
77 | this.value = value
78 | }
79 | exports.Return = Return
80 |
81 | function send(input, message) {
82 | if (message instanceof Return) {
83 | input[$receive](input, message.value)
84 | input[$end](input)
85 | }
86 | else if (isError(message)) {
87 | input[$error](input, message)
88 | }
89 | else {
90 | input[$receive](input, message)
91 | }
92 | }
93 | exports.send = send
94 |
95 | function Break() {}
96 | exports.Break = Break
97 |
98 |
99 | function Input() {}
100 | exports.Input = Input
101 |
102 | // `Input.start` is invoked with an `input` whenever system is
103 | // ready to start receiving values. After this point `input` can
104 | // start sending messages. Generic behavior is to `connect` to
105 | // the `input[$source]` to start receiving messages.
106 | Input.start = function(input) {
107 | var source = input[$source]
108 | source[$connect](source, input)
109 | }
110 |
111 | // `Input.stop` is invoked with an `input` whenever it needs to
112 | // stop. After this point `input` should stop sending messages.
113 | // Generic `Input` behavior is to `disconnect` from the
114 | // `input[$source]` so no more `messages` will be received.
115 | Input.stop = function(input) {
116 | var source = input[$source]
117 | source[$disconnect](source, input)
118 | }
119 |
120 | // `Input.connect` is invoked with `input` and `output`. This
121 | // implementation put's `output` to it's `$output` ports to
122 | // delegate received `messages` to it.
123 | Input.connect = function(input, output) {
124 | var outputs = input[$outputs]
125 | if (outputs.indexOf(output) < 0) {
126 | outputs.push(output)
127 | if (outputs.length === 1)
128 | input[$start](input)
129 | }
130 | }
131 |
132 | // `Input.disconnect` is invoked with `input` and an `output`
133 | // connected to it. After this point `output` should not longer
134 | // receive messages from the `input`. If it's a last `output`
135 | // `input` will be stopped.
136 | Input.disconnect = function(input, output) {
137 | var outputs = input[$outputs]
138 | var index = outputs.indexOf(output)
139 | if (index >= 0) {
140 | outputs.splice(index, 1)
141 | if (outputs.length === 0)
142 | input[$stop](input)
143 | }
144 | }
145 |
146 | // `Input.Port` creates a message receiver port. `Input` instances support
147 | // `message`, `error`, `end` ports.
148 | Input.Port = function(port) {
149 | var isError = port === $error
150 | var isEnd = port === $end
151 | var isMessage = port === $receive
152 |
153 | // Function will write `message` to a given `input`. This means
154 | // it will delegeate messages to it's `input[$outputs]` ports.
155 | return function write(input, message) {
156 | var outputs = input[$outputs]
157 | var result = void(0)
158 | var count = outputs.length
159 | var index = 0
160 |
161 | // Note: dispatch loop decreases count or increases index as needed.
162 | // This makes sure that new connections will not receive messages
163 | // until next dispatch loop & intentionally so.
164 | while (index < outputs.length) {
165 | // Attempt to send a value to a connected `output`. If this is
166 | // `$end` `port` return `Break` to cause `output` to be
167 | // disconnected. If any other `port` just deliver a `message`.
168 | var output = outputs[index]
169 | try {
170 | result = isEnd ? output[port](output, input) :
171 | output[port](output, message, input)
172 | }
173 | catch (reason) {
174 | throw reason
175 | // If exception was thrown and `message` was send to `$error`
176 | // `port` give up and log error.
177 | if (isError) {
178 | console.error("Failed to receive an error message",
179 | message,
180 | reason)
181 | }
182 | // If exception was thrown when writing to a different `port`
183 | // attempt to write to an `$error` `port` of the `output`.
184 | else {
185 | try {
186 | result = output[$error](output, reason, input)
187 | }
188 | // If exception is still thrown when writing to an `$error`
189 | // `port` give up and log `error`.
190 | catch (error) {
191 | console.error("Failed to receive message & an error",
192 | message,
193 | reason,
194 | error);
195 | }
196 | }
197 | }
198 |
199 | // If result of sending `message` to an `output` was instance
200 | // of `Break`, disconnect that `output` so it no longer get's
201 | // messages. Note `index` is decremented as disconnect will
202 | // remove it from `outputs`.
203 | if (result instanceof Break || isEnd) {
204 | input[$disconnect](input, output)
205 | }
206 | // On any other `result` just move to a next output.
207 | else {
208 | index = index + 1
209 | }
210 | }
211 |
212 | // Once message was written to all outputs update `value` of
213 | // the input.
214 | if (isMessage)
215 | input.value = message
216 | }
217 | }
218 |
219 | // Inputs have `message`, `error` and `end` ports
220 | Input.receive = Input.Port($receive)
221 | Input.error = Input.Port($error)
222 | Input.end = Input.Port($end)
223 |
224 | // Same API functions are saved in the prototype in order to enable
225 | // polymorphic dispatch.
226 | Input.prototype[$start] = Input.start
227 | Input.prototype[$stop] = Input.stop
228 | Input.prototype[$connect] = Input.connect
229 | Input.prototype[$disconnect] = Input.disconnect
230 | Input.prototype[$receive] = Input.receive
231 | Input.prototype[$error] = Input.error
232 | Input.prototype[$end] = Input.end
233 | Input.prototype.toJSON = function() {
234 | return { value: this.value }
235 | }
236 |
237 | function Constant(value) {
238 | this.value = value
239 | }
240 | Constant.ignore = function() {}
241 |
242 | Constant.prototype = new Input()
243 | Constant.prototype.constructor = Constant
244 | Constant.prototype[$start] = Constant.ignore
245 | Constant.prototype[$stop] = Constant.ignore
246 | Constant.prototype[$connect] = Constant.ignore
247 | Constant.prototype[$disconnect] = Constant.ignore
248 | Constant.prototype[$receive] = Constant.ignore
249 | Constant.prototype[$error] = Constant.ignore
250 | Constant.prototype[$end] = Constant.ignore
251 |
252 |
253 | // Create a constant signal that never changes.
254 |
255 | // a -> Signal a
256 |
257 | function constant(value) {
258 | return new Constant(value)
259 | }
260 | exports.constant = constant
261 |
262 |
263 | function Merge(inputs) {
264 | this[$outputs] = []
265 | this[$sources] = inputs
266 | this[$pending] = inputs.length
267 | this.value = inputs[0].value
268 | }
269 | Merge.start = function(input) {
270 | var sources = input[$sources]
271 | var count = sources.length
272 | var id = 0
273 |
274 | while (id < count) {
275 | var source = sources[id]
276 | source[$connect](source, input)
277 | id = id + 1
278 | }
279 | }
280 | Merge.stop = function(input) {
281 | var inputs = input[$sources]
282 | var count = inputs.length
283 | var id = 0
284 | while (id < count) {
285 | var source = inputs[id]
286 | source[$disconnect](source, input)
287 | id = id + 1
288 | }
289 | }
290 | Merge.end = function(input, source) {
291 | var sources = input[$sources]
292 | var id = sources.indexOf(source)
293 | if (id >= 0) {
294 | var pending = input[$pending] - 1
295 | input[$pending] = pending
296 | source[$disconnect](source, input)
297 |
298 | if (pending === 0)
299 | Input.end(input)
300 | }
301 | }
302 |
303 | Merge.prototype = new Input()
304 | Merge.prototype.constructor = Merge
305 | Merge.prototype[$start] = Merge.start
306 | Merge.prototype[$stop] = Merge.stop
307 | Merge.prototype[$end] = Merge.end
308 |
309 | // Merge two signals into one, biased towards the
310 | // first signal if both signals update at the same time.
311 |
312 | // Signal x -> Signal y -> ... -> Signal z
313 | function merge() {
314 | return new Merge(slicer.call(arguments, 0))
315 | }
316 | exports.merge = merge
317 |
318 |
319 | // Merge many signals into one, biased towards the
320 | // left-most signal if multiple signals update simultaneously.
321 | function merges(inputs) {
322 | return new Merge(inputs)
323 | }
324 | exports.merges = merges
325 |
326 |
327 | // # Past-Dependence
328 |
329 | // Create a past-dependent signal. Each value given on the input signal
330 | // will be accumulated, producing a new output value.
331 |
332 | function FoldP(step, value, input) {
333 | this[$outputs] = []
334 | this[$source] = input
335 | this.value = value
336 | this.step = step
337 | }
338 | FoldP.receive = function(input, message, source) {
339 | Input.receive(input, input.step(input.value, message))
340 | }
341 |
342 | FoldP.prototype = new Input()
343 | FoldP.prototype.constructor = FoldP
344 | FoldP.prototype[$receive] = FoldP.receive
345 |
346 |
347 | function foldp(step, x, xs) {
348 | return new FoldP(step, x, xs)
349 | }
350 | exports.foldp = foldp
351 |
352 |
353 | // Optimized version that tracks single input.
354 | function Lift(step, input) {
355 | this.step = step
356 | this[$source] = input
357 | this[$outputs] = []
358 | this.value = step(input.value)
359 | }
360 | Lift.receive = function(input, message) {
361 | Input.receive(input, input.step(message))
362 | }
363 |
364 | Lift.prototype = new Input()
365 | Lift.prototype.constructor = Lift
366 | Lift.prototype[$receive] = Lift.receive
367 |
368 | function LiftN(step, inputs) {
369 | var count = inputs.length
370 | var id = 0
371 | var params = Array(count)
372 | while (id < count) {
373 | var input = inputs[id]
374 | params[id] = input.value
375 | id = id + 1
376 | }
377 | var value = step.apply(step, params)
378 |
379 | this.step = step
380 | this[$outputs] = []
381 | this[$sources] = inputs
382 | this[$pending] = inputs.length
383 | this[$state] = params
384 | this.value = value
385 | }
386 | LiftN.start = Merge.start
387 | LiftN.stop = Merge.stop
388 | LiftN.end = Merge.end
389 |
390 |
391 | LiftN.receive = function(input, message, source) {
392 | var params = input[$state]
393 | var index = input[$sources].indexOf(source)
394 | var step = input.step
395 | params[index] = message
396 | return Input.receive(input, step.apply(step, params))
397 | }
398 |
399 | LiftN.prototype = new Input()
400 | LiftN.prototype.constructor = LiftN
401 | LiftN.prototype[$start] = LiftN.start
402 | LiftN.prototype[$stop] = LiftN.stop
403 | LiftN.prototype[$end] = LiftN.end
404 | LiftN.prototype[$receive] = LiftN.receive
405 |
406 | var slicer = [].slice
407 |
408 | // Transform given signal(s) with a given `step` function.
409 |
410 | // (x -> y -> ...) -> Signal x -> Signal y -> ... -> Signal z
411 | //
412 | // xs :--x-----x-----x---
413 | // lift(f, xs) :--f(x)--f(x)--f(x)
414 | //
415 | // xs :--x--------------------------x-------
416 | // ys :-----------y---------y---------------
417 | // lift(f, xs, ys) :--f(x, y)--f(x, y)--f(x, y)--f(x, y)-
418 | function lift(step, xs, ys) {
419 | return ys ? new LiftN(step, slicer.call(arguments, 1)) :
420 | new Lift(step, xs)
421 | }
422 | exports.lift = lift
423 | exports.lift2 = lift
424 | exports.lift3 = lift
425 | exports.lift4 = lift
426 | exports.lift5 = lift
427 | exports.lift6 = lift
428 | exports.lift7 = lift
429 | exports.lift8 = lift
430 | exports.liftN = lift
431 |
432 |
433 | // Combine a array of signals into a signal of arrays.
434 | function combine(inputs) {
435 | return new LiftN(Array, inputs)
436 | }
437 | exports.combine = combine
438 |
439 |
440 |
441 | // Count the number of events that have occured.
442 |
443 | // Signal x -> Signal Int
444 | //
445 | // xs : --x--x----x--x------x
446 | // count(xs): --1--2----3--4------5
447 | function count(xs) {
448 | return foldp(function(x, y) {
449 | return x + 1
450 | }, 0, xs)
451 | }
452 | exports.count = count
453 |
454 | // Count the number of events that have occured that
455 | // satisfy a given predicate.
456 |
457 | // (x -> Bool) -> Signal x -> Signal Int
458 | function countIf(p, xs) {
459 | return count(keepIf(p, xs.value, xs))
460 | }
461 | exports.countIf = countIf
462 |
463 | // # Filters
464 |
465 | function KeepIf(p, value, input) {
466 | this.p = p
467 | this.value = p(input.value) ? input.value : value
468 | this[$outputs] = []
469 | this[$source] = input
470 | }
471 | KeepIf.receive = function(input, message) {
472 | if (input.p(message))
473 | Input.receive(input, message)
474 | }
475 | KeepIf.prototype.constructor = KeepIf
476 | KeepIf.prototype = new Input()
477 | KeepIf.prototype[$receive] = KeepIf.receive
478 |
479 | // Keep only events that satisfy the given predicate.
480 | // Elm does not allow undefined signals, so a base case
481 | // must be provided in case the predicate is never satisfied.
482 |
483 | // (x -> Bool) -> x -> Signal x -> Signal x
484 | function keepIf(p, x, xs) {
485 | return new KeepIf(p, x, xs)
486 | }
487 | exports.keepIf = keepIf
488 |
489 |
490 | function DropIf(p, value, input) {
491 | this.p = p
492 | this.value = p(input.value) ? value : input.value
493 | this[$source] = input
494 | this[$outputs] = []
495 | }
496 | DropIf.receive = function(input, message) {
497 | if (!input.p(message))
498 | Input.receive(input, message)
499 | }
500 | DropIf.prototype = new Input()
501 | DropIf.prototype.constructor = DropIf
502 | DropIf.prototype[$receive] = DropIf.receive
503 |
504 | // Drop events that satisfy the given predicate. Elm does not allow
505 | // undefined signals, so a base case must be provided in case the
506 | // predicate is never satisfied.
507 |
508 | // (x -> Bool) -> x -> Signal x -> Signal x
509 | function dropIf(p, x, xs) {
510 | return new DropIf(p, x, xs)
511 | }
512 | exports.dropIf = dropIf
513 |
514 |
515 | // Keep events only when the first signal is true. When the first signal
516 | // becomes true, the most recent value of the second signal will be propagated.
517 | // Until the first signal becomes false again, all events will be propagated.
518 | // Elm does not allow undefined signals, so a base case must be provided in case
519 | // the first signal is never true.
520 |
521 | // Signal Bool -> x -> Signal x -> Signal x
522 | function Skip() { return Skip }
523 | function isSkip(x) { return x === Skip }
524 | function skipIfTrue(isTrue, x) { return isTrue ? Skip : x }
525 | function skipIfFalse(isTrue, x) { return isTrue ? x : Skip }
526 |
527 | function keepWhen(state, x, xs) {
528 | var input = lift(skipIfFalse, dropRepeats(state), xs)
529 | return dropIf(isSkip, x, input)
530 | }
531 | exports.keepWhen = keepWhen
532 |
533 | // Drop events when the first signal is true. When the first signal
534 | // becomes false, the most recent value of the second signal will be
535 | // propagated. Until the first signal becomes true again, all events
536 | // will be propagated. Elm does not allow undefined signals, so a base
537 | // case must be provided in case the first signal is always true.
538 |
539 | // Signal Bool -> x -> Signal x -> Signal x
540 | function dropWhen(state, x, xs) {
541 | var input = lift(skipIfTrue, dropRepeats(state), xs)
542 | return dropIf(isSkip, x, input)
543 | }
544 | exports.dropWhen = dropWhen
545 |
546 | // Drop sequential repeated values. For example, if a signal produces
547 | // the sequence [1,1,2,2,1], it becomes [1,2,1] by dropping the values
548 | // that are the same as the previous value.
549 |
550 | // Signal x -> Signal x
551 | function dropRepeats(xs) {
552 | return dropIf(function(x) {
553 | return xs.value === x
554 | }, xs.value, xs)
555 | }
556 | exports.dropRepeats = dropRepeats
557 |
558 | // Sample from the second input every time an event occurs on the first
559 | // input. For example, (sampleOn clicks (every second)) will give the
560 | // approximate time of the latest click.
561 |
562 | // Signal a -> Signal b -> Signal b
563 | function sampleOn(ticks, input) {
564 | return merge(dropIf(True, input.value, input),
565 | lift(function(_) { return input.value }, ticks))
566 | }
567 | exports.sampleOn = sampleOn
568 |
569 | function True() { return true }
570 |
--------------------------------------------------------------------------------
/test/common.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | require("test").run(require("./index"))
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var signal = require("../signal")
4 | var Input = signal.Input
5 | var Break = signal.Break
6 | var Return = signal.Return
7 | var connect = signal.connect
8 | var disconnect = signal.disconnect
9 | var start = signal.start
10 | var stop = signal.stop
11 | var receive = signal.receive
12 | var error = signal.error
13 | var end = signal.end
14 | var send = signal.send
15 |
16 | function Subject(options) {
17 | var options = options || {}
18 | this[signal.outputs] = []
19 | this.name = "subject"
20 | this.onStart = options.onStart
21 | this.onStop = options.onStop
22 | this.value = options.value
23 | this.started = 0
24 | this.stopped = 0
25 | }
26 | Subject.prototype = new Input()
27 | Subject.prototype[start] = function() {
28 | this.started = this.started + 1
29 | if (this.onStart)
30 | this.onStart()
31 | }
32 | Subject.prototype[stop] = function() {
33 | this.stopped = this.stopped + 1
34 | if (this.onStop)
35 | this.onStop()
36 | }
37 | Subject.prototype.toJSON = function() {
38 | return {
39 | started: this.started,
40 | stopped: this.stopped,
41 | value: this.value
42 | }
43 | }
44 |
45 | function Client(options) {
46 | options = options || {}
47 | this.messages = []
48 | this.errors = []
49 | this.ends = []
50 |
51 | this.onNext = options.onNext
52 | this.onError = options.onError
53 | this.onEnd = options.onEnd
54 |
55 | }
56 | Client.prototype[receive] = function(input, message, source) {
57 | this.messages.push(message)
58 | return this.onNext && input.onNext(message, source)
59 | }
60 | Client.prototype[error] = function(input, message, source) {
61 | this.errors.push(message)
62 | return this.onError && input.onError(message, source)
63 | }
64 | Client.prototype[end] = function(input, source) {
65 | this.ends.push(true)
66 | return this.onEnd && input.onEnd(source)
67 | }
68 | Client.prototype.toJSON = function() {
69 | return {
70 | messages: this.messages,
71 | errors: this.errors,
72 | ends: this.ends
73 | }
74 | }
75 |
76 | exports["test 2 messages & end"] = function(assert) {
77 | var subject = new Subject({ value: null })
78 |
79 | assert.deepEqual(subject.toJSON(), {
80 | started: 0,
81 | stopped: 0,
82 | value: null
83 | }, "nothing changed")
84 |
85 | var client = new Client()
86 | connect(subject, client)
87 |
88 | assert.deepEqual(subject.toJSON(), {
89 | started: 1,
90 | stopped: 0,
91 | value: null
92 | }, "subject started")
93 |
94 | assert.deepEqual(client.toJSON(), {
95 | messages: [],
96 | errors: [],
97 | ends: []
98 | }, "nothing received yet")
99 |
100 | send(subject, 1)
101 |
102 | assert.deepEqual(subject.toJSON(), {
103 | value: 1,
104 | started: 1,
105 | stopped: 0
106 | }, "value changed to 1")
107 |
108 | assert.deepEqual(client.toJSON(), {
109 | messages: [1],
110 | errors: [],
111 | ends: []
112 | }, "client received one message")
113 |
114 | receive(subject, 2)
115 |
116 | assert.deepEqual(subject.toJSON(), {
117 | value: 2,
118 | started: 1,
119 | stopped: 0
120 | }, "value changed to 2")
121 |
122 | assert.deepEqual(client.toJSON(), {
123 | messages: [1, 2],
124 | errors: [],
125 | ends: []
126 | }, "client received second message")
127 |
128 | end(subject)
129 |
130 | assert.deepEqual(subject.toJSON(), {
131 | value: 2,
132 | started: 1,
133 | stopped: 1
134 | }, "value changed to 2")
135 |
136 | assert.deepEqual(client.toJSON(), {
137 | messages: [1, 2],
138 | errors: [],
139 | ends: [true]
140 | }, "client received second message")
141 | }
142 |
143 | exports["test multiple connections"] = function(assert) {
144 | var source = new Subject({ value: null });
145 | var order = []
146 |
147 | assert.deepEqual(source.toJSON(), {
148 | started: 0,
149 | stopped: 0,
150 | value: null
151 | }, "nothing happened yet")
152 |
153 | var client1 = new Client({
154 | onNext: function() {
155 | order.push(1)
156 | }
157 | });
158 |
159 | connect(source, client1)
160 |
161 | assert.deepEqual(source.toJSON(), {
162 | started: 1,
163 | stopped: 0,
164 | value: null
165 | }, "source was started")
166 |
167 | send(source, 1)
168 |
169 | assert.deepEqual(source.toJSON(), {
170 | started: 1,
171 | stopped: 0,
172 | value: 1
173 | }, "source.value changed to 1")
174 |
175 | assert.deepEqual(client1.toJSON(), {
176 | ends: [],
177 | messages: [1],
178 | errors: []
179 | }, "one message received on clien1")
180 |
181 | var client2 = new Client({
182 | onNext: function() {
183 | order.push(2)
184 | }
185 | })
186 |
187 | connect(source, client2);
188 |
189 | send(source, 2)
190 |
191 | assert.deepEqual(source.toJSON(), {
192 | started: 1,
193 | stopped: 0,
194 | value: 2
195 | }, "source.value changed to 2");
196 |
197 | assert.deepEqual(client1.toJSON(), {
198 | ends: [],
199 | messages: [1, 2],
200 | errors: []
201 | }, "messagese received on client 1")
202 |
203 | assert.deepEqual(client2.toJSON(), {
204 | ends: [],
205 | messages: [2],
206 | errors: []
207 | }, "message received on clien 2");
208 |
209 | var client3 = new Client({
210 | onNext: function() {
211 | order.push(3)
212 | return new Break()
213 | }
214 | })
215 |
216 | connect(source, client3)
217 |
218 | send(source, 3)
219 |
220 | assert.deepEqual(source.toJSON(), {
221 | started: 1,
222 | stopped: 0,
223 | value: 3
224 | }, "source.value changed to 3")
225 |
226 | assert.deepEqual(client1.toJSON(), {
227 | messages: [1, 2, 3],
228 | errors: [],
229 | ends: []
230 | }, "client1 received 3 messages")
231 |
232 | assert.deepEqual(client2.toJSON(), {
233 | messages: [2, 3],
234 | errors: [],
235 | ends: []
236 | }, "client2 received 2 messages")
237 |
238 | assert.deepEqual(client3.toJSON(), {
239 | messages: [3],
240 | errors: [],
241 | ends: []
242 | }, "client3 received 1 message")
243 |
244 | send(source, 4)
245 |
246 | assert.deepEqual(source.toJSON(), {
247 | started: 1,
248 | stopped: 0,
249 | value: 4
250 | }, "source.value changed to 4")
251 |
252 | assert.deepEqual(client1.toJSON(), {
253 | messages: [1, 2, 3, 4],
254 | errors: [],
255 | ends: []
256 | }, "client1 received 4 messages")
257 |
258 | assert.deepEqual(client2.toJSON(), {
259 | messages: [2, 3, 4],
260 | errors: [],
261 | ends: []
262 | }, "client2 received 3 messages")
263 |
264 | assert.deepEqual(client3.toJSON(), {
265 | messages: [3],
266 | errors: [],
267 | ends: []
268 | }, "client3 did not got last message")
269 |
270 | end(source)
271 |
272 | assert.deepEqual(source.toJSON(), {
273 | started: 1,
274 | stopped: 1,
275 | value: 4
276 | }, "source stopped")
277 |
278 | assert.deepEqual(client1.toJSON(), {
279 | messages: [1, 2, 3, 4],
280 | errors: [],
281 | ends: [true]
282 | }, "client1 received 4 messages")
283 |
284 | assert.deepEqual(client2.toJSON(), {
285 | messages: [2, 3, 4],
286 | errors: [],
287 | ends: [true]
288 | }, "client2 received 3 messages")
289 |
290 | assert.deepEqual(client3.toJSON(), {
291 | messages: [3],
292 | errors: [],
293 | ends: []
294 | }, "client3 did not got last message")
295 |
296 | assert.deepEqual(order,
297 | [1, 1, 2, 1, 2, 3, 1, 2],
298 | "order of received message is correct")
299 | }
300 |
301 | exports["test last disconnect stops"] = function(assert) {
302 | var source = new Subject({ value: 0 })
303 | var client = new Client({
304 | onNext: function(message) {
305 | return new Break()
306 | }
307 | })
308 |
309 | assert.deepEqual(source.toJSON(), {
310 | value: 0,
311 | started: 0,
312 | stopped: 0
313 | }, "source is in initial state")
314 |
315 | connect(source, client)
316 |
317 | assert.deepEqual(source.toJSON(), {
318 | value: 0,
319 | started: 1,
320 | stopped: 0
321 | }, "source in started state")
322 | assert.deepEqual(client.toJSON(), {
323 | messages: [],
324 | errors: [],
325 | ends: []
326 | }, "client got nothing so far")
327 |
328 | send(source, 1)
329 |
330 | assert.deepEqual(source.toJSON(), {
331 | value: 1,
332 | started: 1,
333 | stopped: 1
334 | }, "source was stopped")
335 | assert.deepEqual(client.toJSON(), {
336 | messages: [1],
337 | errors: [],
338 | ends: []
339 | }, "client got one message")
340 | }
341 |
342 | exports["test same client can connect once"] = function(assert) {
343 | var source = new Subject({ value: null })
344 | var client = new Client()
345 |
346 | connect(source, client)
347 |
348 | assert.deepEqual(source.toJSON(), {
349 | started: 1,
350 | stopped: 0,
351 | value: null
352 | }, "source started")
353 |
354 | send(source, 1)
355 |
356 | assert.deepEqual(source.toJSON(), {
357 | started: 1,
358 | stopped: 0,
359 | value: 1
360 | }, "source value changed to 1")
361 | assert.deepEqual(client.toJSON(), {
362 | messages: [1],
363 | errors: [],
364 | ends: []
365 | }, "got one message")
366 |
367 | connect(source, client)
368 | send(source, 2)
369 |
370 | assert.deepEqual(source.toJSON(), {
371 | started: 1,
372 | stopped: 0,
373 | value: 2
374 | }, "source value changed to 2")
375 | assert.deepEqual(client.toJSON(), {
376 | messages: [1, 2],
377 | errors: [],
378 | ends: []
379 | }, "got only one message")
380 | }
381 |
382 | exports["test manual disconnect stops"] = function(assert) {
383 | var source = new Subject({ value: 0 })
384 | var a = new Client()
385 | var b = new Client()
386 |
387 | connect(source, a)
388 | connect(source, b)
389 |
390 | assert.deepEqual(source.toJSON(), {
391 | started: 1,
392 | stopped: 0,
393 | value: 0
394 | }, "input started")
395 |
396 | send(source, 1)
397 |
398 | assert.deepEqual(source.toJSON(), {
399 | started: 1,
400 | stopped: 0,
401 | value: 1
402 | }, "source.value is 1")
403 |
404 | assert.deepEqual(a.toJSON(), {
405 | messages: [1],
406 | errors: [],
407 | ends: []
408 | }, "a got a message")
409 |
410 | assert.deepEqual(b.toJSON(), {
411 | messages: [1],
412 | errors: [],
413 | ends: []
414 | }, "b got a message")
415 |
416 | disconnect(source, a)
417 |
418 | assert.deepEqual(source.toJSON(), {
419 | started: 1,
420 | stopped: 0,
421 | value: 1
422 | }, "source.value is 1")
423 |
424 | assert.deepEqual(a.toJSON(), {
425 | messages: [1],
426 | errors: [],
427 | ends: []
428 | }, "disconnected a did not get a message")
429 |
430 | send(source, 2)
431 |
432 | assert.deepEqual(source.toJSON(), {
433 | started: 1,
434 | stopped: 0,
435 | value: 2
436 | }, "source.value is 2")
437 |
438 | assert.deepEqual(a.toJSON(), {
439 | messages: [1],
440 | errors: [],
441 | ends: []
442 | }, "disconnected a did not get a message")
443 |
444 | assert.deepEqual(b.toJSON(), {
445 | messages: [1, 2],
446 | errors: [],
447 | ends: []
448 | }, "b got a message")
449 |
450 | disconnect(source, b)
451 |
452 | assert.deepEqual(source.toJSON(), {
453 | started: 1,
454 | stopped: 1,
455 | value: 2
456 | }, "source was stopped")
457 |
458 | assert.deepEqual(a.toJSON(), {
459 | messages: [1],
460 | errors: [],
461 | ends: []
462 | }, "a didn't change")
463 |
464 | assert.deepEqual(b.toJSON(), {
465 | messages: [1, 2],
466 | errors: [],
467 | ends: []
468 | }, "b ended")
469 | }
470 |
471 |
472 | var constant = signal.constant
473 | exports["test constant"] = function(assert) {
474 | var one = constant(1)
475 |
476 | assert.equal(one.value, 1, "value is given one")
477 |
478 | var received = 0
479 | var errored = 0
480 | var ended = 0
481 |
482 | var client = new Client()
483 | connect(one, client)
484 |
485 | assert.deepEqual(client.toJSON(), {
486 | messages: [],
487 | errors: [],
488 | ends: []
489 | }, "nothing received");
490 | }
491 |
492 | var lift = signal.lift
493 | exports["test lift1"] = function(assert) {
494 | var order = []
495 | var source = new Subject({ value: 0 })
496 |
497 | assert.equal(source.value, 0, "value is 0")
498 |
499 | var xs = lift(function(x) { return x + 1 }, source)
500 | var ys = lift(function(x) { return x * 2 }, source)
501 | var zs = lift(function(y) { return y + 2 }, ys)
502 |
503 | assert.deepEqual(source.toJSON(), {
504 | started: 0,
505 | stopped: 0,
506 | value: 0
507 | }, "source isn't started yet")
508 |
509 | assert.equal(xs.value, 1, "xs.value is 1")
510 | assert.equal(ys.value, 0, "ys.value is 0")
511 | assert.equal(zs.value, 2, "zs.value is 2")
512 |
513 | var zclient = new Client({ onNext: function() { order.push("z") } })
514 | connect(zs, zclient)
515 |
516 | assert.deepEqual(source.toJSON(), {
517 | started: 1,
518 | stopped: 0,
519 | value: 0
520 | }, "source started")
521 |
522 | send(source, 3)
523 |
524 | assert.deepEqual(source.toJSON(), {
525 | started: 1,
526 | stopped: 0,
527 | value: 3
528 | }, "source value is 3")
529 |
530 | assert.deepEqual(zclient.toJSON(), {
531 | messages: [8],
532 | errors: [],
533 | ends: []
534 | }, "message received on the client")
535 |
536 | assert.equal(xs.value, 1, "xs.value didn't changed")
537 | assert.equal(ys.value, 6, "ys.value changed to 6")
538 | assert.equal(zs.value, 8, "zs.value changed to 8")
539 |
540 | var xclient = new Client({ onNext: function() { order.push("x") }})
541 | connect(xs, xclient)
542 |
543 | assert.deepEqual(source.toJSON(), {
544 | started: 1,
545 | stopped: 0,
546 | value: 3
547 | }, "source didnt changed")
548 |
549 | assert.deepEqual(zclient.toJSON(), {
550 | messages: [8],
551 | errors: [],
552 | ends: []
553 | }, "message received on the client")
554 |
555 | assert.deepEqual(xclient.toJSON(), {
556 | messages: [],
557 | errors: [],
558 | ends: []
559 | }, "nothing happende yet")
560 |
561 | send(source, Return(4))
562 |
563 | assert.deepEqual(source.toJSON(), {
564 | started: 1,
565 | stopped: 1,
566 | value: 4
567 | }, "source value is 4 and it's stopped")
568 |
569 | assert.deepEqual(zclient.toJSON(), {
570 | messages: [8, 10],
571 | errors: [],
572 | ends: [true]
573 | }, "z client received message & ended")
574 |
575 | assert.deepEqual(xclient.toJSON(), {
576 | messages: [5],
577 | errors: [],
578 | ends: [true]
579 | }, "x client received message & ended")
580 |
581 | assert.equal(xs.value, 5, "xs.value changed to 5")
582 | assert.equal(ys.value, 8, "ys.value changed to 8")
583 | assert.equal(zs.value, 10, "zs.value changed to 10")
584 | }
585 |
586 | exports["test liftN"] = function(assert) {
587 | var xs = new Subject({ value: 0 })
588 | var ys = new Subject({ value: 5 })
589 | var client = new Client();
590 |
591 | var xys = lift(function(x, y) {
592 | return x + y
593 | }, xs, ys);
594 |
595 | assert.deepEqual(xs.toJSON(), {
596 | started: 0,
597 | stopped: 0,
598 | value: 0
599 | }, "xs has not started yet")
600 |
601 | assert.deepEqual(ys.toJSON(), {
602 | started: 0,
603 | stopped: 0,
604 | value: 5
605 | }, "ys has not started yet")
606 |
607 | assert.equal(xys.value, 5, "xys.value is 5")
608 |
609 | connect(xys, client);
610 |
611 | assert.deepEqual(xs.toJSON(), {
612 | started: 1,
613 | stopped: 0,
614 | value: 0
615 | }, "xs started")
616 |
617 | assert.deepEqual(ys.toJSON(), {
618 | started: 1,
619 | stopped: 0,
620 | value: 5
621 | }, "ys started")
622 |
623 | assert.equal(xys.value, 5, "xys.value is still 5")
624 |
625 | send(xs, 1)
626 |
627 | assert.deepEqual(xs.toJSON(), {
628 | started: 1,
629 | stopped: 0,
630 | value: 1
631 | }, "xs.value changed to 1")
632 |
633 | assert.deepEqual(ys.toJSON(), {
634 | started: 1,
635 | stopped: 0,
636 | value: 5
637 | }, "ys value didn't change")
638 |
639 | assert.equal(xys.value, 6, "xys.value changed to 6")
640 |
641 | send(ys, 6)
642 |
643 | assert.deepEqual(xs.toJSON(), {
644 | started: 1,
645 | stopped: 0,
646 | value: 1
647 | }, "xs.value is still 1")
648 |
649 | assert.deepEqual(ys.toJSON(), {
650 | started: 1,
651 | stopped: 0,
652 | value: 6
653 | }, "ys value changed to 6")
654 |
655 | assert.equal(xys.value, 7, "xys.value changed to 7")
656 |
657 | send(xs, 2)
658 |
659 | assert.deepEqual(xs.toJSON(), {
660 | started: 1,
661 | stopped: 0,
662 | value: 2
663 | }, "xs.value changed to 2")
664 |
665 | assert.deepEqual(ys.toJSON(), {
666 | started: 1,
667 | stopped: 0,
668 | value: 6
669 | }, "ys value didn't change")
670 |
671 | assert.equal(xys.value, 8, "xys.value changed to 8")
672 |
673 | send(ys, 8)
674 |
675 | assert.deepEqual(xs.toJSON(), {
676 | started: 1,
677 | stopped: 0,
678 | value: 2
679 | }, "xs.value didn't change")
680 |
681 | assert.deepEqual(ys.toJSON(), {
682 | started: 1,
683 | stopped: 0,
684 | value: 8
685 | }, "ys value changed to 8")
686 |
687 | assert.equal(xys.value, 10, "xys.value changed to 10")
688 |
689 | send(ys, Return(5))
690 |
691 | assert.deepEqual(xs.toJSON(), {
692 | started: 1,
693 | stopped: 0,
694 | value: 2
695 | }, "xs.value didn't change")
696 |
697 | assert.deepEqual(ys.toJSON(), {
698 | started: 1,
699 | stopped: 1,
700 | value: 5
701 | }, "ys value changed to 5 & stopped")
702 |
703 | assert.equal(xys.value, 7, "xys.value changed to 7")
704 |
705 | send(xs, 5)
706 |
707 | assert.deepEqual(xs.toJSON(), {
708 | started: 1,
709 | stopped: 0,
710 | value: 5
711 | }, "xs.value changed to 5")
712 |
713 | assert.deepEqual(ys.toJSON(), {
714 | started: 1,
715 | stopped: 1,
716 | value: 5
717 | }, "ys value didn't change")
718 |
719 | assert.equal(xys.value, 10, "xys.value changed to 10")
720 |
721 | send(xs, Return(7))
722 |
723 | assert.deepEqual(xs.toJSON(), {
724 | started: 1,
725 | stopped: 1,
726 | value: 7
727 | }, "stopped and changed xs.value to 7")
728 |
729 | assert.deepEqual(ys.toJSON(), {
730 | started: 1,
731 | stopped: 1,
732 | value: 5
733 | }, "ys value didn't change")
734 |
735 | assert.equal(xys.value, 12, "xys.value changed to 12")
736 |
737 | assert.deepEqual(client.toJSON(), {
738 | messages: [6, 7, 8, 10, 7, 10, 12],
739 | errors: [],
740 | ends: [true]
741 | }, "all messages received on the client")
742 | }
743 |
744 |
745 |
746 | var keepIf = signal.keepIf
747 | var isOdd = function(x) { return x % 2 }
748 | var isEven = function(x) { return !(x % 2) }
749 | exports["test keepIf (keep initial)"] = function(assert) {
750 | var xs = new Subject({ value: 1 })
751 | var ys = keepIf(isOdd, 0, xs)
752 |
753 | assert.deepEqual(xs.toJSON(), {
754 | started: 0,
755 | stopped: 0,
756 | value: 1
757 | }, "xs is in initial state")
758 |
759 | assert.equal(ys.value, 1, "xs.value is kept since it's odd")
760 |
761 | var client = new Client()
762 |
763 | connect(ys, client)
764 |
765 | assert.deepEqual(xs.toJSON(), {
766 | started: 1,
767 | stopped: 0,
768 | value: 1
769 | }, "xs started")
770 |
771 | assert.deepEqual(client.toJSON(), {
772 | messages: [],
773 | errors: [],
774 | ends: []
775 | }, "no messages received yet")
776 |
777 | assert.equal(ys.value, 1, "ys.value is 1")
778 |
779 | send(xs, 2)
780 |
781 | assert.deepEqual(xs.toJSON(), {
782 | started: 1,
783 | stopped: 0,
784 | value: 2
785 | }, "xs.value changed to 2")
786 |
787 | assert.deepEqual(client.toJSON(), {
788 | messages: [],
789 | errors: [],
790 | ends: []
791 | }, "no messages were received")
792 |
793 | assert.equal(ys.value, 1, "ys.value is kept 1")
794 |
795 | send(xs, 3)
796 |
797 | assert.deepEqual(xs.toJSON(), {
798 | started: 1,
799 | stopped: 0,
800 | value: 3
801 | }, "xs.value changed to 3")
802 |
803 | assert.deepEqual(client.toJSON(), {
804 | messages: [3],
805 | errors: [],
806 | ends: []
807 | }, "message was received")
808 |
809 | assert.equal(ys.value, 3, "ys.value updated to 3")
810 |
811 | send(xs, 4)
812 |
813 | assert.deepEqual(xs.toJSON(), {
814 | started: 1,
815 | stopped: 0,
816 | value: 4
817 | }, "xs.value changed to 4")
818 |
819 | assert.deepEqual(client.toJSON(), {
820 | messages: [3],
821 | errors: [],
822 | ends: []
823 | }, "no messages were received")
824 |
825 | assert.equal(ys.value, 3, "ys.value is still 3")
826 |
827 | send(xs, new Return(5))
828 |
829 | assert.deepEqual(xs.toJSON(), {
830 | started: 1,
831 | stopped: 1,
832 | value: 5
833 | }, "xs.value changed to 5 and stopped")
834 |
835 | assert.deepEqual(client.toJSON(), {
836 | messages: [3, 5],
837 | errors: [],
838 | ends: [true]
839 | }, "message & end was received")
840 |
841 | assert.equal(ys.value, 5, "ys.value updated to 5")
842 | }
843 |
844 |
845 | exports["test keepIf (update initial)"] = function(assert) {
846 | var xs = new Subject({ value: 1 })
847 | var ys = keepIf(isEven, 0, xs)
848 |
849 | assert.deepEqual(xs.toJSON(), {
850 | started: 0,
851 | stopped: 0,
852 | value: 1
853 | }, "xs is in initial state")
854 |
855 | assert.equal(ys.value, 0, "ys.value updated since it's not even")
856 |
857 | var client = new Client()
858 |
859 | connect(ys, client)
860 |
861 | assert.deepEqual(xs.toJSON(), {
862 | started: 1,
863 | stopped: 0,
864 | value: 1
865 | }, "xs started")
866 |
867 | assert.deepEqual(client.toJSON(), {
868 | messages: [],
869 | errors: [],
870 | ends: []
871 | }, "no messages received yet")
872 |
873 | send(xs, 2)
874 |
875 | assert.deepEqual(xs.toJSON(), {
876 | started: 1,
877 | stopped: 0,
878 | value: 2
879 | }, "xs.value changed to 2")
880 |
881 | assert.deepEqual(client.toJSON(), {
882 | messages: [2],
883 | errors: [],
884 | ends: []
885 | }, "messages was received")
886 |
887 | assert.equal(ys.value, 2, "ys.value is updated to 2")
888 |
889 | send(xs, 3)
890 |
891 | assert.deepEqual(xs.toJSON(), {
892 | started: 1,
893 | stopped: 0,
894 | value: 3
895 | }, "xs.value changed to 3")
896 |
897 | assert.deepEqual(client.toJSON(), {
898 | messages: [2],
899 | errors: [],
900 | ends: []
901 | }, "message wasn't received")
902 |
903 | assert.equal(ys.value, 2, "ys.value is 3")
904 |
905 | send(xs, 4)
906 |
907 | assert.deepEqual(xs.toJSON(), {
908 | started: 1,
909 | stopped: 0,
910 | value: 4
911 | }, "xs.value changed to 4")
912 |
913 | assert.deepEqual(client.toJSON(), {
914 | messages: [2, 4],
915 | errors: [],
916 | ends: []
917 | }, "messages was received")
918 |
919 | assert.equal(ys.value, 4, "ys.value set to 4")
920 |
921 | send(xs, new Return(5))
922 |
923 | assert.deepEqual(xs.toJSON(), {
924 | started: 1,
925 | stopped: 1,
926 | value: 5
927 | }, "xs.value changed to 5 and stopped")
928 |
929 | assert.deepEqual(client.toJSON(), {
930 | messages: [2, 4],
931 | errors: [],
932 | ends: [true]
933 | }, "ys ended, but nothing was received")
934 |
935 | assert.equal(ys.value, 4, "ys.value is still 4")
936 | }
937 |
938 |
939 | var dropIf = signal.dropIf
940 | exports["test dropIf (update initial)"] = function(assert) {
941 | var xs = new Subject({ value: 1 })
942 | var ys = dropIf(isOdd, 0, xs)
943 |
944 | assert.deepEqual(xs.toJSON(), {
945 | started: 0,
946 | stopped: 0,
947 | value: 1
948 | }, "xs is in initial state")
949 |
950 | assert.equal(ys.value, 0, "ys.value updated since it's odd")
951 |
952 | var client = new Client()
953 |
954 | connect(ys, client)
955 |
956 | assert.deepEqual(xs.toJSON(), {
957 | started: 1,
958 | stopped: 0,
959 | value: 1
960 | }, "xs started")
961 |
962 | assert.deepEqual(client.toJSON(), {
963 | messages: [],
964 | errors: [],
965 | ends: []
966 | }, "no messages received yet")
967 |
968 | send(xs, 2)
969 |
970 | assert.deepEqual(xs.toJSON(), {
971 | started: 1,
972 | stopped: 0,
973 | value: 2
974 | }, "xs.value changed to 2")
975 |
976 | assert.deepEqual(client.toJSON(), {
977 | messages: [2],
978 | errors: [],
979 | ends: []
980 | }, "messages was received")
981 |
982 | assert.equal(ys.value, 2, "ys.value is updated to 2")
983 |
984 | send(xs, 3)
985 |
986 | assert.deepEqual(xs.toJSON(), {
987 | started: 1,
988 | stopped: 0,
989 | value: 3
990 | }, "xs.value changed to 3")
991 |
992 | assert.deepEqual(client.toJSON(), {
993 | messages: [2],
994 | errors: [],
995 | ends: []
996 | }, "message wasn't received")
997 |
998 | assert.equal(ys.value, 2, "ys.value is 2")
999 |
1000 | send(xs, new Return(4))
1001 |
1002 | assert.deepEqual(xs.toJSON(), {
1003 | started: 1,
1004 | stopped: 1,
1005 | value: 4
1006 | }, "xs.value changed to 4")
1007 |
1008 | assert.deepEqual(client.toJSON(), {
1009 | messages: [2, 4],
1010 | errors: [],
1011 | ends: [true]
1012 | }, "message received & end")
1013 |
1014 | assert.equal(ys.value, 4, "ys.value is 4")
1015 | }
1016 |
1017 | exports["test dropIf (keep initial)"] = function(assert) {
1018 | var xs = new Subject({ value: 1 })
1019 | var ys = dropIf(isEven, 0, xs)
1020 |
1021 | assert.deepEqual(xs.toJSON(), {
1022 | started: 0,
1023 | stopped: 0,
1024 | value: 1
1025 | }, "xs is in initial state")
1026 |
1027 | assert.equal(ys.value, 1, "ys.value remained")
1028 |
1029 | var client = new Client()
1030 |
1031 | connect(ys, client)
1032 |
1033 | assert.deepEqual(xs.toJSON(), {
1034 | started: 1,
1035 | stopped: 0,
1036 | value: 1
1037 | }, "xs started")
1038 |
1039 | assert.deepEqual(client.toJSON(), {
1040 | messages: [],
1041 | errors: [],
1042 | ends: []
1043 | }, "no messages received yet")
1044 |
1045 | send(xs, 2)
1046 |
1047 | assert.deepEqual(xs.toJSON(), {
1048 | started: 1,
1049 | stopped: 0,
1050 | value: 2
1051 | }, "xs.value changed to 2")
1052 |
1053 | assert.deepEqual(client.toJSON(), {
1054 | messages: [],
1055 | errors: [],
1056 | ends: []
1057 | }, "messages wasn't received")
1058 |
1059 | assert.equal(ys.value, 1, "ys.value is stayed same")
1060 |
1061 | send(xs, 3)
1062 |
1063 | assert.deepEqual(xs.toJSON(), {
1064 | started: 1,
1065 | stopped: 0,
1066 | value: 3
1067 | }, "xs.value changed to 3")
1068 |
1069 | assert.deepEqual(client.toJSON(), {
1070 | messages: [3],
1071 | errors: [],
1072 | ends: []
1073 | }, "message was received")
1074 |
1075 | assert.equal(ys.value, 3, "ys.value updated")
1076 |
1077 | send(xs, new Return(4))
1078 |
1079 | assert.deepEqual(xs.toJSON(), {
1080 | started: 1,
1081 | stopped: 1,
1082 | value: 4
1083 | }, "xs.value changed")
1084 |
1085 | assert.deepEqual(client.toJSON(), {
1086 | messages: [3],
1087 | errors: [],
1088 | ends: [true]
1089 | }, "message received & end")
1090 |
1091 | assert.equal(ys.value, 3, "ys.value remained")
1092 | }
1093 |
1094 |
1095 | var foldp = signal.foldp
1096 | exports["test flodp"] = function(assert) {
1097 | var xs = new Subject({ value: 0 })
1098 | var ys = foldp(function(p, x) {
1099 | return p + x
1100 | }, 5, xs)
1101 |
1102 | assert.deepEqual(xs.toJSON(), {
1103 | value: 0,
1104 | started: 0,
1105 | stopped: 0
1106 | }, "source is in inital state")
1107 |
1108 | assert.equal(ys.value, 5, "initial value is set")
1109 |
1110 | var client = new Client()
1111 |
1112 | connect(ys, client)
1113 |
1114 | assert.deepEqual(xs.toJSON(), {
1115 | value: 0,
1116 | started: 1,
1117 | stopped: 0
1118 | }, "source was started")
1119 |
1120 | assert.equal(ys.value, 5, "still in initial state")
1121 |
1122 | send(xs, 1)
1123 |
1124 | assert.deepEqual(xs.toJSON(), {
1125 | value: 1,
1126 | started: 1,
1127 | stopped: 0
1128 | }, "source was started")
1129 |
1130 | assert.deepEqual(client.toJSON(), {
1131 | messages: [6],
1132 | errors: [],
1133 | ends: []
1134 | }, "message was received")
1135 |
1136 | assert.equal(ys.value, 6, "value changed")
1137 |
1138 | send(xs, 2)
1139 |
1140 | assert.deepEqual(xs.toJSON(), {
1141 | value: 2,
1142 | started: 1,
1143 | stopped: 0
1144 | }, "source was started")
1145 |
1146 | assert.deepEqual(client.toJSON(), {
1147 | messages: [6, 8],
1148 | errors: [],
1149 | ends: []
1150 | }, "message was received")
1151 |
1152 | assert.equal(ys.value, 8, "value changed")
1153 |
1154 | send(xs, 3)
1155 |
1156 | assert.deepEqual(xs.toJSON(), {
1157 | value: 3,
1158 | started: 1,
1159 | stopped: 0
1160 | }, "source was started")
1161 |
1162 | assert.deepEqual(client.toJSON(), {
1163 | messages: [6, 8, 11],
1164 | errors: [],
1165 | ends: []
1166 | }, "message was received")
1167 |
1168 | assert.equal(ys.value, 11, "value changed")
1169 |
1170 | send(xs, new Return(4))
1171 |
1172 | assert.deepEqual(xs.toJSON(), {
1173 | value: 4,
1174 | started: 1,
1175 | stopped: 1
1176 | }, "source was stopped")
1177 |
1178 | assert.deepEqual(client.toJSON(), {
1179 | messages: [6, 8, 11, 15],
1180 | errors: [],
1181 | ends: [true]
1182 | }, "message & end was received")
1183 |
1184 | assert.equal(ys.value, 15, "value changed")
1185 | }
1186 |
1187 |
1188 | var merge = signal.merge
1189 | exports["test merge"] = function(assert) {
1190 | var xs = new Subject({ value: 0 })
1191 | var ys = new Subject({ value: 5 })
1192 | var xys = merge(xs, ys)
1193 |
1194 | assert.deepEqual(xs.toJSON(), {
1195 | value: 0,
1196 | started: 0,
1197 | stopped: 0
1198 | }, "source#1 is in inital state")
1199 |
1200 | assert.deepEqual(ys.toJSON(), {
1201 | value: 5,
1202 | started: 0,
1203 | stopped: 0
1204 | }, "source#2 is in inital state")
1205 |
1206 | assert.equal(xys.value, 0, "initial value is from source#1")
1207 |
1208 | var client = new Client()
1209 | connect(xys, client)
1210 |
1211 | assert.deepEqual(xs.toJSON(), {
1212 | value: 0,
1213 | started: 1,
1214 | stopped: 0
1215 | }, "source#1 is in started state")
1216 |
1217 | assert.deepEqual(ys.toJSON(), {
1218 | value: 5,
1219 | started: 1,
1220 | stopped: 0
1221 | }, "source#2 is in started state")
1222 |
1223 | assert.equal(xys.value, 0, "initial value is from source#1")
1224 |
1225 | send(xs, 1)
1226 |
1227 | assert.deepEqual(xs.toJSON(), {
1228 | value: 1,
1229 | started: 1,
1230 | stopped: 0
1231 | }, "source#1 value updated")
1232 |
1233 | assert.deepEqual(ys.toJSON(), {
1234 | value: 5,
1235 | started: 1,
1236 | stopped: 0
1237 | }, "source#2 value didn't change")
1238 |
1239 | assert.deepEqual(client.toJSON(), {
1240 | messages: [1],
1241 | errors: [],
1242 | ends: []
1243 | }, "message received")
1244 |
1245 | assert.equal(xys.value, 1, "merged signal value updated")
1246 |
1247 | send(xs, 2)
1248 |
1249 | assert.deepEqual(xs.toJSON(), {
1250 | value: 2,
1251 | started: 1,
1252 | stopped: 0
1253 | }, "source#1 value updated")
1254 |
1255 | assert.deepEqual(ys.toJSON(), {
1256 | value: 5,
1257 | started: 1,
1258 | stopped: 0
1259 | }, "source#2 value didn't change")
1260 |
1261 | assert.deepEqual(client.toJSON(), {
1262 | messages: [1, 2],
1263 | errors: [],
1264 | ends: []
1265 | }, "message received")
1266 |
1267 | assert.equal(xys.value, 2, "merged signal value updated")
1268 |
1269 | send(ys, 3)
1270 |
1271 | assert.deepEqual(xs.toJSON(), {
1272 | value: 2,
1273 | started: 1,
1274 | stopped: 0
1275 | }, "source#1 value stayed")
1276 |
1277 | assert.deepEqual(ys.toJSON(), {
1278 | value: 3,
1279 | started: 1,
1280 | stopped: 0
1281 | }, "source#2 value changed")
1282 |
1283 | assert.deepEqual(client.toJSON(), {
1284 | messages: [1, 2, 3],
1285 | errors: [],
1286 | ends: []
1287 | }, "message received")
1288 |
1289 | assert.equal(xys.value, 3, "merged signal value updated")
1290 |
1291 | send(xs, new Return(4))
1292 |
1293 | assert.deepEqual(xs.toJSON(), {
1294 | value: 4,
1295 | started: 1,
1296 | stopped: 1
1297 | }, "source#1 value changed & stopped")
1298 |
1299 | assert.deepEqual(ys.toJSON(), {
1300 | value: 3,
1301 | started: 1,
1302 | stopped: 0
1303 | }, "source#2 value didn't change")
1304 |
1305 | assert.deepEqual(client.toJSON(), {
1306 | messages: [1, 2, 3, 4],
1307 | errors: [],
1308 | ends: []
1309 | }, "message received")
1310 |
1311 | assert.equal(xys.value, 4, "merged signal value updated")
1312 |
1313 | send(ys, 5)
1314 |
1315 | assert.deepEqual(xs.toJSON(), {
1316 | value: 4,
1317 | started: 1,
1318 | stopped: 1
1319 | }, "source#1 value changed & stopped")
1320 |
1321 | assert.deepEqual(ys.toJSON(), {
1322 | value: 5,
1323 | started: 1,
1324 | stopped: 0
1325 | }, "source#2 value changed")
1326 |
1327 | assert.deepEqual(client.toJSON(), {
1328 | messages: [1, 2, 3, 4, 5],
1329 | errors: [],
1330 | ends: []
1331 | }, "message received")
1332 |
1333 | assert.equal(xys.value, 5, "merged signal value updated")
1334 |
1335 | send(ys, new Return(6))
1336 |
1337 | assert.deepEqual(xs.toJSON(), {
1338 | value: 4,
1339 | started: 1,
1340 | stopped: 1
1341 | }, "source#1 value changed & stopped")
1342 |
1343 | assert.deepEqual(ys.toJSON(), {
1344 | value: 6,
1345 | started: 1,
1346 | stopped: 1
1347 | }, "source#2 value changed & stopped")
1348 |
1349 | assert.deepEqual(client.toJSON(), {
1350 | messages: [1, 2, 3, 4, 5, 6],
1351 | errors: [],
1352 | ends: [true]
1353 | }, "message & end received")
1354 |
1355 | assert.equal(xys.value, 6, "merged signal value updated")
1356 | }
1357 |
1358 | var merges = signal.merges
1359 | exports["test merges"] = function(assert) {
1360 | var xs = new Subject({ value: 0 })
1361 | var ys = new Subject({ value: 5 })
1362 | var xys = merges([xs, ys])
1363 |
1364 | assert.deepEqual(xs.toJSON(), {
1365 | value: 0,
1366 | started: 0,
1367 | stopped: 0
1368 | }, "source#1 is in inital state")
1369 |
1370 | assert.deepEqual(ys.toJSON(), {
1371 | value: 5,
1372 | started: 0,
1373 | stopped: 0
1374 | }, "source#2 is in inital state")
1375 |
1376 | assert.equal(xys.value, 0, "initial value is from source#1")
1377 |
1378 | var client = new Client()
1379 | connect(xys, client)
1380 |
1381 | assert.deepEqual(xs.toJSON(), {
1382 | value: 0,
1383 | started: 1,
1384 | stopped: 0
1385 | }, "source#1 is in started state")
1386 |
1387 | assert.deepEqual(ys.toJSON(), {
1388 | value: 5,
1389 | started: 1,
1390 | stopped: 0
1391 | }, "source#2 is in started state")
1392 |
1393 | assert.equal(xys.value, 0, "initial value is from source#1")
1394 |
1395 | send(xs, 1)
1396 |
1397 | assert.deepEqual(xs.toJSON(), {
1398 | value: 1,
1399 | started: 1,
1400 | stopped: 0
1401 | }, "source#1 value updated")
1402 |
1403 | assert.deepEqual(ys.toJSON(), {
1404 | value: 5,
1405 | started: 1,
1406 | stopped: 0
1407 | }, "source#2 value didn't change")
1408 |
1409 | assert.deepEqual(client.toJSON(), {
1410 | messages: [1],
1411 | errors: [],
1412 | ends: []
1413 | }, "message received")
1414 |
1415 | assert.equal(xys.value, 1, "merged signal value updated")
1416 |
1417 | send(xs, 2)
1418 |
1419 | assert.deepEqual(xs.toJSON(), {
1420 | value: 2,
1421 | started: 1,
1422 | stopped: 0
1423 | }, "source#1 value updated")
1424 |
1425 | assert.deepEqual(ys.toJSON(), {
1426 | value: 5,
1427 | started: 1,
1428 | stopped: 0
1429 | }, "source#2 value didn't change")
1430 |
1431 | assert.deepEqual(client.toJSON(), {
1432 | messages: [1, 2],
1433 | errors: [],
1434 | ends: []
1435 | }, "message received")
1436 |
1437 | assert.equal(xys.value, 2, "merged signal value updated")
1438 |
1439 | send(ys, 3)
1440 |
1441 | assert.deepEqual(xs.toJSON(), {
1442 | value: 2,
1443 | started: 1,
1444 | stopped: 0
1445 | }, "source#1 value stayed")
1446 |
1447 | assert.deepEqual(ys.toJSON(), {
1448 | value: 3,
1449 | started: 1,
1450 | stopped: 0
1451 | }, "source#2 value changed")
1452 |
1453 | assert.deepEqual(client.toJSON(), {
1454 | messages: [1, 2, 3],
1455 | errors: [],
1456 | ends: []
1457 | }, "message received")
1458 |
1459 | assert.equal(xys.value, 3, "merged signal value updated")
1460 |
1461 | send(xs, new Return(4))
1462 |
1463 | assert.deepEqual(xs.toJSON(), {
1464 | value: 4,
1465 | started: 1,
1466 | stopped: 1
1467 | }, "source#1 value changed & stopped")
1468 |
1469 | assert.deepEqual(ys.toJSON(), {
1470 | value: 3,
1471 | started: 1,
1472 | stopped: 0
1473 | }, "source#2 value didn't change")
1474 |
1475 | assert.deepEqual(client.toJSON(), {
1476 | messages: [1, 2, 3, 4],
1477 | errors: [],
1478 | ends: []
1479 | }, "message received")
1480 |
1481 | assert.equal(xys.value, 4, "merged signal value updated")
1482 |
1483 | send(ys, 5)
1484 |
1485 | assert.deepEqual(xs.toJSON(), {
1486 | value: 4,
1487 | started: 1,
1488 | stopped: 1
1489 | }, "source#1 value changed & stopped")
1490 |
1491 | assert.deepEqual(ys.toJSON(), {
1492 | value: 5,
1493 | started: 1,
1494 | stopped: 0
1495 | }, "source#2 value changed")
1496 |
1497 | assert.deepEqual(client.toJSON(), {
1498 | messages: [1, 2, 3, 4, 5],
1499 | errors: [],
1500 | ends: []
1501 | }, "message received")
1502 |
1503 | assert.equal(xys.value, 5, "merged signal value updated")
1504 |
1505 | send(ys, new Return(6))
1506 |
1507 | assert.deepEqual(xs.toJSON(), {
1508 | value: 4,
1509 | started: 1,
1510 | stopped: 1
1511 | }, "source#1 value changed & stopped")
1512 |
1513 | assert.deepEqual(ys.toJSON(), {
1514 | value: 6,
1515 | started: 1,
1516 | stopped: 1
1517 | }, "source#2 value changed & stopped")
1518 |
1519 | assert.deepEqual(client.toJSON(), {
1520 | messages: [1, 2, 3, 4, 5, 6],
1521 | errors: [],
1522 | ends: [true]
1523 | }, "message & end received")
1524 |
1525 | assert.equal(xys.value, 6, "merged signal value updated")
1526 | }
1527 |
1528 | var combine = signal.combine
1529 | exports["test combine"] = function(assert) {
1530 | var xs = new Subject({ value: 0 })
1531 | var ys = new Subject({ value: 5 })
1532 | var client = new Client();
1533 |
1534 | var xys = combine([xs, ys]);
1535 |
1536 | assert.deepEqual(xs.toJSON(), {
1537 | started: 0,
1538 | stopped: 0,
1539 | value: 0
1540 | }, "xs has not started yet")
1541 |
1542 | assert.deepEqual(ys.toJSON(), {
1543 | started: 0,
1544 | stopped: 0,
1545 | value: 5
1546 | }, "ys has not started yet")
1547 |
1548 | assert.deepEqual(xys.value, [0, 5], "xys.value combined")
1549 |
1550 | connect(xys, client);
1551 |
1552 | assert.deepEqual(xs.toJSON(), {
1553 | started: 1,
1554 | stopped: 0,
1555 | value: 0
1556 | }, "xs started")
1557 |
1558 | assert.deepEqual(ys.toJSON(), {
1559 | started: 1,
1560 | stopped: 0,
1561 | value: 5
1562 | }, "ys started")
1563 |
1564 | assert.deepEqual(xys.value, [0, 5], "xys.value didn't change")
1565 |
1566 | send(xs, 1)
1567 |
1568 | assert.deepEqual(xs.toJSON(), {
1569 | started: 1,
1570 | stopped: 0,
1571 | value: 1
1572 | }, "xs.value changed to 1")
1573 |
1574 | assert.deepEqual(ys.toJSON(), {
1575 | started: 1,
1576 | stopped: 0,
1577 | value: 5
1578 | }, "ys value didn't change")
1579 |
1580 | assert.deepEqual(xys.value, [1, 5], "xys.value changed")
1581 |
1582 | send(ys, 6)
1583 |
1584 | assert.deepEqual(xs.toJSON(), {
1585 | started: 1,
1586 | stopped: 0,
1587 | value: 1
1588 | }, "xs.value is still 1")
1589 |
1590 | assert.deepEqual(ys.toJSON(), {
1591 | started: 1,
1592 | stopped: 0,
1593 | value: 6
1594 | }, "ys value changed to 6")
1595 |
1596 | assert.deepEqual(xys.value, [1, 6], "xys.value changed")
1597 |
1598 | send(xs, 2)
1599 |
1600 | assert.deepEqual(xs.toJSON(), {
1601 | started: 1,
1602 | stopped: 0,
1603 | value: 2
1604 | }, "xs.value changed to 2")
1605 |
1606 | assert.deepEqual(ys.toJSON(), {
1607 | started: 1,
1608 | stopped: 0,
1609 | value: 6
1610 | }, "ys value didn't change")
1611 |
1612 | assert.deepEqual(xys.value, [2, 6], "xys.value changed")
1613 |
1614 | send(ys, 8)
1615 |
1616 | assert.deepEqual(xs.toJSON(), {
1617 | started: 1,
1618 | stopped: 0,
1619 | value: 2
1620 | }, "xs.value didn't change")
1621 |
1622 | assert.deepEqual(ys.toJSON(), {
1623 | started: 1,
1624 | stopped: 0,
1625 | value: 8
1626 | }, "ys value changed to 8")
1627 |
1628 | assert.deepEqual(xys.value, [2, 8], "xys.value changed")
1629 |
1630 | send(ys, Return(5))
1631 |
1632 | assert.deepEqual(xs.toJSON(), {
1633 | started: 1,
1634 | stopped: 0,
1635 | value: 2
1636 | }, "xs.value didn't change")
1637 |
1638 | assert.deepEqual(ys.toJSON(), {
1639 | started: 1,
1640 | stopped: 1,
1641 | value: 5
1642 | }, "ys value changed to 5 & stopped")
1643 |
1644 | assert.deepEqual(xys.value, [2, 5], "xys.value changed")
1645 |
1646 | send(xs, 5)
1647 |
1648 | assert.deepEqual(xs.toJSON(), {
1649 | started: 1,
1650 | stopped: 0,
1651 | value: 5
1652 | }, "xs.value changed to 5")
1653 |
1654 | assert.deepEqual(ys.toJSON(), {
1655 | started: 1,
1656 | stopped: 1,
1657 | value: 5
1658 | }, "ys value didn't change")
1659 |
1660 | assert.deepEqual(xys.value, [5, 5], "xys.value changed")
1661 |
1662 | send(xs, Return(7))
1663 |
1664 | assert.deepEqual(xs.toJSON(), {
1665 | started: 1,
1666 | stopped: 1,
1667 | value: 7
1668 | }, "stopped and changed xs.value to 7")
1669 |
1670 | assert.deepEqual(ys.toJSON(), {
1671 | started: 1,
1672 | stopped: 1,
1673 | value: 5
1674 | }, "ys value didn't change")
1675 |
1676 | assert.deepEqual(xys.value, [7, 5], "xys.value changed")
1677 |
1678 | assert.deepEqual(client.toJSON(), {
1679 | messages: [
1680 | [1, 5],
1681 | [1, 6],
1682 | [2, 6],
1683 | [2, 8],
1684 | [2, 5],
1685 | [5, 5],
1686 | [7, 5]
1687 | ],
1688 | errors: [],
1689 | ends: [true]
1690 | }, "all messages received on the client")
1691 | }
1692 |
1693 |
1694 | var count = signal.count
1695 | exports["test count"] = function(assert) {
1696 | var xs = new Subject({ value: null })
1697 | var ys = count(xs)
1698 |
1699 | assert.deepEqual(xs.toJSON(), {
1700 | started: 0,
1701 | stopped: 0,
1702 | value: null
1703 | }, "source is in initial state")
1704 | assert.equal(ys.value, 0, "counter is at 0")
1705 |
1706 | var client = new Client()
1707 | connect(ys, client)
1708 |
1709 | assert.deepEqual(xs.toJSON(), {
1710 | started: 1,
1711 | stopped: 0,
1712 | value: null
1713 | }, "source is in initial state")
1714 |
1715 | assert.deepEqual(client.toJSON(), {
1716 | messages: [],
1717 | errors: [],
1718 | ends: []
1719 | }, "counter hasn't recevied anything yet")
1720 |
1721 | assert.equal(ys.value, 0, "counter is at 0")
1722 |
1723 | send(xs, "a")
1724 |
1725 | assert.deepEqual(xs.toJSON(), {
1726 | started: 1,
1727 | stopped: 0,
1728 | value: "a"
1729 | }, "source vaule changed")
1730 |
1731 | assert.deepEqual(client.toJSON(), {
1732 | messages: [1],
1733 | errors: [],
1734 | ends: []
1735 | }, "counter received message")
1736 |
1737 | assert.equal(ys.value, 1, "counter incremented")
1738 |
1739 | send(xs, "b")
1740 |
1741 | assert.deepEqual(xs.toJSON(), {
1742 | started: 1,
1743 | stopped: 0,
1744 | value: "b"
1745 | }, "source vaule changed")
1746 |
1747 | assert.deepEqual(client.toJSON(), {
1748 | messages: [1, 2],
1749 | errors: [],
1750 | ends: []
1751 | }, "counter received message")
1752 |
1753 | assert.equal(ys.value, 2, "counter incremented")
1754 |
1755 | send(xs, "c")
1756 |
1757 | assert.deepEqual(xs.toJSON(), {
1758 | started: 1,
1759 | stopped: 0,
1760 | value: "c"
1761 | }, "source vaule changed")
1762 |
1763 | assert.deepEqual(client.toJSON(), {
1764 | messages: [1, 2, 3],
1765 | errors: [],
1766 | ends: []
1767 | }, "counter received message")
1768 |
1769 | assert.equal(ys.value, 3, "counter incremented")
1770 |
1771 | send(xs, new Return("d"))
1772 |
1773 | assert.deepEqual(xs.toJSON(), {
1774 | started: 1,
1775 | stopped: 1,
1776 | value: "d"
1777 | }, "source vaule changed & stopped")
1778 |
1779 | assert.deepEqual(client.toJSON(), {
1780 | messages: [1, 2, 3, 4],
1781 | errors: [],
1782 | ends: [true]
1783 | }, "counter received message & end")
1784 |
1785 | assert.equal(ys.value, 4, "counter incremented")
1786 | }
1787 |
1788 |
1789 | var countIf = signal.countIf
1790 | exports["test countIf"] = function(assert) {
1791 | var isUpperCase = function(c) {
1792 | return c.toUpperCase() == c
1793 | }
1794 |
1795 | var xs = new Subject({ value: "B" })
1796 | var ys = countIf(isUpperCase, xs)
1797 |
1798 | assert.deepEqual(xs.toJSON(), {
1799 | started: 0,
1800 | stopped: 0,
1801 | value: "B"
1802 | }, "source is in initial state")
1803 |
1804 | assert.equal(ys.value, 0, "counter value is at 0")
1805 |
1806 | var client = new Client()
1807 | connect(ys, client)
1808 |
1809 | assert.deepEqual(xs.toJSON(), {
1810 | started: 1,
1811 | stopped: 0,
1812 | value: "B"
1813 | }, "source is in start state")
1814 |
1815 | assert.deepEqual(client.toJSON(), {
1816 | messages: [],
1817 | errors: [],
1818 | ends: []
1819 | }, "no messages received")
1820 |
1821 | assert.equal(ys.value, 0, "count is at 0")
1822 |
1823 | send(xs, "a")
1824 |
1825 | assert.deepEqual(xs.toJSON(), {
1826 | started: 1,
1827 | stopped: 0,
1828 | value: "a"
1829 | }, "source value changed")
1830 |
1831 | assert.deepEqual(client.toJSON(), {
1832 | messages: [],
1833 | errors: [],
1834 | ends: []
1835 | }, "no messages received")
1836 |
1837 | assert.equal(ys.value, 0, "count is at 0")
1838 |
1839 | send(xs, "B")
1840 |
1841 | assert.deepEqual(xs.toJSON(), {
1842 | started: 1,
1843 | stopped: 0,
1844 | value: "B"
1845 | }, "source value changed")
1846 |
1847 | assert.deepEqual(client.toJSON(), {
1848 | messages: [1],
1849 | errors: [],
1850 | ends: []
1851 | }, "message received")
1852 |
1853 | assert.equal(ys.value, 1, "counter incremented")
1854 |
1855 | send(xs, "C")
1856 |
1857 | assert.deepEqual(xs.toJSON(), {
1858 | started: 1,
1859 | stopped: 0,
1860 | value: "C"
1861 | }, "source value changed")
1862 |
1863 | assert.deepEqual(client.toJSON(), {
1864 | messages: [1, 2],
1865 | errors: [],
1866 | ends: []
1867 | }, "message received")
1868 |
1869 | assert.equal(ys.value, 2, "counter incremented")
1870 |
1871 | send(xs, "d")
1872 |
1873 | assert.deepEqual(xs.toJSON(), {
1874 | started: 1,
1875 | stopped: 0,
1876 | value: "d"
1877 | }, "source value changed")
1878 |
1879 | assert.deepEqual(client.toJSON(), {
1880 | messages: [1, 2],
1881 | errors: [],
1882 | ends: []
1883 | }, "message wasn't received")
1884 |
1885 | assert.equal(ys.value, 2, "counter didn't incremented")
1886 |
1887 | send(xs, new Return("D"))
1888 |
1889 | assert.deepEqual(xs.toJSON(), {
1890 | started: 1,
1891 | stopped: 1,
1892 | value: "D"
1893 | }, "source value changed & stopped")
1894 |
1895 | assert.deepEqual(client.toJSON(), {
1896 | messages: [1, 2, 3],
1897 | errors: [],
1898 | ends: [true]
1899 | }, "message & end received")
1900 |
1901 | assert.equal(ys.value, 3, "counter incremented")
1902 | }
1903 |
1904 | var dropRepeats = signal.dropRepeats
1905 | exports["test dropRepeats"] = function(assert) {
1906 | var xs = new Subject({ value: 0 })
1907 | var ys = dropRepeats(xs)
1908 |
1909 | assert.deepEqual(xs.toJSON(), {
1910 | value: 0,
1911 | started: 0,
1912 | stopped: 0
1913 | }, "source in initial state")
1914 |
1915 | assert.equal(ys.value, 0, "filter is in initial state")
1916 |
1917 | var client = new Client()
1918 | connect(ys, client)
1919 |
1920 | assert.deepEqual(xs.toJSON(), {
1921 | value: 0,
1922 | started: 1,
1923 | stopped: 0
1924 | }, "source started")
1925 |
1926 | assert.deepEqual(client.toJSON(), {
1927 | messages: [],
1928 | errors: [],
1929 | ends: []
1930 | }, "no messages recevied")
1931 |
1932 | assert.equal(ys.value, 0, "filter is in initial state")
1933 |
1934 | send(xs, 0)
1935 |
1936 | assert.deepEqual(xs.toJSON(), {
1937 | value: 0,
1938 | started: 1,
1939 | stopped: 0
1940 | }, "source started")
1941 |
1942 | assert.deepEqual(client.toJSON(), {
1943 | messages: [],
1944 | errors: [],
1945 | ends: []
1946 | }, "no messages recevied")
1947 |
1948 | assert.equal(ys.value, 0, "filter is in initial state")
1949 |
1950 | send(xs, 1)
1951 |
1952 | assert.deepEqual(xs.toJSON(), {
1953 | value: 1,
1954 | started: 1,
1955 | stopped: 0
1956 | }, "source value changed")
1957 |
1958 | assert.deepEqual(client.toJSON(), {
1959 | messages: [1],
1960 | errors: [],
1961 | ends: []
1962 | }, "messages recevied")
1963 |
1964 | assert.equal(ys.value, 1, "filter value changed")
1965 |
1966 | send(xs, 2)
1967 |
1968 | assert.deepEqual(xs.toJSON(), {
1969 | value: 2,
1970 | started: 1,
1971 | stopped: 0
1972 | }, "source value changed")
1973 |
1974 | assert.deepEqual(client.toJSON(), {
1975 | messages: [1, 2],
1976 | errors: [],
1977 | ends: []
1978 | }, "messages recevied")
1979 |
1980 | assert.equal(ys.value, 2, "filter value changed")
1981 |
1982 | send(xs, 2)
1983 |
1984 | assert.deepEqual(xs.toJSON(), {
1985 | value: 2,
1986 | started: 1,
1987 | stopped: 0
1988 | }, "source value changed")
1989 |
1990 | assert.deepEqual(client.toJSON(), {
1991 | messages: [1, 2],
1992 | errors: [],
1993 | ends: []
1994 | }, "messages isn't recevied")
1995 |
1996 | assert.equal(ys.value, 2, "filter value didn't changed")
1997 |
1998 | send(xs, 2)
1999 |
2000 | assert.deepEqual(xs.toJSON(), {
2001 | value: 2,
2002 | started: 1,
2003 | stopped: 0
2004 | }, "source value changed")
2005 |
2006 | assert.deepEqual(client.toJSON(), {
2007 | messages: [1, 2],
2008 | errors: [],
2009 | ends: []
2010 | }, "messages isn't recevied")
2011 |
2012 | assert.equal(ys.value, 2, "filter value didn't changed")
2013 |
2014 | send(xs, 3)
2015 |
2016 | assert.deepEqual(xs.toJSON(), {
2017 | value: 3,
2018 | started: 1,
2019 | stopped: 0
2020 | }, "source value changed")
2021 |
2022 | assert.deepEqual(client.toJSON(), {
2023 | messages: [1, 2, 3],
2024 | errors: [],
2025 | ends: []
2026 | }, "messages recevied")
2027 |
2028 | assert.equal(ys.value, 3, "filter value changed")
2029 |
2030 | send(xs, 3)
2031 |
2032 | assert.deepEqual(xs.toJSON(), {
2033 | value: 3,
2034 | started: 1,
2035 | stopped: 0
2036 | }, "source value changed")
2037 |
2038 | assert.deepEqual(client.toJSON(), {
2039 | messages: [1, 2, 3],
2040 | errors: [],
2041 | ends: []
2042 | }, "messages isn't received")
2043 |
2044 | assert.equal(ys.value, 3, "filter value didn't change")
2045 |
2046 | send(xs, 4)
2047 |
2048 | assert.deepEqual(xs.toJSON(), {
2049 | value: 4,
2050 | started: 1,
2051 | stopped: 0
2052 | }, "source value changed")
2053 |
2054 | assert.deepEqual(client.toJSON(), {
2055 | messages: [1, 2, 3, 4],
2056 | errors: [],
2057 | ends: []
2058 | }, "messages recevied")
2059 |
2060 | assert.equal(ys.value, 4, "filter value changed")
2061 |
2062 | send(xs, new Return(3))
2063 |
2064 | assert.deepEqual(xs.toJSON(), {
2065 | value: 3,
2066 | started: 1,
2067 | stopped: 1
2068 | }, "source value changed")
2069 |
2070 | assert.deepEqual(client.toJSON(), {
2071 | messages: [1, 2, 3, 4, 3],
2072 | errors: [],
2073 | ends: [true]
2074 | }, "messages recevied")
2075 |
2076 | assert.equal(ys.value, 3, "filter value changed")
2077 | }
2078 |
2079 | var keepWhen = signal.keepWhen
2080 | exports["test keepWhen"] = function(assert) {
2081 | var setState = function(x) { send(state, x) }
2082 | var setX = function(x) { send(xs, x) }
2083 |
2084 | var state = new Subject({ value: false })
2085 | var xs = new Subject({ value: 0 })
2086 |
2087 | var ys = keepWhen(state, 10, xs)
2088 |
2089 | assert.deepEqual(state.toJSON(), {
2090 | started: 0,
2091 | stopped: 0,
2092 | value: false
2093 | }, "state in initial state")
2094 |
2095 | assert.deepEqual(xs.toJSON(), {
2096 | started: 0,
2097 | stopped: 0,
2098 | value: 0
2099 | }, "source is in initial state")
2100 |
2101 | assert.equal(ys.value, 10, "filter set in inital state")
2102 |
2103 | var client = new Client()
2104 | connect(ys, client)
2105 | setX(1)
2106 |
2107 | assert.deepEqual(state.toJSON(), {
2108 | started: 1,
2109 | stopped: 0,
2110 | value: false
2111 | }, "state in initial state")
2112 |
2113 | assert.deepEqual(xs.toJSON(), {
2114 | started: 1,
2115 | stopped: 0,
2116 | value: 1
2117 | }, "source value changed")
2118 |
2119 | assert.equal(ys.value, 10, "filter didn't change")
2120 |
2121 | setX(2)
2122 |
2123 | assert.deepEqual(state.toJSON(), {
2124 | started: 1,
2125 | stopped: 0,
2126 | value: false
2127 | }, "state in initial state")
2128 |
2129 | assert.deepEqual(xs.toJSON(), {
2130 | started: 1,
2131 | stopped: 0,
2132 | value: 2
2133 | }, "source value changed")
2134 |
2135 | assert.equal(ys.value, 10, "filter didn't change")
2136 |
2137 | setState(true)
2138 |
2139 | assert.deepEqual(state.toJSON(), {
2140 | started: 1,
2141 | stopped: 0,
2142 | value: true
2143 | }, "state in initial state")
2144 |
2145 | assert.deepEqual(xs.toJSON(), {
2146 | started: 1,
2147 | stopped: 0,
2148 | value: 2
2149 | }, "source value didn't change")
2150 |
2151 | assert.equal(ys.value, 2, "filter changed")
2152 |
2153 |
2154 | setX(3)
2155 |
2156 | assert.deepEqual(state.toJSON(), {
2157 | started: 1,
2158 | stopped: 0,
2159 | value: true
2160 | }, "state in initial state")
2161 |
2162 | assert.deepEqual(xs.toJSON(), {
2163 | started: 1,
2164 | stopped: 0,
2165 | value: 3
2166 | }, "source value changed")
2167 |
2168 | assert.equal(ys.value, 3, "filter value changed")
2169 |
2170 | setX(3)
2171 |
2172 | assert.deepEqual(state.toJSON(), {
2173 | started: 1,
2174 | stopped: 0,
2175 | value: true
2176 | }, "state is on")
2177 |
2178 | assert.deepEqual(xs.toJSON(), {
2179 | started: 1,
2180 | stopped: 0,
2181 | value: 3
2182 | }, "source value changed")
2183 |
2184 | assert.equal(ys.value, 3, "filter changed")
2185 |
2186 | setState(false)
2187 |
2188 | assert.deepEqual(state.toJSON(), {
2189 | started: 1,
2190 | stopped: 0,
2191 | value: false
2192 | }, "state is off")
2193 |
2194 | assert.deepEqual(xs.toJSON(), {
2195 | started: 1,
2196 | stopped: 0,
2197 | value: 3
2198 | }, "source value didn't change")
2199 |
2200 | assert.equal(ys.value, 3, "filter didn't change")
2201 |
2202 | setX(4)
2203 |
2204 | assert.deepEqual(state.toJSON(), {
2205 | started: 1,
2206 | stopped: 0,
2207 | value: false
2208 | }, "state is off")
2209 |
2210 | assert.deepEqual(xs.toJSON(), {
2211 | started: 1,
2212 | stopped: 0,
2213 | value: 4
2214 | }, "source value changed")
2215 |
2216 | assert.equal(ys.value, 3, "filter didn't changed")
2217 |
2218 | setState(false)
2219 |
2220 | assert.deepEqual(state.toJSON(), {
2221 | started: 1,
2222 | stopped: 0,
2223 | value: false
2224 | }, "state is off")
2225 |
2226 | assert.deepEqual(xs.toJSON(), {
2227 | started: 1,
2228 | stopped: 0,
2229 | value: 4
2230 | }, "source value changed")
2231 |
2232 | assert.equal(ys.value, 3, "filter changed")
2233 |
2234 | setState(true)
2235 |
2236 | assert.deepEqual(state.toJSON(), {
2237 | started: 1,
2238 | stopped: 0,
2239 | value: true
2240 | }, "state is on")
2241 |
2242 | assert.deepEqual(xs.toJSON(), {
2243 | started: 1,
2244 | stopped: 0,
2245 | value: 4
2246 | }, "source value didn't changed")
2247 |
2248 | assert.equal(ys.value, 4, "filter changed")
2249 |
2250 | setState(false)
2251 |
2252 | assert.deepEqual(state.toJSON(), {
2253 | started: 1,
2254 | stopped: 0,
2255 | value: false
2256 | }, "state is off")
2257 |
2258 | assert.deepEqual(xs.toJSON(), {
2259 | started: 1,
2260 | stopped: 0,
2261 | value: 4
2262 | }, "source value didn't changed")
2263 |
2264 | assert.equal(ys.value, 4, "filter didn't changed")
2265 |
2266 | setState(new Return(true))
2267 |
2268 | assert.deepEqual(state.toJSON(), {
2269 | started: 1,
2270 | stopped: 1,
2271 | value: true
2272 | }, "state is on & stopped")
2273 |
2274 | assert.deepEqual(xs.toJSON(), {
2275 | started: 1,
2276 | stopped: 0,
2277 | value: 4
2278 | }, "source value didn't changed")
2279 |
2280 | assert.equal(ys.value, 4, "filter changed")
2281 |
2282 | assert.deepEqual(client.toJSON(), {
2283 | messages: [2, 3, 3, 4, 4],
2284 | errors: [],
2285 | ends: []
2286 | }, "received all the messages")
2287 |
2288 | setX(5)
2289 |
2290 | assert.deepEqual(state.toJSON(), {
2291 | started: 1,
2292 | stopped: 1,
2293 | value: true
2294 | }, "state is on & stopped")
2295 |
2296 | assert.deepEqual(xs.toJSON(), {
2297 | started: 1,
2298 | stopped: 0,
2299 | value: 5
2300 | }, "source value changed")
2301 |
2302 | assert.equal(ys.value, 5, "filter changed")
2303 |
2304 | setX(Return(5))
2305 |
2306 | assert.deepEqual(state.toJSON(), {
2307 | started: 1,
2308 | stopped: 1,
2309 | value: true
2310 | }, "state is on & stopped")
2311 |
2312 | assert.deepEqual(xs.toJSON(), {
2313 | started: 1,
2314 | stopped: 1,
2315 | value: 5
2316 | }, "source value changed & stopped")
2317 |
2318 | assert.equal(ys.value, 5, "filter changed")
2319 |
2320 | assert.deepEqual(client.toJSON(), {
2321 | messages: [2, 3, 3, 4, 4, 5, 5],
2322 | errors: [],
2323 | ends: [true]
2324 | }, "received all the messages & end")
2325 | }
2326 |
2327 | var dropWhen = signal.dropWhen
2328 | exports["test dropWhen"] = function(assert) {
2329 | var setState = function(x) { send(state, x) }
2330 | var setX = function(x) { send(xs, x) }
2331 |
2332 | var state = new Subject({ value: false })
2333 | var xs = new Subject({ value: 0 })
2334 |
2335 | var ys = dropWhen(state, 10, xs)
2336 |
2337 | assert.deepEqual(state.toJSON(), {
2338 | started: 0,
2339 | stopped: 0,
2340 | value: false
2341 | }, "state in initial state")
2342 |
2343 | assert.deepEqual(xs.toJSON(), {
2344 | started: 0,
2345 | stopped: 0,
2346 | value: 0
2347 | }, "source is in initial state")
2348 |
2349 | assert.equal(ys.value, 0, "filter inherts value as state is false")
2350 |
2351 | var client = new Client()
2352 | connect(ys, client)
2353 | setX(1)
2354 |
2355 | assert.deepEqual(state.toJSON(), {
2356 | started: 1,
2357 | stopped: 0,
2358 | value: false
2359 | }, "state in initial state")
2360 |
2361 | assert.deepEqual(xs.toJSON(), {
2362 | started: 1,
2363 | stopped: 0,
2364 | value: 1
2365 | }, "source value changed")
2366 |
2367 | assert.equal(ys.value, 1, "filter changed")
2368 |
2369 | setX(2)
2370 |
2371 | assert.deepEqual(state.toJSON(), {
2372 | started: 1,
2373 | stopped: 0,
2374 | value: false
2375 | }, "state in initial state")
2376 |
2377 | assert.deepEqual(xs.toJSON(), {
2378 | started: 1,
2379 | stopped: 0,
2380 | value: 2
2381 | }, "source value changed")
2382 |
2383 | assert.equal(ys.value, 2, "filter chnaged")
2384 |
2385 | setState(true)
2386 |
2387 | assert.deepEqual(state.toJSON(), {
2388 | started: 1,
2389 | stopped: 0,
2390 | value: true
2391 | }, "state signal changed")
2392 |
2393 | assert.deepEqual(xs.toJSON(), {
2394 | started: 1,
2395 | stopped: 0,
2396 | value: 2
2397 | }, "source value didn't change")
2398 |
2399 | assert.equal(ys.value, 2, "filter didn't change")
2400 |
2401 |
2402 | setX(3)
2403 |
2404 | assert.deepEqual(state.toJSON(), {
2405 | started: 1,
2406 | stopped: 0,
2407 | value: true
2408 | }, "state is true")
2409 |
2410 | assert.deepEqual(xs.toJSON(), {
2411 | started: 1,
2412 | stopped: 0,
2413 | value: 3
2414 | }, "source value changed")
2415 |
2416 | assert.equal(ys.value, 2, "filter didn't changed")
2417 |
2418 | setX(3)
2419 |
2420 | assert.deepEqual(state.toJSON(), {
2421 | started: 1,
2422 | stopped: 0,
2423 | value: true
2424 | }, "state is on")
2425 |
2426 | assert.deepEqual(xs.toJSON(), {
2427 | started: 1,
2428 | stopped: 0,
2429 | value: 3
2430 | }, "source value changed")
2431 |
2432 | assert.equal(ys.value, 2, "filter didn't change")
2433 |
2434 | setState(false)
2435 |
2436 | assert.deepEqual(state.toJSON(), {
2437 | started: 1,
2438 | stopped: 0,
2439 | value: false
2440 | }, "state is off")
2441 |
2442 | assert.deepEqual(xs.toJSON(), {
2443 | started: 1,
2444 | stopped: 0,
2445 | value: 3
2446 | }, "source value didn't change")
2447 |
2448 | assert.equal(ys.value, 3, "filter changed")
2449 |
2450 | setX(4)
2451 |
2452 | assert.deepEqual(state.toJSON(), {
2453 | started: 1,
2454 | stopped: 0,
2455 | value: false
2456 | }, "state is off")
2457 |
2458 | assert.deepEqual(xs.toJSON(), {
2459 | started: 1,
2460 | stopped: 0,
2461 | value: 4
2462 | }, "source value changed")
2463 |
2464 | assert.equal(ys.value, 4, "filter changed")
2465 |
2466 | setState(false)
2467 |
2468 | assert.deepEqual(state.toJSON(), {
2469 | started: 1,
2470 | stopped: 0,
2471 | value: false
2472 | }, "state is off")
2473 |
2474 | assert.deepEqual(xs.toJSON(), {
2475 | started: 1,
2476 | stopped: 0,
2477 | value: 4
2478 | }, "source value changed")
2479 |
2480 | assert.equal(ys.value, 4, "filter changed")
2481 |
2482 | setState(true)
2483 |
2484 | assert.deepEqual(state.toJSON(), {
2485 | started: 1,
2486 | stopped: 0,
2487 | value: true
2488 | }, "state is on")
2489 |
2490 | assert.deepEqual(xs.toJSON(), {
2491 | started: 1,
2492 | stopped: 0,
2493 | value: 4
2494 | }, "source value didn't changed")
2495 |
2496 | assert.equal(ys.value, 4, "filter didn't change")
2497 |
2498 | setState(false)
2499 |
2500 | assert.deepEqual(state.toJSON(), {
2501 | started: 1,
2502 | stopped: 0,
2503 | value: false
2504 | }, "state is off")
2505 |
2506 | assert.deepEqual(xs.toJSON(), {
2507 | started: 1,
2508 | stopped: 0,
2509 | value: 4
2510 | }, "source value didn't changed")
2511 |
2512 | assert.equal(ys.value, 4, "filter changed")
2513 |
2514 | setState(new Return(true))
2515 |
2516 | assert.deepEqual(state.toJSON(), {
2517 | started: 1,
2518 | stopped: 1,
2519 | value: true
2520 | }, "state is on & stopped")
2521 |
2522 | assert.deepEqual(xs.toJSON(), {
2523 | started: 1,
2524 | stopped: 0,
2525 | value: 4
2526 | }, "source value didn't changed")
2527 |
2528 | assert.equal(ys.value, 4, "filter didn't changed")
2529 |
2530 | assert.deepEqual(client.toJSON(), {
2531 | messages: [1, 2, 3, 4, 4],
2532 | errors: [],
2533 | ends: []
2534 | }, "received all the messages & end")
2535 |
2536 | setX(5)
2537 |
2538 | assert.deepEqual(state.toJSON(), {
2539 | started: 1,
2540 | stopped: 1,
2541 | value: true
2542 | }, "state is on & stopped")
2543 |
2544 | assert.deepEqual(xs.toJSON(), {
2545 | started: 1,
2546 | stopped: 0,
2547 | value: 5
2548 | }, "source value changed")
2549 |
2550 | assert.equal(ys.value, 4, "filter didn't changed")
2551 |
2552 | setX(new Return(6))
2553 |
2554 | assert.deepEqual(state.toJSON(), {
2555 | started: 1,
2556 | stopped: 1,
2557 | value: true
2558 | }, "state is on & stopped")
2559 |
2560 | assert.deepEqual(xs.toJSON(), {
2561 | started: 1,
2562 | stopped: 1,
2563 | value: 6
2564 | }, "source value changed & source stopped")
2565 |
2566 | assert.equal(ys.value, 4, "filter didn't changed")
2567 |
2568 | assert.deepEqual(client.toJSON(), {
2569 | messages: [1, 2, 3, 4, 4],
2570 | errors: [],
2571 | ends: [true]
2572 | }, "received all the messages & end")
2573 | }
2574 |
2575 |
2576 | var sampleOn = signal.sampleOn
2577 | exports["test sampleOn"] = function(assert) {
2578 | var ticks = new Subject({ value: null })
2579 | var xs = new Subject({ value: 0 })
2580 | var ys = sampleOn(ticks, xs)
2581 |
2582 | assert.deepEqual(ticks.toJSON(), {
2583 | started: 0,
2584 | stopped: 0,
2585 | value: null
2586 | }, "ticks is in initial state")
2587 |
2588 | assert.deepEqual(xs.toJSON(), {
2589 | started: 0,
2590 | stopped: 0,
2591 | value: 0
2592 | }, "source is in initial state")
2593 |
2594 | assert.deepEqual(ys.value, 0, "sampler inherits initial value from source")
2595 |
2596 | var client = new Client()
2597 |
2598 | connect(ys, client)
2599 |
2600 |
2601 | assert.deepEqual(ticks.toJSON(), {
2602 | started: 1,
2603 | stopped: 0,
2604 | value: null
2605 | }, "ticks started")
2606 |
2607 | assert.deepEqual(xs.toJSON(), {
2608 | started: 1,
2609 | stopped: 0,
2610 | value: 0
2611 | }, "source started")
2612 |
2613 | assert.deepEqual(ys.value, 0, "sampler value didn't change")
2614 |
2615 | send(ticks, null)
2616 |
2617 | assert.deepEqual(client.toJSON(), {
2618 | messages: [0],
2619 | errors: [],
2620 | ends: []
2621 | }, "client received message")
2622 |
2623 | send(xs, 2)
2624 |
2625 | assert.deepEqual(ticks.toJSON(), {
2626 | started: 1,
2627 | stopped: 0,
2628 | value: null
2629 | }, "ticks didn't change")
2630 |
2631 | assert.deepEqual(xs.toJSON(), {
2632 | started: 1,
2633 | stopped: 0,
2634 | value: 2
2635 | }, "source value changed")
2636 |
2637 |
2638 | assert.deepEqual(client.toJSON(), {
2639 | messages: [0],
2640 | errors: [],
2641 | ends: []
2642 | }, "client didn't receive message")
2643 |
2644 | send(xs, 3)
2645 |
2646 | assert.deepEqual(ticks.toJSON(), {
2647 | started: 1,
2648 | stopped: 0,
2649 | value: null
2650 | }, "ticks didn't change")
2651 |
2652 | assert.deepEqual(xs.toJSON(), {
2653 | started: 1,
2654 | stopped: 0,
2655 | value: 3
2656 | }, "source value changed")
2657 |
2658 |
2659 | assert.deepEqual(client.toJSON(), {
2660 | messages: [0],
2661 | errors: [],
2662 | ends: []
2663 | }, "client didn't received a message")
2664 |
2665 | send(ticks, 1)
2666 |
2667 | assert.deepEqual(ticks.toJSON(), {
2668 | started: 1,
2669 | stopped: 0,
2670 | value: 1
2671 | }, "ticks changed")
2672 |
2673 | assert.deepEqual(xs.toJSON(), {
2674 | started: 1,
2675 | stopped: 0,
2676 | value: 3
2677 | }, "source value didn't changed")
2678 |
2679 |
2680 | assert.deepEqual(client.toJSON(), {
2681 | messages: [0, 3],
2682 | errors: [],
2683 | ends: []
2684 | }, "client received a message")
2685 |
2686 | send(xs, new Return(4))
2687 |
2688 | assert.deepEqual(ticks.toJSON(), {
2689 | started: 1,
2690 | stopped: 0,
2691 | value: 1
2692 | }, "ticks didn't change")
2693 |
2694 | assert.deepEqual(xs.toJSON(), {
2695 | started: 1,
2696 | stopped: 1,
2697 | value: 4
2698 | }, "source value changed & stopped")
2699 |
2700 |
2701 | assert.deepEqual(client.toJSON(), {
2702 | messages: [0, 3],
2703 | errors: [],
2704 | ends: []
2705 | }, "client didn't received message")
2706 |
2707 | send(ticks, 2)
2708 |
2709 | assert.deepEqual(ticks.toJSON(), {
2710 | started: 1,
2711 | stopped: 0,
2712 | value: 2
2713 | }, "ticks didn't change")
2714 |
2715 | assert.deepEqual(xs.toJSON(), {
2716 | started: 1,
2717 | stopped: 1,
2718 | value: 4
2719 | }, "source value didn't change")
2720 |
2721 |
2722 | assert.deepEqual(client.toJSON(), {
2723 | messages: [0, 3, 4],
2724 | errors: [],
2725 | ends: []
2726 | }, "client received message")
2727 |
2728 | send(ticks, new Return(3))
2729 |
2730 | assert.deepEqual(ticks.toJSON(), {
2731 | started: 1,
2732 | stopped: 1,
2733 | value: 3
2734 | }, "ticks changed")
2735 |
2736 | assert.deepEqual(xs.toJSON(), {
2737 | started: 1,
2738 | stopped: 1,
2739 | value: 4
2740 | }, "source value didn't change")
2741 |
2742 |
2743 | assert.deepEqual(client.toJSON(), {
2744 | messages: [0, 3, 4, 4],
2745 | errors: [],
2746 | ends: [true]
2747 | }, "client received message and ended")
2748 | }
2749 |
--------------------------------------------------------------------------------
/test/tap.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | require("retape")(require("./index"))
--------------------------------------------------------------------------------
/time.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var Signal = require("./signal").Signal
4 |
5 | // Type alias to make it clearer when you are working with time values.
6 | // Using the `Time` constants instead of raw numbers is very highly recommended.
7 | var Time = Number
8 | exports.Time = Time
9 |
10 | // Units of time, making it easier to specify things like a
11 | // half-second `(500 * milliseconds)` without remembering Elm’s
12 | // underlying units of time.
13 | // millisecond : Time
14 | var millisecond = 1
15 | exports.millisecond = millisecond
16 |
17 | // second : Time
18 | var second = 1000 * millisecond
19 | exports.second = second
20 |
21 | // minute : Time
22 | var minute = 60 * second
23 | exports.minute = minute
24 |
25 | // hour : Time
26 | var hour = 60 * minute
27 | exports.hour = hour
28 |
29 | function inMilliseconds(time) { return time }
30 | exports.inMilliseconds = inMilliseconds
31 |
32 | function inSeconds(time) { return time / second }
33 | exports.inSeconds = inSeconds
34 |
35 | function inMinutes(time) { return time / minute }
36 | exports.inMinutes = inMinutes
37 |
38 | function inHours(time) { return time / hour }
39 | exports.inHours = inHours
40 |
41 | // Takes desired number of frames per second (fps). The
42 | // resulting signal gives a sequence of time deltas as
43 | // quickly as possible until it reaches the desired FPS.
44 | // A time delta is the time between the last frame and the
45 | // current frame.
46 | // int -> Signal Time
47 | var fps = function(n) {
48 | var ms = 1000 / n
49 |
50 | return new Signal(function(next) {
51 | var before = Date.now()
52 |
53 | function tick() {
54 | var now = Date.now()
55 | var diff = before - now
56 | before = now
57 |
58 | if (next(diff) !== Signal.Break)
59 | setTimeout(tick, ms)
60 | }
61 |
62 | setTimeout(tick, ms)
63 | }, 0)
64 | }
65 | exports.fps = fps
66 |
67 | // Same as the fps function, but you can turn it on and off.
68 | // Allows you to do brief animations based on user input without
69 | // major inefficiencies. The first time delta after a pause is always
70 | // zero, no matter how long the pause was. This way summing the deltas
71 | // will actually give the amount of time that the output signal has been
72 | // running.
73 | // fpsWhen : number -> Signal Bool -> Signal Time
74 | function fpsWhen(n, state) {
75 | return keepWhen(state, fps(n))
76 | }
77 | exports.fpsWhen = fpsWhen
78 |
79 | function every(ms) {
80 | return new Signal(function(next) {
81 | function tick() {
82 | if (Signal.Break !== next(Date.now()))
83 | setTimeout(tick, ms)
84 | }
85 |
86 | setTimeout(tick, ms)
87 | }, Date.now())
88 | }
89 | exports.every = every
90 |
91 | function fps(n) {
92 | var tickTimes = every(1000 / n)
93 | var states = foldp(function(result, time) {
94 | return [result, result[0] - time]
95 | }, [0, tickTimes.value], tickTimes)
96 |
97 | return map(field(1), states)
98 | }
99 | exports.fps = fps
100 |
101 | // Add a timestamp to any signal. Timestamps increase monotonically.
102 | // Each timestamp is related to a specfic event, so Mouse.x and Mouse.y
103 | // will always have the same timestamp because they both rely on the same
104 | // underlying event.
105 | // timestamp : Signal a -> Signal (Time, a)
106 | function timestamp(input) {
107 | return map(function(value) {
108 | return [Date.now(), value]
109 | }, input)
110 | }
111 | exports.timestamp = timestamp
112 |
113 | // Delay a signal by a certain amount of time. So (delay second Mouse.clicks) will
114 | // update one second later than any mouse click.
115 | // delay : Time -> Signal a -> Signal a
116 | function delay(ms, input) {
117 | return new Signal(function(next) {
118 | var result = void(0)
119 | spawn(function(value) {
120 | setTimeout(function() { result = next(value) }, ms)
121 | return result
122 | }, input)
123 | }, input.value)
124 | }
125 | exports.delay = delay
126 |
127 | function since(ms, input) {
128 | var on = map(True, input)
129 | var off = map(False, delay(ms, input))
130 | return merge(on, off)
131 | }
132 | exports.since = since
133 |
--------------------------------------------------------------------------------
/window.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var Event = require("./event")
4 | var field = require("oops").field
5 | var map = require("./signal").map
6 |
7 | var root = document.documentElement
8 | var Tuple = Array
9 |
10 |
11 | // The current dimensions of the window (i.e. the area viewable
12 | // to the user, not including scroll bars).
13 | // dimensions : Signal [Int,Int]
14 | var dimensions = map(function() {
15 | return new Tuple(root.clientWidth, root.clientHeight)
16 | }, Event(window, "resize"))
17 | exports.dimensions = dimensions
18 |
19 | // The current width of the window.
20 | // width : Signal Int
21 | var width = map(field(0), dimensions)
22 | exports.width = width
23 |
24 | // The current height of the window.
25 | // height : Signal Int
26 | var height = map(field(1), dimensions)
27 | exports.height = height
28 |
--------------------------------------------------------------------------------