Mediator awareness

This commit is contained in:
Hexagon 2014-09-23 19:54:36 +02:00
parent 29d1799d56
commit c74d265579
6 changed files with 245 additions and 195 deletions

View File

@ -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(); });
});

View File

@ -1,7 +1,7 @@
// Main cryptalk module // Main cryptalk module
define({ define({
compiles: ['$'], compiles: ['$'],
requires: ['hosts', 'templates', 'sound', 'fandango','notifications'] requires: ['mediator','hosts', 'templates', 'audio', 'fandango','notifications','sounds']
}, function ($, requires, data) { }, function ($, requires, data) {
var socket, var socket,
key, key,
@ -9,7 +9,7 @@ define({
room, room,
hash, hash,
nick, nick,
mute, mute = false,
settings = {}, settings = {},
@ -29,8 +29,8 @@ define({
hosts = requires.hosts, hosts = requires.hosts,
fandango = requires.fandango, fandango = requires.fandango,
templates = requires.templates, templates = requires.templates,
sound = requires.sound, sounds = requires.sounds,
notifications = requires.notifications, channel = requires.mediator(),
lockInput = function () { lockInput = function () {
components.input[0].setAttribute('disabled', 'disabled'); components.input[0].setAttribute('disabled', 'disabled');
@ -44,16 +44,21 @@ define({
}, },
showNotification = function (type, nick, text) { showNotification = function (type, nick, text) {
if (!mute) {
if ( type == 'message') { var title = (type!='message') ? 'Cryptalk' : nick,
notifications.notify(nick.substring(0, 20), text.substring(0, 80),'gfx/icon_128x128.png',true); icon = (type == 'message') ? 'gfx/icon_128x128.png' : (type == 'error') ? 'gfx/icon_128x128_error.png' : 'gfx/icon_128x128_info.png';
} else if ( type == 'error' ) {
notifications.notify('Cryptalk', text.substring(0, 80),'gfx/icon_128x128_error.png',true); // Emit notification
} else { channel.emit('notification:send',
notifications.notify('Cryptalk', text.substring(0, 80),'gfx/icon_128x128_info.png',true); {
} 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 // Adds a new message to the DOM
@ -63,8 +68,7 @@ define({
post, post,
data = fandango.merge({}, settings, { data = fandango.merge({}, settings, {
nick: nick, nick: nick,
room: room, room: room
mute: mute
}); });
data.text = $.template(text, data); data.text = $.template(text, data);
@ -77,7 +81,6 @@ define({
showNotification(type, nick, text) showNotification(type, nick, text)
// Append the post to the chat DOM element // Append the post to the chat DOM element
components.chat[clearChat ? 'html' : 'append'](post); components.chat[clearChat ? 'html' : 'append'](post);
}, },
@ -214,7 +217,6 @@ define({
post('error', templates.messages.unable_to_decrypt); post('error', templates.messages.unable_to_decrypt);
} else { } else {
post('message', sanitized, false, false, nick); post('message', sanitized, false, false, nick);
//if( !mute && !notifications.windowActive()) sound.playTones(sound.messages.message);
} }
}) })
@ -228,10 +230,6 @@ define({
} else { } else {
post('server', templates.server[sanitized]); post('server', templates.server[sanitized]);
} }
// Play sound
//if (sound.messages[sanitized] !== undefined && !mute && !notifications.windowActive() ) sound.playTones(sound.messages[sanitized]);
} else { } else {
post('error', templates.server.bogus); post('error', templates.server.bogus);
} }
@ -347,11 +345,11 @@ define({
}, },
mute: function () { mute: function () {
// Invert mute mute = true;
mute = !mute; },
// Inform that the key has been set unmute: function () {
post('info', $.template(templates.messages[mute ? 'muted' : 'unmuted'])); mute = false;
}, },
join: function (payload) { join: function (payload) {
@ -516,9 +514,17 @@ define({
// Post the help/welcome message // Post the help/welcome message
post('motd', templates.motd, true); 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. // It's possible to provide room and key using the hashtag.
// The room and key is then seperated by semicolon (room:key). // The room and key is then seperated by semicolon (room:key).

View File

@ -1,24 +1,24 @@
/* /*
Usage Usage
Native notifications without fallback: // Send an notification
notification.enableNative(); // Once channel.emit('notification:send',{
notification.notify("Woop Woop"); title: 'Woop',
body: 'Woop woop',
Native notifications with fallback: icon: 'gfx/icon.png'
notification.enableNative(); // Once });
notification.notify("Woop Woop",true); // True in 2nd parameter enables fallback
// Turn notifications on
Title blink only: channel.emit('notification:on');
notifications.blinkTitleUntilFocus("Woop Woop",1000);
// 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, native_supported = false,
new_title, new_title,
@ -26,108 +26,93 @@ define(function (){
blink_timer, blink_timer,
interval, interval,
now = function () { channel = mediator(),
return performance.now() || Date.now();
}, now = function () {
return performance.now() || Date.now();
focusCallback = function() {
/* Reset everything after regaining focus */
resetState();
}, },
on = function () {
enabled = true;
},
off = function () {
enabled = false;
},
resetState = function() { resetState = function() {
clearTimeout(blur_delay_timer);
clearTimeout(blink_timer); clearTimeout(blink_timer);
if (original_title !== undefined) setTitle(original_title); if (original_title !== undefined) setTitle(original_title);
original_title = undefined; original_title = undefined;
new_title = undefined; new_title = undefined;
window_active = true; 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() { doBlink = function() {
if(!window_active) { if(enabled) {
if( getTitle() == original_title ) if( win.getTitle() == original_title )
setTitle( new_title ); win.setTitle( new_title );
else else
setTitle( original_title); win.setTitle( original_title);
blink_timer = setTimeout(doBlink,interval); blink_timer = setTimeout(doBlink,interval);
} else { } else {
resetState(); resetState();
} }
}; },
exports.enableNative = function() {
if( native_supported && Notification.permission !== 'denied' ) {
Notification.requestPermission(function (status) {
Notification.permission = status;
});
}
};
exports.windowActive = function() { enableNative = function() {
return window_active; if( native_supported && Notification.permission !== 'denied' ) {
}; Notification.requestPermission(function (status) {
Notification.permission = status;
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);
} }
} },
};
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); native_supported = (window.Notification !== undefined);
// Keep track of document focus/blur channel.on('notification:send',function(data) { notify(data.title,data.body,data.icon,true); });
if (window.addEventListener){ channel.on('notification:on',function() { on(); });
// Normal browsers channel.on('notification:off',function() { off(); });
window.addEventListener("focus", focusCallback, true);
window.addEventListener("blur", blurCallback, true); enableNative();
} else {
// IE off();
window.observe("focusin", focusCallback);
window.observe("focusout", blurCallback);
}
// Make sure we are at square one // Make sure we are at square one
resetState(); resetState();
return exports;
}); });

View File

@ -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;
});

View File

@ -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]
]
});

View File

@ -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;
});