123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452 |
- /*
- Copyright 2010, François de Metz <francois@2metz.fr>
- */
- /**
- * Roster Plugin
- * Allow easily roster management
- *
- * Features
- * * Get roster from server
- * * handle presence
- * * handle roster iq
- * * subscribe/unsubscribe
- * * authorize/unauthorize
- * * roster versioning (xep 237)
- */
- // AMD/global registrations
- (function (root, factory) {
- if (typeof define === 'function' && define.amd) {
- define([
- "Libraries/strophe"
- ], function () {
- return factory(jQuery, console);
- }
- );
- }
- }(this, function ($, console) {
- Strophe.addConnectionPlugin('roster',
- {
- _connection: null,
- _callbacks : [],
- /** Property: items
- * Roster items
- * [
- * {
- * name : "",
- * jid : "",
- * subscription : "",
- * ask : "",
- * groups : ["", ""],
- * resources : {
- * myresource : {
- * show : "",
- * status : "",
- * priority : ""
- * }
- * }
- * }
- * ]
- */
- items : [],
- /** Property: ver
- * current roster revision
- * always null if server doesn't support xep 237
- */
- ver : null,
- /** Function: init
- * Plugin init
- *
- * Parameters:
- * (Strophe.Connection) conn - Strophe connection
- */
- init: function(conn)
- {
- this._connection = conn;
- this.items = [];
- // Override the connect and attach methods to always add presence and roster handlers.
- // They are removed when the connection disconnects, so must be added on connection.
- var oldCallback, roster = this, _connect = conn.connect, _attach = conn.attach;
- var newCallback = function(status)
- {
- if (status == Strophe.Status.ATTACHED || status == Strophe.Status.CONNECTED)
- {
- try
- {
- // Presence subscription
- conn.addHandler(roster._onReceivePresence.bind(roster), null, 'presence', null, null, null);
- conn.addHandler(roster._onReceiveIQ.bind(roster), Strophe.NS.ROSTER, 'iq', "set", null, null);
- }
- catch (e)
- {
- Strophe.error(e);
- }
- }
- if (oldCallback !== null)
- oldCallback.apply(this, arguments);
- };
- conn.connect = function(jid, pass, callback, wait, hold)
- {
- oldCallback = callback;
- if (typeof arguments[0] == "undefined")
- arguments[0] = null;
- if (typeof arguments[1] == "undefined")
- arguments[1] = null;
- arguments[2] = newCallback;
- _connect.apply(conn, arguments);
- };
- conn.attach = function(jid, sid, rid, callback, wait, hold, wind)
- {
- oldCallback = callback;
- if (typeof arguments[0] == "undefined")
- arguments[0] = null;
- if (typeof arguments[1] == "undefined")
- arguments[1] = null;
- if (typeof arguments[2] == "undefined")
- arguments[2] = null;
- arguments[3] = newCallback;
- _attach.apply(conn, arguments);
- };
- Strophe.addNamespace('ROSTER_VER', 'urn:xmpp:features:rosterver');
- },
- /** Function: supportVersioning
- * return true if roster versioning is enabled on server
- */
- supportVersioning: function()
- {
- return (this._connection.features && this._connection.features.getElementsByTagName('ver').length > 0);
- },
- /** Function: get
- * Get Roster on server
- *
- * Parameters:
- * (Function) userCallback - callback on roster result
- * (String) ver - current rev of roster
- * (only used if roster versioning is enabled)
- * (Array) items - initial items of ver
- * (only used if roster versioning is enabled)
- * In browser context you can use sessionStorage
- * to store your roster in json (JSON.stringify())
- */
- get: function(userCallback, ver, items)
- {
- var attrs = {xmlns: Strophe.NS.ROSTER};
- this.items = [];
- if (this.supportVersioning())
- {
- // empty rev because i want an rev attribute in the result
- attrs.ver = ver || '';
- this.items = items || [];
- }
- var iq = $iq({type: 'get', 'id' : this._connection.getUniqueId('roster')}).c('query', attrs);
- this._connection.sendIQ(iq,
- this._onReceiveRosterSuccess.bind(this, userCallback),
- this._onReceiveRosterError.bind(this, userCallback));
- },
- /** Function: registerCallback
- * register callback on roster (presence and iq)
- *
- * Parameters:
- * (Function) call_back
- */
- registerCallback: function(call_back)
- {
- this._callbacks.push(call_back);
- },
- /** Function: findItem
- * Find item by JID
- *
- * Parameters:
- * (String) jid
- */
- findItem : function(jid)
- {
- for (var i = 0; i < this.items.length; i++)
- {
- if (this.items[i] && this.items[i].jid == jid)
- {
- return this.items[i];
- }
- }
- return false;
- },
- /** Function: removeItem
- * Remove item by JID
- *
- * Parameters:
- * (String) jid
- */
- removeItem : function(jid)
- {
- for (var i = 0; i < this.items.length; i++)
- {
- if (this.items[i] && this.items[i].jid == jid)
- {
- this.items.splice(i, 1);
- return true;
- }
- }
- return false;
- },
- /** Function: subscribe
- * Subscribe presence
- *
- * Parameters:
- * (String) jid
- * (String) message
- */
- subscribe: function(jid, message)
- {
- var pres = $pres({to: jid, type: "subscribe"});
- if (message && message != "")
- pres.c("status").t(message);
- this._connection.send(pres);
- },
- /** Function: unsubscribe
- * Unsubscribe presence
- *
- * Parameters:
- * (String) jid
- * (String) message
- */
- unsubscribe: function(jid, message)
- {
- var pres = $pres({to: jid, type: "unsubscribe"});
- if (message && message != "")
- pres.c("status").t(message);
- this._connection.send(pres);
- },
- /** Function: authorize
- * Authorize presence subscription
- *
- * Parameters:
- * (String) jid
- * (String) message
- */
- authorize: function(jid, message)
- {
- var pres = $pres({to: jid, type: "subscribed"});
- if (message && message != "")
- pres.c("status").t(message);
- this._connection.send(pres);
- },
- /** Function: unauthorize
- * Unauthorize presence subscription
- *
- * Parameters:
- * (String) jid
- * (String) message
- */
- unauthorize: function(jid, message)
- {
- var pres = $pres({to: jid, type: "unsubscribed"});
- if (message && message != "")
- pres.c("status").t(message);
- this._connection.send(pres);
- },
- /** Function: add
- * Add roster item
- *
- * Parameters:
- * (String) jid - item jid
- * (String) name - name
- * (Array) groups
- * (Function) call_back
- */
- add: function(jid, name, groups, call_back)
- {
- var iq = $iq({type: 'set'}).c('query', {xmlns: Strophe.NS.ROSTER}).c('item', {jid: jid,
- name: name});
- for (var i = 0; i < groups.length; i++)
- {
- iq.c('group').t(groups[i]).up();
- }
- this._connection.sendIQ(iq, call_back, call_back);
- },
- /** Function: update
- * Update roster item
- *
- * Parameters:
- * (String) jid - item jid
- * (String) name - name
- * (Array) groups
- * (Function) call_back
- */
- update: function(jid, name, groups, call_back)
- {
- var item = this.findItem(jid);
- if (!item)
- {
- throw "item not found";
- }
- var newName = name || item.name;
- var newGroups = groups || item.groups;
- var iq = $iq({type: 'set'}).c('query', {xmlns: Strophe.NS.ROSTER}).c('item', {jid: item.jid,
- name: newName});
- for (var i = 0; i < newGroups.length; i++)
- {
- iq.c('group').t(newGroups[i]).up();
- }
- this._connection.sendIQ(iq, call_back, call_back);
- },
- /** Function: remove
- * Remove roster item
- *
- * Parameters:
- * (String) jid - item jid
- * (Function) call_back
- */
- remove: function(jid, call_back)
- {
- var item = this.findItem(jid);
- if (!item)
- {
- throw "item not found";
- }
- var iq = $iq({type: 'set'}).c('query', {xmlns: Strophe.NS.ROSTER}).c('item', {jid: item.jid,
- subscription: "remove"});
- this._connection.sendIQ(iq, call_back, call_back);
- },
- /** PrivateFunction: _onReceiveRosterSuccess
- *
- */
- _onReceiveRosterSuccess: function(userCallback, stanza)
- {
- this._updateItems(stanza);
- userCallback(this.items);
- },
- /** PrivateFunction: _onReceiveRosterError
- *
- */
- _onReceiveRosterError: function(userCallback, stanza)
- {
- userCallback(this.items);
- },
- /** PrivateFunction: _onReceivePresence
- * Handle presence
- */
- _onReceivePresence : function(presence)
- {
- // TODO: from is optional
- var jid = presence.getAttribute('from');
- var from = Strophe.getBareJidFromJid(jid);
- var item = this.findItem(from);
- // not in roster
- if (!item)
- {
- return true;
- }
- var type = presence.getAttribute('type');
- if (type == 'unavailable')
- {
- delete item.resources[Strophe.getResourceFromJid(jid)];
- }
- else if (!type)
- {
- // TODO: add timestamp
- item.resources[Strophe.getResourceFromJid(jid)] = {
- show : (presence.getElementsByTagName('show').length != 0) ? Strophe.getText(presence.getElementsByTagName('show')[0]) : "",
- status : (presence.getElementsByTagName('status').length != 0) ? Strophe.getText(presence.getElementsByTagName('status')[0]) : "",
- priority : (presence.getElementsByTagName('priority').length != 0) ? Strophe.getText(presence.getElementsByTagName('priority')[0]) : ""
- };
- }
- else
- {
- // Stanza is not a presence notification. (It's probably a subscription type stanza.)
- return true;
- }
- this._call_backs(this.items, item);
- return true;
- },
- /** PrivateFunction: _call_backs
- *
- */
- _call_backs : function(items, item)
- {
- for (var i = 0; i < this._callbacks.length; i++) // [].forEach my love ...
- {
- this._callbacks[i](items, item);
- }
- },
- /** PrivateFunction: _onReceiveIQ
- * Handle roster push.
- */
- _onReceiveIQ : function(iq)
- {
- var id = iq.getAttribute('id');
- var from = iq.getAttribute('from');
- // Receiving client MUST ignore stanza unless it has no from or from = user's JID.
- if (from && from != "" && from != this._connection.jid && from != Strophe.getBareJidFromJid(this._connection.jid))
- return true;
- var iqresult = $iq({type: 'result', id: id, from: this._connection.jid});
- this._connection.send(iqresult);
- this._updateItems(iq);
- return true;
- },
- /** PrivateFunction: _updateItems
- * Update items from iq
- */
- _updateItems : function(iq)
- {
- var query = iq.getElementsByTagName('query');
- if (query.length != 0)
- {
- this.ver = query.item(0).getAttribute('ver');
- var self = this;
- Strophe.forEachChild(query.item(0), 'item',
- function (item)
- {
- self._updateItem(item);
- }
- );
- }
- this._call_backs(this.items);
- },
- /** PrivateFunction: _updateItem
- * Update internal representation of roster item
- */
- _updateItem : function(item)
- {
- var jid = item.getAttribute("jid");
- var name = item.getAttribute("name");
- var subscription = item.getAttribute("subscription");
- var ask = item.getAttribute("ask");
- var groups = [];
- Strophe.forEachChild(item, 'group',
- function(group)
- {
- groups.push(Strophe.getText(group));
- }
- );
- if (subscription == "remove")
- {
- this.removeItem(jid);
- return;
- }
- var item = this.findItem(jid);
- if (!item)
- {
- this.items.push({
- name : name,
- jid : jid,
- subscription : subscription,
- ask : ask,
- groups : groups,
- resources : {}
- });
- }
- else
- {
- item.name = name;
- item.subscription = subscription;
- item.ask = ask;
- item.groups = groups;
- }
- }
- });
- }));
|