Channel Info+admin window; RPL_CHANNEL_URL; Ban list; CSS forms refactor
authorDarren <darren@darrenwhitlen.com>
Wed, 1 Jan 2014 16:59:29 +0000 (16:59 +0000)
committerDarren <darren@darrenwhitlen.com>
Wed, 1 Jan 2014 16:59:29 +0000 (16:59 +0000)
14 files changed:
client/assets/css/style.css
client/src/app.js
client/src/index.html.tmpl
client/src/models/application.js
client/src/models/channel.js
client/src/models/channelinfo.js [new file with mode: 0644]
client/src/models/gateway.js
client/src/models/network.js
client/src/views/channelinfo.js [new file with mode: 0644]
client/src/views/menubox.js
server/clientcommands.js
server/httphandler.js
server/irc/channel.js
server/irc/commands.js

index 4afa2f8e9d267dcd7f4c6de1169b6f1e855e1876..a6fe3e259fe5b9c13f7d687b1d5e78f7f9161c6a 100644 (file)
@@ -14,6 +14,25 @@ html, body { height:100%; }
 #kiwi a img { border:none; }
 #kiwi .format_span a { color: inherit; background-color: inherit; text-decoration: inherit; font-style: inherit; font-weight: inherit;}
 
+#kiwi h1,
+#kiwi h2,
+#kiwi h3 {
+    margin-top: 22px;
+    margin-bottom: 11px;
+}
+#kiwi h4,
+#kiwi h5,
+#kiwi h6 {
+    margin-top: 11px;
+    margin-bottom: 11px;
+}
+#kiwi h1 { font-size: 46px; }
+#kiwi h2 { font-size: 40px; }
+#kiwi h3 { font-size: 34px; }
+#kiwi h4 { font-size: 28px; }
+#kiwi h5 { font-size: 22px; }
+#kiwi h6 { font-size: 16px; }
+
 
 /**
  * Main layout blocks
@@ -271,97 +290,63 @@ html, body { height:100%; }
     display: inline-block;
 }
 
-#kiwi .settings_container h1 {
-    margin-top: 22px;
-    margin-bottom: 11px;
-    font-size: 46px;
-}
-
-#kiwi .settings_container h2 {
-    margin-top: 22px;
-    margin-bottom: 11px;
-    font-size: 40px;
-}
-
-#kiwi .settings_container h3 {
-    margin-top: 22px;
-    margin-bottom: 11px;
-    font-size: 34px;
-}
-
-#kiwi .settings_container h4 {
-    margin-top: 11px;
-    margin-bottom: 11px;
-    font-size: 28px;
-}
-
-#kiwi .settings_container h5 {
-    margin-top: 11px;
-    margin-bottom: 11px;
-    font-size: 22px;
-}
-
-#kiwi .settings_container h6 {
-    margin-top: 11px;
-    margin-bottom: 11px;
-    font-size: 16px;
-}
-
 #kiwi .settings_container label {
     cursor: pointer;
 }
 
-#kiwi .settings_container input,
-#kiwi .settings_container select,
-#kiwi .settings_container textarea {
-    width: 100%;
+#kiwi_ form label { display: block; }
+#kiwi form input,
+#kiwi form select,
+#kiwi form textarea {
+    /*width: 100%;
+    box-sizing: border-box;*/
 }
 
-#kiwi .settings_container input[size],
-#kiwi .settings_container select[size],
-#kiwi .settings_container textarea[size] {
+#kiwi form input[size],
+#kiwi form select[size],
+#kiwi form textarea[size] {
     width: auto;
 }
 
-#kiwi .settings_container input[type="file"],
-#kiwi .settings_container input[type="image"],
-#kiwi .settings_container input[type="submit"],
-#kiwi .settings_container input[type="reset"],
-#kiwi .settings_container input[type="button"] {
+#kiwi form input[type="file"],
+#kiwi form input[type="image"],
+#kiwi form input[type="submit"],
+#kiwi form input[type="reset"],
+#kiwi form input[type="button"] {
     width: auto;
 }
 
-#kiwi .settings_container input[type="radio"] {
+#kiwi form input[type="radio"] {
     width: auto;
     cursor: pointer;
     margin-top: 2px;
 }
 
-#kiwi .settings_container input[type="checkbox"] {
+#kiwi form input[type="checkbox"] {
     width: auto;
     cursor: pointer;
     margin-top: 3px;
 }
 
-#kiwi .settings_container .radio,
-#kiwi .settings_container .checkbox {
+#kiwi form .radio,
+#kiwi form .checkbox {
     margin-bottom: 10px;
     padding-left: 20px;
 }
 
-#kiwi .settings_container .radio:last-child,
-#kiwi .settings_container .checkbox:last-child {
+#kiwi form .radio:last-child,
+#kiwi form .checkbox:last-child {
     margin-bottom: 0;
 }
 
