/** * 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 draggingOrientation = null; var subscribers = {}; var logger = g.JK.logger; function faderClick(e) { e.stopPropagation(); var $fader = $(this); draggingOrientation = $fader.attr('orientation'); var faderId = $fader.attr("fader-id"); 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; } // Notify subscribers of value change g._.each(subscribers, function (changeFunc, index, list) { if (faderId === index) { changeFunc(faderId, faderPct, false); } }); setHandlePosition($fader, faderPct); return false; } function setHandlePosition($fader, value) { var ratio, position; var $handle = $fader.find('div[control="fader-handle"]'); var orientation = $fader.attr('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('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('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 faderId = $draggingFader.attr("fader-id"); 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; } // Notify subscribers of value change g._.each(subscribers, function (changeFunc, index, list) { if (faderId === index) { changeFunc(faderId, faderPct, true); } }); } function onFaderDragStart(e, ui) { $draggingFaderHandle = $(this); $draggingFader = $draggingFaderHandle.closest('div[control="fader"]'); draggingOrientation = $draggingFader.attr('orientation'); } function onFaderDragStop(e, ui) { var faderId = $draggingFader.attr("fader-id"); 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; } // Notify subscribers of value change g._.each(subscribers, function (changeFunc, index, list) { if (faderId === index) { changeFunc(faderId, faderPct, false); } }); $draggingFaderHandle = null; $draggingFader = null; draggingOrientation = null; } function handleWidth(orientation) { return orientation === "horizontal" ? 8 : 11; } g.JK.FaderHelpers = { /** * Subscribe to fader change events. Provide a subscriber id * and a function in the form: change(faderId, newValue) which * will be called anytime a fader changes value. */ subscribe: function (subscriberId, changeFunction) { subscribers[subscriberId] = changeFunction; }, /** * Render a fader into the element identifed by the provided * selector, with the provided options. */ renderFader: function (selector, userOptions) { if (userOptions === undefined) { throw ("renderFader: userOptions is required"); } if (!(userOptions.hasOwnProperty("faderId"))) { throw ("renderFader: userOptions.faderId 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)); $('div[control="fader-handle"]', $(selector)).draggable({ drag: onFaderDrag, start: onFaderDragStart, stop: onFaderDragStop, containment: "parent", axis: options.faderType === 'horizontal' ? 'x' : 'y' }) // Embed any custom styles, applied to the .fader below selector if ("style" in options) { for (var key in options.style) { $(selector + ' .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; }, setFaderValue: function (faderId, faderValue) { var $fader = $('[fader-id="' + faderId + '"]'); this.setHandlePosition($fader, faderValue); }, setHandlePosition: function ($fader, faderValue) { draggingOrientation = $fader.attr('orientation'); setHandlePosition($fader, faderValue); draggingOrientation = null; }, initialize: function () { $('body').on('click', 'div[control="fader"]', faderClick); } }; })(window, jQuery);