2022-07-02 19:23:03 +01:00
|
|
|
/**
|
|
|
|
* @author n1474335 [n1474335@gmail.com]
|
|
|
|
* @copyright Crown Copyright 2022
|
|
|
|
* @license Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
import {showPanel} from "@codemirror/view";
|
2022-09-02 12:56:04 +01:00
|
|
|
import {CHR_ENC_SIMPLE_LOOKUP, CHR_ENC_SIMPLE_REVERSE_LOOKUP} from "../../core/lib/ChrEnc.mjs";
|
2022-07-02 19:23:03 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A Status bar extension for CodeMirror
|
|
|
|
*/
|
|
|
|
class StatusBarPanel {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* StatusBarPanel constructor
|
|
|
|
* @param {Object} opts
|
|
|
|
*/
|
|
|
|
constructor(opts) {
|
|
|
|
this.label = opts.label;
|
2023-02-03 14:55:15 +00:00
|
|
|
this.timing = opts.timing;
|
|
|
|
this.tabNumGetter = opts.tabNumGetter;
|
2022-07-02 19:23:03 +01:00
|
|
|
this.eolHandler = opts.eolHandler;
|
2022-09-02 12:56:04 +01:00
|
|
|
this.chrEncHandler = opts.chrEncHandler;
|
2022-09-16 19:24:57 +01:00
|
|
|
this.chrEncGetter = opts.chrEncGetter;
|
2022-12-09 21:23:25 +00:00
|
|
|
this.htmlOutput = opts.htmlOutput;
|
2022-09-02 12:56:04 +01:00
|
|
|
|
|
|
|
this.eolVal = null;
|
|
|
|
this.chrEncVal = null;
|
2022-07-02 19:23:03 +01:00
|
|
|
|
|
|
|
this.dom = this.buildDOM();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Builds the status bar DOM tree
|
|
|
|
* @returns {DOMNode}
|
|
|
|
*/
|
|
|
|
buildDOM() {
|
|
|
|
const dom = document.createElement("div");
|
|
|
|
const lhs = document.createElement("div");
|
|
|
|
const rhs = document.createElement("div");
|
|
|
|
|
|
|
|
dom.className = "cm-status-bar";
|
|
|
|
lhs.innerHTML = this.constructLHS();
|
|
|
|
rhs.innerHTML = this.constructRHS();
|
|
|
|
|
|
|
|
dom.appendChild(lhs);
|
|
|
|
dom.appendChild(rhs);
|
|
|
|
|
|
|
|
// Event listeners
|
2022-09-02 12:56:04 +01:00
|
|
|
dom.querySelectorAll(".cm-status-bar-select-btn").forEach(
|
|
|
|
el => el.addEventListener("click", this.showDropUp.bind(this), false)
|
|
|
|
);
|
|
|
|
dom.querySelector(".eol-select").addEventListener("click", this.eolSelectClick.bind(this), false);
|
|
|
|
dom.querySelector(".chr-enc-select").addEventListener("click", this.chrEncSelectClick.bind(this), false);
|
|
|
|
dom.querySelector(".cm-status-bar-filter-input").addEventListener("keyup", this.chrEncFilter.bind(this), false);
|
2022-07-02 19:23:03 +01:00
|
|
|
|
|
|
|
return dom;
|
|
|
|
}
|
|
|
|
|
2022-09-02 12:56:04 +01:00
|
|
|
/**
|
|
|
|
* Handler for dropup clicks
|
|
|
|
* Shows/Hides the dropup
|
|
|
|
* @param {Event} e
|
|
|
|
*/
|
|
|
|
showDropUp(e) {
|
|
|
|
const el = e.target
|
|
|
|
.closest(".cm-status-bar-select")
|
|
|
|
.querySelector(".cm-status-bar-select-content");
|
2022-12-09 21:23:25 +00:00
|
|
|
const btn = e.target.closest(".cm-status-bar-select-btn");
|
|
|
|
|
|
|
|
if (btn.classList.contains("disabled")) return;
|
2022-09-02 12:56:04 +01:00
|
|
|
|
|
|
|
el.classList.add("show");
|
|
|
|
|
|
|
|
// Focus the filter input if present
|
|
|
|
const filter = el.querySelector(".cm-status-bar-filter-input");
|
|
|
|
if (filter) filter.focus();
|
|
|
|
|
|
|
|
// Set up a listener to close the menu if the user clicks outside of it
|
|
|
|
hideOnClickOutside(el, e);
|
|
|
|
}
|
|
|
|
|
2022-07-02 19:23:03 +01:00
|
|
|
/**
|
|
|
|
* Handler for EOL Select clicks
|
|
|
|
* Sets the line separator
|
|
|
|
* @param {Event} e
|
|
|
|
*/
|
|
|
|
eolSelectClick(e) {
|
2022-09-09 16:35:21 +01:00
|
|
|
// preventDefault is required to stop the URL being modified and popState being triggered
|
|
|
|
e.preventDefault();
|
|
|
|
|
2022-07-02 19:23:03 +01:00
|
|
|
const eolLookup = {
|
|
|
|
"LF": "\u000a",
|
|
|
|
"VT": "\u000b",
|
|
|
|
"FF": "\u000c",
|
|
|
|
"CR": "\u000d",
|
|
|
|
"CRLF": "\u000d\u000a",
|
|
|
|
"NEL": "\u0085",
|
|
|
|
"LS": "\u2028",
|
|
|
|
"PS": "\u2029"
|
|
|
|
};
|
|
|
|
const eolval = eolLookup[e.target.getAttribute("data-val")];
|
|
|
|
|
2022-09-02 12:56:04 +01:00
|
|
|
if (eolval === undefined) return;
|
|
|
|
|
2022-07-02 19:23:03 +01:00
|
|
|
// Call relevant EOL change handler
|
|
|
|
this.eolHandler(eolval);
|
2022-09-02 12:56:04 +01:00
|
|
|
hideElement(e.target.closest(".cm-status-bar-select-content"));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler for Chr Enc Select clicks
|
|
|
|
* Sets the character encoding
|
|
|
|
* @param {Event} e
|
|
|
|
*/
|
|
|
|
chrEncSelectClick(e) {
|
2022-09-09 16:35:21 +01:00
|
|
|
// preventDefault is required to stop the URL being modified and popState being triggered
|
2022-12-09 20:46:01 +00:00
|
|
|
e.preventDefault();
|
2022-09-09 16:35:21 +01:00
|
|
|
|
2022-09-02 12:56:04 +01:00
|
|
|
const chrEncVal = parseInt(e.target.getAttribute("data-val"), 10);
|
|
|
|
|
|
|
|
if (isNaN(chrEncVal)) return;
|
|
|
|
|
|
|
|
this.chrEncHandler(chrEncVal);
|
|
|
|
this.updateCharEnc(chrEncVal);
|
|
|
|
hideElement(e.target.closest(".cm-status-bar-select-content"));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler for Chr Enc keyup events
|
|
|
|
* Filters the list of selectable character encodings
|
|
|
|
* @param {Event} e
|
|
|
|
*/
|
|
|
|
chrEncFilter(e) {
|
|
|
|
const input = e.target;
|
|
|
|
const filter = input.value.toLowerCase();
|
|
|
|
const div = input.closest(".cm-status-bar-select-content");
|
|
|
|
const a = div.getElementsByTagName("a");
|
|
|
|
for (let i = 0; i < a.length; i++) {
|
|
|
|
const txtValue = a[i].textContent || a[i].innerText;
|
|
|
|
if (txtValue.toLowerCase().includes(filter)) {
|
|
|
|
a[i].style.display = "block";
|
|
|
|
} else {
|
|
|
|
a[i].style.display = "none";
|
|
|
|
}
|
|
|
|
}
|
2022-07-02 19:23:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Counts the stats of a document
|
|
|
|
* @param {Text} doc
|
|
|
|
*/
|
|
|
|
updateStats(doc) {
|
|
|
|
const length = this.dom.querySelector(".stats-length-value"),
|
|
|
|
lines = this.dom.querySelector(".stats-lines-value");
|
|
|
|
length.textContent = doc.length;
|
|
|
|
lines.textContent = doc.lines;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the current selection info
|
|
|
|
* @param {EditorState} state
|
|
|
|
* @param {boolean} selectionSet
|
|
|
|
*/
|
|
|
|
updateSelection(state, selectionSet) {
|
2022-10-28 13:24:03 +01:00
|
|
|
const selLen = state?.selection?.main ?
|
2022-07-02 19:23:03 +01:00
|
|
|
state.selection.main.to - state.selection.main.from :
|
|
|
|
0;
|
|
|
|
|
|
|
|
const selInfo = this.dom.querySelector(".sel-info"),
|
|
|
|
curOffsetInfo = this.dom.querySelector(".cur-offset-info");
|
|
|
|
|
|
|
|
if (!selectionSet) {
|
|
|
|
selInfo.style.display = "none";
|
|
|
|
curOffsetInfo.style.display = "none";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selLen > 0) { // Range
|
|
|
|
const start = this.dom.querySelector(".sel-start-value"),
|
|
|
|
end = this.dom.querySelector(".sel-end-value"),
|
|
|
|
length = this.dom.querySelector(".sel-length-value");
|
|
|
|
|
|
|
|
selInfo.style.display = "inline-block";
|
|
|
|
curOffsetInfo.style.display = "none";
|
|
|
|
|
|
|
|
start.textContent = state.selection.main.from;
|
|
|
|
end.textContent = state.selection.main.to;
|
|
|
|
length.textContent = state.selection.main.to - state.selection.main.from;
|
|
|
|
} else { // Position
|
|
|
|
const offset = this.dom.querySelector(".cur-offset-value");
|
|
|
|
|
|
|
|
selInfo.style.display = "none";
|
|
|
|
curOffsetInfo.style.display = "inline-block";
|
|
|
|
|
|
|
|
offset.textContent = state.selection.main.from;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-09-02 12:56:04 +01:00
|
|
|
* Sets the current EOL separator in the status bar
|
2022-07-02 19:23:03 +01:00
|
|
|
* @param {EditorState} state
|
|
|
|
*/
|
|
|
|
updateEOL(state) {
|
2022-09-02 12:56:04 +01:00
|
|
|
if (state.lineBreak === this.eolVal) return;
|
|
|
|
|
2022-07-02 19:23:03 +01:00
|
|
|
const eolLookup = {
|
2022-09-02 12:56:04 +01:00
|
|
|
"\u000a": ["LF", "Line Feed"],
|
|
|
|
"\u000b": ["VT", "Vertical Tab"],
|
|
|
|
"\u000c": ["FF", "Form Feed"],
|
|
|
|
"\u000d": ["CR", "Carriage Return"],
|
|
|
|
"\u000d\u000a": ["CRLF", "Carriage Return + Line Feed"],
|
|
|
|
"\u0085": ["NEL", "Next Line"],
|
|
|
|
"\u2028": ["LS", "Line Separator"],
|
|
|
|
"\u2029": ["PS", "Paragraph Separator"]
|
2022-07-02 19:23:03 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
const val = this.dom.querySelector(".eol-value");
|
2022-09-02 12:56:04 +01:00
|
|
|
const button = val.closest(".cm-status-bar-select-btn");
|
|
|
|
const eolName = eolLookup[state.lineBreak];
|
|
|
|
val.textContent = eolName[0];
|
2023-01-13 14:25:40 +00:00
|
|
|
button.setAttribute("title", `End of line sequence:<br>${eolName[1]}`);
|
|
|
|
button.setAttribute("data-original-title", `End of line sequence:<br>${eolName[1]}`);
|
2022-09-02 12:56:04 +01:00
|
|
|
this.eolVal = state.lineBreak;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2022-10-21 18:29:52 +01:00
|
|
|
* Sets the current character encoding of the document
|
2022-09-02 12:56:04 +01:00
|
|
|
*/
|
2022-09-16 19:24:57 +01:00
|
|
|
updateCharEnc() {
|
|
|
|
const chrEncVal = this.chrEncGetter();
|
2022-09-02 12:56:04 +01:00
|
|
|
if (chrEncVal === this.chrEncVal) return;
|
|
|
|
|
|
|
|
const name = CHR_ENC_SIMPLE_REVERSE_LOOKUP[chrEncVal] ? CHR_ENC_SIMPLE_REVERSE_LOOKUP[chrEncVal] : "Raw Bytes";
|
|
|
|
|
|
|
|
const val = this.dom.querySelector(".chr-enc-value");
|
|
|
|
const button = val.closest(".cm-status-bar-select-btn");
|
|
|
|
val.textContent = name;
|
2023-01-13 14:25:40 +00:00
|
|
|
button.setAttribute("title", `${this.label} character encoding:<br>${name}`);
|
|
|
|
button.setAttribute("data-original-title", `${this.label} character encoding:<br>${name}`);
|
2022-09-02 12:56:04 +01:00
|
|
|
this.chrEncVal = chrEncVal;
|
2022-07-02 19:23:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-02-03 14:55:15 +00:00
|
|
|
* Sets the latest timing info
|
2022-07-02 19:23:03 +01:00
|
|
|
*/
|
2023-02-03 14:55:15 +00:00
|
|
|
updateTiming() {
|
|
|
|
if (!this.timing) return;
|
|
|
|
|
2022-07-02 19:23:03 +01:00
|
|
|
const bakingTime = this.dom.querySelector(".baking-time-value");
|
|
|
|
const bakingTimeInfo = this.dom.querySelector(".baking-time-info");
|
|
|
|
|
2023-02-03 14:55:15 +00:00
|
|
|
if (this.label === "Output" && this.timing) {
|
2022-07-02 19:23:03 +01:00
|
|
|
bakingTimeInfo.style.display = "inline-block";
|
2023-02-03 14:55:15 +00:00
|
|
|
bakingTime.textContent = this.timing.overallDuration(this.tabNumGetter());
|
|
|
|
|
|
|
|
const info = this.timing.printStages(this.tabNumGetter()).replace(/\n/g, "<br>");
|
|
|
|
bakingTimeInfo.setAttribute("title", info);
|
|
|
|
bakingTimeInfo.setAttribute("data-original-title", info);
|
2022-07-02 19:23:03 +01:00
|
|
|
} else {
|
|
|
|
bakingTimeInfo.style.display = "none";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-02 12:56:04 +01:00
|
|
|
/**
|
|
|
|
* Updates the sizing of elements that need to fit correctly
|
|
|
|
* @param {EditorView} view
|
|
|
|
*/
|
|
|
|
updateSizing(view) {
|
2023-01-13 13:12:01 +00:00
|
|
|
const viewHeight = view.contentDOM.parentNode.clientHeight;
|
2022-09-02 12:56:04 +01:00
|
|
|
this.dom.querySelectorAll(".cm-status-bar-select-scroll").forEach(
|
|
|
|
el => {
|
|
|
|
el.style.maxHeight = (viewHeight - 50) + "px";
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-12-09 21:23:25 +00:00
|
|
|
/**
|
|
|
|
* Checks whether there is HTML output requiring some widgets to be disabled
|
|
|
|
*/
|
|
|
|
monitorHTMLOutput() {
|
|
|
|
if (!this.htmlOutput?.changed) return;
|
|
|
|
|
|
|
|
if (this.htmlOutput?.html === "") {
|
|
|
|
// Enable all controls
|
|
|
|
this.dom.querySelectorAll(".disabled").forEach(el => {
|
|
|
|
el.classList.remove("disabled");
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// Disable chrenc, length, selection etc.
|
|
|
|
this.dom.querySelectorAll(".cm-status-bar-select-btn").forEach(el => {
|
|
|
|
el.classList.add("disabled");
|
|
|
|
});
|
|
|
|
|
|
|
|
this.dom.querySelector(".stats-length-value").parentNode.classList.add("disabled");
|
|
|
|
this.dom.querySelector(".stats-lines-value").parentNode.classList.add("disabled");
|
|
|
|
this.dom.querySelector(".sel-info").classList.add("disabled");
|
|
|
|
this.dom.querySelector(".cur-offset-info").classList.add("disabled");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-02 19:23:03 +01:00
|
|
|
/**
|
|
|
|
* Builds the Left-hand-side widgets
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
|
|
|
constructLHS() {
|
|
|
|
return `
|
|
|
|
<span data-toggle="tooltip" title="${this.label} length">
|
|
|
|
<i class="material-icons">abc</i>
|
|
|
|
<span class="stats-length-value"></span>
|
|
|
|
</span>
|
|
|
|
<span data-toggle="tooltip" title="Number of lines">
|
|
|
|
<i class="material-icons">sort</i>
|
|
|
|
<span class="stats-lines-value"></span>
|
|
|
|
</span>
|
|
|
|
|
|
|
|
<span class="sel-info" data-toggle="tooltip" title="Main selection">
|
|
|
|
<i class="material-icons">highlight_alt</i>
|
|
|
|
<span class="sel-start-value"></span>\u279E<span class="sel-end-value"></span>
|
|
|
|
(<span class="sel-length-value"></span> selected)
|
|
|
|
</span>
|
|
|
|
<span class="cur-offset-info" data-toggle="tooltip" title="Cursor offset">
|
|
|
|
<i class="material-icons">location_on</i>
|
|
|
|
<span class="cur-offset-value"></span>
|
|
|
|
</span>`;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Builds the Right-hand-side widgets
|
|
|
|
* Event listener set up in Manager
|
2022-09-02 12:56:04 +01:00
|
|
|
*
|
2022-07-02 19:23:03 +01:00
|
|
|
* @returns {string}
|
|
|
|
*/
|
|
|
|
constructRHS() {
|
2022-09-02 12:56:04 +01:00
|
|
|
const chrEncOptions = Object.keys(CHR_ENC_SIMPLE_LOOKUP).map(name =>
|
|
|
|
`<a href="#" draggable="false" data-val="${CHR_ENC_SIMPLE_LOOKUP[name]}">${name}</a>`
|
|
|
|
).join("");
|
|
|
|
|
2022-07-02 19:23:03 +01:00
|
|
|
return `
|
2023-02-03 14:55:15 +00:00
|
|
|
<span class="baking-time-info" style="display: none" data-toggle="tooltip" data-html="true" title="Baking time">
|
2022-07-02 19:23:03 +01:00
|
|
|
<i class="material-icons">schedule</i>
|
|
|
|
<span class="baking-time-value"></span>ms
|
|
|
|
</span>
|
|
|
|
|
2022-09-02 12:56:04 +01:00
|
|
|
<div class="cm-status-bar-select chr-enc-select">
|
2023-01-13 14:25:40 +00:00
|
|
|
<span class="cm-status-bar-select-btn" data-toggle="tooltip" data-html="true" data-placement="left" title="${this.label} character encoding">
|
2022-09-02 12:56:04 +01:00
|
|
|
<i class="material-icons">text_fields</i> <span class="chr-enc-value">Raw Bytes</span>
|
|
|
|
</span>
|
|
|
|
<div class="cm-status-bar-select-content">
|
|
|
|
<div class="cm-status-bar-select-scroll no-select">
|
|
|
|
<a href="#" draggable="false" data-val="0">Raw Bytes</a>
|
|
|
|
${chrEncOptions}
|
|
|
|
</div>
|
|
|
|
<div class="input-group cm-status-bar-filter-search">
|
|
|
|
<div class="input-group-prepend">
|
|
|
|
<span class="input-group-text">
|
|
|
|
<i class="material-icons">search</i>
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
<input type="text" class="form-control cm-status-bar-filter-input" placeholder="Filter...">
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
2022-07-02 19:23:03 +01:00
|
|
|
|
|
|
|
<div class="cm-status-bar-select eol-select">
|
2023-01-13 14:25:40 +00:00
|
|
|
<span class="cm-status-bar-select-btn" data-toggle="tooltip" data-html="true" data-placement="left" title="End of line sequence">
|
2022-07-02 19:23:03 +01:00
|
|
|
<i class="material-icons">keyboard_return</i> <span class="eol-value"></span>
|
|
|
|
</span>
|
2022-09-02 12:56:04 +01:00
|
|
|
<div class="cm-status-bar-select-content no-select">
|
|
|
|
<a href="#" draggable="false" data-val="LF">Line Feed, U+000A</a>
|
|
|
|
<a href="#" draggable="false" data-val="VT">Vertical Tab, U+000B</a>
|
|
|
|
<a href="#" draggable="false" data-val="FF">Form Feed, U+000C</a>
|
|
|
|
<a href="#" draggable="false" data-val="CR">Carriage Return, U+000D</a>
|
|
|
|
<a href="#" draggable="false" data-val="CRLF">CR+LF, U+000D U+000A</a>
|
|
|
|
<!-- <a href="#" draggable="false" data-val="NL">Next Line, U+0085</a> This causes problems. -->
|
|
|
|
<a href="#" draggable="false" data-val="LS">Line Separator, U+2028</a>
|
|
|
|
<a href="#" draggable="false" data-val="PS">Paragraph Separator, U+2029</a>
|
2022-07-02 19:23:03 +01:00
|
|
|
</div>
|
|
|
|
</div>`;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-09-02 12:56:04 +01:00
|
|
|
const elementsWithListeners = {};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Hides the provided element when a click is made outside of it
|
|
|
|
* @param {Element} element
|
|
|
|
* @param {Event} instantiatingEvent
|
|
|
|
*/
|
|
|
|
function hideOnClickOutside(element, instantiatingEvent) {
|
|
|
|
/**
|
|
|
|
* Handler for document click events
|
|
|
|
* Closes element if click is outside it.
|
|
|
|
* @param {Event} event
|
|
|
|
*/
|
|
|
|
const outsideClickListener = event => {
|
|
|
|
// Don't trigger if we're clicking inside the element, or if the element
|
|
|
|
// is not visible, or if this is the same click event that opened it.
|
|
|
|
if (!element.contains(event.target) &&
|
|
|
|
event.timeStamp !== instantiatingEvent.timeStamp) {
|
|
|
|
hideElement(element);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-09-09 16:35:21 +01:00
|
|
|
if (!Object.prototype.hasOwnProperty.call(elementsWithListeners, element)) {
|
2022-09-02 12:56:04 +01:00
|
|
|
elementsWithListeners[element] = outsideClickListener;
|
2022-09-09 16:35:21 +01:00
|
|
|
document.addEventListener("click", elementsWithListeners[element], false);
|
2022-09-02 12:56:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Hides the specified element and removes the click listener for it
|
|
|
|
* @param {Element} element
|
|
|
|
*/
|
|
|
|
function hideElement(element) {
|
|
|
|
element.classList.remove("show");
|
2022-09-09 16:35:21 +01:00
|
|
|
document.removeEventListener("click", elementsWithListeners[element], false);
|
2022-09-02 12:56:04 +01:00
|
|
|
delete elementsWithListeners[element];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-07-02 19:23:03 +01:00
|
|
|
/**
|
|
|
|
* A panel constructor factory building a panel that re-counts the stats every time the document changes.
|
|
|
|
* @param {Object} opts
|
|
|
|
* @returns {Function<PanelConstructor>}
|
|
|
|
*/
|
|
|
|
function makePanel(opts) {
|
|
|
|
const sbPanel = new StatusBarPanel(opts);
|
|
|
|
|
|
|
|
return (view) => {
|
|
|
|
sbPanel.updateEOL(view.state);
|
2022-09-16 19:24:57 +01:00
|
|
|
sbPanel.updateCharEnc();
|
2023-02-03 14:55:15 +00:00
|
|
|
sbPanel.updateTiming();
|
2022-07-02 19:23:03 +01:00
|
|
|
sbPanel.updateStats(view.state.doc);
|
|
|
|
sbPanel.updateSelection(view.state, false);
|
2022-12-09 21:23:25 +00:00
|
|
|
sbPanel.monitorHTMLOutput();
|
2022-07-02 19:23:03 +01:00
|
|
|
|
|
|
|
return {
|
|
|
|
"dom": sbPanel.dom,
|
|
|
|
update(update) {
|
|
|
|
sbPanel.updateEOL(update.state);
|
2022-09-16 19:24:57 +01:00
|
|
|
sbPanel.updateCharEnc();
|
2022-07-02 19:23:03 +01:00
|
|
|
sbPanel.updateSelection(update.state, update.selectionSet);
|
2023-02-03 14:55:15 +00:00
|
|
|
sbPanel.updateTiming();
|
2022-12-09 21:23:25 +00:00
|
|
|
sbPanel.monitorHTMLOutput();
|
2022-09-02 12:56:04 +01:00
|
|
|
if (update.geometryChanged) {
|
|
|
|
sbPanel.updateSizing(update.view);
|
|
|
|
}
|
2022-07-02 19:23:03 +01:00
|
|
|
if (update.docChanged) {
|
|
|
|
sbPanel.updateStats(update.state.doc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A function that build the extension that enables the panel in an editor.
|
|
|
|
* @param {Object} opts
|
|
|
|
* @returns {Extension}
|
|
|
|
*/
|
|
|
|
export function statusBar(opts) {
|
|
|
|
const panelMaker = makePanel(opts);
|
|
|
|
return showPanel.of(panelMaker);
|
|
|
|
}
|