├── .gitignore
├── index.js
├── package.json
├── lib
├── RegExp.js
├── Math.js
├── operators.js
├── Function.js
└── Array.js
├── test.js
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | exports.Array = require("./lib/Array")
2 | exports.ops =
3 | exports.operators = require("./lib/operators")
4 | exports.Function = require("./lib/Function")
5 | exports.Math = require("./lib/Math")
6 | exports.RegExp = require("./lib/RegExp")
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "author": "Jed Schmidt
(http://jed.is)",
3 | "name": "emit",
4 | "description": "A reactive toolkit for JavaScript",
5 | "version": "0.0.2",
6 | "keywords": [
7 | "functional",
8 | "reactive",
9 | "emitter",
10 | "emit"
11 | ],
12 | "homepage": "https://emitjs.org",
13 | "repository": {
14 | "type": "git",
15 | "url": "git://github.com/jed/emit.git"
16 | },
17 | "engines": {
18 | "node": "~0.6.6"
19 | },
20 | "dependencies": {},
21 | "devDependencies": {}
22 | }
--------------------------------------------------------------------------------
/lib/RegExp.js:
--------------------------------------------------------------------------------
1 | var apply = require("./Function").prototype.apply
2 | , original = /./.constructor
3 | , methods = ["exec", "toString", "test"]
4 | , i = methods.length
5 |
6 | function RegExp() {
7 | return apply.call(original, null, arguments)
8 | }
9 |
10 | while (i--) !function(name) {
11 | RegExp.prototype[name] = function() {
12 | var method = original[name]
13 |
14 | exports[name] = function() {
15 | return apply.call(method, this, Array.apply(null, arguments))
16 | }
17 | }
18 | }(methods[i])
19 |
20 | module.exports = RegExp
--------------------------------------------------------------------------------
/lib/Math.js:
--------------------------------------------------------------------------------
1 | var apply = require("./Function").prototype.apply
2 | , methods, i
3 |
4 | methods = ["LN10", "PI", "E", "LOG10E", "SQRT2", "LOG2E", "SQRT1_2", "LN2"]
5 | i = methods.length
6 |
7 | while (i--) exports[methods[i]] = Math[methods[i]]
8 |
9 | methods = ["cos", "pow", "log", "tan", "sqrt", "ceil", "asin", "abs", "max", "exp", "atan2", "random", "round", "floor", "acos", "atan", "min", "sin"]
10 | i = methods.length
11 |
12 | while (i--) !function(name) {
13 | var method = Math[name]
14 | exports[name] = function(){ return apply.call(method, null, arguments) }
15 | }(methods[i])
--------------------------------------------------------------------------------
/lib/operators.js:
--------------------------------------------------------------------------------
1 | var apply = require("./Function").prototype.apply
2 | , operators = ["!0", "~0", "typeof 0", "void 0", "0*1", "0/1", "0%1", "0+1", "0-1", "0<<1", "0>>1", "0>>>1", "0<1", "0<=1", "0>1", "0>=1", "0==1", "0!=1", "0===1", "0!==1", "0&1", "0^1", "0|1", "0&&1", "0||1", "0,1", "0?1:2", "0[1]", "0 in 1"]
3 | , i = operators.length
4 |
5 | while (i--) !function(str) {
6 | var body = str.replace(/\d/g, "arguments[$&]")
7 | , name = str.replace(/\d| /g, "")
8 | , fn = Function("return " + body)
9 |
10 | exports[name] = function(){ return apply.call(fn, null, arguments) }
11 | }(operators[i])
--------------------------------------------------------------------------------
/lib/Function.js:
--------------------------------------------------------------------------------
1 | var Array = require("./Array")
2 | , ArrayCtor = [].constructor
3 | , FunctionCtor = ArrayCtor.constructor
4 | , concat = ArrayCtor.prototype.concat
5 | , proto = Function.prototype
6 | , call
7 | , apply
8 | , _new
9 |
10 | function Function() {
11 | return apply.call(FunctionCtor, null, arguments)
12 | }
13 |
14 | call = proto.call = function call() {
15 | var args = Array.apply(null, arguments)
16 | , fn = this
17 |
18 | return typeof args != "function" ?
19 |
20 | fn.call.apply(fn, args) :
21 |
22 | function(emit) {
23 | args(function(err, data) {
24 | if (!err) data = fn.call.apply(fn, data)
25 | return emit(err, data)
26 | })
27 | }
28 | }
29 |
30 | apply = proto.apply = function(context, args) {
31 | var fn = this
32 |
33 | return typeof args != "function" ?
34 |
35 | call.apply(fn, concat.apply([context], args)) :
36 |
37 | function(emit) {
38 | args(function(err, data) {
39 | if (!err) data = apply.call(fn, context, data)
40 | return emit(err, data)
41 | })
42 | }
43 | }
44 |
45 | _new = proto["new"] = function() {
46 | var self = this
47 | , args = Array.apply(null, arguments)
48 |
49 | function getFn(arity) {
50 | return _new[arity] || (_new[arity] = Function(
51 | "Ctor, args, i",
52 | "return new Ctor(" +
53 | ArrayCtor(arity + 1).join(",args[i++]").slice(1) +
54 | ")"
55 | ))
56 | }
57 |
58 | return typeof args != "function" ?
59 |
60 | getFn(args.length)(self, args, 0) :
61 |
62 | function(emit) {
63 | args(function(err, data) {
64 | if (!err) data = getFn(data.length)(self, data, 0)
65 | return emit(err, data)
66 | })
67 | }
68 | }
69 |
70 | module.exports = Function
--------------------------------------------------------------------------------
/lib/Array.js:
--------------------------------------------------------------------------------
1 | var original = [].constructor
2 | , originalProto = original.prototype
3 | , arrayProto = Array.prototype
4 | , slice = originalProto.slice
5 | , methods
6 | , apply
7 | , i
8 |
9 | function Array() {
10 | var args = arguments
11 | , length = args.length
12 | , i = length
13 |
14 | while (i--) if (typeof args[i] == "function") break
15 |
16 | return i < 0 ? slice.call(args) :
17 |
18 | function(emit) {
19 | var evaluated = []
20 | , remaining = length
21 | , i = 0
22 |
23 | while (i < length) !function(i) {
24 | !function set(err, data) {
25 | if (!err) {
26 | if (typeof data == "function") return data(set)
27 | if (remaining && !(i in evaluated)) remaining--
28 | evaluated[i] = data
29 | }
30 |
31 | if (!remaining || err) return emit(err, evaluated.slice(0))
32 | }(null, args[i])
33 | }(i++)
34 | }
35 | }
36 |
37 | module.exports = Array
38 |
39 | apply = require("./Function").prototype.apply
40 |
41 | methods = ["concat", "indexOf", "join", "toString", "splice", "shift", "unshift", "lastIndexOf", "reverse", "pop", "push", "slice"]
42 | i = methods.length
43 |
44 | while (i--) !function(name) {
45 | arrayProto[name] = function() {
46 | return apply.call(
47 | originalProto[name],
48 | apply.call(Array, null, this),
49 | Array.apply(null, arguments)
50 | )
51 | }
52 | }(methods[i])
53 |
54 | methods = ["map", "sort", "filter", "some", "reduceRight", "forEach", "reduce", "every"]
55 | i = methods.length
56 |
57 | while (i--) !function(name) {
58 | arrayProto[name] = function() {
59 | var args = arguments
60 |
61 | return apply.call(
62 | function(){ return originalProto[name].apply(this, args) },
63 | apply.call(Array, null, this)
64 | )
65 | }
66 | }(methods[i])
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | emit = require("emit")
2 |
3 | // emit method APIs are identical to their native counterparts...
4 | Math.pow(2, 2) // 4
5 | emit.Math.pow(2, 2) // 4
6 |
7 | Array(1, 2, 3) // [1, 2, 3]
8 | emit.Array(1, 2, 3) // [1, 2, 3]
9 |
10 | 1 / 2 // .5
11 | emit.ops["/"](1, 2) // .5
12 |
13 | // with one exception: emit methods can also take functions, which are
14 | // treated as yet-unevaluated values that can be "watched". All emit
15 | // methods compose these (possibly unevaluated) values into a single
16 | // value; if any of these values are functions, the returned value will
17 | // also be a function.
18 |
19 | // Here's an example of such a function; it reports an incremented number
20 | // back to its listener every second.
21 | function count(cb) {
22 | var send = function(){ cb(null, i++) }
23 | , i = 0
24 |
25 | send()
26 | setInterval(send, 1000)
27 | }
28 |
29 | // Since the value reported by count changes every second, so does
30 | // the value of any method that uses it. So these are all functions:
31 | emit.Math.pow(2, count) // [Function]
32 | emit.Array(1, 2, count) // [Function]
33 | emit.ops["/"](1, count) // [Function]
34 |
35 | // We can observe these changing values through the console.
36 | function log(e, data){ console.log(e || data) }
37 |
38 | emit.Math.pow(2, count)(log) // 1, 2, 4, 8, 16, 32...
39 | emit.Array(1, 2, count)(log) // [1, 2, 0], [1, 2, 1], [1, 2, 2]...
40 | emit.ops["/"](1, count)(log) // Infinity, 1, .5, .25...
41 |
42 | // Now we can now start to compose these into increasingly
43 | // complex values.
44 | random = emit.Math.random(count)
45 | randBin = emit.Math.round(random)
46 | coinToss = emit.ops["?:"](randBin, "heads", "tails")
47 | message = emit.ops["+"]("The coin landed on ", coinToss)
48 |
49 | message(log) // "The coin landed on heads"
50 | // "The coin landed on tails"
51 | // "The coin landed on heads"
52 | // ...
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ( ( (emit) ) )
2 | ================
3 |
4 | (emit) is a [functional reactive programming][frp] toolkit based on JavaScript primitives, allowing you to compose functional applications using the APIs you already know.
5 |
6 | (emit) is a very rough work in progress. It can run both in the browser and on [node.js][node].
7 |
8 | Example
9 | -------
10 |
11 | ```javascript
12 | emit = require("emit")
13 |
14 | // emit method APIs are identical to their native counterparts...
15 | Math.pow(2, 2) // 4
16 | emit.Math.pow(2, 2) // 4
17 |
18 | Array(1, 2, 3) // [1, 2, 3]
19 | emit.Array(1, 2, 3) // [1, 2, 3]
20 |
21 | 1 / 2 // .5
22 | emit.ops["/"](1, 2) // .5
23 |
24 | // with one exception: emit methods can also take functions, which are
25 | // treated as yet-unevaluated values that can be "watched". All emit
26 | // methods compose these (possibly unevaluated) values into a single
27 | // value; if any of these values are functions, the returned value will
28 | // also be a function.
29 |
30 | // Here's an example of such a function; it reports an incremented number
31 | // back to its listener every second.
32 | function count(cb) {
33 | var send = function(){ cb(null, i++) }
34 | , i = 0
35 |
36 | send()
37 | setInterval(send, 1000)
38 | }
39 |
40 | // Since the value reported by count changes every second, so does
41 | // the value of any method that uses it. So these are all functions:
42 | emit.Math.pow(2, count) // [Function]
43 | emit.Array(1, 2, count) // [Function]
44 | emit.ops["/"](1, count) // [Function]
45 |
46 | // We can observe these changing values through the console.
47 | function log(e, data){ console.log(e || data) }
48 |
49 | emit.Math.pow(2, count)(log) // 1, 2, 4, 8, 16, 32...
50 | emit.Array(1, 2, count)(log) // [1, 2, 0], [1, 2, 1], [1, 2, 2]...
51 | emit.ops["/"](1, count)(log) // Infinity, 1, .5, .25...
52 |
53 | // Now we can now start to compose these into increasingly
54 | // complex values.
55 | random = emit.Math.random(ping)
56 | randBin = emit.Math.round(random)
57 | coinToss = emit.ops["?:"](randBin, "heads", "tails")
58 | message = emit.ops["+"]("The coin landed on ", coinToss)
59 |
60 | message(log) // "The coin landed on heads"
61 | // "The coin landed on tails"
62 | // "The coin landed on heads"
63 | // ...
64 | ```
65 |
66 | Installation
67 | ------------
68 |
69 | To install, head to your terminal and enter:
70 |
71 | npm install emit
72 |
73 | Support
74 | -------
75 |
76 | ### Current
77 |
78 | - `Array`: all ES3/ES5 methods
79 | - `Function`: `apply`, and `call`
80 | - `Math`: all statics and function
81 | - operators: most accessors, including `!`, `~`, `typeof`, `void`, `*`, `/`, `%`, `+`, `-`, `<<`, `>>`, `>>>`, `<`, `<=`, `>`, `>=`, `==`, `!=`, `===`, `!==`, `&`, `^`, `|`, `&&`, `||`, `, `, `?:`, `[]`, and `in`
82 |
83 | ### Planned
84 |
85 | - RegExp, String, Date, Number, Boolean
86 | - Function::bind
87 | - DOM/HTML
88 | - DOM events
89 | - JSON
90 | - node.js file system, etc.
91 |
92 | License
93 | -------
94 |
95 | Copyright (c) 2012 Jed Schmidt, http://jed.is/
96 |
97 | Permission is hereby granted, free of charge, to any person obtaining
98 | a copy of this software and associated documentation files (the
99 | "Software"), to deal in the Software without restriction, including
100 | without limitation the rights to use, copy, modify, merge, publish,
101 | distribute, sublicense, and/or sell copies of the Software, and to
102 | permit persons to whom the Software is furnished to do so, subject to
103 | the following conditions:
104 |
105 | The above copyright notice and this permission notice shall be
106 | included in all copies or substantial portions of the Software.
107 |
108 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
109 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
110 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
111 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
112 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
113 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
114 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
115 |
116 | [frp]: http://en.wikipedia.org/wiki/Functional_reactive_programming
117 | [node]: http://nodejs.org/
118 | [flapjax]: http://www.flapjax-lang.org/
119 | [tangle]: http://worrydream.com/Tangle/
120 | [spreadsheet]: http://en.wikipedia.org/wiki/Spreadsheet
--------------------------------------------------------------------------------