Kiwi IRC is a fully featured IRC client that can be extended to suit almost any needs.\r
Using the web application is extremly simple even without any IRC knowledge as all the common needs are built directly into the UI.\r
\r
-For more information see http://kiwiirc.com or an example of the application can be found at http://kiwiirc.com/client/\r
+For more information see https://kiwiirc.com or an example of the application can be found at https://kiwiirc.com/client/.\r
+Our development IRC channel is on the Freenode network, irc.freenode.net #kiwiirc\r
\r
\r
### Installation\r
\r
+*Note: This requires Node.js to run. Make sure you have installed Node.js first! http://nodejs.org/download/*\r
+\r
1. Download the Kiwi source or clone the git repository:\r
\r
`$ git clone git@github.com:prawnsalad/KiwiIRC.git && cd KiwiIRC`\r
\r
-2. Install the dependancies:\r
+2. Install the dependencies:\r
\r
`$ npm install`\r
\r
* Control box
*/
#kiwi #controlbox .input {
- height:1.7em;
+ height:1.7em; position:relative;
}
/* The nick label */
#kiwi #controlbox .nickchange input { }
#kiwi #controlbox .nickchange button { }
+/* Plugin tools */
+#kiwi #controlbox .input_tools { float:right; }
+#kiwi #controlbox .input_tools .tool { margin:0 1em; display:inline; }
+
/**
#kiwi.theme_relaxed .messages a { text-decoration:none; }
#kiwi.theme_relaxed .messages .msg { border-bottom: 1px solid #DEDEDE; padding: 1px; font-family:arial; font-size:0.9em; }
-#kiwi.theme_relaxed .messages .msg .time { width:6em; float:left; color:#777; display:none; }
+#kiwi.theme_relaxed .messages .msg .time { width:5em; float:left; color:#777; padding:5px; display:none; }
#kiwi.theme_relaxed .messages .msg .nick { width:11em; float:left; font-size:12px; font-family:Arial; text-align:right; padding: 5px; overflow:hidden; }
#kiwi.theme_relaxed .messages .msg .text { display:block; margin-left:12em; border-left: 1px solid #DEDEDE; white-space:pre-wrap; word-wrap:break-word; font-family:arial; padding:5px; }
padding:0.5em; margin-top:1em; margin-bottom:1em; margin-right:2em;
}
+#kiwi.theme_relaxed.timestamps .messages .msg .time { display:block; }
+#kiwi.theme_relaxed.timestamps .messages .msg .text { margin-left:17em; }
+
#kiwi.theme_relaxed .messages .msg.global_nick_highlight,
#kiwi.theme_relaxed .messages .msg.highlight { background:#D9D9D9; }
#kiwi.theme_cli #toolbar .panellist li .activity { display:inline; margin-left:0.5em; font-style:italic; font-size:0.8em; }
#kiwi.theme_cli #toolbar .panellist li .activity:before { content:"("; }
#kiwi.theme_cli #toolbar .panellist li .activity:after { content:")"; }
+#kiwi.theme_cli #toolbar .panellist li.alert_highlight .activity {
+ color: #fff; background: #a60400; padding:2px; border-radius:3px;
+}
#kiwi.theme_cli #memberlists { background:#222222; }
}
#kiwi.theme_cli .messages .msg.global_nick_highlight { background:#111111; }
+#kiwi.theme_cli .messages .msg.highlight { background:#111111; }
#kiwi.theme_cli.chanlist_treeview #toolbar #tabs { position:absolute; left:0px; bottom:0px; top:0px; margin:0; width:200px; background:#1B1B1B; overflow-y:auto; }
#kiwi.theme_cli.chanlist_treeview #tabs ul li { display:block; float:none; }
#kiwi.theme_cli.chanlist_treeview #tabs ul li .activity { float:right; }
-#kiwi.theme_cli.chanlist_treeview #tabs ul li.active { padding-left:1em; }
\ No newline at end of file
+#kiwi.theme_cli.chanlist_treeview #tabs ul li.active { padding-left:1em; }
+
+
+
+
+
+
+
+
+
+
+/**
+ * Basic theme
+ */
+#kiwi.theme_basic {
+ background: #FFF;
+ color: #555555;
+}
+#kiwi.theme_basic,
+#kiwi.theme_basic input,
+#kiwi.theme_basic button,
+#kiwi.theme_basic textarea {
+ font-family:Arial, Helvetica, sans-serif;
+ font-size:14px; line-height:1.4em;
+}
+#kiwi.theme_basic input, textarea {
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.5),0 1px 0px rgba(255, 255, 255, 0.3);
+ border: none;
+ border-radius: 3px;
+}
+#kiwi.theme_basic #toolbar {
+ background-color:#1B1B1B; font-size:0.9em;
+}
+#kiwi.theme_basic #controlbox { background-color:#1B1B1B; }
+#kiwi.theme_basic #memberlists_resize_handle {
+ display:none; width:0;
+}
+#kiwi.theme_basic #toolbar .panellist li {
+ line-height: 1.4em;
+ vertical-align: middle;
+
+ border-radius:5px;
+ -moz-border-radius:5px;
+ -webkit-border-radius:5px;
+ -khtml-border-radius:5px;
+
+ border: 1px solid #333;
+ background-color: #eee;
+}
+
+#kiwi.theme_basic #toolbar #tabs { margin-right:0 !important; }
+#kiwi.theme_basic #toolbar .panellist .alert_highlight { font-weight:bold; color:red; }
+#kiwi.theme_basic #toolbar .panellist .alert_activity { font-weight:normal; color:green; }
+#kiwi.theme_basic #toolbar .panellist .alert_action { font-weight:normal; color:green; }
+
+#kiwi.theme_basic #toolbar .panellist .active { padding-right:23px; }
+#kiwi.theme_basic #toolbar .panellist li .part {}
+#kiwi.theme_basic #toolbar .panellist li .part:after { content:"[x]"; }
+
+#kiwi.theme_basic #toolbar .panellist li.server span { }
+
+/* Tab texts are within a span */
+#kiwi.theme_basic #toolbar .panellist li span { line-height:20px; vertical-align:middle; display:inline-block; }
+
+#kiwi.theme_basic #toolbar .panellist li .activity { display:inline; margin-left:0.5em; font-style:italic; font-size:0.8em; }
+#kiwi.theme_basic #toolbar .panellist li .activity:before { content:"("; }
+#kiwi.theme_basic #toolbar .panellist li .activity:after { content:")"; }
+
+#kiwi.theme_basic #status_message {
+ background: #FEEFB3; color: #9F6000;
+ border-bottom: 1px solid;
+ padding: 0.9em;
+ text-align: center; font-size:1.1em;
+}
+#kiwi.theme_basic #status_message.err { color:#D8000C; background:#FFBABA; }
+
+#kiwi.theme_basic .messages { }
+#kiwi.theme_basic .messages.active { }
+#kiwi.theme_basic .messages a { text-decoration:underline; }
+
+#kiwi.theme_basic .messages .msg { }
+#kiwi.theme_basic .messages .msg > div { font-family: Consolas, "Lucida Console", monospace; font-size:0.9em; }
+#kiwi.theme_basic .messages .msg { border: none; }
+#kiwi.theme_basic .messages .msg .time { display:inline; margin-right:1em; margin-left:2px; color:gray; }
+#kiwi.theme_basic .messages .msg .nick { display:inline; margin-right:1em; }
+#kiwi.theme_basic .messages .msg .nick:before { content:"<"; }
+#kiwi.theme_basic .messages .msg .nick:after { content:">"; }
+#kiwi.theme_basic .messages .msg .text { white-space:pre-wrap; word-wrap:break-word; color:#1e1e1e; }
+
+#kiwi.theme_basic .messages .msg.action .nick,
+#kiwi.theme_basic .messages .msg.static .nick,
+#kiwi.theme_basic .messages .msg.topic .nick { display:none; }
+
+#kiwi.theme_basic .messages .msg.action .text { color:#009900; font-style:italic; }
+#kiwi.theme_basic .messages .msg.action.join { color:#009900; }
+#kiwi.theme_basic .messages .msg.action.part .text { color:#900; }
+#kiwi.theme_basic .messages .msg.action.quit .text { color:#900; }
+#kiwi.theme_basic .messages .msg.action.kick .text { color:#900; }
+
+#kiwi.theme_basic .messages .msg.motd { border:none; }
+/*#kiwi.theme_basic .messages .msg.motd .nick { display:none; }*/
+#kiwi.theme_basic .messages .msg.motd .text { color:#666; }
+#kiwi.theme_basic .messages .msg.whois .nick { font-weight:normal; }
+#kiwi.theme_basic .messages .msg.whois .text { padding-left:1em; border-left:1px dashed #999; }
+#kiwi.theme_basic .messages .msg.error .text {
+ border:1px solid #A33F3F; background-color:#D28A8A;
+ padding:0.5em; margin-top:1em; margin-bottom:1em; margin-right:2em;
+}
+
+#kiwi.theme_basic .messages .msg.global_nick_highlight,
+#kiwi.theme_basic .messages .msg.highlight { background:#D9D9D9; }
+
+
+
+#kiwi.theme_basic #memberlists {
+ background-color: #DADADA;
+ border-left: 1px dashed #8A8A8A;
+}
+#kiwi.theme_basic #memberlists ul { list-style: none; margin-left:2px; }
+#kiwi.theme_basic #memberlists ul.active { }
+#kiwi.theme_basic #memberlists ul li { padding:0 2px; }
+#kiwi.theme_basic #memberlists ul li:hover {}
+#kiwi.theme_basic #memberlists ul li a.nick { display:block; color:black; }
+
+#kiwi.theme_basic #memberlists ul li .userbox { margin:0 1em 5px 1em; padding-bottom:0.7em; font-size:.9em; }
+#kiwi.theme_basic #memberlists ul li .userbox a { display:block; text-decoration:none; margin-bottom:2px; }
+#kiwi.theme_basic #memberlists ul li .userbox a i { font-size:1.1em; margin-right:5px; }
+
+
+#kiwi.theme_basic #controlbox .input {
+ background:#fff; margin:3px;
+ height:1.7em;
+}
+#kiwi.theme_basic #controlbox .input .nick {
+ text-align: right;
+ width: 11em;
+ left: 0px;
+ position: absolute;
+ padding: 2px;
+ overflow: hidden;
+}
+#kiwi.theme_basic #controlbox .input .nick:after { content:">"; margin-left:3px; top:2px; }
+#kiwi.theme_basic #controlbox .input .nick a { text-decoration:none; color:black; }
+#kiwi.theme_basic #controlbox .input .input_wrap {
+ position:absolute;
+ right:3px; left:12em;
+ height:1.7em;
+}
+#kiwi.theme_basic #controlbox .input .inp {
+ line-height:1.7em;
+ border: medium none;
+ box-shadow: none;
+ border-radius: 0;
+ resize:none;
+ overflow:hidden;
+ position:relative;
+ height:100%; width:100%;
+ display: block;
+}
+
+
+#kiwi.theme_basic #controlbox .nickchange {
+ padding:10px; left: 0px;
+ background: #1B1B1B; color:#eeeeee;
+}
+#kiwi.theme_basic #controlbox .nickchange input { padding:0.3em 0.5em; }
+#kiwi.theme_basic #controlbox .nickchange button { padding:0.5em; }
+
+
+
+#kiwi.theme_basic #topic { }
+#kiwi.theme_basic #topic div {
+ top:2; bottom:2px; left:0; width:100%;
+ padding: 0.2em 1em;
+ text-align: center;
+ box-shadow: none;
+ border-radius: 0;
+ background-color:#FFF;
+ height: 1.5em;
+ overflow: hidden;
+ outline: none;
+}
+#kiwi.theme_basic #topic:hover div {
+ min-height:1.5em;
+ white-space:pre-wrap; word-wrap:break-word;
+ overflow:visible;
+ background-color:#FFF;
+ z-index: 1;
+ height:auto; bottom:auto;
+ border-bottom: 1px dotted #1B1B1B;
+}
+
+
+#kiwi.theme_basic #toolbar .app_tools { padding-left:10px; color:#D4D4D4; }
+#kiwi.theme_basic #toolbar .app_tools ul li {
+ font-size:26px;
+ -webkit-transition: all .3s ease;
+ -moz-transition: all .3s ease;
+ transition: all .3s ease;
+ margin-left:10px;
+}
+#kiwi.theme_basic #toolbar .app_tools ul li:hover { color:#88C56A; }
+#kiwi.theme_basic #toolbar .app_tools img { }
+
+
+/* The server select dialog */
+#kiwi.theme_basic .server_select { width:730px; padding:3em 0 2em 0; margin: 0 auto; }
+#kiwi.theme_basic .server_select a { text-decoration: none; }
+#kiwi.theme_basic .server_select .more { display: none; width:270px; margin:0 auto; }
+#kiwi.theme_basic .server_select table tr td { padding:5px; }
+#kiwi.theme_basic .server_select button { float:right; padding:3px 7px; }
+#kiwi.theme_basic .server_select input { padding:3px 7px; width:150px; }
+#kiwi.theme_basic .server_select label { }
+#kiwi.theme_basic .server_select br { clear:both; }
+#kiwi.theme_basic .server_select .basic input, .server_select .basic button { font-size:1em; padding:0.5em 1em; }
+#kiwi.theme_basic .server_select .basic input { width:170px; }
+#kiwi.theme_basic .server_select .basic label { font-size:1.3em; margin-top:4px; }
+#kiwi.theme_basic .server_select .basic tr.have_pass { font-size:0.8em; }
+#kiwi.theme_basic .server_select .basic tr.channel td { padding-top:1em; }
+#kiwi.theme_basic .server_select .basic { border-bottom: 1px dashed gray; margin-bottom:1em; }
+#kiwi.theme_basic .server_select .basic .show_more { display: block; width:110px; margin:10px 0 0 0; font-size:0.8em; background: url(../img/more.png) no-repeat right 7px; }
+#kiwi.theme_basic .server_select.single_server .basic { border:none; }
+#kiwi.theme_basic .server_select .status { text-align: center; font-weight: bold; padding:1em; }
+#kiwi.theme_basic .server_select .status.ok { }
+#kiwi.theme_basic .server_select .status.error {
+ border:1px solid #A33F3F; background-color:#D28A8A;
+ padding:0.5em; margin-top:1em; margin-bottom:1em; margin-right:2em;
+}
+
+
+#kiwi.theme_basic .server_select .kiwi_logo { text-align: center; display:block; }
+#kiwi.theme_basic .server_select .kiwi_logo h1 {
+ font-size:20px;
+ line-height:48px; vertical-align: middle;
+ color: #555555;
+}
+#kiwi.theme_basic .server_select .kiwi_logo img { }
+
+
+#kiwi.theme_basic.chanlist_treeview #panels { left:160px; }
+#kiwi.theme_basic.chanlist_treeview #toolbar { position:static; }
+#kiwi.theme_basic.chanlist_treeview #toolbar .app_tools { float:none; }
+#kiwi.theme_basic.chanlist_treeview #toolbar > div { margin-left:160px; }
+#kiwi.theme_basic.chanlist_treeview #toolbar #tabs { position:absolute; left:0px; bottom:0px; top:0px; margin:0; width:160px; background:#1B1B1B; overflow-y:auto; }
+#kiwi.theme_basic.chanlist_treeview #tabs ul li { display:block; float:none; }
+#kiwi.theme_basic.chanlist_treeview #tabs ul li .activity { float:right; }
+#kiwi.theme_basic.chanlist_treeview #tabs ul li.active {
+ margin-right:0;
+ border-right-width:0;
+ border-bottom-right-radius:0;
+ border-top-right-radius:0;
+}
\ No newline at end of file
* and data (think: plugins)\r
*/\r
_kiwi.global = {\r
- settings: undefined,\r
+ settings: undefined, // Instance of _kiwi.model.DataStore\r
plugins: undefined,\r
utils: undefined, // TODO: Re-usable methods\r
user: undefined, // TODO: Limited user methods\r
}\r
\r
_.extend(this, Backbone.Events);\r
+ this._source = event_source;\r
\r
// Proxy the events to this dispatcher\r
event_source.on('all', proxyEvent, this);\r
this.dispose = function () {\r
event_source.off('all', proxyEvent);\r
this.off();\r
+ delete this.event_source;\r
};\r
},\r
\r
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'\r
+ notice: 'notice', msg: 'privmsg',\r
+ get: 'get'\r
};\r
\r
_.each(funcs, function(gateway_fn, func_name) {\r
obj[func_name] = function() {\r
var fn_name = gateway_fn;\r
- _kiwi.gateway[fn_name].apply(_kiwi.gateway, arguments);\r
+ return _kiwi.gateway[fn_name].apply(_kiwi.gateway, arguments);\r
};\r
});\r
\r
};\r
\r
_.each(funcs, function(controlbox_fn, func_name) {\r
- obj[func_name] = _kiwi.app.controlbox[controlbox_fn];\r
+ obj[func_name] = function() {\r
+ var fn_name = controlbox_fn;\r
+ return _kiwi.app.controlbox[fn_name].apply(_kiwi.app.controlbox, arguments);\r
+ };\r
});\r
\r
return obj;\r
this.model.on('applet_loaded', function () {
that.$el.parent().css('height', '100%');
- $script('http://d1n0x3qji82z53.cloudfront.net/src-min-noconflict/ace.js', function (){ that.createAce(); });
+ $script('https://d1n0x3qji82z53.cloudfront.net/src-min-noconflict/ace.js', function (){ that.createAce(); });
});
},
} else {\r
this.$el.find('.setting-show_joins_parts').attr('checked', false);\r
}\r
+\r
+ if (typeof settings.get('show_timestamps') === 'undefined' || !settings.get('show_timestamps')) {\r
+ this.$el.find('.setting-show_timestamps').attr('checked', false);\r
+ } else {\r
+ this.$el.find('.setting-show_timestamps').attr('checked', true);\r
+ }\r
},\r
\r
\r
settings.set('channel_list_style', $('.setting-channel_list_style', this.$el).val());\r
settings.set('scrollback', $('.setting-scrollback', this.$el).val());\r
settings.set('show_joins_parts', $('.setting-show_joins_parts', this.$el).is(':checked'));\r
+ settings.set('show_timestamps', $('.setting-show_timestamps', this.$el).is(':checked'));\r
\r
settings.save();\r
\r
<title> KiwiIRC </title>\r
\r
<link rel="stylesheet" type="text/css" href="<%base_path%>/assets/css/style.css" />\r
-<link rel="stylesheet" type="text/css" href="<%base_path%>/assets/css/font-awesome.css" />\r
+<link rel="stylesheet" type="text/css" href="<%base_path%>/assets/css/font-awesome.min.css" />\r
<!--[if IE 7]>\r
<link rel="stylesheet" type="text/css" href="/kiwi/assets/css/font-awesome-ie7.css" />\r
<![endif]-->\r
<div class="app_tools">\r
<ul class="main">\r
<li class="settings"><i class="icon-cogs" title="Settings"></i></li>\r
- <li><a href="http://kiwiirc.com/" target="_blank"><img src="<%base_path%>/assets/img/ico.png" alt="KiwiIRC" title="KiwiIRC" /></a></li>\r
+ <li><a href="https://kiwiirc.com/" target="_blank"><img src="<%base_path%>/assets/img/ico.png" alt="KiwiIRC" title="KiwiIRC" /></a></li>\r
</ul>\r
</div>\r
\r
<div class="input">\r
<span class="nick"> </span>\r
<div class="input_wrap"><textarea class="inp"></textarea></div>\r
+ <div class="input_tools"></div>\r
</div>\r
</div>\r
</div>\r
</div>\r
\r
<div class="about_kiwi" style="position:relative;float:left;width:320px;margin-left:3em;color:#555555;">\r
- <a class="kiwi_logo" href="http://www.kiwiirc.com/" target="_blank">\r
+ <a class="kiwi_logo" href="https://kiwiirc.com/" target="_blank">\r
<img src="<%base_path%>/assets/img/ico.png" alt="KiwiIRC Logo" title="Kiwi IRC" /> <br />\r
<h1>Powered by Kiwi IRC</h1>\r
</a>\r
\r
<p style="font-style:italic;">A <strong>hand-crafted IRC client</strong> that you can enjoy. Designed to be used <strong>easily</strong> and <strong>freely</strong>.</p>\r
\r
- <p style="font-size:0.9em;margin-top:2em;">Peek at the <a href="http://www.kiwiirc.com/">Kiwi IRC homepage</a> for more information or to find out how to embed it on your own website. Looking for source code? Try the <a href="http://github.com/prawnsalad/KiwiIRC/">GitHub</a> page. This network of people may not be associated with Kiwi IRC itself.</p>\r
+ <p style="font-size:0.9em;margin-top:2em;">Peek at the <a href="https://kiwiirc.com/">Kiwi IRC homepage</a> for more information or to find out how to embed it on your own website. Looking for source code? Try the <a href="http://github.com/prawnsalad/KiwiIRC/">GitHub</a> page. This network of people may not be associated with Kiwi IRC itself.</p>\r
</div>\r
</div>\r
</script>\r
<option value="relaxed">Relaxed</option>\r
<option value="mini">Mini</option>\r
<option value="cli">CLI</option>\r
+ <option value="basic">Basic</option>\r
</select>\r
</td>\r
</tr>\r
<td class="setting"><input type="checkbox" class="setting-show_joins_parts" /></td>\r
</tr>\r
\r
+ <tr>\r
+ <td class="label">Show timestamps</td>\r
+ <td class="setting"><input type="checkbox" class="setting-show_timestamps" /></td>\r
+ </tr>\r
+\r
<tr class="save">\r
<td colspan="2"><button class="save">Save</button>​​​​​​​​​</td>\r
</tr>\r
this.populateDefaultServerSettings = function () {\r
var parts;\r
var defaults = {\r
- nick: getQueryVariable('nick') || 'kiwi_?',\r
- server: 'irc.kiwiirc.com',\r
+ nick: getQueryVariable('nick') || '',\r
+ server: '',\r
port: 6667,\r
ssl: false,\r
- channel: window.location.hash || '#kiwiirc',\r
+ channel: window.location.hash || '#chat',\r
channel_key: ''\r
};\r
var uricheck;\r
// If any settings have been given by the server.. override any auto detected settings\r
/**\r
* Get any server restrictions as set in the server config\r
- * These settings can not changed in the server selection dialog\r
+ * These settings can not be changed in the server selection dialog\r
*/\r
if (this.server_settings && this.server_settings.connection) {\r
if (this.server_settings.connection.server) {\r
var panel,\r
is_pm = (event.channel == _kiwi.gateway.get('nick'));\r
\r
+ // An ignored user? don't do anything with it\r
+ if (gw.isNickIgnored(event.nick)) {\r
+ return;\r
+ }\r
+\r
if (is_pm) {\r
// If a panel isn't found for this PM, create one\r
panel = that.panels.getByName(event.nick);\r
panel = that.panels.server;\r
}\r
}\r
- \r
+\r
panel.addMsg(event.nick, event.msg);\r
});\r
\r
\r
gw.on('onctcp_request', function (event) {\r
+ // An ignored user? don't do anything with it\r
+ if (gw.isNickIgnored(event.nick)) {\r
+ return;\r
+ }\r
+\r
// Reply to a TIME ctcp\r
if (event.msg.toUpperCase() === 'TIME') {\r
gw.ctcp(false, event.type, event.nick, (new Date()).toString());\r
\r
\r
gw.on('onctcp_response', function (event) {\r
+ // An ignored user? don't do anything with it\r
+ if (gw.isNickIgnored(event.nick)) {\r
+ return;\r
+ }\r
+ \r
that.panels.server.addMsg('[' + event.nick + ']', 'CTCP ' + event.msg);\r
});\r
\r
gw.on('onnotice', function (event) {\r
var panel;\r
\r
+ // An ignored user? don't do anything with it\r
+ if (event.nick && gw.isNickIgnored(event.nick)) {\r
+ return;\r
+ }\r
+\r
// Find a panel for the destination(channel) or who its from\r
panel = that.panels.getByName(event.target) || that.panels.getByName(event.nick);\r
if (!panel) {\r
var panel,\r
is_pm = (event.channel == _kiwi.gateway.get('nick'));\r
\r
+ // An ignored user? don't do anything with it\r
+ if (gw.isNickIgnored(event.nick)) {\r
+ return;\r
+ }\r
+\r
if (is_pm) {\r
// If a panel isn't found for this PM, create one\r
panel = that.panels.getByName(event.nick);\r
\r
controlbox.on('command:clear', clearCommand);\r
\r
- controlbox.on('command_ctcp', ctcpCommand);\r
+ controlbox.on('command:ctcp', ctcpCommand);\r
\r
\r
controlbox.on('command:css', function (ev) {\r
ev.params.shift();\r
\r
value = ev.params.join(' ');\r
+\r
+ // If we're setting a true boolean value..\r
+ if (value === 'true')\r
+ value = true;\r
+\r
+ // If we're setting a false boolean value..\r
+ if (value === 'false')\r
+ value = false;\r
+\r
+ // If we're setting a number..\r
+ if (parseInt(value, 10).toString() === value)\r
+ value = parseInt(value, 10);\r
+\r
_kiwi.global.settings.set(setting, value);\r
}\r
\r
// Read the value to the user\r
- _kiwi.app.panels.active.addMsg('', setting + ' = ' + _kiwi.global.settings.get(setting));\r
+ _kiwi.app.panels.active.addMsg('', setting + ' = ' + _kiwi.global.settings.get(setting).toString());\r
});\r
\r
\r
controlbox.preprocessor.aliases[name] = rule;\r
});\r
\r
+ \r
+ controlbox.on('command:ignore', function (ev) {\r
+ var list = _kiwi.gateway.get('ignore_list');\r
+\r
+ // No parameters passed so list them\r
+ if (!ev.params[0]) {\r
+ if (list.length > 0) {\r
+ _kiwi.app.panels.active.addMsg(' ', 'Ignored nicks:');\r
+ $.each(list, function (idx, ignored_pattern) {\r
+ _kiwi.app.panels.active.addMsg(' ', ignored_pattern);\r
+ });\r
+ } else {\r
+ _kiwi.app.panels.active.addMsg(' ', 'Not ignoring anybody');\r
+ }\r
+ return;\r
+ }\r
+\r
+ // We have a parameter, so add it\r
+ list.push(ev.params[0]);\r
+ _kiwi.gateway.set('ignore_list', list);\r
+ _kiwi.app.panels.active.addMsg(' ', 'Ignoring ' + ev.params[0]);\r
+ });\r
+\r
+\r
+ controlbox.on('command:unignore', function (ev) {\r
+ var list = _kiwi.gateway.get('ignore_list');\r
+\r
+ if (!ev.params[0]) {\r
+ _kiwi.app.panels.active.addMsg(' ', 'Specifiy which nick you wish to stop ignoring');\r
+ return;\r
+ }\r
+\r
+ list = _.reject(list, function(pattern) {\r
+ return pattern === ev.params[0];\r
+ });\r
+\r
+ _kiwi.gateway.set('ignore_list', list);\r
+\r
+ _kiwi.app.panels.active.addMsg(' ', 'Stopped ignoring ' + ev.params[0]);\r
+ });\r
+\r
+\r
controlbox.on('command:applet', appletCommand);\r
controlbox.on('command:settings', settingsCommand);\r
controlbox.on('command:script', scriptCommand);\r
\r
channel_names = ev.params.join(' ').split(',');\r
\r
- $.each(channel_names, function (index, channel_name) {\r
+ $.each(channel_names, function (index, channel_name_key) {\r
+ // We may have a channel key so split it off\r
+ var spli = channel_name_key.split(' '),\r
+ channel_name = spli[0],\r
+ channel_key = spli[1] || '';\r
+\r
// Trim any whitespace off the name\r
channel_name = channel_name.trim();\r
\r
+ // If not a valid channel name, display a warning\r
+ if (!that.isChannelName(channel_name)) {\r
+ _kiwi.app.panels.server.addMsg('', channel_name + ' is not a valid channel name');\r
+ _kiwi.app.message.text(channel_name + ' is not a valid channel name', {timeout: 5000});\r
+ return;\r
+ }\r
+\r
// Check if we have the panel already. If not, create it\r
channel = that.panels.getByName(channel_name);\r
if (!channel) {\r
_kiwi.app.panels.add(channel);\r
}\r
\r
- _kiwi.gateway.join(channel_name);\r
+ _kiwi.gateway.join(channel_name, channel_key);\r
});\r
\r
if (channel) channel.view.show();\r
* The URL to the Kiwi server\r
* @type String\r
*/\r
- kiwi_server: '//kiwi'\r
+ kiwi_server: '//kiwi',\r
+\r
+ /**\r
+ * List of nicks we are ignoring\r
+ * @type Array\r
+ */\r
+ ignore_list: []\r
};\r
\r
\r
$.each(data.options, function (name, value) {\r
switch (name) {\r
case 'CHANTYPES':\r
- // TODO: Check this. Why is it only getting the first char?\r
- that.set('channel_prefix', value.join('').charAt(0));\r
+ that.set('channel_prefix', value.join(''));\r
break;\r
case 'NETWORK':\r
that.set('name', value);\r
};\r
\r
\r
+ // Check a nick alongside our ignore list\r
+ this.isNickIgnored = function (nick) {\r
+ var idx, list = this.get('ignore_list');\r
+ var pattern, regex;\r
+\r
+ for (idx = 0; idx < list.length; idx++) {\r
+ pattern = list[idx].replace(/([.+^$[\]\\(){}|-])/g, "\\$1")\r
+ .replace('*', '.*')\r
+ .replace('?', '.');\r
+\r
+ regex = new RegExp(pattern, 'i');\r
+ if (regex.test(nick)) return true;\r
+ }\r
+\r
+ return false;\r
+ }\r
+\r
+\r
return new (Backbone.Model.extend(this))(arguments);\r
};
\ No newline at end of file
\r
// Update the scrollback\r
bs = this.get("scrollback");\r
- bs.push(message_obj);\r
-\r
- // Keep the scrolback limited\r
- if (bs.length > scrollback) {\r
- bs.splice(scrollback);\r
+ if (bs) {\r
+ bs.push(message_obj);\r
+\r
+ // Keep the scrolback limited\r
+ if (bs.length > scrollback) {\r
+ bs.splice(scrollback);\r
+ }\r
+ this.set({"scrollback": bs}, {silent: true});\r
}\r
- this.set({"scrollback": bs}, {silent: true});\r
\r
this.trigger("msg", message_obj);\r
},\r
},\r
\r
submitForm: function (event) {\r
+ event.preventDefault();\r
+\r
+ // Make sure a nick is chosen\r
+ if (!$('input.nick', this.$el).val().trim()) {\r
+ this.setStatus('Select a nickname first!');\r
+ $('input.nick', this.$el).select();\r
+ return;\r
+ }\r
+\r
if (state === 'nick_change') {\r
this.submitNickChange(event);\r
} else {\r
}\r
\r
$('button', this.$el).attr('disabled', 1);\r
- return false;\r
+ return;\r
},\r
\r
submitLogin: function (event) {\r
$('.status', this.$el)\r
.text(text)\r
.attr('class', 'status')\r
- .addClass(class_name)\r
+ .addClass(class_name||'')\r
.show();\r
},\r
clearStatus: function () {\r
}\r
\r
(function () {\r
- var tokens = inp_val.substring(0, inp[0].selectionStart).split(' '),\r
- val,\r
- p1,\r
- newnick,\r
- range,\r
- nick = tokens[tokens.length - 1];\r
+ var tokens, // Words before the cursor position\r
+ val, // New value being built up\r
+ p1, // Position in the value just before the nick \r
+ newnick, // New nick to be displayed (cycles through)\r
+ range, // TextRange for setting new text cursor position\r
+ nick, // Current nick in the value\r
+ trailing = ': '; // Text to be inserted after a tabbed nick\r
+\r
+ tokens = inp_val.substring(0, inp[0].selectionStart).split(' ');\r
+ if (tokens[tokens.length-1] == ':')\r
+ tokens.pop();\r
+\r
+ nick = tokens[tokens.length - 1];\r
+\r
if (this.tabcomplete.prefix === '') {\r
this.tabcomplete.prefix = nick;\r
}\r
});\r
\r
if (this.tabcomplete.data.length > 0) {\r
+ // Get the current value before cursor position\r
p1 = inp[0].selectionStart - (nick.length);\r
val = inp_val.substr(0, p1);\r
+\r
+ // Include the current selected nick\r
newnick = this.tabcomplete.data.shift();\r
this.tabcomplete.data.push(newnick);\r
val += newnick;\r
+\r
+ if (inp_val.substr(inp[0].selectionStart, 2) !== trailing)\r
+ val += trailing;\r
+\r
+ // Now include the rest of the current value\r
val += inp_val.substr(inp[0].selectionStart);\r
+\r
inp.val(val);\r
\r
+ // Move the cursor position to the end of the nick\r
if (inp[0].setSelectionRange) {\r
- inp[0].setSelectionRange(p1 + newnick.length, p1 + newnick.length);\r
+ inp[0].setSelectionRange(p1 + newnick.length + trailing.length, p1 + newnick.length + trailing.length);\r
} else if (inp[0].createTextRange) { // not sure if this bit is actually needed....\r
range = inp[0].createTextRange();\r
range.collapse(true);\r
- range.moveEnd('character', p1 + newnick.length);\r
- range.moveStart('character', p1 + newnick.length);\r
+ range.moveEnd('character', p1 + newnick.length + trailing.length);\r
+ range.moveStart('character', p1 + newnick.length + trailing.length);\r
range.select();\r
}\r
}\r
opt.timeout = opt.timeout || 5000;\r
\r
this.$el.text(text).attr('class', opt.type);\r
- this.$el.slideDown(_kiwi.app.view.doLayout);\r
+ this.$el.slideDown($.proxy(_kiwi.app.view.doLayout, this));\r
\r
if (opt.timeout) this.doTimeout(opt.timeout);\r
},\r
},\r
\r
hide: function () {\r
- this.$el.slideUp(_kiwi.app.view.doLayout);\r
+ this.$el.slideUp($.proxy(_kiwi.app.view.doLayout, this));\r
},\r
\r
doTimeout: function (length) {\r
\r
_kiwi.view.Application = Backbone.View.extend({\r
initialize: function () {\r
- $(window).resize(this.doLayout);\r
- $('#toolbar').resize(this.doLayout);\r
- $('#controlbox').resize(this.doLayout);\r
+ var that = this;\r
+\r
+ $(window).resize(function() { that.doLayout.apply(that); });\r
+ $('#toolbar').resize(function() { that.doLayout.apply(that); });\r
+ $('#controlbox').resize(function() { that.doLayout.apply(that); });\r
\r
// Change the theme when the config is changed\r
_kiwi.global.settings.on('change:theme', this.updateTheme, this);\r
_kiwi.global.settings.on('change:channel_list_style', this.setTabLayout, this);\r
this.setTabLayout(_kiwi.global.settings.get('channel_list_style'));\r
\r
+ _kiwi.global.settings.on('change:show_timestamps', this.displayTimestamps, this);\r
+ this.displayTimestamps(_kiwi.global.settings.get('show_timestamps'));\r
+\r
this.doLayout();\r
\r
$(document).keydown(this.setKeyFocus);\r
},\r
\r
\r
+ displayTimestamps: function (show_timestamps) {\r
+ // If called by the settings callback, get the correct new_value\r
+ if (show_timestamps === _kiwi.global.settings) {\r
+ show_timestamps = arguments[1];\r
+ }\r
+\r
+ if (show_timestamps) {\r
+ this.$el.addClass('timestamps');\r
+ } else {\r
+ this.$el.removeClass('timestamps');\r
+ }\r
+ },\r
+\r
+\r
// Globally shift focus to the command input box on a keypress\r
setKeyFocus: function (ev) {\r
// If we're copying text, don't shift focus\r
\r
\r
doLayout: function () {\r
- var el_kiwi = $('#kiwi');\r
- var el_panels = $('#panels');\r
- var el_memberlists = $('#memberlists');\r
- var el_toolbar = $('#toolbar');\r
- var el_controlbox = $('#controlbox');\r
- var el_resize_handle = $('#memberlists_resize_handle');\r
+ var el_kiwi = this.$el;\r
+ var el_panels = $('#kiwi #panels');\r
+ var el_memberlists = $('#kiwi #memberlists');\r
+ var el_toolbar = $('#kiwi #toolbar');\r
+ var el_controlbox = $('#kiwi #controlbox');\r
+ var el_resize_handle = $('#kiwi #memberlists_resize_handle');\r
\r
var css_heights = {\r
top: el_toolbar.outerHeight(true),\r
\r
// If we have channel tabs on the side, adjust the height\r
if (el_kiwi.hasClass('chanlist_treeview')) {\r
- $('#kiwi #tabs').css(css_heights);\r
+ $('#tabs', el_kiwi).css(css_heights);\r
}\r
\r
// Determine if we have a narrow window (mobile/tablet/or even small desktop window)\r
// And move the handle just out of sight to the right\r
el_resize_handle.css('left', el_panels.outerWidth(true));\r
}\r
+\r
+ var input_wrap_width = parseInt($('#kiwi #controlbox .input_tools').outerWidth());\r
+ el_controlbox.find('.input_wrap').css('right', input_wrap_width + 7);\r
},\r
\r
\r
var that = this;\r
\r
if (!instant) {\r
- $('#toolbar').slideUp({queue: false, duration: 400, step: this.doLayout});\r
- $('#controlbox').slideUp({queue: false, duration: 400, step: this.doLayout});\r
+ $('#toolbar').slideUp({queue: false, duration: 400, step: $.proxy(this.doLayout, this)});\r
+ $('#controlbox').slideUp({queue: false, duration: 400, step: $.proxy(this.doLayout, this)});\r
} else {\r
$('#toolbar').slideUp(0);\r
$('#controlbox').slideUp(0);\r
var that = this;\r
\r
if (!instant) {\r
- $('#toolbar').slideDown({queue: false, duration: 400, step: this.doLayout});\r
- $('#controlbox').slideDown({queue: false, duration: 400, step: this.doLayout});\r
+ $('#toolbar').slideDown({queue: false, duration: 400, step: $.proxy(this.doLayout, this)});\r
+ $('#controlbox').slideDown({queue: false, duration: 400, step: $.proxy(this.doLayout, this)});\r
} else {\r
$('#toolbar').slideDown(0);\r
$('#controlbox').slideDown(0);\r
+// Do we want to enable the built in Identd server?
+conf.identd = {
+ enabled: false,
+ port: 113,
+ address: "0.0.0.0"
+};
+
+
+
// Some IRCDs require the clients IP via the username/ident
conf.ip_as_username = [
- "irc.network.com",
- "127.0.0.1"
+ //"irc.network.com",
+ //"127.0.0.1"
];
// Whether to verify IRC servers' SSL certificates against built-in well-known certificate authorities
};
-// If not empty, the client may only connect to this 1 IRC server
+// If set, the client may only connect to this 1 IRC server
//conf.restrict_server = "irc.kiwiirc.com";
//conf.restrict_server_port = 6667;
//conf.restrict_server_ssl = false;
/*
* Do not ammend the below lines unless you understand the changes!
*/
-module.exports.production = conf;
\ No newline at end of file
+module.exports.production = conf;
if (command.hostname && command.port && command.nick) {
var con;
- if (global.config.restrict_server) {
- this.state.connect(
- global.config.restrict_server,
- global.config.restrict_server_port,
- global.config.restrict_server_ssl,
- command.nick,
- {hostname: this.websocket.handshake.revdns, address: this.websocket.handshake.real_address},
- global.config.restrict_server_password,
- callback);
-
- } else {
- this.state.connect(
- command.hostname,
- command.port,
- command.ssl,
- command.nick,
- {hostname: this.websocket.handshake.revdns, address: this.websocket.handshake.real_address},
- command.password,
- callback);
- }
+ this.state.connect(
+ (global.config.restrict_server || command.hostname),
+ (global.config.restrict_server_port || command.port),
+ (global.config.restrict_server_ssl || command.ssl),
+ command.nick,
+ {hostname: this.websocket.handshake.revdns, address: this.websocket.handshake.real_address},
+ (global.config.restrict_server_password || command.password),
+ callback);
} else {
return callback('Hostname, port and nickname must be specified');
}
// TODO: Should this close all the websocket connections too?
function websocketError() {
this.dispose();
-}
\ No newline at end of file
+}
--- /dev/null
+var net = require('net');
+
+function IdentdServer(opts) {
+
+ // Option defaults
+ opts = opts || {};
+ opts.bind_addr = opts.bind_addr || '0.0.0.0';
+ opts.bind_port = opts.bind_port || 113;
+ opts.system_id = opts.system_id || 'UNIX-KiwiIRC';
+ opts.user_id = opts.user_id || 'kiwi';
+
+
+ var server = net.createServer(function(socket) {
+ var user, system;
+
+ if (typeof opts.user_id === 'function') {
+ user = opts.user_id(socket).toString();
+ } else {
+ user = opts.user_id.toString();
+ }
+
+ if (typeof opts.system_id === 'function') {
+ system = opts.system_id(socket).toString();
+ } else {
+ system = opts.system_id.toString();
+ }
+
+ socket.end('25,25 : USERID : ' + system + ' : ' + user);
+ });
+
+ server.on('listening', function() {
+ console.log('Ident Server listening on ' + server.address().address + ':' + server.address().port);
+ });
+
+
+ this.start = function() {
+ server.listen(opts.bind_port, opts.bind_addr);
+ };
+
+ this.stop = function(callback) {
+ server.close(callback);
+ };
+}
+
+
+module.exports = IdentdServer;
\ No newline at end of file
function onNotice(event) {
- this.irc_connection.clientEvent('msg', {
+ this.irc_connection.clientEvent('notice', {
nick: event.nick,
ident: event.ident,
hostname: event.hostname,
- channel: this.name,
- msg: event.trailing
+ target: event.target,
+ msg: event.msg
});
};
State.prototype.connect = function (hostname, port, ssl, nick, user, pass, callback) {
var that = this;
var con, con_num;
- if (global.config.restrict_server) {
- con = new IrcConnection(
- global.config.restrict_server,
- global.config.restrict_server_port,
- global.config.restrict_server_ssl,
- nick,
- user,
- global.config.restrict_server_password,
- this);
-
- } else {
- con = new IrcConnection(
- hostname,
- port,
- ssl,
- nick,
- user,
- pass,
- this);
- }
+ con = new IrcConnection(
+ hostname,
+ port,
+ ssl,
+ nick,
+ user,
+ pass,
+ this);
con_num = this.next_connection++;
this.irc_connections[con_num] = con;
WebListener = require('./weblistener.js'),
config = require('./configuration.js'),
rehash = require('./rehash.js'),
- modules = require('./modules.js');
+ modules = require('./modules.js'),
+ Identd = require('./identd.js');
// If we're not running in the forground and we have a log file.. switch
// console.log to output to a file
-if (process.argv.indexOf('-f') === -1 && global.config.log) {
+if (process.argv.indexOf('-f') === -1 && global.config && global.config.log) {
(function () {
var log_file_name = global.config.log;
+/*
+ * Identd server
+ */
+if (global.config.identd && global.config.identd.enabled) {
+ new Identd({
+ bind_addr: global.config.identd.address,
+ bind_port: global.config.identd.port
+ }).start();
+}
+
+
+
+
/*
* Web listeners
*/