Removed Vue as it was like putting a round peg in a square hole. Using LDOM now and building UI manually.

This commit is contained in:
punkfairie 2023-05-09 12:55:15 -07:00
parent d70c4143f8
commit d68ae26032
18 changed files with 1021 additions and 199 deletions

5
.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/

View file

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

12
.idea/ed-safari.iml Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/ed-safari.iml" filepath="$PROJECT_DIR$/.idea/ed-safari.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View file

@ -1,145 +1,84 @@
<!DOCTYPE html>
<html>
<head>
<head>
<meta charset="UTF-8" />
<title>ED Safari v0.0.1</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" crossorigin="anonymous">
</head>
<body>
<script src="src/assets/ldom.dev.js"></script>
</head>
<body>
<div id="app">
<!-- CURRENT LOCATION -->
<div class="container-fluid">
<div class="row separator align-items-center">
<div class="col"><hr class="separator"></div>
<div class="col-auto">Current Location</div>
<div class="col"><hr class="separator"></div>
</div>
</div>
<!-- table header -->
<div class="container-fluid">
<div class="row ml-1 mr-1 separator">
<div class="col-3 text-center">Name</div>
<div class="col">Type</div>
<div class="col-auto text-center">Distance</div>
<div class="col-1 text-center">Details</div>
<div class="col-2 text-center">Mapped Value</div>
</div>
</div>
<!-- system name -->
<div class="container-fluid">
<div class="row ml-1 mr-1">
<div id="currentLocation" :class="[
'col',
'system',
{
charted: currentLocation !== 'Unknown',
highlighted: currentLocation === 'Unknown',
'text-center': currentLocation === 'Unknown'
}
]">
<i class="flaticon-solar-system"></i>
{{ currentLocation.name }}
<div :key="reload">
<!-- CURRENT LOCATION -->
<div class="container-fluid">
<div class="row separator align-items-center">
<div class="col"><hr class="separator"></div>
<div class="col-auto">Current Location</div>
<div class="col"><hr class="separator"></div>
</div>
</div>
</div>
<!-- high-value scans -->
<div class="container-fluid">
<div class="row ml-1 mr-1 align-items-center"></div>
</div>
<!-- low-value scans -->
<div class="container-fluid">
<div v-for="body in currentLocationBodies" :key="body.BodyID" class="row ml-1 mr-1">
<div class="col-1 system"><!-- indent --></div>
<div class="col-2 system charted text-left">
<i :class="{
'flaticon-jupiter-3': body.isPlanet(),
'flaticon-star': body.isStar(),
'flaticon-asteroid-4': body.isAsteroid()
}"></i>
{{ body.simpleName() }}
<!-- table header -->
<div class="container-fluid">
<div class="row ml-1 mr-1 separator">
<div class="col-3 text-center">Name</div>
<div class="col">Type</div>
<div class="col-auto text-center">Distance</div>
<div class="col-1 text-center">Details</div>
<div class="col-2 text-center">Mapped Value</div>
</div>
<div class="col pr-0 mr-0 system charted">
<i :class="{
'flaticon-star': body.isStar(),
'flaticon-asteroid-4': body.isAsteroid(),
'flaticon-ingot': body.PlanetClass?.toLowerCase().includes('metal'),
'flaticon-snowflake': body.PlanetClass?.toLowerCase().includes('icy'),
'flaticon-earth': body.PlanetClass?.toLowerCase().includes('earth'),
'flaticon-jupiter-1': body.PlanetClass?.toLowerCase().includes('gas giant'),
'flaticon-asteroid-3': body.PlanetClass?.toLowerCase().includes('rock'),
'flaticon-water-drops': body.PlanetClass?.toLowerCase().includes('water') || body.PlanetClass?.toLowerCase().includes('ammonia'),
}"></i>
<!-- rings -->
<template v-if="body.Rings">
)
<template v-for="(ring, index) in body.Rings" :key="index">
<i :class="{
'flaticon-gold-bars': ring.RingClass === 'eRingClass_MetalRich',
'flaticon-ingot': ring.RingClass === 'eRingClass_Metalic' || ring.RingClass === 'eRingClass_Metallic',
'flaticon-snowflake': ring.RingClass === 'eRingClass_Icy',
'flaticon-asteroid-3': ring.RingClass === 'eRingClass_Rocky',
}"></i> )
</template>
</template>
{{ body.PlanetClass || body.StarType || '' }}
</div>
<div class="col-auto pl-2 ml-0 system charted">
{{ body.distance() }}
</div>
<div class="col-1 system charted">
<i :class="{
'flaticon-cooling-tower': true,
'opacity-0': !(body.isPlanet() && body.TerraformState),
}"></i>
<i :class="{
'flaticon-flag-outline-on-a-pole-with-stars-around': true,
'opacity-0': !(body.isPlanet() && !body.WasMapped),
}"></i>
</div>
<div class="col-2 system charted text-right"><!-- mapped value --></div>
</div>
</div>
<!-- NAV ROUTE -->
<div class="container-fluid">
<div class="row separator align-items-center">
<div class="col"><hr class="separator"></div>
<div class="col-auto">Nav Route</div>
<div class="col"><hr class="separator"></div>
<!-- system name -->
<div class="container-fluid">
<div class="row ml-1 mr-1">
<div id="currentSystem" class="col highlighted text-center">
<i id="currentSystemIcon" class="flaticon-solar-system hidden"></i>
<span id="currentSystemName">Unknown</span>
</div>
</div>
</div>
</div>
<!-- table header -->
<div class="container-fluid">
<div class="row ml-1 mr-1 separator">
<div class="col-1 text-right">LY</div>
<div class="col-3 text-center">Name</div>
<div class="col">Type</div>
<div class="col-2 text-center">Mapped Value</div>
<!-- high-value scans -->
<div class="container-fluid">
<div class="row ml-1 mr-1 align-items-center">
<div class="col system charted">
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row separator align-items-center">
<div class="col"><hr class="seperator"></div>
<!-- low-value scans -->
<div class="container-fluid" id="lowValueScans">
</div>
<!-- NAV ROUTE -->
<div class="container-fluid">
<div class="row separator align-items-center">
<div class="col"><hr class="separator"></div>
<div class="col-auto">Nav Route</div>
<div class="col"><hr class="separator"></div>
</div>
</div>
<!-- table header -->
<div class="container-fluid">
<div class="row ml-1 mr-1 separator">
<div class="col-1 text-right">LY</div>
<div class="col-3 text-center">Name</div>
<div class="col">Type</div>
<div class="col-2 text-center">Mapped Value</div>
</div>
</div>
<div class="container-fluid">
<div class="row separator align-items-center">
<div class="col"><hr class="seperator"></div>
</div>
</div>
</div>
</div>
<script type="module" src="/src/renderer.js"></script>
</body>
</body>
</html>

