From 7a820e78271f0e79192ca35666d9096b527bb129 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 1 Mar 2014 08:58:42 -0600 Subject: [PATCH] * VRFS-726 - don't care if ftue has succeeded before; we care if there are any good devices --- .../assets/javascripts/createSession.js.erb | 2 +- web/app/assets/javascripts/fakeJamClient.js | 5 + web/app/assets/javascripts/layout.js | 7 - web/app/assets/javascripts/sessionList.js | 2 +- web/app/assets/javascripts/utils.js | 1596 +++++++++-------- web/lib/music_session_manager.rb | 4 +- .../lib/jam_websockets/router.rb | 17 +- 7 files changed, 831 insertions(+), 802 deletions(-) diff --git a/web/app/assets/javascripts/createSession.js.erb b/web/app/assets/javascripts/createSession.js.erb index dfec928f2..16b8214dd 100644 --- a/web/app/assets/javascripts/createSession.js.erb +++ b/web/app/assets/javascripts/createSession.js.erb @@ -117,7 +117,7 @@ evt.preventDefault(); // If user hasn't completed FTUE - do so now. - if (!(context.jamClient.FTUEGetStatus())) { + if (!(context.JK.hasOneConfiguredDevice())) { app.afterFtue = function() { submitForm(evt); }; app.layout.startNewFtue(); return; diff --git a/web/app/assets/javascripts/fakeJamClient.js b/web/app/assets/javascripts/fakeJamClient.js index 95d997168..682aa8fc0 100644 --- a/web/app/assets/javascripts/fakeJamClient.js +++ b/web/app/assets/javascripts/fakeJamClient.js @@ -136,6 +136,10 @@ return {latencyknown:true, latency:5} } + function FTUEGetGoodConfigurationList() { + return ['a'] + } + function RegisterVolChangeCallBack(functionName) { dbg('RegisterVolChangeCallBack'); } @@ -659,6 +663,7 @@ this.FTUESetStatus = FTUESetStatus; this.FTUEStartLatency = FTUEStartLatency; this.FTUEGetExpectedLatency = FTUEGetExpectedLatency; + this.FTUEGetGoodConfigurationList = FTUEGetGoodConfigurationList; // Session this.SessionAddTrack = SessionAddTrack; diff --git a/web/app/assets/javascripts/layout.js b/web/app/assets/javascripts/layout.js index aa24db43e..cc82c6825 100644 --- a/web/app/assets/javascripts/layout.js +++ b/web/app/assets/javascripts/layout.js @@ -396,13 +396,6 @@ return; } - // If link requires FTUE, show that first. - if ($currentTarget.hasClass("requires-ftue")) { - if (!(context.jamClient.FTUEGetStatus())) { - app.layout.showDialog('ftue'); - } - } - var destination = $(evt.currentTarget).attr('layout-link'); var destinationType = $('[layout-id="' + destination + '"]').attr("layout"); if (destinationType === "screen") { diff --git a/web/app/assets/javascripts/sessionList.js b/web/app/assets/javascripts/sessionList.js index 5c4ea6de8..13c513d3f 100644 --- a/web/app/assets/javascripts/sessionList.js +++ b/web/app/assets/javascripts/sessionList.js @@ -141,7 +141,7 @@ $('.join-link', $parentRow).click(function(evt) { // If no FTUE, show that first. - if (!(context.jamClient.FTUEGetStatus())) { + if (!(context.JK.hasOneConfiguredDevice())) { app.afterFtue = function() { joinClick(session.id); }; app.layout.startNewFtue(); return; diff --git a/web/app/assets/javascripts/utils.js b/web/app/assets/javascripts/utils.js index 8ed999894..6ed8f2539 100644 --- a/web/app/assets/javascripts/utils.js +++ b/web/app/assets/javascripts/utils.js @@ -1,820 +1,836 @@ /** -* Common utility functions. -*/ -(function(context,$) { + * Common utility functions. + */ +(function (context, $) { - "use strict"; + "use strict"; - context.JK = context.JK || {}; - var logger = context.JK.logger; + context.JK = context.JK || {}; + var logger = context.JK.logger; - var days = new Array("Sun", "Mon", "Tue", - "Wed", "Thu", "Fri", "Sat"); + var days = new Array("Sun", "Mon", "Tue", + "Wed", "Thu", "Fri", "Sat"); - var months = new Array("January", "February", "March", - "April", "May", "June", "July", "August", "September", - "October", "November", "December"); + var months = new Array("January", "February", "March", + "April", "May", "June", "July", "August", "September", + "October", "November", "December"); - context.JK.stringToBool = function(s) { - switch(s.toLowerCase()){ - case "true": case "yes": case "1": return true; - case "false": case "no": case "0": case null: return false; - default: return Boolean(s); + context.JK.stringToBool = function (s) { + switch (s.toLowerCase()) { + case "true": + case "yes": + case "1": + return true; + case "false": + case "no": + case "0": + case null: + return false; + default: + return Boolean(s); + } + }; + + // http://stackoverflow.com/a/8809472/834644 + context.JK.generateUUID = function () { + var d = new Date().getTime(); + var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = (d + Math.random() * 16) % 16 | 0; + d = Math.floor(d / 16); + return (c == 'x' ? r : (r & 0x7 | 0x8)).toString(16); + }); + return uuid; + }; + + // Build up two maps of images, for each instrument id. + // This map is a simple base map of instrument id to the basic image name. + // Below, a loop goes through this and builds two size-specific maps. + // In the future, we should test Whether having a single larger image + // available, and allowing the browser to resize offers better quality. + var icon_map_base = { + "accordion": "accordion", + "acoustic guitar": "acoustic_guitar", + "banjo": "banjo", + "bass guitar": "bass_guitar", + "cello": "cello", + "clarinet": "clarinet", + "computer": "computer", + "default": "default", + "drums": "drums", + "electric guitar": "electric_guitar", + "euphonium": "euphonium", + "flute": "flute", + "french horn": "french_horn", + "harmonica": "harmonica", + "keyboard": "keyboard", + "mandolin": "mandolin", + "oboe": "oboe", + "other": "other", + "piano": "piano", + "saxophone": "saxophone", + "trombone": "trombone", + "trumpet": "trumpet", + "tuba": "tuba", + "ukulele": "ukelele", + "upright bass": "upright_bass", + "viola": "viola", + "violin": "violin", + "voice": "voice" + }; + + var instrumentIconMap24 = {}; + var instrumentIconMap45 = {}; + + $.each(context._.keys(icon_map_base), function (index, instrumentId) { + var icon = icon_map_base[instrumentId]; + instrumentIconMap24[instrumentId] = "/assets/content/icon_instrument_" + icon + "24.png"; + instrumentIconMap45[instrumentId] = "/assets/content/icon_instrument_" + icon + "45.png"; + }); + + /** + * Associates a help bubble on hover (by default) with the specified $element, using jquery.bt.js (BeautyTips) + * @param $element The element that should show the help when hovered + * @param templateName the name of the help template (without the '#template-help' prefix). Add to _help.html.erb + * @param data (optional) data for your template, if applicable + * @param options (optional) You can override the default BeautyTips options: https://github.com/dillon-sellars/BeautyTips + * + */ + context.JK.helpBubble = function ($element, templateName, data, options) { + if (!data) { + data = {} + } + var helpText = context._.template($('#template-help-' + templateName).html(), data, { variable: 'data' }); + + var holder = $('
'); + holder.append(helpText); + + context.JK.hoverBubble($element, helpText, options); + } + + /** + * Associates a bubble on hover (by default) with the specified $element, using jquery.bt.js (BeautyTips) + * @param $element The element that should show the bubble when hovered + * @param text the text or jquery element that should be shown as contents of the bubble + * @param options (optional) You can override the default BeautyTips options: https://github.com/dillon-sellars/BeautyTips + */ + context.JK.hoverBubble = function ($element, text, options) { + if (!text) { + logger.error("hoverBubble: no text to attach to $element %o", $element); + return; + } + + if ($element instanceof jQuery) { + if ($element.length == 0) { + logger.error("hoverBubble: no element specified with text %o", text); + return; + } + } + + var defaultOpts = { + fill: '#333', + strokeStyle: '#ED3618', + spikeLength: 10, + spikeGirth: 10, + padding: 8, + cornerRadius: 0, + cssStyles: { + fontSize: '11px', + color: 'white', + whiteSpace: 'normal' + } + }; + + if (options) { + options = $.extend(false, defaultOpts, options); + } + else { + options = defaultOpts; + } + + $element.bt(text, options); + } + + context.JK.bindHoverEvents = function () { + + function showBubble(bubble, $hoverElement) { + $hoverElement.attr("bubble-id", bubble.id); + bubble.showBubble(); + } + + function hideBubble($hoverElement) { + var bubbleSelector = $hoverElement.attr("bubble-id"); + $(bubbleSelector).hover( + function () { + // do nothing when entering the bubble (this should never happen) + }, + function () { + $(this).fadeOut(500); } - }; + ); + } - // http://stackoverflow.com/a/8809472/834644 - context.JK.generateUUID = function(){ - var d = new Date().getTime(); - var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - var r = (d + Math.random()*16)%16 | 0; - d = Math.floor(d/16); - return (c=='x' ? r : (r&0x7|0x8)).toString(16); - }); - return uuid; - }; - - // Build up two maps of images, for each instrument id. - // This map is a simple base map of instrument id to the basic image name. - // Below, a loop goes through this and builds two size-specific maps. - // In the future, we should test Whether having a single larger image - // available, and allowing the browser to resize offers better quality. - var icon_map_base = { - "accordion":"accordion", - "acoustic guitar":"acoustic_guitar", - "banjo":"banjo", - "bass guitar":"bass_guitar", - "cello":"cello", - "clarinet":"clarinet", - "computer":"computer", - "default":"default", - "drums":"drums", - "electric guitar":"electric_guitar", - "euphonium":"euphonium", - "flute":"flute", - "french horn":"french_horn", - "harmonica":"harmonica", - "keyboard":"keyboard", - "mandolin":"mandolin", - "oboe":"oboe", - "other":"other", - "piano":"piano", - "saxophone":"saxophone", - "trombone":"trombone", - "trumpet":"trumpet", - "tuba":"tuba", - "ukulele":"ukelele", - "upright bass":"upright_bass", - "viola":"viola", - "violin":"violin", - "voice":"voice" - }; - - var instrumentIconMap24 = {}; - var instrumentIconMap45 = {}; - - $.each(context._.keys(icon_map_base), function(index, instrumentId) { - var icon = icon_map_base[instrumentId]; - instrumentIconMap24[instrumentId] = "/assets/content/icon_instrument_" + icon + "24.png"; - instrumentIconMap45[instrumentId] = "/assets/content/icon_instrument_" + icon + "45.png"; + // MUSICIAN + $("[hoveraction='musician']").hoverIntent({ + over: function () { + var bubble = new JK.MusicianHoverBubble($(this).attr('user-id'), $(this).offset()); + showBubble(bubble, $(this)); + }, + out: function () { // this registers for leaving the hoverable element + hideBubble($(this)); + }, + sensitivity: 1 }); - /** - * Associates a help bubble on hover (by default) with the specified $element, using jquery.bt.js (BeautyTips) - * @param $element The element that should show the help when hovered - * @param templateName the name of the help template (without the '#template-help' prefix). Add to _help.html.erb - * @param data (optional) data for your template, if applicable - * @param options (optional) You can override the default BeautyTips options: https://github.com/dillon-sellars/BeautyTips - * - */ - context.JK.helpBubble = function($element, templateName, data, options) { - if(!data) { - data = {} - } - var helpText = context._.template($('#template-help-' + templateName).html(), data, { variable: 'data' }); + // FAN + $("[hoveraction='fan']").hoverIntent({ + over: function () { + var bubble = new JK.FanHoverBubble($(this).attr('user-id'), $(this).offset()); + showBubble(bubble, $(this)); + }, + out: function () { // this registers for leaving the hoverable element + hideBubble($(this)); + }, + sensitivity: 1 + }); - var holder = $('
'); - holder.append(helpText); + // BAND + $("[hoveraction='band']").hoverIntent({ + over: function () { + var bubble = new JK.BandHoverBubble($(this).attr('band-id'), $(this).offset()); + showBubble(bubble, $(this)); + }, + out: function () { // this registers for leaving the hoverable element + hideBubble($(this)); + }, + sensitivity: 1 + }); - context.JK.hoverBubble($element, helpText, options); + // SESSION + $("[hoveraction='session']").hoverIntent({ + over: function () { + var bubble = new JK.SessionHoverBubble($(this).attr('session-id'), $(this).offset()); + showBubble(bubble, $(this)); + }, + out: function () { // this registers for leaving the hoverable element + hideBubble($(this)); + }, + sensitivity: 1 + }); + + // RECORDING + $("[hoveraction='recording']").hoverIntent({ + over: function () { + var bubble = new JK.RecordingHoverBubble($(this).attr('recording-id'), $(this).offset()); + showBubble(bubble, $(this)); + }, + out: function () { // this registers for leaving the hoverable element + hideBubble($(this)); + }, + sensitivity: 1 + }); + } + + context.JK.fetchUserNetworkOrServerFailure = function () { + JK.app.notify({ + title: "Unable to communicate with server", + text: "Please try again later", + icon_url: "/assets/content/icon_alert_big.png" + }); + } + + context.JK.entityNotFound = function (type) { + JK.app.notify({ + title: type + " Deleted", + text: "The " + type + " no longer exists.", + icon_url: "/assets/content/icon_alert_big.png" + }); + } + + + // Uber-simple templating + // var template = "Hey {name}"; + // var vals = { name: "Jon" }; + // _fillTemplate(template, vals); + // --> "Hey Jon" + // + // use context._.template for something more powerful + context.JK.fillTemplate = function (template, vals) { + if (template == null) throw 'null template in fillTemplate' + for (var val in vals) + template = template.replace(new RegExp('{' + val + '}', 'g'), vals[val]); + return template; + }; + + context.JK.resolveAvatarUrl = function (photo_url) { + return photo_url ? photo_url : "/assets/shared/avatar_generic.png"; + }; + + context.JK.resolveBandAvatarUrl = function (photo_url) { + return photo_url ? photo_url : "/assets/shared/avatar_generic_band.png"; + }; + + context.JK.getInstrumentIconMap24 = function () { + return instrumentIconMap24; + }; + + context.JK.getInstrumentIconMap45 = function () { + return instrumentIconMap45; + }; + + context.JK.getInstrumentIcon24 = function (instrument) { + if (instrument in instrumentIconMap24) { + return instrumentIconMap24[instrument]; } - /** - * Associates a bubble on hover (by default) with the specified $element, using jquery.bt.js (BeautyTips) - * @param $element The element that should show the bubble when hovered - * @param text the text or jquery element that should be shown as contents of the bubble - * @param options (optional) You can override the default BeautyTips options: https://github.com/dillon-sellars/BeautyTips - */ - context.JK.hoverBubble = function($element, text, options) { - if(!text) { - logger.error("hoverBubble: no text to attach to $element %o", $element); - return; - } + return instrumentIconMap24["default"]; + }; - if($element instanceof jQuery) { - if ($element.length == 0) { - logger.error("hoverBubble: no element specified with text %o", text); - return; - } - } - - var defaultOpts = { - fill: '#333', - strokeStyle: '#ED3618', - spikeLength: 10, - spikeGirth: 10, - padding: 8, - cornerRadius: 0, - cssStyles: { - fontSize: '11px', - color:'white', - whiteSpace:'normal' - } - }; - - if(options) { - options = $.extend(false, defaultOpts, options); - } - else { - options = defaultOpts; - } - - $element.bt(text, options); + context.JK.getInstrumentIcon45 = function (instrument) { + if (instrument in instrumentIconMap45) { + return instrumentIconMap45[instrument]; } - context.JK.bindHoverEvents = function() { + return instrumentIconMap45["default"]; + }; - function showBubble(bubble, $hoverElement) { - $hoverElement.attr("bubble-id", bubble.id); - bubble.showBubble(); - } + // meant to pass in a bunch of images with an instrument-id attribute on them. - function hideBubble($hoverElement) { - var bubbleSelector = $hoverElement.attr("bubble-id"); - $(bubbleSelector).hover( - function() { - // do nothing when entering the bubble (this should never happen) - }, - function() { - $(this).fadeOut(500); - } - ); - } - - // MUSICIAN - $("[hoveraction='musician']").hoverIntent({ - over: function() { - var bubble = new JK.MusicianHoverBubble($(this).attr('user-id'), $(this).offset()); - showBubble(bubble, $(this)); - }, - out: function() { // this registers for leaving the hoverable element - hideBubble($(this)); - }, - sensitivity: 1 - }); - - // FAN - $("[hoveraction='fan']").hoverIntent({ - over: function() { - var bubble = new JK.FanHoverBubble($(this).attr('user-id'), $(this).offset()); - showBubble(bubble, $(this)); - }, - out: function() { // this registers for leaving the hoverable element - hideBubble($(this)); - }, - sensitivity: 1 - }); - - // BAND - $("[hoveraction='band']").hoverIntent({ - over: function() { - var bubble = new JK.BandHoverBubble($(this).attr('band-id'), $(this).offset()); - showBubble(bubble, $(this)); - }, - out: function() { // this registers for leaving the hoverable element - hideBubble($(this)); - }, - sensitivity: 1 - }); - - // SESSION - $("[hoveraction='session']").hoverIntent({ - over: function() { - var bubble = new JK.SessionHoverBubble($(this).attr('session-id'), $(this).offset()); - showBubble(bubble, $(this)); - }, - out: function() { // this registers for leaving the hoverable element - hideBubble($(this)); - }, - sensitivity: 1 - }); - - // RECORDING - $("[hoveraction='recording']").hoverIntent({ - over: function() { - var bubble = new JK.RecordingHoverBubble($(this).attr('recording-id'), $(this).offset()); - showBubble(bubble, $(this)); - }, - out: function() { // this registers for leaving the hoverable element - hideBubble($(this)); - }, - sensitivity: 1 - }); - } - - context.JK.fetchUserNetworkOrServerFailure = function() { - JK.app.notify({ - title: "Unable to communicate with server", - text: "Please try again later", - icon_url: "/assets/content/icon_alert_big.png" - }); - } - - context.JK.entityNotFound = function(type) { - JK.app.notify({ - title: type + " Deleted", - text: "The " + type + " no longer exists.", - icon_url: "/assets/content/icon_alert_big.png" - }); - } - - - // Uber-simple templating - // var template = "Hey {name}"; - // var vals = { name: "Jon" }; - // _fillTemplate(template, vals); - // --> "Hey Jon" - // - // use context._.template for something more powerful - context.JK.fillTemplate = function(template, vals) { - if(template == null) throw 'null template in fillTemplate' - for(var val in vals) - template=template.replace(new RegExp('{'+val+'}','g'), vals[val]); - return template; - }; - - context.JK.resolveAvatarUrl = function(photo_url) { - return photo_url ? photo_url : "/assets/shared/avatar_generic.png"; - }; - - context.JK.resolveBandAvatarUrl = function(photo_url) { - return photo_url ? photo_url : "/assets/shared/avatar_generic_band.png"; - }; - - context.JK.getInstrumentIconMap24 = function() { - return instrumentIconMap24; - }; - - context.JK.getInstrumentIconMap45 = function() { - return instrumentIconMap45; - }; - - context.JK.getInstrumentIcon24 = function(instrument) { - if (instrument in instrumentIconMap24) { - return instrumentIconMap24[instrument]; - } - - return instrumentIconMap24["default"]; - }; - - context.JK.getInstrumentIcon45 = function(instrument) { - if (instrument in instrumentIconMap45) { - return instrumentIconMap45[instrument]; - } - - return instrumentIconMap45["default"]; - }; - - // meant to pass in a bunch of images with an instrument-id attribute on them. - - context.JK.setInstrumentAssetPath = function($elements) { - $.each($elements, function(index, item) { - var $element = $(this); - if(!$element.is('img')) { throw "expected to receive an in setInstrumentAssetPath" } - - var instrument = $element.attr('instrument-id'); - - $element.attr('src', context.JK.getInstrumentIcon24(instrument)) - }) - } - - context.JK.listInstruments = function(app, callback) { - var url = "/api/instruments"; - $.ajax({ - type: "GET", - dataType: "json", - url: url, - success: function(response) { - callback(response); - }, - error: app.ajaxError - }); - }; - - context.JK.showErrorDialog = function(app, msg, title) { - app.layout.showDialog('error-dialog'); - $('#error-msg', 'div[layout-id="error-dialog"]').html(msg); - $('#error-summary', 'div[layout-id="error-dialog"]').html(title); - } - - // TODO: figure out how to handle this in layout.js for layered popups - context.JK.showOverlay = function() { - $('.dialog-overlay').show(); - } - - context.JK.hideOverlay = function() { - $('.dialog-overlay').hide(); - } - - /* - * Loads a listbox or dropdown with the values in input_array, setting the option value - * to the id_field and the option text to text_field. It will preselect the option with - * value equal to selected_id. - */ - context.JK.loadOptions = function(templateHtml, listbox_id, input_array, id_field, text_field, selected_id) { - $.each(input_array, function(index, val) { - var isSelected = ""; - if (val[id_field] === selected_id) { - isSelected = "selected"; - } - var html = context.JK.fillTemplate(templateHtml, { - value: val[id_field], - label: val[text_field], - selected: isSelected - }); - - listbox_id.append(html); - }); - } - - context.JK.trimString = function(str) { - return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; - - context.JK.padString = function(str, max) { - var retVal = '' + str; - while (retVal.length < max) { - retVal = '0' + retVal; - } - - return retVal; - } - - context.JK.formatDateTime = function(dateString) { - var date = new Date(dateString); - return context.JK.padString(date.getMonth()+1, 2) + "/" + context.JK.padString(date.getDate(), 2) + "/" + date.getFullYear() + " - " + date.toLocaleTimeString(); - } - - // returns Fri May 20, 2013 - context.JK.formatDate = function(dateString) { - var date = new Date(dateString); - - return days[date.getDay()] + ' ' + months[date.getMonth()] + ' ' + context.JK.padString(date.getDate(), 2) + ', ' + date.getFullYear(); - } - - context.JK.formatTime = function(dateString) { - var date = new Date(dateString); - return date.toLocaleTimeString(); - } - - context.JK.prettyPrintElements = function($elements) { - $.each($elements, function(index, item) { - var $item = $(item); - $item.text(context.JK.prettyPrintSeconds(parseInt($item.attr('duration')))) - }); - } - - context.JK.prettyPrintSeconds = function(seconds) { - // from: http://stackoverflow.com/questions/3733227/javascript-seconds-to-minutes-and-seconds - - // Minutes and seconds - var mins = ~~(seconds / 60); - var secs = seconds % 60; - - // Hours, minutes and seconds - var hrs = ~~(seconds / 3600); - var mins = ~~((seconds % 3600) / 60); - var secs = seconds % 60; - - // Output like "1:01" or "4:03:59" or "123:03:59" - var ret = ""; - - if (hrs > 0) - ret += "" + hrs + ":" + (mins < 10 ? "0" : ""); - - ret += "" + mins + ":" + (secs < 10 ? "0" : ""); - ret += "" + secs; - return ret; - } - - context.JK.search = function(query, app, callback) { - //logger.debug("search: "+ query) - $.ajax({ - type: "GET", - dataType: "json", - contentType: 'application/json', - url: "/api/search?query=" + query, - processData: false, - success: function(response) { - callback(response); - }, - error: app.ajaxError - }); - }; - - /* - * Get the length of a dictionary - */ - context.JK.dlen = function(d) { - var count = 0; - for (var i in d) { - if (d.hasOwnProperty(i)) { - count++; - } - } - return count; - }; - - /* - * Get the keys of a dictionary as an array (same as Object.keys, but works in all browsers) - */ - context.JK.dkeys = function(d) { - var keys = [] - for (var i in d) { - if (d.hasOwnProperty(i)) { - keys.push(i); - } - } - return keys; - }; - - /** - * Finds the first error associated with the field. - * @param fieldName the name of the field - * @param errors_data response from a 422 response in ajax - * @returns null if no error for the field name, or the 1st error associated with that field - */ - context.JK.get_first_error = function(fieldName, errors_data) { - var errors = errors_data["errors"]; - if(errors == null) return null; - - if(errors[fieldName] && errors[fieldName].length > 0) { - return errors[fieldName][0] - } - else { - return null; - } - } - - /** - * Returns a ul with an li per field name. - * @param fieldName the name of the field - * @param errors_data error data return by a 422 ajax response - * @returns null if no error for the field name; otherwise a ul/li - */ - context.JK.format_errors = function(fieldName, errors_data) { - var errors = errors_data["errors"]; - if(errors == null) return null; - - var field_errors = errors[fieldName]; - if(field_errors == null) { - return null; - } - - var ul = $(''); - - $.each(field_errors, function(index, item) { - ul.append(context.JK.fillTemplate("
  • {message}
  • ", {message: item})) - }) - - return ul; - } - - context.JK.format_all_errors = function(errors_data) { - var errors = errors_data["errors"]; - if(errors == null) return $(''); - - var ul = $(''); - - $.each(errors, function(fieldName, field_errors) { - - $.each(field_errors, function(index, item) { - ul.append(context.JK.fillTemplate("
  • {field} {message}
  • ", {field: fieldName, message: item})) - }); - }); - - return ul; - } - - - /** - * Way to verify that a number of parallel tasks have all completed. - * Provide a function to evaluate completion, and a callback to - * invoke when that function evaluates to true. - * NOTE: this does not pause execution, it simply ensures that - * when the test function evaluates to true, the callback will - * be invoked. - */ - context.JK.joinCalls = function(completionTestFunction, callback, interval) { - function doneYet() { - if (completionTestFunction()) { - callback(); - } else { - context.setTimeout(doneYet, interval); - } - } - doneYet(); - }; - - /** - * Returns 'MacOSX' if the os appears to be macintosh, - * 'Win32' if the os appears to be windows, - * 'Unix' if the OS appears to be linux, - * and null if unknown - * @returns {*} - */ - context.JK.detectOS = function() { - if(!navigator.platform) { return null; } - - if (navigator.platform.toLowerCase().indexOf( 'mac' ) !== -1) { - return "MacOSX"; - } - if (navigator.platform.toLowerCase().indexOf( 'win' ) !== -1 ) { - return "Win32" - } - if (navigator.platform.toLowerCase().indexOf('linux') !== -1 ) { - return "Unix" - } - - return null; - } - - context.JK.popExternalLinks = function() { - // Allow any a link with a rel="external" attribute to launch - // the link in the default browser, using jamClient: - $('body').on('click', 'a[rel="external"]', function(evt) { - - if(!context.jamClient) { - return; - } - - evt.preventDefault(); - var href = $(this).attr("href"); - if (href) { - // make absolute if not already - if(href.indexOf('http') != 0 && href.indexOf('mailto') != 0) { - href = window.location.protocol + '//' + window.location.host + href; - } - - context.jamClient.OpenSystemBrowser(href); - } - return false; - }); - } - - context.JK.popExternalLink = function(href) { - if(!context.jamClient) { - return; + context.JK.setInstrumentAssetPath = function ($elements) { + $.each($elements, function (index, item) { + var $element = $(this); + if (!$element.is('img')) { + throw "expected to receive an in setInstrumentAssetPath" } + var instrument = $element.attr('instrument-id'); + + $element.attr('src', context.JK.getInstrumentIcon24(instrument)) + }) + } + + context.JK.listInstruments = function (app, callback) { + var url = "/api/instruments"; + $.ajax({ + type: "GET", + dataType: "json", + url: url, + success: function (response) { + callback(response); + }, + error: app.ajaxError + }); + }; + + context.JK.showErrorDialog = function (app, msg, title) { + app.layout.showDialog('error-dialog'); + $('#error-msg', 'div[layout-id="error-dialog"]').html(msg); + $('#error-summary', 'div[layout-id="error-dialog"]').html(title); + } + + // TODO: figure out how to handle this in layout.js for layered popups + context.JK.showOverlay = function () { + $('.dialog-overlay').show(); + } + + context.JK.hideOverlay = function () { + $('.dialog-overlay').hide(); + } + + /* + * Loads a listbox or dropdown with the values in input_array, setting the option value + * to the id_field and the option text to text_field. It will preselect the option with + * value equal to selected_id. + */ + context.JK.loadOptions = function (templateHtml, listbox_id, input_array, id_field, text_field, selected_id) { + $.each(input_array, function (index, val) { + var isSelected = ""; + if (val[id_field] === selected_id) { + isSelected = "selected"; + } + var html = context.JK.fillTemplate(templateHtml, { + value: val[id_field], + label: val[text_field], + selected: isSelected + }); + + listbox_id.append(html); + }); + } + + context.JK.trimString = function (str) { + return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); + }; + + context.JK.padString = function (str, max) { + var retVal = '' + str; + while (retVal.length < max) { + retVal = '0' + retVal; + } + + return retVal; + } + + context.JK.formatDateTime = function (dateString) { + var date = new Date(dateString); + return context.JK.padString(date.getMonth() + 1, 2) + "/" + context.JK.padString(date.getDate(), 2) + "/" + date.getFullYear() + " - " + date.toLocaleTimeString(); + } + + // returns Fri May 20, 2013 + context.JK.formatDate = function (dateString) { + var date = new Date(dateString); + + return days[date.getDay()] + ' ' + months[date.getMonth()] + ' ' + context.JK.padString(date.getDate(), 2) + ', ' + date.getFullYear(); + } + + context.JK.formatTime = function (dateString) { + var date = new Date(dateString); + return date.toLocaleTimeString(); + } + + context.JK.prettyPrintElements = function ($elements) { + $.each($elements, function (index, item) { + var $item = $(item); + $item.text(context.JK.prettyPrintSeconds(parseInt($item.attr('duration')))) + }); + } + + context.JK.prettyPrintSeconds = function (seconds) { + // from: http://stackoverflow.com/questions/3733227/javascript-seconds-to-minutes-and-seconds + + // Minutes and seconds + var mins = ~~(seconds / 60); + var secs = seconds % 60; + + // Hours, minutes and seconds + var hrs = ~~(seconds / 3600); + var mins = ~~((seconds % 3600) / 60); + var secs = seconds % 60; + + // Output like "1:01" or "4:03:59" or "123:03:59" + var ret = ""; + + if (hrs > 0) + ret += "" + hrs + ":" + (mins < 10 ? "0" : ""); + + ret += "" + mins + ":" + (secs < 10 ? "0" : ""); + ret += "" + secs; + return ret; + } + + context.JK.search = function (query, app, callback) { + //logger.debug("search: "+ query) + $.ajax({ + type: "GET", + dataType: "json", + contentType: 'application/json', + url: "/api/search?query=" + query, + processData: false, + success: function (response) { + callback(response); + }, + error: app.ajaxError + }); + }; + + /* + * Get the length of a dictionary + */ + context.JK.dlen = function (d) { + var count = 0; + for (var i in d) { + if (d.hasOwnProperty(i)) { + count++; + } + } + return count; + }; + + /* + * Get the keys of a dictionary as an array (same as Object.keys, but works in all browsers) + */ + context.JK.dkeys = function (d) { + var keys = [] + for (var i in d) { + if (d.hasOwnProperty(i)) { + keys.push(i); + } + } + return keys; + }; + + /** + * Finds the first error associated with the field. + * @param fieldName the name of the field + * @param errors_data response from a 422 response in ajax + * @returns null if no error for the field name, or the 1st error associated with that field + */ + context.JK.get_first_error = function (fieldName, errors_data) { + var errors = errors_data["errors"]; + if (errors == null) return null; + + if (errors[fieldName] && errors[fieldName].length > 0) { + return errors[fieldName][0] + } + else { + return null; + } + } + + /** + * Returns a ul with an li per field name. + * @param fieldName the name of the field + * @param errors_data error data return by a 422 ajax response + * @returns null if no error for the field name; otherwise a ul/li + */ + context.JK.format_errors = function (fieldName, errors_data) { + var errors = errors_data["errors"]; + if (errors == null) return null; + + var field_errors = errors[fieldName]; + if (field_errors == null) { + return null; + } + + var ul = $(''); + + $.each(field_errors, function (index, item) { + ul.append(context.JK.fillTemplate("
  • {message}
  • ", {message: item})) + }) + + return ul; + } + + context.JK.format_all_errors = function (errors_data) { + var errors = errors_data["errors"]; + if (errors == null) return $(''); + + var ul = $(''); + + $.each(errors, function (fieldName, field_errors) { + + $.each(field_errors, function (index, item) { + ul.append(context.JK.fillTemplate("
  • {field} {message}
  • ", {field: fieldName, message: item})) + }); + }); + + return ul; + } + + + /** + * Way to verify that a number of parallel tasks have all completed. + * Provide a function to evaluate completion, and a callback to + * invoke when that function evaluates to true. + * NOTE: this does not pause execution, it simply ensures that + * when the test function evaluates to true, the callback will + * be invoked. + */ + context.JK.joinCalls = function (completionTestFunction, callback, interval) { + function doneYet() { + if (completionTestFunction()) { + callback(); + } else { + context.setTimeout(doneYet, interval); + } + } + + doneYet(); + }; + + /** + * Returns 'MacOSX' if the os appears to be macintosh, + * 'Win32' if the os appears to be windows, + * 'Unix' if the OS appears to be linux, + * and null if unknown + * @returns {*} + */ + context.JK.detectOS = function () { + if (!navigator.platform) { + return null; + } + + if (navigator.platform.toLowerCase().indexOf('mac') !== -1) { + return "MacOSX"; + } + if (navigator.platform.toLowerCase().indexOf('win') !== -1) { + return "Win32" + } + if (navigator.platform.toLowerCase().indexOf('linux') !== -1) { + return "Unix" + } + + return null; + } + + context.JK.popExternalLinks = function () { + // Allow any a link with a rel="external" attribute to launch + // the link in the default browser, using jamClient: + $('body').on('click', 'a[rel="external"]', function (evt) { + + if (!context.jamClient) { + return; + } + + evt.preventDefault(); + var href = $(this).attr("href"); if (href) { - // make absolute if not already - if(href.indexOf('http') != 0 && href.indexOf('mailto') != 0) { - href = window.location.protocol + '//' + window.location.host + href; - } + // make absolute if not already + if (href.indexOf('http') != 0 && href.indexOf('mailto') != 0) { + href = window.location.protocol + '//' + window.location.host + href; + } - context.jamClient.OpenSystemBrowser(href); + context.jamClient.OpenSystemBrowser(href); } return false; + }); + } + + context.JK.popExternalLink = function (href) { + if (!context.jamClient) { + return; + } + + if (href) { + // make absolute if not already + if (href.indexOf('http') != 0 && href.indexOf('mailto') != 0) { + href = window.location.protocol + '//' + window.location.host + href; + } + + context.jamClient.OpenSystemBrowser(href); + } + return false; + } + + context.JK.checkbox = function ($checkbox) { + $checkbox.iCheck({ + checkboxClass: 'icheckbox_minimal', + radioClass: 'iradio_minimal', + inheritClass: true + }); + } + + context.JK.dropdown = function ($select) { + + $select.each(function (index) { + var $item = $(this); + + if ($item.data('easydropdown-select')) { + // if this has already been initialized, re-init it so it picks up any new + $item.easyDropDown('destroy') + } + $item.easyDropDown({nativeTouch: !(context.jamClient && context.jamClient.IsNativeClient()) && gon.global.env != "test", cutOff: 7}); + }) + } + + context.JK.hasFlash = function () { + var hasFlash = false; + + if (!context.jamClient || !context.jamClient.IsNativeClient()) { + try { + var fo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash'); + if (fo) hasFlash = true; + } catch (e) { + if (navigator.mimeTypes ["application/x-shockwave-flash"] != undefined) hasFlash = true; + } + } + + return hasFlash; + } + + context.JK.hasOneConfiguredDevice = function() { + var result = context.jamClient.FTUEGetGoodConfigurationList(); + console.log("hasOneConfiguredDevice: ", result); + return result.length > 0; + } + + + /* + * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message + * Digest Algorithm, as defined in RFC 1321. + * Copyright (C) Paul Johnston 1999 - 2000. + * Updated by Greg Holt 2000 - 2001. + * See http://pajhome.org.uk/site/legal.html for details. + */ + + /* + * Convert a 32-bit number to a hex string with ls-byte first + */ + var hex_chr = "0123456789abcdef"; + + function rhex(num) { + var str, j; + str = ""; + for (j = 0; j <= 3; j++) { + str += hex_chr.charAt((num >> (j * 8 + 4)) & 0x0F) + + hex_chr.charAt((num >> (j * 8)) & 0x0F); + } + return str; + } + + /* + * Convert a string to a sequence of 16-word blocks, stored as an array. + * Append padding bits and the length, as described in the MD5 standard. + */ + function str2blks_MD5(str) { + var nblk, blks, i; + nblk = ((str.length + 8) >> 6) + 1; + blks = new Array(nblk * 16); + for (i = 0; i < nblk * 16; i++) blks[i] = 0; + for (i = 0; i < str.length; i++) + blks[i >> 2] |= str.charCodeAt(i) << ((i % 4) * 8); + blks[i >> 2] |= 0x80 << ((i % 4) * 8); + blks[nblk * 16 - 2] = str.length * 8; + return blks; + } + + /* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ + function add(x, y) { + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); + } + + /* + * Bitwise rotate a 32-bit number to the left + */ + function rol(num, cnt) { + return (num << cnt) | (num >>> (32 - cnt)); + } + + /* + * These functions implement the basic operation for each round of the + * algorithm. + */ + function cmn(q, a, b, x, s, t) { + return add(rol(add(add(a, q), add(x, t)), s), b); + } + + function ff(a, b, c, d, x, s, t) { + return cmn((b & c) | ((~b) & d), a, b, x, s, t); + } + + function gg(a, b, c, d, x, s, t) { + return cmn((b & d) | (c & (~d)), a, b, x, s, t); + } + + function hh(a, b, c, d, x, s, t) { + return cmn(b ^ c ^ d, a, b, x, s, t); + } + + function ii(a, b, c, d, x, s, t) { + return cmn(c ^ (b | (~d)), a, b, x, s, t); + } + + /* + * Take a string and return the hex representation of its MD5. + */ + context.JK.calcMD5 = function (str) { + var x, a, b, c, d, i, + olda, oldb, oldc, oldd; + x = str2blks_MD5(str); + a = 1732584193; + b = -271733879; + c = -1732584194; + d = 271733878; + + for (i = 0; i < x.length; i += 16) { + olda = a; + oldb = b; + oldc = c; + oldd = d; + + a = ff(a, b, c, d, x[i + 0], 7, -680876936); + d = ff(d, a, b, c, x[i + 1], 12, -389564586); + c = ff(c, d, a, b, x[i + 2], 17, 606105819); + b = ff(b, c, d, a, x[i + 3], 22, -1044525330); + a = ff(a, b, c, d, x[i + 4], 7, -176418897); + d = ff(d, a, b, c, x[i + 5], 12, 1200080426); + c = ff(c, d, a, b, x[i + 6], 17, -1473231341); + b = ff(b, c, d, a, x[i + 7], 22, -45705983); + a = ff(a, b, c, d, x[i + 8], 7, 1770035416); + d = ff(d, a, b, c, x[i + 9], 12, -1958414417); + c = ff(c, d, a, b, x[i + 10], 17, -42063); + b = ff(b, c, d, a, x[i + 11], 22, -1990404162); + a = ff(a, b, c, d, x[i + 12], 7, 1804603682); + d = ff(d, a, b, c, x[i + 13], 12, -40341101); + c = ff(c, d, a, b, x[i + 14], 17, -1502002290); + b = ff(b, c, d, a, x[i + 15], 22, 1236535329); + + a = gg(a, b, c, d, x[i + 1], 5, -165796510); + d = gg(d, a, b, c, x[i + 6], 9, -1069501632); + c = gg(c, d, a, b, x[i + 11], 14, 643717713); + b = gg(b, c, d, a, x[i + 0], 20, -373897302); + a = gg(a, b, c, d, x[i + 5], 5, -701558691); + d = gg(d, a, b, c, x[i + 10], 9, 38016083); + c = gg(c, d, a, b, x[i + 15], 14, -660478335); + b = gg(b, c, d, a, x[i + 4], 20, -405537848); + a = gg(a, b, c, d, x[i + 9], 5, 568446438); + d = gg(d, a, b, c, x[i + 14], 9, -1019803690); + c = gg(c, d, a, b, x[i + 3], 14, -187363961); + b = gg(b, c, d, a, x[i + 8], 20, 1163531501); + a = gg(a, b, c, d, x[i + 13], 5, -1444681467); + d = gg(d, a, b, c, x[i + 2], 9, -51403784); + c = gg(c, d, a, b, x[i + 7], 14, 1735328473); + b = gg(b, c, d, a, x[i + 12], 20, -1926607734); + + a = hh(a, b, c, d, x[i + 5], 4, -378558); + d = hh(d, a, b, c, x[i + 8], 11, -2022574463); + c = hh(c, d, a, b, x[i + 11], 16, 1839030562); + b = hh(b, c, d, a, x[i + 14], 23, -35309556); + a = hh(a, b, c, d, x[i + 1], 4, -1530992060); + d = hh(d, a, b, c, x[i + 4], 11, 1272893353); + c = hh(c, d, a, b, x[i + 7], 16, -155497632); + b = hh(b, c, d, a, x[i + 10], 23, -1094730640); + a = hh(a, b, c, d, x[i + 13], 4, 681279174); + d = hh(d, a, b, c, x[i + 0], 11, -358537222); + c = hh(c, d, a, b, x[i + 3], 16, -722521979); + b = hh(b, c, d, a, x[i + 6], 23, 76029189); + a = hh(a, b, c, d, x[i + 9], 4, -640364487); + d = hh(d, a, b, c, x[i + 12], 11, -421815835); + c = hh(c, d, a, b, x[i + 15], 16, 530742520); + b = hh(b, c, d, a, x[i + 2], 23, -995338651); + + a = ii(a, b, c, d, x[i + 0], 6, -198630844); + d = ii(d, a, b, c, x[i + 7], 10, 1126891415); + c = ii(c, d, a, b, x[i + 14], 15, -1416354905); + b = ii(b, c, d, a, x[i + 5], 21, -57434055); + a = ii(a, b, c, d, x[i + 12], 6, 1700485571); + d = ii(d, a, b, c, x[i + 3], 10, -1894986606); + c = ii(c, d, a, b, x[i + 10], 15, -1051523); + b = ii(b, c, d, a, x[i + 1], 21, -2054922799); + a = ii(a, b, c, d, x[i + 8], 6, 1873313359); + d = ii(d, a, b, c, x[i + 15], 10, -30611744); + c = ii(c, d, a, b, x[i + 6], 15, -1560198380); + b = ii(b, c, d, a, x[i + 13], 21, 1309151649); + a = ii(a, b, c, d, x[i + 4], 6, -145523070); + d = ii(d, a, b, c, x[i + 11], 10, -1120210379); + c = ii(c, d, a, b, x[i + 2], 15, 718787259); + b = ii(b, c, d, a, x[i + 9], 21, -343485551); + + a = add(a, olda); + b = add(b, oldb); + c = add(c, oldc); + d = add(d, oldd); + } + return rhex(a) + rhex(b) + rhex(c) + rhex(d); + }; + + /** validates that no changes are being made to tracks while recording */ + context.JK.verifyNotRecordingForTrackChange = function (app) { + if (context.JK.CurrentSessionModel.recordingModel.isRecording()) { + app.notify({ + title: "Currently Recording", + text: "Tracks cannot be modified while recording.", + icon_url: "/assets/content/icon_alert_big.png"}); + return false; } - context.JK.checkbox = function($checkbox) { - $checkbox.iCheck({ - checkboxClass: 'icheckbox_minimal', - radioClass: 'iradio_minimal', - inheritClass: true - }); - } - - context.JK.dropdown = function($select) { - - $select.each(function(index) { - var $item = $(this); - - if($item.data('easydropdown-select')) { - // if this has already been initialized, re-init it so it picks up any new - $item.easyDropDown('destroy') - } - $item.easyDropDown({nativeTouch: !(context.jamClient && context.jamClient.IsNativeClient()) && gon.global.env != "test", cutOff:7}); - }) - } - - context.JK.hasFlash = function() { - var hasFlash = false; - - if(!context.jamClient || !context.jamClient.IsNativeClient()) { - try { - var fo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash'); - if(fo) hasFlash = true; - }catch(e){ - if(navigator.mimeTypes ["application/x-shockwave-flash"] != undefined) hasFlash = true; - } - } - - return hasFlash; - - } - - /* - * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message - * Digest Algorithm, as defined in RFC 1321. - * Copyright (C) Paul Johnston 1999 - 2000. - * Updated by Greg Holt 2000 - 2001. - * See http://pajhome.org.uk/site/legal.html for details. - */ - - /* - * Convert a 32-bit number to a hex string with ls-byte first - */ - var hex_chr = "0123456789abcdef"; - function rhex(num) { - var str, j; - str = ""; - for(j=0; j<=3; j++) { - str += hex_chr.charAt((num >> (j * 8 + 4)) & 0x0F) + - hex_chr.charAt((num >> (j * 8)) & 0x0F); - } - return str; - } - - /* - * Convert a string to a sequence of 16-word blocks, stored as an array. - * Append padding bits and the length, as described in the MD5 standard. - */ - function str2blks_MD5(str) { - var nblk, blks, i; - nblk = ((str.length + 8) >> 6) + 1; - blks = new Array(nblk * 16); - for(i = 0; i < nblk * 16; i++) blks[i] = 0; - for(i = 0; i < str.length; i++) - blks[i >> 2] |= str.charCodeAt(i) << ((i % 4) * 8); - blks[i >> 2] |= 0x80 << ((i % 4) * 8); - blks[nblk * 16 - 2] = str.length * 8; - return blks; - } - - /* - * Add integers, wrapping at 2^32. This uses 16-bit operations internally - * to work around bugs in some JS interpreters. - */ - function add(x, y) - { - var lsw = (x & 0xFFFF) + (y & 0xFFFF); - var msw = (x >> 16) + (y >> 16) + (lsw >> 16); - return (msw << 16) | (lsw & 0xFFFF); - } - - /* - * Bitwise rotate a 32-bit number to the left - */ - function rol(num, cnt) - { - return (num << cnt) | (num >>> (32 - cnt)); - } - - /* - * These functions implement the basic operation for each round of the - * algorithm. - */ - function cmn(q, a, b, x, s, t) - { - return add(rol(add(add(a, q), add(x, t)), s), b); - } - function ff(a, b, c, d, x, s, t) - { - return cmn((b & c) | ((~b) & d), a, b, x, s, t); - } - function gg(a, b, c, d, x, s, t) - { - return cmn((b & d) | (c & (~d)), a, b, x, s, t); - } - function hh(a, b, c, d, x, s, t) - { - return cmn(b ^ c ^ d, a, b, x, s, t); - } - function ii(a, b, c, d, x, s, t) - { - return cmn(c ^ (b | (~d)), a, b, x, s, t); - } - - /* - * Take a string and return the hex representation of its MD5. - */ - context.JK.calcMD5 = function(str) { - var x, a, b, c, d, i, - olda, oldb, oldc, oldd; - x = str2blks_MD5(str); - a = 1732584193; - b = -271733879; - c = -1732584194; - d = 271733878; - - for(i = 0; i < x.length; i += 16) - { - olda = a; - oldb = b; - oldc = c; - oldd = d; - - a = ff(a, b, c, d, x[i+ 0], 7 , -680876936); - d = ff(d, a, b, c, x[i+ 1], 12, -389564586); - c = ff(c, d, a, b, x[i+ 2], 17, 606105819); - b = ff(b, c, d, a, x[i+ 3], 22, -1044525330); - a = ff(a, b, c, d, x[i+ 4], 7 , -176418897); - d = ff(d, a, b, c, x[i+ 5], 12, 1200080426); - c = ff(c, d, a, b, x[i+ 6], 17, -1473231341); - b = ff(b, c, d, a, x[i+ 7], 22, -45705983); - a = ff(a, b, c, d, x[i+ 8], 7 , 1770035416); - d = ff(d, a, b, c, x[i+ 9], 12, -1958414417); - c = ff(c, d, a, b, x[i+10], 17, -42063); - b = ff(b, c, d, a, x[i+11], 22, -1990404162); - a = ff(a, b, c, d, x[i+12], 7 , 1804603682); - d = ff(d, a, b, c, x[i+13], 12, -40341101); - c = ff(c, d, a, b, x[i+14], 17, -1502002290); - b = ff(b, c, d, a, x[i+15], 22, 1236535329); - - a = gg(a, b, c, d, x[i+ 1], 5 , -165796510); - d = gg(d, a, b, c, x[i+ 6], 9 , -1069501632); - c = gg(c, d, a, b, x[i+11], 14, 643717713); - b = gg(b, c, d, a, x[i+ 0], 20, -373897302); - a = gg(a, b, c, d, x[i+ 5], 5 , -701558691); - d = gg(d, a, b, c, x[i+10], 9 , 38016083); - c = gg(c, d, a, b, x[i+15], 14, -660478335); - b = gg(b, c, d, a, x[i+ 4], 20, -405537848); - a = gg(a, b, c, d, x[i+ 9], 5 , 568446438); - d = gg(d, a, b, c, x[i+14], 9 , -1019803690); - c = gg(c, d, a, b, x[i+ 3], 14, -187363961); - b = gg(b, c, d, a, x[i+ 8], 20, 1163531501); - a = gg(a, b, c, d, x[i+13], 5 , -1444681467); - d = gg(d, a, b, c, x[i+ 2], 9 , -51403784); - c = gg(c, d, a, b, x[i+ 7], 14, 1735328473); - b = gg(b, c, d, a, x[i+12], 20, -1926607734); - - a = hh(a, b, c, d, x[i+ 5], 4 , -378558); - d = hh(d, a, b, c, x[i+ 8], 11, -2022574463); - c = hh(c, d, a, b, x[i+11], 16, 1839030562); - b = hh(b, c, d, a, x[i+14], 23, -35309556); - a = hh(a, b, c, d, x[i+ 1], 4 , -1530992060); - d = hh(d, a, b, c, x[i+ 4], 11, 1272893353); - c = hh(c, d, a, b, x[i+ 7], 16, -155497632); - b = hh(b, c, d, a, x[i+10], 23, -1094730640); - a = hh(a, b, c, d, x[i+13], 4 , 681279174); - d = hh(d, a, b, c, x[i+ 0], 11, -358537222); - c = hh(c, d, a, b, x[i+ 3], 16, -722521979); - b = hh(b, c, d, a, x[i+ 6], 23, 76029189); - a = hh(a, b, c, d, x[i+ 9], 4 , -640364487); - d = hh(d, a, b, c, x[i+12], 11, -421815835); - c = hh(c, d, a, b, x[i+15], 16, 530742520); - b = hh(b, c, d, a, x[i+ 2], 23, -995338651); - - a = ii(a, b, c, d, x[i+ 0], 6 , -198630844); - d = ii(d, a, b, c, x[i+ 7], 10, 1126891415); - c = ii(c, d, a, b, x[i+14], 15, -1416354905); - b = ii(b, c, d, a, x[i+ 5], 21, -57434055); - a = ii(a, b, c, d, x[i+12], 6 , 1700485571); - d = ii(d, a, b, c, x[i+ 3], 10, -1894986606); - c = ii(c, d, a, b, x[i+10], 15, -1051523); - b = ii(b, c, d, a, x[i+ 1], 21, -2054922799); - a = ii(a, b, c, d, x[i+ 8], 6 , 1873313359); - d = ii(d, a, b, c, x[i+15], 10, -30611744); - c = ii(c, d, a, b, x[i+ 6], 15, -1560198380); - b = ii(b, c, d, a, x[i+13], 21, 1309151649); - a = ii(a, b, c, d, x[i+ 4], 6 , -145523070); - d = ii(d, a, b, c, x[i+11], 10, -1120210379); - c = ii(c, d, a, b, x[i+ 2], 15, 718787259); - b = ii(b, c, d, a, x[i+ 9], 21, -343485551); - - a = add(a, olda); - b = add(b, oldb); - c = add(c, oldc); - d = add(d, oldd); - } - return rhex(a) + rhex(b) + rhex(c) + rhex(d); - }; - - /** validates that no changes are being made to tracks while recording */ - context.JK.verifyNotRecordingForTrackChange = function(app) { - if(context.JK.CurrentSessionModel.recordingModel.isRecording()) { - app.notify({ - title: "Currently Recording", - text: "Tracks cannot be modified while recording.", - icon_url: "/assets/content/icon_alert_big.png"}); - return false; - } - - return true; - } + return true; + } -})(window,jQuery); \ No newline at end of file +})(window, jQuery); \ No newline at end of file diff --git a/web/lib/music_session_manager.rb b/web/lib/music_session_manager.rb index 4d0f1061d..e13fb417e 100644 --- a/web/lib/music_session_manager.rb +++ b/web/lib/music_session_manager.rb @@ -141,10 +141,12 @@ MusicSessionManager < BaseManager raise PermissionError, "you do not own this connection" end + recordingId = nil ConnectionManager.new.leave_music_session(user, connection, music_session) do recording = music_session.stop_recording # stop any ongoing recording, if there is one recordingId = recording.id unless recording.nil? - Notification.send_session_depart(music_session, connection.client_id, user, recordingId) end + + Notification.send_session_depart(music_session, connection.client_id, user, recordingId) end end diff --git a/websocket-gateway/lib/jam_websockets/router.rb b/websocket-gateway/lib/jam_websockets/router.rb index 7e32be0b9..d41eb05e2 100644 --- a/websocket-gateway/lib/jam_websockets/router.rb +++ b/websocket-gateway/lib/jam_websockets/router.rb @@ -343,6 +343,10 @@ module JamWebsockets client_context = @client_lookup[cid] self.cleanup_client(client_context.client) unless client_context.nil? + music_session = nil + recordingId = nil + user = nil + # remove this connection from the database ConnectionManager.active_record_transaction do |mgr| mgr.delete_connection(cid) { |conn, count, music_session_id, user_id| @@ -351,9 +355,10 @@ module JamWebsockets user = User.find_by_id(user_id) unless user_id.nil? recording = music_session.stop_recording unless music_session.nil? # stop any ongoing recording, if there is one recordingId = recording.id unless recording.nil? - Notification.send_session_depart(music_session, cid, user, recordingId) unless music_session.nil? || user.nil? } end + + Notification.send_session_depart(music_session, cid, user, recordingId) unless music_session.nil? || user.nil? end end @@ -475,6 +480,10 @@ module JamWebsockets music_session_upon_reentry = connection.music_session + send_depart = false + recordingId = nil + context = nil + ConnectionManager.active_record_transaction do |connection_manager| music_session_id, reconnected = connection_manager.reconnect(connection, reconnect_music_session_id, remote_ip) context = @client_lookup[client_id] @@ -484,7 +493,7 @@ module JamWebsockets unless context.nil? || music_session_upon_reentry.nil? || music_session_upon_reentry.destroyed? recording = music_session_upon_reentry.stop_recording recordingId = recording.id unless recording.nil? - Notification.send_session_depart(music_session_upon_reentry, client.client_id, context.user, recordingId) + send_depart = true end else music_session = MusicSession.find_by_id(music_session_id) @@ -492,6 +501,10 @@ module JamWebsockets end end if connection.stale? + + if send_depart + Notification.send_session_depart(music_session_upon_reentry, client.client_id, context.user, recordingId) + end end # respond with LOGIN_ACK to let client know it was successful