├── .editorconfig
├── .eslintrc.json
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── bower.json
├── dist
├── bullet.js
└── bullet.min.js
├── example
├── index.html
└── js
│ ├── app
│ └── app.js
│ └── libs
│ └── bullet.js
├── gulpfile.js
├── package.json
├── src
└── bullet.js
└── test
├── .jshintrc
├── _root-suite.js
└── spec
├── existence-checks
├── custom-errors.js
├── methods.js
└── properties.js
├── methods
├── _getMappings.js
├── addEventName.js
├── addMultipleEventNames.js
├── getTriggerAsync.js
├── off.js
├── on.js
├── once.js
├── removeEventName.js
├── replaceAllCallbacks.js
├── replaceCallback.js
├── setTriggerAsync.js
└── trigger.js
└── strict-methods
├── getStrictMode.js
├── off.js
├── on.js
├── once.js
├── replaceAllCallbacks.js
├── replaceCallback.js
├── setStrictMode.js
└── trigger.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = false
10 | insert_final_newline = true
11 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "eslint:recommended",
3 | "env": {
4 | "browser": true,
5 | "commonjs": true,
6 | "es6": true,
7 | "node": true
8 | },
9 | "globals": {
10 | "define": false
11 | },
12 | "rules": {
13 | "accessor-pairs": "error",
14 | "array-bracket-newline": "error",
15 | "array-bracket-spacing": "error",
16 | "array-callback-return": "error",
17 | "array-element-newline": "error",
18 | "arrow-body-style": "error",
19 | "arrow-parens": "off",
20 | "arrow-spacing": [
21 | "error",
22 | {
23 | "after": true,
24 | "before": true
25 | }
26 | ],
27 | "block-scoped-var": "error",
28 | "block-spacing": "error",
29 | "brace-style": "off",
30 | "callback-return": "error",
31 | "camelcase": "error",
32 | "capitalized-comments": "off",
33 | "class-methods-use-this": "error",
34 | "comma-dangle": "off",
35 | "comma-spacing": [
36 | "error",
37 | {
38 | "after": true,
39 | "before": false
40 | }
41 | ],
42 | "comma-style": [
43 | "error",
44 | "last"
45 | ],
46 | "complexity": "error",
47 | "computed-property-spacing": [
48 | "error",
49 | "never"
50 | ],
51 | "consistent-return": "off",
52 | "consistent-this": "off",
53 | "curly": "off",
54 | "default-case": "error",
55 | "dot-location": [
56 | "error",
57 | "property"
58 | ],
59 | "dot-notation": "error",
60 | "eol-last": "error",
61 | "eqeqeq": "error",
62 | "for-direction": "error",
63 | "func-call-spacing": "error",
64 | "func-name-matching": "error",
65 | "func-names": [
66 | "error",
67 | "never"
68 | ],
69 | "func-style": [
70 | "error",
71 | "declaration"
72 | ],
73 | "generator-star-spacing": "error",
74 | "global-require": "error",
75 | "guard-for-in": "off",
76 | "handle-callback-err": "error",
77 | "id-blacklist": "error",
78 | "id-length": ["error", { "exceptions": ["i"] }],
79 | "id-match": "error",
80 | "indent": "off",
81 | "indent-legacy": "off",
82 | "init-declarations": "off",
83 | "jsx-quotes": "error",
84 | "key-spacing": "off",
85 | "keyword-spacing": "off",
86 | "line-comment-position": "error",
87 | "linebreak-style": [
88 | "error",
89 | "unix"
90 | ],
91 | "lines-around-comment": "error",
92 | "lines-around-directive": "error",
93 | "max-depth": "error",
94 | "max-len": "off",
95 | "max-lines": "off",
96 | "max-nested-callbacks": "error",
97 | "max-params": ["error", 4],
98 | "max-statements": "off",
99 | "max-statements-per-line": "error",
100 | "multiline-ternary": [
101 | "error",
102 | "never"
103 | ],
104 | "new-cap": "error",
105 | "new-parens": "error",
106 | "newline-after-var": "off",
107 | "newline-before-return": "off",
108 | "newline-per-chained-call": "error",
109 | "no-alert": "error",
110 | "no-array-constructor": "error",
111 | "no-await-in-loop": "error",
112 | "no-bitwise": "error",
113 | "no-buffer-constructor": "error",
114 | "no-caller": "error",
115 | "no-catch-shadow": "error",
116 | "no-confusing-arrow": "error",
117 | "no-console": "off",
118 | "no-continue": "error",
119 | "no-div-regex": "error",
120 | "no-duplicate-imports": "error",
121 | "no-else-return": "error",
122 | "no-empty-function": "off",
123 | "no-eq-null": "error",
124 | "no-eval": "error",
125 | "no-extend-native": "error",
126 | "no-extra-bind": "error",
127 | "no-extra-label": "error",
128 | "no-extra-parens": [
129 | "error",
130 | "all",
131 | {
132 | "nestedBinaryExpressions": false
133 | }
134 | ],
135 | "no-floating-decimal": "error",
136 | "no-implicit-globals": "error",
137 | "no-implied-eval": "error",
138 | "no-inline-comments": "error",
139 | "no-invalid-this": "off",
140 | "no-iterator": "error",
141 | "no-label-var": "error",
142 | "no-labels": "error",
143 | "no-lone-blocks": "error",
144 | "no-lonely-if": "off",
145 | "no-loop-func": "error",
146 | "no-magic-numbers": "off",
147 | "no-mixed-operators": [
148 | "error",
149 | {
150 | "allowSamePrecedence": true
151 | }
152 | ],
153 | "no-mixed-requires": "error",
154 | "no-multi-assign": "error",
155 | "no-multi-spaces": "error",
156 | "no-multi-str": "error",
157 | "no-multiple-empty-lines": "off",
158 | "no-native-reassign": "error",
159 | "no-negated-condition": "off",
160 | "no-negated-in-lhs": "error",
161 | "no-nested-ternary": "error",
162 | "no-new": "error",
163 | "no-new-func": "error",
164 | "no-new-object": "error",
165 | "no-new-require": "error",
166 | "no-new-wrappers": "error",
167 | "no-octal-escape": "error",
168 | "no-param-reassign": "error",
169 | "no-path-concat": "error",
170 | "no-process-env": "off",
171 | "no-process-exit": "error",
172 | "no-proto": "error",
173 | "no-prototype-builtins": "error",
174 | "no-restricted-globals": "error",
175 | "no-restricted-imports": "error",
176 | "no-restricted-modules": "error",
177 | "no-restricted-properties": "error",
178 | "no-restricted-syntax": "error",
179 | "no-return-assign": "error",
180 | "no-return-await": "error",
181 | "no-script-url": "error",
182 | "no-self-compare": "error",
183 | "no-sequences": "error",
184 | "no-shadow": "off",
185 | "no-shadow-restricted-names": "error",
186 | "no-spaced-func": "error",
187 | "no-sync": "off",
188 | "no-tabs": "error",
189 | "no-template-curly-in-string": "error",
190 | "no-ternary": "off",
191 | "no-throw-literal": "error",
192 | "no-trailing-spaces": "off",
193 | "no-undef-init": "error",
194 | "no-undefined": "error",
195 | "no-underscore-dangle": "off",
196 | "no-unmodified-loop-condition": "error",
197 | "no-unneeded-ternary": "error",
198 | "no-unused-expressions": "off",
199 | "no-unused-vars": [
200 | "error",
201 | {
202 | "args": "none",
203 | "vars": "all"
204 | }
205 | ],
206 | "no-use-before-define": "off",
207 | "no-useless-call": "error",
208 | "no-useless-computed-key": "error",
209 | "no-useless-concat": "error",
210 | "no-useless-constructor": "error",
211 | "no-useless-rename": "error",
212 | "no-useless-return": "error",
213 | "no-var": "error",
214 | "no-void": "error",
215 | "no-warning-comments": "off",
216 | "no-whitespace-before-property": "error",
217 | "no-with": "error",
218 | "nonblock-statement-body-position": "error",
219 | "object-curly-newline": "off",
220 | "object-curly-spacing": [
221 | "error",
222 | "never"
223 | ],
224 | "object-property-newline": [
225 | "error",
226 | {
227 | "allowMultiplePropertiesPerLine": true
228 | }
229 | ],
230 | "object-shorthand": "off",
231 | "one-var": "off",
232 | "one-var-declaration-per-line": "error",
233 | "operator-assignment": [
234 | "error",
235 | "always"
236 | ],
237 | "operator-linebreak": "error",
238 | "padded-blocks": "off",
239 | "padding-line-between-statements": "error",
240 | "prefer-arrow-callback": "off",
241 | "prefer-const": "error",
242 | "prefer-numeric-literals": "error",
243 | "prefer-promise-reject-errors": "off",
244 | "prefer-reflect": "off",
245 | "prefer-rest-params": "error",
246 | "prefer-spread": "error",
247 | "prefer-template": "off",
248 | "quote-props": "off",
249 | "quotes": [
250 | "error",
251 | "single"
252 | ],
253 | "radix": [
254 | "error",
255 | "always"
256 | ],
257 | "require-await": "error",
258 | "require-jsdoc": "off",
259 | "rest-spread-spacing": "error",
260 | "semi": ["error", "always"],
261 | "semi-spacing": "error",
262 | "semi-style": [
263 | "error",
264 | "last"
265 | ],
266 | "sort-imports": "off",
267 | "sort-keys": "off",
268 | "sort-vars": "error",
269 | "space-before-blocks": "error",
270 | "space-before-function-paren": "error",
271 | "space-in-parens": "off",
272 | "space-infix-ops": "error",
273 | "space-unary-ops": "error",
274 | "spaced-comment": "off",
275 | "strict": "off",
276 | "switch-colon-spacing": "error",
277 | "symbol-description": "error",
278 | "template-curly-spacing": [
279 | "error",
280 | "never"
281 | ],
282 | "template-tag-spacing": "error",
283 | "unicode-bom": [
284 | "error",
285 | "never"
286 | ],
287 | "valid-jsdoc": "error",
288 | "vars-on-top": "off",
289 | "wrap-iife": [
290 | "error",
291 | "inside"
292 | ],
293 | "wrap-regex": "error",
294 | "yield-star-spacing": "error",
295 | "yoda": [
296 | "error",
297 | "never"
298 | ]
299 | }
300 | }
301 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #---------------------------------------
2 | # Packages
3 | #---------------------------------------
4 | node_modules/
5 |
6 | #---------------------------------------
7 | # General
8 | #---------------------------------------
9 | logs
10 | *.log
11 | *.sublime-*
12 |
13 |
14 | #---------------------------------------
15 | # OS generated files
16 | #---------------------------------------
17 | .DS_Store
18 | .DS_Store?
19 | ._*
20 | .Spotlight-V100
21 | .Trashes
22 | ehthumbs.db
23 | Thumbs.db
24 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .npmignore
2 | .jshintrc
3 | bower.json
4 | Gruntfile.js
5 | src
6 | test
7 | example
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Ivan Hayes
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Bullet
2 |
3 | Bullet is a lightweight and simple to use pub-sub library, with AMD/CJS support and an intuitive API.
4 | It was built to facilitate a simple and consistent system of communication across web applications and includes only the bare essentials typically needed to achieve this, along with great error-handling and thorough unit tests.
5 |
6 | ### Usage
7 |
8 | #### npm
9 | Install via npm using the following command in your command prompt:
10 |
11 | ```shell
12 | npm i -S bullet-pubsub
13 | ```
14 |
15 | Include Bullet within your application:
16 |
17 | ```javascript
18 | var Bullet = require('bullet-pubsub');
19 | ```
20 |
21 |
22 | #### Bower
23 | Install via Bower using the following command in your command prompt:
24 |
25 | ```shell
26 | bower install bullet
27 | ```
28 |
29 | Include Bullet in your application:
30 |
31 | ```html
32 |
33 | ```
34 |
35 |
36 | #### Installation without a package manager
37 | If you are not using npm or Bower, then grab either the [minified](https://raw.githubusercontent.com/munkychop/bullet/master/dist/bullet.min.js), or [non-minified](https://raw.githubusercontent.com/munkychop/bullet/master/dist/bullet.js) source from Github and include Bullet in your application:
38 |
39 | ```html
40 |
41 | ```
42 |
43 |
44 | ### Methods
45 |
46 | #### **.on()**
47 |
48 | ```javascript
49 | Bullet.on('someMessageName', callback);
50 | ```
51 |
52 | Register a callback function to get called whenever the specified message is triggered.
53 |
54 |
55 | **Example usage:**
56 |
57 | ```javascript
58 |
59 | function helloCallback () {
60 | console.log('hello there :)');
61 | }
62 |
63 |
64 | // Register the 'helloCallback' function to be called whenever the 'hello' message is triggered:
65 |
66 | Bullet.on('hello', helloCallback);
67 |
68 |
69 | // Somewhere later in the application...
70 |
71 |
72 | // Trigger the 'hello' message – Bullet will call the 'helloCallback' function:
73 |
74 | Bullet.trigger('hello');
75 |
76 | ```
77 |
78 |
79 | ----------
80 |
81 |
82 | #### **.off()**
83 |
84 | ```javascript
85 | Bullet.off('someMessageName'[, callback]);
86 | ```
87 |
88 | Remove either all callback functions or a specific callback function registered against the specified message.
89 |
90 | ```javascript
91 | Bullet.off();
92 | ```
93 |
94 | Remove all registered mappings by calling `off` with no parameters.
95 |
96 |
97 | **Example usage:**
98 |
99 | ```javascript
100 |
101 | function helloCallback () {
102 | console.log('hello there :)');
103 | }
104 |
105 | function anotherCallback () {
106 | console.log('hello again :)');
107 | }
108 |
109 |
110 | Bullet.on('hello', helloCallback);
111 | Bullet.on('hello', anotherCallback);
112 |
113 |
114 | // Somewhere later in the application...
115 |
116 |
117 | // Trigger the 'hello' message – Bullet will call both the 'helloCallback' and 'anotherCallback' functions:
118 |
119 | Bullet.trigger('hello');
120 |
121 |
122 | // Remove all callback functions associated with the 'hello' message:
123 |
124 | Bullet.off('hello');
125 |
126 |
127 | // Attempt to trigger the 'hello' message again – Bullet won't call any functions:
128 |
129 | Bullet.trigger('hello');
130 |
131 | ```
132 |
133 |
134 | **Example usage removing a specific callback:**
135 |
136 | ```javascript
137 |
138 | function helloCallback () {
139 | console.log('hello there :)');
140 | }
141 |
142 | function anotherCallback () {
143 | console.log('hello again :)');
144 | }
145 |
146 |
147 | Bullet.on('hello', helloCallback);
148 | Bullet.on('hello', anotherCallback);
149 |
150 |
151 | // Somewhere later in the application...
152 |
153 |
154 | // Trigger the 'hello' message – Bullet will call both the 'helloCallback' and 'anotherCallback' functions:
155 |
156 | Bullet.trigger('hello');
157 |
158 |
159 | // Remove only the 'anotherCallback' function associated with the 'hello' message:
160 |
161 | Bullet.off('hello', anotherCallback);
162 |
163 |
164 | // Trigger the 'hello' message again – Bullet will only call the 'helloCallback' function:
165 |
166 | Bullet.trigger('hello');
167 |
168 | ```
169 |
170 |
171 | **Example usage removing all mappings:**
172 |
173 | ```javascript
174 |
175 | function helloCallback () {
176 | console.log('hello there :)');
177 | }
178 |
179 | function goodbyeCallback () {
180 | console.log('goodbye :)');
181 | }
182 |
183 |
184 | Bullet.on('hello', helloCallback);
185 | Bullet.on('goodbye', goodbyeCallback);
186 |
187 |
188 | // Somewhere later in the application...
189 |
190 |
191 | // Trigger the 'hello' message – Bullet will call the 'helloCallback' function:
192 |
193 | Bullet.trigger('hello');
194 |
195 |
196 | // Trigger the 'goodbye' message – Bullet will call the 'goodbyeCallback' function:
197 |
198 | Bullet.trigger('goodbye');
199 |
200 |
201 | // Remove all mappings by calling the Bullet.off method with no parameters:
202 |
203 | Bullet.off();
204 |
205 |
206 | // Attempt to trigger the 'hello' message again – Bullet will not call the 'helloCallback' function:
207 |
208 | Bullet.trigger('hello');
209 |
210 |
211 | // Attempt to trigger the 'goodbye' message again – Bullet will not call the 'goodbyeCallback' function:
212 |
213 | Bullet.trigger('goodbye');
214 |
215 | ```
216 |
217 |
218 | ----------
219 |
220 |
221 | #### **.once()**
222 |
223 | ```javascript
224 | Bullet.once('someMessageName', callback);
225 | ```
226 |
227 | This function behaves in the same way as the the `on` function, except that – once registered – the callback function will only be called a single time when the specified message is triggered.
228 |
229 |
230 | **Example usage:**
231 |
232 | ```javascript
233 |
234 | function helloCallback () {
235 | console.log('hello there :)');
236 | }
237 |
238 |
239 | // Register the 'helloCallback' function to be called the first time that the 'hello' message is triggered:
240 |
241 | Bullet.once('hello', helloCallback);
242 |
243 |
244 | // Somewhere later in the application...
245 |
246 |
247 | // Trigger the 'hello' message – Bullet will call the 'helloCallback' function:
248 |
249 | Bullet.trigger('hello');
250 |
251 |
252 | // Attempt to trigger the 'hello' message again – Bullet won't call any functions this time:
253 |
254 | Bullet.trigger('hello');
255 |
256 | ```
257 |
258 |
259 | ----------
260 |
261 |
262 | #### **.trigger()**
263 |
264 | ```javascript
265 | Bullet.trigger('someMessageName'[, data]);
266 | ```
267 |
268 | This function will call all callback functions registered against the specified message, optionally passing in custom data as a payload.
269 |
270 |
271 | **Example usage:**
272 |
273 | ```javascript
274 |
275 | function helloCallback () {
276 | console.log('hello there :)');
277 | }
278 |
279 |
280 | // Register the 'helloCallback' function to be called whenever the 'hello' message is triggered:
281 |
282 | Bullet.on('hello', helloCallback);
283 |
284 |
285 | // Somewhere later in the application...
286 |
287 |
288 | // Trigger the 'hello' message – Bullet will call the 'helloCallback' function:
289 |
290 | Bullet.trigger('hello');
291 |
292 | ```
293 |
294 |
295 | **Example usage with custom data:**
296 |
297 | ```javascript
298 |
299 | function helloCallback (data) {
300 | console.log(data);
301 | }
302 |
303 |
304 | // Register the 'helloCallback' function to be called whenever the 'hello' message is triggered:
305 |
306 | Bullet.on('hello', helloCallback);
307 |
308 |
309 | // Somewhere later in the application...
310 |
311 |
312 | // Create some custom data:
313 |
314 | var customData = {
315 | someProp : 'bro',
316 | someOtherProp : 'awesome!'
317 | };
318 |
319 |
320 | // Trigger the 'hello' message – Bullet will call the 'helloCallback' function and
321 | // pass in the custom data that you created, which will be sent to the function as a parameter:
322 |
323 | Bullet.trigger('hello', customData);
324 |
325 | ```
326 |
327 |
328 | ----------
329 |
330 |
331 | #### **.replaceCallback()**
332 |
333 | ```javascript
334 | Bullet.replaceCallback('someMessageName', oldCallback, newCallback[, once]);
335 | ```
336 |
337 | Replace a single mapped callback for the specified event name with a new callback, optionally setting the 'once' parameter.
338 |
339 |
340 | **Example usage:**
341 |
342 | ```javascript
343 |
344 | function helloCallback () {
345 | console.log('hello!');
346 | }
347 |
348 |
349 | function someOtherCallback () {
350 | console.log('konnichiwa!');
351 | }
352 |
353 |
354 | // Explicitly add the event name.
355 |
356 | Bullet.addEventName('hello')
357 |
358 |
359 | // Create an event mapping.
360 |
361 | Bullet.on('hello', helloCallback);
362 |
363 |
364 | // Remove the 'helloCallback' function mapping from the 'hello' event and replace it with a mapping for the 'someOtherCallback' function, while setting the 'once' value for the new callback (optional).
365 |
366 | Bullet.replaceCallback(Bullet.events.hello, helloCallback, someOtherCallback, true);
367 |
368 | ```
369 |
370 |
371 | ----------
372 |
373 |
374 | #### **.replaceAllCallbacks()**
375 |
376 | ```javascript
377 | Bullet.replaceAllCallbacks('someMessageName', newCallback[, once]);
378 | ```
379 |
380 | Replace all of the specified event name’s mapped callbacks with the specified callback, optionally setting the 'once' parameter.
381 |
382 |
383 | **Example usage:**
384 |
385 | ```javascript
386 |
387 | function helloCallback () {
388 | console.log('hello!');
389 | }
390 |
391 |
392 | function someOtherCallback () {
393 | console.log('konnichiwa!');
394 | }
395 |
396 |
397 | // Explicitly add an event name.
398 |
399 | Bullet.addEventName('hello')
400 |
401 |
402 | // Create an event mapping.
403 |
404 | Bullet.on('hello', helloCallback);
405 |
406 |
407 | // Replace all function mappings from the 'hello' event with a mapping for the 'someOtherCallback' function, while setting the 'once' value for the new callback (optional).
408 |
409 | Bullet.replaceAllCallbacks(Bullet.events.hello, someOtherCallback, true);
410 |
411 | ```
412 |
413 |
414 | ----------
415 |
416 |
417 | #### **.getStrictMode()**
418 |
419 | ```javascript
420 | Bullet.getStrictMode();
421 | ```
422 |
423 | Returns a boolean – true if strict mode is enabled and false if not.
424 |
425 |
426 | **Example usage:**
427 |
428 | ```javascript
429 |
430 | // Check whether or not strict mode is enabled:
431 |
432 | var strictMode = Bullet.getStrictMode(); // false (the default)
433 |
434 |
435 | // Turn on strict mode:
436 |
437 | Bullet.setStrictMode(true);
438 |
439 |
440 | // Check again whether or not strict mode is enabled:
441 |
442 | strictMode = Bullet.getStrictMode(); // true
443 |
444 | ```
445 |
446 |
447 | ----------
448 |
449 |
450 | #### **.setStrictMode()**
451 |
452 | ```javascript
453 | Bullet.setStrictMode(boolean);
454 | ```
455 |
456 | Calling the `on`, `once`, `trigger`, `replaceCallback`, or `replaceAllCallbacks` methods – when strict mode is enabled – will cause Bullet to check if the specified message was explicitly added to the `events` object and, if not, Bullet will throw an Error. See the `addEventName` method for details on defining event names.
457 |
458 |
459 | **Example errors when calling the `on`, `once`, or `trigger` methods:**
460 |
461 | ```javascript
462 |
463 | function helloCallback () {
464 | console.log('hello there :)');
465 | }
466 |
467 | function someOtherCallback () {
468 | console.log('konnichiwa!');
469 | }
470 |
471 |
472 | // Turn on strict mode:
473 |
474 | Bullet.setStrictMode(true);
475 |
476 |
477 | // Attempt to register the 'helloCallback' function to be called whenever the 'hello' message is triggered – Bullet will throw an error:
478 |
479 | Bullet.on('hello', helloCallback); // throws error due to unrecognised message
480 |
481 |
482 | // Attempt to register the 'helloCallback' function to be called just once, when the 'hello' message is triggered – Bullet will throw an error:
483 |
484 | Bullet.once('hello', helloCallback); // throws error due to unrecognised message
485 |
486 |
487 | // Attempt to trigger a 'hello' message which hasn't been explicitly added as an event – Bullet will throw an error:
488 |
489 | Bullet.trigger('hello'); // throws error due to unrecognised message
490 |
491 | ```
492 |
493 |
494 | **Example errors when calling the `replaceCallback`, or `replaceAllCallbacks` methods:**
495 |
496 | ```javascript
497 |
498 | function helloCallback () {
499 | console.log('hello there :)');
500 | }
501 |
502 | function someOtherCallback () {
503 | console.log('konnichiwa!');
504 | }
505 |
506 |
507 | // Map the 'helloCallback' to the 'hello' message:
508 |
509 | Bullet.on('hello', helloCallback);
510 |
511 |
512 | // Turn on strict mode:
513 |
514 | Bullet.setStrictMode(true);
515 |
516 |
517 | // Attempt to replace a function mapping for a message which hasn't been explicitly added as an event – Bullet will throw an error:
518 |
519 | Bullet.replaceCallback('hello', helloCallback, someOtherCallback); // throws error due to unrecognised message
520 |
521 |
522 | // Attempt to replace all function mappings for a message which hasn't been explicitly added as an event – Bullet will throw an error:
523 |
524 | Bullet.replaceAllCallbacks('hello', someOtherCallback); // throws error due to unrecognised message
525 |
526 | ```
527 |
528 |
529 | ----------
530 |
531 |
532 | #### **.addEventName()**
533 |
534 | ```javascript
535 | Bullet.addEventName('someMessage');
536 | ```
537 |
538 | Explicitly add a message to Bullet’s 'events' object. **_Explicitly defined message names are required when strict mode is enabled._**
539 |
540 |
541 | **Example usage:**
542 |
543 | ```javascript
544 |
545 | function helloCallback () {
546 | console.log('hello there :)');
547 | }
548 |
549 |
550 | // Register the 'helloCallback' function to be called whenever a 'hello' message is triggered:
551 |
552 | Bullet.on('hello', helloCallback);
553 |
554 |
555 | // Attempt to trigger the 'hello' message – Bullet will call the 'helloCallback' function as expected:
556 |
557 | Bullet.trigger('hello');
558 |
559 |
560 | // Turn on strict mode:
561 |
562 | Bullet.setStrictMode(true);
563 |
564 |
565 | // Attempt to trigger the 'hello' message again – Bullet will throw an error due to strict mode, as the message hasn't been explicitly added:
566 |
567 | Bullet.trigger('hello');
568 |
569 |
570 | // Explicitly add the 'hello' message:
571 |
572 | Bullet.addEventName('hello');
573 |
574 |
575 | // Attempt to trigger the 'hello' message again – Bullet will call the 'helloCallback' function as expected, now that the 'hello' message has been explicitly added:
576 |
577 | Bullet.trigger('hello');
578 |
579 | ```
580 |
581 |
582 | ----------
583 |
584 |
585 | #### **.addMultipleEventNames()**
586 |
587 | ```javascript
588 | Bullet.addMultipleEventNames(['someMessage', 'someOtherMessage']);
589 | ```
590 |
591 | Explicitly add one or more messages to Bullet’s 'events' object. **_Explicitly defined message names are required when strict mode is enabled._**
592 |
593 | This method works in the same way as the `addEventName` method, but instead accepts an array of message names.
594 |
595 |
596 | ----------
597 |
598 |
599 | #### **.removeEventName()**
600 |
601 | ```javascript
602 | Bullet.removeEventName('someMessage');
603 | ```
604 |
605 | Explicitly remove a message from Bullet’s 'events' object.
606 |
607 |
608 | **Example usage:**
609 |
610 | ```javascript
611 |
612 | function helloCallback () {
613 | console.log('hello there :)');
614 | }
615 |
616 |
617 | // Turn on strict mode:
618 |
619 | Bullet.setStrictMode(true);
620 |
621 |
622 | // Explicitly add a 'hello' message:
623 |
624 | Bullet.addEventName('hello');
625 |
626 |
627 | // Register the 'helloCallback' function to be called whenever the 'hello' message is triggered:
628 |
629 | Bullet.on('hello', helloCallback);
630 |
631 |
632 | // Attempt to trigger the 'hello' message – Bullet will call the 'helloCallback' function as expected:
633 |
634 | Bullet.trigger('hello');
635 |
636 |
637 | // Explicitly remove the 'hello' message:
638 |
639 | Bullet.removeEventName('hello');
640 |
641 |
642 | // Attempt to trigger the 'hello' message again – Bullet will throw an error due to strict mode, as the message no longer exists as a part of Bullet’s 'events' object:
643 |
644 | Bullet.trigger('hello');
645 |
646 | ```
647 |
648 |
649 | ----------
650 |
651 |
652 | #### **.getTriggerAsync()**
653 |
654 | ```javascript
655 | Bullet.getTriggerAsync();
656 | ```
657 |
658 | Returns a boolean – true if async message triggers are enabled and false if not.
659 |
660 |
661 | **Example usage:**
662 |
663 | ```javascript
664 |
665 | // Check whether or not async message triggers are enabled:
666 |
667 | var triggerAsync = Bullet.getTriggerAsync(); // true (the default)
668 |
669 |
670 | // Turn off async message triggers:
671 |
672 | Bullet.setTriggerAsync(false);
673 |
674 |
675 | // Check again whether or not async message triggers are enabled:
676 |
677 | triggerAsync = Bullet.getTriggerAsync(); // false
678 |
679 | ```
680 |
681 |
682 | ----------
683 |
684 |
685 | #### **.setTriggerAsync()**
686 |
687 | ```javascript
688 | Bullet.setTriggerAsync(boolean);
689 | ```
690 |
691 | When called and passed a value of `true`, Bullet will trigger messages asynchronously (outside of the current execution call stack) and when called and passed a value of `false`, Bullet will trigger messages synchronously.
692 |
693 |
694 | **Example usage:**
695 |
696 | ```javascript
697 |
698 | // Check whether or not async message triggers are enabled:
699 |
700 | var triggerAsync = Bullet.getTriggerAsync(); // true (the default)
701 |
702 |
703 | // Turn off async message triggers:
704 |
705 | Bullet.setTriggerAsync(false);
706 |
707 |
708 | // Check again whether or not async message triggers are enabled:
709 |
710 | triggerAsync = Bullet.getTriggerAsync(); // false
711 |
712 | ```
713 |
714 |
715 | ----------
716 |
717 |
718 | ### Properties
719 |
720 | #### **.events**
721 |
722 | ```javascript
723 | Bullet.events
724 | ```
725 |
726 | Used for getting a reference to message strings that have been explicitly defined within the 'events' object, usually via the `addEventName` method.
727 | *This property becomes most important when strict mode is enabled.*
728 |
729 |
730 | **Example usage:**
731 |
732 | ```javascript
733 |
734 | function helloCallback () {
735 | console.log('hi');
736 | }
737 |
738 |
739 | // Explicitly define a message string using the 'addEventName' method.
740 |
741 | Bullet.addEventName('hello');
742 |
743 |
744 | // Within the 'on' method, reference the message that was explicitly added to the 'events' object.
745 | // This is helpful because an error will be thrown if the message is undefined:
746 |
747 | Bullet.on(Bullet.events.hello, helloCallback);
748 |
749 | // Bracket notation can be used to access the property instead, if necessary:
750 | // Bullet.events['hello']
751 |
752 |
753 | // Somewhere later in the application...
754 |
755 |
756 | // Trigger the message that was explicitly added to the 'events' object – Bullet will call the 'helloCallback' function.
757 | // Again, this is helpful because an error will be thrown if the message is undefined:
758 |
759 | Bullet.trigger(Bullet.events.hello);
760 |
761 |
762 | // It is also still possible to trigger messages by using a string literal – Bullet will still call the 'helloCallback' function:
763 |
764 | Bullet.trigger('hello');
765 |
766 |
767 | // Note that – when using a string literal – an error will NOT be thrown here if the message doesn't exist within the 'events' object, unless we enable strict mode:
768 |
769 | Bullet.trigger('someOtherMessage'); // no error thrown for unrecognised message
770 |
771 |
772 | // Enable strict mode.
773 |
774 | Bullet.setStrictMode(true);
775 |
776 |
777 | // Now that strict mode is enabled, attempt to trigger the unrecognised message again – Bullet will throw an error due to the unrecognised message:
778 |
779 | Bullet.trigger('someOtherMessage'); // error thrown
780 |
781 | ```
782 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bullet",
3 | "homepage": "https://github.com/munkychop/bullet",
4 | "authors": [
5 | "Ivan Hayes <@munkychop>"
6 | ],
7 | "description": "Bullet is an ultra lightweight and simple to use pub-sub library.",
8 | "main": ["dist/bullet.js", "dist/bullet.min.js"],
9 | "keywords": [
10 | "bullet",
11 | "bullet-pubsub",
12 | "javascript",
13 | "library",
14 | "pub-sub",
15 | "pubsub",
16 | "events",
17 | "communication"
18 | ],
19 | "license": "MIT",
20 | "ignore": [
21 | "**/.*",
22 | "bower_components",
23 | "src",
24 | "test",
25 | "example",
26 | "Gruntfile.js",
27 | "node_modules",
28 | "package.json",
29 | "LICENSE"
30 | ]
31 | }
32 |
--------------------------------------------------------------------------------
/dist/bullet.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
4 |
5 | (function () {
6 |
7 | function Bullet() {
8 | // ------------------------------------------------------------------------------------------
9 | // -- Custom Errors
10 | // ------------------------------------------------------------------------------------------
11 | function ParamCountError(methodName, expectedParamsString, paramCount) {
12 | this.message = 'Bullet:: [' + methodName + '] ' + expectedParamsString + ', but received: ' + paramCount;
13 | var error = new Error(this.message);
14 | if (typeof error.stack !== 'undefined') this.stack = error.stack;
15 | }
16 | setupCustomError(ParamCountError);
17 |
18 | function ParamTypeError(methodName, parameterName, parameter, expectedType) {
19 | this.message = 'Bullet:: [' + methodName + '] Expected parameter - ' + parameterName + ' - to be type: ' + expectedType + ', but received type: ' + (typeof parameter === 'undefined' ? 'undefined' : _typeof(parameter));
20 | var error = new TypeError(this.message);
21 | if (typeof error.stack !== 'undefined') this.stack = error.stack;
22 | }
23 | setupCustomError(ParamTypeError);
24 |
25 | function EventNameLengthError(methodName) {
26 | this.message = 'Bullet:: [' + methodName + '] Expected event name parameter to be longer than 0 characters';
27 | var error = new Error(this.message);
28 | if (typeof error.stack !== 'undefined') this.stack = error.stack;
29 | }
30 | setupCustomError(EventNameLengthError);
31 |
32 | function EventNamesArrayLengthError(methodName) {
33 | this.message = 'Bullet:: [' + methodName + '] Expected event names array to contain one or more event names';
34 | var error = new Error(this.message);
35 | if (typeof error.stack !== 'undefined') this.stack = error.stack;
36 | }
37 | setupCustomError(EventNamesArrayLengthError);
38 |
39 | function UndeclaredEventError(methodName, eventName) {
40 | this.message = 'Bullet:: [' + methodName + '] Event string: "' + eventName + '" does not exist within the events dictionary\nPlease use the Bullet.addEventName method to add this string.';
41 |
42 | var error = new Error(this.message);
43 | if (typeof error.stack !== 'undefined') this.stack = error.stack;
44 | }
45 | setupCustomError(UndeclaredEventError);
46 |
47 | function UnmappedEventError(methodName, eventName) {
48 | this.message = 'Bullet:: [' + methodName + '] Event string: "' + eventName + '" is not mapped to any callbacks\nPlease use the Bullet.on method to map this string to a callback.';
49 |
50 | var error = new Error(this.message);
51 | if (typeof error.stack !== 'undefined') this.stack = error.stack;
52 | }
53 | setupCustomError(UnmappedEventError);
54 |
55 | function setupCustomError(CustomError) {
56 | CustomError.prototype = new Error();
57 | CustomError.prototype.name = CustomError.name;
58 | CustomError.prototype.constructor = CustomError;
59 | }
60 |
61 | var _CALLBACK_NAMESPACE = '__bullet_pubsub__';
62 | // ------------------------------------------------------------------------------------------
63 | // -- Private variables
64 | // ------------------------------------------------------------------------------------------
65 | var _self = this;
66 | var _mappings = {};
67 | var _strictMode = false;
68 | var _triggerAsync = true;
69 |
70 | // Expose custom error type constructors (for testing), but use an underscore to imply privacy.
71 | _self._errors = {
72 | ParamCountError: ParamCountError,
73 | ParamTypeError: ParamTypeError,
74 | EventNameLengthError: EventNameLengthError,
75 | EventNamesArrayLengthError: EventNamesArrayLengthError,
76 | UndeclaredEventError: UndeclaredEventError,
77 | UnmappedEventError: UnmappedEventError
78 | };
79 |
80 | // ------------------------------------------------------------------------------------------
81 | // -- Public variables
82 | // ------------------------------------------------------------------------------------------
83 | _self.events = {};
84 |
85 | // ------------------------------------------------------------------------------------------
86 | // -- Private methods
87 | // ------------------------------------------------------------------------------------------
88 | function _runCallback(eventName, data) {
89 | for (var id in _mappings[eventName].callbacks) {
90 | var callbackObject = _mappings[eventName].callbacks[id];
91 |
92 | if (typeof callbackObject.cb === 'function') callbackObject.cb(data);
93 | if (typeof callbackObject.once === 'boolean' && callbackObject.once === true) _self.off(eventName, callbackObject.cb);
94 | }
95 | }
96 |
97 | function _cloneCallbacks(callbacks) {
98 | var clonedCallbacks = {};
99 |
100 | for (var callbackName in callbacks) {
101 | clonedCallbacks[callbackName] = {
102 | cb: callbacks[callbackName].cb,
103 | once: callbacks[callbackName].once
104 | };
105 | }
106 |
107 | return clonedCallbacks;
108 | }
109 |
110 | function _deleteAllCallbackReferencesForEvent(eventName) {
111 | for (var id in _mappings[eventName].callbacks) {
112 | var callback = _mappings[eventName].callbacks[id].cb;
113 |
114 | callback[_CALLBACK_NAMESPACE].totalEvents--;
115 |
116 | if (callback[_CALLBACK_NAMESPACE].totalEvents === 0) {
117 | delete callback[_CALLBACK_NAMESPACE];
118 | } else {
119 | delete callback[_CALLBACK_NAMESPACE][eventName];
120 | }
121 | }
122 | }
123 |
124 | function _deleteAllCallbackReferences() {
125 | for (var eventName in _mappings) {
126 | _deleteAllCallbackReferencesForEvent(eventName);
127 | }
128 | }
129 |
130 | // Expose _getMappings method (for testing), but use an underscore to imply privacy.
131 | _self._getMappings = function () {
132 | // Return a dictionary object that has no effect on app state to ensure '_mappings'
133 | // stays private, even if the value returned from this method is modified.
134 | var clonedMappings = {};
135 |
136 | for (var mapping in _mappings) {
137 | clonedMappings[mapping] = {
138 | callbacks: _cloneCallbacks(_mappings[mapping].callbacks),
139 | totalCallbacks: _mappings[mapping].totalCallbacks
140 | };
141 | }
142 |
143 | return clonedMappings;
144 | };
145 |
146 | // ------------------------------------------------------------------------------------------
147 | // -- Public methods
148 | // ------------------------------------------------------------------------------------------
149 | _self.on = function (eventName, fn, once) {
150 | if (arguments.length < 2 || arguments.length > 3) {
151 | throw new ParamCountError('on', 'Expected between 2 and 3 parameters', arguments.length);
152 | }
153 |
154 | if (typeof eventName !== 'string') {
155 | throw new ParamTypeError('on', 'event name', eventName, 'string');
156 | } else if (eventName.length === 0) {
157 | throw new EventNameLengthError('on');
158 | } else if (_strictMode && typeof _self.events[eventName] === 'undefined') {
159 | throw new UndeclaredEventError('on', eventName);
160 | }
161 |
162 | if (typeof fn !== 'function') {
163 | throw new ParamTypeError('on', 'callback', fn, 'function');
164 | }
165 |
166 | if (typeof once !== 'undefined' && typeof once !== 'boolean') {
167 | throw new ParamTypeError('on', 'once', once, 'boolean');
168 | }
169 |
170 | // Create a reference between the callback and stored event.
171 | var callbackId = null;
172 |
173 | // If the named event object already exists in the dictionary...
174 | if (typeof _mappings[eventName] !== 'undefined') {
175 | // Attempt to get the callback ID from the callback itself.
176 | if (typeof fn[_CALLBACK_NAMESPACE] === 'undefined') {
177 | fn[_CALLBACK_NAMESPACE] = {
178 | totalEvents: 0
179 | };
180 | }
181 |
182 | // Add a new callback object to the existing event object.
183 | if (typeof fn[_CALLBACK_NAMESPACE][eventName] === 'undefined') {
184 | callbackId = _mappings[eventName].totalCallbacks;
185 |
186 | _mappings[eventName].totalCallbacks++;
187 |
188 | _mappings[eventName].callbacks[callbackId] = {
189 | cb: fn,
190 | once: typeof once === 'boolean' ? once : false
191 | };
192 |
193 | // On the callback, create a reference to the event mapping.
194 | fn[_CALLBACK_NAMESPACE][eventName] = callbackId;
195 | fn[_CALLBACK_NAMESPACE].totalEvents++;
196 | }
197 |
198 | if (typeof once === 'boolean') {
199 | // Get the callback ID from the value of the existing event name key.
200 | callbackId = fn[_CALLBACK_NAMESPACE][eventName];
201 |
202 | // The function already exists, so update it's 'once' value.
203 | _mappings[eventName].callbacks[callbackId].once = once;
204 | }
205 | } else {
206 | // Create a new event object in the dictionary with the specified name and callback.
207 | _mappings[eventName] = {
208 | callbacks: {}
209 | };
210 |
211 | callbackId = 0;
212 |
213 | _mappings[eventName].callbacks[callbackId] = { cb: fn, once: !!once };
214 | _mappings[eventName].totalCallbacks = 1;
215 |
216 | // On the callback, create a reference to the event mapping.
217 | if (typeof fn[_CALLBACK_NAMESPACE] === 'undefined') {
218 | fn[_CALLBACK_NAMESPACE] = {};
219 | fn[_CALLBACK_NAMESPACE].totalEvents = 1;
220 | } else {
221 | fn[_CALLBACK_NAMESPACE].totalEvents++;
222 | }
223 |
224 | fn[_CALLBACK_NAMESPACE][eventName] = callbackId;
225 | }
226 | };
227 |
228 | _self.once = function (eventName, fn) {
229 | if (arguments.length !== 2) {
230 | throw new ParamCountError('once', 'Expected 2 parameters', arguments.length);
231 | } else if (typeof eventName !== 'string') {
232 | throw new ParamTypeError('once', 'event name', eventName, 'string');
233 | } else if (eventName.length === 0) {
234 | throw new EventNameLengthError('once');
235 | } else if (_strictMode && typeof _self.events[eventName] === 'undefined') {
236 | throw new UndeclaredEventError('once', eventName);
237 | }
238 |
239 | if (typeof fn !== 'function') {
240 | throw new ParamTypeError('once', 'callback', fn, 'function');
241 | }
242 |
243 | _self.on(eventName, fn, true);
244 | };
245 |
246 | _self.off = function (eventName, fn) {
247 | if (arguments.length === 0) {
248 | // delete all references to Bullet that exist on mapped callbacks.
249 | _deleteAllCallbackReferences();
250 |
251 | // Remove all mappings from the dictionary.
252 | _mappings = {};
253 |
254 | return;
255 | } else if (typeof eventName !== 'string') {
256 | throw new ParamTypeError('off', 'event name', eventName, 'string');
257 | } else if (eventName.length === 0) {
258 | throw new EventNameLengthError('off');
259 | } else if (_strictMode && typeof _self.events[eventName] === 'undefined') {
260 | throw new UndeclaredEventError('off', eventName);
261 | }
262 |
263 | if (typeof _mappings[eventName] === 'undefined') {
264 | // There is no mapping to remove, so return silently.
265 | return;
266 | }
267 |
268 | // Remove just the function, if passed as a parameter and in the dictionary.
269 | if (typeof fn === 'function') {
270 | // if (typeof fn[_CALLBACK_NAMESPACE] === 'undefined' || typeof fn[_CALLBACK_NAMESPACE][eventName] === 'undefined') {
271 | // // TODO: Throw error here if in strict mode.
272 | // }
273 |
274 | // Retrieve a reference to the stored event from the callback.
275 | var id = fn[_CALLBACK_NAMESPACE][eventName];
276 | var fnToRemove = _mappings[eventName].callbacks[id];
277 |
278 | if (typeof fnToRemove !== 'undefined') {
279 | // delete the callback object from the dictionary.
280 | delete _mappings[eventName].callbacks[id];
281 |
282 | // delete the event reference on the callback function.
283 | delete fn[_CALLBACK_NAMESPACE][eventName];
284 |
285 | _mappings[eventName].totalCallbacks--;
286 | fn[_CALLBACK_NAMESPACE].totalEvents--;
287 |
288 | if (_mappings[eventName].totalCallbacks === 0) {
289 | // There are no more functions in the dictionary that are
290 | // registered to this event, so delete the named event object.
291 | delete _mappings[eventName];
292 | }
293 |
294 | if (fn[_CALLBACK_NAMESPACE].totalEvents === 0) {
295 | // There are no more events registered on this callback,
296 | // so delete the Bullet namespace.
297 | delete fn[_CALLBACK_NAMESPACE];
298 | }
299 | }
300 | } else if (typeof fn !== 'undefined') {
301 | throw new ParamTypeError('off', 'callback', fn, 'function');
302 | } else {
303 | // No callback was passed to the 'off' method...
304 |
305 | // For each callback in _mappings[eventName], delete the reference to
306 | // the specified event name on the callback itself.
307 | _deleteAllCallbackReferencesForEvent(eventName);
308 |
309 | // Delete all functions in the dictionary that are registered to this
310 | // event by deleting the named event object.
311 | delete _mappings[eventName];
312 | }
313 | };
314 |
315 | // Replace a single mapped callback for the specified event name with a new callback.
316 | _self.replaceCallback = function (eventName, oldFn, newFn, once) {
317 | if (typeof eventName !== 'string') {
318 | throw new ParamTypeError('replaceCallback', 'event name', eventName, 'string');
319 | } else if (eventName.length === 0) {
320 | throw new EventNameLengthError('replaceCallback');
321 | } else if (typeof _mappings[eventName] === 'undefined') {
322 | throw new UnmappedEventError('replaceCallback', eventName);
323 | } else if (_strictMode && typeof _self.events[eventName] === 'undefined') {
324 | throw new UndeclaredEventError('replaceCallback', eventName);
325 | }
326 |
327 | if (typeof oldFn !== 'function') {
328 | throw new ParamTypeError('replaceCallback', 'callback', oldFn, 'function');
329 | }
330 |
331 | if (typeof newFn !== 'function') {
332 | throw new ParamTypeError('replaceCallback', 'callback', newFn, 'function');
333 | }
334 |
335 | if (typeof once !== 'undefined' && typeof once !== 'boolean') {
336 | throw new ParamTypeError('replaceCallback', 'once', once, 'boolean');
337 | }
338 |
339 | _self.off(eventName, oldFn);
340 | _self.on(eventName, newFn, once);
341 | };
342 |
343 | // Replace all of the specified event name’s mapped callbacks with the specified callback.
344 | _self.replaceAllCallbacks = function (eventName, newFn, once) {
345 | if (typeof eventName !== 'string') {
346 | throw new ParamTypeError('replace', 'event name', eventName, 'string');
347 | } else if (eventName.length === 0) {
348 | throw new EventNameLengthError('replace');
349 | } else if (typeof _mappings[eventName] === 'undefined') {
350 | throw new UnmappedEventError('replace', eventName);
351 | } else if (_strictMode && typeof _self.events[eventName] === 'undefined') {
352 | throw new UndeclaredEventError('replace', eventName);
353 | }
354 |
355 | if (typeof newFn !== 'function') {
356 | throw new ParamTypeError('replace', 'callback', newFn, 'function');
357 | }
358 |
359 | if (typeof once !== 'undefined' && typeof once !== 'boolean') {
360 | throw new ParamTypeError('replace', 'once', once, 'boolean');
361 | }
362 |
363 | _self.off(eventName);
364 | _self.on(eventName, newFn, once);
365 | };
366 |
367 | _self.trigger = function (eventName, data) {
368 | if (typeof eventName !== 'string') {
369 | throw new ParamTypeError('trigger', 'event name', eventName, 'string');
370 | } else if (eventName.length === 0) {
371 | throw new EventNameLengthError('trigger');
372 | } else if (_strictMode && typeof _self.events[eventName] === 'undefined') {
373 | throw new UndeclaredEventError('trigger', eventName);
374 | }
375 |
376 | if (typeof _mappings[eventName] === 'undefined') {
377 | if (_strictMode) throw new UnmappedEventError('trigger', eventName);
378 |
379 | // Return silently if not in strict mode.
380 | return;
381 | }
382 |
383 | // Check whether or not this is a browser environment.
384 | if (_triggerAsync && typeof window !== 'undefined') {
385 | window.setTimeout(function () {
386 | _runCallback(eventName, data);
387 | }, 0);
388 | } else {
389 | _runCallback(eventName, data);
390 | }
391 | };
392 |
393 | _self.addEventName = function (eventName) {
394 | if (typeof eventName !== 'string') {
395 | throw new ParamTypeError('addEventName', 'event name', eventName, 'string');
396 | } else if (eventName.length === 0) {
397 | throw new EventNameLengthError('addEventName');
398 | }
399 |
400 | _self.events[eventName] = eventName;
401 | };
402 |
403 | _self.addMultipleEventNames = function (eventNames) {
404 | if (!(eventNames instanceof Array)) {
405 | throw new ParamTypeError('addMultipleEventNames', 'event names', eventNames, 'array');
406 | } else if (eventNames.length === 0) {
407 | throw new EventNamesArrayLengthError('addMultipleEventNames');
408 | }
409 |
410 | var i = 0;
411 | var length = eventNames.length;
412 |
413 | for (i; i < length; i++) {
414 | var currentEventName = eventNames[i];
415 |
416 | _self.addEventName(currentEventName);
417 | }
418 | };
419 |
420 | _self.removeEventName = function (eventName) {
421 | if (typeof eventName !== 'string') {
422 | throw new ParamTypeError('removeEventName', 'event name', eventName, 'string');
423 | } else if (eventName.length === 0) {
424 | throw new EventNameLengthError('removeEventName');
425 | }
426 |
427 | if (_self.events[eventName]) delete _self.events[eventName];
428 | };
429 |
430 | _self.getStrictMode = function () {
431 | // Return a boolean that doesn't directly point to the internal '_strictMode' property.
432 | return _strictMode === true;
433 | };
434 |
435 | _self.setStrictMode = function (useStrictMode) {
436 | if (typeof useStrictMode !== 'boolean') {
437 | throw new ParamTypeError('setStrictMode', 'strict mode', useStrictMode, 'boolean');
438 | }
439 |
440 | _strictMode = useStrictMode;
441 | };
442 |
443 | _self.getTriggerAsync = function () {
444 | // Return a boolean that doesn't directly point to the internal '_triggerAsync' property.
445 | return _triggerAsync === true;
446 | };
447 |
448 | _self.setTriggerAsync = function (useAsync) {
449 | if (typeof useAsync !== 'boolean') {
450 | throw new ParamTypeError('setTriggerAsync', 'trigger async', useAsync, 'boolean');
451 | }
452 |
453 | _triggerAsync = useAsync;
454 | };
455 |
456 | // TODO : Create a 'replaceAllEventNames' method with an array of strings passed as a param.
457 | // - include type checks for string while looping over the array.
458 |
459 | // TODO : Create a 'removeAllEventNames' method. No params necessary.
460 | // – Internally this could simply call 'replaceAllEventNames' and pass an empty array as a param.
461 |
462 | // TODO : Create an 'onAny' method with an array of strings passed as the first param and a single callback as the second.
463 | // - include type checks for string while looping over the array.
464 |
465 | // TODO : Create an 'onMultiple' method with an array of flat objects passed as a param.
466 | // - example of required param structure:
467 | // [{eventName: 'someEvent', callback: someCallback, once: false}, {eventName: 'anotherEvent', callback: anotherCallback, once: true}]
468 | }
469 |
470 | // ------------------------------------------------------------------------------------------
471 | // -- Module definition
472 | // ------------------------------------------------------------------------------------------
473 | // Check for AMD/Module support, otherwise define Bullet as a global variable.
474 |
475 | if (typeof define !== 'undefined' && define.amd) {
476 | // AMD. Register as an anonymous module.
477 | define(function () {
478 | return new Bullet();
479 | });
480 | } else if (typeof module !== 'undefined' && module.exports) {
481 | module.exports = new Bullet();
482 | } else {
483 | window.Bullet = new Bullet();
484 | }
485 | })();
--------------------------------------------------------------------------------
/dist/bullet.min.js:
--------------------------------------------------------------------------------
1 | "use strict";var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};!function(){function e(){function e(e,t,n){this.message="Bullet:: ["+e+"] "+t+", but received: "+n;var o=new Error(this.message);void 0!==o.stack&&(this.stack=o.stack)}function t(e,t,n,o){this.message="Bullet:: ["+e+"] Expected parameter - "+t+" - to be type: "+o+", but received type: "+(void 0===n?"undefined":_typeof(n));var a=new TypeError(this.message);void 0!==a.stack&&(this.stack=a.stack)}function n(e){this.message="Bullet:: ["+e+"] Expected event name parameter to be longer than 0 characters";var t=new Error(this.message);void 0!==t.stack&&(this.stack=t.stack)}function o(e){this.message="Bullet:: ["+e+"] Expected event names array to contain one or more event names";var t=new Error(this.message);void 0!==t.stack&&(this.stack=t.stack)}function a(e,t){this.message="Bullet:: ["+e+'] Event string: "'+t+'" does not exist within the events dictionary\nPlease use the Bullet.addEventName method to add this string.';var n=new Error(this.message);void 0!==n.stack&&(this.stack=n.stack)}function r(e,t){this.message="Bullet:: ["+e+'] Event string: "'+t+'" is not mapped to any callbacks\nPlease use the Bullet.on method to map this string to a callback.';var n=new Error(this.message);void 0!==n.stack&&(this.stack=n.stack)}function i(e){e.prototype=new Error,e.prototype.name=e.name,e.prototype.constructor=e}function c(e,t){for(var n in d[e].callbacks){var o=d[e].callbacks[n];"function"==typeof o.cb&&o.cb(t),"boolean"==typeof o.once&&!0===o.once&&w.off(e,o.cb)}}function l(e){var t={};for(var n in e)t[n]={cb:e[n].cb,once:e[n].once};return t}function s(e){for(var t in d[e].callbacks){var n=d[e].callbacks[t].cb;n[v].totalEvents--,0===n[v].totalEvents?delete n[v]:delete n[v][e]}}function f(){for(var e in d)s(e)}i(e),i(t),i(n),i(o),i(a),i(r);var v="__bullet_pubsub__",w=this,d={},h=!1,u=!0;w._errors={ParamCountError:e,ParamTypeError:t,EventNameLengthError:n,EventNamesArrayLengthError:o,UndeclaredEventError:a,UnmappedEventError:r},w.events={},w._getMappings=function(){var e={};for(var t in d)e[t]={callbacks:l(d[t].callbacks),totalCallbacks:d[t].totalCallbacks};return e},w.on=function(o,r,i){if(arguments.length<2||arguments.length>3)throw new e("on","Expected between 2 and 3 parameters",arguments.length);if("string"!=typeof o)throw new t("on","event name",o,"string");if(0===o.length)throw new n("on");if(h&&void 0===w.events[o])throw new a("on",o);if("function"!=typeof r)throw new t("on","callback",r,"function");if(void 0!==i&&"boolean"!=typeof i)throw new t("on","once",i,"boolean");var c=null;void 0!==d[o]?(void 0===r[v]&&(r[v]={totalEvents:0}),void 0===r[v][o]&&(c=d[o].totalCallbacks,d[o].totalCallbacks++,d[o].callbacks[c]={cb:r,once:"boolean"==typeof i&&i},r[v][o]=c,r[v].totalEvents++),"boolean"==typeof i&&(c=r[v][o],d[o].callbacks[c].once=i)):(d[o]={callbacks:{}},c=0,d[o].callbacks[c]={cb:r,once:!!i},d[o].totalCallbacks=1,void 0===r[v]?(r[v]={},r[v].totalEvents=1):r[v].totalEvents++,r[v][o]=c)},w.once=function(o,r){if(2!==arguments.length)throw new e("once","Expected 2 parameters",arguments.length);if("string"!=typeof o)throw new t("once","event name",o,"string");if(0===o.length)throw new n("once");if(h&&void 0===w.events[o])throw new a("once",o);if("function"!=typeof r)throw new t("once","callback",r,"function");w.on(o,r,!0)},w.off=function(e,o){if(0===arguments.length)return f(),void(d={});if("string"!=typeof e)throw new t("off","event name",e,"string");if(0===e.length)throw new n("off");if(h&&void 0===w.events[e])throw new a("off",e);if(void 0!==d[e])if("function"==typeof o){var r=o[v][e];void 0!==d[e].callbacks[r]&&(delete d[e].callbacks[r],delete o[v][e],d[e].totalCallbacks--,o[v].totalEvents--,0===d[e].totalCallbacks&&delete d[e],0===o[v].totalEvents&&delete o[v])}else{if(void 0!==o)throw new t("off","callback",o,"function");s(e),delete d[e]}},w.replaceCallback=function(e,o,i,c){if("string"!=typeof e)throw new t("replaceCallback","event name",e,"string");if(0===e.length)throw new n("replaceCallback");if(void 0===d[e])throw new r("replaceCallback",e);if(h&&void 0===w.events[e])throw new a("replaceCallback",e);if("function"!=typeof o)throw new t("replaceCallback","callback",o,"function");if("function"!=typeof i)throw new t("replaceCallback","callback",i,"function");if(void 0!==c&&"boolean"!=typeof c)throw new t("replaceCallback","once",c,"boolean");w.off(e,o),w.on(e,i,c)},w.replaceAllCallbacks=function(e,o,i){if("string"!=typeof e)throw new t("replace","event name",e,"string");if(0===e.length)throw new n("replace");if(void 0===d[e])throw new r("replace",e);if(h&&void 0===w.events[e])throw new a("replace",e);if("function"!=typeof o)throw new t("replace","callback",o,"function");if(void 0!==i&&"boolean"!=typeof i)throw new t("replace","once",i,"boolean");w.off(e),w.on(e,o,i)},w.trigger=function(e,o){if("string"!=typeof e)throw new t("trigger","event name",e,"string");if(0===e.length)throw new n("trigger");if(h&&void 0===w.events[e])throw new a("trigger",e);if(void 0!==d[e])u&&"undefined"!=typeof window?window.setTimeout(function(){c(e,o)},0):c(e,o);else if(h)throw new r("trigger",e)},w.addEventName=function(e){if("string"!=typeof e)throw new t("addEventName","event name",e,"string");if(0===e.length)throw new n("addEventName");w.events[e]=e},w.addMultipleEventNames=function(e){if(!(e instanceof Array))throw new t("addMultipleEventNames","event names",e,"array");if(0===e.length)throw new o("addMultipleEventNames");var n=0,a=e.length;for(n;n
2 |
3 |
4 |
5 | Bullet
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Foo events triggered: 0
14 | Bar events triggered: 0
15 | Baz events triggered: 0
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/example/js/app/app.js:
--------------------------------------------------------------------------------
1 | // TODO : Visually display the current state of Bullet.events
2 | // TODO : Provide buttons to add/remove events
3 | // TODO : Provide buttons to map/unmap events to callbacks??
4 |
5 | (function () {
6 |
7 | 'use strict';
8 |
9 | var Bullet = window.Bullet;
10 |
11 | // Bullet.setStrictMode(true);
12 |
13 | Bullet.addEventName('foo');
14 | Bullet.addEventName('bar');
15 | Bullet.addEventName('baz');
16 |
17 | var buttonFoo = document.getElementById('button-foo');
18 | var buttonBar = document.getElementById('button-bar');
19 | var buttonBaz = document.getElementById('button-baz');
20 | var textFoo = document.getElementById('text-foo');
21 | var textBar = document.getElementById('text-bar');
22 | var textBaz = document.getElementById('text-baz');
23 |
24 | var numFooEvents = 0;
25 | var numBarEvents = 0;
26 | var numBazEvents = 0;
27 |
28 | function fooEventHandler ()
29 | {
30 | numFooEvents++;
31 | textFoo.innerHTML = numFooEvents;
32 | }
33 |
34 | function barEventHandler ()
35 | {
36 | numBarEvents++;
37 | textBar.innerHTML = numBarEvents;
38 | }
39 |
40 | function bazEventHandler ()
41 | {
42 | numBazEvents++;
43 | textBaz.innerHTML = numBazEvents;
44 | }
45 |
46 | function triggerFoo ()
47 | {
48 | Bullet.trigger(Bullet.events.foo);
49 | }
50 |
51 | function triggerBar ()
52 | {
53 | Bullet.trigger(Bullet.events.bar);
54 | }
55 |
56 | function triggerBaz ()
57 | {
58 | Bullet.trigger(Bullet.events.baz);
59 | }
60 |
61 | Bullet.on(Bullet.events.foo, fooEventHandler);
62 | Bullet.on(Bullet.events.bar, barEventHandler);
63 | Bullet.once(Bullet.events.baz, bazEventHandler);
64 |
65 | buttonFoo.onclick = triggerFoo;
66 | buttonBar.onclick = triggerBar;
67 | buttonBaz.onclick = triggerBaz;
68 | })();
--------------------------------------------------------------------------------
/example/js/libs/bullet.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
4 |
5 | (function () {
6 |
7 | function Bullet() {
8 | // ------------------------------------------------------------------------------------------
9 | // -- Custom Errors
10 | // ------------------------------------------------------------------------------------------
11 | function ParamCountError(methodName, expectedParamsString, paramCount) {
12 | this.message = 'Bullet:: [' + methodName + '] ' + expectedParamsString + ', but received: ' + paramCount;
13 | var error = new Error(this.message);
14 | if (typeof error.stack !== 'undefined') this.stack = error.stack;
15 | }
16 | setupCustomError(ParamCountError);
17 |
18 | function ParamTypeError(methodName, parameterName, parameter, expectedType) {
19 | this.message = 'Bullet:: [' + methodName + '] Expected parameter - ' + parameterName + ' - to be type: ' + expectedType + ', but received type: ' + (typeof parameter === 'undefined' ? 'undefined' : _typeof(parameter));
20 | var error = new TypeError(this.message);
21 | if (typeof error.stack !== 'undefined') this.stack = error.stack;
22 | }
23 | setupCustomError(ParamTypeError);
24 |
25 | function EventNameLengthError(methodName) {
26 | this.message = 'Bullet:: [' + methodName + '] Expected event name parameter to be longer than 0 characters';
27 | var error = new Error(this.message);
28 | if (typeof error.stack !== 'undefined') this.stack = error.stack;
29 | }
30 | setupCustomError(EventNameLengthError);
31 |
32 | function EventNamesArrayLengthError(methodName) {
33 | this.message = 'Bullet:: [' + methodName + '] Expected event names array to contain one or more event names';
34 | var error = new Error(this.message);
35 | if (typeof error.stack !== 'undefined') this.stack = error.stack;
36 | }
37 | setupCustomError(EventNamesArrayLengthError);
38 |
39 | function UndeclaredEventError(methodName, eventName) {
40 | this.message = 'Bullet:: [' + methodName + '] Event string: "' + eventName + '" does not exist within the events dictionary\nPlease use the Bullet.addEventName method to add this string.';
41 |
42 | var error = new Error(this.message);
43 | if (typeof error.stack !== 'undefined') this.stack = error.stack;
44 | }
45 | setupCustomError(UndeclaredEventError);
46 |
47 | function UnmappedEventError(methodName, eventName) {
48 | this.message = 'Bullet:: [' + methodName + '] Event string: "' + eventName + '" is not mapped to any callbacks\nPlease use the Bullet.on method to map this string to a callback.';
49 |
50 | var error = new Error(this.message);
51 | if (typeof error.stack !== 'undefined') this.stack = error.stack;
52 | }
53 | setupCustomError(UnmappedEventError);
54 |
55 | function setupCustomError(CustomError) {
56 | CustomError.prototype = new Error();
57 | CustomError.prototype.name = CustomError.name;
58 | CustomError.prototype.constructor = CustomError;
59 | }
60 |
61 | var _CALLBACK_NAMESPACE = '__bullet_pubsub__';
62 | // ------------------------------------------------------------------------------------------
63 | // -- Private variables
64 | // ------------------------------------------------------------------------------------------
65 | var _self = this;
66 | var _mappings = {};
67 | var _strictMode = false;
68 | var _triggerAsync = true;
69 |
70 | // Expose custom error type constructors (for testing), but use an underscore to imply privacy.
71 | _self._errors = {
72 | ParamCountError: ParamCountError,
73 | ParamTypeError: ParamTypeError,
74 | EventNameLengthError: EventNameLengthError,
75 | EventNamesArrayLengthError: EventNamesArrayLengthError,
76 | UndeclaredEventError: UndeclaredEventError,
77 | UnmappedEventError: UnmappedEventError
78 | };
79 |
80 | // ------------------------------------------------------------------------------------------
81 | // -- Public variables
82 | // ------------------------------------------------------------------------------------------
83 | _self.events = {};
84 |
85 | // ------------------------------------------------------------------------------------------
86 | // -- Private methods
87 | // ------------------------------------------------------------------------------------------
88 | function _runCallback(eventName, data) {
89 | for (var id in _mappings[eventName].callbacks) {
90 | var callbackObject = _mappings[eventName].callbacks[id];
91 |
92 | if (typeof callbackObject.cb === 'function') callbackObject.cb(data);
93 | if (typeof callbackObject.once === 'boolean' && callbackObject.once === true) _self.off(eventName, callbackObject.cb);
94 | }
95 | }
96 |
97 | function _cloneCallbacks(callbacks) {
98 | var clonedCallbacks = {};
99 |
100 | for (var callbackName in callbacks) {
101 | clonedCallbacks[callbackName] = {
102 | cb: callbacks[callbackName].cb,
103 | once: callbacks[callbackName].once
104 | };
105 | }
106 |
107 | return clonedCallbacks;
108 | }
109 |
110 | function _deleteAllCallbackReferencesForEvent(eventName) {
111 | for (var id in _mappings[eventName].callbacks) {
112 | var callback = _mappings[eventName].callbacks[id].cb;
113 |
114 | callback[_CALLBACK_NAMESPACE].totalEvents--;
115 |
116 | if (callback[_CALLBACK_NAMESPACE].totalEvents === 0) {
117 | delete callback[_CALLBACK_NAMESPACE];
118 | } else {
119 | delete callback[_CALLBACK_NAMESPACE][eventName];
120 | }
121 | }
122 | }
123 |
124 | function _deleteAllCallbackReferences() {
125 | for (var eventName in _mappings) {
126 | _deleteAllCallbackReferencesForEvent(eventName);
127 | }
128 | }
129 |
130 | // Expose _getMappings method (for testing), but use an underscore to imply privacy.
131 | _self._getMappings = function () {
132 | // Return a dictionary object that has no effect on app state to ensure '_mappings'
133 | // stays private, even if the value returned from this method is modified.
134 | var clonedMappings = {};
135 |
136 | for (var mapping in _mappings) {
137 | clonedMappings[mapping] = {
138 | callbacks: _cloneCallbacks(_mappings[mapping].callbacks),
139 | totalCallbacks: _mappings[mapping].totalCallbacks
140 | };
141 | }
142 |
143 | return clonedMappings;
144 | };
145 |
146 | // ------------------------------------------------------------------------------------------
147 | // -- Public methods
148 | // ------------------------------------------------------------------------------------------
149 | _self.on = function (eventName, fn, once) {
150 | if (arguments.length < 2 || arguments.length > 3) {
151 | throw new ParamCountError('on', 'Expected between 2 and 3 parameters', arguments.length);
152 | }
153 |
154 | if (typeof eventName !== 'string') {
155 | throw new ParamTypeError('on', 'event name', eventName, 'string');
156 | } else if (eventName.length === 0) {
157 | throw new EventNameLengthError('on');
158 | } else if (_strictMode && typeof _self.events[eventName] === 'undefined') {
159 | throw new UndeclaredEventError('on', eventName);
160 | }
161 |
162 | if (typeof fn !== 'function') {
163 | throw new ParamTypeError('on', 'callback', fn, 'function');
164 | }
165 |
166 | if (typeof once !== 'undefined' && typeof once !== 'boolean') {
167 | throw new ParamTypeError('on', 'once', once, 'boolean');
168 | }
169 |
170 | // Create a reference between the callback and stored event.
171 | var callbackId = null;
172 |
173 | // If the named event object already exists in the dictionary...
174 | if (typeof _mappings[eventName] !== 'undefined') {
175 | // Attempt to get the callback ID from the callback itself.
176 | if (typeof fn[_CALLBACK_NAMESPACE] === 'undefined') {
177 | fn[_CALLBACK_NAMESPACE] = {
178 | totalEvents: 0
179 | };
180 | }
181 |
182 | // Add a new callback object to the existing event object.
183 | if (typeof fn[_CALLBACK_NAMESPACE][eventName] === 'undefined') {
184 | callbackId = _mappings[eventName].totalCallbacks;
185 |
186 | _mappings[eventName].totalCallbacks++;
187 |
188 | _mappings[eventName].callbacks[callbackId] = {
189 | cb: fn,
190 | once: typeof once === 'boolean' ? once : false
191 | };
192 |
193 | // On the callback, create a reference to the event mapping.
194 | fn[_CALLBACK_NAMESPACE][eventName] = callbackId;
195 | fn[_CALLBACK_NAMESPACE].totalEvents++;
196 | }
197 |
198 | if (typeof once === 'boolean') {
199 | // Get the callback ID from the value of the existing event name key.
200 | callbackId = fn[_CALLBACK_NAMESPACE][eventName];
201 |
202 | // The function already exists, so update it's 'once' value.
203 | _mappings[eventName].callbacks[callbackId].once = once;
204 | }
205 | } else {
206 | // Create a new event object in the dictionary with the specified name and callback.
207 | _mappings[eventName] = {
208 | callbacks: {}
209 | };
210 |
211 | callbackId = 0;
212 |
213 | _mappings[eventName].callbacks[callbackId] = { cb: fn, once: !!once };
214 | _mappings[eventName].totalCallbacks = 1;
215 |
216 | // On the callback, create a reference to the event mapping.
217 | if (typeof fn[_CALLBACK_NAMESPACE] === 'undefined') {
218 | fn[_CALLBACK_NAMESPACE] = {};
219 | fn[_CALLBACK_NAMESPACE].totalEvents = 1;
220 | } else {
221 | fn[_CALLBACK_NAMESPACE].totalEvents++;
222 | }
223 |
224 | fn[_CALLBACK_NAMESPACE][eventName] = callbackId;
225 | }
226 | };
227 |
228 | _self.once = function (eventName, fn) {
229 | if (arguments.length !== 2) {
230 | throw new ParamCountError('once', 'Expected 2 parameters', arguments.length);
231 | } else if (typeof eventName !== 'string') {
232 | throw new ParamTypeError('once', 'event name', eventName, 'string');
233 | } else if (eventName.length === 0) {
234 | throw new EventNameLengthError('once');
235 | } else if (_strictMode && typeof _self.events[eventName] === 'undefined') {
236 | throw new UndeclaredEventError('once', eventName);
237 | }
238 |
239 | if (typeof fn !== 'function') {
240 | throw new ParamTypeError('once', 'callback', fn, 'function');
241 | }
242 |
243 | _self.on(eventName, fn, true);
244 | };
245 |
246 | _self.off = function (eventName, fn) {
247 | if (arguments.length === 0) {
248 | // delete all references to Bullet that exist on mapped callbacks.
249 | _deleteAllCallbackReferences();
250 |
251 | // Remove all mappings from the dictionary.
252 | _mappings = {};
253 |
254 | return;
255 | } else if (typeof eventName !== 'string') {
256 | throw new ParamTypeError('off', 'event name', eventName, 'string');
257 | } else if (eventName.length === 0) {
258 | throw new EventNameLengthError('off');
259 | } else if (_strictMode && typeof _self.events[eventName] === 'undefined') {
260 | throw new UndeclaredEventError('off', eventName);
261 | }
262 |
263 | if (typeof _mappings[eventName] === 'undefined') {
264 | // There is no mapping to remove, so return silently.
265 | return;
266 | }
267 |
268 | // Remove just the function, if passed as a parameter and in the dictionary.
269 | if (typeof fn === 'function') {
270 | // if (typeof fn[_CALLBACK_NAMESPACE] === 'undefined' || typeof fn[_CALLBACK_NAMESPACE][eventName] === 'undefined') {
271 | // // TODO: Throw error here if in strict mode.
272 | // }
273 |
274 | // Retrieve a reference to the stored event from the callback.
275 | var id = fn[_CALLBACK_NAMESPACE][eventName];
276 | var fnToRemove = _mappings[eventName].callbacks[id];
277 |
278 | if (typeof fnToRemove !== 'undefined') {
279 | // delete the callback object from the dictionary.
280 | delete _mappings[eventName].callbacks[id];
281 |
282 | // delete the event reference on the callback function.
283 | delete fn[_CALLBACK_NAMESPACE][eventName];
284 |
285 | _mappings[eventName].totalCallbacks--;
286 | fn[_CALLBACK_NAMESPACE].totalEvents--;
287 |
288 | if (_mappings[eventName].totalCallbacks === 0) {
289 | // There are no more functions in the dictionary that are
290 | // registered to this event, so delete the named event object.
291 | delete _mappings[eventName];
292 | }
293 |
294 | if (fn[_CALLBACK_NAMESPACE].totalEvents === 0) {
295 | // There are no more events registered on this callback,
296 | // so delete the Bullet namespace.
297 | delete fn[_CALLBACK_NAMESPACE];
298 | }
299 | }
300 | } else if (typeof fn !== 'undefined') {
301 | throw new ParamTypeError('off', 'callback', fn, 'function');
302 | } else {
303 | // No callback was passed to the 'off' method...
304 |
305 | // For each callback in _mappings[eventName], delete the reference to
306 | // the specified event name on the callback itself.
307 | _deleteAllCallbackReferencesForEvent(eventName);
308 |
309 | // Delete all functions in the dictionary that are registered to this
310 | // event by deleting the named event object.
311 | delete _mappings[eventName];
312 | }
313 | };
314 |
315 | // Replace a single mapped callback for the specified event name with a new callback.
316 | _self.replaceCallback = function (eventName, oldFn, newFn, once) {
317 | if (typeof eventName !== 'string') {
318 | throw new ParamTypeError('replaceCallback', 'event name', eventName, 'string');
319 | } else if (eventName.length === 0) {
320 | throw new EventNameLengthError('replaceCallback');
321 | } else if (typeof _mappings[eventName] === 'undefined') {
322 | throw new UnmappedEventError('replaceCallback', eventName);
323 | } else if (_strictMode && typeof _self.events[eventName] === 'undefined') {
324 | throw new UndeclaredEventError('replaceCallback', eventName);
325 | }
326 |
327 | if (typeof oldFn !== 'function') {
328 | throw new ParamTypeError('replaceCallback', 'callback', oldFn, 'function');
329 | }
330 |
331 | if (typeof newFn !== 'function') {
332 | throw new ParamTypeError('replaceCallback', 'callback', newFn, 'function');
333 | }
334 |
335 | if (typeof once !== 'undefined' && typeof once !== 'boolean') {
336 | throw new ParamTypeError('replaceCallback', 'once', once, 'boolean');
337 | }
338 |
339 | _self.off(eventName, oldFn);
340 | _self.on(eventName, newFn, once);
341 | };
342 |
343 | // Replace all of the specified event name’s mapped callbacks with the specified callback.
344 | _self.replaceAllCallbacks = function (eventName, newFn, once) {
345 | if (typeof eventName !== 'string') {
346 | throw new ParamTypeError('replace', 'event name', eventName, 'string');
347 | } else if (eventName.length === 0) {
348 | throw new EventNameLengthError('replace');
349 | } else if (typeof _mappings[eventName] === 'undefined') {
350 | throw new UnmappedEventError('replace', eventName);
351 | } else if (_strictMode && typeof _self.events[eventName] === 'undefined') {
352 | throw new UndeclaredEventError('replace', eventName);
353 | }
354 |
355 | if (typeof newFn !== 'function') {
356 | throw new ParamTypeError('replace', 'callback', newFn, 'function');
357 | }
358 |
359 | if (typeof once !== 'undefined' && typeof once !== 'boolean') {
360 | throw new ParamTypeError('replace', 'once', once, 'boolean');
361 | }
362 |
363 | _self.off(eventName);
364 | _self.on(eventName, newFn, once);
365 | };
366 |
367 | _self.trigger = function (eventName, data) {
368 | if (typeof eventName !== 'string') {
369 | throw new ParamTypeError('trigger', 'event name', eventName, 'string');
370 | } else if (eventName.length === 0) {
371 | throw new EventNameLengthError('trigger');
372 | } else if (_strictMode && typeof _self.events[eventName] === 'undefined') {
373 | throw new UndeclaredEventError('trigger', eventName);
374 | }
375 |
376 | if (typeof _mappings[eventName] === 'undefined') {
377 | if (_strictMode) throw new UnmappedEventError('trigger', eventName);
378 |
379 | // Return silently if not in strict mode.
380 | return;
381 | }
382 |
383 | // Check whether or not this is a browser environment.
384 | if (_triggerAsync && typeof window !== 'undefined') {
385 | window.setTimeout(function () {
386 | _runCallback(eventName, data);
387 | }, 0);
388 | } else {
389 | _runCallback(eventName, data);
390 | }
391 | };
392 |
393 | _self.addEventName = function (eventName) {
394 | if (typeof eventName !== 'string') {
395 | throw new ParamTypeError('addEventName', 'event name', eventName, 'string');
396 | } else if (eventName.length === 0) {
397 | throw new EventNameLengthError('addEventName');
398 | }
399 |
400 | _self.events[eventName] = eventName;
401 | };
402 |
403 | _self.addMultipleEventNames = function (eventNames) {
404 | if (!(eventNames instanceof Array)) {
405 | throw new ParamTypeError('addMultipleEventNames', 'event names', eventNames, 'array');
406 | } else if (eventNames.length === 0) {
407 | throw new EventNamesArrayLengthError('addMultipleEventNames');
408 | }
409 |
410 | var i = 0;
411 | var length = eventNames.length;
412 |
413 | for (i; i < length; i++) {
414 | var currentEventName = eventNames[i];
415 |
416 | _self.addEventName(currentEventName);
417 | }
418 | };
419 |
420 | _self.removeEventName = function (eventName) {
421 | if (typeof eventName !== 'string') {
422 | throw new ParamTypeError('removeEventName', 'event name', eventName, 'string');
423 | } else if (eventName.length === 0) {
424 | throw new EventNameLengthError('removeEventName');
425 | }
426 |
427 | if (_self.events[eventName]) delete _self.events[eventName];
428 | };
429 |
430 | _self.getStrictMode = function () {
431 | // Return a boolean that doesn't directly point to the internal '_strictMode' property.
432 | return _strictMode === true;
433 | };
434 |
435 | _self.setStrictMode = function (useStrictMode) {
436 | if (typeof useStrictMode !== 'boolean') {
437 | throw new ParamTypeError('setStrictMode', 'strict mode', useStrictMode, 'boolean');
438 | }
439 |
440 | _strictMode = useStrictMode;
441 | };
442 |
443 | _self.getTriggerAsync = function () {
444 | // Return a boolean that doesn't directly point to the internal '_triggerAsync' property.
445 | return _triggerAsync === true;
446 | };
447 |
448 | _self.setTriggerAsync = function (useAsync) {
449 | if (typeof useAsync !== 'boolean') {
450 | throw new ParamTypeError('setTriggerAsync', 'trigger async', useAsync, 'boolean');
451 | }
452 |
453 | _triggerAsync = useAsync;
454 | };
455 |
456 | // TODO : Create a 'replaceAllEventNames' method with an array of strings passed as a param.
457 | // - include type checks for string while looping over the array.
458 |
459 | // TODO : Create a 'removeAllEventNames' method. No params necessary.
460 | // – Internally this could simply call 'replaceAllEventNames' and pass an empty array as a param.
461 |
462 | // TODO : Create an 'onAny' method with an array of strings passed as the first param and a single callback as the second.
463 | // - include type checks for string while looping over the array.
464 |
465 | // TODO : Create an 'onMultiple' method with an array of flat objects passed as a param.
466 | // - example of required param structure:
467 | // [{eventName: 'someEvent', callback: someCallback, once: false}, {eventName: 'anotherEvent', callback: anotherCallback, once: true}]
468 | }
469 |
470 | // ------------------------------------------------------------------------------------------
471 | // -- Module definition
472 | // ------------------------------------------------------------------------------------------
473 | // Check for AMD/Module support, otherwise define Bullet as a global variable.
474 |
475 | if (typeof define !== 'undefined' && define.amd) {
476 | // AMD. Register as an anonymous module.
477 | define(function () {
478 | return new Bullet();
479 | });
480 | } else if (typeof module !== 'undefined' && module.exports) {
481 | module.exports = new Bullet();
482 | } else {
483 | window.Bullet = new Bullet();
484 | }
485 | })();
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | const gulp = require('gulp');
2 | const rename = require('gulp-rename');
3 | const babel = require('gulp-babel');
4 | const uglify = require('gulp-uglify');
5 | const pump = require('pump');
6 |
7 | gulp.task('compile', (cb) => {
8 | pump([
9 | gulp.src('src/bullet.js'),
10 | babel({presets: ['es2015']}),
11 | gulp.dest('dist'),
12 | gulp.dest('example/js/libs'),
13 | rename('bullet.min.js'),
14 | uglify({
15 | compress : {
16 | drop_console: true
17 | },
18 | mangle : true,
19 | }),
20 | gulp.dest('dist'),
21 | ], cb);
22 | });
23 |
24 | gulp.task('default', ['watch']);
25 |
26 | gulp.task('watch', ['compile'], () => {
27 | gulp.watch('src/bullet.js', ['compile']);
28 | });
29 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bullet-pubsub",
3 | "title": "Bullet",
4 | "description": "A lightweight and simple to use pub-sub library.",
5 | "version": "2.3.0",
6 | "homepage": "https://github.com/munkychop/bullet",
7 | "author": {
8 | "name": "Ivan Hayes",
9 | "url": "http://www.ivanhayes.com"
10 | },
11 | "main": "dist/bullet.js",
12 | "scripts": {
13 | "test": "mocha test/**/*.js",
14 | "lint": "eslint src/bullet.js",
15 | "watch": "gulp",
16 | "start": "npm run test && npm run watch",
17 | "dist": "npm run lint && npm run test && gulp compile"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git://github.com/munkychop/bullet.git"
22 | },
23 | "keywords": [
24 | "bullet",
25 | "io",
26 | "emit",
27 | "events",
28 | "javascript",
29 | "library",
30 | "pub-sub",
31 | "pubsub",
32 | "communication"
33 | ],
34 | "private": false,
35 | "license": "MIT",
36 | "devDependencies": {
37 | "babel-preset-es2015": "^6.24.1",
38 | "chai": "^3.0.0",
39 | "eslint": "^4.8.0",
40 | "gulp": "^3.9.1",
41 | "gulp-babel": "^7.0.0",
42 | "gulp-rename": "^1.2.2",
43 | "gulp-uglify": "^3.0.0",
44 | "mocha": "^3.5.3",
45 | "pump": "^1.0.2",
46 | "sinon": "^1.16.1",
47 | "sinon-chai": "^2.8.0"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/bullet.js:
--------------------------------------------------------------------------------
1 | (function () {
2 |
3 | function Bullet () {
4 | // ------------------------------------------------------------------------------------------
5 | // -- Custom Errors
6 | // ------------------------------------------------------------------------------------------
7 | function ParamCountError (methodName, expectedParamsString, paramCount) {
8 | this.message = 'Bullet:: [' + methodName + '] ' + expectedParamsString + ', but received: ' + paramCount;
9 | const error = new Error(this.message);
10 | if (typeof error.stack !== 'undefined') this.stack = error.stack;
11 | }
12 | setupCustomError(ParamCountError);
13 |
14 | function ParamTypeError (methodName, parameterName, parameter, expectedType) {
15 | this.message = 'Bullet:: [' + methodName + '] Expected parameter - ' + parameterName + ' - to be type: ' + expectedType + ', but received type: ' + typeof parameter;
16 | const error = new TypeError(this.message);
17 | if (typeof error.stack !== 'undefined') this.stack = error.stack;
18 | }
19 | setupCustomError(ParamTypeError);
20 |
21 | function EventNameLengthError (methodName) {
22 | this.message = 'Bullet:: [' + methodName + '] Expected event name parameter to be longer than 0 characters';
23 | const error = new Error(this.message);
24 | if (typeof error.stack !== 'undefined') this.stack = error.stack;
25 | }
26 | setupCustomError(EventNameLengthError);
27 |
28 | function EventNamesArrayLengthError (methodName) {
29 | this.message = 'Bullet:: [' + methodName + '] Expected event names array to contain one or more event names';
30 | const error = new Error(this.message);
31 | if (typeof error.stack !== 'undefined') this.stack = error.stack;
32 | }
33 | setupCustomError(EventNamesArrayLengthError);
34 |
35 | function UndeclaredEventError (methodName, eventName) {
36 | this.message = 'Bullet:: [' + methodName + '] Event string: "' + eventName + '" does not exist within the events dictionary\nPlease use the Bullet.addEventName method to add this string.';
37 |
38 | const error = new Error(this.message);
39 | if (typeof error.stack !== 'undefined') this.stack = error.stack;
40 | }
41 | setupCustomError(UndeclaredEventError);
42 |
43 | function UnmappedEventError (methodName, eventName) {
44 | this.message = 'Bullet:: [' + methodName + '] Event string: "' + eventName + '" is not mapped to any callbacks\nPlease use the Bullet.on method to map this string to a callback.';
45 |
46 | const error = new Error(this.message);
47 | if (typeof error.stack !== 'undefined') this.stack = error.stack;
48 | }
49 | setupCustomError(UnmappedEventError);
50 |
51 | function setupCustomError (CustomError) {
52 | CustomError.prototype = new Error();
53 | CustomError.prototype.name = CustomError.name;
54 | CustomError.prototype.constructor = CustomError;
55 | }
56 |
57 | const _CALLBACK_NAMESPACE = '__bullet_pubsub__';
58 | // ------------------------------------------------------------------------------------------
59 | // -- Private variables
60 | // ------------------------------------------------------------------------------------------
61 | const _self = this;
62 | let _mappings = {};
63 | let _strictMode = false;
64 | let _triggerAsync = true;
65 |
66 | // Expose custom error type constructors (for testing), but use an underscore to imply privacy.
67 | _self._errors = {
68 | ParamCountError : ParamCountError,
69 | ParamTypeError : ParamTypeError,
70 | EventNameLengthError : EventNameLengthError,
71 | EventNamesArrayLengthError: EventNamesArrayLengthError,
72 | UndeclaredEventError : UndeclaredEventError,
73 | UnmappedEventError : UnmappedEventError,
74 | };
75 |
76 |
77 | // ------------------------------------------------------------------------------------------
78 | // -- Public variables
79 | // ------------------------------------------------------------------------------------------
80 | _self.events = {};
81 |
82 |
83 | // ------------------------------------------------------------------------------------------
84 | // -- Private methods
85 | // ------------------------------------------------------------------------------------------
86 | function _runCallback (eventName, data) {
87 | for (const id in _mappings[eventName].callbacks) {
88 | const callbackObject = _mappings[eventName].callbacks[id];
89 |
90 | if (typeof callbackObject.cb === 'function') callbackObject.cb(data);
91 | if (typeof callbackObject.once === 'boolean' && callbackObject.once === true) _self.off(eventName, callbackObject.cb);
92 | }
93 | }
94 |
95 | function _cloneCallbacks (callbacks) {
96 | const clonedCallbacks = {};
97 |
98 | for (const callbackName in callbacks) {
99 | clonedCallbacks[callbackName] = {
100 | cb : callbacks[callbackName].cb,
101 | once : callbacks[callbackName].once
102 | };
103 | }
104 |
105 | return clonedCallbacks;
106 | }
107 |
108 | function _deleteAllCallbackReferencesForEvent (eventName) {
109 | for (const id in _mappings[eventName].callbacks) {
110 | const callback = _mappings[eventName].callbacks[id].cb;
111 |
112 | callback[_CALLBACK_NAMESPACE].totalEvents--;
113 |
114 | if (callback[_CALLBACK_NAMESPACE].totalEvents === 0) {
115 | delete callback[_CALLBACK_NAMESPACE];
116 | }
117 | else {
118 | delete callback[_CALLBACK_NAMESPACE][eventName];
119 | }
120 | }
121 | }
122 |
123 | function _deleteAllCallbackReferences () {
124 | for (const eventName in _mappings) {
125 | _deleteAllCallbackReferencesForEvent(eventName);
126 | }
127 | }
128 |
129 | // Expose _getMappings method (for testing), but use an underscore to imply privacy.
130 | _self._getMappings = function () {
131 | // Return a dictionary object that has no effect on app state to ensure '_mappings'
132 | // stays private, even if the value returned from this method is modified.
133 | const clonedMappings = {};
134 |
135 | for (const mapping in _mappings) {
136 | clonedMappings[mapping] = {
137 | callbacks : _cloneCallbacks(_mappings[mapping].callbacks),
138 | totalCallbacks : _mappings[mapping].totalCallbacks
139 | };
140 | }
141 |
142 | return clonedMappings;
143 | };
144 |
145 |
146 | // ------------------------------------------------------------------------------------------
147 | // -- Public methods
148 | // ------------------------------------------------------------------------------------------
149 | _self.on = function (eventName, fn, once) {
150 | if (arguments.length < 2 || arguments.length > 3) {
151 | throw new ParamCountError('on', 'Expected between 2 and 3 parameters', arguments.length);
152 | }
153 |
154 | if (typeof eventName !== 'string') {
155 | throw new ParamTypeError('on', 'event name', eventName, 'string');
156 | }
157 | else if (eventName.length === 0) {
158 | throw new EventNameLengthError('on');
159 | }
160 | else if (_strictMode && typeof _self.events[eventName] === 'undefined') {
161 | throw new UndeclaredEventError('on', eventName);
162 | }
163 |
164 | if (typeof fn !== 'function') {
165 | throw new ParamTypeError('on', 'callback', fn, 'function');
166 | }
167 |
168 | if (typeof once !== 'undefined' && typeof once !== 'boolean') {
169 | throw new ParamTypeError('on', 'once', once, 'boolean');
170 | }
171 |
172 | // Create a reference between the callback and stored event.
173 | let callbackId = null;
174 |
175 | // If the named event object already exists in the dictionary...
176 | if (typeof _mappings[eventName] !== 'undefined') {
177 | // Attempt to get the callback ID from the callback itself.
178 | if (typeof fn[_CALLBACK_NAMESPACE] === 'undefined') {
179 | fn[_CALLBACK_NAMESPACE] = {
180 | totalEvents: 0
181 | };
182 | }
183 |
184 | // Add a new callback object to the existing event object.
185 | if (typeof fn[_CALLBACK_NAMESPACE][eventName] === 'undefined') {
186 | callbackId = _mappings[eventName].totalCallbacks;
187 |
188 | _mappings[eventName].totalCallbacks++;
189 |
190 | _mappings[eventName].callbacks[callbackId] = {
191 | cb : fn,
192 | once : typeof once === 'boolean' ? once : false
193 | };
194 |
195 | // On the callback, create a reference to the event mapping.
196 | fn[_CALLBACK_NAMESPACE][eventName] = callbackId;
197 | fn[_CALLBACK_NAMESPACE].totalEvents++;
198 | }
199 |
200 | if (typeof once === 'boolean') {
201 | // Get the callback ID from the value of the existing event name key.
202 | callbackId = fn[_CALLBACK_NAMESPACE][eventName];
203 |
204 | // The function already exists, so update it's 'once' value.
205 | _mappings[eventName].callbacks[callbackId].once = once;
206 | }
207 | }
208 | else {
209 | // Create a new event object in the dictionary with the specified name and callback.
210 | _mappings[eventName] = {
211 | callbacks : {}
212 | };
213 |
214 | callbackId = 0;
215 |
216 | _mappings[eventName].callbacks[callbackId] = {cb : fn, once : !!once};
217 | _mappings[eventName].totalCallbacks = 1;
218 |
219 | // On the callback, create a reference to the event mapping.
220 | if (typeof fn[_CALLBACK_NAMESPACE] === 'undefined') {
221 | fn[_CALLBACK_NAMESPACE] = {};
222 | fn[_CALLBACK_NAMESPACE].totalEvents = 1;
223 | }
224 | else {
225 | fn[_CALLBACK_NAMESPACE].totalEvents++;
226 | }
227 |
228 | fn[_CALLBACK_NAMESPACE][eventName] = callbackId;
229 | }
230 | };
231 |
232 | _self.once = function (eventName, fn) {
233 | if (arguments.length !== 2) {
234 | throw new ParamCountError('once', 'Expected 2 parameters', arguments.length);
235 | }
236 | else if (typeof eventName !== 'string') {
237 | throw new ParamTypeError('once', 'event name', eventName, 'string');
238 | }
239 | else if (eventName.length === 0) {
240 | throw new EventNameLengthError('once');
241 | }
242 | else if (_strictMode && typeof _self.events[eventName] === 'undefined') {
243 | throw new UndeclaredEventError('once', eventName);
244 | }
245 |
246 | if (typeof fn !== 'function') {
247 | throw new ParamTypeError('once', 'callback', fn, 'function');
248 | }
249 |
250 | _self.on(eventName, fn, true);
251 | };
252 |
253 | _self.off = function (eventName, fn) {
254 | if (arguments.length === 0) {
255 | // delete all references to Bullet that exist on mapped callbacks.
256 | _deleteAllCallbackReferences();
257 |
258 | // Remove all mappings from the dictionary.
259 | _mappings = {};
260 |
261 | return;
262 | }
263 | else if (typeof eventName !== 'string') {
264 | throw new ParamTypeError('off', 'event name', eventName, 'string');
265 | }
266 | else if (eventName.length === 0) {
267 | throw new EventNameLengthError('off');
268 | }
269 | else if (_strictMode && typeof _self.events[eventName] === 'undefined') {
270 | throw new UndeclaredEventError('off', eventName);
271 | }
272 |
273 | if (typeof _mappings[eventName] === 'undefined') {
274 | // There is no mapping to remove, so return silently.
275 | return;
276 | }
277 |
278 | // Remove just the function, if passed as a parameter and in the dictionary.
279 | if (typeof fn === 'function') {
280 | // if (typeof fn[_CALLBACK_NAMESPACE] === 'undefined' || typeof fn[_CALLBACK_NAMESPACE][eventName] === 'undefined') {
281 | // // TODO: Throw error here if in strict mode.
282 | // }
283 |
284 | // Retrieve a reference to the stored event from the callback.
285 | const id = fn[_CALLBACK_NAMESPACE][eventName];
286 | const fnToRemove = _mappings[eventName].callbacks[id];
287 |
288 | if (typeof fnToRemove !== 'undefined') {
289 | // delete the callback object from the dictionary.
290 | delete _mappings[eventName].callbacks[id];
291 |
292 | // delete the event reference on the callback function.
293 | delete fn[_CALLBACK_NAMESPACE][eventName];
294 |
295 | _mappings[eventName].totalCallbacks--;
296 | fn[_CALLBACK_NAMESPACE].totalEvents--;
297 |
298 | if (_mappings[eventName].totalCallbacks === 0) {
299 | // There are no more functions in the dictionary that are
300 | // registered to this event, so delete the named event object.
301 | delete _mappings[eventName];
302 | }
303 |
304 | if (fn[_CALLBACK_NAMESPACE].totalEvents === 0) {
305 | // There are no more events registered on this callback,
306 | // so delete the Bullet namespace.
307 | delete fn[_CALLBACK_NAMESPACE];
308 | }
309 | }
310 | }
311 | else if (typeof fn !== 'undefined') {
312 | throw new ParamTypeError('off', 'callback', fn, 'function');
313 | }
314 | else {
315 | // No callback was passed to the 'off' method...
316 |
317 | // For each callback in _mappings[eventName], delete the reference to
318 | // the specified event name on the callback itself.
319 | _deleteAllCallbackReferencesForEvent(eventName);
320 |
321 | // Delete all functions in the dictionary that are registered to this
322 | // event by deleting the named event object.
323 | delete _mappings[eventName];
324 | }
325 | };
326 |
327 | // Replace a single mapped callback for the specified event name with a new callback.
328 | _self.replaceCallback = function (eventName, oldFn, newFn, once) {
329 | if (typeof eventName !== 'string') {
330 | throw new ParamTypeError('replaceCallback', 'event name', eventName, 'string');
331 | }
332 | else if (eventName.length === 0) {
333 | throw new EventNameLengthError('replaceCallback');
334 | }
335 | else if (typeof _mappings[eventName] === 'undefined') {
336 | throw new UnmappedEventError('replaceCallback', eventName);
337 | }
338 | else if (_strictMode && typeof _self.events[eventName] === 'undefined') {
339 | throw new UndeclaredEventError('replaceCallback', eventName);
340 | }
341 |
342 | if (typeof oldFn !== 'function') {
343 | throw new ParamTypeError('replaceCallback', 'callback', oldFn, 'function');
344 | }
345 |
346 | if (typeof newFn !== 'function') {
347 | throw new ParamTypeError('replaceCallback', 'callback', newFn, 'function');
348 | }
349 |
350 | if (typeof once !== 'undefined' && typeof once !== 'boolean') {
351 | throw new ParamTypeError('replaceCallback', 'once', once, 'boolean');
352 | }
353 |
354 | _self.off(eventName, oldFn);
355 | _self.on(eventName, newFn, once);
356 | };
357 |
358 | // Replace all of the specified event name’s mapped callbacks with the specified callback.
359 | _self.replaceAllCallbacks = function (eventName, newFn, once) {
360 | if (typeof eventName !== 'string') {
361 | throw new ParamTypeError('replace', 'event name', eventName, 'string');
362 | }
363 | else if (eventName.length === 0) {
364 | throw new EventNameLengthError('replace');
365 | }
366 | else if (typeof _mappings[eventName] === 'undefined') {
367 | throw new UnmappedEventError('replace', eventName);
368 | }
369 | else if (_strictMode && typeof _self.events[eventName] === 'undefined') {
370 | throw new UndeclaredEventError('replace', eventName);
371 | }
372 |
373 | if (typeof newFn !== 'function') {
374 | throw new ParamTypeError('replace', 'callback', newFn, 'function');
375 | }
376 |
377 | if (typeof once !== 'undefined' && typeof once !== 'boolean') {
378 | throw new ParamTypeError('replace', 'once', once, 'boolean');
379 | }
380 |
381 | _self.off(eventName);
382 | _self.on(eventName, newFn, once);
383 | };
384 |
385 | _self.trigger = function (eventName, data) {
386 | if (typeof eventName !== 'string') {
387 | throw new ParamTypeError('trigger', 'event name', eventName, 'string');
388 | }
389 | else if (eventName.length === 0) {
390 | throw new EventNameLengthError('trigger');
391 | }
392 | else if (_strictMode && typeof _self.events[eventName] === 'undefined') {
393 | throw new UndeclaredEventError('trigger', eventName);
394 | }
395 |
396 | if (typeof _mappings[eventName] === 'undefined') {
397 | if (_strictMode) throw new UnmappedEventError('trigger', eventName);
398 |
399 | // Return silently if not in strict mode.
400 | return;
401 | }
402 |
403 | // Check whether or not this is a browser environment.
404 | if (_triggerAsync && typeof window !== 'undefined') {
405 | window.setTimeout(function () {
406 | _runCallback(eventName, data);
407 | }, 0);
408 | }
409 | else {
410 | _runCallback(eventName, data);
411 | }
412 | };
413 |
414 | _self.addEventName = function (eventName) {
415 | if (typeof eventName !== 'string') {
416 | throw new ParamTypeError('addEventName', 'event name', eventName, 'string');
417 | }
418 | else if (eventName.length === 0) {
419 | throw new EventNameLengthError('addEventName');
420 | }
421 |
422 | _self.events[eventName] = eventName;
423 | };
424 |
425 | _self.addMultipleEventNames = function (eventNames) {
426 | if (!(eventNames instanceof Array)) {
427 | throw new ParamTypeError('addMultipleEventNames', 'event names', eventNames, 'array');
428 | }
429 | else if (eventNames.length === 0) {
430 | throw new EventNamesArrayLengthError('addMultipleEventNames');
431 | }
432 |
433 | let i = 0;
434 | const length = eventNames.length;
435 |
436 | for (i; i < length; i++) {
437 | const currentEventName = eventNames[i];
438 |
439 | _self.addEventName(currentEventName);
440 | }
441 | };
442 |
443 | _self.removeEventName = function (eventName) {
444 | if (typeof eventName !== 'string') {
445 | throw new ParamTypeError('removeEventName', 'event name', eventName, 'string');
446 | }
447 | else if (eventName.length === 0) {
448 | throw new EventNameLengthError('removeEventName');
449 | }
450 |
451 | if (_self.events[eventName]) delete _self.events[eventName];
452 | };
453 |
454 | _self.getStrictMode = function () {
455 | // Return a boolean that doesn't directly point to the internal '_strictMode' property.
456 | return _strictMode === true;
457 | };
458 |
459 | _self.setStrictMode = function (useStrictMode) {
460 | if (typeof useStrictMode !== 'boolean') {
461 | throw new ParamTypeError('setStrictMode', 'strict mode', useStrictMode, 'boolean');
462 | }
463 |
464 | _strictMode = useStrictMode;
465 | };
466 |
467 | _self.getTriggerAsync = function () {
468 | // Return a boolean that doesn't directly point to the internal '_triggerAsync' property.
469 | return _triggerAsync === true;
470 | };
471 |
472 | _self.setTriggerAsync = function (useAsync) {
473 | if (typeof useAsync !== 'boolean') {
474 | throw new ParamTypeError('setTriggerAsync', 'trigger async', useAsync, 'boolean');
475 | }
476 |
477 | _triggerAsync = useAsync;
478 | };
479 |
480 | // TODO : Create a 'replaceAllEventNames' method with an array of strings passed as a param.
481 | // - include type checks for string while looping over the array.
482 |
483 | // TODO : Create a 'removeAllEventNames' method. No params necessary.
484 | // – Internally this could simply call 'replaceAllEventNames' and pass an empty array as a param.
485 |
486 | // TODO : Create an 'onAny' method with an array of strings passed as the first param and a single callback as the second.
487 | // - include type checks for string while looping over the array.
488 |
489 | // TODO : Create an 'onMultiple' method with an array of flat objects passed as a param.
490 | // - example of required param structure:
491 | // [{eventName: 'someEvent', callback: someCallback, once: false}, {eventName: 'anotherEvent', callback: anotherCallback, once: true}]
492 | }
493 |
494 |
495 | // ------------------------------------------------------------------------------------------
496 | // -- Module definition
497 | // ------------------------------------------------------------------------------------------
498 | // Check for AMD/Module support, otherwise define Bullet as a global variable.
499 |
500 | if (typeof define !== 'undefined' && define.amd) {
501 | // AMD. Register as an anonymous module.
502 | define(function () {
503 | return new Bullet();
504 | });
505 | }
506 | else if (typeof module !== 'undefined' && module.exports) {
507 | module.exports = new Bullet();
508 | }
509 | else {
510 | window.Bullet = new Bullet();
511 | }
512 |
513 | })();
514 |
--------------------------------------------------------------------------------
/test/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../.jshintrc",
3 |
4 | "browser" : false,
5 | "mocha" : true,
6 |
7 | // Custom Globals
8 | "globals" : {
9 |
10 | "expect" : true,
11 | "chai" : true,
12 | "sinon" : true,
13 | "BULLET_NAMESPACE" : true
14 | }
15 | }
--------------------------------------------------------------------------------
/test/_root-suite.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | global.chai = require('chai');
4 | global.sinon = require('sinon');
5 | global.expect = chai.expect;
6 |
7 | global.BULLET_NAMESPACE = '__bullet_pubsub__';
8 |
9 | var sinonChai = require('sinon-chai');
10 | chai.use(sinonChai);
11 |
12 | var bulletSingleton = require('../src/bullet');
13 | var BulletClass = bulletSingleton.constructor;
14 |
15 |
16 | beforeEach(function () {
17 |
18 | this.bullet = new BulletClass();
19 | this.testEventName = 'hello there';
20 | this.testCallback = function testCallback () {};
21 | });
--------------------------------------------------------------------------------
/test/spec/existence-checks/custom-errors.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Custom Error Existence', function () {
4 |
5 | it('should have custom error type "ParamCountError"', function () {
6 | expect(this.bullet._errors.ParamCountError).to.be.a('function');
7 | });
8 |
9 | it('should have custom error type "ParamTypeError"', function () {
10 | expect(this.bullet._errors.ParamTypeError).to.be.a('function');
11 | });
12 |
13 | it('should have custom error type "EventNameLengthError"', function () {
14 | expect(this.bullet._errors.EventNameLengthError).to.be.a('function');
15 | });
16 |
17 | it('should have custom error type "EventNamesArrayLengthError"', function () {
18 | expect(this.bullet._errors.EventNamesArrayLengthError).to.be.a('function');
19 | });
20 |
21 | it('should have custom error type "UndeclaredEventError"', function () {
22 | expect(this.bullet._errors.UndeclaredEventError).to.be.a('function');
23 | });
24 |
25 | it('should have custom error type "UnmappedEventError"', function () {
26 | expect(this.bullet._errors.UnmappedEventError).to.be.a('function');
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/test/spec/existence-checks/methods.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Method Existence', function () {
4 |
5 | it('should have a private method named "_getMappings"', function () {
6 | expect(this.bullet._getMappings).to.be.a('function');
7 | });
8 |
9 | it('should have a public method named "on"', function () {
10 | expect(this.bullet.on).to.be.a('function');
11 | });
12 |
13 | it('should have a public method named "off"', function () {
14 | expect(this.bullet.off).to.be.a('function');
15 | });
16 |
17 | it('should have a public method named "replaceCallback"', function () {
18 | expect(this.bullet.replaceCallback).to.be.a('function');
19 | });
20 |
21 | it('should have a public method named "replaceAllCallbacks"', function () {
22 | expect(this.bullet.replaceAllCallbacks).to.be.a('function');
23 | });
24 |
25 | it('should have a public method named "trigger"', function () {
26 | expect(this.bullet.trigger).to.be.a('function');
27 | });
28 |
29 | it('should have a public method named "once"', function () {
30 | expect(this.bullet.once).to.be.a('function');
31 | });
32 |
33 | it('should have a public method named "addEventName"', function () {
34 | expect(this.bullet.addEventName).to.be.a('function');
35 | });
36 |
37 | it('should have a public method named "addMultipleEventNames"', function () {
38 | expect(this.bullet.addMultipleEventNames).to.be.a('function');
39 | });
40 |
41 | it('should have a public method named "removeEventName"', function () {
42 | expect(this.bullet.removeEventName).to.be.a('function');
43 | });
44 |
45 | it('should have a public method named "getStrictMode"', function () {
46 | expect(this.bullet.getStrictMode).to.be.a('function');
47 | });
48 |
49 | it('should have a public method named "setStrictMode"', function () {
50 | expect(this.bullet.setStrictMode).to.be.a('function');
51 | });
52 |
53 | it('should have a public method named "getTriggerAsync"', function () {
54 | expect(this.bullet.getTriggerAsync).to.be.a('function');
55 | });
56 |
57 | it('should have a public method named "setTriggerAsync"', function () {
58 | expect(this.bullet.setTriggerAsync).to.be.a('function');
59 | });
60 | });
61 |
--------------------------------------------------------------------------------
/test/spec/existence-checks/properties.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Property Existence', function () {
4 |
5 | it('should have a public property named "events"', function () {
6 | expect(this.bullet.events).to.be.an('object');
7 | });
8 | });
9 |
--------------------------------------------------------------------------------
/test/spec/methods/_getMappings.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('_getMappings()', function () {
4 |
5 | it('should return the public state of the internal "_mappings" object', function () {
6 |
7 | var mappings = this.bullet._getMappings();
8 | expect(mappings).to.deep.equal({});
9 |
10 | // Add a property to the returned mappings object.
11 | mappings.foo = 'bar';
12 |
13 | // Get the updated events map.
14 | mappings = this.bullet._getMappings();
15 |
16 | // Bullet's internal mappings should not have been modified.
17 | expect(mappings).to.deep.equal({});
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/test/spec/methods/addEventName.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('addEventName()', function () {
4 |
5 | it('should add an event to the public "events" object', function () {
6 |
7 | var events = this.bullet.events;
8 |
9 | // The map should start empty
10 | expect(events).to.deep.equal({});
11 |
12 | // Add an event.
13 | this.bullet.addEventName('foo');
14 |
15 | // Get the updated events map.
16 | events = this.bullet.events;
17 |
18 | // Bullet's internal _events should not have been modified.
19 | expect(events).to.deep.equal({foo : 'foo'});
20 |
21 | // Add another event.
22 | this.bullet.addEventName('bar');
23 |
24 | // Get the updated events map.
25 | events = this.bullet.events;
26 |
27 | // Bullet's internal _events should not have been modified.
28 | expect(events).to.deep.equal({foo : 'foo', bar : 'bar'});
29 | });
30 |
31 | it('should throw an ParamTypeError if the passed parameter is not a string', function () {
32 |
33 | var self = this;
34 |
35 | function callAddEventName () {
36 | self.bullet.addEventName({hello : 'hi'});
37 | }
38 |
39 | expect(callAddEventName).to.throw(this.bullet._errors.ParamTypeError);
40 | });
41 |
42 | it('should throw an EventNameLengthError if the passed string parameter length is 0', function () {
43 |
44 | var self = this;
45 |
46 | function callAddEventName () {
47 | self.bullet.addEventName('');
48 | }
49 |
50 | expect(callAddEventName).to.throw(this.bullet._errors.EventNameLengthError);
51 | });
52 | });
53 |
--------------------------------------------------------------------------------
/test/spec/methods/addMultipleEventNames.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('addMultipleEventNames()', function () {
4 |
5 | it('should add multiple events to the public "events" object', function () {
6 |
7 | var events = this.bullet.events;
8 |
9 | // The map should start empty
10 | expect(events).to.deep.equal({});
11 |
12 | // Add multiple events.
13 | this.bullet.addMultipleEventNames(['foo', 'bar', 'baz']);
14 |
15 | // Get the updated events map.
16 | events = this.bullet.events;
17 |
18 | // Bullet's public "events" object should now include the new events.
19 | expect(events).to.deep.equal({foo : 'foo', bar : 'bar', baz : 'baz'});
20 | });
21 |
22 | it('should throw an ParamTypeError if the passed parameter is not an array', function () {
23 |
24 | var self = this;
25 |
26 | function callAddMultipleEventNames () {
27 | self.bullet.addMultipleEventNames('hi');
28 | }
29 |
30 | expect(callAddMultipleEventNames).to.throw(this.bullet._errors.ParamTypeError);
31 | });
32 |
33 | it('should throw an EventNamesArrayLengthError if the passed array parameter length is 0', function () {
34 |
35 | var self = this;
36 |
37 | function callAddMultipleEventNames () {
38 | self.bullet.addMultipleEventNames([]);
39 | }
40 |
41 | expect(callAddMultipleEventNames).to.throw(this.bullet._errors.EventNamesArrayLengthError);
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/test/spec/methods/getTriggerAsync.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('getTriggerAsync()', function () {
4 |
5 | it('should return the boolean value of the private "_triggerAsync" property', function () {
6 |
7 | expect(this.bullet.getTriggerAsync()).to.be.a('boolean');
8 | });
9 |
10 | it('should be true by default', function () {
11 |
12 | expect(this.bullet.getTriggerAsync()).to.equal(true);
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/test/spec/methods/off.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('off()', function () {
4 |
5 | before(function () {
6 |
7 | this.someOtherEventName = 'toto';
8 | this.someOtherCallback = function someOtherCallback () {};
9 | this.testCallbackId = 0;
10 | this.someOtherCallbackId = 1;
11 | });
12 |
13 | it('should remove a mapping only for the specified event name and function', function () {
14 |
15 | // Create multiple event mappings so that we can test the removal of a single mapping.
16 | this.bullet.on(this.testEventName, this.testCallback);
17 | this.bullet.on(this.testEventName, this.someOtherCallback);
18 |
19 | // Get the events map.
20 | var mappings = this.bullet._getMappings();
21 |
22 | expect(mappings[this.testEventName].callbacks[this.testCallbackId]).to.be.an('object');
23 | expect(mappings[this.testEventName].callbacks[this.someOtherCallbackId]).to.be.an('object');
24 |
25 | expect(this.testCallback[BULLET_NAMESPACE]).to.be.an('object');
26 | expect(this.someOtherCallback[BULLET_NAMESPACE]).to.be.an('object');
27 |
28 |
29 | // Remove the mapping to this.testCallback
30 | this.bullet.off(this.testEventName, this.testCallback);
31 |
32 | // Get the updated events map.
33 | mappings = this.bullet._getMappings();
34 |
35 | expect(mappings[this.testEventName].callbacks[this.testCallbackId]).to.be.an('undefined');
36 | expect(mappings[this.testEventName].callbacks[this.someOtherCallbackId]).to.be.an('object');
37 |
38 | expect(this.testCallback[BULLET_NAMESPACE]).to.be.an('undefined');
39 | expect(this.someOtherCallback[BULLET_NAMESPACE]).to.be.an('object');
40 |
41 | // Remove the mapping to this.someOtherCallback
42 | this.bullet.off(this.testEventName, this.someOtherCallback);
43 |
44 | // Get the updated events map.
45 | mappings = this.bullet._getMappings();
46 |
47 | // The map should be empty, now that all event mappings have been removed.
48 | expect(mappings).to.deep.equal({});
49 |
50 | expect(this.someOtherCallback[BULLET_NAMESPACE]).to.be.an('undefined');
51 | });
52 |
53 | it('should remove all mappings for the specified event name', function () {
54 |
55 | // Create multiple event mappings so that we can test the removal of all mappings
56 | // for a specific event name.
57 | this.bullet.on(this.testEventName, this.testCallback);
58 | this.bullet.on(this.testEventName, this.someOtherCallback);
59 | this.bullet.on(this.someOtherEventName, this.testCallback);
60 | this.bullet.on(this.someOtherEventName, this.someOtherCallback);
61 |
62 | // Get the events map.
63 | var mappings = this.bullet._getMappings();
64 |
65 | expect(mappings[this.testEventName].callbacks[this.testCallbackId]).to.be.an('object');
66 | expect(mappings[this.testEventName].callbacks[this.someOtherCallbackId]).to.be.an('object');
67 | expect(mappings[this.someOtherEventName].callbacks[this.testCallbackId]).to.be.an('object');
68 | expect(mappings[this.someOtherEventName].callbacks[this.someOtherCallbackId]).to.be.an('object');
69 |
70 | expect(this.testCallback[BULLET_NAMESPACE][this.testEventName]).to.equal(this.testCallbackId);
71 | expect(this.testCallback[BULLET_NAMESPACE][this.someOtherEventName]).to.equal(this.testCallbackId);
72 |
73 | expect(this.someOtherCallback[BULLET_NAMESPACE][this.testEventName]).to.equal(this.someOtherCallbackId);
74 | expect(this.someOtherCallback[BULLET_NAMESPACE][this.someOtherEventName]).to.equal(this.someOtherCallbackId);
75 |
76 | // Remove all mappings for this.testEventName
77 | this.bullet.off(this.testEventName);
78 |
79 | // Get the updated events map.
80 | mappings = this.bullet._getMappings();
81 |
82 | expect(mappings[this.testEventName]).to.be.an('undefined');
83 | expect(mappings[this.someOtherEventName].callbacks[this.testCallbackId]).to.be.an('object');
84 | expect(mappings[this.someOtherEventName].callbacks[this.someOtherCallbackId]).to.be.an('object');
85 |
86 | // All references to the specified event name should have been removed from testCallback and someOtherCallback.
87 | expect(this.testCallback[BULLET_NAMESPACE][this.testEventName]).to.be.an('undefined');
88 | expect(this.someOtherCallback[BULLET_NAMESPACE][this.testEventName]).to.be.an('undefined');
89 |
90 | // The other event – someOtherEventName – on these callbacks shouldn't be affected.
91 | expect(this.testCallback[BULLET_NAMESPACE][this.someOtherEventName]).to.equal(this.testCallbackId);
92 | expect(this.someOtherCallback[BULLET_NAMESPACE][this.someOtherEventName]).to.equal(this.someOtherCallbackId);
93 |
94 | // Remove all mappings for this.someOtherEventName
95 | this.bullet.off(this.someOtherEventName);
96 |
97 | // Get the updated events map.
98 | mappings = this.bullet._getMappings();
99 |
100 | expect(mappings[this.someOtherEventName]).to.be.an('undefined');
101 |
102 | // All references to bullet should have been removed from someOtherCallback.
103 | expect(this.someOtherCallback[BULLET_NAMESPACE]).to.be.an('undefined');
104 | });
105 |
106 | it('should remove all mappings when no params are passed', function () {
107 |
108 | // Create multiple event mappings so that we can test the removal of all mappings
109 | this.bullet.on(this.testEventName, this.testCallback);
110 | this.bullet.on(this.testEventName, this.someOtherCallback);
111 | this.bullet.on(this.someOtherEventName, this.testCallback);
112 | this.bullet.on(this.someOtherEventName, this.someOtherCallback);
113 |
114 | // Get the events map.
115 | var mappings = this.bullet._getMappings();
116 |
117 | expect(mappings[this.testEventName].callbacks[this.testCallbackId]).to.be.an('object');
118 | expect(mappings[this.testEventName].callbacks[this.someOtherCallbackId]).to.be.an('object');
119 | expect(mappings[this.someOtherEventName].callbacks[this.testCallbackId]).to.be.an('object');
120 | expect(mappings[this.someOtherEventName].callbacks[this.someOtherCallbackId]).to.be.an('object');
121 |
122 | expect(this.testCallback[BULLET_NAMESPACE][this.testEventName]).to.equal(this.testCallbackId);
123 | expect(this.testCallback[BULLET_NAMESPACE][this.someOtherEventName]).to.equal(this.testCallbackId);
124 |
125 | expect(this.someOtherCallback[BULLET_NAMESPACE][this.testEventName]).to.equal(this.someOtherCallbackId);
126 | expect(this.someOtherCallback[BULLET_NAMESPACE][this.someOtherEventName]).to.equal(this.someOtherCallbackId);
127 |
128 | // Remove all mappings.
129 | this.bullet.off();
130 |
131 | // Get the updated events map.
132 | mappings = this.bullet._getMappings();
133 |
134 | expect(mappings).to.deep.equal({});
135 |
136 | // All references should have been removed from testCallback and someOtherCallback.
137 | expect(this.testCallback[BULLET_NAMESPACE]).to.be.an('undefined');
138 | expect(this.someOtherCallback[BULLET_NAMESPACE]).to.be.an('undefined');
139 | });
140 |
141 | it('should throw an ParamTypeError if the event name param is not a string', function () {
142 |
143 | var self = this;
144 |
145 | function callOff () {
146 |
147 | // Attempt to unmap an event with a non-string event name parameter.
148 | self.bullet.off({}, self.testCallback);
149 | }
150 |
151 | expect(callOff).to.throw(this.bullet._errors.ParamTypeError);
152 | });
153 |
154 | it('should throw an EventNameLengthError if the event name param is an empty string', function () {
155 |
156 | var self = this;
157 |
158 | function callOff () {
159 |
160 | // Attempt to unmap an event with an empty string as the event name parameter.
161 | self.bullet.off('', self.testCallback);
162 | }
163 |
164 | expect(callOff).to.throw(this.bullet._errors.EventNameLengthError);
165 | });
166 |
167 | it('should throw a ParamTypeError if the callback parameter is not a function', function () {
168 |
169 | var self = this;
170 |
171 | // Map an event via the 'on' method so that we can attempt to remove it below.
172 | this.bullet.on(this.testEventName, this.testCallback);
173 |
174 | function callOff () {
175 |
176 | // Attempt to unmap an event with a non-function as the callback parameter.
177 | self.bullet.off(self.testEventName, {});
178 | }
179 |
180 | expect(callOff).to.throw(this.bullet._errors.ParamTypeError);
181 | });
182 | });
183 |
--------------------------------------------------------------------------------
/test/spec/methods/on.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('on()', function () {
4 |
5 | it('should create a mapping for the specified event name', function () {
6 |
7 | var mappings = this.bullet._getMappings();
8 | var testCallbackId = 0;
9 |
10 | expect(mappings[this.testEventName]).to.be.an('undefined');
11 | expect(this.testCallback[BULLET_NAMESPACE]).to.be.an('undefined');
12 |
13 | // Add an event.
14 | this.bullet.on(this.testEventName, this.testCallback);
15 |
16 | // Get the updated events map.
17 | mappings = this.bullet._getMappings();
18 |
19 | // check that Bullet's internal map has a reference to the callback.
20 | expect(mappings[this.testEventName]).to.be.an('object');
21 | expect(mappings[this.testEventName].callbacks).to.be.an('object');
22 | expect(mappings[this.testEventName].callbacks[testCallbackId]).to.be.an('object');
23 |
24 | // check that a reference to the mapped event has been added to the callback.
25 | expect(this.testCallback[BULLET_NAMESPACE]).to.be.an('object');
26 | expect(this.testCallback[BULLET_NAMESPACE].totalEvents).to.equal(1);
27 | expect(this.testCallback[BULLET_NAMESPACE][this.testEventName]).to.be.a('number');
28 | });
29 |
30 | it('should create mappings for different event names using the same callback', function () {
31 |
32 | // TODO : define 'someOtherEventName' and 'someOtherCallback' in a 'before' block.
33 | // (also update the others functions within the 'on()' block to use the 'before' version, i.e. this.someOtherEventName instead of just someOtherEventName)
34 | var someOtherEventName = 'hey';
35 | var someOtherCallback = function someOtherCallback () {};
36 | var mappings = this.bullet._getMappings();
37 |
38 | expect(mappings[this.testEventName]).to.be.an('undefined');
39 | expect(mappings[someOtherEventName]).to.be.an('undefined');
40 | expect(this.testCallback[BULLET_NAMESPACE]).to.be.an('undefined');
41 | expect(someOtherCallback[BULLET_NAMESPACE]).to.be.an('undefined');
42 |
43 | // Add some events.
44 | this.bullet.on(this.testEventName, this.testCallback);
45 | this.bullet.on(someOtherEventName, this.testCallback);
46 |
47 | this.bullet.on(this.testEventName, someOtherCallback);
48 | this.bullet.on(someOtherEventName, someOtherCallback);
49 |
50 | // Get the updated events map.
51 | mappings = this.bullet._getMappings();
52 |
53 | expect(mappings[this.testEventName]).to.be.an('object');
54 | expect(mappings[this.testEventName].callbacks).to.be.an('object');
55 |
56 | expect(mappings[someOtherEventName]).to.be.an('object');
57 | expect(mappings[someOtherEventName].callbacks).to.be.an('object');
58 |
59 | var testCallbackId = 0;
60 | var someOtherCallbackId = 1;
61 |
62 | expect(mappings[this.testEventName].callbacks[testCallbackId]).to.be.an('object');
63 | expect(mappings[this.testEventName].callbacks[someOtherCallbackId]).to.be.an('object');
64 |
65 | expect(mappings[someOtherEventName].callbacks[testCallbackId]).to.be.an('object');
66 | expect(mappings[someOtherEventName].callbacks[someOtherCallbackId]).to.be.an('object');
67 |
68 | // check that references to the mapped events have been added to the callback.
69 | expect(this.testCallback[BULLET_NAMESPACE]).to.be.an('object');
70 | expect(this.testCallback[BULLET_NAMESPACE].totalEvents).to.equal(2);
71 | expect(this.testCallback[BULLET_NAMESPACE][this.testEventName]).to.equal(testCallbackId);
72 | expect(this.testCallback[BULLET_NAMESPACE][someOtherEventName]).to.equal(testCallbackId);
73 |
74 | expect(someOtherCallback[BULLET_NAMESPACE]).to.be.an('object');
75 | expect(someOtherCallback[BULLET_NAMESPACE].totalEvents).to.equal(2);
76 | expect(someOtherCallback[BULLET_NAMESPACE][this.testEventName]).to.equal(someOtherCallbackId);
77 | expect(someOtherCallback[BULLET_NAMESPACE][someOtherEventName]).to.equal(someOtherCallbackId);
78 | });
79 |
80 | it('should map multiple callbacks to a single event name', function () {
81 |
82 | var someOtherCallback = function someOtherCallback () {};
83 | var mappings = this.bullet._getMappings();
84 |
85 | expect(mappings[this.testEventName]).to.be.an('undefined');
86 | expect(this.testCallback[BULLET_NAMESPACE]).to.be.an('undefined');
87 |
88 | // Add an event.
89 | this.bullet.on(this.testEventName, this.testCallback);
90 | this.bullet.on(this.testEventName, someOtherCallback);
91 |
92 | // Get the updated events map.
93 | mappings = this.bullet._getMappings();
94 |
95 | expect(mappings[this.testEventName]).to.be.an('object');
96 | expect(mappings[this.testEventName].callbacks).to.be.an('object');
97 | expect(mappings[this.testEventName].callbacks[0]).to.be.an('object');
98 | expect(mappings[this.testEventName].callbacks[1]).to.be.an('object');
99 |
100 | var testCallbackId = 0;
101 | var someOtherCallbackId = 1;
102 |
103 | expect(mappings[this.testEventName].callbacks[testCallbackId]).to.be.an('object');
104 |
105 |
106 | // check that a reference to the mapped event has been added to the first callback.
107 | expect(this.testCallback[BULLET_NAMESPACE]).to.be.an('object');
108 | expect(this.testCallback[BULLET_NAMESPACE].totalEvents).to.equal(1);
109 | expect(this.testCallback[BULLET_NAMESPACE][this.testEventName]).to.equal(testCallbackId);
110 |
111 | // check that a reference to the mapped event has been added to the second callback.
112 | expect(someOtherCallback[BULLET_NAMESPACE]).to.be.an('object');
113 | expect(someOtherCallback[BULLET_NAMESPACE].totalEvents).to.equal(1);
114 | expect(someOtherCallback[BULLET_NAMESPACE][this.testEventName]).to.equal(someOtherCallbackId);
115 | });
116 |
117 | it('should throw a ParamTypeError if the event name param is not a string', function () {
118 |
119 | var self = this;
120 |
121 | // The map should start empty
122 | expect(this.bullet._getMappings()).to.deep.equal({});
123 |
124 | function callOn () {
125 |
126 | // Attempt to map an event with a non-string event name parameter.
127 | self.bullet.on({}, self.testCallback);
128 | }
129 |
130 | expect(callOn).to.throw(this.bullet._errors.ParamTypeError);
131 |
132 | // The map should still be empty
133 | expect(this.bullet._getMappings()).to.deep.equal({});
134 | });
135 |
136 | it('should throw an EventNameLengthError if the event name param is an empty string', function () {
137 |
138 | var self = this;
139 |
140 | // The map should start empty
141 | expect(this.bullet._getMappings()).to.deep.equal({});
142 |
143 | function callOn () {
144 |
145 | // Attempt to map an event with an empty string as the event name parameter.
146 | self.bullet.on('', self.testCallback);
147 | }
148 |
149 | expect(callOn).to.throw(this.bullet._errors.EventNameLengthError);
150 |
151 | // The map should still be empty
152 | expect(this.bullet._getMappings()).to.deep.equal({});
153 | });
154 |
155 | it('should throw a ParamTypeError if the callback parameter is not a function', function () {
156 |
157 | var self = this;
158 |
159 | // The map should start empty
160 | expect(this.bullet._getMappings()).to.deep.equal({});
161 |
162 | function callOn () {
163 |
164 | // Attempt to map an event with a non-function as the callback parameter.
165 | self.bullet.on(self.testEventName, {});
166 | }
167 |
168 | expect(callOn).to.throw(this.bullet._errors.ParamTypeError);
169 |
170 | // The map should still be empty
171 | expect(this.bullet._getMappings()).to.deep.equal({});
172 | });
173 |
174 | it('should throw a ParamCountError if only one parameter is passed', function () {
175 |
176 | var self = this;
177 |
178 | // The map should start empty
179 | expect(this.bullet._getMappings()).to.deep.equal({});
180 |
181 | function callOn () {
182 |
183 | // Attempt to map an event with only one parameter.
184 | self.bullet.on(self.testEventName);
185 | }
186 |
187 | expect(callOn).to.throw(this.bullet._errors.ParamCountError);
188 |
189 | // The map should still be empty
190 | expect(this.bullet._getMappings()).to.deep.equal({});
191 | });
192 |
193 | it('should throw a ParamCountError if no parameters are passed', function () {
194 |
195 | var self = this;
196 |
197 | // The map should start empty
198 | expect(this.bullet._getMappings()).to.deep.equal({});
199 |
200 | function callOn () {
201 |
202 | // Attempt to map an event with no params.
203 | self.bullet.on();
204 | }
205 |
206 | expect(callOn).to.throw(this.bullet._errors.ParamCountError);
207 |
208 | // The map should still be empty
209 | expect(this.bullet._getMappings()).to.deep.equal({});
210 | });
211 |
212 | it('should throw a ParamCountError if more than three parameters are passed', function () {
213 |
214 | var self = this;
215 |
216 | // The map should start empty
217 | expect(this.bullet._getMappings()).to.deep.equal({});
218 |
219 | function callOn () {
220 |
221 | // Attempt to map an event with more than three params.
222 | self.bullet.on(self.testEventName, self.testCallback, true, 123);
223 | }
224 |
225 | expect(callOn).to.throw(this.bullet._errors.ParamCountError);
226 |
227 | // The map should still be empty
228 | expect(this.bullet._getMappings()).to.deep.equal({});
229 | });
230 | });
231 |
--------------------------------------------------------------------------------
/test/spec/methods/once.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('once()', function () {
4 |
5 | it('should create a one-time mapping for the specified event name', function () {
6 |
7 | var mappings = this.bullet._getMappings();
8 |
9 | expect(mappings[this.testEventName]).to.be.an('undefined');
10 |
11 | // Add a one-time event.
12 | this.bullet.once(this.testEventName, this.testCallback);
13 |
14 | // Get the updated events map.
15 | mappings = this.bullet._getMappings();
16 |
17 | expect(mappings[this.testEventName]).to.be.an('object');
18 | expect(mappings[this.testEventName].callbacks).to.be.an('object');
19 |
20 | // Trigger the event.
21 | this.bullet.trigger(this.testEventName);
22 |
23 | // Get the updated events map.
24 | mappings = this.bullet._getMappings();
25 |
26 | // The event mapping should have been deleted, as it was triggered once.
27 | expect(mappings[this.testEventName]).to.be.an('undefined');
28 | });
29 |
30 | it('should throw a ParamCountError if no parameters are passed', function () {
31 |
32 | var self = this;
33 |
34 | // The map should start empty
35 | expect(this.bullet._getMappings()).to.deep.equal({});
36 |
37 | function callOnce () {
38 |
39 | // Attempt to map an event with no params.
40 | self.bullet.once();
41 | }
42 |
43 | expect(callOnce).to.throw(this.bullet._errors.ParamCountError);
44 |
45 | // The map should still be empty
46 | expect(this.bullet._getMappings()).to.deep.equal({});
47 | });
48 |
49 | it('should throw an ParamCountError if only one parameter is passed', function () {
50 |
51 | var self = this;
52 |
53 | // The map should start empty
54 | expect(this.bullet._getMappings()).to.deep.equal({});
55 |
56 | function callOnce () {
57 |
58 | // Attempt to map an event with only one parameter.
59 | self.bullet.once(self.testEventName);
60 | }
61 |
62 | expect(callOnce).to.throw(this.bullet._errors.ParamCountError);
63 |
64 | // The map should still be empty
65 | expect(this.bullet._getMappings()).to.deep.equal({});
66 | });
67 |
68 | it('should throw an ParamCountError if more than three parameters are passed', function () {
69 |
70 | var self = this;
71 |
72 | // The map should start empty
73 | expect(this.bullet._getMappings()).to.deep.equal({});
74 |
75 | function callOnce () {
76 |
77 | // Attempt to map an event with more than three params.
78 | self.bullet.once(self.testEventName, self.testCallback, true, 123);
79 | }
80 |
81 | expect(callOnce).to.throw(this.bullet._errors.ParamCountError);
82 |
83 | // The map should still be empty
84 | expect(this.bullet._getMappings()).to.deep.equal({});
85 | });
86 |
87 | it('should throw an ParamTypeError if the event name param is not a string', function () {
88 |
89 | var self = this;
90 |
91 | // The map should start empty
92 | expect(this.bullet._getMappings()).to.deep.equal({});
93 |
94 | function callOnce () {
95 |
96 | // Attempt to map an event with a non-string event name parameter.
97 | self.bullet.once({}, self.testCallback);
98 | }
99 |
100 | expect(callOnce).to.throw(this.bullet._errors.ParamTypeError);
101 |
102 | // The map should still be empty
103 | expect(this.bullet._getMappings()).to.deep.equal({});
104 | });
105 |
106 | it('should throw an EventNameLengthError if the event name param is an empty string', function () {
107 |
108 | var self = this;
109 |
110 | // The map should start empty
111 | expect(this.bullet._getMappings()).to.deep.equal({});
112 |
113 | function callOnce () {
114 |
115 | // Attempt to map an event with an empty string as the event name parameter.
116 | self.bullet.once('', self.testCallback);
117 | }
118 |
119 | expect(callOnce).to.throw(this.bullet._errors.EventNameLengthError);
120 |
121 | // The map should still be empty
122 | expect(this.bullet._getMappings()).to.deep.equal({});
123 | });
124 |
125 | it('should throw a ParamTypeError if the callback parameter is not a function', function () {
126 |
127 | var self = this;
128 |
129 | // The map should start empty
130 | expect(this.bullet._getMappings()).to.deep.equal({});
131 |
132 | function callOnce () {
133 |
134 | // Attempt to map an event with a non-function as the callback parameter.
135 | self.bullet.once(self.testEventName, {});
136 | }
137 |
138 | expect(callOnce).to.throw(this.bullet._errors.ParamTypeError);
139 |
140 | // The map should still be empty
141 | expect(this.bullet._getMappings()).to.deep.equal({});
142 | });
143 | });
144 |
--------------------------------------------------------------------------------
/test/spec/methods/removeEventName.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('removeEventName()', function () {
4 |
5 | it('should remove an event from the internal "_strictEvents" object', function () {
6 |
7 | var events = this.bullet.events;
8 |
9 | // Add multiple events so that we can test the removal of a single event.
10 | this.bullet.addEventName('foo');
11 | this.bullet.addEventName('bar');
12 | this.bullet.addEventName('baz');
13 |
14 | // Get the updated events map.
15 | events = this.bullet.events;
16 |
17 | expect(events).to.deep.equal({foo : 'foo', bar : 'bar', baz : 'baz'});
18 |
19 | // Remove one of the events.
20 | this.bullet.removeEventName('bar');
21 |
22 | // Get the updated events map.
23 | events = this.bullet.events;
24 |
25 | expect(events).to.deep.equal({foo : 'foo', baz : 'baz'});
26 |
27 | // Remove the remaining events.
28 | this.bullet.removeEventName('foo');
29 | this.bullet.removeEventName('baz');
30 |
31 | // Get the updated events map.
32 | events = this.bullet.events;
33 |
34 | // The map should start empty
35 | expect(events).to.deep.equal({});
36 | });
37 |
38 | it('should throw an ParamTypeError if the passed parameter is not a string', function () {
39 |
40 | var self = this;
41 |
42 | function callremoveEventName () {
43 | self.bullet.removeEventName({hello : 'hi'});
44 | }
45 |
46 | expect(callremoveEventName).to.throw(this.bullet._errors.ParamTypeError);
47 | });
48 |
49 | it('should throw an EventNameLengthError if the passed string parameter length is 0', function () {
50 |
51 | var self = this;
52 |
53 | function callremoveEventName () {
54 | self.bullet.removeEventName('');
55 | }
56 |
57 | expect(callremoveEventName).to.throw(this.bullet._errors.EventNameLengthError);
58 | });
59 | });
60 |
--------------------------------------------------------------------------------
/test/spec/methods/replaceAllCallbacks.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('replaceAllCallbacks()', function () {
4 |
5 | before(function () {
6 |
7 | this.someOtherCallback = function someOtherCallback () {};
8 | });
9 |
10 | it('should throw a ParamTypeError if the event name param is not a string', function () {
11 |
12 | var self = this;
13 |
14 | // Create an event mapping.
15 | this.bullet.on(this.testEventName, this.testCallback);
16 |
17 | function callReplaceAllCallbacks () {
18 |
19 | // Attempt to replace all functions mapped to an event name by using a non-string event name parameter.
20 | self.bullet.replaceAllCallbacks({}, self.someOtherCallback);
21 | }
22 |
23 | expect(callReplaceAllCallbacks).to.throw(this.bullet._errors.ParamTypeError);
24 | });
25 |
26 | it('should throw an EventNameLengthError if the event name param is an empty string', function () {
27 |
28 | var self = this;
29 |
30 | // Create an event mapping.
31 | this.bullet.on(this.testEventName, this.testCallback);
32 |
33 | function callReplaceAllCallbacks () {
34 |
35 | // Attempt to replace all functions mapped to an event name by using an empty string for the event name parameter.
36 | self.bullet.replaceAllCallbacks('', self.someOtherCallback);
37 | }
38 |
39 | expect(callReplaceAllCallbacks).to.throw(this.bullet._errors.EventNameLengthError);
40 | });
41 |
42 | it('should throw a ParamTypeError if the new callback param is not a function', function () {
43 |
44 | var self = this;
45 |
46 | // Create an event mapping.
47 | this.bullet.on(this.testEventName, this.testCallback);
48 |
49 | function callReplaceAllCallbacks () {
50 |
51 | // Attempt to replace all functions mapped to an event name by using a non-function for the new callback parameter.
52 | self.bullet.replaceAllCallbacks(self.testEventName, {});
53 | }
54 |
55 | expect(callReplaceAllCallbacks).to.throw(this.bullet._errors.ParamTypeError);
56 | });
57 |
58 | it('should throw a ParamTypeError if the ‘once’ param is defined but not a boolean', function () {
59 |
60 | var self = this;
61 |
62 | // Create an event mapping.
63 | this.bullet.on(this.testEventName, this.testCallback);
64 |
65 | function callReplaceAllCallbacks () {
66 |
67 | // Attempt to replace all functions mapped to an event name by using a non-boolean for the 'once' parameter.
68 | self.bullet.replaceAllCallbacks(self.testEventName, self.someOtherCallback, {});
69 | }
70 |
71 | expect(callReplaceAllCallbacks).to.throw(this.bullet._errors.ParamTypeError);
72 | });
73 |
74 | it('should throw an UnmappedEventError if the specified event name does not exist within the mappings object', function () {
75 |
76 | var self = this;
77 |
78 | function callReplaceAllCallbacks () {
79 |
80 | // Attempt to replace a function that is not mapped to any event name.
81 | self.bullet.replaceAllCallbacks('someRandomEventName', self.someOtherCallback);
82 | }
83 |
84 | expect(callReplaceAllCallbacks).to.throw(this.bullet._errors.UnmappedEventError);
85 | });
86 |
87 | it('should replace all functions mapped to the specified event with a single function', function () {
88 |
89 | var thirdCallback = function thirdCallback () {};
90 | var newCallback = function newCallback () {};
91 |
92 | // Create multiple event mappings.
93 | this.bullet.on(this.testEventName, this.testCallback);
94 | this.bullet.on(this.testEventName, this.someOtherCallback);
95 | this.bullet.on(this.testEventName, thirdCallback);
96 |
97 | // Get the events map.
98 | var mappings = this.bullet._getMappings();
99 |
100 | expect(mappings[this.testEventName].totalCallbacks).to.equal(3);
101 | expect(mappings[this.testEventName].callbacks[0].cb).to.equal(this.testCallback);
102 | expect(mappings[this.testEventName].callbacks[1].cb).to.equal(this.someOtherCallback);
103 | expect(mappings[this.testEventName].callbacks[2].cb).to.equal(thirdCallback);
104 |
105 | // Replace all mapped callbacks with the 'newCallback'.
106 | this.bullet.replaceAllCallbacks(this.testEventName, newCallback);
107 |
108 | // Get the updated events map.
109 | mappings = this.bullet._getMappings();
110 |
111 | expect(mappings[this.testEventName].totalCallbacks).to.equal(1);
112 | expect(mappings[this.testEventName].callbacks[0].cb).to.equal(newCallback);
113 | expect(mappings[this.testEventName].callbacks[1]).to.be.an('undefined');
114 | expect(mappings[this.testEventName].callbacks[2]).to.be.an('undefined');
115 | });
116 |
117 | it('should respect the ‘once’ parameter when replacing all mapped functions', function () {
118 |
119 | // Create an event mapping.
120 | this.bullet.on(this.testEventName, this.testCallback);
121 |
122 | // Update the function mapped to the testEventName and set the 'once' param for the new function.
123 | this.bullet.replaceAllCallbacks(this.testEventName, this.someOtherCallback, true);
124 |
125 | // Get the updated events map.
126 | var mappings = this.bullet._getMappings();
127 |
128 | expect(mappings[this.testEventName].callbacks[0].cb).to.equal(this.someOtherCallback);
129 | expect(mappings[this.testEventName].callbacks[0].once).to.equal(true);
130 | });
131 | });
132 |
--------------------------------------------------------------------------------
/test/spec/methods/replaceCallback.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('replaceCallback()', function () {
4 |
5 | before(function () {
6 |
7 | this.someOtherCallback = function someOtherCallback () {};
8 | });
9 |
10 | it('should throw a ParamTypeError if the event name param is not a string', function () {
11 |
12 | var self = this;
13 |
14 | // Create an event mapping.
15 | this.bullet.on(this.testEventName, this.testCallback);
16 |
17 | function callReplaceCallback () {
18 |
19 | // Attempt to update a function mapped to an event name by using a non-string event name parameter.
20 | self.bullet.replaceCallback({}, self.testCallback, self.someOtherCallback);
21 | }
22 |
23 | expect(callReplaceCallback).to.throw(this.bullet._errors.ParamTypeError);
24 | });
25 |
26 | it('should throw an EventNameLengthError if the event name param is an empty string', function () {
27 |
28 | var self = this;
29 |
30 | // Create an event mapping.
31 | this.bullet.on(this.testEventName, this.testCallback);
32 |
33 | function callReplaceCallback () {
34 |
35 | // Attempt to update a function mapped to an event name by using an empty string for the event name parameter.
36 | self.bullet.replaceCallback('', self.testCallback, self.someOtherCallback);
37 | }
38 |
39 | expect(callReplaceCallback).to.throw(this.bullet._errors.EventNameLengthError);
40 | });
41 |
42 | it('should throw a ParamTypeError if the old callback param is not a function', function () {
43 |
44 | var self = this;
45 |
46 | // Create an event mapping.
47 | this.bullet.on(this.testEventName, this.testCallback);
48 |
49 | function callReplaceCallback () {
50 |
51 | // Attempt to update a function mapped to an event name by using a non-function for the old callback parameter.
52 | self.bullet.replaceCallback(self.testEventName, {}, self.someOtherCallback);
53 | }
54 |
55 | expect(callReplaceCallback).to.throw(this.bullet._errors.ParamTypeError);
56 | });
57 |
58 | it('should throw a ParamTypeError if the new callback param is not a function', function () {
59 |
60 | var self = this;
61 |
62 | // Create an event mapping.
63 | this.bullet.on(this.testEventName, this.testCallback);
64 |
65 | function callReplaceCallback () {
66 |
67 | // Attempt to update a function mapped to an event name by using a non-function for the new callback parameter.
68 | self.bullet.replaceCallback(self.testEventName, self.testCallback, {});
69 | }
70 |
71 | expect(callReplaceCallback).to.throw(this.bullet._errors.ParamTypeError);
72 | });
73 |
74 | it('should throw a ParamTypeError if the ‘once’ param is defined but not a boolean', function () {
75 |
76 | var self = this;
77 |
78 | // Create an event mapping.
79 | this.bullet.on(this.testEventName, this.testCallback);
80 |
81 | function callReplaceCallback () {
82 |
83 | // Attempt to update a function mapped to an event name by using a non-boolean for the 'once' parameter.
84 | self.bullet.replaceCallback(self.testEventName, self.testCallback, self.someOtherCallback, {});
85 | }
86 |
87 | expect(callReplaceCallback).to.throw(this.bullet._errors.ParamTypeError);
88 | });
89 |
90 | it('should throw an UnmappedEventError if the specified event name does not exist within the mappings object', function () {
91 |
92 | var self = this;
93 |
94 | function callReplaceCallback () {
95 |
96 | // Attempt to update a function that is not mapped to any event name.
97 | self.bullet.replaceCallback('someRandomEventName', self.testCallback, self.someOtherCallback);
98 | }
99 |
100 | expect(callReplaceCallback).to.throw(this.bullet._errors.UnmappedEventError);
101 | });
102 |
103 | it('should update a single mapped function for the specified event name', function () {
104 |
105 | // Create an event mapping.
106 | this.bullet.on(this.testEventName, this.testCallback);
107 |
108 | // Get the events map.
109 | var mappings = this.bullet._getMappings();
110 |
111 | expect(mappings[this.testEventName].totalCallbacks).to.equal(1);
112 | expect(mappings[this.testEventName].callbacks[0].cb).to.equal(this.testCallback);
113 |
114 | // Replace the 'this.testCallback' mapping with a mapping for 'this.someOtherCallback'
115 | this.bullet.replaceCallback(this.testEventName, this.testCallback, this.someOtherCallback);
116 |
117 | // Get the updated events map.
118 | mappings = this.bullet._getMappings();
119 |
120 | expect(mappings[this.testEventName].totalCallbacks).to.equal(1);
121 | expect(mappings[this.testEventName].callbacks[0].cb).to.equal(this.someOtherCallback);
122 | });
123 |
124 | it('should respect the ‘once’ parameter when replacing mapped functions', function () {
125 |
126 | var self = this;
127 |
128 | // Create an event mapping.
129 | this.bullet.on(this.testEventName, this.testCallback);
130 |
131 | // update the function mapped to the testEventName and set the 'once' param for the new function.
132 | self.bullet.replaceCallback(self.testEventName, self.testCallback, self.someOtherCallback, true);
133 |
134 | // Get the updated events map.
135 | var mappings = this.bullet._getMappings();
136 |
137 | expect(mappings[this.testEventName].callbacks[0].once).to.equal(true);
138 | });
139 | });
140 |
--------------------------------------------------------------------------------
/test/spec/methods/setTriggerAsync.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('setTriggerAsync()', function () {
4 |
5 | it('should set the private "_triggerAsync" property to a boolean value', function () {
6 |
7 | // It should be true by default.
8 | expect(this.bullet.getTriggerAsync()).to.equal(true);
9 |
10 | // Turn off async triggers.
11 | this.bullet.setTriggerAsync(false);
12 |
13 | expect(this.bullet.getTriggerAsync()).to.equal(false);
14 |
15 | // Turn on async triggers.
16 | this.bullet.setTriggerAsync(true);
17 |
18 | expect(this.bullet.getTriggerAsync()).to.equal(true);
19 | });
20 |
21 | it('should throw a ParamTypeError if a non-boolean value is passed as the parameter', function () {
22 |
23 | var self = this;
24 |
25 | // It should be true by default.
26 | expect(this.bullet.getTriggerAsync()).to.equal(true);
27 |
28 | function callSetTriggerAsync () {
29 |
30 | // Call setTriggerAsync and pass in a non-boolean value.
31 | self.bullet.setTriggerAsync({});
32 | }
33 |
34 | expect(callSetTriggerAsync).to.throw(this.bullet._errors.ParamTypeError);
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/test/spec/methods/trigger.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('trigger()', function () {
4 |
5 | afterEach(function () {
6 |
7 | sinon.restore(this.testCallback);
8 | });
9 |
10 | it('should trigger a mapped callback', function () {
11 |
12 | sinon.spy(this, 'testCallback');
13 |
14 | // Create an event mapping so that we can test that it gets triggered below.
15 | this.bullet.on(this.testEventName, this.testCallback);
16 |
17 | // Trigger the event.
18 | this.bullet.trigger(this.testEventName);
19 |
20 | expect(this.testCallback.calledOnce).to.equal(true);
21 | });
22 |
23 | it('should trigger a mapped callback with data', function () {
24 |
25 | var testData = {hello : 'sunshine'};
26 |
27 | sinon.spy(this, 'testCallback');
28 |
29 | // Create an event mapping so that we can test that it gets triggered below.
30 | this.bullet.on(this.testEventName, this.testCallback);
31 |
32 | // Trigger the event with data.
33 | this.bullet.trigger(this.testEventName, testData);
34 |
35 | expect(this.testCallback).to.have.been.calledWith(testData);
36 | });
37 |
38 | it('should throw an ParamTypeError if the event name param is not a string', function () {
39 |
40 | var self = this;
41 |
42 | function callTrigger () {
43 |
44 | // Attempt to trigger an event with a non-string event name parameter.
45 | self.bullet.trigger({});
46 | }
47 |
48 | expect(callTrigger).to.throw(this.bullet._errors.ParamTypeError);
49 | });
50 |
51 | it('should throw an EventNameLengthError if the event name param is an empty string', function () {
52 |
53 | var self = this;
54 |
55 | function callTrigger () {
56 |
57 | // Attempt to trigger an event with an empty string as the event name parameter.
58 | self.bullet.trigger('');
59 | }
60 |
61 | expect(callTrigger).to.throw(this.bullet._errors.EventNameLengthError);
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/test/spec/strict-methods/getStrictMode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Strict Mode:: getStrictMode()', function () {
4 |
5 | it('should return the boolean value of the private "_strictMode" property', function () {
6 |
7 | expect(this.bullet.getStrictMode()).to.be.a('boolean');
8 | });
9 |
10 | it('should be false by default', function () {
11 |
12 | expect(this.bullet.getStrictMode()).to.equal(false);
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/test/spec/strict-methods/off.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Strict Mode:: off()', function () {
4 |
5 | it('should throw an UndeclaredEventError if the event name param is not in the "events" object', function () {
6 |
7 | var self = this;
8 |
9 | // Turn on strict mode.
10 | this.bullet.setStrictMode(true);
11 |
12 | function callOff () {
13 |
14 | // Attempt to unmap an event that hasn't been added to the 'events' object.
15 | self.bullet.off(self.testEventName, self.testCallback);
16 | }
17 |
18 | expect(callOff).to.throw(this.bullet._errors.UndeclaredEventError);
19 | });
20 |
21 | it('should not throw an UndeclaredEventError if the event name param is in the "events" object', function () {
22 |
23 | var self = this;
24 |
25 | // Turn on strict mode.
26 | this.bullet.setStrictMode(true);
27 |
28 | // Add the test event to the 'events' object via the 'addEventName' method.
29 | this.bullet.addEventName(this.testEventName);
30 |
31 | // Map the test event to a callback via the 'on' method.
32 | this.bullet.on(this.testEventName, this.testCallback);
33 |
34 | function callOff () {
35 |
36 | // Unmap the event that was added to the 'events' object.
37 | self.bullet.off(self.testEventName, self.testCallback);
38 | }
39 |
40 | expect(callOff).to.not.throw(this.bullet._errors.UndeclaredEventError);
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/test/spec/strict-methods/on.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Strict Mode:: on()', function () {
4 |
5 | it('should throw an UndeclaredEventError if the event name param is not in the "events" object', function () {
6 |
7 | var self = this;
8 |
9 | // Turn on strict mode.
10 | this.bullet.setStrictMode(true);
11 |
12 | // The map should start empty
13 | expect(this.bullet._getMappings()).to.deep.equal({});
14 |
15 | function callOn () {
16 |
17 | // Attempt to map an event that hasn't been added to the 'events' object.
18 | self.bullet.on(self.testEventName, self.testCallback);
19 | }
20 |
21 | expect(callOn).to.throw(this.bullet._errors.UndeclaredEventError);
22 |
23 | // The map should still be empty
24 | expect(this.bullet._getMappings()).to.deep.equal({});
25 | });
26 |
27 | it('should not throw an UndeclaredEventError if the event name param is in the "events" object', function () {
28 |
29 | var self = this;
30 |
31 | // Turn on strict mode.
32 | this.bullet.setStrictMode(true);
33 |
34 | // Add the test event to the 'events' object via the 'addEventName' method.
35 | this.bullet.addEventName(this.testEventName);
36 |
37 | // Get the mappings.
38 | var mappings = this.bullet._getMappings();
39 |
40 | // The map should start empty.
41 | expect(mappings).to.deep.equal({});
42 |
43 | function callOn () {
44 |
45 | // Map an event that was added to the 'events' object.
46 | self.bullet.on(self.testEventName, self.testCallback);
47 | }
48 |
49 | expect(callOn).to.not.throw(this.bullet._errors.UndeclaredEventError);
50 |
51 | // Get the updated events map.
52 | mappings = this.bullet._getMappings();
53 |
54 | expect(mappings[this.testEventName]).to.be.an('object');
55 | expect(mappings[this.testEventName].callbacks[0].cb).to.equal(this.testCallback);
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/test/spec/strict-methods/once.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Strict Mode:: once()', function () {
4 |
5 | it('should throw an UndeclaredEventError if the event name param is not in the "events" object', function () {
6 |
7 | var self = this;
8 |
9 | // Turn on strict mode.
10 | this.bullet.setStrictMode(true);
11 |
12 | // The map should start empty
13 | expect(this.bullet._getMappings()).to.deep.equal({});
14 |
15 | function callOnce () {
16 |
17 | // Attempt to map an event that hasn't been added to the 'events' object.
18 | self.bullet.once(self.testEventName, self.testCallback);
19 | }
20 |
21 | expect(callOnce).to.throw(this.bullet._errors.UndeclaredEventError);
22 |
23 | // The map should still be empty
24 | expect(this.bullet._getMappings()).to.deep.equal({});
25 | });
26 |
27 | it('should not throw an UndeclaredEventError if the event name param is in the "events" object', function () {
28 |
29 | var self = this;
30 |
31 | // Turn on strict mode.
32 | this.bullet.setStrictMode(true);
33 |
34 | // Add the test event to the 'events' object via the 'addEventName' method.
35 | this.bullet.addEventName(this.testEventName);
36 |
37 | // Get the mappings.
38 | var mappings = this.bullet._getMappings();
39 |
40 | // The map should start empty.
41 | expect(mappings).to.deep.equal({});
42 |
43 | function callOnce () {
44 |
45 | // Map an event that was added to the 'events' object.
46 | self.bullet.once(self.testEventName, self.testCallback);
47 | }
48 |
49 | expect(callOnce).to.not.throw(this.bullet._errors.UndeclaredEventError);
50 |
51 | // Get the updated events map.
52 | mappings = this.bullet._getMappings();
53 |
54 | expect(mappings[this.testEventName]).to.be.an('object');
55 | expect(mappings[this.testEventName].callbacks[0].cb).to.equal(this.testCallback);
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/test/spec/strict-methods/replaceAllCallbacks.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Strict Mode:: replaceAllCallbacks()', function () {
4 |
5 | it('should throw an UndeclaredEventError if the event name param is not in the "events" object', function () {
6 |
7 | var self = this;
8 |
9 | // Create an event mapping.
10 | this.bullet.on(this.testEventName, this.testCallback);
11 |
12 | // Turn on strict mode after the event was already mapped (to avoid errors from the 'on' method).
13 | this.bullet.setStrictMode(true);
14 |
15 | function callReplaceAllCallbacks () {
16 |
17 | // Attempt to replace all functions for an event that hasn't been added to the 'events' object.
18 | self.bullet.replaceAllCallbacks(self.testEventName, self.someOtherCallback);
19 | }
20 |
21 | expect(callReplaceAllCallbacks).to.throw(this.bullet._errors.UndeclaredEventError);
22 | });
23 |
24 | it('should not throw an UndeclaredEventError if the event name param is in the "events" object', function () {
25 |
26 | var self = this;
27 |
28 | // Turn on strict mode.
29 | this.bullet.setStrictMode(true);
30 |
31 | // Add the test event to the 'events' object via the 'addEventName' method.
32 | this.bullet.addEventName(this.testEventName);
33 |
34 | // Create an event mapping.
35 | this.bullet.on(this.testEventName, this.testCallback);
36 |
37 | function callReplaceAllCallbacks () {
38 |
39 | // Replace all functions for an event that was added to the 'events' object.
40 | self.bullet.replaceAllCallbacks(self.testEventName, self.someOtherCallback);
41 | }
42 |
43 | expect(callReplaceAllCallbacks).to.not.throw(this.bullet._errors.UndeclaredEventError);
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/test/spec/strict-methods/replaceCallback.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Strict Mode:: replaceCallback()', function () {
4 |
5 | it('should throw an UndeclaredEventError if the event name param is not in the "events" object', function () {
6 |
7 | var self = this;
8 |
9 | // Create an event mapping.
10 | this.bullet.on(this.testEventName, this.testCallback);
11 |
12 | // Turn on strict mode after the event was already mapped (to avoid errors from the 'on' method).
13 | this.bullet.setStrictMode(true);
14 |
15 | function callReplace () {
16 |
17 | // Attempt to replace a function for an event that hasn't been added to the 'events' object.
18 | self.bullet.replaceCallback(self.testEventName, self.testCallback, self.someOtherCallback);
19 | }
20 |
21 | expect(callReplace).to.throw(this.bullet._errors.UndeclaredEventError);
22 | });
23 |
24 | it('should not throw an UndeclaredEventError if the event name param is in the "events" object', function () {
25 |
26 | var self = this;
27 |
28 | // Turn on strict mode.
29 | this.bullet.setStrictMode(true);
30 |
31 | // Add the test event to the 'events' object via the 'addEventName' method.
32 | this.bullet.addEventName(this.testEventName);
33 |
34 | // Create an event mapping.
35 | this.bullet.on(this.testEventName, this.testCallback);
36 |
37 | function callReplace () {
38 |
39 | // Replace a function for an event that was added to the 'events' object.
40 | self.bullet.replaceCallback(self.testEventName, self.testCallback, self.someOtherCallback);
41 | }
42 |
43 | expect(callReplace).to.not.throw(this.bullet._errors.UndeclaredEventError);
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/test/spec/strict-methods/setStrictMode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Strict Mode:: setStrictMode()', function () {
4 |
5 | it('should set the private "_strictMode" property to a boolean value', function () {
6 |
7 | // It should be false by default.
8 | expect(this.bullet.getStrictMode()).to.equal(false);
9 |
10 | // Turn on strict mode.
11 | this.bullet.setStrictMode(true);
12 |
13 | expect(this.bullet.getStrictMode()).to.equal(true);
14 |
15 | // Turn off strict mode.
16 | this.bullet.setStrictMode(false);
17 |
18 | expect(this.bullet.getStrictMode()).to.equal(false);
19 | });
20 |
21 | it('should throw a ParamTypeError if a non-boolean value is passed as the parameter', function () {
22 |
23 | var self = this;
24 |
25 | // It should be false by default.
26 | expect(this.bullet.getStrictMode()).to.equal(false);
27 |
28 | function callSetStrictMode () {
29 |
30 | // Call setStrictMode and pass in a non-boolean value.
31 | self.bullet.setStrictMode({});
32 | }
33 |
34 | expect(callSetStrictMode).to.throw(this.bullet._errors.ParamTypeError);
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/test/spec/strict-methods/trigger.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Strict Mode:: trigger()', function () {
4 |
5 | it('should throw an UndeclaredEventError if the event name param is not in the "events" object', function () {
6 |
7 | var self = this;
8 |
9 | // Turn on strict mode.
10 | this.bullet.setStrictMode(true);
11 |
12 | function callTrigger () {
13 |
14 | // Attempt to trigger an event that hasn't been added to the 'events' object.
15 | self.bullet.trigger(self.testEventName);
16 | }
17 |
18 | expect(callTrigger).to.throw(this.bullet._errors.UndeclaredEventError);
19 | });
20 |
21 | it('should throw an UnmappedEventError if the event name param is not mapped to any callbacks', function () {
22 |
23 | var self = this;
24 |
25 | // Turn on strict mode.
26 | this.bullet.setStrictMode(true);
27 |
28 | // Add the test event to the 'events' object via the 'addEventName' method.
29 | this.bullet.addEventName(this.testEventName);
30 |
31 | function callTrigger () {
32 |
33 | // Attempt to trigger an event that hasn't been mapped any callbacks via the 'on' method.
34 | self.bullet.trigger(self.testEventName);
35 | }
36 |
37 | expect(callTrigger).to.throw(this.bullet._errors.UnmappedEventError);
38 | });
39 |
40 | it('should not throw an UndeclaredEventError if the event name param is in the "events" object', function () {
41 |
42 | var self = this;
43 |
44 | // Turn on strict mode.
45 | this.bullet.setStrictMode(true);
46 |
47 | // Add the test event to the 'events' object via the 'addEventName' method.
48 | this.bullet.addEventName(this.testEventName);
49 |
50 | function callTrigger () {
51 |
52 | // Trigger the event that was added to the 'events' object, but wasn't mapped to any callback.
53 | self.bullet.trigger(self.testEventName);
54 | }
55 |
56 | expect(callTrigger).not.to.throw(this.bullet._errors.UndeclaredEventError);
57 | });
58 |
59 | it('should not throw an UnmappedEventError if the event name param is mapped to a callback', function () {
60 |
61 | var self = this;
62 |
63 | // Turn on strict mode.
64 | this.bullet.setStrictMode(true);
65 |
66 | // Add the test event to the 'events' object via the 'addEventName' method.
67 | this.bullet.addEventName(this.testEventName);
68 |
69 | // Map the event that was added to the 'events' object.
70 | self.bullet.on(self.testEventName, self.testCallback);
71 |
72 | function callTrigger () {
73 |
74 | // Trigger the event that was added to the 'events' object and mapped to a callback.
75 | self.bullet.trigger(self.testEventName);
76 | }
77 |
78 | expect(callTrigger).to.not.throw(this.bullet._errors.UnmappedEventError);
79 | });
80 | });
81 |
--------------------------------------------------------------------------------