/** * This program is distributed under the terms of the MIT license. * Please see the LICENSE file for details. * * Copyright 2008 Michael Garvin */ var TROPHYIM_BOSH_SERVICE = "/proxy/xmpp-httpbind"; //Change to suit var TROPHYIM_LOG_LINES = 200; var TROPHYIM_LOGLEVEL = 0; //0=debug, 1=info, 2=warn, 3=error, 4=fatal var TROPHYIM_VERSION = "0.3"; var TROPHYIM_RESOURCE = "/JABBERITWEB"; //Uncomment to make session reattachment work //var TROPHYIM_JSON_STORE = "json_store.php"; /** Object: DOMObjects * This class contains builders for all the DOM objects needed by TrophyIM */ DOMObjects = { /** Function: xmlParse * Cross-browser alternative to using innerHTML * Parses given string, returns valid DOM HTML object * * Parameters: * (String) xml - the xml string to parse */ xmlParse : function(xmlString) { var xmlObj = this.xmlRender(xmlString); if(xmlObj) { try { //Firefox, Gecko, etc if (this.processor == undefined) { this.processor = new XSLTProcessor(); this.processor.importStylesheet(this.xmlRender( '\ \ ')); } var htmlObj = this.processor.transformToDocument(xmlObj).documentElement; //Safari has a quirk where it wraps dom elements in if (htmlObj.tagName.toLowerCase() == 'html') { htmlObj = htmlObj.firstChild.firstChild; } return document.importNode(htmlObj, true); } catch(e) { try { //IE is so very very special var htmlObj = document.importNode(xmlObj.documentElement, true); if (htmlObj.tagName.toLowerCase() == "div") { var div_wrapper = document.createElement('div'); div_wrapper.appendChild(htmlObj); if(div_wrapper.innerHTML) { div_wrapper.innerHTML = div_wrapper.innerHTML; } htmlObj = div_wrapper.firstChild; } return htmlObj; } catch(e) { alert( "TrophyIM Error: Cannot add html to page" + e.message); } } } }, /** Function: xmlRender * Uses browser-specific methods to turn given string into xml object * * Parameters: * (String) xml - the xml string to parse */ xmlRender : function(xmlString) { try {//IE var renderObj = new ActiveXObject("Microsoft.XMLDOM"); renderObj.async="false"; if(xmlString) { renderObj.loadXML(xmlString); } } catch (e) { try { //Firefox, Gecko, etc if (this.parser == undefined) { this.parser = new DOMParser(); } var renderObj = this.parser.parseFromString(xmlString, "application/xml"); } catch(e) { alert("TrophyIM Error: Cannot create new html for page"); } } return renderObj; }, /** Function: getHTML * Returns named HTML snippet as DOM object * * Parameters: * (String) name - name of HTML snippet to retrieve (see HTMLSnippets * object) */ getHTML : function(page) { return this.xmlParse(HTMLSnippets[page]); }, /** Function: getScript * Returns script object with src to given script * * Parameters: * (String) script - name of script to put in src attribute of script * element */ getScript : function(script) { var newscript = document.createElement('script'); newscript.setAttribute('src', script); newscript.setAttribute('type', 'text/javascript'); return newscript; } }; /** Object: TrophyIM * * This is the actual TrophyIM application. It searches for the * 'trophyimclient' element and inserts itself into that. */ TrophyIM = { statusConn : { connected : false }, /** Constants: * * (Boolean) stale_roster - roster is stale and needs to be rewritten. */ constants : {stale_roster: false}, /** Object: chatHistory * * Stores chat history (last 10 message) and current presence of active * chat tabs. Indexed by jid. */ chatHistory : {}, /** Object: activeChats * * This object stores the currently active chats. */ activeChats : {current: null, divs: {}}, /** Function: setCookie * * Sets cookie name/value pair. Date and path are auto-selected. * * Parameters: * (String) name - the name of the cookie variable * (String) value - the value of the cookie variable */ setCookie : function(name, value) { var expire = new Date(); expire.setDate(expire.getDate() + 365); document.cookie = name + "=" + value + "; expires=" + expire.toGMTString(); }, /** Function: delCookie * * Deletes cookie * * Parameters: * (String) name) - the name of the cookie to delete */ delCookie : function(name) { var expire = new Date(); expire.setDate(expire.getDate() - 365); document.cookie = name + "= ; expires=" + expire.toGMTString(); delete TrophyIM.cookies[name]; }, /** Function: getCookies * * Retrieves all trophyim cookies into an indexed object. Inteneded to be * called once, at which time the app refers to the returned object instead * of re-parsing the cookie string every time. * * Each cookie is also re-applied so as to refresh the expiry date. */ getCookies : function() { var cObj = {}; var cookies = document.cookie.split(';'); for (var c in cookies) { while (cookies[c].charAt(0)==' ') { cookies[c] = cookies[c].substring(1,cookies[c].length); } if (cookies[c].substr(0, 8) == "trophyim") { var nvpair = cookies[c].split("=", 2); cObj[nvpair[0]] = nvpair[1]; TrophyIM.setCookie(nvpair[0], nvpair[1]); } } return cObj; }, /** Function: load * * This function searches for the trophyimclient div and loads the client * into it. */ load : function() { if (!TrophyIM.statusConn.connected) { TrophyIM.cookies = TrophyIM.getCookies(); //Load other .js scripts needed document.getElementsByTagName('head')[0].appendChild(DOMObjects.getScript(path_jabberit + 'strophejs/strophe.js')); document.getElementsByTagName('head')[0].appendChild(DOMObjects.getScript(path_jabberit + 'strophejs/md5.js')); document.getElementsByTagName('head')[0].appendChild(DOMObjects.getScript(path_jabberit + 'strophejs/sha1.js')); document.getElementsByTagName('head')[0].appendChild(DOMObjects.getScript(path_jabberit + 'strophejs/b64.js')); document.getElementsByTagName('head')[0].appendChild(DOMObjects.getScript(path_jabberit + 'js/json2.js')); //Keep this script last //Wait a second to give scripts time to load setTimeout("TrophyIM.showLogin()", 500); } else { loadIM.rosterDiv(); } }, /** Function: storeData * * Store all our data in the JSONStore, if it is active */ storeData : function() { if ( TrophyIM.connection && TrophyIM.connection.connected ) { TrophyIM.setCookie('trophyim_bosh_xid', TrophyIM.connection.jid + "|" + TrophyIM.connection.sid + "|" + TrophyIM.connection.rid); TrophyIM.rosterObj.save(); } }, /** Function: showlogin * * This function clears out the IM box and either redisplays the login * page, or re-attaches to Strophe, preserving the logging div if it * exists, or creating a new one of we are re-attaching. */ showLogin : function() { //JSON is the last script to load, so we wait on it //Added Strophe check too because of bug where it's sometimes missing if (typeof(JSON) != undefined && typeof(Strophe) != undefined) { TrophyIM.JSONStore = new TrophyIMJSONStore(); if ( TrophyIM.JSONStore.store_working && TrophyIM.cookies['trophyim_bosh_xid'] ) { var xids = TrophyIM.cookies['trophyim_bosh_xid'].split("|"); TrophyIM.delCookie('trophyim_bosh_xid'); TrophyIM.constants.stale_roster = true; TrophyIM.connection = new Strophe.Connection(TROPHYIM_BOSH_SERVICE); TrophyIM.connection.rawInput = TrophyIM.rawInput; TrophyIM.connection.rawOutput = TrophyIM.rawOutput; Strophe.log = TrophyIM.log; Strophe.info('Attempting Strophe attach.'); TrophyIM.connection.attach(xids[0], xids[1], xids[2], TrophyIM.onConnect); TrophyIM.onConnect(Strophe.Status.CONNECTED); } else { // List Contact loadIM.rosterDiv(); if ( loadIM.getUserCurrent() != null ) { TrophyIM.login( loadIM.getUserCurrent().jid, loadIM.getUserCurrent().password ); } else { loadIM.loginPage(); } } } else { setTimeout("TrophyIM.showLogin()", 500); } }, /** Function: log * * This function logs the given message in the trophyimlog div * * Parameter: (String) msg - the message to log */ log : function(level, msg) { if (TrophyIM.logging_div && level >= TROPHYIM_LOGLEVEL) { while(TrophyIM.logging_div.childNodes.length > TROPHYIM_LOG_LINES) { TrophyIM.logging_div.removeChild( TrophyIM.logging_div.firstChild); } var msg_div = document.createElement('div'); msg_div.className = 'trophyimlogitem'; msg_div.appendChild(document.createTextNode(msg)); TrophyIM.logging_div.appendChild(msg_div); TrophyIM.logging_div.scrollTop = TrophyIM.logging_div.scrollHeight; } }, /** Function: rawInput * * This logs the packets actually recieved by strophe at the debug level */ rawInput : function (data) { Strophe.debug("RECV: " + data); }, /** Function: rawInput * * This logs the packets actually recieved by strophe at the debug level */ rawOutput : function (data) { Strophe.debug("SEND: " + data); }, /** Function: login * * This function logs into server using information given on login page. * Since the login page is where the logging checkbox is, it makes or * removes the logging div and cookie accordingly. * */ login : function() { if (TrophyIM.JSONStore.store_working) { //In case they never logged out TrophyIM.JSONStore.delData(['groups','roster', 'active_chat', 'chat_history']); } TrophyIM.connection = new Strophe.Connection(TROPHYIM_BOSH_SERVICE); TrophyIM.connection.rawInput = TrophyIM.rawInput; TrophyIM.connection.rawOutput = TrophyIM.rawOutput; Strophe.log = TrophyIM.log; if ( arguments.length > 0 ) { var barejid = arguments[0]; var password = arguments[1]; TrophyIM.connection.connect(barejid + TROPHYIM_RESOURCE, password, TrophyIM.onConnect); } else { var barejid = document.getElementById('trophyimjid').value var fulljid = barejid + TROPHYIM_RESOURCE; var password = document.getElementById('trophyimpass').value; var button = document.getElementById('trophyimconnect'); loadIM.setUserCurrent( barejid, password); if ( button.value == 'connect' ) { button.value = 'disconnect'; TrophyIM.connection.connect(fulljid, password, TrophyIM.onConnect); } else { button.value = 'connect'; TrophyIM.connection.disconnect(); } } TrophyIM.setCookie('trophyimjid', barejid); }, /** Function: logout * * Logs into fresh session through Strophe, purging any old data. */ logout : function() { TrophyIM.delCookie('trophyim_bosh_xid'); delete TrophyIM['cookies']['trophyim_bosh_xid']; if (TrophyIM.JSONStore.store_working) { TrophyIM.JSONStore.delData(['groups','roster', 'active_chat', 'chat_history']); } for (var chat in TrophyIM.activeChats['divs']) { delete TrophyIM.activeChats['divs'][chat]; } TrophyIM.activeChats = {current: null, divs: {}}, TrophyIM.connection.disconnect(); TrophyIM.showLogin(); }, /** Function onConnect * * Callback given to Strophe upon connection to BOSH proxy. */ onConnect : function(status) { if (status == Strophe.Status.CONNECTING) { Strophe.info('Strophe is connecting.'); } if (status == Strophe.Status.CONNFAIL) { Strophe.info('Strophe failed to connect.'); TrophyIM.delCookie('trophyim_bosh_xid'); TrophyIM.showLogin(); } if (status == Strophe.Status.DISCONNECTING) { Strophe.info('Strophe is disconnecting.'); } if (status == Strophe.Status.DISCONNECTED) { Strophe.info('Strophe is disconnected.'); TrophyIM.delCookie('trophyim_bosh_xid'); TrophyIM.showLogin(); } if (status == Strophe.Status.CONNECTED) { loadIM.setStatusJabber('Disponível','available'); TrophyIM.statusConn.connected = true; TrophyIM.showClient(); Strophe.info('Strophe is connected.'); } }, /** Function: showClient * * This clears out the main div and puts in the main client. It also * registers all the handlers for Strophe to call in the client. */ showClient : function() { TrophyIM.setCookie('trophyim_bosh_xid', TrophyIM.connection.jid + "|" + TrophyIM.connection.sid + "|" + TrophyIM.connection.rid); TrophyIM.rosterObj = new TrophyIMRoster(); TrophyIM.connection.addHandler(TrophyIM.onVersion, Strophe.NS.VERSION, 'iq', null, null, null); TrophyIM.connection.addHandler(TrophyIM.onRoster, Strophe.NS.ROSTER, 'iq', null, null, null); TrophyIM.connection.addHandler(TrophyIM.onPresence, null, 'presence', null, null, null); TrophyIM.connection.addHandler(TrophyIM.onMessage, null, 'message', null, null, null); //Get roster then announce presence. TrophyIM.connection.send($iq({type: 'get', xmlns: Strophe.NS.CLIENT}).c('query', {xmlns: Strophe.NS.ROSTER}).tree()); TrophyIM.connection.send($pres().tree()); setTimeout("TrophyIM.renderRoster()", 1000); }, /** Function: clearClient * * Clears out client div, preserving and returning existing logging_div if * one exists */ clearClient : function() { if(TrophyIM.logging_div) { var logging_div = TrophyIM.client_div.removeChild( document.getElementById('trophyimlog')); } else { var logging_div = null; } while(TrophyIM.client_div.childNodes.length > 0) { TrophyIM.client_div.removeChild(TrophyIM.client_div.firstChild); } return logging_div; }, /** Function: onVersion * * jabber:iq:version query handler */ onVersion : function(msg) { Strophe.debug("Version handler"); if (msg.getAttribute('type') == 'get') { var from = msg.getAttribute('from'); var to = msg.getAttribute('to'); var id = msg.getAttribute('id'); var reply = $iq({type: 'result', to: from, from: to, id: id}).c('query', {name: "TrophyIM", version: TROPHYIM_VERSION, os: "Javascript-capable browser"}); TrophyIM.connection.send(reply.tree()); } return true; }, /** Function: onRoster * * Roster iq handler */ onRoster : function(msg) { //Strophe.debug("Roster handler"); var roster_items = msg.firstChild.getElementsByTagName('item'); for (var i = 0; i < roster_items.length; i++) { var groups = roster_items[i].getElementsByTagName('group'); var group_array = new Array(); for (var g = 0; g < groups.length; g++) { group_array[group_array.length] = groups[g].firstChild.nodeValue; } with ( roster_items[i] ) { TrophyIM.rosterObj.addContact(getAttribute('jid'), getAttribute('subscription'), getAttribute('name'), group_array); } } if ( msg.getAttribute('type') == 'set' ) { TrophyIM.connection.send($iq({type: 'reply', id: msg.getAttribute('id'), to: msg.getAttribute('from')}).tree()); } return true; }, /** Function: onPresence * * Presence handler */ onPresence : function(msg) { Strophe.debug("Presence handler"); var type = msg.getAttribute('type') ? msg.getAttribute('type') : 'available'; var show = msg.getElementsByTagName('show').length ? Strophe.getText(msg.getElementsByTagName('show')[0]) : type; var status = msg.getElementsByTagName('status').length ? Strophe.getText(msg.getElementsByTagName('status')[0]) : ''; var priority = msg.getElementsByTagName('priority').length ? parseInt(Strophe.getText(msg.getElementsByTagName('priority')[0])) : 0; TrophyIM.rosterObj.setPresence(msg.getAttribute('from'), priority, show, status); return true; }, /** Function: onMessage * * Message handler */ onMessage : function(msg) { Strophe.debug("Message handler"); var from = msg.getAttribute('from'); var type = msg.getAttribute('type'); var elems = msg.getElementsByTagName('body'); if ( (type == 'chat' || type == 'normal') && elems.length > 0 ) { var barejid = Strophe.getBareJidFromJid(from); var jid_lower = barejid.toLowerCase(); var contact = ""; if( TrophyIM.rosterObj.roster[barejid.toLowerCase()]['contact']['name'] ) { contact = TrophyIM.rosterObj.roster[barejid.toLowerCase()]['contact']['name']; } else { contact = barejid.toLowerCase(); contact = contact.substring(0, contact.indexOf('@')); } var message = { contact : "" + contact + "", msg : Strophe.getText(elems[0]) }; TrophyIM.makeChat(from); //Make sure we have a chat window TrophyIM.addMessage(message, jid_lower); } return true; }, /** Function: makeChat * * Make sure chat window to given fulljid exists, switching chat context to * given resource */ makeChat : function(fulljid) { var barjid = Strophe.getBareJidFromJid(fulljid); var paramsChatBox = { 'idChatBox' : barjid + "__chatBox", 'jidTo' : barjid, 'path_jabberit' : path_jabberit }; var winChatBox = { id_window : "window_chat_area_" + barjid, width : 387, height : 365, top : 100, left : 400, draggable : true, visible : "display", resizable : true, zindex : loadIM.getZIndex(), title : barjid.substring(0, barjid.indexOf('@')), closeAction : "hidden", content : loadIM.parse("chat_box","chatBox.xsl", paramsChatBox) } _winBuild(winChatBox) loadIM.configEvents( document.getElementById( barjid + '__sendBox'), 'onkeyup', function(e) { if( e.keyCode == 13 ) { TrophyIM.sendMessage( barjid ); document.getElementById( barjid + '__sendBox').value = ''; return false; } } ); }, /** Function addContacts * * Parameters: * (string) jid * (string) name * (string) group */ addContacts : function( jidFrom, jidTo, name, group ) { var newPresence = $pres({from: jidFrom, to: jidTo, type: 'subscribe'}).tree(); TrophyIM.connection.send(newPresence); var newContact = $iq({type: 'set', id: 'set1'}); newContact = newContact.c('query').attrs({xmlns : 'jabber:iq:roster'}); newContact = newContact.c('item').attrs({jid: jid, name: name, ask:'subscribe', subscription :'none'}); newContact = newContact.c('group').t(group).tree(); TrophyIM.connection.send(newContact); }, /** Function: addMessage * * Parameters: * (string) msg - the message to add * (string) jid - the jid of chat box to add the message to. */ addMessage : function(msg, jid) { var chatBox = document.getElementById(jid + "__chatBox"); var messageDiv = document.createElement("div"); messageDiv.style.margin = "3px 0px 3px 3px"; messageDiv.innerHTML = msg.contact + " : " + msg.msg ; chatBox.appendChild(messageDiv); chatBox.scrollTop = chatBox.scrollHeight; }, /** Function: renderRoster * * Renders roster, looking only for jids flagged by setPresence as having * changed. */ renderRoster : function() { if( TrophyIM.rosterObj.changes.length > 0 ) { var roster_div = document.getElementById('JabberIMRoster'); if( roster_div ) { var users = new Array(); for( var user in TrophyIM.rosterObj.roster ) { users[users.length] = TrophyIM.rosterObj.roster[user].contact.jid; } users.sort(); var groups = new Array(); for (var group in TrophyIM.rosterObj.groups) { if( group ) groups[groups.length] = group; } groups.sort(); for ( var i = 0; i < groups.length; i++ ) { TrophyIM.renderGroups( groups[i] , roster_div.lastChild ); } TrophyIM.renderItensGroup(users, roster_div); } } setTimeout("TrophyIM.renderRoster()", 1000 ); }, /** Function: renderGroups * * */ renderGroups: function( nameGroup, element ) { var _addGroup = function() { var paramsGroup = { 'nameGroup' : arguments[0], 'path_jabberit' : path_jabberit } element.innerHTML += loadIM.parse("group","groups.xsl", paramsGroup); } if( !element.hasChildNodes() ) { _addGroup(nameGroup); } else { var elementChild = element; var flag = false; while ( elementChild ) { if ( elementChild.childNodes[0].childNodes[0].firstChild.nodeValue === nameGroup ) { flag = true; } elementChild = elementChild.nextSibling; } if( !flag ) _addGroup( nameGroup ); } }, /** Function: renderItensGroup * * */ renderItensGroup : function( users, element ) { var addItemGroup = function() { if( arguments.length > 0 ) { var objContact = arguments[0]; var element = arguments[1]; var itensJid = document.getElementById( 'itenContact_' + objContact.contact.jid ); if( itensJid == null ) { // Name var nameContact = ""; if ( objContact.contact.name ) nameContact = objContact.contact.name; else { nameContact = objContact.contact.jid; nameContact = nameContact.substring(0, nameContact.indexOf('@')); } // Presence e Status var presence = "unavailable"; var status = ""; var statusDisplay = "none"; if (objContact.presence) { for (var resource in objContact.presence) { presence = objContact.presence[resource].show; if( objContact.presence[resource].status ) { status = " ( " + objContact.presence[resource].status + " ) "; statusDisplay = "block"; } } } var paramsContact = { 'nameContact' : nameContact, 'jid' : objContact.contact.jid, 'id' : 'itenContact_' + objContact.contact.jid, 'presence' : presence, 'path_jabberit' : path_jabberit, 'status' : status, 'statusDisplay' : statusDisplay } if( element.id == "rosterIM_with_groups" ) { var elementChild = element; while ( elementChild ) { if( elementChild.childNodes[0].childNodes[0].firstChild.nodeValue == objContact.contact.groups[0] ) elementChild.childNodes[0].innerHTML += loadIM.parse("itens_group", "itensGroup.xsl", paramsContact); elementChild = elementChild.nextSibling; } } else { element.innerHTML += loadIM.parse("itens_group", "itensGroup.xsl", paramsContact); } } else { // Presence e Status var presence = "unavailable"; var status = ""; var statusDisplay = "none"; if (objContact.presence) { for (var resource in objContact.presence) { presence = objContact.presence[resource].show; if( objContact.presence[resource].status ) { status = objContact.presence[resource].status; statusDisplay = "block"; } } } itensJid.style.background = "url('"+path_jabberit+"templates/default/images/" + presence + ".gif')no-repeat center left"; itensJid.lastChild.style.display = statusDisplay; itensJid.lastChild.innerHTML = " ( " + status + " ) "; } } } for( var i = 0 ; i < users.length; i++ ) { if( TrophyIM.rosterObj.roster[users[i]].contact.groups ) if( TrophyIM.rosterObj.roster[users[i]].contact.groups[0] ) addItemGroup(TrophyIM.rosterObj.roster[users[i]], element.lastChild ); else addItemGroup(TrophyIM.rosterObj.roster[users[i]], element.firstChild ); else addItemGroup(TrophyIM.rosterObj.roster[users[i]], element.firstChild ); } }, /** Function: rosterClick * * Handles actions when a roster item is clicked */ rosterClick : function(fulljid) { TrophyIM.makeChat(fulljid); }, /** Function SetAutorization * */ setAutorization : function( jid ) { // //var _autorization = $pres( ).attrs( {to: jid, from: loadIM.getUserCurrent(), type:'subscribed'}).tree(); TrophyIM.connection.send($pres( ).attrs( {to: jid, from: loadIM.getUserCurrent().jid, type:'subscribed'}).tree()); }, /** Function: setPresence * */ setPresence : function( show ) { TrophyIM.connection.send($pres( ).c('show').t(show).tree()); }, /** Function: sendMessage * * Send message from chat input to user */ sendMessage : function() { if( arguments.length > 0 ) { var jidTo = arguments[0]; var message_input = document.getElementById(jidTo + "__sendBox").value; if( ( message_input = message_input.replace(/^\s+|\s+$|^\n|\n$/g,"") ) != "" ) { // Send Message TrophyIM.connection.send($msg({to: jidTo, from: TrophyIM.connection.jid, type: 'chat'}).c('body').t(message_input).tree()); var message = { contact : "" + "Eu" + "", msg : message_input } // Add Message in chatBox; TrophyIM.addMessage( message, jidTo); document.getElementById(jidTo + "__sendBox").value = ""; document.getElementById(jidTo + "__sendBox").focus(); } } } }; /** Class: TrophyIMRoster * * * This object stores the roster and presence info for the TrophyIMClient * * roster[jid_lower]['contact'] * roster[jid_lower]['presence'][resource] */ function TrophyIMRoster() { /** Constants: internal arrays * (Object) roster - the actual roster/presence information * (Object) groups - list of current groups in the roster * (Array) changes - array of jids with presence changes */ if (TrophyIM.JSONStore.store_working) { var data = TrophyIM.JSONStore.getData(['roster', 'groups']); this.roster = (data['roster'] != null) ? data['roster'] : {}; this.groups = (data['groups'] != null) ? data['groups'] : {}; } else { this.roster = {}; this.groups = {}; } this.changes = new Array(); if (TrophyIM.constants.stale_roster) { for (var jid in this.roster) { this.changes[this.changes.length] = jid; } } /** Function: addContact * * Adds given contact to roster * * Parameters: * (String) jid - bare jid * (String) subscription - subscription attribute for contact * (String) name - name attribute for contact * (Array) groups - array of groups contact is member of */ this.addContact = function(jid, subscription, name, groups) { var contact = { jid:jid, subscription:subscription, name:name, groups:groups } var jid_lower = jid.toLowerCase(); if ( this.roster[jid_lower] ) { this.roster[jid_lower]['contact'] = contact; } else { this.roster[jid_lower] = {contact:contact}; } groups = groups ? groups : ['']; for ( var g = 0; g < groups.length; g++ ) { if ( !this.groups[groups[g]] ) { this.groups[groups[g]] = {}; } this.groups[groups[g]][jid_lower] = jid_lower; } } /** Function: getContact * * Returns contact entry for given jid * * Parameter: (String) jid - jid to return */ this.getContact = function(jid) { if (this.roster[jid.toLowerCase()]) { return this.roster[jid.toLowerCase()]['contact']; } } /** Function: setPresence * * Sets presence * * Parameters: * (String) fulljid: full jid with presence * (Integer) priority: priority attribute from presence * (String) show: show attribute from presence * (String) status: status attribute from presence */ this.setPresence = function(fulljid, priority, show, status) { var barejid = Strophe.getBareJidFromJid(fulljid); var resource = Strophe.getResourceFromJid(fulljid); var jid_lower = barejid.toLowerCase(); if( show != 'unavailable') { if (!this.roster[jid_lower]) { this.addContact(barejid, 'not-in-roster'); } var presence = { resource:resource, priority:priority, show:show, status:status } if (!this.roster[jid_lower]['presence']) { this.roster[jid_lower]['presence'] = {} } this.roster[jid_lower]['presence'][resource] = presence } else if (this.roster[jid_lower] && this.roster[jid_lower]['presence'] && this.roster[jid_lower]['presence'][resource]) { delete this.roster[jid_lower]['presence'][resource]; } this.addChange(jid_lower); if (TrophyIM.activeChats['divs'][jid_lower]) { TrophyIM.setTabPresence(jid_lower, TrophyIM.activeChats['divs'][jid_lower]['tab']); } } /** Function: addChange * * Adds given jid to this.changes, keeping this.changes sorted and * preventing duplicates. * * Parameters * (String) jid : jid to add to this.changes */ this.addChange = function(jid) { for (var c = 0; c < this.changes.length; c++) { if (this.changes[c] == jid) { return; } } this.changes[this.changes.length] = jid; this.changes.sort(); } /** Function: getPresence * * Returns best presence for given jid as Array(resource, priority, show, * status) * * Parameter: (String) fulljid - jid to return best presence for */ this.getPresence = function(fulljid) { var jid = Strophe.getBareJidFromJid(fulljid); var current = null; if (this.roster[jid.toLowerCase()] && this.roster[jid.toLowerCase()]['presence']) { for (var resource in this.roster[jid.toLowerCase()]['presence']) { var presence = this.roster[jid.toLowerCase()]['presence'][resource]; if (current == null) { current = presence } else { if(presence['priority'] > current['priority'] && ((presence['show'] == "chat" || presence['show'] == "available") || (current['show'] != "chat" || current['show'] != "available"))) { current = presence } } } } return current; } /** Function: groupHasChanges * * Returns true if current group has members in this.changes * * Parameters: * (String) group - name of group to check */ this.groupHasChanges = function(group) { for (var c = 0; c < this.changes.length; c++) { if (this.groups[group][this.changes[c]]) { return true; } } return false; } /** Fuction: save * * Saves roster data to JSON store */ this.save = function() { if (TrophyIM.JSONStore.store_working) { TrophyIM.JSONStore.setData({roster:this.roster, groups:this.groups, active_chat:TrophyIM.activeChats['current'], chat_history:TrophyIM.chatHistory}); } } } /** Class: TrophyIMJSONStore * * * This object is the mechanism by which TrophyIM stores and retrieves its * variables from the url provided by TROPHYIM_JSON_STORE * */ function TrophyIMJSONStore() { this.store_working = false; /** Function _newXHR * * Set up new cross-browser xmlhttprequest object * * Parameters: * (function) handler = what to set onreadystatechange to */ this._newXHR = function (handler) { var xhr = null; if (window.XMLHttpRequest) { xhr = new XMLHttpRequest(); if (xhr.overrideMimeType) { xhr.overrideMimeType("text/xml"); } } else if (window.ActiveXObject) { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } return xhr; } /** Function getData * Gets data from JSONStore * * Parameters: * (Array) vars = Variables to get from JSON store * * Returns: * Object with variables indexed by names given in parameter 'vars' */ this.getData = function(vars) { if (typeof(TROPHYIM_JSON_STORE) != undefined) { Strophe.debug("Retrieving JSONStore data"); var xhr = this._newXHR(); var getdata = "get=" + vars.join(","); try { xhr.open("POST", TROPHYIM_JSON_STORE, false); } catch (e) { Strophe.error("JSONStore open failed."); return false; } xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xhr.setRequestHeader('Content-length', getdata.length); xhr.send(getdata); if (xhr.readyState == 4 && xhr.status == 200) { try { var dataObj = JSON.parse(xhr.responseText); return this.emptyFix(dataObj); } catch(e) { Strophe.error("Could not parse JSONStore response" + xhr.responseText); return false; } } else { Strophe.error("JSONStore open failed. Status: " + xhr.status); return false; } } } /** Function emptyFix * Fix for bugs in external JSON implementations such as * http://bugs.php.net/bug.php?id=41504. * A.K.A. Don't use PHP, people. */ this.emptyFix = function(obj) { if (typeof(obj) == "object") { for (var i in obj) { if (i == '_empty_') { obj[""] = this.emptyFix(obj['_empty_']); delete obj['_empty_']; } else { obj[i] = this.emptyFix(obj[i]); } } } return obj } /** Function delData * Deletes data from JSONStore * * Parameters: * (Array) vars = Variables to delete from JSON store * * Returns: * Status of delete attempt. */ this.delData = function(vars) { if (typeof(TROPHYIM_JSON_STORE) != undefined) { Strophe.debug("Retrieving JSONStore data"); var xhr = this._newXHR(); var deldata = "del=" + vars.join(","); try { xhr.open("POST", TROPHYIM_JSON_STORE, false); } catch (e) { Strophe.error("JSONStore open failed."); return false; } xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xhr.setRequestHeader('Content-length', deldata.length); xhr.send(deldata); if (xhr.readyState == 4 && xhr.status == 200) { try { var dataObj = JSON.parse(xhr.responseText); return dataObj; } catch(e) { Strophe.error("Could not parse JSONStore response"); return false; } } else { Strophe.error("JSONStore open failed. Status: " + xhr.status); return false; } } } /** Function setData * Stores data in JSONStore, overwriting values if they exist * * Parameters: * (Object) vars : Object containing named vars to store ({name: value, * othername: othervalue}) * * Returns: * Status of storage attempt */ this.setData = function(vars) { if (typeof(TROPHYIM_JSON_STORE) != undefined) { Strophe.debug("Storing JSONStore data"); var senddata = "set=" + JSON.stringify(vars); var xhr = this._newXHR(); try { xhr.open("POST", TROPHYIM_JSON_STORE, false); } catch (e) { Strophe.error("JSONStore open failed."); return false; } xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xhr.setRequestHeader('Content-length', senddata.length); xhr.send(senddata); if (xhr.readyState == 4 && xhr.status == 200 && xhr.responseText == "OK") { return true; } else { Strophe.error("JSONStore open failed. Status: " + xhr.status); return false; } } } var testData = true; if (this.setData({testData:testData})) { var testResult = this.getData(['testData']); if (testResult && testResult['testData'] == true) { this.store_working = true; } } } /** Constants: Node types * * Implementations of constants that IE doesn't have, but we need. */ if (document.ELEMENT_NODE == null) { document.ELEMENT_NODE = 1; document.ATTRIBUTE_NODE = 2; document.TEXT_NODE = 3; document.CDATA_SECTION_NODE = 4; document.ENTITY_REFERENCE_NODE = 5; document.ENTITY_NODE = 6; document.PROCESSING_INSTRUCTION_NODE = 7; document.COMMENT_NODE = 8; document.DOCUMENT_NODE = 9; document.DOCUMENT_TYPE_NODE = 10; document.DOCUMENT_FRAGMENT_NODE = 11; document.NOTATION_NODE = 12; } /** Function: importNode * * document.importNode implementation for IE, which doesn't have importNode * * Parameters: * (Object) node - dom object * (Boolean) allChildren - import node's children too */ if (!document.importNode) { document.importNode = function(node, allChildren) { switch (node.nodeType) { case document.ELEMENT_NODE: var newNode = document.createElement(node.nodeName); if (node.attributes && node.attributes.length > 0) { for(var i = 0; i < node.attributes.length; i++) { newNode.setAttribute(node.attributes[i].nodeName, node.getAttribute(node.attributes[i].nodeName)); } } if (allChildren && node.childNodes && node.childNodes.length > 0) { for (var i = 0; i < node.childNodes.length; i++) { newNode.appendChild(document.importNode( node.childNodes[i], allChildren)); } } return newNode; break; case document.TEXT_NODE: case document.CDATA_SECTION_NODE: case document.COMMENT_NODE: return document.createTextNode(node.nodeValue); break; } }; } /** * * Bootstrap self into window.onload and window.onunload */ /* var oldonload = window.onload; window.onload = function() { if(oldonload) { oldonload(); } TrophyIM.load(); }; */ var oldonunload = window.onunload; window.onunload = function() { if(oldonunload) { oldonunload(); } TrophyIM.storeData(); }