Add event subscription to main app with tests. Early start on track view. Add some docs.

This commit is contained in:
Jonathon Wilson 2012-10-21 17:03:46 -06:00
parent 9205b35bc0
commit c5a7a89413
6 changed files with 249 additions and 12 deletions

View File

@ -6,11 +6,17 @@
var JamKazam = context.JK.JamKazam = function() {
var app;
var logger = context.JK.logger;
var subscribers = {}; // Keys are MessageType.MESSAGE values Values are lists of functions to call
/**
* Dynamically build routes from markup. Any layout="screen" will get a route corresponding to
* his layout-id attribute. If a layout-arg attribute is present, that will be named as a data
* section of the route.
*/
function routing() {
var routes = window.RouteMap, rules, rule;
rules = {};
context = {};
routingContext = {};
$('div[layout="screen"]').each(function() {
var target = $(this).attr('layout-id');
var targetUrl = target;
@ -22,14 +28,17 @@
app.layout.changeToScreen(target, data);
};
rules[target] = {route: '/' + targetUrl, method: target};
context[target] = fn;
routingContext[target] = fn;
});
routes.context(context);
routes.context(routingContext);
for (rule in rules) if (rules.hasOwnProperty(rule)) routes.add(rules[rule]);
$(window).bind('hashchange', routes.handler);
$(routes.handler);
}
/**
* Some simple events for hovering on the home page. May want to move to a separate file.
*/
function events() {
$('.homecard').on('mouseenter', function() {
$(this).addClass('hover');
@ -39,17 +48,74 @@
});
}
/**
* Handle a websocket message
*/
function handleMessage(header, payload) {
logger.debug(header.type + ": " + JSON.stringify(payload));
if (header.type in subscribers) {
$.each(subscribers[header.type], function() {
try {
this.call();
} catch (ex) {
logger.warn(ex);
}
});
}
}
/**
* Register for all known types, logging events as they happen, and
* notifying subscribers (see this.subscribe) as they occur.
*/
function _registerMessages() {
for (var message in context.JK.MessageType) {
logger.debug("registering " + message);
context.JK.JamServer.registerMessageCallback(message, handleMessage);
}
}
/**
* Provide a function to call when a certain event type happens.
* No checks for dupes here, so take care to not add the same handler
* for the same event type repeatedly.
* Event Types should be one of the values in JK.MessageType
*/
this.subscribe = function(eventType, handler) {
if (!(eventType in subscribers)) {
subscribers[eventType] = [];
}
subscribers[eventType].push(handler);
};
/**
* Expose event firing. Generally not to be used except by tests.
*/
this.fireEvent = handleMessage;
/**
* Provide a handler object for events related to a particular screen
* being shown or hidden.
* @screen is a string corresponding to the screen's layout-id attribute
* @handler is an object with up to four optional keys:
* beforeHide, afterHide, beforeShow, afterShow, which should all have
* functions as values. If there is data provided by the screen's route
* it will be provided to these functions.
*/
this.bindScreen = function(screen, handler) {
this.layout.bindScreen(screen, handler);
};
/**
* Two-phase construction. Call after construction when in non-unit-test use.
*/
this.initialize = function() {
var url, hash;
app = this;
this.layout = new JK.Layout();
this.layout.initialize();
routing();
_registerMessages();
events();
hash = location.hash;

View File

@ -53,14 +53,9 @@
* Register a simple console logger for all known message types.
*/
this.register = function() {
for (var message in messages) {
logger.debug("registering " + message);
server.registerMessageCallback(message, logMessage);
}
registerLoginPinger();
};
};
})(window);
})(window);

View File

@ -13,11 +13,39 @@
}
function renderSession(sessionData) {
$contents = $('#session-contents');
$contents.empty();
_renderSessionInfo(sessionData);
// TODO: Just for testing. Should respond to events.
var trackData = {
clientId: 1,
name: "Jonathon Wilson",
part: "Keyboard",
avatar: "https://en.gravatar.com/userimage/3198431/5ba95e655ce68d976f46dcf6f99fdde5.png",
latency: "good",
vu: 0.0,
gain: 0.5,
mute: false
};
_addTrack(trackData);
// TODO - hook events to the session-tracks container, not the tracks.
// then have the events see which track fired.
}
function _renderSessionInfo(sessionData) {
$info = $('#session-info');
$info.empty();
var template = $('#template-session-contents').html();
var contents = JK.fillTemplate(template, sessionData);
$contents.html(contents);
$info.html(contents);
}
function _addTrack(trackData) {
var template = $('#template-session-track').html();
var newTrack = JK.fillTemplate(template, trackData);
$('#session-tracks').append(newTrack);
}
function _userJoinedSession() {
logger.debug(arguments);
}
function deleteSession(evt) {
@ -42,6 +70,7 @@
'afterShow': afterShow
};
app.bindScreen('session', screenBindings);
app.subscribe(JK.MessageType.USER_JOINED_MUSIC_SESSION, _userJoinedSession);
};
};

