/** * Common utility functions. */ (function (context, $) { "use strict"; context.JK = context.JK || {}; var logger = context.JK.logger; 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"); 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 ($parent) { if(!$parent) { $parent = $('body'); } 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); } ); } // MUSICIAN $("[hoveraction='musician']", $parent).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']", $parent).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']", $parent).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']", $parent).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']", $parent).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)) }) } // 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(); } /* * 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; } 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; }; context.JK.getGoodAudioConfigs = function() { var result = context.jamClient.FTUEGetGoodAudioConfigurations(); console.log("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]); } } console.log("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; } context.JK.nowUTC = function() { var d = new Date(); return new Date( d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds() ); } /* * 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);