/* * Pushlet JS client library. * * $Id: js-pushlet-client.jsp,v 1.18 2005/03/07 16:26:11 justb Exp $ */ var flag = false; var pushletWebRoot='/gt'; var pushletURI; var pushletNetURI; var sessionId=null; var controlQueue = new Queue(20); var statusMsg='null'; var statusChanged=false; var statusChar = '|'; var pushletNet; var listenMode=null; var listenSubject=null; // Initialize various URLs _initURIs(); /************ Public application functions ******************/ // Embed pushlet frame in page function p_embed(thePushletWebRoot) { if (thePushletWebRoot) { // Use this when webapp is not in same server e.g. with virtual hosts pushletWebRoot = thePushletWebRoot; _initURIs(); } p_debug(flag, "p_embed", 'write ' + pushletLayer); var pushletLayer = ''; self.document.write(pushletLayer); _setStatus('initializing...'); _showStatus(); _waitForPushletFrame(); } // Join the pushlet server function p_join() { // Ignore (for now) if already joined if (sessionId != null) { return; } _setStatus('connecting...'); p_debug(flag, "p_join", 'joining..'); // Check if pushlet frame is loaded if (pushletNet) { // Pushlet iframe is ready for calls pushletNet.setControlURI(pushletURI + '?p_event=join'); } else { // Pushlet net iframe not loaded: continue waiting setTimeout("p_join()", 100); } } // Create data event channel with the server function p_listen(subject, mode) { // Optional initial subject to subscribe to if (subject) { // Remember listenSubject = subject; } // Optional mode (stream, pull, poll) i.s.o. default if (mode) { // Remember listenMode = mode; } // Loop forever as long not joined if (sessionId) { // ok we have joined // Create event URI for listen var uri = pushletURI + '?p_id=' + sessionId + '&p_event=listen'; // Optional subject to subscribe to if (listenSubject) { uri = uri + '&p_subject=' + listenSubject; } // Optional mode (stream, pull, poll) i.s.o. default if (listenMode) { uri = uri + '&p_mode=' + listenMode; } pushletNet.listen(uri); return; } // No join ack (sessionId) yet: wait until received setTimeout("p_listen()", 100); } // Shorthand: Join the pushlet server and start listening immediately function p_join_listen(subject, mode) { p_join(); p_listen(subject, mode); } // Leave the pushlet server function p_leave() { // Ignore (for now) if already left if (sessionId == null) { return; } var uri = pushletURI + '?p_event=leave'; p_debug(flag, 'p_leave', 'leave uri=' + uri); _sendControlURI(uri); } // Send heartbeat event; callback is onHeartbeatAck() function p_heartbeat() { var uri = pushletURI + '?p_event=hb'; p_debug(flag, 'p_heartbeat'); _sendControlURI(uri); } // Publish to a subject function p_publish(subject, nvPairs) { if (!subject) { return false; } var uri = pushletURI + '?p_event=publish&p_subject=' + subject; var args = p_publish.arguments; // Put the arguments' name/value pairs in the URI for (var i=1; i < args.length; i++) { uri = uri + '&' + args[i] + '=' + args[++i]; } p_debug(false, 'p_publish', 'publish uri=' + uri); _sendControlURI(uri); } // Subscribe to a subject with optional label function p_subscribe(subject, label) { var uri = pushletURI + '?p_event=subscribe&p_subject=' + subject; if (label) { uri = uri + '&p_label=' + label; } p_debug(flag, 'p_subscribe', 'subscribe uri=' + uri); _sendControlURI(uri); } // Unsubscribe from a subject function p_unsubscribe(subscriptionId) { var uri = pushletURI + '?p_event=unsubscribe'; if (subscriptionId) { uri = uri + '&p_sid=' + subscriptionId; } p_debug(flag, 'p_unsubscribe', 'unsubscribe uri=' + uri); _sendControlURI(uri); } // Get webroot for this webapp function p_getWebRoot() { return pushletWebRoot; } // Get pushlet session id function p_getSessionId() { return sessionId; } // Show debug window function p_setDebug(aFlag) { flag = aFlag; p_setNetDebug(aFlag); } // Show network debug window function p_setNetDebug(aFlag) { pushletNet.p_setDebug(aFlag); } /************ Private functions ******************/ /** CALLBACKS FROM pushletFrame ***/ // Generic callback from server; this function is called from within the // Pushlet subscriber frame (see frames below). function _push(args) { // Create a PushletEvent object from the arguments passed in // push.arguments is event data coming from the Server var event = new PushletEvent(args); p_debug(flag, '_push() from server: ', event.toString()); // Do action based on event type var eventType = event.getEvent(); if (eventType == 'data') { _setStatus('data'); _doCallback(event, window.onData); } else if (eventType == 'join-ack') { sessionId = event.get('p_id'); _setStatus('connected'); _doCallback(event, window.onJoinAck); } else if (eventType == 'listen-ack') { _setStatus('listening'); _doCallback(event, window.onListenAck); // Send empty heartbeat event. This // silences many busy browser windows. // At least in Moz and IE. p_heartbeat(); } else if (eventType == 'hb') { _setStatus('heartbeat'); _doCallback(event, window.onHeartbeat); } else if (eventType == 'hb-ack') { _doCallback(event, window.onHeartbeatAck); } else if (eventType == 'leave-ack') { sessionId = null; _setStatus('disconnected'); _doCallback(event, window.onLeaveAck); } else if (eventType == 'refresh-ack') { _doCallback(event, window.onRefreshAck); } else if (eventType == 'subscribe-ack') { _setStatus('subscribed to ' + event.get('p_subject')); _doCallback(event, window.onSubscribeAck); } else if (eventType == 'unsubscribe-ack') { _setStatus('unsubscribed'); _doCallback(event, window.onUnsubscribeAck); } else if (eventType == 'abort') { _setStatus('abort'); _doCallback(event, window.onAbort); } else if (eventType.match(/nack$/)) { _setStatus('error response: ' + event.get('p_reason')); _doCallback(event, window.onNack); } } function _initURIs() { pushletURI = pushletWebRoot + '/pushlet.srv'; pushletNetURI = pushletWebRoot + '/lib/js-pushlet-net.html'; } function _showStatus() { // To show progress if (statusChanged == true) { if (statusChar == '|') statusChar = '/'; else if (statusChar == '/') statusChar = '--'; else if (statusChar == '--') statusChar = '\\'; else statusChar = '|'; statusChanged = false; } window.defaultStatus = statusMsg; window.status = statusMsg + ' ' + statusChar; timeout = window.setTimeout('_showStatus()', 400); } function _setStatus(status) { statusMsg = "pushlet - " + status; statusChanged = true; } function _onUnload() { p_debug(true, "pushlet-lib", "_onUnload() called"); } function _onBeforeUnload() { p_debug(true, "pushlet-lib", "_onBeforeUnload() called"); } function _onStop() { p_debug(true, "pushlet-lib", "_onStop() called"); } function _doCallback(event, cbFunction) { // Do specific callback function if provided by client if (cbFunction) { // Do specific callback like onData(), onJoinAck() etc. cbFunction(event); } else if (window.onEvent) { // general callback onEvent() provided to catch all events onEvent(event); } } /** CALLS TO pushletFrame ***/ function _sendControlURI(uri) { if (controlQueue.isFull()) { // TODO divert to errpage alert('serious problem: control queue is full'); // no sense going on return; } if (sessionId == null) { controlQueue.enqueue(uri); _processControlQueue(); return; } // All clear to send immediately ? if (controlQueue.isEmpty()) { if (pushletNet.isControlReady()) { // Ok send direct uri = uri + '&p_id=' + sessionId; pushletNet.setControlURI(uri); } else { controlQueue.enqueue(uri); } } else { // Queue not empty controlQueue.enqueue(uri); } if (!controlQueue.isEmpty()) { _processControlQueue(); } } function _processControlQueue() { if (controlQueue.isEmpty()) { // all done return; } if (sessionId != null) { // Dequeue next control URI if pushletFrame ready if (pushletNet.isControlReady()) { var uri = controlQueue.dequeue() + '&p_id=' + sessionId; pushletNet.setControlURI(uri); } } // Loop forever as long queue is not empty if (!controlQueue.isEmpty()) { setTimeout("_processControlQueue()", 50); } } function _waitForPushletFrame() { // Loop forever as long net uri not ready if (self.pushletFrame && self.pushletFrame.isLoaded && self.pushletFrame.isLoaded()) { _setStatus('loaded pushlet frame...'); pushletNet = self.pushletFrame; return; } _setStatus('pushlet frame not ready'); setTimeout("_waitForPushletFrame()", 20); } /************** Util classes *******************************/ /** NV pair object */ function NameValuePair(name, value) { this.name = name; this.value = value; } /** Simple Map object to store array of name/value pairs */ function Map() { // Data members this.index = 0; this.map = new Array(); // Function members this.get = MapGet; this.put = MapPut; this.toString = MapToString; this.toTable = MapToTable; } /** get() */ function MapGet(name) { for (var i=0; i < this.index; i++) { if (this.map[i].name == name) { return this.map[i].value; } } return ''; } /** put() */ function MapPut(name, value) { this.map[this.index++] = new NameValuePair(name, value); } /** To HTML string */ function MapToString() { var res = ''; for (var i=0; i < this.index; i++) { res = res + this.map[i].name+'='+this.map[i].value+'\n'; } return res; } /** To HTML table */ function MapToTable() { var res = '
| '+styleDiv+this.map[i].name+' | '+styleDiv+this.map[i].value+' |
' + i + ': ' + messages[i] + ''); } // Write doc footer and close debugWindow.document.writeln(''); debugWindow.document.close(); debugWindow.focus(); }