source: sandbox/jabberit_messenger/trophy/js/trophyim.js @ 2339

Revision 2339, 45.2 KB checked in by alexandrecorreia, 14 years ago (diff)

Ticket #986 - Refeita a lista de contatos para o uso de forms xsl.

  • Property svn:executable set to *
Line 
1/*
2    This program is distributed under the terms of the MIT license.
3    Please see the LICENSE file for details.
4
5    Copyright 2008 Michael Garvin
6*/
7/*TODO dump / very loose roadmap
8--0.4
9add chats to json store
10Mouseover status messages in roster
11HTML in messages (xslt?)
12Select presence status/message
13Optional user-specified resource
14Loglevel select on login instead of check box
15vcard support http://xmpp.org/extensions/xep-0153.html
16Notifications of closed chats
17Notifications of typing
18--0.5
19roster management
20figure out how we want to handle presence from our own jid (and transports)
21roster sorting by presence / offline roster capablility
22auto-subscribe vs prompted subscribe based on config option
23make sure makeChat() et al. can handle empty resources
24    (offline chat capabilities)
25--1.0 (or whenever someone submits better .css)
26layout overhaul
27code cleanup (like checking for excessive function lengths)
28roster versioning http://xmpp.org/extensions/attic/xep-0237-0.1.html
29make sure onload bootstrapping actually preserves existing onloads
30*/
31
32var TROPHYIM_BOSH_SERVICE       = '/proxy/xmpp-httpbind';  //Change to suit
33var TROPHYIM_LOG_LINES          = 200;
34
35//0=debug, 1=info, 2=warn, 3=error, 4=fatal
36var TROPHYIM_LOGLEVEL           = 0;
37
38var TROPHYIM_VERSION            = "0.3";
39
40var TROPHYIM_RESOURCE           = "/JABBERITWEB";
41
42//Uncomment to make session reattachment work
43//var TROPHYIM_JSON_STORE = "json_store.php";
44
45/** File: trophyimclient.js
46 *  A JavaScript front-end for strophe.js
47 *
48 *  This is a JavaScript library that runs on top of strophe.js.  All that
49 *  is required is that your page have a <div> element with an id of
50 *  'trophyimclient', and that your page does not explicitly set an onload
51 *  event in its <body> tag.  In that case you need to append TrophyIM.load()
52 *  to it.
53 *
54 *  The style of the client can be conrolled via trophyim.css, which is
55 *  auto-included by the client.
56 */
57
58/** Object: HTMLSnippets
59 *
60 *  This is the repository for all the html snippets that TrophyIM uses
61 *
62 */
63HTMLSnippets = {
64
65                //chatArea      : loadIM.HTMLSnippets.chatArea( ),
66               
67        chatBox         : loadIM.HTMLSnippets.chatBox( ),
68
69        chatTab         : loadIM.HTMLSnippets.chatTab( ),
70               
71                //loginPage     : loadIM.HTMLSnippets.loginPage( ),
72   
73        loggingDiv      : loadIM.HTMLSnippets.loggingDiv( ),
74   
75        rosterDiv       : loadIM.HTMLSnippets.rosterDiv( ),
76   
77        rosterGroup     : loadIM.HTMLSnippets.rosterGroup( ),
78       
79        rosterItem      : loadIM.HTMLSnippets.rosterItem( ),
80   
81        //statusDiv     : loadIM.HTMLSnippets.statusDiv( ),
82};
83
84/** Object: DOMObjects
85 *  This class contains builders for all the DOM objects needed by TrophyIM
86 */
87DOMObjects = {
88    /** Function: xmlParse
89     *  Cross-browser alternative to using innerHTML
90     *  Parses given string, returns valid DOM HTML object
91     *
92     *  Parameters:
93     *    (String) xml - the xml string to parse
94     */
95    xmlParse : function(xmlString) {
96        var xmlObj = this.xmlRender(xmlString);
97        if(xmlObj) {
98            try { //Firefox, Gecko, etc
99                if (this.processor == undefined) {
100                    this.processor = new XSLTProcessor();
101                    this.processor.importStylesheet(this.xmlRender(
102                    '<xsl:stylesheet version="1.0"\
103                    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">\
104                    <xsl:output method="html" indent="yes"/><xsl:template\
105                    match="@*|node()"><xsl:copy><xsl:copy-of\
106                    select="@*|node()"/></xsl:copy></xsl:template>\
107                    </xsl:stylesheet>'));
108                }
109                var htmlObj =
110                this.processor.transformToDocument(xmlObj).documentElement;
111                //Safari has a quirk where it wraps dom elements in <html><body>
112                if (htmlObj.tagName.toLowerCase() == 'html') {
113                    htmlObj = htmlObj.firstChild.firstChild;
114                }
115                return document.importNode(htmlObj, true);
116            } catch(e) {
117                try { //IE is so very very special
118                    var htmlObj = document.importNode(xmlObj.documentElement, true);
119                    if (htmlObj.tagName.toLowerCase() == "div") {
120                        var div_wrapper = document.createElement('div');
121                        div_wrapper.appendChild(htmlObj);
122                        if(div_wrapper.innerHTML) {
123                            div_wrapper.innerHTML = div_wrapper.innerHTML;
124                        }
125                        htmlObj = div_wrapper.firstChild;
126                    }
127                    return htmlObj;
128                } catch(e) {
129                    alert(
130                    "TrophyIM Error: Cannot add html to page" + e.message);
131                }
132            }
133        }
134    },
135    /** Function: xmlRender
136     *  Uses browser-specific methods to turn given string into xml object
137     *
138     *  Parameters:
139     *    (String) xml - the xml string to parse
140     */
141    xmlRender : function(xmlString) {
142        try {//IE
143            var renderObj = new ActiveXObject("Microsoft.XMLDOM");
144            renderObj.async="false";
145            if(xmlString) {
146                renderObj.loadXML(xmlString);
147            }
148        } catch (e) {
149            try { //Firefox, Gecko, etc
150                if (this.parser == undefined) {
151                    this.parser = new DOMParser();
152                }
153                var renderObj = this.parser.parseFromString(xmlString,
154                "application/xml");
155            } catch(e) {
156                alert("TrophyIM Error: Cannot create new html for page");
157            }
158        }
159
160        return renderObj;
161    },
162    /** Function: getHTML
163     *  Returns named HTML snippet as DOM object
164     *
165     *  Parameters:
166     *    (String) name - name of HTML snippet to retrieve (see HTMLSnippets
167     *    object)
168     */
169    getHTML : function(page)
170        {
171        return this.xmlParse(HTMLSnippets[page]);
172    },
173       
174    /** Function: getScript
175     *  Returns script object with src to given script
176     *
177     *  Parameters:
178     *    (String) script - name of script to put in src attribute of script
179     *    element
180     */
181    getScript : function(script) {
182        var newscript = document.createElement('script');
183        newscript.setAttribute('src', script);
184        newscript.setAttribute('type', 'text/javascript');
185        return newscript;
186    }
187};
188
189/** Object: TrophyIM
190 *
191 *  This is the actual TrophyIM application.  It searches for the
192 *  'trophyimclient' element and inserts itself into that.
193 */
194TrophyIM = {
195    /** Constants:
196     *
197     *    (Boolean) stale_roster - roster is stale and needs to be rewritten.
198     */
199    constants : {stale_roster: false},
200    /** Object: chatHistory
201     *
202     *  Stores chat history (last 10 message) and current presence of active
203     *  chat tabs.  Indexed by jid.
204     */
205    chatHistory : {},
206   
207    /** Object: activeChats
208     *
209     *  This object stores the currently active chats.
210     */
211   
212     activeChats : {current: null, divs: {}},
213   
214     /** Function: setCookie
215     *
216     *  Sets cookie name/value pair.  Date and path are auto-selected.
217     *
218     *  Parameters:
219     *    (String) name - the name of the cookie variable
220     *    (String) value - the value of the cookie variable
221     */
222    setCookie : function(name, value) {
223        var expire = new Date();
224        expire.setDate(expire.getDate() + 365);
225        document.cookie = name + "=" + value + "; expires=" + expire.toGMTString();
226    },
227    /** Function: delCookie
228     *
229     *  Deletes cookie
230     *
231     *  Parameters:
232     *    (String) name) - the name of the cookie to delete
233     */
234    delCookie : function(name) {
235        var expire = new Date();
236        expire.setDate(expire.getDate() - 365);
237        document.cookie = name + "= ; expires=" + expire.toGMTString();
238        delete TrophyIM.cookies[name];
239    },
240    /** Function: getCookies
241     *
242     *  Retrieves all trophyim cookies into an indexed object.  Inteneded to be
243     *  called once, at which time the app refers to the returned object instead
244     *  of re-parsing the cookie string every time.
245     *
246     *  Each cookie is also re-applied so as to refresh the expiry date.
247     */
248    getCookies : function() {
249        var cObj = {};
250        var cookies = document.cookie.split(';');
251        for (var c in cookies) {
252            while (cookies[c].charAt(0)==' ') {
253                cookies[c] = cookies[c].substring(1,cookies[c].length);
254            }
255            if (cookies[c].substr(0, 8) == "trophyim") {
256                var nvpair = cookies[c].split("=", 2);
257                cObj[nvpair[0]] = nvpair[1];
258                TrophyIM.setCookie(nvpair[0], nvpair[1]);
259            }
260        }
261        return cObj;
262    },
263       
264    /** Function: load
265     *
266     *  This function searches for the trophyimclient div and loads the client
267     *  into it.
268     */
269
270        load : function()
271        {
272                TrophyIM.cookies        = TrophyIM.getCookies();
273
274                TrophyIM.client_div = document.getElementById('trophyimclient');
275
276        //Load other .js scripts needed
277        document.getElementsByTagName('head')[0].appendChild(DOMObjects.getScript('strophejs/strophe.js'));
278        document.getElementsByTagName('head')[0].appendChild(DOMObjects.getScript('strophejs/md5.js'));
279        document.getElementsByTagName('head')[0].appendChild(DOMObjects.getScript('strophejs/sha1.js'));
280        document.getElementsByTagName('head')[0].appendChild(DOMObjects.getScript('strophejs/b64.js'));
281        document.getElementsByTagName('head')[0].appendChild(DOMObjects.getScript('js/json2.js')); //Keep this script last
282
283                //Wait a second to give scripts time to load
284                setTimeout("TrophyIM.showLogin()", 500);
285
286   },
287   
288    /** Function: storeData
289     *
290     *  Store all our data in the JSONStore, if it is active
291     */
292   storeData : function()
293   {
294        if (TrophyIM.connection && TrophyIM.connection.connected)
295                {
296            TrophyIM.setCookie('trophyim_bosh_xid', TrophyIM.connection.jid + "|" +
297            TrophyIM.connection.sid + "|" +  TrophyIM.connection.rid);
298            TrophyIM.rosterObj.save();
299        }
300    },
301    /**  Function: showlogin
302     *
303     *   This function clears out the IM box and either redisplays the login
304     *   page, or re-attaches to Strophe, preserving the logging div if it
305     *   exists, or creating a new one of we are re-attaching.
306     */
307    showLogin : function() {
308        //JSON is the last script to load, so we wait on it
309        //Added Strophe check too because of bug where it's sometimes missing
310        if (typeof(JSON) != undefined && typeof(Strophe) != undefined)
311        {
312                TrophyIM.JSONStore = new TrophyIMJSONStore();
313               
314            if ( TrophyIM.JSONStore.store_working && TrophyIM.cookies['trophyim_bosh_xid'] )
315            {
316                var xids = TrophyIM.cookies['trophyim_bosh_xid'].split("|");
317                TrophyIM.delCookie('trophyim_bosh_xid');
318                TrophyIM.constants.stale_roster = true;
319                if (TrophyIM.cookies['trophyimloglevel'])
320                {
321                    TrophyIM.client_div.appendChild(DOMObjects.getHTML('loggingDiv'));
322                    TrophyIM.logging_div = document.getElementById('trophyimlog');
323                }
324                TrophyIM.connection = new Strophe.Connection(TROPHYIM_BOSH_SERVICE);
325                TrophyIM.connection.rawInput = TrophyIM.rawInput;
326                TrophyIM.connection.rawOutput = TrophyIM.rawOutput;
327                Strophe.log = TrophyIM.log;
328                Strophe.info('Attempting Strophe attach.');
329                TrophyIM.connection.attach(xids[0], xids[1], xids[2], TrophyIM.onConnect);
330                TrophyIM.onConnect(Strophe.Status.CONNECTED);
331            }
332            else
333            {
334                var logging_div = TrophyIM.clearClient();
335                loadIM.HTMLSnippets.loginPage();
336            }
337        }
338        else
339        {
340                setTimeout("TrophyIM.showLogin()", 500);
341        }
342    },
343    /** Function: log
344     *
345     *  This function logs the given message in the trophyimlog div
346     *
347     *  Parameter: (String) msg - the message to log
348     */
349   
350    log : function(level, msg)
351    {
352        if (TrophyIM.logging_div && level >= TROPHYIM_LOGLEVEL) {
353            while(TrophyIM.logging_div.childNodes.length > TROPHYIM_LOG_LINES) {
354                TrophyIM.logging_div.removeChild(
355                TrophyIM.logging_div.firstChild);
356            }
357            var msg_div = document.createElement('div');
358            msg_div.className = 'trophyimlogitem';
359            msg_div.appendChild(document.createTextNode(msg));
360            TrophyIM.logging_div.appendChild(msg_div);
361            TrophyIM.logging_div.scrollTop = TrophyIM.logging_div.scrollHeight;
362        }
363    },
364    /** Function: rawInput
365     *
366     *  This logs the packets actually recieved by strophe at the debug level
367     */
368    rawInput : function (data) {
369        Strophe.debug("RECV: " + data);
370    },
371    /** Function: rawInput
372     *
373     *  This logs the packets actually recieved by strophe at the debug level
374     */
375    rawOutput : function (data) {
376        Strophe.debug("SEND: " + data);
377    },
378    /** Function: login
379     *
380     *  This function logs into server using information given on login page.
381     *  Since the login page is where the logging checkbox is, it makes or
382     *  removes the logging div and cookie accordingly.
383     *
384     */
385    login : function()
386        {
387        if (document.getElementById('trophyimloglevel').checked) {
388            TrophyIM.setCookie('trophyimloglevel', 1);
389            if (!document.getElementById('trophyimlog')) {
390                TrophyIM.client_div.appendChild(DOMObjects.getHTML('loggingDiv'));
391                TrophyIM.logging_div = document.getElementById('trophyimlog');
392            }
393        } else {
394            TrophyIM.delCookie('trophyimloglevel');
395            if (document.getElementById('trophyimlog')) {
396                TrophyIM.client_div.removeChild(document.getElementById(
397                'trophyimlog'));
398                TrophyIM.logging_div = null;
399            }
400        }
401        if (TrophyIM.JSONStore.store_working) { //In case they never logged out
402            TrophyIM.JSONStore.delData(['groups','roster', 'active_chat',
403            'chat_history']);
404        }
405        TrophyIM.connection = new Strophe.Connection(TROPHYIM_BOSH_SERVICE);
406        TrophyIM.connection.rawInput = TrophyIM.rawInput;
407        TrophyIM.connection.rawOutput = TrophyIM.rawOutput;
408        Strophe.log = TrophyIM.log;
409        var barejid  = document.getElementById('trophyimjid').value
410        var fulljid = barejid + TROPHYIM_RESOURCE;
411        TrophyIM.setCookie('trophyimjid', barejid);
412        var password = document.getElementById('trophyimpass').value;
413        var button = document.getElementById('trophyimconnect');
414        if (button.value == 'connect') {
415            button.value = 'disconnect';
416            TrophyIM.connection.connect(fulljid, password, TrophyIM.onConnect);
417        } else {
418            button.value = 'connect';
419            TrophyIM.connection.disconnect();
420        }
421
422    },
423    /** Function: login
424     *
425     *  Logs into fresh session through Strophe, purging any old data.
426     */
427    logout : function()
428        {
429        TrophyIM.delCookie('trophyim_bosh_xid');
430        delete TrophyIM['cookies']['trophyim_bosh_xid'];
431        if (TrophyIM.JSONStore.store_working)
432                {
433            TrophyIM.JSONStore.delData(['groups','roster', 'active_chat', 'chat_history']);
434        }
435       
436                for (var chat in TrophyIM.activeChats['divs'])
437                {
438            delete TrophyIM.activeChats['divs'][chat];
439        }
440               
441        TrophyIM.activeChats = {current: null, divs: {}},
442        TrophyIM.connection.disconnect();
443        TrophyIM.showLogin();
444               
445    },
446    /** Function onConnect
447     *
448     *  Callback given to Strophe upon connection to BOSH proxy.
449     */
450    onConnect : function(status)
451        {
452        if (status == Strophe.Status.CONNECTING) {
453            Strophe.info('Strophe is connecting.');
454        } else if (status == Strophe.Status.CONNFAIL) {
455            Strophe.info('Strophe failed to connect.');
456            TrophyIM.delCookie('trophyim_bosh_xid');
457            TrophyIM.showLogin();
458        } else if (status == Strophe.Status.DISCONNECTING) {
459            Strophe.info('Strophe is disconnecting.');
460        } else if (status == Strophe.Status.DISCONNECTED) {
461            Strophe.info('Strophe is disconnected.');
462            TrophyIM.delCookie('trophyim_bosh_xid');
463            TrophyIM.showLogin();
464        } else if (status == Strophe.Status.CONNECTED) {
465            Strophe.info('Strophe is connected.');
466            TrophyIM.showClient();
467        }
468    },
469
470    /** Function: showClient
471     *
472     *  This clears out the main div and puts in the main client.  It also
473     *  registers all the handlers for Strophe to call in the client.
474     */
475    showClient : function()
476        {
477        TrophyIM.setCookie('trophyim_bosh_xid', TrophyIM.connection.jid + "|" +
478        TrophyIM.connection.sid + "|" +  TrophyIM.connection.rid);
479        var logging_div = TrophyIM.clearClient();
480       
481        if( logging_div )
482                {
483            TrophyIM.client_div.appendChild(logging_div);
484            TrophyIM.logging_div = document.getElementById('trophyimlog');
485        }
486               
487        TrophyIM.rosterObj = new TrophyIMRoster();
488        TrophyIM.connection.addHandler(TrophyIM.onVersion, Strophe.NS.VERSION, 'iq', null, null, null);
489        TrophyIM.connection.addHandler(TrophyIM.onRoster, Strophe.NS.ROSTER, 'iq', null, null, null);
490        TrophyIM.connection.addHandler(TrophyIM.onPresence, null, 'presence', null, null, null);
491        TrophyIM.connection.addHandler(TrophyIM.onMessage, null, 'message', null, null,  null);
492       
493                //Get roster then announce presence.
494        TrophyIM.connection.send($iq({type: 'get', xmlns: Strophe.NS.CLIENT}).c('query', {xmlns: Strophe.NS.ROSTER}).tree());
495        TrophyIM.connection.send($pres().tree());
496                setTimeout("TrophyIM.renderRoster()", 1000);
497    },
498       
499    /** Function: clearClient
500     *
501     *  Clears out client div, preserving and returning existing logging_div if
502     *  one exists
503     */
504    clearClient : function() {
505        if(TrophyIM.logging_div) {
506            var logging_div = TrophyIM.client_div.removeChild(
507            document.getElementById('trophyimlog'));
508        } else {
509            var logging_div = null;
510        }
511        while(TrophyIM.client_div.childNodes.length > 0) {
512            TrophyIM.client_div.removeChild(TrophyIM.client_div.firstChild);
513        }
514        return logging_div;
515    },
516    /** Function: onVersion
517     *
518     *  jabber:iq:version query handler
519     */
520    onVersion : function(msg) {
521        Strophe.debug("Version handler");
522        if (msg.getAttribute('type') == 'get') {
523            var from = msg.getAttribute('from');
524            var to = msg.getAttribute('to');
525            var id = msg.getAttribute('id');
526            var reply = $iq({type: 'result', to: from, from: to, id: id}).c('query',
527            {name: "TrophyIM", version: TROPHYIM_VERSION, os:
528            "Javascript-capable browser"});
529            TrophyIM.connection.send(reply.tree());
530        }
531        return true;
532    },
533    /** Function: onRoster
534     *
535     *  Roster iq handler
536     */
537    onRoster : function(msg) {
538        Strophe.debug("Roster handler");
539        var roster_items = msg.firstChild.getElementsByTagName('item');
540        for (var i = 0; i < roster_items.length; i++) {
541            var groups = roster_items[i].getElementsByTagName('group');
542            var group_array = new Array();
543            for (var g = 0; g < groups.length; g++) {
544                group_array[group_array.length] =
545                groups[g].firstChild.nodeValue;
546            }
547            TrophyIM.rosterObj.addContact(roster_items[i].getAttribute('jid'),
548            roster_items[i].getAttribute('subscription'),
549            roster_items[i].getAttribute('name'), group_array);
550        }
551        if (msg.getAttribute('type') == 'set') {
552            TrophyIM.connection.send($iq({type: 'reply', id:
553            msg.getAttribute('id'), to: msg.getAttribute('from')}).tree());
554        }
555        return true;
556    },
557    /** Function: onPresence
558     *
559     *  Presence handler
560     */
561    onPresence : function(msg)
562        {
563                Strophe.debug("Presence handler");
564        var type = msg.getAttribute('type') ? msg.getAttribute('type') : 'available';
565        var show = msg.getElementsByTagName('show').length ? Strophe.getText(msg.getElementsByTagName('show')[0]) : type;
566        var status = msg.getElementsByTagName('status').length ? Strophe.getText(msg.getElementsByTagName('status')[0]) : '';
567        var priority = msg.getElementsByTagName('priority').length ? parseInt(Strophe.getText(msg.getElementsByTagName('priority')[0])) : 0;
568                TrophyIM.rosterObj.setPresence(msg.getAttribute('from'), priority, show, status);
569               
570        return true;
571    },
572    /** Function: onMessage
573     *
574     *  Message handler
575     */
576    onMessage : function(msg)
577    {
578        Strophe.debug("Message handler");
579   
580        var from = msg.getAttribute('from');
581        var type = msg.getAttribute('type');
582        var elems = msg.getElementsByTagName('body');
583
584        if ( (type == 'chat' || type == 'normal') && elems.length > 0 )
585        {
586            var barejid         = Strophe.getBareJidFromJid(from);
587            var jid_lower       = barejid.toLowerCase();
588            var contact = "";
589               
590            if( TrophyIM.rosterObj.roster[barejid.toLowerCase()]['contact']['name'] )
591            {
592                contact = TrophyIM.rosterObj.roster[barejid.toLowerCase()]['contact']['name'];
593            }
594            else
595            {
596                contact = barejid.toLowerCase();
597                contact = contact.substring(0, contact.indexOf('@'));
598            }   
599           
600            var message =
601            {
602                contact : "<font style='font-weight:bold; color:black;'>" + contact + "</font>",
603                msg             : Strophe.getText(elems[0])     
604            };
605
606            TrophyIM.makeChat(from); //Make sure we have a chat window
607            TrophyIM.addMessage(message, jid_lower);
608        }
609        return true;
610    },
611    /** Function: makeChat
612     *
613     *  Make sure chat window to given fulljid exists, switching chat context to
614     *  given resource
615     */
616    makeChat : function(fulljid)
617    {
618       
619        var barjid = Strophe.getBareJidFromJid(fulljid);
620       
621        var paramsChatBox =
622        {
623                        'idChatBox' : barjid + "__chatBox",
624                        'jidTo'         : barjid,
625        };
626       
627        var winChatBox =
628        {
629                         id_window              : "window_chat_area_" + barjid,
630                         width                  : 387,
631                         height                 : 365,
632                         top                    : 100,
633                         left                   : 400,
634                         draggable              : true,
635                         visible                : "display",
636                         resizable              : true,
637                         zindex                 : loadIM.getZIndex(),
638                         title                  : barjid.substring(0, barjid.indexOf('@')),
639                         closeAction    : "hidden",
640                         content                : loadIM.parse("chat_box","chatBox.xsl", paramsChatBox)
641        }
642       
643        _winBuild(winChatBox)
644               
645                loadIM.configEvents(
646                        document.getElementById( barjid + '__sendBox'),
647                        'onkeyup', function(e)
648                        {
649                                if( e.keyCode == 13 )
650                                {
651                                        TrophyIM.sendMessage( barjid );
652                                        document.getElementById( barjid + '__sendBox').value = '';
653                                        return false;
654                                }
655                        }
656                );
657    },
658
659    /** Function: addMessage
660     *
661     *  Parameters:
662     *    (string) msg - the message to add
663     *    (string) jid - the jid of chat box to add the message to.
664     */
665       
666    addMessage : function(msg, jid)
667    {
668        var chatBox             = document.getElementById(jid + "__chatBox");
669        var messageDiv  = document.createElement("div");
670       
671                messageDiv.style.margin = "3px 0px 3px 3px";
672        messageDiv.innerHTML    = msg.contact + " : " + msg.msg ;
673               
674        chatBox.appendChild(messageDiv);
675        chatBox.scrollTop = chatBox.scrollHeight;
676    },
677       
678    /** Function: renderRoster
679     *
680     *  Renders roster, looking only for jids flagged by setPresence as having
681     *  changed.
682     */
683   
684        renderRoster : function()
685        {
686                if( TrophyIM.rosterObj.changes.length > 0 )
687                {
688                        var roster_div = document.getElementById('trophyimroster');
689                       
690                        if( roster_div )
691                        {
692                                var groups = new Array();
693                               
694                for (var group in TrophyIM.rosterObj.groups)
695                                {
696                                        groups[groups.length] = ( group ) ? group : "SEM GRUPO";
697                }
698               
699                                groups.sort();
700
701                                for ( var i = 0; i < groups.length; i++ )
702                                {
703                                       
704                                        TrophyIM.renderGroups( groups[i] , roster_div );       
705                                }
706                               
707                                TrophyIM.renderItensGroup( roster_div );
708                        }
709                }
710                       
711                setTimeout("TrophyIM.renderRoster()", 1000 );           
712        },
713
714    /** Function: renderGroups
715     *
716     *
717     */
718       
719        renderGroups: function( nameGroup, elementParent )
720        {
721                var _addGroup = function()
722                {
723                        var paramsGroup =
724                        {
725                                'nameGroup' : arguments[0],
726                                'id'            : (( arguments[0] == "SEM GRUPO" ) ? "sem_grupo_jabber" : "" )
727                        }
728                       
729                        elementParent.innerHTML += loadIM.parse("group","groups.xsl", paramsGroup);
730                }
731               
732                if( !elementParent.hasChildNodes() )
733                {
734                        _addGroup(nameGroup);   
735                }
736                else
737                {
738                        var elementChild = elementParent.firstChild;
739                        var flag = false;
740                       
741                        while ( elementChild )
742                        {
743                                if ( elementChild.childNodes[0].childNodes[0].nodeValue === nameGroup )
744                                {
745                                        flag = true;   
746                                }
747                                elementChild = elementChild.nextSibling;
748                        }
749                       
750                        if( !flag )
751                                _addGroup(nameGroup);
752                }
753                       
754        },
755
756    /** Function: renderItensGroup
757     *
758     *
759     */
760
761        renderItensGroup : function( elementParent )
762        {
763                var _addItensGroup = function()
764                {
765                        if( arguments.length > 0 )
766                        {
767                                var objContact  = arguments[0];
768                                var element             = arguments[1];
769                                var itensGroup  = document.getElementById('itenContact_' + objContact.contact.jid);
770
771                                if ( itensGroup == null )
772                                {
773                                        // Name
774                                        var nameContact = "";                                   
775                                       
776                                        if (objContact.contact.name)
777                                                nameContact = objContact.contact.name;
778                                        else
779                                        {
780                                                nameContact = objContact.contact.jid;
781                                                nameContact = nameContact.substring(0, nameContact.indexOf('@'));
782                                        }
783                                       
784                                        // Presence
785                                        var presence = 'unavailable';
786                                       
787                                        if (objContact.presence)
788                                        {
789                                                for (var resource in objContact.presence)
790                                                {
791                                                        presence = objContact.presence[resource].show;
792                                                }
793                                        }
794                                       
795                                        var paramsContact =
796                                        {
797                                                'nameContact' : nameContact,
798                                                'jid'             : objContact.contact.jid,     
799                                                'id'              : 'itenContact_' + objContact.contact.jid,
800                                                'presence'        : presence
801                                        }
802                                       
803                                        element.innerHTML += loadIM.parse("itens_group", "itensGroup.xsl", paramsContact);
804                                }
805                                else
806                                {
807                                        // Presence
808                                        var presence = 'unavailable';
809                                       
810                                        if (objContact.presence)
811                                        {
812                                                for (var resource in objContact.presence)
813                                                        presence = objContact.presence[resource].show;
814                                        }
815
816                                        itensGroup.style.background = "url('images/" + presence + ".gif')no-repeat center left";
817                                }
818                        }
819                }
820               
821                for ( var userItem in TrophyIM.rosterObj.roster)
822                {
823                        if ( TrophyIM.rosterObj.roster[userItem].contact.groups )
824                        {
825                                var elementChild = elementParent.firstChild
826                               
827                                while ( elementChild )
828                                {
829                                        if( elementChild.childNodes[0].childNodes[0].nodeValue == TrophyIM.rosterObj.roster[userItem].contact.groups )
830                                                _addItensGroup( TrophyIM.rosterObj.roster[userItem], elementChild );
831                                               
832                                        elementChild = elementChild.nextSibling;
833                                }
834                        }
835                        else
836                        {
837                                var elementChild = document.getElementById("sem_grupo_jabber");
838                               
839                                _addItensGroup( TrophyIM.rosterObj.roster[userItem], elementChild );
840                        }
841                }
842        },
843       
844    /** Function: rosterClick
845     *
846     *  Handles actions when a roster item is clicked
847     */
848   
849        rosterClick : function(fulljid)
850        {
851        TrophyIM.makeChat(fulljid);
852    },
853
854        setPresence : function()
855        {
856                if( arguments.length > 0 )
857                {
858                        TrophyIM.connection.send($pres( ).c('show').t(arguments[0]));
859                }
860        },
861       
862        /** Function: sendMessage
863     *
864     *  Send message from chat input to user
865     */
866     
867    sendMessage : function()
868    {
869
870        if( arguments.length > 0 )
871        {
872                var jidTo = arguments[0];
873                var message_input = document.getElementById(jidTo + "__sendBox").value;
874           
875                if( ( message_input = message_input.replace(/^\s+|\s+$|^\n|\n$/g,"") ) != "" )
876                {
877                        // Send Message
878                        TrophyIM.connection.send($msg({to: jidTo, from: TrophyIM.connection.jid, type: 'chat'}).c('body').t(message_input).tree());
879                       
880                        var message =
881                        {
882                                        contact : "<font style='font-weight:bold; color:red;'>" + "Eu" + "</font>",
883                                        msg : message_input
884                        }
885                       
886                        // Add Message in chatBox;
887                        TrophyIM.addMessage( message, jidTo);
888                                document.getElementById(jidTo + "__sendBox").value = "";
889                                document.getElementById(jidTo + "__sendBox").focus();
890                }
891        }
892    }
893};
894
895/** Class: TrophyIMRoster
896 *
897 *
898 *  This object stores the roster and presence info for the TrophyIMClient
899 *
900 *  roster[jid_lower]['contact']
901 *  roster[jid_lower]['presence'][resource]
902 */
903function TrophyIMRoster()
904{
905    /** Constants: internal arrays
906     *    (Object) roster - the actual roster/presence information
907     *    (Object) groups - list of current groups in the roster
908     *    (Array) changes - array of jids with presence changes
909     */
910    if (TrophyIM.JSONStore.store_working)
911        {
912        var data = TrophyIM.JSONStore.getData(['roster', 'groups']);
913        this.roster = (data['roster'] != null) ? data['roster'] : {};
914        this.groups = (data['groups'] != null) ? data['groups'] : {};
915    }
916        else
917        {
918        this.roster = {};
919        this.groups = {};
920    }
921    this.changes = new Array();
922   
923        if (TrophyIM.constants.stale_roster)
924        {
925        for (var jid in this.roster)
926                {
927                        this.changes[this.changes.length] = jid;
928        }
929    }
930       
931    /** Function: addContact
932     *
933     *  Adds given contact to roster
934     *
935     *  Parameters:
936     *    (String) jid - bare jid
937     *    (String) subscription - subscription attribute for contact
938     *    (String) name - name attribute for contact
939     *    (Array) groups - array of groups contact is member of
940     */
941   
942        this.addContact = function(jid, subscription, name, groups) {
943        var contact = {jid:jid, subscription:subscription, name:name, groups:groups}
944        var jid_lower = jid.toLowerCase();
945        if (this.roster[jid_lower]) {
946            this.roster[jid_lower]['contact'] = contact;
947        } else {
948            this.roster[jid_lower] = {contact:contact};
949        }
950        groups = groups ? groups : [''];
951        for (var g = 0; g < groups.length; g++) {
952            if (!this.groups[groups[g]]) {
953                this.groups[groups[g]] = {};
954            }
955            this.groups[groups[g]][jid_lower] = jid_lower;
956        }
957    }
958    /** Function: getContact
959     *
960     *  Returns contact entry for given jid
961     *
962     *  Parameter: (String) jid - jid to return
963     */
964    this.getContact = function(jid) {
965        if (this.roster[jid.toLowerCase()]) {
966            return this.roster[jid.toLowerCase()]['contact'];
967        }
968    }
969    /** Function: setPresence
970     *
971     *  Sets presence
972     *
973     *  Parameters:
974     *    (String) fulljid: full jid with presence
975     *    (Integer) priority: priority attribute from presence
976     *    (String) show: show attribute from presence
977     *    (String) status: status attribute from presence
978     */
979    this.setPresence = function(fulljid, priority, show, status)
980        {
981                var barejid = Strophe.getBareJidFromJid(fulljid);
982        var resource = Strophe.getResourceFromJid(fulljid);
983        var jid_lower = barejid.toLowerCase();
984       
985                if( show != 'unavailable')
986                {
987            if (!this.roster[jid_lower])
988                        {
989                this.addContact(barejid, 'not-in-roster');
990            }
991            var presence =
992                        {
993                resource:resource, priority:priority, show:show, status:status
994            }
995           
996                        if (!this.roster[jid_lower]['presence'])
997                        {
998                this.roster[jid_lower]['presence'] = {}
999            }
1000            this.roster[jid_lower]['presence'][resource] = presence
1001        }
1002                else if (this.roster[jid_lower] && this.roster[jid_lower]['presence'] && this.roster[jid_lower]['presence'][resource])
1003                {
1004            delete this.roster[jid_lower]['presence'][resource];
1005        }
1006       
1007                this.addChange(jid_lower);
1008       
1009                if (TrophyIM.activeChats['divs'][jid_lower])
1010                {
1011            TrophyIM.setTabPresence(jid_lower, TrophyIM.activeChats['divs'][jid_lower]['tab']);
1012        }
1013    }
1014    /** Function: addChange
1015     *
1016     *  Adds given jid to this.changes, keeping this.changes sorted and
1017     *  preventing duplicates.
1018     *
1019     *  Parameters
1020     *    (String) jid : jid to add to this.changes
1021     */
1022    this.addChange = function(jid) {
1023        for (var c = 0; c < this.changes.length; c++) {
1024            if (this.changes[c] == jid) {
1025                return;
1026            }
1027        }
1028        this.changes[this.changes.length] = jid;
1029        this.changes.sort();
1030    }
1031    /** Function: getPresence
1032     *
1033     *  Returns best presence for given jid as Array(resource, priority, show,
1034     *  status)
1035     *
1036     *  Parameter: (String) fulljid - jid to return best presence for
1037     */
1038    this.getPresence = function(fulljid) {
1039        var jid = Strophe.getBareJidFromJid(fulljid);
1040        var current = null;
1041        if (this.roster[jid.toLowerCase()] &&
1042        this.roster[jid.toLowerCase()]['presence']) {
1043            for (var resource in this.roster[jid.toLowerCase()]['presence']) {
1044                var presence = this.roster[jid.toLowerCase()]['presence'][resource];
1045                if (current == null) {
1046                    current = presence
1047                } else {
1048                    if(presence['priority'] > current['priority'] && ((presence['show'] == "chat"
1049                    || presence['show'] == "available") || (current['show'] != "chat" ||
1050                    current['show'] != "available"))) {
1051                        current = presence
1052                    }
1053                }
1054            }
1055        }
1056        return current;
1057    }
1058    /** Function: groupHasChanges
1059     *
1060     *  Returns true if current group has members in this.changes
1061     *
1062     *  Parameters:
1063     *    (String) group - name of group to check
1064     */
1065    this.groupHasChanges = function(group) {
1066        for (var c = 0; c < this.changes.length; c++) {
1067            if (this.groups[group][this.changes[c]]) {
1068                return true;
1069            }
1070        }
1071        return false;
1072    }
1073    /** Fuction: save
1074     *
1075     *  Saves roster data to JSON store
1076     */
1077    this.save = function() {
1078        if (TrophyIM.JSONStore.store_working) {
1079            TrophyIM.JSONStore.setData({roster:this.roster,
1080            groups:this.groups, active_chat:TrophyIM.activeChats['current'],
1081            chat_history:TrophyIM.chatHistory});
1082        }
1083    }
1084}
1085/** Class: TrophyIMJSONStore
1086 *
1087 *
1088 *  This object is the mechanism by which TrophyIM stores and retrieves its
1089 *  variables from the url provided by TROPHYIM_JSON_STORE
1090 *
1091 */
1092function TrophyIMJSONStore() {
1093    this.store_working = false;
1094    /** Function _newXHR
1095     *
1096     *  Set up new cross-browser xmlhttprequest object
1097     *
1098     *  Parameters:
1099     *    (function) handler = what to set onreadystatechange to
1100     */
1101     this._newXHR = function (handler) {
1102        var xhr = null;
1103        if (window.XMLHttpRequest) {
1104            xhr = new XMLHttpRequest();
1105            if (xhr.overrideMimeType) {
1106            xhr.overrideMimeType("text/xml");
1107            }
1108        } else if (window.ActiveXObject) {
1109            xhr = new ActiveXObject("Microsoft.XMLHTTP");
1110        }
1111        return xhr;
1112    }
1113    /** Function getData
1114     *  Gets data from JSONStore
1115     *
1116     *  Parameters:
1117     *    (Array) vars = Variables to get from JSON store
1118     *
1119     *  Returns:
1120     *    Object with variables indexed by names given in parameter 'vars'
1121     */
1122    this.getData = function(vars) {
1123        if (typeof(TROPHYIM_JSON_STORE) != undefined) {
1124            Strophe.debug("Retrieving JSONStore data");
1125            var xhr = this._newXHR();
1126            var getdata = "get=" + vars.join(",");
1127            try {
1128                xhr.open("POST", TROPHYIM_JSON_STORE, false);
1129            } catch (e) {
1130                Strophe.error("JSONStore open failed.");
1131                return false;
1132            }
1133            xhr.setRequestHeader('Content-type',
1134            'application/x-www-form-urlencoded');
1135            xhr.setRequestHeader('Content-length', getdata.length);
1136            xhr.send(getdata);
1137            if (xhr.readyState == 4 && xhr.status == 200) {
1138                try {
1139                    var dataObj = JSON.parse(xhr.responseText);
1140                    return this.emptyFix(dataObj);
1141                } catch(e) {
1142                    Strophe.error("Could not parse JSONStore response" +
1143                    xhr.responseText);
1144                    return false;
1145                }
1146            } else {
1147                Strophe.error("JSONStore open failed. Status: " + xhr.status);
1148                return false;
1149            }
1150        }
1151    }
1152    /** Function emptyFix
1153     *    Fix for bugs in external JSON implementations such as
1154     *    http://bugs.php.net/bug.php?id=41504.
1155     *    A.K.A. Don't use PHP, people.
1156     */
1157    this.emptyFix = function(obj) {
1158        if (typeof(obj) == "object") {
1159            for (var i in obj) {
1160                if (i == '_empty_') {
1161                    obj[""] = this.emptyFix(obj['_empty_']);
1162                    delete obj['_empty_'];
1163                } else {
1164                    obj[i] = this.emptyFix(obj[i]);
1165                }
1166            }
1167        }
1168        return obj
1169    }
1170    /** Function delData
1171     *    Deletes data from JSONStore
1172     *
1173     *  Parameters:
1174     *    (Array) vars  = Variables to delete from JSON store
1175     *
1176     *  Returns:
1177     *    Status of delete attempt.
1178     */
1179    this.delData = function(vars) {
1180        if (typeof(TROPHYIM_JSON_STORE) != undefined) {
1181            Strophe.debug("Retrieving JSONStore data");
1182            var xhr = this._newXHR();
1183            var deldata = "del=" + vars.join(",");
1184            try {
1185                xhr.open("POST", TROPHYIM_JSON_STORE, false);
1186            } catch (e) {
1187                Strophe.error("JSONStore open failed.");
1188                return false;
1189            }
1190            xhr.setRequestHeader('Content-type',
1191            'application/x-www-form-urlencoded');
1192            xhr.setRequestHeader('Content-length', deldata.length);
1193            xhr.send(deldata);
1194            if (xhr.readyState == 4 && xhr.status == 200) {
1195                try {
1196                    var dataObj = JSON.parse(xhr.responseText);
1197                    return dataObj;
1198                } catch(e) {
1199                    Strophe.error("Could not parse JSONStore response");
1200                    return false;
1201                }
1202            } else {
1203                Strophe.error("JSONStore open failed. Status: " + xhr.status);
1204                return false;
1205            }
1206        }
1207    }
1208    /** Function setData
1209     *    Stores data in JSONStore, overwriting values if they exist
1210     *
1211     *  Parameters:
1212     *    (Object) vars : Object containing named vars to store ({name: value,
1213     *    othername: othervalue})
1214     *
1215     *  Returns:
1216     *    Status of storage attempt
1217     */
1218    this.setData = function(vars) {
1219        if (typeof(TROPHYIM_JSON_STORE) != undefined) {
1220            Strophe.debug("Storing JSONStore data");
1221            var senddata = "set=" + JSON.stringify(vars);
1222            var xhr = this._newXHR();
1223            try {
1224                xhr.open("POST", TROPHYIM_JSON_STORE, false);
1225            } catch (e) {
1226                Strophe.error("JSONStore open failed.");
1227                return false;
1228            }
1229            xhr.setRequestHeader('Content-type',
1230            'application/x-www-form-urlencoded');
1231            xhr.setRequestHeader('Content-length', senddata.length);
1232            xhr.send(senddata);
1233            if (xhr.readyState == 4 && xhr.status == 200 && xhr.responseText ==
1234            "OK") {
1235                return true;
1236            } else {
1237                Strophe.error("JSONStore open failed. Status: " + xhr.status);
1238                return false;
1239            }
1240        }
1241    }
1242    var testData = true;
1243    if (this.setData({testData:testData})) {
1244        var testResult = this.getData(['testData']);
1245        if (testResult && testResult['testData'] == true) {
1246            this.store_working = true;
1247        }
1248    }
1249}
1250/** Constants: Node types
1251 *
1252 * Implementations of constants that IE doesn't have, but we need.
1253 */
1254if (document.ELEMENT_NODE == null) {
1255    document.ELEMENT_NODE = 1;
1256    document.ATTRIBUTE_NODE = 2;
1257    document.TEXT_NODE = 3;
1258    document.CDATA_SECTION_NODE = 4;
1259    document.ENTITY_REFERENCE_NODE = 5;
1260    document.ENTITY_NODE = 6;
1261    document.PROCESSING_INSTRUCTION_NODE = 7;
1262    document.COMMENT_NODE = 8;
1263    document.DOCUMENT_NODE = 9;
1264    document.DOCUMENT_TYPE_NODE = 10;
1265    document.DOCUMENT_FRAGMENT_NODE = 11;
1266    document.NOTATION_NODE = 12;
1267}
1268
1269/** Function: importNode
1270 *
1271 *  document.importNode implementation for IE, which doesn't have importNode
1272 *
1273 *  Parameters:
1274 *    (Object) node - dom object
1275 *    (Boolean) allChildren - import node's children too
1276 */
1277if (!document.importNode) {
1278    document.importNode = function(node, allChildren) {
1279        switch (node.nodeType) {
1280            case document.ELEMENT_NODE:
1281                var newNode = document.createElement(node.nodeName);
1282                if (node.attributes && node.attributes.length > 0) {
1283                    for(var i = 0; i < node.attributes.length; i++) {
1284                        newNode.setAttribute(node.attributes[i].nodeName,
1285                        node.getAttribute(node.attributes[i].nodeName));
1286                    }
1287                }
1288                if (allChildren && node.childNodes &&
1289                node.childNodes.length > 0) {
1290                    for (var i = 0; i < node.childNodes.length; i++) {
1291                        newNode.appendChild(document.importNode(
1292                        node.childNodes[i], allChildren));
1293                    }
1294                }
1295                return newNode;
1296                break;
1297            case document.TEXT_NODE:
1298            case document.CDATA_SECTION_NODE:
1299            case document.COMMENT_NODE:
1300                return document.createTextNode(node.nodeValue);
1301                break;
1302        }
1303    };
1304}
1305
1306/** Function: getElementsByClassName
1307 *
1308 *  DOMObject.getElementsByClassName implementation for browsers that don't
1309 *  support it yet.
1310 *
1311 *  Developed by Robert Nyman, http://www.robertnyman.com
1312 *  Code/licensing: http://code.google.com/p/getelementsbyclassname/
1313*/
1314var getElementsByClassName = function (className, tag, elm){
1315    if (document.getElementsByClassName) {
1316        getElementsByClassName = function (className, tag, elm) {
1317            elm = elm || document;
1318            var elements = elm.getElementsByClassName(className),
1319                nodeName = (tag)? new RegExp("\\b" + tag + "\\b", "i") : null,
1320                returnElements = [],
1321                current;
1322            for(var i=0, il=elements.length; i<il; i+=1){
1323                current = elements[i];
1324                if(!nodeName || nodeName.test(current.nodeName)) {
1325                    returnElements.push(current);
1326                }
1327            }
1328            return returnElements;
1329        };
1330    } else if (document.evaluate) {
1331        getElementsByClassName = function (className, tag, elm) {
1332            tag = tag || "*";
1333            elm = elm || document;
1334            var classes = className.split(" "),
1335                classesToCheck = "",
1336                xhtmlNamespace = "http://www.w3.org/1999/xhtml",
1337                namespaceResolver = (document.documentElement.namespaceURI ===
1338                    xhtmlNamespace)? xhtmlNamespace : null,
1339                returnElements = [],
1340                elements,
1341                node;
1342            for(var j=0, jl=classes.length; j<jl; j+=1){
1343                classesToCheck += "[contains(concat(' ', @class, ' '), ' " +
1344                    classes[j] + " ')]";
1345            }
1346            try {
1347                elements = document.evaluate(".//" + tag + classesToCheck,
1348                    elm, namespaceResolver, 0, null);
1349            } catch (e) {
1350                elements = document.evaluate(".//" + tag + classesToCheck,
1351                    elm, null, 0, null);
1352            }
1353            while ((node = elements.iterateNext())) {
1354                returnElements.push(node);
1355            }
1356            return returnElements;
1357        };
1358    } else {
1359        getElementsByClassName = function (className, tag, elm) {
1360            tag = tag || "*";
1361            elm = elm || document;
1362            var classes = className.split(" "),
1363                classesToCheck = [],
1364                elements = (tag === "*" && elm.all)? elm.all :
1365                     elm.getElementsByTagName(tag),
1366                current,
1367                returnElements = [],
1368                match;
1369            for(var k=0, kl=classes.length; k<kl; k+=1){
1370                classesToCheck.push(new RegExp("(^|\\s)" + classes[k] +
1371                    "(\\s|$)"));
1372            }
1373            for(var l=0, ll=elements.length; l<ll; l+=1){
1374                current = elements[l];
1375                match = false;
1376                for(var m=0, ml=classesToCheck.length; m<ml; m+=1){
1377                    match = classesToCheck[m].test(current.className);
1378                    if (!match) {
1379                        break;
1380                    }
1381                }
1382                if (match) {
1383                    returnElements.push(current);
1384                }
1385            }
1386            return returnElements;
1387        };
1388    }
1389    return getElementsByClassName(className, tag, elm);
1390};
1391
1392/**
1393 *
1394 * Bootstrap self into window.onload and window.onunload
1395 */
1396var oldonload = window.onload;
1397window.onload = function() {
1398    if(oldonload) {
1399        oldonload();
1400    }
1401    TrophyIM.load();
1402};
1403var oldonunload = window.onunload;
1404window.onunload = function() {
1405    if(oldonunload) {
1406        oldonunload();
1407    }
1408    TrophyIM.storeData();
1409}
Note: See TracBrowser for help on using the repository browser.