View file

@ -32,6 +32,12 @@ DETAILED SURFACE SCAN - MAPPED BODY
{ "timestamp":"2023-05-08T19:22:20Z", "event":"SAAScanComplete", "BodyName":"LHS 551 1", "SystemAddress":22660650050977, "BodyID":1, "ProbesUsed":8, "EfficiencyTarget":8 }
{ "timestamp":"2023-05-08T19:22:21Z", "event":"Scan", "ScanType":"Detailed", "BodyName":"LHS 551 1", "BodyID":1, "Parents":[ {"Star":0} ], "StarSystem":"LHS 551", "SystemAddress":22660650050977, "DistanceFromArrivalLS":578.392122, "TidalLock":false, "TerraformState":"", "PlanetClass":"Rocky ice body", "Atmosphere":"helium atmosphere", "AtmosphereType":"Helium", "AtmosphereComposition":[ { "Name":"Helium", "Percent":89.334976 }, { "Name":"Hydrogen", "Percent":8.427828 }, { "Name":"Neon", "Percent":2.237205 } ], "Volcanism":"major water geysers volcanism", "MassEM":4.158691, "Radius":10698825.000000, "SurfaceGravity":14.480845, "SurfaceTemperature":58.117901, "SurfacePressure":67337.343750, "Landable":false, "Composition":{ "Ice":0.403397, "Rock":0.397748, "Metal":0.198856 }, "SemiMajorAxis":173410028219.223022, "Eccentricity":0.000081, "OrbitalInclination":-0.013599, "Periapsis":290.186625, "OrbitalPeriod":78148038.387299, "AscendingNode":-3.343060, "MeanAnomaly":332.810795, "RotationPeriod":157634.536311, "AxialTilt":0.224570, "WasDiscovered":true, "WasMapped":true }
TERRAFORM STATES - TERRAFORMED
{ "timestamp":"2023-04-27T21:32:51Z", "event":"Scan", "ScanType":"NavBeaconDetail", "BodyName":"Reagan's Legacy", "BodyID":3, "Parents":[ {"Star":0} ], "StarSystem":"Delta Pavonis", "SystemAddress":633608344274, "DistanceFromArrivalLS":688.627081, "TidalLock":false, "TerraformState":"Terraformed", "PlanetClass":"Earthlike body", "Atmosphere":"thick atmosphere", "AtmosphereType":"EarthLike", "AtmosphereComposition":[ { "Name":"Nitrogen", "Percent":96.026627 }, { "Name":"Oxygen", "Percent":3.704388 }, { "Name":"Water", "Percent":0.253592 } ], "Volcanism":"", "MassEM":1.010000, "Radius":6151805.500000, "SurfaceGravity":10.637157, "SurfaceTemperature":318.681152, "SurfacePressure":683817.312500, "Landable":false, "Composition":{ "Ice":0.000000, "Rock":0.700000, "Metal":0.300000 }, "SemiMajorAxis":206445205211.639404, "Eccentricity":0.000000, "OrbitalInclination":2.800000, "Periapsis":197.927366, "OrbitalPeriod":17798399.925232, "AscendingNode":0.000000, "MeanAnomaly":259.923139, "RotationPeriod":133722.362175, "AxialTilt":0.470025, "WasDiscovered":false, "WasMapped":true }
TERRAFORM STATES - TERRAFORMABLE
{ "timestamp":"2023-04-15T20:02:16Z", "event":"Scan", "ScanType":"Detailed", "BodyName":"HIP 24065 3", "BodyID":16, "Parents":[ {"Null":10}, {"Star":0} ], "StarSystem":"HIP 24065", "SystemAddress":216635803987, "DistanceFromArrivalLS":873.284729, "TidalLock":false, "TerraformState":"Terraformable", "PlanetClass":"High metal content body", "Atmosphere":"hot thick carbon dioxide atmosphere", "AtmosphereType":"CarbonDioxide", "AtmosphereComposition":[ { "Name":"CarbonDioxide", "Percent":95.819931 }, { "Name":"Nitrogen", "Percent":3.221208 }, { "Name":"SulphurDioxide", "Percent":0.958199 } ], "Volcanism":"minor silicate vapour geysers volcanism", "MassEM":1.511658, "Radius":6875047.500000, "SurfaceGravity":12.747104, "SurfaceTemperature":806.785522, "SurfacePressure":24143846.000000, "Landable":false, "Composition":{ "Ice":0.000011, "Rock":0.663949, "Metal":0.335113 }, "SemiMajorAxis":20969787836.074829, "Eccentricity":0.097795, "OrbitalInclination":8.521953, "Periapsis":8.870370, "OrbitalPeriod":11576774.120331, "AscendingNode":160.796119, "MeanAnomaly":5.280380, "RotationPeriod":7718705.210996, "AxialTilt":-0.239401, "Rings":[ { "Name":"HIP 24065 3 A Ring", "RingClass":"eRingClass_Rocky", "MassMT":9.8768e+08, "InnerRad":1.1901e+07, "OuterRad":3.9954e+07 } ], "ReserveLevel":"PristineResources", "WasDiscovered":true, "WasMapped":true }
START HYPERSPACE JUMP
{ "timestamp":"2023-05-08T19:11:12Z", "event":"StartJump", "JumpType":"Hyperspace", "StarSystem":"LHS 6427", "SystemAddress":22660650116513, "StarClass":"M" }

