function Editor(table) { this.table = table; } Editor.prototype.getInputHtml = function() { return ''; } Editor.prototype.render = function(el) { this.input = $(this.getInputHtml()); el.append(this.input); this.setupEvents(); }; Editor.prototype.setupEvents = function() { var me = this; this.input.on('blur', function() { me.submit(); }).on('change', function() { me.submit(); }).on('keydown', function(e){ if (e.keyCode == 13) return me.submit(); if (e.keyCode == 27) return me.cancel(); }); }; Editor.prototype.submit = function() { if (!this.onSubmit) return; var submit = this.onSubmit; delete this.onSubmit; submit(); }; Editor.prototype.cancel = function() { if (!this.onCancel) return; var cancel = this.onCancel; delete this.onCancel; cancel(); }; Editor.prototype.focus = function() { this.input.focus(); }; Editor.prototype.disable = function(flag) { this.input.prop('disabled', flag); }; Editor.prototype.setValue = function(value) { this.input.val(value); }; Editor.prototype.getValue = function() { return this.input.val(); }; Editor.prototype.getHtml = function() { return this.getValue(); }; function NameEditor(table) { Editor.call(this, table); } NameEditor.prototype = new Editor(); NameEditor.prototype.getInputHtml = function() { return ''; } function FrequencyEditor(table) { Editor.call(this, table); } FrequencyEditor.suffixes = { '': 0, 'K': 3, 'M': 6, 'G': 9, 'T': 12 }; FrequencyEditor.prototype = new Editor(); FrequencyEditor.prototype.getInputHtml = function() { return '
' + '' + '
' + '' + '
' + '
'; }; FrequencyEditor.prototype.render = function(el) { this.input = $(this.getInputHtml()); el.append(this.input); this.freqInput = el.find('input'); this.expInput = el.find('select'); this.setupEvents(); }; FrequencyEditor.prototype.setupEvents = function() { var me = this; var inputs = [this.freqInput, this.expInput].map(function(i) { return i[0]; }); inputs.forEach(function(input) { $(input).on('blur', function(e){ if (inputs.indexOf(e.relatedTarget) >= 0) { return; } me.submit(); }); }); var me = this; this.freqInput.on('keydown', function(e){ if (e.keyCode == 13) return me.submit(); if (e.keyCode == 27) return me.cancel(); var c = String.fromCharCode(e.which); if (c in FrequencyEditor.suffixes) { me.expInput.val(FrequencyEditor.suffixes[c]); } }); } FrequencyEditor.prototype.getValue = function() { var frequency = parseFloat(this.freqInput.val()); var exp = parseInt(this.expInput.val()); return Math.floor(frequency * 10 ** exp); }; FrequencyEditor.prototype.setValue = function(value) { var value = parseFloat(value); var exp = 0; if (!Number.isNaN(value) && value > 0) { exp = Math.floor(Math.log10(value) / 3) * 3; } this.freqInput.val(value / 10 ** exp); this.expInput.val(exp); }; FrequencyEditor.prototype.focus = function() { this.freqInput.focus(); }; var renderFrequency = function(freq) { var exp = 0; if (!Number.isNaN(freq)) { exp = Math.floor(Math.log10(freq) / 3) * 3; } var frequency = freq / 10 ** exp; var suffix = Object.entries(FrequencyEditor.suffixes).find(function(e) { return e[1] == exp; }); if (!suffix) { return freq + ' Hz'; } // fix lowercase 'kHz' suffix = suffix[0] == 'K' ? 'k' : suffix[0]; var expString = suffix[0] + 'Hz'; return frequency + ' ' + expString; } FrequencyEditor.prototype.getHtml = function() { return renderFrequency(this.getValue()); }; function ModulationEditor(table) { Editor.call(this, table); this.modes = table.data('modes'); } ModulationEditor.prototype = new Editor(); ModulationEditor.prototype.getInputHtml = function() { return ''; }; ModulationEditor.prototype.getHtml = function() { var $option = this.input.find('option:selected') return $option.html(); }; $.fn.bookmarktable = function() { var editors = { name: NameEditor, frequency: FrequencyEditor, modulation: ModulationEditor }; $.each(this, function(){ var $table = $(this).find('table'); $table.on('dblclick', 'td', function(e) { var $cell = $(e.target); var html = $cell.html(); var $row = $cell.parents('tr'); var name = $cell.data('editor'); var EditorCls = editors[name]; if (!EditorCls) return; var editor = new EditorCls($table); editor.render($cell.html('')); editor.setValue($cell.data('value')); editor.focus(); editor.onSubmit = function() { editor.disable(true); $.ajax(document.location.href + "/" + $row.data('id'), { data: JSON.stringify(Object.fromEntries([[name, editor.getValue()]])), contentType: 'application/json', method: 'POST' }).done(function(){ $cell.data('value', editor.getValue()); $cell.html(editor.getHtml()); }); }; editor.onCancel = function() { $cell.html(html); }; }); var $modal = $('#deleteModal').modal({show:false}); $modal.on('hidden.bs.modal', function() { var $row = $modal.data('row'); if (!$row) return; $row.find('.bookmark-delete').prop('disabled', false); $modal.removeData('row'); }); $modal.on('click', '.confirm', function() { var $row = $modal.data('row'); if (!$row) return; $.ajax(document.location.href + "/" + $row.data('id'), { data: "{}", contentType: 'application/json', method: 'DELETE' }).done(function(){ $row.remove(); $modal.modal('hide'); }); }); $table.on('click', '.bookmark-delete', function(e) { var $button = $(e.target); $button.prop('disabled', true); var $row = $button.parents('tr'); $modal.data('row', $row); $modal.modal('show'); }); $(this).on('click', '.bookmark-add', function() { if ($table.find('tr[data-id="new"]').length) return; $table.find('.emptytext').remove(); var row = $(''); var inputs = Object.fromEntries( Object.entries(editors).map(function(e) { return [e[0], new e[1]($table)]; }) ); row.append($.map(inputs, function(editor, name){ var cell = $(''); editor.render(cell); return cell; })); row.append($( '' + '
' + '' + '' + '
' + '' )); row.on('click', '.bookmark-cancel', function() { row.remove(); }); row.on('click', '.bookmark-save', function() { var data = Object.fromEntries( $.map(inputs, function(input, name){ input.disable(true); // double wrapped because jQuery.map() flattens the result return [[name, input.getValue()]]; }) ); $.ajax(document.location.href, { data: JSON.stringify([data]), contentType: 'application/json', method: 'POST' }).done(function(data){ if (data.length && data.length === 1 && 'bookmark_id' in data[0]) { row.attr('data-id', data[0]['bookmark_id']); var tds = row.find('td'); Object.values(inputs).forEach(function(input, index) { var td = $(tds[index]); td.data('value', input.getValue()); td.html(input.getHtml()); }); var $cell = row.find('td').last(); var $group = $cell.find('.btn-group'); if ($group.length) { $group.remove; $cell.html('
delete
'); } } }); }); $table.append(row); row[0].scrollIntoView(); }); var $importModal = $('#importModal').modal({show: false}); $(this).find('.bookmark-import').on('click', function() { var storage = new BookmarkLocalStorage(); var bookmarks = storage.getBookmarks(); if (bookmarks.length) { var modes = $table.data('modes'); var $list = $(''); $list.append(bookmarks.map(function(b) { var modulation = b.modulation; if (modulation in modes) { modulation = modes[modulation]; } var row = $( '' + '' + '' + '' + '' + '' ); row.data('bookmark', b); return row; })); $importModal.find('.bookmark-list').html($list); } else { $importModal.find('.bookmark-list').html('No personal bookmarks found in this browser'); } $importModal.modal('show'); }); $importModal.on('click', '.confirm', function() { var $list = $importModal.find('.bookmark-list table'); if ($list.length) { var selected = $list.find('tr').filter(function(){ return $(this).find('.select').is(':checked'); }).map(function(){ return $(this).data('bookmark'); }).toArray(); if (selected.length) { $.ajax(document.location.href, { data: JSON.stringify(selected), contentType: 'application/json', method: 'POST' }).done(function(data){ $table.find('.emptytext').remove(); var modes = $table.data('modes'); if (data.length && data.length == selected.length) { $table.append(data.map(function(obj, index) { var bookmark = selected[index]; var modulation_name = bookmark.modulation; if (modulation_name in modes) { modulation_name = modes[modulation_name]; } return $( '' + '' + '' + '' + '' + '' ) })); } }); } } $importModal.modal('hide'); }); }); };
' + b.name + '' + renderFrequency(b.frequency) + '' + modulation + '
' + bookmark.name + '' + renderFrequency(bookmark.frequency) +'' + modulation_name + '' + '' + '