// extended jQuery UI controls /* eslint-env console, jquery, browser */ /* eslint-disable comma-dangle, no-empty */ /* global assert, log, debugPrint */ // ---------------------------------------------------------------------------- // WIDGET BASE Object.assign($.Widget.prototype, { // common bootstrapping across widget types initHifiControl: function initHifiControl(hifiType) { initHifiControl.widgetCount = (initHifiControl.widgetCount || 0) + 1; hifiType = hifiType || this.widgetName; var element = this.element, options = this.options, node = element.get(0), dataset = node.dataset; assert(!this.element.is('.initialized')); this.element.addClass('initialized'); var attributes = [].reduce.call(node.attributes, function(out, attribute) { out[attribute.name] = attribute.value; return out; }, {}); var searchOrder = [ options, dataset, attributes, node ]; function setData(key, fallback) { var value = searchOrder.map(function(obj) { return obj[key]; }).concat(fallback).filter(function(value) { return value !== undefined; })[0]; return value === undefined ? null : (dataset[key] = value); } options.hifiWidgetId = hifiType + '-' + initHifiControl.widgetCount; node.id = node.id || options.hifiWidgetId; dataset.hifiType = hifiType; setData('type'); setData('for', node.id); setData('checked'); if (setData('value', null) !== null) { element.attr('value', dataset.value); } return node.id; }, hifiFindWidget: function(hifiType, quiet) { var selector = ':ui-'+hifiType; var _for = JSON.stringify(this.element.data('for')||undefined), element = _for && $('[id='+_for+']').filter(selector); if (!element.is(selector)) { element = this.element.closest(selector); } var instance = element.filter(selector)[hifiType]('instance'); if (!instance && !quiet) { // eslint-disable-next-line no-console console.error([ instance, 'could not find target instance ' + selector + ' for ' + this.element.data('hifi-type') + ' #' + this.element.prop('id') + ' for=' + this.element.data('for') ]); } return instance; }, }); // CHECKBOX $.widget('ui.hifiCheckbox', $.ui.checkboxradio, { value: function value(nv) { if (arguments.length) { var currentValue = this.element.prop('checked'); if (nv !== currentValue){ this.element.prop('checked', nv); this.element.change(); } } return this.element.prop('checked'); }, _create: function() { var id = this.initHifiControl(); this.element.attr('value', id); // add an implicit label if missing var forId = 'for=' + JSON.stringify(id); var label = $(this.element.get(0)).closest('label').add($('label[' + forId + ']')); if (!label.get(0)) { $('').appendTo(this.element); } this._super(); this.element.on('change._hifiCheckbox, click._hifiCheckbox', function() { var checked = this.value(), attr = this.element.attr('checked'); if (checked && !attr) { this.element.attr('checked', 'checked'); } else if (!checked && attr) { this.element.removeAttr('checked'); } this.refresh(); }.bind(this)); }, }); // BUTTON $.widget('ui.hifiButton', $.ui.button, { value: function(nv) { var dataset = this.element[0].dataset; if (arguments.length) { var checked = (dataset.checked === 'true'); nv = (nv === 'true' || !!nv); if (nv !== checked) { debugPrint('hifibutton checked changed', nv, checked); dataset.checked = nv; this.element.change(); } else { debugPrint('hifibutton value same', nv, checked); } } return dataset.checked === 'true'; }, _create: function() { this.element.data('type', 'checkbox'); this.initHifiControl(); this._super(); this.element[0].dataset.checked = !!this.element.attr('checked'); var _for = this.element.data('for') || undefined; if (_for && _for !== this.element[0].id) { _for = JSON.stringify(_for); var checkbox = this.hifiFindWidget('hifiCheckbox', true); if (!checkbox) { var input = $('').hide(); input.appendTo(this.element); checkbox = input.find('input') .hifiCheckbox() .hifiCheckbox('instance'); } this.element.find('.tooltip-target').removeClass('tooltip-target'); this.element.prop('id', 'button-'+this.element.prop('id')); checkbox.element.on('change._hifiButton', function() { debugPrint('checkbox -> button'); this.value(checkbox.value()); }.bind(this)); this.element.on('change', function() { debugPrint('button -> checkbox'); checkbox.value(this.value()); }.bind(this)); this.checkbox = checkbox; } }, }); // RADIO BUTTON $.widget('ui.hifiRadioButton', $.ui.checkboxradio, { value: function value(nv) { if (arguments.length) { this.element.prop('checked', !!nv); this.element.change(); } return this.element.prop('checked'); }, _create: function() { var id = this.initHifiControl(); this.element.attr('value', this.element.data('value') || id); // console.log(this.element[0]); assert(this.element.data('for')); this._super(); this.element.on('change._hifiRadioButton, click._hifiRadioButton', function() { var group = this.hifiFindWidget('hifiRadioGroup'), checked = !!this.element.attr('checked'), dotchecked = this.element.prop('checked'), value = this.element.attr('value'); if (dotchecked !== checked || group.value() !== value) { if (dotchecked && group.value() !== value) { log(value, 'UPDATING GRUOP', group.element[0].id); group.value(value); } } }.bind(this)); }, }); // RADIO GROUP $.widget('ui.hifiRadioGroup', $.ui.controlgroup, { radio: function(selector) { return this.element.find(':ui-hifiRadioButton' + selector).hifiRadioButton('instance'); }, refresh: function() { var value = this.value(); this.element.find(':ui-hifiRadioButton').each(function() { $(this).prop('checked', $(this).attr('value') === value).hifiRadioButton('refresh'); }); this._super(); }, value: function value(nv) { if (arguments.length) { var id = this.element[0].id, previous = this.value(); debugPrint('RADIOBUTTON GROUP value', id + ' = ' + nv + '(was: ' + previous + ')'); this.element.attr('value', nv); this.refresh(); } return this.element.attr('value'); }, _create: function(x) { debugPrint('ui.hifiRadioGroup._create', this.element[0]); this.initHifiControl(); this.options.items = { hifiRadioButton: 'input[type=radio]', }; this._super(); // allow setting correct radio button by assign to .value property (or $.fn.val() etc.) Object.defineProperty(this.element[0], 'value', { set: function(nv) { try { this.radio('#' + nv).value(true); } catch (e) {} return this.value(); }.bind(this), get: function() { return this.element.attr('value'); }.bind(this), }); }, }); // SPINNER (numeric input + up/down buttons) $.widget('ui.hifiSpinner', $.ui.spinner, { value: function value(nv) { if (arguments.length) { var num = parseFloat(nv); debugPrint('ui.hifiSpinner.value set', this.element[0].id, num, '(was: ' + this.value() + ')', 'raw:'+nv); this._value(num); this.element.change(); } return parseFloat(this.element.val()); }, _value: function(value, allowAny) { debugPrint('ui.hifiSpinner._value', value, allowAny); return this._super(value, allowAny); }, _create: function() { this.initHifiControl(); var step = this.options.step = this.options.step || 1.0; // allow step=".01" for precision and data-step=".1" for default increment amount this.options.prescale = parseFloat(this.element.data('step') || step) / (step); this._super(); this.previous = null; this.element.on('change._hifiSpinner', function() { var value = this.value(), invalid = !this.isValid(); debugPrint('hifiSpinner.changed', value, invalid ? '!!!invalid' : 'valid'); !invalid && this.element.attr('value', value); }.bind(this)); }, _spin: function( step, event ) { step = step * this.options.prescale * ( event.shiftKey ? 0.1 : event.ctrlKey ? 10 : 1 ); return this._super( step, event ); }, _stop: function( event, ui ) { try { return this._super(event, ui); } finally { if (/mouse/.test(event && event.type)) { var value = this.value(); if ((value || value === 0) && !isNaN(value) && this.previous !== null && this.previous !== value) { this.value(this.value()); } this.previous = value; } } }, _format: function(n) { var precision = this._precision(); return parseFloat(n).toFixed(precision); }, _events: { mousewheel: function(event, delta) { if (document.activeElement === this.element[0]) { // fix broken mousewheel on Chrome / embedded webkit delta = delta === undefined ? -(event.originalEvent.deltaY+event.originalEvent.deltaX) : delta; $.ui.spinner.prototype._events.mousewheel.call(this, event, delta); } } } }); // SLIDER $.widget('ui.hifiSlider', $.ui.slider, { value: function value(nv) { if (arguments.length) { var num = this._trimAlignValue(nv); debugPrint('hifiSlider.value', nv, num); if (this.options.value !== num) { this.options.value = num; this.element.change(); } } return this.options.value; }, _create: function() { this.initHifiControl(); this._super(); this.element .attr('type', this.element.attr('type') || 'slider') .find('.ui-slider-handle').html('
').end() .on('change', function() { this.hifiFindWidget('hifiSpinner').value(this.value()); this._refresh(); }.bind(this)); }, });