-#kiwi .settings_container .radio input[type="radio"],
-#kiwi .settings_container .checkbox input[type="checkbox"] {
+#kiwi form .radio input[type="radio"],
+#kiwi form .checkbox input[type="checkbox"] {
     float: left;
     margin-left: -20px;
 }
 
-#kiwi .settings_container .radio+.radio,
-#kiwi .settings_container .checkbox+.checkbox {
+#kiwi form .radio+.radio,
+#kiwi form .checkbox+.checkbox {
     margin-top: -7px;
 }
 
@@ -397,11 +382,11 @@ html, body { height:100%; }
     width: 75px;
 }
 
-#kiwi .settings_container .control-group {
+#kiwi .control-group {
     margin: 0 0 20px 20px;
 }
 
-#kiwi .settings_container .control-group:last-child {
+#kiwi .control-group:last-child {
     margin-bottom: 0;
 }
 
@@ -414,6 +399,14 @@ html, body { height:100%; }
 }
 
 
+#kiwi .schannel_info {}
+#kiwi .channel_info label { display: block; }
+#kiwi .channel_info .channel_url {
+    display: none;
+}
+#kiwi .channel_info .remove-ban { cursor: pointer; }
+
+
 
 
 
@@ -746,7 +739,7 @@ html, body { height:100%; }
     padding:10px; left: 0px;
     background: #1B1B1B; color:#eeeeee;
 }
-#kiwi.theme_relaxed .controlbox .nickchange input { padding:0.3em 0.5em; margin-left: 0.5em; }
+#kiwi.theme_relaxed .controlbox .nickchange input { padding:0.3em 0.5em; margin-left: 0.5em; width: 165px; }
 #kiwi.theme_relaxed .controlbox .nickchange button { padding:0.5em; margin: 0 0.5em 0 1em; }
 
 
@@ -964,7 +957,7 @@ html, body { height:100%; }
 
 #kiwi.theme_mini .messages .msg { border-bottom: 1px solid #DEDEDE; padding: 5px; font-family:arial; font-size:0.9em; }
 #kiwi.theme_mini .messages .msg .time { display:none; }
-#kiwi.theme_mini .messages .msg .nick { display:block; font-family:Arial; text-transform:capitalize; }
+#kiwi.theme_mini .messages .msg .nick { display:block; font-family:Arial; text-tranform:capitalize; }
 #kiwi.theme_mini .messages .msg .text { display:block; white-space:pre-wrap; word-wrap:break-word; font-family:arial; }
 
 #kiwi.theme_mini .messages .msg.action .nick { }
