From a9dcc68af27ad86bc25bfa050698db6f1dbe1f4e Mon Sep 17 00:00:00 2001 From: Tor Hveem <tor@hveem.no> Date: Tue, 15 Oct 2013 14:31:41 +0200 Subject: [PATCH 1/9] Instead of using buffer.notification as a boolean, save number of notifications into the variable. And when displaying unread messages display unread notifications if any and if not then display number of unreads. --- index.html | 3 ++- js/models.js | 4 ++-- js/websockets.js | 8 ++++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/index.html b/index.html index c9a0301..578651b 100644 --- a/index.html +++ b/index.html @@ -100,7 +100,8 @@ $ openssl req -nodes -newkey rsa:2048 -keyout relay.pem -x509 -days 365 -out rel </li> <li class="label" ng-class="{'active': content.active }" ng-repeat="(key, content) in buffers | toArray | filter:search | filter:hasUnread | orderBy:'content.number':true"> <a href="#" ng-click="setActiveBuffer(content.id)" title="{{ content.fullName }}"> - <span class="badge pull-right" ng-class="{'danger': content.notification }" ng-bind="content.unread"></span> + <span class="badge pull-right" ng-hide="content.notification" ng-bind="content.unread"></span> + <span class="badge pull-right danger" ng-show="content.notification" ng-bind="content.notification"></span> {{ content.shortName }}<span ng-hide="content.shortName">{{ content.fullName }}</span> </a> </li> diff --git a/js/models.js b/js/models.js index a505b81..bbac9e6 100644 --- a/js/models.js +++ b/js/models.js @@ -17,7 +17,7 @@ models.service('models', ['$rootScope', 'colors', function($rootScope, colors) { var pointer = message['pointers'][0] var lines = [] var active = false; - var notification = false; + var notification = ''; var unread = ''; var lastSeen = -2; @@ -161,9 +161,9 @@ models.service('models', ['$rootScope', 'colors', function($rootScope, colors) { } }); - activeBuffer.notification = false; activeBuffer.active = true; activeBuffer.unread = ''; + activeBuffer.notification = ''; $rootScope.$emit('activeBufferChanged'); } diff --git a/js/websockets.js b/js/websockets.js index 70965de..ec10555 100644 --- a/js/websockets.js +++ b/js/websockets.js @@ -209,7 +209,11 @@ weechat.factory('handlers', ['$rootScope', 'colors', 'models', 'plugins', functi if(message.highlight || _.contains(message.tags, 'notify_private') ) { $rootScope.createHighlight(buffer, message); - buffer.notification = true; + if (buffer.notification == '' || buffer.notification == undefined) { + buffer.notification = 1; + }else { + buffer.notification++; + } } } } @@ -519,7 +523,7 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout', // Find next buffer with activity and switch to it for(i in $scope.buffers) { var buffer = $scope.buffers[i]; - if(buffer.notification) { + if((parseInt(buffer.notification) || 0) > 0) { $scope.setActiveBuffer(buffer.id); break; }else if((parseInt(buffer.unread) || 0) > 0) { From 2617dadb65ee1e3a5c36f9977e4f52dc85524c22 Mon Sep 17 00:00:00 2001 From: Tor Hveem <tor@hveem.no> Date: Tue, 15 Oct 2013 14:59:06 +0200 Subject: [PATCH 2/9] Simplify/clean up the unread and notification code --- index.html | 2 +- js/models.js | 14 +++++++++----- js/websockets.js | 16 ++++------------ 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/index.html b/index.html index 578651b..bdb9efe 100644 --- a/index.html +++ b/index.html @@ -100,7 +100,7 @@ $ openssl req -nodes -newkey rsa:2048 -keyout relay.pem -x509 -days 365 -out rel </li> <li class="label" ng-class="{'active': content.active }" ng-repeat="(key, content) in buffers | toArray | filter:search | filter:hasUnread | orderBy:'content.number':true"> <a href="#" ng-click="setActiveBuffer(content.id)" title="{{ content.fullName }}"> - <span class="badge pull-right" ng-hide="content.notification" ng-bind="content.unread"></span> + <span class="badge pull-right" ng-hide="content.notification" ng-if="content.unread" ng-bind="content.unread"></span> <span class="badge pull-right danger" ng-show="content.notification" ng-bind="content.notification"></span> {{ content.shortName }}<span ng-hide="content.shortName">{{ content.fullName }}</span> </a> diff --git a/js/models.js b/js/models.js index bbac9e6..42e46fa 100644 --- a/js/models.js +++ b/js/models.js @@ -16,10 +16,10 @@ models.service('models', ['$rootScope', 'colors', function($rootScope, colors) { var number = message['number'] var pointer = message['pointers'][0] var lines = [] - var active = false; - var notification = ''; - var unread = ''; - var lastSeen = -2; + var active = false + var notification = 0 + var unread = 0 + var lastSeen = -2 /* * Adds a line to this buffer @@ -30,7 +30,7 @@ models.service('models', ['$rootScope', 'colors', function($rootScope, colors) { var addLine = function(line) { lines.push(line); } - + return { id: pointer, fullName: fullName, @@ -40,6 +40,8 @@ models.service('models', ['$rootScope', 'colors', function($rootScope, colors) { lines: lines, addLine: addLine, lastSeen: lastSeen, + unread: unread, + notification: notification, } } @@ -100,6 +102,8 @@ models.service('models', ['$rootScope', 'colors', function($rootScope, colors) { var BufferList = [] activeBuffer = null; + unreads = 0; + notifications = 0; this.model = { 'buffers': {} } diff --git a/js/websockets.js b/js/websockets.js index ec10555..3bb68cd 100644 --- a/js/websockets.js +++ b/js/websockets.js @@ -200,20 +200,12 @@ weechat.factory('handlers', ['$rootScope', 'colors', 'models', 'plugins', functi if (!initial) { if (!buffer.active && _.contains(message.tags, 'notify_message') && !_.contains(message.tags, 'notify_none')) { - if (buffer.unread == '' || buffer.unread == undefined) { - buffer.unread = 1; - }else { - buffer.unread++; - } + buffer.unread++; } if(message.highlight || _.contains(message.tags, 'notify_private') ) { + buffer.notification++; $rootScope.createHighlight(buffer, message); - if (buffer.notification == '' || buffer.notification == undefined) { - buffer.notification = 1; - }else { - buffer.notification++; - } } } } @@ -523,10 +515,10 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout', // Find next buffer with activity and switch to it for(i in $scope.buffers) { var buffer = $scope.buffers[i]; - if((parseInt(buffer.notification) || 0) > 0) { + if(buffer.notification > 0) { $scope.setActiveBuffer(buffer.id); break; - }else if((parseInt(buffer.unread) || 0) > 0) { + }else if(buffer.unread > 0) { $scope.setActiveBuffer(buffer.id); break; } From a9d469867a4e8c20479b614c7cf4f51a3cf5c637 Mon Sep 17 00:00:00 2001 From: Tor Hveem <tor@hveem.no> Date: Tue, 15 Oct 2013 15:21:13 +0200 Subject: [PATCH 3/9] Add Favio.js support. Fixes #28 --- index.html | 1 + js/favico-0.3.0.min.js | 7 +++++++ js/models.js | 5 +++-- js/websockets.js | 23 +++++++++++++++++++++-- 4 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 js/favico-0.3.0.min.js diff --git a/index.html b/index.html index bdb9efe..8b46381 100644 --- a/index.html +++ b/index.html @@ -16,6 +16,7 @@ <script type="text/javascript" src="js/websockets.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/favico-0.3.0.min.js"></script> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script> <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script> </head> diff --git a/js/favico-0.3.0.min.js b/js/favico-0.3.0.min.js new file mode 100644 index 0000000..0ebcc24 --- /dev/null +++ b/js/favico-0.3.0.min.js @@ -0,0 +1,7 @@ +/** + * @license MIT + * @fileOverview Favico animations + * @author Miroslav Magda, http://blog.ejci.net + * @version 0.3.0 + */ +!function(){var t=function(t){"use strict";function e(t){if(t.paused||t.ended||g)return!1;try{s.clearRect(0,0,h,a),s.drawImage(t,0,0,h,a)}catch(o){}setTimeout(e,L.duration,t),E.setIcon(c)}function o(t){var e=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;t=t.replace(e,function(t,e,o,n){return e+e+o+o+n+n});var o=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return o?{r:parseInt(o[1],16),g:parseInt(o[2],16),b:parseInt(o[3],16)}:!1}function n(t,e){var o,n={};for(o in t)n[o]=t[o];for(o in e)n[o]=e[o];return n}t=t?t:{};var r,i,a,h,c,s,l,f,u,d,y,g,w,x={bgColor:"#d00",textColor:"#fff",fontFamily:"sans-serif",fontStyle:"bold",type:"circle",position:"down",animation:"slide",elementId:!1},m=[];y=function(){},f=g=!1;var p=function(){if(r=n(x,t),r.bgColor=o(r.bgColor),r.textColor=o(r.textColor),r.position=r.position.toLowerCase(),r.animation=L.types[""+r.animation]?r.animation:x.animation,"up"===r.position)for(var e=0;e<L.types[""+r.animation].length;e++){var f=L.types[""+r.animation][e];f.y=f.y<.6?f.y-.4:f.y-2*f.y+(1-f.w),L.types[""+r.animation][e]=f}r.type=b[""+r.type]?r.type:x.type;try{i=E.getIcon(),c=document.createElement("canvas"),l=document.createElement("img"),l.setAttribute("src",i.getAttribute("href")),l.onload=function(){a=l.height>0?l.height:32,h=l.width>0?l.width:32,c.height=a,c.width=h,s=c.getContext("2d"),v.ready()},w={},w.ff=/firefox/i.test(navigator.userAgent.toLowerCase()),w.chrome=/chrome/i.test(navigator.userAgent.toLowerCase()),w.opera=/opera/i.test(navigator.userAgent.toLowerCase()),w.ie=/msie/i.test(navigator.userAgent.toLowerCase())||/trident/i.test(navigator.userAgent.toLowerCase()),w.supported=w.chrome||w.ff||w.opera}catch(u){throw"Error initializing favico..."}},v={};v.ready=function(){f=!0,v.reset(),y()},v.reset=function(){m=[],u=!1,s.clearRect(0,0,h,a),s.drawImage(l,0,0,h,a),E.setIcon(c)},v.start=function(){if(f&&!d){var t=function(){u=m[0],d=!1,m.length>0&&(m.shift(),v.start())};m.length>0&&(d=!0,u?L.run(u.options,function(){L.run(m[0].options,function(){t()},!1)},!0):L.run(m[0].options,function(){t()},!1))}};var b={},C=function(t){return t.n=Math.abs(t.n),t.x=h*t.x,t.y=a*t.y,t.w=h*t.w,t.h=a*t.h,t};b.circle=function(t){t=C(t);var e=t.n>9&&t.n<100;e&&(t.x=t.x-.4*t.w,t.w=1.4*t.w),s.clearRect(0,0,h,a),s.drawImage(l,0,0,h,a),s.beginPath(),s.font=r.fontStyle+" "+Math.floor(t.h)+"px "+r.fontFamily,s.textAlign="center",e?(s.moveTo(t.x+t.w/2,t.y),s.lineTo(t.x+t.w-t.h/2,t.y),s.quadraticCurveTo(t.x+t.w,t.y,t.x+t.w,t.y+t.h/2),s.lineTo(t.x+t.w,t.y+t.h-t.h/2),s.quadraticCurveTo(t.x+t.w,t.y+t.h,t.x+t.w-t.h/2,t.y+t.h),s.lineTo(t.x+t.h/2,t.y+t.h),s.quadraticCurveTo(t.x,t.y+t.h,t.x,t.y+t.h-t.h/2),s.lineTo(t.x,t.y+t.h/2),s.quadraticCurveTo(t.x,t.y,t.x+t.h/2,t.y)):s.arc(t.x+t.w/2,t.y+t.h/2,t.h/2,0,2*Math.PI),s.fillStyle="rgba("+r.bgColor.r+","+r.bgColor.g+","+r.bgColor.b+","+t.o+")",s.fill(),s.closePath(),s.beginPath(),s.stroke(),s.fillStyle="rgba("+r.textColor.r+","+r.textColor.g+","+r.textColor.b+","+t.o+")",t.n>99?s.fillText("∞",Math.floor(t.x+t.w/2),Math.floor(t.y+t.h-.15*t.h)):s.fillText(t.n,Math.floor(t.x+t.w/2),Math.floor(t.y+t.h-.15*t.h)),s.closePath()},b.rectangle=function(t){t=C(t);var e=t.n>9&&t.n<100;e&&(t.x=Math.floor(t.x-.4*t.w),t.w=Math.floor(1.4*t.w)),s.clearRect(0,0,h,a),s.drawImage(l,0,0,h,a),s.beginPath(),s.font="bold "+Math.floor(t.h)+"px sans-serif",s.textAlign="center",s.fillStyle="rgba("+r.bgColor.r+","+r.bgColor.g+","+r.bgColor.b+","+t.o+")",s.fillRect(t.x,t.y,t.w,t.h),s.fillStyle="rgba("+r.textColor.r+","+r.textColor.g+","+r.textColor.b+","+t.o+")",t.n>99?s.fillText("∞",Math.floor(t.x+t.w/2),Math.floor(t.y+t.h-.15*t.h)):s.fillText(t.n,Math.floor(t.x+t.w/2),Math.floor(t.y+t.h-.15*t.h)),s.closePath()};var I=function(t,e){y=function(){try{if(t>0){if(L.types[""+e]&&(r.animation=e),m.push({type:"badge",options:{n:t}}),m.length>100)throw"Too many badges requests in queue...";v.start()}else v.reset()}catch(o){throw"Error setting badge..."}},f&&y()},A=function(t){y=function(){try{var e=t.width,o=t.height,n=document.createElement("img"),r=o/a>e/h?e/h:o/a;n.setAttribute("src",t.getAttribute("src")),n.height=o/r,n.width=e/r,s.clearRect(0,0,h,a),s.drawImage(n,0,0,h,a),E.setIcon(c)}catch(i){throw"Error setting image..."}},f&&y()},M=function(t){y=function(){try{if("stop"===t)return g=!0,v.reset(),g=!1,void 0;t.addEventListener("play",function(){e(this)},!1)}catch(o){throw"Error setting video..."}},f&&y()},T=function(t){if(window.URL&&window.URL.createObjectURL||(window.URL=window.URL||{},window.URL.createObjectURL=function(t){return t}),w.supported){var o=!1;navigator.getUserMedia=navigator.getUserMedia||navigator.oGetUserMedia||navigator.msGetUserMedia||navigator.mozGetUserMedia||navigator.webkitGetUserMedia,y=function(){try{if("stop"===t)return g=!0,v.reset(),g=!1,void 0;o=document.createElement("video"),o.width=h,o.height=a,navigator.getUserMedia({video:!0,audio:!1},function(t){o.src=URL.createObjectURL(t),o.play(),e(o)},function(){})}catch(n){throw"Error setting webcam..."}},f&&y()}},E={};E.getIcon=function(){var t=!1,e=function(){for(var t=document.getElementsByTagName("head")[0].getElementsByTagName("link"),e=t.length,o=e-1;o>=0;o--)if(/icon/i.test(t[o].getAttribute("rel")))return t[o];return!1};return r.elementId?(t=document.getElementById(r.elementId),t.setAttribute("href",t.getAttribute("src"))):(t=e(),t===!1&&(t=document.createElement("link"),t.setAttribute("rel","icon"),document.getElementsByTagName("head")[0].appendChild(t))),t.setAttribute("type","image/png"),t},E.setIcon=function(t){var e=t.toDataURL("image/png");if(r.elementId)document.getElementById(r.elementId).setAttribute("src",e);else if(w.ff||w.opera){var o=i;i=document.createElement("link"),w.opera&&i.setAttribute("rel","icon"),i.setAttribute("rel","icon"),i.setAttribute("type","image/png"),document.getElementsByTagName("head")[0].appendChild(i),i.setAttribute("href",e),o.parentNode&&o.parentNode.removeChild(o)}else i.setAttribute("href",e)};var L={};return L.duration=40,L.types={},L.types.fade=[{x:.4,y:.4,w:.6,h:.6,o:0},{x:.4,y:.4,w:.6,h:.6,o:.1},{x:.4,y:.4,w:.6,h:.6,o:.2},{x:.4,y:.4,w:.6,h:.6,o:.3},{x:.4,y:.4,w:.6,h:.6,o:.4},{x:.4,y:.4,w:.6,h:.6,o:.5},{x:.4,y:.4,w:.6,h:.6,o:.6},{x:.4,y:.4,w:.6,h:.6,o:.7},{x:.4,y:.4,w:.6,h:.6,o:.8},{x:.4,y:.4,w:.6,h:.6,o:.9},{x:.4,y:.4,w:.6,h:.6,o:1}],L.types.none=[{x:.4,y:.4,w:.6,h:.6,o:1}],L.types.pop=[{x:1,y:1,w:0,h:0,o:1},{x:.9,y:.9,w:.1,h:.1,o:1},{x:.8,y:.8,w:.2,h:.2,o:1},{x:.7,y:.7,w:.3,h:.3,o:1},{x:.6,y:.6,w:.4,h:.4,o:1},{x:.5,y:.5,w:.5,h:.5,o:1},{x:.4,y:.4,w:.6,h:.6,o:1}],L.types.popFade=[{x:.75,y:.75,w:0,h:0,o:0},{x:.65,y:.65,w:.1,h:.1,o:.2},{x:.6,y:.6,w:.2,h:.2,o:.4},{x:.55,y:.55,w:.3,h:.3,o:.6},{x:.5,y:.5,w:.4,h:.4,o:.8},{x:.45,y:.45,w:.5,h:.5,o:.9},{x:.4,y:.4,w:.6,h:.6,o:1}],L.types.slide=[{x:.4,y:1,w:.6,h:.6,o:1},{x:.4,y:.9,w:.6,h:.6,o:1},{x:.4,y:.9,w:.6,h:.6,o:1},{x:.4,y:.8,w:.6,h:.6,o:1},{x:.4,y:.7,w:.6,h:.6,o:1},{x:.4,y:.6,w:.6,h:.6,o:1},{x:.4,y:.5,w:.6,h:.6,o:1},{x:.4,y:.4,w:.6,h:.6,o:1}],L.run=function(t,e,o,i){var a=L.types[r.animation];return i=o===!0?"undefined"!=typeof i?i:a.length-1:"undefined"!=typeof i?i:0,e=e?e:function(){},i<a.length&&i>=0?(b[r.type](n(t,a[i])),setTimeout(function(){o?i-=1:i+=1,L.run(t,e,o,i)},L.duration),E.setIcon(c),void 0):(e(),void 0)},p(),{badge:I,video:M,image:A,webcam:T,reset:v.reset}};"undefined"!=typeof define&&define.amd?define([],function(){return t}):"undefined"!=typeof module&&module.exports?module.exports=t:this.Favico=t}(); \ No newline at end of file diff --git a/js/models.js b/js/models.js index 42e46fa..5afdcb2 100644 --- a/js/models.js +++ b/js/models.js @@ -166,10 +166,11 @@ models.service('models', ['$rootScope', 'colors', function($rootScope, colors) { }); activeBuffer.active = true; - activeBuffer.unread = ''; - activeBuffer.notification = ''; + activeBuffer.unread = 0; + activeBuffer.notification = 0; $rootScope.$emit('activeBufferChanged'); + $rootScope.$emit('notificationChanged'); } /* diff --git a/js/websockets.js b/js/websockets.js index 3bb68cd..a39b86d 100644 --- a/js/websockets.js +++ b/js/websockets.js @@ -201,11 +201,13 @@ weechat.factory('handlers', ['$rootScope', 'colors', 'models', 'plugins', functi if (!initial) { if (!buffer.active && _.contains(message.tags, 'notify_message') && !_.contains(message.tags, 'notify_none')) { buffer.unread++; + $rootScope.$emit('notificationChanged'); } if(message.highlight || _.contains(message.tags, 'notify_private') ) { buffer.notification++; $rootScope.createHighlight(buffer, message); + $rootScope.$emit('notificationChanged'); } } } @@ -424,13 +426,29 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout', } } - $rootScope.$on('activeBufferChanged', function() { $rootScope.scrollToBottom(); document.getElementById('sendMessage').focus(); var ab = models.getActiveBuffer(); $rootScope.pageTitle = ab.shortName + ' | ' + ab.title; }); + $rootScope.$on('notificationChanged', function() { + var notifications = _.reduce(models.model.buffers, function(memo, num) { return parseInt(memo||0) + num.notification;}); + if (notifications > 0 ) { + $scope.favico = new Favico({ + animation:'none' + }); + $scope.favico.badge(notifications); + }else { + var unread = _.reduce(models.model.buffers, function(memo, num) { return parseInt(memo||0) + num.unread;}); + $scope.favico = new Favico({ + animation:'none', + bgColor : '#5CB85C', + textColor : '#ff0', + }); + $scope.favico.badge(unread); + } + }); $scope.buffers = models.model.buffers; $scope.activeBuffer = models.getActiveBuffer @@ -506,7 +524,7 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout', if (models.getActiveBuffer() == buffer) { return true; } - return (parseInt(buffer.unread) || 0) > 0; + return buffer.unread > 0; } return true; }; @@ -556,5 +574,6 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout', return true; } }; + }] ); From d1ab293b4b32fa6244fb6683bb73419681d0a873 Mon Sep 17 00:00:00 2001 From: Tor Hveem <tor@hveem.no> Date: Tue, 15 Oct 2013 15:41:13 +0200 Subject: [PATCH 4/9] Attempt at tidying up the landing page slightly --- index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 8b46381..581dde5 100644 --- a/index.html +++ b/index.html @@ -29,7 +29,6 @@ WeeChat web frontend </small> </h2> - <div class="alert alert-info">WeeChat 0.4.2 or later is required</div> <div class="alert alert-danger" ng-show="errorMessage"> <strong>Oh no!</strong> We cannot connect! </div> @@ -67,7 +66,8 @@ <pre> /set relay.network.password yourpassword /relay add weechat 9001</pre> - Note: The communication goes directly between your browser and your weechat in clear text. + <span class="label label-warning">WeeChat version 0.4.2 or higher is required.</span><br> + The communication goes directly between your browser and your weechat in clear text. Connection settings are saved between sessions, including password, in your own browser. <h4>Encryption</h4> If you want to use encrypted session you first have to set up the relay using SSL From 001024db7d4206f415f3a64c37eaef7abc176bee Mon Sep 17 00:00:00 2001 From: Tor Hveem <tor@hveem.no> Date: Tue, 15 Oct 2013 16:09:08 +0200 Subject: [PATCH 5/9] Collapse for frontpage, and style inputs --- css/glowingbear.css | 23 +++++++- index.html | 135 +++++++++++++++++++++++++++++--------------- 2 files changed, 109 insertions(+), 49 deletions(-) diff --git a/css/glowingbear.css b/css/glowingbear.css index 46e4b13..71c3692 100644 --- a/css/glowingbear.css +++ b/css/glowingbear.css @@ -71,6 +71,26 @@ input#sendMessage { border: 0; width: 100%; } +.panel input { + max-width: 300px; +} +input[type=text], input[type=password] { + color: black; + border: 0; + -webkit-box-shadow: + inset 0 0 8px rgba(0,0,0,0.4), + 0 0 16px rgba(0,0,0,0.4); + -moz-box-shadow: + inset 0 0 8px rgba(0,0,0,0.4), + 0 0 16px rgba(0,0,0,0.4); + box-shadow: + inset 0 0 8px rgba(0,0,0,0.4), + 0 0 16px rgba(0,0,0,0.4); + background: rgba(255,255,255,0.5); +} +#sidebar, .panel { + background: #282828; +} #sidebar { position: fixed; width: 12%; @@ -78,8 +98,7 @@ input#sendMessage { height: 100%; min-width: 130px; overflow: auto; - background: #282828; -} + } .content { height: 100%; min-height: 100%; diff --git a/index.html b/index.html index 581dde5..e56cb12 100644 --- a/index.html +++ b/index.html @@ -32,55 +32,96 @@ <div class="alert alert-danger" ng-show="errorMessage"> <strong>Oh no!</strong> We cannot connect! </div> - <form class="form-signin" role="form"> - <div class="form-group"> - <label class="control-label" for="host">WeeChat hostname</label> - <input type="text" class="form-control" id="host" ng-model="host" placeholder="Address"> - <p class="help-block">Enter the hostname to the WeeChat relay</p> - </div> - <div class="form-group"> - <label class="control-label" for="port">WeeChat port number</label> - <input type="text" class="form-control" id="port" ng-model="port" placeholder="9001"> - <p class="help-block">Enter the the port to the WeeChat relay</p> - </div> - <div class="form-group"> - <label class="control-label" for="password">WeeChat relay password</label> - <input type="password" class="form-control" id="password" ng-model="password" placeholder="Password"> - <p class="help-block">Password will be stored in your browser session</p> + <div class="panel-group" id="accordion"> + <div class="panel "> + <div class="panel-heading"> + <h4 class="panel-title"> + <a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion" href="#collapseOne"> + Connection settings + </a> + </h4> </div> - <div class="form-group"> - <label class="control-label" for="proto">Encryption</label> - <input type="checkbox" class="form-control" id="ssl" ng-model="ssl"> - <p class="help-block">Check the box if you want to encrypt communication between browser and WeeChat. <strong>Note</strong>: Due to a <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=594502">bug</a> encryption will not work in Firefox. You must also first visit the URL https://weechathost:relayport/ to accept the certificate</p> + <div id="collapseOne" class="panel-collapse collapse in"> + <div class="panel-body"> + <form class="form-signin" role="form"> + <div class="form-group"> + <label class="control-label" for="host">WeeChat hostname</label> + <input type="text" class="form-control" id="host" ng-model="host" placeholder="Address"> + <p class="help-block">Enter the hostname to the WeeChat relay</p> + </div> + <div class="form-group"> + <label class="control-label" for="port">WeeChat port number</label> + <input type="text" class="form-control" id="port" ng-model="port" placeholder="9001"> + <p class="help-block">Enter the the port to the WeeChat relay</p> + </div> + <div class="form-group"> + <label class="control-label" for="password">WeeChat relay password</label> + <input type="password" class="form-control" id="password" ng-model="password" placeholder="Password"> + <p class="help-block">Password will be stored in your browser session</p> + </div> + <div class="form-group"> + <label class="control-label" for="proto">Encryption</label> + <input type="checkbox" class="form-control" id="ssl" ng-model="ssl"> + <p class="help-block">Read encryption instructions for help</p> + </div> + <div class="form-group"> + <label class="control-label" for="port">Lines</label> + <input type="text" class="form-control" id="lines" ng-model="lines" placeholder="40"> + <p class="help-block">Enter number of lines to sync from WeeChat on connect</p> + </div> + <button class="btn btn-lg btn-primary" ng-click="connect()">Connect!</button> + </form> + </div> + </div> + </div> + <div class="panel "> + <div class="panel-heading"> + <h4 class="panel-title"> + <a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion" href="#collapseTwo"> + Usage instructions + </a> + </h4> + </div> + <div id="collapseTwo" class="panel-collapse collapse"> + <div class="panel-body"> + <div>To start using, please enable relay in your WeeChat client: + <pre> + /set relay.network.password yourpassword + /relay add weechat 9001</pre> + <span class="label label-warning">WeeChat version 0.4.2 or higher is required.</span><br> + The communication goes directly between your browser and your weechat in clear text. + Connection settings are saved between sessions, including password, in your own browser. + </div> + </div> + </div> + <div class="panel "> + <div class="panel-heading"> + <h4 class="panel-title"> + <a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion" href="#collapseThree"> + Encryption instructions + </a> + </h4> + </div> + <div id="collapseThree" class="panel-collapse collapse"> + <div class="panel-body"> + If you check the encryption box, communication between browser and WeeChat will be encrypted.<br> + <strong>Note</strong>: Due to a <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=594502">bug</a> encryption will not work in Firefox. You must also first visit the URL https://weechathost:relayport/ to accept the certificate</p> + If you want to use encrypted session you first have to set up the relay using SSL like this: + <pre> + $ mkdir -p ~/.weechat/ssl + $ cd ~/.weechat/ssl + $ openssl req -nodes -newkey rsa:2048 -keyout relay.pem -x509 -days 365 -out relay.pem + </pre> + If WeeChat is already running, you can reload the certificate and private key with command: + <pre> + /relay sslcertkey + /relay add ssl.weechat 8000 + </pre> + </div> + </div> + </div> + </div> </div> - <div class="form-group"> - <label class="control-label" for="port">Lines</label> - <input type="text" class="form-control" id="lines" ng-model="lines" placeholder="40"> - <p class="help-block">Enter number of lines to sync from WeeChat on connect</p> - </div> - <button class="btn btn-lg btn-primary" ng-click="connect()">Connect!</button> - </form> - - <h3>Instructions</h3> - <div>To start using, please enable relay in your WeeChat client: - <pre> -/set relay.network.password yourpassword -/relay add weechat 9001</pre> - <span class="label label-warning">WeeChat version 0.4.2 or higher is required.</span><br> - The communication goes directly between your browser and your weechat in clear text. - Connection settings are saved between sessions, including password, in your own browser. - <h4>Encryption</h4> - If you want to use encrypted session you first have to set up the relay using SSL - <pre> -$ mkdir -p ~/.weechat/ssl -$ cd ~/.weechat/ssl -$ openssl req -nodes -newkey rsa:2048 -keyout relay.pem -x509 -days 365 -out relay.pem -</pre> - If WeeChat is already running, you can reload the certificate and private key with command: - <pre> -/relay sslcertkey -/relay add ssl.weechat 8000 -</pre> </div> </div> <div class="content" ng-show="connected"> From dd77529cba3cf116037c59b85b51513bf11d82ce Mon Sep 17 00:00:00 2001 From: Tor Hveem <tor@hveem.no> Date: Tue, 15 Oct 2013 16:57:40 +0200 Subject: [PATCH 6/9] remove old bufinfo handler --- js/websockets.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/js/websockets.js b/js/websockets.js index a39b86d..6dc4986 100644 --- a/js/websockets.js +++ b/js/websockets.js @@ -241,18 +241,6 @@ weechat.factory('handlers', ['$rootScope', 'colors', 'models', 'plugins', functi old.shortName = obj['short_name']; } - - /* - * Handle answers to (bufinfo) messages - * - * (bufinfo) messages are specified by this client. It is the first - * message that is sent to the relay after connection. - */ - var handleBufferInfo = function(message) { - // buffer info from message - } - - /* * Handle answers to (lineinfo) messages * @@ -274,7 +262,6 @@ weechat.factory('handlers', ['$rootScope', 'colors', 'models', 'plugins', functi } var eventHandlers = { - bufinfo: handleBufferInfo, lineinfo: handleLineInfo, _buffer_closing: handleBufferClosing, _buffer_line_added: handleBufferLineAdded, From 5ed02a5e545e0fc4c9d4a19507d797e580dece2e Mon Sep 17 00:00:00 2001 From: Tor Hveem <tor@hveem.no> Date: Tue, 15 Oct 2013 17:20:35 +0200 Subject: [PATCH 7/9] Change lineinfo into async promise --- js/websockets.js | 43 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/js/websockets.js b/js/websockets.js index 6dc4986..9715ed2 100644 --- a/js/websockets.js +++ b/js/websockets.js @@ -277,7 +277,7 @@ weechat.factory('handlers', ['$rootScope', 'colors', 'models', 'plugins', functi }]); -weechat.factory('connection', ['$q', '$rootScope', '$log', 'handlers', 'colors', 'models', function($q, $rootScope, $log, handlers, colors, models) { +weechat.factory('connection', ['$q', '$rootScope', '$log', '$store', 'handlers', 'colors', 'models', function($q, $rootScope, $log, storage, handlers, colors, models) { protocol = new WeeChatProtocol(); var websocket = null; @@ -314,14 +314,16 @@ weechat.factory('connection', ['$q', '$rootScope', '$log', 'handlers', 'colors', websocket.binaryType = "arraybuffer" websocket.onopen = function (evt) { - doSend(WeeChatProtocol.formatInit({ + $log.info("Connected to relay"); + doSend(WeeChatProtocol.formatInit({ password: passwd, compression: 'off' - })); + })); doSendWithCallback(WeeChatProtocol.formatHdata({ path: 'buffer:gui_buffers(*)', keys: ['number,full_name,short_name,title'] - })).then(function(hdata) { + })).then(function(message) { + $log.info("Parsing bufinfo"); var bufferInfos = message['objects'][0]['content']; // buffers objects for (var i = 0; i < bufferInfos.length ; i++) { @@ -332,16 +334,19 @@ weechat.factory('connection', ['$q', '$rootScope', '$log', 'handlers', 'colors', models.setActiveBuffer(buffer.id); } } - - // Request latest buffer lines for each buffer - $rootScope.getLines(); - + $rootScope.connected = true; + }).then(function() { + $log.info("Parsing lineinfo"); + doSendWithCallback(WeeChatProtocol.formatHdata({ + path: "buffer:gui_buffers(*)/own_lines/last_line(-"+storage.get('lines')+")/data", + keys: [] + })).then(function(hdata) { + handlers.handleLineInfo(hdata); + }); + }).then(function() { + doSend(WeeChatProtocol.formatSync({})); + $log.info("Synced"); }); - doSend(WeeChatProtocol.formatSync({})); - - $log.info("Connected to relay"); - $rootScope.connected = true; - $rootScope.$apply(); } websocket.onclose = function (evt) { @@ -380,18 +385,9 @@ weechat.factory('connection', ['$q', '$rootScope', '$log', 'handlers', 'colors', })); } - var getLines = function(count) { - doSendWithCallback(WeeChatProtocol.formatHdata({ - path: "buffer:gui_buffers(*)/own_lines/last_line(-"+count+")/data", - keys: [] - })).then(function(hdata) { - handlers.handleLineInfo(hdata); - }); - } return { send: doSend, - getLines: getLines, connect: connect, sendMessage: sendMessage } @@ -481,9 +477,6 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout', $scope.connect = function() { connection.connect($scope.host, $scope.port, $scope.password, $scope.ssl); } - $rootScope.getLines = function() { - connection.getLines($scope.lines); - } /* Function gets called from bufferLineAdded code if user should be notified */ $rootScope.createHighlight = function(buffer, message) { From b2174db1970eea3e3aff209e861f4e33ed54195c Mon Sep 17 00:00:00 2001 From: Tor Hveem <tor@hveem.no> Date: Tue, 15 Oct 2013 18:58:00 +0200 Subject: [PATCH 8/9] Remove URLplugin, replace URLs inline with a hrefs --- index.html | 2 +- js/plugins.js | 37 +++++++++++++++++++++---------------- js/websockets.js | 2 +- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/index.html b/index.html index e56cb12..0273904 100644 --- a/index.html +++ b/index.html @@ -162,7 +162,7 @@ <span ng-repeat="part in bufferline.prefix" class="text" style="{{ part.fg }}">{{ part.text }}</span> </td> <td class="message"> - <span ng-repeat="part in bufferline.content" class="text" style="{{ part.fg }}">{{ part.text }} </span> + <span ng-repeat="part in bufferline.content" class="text" style="{{ part.fg }}" ng-bind-html="part.text"></span> <div ng-repeat="metadata in bufferline.metadata"> <div ng-show="metadata.visible"> diff --git a/js/plugins.js b/js/plugins.js index af04f2b..a151128 100644 --- a/js/plugins.js +++ b/js/plugins.js @@ -55,31 +55,46 @@ plugins.service('plugins', ['userPlugins', '$sce', function(userPlugins, $sce) */ var contentForMessage = function(message) { - var content = []; + message.metadata = []; for (var i = 0; i < plugins.length; i++) { var nsfw = false; var visible = true; - if (message.match(nsfwRegexp)) { + if (message.text.match(nsfwRegexp)) { var nsfw = true; var visible = false; } - var pluginContent = plugins[i].contentForMessage(message); + var pluginContent = plugins[i].contentForMessage(message.text); if (pluginContent) { var pluginContent = {'visible': visible, 'content': $sce.trustAsHtml(pluginContent), 'nsfw': nsfw, 'name': plugins[i].name } - content.push(pluginContent); + message.metadata.push(pluginContent); + if (plugins[i].exclusive) { break; } } } - return content; + + /* Replace all URLs with hyperlinks */ + + var urlRegexp = RegExp(/(http|ftp|https):\/\/[\w-]+(\.[\w-]+)+([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])?/g); + for(k in message.content) { + var text = message.content[k].text; + var url = text.match(urlRegexp); + for(i in url) { + var u = url[i]; + text = text.replace(u, '<a target="_blank" href="' + u + '">' + u + '</a>'); + } + message.content[k].text = $sce.trustAsHtml(text); + } + + return message; } return { @@ -124,16 +139,6 @@ plugins.factory('userPlugins', function() { }); youtubePlugin.name = 'youtube video'; - var urlPlugin = new Plugin(function(message) { - var url = message.match(urlRegexp); - if (url) { - return '<a target="_blank" href="' + url[0] + '">' + url[0] + '</a>'; - } - return null; - - }); - urlPlugin.name = 'url'; - var imagePlugin = new Plugin(function(message) { var url = message.match(urlRegexp); @@ -149,6 +154,6 @@ plugins.factory('userPlugins', function() { imagePlugin.name = 'image'; return { - plugins: [youtubePlugin, urlPlugin, imagePlugin] + plugins: [youtubePlugin, imagePlugin] } }); diff --git a/js/websockets.js b/js/websockets.js index 9715ed2..1ad4c59 100644 --- a/js/websockets.js +++ b/js/websockets.js @@ -191,7 +191,7 @@ weechat.factory('handlers', ['$rootScope', 'colors', 'models', 'plugins', functi // Only react to line if its displayed if(message.displayed) { var buffer = models.getBuffer(message.buffer); - message.metadata = plugins.PluginManager.contentForMessage(message.text); + message = plugins.PluginManager.contentForMessage(message); buffer.addLine(message); if (buffer.active) { From 3a1fb161a7613f00189efd629d1e0a3620aaaa5d Mon Sep 17 00:00:00 2001 From: Tor Hveem <tor@hveem.no> Date: Tue, 15 Oct 2013 19:08:24 +0200 Subject: [PATCH 9/9] Remove uneeded parseInt --- js/websockets.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/websockets.js b/js/websockets.js index 1ad4c59..82c99cc 100644 --- a/js/websockets.js +++ b/js/websockets.js @@ -416,14 +416,14 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout', $rootScope.pageTitle = ab.shortName + ' | ' + ab.title; }); $rootScope.$on('notificationChanged', function() { - var notifications = _.reduce(models.model.buffers, function(memo, num) { return parseInt(memo||0) + num.notification;}); + var notifications = _.reduce(models.model.buffers, function(memo, num) { return (memo||0) + num.notification;}); if (notifications > 0 ) { $scope.favico = new Favico({ animation:'none' }); $scope.favico.badge(notifications); }else { - var unread = _.reduce(models.model.buffers, function(memo, num) { return parseInt(memo||0) + num.unread;}); + var unread = _.reduce(models.model.buffers, function(memo, num) { return (memo||0) + num.unread;}); $scope.favico = new Favico({ animation:'none', bgColor : '#5CB85C',