mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-19 19:33:47 +00:00
Introducing Orca Cloud: https://cloud.orcaslicer.com (#13414)
* Add OrcaCloud sync platform and preset bundle sharing system Introduce OrcaCloud, a cloud sync platform for user presets, alongside a preset bundle system that enables sharing printer/filament/process profiles as local exportable bundles or subscribed cloud bundles. OrcaCloud platform: - Auth to Orca Cloud - Encrypted token storage (file-based or system keychain) - User preset sync with - Profile migration from default/bambu folders on first login - Homepage integration with entrance to cloud.orcaslicer.com Preset bundles: - Local bundle import/export with bundle_structure.json metadata - Subscribed cloud bundles with version-based update checking - Thread-safe concurrent bundle access with read-write mutex - Canonical bundle preset naming (_local/<id>/... and _subscribed/<id>/...) - Bundle presets are read-only; grouped under subheaders in combo boxes - PresetBundleDialog with auto-sync toggle, refresh, update notifications - Hyperlinked bundle names to cloud bundle pages Co-authored-by: Sabriel Koh <sabrielkcr@gmail.com> Co-authored-by: Derrick <derrick992110@gmail.com> Co-authored-by: Mykola Nahirnyi <mnahirnyi@amcbridge.com> Co-authored-by: Ian Chua <iancrb00@gmail.com> Co-authored-by: Draginraptor <draginraptor@gmail.com> Co-authored-by: ExPikaPaka <112851715+ExPikaPaka@users.noreply.github.com> Co-authored-by: Ian Bassi <ian.bassi@outlook.com> Co-authored-by: Ocraftyone <Ocraftyone@users.noreply.github.com> Co-authored-by: yw4z <ywsyildiz@gmail.com> Co-authored-by: peterm-m <101202951+peterm-m@users.noreply.github.com> * Fixed an issue on Windows it failed to login Orca Cloud with Google account
This commit is contained in:
75
resources/web/dialog/ExportPresetDialog/index.html
Normal file
75
resources/web/dialog/ExportPresetDialog/index.html
Normal file
@@ -0,0 +1,75 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Cache-Control" content="max-age=0" />
|
||||
<title>Export Presets</title>
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../include/global.css" /> <!-- ORCA One for all-->
|
||||
<link rel="stylesheet" type="text/css" href="../css/common.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../css/dark.css" />
|
||||
<script type="text/javascript" src="../js/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../js/json2.js"></script>
|
||||
<script type="text/javascript" src="../../data/text.js"></script>
|
||||
<script type="text/javascript" src="../js/globalapi.js"></script>
|
||||
<script type="text/javascript" src="../js/common.js"></script>
|
||||
<script src="./index.js"></script>
|
||||
</head>
|
||||
<body onLoad="OnInit()">
|
||||
|
||||
<!-- ORCA column browser -->
|
||||
<div class="cbr-browser-container">
|
||||
<div class="cbr-column">
|
||||
<div class="cbr-column-title-container">
|
||||
<div class="search-icon"></div>
|
||||
<input type="text" class="cbr-search-bar" placeholder=" " tabindex="1"/>
|
||||
<span class="cbr-search-placeholder trans" tid="t15">printer</span>
|
||||
<div class="clear-icon"></div>
|
||||
</div>
|
||||
<div class="cbr-content thin-scroll" id="MachineList">
|
||||
<div class="CValues">
|
||||
<label><input type="checkbox" mode="all" onClick="ChooseAllMachine()" /><span class="trans" tid="t11">all</span></label>
|
||||
</div>
|
||||
<div class="cbr-no-items">No items</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cbr-column">
|
||||
<div class="cbr-column-title-container">
|
||||
<div class="search-icon"></div>
|
||||
<input type="text" class="cbr-search-bar" placeholder=" " tabindex="2"/>
|
||||
<span class="cbr-search-placeholder trans" tid="t16">filament</span>
|
||||
<div class="clear-icon"></div>
|
||||
</div>
|
||||
<div class="cbr-content thin-scroll" id="FilatypeList">
|
||||
<div class="CValues">
|
||||
<label><input type="checkbox" class="trans" tid="t11" onClick="ChooseAllFilament()" /><span class="trans" tid="t11">all</span></label>
|
||||
</div>
|
||||
<div class="cbr-no-items">No items</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cbr-column">
|
||||
<div class="cbr-column-title-container">
|
||||
<div class="search-icon"></div>
|
||||
<input type="text" class="cbr-search-bar" placeholder=" " tabindex="3"/>
|
||||
<span class="cbr-search-placeholder trans" tid="t17">presets</span>
|
||||
<div class="clear-icon"></div>
|
||||
</div>
|
||||
<div class="cbr-content thin-scroll" id="PresetList">
|
||||
<div class="CValues">
|
||||
<label><input type="checkbox" class="trans" tid="t11" onClick="ChooseAllPreset()" /><span class="trans" tid="t11">all</span></label>
|
||||
</div>
|
||||
<div class="cbr-no-items">No items</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="AcceptArea">
|
||||
<div id="export_cloud_btn" class="ButtonStyleConfirm ButtonTypeChoice">Export to OrcaCloud</div>
|
||||
<div id="export_local_btn" class="ButtonStyleRegular ButtonTypeChoice">Export to folder</div>
|
||||
<div id="close_btn" class="ButtonStyleRegular ButtonTypeChoice">Close</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
374
resources/web/dialog/ExportPresetDialog/index.js
Normal file
374
resources/web/dialog/ExportPresetDialog/index.js
Normal file
@@ -0,0 +1,374 @@
|
||||
var g_profile = {
|
||||
machines: [],
|
||||
filaments: [],
|
||||
presets: []
|
||||
};
|
||||
|
||||
var g_search = {
|
||||
machine: "",
|
||||
filament: "",
|
||||
preset: ""
|
||||
};
|
||||
|
||||
function OnInit()
|
||||
{
|
||||
if (typeof TranslatePage === "function")
|
||||
TranslatePage();
|
||||
|
||||
InstallInputSafeKeydown();
|
||||
BindSearchInputs();
|
||||
BindClearIcons();
|
||||
BindBottomButtons();
|
||||
|
||||
// Always load demo data first so the page works without C++ backend.
|
||||
// LoadDemoProfile();
|
||||
RequestProfile();
|
||||
}
|
||||
|
||||
function InstallInputSafeKeydown()
|
||||
{
|
||||
// common.js blocks all key events globally; allow typing in text inputs.
|
||||
document.onkeydown = function (event) {
|
||||
var e = event || window.event || arguments.callee.caller.arguments[0];
|
||||
var target = e && e.target ? e.target : null;
|
||||
var tag = target && target.tagName ? String(target.tagName).toUpperCase() : "";
|
||||
var type = target && target.type ? String(target.type).toLowerCase() : "";
|
||||
|
||||
var editable =
|
||||
!!(target && target.isContentEditable) ||
|
||||
tag === "TEXTAREA" ||
|
||||
(tag === "INPUT" && type !== "checkbox" && type !== "radio" && type !== "button" && type !== "submit");
|
||||
|
||||
if (editable)
|
||||
return true;
|
||||
|
||||
if (e && e.keyCode === 27 && typeof ClosePage === "function")
|
||||
ClosePage();
|
||||
|
||||
if (window.event) {
|
||||
try { e.keyCode = 0; } catch (err) { }
|
||||
e.returnValue = false;
|
||||
}
|
||||
|
||||
if (e && typeof e.preventDefault === "function")
|
||||
e.preventDefault();
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
function RequestProfile()
|
||||
{
|
||||
SendMessage("request_export_preset_profile", {});
|
||||
}
|
||||
|
||||
function HandleStudio(pVal)
|
||||
{
|
||||
var payload = (typeof pVal === "string") ? SafeJsonParse(pVal) : pVal;
|
||||
if (!payload || typeof payload !== "object")
|
||||
return;
|
||||
|
||||
var cmd = String(payload.command || "");
|
||||
if (cmd === "response_export_preset_profile" ) {
|
||||
ApplyProfile(payload.data);
|
||||
}
|
||||
}
|
||||
|
||||
function ApplyProfile(profile)
|
||||
{
|
||||
|
||||
g_profile.machines = BuildNameRows(profile.printers);
|
||||
g_profile.filaments = BuildNameRows(profile.filaments);
|
||||
g_profile.presets = BuildNameRows(profile.process);
|
||||
|
||||
RenderColumn("MachineList", g_profile.machines, "mode", "MachineClick");
|
||||
RenderColumn("FilatypeList", g_profile.filaments, "filatype", "FilaClick");
|
||||
RenderColumn("PresetList", g_profile.presets, "preset", "PresetClick");
|
||||
|
||||
ApplyColumnSearch("MachineList", g_search.machine);
|
||||
ApplyColumnSearch("FilatypeList", g_search.filament);
|
||||
ApplyColumnSearch("PresetList", g_search.preset);
|
||||
}
|
||||
|
||||
function BuildNameRows(names)
|
||||
{
|
||||
var src = Array.isArray(names) ? names : [];
|
||||
var out = [];
|
||||
|
||||
for (var n = 0; n < src.length; n++) {
|
||||
var row = src[n];
|
||||
if (row === undefined || row === null)
|
||||
continue;
|
||||
|
||||
var name = String(row);
|
||||
out.push({ id: name, label: name, checked: false });
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function RenderColumn(listId, items, attrName, onChangeFn)
|
||||
{
|
||||
var root = $("#" + listId + " .CValues");
|
||||
if (!root.length)
|
||||
return;
|
||||
|
||||
root.find("label:gt(0)").remove();
|
||||
|
||||
var html = "";
|
||||
for (var n = 0; n < items.length; n++) {
|
||||
var one = items[n];
|
||||
html += '<label data-dynamic="1">' +
|
||||
'<input type="checkbox" data-key="' + EscapeAttr(one.id) + '" ' + attrName + '="' + EscapeAttr(one.id) + '"' +
|
||||
(one.checked ? ' checked="checked"' : "") +
|
||||
' onChange="' + onChangeFn + '()" />' +
|
||||
'<span title="' + EscapeAttr(one.label) + '">' + EscapeHtml(one.label) + '</span>' +
|
||||
'</label>';
|
||||
}
|
||||
|
||||
root.append(html);
|
||||
SyncMasterCheckbox(listId);
|
||||
ToggleNoItems(listId, items.length === 0);
|
||||
}
|
||||
|
||||
function ChooseAllMachine()
|
||||
{
|
||||
var checked = !!$("#MachineList .CValues input:first").prop("checked");
|
||||
$("#MachineList .CValues input:gt(0)").prop("checked", checked);
|
||||
SyncListFromDom("MachineList", g_profile.machines);
|
||||
}
|
||||
|
||||
function MachineClick()
|
||||
{
|
||||
SyncMasterCheckbox("MachineList");
|
||||
SyncListFromDom("MachineList", g_profile.machines);
|
||||
}
|
||||
|
||||
function ChooseAllFilament()
|
||||
{
|
||||
var checked = !!$("#FilatypeList .CValues input:first").prop("checked");
|
||||
$("#FilatypeList .CValues input:gt(0)").prop("checked", checked);
|
||||
SyncListFromDom("FilatypeList", g_profile.filaments);
|
||||
}
|
||||
|
||||
function FilaClick()
|
||||
{
|
||||
SyncMasterCheckbox("FilatypeList");
|
||||
SyncListFromDom("FilatypeList", g_profile.filaments);
|
||||
}
|
||||
|
||||
function ChooseAllPreset()
|
||||
{
|
||||
var checked = !!$("#PresetList .CValues input:first").prop("checked");
|
||||
$("#PresetList .CValues input:gt(0)").prop("checked", checked);
|
||||
SyncListFromDom("PresetList", g_profile.presets);
|
||||
}
|
||||
|
||||
function PresetClick()
|
||||
{
|
||||
SyncMasterCheckbox("PresetList");
|
||||
SyncListFromDom("PresetList", g_profile.presets);
|
||||
}
|
||||
|
||||
function SyncMasterCheckbox(listId)
|
||||
{
|
||||
var all = $("#" + listId + " .CValues input:gt(0)");
|
||||
var master = $("#" + listId + " .CValues input:first");
|
||||
|
||||
if (!all.length) {
|
||||
master.prop("checked", false);
|
||||
return;
|
||||
}
|
||||
|
||||
master.prop("checked", all.length === all.filter(":checked").length);
|
||||
}
|
||||
|
||||
function SyncListFromDom(listId, store)
|
||||
{
|
||||
var map = {};
|
||||
for (var n = 0; n < store.length; n++)
|
||||
map[store[n].id] = store[n];
|
||||
|
||||
$("#" + listId + " .CValues input:gt(0)").each(function () {
|
||||
var id = String($(this).attr("data-key") || "");
|
||||
if (map[id])
|
||||
map[id].checked = !!$(this).prop("checked");
|
||||
});
|
||||
}
|
||||
|
||||
function BindSearchInputs()
|
||||
{
|
||||
var inputs = document.querySelectorAll(".cbr-search-bar");
|
||||
|
||||
if (inputs.length > 0) {
|
||||
inputs[0].addEventListener("input", function () {
|
||||
g_search.machine = String(this.value || "").toLowerCase();
|
||||
ApplyColumnSearch("MachineList", g_search.machine);
|
||||
});
|
||||
}
|
||||
|
||||
if (inputs.length > 1) {
|
||||
inputs[1].addEventListener("input", function () {
|
||||
g_search.filament = String(this.value || "").toLowerCase();
|
||||
ApplyColumnSearch("FilatypeList", g_search.filament);
|
||||
});
|
||||
}
|
||||
|
||||
if (inputs.length > 2) {
|
||||
inputs[2].addEventListener("input", function () {
|
||||
g_search.preset = String(this.value || "").toLowerCase();
|
||||
ApplyColumnSearch("PresetList", g_search.preset);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function ApplyColumnSearch(listId, query)
|
||||
{
|
||||
var rows = $("#" + listId + " .CValues label:gt(0)");
|
||||
var visibleCount = 0;
|
||||
|
||||
rows.each(function () {
|
||||
var row = $(this);
|
||||
var text = String(row.text() || "").toLowerCase();
|
||||
var key = String(row.find("input").attr("data-key") || "").toLowerCase();
|
||||
|
||||
if (!query || text.indexOf(query) >= 0 || key.indexOf(query) >= 0) {
|
||||
row.show();
|
||||
visibleCount++;
|
||||
}
|
||||
else {
|
||||
row.hide();
|
||||
}
|
||||
});
|
||||
|
||||
ToggleNoItems(listId, visibleCount === 0);
|
||||
}
|
||||
|
||||
function ToggleNoItems(listId, show)
|
||||
{
|
||||
var node = $("#" + listId + " .cbr-no-items");
|
||||
if (!node.length)
|
||||
return;
|
||||
|
||||
if (show)
|
||||
node.addClass("show");
|
||||
else
|
||||
node.removeClass("show");
|
||||
}
|
||||
|
||||
function BindClearIcons()
|
||||
{
|
||||
var icons = document.querySelectorAll(".clear-icon");
|
||||
|
||||
for (var n = 0; n < icons.length; n++) {
|
||||
icons[n].addEventListener("click", function () {
|
||||
var parent = this.parentElement;
|
||||
if (!parent)
|
||||
return;
|
||||
|
||||
var input = parent.querySelector("input[type='text']");
|
||||
if (!input)
|
||||
return;
|
||||
|
||||
input.value = "";
|
||||
input.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
input.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function BindBottomButtons()
|
||||
{
|
||||
var backBtn = document.getElementById("back_btn");
|
||||
var exportCloud = document.getElementById("export_cloud_btn")
|
||||
var exportLocal = document.getElementById("export_local_btn");
|
||||
var closeBtn = document.getElementById("close_btn");
|
||||
|
||||
backBtn?.addEventListener("click", function () {
|
||||
SendMessage("navigate_back", {});
|
||||
});
|
||||
|
||||
|
||||
exportLocal?.addEventListener("click", function () {
|
||||
SendMessage("export_local", BuildResultPayload());
|
||||
});
|
||||
|
||||
|
||||
closeBtn?.addEventListener("click", () => {
|
||||
const tSend = {
|
||||
sequence_id: Math.round(Date.now() / 1000),
|
||||
command: "close_page"
|
||||
};
|
||||
SendWXMessage(JSON.stringify(tSend));
|
||||
});
|
||||
}
|
||||
|
||||
function BuildResultPayload()
|
||||
{
|
||||
return {
|
||||
machines: g_profile.machines.filter(function (x) { return x.checked; }).map(function (x) { return x.id; }),
|
||||
filaments: g_profile.filaments.filter(function (x) { return x.checked; }).map(function (x) { return x.id; }),
|
||||
presets: g_profile.presets.filter(function (x) { return x.checked; }).map(function (x) { return x.id; })
|
||||
};
|
||||
}
|
||||
|
||||
function LoadDemoProfile()
|
||||
{
|
||||
ApplyProfile({
|
||||
machines: [
|
||||
{ id: "printer_x1c_04", name: "X1 Carbon 0.4 nozzle", selected: 1 },
|
||||
{ id: "printer_p1s_04", name: "P1S 0.4 nozzle", selected: 1 },
|
||||
{ id: "printer_a1_04", name: "A1 0.4 nozzle", selected: 0 },
|
||||
{ id: "printer_prusa_mk4_04", name: "Prusa MK4 0.4 nozzle", selected: 1 }
|
||||
],
|
||||
filaments: [
|
||||
{ id: "filament_generic_pla", name: "Generic PLA", selected: 1 },
|
||||
{ id: "filament_generic_petg", name: "Generic PETG", selected: 1 },
|
||||
{ id: "filament_bambu_abs", name: "Bambu ABS", selected: 0 },
|
||||
{ id: "filament_esun_pla_plus", name: "eSUN PLA+", selected: 1 }
|
||||
],
|
||||
presets: [
|
||||
{ id: "preset_quality_020", name: "Quality 0.20mm", selected: 1 },
|
||||
{ id: "preset_quality_012", name: "Quality 0.12mm", selected: 0 },
|
||||
{ id: "preset_speed_024", name: "Speed 0.24mm", selected: 1 },
|
||||
{ id: "preset_draft_028", name: "Draft 0.28mm", selected: 0 }
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
function SendMessage(command, data)
|
||||
{
|
||||
var msg = {};
|
||||
msg.sequence_id = Math.round(new Date() / 1000);
|
||||
msg.command = command;
|
||||
if (data && typeof data === "object")
|
||||
msg.data = data;
|
||||
|
||||
if (typeof SendWXMessage === "function")
|
||||
SendWXMessage(JSON.stringify(msg));
|
||||
}
|
||||
|
||||
function SafeJsonParse(str)
|
||||
{
|
||||
try {
|
||||
return JSON.parse(str);
|
||||
}
|
||||
catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function EscapeHtml(str)
|
||||
{
|
||||
return String(str)
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/\"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
function EscapeAttr(str)
|
||||
{
|
||||
return EscapeHtml(str);
|
||||
}
|
||||
191
resources/web/dialog/ExportPresetDialog/styles.css
Normal file
191
resources/web/dialog/ExportPresetDialog/styles.css
Normal file
@@ -0,0 +1,191 @@
|
||||
:root {
|
||||
--cbr-border-color: #d2d2d7;
|
||||
--cbr-header-bg: #f6f7f9;
|
||||
--cbr-panel-bg: #ffffff;
|
||||
--cbr-input-bg: #ffffff;
|
||||
--cbr-input-focus-bg: #f2f8f7;
|
||||
--cbr-label-color: #7b7b84;
|
||||
--cbr-icon-color: #75757f;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--cbr-border-color: #4a4a51;
|
||||
--cbr-header-bg: #2f2f34;
|
||||
--cbr-panel-bg: #2d2d31;
|
||||
--cbr-input-bg: #2d2d31;
|
||||
--cbr-input-focus-bg: #3b3b41;
|
||||
--cbr-label-color: #b9b9bc;
|
||||
--cbr-icon-color: #b9b9bc;
|
||||
}
|
||||
}
|
||||
|
||||
.cbr-browser-container {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
margin: 10px 15px 0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
grid-template-rows: minmax(0, 1fr);
|
||||
border: 1px solid var(--cbr-border-color);
|
||||
background: var(--cbr-panel-bg);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.cbr-column {
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cbr-column:not(:last-child) {
|
||||
border-right: 1px solid var(--cbr-border-color);
|
||||
}
|
||||
|
||||
.cbr-column-title-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 36px;
|
||||
padding: 3px 6px;
|
||||
background: var(--cbr-header-bg);
|
||||
border-bottom: 1px solid var(--cbr-border-color);
|
||||
}
|
||||
|
||||
.cbr-search-bar {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
padding: 2px 26px 2px 26px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
background: var(--cbr-input-bg);
|
||||
}
|
||||
|
||||
.cbr-search-bar:hover,
|
||||
.cbr-search-bar:focus {
|
||||
border-color: var(--main-color);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.cbr-search-bar:focus {
|
||||
background: var(--cbr-input-focus-bg);
|
||||
}
|
||||
|
||||
.cbr-search-placeholder {
|
||||
position: absolute;
|
||||
left: 33px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: var(--cbr-label-color);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.cbr-search-bar:not(:placeholder-shown) + .cbr-search-placeholder {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.search-icon,
|
||||
.clear-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--cbr-icon-color);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
left: 11px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.search-icon::before {
|
||||
content: "";
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
box-sizing: border-box;
|
||||
border: 1.8px solid currentColor;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.search-icon::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 5px;
|
||||
height: 1.8px;
|
||||
background: currentColor;
|
||||
transform: rotate(45deg);
|
||||
right: 0;
|
||||
bottom: 2px;
|
||||
}
|
||||
|
||||
.clear-icon {
|
||||
right: 11px;
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.clear-icon::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
height: 1.8px;
|
||||
background: currentColor;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.clear-icon::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
height: 1.8px;
|
||||
background: currentColor;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.cbr-search-bar:not(:placeholder-shown) ~ .clear-icon {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.cbr-content {
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.cbr-column .CValues {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.CValues label {
|
||||
margin: 0 !important;
|
||||
padding: 1px 0;
|
||||
}
|
||||
|
||||
.cbr-content .cbr-no-items {
|
||||
display: none;
|
||||
color: var(--cbr-label-color);
|
||||
font-size: 12px;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.cbr-content .cbr-no-items.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#AcceptArea {
|
||||
border-top: 1px solid var(--cbr-border-color);
|
||||
}
|
||||
|
||||
|
||||
71
resources/web/dialog/PresetBundleDialog/index.html
Normal file
71
resources/web/dialog/PresetBundleDialog/index.html
Normal file
@@ -0,0 +1,71 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Preset Bundle</title>
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../include/global.css" /> <!-- ORCA One for all-->
|
||||
<link rel="stylesheet" type="text/css" href="../css/common.css" />
|
||||
<!-- <link rel="stylesheet" type="text/css" href="23.css" /> -->
|
||||
<link rel="stylesheet" type="text/css" href="../css/dark.css" />
|
||||
<script type="text/javascript" src="../js/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../js/json2.js"></script>
|
||||
<script type="text/javascript" src="../../data/text.js"></script>
|
||||
<script type="text/javascript" src="../js/globalapi.js"></script>
|
||||
<script type="text/javascript" src="../js/common.js"></script>
|
||||
<!-- <script type="text/javascript" src="./23.js"></script> -->
|
||||
<script src="./index.js"></script>
|
||||
</head>
|
||||
<body onLoad="OnInit()">
|
||||
<div class="app">
|
||||
<div class="split">
|
||||
<div class="top-toolbar">
|
||||
<button id="refresh_btn" class="ButtonStyleConfirm ButtonTypeChoice toolbar-btn">Refresh</button>
|
||||
|
||||
<!-- <label class="auto-update-switch" for="auto_update_toggle">
|
||||
<span class="auto-update-label">Auto update</span>
|
||||
<input id="auto_update_toggle" type="checkbox" />
|
||||
<span class="auto-update-slider" aria-hidden="true">
|
||||
<span class="auto-update-text auto-update-text-off">OFF</span>
|
||||
<span class="auto-update-text auto-update-text-on">ON</span>
|
||||
</span>
|
||||
</label> -->
|
||||
</div>
|
||||
|
||||
<section class="pane top">
|
||||
<div class="hdr top-cols">
|
||||
<span>Name</span>
|
||||
<span>Type</span>
|
||||
<span>Version</span>
|
||||
<span>Update</span>
|
||||
</div>
|
||||
<div id="topList" class="body"></div>
|
||||
</section>
|
||||
|
||||
<section class="pane bottom">
|
||||
<div class="hdr bot-cols">
|
||||
<span>Name</span>
|
||||
<span>Type</span>
|
||||
</div>
|
||||
<div id="bottomList" class="body"></div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div id="AcceptArea">
|
||||
<div id="export_btn" class="ButtonStyleConfirm ButtonTypeChoice" hidden>Export Presets</div>
|
||||
<div id="close_btn" class="ButtonStyleRegular ButtonTypeChoice">Close</div>
|
||||
</div>
|
||||
|
||||
<!-- Context Menu -->
|
||||
<div id="ctxMenu" class="ctx" hidden>
|
||||
<button class="ctx-item" data-action="open_folder">Open folder in explorer</button>
|
||||
<button id = "delete_btn" class="ctx-item-delete" data-action="delete_bundle" hidden>Delete bundle</button>
|
||||
<button id = "unsubscribe_btn" class="ctx-item-subscribed" data-action="unsubscribe_bundle" hidden>Unsubscribe bundle</button>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
375
resources/web/dialog/PresetBundleDialog/index.js
Normal file
375
resources/web/dialog/PresetBundleDialog/index.js
Normal file
@@ -0,0 +1,375 @@
|
||||
// ========= Data Stores =========
|
||||
const bundlesById = new Map(); // bundleId -> bundle object
|
||||
const printersByBundle = new Map(); // bundleId -> Map(index -> printerName)
|
||||
const filamentsByBundle = new Map(); // bundleId -> Map(index -> filamentName)
|
||||
const presetsByBundle = new Map(); // bundleId -> Map(index -> presetName)
|
||||
const UPDATE_TOOLTIP = "Update available";
|
||||
const UNAUTHORIZED_TOOLTIP = "Unauthorized bundle";
|
||||
|
||||
// ========= DOM =========
|
||||
let topList = null;
|
||||
let bottomList = null;
|
||||
|
||||
let ctxMenu = null;
|
||||
let contextRow = null;
|
||||
let ctxMenuSubscribed = null;
|
||||
|
||||
let ctxMenuDelete = null;
|
||||
|
||||
let selectedBundleId = null;
|
||||
|
||||
// ========= Init =========
|
||||
function OnInit() {
|
||||
|
||||
topList = document.getElementById("topList");
|
||||
bottomList = document.getElementById("bottomList");
|
||||
ctxMenu = document.getElementById("ctxMenu");
|
||||
ctxMenuSubscribed = document.getElementById("unsubscribe_btn");
|
||||
ctxMenuDelete = document.getElementById("delete_btn");
|
||||
const closeBtn = document.getElementById("close_btn");
|
||||
const exportbtn = document.getElementById("export_btn");
|
||||
const refreshBtn = document.getElementById("refresh_btn");
|
||||
const autoUpdateToggle = document.getElementById("auto_update_toggle");
|
||||
|
||||
if (!topList || !bottomList) return;
|
||||
TranslatePage();
|
||||
|
||||
// If wx side needs to request bundles after page load:
|
||||
RequestBundles();
|
||||
|
||||
|
||||
refreshBtn?.addEventListener("click", () => {
|
||||
const tSend = {
|
||||
sequence_id: Math.round(Date.now() / 1000),
|
||||
command: "refresh_bundles"
|
||||
};
|
||||
SendWXMessage(JSON.stringify(tSend));
|
||||
});
|
||||
|
||||
autoUpdateToggle?.addEventListener("change", () => {
|
||||
const tSend = {
|
||||
sequence_id: Math.round(Date.now() / 1000),
|
||||
command: "set_auto_update",
|
||||
enabled: !!autoUpdateToggle.checked
|
||||
};
|
||||
SendWXMessage(JSON.stringify(tSend));
|
||||
});
|
||||
// Hook selection on top list
|
||||
topList.addEventListener("click", (e) => {
|
||||
const cloudLink = e.target.closest(".bundle-cloud-link");
|
||||
if (cloudLink) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const row = cloudLink.closest(".row");
|
||||
if (!row) return;
|
||||
|
||||
selectTopRow(row);
|
||||
selectedBundleId = String(row.dataset.id || "");
|
||||
renderBottomForBundle(selectedBundleId);
|
||||
sendOpenBundleOnCloud(selectedBundleId);
|
||||
return;
|
||||
}
|
||||
|
||||
const updateBtn = e.target.closest(".bundle-update-btn");
|
||||
if (updateBtn) {
|
||||
e.stopPropagation();
|
||||
if (updateBtn.disabled) return;
|
||||
|
||||
const row = updateBtn.closest(".row");
|
||||
if (!row) return;
|
||||
|
||||
selectTopRow(row);
|
||||
selectedBundleId = String(row.dataset.id || "");
|
||||
renderBottomForBundle(selectedBundleId);
|
||||
sendUpdateBundleCommand(selectedBundleId);
|
||||
return;
|
||||
}
|
||||
|
||||
const row = e.target.closest(".row");
|
||||
if (!row) return;
|
||||
|
||||
selectTopRow(row);
|
||||
selectedBundleId = String(row.dataset.id || "");
|
||||
renderBottomForBundle(selectedBundleId);
|
||||
});
|
||||
|
||||
// for top list rows if right click open context menu
|
||||
topList.addEventListener("contextmenu", (e) => {
|
||||
const row = e.target.closest(".row");
|
||||
if (!row) return; // top rows only
|
||||
|
||||
const bundleType = String(row.dataset.bundleType || "").toLowerCase();
|
||||
if (bundleType !== "subscribed") return;
|
||||
|
||||
e.preventDefault();
|
||||
selectTopRow(row);
|
||||
contextRow = row;
|
||||
showSubscribedMenu(e.clientX, e.clientY);
|
||||
});
|
||||
|
||||
// for top list rows except subscribed if right click open regular context menu
|
||||
topList.addEventListener("contextmenu", (e) => {
|
||||
const row = e.target.closest(".row");
|
||||
if (!row) return; // top rows only
|
||||
const bundleType = String(row.dataset.bundleType || "").toLowerCase();
|
||||
if (bundleType === "subscribed") return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
selectTopRow(row);
|
||||
contextRow = row;
|
||||
showMenu(e.clientX, e.clientY);
|
||||
});
|
||||
|
||||
ctxMenu?.addEventListener("click", (e) => {
|
||||
const btn = e.target.closest("[data-action]");
|
||||
if (!btn || !contextRow) return;
|
||||
|
||||
const tSend = {
|
||||
sequence_id: Math.round(Date.now() / 1000),
|
||||
command: "top_row_menu_action",
|
||||
action: String(btn.dataset.action || ""),
|
||||
bundle_id: String(contextRow.dataset.id || "")
|
||||
};
|
||||
SendWXMessage(JSON.stringify(tSend));
|
||||
hideMenu();
|
||||
});
|
||||
|
||||
closeBtn?.addEventListener("click", () => {
|
||||
const tSend = {
|
||||
sequence_id: Math.round(Date.now() / 1000),
|
||||
command: "close_page"
|
||||
};
|
||||
SendWXMessage(JSON.stringify(tSend));
|
||||
});
|
||||
|
||||
exportbtn?.addEventListener("click", () => {
|
||||
const tSend = {
|
||||
sequence_id: Math.round(Date.now() / 1000),
|
||||
command: "export_page"
|
||||
};
|
||||
SendWXMessage(JSON.stringify(tSend));
|
||||
});
|
||||
|
||||
document.addEventListener("click", (e) => {
|
||||
if (!e.target.closest(".ctx")) hideMenu();
|
||||
});
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape") hideMenu();
|
||||
});
|
||||
}
|
||||
// ========= wx bridge requests =========
|
||||
|
||||
function RequestBundles() {
|
||||
var tSend={};
|
||||
tSend['sequence_id']=Math.round(new Date() / 1000);
|
||||
tSend['command']="request_bundles";
|
||||
|
||||
SendWXMessage(JSON.stringify(tSend));
|
||||
}
|
||||
|
||||
function HandleStudio(pVal) {
|
||||
|
||||
const msg = (typeof pVal === "string") ? safeJsonParse(pVal) : pVal;
|
||||
if (!msg || typeof msg !== "object") return;
|
||||
|
||||
const strCmd = msg.command;
|
||||
if (strCmd === "list_bundles") {
|
||||
unpackPayload(msg);
|
||||
renderTop();
|
||||
// auto-select first bundle if none selected
|
||||
autoSelectFirstBundle();
|
||||
|
||||
const autoUpdateToggle = document.getElementById("auto_update_toggle");
|
||||
if (autoUpdateToggle) {
|
||||
autoUpdateToggle.checked = !!msg.auto_update_enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========= Parse / store =========
|
||||
function unpackPayload(payload) {
|
||||
bundlesById.clear();
|
||||
printersByBundle.clear();
|
||||
filamentsByBundle.clear();
|
||||
presetsByBundle.clear();
|
||||
|
||||
const list = payload?.data || [];
|
||||
for (const bundle of list) {
|
||||
const id = String(bundle.id ?? "");
|
||||
if (!id) continue;
|
||||
|
||||
bundlesById.set(id, {
|
||||
id,
|
||||
name: bundle.name ?? "",
|
||||
type: bundle.type ?? "",
|
||||
version: bundle.version ?? "",
|
||||
path: bundle.path ?? "",
|
||||
update_available: Boolean(bundle.update_available) ,
|
||||
unauthorized: Boolean(bundle.unauthorized)
|
||||
});
|
||||
|
||||
printersByBundle.set(id, new Map((bundle.printers || []).map((name, i) => [i, name])));
|
||||
filamentsByBundle.set(id, new Map((bundle.filaments || []).map((name, i) => [i, name])));
|
||||
presetsByBundle.set(id, new Map((bundle.presets || []).map((name, i) => [i, name])));
|
||||
}
|
||||
}
|
||||
|
||||
// ========= Render: top =========
|
||||
function renderTop() {
|
||||
const bundles = Array.from(bundlesById.values());
|
||||
|
||||
topList.innerHTML = bundles.map(b => `
|
||||
<div class="row" data-id="${escapeAttr(b.id)}" data-bundle-type="${escapeAttr(String(b.type || "").toLowerCase())}">
|
||||
<div class="cell bundle-name-cell" title="${escapeAttr(b.name)}">
|
||||
${b.unauthorized
|
||||
? `<span class="bundle-status-icon bundle-status-icon-unauthorized" title="${escapeAttr(UNAUTHORIZED_TOOLTIP)}" aria-label="${escapeAttr(UNAUTHORIZED_TOOLTIP)}">!</span>`
|
||||
: b.update_available
|
||||
? `<span class="bundle-status-icon bundle-status-icon-update" title="${escapeAttr(UPDATE_TOOLTIP)}" aria-label="${escapeAttr(UPDATE_TOOLTIP)}">↑</span>`
|
||||
: `<span class="bundle-status-icon-spacer" aria-hidden="true"></span>`}
|
||||
${
|
||||
b.type === "Subscribed" ?
|
||||
`<a href="#" class="bundle-name-text bundle-cloud-link" title="Open this bundle in your browser">${escapeHtml(b.name)}</a>`
|
||||
: `<span class="bundle-name-text">${escapeHtml(b.name)}</span>`
|
||||
}
|
||||
</div>
|
||||
<span title="${escapeAttr(b.type)}">${escapeHtml(b.type)}</span>
|
||||
<span title="${escapeAttr(b.version)}">${escapeHtml(b.version)}</span>
|
||||
<div class="cell bundle-update-cell">
|
||||
<button
|
||||
type="button"
|
||||
class="bundle-update-btn ${(!b.unauthorized && b.update_available) ? "is-enabled" : "is-disabled"}"
|
||||
${(!b.unauthorized && b.update_available) ? "" : "disabled"}
|
||||
data-id="${escapeAttr(b.id)}"
|
||||
>Update</button>
|
||||
</div>
|
||||
</div>
|
||||
`).join("");
|
||||
}
|
||||
|
||||
function sendOpenBundleOnCloud(bundleId) {
|
||||
const bundle = bundlesById.get(String(bundleId || ""));
|
||||
if (!bundle) return;
|
||||
|
||||
const tSend = {
|
||||
sequence_id: Math.round(Date.now() / 1000),
|
||||
command: "open_bundle_on_cloud",
|
||||
bundle_id: String(bundle.id || "")
|
||||
};
|
||||
SendWXMessage(JSON.stringify(tSend));
|
||||
}
|
||||
|
||||
function sendUpdateBundleCommand(bundleId) {
|
||||
const bundle = bundlesById.get(String(bundleId || ""));
|
||||
if (!bundle || bundle.unauthorized || !bundle.update_available) return;
|
||||
|
||||
const tSend = {
|
||||
sequence_id: Math.round(Date.now() / 1000),
|
||||
command: "update_bundle",
|
||||
bundle_id: String(bundle.id || "")
|
||||
};
|
||||
SendWXMessage(JSON.stringify(tSend));
|
||||
}
|
||||
|
||||
// ========= Render: bottom (for a selected bundle) =========
|
||||
function renderBottomForBundle(bundleId) {
|
||||
const key = String(bundleId || "");
|
||||
const printers = printersByBundle.get(key) || new Map();
|
||||
const filaments = filamentsByBundle.get(key) || new Map();
|
||||
const presets = presetsByBundle.get(key) || new Map();
|
||||
|
||||
// Convert to a flat list of rows { typeLabel, name }
|
||||
const rows = [];
|
||||
|
||||
for (const [, name] of printers) rows.push({ type: "Printer", name });
|
||||
for (const [, name] of filaments) rows.push({ type: "Filament", name });
|
||||
for (const [, name] of presets) rows.push({ type: "Preset", name });
|
||||
|
||||
bottomList.innerHTML = rows.map((r, idx) => `
|
||||
<div class="row" data-id="${escapeAttr(bundleId)}" data-idx="${idx}">
|
||||
<span>${escapeHtml(r.name)}</span>
|
||||
<span title="${escapeAttr(r.type)}">${escapeHtml(r.type)}</span>
|
||||
</div>
|
||||
`).join("");
|
||||
}
|
||||
|
||||
// ========= Selection helpers =========
|
||||
function clearSelection() {
|
||||
document.querySelectorAll(".row.selected").forEach(r => r.classList.remove("selected"));
|
||||
}
|
||||
|
||||
function selectTopRow(rowEl) {
|
||||
// only clear selection in top list, not bottom
|
||||
topList.querySelectorAll(".row.selected").forEach(r => r.classList.remove("selected"));
|
||||
rowEl.classList.add("selected");
|
||||
}
|
||||
|
||||
function autoSelectFirstBundle() {
|
||||
if (selectedBundleId && bundlesById.has(selectedBundleId)) {
|
||||
// reselect existing
|
||||
const el = topList.querySelector(`.row[data-id="${cssEscape(selectedBundleId)}"]`);
|
||||
if (el) selectTopRow(el);
|
||||
renderBottomForBundle(selectedBundleId);
|
||||
return;
|
||||
}
|
||||
|
||||
const first = topList.querySelector(".row");
|
||||
if (!first) {
|
||||
bottomList.innerHTML = "";
|
||||
selectedBundleId = null;
|
||||
return;
|
||||
}
|
||||
|
||||
selectTopRow(first);
|
||||
selectedBundleId = first.dataset.id;
|
||||
renderBottomForBundle(selectedBundleId);
|
||||
}
|
||||
|
||||
function showSubscribedMenu(x, y) {
|
||||
if (!ctxMenu) return;
|
||||
ctxMenu.style.left = `${x}px`;
|
||||
ctxMenu.style.top = `${y}px`;
|
||||
ctxMenu.hidden = false;
|
||||
ctxMenuDelete.hidden = true;
|
||||
ctxMenuSubscribed.hidden = false;
|
||||
}
|
||||
|
||||
function showMenu(x, y) {
|
||||
if (!ctxMenu) return;
|
||||
ctxMenu.style.left = `${x}px`;
|
||||
ctxMenu.style.top = `${y}px`;
|
||||
ctxMenu.hidden = false;
|
||||
ctxMenuDelete.hidden = false;
|
||||
ctxMenuSubscribed.hidden = true;
|
||||
}
|
||||
|
||||
function hideMenu() {
|
||||
if (!ctxMenu) return;
|
||||
ctxMenu.hidden = true;
|
||||
ctxMenuSubscribed.hidden = true;
|
||||
contextRow = null;
|
||||
}
|
||||
// ========= Utilities =========
|
||||
function safeJsonParse(s) {
|
||||
try { return JSON.parse(s); } catch { return null; }
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
return String(str)
|
||||
.replaceAll("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll(">", ">")
|
||||
.replaceAll('"', """)
|
||||
.replaceAll("'", "'");
|
||||
}
|
||||
|
||||
function escapeAttr(str) {
|
||||
// minimal attribute escaping
|
||||
return escapeHtml(str);
|
||||
}
|
||||
|
||||
function cssEscape(str) {
|
||||
// basic css escape for attribute selectors
|
||||
return String(str).replaceAll('"', '\\"');
|
||||
}
|
||||
394
resources/web/dialog/PresetBundleDialog/styles.css
Normal file
394
resources/web/dialog/PresetBundleDialog/styles.css
Normal file
@@ -0,0 +1,394 @@
|
||||
:root {
|
||||
--bg: #ffffff;
|
||||
--panel: #ffffff;
|
||||
--border: #d8d8d8;
|
||||
--border-strong: #e6e6e6;
|
||||
--border-soft: #f0f0f0;
|
||||
--col-sep: #e1e1e1;
|
||||
|
||||
--text: #1f2328;
|
||||
--row-hover: #f7f9fb;
|
||||
--row-selected: #eaf2ff;
|
||||
--row-selected-outline: #b7d0ff;
|
||||
|
||||
--footer-bg: #fafafa;
|
||||
--btn-bg: #ffffff;
|
||||
--btn-border: #cccccc;
|
||||
--btn-hover: #f0f0f0;
|
||||
|
||||
--ctx-bg: #ffffff;
|
||||
--ctx-border: #cccccc;
|
||||
--ctx-hover: #efefef;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* App layout: split content + footer */
|
||||
.app {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Split panes */
|
||||
.split {
|
||||
height: 100%;
|
||||
display: grid;
|
||||
grid-template-rows: auto minmax(0, 1.25fr) minmax(140px, 0.75fr);
|
||||
gap: 8px;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.top-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
.toolbar-btn {
|
||||
min-width: 92px;
|
||||
}
|
||||
|
||||
.auto-update-switch {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.auto-update-switch input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.auto-update-label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.auto-update-slider {
|
||||
position: relative;
|
||||
width: 52px;
|
||||
height: 24px;
|
||||
flex: 0 0 52px;
|
||||
border-radius: 999px;
|
||||
background: #b9b9b9;
|
||||
transition: background 0.2s ease;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.auto-update-slider::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.28);
|
||||
transition: transform 0.2s ease;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.auto-update-text {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
letter-spacing: 0.3px;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.auto-update-text-off {
|
||||
right: 7px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.auto-update-text-on {
|
||||
left: 7px;
|
||||
color: #ffffff;
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.auto-update-switch input:checked + .auto-update-slider {
|
||||
background: var(--main-color);
|
||||
}
|
||||
|
||||
.auto-update-switch input:checked + .auto-update-slider::before {
|
||||
transform: translateX(28px);
|
||||
}
|
||||
|
||||
.auto-update-switch input:checked + .auto-update-slider .auto-update-text-on {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.auto-update-switch input:checked + .auto-update-slider .auto-update-text-off {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.auto-update-switch input:not(:checked) + .auto-update-slider .auto-update-text-on {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.auto-update-switch input:not(:checked) + .auto-update-slider .auto-update-text-off {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.auto-update-switch input:focus-visible + .auto-update-slider {
|
||||
outline: 2px solid var(--main-color);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.auto-update-switch input:disabled + .auto-update-slider {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.pane {
|
||||
min-height: 0;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
background: var(--panel);
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
}
|
||||
|
||||
.hdr {
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
border-bottom: 1px solid var(--border-strong);
|
||||
padding: 8px 10px;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.top-cols { grid-template-columns: minmax(0, 2fr) 1fr 1fr 88px; }
|
||||
.bot-cols { grid-template-columns: 2fr 1fr; }
|
||||
|
||||
.top .row { grid-template-columns: minmax(0, 2fr) 1fr 1fr 88px; }
|
||||
.bottom .row { grid-template-columns: 2fr 1fr; }
|
||||
|
||||
.body {
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hdr > *,
|
||||
.row > * {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
padding-right: 8px;
|
||||
border-right: 1px solid var(--col-sep);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.hdr > *:last-child,
|
||||
.row > *:last-child {
|
||||
border-right: none;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.bundle-name-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.bundle-name-text {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.bundle-cloud-link {
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
.bundle-cloud-link:hover,
|
||||
.bundle-cloud-link:focus-visible {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.bundle-status-icon,
|
||||
.bundle-status-icon-spacer {
|
||||
flex: 0 0 14px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.bundle-status-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
background: #f59e0b;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.bundle-status-icon-unauthorized {
|
||||
background: #dc2626;
|
||||
}
|
||||
|
||||
.bundle-status-icon-update {
|
||||
background: var(--main-color);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.bundle-update-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.bundle-update-btn {
|
||||
box-sizing: border-box;
|
||||
min-width: 64px;
|
||||
height: 20px;
|
||||
line-height: 18px;
|
||||
padding: 0 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.bundle-update-btn.is-enabled {
|
||||
background: var(--main-color);
|
||||
color: var(--button-fg-light);
|
||||
}
|
||||
|
||||
.bundle-update-btn.is-enabled:hover {
|
||||
background: var(--main-color-hover);
|
||||
}
|
||||
|
||||
.bundle-update-btn.is-disabled {
|
||||
background: var(--button-bg-disabled);
|
||||
color: var(--button-fg-disabled);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
|
||||
#topList,
|
||||
#bottomList {
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Column separators + clipping */
|
||||
|
||||
.row {
|
||||
padding: 8px 10px;
|
||||
border-bottom: 1px solid var(--border-soft);
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.row:hover { background: var(--row-hover); }
|
||||
|
||||
.row.selected {
|
||||
background: var(--row-selected);
|
||||
outline: 1px solid var(--row-selected-outline);
|
||||
}
|
||||
|
||||
/* Footer styling
|
||||
.footer {
|
||||
border-top: 1px solid var(--border);
|
||||
padding: 10px 16px;
|
||||
background: var(--footer-bg);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.footer-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--btn-border);
|
||||
background: var(--btn-bg);
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.footer-btn:hover { background: var(--btn-hover); } */
|
||||
|
||||
/* Context menu */
|
||||
.ctx {
|
||||
position: fixed;
|
||||
min-width: 180px;
|
||||
background: var(--ctx-bg);
|
||||
border: 1px solid var(--ctx-border);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 8px 20px rgba(0,0,0,.18);
|
||||
padding: 4px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.ctx-item,
|
||||
.ctx-item-subscribed, .ctx-item-delete {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--text);
|
||||
padding: 8px 10px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.ctx-item:hover,
|
||||
.ctx-item-subscribed:hover ,
|
||||
.ctx-item-delete:hover
|
||||
{ background: var(--ctx-hover); }
|
||||
|
||||
.ctx-item[hidden],
|
||||
.ctx-item-subscribed[hidden] ,
|
||||
.ctx-item-delete[hidden]{
|
||||
display: none;
|
||||
}
|
||||
|
||||
#AcceptArea {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
173
resources/web/dialog/css/common.css
Normal file
173
resources/web/dialog/css/common.css
Normal file
@@ -0,0 +1,173 @@
|
||||
*
|
||||
{
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
font-family: "system-ui", "Segoe UI", Roboto, Oxygen, Ubuntu, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-sans;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
html
|
||||
{
|
||||
height:100%;
|
||||
background-color: #626262;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
height:100%;
|
||||
max-height: 660px;
|
||||
max-width: 820px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.TextPoint
|
||||
{
|
||||
font-size:1px;
|
||||
}
|
||||
|
||||
.ZScrol::-webkit-scrollbar {/*滚动条整体样式*/
|
||||
width: 12px; /*高宽分别对应横竖滚动条的尺寸*/
|
||||
height: 12px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.ZScrol::-webkit-scrollbar-thumb {/*滚动条里面小方块*/
|
||||
border-radius: 6px;
|
||||
-webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
|
||||
box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
|
||||
background-color: #AAAAAA;
|
||||
}
|
||||
|
||||
.ZScrol::-webkit-scrollbar-track {/*滚动条里面轨道*/
|
||||
-webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
|
||||
box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
|
||||
border-radius: 10px;
|
||||
background: #EDEDED;
|
||||
}
|
||||
|
||||
/*----Three Part----*/
|
||||
body
|
||||
{
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
|
||||
#Title
|
||||
{
|
||||
height: 12%;
|
||||
display: flex;
|
||||
text-align: center;
|
||||
flex-direction:column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#Title div
|
||||
{
|
||||
font-size:28px;
|
||||
line-height: 28px;
|
||||
color: #009688;
|
||||
padding: 0px 10mm;
|
||||
}
|
||||
|
||||
|
||||
#Content
|
||||
{
|
||||
height: 76%;
|
||||
padding: 20px 40px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
color: #464646;
|
||||
position: relative;
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#Content div
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
#AcceptArea
|
||||
{
|
||||
height:var(--dialog-button-sizer-height); /*----ORCA Use fixed size to prevent position change----*/
|
||||
max-height:var(--dialog-button-sizer-height);
|
||||
min-height:var(--dialog-button-sizer-height);
|
||||
padding: 0 var(--dialog-button-gap);
|
||||
text-align: left;
|
||||
display: flex;
|
||||
justify-content:flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/*---HyperLink---*/
|
||||
.HyperLink
|
||||
{
|
||||
color: #009688;
|
||||
text-decoration: underline;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*---Checkboxes ORCA ---*/
|
||||
label:has(input[type="checkbox"]){
|
||||
margin:0;
|
||||
padding: 0;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
label:has(input[type="checkbox"])>span{
|
||||
vertical-align: middle;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background-color:#FFFFFF;
|
||||
margin:0;
|
||||
margin-right: 6px;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border: 0.1em solid #DBDBDB;
|
||||
border-radius: 0.15em;
|
||||
display: inline-flex;
|
||||
place-content: center;
|
||||
background-color:#FFFFFF;
|
||||
}
|
||||
|
||||
input[type="checkbox"]::before {
|
||||
content: "";
|
||||
width: 0.8em;
|
||||
height: 0.8em;
|
||||
transform: scale(0);
|
||||
box-shadow: inset 1em 1em #FFFFFF;
|
||||
clip-path: polygon(7% 37%, 0 45%, 33% 78%, 100% 30%, 95% 21%, 33% 64%);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked {
|
||||
border-color:#009688;
|
||||
background-color:#009688;
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked::before {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
/*----------------Light Mode-------------------*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
100
resources/web/dialog/css/dark.css
Normal file
100
resources/web/dialog/css/dark.css
Normal file
@@ -0,0 +1,100 @@
|
||||
:root {
|
||||
--bg: #1b1f24;
|
||||
--panel: #242a31;
|
||||
--border: #3a424d;
|
||||
--border-strong: #3a424d;
|
||||
--border-soft: #313843;
|
||||
--col-sep: #3a424d;
|
||||
|
||||
--text: #e6ebf0;
|
||||
--row-hover: #2b3340;
|
||||
--row-selected: #244945;
|
||||
--row-selected-outline: #00bfa5;
|
||||
|
||||
--footer-bg: #20262d;
|
||||
--btn-bg: #2a313a;
|
||||
--btn-border: #4b5664;
|
||||
--btn-hover: #333c47;
|
||||
|
||||
--ctx-bg: #2a313a;
|
||||
--ctx-border: #4b5664;
|
||||
--ctx-hover: #3a4451;
|
||||
}
|
||||
|
||||
*
|
||||
{
|
||||
color: #efeff0;
|
||||
border-color: #B9B9BC;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
background-color:#2D2D31; /* ORCA match background color */
|
||||
color: #efeff0;
|
||||
}
|
||||
|
||||
.ZScrol::-webkit-scrollbar-thumb {/*滚动条里面小方块*/
|
||||
background-color: #939594;
|
||||
}
|
||||
|
||||
.ZScrol::-webkit-scrollbar-track {/*滚动条里面轨道*/
|
||||
background: #161817;
|
||||
}
|
||||
|
||||
#Title div
|
||||
{
|
||||
color: #009688;
|
||||
}
|
||||
|
||||
.search>input[type=text]{
|
||||
background-color:#2D2D31;
|
||||
}
|
||||
|
||||
/*---Checkboxes ORCA---*/
|
||||
input[type=checkbox]{
|
||||
background-color:#2D2D31;
|
||||
border-color:#4A4A51;
|
||||
}
|
||||
|
||||
input[type=checkbox]:checked{
|
||||
background-color:#009688;
|
||||
}
|
||||
|
||||
/*-------Text------*/
|
||||
|
||||
.TextS1
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
.TextS2
|
||||
{
|
||||
color:#B9B9BC;
|
||||
}
|
||||
|
||||
/*---Policy---*/
|
||||
.TextArea1
|
||||
{
|
||||
background-color: #4A4A51;
|
||||
color: #BEBEC0;
|
||||
}
|
||||
|
||||
/*----Region---*/
|
||||
.RegionItem:hover
|
||||
{
|
||||
background-color:#4C4C55;
|
||||
}
|
||||
|
||||
.RegionSelected:hover
|
||||
{
|
||||
background-color:#009688;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/*----Menu----*/
|
||||
#Title div.TitleUnselected
|
||||
{
|
||||
color: #BEBEC0;
|
||||
}
|
||||
|
||||
|
||||
35
resources/web/dialog/css/home.css
Normal file
35
resources/web/dialog/css/home.css
Normal file
@@ -0,0 +1,35 @@
|
||||
#FullArea
|
||||
{
|
||||
height:100%;
|
||||
}
|
||||
|
||||
|
||||
#LoadProgress
|
||||
{
|
||||
position:fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 3mm;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#PercentTip
|
||||
{
|
||||
width:70%;
|
||||
height: 100%;
|
||||
background-color: #335DFC;
|
||||
}
|
||||
|
||||
|
||||
#PageArea
|
||||
{
|
||||
width:100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#IEPage
|
||||
{
|
||||
height:100%;
|
||||
width: 100%;
|
||||
}
|
||||
58
resources/web/dialog/css/test.css
Normal file
58
resources/web/dialog/css/test.css
Normal file
@@ -0,0 +1,58 @@
|
||||
*
|
||||
{
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
html,body
|
||||
{
|
||||
height:100%;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
#PageArea
|
||||
{
|
||||
height:100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.swiper {
|
||||
height: 100%;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
bottom: 35px;
|
||||
}
|
||||
|
||||
.swiper-slide {
|
||||
font-size: 18px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.swiper-slide iframe
|
||||
{
|
||||
width:100%;
|
||||
height: 100%;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.CBtn
|
||||
{
|
||||
padding: 3mm 100m;
|
||||
background-color: #77EFF9;
|
||||
display: inline-block;
|
||||
height: 30px;
|
||||
width: 150px;
|
||||
text-align: center;
|
||||
z-index: 99;
|
||||
position: absolute;
|
||||
top:30px;
|
||||
}
|
||||
21
resources/web/dialog/js/common.js
Normal file
21
resources/web/dialog/js/common.js
Normal file
@@ -0,0 +1,21 @@
|
||||
function ClosePage() {
|
||||
var tSend = {};
|
||||
tSend['sequence_id'] = Math.round(new Date() / 1000);
|
||||
tSend['command'] = "close_page";
|
||||
SendWXMessage(JSON.stringify(tSend));
|
||||
}
|
||||
|
||||
document.onkeydown = function (event) {
|
||||
var e = event || window.event || arguments.callee.caller.arguments[0];
|
||||
|
||||
if (window.event) {
|
||||
try { e.keyCode = 0; } catch (e) { }
|
||||
e.returnValue = false;
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('wheel', function (event) {
|
||||
if (event.ctrlKey === true || event.metaKey) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}, { passive: false });
|
||||
339
resources/web/dialog/js/globalapi.js
Normal file
339
resources/web/dialog/js/globalapi.js
Normal file
@@ -0,0 +1,339 @@
|
||||
|
||||
/*------------------ Date Function ------------------------*/
|
||||
function GetFullToday( )
|
||||
{
|
||||
var d=new Date();
|
||||
|
||||
var nday=d.getDate();
|
||||
var nmonth=d.getMonth()+1;
|
||||
var nyear=d.getFullYear();
|
||||
|
||||
var strM=nmonth+'';
|
||||
if( nmonth<10 )
|
||||
strM='0'+nmonth;
|
||||
|
||||
var strD=nday+'';
|
||||
if( nday<10 )
|
||||
strD='0'+nday;
|
||||
|
||||
return nyear+'-'+strM+'-'+strD;
|
||||
}
|
||||
|
||||
function GetFullDate()
|
||||
{
|
||||
var d=new Date();
|
||||
|
||||
var tDate={};
|
||||
|
||||
tDate.nyear=d.getFullYear();
|
||||
tDate.nmonth=d.getMonth()+1;
|
||||
tDate.nday=d.getDate();
|
||||
|
||||
tDate.nhour=d.getHours();
|
||||
tDate.nminute=d.getMinutes();
|
||||
tDate.nsecond=d.getSeconds();
|
||||
|
||||
tDate.nweek=d.getDay();
|
||||
tDate.ndate=d.getDate();
|
||||
|
||||
var strM=tDate.nmonth+'';
|
||||
if( tDate.nmonth<10 )
|
||||
strM='0'+tDate.nmonth;
|
||||
|
||||
var strD=tDate.nday+'';
|
||||
if( tDate.nday<10 )
|
||||
strD='0'+tDate.nday;
|
||||
|
||||
var strH=tDate.nhour+'';
|
||||
if( tDate.nhour<10 )
|
||||
strH='0'+tDate.nhour;
|
||||
|
||||
var strMin=tDate.nminute+'';
|
||||
if( tDate.nminute<10 )
|
||||
strMin='0'+tDate.nminute;
|
||||
|
||||
var strS=tDate.nsecond+'';
|
||||
if( tDate.nsecond<10 )
|
||||
strS='0'+tDate.nsecond;
|
||||
|
||||
tDate.strdate=tDate.nyear+'-'+strM+'-'+strD;
|
||||
tDate.strFulldate=tDate.strdate+' '+strH+':'+strMin+':'+strS;
|
||||
|
||||
return tDate;
|
||||
}
|
||||
|
||||
|
||||
function Unixtimestamp2Date( nSecond )
|
||||
{
|
||||
var d=new Date(nSecond*1000);
|
||||
|
||||
var tDate={};
|
||||
|
||||
tDate.nyear=d.getFullYear();
|
||||
tDate.nmonth=d.getMonth()+1;
|
||||
tDate.nday=d.getDate();
|
||||
|
||||
tDate.nhour=d.getHours();
|
||||
tDate.nminute=d.getMinutes();
|
||||
tDate.nsecond=d.getSeconds();
|
||||
|
||||
tDate.nweek=d.getDay();
|
||||
tDate.ndate=d.getDate();
|
||||
|
||||
var strM=tDate.nmonth+'';
|
||||
if( tDate.nmonth<10 )
|
||||
strM='0'+tDate.nmonth;
|
||||
|
||||
var strD=tDate.nday+'';
|
||||
if( tDate.nday<10 )
|
||||
strD='0'+tDate.nday;
|
||||
|
||||
tDate.strdate=tDate.nyear+'-'+strM+'-'+strD;
|
||||
|
||||
return tDate.strdate;
|
||||
}
|
||||
|
||||
|
||||
//------------Array Function-------------
|
||||
Array.prototype.in_array = function (e) {
|
||||
let sArray= ',' + this.join(this.S) + ',';
|
||||
let skey=','+e+',';
|
||||
|
||||
if(sArray.indexOf(skey)>=0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------String Function------------------
|
||||
/**
|
||||
* Delete Left/Right Side Blank
|
||||
*/
|
||||
String.prototype.trim=function()
|
||||
{
|
||||
return this.replace(/(^\s*)|(\s*$)/g, '');
|
||||
}
|
||||
/**
|
||||
* Delete Left Side Blank
|
||||
*/
|
||||
String.prototype.ltrim=function()
|
||||
{
|
||||
return this.replace(/(^\s*)/g,'');
|
||||
}
|
||||
/**
|
||||
* Delete Right Side Blank
|
||||
*/
|
||||
String.prototype.rtrim=function()
|
||||
{
|
||||
return this.replace(/(\s*$)/g,'');
|
||||
}
|
||||
|
||||
|
||||
//----------------Get Param-------------
|
||||
function GetQueryString(name)
|
||||
{
|
||||
var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");
|
||||
var r = window.location.search.substr(1).match(reg);
|
||||
if (r!=null)
|
||||
{
|
||||
return unescape(r[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function GetGetStr()
|
||||
{
|
||||
let strGet="";
|
||||
|
||||
//获取当前URL
|
||||
let url = document.location.href;
|
||||
|
||||
//获取?的位置
|
||||
let index = url.indexOf("?")
|
||||
if(index != -1) {
|
||||
//截取出?后面的字符串
|
||||
strGet = url.substr(index + 1);
|
||||
}
|
||||
|
||||
return strGet;
|
||||
}
|
||||
|
||||
|
||||
/*--------------------JSON Function------------*/
|
||||
|
||||
/*
|
||||
功能:检查一个字符串是不是标准的JSON格式
|
||||
参数: strJson 被检查的字符串
|
||||
返回值: 如果字符串是一个标准的JSON格式,则返回JSON对象
|
||||
如果字符串不是标准JSON格式,则返回null
|
||||
*/
|
||||
function IsJson( strJson )
|
||||
{
|
||||
var tJson=null;
|
||||
try
|
||||
{
|
||||
tJson=JSON.parse(strJson);
|
||||
}
|
||||
catch(exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return tJson;
|
||||
}
|
||||
|
||||
/*-----------------------Ajax Function--------------------*/
|
||||
/*对JQuery的Ajax函数的封装,只支持异步
|
||||
参数说明:
|
||||
url 目标地址
|
||||
action post/get
|
||||
data 字符串格式的发送内容
|
||||
asyn true---异步模式;false-----同步模式;
|
||||
*/
|
||||
function HttpReq( url,action, data,callbackfunc)
|
||||
{
|
||||
var strAction=action.toLowerCase();
|
||||
|
||||
if( strAction=="post")
|
||||
{
|
||||
$.post(url,data,callbackfunc);
|
||||
}
|
||||
else if( strAction=="get")
|
||||
{
|
||||
$.get(url,callbackfunc);
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------Cookie Function-------------------*/
|
||||
function setCookie(name, value, time='',path='') {
|
||||
if(time && path){
|
||||
var strsec = time * 1000;
|
||||
var exp = new Date();
|
||||
exp.setTime(exp.getTime() + strsec * 1);
|
||||
document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString() + ";path="+path;
|
||||
}else if(time){
|
||||
var strsec = time * 1000;
|
||||
var exp = new Date();
|
||||
exp.setTime(exp.getTime() + strsec * 1);
|
||||
document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString();
|
||||
}else if(path){
|
||||
document.cookie = name + "=" + escape(value) + ";path="+path;
|
||||
}else{
|
||||
document.cookie = name + "=" + escape(value);
|
||||
}
|
||||
}
|
||||
|
||||
function getCookie(c_name)
|
||||
{
|
||||
if(document.cookie.length > 0) {
|
||||
c_start = document.cookie.indexOf(c_name + "=");//获取字符串的起点
|
||||
if(c_start != -1) {
|
||||
c_start = c_start + c_name.length + 1;//获取值的起点
|
||||
c_end = document.cookie.indexOf(";", c_start);//获取结尾处
|
||||
if(c_end == -1) c_end = document.cookie.length;//如果是最后一个,结尾就是cookie字符串的结尾
|
||||
return decodeURI(document.cookie.substring(c_start, c_end));//截取字符串返回
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
function checkCookie(c_name) {
|
||||
username = getCookie(c_name);
|
||||
console.log(username);
|
||||
if (username != null && username != "")
|
||||
{ return true; }
|
||||
else
|
||||
{ return false; }
|
||||
}
|
||||
|
||||
function clearCookie(name) {
|
||||
setCookie(name, "", -1);
|
||||
}
|
||||
|
||||
|
||||
/*--------Studio WX Message-------*/
|
||||
function IsInSlicer()
|
||||
{
|
||||
let bMatch=navigator.userAgent.match( RegExp('BBL-Slicer','i') );
|
||||
|
||||
return bMatch;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function SendWXMessage( strMsg )
|
||||
{
|
||||
let bCheck=IsInSlicer();
|
||||
|
||||
if(bCheck!=null)
|
||||
{
|
||||
window.wx.postMessage(strMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*------CSS Link Control----*/
|
||||
function RemoveCssLink( LinkPath )
|
||||
{
|
||||
let pNow=$("head link[href='"+LinkPath+"']");
|
||||
|
||||
let nTotal=pNow.length;
|
||||
for( let n=0;n<nTotal;n++ )
|
||||
{
|
||||
pNow[n].remove();
|
||||
}
|
||||
}
|
||||
|
||||
function AddCssLink( LinkPath )
|
||||
{
|
||||
var head = document.getElementsByTagName('head')[0];
|
||||
var link = document.createElement('link');
|
||||
link.href = LinkPath;
|
||||
link.rel = 'stylesheet';
|
||||
link.type = 'text/css';
|
||||
head.appendChild(link);
|
||||
}
|
||||
|
||||
function CheckCssLinkExist( LinkPath )
|
||||
{
|
||||
let pNow=$("head link[href='"+LinkPath+"']");
|
||||
let nTotal=pNow.length;
|
||||
|
||||
return nTotal;
|
||||
}
|
||||
|
||||
|
||||
/*------Dark Mode------*/
|
||||
|
||||
function SwitchDarkMode( DarkCssPath )
|
||||
{
|
||||
ExecuteDarkMode( DarkCssPath );
|
||||
setInterval("ExecuteDarkMode('"+DarkCssPath+"')",1000);
|
||||
}
|
||||
|
||||
function ExecuteDarkMode( DarkCssPath )
|
||||
{
|
||||
let nMode=0;
|
||||
let bDarkMode=navigator.userAgent.match( RegExp('dark','i') );
|
||||
if( bDarkMode!=null )
|
||||
nMode=1;
|
||||
|
||||
let nNow=CheckCssLinkExist(DarkCssPath);
|
||||
if( nMode==0 )
|
||||
{
|
||||
if(nNow>0)
|
||||
RemoveCssLink(DarkCssPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(nNow==0)
|
||||
AddCssLink(DarkCssPath);
|
||||
}
|
||||
}
|
||||
|
||||
SwitchDarkMode( "../css/dark.css" );
|
||||
12
resources/web/dialog/js/home.js
Normal file
12
resources/web/dialog/js/home.js
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
|
||||
|
||||
function NextSlide()
|
||||
{
|
||||
$('.swiper-button-next').click();
|
||||
}
|
||||
|
||||
function PreSlide()
|
||||
{
|
||||
$('.swiper-button-prev').click();
|
||||
}
|
||||
4
resources/web/dialog/js/jquery-2.1.1.min.js
vendored
Normal file
4
resources/web/dialog/js/jquery-2.1.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
resources/web/dialog/js/jquery-3.6.0.min.js
vendored
Normal file
2
resources/web/dialog/js/jquery-3.6.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
185
resources/web/dialog/js/json2.js
Normal file
185
resources/web/dialog/js/json2.js
Normal file
@@ -0,0 +1,185 @@
|
||||
var JSON;
|
||||
if (!JSON) {
|
||||
JSON = {};
|
||||
}
|
||||
(function () {
|
||||
'use strict';
|
||||
function f(n) {
|
||||
// Format integers to have at least two digits.
|
||||
return n < 10 ? '0' + n : n;
|
||||
}
|
||||
if (typeof Date.prototype.toJSON !== 'function') {
|
||||
Date.prototype.toJSON = function (key) {
|
||||
|
||||
return isFinite(this.valueOf())
|
||||
? this.getUTCFullYear() + '-' +
|
||||
f(this.getUTCMonth() + 1) + '-' +
|
||||
f(this.getUTCDate()) + 'T' +
|
||||
f(this.getUTCHours()) + ':' +
|
||||
f(this.getUTCMinutes()) + ':' +
|
||||
f(this.getUTCSeconds()) + 'Z'
|
||||
: null;
|
||||
};
|
||||
|
||||
String.prototype.toJSON =
|
||||
Number.prototype.toJSON =
|
||||
Boolean.prototype.toJSON = function (key) {
|
||||
return this.valueOf();
|
||||
};
|
||||
}
|
||||
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||
gap,
|
||||
indent,
|
||||
meta = { // table of character substitutions
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"' : '\\"',
|
||||
'\\': '\\\\'
|
||||
},
|
||||
rep;
|
||||
function quote(string) {
|
||||
escapable.lastIndex = 0;
|
||||
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
|
||||
var c = meta[a];
|
||||
return typeof c === 'string'
|
||||
? c
|
||||
: '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||
}) + '"' : '"' + string + '"';
|
||||
}
|
||||
function str(key, holder) {
|
||||
var i, // The loop counter.
|
||||
k, // The member key.
|
||||
v, // The member value.
|
||||
length,
|
||||
mind = gap,
|
||||
partial,
|
||||
value = holder[key];
|
||||
if (value && typeof value === 'object' &&
|
||||
typeof value.toJSON === 'function') {
|
||||
value = value.toJSON(key);
|
||||
}
|
||||
if (typeof rep === 'function') {
|
||||
value = rep.call(holder, key, value);
|
||||
}
|
||||
switch (typeof value) {
|
||||
case 'string':
|
||||
return quote(value);
|
||||
case 'number':
|
||||
return isFinite(value) ? String(value) : 'null';
|
||||
case 'boolean':
|
||||
case 'null':
|
||||
return String(value);
|
||||
case 'object':
|
||||
if (!value) {
|
||||
return 'null';
|
||||
}
|
||||
gap += indent;
|
||||
partial = [];
|
||||
if (Object.prototype.toString.apply(value) === '[object Array]') {
|
||||
length = value.length;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
partial[i] = str(i, value) || 'null';
|
||||
}
|
||||
v = partial.length === 0
|
||||
? '[]'
|
||||
: gap
|
||||
? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
|
||||
: '[' + partial.join(',') + ']';
|
||||
gap = mind;
|
||||
return v;
|
||||
}
|
||||
if (rep && typeof rep === 'object') {
|
||||
length = rep.length;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
if (typeof rep[i] === 'string') {
|
||||
k = rep[i];
|
||||
v = str(k, value);
|
||||
if (v) {
|
||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (k in value) {
|
||||
if (Object.prototype.hasOwnProperty.call(value, k)) {
|
||||
v = str(k, value);
|
||||
if (v) {
|
||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
v = partial.length === 0
|
||||
? '{}'
|
||||
: gap
|
||||
? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
|
||||
: '{' + partial.join(',') + '}';
|
||||
gap = mind;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
if (typeof JSON.stringify !== 'function') {
|
||||
JSON.stringify = function (value, replacer, space) {
|
||||
var i;
|
||||
gap = '';
|
||||
indent = '';
|
||||
if (typeof space === 'number') {
|
||||
for (i = 0; i < space; i += 1) {
|
||||
indent += ' ';
|
||||
}
|
||||
} else if (typeof space === 'string') {
|
||||
indent = space;
|
||||
}
|
||||
rep = replacer;
|
||||
if (replacer && typeof replacer !== 'function' &&
|
||||
(typeof replacer !== 'object' ||
|
||||
typeof replacer.length !== 'number')) {
|
||||
throw new Error('JSON.stringify');
|
||||
}
|
||||
return str('', {'': value});
|
||||
};
|
||||
}
|
||||
if (typeof JSON.parse !== 'function') {
|
||||
JSON.parse = function (text, reviver) {
|
||||
var j;
|
||||
function walk(holder, key) {
|
||||
var k, v, value = holder[key];
|
||||
if (value && typeof value === 'object') {
|
||||
for (k in value) {
|
||||
if (Object.prototype.hasOwnProperty.call(value, k)) {
|
||||
v = walk(value, k);
|
||||
if (v !== undefined) {
|
||||
value[k] = v;
|
||||
} else {
|
||||
delete value[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return reviver.call(holder, key, value);
|
||||
}
|
||||
text = String(text);
|
||||
cx.lastIndex = 0;
|
||||
if (cx.test(text)) {
|
||||
text = text.replace(cx, function (a) {
|
||||
return '\\u' +
|
||||
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||
});
|
||||
}
|
||||
if (/^[\],:{}\s]*$/
|
||||
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
|
||||
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
|
||||
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
|
||||
j = eval('(' + text + ')');
|
||||
return typeof reviver === 'function'
|
||||
? walk({'': j}, '')
|
||||
: j;
|
||||
}
|
||||
throw new SyntaxError('JSON.parse');
|
||||
};
|
||||
}
|
||||
}());
|
||||
Reference in New Issue
Block a user