Userbox implimented
authorDarren <darren@darrenwhitlen.com>
Thu, 19 Jul 2012 14:50:15 +0000 (15:50 +0100)
committerDarren <darren@darrenwhitlen.com>
Thu, 19 Jul 2012 14:50:15 +0000 (15:50 +0100)
client_backbone/index.html
client_backbone/model.js
client_backbone/style.css
client_backbone/view.js

index 4f9ecaa057a5c77a6b17c5d5ff7a4d48f1f7a97d..ea3a52a81e494590787e2940f40badde242140f3 100644 (file)
         </div>
     </div>
 
+    <script type="text/x-jquery-tmpl" id="tmpl_userbox">
+        <div class="userbox">
+            <a class="query">Message</a>
+            <a class="info">Info</a>
+        </div>
+    </script>
+
     <script src="jquery-1.7.1.min.js"></script>
     <script src="underscore-min.js"></script>
     <script src="backbone-git.js"></script>
index 3a523443789bc2334ab9e67f5d4106bc7d15a448..fdf19735313cf69bf0d87a6afd6a8c2cd6e9953e 100644 (file)
-/*jslint white:true, regexp: true, nomen: true, devel: true, undef: true, browser: true, continue: true, sloppy: true, forin: true, newcap: true, plusplus: true, maxerr: 50, indent: 4 */
-/*global kiwi */
-kiwi.model = {};
-
-kiwi.model.MemberList = Backbone.Collection.extend({
-    model: kiwi.model.Member,
-    comparator: function (a, b) {
-        var i, a_modes, b_modes, a_idx, b_idx, a_nick, b_nick;
-        var user_prefixes = kiwi.gateway.get('user_prefixes');
-        a_modes = a.get("modes");
-        b_modes = b.get("modes");
-        // Try to sort by modes first
-        if (a_modes.length > 0) {
-            // a has modes, but b doesn't so a should appear first
-            if (b_modes.length === 0) {
-                return -1;
-            }
-            a_idx = b_idx = -1;
-            // Compare the first (highest) mode
-            for (i = 0; i < user_prefixes.length; i++) {
-                if (user_prefixes[i].mode === a_modes[0]) {
-                    a_idx = i;
-                }
-            }
-            for (i = 0; i < user_prefixes.length; i++) {
-                if (user_prefixes[i].mode === b_modes[0]) {
-                    b_idx = i;
-                }
-            }
-            if (a_idx < b_idx) {
-                return -1;
-            } else if (a_idx > b_idx) {
-                return 1;
-            }
-            // If we get to here both a and b have the same highest mode so have to resort to lexicographical sorting
-
-        } else if (b_modes.length > 0) {
-            // b has modes but a doesn't so b should appear first
-            return 1;
-        }
-        a_nick = a.get("nick").toLocaleUpperCase();
-        b_nick = b.get("nick").toLocaleUpperCase();
-        // Lexicographical sorting
-        if (a_nick < b_nick) {
-            return -1;
-        } else if (a_nick > b_nick) {
-            return 1;
-        } else {
-            // This should never happen; both users have the same nick.
-            console.log('Something\'s gone wrong somewhere - two users have the same nick!');
-            return 0;
-        }
-    },
-    initialize: function (options) {
-        this.view = new kiwi.view.MemberList({"model": this});
-    },
-    getByNick: function (nick) {
-        return this.find(function (m) {
-            return nick === m.get("nick");
-        });
-    }
-});
-
-kiwi.model.Member = Backbone.Model.extend({
-    sortModes: function (modes) {
-        return modes.sort(function (a, b) {
-            var a_idx, b_idx, i;
-            var user_prefixes = kiwi.gateway.get('user_prefixes');
-
-            for (i = 0; i < user_prefixes.length; i++) {
-                if (user_prefixes[i].mode === a) {
-                    a_idx = i;
-                }
-            }
-            for (i = 0; i < user_prefixes.length; i++) {
-                if (user_prefixes[i].mode === b) {
-                    b_idx = i;
-                }
-            }
-            if (a_idx < b_idx) {
-                return -1;
-            } else if (a_idx > b_idx) {
-                return 1;
-            } else {
-                return 0;
-            }
-        });
-    },
-    initialize: function (attributes) {
-        var nick, modes, prefix;
-        nick = this.stripPrefix(this.get("nick"));
-
-        modes = this.get("modes");
-        modes = modes || [];
-        this.sortModes(modes);
-        this.set({"nick": nick, "modes": modes, "prefix": this.getPrefix(modes)}, {silent: true});
-    },
-    addMode: function (mode) {
-        var modes, prefix;
-        modes = this.get("modes");
-        modes.push(mode);
-        modes = this.sortModes(modes);
-        this.set({"prefix": this.getPrefix(modes), "modes": modes});
-    },
-    removeMode: function (mode) {
-        var modes, prefix;
-        modes = this.get("modes");
-        modes = _.reject(modes, function (m) {
-            return m === mode;
-        });
-        this.set({"prefix": this.getPrefix(modes), "modes": modes});
-    },
-    getPrefix: function (modes) {
-        var prefix = '';
-        var user_prefixes = kiwi.gateway.get('user_prefixes');
-
-        if (typeof modes[0] !== 'undefined') {
-            prefix = _.detect(user_prefixes, function (prefix) {
-                return prefix.mode === modes[0];
-            });
-            prefix = (prefix) ? prefix.symbol : '';
-        }
-        return prefix;
-    },
-    stripPrefix: function (nick) {
-        var tmp = nick, i, j, k;
-        var user_prefixes = kiwi.gateway.get('user_prefixes');
-        i = 0;
-
-        for (j = 0; j < nick.length; j++) {
-            for (k = 0; k < user_prefixes.length; k++) {
-                if (nick.charAt(j) === user_prefixes[k].symbol) {
-                    i++;
-                    break;
-                }
-            }
-        }
-
-        return tmp.substr(i);
-    }
-});
-
-kiwi.model.PanelList = Backbone.Collection.extend({
-    model: kiwi.model.Panel,
-    comparator: function (chan) {
-        return chan.get("name");
-    },
-    initialize: function () {
-        this.view = new kiwi.view.Tabs({"el": $('#toolbar .panellist')[0], "model": this});
-
-        // Automatically create a server tab
-        this.server = new kiwi.model.Server({'name': kiwi.gateway.get('name')});
-        kiwi.gateway.on('change:name', this.view.render, this.view);
-        this.add(this.server);
-
-        // Set the default view to the server tab
-        kiwi.current_panel = this.server;
-
-    },
-    getByName: function (name) {
-        return this.find(function (c) {
-            return name === c.get("name");
-        });
-    }
-});
-
-kiwi.model.Panel = Backbone.Model.extend({
-    initialize: function (attributes) {
-        var name = this.get("name") || "";
-        this.view = new kiwi.view.Panel({"model": this, "name": name});
-        this.set({
-            "scrollback": [],
-            "name": name
-        }, {"silent": true});
-
-        this.isChannel = false;
-    },
-
-    addMsg: function (nick, msg, type, opts) {
-        var message_obj, bs, d;
-
-        opts = opts || {};
-
-        // Time defaults to now
-        if (!opts || typeof opts.time === 'undefined') {
-            d = new Date();
-            opts.time = d.getHours().toString().lpad(2, "0") + ":" + d.getMinutes().toString().lpad(2, "0") + ":" + d.getSeconds().toString().lpad(2, "0");
-        }
-
-        // CSS style defaults to empty string
-        if (!opts || typeof opts.style === 'undefined') {
-            opts.style = '';
-        }
-
-        // Escape any HTML that may be in here
-        // This doesn't seem right to be here.. should be in view (??)
-        msg =  $('<div />').text(msg).html();
-
-        // Run through the plugins
-        message_obj = {"msg": msg, "time": opts.time, "nick": nick, "chan": this.get("name"), "type": type, "style": opts.style};
-        //tmp = kiwi.plugs.run('addmsg', message_obj);
-        if (!message_obj) {
-            return;
-        }
-
-        // The CSS class (action, topic, notice, etc)
-        if (typeof message_obj.type !== "string") {
-            message_obj.type = '';
-        }
-
-        // Make sure we don't have NaN or something
-        if (typeof message_obj.msg !== "string") {
-            message_obj.msg = '';
-        }
-
-        // Convert IRC formatting into HTML formatting
-        message_obj.msg = formatIRCMsg(message_obj.msg);
-
-        // Update the scrollback
-        bs = this.get("scrollback");
-        bs.push(message_obj);
-
-        // Keep the scrolback limited
-        if (bs.length > 250) {
-            bs.splice(250);
-        }
-        this.set({"scrollback": bs}, {silent: true});
-
-        this.trigger("msg", message_obj);
-    },
-
-    close: function () {
-        this.view.remove();
-        delete this.view;
-
-        var members = this.get('members');
-        if (members) {
-            members.reset([]);
-            this.unset('members');
-        }
-
-        this.destroy();
-
-        if (this.cid === kiwi.current_panel.cid) {
-            kiwi.app.panels.server.view.show();
-        }
-    }
-});
-
-kiwi.model.Server = kiwi.model.Panel.extend({
-    initialize: function (attributes) {
-        var name = "Server";
-        this.view = new kiwi.view.Panel({"model": this, "name": name});
-        this.set({
-            "scrollback": [],
-            "name": name
-        }, {"silent": true});
-        this.isChannel = false;
-
-        this.addMsg(' ', '--> Kiwi IRC: Such an awesome IRC client', '', {style: 'color:#009900;'});
-    }
-});
-
-// TODO: Channel modes
-kiwi.model.Channel = kiwi.model.Panel.extend({
-    initialize: function (attributes) {
-        var that = this,
-            name = this.get("name") || "",
-            members;
-
-        this.view = new kiwi.view.Channel({"model": this, "name": name});
-        this.set({
-            "members": new kiwi.model.MemberList(),
-            "name": name,
-            "scrollback": [],
-            "topic": ""
-        }, {"silent": true});
-
-        //this.addMsg(' ', '--> You have joined ' + name, 'action join', {style: 'color:#009900;'});
-
-        members = this.get("members");
-        members.bind("add", function (member) {
-            var disp = member.get("nick") + ' [' + member.get("ident") + '@' + member.get("hostname") + ']';
-            this.addMsg(' ', '--> ' + disp + ' has joined', 'action join');
-        }, this);
-
-        members.bind("remove", function (member, options) {
-            var disp = member.get("nick") + ' [' + member.get("ident") + '@' + member.get("hostname") + ']';
-            this.addMsg(' ', '<-- ' + disp + ' has left ' + ((options.message) ? '(' + options.message + ')' : ''), 'action part');
-        }, this);
-
-        members.bind("quit", function (args) {
-            var disp = member.get("nick") + ' [' + member.get("ident") + '@' + member.get("hostname") + ']';
-            this.addMsg(' ', '<-- ' + disp + ' has quit ' + ((args.message) ? '(' + args.message + ')' : ''), 'action quit');
-        }, this);
-
-        this.isChannel = true;
-    }
-});
+/*jslint white:true, regexp: true, nomen: true, devel: true, undef: true, browser: true, continue: true, sloppy: true, forin: true, newcap: true, plusplus: true, maxerr: 50, indent: 4 */\r
+/*global kiwi */\r
+kiwi.model = {};\r
+\r
+kiwi.model.MemberList = Backbone.Collection.extend({\r
+    model: kiwi.model.Member,\r
+    comparator: function (a, b) {\r
+        var i, a_modes, b_modes, a_idx, b_idx, a_nick, b_nick;\r
+        var user_prefixes = kiwi.gateway.get('user_prefixes');\r
+        a_modes = a.get("modes");\r
+        b_modes = b.get("modes");\r
+        // Try to sort by modes first\r
+        if (a_modes.length > 0) {\r
+            // a has modes, but b doesn't so a should appear first\r
+            if (b_modes.length === 0) {\r
+                return -1;\r
+            }\r
+            a_idx = b_idx = -1;\r
+            // Compare the first (highest) mode\r
+            for (i = 0; i < user_prefixes.length; i++) {\r
+                if (user_prefixes[i].mode === a_modes[0]) {\r
+                    a_idx = i;\r
+                }\r
+            }\r
+            for (i = 0; i < user_prefixes.length; i++) {\r
+                if (user_prefixes[i].mode === b_modes[0]) {\r
+                    b_idx = i;\r
+                }\r
+            }\r
+            if (a_idx < b_idx) {\r
+                return -1;\r
+            } else if (a_idx > b_idx) {\r
+                return 1;\r
+            }\r
+            // If we get to here both a and b have the same highest mode so have to resort to lexicographical sorting\r
+\r
+        } else if (b_modes.length > 0) {\r
+            // b has modes but a doesn't so b should appear first\r
+            return 1;\r
+        }\r
+        a_nick = a.get("nick").toLocaleUpperCase();\r
+        b_nick = b.get("nick").toLocaleUpperCase();\r
+        // Lexicographical sorting\r
+        if (a_nick < b_nick) {\r
+            return -1;\r
+        } else if (a_nick > b_nick) {\r
+            return 1;\r
+        } else {\r
+            // This should never happen; both users have the same nick.\r
+            console.log('Something\'s gone wrong somewhere - two users have the same nick!');\r
+            return 0;\r
+        }\r
+    },\r
+    initialize: function (options) {\r
+        this.view = new kiwi.view.MemberList({"model": this});\r
+    },\r
+    getByNick: function (nick) {\r
+        return this.find(function (m) {\r
+            return nick === m.get("nick");\r
+        });\r
+    }\r
+});\r
+\r
+kiwi.model.Member = Backbone.Model.extend({\r
+    sortModes: function (modes) {\r
+        return modes.sort(function (a, b) {\r
+            var a_idx, b_idx, i;\r
+            var user_prefixes = kiwi.gateway.get('user_prefixes');\r
+\r
+            for (i = 0; i < user_prefixes.length; i++) {\r
+                if (user_prefixes[i].mode === a) {\r
+                    a_idx = i;\r
+                }\r
+            }\r
+            for (i = 0; i < user_prefixes.length; i++) {\r
+                if (user_prefixes[i].mode === b) {\r
+                    b_idx = i;\r
+                }\r
+            }\r
+            if (a_idx < b_idx) {\r
+                return -1;\r
+            } else if (a_idx > b_idx) {\r
+                return 1;\r
+            } else {\r
+                return 0;\r
+            }\r
+        });\r
+    },\r
+    initialize: function (attributes) {\r
+        var nick, modes, prefix;\r
+        nick = this.stripPrefix(this.get("nick"));\r
+\r
+        modes = this.get("modes");\r
+        modes = modes || [];\r
+        this.sortModes(modes);\r
+        this.set({"nick": nick, "modes": modes, "prefix": this.getPrefix(modes)}, {silent: true});\r
+    },\r
+    addMode: function (mode) {\r
+        var modes, prefix;\r
+        modes = this.get("modes");\r
+        modes.push(mode);\r
+        modes = this.sortModes(modes);\r
+        this.set({"prefix": this.getPrefix(modes), "modes": modes});\r
+    },\r
+    removeMode: function (mode) {\r
+        var modes, prefix;\r
+        modes = this.get("modes");\r
+        modes = _.reject(modes, function (m) {\r
+            return m === mode;\r
+        });\r
+        this.set({"prefix": this.getPrefix(modes), "modes": modes});\r
+    },\r
+    getPrefix: function (modes) {\r
+        var prefix = '';\r
+        var user_prefixes = kiwi.gateway.get('user_prefixes');\r
+\r
+        if (typeof modes[0] !== 'undefined') {\r
+            prefix = _.detect(user_prefixes, function (prefix) {\r
+                return prefix.mode === modes[0];\r
+            });\r
+            prefix = (prefix) ? prefix.symbol : '';\r
+        }\r
+        return prefix;\r
+    },\r
+    stripPrefix: function (nick) {\r
+        var tmp = nick, i, j, k;\r
+        var user_prefixes = kiwi.gateway.get('user_prefixes');\r
+        i = 0;\r
+\r
+        for (j = 0; j < nick.length; j++) {\r
+            for (k = 0; k < user_prefixes.length; k++) {\r
+                if (nick.charAt(j) === user_prefixes[k].symbol) {\r
+                    i++;\r
+                    break;\r
+                }\r
+            }\r
+        }\r
+\r
+        return tmp.substr(i);\r
+    }\r
+});\r
+\r
+kiwi.model.PanelList = Backbone.Collection.extend({\r
+    model: kiwi.model.Panel,\r
+    comparator: function (chan) {\r
+        return chan.get("name");\r
+    },\r
+    initialize: function () {\r
+        this.view = new kiwi.view.Tabs({"el": $('#toolbar .panellist')[0], "model": this});\r
+\r
+        // Automatically create a server tab\r
+        this.server = new kiwi.model.Server({'name': kiwi.gateway.get('name')});\r
+        kiwi.gateway.on('change:name', this.view.render, this.view);\r
+        this.add(this.server);\r
+\r
+        // Set the default view to the server tab\r
+        kiwi.current_panel = this.server;\r
+\r
+    },\r
+    getByName: function (name) {\r
+        return this.find(function (c) {\r
+            return name === c.get("name");\r
+        });\r
+    }\r
+});\r
+\r
+kiwi.model.Panel = Backbone.Model.extend({\r
+    initialize: function (attributes) {\r
+        var name = this.get("name") || "";\r
+        this.view = new kiwi.view.Panel({"model": this, "name": name});\r
+        this.set({\r
+            "scrollback": [],\r
+            "name": name\r
+        }, {"silent": true});\r
+    },\r
+\r
+    addMsg: function (nick, msg, type, opts) {\r
+        var message_obj, bs, d;\r
+\r
+        opts = opts || {};\r
+\r
+        // Time defaults to now\r
+        if (!opts || typeof opts.time === 'undefined') {\r
+            d = new Date();\r
+            opts.time = d.getHours().toString().lpad(2, "0") + ":" + d.getMinutes().toString().lpad(2, "0") + ":" + d.getSeconds().toString().lpad(2, "0");\r
+        }\r
+\r
+        // CSS style defaults to empty string\r
+        if (!opts || typeof opts.style === 'undefined') {\r
+            opts.style = '';\r
+        }\r
+\r
+        // Escape any HTML that may be in here\r
+        // This doesn't seem right to be here.. should be in view (??)\r
+        msg =  $('<div />').text(msg).html();\r
+\r
+        // Run through the plugins\r
+        message_obj = {"msg": msg, "time": opts.time, "nick": nick, "chan": this.get("name"), "type": type, "style": opts.style};\r
+        //tmp = kiwi.plugs.run('addmsg', message_obj);\r
+        if (!message_obj) {\r
+            return;\r
+        }\r
+\r
+        // The CSS class (action, topic, notice, etc)\r
+        if (typeof message_obj.type !== "string") {\r
+            message_obj.type = '';\r
+        }\r
+\r
+        // Make sure we don't have NaN or something\r
+        if (typeof message_obj.msg !== "string") {\r
+            message_obj.msg = '';\r
+        }\r
+\r
+        // Convert IRC formatting into HTML formatting\r
+        message_obj.msg = formatIRCMsg(message_obj.msg);\r
+\r
+        // Update the scrollback\r
+        bs = this.get("scrollback");\r
+        bs.push(message_obj);\r
+\r
+        // Keep the scrolback limited\r
+        if (bs.length > 250) {\r
+            bs.splice(250);\r
+        }\r
+        this.set({"scrollback": bs}, {silent: true});\r
+\r
+        this.trigger("msg", message_obj);\r
+    },\r
+\r
+    close: function () {\r
+        this.view.remove();\r
+        delete this.view;\r
+\r
+        var members = this.get('members');\r
+        if (members) {\r
+            members.reset([]);\r
+            this.unset('members');\r
+        }\r
+\r
+        this.destroy();\r
+\r
+        if (this.cid === kiwi.current_panel.cid) {\r
+            kiwi.app.panels.server.view.show();\r
+        }\r
+    },\r
+\r
+    isChannel: function () {\r
+        var channel_prefix = kiwi.gateway.get('channel_prefix'),\r
+            this_name = this.get('name');\r
+\r
+        if (!this_name) return false;\r
+        return (channel_prefix.indexOf(this_name[0]) > -1);\r
+    }\r
+});\r
+\r
+kiwi.model.Server = kiwi.model.Panel.extend({\r
+    initialize: function (attributes) {\r
+        var name = "Server";\r
+        this.view = new kiwi.view.Panel({"model": this, "name": name});\r
+        this.set({\r
+            "scrollback": [],\r
+            "name": name\r
+        }, {"silent": true});\r
+\r
+        this.addMsg(' ', '--> Kiwi IRC: Such an awesome IRC client', '', {style: 'color:#009900;'});\r
+    }\r
+});\r
+\r
+// TODO: Channel modes\r
+// TODO: Listen to gateway events for anythign related to this channel\r
+kiwi.model.Channel = kiwi.model.Panel.extend({\r
+    initialize: function (attributes) {\r
+        var that = this,\r
+            name = this.get("name") || "",\r
+            members;\r
+\r
+        this.view = new kiwi.view.Channel({"model": this, "name": name});\r
+        this.set({\r
+            "members": new kiwi.model.MemberList(),\r
+            "name": name,\r
+            "scrollback": [],\r
+            "topic": ""\r
+        }, {"silent": true});\r
+\r
+        //this.addMsg(' ', '--> You have joined ' + name, 'action join', {style: 'color:#009900;'});\r
+\r
+        members = this.get("members");\r
+        members.bind("add", function (member) {\r
+            var disp = member.get("nick") + ' [' + member.get("ident") + '@' + member.get("hostname") + ']';\r
+            this.addMsg(' ', '--> ' + disp + ' has joined', 'action join');\r
+        }, this);\r
+\r
+        members.bind("remove", function (member, options) {\r
+            var disp = member.get("nick") + ' [' + member.get("ident") + '@' + member.get("hostname") + ']';\r
+            this.addMsg(' ', '<-- ' + disp + ' has left ' + ((options.message) ? '(' + options.message + ')' : ''), 'action part');\r
+        }, this);\r
+\r
+        members.bind("quit", function (args) {\r
+            var disp = member.get("nick") + ' [' + member.get("ident") + '@' + member.get("hostname") + ']';\r
+            this.addMsg(' ', '<-- ' + disp + ' has quit ' + ((args.message) ? '(' + args.message + ')' : ''), 'action quit');\r
+        }, this);\r
+    }\r
+});\r
index 774aa3142269c11b4b89fefc710c193071b069cc..27d0c6accbe88ba10785cff5dc8199d4203c0d0e 100644 (file)
@@ -1,5 +1,6 @@
 * { margin:0px; padding:0px; }
 html, body { height:100%; }