View file

@ -18,6 +18,10 @@ body {
opacity: 0;
}
.hidden {
display: none;
}
.system {
margin-top: 1px;
margin-bottom: 1px;

617
src/assets/ldom.dev.js Normal file
View file

@ -0,0 +1,617 @@
(function() {
var LDOMCache = {
eventListenerCounter: 0,
eventListenerFunctions: {},
eventListenerFunctionsIds: {},
functionsUsed: {}
};
function $(input) {
if (typeof input === "string" && input[0] === "<" && input[input.length - 1] === ">") {
return new LDOMObject(document.createElement(input.substring(1, input.length - 1)));
} else if (input === null || !input) {
return new LDOMObject([]);
} else if (input._LDOM) {
return input;
} else if (input === window) {
return new LDOMWindowObject();
} else if (input.nodeType > 0) {
return new LDOMObject(input);
} else if (typeof input !== "string" && typeof input.length !== "undefined") {
var elements = [];
for (var i = 0; i < input.length; i++) {
var obj = $(input[i]);
if (obj.length > 0) {
elements.push(obj);
}
}
return new LDOMObject(elements);
} else {
return $(document).find(input);
}
}
window.LDOM = $;
window.$ = window.$ || $;
window.getLDOMFunctionUsage = function() {
var obj = $("<null>");
var keys = Object.keys(Object.getPrototypeOf(obj));
var unused = [];
for (var i = 0; i < keys.length; i++) {
if (keys[i][0] !== "_" && !LDOMCache.functionsUsed[keys[i]] && typeof obj[keys[i]] === "function") {
unused.push(keys[i]);
}
}
return {
used: Object.keys(LDOMCache.functionsUsed),
unused: unused
};
};
function LDOMObject(elem) {
this._LDOM = true;
if (Array.isArray(elem)) {
this.length = elem.length;
this._elements = elem;
} else {
this.length = 1;
this._node = elem;
}
}
LDOMObject.prototype.each = each;
LDOMObject.prototype.equals = equals;
LDOMObject.prototype.find = find;
LDOMObject.prototype.get = get;
LDOMObject.prototype.on = on;
LDOMObject.prototype.off = off;
LDOMObject.prototype.trigger = trigger;
LDOMObject.prototype.hide = hide;
LDOMObject.prototype.show = show;
LDOMObject.prototype.toggle = toggle;
LDOMObject.prototype.css = css;
LDOMObject.prototype.html = html;
LDOMObject.prototype.outerHTML = outerHTML;
LDOMObject.prototype.text = text;
LDOMObject.prototype.prop = prop;
LDOMObject.prototype.attr = attr;
LDOMObject.prototype.removeAttr = removeAttr;
LDOMObject.prototype.addClass = addClass;
LDOMObject.prototype.removeClass = removeClass;
LDOMObject.prototype.hasClass = hasClass;
LDOMObject.prototype.parent = parent;
LDOMObject.prototype.children = children;
LDOMObject.prototype.filter = filter;
LDOMObject.prototype.first = first;
LDOMObject.prototype.last = last;
LDOMObject.prototype.eq = eq;
LDOMObject.prototype.insertAfter = insertAfter;
LDOMObject.prototype.insertBefore = insertBefore;
LDOMObject.prototype.appendChild = appendChild;
LDOMObject.prototype.prependChild = prependChild;
LDOMObject.prototype.remove = remove;
function LDOMWindowObject() {
this._LDOM = true;
this.length = 1;
this._node = window;
}
LDOMWindowObject.prototype.each = each;
LDOMWindowObject.prototype.equals = equals;
LDOMWindowObject.prototype.get = get;
LDOMWindowObject.prototype.on = on;
LDOMWindowObject.prototype.off = off;
LDOMWindowObject.prototype.trigger = trigger;
LDOMWindowObject.prototype.prop = prop;
LDOMWindowObject.prototype.attr = attr;
LDOMWindowObject.prototype.removeAttr = removeAttr;
function each(funct, reverse) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
var elementsArray = getElementsArray(this);
var start = reverse ? elementsArray.length - 1 : 0;
var change = reverse ? -1 : 1;
var end = (reverse ? 0 : elementsArray.length - 1) + change;
for (var i = start; i !== end; i += change) {
var shouldContinue = funct.apply(elementsArray[i], [i]);
if (shouldContinue === false) {
break;
}
}
return this;
}
function equals(ldomObject) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
if (this.length !== ldomObject.length) {
return false;
}
var thisElementsArray = getElementsArray(this);
var otherElementsArray = getElementsArray(ldomObject);
if (thisElementsArray.length !== otherElementsArray.length) {
return false;
}
var otherNodes = [];
for (var i = 0; i < otherElementsArray.length; i++) {
otherNodes.push(otherElementsArray[i]._node);
}
for (var i = 0; i < thisElementsArray.length; i++) {
if (otherNodes.indexOf(thisElementsArray[i]._node) === -1) {
return false;
}
}
return true;
}
function find(selector) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
var output = [];
this.each(function() {
var elems = this._node.querySelectorAll(selector);
for (var i = 0; i < elems.length; i++) {
output.push(elems[i]);
}
});
return $(output);
}
function get(index) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
if (!isDefined(index)) {
var nodes = [];
this.each(function() {
nodes.push(this._node);
});
return nodes;
} else {
var elementsArray = getElementsArray(this);
if (index < 0) {
index = elementsArray.length + index;
}
if (!isDefined(elementsArray[index])) {
return null;
}
return elementsArray[index]._node;
}
}
function on(eventName, handler) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
var eventId = ++LDOMCache.eventListenerCounter;
var handlerWrapper = function(evt) {
handler.apply($(this), [evt]);
};
this.each(function() {
this._node.addEventListener(eventName, handlerWrapper);
var eventIds = this._node._LDOMEvents || [];
eventIds.push(eventId);
this._node._LDOMEvents = eventIds;
});
if (!LDOMCache.eventListenerFunctions[eventName]) {
LDOMCache.eventListenerFunctions[eventName] = {};
}
LDOMCache.eventListenerFunctions[eventName][eventId] = {
funct: handlerWrapper,
count: this.length
};
LDOMCache.eventListenerFunctionsIds[eventId] = {
name: eventName
};
return eventId;
}
function off(eventName) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
if (!isDefined(eventName)) {
this.each(function() {
var eventIds = this._node._LDOMEvents || [];
for (var i = eventIds.length - 1; i >= 0; i--) {
this.off(eventIds[i]);
}
});
} else if (typeof eventName === "string") {
this.each(function() {
if (!LDOMCache.eventListenerFunctions[eventName]) {
return;
}
var eventIds = this._node._LDOMEvents || [];
for (var i = eventIds.length - 1; i >= 0; i--) {
if (LDOMCache.eventListenerFunctionsIds[eventIds[i]].name === eventName) {
this.off(eventIds[i]);
}
}
});
} else if (typeof eventName === "number") {
var eventId = eventName;
this.each(function() {
if (!LDOMCache.eventListenerFunctionsIds[eventId]) {
return;
}
var eventName = LDOMCache.eventListenerFunctionsIds[eventId].name;
if (!LDOMCache.eventListenerFunctions[eventName][eventId]) {
return;
}
var event = LDOMCache.eventListenerFunctions[eventName][eventId];
this._node.removeEventListener(eventName, event.funct);
var eventIds = this._node._LDOMEvents || [];
eventIds.splice(eventIds.indexOf(eventId), 1);
if (eventIds.length === 0) {
delete this._node._LDOMEvents;
} else {
this._node._LDOMEvents = eventIds;
}
if (--event.count === 0) {
delete LDOMCache.eventListenerFunctions[eventName][eventId];
if (Object.keys(LDOMCache.eventListenerFunctions[eventName]).length === 0) {
delete LDOMCache.eventListenerFunctions[eventName];
}
delete LDOMCache.eventListenerFunctionsIds[eventId];
}
});
}
}
function trigger(eventName, data) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
var event = document.createEvent("Event");
event.initEvent(eventName, true, true);
for (var key in (data || {})) {
event[key] = data[key];
}
this.each(function() {
var that = this;
setTimeout(function() {
that._node.dispatchEvent(event);
}, 0);
});
return this;
}
function hide() {
LDOMCache.functionsUsed[arguments.callee.name] = true;
this.each(function() {
if (this._node.style.display === "none") {
return;
}
var isImportant = false;
if (this._node.style.display !== "") {
this._node.setAttribute("data-LDOM-hidden-previous-display", this._node.style.display);
if (this._node.style.getPropertyPriority("display") === "important") {
this._node.setAttribute("data-LDOM-hidden-previous-display-important", "true");
isImportant = true;
}
}
this._node.style.setProperty("display", "none", isImportant ? "important" : "");
});
return this;
}
function show() {
LDOMCache.functionsUsed[arguments.callee.name] = true;
this.each(function() {
if (this._node.hasAttribute("data-LDOM-hidden-previous-display")) {
this._node.style.setProperty("display", this._node.getAttribute("data-LDOM-hidden-previous-display"), this._node.hasAttribute("data-LDOM-hidden-previous-display-important") ? "important" : "");
this._node.removeAttribute("data-LDOM-hidden-previous-display");
this._node.removeAttribute("data-LDOM-hidden-previous-display-important");
} else if (this._node.style.display === "none") {
this._node.style.display = "";
}
});
return this;
}
function toggle(show) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
this.each(function() {
var shouldShow = this._node.hasAttribute("data-LDOM-hidden-previous-display") || this._node.style.display === "none";
if (isDefined(show)) {
shouldShow = show;
}
if (shouldShow) {
this.show()
} else {
this.hide();
}
});
return this;
}
function css(property, value, isImportant) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
if (!isDefined(value)) {
var elementsArray = getElementsArray(this);
return elementsArray.length > 0 ? window.getComputedStyle(elementsArray[0]._node)[property] : "";
} else {
this.each(function() {
this._node.style.setProperty(property, value, isImportant ? "important" : "");
});
return this;
}
}
function html(htmlString) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
if (!isDefined(htmlString)) {
var elementsArray = getElementsArray(this);
return elementsArray.length > 0 ? elementsArray[0]._node.innerHTML : "";
} else {
return setPropertyAndRemoveDetachedNodes(this, "innerHTML", htmlString);
}
}
function outerHTML(htmlString) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
if (!isDefined(htmlString)) {
var elementsArray = getElementsArray(this);
return elementsArray.length > 0 ? elementsArray[0]._node.outerHTML : "";
} else {
return setPropertyAndRemoveDetachedNodes(this, "outerHTML", htmlString);
}
}
function text(textString) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
if (!isDefined(textString)) {
var elementsArray = getElementsArray(this);
return elementsArray.length > 0 ? elementsArray[0]._node.innerText : "";
} else {
return setPropertyAndRemoveDetachedNodes(this, "innerText", textString);
}
}
function prop(propertyName, value) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
if (!isDefined(value)) {
var elementsArray = getElementsArray(this);
return elementsArray.length > 0 ? elementsArray[0]._node[propertyName] : "";
} else {
this.each(function() {
this._node[propertyName] = value;
});
return this;
}
}
function attr(attributeName, value) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
if (!isDefined(value)) {
var elementsArray = getElementsArray(this);
return elementsArray.length > 0 ? elementsArray[0]._node.getAttribute(attributeName) : "";
} else {
this.each(function() {
this._node.setAttribute(attributeName, value);
});
return this;
}
}
function removeAttr(attributeName) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
this.each(function() {
this._node.removeAttribute(attributeName);
});
return this;
}
function addClass(className) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
this.each(function() {
var classes = (this._node.getAttribute("class") || "").split(" ");
var newClasses = className.split(" ");
for (var i = 0; i < newClasses.length; i++) {
if (classes.indexOf(newClasses[i]) === -1) {
classes.push(newClasses[i]);
}
}
this._node.setAttribute("class", classes.join(" ").trim());
});
return this;
}
function removeClass(className) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
if (!isDefined(className)) {
this.each(function() {
this._node.removeAttribute("class");
});
} else {
this.each(function() {
var classes = (this._node.getAttribute("class") || "").split(" ");
var newClasses = className.split(" ");
for (var i = 0; i < newClasses.length; i++) {
var classIndex = classes.indexOf(newClasses[i]);
if (classIndex !== -1) {
classes.splice(classIndex, 1);
}
}
this._node.setAttribute("class", classes.join(" ").trim());
});
}
return this;
}
function hasClass(className, all) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
var doesHaveClass = false;
this.each(function() {
var classes = (this._node.getAttribute("class") || "").split(" ");
if (classes.indexOf(className) !== -1) {
doesHaveClass = true;
if (!all) {
return false;
}
} else {
doesHaveClass = false;
if (all) {
return false;
}
}
});
return doesHaveClass;
}
function parent() {
LDOMCache.functionsUsed[arguments.callee.name] = true;
var output = [];
this.each(function() {
if (output.indexOf(this._node.parentNode) === -1) {
output.push(this._node.parentNode);
}
});
return $(output);
}
function children() {
LDOMCache.functionsUsed[arguments.callee.name] = true;
var output = [];
this.each(function() {
var elems = this._node.children;
for (var i = 0; i < elems.length; i++) {
output.push(elems[i]);
}
});
return $(output);
}
function filter(selector) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
if (!selector) {
return $([]);
}
var matchesMethod = "matches";
if (Element.prototype.matches) {
matchesMethod = "matches";
} else if (Element.prototype.matchesSelector) {
matchesMethod = "matchesSelector";
} else if (Element.prototype.msMatchesSelector) {
matchesMethod = "msMatchesSelector";
} else if (Element.prototype.webkitMatchesSelector) {
matchesMethod = "webkitMatchesSelector";
}
var output = [];
this.each(function() {
if (this._node[matchesMethod](selector)) {
output.push(this);
}
});
return $(output);
}
function first() {
LDOMCache.functionsUsed[arguments.callee.name] = true;
var elementsArray = getElementsArray(this);
if (elementsArray.length === 0) {
return $([]);
}
return elementsArray[0];
}
function last() {
LDOMCache.functionsUsed[arguments.callee.name] = true;
var elementsArray = getElementsArray(this);
if (elementsArray.length === 0) {
return $([]);
}
return elementsArray[elementsArray.length - 1];
}
function eq(index) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
var elementsArray = getElementsArray(this);
if (index < 0) {
index = elementsArray.length + index;
}
if (!isDefined(elementsArray[index])) {
return $([]);
}
return elementsArray[index];
}
function insertAfter(ldomObjectTarget) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
this.each(function() {
var callingNode = this._node;
ldomObjectTarget.each(function() {
this._node.parentNode.insertBefore(callingNode.cloneNode(true), this._node.nextSibling);
});
}, true);
return this;
}
function insertBefore(ldomObjectTarget) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
this.each(function() {
var callingNode = this._node;
ldomObjectTarget.each(function() {
this._node.parentNode.insertBefore(callingNode.cloneNode(true), this._node);
});
});
return this;
}
function appendChild(childElement) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
this.each(function() {
var callingNode = this._node;
childElement.each(function() {
callingNode.appendChild(this._node.cloneNode(true));
});
});
return this;
}
function prependChild(childElement) {
LDOMCache.functionsUsed[arguments.callee.name] = true;
this.each(function() {
var callingNode = this._node;
childElement.each(function() {
callingNode.insertBefore(this._node.cloneNode(true), callingNode.firstChild);
}, true);
});
return this;
}
function remove() {
LDOMCache.functionsUsed[arguments.callee.name] = true;
this.each(function() {
if (isDefined(this.off)) {
this.off();
}
this._node.parentNode.removeChild(this._node);
});
}
// Internal Helper Functions
function setPropertyAndRemoveDetachedNodes(ldomObject, property, value) {
ldomObject.each(function() {
this.inPage = document.body.contains(this._node);
});
ldomObject.each(function() {
this._node[property] = value;
});
var output = [];
ldomObject.each(function() {
if (!this.inPage || document.body.contains(this._node)) {
output.push(this);
}
delete this.inPage;
});
return $(output);
}
function getElementsArray(obj) {
if (obj._elements) {
return obj._elements;
} else {
return [obj];
}
}
function isDefined(obj) {
if (typeof obj === "undefined") {
return false;
}
return true;
}
})();

