webstrate-revision-browser-component/webstrate-revision-browser.js

/**
 *  Revision Browser
 *  Manage revisions of a Webstrate in a visual editor
 * 
 *  Copyright 2020, 2021 Rolf Bagge, Janus B. Kristensen, CAVI,
 *  Center for Advanced Visualization and Interacion, Aarhus University
 *    
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0

 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
**/
   
/**
 * Manage revisions of a Webstrate in a visual editor
 */
window.RevisionBrowser = class RevisionBrowser {
    constructor() {
        let self = this;

        this.selfItemTemplate = WebstrateComponents.Tools.loadTemplate("#revisionBrowserSelfItem");
        this.listItemTemplate = WebstrateComponents.Tools.loadTemplate("#revisionBrowserListItem");
        this.cardTemplate = WebstrateComponents.Tools.loadTemplate("#revisionBrowserPresentationCard");
        this.tagTemplate = WebstrateComponents.Tools.loadTemplate("#revisionBrowserTaggingCard");

        this.html = WebstrateComponents.Tools.loadTemplate("#revision-browser-base");

        this.populateList();

        webstrate.on("tag", ()=>{
            self.populateList();
        });

        let revisionSelector = cQuery(this.html).find(".revisionSelector");
        revisionSelector[0].value = webstrate.version;

        revisionSelector.on("input", ()=>{
            if(parseInt(revisionSelector[0].value) > webstrate.version) {
                revisionSelector[0].value = webstrate.version;
            }
            self.loadRevision(revisionSelector[0].value);
            self.unselect();
            self.selectTagFromRevision(revisionSelector[0].value);

        });
    }

    loadTagUI() {
        let self = this;
        let target = cQuery(this.html).find(".revision_framecontainer");
        target.empty();
        let card = this.tagTemplate.cloneNode(true);
        let tagButton = cQuery(card).find(".tagButton");
        tagButton.on("click", ()=>{        
            let label = window.prompt("Enter wanted tag label:");

            if(label != null && label.trim() !== "") {
                webstrate.on("tag", function once(version, label) {
                    console.log("Saved tag:", version, label);
                    webstrate.off("tag", once);
                });

                webstrate.tag(label);
            }        
        });
        
        mdc.autoInit(card);        
        target.append(card);
    }

    loadRevision(revision, tagLabel = null) {
        let self = this;

        let tags = webstrate.tags();
        let tagList = Object.keys(tags).sort((v1, v2)=>{
            return parseInt(v2) - parseInt(v1);
        });

        let target = cQuery(this.html).find(".revision_framecontainer");

        let card = this.cardTemplate.cloneNode(true);
        let frameTarget = cQuery(card).one("iframe");
        let authorTarget = cQuery(card).one(".revision-author");
        let deleteButton = cQuery(card).find(".deleteButton");
        let restoreButton = cQuery(card).find(".restoreButton");

        target.empty();
        if (!revision) return;
        
        frameTarget.src = revision;
        cQuery(card).one(".revision-name").innerText = tags[revision];
        mdc.autoInit(card);
        target.append(card);

        webstrate.getOps(parseInt(revision)-1,parseInt(revision), (err, ops) => {
            let username = "System";
            if (ops != null && ops.length > 0){
                if (ops[0].session){
                    username = ops[0].session.userId;
                } else {
                    username = ops[0].src;
                }
            }
            if (username == "anonymous:") username = "Anonymous";
            authorTarget.innerText = "by "+username;
        });

        restoreButton.on("click", ()=>{
            webstrate.restore(parseInt(revision), (err, newVersion) =>{
                if(err) {
                    console.error(err);
                } else {
                    console.log("Successfully restored");
                    EventSystem.triggerEvent("RevisionBrowser.OnRestore", self);
                }
            });
        });

        if(tagLabel != null) {
            deleteButton.on("click", ()=>{

                console.log("Deleting: ", tagLabel);

                webstrate.untag(tagLabel);

                target.empty();

                self.populateList();
            });
        } else {
            deleteButton.remove();
        }
    }
    
    unselect(){
        cQuery(this.html).find(".mdc-list-item--selected").removeClass("mdc-list-item--selected");
        cQuery(this.html).find(".mdc-list")[0].MDCList.foundation.selectedIndex_ = -1;
    }

    selectTagFromRevision(revision) {
        let listItem = cQuery(this.html).find(".mdc-list-item[data-revision='"+revision+"']");

        if(listItem != null) {
            let foundIndex = -1;

            cQuery(this.html).find(".mdc-list")[0].MDCList.listElements.forEach((listElement, index)=>{
                if(listItem[0] === listElement) {
                    foundIndex = index;
                }
            });

            cQuery(this.html).find(".mdc-list")[0].MDCList.selectedIndex = foundIndex;
        }
    }

    populateList() {
        let self = this;

        // Populate list
        let tags = webstrate.tags();
        let tagList = Object.keys(tags).sort((v1, v2)=>{
            return parseInt(v2) - parseInt(v1);
        });
        let browserList = cQuery(this.html).one(".mdc-list");

        //Clear old entries
        cQuery(browserList).find(".mdc-list-item").remove();

        let currentItem = this.selfItemTemplate.cloneNode(true);
        cQuery(currentItem).one(".revision-subtext").innerText = "Revision "+webstrate.version;
        currentItem.setAttribute("data-revision", webstrate.version);
        browserList.append(currentItem);        

        tagList.forEach((revision)=>{
            let listItem = this.listItemTemplate.cloneNode(true);
            cQuery(listItem).one(".revision-name").innerText = tags[revision];
            cQuery(listItem).one(".revision-subtext").innerText = "Revision "+revision;
            browserList.append(listItem);
            listItem.setAttribute("data-revision", revision);
        });

        // Auto-init all the components
        mdc.autoInit(this.html);

        // Configure list and preview
        let list = browserList.MDCList;
        list.singleSelection = true;

        list.listen("MDCList:action", (evt)=>{
            if (evt.detail.index>0){
                let revision = tagList[evt.detail.index-1];
                let tagLabel = tags[revision];

                self.loadRevision(revision, tagLabel);
                cQuery(self.html).find(".revisionSelector")[0].value=revision;
            } else {
                self.loadTagUI();
            }
        });
        
        list.selectedIndex = 0;
        this.loadTagUI();
    }
};