Channel = function (channelName) {
this.channelName = channelName;
}...
if (!channelName) {
throw new Error('You must provide a name for the channel.');
}
if (Radio._channels[channelName]) {
return Radio._channels[channelName];
} else {
return (Radio._channels[channelName] = new Radio.Channel(channelName));
}
};
/*
* Backbone.Radio.Channel
* ----------------------
* A Channel is an object that extends from Backbone.Events,
..._callHandler = function (callback, context, args) {
var a1 = args[0],
a2 = args[1],
a3 = args[2];
switch (args.length) {
case 0:
return callback.call(context);
case 1:
return callback.call(context, a1);
case 2:
return callback.call(context, a1, a2);
case 3:
return callback.call(context, a1, a2, a3);
default:
return callback.apply(context, args);
}
}...
Radio.log.apply(this, [channelName, name].concat(args));
}
// If the request isn't handled, log it in DEBUG mode and exit
if (requests && (requests[name] || requests['default'])) {
var handler = requests[name] || requests['default'];
args = requests[name] ? args : arguments;
return Radio._callHandler(handler.callback, handler.context, args);
} else {
Radio.debugLog('An unhandled request was fired', name, channelName);
}
},
// Set up a handler for a request
reply: function(name, callback, context) {
..._debugText = function (warning, eventName, channelName) {
return warning + (channelName ? ' on the ' + channelName + ' channel' : '') + ': "' + eventName + '"';
}...
// This is the method that's called when an unregistered event was called.
// By default, it logs warning to the console. By overriding this you could
// make it throw an Error, for instance. This would make firing a nonexistent event
// have the same consequence as firing a nonexistent method on an Object.
Radio.debugLog = function(warning, eventName, channelName) {
if (Radio.DEBUG && console && console.warn) {
console.warn(Radio._debugText(warning, eventName, channelName));
}
};
var eventSplitter = /\s+/;
// An internal method used to handle Radio's method overloading for Requests.
// It's borrowed from Backbone.Events. It differs from Backbone's overload
..._eventsApi = function (obj, action, name, rest) {
if (!name) {
return false;
}
var results = {};
// Handle event maps.
if ((typeof name === 'undefined' ? 'undefined' : _typeof(name)) === 'object') {
for (var key in name) {
var result = obj[action].apply(obj, [key, name[key]].concat(rest));
eventSplitter.test(key) ? _.extend(results, result) : results[key] = result;
}
return results;
}
// Handle space separated event names.
if (eventSplitter.test(name)) {
var names = name.split(eventSplitter);
for (var i = 0, l = names.length; i < l; i++) {
results[names[i]] = obj[action].apply(obj, [names[i]].concat(rest));
}
return results;
}
return false;
}...
}
Radio.Requests = {
// Make a request
request: function(name) {
var args = _.toArray(arguments).slice(1);
var results = Radio._eventsApi(this, 'request', name, args);
if (results) {
return results;
}
var channelName = this.channelName;
var requests = this._requests;
// Check if we should log the request, and if so, do it
...bind = function (channelName) {
args = _.toArray(arguments).slice(1);
channel = this.channel(channelName);
return channel[methodName].apply(channel, args);
}...
*/
var _logs = {};
// This is to produce an identical function in both tuneIn and tuneOut,
// so that Backbone.Events unregisters it.
function _partial(channelName) {
return _logs[channelName] || (_logs[channelName] = _.bind(Radio.log, Radio, channelName
));
}
_.extend(Radio, {
// Log information about the channel and event
log: function(channelName, eventName) {
if (typeof console === 'undefined') { return; }
...channel = function (channelName) {
if (!channelName) {
throw new Error('You must provide a name for the channel.');
}
if (Radio._channels[channelName]) {
return Radio._channels[channelName];
} else {
return Radio._channels[channelName] = new Radio.Channel(channelName);
}
}...
The real draw of Backbone.Radio are Channels. A Channel is simply an object that has Backbone.Events and Radio.Requests mixed into
it:
it's a standalone message bus comprised of both systems.
Getting a handle of a Channel is easy.
```js
// Get a reference to the channel named 'user'
var userChannel = Backbone.Radio.channel('user');
```
Once you've got a channel, you can attach handlers to it.
```js
userChannel.on('some:event', function() {
console.log('An event has happened!');
...debugLog = function (warning, eventName, channelName) {
if (Radio.DEBUG && console && console.warn) {
console.warn(Radio._debugText(warning, eventName, channelName));
}
}...
// If the request isn't handled, log it in DEBUG mode and exit
if (requests && (requests[name] || requests['default'])) {
var handler = requests[name] || requests['default'];
args = requests[name] ? args : arguments;
return Radio._callHandler(handler.callback, handler.context, args);
} else {
Radio.debugLog('An unhandled request was fired', name, channelName);
}
},
// Set up a handler for a request
reply: function(name, callback, context) {
if (Radio._eventsApi(this, 'reply', name, [callback, context])) {
return this;
...listenTo = function (channelName) {
args = _.toArray(arguments).slice(1);
channel = this.channel(channelName);
return channel[methodName].apply(channel, args);
}...
Anyone who has used Backbone should be quite familiar with Backbone.Events. Backbone.Events is what facilitates
communications between objects in your application. The quintessential example of this is listening in on a
Model's change event.
```js
// Listen in on a model's change events
this.listenTo(someModel, 'change', myCallback);
// Later on, the model triggers a change event when it has been changed
someModel.trigger('change');
```
Let's look at a diagram for Backbone.Events:
...listenToOnce = function (channelName) {
args = _.toArray(arguments).slice(1);
channel = this.channel(channelName);
return channel[methodName].apply(channel, args);
}n/a
function log(channelName, eventName) {
if (typeof console === 'undefined') {
return;
}
var args = _.toArray(arguments).slice(2);
console.log('[' + channelName + '] "' + eventName + '"', args);
}...
var userChannel = Backbone.Radio.channel('user');
```
Once you've got a channel, you can attach handlers to it.
```js
userChannel.on('some:event', function() {
console.log('An event has happened!');
});
userChannel.reply('some:request', 'food is good');
```
You can also use the 'trigger' methods on the Channel.
...noConflict = function () {
Backbone.Radio = previousRadio;
return this;
}n/a
off = function (channelName) {
args = _.toArray(arguments).slice(1);
channel = this.channel(channelName);
return channel[methodName].apply(channel, args);
}...
return this;
},
// Stop logging all of the activities on this channel to the console
tuneOut: function(channelName) {
var channel = Radio.channel(channelName);
channel._tunedIn = false;
channel.off('all', _partial(channelName));
delete _logs[channelName];
return this;
}
});
/*
* Backbone.Radio.Requests
...on = function (channelName) {
args = _.toArray(arguments).slice(1);
channel = this.channel(channelName);
return channel[methodName].apply(channel, args);
}...
// Get a reference to the channel named 'user'
var userChannel = Backbone.Radio.channel('user');
```
Once you've got a channel, you can attach handlers to it.
```js
userChannel.on('some:event', function() {
console.log('An event has happened!');
});
userChannel.reply('some:request', 'food is good');
```
You can also use the 'trigger' methods on the Channel.
...once = function (channelName) {
args = _.toArray(arguments).slice(1);
channel = this.channel(channelName);
return channel[methodName].apply(channel, args);
}...
replyOnce: function(name, callback, context) {
if (Radio._eventsApi(this, 'replyOnce', name, [callback, context])) {
return this;
}
var self = this;
var once = _.once(function() {
self.stopReplying(name);
return makeCallback(callback).apply(this, arguments);
});
return this.reply(name, once, context);
},
...radio.Channel = function (channelName) {
this.channelName = channelName;
}n/a
reply = function (channelName) {
args = _.toArray(arguments).slice(1);
channel = this.channel(channelName);
return channel[methodName].apply(channel, args);
}...
nothing in particular about an occurrence, Requests are asking for a very specific thing to occur. As a consequence of this,
requests are 'one-to-one,' which means that you cannot have multiple 'listeners' to a single request.
Let's look at a basic example.
```js
// Set up an object to reply to a request. In this case, whether or not its visible.
myObject.reply('visible', this.isVisible);
// Get whether it's visible or not.
var isViewVisible = myObject.request('visible');
```
The handler in `reply` can either return a flat value, like `true` or `false`, or a function to be executed. Either way, the value
is sent back to
the requester.
...replyOnce = function (channelName) {
args = _.toArray(arguments).slice(1);
channel = this.channel(channelName);
return channel[methodName].apply(channel, args);
}n/a
request = function (channelName) {
args = _.toArray(arguments).slice(1);
channel = this.channel(channelName);
return channel[methodName].apply(channel, args);
}...
Let's look at a basic example.
```js
// Set up an object to reply to a request. In this case, whether or not its visible.
myObject.reply('visible', this.isVisible);
// Get whether it's visible or not.
var isViewVisible = myObject.request('visible');
```
The handler in `reply` can either return a flat value, like `true` or `false`, or a function to be executed. Either way, the value
is sent back to
the requester.
Here's a diagram of the Requests pattern:
...reset = function (channelName) {
var channels = !channelName ? this._channels : [this._channels[channelName]];
_.each(channels, function (channel) {
channel.reset();
});
}...
You can also reset a single channel, or all Channels, from the `Radio` object directly. Pass a
`channelName` to reset just that specific channel, or call the method without any arguments
to reset every channel.
```js
// Reset all channels
Radio.reset();
```
...stopListening = function (channelName) {
args = _.toArray(arguments).slice(1);
channel = this.channel(channelName);
return channel[methodName].apply(channel, args);
}...
};
_.extend(Radio.Channel.prototype, Backbone.Events, Radio.Requests, {
// Remove all handlers from the messaging systems of this channel
reset: function() {
this.off();
this.stopListening();
this.stopReplying();
return this;
}
});
/*
* Top-level API
...stopReplying = function (channelName) {
args = _.toArray(arguments).slice(1);
channel = this.channel(channelName);
return channel[methodName].apply(channel, args);
}...
// Turn on debug mode
Backbone.Radio.DEBUG = true;
// This will log a warning to the console if it goes unhandled
myChannel.request('show:view');
// Likewise, this will too, helping to prevent memory leaks
myChannel.stopReplying('startTime');
```
#### `debugLog(warning, eventName, channelName)`
A function executed whenever an unregistered request is interacted with on a Channel. Only
called when `DEBUG` is set to `true`. By overriding this you could, for instance, make unhandled
events throw Errors.
...trigger = function (channelName) {
args = _.toArray(arguments).slice(1);
channel = this.channel(channelName);
return channel[methodName].apply(channel, args);
}...
Model's change event.
```js
// Listen in on a model's change events
this.listenTo(someModel, 'change', myCallback);
// Later on, the model triggers a change event when it has been changed
someModel.trigger('change');
```
Let's look at a diagram for Backbone.Events:
<p align='center'>
<img src='https://cloud.githubusercontent.com/assets/10248067/11762943/5a927e54-a0bd-11e5-8aa5-e0fafae0e559.png'
; alt='Backbone.Events diagram'>
</p>
...function tuneIn(channelName) {
var channel = Radio.channel(channelName);
channel._tunedIn = true;
channel.on('all', _partial(channelName));
return this;
}...
Tuning into a Channel is another useful tool for debugging. It passes all
triggers and requests made on the channel to
[`Radio.log`](https://github.com/jmeas/backbone.radio#log-channelname-eventname--args-).
Returns `Backbone.Radio`.
```js
Backbone.Radio.tuneIn('calendar');
```
#### `tuneOut( channelName )`
Once you're done tuning in you can call `tuneOut` to stop the logging. Returns `Backbone.Radio`.
```js
...function tuneOut(channelName) {
var channel = Radio.channel(channelName);
channel._tunedIn = false;
channel.off('all', _partial(channelName));
delete _logs[channelName];
return this;
}...
```
#### `tuneOut( channelName )`
Once you're done tuning in you can call `tuneOut` to stop the logging. Returns `Backbone.Radio`.
```js
Backbone.Radio.tuneOut('calendar');
```
#### `log( channelName, eventName [, args...] )`
When tuned into a Channel, this method will be called for all activity on
a channel. The default implementation is to `console.log` the following message:
...unbind = function (channelName) {
args = _.toArray(arguments).slice(1);
channel = this.channel(channelName);
return channel[methodName].apply(channel, args);
}n/a
Channel = function (channelName) {
this.channelName = channelName;
}...
if (!channelName) {
throw new Error('You must provide a name for the channel.');
}
if (Radio._channels[channelName]) {
return Radio._channels[channelName];
} else {
return (Radio._channels[channelName] = new Radio.Channel(channelName));
}
};
/*
* Backbone.Radio.Channel
* ----------------------
* A Channel is an object that extends from Backbone.Events,
...bind = function (name, callback, context) {
return internalOn(this, name, callback, context);
}...
*/
var _logs = {};
// This is to produce an identical function in both tuneIn and tuneOut,
// so that Backbone.Events unregisters it.
function _partial(channelName) {
return _logs[channelName] || (_logs[channelName] = _.bind(Radio.log, Radio, channelName
));
}
_.extend(Radio, {
// Log information about the channel and event
log: function(channelName, eventName) {
if (typeof console === 'undefined') { return; }
...listenTo = function (obj, name, callback) {
if (!obj) return this;
var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
var listeningTo = this._listeningTo || (this._listeningTo = {});
var listening = listeningTo[id];
// This object is not listening to any other events on `obj` yet.
// Setup the necessary references to track the listening callbacks.
if (!listening) {
var thisId = this._listenId || (this._listenId = _.uniqueId('l'));
listening = listeningTo[id] = {obj: obj, objId: id, id: thisId, listeningTo: listeningTo, count: 0};
}
// Bind callbacks on obj, and keep track of them on listening.
internalOn(obj, name, callback, this, listening);
return this;
}...
Anyone who has used Backbone should be quite familiar with Backbone.Events. Backbone.Events is what facilitates
communications between objects in your application. The quintessential example of this is listening in on a
Model's change event.
```js
// Listen in on a model's change events
this.listenTo(someModel, 'change', myCallback);
// Later on, the model triggers a change event when it has been changed
someModel.trigger('change');
```
Let's look at a diagram for Backbone.Events:
...listenToOnce = function (obj, name, callback) {
// Map the event into a `{event: once}` object.
var events = eventsApi(onceMap, {}, name, callback, _.bind(this.stopListening, this, obj));
return this.listenTo(obj, events);
}n/a
off = function (name, callback, context) {
if (!this._events) return this;
this._events = eventsApi(offApi, this._events, name, callback, {
context: context,
listeners: this._listeners
});
return this;
}...
return this;
},
// Stop logging all of the activities on this channel to the console
tuneOut: function(channelName) {
var channel = Radio.channel(channelName);
channel._tunedIn = false;
channel.off('all', _partial(channelName));
delete _logs[channelName];
return this;
}
});
/*
* Backbone.Radio.Requests
...on = function (name, callback, context) {
return internalOn(this, name, callback, context);
}...
// Get a reference to the channel named 'user'
var userChannel = Backbone.Radio.channel('user');
```
Once you've got a channel, you can attach handlers to it.
```js
userChannel.on('some:event', function() {
console.log('An event has happened!');
});
userChannel.reply('some:request', 'food is good');
```
You can also use the 'trigger' methods on the Channel.
...once = function (name, callback, context) {
// Map the event into a `{event: once}` object.
var events = eventsApi(onceMap, {}, name, callback, _.bind(this.off, this));
if (typeof name === 'string' && context == null) callback = void 0;
return this.on(events, callback, context);
}...
replyOnce: function(name, callback, context) {
if (Radio._eventsApi(this, 'replyOnce', name, [callback, context])) {
return this;
}
var self = this;
var once = _.once(function() {
self.stopReplying(name);
return makeCallback(callback).apply(this, arguments);
});
return this.reply(name, once, context);
},
...function reply(name, callback, context) {
if (Radio._eventsApi(this, 'reply', name, [callback, context])) {
return this;
}
this._requests || (this._requests = {});
if (this._requests[name]) {
Radio.debugLog('A request was overwritten', name, this.channelName);
}
this._requests[name] = {
callback: makeCallback(callback),
context: context || this
};
return this;
}...
nothing in particular about an occurrence, Requests are asking for a very specific thing to occur. As a consequence of this,
requests are 'one-to-one,' which means that you cannot have multiple 'listeners' to a single request.
Let's look at a basic example.
```js
// Set up an object to reply to a request. In this case, whether or not its visible.
myObject.reply('visible', this.isVisible);
// Get whether it's visible or not.
var isViewVisible = myObject.request('visible');
```
The handler in `reply` can either return a flat value, like `true` or `false`, or a function to be executed. Either way, the value
is sent back to
the requester.
...function replyOnce(name, callback, context) {
if (Radio._eventsApi(this, 'replyOnce', name, [callback, context])) {
return this;
}
var self = this;
var once = _.once(function () {
self.stopReplying(name);
return makeCallback(callback).apply(this, arguments);
});
return this.reply(name, once, context);
}n/a
function request(name) {
var args = _.toArray(arguments).slice(1);
var results = Radio._eventsApi(this, 'request', name, args);
if (results) {
return results;
}
var channelName = this.channelName;
var requests = this._requests;
// Check if we should log the request, and if so, do it
if (channelName && this._tunedIn) {
Radio.log.apply(this, [channelName, name].concat(args));
}
// If the request isn't handled, log it in DEBUG mode and exit
if (requests && (requests[name] || requests['default'])) {
var handler = requests[name] || requests['default'];
args = requests[name] ? args : arguments;
return Radio._callHandler(handler.callback, handler.context, args);
} else {
Radio.debugLog('An unhandled request was fired', name, channelName);
}
}...
Let's look at a basic example.
```js
// Set up an object to reply to a request. In this case, whether or not its visible.
myObject.reply('visible', this.isVisible);
// Get whether it's visible or not.
var isViewVisible = myObject.request('visible');
```
The handler in `reply` can either return a flat value, like `true` or `false`, or a function to be executed. Either way, the value
is sent back to
the requester.
Here's a diagram of the Requests pattern:
...function reset() {
this.off();
this.stopListening();
this.stopReplying();
return this;
}...
You can also reset a single channel, or all Channels, from the `Radio` object directly. Pass a
`channelName` to reset just that specific channel, or call the method without any arguments
to reset every channel.
```js
// Reset all channels
Radio.reset();
```
...stopListening = function (obj, name, callback) {
var listeningTo = this._listeningTo;
if (!listeningTo) return this;
var ids = obj ? [obj._listenId] : _.keys(listeningTo);
for (var i = 0; i < ids.length; i++) {
var listening = listeningTo[ids[i]];
// If listening doesn't exist, this object is not currently
// listening to obj. Break out early.
if (!listening) break;
listening.obj.off(name, callback, this);
}
return this;
}...
};
_.extend(Radio.Channel.prototype, Backbone.Events, Radio.Requests, {
// Remove all handlers from the messaging systems of this channel
reset: function() {
this.off();
this.stopListening();
this.stopReplying();
return this;
}
});
/*
* Top-level API
...function stopReplying(name, callback, context) {
if (Radio._eventsApi(this, 'stopReplying', name)) {
return this;
}
// Remove everything if there are no arguments passed
if (!name && !callback && !context) {
delete this._requests;
} else if (!removeHandlers(this._requests, name, callback, context)) {
Radio.debugLog('Attempted to remove the unregistered request', name, this.channelName);
}
return this;
}...
// Turn on debug mode
Backbone.Radio.DEBUG = true;
// This will log a warning to the console if it goes unhandled
myChannel.request('show:view');
// Likewise, this will too, helping to prevent memory leaks
myChannel.stopReplying('startTime');
```
#### `debugLog(warning, eventName, channelName)`
A function executed whenever an unregistered request is interacted with on a Channel. Only
called when `DEBUG` is set to `true`. By overriding this you could, for instance, make unhandled
events throw Errors.
...trigger = function (name) {
if (!this._events) return this;
var length = Math.max(0, arguments.length - 1);
var args = Array(length);
for (var i = 0; i < length; i++) args[i] = arguments[i + 1];
eventsApi(triggerApi, this._events, name, void 0, args);
return this;
}...
Model's change event.
```js
// Listen in on a model's change events
this.listenTo(someModel, 'change', myCallback);
// Later on, the model triggers a change event when it has been changed
someModel.trigger('change');
```
Let's look at a diagram for Backbone.Events:
<p align='center'>
<img src='https://cloud.githubusercontent.com/assets/10248067/11762943/5a927e54-a0bd-11e5-8aa5-e0fafae0e559.png'
; alt='Backbone.Events diagram'>
</p>
...unbind = function (name, callback, context) {
if (!this._events) return this;
this._events = eventsApi(offApi, this._events, name, callback, {
context: context,
listeners: this._listeners
});
return this;
}n/a
function reply(name, callback, context) {
if (Radio._eventsApi(this, 'reply', name, [callback, context])) {
return this;
}
this._requests || (this._requests = {});
if (this._requests[name]) {
Radio.debugLog('A request was overwritten', name, this.channelName);
}
this._requests[name] = {
callback: makeCallback(callback),
context: context || this
};
return this;
}...
nothing in particular about an occurrence, Requests are asking for a very specific thing to occur. As a consequence of this,
requests are 'one-to-one,' which means that you cannot have multiple 'listeners' to a single request.
Let's look at a basic example.
```js
// Set up an object to reply to a request. In this case, whether or not its visible.
myObject.reply('visible', this.isVisible);
// Get whether it's visible or not.
var isViewVisible = myObject.request('visible');
```
The handler in `reply` can either return a flat value, like `true` or `false`, or a function to be executed. Either way, the value
is sent back to
the requester.
...function replyOnce(name, callback, context) {
if (Radio._eventsApi(this, 'replyOnce', name, [callback, context])) {
return this;
}
var self = this;
var once = _.once(function () {
self.stopReplying(name);
return makeCallback(callback).apply(this, arguments);
});
return this.reply(name, once, context);
}n/a
function request(name) {
var args = _.toArray(arguments).slice(1);
var results = Radio._eventsApi(this, 'request', name, args);
if (results) {
return results;
}
var channelName = this.channelName;
var requests = this._requests;
// Check if we should log the request, and if so, do it
if (channelName && this._tunedIn) {
Radio.log.apply(this, [channelName, name].concat(args));
}
// If the request isn't handled, log it in DEBUG mode and exit
if (requests && (requests[name] || requests['default'])) {
var handler = requests[name] || requests['default'];
args = requests[name] ? args : arguments;
return Radio._callHandler(handler.callback, handler.context, args);
} else {
Radio.debugLog('An unhandled request was fired', name, channelName);
}
}...
Let's look at a basic example.
```js
// Set up an object to reply to a request. In this case, whether or not its visible.
myObject.reply('visible', this.isVisible);
// Get whether it's visible or not.
var isViewVisible = myObject.request('visible');
```
The handler in `reply` can either return a flat value, like `true` or `false`, or a function to be executed. Either way, the value
is sent back to
the requester.
Here's a diagram of the Requests pattern:
...function stopReplying(name, callback, context) {
if (Radio._eventsApi(this, 'stopReplying', name)) {
return this;
}
// Remove everything if there are no arguments passed
if (!name && !callback && !context) {
delete this._requests;
} else if (!removeHandlers(this._requests, name, callback, context)) {
Radio.debugLog('Attempted to remove the unregistered request', name, this.channelName);
}
return this;
}...
// Turn on debug mode
Backbone.Radio.DEBUG = true;
// This will log a warning to the console if it goes unhandled
myChannel.request('show:view');
// Likewise, this will too, helping to prevent memory leaks
myChannel.stopReplying('startTime');
```
#### `debugLog(warning, eventName, channelName)`
A function executed whenever an unregistered request is interacted with on a Channel. Only
called when `DEBUG` is set to `true`. By overriding this you could, for instance, make unhandled
events throw Errors.
...