Source: xxe/part/ValidateTool.js

/**
 * {@link XMLEditor} part used to display the validity status of 
 * the document being edited and validity error messages (if any).
 */
class ValidateTool extends HTMLElement {
    constructor() {
        super();

        this._severityInfo = {};
        const infos = [
            { severity: SEVERITY_NONE, icon: "ok-circled",
              color: "green", description: "Document is Valid" },
            { severity: SEVERITY_SEMANTIC_WARNING, icon: "minus-circled",
              color: "orange", description: "Semantic Warning" },
            { severity: SEVERITY_SEMANTIC_ERROR, icon: "minus-circled",
              color: "red", description: "Semantic Error" },
            { severity: SEVERITY_INVALID_REFERENCE, icon: "attention",
              color: "#FFEF00", description: "Invalid Reference" },
            { severity: SEVERITY_INVALID_DATA, icon: "cancel-squared",
              color: "orange", description: "Invalid Data" },
            { severity: SEVERITY_INVALID_STRUCTURE, icon: "cancel-squared",
              color: "red", description: "Invalid Structure" }
        ];
        for (let info of infos) {
            this._severityInfo[info.severity] = info;
        }
        
        this._xmlEditor = null;
        this._button = null;
        this._diagPopup = null;
        this._onDiagPopupClosed = this.onDiagPopupClosed.bind(this);
        this._onLinkClicked = this.onLinkClicked.bind(this);
    }

    // -----------------------------------------------------------------------
    // Custom element
    // -----------------------------------------------------------------------

    connectedCallback() {
        if (this.firstChild === null) {
            this._button = document.createElement("span");
            this._button.className = "xxe-tool-button xxe-valid-tool-button";
            this.showSeverity(-1);
            this.appendChild(this._button);

            let handler = this.onClick.bind(this);
            this._button.addEventListener("click", handler);
            this._button.addEventListener("contextmenu", handler);
        }
        // Otherwise, already connected.
    }

    // -----------------------------------------------------------------------
    // Event handlers
    // -----------------------------------------------------------------------

    onClick(event) {
        XUI.Util.consumeEvent(event);

        if (this._xmlEditor !== null &&
            !this._button.classList.contains("xui-control-disabled")) {
            this._xmlEditor.validateDocument()
                .then((result) => {
                    // null result: document cannot be checked for validity. 
                    this.showDiagnostics((result !== null)?
                                         result.diagnostics : null);
                })
                .catch((error) => {
                    console.error(`Request "validateDocument" has failed: \
${error}`);
                });
        }
    }

    showDiagnostics(diagnostics) {
        if (this._diagPopup === null) {
            if (diagnostics !== null && diagnostics.length > 0) {
                this._diagPopup = XUI.Dialogs.open({
                    form: this.createDiagPopup(diagnostics), type: "popup",
                    classes:
                    "xui-control xui-dialog xxe-tool-popup xxe-valid-tool-diag",
                    position: "startmenu", reference: this
                });
                this._diagPopup.addEventListener("dialogclosed",
                                                 this._onDiagPopupClosed);
            }
            // Otherwise, either cannot be checked or is valid: do not show
            // anything.
        } else {
            XUI.Dialogs.close(this._diagPopup);
        }
    }
    
    createDiagPopup(diagnostics) {
        let list = document.createElement("div");
        list.className =
            "xui-control xxe-tool-popup-list xxe-valid-tool-diag-list";
        list.style.width =
            String(this.parentElement.getBoundingClientRect().width / 2) + "px";

        let count = 0;
        for (let diag of diagnostics) {
            let item = document.createElement("div");
            item.className = 
                "xxe-valid-tool-diag-item xxe-valid-tool-diag-" +
                String(diag.severity) + "-" + String(count % 2);

            let link1 =
              this.createDiagLink("[" + String(1+count) + "]", diag.elementUID);
            item.appendChild(link1);

            let msg = document.createElement("div");
            msg.className = "xxe-valid-tool-diag-msg";
            msg.appendChild(document.createTextNode(diag.message));
            
            if (diag.severity <= SEVERITY_INVALID_REFERENCE) {
                let detail = diag.detail;
                if (detail !== null &&
                    Array.isArray(detail) && detail.length === 3 &&
                    (detail[0] === "DUPLICATE_ID" ||
                     detail[0] === "DUPLICATE_ANCHOR")) {
                    msg.appendChild(document.createElement("br"));
                    msg.appendChild(document.createTextNode(
                        `First occurrence of "${detail[1]}" is found `));
                    
                    let link2 = this.createDiagLink("here", detail[2]);
                    msg.appendChild(link2);
                }
            }
            
            item.appendChild(msg);

            list.appendChild(item);
            ++count;
        }
        
        return list;
    }

    createDiagLink(text, elementUID) {
        let link = document.createElement("span");
        link.className = "xxe-valid-tool-diag-link";
        link.setAttribute("data-uid", elementUID);
        link.appendChild(document.createTextNode(text));
        link.addEventListener("click", this._onLinkClicked);

        return link;
    }
    
    onLinkClicked(event) {
        XUI.Util.consumeEvent(event);
        
        if (this._xmlEditor !== null) {
            let docView = this._xmlEditor.documentView;
            
            let uid = event.target.getAttribute("data-uid");
            if (uid) {
                let view = docView.getNodeView(uid, /*reportError*/ false);
                if (view !== null) {
                    docView.selectNode(view, /*show*/ true);
                }
            }
        }
    }
    
    onDiagPopupClosed(event) {
        this._diagPopup = null;
        
        if (this._xmlEditor !== null) {
            this._xmlEditor.documentView.requestFocus();
        }
    }
    
    // -----------------------------------------------------------------------
    // Used by XMLEditor
    // -----------------------------------------------------------------------

    set xmlEditor(editor) {
        this._xmlEditor = editor;
    }

    validityStateChanged(event) {
        // When a document is opened or closed, XMLEditor invokes this method
        // with a null event ==> check validity is always initially disabled.
        //
        // After the document is opened, if it can be checked for validity, an
        // actual DocumentValidatedEvent is always sent by the server, even if
        // this event just says: it's all good.
        
        let severity = -1;
        if (event !== null) {
            severity = event.severity;
        }
        this.showSeverity(severity);
    }
    
    showSeverity(severity) {
        let title = "Check Document Validity";
        let icon = "ok-circled";
        let color = null;
        
        if (severity >= SEVERITY_NONE &&
            severity <= SEVERITY_INVALID_STRUCTURE) {
            this._button.classList.remove("xui-control-disabled");
            
            const info = this._severityInfo[severity];
            title += "\n\n" + info.description;
            icon = info.icon;
            color = info.color;
        } else {
            this._button.classList.add("xui-control-disabled");
        }

        this._button.setAttribute("title", title);
        this._button.textContent = XUI.StockIcon[icon];
        this._button.style.color = color; // null removes CSS prop "color".
    }
}

window.customElements.define("xxe-validate-tool", ValidateTool);