index 885684b95cb0c1b38e1536636991b7b912183cde..e5790ce5086bbd3b2582dde49cc900c4dda344c8 100644 (file)
@@ -65,7 +65,8 @@ _kiwi.global = {
             var funcs = {\r
                 kiwi: 'kiwi', raw: 'raw', kick: 'kick', topic: 'topic',\r
                 part: 'part', join: 'join', action: 'action', ctcp: 'ctcp',\r
-                notice: 'notice', msg: 'privmsg', changeNick: 'changeNick'\r
+                notice: 'notice', msg: 'privmsg', changeNick: 'changeNick',\r
+                channelInfo: 'channelInfo', mode: 'mode'\r
             };\r
 \r
             // Proxy each gateway method\r
index aeed33dd4e13d4e4153dc52bbd5febd8a1dfc739..987226839f7cc8528d51c4dee717c3108ebb644f 100644 (file)
     </script>\r
 \r
 \r
+    <script type="text/html" id="tmpl_channel_info">\r
+        <div class="channel_info">\r
+            <h6><%= channel_name %></h6>\r
+            <b class="channel_url"><a href=""></a></b>\r
+\r
+            <div class="control-group">\r
+                <form class="channel_info_modes">\r
+                    <label>\r
+                        <input type="checkbox" name="channel_mute" class="channel-mode" data-mode="m" />\r
+                        Moderated chat\r
+                    </label>\r
+                    <label>\r
+                        <input type="checkbox" name="channel_invite" class="channel-mode" data-mode="i" />\r
+                        Invite only\r
+                    </label>\r
+                    <label>\r
+                        <input type="checkbox" name="channel_topic" class="channel-mode" data-mode="t" />\r
+                        Only operators can change the topic\r
+                    </label>\r
+                    <label>\r
+                        <input type="checkbox" name="channel_external_messages" class="channel-mode" data-mode="n" />\r
+                        Block messages from outside this channel\r
+                    </label>\r
+                    <label>\r
+                        Password\r
+                        <input type="text" name="channel_key" class="channel-mode" data-mode="k" />\r
+                    </label>\r
+                </form>\r
+            </div>\r
+\r
+            <div class="control-group">\r
+                <form class="channel_banlist">\r
+                    <button class="show_banlist">Show banlist</button>\r
+                    <table>\r
+                        <thead>\r
+                            <tr>\r
+                                <td>Bank Mask</td>\r
+                                <td>Added By</td>\r
+                                <td>Date Added</td>\r
+                                <td></td>\r
+                            </tr>\r
+                        </thead>\r
+                        <tbody>\r
+                        </tbody>\r
+                    </table>\r
+                </form>\r
+            </div>\r
+        </div>\r
+    </script>\r
+\r
+\r
     <script type="text/html" id="tmpl_userbox">\r
         <div class="userbox">\r
             <a class="close_menu if_op op"><i class="icon-star"></i><%= op %></a>\r
 \r
     <script type="text/html" id="tmpl_applet_settings">\r
         <div class="settings_container">\r
-            <section>\r
-                <h6>Theme</h6>\r
-                <div class="control-group">\r
-                    <div class="thumbnails">\r
-                        <a class="thumbnail" data-setting="theme" data-value="relaxed" href="#">\r
-                            <div class="thumbnail_wrapper"><div class="theme_color" style="background-color: #e7e7e7;"></div></div>\r
-                            <div class="caption"><u>Relaxed</u></div>\r
-                        </a>\r
-                        <a class="thumbnail" data-setting="theme" data-value="mini" href="#">\r
-                            <div class="thumbnail_wrapper"><div class="theme_color" style="background-color: #fff;"></div></div>\r
-                            <div class="caption"><u>Mini</u></div>\r
-                        </a>\r
-                        <a class="thumbnail" data-setting="theme" data-value="cli" href="#">\r
-                            <div class="thumbnail_wrapper"><div class="theme_color" style="background-color: #222;"></div></div>\r
-                            <div class="caption"><u>CLI</u></div>\r
-                        </a>\r
-                        <a class="thumbnail" data-setting="theme" data-value="basic" href="#">\r
-                            <div class="thumbnail_wrapper"><div class="theme_color" style="background-color: #e7e7e7;"></div></div>\r
-                            <div class="caption"><u>Basic</u></div>\r
-                        </a>\r
+            <form>\r
+                <section>\r
+                    <h6>Theme</h6>\r
+                    <div class="control-group">\r
+                        <div class="thumbnails">\r
+                            <a class="thumbnail" data-setting="theme" data-value="relaxed" href="#">\r
+                                <div class="thumbnail_wrapper"><div class="theme_color" style="background-color: #e7e7e7;"></div></div>\r
+                                <div class="caption"><u>Relaxed</u></div>\r
+                            </a>\r
+                            <a class="thumbnail" data-setting="theme" data-value="mini" href="#">\r
+                                <div class="thumbnail_wrapper"><div class="theme_color" style="background-color: #fff;"></div></div>\r
+                                <div class="caption"><u>Mini</u></div>\r
+                            </a>\r
+                            <a class="thumbnail" data-setting="theme" data-value="cli" href="#">\r
+                                <div class="thumbnail_wrapper"><div class="theme_color" style="background-color: #222;"></div></div>\r
+                                <div class="caption"><u>CLI</u></div>\r
+                            </a>\r
+                            <a class="thumbnail" data-setting="theme" data-value="basic" href="#">\r
+                                <div class="thumbnail_wrapper"><div class="theme_color" style="background-color: #e7e7e7;"></div></div>\r
+                                <div class="caption"><u>Basic</u></div>\r
+                            </a>\r
+                        </div>\r
                     </div>\r
-                </div>\r
-            </section>\r
-\r
-            <section>\r
-                <h6>Channels</h6>\r
-                <div class="control-group">\r
-                    <div class="radio">\r
-                        <label>\r
-                            <input type="radio" name="channel_list_style" data-setting="channel_list_style" value="tabs">\r
-                            <%= tabs %>\r
-                        </label>\r
-                    </div>\r
-                    <div class="radio">\r
-                        <label>\r
-                            <input type="radio" name="channel_list_style" data-setting="channel_list_style" value="list">\r
-                            <%= list %><small class="text-muted">(<%= large_amounts_of_chans %>)</small>\r
-                        </label>\r
+                </section>\r
+\r
+                <section>\r
+                    <h6>Channels</h6>\r
+                    <div class="control-group">\r
+                        <div class="radio">\r
+                            <label>\r
+                                <input type="radio" name="channel_list_style" data-setting="channel_list_style" value="tabs">\r
+                                <%= tabs %>\r
+                            </label>\r
+                        </div>\r
+                        <div class="radio">\r
+                            <label>\r
+                                <input type="radio" name="channel_list_style" data-setting="channel_list_style" value="list">\r
+                                <%= list %><small class="text-muted">(<%= large_amounts_of_chans %>)</small>\r
+                            </label>\r
+                        </div>\r
                     </div>\r
-                </div>\r
-            </section>\r
-\r
-            <section>\r
-                <h6>Chat window</h6>\r
-                <div class="control-group">\r
-                    <div class="checkbox">\r
+                </section>\r
+\r
+                <section>\r
+                    <h6>Chat window</h6>\r
+                    <div class="control-group">\r
+                        <div class="checkbox">\r
+                            <label>\r
+                                <input data-setting="show_joins_parts" type="checkbox">\r
+                                <%= join_part %>\r
+                            </label>\r
+                        </div>\r
+                        <div class="checkbox">\r
+                            <label>\r
+                                <input data-setting="show_timestamps" type="checkbox">\r
+                                <%= timestamps %>\r
+                            </label>\r
+                        </div>\r
+                        <div class="checkbox">\r
+                            <label>\r
+                                <input data-setting="mute_sounds" type="checkbox">\r
+                                <%= mute %>\r
+                            </label>\r
+                        </div>\r
+                        <div class="checkbox">\r
+                            <label>\r
+                                <input data-setting="show_emoticons" type="checkbox">\r
+                                <%= emoticons %>\r
+                            </label>\r
+                        </div>\r
                         <label>\r
-                            <input data-setting="show_joins_parts" type="checkbox">\r
-                            <%= join_part %>\r
+                            <input data-setting="scrollback" class="input-small" type="text" size="4" pattern="\d*">\r
+                            <span><%= scroll_history %></span>\r
                         </label>\r
                     </div>\r
-                    <div class="checkbox">\r
-                        <label>\r
-                            <input data-setting="show_timestamps" type="checkbox">\r
-                            <%= timestamps %>\r
-                        </label>\r
+                </section>\r
+\r
+                <section class="language">\r
+                    <h6>Language</h6>\r
+                    <div class="control-group">\r
+                        <select data-setting="locale">\r
+                            <% _.forEach(languages, function(lang) { %>\r
+                                <option value="<%= lang.tag %>"><%= lang.language %></li>\r
+                            <% }); %>\r
+                        </select>\r
+                        <br>\r
+                        <small><%= locale_restart_needed %></small>\r
                     </div>\r
-                    <div class="checkbox">\r
-                        <label>\r
-                            <input data-setting="mute_sounds" type="checkbox">\r
-                            <%= mute %>\r
-                        </label>\r
+                </section>\r
+\r
+                <section class="protocol_handler">\r
+                    <h6><%= default_client %></h6>\r
+                    <div class="control-group">\r
+                        <button class="register_protocol"><%= make_default %></button>\r
+                        <br>\r
+                        <small><%= default_note %></small>\r
                     </div>\r
-                    <div class="checkbox">\r
-                        <label>\r
-                            <input data-setting="show_emoticons" type="checkbox">\r
-                            <%= emoticons %>\r
-                        </label>\r
-                    </div>\r
-                    <label>\r
-                        <input data-setting="scrollback" class="input-small" type="text" size="4" pattern="\d*">\r
-                        <span><%= scroll_history %></span>\r
-                    </label>\r
-                </div>\r
-            </section>\r
-\r
-            <section class="language">\r
-                <h6>Language</h6>\r
-                <div class="control-group">\r
-                    <select data-setting="locale">\r
-                        <% _.forEach(languages, function(lang) { %>\r
-                            <option value="<%= lang.tag %>"><%= lang.language %></li>\r
-                        <% }); %>\r
-                    </select>\r
-                    <br>\r
-                    <small><%= locale_restart_needed %></small>\r
-                </div>\r
-            </section>\r
-\r
-            <section class="protocol_handler">\r
-                <h6><%= default_client %></h6>\r
-                <div class="control-group">\r
-                    <button class="register_protocol"><%= make_default %></button>\r
-                    <br>\r
-                    <small><%= default_note %></small>\r
-                </div>\r
-            </section>\r
-\r
-            <section class="notification_enabler">\r
-                <h6><%= html5_notifications %></h6>\r
-                <div class="control-group">\r
-                    <button class="enable_notifications"><%= enable_notifications %></button>\r
-                </div>\r
-            </section>\r
+                </section>\r
 \r
+                <section class="notification_enabler">\r
+                    <h6><%= html5_notifications %></h6>\r
+                    <div class="control-group">\r
+                        <button class="enable_notifications"><%= enable_notifications %></button>\r
+                    </div>\r
+                </section>\r
+            </form>\r
         </div>\r
     </script>\r
 \r
index e65c2cae2adba56c0d8e64c14b50768447940dc8..80a2a85cc62d30a06e10c46366682f8815020469 100644 (file)
@@ -495,6 +495,8 @@ _kiwi.model.Application = function () {
                 '/voice': '/quote mode $channel +v $1+',\r
                 '/devoice': '/quote mode $channel -v $1+',\r
                 '/k': '/kick $channel $1+',\r
+                '/ban': '/quote mode $channel +b $1+',\r
+                '/unban': '/quote mode $channel -b $1+',\r
 \r
                 // Misc aliases\r
                 '/slap': '/me slaps $1 around a bit with a large trout'\r
@@ -539,6 +541,15 @@ _kiwi.model.Application = function () {
 \r
             controlbox.on('command:encoding', encodingCommand);\r
 \r
+            controlbox.on('command:info', function(ev) {\r
+                var active_panel = _kiwi.app.panels().active;\r
+\r
+                if (!active_panel.isChannel())\r
+                    return;\r
+\r
+                new _kiwi.model.ChannelInfo({channel: _kiwi.app.panels().active});\r
+            });\r
+\r
             controlbox.on('command:css', function (ev) {\r
                 var queryString = '?reload=' + new Date().getTime();\r
                 $('link[rel="stylesheet"]').each(function () {\r
index 7fb124aafa7869a62d7b9107dd45d1152356b355..18d0538f97445511baae4d775ce06484c1d3e663 100644 (file)
@@ -105,5 +105,10 @@ _kiwi.model.Channel = _kiwi.model.Panel.extend({
         this.addMsg('', 'Window cleared');\r
 \r
         this.view.render();\r
+    },\r
+\r
+\r
+    setMode: function(mode_string) {\r
+        this.get('network').gateway.mode(this.get('name'), mode_string);\r
     }\r
 });\r
diff --git a/client/src/models/channelinfo.js b/client/src/models/channelinfo.js
new file mode 100644 (file)
index 0000000..501c9fa
--- /dev/null
@@ -0,0 +1,5 @@
+_kiwi.model.ChannelInfo = Backbone.Model.extend({
+    initialize: function () {
+        this.view = new _kiwi.view.ChannelInfo({"model": this});
+    }
+});
\ No newline at end of file
index 0a010b7734d0d2b22006212a7b4a3cc9ed79b8ca..268b6344b71d3a4511b186306a23af4bb0c79913 100644 (file)
@@ -465,6 +465,20 @@ _kiwi.model.Gateway = function () {
         this.sendData(connection_id, data, callback);\r
     };\r
 \r
+    /**\r
+    *   Retrieves channel information\r
+    */\r
+    this.channelInfo = function (connection_id, channel, callback) {\r
+        var data = {\r
+            method: 'channel_info',\r
+            args: {\r
+                channel: channel\r
+            }\r
+        };\r
+\r
+        this.sendData(connection_id, data, callback);\r
+    };\r
+\r
     /**\r
     *   Leaves a channel\r
     *   @param  {String}    channel     The channel to part\r
@@ -568,6 +582,21 @@ _kiwi.model.Gateway = function () {
         this.sendData(connection_id, data, callback);\r
     };\r
 \r
+    /**\r
+    * Sets a mode for a target\r
+    */\r
+    this.mode = function (connection_id, target, mode_string, callback) {\r
+        data = {\r
+            method: 'raw',\r
+            args: {\r
+                data: 'MODE ' + target + ' ' + mode_string\r
+            }\r
+        };\r
+\r
+        this.sendData(connection_id, data, callback);\r
+    };\r
+\r
+\r
     /**\r
      *  Sends ENCODING change request to server.\r
      *  @param  {String}     new_encoding  The new proposed encode\r
index 61ed2417dbf81052d5a332fa0a1f28eaf81bb6d0..a80445bc5868d0da4bcff3f4b17af14ef36636d6 100644 (file)
             this.gateway.on('topicsetby', onTopicSetBy, this);
             this.gateway.on('userlist', onUserlist, this);
             this.gateway.on('userlist_end', onUserlistEnd, this);
+            this.gateway.on('banlist', onBanlist, this);
             this.gateway.on('mode', onMode, this);
             this.gateway.on('whois', onWhois, this);
             this.gateway.on('whowas', onWhowas, this);
             this.gateway.on('list_start', onListStart, this);
             this.gateway.on('irc_error', onIrcError, this);
             this.gateway.on('unknown_command', onUnknownCommand, this);
+            this.gateway.on('channel_info', onChannelInfo, this);
         },
 
 
                 // Check if we have the panel already. If not, create it
                 channel = that.panels.getByName(channel_name);
                 if (!channel) {
-                    channel = new _kiwi.model.Channel({name: channel_name});
+                    channel = new _kiwi.model.Channel({name: channel_name, network: that});
                     that.panels.add(channel);
                 }
 
         var c, members, user;
         c = this.panels.getByName(event.channel);
         if (!c) {
-            c = new _kiwi.model.Channel({name: event.channel});
+            c = new _kiwi.model.Channel({name: event.channel, network: this});
             this.panels.add(c);
         }
 
             // If a panel isn't found for this PM, create one
             panel = this.panels.getByName(event.nick);
             if (!panel) {
-                panel = new _kiwi.model.Channel({name: event.nick});
+                panel = new _kiwi.model.Channel({name: event.nick, network: this});
                 this.panels.add(panel);
             }
 
 
 
 
+    function onChannelInfo(event) {
+        var channel = this.panels.getByName(event.channel);
+        if (!channel) return;
+
+        if (event.url) {
+            channel.set('info_url', event.url);
+        } else if (event.modes) {
+            channel.set('info_modes', event.modes);
+        }
+    }
+
+
+
     function onUserlist(event) {
         var channel;
         channel = this.panels.getByName(event.channel);
 
 
 
+    function onBanlist(event) {
+        console.log('banlist', event);
+        var channel = this.panels.getByName(event.channel);
+        if (!channel)
+            return;
+
+        channel.set('banlist', event.bans || []);
+    }
+
+
+
     function onMode(event) {
         var channel, i, prefixes, members, member, find_prefix;
 
diff --git a/client/src/views/channelinfo.js b/client/src/views/channelinfo.js
new file mode 100644 (file)
index 0000000..97dfde3
--- /dev/null
@@ -0,0 +1,153 @@
+// var f = new _kiwi.model.ChannelInfo({channel: _kiwi.app.panels().active});
+
+_kiwi.view.ChannelInfo = Backbone.View.extend({
+    events: {
+        'click .show_banlist': 'updateBanlist',
+        'change .channel-mode': 'onModeChange',
+        'click .remove-ban': 'onRemoveBanClick'
+    },
+
+
+    initialize: function () {
+        var that = this,
+            network,
+            channel = this.model.get('channel'),
+            text = {
+                channel_name: channel.get('name')
+            };
+
+        this.$el = $(_.template($('#tmpl_channel_info').html().trim(), text));
+
+        // Create the menu box this view will sit inside
+        this.menu = new _kiwi.view.MenuBox();
+        this.menu.addItem('channel_info', this.$el);
+        this.menu.$el.appendTo(channel.view.$container);
+        this.menu.show();
+
+        // Menu box will call this destroy on closing
+        this.$el.dispose = _.bind(this.dispose, this);
+
+        // Display the info we have, then listen for further changes
+        this.updateInfo(channel);
+        channel.on('change:info_modes change:info_url change:banlist', this.updateInfo, this);
+
+        // Request the latest info for ths channel from the network
+        channel.get('network').gateway.channelInfo(channel.get('name'));
+    },
+
+
+    render: function () {
+    },
+
+
+    onModeChange: function(event) {
+        var $this = $(event.currentTarget),
+            channel = this.model.get('channel'),
+            mode = $this.data('mode'),
+            mode_string = '';
+
+        if ($this.attr('type') == 'checkbox') {
+            mode_string = $this.is(':checked') ? '+' : '-';
+            mode_string += mode;
+            channel.setMode(mode_string);
+
+            return;
+        }
+
+        if ($this.attr('type') == 'text') {
+            mode_string = $this.val() ?
+                '+' + mode + ' ' + $this.val() :
+                '-' + mode;
+
+            channel.setMode(mode_string);
+
+            return;
+        }
+    },
+
+
+    onRemoveBanClick: function (event) {
+        event.preventDefault();
+        event.stopPropagation();
+
+        var $this = $(event.currentTarget),
+            $tr = $this.parents('tr:first'),
+            ban = $tr.data('ban');
+
+        if (!ban)
+            return;
+
+        var channel = this.model.get('channel');
+        channel.setMode('-b ' + ban.banned);
+
+        $tr.remove();
+    },
+
+
+    updateInfo: function (channel, new_val) {
+        var that = this,
+            modes, url, banlist;
+
+        modes = channel.get('info_modes');
+        if (modes) {
+            _.each(modes, function(mode, idx) {
+                mode.mode = mode.mode.toLowerCase();
+
+                if (mode.mode == '+k') {
+                    that.$el.find('[name="channel_key"]').val(mode.param);
+                } else if (mode.mode == '+m') {
+                    that.$el.find('[name="channel_mute"]').attr('checked', 'checked');
+                } else if (mode.mode == '+i') {
+                    that.$el.find('[name="channel_invite"]').attr('checked', 'checked');
+                } else if (mode.mode == '+n') {
+                    that.$el.find('[name="channel_external_messages"]').attr('checked', 'checked');
+                } else if (mode.mode == '+t') {
+                    that.$el.find('[name="channel_topic"]').attr('checked', 'checked');
+                }
+            });
+        }
+
+        url = channel.get('info_url');
+        if (url) {
+            this.$el.find('.channel_url a')
+                .text(url)
+                .attr('href', url);
+
+            this.$el.find('.channel_url').slideDown();
+        }
+
+        banlist = channel.get('banlist');
+        if (banlist && banlist.length) {
+            var $table = this.$el.find('.channel_banlist table tbody');
+
+            $table.empty();
+            _.each(banlist, function(ban) {
+                var $tr = $('<tr></tr>').data('ban', ban);
+
+                $('<td></td>').text(ban.banned).appendTo($tr);
+                $('<td></td>').text(ban.banned_by.split(/[!@]/)[0]).appendTo($tr);
+                $('<td></td>').text(formatDate(new Date(parseInt(ban.banned_at, 10) * 1000))).appendTo($tr);
+                $('<td><i class="icon-remove remove-ban"></i></td>').appendTo($tr);
+
+                $table.append($tr);
+            });
+        }
+    },
+
+
+    updateBanlist: function (event) {
+        event.preventDefault();
+
+        var channel = this.model.get('channel'),
+            network = channel.get('network');
+
+        network.gateway.raw('MODE ' + channel.get('name') + ' +b');
+    },
+
+
+    dispose: function () {
+        this.model.get('channel').off('change:info_modes change:info_url change:banlist', this.updateInfo, this);
+
+        this.$el.remove();
+    }
+});
\ No newline at end of file
index 9084b74a72388677048b97600b3fec92789b398e..71931c3df738d9b9ba166a639ef1185b0c56854a 100644 (file)
@@ -66,7 +66,6 @@ _kiwi.view.MenuBox = Backbone.View.extend({
 
 
     addItem: function(item_name, $item) {
-        $item = $($item);
         if ($item.is('a')) $item.addClass('icon-chevron-right');
         this._items[item_name] = $item;
     },
index 33edeffb79cabf93054688fd7871bf0e5c5cbfb3..d87e13c55a4d615b6f055b33a2d6367e0c87f58d 100644 (file)
@@ -55,7 +55,7 @@ var listeners = {
             irc_connection.write('PRIVMSG ' + args.target + ' :' + block, cb);\r
         });\r
     },\r
-    \r
+\r
 \r
     CTCP: function (args, irc_connection, callback) {\r
         if ((args.target) && (args.type)) {\r
@@ -84,6 +84,13 @@ var listeners = {
     },\r
 \r
 \r
+    CHANNEL_INFO: function (args, irc_connection, callback) {\r
+        if (args.channel) {\r
+            irc_connection.write('MODE ' + args.channel, callback);\r
+        }\r
+    },\r
+\r
+\r
     PART: function (args, irc_connection, callback) {\r
         if (args.channel) {\r
             _.each(args.channel.split(","), function (chan) {\r
index ee8b7e181afd63a4139d3ca25de9bc24e1e694e3..225e1842307fafe9983c3651a00c90d12062ae05 100644 (file)
@@ -235,7 +235,8 @@ function generateSettings(request, debug, callback) {
                 'src/models/panel.js',
                 'src/models/member.js',
                 'src/models/memberlist.js',
-                'src/models/network.js'
+                'src/models/network.js',
+                'src/models/channelinfo.js'
             ],
 
             [
@@ -280,7 +281,8 @@ function generateSettings(request, debug, callback) {
                 'src/views/statusmessage.js',
                 'src/views/tabs.js',
                 'src/views/topicbar.js',
-                'src/views/userbox.js'
+                'src/views/userbox.js',
+                'src/views/channelinfo.js'
             ]
         ]);
     } else {
index 2c207368f0669e102946ac038a1276e96516ae53..7c3a56eef603eadb7fbe227f38fbb184bca9b1c7 100644 (file)
@@ -25,7 +25,8 @@ var IrcChannel = function(irc_connection, name) {
         banlist:        onBanList,
         banlist_end:    onBanListEnd,
         topicsetby:     onTopicSetBy,
-        mode:           onMode
+        mode:           onMode,
+        info:           onChannelInfo
     };
     EventBinder.bindIrcEvents('channel ' + this.name, this.irc_events, this, irc_connection);
 };
@@ -181,15 +182,24 @@ function onTopic(event) {
 }
 
 
+function onChannelInfo(event) {
+    // Channel info event may contain 1 of several types of info,
+    // including creation time, modes. So just pipe the event
+    // right through to the client
+    this.irc_connection.clientEvent('channel_info', event);
+}
+
+
 function onBanList(event) {
     this.ban_list_buffer.push(event);
 }
 
 function onBanListEnd(event) {
-    var that = this;
-    this.ban_list_buffer.forEach(function (ban) {
-        that.irc_connection.clientEvent('banlist', ban);
+    this.irc_connection.clientEvent('banlist', {
+        channel: this.name,
+        bans: this.ban_list_buffer
     });
+
     this.ban_list_buffer = [];
 }
 
index f951dfa16510866d2debb4b27c6817c3f26c1bbe..03899aa66e537cebb8e1fb0d66fc62508334b95a 100644 (file)
@@ -31,6 +31,9 @@ irc_numerics = {
     '321': 'RPL_LISTSTART',
     '322': 'RPL_LIST',
     '323': 'RPL_LISTEND',
+    '324': 'RPL_CHANNELMODEIS',
+    '328': 'RPL_CHANNEL_URL',
+    '329': 'RPL_CREATIONTIME',
     '330': 'RPL_WHOISACCOUNT',
     '331': 'RPL_NOTOPIC',
     '332': 'RPL_TOPIC',
@@ -295,6 +298,34 @@ handlers = {
         });
     },
 
+    'RPL_CHANNELMODEIS': function (command) {
+        var channel = command.params[1],
+            modes = parseModeList.call(this, command.params[2], command.params.slice(3));
+
+        this.irc_connection.emit('channel ' + channel + ' info', {
+            channel: channel,
+            modes: modes
+        });
+    },
+
+    'RPL_CREATIONTIME': function (command) {
+        var channel = command.params[1];
+
+        this.irc_connection.emit('channel ' + channel + ' info', {
+            channel: channel,
+            created_at: parseInt(command.params[2], 10)
+        });
+    },
+
+    'RPL_CHANNEL_URL': function (command) {
+        var channel = command.params[1];
+
+        this.irc_connection.emit('channel ' + channel + ' info', {
+            channel: channel,
+            url: command.trailing
+        });
+    },
+
     'RPL_MOTD': function (command) {
         this.irc_connection.emit('server '  + this.irc_connection.irc_host.hostname + ' motd', {
             motd: command.trailing + '\n'
@@ -543,58 +574,13 @@ handlers = {
     },
 
     'MODE': function (command) {
-        var chanmodes = this.irc_connection.options.CHANMODES || [],
-            prefixes = this.irc_connection.options.PREFIX || [],
-            always_param = (chanmodes[0] || '').concat((chanmodes[1] || '')),
-            modes = [],
-            has_param, i, j, add, event, time;
+        var modes = [], event, time;
 
         // Check if we have a server-time
         time = getServerTime.call(this, command);
 
-        prefixes = _.reduce(prefixes, function (list, prefix) {
-            list.push(prefix.mode);
-            return list;
-        }, []);
-        always_param = always_param.split('').concat(prefixes);
-
-        has_param = function (mode, add) {
-            if (_.find(always_param, function (m) {
-                return m === mode;
-            })) {
-                return true;
-            } else if (add && _.find((chanmodes[2] || '').split(''), function (m) {
-                return m === mode;
-            })) {
-                return true;
-            } else {
-                return false;
-            }
-        };
-
-        if (!command.params[1]) {
-            command.params[1] = command.trailing;
-        }
-
-        j = 0;
-        for (i = 0; i < command.params[1].length; i++) {
-            switch (command.params[1][i]) {
-                case '+':
-                    add = true;
-                    break;
-                case '-':
-                    add = false;
-                    break;
-                default:
-                    if (has_param(command.params[1][i], add)) {
-                        modes.push({mode: (add ? '+' : '-') + command.params[1][i], param: command.params[2 + j]});
-                        j++;
-                    } else {
-                        modes.push({mode: (add ? '+' : '-') + command.params[1][i], param: null});
-                    }
-            }
-        }
-
+        // Get a JSON representation of the modes
+        modes = parseModeList.call(this, command.params[1] || command.trailing, command.params.slice(2));
         event = (_.contains(this.irc_connection.options.CHANTYPES, command.params[0][0]) ? 'channel ' : 'user ') + command.params[0] + ' mode';
 
         this.irc_connection.emit(event, {
@@ -999,6 +985,62 @@ function genericNotice (command, msg, is_error) {
 }
 
 
+/**
+ * Convert a mode string such as '+k pass', or '-i' to a readable
+ * format.
+ * [ { mode: '+k', param: 'pass' } ]
+ * [ { mode: '-i', param: null } ]
+ */
+function parseModeList(mode_string, mode_params) {
+    var chanmodes = this.irc_connection.options.CHANMODES || [],
+        prefixes = this.irc_connection.options.PREFIX || [],
+        always_param = (chanmodes[0] || '').concat((chanmodes[1] || '')),
+        modes = [],
+        has_param, i, j, add;
+
+    prefixes = _.reduce(prefixes, function (list, prefix) {
+        list.push(prefix.mode);
+        return list;
+    }, []);
+    always_param = always_param.split('').concat(prefixes);
+
+    has_param = function (mode, add) {
+        if (_.find(always_param, function (m) {
+            return m === mode;
+        })) {
+            return true;
+        } else if (add && _.find((chanmodes[2] || '').split(''), function (m) {
+            return m === mode;
+        })) {
+            return true;
+        } else {
+            return false;
+        }
+    };
+
+    j = 0;
+    for (i = 0; i < mode_string.length; i++) {
+        switch (mode_string[i]) {
+            case '+':
+                add = true;
+                break;
+            case '-':
+                add = false;
+                break;
+            default:
+                if (has_param(mode_string[i], add)) {
+                    modes.push({mode: (add ? '+' : '-') + mode_string[i], param: mode_params[j]});
+                    j++;
+                } else {
+                    modes.push({mode: (add ? '+' : '-') + mode_string[i], param: null});
+                }
+        }
+    }
+
+    return modes;
+}
+
+
 function getServerTime(command) {
     var time;