commit
5e70391bcf
7 changed files with 632 additions and 118 deletions
|
@ -58,7 +58,14 @@ td.time {
|
||||||
}
|
}
|
||||||
|
|
||||||
.repeated-time {
|
.repeated-time {
|
||||||
display: none;
|
}
|
||||||
|
.repeated-time .cof-chat_time,
|
||||||
|
.repeated-time .cof-chat_time_delimiters {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.repeated-time .cob-chat_time,
|
||||||
|
.repeated-time .cob-chat_time_delimiters {
|
||||||
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
td.prefix {
|
td.prefix {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
@ -100,7 +107,6 @@ body {
|
||||||
|
|
||||||
input#sendMessage {
|
input#sendMessage {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: large;
|
|
||||||
}
|
}
|
||||||
#footer button {
|
#footer button {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
@ -111,7 +117,7 @@ input#sendMessage {
|
||||||
input[type=text], input[type=password], .badge {
|
input[type=text], input[type=password], .badge {
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
color: #6e6e6e;
|
color: #ccc;
|
||||||
box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.1), 0px 1px 7px 0px rgba(0, 0, 0, 0.8) inset;
|
box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.1), 0px 1px 7px 0px rgba(0, 0, 0, 0.8) inset;
|
||||||
background: none repeat scroll 0% 0% rgba(0, 0, 0, 0.3);
|
background: none repeat scroll 0% 0% rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
@ -124,30 +130,30 @@ input[type=text], input[type=password], .badge {
|
||||||
line-height: 25px;
|
line-height: 25px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
#topbar .brand {
|
#topbar .brand {
|
||||||
float: left;
|
float: left;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
}
|
}
|
||||||
#topbar .brand img {
|
#topbar .brand img {
|
||||||
height: 25px;
|
height: 25px;
|
||||||
}
|
}
|
||||||
#topbar .title {
|
#topbar .title {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 14%;
|
left: 14%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
#topbar .actions {
|
#topbar .actions {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 0;
|
right: 0;
|
||||||
background: #282828;
|
background: #282828;
|
||||||
}
|
}
|
||||||
#topbar .dropdown-menu form {
|
#topbar .dropdown-menu form {
|
||||||
padding-left: 6px;
|
padding-left: 6px;
|
||||||
padding-right: 6px;
|
padding-right: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#topbar, #sidebar, .panel, .dropdown-menu {
|
#topbar, #sidebar, .panel, .dropdown-menu {
|
||||||
background: #282828;
|
background: #282828;
|
||||||
|
@ -162,13 +168,50 @@ input[type=text], input[type=password], .badge {
|
||||||
padding-top: 35px; /* topbar */
|
padding-top: 35px; /* topbar */
|
||||||
font-size: smaller;
|
font-size: smaller;
|
||||||
}
|
}
|
||||||
#sidebar form {
|
#sidebar form {
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar .badge {
|
#sidebar .badge {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
margin-right: -15px;
|
margin-right: -15px;
|
||||||
}
|
}
|
||||||
|
#nicklist {
|
||||||
|
position: fixed;
|
||||||
|
width: 100px;
|
||||||
|
min-height: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
padding-top: 25px;
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-bottom: 35px;
|
||||||
|
}
|
||||||
|
#nicklist ul {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
#nicklist li,
|
||||||
|
#nicklist a {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#nicklist a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
#nicklist a:hover {
|
||||||
|
background: #3b3b3b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#connection-infos {
|
||||||
|
float: left;
|
||||||
|
max-width: 10%;
|
||||||
|
padding-left: 5px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #aaa;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.nav-pills > li > a {
|
.nav-pills > li > a {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
color: #ddd;
|
color: #ddd;
|
||||||
|
@ -181,16 +224,22 @@ input[type=text], input[type=password], .badge {
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#bufferlines {
|
.monospace {
|
||||||
font-family: 'Terminus', 'Consolas', 'Monaco', 'Inconsolata', 'Ubuntu Mono', monospace;
|
font-family: 'Terminus', 'Consolas', 'Monaco', 'Inconsolata', 'Ubuntu Mono', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bufferlines {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 99%;
|
height: 99%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
margin-left: 14%;
|
margin-left: 14%; /* sidebar */
|
||||||
width: auto;
|
width: auto;
|
||||||
top: 25px; /* topbar */
|
top: 25px; /* topbar */
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
.withnicklist {
|
||||||
|
margin-right: 100px !important; /* nicklist */
|
||||||
|
}
|
||||||
#bufferlines .btn {
|
#bufferlines .btn {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
}
|
}
|
||||||
|
@ -202,7 +251,7 @@ input[type=text], input[type=password], .badge {
|
||||||
background-color: #181818;
|
background-color: #181818;
|
||||||
}
|
}
|
||||||
@media (max-width: 968px) {
|
@media (max-width: 968px) {
|
||||||
#sidebar, #bufferlines {
|
#sidebar, #bufferlines, #nicklist {
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
|
@ -210,7 +259,7 @@ input[type=text], input[type=password], .badge {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
#sidebar {
|
#sidebar, #nicklist {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
@ -235,50 +284,6 @@ input[type=text], input[type=password], .badge {
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-28 {
|
|
||||||
color: greenyellow;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-00 {
|
|
||||||
color: coral;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-yellow {
|
|
||||||
color: yellow;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-dark-red {
|
|
||||||
color: darkred;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-dark-green {
|
|
||||||
color: green;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-dark-blue {
|
|
||||||
color: darkblue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-dark-gray {
|
|
||||||
color: gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-gray {
|
|
||||||
color: lightgray;u
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-brown {
|
|
||||||
color: brown;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-cyan {
|
|
||||||
color: cyan;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-dark-cyan {
|
|
||||||
color: darkcyan;
|
|
||||||
}
|
|
||||||
|
|
||||||
li.notification {
|
li.notification {
|
||||||
color: green;
|
color: green;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
color: #77dfd8;
|
color: #77dfd8;
|
||||||
}
|
}
|
||||||
.cof-chat_nick_self {
|
.cof-chat_nick_self {
|
||||||
color: #d9d9d9;
|
color: #fff1bd;
|
||||||
}
|
}
|
||||||
.cof-chat_nick_other {
|
.cof-chat_nick_other {
|
||||||
color: #77dfd8;
|
color: #77dfd8;
|
||||||
|
@ -143,6 +143,7 @@
|
||||||
.cob-chat_nick {
|
.cob-chat_nick {
|
||||||
}
|
}
|
||||||
.cob-chat_nick_self {
|
.cob-chat_nick_self {
|
||||||
|
background: #080808;
|
||||||
}
|
}
|
||||||
.cob-chat_nick_other {
|
.cob-chat_nick_other {
|
||||||
}
|
}
|
||||||
|
@ -268,13 +269,13 @@
|
||||||
.cwf-darkgray {
|
.cwf-darkgray {
|
||||||
color: #5d5d5d;
|
color: #5d5d5d;
|
||||||
}
|
}
|
||||||
.cwf-darkred {
|
.cwf-red {
|
||||||
color: #c75646;
|
color: #c75646;
|
||||||
}
|
}
|
||||||
.cwf-lightred {
|
.cwf-lightred {
|
||||||
color: #e09690;
|
color: #e09690;
|
||||||
}
|
}
|
||||||
.cwf-darkgreen {
|
.cwf-green {
|
||||||
color: #8eb33b;
|
color: #8eb33b;
|
||||||
}
|
}
|
||||||
.cwf-lightgreen {
|
.cwf-lightgreen {
|
||||||
|
@ -286,19 +287,19 @@
|
||||||
.cwf-yellow {
|
.cwf-yellow {
|
||||||
color: #ffe377;
|
color: #ffe377;
|
||||||
}
|
}
|
||||||
.cwf-darkblue {
|
.cwf-blue {
|
||||||
color: #72b3cc;
|
color: #72b3cc;
|
||||||
}
|
}
|
||||||
.cwf-lightblue {
|
.cwf-lightblue {
|
||||||
color: #9cd9f0;
|
color: #9cd9f0;
|
||||||
}
|
}
|
||||||
.cwf-darkmagenta {
|
.cwf-magenta {
|
||||||
color: #c8a0d1;
|
color: #c8a0d1;
|
||||||
}
|
}
|
||||||
.cwf-lightmagenta {
|
.cwf-lightmagenta {
|
||||||
color: #fbb1f9;
|
color: #fbb1f9;
|
||||||
}
|
}
|
||||||
.cwf-darkcyan {
|
.cwf-cyan {
|
||||||
color: #218693;
|
color: #218693;
|
||||||
}
|
}
|
||||||
.cwf-lightcyan {
|
.cwf-lightcyan {
|
||||||
|
@ -321,13 +322,13 @@
|
||||||
.cwb-darkgray {
|
.cwb-darkgray {
|
||||||
background-color: #5d5d5d;
|
background-color: #5d5d5d;
|
||||||
}
|
}
|
||||||
.cwb-darkred {
|
.cwb-red {
|
||||||
background-color: #c75646;
|
background-color: #c75646;
|
||||||
}
|
}
|
||||||
.cwb-lightred {
|
.cwb-lightred {
|
||||||
background-color: #e09690;
|
background-color: #e09690;
|
||||||
}
|
}
|
||||||
.cwb-darkgreen {
|
.cwb-green {
|
||||||
background-color: #8eb33b;
|
background-color: #8eb33b;
|
||||||
}
|
}
|
||||||
.cwb-lightgreen {
|
.cwb-lightgreen {
|
||||||
|
@ -339,19 +340,19 @@
|
||||||
.cwb-yellow {
|
.cwb-yellow {
|
||||||
background-color: #ffe377;
|
background-color: #ffe377;
|
||||||
}
|
}
|
||||||
.cwb-darkblue {
|
.cwb-blue {
|
||||||
background-color: #72b3cc;
|
background-color: #72b3cc;
|
||||||
}
|
}
|
||||||
.cwb-lightblue {
|
.cwb-lightblue {
|
||||||
background-color: #9cd9f0;
|
background-color: #9cd9f0;
|
||||||
}
|
}
|
||||||
.cwb-darkmagenta {
|
.cwb-magenta {
|
||||||
background-color: #c8a0d1;
|
background-color: #c8a0d1;
|
||||||
}
|
}
|
||||||
.cwb-lightmagenta {
|
.cwb-lightmagenta {
|
||||||
background-color: #fbb1f9;
|
background-color: #fbb1f9;
|
||||||
}
|
}
|
||||||
.cwb-darkcyan {
|
.cwb-cyan {
|
||||||
background-color: #218693;
|
background-color: #218693;
|
||||||
}
|
}
|
||||||
.cwb-lightcyan {
|
.cwb-lightcyan {
|
||||||
|
|
35
index.html
35
index.html
|
@ -15,6 +15,7 @@
|
||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>
|
||||||
<script type="text/javascript" src="js/localstorage.js"></script>
|
<script type="text/javascript" src="js/localstorage.js"></script>
|
||||||
<script type="text/javascript" src="js/weechat.js"></script>
|
<script type="text/javascript" src="js/weechat.js"></script>
|
||||||
|
<script type="text/javascript" src="js/irc-utils.js"></script>
|
||||||
<script type="text/javascript" src="js/websockets.js"></script>
|
<script type="text/javascript" src="js/websockets.js"></script>
|
||||||
<script type="text/javascript" src="js/models.js"></script>
|
<script type="text/javascript" src="js/models.js"></script>
|
||||||
<script type="text/javascript" src="js/plugins.js"></script>
|
<script type="text/javascript" src="js/plugins.js"></script>
|
||||||
|
@ -48,14 +49,14 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label" for="host">WeeChat relay hostname and port number</label>
|
<label class="control-label" for="host">WeeChat relay hostname and port number</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" class="form-control" id="host" ng-model="host" placeholder="Address">
|
<input type="text" class="form-control monospace" id="host" ng-model="host" placeholder="Address">
|
||||||
<input type="text" class="form-control" id="port" ng-model="port" placeholder="9001">
|
<input type="text" class="form-control monospace" id="port" ng-model="port" placeholder="9001">
|
||||||
</div>
|
</div>
|
||||||
<div class="alert alert-danger" ng-show="passwordError">
|
<div class="alert alert-danger" ng-show="passwordError">
|
||||||
Error wrong password
|
Error wrong password
|
||||||
</div>
|
</div>
|
||||||
<label class="control-label" for="password">WeeChat relay password</label>
|
<label class="control-label" for="password">WeeChat relay password</label>
|
||||||
<input type="password" class="form-control" id="password" ng-model="password" placeholder="Password">
|
<input type="password" class="form-control monospace" id="password" ng-model="password" placeholder="Password">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" class="checkbox" id="savepassword" ng-model="savepassword">
|
<input type="checkbox" class="checkbox" id="savepassword" ng-model="savepassword">
|
||||||
|
@ -71,7 +72,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label" for="port">Lines</label>
|
<label class="control-label" for="port">Lines</label>
|
||||||
<input type="text" class="form-control" id="lines" ng-model="lines" placeholder="40">
|
<input type="text" class="form-control monospace" id="lines" ng-model="lines" placeholder="40">
|
||||||
<p class="help-block">Enter number of lines to sync from WeeChat on connect</p>
|
<p class="help-block">Enter number of lines to sync from WeeChat on connect</p>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-lg btn-primary" ng-click="connect()">Connect <i class="glyphicon glyphicon-chevron-right"></i></button>
|
<button class="btn btn-lg btn-primary" ng-click="connect()">Connect <i class="glyphicon glyphicon-chevron-right"></i></button>
|
||||||
|
@ -150,6 +151,7 @@ $ openssl req -nodes -newkey rsa:2048 -keyout relay.pem -x509 -days 365 -out rel
|
||||||
<div class="brand">
|
<div class="brand">
|
||||||
<img alt="brand" src="img/favicon.png">
|
<img alt="brand" src="img/favicon.png">
|
||||||
</div>
|
</div>
|
||||||
|
<div id="connection-infos" class="monospace">{{ host }}:{{ port }}</div>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
{{ activeBuffer().title}}
|
{{ activeBuffer().title}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -190,6 +192,16 @@ $ openssl req -nodes -newkey rsa:2048 -keyout relay.pem -x509 -days 365 -out rel
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="">
|
||||||
|
<form class="form-inline" role="form">
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" ng-model="nonicklist">
|
||||||
|
Hide nicklist
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<a ng-click="disconnect()">
|
<a ng-click="disconnect()">
|
||||||
|
@ -201,7 +213,7 @@ $ openssl req -nodes -newkey rsa:2048 -keyout relay.pem -x509 -days 365 -out rel
|
||||||
<ul class="nav nav-pills nav-stacked">
|
<ul class="nav nav-pills nav-stacked">
|
||||||
<li class="bufferfilter">
|
<li class="bufferfilter">
|
||||||
<form role="form">
|
<form role="form">
|
||||||
<input class="form-control" type="text" id="bufferFilter" ng-model="search" ng-keydown="handleSearchBoxKey($event)" placeholder="Search">
|
<input class="form-control monospace" type="text" id="bufferFilter" ng-model="search" ng-keydown="handleSearchBoxKey($event)" placeholder="Search">
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
<li class="buffer" ng-class="{'active': content.active }" ng-repeat="(key, content) in buffers | toArray | filter:{fullName:search} | filter:hasUnread | orderBy:'content.number':true">
|
<li class="buffer" ng-class="{'active': content.active }" ng-repeat="(key, content) in buffers | toArray | filter:{fullName:search} | filter:hasUnread | orderBy:'content.number':true">
|
||||||
|
@ -213,7 +225,14 @@ $ openssl req -nodes -newkey rsa:2048 -keyout relay.pem -x509 -days 365 -out rel
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div id="bufferlines">
|
<div id="bufferlines" class="monospace" ng-class="{'withnicklist': showNicklist}">
|
||||||
|
<div id="nicklist" ng-show="showNicklist" class="vertical-line-left">
|
||||||
|
<ul class="nicklistgroup list-unstyled" ng-repeat="group in activeBuffer().nicklist">
|
||||||
|
<li class="" ng-repeat="nick in group.nicks|orderBy:'nick.name'">
|
||||||
|
<a ng-click="nickAction(nick)"><span ng-class="nick.prefixClasses">{{nick.prefix}}</span><span ng-class="nick.nameClasses">{{nick.name}}</span></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr class="bufferline" ng-repeat-start="bufferline in (bufferlines = activeBuffer().lines)">
|
<tr class="bufferline" ng-repeat-start="bufferline in (bufferlines = activeBuffer().lines)">
|
||||||
|
@ -249,10 +268,10 @@ $ openssl req -nodes -newkey rsa:2048 -keyout relay.pem -x509 -days 365 -out rel
|
||||||
<div class="push"></div>
|
<div class="push"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="footer" ng-show="connected">
|
<div id="footer" ng-show="connected">
|
||||||
<div class="navbar navbar-inverse navbar-fixed-bottom">
|
<div class="navbar navbar-inverse navbar-fixed-bottom" ng-class="{'withnicklist': showNicklist}">
|
||||||
<form class="form form-horizontal" ng-submit="sendMessage()">
|
<form class="form form-horizontal" ng-submit="sendMessage()">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="sendMessage" type="text" class="form-control" autocomplete="off" ng-model="command" autofocus>
|
<input id="sendMessage" type="text" class="form-control monospace" autocomplete="off" ng-model="command" autofocus>
|
||||||
<span class="input-group-btn">
|
<span class="input-group-btn">
|
||||||
<button class="btn btn-default btn-primary">Send</button>
|
<button class="btn btn-default btn-primary">Send</button>
|
||||||
</span>
|
</span>
|
||||||
|
|
208
js/irc-utils.js
Normal file
208
js/irc-utils.js
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
/**
|
||||||
|
* Portable utilities for IRC.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var IrcUtils = {
|
||||||
|
/**
|
||||||
|
* Get a new version of a nick list, sorted alphabetically by lowercase nick.
|
||||||
|
*
|
||||||
|
* @param nickList Original nick list
|
||||||
|
* @return Nick list sorted alphabetically by lowercase nick
|
||||||
|
*/
|
||||||
|
_ciSearchNickList: function(nickList) {
|
||||||
|
var newList = [];
|
||||||
|
|
||||||
|
nickList.forEach(function(nick) {
|
||||||
|
newList.push(nick);
|
||||||
|
});
|
||||||
|
newList.sort(function(a, b) {
|
||||||
|
return a.toLowerCase() < b.toLowerCase() ? -1 : 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
return newList;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Completes a single nick.
|
||||||
|
*
|
||||||
|
* @param candidate What to search for
|
||||||
|
* @param nickList Array of current nicks sorted for case insensitive searching
|
||||||
|
* @return Completed nick (null if not found)
|
||||||
|
*/
|
||||||
|
_completeSingleNick: function(candidate, nickList) {
|
||||||
|
var foundNick = null;
|
||||||
|
|
||||||
|
nickList.some(function(nick) {
|
||||||
|
if (nick.toLowerCase().search(candidate.toLowerCase()) == 0) {
|
||||||
|
// found!
|
||||||
|
foundNick = nick;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
return foundNick;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next nick when iterating nicks.
|
||||||
|
*
|
||||||
|
* @param iterCandidate First characters to look at
|
||||||
|
* @param currentNick Current selected nick
|
||||||
|
* @param nickList Array of current nicks sorted for case insensitive searching
|
||||||
|
* @return Next nick (may be the same)
|
||||||
|
*/
|
||||||
|
_nextNick: function(iterCandidate, currentNick, nickList) {
|
||||||
|
var firstInGroup = null;
|
||||||
|
var matchingNicks = [];
|
||||||
|
var at = null;
|
||||||
|
var lcIterCandidate = iterCandidate.toLowerCase();
|
||||||
|
var lcCurrentNick = currentNick.toLowerCase();
|
||||||
|
|
||||||
|
// collect matching nicks
|
||||||
|
for (var i = 0; i < nickList.length; ++i) {
|
||||||
|
var lcNick = nickList[i].toLowerCase();
|
||||||
|
if (lcNick.search(lcIterCandidate) == 0) {
|
||||||
|
matchingNicks.push(nickList[i]);
|
||||||
|
if (lcCurrentNick == lcNick) {
|
||||||
|
at = matchingNicks.length - 1;
|
||||||
|
}
|
||||||
|
} else if (matchingNicks.length > 0) {
|
||||||
|
// end of group, no need to check after this
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (at == null || matchingNicks.length == 0) {
|
||||||
|
return currentNick;
|
||||||
|
} else {
|
||||||
|
++at;
|
||||||
|
if (at == matchingNicks.length) {
|
||||||
|
// cycle
|
||||||
|
at = 0;
|
||||||
|
}
|
||||||
|
return matchingNicks[at];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nicks tab completion.
|
||||||
|
*
|
||||||
|
* @param text Plain text (no colors)
|
||||||
|
* @param caretPos Current caret position (0 means before the first character)
|
||||||
|
* @param iterCandidate Current iteration candidate (null if not iterating)
|
||||||
|
* @param nickList Array of current nicks
|
||||||
|
* @param suf Custom suffix (at least one character, escaped for regex)
|
||||||
|
* @return Object with following properties:
|
||||||
|
* text: new complete replacement text
|
||||||
|
* caretPos: new caret position within new text
|
||||||
|
* foundNick: completed nick (or null if not possible)
|
||||||
|
* iterCandidate: current iterating candidate
|
||||||
|
*/
|
||||||
|
completeNick: function(text, caretPos, iterCandidate, nickList, suf) {
|
||||||
|
var doIterate = (iterCandidate !== null);
|
||||||
|
if (suf === null) {
|
||||||
|
suf = ':';
|
||||||
|
}
|
||||||
|
|
||||||
|
// new nick list to search in
|
||||||
|
var searchNickList = IrcUtils._ciSearchNickList(nickList);
|
||||||
|
|
||||||
|
// text before and after caret
|
||||||
|
var beforeCaret = text.substring(0, caretPos);
|
||||||
|
var afterCaret = text.substring(caretPos);
|
||||||
|
|
||||||
|
// default: don't change anything
|
||||||
|
var ret = {
|
||||||
|
text: text,
|
||||||
|
caretPos: caretPos,
|
||||||
|
foundNick: null,
|
||||||
|
iterCandidate: null
|
||||||
|
};
|
||||||
|
|
||||||
|
// iterating nicks at the beginning?
|
||||||
|
var m = beforeCaret.match(new RegExp('^([a-zA-Z0-9_\\\\\\[\\]{}^`|-]+)' + suf + ' $'));
|
||||||
|
if (m) {
|
||||||
|
if (doIterate) {
|
||||||
|
// try iterating
|
||||||
|
var newNick = IrcUtils._nextNick(iterCandidate, m[1], searchNickList);
|
||||||
|
beforeCaret = newNick + suf + ' ';
|
||||||
|
return {
|
||||||
|
text: beforeCaret + afterCaret,
|
||||||
|
caretPos: beforeCaret.length,
|
||||||
|
foundNick: newNick,
|
||||||
|
iterCandidate: iterCandidate
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// if not iterating, don't do anything
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nick completion in the beginning?
|
||||||
|
m = beforeCaret.match(/^([a-zA-Z0-9_\\\[\]{}^`|-]+)$/);
|
||||||
|
if (m) {
|
||||||
|
// try completing
|
||||||
|
var newNick = IrcUtils._completeSingleNick(m[1], searchNickList);
|
||||||
|
if (newNick === null) {
|
||||||
|
// no match
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
beforeCaret = newNick + suf + ' ';
|
||||||
|
if (afterCaret[0] == ' ') {
|
||||||
|
// swallow first space after caret if any
|
||||||
|
afterCaret = afterCaret.substring(1);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
text: beforeCaret + afterCaret,
|
||||||
|
caretPos: beforeCaret.length,
|
||||||
|
foundNick: newNick,
|
||||||
|
iterCandidate: m[1]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterating nicks in the middle?
|
||||||
|
m = beforeCaret.match(/^(.* )([a-zA-Z0-9_\\\[\]{}^`|-]+) $/);
|
||||||
|
if (m) {
|
||||||
|
if (doIterate) {
|
||||||
|
// try iterating
|
||||||
|
var newNick = IrcUtils._nextNick(iterCandidate, m[2], searchNickList);
|
||||||
|
beforeCaret = m[1] + newNick + ' ';
|
||||||
|
return {
|
||||||
|
text: beforeCaret + afterCaret,
|
||||||
|
caretPos: beforeCaret.length,
|
||||||
|
foundNick: newNick,
|
||||||
|
iterCandidate: iterCandidate
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// if not iterating, don't do anything
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nick completion elsewhere in the middle?
|
||||||
|
m = beforeCaret.match(/^(.* )([a-zA-Z0-9_\\\[\]{}^`|-]+)$/);
|
||||||
|
if (m) {
|
||||||
|
// try completing
|
||||||
|
var newNick = IrcUtils._completeSingleNick(m[2], searchNickList);
|
||||||
|
if (newNick === null) {
|
||||||
|
// no match
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
beforeCaret = m[1] + newNick + ' ';
|
||||||
|
if (afterCaret[0] == ' ') {
|
||||||
|
// swallow first space after caret if any
|
||||||
|
afterCaret = afterCaret.substring(1);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
text: beforeCaret + afterCaret,
|
||||||
|
caretPos: beforeCaret.length,
|
||||||
|
foundNick: newNick,
|
||||||
|
iterCandidate: m[2]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// completion not possible
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
134
js/models.js
134
js/models.js
|
@ -18,6 +18,8 @@ models.service('models', ['$rootScope', '$filter', function($rootScope, $filter)
|
||||||
var local_variables = message['local_vars'];
|
var local_variables = message['local_vars'];
|
||||||
var notify = 3 // Default 3 == message
|
var notify = 3 // Default 3 == message
|
||||||
var lines = []
|
var lines = []
|
||||||
|
var nicklist = {}
|
||||||
|
var flatnicklist = []
|
||||||
var active = false
|
var active = false
|
||||||
var notification = 0
|
var notification = 0
|
||||||
var unread = 0
|
var unread = 0
|
||||||
|
@ -38,6 +40,64 @@ models.service('models', ['$rootScope', '$filter', function($rootScope, $filter)
|
||||||
lines.push(line);
|
lines.push(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adds a nick to nicklist
|
||||||
|
*/
|
||||||
|
var addNick = function(group, nick) {
|
||||||
|
nicklist[group].nicks.push(nick);
|
||||||
|
flatnicklist = getFlatNicklist();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Deletes a nick from nicklist
|
||||||
|
*/
|
||||||
|
var delNick = function(group, nick) {
|
||||||
|
var group = nicklist[group];
|
||||||
|
group.nicks = _.filter(group.nicks, function(n) { return n.name != nick.name});
|
||||||
|
flatnicklist = getFlatNicklist();
|
||||||
|
/*
|
||||||
|
for(i in group.nicks) {
|
||||||
|
if(group.nicks[i].name == nick.name) {
|
||||||
|
delete group.nicks[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Updates a nick in nicklist
|
||||||
|
*/
|
||||||
|
var updateNick = function(group, nick) {
|
||||||
|
var group = nicklist[group];
|
||||||
|
for(i in group.nicks) {
|
||||||
|
if(group.nicks[i].name == nick.name) {
|
||||||
|
group.nicks[i] = nick;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flatnicklist = getFlatNicklist();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Maintain a cached version of a flat sorted nicklist
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
var getFlatNicklist = function() {
|
||||||
|
var newlist = [];
|
||||||
|
_.each(nicklist, function(nickGroup) {
|
||||||
|
_.each(nickGroup.nicks, function(nickObj) {
|
||||||
|
newlist.push(nickObj.name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
newlist.sort(function(a, b) {
|
||||||
|
return a.toLowerCase() < b.toLowerCase() ? -1 : 1;
|
||||||
|
});
|
||||||
|
return newlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
var flatNicklist = function() {
|
||||||
|
return flatnicklist;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: pointer,
|
id: pointer,
|
||||||
fullName: fullName,
|
fullName: fullName,
|
||||||
|
@ -51,6 +111,11 @@ models.service('models', ['$rootScope', '$filter', function($rootScope, $filter)
|
||||||
notification: notification,
|
notification: notification,
|
||||||
localvars: local_variables,
|
localvars: local_variables,
|
||||||
notify: notify,
|
notify: notify,
|
||||||
|
nicklist: nicklist,
|
||||||
|
addNick: addNick,
|
||||||
|
delNick: delNick,
|
||||||
|
updateNick: updateNick,
|
||||||
|
flatNicklist: flatNicklist
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -130,6 +195,75 @@ models.service('models', ['$rootScope', '$filter', function($rootScope, $filter)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function nickGetColorClasses(nickMsg, propName) {
|
||||||
|
if (propName in nickMsg && nickMsg[propName] && nickMsg[propName].length > 0) {
|
||||||
|
var color = nickMsg[propName];
|
||||||
|
if (color.match(/^weechat/)) {
|
||||||
|
// color option
|
||||||
|
var colorName = color.match(/[a-zA-Z0-9_]+$/)[0];
|
||||||
|
return [
|
||||||
|
'cof-' + colorName,
|
||||||
|
'cob-' + colorName,
|
||||||
|
'coa-' + colorName
|
||||||
|
];
|
||||||
|
} else if (color.match(/^[a-zA-Z]+$/)) {
|
||||||
|
// WeeChat color name
|
||||||
|
return [
|
||||||
|
'cwf-' + color
|
||||||
|
];
|
||||||
|
} else if (color.match(/^[0-9]+$/)) {
|
||||||
|
// extended color
|
||||||
|
return [
|
||||||
|
'cef-' + color
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'cwf-default'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function nickGetClasses(nickMsg) {
|
||||||
|
return {
|
||||||
|
'name': nickGetColorClasses(nickMsg, 'color'),
|
||||||
|
'prefix': nickGetColorClasses(nickMsg, 'prefix_color')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nick class
|
||||||
|
*/
|
||||||
|
this.Nick = function(message) {
|
||||||
|
var prefix = message['prefix'];
|
||||||
|
var visible = message['visible'];
|
||||||
|
var name = message['name'];
|
||||||
|
var colorClasses = nickGetClasses(message);
|
||||||
|
|
||||||
|
return {
|
||||||
|
prefix: prefix,
|
||||||
|
visible: visible,
|
||||||
|
name: name,
|
||||||
|
prefixClasses: colorClasses.prefix,
|
||||||
|
nameClasses: colorClasses.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Nicklist Group class
|
||||||
|
*/
|
||||||
|
this.NickGroup = function(message) {
|
||||||
|
var name = message['name'];
|
||||||
|
var visible = message['visible'];
|
||||||
|
var nicks = [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: name,
|
||||||
|
visible: visible,
|
||||||
|
nicks: nicks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var BufferList = []
|
var BufferList = []
|
||||||
activeBuffer = null;
|
activeBuffer = null;
|
||||||
|
|
169
js/websockets.js
169
js/websockets.js
|
@ -114,6 +114,54 @@ weechat.factory('handlers', ['$rootScope', 'models', 'plugins', function($rootSc
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle nicklist event
|
||||||
|
*/
|
||||||
|
var handleNicklist = function(message) {
|
||||||
|
var nicklist = message['objects'][0]['content'];
|
||||||
|
var group = 'root';
|
||||||
|
nicklist.forEach(function(n) {
|
||||||
|
var buffer = models.getBuffer(n.pointers[0]);
|
||||||
|
if(n.group == 1) {
|
||||||
|
var g = new models.NickGroup(n);
|
||||||
|
group = g.name;
|
||||||
|
buffer.nicklist[group] = g;
|
||||||
|
}else{
|
||||||
|
var nick = new models.Nick(n);
|
||||||
|
buffer.addNick(group, nick);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Handle nicklist diff event
|
||||||
|
*/
|
||||||
|
var handleNicklistDiff = function(message) {
|
||||||
|
var nicklist = message['objects'][0]['content'];
|
||||||
|
var group;
|
||||||
|
nicklist.forEach(function(n) {
|
||||||
|
var buffer = models.getBuffer(n.pointers[0]);
|
||||||
|
var d = n['_diff'];
|
||||||
|
if(n.group == 1) {
|
||||||
|
group = n.name;
|
||||||
|
if(group==undefined) {
|
||||||
|
var g = new models.NickGroup(n);
|
||||||
|
buffer.nicklist[group] = g;
|
||||||
|
group = g.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var nick = new models.Nick(n);
|
||||||
|
if(d == 43) { // +
|
||||||
|
buffer.addNick(group, nick);
|
||||||
|
}else if (d == 45) { // -
|
||||||
|
buffer.delNick(group, nick);
|
||||||
|
}else if (d == 42) { // *
|
||||||
|
buffer.updateNick(group, nick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var handleEvent = function(event) {
|
var handleEvent = function(event) {
|
||||||
|
|
||||||
if (_.has(eventHandlers, event['id'])) {
|
if (_.has(eventHandlers, event['id'])) {
|
||||||
|
@ -127,13 +175,16 @@ weechat.factory('handlers', ['$rootScope', 'models', 'plugins', function($rootSc
|
||||||
_buffer_line_added: handleBufferLineAdded,
|
_buffer_line_added: handleBufferLineAdded,
|
||||||
_buffer_opened: handleBufferOpened,
|
_buffer_opened: handleBufferOpened,
|
||||||
_buffer_title_changed: handleBufferTitleChanged,
|
_buffer_title_changed: handleBufferTitleChanged,
|
||||||
_buffer_renamed: handleBufferRenamed
|
_buffer_renamed: handleBufferRenamed,
|
||||||
|
_nicklist: handleNicklist,
|
||||||
|
_nicklist_diff: handleNicklistDiff
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handleEvent: handleEvent,
|
handleEvent: handleEvent,
|
||||||
handleLineInfo: handleLineInfo,
|
handleLineInfo: handleLineInfo,
|
||||||
handleHotlistInfo: handleHotlistInfo
|
handleHotlistInfo: handleHotlistInfo,
|
||||||
|
handleNicklist: handleNicklist
|
||||||
}
|
}
|
||||||
|
|
||||||
}]);
|
}]);
|
||||||
|
@ -237,6 +288,12 @@ weechat.factory('connection', ['$q', '$rootScope', '$log', '$store', 'handlers',
|
||||||
})).then(function(hdata) {
|
})).then(function(hdata) {
|
||||||
handlers.handleHotlistInfo(hdata)
|
handlers.handleHotlistInfo(hdata)
|
||||||
});
|
});
|
||||||
|
}).then(function() {
|
||||||
|
$log.info("Requesting nicklist");
|
||||||
|
doSendWithCallback(weeChat.Protocol.formatNicklist({
|
||||||
|
})).then(function(nicklistdata) {
|
||||||
|
handlers.handleNicklist(nicklistdata)
|
||||||
|
});
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
doSend(weeChat.Protocol.formatSync({}));
|
doSend(weeChat.Protocol.formatSync({}));
|
||||||
$log.info("Synced");
|
$log.info("Synced");
|
||||||
|
@ -313,8 +370,6 @@ weechat.factory('connection', ['$q', '$rootScope', '$log', '$store', 'handlers',
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout', '$log', 'models', 'connection', function ($rootScope, $scope, $store, $timeout, $log, models, connection, testService) {
|
weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout', '$log', 'models', 'connection', function ($rootScope, $scope, $store, $timeout, $log, models, connection, testService) {
|
||||||
|
|
||||||
|
|
||||||
if(window.Notification) {
|
if(window.Notification) {
|
||||||
// Request notification permission
|
// Request notification permission
|
||||||
Notification.requestPermission(function (status) {
|
Notification.requestPermission(function (status) {
|
||||||
|
@ -361,6 +416,9 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout',
|
||||||
|
|
||||||
// Clear search term on buffer change
|
// Clear search term on buffer change
|
||||||
$scope.search = '';
|
$scope.search = '';
|
||||||
|
|
||||||
|
// Check if we should show nicklist or not
|
||||||
|
$scope.showNicklist = $scope.updateShowNicklist();
|
||||||
});
|
});
|
||||||
$rootScope.$on('notificationChanged', function() {
|
$rootScope.$on('notificationChanged', function() {
|
||||||
var notifications = _.reduce(models.model.buffers, function(memo, num) { return (memo||0) + num.notification;});
|
var notifications = _.reduce(models.model.buffers, function(memo, num) { return (memo||0) + num.notification;});
|
||||||
|
@ -389,6 +447,8 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout',
|
||||||
|
|
||||||
$rootScope.buffer = []
|
$rootScope.buffer = []
|
||||||
|
|
||||||
|
$rootScope.iterCandidate = null;
|
||||||
|
|
||||||
$store.bind($scope, "host", "localhost");
|
$store.bind($scope, "host", "localhost");
|
||||||
$store.bind($scope, "port", "9001");
|
$store.bind($scope, "port", "9001");
|
||||||
$store.bind($scope, "proto", "weechat");
|
$store.bind($scope, "proto", "weechat");
|
||||||
|
@ -405,7 +465,8 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout',
|
||||||
$store.bind($scope, "notimestamp", false);
|
$store.bind($scope, "notimestamp", false);
|
||||||
// Save setting for syncing hotlist
|
// Save setting for syncing hotlist
|
||||||
$store.bind($scope, "hotlistsync", true);
|
$store.bind($scope, "hotlistsync", true);
|
||||||
|
// Save setting for displaying nicklist
|
||||||
|
$store.bind($scope, "nonicklist", false);
|
||||||
|
|
||||||
$scope.setActiveBuffer = function(key) {
|
$scope.setActiveBuffer = function(key) {
|
||||||
models.setActiveBuffer(key);
|
models.setActiveBuffer(key);
|
||||||
|
@ -500,6 +561,30 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout',
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Watch model and update show setting when it changes
|
||||||
|
$scope.$watch('nonicklist', function() {
|
||||||
|
$scope.showNicklist = $scope.updateShowNicklist();
|
||||||
|
});
|
||||||
|
$scope.showNicklist = false;
|
||||||
|
// Utility function that template can use to check if nicklist should
|
||||||
|
// be displayed for current buffer or not
|
||||||
|
// is called on buffer switch
|
||||||
|
$scope.updateShowNicklist = function() {
|
||||||
|
var ab = models.getActiveBuffer();
|
||||||
|
if(!ab) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Check if option no nicklist is set
|
||||||
|
if($scope.nonicklist) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Use flat nicklist to check if empty
|
||||||
|
if(ab.flatNicklist().length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
$rootScope.switchToActivityBuffer = function() {
|
$rootScope.switchToActivityBuffer = function() {
|
||||||
// Find next buffer with activity and switch to it
|
// Find next buffer with activity and switch to it
|
||||||
for(i in $scope.buffers) {
|
for(i in $scope.buffers) {
|
||||||
|
@ -514,11 +599,49 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$rootScope.completeNick = function() {
|
||||||
|
// input DOM node
|
||||||
|
var inputNode = document.getElementById('sendMessage');
|
||||||
|
|
||||||
|
// get current input
|
||||||
|
var inputText = inputNode.value;
|
||||||
|
|
||||||
|
// get current caret position
|
||||||
|
var caretPos = inputNode.selectionStart;
|
||||||
|
|
||||||
|
// create flat array of nicks
|
||||||
|
var activeBuffer = models.getActiveBuffer();
|
||||||
|
|
||||||
|
// complete nick
|
||||||
|
var nickComp = IrcUtils.completeNick(inputText, caretPos,
|
||||||
|
$rootScope.iterCandidate, activeBuffer.flatNicklist(), ':');
|
||||||
|
|
||||||
|
// remember iteration candidate
|
||||||
|
$rootScope.iterCandidate = nickComp.iterCandidate;
|
||||||
|
|
||||||
|
// update current input
|
||||||
|
inputNode.value = nickComp.text;
|
||||||
|
|
||||||
|
// update current caret position
|
||||||
|
inputNode.focus();
|
||||||
|
inputNode.setSelectionRange(nickComp.caretPos, nickComp.caretPos);
|
||||||
|
}
|
||||||
|
|
||||||
$scope.handleKeyPress = function($event) {
|
$scope.handleKeyPress = function($event) {
|
||||||
|
// don't do anything if not connected
|
||||||
|
if (!$rootScope.connected) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Support different browser quirks
|
// Support different browser quirks
|
||||||
var code = $event.keyCode ? $event.keyCode : $event.charCode;
|
var code = $event.keyCode ? $event.keyCode : $event.charCode;
|
||||||
|
|
||||||
if ($event.altKey && (code > 47 && code < 58)) {
|
// any other key than Tab resets nick completion iteration
|
||||||
|
var tmpIterCandidate = $rootScope.iterCandidate;
|
||||||
|
$rootScope.iterCandidate = null;
|
||||||
|
|
||||||
|
// Left Alt+[0-9] -> jump to buffer
|
||||||
|
if ($event.altKey && !$event.ctrlKey && (code > 47 && code < 58)) {
|
||||||
if (code == 48) {
|
if (code == 48) {
|
||||||
code = 58;
|
code = 58;
|
||||||
}
|
}
|
||||||
|
@ -531,16 +654,40 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//log('keypress', $event.charCode, $event.altKey);
|
// Tab -> nick completion
|
||||||
|
if (code == 9 && !$event.altKey && !$event.ctrlKey) {
|
||||||
|
$event.preventDefault();
|
||||||
|
$rootScope.iterCandidate = tmpIterCandidate;
|
||||||
|
$rootScope.completeNick();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle alt-a
|
// Alt+A -> switch to buffer with activity
|
||||||
if($event.altKey && (code == 97 || code == 65)) {
|
if ($event.altKey && (code == 97 || code == 65)) {
|
||||||
$event.preventDefault();
|
$event.preventDefault();
|
||||||
$rootScope.switchToActivityBuffer();
|
$rootScope.switchToActivityBuffer();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Handle ctrl-g
|
|
||||||
if($event.ctrlKey && (code == 103 || code == 71)) {
|
// Alt+L -> focus on input bar
|
||||||
|
if ($event.altKey && (code == 76 || code == 108)) {
|
||||||
|
$event.preventDefault();
|
||||||
|
var inputNode = document.getElementById('sendMessage');
|
||||||
|
inputNode.focus();
|
||||||
|
inputNode.setSelectionRange(inputNode.value.length, inputNode.value.length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape -> disconnect
|
||||||
|
if (code == 27) {
|
||||||
|
$event.preventDefault();
|
||||||
|
connection.disconnect();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ctrl+G -> focus on buffer filter input
|
||||||
|
if ($event.ctrlKey && (code == 103 || code == 71)) {
|
||||||
|
$event.preventDefault();
|
||||||
document.getElementById('bufferFilter').focus();
|
document.getElementById('bufferFilter').focus();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,17 +44,17 @@
|
||||||
'default',
|
'default',
|
||||||
'black',
|
'black',
|
||||||
'darkgray',
|
'darkgray',
|
||||||
'darkred',
|
'red',
|
||||||
'lightred',
|
'lightred',
|
||||||
'darkgreen',
|
'green',
|
||||||
'lightgreen',
|
'lightgreen',
|
||||||
'brown',
|
'brown',
|
||||||
'yellow',
|
'yellow',
|
||||||
'darkblue',
|
'blue',
|
||||||
'lightblue',
|
'lightblue',
|
||||||
'darkmagenta',
|
'magenta',
|
||||||
'lightmagenta',
|
'lightmagenta',
|
||||||
'darkcyan',
|
'cyan',
|
||||||
'lightcyan',
|
'lightcyan',
|
||||||
'gray',
|
'gray',
|
||||||
'white'
|
'white'
|
||||||
|
|
Loading…
Reference in a new issue