1 /**
  2  * @fileOverview
  3  * telepathy.enchant.js
  4  * @version 0.1.0
  5  * @require enchant.js v0.8.0+
  6  * @author UEI Corporation
  7  */
  8 (function() {
  9 /**
 10  * @namespace enchant.telepathy
 11  */
 12 enchant.telepathy = {};
 13 
 14 /**
 15  * デフォルトの送信APIのURL.
 16  * @type String
 17  */
 18 enchant.telepathy.SEND_API_URL = 'http://skylab.enchantmoon.com/moonblock/add_data/';
 19 /**
 20  * デフォルトの受信APIのURL.
 21  * @type String
 22  */
 23 enchant.telepathy.RECV_API_URL = 'http://skylab.enchantmoon.com/moonblock/watch/';
 24 
 25 /**
 26  * オブジェクトににアプリケーション外からのメッセージ(テレパシー)を与えるイベント.
 27  * 発行するオブジェクト: {@link enchant.telepathy.TelepathySence}
 28  * @type String
 29  */
 30 enchant.Event.TELEPATHY = 'telepathy';
 31 
 32 /**
 33  * @scope enchant.telepathy.Telepathy.prototype
 34  */
 35 enchant.telepathy.Telepathy = enchant.Class.create(enchant.Event, {
 36     /**
 37      * @name enchant.telepathy.Telepathy
 38      * @class
 39      * アプリケーション外からのメッセージを表すイベントオブジェクト.
 40      * @param {String} channel チャンネル.
 41      * @param {*} data データ.
 42      * @constructs
 43      * @extends enchant.Event
 44      */
 45     initialize: function(channel, data) {
 46         enchant.Event.call(this, enchant.Event.TELEPATHY);
 47 
 48         /**
 49          * テレパシーのチャンネルを表す.
 50          * @type String
 51          */
 52         this.channel = channel;
 53         /**
 54          * テレパシーの内容を表す.
 55          * @type *
 56          */
 57         this.data = data;
 58     }
 59 });
 60 
 61 /**
 62  * @scope enchant.telepathy.TelepathySense.prototype
 63  */
 64 enchant.telepathy.TelepathySense = enchant.Class.create(enchant.EventTarget, {
 65     /**
 66      * @name enchant.telepathy.TelepathySense
 67      * @class
 68      * テレパシーの送受信を行うためのオブジェクト.
 69      * @construct
 70      * @extends enchant.EventTarget
 71      */
 72     initialize: function() {
 73         enchant.EventTarget.call(this);
 74 
 75         /**
 76          * Telepathyの受信者を保持するオブジェクト.
 77          * @type Object
 78          */
 79         this.channelers = {};
 80         /**
 81          * Telepathyの受信に使用するEventSourceを保持するオブジェクト.
 82          * @type Object
 83          */
 84         this.eventSources = {};
 85         /**
 86          * 最後に受信したTelepathyオブジェクト.
 87          * @type enchant.telepathy.Telepathy
 88          */
 89         this.lastTelepathy = null;
 90         /**
 91          * テレパシー送信APIのURL.
 92          * @type String
 93          */
 94         this.sendAPI = enchant.telepathy.SEND_API_URL;
 95         /**
 96          * テレパシー受信APIのURL.
 97          * @type String
 98          */
 99         this.recvAPI = enchant.telepathy.RECV_API_URL;
100 
101         this.addEventListener(enchant.Event.TELEPATHY, this._ontelepathy);
102     },
103     /**
104      * @param {enchant.Event} evt
105      * @private
106      */
107     _ontelepathy: function(evt) {
108         var i, l,
109             channelers = this.channelers[evt.channel];
110 
111         this.lastTelepathy = evt;
112 
113         if (!channelers) {
114             return;
115         }
116 
117         for (i = 0, l = channelers.length; i < l; i++) {
118             channelers[i].dispatchEvent(evt);
119         }
120     },
121     /**
122      * オブジェクトをTelepathyの受信者に設定する.
123      * @param {String} channel 対象のチャンネル.
124      * @param {enchant.EventTarget} target 対象のオブジェクト.
125      */
126     addChanneler: function(channel, target) {
127         if (this.channelers[channel]) {
128             if (this.channelers[channel].indexOf(target) === -1) {
129                 this.channelers[channel].push(target);
130             }
131         } else {
132             this.channelers[channel] = [ target ];
133         }
134     },
135     /**
136      * オブジェクトをTelepathyの受信者から除外する.
137      * @param {String} channel 対象のチャンネル.
138      * @param {enchant.EventTarget} target 対象のオブジェクト.
139      */
140     removeChanneler: function(channel, target) {
141         var i;
142 
143         if (!this.channelers[channel]) {
144             return;
145         }
146 
147         i = this.channelers[channel].indexOf(target);
148 
149         if (i !== -1) {
150             this.channelers[channel].splice(i, 1);
151         }
152     },
153     /**
154      * テレパシーを送信する.
155      * @param {String} channel 対象のチャンネル.
156      * @param {*} message 送信するデータ.
157      */
158     send: function(channel, message) {
159         var xhr = new XMLHttpRequest(),
160             data = JSON.stringify({ value: message });
161 
162         xhr.open('POST', this.sendAPI + channel, true);
163         xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
164         xhr.addEventListener('error', function(evt) {
165             var errorEvent = new enchant.Event(enchant.Event.ERROR);
166             errorEvent.message = 'Cannot send telepathy: ' + channel;
167             this.dispatchEvent(errorEvent);
168         }.bind(this));
169         xhr.send('object=' + data);
170     },
171     /**
172      * テレパシーの受信を開始する.
173      * @param {String} channel 対象のチャンネル.
174      */
175     open: function(channel) {
176         var eventSource = new EventSource(this.recvAPI + channel);
177 
178         eventSource.addEventListener('message', function(evt) {
179             var i, l,
180                 data = JSON.parse(evt.data),
181                 stream = data.objects;
182 
183             for (i = 0, l = stream.length; i < l; i++) {
184                 this.dispatchEvent(new enchant.telepathy.Telepathy(channel, JSON.parse(stream[i]).value));
185             }
186         }.bind(this));
187 
188         this.eventSources[channel] = eventSource;
189     },
190     /**
191      * テレパシーの受信を終了する.
192      * @param {String} channel 対象のチャンネル.
193      */
194     close: function(channel) {
195         var eventSource = this.eventSources[channel];
196 
197         if (eventSource) {
198             eventSource.close();
199             delete this.eventSources[channel];
200             delete this.channelers[channel];
201         }
202     },
203     /**
204      * 全てのチャンネルについてテレパシーの受信を終了する.
205      */
206     closeAll: function() {
207         for (var channel in this.eventSources) {
208             this.close(channel);
209         }
210     },
211     /**
212      * TelepathySenseの後処理を行う.
213      */
214     finalize: function() {
215         this.clearEventListener();
216         this.closeAll();
217         this.channelers = {};
218     }
219 });
220 
221 }());
222