diff --git a/public/gfx/icon_128x128.png b/public/gfx/icon_128x128.png new file mode 100644 index 0000000..ec48cc3 Binary files /dev/null and b/public/gfx/icon_128x128.png differ diff --git a/public/gfx/icon_128x128_error.png b/public/gfx/icon_128x128_error.png new file mode 100644 index 0000000..672c207 Binary files /dev/null and b/public/gfx/icon_128x128_error.png differ diff --git a/public/gfx/icon_128x128_info.png b/public/gfx/icon_128x128_info.png new file mode 100644 index 0000000..e16d45e Binary files /dev/null and b/public/gfx/icon_128x128_info.png differ diff --git a/public/gfx/icon_16x16.png b/public/gfx/icon_16x16.png new file mode 100644 index 0000000..6249c85 Binary files /dev/null and b/public/gfx/icon_16x16.png differ diff --git a/public/gfx/icon_32x32.png b/public/gfx/icon_32x32.png new file mode 100644 index 0000000..58b6985 Binary files /dev/null and b/public/gfx/icon_32x32.png differ diff --git a/public/gfx/icon_64x64.png b/public/gfx/icon_64x64.png new file mode 100644 index 0000000..259396d Binary files /dev/null and b/public/gfx/icon_64x64.png differ 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) {