Mediator 오브젝트에 레퍼런스를 유지한다. Mediator를 통하여 다른 Colleague와 통신한다
var mediator = (function(){
// Storage for topics that can be broadcast or listened to
var topics = {};
// Subscribe to a topic, supply a callback to be executed
// when that topic is broadcast to
var subscribe = function( topic, fn ){
if ( !topics[topic] ){
topics[topic] = [];
}
topics[topic].push( { context: this, callback: fn } );
return this;
};
// Publish/broadcast an event to the rest of the application
var publish = function( topic ){
var args;
if ( !topics[topic] ){
return false;
}
args = Array.prototype.slice.call( arguments, 1 );
for ( var i = 0, l = topics[topic].length; i < l; i++ ) {
var subscription = topics[topic][i];
subscription.callback.apply( subscription.context, args );
}
return this;
};
return {
publish: publish,
subscribe: subscribe,
installTo: function( obj ){
obj.subscribe = subscribe;
obj.publish = publish;
}
};
}());
/*!
* Mediator.js Library v0.9.5
* https://github.com/ajacksified/Mediator.js
*
* Copyright 2013, Jack Lawson
* MIT Licensed (http://www.opensource.org/licenses/mit-license.php)
*
* For more information: http://thejacklawson.com/2011/06/mediators-for-modularized-asynchronous-programming-in-javascript/index.html
* Project on GitHub: https://github.com/ajacksified/Mediator.js
*
* Last update: June 13 2013
*/
(function(global, factory) {
'use strict';
// 다양한 환경에서 적용할 수 있도록 모듈에 대한 export
if(typeof exports !== 'undefined') {
// Node/CommonJS
exports.Mediator = factory();
} else if(typeof define === 'function' && define.amd) {
// AMD
define('mediator-js', [], function() {
global.Mediator = factory();
return global.Mediator();
});
} else {
// Browser global
global.Mediator = factory();
}
}(this, function() {
'use strict';
// We'll generate guids for class instances for easy referencing later on.
// Subscriber instances will have an id that can be refernced for quick
// lookups.
// 글로벌 유니크 아이디를 생성하는 로직이다. 등록된 Colleague 오브젝트에 (즉, Subscriber Instances)
// 아이디를 부여해 준다
function guidGenerator() {
var S4 = function() {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
};
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
}
// Subscribers are instances of Mediator Channel registrations. We generate
// an object instance so that it can be updated later on without having to
// unregister and re-register. Subscribers are constructed with a function
// to be called, options object, and context.
// Colleague인 Subscriber 펑션 정의
function Subscriber(fn, options, context){
if(!(this instanceof Subscriber)) {
return new Subscriber(fn, options, context);
}
this.id = guidGenerator();
this.fn = fn;
this.options = options;
this.context = context;
this.channel = null;
}
// Subscriber Prototype 객체 정의
// Subscriber에 등록한 fn, context, options를 변경할 수 있다
Subscriber.prototype = {
// Mediator.update on a subscriber instance can update its function,context,
// or options object. It takes in an object and looks for fn, context, or
// options keys.
update: function(options){
if(options){
this.fn = options.fn || this.fn;
this.context = options.context || this.context;
this.options = options.options || this.options;
if(this.channel && this.options && this.options.priority !== undefined) {
this.channel.setPriority(this.id, this.options.priority);
}
}
}
};
// 채널을 통하여 Subscriber 의 등록/제거 관리
// Subscriber에 정보 publish
function Channel(namespace, parent){
if(!(this instanceof Channel)) {
return new Channel(namespace);
}
this.namespace = namespace || "";
this._subscribers = [];
this._channels = [];
this._parent = parent;
this.stopped = false;
}
// A Mediator channel holds a list of sub-channels and subscribers to be fired
// when Mediator.publish is called on the Mediator instance. It also contains
// some methods to manipulate its lists of data; only setPriority and
// StopPropagation are meant to be used. The other methods should be accessed
// through the Mediator instance.
// Mediator 채널이 sub-channel 목록을 가지고 있다
// Channel Prototype 객체 정의
Channel.prototype = {
addSubscriber: function(fn, options, context){
var subscriber = new Subscriber(fn, options, context);
if(options && options.priority !== undefined){
// Cheap hack to either parse as an int or turn it into 0. Runs faster
// in many browsers than parseInt with the benefit that it won't
// return a NaN.
options.priority = options.priority >> 0;
if(options.priority < 0){ options.priority = 0; }
if(options.priority >= this._subscribers.length){ options.priority = this._subscribers.length-1; }
this._subscribers.splice(options.priority, 0, subscriber);
}else{
this._subscribers.push(subscriber);
}
subscriber.channel = this;
return subscriber;
},
// The channel instance is passed as an argument to the mediator subscriber,
// and further subscriber propagation can be called with
// channel.StopPropagation().
// Mediator에 등록한 Subscriber에 아규먼트 전달 멈춤
stopPropagation: function(){
this.stopped = true;
},
getSubscriber: function(identifier){
var x = 0,
y = this._subscribers.length;
for(x, y; x < y; x++){
if(this._subscribers[x].id === identifier || this._subscribers[x].fn === identifier){
return this._subscribers[x];
}
}
},
// Channel.setPriority is useful in updating the order in which Subscribers
// are called, and takes an identifier (subscriber id or named function) and
// an array index. It will not search recursively through subchannels.
// subchannel을 돌면서 우선순위를 업데이트 한다
setPriority: function(identifier, priority){
var oldIndex = 0,
x = 0,
sub, firstHalf, lastHalf, y;
for(x = 0, y = this._subscribers.length; x < y; x++){
if(this._subscribers[x].id === identifier || this._subscribers[x].fn === identifier){
break;
}
oldIndex ++;
}
sub = this._subscribers[oldIndex];
firstHalf = this._subscribers.slice(0, oldIndex);
lastHalf = this._subscribers.slice(oldIndex+1);
this._subscribers = firstHalf.concat(lastHalf);
this._subscribers.splice(priority, 0, sub);
},
// sub channel 등록/삭제
addChannel: function(channel){
this._channels[channel] = new Channel((this.namespace ? this.namespace + ':' : '') + channel, this);
},
hasChannel: function(channel){
return this._channels.hasOwnProperty(channel);
},
returnChannel: function(channel){
return this._channels[channel];
},
removeSubscriber: function(identifier){
var x = this._subscribers.length - 1;
// If we don't pass in an id, we're clearing all
if(!identifier){
this._subscribers = [];
return;
}
// Going backwards makes splicing a whole lot easier.
for(x; x >= 0; x--) {
if(this._subscribers[x].fn === identifier || this._subscribers[x].id === identifier){
this._subscribers[x].channel = null;
this._subscribers.splice(x,1);
}
}
},
// This will publish arbitrary arguments to a subscriber and then to parent
// channels.
// 퍼블리싱 : subscriber에 아규먼트 전달
publish: function(data){
var x = 0,
y = this._subscribers.length,
called = false,
subscriber, l,
subsBefore,subsAfter;
// Priority is preserved in the _subscribers index.
for(x, y; x < y; x++) {
if(!this.stopped){
subscriber = this._subscribers[x];
if(subscriber.options !== undefined && typeof subscriber.options.predicate === "function"){
if(subscriber.options.predicate.apply(subscriber.context, data)){
subscriber.fn.apply(subscriber.context, data);
called = true;
}
}else{
subsBefore = this._subscribers.length;
subscriber.fn.apply(subscriber.context, data);
subsAfter = this._subscribers.length;
y = subsAfter;
if (subsAfter === subsBefore - 1){
x--;
}
called = true;
}
}
if(called && subscriber.options && subscriber.options !== undefined){
subscriber.options.calls--;
if(subscriber.options.calls < 1){
this.removeSubscriber(subscriber.id);
y--;
x--;
}
}
}
if(this._parent){
this._parent.publish(data);
}
this.stopped = false;
}
};
// Mediator 펑션
function Mediator() {
if(!(this instanceof Mediator)) {
return new Mediator();
}
this._channels = new Channel('');
}
// A Mediator instance is the interface through which events are registered
// and removed from publish channels.
Mediator.prototype = {
// Returns a channel instance based on namespace, for example
// application:chat:message:received
// sub channel은 namespace 기반으로 만들어 진다
getChannel: function(namespace){
var channel = this._channels,
namespaceHierarchy = namespace.split(':'),
x = 0,
y = namespaceHierarchy.length;
if(namespace === ''){
return channel;
}
if(namespaceHierarchy.length > 0){
for(x, y; x < y; x++){
if(!channel.hasChannel(namespaceHierarchy[x])){
channel.addChannel(namespaceHierarchy[x]);
}
channel = channel.returnChannel(namespaceHierarchy[x]);
}
}
return channel;
},
// Pass in a channel namespace, function to be called, options, and context
// to call the function in to Subscribe. It will create a channel if one
// does not exist. Options can include a predicate to determine if it
// should be called (based on the data published to it) and a priority
// index.
// sub channel에 namespace 기반으로 Subscriber 등록하기
subscribe: function(channelName, fn, options, context){
var channel = this.getChannel(channelName);
options = options || {};
context = context || {};
return channel.addSubscriber(fn, options, context);
},
// Pass in a channel namespace, function to be called, options, and context
// to call the function in to Subscribe. It will create a channel if one
// does not exist. Options can include a predicate to determine if it
// should be called (based on the data published to it) and a priority
// index.
// 한번 호출하고 끝내기
once: function(channelName, fn, options, context){
options = options || {};
options.calls = 1;
return this.subscribe(channelName, fn, options, context);
},
// Returns a subscriber for a given subscriber id / named function and
// channel namespace
// 아이디와 명칭으로 Subscriber 얻어오기 (identifier === GUID)
getSubscriber: function(identifier, channel){
return this.getChannel(channel || "").getSubscriber(identifier);
},
// Remove a subscriber from a given channel namespace recursively based on
// a passed-in subscriber id or named function.
remove: function(channelName, identifier){
this.getChannel(channelName).removeSubscriber(identifier);
},
// Publishes arbitrary data to a given channel namespace. Channels are
// called recursively downwards; a post to application:chat will post to
// application:chat:receive and application:chat:derp:test:beta:bananas.
// Called using Mediator.publish("application:chat", [ args ]);
// 퍼블리싱
publish: function(channelName){
var args = Array.prototype.slice.call(arguments, 1),
channel = this.getChannel(channelName);
args.push(channel);
this.getChannel(channelName).publish(args);
}
};
// 일반적으로 사용하는 이름으로도 Aliasing
// Alias some common names for easy interop
Mediator.prototype.on = Mediator.prototype.subscribe;
Mediator.prototype.bind = Mediator.prototype.subscribe;
Mediator.prototype.emit = Mediator.prototype.publish;
Mediator.prototype.trigger = Mediator.prototype.publish;
Mediator.prototype.off = Mediator.prototype.remove;
// Finally, expose it all.
Mediator.Channel = Channel;
Mediator.Subscriber = Subscriber;
Mediator.version = "0.9.5";
return Mediator;
}));
<!DOCTYPE html>
<html>
<head>
<script data-require="jquery@*" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<link data-require="bootstrap-css@*" data-semver="3.0.0" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" />
<link data-require="bootstrap@*" data-semver="3.0.0" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" />
<script data-require="bootstrap@*" data-semver="3.0.0" src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="mediator.js"></script>
<script src="script.js"></script>
</head>
<body>
<h1>Chat</h1>
<form id="chatForm">
<label for="fromBox">Your Name:</label>
<input id="fromBox" type="text" />
<br />
<label for="toBox">Send to:</label>
<input id="toBox" type="text" />
<br />
<label for="chatBox">Message:</label>
<input id="chatBox" type="text" />
<button type="submit">Chat</button>
</form>
<div id="chatResult"></div>
</body>
</html>
// create Mediator.js
var mediator = new Mediator();
$("#chatForm").submit(function(){
// Collect the details of the chat from our UI
var text = $( "#chatBox" ).val(),
from = $( "#fromBox" ).val(),
to = $( "#toBox" ).val();
// newMessage 채널로 퍼블리싱 하기
// Publish data from the chat to the newMessage topic
mediator.publish( "newMessage" , { message: text, from: from, to: to } );
return false;
});
// Append new messages as they come through
function displayChat( data ) {
var date = new Date(),
msg = data.from + " said \"" + data.message + "\" to " + data.to;
$( "#chatResult" ).prepend("<p>" + msg + " (" + date.toLocaleTimeString() + ")</p>");
}
// Log messages
function logChat( data ) {
if ( window.console ) {
console.log( data );
}
}
// newMessage 명칭으로 Subscriber 등록하기
// Subscribe to new chat messages being submitted
// via the mediator
mediator.subscribe( "newMessage", displayChat );
mediator.subscribe( "newMessage", logChat );