added build management, WIP custom cache checks

This commit is contained in:
FinnHornhoover 2023-10-22 23:10:10 +03:00
parent 547ca778b0
commit cdabb36639
5 changed files with 535 additions and 58 deletions

View File

@ -115,6 +115,26 @@ body {
border-color: #6699ff; border-color: #6699ff;
} }
#of-addversionmodal > .modal-dialog > .modal-content {
background-color: #093363;
border-color: #6699ff;
}
#of-editversionmodal > .modal-dialog > .modal-content {
background-color: #093363;
border-color: #6699ff;
}
#of-deleteversionmodal > .modal-dialog > .modal-content {
background-color: #093363;
border-color: #6699ff;
}
#of-restoreversionsmodal > .modal-dialog > .modal-content {
background-color: #093363;
border-color: #6699ff;
}
.form-control, .form-control,
.form-control:focus { .form-control:focus {
border-color: #0099ff; border-color: #0099ff;

View File

@ -16,9 +16,12 @@ var cacheRoot = path.join(
); );
var offlineRoot = path.join(cacheRoot, "Offline"); var offlineRoot = path.join(cacheRoot, "Offline");
var cdnString = "http://cdn.dexlabs.systems/ff/big";
var versionArray; var versionArray;
var serverArray; var serverArray;
var cacheSizes; var cacheSizes;
var defaultHashes;
var config; var config;
function enableServerListButtons() { function enableServerListButtons() {
@ -39,6 +42,30 @@ function disableServerListButtons() {
$("#of-deleteserver-button").prop("disabled", true); $("#of-deleteserver-button").prop("disabled", true);
} }
function enableVersionAddButton() {
$("#of-addversion-button").removeClass("disabled");
$("#of-addversion-button").prop("disabled", false);
}
function enableVersionListButtons() {
$("#of-editversion-button").removeClass("disabled");
$("#of-editversion-button").prop("disabled", false);
$("#of-deleteversion-button").removeClass("disabled");
$("#of-deleteversion-button").prop("disabled", false);
}
function disableVersionAddButton() {
$("#of-addversion-button").addClass("disabled");
$("#of-addversion-button").prop("disabled", true);
}
function disableVersionListButtons() {
$("#of-editversion-button").addClass("disabled");
$("#of-editversion-button").prop("disabled", true);
$("#of-deleteversion-button").addClass("disabled");
$("#of-deleteversion-button").prop("disabled", true);
}
function getAppVersion() { function getAppVersion() {
appVersion = remote.require("app").getVersion(); appVersion = remote.require("app").getVersion();
@ -74,8 +101,6 @@ function addServer() {
jsonToModify["servers"].push(server); jsonToModify["servers"].push(server);
console.log(serversPath);
console.log(JSON.stringify(jsonToModify, null, 4));
remotefs.writeFileSync(serversPath, JSON.stringify(jsonToModify, null, 4)); remotefs.writeFileSync(serversPath, JSON.stringify(jsonToModify, null, 4));
loadServerList(); loadServerList();
} }
@ -124,6 +149,84 @@ function restoreDefaultServers() {
loadServerList(); loadServerList();
} }
function addVersion() {
var jsonToModify = JSON.parse(remotefs.readFileSync(versionsPath));
var version = {};
version["name"] =
$("#addversion-nameinput").val().length == 0
? "custom-build-" + uuidv4().substring(0, 8)
: $("#addversion-nameinput").val();
version["url"] =
$("#addversion-urlinput").val().length == 0
? cdnString + "/" + version["name"] + "/"
: $("#addversion-urlinput").val();
var matchingVersions = jsonToModify["versions"].filter(function (obj) {
return obj.name === version["name"];
});
if (matchingVersions.length > 0) return;
jsonToModify["versions"].push(version);
remotefs.writeFileSync(versionsPath, JSON.stringify(jsonToModify, null, 4));
loadCacheList();
startHashCheck(true);
}
function editVersion() {
var jsonToModify = JSON.parse(remotefs.readFileSync(versionsPath));
var edited = false;
$.each(jsonToModify["versions"], function (key, value) {
if (value["name"] == getSelectedVersion() && !defaultHashes.hasOwnProperty(value["name"])) {
value["name"] =
$("#editversion-nameinput").val().length == 0
? value["name"]
: $("#editversion-nameinput").val();
value["url"] =
$("#editversion-urlinput").val().length == 0
? value["url"]
: $("#editversion-urlinput").val();
edited = true;
}
});
if (!edited) return;
remotefs.writeFileSync(versionsPath, JSON.stringify(jsonToModify, null, 4));
loadCacheList();
startHashCheck(true);
}
function deleteVersion() {
var jsonToModify = JSON.parse(remotefs.readFileSync(versionsPath));
var result = jsonToModify["versions"].filter(function (obj) {
return obj.name === getSelectedVersion();
})[0];
if (defaultHashes.hasOwnProperty(result.name)) return;
var resultindex = jsonToModify["versions"].indexOf(result);
jsonToModify["versions"].splice(resultindex, 1);
remotefs.writeFileSync(versionsPath, JSON.stringify(jsonToModify, null, 4));
loadCacheList();
startHashCheck(true);
}
function restoreDefaultVersions() {
remotefs.copySync(
path.join(__dirname, "/defaults/versions.json"),
versionsPath
);
loadCacheList();
startHashCheck(true);
}
function loadGameVersions() { function loadGameVersions() {
var versionJson = remotefs.readJsonSync(versionsPath); var versionJson = remotefs.readJsonSync(versionsPath);
versionArray = versionJson["versions"]; versionArray = versionJson["versions"];
@ -142,8 +245,8 @@ function loadServerList() {
var serverJson = remotefs.readJsonSync(serversPath); var serverJson = remotefs.readJsonSync(serversPath);
serverArray = serverJson["servers"]; serverArray = serverJson["servers"];
deselectServer(); // Disable buttons until another server is selected
$(".server-listing-entry").remove(); // Clear out old stuff, if any $(".server-listing-entry").remove(); // Clear out old stuff, if any
disableServerListButtons(); // Disable buttons until another server is selected
if (serverArray.length > 0) { if (serverArray.length > 0) {
// Servers were found in the JSON // Servers were found in the JSON
@ -169,6 +272,46 @@ function loadServerList() {
} }
} }
function loadCacheList() {
var versionjson = remotefs.readJsonSync(versionsPath);
versionArray = versionjson["versions"];
if (!defaultHashes) {
defaultHashes = remotefs.readJsonSync(path.join(
__dirname,
"/defaults/hashes.json"
));
}
deselectVersion();
$(".cache-listing-entry").remove();
$.each(versionArray, function (key, value) {
var row = document.createElement("tr");
row.className = "cache-listing-entry"
row.setAttribute("id", value.name);
var cellVersion = document.createElement("td");
cellVersion.textContent = value.name;
cellVersion.className = "text-monospace";
var cellPlayableCache = getCacheInfoCell(value.name, "playable");
var cellOfflineCache = getCacheInfoCell(value.name, "offline");
row.appendChild(cellVersion);
row.appendChild(cellPlayableCache);
row.appendChild(cellOfflineCache);
$("#cache-tablebody").append(row);
});
}
function startHashCheck(forced) {
// only run once unless forced
if (forced || !cacheSizes)
handleCache("hash-check");
}
function getCacheElemID(versionString, cacheMode, elementName) { function getCacheElemID(versionString, cacheMode, elementName) {
return [versionString, cacheMode, "cache", elementName].filter(function (value) { return [versionString, cacheMode, "cache", elementName].filter(function (value) {
return typeof value !== "undefined"; return typeof value !== "undefined";
@ -180,6 +323,9 @@ function getCacheButtonID(versionString, cacheMode, buttonMode) {
} }
function getCacheLabelText(sizes) { function getCacheLabelText(sizes) {
if (!sizes || sizes.total === 0)
return "?.?? GB / ?.?? GB";
var gb = 1 << 30; var gb = 1 << 30;
var labelText = (sizes.intact / gb).toFixed(2) + " / " + (sizes.total / gb).toFixed(2) + " GB"; var labelText = (sizes.intact / gb).toFixed(2) + " / " + (sizes.total / gb).toFixed(2) + " GB";
@ -218,7 +364,7 @@ function getCacheInfoCell(versionString, cacheMode) {
var labelCache = document.createElement("label"); var labelCache = document.createElement("label");
labelCache.setAttribute("id", labelID); labelCache.setAttribute("id", labelID);
labelCache.setAttribute("for", divID); labelCache.setAttribute("for", divID);
labelCache.innerHTML = " 0.00 GB / 0.00 GB" labelCache.innerHTML = getCacheLabelText();
var divCacheButtons = document.createElement("div"); var divCacheButtons = document.createElement("div");
divCacheButtons.setAttribute("id", labelID); divCacheButtons.setAttribute("id", labelID);
@ -263,6 +409,9 @@ function storageLoadingStart(vString, cMode) {
}); });
var cacheModes = (cMode) ? [cMode] : ["offline", "playable"]; var cacheModes = (cMode) ? [cMode] : ["offline", "playable"];
deselectVersion();
disableVersionAddButton();
$.each(versionStrings, function (vKey, versionString) { $.each(versionStrings, function (vKey, versionString) {
$.each(cacheModes, function (cKey, cacheMode) { $.each(cacheModes, function (cKey, cacheMode) {
var buttonDelete = document.getElementById(getCacheButtonID(versionString, cacheMode, "delete")); var buttonDelete = document.getElementById(getCacheButtonID(versionString, cacheMode, "delete"));
@ -337,6 +486,8 @@ function storageLoadingComplete(allSizes) {
} }
}); });
}); });
enableVersionAddButton();
} }
function handleCache(mode, versionString, cacheMode, callback) { function handleCache(mode, versionString, cacheMode, callback) {
@ -344,7 +495,7 @@ function handleCache(mode, versionString, cacheMode, callback) {
return obj.name === versionString; return obj.name === versionString;
}); });
var cdnRoot = (versions.length === 0) ? var cdnRoot = (versions.length === 0) ?
"http://cdn.dexlabs.systems/ff/big" : cdnString :
path.dirname(versions[0].url); path.dirname(versions[0].url);
var lastSizes = { intact: 0, altered: 0, total: 0 }; var lastSizes = { intact: 0, altered: 0, total: 0 };
@ -373,16 +524,20 @@ function handleCache(mode, versionString, cacheMode, callback) {
}); });
server.listen(0, "localhost", function () { server.listen(0, "localhost", function () {
spawn(path.join(__dirname, "lib", "cache_handler.exe"), [ spawn(
"--mode", (mode === "fix") ? "download" : mode, path.join(__dirname, "lib", "cache_handler.exe"),
"--playable-root", cacheRoot, [
"--offline-root", offlineRoot, "--mode", (mode === "fix") ? "download" : mode,
"--user-dir", userData, "--playable-root", cacheRoot,
"--cdn-root", cdnRoot, "--offline-root", offlineRoot,
"--cache-mode", (cacheMode) ? cacheMode : "all", "--user-dir", userData,
"--cache-version", (versionString) ? versionString : "all", "--cdn-root", cdnRoot,
"--port", server.address().port "--cache-mode", (cacheMode) ? cacheMode : "all",
]).on("exit", function (code, signal) { "--cache-version", (versionString) ? versionString : "all",
"--port", server.address().port
],
{ stdio: "inherit" }
).on("exit", function (code, signal) {
if (code !== 0 || signal) { if (code !== 0 || signal) {
dialog.showErrorBox( dialog.showErrorBox(
"Sorry!", "Sorry!",
@ -398,38 +553,6 @@ function handleCache(mode, versionString, cacheMode, callback) {
}); });
} }
function loadCacheList() {
var versionjson = remotefs.readJsonSync(versionsPath);
versionArray = versionjson["versions"];
$(".cache-listing-entry").remove();
$.each(versionArray, function (key, value) {
var row = document.createElement("tr");
row.className = "cache-listing-entry"
row.setAttribute("id", value.name);
var cellVersion = document.createElement("td");
cellVersion.textContent = value.name;
cellVersion.className = "text-monospace";
var cellPlayableCache = getCacheInfoCell(value.name, "playable");
var cellOfflineCache = getCacheInfoCell(value.name, "offline");
row.appendChild(cellVersion);
row.appendChild(cellPlayableCache);
row.appendChild(cellOfflineCache);
$("#cache-tablebody").append(row);
});
}
function startHashCheck() {
// only run once
if (!cacheSizes)
handleCache("hash-check");
}
function performCacheSwap(newVersion) { function performCacheSwap(newVersion) {
var currentCache = path.join(cacheRoot, "FusionFall"); var currentCache = path.join(cacheRoot, "FusionFall");
var newCache = path.join(cacheRoot, newVersion); var newCache = path.join(cacheRoot, newVersion);
@ -594,6 +717,10 @@ function getSelectedServer() {
return $("#server-tablebody > tr.bg-primary").prop("id"); return $("#server-tablebody > tr.bg-primary").prop("id");
} }
function getSelectedVersion() {
return $("#cache-tablebody > tr.bg-primary").prop("id");
}
function connectToServer() { function connectToServer() {
// Get ID of the selected server, which corresponds to its UUID in the json // Get ID of the selected server, which corresponds to its UUID in the json
console.log("Connecting to server with UUID of " + getSelectedServer()); console.log("Connecting to server with UUID of " + getSelectedServer());
@ -615,11 +742,26 @@ function deselectServer() {
$(".server-listing-entry").removeClass("bg-primary"); $(".server-listing-entry").removeClass("bg-primary");
} }
function deselectVersion() {
disableVersionListButtons();
$(".cache-listing-entry").removeClass("bg-primary");
}
$("#server-table").on("click", ".server-listing-entry", function (event) { $("#server-table").on("click", ".server-listing-entry", function (event) {
enableServerListButtons(); enableServerListButtons();
$(this).addClass("bg-primary").siblings().removeClass("bg-primary"); $(this).addClass("bg-primary").siblings().removeClass("bg-primary");
}); });
$("#cache-table").on("click", ".cache-listing-entry", function (event) {
// wait for the add button to be re-enabled first
if ($("#of-addversion-button").prop("disabled")) return;
// do not select default builds
if (defaultHashes.hasOwnProperty($(this).attr("id"))) return;
enableVersionListButtons();
$(this).addClass("bg-primary").siblings().removeClass("bg-primary");
});
// QoL feature: if you double click on a server it will connect // QoL feature: if you double click on a server it will connect
$("#server-table").on("dblclick", ".server-listing-entry", function (event) { $("#server-table").on("dblclick", ".server-listing-entry", function (event) {
$(this).addClass("bg-primary").siblings().removeClass("bg-primary"); $(this).addClass("bg-primary").siblings().removeClass("bg-primary");
@ -627,9 +769,8 @@ $("#server-table").on("dblclick", ".server-listing-entry", function (event) {
}); });
$("#of-editservermodal").on("show.bs.modal", function (e) { $("#of-editservermodal").on("show.bs.modal", function (e) {
var jsonToModify = remotefs.readJsonSync( var jsonToModify = remotefs.readJsonSync(serversPath);
path.join(userData, "servers.json")
);
$.each(jsonToModify["servers"], function (key, value) { $.each(jsonToModify["servers"], function (key, value) {
if (value["uuid"] == getSelectedServer()) { if (value["uuid"] == getSelectedServer()) {
$("#editserver-descinput")[0].value = value["description"]; $("#editserver-descinput")[0].value = value["description"];
@ -646,9 +787,27 @@ $("#of-editservermodal").on("show.bs.modal", function (e) {
}); });
}); });
$("#of-editversionmodal").on("show.bs.modal", function (e) {
var jsonToModify = remotefs.readJsonSync(versionsPath);
$.each(jsonToModify["versions"], function (key, value) {
if (value["name"] == getSelectedVersion()) {
$("#editversion-nameinput")[0].value = value["name"];
$("#editversion-urlinput")[0].value = value["url"];
}
});
});
$("#of-deleteservermodal").on("show.bs.modal", function (e) { $("#of-deleteservermodal").on("show.bs.modal", function (e) {
var result = serverArray.filter(function (obj) { var result = serverArray.filter(function (obj) {
return obj.uuid === getSelectedServer(); return obj.uuid === getSelectedServer();
})[0]; })[0];
$("#deleteserver-servername").html(result.description); $("#deleteserver-servername").html(result.description);
}); });
$("#of-deleteversionmodal").on("show.bs.modal", function (e) {
var result = versionArray.filter(function (obj) {
return obj.name === getSelectedVersion();
})[0];
$("#deleteversion-versionname").html(result.name);
});

View File

@ -152,6 +152,24 @@ def compile_file_list(args: Namespace) -> List[FileInfo]:
with open(Path(args.user_dir) / 'hashes.json') as r: with open(Path(args.user_dir) / 'hashes.json') as r:
hash_dict = json.load(r) hash_dict = json.load(r)
with open(Path(args.user_dir) / 'versions.json') as r:
versions = json.load(r)['versions']
updated = False
for version in versions:
if version['name'] not in hash_dict:
hash_dict[version['name']] = {
'playable_size': 0,
'offline_size': 0,
'playable': {},
'offline': {},
}
updated = True
if updated:
with open(Path(args.user_dir) / 'hashes.json', 'w') as w:
json.dump(hash_dict, w, indent=4)
cache_modes = ['offline', 'playable'] if args.cache_mode == 'all' else [args.cache_mode] cache_modes = ['offline', 'playable'] if args.cache_mode == 'all' else [args.cache_mode]
cache_versions = list(hash_dict) if args.cache_version == 'all' else [args.cache_version] cache_versions = list(hash_dict) if args.cache_version == 'all' else [args.cache_version]

View File

@ -115,11 +115,11 @@
data-placement="bottom" data-placement="bottom"
id="of-editcache-button" id="of-editcache-button"
type="button" type="button"
title="Edit Cache Storage" title="Edit Game Builds"
data-target="#of-editcacheconfigmodal" data-target="#of-editcacheconfigmodal"
onclick="startHashCheck()" onclick="startHashCheck()"
> >
<i class="fas fa-cog"></i> <i class="fas fa-database"></i>
</button> </button>
<button <button
class="btn btn-primary disabled" class="btn btn-primary disabled"
@ -172,6 +172,13 @@
data-target="#of-restoreserversmodal" data-target="#of-restoreserversmodal"
>Reset to Default Servers</a >Reset to Default Servers</a
> >
<a
href="#of-restoreversionsmodal"
onclick="$('#of-aboutmodal').modal('toggle')"
data-toggle="modal"
data-target="#of-restoreversionsmodal"
>Reset to Default Game Builds</a
>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<div class="row flex-fill"> <div class="row flex-fill">
@ -182,7 +189,7 @@
data-bs-tooltip="" data-bs-tooltip=""
type="button" type="button"
title="Github Page" title="Github Page"
onclick="window.open(&#39;https://github.com/OpenFusionProject/OpenFusion&#39;,&#39;_blank&#39;);" onclick="window.open('https://github.com/OpenFusionProject/OpenFusion','_blank');"
> >
<i <i
class="fab fa-github" class="fab fa-github"
@ -194,7 +201,7 @@
data-bs-tooltip="" data-bs-tooltip=""
type="button" type="button"
title="Discord Chat" title="Discord Chat"
onclick="window.open(&#39;https://discord.gg/DYavckB&#39;,&#39;_blank&#39;);" onclick="window.open('https://discord.gg/DYavckB','_blank');"
> >
<i <i
class="fab fa-discord" class="fab fa-discord"
@ -323,7 +330,7 @@
required="" required=""
minlength="1" minlength="1"
maxlength="70" maxlength="70"
/><label for="addserver-ipinput" /><label for="editserver-ipinput"
>Server IP</label >Server IP</label
><input ><input
class="form-control form-row w-75" class="form-control form-row w-75"
@ -352,7 +359,7 @@
Cancel</button Cancel</button
><button ><button
class="btn btn-primary border rounded border-primary btn-success border-success" class="btn btn-primary border rounded border-primary btn-success border-success"
id="addserver-savebutton" id="editserver-savebutton"
type="submit" type="submit"
data-dismiss="modal" data-dismiss="modal"
form="editserver-form" form="editserver-form"
@ -445,7 +452,7 @@
Cancel</button Cancel</button
><button ><button
class="btn btn-primary border rounded border-primary btn-danger border-danger" class="btn btn-primary border rounded border-primary btn-danger border-danger"
id="deleteserver-button" id="restoreservers-button"
type="button" type="button"
data-dismiss="modal" data-dismiss="modal"
onclick="restoreDefaultServers();" onclick="restoreDefaultServers();"
@ -459,9 +466,10 @@
<div <div
class="modal fade" class="modal fade"
role="dialog" role="dialog"
tabindex="-1"
id="of-editcacheconfigmodal" id="of-editcacheconfigmodal"
> >
<div class="modal-dialog modal-dialog-centered" role="document"> <div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title">Edit Cache Config</h4> <h4 class="modal-title">Edit Cache Config</h4>
@ -475,6 +483,56 @@
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div
class="row row-cols-2 d-xl-flex justify-content-center justify-content-xl-start"
id="of-versionbuttons"
style="padding-bottom: 16px"
>
<div
class="col-4 text-left d-inline-flex justify-content-xl-start"
id="cache-buttons"
>
<button
class="btn btn-success mr-1"
data-toggle="modal"
data-bs-tooltip=""
data-placement="bottom"
id="of-addversion-button"
type="button"
title="Add Version"
data-target="#of-addversionmodal"
onclick="deselectVersion()"
>
<i class="fas fa-plus"></i>
</button>
<button
class="btn btn-primary mr-1 disabled"
data-toggle="modal"
data-bs-tooltip=""
data-placement="bottom"
id="of-editversion-button"
type="button"
title="Edit Version"
data-target="#of-editversionmodal"
disabled=""
>
<i class="fas fa-edit"></i>
</button>
<button
class="btn btn-danger mr-1 disabled"
data-toggle="modal"
data-bs-tooltip=""
data-placement="bottom"
id="of-deleteversion-button"
type="button"
title="Delete Version"
data-target="#of-deleteversionmodal"
disabled=""
>
<i class="fas fa-trash-alt"></i>
</button>
</div>
</div>
<div <div
class="table-responsive text-center border rounded border-primary" class="table-responsive text-center border rounded border-primary"
id="cache-table" id="cache-table"
@ -495,6 +553,228 @@
</div> </div>
</div> </div>
</div> </div>
<div
class="modal fade"
role="dialog"
tabindex="-1"
id="of-addversionmodal"
>
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Add Server</h4>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form id="addversion-form" class="needs-validation">
<label for="addversion-nameinput"
>Version Name</label
><input
class="form-control form-row w-75"
type="text"
id="addversion-nameinput"
placeholder="custom-build-000"
required=""
minlength="1"
maxlength="70"
/><label for="addversion-urlinput"
>Version URL</label
><input
class="form-control form-row w-75"
type="text"
id="addversion-urlinput"
placeholder="http://cdn.dexlabs.systems/custom-build-000/"
required=""
pattern="^(https?|file):\/\/\/?([-a-zA-Z0-9@:%._\+~#= ]{1,256}\/){1,64}$"
/>
</form>
</div>
<div class="modal-footer">
<button
class="btn btn-primary border rounded border-primary btn-danger border-danger"
id="addversion-cancel"
type="button"
data-dismiss="modal"
>
Cancel</button
><button
class="btn btn-primary border rounded border-primary btn-success border-success"
id="addversion-savebutton"
type="submit"
data-dismiss="modal"
form="addversion-form"
onclick="addVersion();"
>
Save
</button>
</div>
</div>
</div>
</div>
<div
class="modal fade"
role="dialog"
tabindex="-1"
id="of-editversionmodal"
>
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Edit Version</h4>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form id="addversion-form" class="needs-validation">
<label for="editversion-nameinput"
>Version Name</label
><input
class="form-control form-row w-75"
type="text"
id="editversion-nameinput"
placeholder="custom-build-000"
required=""
minlength="1"
maxlength="70"
/><label for="editversion-urlinput"
>Version URL</label
><input
class="form-control form-row w-75"
type="text"
id="editversion-urlinput"
placeholder="http://cdn.dexlabs.systems/custom-build-000/"
required=""
pattern="^(https?|file):\/\/\/?([-a-zA-Z0-9@:%._\+~#= ]{1,256}\/){1,64}$"
/>
</form>
</div>
<div class="modal-footer">
<button
class="btn btn-primary border rounded border-primary btn-danger border-danger"
id="editversion-cancel"
type="button"
data-dismiss="modal"
>
Cancel</button
><button
class="btn btn-primary border rounded border-primary btn-success border-success"
id="editversion-savebutton"
type="submit"
data-dismiss="modal"
form="editversion-form"
onclick="editVersion();"
>
Save
</button>
</div>
</div>
</div>
</div>
<div
class="modal fade"
role="dialog"
tabindex="-1"
id="of-deleteversionmodal"
>
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Are you sure?</h4>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p class="lead">
Do you really want to delete<br />"<a
id="deleteversion-versionname"
>VERSION_NAME</a
>"?<br /><br />You could always re-add it later.
</p>
</div>
<div class="modal-footer">
<button
class="btn btn-primary border rounded border-primary"
type="button"
data-dismiss="modal"
>
Cancel</button
><button
class="btn btn-primary border rounded border-primary btn-danger border-danger"
id="deleteversion-button"
type="button"
data-dismiss="modal"
onclick="deleteVersion();"
>
Yes, Delete
</button>
</div>
</div>
</div>
</div>
<div
class="modal fade"
role="dialog"
tabindex="-1"
id="of-restoreversionsmodal"
>
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Are you sure?</h4>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p class="lead">
Do you really want to restore the default
versions?
</p>
</div>
<div class="modal-footer">
<button
class="btn btn-primary border rounded border-primary"
type="button"
data-dismiss="modal"
>
Cancel</button
><button
class="btn btn-primary border rounded border-primary btn-danger border-danger"
id="restoreversions-button"
type="button"
data-dismiss="modal"
onclick="restoreDefaultVersions();"
>
Yes, Restore
</button>
</div>
</div>
</div>
</div>
<div id="of-versionnumberdiv"> <div id="of-versionnumberdiv">
<a <a
id="of-versionnumber" id="of-versionnumber"

Binary file not shown.