jam-cloud/web/app/assets/javascripts/globals.js

450 lines
22 KiB
JavaScript

/**
* Static, simple definitions like strings, hashes, enums
*/
(function (context, $) {
"use strict";
context.JK = context.JK || {};
var logger = context.JK.logger;
context.JK.isQWebEngine = !!navigator.userAgent.match("(JamKazam)")
context.JK.MIDI_TRACK = 100
context.JK.CLIENT_ROLE = {
CHILD: 0,
PARENT: 1
}
context.JK.OS = {
WIN32: "Win32",
OSX: "MacOSX",
UNIX: "Unix"
};
context.JK.ASSIGNMENT = {
CHAT: -2,
OUTPUT: -1,
UNASSIGNED: 0,
TRACK1: 1,
TRACK2: 2
};
context.JK.VOICE_CHAT = {
NO_CHAT: "0",
CHAT: "1"
};
context.JK.AUDIO_FORMATS = ['.mp3', '.ogg', '.wav', '.flac', '.au'];
context.JK.VIDEO_FORMATS = ['.flv', '.mp4', '.mov', '.mkv', '.ts', '.m3u8'];
context.JK.AUDIO_STORE_TYPE_MIX_AND_STEMS = {
key: 'session_mix_and_steams',
value: 'Session mix + individual parts (streams)',
backendValues: [2, 3]
};
context.JK.AUDIO_STORE_TYPE_MIX_ONLY = {
key: 'session_mix_only',
value: 'Session mix only',
backendValues: [1]
};
context.JK.RECORD_TYPE_AUDIO = 'audio-only'
context.JK.RECORD_TYPE_BOTH = 'audio-video'
context.JK.AVAILABILITY_US = "United States";
context.JK.MASTER_TRACK = "Master";
context.JK.EVENTS = {
DIALOG_CLOSED: 'dialog_closed',
SHOW_SIGNUP: 'show_signup',
SHOW_SIGNIN: 'show_signin',
RSVP_SUBMITTED: 'rsvp_submitted',
RSVP_CANCELED: 'rsvp_canceled',
USER_UPDATED: 'user_updated',
SESSION_STARTED: 'session_started',
SESSION_ENDED: 'session_stopped',
FILE_MANAGER_CMD_START: 'file_manager_cmd_start',
FILE_MANAGER_CMD_STOP: 'file_manager_cmd_stop',
FILE_MANAGER_CMD_PROGRESS: 'file_manager_cmd_progress',
FILE_MANAGER_CMD_ASAP_UPDATE: 'file_manager_cmd_asap_update',
MIXER_MODE_CHANGED: 'mixer_mode_changed',
MUTE_SELECTED: 'mute_selected',
SUBSCRIBE_NOTIFICATION: 'subscribe_notification',
CONNECTION_UP: 'connection_up',
CONNECTION_DOWN: 'connection_down',
SCREEN_CHANGED: 'screen_changed',
JAMTRACK_DOWNLOADER_STATE_CHANGED: 'jamtrack_downloader_state',
METRONOME_PLAYBACK_MODE_SELECTED: 'metronome_playback_mode_selected',
CHECKOUT_SIGNED_IN: 'checkout_signed_in',
CHECKOUT_SKIP_SIGN_IN: 'checkout_skip_sign_in',
PREVIEW_PLAYED: 'preview_played',
VST_OPERATION_SELECTED: 'vst_operation_selected',
VST_EFFECT_SELECTED: 'vst_effect_selected',
LESSON_SESSION_ACTION: 'lesson_session_action',
JAMBLASTER_ACTION: 'jamblaster_action'
};
context.JK.PLAYBACK_MONITOR_MODE = {
MEDIA_FILE: 'MEDIA_FILE',
JAMTRACK: 'JAMTRACK',
METRONOME: 'METRONOME',
BROWSER_MEDIA: 'BROWSER_MEDIA'
}
context.JK.ALERT_NAMES = {
NO_EVENT: 0,
BACKEND_ERROR: 1, //generic error - eg P2P message error
BACKEND_MIXER_CHANGE: 2, //event that controls have been regenerated
//network related
PACKET_JTR: 3,
PACKET_LOSS: 4,
PACKET_LATE: 5,
JTR_QUEUE_DEPTH: 6,
NETWORK_JTR: 7,
NETWORK_PING: 8,
BITRATE_THROTTLE_WARN: 9,
BANDWIDTH_LOW: 10,
//IO related events
INPUT_IO_RATE: 11,
INPUT_IO_JTR: 12,
OUTPUT_IO_RATE: 13,
OUTPUT_IO_JTR: 14,
// CPU load related
CPU_LOAD: 15,
DECODE_VIOLATIONS: 16,
LAST_THRESHOLD: 17,
WIFI_NETWORK_ALERT: 18, //user or peer is using wifi
NO_VALID_AUDIO_CONFIG: 19, // alert the user to popup a config
AUDIO_DEVICE_NOT_PRESENT: 20, // the audio device is not connected
RECORD_PLAYBACK_STATE: 21, // record/playback events have occurred
RUN_UPDATE_CHECK_BACKGROUND: 22, //this is auto check - do
RUN_UPDATE_CHECK_INTERACTIVE: 23, //this is initiated by user
STUN_EVENT: 24, // system completed stun test... come get the result
DEAD_USER_WARN_EVENT: 25, //the backend is not receiving audio from this peer
DEAD_USER_REMOVE_EVENT: 26, //the backend is removing the user from session as no audio is coming from this peer
WINDOW_CLOSE_BACKGROUND_MODE: 27, //the user has closed the window and the client is now in background mode
WINDOW_OPEN_FOREGROUND_MODE: 28, //the user has opened the window and the client is now in forground mode/
SESSION_LIVEBROADCAST_FAIL: 29, //error of some sort - so can't broadcast
SESSION_LIVEBROADCAST_ACTIVE: 30, //active
SESSION_LIVEBROADCAST_STOPPED: 31, //stopped by server/user
SESSION_LIVEBROADCAST_PINNED: 32, //node pinned by user
SESSION_LIVEBROADCAST_UNPINNED: 33, //node unpinned by user
BACKEND_STATUS_MSG: 34, //status/informational message
LOCAL_NETWORK_VARIANCE_HIGH: 35,//the ping time via a hairpin for the user network is unnaturally high or variable.
//indicates problem with user computer stack or network itself (wifi, antivirus etc)
LOCAL_NETWORK_LATENCY_HIGH: 36,
RECORDING_CLOSE: 37, //update and remove tracks from front-end
PEER_REPORTS_NO_AUDIO_RECV: 38, //letting front-end know audio is not being received from a user in session
SHOW_PREFERENCES: 39, // tell frontend to show preferences dialog
USB_CONNECTED: 40, // tell frontend that a USB device was connected
USB_DISCONNECTED: 41, // tell frontend that a USB device was disconnected
JAM_TRACK_SERVER_ERROR: 42, //error talking with server
BAD_INTERVAL_RATE: 43, //the audio gear is calling back at rate that does not match the expected interval
FIRST_AUDIO_PACKET: 44,// we are receiving audio from peer
NETWORK_PORT_MANGLED: 45, // packet from peer indicates network port is being mangled
NO_GLOBAL_CLOCK_SERVER: 46, //can't reach global clock NTP server
GLOBAL_CLOCK_SYNCED: 47, //clock synced
RECORDING_DONE: 48, //the recording writer thread is done
VIDEO_WINDOW_OPENED: 49, //video window opened
VIDEO_WINDOW_CLOSED: 50,
VST_CHANGED: 51, // VST state changed
SAMPLERATE_CONFIGURATION_BAD: 52,
SHOW_NETWORK_TEST: 53,
LAST_ALERT: 54
}
// recreate eThresholdType enum from MixerDialog.h
context.JK.ALERT_TYPES = {
0: { "title": "", "message": "" }, // NO_EVENT,
1: { "title": "", "message": "" }, // BACKEND_ERROR: generic error - eg P2P message error
2: { "title": "", "message": "" }, // BACKEND_MIXER_CHANGE, - event that controls have been regenerated
3: { "title": "High Packet Jitter", "message": "Your network connection is currently experiencing packet jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // PACKET_JTR,
4: { "title": "High Packet Loss", "message": "Your network connection is currently experiencing packet loss at a rate that is too high to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // PACKET_LOSS
5: { "title": "High Packet Late", "message": "Your network connection is currently experiencing packet loss at a rate that is too high to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // PACKET_LATE,
6: { "title": "Large Jitter Queue", "message": "Your network connection is currently experiencing packet jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // JTR_QUEUE_DEPTH,
7: { "title": "High Network Jitter", "message": "Your network connection is currently experiencing network jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // NETWORK_JTR,
8: { "title": "High Session Latency", "message": "The latency of your audio device combined with your Internet connection has become high enough to impact your session quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // NETWORK_PING,
9: { "title": "Bandwidth Throttled", "message": "The available bandwidth on your network has diminished, and this may impact your audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // BITRATE_THROTTLE_WARN,
10: { "title": "Low Bandwidth", "message": "The available bandwidth on your network has become too low, and this may impact your audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // BANDWIDTH_LOW
// IO related events
11: { "title": "Variable Input Rate", "message": "The input rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // INPUT_IO_RATE
12: { "title": "High Input Jitter", "message": "The input rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // INPUT_IO_JTR,
13: { "title": "Variable Output Rate", "message": "The output rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // OUTPUT_IO_RATE
14: { "title": "High Output Jitter", "message": "The output rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // OUTPUT_IO_JTR,
// CPU load related
15: { "title": "CPU Utilization High", "message": "The CPU of your computer is unable to keep up with the current processing load, and this may impact your audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // CPU_LOAD
16: { "title": "Decode Violations", "message": "" }, // DECODE_VIOLATIONS,
17: { "title": "", "message": "" }, // LAST_THRESHOLD
18: { "title": "Wifi Alert", "message": "" }, // WIFI_NETWORK_ALERT, //user or peer is using wifi
19: { "title": "No Audio Configuration", "message": "You cannot join the session because you do not have a valid audio configuration." }, // NO_VALID_AUDIO_CONFIG,
20: { "title": "", "message": "" }, // AUDIO_DEVICE_NOT_PRESENT, // the audio device is not connected
//20: {"title": "Audio Device Not Present", "message": ""}, // AUDIO_DEVICE_NOT_PRESENT, // the audio device is not connected
21: { "title": "", "message": "" }, // RECORD_PLAYBACK_STATE, // record/playback events have occurred
22: { "title": "", "message": "" }, // RUN_UPDATE_CHECK_BACKGROUND, //this is auto check - do
23: { "title": "", "message": "" }, // RUN_UPDATE_CHECK_INTERACTIVE, //this is initiated by user
24: { "title": "", "message": "" }, // STUN_EVENT, // system completed stun test... come get the result
25: { "title": "No Audio", "message": "Your system is no longer transmitting audio. Other session members are unable to hear you." }, // DEAD_USER_WARN_EVENT, //the backend is not receiving audio from this peer
26: { "title": "No Audio", "message": "Your system is no longer transmitting audio. Other session members are unable to hear you." }, // DEAD_USER_REMOVE_EVENT, //the backend is removing the user from session as no audio is coming from this peer
27: { "title": "", "message": "" }, // WINDOW_CLOSE_BACKGROUND_MODE, //the user has closed the window and the client is now in background mode
28: { "title": "", "message": "" }, // WINDOW_OPEN_FOREGROUND_MODE, //the user has opened the window and the client is now in forground mode/
29: { "title": "Failed to Broadcast", "message": "" }, // SESSION_LIVEBROADCAST_FAIL, //error of some sort - so can't broadcast
30: { "title": "", "message": "" }, // SESSION_LIVEBROADCAST_ACTIVE, //active
31: { "title": "", "message": "" }, // SESSION_LIVEBROADCAST_STOPPED, //stopped by server/user
32: { "title": "Client Pinned", "message": "This client will be the source of a broadcast." }, // SESSION_LIVEBROADCAST_PINNED, //node pinned by user
33: { "title": "Client No Longer Pinned", "message": "This client is no longer designated as the source of the broadcast." }, // SESSION_LIVEBROADCAST_UNPINNED, //node unpinned by user
34: { "title": "", "message": "" }, // BACKEND_STATUS_MSG, //status/informational message
35: { "title": "LAN Unpredictable", "message": "Your local network is adding considerable variance to transmit times. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // LOCAL_NETWORK_VARIANCE_HIGH,//the ping time via a hairpin for the user network is unnaturally high or variable.
//indicates problem with user computer stack or network itself (wifi, antivirus etc)
36: { "title": "LAN High Latency", "message": "Your local network is adding considerable latency. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // LOCAL_NETWORK_LATENCY_HIGH,
37: { "title": "", "message": "" }, // RECORDING_CLOSE, //update and remove tracks from front-end
38: { "title": "No Audio Sent", "message": "" }, // PEER_REPORTS_NO_AUDIO_RECV, //update and remove tracks from front-end
39: { "title": "", "message": "" }, // SHOW_PREFERENCES, //show preferences dialog
40: { "title": "", "message": "" }, // USB_CONNECTED
41: { "title": "", "message": "" }, // USB_DISCONNECTED, // tell frontend that a USB device was disconnected
42: { "title": "", "message": "" }, // JAM_TRACK_SERVER_ERROR
43: { "title": "", "message": "" }, // BAD_INTERVAL_RATE
44: { "title": "", "message": "" }, // FIRST_AUDIO_PACKET
45: { "title": "", "message": "" }, // NETWORK_PORT_MANGLED
46: { "title": "", "message": "" }, // NO_GLOBAL_CLOCK_SERVER
47: { "title": "", "message": "" }, // GLOBAL_CLOCK_SYNCED
48: { "title": "", "message": "" }, // RECORDING_DONE
49: { "title": "", "message": "" }, // VIDEO_WINDOW_OPENED
50: { "title": "", "message": "" }, // VIDEO_WINDOW_CLOSED
51: { "title": "", "message": "" }, // VST_CHANGED
52: { "title": "", "message": "" }, // SAMPLERATE_CONFIGURATION_BAD
53: { "title": "", "message": "" }, // SHOW_NETWORK_TEST
54: { "title": "", "message": "" } // LAST_ALERT
};
// add the alert's name to the ALERT_TYPES structure
context._.each(context.JK.ALERT_NAMES, function (alert_code, alert_name) {
var alert_data = context.JK.ALERT_TYPES[alert_code];
alert_data.name = alert_name;
})
context.JK.MAX_TRACKS = 6;
context.JK.MAX_OUTPUTS = 2;
// TODO: store these client_id values in instruments table, or store
// server_id as the client_id to prevent maintenance nightmares. As it's
// set up now, we will have to deploy each time we add new instruments.
context.JK.server_to_client_instrument_map = {
"Acoustic Guitar": { "client_id": 10, "server_id": "acoustic guitar" },
"Bass Guitar": { "client_id": 20, "server_id": "bass guitar" },
"Computer": { "client_id": 30, "server_id": "computer" },
"Drums": { "client_id": 40, "server_id": "drums" },
"Percussion": { "client_id": 41, "server_id": "percussion" },
"Electric Guitar": { "client_id": 50, "server_id": "electric guitar" },
"Keyboard": { "client_id": 60, "server_id": "keyboard" },
"Piano": { "client_id": 61, "server_id": "piano" },
"Double Bass": { "client_id": 62, "server_id": "double bass" },
"Voice": { "client_id": 70, "server_id": "voice" },
"Flute": { "client_id": 80, "server_id": "flute" },
"Clarinet": { "client_id": 90, "server_id": "clarinet" },
"Saxophone": { "client_id": 100, "server_id": "saxophone" },
"Trumpet": { "client_id": 110, "server_id": "trumpet" },
"Violin": { "client_id": 120, "server_id": "violin" },
"Trombone": { "client_id": 130, "server_id": "trombone" },
"Banjo": { "client_id": 140, "server_id": "banjo" },
"Harmonica": { "client_id": 150, "server_id": "harmonica" },
"Accordion": { "client_id": 160, "server_id": "accordion" },
"French Horn": { "client_id": 170, "server_id": "french horn" },
"Euphonium": { "client_id": 180, "server_id": "euphonium" },
"Tuba": { "client_id": 190, "server_id": "tuba" },
"Oboe": { "client_id": 200, "server_id": "oboe" },
"Ukulele": { "client_id": 210, "server_id": "ukulele" },
"Cello": { "client_id": 220, "server_id": "cello" },
"Viola": { "client_id": 230, "server_id": "viola" },
"Mandolin": { "client_id": 240, "server_id": "mandolin" },
"Other": { "client_id": 250, "server_id": "other" }
};
context.JK.client_to_server_instrument_map = {
10: { "server_id": "acoustic guitar" },
20: { "server_id": "bass guitar" },
30: { "server_id": "computer" },
40: { "server_id": "drums" },
41: { "server_id": "percussion" },
50: { "server_id": "electric guitar" },
60: { "server_id": "keyboard" },
61: { "server_id": "piano" },
62: { "server_id": "double bass" },
70: { "server_id": "voice" },
80: { "server_id": "flute" },
90: { "server_id": "clarinet" },
100: { "server_id": "saxophone" },
110: { "server_id": "trumpet" },
120: { "server_id": "violin" },
130: { "server_id": "trombone" },
140: { "server_id": "banjo" },
150: { "server_id": "harmonica" },
160: { "server_id": "accordion" },
170: { "server_id": "french horn" },
180: { "server_id": "euphonium" },
190: { "server_id": "tuba" },
200: { "server_id": "oboe" },
210: { "server_id": "ukulele" },
220: { "server_id": "cello" },
230: { "server_id": "viola" },
240: { "server_id": "mandolin" },
250: { "server_id": "other" }
};
context.JK.instrument_id_to_instrument = {};
context.JK.server_to_client_instrument_alpha = [];
(function () {
$.each(context.JK.server_to_client_instrument_map, function (key, value) {
context.JK.instrument_id_to_instrument[value.server_id] = { client_id: value.client_id, display: key }
context.JK.server_to_client_instrument_alpha.push({ client_id: value.client_id, display: key, server_id: value.server_id })
});
context.JK.server_to_client_instrument_alpha.sort(function (a, b) {
if (a.display < b.display)
return -1;
if (a.display > b.display)
return 1;
return 0;
});
})();
context.JK.entityToPrintable = {
music_session: "music session",
slot: "Requested time"
}
context.JK.AUDIO_DEVICE_BEHAVIOR = {
MacOSX_builtin: {
display: 'MacOSX Built-In',
shortName: 'Built-In',
videoURL: "https://www.youtube.com/watch?v=7-9PW50ygHk",
showKnobs: true,
showASIO: false
},
MacOSX_interface: {
display: 'MacOSX external interface',
shortName: 'External',
videoURL: "https://www.youtube.com/watch?v=7BLld6ogm14",
showKnobs: true,
showASIO: false
},
Win32_wdm: {
display: 'Windows WDM',
shortName: 'WDM',
videoURL: "https://www.youtube.com/watch?v=L36UBkAV14c",
showKnobs: true,
showASIO: false
},
Win32_asio: {
display: 'Windows ASIO',
shortName: 'ASIO',
videoURL: "https://www.youtube.com/watch?v=PGUmISTVVMY",
showKnobs: true,
showASIO: true
},
Win32_asio4all: {
display: 'Windows ASIO4ALL',
shortName: 'ASIO4ALL',
videoURL: "https://www.youtube.com/watch?v=PGUmISTVVMY",
showKnobs: true,
showASIO: true
},
Linux: {
display: 'Linux',
shortName: 'linux',
videoURL: undefined,
showKnobs: true,
showASIO: false
}
}
context.JK.MIX_MODES = {
MASTER: true,
PERSONAL: false
}
/** NAMED_MESSAGES means messages that we show to the user (dialogs/banners/whatever), that we have formally named */
context.JK.NAMED_MESSAGES = {
MASTER_VS_PERSONAL_MIX: 'master_vs_personal_mix',
HOWTO_USE_VIDEO_NOSHOW: 'how-to-use-video',
CONFIGURE_VIDEO_NOSHOW: 'configure-video',
TEACHER_MUSICIAN_PROFILE: 'teacher-musician-profile'
}
context.JK.ALERT_MESSAGES = {
OBS_UNAVAILABLE: 'To make a video recording in JamKazam, you must first install and configure OBS software. Click the link below for a help article that explains how to do this. <a href="" target="_blank" title="Install OBS">View Help Article</a>'
}
context.JK.ChannelGroupIds = {
"MasterGroup": 0,
"MonitorGroup": 1,
"MasterCatGroup": 2,
"MonitorCatGroup": 3,
"AudioInputMusicGroup": 4,
"AudioInputChatGroup": 5,
"MediaTrackGroup": 6,
"StreamOutMusicGroup": 7,
"StreamOutChatGroup": 8,
"StreamOutMediaGroup": 9,
"UserMusicInputGroup": 10,
"UserChatInputGroup": 11,
"UserMediaInputGroup": 12,
"PeerAudioInputMusicGroup": 13,
"PeerMediaTrackGroup": 14,
"JamTrackGroup": 15,
"MetronomeGroup": 16,
"MidiInputMusicGroup": 17,
"PeerMidiInputMusicGroup": 18,
"UsbInputMusicGroup": 19,
"PeerUsbInputMusicGroup": 20
};
context.JK.ChannelGroupLookup = {
0: "MasterGroup",
1: "MonitorGroup",
2: "MasterCatGroup",
3: "MonitorCatGroup",
4: "AudioInputMusicGroup",
5: "AudioInputChatGroup",
6: "MediaTrackGroup",
7: "StreamOutMusicGroup",
8: "StreamOutChatGroup",
9: "StreamOutMediaGroup",
10: "UserMusicInputGroup",
11: "UserChatInputGroup",
12: "UserMediaInputGroup",
13: "PeerAudioInputMusicGroup",
14: "PeerMediaTrackGroup",
15: "JamTrackGroup",
16: "MetronomeGroup",
17: "MidiInputMusicGroup",
18: "PeerMidiInputMusicGroup"
}
context.JK.CategoryGroupIds = {
"AudioInputMusic": "AudioInputMusic",
"AudioInputChat": "AudioInputChat",
"UserMusic": "UserMusic",
"UserChat": "UserChat",
"UserMedia": "UserMedia",
"MediaTrack": "MediaTrack",
"Metronome": "Metronome"
}
})(window, jQuery);