diff --git a/saltgui/index.html b/saltgui/index.html
index c455b85af..1d0e27cff 100644
--- a/saltgui/index.html
+++ b/saltgui/index.html
@@ -18,6 +18,7 @@
+
diff --git a/saltgui/static/scripts/output/Output.js b/saltgui/static/scripts/output/Output.js
index f13b2a4fb..63dc4bd8d 100644
--- a/saltgui/static/scripts/output/Output.js
+++ b/saltgui/static/scripts/output/Output.js
@@ -8,6 +8,12 @@ import {OutputNested} from "./OutputNested.js";
import {OutputYaml} from "./OutputYaml.js";
import {ParseCommandLine} from "../ParseCommandLine.js";
import {Utils} from "../Utils.js";
+import hljs from "../../highlight/es/core.js";
+import json from "../../highlight/es/languages/json.js";
+import yaml from "../../highlight/es/languages/yaml.js";
+
+hljs.registerLanguage("json", json);
+hljs.registerLanguage("yaml", yaml);
// Functions to turn responses from the salt system into visual information
// The following variations exist:
@@ -108,6 +114,36 @@ export class Output {
return OutputJson.formatJSON(pObject);
}
+ static setHighlightObject (pParent, pObject, pStyleWhiteSpace = "pre-wrap", pLanguage = null) {
+ const code = Utils.createElem("code");
+ code.style.whiteSpace = pStyleWhiteSpace;
+ // light-yellow, but lighter
+ pParent.style.backgroundColor = "#FFFFF8";
+
+ if (pLanguage !== null) {
+ const outputFormats = Utils.getStorageItem("session", "output_formats");
+ Utils.setStorageItem("session", "output_formats", pLanguage);
+ code.innerHTML = hljs.highlight(Output.formatObject(pObject), {language:pLanguage}).value;
+ Utils.setStorageItem("session", "output_formats", outputFormats);
+ } else if (Output.isOutputFormatAllowed("json")) {
+ code.innerHTML = hljs.highlight(Output.formatObject(pObject), {language:'json'}).value;
+ } else if (Output.isOutputFormatAllowed("nested")) {
+ // yes, yaml
+ code.innerHTML = hljs.highlight(Output.formatObject(pObject), {language:'yaml'}).value;
+ } else if (Output.isOutputFormatAllowed("yaml")) {
+ code.innerHTML = hljs.highlight(Output.formatObject(pObject), {language:'yaml'}).value;
+ } else {
+ code.innerText = Output.formatObject(pObject);
+ }
+
+ if (pParent.firstElementChild) {
+ pParent.replaceChild(code, pParent.firstElementChild);
+ } else {
+ // empty or only a text-node
+ pParent.innerText = "";
+ pParent.appendChild(code);
+ }
+ }
// this is the default output form
// just format the returned objects
diff --git a/saltgui/static/scripts/panels/BeaconsMinion.js b/saltgui/static/scripts/panels/BeaconsMinion.js
index 68c9096d0..b9cea1cd7 100644
--- a/saltgui/static/scripts/panels/BeaconsMinion.js
+++ b/saltgui/static/scripts/panels/BeaconsMinion.js
@@ -160,31 +160,44 @@ export class BeaconsMinionPanel extends Panel {
this._addMenuItemBeaconsEnableBeaconWhenNeeded(beaconMenu, pMinionId, beaconName, beacon);
this._addMenuItemBeaconsDelete(beaconMenu, pMinionId, beaconName);
- // menu comes before this data on purpose
- const beaconConfig = Output.formatObject(beacon);
- const beaconConfigTd = Utils.createTd("beacon-config", beaconConfig);
- let initialTimestamp = "(waiting)";
- let initialValue = "(waiting)";
+ const beaconConfigTd = Utils.createTd();
+ Output.setHighlightObject(beaconConfigTd, beacon, "pre");
+ tr.appendChild(beaconConfigTd);
+
+ let initialTimestamp;
+ let initialValue;
+ let initialClass;
if (beacon.enabled === false) {
- beaconConfigTd.classList.add("beacon-disabled");
+ initialClass = "beacon-disabled";
initialTimestamp = Character.EM_DASH;
initialValue = "(beacon" + Character.NO_BREAK_SPACE + "disabled)";
} else if (beacons.enabled === false) {
- beaconConfigTd.classList.add("beacon-disabled");
+ initialClass = "beacon-disabled";
initialTimestamp = Character.EM_DASH;
initialValue = "(beacons" + Character.NO_BREAK_SPACE + "disabled)";
+ } else {
+ initialClass = "beacon-waiting";
+ initialTimestamp = "(waiting)";
+ initialValue = "(waiting)";
}
- tr.appendChild(beaconConfigTd);
const beaconTimestampTd = Utils.createTd();
- const beaconTimestampSpan = Utils.createSpan(["beacon-timestamp", "beacon-waiting"], initialTimestamp);
+ const beaconTimestampSpan = Utils.createSpan(["beacon-timestamp", initialClass], initialTimestamp);
beaconTimestampTd.appendChild(beaconTimestampSpan);
tr.appendChild(beaconTimestampTd);
tr.beaconTimestampSpan = beaconTimestampSpan;
- const beaconValueTd = Utils.createTd(["beacon-value", "beacon-waiting"], initialValue);
+ const beaconValueTd = Utils.createTd();
+ const beaconValueLabelDiv = Utils.createDiv();
+ beaconValueLabelDiv.style.display = "none";
+ beaconValueTd.appendChild(beaconValueLabelDiv);
+ const beaconValueValueDiv = Utils.createDiv();
+ beaconValueValueDiv.innerText = initialValue;
+ beaconValueValueDiv.classList.add("beacon-value", initialClass);
+ beaconValueTd.appendChild(beaconValueValueDiv);
tr.appendChild(beaconValueTd);
- tr.beaconValueTd = beaconValueTd;
+ tr.beaconValueLabelDiv = beaconValueLabelDiv;
+ tr.beaconValueValueDiv = beaconValueValueDiv;
const tbody = this.table.tBodies[0];
tbody.appendChild(tr);
@@ -309,7 +322,6 @@ export class BeaconsMinionPanel extends Panel {
return;
}
- let value = "";
let stamp = "";
if (pData["_stamp"]) {
// keep timestamp for further logic
@@ -321,13 +333,13 @@ export class BeaconsMinionPanel extends Panel {
if (pTag !== prefix + beaconName + "/") {
// Show the tag when it has extra information
- value = pTag + "\n";
+ tr.beaconValueLabelDiv.style.display = "";
+ tr.beaconValueLabelDiv.innerText = pTag;
}
if (pData["id"] === minionId) {
delete pData["id"];
}
- value += Output.formatObject(pData);
- tr.beaconValueTd.classList.remove("beacon-waiting");
+ tr.beaconValueValueDiv.classList.remove("beacon-waiting");
// round down to 0.1 second
// secondary events are close, but rarely exact on the same time
@@ -366,7 +378,7 @@ export class BeaconsMinionPanel extends Panel {
tr.helpButtonSpan.style.display = "none";
}
- tr.beaconValueTd.innerText = value;
+ Output.setHighlightObject(tr.beaconValueValueDiv, pData, "pre");
tr.prevStamp = stamp;
tr.prevTag = pTag;
diff --git a/saltgui/static/scripts/panels/Events.js b/saltgui/static/scripts/panels/Events.js
index 8ded134ba..249e87065 100644
--- a/saltgui/static/scripts/panels/Events.js
+++ b/saltgui/static/scripts/panels/Events.js
@@ -74,7 +74,8 @@ export class EventsPanel extends Panel {
const pDataObj = {};
Object.assign(pDataObj, pData);
delete pDataObj._stamp;
- const dataTd = Utils.createTd("event-data", Output.formatObject(pDataObj));
+ const dataTd = Utils.createTd();
+ Output.setHighlightObject(dataTd, pDataObj);
tr.append(dataTd);
tbody.prepend(tr);
diff --git a/saltgui/static/scripts/panels/Grains.js b/saltgui/static/scripts/panels/Grains.js
index 55a07d4d7..0858ce56f 100644
--- a/saltgui/static/scripts/panels/Grains.js
+++ b/saltgui/static/scripts/panels/Grains.js
@@ -143,8 +143,8 @@ export class GrainsPanel extends Panel {
// it is a json path
const obj = jsonPath(pMinionData, previewGrainValue);
if (Array.isArray(obj)) {
- td.innerText = Output.formatObject(obj[0]);
- td.classList.add("grain-value");
+ // TODO: why [0]
+ Output.setHighlightObject(td, obj[0]);
}
} else {
// a plain grain-name or a path in the grains.get style
@@ -156,8 +156,7 @@ export class GrainsPanel extends Panel {
}
}
if (obj) {
- td.innerText = Output.formatObject(obj);
- td.classList.add("grain-value");
+ Output.setHighlightObject(td, obj);
}
}
} else {
diff --git a/saltgui/static/scripts/panels/GrainsMinion.js b/saltgui/static/scripts/panels/GrainsMinion.js
index 99f0dcc50..8a1764bca 100644
--- a/saltgui/static/scripts/panels/GrainsMinion.js
+++ b/saltgui/static/scripts/panels/GrainsMinion.js
@@ -82,7 +82,8 @@ export class GrainsMinionPanel extends Panel {
this._addMenuItemGrainsDelVal(grainMenu, pMinionId, grainName, grains[grainName]);
// menu comes before this data on purpose
- const grainValueTd = Utils.createTd("grain-value", grainValue);
+ const grainValueTd = Utils.createTd();
+ Output.setHighlightObject(grainValueTd, grains[grainName]);
grainTr.appendChild(grainValueTd);
const tbody = this.table.tBodies[0];
diff --git a/saltgui/static/scripts/panels/Options.js b/saltgui/static/scripts/panels/Options.js
index 38c0d3051..f50b0b7d4 100644
--- a/saltgui/static/scripts/panels/Options.js
+++ b/saltgui/static/scripts/panels/Options.js
@@ -3,7 +3,6 @@
import {Character} from "../Character.js";
import {LoginPanel} from "../panels/Login.js";
import {Output} from "../output/Output.js";
-import {OutputYaml} from "../output/OutputYaml.js";
import {Panel} from "./Panel.js";
import {Router} from "../Router.js";
import {Utils} from "../Utils.js";
@@ -324,11 +323,12 @@ export class OptionsPanel extends Panel {
} else if (category === "session" && name === "expire") {
OptionsPanel._enhanceSessionExpire(td, value, sessionStart);
} else if (category === "session" && name === "perms") {
- td.innerText = OutputYaml.formatYAML(value);
+ // because the "master" file is also in YAML
+ Output.setHighlightObject(td, value, null, "yaml");
} else if (category === "session") {
td.innerText = value;
} else {
- td.innerText = this._parseAndFormat(name, value);
+ this._parseAndFormat(td, name, value);
}
if (category === "session" && name === "expire") {
@@ -377,31 +377,40 @@ export class OptionsPanel extends Panel {
}
}
- _parseAndFormat (id, valueStr) {
+ _parseAndFormat (pTd, id, valueStr) {
/* eslint-disable curly */
if (valueStr === null) valueStr = undefined;
if (valueStr === "undefined") valueStr = undefined;
/* eslint-enable curly */
+
if (valueStr === undefined) {
const tr = this.div.querySelector("#option-" + id);
if (tr.dataset.defaultValue) {
- return "(undefined) " + Character.RIGHTWARDS_ARROW + " " + tr.dataset.defaultValue;
+ pTd.innerText = "(undefined) " + Character.RIGHTWARDS_ARROW + " " + tr.dataset.defaultValue;
+ return;
}
- return "(undefined)";
+ pTd.innerText = "(undefined)";
+ return;
}
+
if (valueStr.length === 0) {
- return "(empty string)";
+ pTd.innerText = "(empty string)";
+ return;
}
+
if (valueStr[0] !== "{" && valueStr[0] !== "[") {
- return valueStr;
+ pTd.innerText = valueStr;
+ return;
}
+
let value;
try {
value = JSON.parse(valueStr);
} catch (err) {
value = err + " in \"" + valueStr + "\"";
}
- return OutputYaml.formatYAML(value);
+ // because the "master" file is also in YAML
+ Output.setHighlightObject(pTd, value, null, "yaml");
}
_isSelected (pCategory, pRow, pName) {
diff --git a/saltgui/static/scripts/panels/PillarsMinion.js b/saltgui/static/scripts/panels/PillarsMinion.js
index 0b10a426f..05a6f2484 100644
--- a/saltgui/static/scripts/panels/PillarsMinion.js
+++ b/saltgui/static/scripts/panels/PillarsMinion.js
@@ -97,8 +97,8 @@ export class PillarsMinionPanel extends Panel {
// initially use the hidden view
pillarValueTd.appendChild(pillarHiddenDiv);
- const pillarValueShown = Output.formatObject(pillars[pillarName]);
- const pillarShownDiv = Utils.createDiv("pillar-shown", pillarValueShown);
+ const pillarShownDiv = Utils.createDiv("pillar-shown");
+ Output.setHighlightObject(pillarShownDiv, pillars[pillarName]);
// initially hide the normal view
pillarShownDiv.style.display = "none";
Utils.addToolTip(pillarShownDiv, "Click to hide");
diff --git a/saltgui/static/scripts/panels/Reactors.js b/saltgui/static/scripts/panels/Reactors.js
index 7da72f138..b4f4eb064 100644
--- a/saltgui/static/scripts/panels/Reactors.js
+++ b/saltgui/static/scripts/panels/Reactors.js
@@ -66,7 +66,9 @@ export class ReactorsPanel extends Panel {
_addReactor (pEvent, pReactor) {
const tr = Utils.createTr();
tr.appendChild(Utils.createTd("", pEvent));
- tr.appendChild(Utils.createTd("", Output.formatObject(pReactor)));
+ const td = Utils.createTd();
+ Output.setHighlightObject(td, pReactor);
+ tr.appendChild(td);
const tbody = this.table.tBodies[0];
tbody.appendChild(tr);
diff --git a/saltgui/static/scripts/panels/SchedulesMinion.js b/saltgui/static/scripts/panels/SchedulesMinion.js
index e83a988e3..7ef4b6b49 100644
--- a/saltgui/static/scripts/panels/SchedulesMinion.js
+++ b/saltgui/static/scripts/panels/SchedulesMinion.js
@@ -112,11 +112,11 @@ export class SchedulesMinionPanel extends Panel {
this._addMenuItemScheduleRunJob(scheduleMenu, pMinionId, scheduleName, schedule);
// menu comes before this data on purpose
- const scheduleValue = Output.formatObject(schedule);
- const scheduleValueTd = Utils.createTd("schedule-value", scheduleValue);
+ const scheduleValueTd = Utils.createTd();
if (schedule.enabled === false || schedules.enabled === false) {
scheduleValueTd.classList.add("schedule-disabled");
}
+ Output.setHighlightObject(scheduleValueTd, schedule);
tr.appendChild(scheduleValueTd);
const tbody = this.table.tBodies[0];
diff --git a/saltgui/static/scripts/panels/Stats.js b/saltgui/static/scripts/panels/Stats.js
index f4a2409dc..d46494d41 100644
--- a/saltgui/static/scripts/panels/Stats.js
+++ b/saltgui/static/scripts/panels/Stats.js
@@ -69,7 +69,7 @@ export class StatsPanel extends Panel {
_handleStats (pStatsData) {
if (this.showErrorRowInstead(pStatsData)) {
- this.statsTd.innerHTML = "this error is typically caused by using the collect_stats: True setting in the master configuration file, which is broken in at least the recent versions of salt-api";
+ this.statsTd.innerHTML = "This error is typically caused by using the collect_stats: True setting in the master configuration file, which is broken in at least the recent versions of salt-api";
window.clearInterval(this.updateStatsTimer);
this.updateStatsTimer = null;
return;
@@ -141,6 +141,6 @@ export class StatsPanel extends Panel {
}
}
- this.statsTd.innerText = Output.formatObject(pStatsData);
+ Output.setHighlightObject(this.statsTd, pStatsData);
}
}
diff --git a/saltgui/static/stylesheets/beacons.css b/saltgui/static/stylesheets/beacons.css
index 0a6c3f902..3f3c07243 100644
--- a/saltgui/static/stylesheets/beacons.css
+++ b/saltgui/static/stylesheets/beacons.css
@@ -1,11 +1,3 @@
-.beacon-config {
- white-space: pre;
-}
-
-.beacon-value {
- white-space: pre;
-}
-
#page-beacons {
width: 100%;
}
diff --git a/saltgui/static/stylesheets/events.css b/saltgui/static/stylesheets/events.css
index 086b067b9..9f21a0a07 100644
--- a/saltgui/static/stylesheets/events.css
+++ b/saltgui/static/stylesheets/events.css
@@ -1,7 +1,3 @@
-td.event-data {
- white-space: pre-wrap;
-}
-
#page-events {
width: 100%;
}
diff --git a/saltgui/static/stylesheets/grains.css b/saltgui/static/stylesheets/grains.css
index 5181fdcd0..1797a6d44 100644
--- a/saltgui/static/stylesheets/grains.css
+++ b/saltgui/static/stylesheets/grains.css
@@ -1,8 +1,3 @@
-td.grain-value {
- white-space: pre-wrap;
- word-break: keep-all;
-}
-
#page-grains {
width: 100%;
}
diff --git a/saltgui/static/stylesheets/options.css b/saltgui/static/stylesheets/options.css
index 0c59421af..91d18a21b 100644
--- a/saltgui/static/stylesheets/options.css
+++ b/saltgui/static/stylesheets/options.css
@@ -15,7 +15,3 @@ label {
white-space: pre-wrap;
vertical-align: top;
}
-
-#stats-table tr td {
- white-space: pre-wrap;
-}
diff --git a/saltgui/static/stylesheets/pillars.css b/saltgui/static/stylesheets/pillars.css
index faac22b7b..bdfdb0d1c 100644
--- a/saltgui/static/stylesheets/pillars.css
+++ b/saltgui/static/stylesheets/pillars.css
@@ -12,5 +12,4 @@
.pillar-shown {
cursor: pointer;
- white-space: pre;
}
diff --git a/saltgui/static/stylesheets/schedules.css b/saltgui/static/stylesheets/schedules.css
index cfcbfdebb..ec0d1c1f2 100644
--- a/saltgui/static/stylesheets/schedules.css
+++ b/saltgui/static/stylesheets/schedules.css
@@ -2,10 +2,6 @@
width: 100%;
}
-td.schedule-value {
- white-space: pre-wrap;
-}
-
td.schedule-disabled {
color: gray;
}