From 0eca4081d27934008e7edc5516ef5cdd090641f3 Mon Sep 17 00:00:00 2001 From: Hexagon Date: Mon, 22 Sep 2014 22:11:13 +0200 Subject: [PATCH] Notifications and stuff --- public/gfx/icon_128x128.png | Bin 0 -> 8941 bytes public/gfx/icon_128x128_error.png | Bin 0 -> 8417 bytes public/gfx/icon_128x128_info.png | Bin 0 -> 7959 bytes public/gfx/icon_16x16.png | Bin 0 -> 628 bytes public/gfx/icon_32x32.png | Bin 0 -> 1450 bytes public/gfx/icon_64x64.png | Bin 0 -> 3395 bytes public/index.html | 5 + public/js/cryptalk_modules/cryptalk.js | 47 +++++-- public/js/cryptalk_modules/hosts.js | 2 +- public/js/cryptalk_modules/notifications.js | 133 ++++++++++++++++++++ public/js/cryptalk_modules/templates.js | 4 +- server.js | 37 +++++- 12 files changed, 209 insertions(+), 19 deletions(-) create mode 100644 public/gfx/icon_128x128.png create mode 100644 public/gfx/icon_128x128_error.png create mode 100644 public/gfx/icon_128x128_info.png create mode 100644 public/gfx/icon_16x16.png create mode 100644 public/gfx/icon_32x32.png create mode 100644 public/gfx/icon_64x64.png create mode 100644 public/js/cryptalk_modules/notifications.js diff --git a/public/gfx/icon_128x128.png b/public/gfx/icon_128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..ec48cc3a6f35afb9cdd2a9aa29cd7b59cee67a66 GIT binary patch literal 8941 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 literal 0 HcmV?d00001 diff --git a/public/gfx/icon_128x128_error.png b/public/gfx/icon_128x128_error.png new file mode 100644 index 0000000000000000000000000000000000000000..672c2072ed18ae697747eaa2945bcfc6bcb84b64 GIT binary patch literal 8417 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 literal 0 HcmV?d00001 diff --git a/public/gfx/icon_128x128_info.png b/public/gfx/icon_128x128_info.png new file mode 100644 index 0000000000000000000000000000000000000000..e16d45e78ada784f13215412e541ee836aa6586e GIT binary patch literal 7959 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 W`tB|r=UYgb0#!v#g|82-LjD80K?#5W literal 0 HcmV?d00001 diff --git a/public/gfx/icon_16x16.png b/public/gfx/icon_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..6249c859bf799f50759f42651372a28f8923ff49 GIT binary patch literal 628 zcmV-)0*n2LP)<40s%=x zK~y-)rBh8vR8bf`_vgLW8=Uc{`H6fZ6$TZi?IK7Av53ZO7u8;++-M?2gKd%)2GJr{ z;wor2fnl^Mifo}6)*^^742G#FCL5C;)A-)IUyGP<-lP_t)yMg`=N=B{1O5{N0QL90gFAYI6*h>T`5HC2z z{-wgoTtlkky#K+@-a|vtRNLp4v9mkM8~{M<+L2cdS4{+fISRt|yxizB2>=I%lHuHP zZrX93y>2`NG9_xwV7FK+V0AXXwB2Gi7{H4jlt;X#qcO0(DIUzNEX^xY5eTu$s?GVD zs?f!EgZFMTzs1<-vFTNETE*8Q6az@(&^~?#=-1xVjGLM9*};koN~qluJO={cDgX?O z7~DGZw}2FK_KzW!@TNcg+W3K~wdtrT4Wx4r2v&I8%!UF0z->2QmM;-hBmj*|JRM1A zHx=-dcf!w+DM8i7%(^`&;qb_5%k|v4%F2olU5VpI{k-%O+5D$u+0P(E0$V%ZUYdL` zjz<-4WxVI$m>n~ESU@u3jN4xd0v-d15Rm%a-SxZ9Jn5To*Q>Hrpte0M(!79gbBa-j z6&!F)+E%!}r1vg1V zK~z}7+sA`lMWl2tJ0%~sV&o1t0xKo00wMMDDw+vAfy0Gf+7XeWu9^g{qXEj zlaJhMs;LUsO|@@cy7NQ;jN3kcesN>P5oi)D0Wd?gN-6*z-N<#ZmK{I%g{=Jxw>0#| zNJ9ETPJDH0cy`g5#!G7sJp`OP3g>Ui&NejTN+`7VE#H(Hf79nD^Yixh#`wis`J%k8 zGG+J4tlFBUx{vcp^0kDoS5rt20KkOZOD7dn7o8QF0GQf6&`!1j)%oyz?5Gz{t^M{= zS32JKWU&A+tf2J9_kPPi-i|h5n;Kk(ekCq?=pxIuuKv3EfX!WP{v|2voW~V*GGyE= z8$^s7l|}oz;xEZuD@&^HzL#HIaLpgE#sdTh28a-Ff@A-vymh6zxqkhhb$11YGBS66 za@?6azg;K64|{+BuD1YN!GS_-)SxB#sYeb1!0c_yr9WuJUMnli^7?&~iCQ-?!Hzaz z8xpLstr)ffoFPv=vyHlo-;j)1S&hNwC5j#v6IEU)K~T(KA?hl(=0?A=aW?w{*0Rcm znlBvX$Oq+>CTzQ(%Mxy*&Ir?w8@icuJ?Y4CBJy?%AmvzQ)PYMsRyp*y2Poix>;3Hj zKtP1y$yBBFW121RbfV5GO^+vQ(6FI<@>0Ib>+u?Pq~W{fX4LwcHnd4{QvmPZe7N9)LCJGg-R)H!=txQ_t^fAvbqB+Q6SO?PXo|$Lc5Csr~uooLFu9sOP5++clxb;2!dcf+mn2+0a(GnoZbgZ!H#i57uxAQ0Z>*`?ryexqkFIksFn|b z{1j>285lD-r%wRP+_iEJQMc8qEe8~UDz`Kaq{lBEoBPqBf}V8C-T-LyHq8rpVH7}2 z08zbUnI9J&yCLu0{pXW$toI|?=(L&rWrRu41Y2aY*S3g-5R#cigyFk?`Oa0J6Npqq zAOWZrgHTK$!9EgP>M=Y!i_&tweAJmx-YhOO6Lu{AQb?PXOL`ok+%}Y3Ie~D&Y|=fi z+i&^9A-O%&t*2Iss3J@^jwU56O5HehMFai;QGD9`$pxi1&p1z;P!TRb62R0xgo-8$ zTyH@!p~V9PVy+QVTpk|t?B=|bgXbS-DwY-X)f1T*TAg)`q9V@w5m@K|MoS|Cs5$sx zWAZWc?Qp~MY2nrrd&>J@Ea#vdcv%X0VdBV_rkACiSmu_-J|b#rOA+?jXqiW-#ME8i ztV>)zzHsY_J$>~sVO{EtE2h0OH9K{02>=wQ9!m<>!ZMhsmb?KFQMF|GbKHO-3yL#x z-`_lAWdr(>a(_Q(bhSPr^X-)b)25Wht$%&hfBJR&7pNNf9?&k@?f?J)07*qoM6N<$ Ef(k&TfdBvi literal 0 HcmV?d00001 diff --git a/public/gfx/icon_64x64.png b/public/gfx/icon_64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..259396dadc5125cbfe2916587554ce999df5b81b GIT binary patch literal 3395 zcmV-J4ZQM+P)(JT7~_DNfNcO3JL(E3s~aOkrlKfne#5=zoZUaF9#T+cO^<*s_FDDh zzPiKS-~IO4-#PnS;Jlr;^LE}&GGh#_c;$(UyUx2}%j93p8<+UK%8SysG2VKE>b;=g zjeTW@uL^`h=$O;)_kB9{Sif=d;#U)3qz1zBtpy4y*L6pI%bR0T-eOLIhC+CV&))qZ}!V zS8_(U6*^_+uj`Z7eZ#uz7Hk86xb`^c1nf7O0RS*y*$pedC_Ow|6p;`?7+_`)1K^s= znMARYvUnwDhSJfkb%)0WwCnlnHDl&}Py+@9oq&Af0MN#eVs^6U5A{eXu>`6I03@O# zB4J`Fi&t>#luX+-tNoh(`4??`xZs|Ts=@Fn=(uMT4Bu#Sl_N=E000bGb@RQS7Jadp zltM!QoP0+D2{TJsw1RU|Gj)%g3s?2a@BY~8(R22liNFsXFXa5JHUuEB>&V`C@BD7I z#Q+#}$5_}SAd@?Y5Y>bA1hs_nQw zS6sUG`URgfbPYFjf?_4Bw``(bd0l_v8=oM}W3ND1TdiWzc-p`fH{P49ppTRk5de%| zK4aFJS2rB0(9sLDHQ@~({_*3>AD^~DMCkM41Kgq%NLai3e^?kbUJeLM)HDVF0IUd? z#33Y9ykdKbZ~R!Gg@fb6Z*0tZ^tIo-Q&?8iTd{(K9hl4zj6+b4vMIsP@f-V&8n|-w zoc+yWK*C=C?5{MSL$7HuKhDi%0mTgfkYXjhmW*B3+~AAKiwOXJ{L9%l{OqY^$4evS zy%Z}z0C5!n0YDJ39M@jUWKHS-@6dR;MS`KUWeiAb8n001bDRq>?0Kb%(OMHx+fcOn8Ult3vhm|}nW zhXCILWU zWid5thSy`|#CMCMC6^iqH@XUFD^wb~Rm7@- zH?6wkxlh0R-)nyE$izGzA6?W09gH}bpr;>D~RqdIyffi2J45}p_=@!Rek^n z2vki!5n&>Gu;OUmo}zsVn?1+>-h(&1SXgy@yb((cGJpuGI9fe+5>7Ax1Zc77*|y8w z@6Y+|LnmKyp!f*2&u*(m{`^Nzez5nGTT%mI5eR)vorO(xfv^J;U|2^%IV50$5Xgv) zx{mw#e_sCM)%RRBbv*#Yw8zZ?KpV}9Rc_j%p+|yl0HB6bKovzG%%=cA07F1b#f#rj z+8Rkn*p)A@?Y82NPkgF~NgJ&iAv9oH1%QwP<_UIJ(3;0c378HTb4L6Aep+IOybA3+= z0PWLrWy@`kKieiXYabJzh?snf=FS{VR;W?udA`RXH@Nkz0ssJ>@%ifRm(Gm%H4>T7 zMEchjH9E7A0B9d`$IW`rZ^i)FVj)!LL}|_SA0CUA_BRmbqY*#^1ga%}Pt^fbbA00e z0E7uD>cc6Fe6{dWXTk^-ZTOfHOLu3D0SSqG@37luR>UI=D}ZiUTfn3RAOWslUNcKh zdiMV%mhR4K3IKqk96Pn&n7?(+>af}I(c-2E8G&h=CNNT8rcgk+J6hy(vQt1C&6VQK z%Kxun`%;eHstqzDJp<8Cyk!i0JGyAW@eyos|7KNQ-kbovJlLS%Jw* zt+Oop#>dKA7Vgl-GNo5^Ty$?rTvXkj<{$bR{B53#MXV*WP;+ zvz#+2;3~)VzVDi`;p&x}q?yeL6PLzL6fP!~)~&L39jRg6Il}+|uvW%4b~e@&g=pKP zN+K1kSR&y47N=}->2F9x_;A_LLB`soYYdwaByp?DV$6$Ew|1TXVq5r}WHW%~{BGHm zaqR<;11kH3IIb^?;nJcG)>=L@(F4RR6@HaU z>)!rPNl~xKl)*n(e)DL>XD0TVey0toK;`Tqllv|hF>PN`gj+IwEUqY>7;=L)+3a^o znF=q0aXp6LdSKpbS^xI^iTjiG;*#lTNmbcsE7jfKq%fXHqF#)8=Jnis*F~32nm=M% zgjy=E&jbT}qxtP!+rL*_RT>m&dHojvOk`bl;{QJ6hThw6-%v2)`xBEMe9{1Lm1FOD zZu!_yi{-DtVPz`4Dvazp=&r&AZ>7x{I(0VyD~J#v0Pru(pPMqm^IN zxAU|=&Q)%+%O4X-%#WjEX5O|1BWGm4F!h0liO6)wY;#7p)HT^#5eWcD2avt{=ohVb z9@?FII8vOs>bH;nhEz>Anh8>~3iPjyCFKLdZia|*z*M~hMnu3M2&Cq? zdI6IX-K)eFp{TC4pxH3(NTS-KE}3mUnsM3n1xpIr7ry#BajrUfnmC(0An3yXL<%@b2MLj^ZpD1AuS+%L9&MK?HR? zN)*#jN?9~!CzO@y21r9xV`U@p2zussSyi&T_boY#p%#`#!b4T6Q`M+mxoaY-~cWT}4oY+0iBH|X6AJ4a9 znTRCrdo+32_zCacx#6mz7xrBk*IxZZAOctFIvW=P0W%vBp(}d~xpB{;*S>ql@X7xK zfWSGlb)MJ0`Sbu!`%uiHjiFnwm^AqD+kf#A0K7TOW%t=!<`FnzK)iU-;70eg#8~HFWX{3|0A3-Po%ZGgd4O`A+{?dzZb{<(|>g z_W(esdQ;;$YmOM*?!emj+yCRpKfSK`o$96NuulE96LGI+c5U9Rn8iSdt9)Xmq%vMb zUD|ef;giL$+)6|d0LX2Xea?R`1OOx=?w7|7=?ik(H~6NW-~Bd!S$?$b;;rlUGIPl6 zE#l17j6>rFj=Ju_n-}c#e2)Td;9K!MVZHXjf!*wTerEe6R~;O6|CG4^aHO<|ao)a} Z_W$QAv^v1xgGvAZ002ovPDHLkV1fdYet!S} literal 0 HcmV?d00001 diff --git a/public/index.html b/public/index.html index 91b49d9..9820c3c 100644 --- a/public/index.html +++ b/public/index.html @@ -5,6 +5,11 @@ Cryptalk + + + diff --git a/public/js/cryptalk_modules/cryptalk.js b/public/js/cryptalk_modules/cryptalk.js index 621a012..568739e 100644 --- a/public/js/cryptalk_modules/cryptalk.js +++ b/public/js/cryptalk_modules/cryptalk.js @@ -1,7 +1,7 @@ // Main cryptalk module define({ compiles: ['$'], - requires: ['hosts', 'templates', 'sound', 'fandango'] + requires: ['hosts', 'templates', 'sound', 'fandango','notifications'] }, function ($, requires, data) { var socket, key, @@ -26,10 +26,11 @@ define({ }, // Shortcut - hosts = requires.hosts.hosts; - fandango = requires.fandango; - templates = requires.templates; - sound = requires.sound; + hosts = requires.hosts.hosts, + fandango = requires.fandango, + templates = requires.templates, + sound = requires.sound, + notifications = requires.notifications, lockInput = function () { components.input[0].setAttribute('disabled', 'disabled'); @@ -42,8 +43,22 @@ define({ components.input.focus(); }, + showNotification = function (type, nick, text) { + if (!mute) { + if ( type == 'message') { + notifications.notify(nick.substring(0, 20), text.substring(0, 80),'gfx/icon_128x128.png',true); + } else if ( type == 'error' ) { + notifications.notify('Cryptalk', text.substring(0, 80),'gfx/icon_128x128_error.png',true); + } else { + notifications.notify('Cryptalk', text.substring(0, 80),'gfx/icon_128x128_info.png',true); + } + + } + }, + // Adds a new message to the DOM post = function (type, text, clearChat, clearBuffer, nick) { + var tpl = templates.post[type], post, data = fandango.merge({}, settings, { @@ -60,6 +75,9 @@ define({ clearInput(); } + showNotification(type, nick, text) + + // Append the post to the chat DOM element components.chat[clearChat ? 'html' : 'append'](post); }, @@ -192,7 +210,7 @@ define({ post('error', templates.messages.unable_to_decrypt); } else { post('message', sanitized, false, false, nick); - if( !mute ) sound.playTones(sound.messages.message); + //if( !mute && !notifications.windowActive()) sound.playTones(sound.messages.message); } }) @@ -208,7 +226,7 @@ define({ } // Play sound - if (sound.messages[sanitized] !== undefined && !mute ) sound.playTones(sound.messages[sanitized]); + //if (sound.messages[sanitized] !== undefined && !mute && !notifications.windowActive() ) sound.playTones(sound.messages[sanitized]); } else { post('error', templates.server.bogus); @@ -496,13 +514,18 @@ define({ unlockInput(); + notifications.enableNative(); + // It's possible to provide room and key using the hashtag. // The room and key is then seperated by semicolon (room:key). // If there is no semicolon present, the complete hash will be treated as the room name and the key has to be set manually. - if (host && (hash = window.location.hash)) { - parts = hash.slice(1).split(':'); + commands.connect(0, function() { + if (host && (hash = window.location.hash)) { + parts = hash.slice(1).split(':'); + + parts[0] && commands.join(parts[0]); + parts[1] && commands.key(parts[1]); + } + }); - parts[0] && commands.join(parts[0]); - parts[1] && commands.key(parts[1]); - } }); \ No newline at end of file diff --git a/public/js/cryptalk_modules/hosts.js b/public/js/cryptalk_modules/hosts.js index ea61b1a..256731a 100644 --- a/public/js/cryptalk_modules/hosts.js +++ b/public/js/cryptalk_modules/hosts.js @@ -12,4 +12,4 @@ define({ path: 'http://localhost:8080/js/cryptalk_modules/settings.js' } ] -}); \ No newline at end of file +}); diff --git a/public/js/cryptalk_modules/notifications.js b/public/js/cryptalk_modules/notifications.js new file mode 100644 index 0000000..18f85e6 --- /dev/null +++ b/public/js/cryptalk_modules/notifications.js @@ -0,0 +1,133 @@ +/* +Usage + +Native notifications without fallback: + notification.enableNative(); // Once + notification.notify("Woop Woop"); + +Native notifications with fallback: + notification.enableNative(); // Once + notification.notify("Woop Woop",true); // True in 2nd parameter enables fallback + +Title blink only: + notifications.blinkTitleUntilFocus("Woop Woop",1000); + +*/ +define(function (){ + + var exports = {}, + + window_active, + blur_delay_timer, + native_supported = false, + + new_title, + original_title, + blink_timer, + interval, + + now = function () { + return performance.now() || Date.now(); + }, + + focusCallback = function() { + /* Reset everything after regaining focus */ + resetState(); + }, + + resetState = function() { + clearTimeout(blur_delay_timer); + clearTimeout(blink_timer); + if (original_title !== undefined) setTitle(original_title); + original_title = undefined; + new_title = undefined; + window_active = true; + }, + + blurCallback = function() { + /* Apply a slight delay to prevent notifications from popping when the notifications + cause the windows to lose focus */ + clearTimeout(blur_delay_timer); + blur_delay_timer = setTimeout(function() { window_active = false; },1000); + }, + + setTitle = function(t) { document.title = t; }, + getTitle = function() { return document.title; }, + + doBlink = function() { + if(!window_active) { + if( getTitle() == original_title ) + setTitle( new_title ); + else + setTitle( original_title); + + blink_timer = setTimeout(doBlink,interval); + } else { + resetState(); + } + }; + + exports.enableNative = function() { + if( native_supported && Notification.permission !== 'denied' ) { + Notification.requestPermission(function (status) { + Notification.permission = status; + }); + } + }; + + exports.windowActive = function() { + return window_active; + }; + + exports.blinkTitleUntilFocus = function(t,i) { + interval = (i == undefined) ? 1000 : i; + if (!window_active) { + new_title = t; + original_title = getTitle(); + doBlink(); + } + }; + + exports.notify = function(title,body,icon,fallback) { + // Only notify while in background + if( !window_active ) { + + // Set default value for fallback parameter + if ( fallback === undefined) fallback = false; + + if ( native_supported && Notification.permission === "granted") { + + // Create notification + var n = new Notification(title, {body: body, icon:icon}); + + // Handle on show event + n.onshow = function () { + // Automatically close the notification after 5000ms + setTimeout(function(){n.close();},3000); + } + + } else if ( fallback ) { + exports.blinkTitleUntilFocus("Attention",1000); + } + } + }; + + native_supported = (window.Notification !== undefined); + + // Keep track of document focus/blur + if (window.addEventListener){ + // Normal browsers + window.addEventListener("focus", focusCallback, true); + window.addEventListener("blur", blurCallback, true); + } else { + // IE + window.observe("focusin", focusCallback); + window.observe("focusout", blurCallback); + } + + // Make sure we are at square one + resetState(); + + return exports; + +}); \ No newline at end of file diff --git a/public/js/cryptalk_modules/templates.js b/public/js/cryptalk_modules/templates.js index 7e61386..59ea498 100644 --- a/public/js/cryptalk_modules/templates.js +++ b/public/js/cryptalk_modules/templates.js @@ -79,8 +79,8 @@ define({ leave_from_nowhere: 'How are you supposed to leave, while being nowhere?', // Sounds - muted: 'Notification sounds is now muted.', - unmuted: 'Notifications sounds is now on.', + muted: 'Notifications and sounds are now muted.', + unmuted: 'Notifications and sounds are now on.', // Extra variables: 'commandName' unrecognized_command: 'Unrecognized command: "{commandName}"', diff --git a/server.js b/server.js index b0ed501..0f99b9a 100644 --- a/server.js +++ b/server.js @@ -1,7 +1,7 @@ var express = require('express.io'), - uuid = require('node-uuid'); + uuid = require('node-uuid'), -app = express();app.http().io(); + app = express();app.http().io(); app.use(express.static(__dirname + '/public')); @@ -43,9 +43,38 @@ app.io.route('room', { app.io.route('message', { send: function(req) { - if(req.data && req.data.room) req.socket.broadcast.to(req.data.room).emit('message:send', { msg: req.data.msg, nick: req.data.nick} ); - req.socket.emit('message:send', { msg: req.data.msg, nick: req.data.nick} ); + + // Check that the user is in a room + if(req.data && req.data.room) { + + // Check that the message size is within bounds + var total_msg_size = (req.data.msg) ? req.data.msg.length : 0 + (req.data.nick) ? req.data.nick.length : 0; + if( total_msg_size <= 4096) { + + // Check that at least 100ms has passed since last message + if( req.socket.last_message === undefined || new Date().getTime() - req.socket.last_message > 100 ) { + + req.socket.broadcast.to(req.data.room).emit('message:send', { msg: req.data.msg, nick: req.data.nick} ); + req.socket.emit('message:send', { msg: req.data.msg, nick: req.data.nick} ); + + req.socket.last_message = new Date().getTime(); + + } else { + + // Do not complain if message rate is too fast, that would only generate more traffic + + } + + } else { + + // Message size is out of bounds, complain + req.socket.emit('message:server', {msg:'command_failed'} ); + } + + } + } + }); app.io.sockets.on('connection', function(socket) {