├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── test.yml ├── .gitignore ├── array.js ├── index.js ├── license ├── object.js ├── package.json ├── readme.md └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: tests 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - name: Use Node.js ${{ matrix.node-version }} 9 | uses: actions/setup-node@v1 10 | with: 11 | node-version: ${{ matrix.node-version }} 12 | - run: npm install 13 | - run: npm test 14 | strategy: 15 | matrix: 16 | node-version: 17 | - "4" 18 | 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /array.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var extend = require('xtend') 4 | 5 | module.exports = function createArrayListener (listen) { 6 | return function listenToArray (arr, fn) { 7 | var current = extend(arr._list) 8 | 9 | arr.forEach(function (item) { 10 | listen(item, fn) 11 | }) 12 | arr(onChange) 13 | 14 | function onChange (data) { 15 | if (!arr.getLength()) return 16 | var diff = data._diff 17 | diff.forEach(function (change) { 18 | var index = change[0] 19 | for (var i = index; i < change.length; i++) { 20 | if (current[i] !== arr.get(i) && arr.get(i)) { 21 | listen(arr.get(i), fn) 22 | } 23 | } 24 | }) 25 | 26 | current = extend(arr._list) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var Event = require('geval/event') 4 | var createStore = require('weakmap-shim/create-store') 5 | var createHashListener = require('./object') 6 | var createArrayListener = require('./array') 7 | 8 | module.exports = WeakmapEvent 9 | 10 | function WeakmapEvent () { 11 | var store = createStore() 12 | 13 | listen.toHash = createHashListener(listen) 14 | listen.toArray = createArrayListener(listen) 15 | 16 | return { 17 | broadcast, 18 | listen 19 | } 20 | 21 | function broadcast (obj, value) { 22 | if (arguments.length === 1) { 23 | throw new Error('WeakmapEvent#broadcast expects arguments (obj, value)') 24 | } 25 | return getEvent(obj).broadcast(value, obj) 26 | } 27 | 28 | function listen (obj, fn) { 29 | if (arguments.length === 1) { 30 | throw new Error('WeakmapEvent#listen expects arguments (obj, listen)') 31 | } 32 | return getEvent(obj).listen(fn) 33 | } 34 | 35 | function getEvent (obj) { 36 | var eventStore = store(obj) 37 | eventStore.event = eventStore.event || Event() 38 | return eventStore.event 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Andrew Joslin (ajoslin.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /object.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var extend = require('xtend') 4 | 5 | module.exports = function createHashListener (listen) { 6 | return function listenToHash (hash, fn) { 7 | var current = extend(hash) 8 | 9 | forEach(hash, listenKey) 10 | hash(onChange) 11 | 12 | function listenKey (key) { 13 | listen(hash[key], fn) 14 | } 15 | 16 | function onChange () { 17 | forEach(hash, function (key) { 18 | if (current[key] !== hash[key]) { 19 | listenKey(key) 20 | } 21 | }) 22 | 23 | current = extend(hash) 24 | } 25 | } 26 | } 27 | 28 | function forEach (observable, callback) { 29 | return Object.keys(observable()).forEach(callback) 30 | } 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weakmap-event", 3 | "main": "index.js", 4 | "version": "2.0.7", 5 | "description": "", 6 | "license": "MIT", 7 | "repository": "bendrucker/weakmap-event", 8 | "author": { 9 | "email": "bvdrucker@gmail.com", 10 | "name": "Ben Drucker", 11 | "url": "bendrucker.me" 12 | }, 13 | "scripts": { 14 | "test": "standard && tape test.js" 15 | }, 16 | "keywords": [ 17 | "weakmap", 18 | "event", 19 | "geval", 20 | "functional" 21 | ], 22 | "devDependencies": { 23 | "observ": "~0.2.0", 24 | "observ-array": "~3.2.1", 25 | "observ-struct": "~6.0.0", 26 | "observ-varhash": "~1.0.6", 27 | "standard": "^12.0.1", 28 | "tape": "^4.0.0" 29 | }, 30 | "files": [ 31 | "index.js", 32 | "object.js", 33 | "array.js" 34 | ], 35 | "dependencies": { 36 | "geval": "~2.2.0", 37 | "key-difference": "~1.0.0", 38 | "weakmap-shim": "~1.1.0", 39 | "xtend": "~4.0.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # weakmap-event [![tests](https://github.com/bendrucker/weakmap-event/workflows/tests/badge.svg)](https://github.com/bendrucker/weakmap-event/actions?query=workflow%3Atests) 2 | 3 | > Associate [geval](https://github.com/Raynos/geval) events with a given object. 4 | 5 | Adapted from code in [Mercury](https://github.com/Raynos/mercury)'s examples. 6 | 7 | 8 | ## Install 9 | 10 | ``` 11 | $ npm install --save weakmap-event 12 | ``` 13 | 14 | 15 | ## Usage 16 | 17 | ```js 18 | var WeakmapEvent = require('weakmap-event') 19 | 20 | var onClick = WeakmapEvent() 21 | 22 | var obj1 = {} 23 | var obj2 = {} 24 | 25 | onClick.listen(obj1, function(data) { 26 | assert.equal(data, 'hello') 27 | }) 28 | onClick.listen(obj2, function(data) { 29 | assert.equal(data, 'goodbye') 30 | }) 31 | 32 | onClick.broadcast(obj1, 'hello') 33 | onClick.broadcast(obj2, 'goodbye') 34 | ``` 35 | 36 | ## API 37 | 38 | #### `WeakmapEvent()` -> `event` 39 | 40 | Creates a new weak-mapped event interface. 41 | 42 | #### `event.broadcast(obj, value)` -> `undefined` 43 | 44 | Broadcasts the value for listeners mapped to the object 45 | 46 | ##### obj 47 | 48 | *Required* 49 | Type: `object` 50 | 51 | The object to limit the broadcast to. Listeners on other objects will not be called. 52 | 53 | ##### value 54 | 55 | *Required* 56 | Type: `any` 57 | 58 | The value to broadcast to matched listeners. 59 | 60 | #### `event.listen(obj, listener)` -> `function` 61 | 62 | Listen on values emitted for a given object. Returns an `unlisten` function that will disable the listener when called. 63 | 64 | ##### obj 65 | 66 | *Required* 67 | Type: `object` 68 | 69 | The object to listen on. 70 | 71 | ##### listener 72 | 73 | *Required* 74 | Type: `function` 75 | Arguments: `value` 76 | 77 | A listener function to be called when a value is broadcasted matching the object. 78 | 79 | #### `event.listen.toHash(observable, listener)` -> `undefined` 80 | 81 | Listen on an observable hash. 82 | 83 | ##### observable 84 | 85 | *Required* 86 | Type: `function` 87 | 88 | An observable hash like [observ-struct](https://github.com/raynos/observ-struct) or [observ-varhash](https://github.com/nrw/observ-varhash). 89 | 90 | ##### listener 91 | 92 | *Required* 93 | Type: `function` 94 | Arguments: `value` 95 | 96 | A listener function to be called when a value is broadcasted matching a value from within the hash. 97 | 98 | 99 | #### `event.listen.toArray(observable, listener)` -> `undefined` 100 | 101 | Listen on an observable array. 102 | 103 | ##### observable 104 | 105 | *Required* 106 | Type: `function` 107 | 108 | An observable array from [observ-array](https://github.com/raynos/observ-array). 109 | 110 | ##### listener 111 | 112 | *Required* 113 | Type: `function` 114 | Arguments: `value` 115 | 116 | A listener function to be called when a value is broadcasted matching a value from within the array. 117 | 118 | ## License 119 | 120 | MIT © [Ben Drucker](http://bendrucker.me) 121 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var test = require('tape') 4 | var Hash = require('observ-varhash') 5 | var Observ = require('observ') 6 | var ObservArray = require('observ-array') 7 | var WeakmapEvent = require('./') 8 | 9 | test('returns gevental eventent mapped to an object', function (t) { 10 | t.plan(1) 11 | var event = WeakmapEvent() 12 | var obj = {} 13 | 14 | var unlisten = event.listen(obj, function (result) { 15 | t.equal(result, 'hello') 16 | unlisten() 17 | }) 18 | event.broadcast(obj, 'hello') 19 | 20 | // This second broadcast is effectively a noop because of the unlisten 21 | event.broadcast(obj, 'hello') 22 | }) 23 | 24 | test('dispatch eventents for an object', function (t) { 25 | t.plan(2) 26 | var event = WeakmapEvent() 27 | var obj = {} 28 | var obj2 = {} 29 | 30 | event.listen(obj, function (data) { 31 | t.equal(data, 'hello') 32 | }) 33 | event.listen(obj2, function (data) { 34 | t.equal(data, 'goodbye') 35 | }) 36 | 37 | event.broadcast(obj, 'hello') 38 | event.broadcast(obj2, 'goodbye') 39 | }) 40 | 41 | test('toHash', function (t) { 42 | t.plan(3) 43 | var hash = Hash({ 44 | a: Observ(1), 45 | b: Observ(2) 46 | }) 47 | var event = WeakmapEvent() 48 | 49 | event.listen.toHash(hash, function (data) { 50 | t.equal(data.value, 'foo') 51 | }) 52 | 53 | event.broadcast(hash.a, { 54 | value: 'foo' 55 | }) 56 | event.broadcast(hash.b, { 57 | value: 'foo' 58 | }) 59 | 60 | // now we add a key which should be listened on 61 | hash.put('c', Observ(3)) 62 | event.broadcast(hash.c, { 63 | value: 'foo' 64 | }) 65 | }) 66 | 67 | test('toArray', function (t) { 68 | t.plan(4) 69 | var arr = ObservArray([Observ(1)]) 70 | var event = WeakmapEvent() 71 | 72 | event.listen.toArray(arr, function (data) { 73 | t.equal(data.value, 'foo') 74 | }) 75 | 76 | event.broadcast(arr.get(0), { 77 | value: 'foo' 78 | }) 79 | 80 | arr.push(Observ(2)) 81 | event.broadcast(arr.get(1), { 82 | value: 'foo' 83 | }) 84 | 85 | arr.push(Observ(3), Observ(4), Observ(5)) 86 | event.broadcast(arr.get(3), { 87 | value: 'foo' 88 | }) 89 | 90 | arr.get(0).set('first') 91 | event.broadcast(arr.get(0), { 92 | value: 'foo' 93 | }) 94 | 95 | arr.splice(3, 1) 96 | }) 97 | 98 | test('argument validation', function (t) { 99 | var event = WeakmapEvent() 100 | t.throws(function () { 101 | event.listen(function listener () {}) 102 | }, 'listen') 103 | t.throws(function () { 104 | event.broadcast('value') 105 | }, 'broadcast') 106 | t.end() 107 | }) 108 | --------------------------------------------------------------------------------