2023-01-25 17:43:47 +00:00
|
|
|
<script type="text/javascript">
|
2024-02-22 16:56:21 +00:00
|
|
|
/*
|
|
|
|
|
*
|
|
|
|
|
* Define global javascript variables
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
var base_url = "<?php echo base_url(); ?>"; // Base URL
|
|
|
|
|
var site_url = "<?php echo site_url(); ?>"; // Site URL
|
|
|
|
|
var icon_dot_url = "<?php echo base_url(); ?>assets/images/dot.png";
|
|
|
|
|
// get the user_callsign from session
|
|
|
|
|
var my_call = "<?php echo $this->session->userdata('user_callsign'); ?>".toUpperCase();
|
2026-03-24 14:13:35 +00:00
|
|
|
// get the user_gridsquare from session or view data
|
|
|
|
|
var my_gridsquare = "<?php echo isset($my_gridsquare) ? $my_gridsquare : $this->session->userdata('user_locator'); ?>";
|
|
|
|
|
// get the user's measurement preference
|
|
|
|
|
var measurement_base = "<?php echo $this->session->userdata('user_measurement_base') ? $this->session->userdata('user_measurement_base') : $this->config->item('measurement_base'); ?>";
|
2023-01-25 17:43:47 +00:00
|
|
|
</script>
|
|
|
|
|
|
2023-10-08 10:41:24 +02:00
|
|
|
<script>
|
2023-11-29 20:16:53 +01:00
|
|
|
/*
|
2023-10-08 10:41:24 +02:00
|
|
|
General Language
|
|
|
|
|
*/
|
|
|
|
|
var lang_general_word_qso_data = "<?php echo lang('general_word_qso_data'); ?>";
|
2023-10-18 11:45:15 +02:00
|
|
|
var lang_general_word_danger = "<?php echo lang('general_word_danger'); ?>";
|
2023-10-30 23:50:56 +01:00
|
|
|
var lang_general_word_attention = "<?php echo lang('general_word_attention'); ?>";
|
|
|
|
|
var lang_general_word_warning = "<?php echo lang('general_word_warning'); ?>";
|
|
|
|
|
var lang_general_word_cancel = "<?php echo lang('general_word_cancel'); ?>";
|
|
|
|
|
var lang_general_word_ok = "<?php echo lang('general_word_ok'); ?>";
|
2023-10-18 11:45:15 +02:00
|
|
|
var lang_qso_delete_warning = "<?php echo lang('qso_delete_warning'); ?>";
|
2023-10-24 17:42:53 +02:00
|
|
|
var lang_general_word_colors = "<?php echo lang('general_word_colors'); ?>";
|
|
|
|
|
var lang_general_word_confirmed = "<?php echo lang('general_word_confirmed'); ?>";
|
|
|
|
|
var lang_general_word_worked_not_confirmed = "<?php echo lang('general_word_worked_not_confirmed'); ?>";
|
|
|
|
|
var lang_general_word_not_worked = "<?php echo lang('general_word_not_worked'); ?>";
|
2023-11-05 19:31:43 +01:00
|
|
|
var lang_admin_close = "<?php echo lang('admin_close'); ?>";
|
2023-10-08 10:41:24 +02:00
|
|
|
</script>
|
2020-09-15 21:20:01 +01:00
|
|
|
<!-- General JS Files used across Cloudlog -->
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/jquery-3.3.1.min.js"></script>
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/jquery.fancybox.min.js"></script>
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/bootstrap.bundle.js"></script>
|
2026-07-06 17:18:39 +01:00
|
|
|
<?php
|
2026-08-06 14:48:05 +01:00
|
|
|
$load_leaflet = in_array($this->uri->segment(1), [NULL, '', 'dashboard', 'logbook', 'logbookadvanced', 'gridmap', 'activated_gridmap', 'qso', 'map', 'search', 'activators', 'activatorsmap'], true)
|
2026-07-06 17:18:39 +01:00
|
|
|
|| ($this->uri->segment(1) == 'awards' && in_array($this->uri->segment(2), ['cq', 'iota', 'dxcc', 'ffma', 'gridmaster', 'waja', 'was', 'sota', 'pota'], true));
|
|
|
|
|
?>
|
|
|
|
|
<?php if ($load_leaflet) { ?>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/leaflet/leaflet.js"></script>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/leaflet/Control.FullScreen.js"></script>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/leaflet/L.Maidenhead.qrb.js"></script>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/leaflet/L.Terminator.js"></script>
|
|
|
|
|
<?php if ($this->uri->segment(1) == "activators") { ?>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/leaflet/L.Maidenhead.activators.js"></script>
|
|
|
|
|
<?php } ?>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/leaflet/leaflet.geodesic.js"></script>
|
2022-06-12 13:18:26 +02:00
|
|
|
<?php } ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/radiohelpers.js"></script>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/darkmodehelpers.js"></script>
|
2020-09-22 21:08:26 +02:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/bootstrapdialog/js/bootstrap-dialog.min.js"></script>
|
2026-07-11 16:45:29 +01:00
|
|
|
<?php if ($load_leaflet) { ?>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/easyprint.js"></script>
|
|
|
|
|
<?php } ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/sections/common.js"></script>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/sections/eqslcharcounter.js"></script>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/sections/version_dialog.js"></script>
|
2023-01-24 10:52:58 +01:00
|
|
|
|
2024-04-12 14:59:04 +01:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/htmx.min.js"></script>
|
2022-02-09 21:10:30 +00:00
|
|
|
|
2023-09-12 14:30:28 +01:00
|
|
|
<script>
|
|
|
|
|
// Reinitialize tooltips after new content has been loaded
|
|
|
|
|
document.addEventListener('htmx:afterSwap', function(event) {
|
2023-11-14 13:35:53 +01:00
|
|
|
$('[data-bs-toggle="tooltip"]').tooltip();
|
2023-09-12 14:30:28 +01:00
|
|
|
});
|
2024-02-22 16:56:21 +00:00
|
|
|
</script>
|
2023-12-03 09:37:39 +01:00
|
|
|
<!-- Version Dialog START -->
|
2023-12-04 01:20:50 +01:00
|
|
|
|
2023-12-03 09:37:39 +01:00
|
|
|
<?php
|
2024-02-22 16:56:21 +00:00
|
|
|
if ($this->session->userdata('user_id') != null) {
|
2023-12-04 05:56:05 +01:00
|
|
|
$versionDialog = $this->optionslib->get_option('version_dialog');
|
2023-12-04 23:03:25 +01:00
|
|
|
if (empty($versionDialog)) {
|
|
|
|
|
$this->optionslib->update('version_dialog', 'release_notes', 'yes');
|
|
|
|
|
}
|
2023-12-04 20:44:26 +01:00
|
|
|
$versionDialogHeader = $this->optionslib->get_option('version_dialog_header');
|
|
|
|
|
if (empty($versionDialogHeader)) {
|
|
|
|
|
$this->optionslib->update('version_dialog_header', $this->lang->line('options_version_dialog'), 'yes');
|
|
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
if ($versionDialog != "disabled") {
|
|
|
|
|
$confirmed = $this->user_options_model->get_options('version_dialog', array('option_name' => 'confirmed'))->result();
|
|
|
|
|
$confirmation_value = (isset($confirmed[0]->option_value)) ? $confirmed[0]->option_value : 'false';
|
2023-12-04 05:56:05 +01:00
|
|
|
if ($confirmation_value != 'true') {
|
|
|
|
|
$this->user_options_model->set_option('version_dialog', 'confirmed', array('boolean' => $confirmation_value));
|
2024-02-22 16:56:21 +00:00
|
|
|
?><script>
|
2023-12-04 05:56:05 +01:00
|
|
|
displayVersionDialog();
|
|
|
|
|
</script><?php
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
?>
|
2023-12-03 09:37:39 +01:00
|
|
|
|
|
|
|
|
<!-- Version Dialog END -->
|
|
|
|
|
|
2022-11-15 18:29:33 +01:00
|
|
|
<?php if ($this->uri->segment(1) == "oqrs") { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/oqrs.js"></script>
|
2022-11-15 18:29:33 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2023-11-21 12:13:09 +01:00
|
|
|
<?php if ($this->uri->segment(1) == "options") { ?>
|
|
|
|
|
<script>
|
|
|
|
|
$('#sendTestMailButton').click(function() {
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: base_url + 'index.php/options/sendTestMail',
|
|
|
|
|
type: 'POST',
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
<?php } ?>
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->uri->segment(1) == "awards" && ($this->uri->segment(2) == "cq")) { ?>
|
2022-10-12 09:44:13 +02:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/Polyline.encoded.js"></script>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script id="cqmapjs" type="text/javascript" src="<?php echo base_url(); ?>assets/js/sections/cqmap.js" tileUrl="<?php echo $this->optionslib->get_option('option_map_tile_server'); ?>"></script>
|
2022-10-12 09:44:13 +02:00
|
|
|
<?php } ?>
|
2021-04-29 23:50:45 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->uri->segment(1) == "awards" && ($this->uri->segment(2) == "iota")) { ?>
|
|
|
|
|
<script id="iotamapjs" type="text/javascript" src="<?php echo base_url(); ?>assets/js/sections/iotamap.js" tileUrl="<?php echo $this->optionslib->get_option('option_map_tile_server'); ?>"></script>
|
2022-12-11 17:47:36 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->uri->segment(1) == "awards" && ($this->uri->segment(2) == "dxcc")) { ?>
|
|
|
|
|
<script id="dxccmapjs" type="text/javascript" src="<?php echo base_url(); ?>assets/js/sections/dxccmap.js" tileUrl="<?php echo $this->optionslib->get_option('option_map_tile_server'); ?>"></script>
|
2022-11-11 19:23:41 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2022-09-18 16:48:16 +02:00
|
|
|
<?php if ($this->uri->segment(1) == "statistics") { ?>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/chart.js"></script>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/chartjs-plugin-piechart-outlabels.js"></script>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/sections/statistics.js"></script>
|
|
|
|
|
<?php } ?>
|
|
|
|
|
|
2026-01-05 22:14:00 +00:00
|
|
|
<?php if ($this->uri->segment(1) == "propagationadvisor") { ?>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/sections/propagationadvisor.js"></script>
|
|
|
|
|
<?php } ?>
|
|
|
|
|
|
2023-02-09 01:05:36 +01:00
|
|
|
<?php if ($this->uri->segment(1) == "continents") { ?>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/chart.js"></script>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/chartjs-plugin-piechart-outlabels.js"></script>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/sections/continents.js"></script>
|
|
|
|
|
<?php } ?>
|
|
|
|
|
|
2023-10-18 20:01:36 +02:00
|
|
|
<?php if ($this->uri->segment(1) == "adif" || $this->uri->segment(1) == "qrz" || $this->uri->segment(1) == "hrdlog" || $this->uri->segment(1) == "webadif" || $this->uri->segment(1) == "sattimers") { ?>
|
2020-09-15 21:20:01 +01:00
|
|
|
<!-- Javascript used for ADIF Import and Export Areas -->
|
2020-11-08 11:26:38 +01:00
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/moment.min.js"></script>
|
2022-12-04 15:16:36 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->uri->segment(1) == "maintenance") { ?>
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/maintenance.js"></script>
|
2023-08-01 08:21:17 +00:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->uri->segment(1) == "adif") { ?>
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/adif.js"></script>
|
2023-11-19 17:04:19 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->uri->segment(1) == "notes" && ($this->uri->segment(2) == "add" || $this->uri->segment(2) == "edit")) { ?>
|
2020-09-15 21:20:01 +01:00
|
|
|
<!-- Javascript used for Notes Area -->
|
2024-02-22 16:56:21 +00:00
|
|
|
<script src="<?php echo base_url(); ?>assets/plugins/quill/quill.min.js"></script>
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/notes.js"></script>
|
2019-10-05 21:57:44 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2026-01-09 13:17:56 +00:00
|
|
|
<?php if ($this->uri->segment(1) == "notes" && ($this->uri->segment(2) == "" || $this->uri->segment(2) == NULL || $this->uri->segment(2) == "view")) { ?>
|
|
|
|
|
<!-- Javascript for Notes list/view (modal wiring) -->
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/notes_list.js"></script>
|
|
|
|
|
<?php } ?>
|
|
|
|
|
|
2022-10-15 20:50:20 +02:00
|
|
|
<?php if ($this->uri->segment(1) == "logbooks" && $this->uri->segment(2) == "edit") { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script>
|
|
|
|
|
function removeSlug() {
|
|
|
|
|
var slugLink = document.getElementById("slugLink");
|
|
|
|
|
if (slugLink !== null) {
|
|
|
|
|
slugLink.style.display = "none";
|
|
|
|
|
}
|
|
|
|
|
document.getElementById('publicSlugInput').value = ''
|
2022-10-15 20:50:20 +02:00
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
</script>
|
2022-10-15 20:50:20 +02:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2021-01-29 20:11:35 +00:00
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/datatables.min.js"></script>
|
2020-10-19 14:10:58 +02:00
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/dataTables.buttons.min.js"></script>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/buttons.html5.min.js"></script>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/selectize.js"></script>
|
2020-09-22 21:08:26 +02:00
|
|
|
|
2025-08-23 21:11:36 +01:00
|
|
|
<?php if ($this->uri->segment(1) == "mostworked") { ?>
|
|
|
|
|
<script>
|
|
|
|
|
$(document).ready(function() {
|
|
|
|
|
$('#mostworked_table').DataTable({
|
|
|
|
|
"pageLength": 25,
|
|
|
|
|
"responsive": true,
|
|
|
|
|
"order": [[ 0, "asc" ]],
|
|
|
|
|
"columnDefs": [
|
|
|
|
|
{ "orderable": false, "targets": 0 },
|
|
|
|
|
{ "type": "num", "targets": 2 }
|
|
|
|
|
],
|
|
|
|
|
"language": {
|
|
|
|
|
url: getDataTablesLanguageUrl()
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Clear filters button
|
|
|
|
|
$('#clear_filters').click(function() {
|
|
|
|
|
$('#mostworked_filter_form')[0].reset();
|
|
|
|
|
$('#mostworked_filter_form').submit();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
<?php } ?>
|
|
|
|
|
|
2022-01-20 14:44:32 +00:00
|
|
|
<?php if ($this->uri->segment(1) == "station") { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script language="javascript" src="<?php echo base_url(); ?>assets/js/HamGridSquare.js"></script>
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/station_locations.js"></script>
|
2022-04-14 15:45:35 +01:00
|
|
|
<script>
|
|
|
|
|
var position;
|
2024-02-22 16:56:21 +00:00
|
|
|
|
2022-04-14 15:45:35 +01:00
|
|
|
function getLocation() {
|
|
|
|
|
if (navigator.geolocation) {
|
|
|
|
|
navigator.geolocation.getCurrentPosition(showPosition);
|
2023-03-17 17:54:35 +00:00
|
|
|
} else {
|
2022-04-14 15:48:09 +01:00
|
|
|
console.log('Geolocation is not supported by this browser.');
|
2022-04-14 15:45:35 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function showPosition(position) {
|
2024-02-22 16:56:21 +00:00
|
|
|
gridsquare = latLonToGridSquare(position.coords.latitude, position.coords.longitude);
|
2022-04-14 15:49:02 +01:00
|
|
|
document.getElementById("stationGridsquareInput").value = gridsquare;
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
2022-04-14 15:45:35 +01:00
|
|
|
</script>
|
2022-01-20 14:50:42 +00:00
|
|
|
<?php } ?>
|
|
|
|
|
|
|
|
|
|
<?php if ($this->uri->segment(1) == "logbooks") { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/station_logbooks.js"></script>
|
2022-01-20 14:44:32 +00:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2023-01-31 13:00:23 +01:00
|
|
|
<?php if ($this->uri->segment(1) == "debug") { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script type="text/javascript">
|
|
|
|
|
function copyURL(url) {
|
|
|
|
|
var urlField = $('#baseUrl');
|
|
|
|
|
navigator.clipboard.writeText(url).then(function() {});
|
|
|
|
|
urlField.addClass('flash-copy')
|
|
|
|
|
.delay('1000').queue(function() {
|
|
|
|
|
urlField.removeClass('flash-copy').dequeue();
|
|
|
|
|
});
|
|
|
|
|
}
|
2023-01-31 13:00:23 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
$(function() {
|
|
|
|
|
$('[data-bs-toggle="tooltip"]').tooltip({
|
|
|
|
|
'delay': {
|
|
|
|
|
show: 500,
|
|
|
|
|
hide: 0
|
|
|
|
|
},
|
|
|
|
|
'placement': 'right'
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
</script>
|
2023-01-31 13:00:23 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2022-09-28 15:41:08 +02:00
|
|
|
<?php if ($this->uri->segment(1) == "api" && $this->uri->segment(2) == "help") { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script type="text/javascript">
|
|
|
|
|
function copyApiKey(apiKey) {
|
|
|
|
|
var apiKeyField = $('#' + apiKey);
|
|
|
|
|
navigator.clipboard.writeText(apiKey).then(function() {});
|
|
|
|
|
apiKeyField.addClass('flash-copy')
|
|
|
|
|
.delay('1000').queue(function() {
|
|
|
|
|
apiKeyField.removeClass('flash-copy').dequeue();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-28 15:41:08 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
function copyApiUrl() {
|
|
|
|
|
var apiUrlField = $('#apiUrl');
|
|
|
|
|
navigator.clipboard.writeText("<?php echo base_url(); ?>").then(function() {});
|
|
|
|
|
apiUrlField.addClass('flash-copy')
|
|
|
|
|
.delay('1000').queue(function() {
|
|
|
|
|
apiUrlField.removeClass('flash-copy').dequeue();
|
|
|
|
|
});
|
|
|
|
|
}
|
2023-03-09 22:57:28 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
$(function() {
|
|
|
|
|
$('[data-bs-toggle="tooltip"]').tooltip({
|
|
|
|
|
'delay': {
|
|
|
|
|
show: 500,
|
|
|
|
|
hide: 0
|
|
|
|
|
},
|
|
|
|
|
'placement': 'right'
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
</script>
|
2022-09-28 15:41:08 +02:00
|
|
|
<?php } ?>
|
2022-01-20 14:50:42 +00:00
|
|
|
|
2019-09-12 00:53:39 +01:00
|
|
|
<?php if ($this->uri->segment(1) == "search" && $this->uri->segment(2) == "filter") { ?>
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/query-builder.standalone.min.js"></script>
|
2019-09-12 00:53:39 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<script type="text/javascript">
|
2026-08-06 15:05:04 +01:00
|
|
|
var ADVANCED_SEARCH_RULES_KEY = 'cloudlog.advancedSearch.rules';
|
2024-02-22 16:56:21 +00:00
|
|
|
$(".search-results-box").hide();
|
|
|
|
|
|
2026-08-06 15:05:04 +01:00
|
|
|
function getSavedAdvancedRules() {
|
|
|
|
|
try {
|
|
|
|
|
var saved = localStorage.getItem(ADVANCED_SEARCH_RULES_KEY);
|
|
|
|
|
if (!saved) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var parsed = JSON.parse(saved);
|
|
|
|
|
return $.isEmptyObject(parsed) ? null : parsed;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function saveAdvancedRules(rules) {
|
|
|
|
|
if ($.isEmptyObject(rules)) {
|
|
|
|
|
localStorage.removeItem(ADVANCED_SEARCH_RULES_KEY);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
localStorage.setItem(ADVANCED_SEARCH_RULES_KEY, JSON.stringify(rules));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function clearAdvancedResults() {
|
|
|
|
|
$('.card-body.result').html('<div class="text-muted p-2" id="advanced-results-empty">Run a query to see matching QSOs.</div>');
|
|
|
|
|
$('.search-results-box').hide();
|
|
|
|
|
$('#advanced-results-count').text('0 QSOs');
|
|
|
|
|
$('#advanced-results-mode').text('Filtered query');
|
|
|
|
|
$('.exportbutton').html('<button class="btn btn-sm btn-primary" id="btn-export" onclick="export_search_result();">Export to ADIF</button>');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function initAdvancedSearchDataTable() {
|
|
|
|
|
var table = $('.card-body.result table').first();
|
|
|
|
|
if (table.length === 0 || !$.fn.DataTable) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var hasTimeColumn = table.find('thead th').length > 1;
|
|
|
|
|
var defaultOrder = hasTimeColumn ? [[0, 'desc'], [1, 'desc']] : [[0, 'desc']];
|
|
|
|
|
|
|
|
|
|
table.DataTable({
|
|
|
|
|
destroy: true,
|
|
|
|
|
paging: true,
|
|
|
|
|
pageLength: 25,
|
|
|
|
|
lengthChange: true,
|
|
|
|
|
info: true,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: true,
|
|
|
|
|
order: defaultOrder,
|
|
|
|
|
scrollX: true,
|
|
|
|
|
searching: false,
|
|
|
|
|
language: {
|
|
|
|
|
url: getDataTablesLanguageUrl(),
|
|
|
|
|
},
|
|
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (isDarkModeTheme()) {
|
|
|
|
|
$('.buttons-csv').css('color', 'white');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderAdvancedSearchResults(html, modeLabel) {
|
|
|
|
|
$('.card-body.result').empty().append(html);
|
|
|
|
|
$('.search-results-box').show();
|
|
|
|
|
|
|
|
|
|
initAdvancedSearchDataTable();
|
|
|
|
|
$('[data-bs-toggle="tooltip"]').tooltip();
|
|
|
|
|
|
|
|
|
|
var rows = $('.card-body.result tbody tr').length;
|
|
|
|
|
$('#advanced-results-count').text(rows + ' QSO' + (rows === 1 ? '' : 's'));
|
|
|
|
|
$('#advanced-results-mode').text(modeLabel || 'Filtered query');
|
|
|
|
|
|
|
|
|
|
if (rows === 0) {
|
|
|
|
|
$('.card-body.result').prepend('<div class="alert alert-info m-2">No QSOs matched your current filter.</div>');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$('.table-responsive .dropdown-toggle').off('mouseenter').on('mouseenter', function() {
|
|
|
|
|
showQsoActionsMenu($(this).closest('.dropdown'));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
$('#builder').queryBuilder({
|
|
|
|
|
filters: [
|
|
|
|
|
<?php foreach ($get_table_names->result() as $row) {
|
|
|
|
|
$value_name = str_replace("COL_", "", $row->Field);
|
|
|
|
|
if ($value_name != "PRIMARY_KEY" && strpos($value_name, 'MY_') === false && strpos($value_name, '_INTL') == false) { ?> {
|
|
|
|
|
id: '<?php echo $row->Field; ?>',
|
|
|
|
|
label: '<?php echo $value_name; ?>',
|
|
|
|
|
<?php if (strpos($row->Type, 'int(') !== false) { ?>
|
|
|
|
|
type: 'integer',
|
|
|
|
|
operators: ['equal', 'not_equal', 'less', 'less_or_equal', 'greater', 'greater_or_equal']
|
|
|
|
|
<?php } elseif (strpos($row->Type, 'double') !== false) { ?>
|
|
|
|
|
type: 'double',
|
|
|
|
|
operators: ['equal', 'not_equal', 'less', 'less_or_equal', 'greater', 'greater_or_equal']
|
|
|
|
|
<?php } elseif (strpos($row->Type, 'datetime') !== false) { ?>
|
|
|
|
|
type: 'datetime',
|
|
|
|
|
operators: ['equal', 'not_equal', 'less', 'less_or_equal', 'greater', 'greater_or_equal']
|
|
|
|
|
<?php } else { ?>
|
|
|
|
|
type: 'string',
|
|
|
|
|
operators: ['equal', 'not_equal', 'begins_with', 'contains', 'ends_with', 'is_empty', 'is_not_empty', 'is_null', 'is_not_null']
|
|
|
|
|
<?php } ?>
|
|
|
|
|
},
|
|
|
|
|
<?php } ?>
|
2021-10-03 20:06:08 +02:00
|
|
|
<?php } ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
]
|
|
|
|
|
});
|
2021-09-25 17:40:15 +02:00
|
|
|
|
2026-08-06 15:05:04 +01:00
|
|
|
var savedRules = getSavedAdvancedRules();
|
|
|
|
|
if (savedRules !== null) {
|
|
|
|
|
try {
|
|
|
|
|
$('#builder').queryBuilder('setRules', savedRules);
|
|
|
|
|
$('#btn-save').show();
|
|
|
|
|
} catch (e) {
|
|
|
|
|
localStorage.removeItem(ADVANCED_SEARCH_RULES_KEY);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$('#btn-reset').on('click', function() {
|
|
|
|
|
$('#builder').queryBuilder('reset');
|
|
|
|
|
localStorage.removeItem(ADVANCED_SEARCH_RULES_KEY);
|
|
|
|
|
clearAdvancedResults();
|
|
|
|
|
$('#btn-save').hide();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$('#btn-clear-advanced-results').on('click', function() {
|
|
|
|
|
clearAdvancedResults();
|
|
|
|
|
});
|
|
|
|
|
|
2021-09-25 17:40:15 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
function export_search_result() {
|
|
|
|
|
var result = $('#builder').queryBuilder('getRules');
|
|
|
|
|
if (!$.isEmptyObject(result)) {
|
|
|
|
|
xhttp = new XMLHttpRequest();
|
|
|
|
|
xhttp.onreadystatechange = function() {
|
|
|
|
|
var a;
|
|
|
|
|
if (xhttp.readyState === 4 && xhttp.status === 200) {
|
|
|
|
|
// Trick for making downloadable link
|
|
|
|
|
a = document.createElement('a');
|
|
|
|
|
a.href = window.URL.createObjectURL(xhttp.response);
|
|
|
|
|
// Give filename you wish to download
|
|
|
|
|
a.download = "advanced_search_export.adi";
|
|
|
|
|
a.style.display = 'none';
|
|
|
|
|
document.body.appendChild(a);
|
|
|
|
|
a.click();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
// Post data to URL which handles post request
|
|
|
|
|
xhttp.open("POST", "<?php echo site_url('search/export_to_adif'); ?>", true);
|
|
|
|
|
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
|
|
|
|
// You should set responseType as blob for binary responses
|
|
|
|
|
xhttp.responseType = 'blob';
|
|
|
|
|
xhttp.send("search=" + JSON.stringify(result, null, 2));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function export_stored_query(id) {
|
2021-10-03 20:06:08 +02:00
|
|
|
xhttp = new XMLHttpRequest();
|
|
|
|
|
xhttp.onreadystatechange = function() {
|
|
|
|
|
var a;
|
|
|
|
|
if (xhttp.readyState === 4 && xhttp.status === 200) {
|
|
|
|
|
// Trick for making downloadable link
|
|
|
|
|
a = document.createElement('a');
|
|
|
|
|
a.href = window.URL.createObjectURL(xhttp.response);
|
|
|
|
|
// Give filename you wish to download
|
|
|
|
|
a.download = "advanced_search_export.adi";
|
|
|
|
|
a.style.display = 'none';
|
|
|
|
|
document.body.appendChild(a);
|
|
|
|
|
a.click();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
// Post data to URL which handles post request
|
2024-02-22 16:56:21 +00:00
|
|
|
xhttp.open("POST", "<?php echo site_url('search/export_stored_query_to_adif'); ?>", true);
|
2021-10-03 20:06:08 +02:00
|
|
|
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
|
|
|
|
// You should set responseType as blob for binary responses
|
|
|
|
|
xhttp.responseType = 'blob';
|
2024-02-22 16:56:21 +00:00
|
|
|
xhttp.send("id=" + id);
|
2021-10-03 20:06:08 +02:00
|
|
|
}
|
2021-09-25 17:40:15 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
$('#btn-save').on('click', function() {
|
|
|
|
|
var resultquery = $('#builder').queryBuilder('getRules');
|
|
|
|
|
if (!$.isEmptyObject(resultquery)) {
|
|
|
|
|
let message = 'Description: <input class="form-control input-group-sm getqueryname">'
|
2021-10-03 20:06:08 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
BootstrapDialog.confirm({
|
|
|
|
|
title: 'Query description',
|
|
|
|
|
size: BootstrapDialog.SIZE_NORMAL,
|
|
|
|
|
cssClass: 'description-dialog',
|
|
|
|
|
closable: true,
|
|
|
|
|
nl2br: false,
|
|
|
|
|
message: message,
|
|
|
|
|
btnCancelLabel: 'Cancel',
|
|
|
|
|
btnOKLabel: 'Save',
|
|
|
|
|
callback: function(result) {
|
|
|
|
|
if (result) {
|
|
|
|
|
$.post("<?php echo site_url('search/save_query'); ?>", {
|
|
|
|
|
search: JSON.stringify(resultquery, null, 2),
|
|
|
|
|
description: $(".getqueryname").val()
|
|
|
|
|
})
|
|
|
|
|
.done(function(data) {
|
|
|
|
|
$(".alert").remove();
|
|
|
|
|
$(".card-body.main").append('<div class="alert alert-success">Your query has been saved!</div>');
|
|
|
|
|
if ($("#querydropdown option").length == 0) {
|
|
|
|
|
var dropdowninfo = ' <button class="btn btn-sm btn-primary" onclick="edit_stored_query_dialog()" id="btn-edit">Edit queries</button></p>' +
|
|
|
|
|
'<div class="mb-3 row querydropdownform">' +
|
|
|
|
|
'<label class="col-md-2 control-label" for="querydropdown"> Stored queries:</label>' +
|
|
|
|
|
'<div class="col-md-3">' +
|
2023-11-14 13:35:53 +01:00
|
|
|
'<select id="querydropdown" name="querydropdown" class="form-select form-select-sm">' +
|
2021-10-11 19:36:52 +02:00
|
|
|
'</select>' +
|
2024-02-22 16:56:21 +00:00
|
|
|
'</div>' +
|
|
|
|
|
'<button class="btn btn-sm btn-primary ld-ext-right runbutton" onclick="run_query()">Run Query<div class="ld ld-ring ld-spin"></div></button>' +
|
|
|
|
|
'</div>';
|
|
|
|
|
$("#btn-save").after(dropdowninfo);
|
|
|
|
|
}
|
|
|
|
|
$('#querydropdown').append(new Option(data.description, data.id)); // We add the saved query to the dropdown
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-01-03 22:03:11 -05:00
|
|
|
},
|
2021-10-03 20:06:08 +02:00
|
|
|
});
|
2019-09-12 00:53:39 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
} else {
|
2021-10-03 20:06:08 +02:00
|
|
|
BootstrapDialog.show({
|
|
|
|
|
title: 'Stored Queries',
|
2024-02-22 16:56:21 +00:00
|
|
|
type: BootstrapDialog.TYPE_WARNING,
|
|
|
|
|
size: BootstrapDialog.SIZE_NORMAL,
|
2021-10-03 20:06:08 +02:00
|
|
|
cssClass: 'queries-dialog',
|
|
|
|
|
nl2br: false,
|
2024-02-22 16:56:21 +00:00
|
|
|
message: 'You need to make a query before you search!',
|
2021-10-03 20:06:08 +02:00
|
|
|
buttons: [{
|
2023-11-05 19:38:34 +01:00
|
|
|
label: lang_admin_close,
|
2021-10-03 20:06:08 +02:00
|
|
|
action: function(dialogItself) {
|
|
|
|
|
dialogItself.close();
|
|
|
|
|
}
|
|
|
|
|
}]
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
function run_query() {
|
|
|
|
|
$(".alert").remove();
|
|
|
|
|
$(".runbutton").addClass('running');
|
|
|
|
|
$(".runbutton").prop('disabled', true);
|
|
|
|
|
let id = $('#querydropdown').val();
|
|
|
|
|
$.post("<?php echo site_url('search/run_query'); ?>", {
|
|
|
|
|
id: id
|
2021-10-03 20:06:08 +02:00
|
|
|
})
|
|
|
|
|
.done(function(data) {
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
$('.exportbutton').html('<button class="btn btn-sm btn-primary" onclick="export_stored_query(' + id + ')">Export to ADIF</button>');
|
2026-08-06 15:05:04 +01:00
|
|
|
renderAdvancedSearchResults(data, 'Stored query');
|
|
|
|
|
})
|
|
|
|
|
.fail(function() {
|
|
|
|
|
$('.card-body.result').html('<div class="alert alert-danger m-2">Unable to run stored query. Please try again.</div>');
|
|
|
|
|
$('.search-results-box').show();
|
|
|
|
|
$('#advanced-results-count').text('0 QSOs');
|
|
|
|
|
})
|
|
|
|
|
.always(function() {
|
2024-02-22 16:56:21 +00:00
|
|
|
$(".runbutton").removeClass('running');
|
|
|
|
|
$(".runbutton").prop('disabled', false);
|
2021-10-03 20:06:08 +02:00
|
|
|
});
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function delete_stored_query(id) {
|
|
|
|
|
BootstrapDialog.confirm({
|
|
|
|
|
title: 'DANGER',
|
|
|
|
|
message: 'Warning! Are you sure you want delete this stored query?',
|
|
|
|
|
type: BootstrapDialog.TYPE_DANGER,
|
|
|
|
|
closable: true,
|
|
|
|
|
draggable: true,
|
|
|
|
|
btnOKClass: 'btn-danger',
|
|
|
|
|
callback: function(result) {
|
|
|
|
|
if (result) {
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: base_url + 'index.php/search/delete_query',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
'id': id
|
|
|
|
|
},
|
|
|
|
|
success: function(data) {
|
|
|
|
|
$(".bootstrap-dialog-message").prepend('<div class="alert alert-danger">The stored query has been deleted!</div>');
|
|
|
|
|
$("#query_" + id).remove(); // removes query from table in dialog
|
|
|
|
|
$("#querydropdown option[value='" + id + "']").remove(); // removes query from dropdown
|
|
|
|
|
if ($("#querydropdown option").length == 0) {
|
|
|
|
|
$("#btn-edit").remove();
|
|
|
|
|
$('.querydropdownform').remove();
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
error: function() {
|
|
|
|
|
$(".bootstrap-dialog-message").prepend('<div class="alert alert-danger">The stored query could not be deleted. Please try again!</div>');
|
|
|
|
|
},
|
|
|
|
|
});
|
2021-10-03 20:06:08 +02:00
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
2021-10-03 20:06:08 +02:00
|
|
|
});
|
|
|
|
|
}
|
2019-09-12 00:53:39 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
function edit_stored_query(id) {
|
|
|
|
|
$('#description_' + id).attr('contenteditable', 'true');
|
|
|
|
|
$('#description_' + id).focus();
|
|
|
|
|
$('#edit_' + id).html('<a class="btn btn-primary btn-sm" href="javascript:save_edited_query(' + id + ');">Save</a>'); // Change to save button
|
2021-10-03 20:06:08 +02:00
|
|
|
}
|
2019-09-12 00:53:39 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
function save_edited_query(id) {
|
|
|
|
|
$('#description_' + id).attr('contenteditable', 'false');
|
|
|
|
|
$('#edit_' + id).html('<a class="btn btn-outline-primary btn-sm" href="javascript:edit_stored_query(' + id + ');">Edit</a>');
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: base_url + 'index.php/search/save_edited_query',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
id: id,
|
|
|
|
|
description: $('#description_' + id).html(),
|
|
|
|
|
},
|
|
|
|
|
success: function(html) {
|
|
|
|
|
$('#edit_' + id).html('<a class="btn btn-outline-primary btn-sm" href="javascript:edit_stored_query(' + id + ');">Edit</a>'); // Change to edit button
|
|
|
|
|
$(".bootstrap-dialog-message").prepend('<div class="alert alert-success">The query description has been updated!</div>');
|
|
|
|
|
$("#querydropdown option[value='" + id + "']").text($('#description_' + id).html()); // Change text in dropdown
|
|
|
|
|
},
|
|
|
|
|
error: function() {
|
|
|
|
|
$(".bootstrap-dialog-message").prepend('<div class="alert alert-danger">Something went wrong with the save. Please try again!</div>');
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function edit_stored_query_dialog() {
|
|
|
|
|
$(".alert").remove();
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: base_url + 'index.php/search/get_stored_queries',
|
|
|
|
|
type: 'post',
|
|
|
|
|
success: function(html) {
|
|
|
|
|
BootstrapDialog.show({
|
|
|
|
|
title: 'Stored Queries',
|
|
|
|
|
size: BootstrapDialog.SIZE_WIDE,
|
|
|
|
|
cssClass: 'queries-dialog',
|
|
|
|
|
nl2br: false,
|
|
|
|
|
message: html,
|
|
|
|
|
buttons: [{
|
|
|
|
|
label: lang_admin_close,
|
|
|
|
|
action: function(dialogItself) {
|
|
|
|
|
dialogItself.close();
|
|
|
|
|
}
|
|
|
|
|
}]
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$('#btn-get').on('click', function() {
|
|
|
|
|
$(".alert").remove();
|
|
|
|
|
var result = $('#builder').queryBuilder('getRules');
|
|
|
|
|
if (!$.isEmptyObject(result)) {
|
|
|
|
|
$(".searchbutton").addClass('running');
|
|
|
|
|
$(".searchbutton").prop('disabled', true);
|
|
|
|
|
|
|
|
|
|
$.post("<?php echo site_url('search/search_result'); ?>", {
|
|
|
|
|
search: JSON.stringify(result, null, 2),
|
|
|
|
|
temp: "testvar"
|
|
|
|
|
})
|
|
|
|
|
.done(function(data) {
|
|
|
|
|
$('.exportbutton').html('<button class="btn btn-sm btn-primary" onclick="export_search_result();">Export to ADIF</button>');
|
2026-08-06 15:05:04 +01:00
|
|
|
saveAdvancedRules(result);
|
2024-02-22 16:56:21 +00:00
|
|
|
|
2026-08-06 15:05:04 +01:00
|
|
|
renderAdvancedSearchResults(data, 'Filtered query');
|
|
|
|
|
$("#btn-save").show();
|
|
|
|
|
})
|
|
|
|
|
.fail(function() {
|
|
|
|
|
$('.card-body.result').html('<div class="alert alert-danger m-2">Search failed. Please try again.</div>');
|
|
|
|
|
$('.search-results-box').show();
|
|
|
|
|
$('#advanced-results-count').text('0 QSOs');
|
|
|
|
|
})
|
|
|
|
|
.always(function() {
|
2024-02-22 16:56:21 +00:00
|
|
|
$(".searchbutton").removeClass('running');
|
|
|
|
|
$(".searchbutton").prop('disabled', false);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
BootstrapDialog.show({
|
|
|
|
|
title: 'Stored Queries',
|
|
|
|
|
type: BootstrapDialog.TYPE_WARNING,
|
|
|
|
|
size: BootstrapDialog.SIZE_NORMAL,
|
|
|
|
|
cssClass: 'queries-dialog',
|
|
|
|
|
nl2br: false,
|
|
|
|
|
message: 'You need to make a query before you search!',
|
|
|
|
|
buttons: [{
|
|
|
|
|
label: lang_admin_close,
|
|
|
|
|
action: function(dialogItself) {
|
|
|
|
|
dialogItself.close();
|
|
|
|
|
}
|
|
|
|
|
}]
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$('#btn-set').on('click', function() {
|
|
|
|
|
//$('#builder').queryBuilder('setRules', rules_basic);
|
|
|
|
|
var result = $('#builder').queryBuilder('getRules');
|
|
|
|
|
if (!$.isEmptyObject(result)) {
|
|
|
|
|
rules_basic = result;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//When rules changed :
|
|
|
|
|
$('#builder').on('getRules.queryBuilder.filter', function(e) {
|
|
|
|
|
//$log.info(e.value);
|
|
|
|
|
});
|
|
|
|
|
</script>
|
2022-02-13 23:03:38 +01:00
|
|
|
|
2019-09-12 00:53:39 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2019-01-09 15:18:46 +00:00
|
|
|
<script>
|
2024-02-22 16:56:21 +00:00
|
|
|
$(document).ready(function() {
|
|
|
|
|
$('#create_station_profile #country').val($("#dxcc_select option:selected").text());
|
|
|
|
|
$("#create_station_profile #dxcc_select").change(function() {
|
|
|
|
|
$('#country').val($("#dxcc_select option:selected").text());
|
2022-03-22 17:41:03 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
});
|
|
|
|
|
});
|
2019-01-09 15:18:46 +00:00
|
|
|
</script>
|
2019-01-13 15:59:16 +00:00
|
|
|
|
2023-10-14 21:05:33 +02:00
|
|
|
<script>
|
2024-02-22 16:56:21 +00:00
|
|
|
function printWarning() {
|
|
|
|
|
if ($("#dxcc_select option:selected").text().includes("<?php echo lang('gen_hamradio_deleted_dxcc'); ?>")) {
|
|
|
|
|
$('#warningMessageDXCC').show();
|
|
|
|
|
$('#dxcc_select').css('border', '2px solid rgb(217, 83, 79)');
|
|
|
|
|
$('#warningMessageDXCC').text("<?php echo lang('station_location_dxcc_warning'); ?>");
|
|
|
|
|
} else {
|
|
|
|
|
$('#dxcc_select').css('border', '');
|
|
|
|
|
$('#warningMessageDXCC').hide();
|
|
|
|
|
}
|
2023-10-14 21:05:33 +02:00
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
$('#dxcc_select').ready(function() {
|
|
|
|
|
printWarning();
|
|
|
|
|
});
|
2023-10-16 17:04:48 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
$('#dxcc_select').on('change', function() {
|
|
|
|
|
printWarning();
|
|
|
|
|
});
|
2023-10-14 21:05:33 +02:00
|
|
|
</script>
|
|
|
|
|
|
2019-03-20 17:47:40 +00:00
|
|
|
<script>
|
2024-02-22 16:56:21 +00:00
|
|
|
var $ = jQuery.noConflict();
|
|
|
|
|
$('[data-fancybox]').fancybox({
|
|
|
|
|
toolbar: false,
|
|
|
|
|
smallBtn: true,
|
|
|
|
|
iframe: {
|
|
|
|
|
preload: false
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Here we capture ALT-L to invoke the Quick lookup
|
|
|
|
|
document.onkeyup = function(e) {
|
|
|
|
|
if (e.altKey && e.which == 76) {
|
|
|
|
|
spawnLookupModal();
|
|
|
|
|
}
|
|
|
|
|
if (e.altKey && e.which == 81) {
|
|
|
|
|
spawnQrbCalculator();
|
|
|
|
|
}
|
|
|
|
|
};
|
2023-04-01 15:28:21 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
function showActivatorsMap(call, count, grids) {
|
2022-06-12 13:18:26 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
let re = /,/g;
|
|
|
|
|
grids = grids.replace(re, ', ');
|
2022-06-12 13:18:26 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
var result = "Callsign: " + call.replace('0', 'Ø') + "<br />";
|
|
|
|
|
result += "Count: " + count + "<br/>";
|
|
|
|
|
result += "Grids: " + grids + "<br/><br />";
|
2022-06-12 13:18:26 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
$(".activatorsmapResult").html(result);
|
2022-06-12 13:18:26 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
// If map is already initialized
|
|
|
|
|
var container = L.DomUtil.get('mapactivators');
|
2022-06-12 13:18:26 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
if (container != null) {
|
|
|
|
|
container._leaflet_id = null;
|
|
|
|
|
}
|
2022-06-12 13:18:26 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
const map = new L.map('mapactivators').setView([30, 0], 1.5);
|
2022-06-12 13:18:26 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
var grid_four = grids.split(', ');
|
2022-06-12 13:18:26 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
var maidenhead = new L.maidenheadactivators(grid_four).addTo(map);
|
2022-06-12 13:18:26 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
var osmUrl = '<?php echo $this->optionslib->get_option('option_map_tile_server'); ?>';
|
|
|
|
|
var osmAttrib = 'Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';
|
|
|
|
|
var osm = new L.TileLayer(osmUrl, {
|
|
|
|
|
minZoom: 1,
|
|
|
|
|
maxZoom: 12,
|
|
|
|
|
attribution: osmAttrib
|
|
|
|
|
});
|
2022-06-12 13:18:26 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
map.addLayer(osm);
|
|
|
|
|
}
|
2019-03-20 17:47:40 +00:00
|
|
|
</script>
|
2019-04-08 15:36:23 +01:00
|
|
|
|
2020-12-07 17:28:19 +00:00
|
|
|
<?php if ($this->uri->segment(1) == "map" && $this->uri->segment(2) == "custom") { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/leaflet/L.Maidenhead.js"></script>
|
|
|
|
|
<script id="leafembed" type="text/javascript" src="<?php echo base_url(); ?>assets/js/leaflet/leafembed.js" tileUrl="<?php echo $this->optionslib->get_option('option_map_tile_server'); ?>"></script>
|
2020-12-07 17:28:19 +00:00
|
|
|
<script type="text/javascript">
|
2024-02-22 16:56:21 +00:00
|
|
|
$(function() {
|
|
|
|
|
$('[data-bs-toggle="tooltip"]').tooltip()
|
|
|
|
|
});
|
2020-12-07 17:28:19 +00:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($qra == "set") { ?>
|
|
|
|
|
var q_lat = <?php echo $qra_lat; ?>;
|
|
|
|
|
var q_lng = <?php echo $qra_lng; ?>;
|
2020-12-07 17:28:19 +00:00
|
|
|
<?php } else { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
var q_lat = 40.313043;
|
|
|
|
|
var q_lng = -32.695312;
|
2020-12-07 17:28:19 +00:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
var qso_loc = '<?php echo site_url('map/map_plot_json/'); ?>';
|
2022-03-09 19:01:04 +01:00
|
|
|
var q_zoom = 3;
|
2020-12-07 17:28:19 +00:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
$(document).ready(function() {
|
2020-12-07 17:28:19 +00:00
|
|
|
<?php if ($this->config->item('map_gridsquares') != FALSE) { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
var grid = "Yes";
|
2020-12-07 17:28:19 +00:00
|
|
|
<?php } else { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
var grid = "No";
|
2020-12-07 17:28:19 +00:00
|
|
|
<?php } ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
initmap(grid, 'custommap', {
|
|
|
|
|
'initmap_only': true
|
|
|
|
|
});
|
2023-12-13 22:54:04 +01:00
|
|
|
// Check and change date if to < from //
|
|
|
|
|
$('.custom-map-QSOs input[name="to"]').off('change').on('change', function() {
|
2024-02-22 16:56:21 +00:00
|
|
|
if ($('.custom-map-QSOs input[name="to"]').val().replaceAll('-', '') < $('.custom-map-QSOs input[name="from"]').val().replaceAll('-', '')) {
|
2023-12-13 22:54:04 +01:00
|
|
|
$('.custom-map-QSOs input[name="from"]').val($('.custom-map-QSOs input[name="to"]').val());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
$('.custom-map-QSOs input[name="from"]').off('change').on('change', function() {
|
2024-02-22 16:56:21 +00:00
|
|
|
if ($('.custom-map-QSOs input[name="from"]').val().replaceAll('-', '') > $('.custom-map-QSOs input[name="to"]').val().replaceAll('-', '')) {
|
2023-12-13 22:54:04 +01:00
|
|
|
$('.custom-map-QSOs input[name="to"]').val($('.custom-map-QSOs input[name="from"]').val());
|
|
|
|
|
}
|
|
|
|
|
});
|
2023-12-13 07:10:10 +01:00
|
|
|
// Form "submit" //
|
2024-02-22 16:56:21 +00:00
|
|
|
$('.custom-map-QSOs .btn_submit_map_custom').off('click').on('click', function() {
|
2025-08-10 18:29:47 +01:00
|
|
|
// Show loading spinner and disable button
|
|
|
|
|
const $button = $(this);
|
|
|
|
|
const $spinner = $('#load-spinner');
|
|
|
|
|
|
|
|
|
|
$button.prop('disabled', true);
|
|
|
|
|
$spinner.removeClass('d-none');
|
|
|
|
|
|
|
|
|
|
// Hide any previous alerts
|
|
|
|
|
$('.warningOnSubmit').hide();
|
|
|
|
|
$('#map-success-alert').addClass('d-none');
|
|
|
|
|
|
2025-08-10 22:08:31 +01:00
|
|
|
// Failsafe timeout to prevent stuck spinner (60 seconds)
|
2025-08-10 18:29:47 +01:00
|
|
|
const failsafeTimeout = setTimeout(function() {
|
|
|
|
|
console.warn('Map loading timed out - forcing spinner hide');
|
|
|
|
|
$button.prop('disabled', false);
|
|
|
|
|
$spinner.addClass('d-none');
|
|
|
|
|
$('.warningOnSubmit .warningOnSubmit_txt').text('Map loading timed out. Please try again.');
|
|
|
|
|
$('.warningOnSubmit').show();
|
2025-08-10 22:08:31 +01:00
|
|
|
}, 60000);
|
2025-08-10 18:29:47 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
var customdata = {
|
|
|
|
|
'dataPost': {
|
|
|
|
|
'date_from': $('.custom-map-QSOs input[name="from"]').val(),
|
|
|
|
|
'date_to': $('.custom-map-QSOs input[name="to"]').val(),
|
|
|
|
|
'band': $('.custom-map-QSOs select[name="band"]').val(),
|
|
|
|
|
'mode': $('.custom-map-QSOs select[name="mode"]').val(),
|
|
|
|
|
'prop_mode': $('.custom-map-QSOs select[name="prop_mode"]').val(),
|
|
|
|
|
'isCustom': true
|
|
|
|
|
},
|
|
|
|
|
'map_id': '#custommap'
|
|
|
|
|
};
|
2025-08-10 18:29:47 +01:00
|
|
|
|
|
|
|
|
// Add success and error callbacks to the customdata
|
|
|
|
|
customdata.onSuccess = function(plotjson) {
|
2025-08-10 22:06:11 +01:00
|
|
|
console.log('Map loading success callback called with data:', plotjson);
|
|
|
|
|
|
2025-08-10 18:29:47 +01:00
|
|
|
// Clear the failsafe timeout
|
|
|
|
|
clearTimeout(failsafeTimeout);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Update statistics
|
|
|
|
|
if (typeof updateMapStatistics === 'function') {
|
2025-08-10 22:06:11 +01:00
|
|
|
console.log('Calling updateMapStatistics function');
|
2025-08-10 18:29:47 +01:00
|
|
|
updateMapStatistics(plotjson);
|
2025-08-10 22:06:11 +01:00
|
|
|
} else {
|
|
|
|
|
console.warn('updateMapStatistics function not found');
|
2025-08-10 18:29:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Show success message
|
|
|
|
|
const qsoCount = plotjson['markers'] ? plotjson['markers'].length : 0;
|
2025-08-10 22:06:11 +01:00
|
|
|
console.log('QSO count:', qsoCount);
|
2025-08-10 18:29:47 +01:00
|
|
|
$('#qso-count-display').text(`Loaded ${qsoCount} QSOs successfully`);
|
|
|
|
|
$('#map-success-alert').removeClass('d-none');
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
$('#map-success-alert').addClass('d-none');
|
|
|
|
|
}, 3000);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error in success callback:', error);
|
|
|
|
|
$('.warningOnSubmit .warningOnSubmit_txt').text('Map loaded but encountered an error displaying statistics.');
|
|
|
|
|
$('.warningOnSubmit').show();
|
|
|
|
|
} finally {
|
|
|
|
|
// Always re-enable button and hide spinner
|
2025-08-10 22:06:11 +01:00
|
|
|
console.log('Re-enabling button and hiding spinner');
|
2025-08-10 18:29:47 +01:00
|
|
|
$button.prop('disabled', false);
|
|
|
|
|
$spinner.addClass('d-none');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
customdata.onError = function() {
|
2025-08-10 22:06:11 +01:00
|
|
|
console.log('Map loading error callback called');
|
|
|
|
|
|
2025-08-10 18:29:47 +01:00
|
|
|
// Clear the failsafe timeout
|
|
|
|
|
clearTimeout(failsafeTimeout);
|
|
|
|
|
|
|
|
|
|
$('.warningOnSubmit .warningOnSubmit_txt').text('Failed to load map data. Please try again.');
|
|
|
|
|
$('.warningOnSubmit').show();
|
|
|
|
|
|
|
|
|
|
// Re-enable button and hide spinner
|
|
|
|
|
$button.prop('disabled', false);
|
|
|
|
|
$spinner.addClass('d-none');
|
|
|
|
|
};
|
|
|
|
|
|
2023-12-13 22:54:04 +01:00
|
|
|
initplot(qso_loc, customdata);
|
2023-12-13 07:10:10 +01:00
|
|
|
})
|
2024-02-22 16:56:21 +00:00
|
|
|
});
|
2024-10-12 16:56:04 +02:00
|
|
|
|
|
|
|
|
function get_oldest_qso_date() {
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: base_url + 'index.php/map/get_oldest_qso_date',
|
|
|
|
|
type: 'post',
|
|
|
|
|
success: function(data) {
|
|
|
|
|
document.getElementById('from').value = data;
|
|
|
|
|
document.getElementById('to').value = new Date().toISOString().split('T')[0];
|
2025-08-10 18:29:47 +01:00
|
|
|
// Update the date range display
|
|
|
|
|
if (typeof updateDateRangeDisplay === 'function') {
|
|
|
|
|
updateDateRangeDisplay();
|
|
|
|
|
}
|
2024-10-12 16:56:04 +02:00
|
|
|
},
|
2024-12-06 15:07:52 +00:00
|
|
|
error: function() {},
|
2024-10-12 16:56:04 +02:00
|
|
|
});
|
|
|
|
|
}
|
2020-12-07 16:32:12 +00:00
|
|
|
</script>
|
|
|
|
|
<?php } ?>
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->uri->segment(1) == "" || $this->uri->segment(1) == "dashboard") { ?>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/leaflet/L.Maidenhead.js"></script>
|
2023-11-29 20:16:53 +01:00
|
|
|
|
2024-05-20 15:57:58 +01:00
|
|
|
<script>
|
|
|
|
|
var iconsList = {
|
|
|
|
|
'qso': {
|
|
|
|
|
'color': '#FF0000',
|
|
|
|
|
'icon': 'fas fa-dot-circle'
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
// jquery onready
|
|
|
|
|
$(document).ready(function() {
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: base_url + 'index.php/user_options/get_map_custom',
|
|
|
|
|
type: 'GET',
|
|
|
|
|
dataType: 'json',
|
|
|
|
|
error: function() {
|
|
|
|
|
console.log('[ERROR] ajax get_map_custom() function return error.');
|
|
|
|
|
},
|
|
|
|
|
success: function(json_mapinfo) {
|
|
|
|
|
console.log(json_mapinfo);
|
|
|
|
|
if (typeof json_mapinfo.qso !== "undefined") {
|
|
|
|
|
iconsList = json_mapinfo;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
console.log(iconsList);
|
|
|
|
|
</script>
|
|
|
|
|
|
2019-03-20 16:58:06 +00:00
|
|
|
<script type="text/javascript">
|
2024-02-22 16:56:21 +00:00
|
|
|
$(function() {
|
|
|
|
|
$('[data-bs-toggle="tooltip"]').tooltip()
|
|
|
|
|
});
|
2020-02-27 22:18:54 +00:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($qra == "set") { ?>
|
|
|
|
|
var q_lat = <?php echo $qra_lat; ?>;
|
|
|
|
|
var q_lng = <?php echo $qra_lng; ?>;
|
2019-03-20 16:58:06 +00:00
|
|
|
<?php } else { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
var q_lat = 40.313043;
|
|
|
|
|
var q_lng = -32.695312;
|
2019-03-20 16:58:06 +00:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
var qso_loc = '<?php echo site_url('map/map_plot_json'); ?>';
|
2020-08-22 23:34:15 +01:00
|
|
|
var q_zoom = 3;
|
2024-05-16 14:46:24 +01:00
|
|
|
var osmUrl = '<?php echo $this->optionslib->get_option('option_map_tile_server'); ?>';
|
|
|
|
|
var osmCopyright = '<?php echo $this->optionslib->get_option('map_tile_server_copyright'); ?>';
|
|
|
|
|
|
|
|
|
|
var redIconImg = L.icon({
|
|
|
|
|
iconUrl: icon_dot_url,
|
|
|
|
|
iconSize: [10, 10]
|
|
|
|
|
});
|
2019-03-20 16:58:06 +00:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
$(document).ready(function() {
|
2019-06-24 17:32:22 +01:00
|
|
|
<?php if ($this->config->item('map_gridsquares') != FALSE) { ?>
|
2024-05-16 15:10:09 +01:00
|
|
|
var ShowGrid = "Yes";
|
2019-06-24 17:32:22 +01:00
|
|
|
<?php } else { ?>
|
2024-05-16 15:10:09 +01:00
|
|
|
var ShowGrid = "No";
|
2019-06-24 17:32:22 +01:00
|
|
|
<?php } ?>
|
2024-05-16 14:46:24 +01:00
|
|
|
|
2024-05-19 17:50:10 +01:00
|
|
|
var layer = L.tileLayer(osmUrl, {
|
|
|
|
|
maxZoom: 18,
|
|
|
|
|
attribution: osmCopyright,
|
|
|
|
|
});
|
2024-05-16 15:10:09 +01:00
|
|
|
|
2024-05-19 17:50:10 +01:00
|
|
|
var map = L.map('map', {
|
|
|
|
|
layers: [layer],
|
|
|
|
|
center: [q_lat, q_lng],
|
|
|
|
|
zoom: q_zoom,
|
|
|
|
|
fullscreenControl: true,
|
|
|
|
|
fullscreenControlOptions: {
|
|
|
|
|
position: 'topleft'
|
|
|
|
|
},
|
|
|
|
|
});
|
2024-05-16 15:10:09 +01:00
|
|
|
|
2026-03-26 22:39:04 +00:00
|
|
|
var markersLayer = L.layerGroup().addTo(map);
|
|
|
|
|
|
|
|
|
|
var baseLayers = {
|
|
|
|
|
"Map": layer
|
|
|
|
|
};
|
|
|
|
|
var overlays = {
|
|
|
|
|
"QSOs": markersLayer
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var greylineEnabled = <?php echo (isset($dashboard_map_greyline) && $dashboard_map_greyline) ? 'true' : 'false'; ?>;
|
|
|
|
|
var greyline = L.terminator({
|
2026-03-26 22:45:51 +00:00
|
|
|
color: '#4a6fa5',
|
|
|
|
|
fillColor: '#001f3f',
|
|
|
|
|
fillOpacity: 0.18,
|
|
|
|
|
weight: 1.5,
|
2026-03-26 22:39:04 +00:00
|
|
|
interactive: false
|
|
|
|
|
});
|
|
|
|
|
var greylineIntervalId = null;
|
|
|
|
|
|
|
|
|
|
function refreshGreyline() {
|
|
|
|
|
greyline.setTime(new Date());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function startGreylineUpdates() {
|
|
|
|
|
if (greylineIntervalId !== null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
refreshGreyline();
|
|
|
|
|
greylineIntervalId = setInterval(refreshGreyline, 60000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function stopGreylineUpdates() {
|
|
|
|
|
if (greylineIntervalId !== null) {
|
|
|
|
|
clearInterval(greylineIntervalId);
|
|
|
|
|
greylineIntervalId = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
overlays["Greyline"] = greyline;
|
|
|
|
|
|
|
|
|
|
if (greylineEnabled) {
|
|
|
|
|
greyline.addTo(map);
|
|
|
|
|
startGreylineUpdates();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
L.control.layers(baseLayers, overlays, {
|
|
|
|
|
collapsed: true
|
|
|
|
|
}).addTo(map);
|
|
|
|
|
|
|
|
|
|
map.on('overlayadd', function(event) {
|
|
|
|
|
if (event.layer === greyline) {
|
|
|
|
|
startGreylineUpdates();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
map.on('overlayremove', function(event) {
|
|
|
|
|
if (event.layer === greyline) {
|
|
|
|
|
stopGreylineUpdates();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
map.on('unload', function() {
|
|
|
|
|
stopGreylineUpdates();
|
|
|
|
|
});
|
|
|
|
|
|
2024-05-19 17:50:10 +01:00
|
|
|
/*var printer = L.easyPrint({
|
2024-05-16 14:46:24 +01:00
|
|
|
sizeModes: ['Current'],
|
|
|
|
|
filename: 'myMap',
|
|
|
|
|
exportOnly: true,
|
|
|
|
|
hideControlContainer: true
|
2024-05-19 17:50:10 +01:00
|
|
|
}).addTo(map);*/
|
2024-05-16 14:46:24 +01:00
|
|
|
|
|
|
|
|
var markers = {};
|
|
|
|
|
|
|
|
|
|
function loadMarkers() {
|
|
|
|
|
fetch(qso_loc)
|
|
|
|
|
.then(response => response.json())
|
|
|
|
|
.then(data => {
|
2024-06-19 13:46:31 +01:00
|
|
|
if (data.error !== "No QSOs found") {
|
|
|
|
|
var newMarkers = {};
|
|
|
|
|
data.markers.forEach(marker => {
|
|
|
|
|
var key = `${marker.lat},${marker.lng}`;
|
|
|
|
|
var html = `<h3>${marker.flag}${marker.label}</h3> ${marker.html}`;
|
|
|
|
|
newMarkers[key] = marker;
|
|
|
|
|
if (!markers[key]) {
|
|
|
|
|
var icon = L.divIcon({
|
|
|
|
|
className: 'custom-icon',
|
|
|
|
|
html: `<i class="${iconsList.qso.icon}" style="color:${iconsList.qso.color}"></i>`
|
2024-12-06 15:07:52 +00:00
|
|
|
});
|
|
|
|
|
|
2026-03-26 22:39:04 +00:00
|
|
|
markers[key] = L.marker([marker.lat, marker.lng], {
|
2024-06-19 13:46:31 +01:00
|
|
|
icon: icon
|
|
|
|
|
})
|
2026-03-26 22:39:04 +00:00
|
|
|
.addTo(markersLayer)
|
2024-06-19 13:46:31 +01:00
|
|
|
.bindPopup(html);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
Object.keys(markers).forEach(key => {
|
|
|
|
|
if (!newMarkers[key]) {
|
2026-03-26 22:39:04 +00:00
|
|
|
markersLayer.removeLayer(markers[key]);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
Object.keys(markers).forEach(key => {
|
|
|
|
|
if (newMarkers[key]) {
|
|
|
|
|
newMarkers[key] = markers[key];
|
2024-06-19 13:46:31 +01:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
markers = newMarkers;
|
2024-12-06 15:07:52 +00:00
|
|
|
} else {
|
|
|
|
|
console.log("No QSOs found to populate dashboard map.");
|
|
|
|
|
}
|
2024-05-16 14:46:24 +01:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loadMarkers();
|
|
|
|
|
setInterval(loadMarkers, 5000);
|
2019-06-24 17:32:22 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
});
|
2019-03-20 16:58:06 +00:00
|
|
|
</script>
|
2019-04-08 15:36:23 +01:00
|
|
|
<?php } ?>
|
2019-03-20 16:58:06 +00:00
|
|
|
|
|
|
|
|
|
2019-01-13 15:59:16 +00:00
|
|
|
|
|
|
|
|
<?php if ($this->uri->segment(1) == "radio") { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<!-- If this is the admin/radio page run the JS -->
|
|
|
|
|
<script type="text/javascript">
|
|
|
|
|
$(document).ready(function() {
|
2025-11-22 17:36:26 +00:00
|
|
|
// Load radio status immediately on page load
|
|
|
|
|
function loadRadioStatus() {
|
2024-02-22 16:56:21 +00:00
|
|
|
$.get('radio/status/', function(result) {
|
2025-11-22 17:36:26 +00:00
|
|
|
$('#radio-loading').hide();
|
|
|
|
|
$('.status').html(result).show();
|
2024-02-22 16:56:21 +00:00
|
|
|
});
|
2025-11-22 17:36:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Load immediately
|
|
|
|
|
loadRadioStatus();
|
|
|
|
|
|
|
|
|
|
// Then refresh every 2 seconds
|
|
|
|
|
setInterval(loadRadioStatus, 2000);
|
2024-02-22 16:56:21 +00:00
|
|
|
});
|
|
|
|
|
</script>
|
2019-01-13 15:59:16 +00:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2019-05-14 11:52:11 +01:00
|
|
|
|
2022-10-31 15:27:54 +01:00
|
|
|
|
2022-01-12 20:51:24 +01:00
|
|
|
<script type="text/javascript">
|
2024-02-22 16:56:21 +00:00
|
|
|
$(function() {
|
|
|
|
|
$('[data-bs-toggle="tooltip"]').tooltip()
|
|
|
|
|
});
|
2022-01-12 20:51:24 +01:00
|
|
|
</script>
|
2022-10-31 15:27:54 +01:00
|
|
|
|
|
|
|
|
<?php if ($this->uri->segment(1) == "search") { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script type="text/javascript">
|
2026-08-06 15:05:04 +01:00
|
|
|
var RECENT_SEARCHES_KEY = 'cloudlog.recentSearches';
|
|
|
|
|
var MAX_RECENT_SEARCHES = 8;
|
|
|
|
|
|
|
|
|
|
function preventEvent(eventObj) {
|
|
|
|
|
if (eventObj && typeof eventObj.preventDefault === 'function') {
|
|
|
|
|
eventObj.preventDefault();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (typeof window !== 'undefined' && window.event) {
|
|
|
|
|
window.event.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeSearchInput(value) {
|
|
|
|
|
return String(value || '').trim().toUpperCase();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isExactMatchEnabled() {
|
|
|
|
|
return $('#exact_match').length > 0 && $('#exact_match').is(':checked');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function buildSearchResultUrl(callsign, exactMatch) {
|
|
|
|
|
var url = "logbook/search_result/" + encodeURI(callsign.replace(/Ø/g, '0'));
|
|
|
|
|
if (exactMatch) {
|
|
|
|
|
url += '?exact=1';
|
|
|
|
|
}
|
|
|
|
|
return url;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateSearchUrl(callsign, exactMatch) {
|
|
|
|
|
if (!window.history || typeof window.history.replaceState !== 'function') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var searchUrl = base_url + 'index.php/search';
|
|
|
|
|
if (callsign !== '') {
|
|
|
|
|
searchUrl += '?callsign=' + encodeURIComponent(callsign);
|
|
|
|
|
if (exactMatch) {
|
|
|
|
|
searchUrl += '&exact=1';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
window.history.replaceState({}, document.title, searchUrl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getRecentSearches() {
|
|
|
|
|
try {
|
|
|
|
|
var parsed = JSON.parse(localStorage.getItem(RECENT_SEARCHES_KEY) || '[]');
|
|
|
|
|
if (!Array.isArray(parsed)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
return parsed;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setRecentSearches(items) {
|
|
|
|
|
localStorage.setItem(RECENT_SEARCHES_KEY, JSON.stringify(items));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function rememberSearch(callsign, exactMatch) {
|
|
|
|
|
var normalizedCallsign = normalizeSearchInput(callsign);
|
|
|
|
|
if (normalizedCallsign === '') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var recent = getRecentSearches();
|
|
|
|
|
var entryKey = normalizedCallsign + '|' + (exactMatch ? '1' : '0');
|
|
|
|
|
var filtered = recent.filter(function(item) {
|
|
|
|
|
return item.key !== entryKey;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
filtered.unshift({
|
|
|
|
|
key: entryKey,
|
|
|
|
|
callsign: normalizedCallsign,
|
|
|
|
|
exact: exactMatch
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
setRecentSearches(filtered.slice(0, MAX_RECENT_SEARCHES));
|
|
|
|
|
renderRecentSearches();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderRecentSearches() {
|
|
|
|
|
var wrapper = $('#recent_searches_wrapper');
|
|
|
|
|
var container = $('#recent_searches');
|
2019-09-04 22:11:55 +01:00
|
|
|
|
2026-08-06 15:05:04 +01:00
|
|
|
if (wrapper.length === 0 || container.length === 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var recent = getRecentSearches();
|
|
|
|
|
if (recent.length === 0) {
|
|
|
|
|
wrapper.hide();
|
|
|
|
|
container.empty();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wrapper.show();
|
|
|
|
|
container.empty();
|
|
|
|
|
|
|
|
|
|
recent.forEach(function(item) {
|
|
|
|
|
var label = item.callsign + (item.exact ? ' (exact)' : '');
|
|
|
|
|
var chip = $('<button type="button" class="btn btn-sm btn-outline-secondary me-1 mb-1"></button>')
|
|
|
|
|
.text(label)
|
|
|
|
|
.attr('data-callsign', item.callsign)
|
|
|
|
|
.attr('data-exact', item.exact ? '1' : '0');
|
|
|
|
|
container.append(chip);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function initSearchResultsDataTable() {
|
|
|
|
|
var table = $('#partial_view table.contacttable, #partial_view #search-results-table').first();
|
|
|
|
|
if (table.length === 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$.fn.DataTable) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
table.DataTable({
|
|
|
|
|
destroy: true,
|
|
|
|
|
paging: true,
|
|
|
|
|
pageLength: 25,
|
|
|
|
|
lengthChange: true,
|
|
|
|
|
info: true,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: true,
|
|
|
|
|
order: [],
|
|
|
|
|
scrollX: true,
|
|
|
|
|
searching: false,
|
|
|
|
|
language: {
|
|
|
|
|
url: getDataTablesLanguageUrl(),
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Search results DataTable failed to initialize:', error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function loadSearchResult(callsign, syncUrl) {
|
|
|
|
|
var normalizedCallsign = normalizeSearchInput(callsign);
|
|
|
|
|
var exactMatch = isExactMatchEnabled();
|
|
|
|
|
|
|
|
|
|
$('#results_mode').text(exactMatch ? 'Exact match' : 'Partial match');
|
|
|
|
|
|
|
|
|
|
if (normalizedCallsign === '') {
|
|
|
|
|
$('#partial_view').empty();
|
|
|
|
|
$('#results_count').text('0 QSOs');
|
|
|
|
|
$('#results_card').hide();
|
|
|
|
|
if (syncUrl) {
|
|
|
|
|
updateSearchUrl('', exactMatch);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!exactMatch && normalizedCallsign.length < 2) {
|
|
|
|
|
$('#partial_view').html('<div class="alert alert-warning mt-2">Please enter at least 2 characters, or enable exact callsign match.</div>');
|
|
|
|
|
$('#results_count').text('0 QSOs');
|
|
|
|
|
$('#results_card').show();
|
|
|
|
|
if (syncUrl) {
|
|
|
|
|
updateSearchUrl(normalizedCallsign, exactMatch);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$('#callsign').val(normalizedCallsign);
|
|
|
|
|
$('#results_card').show();
|
|
|
|
|
$('#results_count').text('Searching...');
|
|
|
|
|
$('#partial_view').html('<div class="p-3 text-muted">Searching...</div>');
|
|
|
|
|
$('#partial_view').load(buildSearchResultUrl(normalizedCallsign, exactMatch), function() {
|
|
|
|
|
$('[data-bs-toggle="tooltip"]').tooltip();
|
|
|
|
|
|
|
|
|
|
initSearchResultsDataTable();
|
|
|
|
|
|
|
|
|
|
var rows = $('#partial_view tbody tr').length;
|
|
|
|
|
$('#results_count').text(rows + ' QSO' + (rows === 1 ? '' : 's'));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
rememberSearch(normalizedCallsign, exactMatch);
|
|
|
|
|
|
|
|
|
|
if (syncUrl) {
|
|
|
|
|
updateSearchUrl(normalizedCallsign, exactMatch);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function findduplicates(eventObj) {
|
|
|
|
|
preventEvent(eventObj);
|
2024-02-22 16:56:21 +00:00
|
|
|
$('#partial_view').load(base_url + "index.php/logbook/search_duplicates/" + $("#station_id").val(), function() {
|
|
|
|
|
$('.qsolist').DataTable({
|
|
|
|
|
"pageLength": 25,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: false,
|
|
|
|
|
"scrollY": "500px",
|
|
|
|
|
"scrollCollapse": true,
|
|
|
|
|
"paging": false,
|
|
|
|
|
"scrollX": true,
|
|
|
|
|
"language": {
|
|
|
|
|
url: getDataTablesLanguageUrl(),
|
|
|
|
|
},
|
|
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
// change color of csv-button if dark mode is chosen
|
|
|
|
|
if (isDarkModeTheme()) {
|
|
|
|
|
$(".buttons-csv").css("color", "white");
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-10-31 20:09:19 +01:00
|
|
|
}
|
|
|
|
|
|
2026-08-06 15:05:04 +01:00
|
|
|
function findlotwunconfirmed(eventObj) {
|
|
|
|
|
preventEvent(eventObj);
|
2024-02-22 16:56:21 +00:00
|
|
|
$('#partial_view').load(base_url + "index.php/logbook/search_lotw_unconfirmed/" + $("#station_id").val(), function() {
|
|
|
|
|
$('.qsolist').DataTable({
|
|
|
|
|
"pageLength": 25,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: false,
|
|
|
|
|
"scrollY": "500px",
|
|
|
|
|
"scrollCollapse": true,
|
|
|
|
|
"paging": false,
|
|
|
|
|
"scrollX": true,
|
|
|
|
|
"language": {
|
|
|
|
|
url: getDataTablesLanguageUrl(),
|
|
|
|
|
},
|
|
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
// change color of csv-button if dark mode is chosen
|
|
|
|
|
if (isDarkModeTheme()) {
|
|
|
|
|
$(".buttons-csv").css("color", "white");
|
|
|
|
|
}
|
|
|
|
|
});
|
2023-05-26 10:38:31 +02:00
|
|
|
}
|
|
|
|
|
|
2026-08-06 15:05:04 +01:00
|
|
|
function findincorrectcqzones(eventObj) {
|
|
|
|
|
preventEvent(eventObj);
|
2024-02-22 16:56:21 +00:00
|
|
|
$('#partial_view').load(base_url + "index.php/logbook/search_incorrect_cq_zones/" + $("#station_id").val(), function() {
|
|
|
|
|
$('.qsolist').DataTable({
|
|
|
|
|
"pageLength": 25,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: false,
|
|
|
|
|
"scrollY": "500px",
|
|
|
|
|
"scrollCollapse": true,
|
|
|
|
|
"paging": false,
|
|
|
|
|
"scrollX": true,
|
|
|
|
|
"language": {
|
|
|
|
|
url: getDataTablesLanguageUrl(),
|
|
|
|
|
},
|
|
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
// change color of csv-button if dark mode is chosen
|
|
|
|
|
if (isDarkModeTheme()) {
|
|
|
|
|
$(".buttons-csv").css("color", "white");
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-10-31 12:25:56 +01:00
|
|
|
}
|
|
|
|
|
|
2026-08-06 15:05:04 +01:00
|
|
|
function searchButtonPress(eventObj) {
|
|
|
|
|
preventEvent(eventObj);
|
|
|
|
|
loadSearchResult($('#callsign').val(), true);
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
2019-09-04 22:11:55 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
$(document).ready(function() {
|
2026-08-06 15:05:04 +01:00
|
|
|
if ($('#callsign').length) {
|
|
|
|
|
renderRecentSearches();
|
2019-05-14 11:52:11 +01:00
|
|
|
|
2026-08-06 15:05:04 +01:00
|
|
|
$('#search_box').on('submit', function(e) {
|
|
|
|
|
searchButtonPress(e);
|
2024-02-22 16:56:21 +00:00
|
|
|
});
|
2021-02-12 23:27:08 +01:00
|
|
|
|
2026-08-06 15:05:04 +01:00
|
|
|
$('#recent_searches').on('click', 'button[data-callsign]', function() {
|
|
|
|
|
var callsign = $(this).attr('data-callsign') || '';
|
|
|
|
|
var exact = $(this).attr('data-exact') === '1';
|
2019-05-14 11:52:11 +01:00
|
|
|
|
2026-08-06 15:05:04 +01:00
|
|
|
$('#callsign').val(callsign);
|
|
|
|
|
if ($('#exact_match').length) {
|
|
|
|
|
$('#exact_match').prop('checked', exact);
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
2026-08-06 15:05:04 +01:00
|
|
|
loadSearchResult(callsign, true);
|
|
|
|
|
});
|
2019-05-14 11:52:11 +01:00
|
|
|
|
2026-08-06 15:05:04 +01:00
|
|
|
$('#exact_match').on('change', function() {
|
|
|
|
|
var currentCallsign = normalizeSearchInput($('#callsign').val());
|
|
|
|
|
if (currentCallsign !== '') {
|
|
|
|
|
loadSearchResult(currentCallsign, true);
|
|
|
|
|
}
|
|
|
|
|
});
|
2019-09-04 22:11:55 +01:00
|
|
|
|
2026-08-06 15:05:04 +01:00
|
|
|
var initialSearch = normalizeSearchInput($('#callsign').val());
|
|
|
|
|
if (initialSearch !== '') {
|
|
|
|
|
loadSearchResult(initialSearch, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
});
|
|
|
|
|
</script>
|
2019-05-14 11:52:11 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2019-05-25 18:43:32 +01:00
|
|
|
<?php if ($this->uri->segment(1) == "logbook" && $this->uri->segment(2) != "view") { ?>
|
Add public Station Diary with images & RSS
Introduce public Station Diary functionality: add global config flag, routes, controller, model changes, views and DB migrations. New migrations (252-254) add is_public/include_qso_summary, diary_images table and logbook_id on notes. Notes model updated to handle public flags, diary images, qso summaries, caching (per-user cache version), and helper methods for listing/counting public diary entries. New Stationdiary controller serves paginated public pages and RSS feed with caching. Notes controller updated to handle image uploads, attach images to diary entries, and provide an AJAX endpoint to delete images. Views updated to support public visibility options, logbook selector, image upload UI and Quill editor assets; routes and migration version bumped accordingly. Includes file upload example added to uploads/diary.
2026-03-08 11:42:41 +00:00
|
|
|
<!-- Quill editor for Station Diary modal -->
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/plugins/quill/quill.min.js"></script>
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/leaflet/L.Maidenhead.js"></script>
|
|
|
|
|
<script id="leafembed" type="text/javascript" src="<?php echo base_url(); ?>assets/js/leaflet/leafembed.js" tileUrl="<?php echo $this->optionslib->get_option('option_map_tile_server'); ?>"></script>
|
2022-01-12 20:51:24 +01:00
|
|
|
<script type="text/javascript">
|
2024-02-22 16:56:21 +00:00
|
|
|
$(function() {
|
|
|
|
|
$('[data-bs-toggle="tooltip"]').tooltip()
|
|
|
|
|
});
|
2022-01-12 20:51:24 +01:00
|
|
|
</script>
|
2019-05-14 16:46:16 +01:00
|
|
|
<script type="text/javascript">
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($qra == "set") { ?>
|
|
|
|
|
var q_lat = <?php echo $qra_lat; ?>;
|
|
|
|
|
var q_lng = <?php echo $qra_lng; ?>;
|
2019-05-14 16:46:16 +01:00
|
|
|
<?php } else { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
var q_lat = 40.313043;
|
|
|
|
|
var q_lng = -32.695312;
|
2019-05-14 16:46:16 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2023-12-12 08:18:42 +01:00
|
|
|
var qso_loc = '<?php echo site_url('map/map_plot_json'); ?>';
|
2020-08-23 16:42:23 +01:00
|
|
|
var q_zoom = 3;
|
2019-05-14 16:46:16 +01:00
|
|
|
|
2019-06-24 18:21:02 +01:00
|
|
|
<?php if ($this->config->item('map_gridsquares') != FALSE) { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
var grid = "Yes";
|
2019-06-24 18:21:02 +01:00
|
|
|
<?php } else { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
var grid = "No";
|
2019-06-24 18:21:02 +01:00
|
|
|
<?php } ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
initmap(grid, 'map', {
|
|
|
|
|
'dataPost': {
|
|
|
|
|
'nb_qso': '25',
|
|
|
|
|
'offset': '<?php echo $this->uri->segment(3); ?>'
|
|
|
|
|
}
|
|
|
|
|
});
|
2019-05-14 16:46:16 +01:00
|
|
|
</script>
|
2019-05-21 13:44:22 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
|
|
|
|
<?php if ($this->uri->segment(1) == "qso") { ?>
|
2023-08-01 16:24:33 +01:00
|
|
|
|
2026-03-29 13:57:36 +01:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/qra-utils.js"></script>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/qso.js"></script>
|
2026-05-14 22:34:51 +01:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/cw-sidetone.js"></script>
|
2026-05-16 13:44:09 +01:00
|
|
|
<?php if (isset($isRemoteOperationEnabled) ? $isRemoteOperationEnabled : $this->session->userdata('isRemoteOperationEnabled')) { ?>
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/remote-operation.js"></script>
|
|
|
|
|
<?php } ?>
|
2025-05-28 14:24:03 +01:00
|
|
|
<?php if ($this->session->userdata('isWinkeyEnabled') && !$this->session->userdata('isWinkeyWebsocketEnabled')) { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/winkey.js"></script>
|
2025-05-28 14:24:03 +01:00
|
|
|
<?php } elseif ($this->session->userdata('isWinkeyEnabled') && $this->session->userdata('isWinkeyWebsocketEnabled')) { ?>
|
|
|
|
|
<script>
|
|
|
|
|
console.log('Winkey Websocket enabled');
|
2025-10-06 14:45:31 +01:00
|
|
|
|
|
|
|
|
// Mode detection for WebSocket Winkey
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
const ModeSelected = document.getElementById('mode');
|
|
|
|
|
|
|
|
|
|
function toggleWinkeyVisibility() {
|
|
|
|
|
const winkeyElement = document.getElementById('winkey');
|
|
|
|
|
if (ModeSelected && winkeyElement) {
|
|
|
|
|
if (ModeSelected.value === 'CW') {
|
|
|
|
|
winkeyElement.style.display = 'block';
|
|
|
|
|
} else {
|
|
|
|
|
winkeyElement.style.display = 'none';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check initial mode
|
|
|
|
|
toggleWinkeyVisibility();
|
|
|
|
|
|
|
|
|
|
// Listen for mode changes
|
|
|
|
|
if (ModeSelected) {
|
|
|
|
|
ModeSelected.addEventListener('change', toggleWinkeyVisibility);
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-05-28 14:24:03 +01:00
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
let ws = null;
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
let wsRelayEnabled = false;
|
|
|
|
|
let wsRelayRoom = 'cw_room';
|
2026-05-15 14:19:48 +01:00
|
|
|
let wsRelayToken = '';
|
|
|
|
|
let wsRelayUrl = 'wss://relay.cloudlog.org/';
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
|
|
|
|
|
const RELAY_STORAGE = {
|
|
|
|
|
enabled: 'cloudlog.winkeyRelay.enabled',
|
|
|
|
|
url: 'cloudlog.winkeyRelay.url',
|
|
|
|
|
token: 'cloudlog.winkeyRelay.token',
|
|
|
|
|
room: 'cloudlog.winkeyRelay.room'
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-15 14:19:48 +01:00
|
|
|
async function loadRelaySettingsFromAccount() {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch(base_url + 'index.php/qso/winkeyrelaysettings_json');
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
if (data.status === 'ok' && data.settings) {
|
|
|
|
|
wsRelayEnabled = !!data.settings.enabled;
|
|
|
|
|
wsRelayUrl = data.settings.url || 'wss://relay.cloudlog.org/';
|
|
|
|
|
wsRelayRoom = data.settings.room || 'cw_room';
|
|
|
|
|
wsRelayToken = data.settings.token || '';
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Failed to load relay settings from account', error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Backward compatibility fallback from previous localStorage implementation.
|
|
|
|
|
wsRelayEnabled = localStorage.getItem(RELAY_STORAGE.enabled) === '1';
|
|
|
|
|
wsRelayUrl = localStorage.getItem(RELAY_STORAGE.url) || 'wss://relay.cloudlog.org/';
|
|
|
|
|
wsRelayRoom = localStorage.getItem(RELAY_STORAGE.room) || 'cw_room';
|
|
|
|
|
wsRelayToken = localStorage.getItem('cloudlog.winkeyRelay.token') || '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function saveRelaySettingsToAccount(config) {
|
|
|
|
|
const payload = new URLSearchParams();
|
|
|
|
|
payload.append('enabled', config.enabled ? '1' : '0');
|
|
|
|
|
payload.append('url', config.url);
|
|
|
|
|
payload.append('room', config.room);
|
|
|
|
|
payload.append('token', config.token);
|
|
|
|
|
|
|
|
|
|
const response = await fetch(base_url + 'index.php/qso/winkeyrelaysettings_save', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
|
|
|
|
},
|
|
|
|
|
body: payload.toString()
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
if (!response.ok || data.status !== 'ok') {
|
|
|
|
|
throw new Error(data.message || 'Failed to save relay token');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wsRelayEnabled = config.enabled;
|
|
|
|
|
wsRelayUrl = config.url;
|
|
|
|
|
wsRelayRoom = config.room;
|
|
|
|
|
wsRelayToken = config.token;
|
|
|
|
|
|
|
|
|
|
// Clear legacy local storage keys after successful account save.
|
|
|
|
|
localStorage.removeItem(RELAY_STORAGE.enabled);
|
|
|
|
|
localStorage.removeItem(RELAY_STORAGE.url);
|
|
|
|
|
localStorage.removeItem(RELAY_STORAGE.room);
|
|
|
|
|
localStorage.removeItem(RELAY_STORAGE.token);
|
|
|
|
|
}
|
|
|
|
|
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
function getRelayConfig() {
|
|
|
|
|
return {
|
2026-05-15 14:19:48 +01:00
|
|
|
enabled: wsRelayEnabled,
|
|
|
|
|
url: wsRelayUrl,
|
|
|
|
|
token: wsRelayToken,
|
|
|
|
|
room: wsRelayRoom
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setSocketStatus(text, className) {
|
|
|
|
|
const statusBadge = document.getElementById('cw_socket_status');
|
|
|
|
|
if (!statusBadge) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
statusBadge.className = className;
|
|
|
|
|
statusBadge.innerHTML = text;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getTransportLabel() {
|
|
|
|
|
return wsRelayEnabled ? 'Relay' : 'Direct';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function openWinkeyRelaySettings() {
|
|
|
|
|
const config = getRelayConfig();
|
|
|
|
|
|
|
|
|
|
const enabledField = document.getElementById('winkeyRelayEnabled');
|
|
|
|
|
const urlField = document.getElementById('winkeyRelayUrl');
|
|
|
|
|
const tokenField = document.getElementById('winkeyRelayToken');
|
|
|
|
|
const roomField = document.getElementById('winkeyRelayRoom');
|
|
|
|
|
|
|
|
|
|
if (enabledField) {
|
|
|
|
|
enabledField.checked = config.enabled;
|
|
|
|
|
}
|
|
|
|
|
if (urlField) {
|
|
|
|
|
urlField.value = config.url;
|
|
|
|
|
}
|
|
|
|
|
if (tokenField) {
|
|
|
|
|
tokenField.value = config.token;
|
|
|
|
|
}
|
|
|
|
|
if (roomField) {
|
|
|
|
|
roomField.value = config.room;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const modalEl = document.getElementById('winkeyRelaySettingsModal');
|
|
|
|
|
if (modalEl) {
|
|
|
|
|
bootstrap.Modal.getOrCreateInstance(modalEl).show();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-15 14:19:48 +01:00
|
|
|
async function saveWinkeyRelaySettings() {
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
const enabledField = document.getElementById('winkeyRelayEnabled');
|
|
|
|
|
const urlField = document.getElementById('winkeyRelayUrl');
|
|
|
|
|
const tokenField = document.getElementById('winkeyRelayToken');
|
|
|
|
|
const roomField = document.getElementById('winkeyRelayRoom');
|
|
|
|
|
|
|
|
|
|
const config = {
|
|
|
|
|
enabled: enabledField ? enabledField.checked : false,
|
|
|
|
|
url: urlField ? urlField.value.trim() : '',
|
|
|
|
|
token: tokenField ? tokenField.value.trim() : '',
|
|
|
|
|
room: roomField ? roomField.value.trim() : 'cw_room'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (config.enabled) {
|
|
|
|
|
if (!config.url.startsWith('ws://') && !config.url.startsWith('wss://')) {
|
|
|
|
|
alert('Relay URL must start with ws:// or wss://');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (config.token.length < 8) {
|
|
|
|
|
alert('Relay token must be at least 8 characters');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (config.room === '') {
|
|
|
|
|
alert('Relay room is required');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (config.room === '') {
|
|
|
|
|
config.room = 'cw_room';
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-15 14:19:48 +01:00
|
|
|
try {
|
|
|
|
|
await saveRelaySettingsToAccount(config);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
alert(error.message || 'Failed to save relay settings');
|
|
|
|
|
return;
|
|
|
|
|
}
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
|
|
|
|
|
const modalEl = document.getElementById('winkeyRelaySettingsModal');
|
|
|
|
|
if (modalEl) {
|
|
|
|
|
bootstrap.Modal.getOrCreateInstance(modalEl).hide();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logMessage('Relay settings saved');
|
|
|
|
|
connectWebSocket();
|
|
|
|
|
}
|
2025-05-28 14:24:03 +01:00
|
|
|
|
|
|
|
|
function connectWebSocket() {
|
|
|
|
|
if (ws !== null) {
|
|
|
|
|
ws.close();
|
|
|
|
|
}
|
|
|
|
|
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
const relayConfig = getRelayConfig();
|
|
|
|
|
wsRelayEnabled = relayConfig.enabled;
|
|
|
|
|
wsRelayRoom = relayConfig.room || 'cw_room';
|
2026-05-15 14:19:48 +01:00
|
|
|
wsRelayUrl = relayConfig.url || 'wss://relay.cloudlog.org/';
|
|
|
|
|
wsRelayToken = relayConfig.token || '';
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
|
|
|
|
|
if (wsRelayEnabled) {
|
2026-05-15 14:19:48 +01:00
|
|
|
if ((!wsRelayUrl.startsWith('ws://') && !wsRelayUrl.startsWith('wss://')) || wsRelayToken.length < 8) {
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
setSocketStatus('Status: Relay Config Error', 'badge bg-danger');
|
|
|
|
|
logMessage('Relay settings are invalid. Open Relay settings to fix URL/token.');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const wsUrl = wsRelayEnabled
|
2026-05-15 14:19:48 +01:00
|
|
|
? wsRelayUrl
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
: `ws://localhost:8181?chatRoom=${encodeURIComponent(wsRelayRoom)}`;
|
|
|
|
|
|
|
|
|
|
setSocketStatus('Status: Connecting (' + getTransportLabel() + ')', 'badge bg-warning text-dark');
|
|
|
|
|
logMessage('Connecting to ' + wsUrl);
|
2025-05-28 14:24:03 +01:00
|
|
|
|
|
|
|
|
ws = new WebSocket(wsUrl);
|
|
|
|
|
|
|
|
|
|
ws.onopen = function() {
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
if (wsRelayEnabled) {
|
|
|
|
|
const joinMessage = {
|
|
|
|
|
op: 'join',
|
2026-05-15 14:19:48 +01:00
|
|
|
token: wsRelayToken,
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
role: 'browser',
|
|
|
|
|
rooms: [wsRelayRoom]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ws.send(JSON.stringify(joinMessage));
|
|
|
|
|
setSocketStatus('Status: Authenticating (Relay)', 'badge bg-warning text-dark');
|
|
|
|
|
logMessage('Sent relay join request for room: ' + wsRelayRoom);
|
|
|
|
|
} else {
|
|
|
|
|
setSocketStatus('Status: Connected (Direct)', 'badge bg-success');
|
|
|
|
|
logMessage('Connected to WebSocket server in room: ' + wsRelayRoom);
|
|
|
|
|
setTimeout(getCwSpeed, 1000);
|
|
|
|
|
}
|
2025-05-28 14:24:03 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ws.onclose = function() {
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
setSocketStatus('Status: Disconnected (' + getTransportLabel() + ')', 'badge bg-secondary');
|
2025-05-28 14:24:03 +01:00
|
|
|
logMessage('Disconnected from WebSocket server');
|
|
|
|
|
ws = null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ws.onerror = function(error) {
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
setSocketStatus('Status: Error', 'badge bg-danger');
|
2025-05-28 14:24:03 +01:00
|
|
|
logMessage('WebSocket Error: ' + error);
|
|
|
|
|
};
|
2023-08-01 16:24:33 +01:00
|
|
|
|
2025-05-28 14:24:03 +01:00
|
|
|
ws.onmessage = function(event) {
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
let message = event.data;
|
|
|
|
|
|
|
|
|
|
if (wsRelayEnabled) {
|
|
|
|
|
try {
|
|
|
|
|
const relayMessage = JSON.parse(event.data);
|
|
|
|
|
if (relayMessage && relayMessage.op === 'joined') {
|
|
|
|
|
setSocketStatus('Status: Connected (Relay)', 'badge bg-success');
|
|
|
|
|
logMessage('Relay joined room: ' + wsRelayRoom);
|
|
|
|
|
setTimeout(getCwSpeed, 1000);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (relayMessage && relayMessage.op === 'frame') {
|
|
|
|
|
if (relayMessage.room !== wsRelayRoom) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
message = relayMessage.data;
|
|
|
|
|
} else if (relayMessage && relayMessage.op === 'error') {
|
|
|
|
|
setSocketStatus('Status: Relay Error', 'badge bg-danger');
|
|
|
|
|
logMessage('Relay error: ' + (relayMessage.message || 'unknown'));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
// Keep compatibility with plain text payloads.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logMessage('Received: ' + message);
|
|
|
|
|
|
2025-10-06 14:42:03 +01:00
|
|
|
if (message.startsWith('CWSPEED SET:')) {
|
|
|
|
|
const match = message.match(/CWSPEED SET: (\d+) WPM/);
|
|
|
|
|
if (match) {
|
|
|
|
|
updateSpeedDisplay(parseInt(match[1]));
|
|
|
|
|
}
|
|
|
|
|
} else if (message.startsWith('CWSPEED:')) {
|
|
|
|
|
const match = message.match(/CWSPEED: (\d+) WPM/);
|
|
|
|
|
if (match) {
|
|
|
|
|
updateSpeedDisplay(parseInt(match[1]));
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-28 14:24:03 +01:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function disconnectWebSocket() {
|
|
|
|
|
if (ws !== null) {
|
|
|
|
|
ws.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
function sendWinkeyCommand(command, quiet) {
|
|
|
|
|
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
|
|
|
if (!quiet) {
|
|
|
|
|
alert('Please connect to the WebSocket server first');
|
|
|
|
|
}
|
2025-05-28 14:24:03 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
if (wsRelayEnabled) {
|
|
|
|
|
const relayFrame = {
|
|
|
|
|
op: 'frame',
|
|
|
|
|
room: wsRelayRoom,
|
|
|
|
|
data: command
|
|
|
|
|
};
|
|
|
|
|
ws.send(JSON.stringify(relayFrame));
|
|
|
|
|
} else {
|
|
|
|
|
ws.send(command);
|
2025-05-28 14:24:03 +01:00
|
|
|
}
|
|
|
|
|
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
logMessage('Sent: ' + command);
|
2025-05-28 14:24:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function logMessage(message) {
|
|
|
|
|
const messageLog = document.getElementById('messageLog');
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
if (!messageLog) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-05-28 14:24:03 +01:00
|
|
|
messageLog.value += message + '\n';
|
|
|
|
|
// Auto-scroll to bottom
|
|
|
|
|
messageLog.scrollTop = messageLog.scrollHeight;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-14 22:36:19 +01:00
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
const sendTextElement = document.getElementById('sendText');
|
|
|
|
|
if (sendTextElement) {
|
|
|
|
|
sendTextElement.addEventListener('keydown', function(e) {
|
|
|
|
|
if (e.key === 'Enter') {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
sendMyMessage();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
|
|
|
|
|
const saveRelayButton = document.getElementById('saveWinkeyRelaySettings');
|
|
|
|
|
if (saveRelayButton) {
|
|
|
|
|
saveRelayButton.addEventListener('click', saveWinkeyRelaySettings);
|
|
|
|
|
}
|
2026-05-15 14:19:48 +01:00
|
|
|
|
|
|
|
|
loadRelaySettingsFromAccount().finally(function() {
|
|
|
|
|
connectWebSocket();
|
|
|
|
|
});
|
2026-05-14 22:36:19 +01:00
|
|
|
});
|
2025-05-28 14:24:03 +01:00
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
function morsekey_func1() {
|
2026-05-14 22:34:51 +01:00
|
|
|
const textToSend = UpdateMacros(function1Macro);
|
|
|
|
|
console.log("F1: " + textToSend);
|
|
|
|
|
const cwMessage = 'CW:' + textToSend;
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
sendWinkeyCommand(cwMessage, false);
|
2026-05-14 22:34:51 +01:00
|
|
|
if (window.cloudlogCwSidetone) {
|
|
|
|
|
window.cloudlogCwSidetone.playText(textToSend, currentCwSpeed || 20);
|
|
|
|
|
}
|
2025-05-28 14:24:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function morsekey_func2() {
|
2026-05-14 22:34:51 +01:00
|
|
|
const textToSend = UpdateMacros(function2Macro);
|
|
|
|
|
console.log("F2: " + textToSend);
|
|
|
|
|
const cwMessage = 'CW:' + textToSend;
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
sendWinkeyCommand(cwMessage, false);
|
2026-05-14 22:34:51 +01:00
|
|
|
if (window.cloudlogCwSidetone) {
|
|
|
|
|
window.cloudlogCwSidetone.playText(textToSend, currentCwSpeed || 20);
|
|
|
|
|
}
|
2025-05-28 14:24:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function morsekey_func3() {
|
2026-05-14 22:34:51 +01:00
|
|
|
const textToSend = UpdateMacros(function3Macro);
|
|
|
|
|
console.log("F3: " + textToSend);
|
|
|
|
|
const cwMessage = 'CW:' + textToSend;
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
sendWinkeyCommand(cwMessage, false);
|
2026-05-14 22:34:51 +01:00
|
|
|
if (window.cloudlogCwSidetone) {
|
|
|
|
|
window.cloudlogCwSidetone.playText(textToSend, currentCwSpeed || 20);
|
|
|
|
|
}
|
2025-05-28 14:24:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function morsekey_func4() {
|
2026-05-14 22:34:51 +01:00
|
|
|
const textToSend = UpdateMacros(function4Macro);
|
|
|
|
|
console.log("F4: " + textToSend);
|
|
|
|
|
const cwMessage = 'CW:' + textToSend;
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
sendWinkeyCommand(cwMessage, false);
|
2026-05-14 22:34:51 +01:00
|
|
|
if (window.cloudlogCwSidetone) {
|
|
|
|
|
window.cloudlogCwSidetone.playText(textToSend, currentCwSpeed || 20);
|
|
|
|
|
}
|
2025-05-28 14:24:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function morsekey_func5() {
|
2026-05-14 22:34:51 +01:00
|
|
|
const textToSend = UpdateMacros(function5Macro);
|
|
|
|
|
console.log("F5: " + textToSend);
|
|
|
|
|
const cwMessage = 'CW:' + textToSend;
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
sendWinkeyCommand(cwMessage, false);
|
2026-05-14 22:34:51 +01:00
|
|
|
if (window.cloudlogCwSidetone) {
|
|
|
|
|
window.cloudlogCwSidetone.playText(textToSend, currentCwSpeed || 20);
|
|
|
|
|
}
|
2025-05-28 14:24:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let function1Name, function1Macro, function2Name, function2Macro, function3Name, function3Macro, function4Name, function4Macro, function5Name, function5Macro;
|
|
|
|
|
|
|
|
|
|
getMacros();
|
|
|
|
|
|
|
|
|
|
document.addEventListener('keydown', function(event) {
|
|
|
|
|
|
|
|
|
|
if (event.key === 'F1') {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
morsekey_func1();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (event.key === 'F2') {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
morsekey_func2();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (event.key === 'F3') {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
morsekey_func3();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (event.key === 'F4') {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
morsekey_func4();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (event.key === 'F5') {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
morsekey_func5();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function UpdateMacros(macrotext) {
|
|
|
|
|
|
|
|
|
|
// Get the values from the form set to uppercase
|
|
|
|
|
let CALL = document.getElementById("callsign").value.toUpperCase();
|
|
|
|
|
let RSTS = document.getElementById("rst_sent").value;
|
|
|
|
|
|
|
|
|
|
let newString;
|
|
|
|
|
newString = macrotext.replace(/\[MYCALL\]/g, my_call);
|
|
|
|
|
newString = newString.replace(/\[CALL\]/g, CALL);
|
|
|
|
|
newString = newString.replace(/\[RSTS\]/g, RSTS);
|
|
|
|
|
console.log(newString);
|
|
|
|
|
return newString;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Call url and store the returned json data as variables
|
|
|
|
|
function getMacros() {
|
|
|
|
|
fetch(base_url + 'index.php/qso/cwmacros_json')
|
|
|
|
|
.then(response => response.json())
|
|
|
|
|
.then(data => {
|
|
|
|
|
function1Name = data.function1_name;
|
|
|
|
|
function1Macro = data.function1_macro;
|
|
|
|
|
function2Name = data.function2_name;
|
|
|
|
|
function2Macro = data.function2_macro;
|
|
|
|
|
function3Name = data.function3_name;
|
|
|
|
|
function3Macro = data.function3_macro;
|
|
|
|
|
function4Name = data.function4_name;
|
|
|
|
|
function4Macro = data.function4_macro;
|
|
|
|
|
function5Name = data.function5_name;
|
|
|
|
|
function5Macro = data.function5_macro;
|
|
|
|
|
|
|
|
|
|
const morsekey_func1_Button = document.getElementById('morsekey_func1');
|
|
|
|
|
morsekey_func1_Button.textContent = 'F1 (' + function1Name + ')';
|
|
|
|
|
|
|
|
|
|
const morsekey_func2_Button = document.getElementById('morsekey_func2');
|
|
|
|
|
morsekey_func2_Button.textContent = 'F2 (' + function2Name + ')';
|
|
|
|
|
|
|
|
|
|
const morsekey_func3_Button = document.getElementById('morsekey_func3');
|
|
|
|
|
morsekey_func3_Button.textContent = 'F3 (' + function3Name + ')';
|
|
|
|
|
|
|
|
|
|
const morsekey_func4_Button = document.getElementById('morsekey_func4');
|
|
|
|
|
morsekey_func4_Button.textContent = 'F4 (' + function4Name + ')';
|
|
|
|
|
|
|
|
|
|
const morsekey_func5_Button = document.getElementById('morsekey_func5');
|
|
|
|
|
morsekey_func5_Button.textContent = 'F5 (' + function5Name + ')';
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-28 16:56:03 +01:00
|
|
|
|
2025-05-28 14:24:03 +01:00
|
|
|
function sendMyMessage() {
|
|
|
|
|
const message = document.getElementById('sendText').value;
|
|
|
|
|
if (message.trim() === '') {
|
|
|
|
|
alert('Please enter a message');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const cwMessage = 'CW:' + message;
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
sendWinkeyCommand(cwMessage, false);
|
2026-05-14 22:34:51 +01:00
|
|
|
if (window.cloudlogCwSidetone) {
|
|
|
|
|
window.cloudlogCwSidetone.playText(message, currentCwSpeed || 20);
|
|
|
|
|
}
|
2025-05-28 14:24:03 +01:00
|
|
|
|
2026-05-12 17:04:20 +01:00
|
|
|
// Clear the input field and return focus so the next message can be typed immediately
|
|
|
|
|
const sendTextEl = document.getElementById('sendText');
|
|
|
|
|
sendTextEl.value = '';
|
2026-05-12 17:10:56 +01:00
|
|
|
setTimeout(function() { sendTextEl.focus(); }, 0);
|
2025-05-28 14:24:03 +01:00
|
|
|
}
|
2025-10-06 14:42:03 +01:00
|
|
|
|
|
|
|
|
// CW Speed Control Functions
|
|
|
|
|
let currentCwSpeed = 20; // Default speed
|
|
|
|
|
|
|
|
|
|
function adjustCwSpeed(change) {
|
|
|
|
|
if (ws === null) {
|
|
|
|
|
alert('Please connect to WebSocket first');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const newSpeed = Math.max(5, Math.min(99, currentCwSpeed + change));
|
|
|
|
|
|
|
|
|
|
if (newSpeed !== currentCwSpeed) {
|
|
|
|
|
const speedMessage = 'CWSPEED:' + newSpeed;
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
sendWinkeyCommand(speedMessage, false);
|
2025-10-10 14:54:50 +01:00
|
|
|
// Get updated speed after changing it
|
|
|
|
|
setTimeout(getCwSpeed, 500);
|
2025-10-06 14:42:03 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getCwSpeed() {
|
|
|
|
|
if (ws === null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Add Winkey Relay support and settings UI
Introduce optional Winkey Relay transport for CW commands and a browser-based settings UI. Adds localStorage-backed relay config (enabled, url, token, room), a modal to edit/save settings, and validation for URL/token/room. WebSocket logic now switches between direct localhost connection and relay mode, handles relay protocol messages (join, frame, error), authenticates/join on open, and wraps sends via sendWinkeyCommand to support framed relay messages. Updates UI: new Relay button/modal, changed Settings button label/icon, socket status badge updates, and reconnect on save. Keeps compatibility with direct WebSocket server and retains logging and sidetone playback behavior.
2026-05-15 13:48:34 +01:00
|
|
|
sendWinkeyCommand('GETCWSPEED', true);
|
2025-10-06 14:42:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateSpeedDisplay(speed) {
|
|
|
|
|
currentCwSpeed = speed;
|
|
|
|
|
const speedDisplay = document.getElementById('cwSpeedDisplay');
|
|
|
|
|
if (speedDisplay) {
|
|
|
|
|
speedDisplay.textContent = speed + ' WPM';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-10 14:54:50 +01:00
|
|
|
// Removed constant polling functions to prevent WebSocket disruption
|
|
|
|
|
// Speed is now only read on initial connection and after speed changes
|
2025-10-06 14:42:03 +01:00
|
|
|
|
|
|
|
|
// Message log toggle function
|
|
|
|
|
function toggleMessageLog() {
|
|
|
|
|
const logContainer = document.getElementById('messageLogContainer');
|
|
|
|
|
const toggleButton = document.getElementById('toggleLogButton');
|
|
|
|
|
|
|
|
|
|
if (logContainer.style.display === 'none') {
|
|
|
|
|
logContainer.style.display = 'block';
|
|
|
|
|
toggleButton.innerHTML = '<i class="fas fa-eye-slash"></i>';
|
|
|
|
|
toggleButton.title = 'Hide Message Log';
|
|
|
|
|
} else {
|
|
|
|
|
logContainer.style.display = 'none';
|
|
|
|
|
toggleButton.innerHTML = '<i class="fas fa-list"></i>';
|
|
|
|
|
toggleButton.title = 'Show Message Log';
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-28 14:24:03 +01:00
|
|
|
</script>
|
|
|
|
|
<?php } ?>
|
2025-10-04 14:53:43 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php
|
2022-02-18 13:46:16 +00:00
|
|
|
|
|
|
|
|
$this->load->model('stations');
|
|
|
|
|
$active_station_id = $this->stations->find_active();
|
|
|
|
|
$station_profile = $this->stations->profile($active_station_id);
|
|
|
|
|
$active_station_info = $station_profile->row();
|
|
|
|
|
|
|
|
|
|
if (strpos($active_station_info->station_gridsquare, ',') !== false) {
|
|
|
|
|
$gridsquareArray = explode(',', $active_station_info->station_gridsquare);
|
|
|
|
|
$user_gridsquare = $gridsquareArray[0];
|
|
|
|
|
} else {
|
|
|
|
|
$user_gridsquare = $active_station_info->station_gridsquare;
|
|
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
?>
|
2019-06-14 00:14:39 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<script>
|
|
|
|
|
var markers = L.layerGroup();
|
|
|
|
|
var pos = [51.505, -0.09];
|
|
|
|
|
var mymap = L.map('qsomap').setView(pos, 12);
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: base_url + 'index.php/logbook/qralatlngjson',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
<?php if ($active_station_info->station_gridsquare != "") { ?>
|
|
|
|
|
qra: '<?php echo $user_gridsquare; ?>',
|
|
|
|
|
<?php } else if (null !== $this->config->item('locator')) { ?>
|
|
|
|
|
qra: '<?php echo $this->config->item('locator'); ?>',
|
|
|
|
|
<?php } else { ?>
|
|
|
|
|
// Fallback to London in case all else fails
|
|
|
|
|
qra: 'IO91WM',
|
|
|
|
|
<?php } ?>
|
|
|
|
|
},
|
|
|
|
|
success: function(data) {
|
|
|
|
|
result = JSON.parse(data);
|
|
|
|
|
if (typeof result[0] !== "undefined" && typeof result[1] !== "undefined") {
|
|
|
|
|
mymap.panTo([result[0], result[1]]);
|
|
|
|
|
pos = result;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
error: function() {},
|
|
|
|
|
});
|
2019-06-14 00:14:39 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
L.tileLayer('<?php echo $this->optionslib->get_option('option_map_tile_server'); ?>', {
|
|
|
|
|
maxZoom: 18,
|
|
|
|
|
attribution: '<?php echo $this->optionslib->get_option('option_map_tile_server_copyright'); ?>',
|
|
|
|
|
id: 'mapbox.streets'
|
|
|
|
|
}).addTo(mymap);
|
|
|
|
|
</script>
|
2019-05-21 13:44:22 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<script type="text/javascript">
|
|
|
|
|
var manual = <?php echo $_GET['manual']; ?>;
|
2019-05-21 13:44:22 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
$(document).ready(function() {
|
2019-05-21 13:44:22 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
$('.callsign-suggest').hide();
|
2020-04-25 08:09:29 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
setRst($(".mode").val());
|
2019-08-15 12:03:15 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
/* On Page Load */
|
2025-12-12 14:54:38 +00:00
|
|
|
var catcher = function(e) {
|
2026-02-11 21:45:03 +00:00
|
|
|
// Skip warning if callsign field is empty
|
|
|
|
|
var callsignField = $('#callsign');
|
|
|
|
|
if (callsignField.length > 0 && !callsignField.val().trim()) {
|
|
|
|
|
return; // No warning if callsign is empty
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
var changed = false;
|
|
|
|
|
$('form').each(function() {
|
|
|
|
|
if ($(this).data('initialForm') != $(this).serialize()) {
|
|
|
|
|
changed = true;
|
|
|
|
|
$(this).addClass('changed');
|
|
|
|
|
} else {
|
|
|
|
|
$(this).removeClass('changed');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
if (changed) {
|
2025-12-12 14:54:38 +00:00
|
|
|
// For external navigation (closing tab/window), browsers require native dialog
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
e.returnValue = 'You have unsaved changes. Are you sure you want to leave?';
|
|
|
|
|
return e.returnValue;
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
|
|
|
|
};
|
2019-05-21 13:44:22 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
$(function() {
|
|
|
|
|
$('form').each(function() {
|
|
|
|
|
$(this).data('initialForm', $(this).serialize());
|
|
|
|
|
}).submit(function(e) {
|
|
|
|
|
var formEl = this;
|
|
|
|
|
var changed = false;
|
|
|
|
|
$('form').each(function() {
|
|
|
|
|
if (this != formEl && $(this).data('initialForm') != $(this).serialize()) {
|
|
|
|
|
changed = true;
|
|
|
|
|
$(this).addClass('changed');
|
|
|
|
|
} else {
|
|
|
|
|
$(this).removeClass('changed');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
if (changed && !confirm('You have an unsaved QSO. Continue with QSO?')) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
} else {
|
|
|
|
|
$(window).unbind('beforeunload', catcher);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
$(window).bind('beforeunload', catcher);
|
2025-12-12 14:54:38 +00:00
|
|
|
|
|
|
|
|
// Intercept internal link clicks to show custom modal
|
|
|
|
|
$(document).on('click', 'a[href]:not([href^="#"]):not([target="_blank"]):not(.no-confirm):not([data-bs-toggle])', function(e) {
|
|
|
|
|
var href = $(this).attr('href');
|
|
|
|
|
|
|
|
|
|
// Skip if it's a javascript: link, empty, or hash link
|
|
|
|
|
if (!href || href === '#' || href.indexOf('javascript:') === 0) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-11 21:45:03 +00:00
|
|
|
// Skip warning if callsign field is empty
|
|
|
|
|
var callsignField = $('#callsign');
|
|
|
|
|
if (callsignField.length > 0 && !callsignField.val().trim()) {
|
|
|
|
|
return true; // No warning if callsign is empty
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-12 14:54:38 +00:00
|
|
|
// Check if form has unsaved changes
|
|
|
|
|
var hasChanges = false;
|
|
|
|
|
$('form').each(function() {
|
|
|
|
|
if ($(this).data('initialForm') != $(this).serialize()) {
|
|
|
|
|
hasChanges = true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (hasChanges) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
|
|
// Check if custom modal element exists in DOM at click time
|
|
|
|
|
var modalEl = document.getElementById('leaveQsoModal');
|
|
|
|
|
if (modalEl && typeof bootstrap !== 'undefined' && bootstrap.Modal) {
|
|
|
|
|
// Use custom modal
|
|
|
|
|
window.pendingNavigation = href;
|
|
|
|
|
var modal = bootstrap.Modal.getInstance(modalEl);
|
|
|
|
|
if (!modal) {
|
|
|
|
|
modal = new bootstrap.Modal(modalEl);
|
|
|
|
|
}
|
|
|
|
|
modal.show();
|
|
|
|
|
} else {
|
|
|
|
|
// Fallback to confirm dialog on pages without custom modal
|
|
|
|
|
if (confirm('You have unsaved changes. Are you sure you want to leave QSO entry?')) {
|
|
|
|
|
$(window).unbind('beforeunload', catcher);
|
|
|
|
|
window.location.href = href;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
});
|
2024-02-22 16:56:21 +00:00
|
|
|
});
|
2019-05-21 13:44:22 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
// Callsign always has focus on load
|
|
|
|
|
$("#callsign").focus();
|
2019-05-21 13:44:22 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
if (!manual) {
|
|
|
|
|
$(function($) {
|
|
|
|
|
resetTimers(0);
|
|
|
|
|
});
|
|
|
|
|
}
|
2019-05-25 22:17:35 +01:00
|
|
|
});
|
2019-06-24 16:13:51 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->session->userdata('user_qso_end_times') == 1) { ?>
|
|
|
|
|
$('#callsign').focusout(function() {
|
|
|
|
|
if (!manual && $('#callsign').val() != '') {
|
|
|
|
|
clearInterval(handleStart);
|
|
|
|
|
clearInterval(handleDate);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
$('#start_time').focusout(function() {
|
|
|
|
|
if (manual && $('#start_time').val() != '') {
|
|
|
|
|
$('#end_time').val($('#start_time').val());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
<?php } ?>
|
2022-10-19 14:52:43 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
jQuery(function($) {
|
|
|
|
|
var input = $('#callsign');
|
|
|
|
|
input.on('keydown', function() {
|
|
|
|
|
var key = event.keyCode || event.charCode;
|
2020-04-13 10:34:02 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
if (key == 8 || key == 46) {
|
|
|
|
|
reset_fields();
|
|
|
|
|
}
|
|
|
|
|
});
|
2023-05-01 21:14:30 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
$(document).keyup(function(e) {
|
|
|
|
|
if (e.charCode === 0) {
|
|
|
|
|
let fixedcall = $('#callsign').val();
|
|
|
|
|
$('#callsign').val(fixedcall.replace('Ø', '0'));
|
|
|
|
|
}
|
|
|
|
|
if (e.key === "Escape") { // escape key maps to keycode `27`
|
2026-05-28 22:19:53 +01:00
|
|
|
const escHandledAt = Number(window.cloudlogQsoEscHandledAt || 0);
|
|
|
|
|
if (escHandledAt && (Date.now() - escHandledAt) < 500) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
reset_fields();
|
|
|
|
|
if (!manual) {
|
|
|
|
|
resetTimers(0)
|
|
|
|
|
}
|
|
|
|
|
$('#callsign').val("");
|
|
|
|
|
$("#callsign").focus();
|
|
|
|
|
updateFromCAT();
|
2024-02-22 16:59:21 +00:00
|
|
|
if (document.querySelector('#radio').value != '0') {
|
|
|
|
|
updateFromCAT();
|
|
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
2023-04-27 23:02:57 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->session->userdata('user_sota_lookup') == 1) { ?>
|
|
|
|
|
$('#sota_ref').change(function() {
|
|
|
|
|
var sota = $('#sota_ref').val();
|
|
|
|
|
if (sota.length > 0) {
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: base_url + 'index.php/qso/get_sota_info',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
'sota': sota
|
|
|
|
|
},
|
|
|
|
|
success: function(res) {
|
|
|
|
|
$('#qth').val(res.name);
|
|
|
|
|
$('#locator').val(res.locator);
|
|
|
|
|
},
|
|
|
|
|
error: function() {
|
|
|
|
|
$('#qth').val('');
|
|
|
|
|
$('#locator').val('');
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
<?php } ?>
|
2020-12-07 21:26:16 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->session->userdata('user_wwff_lookup') == 1) { ?>
|
|
|
|
|
$('#wwff_ref').change(function() {
|
|
|
|
|
var wwff = $('#wwff_ref').val();
|
|
|
|
|
if (wwff.length > 0) {
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: base_url + 'index.php/qso/get_wwff_info',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
'wwff': wwff
|
|
|
|
|
},
|
|
|
|
|
success: function(res) {
|
|
|
|
|
$('#qth').val(res.name);
|
|
|
|
|
$('#locator').val(res.locator);
|
|
|
|
|
},
|
|
|
|
|
error: function() {
|
|
|
|
|
$('#qth').val('');
|
|
|
|
|
$('#locator').val('');
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
<?php } ?>
|
2023-08-14 15:10:24 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->session->userdata('user_pota_lookup') == 1) { ?>
|
|
|
|
|
$('#pota_ref').change(function() {
|
2026-03-24 14:20:17 +00:00
|
|
|
var pota = ($('#pota_ref').val() || '').split(',')[0].trim();
|
2024-02-22 16:56:21 +00:00
|
|
|
if (pota.length > 0) {
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: base_url + 'index.php/qso/get_pota_info',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
'pota': pota
|
|
|
|
|
},
|
|
|
|
|
success: function(res) {
|
|
|
|
|
$('#qth').val(res.name);
|
|
|
|
|
$('#locator').val(res.grid6);
|
|
|
|
|
},
|
|
|
|
|
error: function() {
|
|
|
|
|
$('#qth').val('');
|
|
|
|
|
$('#locator').val('');
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
<?php } ?>
|
2023-11-01 14:24:13 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
$('#stationProfile').change(function() {
|
|
|
|
|
var stationProfile = $('#stationProfile').val();
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: base_url + 'index.php/qso/get_station_power',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
'stationProfile': stationProfile
|
|
|
|
|
},
|
|
|
|
|
success: function(res) {
|
|
|
|
|
$('#transmit_power').val(res.station_power);
|
|
|
|
|
},
|
|
|
|
|
error: function() {
|
|
|
|
|
$('#transmit_power').val('');
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
// [eQSL default msg] change value on change station profle //
|
|
|
|
|
qso_set_eqsl_qslmsg(stationProfile, false, '.qso_panel');
|
|
|
|
|
});
|
|
|
|
|
// [eQSL default msg] change value on clic //
|
|
|
|
|
$('.qso_panel .qso_eqsl_qslmsg_update').off('click').on('click', function() {
|
|
|
|
|
qso_set_eqsl_qslmsg($('.qso_panel #stationProfile').val(), true, '.qso_panel');
|
|
|
|
|
$('#charsLeft').text(" ");
|
|
|
|
|
});
|
2023-11-01 14:24:13 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->session->userdata('user_qth_lookup') == 1) { ?>
|
|
|
|
|
$('#qth').focusout(function() {
|
|
|
|
|
if ($('#locator').val() === '') {
|
|
|
|
|
var lat = 0;
|
|
|
|
|
var lon = 0;
|
|
|
|
|
$.ajax({
|
|
|
|
|
async: false,
|
|
|
|
|
type: 'GET',
|
|
|
|
|
dataType: "json",
|
|
|
|
|
url: "https://nominatim.openstreetmap.org/?city=" + $(this).val() + "&format=json&addressdetails=1&limit=1",
|
|
|
|
|
data: {},
|
|
|
|
|
success: function(data) {
|
|
|
|
|
if (typeof data[0].lat !== 'undefined') {
|
|
|
|
|
lat = parseFloat(data[0].lat);
|
|
|
|
|
}
|
|
|
|
|
if (typeof data[0].lon !== 'undefined') {
|
|
|
|
|
lon = parseFloat(data[0].lon);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
if (lat !== 0 && lon !== 0) {
|
|
|
|
|
var qthloc = LatLng2Loc(lat, lon, 10);
|
|
|
|
|
if (qthloc.length > 0) {
|
|
|
|
|
$('#locator').val(qthloc.substr(0, 6)).trigger('focusout');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
LatLng2Loc = function(y, x, num) {
|
|
|
|
|
if (x < -180) {
|
|
|
|
|
x = x + 360;
|
|
|
|
|
}
|
|
|
|
|
if (x > 180) {
|
|
|
|
|
x = x - 360;
|
|
|
|
|
}
|
|
|
|
|
var yqth, yi, yk, ydiv, yres, ylp, y;
|
|
|
|
|
var ycalc = new Array(0, 0, 0);
|
|
|
|
|
var yn = new Array(0, 0, 0, 0, 0, 0, 0);
|
|
|
|
|
|
|
|
|
|
var ydiv_arr = new Array(10, 1, 1 / 24, 1 / 240, 1 / 240 / 24);
|
|
|
|
|
ycalc[0] = (x + 180) / 2;
|
|
|
|
|
ycalc[1] = y + 90;
|
|
|
|
|
|
|
|
|
|
for (yi = 0; yi < 2; yi++) {
|
|
|
|
|
for (yk = 0; yk < 5; yk++) {
|
|
|
|
|
ydiv = ydiv_arr[yk];
|
|
|
|
|
yres = ycalc[yi] / ydiv;
|
|
|
|
|
ycalc[yi] = yres;
|
|
|
|
|
if (ycalc[yi] > 0) ylp = Math.floor(yres);
|
|
|
|
|
else ylp = Math.ceil(yres);
|
|
|
|
|
ycalc[yi] = (ycalc[yi] - ylp) * ydiv;
|
|
|
|
|
yn[2 * yk + yi] = ylp;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-01 14:24:13 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
var qthloc = "";
|
|
|
|
|
if (num >= 2) qthloc += String.fromCharCode(yn[0] + 0x41) + String.fromCharCode(yn[1] + 0x41);
|
|
|
|
|
if (num >= 4) qthloc += String.fromCharCode(yn[2] + 0x30) + String.fromCharCode(yn[3] + 0x30);
|
|
|
|
|
if (num >= 6) qthloc += String.fromCharCode(yn[4] + 0x41) + String.fromCharCode(yn[5] + 0x41);
|
|
|
|
|
if (num >= 8) qthloc += ' ' + String.fromCharCode(yn[6] + 0x30) + String.fromCharCode(yn[7] + 0x30);
|
|
|
|
|
if (num >= 10) qthloc += String.fromCharCode(yn[8] + 0x61) + String.fromCharCode(yn[9] + 0x61);
|
|
|
|
|
return qthloc;
|
|
|
|
|
}
|
|
|
|
|
<?php } ?>
|
2020-12-07 21:26:16 +01:00
|
|
|
</script>
|
2021-10-06 20:24:14 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php } ?>
|
|
|
|
|
<?php if ($this->uri->segment(1) == "qso" || ($this->uri->segment(1) == "contesting" && $this->uri->segment(2) != "add")) { ?>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/moment.min.js"></script>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/datetime-moment.js"></script>
|
2020-12-07 21:26:16 +01:00
|
|
|
<script>
|
2024-02-22 16:56:21 +00:00
|
|
|
$('#notice-alerts').delay(1000).fadeOut(5000);
|
|
|
|
|
|
|
|
|
|
function setRst(mode) {
|
2025-05-28 17:08:54 +01:00
|
|
|
if (mode == 'JT65' || mode == 'JT65B' || mode == 'JT6C' || mode == 'JTMS' || mode == 'ISCAT' || mode == 'MSK144' || mode == 'JTMSK' || mode == 'QRA64' || mode == 'FT8' || mode == 'FT4' || mode == 'JS8' || mode == 'JT9' || mode == 'JT9-1' || mode == 'ROS' || mode == 'Q65' || mode == 'FST4' || mode == 'FST4W') {
|
2024-02-22 16:56:21 +00:00
|
|
|
$('#rst_sent').val('-5');
|
|
|
|
|
$('#rst_rcvd').val('-5');
|
|
|
|
|
} else if (mode == 'FSK441' || mode == 'JT6M') {
|
|
|
|
|
$('#rst_sent').val('26');
|
|
|
|
|
$('#rst_rcvd').val('26');
|
|
|
|
|
} else if (mode == 'CW' || mode == 'RTTY' || mode == 'PSK31' || mode == 'PSK63') {
|
|
|
|
|
$('#rst_sent').val('599');
|
|
|
|
|
$('#rst_rcvd').val('599');
|
|
|
|
|
} else {
|
|
|
|
|
$('#rst_sent').val('59');
|
|
|
|
|
$('#rst_rcvd').val('59');
|
|
|
|
|
}
|
2023-12-21 09:22:56 +01:00
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
|
|
|
|
|
function getUTCTimeStamp(el) {
|
|
|
|
|
var now = new Date();
|
|
|
|
|
var localTime = now.getTime();
|
|
|
|
|
var utc = localTime + (now.getTimezoneOffset() * 60000);
|
|
|
|
|
$(el).attr('value', ("0" + now.getUTCHours()).slice(-2) + ':' + ("0" + now.getUTCMinutes()).slice(-2) + ':' + ("0" + now.getUTCSeconds()).slice(-2));
|
2023-12-21 09:22:56 +01:00
|
|
|
}
|
2019-05-21 13:44:22 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
function getUTCDateStamp(el) {
|
|
|
|
|
var now = new Date();
|
|
|
|
|
var localTime = now.getTime();
|
|
|
|
|
var utc = localTime + (now.getTimezoneOffset() * 60000);
|
2025-10-18 15:10:03 +01:00
|
|
|
|
|
|
|
|
// Get user's date format preference
|
|
|
|
|
var userDateFormat = '<?php
|
|
|
|
|
if ($this->session->userdata('user_date_format')) {
|
|
|
|
|
echo $this->session->userdata('user_date_format');
|
|
|
|
|
} else {
|
|
|
|
|
echo $this->config->item('qso_date_format');
|
|
|
|
|
}
|
|
|
|
|
?>';
|
|
|
|
|
|
|
|
|
|
// Format date according to user preference
|
|
|
|
|
var day = ("0" + now.getUTCDate()).slice(-2);
|
|
|
|
|
var month = ("0" + (now.getUTCMonth() + 1)).slice(-2);
|
|
|
|
|
var year2 = now.getUTCFullYear().toString().slice(-2);
|
|
|
|
|
var year4 = now.getUTCFullYear();
|
|
|
|
|
|
|
|
|
|
var formattedDate;
|
|
|
|
|
switch(userDateFormat) {
|
|
|
|
|
case 'd/m/y':
|
|
|
|
|
formattedDate = day + '/' + month + '/' + year2;
|
|
|
|
|
break;
|
|
|
|
|
case 'd/m/Y':
|
|
|
|
|
formattedDate = day + '/' + month + '/' + year4;
|
|
|
|
|
break;
|
|
|
|
|
case 'm/d/y':
|
|
|
|
|
formattedDate = month + '/' + day + '/' + year2;
|
|
|
|
|
break;
|
|
|
|
|
case 'm/d/Y':
|
|
|
|
|
formattedDate = month + '/' + day + '/' + year4;
|
|
|
|
|
break;
|
|
|
|
|
case 'd.m.Y':
|
|
|
|
|
formattedDate = day + '.' + month + '.' + year4;
|
|
|
|
|
break;
|
|
|
|
|
case 'y/m/d':
|
|
|
|
|
formattedDate = year2 + '/' + month + '/' + day;
|
|
|
|
|
break;
|
|
|
|
|
case 'Y-m-d':
|
|
|
|
|
formattedDate = year4 + '-' + month + '-' + day;
|
|
|
|
|
break;
|
|
|
|
|
case 'M d, Y':
|
|
|
|
|
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
|
|
|
formattedDate = months[now.getUTCMonth()] + ' ' + now.getUTCDate() + ', ' + year4;
|
|
|
|
|
break;
|
|
|
|
|
case 'M d, y':
|
|
|
|
|
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
|
|
|
formattedDate = months[now.getUTCMonth()] + ' ' + now.getUTCDate() + ', ' + year2;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
// Fallback to original format if no match
|
|
|
|
|
formattedDate = day + '-' + month + '-' + year4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$(el).attr('value', formattedDate);
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
|
|
|
|
</script>
|
2019-05-25 17:59:19 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<script>
|
2026-04-07 14:45:22 +01:00
|
|
|
let catRequestCounter = 0;
|
|
|
|
|
let lastProcessedCatRequest = 0;
|
|
|
|
|
let catSelectionContextVersion = 0;
|
2026-05-01 10:23:08 +01:00
|
|
|
let consecutiveCatPollFailures = 0;
|
|
|
|
|
let catPollTimer = null;
|
|
|
|
|
let lastSuccessfulCatUpdateAt = null;
|
2026-05-27 22:04:14 +01:00
|
|
|
let lockSatelliteFieldsToUserInput = false;
|
2026-05-01 10:23:08 +01:00
|
|
|
|
2026-06-23 11:35:51 +01:00
|
|
|
window.cloudlogLastCatData = null;
|
|
|
|
|
window.cloudlogLastCatRadioId = null;
|
|
|
|
|
|
2026-05-01 10:23:08 +01:00
|
|
|
const CAT_POLL_BASE_INTERVAL_MS = 3000;
|
|
|
|
|
const CAT_POLL_MAX_INTERVAL_MS = 15000;
|
|
|
|
|
const CAT_POLL_WARNING_THRESHOLD = 3;
|
2026-04-07 14:45:22 +01:00
|
|
|
|
2024-12-06 15:07:52 +00:00
|
|
|
// Helper function to update a UI element with CAT data
|
|
|
|
|
const cat2UI = (ui, cat, allowEmpty = true, allowZero = true, callbackOnUpdate) => {
|
|
|
|
|
if (
|
|
|
|
|
cat == null ||
|
|
|
|
|
(!allowEmpty && cat === '') ||
|
|
|
|
|
(!allowZero && cat === '0')
|
|
|
|
|
) return;
|
|
|
|
|
|
|
|
|
|
if (ui.data('catValue') != cat) {
|
|
|
|
|
ui.val(cat).data('catValue', cat);
|
|
|
|
|
if (typeof callbackOnUpdate === 'function') callbackOnUpdate(cat);
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
2024-12-06 15:07:52 +00:00
|
|
|
};
|
2024-02-22 16:56:21 +00:00
|
|
|
|
2024-12-06 15:07:52 +00:00
|
|
|
// Update UI from CAT data
|
|
|
|
|
const updateFromCAT = (radioID) => {
|
2026-04-13 13:36:45 +01:00
|
|
|
if (!radioID || radioID === '0' || radioID === 'undefined') return;
|
2024-12-06 15:07:52 +00:00
|
|
|
|
2026-04-07 14:45:22 +01:00
|
|
|
const requestedRadioID = String(radioID);
|
|
|
|
|
const requestId = ++catRequestCounter;
|
|
|
|
|
const requestContextVersion = catSelectionContextVersion;
|
|
|
|
|
|
2026-05-01 10:22:11 +01:00
|
|
|
$.ajax({
|
|
|
|
|
url: `radio/json/${requestedRadioID}`,
|
|
|
|
|
dataType: 'json',
|
|
|
|
|
cache: false,
|
|
|
|
|
success: (data) => {
|
|
|
|
|
const currentSelectedRadioID = String($('select.radios option:selected').val() || '0');
|
|
|
|
|
|
|
|
|
|
// Ignore stale CAT responses when radio selection changed or newer requests have already been applied.
|
|
|
|
|
if (
|
|
|
|
|
requestContextVersion !== catSelectionContextVersion ||
|
|
|
|
|
currentSelectedRadioID !== requestedRadioID ||
|
|
|
|
|
requestId < lastProcessedCatRequest
|
|
|
|
|
) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-04-07 14:45:22 +01:00
|
|
|
|
2026-05-01 10:22:11 +01:00
|
|
|
lastProcessedCatRequest = requestId;
|
2026-04-07 14:45:22 +01:00
|
|
|
|
2026-05-01 10:22:11 +01:00
|
|
|
if (data.error) {
|
|
|
|
|
if (data.error === 'not_logged_in') {
|
|
|
|
|
handleLoginError();
|
|
|
|
|
}
|
2026-05-01 10:23:08 +01:00
|
|
|
handleCATPollFailure(requestedRadioID);
|
2026-05-01 10:22:11 +01:00
|
|
|
return;
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
2024-12-06 15:07:52 +00:00
|
|
|
|
2026-05-01 10:22:11 +01:00
|
|
|
clearLoginError();
|
2026-05-01 10:23:08 +01:00
|
|
|
handleCATPollSuccess();
|
2026-06-23 11:35:51 +01:00
|
|
|
window.cloudlogLastCatData = data || null;
|
|
|
|
|
window.cloudlogLastCatRadioId = requestedRadioID;
|
2026-05-01 10:22:11 +01:00
|
|
|
updateUIWithCATData(data);
|
2026-05-01 10:23:08 +01:00
|
|
|
},
|
|
|
|
|
error: () => {
|
|
|
|
|
handleCATPollFailure(requestedRadioID);
|
2026-05-01 10:22:11 +01:00
|
|
|
}
|
2024-12-06 15:07:52 +00:00
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-01 10:23:08 +01:00
|
|
|
const getNextCATPollDelay = () => {
|
|
|
|
|
if (consecutiveCatPollFailures <= 0) {
|
|
|
|
|
return CAT_POLL_BASE_INTERVAL_MS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Math.min(
|
|
|
|
|
CAT_POLL_MAX_INTERVAL_MS,
|
|
|
|
|
CAT_POLL_BASE_INTERVAL_MS * Math.pow(2, consecutiveCatPollFailures - 1)
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const clearCATPollWarning = () => {
|
|
|
|
|
$('.radio_poll_warning').remove();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const showCATPollWarning = () => {
|
|
|
|
|
const secondsSinceSuccess = lastSuccessfulCatUpdateAt === null
|
|
|
|
|
? null
|
|
|
|
|
: Math.max(0, Math.floor((Date.now() - lastSuccessfulCatUpdateAt) / 1000));
|
|
|
|
|
|
|
|
|
|
let warningText = 'Live rig sync is temporarily unstable. Retrying automatically.';
|
|
|
|
|
if (secondsSinceSuccess !== null) {
|
|
|
|
|
warningText += ` Last successful update was ${secondsSinceSuccess} seconds ago.`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($('.radio_poll_warning').length === 0) {
|
|
|
|
|
$('#radio_status').prepend(
|
|
|
|
|
`<div class="alert alert-warning radio_poll_warning" role="alert"><i class="fas fa-exclamation-triangle"></i> ${warningText}</div>`
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
$('.radio_poll_warning').html(`<i class="fas fa-exclamation-triangle"></i> ${warningText}`);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleCATPollSuccess = () => {
|
|
|
|
|
consecutiveCatPollFailures = 0;
|
|
|
|
|
lastSuccessfulCatUpdateAt = Date.now();
|
|
|
|
|
clearCATPollWarning();
|
|
|
|
|
scheduleNextCATPoll(CAT_POLL_BASE_INTERVAL_MS);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleCATPollFailure = (radioID) => {
|
|
|
|
|
const currentSelectedRadioID = String($('select.radios option:selected').val() || '0');
|
|
|
|
|
if (!radioID || currentSelectedRadioID !== String(radioID)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
consecutiveCatPollFailures += 1;
|
|
|
|
|
|
|
|
|
|
if (consecutiveCatPollFailures >= CAT_POLL_WARNING_THRESHOLD) {
|
|
|
|
|
showCATPollWarning();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scheduleNextCATPoll(getNextCATPollDelay());
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const scheduleNextCATPoll = (delayMs) => {
|
|
|
|
|
if (catPollTimer !== null) {
|
|
|
|
|
clearTimeout(catPollTimer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
catPollTimer = setTimeout(() => {
|
|
|
|
|
pollSelectedRadio();
|
|
|
|
|
}, delayMs);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const pollSelectedRadio = () => {
|
|
|
|
|
const selectedRadioID = String($('select.radios option:selected').val() || '0');
|
|
|
|
|
if (selectedRadioID !== '0') {
|
|
|
|
|
updateFromCAT(selectedRadioID);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
consecutiveCatPollFailures = 0;
|
|
|
|
|
clearCATPollWarning();
|
|
|
|
|
scheduleNextCATPoll(CAT_POLL_BASE_INTERVAL_MS);
|
|
|
|
|
};
|
|
|
|
|
|
2024-12-06 15:07:52 +00:00
|
|
|
// Handle login error display
|
|
|
|
|
const handleLoginError = () => {
|
|
|
|
|
$(".radio_cat_state").remove();
|
|
|
|
|
if ($('.radio_login_error').length === 0) {
|
|
|
|
|
$('.qso_panel').prepend(
|
|
|
|
|
'<div class="alert alert-danger radio_login_error" role="alert">' +
|
|
|
|
|
'<i class="fas fa-broadcast-tower"></i> You\'re not logged in. ' +
|
|
|
|
|
'Please <a href="<?php echo base_url(); ?>">login</a></div>'
|
|
|
|
|
);
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-12-06 15:07:52 +00:00
|
|
|
// Clear login error
|
|
|
|
|
const clearLoginError = () => {
|
|
|
|
|
$(".radio_login_error").remove();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Update UI elements with CAT data
|
|
|
|
|
const updateUIWithCATData = (data) => {
|
|
|
|
|
cat2UI($('#frequency'), data.frequency, false, true, (d) => {
|
|
|
|
|
$("#band").val(frequencyToBand(d));
|
|
|
|
|
});
|
|
|
|
|
cat2UI($('#frequency_rx'), data.frequency_rx, false, true, (d) => {
|
|
|
|
|
$("#band_rx").val(frequencyToBand(d));
|
|
|
|
|
});
|
|
|
|
|
cat2UI($('.mode'), data.mode, false, false, () => {
|
|
|
|
|
setRst($(".mode").val());
|
2025-10-07 22:40:23 +01:00
|
|
|
// If the data.mode is winkey show the div with id winkey if not hide it
|
|
|
|
|
if (data.mode === 'CW') {
|
|
|
|
|
$('#winkey').show();
|
|
|
|
|
} else {
|
|
|
|
|
$('#winkey').hide();
|
|
|
|
|
}
|
2024-12-06 15:07:52 +00:00
|
|
|
});
|
2026-05-27 22:04:14 +01:00
|
|
|
if (!lockSatelliteFieldsToUserInput) {
|
|
|
|
|
const satNameFromCat = String(data.satname || '').trim();
|
|
|
|
|
const satModeFromCat = String(data.satmode || '').trim();
|
2026-05-27 22:27:52 +01:00
|
|
|
const propModeFromCat = String(data.prop_mode || '').trim();
|
2026-05-27 22:04:14 +01:00
|
|
|
|
|
|
|
|
// If CAT does not provide satellite fields for this radio, clear stale values
|
|
|
|
|
// so we do not carry over sat details from a previous save or radio.
|
|
|
|
|
if (satNameFromCat === '') {
|
|
|
|
|
$('#sat_name').val('').removeData('catValue');
|
|
|
|
|
} else {
|
|
|
|
|
cat2UI($('#sat_name'), satNameFromCat, false, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (satModeFromCat === '') {
|
|
|
|
|
$('#sat_mode').val('').removeData('catValue');
|
|
|
|
|
} else {
|
|
|
|
|
cat2UI($('#sat_mode'), satModeFromCat, false, false);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-27 22:27:52 +01:00
|
|
|
if (propModeFromCat === '') {
|
|
|
|
|
$('#selectPropagation').val('').removeData('catValue');
|
|
|
|
|
} else {
|
|
|
|
|
cat2UI($('#selectPropagation'), propModeFromCat, false, false);
|
|
|
|
|
}
|
2026-05-27 22:04:14 +01:00
|
|
|
}
|
2024-12-06 15:07:52 +00:00
|
|
|
cat2UI($('#transmit_power'), data.power, false, false);
|
|
|
|
|
|
|
|
|
|
handleCATTimeout(data);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Handle CAT timeout
|
|
|
|
|
const handleCATTimeout = (data) => {
|
|
|
|
|
const minutes = Math.floor(<?php echo $this->optionslib->get_option('cat_timeout_interval'); ?> / 60);
|
|
|
|
|
|
|
|
|
|
if (data.updated_minutes_ago > minutes) {
|
|
|
|
|
$(".radio_cat_state").remove();
|
|
|
|
|
const errorText = `Radio connection timed-out: ${$('select.radios option:selected').text()} data is ${data.updated_minutes_ago} minutes old.`;
|
|
|
|
|
if ($('.radio_timeout_error').length === 0) {
|
|
|
|
|
$('#radio_status').prepend(
|
|
|
|
|
`<div class="alert alert-danger radio_timeout_error" role="alert"><i class="fas fa-broadcast-tower"></i> ${errorText}</div>`
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
$('.radio_timeout_error').html(errorText);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2024-02-22 16:56:21 +00:00
|
|
|
$(".radio_timeout_error").remove();
|
2024-12-06 15:07:52 +00:00
|
|
|
updateCATStatusDisplay(data);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Update the status display
|
|
|
|
|
const updateCATStatusDisplay = (data) => {
|
|
|
|
|
let text = `<i class="fas fa-broadcast-tower"></i><span style="margin-left:10px;"></span><b>TX:</b> ${(Math.round(parseInt(data.frequency) / 100) / 10000).toFixed(4)} MHz`;
|
|
|
|
|
if (data.mode) text += `<span style="margin-left:10px"></span>${data.mode}`;
|
|
|
|
|
if (data.power && data.power !== 0) text += `<span style="margin-left:10px"></span>${data.power} W`;
|
|
|
|
|
|
|
|
|
|
let ptext = '';
|
|
|
|
|
if (data.prop_mode) {
|
|
|
|
|
ptext = data.prop_mode;
|
|
|
|
|
if (data.prop_mode === 'SAT') ptext += ` ${data.satname}`;
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
2024-12-06 15:07:52 +00:00
|
|
|
if (data.frequency_rx && data.frequency_rx !== 0) {
|
|
|
|
|
ptext += `<span style="margin-left:10px"></span><b>RX:</b> ${(Math.round(parseInt(data.frequency_rx) / 1000) / 1000).toFixed(3)} MHz`;
|
|
|
|
|
}
|
|
|
|
|
if (ptext) text += `<span style="margin-left:10px"></span>(${ptext})`;
|
|
|
|
|
|
|
|
|
|
if (!$('#radio_cat_state').length) {
|
|
|
|
|
$('#radio_status').prepend(
|
|
|
|
|
`<div aria-hidden="true"><div id="radio_cat_state" class="alert alert-success radio_cat_state" role="alert">${text}</div></div>`
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
$('#radio_cat_state').html(text);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Reset UI when no radio is selected
|
|
|
|
|
const resetUI = () => {
|
2026-05-27 22:04:14 +01:00
|
|
|
lockSatelliteFieldsToUserInput = false;
|
2026-06-23 11:35:51 +01:00
|
|
|
window.cloudlogLastCatData = null;
|
|
|
|
|
window.cloudlogLastCatRadioId = null;
|
2024-12-06 15:07:52 +00:00
|
|
|
$("#sat_name, #sat_mode, #frequency, #frequency_rx, #band_rx").val("");
|
|
|
|
|
$("#selectPropagation").val($("#selectPropagation option:first").val());
|
2026-04-07 16:21:30 +01:00
|
|
|
// Clear CAT value cache so re-selecting a radio with identical values still repopulates fields.
|
|
|
|
|
$('#frequency, #frequency_rx, #sat_name, #sat_mode, #transmit_power, #selectPropagation, #mode').removeData('catValue');
|
2024-12-06 15:07:52 +00:00
|
|
|
$(".radio_timeout_error").remove();
|
2026-05-01 10:23:08 +01:00
|
|
|
clearCATPollWarning();
|
2024-12-06 15:07:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Event listeners
|
|
|
|
|
$(document).ready(() => {
|
2025-05-28 16:56:03 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Prevents multiple form submissions by tracking submission state
|
|
|
|
|
*
|
|
|
|
|
* This script prevents duplicate QSO (contact) submissions by:
|
|
|
|
|
* - Maintaining an isSubmitting flag to track form submission state
|
|
|
|
|
* - Adding an event listener to the 'qso_input' form
|
|
|
|
|
* - Preventing form submission if a submission is already in progress
|
|
|
|
|
* - Setting the flag to true when a valid submission begins
|
|
|
|
|
*
|
|
|
|
|
* @since Unknown
|
|
|
|
|
* @global boolean isSubmitting Flag to track if form is currently being submitted
|
|
|
|
|
*/
|
|
|
|
|
let isSubmitting = false;
|
|
|
|
|
document.getElementById('qso_input').addEventListener('submit', function(e) {
|
|
|
|
|
if (isSubmitting) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
isSubmitting = true;
|
|
|
|
|
});
|
|
|
|
|
|
2026-05-01 10:23:08 +01:00
|
|
|
scheduleNextCATPoll(CAT_POLL_BASE_INTERVAL_MS);
|
2024-12-06 15:07:52 +00:00
|
|
|
|
2026-05-27 22:04:14 +01:00
|
|
|
$('#sat_name, #sat_mode, #selectPropagation').on('input change', function() {
|
|
|
|
|
const satName = String($('#sat_name').val() || '').trim();
|
|
|
|
|
const satMode = String($('#sat_mode').val() || '').trim();
|
|
|
|
|
const propMode = String($('#selectPropagation').val() || '').trim().toUpperCase();
|
|
|
|
|
lockSatelliteFieldsToUserInput = satName !== '' || satMode !== '' || propMode === 'SAT';
|
|
|
|
|
});
|
|
|
|
|
|
2024-12-06 15:07:52 +00:00
|
|
|
// Trigger updateFromCAT when any <select> with class 'radios' changes
|
|
|
|
|
$('.radios').on('change', function() {
|
2026-04-07 14:45:22 +01:00
|
|
|
catSelectionContextVersion++;
|
2026-05-01 10:23:08 +01:00
|
|
|
consecutiveCatPollFailures = 0;
|
|
|
|
|
clearCATPollWarning();
|
2026-05-27 22:04:14 +01:00
|
|
|
lockSatelliteFieldsToUserInput = false;
|
2024-12-06 15:07:52 +00:00
|
|
|
const selectedRadioID = $(this).val();
|
|
|
|
|
if (selectedRadioID === '0') {
|
|
|
|
|
resetUI();
|
2026-05-01 10:23:08 +01:00
|
|
|
scheduleNextCATPoll(CAT_POLL_BASE_INTERVAL_MS);
|
2024-12-06 15:07:52 +00:00
|
|
|
} else {
|
|
|
|
|
updateFromCAT(selectedRadioID);
|
|
|
|
|
}
|
|
|
|
|
});
|
2024-02-22 16:56:21 +00:00
|
|
|
});
|
|
|
|
|
</script>
|
2019-05-25 18:43:32 +01:00
|
|
|
|
2025-05-28 14:24:03 +01:00
|
|
|
<script>
|
|
|
|
|
$(document).ready(function() {
|
2025-10-02 17:40:49 +01:00
|
|
|
// Load saved radio selection from localStorage
|
|
|
|
|
var savedRadio = localStorage.getItem("selectedRadio");
|
|
|
|
|
if (savedRadio !== null && savedRadio !== undefined) {
|
|
|
|
|
$('.radios').val(savedRadio);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Synchronize the two selects and save to localStorage
|
2025-05-28 14:24:03 +01:00
|
|
|
$('.radios').on('change', function() {
|
|
|
|
|
const selectedValue = $(this).val(); // Get the selected value
|
|
|
|
|
$('.radios').not(this).val(selectedValue); // Update other selects to match
|
2025-10-02 17:40:49 +01:00
|
|
|
localStorage.setItem("selectedRadio", selectedValue); // Save to localStorage
|
2025-05-28 14:24:03 +01:00
|
|
|
});
|
2024-12-06 15:07:52 +00:00
|
|
|
});
|
2025-05-28 14:24:03 +01:00
|
|
|
</script>
|
2024-12-06 15:07:52 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php } ?>
|
2019-05-25 18:43:32 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->uri->segment(1) == "logbook" && $this->uri->segment(2) == "view") { ?>
|
|
|
|
|
<script>
|
|
|
|
|
var mymap = L.map('map').setView([lat, long], 5);
|
2019-05-25 18:43:32 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
L.tileLayer('<?php echo $this->optionslib->get_option('option_map_tile_server'); ?>', {
|
|
|
|
|
maxZoom: 18,
|
|
|
|
|
attribution: '<?php echo $this->optionslib->get_option('option_map_tile_server_copyright'); ?>',
|
|
|
|
|
id: 'mapbox.streets'
|
|
|
|
|
}).addTo(mymap);
|
2020-06-17 14:37:17 +01:00
|
|
|
|
2022-02-04 14:09:51 +00:00
|
|
|
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
var printer = L.easyPrint({
|
|
|
|
|
tileLayer: tiles,
|
|
|
|
|
sizeModes: ['Current', 'A4Landscape', 'A4Portrait'],
|
|
|
|
|
filename: 'myMap',
|
|
|
|
|
exportOnly: true,
|
|
|
|
|
hideControlContainer: true
|
|
|
|
|
}).addTo(mymap);
|
2020-06-17 14:37:17 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
var redIcon = L.icon({
|
|
|
|
|
iconUrl: icon_dot_url,
|
|
|
|
|
iconSize: [18, 18], // size of the icon
|
|
|
|
|
});
|
2019-05-25 18:43:32 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
L.marker([lat, long], {
|
|
|
|
|
icon: redIcon
|
|
|
|
|
}).addTo(mymap)
|
|
|
|
|
.bindPopup(callsign);
|
2019-05-25 18:43:32 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
mymap.on('click', onMapClick);
|
|
|
|
|
</script>
|
2019-05-25 18:43:32 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2019-05-25 17:59:19 +01:00
|
|
|
<?php if ($this->uri->segment(1) == "update") { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script>
|
|
|
|
|
$(document).ready(function() {
|
|
|
|
|
$('#btn_update_dxcc').bind('click', function() {
|
2025-08-09 21:45:27 +01:00
|
|
|
// Show spinner and disable button
|
|
|
|
|
$('#dxcc_spinner').show();
|
|
|
|
|
$('#btn_text').text('Updating...');
|
|
|
|
|
$('#btn_update_dxcc').prop('disabled', true);
|
|
|
|
|
|
|
|
|
|
// Show initial status immediately
|
|
|
|
|
$('#dxcc_update_status').show().html('Starting update...<br/>');
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
$.ajax({
|
|
|
|
|
url: "update/dxcc"
|
|
|
|
|
});
|
2025-08-09 21:45:27 +01:00
|
|
|
|
|
|
|
|
// Start polling immediately, then every 2 seconds for more responsive updates
|
|
|
|
|
setTimeout(update_stats, 1000);
|
2024-02-22 16:56:21 +00:00
|
|
|
});
|
2019-05-25 17:59:19 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
function update_stats() {
|
2025-08-09 21:45:27 +01:00
|
|
|
console.log('Polling status at: ' + new Date().toLocaleTimeString());
|
|
|
|
|
|
|
|
|
|
$.get('<?php echo base_url() ?>index.php/update/get_status')
|
|
|
|
|
.done(function(response) {
|
|
|
|
|
console.log('Status response:', response);
|
|
|
|
|
|
|
|
|
|
// Always update the status display with the response
|
|
|
|
|
if (response && response.trim() !== '') {
|
|
|
|
|
$('#dxcc_update_status').html(response);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if update is complete
|
|
|
|
|
if (response && typeof response === 'string' && response.substring(0, 4) === "DONE") {
|
|
|
|
|
console.log('Update completed!');
|
|
|
|
|
// Update is done - hide spinner and re-enable button
|
|
|
|
|
$('#dxcc_spinner').hide();
|
|
|
|
|
$('#btn_text').text('Update DXCC Data');
|
|
|
|
|
$('#btn_update_dxcc').prop('disabled', false);
|
|
|
|
|
} else {
|
|
|
|
|
// Continue polling if not done - reduced interval for better responsiveness
|
|
|
|
|
setTimeout(update_stats, 2000);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.fail(function(xhr, status, error) {
|
|
|
|
|
console.log('Error loading status: ' + status + ' - ' + error);
|
|
|
|
|
$('#dxcc_update_status').html('Error loading status... Retrying...<br/>');
|
|
|
|
|
|
|
|
|
|
// Reset button state on persistent error
|
|
|
|
|
if (xhr.status === 0 || xhr.status >= 500) {
|
|
|
|
|
$('#dxcc_spinner').hide();
|
|
|
|
|
$('#btn_text').text('Update DXCC Data');
|
|
|
|
|
$('#btn_update_dxcc').prop('disabled', false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setTimeout(update_stats, 5000); // Retry in 5 seconds on error
|
|
|
|
|
});
|
2024-02-22 16:56:21 +00:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
</script>
|
2019-05-21 13:44:22 +01:00
|
|
|
|
2019-06-26 21:11:44 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2024-04-20 16:01:50 +01:00
|
|
|
<?php if ($this->uri->segment(1) == "awards" && $this->uri->segment(2) == "wab") { ?>
|
2026-01-21 16:54:24 +00:00
|
|
|
<!-- WAB map handled by assets/js/sections/wab.js -->
|
2024-04-20 16:01:50 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2022-10-06 12:51:36 +02:00
|
|
|
<?php if ($this->uri->segment(1) == "gridsquares" && !empty($this->uri->segment(2))) { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script>
|
|
|
|
|
var gridsquaremap = true;
|
|
|
|
|
</script>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/leaflet/L.MaidenheadColoured.js"></script>
|
2019-06-26 21:11:44 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<script>
|
|
|
|
|
var layer = L.tileLayer('<?php echo $this->optionslib->get_option('option_map_tile_server'); ?>', {
|
|
|
|
|
maxZoom: 18,
|
|
|
|
|
attribution: '<?php echo $this->optionslib->get_option('option_map_tile_server_copyright'); ?>',
|
|
|
|
|
id: 'mapbox.streets'
|
|
|
|
|
});
|
2019-06-26 21:11:44 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
var map = L.map('gridsquare_map', {
|
|
|
|
|
layers: [layer],
|
|
|
|
|
center: [19, 0],
|
|
|
|
|
zoom: 2,
|
|
|
|
|
minZoom: 1,
|
|
|
|
|
fullscreenControl: true,
|
|
|
|
|
fullscreenControlOptions: {
|
|
|
|
|
position: 'topleft'
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var printer = L.easyPrint({
|
|
|
|
|
tileLayer: layer,
|
|
|
|
|
sizeModes: ['Current'],
|
|
|
|
|
filename: 'myMap',
|
|
|
|
|
exportOnly: true,
|
|
|
|
|
hideControlContainer: true
|
|
|
|
|
}).addTo(map);
|
|
|
|
|
|
|
|
|
|
var grid_two = <?php echo $grid_2char; ?>;
|
|
|
|
|
var grid_four = <?php echo $grid_4char; ?>;
|
|
|
|
|
var grid_six = <?php echo $grid_6char; ?>;
|
|
|
|
|
|
|
|
|
|
var grid_two_count = grid_two.length;
|
|
|
|
|
var grid_four_count = grid_four.length;
|
|
|
|
|
var grid_six_count = grid_six.length;
|
|
|
|
|
|
|
|
|
|
var grid_two_confirmed = <?php echo $grid_2char_confirmed; ?>;
|
|
|
|
|
var grid_four_confirmed = <?php echo $grid_4char_confirmed; ?>;
|
|
|
|
|
var grid_six_confirmed = <?php echo $grid_6char_confirmed; ?>;
|
|
|
|
|
|
|
|
|
|
var grid_two_confirmed_count = grid_two_confirmed.length;
|
|
|
|
|
var grid_four_confirmed_count = grid_four_confirmed.length;
|
|
|
|
|
var grid_six_confirmed_count = grid_six_confirmed.length;
|
|
|
|
|
|
|
|
|
|
if (grid_four_confirmed_count > 0) {
|
|
|
|
|
var span = document.getElementById('confirmed_grids');
|
|
|
|
|
span.innerText = span.textContent = '(' + grid_four_confirmed_count + ' <?php echo lang('gridsquares_grid_squares'); ?>' + (grid_four_confirmed_count != 1 ? 's' : '') + ') ';
|
|
|
|
|
}
|
|
|
|
|
if ((grid_four_count - grid_four_confirmed_count) > 0) {
|
|
|
|
|
var span = document.getElementById('worked_grids');
|
|
|
|
|
span.innerText = span.textContent = '(' + (grid_four_count - grid_four_confirmed_count) + ' <?php echo lang('gridsquares_grid_squares'); ?>' + (grid_four_count - grid_four_confirmed_count != 1 ? 's' : '') + ') ';
|
|
|
|
|
}
|
|
|
|
|
var span = document.getElementById('sum_grids');
|
|
|
|
|
span.innerText = span.textContent = ' <?php echo lang('gridsquares_total_count'); ?>' + ': ' + grid_four_count + ' <?php echo lang('gridsquares_grid_squares'); ?>' + (grid_four_count != 1 ? 's' : '');
|
|
|
|
|
|
|
|
|
|
var maidenhead = L.maidenhead().addTo(map);
|
|
|
|
|
|
|
|
|
|
map.on('click', onMapClick);
|
|
|
|
|
|
|
|
|
|
function onMapClick(event) {
|
|
|
|
|
var LatLng = event.latlng;
|
|
|
|
|
var lat = LatLng.lat;
|
|
|
|
|
var lng = LatLng.lng;
|
|
|
|
|
var locator = LatLng2Loc(lat, lng, 10);
|
|
|
|
|
var loc_4char = locator.substring(0, 4);
|
|
|
|
|
|
|
|
|
|
if (map.getZoom() > 2) {
|
|
|
|
|
<?php if ($this->session->userdata('user_callsign')) { ?>
|
|
|
|
|
spawnGridsquareModal(loc_4char);
|
|
|
|
|
<?php } ?>
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function spawnGridsquareModal(loc_4char) {
|
|
|
|
|
var band = '';
|
|
|
|
|
var search_type = "<?php echo $this->uri->segment(2); ?>";
|
|
|
|
|
if (search_type == "satellites") {
|
|
|
|
|
band = 'SAT';
|
|
|
|
|
} else {
|
|
|
|
|
band = "<?php echo $this->uri->segment(3); ?>";
|
|
|
|
|
}
|
|
|
|
|
$(".modal-body").empty();
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: base_url + 'index.php/awards/qso_details_ajax',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
'Searchphrase': loc_4char,
|
|
|
|
|
'Band': band,
|
|
|
|
|
'Mode': 'All',
|
|
|
|
|
'Type': 'VUCC'
|
|
|
|
|
},
|
|
|
|
|
success: function(html) {
|
|
|
|
|
$(".modal-body").html(html);
|
|
|
|
|
$(".modal-body table").addClass('table-sm');
|
|
|
|
|
$(".modal-body h5").empty();
|
|
|
|
|
var count = $('.table tr').length;
|
|
|
|
|
count = count - 1;
|
|
|
|
|
$('#qso_count').text(count);
|
|
|
|
|
if (count > 1) {
|
|
|
|
|
$('#gt1_qso').text("s");
|
|
|
|
|
} else {
|
|
|
|
|
$('#gt1_qso').text("");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (count > 0) {
|
|
|
|
|
$('#square_number').text(loc_4char);
|
|
|
|
|
$('#exampleModal').modal('show');
|
|
|
|
|
$('[data-bs-toggle="tooltip"]').tooltip({
|
|
|
|
|
boundary: 'window'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
$('.table-responsive .dropdown-toggle').off('mouseenter').on('mouseenter', function() {
|
2023-12-22 11:15:00 +01:00
|
|
|
showQsoActionsMenu($(this).closest('.dropdown'));
|
|
|
|
|
});
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
|
|
|
|
});
|
2019-06-28 18:17:54 +01:00
|
|
|
}
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->uri->segment(1) == "gridsquares" && $this->uri->segment(2) == "band") { ?>
|
2019-06-28 18:17:54 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
var bands_available = <?php echo $bands_available; ?>;
|
|
|
|
|
$('#gridsquare_bands').append('<option value="All">All</option>')
|
|
|
|
|
$.each(bands_available, function(key, value) {
|
|
|
|
|
$('#gridsquare_bands')
|
|
|
|
|
.append($("<option></option>")
|
|
|
|
|
.attr("value", value)
|
|
|
|
|
.text(value));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var num = "<?php echo $this->uri->segment(3); ?>";
|
|
|
|
|
$("#gridsquare_bands option").each(function() {
|
|
|
|
|
if ($(this).val() == num) { // EDITED THIS LINE
|
|
|
|
|
$(this).attr("selected", "selected");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$(function() {
|
|
|
|
|
// bind change event to select
|
|
|
|
|
$('#gridsquare_bands').on('change', function() {
|
|
|
|
|
var url = $(this).val(); // get selected value
|
|
|
|
|
if (url) { // require a URL
|
|
|
|
|
window.location = "<?php echo site_url('gridsquares/band/'); ?>" + url
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
<?php } ?>
|
|
|
|
|
</script>
|
2020-02-18 10:49:27 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2022-10-06 12:51:36 +02:00
|
|
|
<?php if ($this->uri->segment(1) == "activated_grids" && !empty($this->uri->segment(2))) { ?>
|
2022-02-03 22:09:05 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/leaflet/L.MaidenheadColoured.js"></script>
|
2022-02-03 22:09:05 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<script>
|
|
|
|
|
var layer = L.tileLayer('<?php echo $this->optionslib->get_option('option_map_tile_server'); ?>', {
|
|
|
|
|
maxZoom: 18,
|
|
|
|
|
attribution: '<?php echo $this->optionslib->get_option('option_map_tile_server_copyright'); ?>',
|
|
|
|
|
id: 'mapbox.streets'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var map = L.map('gridsquare_map', {
|
|
|
|
|
layers: [layer],
|
|
|
|
|
center: [19, 0],
|
|
|
|
|
zoom: 2,
|
|
|
|
|
minZoom: 1,
|
|
|
|
|
fullscreenControl: true,
|
|
|
|
|
fullscreenControlOptions: {
|
|
|
|
|
position: 'topleft'
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var grid_two = <?php echo $grid_2char; ?>;
|
|
|
|
|
var grid_four = <?php echo $grid_4char; ?>;
|
|
|
|
|
var grid_six = <?php echo $grid_6char; ?>;
|
|
|
|
|
|
|
|
|
|
var grid_two_count = grid_two.length;
|
|
|
|
|
var grid_four_count = grid_four.length;
|
|
|
|
|
var grid_six_count = grid_six.length;
|
|
|
|
|
|
|
|
|
|
var grid_two_confirmed = <?php echo $grid_2char_confirmed; ?>;
|
|
|
|
|
var grid_four_confirmed = <?php echo $grid_4char_confirmed; ?>;
|
|
|
|
|
var grid_six_confirmed = <?php echo $grid_6char_confirmed; ?>;
|
|
|
|
|
|
|
|
|
|
var grid_two_confirmed_count = grid_two_confirmed.length;
|
|
|
|
|
var grid_four_confirmed_count = grid_four_confirmed.length;
|
|
|
|
|
var grid_six_confirmed_count = grid_six_confirmed.length;
|
|
|
|
|
|
|
|
|
|
if (grid_four_confirmed_count > 0) {
|
|
|
|
|
var span = document.getElementById('confirmed_grids');
|
|
|
|
|
span.innerText = span.textContent = '(' + grid_four_confirmed_count + ' <?php echo lang('gridsquares_grid_squares'); ?>' + (grid_four_confirmed_count != 1 ? 's' : '') + ') ';
|
2022-02-03 22:09:05 +01:00
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
if ((grid_four_count - grid_four_confirmed_count) > 0) {
|
|
|
|
|
var span = document.getElementById('activated_grids');
|
|
|
|
|
span.innerText = span.textContent = '(' + (grid_four_count - grid_four_confirmed_count) + ' <?php echo lang('gridsquares_grid_squares'); ?>' + (grid_four_count - grid_four_confirmed_count != 1 ? 's' : '') + ') ';
|
|
|
|
|
}
|
|
|
|
|
var span = document.getElementById('sum_grids');
|
|
|
|
|
span.innerText = span.textContent = ' <?php echo lang('gridsquares_total_count'); ?>' + ': ' + grid_four_count + ' <?php echo lang('gridsquares_grid_squares'); ?>' + (grid_four_count != 1 ? 's' : '');
|
|
|
|
|
|
|
|
|
|
var maidenhead = L.maidenhead().addTo(map);
|
|
|
|
|
|
|
|
|
|
map.on('click', onMapClick);
|
|
|
|
|
|
|
|
|
|
function onMapClick(event) {
|
|
|
|
|
var LatLng = event.latlng;
|
|
|
|
|
var lat = LatLng.lat;
|
|
|
|
|
var lng = LatLng.lng;
|
|
|
|
|
var locator = LatLng2Loc(lat, lng, 10);
|
|
|
|
|
var loc_4char = locator.substring(0, 4);
|
|
|
|
|
|
|
|
|
|
if (map.getZoom() > 2) {
|
|
|
|
|
<?php if ($this->session->userdata('user_callsign')) { ?>
|
|
|
|
|
var band = '';
|
|
|
|
|
var search_type = "<?php echo $this->uri->segment(2); ?>";
|
|
|
|
|
if (search_type == "satellites") {
|
|
|
|
|
band = 'SAT';
|
|
|
|
|
} else {
|
|
|
|
|
band = "<?php echo $this->uri->segment(3); ?>";
|
|
|
|
|
}
|
|
|
|
|
$(".modal-body").empty();
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: base_url + 'index.php/activated_grids/qso_details_ajax',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
'Searchphrase': loc_4char,
|
|
|
|
|
'Band': band,
|
|
|
|
|
'Mode': 'All',
|
|
|
|
|
},
|
|
|
|
|
success: function(html) {
|
|
|
|
|
$(".modal-body").html(html);
|
|
|
|
|
$(".modal-body table").addClass('table-sm');
|
|
|
|
|
$(".modal-body h5").empty();
|
|
|
|
|
var count = $('.table tr').length;
|
|
|
|
|
count = count - 1;
|
|
|
|
|
$('#qso_count').text(count);
|
|
|
|
|
if (count > 1) {
|
|
|
|
|
$('#gt1_qso').text("s");
|
|
|
|
|
} else {
|
|
|
|
|
$('#gt1_qso').text("");
|
|
|
|
|
}
|
2022-02-03 22:09:05 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
if (count > 0) {
|
|
|
|
|
$('#square_number').text(loc_4char);
|
|
|
|
|
$('#exampleModal').modal('show');
|
|
|
|
|
$('[data-bs-toggle="tooltip"]').tooltip({
|
|
|
|
|
boundary: 'window'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
$('.table-responsive .dropdown-toggle').off('mouseenter').on('mouseenter', function() {
|
|
|
|
|
showQsoActionsMenu($(this).closest('.dropdown'));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
<?php } ?>
|
|
|
|
|
}
|
|
|
|
|
};
|
2019-06-28 18:17:54 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->uri->segment(1) == "activated_grids" && $this->uri->segment(2) == "band") { ?>
|
|
|
|
|
|
|
|
|
|
var bands_available = <?php echo $bands_available; ?>;
|
|
|
|
|
$('#gridsquare_bands').append('<option value="All">All</option>')
|
|
|
|
|
$.each(bands_available, function(key, value) {
|
|
|
|
|
$('#gridsquare_bands')
|
|
|
|
|
.append($("<option></option>")
|
|
|
|
|
.attr("value", value)
|
|
|
|
|
.text(value));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var num = "<?php echo $this->uri->segment(3); ?>";
|
|
|
|
|
$("#gridsquare_bands option").each(function() {
|
|
|
|
|
if ($(this).val() == num) { // EDITED THIS LINE
|
|
|
|
|
$(this).attr("selected", "selected");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$(function() {
|
|
|
|
|
// bind change event to select
|
|
|
|
|
$('#gridsquare_bands').on('change', function() {
|
|
|
|
|
var url = $(this).val(); // get selected value
|
|
|
|
|
if (url) { // require a URL
|
|
|
|
|
window.location = "<?php echo site_url('activated_grids/band/'); ?>" + url
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
<?php } ?>
|
|
|
|
|
</script>
|
2020-02-18 10:49:27 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2020-04-24 23:29:01 +02:00
|
|
|
<?php if ($this->uri->segment(1) == "dayswithqso") { ?>
|
2020-11-08 11:26:38 +01:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/chart.js"></script>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/dayswithqso.js"></script>
|
2020-04-24 23:29:01 +02:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2020-02-18 10:49:27 +01:00
|
|
|
<?php if ($this->uri->segment(1) == "distances") { ?>
|
2020-11-08 11:26:38 +01:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/highstock.js"></script>
|
2020-12-02 14:49:14 +01:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/highstock/exporting.js"></script>
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/highstock/offline-exporting.js"></script>
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/highstock/export-data.js"></script>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/distances.js"></script>
|
2019-05-14 16:46:16 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2020-04-25 23:11:13 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->uri->segment(1) == "hrdlog") { ?>
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/hrdlog.js"></script>
|
|
|
|
|
<?php } ?>
|
|
|
|
|
<?php if ($this->uri->segment(1) == "qrz") { ?>
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/qrzlogbook.js"></script>
|
|
|
|
|
<?php } ?>
|
|
|
|
|
<?php if ($this->uri->segment(1) == "webadif") { ?>
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/webadif.js"></script>
|
|
|
|
|
<?php } ?>
|
2020-09-18 12:17:41 +02:00
|
|
|
|
2020-09-22 21:08:26 +02:00
|
|
|
<script>
|
2024-02-22 16:56:21 +00:00
|
|
|
function displayQso(id) {
|
|
|
|
|
var baseURL = "<?php echo base_url(); ?>";
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: baseURL + 'index.php/logbook/view/' + id,
|
|
|
|
|
type: 'post',
|
|
|
|
|
success: function(html) {
|
|
|
|
|
BootstrapDialog.show({
|
|
|
|
|
title: lang_general_word_qso_data,
|
|
|
|
|
cssClass: 'qso-dialog',
|
|
|
|
|
size: BootstrapDialog.SIZE_WIDE,
|
|
|
|
|
nl2br: false,
|
|
|
|
|
message: html,
|
|
|
|
|
onshown: function(dialog) {
|
|
|
|
|
var qsoid = $("#qsoid").text();
|
|
|
|
|
$(".editButton").html('<a class="btn btn-primary" id="edit_qso" href="javascript:qso_edit(' + qsoid + ')"><i class="fas fa-edit"></i><?php echo lang('general_edit_qso'); ?></a>');
|
|
|
|
|
var lat = $("#lat").text();
|
|
|
|
|
var long = $("#long").text();
|
|
|
|
|
var callsign = $("#callsign").text();
|
|
|
|
|
var mymap = L.map('mapqso').setView([lat, long], 5);
|
|
|
|
|
|
|
|
|
|
var tiles = L.tileLayer('<?php echo $this->optionslib->get_option('option_map_tile_server'); ?>', {
|
|
|
|
|
maxZoom: 18,
|
|
|
|
|
attribution: '<?php echo $this->optionslib->get_option('option_map_tile_server_copyright'); ?>',
|
|
|
|
|
}).addTo(mymap);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var printer = L.easyPrint({
|
|
|
|
|
tileLayer: tiles,
|
|
|
|
|
sizeModes: ['Current'],
|
|
|
|
|
filename: 'myMap',
|
|
|
|
|
exportOnly: true,
|
|
|
|
|
hideControlContainer: true
|
|
|
|
|
}).addTo(mymap);
|
|
|
|
|
|
|
|
|
|
var redIcon = L.icon({
|
|
|
|
|
iconUrl: icon_dot_url,
|
|
|
|
|
iconSize: [18, 18], // size of the icon
|
|
|
|
|
});
|
2023-11-29 20:16:53 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
L.marker([lat, long], {
|
|
|
|
|
icon: redIcon
|
|
|
|
|
}).addTo(mymap)
|
|
|
|
|
.bindPopup(callsign);
|
2023-11-29 20:16:53 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
},
|
|
|
|
|
});
|
2023-11-29 20:16:53 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
|
|
|
|
});
|
2023-11-29 20:16:53 +01:00
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
</script>
|
2020-09-20 14:09:46 +02:00
|
|
|
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->uri->segment(2) == "dxcc") { ?>
|
2020-09-22 21:08:26 +02:00
|
|
|
<script>
|
2024-02-22 16:56:21 +00:00
|
|
|
$('.tabledxcc').DataTable({
|
2020-09-22 21:08:26 +02:00
|
|
|
"pageLength": 25,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: false,
|
2024-02-22 16:56:21 +00:00
|
|
|
"scrollY": "400px",
|
2020-09-22 21:08:26 +02:00
|
|
|
"scrollCollapse": true,
|
2024-02-22 16:56:21 +00:00
|
|
|
"paging": false,
|
2020-10-19 19:16:42 +02:00
|
|
|
"scrollX": true,
|
2024-01-03 22:03:11 -05:00
|
|
|
"language": {
|
2024-01-03 22:18:20 -05:00
|
|
|
url: getDataTablesLanguageUrl(),
|
2024-01-03 22:03:11 -05:00
|
|
|
},
|
2020-10-19 19:16:42 +02:00
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$('.tablesummary').DataTable({
|
|
|
|
|
info: false,
|
|
|
|
|
searching: false,
|
|
|
|
|
ordering: false,
|
2024-02-22 16:56:21 +00:00
|
|
|
"paging": false,
|
2024-01-03 22:03:11 -05:00
|
|
|
"language": {
|
2024-01-03 22:18:20 -05:00
|
|
|
url: getDataTablesLanguageUrl(),
|
2024-01-03 22:03:11 -05:00
|
|
|
},
|
2020-10-19 19:16:42 +02:00
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
2020-09-22 21:08:26 +02:00
|
|
|
});
|
2020-09-20 11:09:22 +02:00
|
|
|
|
2021-09-10 18:53:24 +02:00
|
|
|
// change color of csv-button if dark mode is chosen
|
|
|
|
|
if (isDarkModeTheme()) {
|
2020-10-19 19:16:42 +02:00
|
|
|
$(".buttons-csv").css("color", "white");
|
|
|
|
|
}
|
2020-09-22 21:08:26 +02:00
|
|
|
</script>
|
|
|
|
|
<?php } ?>
|
2020-09-20 11:02:29 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->uri->segment(2) == "waja") { ?>
|
2020-09-22 21:08:26 +02:00
|
|
|
<script>
|
2024-02-22 16:56:21 +00:00
|
|
|
$('.tablewaja').DataTable({
|
2020-10-06 22:01:29 +02:00
|
|
|
"pageLength": 25,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: false,
|
2024-02-22 16:56:21 +00:00
|
|
|
"scrollY": "400px",
|
2020-10-06 22:01:29 +02:00
|
|
|
"scrollCollapse": true,
|
2024-02-22 16:56:21 +00:00
|
|
|
"paging": false,
|
2020-10-19 19:16:42 +02:00
|
|
|
"scrollX": true,
|
2024-01-03 22:03:11 -05:00
|
|
|
"language": {
|
2024-01-03 22:18:20 -05:00
|
|
|
url: getDataTablesLanguageUrl(),
|
2024-01-03 22:03:11 -05:00
|
|
|
},
|
2020-10-19 19:16:42 +02:00
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
2020-10-06 22:01:29 +02:00
|
|
|
});
|
|
|
|
|
|
2020-10-19 19:16:42 +02:00
|
|
|
$('.tablesummary').DataTable({
|
|
|
|
|
info: false,
|
|
|
|
|
searching: false,
|
|
|
|
|
ordering: false,
|
2024-02-22 16:56:21 +00:00
|
|
|
"paging": false,
|
2024-01-03 22:03:11 -05:00
|
|
|
"language": {
|
2024-01-03 22:18:20 -05:00
|
|
|
url: getDataTablesLanguageUrl(),
|
2024-01-03 22:03:11 -05:00
|
|
|
},
|
2020-10-19 19:16:42 +02:00
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
|
2021-09-10 18:53:24 +02:00
|
|
|
// change color of csv-button if dark mode is chosen
|
|
|
|
|
if (isDarkModeTheme()) {
|
2020-10-19 19:16:42 +02:00
|
|
|
$(".buttons-csv").css("color", "white");
|
|
|
|
|
}
|
2020-09-22 21:08:26 +02:00
|
|
|
</script>
|
|
|
|
|
<?php } ?>
|
2020-09-20 11:02:29 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->uri->segment(2) == "vucc_band") { ?>
|
2020-09-22 21:08:26 +02:00
|
|
|
<script>
|
2024-02-22 16:56:21 +00:00
|
|
|
$('.tablevucc').DataTable({
|
2020-10-06 22:01:29 +02:00
|
|
|
"pageLength": 25,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: false,
|
2024-02-22 16:56:21 +00:00
|
|
|
"scrollY": "400px",
|
2020-10-06 22:01:29 +02:00
|
|
|
"scrollCollapse": true,
|
2024-02-22 16:56:21 +00:00
|
|
|
"paging": false,
|
2020-10-19 19:16:42 +02:00
|
|
|
"scrollX": true,
|
2024-01-03 22:03:11 -05:00
|
|
|
"language": {
|
2024-01-03 22:18:20 -05:00
|
|
|
url: getDataTablesLanguageUrl(),
|
2024-01-03 22:03:11 -05:00
|
|
|
},
|
2020-10-19 19:16:42 +02:00
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
// change color of csv-button if dark mode is chosen
|
|
|
|
|
if (isDarkModeTheme()) {
|
|
|
|
|
$(".buttons-csv").css("color", "white");
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
<?php } ?>
|
|
|
|
|
|
|
|
|
|
<?php if ($this->uri->segment(2) == "iota") { ?>
|
|
|
|
|
<script>
|
|
|
|
|
$('.tableiota').DataTable({
|
|
|
|
|
"pageLength": 25,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: false,
|
|
|
|
|
"scrollY": "400px",
|
|
|
|
|
"scrollCollapse": true,
|
|
|
|
|
"paging": false,
|
|
|
|
|
"scrollX": true,
|
|
|
|
|
"language": {
|
|
|
|
|
url: getDataTablesLanguageUrl(),
|
|
|
|
|
},
|
|
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$('.tablesummary').DataTable({
|
|
|
|
|
info: false,
|
|
|
|
|
searching: false,
|
|
|
|
|
ordering: false,
|
|
|
|
|
"paging": false,
|
|
|
|
|
"language": {
|
|
|
|
|
url: getDataTablesLanguageUrl(),
|
|
|
|
|
},
|
|
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
2020-10-06 22:01:29 +02:00
|
|
|
});
|
|
|
|
|
|
2021-09-10 18:53:24 +02:00
|
|
|
// change color of csv-button if dark mode is chosen
|
|
|
|
|
if (isDarkModeTheme()) {
|
2020-10-19 19:16:42 +02:00
|
|
|
$(".buttons-csv").css("color", "white");
|
|
|
|
|
}
|
2020-09-22 21:08:26 +02:00
|
|
|
</script>
|
2024-02-22 16:56:21 +00:00
|
|
|
|
2020-09-22 21:08:26 +02:00
|
|
|
<?php } ?>
|
2020-09-20 11:02:29 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->uri->segment(2) == "cq") { ?>
|
|
|
|
|
<script>
|
2026-01-07 11:02:44 +00:00
|
|
|
// Initialize DataTable for summary
|
2024-02-22 16:56:21 +00:00
|
|
|
$('.tablesummary').DataTable({
|
|
|
|
|
info: false,
|
|
|
|
|
searching: false,
|
|
|
|
|
ordering: false,
|
|
|
|
|
"paging": false,
|
|
|
|
|
"language": {
|
|
|
|
|
url: getDataTablesLanguageUrl(),
|
|
|
|
|
},
|
|
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
|
|
|
|
});
|
2021-01-31 15:27:56 +01:00
|
|
|
|
2026-01-07 11:25:03 +00:00
|
|
|
// Helper function to update statistics cards based on visible rows
|
|
|
|
|
function updateCqStatistics() {
|
|
|
|
|
const table = document.getElementById('cq_table');
|
|
|
|
|
const rows = table.querySelectorAll('tbody tr');
|
|
|
|
|
|
|
|
|
|
let workedCount = 0;
|
|
|
|
|
let confirmedCount = 0;
|
|
|
|
|
|
|
|
|
|
rows.forEach((row, rowIndex) => {
|
|
|
|
|
// Check if row is actually visible (not display:none)
|
|
|
|
|
const isVisible = row.offsetParent !== null || row.style.display === '';
|
|
|
|
|
if (isVisible || row.style.display !== 'none') {
|
|
|
|
|
const cells = row.querySelectorAll('td');
|
|
|
|
|
|
|
|
|
|
if (cells.length > 1) { // At least zone + one band
|
|
|
|
|
// Check all band columns (starting from column 1, after zone)
|
|
|
|
|
let hasWorked = false;
|
|
|
|
|
let hasConfirmed = false;
|
|
|
|
|
|
|
|
|
|
for (let i = 1; i < cells.length; i++) {
|
|
|
|
|
const cellValue = cells[i].textContent.trim();
|
|
|
|
|
// If cell is not empty and not a dash, it's worked
|
|
|
|
|
if (cellValue && cellValue !== '-') {
|
|
|
|
|
hasWorked = true;
|
|
|
|
|
// If it contains 'C', it's confirmed
|
|
|
|
|
if (cellValue.toUpperCase().includes('C')) {
|
|
|
|
|
hasConfirmed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hasWorked) workedCount++;
|
|
|
|
|
if (hasConfirmed) confirmedCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Update the stats cards
|
|
|
|
|
const statsCards = document.querySelectorAll('.stats-card');
|
|
|
|
|
statsCards.forEach(card => {
|
|
|
|
|
const labels = card.querySelectorAll('.progress-label');
|
|
|
|
|
labels.forEach(label => {
|
|
|
|
|
const parent = label.parentElement;
|
|
|
|
|
if (label.textContent.includes('Total Worked')) {
|
|
|
|
|
const numberDiv = parent.querySelector('.stats-number');
|
|
|
|
|
if (numberDiv) {
|
|
|
|
|
numberDiv.textContent = workedCount;
|
|
|
|
|
}
|
|
|
|
|
} else if (label.textContent.includes('Total Confirmed')) {
|
|
|
|
|
const numberDiv = parent.querySelector('.stats-number');
|
|
|
|
|
if (numberDiv) {
|
|
|
|
|
numberDiv.textContent = confirmedCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Update milestone indicators
|
|
|
|
|
const milestoneCard = document.querySelector('.stats-card.milestone');
|
|
|
|
|
if (milestoneCard) {
|
|
|
|
|
const cols = milestoneCard.querySelectorAll('.col-6');
|
|
|
|
|
cols.forEach(col => {
|
|
|
|
|
const label = col.querySelector('div:first-child');
|
|
|
|
|
const indicator = col.querySelector('div:nth-child(2)');
|
|
|
|
|
|
|
|
|
|
if (label && indicator) {
|
|
|
|
|
if (label.textContent.includes('All Worked')) {
|
|
|
|
|
indicator.textContent = workedCount === 40 ? '✓' : '✗';
|
|
|
|
|
indicator.style.color = workedCount === 40 ? '#28a745' : '#dc3545';
|
|
|
|
|
} else if (label.textContent.includes('All Confirmed')) {
|
|
|
|
|
indicator.textContent = confirmedCount === 40 ? '✓' : '✗';
|
|
|
|
|
indicator.style.color = confirmedCount === 40 ? '#28a745' : '#dc3545';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Call on page load to initialize with filtered data
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
2026-01-07 11:38:09 +00:00
|
|
|
setTimeout(function() {
|
|
|
|
|
updateCqStatistics();
|
|
|
|
|
}, 500);
|
2026-01-07 11:25:03 +00:00
|
|
|
});
|
|
|
|
|
|
2026-01-07 11:02:44 +00:00
|
|
|
// Table search functionality
|
|
|
|
|
const searchInput = document.getElementById('searchInput');
|
|
|
|
|
if (searchInput) {
|
|
|
|
|
searchInput.addEventListener('keyup', function() {
|
|
|
|
|
const searchTerm = this.value.toLowerCase();
|
|
|
|
|
const table = document.getElementById('cq_table');
|
|
|
|
|
const rows = table.querySelectorAll('tbody tr');
|
|
|
|
|
|
|
|
|
|
rows.forEach(row => {
|
2026-01-07 11:25:03 +00:00
|
|
|
const cqZone = row.querySelectorAll('td')[0].textContent.toLowerCase();
|
2026-01-07 11:02:44 +00:00
|
|
|
|
|
|
|
|
if (cqZone.includes(searchTerm)) {
|
|
|
|
|
row.style.display = '';
|
|
|
|
|
} else {
|
|
|
|
|
row.style.display = 'none';
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-01-07 11:38:09 +00:00
|
|
|
|
|
|
|
|
updateCqStatistics();
|
2026-01-07 11:02:44 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sortable column functionality
|
|
|
|
|
document.querySelectorAll('.sortable').forEach(header => {
|
|
|
|
|
header.addEventListener('click', function() {
|
|
|
|
|
const table = document.getElementById('cq_table');
|
|
|
|
|
const column = parseInt(this.dataset.column);
|
|
|
|
|
const tbody = table.querySelector('tbody');
|
|
|
|
|
const rows = Array.from(tbody.querySelectorAll('tr'));
|
|
|
|
|
|
|
|
|
|
// Toggle sort direction
|
|
|
|
|
const isAsc = this.classList.contains('asc');
|
|
|
|
|
document.querySelectorAll('.sortable').forEach(h => h.classList.remove('asc', 'desc'));
|
|
|
|
|
|
|
|
|
|
if (isAsc) {
|
|
|
|
|
this.classList.add('desc');
|
|
|
|
|
rows.reverse();
|
|
|
|
|
} else {
|
|
|
|
|
this.classList.add('asc');
|
|
|
|
|
rows.sort((a, b) => {
|
|
|
|
|
const aVal = a.querySelectorAll('td')[column].textContent.trim();
|
|
|
|
|
const bVal = b.querySelectorAll('td')[column].textContent.trim();
|
|
|
|
|
|
|
|
|
|
// Try numeric sort first
|
|
|
|
|
const aNum = parseFloat(aVal);
|
|
|
|
|
const bNum = parseFloat(bVal);
|
|
|
|
|
if (!isNaN(aNum) && !isNaN(bNum)) {
|
|
|
|
|
return aNum - bNum;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fall back to string sort
|
|
|
|
|
return aVal.localeCompare(bVal);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rows.forEach(row => tbody.appendChild(row));
|
2026-01-07 11:38:09 +00:00
|
|
|
updateCqStatistics();
|
2026-01-07 11:02:44 +00:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
// change color of csv-button if dark mode is chosen
|
|
|
|
|
if (isDarkModeTheme()) {
|
|
|
|
|
$(".buttons-csv").css("color", "white");
|
2020-08-19 07:57:35 +02:00
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
</script>
|
2026-01-07 11:02:44 +00:00
|
|
|
<script>
|
|
|
|
|
// Preset filter functions
|
|
|
|
|
function applyPreset(preset) {
|
|
|
|
|
const form = document.getElementById('cqForm');
|
|
|
|
|
|
|
|
|
|
// Reset all checkboxes first
|
|
|
|
|
form.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = false);
|
|
|
|
|
|
|
|
|
|
// All QSL types
|
|
|
|
|
const allQslTypes = ['qsl', 'lotw', 'eqsl'];
|
|
|
|
|
|
|
|
|
|
switch(preset) {
|
|
|
|
|
case 'confirmed':
|
|
|
|
|
document.getElementById('confirmed').checked = true;
|
|
|
|
|
allQslTypes.forEach(type => {
|
|
|
|
|
const el = document.getElementById(type);
|
|
|
|
|
if (el) el.checked = true;
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
case 'worked':
|
|
|
|
|
document.getElementById('worked').checked = true;
|
|
|
|
|
allQslTypes.forEach(type => {
|
|
|
|
|
const el = document.getElementById(type);
|
|
|
|
|
if (el) el.checked = true;
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
case 'unworked':
|
|
|
|
|
document.getElementById('notworked').checked = true;
|
|
|
|
|
allQslTypes.forEach(type => {
|
|
|
|
|
const el = document.getElementById(type);
|
|
|
|
|
if (el) el.checked = true;
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
form.submit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function resetFilters() {
|
|
|
|
|
const form = document.getElementById('cqForm');
|
|
|
|
|
form.reset();
|
|
|
|
|
// Make sure worked, confirmed, and QSL types are checked by default
|
|
|
|
|
document.getElementById('worked').checked = true;
|
|
|
|
|
document.getElementById('confirmed').checked = true;
|
|
|
|
|
document.getElementById('qsl').checked = true;
|
|
|
|
|
document.getElementById('lotw').checked = true;
|
|
|
|
|
form.submit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CSV Export functionality
|
|
|
|
|
function exportTableToCSV(filename) {
|
|
|
|
|
const table = document.getElementById('cq_table');
|
|
|
|
|
let csv = [];
|
|
|
|
|
|
|
|
|
|
// Add header row
|
|
|
|
|
const headers = Array.from(table.querySelectorAll('thead tr td')).map(h => h.textContent.trim());
|
|
|
|
|
csv.push(headers.join(','));
|
|
|
|
|
|
|
|
|
|
// Add data rows
|
|
|
|
|
const rows = table.querySelectorAll('tbody tr');
|
|
|
|
|
rows.forEach(row => {
|
|
|
|
|
if (row.style.display !== 'none') { // Only export visible rows
|
|
|
|
|
const cells = Array.from(row.querySelectorAll('td')).map(cell => {
|
|
|
|
|
let text = cell.textContent.trim();
|
|
|
|
|
// Escape quotes and wrap in quotes if contains comma
|
|
|
|
|
text = text.replace(/"/g, '""');
|
|
|
|
|
return '"' + text + '"';
|
|
|
|
|
});
|
|
|
|
|
csv.push(cells.join(','));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Create blob and download
|
|
|
|
|
const blob = new Blob([csv.join('\n')], { type: 'text/csv' });
|
|
|
|
|
const url = window.URL.createObjectURL(blob);
|
|
|
|
|
const a = document.createElement('a');
|
|
|
|
|
a.href = url;
|
|
|
|
|
a.download = filename;
|
|
|
|
|
document.body.appendChild(a);
|
|
|
|
|
a.click();
|
|
|
|
|
window.URL.revokeObjectURL(url);
|
|
|
|
|
document.body.removeChild(a);
|
|
|
|
|
}
|
|
|
|
|
</script>
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php } ?>
|
2020-08-25 22:03:54 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->uri->segment(2) == "was") { ?>
|
|
|
|
|
<script>
|
2026-01-07 11:25:03 +00:00
|
|
|
// Initialize DataTable for summary
|
2024-02-22 16:56:21 +00:00
|
|
|
$('.tablesummary').DataTable({
|
|
|
|
|
info: false,
|
|
|
|
|
searching: false,
|
|
|
|
|
ordering: false,
|
|
|
|
|
"paging": false,
|
|
|
|
|
"language": {
|
|
|
|
|
url: getDataTablesLanguageUrl(),
|
|
|
|
|
},
|
|
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
|
2026-01-07 11:25:03 +00:00
|
|
|
// Helper function to update statistics cards based on visible rows
|
|
|
|
|
function updateWasStatistics() {
|
|
|
|
|
const table = document.getElementById('was_table');
|
|
|
|
|
const rows = table.querySelectorAll('tbody tr');
|
|
|
|
|
|
|
|
|
|
let workedCount = 0;
|
|
|
|
|
let confirmedCount = 0;
|
|
|
|
|
|
|
|
|
|
rows.forEach((row, rowIndex) => {
|
|
|
|
|
// Check if row is actually visible (not display:none)
|
|
|
|
|
const isVisible = row.offsetParent !== null || row.style.display === '';
|
|
|
|
|
if (isVisible || row.style.display !== 'none') {
|
|
|
|
|
const cells = row.querySelectorAll('td');
|
|
|
|
|
|
|
|
|
|
if (cells.length > 1) { // At least state + one band
|
|
|
|
|
// Check all band columns (starting from column 1, after state name)
|
|
|
|
|
let hasWorked = false;
|
|
|
|
|
let hasConfirmed = false;
|
|
|
|
|
|
|
|
|
|
for (let i = 1; i < cells.length; i++) {
|
|
|
|
|
const cellValue = cells[i].textContent.trim();
|
|
|
|
|
// If cell is not empty and not a dash, it's worked
|
|
|
|
|
if (cellValue && cellValue !== '-') {
|
|
|
|
|
hasWorked = true;
|
|
|
|
|
// If it contains 'C', it's confirmed
|
|
|
|
|
if (cellValue.toUpperCase().includes('C')) {
|
|
|
|
|
hasConfirmed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hasWorked) workedCount++;
|
|
|
|
|
if (hasConfirmed) confirmedCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Update the stats cards
|
|
|
|
|
const statsCards = document.querySelectorAll('.stats-card');
|
|
|
|
|
statsCards.forEach(card => {
|
|
|
|
|
const labels = card.querySelectorAll('.progress-label');
|
|
|
|
|
labels.forEach(label => {
|
|
|
|
|
const parent = label.parentElement;
|
|
|
|
|
if (label.textContent.includes('Total Worked')) {
|
|
|
|
|
const numberDiv = parent.querySelector('.stats-number');
|
|
|
|
|
if (numberDiv) {
|
|
|
|
|
numberDiv.textContent = workedCount;
|
|
|
|
|
}
|
|
|
|
|
} else if (label.textContent.includes('Total Confirmed')) {
|
|
|
|
|
const numberDiv = parent.querySelector('.stats-number');
|
|
|
|
|
if (numberDiv) {
|
|
|
|
|
numberDiv.textContent = confirmedCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Update milestone indicators
|
|
|
|
|
const milestoneCard = document.querySelector('.stats-card.milestone');
|
|
|
|
|
if (milestoneCard) {
|
|
|
|
|
const cols = milestoneCard.querySelectorAll('.col-6');
|
|
|
|
|
cols.forEach(col => {
|
|
|
|
|
const label = col.querySelector('div:first-child');
|
|
|
|
|
const indicator = col.querySelector('div:nth-child(2)');
|
|
|
|
|
|
|
|
|
|
if (label && indicator) {
|
|
|
|
|
if (label.textContent.includes('All Worked')) {
|
|
|
|
|
indicator.textContent = workedCount === 50 ? '✓' : '✗';
|
|
|
|
|
indicator.style.color = workedCount === 50 ? '#28a745' : '#dc3545';
|
|
|
|
|
} else if (label.textContent.includes('All Confirmed')) {
|
|
|
|
|
indicator.textContent = confirmedCount === 50 ? '✓' : '✗';
|
|
|
|
|
indicator.style.color = confirmedCount === 50 ? '#28a745' : '#dc3545';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Call on page load to initialize with filtered data
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
2026-01-07 11:38:09 +00:00
|
|
|
setTimeout(function() {
|
|
|
|
|
updateWasStatistics();
|
|
|
|
|
}, 500);
|
2026-01-07 11:25:03 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Table search functionality
|
|
|
|
|
const searchInput = document.getElementById('searchInput');
|
|
|
|
|
if (searchInput) {
|
|
|
|
|
searchInput.addEventListener('keyup', function() {
|
|
|
|
|
const searchTerm = this.value.toLowerCase();
|
|
|
|
|
const table = document.getElementById('was_table');
|
|
|
|
|
const rows = table.querySelectorAll('tbody tr');
|
|
|
|
|
|
|
|
|
|
rows.forEach(row => {
|
|
|
|
|
const state = row.querySelectorAll('td')[0].textContent.toLowerCase();
|
|
|
|
|
|
|
|
|
|
if (state.includes(searchTerm)) {
|
|
|
|
|
row.style.display = '';
|
|
|
|
|
} else {
|
|
|
|
|
row.style.display = 'none';
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-01-07 11:38:09 +00:00
|
|
|
|
|
|
|
|
updateWasStatistics();
|
2026-01-07 11:25:03 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sortable column functionality
|
|
|
|
|
document.querySelectorAll('.sortable').forEach(header => {
|
|
|
|
|
header.addEventListener('click', function() {
|
|
|
|
|
const table = document.getElementById('was_table');
|
|
|
|
|
const column = parseInt(this.dataset.column);
|
|
|
|
|
const tbody = table.querySelector('tbody');
|
|
|
|
|
const rows = Array.from(tbody.querySelectorAll('tr'));
|
|
|
|
|
|
|
|
|
|
// Toggle sort direction
|
|
|
|
|
const isAsc = this.classList.contains('asc');
|
|
|
|
|
document.querySelectorAll('.sortable').forEach(h => h.classList.remove('asc', 'desc'));
|
|
|
|
|
|
|
|
|
|
if (isAsc) {
|
|
|
|
|
this.classList.add('desc');
|
|
|
|
|
rows.reverse();
|
|
|
|
|
} else {
|
|
|
|
|
this.classList.add('asc');
|
|
|
|
|
rows.sort((a, b) => {
|
|
|
|
|
const aVal = a.querySelectorAll('td')[column].textContent.trim();
|
|
|
|
|
const bVal = b.querySelectorAll('td')[column].textContent.trim();
|
|
|
|
|
|
|
|
|
|
// Try numeric sort first
|
|
|
|
|
const aNum = parseFloat(aVal);
|
|
|
|
|
const bNum = parseFloat(bVal);
|
|
|
|
|
if (!isNaN(aNum) && !isNaN(bNum)) {
|
|
|
|
|
return aNum - bNum;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fall back to string sort
|
|
|
|
|
return aVal.localeCompare(bVal);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rows.forEach(row => tbody.appendChild(row));
|
2026-01-07 11:38:09 +00:00
|
|
|
updateWasStatistics();
|
2026-01-07 11:25:03 +00:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
// change color of csv-button if dark mode is chosen
|
|
|
|
|
if (isDarkModeTheme()) {
|
|
|
|
|
$(".buttons-csv").css("color", "white");
|
|
|
|
|
}
|
|
|
|
|
</script>
|
2026-01-07 11:25:03 +00:00
|
|
|
<script>
|
|
|
|
|
// Preset filter functions
|
|
|
|
|
function applyPreset(preset) {
|
|
|
|
|
const form = document.getElementById('wasForm');
|
|
|
|
|
|
|
|
|
|
// Reset all checkboxes first
|
|
|
|
|
form.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = false);
|
|
|
|
|
|
|
|
|
|
// All QSL types
|
|
|
|
|
const allQslTypes = ['qsl', 'lotw', 'eqsl'];
|
|
|
|
|
|
|
|
|
|
switch(preset) {
|
|
|
|
|
case 'confirmed':
|
|
|
|
|
document.getElementById('confirmed').checked = true;
|
|
|
|
|
allQslTypes.forEach(type => {
|
|
|
|
|
const el = document.getElementById(type);
|
|
|
|
|
if (el) el.checked = true;
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
case 'worked':
|
|
|
|
|
document.getElementById('worked').checked = true;
|
|
|
|
|
allQslTypes.forEach(type => {
|
|
|
|
|
const el = document.getElementById(type);
|
|
|
|
|
if (el) el.checked = true;
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
case 'unworked':
|
|
|
|
|
document.getElementById('notworked').checked = true;
|
|
|
|
|
allQslTypes.forEach(type => {
|
|
|
|
|
const el = document.getElementById(type);
|
|
|
|
|
if (el) el.checked = true;
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
form.submit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function resetFilters() {
|
|
|
|
|
const form = document.getElementById('wasForm');
|
|
|
|
|
form.reset();
|
|
|
|
|
// Make sure worked, confirmed, and QSL types are checked by default
|
|
|
|
|
document.getElementById('worked').checked = true;
|
|
|
|
|
document.getElementById('confirmed').checked = true;
|
|
|
|
|
document.getElementById('qsl').checked = true;
|
|
|
|
|
document.getElementById('lotw').checked = true;
|
|
|
|
|
form.submit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CSV Export functionality
|
|
|
|
|
function exportTableToCSV(filename) {
|
|
|
|
|
const table = document.getElementById('was_table');
|
|
|
|
|
let csv = [];
|
|
|
|
|
|
|
|
|
|
// Add header row
|
|
|
|
|
const headers = Array.from(table.querySelectorAll('thead tr td')).map(h => h.textContent.trim());
|
|
|
|
|
csv.push(headers.join(','));
|
|
|
|
|
|
|
|
|
|
// Add data rows
|
|
|
|
|
const rows = table.querySelectorAll('tbody tr');
|
|
|
|
|
rows.forEach(row => {
|
|
|
|
|
if (row.style.display !== 'none') { // Only export visible rows
|
|
|
|
|
const cells = Array.from(row.querySelectorAll('td')).map(cell => {
|
|
|
|
|
let text = cell.textContent.trim();
|
|
|
|
|
// Escape quotes and wrap in quotes if contains comma
|
|
|
|
|
text = text.replace(/"/g, '""');
|
|
|
|
|
return '"' + text + '"';
|
|
|
|
|
});
|
|
|
|
|
csv.push(cells.join(','));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Create blob and download
|
|
|
|
|
const blob = new Blob([csv.join('\n')], { type: 'text/csv' });
|
|
|
|
|
const url = window.URL.createObjectURL(blob);
|
|
|
|
|
const a = document.createElement('a');
|
|
|
|
|
a.href = url;
|
|
|
|
|
a.download = filename;
|
|
|
|
|
document.body.appendChild(a);
|
|
|
|
|
a.click();
|
|
|
|
|
window.URL.revokeObjectURL(url);
|
|
|
|
|
document.body.removeChild(a);
|
|
|
|
|
}
|
|
|
|
|
</script>
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php } ?>
|
2020-10-19 19:16:42 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<script>
|
|
|
|
|
function selectize_usa_county() {
|
|
|
|
|
var baseURL = "<?php echo base_url(); ?>";
|
|
|
|
|
$('#stationCntyInputEdit').selectize({
|
|
|
|
|
delimiter: ';',
|
|
|
|
|
maxItems: 1,
|
|
|
|
|
closeAfterSelect: true,
|
|
|
|
|
loadThrottle: 250,
|
|
|
|
|
valueField: 'name',
|
|
|
|
|
labelField: 'name',
|
|
|
|
|
searchField: 'name',
|
|
|
|
|
options: [],
|
|
|
|
|
create: false,
|
|
|
|
|
load: function(query, callback) {
|
|
|
|
|
var state = $("#input_usa_state_edit option:selected").text();
|
|
|
|
|
|
|
|
|
|
if (!query || state == "") return callback();
|
2020-09-25 18:22:13 +02:00
|
|
|
$.ajax({
|
2024-02-22 16:56:21 +00:00
|
|
|
url: baseURL + 'index.php/qso/get_county',
|
|
|
|
|
type: 'GET',
|
|
|
|
|
dataType: 'json',
|
|
|
|
|
data: {
|
|
|
|
|
query: query,
|
|
|
|
|
state: state,
|
2020-09-25 18:22:13 +02:00
|
|
|
},
|
2024-02-22 16:56:21 +00:00
|
|
|
error: function() {
|
|
|
|
|
callback();
|
|
|
|
|
},
|
|
|
|
|
success: function(res) {
|
|
|
|
|
callback(res);
|
2022-03-22 17:41:03 +01:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function qso_save() {
|
|
|
|
|
var baseURL = "<?php echo base_url(); ?>";
|
|
|
|
|
var myform = document.getElementById("qsoform");
|
|
|
|
|
var fd = new FormData(myform);
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: baseURL + 'index.php/qso/qso_save_ajax',
|
|
|
|
|
data: fd,
|
|
|
|
|
cache: false,
|
|
|
|
|
processData: false,
|
|
|
|
|
contentType: false,
|
|
|
|
|
type: 'POST',
|
|
|
|
|
success: function(dataofconfirm) {
|
|
|
|
|
$(".edit-dialog").modal('hide');
|
|
|
|
|
$(".qso-dialog").modal('hide');
|
|
|
|
|
<?php if ($this->uri->segment(1) != "search" && $this->uri->segment(2) != "filter" && $this->uri->segment(1) != "qso" && $this->uri->segment(1) != "logbookadvanced") { ?>location.reload();
|
|
|
|
|
<?php } ?>
|
|
|
|
|
},
|
|
|
|
|
error: function(xhr, status, error) {
|
|
|
|
|
console.log(xhr.responseText);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
<?php if ($this->uri->segment(1) == "timeline") { ?>
|
|
|
|
|
<script>
|
2026-04-26 18:12:58 +01:00
|
|
|
if ($('.timelinetable').length) {
|
|
|
|
|
$('.timelinetable').DataTable({
|
|
|
|
|
"pageLength": 25,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: false,
|
|
|
|
|
"scrollY": "500px",
|
|
|
|
|
"scrollCollapse": true,
|
|
|
|
|
"paging": false,
|
|
|
|
|
"scrollX": true,
|
|
|
|
|
"language": {
|
|
|
|
|
url: getDataTablesLanguageUrl(),
|
|
|
|
|
},
|
|
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
|
|
|
|
|
// change color of csv-button if dark mode is chosen
|
|
|
|
|
if (isDarkModeTheme()) {
|
|
|
|
|
$(".buttons-csv").css("color", "white");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function displayTimelineContacts(querystring, band, mode, type) {
|
|
|
|
|
var baseURL = "<?php echo base_url(); ?>";
|
2026-04-26 18:12:58 +01:00
|
|
|
var modalElement = document.getElementById('timelineDetailsModal');
|
|
|
|
|
var modalBody = document.getElementById('timelineDetailsBody');
|
|
|
|
|
if (!modalElement || !modalBody) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
modalBody.innerHTML = '<div class="d-flex justify-content-center py-4"><div class="spinner-border" role="status" aria-hidden="true"></div></div>';
|
|
|
|
|
|
|
|
|
|
var modal = bootstrap.Modal.getOrCreateInstance(modalElement);
|
|
|
|
|
modal.show();
|
|
|
|
|
|
|
|
|
|
if (window.htmx) {
|
|
|
|
|
htmx.ajax('POST', baseURL + 'index.php/timeline/details', {
|
|
|
|
|
target: '#timelineDetailsBody',
|
|
|
|
|
swap: 'innerHTML',
|
|
|
|
|
values: {
|
|
|
|
|
Querystring: querystring,
|
|
|
|
|
Band: band,
|
|
|
|
|
Mode: mode,
|
|
|
|
|
Type: type
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
$.post(baseURL + 'index.php/timeline/details', {
|
|
|
|
|
Querystring: querystring,
|
|
|
|
|
Band: band,
|
|
|
|
|
Mode: mode,
|
|
|
|
|
Type: type
|
|
|
|
|
}).done(function(html) {
|
|
|
|
|
modalBody.innerHTML = html;
|
|
|
|
|
initTimelineDetailsTable();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function initTimelineDetailsTable() {
|
|
|
|
|
$('[data-bs-toggle="tooltip"]').tooltip();
|
|
|
|
|
|
|
|
|
|
var detailsTable = $('.contacttable');
|
|
|
|
|
if (detailsTable.length) {
|
|
|
|
|
if ($.fn.dataTable.isDataTable(detailsTable)) {
|
|
|
|
|
detailsTable.DataTable().destroy();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
detailsTable.DataTable({
|
|
|
|
|
"pageLength": 10,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: false,
|
|
|
|
|
"scrollY": "55vh",
|
|
|
|
|
"scrollCollapse": true,
|
|
|
|
|
"paging": true,
|
|
|
|
|
"scrollX": true,
|
|
|
|
|
"language": {
|
|
|
|
|
url: getDataTablesLanguageUrl(),
|
|
|
|
|
},
|
|
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$('.table-responsive .dropdown-toggle').off('mouseenter').on('mouseenter', function() {
|
|
|
|
|
showQsoActionsMenu($(this).closest('.dropdown'));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (window.htmx) {
|
|
|
|
|
document.body.addEventListener('htmx:afterSwap', function(evt) {
|
|
|
|
|
if (evt.target && evt.target.id === 'timelineDetailsBody') {
|
|
|
|
|
initTimelineDetailsTable();
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
2022-03-22 17:41:03 +01:00
|
|
|
});
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
<?php } ?>
|
|
|
|
|
<?php if ($this->uri->segment(1) == "activators") { ?>
|
|
|
|
|
<script>
|
|
|
|
|
$('.activatorstable').DataTable({
|
|
|
|
|
"pageLength": 25,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: false,
|
|
|
|
|
"scrollY": "500px",
|
|
|
|
|
"scrollCollapse": true,
|
|
|
|
|
"paging": false,
|
|
|
|
|
"scrollX": true,
|
|
|
|
|
"language": {
|
|
|
|
|
url: getDataTablesLanguageUrl(),
|
|
|
|
|
},
|
|
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
|
|
|
|
});
|
2022-03-22 17:41:03 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
// change color of csv-button if dark mode is chosen
|
|
|
|
|
if (isDarkModeTheme()) {
|
|
|
|
|
$(".buttons-csv").css("color", "white");
|
|
|
|
|
}
|
2022-03-22 17:41:03 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
$(document).ready(function() {
|
|
|
|
|
$('#band').change(function() {
|
|
|
|
|
if ($(this).val() == "SAT") {
|
|
|
|
|
$('#leogeo').show();
|
|
|
|
|
} else {
|
|
|
|
|
$('#leogeo').hide();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
<?php if ($this->input->post('band') != "SAT") { ?>
|
|
|
|
|
$('#leogeo').hide();
|
|
|
|
|
<?php } ?>
|
|
|
|
|
});
|
2020-09-25 18:22:13 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
function displayActivatorsContacts(call, band, leogeo) {
|
|
|
|
|
var baseURL = "<?php echo base_url(); ?>";
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: baseURL + 'index.php/activators/details',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
'Callsign': call,
|
|
|
|
|
'Band': band,
|
|
|
|
|
'LeoGeo': leogeo
|
|
|
|
|
},
|
|
|
|
|
success: function(html) {
|
|
|
|
|
BootstrapDialog.show({
|
|
|
|
|
title: lang_general_word_qso_data,
|
|
|
|
|
size: BootstrapDialog.SIZE_WIDE,
|
|
|
|
|
cssClass: 'qso-was-dialog',
|
|
|
|
|
nl2br: false,
|
|
|
|
|
message: html,
|
|
|
|
|
onshown: function(dialog) {
|
|
|
|
|
$('[data-bs-toggle="tooltip"]').tooltip();
|
|
|
|
|
},
|
|
|
|
|
buttons: [{
|
|
|
|
|
label: lang_admin_close,
|
|
|
|
|
action: function(dialogItself) {
|
|
|
|
|
dialogItself.close();
|
|
|
|
|
}
|
|
|
|
|
}]
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
<?php } ?>
|
2020-10-09 18:29:41 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->uri->segment(1) == "mode") { ?>
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/mode.js"></script>
|
|
|
|
|
<?php } ?>
|
|
|
|
|
|
|
|
|
|
<?php if ($this->uri->segment(1) == "band") { ?>
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/bands.js"></script>
|
|
|
|
|
<?php } ?>
|
2022-09-05 20:26:08 +02:00
|
|
|
|
2025-12-02 13:32:11 +00:00
|
|
|
<?php if ($this->uri->segment(1) == "award") { ?>
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/awards.js"></script>
|
|
|
|
|
<?php } ?>
|
|
|
|
|
|
2020-10-06 21:12:14 +02:00
|
|
|
<?php if ($this->uri->segment(1) == "accumulated") { ?>
|
2020-11-08 11:26:38 +01:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/chart.js"></script>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/accumulatedstatistics.js"></script>
|
2020-10-06 21:12:14 +02:00
|
|
|
<?php } ?>
|
2020-09-18 12:17:41 +02:00
|
|
|
|
2020-10-18 00:00:01 +02:00
|
|
|
<?php if ($this->uri->segment(1) == "timeplotter") { ?>
|
2020-11-08 11:26:38 +01:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/highstock.js"></script>
|
2020-12-02 14:49:14 +01:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/highstock/exporting.js"></script>
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/highstock/offline-exporting.js"></script>
|
|
|
|
|
<script src="<?php echo base_url(); ?>assets/js/highstock/export-data.js"></script>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/timeplot.js"></script>
|
2020-10-18 00:00:01 +02:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2023-05-10 16:03:30 +02:00
|
|
|
<?php if ($this->uri->segment(1) == "qsl" || $this->uri->segment(1) == "eqsl") {
|
2024-02-22 16:56:21 +00:00
|
|
|
// Get Date format
|
|
|
|
|
if ($this->session->userdata('user_date_format')) {
|
|
|
|
|
// If Logged in and session exists
|
|
|
|
|
$custom_date_format = $this->session->userdata('user_date_format');
|
|
|
|
|
} else {
|
|
|
|
|
// Get Default date format from /config/cloudlog.php
|
|
|
|
|
$custom_date_format = $this->config->item('qso_date_format');
|
|
|
|
|
}
|
2022-05-27 20:20:25 +02:00
|
|
|
|
|
|
|
|
switch ($custom_date_format) {
|
2024-02-22 16:56:21 +00:00
|
|
|
case 'd/m/y':
|
|
|
|
|
$usethisformat = 'D/MM/YY';
|
|
|
|
|
break;
|
|
|
|
|
case 'd/m/Y':
|
|
|
|
|
$usethisformat = 'D/MM/YYYY';
|
|
|
|
|
break;
|
|
|
|
|
case 'm/d/y':
|
|
|
|
|
$usethisformat = 'MM/D/YY';
|
|
|
|
|
break;
|
|
|
|
|
case 'm/d/Y':
|
|
|
|
|
$usethisformat = 'MM/D/YYYY';
|
|
|
|
|
break;
|
|
|
|
|
case 'd.m.Y':
|
|
|
|
|
$usethisformat = 'D.MM.YYYY';
|
|
|
|
|
break;
|
|
|
|
|
case 'y/m/d':
|
|
|
|
|
$usethisformat = 'YY/MM/D';
|
|
|
|
|
break;
|
|
|
|
|
case 'Y-m-d':
|
|
|
|
|
$usethisformat = 'YYYY-MM-D';
|
|
|
|
|
break;
|
|
|
|
|
case 'M d, Y':
|
|
|
|
|
$usethisformat = 'MMM D, YYYY';
|
|
|
|
|
break;
|
|
|
|
|
case 'M d, y':
|
|
|
|
|
$usethisformat = 'MMM D, YY';
|
|
|
|
|
break;
|
2022-05-27 20:20:25 +02:00
|
|
|
}
|
2023-03-17 17:54:35 +00:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
?>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/moment.min.js"></script>
|
2022-05-27 20:20:25 +02:00
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/datetime-moment.js"></script>
|
2020-10-28 23:20:03 +01:00
|
|
|
<script>
|
2022-05-27 23:10:19 +02:00
|
|
|
$.fn.dataTable.moment('<?php echo $usethisformat ?>');
|
2023-08-15 22:23:11 +02:00
|
|
|
$.fn.dataTable.ext.buttons.clear = {
|
|
|
|
|
className: 'buttons-clear',
|
2024-02-22 16:56:21 +00:00
|
|
|
action: function(e, dt, node, config) {
|
|
|
|
|
dt.search('').draw();
|
2023-08-15 22:23:11 +02:00
|
|
|
}
|
|
|
|
|
};
|
2024-02-22 16:56:21 +00:00
|
|
|
<?php if ($this->uri->segment(1) == "qsl") { ?>
|
|
|
|
|
$('.qsltable').DataTable({
|
|
|
|
|
<?php } else if ($this->uri->segment(1) == "eqsl") { ?>
|
|
|
|
|
$('.eqsltable').DataTable({
|
|
|
|
|
<?php } ?> "pageLength": 25,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: true,
|
|
|
|
|
"scrollY": "500px",
|
|
|
|
|
"scrollCollapse": true,
|
|
|
|
|
"paging": false,
|
|
|
|
|
"scrollX": true,
|
|
|
|
|
"language": {
|
|
|
|
|
url: getDataTablesLanguageUrl(),
|
|
|
|
|
},
|
|
|
|
|
"order": [2, 'desc'],
|
|
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [{
|
|
|
|
|
extend: 'clear',
|
|
|
|
|
text: 'Clear'
|
|
|
|
|
}]
|
|
|
|
|
});
|
|
|
|
|
// change color of csv-button if dark mode is chosen
|
|
|
|
|
if (isDarkModeTheme()) {
|
|
|
|
|
$('[class*="buttons"]').css("color", "white");
|
|
|
|
|
}
|
2020-11-01 21:45:47 +01:00
|
|
|
</script>
|
|
|
|
|
<?php } ?>
|
2020-10-28 23:20:03 +01:00
|
|
|
|
2020-12-06 13:21:35 +01:00
|
|
|
|
2020-11-01 21:45:47 +01:00
|
|
|
<script>
|
2024-02-22 16:56:21 +00:00
|
|
|
function viewQsl(picture, callsign) {
|
2024-04-03 21:50:47 -05:00
|
|
|
let title = '';
|
|
|
|
|
const baseURL = "<?php echo base_url(); ?>";
|
|
|
|
|
const textAndPic = $('<div></div>').append('<center><img class="img-fluid w-qsl" style="height:auto;width:auto;"src="' + baseURL + '/assets/qslcard/' + picture + '" /><center>');
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
if (callsign == null) {
|
|
|
|
|
title = 'QSL Card';
|
|
|
|
|
} else {
|
|
|
|
|
title = 'QSL Card for ' + callsign.replace('0', 'Ø');
|
2020-10-28 23:20:03 +01:00
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
|
|
|
|
|
BootstrapDialog.show({
|
|
|
|
|
title: title,
|
|
|
|
|
size: BootstrapDialog.SIZE_WIDE,
|
2024-04-03 21:50:47 -05:00
|
|
|
message: textAndPic,
|
2024-02-22 16:56:21 +00:00
|
|
|
buttons: [{
|
|
|
|
|
label: lang_admin_close,
|
|
|
|
|
action: function(dialogRef) {
|
|
|
|
|
dialogRef.close();
|
|
|
|
|
}
|
|
|
|
|
}]
|
|
|
|
|
});
|
|
|
|
|
}
|
2020-11-01 21:45:47 +01:00
|
|
|
</script>
|
2020-10-28 23:20:03 +01:00
|
|
|
<script>
|
2024-02-22 16:56:21 +00:00
|
|
|
function deleteQsl(id) {
|
2024-04-03 21:50:47 -05:00
|
|
|
const baseURL = "<?php echo base_url(); ?>";
|
2024-02-22 16:56:21 +00:00
|
|
|
BootstrapDialog.confirm({
|
|
|
|
|
title: 'DANGER',
|
|
|
|
|
message: 'Warning! Are you sure you want to delete this QSL card?',
|
|
|
|
|
type: BootstrapDialog.TYPE_DANGER,
|
|
|
|
|
closable: true,
|
|
|
|
|
draggable: true,
|
|
|
|
|
btnOKClass: 'btn-danger',
|
|
|
|
|
callback: function(result) {
|
|
|
|
|
if (result) {
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: baseURL + 'index.php/qsl/delete',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
'id': id
|
|
|
|
|
},
|
|
|
|
|
success: function(data) {
|
|
|
|
|
$("#" + id).parent("tr:first").remove(); // removes qsl from table
|
2020-11-01 12:36:02 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
// remove qsl from carousel
|
|
|
|
|
$(".carousel-indicators li:last-child").remove();
|
|
|
|
|
$(".carouselimageid_" + id).remove();
|
|
|
|
|
$('#carouselExampleIndicators').find('.carousel-item').first().addClass('active');
|
2020-11-01 12:36:02 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
// remove table and hide tab if all qsls are deleted
|
|
|
|
|
if ($('.qsltable tr').length == 1) {
|
|
|
|
|
$('.qsltable').remove();
|
|
|
|
|
$('.qslcardtab').attr('hidden', '');
|
2020-10-28 23:20:03 +01:00
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
|
|
|
|
});
|
2020-10-28 23:20:03 +01:00
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2020-10-28 23:20:03 +01:00
|
|
|
</script>
|
2024-03-26 22:08:16 -05:00
|
|
|
<script>
|
|
|
|
|
function viewSstv(picture) {
|
2024-04-03 21:50:47 -05:00
|
|
|
const title = 'SSTV Image';
|
|
|
|
|
const baseURL = "<?php echo base_url(); ?>";
|
|
|
|
|
const textAndPic = $('<div></div>').append(`<center><img class="img-fluid w-qsl" style="height:auto;width:auto;"src="${baseURL}/assets/sstvimages/${picture}" /><center>`);
|
2024-03-26 22:08:16 -05:00
|
|
|
|
|
|
|
|
BootstrapDialog.show({
|
|
|
|
|
title: title,
|
|
|
|
|
size: BootstrapDialog.SIZE_WIDE,
|
2024-04-03 21:50:47 -05:00
|
|
|
message: textAndPic,
|
2024-03-26 22:08:16 -05:00
|
|
|
buttons: [{
|
|
|
|
|
label: lang_admin_close,
|
|
|
|
|
action: function(dialogRef) {
|
|
|
|
|
dialogRef.close();
|
|
|
|
|
}
|
|
|
|
|
}]
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-04-03 21:50:47 -05:00
|
|
|
|
2024-03-26 22:08:16 -05:00
|
|
|
function deleteSstv(id) {
|
2024-04-03 21:50:47 -05:00
|
|
|
const baseURL = "<?php echo base_url(); ?>";
|
2024-03-26 22:08:16 -05:00
|
|
|
BootstrapDialog.confirm({
|
|
|
|
|
title: 'DANGER',
|
|
|
|
|
message: 'Warning! Are you sure you want to delete this SSTV Image?',
|
|
|
|
|
type: BootstrapDialog.TYPE_DANGER,
|
|
|
|
|
closable: true,
|
|
|
|
|
draggable: true,
|
|
|
|
|
btnOKClass: 'btn-danger',
|
|
|
|
|
callback: function(result) {
|
|
|
|
|
if (result) {
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: baseURL + 'index.php/sstv/delete',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
'id': id
|
|
|
|
|
},
|
|
|
|
|
success: function(data) {
|
|
|
|
|
// remove selected sstv image from table
|
2024-04-20 16:01:50 +01:00
|
|
|
$("#" + id).parent("tr:first").remove();
|
2024-03-26 22:08:16 -05:00
|
|
|
|
|
|
|
|
// remove sstv image from carousel
|
|
|
|
|
$("#sstvCarouselIndicators .carousel-indicators li:last-child").remove();
|
|
|
|
|
$("#sstvCarouselIndicators .carouselimageid_" + id).remove();
|
|
|
|
|
$('#sstvCarouselIndicators').find('.carousel-item').first().addClass('active');
|
|
|
|
|
|
|
|
|
|
// remove table and hide tab if all sstv images are deleted
|
|
|
|
|
if ($('.sstvtable tr').length == 1) {
|
|
|
|
|
$('.sstvtable').remove();
|
|
|
|
|
$('.sstvimagetab').attr('hidden', '');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
2023-05-10 16:03:30 +02:00
|
|
|
<script>
|
2024-02-22 16:56:21 +00:00
|
|
|
function viewEqsl(picture, callsign) {
|
|
|
|
|
var baseURL = "<?php echo base_url(); ?>";
|
|
|
|
|
var $textAndPic = $('<div></div>');
|
|
|
|
|
$textAndPic.append('<img class="img-fluid" style="height:auto;width:auto;"src="' + baseURL + 'images/eqsl_card_images/' + picture + '" />');
|
|
|
|
|
var title = '';
|
|
|
|
|
if (callsign == null) {
|
|
|
|
|
title = 'eQSL Card';
|
|
|
|
|
} else {
|
|
|
|
|
title = 'eQSL Card for ' + callsign.replace('0', 'Ø');
|
2023-05-10 16:03:30 +02:00
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
|
|
|
|
|
BootstrapDialog.show({
|
|
|
|
|
title: title,
|
|
|
|
|
size: BootstrapDialog.SIZE_WIDE,
|
|
|
|
|
message: $textAndPic,
|
|
|
|
|
buttons: [{
|
|
|
|
|
label: lang_admin_close,
|
|
|
|
|
action: function(dialogRef) {
|
|
|
|
|
dialogRef.close();
|
|
|
|
|
}
|
|
|
|
|
}]
|
|
|
|
|
});
|
|
|
|
|
}
|
2023-05-10 16:03:30 +02:00
|
|
|
</script>
|
2023-10-20 15:44:34 +02:00
|
|
|
<script>
|
2024-02-22 16:56:21 +00:00
|
|
|
$('#displayAwardInfo').click(function(event) {
|
2023-10-20 15:44:34 +02:00
|
|
|
var awardInfoLines = [
|
|
|
|
|
lang_award_info_ln2,
|
|
|
|
|
lang_award_info_ln3,
|
|
|
|
|
lang_award_info_ln4
|
|
|
|
|
];
|
|
|
|
|
var awardInfoContent = "";
|
2024-02-22 16:56:21 +00:00
|
|
|
awardInfoLines.forEach(function(line) {
|
2023-10-20 15:44:34 +02:00
|
|
|
awardInfoContent += line + "<br><br>";
|
|
|
|
|
});
|
|
|
|
|
BootstrapDialog.alert({
|
2024-02-22 16:56:21 +00:00
|
|
|
title: "<h4>" + lang_award_info_ln1 + "</h4>",
|
2023-10-20 15:44:34 +02:00
|
|
|
message: awardInfoContent,
|
|
|
|
|
});
|
2024-02-22 16:56:21 +00:00
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
<script>
|
|
|
|
|
/*
|
|
|
|
|
* Used to fetch QSOs from the logbook in the awards
|
|
|
|
|
*/
|
|
|
|
|
function displayContacts(searchphrase, band, mode, type, qsl) {
|
|
|
|
|
var baseURL = "<?php echo base_url(); ?>";
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: baseURL + 'index.php/awards/qso_details_ajax',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
'Searchphrase': searchphrase,
|
|
|
|
|
'Band': band,
|
|
|
|
|
'Mode': mode,
|
|
|
|
|
'Type': type,
|
|
|
|
|
'QSL': qsl
|
|
|
|
|
},
|
|
|
|
|
success: function(html) {
|
|
|
|
|
BootstrapDialog.show({
|
|
|
|
|
title: lang_general_word_qso_data,
|
|
|
|
|
size: BootstrapDialog.SIZE_WIDE,
|
|
|
|
|
cssClass: 'qso-dialog',
|
|
|
|
|
nl2br: false,
|
|
|
|
|
message: html,
|
|
|
|
|
onshown: function(dialog) {
|
|
|
|
|
$('[data-bs-toggle="tooltip"]').tooltip();
|
|
|
|
|
$('.contacttable').DataTable({
|
|
|
|
|
"pageLength": 7,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: false,
|
|
|
|
|
"scrollY": "550px",
|
|
|
|
|
"scrollCollapse": true,
|
|
|
|
|
"paging": true,
|
|
|
|
|
"scrollX": true,
|
|
|
|
|
"language": {
|
|
|
|
|
url: getDataTablesLanguageUrl(),
|
|
|
|
|
},
|
|
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
$('.table-responsive .dropdown-toggle').off('mouseenter').on('mouseenter', function() {
|
|
|
|
|
showQsoActionsMenu($(this).closest('.dropdown'));
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
buttons: [{
|
|
|
|
|
label: lang_admin_close,
|
|
|
|
|
action: function(dialogItself) {
|
|
|
|
|
dialogItself.close();
|
|
|
|
|
}
|
|
|
|
|
}]
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function displayContactsOnMap(target, searchphrase, band, mode, type, qsl) {
|
2021-07-24 14:31:16 +02:00
|
|
|
$.ajax({
|
2024-02-22 16:56:21 +00:00
|
|
|
url: base_url + 'index.php/awards/qso_details_ajax',
|
2021-07-24 14:31:16 +02:00
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
'Searchphrase': searchphrase,
|
|
|
|
|
'Band': band,
|
|
|
|
|
'Mode': mode,
|
2023-01-16 17:08:27 +01:00
|
|
|
'Type': type,
|
2024-02-22 16:56:21 +00:00
|
|
|
'QSL': qsl
|
2021-07-24 14:31:16 +02:00
|
|
|
},
|
2024-02-22 16:56:21 +00:00
|
|
|
success: function(html) {
|
|
|
|
|
var dialog = new BootstrapDialog({
|
2023-10-08 10:41:24 +02:00
|
|
|
title: lang_general_word_qso_data,
|
2021-07-24 14:31:16 +02:00
|
|
|
size: BootstrapDialog.SIZE_WIDE,
|
|
|
|
|
cssClass: 'qso-dialog',
|
|
|
|
|
nl2br: false,
|
|
|
|
|
message: html,
|
2022-01-12 20:51:24 +01:00
|
|
|
onshown: function(dialog) {
|
2024-02-22 16:56:21 +00:00
|
|
|
$('[data-bs-toggle="tooltip"]').tooltip();
|
|
|
|
|
$('.contacttable').DataTable({
|
|
|
|
|
"pageLength": 25,
|
2022-11-12 10:50:46 +01:00
|
|
|
responsive: false,
|
|
|
|
|
ordering: false,
|
2024-02-22 16:56:21 +00:00
|
|
|
"scrollY": "550px",
|
2022-11-12 10:50:46 +01:00
|
|
|
"scrollCollapse": true,
|
2024-02-22 16:56:21 +00:00
|
|
|
"paging": false,
|
2022-11-12 10:50:46 +01:00
|
|
|
"scrollX": true,
|
2024-01-03 22:03:11 -05:00
|
|
|
"language": {
|
2024-01-03 22:18:20 -05:00
|
|
|
url: getDataTablesLanguageUrl(),
|
2024-01-03 22:03:11 -05:00
|
|
|
},
|
2022-11-12 10:50:46 +01:00
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
|
|
|
|
});
|
2024-02-22 16:56:21 +00:00
|
|
|
$('.table-responsive .dropdown-toggle').off('mouseenter').on('mouseenter', function() {
|
2023-12-22 11:15:00 +01:00
|
|
|
showQsoActionsMenu($(this).closest('.dropdown'));
|
|
|
|
|
});
|
2022-01-12 20:51:24 +01:00
|
|
|
},
|
2021-07-24 14:31:16 +02:00
|
|
|
buttons: [{
|
2023-11-05 19:38:34 +01:00
|
|
|
label: lang_admin_close,
|
2024-02-22 16:56:21 +00:00
|
|
|
action: function(dialogItself) {
|
2021-07-24 14:31:16 +02:00
|
|
|
dialogItself.close();
|
|
|
|
|
}
|
|
|
|
|
}]
|
|
|
|
|
});
|
2024-02-22 16:56:21 +00:00
|
|
|
dialog.realize();
|
|
|
|
|
target.append(dialog.getModal());
|
|
|
|
|
dialog.open();
|
2021-07-24 14:31:16 +02:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-04-03 21:50:47 -05:00
|
|
|
|
|
|
|
|
function createTable(title, type) {
|
|
|
|
|
const tableClass = type === 'sstv' ? 'sstvtable' : 'qsltable';
|
|
|
|
|
return `<table style="width:100%" class="${tableClass} table table-sm table-bordered table-hover table-striped table-condensed">` +
|
2024-04-20 16:01:50 +01:00
|
|
|
'<thead>' +
|
|
|
|
|
'<tr>' +
|
|
|
|
|
'<th style="text-align: center">' + title + '</th>' +
|
|
|
|
|
'<th style="text-align: center"></th>' +
|
|
|
|
|
'<th style="text-align: center"></th>' +
|
|
|
|
|
'</tr>' +
|
|
|
|
|
'</thead>' +
|
|
|
|
|
'<tbody></tbody>' +
|
|
|
|
|
'</table>'
|
2024-04-03 21:50:47 -05:00
|
|
|
}
|
|
|
|
|
|
2024-04-20 16:01:50 +01:00
|
|
|
function createTableRow(image, type) {
|
2024-04-03 21:50:47 -05:00
|
|
|
const viewFunction = type === 'sstv' ? 'viewSstv' : 'viewQsl';
|
|
|
|
|
const deleteFunction = type === 'sstv' ? 'deleteSstv' : 'deleteQsl';
|
|
|
|
|
return '<tr><td style="text-align: center">' + image.filename + '</td>' +
|
2024-04-20 16:01:50 +01:00
|
|
|
`<td id="${image.insertid}" style="text-align: center"><button onclick="${deleteFunction}(${image.insertid});" class="btn btn-sm btn-danger">Delete</button></td>` +
|
|
|
|
|
`<td style="text-align: center"><button onclick="${viewFunction}('${image.filename}')" class="btn btn-sm btn-success">View</button></td>` +
|
|
|
|
|
'</tr>'
|
2024-04-03 21:50:47 -05:00
|
|
|
}
|
|
|
|
|
|
2024-04-20 16:01:50 +01:00
|
|
|
function handleSSTVImageUpload(sstvImage) {
|
2024-04-03 21:50:47 -05:00
|
|
|
const baseURL = "<?php echo base_url(); ?>";
|
|
|
|
|
const numCarouselItems = $('#sstv-carousel-indicators li').length;
|
|
|
|
|
|
|
|
|
|
// Next, append card to the table
|
|
|
|
|
$('.sstvtable').length === 0 ? $("#sstvupload").prepend(createTable("SSTV image file", "sstv")) : null;
|
|
|
|
|
$('.sstvtable tbody:last').append(createTableRow(sstvImage, "sstv"));
|
|
|
|
|
|
|
|
|
|
// Append card to the carousel
|
2024-04-20 16:01:50 +01:00
|
|
|
const newCarouselItem = '<div class="' + (numCarouselItems === 0 ? 'active ' : '') + 'carousel-item carouselimageid_' + sstvImage.insertid + '"><img class="img-fluid w-qsl" src="' + baseURL + '/assets/sstvimages/' + sstvImage.filename + '" alt="QSL picture"></div>';
|
2024-04-03 21:50:47 -05:00
|
|
|
$("#sstv-carousel-inner").append(newCarouselItem);
|
|
|
|
|
|
|
|
|
|
// Append new carousel indicator
|
|
|
|
|
const newCarouselIndicator = '<li class="' + (numCarouselItems === 0 ? 'active ' : '') + '" data-bs-target="#sstvCarouselIndicators" data-bs-slide-to="' + numCarouselItems + '"></li>';
|
|
|
|
|
$("#sstv-carousel-indicators").append(newCarouselIndicator);
|
|
|
|
|
|
|
|
|
|
// Initialize the bootstrap carousel
|
|
|
|
|
$("#sstvCarouselIndicators").carousel();
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-26 22:08:16 -05:00
|
|
|
function uploadSSTV() {
|
2024-04-03 21:50:47 -05:00
|
|
|
const baseURL = "<?php echo base_url(); ?>";
|
|
|
|
|
const formdata = new FormData(document.getElementById("sstvinfo"));
|
2024-04-20 16:01:50 +01:00
|
|
|
|
2024-03-26 22:08:16 -05:00
|
|
|
$.ajax({
|
|
|
|
|
url: baseURL + 'index.php/sstv/uploadsstv',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: formdata,
|
|
|
|
|
enctype: 'multipart/form-data',
|
|
|
|
|
processData: false,
|
|
|
|
|
contentType: false,
|
|
|
|
|
success: function(data) {
|
2024-04-03 21:50:47 -05:00
|
|
|
// Iterate over each SSTV image and handle it
|
2024-03-26 22:08:16 -05:00
|
|
|
data.forEach((sstvImage) => {
|
2024-04-03 21:50:47 -05:00
|
|
|
if (sstvImage.status == 'Success') {
|
|
|
|
|
// Show the SSTV image tab
|
|
|
|
|
$('.sstvimagetab').removeAttr('hidden');
|
2024-04-20 16:01:50 +01:00
|
|
|
|
2024-04-03 21:50:47 -05:00
|
|
|
// Handle the SSTV image upload
|
|
|
|
|
handleSSTVImageUpload(sstvImage);
|
|
|
|
|
} else if (sstvImage.status != '') {
|
|
|
|
|
$("#sstvupload").append('<div class="alert alert-danger">SSTV image:' +
|
|
|
|
|
sstvImage.error +
|
|
|
|
|
'</div>');
|
|
|
|
|
}
|
|
|
|
|
// Reset the image inputs
|
|
|
|
|
$("#sstvimages").val(null);
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-03-26 22:08:16 -05:00
|
|
|
|
2024-04-03 21:50:47 -05:00
|
|
|
function handleQslCardUpload(qslCard) {
|
|
|
|
|
const baseURL = "<?php echo base_url(); ?>";
|
|
|
|
|
const numCarouselItems = $('#qsl-carousel-indicators li').length;
|
2024-03-26 22:08:16 -05:00
|
|
|
|
2024-04-03 21:50:47 -05:00
|
|
|
// append card to the qsl management table
|
|
|
|
|
$('.qsltable').length === 0 ? $("#qslupload").prepend(createTable("QSL image file", "qsl")) : null;
|
|
|
|
|
$('.qsltable tbody:last').append(createTableRow(qslCard, "qsl"));
|
2024-03-26 22:08:16 -05:00
|
|
|
|
2024-04-03 21:50:47 -05:00
|
|
|
// Append card image to the carousel
|
2024-04-20 16:01:50 +01:00
|
|
|
const newCarouselItem = '<div class="' + (numCarouselItems === 0 ? 'active ' : '') + 'carousel-item carouselimageid_' + qslCard.insertid + '"><img class="img-fluid w-qsl" src="' + baseURL + '/assets/qslcard/' + qslCard.filename + '" alt="QSL picture"></div>';
|
2024-04-03 21:50:47 -05:00
|
|
|
$("#qsl-carousel-inner").append(newCarouselItem);
|
2024-03-26 22:08:16 -05:00
|
|
|
|
2024-04-03 21:50:47 -05:00
|
|
|
// Append carousel indicator for the new card
|
|
|
|
|
const newCarouselIndicator = '<li class="' + (numCarouselItems === 0 ? 'active ' : '') + '" data-bs-target="#qslCarouselIndicators" data-bs-slide-to="' + numCarouselItems + '"></li>';
|
|
|
|
|
$("#qsl-carousel-indicators").append(newCarouselIndicator);
|
2024-03-26 22:08:16 -05:00
|
|
|
|
2024-04-03 21:50:47 -05:00
|
|
|
// Initialize the bootstrap carousel
|
|
|
|
|
$("#qslCarouselIndicators").carousel();
|
2024-03-26 22:08:16 -05:00
|
|
|
}
|
2021-07-24 14:31:16 +02:00
|
|
|
|
2020-11-01 12:36:02 +01:00
|
|
|
function uploadQsl() {
|
2024-04-03 21:50:47 -05:00
|
|
|
const baseURL = "<?php echo base_url(); ?>";
|
|
|
|
|
const formdata = new FormData(document.getElementById("fileinfo"));
|
2020-11-01 12:36:02 +01:00
|
|
|
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: baseURL + 'index.php/qsl/uploadqsl',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: formdata,
|
|
|
|
|
enctype: 'multipart/form-data',
|
|
|
|
|
processData: false,
|
|
|
|
|
contentType: false,
|
|
|
|
|
success: function(data) {
|
2024-04-03 21:50:47 -05:00
|
|
|
const qslCard = data.status || {}
|
2020-11-01 12:36:02 +01:00
|
|
|
if (data.status.front.status == 'Success') {
|
2024-04-03 21:50:47 -05:00
|
|
|
handleQslCardUpload(qslCard.front);
|
|
|
|
|
} else if (qslCard.front.status != '') {
|
2023-12-07 01:20:39 +01:00
|
|
|
$("#qslupload").append('<div class="alert alert-danger">Front QSL Card:' +
|
2024-04-03 21:50:47 -05:00
|
|
|
qslCard.front.error +
|
2020-11-01 12:36:02 +01:00
|
|
|
'</div>');
|
|
|
|
|
}
|
2024-04-03 21:50:47 -05:00
|
|
|
|
|
|
|
|
if (qslCard.back.status == 'Success') {
|
|
|
|
|
handleQslCardUpload(qslCard.back);
|
|
|
|
|
} else if (qslCard.back.status != '') {
|
2023-12-07 01:20:39 +01:00
|
|
|
$("#qslupload").append('<div class="alert alert-danger">\nBack QSL Card: ' +
|
2024-04-03 21:50:47 -05:00
|
|
|
qslCard.back.error +
|
2020-11-01 12:36:02 +01:00
|
|
|
'</div>');
|
|
|
|
|
}
|
2024-04-03 21:50:47 -05:00
|
|
|
|
|
|
|
|
// Show the QSL card tab
|
|
|
|
|
$('.qslcardtab').removeAttr('hidden');
|
|
|
|
|
|
|
|
|
|
// Reset the image inputs
|
|
|
|
|
$("#qslcardfront").val(null);
|
|
|
|
|
$("#qslcardback").val(null);
|
2020-11-01 12:36:02 +01:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
</script>
|
2021-07-22 07:55:34 +02:00
|
|
|
<script>
|
2024-02-22 16:56:21 +00:00
|
|
|
function addQsosToQsl(filename) {
|
|
|
|
|
var title = 'Add additional QSOs to a QSL Card';
|
|
|
|
|
|
|
|
|
|
var baseURL = "<?php echo base_url(); ?>";
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: baseURL + 'index.php/qsl/loadSearchForm',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
'filename': filename
|
|
|
|
|
},
|
|
|
|
|
success: function(html) {
|
|
|
|
|
BootstrapDialog.show({
|
|
|
|
|
title: title,
|
|
|
|
|
size: BootstrapDialog.SIZE_WIDE,
|
|
|
|
|
cssClass: 'qso-search_results',
|
|
|
|
|
nl2br: false,
|
|
|
|
|
message: html,
|
|
|
|
|
buttons: [{
|
|
|
|
|
label: lang_admin_close,
|
|
|
|
|
action: function(dialogItself) {
|
|
|
|
|
dialogItself.close();
|
|
|
|
|
}
|
|
|
|
|
}]
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2021-07-22 07:55:34 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
function addQsoToQsl(qsoid, filename, id) {
|
|
|
|
|
var title = 'Add additional QSOs to a QSL Card';
|
|
|
|
|
|
|
|
|
|
var baseURL = "<?php echo base_url(); ?>";
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: baseURL + 'index.php/qsl/addQsoToQsl',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
'filename': filename,
|
|
|
|
|
'qsoid': qsoid
|
|
|
|
|
},
|
|
|
|
|
success: function(html) {
|
|
|
|
|
if (html.status == 'Success') {
|
|
|
|
|
location.reload();
|
|
|
|
|
} else {
|
|
|
|
|
$(".alert").remove();
|
|
|
|
|
$('#searchresult').prepend('<div class="alert alert-danger">Something went wrong. Please try again!</div>');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function searchAdditionalQsos(filename) {
|
|
|
|
|
var baseURL = "<?php echo base_url(); ?>";
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: baseURL + 'index.php/qsl/searchQsos',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
'callsign': $('#callsign').val(),
|
|
|
|
|
'filename': filename
|
|
|
|
|
},
|
|
|
|
|
success: function(html) {
|
|
|
|
|
$('#searchresult').empty();
|
|
|
|
|
$('#searchresult').append(html);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2021-07-22 07:55:34 +02:00
|
|
|
</script>
|
2023-04-25 00:06:52 +02:00
|
|
|
<?php if ($this->uri->segment(1) == "contesting" && ($this->uri->segment(2) != "add" && $this->uri->segment(2) != "edit")) { ?>
|
2022-03-27 17:33:05 +02:00
|
|
|
<script>
|
2022-05-30 14:30:28 +01:00
|
|
|
var manual = <?php echo $_GET['manual']; ?>;
|
2022-03-27 17:33:05 +02:00
|
|
|
</script>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/contesting.js?v2"></script>
|
2021-01-24 15:29:29 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
|
|
|
|
<?php if ($this->uri->segment(1) == "station") { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script>
|
|
|
|
|
var baseURL = "<?php echo base_url(); ?>";
|
2021-03-14 17:30:36 +01:00
|
|
|
|
2024-05-14 15:25:10 +01:00
|
|
|
$(document).ready(function() {
|
|
|
|
|
function checkSelectedValue() {
|
|
|
|
|
var selectedValue = $('#dxcc_select').val();
|
|
|
|
|
var valuesToShow = [223, 279, 294, 265, 106, 122];
|
|
|
|
|
|
|
|
|
|
if (valuesToShow.includes(Number(selectedValue))) {
|
|
|
|
|
$('#WABbox').show();
|
|
|
|
|
} else {
|
|
|
|
|
$('#WABbox').hide();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Call on page load
|
|
|
|
|
checkSelectedValue();
|
|
|
|
|
|
|
|
|
|
// Call on change event
|
|
|
|
|
$('#dxcc_select').change(checkSelectedValue);
|
|
|
|
|
});
|
|
|
|
|
|
2021-01-24 15:29:29 +01:00
|
|
|
var state = $("#StateHelp option:selected").text();
|
|
|
|
|
if (state != "") {
|
|
|
|
|
$("#stationCntyInput").prop('disabled', false);
|
2024-02-22 16:56:21 +00:00
|
|
|
station_profile_selectize_usa_county();
|
2021-01-24 15:29:29 +01:00
|
|
|
}
|
2021-03-14 17:30:36 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
$('#StateHelp').change(function() {
|
|
|
|
|
var state = $("#StateHelp option:selected").text();
|
|
|
|
|
if (state != "") {
|
|
|
|
|
$("#stationCntyInput").prop('disabled', false);
|
|
|
|
|
station_profile_selectize_usa_county();
|
|
|
|
|
} else {
|
|
|
|
|
$("#stationCntyInput").prop('disabled', true);
|
|
|
|
|
//$('#stationCntyInput')[0].selectize.destroy();
|
|
|
|
|
$("#stationCntyInput").val("");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function station_profile_selectize_usa_county() {
|
|
|
|
|
$('#stationCntyInput').selectize({
|
|
|
|
|
maxItems: 1,
|
|
|
|
|
closeAfterSelect: true,
|
|
|
|
|
loadThrottle: 250,
|
|
|
|
|
valueField: 'name',
|
|
|
|
|
labelField: 'name',
|
|
|
|
|
searchField: 'name',
|
|
|
|
|
options: [],
|
|
|
|
|
create: false,
|
|
|
|
|
load: function(query, callback) {
|
|
|
|
|
var state = $("#StateHelp option:selected").text();
|
|
|
|
|
|
|
|
|
|
if (!query || state == "") return callback();
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: baseURL + 'index.php/station/get_county',
|
|
|
|
|
type: 'GET',
|
|
|
|
|
dataType: 'json',
|
|
|
|
|
data: {
|
|
|
|
|
query: query,
|
|
|
|
|
state: state,
|
|
|
|
|
},
|
|
|
|
|
error: function() {
|
|
|
|
|
callback();
|
|
|
|
|
},
|
|
|
|
|
success: function(res) {
|
|
|
|
|
callback(res);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
</script>
|
2021-01-24 15:29:29 +01:00
|
|
|
|
2021-02-07 00:03:05 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2021-02-07 15:22:03 +01:00
|
|
|
<?php if ($this->uri->segment(2) == "counties" || $this->uri->segment(2) == "counties_details") { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script>
|
|
|
|
|
$('.countiestable').DataTable({
|
|
|
|
|
"pageLength": 25,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: false,
|
|
|
|
|
"scrollY": "390px",
|
|
|
|
|
"scrollCollapse": true,
|
|
|
|
|
"paging": false,
|
|
|
|
|
"scrollX": true,
|
|
|
|
|
"language": {
|
|
|
|
|
url: getDataTablesLanguageUrl(),
|
|
|
|
|
},
|
|
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
|
|
|
|
});
|
2021-02-07 00:03:05 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
// change color of csv-button if dark mode is chosen
|
|
|
|
|
if (isDarkModeTheme()) {
|
|
|
|
|
$(".buttons-csv").css("color", "white");
|
|
|
|
|
}
|
2021-02-07 15:22:03 +01:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
function displayCountyContacts(state, county) {
|
|
|
|
|
var baseURL = "<?php echo base_url(); ?>";
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: baseURL + 'index.php/awards/counties_details_ajax',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
'State': state,
|
|
|
|
|
'County': county
|
|
|
|
|
},
|
|
|
|
|
success: function(html) {
|
|
|
|
|
BootstrapDialog.show({
|
|
|
|
|
title: lang_general_word_qso_data,
|
|
|
|
|
size: BootstrapDialog.SIZE_WIDE,
|
|
|
|
|
cssClass: 'qso-counties-dialog',
|
|
|
|
|
nl2br: false,
|
|
|
|
|
message: html,
|
|
|
|
|
onshown: function(dialog) {
|
|
|
|
|
$('[data-bs-toggle="tooltip"]').tooltip();
|
|
|
|
|
},
|
|
|
|
|
buttons: [{
|
|
|
|
|
label: lang_admin_close,
|
|
|
|
|
action: function(dialogItself) {
|
|
|
|
|
dialogItself.close();
|
|
|
|
|
}
|
|
|
|
|
}]
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
</script>
|
2021-02-12 23:27:08 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2021-03-21 08:19:38 +01:00
|
|
|
<?php if ($this->uri->segment(2) == "sig_details") { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script>
|
|
|
|
|
$('.tablesig').DataTable({
|
|
|
|
|
"pageLength": 25,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: false,
|
|
|
|
|
"scrollY": "400px",
|
|
|
|
|
"scrollCollapse": true,
|
|
|
|
|
"paging": false,
|
|
|
|
|
"scrollX": true,
|
2024-01-03 22:03:11 -05:00
|
|
|
"language": {
|
2024-01-03 22:18:20 -05:00
|
|
|
url: getDataTablesLanguageUrl(),
|
2024-01-03 22:03:11 -05:00
|
|
|
},
|
2024-02-22 16:56:21 +00:00
|
|
|
dom: 'Bfrtip',
|
|
|
|
|
buttons: [
|
|
|
|
|
'csv'
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// change color of csv-button if dark mode is chosen
|
|
|
|
|
if (isDarkModeTheme()) {
|
|
|
|
|
$(".buttons-csv").css("color", "white");
|
|
|
|
|
}
|
|
|
|
|
</script>
|
2021-03-21 08:19:38 +01:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2021-02-12 23:27:08 +01:00
|
|
|
<?php if ($this->uri->segment(1) == "contesting" && $this->uri->segment(2) == "add") { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script src="<?php echo base_url(); ?>assets/js/sections/contestingnames.js"></script>
|
2020-11-16 19:20:45 +01:00
|
|
|
<?php } ?>
|
2021-05-22 08:45:24 +02:00
|
|
|
|
2021-08-09 14:29:53 +02:00
|
|
|
<?php if ($this->uri->segment(1) == "themes") { ?>
|
|
|
|
|
<script>
|
2024-02-22 16:56:21 +00:00
|
|
|
function deleteTheme(id, name) {
|
|
|
|
|
BootstrapDialog.confirm({
|
|
|
|
|
title: 'DANGER',
|
|
|
|
|
message: 'Warning! Are you sure you want to delete the following theme: ' + name + '?',
|
|
|
|
|
type: BootstrapDialog.TYPE_DANGER,
|
|
|
|
|
closable: true,
|
|
|
|
|
draggable: true,
|
|
|
|
|
btnOKClass: 'btn-danger',
|
|
|
|
|
callback: function(result) {
|
|
|
|
|
if (result) {
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: base_url + 'index.php/themes/delete',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
'id': id
|
|
|
|
|
},
|
|
|
|
|
success: function(data) {
|
|
|
|
|
$(".theme_" + id).parent("tr:first").remove(); // removes mode from table
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function addThemeDialog() {
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: base_url + 'index.php/themes/add',
|
|
|
|
|
type: 'post',
|
|
|
|
|
success: function(html) {
|
|
|
|
|
BootstrapDialog.show({
|
|
|
|
|
title: 'Create Theme',
|
|
|
|
|
size: BootstrapDialog.SIZE_WIDE,
|
|
|
|
|
cssClass: 'create-theme-dialog',
|
|
|
|
|
nl2br: false,
|
|
|
|
|
message: html,
|
|
|
|
|
buttons: [{
|
|
|
|
|
label: lang_admin_close,
|
|
|
|
|
action: function(dialogItself) {
|
|
|
|
|
dialogItself.close();
|
|
|
|
|
}
|
|
|
|
|
}]
|
2021-08-09 14:29:53 +02:00
|
|
|
});
|
|
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
});
|
|
|
|
|
}
|
2021-08-09 14:29:53 +02:00
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
function addTheme(form) {
|
|
|
|
|
if (form.name.value != '') {
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: base_url + 'index.php/themes/add',
|
|
|
|
|
type: 'post',
|
|
|
|
|
data: {
|
|
|
|
|
'name': form.name.value,
|
|
|
|
|
'foldername': form.foldername.value,
|
|
|
|
|
},
|
|
|
|
|
success: function(html) {
|
|
|
|
|
location.reload();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-08-09 14:29:53 +02:00
|
|
|
</script>
|
|
|
|
|
<?php } ?>
|
2021-09-15 19:30:34 +02:00
|
|
|
|
2023-05-05 12:00:23 +02:00
|
|
|
|
|
|
|
|
<?php if ($this->uri->segment(1) == "eqsl") { ?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script>
|
|
|
|
|
$('.qsotable').DataTable({
|
|
|
|
|
"stateSave": true,
|
|
|
|
|
"pageLength": 25,
|
|
|
|
|
responsive: false,
|
|
|
|
|
"scrollY": "400px",
|
|
|
|
|
"scrollCollapse": true,
|
|
|
|
|
"paging": false,
|
|
|
|
|
"scrollX": true,
|
|
|
|
|
"language": {
|
|
|
|
|
url: getDataTablesLanguageUrl(),
|
|
|
|
|
},
|
|
|
|
|
"ordering": true,
|
|
|
|
|
"order": [0, 'desc'],
|
|
|
|
|
});
|
|
|
|
|
</script>
|
2023-05-05 12:00:23 +02:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2022-11-30 17:17:19 +01:00
|
|
|
<?php if ($this->uri->segment(1) == "awards") {
|
2024-02-22 16:56:21 +00:00
|
|
|
// Get Date format
|
|
|
|
|
if ($this->session->userdata('user_date_format')) {
|
|
|
|
|
// If Logged in and session exists
|
|
|
|
|
$custom_date_format = $this->session->userdata('user_date_format');
|
|
|
|
|
} else {
|
|
|
|
|
// Get Default date format from /config/cloudlog.php
|
|
|
|
|
$custom_date_format = $this->config->item('qso_date_format');
|
|
|
|
|
}
|
2022-10-25 13:35:45 +02:00
|
|
|
|
|
|
|
|
switch ($custom_date_format) {
|
2024-02-22 16:56:21 +00:00
|
|
|
case 'd/m/y':
|
|
|
|
|
$usethisformat = 'D/MM/YY';
|
|
|
|
|
break;
|
|
|
|
|
case 'd/m/Y':
|
|
|
|
|
$usethisformat = 'D/MM/YYYY';
|
|
|
|
|
break;
|
|
|
|
|
case 'm/d/y':
|
|
|
|
|
$usethisformat = 'MM/D/YY';
|
|
|
|
|
break;
|
|
|
|
|
case 'm/d/Y':
|
|
|
|
|
$usethisformat = 'MM/D/YYYY';
|
|
|
|
|
break;
|
|
|
|
|
case 'd.m.Y':
|
|
|
|
|
$usethisformat = 'D.MM.YYYY';
|
|
|
|
|
break;
|
|
|
|
|
case 'y/m/d':
|
|
|
|
|
$usethisformat = 'YY/MM/D';
|
|
|
|
|
break;
|
|
|
|
|
case 'Y-m-d':
|
|
|
|
|
$usethisformat = 'YYYY-MM-D';
|
|
|
|
|
break;
|
|
|
|
|
case 'M d, Y':
|
|
|
|
|
$usethisformat = 'MMM D, YYYY';
|
|
|
|
|
break;
|
|
|
|
|
case 'M d, y':
|
|
|
|
|
$usethisformat = 'MMM D, YY';
|
|
|
|
|
break;
|
2022-10-25 13:35:45 +02:00
|
|
|
}
|
|
|
|
|
|
2024-02-22 16:56:21 +00:00
|
|
|
?>
|
2022-10-25 13:35:45 +02:00
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/moment.min.js"></script>
|
|
|
|
|
<script type="text/javascript" src="<?php echo base_url(); ?>assets/js/datetime-moment.js"></script>
|
2022-11-30 17:17:19 +01:00
|
|
|
<?php if ($this->uri->segment(2) == "wwff") { ?>
|
|
|
|
|
<script>
|
|
|
|
|
$.fn.dataTable.moment('<?php echo $usethisformat ?>');
|
|
|
|
|
$.fn.dataTable.ext.buttons.clear = {
|
|
|
|
|
className: 'buttons-clear',
|
2024-02-22 16:56:21 +00:00
|
|
|
action: function(e, dt, node, config) {
|
|
|
|
|
dt.search('').draw();
|
2022-11-30 17:17:19 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
$('#wwfftable').DataTable({
|
|
|
|
|
"pageLength": 25,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: true,
|
2024-02-22 16:56:21 +00:00
|
|
|
"scrollY": "500px",
|
2022-11-30 17:17:19 +01:00
|
|
|
"scrollCollapse": true,
|
2024-02-22 16:56:21 +00:00
|
|
|
"paging": false,
|
2022-11-30 17:17:19 +01:00
|
|
|
"scrollX": true,
|
2024-01-03 22:03:11 -05:00
|
|
|
"language": {
|
2024-01-03 22:18:20 -05:00
|
|
|
url: getDataTablesLanguageUrl(),
|
2024-01-03 22:03:11 -05:00
|
|
|
},
|
2024-02-22 16:56:21 +00:00
|
|
|
"order": [0, 'asc'],
|
2022-11-30 17:17:19 +01:00
|
|
|
dom: 'Bfrtip',
|
2024-02-22 16:56:21 +00:00
|
|
|
buttons: [{
|
|
|
|
|
extend: 'csv'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
extend: 'clear',
|
|
|
|
|
text: 'Clear'
|
|
|
|
|
}
|
2022-11-30 17:17:19 +01:00
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
// change color of csv-button if dark mode is chosen
|
|
|
|
|
if (isDarkModeTheme()) {
|
2024-02-22 16:56:21 +00:00
|
|
|
$('[class*="buttons"]').css("color", "white");
|
2022-10-25 13:35:45 +02:00
|
|
|
}
|
2022-11-30 17:17:19 +01:00
|
|
|
</script>
|
|
|
|
|
<?php } else if ($this->uri->segment(2) == "pota") { ?>
|
|
|
|
|
<script>
|
|
|
|
|
$.fn.dataTable.moment('<?php echo $usethisformat ?>');
|
|
|
|
|
$.fn.dataTable.ext.buttons.clear = {
|
|
|
|
|
className: 'buttons-clear',
|
2024-02-22 16:56:21 +00:00
|
|
|
action: function(e, dt, node, config) {
|
|
|
|
|
dt.search('').draw();
|
2022-11-30 17:17:19 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
$('#potatable').DataTable({
|
|
|
|
|
"pageLength": 25,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: true,
|
2024-02-22 16:56:21 +00:00
|
|
|
"scrollY": "500px",
|
2022-11-30 17:17:19 +01:00
|
|
|
"scrollCollapse": true,
|
2024-02-22 16:56:21 +00:00
|
|
|
"paging": false,
|
2022-11-30 17:17:19 +01:00
|
|
|
"scrollX": true,
|
2024-01-03 22:03:11 -05:00
|
|
|
"language": {
|
2024-01-03 22:18:20 -05:00
|
|
|
url: getDataTablesLanguageUrl(),
|
2024-01-03 22:03:11 -05:00
|
|
|
},
|
2024-02-22 16:56:21 +00:00
|
|
|
"order": [0, 'asc'],
|
2022-11-30 17:17:19 +01:00
|
|
|
dom: 'Bfrtip',
|
2024-02-22 16:56:21 +00:00
|
|
|
buttons: [{
|
|
|
|
|
extend: 'csv'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
extend: 'clear',
|
|
|
|
|
text: 'Clear'
|
|
|
|
|
}
|
2022-11-30 17:17:19 +01:00
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
// change color of csv-button if dark mode is chosen
|
|
|
|
|
if (isDarkModeTheme()) {
|
2024-02-22 16:56:21 +00:00
|
|
|
$('[class*="buttons"]').css("color", "white");
|
2022-11-30 17:17:19 +01:00
|
|
|
}
|
|
|
|
|
</script>
|
2023-01-16 17:08:27 +01:00
|
|
|
<?php } else if ($this->uri->segment(2) == "dok") { ?>
|
|
|
|
|
<script>
|
|
|
|
|
$.fn.dataTable.ext.buttons.clear = {
|
|
|
|
|
className: 'buttons-clear',
|
2024-02-22 16:56:21 +00:00
|
|
|
action: function(e, dt, node, config) {
|
|
|
|
|
dt.search('').draw();
|
2023-01-16 17:08:27 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
$('#doktable').DataTable({
|
|
|
|
|
"pageLength": 25,
|
|
|
|
|
responsive: false,
|
|
|
|
|
ordering: false,
|
2024-02-22 16:56:21 +00:00
|
|
|
"scrollY": "500px",
|
2023-01-16 17:08:27 +01:00
|
|
|
"scrollCollapse": true,
|
2024-02-22 16:56:21 +00:00
|
|
|
"paging": false,
|
2023-01-16 17:08:27 +01:00
|
|
|
"scrollX": true,
|
2024-01-03 22:03:11 -05:00
|
|
|
"language": {
|
2024-01-03 22:18:20 -05:00
|
|
|
url: getDataTablesLanguageUrl(),
|
2024-01-03 22:03:11 -05:00
|
|
|
},
|
2023-01-16 17:08:27 +01:00
|
|
|
dom: 'Bfrtip',
|
2024-02-22 16:56:21 +00:00
|
|
|
buttons: [{
|
|
|
|
|
extend: 'csv'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
extend: 'clear',
|
|
|
|
|
text: 'Clear'
|
|
|
|
|
}
|
2023-01-16 17:08:27 +01:00
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
// change color of csv-button if dark mode is chosen
|
|
|
|
|
if (isDarkModeTheme()) {
|
2024-02-22 16:56:21 +00:00
|
|
|
$('[class*="buttons"]').css("color", "white");
|
2023-01-16 17:08:27 +01:00
|
|
|
}
|
|
|
|
|
</script>
|
2022-11-30 17:17:19 +01:00
|
|
|
<?php } ?>
|
2022-10-25 13:35:45 +02:00
|
|
|
<?php } ?>
|
|
|
|
|
|
2023-12-18 09:01:45 +01:00
|
|
|
<?php if ($this->uri->segment(1) == "user") { ?>
|
2023-11-30 18:11:22 +01:00
|
|
|
<!-- [MAP Custom] select list with icons -->
|
|
|
|
|
<script>
|
2024-02-22 16:56:21 +00:00
|
|
|
$(document).ready(function() {
|
|
|
|
|
$('.icon_selectBox').off('click').on('click', function() {
|
2023-11-30 18:11:22 +01:00
|
|
|
var boxcontent = $(this).attr('data-boxcontent');
|
2024-02-22 16:56:21 +00:00
|
|
|
if ($('.icon_selectBox_data[data-boxcontent="' + boxcontent + '"]').is(":hidden")) {
|
|
|
|
|
$('.icon_selectBox_data[data-boxcontent="' + boxcontent + '"]').show();
|
|
|
|
|
} else {
|
|
|
|
|
$('.icon_selectBox_data[data-boxcontent="' + boxcontent + '"]').hide();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
$('.icon_selectBox_data').off('mouseleave').on('mouseleave', function() {
|
|
|
|
|
if ($(this).is(":visible")) {
|
|
|
|
|
$(this).hide();
|
|
|
|
|
}
|
2023-11-30 18:11:22 +01:00
|
|
|
});
|
2024-02-22 16:56:21 +00:00
|
|
|
$('.icon_selectBox_data label').off('click').on('click', function() {
|
2023-11-30 18:11:22 +01:00
|
|
|
var boxcontent = $(this).closest('.icon_selectBox_data').attr('data-boxcontent');
|
2024-02-22 16:56:21 +00:00
|
|
|
$('input[name="user_map_' + boxcontent + '_icon"]').attr('value', $(this).attr('data-value'));
|
2023-12-12 21:41:23 +01:00
|
|
|
if ($(this).attr('data-value') != "0") {
|
2024-02-22 16:56:21 +00:00
|
|
|
$('.user_icon_color[data-icon="' + boxcontent + '"]').show();
|
|
|
|
|
$('.icon_selectBox[data-boxcontent="' + boxcontent + '"] .icon_overSelect').html($(this).html());
|
2023-11-30 18:11:22 +01:00
|
|
|
} else {
|
2024-02-22 16:56:21 +00:00
|
|
|
$('.user_icon_color[data-icon="' + boxcontent + '"]').hide();
|
|
|
|
|
$('.icon_selectBox[data-boxcontent="' + boxcontent + '"] .icon_overSelect').html($(this).html().substring(0, 10) + '.');
|
2023-11-30 18:11:22 +01:00
|
|
|
}
|
2024-02-22 16:56:21 +00:00
|
|
|
$('.icon_selectBox_data[data-boxcontent="' + boxcontent + '"]').hide();
|
2023-12-12 21:41:23 +01:00
|
|
|
});
|
2023-12-18 09:01:45 +01:00
|
|
|
|
|
|
|
|
$('.collapse').on('shown.bs.collapse', function(e) {
|
|
|
|
|
var $card = $(this).closest('.accordion-item');
|
|
|
|
|
var $open = $($(this).data('parent')).find('.collapse.show');
|
2024-02-22 16:56:21 +00:00
|
|
|
|
2023-12-18 09:01:45 +01:00
|
|
|
var additionalOffset = 0;
|
2024-02-22 16:56:21 +00:00
|
|
|
if ($card.prevAll().filter($open.closest('.accordion-item')).length !== 0) {
|
|
|
|
|
additionalOffset = $open.height();
|
2023-12-18 09:01:45 +01:00
|
|
|
}
|
|
|
|
|
$('html,body').animate({
|
|
|
|
|
scrollTop: $card.offset().top - additionalOffset
|
|
|
|
|
}, 300);
|
|
|
|
|
});
|
2023-11-30 18:11:22 +01:00
|
|
|
});
|
|
|
|
|
</script>
|
2023-12-12 21:41:23 +01:00
|
|
|
<?php } ?>
|
2023-11-30 18:11:22 +01:00
|
|
|
|
2022-12-18 16:49:54 +01:00
|
|
|
<?php
|
2024-02-22 16:56:21 +00:00
|
|
|
if (isset($scripts) && is_array($scripts)) {
|
|
|
|
|
foreach ($scripts as $script) {
|
|
|
|
|
?><script type="text/javascript" src="<?php echo base_url() . $script; ?>"></script>
|
|
|
|
|
<?php
|
|
|
|
|
}
|
2022-12-18 16:49:54 +01:00
|
|
|
}
|
|
|
|
|
?>
|
2024-02-22 16:56:21 +00:00
|
|
|
<script>
|
|
|
|
|
<?php
|
|
|
|
|
echo "var lang_datatables_language = '" . lang("datatables_language") . "';"
|
|
|
|
|
?>
|
|
|
|
|
</script>
|
|
|
|
|
</body>
|
|
|
|
|
|
|
|
|
|
</html>
|