+a { color:#36C; text-decoration:none; cursor:pointer; }
 
 
 
@@ -116,8 +117,9 @@ body {
 #memberlists ul.active { display:block; }
 #memberlists ul li { padding: 0.2em 1em; overflow-y:auto; overflow-x:hidden; cursor:pointer; }
 #memberlists ul li:hover { background-color:#FAF7D3; }
+#memberlists ul li a.nick { display:block; color:black; }
 
-#memberlists ul li .userbox { margin:0px 5px 5px 5px; font-size:.9em; }
+#memberlists ul li .userbox { margin:0 1em 5px 1em; font-size:.9em; }
 #memberlists ul li .userbox a { display:block; text-decoration:none; border-bottom: 1px dashed #aaa; }
 
 
@@ -131,7 +133,7 @@ body {
     -khtml-border-radius:5px;
 }
 #controlbox .input .nick { text-align: right; width:11em; left:0px; position:absolute; padding:2px; }
-#controlbox .input .nick a { text-decoration:none; }
+#controlbox .input .nick a { text-decoration:none; color:black; }
 #controlbox .input .input_wrap {
     position:absolute;
     right:7px; left: 12.2em;
index ec70c2f1b9484a51189c55b361db9bccce4c369d..65158fe449e9848e2aecfc73cb73caf840f04452 100644 (file)
@@ -16,11 +16,19 @@ kiwi.view.MemberList = Backbone.View.extend({
         var $this = $(this.el);\r
         $this.empty();\r
         this.model.forEach(function (member) {\r
-            $('<li><a class="nick"><span class="prefix">' + member.get("prefix") + '</span>' + member.get("nick") + '</a></li>').appendTo($this).data('member', member);\r
+            $('<li><a class="nick"><span class="prefix">' + member.get("prefix") + '</span>' + member.get("nick") + '</a></li>')\r
+                .appendTo($this)\r
+                .data('member', member);\r
         });\r
     },\r
     nickClick: function (x) {\r
-        console.log(x);\r
+        var target = $(x.currentTarget).parent('li'),\r
+            member = target.data('member'),\r
+            userbox = new kiwi.view.UserBox();\r
+        \r
+        userbox.member = member;\r
+        $('.userbox', this.$el).remove();\r
+        target.append(userbox.$el);\r
     },\r
     show: function () {\r
         $('#memberlists').children().removeClass('active');\r
@@ -28,6 +36,32 @@ kiwi.view.MemberList = Backbone.View.extend({
     }\r
 });\r
 \r
+\r
+kiwi.view.UserBox = Backbone.View.extend({\r
+    // Member this userbox is relating to\r
+    member: {},\r
+\r
+    events: {\r
+        'click .query': 'queryClick',\r
+        'click .info': 'infoClick'\r
+    },\r
+\r
+    initialize: function () {\r
+        this.$el = $($('#tmpl_userbox').html());\r
+    },\r
+\r
+    queryClick: function (event) {\r
+        var panel = new kiwi.model.Channel({name: this.member.get('nick')});\r
+        kiwi.app.panels.add(panel);\r
+        panel.view.show();\r
+    },\r
+\r
+    infoClick: function (event) {\r
+        kiwi.gateway.raw('WHOIS ' + this.member.get('nick'));\r
+    }\r
+});\r
+\r
+\r
 kiwi.view.Panel = Backbone.View.extend({\r
     tagName: "div",\r
     className: "messages",\r
@@ -45,6 +79,7 @@ kiwi.view.Panel = Backbone.View.extend({
     initializePanel: function (options) {\r
         this.$el.css('display', 'none');\r
 \r
+        // Containing element for this panel\r
         if (options.container) {\r
             this.$container = $(options.container);\r
         } else {\r
@@ -212,7 +247,13 @@ kiwi.view.Tabs = Backbone.View.extend({
 \r
     partClick: function (e) {\r
         var panel = this.model.getByCid($(e.currentTarget).parent().data('panel_id'));\r
-        kiwi.gateway.part(panel.get('name'));\r
+\r
+        // Only need to part if it's a channel\r
+        if (panel.isChannel()) {\r
+            kiwi.gateway.part(panel.get('name'));\r
+        } else {\r
+            panel.close();\r
+        }\r
     }\r
 });\r
 \r