diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..b58b603 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/ed-safari.iml b/.idea/ed-safari.iml new file mode 100644 index 0000000..24643cc --- /dev/null +++ b/.idea/ed-safari.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..e1f43e4 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/index.html b/index.html index 698da49..d9d0168 100644 --- a/index.html +++ b/index.html @@ -1,145 +1,84 @@ - + ED Safari v0.0.1 - - - + + +
- -
-
-

-
Current Location
-

-
-
- - -
-
-
Name
-
Type
-
Distance
-
Details
-
Mapped Value
-
-
- - -
-
-
- - {{ currentLocation.name }} +
+ +
+
+

+
Current Location
+

-
- -
-
-
- - -
-
-
- -
- - {{ body.simpleName() }} + +
+
+
Name
+
Type
+
Distance
+
Details
+
Mapped Value
- -
- - - - - - {{ body.PlanetClass || body.StarType || '' }} -
- -
- {{ body.distance() }} -
- -
- - - -
- -
-
- -
-
-

-
Nav Route
-

+ +
+
+
+ + Unknown +
+
-
- -
-
-
LY
-
Name
-
Type
-
Mapped Value
+ +
+
+
+
+
-
-
-
-

+ +
+
+ + +
+
+

+
Nav Route
+

+
+
+ + +
+
+
LY
+
Name
+
Type
+
Mapped Value
+
+
+ +
+
+

+
- + diff --git a/journal examples.txt b/journal examples.txt index 7235a9e..333b050 100644 --- a/journal examples.txt +++ b/journal examples.txt @@ -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" } diff --git a/src/Eurostile-Roman.woff b/src/assets/Eurostile-Roman.woff similarity index 100% rename from src/Eurostile-Roman.woff rename to src/assets/Eurostile-Roman.woff diff --git a/src/Eurostile-Roman.woff2 b/src/assets/Eurostile-Roman.woff2 similarity index 100% rename from src/Eurostile-Roman.woff2 rename to src/assets/Eurostile-Roman.woff2 diff --git a/src/index.css b/src/assets/index.css similarity index 98% rename from src/index.css rename to src/assets/index.css index de5bb08..d004c48 100644 --- a/src/index.css +++ b/src/assets/index.css @@ -18,6 +18,10 @@ body { opacity: 0; } +.hidden { + display: none; +} + .system { margin-top: 1px; margin-bottom: 1px; diff --git a/src/assets/ldom.dev.js b/src/assets/ldom.dev.js new file mode 100644 index 0000000..9ea97d2 --- /dev/null +++ b/src/assets/ldom.dev.js @@ -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 = $(""); + 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; + } +})(); diff --git a/src/assets/ldom.dev.js:Zone.Identifier b/src/assets/ldom.dev.js:Zone.Identifier new file mode 100644 index 0000000..053d112 --- /dev/null +++ b/src/assets/ldom.dev.js:Zone.Identifier @@ -0,0 +1,3 @@ +[ZoneTransfer] +ZoneId=3 +HostUrl=about:internet diff --git a/src/interfaces/Body.js b/src/interfaces/Body.js deleted file mode 100644 index 49f6f8e..0000000 --- a/src/interfaces/Body.js +++ /dev/null @@ -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)) - } -} \ No newline at end of file diff --git a/src/interfaces/JournalInterface.js b/src/interfaces/JournalInterface.js index 55008ae..927bef6 100644 --- a/src/interfaces/JournalInterface.js +++ b/src/interfaces/JournalInterface.js @@ -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}`)) } } \ No newline at end of file diff --git a/src/models/Body.js b/src/models/Body.js new file mode 100644 index 0000000..4a423a0 --- /dev/null +++ b/src/models/Body.js @@ -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)) + } +} \ No newline at end of file diff --git a/src/interfaces/System.js b/src/models/System.js similarity index 100% rename from src/interfaces/System.js rename to src/models/System.js diff --git a/src/renderer.js b/src/renderer.js index 713d6fb..d5fb5b5 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -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) + }) + } +}) \ No newline at end of file diff --git a/src/ui.js b/src/ui.js new file mode 100644 index 0000000..2e1344e --- /dev/null +++ b/src/ui.js @@ -0,0 +1,95 @@ +/* ------------------------------------------------------------------------------ buildRings ---- */ + +const buildRings = (body) => { + const rings = $('') + const seperator = $('').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($('').addClass(`flaticon-${icon}`)) + rings.appendChild(seperator) + } + }) + + return rings +} + +/* --------------------------------------------------------------------------- createBodyRow ---- */ + +export const createBodyRow = (body) => { + const row = $('
').addClass('row ml-1 mr-1') + row.attr('id', body.bodyID) + + // spacer + row.appendChild($('
').addClass('col-1 system')) + + // name + const name = $('
').addClass('col-2 system charted text-left') + name.appendChild($('')).addClass(`flaticon-${body.nameIcon()}`) + name.appendChild($('')).text(body.simpleName()) + row.appendChild(name) + + // type icon + const type = $('
').addClass('col pr-0 mr-0 system charted') + type.appendChild($('').addClass(`flaticon-${body.typeIcon()}`)) + // rings + if (body.Rings !== undefined) { + type.appendChild(buildRings(body)) + } + // type + const typeName = body.PlanetClass || body.starType || '' + type.appendChild($('').text(typeName)) + row.appendChild(type) + + // distance + const distance = $('
').addClass('col-auto pl-2 ml-0 system charted').text(body.distance()) + row.appendChild(distance) + + // info + const info = $('
').addClass('col-1 system charted') + // terraformable + const terraform = $('').addClass('flaticon-cooling-tower opacity-0') + if (body.isPlanet && body.TerraformState) { + terraform.removeClass('opacity-0') + } + info.appendChild(terraform) + // was mapped + const mapped = $('').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 = $('
').addClass('col-2 system charted text-right') + row.appendChild(value) + + return row +} \ No newline at end of file