/** * Functions related to faders (slider controls for volume). * These functions are intimately tied to the markup defined in * the templates in _faders.html.erb */ (function (g, $) { "use strict"; g.JK = g.JK || {}; var $draggingFaderHandle = null; var $draggingFader = null; var $floater = null; var draggingOrientation = null; var logger = g.JK.logger; function faderClick(e) { e.stopPropagation(); var $fader = $(this); var floaterConvert = $fader.data('floaterConverter') var sessionModel = window.JK.CurrentSessionModel || null; /** if(!$fader.data('has-session-control')) { var sessionControllerName = $fader.data('session-controller-name'); window.JK.prodBubble($fader, 'not-session-controller', {sessionControllerName:sessionControllerName}, {positions:['left', 'right'], offsetParent: $fader.closest('.top-parent'), duration:12000}) return false; }*/ var mediaControlsDisabled = $fader.data('media-controls-disabled'); if(mediaControlsDisabled) { var mediaTrackOpener = $fader.data('media-track-opener'); window.JK.prodBubble($fader, 'media-controls-disabled', {mediaTrackOpener:mediaTrackOpener}, {positions:['top'], offsetParent: $fader.closest('.screen')}) return false; } if(sessionModel && sessionModel.areControlsLockedForJamTrackRecording() && $control.closest('.session-track').data('track_data').type == 'jam_track') { window.JK.prodBubble($fader, 'jamtrack-controls-disabled', {}, {positions:['top'], offsetParent: $fader.closest('.screen')}) return false; } if($fader.data('showHelpAboutMediaMixers')) { if(sessionModel) { if(!sessionModel.hasShownAudioMediaMixerHelp()) { window.JK.prodBubble($fader, 'volume-media-mixers', {}, {positions:['top'], offsetParent: $fader.closest('.screen')}) sessionModel.markShownAudioMediaMixerHelp() } } } draggingOrientation = $fader.attr('data-orientation'); var offset = $fader.offset(); var position = { top: e.pageY - offset.top, left: e.pageX - offset.left} var faderPct = faderValue($fader, e, position); if (faderPct < 0 || faderPct > 100) { return false; } if(floaterConvert) { window.JK.FaderHelpers.setFloaterValue($fader.find('.floater'), floaterConvert(faderPct)) } $fader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: false}) setHandlePosition($fader, faderPct); return false; } function setHandlePosition($fader, value) { var ratio, position; var $handle = $fader.find('div[data-control="fader-handle"]'); var orientation = $fader.attr('data-orientation'); var handleCssAttribute = getHandleCssAttribute($fader); // required because this method is entered directly when from a callback if (draggingOrientation === "horizontal") { ratio = value / 100; position = ((ratio * $fader.width()) - (ratio * handleWidth(draggingOrientation))) + 'px'; } else { ratio = (100 - value) / 100; position = ((ratio * $fader.height()) - (ratio * handleWidth(draggingOrientation))) + 'px'; } $handle.css(handleCssAttribute, position); } function faderValue($fader, e, offset) { var orientation = $fader.attr('data-orientation'); var getPercentFunction = getVerticalFaderPercent; var relativePosition = offset.top; if (orientation && orientation == 'horizontal') { getPercentFunction = getHorizontalFaderPercent; relativePosition = offset.left; } return getPercentFunction(relativePosition, $fader); } function getHandleCssAttribute($fader) { var orientation = $fader.attr('data-orientation'); return (orientation === 'horizontal') ? 'left' : 'top'; } function getVerticalFaderPercent(eventY, $fader) { return getFaderPercent(eventY, $fader, 'vertical'); } function getHorizontalFaderPercent(eventX, $fader) { return getFaderPercent(eventX, $fader, 'horizontal'); } /** * Returns the current value of the fader as int percent 0-100 */ function getFaderPercent(value, $fader, orientation) { var faderSize, faderPct; // the handle takes up room, and all calculations use top. So when the // handle *looks* like it's at the bottom by the user, it won't give a 0% value. // so, we subtract handleWidth from the size of it's parent if (orientation === "horizontal") { faderSize = $fader.width(); faderPct = Math.round(( value + (value / faderSize * handleWidth(orientation))) / faderSize * 100); } else { faderSize = $fader.height(); faderPct = Math.round((faderSize - handleWidth(orientation) - value) / (faderSize - handleWidth(orientation)) * 100); } return faderPct; } function onFaderDrag(e, ui) { var faderPct = faderValue($draggingFader, e, ui.position); // protect against attempts to drag outside of the slider, which jquery.draggable sometimes allows if (faderPct < 0 || faderPct > 100) { return false; } // simple snap feature to stick to the mid point if(faderPct > 46 && faderPct < 54 && $draggingFader.data('snap')) { faderPct = 50 var orientation = $draggingFader.attr('data-orientation'); if(orientation == 'horizontal') { var width = $draggingFader.width() var left = width / 2 ui.position.left = left } else { var height = $draggingFader.height() var top = height / 2 ui.position.top = top } } var floaterConvert = $draggingFaderHandle.data('floaterConverter') if(floaterConvert && $floater) { window.JK.FaderHelpers.setFloaterValue($floater, floaterConvert(faderPct)) } $draggingFader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: true}) } function onFaderDragStart(e, ui) { $draggingFaderHandle = $(this); $draggingFader = $draggingFaderHandle.closest('div[data-control="fader"]'); $floater = $draggingFaderHandle.find('.floater') draggingOrientation = $draggingFader.attr('orientation'); var mediaControlsDisabled = $draggingFaderHandle.data('media-controls-disabled'); var mediaTrackOpener = $draggingFaderHandle.data('media-track-opener'); var sessionModel = window.JK.CurrentSessionModel || null; /** if(!$draggingFaderHandle.data('has-session-control')) { var sessionControllerName = $draggingFaderHandle.data('session-controller-name'); window.JK.prodBubble($draggingFaderHandle, 'not-session-controller', {sessionControllerName:sessionControllerName}, {positions:['left', 'right'], offsetParent: $draggingFaderHandle.closest('.top-parent'), duration:12000}) return false; }*/ if(mediaControlsDisabled) { return false; } if(sessionModel && sessionModel.areControlsLockedForJamTrackRecording() && $draggingFaderHandle.closest('.session-track').data('track_data').type == 'jam_track') { return false; } return true; } function onFaderDragStop(e, ui) { if($draggingFader.data('showHelpAboutMediaMixers')) { if(window.JK.CurrentSessionModel) { if(!window.JK.CurrentSessionModel.hasShownAudioMediaMixerHelp()) { window.JK.prodBubble($draggingFader, 'volume-media-mixers', {}, {positions:['bottom'], offsetParent: $draggingFader.closest('.screen')}) window.JK.CurrentSessionModel.markShownAudioMediaMixerHelp() } } } var faderPct = faderValue($draggingFader, e, ui.position); // protect against attempts to drag outside of the slider, which jquery.draggable sometimes allows // do not return 'false' though, because that stops future drags from working, for some reason if (faderPct < 0 || faderPct > 100) { return; } $draggingFader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: false}); $draggingFaderHandle = null; $draggingFader = null; draggingOrientation = null; } function handleWidth(orientation) { return orientation === "horizontal" ? 8 : 11; } g.JK.FaderHelpers = { /** * Render a fader into the element identifed by the provided * selector, with the provided options. */ renderFader: function (selector, userOptions) { selector = $(selector); if (userOptions === undefined) { throw ("renderFader: userOptions is required"); } var renderDefaults = { faderType: "vertical", height: 83, // only used for vertical width: 83 // only used for horizontal }; var options = $.extend({}, renderDefaults, userOptions); var templateSelector = (options.faderType === 'horizontal') ? "#template-fader-h" : '#template-fader-v'; var templateSource = $(templateSelector).html(); selector.html(g._.template(templateSource, options)); selector.find('div[data-control="fader"]') .data('media-controls-disabled', selector.data('media-controls-disabled')) .data('media-track-opener', selector.data('media-track-opener')) .data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers')) selector.find('div[data-control="fader-handle"]').draggable({ drag: onFaderDrag, start: onFaderDragStart, stop: onFaderDragStop, containment: "parent", axis: options.faderType === 'horizontal' ? 'x' : 'y' }).data('media-controls-disabled', selector.data('media-controls-disabled')) .data('media-track-opener', selector.data('media-track-opener')) .data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers')) // Embed any custom styles, applied to the .fader below selector if ("style" in options) { for (var key in options.style) { selector.find(' .fader').css(key, options.style[key]); } } }, renderFader2: function (selector, userOptions, floaterConverter) { selector = $(selector); if (userOptions === undefined) { throw ("renderFader: userOptions is required"); } var renderDefaults = { faderType: "vertical", sessionController: null }; var options = $.extend({}, renderDefaults, userOptions); var sessionCanControl = true var sessionControllerName = null if(userOptions.sessionController) { if(!userOptions.sessionController.can_control) { sessionCanControl = false sessionControllerName = userOptions.sessionController.session_controller.name } } selector.find('div[data-control="fader"]') .data('media-controls-disabled', selector.data('media-controls-disabled')) .data('media-track-opener', selector.data('media-track-opener')) .data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers')) .data('floaterConverter', floaterConverter) .data('snap', userOptions.snap) .data('has-session-control', sessionCanControl) .data('session-controller-name', sessionControllerName) if(userOptions.sessionController) { } selector.find('div[data-control="fader-handle"]').draggable({ drag: onFaderDrag, start: onFaderDragStart, stop: onFaderDragStop, containment: "parent", axis: options.faderType === 'horizontal' ? 'x' : 'y' }).data('media-controls-disabled', selector.data('media-controls-disabled')) .data('media-track-opener', selector.data('media-track-opener')) .data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers')) .data('floaterConverter', floaterConverter) .data('snap', userOptions.snap) .data('has-session-control', sessionCanControl) .data('session-controller-name', sessionControllerName) // Embed any custom styles, applied to the .fader below selector if ("style" in options) { for (var key in options.style) { selector.find(' .fader').css(key, options.style[key]); } } }, convertLinearToDb: function (input) { // deal with extremes better if (input <= 1) { return -80; } if (input >= 99) { return 20; } var temp; temp = 0.0; // coefficients var a = -8.0013990435159329E+01; var b = 4.1755639785242042E+00; var c = -1.4036729740086906E-01; var d = 3.1788545454166156E-03; var f = -3.5148685730880861E-05; var g = 1.4221429222004657E-07; temp += a + b * input + c * Math.pow(input, 2.0) + d * Math.pow(input, 3.0) + f * Math.pow(input, 4.0) + g * Math.pow(input, 5.0); return temp; }, convertPercentToAudioTaper: function (input) { // composite function resembling audio taper if (input <= 1) { return -80; } if (input <= 28) { return Math.round((2 * input - 80)); } // -78 to -24 db if (input <= 79) { return Math.round((0.5 * input - 38)); } // -24 to 1.5 db if (input < 99) { return Math.round((0.875 * input - 67.5)); } // 1.625 - 19.125 db if (input >= 99) { return 20; } }, convertAudioTaperToPercent: function(db) { if(db <= -78) { return 0} if(db <= -24) { return (db + 80) / 2 } if(db <= 1.5) { return (db + 38) / .5 } if(db <= 19.125) { return (db + 67.5) / 0.875 } return 100; }, setFaderValue: function (faderId, faderValue, floaterValue) { var $fader = $('[data-fader-id="' + faderId + '"]'); this.setHandlePosition($fader, faderValue); if(floaterValue !== undefined) { var $floater = $fader.find('.floater') this.setFloaterValue($floater, floaterValue) } }, showFader: function(faderId) { var $fader = $('[data-fader-id="' + faderId + '"]'); $fader.find('div[data-control="fader-handle"]').show() }, setHandlePosition: function ($fader, faderValue) { draggingOrientation = $fader.attr('data-orientation'); setHandlePosition($fader, faderValue); draggingOrientation = null; }, setFloaterValue: function($floater, floaterValue) { $floater.text(floaterValue) }, initialize: function () { $('body').on('click', 'div[data-control="fader"]', faderClick); } }; })(window, jQuery);