connectcore-demo-example: initial dashboard and management pages
Generated from repo https://github.com/digidotcom/digi-iot-web-apps at commit 95cb32f39f2fafd7bde7fb02c47bc966d8f75916 and directory 'connectcore'. https://onedigi.atlassian.net/browse/DEL-7742 Signed-off-by: Tatiana Leon <Tatiana.Leon@digi.com>
|
|
@ -0,0 +1,534 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
Digi Demo - Management
|
||||
</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
|
||||
<link href="https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/css/bootstrap4-toggle.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/9.7.3/css/bootstrap-slider.min.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="/static/css/login.css">
|
||||
<link rel="stylesheet" href="/static/css/general.css">
|
||||
<link rel="stylesheet" href="/static/css/toastr.css">
|
||||
<link rel="stylesheet" href="/static/css/xterm.css">
|
||||
|
||||
<!-- JS, Popper.js, and jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
|
||||
<script src="https://kit.fontawesome.com/1e66c78073.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/js/bootstrap4-toggle.min.js"></script>
|
||||
<script src="https://www.gstatic.com/charts/loader.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/9.7.3/bootstrap-slider.min.js"></script>
|
||||
<script>
|
||||
google.charts.load("current", {"packages":["line"]});
|
||||
</script>
|
||||
<script type="text/javascript" src="/static/js/common.js"></script>
|
||||
<script type="text/javascript" src="/static/js/devices.js"></script>
|
||||
<script type="text/javascript" src="/static/js/jquery.pjax.js"></script>
|
||||
<script type="text/javascript" src="/static/js/jquery.matchHeight-min.js"></script>
|
||||
<script type="text/javascript" src="/static/js/toastr.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav id="topBar" class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
|
||||
<div class="nav-container">
|
||||
<a id="banner-link" class="navbar-brand align-middle" href="/">
|
||||
<div class="d-flex align-items-baseline">
|
||||
<img id="banner-logo" class="banner-icon" src="/static/images/Digi_logo_banner.png">
|
||||
<p id="banner-text">ConnectCore Demo</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="nav-right-container">
|
||||
|
||||
<div id="data_usage_panel" class="data-usage-panel" style="display: none;">
|
||||
<div class="data-usage-panel-wrapper">
|
||||
<div id="data_usage_loading" class="data-usage-loading">
|
||||
<div class="data-usage-loading-background"></div>
|
||||
<img class="data-usage-loading-icon" src="/static/images/loading.gif" alt="Loading..." />
|
||||
<div class="data-usage-loading-text">Loading data...</div>
|
||||
</div>
|
||||
<div id="data_usage_content" class="data-usage-content">
|
||||
<div class="data-usage-section">
|
||||
<div class="data-usage-header">
|
||||
<div class="data-usage-title">Total account data usage</div>
|
||||
<div class="fas fa-sync device-card-header-button" onclick="refreshUsageData()"></div>
|
||||
</div>
|
||||
<div id="data_usage_total" class="data-usage-total-value">-</div>
|
||||
<div class="data-usage-graphic-container">
|
||||
<button id="data_usage_graphic_devices" class="fas data-usage-graphic data-usage-graphic-devices" data-toggle="tooltip" data-placement="bottom" title="Total data usage by devices"></button>
|
||||
<button id="data_usage_graphic_web" class="fas data-usage-graphic data-usage-graphic-web" data-toggle="tooltip" data-placement="bottom" title="Total data usage by web APIs"></button>
|
||||
</div>
|
||||
<div class="data-usage-values-container">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="data-usage-legend data-usage-graphic-devices"></div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="data-usage-legend-text">Devices data usage</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="data-usage-legend data-usage-graphic-web"></div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="data-usage-legend-text">Web APIs data usage</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-usage-section">
|
||||
<div class="data-usage-title">Devices usage</div>
|
||||
<div class="data-usage-values-container">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="data-usage-value-label-bold">Total devices usage:</div>
|
||||
</td>
|
||||
<td>
|
||||
<div id="data_usage_devices" class="data-usage-value-bold">-</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="data-usage-value-label">Current device usage:</div>
|
||||
</td>
|
||||
<td>
|
||||
<div id="data_usage_current_device" class="data-usage-value">-</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-usage-section">
|
||||
<div class="data-usage-title">Web APIs usage</div>
|
||||
<div class="data-usage-values-container">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="data-usage-value-label-bold">Total web APIs usage:</div>
|
||||
</td>
|
||||
<td>
|
||||
<div id="data_usage_web" class="data-usage-value-bold">-</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="data-usage-value-label">Web services usage:</div>
|
||||
</td>
|
||||
<td>
|
||||
<div id="data_usage_web_services" class="data-usage-value">-</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="data-usage-value-label">Monitors usage:</div>
|
||||
</td>
|
||||
<td>
|
||||
<div id="data_usage_monitors" class="data-usage-value">-</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<img src="/static/images/board.png" class="device-title-img" title="Device">
|
||||
</div>
|
||||
<div id="device-name"></div>
|
||||
<div>
|
||||
<img src="/static/images/status_offline.png" id="device-connection-status" class="device-title-img" title="Offline">
|
||||
</div>
|
||||
<div class="vertical-separator"></div>
|
||||
<div>
|
||||
<button id="devices-list-button" class="btn widget-button" type="button" value="0" onclick="window.open('/','_self');">
|
||||
<img src="/static/images/devices_list_gray.png" id="devices-list-icon" class="devices-list-icon" data-toggle="tooltip" data-placement="bottom" title="Open devices list">
|
||||
</button>
|
||||
<button id="data_usage_button" class="btn widget-button" type="button" value="0" onclick="toggleDataUsagePanel();">
|
||||
<img src="/static/images/data_usage.png" id="data_usage_icon" class="devices-list-icon" data-toggle="tooltip" data-placement="bottom" title="Show data usage">
|
||||
</button>
|
||||
</div>
|
||||
<div class="vertical-separator"></div>
|
||||
|
||||
<div class="dropdown digi-gray" id="profile-container">
|
||||
<button class="btn dropdown-toggle" type="button" id="profileMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<img id="profile-logo" class="banner-icon" src="/static/images/profile_gray.png">
|
||||
</button>
|
||||
<div class="dropdown-menu shadow-sm" aria-labelledby="profileMenuButton">
|
||||
<a class="dropdown-item" href="/access/logout/">Log out</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="bg-light" id="body-row">
|
||||
|
||||
<!-- Sidebar https://www.codeply.com/go/3e0RAjccRO/bootstrap-4-collapsing-sidebar-menu# -->
|
||||
<div id="sidebar-container" class="sidebar-expanded d-none d-md-block">
|
||||
<!-- d-* hides the Sidebar in smaller devices. Its items can be kept on the Navbar 'Menu' -->
|
||||
<div id="sidebar-contents">
|
||||
<!-- Bootstrap List Group -->
|
||||
|
||||
<ul id="sections" data-pjax class="list-group">
|
||||
<li>
|
||||
<a data-pjax href="/dashboard/" class="list-group-item list-group-item-action d-flex align-items-center">
|
||||
<div class="d-flex w-100 justify-content-start align-items-center">
|
||||
<span class="digi-menu-icon fas fa-tachometer-alt fa-fw fa-lg mr-3"></span>
|
||||
<span class="menu-collapsed">Dashboard</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a data-pjax href="/history/" class="list-group-item list-group-item-action d-flex align-items-center">
|
||||
<div class="d-flex w-100 justify-content-start align-items-center">
|
||||
<span class="digi-menu-icon fas fa-chart-area fa-fw fa-lg mr-3"></span>
|
||||
<span class="menu-collapsed">Historical data</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a data-pjax href="/management/" class="list-group-item list-group-item-action d-flex align-items-center">
|
||||
<div class="d-flex w-100 justify-content-start align-items-center">
|
||||
<span class="digi-menu-icon fas fa-cog fa-fw fa-lg mr-3"></span>
|
||||
<span class="menu-collapsed">Management</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul id="collapsator" class="list-group">
|
||||
<a href="#top" data-toggle="sidebar-collapse" class="list-group-item list-group-item-action d-flex align-items-center">
|
||||
<div class="d-flex w-100 justify-content-start align-items-center">
|
||||
<span id="collapse-icon" class="digi-menu-icon fas fa-fw fa-lg mr-3"></span>
|
||||
<span id="collapse-text" class="menu-collapsed">Collapse</span>
|
||||
</div>
|
||||
</a>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid">
|
||||
|
||||
<div id="loading">
|
||||
<img id="loading-image" src="/static/images/loading.gif" alt="Loading..." />
|
||||
</div>
|
||||
<div id="pjax-container">
|
||||
|
||||
<!-- <input type="file" id="firmware_file" style="display:none" onchange="firmwareFileChanged()" accept=".swu,.json,.txt"/>-->
|
||||
<input type="file" id="firmware_file" style="display:none" onchange="firmwareFileChanged()"/>
|
||||
<div class="row justify-content-lg-center">
|
||||
<div class="col-lg-12 col-xl-12">
|
||||
<div id="loading_popup" class="popup popup-loading shadow">
|
||||
<img class="popup-item" src="/static/images/loading.gif" alt="Loading..." />
|
||||
<div id="loading_popup_message" class="popup-text">Loading information...</div>
|
||||
</div>
|
||||
<div id="info_popup" class="popup popup-info shadow d-none">
|
||||
<div id="info_popup_title" class="popup-title">Error</div>
|
||||
<div id="info_popup_message" class="popup-text">-</div>
|
||||
</div>
|
||||
<div id="confirm_dialog" class="confirm-dialog d-none">
|
||||
<div class="device-card-header">
|
||||
<span class="fas fa-question-circle fa-lg mr-2"></span>
|
||||
<span id="confirm_dialog_title"></span>
|
||||
<div class="fas fa-window-close fa-lg device-card-header-button" onclick="showPopup(ID_LOADING_WRAPPER, ID_CONFIRM_DIALOG, false)"></div>
|
||||
</div>
|
||||
<div id="confirm_dialog_message" class="confirm-dialog-message"></div>
|
||||
<div class="confirm-dialog-buttons-container">
|
||||
<div id="confirm_dialog_no_button" class="device-card-button confirm-dialog-button">No</div>
|
||||
<div id="confirm_dialog_yes_button" class="device-card-button confirm-dialog-button">Yes</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="loading_wrapper" class="loading-wrapper element-grayed">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">System monitor</h5>
|
||||
<div>Configure the system monitor service parameters.</div>
|
||||
<div class="system-monitor-param-container">
|
||||
<span class="system-monitor-param-label">Time between samples:</span>
|
||||
<input id="sample_rate" class="system-monitor-input" type="text"/>
|
||||
<button class="fas fa-question-circle system-monitor-help" title="Time to wait between samples read (in seconds). Must be a positive number greater than 0." data-toggle="tooltip" data-placement="right"></button>
|
||||
</div>
|
||||
<div id="sample_rate_error" class="system-monitor-error"></div>
|
||||
<div class="system-monitor-param-container">
|
||||
<span class="system-monitor-param-label">Samples to buffer:</span>
|
||||
<input id="num_samples_upload" class="system-monitor-input" type="text"/>
|
||||
<button class="fas fa-question-circle system-monitor-help" title="Number of samples of each parameter to gather before uploading them to Digi Remote Manager. Must be a positive number greater than 0." data-toggle="tooltip" data-placement="right"></button>
|
||||
</div>
|
||||
<div id="samples_buffer_error" class="system-monitor-error"></div>
|
||||
<div id="save_button" class="device-card-button system-monitor-save system-monitor-save-disabled" onclick="saveSystemMonitor()">Save</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Reboot device</h5>
|
||||
<div>Click <b>REBOOT</b> to restart your device.</div>
|
||||
<div id="reboot_button" class="device-card-button reboot-button" onclick="askReboot()">REBOOT</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Firmware update</h5>
|
||||
<table style="margin: 15px;">
|
||||
<tr>
|
||||
<td style="width: 180px; vertical-align: top;">
|
||||
<span class="device-card-content-text">Current DEY version:</span>
|
||||
</td>
|
||||
<td>
|
||||
<span id="dey_version" class="device-card-content-value">-</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">
|
||||
<span class="device-card-content-text">Current kernel version:</span>
|
||||
</td>
|
||||
<td>
|
||||
<span id="kernel_version" class="device-card-content-value">-</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">
|
||||
<span class="device-card-content-text">Current U-Boot version:</span>
|
||||
</td>
|
||||
<td>
|
||||
<span id="uboot_version" class="device-card-content-value">-</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="firmware-tabs">
|
||||
<div id="firmware_tab_upload_header" class="firmware-tab-header firmware-tab-header-active" onclick="showFirmwareTab(ID_FIRMWARE_TAB_UPLOAD)">
|
||||
<span class="fas fa-upload fa-lg"></span>
|
||||
<span class="firmware-tab-text">Upload file</span>
|
||||
</div>
|
||||
<div id="firmware_tab_fileset_header" class="firmware-tab-header" onclick="showFirmwareTab(ID_FIRMWARE_TAB_FILESET)">
|
||||
<span class="fas fa-database fa-lg"></span>
|
||||
<span class="firmware-tab-text">Download from storage</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="firmware-tab-container">
|
||||
<div id="firmware_tab_upload" class="firmware-tab firmware-tab-upload">
|
||||
<div>Upload and install a firmware file from your computer.</div>
|
||||
<div class="firmware-file-container">
|
||||
<div id="select_firmware_button" class="device-card-button browse-firmware-button" onclick="openFirmwareBrowser()">Choose file...</div>
|
||||
<div id="firmware_file_label" class="firmware-file-label">No file chosen</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="firmware_tab_fileset" class="firmware-tab firmware-tab-fileset">
|
||||
<div>Download and install firmware from Digi Remote Manager storage.</div>
|
||||
<div class="fileset-items-header">
|
||||
<div class="fileset-entry-icon"></div>
|
||||
<div class="fileset-entry-name">Name</div>
|
||||
<div class="fileset-entry-path">Path</div>
|
||||
<div class="fileset-entry-size">Size</div>
|
||||
<div class="fileset-entry-last-modified">Last modified</div>
|
||||
</div>
|
||||
<div id="fileset_items_container" class="fileset-items-container"></div>
|
||||
<div id="refresh_fileset_button" class="device-card-button refresh-fileset-button" onclick="refreshFilesetFiles()">Refresh</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="update_firmware_progress" class="update-firmware-progress">
|
||||
<div id="update_firmware_progress_title" class="update-firmware-progress-title">Firmware update in progress...</div>
|
||||
<div class="progress update-firmware-progress-bar">
|
||||
<div id="update_firmware_progress_bar" class="progress-bar progress-bar-striped update-firmware-progress-bar update-firmware-progress-bar-info update-firmware-progress-bar-hover" role="progressbar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" style="width:40%">40%</div>
|
||||
</div>
|
||||
<span id="update_firmware_progress_message" class="update-firmware-progress-status">Waiting for firmware update...</span>
|
||||
</div>
|
||||
<div>
|
||||
<div id="update_firmware_button" class="device-card-button update-firmware-button management-button-disabled" onclick="askUpdateFirmware()">Update Firmware</div>
|
||||
<div id="cancel_update_firmware_button" class="device-card-button cancel-firmware-update-button management-button-disabled" onclick="askCancelFirmwareUpdate()">Cancel update</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Reset variables.
|
||||
readingManagementInfo = false;
|
||||
managementInfoRead = false;
|
||||
deviceRebooting = false;
|
||||
updatingFirmware = false;
|
||||
firmwareUpdateTimer = null;
|
||||
selectedFilesetEntry = null;
|
||||
filesLoaded = false;
|
||||
// Initialize page.
|
||||
initializeManagementPage();
|
||||
// Register system monitor input changed.
|
||||
$("#sample_rate").on("input", function(event) {
|
||||
validateSystemMonitor(event.target.value);
|
||||
});
|
||||
$("#num_samples_upload").on("input", function(event) {
|
||||
validateSystemMonitor();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function getCookie(cName) {
|
||||
if (document.cookie.length > 0) {
|
||||
cStart = document.cookie.indexOf(cName + "=");
|
||||
if (cStart != -1) {
|
||||
cStart = cStart + cName.length + 1;
|
||||
cEnd = document.cookie.indexOf(";", cStart);
|
||||
if (cEnd == -1)
|
||||
cEnd = document.cookie.length;
|
||||
return unescape(document.cookie.substring(cStart, cEnd));
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
$.ajaxSetup({
|
||||
headers: {
|
||||
"X-CSRFToken": getCookie("csrftoken")
|
||||
}
|
||||
});
|
||||
|
||||
$(document).ready(function() {
|
||||
$("#banner-link").on({
|
||||
"mouseover" : function() {
|
||||
$("#banner-logo").attr("src", "/static/images/Digi_logo_banner_gray.png");
|
||||
},
|
||||
"mouseout" : function() {
|
||||
$("#banner-logo").attr("src", "/static/images/Digi_logo_banner.png");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Enable the tooltip library.
|
||||
$(function() {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Local JS files and functions -->
|
||||
|
||||
<script>
|
||||
$( document ).ready(function() {
|
||||
$("#profile-container").on({
|
||||
"mouseover" : function() {
|
||||
$("#profile-logo").attr("src", "/static/images/profile_green.png");
|
||||
if (!$("#profile-container").hasClass("digi-green")) {
|
||||
$("#profile-container").addClass("digi-green");
|
||||
}
|
||||
if ($("#profile-container").hasClass("digi-gray")) {
|
||||
$("#profile-container").removeClass("digi-gray");
|
||||
}
|
||||
},
|
||||
"mouseout" : function() {
|
||||
$("#profile-logo").attr("src", "/static/images/profile_gray.png");
|
||||
if (!$("#profile-container").hasClass("digi-gray")) {
|
||||
$("#profile-container").addClass("digi-gray");
|
||||
}
|
||||
if ($("#profile-container").hasClass("digi-green")) {
|
||||
$("#profile-container").removeClass("digi-green");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Local JS files and functions -->
|
||||
<script type="text/javascript" src="/static/js/sidebar.js"></script>
|
||||
<script type="text/javascript" src="/static/js/ccimx8mm-dvk.js"></script>
|
||||
<script type="text/javascript" src="/static/js/ccimx8m-nano.js"></script>
|
||||
<script type="text/javascript" src="/static/js/ccimx8x-sbc-pro.js"></script>
|
||||
<script type="text/javascript" src="/static/js/dashboard.js"></script>
|
||||
<script type="text/javascript" src="/static/js/management.js"></script>
|
||||
<script type="text/javascript" src="/static/js/history.js"></script>
|
||||
<script type="text/javascript" src="/static/js/xterm.js"></script>
|
||||
<script type="text/javascript" src="/static/js/xterm-addon-fit.js"></script>
|
||||
<script type="text/javascript" src="/static/js/console.js"></script>
|
||||
<script type="text/javascript" src="/static/js/file-system.js"></script>
|
||||
<script type="text/javascript" src="/static/js/data-usage.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Don't show the loading spinner at the beginning. Initial page
|
||||
// load is full, so not relying on AJAX.
|
||||
$("#loading").hide();
|
||||
|
||||
verifyParameters();
|
||||
|
||||
// Set up PJAX.
|
||||
if ($.support.pjax) {
|
||||
$.pjax.defaults.timeout = 20000;
|
||||
$.pjax.defaults.fragment = "#pjax-container";
|
||||
$(document).pjax("a[data-pjax]", "#pjax-container");
|
||||
|
||||
$(document).on("pjax:beforeSend", function() {
|
||||
// Do not load new content if there are unsaved changes.
|
||||
if ($("#save-schedule").length && !$("#save-schedule").attr("disabled") && !confirm("There are unsaved changes. Do you want to exit?"))
|
||||
return false;
|
||||
});
|
||||
|
||||
$(document).on("pjax:send", function(e) {
|
||||
setSelectedSection(e.currentTarget.activeElement);
|
||||
$("#pjax-container").hide();
|
||||
$("#loading").show();
|
||||
});
|
||||
|
||||
$(document).on("pjax:complete", function() {
|
||||
$("#loading").hide();
|
||||
$("#pjax-container").show();
|
||||
verifyParameters();
|
||||
});
|
||||
}
|
||||
|
||||
// Append the URL parameters to the section links.
|
||||
var params = new URLSearchParams(window.location.search).toString();
|
||||
if (params) {
|
||||
$("#sections li").each(function(i, n) {
|
||||
n.children[0].href += "?" + params;
|
||||
});
|
||||
}
|
||||
|
||||
// Set the selected section.
|
||||
setSelectedSection();
|
||||
|
||||
// Set the name of the selected device.
|
||||
$("#device-name").html("DEY device".toUpperCase());
|
||||
|
||||
// Change the color of the devices icon when hovering.
|
||||
$("#devices-list-button").on({
|
||||
"mouseover" : function() {
|
||||
$("#devices-list-icon").attr("src", "/static/images/devices_list_green.png");
|
||||
},
|
||||
"mouseout" : function() {
|
||||
$("#devices-list-icon").attr("src", "/static/images/devices_list_gray.png");
|
||||
}
|
||||
});
|
||||
// Change the color of the data usage icon when hovering.
|
||||
$("#data_usage_button").on({
|
||||
"mouseover" : function() {
|
||||
$("#data_usage_icon").attr("src", "/static/images/data_usage_green.png");
|
||||
},
|
||||
"mouseout" : function() {
|
||||
$("#data_usage_icon").attr("src", "/static/images/data_usage.png");
|
||||
}
|
||||
});
|
||||
|
||||
// Check the device connection status.
|
||||
checkDeviceConnectionStatus();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,228 @@
|
|||
.toast-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
.toast-message {
|
||||
-ms-word-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.toast-message a,
|
||||
.toast-message label {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.toast-message a:hover {
|
||||
color: #CCCCCC;
|
||||
text-decoration: none;
|
||||
}
|
||||
.toast-close-button {
|
||||
position: relative;
|
||||
right: -0.3em;
|
||||
top: -0.3em;
|
||||
float: right;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #FFFFFF;
|
||||
-webkit-text-shadow: 0 1px 0 #ffffff;
|
||||
text-shadow: 0 1px 0 #ffffff;
|
||||
opacity: 0.8;
|
||||
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80);
|
||||
filter: alpha(opacity=80);
|
||||
line-height: 1;
|
||||
}
|
||||
.toast-close-button:hover,
|
||||
.toast-close-button:focus {
|
||||
color: #000000;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
opacity: 0.4;
|
||||
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=40);
|
||||
filter: alpha(opacity=40);
|
||||
}
|
||||
.rtl .toast-close-button {
|
||||
left: -0.3em;
|
||||
float: left;
|
||||
right: 0.3em;
|
||||
}
|
||||
/*Additional properties for button version
|
||||
iOS requires the button element instead of an anchor tag.
|
||||
If you want the anchor version, it requires `href="#"`.*/
|
||||
button.toast-close-button {
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
.toast-top-center {
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.toast-bottom-center {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.toast-top-full-width {
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.toast-bottom-full-width {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.toast-top-left {
|
||||
top: 12px;
|
||||
left: 12px;
|
||||
}
|
||||
.toast-top-right {
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
}
|
||||
.toast-bottom-right {
|
||||
right: 12px;
|
||||
bottom: 12px;
|
||||
}
|
||||
.toast-bottom-left {
|
||||
bottom: 12px;
|
||||
left: 12px;
|
||||
}
|
||||
#toast-container {
|
||||
position: fixed;
|
||||
z-index: 999999;
|
||||
pointer-events: none;
|
||||
/*overrides*/
|
||||
}
|
||||
#toast-container * {
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#toast-container > div {
|
||||
position: relative;
|
||||
pointer-events: auto;
|
||||
overflow: hidden;
|
||||
margin: 0 0 6px;
|
||||
padding: 15px 15px 15px 50px;
|
||||
width: 300px;
|
||||
-moz-border-radius: 3px 3px 3px 3px;
|
||||
-webkit-border-radius: 3px 3px 3px 3px;
|
||||
border-radius: 3px 3px 3px 3px;
|
||||
background-position: 15px center;
|
||||
background-repeat: no-repeat;
|
||||
-moz-box-shadow: 0 0 12px #999999;
|
||||
-webkit-box-shadow: 0 0 12px #999999;
|
||||
box-shadow: 0 0 12px #999999;
|
||||
color: #FFFFFF;
|
||||
opacity: 0.8;
|
||||
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80);
|
||||
filter: alpha(opacity=80);
|
||||
}
|
||||
#toast-container > div.rtl {
|
||||
direction: rtl;
|
||||
padding: 15px 50px 15px 15px;
|
||||
background-position: right 15px center;
|
||||
}
|
||||
#toast-container > div:hover {
|
||||
-moz-box-shadow: 0 0 12px #000000;
|
||||
-webkit-box-shadow: 0 0 12px #000000;
|
||||
box-shadow: 0 0 12px #000000;
|
||||
opacity: 1;
|
||||
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
|
||||
filter: alpha(opacity=100);
|
||||
cursor: pointer;
|
||||
}
|
||||
#toast-container > .toast-info {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=") !important;
|
||||
}
|
||||
#toast-container > .toast-error {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=") !important;
|
||||
}
|
||||
#toast-container > .toast-success {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==") !important;
|
||||
}
|
||||
#toast-container > .toast-warning {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=") !important;
|
||||
}
|
||||
#toast-container.toast-top-center > div,
|
||||
#toast-container.toast-bottom-center > div {
|
||||
width: 300px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
#toast-container.toast-top-full-width > div,
|
||||
#toast-container.toast-bottom-full-width > div {
|
||||
width: 96%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.toast {
|
||||
background-color: #030303;
|
||||
}
|
||||
.toast-success {
|
||||
background-color: #51A351;
|
||||
}
|
||||
.toast-error {
|
||||
background-color: #BD362F;
|
||||
}
|
||||
.toast-info {
|
||||
background-color: #2F96B4;
|
||||
}
|
||||
.toast-warning {
|
||||
background-color: #F89406;
|
||||
}
|
||||
.toast-progress {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
height: 4px;
|
||||
background-color: #000000;
|
||||
opacity: 0.4;
|
||||
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=40);
|
||||
filter: alpha(opacity=40);
|
||||
}
|
||||
/*Responsive Design*/
|
||||
@media all and (max-width: 240px) {
|
||||
#toast-container > div {
|
||||
padding: 8px 8px 8px 50px;
|
||||
width: 11em;
|
||||
}
|
||||
#toast-container > div.rtl {
|
||||
padding: 8px 50px 8px 8px;
|
||||
}
|
||||
#toast-container .toast-close-button {
|
||||
right: -0.2em;
|
||||
top: -0.2em;
|
||||
}
|
||||
#toast-container .rtl .toast-close-button {
|
||||
left: -0.2em;
|
||||
right: 0.2em;
|
||||
}
|
||||
}
|
||||
@media all and (min-width: 241px) and (max-width: 480px) {
|
||||
#toast-container > div {
|
||||
padding: 8px 8px 8px 50px;
|
||||
width: 18em;
|
||||
}
|
||||
#toast-container > div.rtl {
|
||||
padding: 8px 50px 8px 8px;
|
||||
}
|
||||
#toast-container .toast-close-button {
|
||||
right: -0.2em;
|
||||
top: -0.2em;
|
||||
}
|
||||
#toast-container .rtl .toast-close-button {
|
||||
left: -0.2em;
|
||||
right: 0.2em;
|
||||
}
|
||||
}
|
||||
@media all and (min-width: 481px) and (max-width: 768px) {
|
||||
#toast-container > div {
|
||||
padding: 15px 15px 15px 50px;
|
||||
width: 25em;
|
||||
}
|
||||
#toast-container > div.rtl {
|
||||
padding: 15px 50px 15px 15px;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
/**
|
||||
* Copyright (c) 2014 The xterm.js authors. All rights reserved.
|
||||
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
||||
* https://github.com/chjj/term.js
|
||||
* @license MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* Originally forked from (with the author's permission):
|
||||
* Fabrice Bellard's javascript vt100 for jslinux:
|
||||
* http://bellard.org/jslinux/
|
||||
* Copyright (c) 2011 Fabrice Bellard
|
||||
* The original design remains. The terminal itself
|
||||
* has been extended to include xterm CSI codes, among
|
||||
* other features.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default styles for xterm.js
|
||||
*/
|
||||
|
||||
.xterm {
|
||||
position: relative;
|
||||
user-select: none;
|
||||
-ms-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.xterm.focus,
|
||||
.xterm:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.xterm .xterm-helpers {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
/**
|
||||
* The z-index of the helpers must be higher than the canvases in order for
|
||||
* IMEs to appear on top.
|
||||
*/
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.xterm .xterm-helper-textarea {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
/* Move textarea out of the screen to the far left, so that the cursor is not visible */
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
left: -9999em;
|
||||
top: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
z-index: -5;
|
||||
/** Prevent wrapping so the IME appears against the textarea at the correct position */
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.xterm .composition-view {
|
||||
/* TODO: Composition position got messed up somewhere */
|
||||
background: #000;
|
||||
color: #FFF;
|
||||
display: none;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.xterm .composition-view.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.xterm .xterm-viewport {
|
||||
/* On OS X this is required in order for the scroll bar to appear fully opaque */
|
||||
background-color: #000;
|
||||
overflow-y: scroll;
|
||||
cursor: default;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.xterm .xterm-screen {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.xterm .xterm-screen canvas {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.xterm .xterm-scroll-area {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.xterm-char-measure-element {
|
||||
display: inline-block;
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -9999em;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.xterm {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.xterm.enable-mouse-events {
|
||||
/* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.xterm.xterm-cursor-pointer,
|
||||
.xterm .xterm-cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.xterm.column-select.focus {
|
||||
/* Column selection mode */
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.xterm .xterm-accessibility,
|
||||
.xterm .xterm-message {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.xterm .live-region {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.xterm-dim {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.xterm-underline {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.xterm-strikethrough {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.xterm-screen .xterm-decoration-container .xterm-decoration {
|
||||
z-index: 6;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.xterm-decoration-overview-ruler {
|
||||
z-index: 7;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 337 B |
|
After Width: | Height: | Size: 881 KiB |
|
After Width: | Height: | Size: 881 KiB |
|
After Width: | Height: | Size: 866 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 589 B |
|
After Width: | Height: | Size: 618 B |
|
After Width: | Height: | Size: 587 B |
|
After Width: | Height: | Size: 596 B |
|
After Width: | Height: | Size: 677 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 534 B |
|
After Width: | Height: | Size: 646 B |
|
After Width: | Height: | Size: 994 B |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1019 B |
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright 2022, Digi International Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
class CCIMX8MNANO extends ConnectCoreDevice {
|
||||
|
||||
// Public constants.
|
||||
static DEVICE_TYPE = "ccimx8mn-dvk";
|
||||
static PLATFORM_NAME = "ConnectCore 8M-Nano DVK";
|
||||
|
||||
// Variables.
|
||||
BOARD_IMAGE = "ccimx8m-nano_board.png";
|
||||
BOARD_IMAGE_SCALE = 85;
|
||||
|
||||
CPU_COMPONENT_VISIBLE = true;
|
||||
CPU_COMPONENT_HAS_PANEL = true;
|
||||
CPU_COMPONENT_HAS_ARROW = true;
|
||||
CPU_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
CPU_COMPONENT_PANEL_ORIENTATION = VALUE_TOP;
|
||||
CPU_COMPONENT_PANEL_HORIZONTAL_PERCENT = 28.5;
|
||||
CPU_COMPONENT_PANEL_VERTICAL_PERCENT = 57;
|
||||
CPU_COMPONENT_ARROW_PERCENT = 33.5;
|
||||
CPU_COMPONENT_AREA_TOP_PERCENT = 45.7;
|
||||
CPU_COMPONENT_AREA_LEFT_PERCENT = 32.4;
|
||||
CPU_COMPONENT_AREA_WIDTH_PERCENT = 5.7;
|
||||
CPU_COMPONENT_AREA_HEIGHT_PERCENT = 9;
|
||||
|
||||
MEMORY_COMPONENT_VISIBLE = true;
|
||||
MEMORY_COMPONENT_HAS_PANEL = true;
|
||||
MEMORY_COMPONENT_HAS_ARROW = true;
|
||||
MEMORY_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
MEMORY_COMPONENT_PANEL_ORIENTATION = VALUE_BOTTOM;
|
||||
MEMORY_COMPONENT_PANEL_HORIZONTAL_PERCENT = 22;
|
||||
MEMORY_COMPONENT_PANEL_VERTICAL_PERCENT = 65;
|
||||
MEMORY_COMPONENT_ARROW_PERCENT = 33.7;
|
||||
MEMORY_COMPONENT_AREA_TOP_PERCENT = 37;
|
||||
MEMORY_COMPONENT_AREA_LEFT_PERCENT = 32.4;
|
||||
MEMORY_COMPONENT_AREA_WIDTH_PERCENT = 6;
|
||||
MEMORY_COMPONENT_AREA_HEIGHT_PERCENT = 7;
|
||||
|
||||
WIFI_BT_COMPONENT_VISIBLE = true;
|
||||
WIFI_BT_COMPONENT_HAS_PANEL = true;
|
||||
WIFI_BT_COMPONENT_HAS_ARROW = true;
|
||||
WIFI_BT_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
WIFI_BT_COMPONENT_PANEL_ORIENTATION = VALUE_RIGHT;
|
||||
WIFI_BT_COMPONENT_PANEL_HORIZONTAL_PERCENT = 73;
|
||||
WIFI_BT_COMPONENT_PANEL_VERTICAL_PERCENT = 38;
|
||||
WIFI_BT_COMPONENT_ARROW_PERCENT = 46;
|
||||
WIFI_BT_COMPONENT_AREA_TOP_PERCENT = 46.5;
|
||||
WIFI_BT_COMPONENT_AREA_LEFT_PERCENT = 28.3;
|
||||
WIFI_BT_COMPONENT_AREA_WIDTH_PERCENT = 4;
|
||||
WIFI_BT_COMPONENT_AREA_HEIGHT_PERCENT = 6;
|
||||
|
||||
ETHERNET_COMPONENT_VISIBLE = true;
|
||||
ETHERNET_COMPONENT_HAS_PANEL = true;
|
||||
ETHERNET_COMPONENT_HAS_ARROW = true;
|
||||
ETHERNET_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
ETHERNET_COMPONENT_PANEL_ORIENTATION = VALUE_LEFT;
|
||||
ETHERNET_COMPONENT_PANEL_HORIZONTAL_PERCENT = 21;
|
||||
ETHERNET_COMPONENT_PANEL_VERTICAL_PERCENT = 70;
|
||||
ETHERNET_COMPONENT_ARROW_PERCENT = 78;
|
||||
ETHERNET_COMPONENT_AREA_TOP_PERCENT = 77;
|
||||
ETHERNET_COMPONENT_AREA_LEFT_PERCENT = 11;
|
||||
ETHERNET_COMPONENT_AREA_WIDTH_PERCENT = 8.5;
|
||||
ETHERNET_COMPONENT_AREA_HEIGHT_PERCENT = 16;
|
||||
|
||||
CONSOLE_COMPONENT_VISIBLE = true;
|
||||
CONSOLE_COMPONENT_HAS_PANEL = false;
|
||||
CONSOLE_COMPONENT_HAS_ARROW = false;
|
||||
CONSOLE_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
CONSOLE_COMPONENT_AREA_TOP_PERCENT = 6.5;
|
||||
CONSOLE_COMPONENT_AREA_LEFT_PERCENT = 26.5;
|
||||
CONSOLE_COMPONENT_AREA_WIDTH_PERCENT = 3.4;
|
||||
CONSOLE_COMPONENT_AREA_HEIGHT_PERCENT = 4.5;
|
||||
|
||||
VIDEO_COMPONENT_VISIBLE = true;
|
||||
VIDEO_COMPONENT_HAS_PANEL = true;
|
||||
VIDEO_COMPONENT_HAS_ARROW = true;
|
||||
VIDEO_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
VIDEO_COMPONENT_PANEL_ORIENTATION = VALUE_BOTTOM;
|
||||
VIDEO_COMPONENT_PANEL_HORIZONTAL_PERCENT = 47.5;
|
||||
VIDEO_COMPONENT_PANEL_VERTICAL_PERCENT = 18;
|
||||
VIDEO_COMPONENT_ARROW_PERCENT = 58.5;
|
||||
VIDEO_COMPONENT_AREA_TOP_PERCENT = 84;
|
||||
VIDEO_COMPONENT_AREA_LEFT_PERCENT = 57;
|
||||
VIDEO_COMPONENT_AREA_WIDTH_PERCENT = 6;
|
||||
VIDEO_COMPONENT_AREA_HEIGHT_PERCENT = 8;
|
||||
|
||||
AUDIO_COMPONENT_VISIBLE = false;
|
||||
AUDIO_COMPONENT_HAS_PANEL = true;
|
||||
AUDIO_COMPONENT_HAS_ARROW = true;
|
||||
AUDIO_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
AUDIO_COMPONENT_PANEL_ORIENTATION = VALUE_TOP;
|
||||
AUDIO_COMPONENT_PANEL_HORIZONTAL_PERCENT = 1;
|
||||
AUDIO_COMPONENT_PANEL_VERTICAL_PERCENT = 19;
|
||||
AUDIO_COMPONENT_ARROW_PERCENT = 16;
|
||||
AUDIO_COMPONENT_AREA_TOP_PERCENT = 7;
|
||||
AUDIO_COMPONENT_AREA_LEFT_PERCENT = 14.5;
|
||||
AUDIO_COMPONENT_AREA_WIDTH_PERCENT = 5.4;
|
||||
AUDIO_COMPONENT_AREA_HEIGHT_PERCENT = 11;
|
||||
|
||||
LED_COMPONENT_VISIBLE = true;
|
||||
LED_COMPONENT_HAS_PANEL = true;
|
||||
LED_COMPONENT_HAS_ARROW = false;
|
||||
LED_COMPONENT_PANEL_ALWAYS_VISIBLE = true;
|
||||
LED_COMPONENT_PANEL_ORIENTATION = VALUE_TOP;
|
||||
LED_COMPONENT_PANEL_HORIZONTAL_PERCENT = 61;
|
||||
LED_COMPONENT_PANEL_VERTICAL_PERCENT = 14;
|
||||
LED_COMPONENT_AREA_TOP_PERCENT = 8.5;
|
||||
LED_COMPONENT_AREA_LEFT_PERCENT = 62.6;
|
||||
LED_COMPONENT_AREA_WIDTH_PERCENT = 1.5;
|
||||
LED_COMPONENT_AREA_HEIGHT_PERCENT = 4;
|
||||
|
||||
FLASH_MEMORY_COMPONENT_VISIBLE = true;
|
||||
FLASH_MEMORY_COMPONENT_HAS_PANEL = true;
|
||||
FLASH_MEMORY_COMPONENT_HAS_ARROW = true;
|
||||
FLASH_MEMORY_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
FLASH_MEMORY_COMPONENT_PANEL_ORIENTATION = VALUE_LEFT;
|
||||
FLASH_MEMORY_COMPONENT_PANEL_HORIZONTAL_PERCENT = 45;
|
||||
FLASH_MEMORY_COMPONENT_PANEL_VERTICAL_PERCENT = 36;
|
||||
FLASH_MEMORY_COMPONENT_ARROW_PERCENT = 38;
|
||||
FLASH_MEMORY_COMPONENT_AREA_TOP_PERCENT = 36.3;
|
||||
FLASH_MEMORY_COMPONENT_AREA_LEFT_PERCENT = 38.8;
|
||||
FLASH_MEMORY_COMPONENT_AREA_WIDTH_PERCENT = 5.4;
|
||||
FLASH_MEMORY_COMPONENT_AREA_HEIGHT_PERCENT = 8;
|
||||
|
||||
|
||||
// Capabilities
|
||||
SUPPORTS_VIDEO_BRIGHTNESS = false;
|
||||
|
||||
// Constructor.
|
||||
constructor(deviceID, deviceData) {
|
||||
super(CCIMX8MNANO.DEVICE_TYPE, CCIMX8MNANO.PLATFORM_NAME, deviceID, deviceData);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright 2022, Digi International Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
class CCIMX8MMINI extends ConnectCoreDevice {
|
||||
|
||||
// Public constants.
|
||||
static DEVICE_TYPE = "ccimx8mm-dvk";
|
||||
static PLATFORM_NAME = "ConnectCore 8M-Mini DVK";
|
||||
|
||||
// Variables.
|
||||
BOARD_IMAGE = "ccimx8m-mini_board.png";
|
||||
BOARD_IMAGE_SCALE = 85;
|
||||
|
||||
CPU_COMPONENT_VISIBLE = true;
|
||||
CPU_COMPONENT_HAS_PANEL = true;
|
||||
CPU_COMPONENT_HAS_ARROW = true;
|
||||
CPU_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
CPU_COMPONENT_PANEL_ORIENTATION = VALUE_TOP;
|
||||
CPU_COMPONENT_PANEL_HORIZONTAL_PERCENT = 28.5;
|
||||
CPU_COMPONENT_PANEL_VERTICAL_PERCENT = 57;
|
||||
CPU_COMPONENT_ARROW_PERCENT = 33.5;
|
||||
CPU_COMPONENT_AREA_TOP_PERCENT = 45.7;
|
||||
CPU_COMPONENT_AREA_LEFT_PERCENT = 32.4;
|
||||
CPU_COMPONENT_AREA_WIDTH_PERCENT = 5.7;
|
||||
CPU_COMPONENT_AREA_HEIGHT_PERCENT = 9;
|
||||
|
||||
MEMORY_COMPONENT_VISIBLE = true;
|
||||
MEMORY_COMPONENT_HAS_PANEL = true;
|
||||
MEMORY_COMPONENT_HAS_ARROW = true;
|
||||
MEMORY_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
MEMORY_COMPONENT_PANEL_ORIENTATION = VALUE_BOTTOM;
|
||||
MEMORY_COMPONENT_PANEL_HORIZONTAL_PERCENT = 22;
|
||||
MEMORY_COMPONENT_PANEL_VERTICAL_PERCENT = 65;
|
||||
MEMORY_COMPONENT_ARROW_PERCENT = 33.7;
|
||||
MEMORY_COMPONENT_AREA_TOP_PERCENT = 37;
|
||||
MEMORY_COMPONENT_AREA_LEFT_PERCENT = 32.4;
|
||||
MEMORY_COMPONENT_AREA_WIDTH_PERCENT = 6;
|
||||
MEMORY_COMPONENT_AREA_HEIGHT_PERCENT = 7;
|
||||
|
||||
WIFI_BT_COMPONENT_VISIBLE = true;
|
||||
WIFI_BT_COMPONENT_HAS_PANEL = true;
|
||||
WIFI_BT_COMPONENT_HAS_ARROW = true;
|
||||
WIFI_BT_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
WIFI_BT_COMPONENT_PANEL_ORIENTATION = VALUE_RIGHT;
|
||||
WIFI_BT_COMPONENT_PANEL_HORIZONTAL_PERCENT = 73;
|
||||
WIFI_BT_COMPONENT_PANEL_VERTICAL_PERCENT = 38;
|
||||
WIFI_BT_COMPONENT_ARROW_PERCENT = 46;
|
||||
WIFI_BT_COMPONENT_AREA_TOP_PERCENT = 46.5;
|
||||
WIFI_BT_COMPONENT_AREA_LEFT_PERCENT = 28.3;
|
||||
WIFI_BT_COMPONENT_AREA_WIDTH_PERCENT = 4;
|
||||
WIFI_BT_COMPONENT_AREA_HEIGHT_PERCENT = 6;
|
||||
|
||||
ETHERNET_COMPONENT_VISIBLE = true;
|
||||
ETHERNET_COMPONENT_HAS_PANEL = true;
|
||||
ETHERNET_COMPONENT_HAS_ARROW = true;
|
||||
ETHERNET_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
ETHERNET_COMPONENT_PANEL_ORIENTATION = VALUE_LEFT;
|
||||
ETHERNET_COMPONENT_PANEL_HORIZONTAL_PERCENT = 21;
|
||||
ETHERNET_COMPONENT_PANEL_VERTICAL_PERCENT = 70;
|
||||
ETHERNET_COMPONENT_ARROW_PERCENT = 78;
|
||||
ETHERNET_COMPONENT_AREA_TOP_PERCENT = 77;
|
||||
ETHERNET_COMPONENT_AREA_LEFT_PERCENT = 11;
|
||||
ETHERNET_COMPONENT_AREA_WIDTH_PERCENT = 8.5;
|
||||
ETHERNET_COMPONENT_AREA_HEIGHT_PERCENT = 16;
|
||||
|
||||
CONSOLE_COMPONENT_VISIBLE = true;
|
||||
CONSOLE_COMPONENT_HAS_PANEL = false;
|
||||
CONSOLE_COMPONENT_HAS_ARROW = false;
|
||||
CONSOLE_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
CONSOLE_COMPONENT_AREA_TOP_PERCENT = 6.5;
|
||||
CONSOLE_COMPONENT_AREA_LEFT_PERCENT = 26.5;
|
||||
CONSOLE_COMPONENT_AREA_WIDTH_PERCENT = 3.4;
|
||||
CONSOLE_COMPONENT_AREA_HEIGHT_PERCENT = 4.5;
|
||||
|
||||
VIDEO_COMPONENT_VISIBLE = true;
|
||||
VIDEO_COMPONENT_HAS_PANEL = true;
|
||||
VIDEO_COMPONENT_HAS_ARROW = true;
|
||||
VIDEO_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
VIDEO_COMPONENT_PANEL_ORIENTATION = VALUE_BOTTOM;
|
||||
VIDEO_COMPONENT_PANEL_HORIZONTAL_PERCENT = 47.5;
|
||||
VIDEO_COMPONENT_PANEL_VERTICAL_PERCENT = 18;
|
||||
VIDEO_COMPONENT_ARROW_PERCENT = 58.5;
|
||||
VIDEO_COMPONENT_AREA_TOP_PERCENT = 84;
|
||||
VIDEO_COMPONENT_AREA_LEFT_PERCENT = 57;
|
||||
VIDEO_COMPONENT_AREA_WIDTH_PERCENT = 6;
|
||||
VIDEO_COMPONENT_AREA_HEIGHT_PERCENT = 8;
|
||||
|
||||
AUDIO_COMPONENT_VISIBLE = false;
|
||||
AUDIO_COMPONENT_HAS_PANEL = true;
|
||||
AUDIO_COMPONENT_HAS_ARROW = true;
|
||||
AUDIO_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
AUDIO_COMPONENT_PANEL_ORIENTATION = VALUE_TOP;
|
||||
AUDIO_COMPONENT_PANEL_HORIZONTAL_PERCENT = 1;
|
||||
AUDIO_COMPONENT_PANEL_VERTICAL_PERCENT = 19;
|
||||
AUDIO_COMPONENT_ARROW_PERCENT = 16;
|
||||
AUDIO_COMPONENT_AREA_TOP_PERCENT = 7;
|
||||
AUDIO_COMPONENT_AREA_LEFT_PERCENT = 14.5;
|
||||
AUDIO_COMPONENT_AREA_WIDTH_PERCENT = 5.4;
|
||||
AUDIO_COMPONENT_AREA_HEIGHT_PERCENT = 11;
|
||||
|
||||
LED_COMPONENT_VISIBLE = true;
|
||||
LED_COMPONENT_HAS_PANEL = true;
|
||||
LED_COMPONENT_HAS_ARROW = false;
|
||||
LED_COMPONENT_PANEL_ALWAYS_VISIBLE = true;
|
||||
LED_COMPONENT_PANEL_ORIENTATION = VALUE_TOP;
|
||||
LED_COMPONENT_PANEL_HORIZONTAL_PERCENT = 61;
|
||||
LED_COMPONENT_PANEL_VERTICAL_PERCENT = 14;
|
||||
LED_COMPONENT_AREA_TOP_PERCENT = 8.5;
|
||||
LED_COMPONENT_AREA_LEFT_PERCENT = 62.6;
|
||||
LED_COMPONENT_AREA_WIDTH_PERCENT = 1.5;
|
||||
LED_COMPONENT_AREA_HEIGHT_PERCENT = 4;
|
||||
|
||||
FLASH_MEMORY_COMPONENT_VISIBLE = true;
|
||||
FLASH_MEMORY_COMPONENT_HAS_PANEL = true;
|
||||
FLASH_MEMORY_COMPONENT_HAS_ARROW = true;
|
||||
FLASH_MEMORY_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
FLASH_MEMORY_COMPONENT_PANEL_ORIENTATION = VALUE_LEFT;
|
||||
FLASH_MEMORY_COMPONENT_PANEL_HORIZONTAL_PERCENT = 45;
|
||||
FLASH_MEMORY_COMPONENT_PANEL_VERTICAL_PERCENT = 36;
|
||||
FLASH_MEMORY_COMPONENT_ARROW_PERCENT = 38;
|
||||
FLASH_MEMORY_COMPONENT_AREA_TOP_PERCENT = 36.3;
|
||||
FLASH_MEMORY_COMPONENT_AREA_LEFT_PERCENT = 38.8;
|
||||
FLASH_MEMORY_COMPONENT_AREA_WIDTH_PERCENT = 5.4;
|
||||
FLASH_MEMORY_COMPONENT_AREA_HEIGHT_PERCENT = 8;
|
||||
|
||||
|
||||
// Capabilities
|
||||
SUPPORTS_VIDEO_BRIGHTNESS = false;
|
||||
|
||||
// Constructor.
|
||||
constructor(deviceID, deviceData) {
|
||||
super(CCIMX8MMINI.DEVICE_TYPE, CCIMX8MMINI.PLATFORM_NAME, deviceID, deviceData);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright 2022, Digi International Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
class CCIMX8X extends ConnectCoreDevice {
|
||||
|
||||
// Public constants.
|
||||
static DEVICE_TYPE = "ccimx8x-sbc-pro";
|
||||
static PLATFORM_NAME = "ConnectCore 8X SBC Pro";
|
||||
|
||||
// Variables.
|
||||
BOARD_IMAGE = "ccimx8x-sbc-pro_board.png";
|
||||
BOARD_IMAGE_SCALE = 60;
|
||||
|
||||
CPU_COMPONENT_VISIBLE = true;
|
||||
CPU_COMPONENT_HAS_PANEL = true;
|
||||
CPU_COMPONENT_HAS_ARROW = true;
|
||||
CPU_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
CPU_COMPONENT_PANEL_ORIENTATION = VALUE_TOP;
|
||||
CPU_COMPONENT_PANEL_HORIZONTAL_PERCENT = 49;
|
||||
CPU_COMPONENT_PANEL_VERTICAL_PERCENT = 65;
|
||||
CPU_COMPONENT_ARROW_PERCENT = 62;
|
||||
CPU_COMPONENT_AREA_TOP_PERCENT = 40;
|
||||
CPU_COMPONENT_AREA_LEFT_PERCENT = 54;
|
||||
CPU_COMPONENT_AREA_WIDTH_PERCENT = 19.5;
|
||||
CPU_COMPONENT_AREA_HEIGHT_PERCENT = 24;
|
||||
|
||||
MEMORY_COMPONENT_VISIBLE = true;
|
||||
MEMORY_COMPONENT_HAS_PANEL = true;
|
||||
MEMORY_COMPONENT_HAS_ARROW = true;
|
||||
MEMORY_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
MEMORY_COMPONENT_PANEL_ORIENTATION = VALUE_RIGHT;
|
||||
MEMORY_COMPONENT_PANEL_HORIZONTAL_PERCENT = 43;
|
||||
MEMORY_COMPONENT_PANEL_VERTICAL_PERCENT = 22.5;
|
||||
MEMORY_COMPONENT_ARROW_PERCENT = 29;
|
||||
MEMORY_COMPONENT_AREA_TOP_PERCENT = 22.25;
|
||||
MEMORY_COMPONENT_AREA_LEFT_PERCENT = 57.75;
|
||||
MEMORY_COMPONENT_AREA_WIDTH_PERCENT = 9.5;
|
||||
MEMORY_COMPONENT_AREA_HEIGHT_PERCENT = 16.5;
|
||||
|
||||
WIFI_BT_COMPONENT_VISIBLE = true;
|
||||
WIFI_BT_COMPONENT_HAS_PANEL = true;
|
||||
WIFI_BT_COMPONENT_HAS_ARROW = true;
|
||||
WIFI_BT_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
WIFI_BT_COMPONENT_PANEL_ORIENTATION = VALUE_LEFT;
|
||||
WIFI_BT_COMPONENT_PANEL_HORIZONTAL_PERCENT = 83;
|
||||
WIFI_BT_COMPONENT_PANEL_VERTICAL_PERCENT = 35;
|
||||
WIFI_BT_COMPONENT_ARROW_PERCENT = 41;
|
||||
WIFI_BT_COMPONENT_AREA_TOP_PERCENT = 38;
|
||||
WIFI_BT_COMPONENT_AREA_LEFT_PERCENT = 73.5;
|
||||
WIFI_BT_COMPONENT_AREA_WIDTH_PERCENT = 8.5;
|
||||
WIFI_BT_COMPONENT_AREA_HEIGHT_PERCENT = 10;
|
||||
|
||||
ETHERNET_COMPONENT_VISIBLE = true;
|
||||
ETHERNET_COMPONENT_HAS_PANEL = true;
|
||||
ETHERNET_COMPONENT_HAS_ARROW = true;
|
||||
ETHERNET_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
ETHERNET_COMPONENT_PANEL_ORIENTATION = VALUE_BOTTOM;
|
||||
ETHERNET_COMPONENT_PANEL_HORIZONTAL_PERCENT = -7;
|
||||
ETHERNET_COMPONENT_PANEL_VERTICAL_PERCENT = 34;
|
||||
ETHERNET_COMPONENT_ARROW_PERCENT = 18;
|
||||
ETHERNET_COMPONENT_AREA_TOP_PERCENT = 68;
|
||||
ETHERNET_COMPONENT_AREA_LEFT_PERCENT = 12;
|
||||
ETHERNET_COMPONENT_AREA_WIDTH_PERCENT = 17;
|
||||
ETHERNET_COMPONENT_AREA_HEIGHT_PERCENT = 27;
|
||||
|
||||
CONSOLE_COMPONENT_VISIBLE = true;
|
||||
CONSOLE_COMPONENT_HAS_PANEL = false;
|
||||
CONSOLE_COMPONENT_HAS_ARROW = false;
|
||||
CONSOLE_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
CONSOLE_COMPONENT_AREA_TOP_PERCENT = 22.5;
|
||||
CONSOLE_COMPONENT_AREA_LEFT_PERCENT = 86;
|
||||
CONSOLE_COMPONENT_AREA_WIDTH_PERCENT = 7;
|
||||
CONSOLE_COMPONENT_AREA_HEIGHT_PERCENT = 4.5;
|
||||
|
||||
VIDEO_COMPONENT_VISIBLE = true;
|
||||
VIDEO_COMPONENT_HAS_PANEL = true;
|
||||
VIDEO_COMPONENT_HAS_ARROW = true;
|
||||
VIDEO_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
VIDEO_COMPONENT_PANEL_ORIENTATION = VALUE_TOP;
|
||||
VIDEO_COMPONENT_PANEL_HORIZONTAL_PERCENT = -15;
|
||||
VIDEO_COMPONENT_PANEL_VERTICAL_PERCENT = 18;
|
||||
VIDEO_COMPONENT_ARROW_PERCENT = 20.75;
|
||||
VIDEO_COMPONENT_AREA_TOP_PERCENT = 8.5;
|
||||
VIDEO_COMPONENT_AREA_LEFT_PERCENT = 8.75;
|
||||
VIDEO_COMPONENT_AREA_WIDTH_PERCENT = 27.5;
|
||||
VIDEO_COMPONENT_AREA_HEIGHT_PERCENT = 7.5;
|
||||
|
||||
AUDIO_COMPONENT_VISIBLE = false;
|
||||
AUDIO_COMPONENT_HAS_PANEL = true;
|
||||
AUDIO_COMPONENT_HAS_ARROW = true;
|
||||
AUDIO_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
AUDIO_COMPONENT_PANEL_ORIENTATION = VALUE_TOP;
|
||||
AUDIO_COMPONENT_PANEL_HORIZONTAL_PERCENT = 1;
|
||||
AUDIO_COMPONENT_PANEL_VERTICAL_PERCENT = 19;
|
||||
AUDIO_COMPONENT_ARROW_PERCENT = 16;
|
||||
AUDIO_COMPONENT_AREA_TOP_PERCENT = 7;
|
||||
AUDIO_COMPONENT_AREA_LEFT_PERCENT = 14.5;
|
||||
AUDIO_COMPONENT_AREA_WIDTH_PERCENT = 5.4;
|
||||
AUDIO_COMPONENT_AREA_HEIGHT_PERCENT = 11;
|
||||
|
||||
LED_COMPONENT_VISIBLE = true;
|
||||
LED_COMPONENT_HAS_PANEL = true;
|
||||
LED_COMPONENT_HAS_ARROW = false;
|
||||
LED_COMPONENT_PANEL_ALWAYS_VISIBLE = true;
|
||||
LED_COMPONENT_PANEL_ORIENTATION = VALUE_LEFT;
|
||||
LED_COMPONENT_PANEL_HORIZONTAL_PERCENT = 30;
|
||||
LED_COMPONENT_PANEL_VERTICAL_PERCENT = 52;
|
||||
LED_COMPONENT_AREA_TOP_PERCENT = 53.5;
|
||||
LED_COMPONENT_AREA_LEFT_PERCENT = 27;
|
||||
LED_COMPONENT_AREA_WIDTH_PERCENT = 2.5;
|
||||
LED_COMPONENT_AREA_HEIGHT_PERCENT = 5;
|
||||
|
||||
FLASH_MEMORY_COMPONENT_VISIBLE = true;
|
||||
FLASH_MEMORY_COMPONENT_HAS_PANEL = true;
|
||||
FLASH_MEMORY_COMPONENT_HAS_ARROW = true;
|
||||
FLASH_MEMORY_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
FLASH_MEMORY_COMPONENT_PANEL_ORIENTATION = VALUE_BOTTOM;
|
||||
FLASH_MEMORY_COMPONENT_PANEL_HORIZONTAL_PERCENT = 57.5;
|
||||
FLASH_MEMORY_COMPONENT_PANEL_VERTICAL_PERCENT = 79.5;
|
||||
FLASH_MEMORY_COMPONENT_ARROW_PERCENT = 71.5;
|
||||
FLASH_MEMORY_COMPONENT_AREA_TOP_PERCENT = 22.25;
|
||||
FLASH_MEMORY_COMPONENT_AREA_LEFT_PERCENT = 68;
|
||||
FLASH_MEMORY_COMPONENT_AREA_WIDTH_PERCENT = 10.8;
|
||||
FLASH_MEMORY_COMPONENT_AREA_HEIGHT_PERCENT = 15;
|
||||
|
||||
|
||||
// Capabilities
|
||||
SUPPORTS_VIDEO_BRIGHTNESS = false;
|
||||
|
||||
// Constructor.
|
||||
constructor(deviceID, deviceData) {
|
||||
super(CCIMX8X.DEVICE_TYPE, CCIMX8X.PLATFORM_NAME, deviceID, deviceData);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,455 @@
|
|||
/*
|
||||
* Copyright 2022, Digi International Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
// Constants.
|
||||
const ID_AREA = "area";
|
||||
const ID_AREA_HEIGHT = "area-height";
|
||||
const ID_AREA_LEFT_MARGIN = "area-left-margin";
|
||||
const ID_AREA_TOP_MARGIN = "area-top-margin";
|
||||
const ID_AREA_WIDTH = "area-width";
|
||||
const ID_ARROW = "arrow";
|
||||
const ID_ARROW_MARGIN = "arrow-margin";
|
||||
const ID_AUDIO = "audio";
|
||||
const ID_BLUETOOTH_MAC = "bluetooth_mac";
|
||||
const ID_BOARD_ID = "board_id";
|
||||
const ID_BOARD_VARIANT = "board_variant";
|
||||
const ID_BT_READ_DATA = "bluetooth_received_data";
|
||||
const ID_BT_SENT_DATA = "bluetooth_sent_data";
|
||||
const ID_BT_STATE = "bluetooth_state";
|
||||
const ID_CONSOLE = "console";
|
||||
const ID_CONFIRM_DIALOG = "confirm_dialog";
|
||||
const ID_CONFIRM_DIALOG_MESSAGE = "confirm_dialog_message";
|
||||
const ID_CONFIRM_DIALOG_NO_BUTTON = "confirm_dialog_no_button";
|
||||
const ID_CONFIRM_DIALOG_TITLE = "confirm_dialog_title";
|
||||
const ID_CONFIRM_DIALOG_YES_BUTTON = "confirm_dialog_yes_button";
|
||||
const ID_CPU = "cpu";
|
||||
const ID_CPU_FREQUENCY = "cpu_frequency";
|
||||
const ID_CPU_LOAD = "cpu_load";
|
||||
const ID_CPU_TEMPERATURE = "cpu_temperature";
|
||||
const ID_CPU_UPTIME = "cpu_uptime";
|
||||
const ID_CURRENT_DIR = "current_dir";
|
||||
const ID_DATA = "data";
|
||||
const ID_DATA_USAGE_DEVICES = "data_usage_devices";
|
||||
const ID_DATA_USAGE_MONITORS = "data_usage_monitors";
|
||||
const ID_DATA_USAGE_TOTAL = "data_usage_total";
|
||||
const ID_DATA_USAGE_WEB = "data_usage_web";
|
||||
const ID_DATA_USAGE_WEB_SERVICES = "data_usage_web_services";
|
||||
const ID_DEVICE_ID = "device_id";
|
||||
const ID_DEVICE_NAME = "device_name";
|
||||
const ID_DEVICE_TYPE = "device_type";
|
||||
const ID_DEVICES = "devices";
|
||||
const ID_DEY_VERSION = "dey_version";
|
||||
const ID_ERROR = "error";
|
||||
const ID_ERROR_GUIDE = "error_guide";
|
||||
const ID_ERROR_MESSAGE = "error_msg";
|
||||
const ID_ERROR_TITLE = "error_title";
|
||||
const ID_ETHERNET = "ethernet";
|
||||
const ID_ETHERNET_IP = "ethernet_ip";
|
||||
const ID_ETHERNET_MAC = "ethernet_mac";
|
||||
const ID_ETHERNET_READ_DATA = "ethernet_received_data";
|
||||
const ID_ETHERNET_SENT_DATA = "ethernet_sent_data";
|
||||
const ID_ETHERNET_STATE = "ethernet_state";
|
||||
const ID_FILES = "files";
|
||||
const ID_FLASH_MEMORY = "flash_memory";
|
||||
const ID_FLASH_SIZE = "flash_size";
|
||||
const ID_HAS_ARROW = "has-arrow";
|
||||
const ID_HAS_PANEL = "has-panel";
|
||||
const ID_ICON = "icon";
|
||||
const ID_ID = "id";
|
||||
const ID_INFO_POPUP = "info_popup";
|
||||
const ID_INFO_POPUP_MESSAGE = "info_popup_message";
|
||||
const ID_INFO_POPUP_TITLE = "info_popup_title";
|
||||
const ID_KERNEL_VERSION = "kernel_version";
|
||||
const ID_LAST_MODIFIED = "last_modified";
|
||||
const ID_LED = "led";
|
||||
const ID_LOADING_POPUP = "loading_popup";
|
||||
const ID_LOADING_POPUP_MESSAGE = "loading_popup_message";
|
||||
const ID_LOADING_WRAPPER = "loading_wrapper";
|
||||
const ID_MCA_FW_VERSION = "mca_fw_version";
|
||||
const ID_MCA_HW_VERSION = "mca_hw_version";
|
||||
const ID_MEMORY = "memory";
|
||||
const ID_MEMORY_FREE = "memory_free";
|
||||
const ID_MEMORY_TOTAL = "memory_total";
|
||||
const ID_MEMORY_USED = "memory_used";
|
||||
const ID_MESSAGE = "message";
|
||||
const ID_MODULE_VARIANT = "module_variant";
|
||||
const ID_NAME = "name";
|
||||
const ID_NUM_SAMPLES_UPLOAD = "num_samples_upload";
|
||||
const ID_ONLINE = "online";
|
||||
const ID_PANEL = "panel";
|
||||
const ID_PANEL_ALWAYS_VISIBLE = "panel-always-visible";
|
||||
const ID_PANEL_HORIZONTAL_MARGIN = "panel-horizontal-margin";
|
||||
const ID_PANEL_ORIENTATION = "panel-orientation";
|
||||
const ID_PANEL_VERTICAL_MARGIN = "panel-vertical-margin";
|
||||
const ID_PATH = "path";
|
||||
const ID_PROGRESS = "progress";
|
||||
const ID_SAMPLE_RATE = "sample_rate";
|
||||
const ID_SERIAL_NUMBER = "serial_number";
|
||||
const ID_SESSION_ID = "session_id";
|
||||
const ID_SIZE = "size";
|
||||
const ID_STATUS = "status";
|
||||
const ID_STREAM = "stream";
|
||||
const ID_TIMESTAMP = "timestamp";
|
||||
const ID_TYPE = "type";
|
||||
const ID_UBOOT_VERSION = "uboot_version";
|
||||
const ID_USAGE = "usage";
|
||||
const ID_VALUE = "value";
|
||||
const ID_VIDEO = "video";
|
||||
const ID_VIDEO_RESOLUTION = "video_resolution";
|
||||
const ID_VISIBLE = "visible";
|
||||
const ID_WIFI_BT = "wifi-bt";
|
||||
const ID_WIFI_IP = "wifi_ip";
|
||||
const ID_WIFI_MAC = "wifi_mac";
|
||||
const ID_WIFI_READ_DATA = "wifi_received_data";
|
||||
const ID_WIFI_SENT_DATA = "wifi_sent_data";
|
||||
const ID_WIFI_STATE = "wifi_state";
|
||||
|
||||
const VALUE_ACTIVE = "active";
|
||||
const VALUE_ABORT = "abort";
|
||||
const VALUE_BOTTOM = "bottom";
|
||||
const VALUE_CANCELED = "canceled";
|
||||
const VALUE_FAILED = "failed";
|
||||
const VALUE_LEFT = "left";
|
||||
const VALUE_OFF = "Off";
|
||||
const VALUE_OFFLINE = "Offline";
|
||||
const VALUE_ON = "On";
|
||||
const VALUE_ONLINE = "Online";
|
||||
const VALUE_RIGHT = "right";
|
||||
const VALUE_SUCCESSFUL = "successful";
|
||||
const VALUE_TOP = "top";
|
||||
const VALUE_UNKNOWN = "unknown";
|
||||
|
||||
const CLASS_D_NONE = "d-none";
|
||||
const CLASS_DISABLED_DIV = "disabled-div";
|
||||
const CLASS_ELEMENT_GRAYED = "element-grayed";
|
||||
const CLASS_SELECTED = "selected";
|
||||
|
||||
const ERROR_ABORTED = "Operation aborted";
|
||||
const ERROR_URL_NOT_FOUND = "Requested URL not found";
|
||||
const ERROR_SERVER_ERROR = "Internal server error";
|
||||
const ERROR_TITLE = "Error";
|
||||
const ERROR_UNKNOWN_ERROR = "Unknown error";
|
||||
|
||||
const IMAGE_OFFLINE = "status_offline.png";
|
||||
const IMAGE_ONLINE = "status_online.png";
|
||||
|
||||
const PATH_IMAGES = "../static/images/";
|
||||
|
||||
String.prototype.format = function() {
|
||||
var formatted = this;
|
||||
for (var arg in arguments)
|
||||
formatted = formatted.replace("{" + arg + "}", arguments[arg]);
|
||||
return formatted;
|
||||
};
|
||||
|
||||
// Shows/hides a front popup over the given background element.
|
||||
function showPopup(backElementID, frontElementID, visible) {
|
||||
// Initialize variables.
|
||||
var backElement = document.getElementById(backElementID);
|
||||
var frontElement = document.getElementById(frontElementID);
|
||||
// Sanity checks.
|
||||
if (backElement == null || frontElement == null)
|
||||
return;
|
||||
// Show/Hide the popup.
|
||||
if (visible) {
|
||||
if (!backElement.classList.contains(CLASS_ELEMENT_GRAYED))
|
||||
backElement.classList.add(CLASS_ELEMENT_GRAYED);
|
||||
if (frontElement.classList.contains(CLASS_D_NONE))
|
||||
frontElement.classList.remove(CLASS_D_NONE);
|
||||
} else {
|
||||
if (backElement.classList.contains(CLASS_ELEMENT_GRAYED))
|
||||
backElement.classList.remove(CLASS_ELEMENT_GRAYED);
|
||||
if (!frontElement.classList.contains(CLASS_D_NONE))
|
||||
frontElement.classList.add(CLASS_D_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
// Redirects to the login page.
|
||||
function redirectToLogin() {
|
||||
var url = "/access/login?dest=" + window.location.pathname.replaceAll("/", "");
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
for (let param of params)
|
||||
url += "&" + param[0] + "=" + param[1];
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
// Processes the error response of the AJAX request.
|
||||
function processAjaxErrorResponse(response) {
|
||||
// Check common error codes.
|
||||
if (response.status == 401)
|
||||
redirectToLogin();
|
||||
var errorMessage = "";
|
||||
if (response.statusText == VALUE_ABORT)
|
||||
errorMessage = ERROR_ABORTED;
|
||||
else if (response.status == 400) {
|
||||
errorMessage = response.responseJSON[ID_ERROR];
|
||||
// Show the error message (if any).
|
||||
if (errorMessage == null)
|
||||
errorMessage = ERROR_UNKNOWN_ERROR;
|
||||
} else if (response.status == 404)
|
||||
errorMessage = ERROR_URL_NOT_FOUND;
|
||||
else if (response.status == 500)
|
||||
errorMessage = ERROR_SERVER_ERROR;
|
||||
else
|
||||
errorMessage = ERROR_UNKNOWN_ERROR;
|
||||
// Show toast with the error message.
|
||||
toastr.error(errorMessage);
|
||||
// Return the error message.
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
// Check if there is any error in the response.
|
||||
function checkErrorResponse(response, showErrorDialog) {
|
||||
if (response[ID_ERROR_MESSAGE] != null || response[ID_ERROR] != null) {
|
||||
// Process error.
|
||||
var errorTitle = ERROR_TITLE;
|
||||
var errorMessage = getErrorFromResponse(response);
|
||||
if (response[ID_ERROR_TITLE] != null)
|
||||
errorTitle = response[ID_ERROR_TITLE];
|
||||
// Show toast error.
|
||||
if (errorMessage != null && errorMessage != "")
|
||||
toastr.error(errorMessage);
|
||||
// Hide the loading panel.
|
||||
showLoadingPopup(false);
|
||||
// Show error dialog.
|
||||
if (showErrorDialog)
|
||||
showInfoPopup(true, errorTitle, errorMessage);
|
||||
// There was an error, return true.
|
||||
return true;
|
||||
}
|
||||
// No error found.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns the error message from the response.
|
||||
function getErrorFromResponse(response) {
|
||||
var error = "";
|
||||
if (response[ID_ERROR_MESSAGE] != null || response[ID_ERROR] != null) {
|
||||
if (response[ID_ERROR_MESSAGE] != null) {
|
||||
error = response[ID_ERROR_MESSAGE];
|
||||
if (response[ID_ERROR_GUIDE] != null)
|
||||
error += response[ID_ERROR_GUIDE];
|
||||
} else
|
||||
error = response[ID_ERROR];
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// Shows/hides the loading popup panel.
|
||||
function showLoadingPopup(visible, message=null) {
|
||||
// Set loading message only if it is not null and the popup will be visible.
|
||||
if (visible && message != null)
|
||||
document.getElementById(ID_LOADING_POPUP_MESSAGE).innerHTML = message;
|
||||
// Show/Hide the popup.
|
||||
showPopup(ID_LOADING_WRAPPER, ID_LOADING_POPUP, visible);
|
||||
}
|
||||
|
||||
// Shows/hides the info popup panel.
|
||||
function showInfoPopup(visible, tittle=null, message=null) {
|
||||
// Set title and message.
|
||||
if (visible && tittle != null && message != null) {
|
||||
// Initialize vars.
|
||||
var infoPopupTittleElement = document.getElementById(ID_INFO_POPUP_TITLE);
|
||||
var infoPopupMessageElement = document.getElementById(ID_INFO_POPUP_MESSAGE);
|
||||
// Set the info title.
|
||||
if (infoPopupTittleElement != null)
|
||||
infoPopupTittleElement.innerHTML = tittle;
|
||||
// Set the info message.
|
||||
if (infoPopupMessageElement != null)
|
||||
infoPopupMessageElement.innerHTML = message;
|
||||
}
|
||||
// Show/Hide the popup.
|
||||
showPopup(ID_LOADING_WRAPPER, ID_INFO_POPUP, visible);
|
||||
}
|
||||
|
||||
// Shows the confirm dialog.
|
||||
function showConfirmDialog(title, message, yesCallback, noCallback) {
|
||||
// Initialize variables.
|
||||
var confirmDialogTitleElement = document.getElementById(ID_CONFIRM_DIALOG_TITLE);
|
||||
var confirmDialogMessageElement = document.getElementById(ID_CONFIRM_DIALOG_MESSAGE);
|
||||
var confirmDialogYesButtonElement = document.getElementById(ID_CONFIRM_DIALOG_YES_BUTTON);
|
||||
var confirmDialogNoButtonElement = document.getElementById(ID_CONFIRM_DIALOG_NO_BUTTON);
|
||||
// Check if the elements are valid.
|
||||
if (confirmDialogTitleElement != null && confirmDialogMessageElement != null &&
|
||||
confirmDialogYesButtonElement != null && confirmDialogNoButtonElement != null) {
|
||||
// Set the dialog title.
|
||||
confirmDialogTitleElement.innerHTML = title;
|
||||
// Set the dialog message.
|
||||
confirmDialogMessageElement.innerHTML = message;
|
||||
// Show the dialog.
|
||||
showPopup(ID_LOADING_WRAPPER, ID_CONFIRM_DIALOG, true);
|
||||
// Set the yes button callback.
|
||||
confirmDialogYesButtonElement.onclick = function() {
|
||||
// Hide the dialog.
|
||||
showPopup(ID_LOADING_WRAPPER, ID_CONFIRM_DIALOG, false);
|
||||
// Call the callback.
|
||||
if (yesCallback != null)
|
||||
yesCallback();
|
||||
};
|
||||
// Set the no button callback.
|
||||
confirmDialogNoButtonElement.onclick = function() {
|
||||
// Hide the dialog.
|
||||
showPopup(ID_LOADING_WRAPPER, ID_CONFIRM_DIALOG, false);
|
||||
// Call the callback.
|
||||
if (noCallback != null)
|
||||
noCallback();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Transforms the given ISO8601 date to human readable date.
|
||||
function transformDate(iso8601Date) {
|
||||
// Initialize variables.
|
||||
var date = new Date(iso8601Date);
|
||||
var humanReadableDate = "";
|
||||
// Check if the date is valid.
|
||||
if (date != null) {
|
||||
// Transform the date to human readable date.
|
||||
humanReadableDate = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() + " " +
|
||||
date.getHours().toString().padStart(2, '0') + ":" + date.getMinutes().toString().padStart(2, '0');
|
||||
}
|
||||
// Return the human readable date.
|
||||
return humanReadableDate;
|
||||
}
|
||||
|
||||
// Transforms the given kilo-bytes to mega-bytes
|
||||
function kiloBytesToMegaBytes(kiloBytes) {
|
||||
return (kiloBytes / 1024).toFixed(2);
|
||||
}
|
||||
|
||||
// Format bytes as human-readable text.
|
||||
function sizeToHumanRead(bytes, si=true, dp=2) {
|
||||
// Initialize variables.
|
||||
var threshold = si ? 1000 : 1024;
|
||||
var units = si
|
||||
? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||
: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
|
||||
var unit = -1;
|
||||
// Check if minimum threshold is reached.
|
||||
if (Math.abs(bytes) < threshold)
|
||||
return bytes + ' Bytes';
|
||||
|
||||
const r = 10**dp;
|
||||
do {
|
||||
bytes /= threshold;
|
||||
++unit;
|
||||
} while (Math.round(Math.abs(bytes) * r) / r >= threshold && unit < units.length - 1);
|
||||
|
||||
return bytes.toFixed(dp) + ' ' + units[unit];
|
||||
}
|
||||
|
||||
// Rounds the given number to two decimals.
|
||||
function roundToTwoDecimals(number) {
|
||||
return Math.round(number * 100) / 100;
|
||||
}
|
||||
|
||||
// Returns human readable time from the given seconds.
|
||||
function secondsToTime(numSeconds) {
|
||||
// Initialize variables.
|
||||
var days = 0;
|
||||
var hours = 0;
|
||||
var minutes = 0;
|
||||
var seconds = 0;
|
||||
// Calculate the time values.
|
||||
if (numSeconds < 60)
|
||||
seconds = numSeconds;
|
||||
else if (numSeconds < (60 * 60)) {
|
||||
minutes = Math.floor(numSeconds / 60);
|
||||
seconds = numSeconds % 60;
|
||||
} else if (numSeconds < (60 * 60 * 24)) {
|
||||
hours = Math.floor(numSeconds / (60 * 60));
|
||||
minutes = Math.floor((numSeconds % (60 * 60)) / 60);
|
||||
seconds = numSeconds % 60;
|
||||
} else {
|
||||
days = Math.floor(numSeconds / (60 * 60 * 24));
|
||||
hours = Math.floor((numSeconds % (60 * 60 * 24)) / (60 * 60));
|
||||
minutes = Math.floor((numSeconds % (60 * 60)) / 60);
|
||||
seconds = numSeconds % 60;
|
||||
}
|
||||
// Build time string.
|
||||
var time = "";
|
||||
var numFields = 0;
|
||||
if (days > 0) {
|
||||
time += days + " day";
|
||||
if (days > 1)
|
||||
time += "s";
|
||||
time += " ";
|
||||
numFields += 1;
|
||||
}
|
||||
if (hours > 0) {
|
||||
time += hours + " hour";
|
||||
if (hours > 1)
|
||||
time += "s";
|
||||
time += " ";
|
||||
numFields += 1;
|
||||
}
|
||||
if (minutes > 0 && numFields < 2) {
|
||||
time += minutes + " minute";
|
||||
if (minutes > 1)
|
||||
time += "s";
|
||||
time += " ";
|
||||
numFields += 1;
|
||||
}
|
||||
if ((seconds > 0 && numFields < 2) || time == "") {
|
||||
time += seconds + " second";
|
||||
if (seconds > 1)
|
||||
time += "s";
|
||||
}
|
||||
// Return the time string.
|
||||
return time;
|
||||
}
|
||||
|
||||
// Returns on/off status from the given integer.
|
||||
function onOffStatus(status) {
|
||||
return status == 1 ? VALUE_ON : VALUE_OFF;
|
||||
}
|
||||
|
||||
// Updates the given field ID with the given value.
|
||||
function updateFieldValue(fieldID, value) {
|
||||
var fieldElement = document.getElementById(fieldID);
|
||||
if (fieldElement != null && fieldElement != "undefined")
|
||||
fieldElement.innerText = value;
|
||||
}
|
||||
|
||||
// Rounds the given amount of kilo-bytes to giga bytes.
|
||||
function roundToGigaBytes(kiloBytes) {
|
||||
return (kiloBytes / (1024 * 1024)).toFixed(2);
|
||||
}
|
||||
|
||||
// Returns whether the dashboard page is showing or not.
|
||||
function isDashboardShowing() {
|
||||
return window.location.pathname.indexOf("dashboard") > -1;
|
||||
}
|
||||
|
||||
// Returns whether the management page is showing or not.
|
||||
function isManagementShowing() {
|
||||
return window.location.pathname.indexOf("management") > -1;
|
||||
}
|
||||
|
||||
// Returns whether the history page is showing or not.
|
||||
function isHistoryShowing() {
|
||||
return window.location.pathname.indexOf("history") > -1;
|
||||
}
|
||||
|
||||
// Returns the device ID.
|
||||
function getDeviceID() {
|
||||
return new URLSearchParams(window.location.search).get(ID_DEVICE_ID);
|
||||
}
|
||||
|
||||
// Returns the device name.
|
||||
function getDeviceName() {
|
||||
return new URLSearchParams(window.location.search).get(ID_DEVICE_NAME);
|
||||
}
|
||||
|
|
@ -0,0 +1,392 @@
|
|||
/*
|
||||
* Copyright 2022, Digi International Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
// Constants.
|
||||
const CONSOLE_CURSOR_BLINK = true;
|
||||
const CONSOLE_NUM_ROWS = 30;
|
||||
const CONSOLE_SCROLLBACK = 1000;
|
||||
const CONSOLE_TAB_STOP_WIDTH = 8;
|
||||
const CONSOLE_MAX_SEND_COMMANDS = 5;
|
||||
const CONSOLE_SEND_COMMANDS_DELAY = 150;
|
||||
|
||||
const ID_CONSOLE_CLOSE_BUTTON = "console_close_button";
|
||||
const ID_CONSOLE_CONTAINER = "console_container";
|
||||
const ID_CONSOLE_ERROR_RECONNECT = "console_error_reconnect";
|
||||
const ID_CONSOLE_ERROR_TEXT = "console_error_text";
|
||||
const ID_CONSOLE_HEADER = "console_header";
|
||||
const ID_CONSOLE_HOVER_BACKGROUND = "console_hover_background";
|
||||
const ID_CONSOLE_LOADING = "console_loading";
|
||||
const ID_CONSOLE_PANEL = "console_panel";
|
||||
const ID_CONSOLE_RECONNECT_BUTTON = "console_reconnect_button";
|
||||
const ID_CONSOLE_VIEWPORT = "xterm-viewport";
|
||||
|
||||
const CLI_MESSAGE_TYPE_DATA = "data";
|
||||
const CLI_MESSAGE_TYPE_ERROR = "error";
|
||||
const CLI_MESSAGE_TYPE_START = "start";
|
||||
const CLI_MESSAGE_TYPE_TERMINATE = "terminate";
|
||||
|
||||
const ERROR_SESSION_CLOSED_REMOTELY = "Session closed from the remote end";
|
||||
const ERROR_SESSION_CLOSING = "Previous console session is closing, please wait a moment...";
|
||||
|
||||
// Variables.
|
||||
var term;
|
||||
var cliSessionID;
|
||||
var cliSocket;
|
||||
var cliSessionTerminating = false;
|
||||
var fitAddon;
|
||||
var commandsToSend = [];
|
||||
var cliTerminateFlagTimer = null;
|
||||
var consoleResizeObserver = null;
|
||||
|
||||
// Opens the console.
|
||||
function openConsole() {
|
||||
// Check if console is showing.
|
||||
if (isConsoleShowing())
|
||||
return;
|
||||
// Check if previous session is terminating.
|
||||
if (cliSessionTerminating) {
|
||||
toastr.info(ERROR_SESSION_CLOSING);
|
||||
return;
|
||||
}
|
||||
// Initialize variables.
|
||||
var consoleDiv = document.getElementById(ID_CONSOLE);
|
||||
// Remove any residual terminals.
|
||||
while (consoleDiv.firstChild)
|
||||
consoleDiv.removeChild(consoleDiv.lastChild);
|
||||
// Create the terminal instance.
|
||||
term = new Terminal({
|
||||
cursorBlink: CONSOLE_CURSOR_BLINK,
|
||||
scrollback: CONSOLE_SCROLLBACK,
|
||||
tabStopWidth: CONSOLE_TAB_STOP_WIDTH,
|
||||
rows: CONSOLE_NUM_ROWS
|
||||
});
|
||||
// Configure terminal input data event.
|
||||
term.onData( (data) => {
|
||||
sendCLIData(data);
|
||||
});
|
||||
// Load Fit addon.
|
||||
fitAddon = new FitAddon.FitAddon();
|
||||
term.loadAddon(fitAddon);
|
||||
// Attach the terminal to the div.
|
||||
term.open(consoleDiv);
|
||||
// Setup resize observer.
|
||||
if (consoleResizeObserver == null || consoleResizeObserver == "undefined") {
|
||||
consoleResizeObserver = new ResizeObserver((entries) => {
|
||||
adjustConsoleSize();
|
||||
})
|
||||
consoleResizeObserver.observe(document.getElementById(ID_CONSOLE_CONTAINER));
|
||||
}
|
||||
// Show the console.
|
||||
var consolePanel = document.getElementById(ID_CONSOLE_PANEL);
|
||||
consolePanel.style.visibility = "visible";
|
||||
consolePanel.style.display = "flex";
|
||||
// Adjust console size after some time to give time to the terminal to load.
|
||||
window.setTimeout(function () {
|
||||
adjustConsoleSize();
|
||||
}, 100);
|
||||
adjustConsoleSize();
|
||||
// Initialize the CLI session.
|
||||
initCLISession();
|
||||
}
|
||||
|
||||
// Adjusts the console size.
|
||||
function adjustConsoleSize() {
|
||||
// Sanity checks.
|
||||
if (!isConsoleShowing())
|
||||
return;
|
||||
// Initialize variables.
|
||||
var consoleContainer = document.getElementById(ID_CONSOLE_CONTAINER);
|
||||
var consoleViewPort = document.getElementsByClassName(ID_CONSOLE_VIEWPORT)[0];
|
||||
var consoleHeader = document.getElementById(ID_CONSOLE_HEADER);
|
||||
var consoleHoverBackground = document.getElementById(ID_CONSOLE_HOVER_BACKGROUND);
|
||||
// Adjust terminal size.
|
||||
if (fitAddon != null && fitAddon != "undefined")
|
||||
fitAddon.fit();
|
||||
// Obtain dimensions.
|
||||
var consoleHeight = consoleViewPort.clientHeight;
|
||||
var headerHeight = consoleHeader.clientHeight;
|
||||
// Set size values.
|
||||
consoleContainer.style.height = (consoleHeight + headerHeight).toString() + "px";
|
||||
consoleHoverBackground.style.height = (consoleContainer.clientHeight - headerHeight).toString() + "px";
|
||||
consoleViewPort.style.width = "initial";
|
||||
}
|
||||
|
||||
// Returns whether the console window is open or not.
|
||||
function isConsoleShowing() {
|
||||
// Sanity checks.
|
||||
if (!isDashboardShowing())
|
||||
return false;
|
||||
// Initialize variables.
|
||||
var consolePanel = document.getElementById(ID_CONSOLE_PANEL);
|
||||
// Return whether the console is showing or not.
|
||||
return consolePanel.style.visibility == "visible";
|
||||
}
|
||||
|
||||
// Shows/hides the console loading panel.
|
||||
function showConsoleLoading(visible) {
|
||||
// Initialize variables.
|
||||
var consoleHoverBackground = document.getElementById(ID_CONSOLE_HOVER_BACKGROUND);
|
||||
var consoleLoading = document.getElementById(ID_CONSOLE_LOADING);
|
||||
// Show/hide the loading panel.
|
||||
if (visible && isConsoleShowing()) {
|
||||
consoleHoverBackground.style.visibility = "visible";
|
||||
consoleLoading.style.visibility = "visible";
|
||||
} else {
|
||||
consoleHoverBackground.style.visibility = "hidden";
|
||||
consoleLoading.style.visibility = "hidden";
|
||||
}
|
||||
}
|
||||
|
||||
// Shows/hides the console error with reconnect panel.
|
||||
function showConsoleErrorReconnect(visible, error) {
|
||||
// Initialize variables.
|
||||
var consoleHoverBackground = document.getElementById(ID_CONSOLE_HOVER_BACKGROUND);
|
||||
var consoleErrorReconnect = document.getElementById(ID_CONSOLE_ERROR_RECONNECT);
|
||||
var consoleErrorText = document.getElementById(ID_CONSOLE_ERROR_TEXT);
|
||||
var reconnectButton = document.getElementById(ID_CONSOLE_RECONNECT_BUTTON);
|
||||
var closeButton = document.getElementById(ID_CONSOLE_CLOSE_BUTTON);
|
||||
// Show/hide the error panel.
|
||||
if (visible && isConsoleShowing()) {
|
||||
consoleHoverBackground.style.visibility = "visible";
|
||||
consoleErrorReconnect.style.visibility = "visible";
|
||||
reconnectButton.style.visibility = "visible";
|
||||
closeButton.style.visibility = "visible";
|
||||
if (error == null)
|
||||
error = ERROR_UNKNOWN_ERROR;
|
||||
consoleErrorText.innerText = error;
|
||||
} else {
|
||||
consoleHoverBackground.style.visibility = "hidden";
|
||||
consoleErrorReconnect.style.visibility = "hidden";
|
||||
reconnectButton.style.visibility = "hidden";
|
||||
closeButton.style.visibility = "hidden";
|
||||
}
|
||||
}
|
||||
|
||||
// Initializes a CLI session.
|
||||
function initCLISession() {
|
||||
// Hide error with reconnect panel.
|
||||
showConsoleErrorReconnect(false);
|
||||
// Clear the terminal.
|
||||
term.clear();
|
||||
// Show loading panel.
|
||||
showConsoleLoading(true);
|
||||
$.post(
|
||||
"../ajax/cli_init_session",
|
||||
JSON.stringify({
|
||||
"device_id": getDeviceID()
|
||||
}),
|
||||
function(data) {
|
||||
// Process only if the console is showing.
|
||||
if (!isConsoleShowing())
|
||||
return;
|
||||
// Process answer.
|
||||
processInitCLISessionResponse(data);
|
||||
}
|
||||
).fail(function(response) {
|
||||
// Process only if the console is showing.
|
||||
if (!isConsoleShowing())
|
||||
return;
|
||||
// Process error.
|
||||
var error = processAjaxErrorResponse(response);
|
||||
// Hide the loading status.
|
||||
showConsoleLoading(false);
|
||||
// Show error with reconnect panel.
|
||||
showConsoleErrorReconnect(true, error);
|
||||
});
|
||||
}
|
||||
|
||||
// Processes the init CLI session response.
|
||||
function processInitCLISessionResponse(response) {
|
||||
// Check if there was any error in the request.
|
||||
if (checkErrorResponse(response, false)) {
|
||||
// Hide the loading status.
|
||||
showConsoleLoading(false);
|
||||
// Show error with reconnect panel.
|
||||
showConsoleErrorReconnect(true, error);
|
||||
} else {
|
||||
// Store session ID.
|
||||
cliSessionID = response[ID_SESSION_ID];
|
||||
// Register the TCP monitor.
|
||||
subscribeCLISession(cliSessionID);
|
||||
}
|
||||
}
|
||||
|
||||
// Sends the given CLI data.
|
||||
function sendCLIData(data) {
|
||||
// Check if CLI session exists.
|
||||
if (cliSessionID == null)
|
||||
return;
|
||||
// Add the command to the buffer.
|
||||
commandsToSend.push(data);
|
||||
// If the send timer is scheduled and there is enough data to send,
|
||||
// return to let the timer to complete.
|
||||
if (sendTimerScheduled && commandsToSend.length > CONSOLE_MAX_SEND_COMMANDS)
|
||||
return;
|
||||
if (sendTimer != null && sendTimer != "undefined") {
|
||||
clearTimeout(sendTimer);
|
||||
sendTimer = null;
|
||||
}
|
||||
// Schedule the timer.
|
||||
sendTimer = setTimeout(function() {
|
||||
// Clear timer flag.
|
||||
sendTimerScheduled = false;
|
||||
// Build data.
|
||||
var dataToSend = btoa(commandsToSend.join(""));
|
||||
commandsToSend = [];
|
||||
$.post(
|
||||
"../ajax/cli_data",
|
||||
JSON.stringify({
|
||||
"device_id": getDeviceID(),
|
||||
"session_id": cliSessionID,
|
||||
"data": dataToSend
|
||||
}),
|
||||
function(data) {
|
||||
// Process only if the console is showing.
|
||||
if (!isConsoleShowing())
|
||||
return;
|
||||
// Process answer.
|
||||
processCLISendDataResponse(data);
|
||||
}
|
||||
).fail(function(response) {
|
||||
// Process only if the console is showing.
|
||||
if (!isConsoleShowing())
|
||||
return;
|
||||
// Process error.
|
||||
processAjaxErrorResponse(response);
|
||||
});
|
||||
}, CONSOLE_SEND_COMMANDS_DELAY);
|
||||
// Set timer flag.
|
||||
sendTimerScheduled = true;
|
||||
}
|
||||
|
||||
// Processes the send CLI data response.
|
||||
function processCLISendDataResponse(response) {
|
||||
// Check if there was any error in the request.
|
||||
checkErrorResponse(response, false);
|
||||
}
|
||||
|
||||
// Closes the console.
|
||||
function closeConsole() {
|
||||
// Check if console is showing.
|
||||
if (!isConsoleShowing())
|
||||
return;
|
||||
// Hide the loading panel.
|
||||
showConsoleLoading(false);
|
||||
// Hide the error reconnect panel.
|
||||
showConsoleErrorReconnect(false);
|
||||
// Terminate the CLI session.
|
||||
terminateCLIsession();
|
||||
// Hide the console.
|
||||
var consolePanel = document.getElementById(ID_CONSOLE_PANEL);
|
||||
consolePanel.style.visibility = "collapse";
|
||||
consolePanel.style.display = "none";
|
||||
}
|
||||
|
||||
// Terminates the CLI session.
|
||||
function terminateCLIsession() {
|
||||
// Check if CLI session exists.
|
||||
if (cliSessionID == null)
|
||||
return;
|
||||
// Flag session terminating.
|
||||
cliSessionTerminating = true;
|
||||
cliTerminateFlagTimer = window.setTimeout(function () {
|
||||
cliSessionTerminating = false;
|
||||
}, 30000); // In the case the terminate request hangs, set a timeout to manually remove the flag.
|
||||
// Send request to terminate CLI session.
|
||||
$.post(
|
||||
"../ajax/cli_terminate_session",
|
||||
JSON.stringify({
|
||||
"device_id": getDeviceID(),
|
||||
"session_id": cliSessionID
|
||||
}),
|
||||
function(data) {
|
||||
// Process only if the console is showing.
|
||||
if (!isConsoleShowing())
|
||||
return;
|
||||
// Process answer.
|
||||
processTerminateCLISessionResponse(data);
|
||||
}
|
||||
).fail(function(response) {
|
||||
// Un-flag session terminating.
|
||||
cliSessionTerminating = false;
|
||||
window.clearTimeout(cliTerminateFlagTimer);
|
||||
// Process only if the console is showing.
|
||||
if (!isConsoleShowing())
|
||||
return;
|
||||
// Process error.
|
||||
processAjaxErrorResponse(response);
|
||||
});
|
||||
// Clear CLI session ID.
|
||||
cliSessionID = null;
|
||||
}
|
||||
|
||||
// Processes the terminate CLI session response.
|
||||
function processTerminateCLISessionResponse(response) {
|
||||
// Check if there was any error in the request.
|
||||
if (checkErrorResponse(response, false)) {
|
||||
// Un-flag session terminating.
|
||||
cliSessionTerminating = false;
|
||||
window.clearTimeout(cliTerminateFlagTimer);
|
||||
}
|
||||
}
|
||||
|
||||
// Subscribes to any CLI session change.
|
||||
function subscribeCLISession(sessionID) {
|
||||
// Create the web socket.
|
||||
var socketPrefix = window.location.protocol == "https:" ? "wss" : "ws";
|
||||
cliSocket = new WebSocket(socketPrefix + "://" + window.location.host + "/ws/cli/" + device.getDeviceID() + "/" + sessionID);
|
||||
// Define the callback to be notified when data is received in the web socket.
|
||||
cliSocket.onmessage = function(e) {
|
||||
if (isDashboardShowing() && term != null && term != "undefined") {
|
||||
var event = JSON.parse(e.data);
|
||||
var type = event[ID_TYPE];
|
||||
switch (type) {
|
||||
case CLI_MESSAGE_TYPE_DATA:
|
||||
var data = event[ID_DATA];
|
||||
var decodedData = atob(data);
|
||||
if (isConsoleShowing())
|
||||
term.write(decodedData);
|
||||
break;
|
||||
case CLI_MESSAGE_TYPE_TERMINATE:
|
||||
case CLI_MESSAGE_TYPE_ERROR:
|
||||
cliSessionID = null;
|
||||
if (cliSocket != null && cliSocket != "undefined")
|
||||
cliSocket.close();
|
||||
// Hide the loading status.
|
||||
showConsoleLoading(false);
|
||||
// Check specific type.
|
||||
var error = null;
|
||||
if (type == CLI_MESSAGE_TYPE_TERMINATE && isConsoleShowing())
|
||||
error = ERROR_SESSION_CLOSED_REMOTELY;
|
||||
else if (type == CLI_MESSAGE_TYPE_ERROR)
|
||||
error = event[ID_ERROR];
|
||||
if (isConsoleShowing())
|
||||
showConsoleErrorReconnect(true, error);
|
||||
if (error != null && (type == CLI_MESSAGE_TYPE_ERROR ||
|
||||
(type == CLI_MESSAGE_TYPE_TERMINATE && isConsoleShowing())))
|
||||
toastr.error(error);
|
||||
// Un-flag session terminating.
|
||||
cliSessionTerminating = false;
|
||||
window.clearTimeout(cliTerminateFlagTimer);
|
||||
break;
|
||||
case CLI_MESSAGE_TYPE_START:
|
||||
// Hide the loading status.
|
||||
showConsoleLoading(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Copyright 2022, Digi International Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
// Constants.
|
||||
const ID_DATA_USAGE_BUTTON = "data_usage_button";
|
||||
const ID_DATA_USAGE_CURRENT_DEVICE = "data_usage_current_device";
|
||||
const ID_DATA_USAGE_GRAPHIC_WEB = "data_usage_graphic_web";
|
||||
const ID_DATA_USAGE_GRAPHIC_DEVICES = "data_usage_graphic_devices";
|
||||
const ID_DATA_USAGE_ICON = "data_usage_icon";
|
||||
const ID_DATA_USAGE_LOADING = "data_usage_loading";
|
||||
const ID_DATA_USAGE_PANEL = "data_usage_panel";
|
||||
|
||||
// Returns whether the data usage panel is open or not.
|
||||
function isDataUsageShowing() {
|
||||
// Initialize variables.
|
||||
var dataUsagePanel = document.getElementById(ID_DATA_USAGE_PANEL);
|
||||
// Return visibility.
|
||||
return dataUsagePanel.style.display != "none";
|
||||
}
|
||||
|
||||
// Toggles the data usage panel visibility.
|
||||
function toggleDataUsagePanel() {
|
||||
// Initialize variables.
|
||||
var dataUsageIcon = document.getElementById(ID_DATA_USAGE_ICON);
|
||||
var dataUsageButton = document.getElementById(ID_DATA_USAGE_BUTTON);
|
||||
// Avoid double clicks.
|
||||
dataUsageButton.style.pointerEvents = "none";
|
||||
// Check data usage panel visibility.
|
||||
if (isDataUsageShowing()) {
|
||||
$("#" + ID_DATA_USAGE_PANEL).slideUp("fast", function() {
|
||||
dataUsageIcon.src = "../static/images/data_usage.png";
|
||||
// Enable hover effect.
|
||||
$("#" + ID_DATA_USAGE_BUTTON).on({
|
||||
"mouseover" : function() {
|
||||
dataUsageIcon.src = "../static/images/data_usage_green.png";
|
||||
},
|
||||
"mouseout" : function() {
|
||||
dataUsageIcon.src = "../static/images/data_usage.png";
|
||||
}
|
||||
});
|
||||
// React to mouse events again.
|
||||
dataUsageButton.style.pointerEvents = "auto";
|
||||
// Reset fields.
|
||||
clearDataUsageFields();
|
||||
});
|
||||
} else {
|
||||
$("#" + ID_DATA_USAGE_PANEL).slideDown("fast", function() {
|
||||
dataUsageIcon.src = "../static/images/data_usage_green.png";
|
||||
// Disable hover effect.
|
||||
$("#" + ID_DATA_USAGE_BUTTON).on({
|
||||
"mouseover" : function() {
|
||||
dataUsageIcon.src = "../static/images/data_usage_green.png";
|
||||
},
|
||||
"mouseout" : function() {
|
||||
dataUsageIcon.src = "../static/images/data_usage_green.png";
|
||||
}
|
||||
});
|
||||
// React to mouse events again.
|
||||
dataUsageButton.style.pointerEvents = "auto";
|
||||
// Read data usage.
|
||||
readDataUsage();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Reads the DRM account data usage.
|
||||
function readDataUsage() {
|
||||
// Sanity checks.
|
||||
if (!isDataUsageShowing())
|
||||
return;
|
||||
// Show the data usage loading panel.
|
||||
showDataUsageLoading(true);
|
||||
// Send request to change the LED status.
|
||||
$.post(
|
||||
"../ajax/get_data_usage",
|
||||
function(data) {
|
||||
// Process only if the panel is showing.
|
||||
if (!isDataUsageShowing())
|
||||
return;
|
||||
// Process answer.
|
||||
processReadDataUsageResponse(data);
|
||||
}
|
||||
).fail(function(response) {
|
||||
// Process only if the panel is showing.
|
||||
if (!isDataUsageShowing())
|
||||
return;
|
||||
// Process error.
|
||||
error = processAjaxErrorResponse(response);
|
||||
// Hide the data usage loading panel.
|
||||
showDataUsageLoading(true);
|
||||
});
|
||||
}
|
||||
|
||||
// Processes the read data usage response.
|
||||
function processReadDataUsageResponse(response) {
|
||||
// Hide the data usage loading panel.
|
||||
showDataUsageLoading(false);
|
||||
// Check if there was any error in the request.
|
||||
if (!checkErrorResponse(response, false)) {
|
||||
// Update the data usage values.
|
||||
updateDataUsageValues(response);
|
||||
}
|
||||
}
|
||||
|
||||
// Updates the data usage values.
|
||||
function updateDataUsageValues(response) {
|
||||
// Sanity checks.
|
||||
if (!isDataUsageShowing())
|
||||
return;
|
||||
// Initialize variables.
|
||||
var dataUsageTotal = response[ID_DATA_USAGE_TOTAL];
|
||||
var dataUsageWeb = response[ID_DATA_USAGE_WEB];
|
||||
var dataUsageDevices = response[ID_DATA_USAGE_DEVICES];
|
||||
var dataUsageDevicesPercent = 0;
|
||||
var dataUsageWebPercent = 0;
|
||||
var dataUsageCurrentDevice = 0;
|
||||
var graphicDevicesElement = document.getElementById(ID_DATA_USAGE_GRAPHIC_DEVICES);
|
||||
var graphicWebElement = document.getElementById(ID_DATA_USAGE_GRAPHIC_WEB);
|
||||
// Calculate percent for data devices.
|
||||
if (dataUsageTotal > 0) {
|
||||
dataUsageDevicesPercent = Math.round(dataUsageDevices * 100 / dataUsageTotal);
|
||||
dataUsageWebPercent = Math.round(dataUsageWeb * 100 / dataUsageTotal);
|
||||
}
|
||||
// Calculate values for totals.
|
||||
dataUsageDevices = sizeToHumanRead(dataUsageDevices * 1000 * 1000); // This usage is given in MB.
|
||||
dataUsageWeb = sizeToHumanRead(dataUsageWeb * 1000 * 1000); // This usage is given in MB.
|
||||
// Retrieve the data usage for the current device.
|
||||
for (var device in response[ID_DEVICES]) {
|
||||
if (response[ID_DEVICES][device][ID_DEVICE_ID].toLowerCase() == getDeviceID().toLowerCase()) {
|
||||
dataUsageCurrentDevice = response[ID_DEVICES][device][ID_USAGE];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Set percentage size of graphics.
|
||||
graphicDevicesElement.style.width = dataUsageDevicesPercent + "%";
|
||||
graphicWebElement.style.width = dataUsageWebPercent + "%";
|
||||
// Set graphics tooltips.
|
||||
graphicDevicesElement.setAttribute("data-original-title", "Total data usage by devices: " + dataUsageDevices + " (" + dataUsageDevicesPercent + "%)");
|
||||
graphicWebElement.setAttribute("data-original-title", "Total data usage by web APIs: " + dataUsageWeb + " (" + dataUsageWebPercent + "%)");
|
||||
// Update the data usage values.
|
||||
updateFieldValue(ID_DATA_USAGE_TOTAL, sizeToHumanRead(dataUsageTotal * 1000 * 1000)); // This usage is given in MB.
|
||||
updateFieldValue(ID_DATA_USAGE_DEVICES, dataUsageDevices);
|
||||
updateFieldValue(ID_DATA_USAGE_CURRENT_DEVICE, sizeToHumanRead(dataUsageCurrentDevice * 1000)); // This usage is given in KB.
|
||||
updateFieldValue(ID_DATA_USAGE_WEB, dataUsageWeb);
|
||||
updateFieldValue(ID_DATA_USAGE_WEB_SERVICES, sizeToHumanRead(response[ID_DATA_USAGE_WEB_SERVICES] * 1000)); // This usage is given in KB.
|
||||
updateFieldValue(ID_DATA_USAGE_MONITORS, sizeToHumanRead(response[ID_DATA_USAGE_MONITORS] * 1000)); // This usage is given in KB.
|
||||
}
|
||||
|
||||
// Shows/hides the data usage loading panel.
|
||||
function showDataUsageLoading(visible) {
|
||||
// Sanity checks.
|
||||
if (!isDataUsageShowing())
|
||||
return;
|
||||
// Initialize variables.
|
||||
var dataUsageLoading = document.getElementById(ID_DATA_USAGE_LOADING);
|
||||
// Show the data usage loading panel.
|
||||
if (visible)
|
||||
dataUsageLoading.style.display = "block";
|
||||
else
|
||||
dataUsageLoading.style.display = "none";
|
||||
}
|
||||
|
||||
// Clears all data usage fields.
|
||||
function clearDataUsageFields() {
|
||||
// Initialize variables.
|
||||
var graphicDevicesElement = document.getElementById(ID_DATA_USAGE_GRAPHIC_DEVICES);
|
||||
var graphicWebElement = document.getElementById(ID_DATA_USAGE_GRAPHIC_WEB);
|
||||
// Clear all data usage fields.
|
||||
updateFieldValue(ID_DATA_USAGE_TOTAL, "");
|
||||
updateFieldValue(ID_DATA_USAGE_DEVICES, "");
|
||||
updateFieldValue(ID_DATA_USAGE_CURRENT_DEVICE, "");
|
||||
updateFieldValue(ID_DATA_USAGE_WEB, "");
|
||||
updateFieldValue(ID_DATA_USAGE_WEB_SERVICES, "");
|
||||
updateFieldValue(ID_DATA_USAGE_MONITORS, "");
|
||||
// Reset graphic sizes.
|
||||
graphicDevicesElement.style.width = "0%";
|
||||
graphicWebElement.style.width = "0%";
|
||||
}
|
||||
|
||||
// Refreshes the usage data.
|
||||
function refreshUsageData() {
|
||||
// Sanity checks.
|
||||
if (!isDataUsageShowing())
|
||||
return;
|
||||
// Clear all data usage fields.
|
||||
clearDataUsageFields();
|
||||
// Read data usage.
|
||||
readDataUsage();
|
||||
}
|
||||
|
|
@ -0,0 +1,805 @@
|
|||
/*
|
||||
* Copyright 2022, Digi International Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
// Constants.
|
||||
const CLASS_ADD_DEVICE_DIALOG_BUTTON_DISABLED = "add-device-button-disabled";
|
||||
const CLASS_ADD_DEVICE_DIALOG_INPUT_ERROR = "add-device-input-error";
|
||||
const CLASS_DEVICE_SELECTED = "devices-list-entry-selected";
|
||||
|
||||
const ERROR_DEVICE_ID_EMPTY = "A Device ID or MAC Address must be provided.";
|
||||
const ERROR_DEVICE_ID_INVALID = "A device id format is 00000000-00000000-00000000-00000000, while a mac address format is 00:00:00:00:00:00 (optional colon separators)";
|
||||
const ERROR_INVALID_PROVISION_VALUE = "Provided provision value was not valid.";
|
||||
|
||||
const ID_ADD_DEVICE_DIALOG = "add_device_dialog";
|
||||
const ID_ADD_DEVICE_DIALOG_BUTTON = "add_device_button";
|
||||
const ID_ADD_DEVICE_DIALOG_ERROR = "add_device_error";
|
||||
const ID_ADD_DEVICE_DIALOG_INPUT = "add_device_input";
|
||||
const ID_CONTINUE_BUTTON = "continue-button";
|
||||
const ID_DEVICES_LIST = "devices-list";
|
||||
const ID_REFRESH_BUTTON = "refresh-button";
|
||||
|
||||
const MESSAGE_LOADING_DEVICES = "Loading devices..."
|
||||
const MESSAGE_REGISTERING_DEVICE = "Registering device..."
|
||||
|
||||
const PROVISION_TYPE_ID = "id";
|
||||
const PROVISION_TYPE_MAC = "mac";
|
||||
const PROVISION_TYPE_IMEI = "imei";
|
||||
|
||||
const REGEX_DEVICE_ID = "^(?:[a-fA-F0-9]{8}-){3}[a-fA-F0-9]{8}$";
|
||||
const REGEX_DEVICE_MAC = "^(?:[a-fA-F0-9]{2}:?){5}[a-fA-F0-9]{2}$";
|
||||
const REGEX_DEVICE_IMEI = "^[0-9]{15}$";
|
||||
|
||||
const TEMPLATE_COMPONENT_DATA = "" +
|
||||
"{" +
|
||||
" \"" + ID_VISIBLE + "\" : {0}," +
|
||||
" \"" + ID_HAS_PANEL + "\" : {1}," +
|
||||
" \"" + ID_HAS_ARROW + "\" : {2}," +
|
||||
" \"" + ID_PANEL_ALWAYS_VISIBLE + "\" : {3}," +
|
||||
" \"" + ID_PANEL_ORIENTATION + "\" : \"{4}\"," +
|
||||
" \"" + ID_PANEL_HORIZONTAL_MARGIN + "\" : {5}," +
|
||||
" \"" + ID_PANEL_VERTICAL_MARGIN + "\" : {6}," +
|
||||
" \"" + ID_ARROW_MARGIN + "\" : {7}," +
|
||||
" \"" + ID_AREA_TOP_MARGIN + "\" : {8}," +
|
||||
" \"" + ID_AREA_LEFT_MARGIN + "\" : {9}," +
|
||||
" \"" + ID_AREA_WIDTH + "\" : {10}," +
|
||||
" \"" + ID_AREA_HEIGHT + "\" : {11}" +
|
||||
"}"
|
||||
const TEMPLATE_DEVICE_LIST_ENTRY = "" +
|
||||
"<div id='@@ID@@' onclick='selectDevice(\"@@ID@@\")' class='devices-list-entry'>" +
|
||||
" <table>" +
|
||||
" <thead>" +
|
||||
" <tr>" +
|
||||
" <td rowspan='2'><img class='device-list-entry-status' src='/static/images/@@STATUS_IMAGE@@'></td>" +
|
||||
" <td><span class='device-list-entry-name'>@@TYPE@@</span></td>" +
|
||||
" </tr>" +
|
||||
" <tr>" +
|
||||
" <td><span class='device-list-entry-id'>@@ID@@</span></td>" +
|
||||
" <tr>" +
|
||||
" </thead>" +
|
||||
" </table>" +
|
||||
"</div>";
|
||||
|
||||
// Variables.
|
||||
var devices = [];
|
||||
|
||||
// Lists DRM devices.
|
||||
function listDevices() {
|
||||
// Disable the refresh button.
|
||||
document.getElementById(ID_REFRESH_BUTTON).disabled = true;
|
||||
// Disable the continue button.
|
||||
document.getElementById(ID_CONTINUE_BUTTON).disabled = true;
|
||||
// Clear the devices list
|
||||
clearDevices();
|
||||
// Hide info dialog.
|
||||
showInfoPopup(false);
|
||||
// Show loading dialog.
|
||||
showLoadingPopup(true, MESSAGE_LOADING_DEVICES);
|
||||
// Send the request.
|
||||
$.post(
|
||||
"../ajax/get_devices",
|
||||
function(data) {
|
||||
// Hide the loading panel.
|
||||
showLoadingPopup(false);
|
||||
// Process answer.
|
||||
processListDevicesAnswer(data);
|
||||
// Enable the refresh button.
|
||||
document.getElementById(ID_REFRESH_BUTTON).disabled = false;
|
||||
// Update continue button.
|
||||
updateContinueButton();
|
||||
}
|
||||
).fail(function(response) {
|
||||
// Hide the loading panel.
|
||||
showLoadingPopup(false);
|
||||
// Process error.
|
||||
processAjaxErrorResponse(response);
|
||||
// Enable the refresh button.
|
||||
document.getElementById(ID_REFRESH_BUTTON).disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
// Processes the answer of the list devices request.
|
||||
function processListDevicesAnswer(response) {
|
||||
// Check if there was any error in the request.
|
||||
if (checkErrorResponse(response, false)) {
|
||||
// Do not continue.
|
||||
return;
|
||||
}
|
||||
// Get the devices from the JSON response.
|
||||
let readDevices = response[ID_DEVICES];
|
||||
// Check if the list of devices contains any device.
|
||||
if (readDevices == null || readDevices.length == 0)
|
||||
return;
|
||||
// Process devices.
|
||||
for (let device of readDevices) {
|
||||
// Add a new device entry to the list of devices.
|
||||
devices.push(device);
|
||||
let deviceDivContent = TEMPLATE_DEVICE_LIST_ENTRY;
|
||||
deviceDivContent = deviceDivContent.replace(/@@ID@@/g, device[ID_ID]);
|
||||
deviceDivContent = deviceDivContent.replace(/@@TYPE@@/g, device[ID_TYPE]);
|
||||
if (device[ID_ONLINE] == true)
|
||||
deviceDivContent = deviceDivContent.replace(/@@STATUS_IMAGE@@/g, IMAGE_ONLINE);
|
||||
else
|
||||
deviceDivContent = deviceDivContent.replace(/@@STATUS_IMAGE@@/g, IMAGE_OFFLINE);
|
||||
let deviceDiv = document.createElement("div");
|
||||
deviceDiv.innerHTML = deviceDivContent;
|
||||
$("#" + ID_DEVICES_LIST).append(deviceDiv);
|
||||
}
|
||||
}
|
||||
|
||||
// Clears the list of devices.
|
||||
function clearDevices() {
|
||||
unselectDevices();
|
||||
devices = [];
|
||||
$("#" + ID_DEVICES_LIST).html("");
|
||||
}
|
||||
|
||||
// Selects the given device
|
||||
function selectDevice(deviceID) {
|
||||
// Unselect all devices.
|
||||
unselectDevices();
|
||||
// Set selected style to the selected device div.
|
||||
if (document.getElementById(deviceID) != null)
|
||||
document.getElementById(deviceID).classList.add(CLASS_DEVICE_SELECTED);
|
||||
// Save selected device.
|
||||
for (i = 0; i < devices.length; i++) {
|
||||
device = devices[i];
|
||||
if (device[ID_ID] == deviceID)
|
||||
selectedDevice = device;
|
||||
}
|
||||
// Configure continue button.
|
||||
updateContinueButton();
|
||||
}
|
||||
|
||||
// Unselects all the devices.
|
||||
function unselectDevices() {
|
||||
// Clear selected style from all device divs.
|
||||
for (i = 0; i < devices.length; i++) {
|
||||
device = devices[i];
|
||||
if (document.getElementById(device[ID_ID]) != null)
|
||||
document.getElementById(device[ID_ID]).classList.remove(CLASS_DEVICE_SELECTED);
|
||||
}
|
||||
// Remove selected device.
|
||||
selectedDevice = null;
|
||||
// Configure continue button.
|
||||
updateContinueButton();
|
||||
}
|
||||
|
||||
// Updates the continue button state.
|
||||
function updateContinueButton() {
|
||||
// Initialize variables.
|
||||
var continueButton = document.getElementById(ID_CONTINUE_BUTTON);
|
||||
// Check if there is any selected device.
|
||||
if (selectedDevice != null) {
|
||||
continueButton.disabled = false;
|
||||
continueButton.onclick = function() {openSelectedDevice();};
|
||||
} else {
|
||||
continueButton.disabled = true;
|
||||
continueButton.onclick = function() { };
|
||||
}
|
||||
}
|
||||
|
||||
// Opens the selected device
|
||||
function openSelectedDevice() {
|
||||
// Avoid double click.
|
||||
document.getElementById(ID_CONTINUE_BUTTON).disabled = true;
|
||||
// Show loading dialog.
|
||||
showLoadingPopup(true);
|
||||
// Navigate to device dashboard page.
|
||||
window.open("../dashboard/?device_id=" + selectedDevice[ID_ID] + "&device_name=" + selectedDevice[ID_TYPE], "_self");
|
||||
}
|
||||
|
||||
// Opens the "Add device" dialog.
|
||||
function openAddDeviceDialog() {
|
||||
// Initialize variables.
|
||||
var addDeviceInputElement = document.getElementById(ID_ADD_DEVICE_DIALOG_INPUT);
|
||||
var addDeviceButtonElement = document.getElementById(ID_ADD_DEVICE_DIALOG_BUTTON);
|
||||
var addDeviceErrorElement = document.getElementById(ID_ADD_DEVICE_DIALOG_ERROR);
|
||||
// Reset dialog state.
|
||||
addDeviceInputElement.value = "";
|
||||
addDeviceErrorElement.innerHTML = " ";
|
||||
addDeviceErrorElement.style.visibility = "hidden";
|
||||
if (!addDeviceButtonElement.classList.contains(CLASS_ADD_DEVICE_DIALOG_BUTTON_DISABLED))
|
||||
addDeviceButtonElement.classList.add(CLASS_ADD_DEVICE_DIALOG_BUTTON_DISABLED);
|
||||
if (addDeviceInputElement.classList.contains(CLASS_ADD_DEVICE_DIALOG_INPUT_ERROR))
|
||||
addDeviceInputElement.classList.remove(CLASS_ADD_DEVICE_DIALOG_INPUT_ERROR);
|
||||
// Show panel.
|
||||
showAddDeviceDialog(true);
|
||||
}
|
||||
|
||||
// Closes the "Add device" dialog.
|
||||
function closeAddDeviceDialog() {
|
||||
showAddDeviceDialog(false);
|
||||
}
|
||||
|
||||
// Shows/hides the "Add device" dialog.
|
||||
function showAddDeviceDialog(visible) {
|
||||
// Initialize variables.
|
||||
var addDeviceDialogElement = document.getElementById(ID_ADD_DEVICE_DIALOG);
|
||||
var addDeviceErrorElement = document.getElementById(ID_ADD_DEVICE_DIALOG_ERROR);
|
||||
// Apply visible state.
|
||||
if (visible)
|
||||
addDeviceDialogElement.style.visibility = "visible";
|
||||
else {
|
||||
addDeviceDialogElement.style.visibility = "hidden";
|
||||
addDeviceErrorElement.style.visibility = "hidden";
|
||||
}
|
||||
}
|
||||
|
||||
// Validates the device ID.
|
||||
function validateDeviceID(deviceID) {
|
||||
// Initialize variables.
|
||||
var addDeviceInputElement = document.getElementById(ID_ADD_DEVICE_DIALOG_INPUT);
|
||||
var addDeviceButtonElement = document.getElementById(ID_ADD_DEVICE_DIALOG_BUTTON);
|
||||
var addDeviceErrorElement = document.getElementById(ID_ADD_DEVICE_DIALOG_ERROR);
|
||||
var isValid = true;
|
||||
var error = ERROR_DEVICE_ID_INVALID;
|
||||
// Check if the device ID is valid.
|
||||
if (deviceID == null || deviceID == "") {
|
||||
isValid = false;
|
||||
error = ERROR_DEVICE_ID_EMPTY;
|
||||
} else
|
||||
isValid = deviceID.match(REGEX_DEVICE_ID) || deviceID.match(REGEX_DEVICE_MAC) || deviceID.match(REGEX_DEVICE_IMEI);
|
||||
// Update controls.
|
||||
if (isValid) {
|
||||
if (addDeviceButtonElement.classList.contains(CLASS_ADD_DEVICE_DIALOG_BUTTON_DISABLED))
|
||||
addDeviceButtonElement.classList.remove(CLASS_ADD_DEVICE_DIALOG_BUTTON_DISABLED);
|
||||
if (addDeviceInputElement.classList.contains(CLASS_ADD_DEVICE_DIALOG_INPUT_ERROR))
|
||||
addDeviceInputElement.classList.remove(CLASS_ADD_DEVICE_DIALOG_INPUT_ERROR);
|
||||
addDeviceErrorElement.innerHTML = " ";
|
||||
addDeviceErrorElement.style.visibility = "hidden";
|
||||
} else {
|
||||
if (!addDeviceButtonElement.classList.contains(CLASS_ADD_DEVICE_DIALOG_BUTTON_DISABLED))
|
||||
addDeviceButtonElement.classList.add(CLASS_ADD_DEVICE_DIALOG_BUTTON_DISABLED);
|
||||
if (!addDeviceInputElement.classList.contains(CLASS_ADD_DEVICE_DIALOG_INPUT_ERROR))
|
||||
addDeviceInputElement.classList.add(CLASS_ADD_DEVICE_DIALOG_INPUT_ERROR);
|
||||
addDeviceErrorElement.innerHTML = error;
|
||||
addDeviceErrorElement.style.visibility = "visible";
|
||||
}
|
||||
}
|
||||
|
||||
// Handles what happens when the "Register device" button is pressed.
|
||||
function onRegisterDevice() {
|
||||
// Initialize variables.
|
||||
var addDeviceInputElement = document.getElementById(ID_ADD_DEVICE_DIALOG_INPUT);
|
||||
var provisionValue = addDeviceInputElement.value;
|
||||
var provisionType = "";
|
||||
// Determine provision type.
|
||||
if (provisionValue.match(REGEX_DEVICE_ID))
|
||||
provisionType = PROVISION_TYPE_ID;
|
||||
else if (provisionValue.match(REGEX_DEVICE_MAC))
|
||||
provisionType = PROVISION_TYPE_MAC;
|
||||
else if (provisionValue.match(REGEX_DEVICE_IMEI))
|
||||
provisionType = PROVISION_TYPE_IMEI;
|
||||
else {
|
||||
toastr.error(ERROR_INVALID_PROVISION_VALUE);
|
||||
return;
|
||||
}
|
||||
// Register the device.
|
||||
registerDevice(provisionValue, provisionType);
|
||||
}
|
||||
|
||||
// Registers the given device ID.
|
||||
function registerDevice(provisionValue, provisionType) {
|
||||
// Disable the refresh button.
|
||||
document.getElementById(ID_REFRESH_BUTTON).disabled = true;
|
||||
// Disable the continue button.
|
||||
document.getElementById(ID_CONTINUE_BUTTON).disabled = true;
|
||||
// Close the add device dialog.
|
||||
closeAddDeviceDialog();
|
||||
// Hide info dialog.
|
||||
showInfoPopup(false);
|
||||
// Show loading dialog.
|
||||
showLoadingPopup(true, MESSAGE_REGISTERING_DEVICE);
|
||||
// Send the request.
|
||||
$.post(
|
||||
"../ajax/register_device",
|
||||
JSON.stringify({
|
||||
"provision_value": provisionValue,
|
||||
"provision_type": provisionType
|
||||
}),
|
||||
function(data) {
|
||||
// Hide the loading panel.
|
||||
showLoadingPopup(false);
|
||||
// Process answer.
|
||||
processRegisterDeviceAnswer(data);
|
||||
}
|
||||
).fail(function(response) {
|
||||
// Hide the loading panel.
|
||||
showLoadingPopup(false);
|
||||
// Process error.
|
||||
processAjaxErrorResponse(response);
|
||||
// Enable the refresh button.
|
||||
document.getElementById(ID_REFRESH_BUTTON).disabled = false;
|
||||
// Refresh the continue button.
|
||||
updateContinueButton();
|
||||
});
|
||||
}
|
||||
|
||||
// Processes the answer of the register device request.
|
||||
function processRegisterDeviceAnswer(answer) {
|
||||
// Check if there was any error in the request.
|
||||
if (checkErrorResponse(answer, false)) {
|
||||
// Enable the refresh button.
|
||||
document.getElementById(ID_REFRESH_BUTTON).disabled = false;
|
||||
// Refresh the continue button.
|
||||
updateContinueButton();
|
||||
} else {
|
||||
// Update the device list.
|
||||
listDevices();
|
||||
}
|
||||
}
|
||||
|
||||
// Class that represents a ConnectCore device.
|
||||
class ConnectCoreDevice {
|
||||
|
||||
// Variables
|
||||
// Board image.
|
||||
BOARD_IMAGE;
|
||||
BOARD_IMAGE_SCALE;
|
||||
|
||||
// CPU panel.
|
||||
CPU_COMPONENT_VISIBLE = false;
|
||||
CPU_COMPONENT_HAS_PANEL = false;
|
||||
CPU_COMPONENT_HAS_ARROW = false;
|
||||
CPU_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
CPU_COMPONENT_PANEL_ORIENTATION = VALUE_TOP;
|
||||
CPU_COMPONENT_PANEL_HORIZONTAL_PERCENT = 0;
|
||||
CPU_COMPONENT_PANEL_VERTICAL_PERCENT = 0;
|
||||
CPU_COMPONENT_ARROW_PERCENT = 0;
|
||||
CPU_COMPONENT_AREA_TOP_PERCENT = 0;
|
||||
CPU_COMPONENT_AREA_LEFT_PERCENT = 0;
|
||||
CPU_COMPONENT_AREA_WIDTH_PERCENT = 0;
|
||||
CPU_COMPONENT_AREA_HEIGHT_PERCENT = 0;
|
||||
|
||||
// Memory panel.
|
||||
MEMORY_COMPONENT_VISIBLE = false;
|
||||
MEMORY_COMPONENT_HAS_PANEL = false;
|
||||
MEMORY_COMPONENT_HAS_ARROW = false;
|
||||
MEMORY_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
MEMORY_COMPONENT_PANEL_ORIENTATION = VALUE_TOP;
|
||||
MEMORY_COMPONENT_PANEL_HORIZONTAL_PERCENT = 0;
|
||||
MEMORY_COMPONENT_PANEL_VERTICAL_PERCENT = 0;
|
||||
MEMORY_COMPONENT_ARROW_PERCENT = 0;
|
||||
MEMORY_COMPONENT_AREA_TOP_PERCENT = 0;
|
||||
MEMORY_COMPONENT_AREA_LEFT_PERCENT = 0;
|
||||
MEMORY_COMPONENT_AREA_WIDTH_PERCENT = 0;
|
||||
MEMORY_COMPONENT_AREA_HEIGHT_PERCENT = 0;
|
||||
|
||||
// WiFi/BT panel.
|
||||
WIFI_BT_COMPONENT_VISIBLE = false;
|
||||
WIFI_BT_COMPONENT_HAS_PANEL = false;
|
||||
WIFI_BT_COMPONENT_HAS_ARROW = false;
|
||||
WIFI_BT_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
WIFI_BT_COMPONENT_PANEL_ORIENTATION = VALUE_TOP;
|
||||
WIFI_BT_COMPONENT_PANEL_HORIZONTAL_PERCENT = 0;
|
||||
WIFI_BT_COMPONENT_PANEL_VERTICAL_PERCENT = 0;
|
||||
WIFI_BT_COMPONENT_ARROW_PERCENT = 0;
|
||||
WIFI_BT_COMPONENT_AREA_TOP_PERCENT = 0;
|
||||
WIFI_BT_COMPONENT_AREA_LEFT_PERCENT = 0;
|
||||
WIFI_BT_COMPONENT_AREA_WIDTH_PERCENT = 0;
|
||||
WIFI_BT_COMPONENT_AREA_HEIGHT_PERCENT = 0;
|
||||
|
||||
// Ethernet panel.
|
||||
ETHERNET_COMPONENT_VISIBLE = false;
|
||||
ETHERNET_COMPONENT_HAS_PANEL = false;
|
||||
ETHERNET_COMPONENT_HAS_ARROW = false;
|
||||
ETHERNET_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
ETHERNET_COMPONENT_PANEL_ORIENTATION = VALUE_TOP;
|
||||
ETHERNET_COMPONENT_PANEL_HORIZONTAL_PERCENT = 0;
|
||||
ETHERNET_COMPONENT_PANEL_VERTICAL_PERCENT = 0;
|
||||
ETHERNET_COMPONENT_ARROW_PERCENT = 0;
|
||||
ETHERNET_COMPONENT_AREA_TOP_PERCENT = 0;
|
||||
ETHERNET_COMPONENT_AREA_LEFT_PERCENT = 0;
|
||||
ETHERNET_COMPONENT_AREA_WIDTH_PERCENT = 0;
|
||||
ETHERNET_COMPONENT_AREA_HEIGHT_PERCENT = 0;
|
||||
|
||||
// Console.
|
||||
CONSOLE_COMPONENT_VISIBLE = false;
|
||||
CONSOLE_COMPONENT_HAS_PANEL = false;
|
||||
CONSOLE_COMPONENT_HAS_ARROW = false;
|
||||
CONSOLE_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
CONSOLE_COMPONENT_PANEL_ORIENTATION = VALUE_TOP;
|
||||
CONSOLE_COMPONENT_PANEL_HORIZONTAL_PERCENT = 0;
|
||||
CONSOLE_COMPONENT_PANEL_VERTICAL_PERCENT = 0;
|
||||
CONSOLE_COMPONENT_ARROW_PERCENT = 0;
|
||||
CONSOLE_COMPONENT_AREA_TOP_PERCENT = 0;
|
||||
CONSOLE_COMPONENT_AREA_LEFT_PERCENT = 0;
|
||||
CONSOLE_COMPONENT_AREA_WIDTH_PERCENT = 0;
|
||||
CONSOLE_COMPONENT_AREA_HEIGHT_PERCENT = 0;
|
||||
|
||||
// Video panel.
|
||||
VIDEO_COMPONENT_VISIBLE = false;
|
||||
VIDEO_COMPONENT_HAS_PANEL = false;
|
||||
VIDEO_COMPONENT_HAS_ARROW = false;
|
||||
VIDEO_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
VIDEO_COMPONENT_PANEL_ORIENTATION = VALUE_TOP;
|
||||
VIDEO_COMPONENT_PANEL_HORIZONTAL_PERCENT = 0;
|
||||
VIDEO_COMPONENT_PANEL_VERTICAL_PERCENT = 0;
|
||||
VIDEO_COMPONENT_ARROW_PERCENT = 0;
|
||||
VIDEO_COMPONENT_AREA_TOP_PERCENT = 0;
|
||||
VIDEO_COMPONENT_AREA_LEFT_PERCENT = 0;
|
||||
VIDEO_COMPONENT_AREA_WIDTH_PERCENT = 0;
|
||||
VIDEO_COMPONENT_AREA_HEIGHT_PERCENT = 0;
|
||||
|
||||
// Audio panel.
|
||||
AUDIO_COMPONENT_VISIBLE = false;
|
||||
AUDIO_COMPONENT_HAS_PANEL = false;
|
||||
AUDIO_COMPONENT_HAS_ARROW = false;
|
||||
AUDIO_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
AUDIO_COMPONENT_PANEL_ORIENTATION = VALUE_TOP;
|
||||
AUDIO_COMPONENT_PANEL_HORIZONTAL_PERCENT = 0;
|
||||
AUDIO_COMPONENT_PANEL_VERTICAL_PERCENT = 0;
|
||||
AUDIO_COMPONENT_ARROW_PERCENT = 0;
|
||||
AUDIO_COMPONENT_AREA_TOP_PERCENT = 0;
|
||||
AUDIO_COMPONENT_AREA_LEFT_PERCENT = 0;
|
||||
AUDIO_COMPONENT_AREA_WIDTH_PERCENT = 0;
|
||||
AUDIO_COMPONENT_AREA_HEIGHT_PERCENT = 0;
|
||||
|
||||
// LED panel.
|
||||
LED_COMPONENT_VISIBLE = false;
|
||||
LED_COMPONENT_HAS_PANEL = false;
|
||||
LED_COMPONENT_HAS_ARROW = false;
|
||||
LED_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
LED_COMPONENT_PANEL_ORIENTATION = VALUE_TOP;
|
||||
LED_COMPONENT_PANEL_HORIZONTAL_PERCENT = 0;
|
||||
LED_COMPONENT_PANEL_VERTICAL_PERCENT = 0;
|
||||
LED_COMPONENT_ARROW_PERCENT = 0;
|
||||
LED_COMPONENT_AREA_TOP_PERCENT = 0;
|
||||
LED_COMPONENT_AREA_LEFT_PERCENT = 0;
|
||||
LED_COMPONENT_AREA_WIDTH_PERCENT = 0;
|
||||
LED_COMPONENT_AREA_HEIGHT_PERCENT = 0;
|
||||
|
||||
// Flash memory panel.
|
||||
FLASH_MEMORY_COMPONENT_VISIBLE = false;
|
||||
FLASH_MEMORY_COMPONENT_HAS_PANEL = false;
|
||||
FLASH_MEMORY_COMPONENT_HAS_ARROW = false;
|
||||
FLASH_MEMORY_COMPONENT_PANEL_ALWAYS_VISIBLE = false;
|
||||
FLASH_MEMORY_COMPONENT_PANEL_ORIENTATION = VALUE_TOP;
|
||||
FLASH_MEMORY_COMPONENT_PANEL_HORIZONTAL_PERCENT = 0;
|
||||
FLASH_MEMORY_COMPONENT_PANEL_VERTICAL_PERCENT = 0;
|
||||
FLASH_MEMORY_COMPONENT_ARROW_PERCENT = 0;
|
||||
FLASH_MEMORY_COMPONENT_AREA_TOP_PERCENT = 0;
|
||||
FLASH_MEMORY_COMPONENT_AREA_LEFT_PERCENT = 0;
|
||||
FLASH_MEMORY_COMPONENT_AREA_WIDTH_PERCENT = 0;
|
||||
FLASH_MEMORY_COMPONENT_AREA_HEIGHT_PERCENT = 0;
|
||||
|
||||
// Capabilities
|
||||
SUPPORTS_VIDEO_BRIGHTNESS;
|
||||
|
||||
// Device information.
|
||||
#deviceType;
|
||||
#platformName;
|
||||
#deviceID;
|
||||
#serialNumber;
|
||||
#ubootVersion;
|
||||
#kernelVersion;
|
||||
#deyVersion;
|
||||
#moduleVariant;
|
||||
#boardVariant;
|
||||
#boardID;
|
||||
#mcaHWVersion;
|
||||
#mcaFWVersion;
|
||||
#ethernetMAC;
|
||||
#ethernetIP;
|
||||
#wifiMAC;
|
||||
#wifiIP;
|
||||
#bluetoothMAC;
|
||||
#memoryTotal;
|
||||
#flashSize;
|
||||
#videoResolution;
|
||||
#sampleRate;
|
||||
#numSamplesUpload;
|
||||
|
||||
// Class constructor.
|
||||
constructor(deviceType, platformName, deviceID, deviceData) {
|
||||
this.#deviceType = deviceType;
|
||||
this.#platformName = platformName;
|
||||
this.#deviceID = deviceID;
|
||||
this.#initDevice(deviceData);
|
||||
}
|
||||
|
||||
// Initializes the device with the provided data.
|
||||
#initDevice(deviceData) {
|
||||
this.#ubootVersion = deviceData[ID_UBOOT_VERSION];
|
||||
this.#serialNumber = deviceData[ID_SERIAL_NUMBER];
|
||||
this.#kernelVersion = deviceData[ID_KERNEL_VERSION];
|
||||
this.#deyVersion = deviceData[ID_DEY_VERSION];
|
||||
this.#moduleVariant = deviceData[ID_MODULE_VARIANT];
|
||||
this.#boardVariant = deviceData[ID_BOARD_VARIANT];
|
||||
this.#boardID = deviceData[ID_BOARD_ID];
|
||||
this.#mcaHWVersion = deviceData[ID_MCA_HW_VERSION];
|
||||
this.#mcaFWVersion = deviceData[ID_MCA_FW_VERSION];
|
||||
this.#ethernetMAC = deviceData[ID_ETHERNET_MAC];
|
||||
this.#ethernetIP = deviceData[ID_ETHERNET_IP];
|
||||
this.#wifiMAC = deviceData[ID_WIFI_MAC];
|
||||
this.#wifiIP = deviceData[ID_WIFI_IP];
|
||||
this.#bluetoothMAC = deviceData[ID_BLUETOOTH_MAC];
|
||||
this.#memoryTotal = deviceData[ID_MEMORY_TOTAL];
|
||||
this.#flashSize = deviceData[ID_FLASH_SIZE];
|
||||
this.#videoResolution = deviceData[ID_VIDEO_RESOLUTION];
|
||||
this.#sampleRate = deviceData[ID_SAMPLE_RATE];
|
||||
this.#numSamplesUpload = deviceData[ID_NUM_SAMPLES_UPLOAD];
|
||||
}
|
||||
|
||||
// Returns the device type.
|
||||
getDeviceType() {
|
||||
return this.#deviceType;
|
||||
}
|
||||
|
||||
// Returns the platform name.
|
||||
getPlatformName() {
|
||||
return this.#platformName;
|
||||
}
|
||||
|
||||
// Returns the device serial number.
|
||||
getSerialNumber() {
|
||||
return this.#serialNumber;
|
||||
}
|
||||
|
||||
// Returns the device ID.
|
||||
getDeviceID() {
|
||||
return this.#deviceID;
|
||||
}
|
||||
|
||||
// Returns the board image file name.
|
||||
getBoardImage() {
|
||||
return this.BOARD_IMAGE;
|
||||
}
|
||||
|
||||
// Returns the board image scale.
|
||||
getBoardImageScale() {
|
||||
return this.BOARD_IMAGE_SCALE;
|
||||
}
|
||||
|
||||
// Returns the device U-Boot version.
|
||||
getUbootVersion() {
|
||||
return this.#ubootVersion;
|
||||
}
|
||||
|
||||
// Returns the device Kernel version.
|
||||
getKernelVersion() {
|
||||
return this.#kernelVersion;
|
||||
}
|
||||
|
||||
// Returns the device DEY version.
|
||||
getDEYVersion() {
|
||||
return this.#deyVersion;
|
||||
}
|
||||
|
||||
// Returns the device module variant.
|
||||
getModuleVariant() {
|
||||
return this.#moduleVariant;
|
||||
}
|
||||
|
||||
// Returns the device board variant.
|
||||
getBoardVariant() {
|
||||
return this.#boardVariant;
|
||||
}
|
||||
|
||||
// Returns the device board ID.
|
||||
getBoardID() {
|
||||
return this.#boardID;
|
||||
}
|
||||
|
||||
// Returns the device MCA hardware version.
|
||||
getMCAHWVersion() {
|
||||
return this.#mcaHWVersion;
|
||||
}
|
||||
|
||||
// Returns the device MCA firmware version.
|
||||
getMCAFWVersion() {
|
||||
return this.#mcaFWVersion;
|
||||
}
|
||||
|
||||
// Returns the device Ethernet MAC address.
|
||||
getEthernetMAC() {
|
||||
return this.#ethernetMAC;
|
||||
}
|
||||
|
||||
// Returns the device Ethernet IP address.
|
||||
getEthernetIP() {
|
||||
return this.#ethernetIP;
|
||||
}
|
||||
|
||||
// Returns the device WiFi MAC address.
|
||||
getWifiMAC() {
|
||||
return this.#wifiMAC;
|
||||
}
|
||||
|
||||
// Returns the device WiFi IP address.
|
||||
getWifiIP() {
|
||||
return this.#wifiIP;
|
||||
}
|
||||
|
||||
// Returns the device Bluetooth MAC address.
|
||||
getBluetoothMAC() {
|
||||
return this.#bluetoothMAC;
|
||||
}
|
||||
|
||||
// Returns the device total memory.
|
||||
getMemoryTotal() {
|
||||
return this.#memoryTotal;
|
||||
}
|
||||
|
||||
// Returns the device flash size.
|
||||
getFlashSize() {
|
||||
return this.#flashSize;
|
||||
}
|
||||
|
||||
// Returns the video resolution.
|
||||
getVideoResolution() {
|
||||
return this.#videoResolution;
|
||||
}
|
||||
|
||||
// Returns the sample rate.
|
||||
getSampleRate() {
|
||||
return this.#sampleRate;
|
||||
}
|
||||
|
||||
// Returns the number of samples to upload each batch.
|
||||
getNumSamplesUpload() {
|
||||
return this.#numSamplesUpload;
|
||||
}
|
||||
|
||||
// Returns the CPU panel data.
|
||||
getCPUComponentData() {
|
||||
return JSON.parse(TEMPLATE_COMPONENT_DATA.format(this.CPU_COMPONENT_VISIBLE,
|
||||
this.CPU_COMPONENT_HAS_PANEL,
|
||||
this.CPU_COMPONENT_HAS_ARROW,
|
||||
this.CPU_COMPONENT_PANEL_ALWAYS_VISIBLE,
|
||||
this.CPU_COMPONENT_PANEL_ORIENTATION,
|
||||
this.CPU_COMPONENT_PANEL_HORIZONTAL_PERCENT,
|
||||
this.CPU_COMPONENT_PANEL_VERTICAL_PERCENT,
|
||||
this.CPU_COMPONENT_ARROW_PERCENT,
|
||||
this.CPU_COMPONENT_AREA_TOP_PERCENT,
|
||||
this.CPU_COMPONENT_AREA_LEFT_PERCENT,
|
||||
this.CPU_COMPONENT_AREA_WIDTH_PERCENT,
|
||||
this.CPU_COMPONENT_AREA_HEIGHT_PERCENT));
|
||||
}
|
||||
|
||||
// Returns the Memory panel data.
|
||||
getMemoryComponentData() {
|
||||
return JSON.parse(TEMPLATE_COMPONENT_DATA.format(this.MEMORY_COMPONENT_VISIBLE,
|
||||
this.MEMORY_COMPONENT_HAS_PANEL,
|
||||
this.MEMORY_COMPONENT_HAS_ARROW,
|
||||
this.MEMORY_COMPONENT_PANEL_ALWAYS_VISIBLE,
|
||||
this.MEMORY_COMPONENT_PANEL_ORIENTATION,
|
||||
this.MEMORY_COMPONENT_PANEL_HORIZONTAL_PERCENT,
|
||||
this.MEMORY_COMPONENT_PANEL_VERTICAL_PERCENT,
|
||||
this.MEMORY_COMPONENT_ARROW_PERCENT,
|
||||
this.MEMORY_COMPONENT_AREA_TOP_PERCENT,
|
||||
this.MEMORY_COMPONENT_AREA_LEFT_PERCENT,
|
||||
this.MEMORY_COMPONENT_AREA_WIDTH_PERCENT,
|
||||
this.MEMORY_COMPONENT_AREA_HEIGHT_PERCENT));
|
||||
}
|
||||
|
||||
// Returns the WiFi panel data.
|
||||
getWifiBtComponentData() {
|
||||
return JSON.parse(TEMPLATE_COMPONENT_DATA.format(this.WIFI_BT_COMPONENT_VISIBLE,
|
||||
this.WIFI_BT_COMPONENT_HAS_PANEL,
|
||||
this.WIFI_BT_COMPONENT_HAS_ARROW,
|
||||
this.WIFI_BT_COMPONENT_PANEL_ALWAYS_VISIBLE,
|
||||
this.WIFI_BT_COMPONENT_PANEL_ORIENTATION,
|
||||
this.WIFI_BT_COMPONENT_PANEL_HORIZONTAL_PERCENT,
|
||||
this.WIFI_BT_COMPONENT_PANEL_VERTICAL_PERCENT,
|
||||
this.WIFI_BT_COMPONENT_ARROW_PERCENT,
|
||||
this.WIFI_BT_COMPONENT_AREA_TOP_PERCENT,
|
||||
this.WIFI_BT_COMPONENT_AREA_LEFT_PERCENT,
|
||||
this.WIFI_BT_COMPONENT_AREA_WIDTH_PERCENT,
|
||||
this.WIFI_BT_COMPONENT_AREA_HEIGHT_PERCENT));
|
||||
}
|
||||
|
||||
// Returns the Ethernet panel data.
|
||||
getEthernetComponentData() {
|
||||
return JSON.parse(TEMPLATE_COMPONENT_DATA.format(this.ETHERNET_COMPONENT_VISIBLE,
|
||||
this.ETHERNET_COMPONENT_HAS_PANEL,
|
||||
this.ETHERNET_COMPONENT_HAS_ARROW,
|
||||
this.ETHERNET_COMPONENT_PANEL_ALWAYS_VISIBLE,
|
||||
this.ETHERNET_COMPONENT_PANEL_ORIENTATION,
|
||||
this.ETHERNET_COMPONENT_PANEL_HORIZONTAL_PERCENT,
|
||||
this.ETHERNET_COMPONENT_PANEL_VERTICAL_PERCENT,
|
||||
this.ETHERNET_COMPONENT_ARROW_PERCENT,
|
||||
this.ETHERNET_COMPONENT_AREA_TOP_PERCENT,
|
||||
this.ETHERNET_COMPONENT_AREA_LEFT_PERCENT,
|
||||
this.ETHERNET_COMPONENT_AREA_WIDTH_PERCENT,
|
||||
this.ETHERNET_COMPONENT_AREA_HEIGHT_PERCENT));
|
||||
}
|
||||
|
||||
// Returns the Console panel data.
|
||||
getConsoleComponentData() {
|
||||
return JSON.parse(TEMPLATE_COMPONENT_DATA.format(this.CONSOLE_COMPONENT_VISIBLE,
|
||||
this.CONSOLE_COMPONENT_HAS_PANEL,
|
||||
this.CONSOLE_COMPONENT_HAS_ARROW,
|
||||
this.CONSOLE_COMPONENT_PANEL_ALWAYS_VISIBLE,
|
||||
this.CONSOLE_COMPONENT_PANEL_ORIENTATION,
|
||||
this.CONSOLE_COMPONENT_PANEL_HORIZONTAL_PERCENT,
|
||||
this.CONSOLE_COMPONENT_PANEL_VERTICAL_PERCENT,
|
||||
this.CONSOLE_COMPONENT_ARROW_PERCENT,
|
||||
this.CONSOLE_COMPONENT_AREA_TOP_PERCENT,
|
||||
this.CONSOLE_COMPONENT_AREA_LEFT_PERCENT,
|
||||
this.CONSOLE_COMPONENT_AREA_WIDTH_PERCENT,
|
||||
this.CONSOLE_COMPONENT_AREA_HEIGHT_PERCENT));
|
||||
}
|
||||
|
||||
// Returns the Video panel data.
|
||||
getVideoComponentData() {
|
||||
return JSON.parse(TEMPLATE_COMPONENT_DATA.format(this.VIDEO_COMPONENT_VISIBLE,
|
||||
this.VIDEO_COMPONENT_HAS_PANEL,
|
||||
this.VIDEO_COMPONENT_HAS_ARROW,
|
||||
this.VIDEO_COMPONENT_PANEL_ALWAYS_VISIBLE,
|
||||
this.VIDEO_COMPONENT_PANEL_ORIENTATION,
|
||||
this.VIDEO_COMPONENT_PANEL_HORIZONTAL_PERCENT,
|
||||
this.VIDEO_COMPONENT_PANEL_VERTICAL_PERCENT,
|
||||
this.VIDEO_COMPONENT_ARROW_PERCENT,
|
||||
this.VIDEO_COMPONENT_AREA_TOP_PERCENT,
|
||||
this.VIDEO_COMPONENT_AREA_LEFT_PERCENT,
|
||||
this.VIDEO_COMPONENT_AREA_WIDTH_PERCENT,
|
||||
this.VIDEO_COMPONENT_AREA_HEIGHT_PERCENT));
|
||||
}
|
||||
|
||||
// Returns the Audio panel data.
|
||||
getAudioComponentData() {
|
||||
return JSON.parse(TEMPLATE_COMPONENT_DATA.format(this.AUDIO_COMPONENT_VISIBLE,
|
||||
this.AUDIO_COMPONENT_HAS_PANEL,
|
||||
this.AUDIO_COMPONENT_HAS_ARROW,
|
||||
this.AUDIO_COMPONENT_PANEL_ALWAYS_VISIBLE,
|
||||
this.AUDIO_COMPONENT_PANEL_ORIENTATION,
|
||||
this.AUDIO_COMPONENT_PANEL_HORIZONTAL_PERCENT,
|
||||
this.AUDIO_COMPONENT_PANEL_VERTICAL_PERCENT,
|
||||
this.AUDIO_COMPONENT_ARROW_PERCENT,
|
||||
this.AUDIO_COMPONENT_AREA_TOP_PERCENT,
|
||||
this.AUDIO_COMPONENT_AREA_LEFT_PERCENT,
|
||||
this.AUDIO_COMPONENT_AREA_WIDTH_PERCENT,
|
||||
this.AUDIO_COMPONENT_AREA_HEIGHT_PERCENT));
|
||||
}
|
||||
|
||||
// Returns the LED panel data.
|
||||
getLEDComponentData() {
|
||||
return JSON.parse(TEMPLATE_COMPONENT_DATA.format(this.LED_COMPONENT_VISIBLE,
|
||||
this.LED_COMPONENT_HAS_PANEL,
|
||||
this.LED_COMPONENT_HAS_ARROW,
|
||||
this.LED_COMPONENT_PANEL_ALWAYS_VISIBLE,
|
||||
this.LED_COMPONENT_PANEL_ORIENTATION,
|
||||
this.LED_COMPONENT_PANEL_HORIZONTAL_PERCENT,
|
||||
this.LED_COMPONENT_PANEL_VERTICAL_PERCENT,
|
||||
this.LED_COMPONENT_ARROW_PERCENT,
|
||||
this.LED_COMPONENT_AREA_TOP_PERCENT,
|
||||
this.LED_COMPONENT_AREA_LEFT_PERCENT,
|
||||
this.LED_COMPONENT_AREA_WIDTH_PERCENT,
|
||||
this.LED_COMPONENT_AREA_HEIGHT_PERCENT));
|
||||
}
|
||||
|
||||
// Returns the flash memory panel data.
|
||||
getFlashMemoryComponentData() {
|
||||
return JSON.parse(TEMPLATE_COMPONENT_DATA.format(this.FLASH_MEMORY_COMPONENT_VISIBLE,
|
||||
this.FLASH_MEMORY_COMPONENT_HAS_PANEL,
|
||||
this.FLASH_MEMORY_COMPONENT_HAS_ARROW,
|
||||
this.FLASH_MEMORY_COMPONENT_PANEL_ALWAYS_VISIBLE,
|
||||
this.FLASH_MEMORY_COMPONENT_PANEL_ORIENTATION,
|
||||
this.FLASH_MEMORY_COMPONENT_PANEL_HORIZONTAL_PERCENT,
|
||||
this.FLASH_MEMORY_COMPONENT_PANEL_VERTICAL_PERCENT,
|
||||
this.FLASH_MEMORY_COMPONENT_ARROW_PERCENT,
|
||||
this.FLASH_MEMORY_COMPONENT_AREA_TOP_PERCENT,
|
||||
this.FLASH_MEMORY_COMPONENT_AREA_LEFT_PERCENT,
|
||||
this.FLASH_MEMORY_COMPONENT_AREA_WIDTH_PERCENT,
|
||||
this.FLASH_MEMORY_COMPONENT_AREA_HEIGHT_PERCENT));
|
||||
}
|
||||
|
||||
// Returns whether the device supports video brightness or not.
|
||||
supportsVideoBrightness() {
|
||||
return this.SUPPORTS_VIDEO_BRIGHTNESS;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,653 @@
|
|||
/*
|
||||
* Copyright 2022, Digi International Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
// Constants.
|
||||
const ID_CURRENT_DIRECTORY = "current_directory";
|
||||
const ID_FILE_SYSTEM_CONTAINER = "filesystem_container";
|
||||
const ID_FILE_SYSTEM_DIR_NAME = "filesystem_directory_name";
|
||||
const ID_FILE_SYSTEM_DIR_NAME_BUTTON = "filesystem_directory_name_button";
|
||||
const ID_FILE_SYSTEM_DIR_NAME_ERROR = "filesystem_directory_name_error";
|
||||
const ID_FILE_SYSTEM_DIR_NAME_PANEL = "filesystem_dir_name_panel";
|
||||
const ID_FILE_SYSTEM_DOWNLOAD_FILE_BUTTON = "filesystem_download_file_button";
|
||||
const ID_FILE_SYSTEM_HEADER = "filesystem_header";
|
||||
const ID_FILE_SYSTEM_HOVER_BACKGROUND = "filesystem_hover_background";
|
||||
const ID_FILE_SYSTEM_ITEMS_CONTAINER = "filesystem_items_container";
|
||||
const ID_FILE_SYSTEM_ITEMS_HEADER = "filesystem_items_header";
|
||||
const ID_FILE_SYSTEM_LOADING = "filesystem_loading";
|
||||
const ID_FILE_SYSTEM_PANEL = "filesystem_panel";
|
||||
const ID_FILE_SYSTEM_REMOVE_FILE_BUTTON = "filesystem_remove_file_button";
|
||||
const ID_FILE_SYSTEM_TOOLBAR = "filesystem_toolbar";
|
||||
const ID_FILE_TO_UPLOAD = "file_to_upload";
|
||||
|
||||
const CLASS_FA_FILE = "fa-file";
|
||||
const CLASS_FILE_SYSTEM_BUTTON_DISABLED = "filesystem-button-disabled";
|
||||
const CLASS_FILE_SYSTEM_DIR_NAME_BUTTON_DISABLED = "filesystem-dir-name-button-disabled";
|
||||
const CLASS_FILE_SYSTEM_DIR_NAME_INPUT_ERROR = "filesystem-dir-name-input-error";
|
||||
const CLASS_FILE_SYSTEM_ENTRY_SELECTED = "filesystem-entry-selected";
|
||||
|
||||
const FS_TYPE_FILE = "file";
|
||||
const FS_TYPE_DIRECTORY = "dir";
|
||||
|
||||
const TITLE_CONFIRM_REMOVE = "Confirm Remove";
|
||||
|
||||
const MESSAGE_CONFIRM_REMOVE = "Are you sure you want to remove the selected file?";
|
||||
|
||||
const PREFIX_FS = "fs_";
|
||||
|
||||
const REGEX_DIRECTORY_NAME = '^[^\\s^\x00-\x1f\\?*:"";<>|\\/.][^\x00-\x1f\\?*:"";<>|\\/]*[^\\s^\x00-\x1f\\?*:"";<>|\\/.]+$';
|
||||
|
||||
const TEMPLATE_DIRECTORY = "" +
|
||||
"<div id='fs_{0}' class='filesystem-entry' title='{1}' onclick='listDirectory(\"{2}\")'>" +
|
||||
" <div class='fas fa-folder fa-lg filesystem-entry-icon'></div>" +
|
||||
" <div class='filesystem-entry-name'>{3}</div>" +
|
||||
" <div class='filesystem-entry-size'></div>" +
|
||||
" <div class='filesystem-entry-last-modified'>{4}</div>" +
|
||||
"</div>";
|
||||
const TEMPLATE_FILE = "" +
|
||||
"<div id='fs_{0}' class='filesystem-entry' title='{1}' onclick='selectFileSystemEntry(\"fs_{2}\")' ondblclick='downloadFile(\"{3}\")'>" +
|
||||
" <div class='fas fa-file fa-lg filesystem-entry-icon'></div>" +
|
||||
" <div class='filesystem-entry-name'>{4}</div>" +
|
||||
" <div class='filesystem-entry-size'>{5}</div>" +
|
||||
" <div class='filesystem-entry-last-modified'>{6}</div>" +
|
||||
"</div>";
|
||||
|
||||
const ERROR_DIR_NAME_EMPTY = "Directory name cannot be empty.";
|
||||
const ERROR_DIR_NAME_INVALID = "The entered name is not valid.";
|
||||
|
||||
const ROOT_DIRECTORY = "/";
|
||||
|
||||
// Variables.
|
||||
var currentDirectory = null;
|
||||
var selectedFileSystemEntry = null;
|
||||
var filesystemResizeObserver = null;
|
||||
|
||||
// Opens the file system panel.
|
||||
function openFileSystem() {
|
||||
// Check if the file system is showing.
|
||||
if (isFileSystemShowing())
|
||||
return;
|
||||
// Reset current directory.
|
||||
currentDirectory = null;
|
||||
$("#" + ID_CURRENT_DIRECTORY).text("");
|
||||
// Unselect all entries.
|
||||
unselectFileSystemEntries();
|
||||
// Clear all the file system entries.
|
||||
clearFileSystemEntries();
|
||||
// Setup resize observer.
|
||||
if (filesystemResizeObserver == null || filesystemResizeObserver == "undefined") {
|
||||
filesystemResizeObserver = new ResizeObserver((entries) => {
|
||||
resizeFileSystemElements();
|
||||
})
|
||||
filesystemResizeObserver.observe(document.getElementById(ID_FILE_SYSTEM_CONTAINER));
|
||||
}
|
||||
// Resize filesystem elements.
|
||||
resizeFileSystemElements();
|
||||
// Show the file system.
|
||||
var fileSystemContainer = document.getElementById(ID_FILE_SYSTEM_PANEL);
|
||||
fileSystemContainer.style.visibility = "visible";
|
||||
// List root directory.
|
||||
listDirectory(ROOT_DIRECTORY);
|
||||
}
|
||||
|
||||
// Returns whether the file system window is open or not.
|
||||
function isFileSystemShowing() {
|
||||
// Sanity checks.
|
||||
if (!isDashboardShowing())
|
||||
return false;
|
||||
// Initialize variables.
|
||||
var fileSystemContainer = document.getElementById(ID_FILE_SYSTEM_PANEL);
|
||||
// Return visibility.
|
||||
return fileSystemContainer.style.visibility == "visible";
|
||||
}
|
||||
|
||||
// Closes the file system panel.
|
||||
function closeFileSystem() {
|
||||
// Check if the file system is showing.
|
||||
if (!isFileSystemShowing())
|
||||
return;
|
||||
// Hide the loading panel.
|
||||
showFileSystemLoading(false);
|
||||
// Hide the file system panel.
|
||||
var fileSystemContainer = document.getElementById(ID_FILE_SYSTEM_PANEL);
|
||||
fileSystemContainer.style.visibility = "collapse";
|
||||
}
|
||||
|
||||
// Resizes the file system elements.
|
||||
function resizeFileSystemElements() {
|
||||
// If file system is not showing just return.
|
||||
if (!isFileSystemShowing())
|
||||
return;
|
||||
// Initialize variables.
|
||||
var filesystemContainer = document.getElementById(ID_FILE_SYSTEM_CONTAINER);
|
||||
var filesystemHeader = document.getElementById(ID_FILE_SYSTEM_HEADER);
|
||||
var filesystemToolbar = document.getElementById(ID_FILE_SYSTEM_TOOLBAR);
|
||||
var filesystemHoverBackground = document.getElementById(ID_FILE_SYSTEM_HOVER_BACKGROUND);
|
||||
var filesystemItemsHeader = document.getElementById(ID_FILE_SYSTEM_ITEMS_HEADER);
|
||||
var filesystemItemsContainer = document.getElementById(ID_FILE_SYSTEM_ITEMS_CONTAINER);
|
||||
var containerHeight = filesystemContainer.clientHeight;
|
||||
var headerHeight = filesystemHeader.clientHeight;
|
||||
var toolbarHeight = filesystemToolbar.clientHeight;
|
||||
var itemsHeaderHeight = filesystemItemsHeader.clientHeight;
|
||||
// Set correct heights.
|
||||
filesystemHoverBackground.style.height = (containerHeight - headerHeight).toString() + "px";
|
||||
filesystemItemsContainer.style.height = (containerHeight - headerHeight - toolbarHeight - itemsHeaderHeight).toString() + "px";
|
||||
}
|
||||
|
||||
// Shows/hides the file system loading panel.
|
||||
function showFileSystemLoading(visible) {
|
||||
// Initialize variables.
|
||||
var fileSystemHoverBackground = document.getElementById(ID_FILE_SYSTEM_HOVER_BACKGROUND);
|
||||
var fileSystemLoading = document.getElementById(ID_FILE_SYSTEM_LOADING);
|
||||
// Apply visibility.
|
||||
if (visible && isFileSystemShowing()) {
|
||||
fileSystemHoverBackground.style.visibility = "visible";
|
||||
fileSystemLoading.style.visibility = "visible";
|
||||
} else {
|
||||
fileSystemHoverBackground.style.visibility = "hidden";
|
||||
fileSystemLoading.style.visibility = "hidden";
|
||||
}
|
||||
}
|
||||
|
||||
// Clears all the file system entries.
|
||||
function clearFileSystemEntries() {
|
||||
// Initialize variables.
|
||||
var fileSystemEntriesDiv = document.getElementById(ID_FILE_SYSTEM_ITEMS_CONTAINER);
|
||||
// Clear all file system HTML entries.
|
||||
while (fileSystemEntriesDiv.firstChild)
|
||||
fileSystemEntriesDiv.removeChild(fileSystemEntriesDiv.lastChild);
|
||||
}
|
||||
|
||||
// Enables/disabled the file system buttons.
|
||||
function enableFileSystemButtons(enable) {
|
||||
// Initialize variables.
|
||||
var downloadButton = document.getElementById(ID_FILE_SYSTEM_DOWNLOAD_FILE_BUTTON);
|
||||
var removeButton = document.getElementById(ID_FILE_SYSTEM_REMOVE_FILE_BUTTON);
|
||||
// Apply enable state.
|
||||
if (!enable) {
|
||||
downloadButton.disabled = true;
|
||||
removeButton.disabled = true;
|
||||
downloadButton.classList.add(CLASS_FILE_SYSTEM_BUTTON_DISABLED)
|
||||
removeButton.classList.add(CLASS_FILE_SYSTEM_BUTTON_DISABLED)
|
||||
} else {
|
||||
downloadButton.disabled = false;
|
||||
removeButton.disabled = false;
|
||||
downloadButton.classList.remove(CLASS_FILE_SYSTEM_BUTTON_DISABLED)
|
||||
removeButton.classList.remove(CLASS_FILE_SYSTEM_BUTTON_DISABLED)
|
||||
}
|
||||
}
|
||||
|
||||
// Selects the given file system entry.
|
||||
function selectFileSystemEntry(entryID) {
|
||||
// Unselect all entries.
|
||||
unselectFileSystemEntries();
|
||||
// Set selected style to the selected device div.
|
||||
var entryElement = document.getElementById(entryID)
|
||||
if (entryElement != null)
|
||||
entryElement.classList.add(CLASS_FILE_SYSTEM_ENTRY_SELECTED);
|
||||
// Save selected entry.
|
||||
selectedFileSystemEntry = entryID;
|
||||
// Enable buttons.
|
||||
enableFileSystemButtons(true);
|
||||
}
|
||||
|
||||
// Unselects all the file system entries.
|
||||
function unselectFileSystemEntries() {
|
||||
// Initialize variables.
|
||||
var fileSystemEntriesDiv = document.getElementById(ID_FILE_SYSTEM_ITEMS_CONTAINER);
|
||||
var children = fileSystemEntriesDiv.children;
|
||||
// Remove selected class from all entries.
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var entry = children[i].children[0];
|
||||
entry.classList.remove(CLASS_FILE_SYSTEM_ENTRY_SELECTED);
|
||||
}
|
||||
// Reset selected entry.
|
||||
selectedFileSystemEntry = null;
|
||||
// Disable buttons.
|
||||
enableFileSystemButtons(false);
|
||||
}
|
||||
|
||||
// Lists the contents of the given directory.
|
||||
function listDirectory(directory) {
|
||||
// Build path.
|
||||
var path = currentDirectory;
|
||||
if (path == null || currentDirectory == directory)
|
||||
path = directory;
|
||||
else {
|
||||
if (directory.endsWith("..")) {
|
||||
// Check if last slash must be removed.
|
||||
if (path.endsWith("/") && path.length > ROOT_DIRECTORY.length)
|
||||
path = path.substring(0, path.lastIndexOf("/"));
|
||||
// Move one directory up.
|
||||
path = path.substring(0, path.lastIndexOf("/"));
|
||||
if (path == "")
|
||||
path = ROOT_DIRECTORY;
|
||||
} else {
|
||||
if (!path.endsWith("/"))
|
||||
path = path + "/"
|
||||
path = path + directory;
|
||||
}
|
||||
}
|
||||
// Show loading panel.
|
||||
showFileSystemLoading(true);
|
||||
// Send request.
|
||||
$.post(
|
||||
"../ajax/fs_list_directory",
|
||||
JSON.stringify({
|
||||
"device_id": getDeviceID(),
|
||||
"directory": path
|
||||
}),
|
||||
function(data) {
|
||||
// Process only if the file system window is showing.
|
||||
if (!isFileSystemShowing())
|
||||
return;
|
||||
// Process answer.
|
||||
processListDirectoryResponse(data);
|
||||
}
|
||||
).fail(function(response) {
|
||||
// Process only if the file system window is showing.
|
||||
if (!isFileSystemShowing())
|
||||
return;
|
||||
// Process error.
|
||||
processAjaxErrorResponse(response);
|
||||
// Hide the loading status.
|
||||
showFileSystemLoading(false);
|
||||
});
|
||||
}
|
||||
|
||||
// Processes the list directory response.
|
||||
function processListDirectoryResponse(response) {
|
||||
// Check if there was any error in the request.
|
||||
if (checkErrorResponse(response, false)) {
|
||||
// Hide the loading status.
|
||||
showFileSystemLoading(false);
|
||||
// Do not continue.
|
||||
return;
|
||||
}
|
||||
// Unselect all entries.
|
||||
unselectFileSystemEntries();
|
||||
// Clear all file system entries.
|
||||
clearFileSystemEntries();
|
||||
// Set current directory.
|
||||
currentDirectory = response[ID_CURRENT_DIR];
|
||||
if (!currentDirectory.endsWith("/"))
|
||||
currentDirectory = currentDirectory + "/";
|
||||
$("#" + ID_CURRENT_DIRECTORY).text(currentDirectory);
|
||||
// Iterate over all the file system received entries.
|
||||
if (response[ID_FILES] != null && response[ID_FILES] != "undefined") {
|
||||
var fileSystemEntriesDiv = document.getElementById(ID_FILE_SYSTEM_ITEMS_CONTAINER);
|
||||
// Process response entries and create HTML elements.
|
||||
for (var entry of response[ID_FILES]) {
|
||||
var name = entry[ID_NAME].substring(currentDirectory.length);
|
||||
var lastModified = entry[ID_LAST_MODIFIED];
|
||||
if (lastModified == null || lastModified == "undefined" || lastModified == 0 || name == "..") {
|
||||
lastModified = "";
|
||||
} else {
|
||||
var date = new Date(0); // The 0 there is the key, which sets the date to the epoch
|
||||
date.setUTCSeconds(lastModified);
|
||||
lastModified = date.toLocaleString("en-US", { hour12:false });
|
||||
}
|
||||
var entryDiv = document.createElement("div");
|
||||
if (entry[ID_TYPE] == FS_TYPE_FILE)
|
||||
entryDiv.innerHTML = TEMPLATE_FILE.format(name, name, name, name, name,
|
||||
sizeToHumanRead(entry[ID_SIZE]), lastModified);
|
||||
else if (entry[ID_TYPE] == FS_TYPE_DIRECTORY)
|
||||
entryDiv.innerHTML = TEMPLATE_DIRECTORY.format(name, name, name, name, lastModified);
|
||||
if (entryDiv.innerHTML != null && entryDiv.innerHTML != "")
|
||||
fileSystemEntriesDiv.appendChild(entryDiv);
|
||||
}
|
||||
}
|
||||
// Hide the loading status.
|
||||
showFileSystemLoading(false);
|
||||
}
|
||||
|
||||
// Downloads the selected file.
|
||||
function downloadSelectedFile() {
|
||||
// Build file path.
|
||||
var filePath = selectedFileSystemEntry.substring(PREFIX_FS.length);
|
||||
// Execute the download action.
|
||||
downloadFile(filePath);
|
||||
}
|
||||
|
||||
// Attempts to download the given file name.
|
||||
function downloadFile(fileName) {
|
||||
// Build file path.
|
||||
var path = currentDirectory + fileName;
|
||||
// Show loading panel.
|
||||
showFileSystemLoading(true);
|
||||
// Prepare data.
|
||||
var data = JSON.stringify({
|
||||
"device_id": getDeviceID(),
|
||||
"path": path
|
||||
});
|
||||
// Send request
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: "../ajax/fs_download_file",
|
||||
data: data,
|
||||
cache: false,
|
||||
async: true,
|
||||
xhr: function() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState == 2) {
|
||||
if (xhr.status == 200)
|
||||
xhr.responseType = "blob";
|
||||
else
|
||||
xhr.responseType = "text";
|
||||
}
|
||||
};
|
||||
return xhr;
|
||||
},
|
||||
success: function(response) {
|
||||
// Process only if the file system window is showing.
|
||||
if (!isFileSystemShowing())
|
||||
return;
|
||||
// Process answer.
|
||||
processDownloadFileResponse(response);
|
||||
},
|
||||
error: function(response) {
|
||||
// Process only if the file system window is showing.
|
||||
if (!isFileSystemShowing())
|
||||
return;
|
||||
// Process error.
|
||||
processAjaxErrorResponse(response);
|
||||
// Hide the loading status.
|
||||
showFileSystemLoading(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Processes the download file response.
|
||||
function processDownloadFileResponse(response) {
|
||||
// Check if there was any error in the request.
|
||||
if (checkErrorResponse(response, false)) {
|
||||
// Hide the loading status.
|
||||
showFileSystemLoading(false);
|
||||
// Do not continue.
|
||||
return;
|
||||
}
|
||||
// Obtain file name.
|
||||
var fileName = selectedFileSystemEntry.substring(PREFIX_FS.length)
|
||||
// Convert the Byte Data to BLOB object.
|
||||
var blob = new Blob([response], { type: "application/octetstream" });
|
||||
// Check the Browser type and download the File.
|
||||
var isIE = false || !!document.documentMode;
|
||||
if (isIE) {
|
||||
window.navigator.msSaveBlob(blob, fileName);
|
||||
} else {
|
||||
var url = window.URL || window.webkitURL;
|
||||
link = url.createObjectURL(blob);
|
||||
var a = $("<a />");
|
||||
a.attr("download", fileName);
|
||||
a.attr("href", link);
|
||||
$("body").append(a);
|
||||
a[0].click();
|
||||
$("body").remove(a);
|
||||
}
|
||||
// Hide the loading status.
|
||||
showFileSystemLoading(false);
|
||||
}
|
||||
|
||||
// Asks user to confirm file removal.
|
||||
function askRemoveFile() {
|
||||
showConfirmDialog(TITLE_CONFIRM_REMOVE, MESSAGE_CONFIRM_REMOVE,
|
||||
function() {
|
||||
// Remove the file.
|
||||
removeSelectedFile();
|
||||
},
|
||||
function() {
|
||||
// Do nothing.
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Removes the selected file.
|
||||
function removeSelectedFile() {
|
||||
// Sanity checks.
|
||||
if (selectedFileSystemEntry == null)
|
||||
return;
|
||||
// Initialize variables.
|
||||
var isFile = false;
|
||||
var entryElement = document.getElementById(selectedFileSystemEntry);
|
||||
var iconElement = entryElement.children[0];
|
||||
// Check if it is a file.
|
||||
if (iconElement.classList.contains(CLASS_FA_FILE))
|
||||
isFile = true;
|
||||
// Execute action.
|
||||
removeFile(selectedFileSystemEntry.substring(PREFIX_FS.length), isFile);
|
||||
}
|
||||
|
||||
// Attempts to remove the given file name.
|
||||
function removeFile(fileName, isFile) {
|
||||
// Build file path.
|
||||
var path = currentDirectory + fileName;
|
||||
// Show loading panel.
|
||||
showFileSystemLoading(true);
|
||||
// Send request.
|
||||
$.post(
|
||||
"../ajax/fs_remove_file",
|
||||
JSON.stringify({
|
||||
"device_id": getDeviceID(),
|
||||
"path": path,
|
||||
"is_file": isFile
|
||||
}),
|
||||
function(data) {
|
||||
// Process only if the file system window is showing.
|
||||
if (!isFileSystemShowing())
|
||||
return;
|
||||
// Process answer.
|
||||
processRemoveFileResponse(data);
|
||||
}
|
||||
).fail(function(response) {
|
||||
// Process only if the file system window is showing.
|
||||
if (!isFileSystemShowing())
|
||||
return;
|
||||
// Process error.
|
||||
processAjaxErrorResponse(response);
|
||||
// Hide the loading status.
|
||||
showFileSystemLoading(false);
|
||||
});
|
||||
}
|
||||
|
||||
// Processes the remove file response.
|
||||
function processRemoveFileResponse(response) {
|
||||
// Check if there was any error in the request.
|
||||
if (checkErrorResponse(response, false)) {
|
||||
// Hide the loading status.
|
||||
showFileSystemLoading(false);
|
||||
} else {
|
||||
// List directory contents again.
|
||||
listDirectory(currentDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
// Opens the file browser.
|
||||
function openFileBrowser() {
|
||||
document.getElementById(ID_FILE_TO_UPLOAD).click();
|
||||
}
|
||||
|
||||
// Attempts to upload the given file.
|
||||
function uploadFile(file) {
|
||||
// Build file path.
|
||||
var path = currentDirectory + file.name;
|
||||
// Show loading panel.
|
||||
showFileSystemLoading(true);
|
||||
// Prepare data.
|
||||
var formData = new FormData();
|
||||
formData.append("device_id", getDeviceID());
|
||||
formData.append("path", path);
|
||||
formData.append("file", file);
|
||||
// Send request.
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: "../ajax/fs_upload_file",
|
||||
data: formData,
|
||||
cache: false,
|
||||
async: true,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
enctype: 'multipart/form-data',
|
||||
success: function(response) {
|
||||
// Process only if the file system window is showing.
|
||||
if (!isFileSystemShowing())
|
||||
return;
|
||||
// Process answer.
|
||||
processUploadFileResponse(response);
|
||||
},
|
||||
error: function(response) {
|
||||
// Process only if the file system window is showing.
|
||||
if (!isFileSystemShowing())
|
||||
return;
|
||||
// Process error.
|
||||
processAjaxErrorResponse(response);
|
||||
// Hide the loading status.
|
||||
showFileSystemLoading(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Processes the upload file response.
|
||||
function processUploadFileResponse(response) {
|
||||
// Check if there was any error in the request.
|
||||
if (checkErrorResponse(response, false)) {
|
||||
// Hide the loading status.
|
||||
showFileSystemLoading(false);
|
||||
} else {
|
||||
// List directory contents again.
|
||||
listDirectory(currentDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
// Opens the directory name panel.
|
||||
function openDirectoryNamePanel() {
|
||||
// Initialize variables.
|
||||
var fileSystemDirName = document.getElementById(ID_FILE_SYSTEM_DIR_NAME);
|
||||
var fileSystemDirNameButton = document.getElementById(ID_FILE_SYSTEM_DIR_NAME_BUTTON);
|
||||
var fileSystemDirNameError = document.getElementById(ID_FILE_SYSTEM_DIR_NAME_ERROR);
|
||||
// Reset panel state.
|
||||
fileSystemDirName.value = "";
|
||||
fileSystemDirNameError.innerHTML = " ";
|
||||
fileSystemDirNameError.style.visibility = "hidden";
|
||||
if (!fileSystemDirNameButton.classList.contains(CLASS_FILE_SYSTEM_DIR_NAME_BUTTON_DISABLED))
|
||||
fileSystemDirNameButton.classList.add(CLASS_FILE_SYSTEM_DIR_NAME_BUTTON_DISABLED);
|
||||
if (fileSystemDirName.classList.contains(CLASS_FILE_SYSTEM_DIR_NAME_INPUT_ERROR))
|
||||
fileSystemDirName.classList.remove(CLASS_FILE_SYSTEM_DIR_NAME_INPUT_ERROR);
|
||||
// Show panel.
|
||||
showDirectoryNamePanel(true);
|
||||
}
|
||||
|
||||
// Closes the directory name panel.
|
||||
function closeDirectoryNamePanel() {
|
||||
showDirectoryNamePanel(false);
|
||||
}
|
||||
|
||||
// Opens the directory name panel.
|
||||
function showDirectoryNamePanel(visible) {
|
||||
// Initialize variables.
|
||||
var fileSystemDirNamePanel = document.getElementById(ID_FILE_SYSTEM_DIR_NAME_PANEL);
|
||||
var fileSystemDirNameError = document.getElementById(ID_FILE_SYSTEM_DIR_NAME_ERROR);
|
||||
// Apply visible state.
|
||||
if (visible && isFileSystemShowing())
|
||||
fileSystemDirNamePanel.style.visibility = "visible";
|
||||
else {
|
||||
fileSystemDirNamePanel.style.visibility = "hidden";
|
||||
fileSystemDirNameError.style.visibility = "hidden";
|
||||
}
|
||||
}
|
||||
|
||||
// Handles what happens when the "Create directory" button is pressed.
|
||||
function onCreateDirectory() {
|
||||
var fileSystemDirName = document.getElementById(ID_FILE_SYSTEM_DIR_NAME);
|
||||
var dirName = fileSystemDirName.value;
|
||||
createDirectory(dirName);
|
||||
}
|
||||
|
||||
// Validates the directory name.
|
||||
function validateDirectoryName(dirName) {
|
||||
// Initialize variables.
|
||||
var fileSystemDirName = document.getElementById(ID_FILE_SYSTEM_DIR_NAME);
|
||||
var fileSystemDirNameButton = document.getElementById(ID_FILE_SYSTEM_DIR_NAME_BUTTON);
|
||||
var fileSystemDirNameError = document.getElementById(ID_FILE_SYSTEM_DIR_NAME_ERROR);
|
||||
var isValid = true;
|
||||
var error = ERROR_DIR_NAME_INVALID;
|
||||
// Check if the directory name is valid.
|
||||
if (dirName == null || dirName == "") {
|
||||
isValid = false;
|
||||
error = ERROR_DIR_NAME_EMPTY;
|
||||
} else
|
||||
isValid = dirName.match(REGEX_DIRECTORY_NAME);
|
||||
// Update controls.
|
||||
if (isValid) {
|
||||
if (fileSystemDirNameButton.classList.contains(CLASS_FILE_SYSTEM_DIR_NAME_BUTTON_DISABLED))
|
||||
fileSystemDirNameButton.classList.remove(CLASS_FILE_SYSTEM_DIR_NAME_BUTTON_DISABLED);
|
||||
if (fileSystemDirName.classList.contains(CLASS_FILE_SYSTEM_DIR_NAME_INPUT_ERROR))
|
||||
fileSystemDirName.classList.remove(CLASS_FILE_SYSTEM_DIR_NAME_INPUT_ERROR);
|
||||
fileSystemDirNameError.innerHTML = " ";
|
||||
fileSystemDirNameError.style.visibility = "hidden";
|
||||
} else {
|
||||
if (!fileSystemDirNameButton.classList.contains(CLASS_FILE_SYSTEM_DIR_NAME_BUTTON_DISABLED))
|
||||
fileSystemDirNameButton.classList.add(CLASS_FILE_SYSTEM_DIR_NAME_BUTTON_DISABLED);
|
||||
if (!fileSystemDirName.classList.contains(CLASS_FILE_SYSTEM_DIR_NAME_INPUT_ERROR))
|
||||
fileSystemDirName.classList.add(CLASS_FILE_SYSTEM_DIR_NAME_INPUT_ERROR);
|
||||
fileSystemDirNameError.innerHTML = error;
|
||||
fileSystemDirNameError.style.visibility = "visible";
|
||||
}
|
||||
}
|
||||
|
||||
// Attempts to create the given directory.
|
||||
function createDirectory(directoryName) {
|
||||
// Build file path.
|
||||
var path = currentDirectory + directoryName;
|
||||
// Close the directory name panel.
|
||||
closeDirectoryNamePanel();
|
||||
// Show loading panel.
|
||||
showFileSystemLoading(true);
|
||||
// Send request.
|
||||
$.post(
|
||||
"../ajax/fs_create_dir",
|
||||
JSON.stringify({
|
||||
"device_id": getDeviceID(),
|
||||
"path": path
|
||||
}),
|
||||
function(data) {
|
||||
// Process only if the file system window is showing.
|
||||
if (!isFileSystemShowing())
|
||||
return;
|
||||
// Process answer.
|
||||
processCreateDirectoryResponse(data);
|
||||
}
|
||||
).fail(function(response) {
|
||||
// Process only if the file system window is showing.
|
||||
if (!isFileSystemShowing())
|
||||
return;
|
||||
// Process error.
|
||||
processAjaxErrorResponse(response);
|
||||
// Hide the loading status.
|
||||
showFileSystemLoading(false);
|
||||
});
|
||||
}
|
||||
|
||||
// Processes the create directory response.
|
||||
function processCreateDirectoryResponse(response) {
|
||||
// Check if there was any error in the request.
|
||||
if (checkErrorResponse(response, false)) {
|
||||
// Hide the loading status.
|
||||
showFileSystemLoading(false);
|
||||
} else {
|
||||
// List directory contents again.
|
||||
listDirectory(currentDirectory);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* Copyright 2022, Digi International Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
// Constants.
|
||||
const ID_CPU_CHART = "cpu-chart";
|
||||
const ID_CPU_CHART_LOADING = "cpu-chart-loading";
|
||||
const ID_MEMORY_CHART = "memory-chart";
|
||||
const ID_MEMORY_CHART_LOADING = "memory-chart-loading";
|
||||
const ID_TEMPERATURE_CHART = "temperature-chart";
|
||||
const ID_TEMPERATURE_CHART_LOADING = "temperature-chart-loading";
|
||||
|
||||
const COLOR_CPU_CHART = "#3399FF";
|
||||
const COLOR_MEMORY_CHART = "#FFD500";
|
||||
const COLOR_TEMPERATURE_CHART = "#4F4F4F";
|
||||
|
||||
const TITLE_CPU_CHART = "CPU Usage";
|
||||
const TITLE_MEMORY_CHART = "Memory Usage";
|
||||
const TITLE_TEMPERATURE_CHART = "Temperature";
|
||||
|
||||
const UNITS_CPU_CHART = "%";
|
||||
const UNITS_MEMORY_CHART = "MB";
|
||||
const UNITS_TEMPERATURE_CHART = "°C";
|
||||
|
||||
// Variables.
|
||||
var temperatureData = null;
|
||||
var cpuData = null;
|
||||
var memoryData = null;
|
||||
var temperatureInterval = null;
|
||||
var cpuInterval = null;
|
||||
var memoryInterval = null;
|
||||
|
||||
// Initializes the charts.
|
||||
function initCharts() {
|
||||
// Reset the variables.
|
||||
temperatureData = null;
|
||||
cpuData = null;
|
||||
memoryData = null;
|
||||
temperatureInterval = null;
|
||||
cpuInterval = null;
|
||||
memoryInterval = null;
|
||||
// Draw all the charts.
|
||||
drawAllCharts(true, true);
|
||||
}
|
||||
|
||||
// Draws all the charts.
|
||||
function drawAllCharts(refresh=false, showProgress=true) {
|
||||
// Sanity checks.
|
||||
if (!isHistoryShowing())
|
||||
return;
|
||||
// Draw the charts.
|
||||
drawTemperatureChart(refresh, showProgress);
|
||||
drawCPUChart(refresh, showProgress);
|
||||
drawMemoryChart(refresh, showProgress);
|
||||
}
|
||||
|
||||
// Draws the temperature chart.
|
||||
function drawTemperatureChart(refresh=false, showProgress=false) {
|
||||
if (refresh) {
|
||||
if (showProgress)
|
||||
$("#" + ID_TEMPERATURE_CHART_LOADING).show();
|
||||
$.post(
|
||||
"/ajax/history_temperature",
|
||||
JSON.stringify({
|
||||
"device_id": getDeviceID(),
|
||||
"interval": temperatureInterval
|
||||
}),
|
||||
function(response) {
|
||||
// Process only if histograms page is showing.
|
||||
if (!isHistoryShowing())
|
||||
return;
|
||||
// Check for errors.
|
||||
if (!checkErrorResponse(response, false)) {
|
||||
temperatureData = response.data;
|
||||
drawTemperatureChart();
|
||||
$("#" + ID_TEMPERATURE_CHART_LOADING).hide();
|
||||
}
|
||||
}
|
||||
).fail(function(response) {
|
||||
// Process only if histograms page is showing.
|
||||
if (!isHistoryShowing())
|
||||
return;
|
||||
// Process error.
|
||||
processAjaxErrorResponse(response);
|
||||
});
|
||||
} else {
|
||||
drawChart(ID_TEMPERATURE_CHART, temperatureData, TITLE_TEMPERATURE_CHART, UNITS_TEMPERATURE_CHART, COLOR_TEMPERATURE_CHART);
|
||||
}
|
||||
}
|
||||
|
||||
// Draws the CPU chart.
|
||||
function drawCPUChart(refresh=false, showProgress=false) {
|
||||
if (refresh) {
|
||||
if (showProgress)
|
||||
$("#" + ID_CPU_CHART_LOADING).show();
|
||||
$.post(
|
||||
"/ajax/history_cpu",
|
||||
JSON.stringify({
|
||||
"device_id": getDeviceID(),
|
||||
"interval": cpuInterval
|
||||
}),
|
||||
function(response) {
|
||||
// Process only if histograms page is showing.
|
||||
if (!isHistoryShowing())
|
||||
return;
|
||||
// Check for errors.
|
||||
if (!checkErrorResponse(response, false)) {
|
||||
cpuData = response.data;
|
||||
drawCPUChart();
|
||||
$("#" + ID_CPU_CHART_LOADING).hide();
|
||||
}
|
||||
}
|
||||
).fail(function(response) {
|
||||
// Process only if histograms page is showing.
|
||||
if (!isHistoryShowing())
|
||||
return;
|
||||
// Process error.
|
||||
processAjaxErrorResponse(response);
|
||||
});
|
||||
} else {
|
||||
drawChart(ID_CPU_CHART, cpuData, TITLE_CPU_CHART, UNITS_CPU_CHART, COLOR_CPU_CHART);
|
||||
}
|
||||
}
|
||||
|
||||
// Draws the memory chart.
|
||||
function drawMemoryChart(refresh=false, showProgress=false) {
|
||||
if (refresh) {
|
||||
if (showProgress)
|
||||
$("#" + ID_MEMORY_CHART_LOADING).show();
|
||||
$.post(
|
||||
"/ajax/history_memory",
|
||||
JSON.stringify({
|
||||
"device_id": getDeviceID(),
|
||||
"interval": memoryInterval
|
||||
}),
|
||||
function(response) {
|
||||
// Process only if histograms page is showing.
|
||||
if (!isHistoryShowing())
|
||||
return;
|
||||
// Check for errors.
|
||||
if (!checkErrorResponse(response, false)) {
|
||||
memoryData = response.data;
|
||||
drawMemoryChart();
|
||||
$("#" + ID_MEMORY_CHART_LOADING).hide();
|
||||
}
|
||||
}
|
||||
).fail(function(response) {
|
||||
// Process only if histograms page is showing.
|
||||
if (!isHistoryShowing())
|
||||
return;
|
||||
// Process error.
|
||||
processAjaxErrorResponse(response);
|
||||
});
|
||||
} else {
|
||||
drawChart(ID_MEMORY_CHART, memoryData, TITLE_MEMORY_CHART, UNITS_MEMORY_CHART, COLOR_MEMORY_CHART);
|
||||
}
|
||||
}
|
||||
|
||||
// Draws the chart with the given data.
|
||||
function drawChart(id, data, title, units, color=null) {
|
||||
// Sanity checks.
|
||||
if (!isHistoryShowing())
|
||||
return;
|
||||
// Create the chart table.
|
||||
var dataTable = new google.visualization.DataTable();
|
||||
dataTable.addColumn("date", "");
|
||||
dataTable.addColumn("number", "");
|
||||
// Check if any data was given.
|
||||
if (data.length == 0) {
|
||||
$("#" + id).empty();
|
||||
$("#" + id).append("<span class='no-data-label'>Not enough data</span>");
|
||||
return;
|
||||
}
|
||||
// Add the data rows.
|
||||
dataTable.addRows(data.length);
|
||||
// Fill each row with the given data.
|
||||
$.each(data, function(k, v) {
|
||||
dataTable.setCell(k, 0, new Date(v[ID_TIMESTAMP]));
|
||||
dataTable.setCell(k, 1, v[ID_DATA]);
|
||||
});
|
||||
// Build chart options.
|
||||
var options = {
|
||||
backgroundColor: "transparent",
|
||||
series: {
|
||||
0: {
|
||||
axis: "Data",
|
||||
color: color,
|
||||
visibleInLegend: false
|
||||
}
|
||||
},
|
||||
axes: {
|
||||
y: {
|
||||
Data: {
|
||||
label: units
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
position: "none"
|
||||
},
|
||||
vAxis: {
|
||||
viewWindow: {
|
||||
min: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
// Create the chart.
|
||||
var chart = new google.charts.Line(document.getElementById(id));
|
||||
// Draw the chart.
|
||||
chart.draw(dataTable, google.charts.Line.convertOptions(options));
|
||||
}
|
||||
|
||||
// Returns the device ID.
|
||||
function getDeviceID() {
|
||||
return new URLSearchParams(window.location.search).get(ID_DEVICE_ID);
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* jquery-match-height 0.7.2 by @liabru
|
||||
* http://brm.io/jquery-match-height/
|
||||
* License MIT
|
||||
*/
|
||||
!function(t){"use strict";"function"==typeof define&&define.amd?define(["jquery"],t):"undefined"!=typeof module&&module.exports?module.exports=t(require("jquery")):t(jQuery)}(function(t){var e=-1,o=-1,n=function(t){return parseFloat(t)||0},a=function(e){var o=1,a=t(e),i=null,r=[];return a.each(function(){var e=t(this),a=e.offset().top-n(e.css("margin-top")),s=r.length>0?r[r.length-1]:null;null===s?r.push(e):Math.floor(Math.abs(i-a))<=o?r[r.length-1]=s.add(e):r.push(e),i=a}),r},i=function(e){var o={
|
||||
byRow:!0,property:"height",target:null,remove:!1};return"object"==typeof e?t.extend(o,e):("boolean"==typeof e?o.byRow=e:"remove"===e&&(o.remove=!0),o)},r=t.fn.matchHeight=function(e){var o=i(e);if(o.remove){var n=this;return this.css(o.property,""),t.each(r._groups,function(t,e){e.elements=e.elements.not(n)}),this}return this.length<=1&&!o.target?this:(r._groups.push({elements:this,options:o}),r._apply(this,o),this)};r.version="0.7.2",r._groups=[],r._throttle=80,r._maintainScroll=!1,r._beforeUpdate=null,
|
||||
r._afterUpdate=null,r._rows=a,r._parse=n,r._parseOptions=i,r._apply=function(e,o){var s=i(o),h=t(e),l=[h],c=t(window).scrollTop(),p=t("html").outerHeight(!0),u=h.parents().filter(":hidden");return u.each(function(){var e=t(this);e.data("style-cache",e.attr("style"))}),u.css("display","block"),s.byRow&&!s.target&&(h.each(function(){var e=t(this),o=e.css("display");"inline-block"!==o&&"flex"!==o&&"inline-flex"!==o&&(o="block"),e.data("style-cache",e.attr("style")),e.css({display:o,"padding-top":"0",
|
||||
"padding-bottom":"0","margin-top":"0","margin-bottom":"0","border-top-width":"0","border-bottom-width":"0",height:"100px",overflow:"hidden"})}),l=a(h),h.each(function(){var e=t(this);e.attr("style",e.data("style-cache")||"")})),t.each(l,function(e,o){var a=t(o),i=0;if(s.target)i=s.target.outerHeight(!1);else{if(s.byRow&&a.length<=1)return void a.css(s.property,"");a.each(function(){var e=t(this),o=e.attr("style"),n=e.css("display");"inline-block"!==n&&"flex"!==n&&"inline-flex"!==n&&(n="block");var a={
|
||||
display:n};a[s.property]="",e.css(a),e.outerHeight(!1)>i&&(i=e.outerHeight(!1)),o?e.attr("style",o):e.css("display","")})}a.each(function(){var e=t(this),o=0;s.target&&e.is(s.target)||("border-box"!==e.css("box-sizing")&&(o+=n(e.css("border-top-width"))+n(e.css("border-bottom-width")),o+=n(e.css("padding-top"))+n(e.css("padding-bottom"))),e.css(s.property,i-o+"px"))})}),u.each(function(){var e=t(this);e.attr("style",e.data("style-cache")||null)}),r._maintainScroll&&t(window).scrollTop(c/p*t("html").outerHeight(!0)),
|
||||
this},r._applyDataApi=function(){var e={};t("[data-match-height], [data-mh]").each(function(){var o=t(this),n=o.attr("data-mh")||o.attr("data-match-height");n in e?e[n]=e[n].add(o):e[n]=o}),t.each(e,function(){this.matchHeight(!0)})};var s=function(e){r._beforeUpdate&&r._beforeUpdate(e,r._groups),t.each(r._groups,function(){r._apply(this.elements,this.options)}),r._afterUpdate&&r._afterUpdate(e,r._groups)};r._update=function(n,a){if(a&&"resize"===a.type){var i=t(window).width();if(i===e)return;e=i;
|
||||
}n?o===-1&&(o=setTimeout(function(){s(a),o=-1},r._throttle)):s(a)},t(r._applyDataApi);var h=t.fn.on?"on":"bind";t(window)[h]("load",function(t){r._update(!1,t)}),t(window)[h]("resize orientationchange",function(t){r._update(!0,t)})});
|
||||
|
|
@ -0,0 +1,903 @@
|
|||
/*!
|
||||
* Copyright 2012, Chris Wanstrath
|
||||
* Released under the MIT License
|
||||
* https://github.com/defunkt/jquery-pjax
|
||||
*/
|
||||
|
||||
(function($){
|
||||
|
||||
// When called on a container with a selector, fetches the href with
|
||||
// ajax into the container or with the data-pjax attribute on the link
|
||||
// itself.
|
||||
//
|
||||
// Tries to make sure the back button and ctrl+click work the way
|
||||
// you'd expect.
|
||||
//
|
||||
// Exported as $.fn.pjax
|
||||
//
|
||||
// Accepts a jQuery ajax options object that may include these
|
||||
// pjax specific options:
|
||||
//
|
||||
//
|
||||
// container - String selector for the element where to place the response body.
|
||||
// push - Whether to pushState the URL. Defaults to true (of course).
|
||||
// replace - Want to use replaceState instead? That's cool.
|
||||
//
|
||||
// For convenience the second parameter can be either the container or
|
||||
// the options object.
|
||||
//
|
||||
// Returns the jQuery object
|
||||
function fnPjax(selector, container, options) {
|
||||
options = optionsFor(container, options)
|
||||
return this.on('click.pjax', selector, function(event) {
|
||||
var opts = options
|
||||
if (!opts.container) {
|
||||
opts = $.extend({}, options)
|
||||
opts.container = $(this).attr('data-pjax')
|
||||
}
|
||||
handleClick(event, opts)
|
||||
})
|
||||
}
|
||||
|
||||
// Public: pjax on click handler
|
||||
//
|
||||
// Exported as $.pjax.click.
|
||||
//
|
||||
// event - "click" jQuery.Event
|
||||
// options - pjax options
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// $(document).on('click', 'a', $.pjax.click)
|
||||
// // is the same as
|
||||
// $(document).pjax('a')
|
||||
//
|
||||
// Returns nothing.
|
||||
function handleClick(event, container, options) {
|
||||
options = optionsFor(container, options)
|
||||
|
||||
var link = event.currentTarget
|
||||
var $link = $(link)
|
||||
|
||||
if (link.tagName.toUpperCase() !== 'A')
|
||||
throw "$.fn.pjax or $.pjax.click requires an anchor element"
|
||||
|
||||
// Middle click, cmd click, and ctrl click should open
|
||||
// links in a new tab as normal.
|
||||
if ( event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey )
|
||||
return
|
||||
|
||||
// Ignore cross origin links
|
||||
if ( location.protocol !== link.protocol || location.hostname !== link.hostname )
|
||||
return
|
||||
|
||||
// Ignore case when a hash is being tacked on the current URL
|
||||
if ( link.href.indexOf('#') > -1 && stripHash(link) == stripHash(location) )
|
||||
return
|
||||
|
||||
// Ignore event with default prevented
|
||||
if (event.isDefaultPrevented())
|
||||
return
|
||||
|
||||
var defaults = {
|
||||
url: link.href,
|
||||
container: $link.attr('data-pjax'),
|
||||
target: link
|
||||
}
|
||||
|
||||
var opts = $.extend({}, defaults, options)
|
||||
var clickEvent = $.Event('pjax:click')
|
||||
$link.trigger(clickEvent, [opts])
|
||||
|
||||
if (!clickEvent.isDefaultPrevented()) {
|
||||
pjax(opts)
|
||||
event.preventDefault()
|
||||
$link.trigger('pjax:clicked', [opts])
|
||||
}
|
||||
}
|
||||
|
||||
// Public: pjax on form submit handler
|
||||
//
|
||||
// Exported as $.pjax.submit
|
||||
//
|
||||
// event - "click" jQuery.Event
|
||||
// options - pjax options
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// $(document).on('submit', 'form', function(event) {
|
||||
// $.pjax.submit(event, '[data-pjax-container]')
|
||||
// })
|
||||
//
|
||||
// Returns nothing.
|
||||
function handleSubmit(event, container, options) {
|
||||
options = optionsFor(container, options)
|
||||
|
||||
var form = event.currentTarget
|
||||
var $form = $(form)
|
||||
|
||||
if (form.tagName.toUpperCase() !== 'FORM')
|
||||
throw "$.pjax.submit requires a form element"
|
||||
|
||||
var defaults = {
|
||||
type: ($form.attr('method') || 'GET').toUpperCase(),
|
||||
url: $form.attr('action'),
|
||||
container: $form.attr('data-pjax'),
|
||||
target: form
|
||||
}
|
||||
|
||||
if (defaults.type !== 'GET' && window.FormData !== undefined) {
|
||||
defaults.data = new FormData(form)
|
||||
defaults.processData = false
|
||||
defaults.contentType = false
|
||||
} else {
|
||||
// Can't handle file uploads, exit
|
||||
if ($form.find(':file').length) {
|
||||
return
|
||||
}
|
||||
|
||||
// Fallback to manually serializing the fields
|
||||
defaults.data = $form.serializeArray()
|
||||
}
|
||||
|
||||
pjax($.extend({}, defaults, options))
|
||||
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
// Loads a URL with ajax, puts the response body inside a container,
|
||||
// then pushState()'s the loaded URL.
|
||||
//
|
||||
// Works just like $.ajax in that it accepts a jQuery ajax
|
||||
// settings object (with keys like url, type, data, etc).
|
||||
//
|
||||
// Accepts these extra keys:
|
||||
//
|
||||
// container - String selector for where to stick the response body.
|
||||
// push - Whether to pushState the URL. Defaults to true (of course).
|
||||
// replace - Want to use replaceState instead? That's cool.
|
||||
//
|
||||
// Use it just like $.ajax:
|
||||
//
|
||||
// var xhr = $.pjax({ url: this.href, container: '#main' })
|
||||
// console.log( xhr.readyState )
|
||||
//
|
||||
// Returns whatever $.ajax returns.
|
||||
function pjax(options) {
|
||||
options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options)
|
||||
|
||||
if ($.isFunction(options.url)) {
|
||||
options.url = options.url()
|
||||
}
|
||||
|
||||
var hash = parseURL(options.url).hash
|
||||
|
||||
var containerType = $.type(options.container)
|
||||
if (containerType !== 'string') {
|
||||
throw "expected string value for 'container' option; got " + containerType
|
||||
}
|
||||
var context = options.context = $(options.container)
|
||||
if (!context.length) {
|
||||
throw "the container selector '" + options.container + "' did not match anything"
|
||||
}
|
||||
|
||||
// We want the browser to maintain two separate internal caches: one
|
||||
// for pjax'd partial page loads and one for normal page loads.
|
||||
// Without adding this secret parameter, some browsers will often
|
||||
// confuse the two.
|
||||
if (!options.data) options.data = {}
|
||||
if ($.isArray(options.data)) {
|
||||
options.data.push({name: '_pjax', value: options.container})
|
||||
} else {
|
||||
options.data._pjax = options.container
|
||||
}
|
||||
|
||||
function fire(type, args, props) {
|
||||
if (!props) props = {}
|
||||
props.relatedTarget = options.target
|
||||
var event = $.Event(type, props)
|
||||
context.trigger(event, args)
|
||||
return !event.isDefaultPrevented()
|
||||
}
|
||||
|
||||
var timeoutTimer
|
||||
|
||||
options.beforeSend = function(xhr, settings) {
|
||||
// No timeout for non-GET requests
|
||||
// Its not safe to request the resource again with a fallback method.
|
||||
if (settings.type !== 'GET') {
|
||||
settings.timeout = 0
|
||||
}
|
||||
|
||||
xhr.setRequestHeader('X-PJAX', 'true')
|
||||
xhr.setRequestHeader('X-PJAX-Container', options.container)
|
||||
|
||||
if (!fire('pjax:beforeSend', [xhr, settings]))
|
||||
return false
|
||||
|
||||
if (settings.timeout > 0) {
|
||||
timeoutTimer = setTimeout(function() {
|
||||
if (fire('pjax:timeout', [xhr, options]))
|
||||
xhr.abort('timeout')
|
||||
}, settings.timeout)
|
||||
|
||||
// Clear timeout setting so jquerys internal timeout isn't invoked
|
||||
settings.timeout = 0
|
||||
}
|
||||
|
||||
var url = parseURL(settings.url)
|
||||
if (hash) url.hash = hash
|
||||
options.requestUrl = stripInternalParams(url)
|
||||
}
|
||||
|
||||
options.complete = function(xhr, textStatus) {
|
||||
if (timeoutTimer)
|
||||
clearTimeout(timeoutTimer)
|
||||
|
||||
fire('pjax:complete', [xhr, textStatus, options])
|
||||
|
||||
fire('pjax:end', [xhr, options])
|
||||
}
|
||||
|
||||
options.error = function(xhr, textStatus, errorThrown) {
|
||||
var container = extractContainer("", xhr, options)
|
||||
|
||||
var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options])
|
||||
if (options.type == 'GET' && textStatus !== 'abort' && allowed) {
|
||||
locationReplace(container.url)
|
||||
}
|
||||
}
|
||||
|
||||
options.success = function(data, status, xhr) {
|
||||
var previousState = pjax.state
|
||||
|
||||
// If $.pjax.defaults.version is a function, invoke it first.
|
||||
// Otherwise it can be a static string.
|
||||
var currentVersion = typeof $.pjax.defaults.version === 'function' ?
|
||||
$.pjax.defaults.version() :
|
||||
$.pjax.defaults.version
|
||||
|
||||
var latestVersion = xhr.getResponseHeader('X-PJAX-Version')
|
||||
|
||||
var container = extractContainer(data, xhr, options)
|
||||
|
||||
var url = parseURL(container.url)
|
||||
if (hash) {
|
||||
url.hash = hash
|
||||
container.url = url.href
|
||||
}
|
||||
|
||||
// If there is a layout version mismatch, hard load the new url
|
||||
if (currentVersion && latestVersion && currentVersion !== latestVersion) {
|
||||
locationReplace(container.url)
|
||||
return
|
||||
}
|
||||
|
||||
// If the new response is missing a body, hard load the page
|
||||
if (!container.contents) {
|
||||
locationReplace(container.url)
|
||||
return
|
||||
}
|
||||
|
||||
pjax.state = {
|
||||
id: options.id || uniqueId(),
|
||||
url: container.url,
|
||||
title: container.title,
|
||||
container: options.container,
|
||||
fragment: options.fragment,
|
||||
timeout: options.timeout
|
||||
}
|
||||
|
||||
if (options.push || options.replace) {
|
||||
window.history.replaceState(pjax.state, container.title, container.url)
|
||||
}
|
||||
|
||||
// Only blur the focus if the focused element is within the container.
|
||||
var blurFocus = $.contains(context, document.activeElement)
|
||||
|
||||
// Clear out any focused controls before inserting new page contents.
|
||||
if (blurFocus) {
|
||||
try {
|
||||
document.activeElement.blur()
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
|
||||
if (container.title) document.title = container.title
|
||||
|
||||
fire('pjax:beforeReplace', [container.contents, options], {
|
||||
state: pjax.state,
|
||||
previousState: previousState
|
||||
})
|
||||
context.html(container.contents)
|
||||
|
||||
// FF bug: Won't autofocus fields that are inserted via JS.
|
||||
// This behavior is incorrect. So if theres no current focus, autofocus
|
||||
// the last field.
|
||||
//
|
||||
// http://www.w3.org/html/wg/drafts/html/master/forms.html
|
||||
var autofocusEl = context.find('input[autofocus], textarea[autofocus]').last()[0]
|
||||
if (autofocusEl && document.activeElement !== autofocusEl) {
|
||||
autofocusEl.focus()
|
||||
}
|
||||
|
||||
executeScriptTags(container.scripts)
|
||||
|
||||
var scrollTo = options.scrollTo
|
||||
|
||||
// Ensure browser scrolls to the element referenced by the URL anchor
|
||||
if (hash) {
|
||||
var name = decodeURIComponent(hash.slice(1))
|
||||
var target = document.getElementById(name) || document.getElementsByName(name)[0]
|
||||
if (target) scrollTo = $(target).offset().top
|
||||
}
|
||||
|
||||
if (typeof scrollTo == 'number') $(window).scrollTop(scrollTo)
|
||||
|
||||
fire('pjax:success', [data, status, xhr, options])
|
||||
}
|
||||
|
||||
|
||||
// Initialize pjax.state for the initial page load. Assume we're
|
||||
// using the container and options of the link we're loading for the
|
||||
// back button to the initial page. This ensures good back button
|
||||
// behavior.
|
||||
if (!pjax.state) {
|
||||
pjax.state = {
|
||||
id: uniqueId(),
|
||||
url: window.location.href,
|
||||
title: document.title,
|
||||
container: options.container,
|
||||
fragment: options.fragment,
|
||||
timeout: options.timeout
|
||||
}
|
||||
window.history.replaceState(pjax.state, document.title)
|
||||
}
|
||||
|
||||
// Cancel the current request if we're already pjaxing
|
||||
abortXHR(pjax.xhr)
|
||||
|
||||
pjax.options = options
|
||||
var xhr = pjax.xhr = $.ajax(options)
|
||||
|
||||
if (xhr.readyState > 0) {
|
||||
if (options.push && !options.replace) {
|
||||
// Cache current container element before replacing it
|
||||
cachePush(pjax.state.id, [options.container, cloneContents(context)])
|
||||
|
||||
window.history.pushState(null, "", options.requestUrl)
|
||||
}
|
||||
|
||||
fire('pjax:start', [xhr, options])
|
||||
fire('pjax:send', [xhr, options])
|
||||
}
|
||||
|
||||
return pjax.xhr
|
||||
}
|
||||
|
||||
// Public: Reload current page with pjax.
|
||||
//
|
||||
// Returns whatever $.pjax returns.
|
||||
function pjaxReload(container, options) {
|
||||
var defaults = {
|
||||
url: window.location.href,
|
||||
push: false,
|
||||
replace: true,
|
||||
scrollTo: false
|
||||
}
|
||||
|
||||
return pjax($.extend(defaults, optionsFor(container, options)))
|
||||
}
|
||||
|
||||
// Internal: Hard replace current state with url.
|
||||
//
|
||||
// Work for around WebKit
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=93506
|
||||
//
|
||||
// Returns nothing.
|
||||
function locationReplace(url) {
|
||||
window.history.replaceState(null, "", pjax.state.url)
|
||||
window.location.replace(url)
|
||||
}
|
||||
|
||||
|
||||
var initialPop = true
|
||||
var initialURL = window.location.href
|
||||
var initialState = window.history.state
|
||||
|
||||
// Initialize $.pjax.state if possible
|
||||
// Happens when reloading a page and coming forward from a different
|
||||
// session history.
|
||||
if (initialState && initialState.container) {
|
||||
pjax.state = initialState
|
||||
}
|
||||
|
||||
// Non-webkit browsers don't fire an initial popstate event
|
||||
if ('state' in window.history) {
|
||||
initialPop = false
|
||||
}
|
||||
|
||||
// popstate handler takes care of the back and forward buttons
|
||||
//
|
||||
// You probably shouldn't use pjax on pages with other pushState
|
||||
// stuff yet.
|
||||
function onPjaxPopstate(event) {
|
||||
|
||||
// Hitting back or forward should override any pending PJAX request.
|
||||
if (!initialPop) {
|
||||
abortXHR(pjax.xhr)
|
||||
}
|
||||
|
||||
var previousState = pjax.state
|
||||
var state = event.state
|
||||
var direction
|
||||
|
||||
if (state && state.container) {
|
||||
// When coming forward from a separate history session, will get an
|
||||
// initial pop with a state we are already at. Skip reloading the current
|
||||
// page.
|
||||
if (initialPop && initialURL == state.url) return
|
||||
|
||||
if (previousState) {
|
||||
// If popping back to the same state, just skip.
|
||||
// Could be clicking back from hashchange rather than a pushState.
|
||||
if (previousState.id === state.id) return
|
||||
|
||||
// Since state IDs always increase, we can deduce the navigation direction
|
||||
direction = previousState.id < state.id ? 'forward' : 'back'
|
||||
}
|
||||
|
||||
var cache = cacheMapping[state.id] || []
|
||||
var containerSelector = cache[0] || state.container
|
||||
var container = $(containerSelector), contents = cache[1]
|
||||
|
||||
if (container.length) {
|
||||
if (previousState) {
|
||||
// Cache current container before replacement and inform the
|
||||
// cache which direction the history shifted.
|
||||
cachePop(direction, previousState.id, [containerSelector, cloneContents(container)])
|
||||
}
|
||||
|
||||
var popstateEvent = $.Event('pjax:popstate', {
|
||||
state: state,
|
||||
direction: direction
|
||||
})
|
||||
container.trigger(popstateEvent)
|
||||
|
||||
var options = {
|
||||
id: state.id,
|
||||
url: state.url,
|
||||
container: containerSelector,
|
||||
push: false,
|
||||
fragment: state.fragment,
|
||||
timeout: state.timeout,
|
||||
scrollTo: false
|
||||
}
|
||||
|
||||
if (contents) {
|
||||
container.trigger('pjax:start', [null, options])
|
||||
|
||||
pjax.state = state
|
||||
if (state.title) document.title = state.title
|
||||
var beforeReplaceEvent = $.Event('pjax:beforeReplace', {
|
||||
state: state,
|
||||
previousState: previousState
|
||||
})
|
||||
container.trigger(beforeReplaceEvent, [contents, options])
|
||||
container.html(contents)
|
||||
|
||||
container.trigger('pjax:end', [null, options])
|
||||
} else {
|
||||
pjax(options)
|
||||
}
|
||||
|
||||
// Force reflow/relayout before the browser tries to restore the
|
||||
// scroll position.
|
||||
container[0].offsetHeight // eslint-disable-line no-unused-expressions
|
||||
} else {
|
||||
locationReplace(location.href)
|
||||
}
|
||||
}
|
||||
initialPop = false
|
||||
}
|
||||
|
||||
// Fallback version of main pjax function for browsers that don't
|
||||
// support pushState.
|
||||
//
|
||||
// Returns nothing since it retriggers a hard form submission.
|
||||
function fallbackPjax(options) {
|
||||
var url = $.isFunction(options.url) ? options.url() : options.url,
|
||||
method = options.type ? options.type.toUpperCase() : 'GET'
|
||||
|
||||
var form = $('<form>', {
|
||||
method: method === 'GET' ? 'GET' : 'POST',
|
||||
action: url,
|
||||
style: 'display:none'
|
||||
})
|
||||
|
||||
if (method !== 'GET' && method !== 'POST') {
|
||||
form.append($('<input>', {
|
||||
type: 'hidden',
|
||||
name: '_method',
|
||||
value: method.toLowerCase()
|
||||
}))
|
||||
}
|
||||
|
||||
var data = options.data
|
||||
if (typeof data === 'string') {
|
||||
$.each(data.split('&'), function(index, value) {
|
||||
var pair = value.split('=')
|
||||
form.append($('<input>', {type: 'hidden', name: pair[0], value: pair[1]}))
|
||||
})
|
||||
} else if ($.isArray(data)) {
|
||||
$.each(data, function(index, value) {
|
||||
form.append($('<input>', {type: 'hidden', name: value.name, value: value.value}))
|
||||
})
|
||||
} else if (typeof data === 'object') {
|
||||
var key
|
||||
for (key in data)
|
||||
form.append($('<input>', {type: 'hidden', name: key, value: data[key]}))
|
||||
}
|
||||
|
||||
$(document.body).append(form)
|
||||
form.submit()
|
||||
}
|
||||
|
||||
// Internal: Abort an XmlHttpRequest if it hasn't been completed,
|
||||
// also removing its event handlers.
|
||||
function abortXHR(xhr) {
|
||||
if ( xhr && xhr.readyState < 4) {
|
||||
xhr.onreadystatechange = $.noop
|
||||
xhr.abort()
|
||||
}
|
||||
}
|
||||
|
||||
// Internal: Generate unique id for state object.
|
||||
//
|
||||
// Use a timestamp instead of a counter since ids should still be
|
||||
// unique across page loads.
|
||||
//
|
||||
// Returns Number.
|
||||
function uniqueId() {
|
||||
return (new Date).getTime()
|
||||
}
|
||||
|
||||
function cloneContents(container) {
|
||||
var cloned = container.clone()
|
||||
// Unmark script tags as already being eval'd so they can get executed again
|
||||
// when restored from cache. HAXX: Uses jQuery internal method.
|
||||
cloned.find('script').each(function(){
|
||||
if (!this.src) $._data(this, 'globalEval', false)
|
||||
})
|
||||
return cloned.contents()
|
||||
}
|
||||
|
||||
// Internal: Strip internal query params from parsed URL.
|
||||
//
|
||||
// Returns sanitized url.href String.
|
||||
function stripInternalParams(url) {
|
||||
url.search = url.search.replace(/([?&])(_pjax|_)=[^&]*/g, '').replace(/^&/, '')
|
||||
return url.href.replace(/\?($|#)/, '$1')
|
||||
}
|
||||
|
||||
// Internal: Parse URL components and returns a Locationish object.
|
||||
//
|
||||
// url - String URL
|
||||
//
|
||||
// Returns HTMLAnchorElement that acts like Location.
|
||||
function parseURL(url) {
|
||||
var a = document.createElement('a')
|
||||
a.href = url
|
||||
return a
|
||||
}
|
||||
|
||||
// Internal: Return the `href` component of given URL object with the hash
|
||||
// portion removed.
|
||||
//
|
||||
// location - Location or HTMLAnchorElement
|
||||
//
|
||||
// Returns String
|
||||
function stripHash(location) {
|
||||
return location.href.replace(/#.*/, '')
|
||||
}
|
||||
|
||||
// Internal: Build options Object for arguments.
|
||||
//
|
||||
// For convenience the first parameter can be either the container or
|
||||
// the options object.
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// optionsFor('#container')
|
||||
// // => {container: '#container'}
|
||||
//
|
||||
// optionsFor('#container', {push: true})
|
||||
// // => {container: '#container', push: true}
|
||||
//
|
||||
// optionsFor({container: '#container', push: true})
|
||||
// // => {container: '#container', push: true}
|
||||
//
|
||||
// Returns options Object.
|
||||
function optionsFor(container, options) {
|
||||
if (container && options) {
|
||||
options = $.extend({}, options)
|
||||
options.container = container
|
||||
return options
|
||||
} else if ($.isPlainObject(container)) {
|
||||
return container
|
||||
} else {
|
||||
return {container: container}
|
||||
}
|
||||
}
|
||||
|
||||
// Internal: Filter and find all elements matching the selector.
|
||||
//
|
||||
// Where $.fn.find only matches descendants, findAll will test all the
|
||||
// top level elements in the jQuery object as well.
|
||||
//
|
||||
// elems - jQuery object of Elements
|
||||
// selector - String selector to match
|
||||
//
|
||||
// Returns a jQuery object.
|
||||
function findAll(elems, selector) {
|
||||
return elems.filter(selector).add(elems.find(selector))
|
||||
}
|
||||
|
||||
function parseHTML(html) {
|
||||
return $.parseHTML(html, document, true)
|
||||
}
|
||||
|
||||
// Internal: Extracts container and metadata from response.
|
||||
//
|
||||
// 1. Extracts X-PJAX-URL header if set
|
||||
// 2. Extracts inline <title> tags
|
||||
// 3. Builds response Element and extracts fragment if set
|
||||
//
|
||||
// data - String response data
|
||||
// xhr - XHR response
|
||||
// options - pjax options Object
|
||||
//
|
||||
// Returns an Object with url, title, and contents keys.
|
||||
function extractContainer(data, xhr, options) {
|
||||
var obj = {}, fullDocument = /<html/i.test(data)
|
||||
|
||||
// Prefer X-PJAX-URL header if it was set, otherwise fallback to
|
||||
// using the original requested url.
|
||||
var serverUrl = xhr.getResponseHeader('X-PJAX-URL')
|
||||
obj.url = serverUrl ? stripInternalParams(parseURL(serverUrl)) : options.requestUrl
|
||||
|
||||
var $head, $body
|
||||
// Attempt to parse response html into elements
|
||||
if (fullDocument) {
|
||||
$body = $(parseHTML(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0]))
|
||||
var head = data.match(/<head[^>]*>([\s\S.]*)<\/head>/i)
|
||||
$head = head != null ? $(parseHTML(head[0])) : $body
|
||||
} else {
|
||||
$head = $body = $(parseHTML(data))
|
||||
}
|
||||
|
||||
// If response data is empty, return fast
|
||||
if ($body.length === 0)
|
||||
return obj
|
||||
|
||||
// If there's a <title> tag in the header, use it as
|
||||
// the page's title.
|
||||
obj.title = findAll($head, 'title').last().text()
|
||||
|
||||
if (options.fragment) {
|
||||
var $fragment = $body
|
||||
// If they specified a fragment, look for it in the response
|
||||
// and pull it out.
|
||||
if (options.fragment !== 'body') {
|
||||
$fragment = findAll($fragment, options.fragment).first()
|
||||
}
|
||||
|
||||
if ($fragment.length) {
|
||||
obj.contents = options.fragment === 'body' ? $fragment : $fragment.contents()
|
||||
|
||||
// If there's no title, look for data-title and title attributes
|
||||
// on the fragment
|
||||
if (!obj.title)
|
||||
obj.title = $fragment.attr('title') || $fragment.data('title')
|
||||
}
|
||||
|
||||
} else if (!fullDocument) {
|
||||
obj.contents = $body
|
||||
}
|
||||
|
||||
// Clean up any <title> tags
|
||||
if (obj.contents) {
|
||||
// Remove any parent title elements
|
||||
obj.contents = obj.contents.not(function() { return $(this).is('title') })
|
||||
|
||||
// Then scrub any titles from their descendants
|
||||
obj.contents.find('title').remove()
|
||||
|
||||
// Gather all script[src] elements
|
||||
obj.scripts = findAll(obj.contents, 'script[src]').remove()
|
||||
obj.contents = obj.contents.not(obj.scripts)
|
||||
}
|
||||
|
||||
// Trim any whitespace off the title
|
||||
if (obj.title) obj.title = $.trim(obj.title)
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
// Load an execute scripts using standard script request.
|
||||
//
|
||||
// Avoids jQuery's traditional $.getScript which does a XHR request and
|
||||
// globalEval.
|
||||
//
|
||||
// scripts - jQuery object of script Elements
|
||||
//
|
||||
// Returns nothing.
|
||||
function executeScriptTags(scripts) {
|
||||
if (!scripts) return
|
||||
|
||||
var existingScripts = $('script[src]')
|
||||
|
||||
scripts.each(function() {
|
||||
var src = this.src
|
||||
var matchedScripts = existingScripts.filter(function() {
|
||||
return this.src === src
|
||||
})
|
||||
if (matchedScripts.length) return
|
||||
|
||||
var script = document.createElement('script')
|
||||
var type = $(this).attr('type')
|
||||
if (type) script.type = type
|
||||
script.src = $(this).attr('src')
|
||||
document.head.appendChild(script)
|
||||
})
|
||||
}
|
||||
|
||||
// Internal: History DOM caching class.
|
||||
var cacheMapping = {}
|
||||
var cacheForwardStack = []
|
||||
var cacheBackStack = []
|
||||
|
||||
// Push previous state id and container contents into the history
|
||||
// cache. Should be called in conjunction with `pushState` to save the
|
||||
// previous container contents.
|
||||
//
|
||||
// id - State ID Number
|
||||
// value - DOM Element to cache
|
||||
//
|
||||
// Returns nothing.
|
||||
function cachePush(id, value) {
|
||||
cacheMapping[id] = value
|
||||
cacheBackStack.push(id)
|
||||
|
||||
// Remove all entries in forward history stack after pushing a new page.
|
||||
trimCacheStack(cacheForwardStack, 0)
|
||||
|
||||
// Trim back history stack to max cache length.
|
||||
trimCacheStack(cacheBackStack, pjax.defaults.maxCacheLength)
|
||||
}
|
||||
|
||||
// Shifts cache from directional history cache. Should be
|
||||
// called on `popstate` with the previous state id and container
|
||||
// contents.
|
||||
//
|
||||
// direction - "forward" or "back" String
|
||||
// id - State ID Number
|
||||
// value - DOM Element to cache
|
||||
//
|
||||
// Returns nothing.
|
||||
function cachePop(direction, id, value) {
|
||||
var pushStack, popStack
|
||||
cacheMapping[id] = value
|
||||
|
||||
if (direction === 'forward') {
|
||||
pushStack = cacheBackStack
|
||||
popStack = cacheForwardStack
|
||||
} else {
|
||||
pushStack = cacheForwardStack
|
||||
popStack = cacheBackStack
|
||||
}
|
||||
|
||||
pushStack.push(id)
|
||||
id = popStack.pop()
|
||||
if (id) delete cacheMapping[id]
|
||||
|
||||
// Trim whichever stack we just pushed to to max cache length.
|
||||
trimCacheStack(pushStack, pjax.defaults.maxCacheLength)
|
||||
}
|
||||
|
||||
// Trim a cache stack (either cacheBackStack or cacheForwardStack) to be no
|
||||
// longer than the specified length, deleting cached DOM elements as necessary.
|
||||
//
|
||||
// stack - Array of state IDs
|
||||
// length - Maximum length to trim to
|
||||
//
|
||||
// Returns nothing.
|
||||
function trimCacheStack(stack, length) {
|
||||
while (stack.length > length)
|
||||
delete cacheMapping[stack.shift()]
|
||||
}
|
||||
|
||||
// Public: Find version identifier for the initial page load.
|
||||
//
|
||||
// Returns String version or undefined.
|
||||
function findVersion() {
|
||||
return $('meta').filter(function() {
|
||||
var name = $(this).attr('http-equiv')
|
||||
return name && name.toUpperCase() === 'X-PJAX-VERSION'
|
||||
}).attr('content')
|
||||
}
|
||||
|
||||
// Install pjax functions on $.pjax to enable pushState behavior.
|
||||
//
|
||||
// Does nothing if already enabled.
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// $.pjax.enable()
|
||||
//
|
||||
// Returns nothing.
|
||||
function enable() {
|
||||
$.fn.pjax = fnPjax
|
||||
$.pjax = pjax
|
||||
$.pjax.enable = $.noop
|
||||
$.pjax.disable = disable
|
||||
$.pjax.click = handleClick
|
||||
$.pjax.submit = handleSubmit
|
||||
$.pjax.reload = pjaxReload
|
||||
$.pjax.defaults = {
|
||||
timeout: 650,
|
||||
push: true,
|
||||
replace: false,
|
||||
type: 'GET',
|
||||
dataType: 'html',
|
||||
scrollTo: 0,
|
||||
maxCacheLength: 20,
|
||||
version: findVersion
|
||||
}
|
||||
$(window).on('popstate.pjax', onPjaxPopstate)
|
||||
}
|
||||
|
||||
// Disable pushState behavior.
|
||||
//
|
||||
// This is the case when a browser doesn't support pushState. It is
|
||||
// sometimes useful to disable pushState for debugging on a modern
|
||||
// browser.
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// $.pjax.disable()
|
||||
//
|
||||
// Returns nothing.
|
||||
function disable() {
|
||||
$.fn.pjax = function() { return this }
|
||||
$.pjax = fallbackPjax
|
||||
$.pjax.enable = enable
|
||||
$.pjax.disable = $.noop
|
||||
$.pjax.click = $.noop
|
||||
$.pjax.submit = $.noop
|
||||
$.pjax.reload = function() { window.location.reload() }
|
||||
|
||||
$(window).off('popstate.pjax', onPjaxPopstate)
|
||||
}
|
||||
|
||||
|
||||
// Add the state property to jQuery's event object so we can use it in
|
||||
// $(window).bind('popstate')
|
||||
if ($.event.props && $.inArray('state', $.event.props) < 0) {
|
||||
$.event.props.push('state')
|
||||
} else if (!('state' in $.Event.prototype)) {
|
||||
$.event.addProp('state')
|
||||
}
|
||||
|
||||
// Is pjax supported by this browser?
|
||||
$.support.pjax =
|
||||
window.history && window.history.pushState && window.history.replaceState &&
|
||||
// pushState isn't reliable on iOS until 5.
|
||||
!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/)
|
||||
|
||||
if ($.support.pjax) {
|
||||
enable()
|
||||
} else {
|
||||
disable()
|
||||
}
|
||||
|
||||
})(jQuery)
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* Copyright 2022, Digi International Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
// Constants.
|
||||
const ID_DEVICE_CONNECTION_STATUS = "device-connection-status";
|
||||
|
||||
const ERROR_DEVICE_NOT_CONNECTED_TITLE = "Device offline";
|
||||
const ERROR_DEVICE_NOT_CONNECTED_MESSAGE = "The selected device is offline";
|
||||
|
||||
// Variables.
|
||||
var deviceConnectionStatus;
|
||||
var prevDeviceConnectionStatus;
|
||||
|
||||
// Hide submenus
|
||||
$("#body-row .collapse").collapse("hide");
|
||||
|
||||
// Collapse/Expand icon.
|
||||
$("#collapse-icon").addClass("fa-angle-double-left");
|
||||
|
||||
// Select click.
|
||||
$("#sections > a").click(function() {
|
||||
selectSection($(this));
|
||||
});
|
||||
|
||||
// Collapse click.
|
||||
$("[data-toggle=sidebar-collapse]").click(function() {
|
||||
sidebarCollapse();
|
||||
});
|
||||
|
||||
$(".element-grayed").click(function(){return false;});
|
||||
|
||||
// Selects the given item in the sidebar.
|
||||
function selectSection(selectedItem) {
|
||||
// Remove decorations of previously selected element.
|
||||
$("#sections .selected").removeClass(CLASS_SELECTED);
|
||||
|
||||
// Decorate the selected element.
|
||||
selectedItem.toggleClass(CLASS_SELECTED);
|
||||
}
|
||||
|
||||
// Collapses the sidebar to the left.
|
||||
function sidebarCollapse() {
|
||||
$(".menu-collapsed").toggleClass("d-none");
|
||||
$(".sidebar-submenu").toggleClass("d-none");
|
||||
$(".submenu-icon").toggleClass("d-none");
|
||||
|
||||
// Add/Remove right margin.
|
||||
$(".digi-menu-icon").toggleClass("mr-3 mr-0");
|
||||
|
||||
$("#sidebar-container").toggleClass("sidebar-expanded sidebar-collapsed");
|
||||
|
||||
// Treating d-flex/d-none on separators with title.
|
||||
var separatorTitle = $(".sidebar-separator-title");
|
||||
if (separatorTitle.hasClass("d-flex"))
|
||||
separatorTitle.removeClass("d-flex");
|
||||
else
|
||||
separatorTitle.addClass("d-flex");
|
||||
|
||||
// Collapse/Expand icon.
|
||||
$("#collapse-icon").toggleClass("fa-angle-double-left fa-angle-double-right");
|
||||
|
||||
// Make the cards the same height. Wait some time so size is
|
||||
// calculated after the content of the cards expands or collapses.
|
||||
window.setTimeout(function () {
|
||||
$(".adjust-card-height .card").matchHeight();
|
||||
}, 100);
|
||||
|
||||
if (isDashboardShowing()) {
|
||||
let refreshPanelsInterval = window.setInterval(adjustImageSize, 10);
|
||||
window.setTimeout(function () {
|
||||
window.clearInterval(refreshPanelsInterval);
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the selected section.
|
||||
function setSelectedSection(element=null) {
|
||||
// First, unselect all the sections.
|
||||
$("#sections li").each(function(i, n) {
|
||||
n.children[0].classList.remove(CLASS_SELECTED);
|
||||
});
|
||||
// Select the corresponding section.
|
||||
if (element != null) {
|
||||
element.classList.add(CLASS_SELECTED);
|
||||
} else {
|
||||
$("#sections li").each(function(i, n) {
|
||||
if (window.location.pathname == n.children[0].pathname) {
|
||||
n.children[0].classList.add(CLASS_SELECTED);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies the given device parameters.
|
||||
function verifyParameters() {
|
||||
let url = new URL(window.location.href);
|
||||
let device_id = url.searchParams.get("device_id");
|
||||
let device_name = url.searchParams.get("device_name");
|
||||
$.post(
|
||||
"../ajax/check_device_connection_status",
|
||||
JSON.stringify({
|
||||
"device_id": device_id,
|
||||
"device_name": device_name
|
||||
}),
|
||||
function(data) {
|
||||
if (data["redirect"])
|
||||
window.location.replace(data["redirect"]);
|
||||
}
|
||||
).fail(function(response) {
|
||||
processAjaxErrorResponse(response);
|
||||
});
|
||||
}
|
||||
|
||||
// Requests an update on the connection status of the device.
|
||||
function checkDeviceConnectionStatus() {
|
||||
$.post(
|
||||
"../ajax/check_device_connection_status",
|
||||
JSON.stringify({
|
||||
"device_id": getDeviceID()
|
||||
}),
|
||||
function(data) {
|
||||
processDeviceConnectionStatusAnswer(data);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Processes the device connection status answer.
|
||||
function processDeviceConnectionStatusAnswer(response) {
|
||||
// Sanity checks.
|
||||
if (response[ID_STATUS] == null || response[ID_STATUS] == "undefined") {
|
||||
// Do not continue.
|
||||
return;
|
||||
}
|
||||
// Save the new connection status.
|
||||
deviceConnectionStatus = response[ID_STATUS];
|
||||
// Get icon and title based on connection status.
|
||||
var statusImage = "";
|
||||
var statusTitle = "";
|
||||
if (deviceConnectionStatus == true) {
|
||||
statusImage = IMAGE_ONLINE;
|
||||
statusTitle = VALUE_ONLINE;
|
||||
} else {
|
||||
statusImage = IMAGE_OFFLINE;
|
||||
statusTitle = VALUE_OFFLINE;
|
||||
}
|
||||
// Update the connection status icon and title.
|
||||
var deviceStatusElement = document.getElementById(ID_DEVICE_CONNECTION_STATUS);
|
||||
if (deviceStatusElement != null) {
|
||||
deviceStatusElement.src = PATH_IMAGES + statusImage;
|
||||
deviceStatusElement.title = statusTitle;
|
||||
}
|
||||
// Check if connection changed to update the displayed section.
|
||||
if (prevDeviceConnectionStatus != deviceConnectionStatus) {
|
||||
if (isDashboardShowing()) {
|
||||
if (deviceConnectionStatus)
|
||||
initDevice();
|
||||
else
|
||||
displayDeviceDisconnectedError();
|
||||
} else if (isManagementShowing()) {
|
||||
if (deviceConnectionStatus)
|
||||
initializeManagementPage();
|
||||
else if (!isDeviceRebooting())
|
||||
displayDeviceDisconnectedError();
|
||||
} else if (isHistoryShowing()) {
|
||||
if (deviceConnectionStatus)
|
||||
initCharts();
|
||||
else
|
||||
displayDeviceDisconnectedError();
|
||||
}
|
||||
}
|
||||
// Store connection status.
|
||||
prevDeviceConnectionStatus = deviceConnectionStatus;
|
||||
// Schedule a new connection status update in 30 seconds.
|
||||
setTimeout(checkDeviceConnectionStatus, 30000);
|
||||
}
|
||||
|
||||
// Displays the device disconnected error.
|
||||
function displayDeviceDisconnectedError() {
|
||||
// Device disconnected, display error.
|
||||
toastr.error(ERROR_DEVICE_NOT_CONNECTED_TITLE);
|
||||
// Hide the loading panel of the device.
|
||||
showLoadingPopup(false);
|
||||
// Show info dialog.
|
||||
showInfoPopup(true, ERROR_DEVICE_NOT_CONNECTED_TITLE, ERROR_DEVICE_NOT_CONNECTED_MESSAGE);
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2020, Digi International Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
const SEPARATOR = "@@@"
|
||||
|
||||
var currentTime;
|
||||
var timer;
|
||||
|
||||
function updateTime() {
|
||||
currentTime += 1;
|
||||
postMessage(new Date(1000 * currentTime).toISOString().substr(11, 8));
|
||||
}
|
||||
|
||||
onmessage = function (event) {
|
||||
var parts = event.data.split(SEPARATOR);
|
||||
currentTime = parseInt(parts[0]);
|
||||
var factor = parseInt(parts[1]);
|
||||
setInterval(updateTime, 1000 / factor);
|
||||
};
|
||||
|
|
@ -0,0 +1 @@
|
|||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(self,(function(){return(()=>{"use strict";var e={775:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.FitAddon=void 0;var r=function(){function e(){}return e.prototype.activate=function(e){this._terminal=e},e.prototype.dispose=function(){},e.prototype.fit=function(){var e=this.proposeDimensions();if(e&&this._terminal){var t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}},e.prototype.proposeDimensions=function(){if(this._terminal&&this._terminal.element&&this._terminal.element.parentElement){var e=this._terminal._core;if(0!==e._renderService.dimensions.actualCellWidth&&0!==e._renderService.dimensions.actualCellHeight){var t=window.getComputedStyle(this._terminal.element.parentElement),r=parseInt(t.getPropertyValue("height")),i=Math.max(0,parseInt(t.getPropertyValue("width"))),n=window.getComputedStyle(this._terminal.element),o=r-(parseInt(n.getPropertyValue("padding-top"))+parseInt(n.getPropertyValue("padding-bottom"))),a=i-(parseInt(n.getPropertyValue("padding-right"))+parseInt(n.getPropertyValue("padding-left")))-e.viewport.scrollBarWidth;return{cols:Math.max(2,Math.floor(a/e._renderService.dimensions.actualCellWidth)),rows:Math.max(1,Math.floor(o/e._renderService.dimensions.actualCellHeight))}}}},e}();t.FitAddon=r}},t={};return function r(i){if(t[i])return t[i].exports;var n=t[i]={exports:{}};return e[i](n,n.exports,r),n.exports}(775)})()}));
|
||||