Explorar o código

Keep a local customized copy of strophe.roster.js

The new changes made to strophe.roster.js are incompatible with the way
converse.js works.

Will likely replace strophe.roster.js completely.
JC Brand %!s(int64=10) %!d(string=hai) anos
pai
achega
7e4c1d6d8d
Modificáronse 3 ficheiros con 449 adicións e 3 borrados
  1. 1 2
      bower.json
  2. 1 1
      main.js
  3. 447 0
      src/strophe.roster.js

+ 1 - 2
bower.json

@@ -16,7 +16,6 @@
     "backbone.browserStorage": "*",
     "backbone.overview": "*",
     "strophe": "~1.1.3",
-    "strophe.roster": "https://raw.github.com/strophe/strophejs-plugins/b1f364eb6e854ffe844c57add38e885cfeb9b498/roster/strophe.roster.js",
     "strophe.muc": "https://raw.githubusercontent.com/strophe/strophejs-plugins/master/muc/strophe.muc.js",
     "otr": "0.2.12",
     "crypto-js-evanvosberg": "~3.1.2",
@@ -30,7 +29,7 @@
     "bootstrapJS": "https://raw.githubusercontent.com/jcbrand/bootstrap/7d96a5f60d26c67b5348b270a775518b96a702c8/dist/js/bootstrap.js",
     "fontawesome": "~4.1.0",
     "typeahead.js": "https://raw.githubusercontent.com/jcbrand/typeahead.js/eedfb10505dd3a20123d1fafc07c1352d83f0ab3/dist/typeahead.jquery.js",
-    "strophejs-plugins": "~0.0.4"
+    "strophejs-plugins": "git@github.com:strophe/strophejs-plugins.git#a56421ff4ecf0807113ab48c46728715597df599"
   },
   "exportsOverride": {}
 }

+ 1 - 1
main.js

@@ -17,7 +17,7 @@ config = {
         "strophe":                  "components/strophe/strophe",
         "strophe.disco":            "components/strophejs-plugins/disco/strophe.disco",
         "strophe.muc":              "components/strophe.muc/index",
-        "strophe.roster":           "components/strophe.roster/index",
+        "strophe.roster":           "src/strophe.roster",
         "strophe.vcard":            "components/strophejs-plugins/vcard/strophe.vcard",
         "text":                     'components/requirejs-text/text',
         "tpl":                      'components/requirejs-tpl-jcbrand/tpl',

+ 447 - 0
src/strophe.roster.js

@@ -0,0 +1,447 @@
+/*
+  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)
+ */
+Strophe.addConnectionPlugin('roster',
+{
+    /** Function: init
+     * Plugin init
+     *
+     * Parameters:
+     *   (Strophe.Connection) conn - Strophe connection
+     */
+    init: function(conn)
+    {
+        this._connection = conn;
+        this._callbacks = [];
+        /** Property: items
+         *  Roster items
+         *  [
+         *    {
+         *        name         : "",
+         *        jid          : "",
+         *        subscription : "",
+         *        ask          : "",
+         *        groups       : ["", ""],
+         *        resources    : {
+         *            myresource : {
+         *                show   : "",
+         *                status : "",
+         *                priority : ""
+         *            }
+         *        }
+         *    }
+         * ]
+         */
+        this.items = [];
+        /** Property: ver
+        * current roster revision
+        * always null if server doesn't support xep 237
+        */
+        this.ver = null;
+        // 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 (typeof oldCallback === "function") {
+                oldCallback.apply(this, arguments);
+            }
+        };
+        conn.connect = function(jid, pass, callback, wait, hold)
+        {
+            oldCallback = callback;
+            if (typeof jid  == "undefined")
+                jid  = null;
+            if (typeof pass == "undefined")
+                pass = null;
+            callback = newCallback;
+            _connect.apply(conn, [jid, pass, callback, wait, hold]);
+        };
+        conn.attach = function(jid, sid, rid, callback, wait, hold, wind)
+        {
+            oldCallback = callback;
+            if (typeof jid == "undefined")
+                jid = null;
+            if (typeof sid == "undefined")
+                sid = null;
+            if (typeof rid == "undefined")
+                rid = null;
+            callback = newCallback;
+            _attach.apply(conn, [jid, sid, rid, callback, wait, hold, wind]);
+        };
+
+        Strophe.addNamespace('ROSTER_VER', 'urn:xmpp:features:rosterver');
+        Strophe.addNamespace('NICK', 'http://jabber.org/protocol/nick');
+    },
+    /** 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};
+        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);
+        return 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)
+    {
+        try {
+            for (var i = 0; i < this.items.length; i++)
+            {
+                if (this.items[i] && this.items[i].jid == jid)
+                {
+                    return this.items[i];
+                }
+            }
+        } catch (e)
+        {
+            Strophe.error(e);
+        }
+        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 (optional)
+     *     (String) nick  (optional)
+     */
+    subscribe: function(jid, message, nick) {
+        var pres = $pres({to: jid, type: "subscribe"});
+        if (message && message !== "") {
+            pres.c("status").t(message).up();
+        }
+        if (nick && nick !== "") {
+            pres.c('nick', {'xmlns': Strophe.NS.NICK}).t(nick).up();
+        }
+        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();
+        }
+        return 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);
+        if (typeof userCallback === "function") {
+            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;
+        }
+
+        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;
+        }
+    }
+});