<commit_before>Bacon.js
========

A small reactive programming lib for js.

Inspired largely by RxJs, but includes the `EventStream` and `Property`
concepts from [reactive-bacon](https://github.com/raimohanska/reactive-bacon).

Please note that Bacon.js is in a very early stage and has a very limited feature set as of yet. 
So at this stage, comparing bacon.js to RxJs is like comparing a bacon sandwich to a 
three-course dinner with <NAME>. 

But hey, where's the bacon?

- [CoffeeScript src](https://github.com/raimohanska/bacon.js/blob/master/src/Bacon.coffee)
- [Generated js](https://github.com/raimohanska/bacon.js/blob/master/lib/Bacon.js)
- [Specs](https://github.com/raimohanska/bacon.js/blob/master/spec/BaconSpec.coffee)
- [Examples](https://github.com/raimohanska/bacon.js/blob/master/examples/examples.html)

API
===

Creating streams
----------------

`$.asEventStream("click")` creates an EventStream from events on a
jQuery or Zepto.js object

`Bacon.sequentially(interval, values)` creates a stream containing given
values (given as array). Delivered with given interval (in milliseconds)

`Bacon.repeatedly(interval, values)` repeats given elements indefinitely
with given interval (in millis)

`Bacon.fromPoll(interval, f)` polls given function with given interval.
Function should return Events: either Next or End.

`Bacon.later(delay, value)` creates a single-element stream that
produces given value after given delay (milliseconds).

`Bacon.pushStream()` creates a pushable stream. You can push values by
using the `push` function of the pushable stream. You can send the End event by calling `end`

`new Bacon.EventStream(subscribe)` creates an event stream with the given 
subscribe function.

EventStream
-----------

`Bacon.EventStream` a stream of events. See methods below.

`stream.subscribe(f)` subscribes given handler function to
event stream. Function will receive Event objects (see below).
The subscribe() call returns a `unsubscribe` function that you can
call to unsubscribe. You can also unsubscribe by returning
`Bacon.noMore` from the handler function as a reply to an Event.

`stream.map(f)` maps values using given function

`stream.filter(f)` filters values using given predicate function

`stream.takeWhile(f)` takes while given predicate function holds true

`stream.take(n)` takes at most n elements from the stream

`stream.merge(stream2)` merges two streams into on that delivers events
from both

`stream.flatMap(f)` for each element in the source stream, spawn a new
stream using the function `f`. Collect events from each of the spawned
streams into the result stream

`stream.toProperty(initialValue)` creates a Property based on the
EventStream. You can optionally pass an initial value

Property
--------

`Bacon.Property` a reactive property. Has the concept of "current value"

`property.subscribe(f)` subscribes side-effeect to property. If there's
a current value, an `Initial` event will be pushed immediately.

Event
-----

`Bacon.Event` has subclasses `Next`, `End` and `Initial`

`Bacon.Next` next value in an EventStream of a Property. Call isNext() to
distinguish a Next event from other eventss.

`Bacon.End` an end-of-stream event of EventStream or Property. Call isEnd() to
distinguish an End from other events.

`Bacon.Initial` the initial (current) value of a Property. Call isInitial() to
distinguish from other events. Only sent immediately after subscription
to a Property.

`Event.value` the value associated with a Next or Initial event

Cleaning up the frying pan
--------------------------

As described above, a subscriber can signal the loss of interest in new events 
in any of these two ways:

1. Return `Bacon.noMore` from the handler function
2. Call the `dispose()` function that was returned by the `subscribe()`
   call.

Based on my experience on RxJs coding, an actual side-effect subscriber
in application-code never does this. So the business of unsubscribing is
mostly internal business and you can ignore it unless you're working on
a custom stream implementation or a stream combinator. In that case, I
welcome you to contribute your stuff to the frying pan here.

Examples
========

See [Examples](https://github.com/raimohanska/bacon.js/blob/master/examples/examples.html)

~~~ coffeescript
        $("#clikme").asEventStream("click").subscribe(function(event) {
          alert("mmmm... bacon!")
        })

        function always(value) { return function() { return value }}
        
        function keyCodeIs(keyCode) { 
          return function(event) { return event.keyCode == keyCode }
        }

        function keyDowns(keyCode) { 
          return $(document).asEventStream("keydown").filter(keyCodeIs(keyCode))
        }

        function keyUps(keyCode) { 
          return $(document).asEventStream("keyup").filter(keyCodeIs(keyCode))
        }

        function keyState(keyCode) { 
          return keyDowns(keyCode).map(always("DOWN"))
            .merge(keyUps(keyCode).map(always("UP"))).toProperty("UP")
        }

        keyState(32).subscribe(function(state) {
          $("#state").text(state.value)
        })
~~~

Build
=====

Build the coffeescript source into javascript:

    cake build

Result javascript file will be generated in `lib` directory.

Test
====

Run unit tests:

    npm install&&npm test

Dependencies
============

Runtime: jQuery or Zepto.js (optional; just for jQ/Zepto bindings)
Build/test: node.js, npm, cake

Contribute
==========

Use GitHub issues and Pull Requests.

TODO
====

- Browser tests
- Interactive examples, like in my devday-rx presentation
- API doc with interactive examples
- An animated gif of frying bacon
- More combinators
    - takeUntil
    - switch
    - delay
    - combineLatest
    - scan
- Property combinators
    - map<commit_msg>doc takeUntil<commit_after>Bacon.js
========

A small reactive programming lib for js.

Inspired largely by RxJs, but includes the `EventStream` and `Property`
concepts from [reactive-bacon](https://github.com/raimohanska/reactive-bacon).

Please note that Bacon.js is in a very early stage and has a very limited feature set as of yet. 
So at this stage, comparing bacon.js to RxJs is like comparing a bacon sandwich to a 
three-course dinner with <NAME>. 

But hey, where's the bacon?

- [CoffeeScript src](https://github.com/raimohanska/bacon.js/blob/master/src/Bacon.coffee)
- [Generated js](https://github.com/raimohanska/bacon.js/blob/master/lib/Bacon.js)
- [Specs](https://github.com/raimohanska/bacon.js/blob/master/spec/BaconSpec.coffee)
- [Examples](https://github.com/raimohanska/bacon.js/blob/master/examples/examples.html)

API
===

Creating streams
----------------

`$.asEventStream("click")` creates an EventStream from events on a
jQuery or Zepto.js object

`Bacon.sequentially(interval, values)` creates a stream containing given
values (given as array). Delivered with given interval (in milliseconds)

`Bacon.repeatedly(interval, values)` repeats given elements indefinitely
with given interval (in millis)

`Bacon.fromPoll(interval, f)` polls given function with given interval.
Function should return Events: either Next or End.

`Bacon.later(delay, value)` creates a single-element stream that
produces given value after given delay (milliseconds).

`Bacon.pushStream()` creates a pushable stream. You can push values by
using the `push` function of the pushable stream. You can send the End event by calling `end`

`new Bacon.EventStream(subscribe)` creates an event stream with the given 
subscribe function.

EventStream
-----------

`Bacon.EventStream` a stream of events. See methods below.

`stream.subscribe(f)` subscribes given handler function to
event stream. Function will receive Event objects (see below).
The subscribe() call returns a `unsubscribe` function that you can
call to unsubscribe. You can also unsubscribe by returning
`Bacon.noMore` from the handler function as a reply to an Event.

`stream.map(f)` maps values using given function

`stream.filter(f)` filters values using given predicate function

`stream.takeWhile(f)` takes while given predicate function holds true

`stream.take(n)` takes at most n elements from the stream

`stream.merge(stream2)` merges two streams into on that delivers events
from both

`stream.flatMap(f)` for each element in the source stream, spawn a new
stream using the function `f`. Collect events from each of the spawned
streams into the result stream

`stream.takeUntil(stream2)` takes elements from source until a Next event 
appears in the other stream. If other stream ends without value, it is
ignored

`stream.toProperty(initialValue)` creates a Property based on the
EventStream. You can optionally pass an initial value

Property
--------

`Bacon.Property` a reactive property. Has the concept of "current value"

`property.subscribe(f)` subscribes side-effeect to property. If there's
a current value, an `Initial` event will be pushed immediately.

Event
-----

`Bacon.Event` has subclasses `Next`, `End` and `Initial`

`Bacon.Next` next value in an EventStream of a Property. Call isNext() to
distinguish a Next event from other eventss.

`Bacon.End` an end-of-stream event of EventStream or Property. Call isEnd() to
distinguish an End from other events.

`Bacon.Initial` the initial (current) value of a Property. Call isInitial() to
distinguish from other events. Only sent immediately after subscription
to a Property.

`Event.value` the value associated with a Next or Initial event

Cleaning up the frying pan
--------------------------

As described above, a subscriber can signal the loss of interest in new events 
in any of these two ways:

1. Return `Bacon.noMore` from the handler function
2. Call the `dispose()` function that was returned by the `subscribe()`
   call.

Based on my experience on RxJs coding, an actual side-effect subscriber
in application-code never does this. So the business of unsubscribing is
mostly internal business and you can ignore it unless you're working on
a custom stream implementation or a stream combinator. In that case, I
welcome you to contribute your stuff to the frying pan here.

Examples
========

See [Examples](https://github.com/raimohanska/bacon.js/blob/master/examples/examples.html)

~~~ coffeescript
        $("#clikme").asEventStream("click").subscribe(function(event) {
          alert("mmmm... bacon!")
        })

        function always(value) { return function() { return value }}
        
        function keyCodeIs(keyCode) { 
          return function(event) { return event.keyCode == keyCode }
        }

        function keyDowns(keyCode) { 
          return $(document).asEventStream("keydown").filter(keyCodeIs(keyCode))
        }

        function keyUps(keyCode) { 
          return $(document).asEventStream("keyup").filter(keyCodeIs(keyCode))
        }

        function keyState(keyCode) { 
          return keyDowns(keyCode).map(always("DOWN"))
            .merge(keyUps(keyCode).map(always("UP"))).toProperty("UP")
        }

        keyState(32).subscribe(function(state) {
          $("#state").text(state.value)
        })
~~~

Build
=====

Build the coffeescript source into javascript:

    cake build

Result javascript file will be generated in `lib` directory.

Test
====

Run unit tests:

    npm install&&npm test

Dependencies
============

Runtime: jQuery or Zepto.js (optional; just for jQ/Zepto bindings)
Build/test: node.js, npm, cake

Contribute
==========

Use GitHub issues and Pull Requests.

TODO
====

- Browser tests
- Interactive examples, like in my devday-rx presentation
- API doc with interactive examples
- An animated gif of frying bacon
- More combinators
    - switch
    - delay
    - combineLatest
    - scan
- Property combinators
    - map
{'fraction_non_alphanumeric': 0.1047351866020573, 'fraction_numerical': 0.0009351355946612259, 'mean_word_length': 3.8055555555555554, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 12, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6898562', 'n_tokens_mistral': 3419, 'n_tokens_neox': 3264, 'n_words': 1300}