1 Xmpp4Js.Lang.namespace( "Xmpp4Js.Roster" );
  2 
  3 /**
  4  * @constructor
  5  */
  6 Xmpp4Js.Roster.PresenceManager = function() {
  7     /**
  8      * Map by jid => resource
  9      */
 10     this.map = {};
 11     /**
 12      * Map by jid
 13      */
 14     this.best = {};
 15     
 16     this.getBestImpl = new Xmpp4Js.Roster.PresenceManager.GetBestImpl();
 17 
 18     this.addEvents({
 19         update: true
 20     });
 21     
 22 }
 23 
 24 Xmpp4Js.Roster.PresenceManager.prototype = {
 25 
 26     /**
 27      * Set presence based on jid and resource, and clear best presence cache.
 28      * 
 29      * @param {Xmpp4Js.Packet.Presence} newPresence
 30      */
 31     update: function(newPresence) {
 32         var jid = newPresence.getFromJid().withoutResource().toString();
 33         var resource = newPresence.getFromJid().getResource();
 34         
 35         var type = newPresence.getType();
 36         
 37         if(type != "available" && type != "unavailable") {
 38             throw new Error("Invalid prsence type: " + type);
 39         }
 40         
 41         // If a map entry for JID doesn't exist, create it.
 42         if(this.map[jid]==undefined) { this.map[jid] = {}; }
 43 
 44         // update the new presence
 45         this.map[jid][resource] = newPresence;
 46         
 47         // fire the update event.
 48         this.fireEvent( "update", newPresence );
 49         
 50         delete this.best[jid];
 51     },
 52     /**
 53      * Get the presnce of a JID with a specific resource. Calls
 54      * getBest is resource is null.
 55      *
 56      * @return Xmpp4Js.Packet.Presence
 57      */
 58     get: function( jid, resource) {
 59         if( !resource ) {
 60             // find "best"
 61             return this.getBest(jid);
 62         }
 63         
 64         return this.map[jid] ? this.map[jid][resource] : undefined;
 65         
 66     },
 67     /**
 68      * Finds the presence with the "best" presence based on the algorithm 
 69      * implementation. In the future, it will be swappable. For now, 
 70      * see it.
 71      * 
 72      * @param {String} jid
 73      * @return Xmpp4Js.Packet.Presence
 74      */
 75     getBest: function( jid ) {
 76         var presenceList = this.map[jid];
 77         
 78         this.best[jid] = this.getBestImpl.getBest( presenceList );
 79         return this.best[jid];
 80     },
 81     
 82     /**
 83      * Remove a jid/resource combo. If resource is empty, all resources are removed.
 84      * @param {String} jid
 85      * @param {String} resource
 86      */
 87     remove: function(jid, resource) {
 88         if( this.map[jid] == undefined ) { return; }
 89     
 90         // remove all resources if none is specified
 91         if( !resource ) {
 92             for( var k in this.map[jid]) {
 93                 var mapResource = this.map[jid][k];
 94                 this.remove( jid, mapResource);
 95             }
 96             delete this.map[jid];
 97         } else {
 98             if( this.map[jid][resource] == undefined ) { return; }
 99             
100             this.fireEvent( "remove", this.map[jid][resource] );
101             delete this.map[jid][resource]; 
102         }
103     },
104     /**
105      * Listen to presence packets
106      * @param {Packet} packet
107      * 
108      * TODO ability to gracefully handle non-presence packets
109      */
110     presencePacketListener: function ( packet ) {
111         
112         try {
113             this.update( packet );
114         } catch(e) {
115             // a presence type that we don't care about (subscribe, unsubscribe, etc)
116         }
117     }
118 }
119 
120 Xmpp4Js.Lang.extend(Xmpp4Js.Roster.PresenceManager, Xmpp4Js.Event.EventProvider, Xmpp4Js.Roster.PresenceManager.prototype);
121 
122 
123 
124 /**
125  * @constructor
126  */
127 Xmpp4Js.Roster.PresenceManager.GetBestImpl  = function() {
128 
129 }
130 
131 Xmpp4Js.Roster.PresenceManager.GetBestImpl.prototype = {
132 
133     SHOW_WEIGHT: {
134         chat: 6,
135         normal: 5,
136         away: 4,
137         xa: 3,
138         dnd: 2 // always higher than unavailable
139     },
140     TYPE_WEIGHT: {
141         available: 5,
142         unavailable: 1
143     },
144 
145     /**
146      * Finds the presence with the "best" presence based on availability followed by 
147      * show followed by priority. Sets cache.
148      * 
149      * @param {String} jid
150      * @return Xmpp4Js.Packet.Presence
151      */
152     getBest: function( presenceList ) {
153         var bestPresence = undefined;
154         var bestWeight = 0;
155         
156         for(var k in presenceList) {
157             var presence = presenceList[k];
158             
159             // these return default values if empty.
160             var show = presence.getShow();
161             var type = presence.getType();
162             var priority = presence.getPriority();
163 
164             // calculate the weight of the presence for getBest
165             var weight = this.SHOW_WEIGHT[show] * this.TYPE_WEIGHT[type] * priority;
166             
167             //console.info( [show, type, priority]);
168             //console.info( weight+" > "+bestWeight+"="+(weight > bestWeight) );
169             
170             // use the weight determined in .update()
171             if( bestPresence == null || weight > bestWeight ) {
172                 bestPresence = presence;
173                 bestWeight = weight;
174             }
175         };
176         
177         return bestPresence;
178     }
179 }
180