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 4747cb8..a33cdd8 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 8df7787..775bd7d 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);
},
@@ -196,7 +214,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);
}
})
@@ -212,7 +230,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);
@@ -500,13 +518,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 96ee5f9..a134991 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) {