sidebar.js

536 lines | 19.723 kB Blame History Raw Download
/**
 * Contains the logic for the sidebar.
 * @param graph the graph that belongs to these controls
 * @returns {{}}
 */
module.exports = function ( graph ){
  
  var sidebar = {},
    languageTools = webvowl.util.languageTools(),
    elementTools = webvowl.util.elementTools(),
    // Required for reloading when the language changes
    ontologyInfo,
    visibleSidebar = 1,
    lastSelectedElement,
    
    detailArea = d3.select("#detailsArea"),
    graphArea = d3.select("#canvasArea"),
    menuArea = d3.select("#swipeBarContainer"),
    collapseButton = d3.select("#sidebarExpandButton");
  
  /**
   * Setup the menu bar.
   */
  
  
  function setupCollapsing(){
    // adapted version of this example: http://www.normansblog.de/simple-jquery-accordion/
    function collapseContainers( containers ){
      containers.classed("hidden", true);
    }
    
    function expandContainers( containers ){
      containers.classed("hidden", false);
    }
    
    var triggers = d3.selectAll(".accordion-trigger");
    
    // Collapse all inactive triggers on startup
    collapseContainers(d3.selectAll(".accordion-trigger:not(.accordion-trigger-active) + div"));
    
    triggers.on("click", function (){
      var selectedTrigger = d3.select(this),
        activeTriggers = d3.selectAll(".accordion-trigger-active");
      
      if ( selectedTrigger.classed("accordion-trigger-active") ) {
        // Collapse the active (which is also the selected) trigger
        collapseContainers(d3.select(selectedTrigger.node().nextElementSibling));
        selectedTrigger.classed("accordion-trigger-active", false);
      } else {
        // Collapse the other trigger ...
        collapseContainers(d3.selectAll(".accordion-trigger-active + div"));
        activeTriggers.classed("accordion-trigger-active", false);
        // ... and expand the selected one
        expandContainers(d3.select(selectedTrigger.node().nextElementSibling));
        selectedTrigger.classed("accordion-trigger-active", true);
      }
    });
  }
  
  sidebar.clearOntologyInformation = function (){
    d3.select("#ontology-panel").selectAll("*").remove();
    clearSelectedNodePanel();
  };
  
  /**
   * Updates the information of the passed ontology.
   * @param data the graph data
   * @param statistics the statistics module
   */
  sidebar.updateOntologyInformation = function ( data, statistics ){
    data = data || {};
    ontologyInfo = data.header || {};

    var panel = d3.select("#ontology-panel");
    panel.selectAll("*").remove();

    renderPair(panel, "IRI", ontologyInfo.iri, ontologyInfo.iri);
    var title = languageTools.textInLanguage(ontologyInfo.title, graph.language());
    renderPair(panel, "Title", title);
    renderPair(panel, "Version", ontologyInfo.version);
    var authors = ontologyInfo.author;
    if ( authors instanceof Array ) authors = authors.join(", ");
    renderPair(panel, "Author", authors);
    var description = languageTools.textInLanguage(ontologyInfo.description, graph.language());
    renderPair(panel, "Description", description);

    var other = ontologyInfo.other || {};
    for ( var otherKey in other ) {
      if ( other.hasOwnProperty(otherKey) ) {
        var otherEntries = other[otherKey];
        if ( !otherEntries ) continue;
        var otherLabel = prefixedName(otherKey);
        otherEntries.forEach(function ( ann ){
          if ( !ann ) return;
          renderPair(panel, otherLabel, ann.value, ann.type === "iri" ? ann.value : undefined);
        });
      }
    }

    if ( panel.selectAll(".annotation-row").size() === 0 ) {
      panel.append("p").classed("no-selection-hint", true).text("No ontology information available.");
    }

    sidebar.updateSelectionInformation(undefined);
    setLanguages(ontologyInfo.languages);
  };
  
  function setLanguages( languages ){
    languages = languages || [];
    
    // Put the default and unset label on top of the selection labels
    languages.sort(function ( a, b ){
      if ( a === webvowl.util.constants().LANG_IRIBASED ) {
        return -1;
      } else if ( b === webvowl.util.constants().LANG_IRIBASED ) {
        return 1;
      }
      if ( a === webvowl.util.constants().LANG_UNDEFINED ) {
        return -1;
      } else if ( b === webvowl.util.constants().LANG_UNDEFINED ) {
        return 1;
      }
      return a.localeCompare(b);
    });
    
    var languageSelection = d3.select("#language")
      .on("change", function (){
        graph.language(d3.event.target.value);
        sidebar.updateSelectionInformation(lastSelectedElement);
      });
    
    languageSelection.selectAll("option").remove();
    languageSelection.selectAll("option")
      .data(languages)
      .enter().append("option")
      .attr("value", function ( d ){
        return d;
      })
      .text(function ( d ){
        return d;
      });
    
    if ( !trySelectDefaultLanguage(languageSelection, languages, "en") ) {
      if ( !trySelectDefaultLanguage(languageSelection, languages, webvowl.util.constants().LANG_UNDEFINED) ) {
        trySelectDefaultLanguage(languageSelection, languages, webvowl.util.constants().LANG_IRIBASED);
      }
    }
  }
  
  function trySelectDefaultLanguage( selection, languages, language ){
    var langIndex = languages.indexOf(language);
    if ( langIndex >= 0 ) {
      selection.property("selectedIndex", langIndex);
      graph.language(language);
      return true;
    }
    
    return false;
  }
  
  function renderPair( container, name, value, iri ){
    if ( !value && value !== 0 ) return;
    var row = container.append("div").classed("annotation-row", true);
    row.append("span").classed("annotation-name", true).text(name);
    var valSpan = row.append("span").classed("annotation-value", true);
    appendIriLabel(valSpan, value, iri);
  }

  function clearSelectedNodePanel(){
    var panel = d3.select("#selected-node-panel");
    panel.selectAll("*").remove();
    panel.append("p").classed("no-selection-hint", true).text("Select an element in the visualization.");
  }
  
  /**
   * Update the information of the selected node.
   * @param selectedElement the selection or null if nothing is selected
   */
  sidebar.updateSelectionInformation = function ( selectedElement ){
    lastSelectedElement = selectedElement;

    if ( d3.event && d3.event.defaultPrevented ) {
      return;
    }

    if ( !selectedElement ) {
      clearSelectedNodePanel();
      return;
    }

    if ( elementTools.isProperty(selectedElement) ) {
      displayPropertyInformation(selectedElement);
    } else if ( elementTools.isNode(selectedElement) ) {
      displayNodeInformation(selectedElement);
    }
  };

  function displayPropertyInformation( property ){
    var panel = d3.select("#selected-node-panel");
    panel.selectAll("*").remove();

    renderPair(panel, "IRI", property.iri(), property.iri());
    renderPair(panel, "Name", prefixedName(property.iri()));
    renderPair(panel, "Label", property.labelForCurrentLanguage());
    renderPair(panel, "Type", property.type());

    if ( property.domain() ) renderPair(panel, "Domain", property.domain().labelForCurrentLanguage(), property.domain().iri());
    if ( property.range() )  renderPair(panel, "Range",  property.range().labelForCurrentLanguage(),  property.range().iri());
    if ( property.inverse() ) renderPair(panel, "Inverse", property.inverse().labelForCurrentLanguage(), property.inverse().iri());

    renderNodeArrayPairs(panel, "Equivalent", property.equivalents());
    renderNodeArrayPairs(panel, "Subproperty", property.subproperties());
    renderNodeArrayPairs(panel, "Superproperty", property.superproperties());

    if ( property.minCardinality() !== undefined ) {
      renderPair(panel, "Min. cardinality", property.minCardinality());
      renderPair(panel, "Max. cardinality", property.maxCardinality() !== undefined ? property.maxCardinality() : "*");
    } else if ( property.cardinality() !== undefined ) {
      renderPair(panel, "Cardinality", property.cardinality());
    }

    var attrs = getAttributes(property.attributes());
    if ( attrs ) renderPair(panel, "Characteristics", attrs);

    renderCommentPairs(panel, property.comment());

    renderAnnotationPairs(panel, property.annotations());
  }
  
  function getAttributes( attributes ){
    if ( !attributes || attributes.length === 0 ) return null;
    var filtered = attributes.filter(function ( a ){ return a !== "object" && a !== "datatype" && a !== "rdf"; });
    return filtered.length > 0 ? filtered.join(", ") : null;
  }

  function renderNodeArrayPairs( panel, label, nodes ){
    if ( !nodes || nodes.length === 0 ) return;
    nodes.forEach(function ( el ){
      renderPair(panel, label, el.labelForCurrentLanguage(), el.iri());
    });
  }

  function prefixedName( iri ){
    var pm = graph.options().prefixModule();
    if ( pm && pm.getPrefixRepresentationForFullURI ) {
      var prefixed = pm.getPrefixRepresentationForFullURI(iri);
      if ( prefixed !== iri ) return prefixed;
    }
    return iri;
  }

  function renderCommentPairs( panel, comment ){
    if ( !comment ) return;
    if ( typeof comment === "string" ) {
      renderPair(panel, "rdfs:comment", comment);
      return;
    }
    for ( var lang in comment ) {
      if ( comment.hasOwnProperty(lang) && comment[lang] ) {
        renderPair(panel, "rdfs:comment", comment[lang]);
      }
    }
  }

  function renderAnnotationPairs( panel, annotationObject ){
    annotationObject = annotationObject || {};
    for ( var key in annotationObject ) {
      if ( annotationObject.hasOwnProperty(key) ) {
        var entries = annotationObject[key];
        if ( !entries ) continue;
        var label = prefixedName(key);
        entries.forEach(function ( ann ){
          if ( !ann ) return;
          renderPair(panel, label, ann.value, ann.type === "iri" ? ann.value : undefined);
        });
      }
    }
  }

  function appendIriLabel( element, name, iri ){
    var tag;
    
    if ( iri ) {
      tag = element.append("a")
        .attr("href", iri)
        .attr("title", iri)
        .attr("target", "_blank");
    } else {
      tag = element.append("span");
    }
    tag.text(name);
  }
  
  function displayNodeInformation( node ){
    var panel = d3.select("#selected-node-panel");
    panel.selectAll("*").remove();

    renderPair(panel, "IRI", node.iri(), node.iri());
    renderPair(panel, "Name", prefixedName(node.iri()));
    renderPair(panel, "Label", node.labelForCurrentLanguage());
    renderPair(panel, "Type", node.type());

    renderNodeArrayPairs(panel, "Equivalent", node.equivalents());
    renderNodeArrayPairs(panel, "Disjoint with", node.disjointWith());
    renderNodeArrayPairs(panel, "Individual", node.individuals());

    if ( node.links ) {
      node.links().forEach(function ( link ){
        var prop = link.property ? link.property() : null;
        if ( prop && prop.type() === "rdfs:subClassOf" ) {
          var parent = prop.range();
          if ( parent && parent !== node ) {
            renderPair(panel, "rdfs:subClassOf", prefixedName(parent.iri()), parent.iri());
          }
        }
      });
    }

    var attrs = getAttributes(node.attributes());
    if ( attrs ) renderPair(panel, "Characteristics", attrs);

    renderCommentPairs(panel, node.comment());

    renderAnnotationPairs(panel, node.annotations());
  }
  
  /**
   * Renders the read-only annotation rows for any element into the given D3 container.
   * Used by editSidebar to mirror the non-editing view.
   */
  sidebar.renderAnnotationsForElement = function ( element, panel ){
    if ( !element || !panel || panel.empty() ) return;
    panel.selectAll("*").remove();

    if ( elementTools.isNode(element) ) {
      renderPair(panel, "Name", prefixedName(element.iri()));
      renderPair(panel, "Label", element.labelForCurrentLanguage());
      renderPair(panel, "Type", element.type());
      renderNodeArrayPairs(panel, "Equivalent", element.equivalents());
      renderNodeArrayPairs(panel, "Disjoint with", element.disjointWith());
      renderNodeArrayPairs(panel, "Individual", element.individuals());
      if ( element.links ) {
        element.links().forEach(function ( link ){
          var prop = link.property ? link.property() : null;
          if ( prop && prop.type() === "rdfs:subClassOf" ) {
            var parent = prop.range();
            if ( parent && parent !== element ) {
              renderPair(panel, "rdfs:subClassOf", prefixedName(parent.iri()), parent.iri());
            }
          }
        });
      }
      var attrs = getAttributes(element.attributes());
      if ( attrs ) renderPair(panel, "Characteristics", attrs);
      renderCommentPairs(panel, element.comment());
      renderAnnotationPairs(panel, element.annotations());
    } else {
      renderPair(panel, "Name", prefixedName(element.iri()));
      renderPair(panel, "Label", element.labelForCurrentLanguage());
      renderPair(panel, "Type", element.type());
      if ( element.domain() ) renderPair(panel, "Domain", element.domain().labelForCurrentLanguage(), element.domain().iri());
      if ( element.range() )  renderPair(panel, "Range",  element.range().labelForCurrentLanguage(),  element.range().iri());
      if ( element.inverse() ) renderPair(panel, "Inverse", element.inverse().labelForCurrentLanguage(), element.inverse().iri());
      renderNodeArrayPairs(panel, "Equivalent", element.equivalents());
      renderNodeArrayPairs(panel, "Subproperty", element.subproperties());
      renderNodeArrayPairs(panel, "Superproperty", element.superproperties());
      if ( element.minCardinality() !== undefined ) {
        renderPair(panel, "Min. cardinality", element.minCardinality());
        renderPair(panel, "Max. cardinality", element.maxCardinality() !== undefined ? element.maxCardinality() : "*");
      } else if ( element.cardinality() !== undefined ) {
        renderPair(panel, "Cardinality", element.cardinality());
      }
      var attrs2 = getAttributes(element.attributes());
      if ( attrs2 ) renderPair(panel, "Characteristics", attrs2);
      renderCommentPairs(panel, element.comment());
      renderAnnotationPairs(panel, element.annotations());
    }
  };

  /** Collapsible Sidebar functions; **/
  
  sidebar.showSidebar = function ( val, init ){
    // make val to bool
    if ( val === 1 ) {
      visibleSidebar = true;
      collapseButton.node().innerHTML = ">";
      detailArea.classed("hidden", true);
      if ( init === true ) {
        detailArea.classed("hidden", !visibleSidebar);
        graphArea.style("width", "78%");
        graphArea.style("-webkit-animation-name", "none");
        
        menuArea.style("width", "78%");
        menuArea.style("-webkit-animation-name", "none");
        
        d3.select("#WarningErrorMessagesContainer").style("width", "78%");
        d3.select("#WarningErrorMessagesContainer").style("-webkit-animation-name", "none");
      } else {
        graphArea.style("width", "78%");
        graphArea.style("-webkit-animation-name", "sbCollapseAnimation");
        graphArea.style("-webkit-animation-duration", "0.5s");
        
        menuArea.style("width", "78%");
        menuArea.style("-webkit-animation-name", "sbCollapseAnimation");
        menuArea.style("-webkit-animation-duration", "0.5s");
        
        d3.select("#WarningErrorMessagesContainer").style("width", "78%");
        d3.select("#WarningErrorMessagesContainer").style("-webkit-animation-name", "warn_ExpandRightBarAnimation");
        d3.select("#WarningErrorMessagesContainer").style("-webkit-animation-duration", "0.5s");
      }
      graph.options().width(window.innerWidth - (window.innerWidth * 0.22));
      graph.options().navigationMenu().updateScrollButtonVisibility();
    }
    if ( val === 0 ) {
      visibleSidebar = false;
      detailArea.classed("hidden", true);
      
      collapseButton.node().innerHTML = "<";
      // adjust the layout
      if ( init === true ) {
        graphArea.style("width", "100%");
        graphArea.style("-webkit-animation-name", "none");
        
        menuArea.style("width", "100%");
        menuArea.style("-webkit-animation-name", "none");
        
        d3.select("#WarningErrorMessagesContainer").style("width", "100%");
        d3.select("#WarningErrorMessagesContainer").style("-webkit-animation-name", "none");
      } else {
        graphArea.style("width", "100%");
        graphArea.style("-webkit-animation-name", "sbExpandAnimation");
        graphArea.style("-webkit-animation-duration", "0.5s");
        
        menuArea.style("width", "100%");
        menuArea.style("-webkit-animation-name", "sbExpandAnimation");
        menuArea.style("-webkit-animation-duration", "0.5s");
        
        d3.select("#WarningErrorMessagesContainer").style("width", "100%");
        d3.select("#WarningErrorMessagesContainer").style("-webkit-animation-name", "warn_CollapseRightBarAnimation");
        d3.select("#WarningErrorMessagesContainer").style("-webkit-animation-duration", "0.5s");
        
      }
      graph.options().width(window.innerWidth);
      graph.updateCanvasContainerSize();
      graph.options().navigationMenu().updateScrollButtonVisibility();
    }
  };
  
  sidebar.isSidebarVisible = function (){
    return visibleSidebar;
  };
  
  sidebar.updateSideBarVis = function ( init ){
    var vis = sidebar.getSidebarVisibility();
    sidebar.showSidebar(parseInt(vis), init);
  };
  
  sidebar.getSidebarVisibility = function (){
    var isHidden = detailArea.classed("hidden");
    if ( isHidden === false ) return String(1);
    if ( isHidden === true ) return String(0);
  };
  
  sidebar.initSideBarAnimation = function (){
    graphArea.node().addEventListener("animationend", function (){
      detailArea.classed("hidden", !visibleSidebar);
      graph.updateCanvasContainerSize();
      graph.options().navigationMenu().updateScrollButtonVisibility();
    });
  };
  
  sidebar.setup = function (){
    setupCollapsing();
    sidebar.initSideBarAnimation();
    
    collapseButton.on("click", function (){
      graph.options().navigationMenu().hideAllMenus();
      var settingValue = parseInt(sidebar.getSidebarVisibility());
      if ( settingValue === 1 ) sidebar.showSidebar(0);
      else                  sidebar.showSidebar(1);
    });
  };
  
  
  sidebar.updateShowedInformation = function (){
    var editMode = graph.editorMode();
    d3.select("#generalDetails").classed("hidden", editMode);
    d3.select("#generalDetailsEdit").classed("hidden", !editMode);
    
    // store the meta information in graph.options()
    
    // todo: update edit meta info
    graph.options().editSidebar().updateGeneralOntologyInfo();
    
    // todo: update showed meta info;
    graph.options().sidebar().updateGeneralOntologyInfo();
    
  };
  
  sidebar.updateGeneralOntologyInfo = function (){
    // Refresh the ontology panel using the live meta object from graph.options()
    var generalMetaObj = graph.options().getGeneralMetaObject();
    var preferredLanguage = graph && graph.language ? graph.language() : null;

    var panel = d3.select("#ontology-panel");
    if ( panel.empty() ) return;
    panel.selectAll("*").remove();

    var iri = generalMetaObj.iri;
    renderPair(panel, "IRI", iri, iri);

    var title = typeof generalMetaObj.title === "object"
      ? languageTools.textInLanguage(generalMetaObj.title, preferredLanguage)
      : generalMetaObj.title;
    renderPair(panel, "Title", title);
    renderPair(panel, "Version", generalMetaObj.version);

    var author = generalMetaObj.author instanceof Array
      ? generalMetaObj.author.join(", ")
      : generalMetaObj.author;
    renderPair(panel, "Author", author);

    var description = typeof generalMetaObj.description === "object"
      ? languageTools.textInLanguage(generalMetaObj.description, preferredLanguage)
      : generalMetaObj.description;
    renderPair(panel, "Description", description);

    if ( panel.selectAll(".annotation-row").size() === 0 ) {
      panel.append("p").classed("no-selection-hint", true).text("No ontology information available.");
    }
  };
  
  
  return sidebar;
};