View file

@ -0,0 +1,3 @@
[ZoneTransfer]
ZoneId=3
HostUrl=about:internet

View file

@ -1,23 +0,0 @@
export class Body {
constructor() {}
isAsteroid() {
return this.BodyName.includes('Belt')
}
isPlanet() {
return !!this.PlanetClass
}
isStar() {
return !!this.StarType
}
simpleName() {
return this.BodyName.replace(this.StarSystem, '')
}
distance() {
return Intl.NumberFormat().format(Math.round(this.DistanceFromArrivalLS))
}
}

View file

@ -1,5 +1,5 @@
import { Body } from './Body'
import { System } from './System'
import { Body } from '../models/Body'
import { System } from '../models/System'
const EventEmitter = require('events')
const fs = require('fs')
@ -11,6 +11,7 @@ const lineReader = require('reverse-line-reader')
const chokidar = require('chokidar')
const Tail = require('tail').Tail
const find = require('lodash/find')
const findIndex = require('lodash/findIndex')
// Set log() to console.log() so whenever I get around to setting up a log file, I don't have to
// search and replace all the console.log()'s.
@ -36,21 +37,14 @@ export class JournalInterface extends EventEmitter {
this.currentJournal = this.getLatestJournal()
// LineReader seems to be async, so start async processes here.
this.currentLocation = null
this.location = null
log('JournalInterface initialized. Attempting to find current location.')
this.getCurrentLocation()
.then(() => {
log('Attempting to find scanned bodies in current system.')
this.getScannedBodies()
.then(() => {
log('Scanned bodies found.')
this.emit('SCANNED_BODIES_FOUND')
})
})
}
/* -------------------------------------------------------------------- getLatestJournal ---- */
// https://stackoverflow.com/questions/15696218/get-the-most-recent-file-in-a-directory-node-js
getLatestJournal() {
const journals = globSync(this.journalPattern)
@ -63,31 +57,38 @@ export class JournalInterface extends EventEmitter {
log(`New journal file found, now watching ${path.basename(this.currentJournal)}.`)
}
/* ------------------------------------------------------------------ getCurrentLocation ---- */
// Get current location on setup, so if app is restarted, user can pick up where they left off
// Rather than waiting til they jump to the next system to use the program again.
async getCurrentLocation() {
return lineReader.eachLine(this.currentJournal, (raw, last) => {
getCurrentLocation() {
lineReader.eachLine(this.currentJournal, (raw, last) => {
if (raw) { // skip blank line at end of file
const line = JSON.parse(raw)
if (line.event === 'FSDJump') {
this.currentLocation = new System(line.StarSystem)
log(`Current location set to ${this.currentLocation.name}.`)
this.emit('FSDJump')
this.location = new System(line.StarSystem)
log(`Current location set to ${this.location.name}.`)
this.emit('ENTERED_NEW_SYSTEM')
return false
} else if (last) {
log('Warning: unable to find last hyperspace jump. Current location unknown.')
return false
}
}
}).then(() => {
log('Attempting to find scanned bodies in current system.')
this.getScannedBodies()
})
}
/* -------------------------------------------------------------------- getScannedBodies ---- */
// Look for all scanned bodies before last FSDJump, for same reasons as getCurrentLocation().
async getScannedBodies() {
getScannedBodies() {
let detailedScanLine = null
return lineReader.eachLine(this.currentJournal, (raw, last) => {
lineReader.eachLine(this.currentJournal, (raw, last) => {
if (raw) { // Skip blank line at end of file.
const line = JSON.parse(raw)
@ -97,15 +98,14 @@ export class JournalInterface extends EventEmitter {
if (line.event === 'SAAScanComplete') {
// This was a DSS, so set the DSS flag to true and add to list.
detailedScanLine.DSSDone = true
this.currentLocation.bodies.push(Object.assign(new Body, detailedScanLine))
this.location.bodies.push(Object.assign(new Body, detailedScanLine))
} else {
// Else, check that the body hasn't already been added (by a DSS scan line).
let r = find(this.currentLocation.bodies, ['BodyID', detailedScanLine.BodyID])
let r = find(this.location.bodies, {'BodyName': detailedScanLine.BodyName, 'BodyID': detailedScanLine.BodyID})
if (r === undefined) {
// Set DSS flag if body was not already logged, then add to list.
detailedScanLine.DSSDone = false
this.currentLocation.bodies.push(Object.assign(new Body, detailedScanLine))
// Body was not already logged, so add to list.
this.location.bodies.push(Object.assign(new Body, detailedScanLine))
}
}
@ -122,21 +122,20 @@ export class JournalInterface extends EventEmitter {
detailedScanLine = line
} else if (line.StarType !== undefined) { // Save stars to bodies list.
this.currentLocation.bodies.push(Object.assign(new Body, line))
this.location.bodies.push(Object.assign(new Body, line))
} else if (line.ScanType === 'AutoScan') { // Save auto/discovery scan bodies.
// Check if planet, and then do the duplicate check (otherwise it's an
// astroid, as we've already accounted for stars).
if (line.PlanetClass !== undefined) {
let r = find(this.currentLocation.bodies, ['BodyID', line.BodyID])
let r = find(this.location.bodies, ['BodyID', line.BodyID])
if (r === undefined) {
line.DSSDone = false
this.currentLocation.bodies.push(Object.assign(new Body, line))
this.location.bodies.push(Object.assign(new Body, line))
}
} else { // Asteroids.
this.currentLocation.bodies.push(Object.assign(new Body, line))
this.location.bodies.push(Object.assign(new Body, line))
}
}
} else if (line.event === 'FSDJump') {
@ -144,9 +143,14 @@ export class JournalInterface extends EventEmitter {
return false
}
}
}).then(() => {
log('Scanned bodies found.')
this.emit('INIT_COMPLETE')
})
}
/* ---------------------------------------------------------------------- watchDirectory ---- */
// Set up journal directory watcher to catch new journal files as the game seems to sometimes
// make more than one journal per day.
// Also for instances where UTC day switches over mid-play session.
@ -158,23 +162,80 @@ export class JournalInterface extends EventEmitter {
log('Watching journal folder for changes...')
}
/* ----------------------------------------------------------------------- parseScanLine ---- */
// Parse and handle scan lines.
parseScanLine(line, DSS = false) {
const dupChecker = {'BodyName': line.BodyName, 'BodyID': line.bodyID}
// If it's a DSS scan, then we should have already added the body to the list. But we'll
// check to make sure.
if (DSS) {
// Using findIndex() rather than find() so we can edit the body if found
let body = findIndex(this.location.bodies, dupChecker)
if (body > -1) { // Body was found in list, so simply toggle the DSS flag.
this.location.bodies[body].DSSDone = true
} else { // Body was missed on initial journal scan, so add it to the list.
line.DSSDone = true
this.location.bodies.push(Object.assign(new Body, line))
}
} else { // Otherwise it's an FSS or auto scan, and needs to be added to the list.
// Probably overkill, but do a duplicate check just in case.
let r = find(this.location.bodies, dupChecker)
if (r === undefined) {
this.location.bodies.push(Object.assign(new Body, line))
}
}
log(`Scan detected. Body: ${line.BodyName}.`)
log(this.location.bodies)
}
/* -----------------------------------------------------------------------0000 parseLine ---- */
// Parse and handle journal lines.
parseLine(raw) {
const line = JSON.parse(raw)
if (line.event === 'FSDJump') {
this.currentLocation = new System(line.StarSystem)
this.emit('FSDJump')
let DSSFlag = false
switch (line.event) {
// CMDR jumped to new system, so update current location.
case 'FSDJump': {
this.location = new System(line.StarSystem)
this.emit('ENTERED_NEW_SYSTEM')
break
}
// CMDR completed DSS scan, so set flag for when next line processes and we want to
// figure out what kind of scan occurred.
case 'SAAScanComplete': {
DSSFlag = true
break
}
// A scan occurred, so let's hand that info off to the appropriate function and then
// reset the DSS flag.
case 'Scan': {
this.parseScanLine(line, DSSFlag)
this.emit('BODY_SCANNED')
DSSFlag = false
break
}
}
}
/* ------------------------------------------------------------------------ watchJournal ---- */
// Watch the journal for changes.
watchJournal() {
const tail = new Tail(this.currentJournal, {useWatchFile: true})
log(`Watching ${path.basename(this.currentJournal)}...`)
tail.on('line', data => this.parseLine(data))
tail.on('error', err => console.log(err))
tail.on('line', data => data ? this.parseLine(data) : undefined)
tail.on('error', err => log(`Tail error in JournalInterface.watchJournal(): ${err}`))
}
}

79
src/models/Body.js Normal file
View file

@ -0,0 +1,79 @@
export class Body {
constructor() {
this.DSSDone = false
}
/* -------------------------------------------------------------------------- isAsteroid ---- */
isAsteroid() {
return this.BodyName.includes('Belt')
}
/* ---------------------------------------------------------------------------- isPlanet ---- */
isPlanet() {
return !!this.PlanetClass
}
/* ------------------------------------------------------------------------------ isStar ---- */
isStar() {
return !!this.StarType
}
/* ---------------------------------------------------------------------------- nameIcon ---- */
nameIcon() {
let nameIcon = null
if (this.isAsteroid()) {
nameIcon = 'asteroid-4'
} else if (this.isStar()) {
nameIcon = 'star'
} else if (this.isPlanet()) {
nameIcon = 'jupiter-3'
}
return nameIcon
}
/* -------------------------------------------------------------------------- simpleName ---- */
simpleName() {
return this.BodyName.replace(this.StarSystem, '')
}
/* ---------------------------------------------------------------------------- typeIcon ---- */
typeIcon() {
let typeIcon = null
if (this.isStar() || this.isAsteroid()) {
typeIcon = this.nameIcon()
} else {
const planetClass = this.PlanetClass.toLowerCase()
if (planetClass.includes('metal')) {
typeIcon = 'ingot'
} else if (planetClass.includes('icy')) {
typeIcon = 'snowflake'
} else if (planetClass.includes('earth')) {
typeIcon = 'earth'
} else if (planetClass.includes('gas giant')) {
typeIcon = 'jupiter-1'
} else if (planetClass.includes('rock')) {
typeIcon = 'asteroid-3'
} else if (planetClass.includes('water') || planetClass.includes('ammonia')) {
typeIcon = 'water-drops'
}
}
return typeIcon
}
/* ---------------------------------------------------------------------------- distance ---- */
distance() {
return Intl.NumberFormat().format(Math.round(this.DistanceFromArrivalLS))
}
}

View file

@ -26,12 +26,13 @@
* ```
*/
import './index.css'
import './assets/index.css'
import './icons/flaticon.css'
const { app } = require('electron')
import { createApp, ref } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
import { JournalInterface } from './interfaces/JournalInterface'
import { createBodyRow } from './ui'
// Grab app.isPackaged from main process
let isPackaged = false
@ -41,28 +42,32 @@ window.process.argv.forEach((item) => {
}
})
createApp({
setup() {
const journal = new JournalInterface(isPackaged)
/* ------------------------------------------------------------------------------- app setup ---- */
// TODO: show warning to user
if (journal.journalDir === null) {
return
}
const journal = new JournalInterface(isPackaged)
journal.watchDirectory()
journal.watchJournal()
if (journal.journalDir === null) {
// handle error
}
const currentLocation = ref('Unknown')
const currentLocationBodies = ref([])
journal.watchDirectory()
journal.watchJournal()
journal.on('FSDJump', () => currentLocation.value = journal.currentLocation)
journal.on('SCANNED_BODIES_FOUND', () => currentLocationBodies.value = journal.currentLocation.bodies)
journal.once('INIT_COMPLETE', () => {
if (journal.location !== null) {
$('#currentSystem')
.addClass('charted')
.removeClass('highlighted text-center')
$('#currentSystemName').text(journal.location.name)
return {
currentLocation,
currentLocationBodies,
}
$('#currentSystemIcon').removeClass('hidden')
}
}).mount('#app')
if (journal.location.bodies.length > 0) {
journal.location.bodies.forEach((body) => {
const row = createBodyRow(body)
$('#lowValueScans').appendChild(row)
})
}
})

95
src/ui.js Normal file
View file

@ -0,0 +1,95 @@
/* ------------------------------------------------------------------------------ buildRings ---- */
const buildRings = (body) => {
const rings = $('<span>')
const seperator = $('<span>').text(' ) ')
body.Rings.forEach((ring) => {
const ringClass = ring.RingClass.replace('eRingClass_', '')
let icon = null
switch (ringClass) {
case 'MetalRich': {
icon = 'gold-bars'
break
}
case 'Metalic':
case 'Metallic': {
icon = 'ingot'
break
}
case 'Icy': {
icon = 'snowflake'
break
}
case 'Rocky': {
icon = 'asteroid-3'
break
}
}
if (icon !== null) {
rings.appendChild($('<i>').addClass(`flaticon-${icon}`))
rings.appendChild(seperator)
}
})
return rings
}
/* --------------------------------------------------------------------------- createBodyRow ---- */
export const createBodyRow = (body) => {
const row = $('<div>').addClass('row ml-1 mr-1')
row.attr('id', body.bodyID)
// spacer
row.appendChild($('<div>').addClass('col-1 system'))
// name
const name = $('<div>').addClass('col-2 system charted text-left')
name.appendChild($('<i>')).addClass(`flaticon-${body.nameIcon()}`)
name.appendChild($('<span>')).text(body.simpleName())
row.appendChild(name)
// type icon
const type = $('<div>').addClass('col pr-0 mr-0 system charted')
type.appendChild($('<i>').addClass(`flaticon-${body.typeIcon()}`))
// rings
if (body.Rings !== undefined) {
type.appendChild(buildRings(body))
}
// type
const typeName = body.PlanetClass || body.starType || ''
type.appendChild($('<span>').text(typeName))
row.appendChild(type)
// distance
const distance = $('<div>').addClass('col-auto pl-2 ml-0 system charted').text(body.distance())
row.appendChild(distance)
// info
const info = $('<div>').addClass('col-1 system charted')
// terraformable
const terraform = $('<i>').addClass('flaticon-cooling-tower opacity-0')
if (body.isPlanet && body.TerraformState) {
terraform.removeClass('opacity-0')
}
info.appendChild(terraform)
// was mapped
const mapped = $('<i>').addClass('flaticon-flag-outline-on-a-pole-with-stars-around opacity-0')
if (body.isPlanet() && !body.WasMapped) {
mapped.removeClass('opacity-0')
}
info.appendChild(mapped)
row.appendChild(info)
// mapped value
const value = $('<div>').addClass('col-2 system charted text-right')
row.appendChild(value)
return row
}