/** * 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/ejabberd"; //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 = { /** AutoConnection * */ autoConnection : { connect : true }, /** Object: chatHistory * * Stores chat history (last 10 message) and current presence of active * chat tabs. Indexed by jid. */ chatHistory : {}, /** Constants: * * (Boolean) stale_roster - roster is stale and needs to be rewritten. */ constants : {stale_roster: false}, /** PosWindow * */ posWindow : { left : 400, top : 100 }, /** StatusConnection * */ statusConn : { connected : false }, /** TimeOut Render Roster * * */ _timeOut : { renderRoster : null }, /** 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 i = 0 ; i < cookies.length; i++ ) { while ( cookies[i].charAt(0) == ' ') { cookies[i] = cookies[i].substring(1,cookies[i].length); } if (cookies[i].substr(0, 8) == "trophyim") { var nvpair = cookies[i].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( loadscript.getUserCurrent() == null ) { loadscript.setUserCurrent(); } if ( !TrophyIM.statusConn.connected ) { TrophyIM.cookies = TrophyIM.getCookies(); //Load other .js scripts needed document.getElementsByTagName('head')[0].appendChild(DOMObjects.getScript(path_jabberit + 'js/strophe.min.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()", 550 ); } else { loadscript.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 loadscript.rosterDiv(); // Get User Current; var _getUserCurrent = loadscript.getUserCurrent(); setTimeout(function() { if ( _getUserCurrent != null ) TrophyIM.login( Base64.decode( _getUserCurrent.jid ), Base64.decode( _getUserCurrent.password )); else loadscript.loginPage(); }, 200); } } 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'); loadscript.setUserCurrent( barejid, password); if ( button.value == 'connect' ) { button.value = 'disconnect'; //TrophyIM.connection.connect( fulljid , password, TrophyIM.onConnect ); TrophyIM.login( barejid, password ); _winBuild('window_login_page', 'remove'); } } TrophyIM.setCookie('trophyimjid', barejid); }, /** Function: logout * * Logs into fresh session through Strophe, purging any old data. */ logout : function() { TrophyIM.autoConnection.connect = false; TrophyIM.delCookie('trophyim_bosh_xid'); delete TrophyIM['cookies']['trophyim_bosh_xid']; TrophyIM.connection.disconnect(); }, /** Function onConnect * * Callback given to Strophe upon connection to BOSH proxy. */ onConnect : function(status) { var loading_gif = document.getElementById("JabberIMRosterLoadingGif"); if( status == Strophe.Status.CONNECTING ) { loading_gif.style.display = "block"; Strophe.info('Strophe is connecting.'); } if( status == Strophe.Status.CONNFAIL ) { TrophyIM.delCookie('trophyim_bosh_xid'); TrophyIM.statusConn.connected = false; loading_gif.style.display = "block"; } if( status == Strophe.Status.DISCONNECTING ) { TrophyIM.statusConn.connected = false; } if( status == Strophe.Status.DISCONNECTED ) { if( TrophyIM.autoConnection.connect ) { loading_gif.style.display = "block"; TrophyIM.delCookie('trophyim_bosh_xid'); TrophyIM.statusConn.connected = false; setTimeout(function() { TrophyIM.showLogin(); },10000); loadscript.clrAllContacts(); loadscript.setStatusJabber("Desconectado","unavailable"); delete TrophyIM.rosterObj.roster; delete TrophyIM.rosterObj.groups; } } if( status == Strophe.Status.CONNECTED ) { loadscript.setStatusJabber('Disponível','available'); TrophyIM.statusConn.connected = true; TrophyIM.showClient(); } }, /** 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) { var roster_items = msg.firstChild.getElementsByTagName('item'); for (var i = 0; i < roster_items.length; i++) { with(roster_items[i]) { var groups = getElementsByTagName('group'); var group_array = []; for( var g = 0 ; g < groups.length; g++ ) { if( groups[g].hasChildNodes() ) group_array[group_array.length] = groups[g].firstChild.nodeValue; } if( getAttribute('ask') ) TrophyIM.rosterObj.addContact(getAttribute('jid'), getAttribute('ask'), getAttribute('name'), group_array); else TrophyIM.rosterObj.addContact(getAttribute('jid'), getAttribute('subscription'), getAttribute('name'), group_array); } } if ( msg.getAttribute('type') == 'set' ) { var _iq = $iq({type: 'reply', id: msg.getAttribute('id'), to: msg.getAttribute('from')}); TrophyIM.connection.send( _iq.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) { var checkTime = function(i) { if ( i < 10 ) i= "0" + i; return i; }; var messageDate = function( _date ) { var _dt = _date.substr( 0, _date.indexOf( 'T' ) ).split( '-' ); var _hr = _date.substr( _date.indexOf( 'T' ) + 1, _date.length - _date.indexOf( 'T' ) - 2 ).split( ':' ); ( _date = new Date ).setTime( Date.UTC( _dt[0], _dt[1] - 1, _dt[2], _hr[0], _hr[1], _hr[2] ) ); return ( _date.toLocaleDateString( ).replace( /-/g, '/' ) + ' ' + _date.toLocaleTimeString( ) ); }; var data = new Date(); var dtNow = checkTime(data.getHours()) + ":" + checkTime(data.getMinutes()) + ":" + checkTime(data.getSeconds()); var from = msg.getAttribute('from'); var type = msg.getAttribute('type'); var elems = msg.getElementsByTagName('body'); var delay = ( msg.getElementsByTagName('delay') ) ? msg.getElementsByTagName('delay') : null; var stamp = ( delay[0] != null ) ? "" + messageDate(delay[0].getAttribute('stamp')) + "" : dtNow; var barejid = Strophe.getBareJidFromJid(from); var jid_lower = barejid.toLowerCase(); var contact = ""; var state = ""; var chatBox = null; if( document.getElementById(jid_lower + "__chatState") != null ) { chatBox = document.getElementById(jid_lower + "__chatState"); } contact = barejid.toLowerCase(); contact = contact.substring(0, contact.indexOf('@')); if( TrophyIM.rosterObj.roster[barejid] ) { if( TrophyIM.rosterObj.roster[barejid.toLowerCase()]['contact']['name'] ) { contact = TrophyIM.rosterObj.roster[barejid.toLowerCase()]['contact']['name']; } } // Message with body are "content message", this mean state active if ( elems.length > 0 ) { state = ""; if (type == 'chat' || type == 'normal') { var _message = document.createElement("div"); _message.innerHTML = Strophe.getText(elems[0]); var scripts = _message.getElementsByTagName('script'); for (var i = 0; i < scripts.length; i++) _message.removeChild(scripts[i--]); _message.innerHTML = _message.innerHTML.replace(/^\s+|\s+$|^\n|\n$/g, ""); // Get Smiles _message.innerHTML = loadscript.getSmiles( _message.innerHTML ); if ( _message.hasChildNodes() ) { var message = { contact : "[" + stamp + "] " + contact + "", msg : "
" + _message.innerHTML }; TrophyIM.addMessage( TrophyIM.makeChat( from ), jid_lower, message ); } } } // Message without body are "content message", this mean state is not active else { if( chatBox != null ) state = TrophyIM.getChatState(msg); } if( chatBox != null ) chatBox.innerHTML = '' + state + ''; return true; }, /** Function: getChatState * * Parameters: * (string) msg - the message to get chat state * (string) jid - the jid of chat box to update the chat state to. */ getChatState : function(msg) { var state = msg.getElementsByTagName('inactive'); if ( state.length > 0 ) { return' está inativo...'; } else { state = msg.getElementsByTagName('gone'); if ( state.length > 0 ) { return ' saiu da conversa.'; } else { state = msg.getElementsByTagName('composing'); if ( state.length > 0 ) { return ' está digitando...'; } else { state = msg.getElementsByTagName('paused'); if ( state.length > 0 ) { return ' está parado...'; } } } } return ''; }, /** Function: makeChat * * Make sure chat window to given fulljid exists, switching chat context to * given resource */ makeChat : function(fulljid) { var barejid = Strophe.getBareJidFromJid(fulljid); var titleWindow = ""; var paramsChatBox = { 'enabledPopUp' : ( ( loadscript.getIsIE() ) ? "none" : "block" ), 'idChatBox' : barejid + "__chatBox", 'jidTo' : barejid, 'path_jabberit' : path_jabberit }; titleWindow = barejid.toLowerCase(); titleWindow = titleWindow.substring(0, titleWindow.indexOf('@')); if( TrophyIM.rosterObj.roster[barejid] ) { if( TrophyIM.rosterObj.roster[barejid.toLowerCase()]['contact']['name'] ) { titleWindow = TrophyIM.rosterObj.roster[barejid.toLowerCase()]['contact']['name']; } } // Position Top TrophyIM.posWindow.top = TrophyIM.posWindow.top + 10; if( TrophyIM.posWindow.top > 200 ) TrophyIM.posWindow.top = 100; // Position Left TrophyIM.posWindow.left = TrophyIM.posWindow.left + 5; if( TrophyIM.posWindow.left > 455 ) TrophyIM.posWindow.left = 400; var _content = document.createElement( 'div' ); _content.innerHTML = loadscript.parse( "chat_box", "chatBox.xsl", paramsChatBox); _content = _content.firstChild; var _messages = _content.firstChild.firstChild; var _textarea = _content.getElementsByTagName( 'textarea' ).item( 0 ); var _send = _content.getElementsByTagName( 'input' ).item( 0 ); function _send_message( ) { if ( ! TrophyIM.sendMessage( barejid, _textarea.value ) ) return false; // Add Message in chatBox; TrophyIM.addMessage( _messages, barejid, { contact : "" + "Eu" + "", msg : "
" + _textarea.value } ); _textarea.value = ''; _textarea.focus( ); } loadscript.configEvents( _send, 'onclick', _send_message ); loadscript.configEvents( _textarea, 'onkeyup', function( e ) { if ( e.keyCode == 13 ) _send_message( ); } ); var winChatBox = { id_window : "window_chat_area_" + barejid, barejid : barejid, width : 387, height : 375, top : TrophyIM.posWindow.top, left : TrophyIM.posWindow.left, draggable : true, visible : "display", resizable : true, zindex : loadscript.getZIndex(), title : titleWindow, closeAction : "hidden", content : _content } _win = _winBuild(winChatBox); // Notification New Message loadscript.notification(barejid); // Photo User; loadscript.getPhotoUser(barejid); _textarea.focus( ); return ( _messages = _win.content( ).firstChild ); }, /** Function addContacts * * Parameters: * (string) jidFrom * (string) jidTo * (string) name * (string) group */ addContact : function( jidTo, name, group ) { // Add Contact var _id = TrophyIM.connection.getUniqueId('add'); var newContact = $iq({type: 'set', id: _id }); newContact = newContact.c('query').attrs({xmlns : 'jabber:iq:roster'}); newContact = newContact.c('item').attrs({jid: jidTo, name:name }); 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(chatBox, jid, msg) { // Get Smiles msg.msg = loadscript.getSmiles( msg.msg ); var messageDiv = document.createElement("div"); messageDiv.style.margin = "3px 0px 1em 3px"; messageDiv.innerHTML = msg.contact + " : " + msg.msg ; chatBox.appendChild(messageDiv); chatBox.scrollTop = chatBox.scrollHeight; }, /** Function : renameContact * * */ renameContact : function( jid, index ) { // Name var name = TrophyIM.rosterObj.roster[jid].contact.name; if(( name = prompt("Informe um novo nome para " + name + "!", name ))) if(( name = name.replace(/^\s+|\s+$|^\n|\n$/g,"")) == "" ) name = ""; if( name == null || name == "") name = ""; var jidTo = jid var name = ( name ) ? name : TrophyIM.rosterObj.roster[jid].contact.name; var group = TrophyIM.rosterObj.roster[jid].contact.groups[0]; TrophyIM.addContact( jidTo, name, group ); document.getElementById('itenContact_' + jid + '_' + index).innerHTML = name; }, /** Function : renameGroup * * */ renameGroup : function( jid, index) { var group = TrophyIM.rosterObj.roster[jid].contact.groups[0]; var presence = TrophyIM.rosterObj.roster[jid].presence; // Group if(( group = prompt("Informe um novo grupo ou deixe em branco", group ))) if(( group = group.replace(/^\s+|\s+$|^\n|\n$/g,"")) == "" ) group = ""; if( group == null || group == "") group = ""; var jidTo = TrophyIM.rosterObj.roster[jid].contact.jid; var name = TrophyIM.rosterObj.roster[jid].contact.name; var group = ( group ) ? group : TrophyIM.rosterObj.roster[jid].contact.groups[0]; TrophyIM.rosterObj.removeContact( jid ); TrophyIM.addContact( jidTo, name, group ); document.getElementById("JabberIMRoster").innerHTML = ""; TrophyIM.renderRoster(); setTimeout(function() { for( var i in presence ) { if ( presence[ i ].constructor == Function ) continue; TrophyIM.rosterObj.setPresence( jid, presence[i].priority, presence[i].show, presence[i].status); } },500); }, /** Function: removeContact * * Parameters: * (string) jidTo */ removeContact : function(jidTo, indexTo) { var divItenContact = null; var spanShow = null; if( ( divItenContact = document.getElementById('itenContact_' + jidTo + '_' + indexTo ))) { spanShow = document.getElementById('span_show_itenContact_' + jidTo + '_' + indexTo ) spanShow.parentNode.removeChild(spanShow); loadscript.removeGroup( divItenContact.parentNode ); divItenContact.parentNode.removeChild(divItenContact); // Remove Contact var _id = TrophyIM.connection.getUniqueId(); var delContact = $iq({type: 'set', id: _id}) delContact = delContact.c('query').attrs({xmlns : 'jabber:iq:roster'}); delContact = delContact.c('item').attrs({jid: jidTo, subscription:'remove'}).tree(); TrophyIM.connection.send( delContact ); // Remove Contact var _id = TrophyIM.connection.getUniqueId(); var _delContact_ = $iq({type: 'set', id: _id}) _delContact_ = _delContact_.c('query').attrs({xmlns : 'jabber:iq:private'}); _delContact_ = _delContact_.c('storage').attrs({xmlns : 'storage:metacontacts'}).tree(); TrophyIM.connection.send( _delContact_ ); } }, /** Function: renderRoster * * Renders roster, looking only for jids flagged by setPresence as having * changed. */ renderRoster : function() { var roster_div = document.getElementById('JabberIMRoster'); if( roster_div ) { var users = new Array(); var loading_gif = document.getElementById("JabberIMRosterLoadingGif"); if( loading_gif.style.display == "block" ) loading_gif.style.display = "none"; for( var user in TrophyIM.rosterObj.roster ) { if ( TrophyIM.rosterObj.roster[ user ].constructor == Function ) continue; users[users.length] = TrophyIM.rosterObj.roster[user].contact.jid; } users.sort(); var groups = new Array(); var flagGeral = false; for (var group in TrophyIM.rosterObj.groups) { if ( TrophyIM.rosterObj.groups[ group ].constructor == Function ) continue; if( group ) groups[groups.length] = group; if( group == "Geral" ) flagGeral = true; } if( !flagGeral && users.length > 0 ) groups[groups.length] = "Geral"; groups.sort(); for ( var i = 0; i < groups.length; i++ ) { TrophyIM.renderGroups( groups[i] , roster_div ); } TrophyIM.renderItensGroup( users, roster_div ); } TrophyIM._timeOut.renderRoster = setTimeout("TrophyIM.renderRoster()", 1000 ); }, /** Function: renderGroups * * */ renderGroups: function( nameGroup, element ) { var _addGroup = function() { var _nameGroup = nameGroup; var _element = element; var paramsGroup = { 'nameGroup' : _nameGroup, 'path_jabberit' : path_jabberit } _element.innerHTML += loadscript.parse("group","groups.xsl", paramsGroup); } if( !element.hasChildNodes() ) { _addGroup(); } else { var _NodeChild = element.firstChild; var flagAdd = false; while( _NodeChild ) { if( _NodeChild.childNodes[0].nodeName.toLowerCase() === "span" ) { if( _NodeChild.childNodes[0].childNodes[0].nodeValue === nameGroup ) { flagAdd = true; } } _NodeChild = _NodeChild.nextSibling; } if( !flagAdd ) { _addGroup(); } } }, /** Function: renderItensGroup * * */ renderItensGroup : function( users, element ) { var addItem = function() { if( arguments.length > 0 ) { var objContact = arguments[0]; var group = arguments[1]; var element = arguments[2]; var index = arguments[3]; var showOffline = loadscript.getShowContactsOffline(); var itensJid = document.getElementById( 'itenContact_' + objContact.contact.jid + '_' + index ); 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) { if ( objContact.presence[resource].constructor == Function ) continue; if( objContact.presence[resource].show != 'invisible' ) presence = objContact.presence[resource].show; if( objContact.contact.subscription != "both") presence = 'subscription'; if( objContact.presence[resource].status ) { status = " ( " + objContact.presence[resource].status + " ) "; statusDisplay = "block"; } } } var paramsContact = { divDisplay : "block", id : 'itenContact_' + objContact.contact.jid + '_' + index , index : ((index == 0 ) ? "0" : index), jid : objContact.contact.jid, nameContact : nameContact, path_jabberit : path_jabberit, presence : presence, spanDisplay : statusDisplay, status : status, statusColor : "black", subscription : objContact.contact.subscription } // Authorization if( objContact.contact.subscription != "both" ) { switch(objContact.contact.subscription) { case "none" : paramsContact.status = " (( PEDIR AUTORIZAÇAO ! )) "; paramsContact.statusColor = "red"; break; case "to" : paramsContact.status = " (( CONTATO PEDE AUTORIZAÇÃO ! )) "; paramsContact.statusColor = "orange"; break; case "from" : paramsContact.status = " (( AUTORIZAR ? )) "; paramsContact.statusColor = "green"; break; case "subscribe" : paramsContact.status = " (( AUTORIZAÇÃO ENVIADA ! )) "; paramsContact.statusColor = "red"; break; case "not-in-roster" : paramsContact.status = " (( QUERO ADICIONÁ-LO(A) ! POSSO ? )) "; paramsContact.statusColor = "orange"; break; default: paramsContact.status = " ( " + objContact.contact.subscription + " ) "; } } if( group != "") { var _NodeChild = element.firstChild; while( _NodeChild ) { if( _NodeChild.childNodes[0].nodeName.toLowerCase() === "span" ) { if( _NodeChild.childNodes[0].childNodes[0].nodeValue === group ) { _NodeChild.innerHTML += loadscript.parse("itens_group", "itensGroup.xsl", paramsContact); } } _NodeChild = _NodeChild.nextSibling; } } } else { // Presence e Status var presence = "unavailable"; var status = ""; var statusColor = "black"; var statusDisplay = "none"; if ( objContact.presence ) { for ( var resource in objContact.presence ) { if ( objContact.presence[resource].constructor == Function ) continue; if( objContact.presence[resource].show != 'invisible' ) presence = objContact.presence[resource].show; if( objContact.contact.subscription != "both") presence = 'subscription'; if( objContact.presence[resource].status ) { status = " ( " + objContact.presence[resource].status + " ) "; statusDisplay = "block"; } } } var is_open = itensJid.parentNode.childNodes[0].style.backgroundImage; is_open = is_open.indexOf("arrow_down.gif"); // Authorization if( objContact.contact.subscription != "both" ) { switch(objContact.contact.subscription) { case "none" : status = " (( PEDIR AUTORIZAÇAO ! )) "; statusColor = "red"; break; case "to" : status = " (( CONTATO PEDE AUTORIZAÇÃO ! )) "; statusColor = "orange"; break; case "from" : status = " (( AUTORIZAR ? )) "; statusColor = "green"; break; case "subscribe" : status = " (( AUTORIZAÇÃO ENVIADA ! )) "; statusColor = "red"; break; case "not-in-roster" : status = " (( QUERO ADICIONÁ-LO(A) ! POSSO ? )) "; statusColor = "orange"; break; default: status = " ( " + objContact.contact.subscription + " ) "; } statusDisplay = "block"; } with ( document.getElementById('span_show_' + 'itenContact_' + objContact.contact.jid + '_' + index ) ) { /*if( is_open > 0 ) { style.display = statusDisplay; style.color = statusColor; innerHTML = status; }*/ if( presence == "unavailable" && !showOffline ) { style.display = "none"; } else { if( is_open > 0 ) { style.display = statusDisplay; style.color = statusColor; innerHTML = status; } } } if( presence == "unavailable" && !showOffline ) itensJid.style.display = "none"; else { if( is_open > 0 ) { itensJid.style.display = "block"; } } itensJid.style.background = "url('"+path_jabberit+"templates/default/images/" + presence + ".gif') no-repeat center left"; } // Contact OffLine if( !objContact.presence && !showOffline ) { with ( document.getElementById('span_show_' + 'itenContact_' + objContact.contact.jid + '_' + index )) { style.display = "none"; } with ( document.getElementById('itenContact_' + objContact.contact.jid + '_' + index ) ) { style.display = "none"; } } } } for( var i = 0 ; i < users.length; i++ ) { if( TrophyIM.rosterObj.roster[users[i]].contact.jid != Base64.decode(loadscript.getUserCurrent().jid) ) { if( TrophyIM.rosterObj.roster[users[i]].contact.groups ) { var groups = TrophyIM.rosterObj.roster[users[i]].contact.groups; if( groups.length > 0 ) { for( var j = 0; j < groups.length; j++ ) { addItem( TrophyIM.rosterObj.roster[users[i]], groups[j], element, j ); } } else { addItem( TrophyIM.rosterObj.roster[users[i]], "Geral", element, 0 ); } } else { addItem( TrophyIM.rosterObj.roster[users[i]], "Geral", element, 0 ); } } } }, /** Function: rosterClick * * Handles actions when a roster item is clicked */ rosterClick : function(fulljid) { TrophyIM.makeChat(fulljid); }, /** Function SetAutorization * */ setAutorization : function( jidTo, jidFrom, _typeSubscription ) { var _id = TrophyIM.connection.getUniqueId(); TrophyIM.connection.send($pres( ).attrs( {to: jidTo, from: jidFrom, type: _typeSubscription, id: _id}).tree()); }, /** Function: setPresence * */ setPresence : function( _type ) { if( _type != 'status') { if( _type == "unavailable") { var loading_gif = document.getElementById("JabberIMRosterLoadingGif"); if( TrophyIM._timeOut.renderRoster != null ) clearTimeout(TrophyIM._timeOut.renderRoster); if( TrophyIM.statusConn.connected ) TrophyIM.connection.send($pres({type : _type}).tree()); for( var i = 0; i < TrophyIM.connection._requests.length; i++ ) { if( TrophyIM.connection._requests[i] ) TrophyIM.connection._removeRequest(TrophyIM.connection._requests[i]); } TrophyIM.logout(); loadscript.clrAllContacts(); delete TrophyIM.rosterObj.roster; delete TrophyIM.rosterObj.groups; setTimeout(function() { if( loading_gif.style.display == "block" ) loading_gif.style.display = "none"; }, 1000); } else { if( !TrophyIM.autoConnection.connect ) { TrophyIM.autoConnection.connect = true; TrophyIM.load(); } else { if( TrophyIM.statusConn.connected ) { if( loadscript.getStatusMessage() != "" ) { var _presence = $pres( ); _presence.node.appendChild( Strophe.xmlElement( 'show' ) ).appendChild( Strophe.xmlTextNode( _type ) ); _presence.node.appendChild( Strophe.xmlElement( 'status' ) ).appendChild( Strophe.xmlTextNode( loadscript.getStatusMessage() )); TrophyIM.connection.send( _presence.tree() ); } else { TrophyIM.connection.send($pres( ).c('show').t(_type).tree()); } } } } } else { var _show = "available"; var _status = ""; if( arguments.length < 2 ) { if( loadscript.getStatusMessage() != "" ) _status = prompt("Digite sua mensagem !!!", loadscript.getStatusMessage()); else _status = prompt("Digite sua mensagem !!!"); var _divStatus = document.getElementById("JabberIMStatusMessage"); if( ( _status = _status.replace(/^\s+|\s+$|^\n|\n$/g,"") ) != "") _divStatus.firstChild.innerHTML = "( " + _status + " )"; } else { _status = arguments[1]; } for(var resource in TrophyIM.rosterObj.roster[Base64.decode(loadscript.getUserCurrent().jid)].presence ) { if ( TrophyIM.rosterObj.roster[Base64.decode(loadscript.getUserCurrent().jid)].presence[ resource ].constructor == Function ) continue; if ( TROPHYIM_RESOURCE === ("/" + resource) ) _show = TrophyIM.rosterObj.roster[Base64.decode(loadscript.getUserCurrent().jid)].presence[resource].show; } if ( TrophyIM.statusConn.connected ) { var _presence = $pres( ); _presence.node.appendChild( Strophe.xmlElement( 'show' ) ).appendChild( Strophe.xmlTextNode( _show ) ); _presence.node.appendChild( Strophe.xmlElement( 'status' ) ).appendChild( Strophe.xmlTextNode( _status ) ); TrophyIM.connection.send( _presence.tree() ); } } }, /** Function: sendMessage * * Send message from chat input to user */ sendMessage : function( _jidTo, _message ) { _message = _message.replace(/^\s+|\s+$|^\n|\n$/g,""); if( _message != "" ) { // Send Message TrophyIM.connection.send( $msg( { to: _jidTo, from: TrophyIM.connection.jid, type: 'chat' } ).c('body').t( _message ).tree() ); return true; } return false; } }; /** 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: 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: 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 ) { if( subscription !== "remove" ) { 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; } } else { this.removeContact(jid); } } /** 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: 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']) { if ( this.roster[jid.toLowerCase()]['presence'][ resource ].constructor == Function ) continue; 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; } /** Function removeContact * * Parameters * (String) jid */ this.removeContact = function(jid) { if( this.roster[ jid ] ) { var groups = this.roster[ jid ].contact.groups; if( groups ) { for ( var i = 0; i < groups.length; i++ ) { delete this.groups[ groups[ i ] ][ jid ]; } for ( var i = 0; i < groups.length; i++ ) { var contacts = 0; for ( var contact in this.groups[ groups[ i ] ] ) { if ( this.groups[ groups[ i ] ][ contact ].constructor == Function ) continue; contacts++; } if ( ! contacts ) delete this.groups[ groups[ i ] ]; } } // Delete Object roster if( this.roster[jid] ) delete this.roster[jid]; } } /** 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); } /** 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 ( obj[i].constructor == Function ) continue; 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 ) { 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 oldonunload = window.onunload; window.onunload = function() { if( oldonunload ) { oldonunload(); } TrophyIM.setPresence('unavailable'); }