diff --git a/public/js/cryptalk_modules/audio.js b/public/js/cryptalk_modules/audio.js new file mode 100644 index 0000000..97efc39 --- /dev/null +++ b/public/js/cryptalk_modules/audio.js @@ -0,0 +1,67 @@ +/* Usage: + + channel.emit('audio:play',message); + channel.emit('audio:on'); + channel.emit('audio:off'); + +*/ + +// Sounds module, used for emitting those annoying bl-up sounds when receiving a message +define(['queue','mediator'], function (queue,mediator) { + + var ac = false, + enabled = true, + + channel = mediator(), + + // Recursive function for playing tones, takes an array of [tone,start_ms,duration_ms] - entries + // i is only used for recursion + playTones = function (tones, i) { + + // Parameter defaults + i = (i === undefined) ? 0 : i; + + // Stop if we've reached the end of iteration, and require ac + if( !(i < Object.keys(tones).length) || !ac || !enabled ) return; + + // Add tones to execution queue + var current_tones = tones[i], + freqs = current_tones[0], + start = current_tones[1], + duration = current_tones[2]; + + var o = ac.createOscillator(); + var g = ac.createGain(); + o.frequency.value = freqs; + o.connect(g); + g.gain.value = 0.25; + g.connect(ac.destination); + queue.add_function_delayed(start,function() { o.noteOn(0); }); + queue.add_function_delayed(start+duration,function() { o.noteOff(0); }); + + // Iterate, or start playing + i++; + if( i < Object.keys(tones).length ) { + playTones(tones,i); + } else { + queue.run(); + + } + + }, + + on = function() { + enabled = true; + }, + + off = function() { + enabled = false; + }; + + if( window.AudioContext || window.webkitAudioContext ) ac = new ( window.AudioContext || window.webkitAudioContext ); + + channel.on('audio:play',function(message) { playTones(message) }); + channel.on('audio:on',function(message) { on(); }); + channel.on('audio:off',function(message) { off(); }); + +}); \ No newline at end of file diff --git a/public/js/cryptalk_modules/cryptalk.js b/public/js/cryptalk_modules/cryptalk.js index 3c9f0a5..595fa5b 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','notifications'] + requires: ['mediator','hosts', 'templates', 'audio', 'fandango','notifications','sounds'] }, function ($, requires, data) { var socket, key, @@ -9,7 +9,7 @@ define({ room, hash, nick, - mute, + mute = false, settings = {}, @@ -29,8 +29,8 @@ define({ hosts = requires.hosts, fandango = requires.fandango, templates = requires.templates, - sound = requires.sound, - notifications = requires.notifications, + sounds = requires.sounds, + channel = requires.mediator(), lockInput = function () { components.input[0].setAttribute('disabled', 'disabled'); @@ -44,16 +44,21 @@ define({ }, 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); - } - - } + + var title = (type!='message') ? 'Cryptalk' : nick, + icon = (type == 'message') ? 'gfx/icon_128x128.png' : (type == 'error') ? 'gfx/icon_128x128_error.png' : 'gfx/icon_128x128_info.png'; + + // Emit notification + channel.emit('notification:send', + { + title: title.substring(0, 20), + body: text.substring(0, 80), + icon: icon + }); + + // Emit sound + if ( type == 'message' ) channel.emit('audio:play',sounds.message); + }, // Adds a new message to the DOM @@ -63,8 +68,7 @@ define({ post, data = fandango.merge({}, settings, { nick: nick, - room: room, - mute: mute + room: room }); data.text = $.template(text, data); @@ -77,7 +81,6 @@ define({ showNotification(type, nick, text) - // Append the post to the chat DOM element components.chat[clearChat ? 'html' : 'append'](post); }, @@ -214,7 +217,6 @@ define({ post('error', templates.messages.unable_to_decrypt); } else { post('message', sanitized, false, false, nick); - //if( !mute && !notifications.windowActive()) sound.playTones(sound.messages.message); } }) @@ -228,10 +230,6 @@ define({ } else { post('server', templates.server[sanitized]); } - - // Play sound - //if (sound.messages[sanitized] !== undefined && !mute && !notifications.windowActive() ) sound.playTones(sound.messages[sanitized]); - } else { post('error', templates.server.bogus); } @@ -347,11 +345,11 @@ define({ }, mute: function () { - // Invert mute - mute = !mute; + mute = true; + }, - // Inform that the key has been set - post('info', $.template(templates.messages[mute ? 'muted' : 'unmuted'])); + unmute: function () { + mute = false; }, join: function (payload) { @@ -516,9 +514,17 @@ define({ // Post the help/welcome message post('motd', templates.motd, true); - unlockInput(); + // Route mediator messages + channel.on('window:focused',function() { + channel.emit('audio:off'); + channel.emit('notification:off'); + }); + channel.on('window:blurred',function() { + if( !mute ) channel.emit('audio:on'); + channel.emit('notification:on'); + }); - notifications.enableNative(); + unlockInput(); // It's possible to provide room and key using the hashtag. // The room and key is then seperated by semicolon (room:key). diff --git a/public/js/cryptalk_modules/notifications.js b/public/js/cryptalk_modules/notifications.js index 18f85e6..007f9d2 100644 --- a/public/js/cryptalk_modules/notifications.js +++ b/public/js/cryptalk_modules/notifications.js @@ -1,24 +1,24 @@ /* 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); - + + // Send an notification + channel.emit('notification:send',{ + title: 'Woop', + body: 'Woop woop', + icon: 'gfx/icon.png' + }); + + // Turn notifications on + channel.emit('notification:on'); + + // Turn notifications off + channel.emit('notification:off'); + */ -define(function (){ +define(['mediator','window'],function (mediator,win){ - var exports = {}, + var enabled = true, - window_active, - blur_delay_timer, native_supported = false, new_title, @@ -26,108 +26,93 @@ define(function (){ blink_timer, interval, - now = function () { - return performance.now() || Date.now(); - }, - - focusCallback = function() { - /* Reset everything after regaining focus */ - resetState(); + channel = mediator(), + + now = function () { + return performance.now() || Date.now(); }, - + + on = function () { + enabled = true; + }, + + off = function () { + enabled = false; + }, + 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 ); + if(enabled) { + if( win.getTitle() == original_title ) + win.setTitle( new_title ); else - setTitle( original_title); + win.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); + enableNative = function() { + if( native_supported && Notification.permission !== 'denied' ) { + Notification.requestPermission(function (status) { + Notification.permission = status; + }); } - } - }; + }, + blinkTitleUntilFocus = function(t,i) { + interval = (i == undefined) ? 1000 : i; + if ( enabled ) { + new_title = t; + original_title = getTitle(); + doBlink(); + } + }, + + notify = function(title,body,icon,fallback) { + // Only notify while in background + if( enabled) { + + // 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); - } - + + channel.on('notification:send',function(data) { notify(data.title,data.body,data.icon,true); }); + channel.on('notification:on',function() { on(); }); + channel.on('notification:off',function() { off(); }); + + enableNative(); + + off(); + // Make sure we are at square one resetState(); - return exports; - }); \ No newline at end of file diff --git a/public/js/cryptalk_modules/sound.js b/public/js/cryptalk_modules/sound.js deleted file mode 100644 index 5576ecf..0000000 --- a/public/js/cryptalk_modules/sound.js +++ /dev/null @@ -1,69 +0,0 @@ -// Sounds module, used for emitting those annoying bl-up sounds when receiving a message -define(['queue'], function (queue) { - - var exports = { messages: {} }, - - ac = false; - - if( window.AudioContext || window.webkitAudioContext ) ac = new ( window.AudioContext || window.webkitAudioContext ); - - // Recursive function for playing tones, takes an array of [tone,start_ms,duration_ms] - entries - // i is only used for recursion - exports.playTones = function (tones, i) { - - // Parameter defaults - i = (i === undefined) ? 0 : i; - - // Stop if we've reached the end of iteration, and require ac - if( ac && !(i < Object.keys(tones).length) || !ac ) return; - - // Add tones to execution queue - var current_tones = tones[i], - freqs = current_tones[0], - start = current_tones[1], - duration = current_tones[2]; - - var o = ac.createOscillator(); - var g = ac.createGain(); - o.frequency.value = freqs; - o.connect(g); - g.gain.value = 0.25; - g.connect(ac.destination); - queue.add_function_delayed(start,function() { o.noteOn(0); }); - queue.add_function_delayed(start+duration,function() { o.noteOff(0); }); - - // Iterate, or start playing - i++; - if( i < Object.keys(tones).length ) { - exports.playTones(tones,i); - } else { - queue.run(); - - } - - }; - - exports.messages = { - message: [ - [261.63*2,0,50], - [261.63*3,0,50], - [261.63*4,50,50], - [261.63*5,50,50] - ], - person_joined: [ - [261.63*3,0,200], - [261.63*1,0,200], - [261.63*3,200,500], - [261.63*2,200,500] - ], - person_left: [ - [261.63*3,0,200], - [261.63*2,0,200], - [261.63*3,200,500], - [261.63*1,200,500] - ] - }; - - return exports; - -}); \ No newline at end of file diff --git a/public/js/cryptalk_modules/sounds.js b/public/js/cryptalk_modules/sounds.js new file mode 100644 index 0000000..df9f1c0 --- /dev/null +++ b/public/js/cryptalk_modules/sounds.js @@ -0,0 +1,20 @@ +define({ + message: [ + [261.63*2,0,50], + [261.63*3,0,50], + [261.63*4,50,50], + [261.63*5,50,50] + ], + person_joined: [ + [261.63*3,0,200], + [261.63*1,0,200], + [261.63*3,200,500], + [261.63*2,200,500] + ], + person_left: [ + [261.63*3,0,200], + [261.63*2,0,200], + [261.63*3,200,500], + [261.63*1,200,500] + ] +}); \ No newline at end of file diff --git a/public/js/cryptalk_modules/window.js b/public/js/cryptalk_modules/window.js new file mode 100644 index 0000000..115c35e --- /dev/null +++ b/public/js/cryptalk_modules/window.js @@ -0,0 +1,41 @@ +/* + + Emits: + 'window:focused' + 'window:blurred' + + Exports: + title = window.getTitle(); + window.setTitle(title); + +*/ +define(['mediator'],function (mediator){ + + var exports = {}, + channel = mediator(), + + focusCallback = function() { + channel.emit('window:focused'); + }, + + blurCallback = function() { + channel.emit('window:blurred'); + }; + + exports.getTitle = function(t) { document.title = t; }, + exports.setTitle = function() { return document.title; }; + + // 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); + } + + return exports; + +}); \ No newline at end of file