Changeset View
Changeset View
Standalone View
Standalone View
webroot/rsrc/js/application/aphlict/Aphlict.js
/** | /** | ||||
* @provides javelin-aphlict | * @provides javelin-aphlict | ||||
* @requires javelin-install | * @requires javelin-install | ||||
* javelin-util | * javelin-util | ||||
* javelin-websocket | |||||
* javelin-leader | |||||
* javelin-json | |||||
*/ | */ | ||||
/** | /** | ||||
* Simple JS API for the Flash Aphlict client. Example usage: | * Client for the notification server. Example usage: | ||||
* | * | ||||
* var aphlict = new JX.Aphlict('aphlict_swf', '127.0.0.1', 22280) | * var aphlict = new JX.Aphlict('ws://localhost:22280', subscriptions) | ||||
* .setHandler(function(type, message) { | * .setHandler(function(message) { | ||||
* JX.log("Got " + type + " event!") | * // ... | ||||
* }) | * }) | ||||
* .start(); | * .start(); | ||||
* | * | ||||
* Your handler will receive these events: | |||||
* | |||||
* - `connect` The client initiated a connection to the server. | |||||
* - `connected` The client completed a connection to the server. | |||||
* - `close` The client disconnected from the server. | |||||
* - `error` There was an error. | |||||
* - `receive` Received a message from the server. | |||||
* | |||||
* You do not have to handle any of them in any specific way. | |||||
*/ | */ | ||||
JX.install('Aphlict', { | JX.install('Aphlict', { | ||||
construct: function(id, server, port, subscriptions) { | construct: function(uri, subscriptions) { | ||||
if (__DEV__) { | if (__DEV__) { | ||||
if (JX.Aphlict._instance) { | if (JX.Aphlict._instance) { | ||||
JX.$E('Aphlict object is a singleton.'); | JX.$E('Aphlict object is a singleton.'); | ||||
} | } | ||||
} | } | ||||
this._id = id; | this._uri = uri; | ||||
this._server = server; | |||||
this._port = port; | |||||
this._subscriptions = subscriptions; | this._subscriptions = subscriptions; | ||||
this._setStatus('setup'); | this._setStatus('setup'); | ||||
JX.Aphlict._instance = this; | JX.Aphlict._instance = this; | ||||
}, | }, | ||||
events: ['didChangeStatus'], | events: ['didChangeStatus'], | ||||
members: { | members: { | ||||
_id: null, | |||||
_server: null, | _server: null, | ||||
_port: null, | _port: null, | ||||
_subscriptions: null, | _subscriptions: null, | ||||
_status: null, | _status: null, | ||||
_statusCode: null, | _statusCode: null, | ||||
start: function(node, uri) { | start: function(node, uri) { | ||||
this._setStatus('start'); | JX.Leader.listen('onBecomeLeader', JX.bind(this, this._lead)); | ||||
JX.Leader.listen('onReceiveBroadcast', JX.bind(this, this._receive)); | |||||
JX.Leader.start(); | |||||
// NOTE: This is grotesque, but seems to work everywhere. | JX.Leader.call(JX.bind(this, this._begin)); | ||||
node.innerHTML = | |||||
'<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000">' + | |||||
'<param name="movie" value="' + uri + '" />' + | |||||
'<param name="allowScriptAccess" value="always" />' + | |||||
'<param name="wmode" value="opaque" />' + | |||||
'<embed src="' + uri + '" wmode="opaque"' + | |||||
'width="0" height="0" id="' + this._id + '">' + | |||||
'</embed>' + | |||||
'</object>'; | |||||
}, | |||||
_didStartFlash: function() { | |||||
var id = this._id; | |||||
// Flash puts its "objects" into global scope in an inconsistent way, | |||||
// because it was written in like 1816 when globals were awesome and IE4 | |||||
// didn't support other scopes since global scope is the best anyway. | |||||
var container = document[id] || window[id]; | |||||
this._flashContainer = container; | |||||
this._flashContainer.connect( | |||||
this._server, | |||||
this._port, | |||||
this._subscriptions); | |||||
}, | }, | ||||
getStatus: function() { | getStatus: function() { | ||||
return this._status; | return this._status; | ||||
}, | }, | ||||
getStatusCode: function() { | _begin: function() { | ||||
return this._statusCode; | JX.Leader.broadcast( | ||||
null, | |||||
{type: 'aphlict.getstatus'}); | |||||
JX.Leader.broadcast( | |||||
null, | |||||
{type: 'aphlict.subscribe', data: this._subscriptions}); | |||||
}, | |||||
_lead: function() { | |||||
var socket = new JX.WebSocket(this._uri); | |||||
socket.setOpenHandler(JX.bind(this, this._open)); | |||||
socket.setMessageHandler(JX.bind(this, this._message)); | |||||
socket.setCloseHandler(JX.bind(this, this._close)); | |||||
this._socket = socket; | |||||
socket.open(); | |||||
}, | |||||
_open: function() { | |||||
this._broadcastStatus('open'); | |||||
JX.Leader.broadcast(null, {type: 'aphlict.getsubscribers'}); | |||||
}, | |||||
_close: function() { | |||||
this._broadcastStatus('closed'); | |||||
}, | }, | ||||
_setStatus: function(status, code) { | _broadcastStatus: function(status) { | ||||
JX.Leader.broadcast(null, {type: 'aphlict.status', data: status}); | |||||
}, | |||||
_message: function(raw) { | |||||
var message = JX.JSON.parse(raw); | |||||
JX.Leader.broadcast(null, {type: 'aphlict.server', data: message}); | |||||
}, | |||||
_receive: function(message, is_leader) { | |||||
switch (message.type) { | |||||
case 'aphlict.status': | |||||
this._setStatus(message.data); | |||||
break; | |||||
case 'aphlict.getstatus': | |||||
if (is_leader) { | |||||
this._broadcastStatus(this.getStatus()); | |||||
} | |||||
break; | |||||
case 'aphlict.getsubscribers': | |||||
JX.Leader.broadcast( | |||||
null, | |||||
{type: 'aphlict.subscribe', data: this._subscriptions}); | |||||
break; | |||||
case 'aphlict.subscribe': | |||||
if (is_leader) { | |||||
this._write({ | |||||
command: 'subscribe', | |||||
data: message.data | |||||
}); | |||||
} | |||||
break; | |||||
case 'aphlict.server': | |||||
var handler = this.getHandler(); | |||||
handler && handler(message.data); | |||||
break; | |||||
} | |||||
}, | |||||
_setStatus: function(status) { | |||||
this._status = status; | this._status = status; | ||||
this._statusCode = code || null; | |||||
this.invoke('didChangeStatus'); | this.invoke('didChangeStatus'); | ||||
}, | |||||
_write: function(message) { | |||||
this._socket.send(JX.JSON.stringify(message)); | |||||
} | } | ||||
}, | }, | ||||
properties: { | properties: { | ||||
handler: null | handler: null | ||||
}, | }, | ||||
statics: { | statics: { | ||||
_instance: null, | _instance: null, | ||||
getInstance: function() { | getInstance: function() { | ||||
var self = JX.Aphlict; | var self = JX.Aphlict; | ||||
if (!self._instance) { | if (!self._instance) { | ||||
return null; | return null; | ||||
} | } | ||||
return self._instance; | return self._instance; | ||||
}, | |||||
didReceiveEvent: function(type, message) { | |||||
var client = JX.Aphlict.getInstance(); | |||||
if (!client) { | |||||
return; | |||||
} | } | ||||
if (type == 'status') { | |||||
client._setStatus(message.type, message.code); | |||||
switch (message.type) { | |||||
case 'ready': | |||||
client._didStartFlash(); | |||||
break; | |||||
} | |||||
} | |||||
var handler = client.getHandler(); | |||||
if (handler) { | |||||
handler(type, message); | |||||
} | |||||
} | |||||
} | } | ||||
}); | }); |