/**
* Common utility functions.
*/
(function (context, $) {
"use strict";
context.JK = context.JK || {};
var logger = context.JK.logger;
var AUDIO_DEVICE_BEHAVIOR = context.JK.AUDIO_DEVICE_BEHAVIOR;
var ALERT_NAMES = context.JK.ALERT_NAMES;
var ALERT_TYPES = context.JK.ALERT_TYPES;
var EVENTS = context.JK.EVENTS;
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 os = null;
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": "ukulele",
"upright bass": "upright_bass",
"viola": "viola",
"violin": "violin",
"voice": "voice"
};
var instrumentIconMap24 = {};
var instrumentIconMap45 = {};
var instrumentIconMap256 = {};
var notSpecifiedText = "Not specified";
$.each(icon_map_base, function (instrumentId, icon) {
instrumentIconMap24[instrumentId] = {asset: "/assets/content/icon_instrument_" + icon + "24.png", name: instrumentId};
instrumentIconMap45[instrumentId] = {asset: "/assets/content/icon_instrument_" + icon + "45.png", name: instrumentId};
instrumentIconMap256[instrumentId] = {asset: "/assets/content/icon_instrument_" + icon + "256.png", name: instrumentId};
});
context.JK.helpBubbleFunctionHelper = function(originalFunc) {
var helpText = originalFunc.apply(this);
var holder = $('
');
holder.append(helpText);
return holder;
}
/**
* 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.slim
* @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 = {};
}
if(!options) {
options = {};
}
$element.on('remove', function() {
$element.btOff(); // if the element goes away for some reason, get rid of the bubble too
})
var holder = null;
if (context._.isFunction(templateName)) {
holder = function wrapper() {
return context.JK.helpBubbleFunctionHelper.apply(this, [templateName]);
}
}
else {
var $template = $('#template-help-' + templateName)
if($template.length == 0) {
var helpText = templateName;
}
else {
var helpText = context._.template($template.html(), data, { variable: 'data' });
}
holder = $('');
holder = holder.append(helpText).html()
}
context.JK.hoverBubble($element, holder, options);
}
/**
* Meant to show a little bubble to confirm something happened, in a way less obtrusive than a app.notify
* @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.slim
* @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.ackBubble = function($element, templateName, data, options) {
if(!options) options = {};
options.spikeGirth = 0;
options.spikeLength = 0;
options.shrinkToFit = true
options.duration = 3000
options.cornerRadius = 5
return context.JK.prodBubble($element, templateName, data, options);
}
/**
* Associates a help bubble immediately with the specified $element, using jquery.bt.js (BeautyTips)
* By 'prod' it means to literally prod the user, to make them aware of something important because they did something else
*
* This will open a bubble immediately and show it for 4 seconds,
* if you call it again before the 4 second timer is up, it will renew the 4 second timer.
* @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.slim
* @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.prodBubble = function($element, templateName, data, options) {
if(!options) options = {};
options['trigger'] = 'none';
options['clickAnywhereToClose'] = false
if(!options['duration']) options['duration'] = 6000;
var existingTimer = $element.data("prodTimer");
if(existingTimer) {
clearTimeout(existingTimer);
$element.btOn();
}
else {
context.JK.helpBubble($element, templateName, data, options);
$element.btOn();
}
$element.data("prodTimer", setTimeout(function() {
$element.data("prodTimer", null);
$element.btOff();
}, options['duration']));
$element.on('remove', function() {
var timer = $element.data('prodTimer')
if(timer) {
clearTimeout(timer);
$element.data("prodTimer", null);
$element.btOff();
}
})
return $element;
}
/**
* 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;
}
$element.on('remove', function() {
$element.btOff();
})
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: '#cccccc',
whiteSpace: 'normal'
}
};
if (options) {
options = $.extend(false, defaultOpts, options);
}
else {
options = defaultOpts;
}
if(typeof text != 'string') {
options.contentSelector = text;
$element.bt(options);
}
else {
$element.bt(text, options);
}
}
context.JK.bindProfileClickEvents = function($parent, dialogsToClose) {
if (!$parent) {
$parent = $('body');
}
function closeDialogs() {
if (dialogsToClose) {
$.each(dialogsToClose, function(index, val) {
JK.app.layout.closeDialog(val);
});
}
}
$("[profileaction='band']", $parent).unbind('click');
$("[profileaction='band']", $parent).click(function(evt) {
closeDialogs();
window.location = "/client#/bandProfile/" + $(this).attr('band-id');
});
$("[profileaction='musician']", $parent).unbind('click');
$("[profileaction='musician']", $parent).click(function(evt) {
closeDialogs();
window.location = "/client#/profile/" + $(this).attr('user-id');
});
}
context.JK.bindInstrumentHover = function($parent, options) {
context._.each($parent.find("[hoveraction='instrument']"), function(element) {
var $element = $(element);
var instrumentId = $element.attr('data-instrument-id');
if(instrumentId) {
if (instrumentId === "null") {
instrumentId = "not specified";
}
context.JK.hoverBubble($element, instrumentId, options);
}
else {
logger.warn("no instrument-id on hoveraction=instrument element")
}
});
}
context.JK.calculateHoverPosition = function(mouseX, mouseY, hoverWidth, hoverHeight, $hoverElement) {
var mouseLeft = mouseX < (window.innerWidth / 2);
var mouseTop = mouseY < (window.innerHeight / 2);
var hoverElementX = $hoverElement.offset().left;
var hoverElementY = $hoverElement.offset().top;
var hoverElementWidth = $hoverElement.width();
var hoverElementHeight = $hoverElement.height();
var css = {};
if (mouseLeft) {
css.left = hoverElementX + hoverElementWidth + 5 + 'px';
}
else {
css.left = hoverElementX - hoverWidth - 5 + 'px';
}
if (mouseTop) {
css.top = hoverElementY + 'px';
}
else {
css.top = hoverElementY - hoverHeight + 'px';
}
return css;
}
context.JK.bindHoverEvents = function ($parent) {
var timeout = 300;
var fadeoutValue = 100;
var sensitivity = 3;
var interval = 500;
if (!$parent) {
$parent = $('body');
}
function showBubble(bubble, $hoverElement) {
$hoverElement.attr("bubble-id", bubble.id);
bubble.showBubble($hoverElement)
.done(function() {
$(bubble.id()).hover(
function () {
$(this).data('hovering', true)
// do nothing when entering the bubble
},
function () {
$(this).data('hovering', false).fadeOut(100);
}
);
})
}
function hideBubble($hoverElement) {
var bubbleSelector = $hoverElement.attr("bubble-id");
// first check to see if the user isn't hovering over the hover bubble
if (!$(bubbleSelector).data('hovering')) {
$(bubbleSelector).fadeOut(fadeoutValue);
}
}
// MUSICIAN
$("[hoveraction='musician']", $parent).hoverIntent({
over: function(e) {
var bubble = new JK.MusicianHoverBubble($(this).attr('user-id'), e.pageX, e.pageY);
showBubble(bubble, $(this));
},
out: function () { // this registers for leaving the hoverable element
hideBubble($(this));
},
sensitivity: sensitivity,
interval: interval,
timeout: timeout
});
// FAN
$("[hoveraction='fan']", $parent).hoverIntent({
over: function(e) {
var bubble = new JK.FanHoverBubble($(this).attr('user-id'), e.pageX, e.pageY);
showBubble(bubble, $(this));
},
out: function () { // this registers for leaving the hoverable element
hideBubble($(this));
},
sensitivity: sensitivity,
interval: interval,
timeout: timeout
});
// BAND
$("[hoveraction='band']", $parent).hoverIntent({
over: function(e) {
var bubble = new JK.BandHoverBubble($(this).attr('band-id'), e.pageX, e.pageY);
showBubble(bubble, $(this));
},
out: function () { // this registers for leaving the hoverable element
hideBubble($(this));
},
sensitivity: sensitivity,
interval: interval,
timeout: timeout
});
// SESSION
$("[hoveraction='session']", $parent).hoverIntent({
over: function(e) {
var bubble = new JK.SessionHoverBubble($(this).attr('session-id'), e.pageX, e.pageY);
showBubble(bubble, $(this));
},
out: function () { // this registers for leaving the hoverable element
hideBubble($(this));
},
sensitivity: sensitivity,
interval: interval,
timeout: timeout
});
// RECORDING
$("[hoveraction='recording']", $parent).hoverIntent({
over: function(e) {
var bubble = new JK.RecordingHoverBubble($(this).attr('recording-id'), e.pageX, e.pageY);
showBubble(bubble, $(this));
},
out: function () { // this registers for leaving the hoverable element
hideBubble($(this));
},
sensitivity: sensitivity,
interval: interval,
timeout: timeout
});
}
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"
});
}
// http://stackoverflow.com/questions/4878756/javascript-how-to-capitalize-first-letter-of-each-word-like-a-2-word-city
context.JK.toTitleCase = function(str) {
return str.replace(/\w\S*/g, function(txt){
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
}
// 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.getInstrumentIconMap256 = function () {
return instrumentIconMap256;
};
context.JK.getInstrumentIcon24 = function (instrument) {
if (instrument in instrumentIconMap24) {
return instrumentIconMap24[instrument].asset;
}
return instrumentIconMap24["_default"].asset;
};
context.JK.getInstrumentIcon45 = function (instrument) {
if (instrument in instrumentIconMap45) {
return instrumentIconMap45[instrument].asset;
}
return instrumentIconMap45["_default"].asset;
};
context.JK.getInstrumentIcon256 = function (instrument) {
if (instrument in instrumentIconMap256) {
return instrumentIconMap256[instrument].asset;
}
return instrumentIconMap256["_default"].asset;
};
context.JK.getInstrumentId = function(instrumentId) {
return instrumentId ? instrumentId : notSpecifiedText;
}
// 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))
})
}
// creates an array with entries like [{ id: "drums", description: "Drums"}, ]
context.JK.listInstruments = function () {
var instrumentArray = [];
$.each(context.JK.server_to_client_instrument_map, function (key, val) {
instrumentArray.push({"id": context.JK.server_to_client_instrument_map[key].client_id, "description": key});
});
return instrumentArray;
}
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();
}
// usage: context.JK.onBackendEvent(ALERT_NAMES.SOME_EVENT)
context.JK.onBackendEvent = function(type, namespace, callback) {
var alertData = ALERT_TYPES[type];
if(!alertData) {throw "onBackendEvent: unknown alert type " + type};
logger.debug("onBackendEvent: " + alertData.name + '.' + namespace)
$(document).on(alertData.name + '.' + namespace, callback);
}
context.JK.offBackendEvent = function(type, namespace, callback) {
var alertData = ALERT_TYPES[type];
if(!alertData) {throw "offBackendEvent: unknown alert type " + type};
logger.debug("offBackendEvent: " + alertData.name + '.' + namespace, alertData)
$(document).off(alertData.name + '.' + namespace);
}
/*
* 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 = dateString instanceof Date ? dateString : 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.formatDateYYYYMMDD = function(dateString) {
var date = new Date(dateString);
return date.getFullYear() + '-' + context.JK.padString((date.getMonth() + 1).toString(), 2) + '-' + context.JK.padString(date.getDate(), 2);
}
context.JK.formatTime = function (dateString) {
var date = new Date(dateString);
return date.toLocaleTimeString();
}
context.JK.formatUtcTime = function(date, change) {
if (change) {
date.setMinutes(Math.ceil(date.getMinutes() / 30) * 30);
}
var h12h = date.getHours();
var m12h = date.getMinutes();
var ampm;
if (h12h >= 0 && h12h < 12) {
if (h12h === 0) {
h12h = 12; // 0 becomes 12
}
ampm = "AM";
}
else {
if (h12h > 12) {
h12h -= 12; // 13-23 becomes 1-11
}
ampm = "PM";
}
var timeString = ("00" + h12h).slice(-2) + ":" + ("00" + m12h).slice(-2) + " " + ampm;
return timeString;
}
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.alertSupportedNeeded = function(additionalContext) {
var $item = context.JK.Banner.showAlert(additionalContext + '
Please contact support.');
context.JK.popExternalLinks($item);
return $item;
}
// returns:
// * Win32
// * MacOSX
// * Unix
context.JK.GetOSAsString = function() {
if(!os) {
if(context.jamClient.IsNativeClient()) {
os = context.jamClient.GetOSAsString();
}
else {
os = context.JK.detectOS();
}
}
return os;
}
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();
};
context.JK.initJamClient = function(app) {
// If no jamClient (when not running in native client)
// create a fake one.
if (!(window.jamClient)) {
var p2pMessageFactory = new JK.FakeJamClientMessages();
window.jamClient = new JK.FakeJamClient(app, p2pMessageFactory);
window.jamClient.SetFakeRecordingImpl(new JK.FakeJamClientRecordings(app, jamClient, p2pMessageFactory));
}
else if(false) { // set to true to time long running bridge calls
var originalJamClient = window.jamClient;
var interceptedJamClient = {};
$.each(Object.keys(originalJamClient), function(i, key) {
if(key.indexOf('(') > -1) {
// this is a method. time it
var jsKey = key.substring(0, key.indexOf('('))
console.log("replacing " + jsKey)
interceptedJamClient[jsKey] = function() {
var original = originalJamClient[key]
var start = new Date();
if(key == "FTUEGetDevices()") {
var returnVal = eval('originalJamClient.FTUEGetDevices(' + arguments[0] + ')');
}
else {
var returnVal = original.apply(originalJamClient, arguments);
}
var time = new Date().getTime() - start.getTime();
if(time >= 0) { // if 0, you'll see ALL bridge calls. If you set it to a higher value, you'll only see calls that are beyond that threshold
console.error(time + "ms jamClient." + jsKey + ' returns=', returnVal);
}
return returnVal;
}
}
else {
// we need to intercept properties... but how?
}
});
window.jamClient = interceptedJamClient;
}
}
// pass in 'arguments' in a fail callback of a $.ajax
context.JK.isNetworkError = function(failArgs) {
if(failArgs.length != 3) throw "expected 3 arguments from .fail of $ajax in isNetworkError"
var xhr = failArgs[0];
return xhr.status == 0;
}
context.JK.clientType = function () {
if (context.jamClient) {
return context.jamClient.IsNativeClient() ? 'client' : 'browser';
}
return 'browser';
}
/**
* 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.makeAbsolute = function(path) {
return window.location.protocol + '//' + window.location.host + path;
}
context.JK.popExternalLinks = function ($parent) {
if(!$parent) $parent = $('body');
// Allow any a link with a rel="external" attribute to launch
// the link in the default browser, using jamClient:
$parent.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;
}
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) {
return $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.getNameOfFile = function(filename) {
var index = filename.lastIndexOf('/');
if(index == -1) {
index = filename.lastIndexOf('\\');
}
return index == -1 ? filename : filename.substring(index + 1, filename.length)
}
context.JK.hasOneConfiguredDevice = function () {
var result = context.jamClient.FTUEGetGoodConfigurationList();
logger.debug("hasOneConfiguredDevice: ", result);
return result.length > 0;
};
context.JK.getGoodAudioConfigs = function () {
var result = context.jamClient.FTUEGetGoodAudioConfigurations();
logger.debug("goodAudioConfigs=%o", result);
return result;
};
context.JK.getGoodConfigMap = function () {
var goodConfigMap = [];
var goodConfigs = context.JK.getGoodAudioConfigs();
$.each(goodConfigs, function (index, profileKey) {
var friendlyName = context.jamClient.FTUEGetConfigurationDevice(profileKey);
goodConfigMap.push({key: profileKey, name: friendlyName});
});
return goodConfigMap;
}
context.JK.getBadAudioConfigs = function () {
var badAudioConfigs = [];
var allAudioConfigs = context.jamClient.FTUEGetAllAudioConfigurations();
var goodAudioConfigs = context.JK.getGoodAudioConfigs();
for (var i = 0; i < allAudioConfigs.length; i++) {
if ($.inArray(allAudioConfigs[i], goodAudioConfigs) === -1) {
badAudioConfigs.push(allAudioConfigs[i]);
}
}
logger.debug("badAudioConfigs=%o", badAudioConfigs);
return badAudioConfigs;
};
context.JK.getBadConfigMap = function () {
var badConfigMap = [];
var badConfigs = context.JK.getBadAudioConfigs();
$.each(badConfigs, function (index, profileKey) {
var friendlyName = context.jamClient.FTUEGetConfigurationDevice(profileKey);
badConfigMap.push({key: profileKey, name: friendlyName});
});
return badConfigMap;
}
context.JK.getFirstGoodDevice = function (preferredDeviceId) {
var badConfigs = context.JK.getBadAudioConfigs();
function getGoodDevice() {
var goodConfigs = context.JK.getGoodAudioConfigs();
if (goodConfigs && goodConfigs.length > 0) {
deviceId = goodConfigs[0];
}
else {
deviceId = null;
}
return deviceId;
}
var deviceId = null;
if (preferredDeviceId) {
// if the preferred device is not in the list of invalid configs, use it
if ($.inArray(preferredDeviceId, badConfigs) === -1) {
deviceId = preferredDeviceId;
}
else {
deviceId = getGoodDevice();
}
}
else {
deviceId = getGoodDevice();
}
return deviceId;
}
// returns /client#/home for http://www.jamkazam.com/client#/home
context.JK.locationPath = function () {
var bits = context.location.href.split('/');
return '/' + bits.slice(3).join('/');
}
context.JK.nowUTC = function () {
var d = new Date();
return new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds());
}
context.JK.guardAgainstBrowser = function(app, args) {
if(!gon.isNativeClient) {
logger.debug("guarding against normal browser on screen thaht requires native client")
app.layout.showDialog('launch-app-dialog', args)
.one(EVENTS.DIALOG_CLOSED, function() {
if(args && args.goHome) {
window.location = '/client#/home';
}
})
return false;
}
return true;
}
/*
* 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;
}
})(window, jQuery);