View File

@ -0,0 +1,81 @@
(function(context,$) {
context.JK = context.JK || {};
context.JK.SessionTrack = function() {
var logger = context.JK.logger;
function events() {
$('#session-contents').on("click", '[action="delete"]', deleteSession);
}
/**
* Set latency. val = [good,medium,bad]
*/
function _setLatency(val) {
}
/**
* Set VU level. val = 0.0-1.0
*/
function _setVolumeUnit(val) {
}
/**
* Set the track's gain. val = 0.0-1.0
* Allows external control of channel fader.
*/
function _setGain(val) {
}
/**
* Get the track's gain from current fader. Returns 0.0-1.0
*/
function _getGain() {
}
/**
* Set whether this channel is muted. Takes a boolean where
* true means mute, false, means unmuted.
*/
function _mute(muted) {
}
/**
* Return whether the channel is currently muted.
*/
function _isMute() {
}
/**
* Set the name (typically user name)
*/
function _setName(name) {
}
/**
* Set the part this user is performing. If part is
* one of ENUM.FIGURE_ME_OUT then it is a recognized
* part with an icon, otherwise it is an 'other' value
* with a default icon.
*/
function _setPart(part) {
}
/**
* Set the channel's avatar. Typically the user's profile
* avatar url.
*/
function _setAvatar(avatar_url) {
}
this.initialize = function() {
events();
screenBindings = {
'afterShow': afterShow
};
app.bindScreen('session', screenBindings);
};
};
})(window,jQuery);

View File

@ -371,6 +371,8 @@
<p class="current">Session</p>
</div>
<div id="session-contents">
<div id="session-info"></div>
<div id="session-tracks"></div>
</div>
</div>
@ -381,6 +383,19 @@
<p><a action="delete" action-id="{id}">Delete?</a></p>
</script>
<!-- Template for an individual session track -->
<script type="text/template" id="template-session-track">
<div class="session-track" client-id="{clientId}">
<div class="latency {latency}">Network</div>
<div class="vu">{vu}</div>
<div class="gain">{gain}</div>
<div class="mute">{mute}</div>
<div class="name">{name}</div>
<div class="part">{part}</div>
<div class="avatar"><img src="{avatar}"/></div>
</div>
</script>
<!-- Find Session Screen -->
<div layout="screen" layout-id="findSession" class="screen secondary">

View File

@ -0,0 +1,51 @@
(function(context, $) {
describe("JamKazam Main Application", function() {
var jamkazam;
beforeEach(function() {
jamkazam = new context.JK.JamKazam();
});
describe("Event Subscription", function() {
it("Subscribing to ping should call function", function() {
var called = false;
jamkazam.subscribe(context.JK.MessageType.PING_ACK, function() {
called = true;
});
var header = {type: context.JK.MessageType.PING_ACK};
var payload = {};
jamkazam.fireEvent(header, payload);
expect(called).toBeTruthy();
});
it("All subscribers should be called", function() {
var callCount = 0;
jamkazam.subscribe(context.JK.MessageType.PING_ACK, function() {callCount += 1;});
jamkazam.subscribe(context.JK.MessageType.PING_ACK, function() {callCount += 2;});
var header = {type: context.JK.MessageType.PING_ACK};
var payload = {};
jamkazam.fireEvent(header, payload);
expect(callCount).toEqual(3);
});
it("An error in a subscriber should be caught", function() {
var callCount = 0;
jamkazam.subscribe(context.JK.MessageType.PING_ACK, function() {callCount += 1;});
jamkazam.subscribe(context.JK.MessageType.PING_ACK, function() {throw "Intentional Error";});
jamkazam.subscribe(context.JK.MessageType.PING_ACK, function() {callCount += 1;});
var header = {type: context.JK.MessageType.PING_ACK};
var payload = {};
jamkazam.fireEvent(header, payload);
expect(callCount).toEqual(2);
});
});
});
}(window, jQuery));